libbgi: выделить графику BGI в отдельную библиотеку + тест спрайтов
Графика вынесена из libc/ в новую библиотеку libbgi/:
- common/ — mode-agnostic математика и состояние (один исходник,
.rel попадает в оба driver-архива);
- bgi256/ + bgi16/ — mode-specific leaf'ы (raw-плот/чтение/спаны);
- include/ — graphics.h + gfx.h; _bgi.h — внутренний заголовок.
Собираются lib/bgi256.lib (и bgi16.lib в Фазе 2); выбор режима
линковкой через sprinter-cc --gfx 256|16. libc/ теперь без графики.
tests/bgi_img — тест спрайтов getimage/putimage/imagesize (5 операций
COPY/XOR/OR/AND/NOT + XOR-round-trip + self-check imagesize).
Проверен автотестом в MAME.
Примечание: make size-check пока красный (gfx_dbuf/gfx_demo выросли
после реорга) — закрыть по завершении миграции libbgi.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* _bgi_ellipse_arc — контур дуги/эллипса полилинией.
|
||||
*
|
||||
* Точки параметризованы углом (BGI: 0°=восток, против часовой, ось Y
|
||||
* вниз): px = cx + xr*cos(a), py = cy - yr*sin(a). Соседние выборки
|
||||
* соединяются отрезками — контур сплошной при любом радиусе.
|
||||
*
|
||||
* Тригонометрия в Q7 (×128): xr*cos128 ≤ 255×128 < 32767, помещается в
|
||||
* int — БЕЗ 32-битной арифметики (её SDCC/Z80 собирает криво: раньше
|
||||
* эллипс через (long)…>>8 рисовался прямоугольником).
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
void _bgi_ellipse_arc(int cx, int cy, int stangle, int endangle,
|
||||
int xr, int yr, uint8_t color)
|
||||
{
|
||||
int a, x, y, px, py, first;
|
||||
|
||||
if (endangle < stangle) endangle += 360; /* дуга через 0° */
|
||||
|
||||
px = 0;
|
||||
py = 0;
|
||||
first = 1;
|
||||
for (a = stangle; a <= endangle; a++) {
|
||||
x = cx + ((xr * _bgi_cos128(a)) >> 7);
|
||||
y = cy - ((yr * _bgi_sin128(a)) >> 7);
|
||||
if (!first) _bgi_lineseg(px, py, x, y, color);
|
||||
px = x;
|
||||
py = y;
|
||||
first = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* _bgi_begin — открыть W3-сессию для пакетного рисования: DI, сохранить
|
||||
* текущую страницу W3 и замапить _gfx_bank (видеобанк 0x50..0x5F).
|
||||
* Композитные примитивы обрамляют одной парой _bgi_begin/_bgi_end весь
|
||||
* внутренний цикл — W3-танец оплачивается один раз на операцию, а не
|
||||
* на пиксель. НЕ реентерабельно (сейв в _gfx_saved_w3): между begin и
|
||||
* end IRQ выключены.
|
||||
*
|
||||
* Real код (поглощён из старого libc/gfx/_gfx_w3_video_begin.c) — не
|
||||
* обёртка. Mode-agnostic: использует только _gfx_bank/_gfx_saved_w3.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void _bgi_begin(void) __naked
|
||||
{
|
||||
__asm
|
||||
di
|
||||
in a, (#0xE2)
|
||||
ld (__gfx_saved_w3), a
|
||||
ld a, (__gfx_bank)
|
||||
out (#0xE2), a
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* _bgi_clearall — залить весь экран цветом, self-bracket (своя W3-скобка).
|
||||
* cleardevice зовёт это фоновым цветом. Реальная композиция
|
||||
* _bgi_begin + _bgi_clear_raw + _bgi_end.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void _bgi_clearall(uint8_t color)
|
||||
{
|
||||
_bgi_begin();
|
||||
_bgi_clear_raw(color);
|
||||
_bgi_end();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* _bgi_draw_scaled — строка растровым шрифтом 8×8 с целочисленным
|
||||
* масштабом и направлением, цветом _bgi_fg, прозрачным фоном.
|
||||
*
|
||||
* HORIZ_DIR: пиксель глифа (c,r) → экран (x+c*size, y+r*size).
|
||||
* VERT_DIR (поворот 90° против часовой): (c,r) → (x+r*size, y-c*size).
|
||||
* Каждый «пиксель» глифа — блок size×size из raw-плотов, всё в одной
|
||||
* W3-скобке.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void _bgi_draw_scaled(int x, int y, const char *s)
|
||||
{
|
||||
uint8_t rows[8];
|
||||
uint8_t b, fg, size, dir;
|
||||
int r, c, bx, by, px, py;
|
||||
|
||||
fg = _bgi_fg;
|
||||
size = _bgi_text_size ? _bgi_text_size : 1;
|
||||
dir = _bgi_text_dir;
|
||||
|
||||
_bgi_begin();
|
||||
while (*s) {
|
||||
_bgi_font_rows((unsigned char)*s, rows);
|
||||
for (r = 0; r < 8; r++) {
|
||||
b = rows[r];
|
||||
for (c = 0; c < 8; c++) {
|
||||
if (!((b >> (7 - c)) & 1)) continue;
|
||||
for (by = 0; by < size; by++) {
|
||||
for (bx = 0; bx < size; bx++) {
|
||||
if (dir == VERT_DIR) {
|
||||
px = x + r * size + by;
|
||||
py = y - c * size - bx;
|
||||
} else {
|
||||
px = x + c * size + bx;
|
||||
py = y + r * size + by;
|
||||
}
|
||||
_bgi_plot_raw(px, py, fg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dir == VERT_DIR) y -= 8 * size;
|
||||
else x += 8 * size;
|
||||
s++;
|
||||
}
|
||||
_bgi_end();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* _bgi_end — закрыть W3-сессию: восстановить сохранённую страницу W3 и
|
||||
* EI. Real код (поглощён из старого libc/gfx/_gfx_w3_video_end.c).
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void _bgi_end(void) __naked
|
||||
{
|
||||
__asm
|
||||
ld a, (__gfx_saved_w3)
|
||||
out (#0xE2), a
|
||||
ei
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* _bgi_font_rows — 8 байт строк глифа символа c (mode-agnostic: просто
|
||||
* читает шрифт через _gfx_font_ptr). Шрифт системный, interleaved:
|
||||
* offset = row*256 + char (MSB-first), см. memory/sprinter_font_format.
|
||||
* Грузится лениво (_gfx_font_ensure) при первом использовании.
|
||||
*
|
||||
* Раньше жил в drv256/ по недосмотру — перенесён в common/, т.к. логика
|
||||
* не зависит от видеорежима (один и тот же .rel в bgi256.lib и bgi16.lib).
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void _bgi_font_rows(unsigned char c, uint8_t *rows8)
|
||||
{
|
||||
const uint8_t *f;
|
||||
int r;
|
||||
_gfx_font_ensure();
|
||||
f = _gfx_font_ptr;
|
||||
for (r = 0; r < 8; r++)
|
||||
rows8[r] = f[r * 256 + c];
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* _bgi_hspan — горизонтальный отрезок len пикселей от (x,y), self-bracket
|
||||
* (своя W3-скобка). Для floodfill (заливка строки вне пакетной сессии).
|
||||
* Реальная композиция _bgi_begin + _bgi_hspan_raw + _bgi_end.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void _bgi_hspan(int x, int y, int len, uint8_t color)
|
||||
{
|
||||
_bgi_begin();
|
||||
_bgi_hspan_raw(x, y, len, color);
|
||||
_bgi_end();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* _bgi_line_state — текущий стиль/толщина/маска линий BGI.
|
||||
* initgraph ставит SOLID_LINE / NORM / 0xFFFF. Без инициализации.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
uint8_t _bgi_line_style;
|
||||
uint8_t _bgi_line_thick;
|
||||
uint16_t _bgi_line_pattern;
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* _bgi_lineseg — отрезок (x0,y0)->(x1,y1) БЕЗ стиля/толщины (SOLID+NORM).
|
||||
* Real логика (поглощена из старого libc/gfx/gfx_line256.c), не обёртка.
|
||||
*
|
||||
* Ортогональные отрезки уходят на driver-leaf'ы _bgi_hspan_raw /
|
||||
* _bgi_vspan_raw (акселератор); наклонные — Брезенхэм поточечно через
|
||||
* _bgi_plot_raw. Каждый путь ставит свою W3-скобку (_bgi_begin/_bgi_end)
|
||||
* — вызывать _bgi_lineseg ВНЕ чужой скобки (W3-сейв не реентерабелен).
|
||||
* Вызывают: _bgi_styled_line (SOLID+NORM), _bgi_arc_draw, fillpoly,
|
||||
* sector — все вне скобки.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void _bgi_lineseg(int x0, int y0, int x1, int y1, uint8_t color)
|
||||
{
|
||||
/* Горизонталь — один H-burst. */
|
||||
if (y0 == y1) {
|
||||
int x = x0 <= x1 ? x0 : x1;
|
||||
int w = (x0 <= x1 ? x1 - x0 : x0 - x1) + 1;
|
||||
_bgi_begin();
|
||||
_bgi_hspan_raw(x, y0, w, color);
|
||||
_bgi_end();
|
||||
return;
|
||||
}
|
||||
/* Вертикаль — один V-burst (256) или RMW-цикл (16). */
|
||||
if (x0 == x1) {
|
||||
int y = y0 <= y1 ? y0 : y1;
|
||||
int h = (y0 <= y1 ? y1 - y0 : y0 - y1) + 1;
|
||||
_bgi_begin();
|
||||
_bgi_vspan_raw(x0, y, h, color);
|
||||
_bgi_end();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Наклонный — Брезенхэм в одной W3-скобке. */
|
||||
int dx = x1 - x0; int sx = dx < 0 ? -1 : 1; if (dx < 0) dx = -dx;
|
||||
int dy = y1 - y0; int sy = dy < 0 ? -1 : 1; if (dy < 0) dy = -dy;
|
||||
int err = (dx > dy ? dx : -dy) / 2;
|
||||
int x = x0, y = y0;
|
||||
_bgi_begin();
|
||||
for (;;) {
|
||||
_bgi_plot_raw(x, y, color);
|
||||
if (x == x1 && y == y1) break;
|
||||
int e2 = err;
|
||||
if (e2 > -dx) { err -= dy; x += sx; }
|
||||
if (e2 < dy) { err += dx; y += sy; }
|
||||
}
|
||||
_bgi_end();
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* _bgi_read — цвет пикселя (x,y), self-bracket (своя W3-скобка).
|
||||
* Для разовых чтений из математики, которой не нужен пакетный режим
|
||||
* (floodfill). getpixel тоже зовёт это. Реальная композиция
|
||||
* _bgi_begin + _bgi_read_raw + _bgi_end — не обёртка над обёрткой.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
unsigned _bgi_read(int x, int y)
|
||||
{
|
||||
unsigned c;
|
||||
_bgi_begin();
|
||||
c = _bgi_read_raw(x, y);
|
||||
_bgi_end();
|
||||
return c;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* _bgi_state.c — общее состояние BGI-слоя.
|
||||
*
|
||||
* File-scope переменные НЕ инициализируются (crt0 зануляет _DATA;
|
||||
* см. memory/sdcc_static_storage_gotcha). Осмысленные значения
|
||||
* выставляет initgraph().
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
uint8_t _bgi_fg;
|
||||
uint8_t _bgi_bg;
|
||||
int _bgi_cx;
|
||||
int _bgi_cy;
|
||||
int _bgi_maxx;
|
||||
int _bgi_maxy;
|
||||
int _bgi_maxcolor;
|
||||
uint8_t _bgi_prevmode;
|
||||
int _bgi_result;
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* _bgi_styled_line — отрезок текущим стилем и толщиной линии.
|
||||
*
|
||||
* SOLID+NORM → быстрый _bgi_lineseg (accel). Иначе — Брезенхэм с
|
||||
* 16-битной маской (бит на шаг; когда бит 0 — пропуск) и, для THICK,
|
||||
* дублированием перпендикулярно основной оси (±1). Всё raw в одной
|
||||
* W3-скобке.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
/* Маски стандартных стилей (SOLID/DOTTED/CENTER/DASHED). */
|
||||
static const uint16_t _bgi_line_pat[4] = { 0xFFFF, 0xCCCC, 0xFF18, 0xF8F8 };
|
||||
|
||||
void _bgi_styled_line(int x0, int y0, int x1, int y1, uint8_t color)
|
||||
{
|
||||
uint16_t pat;
|
||||
uint8_t bit, style, thick;
|
||||
int dx, dy, sx, sy, err, e2, major_x;
|
||||
|
||||
style = _bgi_line_style;
|
||||
thick = _bgi_line_thick;
|
||||
if (style == SOLID_LINE && thick <= 1) {
|
||||
_bgi_lineseg(x0, y0, x1, y1, color);
|
||||
return;
|
||||
}
|
||||
pat = (style == USERBIT_LINE) ? _bgi_line_pattern
|
||||
: _bgi_line_pat[style & 3];
|
||||
|
||||
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;
|
||||
major_x = (dx >= dy);
|
||||
bit = 0;
|
||||
|
||||
_bgi_begin();
|
||||
for (;;) {
|
||||
if ((pat >> (bit & 15)) & 1) {
|
||||
_bgi_plot_raw(x0, y0, color);
|
||||
if (thick > 1) {
|
||||
if (major_x) {
|
||||
_bgi_plot_raw(x0, y0 - 1, color);
|
||||
_bgi_plot_raw(x0, y0 + 1, color);
|
||||
} else {
|
||||
_bgi_plot_raw(x0 - 1, y0, color);
|
||||
_bgi_plot_raw(x0 + 1, y0, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
bit++;
|
||||
if (x0 == x1 && y0 == y1) break;
|
||||
e2 = 2 * err;
|
||||
if (e2 > -dy) { err -= dy; x0 += sx; }
|
||||
if (e2 < dx) { err += dx; y0 += sy; }
|
||||
}
|
||||
_bgi_end();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* _bgi_text_state — направление и масштаб текста BGI.
|
||||
* initgraph ставит HORIZ_DIR / size 1. Без инициализации.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
uint8_t _bgi_text_dir;
|
||||
uint8_t _bgi_text_size;
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* _bgi_trig — целочисленная тригонометрия для дуг/эллипсов BGI.
|
||||
*
|
||||
* Угол в градусах (Turbo-C); sin/cos масштабированы на 128 (Q7):
|
||||
* _bgi_sin128(90) = 128. Масштаб 128 (а не 256) выбран так, чтобы
|
||||
* произведение радиус×значение помещалось в 16-бит int для любого
|
||||
* экранного радиуса (255×128 = 32640 < 32767) — и НЕ приходилось
|
||||
* трогать 32-битную арифметику, которую SDCC/Z80 собирает криво
|
||||
* (см. memory/avoid_32bit_arith_z80; ловили эллипс-в-прямоугольник).
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
/* sin(0°..90°) × 128. */
|
||||
static const int _bgi_sintab[91] = {
|
||||
0, 2, 4, 7, 9, 11, 13, 16, 18, 20,
|
||||
22, 24, 27, 29, 31, 33, 35, 37, 40, 42,
|
||||
44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
|
||||
64, 66, 68, 70, 72, 73, 75, 77, 79, 81,
|
||||
82, 84, 86, 87, 89, 91, 92, 94, 95, 97,
|
||||
98, 99, 101, 102, 104, 105, 106, 107, 109, 110,
|
||||
111, 112, 113, 114, 115, 116, 117, 118, 119, 119,
|
||||
120, 121, 122, 122, 123, 124, 124, 125, 125, 126,
|
||||
126, 126, 127, 127, 127, 128, 128, 128, 128, 128,
|
||||
128,
|
||||
};
|
||||
|
||||
/* sin(угол°) × 128, угол приводится к 0..359. */
|
||||
int _bgi_sin128(int deg)
|
||||
{
|
||||
deg %= 360;
|
||||
if (deg < 0) deg += 360;
|
||||
if (deg <= 90) return _bgi_sintab[deg];
|
||||
if (deg <= 180) return _bgi_sintab[180 - deg];
|
||||
if (deg <= 270) return -_bgi_sintab[deg - 180];
|
||||
return -_bgi_sintab[360 - deg];
|
||||
}
|
||||
|
||||
/* cos(угол°) × 128 = sin(угол+90). */
|
||||
int _bgi_cos128(int deg)
|
||||
{
|
||||
return _bgi_sin128(deg + 90);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* _gfx_font_ensure — ленивая инициализация шрифта: текстовые рендеры
|
||||
* зовут её при каждом входе, системный шрифт грузится при первом
|
||||
* использовании (чисто графическая программа BIOS-вызов не платит).
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
void _gfx_font_ensure(void)
|
||||
{
|
||||
if (!_gfx_font_loaded) gfx_load_default_font();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* _gfx_font_state — буфер шрифта, указатель и флаг загрузки (данные).
|
||||
*
|
||||
* Формат (ZX-Spectrum-совместимый): 256 глифов × 8 рядов × 1 байт = 2 КБ,
|
||||
* INTERLEAVED — offset = row*256 + char (ряд 0 всех глифов занимает
|
||||
* 0x000..0x0FF и т.д.), биты MSB-first (бит 7 = левый пиксель).
|
||||
*
|
||||
* _gfx_font_ptr всегда указывает на валидные данные после
|
||||
* _gfx_font_ensure(); программы могут подменить шрифт gfx_set_font().
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
uint8_t _gfx_font_buf[_GFX_FONT_BYTES];
|
||||
const uint8_t *_gfx_font_ptr = _gfx_font_buf;
|
||||
uint8_t _gfx_font_loaded;
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* _gfx_state — общее состояние графики (модуль только с данными).
|
||||
*
|
||||
* Пишут gfx_set_visible_page / gfx_set_draw_page /
|
||||
* gfx_set_bank; читают все mode-специфичные примитивы.
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
uint8_t _gfx_visible_page;
|
||||
uint8_t _gfx_draw_page;
|
||||
|
||||
/* См. memory/sprinter_vram_transparency.md про биты 0x50..0x5F
|
||||
* (0x50 normal, 0x54 temp, 0x58 transparent, 0x5C оба). */
|
||||
uint8_t _gfx_bank = 0x50;
|
||||
|
||||
/* Каждая VRAM-строка — 1024 байта: page 0 занимает байты 0..319
|
||||
* (CPU 0xC000+), page 1 — 320..639 (CPU 0xC140+), остаток — дескрипторы
|
||||
* режима/палитра, их не трогаем. */
|
||||
uint16_t _gfx_addr_base = 0xC000;
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* _gfx_w3_state — сохранённый байт W3 между begin и end (данные).
|
||||
*
|
||||
* НЕ реентерабельно — безопасно только потому, что GFX держит
|
||||
* прерывания выключенными между _gfx_w3_video_begin и _end.
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
uint8_t _gfx_saved_w3;
|
||||
@@ -0,0 +1,7 @@
|
||||
/* arc — дуга окружности радиуса r от stangle до endangle (градусы). */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void arc(int x, int y, int stangle, int endangle, int radius)
|
||||
{
|
||||
_bgi_ellipse_arc(x, y, stangle, endangle, radius, radius, _bgi_fg);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* bar — залитый прямоугольник (left,top)-(right,bottom) включительно
|
||||
* ТЕКУЩИМ стилем заливки (setfillstyle), без рамки — как в Turbo-C.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void bar(int left, int top, int right, int bottom)
|
||||
{
|
||||
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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* circle — окружность (midpoint) с центром (cx,cy) радиуса r,
|
||||
* текущим цветом. Целочисленный алгоритм, 8 октантов за шаг.
|
||||
*
|
||||
* Вся окружность рисуется в ОДНОЙ W3-скобке (_bgi_begin/_bgi_end) +
|
||||
* raw-плот — иначе скобка на каждый пиксель делает большие окружности
|
||||
* мучительно медленными (тысячи DI/EI).
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void circle(int cx, int cy, int r)
|
||||
{
|
||||
int x, y, d;
|
||||
uint8_t c = _bgi_fg;
|
||||
|
||||
if (r < 0) return;
|
||||
|
||||
x = 0;
|
||||
y = r;
|
||||
d = 1 - r;
|
||||
_bgi_begin();
|
||||
while (x <= y) {
|
||||
_bgi_plot_raw(cx + x, cy + y, c);
|
||||
_bgi_plot_raw(cx - x, cy + y, c);
|
||||
_bgi_plot_raw(cx + x, cy - y, c);
|
||||
_bgi_plot_raw(cx - x, cy - y, c);
|
||||
_bgi_plot_raw(cx + y, cy + x, c);
|
||||
_bgi_plot_raw(cx - y, cy + x, c);
|
||||
_bgi_plot_raw(cx + y, cy - x, c);
|
||||
_bgi_plot_raw(cx - y, cy - x, c);
|
||||
if (d < 0) {
|
||||
d += 2 * x + 3;
|
||||
} else {
|
||||
d += 2 * (x - y) + 5;
|
||||
y--;
|
||||
}
|
||||
x++;
|
||||
}
|
||||
_bgi_end();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/* cleardevice — очистить экран фоновым цветом, вернуть CP в (0,0). */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void cleardevice(void)
|
||||
{
|
||||
_bgi_clearall(_bgi_bg);
|
||||
_bgi_cx = 0;
|
||||
_bgi_cy = 0;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/* closegraph — вернуть текстовый режим, действовавший до initgraph.
|
||||
* _bgi_mode_done удалён — _videomode_raw_set (mode-agnostic) */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void closegraph(void)
|
||||
{
|
||||
_videomode_raw_set(_bgi_prevmode);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* drawpoly — ломаная по numpoints точкам (массив x0,y0,x1,y1,…),
|
||||
* текущим цветом. Как в Turbo-C, контур НЕ замыкается автоматически:
|
||||
* чтобы получить замкнутый полигон, повторите первую точку последней.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void drawpoly(int numpoints, const int *polypoints)
|
||||
{
|
||||
int i;
|
||||
if (numpoints < 2) return;
|
||||
for (i = 0; i < numpoints - 1; i++) {
|
||||
_bgi_styled_line(polypoints[2 * i], polypoints[2 * i + 1],
|
||||
polypoints[2 * i + 2], polypoints[2 * i + 3],
|
||||
_bgi_fg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* ellipse — контур эллипса/эллиптической дуги с полуосями xradius,
|
||||
* yradius от stangle до endangle. Полный эллипс: stangle=0,endangle=360.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void ellipse(int x, int y, int stangle, int endangle,
|
||||
int xradius, int yradius)
|
||||
{
|
||||
_bgi_ellipse_arc(x, y, stangle, endangle, xradius, yradius, _bgi_fg);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* floodfill — заливка связной области с (x,y) до границы цвета border.
|
||||
*
|
||||
* Скан-строчный (span) flood: заполняем горизонтальный отрезок текущим
|
||||
* цветом заливки, затем ищем новые «затравки» в строках сверху/снизу.
|
||||
* Стоп-условие пикселя: вне экрана / цвет == border / цвет уже == цвет
|
||||
* заливки (иначе не сойдётся). Паттерн в floodfill не применяется —
|
||||
* заливаем сплошным _bgi_fill_color (упрощение).
|
||||
*
|
||||
* Чтение/запись — self-bracket (_bgi_read/_bgi_hspan): чуть медленнее,
|
||||
* зато не держим DI на всю (возможно большую) заливку.
|
||||
*
|
||||
* Стек затравок ограничен FF_STACK; при переполнении лишние затравки
|
||||
* отбрасываются (заливка может остаться неполной на очень «рваных»
|
||||
* фигурах) — компромисс по памяти.
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
#define FF_STACK 300
|
||||
static int ff_sx[FF_STACK];
|
||||
static int ff_sy[FF_STACK];
|
||||
static int ff_sp;
|
||||
|
||||
/* 1 = пиксель нельзя заливать (стоп): вне экрана / border / уже залит. */
|
||||
static int ff_stop(int x, int y, int border)
|
||||
{
|
||||
int c;
|
||||
if (x < 0 || x > _bgi_maxx || y < 0 || y > _bgi_maxy) return 1;
|
||||
c = _bgi_read(x, y);
|
||||
return (c == border || c == _bgi_fill_color);
|
||||
}
|
||||
|
||||
static void ff_push(int x, int y)
|
||||
{
|
||||
if (ff_sp < FF_STACK) { ff_sx[ff_sp] = x; ff_sy[ff_sp] = y; ff_sp++; }
|
||||
}
|
||||
|
||||
void floodfill(int x, int y, int border)
|
||||
{
|
||||
int xl, xr, xi, yy, d;
|
||||
uint8_t fc = _bgi_fill_color;
|
||||
|
||||
if (ff_stop(x, y, border)) return;
|
||||
|
||||
ff_sp = 0;
|
||||
ff_push(x, y);
|
||||
|
||||
while (ff_sp > 0) {
|
||||
ff_sp--;
|
||||
x = ff_sx[ff_sp];
|
||||
y = ff_sy[ff_sp];
|
||||
if (ff_stop(x, y, border)) continue;
|
||||
|
||||
for (xl = x; !ff_stop(xl - 1, y, border); ) xl--;
|
||||
for (xr = x; !ff_stop(xr + 1, y, border); ) xr++;
|
||||
_bgi_hspan(xl, y, xr - xl + 1, fc);
|
||||
|
||||
for (d = -1; d <= 1; d += 2) {
|
||||
yy = y + d;
|
||||
xi = xl;
|
||||
while (xi <= xr) {
|
||||
while (xi <= xr && ff_stop(xi, yy, border)) xi++;
|
||||
if (xi > xr) break;
|
||||
while (xi <= xr && !ff_stop(xi, yy, border)) xi++;
|
||||
ff_push(xi - 1, yy); /* правый край найденного пробега */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/* getbkcolor — текущий фоновый цвет. */
|
||||
#include "../_bgi.h"
|
||||
|
||||
int getbkcolor(void)
|
||||
{
|
||||
return _bgi_bg;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/* getcolor — текущий цвет рисования. */
|
||||
#include "../_bgi.h"
|
||||
|
||||
int getcolor(void)
|
||||
{
|
||||
return _bgi_fg;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* getimage — сохранить прямоугольник экрана в буфер bitmap.
|
||||
* Формат: uint16 width, uint16 height, затем w*h байт (по строкам).
|
||||
* Чтение raw в одной W3-скобке.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void getimage(int left, int top, int right, int bottom, void *bitmap)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)bitmap;
|
||||
int w = right - left + 1;
|
||||
int h = bottom - top + 1;
|
||||
int x, y;
|
||||
|
||||
if (w <= 0 || h <= 0) return;
|
||||
|
||||
*p++ = (uint8_t)w;
|
||||
*p++ = (uint8_t)(w >> 8);
|
||||
*p++ = (uint8_t)h;
|
||||
*p++ = (uint8_t)(h >> 8);
|
||||
|
||||
_bgi_begin();
|
||||
for (y = 0; y < h; y++)
|
||||
for (x = 0; x < w; x++)
|
||||
*p++ = (uint8_t)_bgi_read_raw(left + x, top + y);
|
||||
_bgi_end();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/* getlinesettings — прочитать текущий стиль/маску/толщину линий. */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void getlinesettings(struct linesettingstype *lineinfo)
|
||||
{
|
||||
if (!lineinfo) return;
|
||||
lineinfo->linestyle = _bgi_line_style;
|
||||
lineinfo->upattern = _bgi_line_pattern;
|
||||
lineinfo->thickness = _bgi_line_thick;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/* getmaxcolor — максимальный индекс цвета (255 в 256-цветном режиме). */
|
||||
#include "../_bgi.h"
|
||||
|
||||
int getmaxcolor(void)
|
||||
{
|
||||
return _bgi_maxcolor;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/* getmaxx — максимальная координата X (ширина-1). */
|
||||
#include "../_bgi.h"
|
||||
|
||||
int getmaxx(void)
|
||||
{
|
||||
return _bgi_maxx;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/* getmaxy — максимальная координата Y (высота-1). */
|
||||
#include "../_bgi.h"
|
||||
|
||||
int getmaxy(void)
|
||||
{
|
||||
return _bgi_maxy;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/* gettextsettings — текущий стиль текста (justify не реализован → 0). */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void gettextsettings(struct textsettingstype *textinfo)
|
||||
{
|
||||
if (!textinfo) return;
|
||||
textinfo->font = DEFAULT_FONT;
|
||||
textinfo->direction = _bgi_text_dir;
|
||||
textinfo->charsize = _bgi_text_size;
|
||||
textinfo->horiz = 0;
|
||||
textinfo->vert = 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/* getx — текущая позиция X. */
|
||||
#include "../_bgi.h"
|
||||
|
||||
int getx(void)
|
||||
{
|
||||
return _bgi_cx;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/* gety — текущая позиция Y. */
|
||||
#include "../_bgi.h"
|
||||
|
||||
int gety(void)
|
||||
{
|
||||
return _bgi_cy;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* gfx_get_bank — текущий байт страницы W3 (0x50..0x5F).
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
uint8_t gfx_get_bank(void)
|
||||
{
|
||||
return _gfx_bank;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* gfx_get_draw_page — кэшированный номер рисуемой страницы (0/1).
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
uint8_t gfx_get_draw_page(void)
|
||||
{
|
||||
return _gfx_draw_page;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* gfx_get_visible_page — кэшированный номер видимой страницы (0/1).
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
uint8_t gfx_get_visible_page(void)
|
||||
{
|
||||
return _gfx_visible_page;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* gfx_load_default_font — забрать активный системный знакогенератор
|
||||
* через BIOS WIN_GET_ZG (fn 0xB8) в _gfx_font_buf и включить его.
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
/* BIOS WIN_GET_ZG (0xB8): DE = приёмник, возвращает 2 КБ. */
|
||||
static void bios_get_zg(uint8_t *dest) __naked
|
||||
{
|
||||
(void)dest;
|
||||
__asm
|
||||
push ix ; BIOS клобберит IX
|
||||
ex de, hl ; DE = dest (аргумент пришёл в HL)
|
||||
xor a ; шрифт 0 (системный)
|
||||
ld c, #0xB8
|
||||
rst #0x08
|
||||
pop ix
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void gfx_load_default_font(void)
|
||||
{
|
||||
bios_get_zg(_gfx_font_buf);
|
||||
_gfx_font_ptr = _gfx_font_buf;
|
||||
_gfx_font_loaded = 1;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* gfx_pal_get — прочитать блок записей графической палитры;
|
||||
* обёртка над pal_get (libc/video) — не на горячем пути.
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
#include <palette.h>
|
||||
|
||||
void gfx_pal_get(uint8_t pal_num, uint8_t start, uint8_t count,
|
||||
uint8_t *data)
|
||||
{
|
||||
pal_get(pal_num, start, count, data);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* gfx_pal_get_color — прочитать одну запись графической палитры в
|
||||
* R/G/B (любой указатель может быть NULL); обёртка над pal_get_color.
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
#include <palette.h>
|
||||
|
||||
void gfx_pal_get_color(uint8_t pal_num, uint8_t idx,
|
||||
uint8_t *r, uint8_t *g, uint8_t *b)
|
||||
{
|
||||
pal_get_color(pal_num, idx, r, g, b);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* gfx_pal_load — загрузить блок записей графической палитры
|
||||
* (BIOS $A4 PIC_SET_PAL): pal_num 0..3, start — первый слот,
|
||||
* count слотов (0 = 256), data — записи по 4 байта (B, G, R, 0).
|
||||
*
|
||||
* Собственный inline-asm вместо тонкой обёртки над pal_load — тонкая
|
||||
* обёртка через __sdcccall(1)-передачу эмпирически ломала текстовую
|
||||
* палитру (следующая печать в текстовом режиме вешалась).
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
#include <palette.h>
|
||||
|
||||
static uint8_t gpl_num_;
|
||||
static uint8_t gpl_start_;
|
||||
static uint8_t gpl_count_;
|
||||
static uint16_t gpl_data_;
|
||||
|
||||
void gfx_pal_load(uint8_t pal_num, uint8_t start, uint8_t count,
|
||||
const uint8_t *data)
|
||||
{
|
||||
gpl_num_ = pal_num;
|
||||
gpl_start_ = start;
|
||||
gpl_count_ = count;
|
||||
gpl_data_ = (uint16_t)(uintptr_t)data;
|
||||
|
||||
__asm
|
||||
push ix
|
||||
ld a, (_gpl_start_)
|
||||
ld e, a ; E = start
|
||||
ld a, (_gpl_count_)
|
||||
ld d, a ; D = count (0 = 256)
|
||||
ld hl, (_gpl_data_) ; HL = data
|
||||
ld b, #0xFF ; маска
|
||||
ld a, (_gpl_num_) ; A = номер палитры
|
||||
ld c, #0xA4 ; BIOS PIC_SET_PAL
|
||||
rst #0x08
|
||||
pop ix
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* gfx_pal_reset — восстановить системную графическую палитру
|
||||
* (BIOS $A6, type = PAL_GRAPH).
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
#include <palette.h>
|
||||
|
||||
void gfx_pal_reset(void)
|
||||
{
|
||||
pal_reset(PAL_GRAPH);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* gfx_pal_set — одна запись графической палитры из RGB: собирает
|
||||
* четвёрку (B, G, R, 0) и зовёт gfx_pal_load(pal_num, idx, 1, ...).
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
void gfx_pal_set(uint8_t pal_num, uint8_t idx,
|
||||
uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
uint8_t entry[4];
|
||||
entry[0] = b;
|
||||
entry[1] = g;
|
||||
entry[2] = r;
|
||||
entry[3] = 0;
|
||||
gfx_pal_load(pal_num, idx, 1, entry);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* gfx_set_bank — задать байт страницы W3 (0x50..0x5F) для gfx-записей.
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
void gfx_set_bank(uint8_t bank)
|
||||
{
|
||||
_gfx_bank = bank;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* gfx_set_draw_page — выбрать страницу для рисования (0/1); обновляет
|
||||
* _gfx_addr_base, который читают все примитивы.
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
void gfx_set_draw_page(uint8_t page)
|
||||
{
|
||||
_gfx_draw_page = page & 1;
|
||||
/* Прямые константы короче (0xC000 + (cond ? 0x140 : 0)) на 3
|
||||
* инструкции Z80 — SDCC не сворачивает сложение констант. */
|
||||
_gfx_addr_base = _gfx_draw_page ? 0xC140 : 0xC000;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* gfx_set_font — подменить шрифт своим (указатель удерживается —
|
||||
* storage должен жить, пока шрифт активен). Формат см. _gfx.h.
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
void gfx_set_font(const uint8_t *font)
|
||||
{
|
||||
_gfx_font_ptr = font;
|
||||
_gfx_font_loaded = 1;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* gfx_set_visible_page — выбрать видимую страницу (ESTEX $54 SELPAGE).
|
||||
*
|
||||
* Прямой OUT в порт 0xC9 бит 0 переключил бы регистр, но не уведомил
|
||||
* бы display-учёт DSS — экран остаётся в рассогласованном состоянии
|
||||
* (один из свопов показывается чёрным). Через syscall DSS доволен.
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
void gfx_set_visible_page(uint8_t page)
|
||||
{
|
||||
_gfx_visible_page = page & 1;
|
||||
__asm
|
||||
push ix
|
||||
ld a, (__gfx_visible_page)
|
||||
ld b, a ; B = страница 0/1
|
||||
ld c, #0x54 ; ESTEX SELPAGE
|
||||
rst #0x10
|
||||
pop ix
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* gfx_wait_vsync — ждать переход бита 5 порта 0xFE из 1 в 0: выход из
|
||||
* нижнего blank/border (Y > 256 — см. MAME sprinter.cpp kbd_fe_r:
|
||||
* `vpos() >= BORDER_TOP+SCREEN_YSIZE`) — начало нового кадра, идеальный
|
||||
* момент для свопа страниц без разрывов. Это чтение РЕАЛЬНОГО
|
||||
* состояния луча (не событие/прерывание), поэтому не подвержено
|
||||
* путанице с другими источниками вектора 0xFF (клавиатура, CBL, CTC) —
|
||||
* подробнее см. memory/gfx_wait_vsync_redesign.
|
||||
*
|
||||
* Бит доступен ТОЛЬКО пока включён cbl_mode() (bit7 порта 0x004E) —
|
||||
* см. libc/cbl/_cbl_port.c: если приложение уже играет через
|
||||
* cbl_open(), берём бит бесплатно (порт не трогаем); иначе
|
||||
* лениво занимаем bit7 "немым" кодом частоты через _cbl_port_ref()
|
||||
* при первом вызове, освобождаем в atexit.
|
||||
*
|
||||
* Fallback: если ни разу не увидели ожидаемое состояние за разумное
|
||||
* число попыток (иное железо, бит ведёт себя не как в MAME) —
|
||||
* откатываемся на одно кадровое прерывание (halt), чтобы не
|
||||
* зависнуть намертво.
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
#include <sprinter_exit.h>
|
||||
|
||||
static uint8_t vsync_ready;
|
||||
|
||||
static void vsync_cleanup(void)
|
||||
{
|
||||
_cbl_port_unref();
|
||||
}
|
||||
|
||||
void gfx_wait_vsync(void)
|
||||
{
|
||||
if (!vsync_ready) {
|
||||
vsync_ready = 1;
|
||||
_cbl_port_ref();
|
||||
atexit(vsync_cleanup);
|
||||
}
|
||||
|
||||
__asm
|
||||
;; --- фаза 1: дождаться bit5==1 (гарантированно в blank) ---
|
||||
ld bc, #0 ; ~65536 попыток на фазу — таймаут
|
||||
_gwv_wait1:
|
||||
in a, (#0xFE)
|
||||
bit 5, a
|
||||
jr nz, _gwv_have1
|
||||
dec bc
|
||||
ld a, b
|
||||
or c
|
||||
jr nz, _gwv_wait1
|
||||
jr _gwv_fallback ; bit5 ни разу не встал за таймаут
|
||||
_gwv_have1:
|
||||
|
||||
;; --- фаза 2: дождаться bit5==0 (сам переход — начало кадра) ---
|
||||
ld bc, #0
|
||||
_gwv_wait0:
|
||||
in a, (#0xFE)
|
||||
bit 5, a
|
||||
jr z, _gwv_done
|
||||
dec bc
|
||||
ld a, b
|
||||
or c
|
||||
jr nz, _gwv_wait0
|
||||
;; таймаут и здесь — переходим к тому же фолбэку
|
||||
|
||||
_gwv_fallback:
|
||||
ei
|
||||
halt ; аварийный путь: одно кадровое прерывание
|
||||
|
||||
_gwv_done:
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/* graphresult — код последней ошибки; вызов сбрасывает его в grOk. */
|
||||
#include "../_bgi.h"
|
||||
|
||||
int graphresult(void)
|
||||
{
|
||||
int r = _bgi_result;
|
||||
_bgi_result = grOk;
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* imagesize — размер буфера под образ (left,top)-(right,bottom) в байтах:
|
||||
* 4 (заголовок w,h) + w*h (по байту на пиксель в режиме 256).
|
||||
*
|
||||
* Результат 16-бит unsigned (как в Turbo-C): образы ≥64 КБ не влезают.
|
||||
* Произведение w*h считаем в unsigned — для валидных (<64 КБ) образов
|
||||
* умещается точно; без 32-бит.
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
unsigned imagesize(int left, int top, int right, int bottom)
|
||||
{
|
||||
int w = right - left + 1;
|
||||
int h = bottom - top + 1;
|
||||
if (w <= 0 || h <= 0) return 0;
|
||||
return (unsigned)4 + (unsigned)w * (unsigned)h;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* initgraph — перейти в графический режим и сбросить состояние BGI.
|
||||
*
|
||||
* Режим определяется driver-библиотекой (для sprinter_gfx256 —
|
||||
* 320×256×256). По умолчанию: цвет WHITE, фон BLACK, позиция (0,0).
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
void initgraph(void)
|
||||
{
|
||||
_bgi_prevmode = _bgi_mode_set();
|
||||
_bgi_fg = WHITE;
|
||||
_bgi_bg = BLACK;
|
||||
_bgi_cx = 0;
|
||||
_bgi_cy = 0;
|
||||
_bgi_result = grOk;
|
||||
_bgi_fill_pattern = SOLID_FILL;
|
||||
_bgi_fill_color = WHITE;
|
||||
_bgi_line_style = SOLID_LINE;
|
||||
_bgi_line_thick = NORM_WIDTH;
|
||||
_bgi_line_pattern = 0xFFFF;
|
||||
_bgi_text_dir = HORIZ_DIR;
|
||||
_bgi_text_size = 1;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/* line — отрезок (x1,y1)->(x2,y2) текущим цветом; CP не меняется. */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void line(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
_bgi_styled_line(x1, y1, x2, y2, _bgi_fg);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/* linerel — линия от CP до CP+(dx,dy); CP сдвигается в конец. */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void linerel(int dx, int dy)
|
||||
{
|
||||
int nx = _bgi_cx + dx;
|
||||
int ny = _bgi_cy + dy;
|
||||
_bgi_styled_line(_bgi_cx, _bgi_cy, nx, ny, _bgi_fg);
|
||||
_bgi_cx = nx;
|
||||
_bgi_cy = ny;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/* lineto — линия от CP до (x,y) текущим цветом; CP := (x,y). */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void lineto(int x, int y)
|
||||
{
|
||||
_bgi_styled_line(_bgi_cx, _bgi_cy, x, y, _bgi_fg);
|
||||
_bgi_cx = x;
|
||||
_bgi_cy = y;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/* moverel — сдвинуть текущую позицию относительно. */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void moverel(int dx, int dy)
|
||||
{
|
||||
_bgi_cx += dx;
|
||||
_bgi_cy += dy;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/* moveto — задать текущую позицию (CP). */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void moveto(int x, int y)
|
||||
{
|
||||
_bgi_cx = x;
|
||||
_bgi_cy = y;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* outtext — строка в текущей позиции (CP) текущим стилем текста; CP
|
||||
* сдвигается на textwidth (гориз.) или вверх на textheight (верт.).
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void outtext(const char *text)
|
||||
{
|
||||
_bgi_draw_scaled(_bgi_cx, _bgi_cy, text);
|
||||
if (_bgi_text_dir == VERT_DIR) _bgi_cy -= textheight(text);
|
||||
else _bgi_cx += textwidth(text);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/* outtextxy — строка в (x,y) текущим стилем текста (масштаб/направление,
|
||||
* прозрачный фон); CP не меняет. */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void outtextxy(int x, int y, const char *text)
|
||||
{
|
||||
_bgi_draw_scaled(x, y, text);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/* pieslice — залитый сектор круга радиуса r (частный случай sector). */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void pieslice(int x, int y, int stangle, int endangle, int radius)
|
||||
{
|
||||
sector(x, y, stangle, endangle, radius, radius);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* putimage — вывести образ bitmap левым-верхним углом в (left,top)
|
||||
* операцией op. Заголовок буфера: uint16 width, uint16 height.
|
||||
*
|
||||
* COPY — пишем src; XOR/OR/AND — комбинируем с экраном (нужно raw-
|
||||
* чтение); NOT — пишем ~src. Всё в одной W3-скобке (raw-плот/чтение).
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void putimage(int left, int top, const void *bitmap, int op)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)bitmap;
|
||||
int w, h, x, y;
|
||||
uint8_t src, dst;
|
||||
|
||||
w = p[0] | (p[1] << 8);
|
||||
h = p[2] | (p[3] << 8);
|
||||
p += 4;
|
||||
if (w <= 0 || h <= 0) return;
|
||||
|
||||
_bgi_begin();
|
||||
for (y = 0; y < h; y++) {
|
||||
for (x = 0; x < w; x++) {
|
||||
src = *p++;
|
||||
switch (op) {
|
||||
case XOR_PUT: dst = (uint8_t)_bgi_read_raw(left + x, top + y) ^ src; break;
|
||||
case OR_PUT: dst = (uint8_t)_bgi_read_raw(left + x, top + y) | src; break;
|
||||
case AND_PUT: dst = (uint8_t)_bgi_read_raw(left + x, top + y) & src; break;
|
||||
case NOT_PUT: dst = (uint8_t)~src; break;
|
||||
case COPY_PUT:
|
||||
default: dst = src; break;
|
||||
}
|
||||
_bgi_plot_raw(left + x, top + y, dst);
|
||||
}
|
||||
}
|
||||
_bgi_end();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/* rectangle — контур прямоугольника по углам (left,top)-(right,bottom). */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void rectangle(int left, int top, int right, int bottom)
|
||||
{
|
||||
uint8_t c = _bgi_fg;
|
||||
_bgi_styled_line(left, top, right, top, c); /* верх */
|
||||
_bgi_styled_line(right, top, right, bottom, c); /* право */
|
||||
_bgi_styled_line(right, bottom, left, bottom, c); /* низ */
|
||||
_bgi_styled_line(left, bottom, left, top, c); /* лево */
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* sector — залитый эллиптический сектор (центр cx,cy, полуоси xr,yr,
|
||||
* от stangle до endangle). Нутро — стилем заливки, контур (дуга + два
|
||||
* радиуса) — текущим цветом.
|
||||
*
|
||||
* Заливка: границу сектора (центр → дуга → центр) прогоняем по
|
||||
* _bgi_poly_edge, получая min/max X по строкам, и заливаем между ними
|
||||
* (как fillpoly). Для секторов ≤180° точно; >180° возможен перелив в
|
||||
* «выемку» (min/max по строке не различает 4 пересечения).
|
||||
*/
|
||||
|
||||
#include "../_bgi.h"
|
||||
|
||||
/* точка на эллипсе под углом a (Q7-тригонометрия). */
|
||||
static int se_x(int cx, int xr, int a) { return cx + ((xr * _bgi_cos128(a)) >> 7); }
|
||||
static int se_y(int cy, int yr, int a) { return cy - ((yr * _bgi_sin128(a)) >> 7); }
|
||||
|
||||
void sector(int cx, int cy, int stangle, int endangle, int xr, int yr)
|
||||
{
|
||||
int a, y, px, py, x, py2;
|
||||
|
||||
if (endangle < stangle) endangle += 360;
|
||||
|
||||
for (y = 0; y <= _bgi_maxy; y++) {
|
||||
_bgi_span_min[y] = 32767;
|
||||
_bgi_span_max[y] = -32768;
|
||||
}
|
||||
|
||||
/* Граница: из центра в первую точку дуги, по дуге, обратно в центр. */
|
||||
px = cx; py = cy;
|
||||
for (a = stangle; a <= endangle; a++) {
|
||||
x = se_x(cx, xr, a);
|
||||
py2 = se_y(cy, yr, a);
|
||||
_bgi_poly_edge(px, py, x, py2);
|
||||
px = x; py = py2;
|
||||
}
|
||||
_bgi_poly_edge(px, py, cx, cy);
|
||||
|
||||
_bgi_begin();
|
||||
for (y = 0; y <= _bgi_maxy; y++)
|
||||
if (_bgi_span_max[y] >= _bgi_span_min[y])
|
||||
_bgi_fill_span(_bgi_span_min[y], _bgi_span_max[y], y);
|
||||
_bgi_end();
|
||||
|
||||
/* Контур: дуга + два радиуса. */
|
||||
_bgi_ellipse_arc(cx, cy, stangle, endangle, xr, yr, _bgi_fg);
|
||||
_bgi_lineseg(cx, cy, se_x(cx, xr, stangle), se_y(cy, yr, stangle), _bgi_fg);
|
||||
_bgi_lineseg(cx, cy, se_x(cx, xr, endangle), se_y(cy, yr, endangle), _bgi_fg);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/* setbkcolor — задать фоновый цвет (cleardevice, фон текста). */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void setbkcolor(int color)
|
||||
{
|
||||
_bgi_bg = (uint8_t)color;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/* setcolor — задать текущий цвет рисования. */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void setcolor(int color)
|
||||
{
|
||||
_bgi_fg = (uint8_t)color;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/* setlinestyle — задать стиль, пользовательскую маску и толщину линий. */
|
||||
#include "../_bgi.h"
|
||||
|
||||
void setlinestyle(int linestyle, unsigned upattern, int thickness)
|
||||
{
|
||||
_bgi_line_style = (uint8_t)linestyle;
|
||||
_bgi_line_pattern = (uint16_t)upattern;
|
||||
_bgi_line_thick = (thickness >= THICK_WIDTH) ? THICK_WIDTH : NORM_WIDTH;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* settextstyle — шрифт (поддержан только DEFAULT_FONT), направление и
|
||||
* масштаб текста. font игнорируется (всегда растровый 8×8).
|
||||
*/
|
||||
#include "../_bgi.h"
|
||||
|
||||
void settextstyle(int font, int direction, int charsize)
|
||||
{
|
||||
(void)font;
|
||||
_bgi_text_dir = (direction == VERT_DIR) ? VERT_DIR : HORIZ_DIR;
|
||||
_bgi_text_size = (charsize < 1) ? 1 : (charsize > 10 ? 10 : (uint8_t)charsize);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/* textheight — высота строки как она будет нарисована (масштаб/направление). */
|
||||
#include "../_bgi.h"
|
||||
#include <string.h>
|
||||
|
||||
int textheight(const char *text)
|
||||
{
|
||||
if (_bgi_text_dir == VERT_DIR) return (int)strlen(text) * 8 * _bgi_text_size;
|
||||
return 8 * _bgi_text_size;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/* textwidth — ширина строки как она будет нарисована (с учётом
|
||||
* масштаба и направления). */
|
||||
#include "../_bgi.h"
|
||||
#include <string.h>
|
||||
|
||||
int textwidth(const char *text)
|
||||
{
|
||||
int n = (int)strlen(text);
|
||||
if (_bgi_text_dir == VERT_DIR) return 8 * _bgi_text_size;
|
||||
return n * 8 * _bgi_text_size;
|
||||
}
|
||||
Reference in New Issue
Block a user