54 constexpr int kWidth = 512;
55 constexpr int kHeight = 512;
56 constexpr int kPixelCount = kWidth * kHeight;
59 static int last_room_id = -1;
60 if (room.
id() != last_room_id) {
61 last_room_id = room.
id();
63 "Room %03X: BG1_Layout(vis=%d,blend=%d) "
64 "BG1_Objects(vis=%d,blend=%d) BG2_Layout(vis=%d,blend=%d) "
65 "BG2_Objects(vis=%d,blend=%d) MergeType=%d",
78 if (output.
width() != kWidth || output.
height() != kHeight) {
79 output.
Create(kWidth, kHeight, 8, std::vector<uint8_t>(kPixelCount, 0));
87 bool palette_copied =
false;
101 const auto& bitmap = buffer.bitmap();
102 return bitmap.is_active() && bitmap.width() > 0;
112 if (index < 0 || index >=
static_cast<int>(mask.size())) {
115 const uint8_t value = mask[index];
116 const bool layout_reveal =
120 const bool object_reveal =
124 return layout_reveal || object_reveal;
128 auto CopyPaletteIfNeeded = [&](
const gfx::Bitmap& src_bitmap) {
129 if (!palette_copied && src_bitmap.surface()) {
130 ApplySDLPaletteToBitmap(src_bitmap.surface(), output);
131 palette_copied =
true;
145 CopyPaletteIfNeeded(bg1_layout.bitmap());
148 CopyPaletteIfNeeded(bg1_objects.bitmap());
151 CopyPaletteIfNeeded(bg2_layout.bitmap());
154 CopyPaletteIfNeeded(bg2_objects.bitmap());
157 const auto& upper_layout_px = bg1_layout.bitmap().data();
158 const auto& upper_object_px = bg1_objects.bitmap().data();
159 const auto& lower_layout_px = bg2_layout.bitmap().data();
160 const auto& lower_object_px = bg2_objects.bitmap().data();
161 const auto& upper_object_coverage = bg1_objects.coverage_data();
162 const auto& lower_object_coverage = bg2_objects.coverage_data();
164 auto resolve_tilemap_pixel =
165 [&](
bool layout_on,
bool objects_on,
const uint8_t* layout_pixels,
166 const uint8_t* object_pixels,
167 const std::vector<uint8_t>& object_coverage,
int index) -> uint8_t {
169 const bool object_wrote =
170 (index < static_cast<int>(object_coverage.size()) &&
171 object_coverage[index] != 0) ||
174 return object_pixels[index];
177 return layout_on ? layout_pixels[index] : 255;
181 for (
int idx = 0; idx < kPixelCount; ++idx) {
182 const uint8_t upper_pixel =
183 resolve_tilemap_pixel(bg1_layout_on, bg1_obj_on, upper_layout_px,
184 upper_object_px, upper_object_coverage, idx);
185 const uint8_t lower_pixel =
186 resolve_tilemap_pixel(bg2_layout_on, bg2_obj_on, lower_layout_px,
187 lower_object_px, lower_object_coverage, idx);
190 dst_data[idx] = upper_pixel;
192 dst_data[idx] = lower_pixel;
209 CopyPaletteIfNeeded(bg1_layout.bitmap());
212 CopyPaletteIfNeeded(bg1_objects.bitmap());
215 CopyPaletteIfNeeded(bg2_layout.bitmap());
218 CopyPaletteIfNeeded(bg2_objects.bitmap());
224 const bool bg2_layout_translucent =
227 const bool bg2_objects_translucent =
233 std::vector<SDL_Color> pal_lut;
234 if ((bg2_layout_translucent || bg2_objects_translucent) &&
236 output.
surface()->format->palette) {
237 SDL_Palette* sdl_pal = output.
surface()->format->palette;
238 int n = std::min(sdl_pal->ncolors, 256);
239 pal_lut.resize(256, {0, 0, 0, 0});
240 for (
int i = 0; i < n; ++i) {
241 pal_lut[i] = sdl_pal->colors[i];
248 auto find_nearest_in_bank = [&](uint8_t base_idx, uint8_t r, uint8_t g,
249 uint8_t b) -> uint8_t {
252 int bank_start = (base_idx / 16) * 16;
253 int bank_end = bank_start + 16;
254 int best_idx = base_idx;
255 int best_dist = INT_MAX;
256 for (
int i = bank_start + 1; i < bank_end && i < 256; ++i) {
257 int dr =
static_cast<int>(pal_lut[i].r) - r;
258 int dg =
static_cast<int>(pal_lut[i].g) - g;
259 int db =
static_cast<int>(pal_lut[i].b) - b;
260 int dist = dr * dr + dg * dg + db * db;
261 if (dist < best_dist) {
266 return static_cast<uint8_t
>(best_idx);
271 std::array<uint8_t, 256 * 256> blend_cache{};
272 std::array<uint8_t, 256 * 256> blend_cache_valid{};
273 auto blend_channel = [&](uint8_t first, uint8_t second) -> uint8_t {
274 if (!full_add_color_math) {
275 return static_cast<uint8_t
>((first + second) / 2);
280 const int sum = std::min(31, (first >> 3) + (second >> 3));
281 return static_cast<uint8_t
>((sum << 3) | (sum >> 2));
283 auto resolve_blended_index = [&](uint8_t winner_idx,
284 uint8_t other_idx) -> uint8_t {
285 if (pal_lut.empty() ||
286 (!full_add_color_math && winner_idx == other_idx)) {
289 const size_t key = (
static_cast<size_t>(winner_idx) << 8) |
290 static_cast<size_t>(other_idx);
291 if (blend_cache_valid[key] != 0) {
292 return blend_cache[key];
295 const SDL_Color& c1 = pal_lut[winner_idx];
296 const SDL_Color& c2 = pal_lut[other_idx];
297 const uint8_t blend_r = blend_channel(c1.r, c2.r);
298 const uint8_t blend_g = blend_channel(c1.g, c2.g);
299 const uint8_t blend_b = blend_channel(c1.b, c2.b);
300 const uint8_t resolved =
301 find_nearest_in_bank(winner_idx, blend_r, blend_g, blend_b);
302 blend_cache[key] = resolved;
303 blend_cache_valid[key] = 1;
307 auto normalize_pri = [](uint8_t pri) -> uint8_t {
309 return (pri == 0xFF) ? 0 : (pri ? 1 : 0);
312 auto rank_for = [&](
bool is_bg1, uint8_t pri) ->
int {
320 const auto& bg1_layout_px = bg1_layout.bitmap().data();
321 const auto& bg1_obj_px = bg1_objects.bitmap().data();
322 const auto& bg2_layout_px = bg2_layout.bitmap().data();
323 const auto& bg2_obj_px = bg2_objects.bitmap().data();
324 const auto& bg1_layout_pri = bg1_layout.priority_data();
325 const auto& bg1_obj_pri = bg1_objects.priority_data();
326 const auto& bg2_layout_pri = bg2_layout.priority_data();
327 const auto& bg2_obj_pri = bg2_objects.priority_data();
328 const auto& bg1_obj_cov = bg1_objects.coverage_data();
329 const auto& bg2_obj_cov = bg2_objects.coverage_data();
332 for (
int idx = 0; idx < kPixelCount; ++idx) {
333 uint8_t bg1_pixel = 255;
335 const bool bg1_obj_wrote =
336 bg1_obj_on && ((idx < static_cast<int>(bg1_obj_cov.size()) &&
337 bg1_obj_cov[idx] != 0) ||
340 if (!bg1_revealed_at(bg1_objects, idx)) {
341 bg1_pixel = bg1_obj_px[idx];
342 bg1_pri = bg1_obj_pri[idx];
344 }
else if (bg1_layout_on && !
IsTransparent(bg1_layout_px[idx])) {
345 if (!bg1_revealed_at(bg1_layout, idx)) {
346 bg1_pixel = bg1_layout_px[idx];
347 bg1_pri = bg1_layout_pri[idx];
351 uint8_t bg2_pixel = 255;
353 bool bg2_pixel_translucent =
false;
354 const bool bg2_obj_wrote =
355 bg2_obj_on && ((idx < static_cast<int>(bg2_obj_cov.size()) &&
356 bg2_obj_cov[idx] != 0) ||
359 bg2_pixel = bg2_obj_px[idx];
360 bg2_pri = bg2_obj_pri[idx];
361 bg2_pixel_translucent = bg2_objects_translucent;
362 }
else if (bg2_layout_on && !
IsTransparent(bg2_layout_px[idx])) {
363 bg2_pixel = bg2_layout_px[idx];
364 bg2_pri = bg2_layout_pri[idx];
365 bg2_pixel_translucent = bg2_layout_translucent;
370 dst_data[idx] = bg2_pixel;
375 dst_data[idx] = bg1_pixel;
380 const int r1 = rank_for(
true, bg1_pri);
381 const int r2 = rank_for(
false, bg2_pri);
387 if (bg2_pixel_translucent && !pal_lut.empty()) {
388 const bool bg1_wins = (r1 >= r2);
389 const uint8_t winner = bg1_wins ? bg1_pixel : bg2_pixel;
390 const uint8_t other = bg1_wins ? bg2_pixel : bg1_pixel;
391 dst_data[idx] = resolve_blended_index(winner, other);
393 dst_data[idx] = (r1 >= r2) ? bg1_pixel : bg2_pixel;
410 if (!layer_enabled(layer_type, buffer))
413 const auto& src_bitmap = buffer.
bitmap();
416 CopyPaletteIfNeeded(src_bitmap);
418 const auto& src_data = src_bitmap.data();
424 for (
int idx = 0; idx < kPixelCount; ++idx) {
427 if (is_bg1_layer && bg1_revealed_at(buffer, idx)) {
430 uint8_t src_pixel = src_data[idx];
437 switch (blend_mode) {
440 dst_data[idx] = src_pixel;
447 dst_data[idx] = src_pixel;
456 dst_data[idx] = src_pixel;
458 dst_data[idx] = src_pixel;
464 dst_data[idx] = src_pixel;
483 if (!palette_copied) {
484 const auto& bg1_bitmap = room.
bg1_buffer().bitmap();
485 if (bg1_bitmap.surface()) {
486 ApplySDLPaletteToBitmap(bg1_bitmap.surface(), output);
497 SDL_SetColorKey(output.
surface(), SDL_TRUE, 255);
498 SDL_SetSurfaceBlendMode(output.
surface(), SDL_BLENDMODE_BLEND);
504 SDL_SetSurfaceColorMod(output.
surface(), 128, 128, 128);
507 SDL_SetSurfaceColorMod(output.
surface(), 255, 255, 255);