Skip to content

Commit e426b08

Browse files
committed
Enhance Pixel2CPP and PixelCanvas components with improved error handling, logging, and support for additional draw modes. Added validation for input data and generated bytes, along with fallback mechanisms for unknown formats. Updated PixelCanvas to handle transparent pixels based on selected background color, ensuring accurate rendering. Refactored packing logic to correctly identify lighter pixels for 1-bit packing.
1 parent 832df2a commit e426b08

File tree

3 files changed

+133
-48
lines changed

3 files changed

+133
-48
lines changed

src/Pixel2CPP.jsx

Lines changed: 114 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -222,27 +222,47 @@ export default function Pixel2CPP() {
222222
const generateCppCode = () => {
223223
const safeName = name.replace(/[^a-zA-Z0-9_]/g, "_");
224224

225+
console.log('generateCppCode called with:', { drawMode, outputFormat, w, h, dataLength: data.length });
226+
227+
// Validate data
228+
if (!data || data.length !== w * h) {
229+
console.error('Invalid data:', { dataLength: data?.length, expectedLength: w * h, w, h });
230+
return `// Error: Invalid data array (length: ${data?.length}, expected: ${w * h})`;
231+
}
232+
233+
// Helper function to safely call packer functions
234+
const safePack = (packerFunc, ...args) => {
235+
try {
236+
const result = packerFunc(...args);
237+
console.log(`${packerFunc.name} result:`, result.length, 'bytes');
238+
return result;
239+
} catch (error) {
240+
console.error(`Error in ${packerFunc.name}:`, error);
241+
return [];
242+
}
243+
};
244+
225245
// Determine data and format based on draw mode
226-
let bytes, byteStr, dataType, dataFormat;
246+
let bytes = [], byteStr, dataType = "uint8_t", dataFormat = "pixels";
227247

228248
if (drawMode === "HORIZONTAL_1BIT") {
229-
bytes = pack1bit(data, w, h, 'horizontal');
249+
bytes = safePack(pack1bit, data, w, h, 'horizontal');
230250
dataType = "uint8_t";
231251
dataFormat = "bits";
232252
} else if (drawMode === "VERTICAL_1BIT") {
233-
bytes = pack1bit(data, w, h, 'vertical');
253+
bytes = safePack(pack1bit, data, w, h, 'vertical');
234254
dataType = "uint8_t";
235255
dataFormat = "bits";
236256
} else if (drawMode === "HORIZONTAL_ALPHA") {
237-
bytes = pack1bitAlpha(data, w, h, 'horizontal');
257+
bytes = safePack(pack1bitAlpha, data, w, h, 'horizontal');
238258
dataType = "uint8_t";
239259
dataFormat = "alpha";
240260
} else if (drawMode === "HORIZONTAL_RGB565") {
241-
bytes = packRGB565(data, w, h);
261+
bytes = safePack(packRGB565, data, w, h);
242262
dataType = "uint16_t";
243263
dataFormat = "pixels";
244264
} else if (drawMode === "HORIZONTAL_RGB888_24") {
245-
bytes = packRGB24(data, w, h);
265+
bytes = safePack(packRGB24, data, w, h);
246266
dataType = "uint8_t";
247267
dataFormat = "pixels";
248268
} else if (drawMode === "HORIZONTAL_RGB888_32") {
@@ -256,17 +276,47 @@ export default function Pixel2CPP() {
256276
}
257277
dataType = "uint8_t";
258278
dataFormat = "pixels";
279+
} else if (drawMode === "HORIZONTAL_RGB332") {
280+
bytes = safePack(packRGB332, data, w, h);
281+
dataType = "uint8_t";
282+
dataFormat = "pixels";
283+
} else if (drawMode === "HORIZONTAL_GRAY4") {
284+
bytes = safePack(packGray4, data, w, h);
285+
dataType = "uint8_t";
286+
dataFormat = "pixels";
287+
} else {
288+
// Fallback for unknown draw modes - use 1-bit horizontal
289+
console.warn(`Unknown draw mode: ${drawMode}, falling back to HORIZONTAL_1BIT`);
290+
bytes = safePack(pack1bit, data, w, h, 'horizontal');
291+
dataType = "uint8_t";
292+
dataFormat = "bits";
293+
}
294+
295+
console.log('Generated bytes:', { bytesLength: bytes.length, dataType, dataFormat });
296+
297+
// Validate bytes were generated
298+
if (!bytes || bytes.length === 0) {
299+
console.error('No bytes generated');
300+
return `// Error: No bytes generated for ${drawMode} format`;
259301
}
260302

261303
// Generate byte string based on output format
262-
if (outputFormat === "PLAIN_BYTES") {
263-
return generatePlainBytes(bytes, safeName, w, h, dataType, dataFormat, drawMode);
264-
} else if (outputFormat === "ARDUINO_CODE") {
265-
return generateArduinoCode(bytes, safeName, w, h, dataType, dataFormat, drawMode);
266-
} else if (outputFormat === "ARDUINO_SINGLE_BITMAP") {
267-
return generateArduinoSingleBitmap(bytes, safeName, w, h, dataType, dataFormat);
268-
} else if (outputFormat === "GFX_BITMAP_FONT") {
269-
return generateGFXBitmapFont(bytes, safeName, w, h);
304+
try {
305+
if (outputFormat === "PLAIN_BYTES") {
306+
return generatePlainBytes(bytes, safeName, w, h, dataType, dataFormat, drawMode);
307+
} else if (outputFormat === "ARDUINO_CODE") {
308+
return generateArduinoCode(bytes, safeName, w, h, dataType, dataFormat, drawMode);
309+
} else if (outputFormat === "ARDUINO_SINGLE_BITMAP") {
310+
return generateArduinoSingleBitmap(bytes, safeName, w, h, dataType, dataFormat);
311+
} else if (outputFormat === "GFX_BITMAP_FONT") {
312+
return generateGFXBitmapFont(bytes, safeName, w, h);
313+
} else {
314+
console.warn(`Unknown output format: ${outputFormat}, falling back to ARDUINO_CODE`);
315+
return generateArduinoCode(bytes, safeName, w, h, dataType, dataFormat, drawMode);
316+
}
317+
} catch (error) {
318+
console.error('Error generating output format:', error);
319+
return `// Error generating ${outputFormat} format: ${error.message}`;
270320
}
271321

272322
// Legacy fallback for old mode system
@@ -351,7 +401,7 @@ void drawImagePixelByPixel(int16_t x0, int16_t y0) {
351401
}
352402
}
353403
*/`;
354-
} else if (mode === "RGB24") {
404+
} else if (drawMode === "HORIZONTAL_RGB888_24") {
355405
const bytes = packRGB24(data, w, h);
356406
const byteStr = bytes.map((b) => "0x" + b.toString(16).toUpperCase().padStart(2, "0")).join(", ");
357407
return `// Generated by Pixel2CPP (RGB24 for ESP32/high-memory displays)
@@ -402,7 +452,7 @@ void drawImage(int16_t x0, int16_t y0) {
402452
}
403453
}
404454
}`;
405-
} else if (mode === "RGB332") {
455+
} else if (drawMode === "HORIZONTAL_RGB332") {
406456
const bytes = packRGB332(data, w, h);
407457
const byteStr = bytes.map((b) => "0x" + b.toString(16).toUpperCase().padStart(2, "0")).join(", ");
408458
return `// Generated by Pixel2CPP (RGB332 for low-memory/retro displays)
@@ -452,7 +502,7 @@ void drawImage(int16_t x0, int16_t y0) {
452502
- Green: 3 bits (0-7)
453503
- Blue: 2 bits (0-3)
454504
*/`;
455-
} else if (mode === "GRAY4") {
505+
} else if (drawMode === "HORIZONTAL_GRAY4") {
456506
const bytes = packGray4(data, w, h);
457507
const byteStr = bytes.map((b) => "0x" + b.toString(16).toUpperCase().padStart(2, "0")).join(", ");
458508
return `// Generated by Pixel2CPP (4-bit Grayscale for E-ink/EPD)
@@ -846,9 +896,15 @@ const GFXfont ${safeName} PROGMEM = {
846896
};
847897

848898
const handleGenerateCode = async () => {
849-
setIsGenerating(true);
850-
setShowCodeModal(true);
851-
setIsGenerating(false);
899+
try {
900+
setIsGenerating(true);
901+
console.log('Generating code for:', { drawMode, outputFormat, w, h, dataLength: data.length });
902+
setShowCodeModal(true);
903+
} catch (error) {
904+
console.error('Error generating code:', error);
905+
} finally {
906+
setIsGenerating(false);
907+
}
852908
};
853909

854910
const handleCopyCode = async () => {
@@ -1267,6 +1323,8 @@ const GFXfont ${safeName} PROGMEM = {
12671323
<option value="HORIZONTAL_ALPHA">Alpha Map</option>
12681324
<option value="HORIZONTAL_RGB888_24">RGB24 (24-bit)</option>
12691325
<option value="HORIZONTAL_RGB888_32">RGBA32 (32-bit)</option>
1326+
<option value="HORIZONTAL_RGB332">RGB332 (8-bit)</option>
1327+
<option value="HORIZONTAL_GRAY4">GRAY4 (4-bit)</option>
12701328
</select>
12711329
</label>
12721330

@@ -1346,26 +1404,32 @@ const GFXfont ${safeName} PROGMEM = {
13461404
</div>
13471405
</div>
13481406

1349-
{/* Background */}
1350-
<div className="space-y-3">
1351-
<h3 className="font-medium text-sm text-neutral-300">Background</h3>
1352-
<div className="grid grid-cols-2 gap-2">
1353-
{["black", "white", "transparent"].map((bg) => (
1354-
<label key={bg} className="flex items-center gap-2 cursor-pointer">
1355-
<input
1356-
type="radio"
1357-
name="backgroundColor"
1358-
value={bg}
1359-
checked={backgroundColor === bg}
1360-
onChange={(e) => setBackgroundColor(e.target.value)}
1361-
className="w-3 h-3 text-blue-500 bg-neutral-800 border-neutral-700 focus:ring-blue-500"
1362-
aria-label={`${bg} background`}
1363-
/>
1364-
<span className="text-xs capitalize">{bg}</span>
1365-
</label>
1366-
))}
1367-
</div>
1368-
</div>
1407+
{/* Background */}
1408+
<div className="space-y-3">
1409+
<h3 className="font-medium text-sm text-neutral-300">Background</h3>
1410+
<div className="grid grid-cols-2 gap-2">
1411+
{["black", "white", "transparent"].map((bg) => (
1412+
<label key={bg} className="flex items-center gap-2 cursor-pointer">
1413+
<input
1414+
type="radio"
1415+
name="backgroundColor"
1416+
value={bg}
1417+
checked={backgroundColor === bg}
1418+
onChange={(e) => setBackgroundColor(e.target.value)}
1419+
className="w-3 h-3 text-blue-500 bg-neutral-800 border-neutral-700 focus:ring-blue-500"
1420+
aria-label={`${bg} background`}
1421+
/>
1422+
<span className="text-xs capitalize">{bg}</span>
1423+
</label>
1424+
))}
1425+
</div>
1426+
<div className="text-xs text-neutral-400 bg-neutral-800/50 p-2 rounded">
1427+
<div className="font-medium mb-1">Background affects display only:</div>
1428+
<div>• Black: White pixels = "on" (1)</div>
1429+
<div>• White: Black pixels = "on" (1)</div>
1430+
<div>• Transparent: Alpha &gt; 0 = "on" (1)</div>
1431+
</div>
1432+
</div>
13691433
</>
13701434
)}
13711435

@@ -1753,7 +1817,16 @@ void loop(){}`}</pre>
17531817
</div>
17541818
<div className="flex-1 overflow-auto">
17551819
<pre className="bg-neutral-950 rounded-xl p-6 overflow-auto text-sm whitespace-pre-wrap border border-neutral-700 shadow-inner">
1756-
{generateCppCode()}
1820+
{(() => {
1821+
try {
1822+
const code = generateCppCode();
1823+
console.log('Generated code length:', code.length);
1824+
return code || '// No code generated - please check console for errors';
1825+
} catch (error) {
1826+
console.error('Error generating code:', error);
1827+
return `// Error generating code: ${error.message}\n// Please check console for details`;
1828+
}
1829+
})()}
17571830
</pre>
17581831
</div>
17591832
<div className="mt-4 text-xs text-neutral-500 text-center">

