@@ -255,15 +255,30 @@ def _build_emoji_map_from_png_dir(cls, dir_name):
255255 @classmethod
256256 def _get_emoji_src (cls , codepoint , target_height ):
257257 cls ._ensure_emoji_maps ()
258+
259+ preferred_dir = None
258260 for tier in cls ._EMOJI_TIERS :
259261 if target_height <= tier ["max_height" ]:
260- dir_name = tier ["dir" ]
261- if codepoint in cls ._emoji_maps .get (dir_name , {}):
262- return cls ._emoji_maps [dir_name ][codepoint ]
262+ preferred_dir = tier ["dir" ]
263+ break
264+
265+ if preferred_dir is not None :
266+ preferred_map = cls ._emoji_maps .get (preferred_dir , {})
267+ if codepoint in preferred_map :
268+ return preferred_map [codepoint ]
269+
270+ # For large emoji requests, explicitly fall back to 20x20.
271+ # The renderer will upscale it to target_height.
272+ if preferred_dir == "56x56" :
273+ fallback_map = cls ._emoji_maps .get ("20x20" , {})
274+ if codepoint in fallback_map :
275+ return fallback_map [codepoint ]
276+
263277 for tier in cls ._EMOJI_TIERS :
264278 dir_name = tier ["dir" ]
265- if codepoint in cls ._emoji_maps .get (dir_name , {}):
266- return cls ._emoji_maps [dir_name ][codepoint ]
279+ emoji_map = cls ._emoji_maps .get (dir_name , {})
280+ if codepoint in emoji_map :
281+ return emoji_map [codepoint ]
267282 return None
268283
269284 @classmethod
@@ -322,15 +337,38 @@ def _get_empty_imgfont_src(cls, target_height):
322337 return cls ._imgfont_empty_src_cache [target_height ]
323338
324339 buf = bytearray (4 )
325- dsc = lv .image_dsc_t ()
326- dsc .data = buf
327- dsc .header .w = 1
328- dsc .header .h = target_height
329- dsc .header .cf = lv .COLOR_FORMAT .ARGB8888
340+ dsc = cls ._build_argb8888_dsc (buf , 1 , target_height )
330341
331342 cls ._imgfont_empty_src_cache [target_height ] = dsc
332343 return dsc
333344
345+ @classmethod
346+ def _build_argb8888_dsc (cls , buf , width , height ):
347+ width = int (width )
348+ height = int (height )
349+ stride = width * 4
350+ try :
351+ return lv .image_dsc_t (
352+ {
353+ "header" : {
354+ "magic" : lv .IMAGE_HEADER_MAGIC ,
355+ "w" : width ,
356+ "h" : height ,
357+ "stride" : stride ,
358+ "cf" : lv .COLOR_FORMAT .ARGB8888 ,
359+ },
360+ "data_size" : len (buf ),
361+ "data" : buf ,
362+ }
363+ )
364+ except Exception :
365+ dsc = lv .image_dsc_t ()
366+ dsc .data = buf
367+ dsc .header .w = width
368+ dsc .header .h = height
369+ dsc .header .cf = lv .COLOR_FORMAT .ARGB8888
370+ return dsc
371+
334372 @classmethod
335373 def _font_pixel_height (cls , font ):
336374 try :
@@ -359,7 +397,9 @@ def _get_scaled_imgfont_src(cls, src, target_height):
359397 return src
360398
361399 target_width = max (1 , round (src_w * target_height / src_h ))
362- dsc , buf = cls ._render_scaled_image_src (src , target_width , target_height )
400+ dsc , buf = cls ._render_scaled_image_src (
401+ src , src_w , src_h , target_width , target_height
402+ )
363403 if dsc is not None :
364404 cls ._imgfont_scaled_src_cache [key ] = (dsc , buf )
365405 return dsc
@@ -385,21 +425,43 @@ def _get_image_size(cls, src):
385425 return size
386426
387427 @classmethod
388- def _render_scaled_image_src (cls , src , target_width , target_height ):
428+ def _render_scaled_image_src (cls , src , src_w , src_h , target_width , target_height ):
389429 renderer = lv .image (lv .layer_top ())
390430 try :
391431 renderer .add_flag (lv .obj .FLAG .HIDDEN )
392- renderer .set_size (target_width , target_height )
393- renderer .set_inner_align (lv .image .ALIGN .CONTAIN )
432+ renderer .set_size (src_w , src_h )
433+ renderer .set_inner_align (lv .image .ALIGN .CENTER )
394434 renderer .set_src (src )
395435
396436 bpp = 4
397- buf = bytearray (target_width * target_height * bpp )
398- dsc = lv .image_dsc_t ()
437+ src_buf = bytearray (src_w * src_h * bpp )
438+ src_dsc = lv .image_dsc_t ()
399439 lv .snapshot_take_to_buf (
400- renderer , lv .COLOR_FORMAT .ARGB8888 , dsc , buf , len (buf )
440+ renderer , lv .COLOR_FORMAT .ARGB8888 , src_dsc , src_buf , len (src_buf )
401441 )
402442
443+ if int (src_dsc .header .w ) <= 0 or int (src_dsc .header .h ) <= 0 :
444+ return None , None
445+
446+ if target_width == src_w and target_height == src_h :
447+ return src_dsc , src_buf
448+
449+ buf = bytearray (target_width * target_height * bpp )
450+ for y in range (target_height ):
451+ src_y = (y * src_h ) // target_height
452+ src_row = src_y * src_w
453+ dst_row = y * target_width
454+ for x in range (target_width ):
455+ src_x = (x * src_w ) // target_width
456+ src_idx = (src_row + src_x ) * bpp
457+ dst_idx = (dst_row + x ) * bpp
458+ buf [dst_idx ] = src_buf [src_idx ]
459+ buf [dst_idx + 1 ] = src_buf [src_idx + 1 ]
460+ buf [dst_idx + 2 ] = src_buf [src_idx + 2 ]
461+ buf [dst_idx + 3 ] = src_buf [src_idx + 3 ]
462+
463+ dsc = cls ._build_argb8888_dsc (buf , target_width , target_height )
464+
403465 return dsc , buf
404466 finally :
405467 renderer .delete ()
0 commit comments