gfx_scroll_v: колоночный accel-проход без буфера-посредника + тест scroll
Вертикальный скролл перестал ходить через строку-буфер на стеке: колонку читаем с Port_Y=ys, пишем с Port_Y=yd, а STOP между read- и write-триггером делает промежуточный OUT Port_Y безопасным. Один проход вместо grab→blit-через-буфер. Справочник libc приведён в соответствие (там же строка про новую точку входа cbl_open_silence). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -262,9 +262,9 @@ tests/bgi_img.
|
|||||||
Скролл региона из НЕактивной страницы в активную (<sprite.h>):
|
Скролл региона из НЕактивной страницы в активную (<sprite.h>):
|
||||||
`gfx_scroll_h(area, dx, dirty)` — горизонтальный (dx>0 = вправо; быстрый
|
`gfx_scroll_h(area, dx, dirty)` — горизонтальный (dx>0 = вправо; быстрый
|
||||||
построчный accel-скролл без страйдов, DI-банды по 16 строк),
|
построчный accel-скролл без страйдов, DI-банды по 16 строк),
|
||||||
`gfx_scroll_v(area, dy, dirty)` — вертикальный (dy>0 = вниз; через
|
`gfx_scroll_v(area, dy, dirty)` — вертикальный (dy>0 = вниз; прямой
|
||||||
строку-буфер на стеке, ПОКА не оптимален — Port_Y один на burst, разный
|
колоночный accel-проход: STOP между чтением и записью позволяет безопасно
|
||||||
read/write Y невозможен без ломающего accel OUT). Копия = скролл + heal
|
сменить Port_Y, промежуточный RAM-буфер не нужен). Копия = скролл + heal
|
||||||
цели (банк 0x50). Открывшуюся полосу |d| не заполняют — возвращают в
|
цели (банк 0x50). Открывшуюся полосу |d| не заполняют — возвращают в
|
||||||
*dirty (NULL = не нужно). Пример: examples/scroll.
|
*dirty (NULL = не нужно). Пример: examples/scroll.
|
||||||
Стиль линий (Ф2d): `setlinestyle(style,upattern,thick)`/`getlinesettings`
|
Стиль линий (Ф2d): `setlinestyle(style,upattern,thick)`/`getlinesettings`
|
||||||
|
|||||||
@@ -27,18 +27,73 @@
|
|||||||
#include "../_bgi.h"
|
#include "../_bgi.h"
|
||||||
|
|
||||||
void gfx_scroll_h(const gfx_rect_t *area, int16_t dx, gfx_rect_t *dirty) {
|
void gfx_scroll_h(const gfx_rect_t *area, int16_t dx, gfx_rect_t *dirty) {
|
||||||
|
#ifdef GFX_NOCHECK
|
||||||
uint8_t saved = gfx_get_bank();
|
uint8_t saved = gfx_get_bank();
|
||||||
uint16_t adx = (dx < 0) ? (uint16_t)(-dx) : (uint16_t)dx;
|
uint16_t adx = (dx < 0) ? (uint16_t)(-dx) : (uint16_t)dx;
|
||||||
/* src-строка стартует правее на |dx| при сдвиге влево, dst — правее
|
uint16_t src_x = area->x + ((dx < 0) ? adx : 0);
|
||||||
* на dx при сдвиге вправо; так оба края остаются внутри area. */
|
uint16_t dst_x = area->x + ((dx > 0) ? adx : 0);
|
||||||
uint16_t src_x = area->x + ((dx < 0) ? adx : 0);
|
uint16_t w = area->w - adx;
|
||||||
uint16_t dst_x = area->x + ((dx > 0) ? adx : 0);
|
uint8_t h = (uint8_t)area->h;
|
||||||
uint16_t w = area->w - adx; /* байт (пикселей) в строке */
|
|
||||||
uint8_t h = (uint8_t)area->h; /* строк (256 => 0 = 256 у leaf) */
|
|
||||||
uint16_t src = _gfx_addr_shadow_base + src_x;
|
uint16_t src = _gfx_addr_shadow_base + src_x;
|
||||||
uint16_t dst = _gfx_addr_base + dst_x;
|
uint16_t dst = _gfx_addr_base + dst_x;
|
||||||
|
|
||||||
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
|
_bgi_begin();
|
||||||
|
while (w) {
|
||||||
|
uint8_t cw = (w > 255) ? 255 : (uint8_t)w;
|
||||||
|
_bgi_scroll_rows_raw(src, dst, cw, h, (uint8_t)area->y);
|
||||||
|
src += cw; dst += cw; w -= cw;
|
||||||
|
}
|
||||||
|
_bgi_end();
|
||||||
|
gfx_set_bank(saved);
|
||||||
|
|
||||||
|
if (dirty) {
|
||||||
|
dirty->x = (dx >= 0) ? area->x : (int16_t)(area->x + area->w - adx);
|
||||||
|
dirty->y = area->y; dirty->w = adx; dirty->h = area->h;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int16_t x, y;
|
||||||
|
uint16_t aw, ah, cut, adx;
|
||||||
|
uint8_t saved, h;
|
||||||
|
uint16_t src_x, dst_x, w, src, dst;
|
||||||
|
|
||||||
|
if (!area) return;
|
||||||
|
x = area->x; y = area->y; aw = area->w; ah = area->h;
|
||||||
|
if (!aw || !ah) goto empty;
|
||||||
|
|
||||||
|
/* Raw-leaf не клипит. Нормализуем область до экрана и не допускаем
|
||||||
|
* 16-битного underflow в area->w-|dx|. */
|
||||||
|
if (x < 0) {
|
||||||
|
cut = (uint16_t)(-x);
|
||||||
|
if (cut >= aw) goto empty;
|
||||||
|
aw -= cut; x = 0;
|
||||||
|
}
|
||||||
|
if (y < 0) {
|
||||||
|
cut = (uint16_t)(-y);
|
||||||
|
if (cut >= ah) goto empty;
|
||||||
|
ah -= cut; y = 0;
|
||||||
|
}
|
||||||
|
if (x >= GFX_WIDTH || y >= GFX_HEIGHT) goto empty;
|
||||||
|
if (aw > (uint16_t)(GFX_WIDTH - x)) aw = (uint16_t)(GFX_WIDTH - x);
|
||||||
|
if (ah > (uint16_t)(GFX_HEIGHT - y)) ah = (uint16_t)(GFX_HEIGHT - y);
|
||||||
|
|
||||||
|
/* Сначала сравнение со знаком: так не вычисляем -INT16_MIN. */
|
||||||
|
if (dx >= (int16_t)aw || dx <= -(int16_t)aw) {
|
||||||
|
if (dirty) { dirty->x=x; dirty->y=y; dirty->w=aw; dirty->h=ah; }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
adx = (dx < 0) ? (uint16_t)(-dx) : (uint16_t)dx;
|
||||||
|
saved = gfx_get_bank();
|
||||||
|
/* src-строка стартует правее на |dx| при сдвиге влево, dst — правее
|
||||||
|
* на dx при сдвиге вправо; так оба края остаются внутри area. */
|
||||||
|
src_x = (uint16_t)x + ((dx < 0) ? adx : 0);
|
||||||
|
dst_x = (uint16_t)x + ((dx > 0) ? adx : 0);
|
||||||
|
w = aw - adx;
|
||||||
|
h = (uint8_t)ah; /* 256 => 0 у leaf */
|
||||||
|
src = _gfx_addr_shadow_base + src_x;
|
||||||
|
dst = _gfx_addr_base + dst_x;
|
||||||
|
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
_bgi_begin();
|
_bgi_begin();
|
||||||
|
|
||||||
@@ -54,7 +109,7 @@ void gfx_scroll_h(const gfx_rect_t *area, int16_t dx, gfx_rect_t *dirty) {
|
|||||||
// src += 256; dst += 256;
|
// src += 256; dst += 256;
|
||||||
// w -= (w > 256) ? 256 : w;
|
// w -= (w > 256) ? 256 : w;
|
||||||
uint8_t cw = (w > 255) ? 255 : (uint8_t)w; /* 0 => 256 */
|
uint8_t cw = (w > 255) ? 255 : (uint8_t)w; /* 0 => 256 */
|
||||||
_bgi_scroll_rows_raw(src, dst, cw, h, (uint8_t)area->y);
|
_bgi_scroll_rows_raw(src, dst, cw, h, (uint8_t)y);
|
||||||
src += cw; dst += cw; w -= cw;
|
src += cw; dst += cw; w -= cw;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,9 +117,14 @@ void gfx_scroll_h(const gfx_rect_t *area, int16_t dx, gfx_rect_t *dirty) {
|
|||||||
gfx_set_bank(saved);
|
gfx_set_bank(saved);
|
||||||
|
|
||||||
if (dirty) {
|
if (dirty) {
|
||||||
dirty->x = (dx >= 0) ? area->x : (int16_t)(area->x + area->w - adx);
|
dirty->x = (dx >= 0) ? x : (int16_t)(x + aw - adx);
|
||||||
dirty->y = area->y;
|
dirty->y = y;
|
||||||
dirty->w = adx;
|
dirty->w = adx;
|
||||||
dirty->h = area->h;
|
dirty->h = ah;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
empty:
|
||||||
|
if (dirty) { dirty->x=0; dirty->y=0; dirty->w=0; dirty->h=0; }
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,20 +24,72 @@
|
|||||||
#include "../_bgi.h"
|
#include "../_bgi.h"
|
||||||
|
|
||||||
void gfx_scroll_v(const gfx_rect_t *area, int16_t dy, gfx_rect_t *dirty) {
|
void gfx_scroll_v(const gfx_rect_t *area, int16_t dy, gfx_rect_t *dirty) {
|
||||||
|
#ifdef GFX_NOCHECK
|
||||||
|
uint8_t saved = gfx_get_bank();
|
||||||
|
uint16_t ady = (dy < 0) ? (uint16_t)(-dy) : (uint16_t)dy;
|
||||||
|
uint8_t ys = (uint8_t)(area->y + ((dy < 0) ? ady : 0));
|
||||||
|
uint8_t yd = (uint8_t)(area->y + ((dy > 0) ? ady : 0));
|
||||||
|
uint16_t hb = area->h - ady;
|
||||||
|
|
||||||
uint8_t saved = gfx_get_bank();
|
if (hb) {
|
||||||
uint16_t ady = (dy < 0) ? (uint16_t)(-dy) : (uint16_t)dy;
|
uint8_t hblk = (uint8_t)hb;
|
||||||
|
uint16_t w = area->w;
|
||||||
|
uint16_t src = _gfx_addr_shadow_base + area->x;
|
||||||
|
uint16_t dst = _gfx_addr_base + area->x;
|
||||||
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
|
_bgi_begin();
|
||||||
|
while (w) {
|
||||||
|
uint8_t cw = (w > 255) ? 255 : (uint8_t)w;
|
||||||
|
_bgi_scroll_cols_raw(src, dst, cw, hblk, ys, yd);
|
||||||
|
src += cw; dst += cw; w -= cw;
|
||||||
|
}
|
||||||
|
_bgi_end();
|
||||||
|
gfx_set_bank(saved);
|
||||||
|
}
|
||||||
|
if (dirty) {
|
||||||
|
dirty->x = area->x;
|
||||||
|
dirty->y = (dy >= 0) ? area->y : (int16_t)(area->y + area->h - ady);
|
||||||
|
dirty->w = area->w; dirty->h = ady;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int16_t x, y;
|
||||||
|
uint16_t aw, ah, cut, ady, hb;
|
||||||
|
uint8_t saved, ys, yd;
|
||||||
|
|
||||||
|
if (!area) return;
|
||||||
|
x = area->x; y = area->y; aw = area->w; ah = area->h;
|
||||||
|
if (!aw || !ah) goto empty;
|
||||||
|
if (x < 0) {
|
||||||
|
cut = (uint16_t)(-x);
|
||||||
|
if (cut >= aw) goto empty;
|
||||||
|
aw -= cut; x = 0;
|
||||||
|
}
|
||||||
|
if (y < 0) {
|
||||||
|
cut = (uint16_t)(-y);
|
||||||
|
if (cut >= ah) goto empty;
|
||||||
|
ah -= cut; y = 0;
|
||||||
|
}
|
||||||
|
if (x >= GFX_WIDTH || y >= GFX_HEIGHT) goto empty;
|
||||||
|
if (aw > (uint16_t)(GFX_WIDTH - x)) aw = (uint16_t)(GFX_WIDTH - x);
|
||||||
|
if (ah > (uint16_t)(GFX_HEIGHT - y)) ah = (uint16_t)(GFX_HEIGHT - y);
|
||||||
|
if (dy >= (int16_t)ah || dy <= -(int16_t)ah) {
|
||||||
|
if (dirty) { dirty->x=x; dirty->y=y; dirty->w=aw; dirty->h=ah; }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ady = (dy < 0) ? (uint16_t)(-dy) : (uint16_t)dy;
|
||||||
|
saved = gfx_get_bank();
|
||||||
/* Строку чтения (ys) сдвигаем вниз на |dy| при сдвиге вверх, строку
|
/* Строку чтения (ys) сдвигаем вниз на |dy| при сдвиге вверх, строку
|
||||||
* записи (yd) — вниз на dy при сдвиге вниз; края в пределах area. */
|
* записи (yd) — вниз на dy при сдвиге вниз; края в пределах area. */
|
||||||
uint8_t ys = (uint8_t)(area->y + ((dy < 0) ? ady : 0));
|
ys = (uint8_t)(y + ((dy < 0) ? ady : 0));
|
||||||
uint8_t yd = (uint8_t)(area->y + ((dy > 0) ? ady : 0));
|
yd = (uint8_t)(y + ((dy > 0) ? ady : 0));
|
||||||
uint16_t hb = area->h - ady; /* строк к переносу (≤256) */
|
hb = ah - ady;
|
||||||
|
|
||||||
if (hb) { /* 0 = переносить нечего */
|
if (hb) { /* 0 = переносить нечего */
|
||||||
uint8_t hblk = (uint8_t)hb; /* размер accel-блока: 256 => 0 */
|
uint8_t hblk = (uint8_t)hb; /* размер accel-блока: 256 => 0 */
|
||||||
uint16_t w = area->w;
|
uint16_t w = aw;
|
||||||
uint16_t src = _gfx_addr_shadow_base + area->x;
|
uint16_t src = _gfx_addr_shadow_base + (uint16_t)x;
|
||||||
uint16_t dst = _gfx_addr_base + area->x;
|
uint16_t dst = _gfx_addr_base + (uint16_t)x;
|
||||||
|
|
||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
_bgi_begin();
|
_bgi_begin();
|
||||||
@@ -55,9 +107,14 @@ void gfx_scroll_v(const gfx_rect_t *area, int16_t dy, gfx_rect_t *dirty) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dirty) {
|
if (dirty) {
|
||||||
dirty->x = area->x;
|
dirty->x = x;
|
||||||
dirty->y = (dy >= 0) ? area->y : (int16_t)(area->y + area->h - ady);
|
dirty->y = (dy >= 0) ? y : (int16_t)(y + ah - ady);
|
||||||
dirty->w = area->w;
|
dirty->w = aw;
|
||||||
dirty->h = ady;
|
dirty->h = ady;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
empty:
|
||||||
|
if (dirty) { dirty->x=0; dirty->y=0; dirty->w=0; dirty->h=0; }
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
|
EXAMPLE := scroll
|
||||||
|
EXTRA_FLAGS ?= --gfx 256 --safe
|
||||||
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
/* Побайтовый регресс gfx_scroll_h/gfx_scroll_v.
|
||||||
|
* Источник — неактивная page 1, приёмник — draw page 0. */
|
||||||
|
#include <graphics.h>
|
||||||
|
#include <gfx.h>
|
||||||
|
#include <sprite.h>
|
||||||
|
|
||||||
|
#define AX 10
|
||||||
|
#define AY 24
|
||||||
|
#define AW 300 /* проверяет разбиение ширины 255+45 */
|
||||||
|
#define AH 160
|
||||||
|
#define DIRT 0x31
|
||||||
|
|
||||||
|
static uint8_t pattern(int x, int y)
|
||||||
|
{
|
||||||
|
return (uint8_t)(0x40 + ((x * 3 + y * 5) & 0x3f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_source(void)
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
for (y = AY; y < AY + AH; y++)
|
||||||
|
for (x = AX; x < AX + AW; x++)
|
||||||
|
putpixel(x, y, pattern(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clear_target(void)
|
||||||
|
{
|
||||||
|
setfillstyle(SOLID_FILL, DIRT);
|
||||||
|
bar(0, 0, 319, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t rect_eq(const gfx_rect_t *r, int x, int y, int w, int h)
|
||||||
|
{
|
||||||
|
return r->x == x && r->y == y && r->w == w && r->h == h;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t check_h(int dx)
|
||||||
|
{
|
||||||
|
gfx_rect_t a = { AX, AY, AW, AH }, d;
|
||||||
|
int x, y, sx;
|
||||||
|
|
||||||
|
clear_target();
|
||||||
|
gfx_scroll_h(&a, dx, &d);
|
||||||
|
if (dx > 0) {
|
||||||
|
if (!rect_eq(&d, AX, AY, dx, AH)) return 0;
|
||||||
|
} else if (dx < 0) {
|
||||||
|
if (!rect_eq(&d, AX + AW + dx, AY, -dx, AH)) return 0;
|
||||||
|
} else if (!rect_eq(&d, AX, AY, 0, AH)) return 0;
|
||||||
|
|
||||||
|
for (y = AY; y < AY + AH; y++)
|
||||||
|
for (x = AX; x < AX + AW; x++) {
|
||||||
|
sx = x - dx;
|
||||||
|
if (sx >= AX && sx < AX + AW) {
|
||||||
|
if ((uint8_t)getpixel(x, y) != pattern(sx, y)) return 0;
|
||||||
|
} else if ((uint8_t)getpixel(x, y) != DIRT) return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t check_v(int dy)
|
||||||
|
{
|
||||||
|
gfx_rect_t a = { AX, AY, AW, AH }, d;
|
||||||
|
int x, y, sy;
|
||||||
|
|
||||||
|
clear_target();
|
||||||
|
gfx_scroll_v(&a, dy, &d);
|
||||||
|
if (dy > 0) {
|
||||||
|
if (!rect_eq(&d, AX, AY, AW, dy)) return 0;
|
||||||
|
} else if (!rect_eq(&d, AX, AY + AH + dy, AW, -dy)) return 0;
|
||||||
|
|
||||||
|
for (y = AY; y < AY + AH; y++)
|
||||||
|
for (x = AX; x < AX + AW; x++) {
|
||||||
|
sy = y - dy;
|
||||||
|
if (sy >= AY && sy < AY + AH) {
|
||||||
|
if ((uint8_t)getpixel(x, y) != pattern(x, sy)) return 0;
|
||||||
|
} else if ((uint8_t)getpixel(x, y) != DIRT) return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t check_limits(void)
|
||||||
|
{
|
||||||
|
gfx_rect_t a = { -5, -7, 25, 27 }, d;
|
||||||
|
gfx_scroll_h(&a, 40, &d);
|
||||||
|
if (!rect_eq(&d, 0, 0, 20, 20)) return 0;
|
||||||
|
gfx_scroll_v(&a, -40, &d);
|
||||||
|
return rect_eq(&d, 0, 0, 20, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t check_source(void)
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
gfx_set_draw_page(1);
|
||||||
|
for (y = AY; y < AY + AH; y++)
|
||||||
|
for (x = AX; x < AX + AW; x++)
|
||||||
|
if ((uint8_t)getpixel(x, y) != pattern(x, y)) {
|
||||||
|
gfx_set_draw_page(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
gfx_set_draw_page(0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void say(int y, const char *name, uint8_t ok)
|
||||||
|
{
|
||||||
|
setcolor(WHITE); outtextxy(8, y, (char *)name);
|
||||||
|
setcolor(ok ? GREEN : RED); outtextxy(232, y, ok ? (char *)"PASS" : (char *)"FAIL");
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
uint8_t t[7], i;
|
||||||
|
|
||||||
|
initgraph();
|
||||||
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
|
gfx_set_draw_page(1); draw_source();
|
||||||
|
gfx_set_draw_page(0);
|
||||||
|
|
||||||
|
t[0] = check_h(8);
|
||||||
|
t[1] = check_h(-13);
|
||||||
|
t[2] = check_v(9);
|
||||||
|
t[3] = check_v(-11);
|
||||||
|
t[4] = check_limits();
|
||||||
|
t[5] = check_h(0);
|
||||||
|
t[6] = check_source();
|
||||||
|
|
||||||
|
clear_target();
|
||||||
|
say(40, "H +8, wide", t[0]);
|
||||||
|
say(56, "H -13, wide", t[1]);
|
||||||
|
say(72, "V +9, wide", t[2]);
|
||||||
|
say(88, "V -11, wide", t[3]);
|
||||||
|
say(104, "clip/full dirty", t[4]);
|
||||||
|
say(120, "H zero/full copy", t[5]);
|
||||||
|
say(136, "source intact", t[6]);
|
||||||
|
for (i = 0; i < 7 && t[i]; i++) { }
|
||||||
|
setcolor(WHITE);
|
||||||
|
outtextxy(8, 160, i == 7 ? (char *)"ALL PASS" : (char *)"SOME FAILED");
|
||||||
|
for (;;) { }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user