/* * _gfx_blit_full — bracket-free ядро блита под-прямоугольника картинки * getimage-формата (см. gfx_blit_part). Клип по экрану + нарезка на * полосы ≤256 + вызов accel-leaf'а _bgi_copy_rows_raw. БЕЗ _bgi_begin/ * _bgi_end и БЕЗ смены банка — вызывающий обязан замапить W3 (_bgi_begin) * и выставить нужный _gfx_bank ДО вызова. * * Вынесено из gfx_blit_part (2026-07-12) ради batch-пасса спрайтового * движка v2: sprite_update держит одну W3-скобку и один банк на ВЕСЬ * проход, вызывая это ядро в цикле по спрайтам (амортизация обвязки — * см. docs/sprite-api-design.md §9.1). gfx_blit_part теперь = _bgi_begin * + это ядро + _bgi_end. * * Clip fast-path: если прямоугольник целиком на экране — 4 сравнения * вместо полного пути коррекции краёв (TODO «клип fast-path»). */ #include "../_bgi.h" void _gfx_blit_full(int x, int y, const void *img, int sx, int sy, int w, int h) { const uint8_t *p = (const uint8_t *)img; const uint8_t *src; uint16_t dst; int img_w; /* img_w — ПОЛНАЯ ширина картинки/ленты из заголовка getimage * (p[0]=lo, p[1]=hi); это шаг между строками источника (sstride). * sx/sy — смещение кадра внутри картинки (атлас). Подробный разбор * формата и sx/sy — в common/_gfx_blit_sprite.c. */ img_w = p[0] | (p[1] << 8); /* Клип по экрану — только если хоть один край вылезает. */ if (x < 0 || y < 0 || x + w > _bgi_maxx + 1 || y + h > _bgi_maxy + 1) { if (x < 0) { sx -= 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 = img + 4 + sy*img_w + sx; sy*img_w умножением (__mulint, * O(1)) — цикл сложений был O(sy), см. _gfx_blit_sprite.c. */ src = p + 4 + sx; if (sy) src += (uint16_t)sy * (uint16_t)img_w; dst = _gfx_addr_base + x; while (w > 256) { _bgi_blit_rows_raw(src, (uint8_t *)dst, 0 /* =256 */, (uint8_t)h, img_w, (uint8_t)y); src += 256; dst += 256; w -= 256; } _bgi_blit_rows_raw(src, (uint8_t *)dst, (uint8_t)w, (uint8_t)h, img_w, (uint8_t)y); }