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:
@@ -25,6 +25,7 @@
|
|||||||
#include "_pop_draw.h" /* pop_onscreen_cols / pop_heal_fast */
|
#include "_pop_draw.h" /* pop_onscreen_cols / pop_heal_fast */
|
||||||
#include "pop_map.h" /* hitp_curr/hitp_max — HP Кида; clip_char */
|
#include "pop_map.h" /* hitp_curr/hitp_max — HP Кида; clip_char */
|
||||||
#include "pop_level.h" /* тип соперника уровня (набор спрайтов) */
|
#include "pop_level.h" /* тип соперника уровня (набор спрайтов) */
|
||||||
|
#include "pop_tile.h" /* pop_heal_off — клип тени/отражения через фон */
|
||||||
#include "kid_data.h"
|
#include "kid_data.h"
|
||||||
#include "pop_guard_pal.h" /* таблица палитр стражей (сгенерена упаковщиком) */
|
#include "pop_guard_pal.h" /* таблица палитр стражей (сгенерена упаковщиком) */
|
||||||
|
|
||||||
@@ -456,7 +457,7 @@ void pop_char_draw(uint8_t who) __banked
|
|||||||
const kframe *fr;
|
const kframe *fr;
|
||||||
const atlas_t *pages;
|
const atlas_t *pages;
|
||||||
const uint8_t *img;
|
const uint8_t *img;
|
||||||
uint8_t npages, page, idx, dp, flip, hurt;
|
uint8_t npages, page, idx, dp, flip, hurt, lskip;
|
||||||
uint16_t w, h, vis_w;
|
uint16_t w, h, vis_w;
|
||||||
int obj_x, obj_y, fp_x, fwd, top, bx, skip, rows, ct, cr;
|
int obj_x, obj_y, fp_x, fwd, top, bx, skip, rows, ct, cr;
|
||||||
|
|
||||||
@@ -533,6 +534,7 @@ void pop_char_draw(uint8_t who) __banked
|
|||||||
if (flip) bx -= (int)w;
|
if (flip) bx -= (int)w;
|
||||||
top = obj_y - (int)h + 1;
|
top = obj_y - (int)h + 1;
|
||||||
vis_w = w;
|
vis_w = w;
|
||||||
|
lskip = 0;
|
||||||
skip = 0;
|
skip = 0;
|
||||||
/* clip_char (seg006:1231/1749): тайл над головой — стена/пол → всё
|
/* clip_char (seg006:1231/1749): тайл над головой — стена/пол → всё
|
||||||
* выше линии полосы своего ряда НЕ рисуется, иначе персонаж
|
* выше линии полосы своего ряда НЕ рисуется, иначе персонаж
|
||||||
@@ -552,6 +554,18 @@ void pop_char_draw(uint8_t who) __banked
|
|||||||
if (avail <= 0) vis_w = 0; /* весь спрайт за косяком */
|
if (avail <= 0) vis_w = 0; /* весь спрайт за косяком */
|
||||||
else if (avail < (int)w) vis_w = (uint16_t)avail;
|
else if (avail < (int)w) vis_w = (uint16_t)avail;
|
||||||
}
|
}
|
||||||
|
/* КЛИП ТЕНИ СЛЕВА (seg008:1699): на уровне зеркала она может
|
||||||
|
* показываться ТОЛЬКО СПРАВА от него —
|
||||||
|
* obj_clip_left = 137 + (mirror_column-4)*32. */
|
||||||
|
if (ch->charid == CHARID_1_SHADOW &&
|
||||||
|
pop_current_level == MIRROR_LEVEL && ch->room == MIRROR_ROOM) {
|
||||||
|
int cl = 137 + (MIRROR_COL - 4) * 32;
|
||||||
|
if (bx < cl) {
|
||||||
|
int d = cl - bx;
|
||||||
|
if (d >= (int)vis_w) vis_w = 0; /* вся тень левее линии */
|
||||||
|
else lskip = (uint8_t)d;
|
||||||
|
}
|
||||||
|
}
|
||||||
/* Поле комнаты — 192 строки (reset_obj_clip, seg006:0507:
|
/* Поле комнаты — 192 строки (reset_obj_clip, seg006:0507:
|
||||||
* obj_clip_top = 0, obj_clip_bottom = 192). Персонаж, вышедший за
|
* obj_clip_top = 0, obj_clip_bottom = 192). Персонаж, вышедший за
|
||||||
* него (скелет, падающий в пропасть), обязан обрезаться: ниже поля
|
* него (скелет, падающий в пропасть), обязан обрезаться: ниже поля
|
||||||
@@ -564,7 +578,10 @@ void pop_char_draw(uint8_t who) __banked
|
|||||||
/* noclip-ядро ширину не ограничивает, поэтому при обрезке справа
|
/* noclip-ядро ширину не ограничивает, поэтому при обрезке справа
|
||||||
* идём общим путём: это редкие кадры (подъём в двери уровня раз
|
* идём общим путём: это редкие кадры (подъём в двери уровня раз
|
||||||
* за уровень), цена подготовки там не видна. */
|
* за уровень), цена подготовки там не видна. */
|
||||||
if (vis_w == w && pop_onscreen_cols(bx, top + POP_YOFF, w, (uint16_t)rows))
|
if (lskip) /* тень у зеркала: срез слева */
|
||||||
|
gfx_blit_cols_part_wx(bx, top + POP_YOFF, img, flip, skip, rows,
|
||||||
|
lskip, (uint8_t)(vis_w - lskip));
|
||||||
|
else if (vis_w == w && pop_onscreen_cols(bx, top + POP_YOFF, w, (uint16_t)rows))
|
||||||
gfx_blit_cols_part_noclip(bx, top + POP_YOFF, img, flip,
|
gfx_blit_cols_part_noclip(bx, top + POP_YOFF, img, flip,
|
||||||
(uint8_t)skip, (uint8_t)rows);
|
(uint8_t)skip, (uint8_t)rows);
|
||||||
else
|
else
|
||||||
@@ -574,8 +591,8 @@ void pop_char_draw(uint8_t who) __banked
|
|||||||
gfx_set_bank(GFX_BANK_NORMAL);
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
/* heal чистит ТОЛЬКО нарисованное: иначе стирается кромка пола над
|
/* heal чистит ТОЛЬКО нарисованное: иначе стирается кромка пола над
|
||||||
* срезом (мусор/дыра в кладке на второй странице дабл-буфера). */
|
* срезом (мусор/дыра в кладке на второй странице дабл-буфера). */
|
||||||
s->x[dp] = bx; s->y[dp] = top;
|
s->x[dp] = bx + lskip; s->y[dp] = top;
|
||||||
s->w[dp] = vis_w; s->h[dp] = (uint16_t)rows;
|
s->w[dp] = (uint16_t)(vis_w - lskip); s->h[dp] = (uint16_t)rows;
|
||||||
s->valid[dp] = (uint8_t)(rows != 0);
|
s->valid[dp] = (uint8_t)(rows != 0);
|
||||||
if (rows) cd_clip_add(s, bx, top, vis_w, (uint16_t)rows);
|
if (rows) cd_clip_add(s, bx, top, vis_w, (uint16_t)rows);
|
||||||
s->fpw = w; s->fph = h; /* габарит КАДРА (не обрезанный): */
|
s->fpw = w; s->fph = h; /* габарит КАДРА (не обрезанный): */
|
||||||
@@ -614,6 +631,102 @@ void pop_char_fore(uint8_t who) __banked
|
|||||||
s->fpx, s->fpy, s->fpw, s->fph);
|
s->fpx, s->fpy, s->fpw, s->fph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- ОТРАЖЕНИЕ В ЗЕРКАЛЕ (check_mirror, seg003:0798) ---------------- *
|
||||||
|
*
|
||||||
|
* Пока Кид стоит на тайле зеркала, оригинал каждый кадр рисует его
|
||||||
|
* зеркальную копию. ОТДЕЛЬНОЙ функцией, а не третьим слотом pop_cd — и это
|
||||||
|
* не экономия, а структура САМОГО оригинала: отражение идёт сокращённым
|
||||||
|
* путём `load_frame_to_obj(); reset_obj_clip(); add_objtable(4)`, а НЕ через
|
||||||
|
* add_kid_to_objtable. У него нет ни клинка, ни брызг, ни fore-прохода, ни
|
||||||
|
* пропуска неизменившегося кадра — гейтить всё это в общем теле значило бы
|
||||||
|
* добавить ветки в самый горячий путь ради двух секунд за игру.
|
||||||
|
*
|
||||||
|
* Char к моменту вызова уже ОТРАЖЁН вызывающим (pop_check_mirror ->
|
||||||
|
* mirror_image), поэтому здесь никакого «зеркалим» нет — обычная математика
|
||||||
|
* кадра, та же, что в pop_char_draw (см. там load_frame_to_obj, seg008:1728;
|
||||||
|
* шесть строк намеренно продублированы, а не вынесены: трогать горячее тело
|
||||||
|
* ради холодного вызова дороже, чем держать их синхронными по комментарию).
|
||||||
|
*/
|
||||||
|
static int mi_x[2], mi_y[2];
|
||||||
|
static uint16_t mi_w[2], mi_h[2];
|
||||||
|
static uint8_t mi_valid[2];
|
||||||
|
|
||||||
|
void pop_mirror_heal(void) __banked
|
||||||
|
{
|
||||||
|
uint8_t p = gfx_get_draw_page() & 1;
|
||||||
|
if (!mi_valid[p]) return;
|
||||||
|
pop_heal_fast(mi_x[p], mi_y[p] + POP_YOFF, mi_w[p], mi_h[p]);
|
||||||
|
mi_valid[p] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_mirror_draw(int clip_top) __banked
|
||||||
|
{
|
||||||
|
uint8_t dp = gfx_get_draw_page() & 1;
|
||||||
|
const kframe *fr = &kid_frame;
|
||||||
|
const uint8_t *img;
|
||||||
|
uint8_t page, idx, flip;
|
||||||
|
uint16_t w, h, vis_w;
|
||||||
|
int fwd, obj_x, obj_y, bx, top, skip = 0, rows, cl;
|
||||||
|
uint8_t lskip = 0;
|
||||||
|
|
||||||
|
if (fr->image == 255) return;
|
||||||
|
page = (uint8_t)(fr->image >> 3);
|
||||||
|
idx = (uint8_t)(fr->image & 7);
|
||||||
|
if (page >= kid_npages) return;
|
||||||
|
|
||||||
|
fwd = (int)Char.x + ((Char.direction < 0) ? -fr->dx : fr->dx);
|
||||||
|
obj_x = (fwd << 1) - 116;
|
||||||
|
obj_y = (int)Char.y + fr->dy;
|
||||||
|
if ((int8_t)(fr->flags ^ Char.direction) >= 0) obj_x++;
|
||||||
|
obj_x = scr_x(obj_x);
|
||||||
|
|
||||||
|
img = (const uint8_t *)atlas_image(&kidp[page], idx);
|
||||||
|
gfx_w0_map(kidp[page].page);
|
||||||
|
w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8));
|
||||||
|
h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8));
|
||||||
|
if (!w || !h) { gfx_w0_unmap(); return; }
|
||||||
|
flip = (uint8_t)(Char.direction >= 0);
|
||||||
|
bx = flip ? obj_x - (int)w : obj_x;
|
||||||
|
top = obj_y - (int)h + 1;
|
||||||
|
|
||||||
|
/* Клип СВЕРХУ — y_clip[curr_row+1] оригинала: отражение не должно
|
||||||
|
* вылезать из проёма зеркала вверх. Значение считает вызывающий
|
||||||
|
* (таблица y_clip живёт в pop_map). */
|
||||||
|
if (clip_top > top) { skip = clip_top - top; top = clip_top; }
|
||||||
|
rows = (int)h - skip;
|
||||||
|
if (top < 0) { skip += -top; rows += top; top = 0; }
|
||||||
|
if (top + rows > 192) rows = 192 - top;
|
||||||
|
vis_w = w;
|
||||||
|
if (rows <= 0) { gfx_w0_unmap(); return; }
|
||||||
|
|
||||||
|
/* Клип СЛЕВА — obj_clip_left = (curr_col<<5)+9: отражение видно только
|
||||||
|
* внутри проёма зеркала. Настоящей обрезкой колонок
|
||||||
|
* (gfx_blit_cols_part_wx), а не «нарисовать и вернуть фон»: для
|
||||||
|
* column-major левая обрезка стоит столько же, сколько правая. */
|
||||||
|
cl = ((int)Char.curr_col << 5) + 9;
|
||||||
|
if (bx < cl) {
|
||||||
|
int d = cl - bx;
|
||||||
|
if (d >= (int)vis_w) { gfx_w0_unmap(); return; } /* целиком за краем */
|
||||||
|
lskip = (uint8_t)d;
|
||||||
|
}
|
||||||
|
|
||||||
|
gfx_set_bank(GFX_BANK_SPRITE);
|
||||||
|
if (lskip)
|
||||||
|
gfx_blit_cols_part_wx(bx, top + POP_YOFF, img, flip, skip, rows,
|
||||||
|
lskip, (uint8_t)(vis_w - lskip));
|
||||||
|
else if (pop_onscreen_cols(bx, top + POP_YOFF, vis_w, (uint16_t)rows))
|
||||||
|
gfx_blit_cols_part_noclip(bx, top + POP_YOFF, img, flip,
|
||||||
|
(uint8_t)skip, (uint8_t)rows);
|
||||||
|
else
|
||||||
|
gfx_blit_cols_part_w(bx, top + POP_YOFF, img, flip, skip, rows, 0);
|
||||||
|
gfx_set_bank(GFX_BANK_NORMAL);
|
||||||
|
gfx_w0_unmap();
|
||||||
|
|
||||||
|
mi_x[dp] = bx + lskip; mi_y[dp] = top;
|
||||||
|
mi_w[dp] = (uint16_t)(vis_w - lskip); mi_h[dp] = (uint16_t)rows;
|
||||||
|
mi_valid[dp] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Индикаторы HP (порт draw_kid_hp / draw_guard_hp, seg000:1416) --- *
|
/* ---- Индикаторы HP (порт draw_kid_hp / draw_guard_hp, seg000:1416) --- *
|
||||||
* Оригинал рисует их в статус-полосе на y = 194 (ниже поля тайлов, внутри
|
* Оригинал рисует их в статус-полосе на y = 194 (ниже поля тайлов, внутри
|
||||||
* тех же 200 строк экрана PoP): HP Кида слева направо шагом 7, HP стража
|
* тех же 200 строк экрана PoP): HP Кида слева направо шагом 7, HP стража
|
||||||
|
|||||||
@@ -135,4 +135,13 @@ void pop_cd_touch(int x, int y, int w, int h);
|
|||||||
* сотрёт прошлый кадр за нас). */
|
* сотрёт прошлый кадр за нас). */
|
||||||
uint8_t pop_char_skip_mask(void) __banked;
|
uint8_t pop_char_skip_mask(void) __banked;
|
||||||
|
|
||||||
|
/* ОТРАЖЕНИЕ В ЗЕРКАЛЕ (уровень 4, seg003:0798). Char к моменту вызова уже
|
||||||
|
* отражён (pop_check_mirror); clip_top — линия y_clip[curr_row+1], выше
|
||||||
|
* которой отражение не рисуется. Отдельно от pop_char_draw, потому что и в
|
||||||
|
* оригинале это сокращённый путь add_objtable(4): без клинка, брызг,
|
||||||
|
* fore-прохода и пропуска кадра. heal — парный вызов, рядом с
|
||||||
|
* pop_char_heal. */
|
||||||
|
void pop_mirror_draw(int clip_top) __banked;
|
||||||
|
void pop_mirror_heal(void) __banked;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -2613,11 +2613,30 @@ static void jump_through_mirror(void)
|
|||||||
hitp_curr = 1;
|
hitp_curr = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Линия клипа отражения СВЕРХУ — y_clip[curr_row+1] оригинала. Таблица та
|
||||||
|
* же, что в pop_clip_char_top; держим её здесь копией из четырёх значений,
|
||||||
|
* а не тащим наружу: ряд у отражения всегда 0..2. */
|
||||||
|
static int mirror_clip_top(int8_t row)
|
||||||
|
{
|
||||||
|
static const int16_t y_clip[5] = { -60, 3, 66, 129, 192 };
|
||||||
|
int8_t r = (int8_t)(row + 1);
|
||||||
|
if (r < 0 || r > 4) return 0;
|
||||||
|
return (int)y_clip[r];
|
||||||
|
}
|
||||||
|
|
||||||
void pop_check_mirror(void) __banked
|
void pop_check_mirror(void) __banked
|
||||||
{
|
{
|
||||||
if (pop_jumped_mirror == -1) jump_through_mirror();
|
if (pop_jumped_mirror == -1) { jump_through_mirror(); return; }
|
||||||
/* Ветка ОТРИСОВКИ ОТРАЖЕНИЯ (шаг 3 задачи L4-MIRROR) сюда добавится
|
/* Отражение: пока Кид СТОИТ на тайле зеркала, рисуем его зеркальную
|
||||||
* позже: пока Кид стоит на тайле зеркала, оригинал каждый кадр рисует
|
* копию (в оригинале — add_objtable(4)). Условия оригинала: тайл под
|
||||||
* его зеркальную копию типом 4 objtable с клипом left = (curr_col<<5)+9
|
* персонажем — зеркало, distance_mirror >= 0 (вошёл в тайл достаточно
|
||||||
* и top = y_clip[curr_row+1]. */
|
* глубоко) и его комната = отрисованная. */
|
||||||
|
if (Kid.room != g_room) return;
|
||||||
|
pop_loadkid();
|
||||||
|
if (get_tile_at_char() != TILE_MIRROR) return;
|
||||||
|
pop_load_frame();
|
||||||
|
if (mirror_image() >= 0)
|
||||||
|
pop_mirror_draw(mirror_clip_top(Char.curr_row));
|
||||||
|
/* Char остался ОТРАЖЁННЫМ — как в оригинале: обратно он не сохраняется,
|
||||||
|
* а следующий тик перезагружает окно из Kid. */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -697,6 +697,10 @@ int main(void)
|
|||||||
skip = pop_char_skip_mask();
|
skip = pop_char_skip_mask();
|
||||||
if (!(skip & 1)) pop_char_heal(POP_CH_KID); /* стереть прошлый кадр ЭТОЙ страницы */
|
if (!(skip & 1)) pop_char_heal(POP_CH_KID); /* стереть прошлый кадр ЭТОЙ страницы */
|
||||||
if (!(skip & 2)) pop_char_heal(POP_CH_OPP); /* и прошлый кадр соперника */
|
if (!(skip & 2)) pop_char_heal(POP_CH_OPP); /* и прошлый кадр соперника */
|
||||||
|
/* Отражение в зеркале — свой heal: у него нет слота pop_cd и в
|
||||||
|
* skip-маске он не участвует (рисуется/стирается безусловно, живёт
|
||||||
|
* только пока Кид стоит на тайле зеркала). */
|
||||||
|
pop_mirror_heal();
|
||||||
pop_fore_heal(); /* + следы оверлея (шире прямоугольника Kid) */
|
pop_fore_heal(); /* + следы оверлея (шире прямоугольника Kid) */
|
||||||
PROF(2); /* логика (тот же синий) */
|
PROF(2); /* логика (тот же синий) */
|
||||||
kid_tick(); /* play_seq: следующий кадр */
|
kid_tick(); /* play_seq: следующий кадр */
|
||||||
|
|||||||
@@ -318,3 +318,8 @@ const uint8_t POP_TILE_MOD[256] = {
|
|||||||
* тесты гоняют физику подземелья, поэтому 0. Не заглушка-функция, а та же
|
* тесты гоняют физику подземелья, поэтому 0. Не заглушка-функция, а та же
|
||||||
* переменная, что и в резиденте: карта читает её напрямую. */
|
* переменная, что и в резиденте: карта читает её напрямую. */
|
||||||
uint8_t pop_palace;
|
uint8_t pop_palace;
|
||||||
|
|
||||||
|
/* Отражение в зеркале (уровень 4) — чистая графика, в тестах физики её нет.
|
||||||
|
* pop_map зовёт pop_mirror_draw из pop_check_mirror, поэтому символ нужен. */
|
||||||
|
void pop_mirror_draw(int clip_top) __banked { (void)clip_top; }
|
||||||
|
void pop_mirror_heal(void) __banked { }
|
||||||
|
|||||||
@@ -1,23 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
* gfx_blit_cols_part_w — блит спрайта column-major с обрезкой СВЕРХУ
|
* gfx_blit_cols_part_w — блит спрайта column-major с обрезкой СВЕРХУ
|
||||||
* (skip/rows) И ОГРАНИЧЕНИЕМ ШИРИНЫ (maxw): рисуются только первые maxw
|
* (skip/rows) и ОГРАНИЧЕНИЕМ ШИРИНЫ (maxw): рисуются только первые maxw
|
||||||
* колонок футпринта, считая от x. maxw = 0 — «вся ширина кадра».
|
* колонок футпринта, считая от x. maxw = 0 — «вся ширина кадра».
|
||||||
*
|
*
|
||||||
* Здесь живёт ПОЛНОЕ тело блита колонками; gfx_blit_cols_part (maxw=0) и
|
* Тонкая обёртка над gfx_blit_cols_part_wx (skipw = 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 всегда срезает справа, как и требуется.
|
|
||||||
*/
|
*/
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "../_bgi.h"
|
#include "../_bgi.h"
|
||||||
@@ -25,46 +12,5 @@
|
|||||||
void gfx_blit_cols_part_w(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)
|
int skip, int rows, uint8_t maxw)
|
||||||
{
|
{
|
||||||
const uint8_t *p = (const uint8_t *)img;
|
gfx_blit_cols_part_wx(x, y, img, flip, skip, rows, 0, maxw);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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,
|
void gfx_blit_cols_part_w(int x, int y, const void *img, uint8_t flip,
|
||||||
int skip, int rows, uint8_t maxw);
|
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_cols_part так же, как
|
||||||
* gfx_blit_part_noclip к gfx_blit_part. Вызывающий гарантирует: спрайт
|
* gfx_blit_part_noclip к gfx_blit_part. Вызывающий гарантирует: спрайт
|
||||||
* целиком на экране, ширина/высота кадра и y <= 255, skip < высоты.
|
* целиком на экране, ширина/высота кадра и y <= 255, skip < высоты.
|
||||||
|
|||||||
Reference in New Issue
Block a user