I have added FreeType 2.13.2 to my code and test run in Windows PC and it looks working fine. After move the same code to ESP32, reset exception happened, I try to location to problem and finally I found one of my display string (global 50byte char variable) is being corrupted after first calling to FT_Render_Glyph(), if I skip this API call everything goes fine and no exception. This is quite basic key function of FreeType that I just cannot drop it to avoid the problem, anyone experience the same/similar when using FreeType?
It is just weird that FT_Render_Glyph works fine in Windows PC but not in ESP32, compiler flags are almost the same except std=C99 (in PC) and std=gnu17 (in ESP32). The FT_Render_Glyph is not having any parameter that related to my global variable address, and it is suppose only operates her own face->glyph data, my global variable location is quite close to the face variable according to the .map file.
.bss.menustring
0x000000003fca2de0 0x32 esp-idf/main/libmain.a(mymain.c.obj)
0x000000003fca2de0 menustring
.bss.face 0x000000003fca2e18 0x4 esp-idf/main/libmain.a(mymain.c.obj)
0x000000003fca2e18 face
.bss.library 0x000000003fca2e1c 0x4 esp-idf/main/libmain.a(mymain.c.obj)
0x000000003fca2e1c library
below is the code snippet of problem detected when calling FT_Render_Glyph(), variable 100% corrupted after 1st call the FT_Render_Glyph().
void showchar(char *textmsg)
{
uint32_t glyph_index;
glyph_index = FT_Get_Char_Index( face, *textmsg );
// ESP_LOGI(TAG, "Showchar: do nothing just return, FT_Get_Char_Index returns %"PRIu32" for char %c", glyph_index, *textmsg);
// return;
if( glyph_index == 0 )
{
ESP_LOGI(TAG, "Showchar: FT_Get_Char_Index returns 0 for char %c", *textmsg);
return;
}
if( FT_Load_Glyph(face, glyph_index, 0) )
{
ESP_LOGI(TAG, "Showchar: FT_Load_Glyph returns error for index %"PRIu32"", glyph_index);
return;
}
// ESP_LOGI(TAG, "Showchar: do nothing just return, FT_Get_Char_Index returns %"PRIu32" for char %c", glyph_index, *textmsg);
// return;
if( FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL) )
{
ESP_LOGI(TAG, "Showchar: FT_Render_Glyph returns error");
return;
}
ESP_LOGI(TAG, "Showchar: do nothing just return, FT_Get_Char_Index returns %"PRIu32" for char %c, FT_Load_Glyph & FT_Render_Glyph returns 0", glyph_index, *textmsg);
return;
still not yet found a solution for this
Thanks all for responding this post. I try to build a small program of printing pages of string in both Windows and ESP32 to see whether able to trigger the same problem. The small program runs perfectly in Windows (same as FreeType integrated my original application in Windows), but it runs to unexpected result in ESP32 (as this is a thin program, it does not reset on corruption but still shows the same problem of memory corruption), it stopped at the 2nd page and loop again(due to I reload the string at start of the loop).
the marco PCsimulator
is the main switch to building Windows or ESP32.
since there are 3 main FreeType API I have to call for printing every characters, I just print my string content after each call to see any memory corruption happen, and in ESP32, the corruption happen when calling FT_Render_Glyph
for character K, you can see the string content is totally changed, from this test result, I would certify FreeType rendering API is having serious memory corruption problem when compile to ESP32 (I cannot say it is a bug as it do works in Windows, it only not working as expected in ESP32, may be RISC-V CPU too).
Any other open source library recommended to replace FreeType? (I want to use scalable TTF at my project)
OK, let's look at ESP tracelog:
FT_Get_Char_Index return index 44 for char I
after get_index String content=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+
FT_Load_Glyph return ok
after load_glyph String content=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+
256levels width/height of the bitmap=3/19, left/top=1/19, advance.x=5
after render_glyph String content=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+
string position 17, xpos,ypos=8/70, advance.x/y=5/0
FT_Get_Char_Index return index 77 for char j
after get_index String content=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+
FT_Load_Glyph return ok
after load_glyph String content=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+
256levels width/height of the bitmap=4/24, left/top=-1/19, advance.x=4
after render_glyph String content=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+
string position 18, xpos,ypos=12/70, advance.x/y=4/0
FT_Get_Char_Index return index 45 for char J
after get_index String content=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+
FT_Load_Glyph return ok
after load_glyph String content=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+
256levels width/height of the bitmap=8/20, left/top=0/19, advance.x=9
after render_glyph String content=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+
string position 19, xpos,ypos=21/70, advance.x/y=9/0
FT_Get_Char_Index return index 78 for char k
after get_index String content=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+
FT_Load_Glyph return ok
after load_glyph String content=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+
256levels width/height of the bitmap=7/19, left/top=1/19, advance.x=9
after render_glyph String content=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+
string position 20, xpos,ypos=30/70, advance.x/y=9/0
FT_Get_Char_Index return index 46 for char K
after get_index String content=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+
FT_Load_Glyph return ok
after load_glyph String content=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+
256levels width/height of the bitmap=10/19, left/top=1/19, advance.x=11
after render_glyph String content=!?
string position 21, xpos,ypos=41/70, advance.x/y=11/0
I (2858) UKKan: Whole LCD update done!
you can see when doing character K, the String content is corrupted after calling FT_Render_Glyph
, this is exactly the same as my original application, the same thing happen after calling this API, and due to program size differences, it may not immediately trigger a reset, but memory corruption now confirmed not due to other part of my program, it MUST be due to FT_Render_Glyph
API as it is 100% duplicated in this thin program. I put the source code below by removing the TTF font which 3000line of data code.
//#define PCsimulator // define it if compile to Windows; remark it if compile to ESP32//
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#ifdef PCsimulator
#include <conio.h>
#include <SDL2/SDL.h>
#include <windows.h>
#else
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_task_wdt.h"
#include "driver/spi_master.h"
#include "esp_check.h"
#include "sdkconfig.h"
#include "esp_random.h"
#endif
// True Type Font file 39335_UniversCondensed.ttf
const uint8_t ttffont[] =
{
......
};
#define LCD_MAX_WIDTH 128
#define LCD_MAX_HEIGHT 128
#define fontmaxheight 23
#define fontmaxwidth 17
#ifdef PCsimulator
#define Myexit return 0
#else
#define Myexit return
#define LCD_HOST SPI2_HOST
#define PIN_NUM_MOSI 35
#define PIN_NUM_CLK 36
#define PIN_NUM_CS 45
#define PIN_NUM_DC 4
#define PIN_NUM_RST 5
#define SSD1351_CMD_WRITECGRAM 0x5C // SSD1351 command for writing data to CGRAM
const char *TAG = "UKKan";
#endif
FT_Library library;
FT_Face face;
char menustring[200];
FT_ULong charcode;
typedef struct BackgndColor
{
uint8_t BlueColor; // this is to match SSD1351
uint8_t GreenColor; // 18bit 666 will drop LSB 2bit
uint8_t RedColor;
}
DSColor;
DSColor CGRAM[LCD_MAX_HEIGHT*LCD_MAX_WIDTH];
//const DSColor sysbkcolor =
DSColor sysbkcolor =
{
0,
0,
0
};
DSColor fillcolor;
#ifdef PCsimulator
SDL_Window* window;
SDL_Renderer* renderer;
#else
typedef struct
{
uint8_t cmd;
uint8_t data[16]; // for SSD1351, max 3 bytes of data follow a command byte, but ST7789 use more bytes
uint8_t databytes; // No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
} lcd_init_cmd_t;
spi_device_handle_t spi;
// Place data into DRAM. Constant data gets placed into DROM by default, which is not accessible by DMA.
DRAM_ATTR const lcd_init_cmd_t ssd1351_init_cmds[]={
{0xFD, {0x12}, 1}, // set command lock
{0xFD, {0xB1}, 1}, // set command lock
{0xAE, {0x00}, 0}, // set sleep mode on (display off)
{0xB3, {0xF1}, 1}, // front clock divider/oscillator freq
{0xCA, {127}, 1}, // set MUX ratio (128mux)
// {0xA0, {0xA2}, 1}, // set Re-map/Color Depth(Display RAM to Panel)
{0xA0, {0xB0}, 1}, // set Re-map/Color Depth(Display RAM to Panel) remap row/col scan direction for LCD module orientation on PCB layout
{0x15, {0x00, 0x7F}, 2}, // set column address, start addr=0, end addr=127
{0x75, {0x00, 0x7F}, 2}, // set row address, start addr=0, end addr=127
{0xA1, {0x00}, 1}, // set display start line
{0xA2, {0x00}, 1}, // set display offset row
{0xB5, {0x00}, 1}, // set GPIO
{0xAB, {0x01}, 1}, // set function selection, enable internal Vdd regulator
{0xB1, {0x32}, 1}, // set reset(phase1)/pre-charge period(phase2) (precharge not the same as reset value)
{0xBE, {0x05}, 1}, // set Vcomh voltage (reset=0x05)
{0xA6, {0x00}, 0}, // set display mode (reset to normal display)
{0xC1, {0xC8, 0x80, 0xC8}, 3}, // set color A,B,C contract current to reset value(blue, green, red)
{0xC7, {0x0F}, 1}, // set master contract current control(reset value)
{0xB4, {0xA0, 0xB5, 0x55}, 3}, // set segment low voltage (Vsl to reset value)
{0xB6, {0x01}, 1}, // set second pre-charge period (1DCLKS, diff from reset value 8DCLKS)
{0xAF, {0x00}, 0}, // set sleep mode off (display on)
// {0xA5, {0x00}, 0}, // set display mode (reset to normal display)
{0, {0}, 0xff}
};
//This function is called (in irq context!) just before a transmission starts. It will
//set the D/C line to the value indicated in the user field.
//void IRAM_ATTR lcd_spi_pre_transfer_callback(spi_transaction_t *spihandle)
void lcd_spi_pre_transfer_callback(spi_transaction_t *spihandle)
{
uint32_t dc = (uint32_t)spihandle->user;
gpio_set_level(PIN_NUM_DC, dc);
}
// Send one command byte to the LCD, which waits until the transfer is completed
// cskeeplow = 1 means keep CS pin low for following data bytes; 0 means restore CS pin to high
void lcd_cmd(spi_device_handle_t spi, uint8_t cmd, bool cskeeplow)
{
esp_err_t ret;
spi_transaction_t t;
memset(&t, 0, sizeof(t)); // Zero out the transaction
t.length = 8; // Command is 8 bits
t.tx_buffer = &cmd; // pionts to th cmd parameter address
t.user = (void*)0; // set DC# to 0 for command
if( cskeeplow )
{
t.flags = SPI_TRANS_CS_KEEP_ACTIVE; // Keep CS active(low) after data transfer
}
ret = spi_device_polling_transmit(spi, &t); // Transmit now!
ESP_LOGI(TAG, "\033[0;36msending command %#04x, ret=%d: %s\033[0m", cmd, ret, esp_err_to_name(ret));
assert( ret == ESP_OK );
}
// Send data byte(s) to the LCD, which waits until the transfer is completed
void lcd_data(spi_device_handle_t spi, uint8_t *dataB, uint32_t NoOfByte)
{
esp_err_t ret;
spi_transaction_t t;
memset(&t, 0, sizeof(t)); // Zero out the transaction
t.length = NoOfByte*8; // transaction length is in bits
t.tx_buffer = dataB; // pointer to data bytes to be sent
t.user=(void*)1; // set DC# to 1 for data
ret = spi_device_polling_transmit(spi, &t); // Transmit now!
ESP_LOGI(TAG, "\033[0;36mSending LCD data %d, ret=%d: %s\033[0m", (uint16_t)NoOfByte, ret, esp_err_to_name(ret));
assert( ret == ESP_OK );
}
//Initialize the SSD1351/ST7789 using either HW SPI or Software SPI
void lcd_init(void)
{
int cmd=0;
lcd_init_cmd_t* lcd_init_cmds=(lcd_init_cmd_t *)ssd1351_init_cmds;
//Reset the SSD1351/ST7789 chip
gpio_set_level(PIN_NUM_RST, 0);
vTaskDelay(10 / portTICK_PERIOD_MS);
gpio_set_level(PIN_NUM_RST, 1);
vTaskDelay(10 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "\033[0;31mLCD pin RST pulse done\033[0m");
// When using SPI_TRANS_CS_KEEP_ACTIVE, bus must be locked/acquired
spi_device_acquire_bus(spi, portMAX_DELAY);
//Send all the commands until seeing an 0xFF
while( lcd_init_cmds[cmd].databytes != 0xFF )
{ // CS pin controlled inside lcd_cmd and lcd_data
lcd_cmd(spi, lcd_init_cmds[cmd].cmd, true);
if( ((lcd_init_cmds[cmd].databytes)&0x1F) != 0x00 ) // some LCD command comes with no data byte follows
{
lcd_data(spi, (uint8_t *)&(lcd_init_cmds[cmd].data), lcd_init_cmds[cmd].databytes&0x1F); // bit7 is for indication of delay between commands
}
if( lcd_init_cmds[cmd].databytes&0x80 ) // bit7 is for indication of delay between commands
{
vTaskDelay(100 / portTICK_PERIOD_MS);
}
cmd++;
}
// Release SPI bus (for SPI flash/PSRAM)
spi_device_release_bus(spi);
}
#endif
void lcd_updateCGRAM(void)
{
#ifdef PCsimulator
uint32_t xpos; // xpos and ypos is the position of point on the screen, not CGRAM address
uint32_t ypos; // assume CGRAM is arranged in first row RGB RGB RGB ... and then next lower row RGB RGB RGB ...
// 0,0 is at top left corner, same as the SDL2 window position arrangement
uint32_t tmppos; // tmppos is the DSColor position (3byte strucutre) in CGRAM array, not offset address
for( ypos = 0; ypos < LCD_MAX_HEIGHT; ypos++ )
{
for( xpos = 0; xpos < LCD_MAX_WIDTH; xpos++ )
{
tmppos = (ypos * LCD_MAX_WIDTH) + xpos; // copy the CGRAM pixel content to SDL2 back buffer
SDL_SetRenderDrawColor(renderer, CGRAM[tmppos].RedColor, CGRAM[tmppos].GreenColor, CGRAM[tmppos].BlueColor, 255);
SDL_RenderDrawPoint(renderer, xpos, ypos);
}
}
SDL_RenderPresent(renderer); // present the composed backbuffer to the screen
#else
esp_err_t ret;
spi_transaction_t *rtrans;
uint8_t updcnt;
//Transaction descriptors. Declared static so they're not allocated on the stack; we need this memory even when this
//function is finished because the SPI driver needs access to it.
static spi_transaction_t trans[6];
//In theory, it's better to initialize trans and data only once and hang on to the initialized
//variables. We allocate them on the stack, so we need to re-init them each call.
for(updcnt = 0; updcnt < 6; updcnt++)
{
memset(&trans[updcnt], 0, sizeof(spi_transaction_t));
}
trans[0].tx_data[0] = SSD1351_CMD_WRITECGRAM;
trans[0].length = 8; // Command is 8 bits
trans[0].user = (void*)0; // DC# needs to be set to 0
trans[0].flags = SPI_TRANS_USE_TXDATA; // Keep CS active after data transfer
trans[1].tx_buffer = (uint8_t *)&CGRAM;
trans[1].length = 128*3*26*8; // Data bit length, in bits
trans[1].user = (void*)1; // DC# needs to be set to 1
trans[2].tx_buffer = (uint8_t *)(&CGRAM)+(128*3*26);
trans[2].length = 128*3*26*8; // Data bit length, in bits
trans[2].user = (void*)1; // DC# needs to be set to 1
trans[3].tx_buffer = (uint8_t *)(&CGRAM)+(128*3*26*2);
trans[3].length = 128*3*26*8; // Data bit length, in bits
trans[3].user = (void*)1; // DC# needs to be set to 1
trans[4].tx_buffer = (uint8_t *)(&CGRAM)+(128*3*26*3);
trans[4].length = 128*3*26*8; // Data bit length, in bits
trans[4].user = (void*)1; // DC# needs to be set to 1
trans[5].tx_buffer = (uint8_t *)(&CGRAM)+(128*3*26*4);
trans[5].length = 128*3*24*8; // Data bit length, in bits
trans[5].user = (void*)1; // DC# needs to be set to 1
ret = spi_device_queue_trans(spi, &trans[0], portMAX_DELAY);
assert( ret == ESP_OK );
ret = spi_device_queue_trans(spi, &trans[1], portMAX_DELAY);
assert( ret == ESP_OK );
ret = spi_device_queue_trans(spi, &trans[2], portMAX_DELAY);
assert( ret == ESP_OK );
ret = spi_device_queue_trans(spi, &trans[3], portMAX_DELAY);
assert( ret == ESP_OK );
ret = spi_device_queue_trans(spi, &trans[4], portMAX_DELAY);
assert( ret == ESP_OK );
ret = spi_device_queue_trans(spi, &trans[5], portMAX_DELAY);
assert( ret == ESP_OK );
//Wait for all 6 transactions to be done and get back the results.
for( updcnt = 0; updcnt < 6; updcnt++ )
{
ret = spi_device_get_trans_result(spi, &rtrans, portMAX_DELAY);
assert( ret == ESP_OK );
}
ESP_LOGI(TAG, "\033[0;31mWhole LCD update done!\033[0m");
#endif
}
// fill the CGRAM with specific color in parameter
void fillwith(DSColor *thiscolor)
{
uint32_t xpos; // xpos and ypos is the position of point on the screen, not CGRAM address
uint32_t ypos; // assume CGRAM is arranged in first row RGB RGB RGB ... and then next lower row RGB RGB RGB ...
uint32_t tmppos; // tmppos is the DSColor position (3byte strucutre) in CGRAM array, not offset address
for( ypos = 0; ypos < LCD_MAX_HEIGHT; ypos++ )
{
for( xpos = 0; xpos < LCD_MAX_WIDTH; xpos++ )
{
tmppos = (ypos * LCD_MAX_WIDTH) + xpos; // copy DSColor value to all pixels of the CGRAM
CGRAM[tmppos].RedColor = thiscolor->RedColor;
CGRAM[tmppos].GreenColor = thiscolor->GreenColor;
CGRAM[tmppos].BlueColor = thiscolor->BlueColor;
}
}
}
// clear screen to specific color in parameter
void clearscreen(DSColor *thiscolor)
{
fillwith(thiscolor);
}
// draw a point to the CGRAM using thiscolor
void DrawPoint(uint32_t xpos, uint32_t ypos, DSColor *thiscolor)
{
uint32_t tmppos;
if( (xpos < LCD_MAX_WIDTH) && (ypos < LCD_MAX_HEIGHT) )
{
tmppos = (ypos * LCD_MAX_WIDTH ) + xpos;
CGRAM[tmppos].RedColor = thiscolor->RedColor;
CGRAM[tmppos].GreenColor = thiscolor->GreenColor;
CGRAM[tmppos].BlueColor = thiscolor->BlueColor;
}
}
#ifdef PCsimulator
int main(int argc, char *argv[]) // PC app main function type
#else
void app_main(void) // ESP32 app main function, (a task in RTOS above min priority), is an indefinite loop
#endif
{
uint32_t glyph_index,xpos,ypos;
uint32_t x,y,s,mwidth,mheight,exitorloop=0;
mwidth = fontmaxwidth;
mheight = fontmaxheight;
#ifdef PCsimulator
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow("RGB888", 8, 30, LCD_MAX_WIDTH, LCD_MAX_HEIGHT, 0);
renderer = SDL_CreateRenderer(window, -1, 0);
#else
// init LCD on ESP32
gpio_config_t io_conf = {};
esp_err_t ret;
spi_bus_config_t buscfg =
{
.miso_io_num = -1, // no such pin for SSD1351
.mosi_io_num = PIN_NUM_MOSI,
.sclk_io_num = PIN_NUM_CLK, // CS pin belongs to device config, D/C, RST pins are outside SPI signal
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 10248
};
spi_device_interface_config_t devcfg =
{
.clock_speed_hz = 16*1000*1000, //Clock out max at 26 MHz, in PCB it is max 23, in dev board it is 5MHz
.mode = 0, //SPI mode 0
.spics_io_num = PIN_NUM_CS, //CS pin
.queue_size = 7, //We want to be able to queue 7 transactions at a time
.pre_cb = lcd_spi_pre_transfer_callback, // Specify pre-transfer callback to handle D/C line
};
// Initialize the SPI2 bus (main SPI2 pins exclude CS, DC, RST)
ret=spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO);
ESP_LOGI(TAG, "\033[0;31mSPI2 bus_initialize return %s\033[0m", (ret == ESP_OK? "ESP_OK" : "FAILED!!"));
// Attach the LCD device to the SPI bus (ie. the CS pin on that LCD, get back handle)
ret=spi_bus_add_device(LCD_HOST, &devcfg, &spi);
ESP_LOGI(TAG, "\033[0;31mSPI2_bus_add_device return %s\033[0m", (ret == ESP_OK? "ESP_OK" : "FAILED!!"));
//Initialize non-SPI GPIOs
io_conf.pin_bit_mask = ((1ULL<<PIN_NUM_DC) | (1ULL<<PIN_NUM_RST));
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.intr_type = GPIO_INTR_DISABLE;
gpio_config(&io_conf);
ESP_LOGI(TAG, "\033[0;31mI/O pin LCD RST, DC init as output\033[0m");
lcd_init();
#endif
if( FT_Init_FreeType(&library) )
{
printf("FreeType library init failed\n");
Myexit;
}
if( FT_New_Memory_Face(library, ttffont, 45516, 0, &face) )
{
FT_Done_FreeType(library);
printf("FreeType open face failed\n");
Myexit;
}
if( FT_Set_Pixel_Sizes(face, mwidth, mheight) )
{
FT_Done_Face(face);
FT_Done_FreeType(library);
printf("FreeType set pixel failed\n");
Myexit;
}
printf("FreeType init ok, face open and set pixel success\n");
xpos=3;
ypos=70;
s=0;
clearscreen(&sysbkcolor);
lcd_updateCGRAM();
StartTest:
clearscreen(&sysbkcolor);
strcpy(menustring, "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvvwWxXyYzZ0123456789aAbBcCdDeEfFgG!$#~@':;<>,./?|^&*()-_=+");
do
{
glyph_index = FT_Get_Char_Index( face, menustring[s] );
if( glyph_index == 0 )
{
Myexit;
}
printf("FT_Get_Char_Index return index %ld for char %c\n", glyph_index, menustring[s]);
printf("after get_index String content=%s\n", menustring);
if( FT_Load_Glyph(face, glyph_index, 0) )
{
Myexit;
}
printf("FT_Load_Glyph return ok\n");
printf("after load_glyph String content=%s\n", menustring);
if( FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL) )
{
Myexit;
}
printf("256levels width/height of the bitmap=%d/%d, left/top=%d/%d, advance.x=%ld\n", face->glyph->bitmap.width, face->glyph->bitmap.rows, face->glyph->bitmap_left, face->glyph->bitmap_top, face->glyph->advance.x >> 6);
printf("after render_glyph String content=%s\n", menustring);
for(y=0; y<face->glyph->bitmap.rows; y++)
{
for(x=0; x<face->glyph->bitmap.width; x++)
{
if( face->glyph->bitmap.buffer[y*face->glyph->bitmap.width+x] != 0 )
{
#ifdef PCsimulator
fillcolor.RedColor=face->glyph->bitmap.buffer[y*face->glyph->bitmap.width+x];
fillcolor.GreenColor=face->glyph->bitmap.buffer[y*face->glyph->bitmap.width+x];
#else
fillcolor.RedColor=face->glyph->bitmap.buffer[y*face->glyph->bitmap.width+x] >>2;
fillcolor.GreenColor=face->glyph->bitmap.buffer[y*face->glyph->bitmap.width+x] >>2;
#endif
fillcolor.BlueColor=0;
DrawPoint(xpos+(face->glyph->bitmap_left)+x, ypos-(face->glyph->bitmap_top)+y, &fillcolor);
// printf("%03d ",face->glyph->bitmap.buffer[y*face->glyph->bitmap.width+x]);
}else
{
// printf(" ");
}
}
// printf(";\n");
}
xpos = xpos + (face->glyph->advance.x >> 6);
printf("string position %ld, xpos,ypos=%ld/%ld, advance.x/y=%ld/%ld\n", s, xpos, ypos, face->glyph->advance.x >> 6, face->glyph->advance.y >> 6);
s++; // next char
}while( (s<strlen(menustring)) && (xpos < LCD_MAX_WIDTH ) );
lcd_updateCGRAM();
#ifdef PCsimulator
Sleep(1000); // wait
#else
vTaskDelay(1000 / portTICK_PERIOD_MS);
#endif
if( s >= strlen(menustring) )
{
s=0;
mwidth=fontmaxwidth;
mheight=fontmaxheight;
}else
{
mwidth = mwidth + 4;
mheight = mheight + 5;
}
xpos=3;
FT_Set_Pixel_Sizes(face, mwidth, mheight);
#ifdef PCsimulator
if(_kbhit())
{
if(_getch() == 27)
{
exitorloop=1;
}
}
#endif
if(!exitorloop)
{
goto StartTest;
}
#ifdef PCsimulator
FT_Done_Face(face);
FT_Done_FreeType(library);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
#endif
Myexit;
}