/* * gfx_blit_cols_part_wx_op — блит спрайта column-major ЛОГИЧЕСКОЙ ОПЕРАЦИЕЙ: * приёмник = приёмник источник (op = GFX_OP_AND / GFX_OP_OR / * GFX_OP_XOR). Полный близнец gfx_blit_cols_part_wx: те же клип-параметры * (skip/rows сверху, skipw/maxw по колонкам), тот же бесплатный флип; вся * разница — вызов op-ядра вместо копирующего. * * ПОЧЕМУ ОТДЕЛЬНОЕ ТЕЛО, а не флаг в gfx_blit_cols_part_wx. Копирующий * путь — самый горячий в движке персонажей (PoP зовёт его на каждого Char * каждый кадр); ветка «а не операция ли?» в его хвосте тянула бы op-ядро в * ЛЮБУЮ программу, использующую блит колонками (~+180 Б _CODE при нулевой * пользе). Клип-математика здесь дублируется сознательно — правки клипа * вносить в ОБА файла (близнец: common/gfx_blit_cols_part_wx.c). * * ПРОЗРАЧНОСТЬ — ГЛАВНОЕ ОТЛИЧИЕ ОТ КОПИИ. Аппаратная прозрачность (банк * 0x58/0x5C: байт #FF не пишется) смотрит на РЕЗУЛЬТАТ операции, а не на * источник. Отсюда: * GFX_OP_AND — прозрачный #FF нейтрален сам по себе (#FF & bg = bg); * банк любой. * GFX_OP_OR — #FF | bg = #FF, запись подавляется банком 0x58/0x5C; * работает с нашими обычными атласами КАК ЕСТЬ. Оговорка: * подавится и «настоящий» результат, случайно равный #FF. * GFX_OP_XOR — с прозрачностью #FF НЕ РАБОТАЕТ: #FF ^ bg = ~bg, то есть * инверсия фона по всему прямоугольнику спрайта. Источник * для XOR обязан хранить прозрачный пиксель как 0x00 — * нейтральный элемент и для XOR, и для OR (так же, как в * оригинале DOS-игр, где прозрачный индекс = 0). * * ЧТО ЧИТАЕТ ОПЕРАЦИЯ. Чтение страниц #50..#5F всегда отдаёт ОЗУ-копию * экрана (memory/sprinter_vram_transparency), поэтому при банке 0x54/0x5C * (не писать в копию) операция считает ЧИСТЫЙ ФОН, а не то, что уже * нарисовано поверх. Нужен результат поверх нарисованного — рисовать * банком GFX_BANK_NORMAL (0x50), у которого копия и видео-ОЗУ совпадают; * платой будет потеря дарового heal из копии. * * Цена: три accel-burst'а на колонку вместо двух (+50% времени accel); * подготовка/клип — как у копии. */ #include #include "../_bgi.h" void gfx_blit_cols_part_wx_op(int x, int y, const void *img, uint8_t flip, int skip, int rows, uint8_t skipw, uint8_t maxw, uint8_t op) { 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); /* NOT — инверсия ИСТОЧНИКА, приёмник в ней не участвует: своя цепочка * (XOR по блоку единиц) и своё ядро. Ветка одна на вызов. */ if (op == GFX_OP_NOT) _bgi_blit_cols_not_raw(src, dst, (uint8_t)w, (uint8_t)h, sstride, (uint8_t)y); else _bgi_blit_cols_op_raw(src, dst, (uint8_t)w, (uint8_t)h, sstride, (uint8_t)y, op); _bgi_end(); }