From a2ee62dc2cdf2963beb644127962d25c1d0e6a74 Mon Sep 17 00:00:00 2001 From: Larry Bank Date: Sat, 23 Aug 2025 20:10:27 +0100 Subject: [PATCH] Added 3 and 4 color examples which use 2-plane G5 images --- examples/four_color/four_color.ino | 37 +++++ examples/four_color/smiley_hearts.h | 29 ++++ examples/three_color/smiley_bwr.h | 27 ++++ examples/three_color/three_color.ino | 23 ++- imageconvert/png_main.cpp | 213 ++++++++++++++++++++++----- src/Group5.h | 1 + src/bb_ep.inl | 68 +++++++-- src/bb_ep_gfx.inl | 98 +++++++++++- src/bb_epaper.cpp | 3 +- src/bb_epaper.h | 2 + 10 files changed, 439 insertions(+), 62 deletions(-) create mode 100644 examples/four_color/four_color.ino create mode 100644 examples/four_color/smiley_hearts.h create mode 100644 examples/three_color/smiley_bwr.h diff --git a/examples/four_color/four_color.ino b/examples/four_color/four_color.ino new file mode 100644 index 0000000..06c06b6 --- /dev/null +++ b/examples/four_color/four_color.ino @@ -0,0 +1,37 @@ +// +// 3 color (Black/White/Red) example +// +#include +#include "smiley_hearts.h" +BBEPAPER bbep(EP29YR_168x384); +// My Xiao ESP32C3 + LCD Adapter +#define DC_PIN 4 +#define BUSY_PIN 20 +#define RESET_PIN 5 +#define CS_PIN 21 + +void setup() +{ + bbep.initIO(DC_PIN, RESET_PIN, BUSY_PIN, CS_PIN, -1, -1); + bbep.allocBuffer(); // use a back buffer + // bbep.setRotation(270); + bbep.fillScreen(BBEP_WHITE); + bbep.setTextColor(BBEP_RED); + bbep.setFont(FONT_12x16); + bbep.println("bb_epaper v1"); + bbep.setTextColor(BBEP_BLACK); + bbep.println("B/W/Y/R Test"); + for (int i=1; i<44; i++) { + bbep.drawCircle(bbep.width()/2, 80, i, (i < 22) ? BBEP_BLACK : BBEP_RED); + } + bbep.loadG5Image(smiley_hearts, 0, 160, 0,0,1.0f); + bbep.writePlane(); + bbep.refresh(REFRESH_FULL); + bbep.wait(); + bbep.sleep(DEEP_SLEEP); +} + +void loop() +{ + +} \ No newline at end of file diff --git a/examples/four_color/smiley_hearts.h b/examples/four_color/smiley_hearts.h new file mode 100644 index 0000000..cbf73cd --- /dev/null +++ b/examples/four_color/smiley_hearts.h @@ -0,0 +1,29 @@ +// +// Created with imageconvert, written by Larry Bank +// 128 x 128 x 2-bits per pixel (split into 2 1-bit planes) +// compressed image data size = 362 bytes +// +const uint8_t smiley_hearts[] = { +0xb2,0xbb,0x80,0x00,0x80,0x00,0x62,0x01,0xe7,0x35,0x15,0x9c,0xc4,0x7a,0x72,0xd2, +0x68,0x20,0xe1,0x03,0x84,0x1c,0x20,0xe1,0x07,0x08,0x3a,0x70,0x83,0xa7,0x09,0xd0, +0x74,0xe9,0xd3,0xa7,0x4e,0x9d,0x3a,0x75,0xdd,0x18,0x76,0x18,0x82,0x3a,0x04,0x18, +0x20,0x6e,0x90,0x69,0xf4,0xd3,0x74,0x9a,0x6e,0x96,0xfd,0xab,0xa5,0xf9,0x84,0xb6, +0x99,0x15,0x74,0x10,0xb8,0x83,0x7a,0xde,0x96,0xfa,0xdb,0xaf,0xeb,0x6f,0xfd,0x7b, +0xeb,0xfb,0xeb,0xff,0xbe,0x9f,0xbd,0x6b,0xde,0xd3,0x5d,0xeb,0xb4,0xd7,0x7a,0xd0, +0x69,0x85,0xc3,0xc2,0x78,0x69,0x85,0xc1,0xe7,0x0d,0x22,0xcc,0x4f,0xd9,0x79,0x3e, +0x23,0xff,0xff,0xff,0xff,0xfd,0x5f,0xff,0xab,0xff,0xab,0xfd,0x5f,0xda,0xf6,0xbd, +0xaf,0x6b,0xda,0xd5,0xed,0x6a,0xf6,0xad,0x5a,0xd5,0xed,0x5a,0xb5,0x61,0x43,0x56, +0xad,0x58,0x50,0xd5,0xa8,0x61,0x43,0x0a,0x18,0x50,0xc2,0x86,0x14,0x18,0x28,0x30, +0x53,0x99,0x8d,0x4e,0x70,0x21,0x1c,0xa2,0x03,0xf9,0xcd,0xc4,0xb3,0x99,0x0e,0x67, +0x2e,0x24,0xc1,0x03,0xc2,0x0f,0x04,0x1e,0x10,0x7a,0x0f,0x08,0x3c,0x20,0xf4,0xf0, +0x9e,0x83,0xd3,0xd3,0xd3,0xc2,0x7a,0x7d,0xe9,0xe9,0xe9,0xe9,0xe9,0xfe,0x9e,0x9e, +0xbd,0xe9,0xfe,0x9f,0xe9,0xfe,0x9f,0xe9,0xff,0xe9,0xff,0xe9,0xff,0xeb,0xdf,0xff, +0xfe,0xbd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x5f,0xbf,0xf3,0x0b,0x13,0x24,0x1f, +0x09,0x43,0xf5,0x6d,0x6d,0x26,0xfd,0x2b,0xf4,0x9b,0xf5,0x6d,0x6d,0x04,0x44,0x51, +0x09,0xfd,0x6a,0x1b,0xf4,0x99,0x4a,0x62,0x75,0xb1,0xb5,0x1f,0x6d,0x2f,0xb4,0x96, +0xdb,0x5f,0x6d,0x25,0xb6,0xd2,0xfb,0x4b,0xb6,0x18,0x55,0xdb,0x49,0x6d,0xb4,0xbd, +0x86,0x10,0x4b,0x6d,0xa5,0xd8,0x61,0x84,0x97,0x61,0x84,0x96,0xc3,0x0c,0x20,0xbb, +0x60,0xc1,0x25,0xb0,0xc1,0x82,0x09,0x70,0xce,0x1a,0x1a,0x12,0xd8,0x62,0x17,0x61, +0x82,0x5b,0x39,0x00,0xc2,0xd9,0xc8,0xc4,0x16,0xc5,0x6d,0x6d,0x6d,0x6c,0x2c,0x35, +0xb5,0xb0,0xb0,0xd6,0x18,0x5b,0x58,0x61,0x61,0x85,0x83,0x05,0x86,0x16,0x0c,0xe4, +0x26,0x13,0x99,0x8c,0xe7,0x3a,0x0c,0xc7,0xe0,0x00}; diff --git a/examples/three_color/smiley_bwr.h b/examples/three_color/smiley_bwr.h new file mode 100644 index 0000000..19940b2 --- /dev/null +++ b/examples/three_color/smiley_bwr.h @@ -0,0 +1,27 @@ +// +// Created with imageconvert, written by Larry Bank +// 80 x 80 x 2-bits per pixel (split into 2 1-bit planes) +// compressed image data size = 333 bytes +// +const uint8_t smiley_bwr[] = { +0xb2,0xbb,0x50,0x00,0x50,0x00,0x45,0x01,0x3a,0x41,0x33,0x9e,0x29,0x04,0x0e,0x08, +0x1c,0x20,0xe1,0x1c,0x70,0xc0,0xe8,0xe2,0xca,0x07,0x08,0x10,0x37,0x41,0x06,0xe8, +0x20,0xdc,0x20,0x83,0x0e,0x93,0x74,0x83,0x74,0x13,0x7a,0x7a,0x4d,0xd2,0x30,0xca, +0x62,0x55,0xba,0x4b,0x6d,0xfb,0x56,0xe9,0x2d,0xf4,0xad,0x5b,0xd7,0xbd,0x7b,0xbd, +0x2e,0xf5,0xfb,0xd5,0xef,0xff,0xa5,0xed,0xfa,0xff,0xfd,0x27,0xa6,0xff,0xff,0xff, +0x4f,0xf7,0xa7,0xa4,0xd3,0x57,0xb4,0xfe,0x23,0xff,0xff,0xe6,0x18,0xcc,0xa1,0xfa, +0x69,0xff,0xee,0xb6,0x96,0xff,0xe9,0x37,0xf5,0xbf,0xd3,0xf6,0xd3,0x4d,0x2e,0x9a, +0x7f,0x86,0x17,0xb6,0xc9,0x13,0x44,0xc9,0x2f,0x16,0xd2,0x1a,0xdb,0x0c,0x24,0xb7, +0x61,0x84,0x15,0x6c,0x30,0xc2,0x4b,0x76,0x18,0x4a,0xb6,0xc1,0x97,0x8c,0x5b,0x6c, +0x42,0x4b,0x86,0x92,0xb6,0xd7,0x6c,0x30,0x92,0xb6,0x0c,0x12,0x56,0xcc,0x5a,0xa5, +0xb1,0x5b,0x61,0x2b,0x0d,0x2b,0x69,0x43,0x0c,0x20,0xac,0x30,0x95,0x86,0x12,0xb0, +0x60,0x82,0x86,0x71,0xc3,0x14,0x31,0x0a,0x18,0x50,0x61,0x41,0x9c,0xa1,0xe3,0xa3, +0x15,0x28,0xa1,0xff,0x3a,0x21,0x99,0xcf,0x14,0xc1,0x03,0xc2,0x0f,0x08,0x3c,0x20, +0xf4,0xf4,0x1e,0x13,0xd3,0xd3,0xd1,0x86,0x53,0x12,0xaf,0x4b,0x6f,0xda,0xbd,0x2d, +0xf5,0x6a,0xf5,0xef,0xf7,0xd2,0xef,0xff,0x57,0xbf,0xfd,0x7b,0xf5,0xff,0xf4,0xf4, +0xff,0xff,0xfd,0x3f,0xbd,0x3d,0x34,0xd7,0xb4,0xfe,0x23,0xff,0xff,0xe6,0x18,0xcc, +0xa1,0xfa,0x69,0xff,0xfa,0xda,0xdf,0xfa,0x4d,0xfa,0xdf,0xd3,0xf6,0x9a,0x6b,0xa6, +0x9f,0xc3,0x0b,0xdb,0x24,0x4d,0x13,0x24,0xb8,0xb6,0x90,0xf6,0xc3,0x09,0x2e,0xc3, +0x08,0x2e,0xc3,0x0c,0x24,0xbb,0x0c,0x25,0xdb,0x06,0x5e,0x31,0x6d,0x88,0x4b,0x86, +0x96,0xda,0xec,0x30,0x96,0xc1,0x82,0x5b,0x31,0x6a,0xb6,0x2b,0x61,0x61,0xad,0xac, +0x30,0xb0,0xc2,0xc3,0x0b,0x06,0x0b,0x3a,0x21,0x98,0xff,0xc0,0x00}; diff --git a/examples/three_color/three_color.ino b/examples/three_color/three_color.ino index 7442c4b..9c45115 100644 --- a/examples/three_color/three_color.ino +++ b/examples/three_color/three_color.ino @@ -2,18 +2,26 @@ // 3 color (Black/White/Red) example // #include +#include "smiley_bwr.h" //BBEPAPER bbep(EP154R_152x152); // 1.54" 152x152 B/W/R -BBEPAPER bbep(EP266YR_184x360); // 1.54" 152x152 B/W/R +BBEPAPER bbep(EP26R_152x296); +//BBEPAPER bbep(EP266YR_184x360); // 1.54" 152x152 B/W/R // My Arduino Nano 33 BLE e-paper adapter -#define DC_PIN 16 -#define BUSY_PIN 15 -#define RESET_PIN 14 -#define CS_PIN 10 +//#define DC_PIN 16 +//#define BUSY_PIN 15 +//#define RESET_PIN 14 +//#define CS_PIN 10 +// My Xiao + LCD Adapter +#define DC_PIN 4 +#define BUSY_PIN 20 +#define RESET_PIN 5 +#define CS_PIN 21 void setup() { - bbep.initIO(DC_PIN, RESET_PIN, BUSY_PIN, CS_PIN); + bbep.initIO(DC_PIN, RESET_PIN, BUSY_PIN, CS_PIN, -1, -1); bbep.allocBuffer(); // use a back buffer + // bbep.setRotation(270); bbep.fillScreen(BBEP_WHITE); bbep.setTextColor(BBEP_RED); bbep.setFont(FONT_12x16); @@ -21,8 +29,9 @@ void setup() bbep.setTextColor(BBEP_BLACK); bbep.println("B/W/R Test"); for (int i=1; i<44; i++) { - bbep.drawCircle(bbep.width()/2, bbep.height()/2, i, (i < 22) ? BBEP_BLACK : BBEP_RED); + bbep.drawCircle(bbep.width()/2, 64, i, (i < 22) ? BBEP_BLACK : BBEP_RED); } + bbep.loadG5Image(smiley_bwr, 0, 160, 0,0,1.0f); bbep.writePlane(); bbep.refresh(REFRESH_FULL); bbep.wait(); diff --git a/imageconvert/png_main.cpp b/imageconvert/png_main.cpp index 6d75aeb..2fd9bd7 100644 --- a/imageconvert/png_main.cpp +++ b/imageconvert/png_main.cpp @@ -10,16 +10,27 @@ #include "PNGdec.h" PNG png; // static instance of class +uint8_t u8Temp[1024]; +enum { + MODE_BW = 0, + MODE_BWR, + MODE_BWYR, + MODE_4GRAY +}; // // Create the comments and const array boilerplate for the hex data bytes // -void StartHexFile(FILE *f, int iLen, int w, int h, const char *fname) +void StartHexFile(FILE *f, int iLen, int w, int h, const char *fname, int iMode) { int i; char szTemp[256]; fprintf(f, "//\n// Created with imageconvert, written by Larry Bank\n"); - fprintf(f, "// %d x %d x 1-bit per pixel\n", w, h); + if (iMode == MODE_BW) { + fprintf(f, "// %d x %d x 1-bit per pixel\n", w, h); + } else { + fprintf(f, "// %d x %d x 2-bits per pixel (split into 2 1-bit planes)\n", w, h); + } fprintf(f, "// compressed image data size = %d bytes\n//\n", iLen); strcpy(szTemp, fname); i = strlen(szTemp); @@ -46,16 +57,86 @@ void AddHexBytes(FILE *f, void *pData, int iLen, int bLast) } } /* AddHexBytes() */ // +// Match the given pixel to black (00), white (01), or red (1x) +// +unsigned char GetBWRPixel(int r, int g, int b) +{ + uint8_t ucOut=0; + int gr; + + gr = (b + r + g*2)>>2; // gray + // match the color to closest of black/white/red + if (r > g && r > b) { // red is dominant + if (gr < 100 && r < 80) { + // black + } else { + if (r-b > 32 && r-g > 32) { + // is red really dominant? + ucOut |= 3; // red (can be 2 or 3, but 3 is compatible w/BWYR) + } else { // yellowish should be white + // no, use white instead of pink/yellow + ucOut |= 1; + } + } + } else { // check for white/black + if (gr >= 100) { + ucOut |= 1; // white + } else { + // black + } + } + return ucOut; +} /* GetBWRPixel() */ +// +// Match the given pixel to black (00), white (01), yellow (10), or red (11) +// returns 2 bit value of closest matching color +// +unsigned char GetBWYRPixel(int r, int g, int b) +{ + uint8_t ucOut=0; + int gr; + + gr = (b + r + g*2)>>2; // gray + // match the color to closest of black/white/yellow/red + if (r > b || g > b) { // red or yellow is dominant + if (gr < 90 && r < 80 && g < 80) { + // black + } else { + if (r-b > 32 && r-g > 32) { + // is red really dominant? + ucOut = 3; // red + } else if (r-b > 32 && g-b > 32) { + // yes, yellow + ucOut = 2; + } else { + ucOut = 1; // gray/white + } + } + } else { // check for white/black + if (gr >= 100) { + ucOut = 1; // white + } else { + // black + } + } + return ucOut; +} /* GetBWYRPixel() */ +// // The user passed a file with the wrong bit depth (not 1) // convert to 1-bpp by converting each color to 0 or 1 based on the gray level // -void ConvertTo1Bpp(uint8_t *pBMP, int w, int h, int iBpp, uint8_t *palette) +void ConvertBpp(uint8_t *pBMP, int iMode, int w, int h, int iBpp, uint8_t *palette) { - int g, x, y, iDelta, iPitch, iDestPitch; + int gray, r, g, b, x, y, iDelta, iPitch, iDestPitch, iDestBpp; uint8_t *s, *d, *pPal, u8, count; + iDestBpp = (iMode == MODE_BW) ? 1 : 2; iPitch = (w * iBpp)/8; - iDestPitch = (w+7)/8; + if (iDestBpp == 1) + iDestPitch = (w+7)/8; + else { + iDestPitch = (w+3)/4; + } iDelta = iBpp/8; for (y=0; y> 3) & 0xfc; // green + b = s[0] << 3; s += 2; break; case 8: if (palette) { pPal = &palette[s[0] * 3]; - g = (pPal[0] + pPal[1]*2 + pPal[2])/4; + r = pPal[0]; + g = pPal[1]; + b = pPal[2]; } else { - g = s[0]; + r = g = b = s[0]; } s++; break; case 4: if (palette) { if (x & 1) { - pPal = &palette[(s[0] & 0xf) * 4]; - g = (pPal[0] + pPal[1]*2 + pPal[2])/4; + pPal = &palette[(s[0] & 0xf) * 3]; s++; } else { - pPal = &palette[(s[0]>>4) * 4]; - g = (pPal[0] + pPal[1]*2 + pPal[2])/4; + pPal = &palette[(s[0]>>4) * 3]; } + r = pPal[0]; + g = pPal[1]; + b = pPal[2]; } else { if (x & 1) { - g = (s[0] & 0xf) | (s[0] << 4); + r = g = b = (s[0] & 0xf) | (s[0] << 4); } else { - g = (s[0] >> 4) | (s[0] & 0xf0); + r = g = b = (s[0] >> 4) | (s[0] & 0xf0); } } break; } // switch on bpp - if (g >= 128) u8 |= 1; // white - count--; + if (iMode == MODE_BW || iMode == MODE_4GRAY) { + gray = (r + g*2 + b)/4; + u8 |= gray >> (8-iDestBpp); + } else if (iMode == MODE_BWR) { + u8 |= GetBWRPixel(r, g, b); // match the closest colors + } else { + u8 |= GetBWYRPixel(r, g, b); + } + count -= iDestBpp; if (count == 0) { // byte is full, move on *d++ = u8; u8 = 0; count = 8; } } // for x - if (w & 7) { - *d++ = u8; // store last partial byte + if (count != 8) { + *d++ = (u8 << count); // store last partial byte } } // for y -} /* ConvertTo1Bpp() */ +} /* ConvertBpp() */ int main(int argc, const char * argv[]) { int w, h, y, rc, bpp; uint8_t *s, *pOut, *pData; - int iPitch, iOutSize, iDataSize; + int iPitch, iOutSize, iDataSize, iMode; FILE *ihandle; uint8_t *pPalette; G5ENCIMAGE g5enc; BB_BITMAP bbbm; int bHFile; // flag indicating if the output will be a .H file of hex data + const char *szModes[] = {"BW", "BWR", "BWYR", "4GRAY", NULL}; printf("PNG to Group5 image conversion tool\n"); - if (argc != 3) { - printf("Usage: ./pngconvert \n"); + if (argc != 4) { + printf("Usage: ./pngconvert \n"); + printf("valid modes: BW, BWR, BWYR, 4GRAY (case insensitive)\n"); + return -1; + } + iMode = 0; + while (szModes[iMode] && strcasecmp(szModes[iMode], argv[3]) != 0) { + iMode++; + } + if (szModes[iMode] == NULL) { + printf("Invalid mode\n"); return -1; } pOut = (uint8_t *)argv[2] + strlen(argv[2]) - 1; @@ -178,24 +280,55 @@ int main(int argc, const char * argv[]) { bpp = 32; break; } // switch - if (bpp != 1) { // need to convert it to 1-bpp - printf("Converting from %d-bpp to 1-bpp\n", bpp); - ConvertTo1Bpp(png.getBuffer(), w, h, bpp, pPalette); + if (iMode != MODE_BW) { // need to convert it to 1 or 2-bpp + printf("Converting pixels to %s\n", szModes[iMode]); + ConvertBpp(png.getBuffer(), iMode, w, h, bpp, pPalette); + } + iPitch = (w+7) >> 3; + pOut = (uint8_t *)malloc(iPitch * h * 2); + if (iMode == MODE_BW) { + s = png.getBuffer(); + rc = g5_encode_init(&g5enc, w, h, pOut, iPitch * h); + for (y=0; y> 3; - pOut = (uint8_t *)malloc(iPitch * h); - s = png.getBuffer(); - rc = g5_encode_init(&g5enc, w, h, pOut, iPitch * h); - for (y=0; y= EP_PANEL_COUNT) return BBEP_ERROR_BAD_PARAMETER; - pBBEP->native_width = pBBEP->width = panelDefs[iPanel].width; - pBBEP->native_height = pBBEP->height = panelDefs[iPanel].height; + switch (pBBEP->iOrientation) { + case 0: + case 180: + pBBEP->width = pBBEP->native_width = panelDefs[iPanel].width; + pBBEP->height = pBBEP->native_height = panelDefs[iPanel].height; + break; + case 90: + case 270: + pBBEP->width = pBBEP->native_height = panelDefs[iPanel].height; + pBBEP->height = pBBEP->native_width = panelDefs[iPanel].width; + break; + } + pBBEP->x_offset = panelDefs[iPanel].x_offset; pBBEP->chip_type = panelDefs[iPanel].chip_type; pBBEP->iFlags = panelDefs[iPanel].flags; + // If the memory is already allocated and the user is setting a + // panel type which needs 2 planes, assume they have been allocated + if (pBBEP->ucScreen && (pBBEP->iFlags & (BBEP_3COLOR | BBEP_4GRAY | BBEP_4COLOR))) { + pBBEP->iFlags |= BBEP_HAS_SECOND_PLANE; + } pBBEP->pInitFull = panelDefs[iPanel].pInitFull; pBBEP->pInitFast = panelDefs[iPanel].pInitFast; pBBEP->pInitPart = panelDefs[iPanel].pInitPart; @@ -2740,17 +2788,11 @@ void bbepSetRotation(BBEPDISP *pBBEP, int iRotation) switch (iRotation) { default: return; case 0: - pBBEP->width = pBBEP->native_width; - pBBEP->height = pBBEP->native_height; - break; - case 90: - pBBEP->width = pBBEP->native_height; - pBBEP->height = pBBEP->native_width; - break; case 180: pBBEP->width = pBBEP->native_width; pBBEP->height = pBBEP->native_height; break; + case 90: case 270: pBBEP->width = pBBEP->native_height; pBBEP->height = pBBEP->native_width; diff --git a/src/bb_ep_gfx.inl b/src/bb_ep_gfx.inl index c5e69cd..ed5974a 100644 --- a/src/bb_ep_gfx.inl +++ b/src/bb_ep_gfx.inl @@ -398,6 +398,22 @@ BBEPDISP *pBBEP = (BBEPDISP *)pb; pBBEP->ucScreen[i] = u8; } /* bbepSetPixelFast4Clr() */ +// Fast version (no pointer verification + no boundary checking) +// special version for logical ORing 2 planes together +void bbepSetPixelFast4ClrV2(void *pb, int x, int y, unsigned char ucColor) +{ +int i; +int iPitch; +uint8_t u8; +BBEPDISP *pBBEP = (BBEPDISP *)pb; + + iPitch = (pBBEP->width+3)>>2; + i = (x >> 2) + (y * iPitch); + u8 = pBBEP->ucScreen[i]; + u8 |= ucColor << (1+((3-(x & 3))*2)); + pBBEP->ucScreen[i] = u8; +} /* bbepSetPixelFast4ClrV2() */ + int bbepSetPixel4Gray(void *pb, int x, int y, unsigned char ucColor) { int i; @@ -625,6 +641,84 @@ void InvertBytes(uint8_t *pData, uint8_t bLen) } } /* InvertBytes() */ // +// 2-plane G5 image +// requires a local framebuffer +// +int bbepLoadG5_2Bit(BBEPDISP *pBBEP, const uint8_t *pG5, int x, int y, float fScale) +{ + uint16_t rc, tx, ty, cx, cy, dx, dy, size; + int row, width, height; + BB_BITMAP *pbbb; + uint8_t *pOldBuffer; + BB_SET_PIXEL_FAST *pOldPixel; + uint32_t u32Frac, u32XAcc, u32YAcc; // integer fraction vars + + pbbb = (BB_BITMAP *)pG5; + u32Frac = (uint32_t)(65536.0f / fScale); // calculate the fraction to advance the destination x/y + cx = pgm_read_word(&pbbb->width); + cy = pgm_read_word(&pbbb->height); + width = pBBEP->width; + height = pBBEP->height; + // Calculate scaled destination size + dx = (int)(fScale * (float)cx); + dy = (int)(fScale * (float)cy); + size = pgm_read_word(&pbbb->size); + rc = g5_decode_init(&g5dec, cx, cy*2, (uint8_t *)&pbbb[1], size); + pOldBuffer = pBBEP->ucScreen; // keep old pointer + pOldPixel = pBBEP->pfnSetPixelFast; + if (!(pBBEP->iFlags & BBEP_4COLOR)) { + pBBEP->pfnSetPixelFast = bbepSetPixelFast2Clr; + } + for (int iPlane = 0; iPlane < 2; iPlane++) { + // Treat it as 2 1-bit planes + if (iPlane == 1) { + if (pBBEP->iFlags & BBEP_4COLOR) { + pBBEP->pfnSetPixelFast = bbepSetPixelFast4ClrV2; + } else { + pBBEP->ucScreen += (((pBBEP->native_width+7)/8) * pBBEP->native_height); + } + } + g5_decode_line(&g5dec, u8Cache); // decode first line to start + u32YAcc = 0; //65536; // force first line to get decoded + row = 1; + for (ty=y; ty<(y+dy) && ty < height; ty++) { + uint8_t u8, *s, u8Color, src_mask; + while (u32YAcc >= 65536) { // advance to next source line + g5_decode_line(&g5dec, u8Cache); + row++; + u32YAcc -= 65536; + } + s = u8Cache; + u32XAcc = 0; + u8 = *s++; // grab first source byte (8 pixels) + src_mask = 0x80; + for (tx=x; txpfnSetPixelFast)(pBBEP, tx, ty, u8Color); + u32XAcc += u32Frac; + while (u32XAcc >= 65536) { + u32XAcc -= 65536; // whole source pixel horizontal movement + src_mask >>= 1; + if (src_mask == 0) { // need to load the next byte + u8 = *s++; + src_mask = 0x80; + } + } + } // for tx + u32YAcc += u32Frac; + } // for y + while (row < cy) { + g5_decode_line(&g5dec, u8Cache); // make sure we reach the 2nd plane + row++; + } + } // for each plane +// Restore pointers + pBBEP->ucScreen = pOldBuffer; + pBBEP->pfnSetPixelFast = pOldPixel; + + return BBEP_SUCCESS; +} /* bbepLoadG5_2Bit() */ +// // Load a 1-bpp Group5 compressed bitmap // Pass the pointer to the beginning of the G5 file // If the FG == BG color, and there is a back buffer, it will @@ -646,6 +740,9 @@ int bbepLoadG5(BBEPDISP *pBBEP, const uint8_t *pG5, int x, int y, int iFG, int i iBG = pBBEP->pColorLookup[iBG & 0xf]; } pbbb = (BB_BITMAP *)pG5; + if (pgm_read_word(&pbbb->u16Marker) == BB_BITMAP2_MARKER) { + return bbepLoadG5_2Bit(pBBEP, pG5, x, y, fScale); + } if (pgm_read_word(&pbbb->u16Marker) != BB_BITMAP_MARKER) return BBEP_ERROR_BAD_DATA; u32Frac = (uint32_t)(65536.0f / fScale); // calculate the fraction to advance the destination x/y cx = pgm_read_word(&pbbb->width); @@ -1989,7 +2086,6 @@ void bbepEllipse(BBEPDISP *pBBEP, int iCenterX, int iCenterY, int32_t iRadiusX, pBBEP->last_error = BBEP_ERROR_BAD_PARAMETER; return; // invalid radii } - ucColor = pBBEP->pColorLookup[ucColor & 0xf]; if (iRadiusX > iRadiusY) {// use X as the primary radius iRadius = iRadiusX; iXFrac = 65536; diff --git a/src/bb_epaper.cpp b/src/bb_epaper.cpp index bc75b5b..24e6d2c 100644 --- a/src/bb_epaper.cpp +++ b/src/bb_epaper.cpp @@ -102,9 +102,10 @@ int rc = BBEP_ERROR_BAD_PARAMETER; } break; case EPD_CROWPANEL213: + case EPD_CROWPANEL213_4GRAY: pinMode(7, OUTPUT); digitalWrite(7, HIGH); // screen power on - if (setPanelType(EP213Z_122x250) == BBEP_SUCCESS) { + if (setPanelType((iProduct == EPD_CROWPANEL213) ? EP213Z_122x250 : EP213Z_122x250_4GRAY) == BBEP_SUCCESS) { initIO(13, 10, 9, 14, 11, 12, 12000000); setRotation(270); return BBEP_SUCCESS; diff --git a/src/bb_epaper.h b/src/bb_epaper.h index e279ec9..09eba64 100644 --- a/src/bb_epaper.h +++ b/src/bb_epaper.h @@ -123,6 +123,7 @@ enum { EPD_CROWPANEL29, EPD_CROWPANEL29_4GRAY, EPD_CROWPANEL213, + EPD_CROWPANEL213_4GRAY, EPD_PRODUCT_COUNT }; @@ -176,6 +177,7 @@ enum { EP29Z_128x296, // SSD1680 (CrowPanel 2.9") EP29Z_128x296_4GRAY, // SSD1680 (CrowPanel 2.9") EP213Z_122x250, // SSD1680 (CrowPanel 2.13") + EP213Z_122x250_4GRAY, // CrowPanel 2.13" 4 gray mode EP_PANEL_COUNT }; #ifdef FUTURE