c/ui/draw_colors.c · 12801 字节 · 下载原文
/* draw_colors.c —— 绘图接口的**颜色体检**:每个能上色的调用各画一格,一屏看完。
* draw_colors.c — a **color health check** for the drawing API: every color-capable call
* paints one cell, so one screen shows them all.
*
* ## 为什么要有这个程序(它与 draw_prims.c 的分工)
* ## Why this program exists (and how it splits the work with draw_prims.c)
*
* `draw_prims.c` 管的是**形状**(圆角是不是圆的、折线开不开口、路径挖不挖洞),
* `draw_prims.c` covers **shapes** (is a rounded rect really round, is a polyline open,
* does a path punch a hole),
* 本程序只管**颜色**:同一条指令画出来的像素,是不是你给的那个色。
* this program covers only **color**: is the pixel a call drew the color you asked for.
*
* 这两件事会**各自独立地坏**。v0.96.297 修的那条就只坏颜色——
* The two break **independently**. The bug fixed in v0.96.297 broke color only —
* 平台把渐变挂成 Android `Paint` 的 shader,而 **shader 优先级高于颜色**,
* the platform attaches a gradient as an Android `Paint` shader, and **a shader outranks a color**,
* 于是"这一帧里用过一个渐变"之后,后面所有纯色填充全被它接管
* so once a gradient has been used in a frame, every later solid fill is taken over by it
* (详细机制见 `WayCoder.Maui/Services/MauiVectorTarget.cs` 类注释「渐变的余荫」)。
* (the full mechanism is in the class comment of `WayCoder.Maui/Services/MauiVectorTarget.cs`).
* 症状是计算器的四档按键配色被**整体抹平**,而形状、文字、布局全对。
* The symptom was the calculator's four key colors being **flattened into one**, while shapes, text and layout were all fine.
*
* ## 判据:**取像素,不靠眼睛**
* ## The criterion: **read pixels, do not trust your eyes**
*
* 全部图形取**同一个颜色** `0xFF3C6EB4`(r/g/b 三个通道互不相同)——
* every figure uses **the same color** `0xFF3C6EB4` (its r/g/b channels all differ) —
* 通道序写反、被别的刷子接管、alpha 被吃掉,都会让它变成另一个色,一眼可辨。
* a swapped channel order, another brush taking over, or a swallowed alpha all turn it into a different color, visible at a glance.
*
* for k in 0..14: 取第 k 格**格心**的像素 == #3C6EB4
* for k in 0..14: read the pixel at the **center** of cell k == #3C6EB4
*
* 第 10 格是**渐变对照格**(本来就该是渐变,不参与断言),
* cell 10 is the **gradient control cell** (it is meant to be a gradient, so it is not asserted),
* 第 11~13 格是**回归格**:它们排在用过渐变之后,必须还是 `#3C6EB4`。
* cells 11..13 are **regression cells**: they come after a gradient was used and must still be `#3C6EB4`.
* 修 v0.96.297 那条之前,这三格画出来是**上一次那个渐变的颜色**。
* Before the v0.96.297 fix, these three cells were painted with **the color of the previous gradient**.
*
* 用法(手机上):`vml run examples/draw_colors.c`,然后截屏逐格比色。
* Usage (on a phone): `vml run examples/draw_colors.c`, then screenshot and compare each cell.
* ⚠ C 前端 + 汇编 + 链接在手机上要一分多钟,别当成卡死。
* ⚠ The C frontend plus assembler plus linker takes over a minute on a phone; do not mistake it for a hang.
*
* ## 格子布局(3 列 × 5 行,格心取样)
* ## Cell layout (3 columns x 5 rows, sampled at cell centers)
*
* 0 实心矩形 1 圆角矩形 2 实心圆
* 0 solid rect 1 rounded rect 2 solid circle
* 3 实心椭圆 4 实心多边形 5 粗折线
* 3 solid ellipse 4 solid polygon 5 thick polyline
* 6 粗直线 7 路径填充 8 路径描边
* 6 thick line 7 path fill 8 path stroke
* 9 空心矩形描边 10 线性渐变 11 渐变后的纯色矩形 ← 回归格
* 9 stroked rect 10 linear grad 11 solid rect after grad <- regression cell
* 12 径向渐变后的圆 13 再后一格纯色 14 收尾纯色圆
* 12 circle after radial grad 13 one more solid cell 14 closing solid circle
*
* (外加一格文字,文字用大号粗体、采样点落在笔画上。)
* (Plus one text cell; the text is large and bold and the sample point lands on a stroke.)
*
* ## ⚠ 路径指令 `ui_2d_path` 收的是**绝对场景坐标**
* ## ⚠ The path command `ui_2d_path` takes **absolute scene coordinates**
*
* 它没有"相对某个格子"的写法,所以本程序自己把格心坐标拼进 `d` 字符串里
* It has no "relative to some cell" form, so this program builds cell-center coordinates into the `d` string itself
* (~~本前端不认 `sprintf`~~ —— **2026-10-04 复验:`sprintf`/`printf` 都已正常**,就手写了一个十几行的整数转字符串(照旧保留,能跑就别动))。
* (~~this frontend does not know `sprintf`~~ -- **re-checked 2026-10-04: `sprintf`/`printf` both work now**; a dozen-line integer-to-string routine is written by hand (this stays; if it runs, do not touch it)).
* 写别的东西时也要留意:`ui_2d_path` 的坐标**不跟着任何布局走**。
* Keep this in mind when writing anything else: `ui_2d_path` coordinates **do not follow any layout**.
*/
#include <vml_ui.h>
#define C 0xFF3C6EB4
int W;
int H;
int cw;
int ch;
int tri[6];
/* ── 小工具:拼字符串(~~本前端不认 sprintf~~ —— **2026-10-04 复验已正常**) ──
* ── Small helper: string building (~~this frontend does not know sprintf~~ -- **re-checked fine 2026-10-04**) ──
*
* ⚠ 写成"往全局缓冲里追加"而不是"返回一个 char*":~~这条前端对**返回数组地址**没有把握~~,
* ⚠ Written as "append into a global buffer" rather than "return a char*": ~~this frontend was not reliable about **returning an array address**~~;
* ✅ **已修好(2026-10-04 复验)**:实测 `char* mk(void){ … return b; }`(b 是全局数组)返回地址正确(`S=XY`)。
* ✅ **fixed (re-checked 2026-10-04)**: `char* mk(void){ … return b; }` (b a global array) measured returning the correct address (`S=XY`).
* 而**把具名数组当实参传**是全文件到处在用的写法 —— 拼好之后直接把数组交给 `ui_2d_path` 就行(**照旧保留**,能跑就别动)。
* and **passing a named array as an argument** is the pattern used all over this file -- once built, just hand the array to `ui_2d_path` (this **stays**; if it runs, do not touch it).
*/
char pathBuf[96];
int pbN;
void pb_reset(void) { pbN = 0; }
void pb_put(char* s)
{
int i = 0;
while (s[i] != 0) { pathBuf[pbN] = s[i]; pbN = pbN + 1; i = i + 1; }
}
void pb_int(int v)
{
char t[12];
int j = 0;
if (v < 0) { pb_put("-"); v = -v; }
if (v == 0) { t[j] = '0'; j = j + 1; }
while (v > 0 && j < 11) { t[j] = (char)('0' + v % 10); j = j + 1; v = v / 10; }
while (j > 0) { j = j - 1; pathBuf[pbN] = t[j]; pbN = pbN + 1; }
}
/* 三角路径 "M x1 y1 L x2 y2 L x3 y3 Z"(格心 cx、底边 yb、半宽 hw、顶点高 tall)。 */
/* Triangle path "M x1 y1 L x2 y2 L x3 y3 Z" (center cx, base yb, half width hw, apex height tall). */
void pb_tri_path(int cx, int yb, int hw, int tall)
{
pb_reset();
pb_put("M "); pb_int(cx - hw); pb_put(" "); pb_int(yb);
pb_put(" L "); pb_int(cx); pb_put(" "); pb_int(yb - tall);
pb_put(" L "); pb_int(cx + hw); pb_put(" "); pb_int(yb);
pb_put(" Z");
pathBuf[pbN] = 0;
}
int cxx(int col) { return col * cw + cw / 2; }
int cyy(int row) { return row * ch + ch / 2; }
int main(void)
{
int m[4];
int lang = ui_scr_get_language(); /* 界面语言:开局查一次(ui_scr_get_language 是 syscall,别每帧调) */
/* UI language: queried once at start (ui_scr_get_language is a syscall, do not call it every frame) */
ui_win_open_ex(lang == 0 ? "颜色体检" : "Color self-check", 0, 0, VML_WIN_PORTRAIT, VML_WIN_NO_GAMEPAD);
W = ui_scr_get_w();
H = ui_scr_get_h();
cw = W / 3;
ch = H / 5;
ui_2d_clear(0xFF000000);
/* 0 实心矩形 */
/* 0 solid rectangle */
ui_2d_rect(cxx(0) - cw / 3, cyy(0) - ch / 3, cw * 2 / 3, ch * 2 / 3, C, 1, 0, 0);
/* 1 圆角矩形 */
/* 1 rounded rectangle */
ui_2d_rect(cxx(1) - cw / 3, cyy(0) - ch / 3, cw * 2 / 3, ch * 2 / 3, C, 1, 0, 14);
/* 2 实心圆 */
/* 2 solid circle */
ui_2d_circle(cxx(2), cyy(0), ch / 3, C, 1, 0);
/* 3 实心椭圆 */
/* 3 solid ellipse */
ui_2d_ellipse(cxx(0), cyy(1), cw / 3, ch / 4, C, 1, 0);
/* 4 实心多边形(三角,fill=C / 无描边) */
/* 4 solid polygon (triangle, fill=C / no stroke) */
tri[0] = cxx(1); tri[1] = cyy(1) - ch / 3;
tri[2] = cxx(1) - cw / 3; tri[3] = cyy(1) + ch / 3;
tri[4] = cxx(1) + cw / 3; tri[5] = cyy(1) + ch / 3;
ui_2d_polygon(tri, 3, C, 0, 0, 0);
/* 5 粗折线(开口;顶点与上面那个三角相同,所以采样取**边上**而不是格心) */
/* 5 thick polyline (open; same vertices as the triangle above, so sample **on the edge**, not the center) */
ui_2d_polyline(tri, 3, C, 18, 0);
/* 6 粗直线(横穿格子) */
/* 6 thick line (across the cell) */
ui_2d_line(cxx(0) - cw / 3, cyy(2), cxx(0) + cw / 3, cyy(2), C, 24);
/* 7 路径填充(只有填充,不描边)
7 path fill (fill only, no stroke)
⚠ `ui_2d_path` 的参数序是 **stroke, width, fill, grad, cap, dash** ——
⚠ the parameter order of `ui_2d_path` is **stroke, width, fill, grad, cap, dash** —
"描边色"排在"填充色"**前面**,与 `ui_2d_rect` 那种"颜色在前、开关在后"不一样。
the "stroke color" comes **before** the "fill color", unlike `ui_2d_rect` where "color first, switches after".
写反了不会报错,只是把"描边"当成了"填充":本格想要空心轮廓,
Swapping them raises no error, it just treats "stroke" as "fill": this cell wants a hollow outline,
结果画出一个实心三角(第一次就是这么写错的)。
but ends up drawing a solid triangle (which is exactly how it was first written wrong). */
pb_tri_path(cxx(1), cyy(2) + ch / 3, cw / 3, ch * 2 / 3);
ui_2d_path(pathBuf, 0, 0, C, 0, 0, 0);
/* 8 路径描边(粗,只有描边不填充 —— 必须是**空心轮廓**) */
/* 8 path stroke (thick, stroke only with no fill — must be a **hollow outline**) */
pb_tri_path(cxx(2), cyy(2) + ch / 3, cw / 3, ch * 2 / 3);
ui_2d_path(pathBuf, C, 20, 0, 0, 0, 0);
/* 9 空心矩形(粗描边、填充透明) */
/* 9 hollow rect (thick stroke, transparent fill) */
ui_2d_rect(cxx(0) - cw / 3, cyy(3) - ch / 3, cw * 2 / 3, ch * 2 / 3, C, 0, 20, 0);
/* 10 线性渐变(**对照格**:本来就该是渐变,不出现在断言里) */
/* 10 linear gradient (**control cell**: meant to be a gradient, not part of the assertions) */
ui_2d_gradient("g1", 0, 0xFFFF0000, 0xFF0000FF, 0, 0, 1000, 0);
ui_2d_rect_grad(cxx(1) - cw / 3, cyy(3) - ch / 3, cw * 2 / 3, ch * 2 / 3, "g1", 0);
/* 11 **回归格**:用过渐变之后再画纯色 —— 必须还是 C */
/* 11 **regression cell**: draw a solid color after a gradient was used — must still be C */
ui_2d_rect(cxx(2) - cw / 3, cyy(3) - ch / 3, cw * 2 / 3, ch * 2 / 3, C, 1, 0, 0);
/* 12 **回归格**:径向渐变之后再画纯色圆 */
/* 12 **regression cell**: draw a solid circle after a radial gradient */
ui_2d_gradient("g2", 1, 0xFF00FF00, 0xFF000000, 500, 500, 500, 0);
ui_2d_circle_grad(cxx(0), cyy(4), ch / 3, "g2");
ui_2d_circle(cxx(0), cyy(4) - ch / 5, ch / 5, C, 1, 0);
/* 13 **回归格**:再往后一格 */
/* 13 **regression cell**: one more cell later */
ui_2d_rect(cxx(1) - cw / 3, cyy(4) - ch / 3, cw * 2 / 3, ch * 2 / 3, C, 1, 0, 0);
/* 14 收尾纯色圆 */
/* 14 closing solid circle */
ui_2d_circle(cxx(2), cyy(4), ch / 3, C, 1, 0);
/* 15 文字(大号粗体;采样点落在笔画上) */
/* 15 text (large bold; the sample point lands on a stroke) */
ui_text_set_font(ch / 2, 1, C, 1);
ui_2d_text_cur(cxx(2), cyy(4) - ch / 6, "H");
ui_2d_present();
while (ui_win_is_closed() == 0) {
ui_msg_wait(m, 500);
}
return 0;
}