c/ui/gradient_check.c · 9924 字节 · 下载原文
/* gradient_check.c —— **渐变自检**(用户 2026-10-07:「渐变好像失效了」→「专门写个程序测试渐变」)
* gradient_check.c -- **Gradient self-check** (user report, 2026-10-07)
*
* ## 为什么单独写一个
* ## Why a dedicated program
*
* `draw_brush.c` 那类"体检"图**靠肉眼看**,而桌面与设备看到的不是一回事。这里改成:
* **每条判据画一条自己的带**,并且**把结论同时打印出来、也画在屏幕上** ——
* 打印那份给无头跑(`vmlcli`)用,屏幕那份给设备抓帧用,两边读的是同一批字。
* The picture-based checks rely on eyes; this one gives every criterion its own band and reports
* both to stdout (for headless `vmlcli`) and on screen (for device captures) -- same text, two exits.
*
* ## 判据
* ## Criteria
*
* G0_PROBE 取证本身有效吗:画一个**已知纯色**再读回(见下"哨兵")
* G1_LINEAR `ui_2d_gradient` + `ui_2d_rect_grad`:**左端红、右端蓝**
* G2_RADIAL 同一套换 `radial=1`:圆心与边缘不同
* G3_CIRCLE `ui_2d_circle_grad`:圆心与边缘不同
* G4_PEN `ui_brush_linear` + `ui_brush_set_pen`:**描边**用渐变
* G5_POLY `ui_2d_polyline(…, grad)`:折线描边用渐变
* G6_SOLID 对照:纯色矩形**两端应当相同**(这条若 FAIL 说明取样本身不可信)
*
* ⚠ **接口是照着 `Lib/c/vml_ui.h` 核过的**,不是猜的:渐变填充有自己的入口
* (`ui_2d_rect_grad` / `ui_2d_circle_grad`,直接收渐变**名字**),
* 而 `ui_2d_rect(x,y,w,h,color,fill,lw,radius)` 收的是**颜色**、不吃刷子。
*
* ## 哨兵:`0` 是合法颜色,不能拿它当"读不回来"
* ## Sentinel: 0 is a legal color, do not use it as "unreadable"
*
* 第一版用「两端都读到 0 ⇒ 记 SKIP(未取证)」来判断读回可不可用 —— **错了**:
* 渐变失效时画出来的是**纯黑**,读回**正好就是 0**,于是**真失败被吞成"未取证"**,
* 一条都没报错(本仓 E142:缺席判据要先过"取证本身有效")。
* 正解 = **先画一个已知纯色并读回**(`G0_PROBE`),它过了才说明读回可信;
* 之后"读到 0"就是"这里真的是黑的",**该报 FAIL 就报 FAIL**。
* The first version treated "both ends read 0" as "read-back unavailable" -- wrong: a broken
* gradient paints solid black, which reads back as exactly 0, so the real failure was swallowed as
* SKIP. `G0_PROBE` draws a known solid color first; only if that reads back is a 0 believed.
*
* ## 实测(2026-10-07,桌面 `vmlcli`,480×640)
* ## Measured (2026-10-07, desktop `vmlcli`, 480x640)
*
* G1/G2/G3 画出来的是**纯黑**(几何正确、填充是 0)⇒ 渐变解析成了"没有颜色"
* G4/G5 **什么都没画**(带子里只有底色)
* G6 正常(255,48,48)
* 读回 **可用**(G6 与 PROBE 都读到了真值)
*/
#include <vml_ui.h>
#define RED_A 0xFFFF3030 /* 红 / red */
#define BLUE_B 0xFF3030FF /* 蓝 / blue */
#define PROBE_C 0xFF20C040 /* 哨兵用的绿(取值刻意**既不是**红/蓝,也**不是底色**) */
#define BG 0xFF101018
static int fails, total, line, sw2, sh2, probe_ok;
/* 放一行结论:屏幕上一条(设备抓帧看),stdout 一条(无头跑看)。 */
/* One verdict line: one on screen (device capture), one on stdout (headless). */
static void say(const char* key, const char* state, int bad)
{
ui_2d_text(8, line, key, bad ? 0xFFE05050 : 0xFF40D060, 13, VML_ANCHOR_LEFT);
ui_2d_text(120, line, state, bad ? 0xFFE05050 : (state[0] == 'S' ? 0xFF909098 : 0xFF40D060), 13, VML_ANCHOR_LEFT);
line += 14;
}
static int rgb(int c) { return c & 0xFFFFFF; }
static int differs(int c1, int c2)
{
int dr = ((c1 >> 16) & 255) - ((c2 >> 16) & 255);
int dg = ((c1 >> 8) & 255) - ((c2 >> 8) & 255);
int db = (c1 & 255) - ((c2 & 255));
if (dr < 0) dr = -dr;
if (dg < 0) dg = -dg;
if (db < 0) db = -db;
return dr + dg + db > 60;
}
/* 通用判据:取两点比一比。`must_differ` = 渐变类(两端必须不同)/ 对照类(必须相同)。 */
/* Generic: compare two points; must_differ for gradients, must-match for the control. */
static void verdict(const char* key, int x1, int y1, int x2, int y2, int must_differ)
{
int a, b, d, bad;
const char* state;
total++;
ui_2d_present();
a = ui_2d_get_pixel(x1, y1);
b = ui_2d_get_pixel(x2, y2);
d = differs(a, b);
if (!probe_ok) {
state = "SKIP(读回不可用)";
bad = 0;
} else if (must_differ ? !d : d) {
state = "FAIL";
bad = 1;
fails++;
} else {
state = "OK";
bad = 0;
}
say(key, state, bad);
printf("%s=%s a=%06X b=%06X (x1=%d,y1=%d x2=%d,y2=%d)\n",
key, state, rgb(a), rgb(b), x1, y1, x2, y2);
ui_2d_present();
}
int main(void)
{
int sw, sh, band, cy, r;
int pts[8];
ui_scr_set_keep_on(1);
/* 尺寸交给宿主(0,0 = 用默认值);**开窗之后再问一次**才是真实画布 */
/* Let the host size it (0,0 = defaults); ask again *after* opening for the real canvas. */
ui_win_open_ex("渐变自检 / gradient check", 0, 0, VML_WIN_PORTRAIT, VML_WIN_NO_GAMEPAD);
sw = ui_scr_get_w();
sh = ui_scr_get_h();
if (sw <= 0 || sh <= 0) { sw = 320; sh = 480; }
sw2 = sw; sh2 = sh;
band = (sh - 40) / 8; /* 8 条带:G0 探测 + G1..G6 + 总账 */
if (band < 12) band = 12;
line = band * 7 + 4; /* 结论文字从第 7 条带开始,避开图元 */
ui_2d_clear(BG);
/* ── G0 哨兵:画一个**已知纯色**再读回。它过了,"读到 0"才可信 ── */
/* ── G0 sentinel: draw a known solid color and read it back ── */
ui_2d_rect(0, 4, sw, 8, PROBE_C, 1, 0, 0);
ui_2d_present();
{
int p = ui_2d_get_pixel(3, 8);
probe_ok = (rgb(p) == rgb(PROBE_C)); /* 精确比对;底色/黑都算不通过 */
printf("G0_PROBE=%s 读到 %06X 期望 %06X\n",
probe_ok ? "OK" : "FAIL(读回不可用)", rgb(p), rgb(PROBE_C));
if (!probe_ok) { fails++; total++; }
say("G0_PROBE", probe_ok ? "OK" : "FAIL(读回不可用)", !probe_ok);
ui_2d_present();
}
/* ── G1 线性渐变:定义 + `ui_2d_rect_grad` 填充 ── */
/* ── G1 linear: define it, then fill a rect with it ── */
ui_2d_gradient("g1", 0, RED_A, BLUE_B, 0, 0, 1000, 0);
ui_2d_rect_grad(0, band * 1, sw, band, "g1", 0);
verdict("G1_LINEAR", 3, band * 1 + band / 2, sw - 4, band * 1 + band / 2, 1);
/* ── G2 径向渐变(同一个 id 换 radial=1 重定义)── */
/* ── G2 radial (redefine the id with radial=1) ── */
ui_2d_gradient("g2", 1, RED_A, BLUE_B, 0, 0, 0, 0);
ui_2d_rect_grad(0, band * 2, sw, band, "g2", 0);
cy = band * 2 + band / 2;
verdict("G2_RADIAL", sw / 2, cy, sw - 6, cy, 1);
/* ── G3 渐变圆 ── */
/* ── G3 gradient-filled circle ── */
r = band / 2 - 2;
if (r < 4) r = 4;
cy = band * 3 + band / 2;
ui_2d_circle_grad(sw / 2, cy, r, "g1");
verdict("G3_CIRCLE", sw / 2, cy, sw / 2 + r - 3, cy, 1);
/* ── G4 渐变**画笔**(描边):`ui_brush_linear` + `ui_brush_set_pen` ── */
/* ── G4 gradient *pen* (stroke) ── */
{
int b = ui_brush_linear(RED_A, BLUE_B, 0, 0, 1000, 0);
ui_brush_set_pen(b, 8, 0, 0, 0);
ui_2d_rect(4, band * 4 + 6, sw - 8, band - 12, 0, 0, 8, 0);
ui_brush_reset();
/* ⚠ 取**描边中心线**(矩形从 `band*4+6` 起、描边宽 8 ⇒ 中心在 `+10`),
* 不取靠近上边缘的 `+8`:那里离边只有 2px,而各平台取整/偏移差 1~3px 就会掉进底色
* ⇒ 两端都读到背景色、判成"没有渐变"(**实测:同一份代码桌面绿、iOS 红**,是判据取样点太脆)。 */
/* ⚠ Sample the **centre line of the stroke** (rect starts at `band*4+6` with an 8px stroke
* => centre at `+10`), not `+8` near the top edge: 2px from the edge means a 1-3px
* platform rounding difference drops into the background and reports "no gradient"
* (**measured: same source, green on desktop, red on iOS** -- the sample point was brittle). */
verdict("G4_PEN", 3, band * 4 + 10, sw - 4, band * 4 + 10, 1);
}
/* ── G5 折线描边用渐变(`ui_2d_polyline(pts, count, stroke, width, grad)`)── */
/* ── G5 polyline stroked with a gradient ── */
pts[0] = 4; pts[1] = band * 5 + band / 2;
pts[2] = sw / 3; pts[3] = band * 5 + 6;
pts[4] = sw * 2 / 3; pts[5] = band * 5 + band - 6;
pts[6] = sw - 4; pts[7] = band * 5 + band / 2;
ui_2d_polyline(pts, 4, 1, 6, "g1");
verdict("G5_POLY", 4, band * 5 + band / 2, sw - 4, band * 5 + band / 2, 1);
/* ── G6 对照:纯色两端应当相同 ── */
/* ── G6 control: a solid fill must read the same at both ends ── */
ui_2d_rect(0, band * 6, sw, band, RED_A, 1, 0, 0);
verdict("G6_SOLID", 3, band * 6 + band / 2, sw - 4, band * 6 + band / 2, 0);
/* 屏上也写一行总账 */
/* A one-line summary on screen as well. */
ui_2d_text(sw / 2, sh - 10, fails ? "SOME FAILED" : "ALL OK",
fails ? 0xFFE05050 : 0xFF40D060, 16, VML_ANCHOR_CENTER);
printf("GRADIENT_SUMMARY total=%d fails=%d probe_ok=%d\n", total, fails, probe_ok);
ui_2d_present();
ui_win_close();
return fails;
}