Added 3 and 4 color examples which use 2-plane G5 images

This commit is contained in:
Larry Bank
2025-09-05 20:19:48 +01:00
parent f5c3662f5a
commit a2ee62dc2c
10 changed files with 439 additions and 62 deletions
+37
View File
@@ -0,0 +1,37 @@
//
// 3 color (Black/White/Red) example
//
#include <bb_epaper.h>
#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()
{
}
+29
View File
@@ -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};
+27
View File
@@ -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};
+16 -7
View File
@@ -2,18 +2,26 @@
// 3 color (Black/White/Red) example
//
#include <bb_epaper.h>
#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();
+173 -40
View File
@@ -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<h; y++) {
s = &pBMP[iPitch * y];
@@ -63,75 +144,96 @@ void ConvertTo1Bpp(uint8_t *pBMP, int w, int h, int iBpp, uint8_t *palette)
count = 8; // bits in a byte
u8 = 0; // start with all black
for (x=0; x<w; x++) { // slower code, but less code :)
u8 <<= 1;
u8 <<= iDestBpp;
switch (iBpp) {
case 24:
case 32:
g = (s[0] + s[1]*2 + s[2])/4; // gray value
r = s[0];
g = s[1];
b = s[2];
s += iDelta;
break;
case 16:
g = s[1] & 0xf8; // red
g += ((s[0] | s[1] << 8) << 2) & 0x1f0; // green x 2
g += (s[0] << 3) & 0xf8; // blue
g /= 4;
r = s[1] & 0xf8; // red
g = ((s[0] | s[1] << 8) >> 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 <PNG image> <g5 compressed image>\n");
if (argc != 4) {
printf("Usage: ./pngconvert <PNG image> <g5 compressed image> <mode>\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<h && rc == G5_SUCCESS; y++) {
rc = g5_encode_encodeLine(&g5enc, s);
s += iPitch;
}
} else { // split the bit planes
uint8_t *d, src, ucMask, uc = 0;
s = png.getBuffer();
rc = g5_encode_init(&g5enc, w, h*2, pOut, iPitch * h * 2);
// encode plane 0 first
for (y=0; y<h*2 && rc == G5_SUCCESS; y++) {
if (y == h) {
s = png.getBuffer(); // restart from the top
}
src = *s++;
d = u8Temp;
ucMask = (y < h) ? 0x40 : 0x80; // lower or upper source bit
for (int x=0; x<w; x++) {
uc <<= 1;
if (src & ucMask) {
uc |= 1;
}
src <<= 2;
if ((x & 3) == 3 && x != w-1) { // new input byte
src = *s++;
}
if ((x & 7) == 7) { // new output byte
*d++ = uc;
}
} // for x
*d = uc; // store last partial byte
rc = g5_encode_encodeLine(&g5enc, u8Temp);
}
}
iPitch = (w+7) >> 3;
pOut = (uint8_t *)malloc(iPitch * h);
s = png.getBuffer();
rc = g5_encode_init(&g5enc, w, h, pOut, iPitch * h);
for (y=0; y<h && rc == G5_SUCCESS; y++) {
rc = g5_encode_encodeLine(&g5enc, s);
s += iPitch;
}
if (rc == G5_ENCODE_COMPLETE) {
FILE *f;
iOutSize = g5_encode_getOutSize(&g5enc);
if (iMode != MODE_BW) iPitch *= 2;
printf("Input data size: %d bytes, compressed size: %d bytes\n", iPitch*h, iOutSize);
printf("Compression ratio: %2.1f:1\n", (float)(iPitch*h) / (float)iOutSize);
bbbm.u16Marker = BB_BITMAP_MARKER;
bbbm.u16Marker = (iMode == MODE_BW) ? BB_BITMAP_MARKER : BB_BITMAP2_MARKER;
bbbm.width = w;
bbbm.height = h;
bbbm.size = iOutSize;
@@ -204,7 +337,7 @@ int main(int argc, const char * argv[]) {
printf("Error opening: %s\n", argv[2]);
} else {
if (bHFile) { // generate HEX file to include in a project
StartHexFile(f, iOutSize+sizeof(BB_BITMAP), w, h, argv[2]);
StartHexFile(f, iOutSize+sizeof(BB_BITMAP), w, h, argv[2], iMode);
AddHexBytes(f, &bbbm, sizeof(BB_BITMAP), 0);
AddHexBytes(f, pOut, iOutSize, 1);
printf(".H file created successfully!\n");
+1
View File
@@ -148,6 +148,7 @@ typedef struct g5_enc_image_tag
// 16-bit marker at the start of a BB_BITMAP file
// (BitBank BitmapFile)
#define BB_BITMAP_MARKER 0xBBBF
#define BB_BITMAP2_MARKER 0xBBB2
// Font info per large character (glyph)
typedef struct {
+55 -13
View File
@@ -999,6 +999,37 @@ const uint8_t epd213z_init_full[] PROGMEM =
0 // end
}; /* epd213z_init_full */
const uint8_t epd213z_init_gray[] PROGMEM =
{
0x01, 0x12, // soft reset
BUSY_WAIT,
0x04, 0x01, 0xf9, 0x00, 0x00,
0x02, 0x011, 0x03,
0x03, 0x44, 0x00, 0x0f,
0x05, 0x45, 0x00, 0x00, 0xf9, 0x00,
0x02, 0x3c, 0x01,
BUSY_WAIT,
0x02, 0x18, 0x80,
0x02, 0x4e, 0x00,
0x03, 0x4f, 0x00, 0x00,
BUSY_WAIT,
91, 0x32, // LUT
0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT 0
0x96, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT 1
0x99, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT 2
0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT 3
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT VCOM
0x0a, 0x0a, 0x07, 0x02, 0x00, // phases for 0 A,B,C,D
0x02, 0x02, 0x00, 0x00, 0x00, // phases for 1 A,B
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, // 2-9 not needed
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0 // end
}; /* epd213z_init_gray */
const uint8_t epd213z_init_fast[] PROGMEM =
{
0x01, 0x12, // soft reset
@@ -1309,10 +1340,10 @@ const uint8_t epd295_init_sequence_part[] PROGMEM =
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
@@ -2226,6 +2257,7 @@ const EPD_PANEL panelDefs[] PROGMEM = {
{128, 296, 0, epd29z_init_full, epd29z_init_fast, epd29z_init_full, 0, BBEP_CHIP_SSD16xx, u8Colors_2clr}, // CrowPanel 2.9 (EP29Z_128x296)
{128, 296, 0, epd29z_init_gray, epd29z_init_gray, epd29z_init_gray, BBEP_4GRAY, BBEP_CHIP_SSD16xx, u8Colors_4gray}, // CrowPanel 2.9 (EP29Z_128x296_4GRAY)
{122, 250, 0, epd213z_init_full, epd213z_init_fast, epd213z_init_full, 0, BBEP_CHIP_SSD16xx, u8Colors_2clr}, // CrowPanel 2.13 (EP213Z_122x250)
{122, 250, 0, epd213z_init_gray, epd213z_init_gray, epd213z_init_gray, BBEP_4GRAY, BBEP_CHIP_SSD16xx, u8Colors_4gray}, // CrowPanel 2.13 (EP213Z_122x250) 4-gray mode
};
//
// Set the e-paper panel type
@@ -2236,11 +2268,27 @@ int bbepSetPanelType(BBEPDISP *pBBEP, int iPanel)
if (pBBEP == NULL || iPanel <= EP_PANEL_UNDEFINED || iPanel >= 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;
+97 -1
View File
@@ -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; tx<x+dx && tx < width; tx++) {
u8Color = ((u8 & src_mask) != 0);
(*pBBEP->pfnSetPixelFast)(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;
+2 -1
View File
@@ -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;
+2
View File
@@ -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