1515#define FONT_DATA_MAGIC 0xA11D0003u
1616#define LV_FONT_GLYPH_FORMAT_A8 0x08u
1717#define STOCK_CHAIN_BUILD_THUMB 0x00470989u
18+ #define LV_MALLOC_THUMB 0x00458383u
1819/* The stock letter helper loads its UTF-8 decoder through the double pointer
1920 at 0x00491F14 (slot address, then function pointer) so it always matches
2021 the firmware's active text encoder. Calling any hardcoded entry instead
2728#define GLYPH_DSC_SIZE 32u
2829#define GLYPH_DSC_FORMAT_OFFSET 14u
2930#define GLYPH_DSC_GID_OFFSET 24u
31+ #define THAI_RUNTIME_MAGIC 0x43414854u
32+ #define THAI_CACHE_SLOTS 4u
33+ #define THAI_CACHE_BUDGET 8192u
34+ #define FONT_WORD_COUNT 9u
3035
3136typedef struct {
3237 uint32_t bitmap_offset ;
@@ -42,6 +47,24 @@ typedef struct {
4247typedef void * (* stock_chain_build_fn )(void * configs , uint32_t count );
4348typedef uint32_t (* stock_decode_fn )(const char * text , uint32_t * offset );
4449typedef void (* flush_cache_fn )(const void * draw_buf , const void * area );
50+ typedef void * (* lv_malloc_fn )(uint32_t size );
51+ typedef uint32_t alias_u32_t __attribute__((__may_alias__ ));
52+
53+ typedef struct {
54+ uint32_t codepoint ;
55+ uint32_t stamp ;
56+ } thai_cache_slot_t ;
57+
58+ typedef struct {
59+ uint32_t font [FONT_WORD_COUNT ];
60+ uint32_t magic ;
61+ uint32_t clock ;
62+ uint16_t slot_bytes ;
63+ uint8_t slot_count ;
64+ uint8_t busy ;
65+ thai_cache_slot_t slots [THAI_CACHE_SLOTS ];
66+ uint8_t pixels [];
67+ } thai_runtime_font_t ;
4568
4669static const uint8_t a4_to_a8 [16 ] = {
4770 0u , 17u , 34u , 51u , 68u , 85u , 102u , 119u ,
@@ -109,6 +132,26 @@ static void write_u32(uint8_t *p, uint32_t value) {
109132 p [3 ] = (uint8_t )(value >> 24 );
110133}
111134
135+ static int writable_ram_range (const void * pointer , uint32_t size ) {
136+ uintptr_t address = (uintptr_t )pointer ;
137+ uint32_t capacity = WRITABLE_RAM_END - WRITABLE_RAM_BASE ;
138+ return size <= capacity && address >= WRITABLE_RAM_BASE &&
139+ address <= WRITABLE_RAM_END - size ;
140+ }
141+
142+ static void copy_bytes (uint8_t * destination , const uint8_t * source , uint32_t count ) {
143+ if ((((uintptr_t )destination | (uintptr_t )source ) & 3u ) == 0u ) {
144+ while (count >= 4u ) {
145+ * (alias_u32_t * )(uintptr_t )destination =
146+ * (const alias_u32_t * )(uintptr_t )source ;
147+ destination += 4 ;
148+ source += 4 ;
149+ count -= 4u ;
150+ }
151+ }
152+ while (count -- ) * destination ++ = * source ++ ;
153+ }
154+
112155static const uint32_t * font_for_line_height (uint32_t height ) {
113156 if (height < 28u ) return thai_font_16 ;
114157 if (height < 34u ) return thai_font_20 ;
@@ -139,6 +182,116 @@ static const thai_glyph_t *glyph_record(const uint32_t *font, uint32_t codepoint
139182 return (const thai_glyph_t * )(data + records_offset + record_index * 12u );
140183}
141184
185+ static uint16_t cache_slot_bytes_for_font (const uint32_t * font ) {
186+ const uint8_t * data = (const uint8_t * )(uintptr_t )thai_font_data_address ;
187+ uint32_t size_index = font [8 ];
188+ if (size_index >= SIZE_COUNT ) return 0 ;
189+ uint32_t records_offset = read_u32 (data + 12 );
190+ uint32_t largest = 0 ;
191+ for (uint32_t index = 0 ; index < GLYPH_COUNT ; index ++ ) {
192+ const thai_glyph_t * glyph = (const thai_glyph_t * )(
193+ data + records_offset + (size_index * GLYPH_COUNT + index ) * 12u );
194+ if (!glyph -> present ) continue ;
195+ uint32_t cache_stride = ((uint32_t )glyph -> box_w + 3u ) & ~3u ;
196+ uint32_t bytes = cache_stride * glyph -> box_h ;
197+ if (bytes > largest ) largest = bytes ;
198+ }
199+ if (!largest || largest > THAI_CACHE_BUDGET || largest > 0xFFFFu ) return 0 ;
200+ return (uint16_t )largest ;
201+ }
202+
203+ static thai_runtime_font_t * runtime_font_from (const uint32_t * font ) {
204+ if (!writable_ram_range (font , sizeof (thai_runtime_font_t ))) return 0 ;
205+ thai_runtime_font_t * runtime = (thai_runtime_font_t * )(uintptr_t )font ;
206+ if (runtime -> magic != THAI_RUNTIME_MAGIC || !runtime -> slot_bytes ||
207+ !runtime -> slot_count || runtime -> slot_count > THAI_CACHE_SLOTS ) return 0 ;
208+ uint32_t cache_bytes = (uint32_t )runtime -> slot_bytes * runtime -> slot_count ;
209+ if (cache_bytes > THAI_CACHE_BUDGET ||
210+ !writable_ram_range (runtime , sizeof (thai_runtime_font_t ) + cache_bytes )) return 0 ;
211+ return runtime ;
212+ }
213+
214+ static const uint32_t * runtime_font_create (const uint32_t * base_font ) {
215+ uint16_t slot_bytes = cache_slot_bytes_for_font (base_font );
216+ if (!slot_bytes ) return base_font ;
217+ uint32_t slot_count = THAI_CACHE_BUDGET / slot_bytes ;
218+ if (slot_count > THAI_CACHE_SLOTS ) slot_count = THAI_CACHE_SLOTS ;
219+ if (!slot_count ) return base_font ;
220+ uint32_t total = sizeof (thai_runtime_font_t ) + slot_count * slot_bytes ;
221+ lv_malloc_fn allocate = (lv_malloc_fn )(uintptr_t )LV_MALLOC_THUMB ;
222+ thai_runtime_font_t * runtime = (thai_runtime_font_t * )allocate (total );
223+ if (!runtime || !writable_ram_range (runtime , total )) return base_font ;
224+ uint8_t * bytes = (uint8_t * )runtime ;
225+ for (uint32_t index = 0 ; index < total ; index ++ ) bytes [index ] = 0 ;
226+ for (uint32_t index = 0 ; index < FONT_WORD_COUNT ; index ++ ) {
227+ runtime -> font [index ] = base_font [index ];
228+ }
229+ runtime -> magic = THAI_RUNTIME_MAGIC ;
230+ runtime -> slot_bytes = slot_bytes ;
231+ runtime -> slot_count = (uint8_t )slot_count ;
232+ return runtime -> font ;
233+ }
234+
235+ static uint8_t * cache_pixels (thai_runtime_font_t * runtime , uint32_t slot ) {
236+ return runtime -> pixels + slot * runtime -> slot_bytes ;
237+ }
238+
239+ static uint32_t cache_tick (thai_runtime_font_t * runtime ) {
240+ runtime -> clock ++ ;
241+ if (!runtime -> clock ) {
242+ runtime -> clock = 1u ;
243+ for (uint32_t index = 0 ; index < runtime -> slot_count ; index ++ ) {
244+ runtime -> slots [index ].stamp = 0 ;
245+ }
246+ }
247+ return runtime -> clock ;
248+ }
249+
250+ static int cache_restore (thai_runtime_font_t * runtime , const thai_glyph_t * glyph ,
251+ uint32_t codepoint , uint8_t * target , uint16_t stride ) {
252+ if (!runtime || runtime -> busy || stride < glyph -> box_w ) return 0 ;
253+ uint32_t cache_stride = ((uint32_t )glyph -> box_w + 3u ) & ~3u ;
254+ if (cache_stride * glyph -> box_h > runtime -> slot_bytes ) return 0 ;
255+ for (uint32_t slot = 0 ; slot < runtime -> slot_count ; slot ++ ) {
256+ if (runtime -> slots [slot ].codepoint != codepoint ) continue ;
257+ runtime -> busy = 1u ;
258+ const uint8_t * source = cache_pixels (runtime , slot );
259+ for (uint32_t y = 0 ; y < glyph -> box_h ; y ++ ) {
260+ copy_bytes (target + y * stride , source + y * cache_stride , glyph -> box_w );
261+ }
262+ runtime -> slots [slot ].stamp = cache_tick (runtime );
263+ runtime -> busy = 0u ;
264+ return 1 ;
265+ }
266+ return 0 ;
267+ }
268+
269+ static void cache_store (thai_runtime_font_t * runtime , const thai_glyph_t * glyph ,
270+ uint32_t codepoint , const uint8_t * source , uint16_t stride ) {
271+ if (!runtime || runtime -> busy || stride < glyph -> box_w ) return ;
272+ uint32_t cache_stride = ((uint32_t )glyph -> box_w + 3u ) & ~3u ;
273+ if (cache_stride * glyph -> box_h > runtime -> slot_bytes ) return ;
274+ uint32_t selected = 0 ;
275+ for (uint32_t slot = 0 ; slot < runtime -> slot_count ; slot ++ ) {
276+ if (!runtime -> slots [slot ].codepoint ) {
277+ selected = slot ;
278+ break ;
279+ }
280+ if (runtime -> slots [slot ].stamp < runtime -> slots [selected ].stamp ) selected = slot ;
281+ }
282+ runtime -> busy = 1u ;
283+ uint8_t * destination = cache_pixels (runtime , selected );
284+ for (uint32_t y = 0 ; y < glyph -> box_h ; y ++ ) {
285+ copy_bytes (destination + y * cache_stride , source + y * stride , glyph -> box_w );
286+ for (uint32_t x = glyph -> box_w ; x < cache_stride ; x ++ ) {
287+ destination [y * cache_stride + x ] = 0 ;
288+ }
289+ }
290+ runtime -> slots [selected ].codepoint = codepoint ;
291+ runtime -> slots [selected ].stamp = cache_tick (runtime );
292+ runtime -> busy = 0u ;
293+ }
294+
142295__attribute__((used , noinline ))
143296bool thai_get_glyph_dsc (const uint32_t * font , void * glyph_dsc ,
144297 uint32_t codepoint , uint32_t next_codepoint ) {
@@ -173,6 +326,16 @@ const void *thai_get_glyph_bitmap(void *glyph_dsc, void *draw_buf) {
173326 uint8_t * draw = (uint8_t * )draw_buf ;
174327 uint16_t stride = read_u16 (draw + 8 );
175328 uint8_t * target = (uint8_t * )(uintptr_t )read_u32 (draw + 16 );
329+ if (!target || stride < glyph -> box_w ) return 0 ;
330+
331+ thai_runtime_font_t * runtime = runtime_font_from (font );
332+ if (cache_restore (runtime , glyph , codepoint , target , stride )) {
333+ const uint32_t * handlers = (const uint32_t * )(uintptr_t )read_u32 (draw + 24 );
334+ if (handlers && handlers [4 ]) {
335+ ((flush_cache_fn )(uintptr_t )handlers [4 ])(draw_buf , 0 );
336+ }
337+ return draw_buf ;
338+ }
176339
177340 for (uint32_t y = 0 ; y < glyph -> box_h ; y ++ ) {
178341 const uint8_t * row = source + y * glyph -> row_bytes ;
@@ -197,6 +360,8 @@ const void *thai_get_glyph_bitmap(void *glyph_dsc, void *draw_buf) {
197360 }
198361 }
199362
363+ cache_store (runtime , glyph , codepoint , target , stride );
364+
200365 const uint32_t * handlers = (const uint32_t * )(uintptr_t )read_u32 (draw + 24 );
201366 if (handlers && handlers [4 ]) {
202367 ((flush_cache_fn )(uintptr_t )handlers [4 ])(draw_buf , 0 );
@@ -247,15 +412,15 @@ void thai_text_encoded_letter_next_2(const char *text, uint32_t *letter,
247412}
248413
249414static int is_thai_font (const uint32_t * font ) {
250- return font == thai_font_16 || font == thai_font_20 ||
415+ if ( font == thai_font_16 || font == thai_font_20 ||
251416 font == thai_font_24 || font == thai_font_28 ||
252417 font == thai_font_32 || font == thai_font_36 ||
253- font == thai_font_40 || font == thai_font_48 ;
418+ font == thai_font_40 || font == thai_font_48 ) return 1 ;
419+ return runtime_font_from (font ) != 0 ;
254420}
255421
256422static int writable_ram_node (const uint32_t * node ) {
257- uintptr_t address = (uintptr_t )node ;
258- return address >= WRITABLE_RAM_BASE && address < WRITABLE_RAM_END ;
423+ return writable_ram_range (node , 32u );
259424}
260425
261426/* Appending must be idempotent: the stock builder runs again whenever a new
@@ -274,7 +439,8 @@ void *thai_chain_append(void *chain_ptr) {
274439 /* Only a verified writable stock node may receive the fallback
275440 pointer; XIP or flash tails are left untouched. */
276441 if (!writable_ram_node (last )) return chain_ptr ;
277- last [7 ] = (uint32_t )(uintptr_t )font_for_line_height (root [3 ]);
442+ const uint32_t * base_font = font_for_line_height (root [3 ]);
443+ last [7 ] = (uint32_t )(uintptr_t )runtime_font_create (base_font );
278444 return chain_ptr ;
279445 }
280446 if (is_thai_font ((const uint32_t * )(uintptr_t )next )) return chain_ptr ;
0 commit comments