src/components/PixelCanvas.jsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,22 @@ export default function PixelCanvas({
7676
const p = pixels[offset + x];
7777
if (!p) continue; // Skip if pixel is undefined
7878
const i = (y * width + x) * 4;
79-
buf[i] = p.a === 0 ? 0 : p.r;
80-
buf[i + 1] = p.a === 0 ? 0 : p.g;
81-
buf[i + 2] = p.a === 0 ? 0 : p.b;
82-
buf[i + 3] = p.a;
79+
// For transparent pixels, use the background color instead of black
80+
if (p.a === 0) {
81+
if (backgroundColor === "white") {
82+
buf[i] = 255; buf[i + 1] = 255; buf[i + 2] = 255; buf[i + 3] = 255;
83+
} else if (backgroundColor === "black") {
84+
buf[i] = 0; buf[i + 1] = 0; buf[i + 2] = 0; buf[i + 3] = 255;
85+
} else {
86+
// For transparent background, keep transparent
87+
buf[i] = 0; buf[i + 1] = 0; buf[i + 2] = 0; buf[i + 3] = 0;
88+
}
89+
} else {
90+
buf[i] = p.r;
91+
buf[i + 1] = p.g;
92+
buf[i + 2] = p.b;
93+
buf[i + 3] = p.a;
94+
}
8395
}
8496
}
8597
ctx.putImageData(imageData, 0, 0);

