libbgi: vflip-блиты для row-major картинок (шаг I.2 плана L9-INVERT)
gfx_blit_noclip_vflip / gfx_blit_part_noclip_vflip / gfx_blit_part_vflip. Ассемблера не потребовалось: у row-major вертикальное зеркало — это порядок строк, а он задаётся ЗНАКОМ src-страйда, который _bgi_blit_rows_raw и так принимает знаковым и патчит SMC. Даём адрес последней строки и -stride. Клип у part_vflip свой: обрезка сверху экрана съедает НИЖНИЕ строки источника, и первая строка обязана считаться от высоты ДО клипа (поймано тестом — сначала формула брала уже укороченную). tests/pageflip расширен до 8 проверок, все PASS в MAME. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* gfx_blit_noclip_vflip — блит картинки БЕЗ клипа, ЗЕРКАЛЬНО ПО ВЕРТИКАЛИ:
|
||||||
|
* последняя строка источника ложится в верхнюю строку прямоугольника,
|
||||||
|
* первая — в нижнюю. (x,y) — по-прежнему ЛЕВЫЙ ВЕРХНИЙ угол результата,
|
||||||
|
* так что вызывающему не надо менять арифметику позиции.
|
||||||
|
*
|
||||||
|
* Ассемблера здесь нет и не нужно: у row-major картинки вертикальное
|
||||||
|
* зеркало — это порядок строк, а он задаётся ЗНАКОМ src-страйда.
|
||||||
|
* `_bgi_blit_rows_raw` берёт `sstride` знаковым 16-битным и патчит его SMC
|
||||||
|
* в adc-цепочку, поэтому достаточно отдать ему адрес ПОСЛЕДНЕЙ строки и
|
||||||
|
* -stride. Цена кадра ровно та же, что у обычного блита — accel копирует
|
||||||
|
* каждую строку вперёд, назад идёт только выбор строки.
|
||||||
|
*
|
||||||
|
* (Для column-major картинок — gfx_blit_cols* — так НЕ выйдет: там зеркало
|
||||||
|
* по вертикали пришлось бы делать внутри колонки, а accel копирует блок
|
||||||
|
* только вперёд. Бесплатное зеркало у них горизонтальное.)
|
||||||
|
*
|
||||||
|
* Ограничения те же, что у gfx_blit_noclip: w,h ≤ 255, картинка целиком в
|
||||||
|
* пределах экрана, банк ставит вызывающий.
|
||||||
|
*/
|
||||||
|
#include "../_bgi.h"
|
||||||
|
|
||||||
|
void gfx_blit_noclip_vflip(int x, int y, const void *img)
|
||||||
|
{
|
||||||
|
const uint8_t *p = (const uint8_t *)img;
|
||||||
|
uint16_t stride = (uint16_t)(p[0] | (p[1] << 8));
|
||||||
|
uint8_t h = p[2];
|
||||||
|
|
||||||
|
if (!stride || !h) return;
|
||||||
|
|
||||||
|
_bgi_begin();
|
||||||
|
_bgi_blit_rows_raw(p + 4 + (uint16_t)(h - 1) * stride,
|
||||||
|
(uint8_t *)(_gfx_addr_base + x),
|
||||||
|
p[0], h, -(int)stride, (uint8_t)y);
|
||||||
|
_bgi_end();
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* gfx_blit_part_noclip_vflip — под-прямоугольник картинки БЕЗ клипа,
|
||||||
|
* ЗЕРКАЛЬНО ПО ВЕРТИКАЛИ. Пара к gfx_blit_part_noclip; про механику
|
||||||
|
* зеркала (знак src-страйда) — см. gfx_blit_noclip_vflip.
|
||||||
|
*
|
||||||
|
* (sx,sy,w,h) задают кусок ИСТОЧНИКА в обычной системе координат, как у
|
||||||
|
* не-зеркального двойника: sy — верхняя строка куска. На экран этот кусок
|
||||||
|
* ложится перевёрнутым, (x,y) — левый верхний угол результата. Значит
|
||||||
|
* первой рисуется строка sy+h-1 источника.
|
||||||
|
*
|
||||||
|
* Тайловому слою это позволяет не трогать выбор куска атласа вообще:
|
||||||
|
* меняется только вызываемая функция и экранный y (его считает
|
||||||
|
* вызывающий — у него поле, у библиотеки экран).
|
||||||
|
*/
|
||||||
|
#include "../_bgi.h"
|
||||||
|
|
||||||
|
void gfx_blit_part_noclip_vflip(int x, int y, const void *img,
|
||||||
|
uint8_t sx, uint8_t sy, uint8_t w, uint8_t h)
|
||||||
|
{
|
||||||
|
const uint8_t *p = (const uint8_t *)img;
|
||||||
|
uint16_t stride = (uint16_t)(p[0] | (p[1] << 8));
|
||||||
|
const uint8_t *src;
|
||||||
|
|
||||||
|
if (!stride || !w || !h) return;
|
||||||
|
/* Стартуем с ПОСЛЕДНЕЙ строки куска и идём вверх. */
|
||||||
|
src = p + 4 + (uint16_t)(sy + h - 1) * stride + sx;
|
||||||
|
|
||||||
|
_bgi_begin();
|
||||||
|
_bgi_blit_rows_raw(src, (uint8_t *)(_gfx_addr_base + x),
|
||||||
|
w, h, -(int)stride, (uint8_t)y);
|
||||||
|
_bgi_end();
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* gfx_blit_part_vflip — под-прямоугольник картинки С КЛИПОМ по экрану,
|
||||||
|
* ЗЕРКАЛЬНО ПО ВЕРТИКАЛИ. Пара к gfx_blit_part; механика зеркала — см.
|
||||||
|
* gfx_blit_noclip_vflip (знак src-страйда).
|
||||||
|
*
|
||||||
|
* Клип здесь СВОЙ, а не через _gfx_blit_full: общее ядро режет источник в
|
||||||
|
* прямом порядке строк, а у зеркала обрезка сверху экрана съедает НИЖНИЕ
|
||||||
|
* строки источника — считать это внутри общего ядра дороже, чем восемь
|
||||||
|
* строк арифметики здесь (функция и так вызывается только у края экрана,
|
||||||
|
* в тайловом слое основной путь — noclip-двойники).
|
||||||
|
*
|
||||||
|
* Соответствие строк: экранная (y + i) <- источник (sy + h - 1 - i).
|
||||||
|
* Значит обрезка сверху (y < 0) сдвигает старт по i, а обрезка снизу
|
||||||
|
* просто укорачивает h.
|
||||||
|
*/
|
||||||
|
#include "../include/gfx.h"
|
||||||
|
#include "../_bgi.h"
|
||||||
|
|
||||||
|
void gfx_blit_part_vflip(int x, int y, const void *img,
|
||||||
|
int sx, int sy, int w, int h)
|
||||||
|
{
|
||||||
|
const uint8_t *p = (const uint8_t *)img;
|
||||||
|
uint16_t stride = (uint16_t)(p[0] | (p[1] << 8));
|
||||||
|
int skip;
|
||||||
|
int h0 = h; /* высота ДО клипа — от неё считается первая
|
||||||
|
* строка источника (h ниже укорачивается) */
|
||||||
|
|
||||||
|
if (!stride || w <= 0 || h <= 0) return;
|
||||||
|
|
||||||
|
/* --- клип по X: источник сдвигается вместе с экраном --- */
|
||||||
|
if (x < 0) { sx -= x; w += x; x = 0; }
|
||||||
|
if (x + w > GFX_WIDTH) w = GFX_WIDTH - x;
|
||||||
|
if (w <= 0) return;
|
||||||
|
|
||||||
|
/* --- клип по Y: сверху съедаются НИЖНИЕ строки источника --- */
|
||||||
|
if (y < 0) { skip = -y; h -= skip; y = 0; } else { skip = 0; }
|
||||||
|
if (y + h > GFX_HEIGHT) h = GFX_HEIGHT - y;
|
||||||
|
if (h <= 0) return;
|
||||||
|
|
||||||
|
/* Полосы шире одного accel-блока режет вызывающий у noclip-двойников;
|
||||||
|
* здесь ширина приходит от тайла (≤ 64), поэтому хватает одной. */
|
||||||
|
if (w > 255) w = 255;
|
||||||
|
|
||||||
|
_bgi_begin();
|
||||||
|
/* Экранная строка (y+i) <- источник (sy + h0 - 1 - i). Обрезка сверху
|
||||||
|
* сдвигает старт на skip строк ВНИЗ по источнику, обрезка снизу только
|
||||||
|
* укорачивает h и первой строки не касается. */
|
||||||
|
_bgi_blit_rows_raw(p + 4 + (uint16_t)(sy + h0 - 1 - skip) * stride + (uint16_t)sx,
|
||||||
|
(uint8_t *)(_gfx_addr_base + x),
|
||||||
|
(uint8_t)w, (uint8_t)h, -(int)stride, (uint8_t)y);
|
||||||
|
_bgi_end();
|
||||||
|
}
|
||||||
@@ -99,6 +99,21 @@ void gfx_blit_noclip(int x, int y, const void *img);
|
|||||||
* 16-бит, а не на пиксели: замер — 11 658 тактов на heal 22×22). */
|
* 16-бит, а не на пиксели: замер — 11 658 тактов на heal 22×22). */
|
||||||
void gfx_heal_noclip(int x, int y, uint8_t w, uint8_t h);
|
void gfx_heal_noclip(int x, int y, uint8_t w, uint8_t h);
|
||||||
|
|
||||||
|
/* ЗЕРКАЛО ПО ВЕРТИКАЛИ (row-major картинки): последняя строка источника
|
||||||
|
* ложится в верхнюю строку прямоугольника. (x,y) — по-прежнему левый
|
||||||
|
* ВЕРХНИЙ угол результата, поэтому вызывающему не надо менять арифметику
|
||||||
|
* позиции — только выбрать другую функцию. Стоят ровно столько же, сколько
|
||||||
|
* не-зеркальные двойники: у row-major вертикальное зеркало это порядок
|
||||||
|
* строк, а он задаётся знаком src-страйда (accel копирует строку вперёд).
|
||||||
|
* Для column-major (gfx_blit_cols*) такого варианта НЕТ и быть не может —
|
||||||
|
* там пришлось бы реверсить внутри колонки; бесплатное зеркало у них
|
||||||
|
* горизонтальное (флаг flip). */
|
||||||
|
void gfx_blit_noclip_vflip(int x, int y, const void *img);
|
||||||
|
void gfx_blit_part_noclip_vflip(int x, int y, const void *img,
|
||||||
|
uint8_t sx, uint8_t sy, uint8_t w, uint8_t h);
|
||||||
|
void gfx_blit_part_vflip(int x, int y, const void *img,
|
||||||
|
int sx, int sy, int w, int h);
|
||||||
|
|
||||||
/* Блит под-прямоугольника (sx,sy,w,h) картинки (атлас кадров:
|
/* Блит под-прямоугольника (sx,sy,w,h) картинки (атлас кадров:
|
||||||
* кадр N ленты — sx = N*FRAME_W). Под-прямоугольник обязан лежать
|
* кадр N ленты — sx = N*FRAME_W). Под-прямоугольник обязан лежать
|
||||||
* внутри img; по экрану — клиппится. */
|
* внутри img; по экрану — клиппится. */
|
||||||
|
|||||||
@@ -48,12 +48,44 @@
|
|||||||
#define WW 320
|
#define WW 320
|
||||||
#define WH 16
|
#define WH 16
|
||||||
|
|
||||||
|
#define IW 16 /* картинка в памяти: ширина */
|
||||||
|
#define IH 8 /* высота */
|
||||||
|
#define IX 8 /* куда блитим (без клипа) */
|
||||||
|
#define IY 216
|
||||||
|
#define CX 8 /* клипающий вариант: частью за верхом экрана */
|
||||||
|
#define CY -3
|
||||||
|
|
||||||
#define ROW_C0 0x41 /* цвет строки i = ROW_C0 + i */
|
#define ROW_C0 0x41 /* цвет строки i = ROW_C0 + i */
|
||||||
#define MARK_L 0x21 /* маркер левого края строки */
|
#define MARK_L 0x21 /* маркер левого края строки */
|
||||||
#define MARK_R 0x22 /* маркер правого края */
|
#define MARK_R 0x22 /* маркер правого края */
|
||||||
#define FRAME 0x2F /* рамка вокруг области */
|
#define FRAME 0x2F /* рамка вокруг области */
|
||||||
#define DIRT 0x35 /* чем забиваем приёмник до копии */
|
#define DIRT 0x35 /* чем забиваем приёмник до копии */
|
||||||
|
|
||||||
|
/* Картинка getimage-формата: w,h (16 бит) + пиксели row-major.
|
||||||
|
* Значение = 0x40 + row*IW + col: и строки, и колонки различимы. */
|
||||||
|
static uint8_t img[4 + IW * IH];
|
||||||
|
|
||||||
|
static void make_img(void)
|
||||||
|
{
|
||||||
|
int col, row;
|
||||||
|
img[0] = IW; img[1] = 0; img[2] = IH; img[3] = 0;
|
||||||
|
for (row = 0; row < IH; row++)
|
||||||
|
for (col = 0; col < IW; col++)
|
||||||
|
img[4 + row * IW + col] = (uint8_t)(0x40 + row * IW + col);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Строка row картинки обязана лежать в экранной строке (IH-1-row). */
|
||||||
|
static uint8_t img_vflip_ok(int x0, int y0, int sx, int sy, int w, int h)
|
||||||
|
{
|
||||||
|
int col, row;
|
||||||
|
for (row = 0; row < h; row++)
|
||||||
|
for (col = 0; col < w; col++) {
|
||||||
|
uint8_t want = (uint8_t)(0x40 + (sy + row) * IW + sx + col);
|
||||||
|
if ((uint8_t)getpixel(x0 + col, y0 + h - 1 - row) != want) return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Нарисовать образец в ТЕКУЩЕЙ draw-странице: каждая строка своим цветом,
|
/* Нарисовать образец в ТЕКУЩЕЙ draw-странице: каждая строка своим цветом,
|
||||||
* по краям маркеры. */
|
* по краям маркеры. */
|
||||||
static void draw_pattern(int x0, int y0, int w, int h)
|
static void draw_pattern(int x0, int y0, int w, int h)
|
||||||
@@ -129,7 +161,7 @@ static void say(int y, const char *s, uint8_t ok)
|
|||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
gfx_rect_t area;
|
gfx_rect_t area;
|
||||||
uint8_t t[5];
|
uint8_t t[8];
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
|
|
||||||
initgraph();
|
initgraph();
|
||||||
@@ -168,6 +200,31 @@ void main(void)
|
|||||||
gfx_copy_page(&area, GFX_COPY_VFLIP);
|
gfx_copy_page(&area, GFX_COPY_VFLIP);
|
||||||
t[3] = check_vflip(WX, WY, WW, WH);
|
t[3] = check_vflip(WX, WY, WW, WH);
|
||||||
|
|
||||||
|
/* T6-T8: vflip-блиты row-major картинки (ядро то же, знак страйда). */
|
||||||
|
make_img();
|
||||||
|
setfillstyle(SOLID_FILL, BLACK);
|
||||||
|
bar(0, IY - 1, 200, IY + IH);
|
||||||
|
gfx_blit_noclip_vflip(IX, IY, img);
|
||||||
|
t[5] = img_vflip_ok(IX, IY, 0, 0, IW, IH);
|
||||||
|
|
||||||
|
gfx_blit_part_noclip_vflip(IX + 40, IY, img, 4, 2, 8, 4);
|
||||||
|
t[6] = img_vflip_ok(IX + 40, IY, 4, 2, 8, 4);
|
||||||
|
|
||||||
|
/* Клипающий вариант: три верхние экранные строки за краем — значит
|
||||||
|
* съедены ТРИ НИЖНИЕ строки картинки (последние по порядку вывода). */
|
||||||
|
gfx_blit_part_vflip(CX + 90, CY, img, 0, 0, IW, IH);
|
||||||
|
/* y = -3 => первые три вывода срезаны, экранная строка 0 получает
|
||||||
|
* источник IH-1-3 = 4, дальше вверх по источнику: строка row <- (4-row). */
|
||||||
|
t[7] = 1;
|
||||||
|
{
|
||||||
|
int col, row;
|
||||||
|
for (row = 0; row < IH - 3; row++)
|
||||||
|
for (col = 0; col < IW; col++) {
|
||||||
|
uint8_t want = (uint8_t)(0x40 + (IH - 1 - 3 - row) * IW + col);
|
||||||
|
if ((uint8_t)getpixel(CX + 90 + col, row) != want) { t[7] = 0; break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* T5: источник обязан остаться нетронутым. */
|
/* T5: источник обязан остаться нетронутым. */
|
||||||
gfx_set_draw_page(1);
|
gfx_set_draw_page(1);
|
||||||
t[4] = check_direct(AX, AY, AW, AH);
|
t[4] = check_direct(AX, AY, AW, AH);
|
||||||
@@ -180,12 +237,14 @@ void main(void)
|
|||||||
say(174, "T3 frame intact", t[2]);
|
say(174, "T3 frame intact", t[2]);
|
||||||
say(186, "T4 vflip 320 wide", t[3]);
|
say(186, "T4 vflip 320 wide", t[3]);
|
||||||
say(198, "T5 source intact", t[4]);
|
say(198, "T5 source intact", t[4]);
|
||||||
|
say(210, "T6 blit vflip", t[5]);
|
||||||
|
say(222, "T7 part vflip", t[6]);
|
||||||
|
say(234, "T8 part vflip clip", t[7]);
|
||||||
|
|
||||||
for (i = 0; i < 5; i++)
|
for (i = 0; i < 8; i++)
|
||||||
if (!t[i]) break;
|
if (!t[i]) break;
|
||||||
setcolor(WHITE);
|
setcolor(WHITE);
|
||||||
outtextxy(8, 216, i == 5 ? (char *)"ALL PASS" : (char *)"SOME FAILED");
|
outtextxy(8, 246, i == 8 ? (char *)"ALL PASS" : (char *)"SOME FAILED");
|
||||||
outtextxy(8, 228, "done");
|
|
||||||
|
|
||||||
for (;;) { }
|
for (;;) { }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user