c/bgi/test_bgi.c · 20127 字节 · 下载原文
/* test_bgi.c —— **BGI 兼容层的全接口体检**(一屏"带标签的格子" + 一份文本小结)。
* test_bgi.c — **a full API check of the BGI compatibility layer** (one screen of labeled cells plus a short text summary).
*
* 做法照 `draw_prims.c` 的先例:每个接口一个小格子,格子里画出它的效果,
* The approach follows the precedent of `draw_prims.c`: one small cell per API, the cell shows its effect,
* 格子底下用 `outtextxy` 写上它的名字 —— 这样**文字接口本身也被覆盖**,
* and the API name is written under the cell with `outtextxy` — so **the text APIs get covered too**,
* 人对着屏幕就能逐条打勾。
* and a human can tick them off one by one against the screen.
*
* ⚠ 与 `test_bgi.cpp` **逐字相同**(只有扩展名不同)。两份一起编、一起看,
* ⚠ **Byte-for-byte identical** to `test_bgi.cpp` (only the extension differs). The two are built and read together
* 是因为 C 与 C++ 是**两个独立的前端**,同一份源码在两边的行为**并不保证一致** ——
* because C and C++ are **two independent front ends**, and the same source is **not guaranteed** to behave the same on both —
* 实测就有过只在一侧错的东西(`char s[] = "…"` 那条路:C++ 侧曾把字符数组
* we have measured bugs that appeared on one side only (the `char s[] = "…"` path: the C++ side once laid a char array out as
* 落成"带长度头的 4 字节格数组",于是 `outtextxy` 读到的是长度头,
* "a 4-byte cell array carrying a length header", so `outtextxy` read the length header
* 屏幕上只剩**一个乱字符**)。同一份源码两个前端各编一遍,是这类差异的唯一判据。
* and only **one garbled character** appeared on screen). Compiling one source with both front ends is the only test for differences like this.
*
* ## 怎么读结果
* ## How to read the results
*
* · **画面**:20 个格子,每个格子里是该接口的效果、格子底是它的名字。
* · **Screen**: 20 cells; each cell shows that API's effect and its name sits at the bottom of the cell.
* 哪一格画得不对,一眼就能定位到接口名。
* If a cell looks wrong, the API name is immediately obvious.
* · **stdout**:能自判的接口把结论打成 ` ok 名字` / ` FAIL 名字`,
* · **stdout**: APIs that can judge themselves print ` ok name` / ` FAIL name`,
* 最后一行是 `OK=n FAIL=m`。
* and the last line is `OK=n FAIL=m`.
* · 输出~~只用 `puts`/`putchar`,不用 `printf` —— 本仓记过它在桌面脚手架里会崩(`MOVEB @2, R0`,地址是垃圾值)~~。
* · Output ~~only `puts`/`putchar`, never `printf` — recorded to crash in the desktop scaffolding (`MOVEB @2, R0`, a garbage address)~~.
* ✅ **已修好(2026-10-04 复验)**:`printf("%s %d %f %x %c", …)` 实测正确打印;本文件**照旧**只用 `puts`/`putchar`(能跑就别动)。
* ✅ **fixed (re-checked 2026-10-04)**: `printf("%s %d %f %x %c", …)` measured printing correctly; this file **still** uses only `puts`/`putchar` (if it runs, do not touch it).
*
* ## 分辨率(`setgraphmode`)为什么不在这屏上画
* ## Why resolution (`setgraphmode`) is not drawn on this screen
*
* `setgraphmode` 的语义是"清屏 + 换模式",而本平台上电脑屏窗口的坐标系**开窗时定死**
* `setgraphmode` means "clear the screen and switch mode", and on this platform the computer-screen window's coordinate system is **fixed when the window opens**
* ⇒ 它只能**关掉重开**。所以多分辨率只在**开场**验一次(切过去、读 `getmaxx/getmaxy`、
* => it can only **close and reopen**. So multiple resolutions are checked once **at the start** (switch over, read `getmaxx/getmaxy`,
* 再切回来),画面本身仍然按 VGA 640×480 排 —— 挤在一屏里反而看不清。
* then switch back), while the picture itself is still laid out for VGA 640x480 — squeezed onto one screen it would be harder to see.
*/
#include <vml_ui.h>
#include <graphics.h>
#include <stdlib.h>
/* ── 网格几何 ──────────────────────────────────────────────── */
/* -- Grid geometry -- */
#define COLS 5
#define ROWS 4
#define CELL_W 124
#define CELL_H 108
#define GRID_X 8
#define GRID_Y 30
#define IN_W 112 /* 格子内可画区(留给标签一条) */
/* Drawable area inside a cell (one strip is left for the label). */
#define IN_H 78
/* ── 自判统计 ──────────────────────────────────────────────── */
/* -- Self-check counters -- */
static int g_ok = 0;
static int g_bad = 0;
/* ── 文本输出小工具(绕开 printf)───────────────────────────── */
/* -- Small text output helpers (bypassing printf) -- */
static void put_int(int v)
{
char buf[12];
int n = 0;
if (v < 0) { putchar('-'); v = -v; }
if (v == 0) { putchar('0'); return; }
while (v > 0) { buf[n] = (char)('0' + v % 10); v = v / 10; n = n + 1; }
while (n > 0) { n = n - 1; putchar(buf[n]); }
}
static void say(char *name, int v)
{
puts(name);
put_int(v);
putchar('\n');
}
static void check(char *name, int ok)
{
if (ok) { puts(" ok "); g_ok = g_ok + 1; }
else { puts(" FAIL "); g_bad = g_bad + 1; }
puts(name);
putchar('\n');
}
/* ── 格子定位(不用指针出参 —— 少一层"读不读得到"的疑点)───── */
/* -- Cell positioning (no pointer out-params — one less "can we read it" doubt) -- */
static int cell_x(int idx) { return GRID_X + (idx % COLS) * CELL_W; }
static int cell_y(int idx) { return GRID_Y + (idx / COLS) * CELL_H; }
static int in_x(int idx) { return cell_x(idx) + 6; }
static int in_y(int idx) { return cell_y(idx) + 6; }
/* 格子的名字写在格底。**这一步就是 `outtextxy` 的判据**:
* The cell name is written at the bottom of the cell. **This step is itself the test for `outtextxy`**:
* 名字画不出来的格子,是文字接口的问题,不是那一格图形的问题。
* a cell whose name cannot be drawn points at the text API, not at that cell's graphics.
*/
static void cell_label(int idx, char *name)
{
setcolor(WHITE);
settextstyle(DEFAULT_FONT, HORIZ_DIR, 1);
settextjustify(LEFT_TEXT, 0);
outtextxy(in_x(idx), in_y(idx) + IN_H + 6, name);
}
/* ── 多边形的顶点(必须是**具名数组**:`(int[]){…}` 复合字面量本前端不支持 —— 2026-10-04 复验:现在会**直接编译报错**,不再静默)── */
/* -- Polygon vertices (must be a **named array**: this front end does not support `(int[]){…}` compound literals -- re-checked 2026-10-04: now a **compile error**, no longer silent) -- */
static int tri[8];
static int quad[10];
int main(void)
{
int gd = DETECT;
int gm = 0;
int i;
int cx, cy, w, h;
void *img;
int area;
int t1, t2, t3;
int px, py;
int lang; /* 界面语言:开局查一次(ui_scr_get_language 是 syscall,别每帧调) */
/* UI language: queried once at start (ui_scr_get_language is a syscall, do not call it every frame) */
initgraph(&gd, &gm, "");
lang = ui_scr_get_language();
puts(lang == 0 ? "=== BGI 自检开始 ===" : "=== BGI selftest begin ===");
/* ── 1. 模式与坐标系(DETECT ⇒ 驱动/模式由库选,本平台是 VGA 640×480)── */
/* -- 1. Mode and coordinate system (DETECT => driver/mode picked by the library; VGA 640x480 here) -- */
say("gd(driver)=", gd);
say("gm(mode)=", gm);
check(lang == 0 ? "initgraph(DETECT) 选出 VGA 驱动" : "initgraph(DETECT) picked the VGA driver", gd == VGA);
check(lang == 0 ? "initgraph(DETECT) 选出 VGAHI 模式" : "initgraph(DETECT) picked the VGAHI mode", gm == VGAHI);
say("getmaxx()=", getmaxx());
say("getmaxy()=", getmaxy());
check(lang == 0 ? "VGAHI 是 640×480(maxx=639)" : "VGAHI is 640x480 (maxx=639)", getmaxx() == 639);
check(lang == 0 ? "VGAHI 是 640×480(maxy=479)" : "VGAHI is 640x480 (maxy=479)", getmaxy() == 479);
/* ── 2. setgraphmode:换模式 = 换分辨率(只能关窗重开,见文件头)── */
/* -- 2. setgraphmode: changing the mode means changing the resolution (it can only close and reopen; see the file header) -- */
setgraphmode(VGALO); /* 640×200 */
say("VGALO maxy=", getmaxy());
check(lang == 0 ? "setgraphmode(VGALO) 后 maxy=199" : "setgraphmode(VGALO) leaves maxy=199", getmaxy() == 199);
setgraphmode(VGAHI); /* 回到 640×480 */
/* Back to 640x480. */
say("VGAHI maxy=", getmaxy());
check(lang == 0 ? "setgraphmode(VGAHI) 切回来 maxy=479" : "setgraphmode(VGAHI) back to maxy=479", getmaxy() == 479);
/* ── 3. cleardevice:整屏刷成背景色 ── */
/* -- 3. cleardevice: paint the whole screen with the background color -- */
setbkcolor(BLACK);
cleardevice();
check(lang == 0 ? "cleardevice 后左上角是背景色 BLACK" : "after cleardevice the top-left is the BLACK background", getpixel(0, 0) == BLACK);
/* ── 4. 文字量测(`textwidth`/`textheight`):老程序靠它居中排版 ──
* -- 4. Text measurement (`textwidth`/`textheight`): old programs center their layout with it --
* ⚠ 这两个走的是宿主注册的 `text` JSON 函数(见 `Lib/c/graphics.h` 的
* ⚠ Both go through the host-registered `text` JSON function (see the "text measurement" section of `Lib/c/graphics.h`) —
* "文字量测"一节)—— 与**渲染同源**才算得对,所以这里量出来的数
* only if they share the source with **rendering** are they correct, so the numbers measured here
* 与画面上的实际宽度是一回事。
* are the same thing as the actual width on screen.
*/
settextstyle(DEFAULT_FONT, HORIZ_DIR, 1);
t1 = textwidth("ABC");
t2 = textheight("ABC");
settextstyle(DEFAULT_FONT, HORIZ_DIR, 3);
t3 = textwidth("ABC");
say("textwidth(ABC,size1)=", t1);
say("textheight(ABC,size1)=", t2);
say("textwidth(ABC,size3)=", t3);
check("textwidth > 0", t1 > 0);
check("textheight > 0", t2 > 0);
check(lang == 0 ? "字号变大 ⇒ textwidth 变大" : "a larger font size gives a larger textwidth", t3 > t1);
settextstyle(DEFAULT_FONT, HORIZ_DIR, 1);
/* ── 5. 按格画图 ──────────────────────────────────────────── */
/* -- 5. Draw cell by cell -- */
/* ① line */
cx = in_x(0); cy = in_y(0);
setcolor(LIGHTGREEN);
line(cx, cy, cx + IN_W - 2, cy + IN_H - 2);
line(cx + IN_W - 2, cy, cx, cy + IN_H - 2);
cell_label(0, "line");
/* ② moveto / lineto / linerel */
cx = in_x(1); cy = in_y(1);
setcolor(CYAN);
moveto(cx, cy);
linerel(IN_W - 2, IN_H - 2);
moveto(cx + IN_W - 2, cy);
linerel(-(IN_W - 2), IN_H - 2);
moveto(cx, cy + IN_H / 2);
lineto(cx + IN_W - 2, cy + IN_H / 2);
cell_label(1, "moveto/lineto");
/* ③ rectangle */
cx = in_x(2); cy = in_y(2);
setcolor(YELLOW);
rectangle(cx, cy, cx + IN_W - 2, cy + IN_H - 2);
rectangle(cx + 20, cy + 16, cx + IN_W - 22, cy + IN_H - 18);
cell_label(2, "rectangle");
/* ④ bar */
cx = in_x(3); cy = in_y(3);
setfillstyle(SOLID_FILL, LIGHTBLUE);
bar(cx, cy, cx + IN_W - 2, cy + IN_H - 2);
setfillstyle(SOLID_FILL, LIGHTRED);
bar(cx + 30, cy + 24, cx + IN_W - 32, cy + IN_H - 2);
cell_label(3, "bar");
/* ⑤ bar3d */
cx = in_x(4); cy = in_y(4);
setcolor(WHITE);
setfillstyle(SOLID_FILL, GREEN);
bar3d(cx, cy + 16, cx + IN_W - 30, cy + IN_H - 18, 16, 1);
cell_label(4, "bar3d");
/* ⑥ circle */
cx = in_x(5); cy = in_y(5);
setcolor(LIGHTMAGENTA);
circle(cx + IN_W / 2, cy + IN_H / 2, 30);
circle(cx + IN_W / 2, cy + IN_H / 2, 18);
cell_label(5, "circle");
/* ⑦ fillellipse */
cx = in_x(6); cy = in_y(6);
setfillstyle(SOLID_FILL, BROWN);
fillellipse(cx + IN_W / 2, cy + IN_H / 2, 44, 28);
cell_label(6, "fillellipse");
/* ⑧ ellipse */
cx = in_x(7); cy = in_y(7);
setcolor(LIGHTGRAY);
ellipse(cx + IN_W / 2, cy + IN_H / 2, 0, 360, 46, 24);
ellipse(cx + IN_W / 2, cy + IN_H / 2, 0, 360, 24, 34);
cell_label(7, "ellipse");
/* ⑨ arc(只描边、不填充)
* ⑨ arc (outline only, no fill)
*/
cx = in_x(8); cy = in_y(8);
setcolor(LIGHTRED);
arc(cx + IN_W / 2, cy + IN_H / 2, 30, 150, 32);
cell_label(8, "arc");
/* ⑩ pieslice */
cx = in_x(9); cy = in_y(9);
setcolor(WHITE);
setfillstyle(SOLID_FILL, YELLOW);
pieslice(cx + IN_W / 2, cy + IN_H / 2, 0, 120, 32);
cell_label(9, "pieslice");
/* ⑪ sector(BGI 里可给两个半径;本平台按 `xr` 画**圆**扇形)
* ⑪ sector (BGI allows two radii; this platform draws a **circular** sector from `xr`)
*/
cx = in_x(10); cy = in_y(10);
setcolor(WHITE);
setfillstyle(SOLID_FILL, LIGHTCYAN);
sector(cx + IN_W / 2, cy + IN_H / 2, 200, 320, 34, 20);
cell_label(10, "sector");
/* ⑫ drawpoly(BGI 的顶点表**首点要重复一次**才闭合)
* ⑫ drawpoly (BGI's vertex list must **repeat the first point once** to close the shape)
*/
cx = in_x(11); cy = in_y(11);
tri[0] = cx + 20; tri[1] = cy + IN_H - 4;
tri[2] = cx + IN_W / 2; tri[3] = cy + 4;
tri[4] = cx + IN_W - 20; tri[5] = cy + IN_H - 4;
tri[6] = tri[0]; tri[7] = tri[1];
setcolor(LIGHTGREEN);
drawpoly(4, tri);
cell_label(11, "drawpoly");
/* ⑬ fillpoly */
cx = in_x(12); cy = in_y(12);
quad[0] = cx + 16; quad[1] = cy + 10;
quad[2] = cx + IN_W - 16; quad[3] = cy + 24;
quad[4] = cx + IN_W - 30; quad[5] = cy + IN_H - 6;
quad[6] = cx + 6; quad[7] = cy + IN_H - 20;
quad[8] = quad[0]; quad[9] = quad[1];
setcolor(WHITE);
setfillstyle(SOLID_FILL, LIGHTMAGENTA);
fillpoly(5, quad);
cell_label(12, "fillpoly");
/* ⑭ putpixel / getpixel */
cx = in_x(13); cy = in_y(13);
for (py = 0; py < 6; py = py + 1)
for (px = 0; px < 16; px = px + 1)
putpixel(cx + 6 + px * 6, cy + 8 + py * 10,
(px + py) % 2 == 0 ? WHITE : BLUE);
check(lang == 0 ? "putpixel 后 getpixel 读回同一索引"
: "getpixel reads back the same index after putpixel",
getpixel(cx + 6, cy + 8) == WHITE);
check(lang == 0 ? "没画过的地方是背景色"
: "an untouched spot is the background color",
getpixel(cx + IN_W - 2, cy + 2) == BLACK);
cell_label(13, "putpixel");
/* ⑮ floodfill(第三参是**边界色**,填什么由 setfillstyle 决定)
* ⑮ floodfill (the third argument is the **border color**; what gets filled is decided by setfillstyle)
*/
cx = in_x(14); cy = in_y(14);
setcolor(WHITE);
rectangle(cx + 10, cy + 6, cx + IN_W - 12, cy + IN_H - 6);
setfillstyle(SOLID_FILL, GREEN);
floodfill(cx + IN_W / 2, cy + IN_H / 2, WHITE);
check(lang == 0 ? "floodfill 填进了 setfillstyle 的颜色"
: "floodfill filled with the setfillstyle color",
getpixel(cx + IN_W / 2, cy + IN_H / 2) == GREEN);
cell_label(14, "floodfill");
/* ⑯ imagesize / getimage / putimage */
cx = in_x(15); cy = in_y(15);
setfillstyle(SOLID_FILL, MAGENTA);
bar(cx + 8, cy + 12, cx + 40, cy + 44);
setcolor(WHITE);
rectangle(cx + 8, cy + 12, cx + 40, cy + 44);
area = imagesize(cx + 8, cy + 12, cx + 40, cy + 44);
say("imagesize(33x33)=", area);
check(lang == 0 ? "imagesize > 0(老程序拿它去 malloc)" : "imagesize > 0 (old programs malloc with it)", area > 0);
img = malloc(area);
getimage(cx + 8, cy + 12, cx + 40, cy + 44, img);
putimage(cx + 56, cy + 12, img, COPY_PUT);
putimage(cx + 30, cy + 52, img, XOR_PUT); /* 贴回自己 ⇒ XOR 抹掉 */
/* Blitting onto itself => XOR erases it. */
check(lang == 0 ? "getimage+putimage(COPY) 贴出了颜色"
: "getimage+putimage(COPY) blitted the color",
getpixel(cx + 70, cy + 28) == MAGENTA);
cell_label(15, "getimage/putimage");
/* ⑰ setcolor:16 色调色板各来一条 */
/* ⑰ setcolor: one line for each of the 16 palette colors. */
cx = in_x(16); cy = in_y(16);
for (i = 0; i < 16; i = i + 1) {
setcolor(i);
line(cx + i * 7, cy + 4, cx + i * 7, cy + IN_H - 4);
}
check(lang == 0 ? "setcolor(14) 后 getpixel 读到 14"
: "getpixel reads 14 after setcolor(14)",
(setcolor(14), getpixel(cx + 14 * 7, cy + 20)) == 14);
cell_label(16, "setcolor(0..15)");
/* ⑱ setfillstyle */
cx = in_x(17); cy = in_y(17);
setfillstyle(SOLID_FILL, BLUE);
bar(cx, cy + 6, cx + 24, cy + IN_H - 6);
setfillstyle(SOLID_FILL, RED);
bar(cx + 28, cy + 6, cx + 52, cy + IN_H - 6);
setfillstyle(SOLID_FILL, GREEN);
bar(cx + 56, cy + 6, cx + 80, cy + IN_H - 6);
setfillstyle(SOLID_FILL, MAGENTA);
bar(cx + 84, cy + 6, cx + IN_W - 2, cy + IN_H - 6);
check(lang == 0 ? "setfillstyle 换色 ⇒ bar 跟着换"
: "changing setfillstyle changes the bar",
getpixel(cx + 12, cy + 20) == BLUE && getpixel(cx + 96, cy + 20) == MAGENTA);
cell_label(17, "setfillstyle");
/* ⑲ setlinestyle */
cx = in_x(18); cy = in_y(18);
setcolor(WHITE);
setlinestyle(SOLID_LINE, 0, NORM_WIDTH);
line(cx, cy + 12, cx + IN_W - 2, cy + 12);
setlinestyle(SOLID_LINE, 0, THICK_WIDTH);
line(cx, cy + 34, cx + IN_W - 2, cy + 34);
setlinestyle(SOLID_LINE, 0, NORM_WIDTH);
line(cx, cy + 58, cx + IN_W - 2, cy + 58);
cell_label(18, "setlinestyle");
/* ⑳ 文字三件套:outtextxy / outtext(跟着画笔)/ settextjustify */
/* ⑳ The text trio: outtextxy / outtext (follows the current position) / settextjustify. */
cx = in_x(19); cy = in_y(19);
setcolor(WHITE);
settextstyle(DEFAULT_FONT, HORIZ_DIR, 2);
settextjustify(LEFT_TEXT, 0);
outtextxy(cx, cy + 4, "outtextxy");
moveto(cx, cy + 34);
outtext("outtext");
settextjustify(CENTER_TEXT, 0);
outtextxy(cx + IN_W / 2, cy + 60, "CENTER");
settextjustify(LEFT_TEXT, 0);
settextstyle(DEFAULT_FONT, HORIZ_DIR, 1);
cell_label(19, "outtextxy");
/* ── 收尾 ────────────────────────────────────────────────── */
/* -- Wrap-up -- */
puts(lang == 0 ? "--- 颜色索引抽查 ---" : "--- color index spot checks ---");
say("getpixel(0,0)=", getpixel(0, 0));
puts(lang == 0 ? "--- 小结 ---" : "--- summary ---");
puts("OK=");
put_int(g_ok);
puts(" FAIL=");
put_int(g_bad);
putchar('\n');
puts(lang == 0 ? "=== BGI 自检结束 ===" : "=== BGI selftest end ===");
ui_2d_present();
getch();
closegraph();
return 0;
}