主仓 SDK 的图片层原本是 220 行 stb 直通(整幅解码后重采样)。公开仓 MeowOS-SDK 的同一子系统已被真实 App(tuku 阅读器)驱动并做过峰值实测与像素比对,故以公开仓 为权威方向并入主仓 meow_ui.a,两个方向的新功能都保留(逐文件 diff 确认公开仓是 严格超集:无一处「双方互有增减」)。 - 新增 meow_jpeg.c / meow_jpeg.h:DCT 域缩解 1/8、1/4、1/2(N=1/2/4 点 IDCT), 渐进式只解到首个精细化扫描;不支持项(CMYK/YCCK、算术编码、过订阅 DHT、16bit 量化表)返回 0 由调用方回退 stb - 新增 meow_png_stream.c:libz 逐行 inflate + 反滤波,按步长丢行丢列直写目标尺寸; BMP 按目标行 seek + 行内跳列(8/16/24/32bpp) - meow_ui_img.c + include/meow_ui_image.h:新增 meow_img_pick_scale / load_file_step / load_file_step_ex / resample 四个公开入口;_dim 与 _mem_dim 先进后端,失败/不支持才回退 stb(对外语义不变) - include/meow_ui_draw.h + meow_ui_gfx.c:M32 RGB565 页图 5 函数 (meow_gfx_rgb565 / rgba_to_rgb565 / blit_rgb565 / _scaled)。gfx 保留主仓的 `#include "../meow_launcherd/meow_font.h"` 与 <time.h>(公开仓自 vendored meow_font.h 且无 <time.h>,机械覆盖会丢这两行) - meow_jpeg.h 保持内部:不进 meow_ui.h 伞头,include/ 下无任何头暴露 meow_jpeg_* - build_sdk.sh:OBJS 插入 meow_jpeg / meow_png_stream(32→34);成员数 76→78(+meow_font.o +43 freetype),--verify 全为 ELF32 MIPS32 rel2/o32 - 测试:新增 test_meow_img_stream.c(S1-S16),test_meow_img_budget.c 同步公开仓 版(B9 断言收紧为 p_new <= p_old);22 个既有宿主测试的编译注释行补 meow_jpeg.c / meow_png_stream.c / -lz(meow_ui_img.c 现引用后端符号,不加即 undefined reference) 宿主实测:S1-S16 全绿(S7 与 stb 网格像素一致:JPEG mad 4.35-13.69、PNG/BMP mad 0.00;S9 回退矩阵 CMYK/1bpp BMP/Adam7 均拒绝且 stb 仍成功),B1-B9 全绿 (B9 PNG 峰值 25.65→0.61 MiB、BMP 14.65→0.59 MiB)。回归 test_meow_ui_widgets / scroll / ime_fix / test_umbrella 全过。zlib 在应用链接期解析:meow_ui.a 只留 inflate* 未定义符号,四个 App 构建脚本原本就带 $SYSROOT/lib/libz.a,静态 linkcheck(meow_ui.a + libz.a -lpthread)通过。 注:主仓 patch_v2/reader_app 仍自带 vendored stb、不用 meow_img_*,本单不改变 阅读器的内存占用;RGB565 页图接入 reader_app 是另一个任务。
232 lines
10 KiB
C
232 lines
10 KiB
C
/* test_meow_ui_box.c — §12.1 meow_box 线性布局引擎宿主验证:
|
||
* 布局求解矩阵(固定/weight/wrap/嵌套/对齐)、margin/padding/gap、交叉轴对齐、view 集成。
|
||
* 纯逻辑(只创控件 + arrange + 断言矩形),不依赖 /dev/fb0/字库;不绘制。
|
||
*
|
||
* 编译(宿主 gcc,链真实 widget/draw/activity/theme/gfx + 打桩 font/global):
|
||
* gcc -o test_meow_ui_box test_meow_ui_box.c test_host_stub.c \
|
||
* meow_ui_gfx.c meow_ui_time.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
||
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c meow_ui_str.c \
|
||
* meow_pinyin.c meow_ui_draw.c meow_ui_view.c meow_ui_box.c meow_ui_anim.c meow_ui_scroll.c \
|
||
* meow_ui_img.c meow_ui_media.c meow_notify_ipc.c stb_image.c meow_jpeg.c meow_png_stream.c \
|
||
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm -lz
|
||
* 运行:./test_meow_ui_box
|
||
*/
|
||
#include "meow_ui.h"
|
||
#include "meow_ui_internal.h" /* meow_widget_rect:真实几何断言 */
|
||
#include "meow_ui_view.h"
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
static int g_pass = 0, g_fail = 0;
|
||
#define CHECK(cond, ...) do { \
|
||
if (cond) { g_pass++; } \
|
||
else { g_fail++; printf("FAIL %s:%d ", __FILE__, __LINE__); printf(__VA_ARGS__); printf("\n"); } \
|
||
} while (0)
|
||
|
||
static void wr(meow_widget *w, int out[4]) { meow_widget_rect(w, out); }
|
||
static void boxr(meow_box *b, int out[4]) { meow_box_rect(b, out); }
|
||
|
||
/* 创建固定尺寸按钮(既有无绝对定位;注册到绘制物注册表即可) */
|
||
static meow_widget *mk(int w, int h)
|
||
{
|
||
meow_widget *b = meow_btn_create(0, 0, w, h, "X");
|
||
return b;
|
||
}
|
||
|
||
/* ---- 竖直列:固定 + padding + gap + CROSS_START ---- */
|
||
static void test_col_fixed(void)
|
||
{
|
||
meow_box *bx = meow_box_new(MEOW_BOX_V);
|
||
CHECK(bx != NULL, "box_new V");
|
||
meow_box_set_rect(bx, 0, 0, 480, 720);
|
||
meow_box_set_padding(bx, 20, 20, 20, 20);
|
||
meow_box_set_gap(bx, 10);
|
||
meow_box_set_align(bx, MEOW_BOX_ALIGN_START, MEOW_BOX_CROSS_START);
|
||
meow_widget *a = mk(100, 50), *b = mk(120, 50), *c = mk(80, 50);
|
||
CHECK(meow_box_add(bx, a, MEOW_BOX_FIXED, 0) == 0, "add a");
|
||
CHECK(meow_box_add(bx, b, MEOW_BOX_FIXED, 0) == 0, "add b");
|
||
CHECK(meow_box_add(bx, c, MEOW_BOX_FIXED, 0) == 0, "add c");
|
||
CHECK(meow_box_count(bx) == 3, "count");
|
||
meow_box_arrange(bx);
|
||
int r[4];
|
||
wr(a, r); CHECK(r[0] == 20 && r[1] == 20 && r[2] == 100 && r[3] == 50, "A x=20 y=20 w100 h50 got %d,%d,%d,%d", r[0], r[1], r[2], r[3]);
|
||
wr(b, r); CHECK(r[0] == 20 && r[1] == 80 && r[2] == 120, "B y=80 got %d,%d,%d,%d", r[0], r[1], r[2], r[3]);
|
||
wr(c, r); CHECK(r[0] == 20 && r[1] == 140 && r[2] == 80, "C y=140 got %d,%d,%d,%d", r[0], r[1], r[2], r[3]);
|
||
meow_box_free(bx);
|
||
}
|
||
|
||
/* ---- 横向行:END 对齐 + 交叉轴中心 ---- */
|
||
static void test_row_end_center(void)
|
||
{
|
||
meow_box *bx = meow_box_new(MEOW_BOX_H);
|
||
meow_box_set_rect(bx, 10, 0, 480, 200);
|
||
meow_box_set_padding(bx, 10, 10, 10, 10);
|
||
meow_box_set_gap(bx, 5);
|
||
meow_box_set_align(bx, MEOW_BOX_ALIGN_END, MEOW_BOX_CROSS_CENTER);
|
||
meow_widget *a = mk(100, 50), *b = mk(100, 50), *c = mk(100, 50);
|
||
CHECK(meow_box_add(bx, a, MEOW_BOX_FIXED, 0) == 0, "add");
|
||
CHECK(meow_box_add(bx, b, MEOW_BOX_FIXED, 0) == 0, "addb");
|
||
CHECK(meow_box_add(bx, c, MEOW_BOX_FIXED, 0) == 0, "addc");
|
||
meow_box_arrange(bx);
|
||
/* 内容区 x∈[20,470] cw=460;foot=100*3+5*2=310;leftover=150;END → off0=150 */
|
||
int r[4];
|
||
wr(a, r);
|
||
CHECK(r[0] == 20 + 150, "END first x=170 got %d", r[0]);
|
||
/* 交叉轴中心:cy=10(pad_t);cross_size=ch=180,cross_base=50 → pad=130 → cross=65 → y=10+65=75 */
|
||
CHECK(r[1] == 75 && r[3] == 50, "center y=75 got %d,%d", r[1], r[3]);
|
||
wr(b, r); CHECK(r[0] == 20 + 150 + 100 + 5, "END second x=%d", r[0]);
|
||
wr(c, r); CHECK(r[0] == 380, "END third x=380 got %d", r[0]); /* 170+100+5+100+5+? = 380 */
|
||
meow_box_free(bx);
|
||
}
|
||
|
||
/* ---- weight 均分:竖直,两个 weight 1:3 ---- */
|
||
static void test_weight(void)
|
||
{
|
||
meow_box *bx = meow_box_new(MEOW_BOX_H);
|
||
meow_box_set_rect(bx, 0, 0, 480, 200);
|
||
/* 内容区 480 宽(无 pad/gap)。child 固定 80 + 两个权重 1:3 */
|
||
meow_widget *fixed = mk(80, 200), *w1 = mk(0, 200), *w2 = mk(0, 200);
|
||
CHECK(meow_box_add(bx, fixed, MEOW_BOX_FIXED, 0) == 0, "fixed");
|
||
CHECK(meow_box_add(bx, w1, MEOW_BOX_WEIGHT, 1) == 0, "w1");
|
||
CHECK(meow_box_add(bx, w2, MEOW_BOX_WEIGHT, 3) == 0, "w2");
|
||
meow_box_arrange(bx);
|
||
/* leftover = 480 - 80 = 400;w1=100, w2=300 */
|
||
int r[4];
|
||
wr(fixed, r); CHECK(r[0] == 0 && r[2] == 80, "fixed x=0 w80 got %d,%d", r[0], r[2]);
|
||
wr(w1, r); CHECK(r[0] == 80 && r[2] == 100, "w1 x=80 w100 got %d,%d", r[0], r[2]);
|
||
wr(w2, r); CHECK(r[0] == 180 && r[2] == 300, "w2 x=180 w300 got %d,%d", r[0], r[2]);
|
||
meow_box_free(bx);
|
||
}
|
||
|
||
/* ---- WRAP 内容测量(measure 回调给 min/preferred) ---- */
|
||
static void wrap_measure(void *ctx, int *mn, int *pf) { (void)ctx; *mn = 40; *pf = 120; }
|
||
static void test_wrap(void)
|
||
{
|
||
meow_box *bx = meow_box_new(MEOW_BOX_H);
|
||
meow_box_set_rect(bx, 0, 0, 480, 100);
|
||
meow_box_set_gap(bx, 10);
|
||
meow_box_set_align(bx, MEOW_BOX_ALIGN_START, MEOW_BOX_CROSS_START);
|
||
meow_widget *w = mk(5, 40); /* 尺寸无关,wrap 用 measure */
|
||
CHECK(meow_box_add_ex(bx, w, MEOW_BOX_WRAP, 0, 40, 120, wrap_measure, NULL) == 0, "wrap-add");
|
||
meow_box_arrange(bx);
|
||
int r[4]; wr(w, r);
|
||
/* wrap 主轴向 preferred=120;cross_start 交叉=40(widget h 落 V=40?这是 H box,交叉=h) */
|
||
CHECK(r[2] == 120, "wrap pref w=120 got %d", r[2]);
|
||
meow_box_free(bx);
|
||
}
|
||
|
||
/* ---- 嵌套:Row 内嵌 Column ---- */
|
||
static void test_nested(void)
|
||
{
|
||
meow_box *row = meow_box_new(MEOW_BOX_H);
|
||
meow_box *col = meow_box_new(MEOW_BOX_V);
|
||
meow_box_set_rect(row, 0, 0, 480, 200);
|
||
meow_box_set_rect(col, 0, 0, 200, 200);
|
||
meow_box_set_gap(row, 10);
|
||
meow_widget *a = mk(100, 50), *b = mk(100, 50), *c = mk(100, 50);
|
||
meow_box_add(row, a, MEOW_BOX_FIXED, 0);
|
||
meow_box_add_sub(row, col, MEOW_BOX_FIXED, 0);
|
||
CHECK(meow_box_add_sub(row, col, MEOW_BOX_FIXED, 0) < 0, "sub dedupe");
|
||
meow_box_add(col, b, MEOW_BOX_FIXED, 0);
|
||
meow_box_add(col, c, MEOW_BOX_FIXED, 0);
|
||
meow_box_arrange(row);
|
||
int r[4];
|
||
wr(a, r); CHECK(r[0] == 0 && r[2] == 100, "row a x0 w100 got %d,%d", r[0], r[2]);
|
||
int cr[4]; boxr(col, cr); CHECK(cr[0] == 110 && cr[1] == 0 && cr[2] == 200, "sub box x=110 got %d", cr[0]);
|
||
/* col 内 b,c 纵向:y 0,50(col y=0, gap0, cross_start) */
|
||
meow_widget_rect(b, r); CHECK(r[0] == 110 && r[1] == 0, "col b y0 got %d", r[1]);
|
||
meow_widget_rect(c, r); CHECK(r[1] == 50, "col c y50 got %d", r[1]);
|
||
meow_box_free(row); meow_box_free(col);
|
||
}
|
||
|
||
/* ---- margin(widget opts)+ 交叉轴 STRETCH ---- */
|
||
static void test_margin_stretch(void)
|
||
{
|
||
meow_box *bx = meow_box_new(MEOW_BOX_V);
|
||
meow_box_set_rect(bx, 0, 0, 480, 300);
|
||
meow_box_set_align(bx, MEOW_BOX_ALIGN_START, MEOW_BOX_CROSS_STRETCH);
|
||
meow_widget *a = mk(100, 60);
|
||
meow_widget_opts o; memset(&o, 0, sizeof o);
|
||
o.margin_x = 30; o.margin_y = 10;
|
||
CHECK(meow_widget_set_opts(a, &o) == 0, "set opts");
|
||
meow_box_add(bx, a, MEOW_BOX_FIXED, 0);
|
||
meow_box_arrange(bx);
|
||
int r[4]; wr(a, r);
|
||
/* V:交叉轴=x,STRETCH → w = cw - 2*margin_x = 480 - 60 = 420;x = margin_x = 30。
|
||
* 主轴 y:lead=margin_y=10 → y=10;ext=h=60。 */
|
||
CHECK(r[0] == 30 && r[2] == 420, "stretch w=420 x=30 got %d,%d", r[0], r[2]);
|
||
CHECK(r[1] == 10 && r[3] == 60, "y=10 h=60 got %d,%d", r[1], r[3]);
|
||
meow_box_free(bx);
|
||
}
|
||
|
||
/* ---- SPACE_BETWEEN ---- */
|
||
static void test_space_between(void)
|
||
{
|
||
meow_box *bx = meow_box_new(MEOW_BOX_H);
|
||
meow_box_set_rect(bx, 0, 0, 500, 100);
|
||
meow_box_set_align(bx, MEOW_BOX_ALIGN_SPACE_BETWEEN, MEOW_BOX_CROSS_START);
|
||
meow_widget *a = mk(100, 50), *b = mk(100, 50), *c = mk(100, 50);
|
||
meow_box_add(bx, a, MEOW_BOX_FIXED, 0);
|
||
meow_box_add(bx, b, MEOW_BOX_FIXED, 0);
|
||
meow_box_add(bx, c, MEOW_BOX_FIXED, 0);
|
||
meow_box_arrange(bx);
|
||
/* cw=500;foot=300;leftover=200;n-1=2 → 每 gap += 100 → A.x=0 B.x=200 C.x=400 */
|
||
int r[4];
|
||
wr(a, r); CHECK(r[0] == 0, "sb A x0 got %d", r[0]);
|
||
wr(b, r); CHECK(r[0] == 200, "sb B x200 got %d", r[0]);
|
||
wr(c, r); CHECK(r[0] == 400, "sb C x400 got %d", r[0]);
|
||
meow_box_free(bx);
|
||
}
|
||
|
||
/* ---- view 集成:meow_view_set_layout + relayout ---- */
|
||
static void test_view_layout(void)
|
||
{
|
||
meow_view *v = meow_view_create(900, MEOW_VIEW_ACTIVITY, NULL, NULL);
|
||
CHECK(v != NULL, "view create");
|
||
v->x = 0; v->y = 0; v->w = 480; v->h = 720;
|
||
meow_box *bx = meow_box_new(MEOW_BOX_V);
|
||
meow_box_set_gap(bx, 8);
|
||
meow_box_set_align(bx, MEOW_BOX_ALIGN_START, MEOW_BOX_CROSS_START);
|
||
meow_widget *a = mk(300, 50), *b = mk(300, 50);
|
||
meow_box_add(bx, a, MEOW_BOX_FIXED, 0);
|
||
meow_box_add(bx, b, MEOW_BOX_FIXED, 0);
|
||
meow_view_add_widget(v, a);
|
||
meow_view_add_widget(v, b);
|
||
meow_view_set_layout(v, bx); /* relayout 立即排一次 */
|
||
int r[4];
|
||
wr(a, r); CHECK(r[0] == 0 && r[1] == 0 && r[2] == 300, "view a x0 y0 w300 got %d,%d,%d", r[0], r[1], r[2]);
|
||
wr(b, r); CHECK(r[1] == 58, "view b y=58 got %d", r[1]); /* 0 + 50 + gap8 */
|
||
meow_view_free(v);
|
||
meow_box_free(bx);
|
||
}
|
||
|
||
/* ---- 越界去重 / 池满 ---- */
|
||
static void test_edge(void)
|
||
{
|
||
meow_box *bx = meow_box_new(MEOW_BOX_H);
|
||
meow_widget *a = mk(10, 10), *dup = mk(10, 10);
|
||
CHECK(meow_box_add(bx, dup, MEOW_BOX_FIXED, 0) == 0, "add dup");
|
||
CHECK(meow_box_add(bx, dup, MEOW_BOX_FIXED, 0) < 0, "dedupe");
|
||
CHECK(meow_box_add(bx, NULL, MEOW_BOX_FIXED, 0) < 0, "null add rejected");
|
||
meow_box_widget_at(bx, 0) ? 0 : 0;
|
||
CHECK(meow_box_widget_at(bx, 0) == dup, "widget_at 0");
|
||
CHECK(meow_box_sub_at(bx, 0) == NULL, "sub_at null");
|
||
meow_box_free(bx);
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
test_col_fixed();
|
||
test_row_end_center();
|
||
test_weight();
|
||
test_wrap();
|
||
test_nested();
|
||
test_margin_stretch();
|
||
test_space_between();
|
||
test_view_layout();
|
||
test_edge();
|
||
printf("box: %d pass / %d fail\n", g_pass, g_fail);
|
||
return g_fail ? 1 : 0;
|
||
}
|