L4-MIRROR шаги 3 и 5 + левый клип колонок в libbgi

libbgi: новая gfx_blit_cols_part_wx — обрезка СЛЕВА (skipw) в дополнение к
верхней (skip/rows) и правой (maxw).  По подсказке пользователя сделано
примитивом, а не обходным путём «нарисовать и вернуть фон поверх лишнего»:
для column-major левая обрезка стоит ровно столько же, сколько правая —
колонка это непрерывный кусок ОЗУ, меняется стартовая колонка источника и
экранная X.  Внутри это уже было (так клипается левый край экрана), наружу
не было выведено.  Полное тело блита колонками переехало туда,
gfx_blit_cols_part_w стала обёрткой (skipw=0).  size-check: OK, роста нет
(62 программы, -22..-33 Б на пользователей блита).

Шаг 5 — клип ТЕНИ (seg008:1699): на уровне зеркала она показывается только
СПРАВА от него, obj_clip_left = 137 + (mirror_column-4)*32.

Шаг 3 — ОТРАЖЕНИЕ (check_mirror, seg003:0798): пока Кид стоит на тайле
зеркала, каждый кадр рисуется его зеркальная копия с клипом
left = (curr_col<<5)+9 и top = y_clip[curr_row+1].  Отдельной функцией
pop_mirror_draw, а НЕ третьим слотом Char — это структура самого оригинала:
отражение идёт сокращённым путём load_frame_to_obj + add_objtable(4), без
клинка, брызг, fore-прохода и пропуска кадра; гейтить всё это в общем теле
значило бы добавить ветки в самый горячий путь.  Свой heal (pop_mirror_heal)
рядом с pop_char_heal, в skip-маске не участвует.

tests-host: заглушки pop_mirror_draw/pop_mirror_heal (pop_map теперь на них
ссылается), все 5 наборов прошли.

