51 constexpr int kWidth = 512;
52 constexpr int kHeight = 512;
53 constexpr int kPixelCount = kWidth * kHeight;
56 static int last_room_id = -1;
57 if (room.
id() != last_room_id) {
58 last_room_id = room.
id();
59 LOG_DEBUG(
"LayerManager",
"Room %03X: BG1_Layout(vis=%d,blend=%d) "
60 "BG1_Objects(vis=%d,blend=%d) BG2_Layout(vis=%d,blend=%d) "
61 "BG2_Objects(vis=%d,blend=%d) MergeType=%d",
75 if (output.
width() != kWidth || output.
height() != kHeight) {
76 output.
Create(kWidth, kHeight, 8,
77 std::vector<uint8_t>(kPixelCount, 0));
85 bool palette_copied =
false;
94 auto CopyPaletteIfNeeded = [&](
const gfx::Bitmap& src_bitmap) {
95 if (!palette_copied && src_bitmap.surface()) {
96 ApplySDLPaletteToBitmap(src_bitmap.surface(), output);
97 palette_copied =
true;
118 const auto& bmp = buf.bitmap();
119 if (!bmp.is_active() || bmp.width() == 0) {
127 CopyPaletteIfNeeded(bg1_layout.bitmap());
130 CopyPaletteIfNeeded(bg1_objects.bitmap());
133 CopyPaletteIfNeeded(bg2_layout.bitmap());
136 CopyPaletteIfNeeded(bg2_objects.bitmap());
141 const bool bg2_translucent =
147 std::vector<SDL_Color> pal_lut;
148 if (bg2_translucent && output.
surface() && output.
surface()->format &&
149 output.
surface()->format->palette) {
150 SDL_Palette* sdl_pal = output.
surface()->format->palette;
151 int n = std::min(sdl_pal->ncolors, 256);
152 pal_lut.resize(256, {0, 0, 0, 0});
153 for (
int i = 0; i < n; ++i) {
154 pal_lut[i] = sdl_pal->colors[i];
161 auto find_nearest_in_bank = [&](uint8_t base_idx, uint8_t r, uint8_t g,
162 uint8_t b) -> uint8_t {
163 if (pal_lut.empty())
return base_idx;
164 int bank_start = (base_idx / 16) * 16;
165 int bank_end = bank_start + 16;
166 int best_idx = base_idx;
167 int best_dist = INT_MAX;
168 for (
int i = bank_start + 1; i < bank_end && i < 256; ++i) {
169 int dr =
static_cast<int>(pal_lut[i].r) - r;
170 int dg =
static_cast<int>(pal_lut[i].g) - g;
171 int db =
static_cast<int>(pal_lut[i].b) - b;
172 int dist = dr * dr + dg * dg + db * db;
173 if (dist < best_dist) {
178 return static_cast<uint8_t
>(best_idx);
183 std::array<uint8_t, 256 * 256> blend_cache{};
184 std::array<uint8_t, 256 * 256> blend_cache_valid{};
185 auto resolve_blended_index = [&](uint8_t winner_idx,
186 uint8_t other_idx) -> uint8_t {
187 if (pal_lut.empty() || winner_idx == other_idx) {
191 (
static_cast<size_t>(winner_idx) << 8) |
static_cast<size_t>(other_idx);
192 if (blend_cache_valid[key] != 0) {
193 return blend_cache[key];
196 const SDL_Color& c1 = pal_lut[winner_idx];
197 const SDL_Color& c2 = pal_lut[other_idx];
198 const uint8_t blend_r =
static_cast<uint8_t
>((c1.r + c2.r) / 2);
199 const uint8_t blend_g =
static_cast<uint8_t
>((c1.g + c2.g) / 2);
200 const uint8_t blend_b =
static_cast<uint8_t
>((c1.b + c2.b) / 2);
201 const uint8_t resolved =
202 find_nearest_in_bank(winner_idx, blend_r, blend_g, blend_b);
203 blend_cache[key] = resolved;
204 blend_cache_valid[key] = 1;
208 auto normalize_pri = [](uint8_t pri) -> uint8_t {
210 return (pri == 0xFF) ? 0 : (pri ? 1 : 0);
213 auto rank_for = [&](
bool is_bg1, uint8_t pri) ->
int {
214 pri = normalize_pri(pri);
221 const auto& bg1_layout_px = bg1_layout.bitmap().data();
222 const auto& bg1_obj_px = bg1_objects.bitmap().data();
223 const auto& bg2_layout_px = bg2_layout.bitmap().data();
224 const auto& bg2_obj_px = bg2_objects.bitmap().data();
225 const auto& bg1_layout_pri = bg1_layout.priority_data();
226 const auto& bg1_obj_pri = bg1_objects.priority_data();
227 const auto& bg2_layout_pri = bg2_layout.priority_data();
228 const auto& bg2_obj_pri = bg2_objects.priority_data();
229 const auto& bg1_obj_cov = bg1_objects.coverage_data();
230 const auto& bg2_obj_cov = bg2_objects.coverage_data();
238 for (
int idx = 0; idx < kPixelCount; ++idx) {
239 uint8_t bg1_pixel = 255;
241 const bool bg1_obj_wrote =
243 ((idx < static_cast<int>(bg1_obj_cov.size()) && bg1_obj_cov[idx] != 0) ||
246 bg1_pixel = bg1_obj_px[idx];
247 bg1_pri = bg1_obj_pri[idx];
248 }
else if (bg1_layout_on && !
IsTransparent(bg1_layout_px[idx])) {
249 bg1_pixel = bg1_layout_px[idx];
250 bg1_pri = bg1_layout_pri[idx];
253 uint8_t bg2_pixel = 255;
255 const bool bg2_obj_wrote =
257 ((idx < static_cast<int>(bg2_obj_cov.size()) && bg2_obj_cov[idx] != 0) ||
260 bg2_pixel = bg2_obj_px[idx];
261 bg2_pri = bg2_obj_pri[idx];
262 }
else if (bg2_layout_on && !
IsTransparent(bg2_layout_px[idx])) {
263 bg2_pixel = bg2_layout_px[idx];
264 bg2_pri = bg2_layout_pri[idx];
269 dst_data[idx] = bg2_pixel;
274 dst_data[idx] = bg1_pixel;
279 const int r1 = rank_for(
true, bg1_pri);
280 const int r2 = rank_for(
false, bg2_pri);
285 if (bg2_translucent && !pal_lut.empty()) {
286 const bool bg1_wins = (r1 >= r2);
287 const uint8_t winner = bg1_wins ? bg1_pixel : bg2_pixel;
288 const uint8_t other = bg1_wins ? bg2_pixel : bg1_pixel;
289 dst_data[idx] = resolve_blended_index(winner, other);
291 dst_data[idx] = (r1 >= r2) ? bg1_pixel : bg2_pixel;
309 const auto& src_bitmap = buffer.
bitmap();
310 if (!src_bitmap.is_active() || src_bitmap.width() == 0)
return;
315 CopyPaletteIfNeeded(src_bitmap);
317 const auto& src_data = src_bitmap.data();
323 for (
int idx = 0; idx < kPixelCount; ++idx) {
324 uint8_t src_pixel = src_data[idx];
331 switch (blend_mode) {
334 dst_data[idx] = src_pixel;
342 dst_data[idx] = src_pixel;
351 dst_data[idx] = src_pixel;
353 dst_data[idx] = src_pixel;
359 dst_data[idx] = src_pixel;
378 if (!palette_copied) {
379 const auto& bg1_bitmap = room.
bg1_buffer().bitmap();
380 if (bg1_bitmap.surface()) {
381 ApplySDLPaletteToBitmap(bg1_bitmap.surface(), output);
392 SDL_SetColorKey(output.
surface(), SDL_TRUE, 255);
393 SDL_SetSurfaceBlendMode(output.
surface(), SDL_BLENDMODE_BLEND);
399 SDL_SetSurfaceColorMod(output.
surface(), 128, 128, 128);
402 SDL_SetSurfaceColorMod(output.
surface(), 255, 255, 255);