/* test_host_icon.c — 宿主最小 harness:复现主界面卡片(menu_draw_grid_cell)的 * 「卡底 → 图标 → 名称」绘制序列(与实际菜单同序),断言图标区像素非空。 * * 背景:真机「应用图标完全不渲染(连首字母占位都无)」。本 harness 用真实 SDK(gfx+img) * 按实际调用顺序绘制,对「有图(meow_ui_img_cover 不透明 226×226)」与「无图(primary * 圆角块占位)」两分支各断言图标区含非背景像素;另转 PPM 目检。 * * 编译(在 patch_v2/meow_launcherd 下): * gcc -o test_host_icon test_host_icon.c ../sdk/test_host_stub.c \ * ../sdk/meow_ui_gfx.c ../sdk/meow_ui_sys.c ../sdk/meow_ui_theme.c \ * ../sdk/meow_ui_vol.c ../sdk/meow_ui_log.c ../sdk/meow_ui_widget.c \ * ../sdk/meow_ui_input.c ../sdk/meow_ui_activity.c ../sdk/meow_ui_dialog.c \ * ../sdk/meow_ui_ime.c ../sdk/meow_ui_draw.c ../sdk/meow_ui_view.c \ * ../sdk/meow_ui_img.c ../sdk/meow_notify_ipc.c ../sdk/meow_ui_media.c \ * ../sdk/stb_image.c -I../sdk -I. -DMEOW_UI_HOST_TEST -pthread -lm * 运行:MEOW_UI_DUMP=/tmp/icon.ppm ./test_host_icon */ #include "meow_ui.h" #include "meow_ui_internal.h" /* meow_ui_back_buf/pitch/bpp + meow_gfx_fill_round */ #include #include #include #include /* 与 launcher 主题色(dark)对齐 */ #define TH_SURFACE_CONTAINER 0x263038 #define TH_PRIMARY 0x4A6A9A /* 不透明图标填充色 */ #define IMG_COLOR 0x336699 /* 与主菜单一致的几何常量(main_menu.c) */ #define MENU_GRID_RADIUS 16 #define MENU_GRID_ICON 44 #define MENU_GRID_CARD_H 112 #define MENU_GRID_COLS 2 #define MENU_GRID_PAD 12 #define MENU_GRID_GAP 10 #define FB_W 480 #define FB_H 720 static void rd_px(int x, int y, int *r, int *g, int *b) { unsigned char *bb = meow_ui_back_buf(); int line = meow_ui_back_buf_pitch(), bpp = meow_ui_back_buf_bpp(); if (!bb || x < 0 || y < 0) { *r = *g = *b = 0; return; } if (bpp == 16) { uint16_t px = *(uint16_t *)(bb + (size_t)y * line + (size_t)x * 2); *r = (((px >> 11) & 0x1F) << 3) | ((px >> 11) & 0x1F) >> 2; *g = (((px >> 5) & 0x3F) << 2) | ((px >> 5) & 0x3F) >> 4; *b = ((px & 0x1F) << 3) | ((px & 0x1F) >> 2); } else { uint32_t px = *(uint32_t *)(bb + (size_t)y * line + (size_t)x * 4); *r = (px >> 16) & 0xFF; *g = (px >> 8) & 0xFF; *b = px & 0xFF; } } static int near2(int a, int b) { int d = a - b; if (d < 0) d = -d; return d <= 16; } static int px_near(int x, int y, int r, int g, int b) { int rr, gg, bb; rd_px(x, y, &rr, &gg, &bb); return near2(rr, r) && near2(gg, g) && near2(bb, b); } /* 区域内含「接近 target 色」的像素数 */ static int region_has(int x, int y, int w, int h, int r, int g, int b) { int cnt = 0; for (int dy = 0; dy < h; dy++) for (int dx = 0; dx < w; dx++) if (px_near(x + dx, y + dy, r, g, b)) cnt++; return cnt; } #define UNPK(c, rr, gg, bb) do { (rr) = ((c) >> 16) & 0xFF; (gg) = ((c) >> 8) & 0xFF; (bb) = (c) & 0xFF; } while (0) /* 菜单图标绘制(menu_draw_icon):primary 圆角垫块始终垫底(MD3 图标 tile), * 有图叠加 meow_ui_img_cover;无图叠加首字母。透明显图时垫块仍露出,图标区绝不空白。 */ static void menu_draw_icon_like(meow_img *icon, int x, int y, int sz) { meow_gfx_fill_round(x, y, sz, sz, sz * 3 / 10, TH_PRIMARY); if (icon) { meow_ui_img_cover(x, y, sz, icon); return; } } /* 复现 menu_draw_grid_cell 的绘制序列(卡底 → 图标;名称与图标区隔离,无关像素) */ static void draw_grid_cell(int x, int y, int w, int h, meow_img *icon) { meow_gfx_fill_round(x, y, w, h, MENU_GRID_RADIUS, TH_SURFACE_CONTAINER); int sz = (h > MENU_GRID_ICON + 40) ? MENU_GRID_ICON : h - 40; int icx = x + (w - sz) / 2, icy = y + 12; menu_draw_icon_like(icon, icx, icy, sz); } int main(void) { setbuf(stdout, NULL); /* 立即冲刷,abort 前也能看到 printf */ setenv("MEOW_UI_DUMP", "/tmp/icon.ppm", 1); meow_ui_opts o = { FB_W, FB_H }; (void)o; assert(meow_ui_init(&o) == 0); assert(meow_ui_width() == FB_W && meow_ui_height() == FB_H); int apps_top = 130; /* 模拟主菜单 app 区顶缘 */ int cx = MENU_GRID_PAD, cy = apps_top; int cw = (FB_W - 2 * MENU_GRID_PAD - (MENU_GRID_COLS - 1) * MENU_GRID_GAP) / MENU_GRID_COLS; int ch = MENU_GRID_CARD_H; int sz = (ch > MENU_GRID_ICON + 40) ? MENU_GRID_ICON : ch - 40; int icx = cx + (cw - sz) / 2, icy = cy + 12; /* ---------- 分支 A:有图(meow_ui_img_cover,不透明 226×226,meow_img_load_file) ---------- */ { meow_img *im = meow_img_load_file("/tmp/icon226.png"); assert(im != NULL); assert(meow_img_w(im) == 226 && meow_img_h(im) == 226); meow_ui_clear(0x000000); draw_grid_cell(cx, cy, cw, ch, im); int tr, tg, tb; UNPK(IMG_COLOR, tr, tg, tb); int got = region_has(icx, icy, sz, sz, tr, tg, tb); printf("A(image 226x226 opaque): icon region(%d,%d %dx%d) img-color px = %d\n", icx, icy, sz, sz, got); assert(got > 0 && "meow_ui_img_cover 未能把不透明图标画进图标区"); int sr, sg, sb; UNPK(TH_SURFACE_CONTAINER, sr, sg, sb); int cardpx = region_has(icx, icy, sz, sz, sr, sg, sb); printf(" card-color px inside icon region = %d\n", cardpx); assert(cardpx == 0 && "图标区仍被卡底色占据(图标被盖掉/未画出)"); meow_img_free(im); } /* ---------- 分支 A':全透明图标(alpha=0)——修复后垫块垫底,图标区非空 ---------- */ { meow_img *im = meow_img_load_file("/tmp/icon226_trans.png"); assert(im != NULL); meow_ui_clear(0x000000); draw_grid_cell(cx, cy, cw, ch, im); int pr, pg, pb; UNPK(TH_PRIMARY, pr, pg, pb); int got = region_has(icx, icy, sz, sz, pr, pg, pb); printf("A'(transparent icon, fixed): icon region primary(tile) px = %d\n", got); assert(got > 0 && "全透明图标不再被放过:垫块应画出(图标区非空)"); /* 垫块圆角角落会透出卡底色,属几何本性 */ meow_img_free(im); } /* ---------- 分支 B:无图(primary 圆角块占位) ---------- */ { meow_ui_clear(0x000000); draw_grid_cell(cx, cy, cw, ch, NULL); int pr, pg, pb; UNPK(TH_PRIMARY, pr, pg, pb); int got = region_has(icx, icy, sz, sz, pr, pg, pb); printf("B(placeholder primary round): icon region primary px = %d\n", got); assert(got > 0 && "占位圆角块未画进图标区"); /* 圆角块角落会透出卡底色——这是几何本性,非 bug;只要求主色像素显著多 */ int sr, sg, sb; UNPK(TH_SURFACE_CONTAINER, sr, sg, sb); int cardpx = region_has(icx, icy, sz, sz, sr, sg, sb); printf(" card-color px in icon region (round corners) = %d\n", cardpx); assert(got > sz * sz * 3 / 4 && "占位圆角块覆盖面积过小(未真正画到图标区)"); } printf("PASS test_host_icon (A image + B placeholder render in icon region)\n"); return 0; }