Цена: _CODE 24501 -> 24629, банк 3 10551, банк 4 8267 -> 9590.
НЕ ПРОВЕРЕНО В MAME.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 22:09:45 +03:00
parent 844fa6d767
commit 8f0362f3d4
8 changed files with 259 additions and 67 deletions
+4 -58
View File
@@ -1,23 +1,10 @@
/*
* gfx_blit_cols_part_w — блит спрайта column-major с обрезкой СВЕРХУ
* (skip/rows) И ОГРАНИЧЕНИЕМ ШИРИНЫ (maxw): рисуются только первые maxw
* (skip/rows) и ОГРАНИЧЕНИЕМ ШИРИНЫ (maxw): рисуются только первые maxw
* колонок футпринта, считая от x. maxw = 0 — «вся ширина кадра».
*
* Здесь живёт ПОЛНОЕ тело блита колонками; gfx_blit_cols_part (maxw=0) и
* gfx_blit_cols (ещё и skip=rows=0) — тонкие обёртки, чтобы клип-логика не
* дублировалась. Подробности зеркала/клипа — см. шапку gfx_blit_cols.c.
*
* Зачем ограничение ширины. Обрезка СПРАВА для column-major — это ровно
* уменьшение числа колонок, то есть внутри уже есть (так же клипается край
* экрана: `w = _bgi_maxx + 1 - x`), наружу просто не было выведено.
* Понадобилось для порта clip_char в PoP: персонаж, поднимающийся по
* лестнице за дверью уровня, обязан уходить за правый косяк проёма
* (obj_clip_right), а дверь рисуется ПОД ним и перекрыть его не может.
*
* Важно: ограничение работает и при flip. При зеркале источник читается с
* последней колонки к первой (sstride < 0), но первые w нарисованных
* колонок в любом случае ложатся в ЛЕВУЮ часть футпринта [x, x+w) — то
* есть уменьшение w всегда срезает справа, как и требуется.
* Тонкая обёртка над gfx_blit_cols_part_wx (skipw = 0) — полное тело блита
* колонками живёт там; здесь оно было до появления обрезки СЛЕВА.
*/
#include <stdint.h>
#include "../_bgi.h"
@@ -25,46 +12,5 @@
void gfx_blit_cols_part_w(int x, int y, const void *img, uint8_t flip,
int skip, int rows, uint8_t maxw)
{
const uint8_t *p = (const uint8_t *)img;
int full_w = p[0] | (p[1] << 8);
int full_h = p[2] | (p[3] << 8);
const uint8_t *px = p + 4;
int w = full_w, h;
int sstride = flip ? -full_h : full_h; /* шаг между колонками в ОЗУ */
int sy; /* пропуск строк сверху колонки */
int cx = 0; /* пропуск колонок слева экрана */
const uint8_t *src;
uint8_t *dst;
if (full_w <= 0 || full_h <= 0)
return;
if (maxw && (int)maxw < w)
w = (int)maxw; /* обрезка справа (см. шапку) */
if (skip < 0) skip = 0;
if (skip >= full_h)
return; /* обрезано целиком */
sy = skip;
h = full_h - skip;
if (rows > 0 && rows < h)
h = rows;
/* Клип по экрану — только если хоть один край вылезает. */
if (x < 0 || y < 0 || x + w > _bgi_maxx + 1 || y + h > _bgi_maxy + 1) {
if (x < 0) { cx = -x; w += x; x = 0; }
if (y < 0) { sy -= y; h += y; y = 0; }
if (x + w > _bgi_maxx + 1) w = _bgi_maxx + 1 - x;
if (y + h > _bgi_maxy + 1) h = _bgi_maxy + 1 - y;
if (w <= 0 || h <= 0) return;
}
/* Первая ВИДИМАЯ колонка (с учётом флипа) + первая видимая строка. */
src = flip ? (px + (uint16_t)(full_w - 1) * (uint16_t)full_h) : px;
if (cx)
src += cx * sstride;
src += sy;
_bgi_begin();
dst = (uint8_t *)(_gfx_addr_base + (uint16_t)x);
_bgi_blit_cols_raw(src, dst, (uint8_t)w, (uint8_t)h, sstride, (uint8_t)y);
_bgi_end();
gfx_blit_cols_part_wx(x, y, img, flip, skip, rows, 0, maxw);
}
+82
View File
@@ -0,0 +1,82 @@
/*
* gfx_blit_cols_part_wx — блит спрайта column-major с обрезкой СВЕРХУ
* (skip/rows) и ОКНОМ ПО КОЛОНКАМ: рисуются только колонки
* [skipw, skipw + maxw) футпринта, считая от x. skipw = 0 — от левого края,
* maxw = 0 — «до правого края кадра».
*
* Здесь живёт ПОЛНОЕ тело блита колонками; gfx_blit_cols_part_w (skipw=0),
* gfx_blit_cols_part (ещё и maxw=0) и gfx_blit_cols (плюс skip=rows=0) —
* тонкие обёртки, чтобы клип-логика не дублировалась. Подробности
* зеркала/клипа — см. шапку gfx_blit_cols.c.
*
* Зачем обрезка СЛЕВА (skipw). Для column-major она стоит ровно столько же,
* сколько обрезка справа: колонка — непрерывный кусок ОЗУ, меняется только
* стартовая колонка источника и экранная X. Внутри это уже было —
* так клипается левый край экрана (`if (x < 0) { cx = -x; ... }`), наружу
* просто не было выведено.
*
* Понадобилось для зеркала в PoP (obj_clip_left, seg003:0798 и seg008:1699):
* отражение Кида видно только внутри проёма зеркала, а тень уровня 4 — только
* СПРАВА от него. Обходной путь «нарисовать и вернуть фон поверх лишнего»
* работал, но стоил лишний heal в кадре и рисовал невидимые колонки.
*
* Важно: окно работает и при flip. При зеркале источник читается с
* последней колонки к первой (sstride < 0), но НАРИСОВАННЫЕ колонки всегда
* идут слева направо по футпринту [x, x+w) — то есть skipw срезает слева, а
* maxw справа, независимо от направления чтения источника.
*/
#include <stdint.h>
#include "../_bgi.h"
void gfx_blit_cols_part_wx(int x, int y, const void *img, uint8_t flip,
int skip, int rows, uint8_t skipw, uint8_t maxw)
{
const uint8_t *p = (const uint8_t *)img;
int full_w = p[0] | (p[1] << 8);
int full_h = p[2] | (p[3] << 8);
const uint8_t *px = p + 4;
int w = full_w, h;
int sstride = flip ? -full_h : full_h; /* шаг между колонками в ОЗУ */
int sy; /* пропуск строк сверху колонки */
int cx = 0; /* пропуск колонок слева */
const uint8_t *src;
uint8_t *dst;
if (full_w <= 0 || full_h <= 0)
return;
if (skipw) { /* обрезка СЛЕВА (см. шапку) */
if ((int)skipw >= w) return; /* срезано целиком */
cx = (int)skipw;
x += (int)skipw;
w -= (int)skipw;
}
if (maxw && (int)maxw < w)
w = (int)maxw; /* обрезка справа (см. шапку) */
if (skip < 0) skip = 0;
if (skip >= full_h)
return; /* обрезано целиком */
sy = skip;
h = full_h - skip;
if (rows > 0 && rows < h)
h = rows;
/* Клип по экрану — только если хоть один край вылезает. */
if (x < 0 || y < 0 || x + w > _bgi_maxx + 1 || y + h > _bgi_maxy + 1) {
if (x < 0) { cx -= x; w += x; x = 0; }
if (y < 0) { sy -= y; h += y; y = 0; }
if (x + w > _bgi_maxx + 1) w = _bgi_maxx + 1 - x;
if (y + h > _bgi_maxy + 1) h = _bgi_maxy + 1 - y;
if (w <= 0 || h <= 0) return;
}
/* Первая ВИДИМАЯ колонка (с учётом флипа) + первая видимая строка. */
src = flip ? (px + (uint16_t)(full_w - 1) * (uint16_t)full_h) : px;
if (cx)
src += cx * sstride;
src += sy;
_bgi_begin();
dst = (uint8_t *)(_gfx_addr_base + (uint16_t)x);
_bgi_blit_cols_raw(src, dst, (uint8_t)w, (uint8_t)h, sstride, (uint8_t)y);
_bgi_end();
}
+14
View File
@@ -142,6 +142,20 @@ void gfx_blit_cols_part(int x, int y, const void *img, uint8_t flip,
void gfx_blit_cols_part_w(int x, int y, const void *img, uint8_t flip,
int skip, int rows, uint8_t maxw);
/* То же + ОБРЕЗКА СЛЕВА: рисуются только колонки [skipw, skipw+maxw)
* футпринта, считая от x (skipw = 0 — от левого края, maxw = 0 — до правого
* края кадра). Для column-major левая обрезка стоит столько же, сколько
* правая: колонка — непрерывный кусок ОЗУ, меняется стартовая колонка
* источника и экранная X. Работает и при flip: НАРИСОВАННЫЕ колонки всегда
* идут слева направо по футпринту, независимо от направления чтения.
*
* Мотивация — зеркало в PoP (obj_clip_left, seg003:0798 / seg008:1699):
* отражение Кида видно только внутри проёма, тень уровня 4 — только справа
* от зеркала. Здесь живёт ПОЛНОЕ тело блита колонками, остальные три
* варианта — обёртки. */
void gfx_blit_cols_part_wx(int x, int y, const void *img, uint8_t flip,
int skip, int rows, uint8_t skipw, uint8_t maxw);
/* То же БЕЗ клипа по экрану — пара к gfx_blit_cols_part так же, как
* gfx_blit_part_noclip к gfx_blit_part. Вызывающий гарантирует: спрайт
* целиком на экране, ширина/высота кадра и y <= 255, skip < высоты.