Skip to content

Commit 594c998

Browse files
Font Manager: fix emoji scaling
1 parent 0d44d5a commit 594c998

2 files changed

Lines changed: 81 additions & 17 deletions

File tree

internal_filesystem/apps/com.micropythonos.showfonts/assets/showfonts.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def onCreate(self):
1313
mydir = os.path.dirname(os.path.abspath(__file__))
1414
self._ttf_font = FontManager.getFont(size=42, ttf=f"M:{mydir}/Rancourt-SmallCaps.ttf")
1515
self._ttf_font56 = FontManager.getFont(size=56, ttf=f"M:{mydir}/Rancourt-SmallCaps.ttf")
16+
self._ttf_font72 = FontManager.getFont(size=72, ttf=f"M:{mydir}/Rancourt-SmallCaps.ttf")
1617

1718
self.addAllFontsTitles(screen)
1819
self.addAllGlyphs(screen)
@@ -23,6 +24,7 @@ def addAllFontsTitles(self, screen):
2324
fonts = FontManager.listFonts()
2425
fonts.append((self._ttf_font, "TTF Rancourt 42"))
2526
fonts.append((self._ttf_font56, "TTF Rancourt 56"))
27+
fonts.append((self._ttf_font72, "TTF Rancourt 72"))
2628

2729
for font_info in fonts:
2830
if isinstance(font_info, tuple):

internal_filesystem/lib/mpos/ui/font_manager.py

Lines changed: 79 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)