Skip to content

Commit

Permalink
Graphics: g.dump/asBMP can now output 16 bit images
Browse files Browse the repository at this point in the history
  • Loading branch information
gfwilliams committed Dec 6, 2024
1 parent 31ae87c commit 432be85
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 48 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
In SAVE_ON_FLASH builds (Microbit 1) remove getSerial, Math.LN*/LOG*SQRT* constants, passwords, Serial/I2C/SPI.find, Date.toUTCString
ESP32: add setIP and setAPIP
Graphics.wrapString fix issue with missing final char if immediately after a '.' or other char we can split after (#2572)
Graphics: g.dump/asBMP can now output 16 bit images

2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()'
Bangle.js2: After Bangle.showTestScreen, put Bangle.js into a hard off state (not soft off)
Expand Down
124 changes: 77 additions & 47 deletions libs/graphics/jswrap_graphics.c
Original file line number Diff line number Diff line change
Expand Up @@ -4145,64 +4145,93 @@ JsVar *jswrap_graphics_asBMP_X(JsVar *parent, bool printBase64) {
int rowstride = (((width*bpp)+31) >> 5) << 2; // padded to 32 bits
// palette length (byte size is 3x this)
int paletteEntries = hasPalette?(1<<bpp):0;
int headerLen = 14 + 12 + paletteEntries*3;
int headerLen;
if (bpp==16) { // Chrome doesn't like 16 bit BMPs in the other format
headerLen = 14 + 56;
} else {
headerLen = 14 + 12 + paletteEntries*3;
}
int fileSize = headerLen + height*rowstride;
// if printing base64 we only need enough memory for header + one row
int imgDataLen = printBase64 ? (headerLen + rowstride) : fileSize;
JsVar *imgData = jsvNewFlatStringOfLength((unsigned)imgDataLen);
if (!imgData) return 0; // not enough memory
unsigned char *imgPtr = (unsigned char *)jsvGetFlatStringPointer(imgData);
if (!imgPtr) return 0; // just in case
imgPtr[0]=66; //B
imgPtr[1]=77; //M
imgPtr[2]=(unsigned char)fileSize;
imgPtr[3]=(unsigned char)(fileSize>>8); // plus 2 more bytes for size
imgPtr[10]=(unsigned char)headerLen;
// maybe we want the InfoHeader, not BITMAPCOREHEADER (http://www.ece.ualberta.ca/~elliott/ee552/studentAppNotes/2003_w/misc/bmp_file_format/bmp_file_format.htm)
// Chrome doesn't like 16 bit BMPs in this format
// BITMAPCOREHEADER
imgPtr[14]=12; // sizeof(BITMAPCOREHEADER)
imgPtr[3]=(unsigned char)(fileSize>>8);
imgPtr[4]=(unsigned char)(fileSize>>16);
imgPtr[5]=(unsigned char)(fileSize>>24);
imgPtr[10]=(unsigned char)headerLen; // data offset
// size in here
imgPtr[18]=(unsigned char)width;
imgPtr[19]=(unsigned char)(width>>8);
imgPtr[20]=(unsigned char)height;
imgPtr[21]=(unsigned char)(height>>8);
imgPtr[22]=1; // color planes, should be 1
imgPtr[24]=(unsigned char)bpp; // bpp
if (hasPalette) {
// palette starts at 26
if (bpp==1) {
// first is white(?)
imgPtr[26]=255;
imgPtr[27]=255;
imgPtr[28]=255;
} else {
if (realBPP==3) {
for (int i=0;i<paletteEntries;i++) {
imgPtr[26 + (i*3)] = (i&1) ? 255 : 0;
imgPtr[27 + (i*3)] = (i&2) ? 255 : 0;
imgPtr[28 + (i*3)] = (i&4) ? 255 : 0;
}
#if defined(GRAPHICS_PALETTED_IMAGES)
} else if (realBPP==4) {
for (int i=0;i<16;i++) {
int p = PALETTE_4BIT[i];
imgPtr[26 + (i*3)] = (unsigned char)((p<<3)&0xF8);
imgPtr[27 + (i*3)] = (unsigned char)((p>>3)&0xFC);
imgPtr[28 + (i*3)] = (unsigned char)((p>>8)&0xF8);
}
} else if (realBPP==8) {
for (int i=0;i<255;i++) {
int p = PALETTE_8BIT[i];
imgPtr[26 + (i*3)] = (unsigned char)((p<<3)&0xF8);
imgPtr[27 + (i*3)] = (unsigned char)((p>>3)&0xFC);
imgPtr[28 + (i*3)] = (unsigned char)((p>>8)&0xF8);
}
#endif
} else { // otherwise default to greyscale
for (int i=0;i<(1<<realBPP);i++) {
unsigned char c = (unsigned char)(255 * i / (1<<realBPP));
imgPtr[26 + (i*3)] = c;
imgPtr[27 + (i*3)] = c;
imgPtr[28 + (i*3)] = c;
if (bpp==16) { // Chrome doesn't like 16 bit BMPs in the other format
// BITMAPINFOHEADER
const int h = 14; // initial header len
imgPtr[h+0]=56; // sizeof(BITMAPV3INFOHEADER)
imgPtr[h+8]=(unsigned char)height;
imgPtr[h+9]=(unsigned char)(height>>8);
imgPtr[h+12]=1; // planes
imgPtr[h+14]=16; // bits
imgPtr[h+16]=3; // compression BI_BITFIELDS
uint32_t size = height*rowstride;
imgPtr[h+20]=(unsigned char)(size);
imgPtr[h+21]=(unsigned char)(size>>8);
imgPtr[h+22]=(unsigned char)(size>>16);
imgPtr[h+23]=(unsigned char)(size>>24);
//imgPtr[h+40]=0x00;//R
imgPtr[h+41]=0xF8;
imgPtr[h+44]=0xE0;//G
imgPtr[h+45]=0x07;
imgPtr[h+48]=0x1F;//B
//imgPtr[h+49]=0x00;
} else {
// BITMAPCOREHEADER
imgPtr[14]=12; // sizeof(BITMAPCOREHEADER)
imgPtr[20]=(unsigned char)height;
imgPtr[21]=(unsigned char)(height>>8);
imgPtr[22]=1; // color planes, should be 1
imgPtr[24]=(unsigned char)bpp; // bpp
if (hasPalette) {
// palette starts at 26
if (bpp==1) {
// first is white(?)
imgPtr[26]=255;
imgPtr[27]=255;
imgPtr[28]=255;
} else {
if (realBPP==3) {
for (int i=0;i<paletteEntries;i++) {
imgPtr[26 + (i*3)] = (i&1) ? 255 : 0;
imgPtr[27 + (i*3)] = (i&2) ? 255 : 0;
imgPtr[28 + (i*3)] = (i&4) ? 255 : 0;
}
#if defined(GRAPHICS_PALETTED_IMAGES)
} else if (realBPP==4) {
for (int i=0;i<16;i++) {
int p = PALETTE_4BIT[i];
imgPtr[26 + (i*3)] = (unsigned char)((p<<3)&0xF8);
imgPtr[27 + (i*3)] = (unsigned char)((p>>3)&0xFC);
imgPtr[28 + (i*3)] = (unsigned char)((p>>8)&0xF8);
}
} else if (realBPP==8) {
for (int i=0;i<255;i++) {
int p = PALETTE_8BIT[i];
imgPtr[26 + (i*3)] = (unsigned char)((p<<3)&0xF8);
imgPtr[27 + (i*3)] = (unsigned char)((p>>3)&0xFC);
imgPtr[28 + (i*3)] = (unsigned char)((p>>8)&0xF8);
}
#endif
} else { // otherwise default to greyscale
for (int i=0;i<(1<<realBPP);i++) {
unsigned char c = (unsigned char)(255 * i / (1<<realBPP));
imgPtr[26 + (i*3)] = c;
imgPtr[27 + (i*3)] = c;
imgPtr[28 + (i*3)] = c;
}
}
}
}
Expand Down Expand Up @@ -4245,6 +4274,7 @@ JsVar *jswrap_graphics_asBMP_X(JsVar *parent, bool printBase64) {
idx += rowstride-bytesWritten;
// if printing to console, we're going to print everything as long as we have a multiple of 3 (or we're at the end)
if (printBase64 && idx>2) {
jshKickWatchDog(); // uploading can take a while
bool isLastRow = y==0;
int count = isLastRow ? idx : (idx-(idx%3));
JsVar *view = jsvNewArrayBufferFromString(imgData, (unsigned int)count); // create an arraybuffer - this means we can pass to btoa with zero allocations
Expand Down
3 changes: 2 additions & 1 deletion libs/graphics/lcd_fsmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1772,7 +1772,8 @@ void lcdFillRect_FSMC(JsGraphics *gfx, int x1, int y1, int x2, int y2, unsigned

unsigned int lcdGetPixel_FSMC(JsGraphics *gfx, int x, int y) {
lcdSetCursor(gfx,x,y);
lcdSetWrite(); // ?
LCD_WR_REG(0x2E); // start read
LCD_RD_Data(); // dummy read
return LCD_RD_Data();
}

Expand Down

0 comments on commit 432be85

Please sign in to comment.