src/lib/packers.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function pack1bit(pixels, width, height, orientation = 'horizontal') {
3131
let cur = 0;
3232
for (let x = 0; x < width; x++) {
3333
const p = pixels[I(x, y)];
34-
const on = p.a > 0 && (p.r + p.g + p.b) < (255 * 3) / 2; // darker pixel → 1
34+
const on = p.a > 0 && (p.r + p.g + p.b) > (255 * 3) / 2; // lighter pixel → 1 (white = on)
3535
if (on) cur |= 1 << bit;
3636
bit--;
3737
if (bit < 0) {
@@ -51,7 +51,7 @@ export function pack1bit(pixels, width, height, orientation = 'horizontal') {
5151
const y = yPage * 8 + (7 - bit);
5252
if (y < height) {
5353
const p = pixels[I(x, y)];
54-
const on = p.a > 0 && (p.r + p.g + p.b) < (255 * 3) / 2;
54+
const on = p.a > 0 && (p.r + p.g + p.b) > (255 * 3) / 2; // lighter pixel → 1 (white = on)
5555
if (on) cur |= 1 << bit;
5656
}
5757
}
@@ -72,7 +72,7 @@ export function pack1bitAlpha(pixels, width, height, orientation = 'horizontal')
7272
let cur = 0;
7373
for (let x = 0; x < width; x++) {
7474
const p = pixels[I(x, y)];
75-
const hasAlpha = p.a > 127; // alpha threshold
75+
const hasAlpha = p.a > 127; // alpha threshold (opaque = on)
7676
if (hasAlpha) cur |= 1 << bit;
7777
bit--;
7878
if (bit < 0) {

0 commit comments

Comments
 (0)