Add support for using 4bits mode smooth fonts
By default,the create_font create a 8 bits/pixel fonts,but now it can be transform into 4 bits/pixel and used on ESP8266/ESP32.I have tested it on ESP32,both SPIFFS and flash array. Main modifications : 1. add readInt16() and readInt8() functions; 2. add a flag c4bpp to struct gFont. 3. adjust 4bits mode display. 4. add a tool made by pure C to convert font files. The API is compatible with previous.Compressed font format is described in tools/create_smooth_font/compress_font/readme.md Signed-off-by: Lucy2003 <53362310+Lucy2003@users.noreply.github.com>
This commit is contained in:
parent
58f457ba97
commit
9393f05fdc
|
|
@ -126,7 +126,7 @@ void TFT_eSPI::loadFont(String fontName, bool flash)
|
|||
gFont.gArray = (const uint8_t*)fontPtr;
|
||||
|
||||
gFont.gCount = (uint16_t)readInt32(); // glyph count in file
|
||||
readInt32(); // vlw encoder version - discard
|
||||
gFont.c4bpp = (readInt32() == 0xC); // vlw encoder version,0xC for 4bpp version
|
||||
gFont.yAdvance = (uint16_t)readInt32(); // Font size in points, not pixels
|
||||
readInt32(); // discard
|
||||
gFont.ascent = (uint16_t)readInt32(); // top of "d"
|
||||
|
|
@ -154,6 +154,10 @@ void TFT_eSPI::loadMetrics(void)
|
|||
{
|
||||
uint32_t headerPtr = 24;
|
||||
uint32_t bitmapPtr = headerPtr + gFont.gCount * 28;
|
||||
if(gFont.c4bpp)
|
||||
{
|
||||
bitmapPtr = headerPtr + gFont.gCount * 8;
|
||||
}
|
||||
|
||||
#if defined (ESP32) && defined (CONFIG_SPIRAM_SUPPORT)
|
||||
if ( psramFound() )
|
||||
|
|
@ -190,6 +194,8 @@ void TFT_eSPI::loadMetrics(void)
|
|||
uint16_t gNum = 0;
|
||||
|
||||
while (gNum < gFont.gCount)
|
||||
{
|
||||
if(!gFont.c4bpp)
|
||||
{
|
||||
gUnicode[gNum] = (uint16_t)readInt32(); // Unicode code point value
|
||||
gHeight[gNum] = (uint8_t)readInt32(); // Height of glyph
|
||||
|
|
@ -198,12 +204,20 @@ void TFT_eSPI::loadMetrics(void)
|
|||
gdY[gNum] = (int16_t)readInt32(); // y delta from baseline
|
||||
gdX[gNum] = (int8_t)readInt32(); // x delta from cursor
|
||||
readInt32(); // ignored
|
||||
|
||||
}else{
|
||||
gUnicode[gNum] = (uint16_t)readInt16(); // Unicode code point value
|
||||
gHeight[gNum] = (uint8_t)readInt8(); // Height of glyph
|
||||
gWidth[gNum] = (uint8_t)readInt8(); // Width of glyph
|
||||
gxAdvance[gNum] = (uint8_t)readInt8(); // xAdvance - to move x cursor
|
||||
gdY[gNum] = (int16_t)readInt16(); // y delta from baseline
|
||||
gdX[gNum] = (int8_t)readInt8(); // x delta from cursor
|
||||
}
|
||||
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gHeight = "); Serial.println(gHeight[gNum]);
|
||||
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gWidth = "); Serial.println(gWidth[gNum]);
|
||||
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gxAdvance = "); Serial.println(gxAdvance[gNum]);
|
||||
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gdY = "); Serial.println(gdY[gNum]);
|
||||
|
||||
//Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", gdX = "); Serial.println(gdX[gNum]);
|
||||
//Serial.println("");
|
||||
// Different glyph sets have different ascent values not always based on "d", so we could get
|
||||
// the maximum glyph ascent by checking all characters. BUT this method can generate bad values
|
||||
// for non-existant glyphs, so we will reply on processing for the value and disable this code for now...
|
||||
|
|
@ -229,14 +243,22 @@ void TFT_eSPI::loadMetrics(void)
|
|||
{
|
||||
gFont.maxDescent = gHeight[gNum] - gdY[gNum];
|
||||
#ifdef SHOW_ASCENT_DESCENT
|
||||
Serial.print("Unicode = 0x"); Serial.print(gUnicode[gNum], HEX); Serial.print(", maxDescent = "); Serial.println(gHeight[gNum] - gdY[gNum]);
|
||||
Serial.print("Unicode = 0x");
|
||||
Serial.print(gUnicode[gNum], HEX); Serial.print(", maxDescent = ");
|
||||
Serial.println(gHeight[gNum] - gdY[gNum]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
gBitmap[gNum] = bitmapPtr;
|
||||
|
||||
if(gFont.c4bpp)
|
||||
{
|
||||
bitmapPtr += ((gWidth[gNum] >> 1) + (gWidth[gNum] & 1)) * gHeight[gNum];
|
||||
}
|
||||
else
|
||||
{
|
||||
bitmapPtr += gWidth[gNum] * gHeight[gNum];
|
||||
}
|
||||
|
||||
gNum++;
|
||||
yield();
|
||||
|
|
@ -333,6 +355,50 @@ uint32_t TFT_eSPI::readInt32(void)
|
|||
return val;
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: readInt16
|
||||
** Description: Get a 16 bit integer from the font file
|
||||
*************************************************************************************x*/
|
||||
uint16_t TFT_eSPI::readInt16(void)
|
||||
{
|
||||
uint16_t val = 0;
|
||||
|
||||
#ifdef FONT_FS_AVAILABLE
|
||||
if (fs_font) {
|
||||
val |= fontFile.read() << 8;
|
||||
val |= fontFile.read();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
val |= pgm_read_byte(fontPtr++) << 8;
|
||||
val |= pgm_read_byte(fontPtr++);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: readInt8
|
||||
** Description: Get a 8 bit integer from the font file
|
||||
*************************************************************************************x*/
|
||||
uint8_t TFT_eSPI::readInt8(void)
|
||||
{
|
||||
uint8_t val = 0;
|
||||
|
||||
#ifdef FONT_FS_AVAILABLE
|
||||
if (fs_font) {
|
||||
val = fontFile.read();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
val = pgm_read_byte(fontPtr++);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: getUnicodeIndex
|
||||
|
|
@ -408,7 +474,8 @@ void TFT_eSPI::drawGlyph(uint16_t code)
|
|||
|
||||
int16_t xs = cx;
|
||||
uint32_t dl = 0;
|
||||
uint8_t pixel;
|
||||
uint8_t pixel = 0;
|
||||
uint8_t pixel_before = 0;
|
||||
|
||||
startWrite(); // Avoid slow ESP32 transaction overhead for every pixel
|
||||
|
||||
|
|
@ -419,14 +486,28 @@ void TFT_eSPI::drawGlyph(uint16_t code)
|
|||
#ifdef FONT_FS_AVAILABLE
|
||||
if (fs_font) {
|
||||
if (spiffs)
|
||||
{
|
||||
if(gFont.c4bpp)
|
||||
{
|
||||
fontFile.read(pbuffer, (gWidth[gNum] >> 1) + (gWidth[gNum] & 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
fontFile.read(pbuffer, gWidth[gNum]);
|
||||
}
|
||||
//Serial.println("SPIFFS");
|
||||
}
|
||||
else
|
||||
{
|
||||
endWrite(); // Release SPI for SD card transaction
|
||||
if(gFont.c4bpp)
|
||||
{
|
||||
fontFile.read(pbuffer, (gWidth[gNum] >> 1) + (gWidth[gNum] & 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
fontFile.read(pbuffer, gWidth[gNum]);
|
||||
}
|
||||
startWrite(); // Re-start SPI for TFT transaction
|
||||
//Serial.println("Not SPIFFS");
|
||||
}
|
||||
|
|
@ -435,10 +516,26 @@ void TFT_eSPI::drawGlyph(uint16_t code)
|
|||
for (int x = 0; x < gWidth[gNum]; x++)
|
||||
{
|
||||
#ifdef FONT_FS_AVAILABLE
|
||||
if (fs_font) pixel = pbuffer[x];
|
||||
else
|
||||
if (fs_font)
|
||||
{
|
||||
pixel = (!gFont.c4bpp ? pbuffer[x] : pbuffer[(x >> 1) + (x & 1)]);
|
||||
pixel_before = (!gFont.c4bpp || x == 0 ? 0 : pbuffer[((x-1) >> 1) + ((x - 1) & 1)]);
|
||||
}
|
||||
else{
|
||||
#endif
|
||||
pixel = pgm_read_byte(gPtr + gBitmap[gNum] + x + gWidth[gNum] * y);
|
||||
pixel = (!gFont.c4bpp ? pgm_read_byte(gPtr + gBitmap[gNum] + x + gWidth[gNum] * y)
|
||||
: pgm_read_byte( gPtr + gBitmap[gNum] + (x >> 1) + (x & 1) +
|
||||
((gWidth[gNum] >> 1) + (gWidth[gNum] & 1)) * y));
|
||||
pixel_before = (!gFont.c4bpp || x == 0 ? 0 : pgm_read_byte( gPtr + gBitmap[gNum] + ((x -1) >> 1) +
|
||||
((x -1) & 1) + ((gWidth[gNum] >> 1) + (gWidth[gNum] & 1)) * y));
|
||||
#ifdef FONT_FS_AVAILABLE
|
||||
}
|
||||
#endif
|
||||
//Prepare real pixel data for 4bpp mode
|
||||
if(gFont.c4bpp)
|
||||
{
|
||||
pixel = (!(x & 1) ? (pixel & 0xf0) : ((pixel_before & 0x0f) << 4));
|
||||
}
|
||||
|
||||
if (pixel)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,9 +27,10 @@
|
|||
int16_t descent; // Offset to bottom of 'p', other characters may have a larger descent
|
||||
uint16_t maxAscent; // Maximum ascent found in font
|
||||
uint16_t maxDescent; // Maximum descent found in font
|
||||
bool c4bpp;
|
||||
} fontMetrics;
|
||||
|
||||
fontMetrics gFont = { nullptr, 0, 0, 0, 0, 0, 0, 0 };
|
||||
fontMetrics gFont = { nullptr, 0, 0, 0, 0, 0, 0, 0 ,false };
|
||||
|
||||
// These are for the metrics for each individual glyph (so we don't need to seek this in file and waste time)
|
||||
uint16_t* gUnicode = NULL; //UTF-16 code, the codes are searched so do not need to be sequential
|
||||
|
|
@ -56,6 +57,8 @@ fontMetrics gFont = { nullptr, 0, 0, 0, 0, 0, 0, 0 };
|
|||
|
||||
void loadMetrics(void);
|
||||
uint32_t readInt32(void);
|
||||
uint16_t readInt16(void);
|
||||
uint8_t readInt8(void);
|
||||
|
||||
uint8_t* fontPtr = nullptr;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
### VLW Compress for TFT_eSPI
|
||||
|
||||
Used to transform 8bit VLW file into 4bit VLW file with no padding support.
|
||||
|
||||
**Advantage**: It will save at least 50% space.
|
||||
**Limitation**: Fonts that height over 255 are not being generated properly.
|
||||
|
||||
To Compile,run the command below:
|
||||
|
||||
Windows(MinGW needed): `gcc vlwcompress.c -o vlwcompress.exe`
|
||||
Linux (GCC needed): `gcc vlwcompress.c -o vlwcompress`
|
||||
|
||||
|
||||
#### Compressed Font format
|
||||
|
||||
Header of vlw file is the same as uncompressed;it comprises 6 uint32_t parameters (24 bytes total):
|
||||
1. The gCount (number of character glyphs)
|
||||
2. A version number (0xB = 11 for uncompressed,0XC = 12 for compressed 4bit)
|
||||
3. The font size (in points, not pixels)
|
||||
4. Deprecated mboxY parameter (typically set to 0)
|
||||
5. Ascent in pixels from baseline to top of "d"
|
||||
6. Descent in pixels from baseline to bottom of "p"
|
||||
|
||||
Next are gCount sets of values for each glyph(8 bytes):
|
||||
1. (16 bits) Glyph Unicode
|
||||
2. (8 bits) Height of bitmap bounding box
|
||||
3. (8 bits) Width of bitmap bounding box
|
||||
4. (8 bits) gxAdvance for cursor (setWidth in Processing)
|
||||
5. (16 bits) dY = distance from cursor baseline to top of glyph bitmap (signed value +ve = up)
|
||||
6. (8 bits) dX = distance from cursor to left side of glyph bitmap (signed value -ve = left)
|
||||
|
||||
The bitmaps start next at 24 + (8 * gCount) bytes from the start of the file.
|
||||
Each pixel is 0.5 byte, an 4 bit Alpha value which represents the transparency from
|
||||
0xF foreground colour, 0x0 background.When a line consist of odd number of pixels,the last byte
|
||||
for the last pixel of the line will be filled with 0 as low 4 bits.
|
||||
4 bits alpha value will be generated to 8 bits before drawing.
|
||||
|
||||
After the bitmaps is (Not modified):
|
||||
1 byte for font name string length (excludes null)
|
||||
a zero terminated character string giving the font name
|
||||
1 byte for Postscript name string length
|
||||
a zero/one terminated character string giving the font name
|
||||
last byte is 0 for non-anti-aliased and 1 for anti-aliased (smoothed)
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
VLW Compress v0.0.1 16/7/21
|
||||
|
||||
Used to transform 8bit VLW file into 4bit VLW file with no padding support.
|
||||
|
||||
Note: The program is made by pure C ;it has no dependence.
|
||||
License:See license in root directory.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
FILE* fin;
|
||||
FILE* fout;
|
||||
struct {
|
||||
uint32_t gCount;
|
||||
uint32_t gVer; //0xB(11) = unCompressed,0xC(12)=compressed.
|
||||
uint32_t fSize;
|
||||
uint32_t mboxY;
|
||||
uint32_t ascent;
|
||||
uint32_t descent;
|
||||
uint8_t* gX;
|
||||
uint8_t* gY;
|
||||
} fInfo;
|
||||
|
||||
uint32_t readu32(){
|
||||
uint32_t temp=0;
|
||||
uint8_t tmp[4]={0};
|
||||
fread(tmp,1,4,fin);
|
||||
temp |= tmp[0] << 24;
|
||||
temp |= tmp[1] << 16;
|
||||
temp |= tmp[2] << 8;
|
||||
temp |= tmp[3] << 0;
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
uint8_t readu8(){
|
||||
uint8_t temp[1];
|
||||
fread(temp,1,1,fin);
|
||||
return temp[0];
|
||||
}
|
||||
|
||||
int writeu32(uint32_t in){
|
||||
uint8_t tmp[4]={0};
|
||||
tmp[0]=in>>24;
|
||||
tmp[1]=(in & 0x00ff0000 )>>16;
|
||||
tmp[2]=(in & 0x0000ff00 )>>8;
|
||||
tmp[3]=(in & 0x000000ff );
|
||||
if(fwrite(tmp,1,4,fout)==4){
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int writeu16(uint16_t in){
|
||||
uint8_t tmp[2]={0};
|
||||
tmp[0]=in>>8;
|
||||
tmp[1]=(in & 0x00ff );
|
||||
if(fwrite(tmp,1,2,fout)==2){
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int writeu8(uint8_t in){
|
||||
if(fwrite(&in,1,1,fout)==1){
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t combine2u8(uint8_t H,uint8_t L){
|
||||
return (H & 0xf0 ) | (L >> 4); //All get High bits and combine
|
||||
}
|
||||
|
||||
|
||||
int getFontInfo(){
|
||||
fInfo.gCount=readu32();
|
||||
fInfo.gVer=readu32();
|
||||
fInfo.fSize=readu32();
|
||||
fInfo.mboxY=readu32();
|
||||
fInfo.ascent=readu32();
|
||||
fInfo.descent=readu32();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int writeHeader(){
|
||||
writeu32(fInfo.gCount);
|
||||
writeu32(12ULL);
|
||||
writeu32(fInfo.fSize);
|
||||
writeu32(fInfo.mboxY);
|
||||
writeu32(fInfo.ascent);
|
||||
writeu32(fInfo.descent);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int compressCharsTable(){
|
||||
for(uint32_t i=0;i<fInfo.gCount;i++){
|
||||
uint16_t unicode=(uint16_t)readu32();
|
||||
uint8_t bh=(uint8_t)readu32();
|
||||
uint8_t bw=(uint8_t)readu32();
|
||||
uint8_t ga=(uint8_t)readu32();
|
||||
int16_t dy=(int16_t)readu32();
|
||||
int8_t dx=(int8_t)readu32();
|
||||
readu32(); //ignore padding
|
||||
fInfo.gX[i]=bw;
|
||||
fInfo.gY[i]=bh;
|
||||
writeu16(unicode);
|
||||
writeu8(bh);
|
||||
writeu8(bw);
|
||||
writeu8(ga);
|
||||
writeu16(dy);
|
||||
writeu8(dx);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int compressCharsBitmap(){
|
||||
for(uint32_t i=0;i<fInfo.gCount;i++){
|
||||
if((fInfo.gX[i] & 1) !=0){//not fix 2
|
||||
for(uint8_t j=0;j<fInfo.gY[i];j++){
|
||||
for(uint8_t k=0;k<fInfo.gX[i]-1;k+=2){
|
||||
uint8_t t1=(uint8_t)readu8();
|
||||
uint8_t t2=(uint8_t)readu8();
|
||||
writeu8(combine2u8(t1,t2));
|
||||
}
|
||||
//process the last pixel of each line with 0 as low bits
|
||||
uint8_t t1=(uint8_t)readu8();
|
||||
writeu8(combine2u8(t1,0));
|
||||
}
|
||||
}else{
|
||||
for(uint16_t j=0;j<fInfo.gX[i] * fInfo.gY[i];j+=2){
|
||||
uint8_t t1=(uint8_t)readu8();
|
||||
uint8_t t2=(uint8_t)readu8();
|
||||
writeu8(combine2u8(t1,t2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc,char* argv[]){
|
||||
printf("\n=== VLWCompress - 4bpp by Lucy2003 v0.0.1 === \n\n");
|
||||
if(argc<=2){
|
||||
printf(" usage: %s [input_path] [output_path]\n\n",argv[0]);
|
||||
return 0;
|
||||
}
|
||||
printf("Input file: %s\nOutput file: %s\n",argv[1],argv[2]);
|
||||
|
||||
|
||||
fin=fopen(argv[1],"rb");
|
||||
if(fin == NULL){
|
||||
printf("\nError: Can't open input file. \n\n");
|
||||
return 1;
|
||||
}
|
||||
fseek(fin,0,SEEK_SET);
|
||||
fout=fopen(argv[2],"w+b");
|
||||
if(fout==NULL){
|
||||
printf("\nError: Can't open output file. \n\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
getFontInfo();
|
||||
printf("Font file info:\n");
|
||||
printf(" Char count: %d\n",fInfo.gCount);
|
||||
printf(" Font version: %d\n",fInfo.gVer);
|
||||
printf(" Font size: %d Bytes\n",fInfo.fSize);
|
||||
printf(" mboxY: %d\n ascent:%d\n descent:%d\n",fInfo.mboxY,fInfo.ascent,fInfo.descent);
|
||||
|
||||
if(fInfo.gVer==12){
|
||||
printf("\nError: Input file already compressed.\n\n");
|
||||
return 3;
|
||||
}
|
||||
|
||||
if(fInfo.gVer!=11){
|
||||
printf("\nError: Unsupport font version.\n\n");
|
||||
return 4;
|
||||
}
|
||||
|
||||
fInfo.gX=NULL;
|
||||
fInfo.gY=NULL;
|
||||
fInfo.gX=(uint8_t*)calloc(fInfo.gCount,sizeof(uint8_t));
|
||||
fInfo.gY=(uint8_t*)calloc(fInfo.gCount,sizeof(uint8_t));
|
||||
if(fInfo.gX==NULL || fInfo.gY==NULL){
|
||||
printf("\nError: Malloc memory failed.\n\n");
|
||||
return 5;
|
||||
}
|
||||
//Compress
|
||||
writeHeader();
|
||||
compressCharsTable();
|
||||
compressCharsBitmap();
|
||||
|
||||
free(fInfo.gX);
|
||||
free(fInfo.gY);
|
||||
|
||||
while(!feof(fin)){
|
||||
writeu8(readu8());
|
||||
}
|
||||
|
||||
fclose(fin);
|
||||
fclose(fout);
|
||||
printf("\nCompress Font successfully!\n\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue