Added support for CrowPanel 2.13 & 2.9, more compatibility with other display library APIs

This commit is contained in:
Larry Bank
2025-09-05 20:18:53 +01:00
parent 4057dcde92
commit 36ec4ae9d6
10 changed files with 1230 additions and 200 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ CC = gcc
CFLAGS = -Wall -I/usr/local/include/freetype2 -I/usr/include/freetype2 -I/usr/include -I/opt/homebrew/include/freetype2
LIBS = -L/opt/homebrew/lib -lfreetype
fontconvert: main.c
fontconvert: main.c ../src/Group5.h
$(CC) $(CFLAGS) main.c $(LIBS) -o fontconvert
strip fontconvert
+84 -48
View File
@@ -91,12 +91,15 @@ int RotateBitmap(int iAngle, uint8_t *pSrc, int iWidth, int iHeight, int iSrcPit
//
// Create the comments and const array boilerplate for the hex data bytes
//
void StartHexFile(FILE *f, int iLen, const char *fname)
void StartHexFile(FILE *f, int iLen, const char *fname, int size, int first, int last)
{
int i, j;
char szTemp[256];
fprintf(f, "//\n// Created with fontconvert, written by Larry Bank\n");
fprintf(f, "// Point size: %d, first: %d, last: %d\n", size, first, last);
fprintf(f, "// compressed font data size = %d bytes\n//\n", iLen);
fprintf(f, "// for non-Arduino builds...\n#ifndef PROGMEM\n#define PROGMEM\n#endif\n");
strcpy(szTemp, fname);
i = strlen(szTemp);
if (szTemp[i-2] == '.') szTemp[i-2] = 0; // get the leaf name for the data
@@ -106,7 +109,7 @@ void StartHexFile(FILE *f, int iLen, const char *fname)
j--;
}
if (szTemp[j] == '/') j++;
fprintf(f, "const uint8_t %s[] = {\n", &szTemp[j]);
fprintf(f, "const uint8_t %s[] PROGMEM = {\n", &szTemp[j]);
} /* StartHexFile() */
//
// Add N bytes of hex data to the output
@@ -162,8 +165,12 @@ int main(int argc, char *argv[])
FT_Bitmap *bitmap;
FT_BitmapGlyphRec *g;
// BitBank Font structures
int bSmallFont = 0; // indicates if we're creating a normal or small font file
BB_GLYPH *pGlyphs;
BB_GLYPH_SMALL *pSmallGlyphs;
BB_FONT bbff;
BB_FONT_SMALL bbf2;
int bHFile; // flag indicating if the output will be a .H file of hex data
if (argc < 4) {
@@ -171,6 +178,7 @@ int main(int argc, char *argv[])
return 1;
}
size = atoi(argv[3]);
bSmallFont = (size < 60); // Glyph info can fit in signed 8-bit values
pTemp = (uint8_t *)argv[2] + strlen(argv[2]) - 1;
bHFile = (pTemp[0] == 'H' || pTemp[0] == 'h'); // output an H file?
@@ -191,10 +199,18 @@ int main(int argc, char *argv[])
printf("Something went wrong - the starting character comes after the ending character. Try again...\n");
return 1;
}
pGlyphs = (BB_GLYPH *)malloc((last - first + 1) * sizeof(BB_GLYPH));
if (!pGlyphs) {
printf("Error allocating memory for glyph data\n");
return 1;
if (bSmallFont) {
pSmallGlyphs = (BB_GLYPH_SMALL *)malloc((last - first + 1) * sizeof(BB_GLYPH_SMALL));
if (!pSmallGlyphs) {
printf("Error allocating memory for glyph data\n");
return 1;
}
} else {
pGlyphs = (BB_GLYPH *)malloc((last - first + 1) * sizeof(BB_GLYPH));
if (!pGlyphs) {
printf("Error allocating memory for glyph data\n");
return 1;
}
}
pBitmap = (uint8_t *)malloc(OUTBUF_SIZE); // Enough to hold the output
pTemp = (uint8_t *)malloc(OUTBUF_SIZE); // for rotated bitmaps
@@ -259,27 +275,21 @@ int main(int argc, char *argv[])
bitmap = &face->glyph->bitmap;
g = (FT_BitmapGlyphRec *)glyph;
// Minimal font and per-glyph information is stored to
// reduce flash space requirements. Glyph bitmaps are
// fully bit-packed; no per-scanline pad, though end of
// each character may be padded to next byte boundary
// when needed. 16-bit offset means 64K max for bitmaps,
// code currently doesn't check for overflow. (Doesn't
// check that size & offsets are within bounds either for
// that matter...please convert fonts responsibly.)
// Check that the requested font size fits within the limits
// of the 8-bit member variables
if (bitmap->width > 255 || (face->glyph->advance.x>>6) > 255) {
printf("The requested size is too large to fit in the BB_GLYPH member vars\nEither request a smaller size or change the member variables to be 16-bits\n");
return -1;
if (bSmallFont) {
pSmallGlyphs[index].bitmapOffset = iOffset;
pSmallGlyphs[index].width = bitmap->width;
pSmallGlyphs[index].height = bitmap->rows;
pSmallGlyphs[index].xAdvance = face->glyph->advance.x >> 6;
pSmallGlyphs[index].xOffset = g->left;
pSmallGlyphs[index].yOffset = 1 - g->top;
} else {
pGlyphs[index].bitmapOffset = iOffset;
pGlyphs[index].width = bitmap->width;
pGlyphs[index].height = bitmap->rows;
pGlyphs[index].xAdvance = face->glyph->advance.x >> 6;
pGlyphs[index].xOffset = g->left;
pGlyphs[index].yOffset = 1 - g->top;
}
pGlyphs[index].bitmapOffset = iOffset;
pGlyphs[index].width = bitmap->width;
pGlyphs[index].height = bitmap->rows;
pGlyphs[index].xAdvance = face->glyph->advance.x >> 6;
pGlyphs[index].xOffset = g->left;
pGlyphs[index].yOffset = 1 - g->top;
s = bitmap->buffer;
iPitch = bitmap->pitch;
if (iRotation != 0) {
@@ -308,29 +318,55 @@ int main(int argc, char *argv[])
return 1;
}
// Write the file header
bbff.u16Marker = BB_FONT_MARKER;
bbff.first = first;
bbff.last = last;
bbff.rotation = iRotation; // save rotation angle
if (face->size->metrics.height == 0) {
// No face height info, assume fixed width and get from a glyph.
bbff.height = pGlyphs[0].height;
if (bSmallFont) {
bbf2.u16Marker = BB_FONT_MARKER_SMALL;
bbf2.first = first;
bbf2.last = last;
bbf2.rotation = iRotation; // save rotation angle
if (face->size->metrics.height == 0) {
// No face height info, assume fixed width and get from a glyph.
bbf2.height = pGlyphs[0].height;
} else {
bbf2.height = (face->size->metrics.height >> 6);
}
iLen = sizeof(bbf2) + (last-first+1)*sizeof(BB_GLYPH_SMALL) + iOffset;
if (bHFile) { // create an H file of hex values
StartHexFile(fOut, iLen, argv[2], size, first, last);
AddHexBytes(fOut, &bbf2, sizeof(bbf2), 0);
AddHexBytes(fOut, pSmallGlyphs, (last-first+1) * sizeof(BB_GLYPH_SMALL), 0);
AddHexBytes(fOut, pBitmap, iOffset, 1);
} else {
fwrite(&bbf2, 1, sizeof(bbf2), fOut);
// Write the glyph table
fwrite(pSmallGlyphs, (last-first+1), sizeof(BB_GLYPH_SMALL), fOut);
// Write the compressed bitmap data
fwrite(pBitmap, 1, iOffset, fOut);
}
} else {
bbff.height = (face->size->metrics.height >> 6);
}
iLen = sizeof(bbff) + (last-first+1)*sizeof(BB_GLYPH) + iOffset;
if (bHFile) { // create an H file of hex values
StartHexFile(fOut, iLen, argv[2]);
AddHexBytes(fOut, &bbff, sizeof(bbff), 0);
AddHexBytes(fOut, pGlyphs, (last-first+1) * sizeof(BB_GLYPH), 0);
AddHexBytes(fOut, pBitmap, iOffset, 1);
} else {
fwrite(&bbff, 1, sizeof(bbff), fOut);
// Write the glyph table
fwrite(pGlyphs, (last-first+1), sizeof(BB_GLYPH), fOut);
// Write the compressed bitmap data
fwrite(pBitmap, 1, iOffset, fOut);
}
bbff.u16Marker = BB_FONT_MARKER;
bbff.first = first;
bbff.last = last;
bbff.rotation = iRotation; // save rotation angle
if (face->size->metrics.height == 0) {
// No face height info, assume fixed width and get from a glyph.
bbff.height = pGlyphs[0].height;
} else {
bbff.height = (face->size->metrics.height >> 6);
}
iLen = sizeof(bbff) + (last-first+1)*sizeof(BB_GLYPH) + iOffset;
if (bHFile) { // create an H file of hex values
StartHexFile(fOut, iLen, argv[2], size, first, last);
AddHexBytes(fOut, &bbff, sizeof(bbff), 0);
AddHexBytes(fOut, pGlyphs, (last-first+1) * sizeof(BB_GLYPH), 0);
AddHexBytes(fOut, pBitmap, iOffset, 1);
} else {
fwrite(&bbff, 1, sizeof(bbff), fOut);
// Write the glyph table
fwrite(pGlyphs, (last-first+1), sizeof(BB_GLYPH), fOut);
// Write the compressed bitmap data
fwrite(pBitmap, 1, iOffset, fOut);
}
} // large fonts
fflush(fOut);
fclose(fOut); // done!
FT_Done_FreeType(library);
+40 -13
View File
@@ -12,14 +12,18 @@
// Written by Larry Bank
// Copyright (c) 2024 BitBank Software, Inc.
//
// Use of this software is governed by the Business Source License
// included in the file ./LICENSE.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// ./APL.txt.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===========================================================================
//
// The name "Group5" is derived from the CCITT Group4 standard
// This code is based on a lot of the good ideas from CCITT T.6
// for FAX image compression, but modified to work in a very
@@ -111,13 +115,13 @@ inline uint32_t TIFFMOTOLONG(uint8_t *p)
// G5 Encoder
//
typedef struct pil_buffered_bits
typedef struct g5_buffered_bits
{
unsigned char *pBuf; // buffer pointer
uint32_t ulBits; // buffered bits
uint32_t ulBitOff; // current bit offset
uint32_t ulDataSize; // available data
} BUFFERED_BITS;
} G5_BUFFERED_BITS;
//
// Encoder state
@@ -131,7 +135,7 @@ typedef struct g5_enc_image_tag
int iDataSize; // generated output size
uint8_t *pOutBuf;
int16_t *pCur, *pRef; // pointers to swap current and reference lines
BUFFERED_BITS bb;
G5_BUFFERED_BITS bb;
int16_t CurFlips[MAX_IMAGE_FLIPS];
int16_t RefFlips[MAX_IMAGE_FLIPS];
} G5ENCIMAGE;
@@ -139,20 +143,33 @@ typedef struct g5_enc_image_tag
// 16-bit marker at the start of a BB_FONT file
// (BitBank FontFile)
#define BB_FONT_MARKER 0xBBFF
#define BB_FONT_MARKER_SMALL 0xBBF2
// 16-bit marker at the start of a BB_BITMAP file
// (BitBank BitmapFile)
#define BB_BITMAP_MARKER 0xBBBF
// Font info per character (glyph)
// Font info per large character (glyph)
typedef struct {
uint16_t bitmapOffset; // Offset to compressed bitmap data for this glyph (starting from the end of the BB_GLYPH[] array)
uint8_t width; // bitmap width in pixels
uint8_t xAdvance; // total width in pixels (bitmap + padding)
uint16_t width; // bitmap width in pixels
uint16_t xAdvance; // total width in pixels (bitmap + padding)
uint16_t height; // bitmap height in pixels
int16_t xOffset; // left padding to upper left corner
int16_t yOffset; // padding from baseline to upper left corner (usually negative)
} BB_GLYPH;
// Font info per small character (glyph)
typedef struct bbg_small {
uint16_t bitmapOffset; // Offset to compressed bitmap data for this glyph (starting from the end of the BB_GLYPH[] array)
uint8_t width; // bitmap width in pixels
uint8_t xAdvance; // total width in pixels (bitmap + padding)
uint8_t height; // bitmap height in pixels
int8_t xOffset; // left padding to upper left corner
int8_t yOffset; // padding from baseline to upper left corner (usually negative)
int8_t struct_padding; // pad the structure to 8 bytes to prevent alignment problems on different target compilers/settings
} BB_GLYPH_SMALL;
// This structure is stored at the beginning of a BB_FONT file
typedef struct {
uint16_t u16Marker; // 16-bit Marker defining a BB_FONT file
@@ -163,6 +180,16 @@ typedef struct {
BB_GLYPH glyphs[]; // Array of glyphs (one for each char)
} BB_FONT;
// This structure is stored at the beginning of a BB_FONT_SMALL file
typedef struct {
uint16_t u16Marker; // 16-bit Marker defining a BB_FONT_SMALL file
uint16_t first; // first char (ASCII value)
uint16_t last; // last char (ASCII value)
uint16_t height; // total height of font
uint32_t rotation; // store this as 32-bits to not have a struct packing problem
BB_GLYPH_SMALL glyphs[]; // Array of glyphs (one for each char)
} BB_FONT_SMALL;
// This structure defines the start of a compressed bitmap file
typedef struct {
uint16_t u16Marker; // 16-bit marker defining a BB_BITMAP file
+6 -2
View File
@@ -64,10 +64,14 @@ void bbepInitIO(BBEPDISP *pBBEP, uint8_t u8DC, uint8_t u8RST, uint8_t u8BUSY, ui
SPI.begin(); // other architectures have fixed SPI pins
#endif
SPI.beginTransaction(SPISettings(u32Speed, MSBFIRST, SPI_MODE0));
#ifdef ARDUINO_ARCH_ESP32
// For NRF52, you have to leave an 'open' transaction
SPI.endTransaction(); // N.B. - if you call beginTransaction() again without a matching endTransaction(), it will hang on ESP32
#endif
pBBEP->is_awake = 1;
// Before we can start sending pixels, many panels need to know the display resolution
bbepSendCMDSequence(pBBEP, pBBEP->pInitFull);
if (pBBEP->iFlags & BBEP_7COLOR) { // need to send before you can send it data
pBBEP->is_awake = 1;
bbepSendCMDSequence(pBBEP, pBBEP->pInitFull);
if (pBBEP->iFlags & BBEP_SPLIT_BUFFER) {
// Send the same sequence to the second controller
pBBEP->iCSPin = pBBEP->iCS2Pin;
+719 -56
View File
File diff suppressed because it is too large Load Diff
+210 -27
View File
@@ -25,7 +25,7 @@
static G5DECIMAGE g5dec;
// forward declarations
void InvertBytes(uint8_t *pData, uint8_t bLen);
void bbepUnicodeString(const char *szMsg, uint8_t *szExtMsg);
const uint8_t ucFont[]PROGMEM = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x5f,0x5f,0x06,0x00,
0x00,0x07,0x07,0x00,0x07,0x07,0x00,0x14,0x7f,0x7f,0x14,0x7f,0x7f,0x14,
@@ -174,6 +174,77 @@ const uint8_t ucSmallFont[] PROGMEM = {
0x00,0x41,0x41,0x3e,0x08,
0x02,0x01,0x02,0x01,0x00,
0x3c,0x26,0x23,0x26,0x3c};
//
// Get the size of text in a custom font area
//
void bbepGetStringBox(BBEPDISP *pBBEP, const char *szMsg, BB_RECT *pRect)
{
int cx = 0;
unsigned int c, i = 0;
BB_FONT *pBBF;
BB_FONT_SMALL *pBBFS;
BB_GLYPH *pGlyph;
BB_GLYPH_SMALL *pSmallGlyph;
int miny, maxy;
uint8_t szExtMsg[80];
miny = 4000; maxy = 0;
if (pBBEP == NULL || pRect == NULL || szMsg == NULL) return; // bad pointers
if (pBBEP->pFont == NULL) { // built-in font
miny = 0;
switch (pBBEP->iFont) {
case FONT_6x8:
cx = 6;
maxy = 8;
break;
case FONT_8x8:
cx = 8;
maxy = 8;
break;
case FONT_12x16:
cx = 12;
maxy = 16;
break;
case FONT_16x16:
cx = 16;
maxy = 16;
break;
}
cx *= strlen(szMsg);
} else { // proportional fonts
bbepUnicodeString(szMsg, szExtMsg); // convert to extended ASCII
if (pgm_read_word(pBBEP->pFont) == BB_FONT_MARKER) {
pBBF = (BB_FONT *)pBBEP->pFont; pBBFS = NULL;
} else { // small font
pBBFS = (BB_FONT_SMALL *)pBBEP->pFont; pBBF = NULL;
}
while (szExtMsg[i]) {
c = szExtMsg[i++];
if (pBBF) { // big font
if (c < pgm_read_byte(&pBBF->first) || c > pgm_read_byte(&pBBF->last)) // undefined character
continue; // skip it
c -= pgm_read_byte(&pBBF->first); // first char of font defined
pGlyph = &pBBF->glyphs[c];
cx += pgm_read_word(&pGlyph->xAdvance);
if ((int16_t)pgm_read_word(&pGlyph->yOffset) < miny) miny = pgm_read_word(&pGlyph->yOffset);
if (pgm_read_word(&pGlyph->height)+(int16_t)pgm_read_word(&pGlyph->yOffset) > maxy) maxy = pgm_read_word(&pGlyph->height)+(int16_t)pgm_read_word(&pGlyph->yOffset);
} else { // small font
if (c < pgm_read_byte(&pBBFS->first) || c > pgm_read_byte(&pBBFS->last)) // undefined character
continue; // skip it
c -= pgm_read_byte(&pBBFS->first); // first char of font defined
pSmallGlyph = &pBBFS->glyphs[c];
cx += pgm_read_byte(&pSmallGlyph->xAdvance);
if ((int8_t)pgm_read_byte(&pSmallGlyph->yOffset) < miny) miny = (int8_t)pgm_read_byte(&pSmallGlyph->yOffset);
if (pgm_read_byte(&pSmallGlyph->height)+(int8_t)pgm_read_byte(&pSmallGlyph->yOffset) > maxy) maxy = pgm_read_byte(&pSmallGlyph->height)+(int8_t)pgm_read_byte(&pSmallGlyph->yOffset);
} // small
}
} // prop fonts
pRect->w = cx;
pRect->x = pBBEP->iCursorX;
pRect->y = pBBEP->iCursorY + miny;
pRect->h = maxy - miny + 1;
} /* bbepGetStringBox() */
//
// Draw a sprite of any size in any position
@@ -327,6 +398,61 @@ BBEPDISP *pBBEP = (BBEPDISP *)pb;
pBBEP->ucScreen[i] = u8;
} /* bbepSetPixelFast4Clr() */
int bbepSetPixel4Gray(void *pb, int x, int y, unsigned char ucColor)
{
int i;
int iPitch, iSize;
BBEPDISP *pBBEP = (BBEPDISP *)pb;
const uint8_t u8Mask = 0x80 >> (x & 7);
// only available for local buffer operations
if (!pBBEP || !pBBEP->ucScreen) return BBEP_ERROR_BAD_PARAMETER;
ucColor = pBBEP->pColorLookup[ucColor & 0xf]; // translate the color for this display type
iPitch = (pBBEP->width+7)>>3;
iSize = ((pBBEP->native_width+7)>>3) * pBBEP->native_height;
i = (x >> 3) + (y * iPitch);
if (x < 0 || x >= pBBEP->width || i < 0 || i > iSize-1) { // off the screen
pBBEP->last_error = BBEP_ERROR_BAD_PARAMETER;
return BBEP_ERROR_BAD_PARAMETER;
}
if (ucColor & 1) { // first plane
pBBEP->ucScreen[i] |= u8Mask;
} else {
pBBEP->ucScreen[i] &= ~u8Mask;
}
if (ucColor & 2) { // second plane
pBBEP->ucScreen[iSize + i] |= u8Mask;
} else {
pBBEP->ucScreen[iSize + i] &= ~u8Mask;
}
return BBEP_SUCCESS;
} /* bbepSetPixel4Gray() */
void bbepSetPixelFast4Gray(void *pb, int x, int y, unsigned char ucColor)
{
int i;
int iPitch, iSize;
BBEPDISP *pBBEP = (BBEPDISP *)pb;
const uint8_t u8Mask = 0x80 >> (x & 7);
iPitch = (pBBEP->width+7)>>3;
iSize = ((pBBEP->native_width+7)>>3) * pBBEP->native_height;
i = (x >> 3) + (y * iPitch);
if (ucColor & 1) { // first plane
pBBEP->ucScreen[i] |= u8Mask;
} else {
pBBEP->ucScreen[i] &= ~u8Mask;
}
if (ucColor & 2) { // second plane
pBBEP->ucScreen[iSize + i] |= u8Mask;
} else {
pBBEP->ucScreen[iSize + i] &= ~u8Mask;
}
} /* bbepSetPixelFast4Gray() */
int bbepSetPixel3Clr(void *pb, int x, int y, unsigned char ucColor)
{
int i;
@@ -959,13 +1085,16 @@ uint16_t u16CP; // 16-bit codepoint encoded by the multi-byte sequence
//
// Draw a string of BB_FONT characters directly into the EPD framebuffer
//
int bbepWriteStringCustom(BBEPDISP *pBBEP, BB_FONT *pFont, int x, int y, char *szMsg, int iColor, uint8_t iPlane)
int bbepWriteStringCustom(BBEPDISP *pBBEP, void *pFont, int x, int y, char *szMsg, int iColor, uint8_t iPlane)
{
int rc, i, h, w, j, end_y, dx, dy, tx, ty, tw, iSrcPitch, iPitch, iBG;
signed int n;
unsigned int c, bInvert = 0;
uint8_t *s, uc0, uc1;
BB_FONT *pBBF;
BB_FONT_SMALL *pBBFS;
BB_GLYPH *pGlyph;
BB_GLYPH_SMALL *pSmallGlyph;
uint8_t *pBits, u8CMD1, u8CMD2, u8CMD, u8EndMask;
uint8_t szExtMsg[256]; // translated extended ASCII message text
uint8_t first, last;
@@ -975,6 +1104,15 @@ int bbepWriteStringCustom(BBEPDISP *pBBEP, BB_FONT *pFont, int x, int y, char *s
pBBEP->last_error = BBEP_ERROR_BAD_PARAMETER;
return BBEP_ERROR_BAD_PARAMETER; // invalid param
}
// Determine if we're using a small or large font
if (pgm_read_word(pFont) == BB_FONT_MARKER) {
pBBF = (BB_FONT *)pFont;
pBBFS = NULL;
} else {
pBBFS = (BB_FONT_SMALL *)pFont;
pBBF = NULL;
}
if (iColor != BBEP_TRANSPARENT) {
iColor = pBBEP->pColorLookup[iColor & 0xf]; // translate the color for this display type
}
@@ -992,8 +1130,13 @@ int bbepWriteStringCustom(BBEPDISP *pBBEP, BB_FONT *pFont, int x, int y, char *s
x = pBBEP->iCursorX;
if (y == -1)
y = pBBEP->iCursorY;
first = pgm_read_byte(&pFont->first);
last = pgm_read_byte(&pFont->last);
if (pBBF) {
first = pgm_read_byte(&pBBF->first);
last = pgm_read_byte(&pBBF->last);
} else {
first = pgm_read_byte(&pBBFS->first);
last = pgm_read_byte(&pBBFS->last);
}
if (x == CENTER_X) { // center the string on the e-paper
dx = i = 0;
while (szExtMsg[i]) {
@@ -1001,37 +1144,67 @@ int bbepWriteStringCustom(BBEPDISP *pBBEP, BB_FONT *pFont, int x, int y, char *s
if (c < first || c > last) // undefined character
continue; // skip it
c -= first; // first char of font defined
pGlyph = &pFont->glyphs[c]; // glyph info for this character
dx += pgm_read_byte(&pGlyph->xAdvance);
if (pBBF) {
pGlyph = &pBBF->glyphs[c]; // glyph info for this character
dx += pgm_read_word(&pGlyph->xAdvance);
} else { // small font
pSmallGlyph = &pBBFS->glyphs[c]; // glyph info for this character
dx += pgm_read_byte(&pSmallGlyph->xAdvance);
}
}
x = (pBBEP->width - dx)/2;
if (x < 0) x = 0;
}
// Point to the start of the compressed data
pBits = (uint8_t *)pFont;
pBits += sizeof(BB_FONT);
pBits += (last - first + 1) * sizeof(BB_GLYPH);
if (pBBF) {
pBits = (uint8_t *)pBBF;
pBits += sizeof(BB_FONT);
pBits += (last - first + 1) * sizeof(BB_GLYPH);
} else {
pBits = (uint8_t *)pBBFS;
pBits += sizeof(BB_FONT_SMALL);
pBits += (last - first + 1) * sizeof(BB_GLYPH_SMALL);
}
i = 0;
while (szExtMsg[i] && x < pBBEP->width && y < pBBEP->height) {
int16_t xOffset, yOffset;
uint32_t u32Offset, u32Rot, xAdvance;
c = szExtMsg[i++];
if (c < first || c > last) // undefined character
continue; // skip it
c -= first; // first char of font defined
pGlyph = &pFont->glyphs[c]; // glyph info for this character
if (pgm_read_byte(&pGlyph->width) > 1) { // skip this if drawing a space
s = pBits + pgm_read_word(&pGlyph->bitmapOffset); // start of compressed bitmap data
if (pgm_read_dword(&pFont->rotation) == 0 || pgm_read_dword(&pFont->rotation) == 180) {
h = pgm_read_word(&pGlyph->height);
w = pgm_read_byte(&pGlyph->width);
dx = x + (int16_t)pgm_read_word(&pGlyph->xOffset); // offset from character UL to start drawing
dy = y + (int16_t)pgm_read_word(&pGlyph->yOffset);
if (pBBF) {
pGlyph = &pBBF->glyphs[c]; // glyph info for this character
w = pgm_read_word(&pGlyph->width);
h = pgm_read_word(&pGlyph->height);
u32Offset = pgm_read_word(&pGlyph->bitmapOffset);
u32Rot = pgm_read_dword(&pBBF->rotation);
xAdvance = pgm_read_word(&pGlyph->xAdvance);
xOffset = (int16_t)pgm_read_word(&pGlyph->xOffset);
yOffset = (int16_t)pgm_read_word(&pGlyph->yOffset);
} else { // small font
pSmallGlyph = &pBBFS->glyphs[c]; // glyph info for this character
w = pgm_read_byte(&pSmallGlyph->width);
h = pgm_read_byte(&pSmallGlyph->height);
u32Offset = pgm_read_word(&pSmallGlyph->bitmapOffset);
u32Rot = pgm_read_dword(&pBBFS->rotation);
xAdvance = pgm_read_byte(&pSmallGlyph->xAdvance);
xOffset = (int8_t)pgm_read_byte(&pSmallGlyph->xOffset);
yOffset = (int8_t)pgm_read_byte(&pSmallGlyph->yOffset);
}
if (w > 1) { // skip this if drawing a space
s = pBits + u32Offset; // start of compressed bitmap data
if (u32Rot == 0 || u32Rot == 180) {
dx = x + xOffset; // offset from character UL to start drawing
dy = y + yOffset;
} else { // rotated
w = pgm_read_word(&pGlyph->height);
h = pgm_read_byte(&pGlyph->width);
n = (int16_t)pgm_read_word(&pGlyph->yOffset); // offset from character UL to start drawing
dx = w; // swap height/width
w = h;
h = dx;
n = yOffset; // offset from character UL to start drawing
dx = x;
if (-n < w) dx -= (w+n); // since we draw from the baseline
dy = y + (int16_t)pgm_read_word(&pGlyph->xOffset);
dy = y + xOffset;
}
if ((dy + h) > pBBEP->height) { // trim it
h = pBBEP->height - dy;
@@ -1041,7 +1214,11 @@ int bbepWriteStringCustom(BBEPDISP *pBBEP, BB_FONT *pFont, int x, int y, char *s
u8EndMask <<= (8-(w & 7));
}
end_y = dy + h;
ty = (pgm_read_word(&pGlyph[1].bitmapOffset) - (intptr_t)(s - pBits)); // compressed size
if (pBBF) {
ty = (pgm_read_word(&pGlyph[1].bitmapOffset) - (intptr_t)(s - pBits)); // compressed size
} else {
ty = (pgm_read_word(&pSmallGlyph[1].bitmapOffset) - (intptr_t)(s - pBits)); // compressed size
}
if (ty < 0 || ty > 4096) ty = 4096; // DEBUG
rc = g5_decode_init(&g5dec, w, h, s, ty);
if (rc != G5_SUCCESS) {
@@ -1101,6 +1278,9 @@ int bbepWriteStringCustom(BBEPDISP *pBBEP, BB_FONT *pFont, int x, int y, char *s
else if (iColor == BBEP_RED && (pBBEP->iFlags & BBEP_3COLOR)) {
u8CMD = u8CMD2; // second plane is red (inverted)
}
if (!(pBBEP->iFlags & BBEP_3COLOR) && pBBEP->iPlane == 1) {
u8CMD = u8CMD2; // set second plane for partial updates
}
bbepWriteCmd(pBBEP, u8CMD); // memory write command
for (ty=dy; ty<end_y && rc == G5_SUCCESS && ty < pBBEP->native_height; ty++) {
rc = g5_decode_line(&g5dec, u8Cache);
@@ -1124,10 +1304,10 @@ int bbepWriteStringCustom(BBEPDISP *pBBEP, BB_FONT *pFont, int x, int y, char *s
} // for y
}
} // if not drawing a space
if (pgm_read_dword(&pFont->rotation) == 0 || pgm_read_dword(&pFont->rotation) == 180) {
x += pgm_read_byte(&pGlyph->xAdvance); // width of this character
if (u32Rot == 0 || u32Rot == 180) {
x += xAdvance; // width of this character
} else {
y += pgm_read_byte(&pGlyph->xAdvance);
y += xAdvance;
}
} // while drawing characters
pBBEP->iCursorX = x;
@@ -1611,7 +1791,7 @@ void bbepGetStringBox(BBEPDISP *pBBEP, BB_FONT *pFont, const char *szMsg, int *w
*bottom = maxy;
} /* bbepGetStringBox() */
int bbepAllocBuffer(BBEPDISP *pBBEP)
int bbepAllocBuffer(BBEPDISP *pBBEP, int bDoubleSize)
{
#ifndef NO_RAM
int iSize;
@@ -1619,7 +1799,10 @@ int bbepAllocBuffer(BBEPDISP *pBBEP)
iSize = (pBBEP->native_width >> 1) * pBBEP->native_height;
} else { // B/W or B/W/R or B/W/R/Y
iSize = ((pBBEP->native_width+7)>>3) * pBBEP->native_height;
iSize *= 2; // 2 bit planes
if (bDoubleSize) {
iSize *= 2; // 2 bit planes
pBBEP->iFlags |= BBEP_HAS_SECOND_PLANE;
}
}
#if defined (HAL_ESP32_HAL_H_) && !defined(__riscv)
if (iSize > 98000) { // need to use PSRAM
+96 -28
View File
@@ -65,6 +65,54 @@ void BBEPAPER::setAddrWindow(int x, int y, int w, int h)
bbepSetAddrWindow(&_bbep, x, y, w, h);
}
int BBEPAPER::begin(int iProduct)
{
int rc = BBEP_ERROR_BAD_PARAMETER;
switch (iProduct) {
case EPD_LILYGO_S3_MINI: // DC:12 CS:13 RST: 11 BUSY: 10 SCK: 14 MOSI: 15
if (setPanelType(EP102_80x128) == BBEP_SUCCESS) {
initIO(12, 11, 10, 13, 15, 14, 8000000);
return BBEP_SUCCESS;
}
break;
case EPD_BADGER2040: // DC:20 CS:17 RST:21 BUSY: 26 PWR: 10
if (setPanelType(EP29_128x296) == BBEP_SUCCESS) {
initIO(20, 21, 26, 17, -1, -1, 12000000);
setRotation(270);
pinMode(10, OUTPUT);
digitalWrite(10, HIGH); // keep power turned on
return BBEP_SUCCESS;
}
break;
case EPD_TRMNL_OG: // DC:5 CS:6 RST:10 BUSY:4 MOSI:8 SCK:7
if (setPanelType(EP75_800x480) == BBEP_SUCCESS) {
initIO(5, 10, 4, 6, 8, 7, 10000000);
return BBEP_SUCCESS;
}
break;
case EPD_CROWPANEL29: // DC:46 CS:45 RST:47 BUSY:48 MOSI:11 SCK:12
pinMode(7, OUTPUT);
digitalWrite(7, HIGH); // screen power on
if (setPanelType(EP29Z_128x296) == BBEP_SUCCESS) {
initIO(46, 47, 48, 45, 11, 12, 12000000);
setRotation(270);
return BBEP_SUCCESS;
}
break;
case EPD_CROWPANEL213:
pinMode(7, OUTPUT);
digitalWrite(7, HIGH); // screen power on
if (setPanelType(EP213Z_122x250) == BBEP_SUCCESS) {
initIO(13, 10, 9, 14, 11, 12, 12000000);
setRotation(270);
return BBEP_SUCCESS;
}
break;
} // switch on product type
return rc;
} /* begin() */
int BBEPAPER::setPanelType(int iPanel)
{
return bbepSetPanelType(&_bbep, iPanel);
@@ -91,11 +139,11 @@ void BBEPAPER::initIO(int iDC, int iReset, int iBusy, int iCS, int iSPIChannel,
bbepInitIO(&_bbep, u32Speed);
} /* initIO() */
#endif
int BBEPAPER::writePlane(int iPlane)
int BBEPAPER::writePlane(int iPlane, bool bInvert)
{
long l = millis();
int rc;
rc = bbepWritePlane(&_bbep, iPlane);
rc = bbepWritePlane(&_bbep, iPlane, (int)bInvert);
_bbep.iDataTime = (int)(millis() - l);
return rc;
} /* writePlane() */
@@ -144,11 +192,15 @@ void BBEPAPER::backupPlane(void)
memcpy(&_bbep.ucScreen[iSize], _bbep.ucScreen, iSize);
}
}
int BBEPAPER::allocBuffer(void)
int BBEPAPER::allocBuffer(bool bSecondPlane)
{
return bbepAllocBuffer(&_bbep);
return bbepAllocBuffer(&_bbep, (int)bSecondPlane);
} /* allocBuffer() */
uint8_t * BBEPAPER::getCache(void)
{
return u8Cache;
}
void * BBEPAPER::getBuffer(void)
{
return (void *)_bbep.ucScreen;
@@ -211,10 +263,6 @@ bool BBEPAPER::hasPartialRefresh()
void BBEPAPER::setTextColor(int iFG, int iBG)
{
if ((_bbep.iFlags & (BBEP_3COLOR | BBEP_4COLOR | BBEP_7COLOR)) == 0) {
if (iFG == BBEP_RED || iFG == BBEP_YELLOW) iFG = BBEP_BLACK; // can't set red color
if (iBG == BBEP_RED || iFG == BBEP_YELLOW) iBG = BBEP_BLACK;
}
_bbep.iFG = iFG;
_bbep.iBG = (iBG == -1) ? iFG : iBG;
} /* setTextColor() */
@@ -419,22 +467,49 @@ static uint8_t u8Unicode0, u8Unicode1;
bbepWriteString(&_bbep, -1, -1, szTemp, _bbep.iFont, _bbep.iFG, _bbep.iBG);
}
} else { // Custom font
BB_FONT *pBBF = (BB_FONT *)_bbep.pFont;
BB_FONT *pBBF;
BB_FONT_SMALL *pBBFS;
BB_GLYPH *pGlyph;
BB_GLYPH_SMALL *pSmallGlyph;
int first, last;
if (pgm_read_word(_bbep.pFont) == BB_FONT_MARKER) {
pBBF = (BB_FONT *)_bbep.pFont; pBBFS = NULL;
first = pgm_read_byte(&pBBF->first);
last = pgm_read_byte(&pBBF->last);
} else { // small font
pBBFS = (BB_FONT_SMALL *)_bbep.pFont; pBBF = NULL;
first = pgm_read_byte(&pBBFS->first);
last = pgm_read_byte(&pBBFS->last);
}
if (c == '\n') {
_bbep.iCursorX = 0;
_bbep.iCursorY += pBBF->height;
if (pBBF) {
_bbep.iCursorY += pBBF->height;
} else {
_bbep.iCursorY += pBBFS->height;
}
} else if (c != '\r') {
if (c >= pBBF->first && c <= pBBF->last) {
BB_GLYPH *pBBG = &pBBF->glyphs[c - pBBF->first];
w = pBBG->width;
h = pBBG->height;
if (w > 0 && h > 0) { // Is there an associated bitmap?
w += pBBG->xOffset;
if (c >= first && c <= last) {
if (pBBF) {
pGlyph = &pBBF->glyphs[c - first];
w = pgm_read_word(&pGlyph->width);
h = pgm_read_word(&pGlyph->height);
} else { // small font
pSmallGlyph = &pBBFS->glyphs[c - first];
w = pgm_read_word(&pSmallGlyph->width);
h = pgm_read_word(&pSmallGlyph->height);
}
if (w > 0 && h > 0) { // Is there an associated bitmap?
if (pBBF) {
w += (int16_t)pgm_read_word(&pGlyph->xOffset);
} else {
w += (int8_t)pgm_read_byte(&pSmallGlyph->xOffset);
}
if (_bbep.wrap && (_bbep.iCursorX + w) > _bbep.width) {
_bbep.iCursorX = 0;
_bbep.iCursorY += h;
}
bbepWriteStringCustom(&_bbep, (BB_FONT *)_bbep.pFont, -1, -1, szTemp, _bbep.iFG, _bbep.iPlane);
bbepWriteStringCustom(&_bbep, _bbep.pFont, -1, -1, szTemp, _bbep.iFG, _bbep.iPlane);
}
}
}
@@ -455,21 +530,14 @@ int16_t BBEPAPER::getCursorY(void)
{
return _bbep.iCursorY;
}
void BBEPAPER::getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h)
void BBEPAPER::getStringBox(const char *string, BB_RECT *pRect)
{
if (_bbep.pFont) {
int width, top, bottom;
bbepGetStringBox(&_bbep, (BB_FONT *)_bbep.pFont, string, &width, &top, &bottom);
*x1 = x;
*w = width;
*y1 = y + top;
*h = (bottom - top + 1);
}
bbepGetStringBox(&_bbep, (char *)string, pRect);
}
#ifdef ARDUINO
void BBEPAPER::getTextBounds(const String &str, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h)
void BBEPAPER::getStringBox(const String &str, BB_RECT *pRect)
{
getTextBounds(str.c_str(), x, y, x1, y1, w, h);
bbepGetStringBox(&_bbep, str.c_str(), pRect);
}
#endif
int BBEPAPER::dataTime(void)
+42 -5
View File
@@ -45,6 +45,15 @@ enum {
BBEP_ERROR_COUNT
};
#ifndef __ONEBITDISPLAY__
typedef struct {
int x;
int y;
int w;
int h;
} BB_RECT;
#endif
#define LIGHT_SLEEP 0
#define DEEP_SLEEP 1
@@ -74,7 +83,9 @@ enum {
PLANE_0=0,
PLANE_1,
PLANE_BOTH,
PLANE_DUPLICATE // duplicate 0 to both 0 and 1
PLANE_DUPLICATE, // duplicate 0 to both 0 and 1
PLANE_0_TO_1, // send plane 0 to plane 1 memory
PLANE_FALSE_DIFF, // use 'partial' mode to force all pixels to update
};
#ifndef __ONEBITDISPLAY__
// 5 possible font sizes: 8x8, 16x32, 6x8, 12x16 (stretched from 6x8 with smoothing), 16x16 (stretched from 8x8)
@@ -103,6 +114,17 @@ typedef struct epd_panel {
const uint8_t *pColorLookup; // color translation table
} EPD_PANEL;
// Products with built-in SPI EPDs
enum {
EPD_PRODUCT_UNDEFINED=0,
EPD_BADGER2040,
EPD_LILYGO_S3_MINI,
EPD_TRMNL_OG,
EPD_CROWPANEL29,
EPD_CROWPANEL213,
EPD_PRODUCT_COUNT
};
// Display types
enum {
EP_PANEL_UNDEFINED=0,
@@ -124,7 +146,10 @@ enum {
EP37_240x416, // GDEY037T03
EP213_104x212, // InkyPHAT 2.13 black and white
EP75_800x480, // GDEY075T7
EP75_800x480_4GRAY, // GDEW075T7 in 4 grayscale mode
EP75_800x480_4GRAY_OLD, // GDEY075T7 in 4 grayscale mode
EP29_128x296, // Pimoroni Badger2040
EP29_128x296_4GRAY, // Pimoroni Badger2040
EP213R_122x250, // Inky phat 2.13 B/W/R
EP154_200x200, // waveshare
EP154B_200x200, // DEPG01540BN
@@ -140,10 +165,14 @@ enum {
EP583R_600x448, // 4-bits per pixel needs different support
EP75R_800x480, // Waveshare 800x480 3-color
EP426_800x480, // Waveshare 4.26" B/W 800x480
EP426_800x480_4GRAY, // Waveshare 4.26" 800x480 2-bit grayscale mode
EP29R2_128x296, // Adafruit 2.9" 128x296 Tricolor FeatherWing
EP41_640x400, // EInk ED040TC1 SPI UC81xx
EP81_SPECTRA_1024x576, // Spectra 8.1" 1024x576 6-colors
EP7_960x640, // ED070EC1
EP213R2_122x250, // UC8151 3-color
EP29Z_128x296, // SSD1680 (CrowPanel 2.9")
EP213Z_122x250, // SSD1680 (CrowPanel 2.13")
EP_PANEL_COUNT
};
#ifdef FUTURE
@@ -183,6 +212,7 @@ enum {
#define BBEP_PARTIAL2 0x0080
#define BBEP_4BPP_DATA 0x0100
#define BBEP_SPLIT_BUFFER 0x0200
#define BBEP_HAS_SECOND_PLANE 0x0400
#define BBEP_BLACK 0
#define BBEP_WHITE 1
@@ -410,30 +440,37 @@ class BBEPAPER
#endif // __LINUX__
{
public:
BBEPAPER(void) { memset(&_bbep, 0, sizeof(_bbep)); }
BBEPAPER(int iPanel);
int createVirtual(int iWidth, int iHeight, int iFlags);
void setAddrWindow(int x, int y, int w, int h);
int setPanelType(int iPanel);
int begin(int iProduct);
void setCS2(uint8_t cs);
bool hasFastRefresh();
bool hasPartialRefresh();
#ifndef __LINUX__
#ifdef ARDUINO
#ifdef SCK
void initIO(int iDC, int iReset, int iBusy, int iCS = SS, int iMOSI = MOSI, int iSCLK = SCK, uint32_t u32Speed = 8000000);
#else // no SCK/MOSI default pins
void initIO(int iDC, int iReset, int iBusy, int iCS, int iMOSI, int iSCLK, uint32_t u32Speed = 8000000);
#endif
#else // esp-idf?
void initIO(int iDC, int iReset, int iBusy, int iCS, int iMOSI, int iSCLK, uint32_t u32Speed);
#endif // ARDUINO
#else // __LINUX__
void initIO(int iDC, int iReset, int iBusy, int iCS, int iSPIChannel, uint32_t u32Speed = 8000000);
#endif
int writePlane(int iPlane = PLANE_BOTH);
int writePlane(int iPlane = PLANE_BOTH, bool bInvert = false);
void startWrite(int iPlane);
void writeData(uint8_t *pData, int iLen);
void writeCmd(uint8_t u8Cmd);
int refresh(int iMode, bool bWait = true);
void setBuffer(uint8_t *pBuffer);
int allocBuffer(void);
int allocBuffer(bool bSecondPlane = true);
void * getBuffer(void);
uint8_t * getCache(void);
void freeBuffer(void);
uint32_t capabilities();
void setRotation(int iAngle);
@@ -460,9 +497,9 @@ class BBEPAPER
int16_t getCursorX(void);
int16_t getCursorY(void);
int testPanelType(void);
void getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);
void getStringBox(const char *string, BB_RECT *pRect);
#ifdef ARDUINO
void getTextBounds(const String &str, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);
void getStringBox(const String &str, BB_RECT *pRect);
#endif
int dataTime();
int opTime();
+11 -6
View File
@@ -5,13 +5,18 @@
// Written by Larry Bank
// Copyright (c) 2024 BitBank Software, Inc.
//
// Use of this software is governed by the Business Source License
// included in the file ./LICENSE.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===========================================================================
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// ./APL.txt.
#include "Group5.h"
/*
+21 -14
View File
@@ -5,13 +5,18 @@
// Written by Larry Bank
// Copyright (c) 2024 BitBank Software, Inc.
//
// Use of this software is governed by the Business Source License
// included in the file ./LICENSE.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===========================================================================
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// ./APL.txt.
#include "Group5.h"
/* Number of consecutive 1 bits in a byte from MSB to LSB */
@@ -45,7 +50,7 @@ static const uint8_t vtable[14] =
2,7}; /* V(3) = 0000010 */
static void G5ENCInsertCode(BUFFERED_BITS *bb, BIGUINT ulCode, int iLen)
static void G5ENCInsertCode(G5_BUFFERED_BITS *bb, BIGUINT ulCode, int iLen)
{
if ((bb->ulBitOff + iLen) > REGISTER_WIDTH) { // need to write data
bb->ulBits |= (ulCode >> (bb->ulBitOff + iLen - REGISTER_WIDTH)); // partial bits on first word
@@ -61,7 +66,7 @@ static void G5ENCInsertCode(BUFFERED_BITS *bb, BIGUINT ulCode, int iLen)
//
// Flush any buffered bits to the output
//
static void G5ENCFlushBits(BUFFERED_BITS *bb)
static void G5ENCFlushBits(G5_BUFFERED_BITS *bb)
{
while (bb->ulBitOff >= 8)
{
@@ -69,9 +74,11 @@ static void G5ENCFlushBits(BUFFERED_BITS *bb)
bb->ulBits <<= 8;
bb->ulBitOff -= 8;
}
*bb->pBuf++ = (unsigned char) (bb->ulBits >> (REGISTER_WIDTH - 8));
bb->ulBitOff = 0;
bb->ulBits = 0;
if (bb->ulBitOff) { // partial byte?
*bb->pBuf++ = (unsigned char) (bb->ulBits >> (REGISTER_WIDTH - 8));
}
bb->ulBitOff = 0;
bb->ulBits = 0;
} /* G5ENCFlushBits() */
//
// Initialize the compressor
@@ -198,13 +205,13 @@ int xsize, iErr, iHighWater;
int iCur, iRef, iLen;
int iHLen; // number of bits for long horizontal codes
int16_t *CurFlips, *RefFlips;
BUFFERED_BITS bb;
G5_BUFFERED_BITS bb;
if (pImage == NULL || pPixels == NULL)
return G5_INVALID_PARAMETER;
iHighWater = pImage->iOutSize - 32;
iHLen = 32 - __builtin_clz(pImage->iWidth);
memcpy(&bb, &pImage->bb, sizeof(BUFFERED_BITS)); // keep local copy
memcpy(&bb, &pImage->bb, sizeof(G5_BUFFERED_BITS)); // keep local copy
CurFlips = pImage->pCur;
RefFlips = pImage->pRef;
xsize = pImage->iWidth; /* For performance reasons */
@@ -283,7 +290,7 @@ BUFFERED_BITS bb;
if (pImage->y == pImage->iHeight-1) { // last line of image
G5ENCFlushBits(&bb); // output the final buffered bits
// wrap up final output
pImage->iDataSize = (int)(bb.pBuf-pImage->pOutBuf);
pImage->iDataSize = 1 + (int)(bb.pBuf-pImage->pOutBuf);
iErr = G5_ENCODE_COMPLETE;
}
pImage->pCur = RefFlips; // swap current and reference lines