libc: BGI Фаза 2b — заливки (setfillstyle/bar/bar3d/fillpoly/fillellipse)

setfillstyle/getfillsettings + 10 стандартных 8×8 паттернов Borland.
bar теперь честно учитывает стиль заливки; bar3d (3D-брусок), fillpoly
(scanline min/max по строкам через брезенхэмовский проход рёбер),
fillellipse (полуширина строки через целочисленный _bgi_isqrt — без
32-бит). Общий _bgi_fill_span (SOLID/EMPTY/паттерн) с клипом, поверх
raw-hline в одной W3-скобке. Проверено в MAME (tests/bgitest): solid/
hatch бары, bar3d со slash, синий fillellipse, xhatch-треугольник.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-08 15:58:06 +03:00
parent 5a48f7fafb
commit ecb419efda
17 changed files with 350 additions and 47 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ banked 1056
bankedbg 1067
banklocl 4832
banktest 3767
bgitest 5043
bgitest 6123
bios_text 4470
cat 915
cblstream 6277
1 # Эталон размеров _CODE (байт); обновление: python3 toolchain/size_check.py --update
6 bankedbg
7 banklocl
8 banktest
9 bgitest
10 bios_text
11 cat
12 cblstream
+22
View File
@@ -13,6 +13,7 @@
#include <stdint.h>
#include <graphics.h>
#include <gfx.h> /* GFX_WIDTH/HEIGHT для внутренних таблиц */
/* ---- Состояние (_bgi_state.c) ------------------------------------ */
extern uint8_t _bgi_fg; /* текущий цвет рисования */
@@ -53,6 +54,27 @@ int _bgi_cos128(int deg);
* по точкам от stangle до endangle с полуосями xr,yr вокруг (cx,cy). */
void _bgi_ellipse_arc(int cx, int cy, int stangle, int endangle,
int xr, int yr, uint8_t color);
/* Целочисленный sqrt (0..~32767) для fillellipse (_bgi_isqrt.c). */
int _bgi_isqrt(int n);
/* ---- Состояние заливки (_bgi_fill_state.c) ----------------------- */
extern uint8_t _bgi_fill_pattern; /* *_FILL */
extern uint8_t _bgi_fill_color;
/* Raw horizontal span (без своей W3-скобки; клип делает сам gfx-raw). */
void _bgi_hspan_raw(int x, int y, int len, uint8_t color);
/* Залить строку [x0..x1] на y ТЕКУЩИМ стилем заливки (паттерн+цвет),
* с клипом по экрану. Вызывать между _bgi_begin/_bgi_end. */
void _bgi_fill_span(int x0, int x1, int y);
/* Скан-таблицы min/max X по строкам для fillpoly (_bgi_poly_state.c). */
extern int _bgi_span_min[GFX_HEIGHT];
extern int _bgi_span_max[GFX_HEIGHT];
/* Прогнать ребро (Брезенхэм) обновляя _bgi_span_min/max (_bgi_poly_edge.c). */
void _bgi_poly_edge(int x0, int y0, int x1, int y1);
void _bgi_hspan(int x, int y, int len, uint8_t color);
void _bgi_lineseg(int x0, int y0, int x1, int y1, uint8_t color);
void _bgi_fillrect(int x, int y, int w, int h, uint8_t color);
+47
View File
@@ -0,0 +1,47 @@
/*
* _bgi_fill_span — залить строку [x0..x1] на y текущим стилем заливки.
*
* SOLID → быстрый raw-hline; EMPTY → hline фоновым цветом; паттерн →
* поточечно по 8×8-битовой маске (строка паттерна = pat[y&7], бит x&7).
* Клип по экрану внутри. ВЫЗЫВАТЬ между _bgi_begin/_bgi_end.
*/
#include "_bgi.h"
/* Стандартные 8×8-паттерны Borland, индексы LINE_FILL..CLOSE_DOT_FILL. */
static const uint8_t _bgi_pat[10][8] = {
{ 0xFF,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00 }, /* LINE_FILL */
{ 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 }, /* LTSLASH_FILL */
{ 0xE0,0xC1,0x83,0x07,0x0E,0x1C,0x38,0x70 }, /* SLASH_FILL */
{ 0xF0,0x78,0x3C,0x1E,0x0F,0x87,0xC3,0xE1 }, /* BKSLASH_FILL */
{ 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01 }, /* LTBKSLASH_FILL */
{ 0xFF,0x88,0x88,0x88,0xFF,0x88,0x88,0x88 }, /* HATCH_FILL */
{ 0x81,0x42,0x24,0x18,0x18,0x24,0x42,0x81 }, /* XHATCH_FILL */
{ 0xCC,0x33,0xCC,0x33,0xCC,0x33,0xCC,0x33 }, /* INTERLEAVE_FILL */
{ 0x80,0x00,0x08,0x00,0x80,0x00,0x08,0x00 }, /* WIDE_DOT_FILL */
{ 0x88,0x00,0x22,0x00,0x88,0x00,0x22,0x00 }, /* CLOSE_DOT_FILL */
};
void _bgi_fill_span(int x0, int x1, int y)
{
int x;
uint8_t p, row;
if (x1 < x0) { x = x0; x0 = x1; x1 = x; }
if (y < 0 || y > _bgi_maxy) return;
if (x0 < 0) x0 = 0;
if (x1 > _bgi_maxx) x1 = _bgi_maxx;
if (x1 < x0) return;
p = _bgi_fill_pattern;
if (p == EMPTY_FILL) { _bgi_hspan_raw(x0, y, x1 - x0 + 1, _bgi_bg); return; }
if (p == SOLID_FILL || p == USER_FILL || p > CLOSE_DOT_FILL) {
_bgi_hspan_raw(x0, y, x1 - x0 + 1, _bgi_fill_color);
return;
}
/* Паттерн LINE_FILL..CLOSE_DOT_FILL. */
row = _bgi_pat[p - LINE_FILL][y & 7];
for (x = x0; x <= x1; x++)
if ((row >> (x & 7)) & 1)
_bgi_plot_raw(x, y, _bgi_fill_color);
}
+10
View File
@@ -0,0 +1,10 @@
/*
* _bgi_fill_state — текущий стиль заливки BGI (паттерн + цвет).
* Значения по умолчанию выставляет initgraph (SOLID_FILL/WHITE).
* Без инициализации (crt0 зануляет _DATA).
*/
#include "_bgi.h"
uint8_t _bgi_fill_pattern;
uint8_t _bgi_fill_color;
+15
View File
@@ -0,0 +1,15 @@
/*
* _bgi_isqrt — целочисленный floor(sqrt(n)) для n в 0..~32767.
* Линейный поиск (n≤180² → ≤180 шагов) — для fillellipse достаточно,
* без float и 32-бит.
*/
#include "_bgi.h"
int _bgi_isqrt(int n)
{
int x = 0;
if (n < 0) return 0;
while ((x + 1) * (x + 1) <= n) x++;
return x;
}
+30
View File
@@ -0,0 +1,30 @@
/*
* _bgi_poly_edge — прогнать ребро (x0,y0)->(x1,y1) по Брезенхэму,
* обновляя _bgi_span_min/_bgi_span_max для каждой затронутой строки.
* Только целочисленные сложения (без 32-бит). Строки вне экрана
* пропускаются. Заливка потом идёт от min до max по строке.
*/
#include "_bgi.h"
void _bgi_poly_edge(int x0, int y0, int x1, int y1)
{
int dx, dy, sx, sy, err, e2;
dx = x1 - x0; if (dx < 0) dx = -dx;
dy = y1 - y0; if (dy < 0) dy = -dy;
sx = (x0 < x1) ? 1 : -1;
sy = (y0 < y1) ? 1 : -1;
err = dx - dy;
for (;;) {
if (y0 >= 0 && y0 < GFX_HEIGHT) {
if (x0 < _bgi_span_min[y0]) _bgi_span_min[y0] = x0;
if (x0 > _bgi_span_max[y0]) _bgi_span_max[y0] = x0;
}
if (x0 == x1 && y0 == y1) break;
e2 = 2 * err;
if (e2 > -dy) { err -= dy; x0 += sx; }
if (e2 < dx) { err += dx; y0 += sy; }
}
}
+11
View File
@@ -0,0 +1,11 @@
/*
* _bgi_poly_state — скан-таблицы min/max X по строкам для fillpoly.
* По строке экрана хранит крайние левый/правый X контура — заливка
* идёт от min до max (выпуклая оболочка строки). ~1 КБ в _DATA,
* однопоточно (рисование не реентерабельно).
*/
#include "_bgi.h"
int _bgi_span_min[GFX_HEIGHT];
int _bgi_span_max[GFX_HEIGHT];
+11 -8
View File
@@ -1,15 +1,18 @@
/*
* bar — залитый прямоугольник (left,top)-(right,bottom) включительно.
*
* В Turbo-C цвет/паттерн заливки задаётся setfillstyle; пока (Фаза 1)
* заливаем текущим цветом рисования сплошняком.
* bar — залитый прямоугольник (left,top)-(right,bottom) включительно
* ТЕКУЩИМ стилем заливки (setfillstyle), без рамки — как в Turbo-C.
*/
#include "_bgi.h"
void bar(int left, int top, int right, int bottom)
{
int w = right - left + 1;
int h = bottom - top + 1;
if (w <= 0 || h <= 0) return;
_bgi_fillrect(left, top, w, h, _bgi_fg);
int y, t;
if (right < left) { t = left; left = right; right = t; }
if (bottom < top) { t = top; top = bottom; bottom = t; }
_bgi_begin();
for (y = top; y <= bottom; y++)
_bgi_fill_span(left, right, y);
_bgi_end();
}
+27
View File
@@ -0,0 +1,27 @@
/*
* bar3d — трёхмерный брусок: передняя грань залита текущим стилем
* заливки, рёбра — текущим цветом рисования. depth — глубина (вправо-
* вверх), topflag != 0 рисует верхнюю грань.
*/
#include "_bgi.h"
void bar3d(int left, int top, int right, int bottom, int depth, int topflag)
{
int t;
if (right < left) { t = left; left = right; right = t; }
if (bottom < top) { t = top; top = bottom; bottom = t; }
bar(left, top, right, bottom); /* передняя грань — заливка */
rectangle(left, top, right, bottom); /* рамка переда — тек. цвет */
if (depth != 0) {
line(right, top, right + depth, top - depth);
line(right, bottom, right + depth, bottom - depth);
line(right + depth, top - depth, right + depth, bottom - depth);
if (topflag) {
line(left, top, left + depth, top - depth);
line(left + depth, top - depth, right + depth, top - depth);
}
}
}
+11
View File
@@ -0,0 +1,11 @@
/*
* _bgi_hspan_raw (drv256) — горизонтальный отрезок без своей W3-скобки
* (вызывать между _bgi_begin/_bgi_end). Клип делает сам raw-примитив.
*/
#include "../_bgi.h"
#include "../../gfx/_gfx.h"
void _bgi_hspan_raw(int x, int y, int len, uint8_t color)
{
_gfx_hline256_raw(x, y, len, color);
}
+31
View File
@@ -0,0 +1,31 @@
/*
* fillellipse — залитый эллипс с полуосями rx,ry, центр (cx,cy).
* Нутро — текущим стилем заливки, контур — текущим цветом.
*
* По строкам: полуширина w = rx*sqrt(ry²-dy²)/ry (целочисленно через
* _bgi_isqrt). ry²,dy² ≤ ~180² < 32767, rx*s ≤ rx*ry < 32767 — без
* 32-бит (см. memory/avoid_32bit_arith_z80).
*/
#include "_bgi.h"
void fillellipse(int cx, int cy, int rx, int ry)
{
int dy, s, w;
if (rx < 0 || ry < 0) return;
_bgi_begin();
if (ry == 0) {
_bgi_fill_span(cx - rx, cx + rx, cy);
} else {
for (dy = -ry; dy <= ry; dy++) {
s = _bgi_isqrt(ry * ry - dy * dy); /* 0..ry */
w = (rx * s) / ry;
_bgi_fill_span(cx - w, cx + w, cy + dy);
}
}
_bgi_end();
/* Контур текущим цветом. */
ellipse(cx, cy, 0, 360, rx, ry);
}
+54
View File
@@ -0,0 +1,54 @@
/*
* fillpoly — залитый многоугольник по numpoints точкам {x0,y0,…}.
* Нутро — текущим стилем заливки, контур — текущим цветом. Полигон
* авто-замыкается (последняя точка соединяется с первой).
*
* Заливка: для каждой строки берём крайние X контура (min/max) и
* заполняем между ними — точно для выпуклых, для вогнутых = выпуклая
* оболочка по строкам (упрощение). Крайние X даёт брезенхэмовский
* проход рёбер (_bgi_poly_edge) — без 32-бит арифметики.
*/
#include "_bgi.h"
void fillpoly(int numpoints, const int *polypoints)
{
int i, j, y, ymin, ymax, xa, ya, xb, yb;
if (numpoints < 2) return;
ymin = 32767;
ymax = -32768;
for (i = 0; i < numpoints; i++) {
y = polypoints[2 * i + 1];
if (y < ymin) ymin = y;
if (y > ymax) ymax = y;
}
if (ymin < 0) ymin = 0;
if (ymax > GFX_HEIGHT - 1) ymax = GFX_HEIGHT - 1;
if (ymax < ymin) return;
for (y = ymin; y <= ymax; y++) {
_bgi_span_min[y] = 32767;
_bgi_span_max[y] = -32768;
}
for (i = 0; i < numpoints; i++) {
j = (i + 1) % numpoints;
_bgi_poly_edge(polypoints[2 * i], polypoints[2 * i + 1],
polypoints[2 * j], polypoints[2 * j + 1]);
}
_bgi_begin();
for (y = ymin; y <= ymax; y++)
if (_bgi_span_max[y] >= _bgi_span_min[y])
_bgi_fill_span(_bgi_span_min[y], _bgi_span_max[y], y);
_bgi_end();
/* Контур текущим цветом (замкнутый). */
for (i = 0; i < numpoints; i++) {
j = (i + 1) % numpoints;
xa = polypoints[2 * i]; ya = polypoints[2 * i + 1];
xb = polypoints[2 * j]; yb = polypoints[2 * j + 1];
_bgi_lineseg(xa, ya, xb, yb, _bgi_fg);
}
}
+9
View File
@@ -0,0 +1,9 @@
/* getfillsettings — прочитать текущий паттерн и цвет заливки. */
#include "_bgi.h"
void getfillsettings(struct fillsettingstype *fillinfo)
{
if (!fillinfo) return;
fillinfo->pattern = _bgi_fill_pattern;
fillinfo->color = _bgi_fill_color;
}
+2
View File
@@ -15,4 +15,6 @@ void initgraph(void)
_bgi_cx = 0;
_bgi_cy = 0;
_bgi_result = grOk;
_bgi_fill_pattern = SOLID_FILL;
_bgi_fill_color = WHITE;
}
+8
View File
@@ -0,0 +1,8 @@
/* setfillstyle — задать паттерн и цвет заливки (bar/fillpoly/…). */
#include "_bgi.h"
void setfillstyle(int pattern, int color)
{
_bgi_fill_pattern = (uint8_t)pattern;
_bgi_fill_color = (uint8_t)color;
}
+28
View File
@@ -93,6 +93,34 @@ void ellipse(int x, int y, int stangle, int endangle,
/* Ломаная по numpoints точкам {x0,y0,x1,y1,…}; НЕ замыкается сама. */
void drawpoly(int numpoints, const int *polypoints);
/* ---- Заливки ----------------------------------------------------- *
* Стиль заливки — паттерн (8×8) + цвет; действует на bar/bar3d/
* fillpoly/fillellipse. SOLID_FILL заполняет сплошняком, EMPTY_FILL —
* фоновым цветом. USER_FILL пока трактуется как SOLID. */
enum {
EMPTY_FILL = 0, SOLID_FILL, LINE_FILL, LTSLASH_FILL, SLASH_FILL,
BKSLASH_FILL, LTBKSLASH_FILL, HATCH_FILL, XHATCH_FILL,
INTERLEAVE_FILL, WIDE_DOT_FILL, CLOSE_DOT_FILL, USER_FILL
};
struct fillsettingstype { int pattern; int color; };
void setfillstyle(int pattern, int color);
void getfillsettings(struct fillsettingstype *fillinfo);
/* bar — залитый прямоугольник (текущий стиль заливки, без рамки). */
/* bar3d — 3D-брусок: перёд залит стилем заливки, рёбра — тек. цветом;
* topflag != 0 рисует верхнюю грань. */
void bar3d(int left, int top, int right, int bottom, int depth, int topflag);
/* fillpoly — залитый многоугольник (авто-замыкается); контур тек.
* цветом, нутро — стилем заливки. Вогнутые заполняются по выпуклой
* оболочке строк (min/max X на строку) — упрощение. */
void fillpoly(int numpoints, const int *polypoints);
/* fillellipse — залитый эллипс (полуоси xradius,yradius). */
void fillellipse(int x, int y, int xradius, int yradius);
/* ---- Текст ------------------------------------------------------- *
* Шрифт 8×8 (системный). Рисуется текущим цветом на фоновом. */
+33 -38
View File
@@ -1,23 +1,20 @@
/*
* bgitest — демонстрация BGI-слоя graphics.h (режим 320×256×256).
*
* Собирается с `--gfx 256` (см. Makefile). Показывает примитивы
* Фазы 1 (рамка/бары/линии/окружность/текст) и Фазы 2a
* (эллипс/дуга/полигон) — визуальная проверка в MAME.
* Собирается с `--gfx 256`. Фаза 2b: заливки — bar (сплошная/паттерн),
* bar3d, fillpoly, fillellipse + setfillstyle. Визуальная проверка.
*/
#include <graphics.h>
int main(void)
{
int i, mx, my;
/* треугольник для drawpoly (замкнут повтором первой точки). */
static const int tri[] = { 120, 244, 210, 244, 165, 210, 120, 244 };
int mx, my;
static const int tri[] = { 210, 235, 300, 235, 255, 150 };
initgraph();
mx = getmaxx(); /* 319 */
my = getmaxy(); /* 255 */
mx = getmaxx();
my = getmaxy();
cleardevice();
@@ -25,39 +22,37 @@ int main(void)
rectangle(0, 0, mx, my);
setcolor(YELLOW);
outtextxy(96, 6, "SPRINTER BGI P2");
outtextxy(110, 6, "BGI FILLS P2b");
/* Палитровая полоса: 15 залитых баров цветами 1..15. */
for (i = 1; i <= 15; i++) {
int x0 = 8 + (i - 1) * 20;
setcolor(i);
bar(x0, 22, x0 + 16, 40);
}
/* Сплошной бар. */
setfillstyle(SOLID_FILL, RED);
bar(12, 28, 92, 78);
setcolor(WHITE);
outtextxy(12, 82, "solid");
/* Окружность (Фаза 1). */
setcolor(LIGHTRED);
circle(60, 120, 40);
/* Бар с штриховкой (HATCH). */
setfillstyle(HATCH_FILL, LIGHTGREEN);
bar(110, 28, 190, 78);
setcolor(WHITE);
outtextxy(110, 82, "hatch");
/* Полный эллипс (Фаза 2a). */
setcolor(LIGHTGREEN);
ellipse(165, 120, 0, 360, 70, 40);
/* 3D-брусок с диагональной штриховкой. */
setcolor(WHITE);
setfillstyle(SLASH_FILL, LIGHTCYAN);
bar3d(210, 34, 280, 78, 14, 1);
outtextxy(210, 82, "bar3d");
/* Верхняя половина дуги (Фаза 2a). */
setcolor(LIGHTCYAN);
arc(270, 140, 0, 180, 35);
/* Залитый эллипс (сплошной). */
setcolor(WHITE);
setfillstyle(SOLID_FILL, LIGHTBLUE);
fillellipse(70, 150, 55, 32);
outtextxy(44, 186, "fillellipse");
/* Треугольник через drawpoly (Фаза 2a). */
setcolor(LIGHTMAGENTA);
drawpoly(4, tri);
/* Залитый треугольник (крестовая штриховка), контур WHITE. */
setcolor(WHITE);
setfillstyle(XHATCH_FILL, LIGHTMAGENTA);
fillpoly(3, tri);
outtextxy(214, 194, "fillpoly");
/* Веер линий слева. */
for (i = 150; i <= my - 6; i += 8) {
setcolor(1 + ((i - 150) / 8) % 15);
line(6, 175, 110, i);
}
setcolor(YELLOW);
outtextxy(120, 190, "circle ellipse arc poly");
for (;;) { } /* держим картинку на экране */
for (;;) { }
}