diff --git a/libbgi/common/gfx_blit_noclip_vflip.c b/libbgi/common/gfx_blit_noclip_vflip.c new file mode 100644 index 0000000..f5bd684 --- /dev/null +++ b/libbgi/common/gfx_blit_noclip_vflip.c @@ -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(); +} diff --git a/libbgi/common/gfx_blit_part_noclip_vflip.c b/libbgi/common/gfx_blit_part_noclip_vflip.c new file mode 100644 index 0000000..f9cdd11 --- /dev/null +++ b/libbgi/common/gfx_blit_part_noclip_vflip.c @@ -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(); +} diff --git a/libbgi/common/gfx_blit_part_vflip.c b/libbgi/common/gfx_blit_part_vflip.c new file mode 100644 index 0000000..cb5f3f3 --- /dev/null +++ b/libbgi/common/gfx_blit_part_vflip.c @@ -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(); +} diff --git a/libbgi/include/gfx.h b/libbgi/include/gfx.h index 2356c41..5989240 100644 --- a/libbgi/include/gfx.h +++ b/libbgi/include/gfx.h @@ -99,6 +99,21 @@ void gfx_blit_noclip(int x, int y, const void *img); * 16-бит, а не на пиксели: замер — 11 658 тактов на heal 22×22). */ 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) картинки (атлас кадров: * кадр N ленты — sx = N*FRAME_W). Под-прямоугольник обязан лежать * внутри img; по экрану — клиппится. */ diff --git a/tests/pageflip/pageflip.c b/tests/pageflip/pageflip.c index 3f9c088..e266914 100644 --- a/tests/pageflip/pageflip.c +++ b/tests/pageflip/pageflip.c @@ -48,12 +48,44 @@ #define WW 320 #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 MARK_L 0x21 /* маркер левого края строки */ #define MARK_R 0x22 /* маркер правого края */ #define FRAME 0x2F /* рамка вокруг области */ #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-странице: каждая строка своим цветом, * по краям маркеры. */ 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) { gfx_rect_t area; - uint8_t t[5]; + uint8_t t[8]; uint8_t i; initgraph(); @@ -168,6 +200,31 @@ void main(void) gfx_copy_page(&area, GFX_COPY_VFLIP); 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: источник обязан остаться нетронутым. */ gfx_set_draw_page(1); t[4] = check_direct(AX, AY, AW, AH); @@ -180,12 +237,14 @@ void main(void) say(174, "T3 frame intact", t[2]); say(186, "T4 vflip 320 wide", t[3]); 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; setcolor(WHITE); - outtextxy(8, 216, i == 5 ? (char *)"ALL PASS" : (char *)"SOME FAILED"); - outtextxy(8, 228, "done"); + outtextxy(8, 246, i == 8 ? (char *)"ALL PASS" : (char *)"SOME FAILED"); for (;;) { } }