72ce66275e
Спрайтовая графика поверх accel block-copy (docs/sprite-api-design.md): - Ядро блиттинга: leaf'ы _bgi_blit_rows_raw (dst фикс, только src-страйд) / _bgi_copy_rows_raw (getimage) / _bgi_heal_rows_raw (src==dst). DI один на спрайт (санкция: малый спрайт под одним DI аудио не рвёт); src[0]-фикс снят (точная MAME подавляет CPU-байт триггера записи — на железе перепроверить; для heal был избыточен и снят безусловно). - Общие bracket-free ядра _gfx_blit_full/_gfx_heal_full (полная ширина: клип по экрану + split >256 для putimage) + лин _gfx_blit_sprite/ _gfx_heal_sprite (кадр ≤64, без split, 8-бит w/h) + noclip-варианты (клип-кода нет → полный codegen-win). Имя *_full (не *_clip) — «clip» двусмысленно (sprite-ядра тоже клипуют; различитель — ширина/split). - Движок retained-модели <sprite.h>: sprite_init/update/flip + inline move/frame/show/hide/touch; drawn[2] per-page внутри структуры; кадр — двухпроходно heal ВСЕ -> блит ВСЕ под одной W3-скобкой/банком на проход. - Флаг gfx_sprite_clip(on/off): приложение, само следящее за границами, отключает клип (~+19% на анимации; диспетч пока через if — funcptr далее). - putsprite/movesprite/gfx_blit/putimage(COPY)/getimage переведены на ядро. Тесты: examples/balls (движок, дабл-буфер, boundary-тест клипа), tests/sprites (RAM PASS, клип 4 края, атлас), tests/blitw (trig-leak), tests/spriteclip (hardware-probe: железо НЕ режет за краем -> клип нужен), tests/blitperf, tests/gfxbanks. size-baseline обновлён (53 программы). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
249 lines
14 KiB
C
249 lines
14 KiB
C
/*
|
||
* graphics.h — Turbo-C-совместимый (функционально) BGI-слой для Sprinter.
|
||
*
|
||
* Слой поверх низкоуровневого gfx.h. Состояние (текущий цвет, позиция,
|
||
* границы экрана) хранится внутри — как в оригинальном BGI, где функции
|
||
* рисуют «текущим» цветом от «текущей» позиции.
|
||
*
|
||
* РЕЖИМ фиксируется на этапе ЛИНКОВКИ выбором driver-библиотеки:
|
||
* sprinter-cc --gfx 256 → 320×256×256 (эта версия)
|
||
* (--gfx 16 → 640×256×16 будет позже; API идентичен, менять код не надо)
|
||
* Одновременно два режима использовать нельзя.
|
||
*
|
||
* Отличия от Turbo-C (функциональная, а не буквальная совместимость):
|
||
* - initgraph() без аргументов-указателей на драйвер: драйвер задан
|
||
* линковкой, грузить .bgi-файл с диска не нужно.
|
||
* - в 256-режиме initgraph() загружает EGA-совместимые цвета 0..15 в
|
||
* палитру, так что setcolor(RED) и т.п. работают как в Turbo-C;
|
||
* индексы 16..255 свободны под свои цвета (getmaxcolor() = 255).
|
||
* - fill-паттерны/стили линий/стили текста — Фаза 2 (пока bar/bar-подобное
|
||
* заливается текущим цветом, линии сплошные, текст 8×8).
|
||
*/
|
||
|
||
#ifndef GRAPHICS_H
|
||
#define GRAPHICS_H
|
||
|
||
#include <stdint.h>
|
||
|
||
/* ---- Стандартные EGA/VGA цвета (индексы палитры 0..15) ------------ */
|
||
enum {
|
||
BLACK = 0, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY,
|
||
DARKGRAY, LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA,
|
||
YELLOW, WHITE
|
||
};
|
||
|
||
/* 8-битный цвет (индекс палитры). При смене формата достаточно поменять
|
||
* один typedef — все public-функции используют этот тип. */
|
||
typedef uint8_t color_t;
|
||
|
||
/* ---- Коды graphresult() ------------------------------------------ */
|
||
#define grOk 0
|
||
#define grNoInitGraph (-1)
|
||
#define grError (-11)
|
||
|
||
/* ---- Внутреннее состояние BGI ------------------------------------- *
|
||
* НЕ трогать напрямую — только через функции ниже. Объявлено здесь,
|
||
* чтобы тривиальные аксессоры были inline (без call/ret и без
|
||
* .rel-модуля на каждый геттер). Именно `inline` БЕЗ static: SDCC
|
||
* инлайнит вызов и НЕ эмитит standalone-тело (static inline эмитил бы
|
||
* мёртвую копию в каждый включивший модуль — проверено 2026-07-10);
|
||
* если SDCC когда-нибудь откажется инлайнить — будет громкая ошибка
|
||
* линковки, не тихая деградация. Хранилище — libbgi/common/
|
||
* _bgi_state.c и _bgi_fill_state.c. */
|
||
extern uint8_t _bgi_fg, _bgi_bg;
|
||
extern int _bgi_cx, _bgi_cy;
|
||
extern int _bgi_maxx, _bgi_maxy, _bgi_maxcolor;
|
||
extern int _bgi_result;
|
||
extern uint8_t _bgi_fill_pattern, _bgi_fill_color;
|
||
|
||
/* ---- Setup / teardown -------------------------------------------- */
|
||
|
||
/* Перейти в графический режим (для этой либы — 320×256×256), загрузить
|
||
* EGA-палитру, сбросить состояние: цвет = WHITE, фон = BLACK, текущая
|
||
* позиция = (0,0). Предыдущий видеорежим запоминается для closegraph. */
|
||
void initgraph(void);
|
||
|
||
/* Вернуться в текстовый режим, действовавший до initgraph(). */
|
||
void closegraph(void);
|
||
|
||
/* Код последней ошибки; вызов сбрасывает его в grOk (как в BGI). */
|
||
inline int graphresult(void)
|
||
{
|
||
int r = _bgi_result;
|
||
_bgi_result = grOk;
|
||
return r;
|
||
}
|
||
|
||
/* Очистить экран фоновым цветом и вернуть позицию в (0,0). */
|
||
void cleardevice(void);
|
||
|
||
/* ---- Границы и цвета --------------------------------------------- */
|
||
|
||
inline int getmaxx(void) { return _bgi_maxx; } /* 319 */
|
||
inline int getmaxy(void) { return _bgi_maxy; } /* 255 */
|
||
inline color_t getmaxcolor(void) { return (color_t)_bgi_maxcolor; } /* 255 */
|
||
|
||
/* текущий цвет рисования */
|
||
inline void setcolor(color_t color) { _bgi_fg = (uint8_t)color; }
|
||
inline color_t getcolor(void) { return _bgi_fg; }
|
||
/* фоновый цвет (для cleardevice/текста) */
|
||
inline void setbkcolor(color_t color) { _bgi_bg = (uint8_t)color; }
|
||
inline color_t getbkcolor(void) { return _bgi_bg; }
|
||
|
||
/* ---- Точки ------------------------------------------------------- */
|
||
|
||
void putpixel(int x, int y, color_t color);
|
||
color_t getpixel(int x, int y);
|
||
|
||
/* ---- Текущая позиция и линии ------------------------------------- */
|
||
|
||
/* задать текущую позицию (CP) */
|
||
inline void moveto(int x, int y) { _bgi_cx = x; _bgi_cy = y; }
|
||
/* сдвинуть CP относительно */
|
||
inline void moverel(int dx, int dy) { _bgi_cx += dx; _bgi_cy += dy; }
|
||
inline int getx(void) { return _bgi_cx; }
|
||
inline int gety(void) { return _bgi_cy; }
|
||
|
||
void lineto(int x, int y); /* линия CP→(x,y), CP := (x,y) */
|
||
void linerel(int dx, int dy); /* линия CP→CP+(dx,dy), CP сдвигается */
|
||
void line(int x1, int y1, int x2, int y2); /* линия, CP не меняет */
|
||
|
||
/* ---- Фигуры ------------------------------------------------------ */
|
||
|
||
void rectangle(int left, int top, int right, int bottom); /* контур */
|
||
void bar(int left, int top, int right, int bottom); /* заливка */
|
||
void circle(int x, int y, int radius); /* окружность */
|
||
|
||
/* Дуги/эллипсы: угол в градусах, 0°=восток, против часовой стрелки.
|
||
* Полный эллипс — ellipse(x,y,0,360,xr,yr). */
|
||
void arc(int x, int y, int stangle, int endangle, int radius);
|
||
void ellipse(int x, int y, int stangle, int endangle,
|
||
int xradius, int yradius);
|
||
|
||
/* Ломаная по numpoints точкам {x0,y0,x1,y1,…}; НЕ замыкается сама. */
|
||
void drawpoly(int numpoints, const int *polypoints);
|
||
|
||
/* ---- Стиль линий ------------------------------------------------- *
|
||
* Действует на line/lineto/linerel/rectangle/drawpoly. USERBIT_LINE
|
||
* использует 16-битный upattern. thickness: NORM_WIDTH или THICK_WIDTH.
|
||
* (Окружности/дуги пока всегда сплошные 1px — упрощение.) */
|
||
enum { SOLID_LINE = 0, DOTTED_LINE, CENTER_LINE, DASHED_LINE, USERBIT_LINE };
|
||
#define NORM_WIDTH 1
|
||
#define THICK_WIDTH 3
|
||
|
||
struct linesettingstype { int linestyle; unsigned upattern; int thickness; };
|
||
|
||
void setlinestyle(int linestyle, unsigned upattern, int thickness);
|
||
void getlinesettings(struct linesettingstype *lineinfo);
|
||
|
||
/* ---- Заливки ----------------------------------------------------- *
|
||
* Стиль заливки — паттерн (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; color_t color; };
|
||
|
||
inline void setfillstyle(int pattern, color_t color)
|
||
{
|
||
_bgi_fill_pattern = (uint8_t)pattern;
|
||
_bgi_fill_color = (uint8_t)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);
|
||
|
||
/* floodfill — заливка области, содержащей (x,y), текущим стилем до
|
||
* границы цвета border. Заливка сплошная цветом заливки (паттерн в
|
||
* floodfill пока не применяется — упрощение). */
|
||
void floodfill(int x, int y, int border);
|
||
|
||
/* pieslice — залитый сектор круга; sector — эллиптический сектор.
|
||
* Контур (дуга + два радиуса) тек. цветом, нутро — стилем заливки.
|
||
* Для секторов >180° возможен перелив в «выемку» (min/max по строке). */
|
||
void pieslice(int x, int y, int stangle, int endangle, int radius);
|
||
void sector(int x, int y, int stangle, int endangle,
|
||
int xradius, int yradius);
|
||
|
||
/* ---- Растровые образы (спрайты) ---------------------------------- *
|
||
* Формат буфера: 2×uint16 (ширина, высота в пикселях) + пиксели
|
||
* построчно (1 байт/пиксель в режиме 256). Буфер выделяет вызывающий
|
||
* размером imagesize(). */
|
||
enum { COPY_PUT = 0, XOR_PUT, OR_PUT, AND_PUT, NOT_PUT };
|
||
|
||
/* Байт под образ прямоугольника (left,top)-(right,bottom) включительно.
|
||
* ВНИМАНИЕ: результат — 16-бит unsigned; образы ≥64 КБ не поддержаны. */
|
||
unsigned imagesize(int left, int top, int right, int bottom);
|
||
|
||
/* Сохранить прямоугольник экрана в bitmap (размер = imagesize()). */
|
||
void getimage(int left, int top, int right, int bottom, void *bitmap);
|
||
|
||
/* Вывести образ левым-верхним углом в (left,top) операцией op
|
||
* (COPY/XOR/OR/AND/NOT_PUT). COPY_PUT — через акселератор, с
|
||
* клиппингом по экрану и текущим банком gfx_set_bank (см. <gfx.h>);
|
||
* XOR/OR/AND/NOT — по-пиксельно, без клиппинга. */
|
||
void putimage(int left, int top, const void *bitmap, int op);
|
||
|
||
/* ---- Спрайты (аппаратная прозрачность; docs/sprite-api-design.md) - *
|
||
* Спрайт — тот же буфер getimage-формата; прозрачные точки в данных =
|
||
* байты 0xFF (GFX_TRANSPARENT). Прозрачность и сохранность фона
|
||
* обеспечивает железо (банк GFX_BANK_SPRITE = 0x5C): прозрачные байты
|
||
* не записываются, ОЗУ-копия фона не трогается — стирание без
|
||
* save-буфера через gfx_heal (см. <gfx.h>).
|
||
* ВАЖНО: фон/сцену рисовать ОБЫЧНЫМ банком (0x50) — то, что нарисовано
|
||
* при активном бите 2 (0x54/0x5C), в ОЗУ-копию не попадает и heal
|
||
* вернёт то, что лежало под ним. */
|
||
|
||
/* Вывести спрайт в (x,y): банк на время вызова — GFX_BANK_SPRITE,
|
||
* затем прежний. Клиппится по экрану. */
|
||
void putsprite(int x, int y, const void *img);
|
||
|
||
/* Переместить спрайт: восстановить фон под (oldx,oldy) (gfx_heal по
|
||
* размерам img) и вывести спрайт в (x,y). Перекрытие старой и новой
|
||
* позиций безопасно (heal читает фон из ОЗУ-копии). */
|
||
void movesprite(int oldx, int oldy, int x, int y, const void *img);
|
||
|
||
/* ---- Текст ------------------------------------------------------- *
|
||
* Шрифт 8×8 (системный). Рисуется текущим цветом на фоновом. */
|
||
|
||
void outtextxy(int x, int y, const char *text); /* в (x,y), CP не меняет */
|
||
void outtext(const char *text); /* в CP; CP сдвигается вправо */
|
||
|
||
/* ---- Стиль текста ------------------------------------------------ *
|
||
* Поддержан только DEFAULT_FONT (8×8, растровый); charsize 1..10 —
|
||
* целочисленный масштаб; direction — HORIZ_DIR или VERT_DIR (поворот
|
||
* 90° против часовой). Фон текста прозрачный (рисуется только цвет). */
|
||
enum { DEFAULT_FONT = 0, TRIPLEX_FONT, SMALL_FONT, SANS_SERIF_FONT,
|
||
GOTHIC_FONT };
|
||
enum { HORIZ_DIR = 0, VERT_DIR = 1 };
|
||
|
||
struct textsettingstype {
|
||
int font;
|
||
int direction;
|
||
int charsize;
|
||
int horiz;
|
||
int vert;
|
||
};
|
||
|
||
void settextstyle(int font, int direction, int charsize);
|
||
void gettextsettings(struct textsettingstype *textinfo);
|
||
int textwidth(const char *text);
|
||
int textheight(const char *text);
|
||
|
||
#endif
|