diff --git a/package-lock.json b/package-lock.json
index 72b7859..92ae001 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
"dependencies": {
"@hookform/resolvers": "^3.9.0",
"bootstrap": "^5.3.3",
- "bootstrap-icons": "^1.11.3",
+ "bootstrap-icons": "^1.13.1",
"classnames": "^2.5.1",
"file-saver": "^2.0.5",
"i18next": "^23.14.0",
@@ -2693,9 +2693,9 @@
}
},
"node_modules/bootstrap-icons": {
- "version": "1.11.3",
- "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz",
- "integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==",
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz",
+ "integrity": "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==",
"funding": [
{
"type": "github",
diff --git a/package.json b/package.json
index 68a9e96..3f7d496 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,7 @@
"dependencies": {
"@hookform/resolvers": "^3.9.0",
"bootstrap": "^5.3.3",
- "bootstrap-icons": "^1.11.3",
+ "bootstrap-icons": "^1.13.1",
"classnames": "^2.5.1",
"file-saver": "^2.0.5",
"i18next": "^23.14.0",
diff --git a/public/images/bootstrap-icons/data.json b/public/images/bootstrap-icons/data.json
index 8f66567..c5c3c9a 100644
--- a/public/images/bootstrap-icons/data.json
+++ b/public/images/bootstrap-icons/data.json
@@ -62,8 +62,10 @@
"amd.svg",
"android.svg",
"android2.svg",
+ "anthropic.svg",
"app-indicator.svg",
"app.svg",
+ "apple-music.svg",
"apple.svg",
"archive-fill.svg",
"archive.svg",
@@ -217,7 +219,10 @@
"battery-charging.svg",
"battery-full.svg",
"battery-half.svg",
+ "battery-low.svg",
"battery.svg",
+ "beaker-fill.svg",
+ "beaker.svg",
"behance.svg",
"bell-fill.svg",
"bell-slash-fill.svg",
@@ -231,6 +236,7 @@
"binoculars.svg",
"blockquote-left.svg",
"blockquote-right.svg",
+ "bluesky.svg",
"bluetooth.svg",
"body-text.svg",
"book-fill.svg",
@@ -554,6 +560,7 @@
"circle-half.svg",
"circle-square.svg",
"circle.svg",
+ "claude.svg",
"clipboard-check-fill.svg",
"clipboard-check.svg",
"clipboard-data-fill.svg",
@@ -667,6 +674,7 @@
"crop.svg",
"crosshair.svg",
"crosshair2.svg",
+ "css.svg",
"cup-fill.svg",
"cup-hot-fill.svg",
"cup-hot.svg",
@@ -1068,6 +1076,10 @@
"fire.svg",
"flag-fill.svg",
"flag.svg",
+ "flask-fill.svg",
+ "flask-florence-fill.svg",
+ "flask-florence.svg",
+ "flask.svg",
"floppy-fill.svg",
"floppy.svg",
"floppy2-fill.svg",
@@ -1086,6 +1098,7 @@
"folder2-open.svg",
"folder2.svg",
"fonts.svg",
+ "fork-knife.svg",
"forward-fill.svg",
"forward.svg",
"front.svg",
@@ -1116,9 +1129,13 @@
"git.svg",
"github.svg",
"gitlab.svg",
+ "globe-americas-fill.svg",
"globe-americas.svg",
+ "globe-asia-australia-fill.svg",
"globe-asia-australia.svg",
+ "globe-central-south-asia-fill.svg",
"globe-central-south-asia.svg",
+ "globe-europe-africa-fill.svg",
"globe-europe-africa.svg",
"globe.svg",
"globe2.svg",
@@ -1245,6 +1262,7 @@
"input-cursor.svg",
"instagram.svg",
"intersect.svg",
+ "javascript.svg",
"journal-album.svg",
"journal-arrow-down.svg",
"journal-arrow-up.svg",
@@ -1291,6 +1309,8 @@
"layout-text-window.svg",
"layout-three-columns.svg",
"layout-wtf.svg",
+ "leaf-fill.svg",
+ "leaf.svg",
"life-preserver.svg",
"lightbulb-fill.svg",
"lightbulb-off-fill.svg",
@@ -1333,6 +1353,8 @@
"marker-tip.svg",
"mask.svg",
"mastodon.svg",
+ "measuring-cup-fill.svg",
+ "measuring-cup.svg",
"medium.svg",
"megaphone-fill.svg",
"megaphone.svg",
@@ -1392,6 +1414,7 @@
"octagon-fill.svg",
"octagon-half.svg",
"octagon.svg",
+ "openai.svg",
"opencollective.svg",
"optical-audio-fill.svg",
"optical-audio.svg",
@@ -1448,6 +1471,7 @@
"people-fill.svg",
"people.svg",
"percent.svg",
+ "perplexity.svg",
"person-add.svg",
"person-arms-up.svg",
"person-badge-fill.svg",
@@ -1935,6 +1959,7 @@
"truck-front.svg",
"truck.svg",
"tsunami.svg",
+ "tux.svg",
"tv-fill.svg",
"tv.svg",
"twitch.svg",
@@ -1951,6 +1976,7 @@
"type-strikethrough.svg",
"type-underline.svg",
"type.svg",
+ "typescript.svg",
"ubuntu.svg",
"ui-checks-grid.svg",
"ui-checks.svg",
@@ -1965,6 +1991,8 @@
"universal-access.svg",
"unlock-fill.svg",
"unlock.svg",
+ "unlock2-fill.svg",
+ "unlock2.svg",
"upc-scan.svg",
"upc.svg",
"upload.svg",
diff --git a/public/images/bootstrap-icons/icons/anthropic.svg b/public/images/bootstrap-icons/icons/anthropic.svg
new file mode 100644
index 0000000..eed7edf
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/anthropic.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/apple-music.svg b/public/images/bootstrap-icons/icons/apple-music.svg
new file mode 100644
index 0000000..325e3a7
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/apple-music.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/battery-low.svg b/public/images/bootstrap-icons/icons/battery-low.svg
new file mode 100644
index 0000000..b99e758
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/battery-low.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/beaker-fill.svg b/public/images/bootstrap-icons/icons/beaker-fill.svg
new file mode 100644
index 0000000..3bf39d5
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/beaker-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/beaker.svg b/public/images/bootstrap-icons/icons/beaker.svg
new file mode 100644
index 0000000..f1958c6
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/beaker.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/bluesky.svg b/public/images/bootstrap-icons/icons/bluesky.svg
new file mode 100644
index 0000000..a070aea
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/bluesky.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/claude.svg b/public/images/bootstrap-icons/icons/claude.svg
new file mode 100644
index 0000000..1403b27
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/claude.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/css.svg b/public/images/bootstrap-icons/icons/css.svg
new file mode 100644
index 0000000..e336af1
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/css.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/flask-fill.svg b/public/images/bootstrap-icons/icons/flask-fill.svg
new file mode 100644
index 0000000..61207af
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/flask-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/flask-florence-fill.svg b/public/images/bootstrap-icons/icons/flask-florence-fill.svg
new file mode 100644
index 0000000..70896a6
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/flask-florence-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/flask-florence.svg b/public/images/bootstrap-icons/icons/flask-florence.svg
new file mode 100644
index 0000000..6df75f7
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/flask-florence.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/flask.svg b/public/images/bootstrap-icons/icons/flask.svg
new file mode 100644
index 0000000..01cc1ba
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/flask.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/fork-knife.svg b/public/images/bootstrap-icons/icons/fork-knife.svg
new file mode 100644
index 0000000..31277b4
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/fork-knife.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/globe-americas-fill.svg b/public/images/bootstrap-icons/icons/globe-americas-fill.svg
new file mode 100644
index 0000000..42259b2
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/globe-americas-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/globe-asia-australia-fill.svg b/public/images/bootstrap-icons/icons/globe-asia-australia-fill.svg
new file mode 100644
index 0000000..8dfde0c
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/globe-asia-australia-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/globe-central-south-asia-fill.svg b/public/images/bootstrap-icons/icons/globe-central-south-asia-fill.svg
new file mode 100644
index 0000000..8f52f93
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/globe-central-south-asia-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/globe-europe-africa-fill.svg b/public/images/bootstrap-icons/icons/globe-europe-africa-fill.svg
new file mode 100644
index 0000000..3fcdf0b
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/globe-europe-africa-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/javascript.svg b/public/images/bootstrap-icons/icons/javascript.svg
new file mode 100644
index 0000000..8a7e716
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/javascript.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/leaf-fill.svg b/public/images/bootstrap-icons/icons/leaf-fill.svg
new file mode 100644
index 0000000..094ab20
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/leaf-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/leaf.svg b/public/images/bootstrap-icons/icons/leaf.svg
new file mode 100644
index 0000000..37b51e3
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/leaf.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/measuring-cup-fill.svg b/public/images/bootstrap-icons/icons/measuring-cup-fill.svg
new file mode 100644
index 0000000..a62ef9e
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/measuring-cup-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/measuring-cup.svg b/public/images/bootstrap-icons/icons/measuring-cup.svg
new file mode 100644
index 0000000..6020df9
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/measuring-cup.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/openai.svg b/public/images/bootstrap-icons/icons/openai.svg
new file mode 100644
index 0000000..ad018a2
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/openai.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/perplexity.svg b/public/images/bootstrap-icons/icons/perplexity.svg
new file mode 100644
index 0000000..8cab953
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/perplexity.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/tux.svg b/public/images/bootstrap-icons/icons/tux.svg
new file mode 100644
index 0000000..6a3242c
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/tux.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/typescript.svg b/public/images/bootstrap-icons/icons/typescript.svg
new file mode 100644
index 0000000..12429d8
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/typescript.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/unlock2-fill.svg b/public/images/bootstrap-icons/icons/unlock2-fill.svg
new file mode 100644
index 0000000..1d8ef92
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/unlock2-fill.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/public/images/bootstrap-icons/icons/unlock2.svg b/public/images/bootstrap-icons/icons/unlock2.svg
new file mode 100644
index 0000000..0815a16
--- /dev/null
+++ b/public/images/bootstrap-icons/icons/unlock2.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/src/components/BitmapEditor/components/ExportDialog/index.tsx b/src/components/BitmapEditor/components/ExportDialog/index.tsx
index 06178ca..63a0df6 100644
--- a/src/components/BitmapEditor/components/ExportDialog/index.tsx
+++ b/src/components/BitmapEditor/components/ExportDialog/index.tsx
@@ -29,7 +29,7 @@ interface FormValues {
const defaultValues: FormValues = {
name: '',
- bitOrder: BitOrder.BigEndian,
+ bitOrder: BitOrder.MSB,
sizeFormat: SizeFormat.Defines,
dataFormat: DataFormat.Hex,
platform: Platform.Arduino,
@@ -83,14 +83,14 @@ export const ExportDialog = ({ bitmapId, area, onClose }: ExportDialogProps): JS
{t('Bit order')}
@@ -114,30 +114,34 @@ export const ExportDialog = ({ bitmapId, area, onClose }: ExportDialogProps): JS
/>
-
-
-
{t('Size format')}
-
-
-
-
-
-
+ {formValues.platform !== Platform.Pico && (
+ <>
+
+
+
{t('Size format')}
+
+
+
+
+
+
+ >
+ )}
{t('Platform')}
@@ -153,9 +157,17 @@ export const ExportDialog = ({ bitmapId, area, onClose }: ExportDialogProps): JS
type="radio"
{...register('platform', { required: true })}
/>
+
-
+ {formValues.platform !== Platform.Pico && (
+
+ )}
diff --git a/src/components/BitmapEditor/components/ExportDialog/utils.test.ts b/src/components/BitmapEditor/components/ExportDialog/utils.test.ts
new file mode 100644
index 0000000..74dbfdb
--- /dev/null
+++ b/src/components/BitmapEditor/components/ExportDialog/utils.test.ts
@@ -0,0 +1,174 @@
+import { describe, it, expect } from 'vitest';
+import { exportBitmap, Platform, DataFormat, SizeFormat } from './utils';
+import { BitOrder, type BitmapEntity } from '@/utils/bitmap/types';
+import { Area } from '@/utils/bitmap/Area';
+
+describe('BitmapEditor/ExportDialog/utils', () => {
+ const mockBitmap: BitmapEntity = {
+ id: 'test-id',
+ name: 'test-name',
+ width: 8,
+ height: 8,
+ // A simple pattern
+ data: [0x18244281, 0x81422418],
+ createdAt: 1,
+ updatedAt: 1,
+ favorite: false,
+ };
+
+ const expectedDataHex = '0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81';
+ const expectedDataBin =
+ '0b10000001, 0b01000010, 0b00100100, 0b00011000, 0b00011000, 0b00100100, 0b01000010, 0b10000001';
+
+ describe('Arduino platform', () => {
+ it('should export with Hex, comments size, and no progmem', () => {
+ const result = exportBitmap({
+ name: 'testBitmap',
+ bitmapEntity: mockBitmap,
+ bitOrder: BitOrder.LSB,
+ platform: Platform.Arduino,
+ dataFormat: DataFormat.Hex,
+ sizeFormat: SizeFormat.Comments,
+ progmem: false,
+ });
+
+ expect(result).toContain('// testbitmap.h');
+ expect(result).toContain('const unsigned char testbitmap[]');
+ expect(result).toContain('/* width: 8, height: 8 */');
+ expect(result).not.toContain('PROGMEM');
+ expect(result).not.toContain('#include');
+ expect(result).toContain(expectedDataHex);
+ });
+
+ it('should export with Bin, variables size, and progmem', () => {
+ const result = exportBitmap({
+ name: 'testBitmap',
+ bitmapEntity: mockBitmap,
+ bitOrder: BitOrder.LSB,
+ platform: Platform.Arduino,
+ dataFormat: DataFormat.Bin,
+ sizeFormat: SizeFormat.Variables,
+ progmem: true,
+ });
+
+ expect(result).toContain('#include ');
+ expect(result).toContain('static const unsigned char PROGMEM testbitmap[]');
+ expect(result).toContain('const unsigned char testbitmap_width = 8;');
+ expect(result).toContain('const unsigned char testbitmap_height = 8;');
+ expect(result).toContain(expectedDataBin);
+ });
+ });
+
+ describe('Clang platform', () => {
+ it('should export with Hex, defines size, and no progmem', () => {
+ const result = exportBitmap({
+ name: 'testBitmap',
+ bitmapEntity: mockBitmap,
+ bitOrder: BitOrder.LSB,
+ platform: Platform.Clang,
+ dataFormat: DataFormat.Hex,
+ sizeFormat: SizeFormat.Defines,
+ progmem: false,
+ });
+
+ expect(result).toContain('#include ');
+ expect(result).not.toContain('PROGMEM');
+ expect(result).toContain('const uint8_t testbitmap[]');
+ expect(result).toContain('#define TESTBITMAP_WIDTH 8');
+ expect(result).toContain('#define TESTBITMAP_HEIGHT 8');
+ expect(result).toContain(expectedDataHex);
+ });
+
+ it('should export with progmem', () => {
+ const result = exportBitmap({
+ name: 'testBitmap',
+ bitmapEntity: mockBitmap,
+ bitOrder: BitOrder.LSB,
+ platform: Platform.Clang,
+ dataFormat: DataFormat.Hex,
+ sizeFormat: SizeFormat.Defines,
+ progmem: true,
+ });
+
+ expect(result).toContain('#include ');
+ expect(result).toContain('#include ');
+ expect(result).toContain('const uint8_t PROGMEM testbitmap[]');
+ });
+ });
+
+ describe('Pico platform', () => {
+ it('should export for Pico platform', () => {
+ const result = exportBitmap({
+ name: 'testBitmap',
+ bitmapEntity: mockBitmap,
+ bitOrder: BitOrder.LSB,
+ platform: Platform.Pico,
+ dataFormat: DataFormat.Hex,
+ sizeFormat: SizeFormat.Comments,
+ progmem: false,
+ });
+
+ expect(result).toContain('// testbitmap.h');
+ expect(result).toContain('#ifndef TESTBITMAP_H');
+ expect(result).toContain('#define TESTBITMAP_H');
+ expect(result).toContain('#include ');
+ expect(result).toContain('#include "bitmap.h"');
+ expect(result).toContain('const bitmap_t testbitmap = {');
+ expect(result).toContain('.width = 8,');
+ expect(result).toContain('.height = 8,');
+ expect(result).toContain(`.data = (uint8_t[]){${expectedDataHex}}`);
+ expect(result).toContain('#endif // TESTBITMAP_H');
+ });
+ });
+
+ describe('General functionality', () => {
+ it('should sanitize the bitmap name', () => {
+ const result = exportBitmap({
+ name: 'My Awesome Bitmap!',
+ bitmapEntity: mockBitmap,
+ bitOrder: BitOrder.LSB,
+ platform: Platform.Arduino,
+ dataFormat: DataFormat.Hex,
+ sizeFormat: SizeFormat.Comments,
+ progmem: false,
+ });
+
+ expect(result).toContain('// my_awesome_bitmap_.h');
+ expect(result).toContain('const unsigned char my_awesome_bitmap_[]');
+ });
+
+ it('should export a specific area of the bitmap', () => {
+ const largeBitmap: BitmapEntity = {
+ id: 'large-test-id',
+ name: 'large-test-name',
+ width: 4,
+ height: 4,
+ data: [0x0660],
+ createdAt: 1,
+ updatedAt: 1,
+ favorite: false,
+ };
+ const area = Area.fromRectangle(1, 1, 2, 2);
+
+ const result = exportBitmap({
+ name: 'areaTest',
+ bitmapEntity: largeBitmap,
+ bitOrder: BitOrder.MSB,
+ platform: Platform.Clang,
+ dataFormat: DataFormat.Hex,
+ sizeFormat: SizeFormat.Comments,
+ progmem: false,
+ area,
+ });
+
+ // The extracted 2x2 bitmap ('11' from the first row, '11' from the second) gives the bit sequence '1111'.
+ // With BitOrder.MSB, bits are packed from the most significant side of the byte.
+ // This results in 0b11110000, which is 0xf0.
+ const expectedAreaData = '0xf0';
+
+ expect(result).toContain('/* width: 2, height: 2 */');
+ expect(result).toContain(expectedAreaData);
+ expect(result.match(/0x/g)?.length).toBe(1); // Only one byte of data
+ });
+ });
+});
diff --git a/src/components/BitmapEditor/components/ExportDialog/utils.ts b/src/components/BitmapEditor/components/ExportDialog/utils.ts
index 4e831c5..8bf3ce5 100644
--- a/src/components/BitmapEditor/components/ExportDialog/utils.ts
+++ b/src/components/BitmapEditor/components/ExportDialog/utils.ts
@@ -17,6 +17,7 @@ export enum DataFormat {
export enum Platform {
Arduino = 'arduino',
Clang = 'clang',
+ Pico = 'pico',
}
interface ExportBitmapParams {
@@ -30,40 +31,73 @@ interface ExportBitmapParams {
area?: Area;
}
-export const exportBitmap = ({
- name,
- bitOrder,
- bitmapEntity,
- dataFormat,
- platform,
+interface PlatformExportParams {
+ nameLower: string;
+ nameUpper: string;
+ width: number;
+ height: number;
+ dataArray: string;
+ sizeFormat: SizeFormat;
+ progmem: boolean;
+}
+
+const exportForArduino = ({
+ nameLower,
+ nameUpper,
+ width,
+ height,
+ dataArray,
sizeFormat,
progmem,
- area,
-}: ExportBitmapParams): string => {
- const srcBitmap = Bitmap.fromJSON(bitmapEntity);
- const bitmap = area ? srcBitmap.copy(area) : srcBitmap;
- const xBitMap = XBitmapSerializer.serialize(bitmap, bitOrder);
- const width = bitmap.width;
- const height = bitmap.height;
-
- const nameLower = name.replace(/[^\w]/gi, '_').toLowerCase();
- const nameUpper = nameLower.toUpperCase();
+}: PlatformExportParams): string => {
const headerName = `${nameUpper}_H`;
-
- const dataArray = Array.from(xBitMap)
- .map((value) =>
- dataFormat === DataFormat.Hex ? `0x${value.toString(16)}` : `0b${value.toString(2).padStart(8, '0')}`,
- )
- .join(', ');
-
- const uint8 = platform === Platform.Arduino ? 'const unsigned char' : 'const uint8_t';
- const varType = platform === Platform.Arduino ? `static ${uint8}` : uint8;
+ const uint8 = 'const unsigned char';
+ const varType = `static ${uint8}`;
const progMem = progmem ? ' PROGMEM' : '';
- const includesBlock = [platform === Platform.Clang && '#include ', progmem && '#include ']
+ const includesBlock = progmem ? '#include ' : '';
+
+ const sizeBlock = [
+ sizeFormat === SizeFormat.Comments && `/* width: ${width}, height: ${height} */`,
+ sizeFormat === SizeFormat.Variables &&
+ `${uint8} ${nameLower}_width = ${width};\n${uint8} ${nameLower}_height = ${height};`,
+ sizeFormat === SizeFormat.Defines && `#define ${nameUpper}_WIDTH ${width}\n#define ${nameUpper}_HEIGHT ${height}`,
+ ]
.filter(Boolean)
.join('\n');
+ return `
+// ${nameLower}.h
+#ifndef ${headerName}
+#define ${headerName}
+${includesBlock}
+
+${sizeBlock}
+
+${varType}${progMem} ${nameLower}[] = { ${dataArray} };
+
+#endif // ${headerName}
+`
+ .replace(/^\n/, '')
+ .replace(/\n$/, '');
+};
+
+const exportForClang = ({
+ nameLower,
+ nameUpper,
+ width,
+ height,
+ dataArray,
+ sizeFormat,
+ progmem,
+}: PlatformExportParams): string => {
+ const headerName = `${nameUpper}_H`;
+ const uint8 = 'const uint8_t';
+ const varType = uint8;
+ const progMem = progmem ? ' PROGMEM' : '';
+
+ const includesBlock = ['#include ', progmem && '#include '].filter(Boolean).join('\n');
+
const sizeBlock = [
sizeFormat === SizeFormat.Comments && `/* width: ${width}, height: ${height} */`,
sizeFormat === SizeFormat.Variables &&
@@ -73,7 +107,7 @@ export const exportBitmap = ({
.filter(Boolean)
.join('\n');
- const output = `
+ return `
// ${nameLower}.h
#ifndef ${headerName}
#define ${headerName}
@@ -87,5 +121,72 @@ ${varType}${progMem} ${nameLower}[] = { ${dataArray} };
`
.replace(/^\n/, '')
.replace(/\n$/, '');
- return output;
+};
+
+const exportForPico = ({ nameLower, nameUpper, width, height, dataArray }: PlatformExportParams): string => {
+ const headerName = `${nameUpper}_H`;
+ const includesBlock = ['#include ', '#include "bitmap.h"'].filter(Boolean).join('\n');
+ return `
+// ${nameLower}.h
+#ifndef ${headerName}
+#define ${headerName}
+${includesBlock}
+
+const bitmap_t ${nameLower} = {
+ .width = ${width},
+ .height = ${height},
+ .data = (uint8_t[]){${dataArray}}
+};
+
+#endif // ${headerName}
+`
+ .replace(/^\n/, '')
+ .replace(/\n$/, '');
+};
+
+export const exportBitmap = ({
+ name,
+ bitOrder,
+ bitmapEntity,
+ dataFormat,
+ platform,
+ sizeFormat,
+ progmem,
+ area,
+}: ExportBitmapParams): string => {
+ const srcBitmap = Bitmap.fromJSON(bitmapEntity);
+ const bitmap = area ? srcBitmap.copy(area) : srcBitmap;
+ const xBitMap = XBitmapSerializer.serialize(bitmap, bitOrder);
+ const { width, height } = bitmap;
+
+ const nameLower = name.replace(/[^\w]/gi, '_').toLowerCase();
+ const nameUpper = nameLower.toUpperCase();
+
+ const dataArray = Array.from(xBitMap)
+ .map((value) =>
+ dataFormat === DataFormat.Hex ? `0x${value.toString(16)}` : `0b${value.toString(2).padStart(8, '0')}`,
+ )
+ .join(', ');
+
+ const params: PlatformExportParams = {
+ nameLower,
+ nameUpper,
+ width,
+ height,
+ dataArray,
+ sizeFormat,
+ progmem,
+ };
+
+ switch (platform) {
+ case Platform.Arduino:
+ return exportForArduino(params);
+ case Platform.Clang:
+ return exportForClang(params);
+ case Platform.Pico:
+ return exportForPico(params);
+ default:
+ // Should not be reachable
+ return '';
+ }
};
diff --git a/src/pages/EditBitmap/index.test.tsx b/src/pages/EditBitmap/index.test.tsx
index 037284b..eb96bc8 100644
--- a/src/pages/EditBitmap/index.test.tsx
+++ b/src/pages/EditBitmap/index.test.tsx
@@ -82,7 +82,7 @@ test('export bitmap', async () => {
const inputName = screen.getByLabelText('Name');
await userEvent.clear(inputName);
await userEvent.type(inputName, 'Sun');
- await userEvent.click(screen.getByLabelText('Little-endian (Adafruit)'));
+ await userEvent.click(screen.getByLabelText('MSB-first (Adafruit)'));
await userEvent.click(screen.getByLabelText('Bin'));
await userEvent.click(screen.getByLabelText('Variables'));
await userEvent.click(screen.getByLabelText('C language'));
diff --git a/src/utils/bitmap/XBitmapSerializer.test.ts b/src/utils/bitmap/XBitmapSerializer.test.ts
index da28584..b36e525 100644
--- a/src/utils/bitmap/XBitmapSerializer.test.ts
+++ b/src/utils/bitmap/XBitmapSerializer.test.ts
@@ -9,11 +9,11 @@ const array = new Uint32Array(bitmapEntity.data);
const bitmap = new Bitmap(new BitmapData(8, 8, array));
test('serialize (BE)', () => {
- const arr = XBitmapSerializer.serialize(bitmap, BitOrder.BigEndian);
+ const arr = XBitmapSerializer.serialize(bitmap, BitOrder.LSB);
expect(arr).toEqual(new Uint8Array([207, 201, 9, 15, 240, 240, 243, 243]));
});
test('serialize (LE)', () => {
- const arr = XBitmapSerializer.serialize(bitmap, BitOrder.LittleEndian);
+ const arr = XBitmapSerializer.serialize(bitmap, BitOrder.MSB);
expect(arr).toEqual(new Uint8Array([243, 147, 144, 240, 15, 15, 207, 207]));
});
diff --git a/src/utils/bitmap/XBitmapSerializer.ts b/src/utils/bitmap/XBitmapSerializer.ts
index 6296e5b..8be7f61 100644
--- a/src/utils/bitmap/XBitmapSerializer.ts
+++ b/src/utils/bitmap/XBitmapSerializer.ts
@@ -9,7 +9,7 @@ export class XBitmapSerializer {
for (let bit = 0; bit < UINT8_BITS; bit++) {
const srcIndex = dstIndex * UINT8_BITS + bit;
if (bitmap.getPixelValue(srcIndex)) {
- const resBit = bitOrder === BitOrder.BigEndian ? bit : UINT8_BITS - 1 - bit;
+ const resBit = bitOrder === BitOrder.LSB ? bit : UINT8_BITS - 1 - bit;
result[dstIndex] |= 1 << resBit;
}
}
diff --git a/src/utils/bitmap/types.ts b/src/utils/bitmap/types.ts
index 2047646..acf1206 100644
--- a/src/utils/bitmap/types.ts
+++ b/src/utils/bitmap/types.ts
@@ -1,8 +1,8 @@
import { Point } from './Point';
export enum BitOrder {
- BigEndian = 'BE', // MSB (Most Significant Byte) or BE (Big-Endian)
- LittleEndian = 'LE', // LSB (Least Significant Byte) or LE (Little-Endian)
+ MSB = 'MSB',
+ LSB = 'LSB',
}
export interface BitmapJSON {