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;
147 CopyPaletteIfNeeded(bg1_layout.bitmap());
150 CopyPaletteIfNeeded(bg1_objects.bitmap());
153 CopyPaletteIfNeeded(bg2_layout.bitmap());
156 CopyPaletteIfNeeded(bg2_objects.bitmap());
168 std::vector<SDL_Color> pal_lut;
169 if (bg2_translucent && output.
surface() && output.
surface()->format &&
170 output.
surface()->format->palette) {
171 SDL_Palette* sdl_pal = output.
surface()->format->palette;
172 int n = std::min(sdl_pal->ncolors, 256);
173 pal_lut.resize(256, {0, 0, 0, 0});
174 for (
int i = 0; i < n; ++i) {
175 pal_lut[i] = sdl_pal->colors[i];
182 auto find_nearest_in_bank = [&](uint8_t base_idx, uint8_t r, uint8_t g,
183 uint8_t b) -> uint8_t {
186 int bank_start = (base_idx / 16) * 16;
187 int bank_end = bank_start + 16;
188 int best_idx = base_idx;
189 int best_dist = INT_MAX;
190 for (
int i = bank_start + 1; i < bank_end && i < 256; ++i) {
191 int dr =
static_cast<int>(pal_lut[i].r) - r;
192 int dg =
static_cast<int>(pal_lut[i].g) - g;
193 int db =
static_cast<int>(pal_lut[i].b) - b;
194 int dist = dr * dr + dg * dg + db * db;
195 if (dist < best_dist) {
200 return static_cast<uint8_t
>(best_idx);
205 std::array<uint8_t, 256 * 256> blend_cache{};
206 std::array<uint8_t, 256 * 256> blend_cache_valid{};
207 auto resolve_blended_index = [&](uint8_t winner_idx,
208 uint8_t other_idx) -> uint8_t {
209 if (pal_lut.empty() || winner_idx == other_idx) {
212 const size_t key = (
static_cast<size_t>(winner_idx) << 8) |
213 static_cast<size_t>(other_idx);
214 if (blend_cache_valid[key] != 0) {
215 return blend_cache[key];
218 const SDL_Color& c1 = pal_lut[winner_idx];
219 const SDL_Color& c2 = pal_lut[other_idx];
220 const uint8_t blend_r =
static_cast<uint8_t
>((c1.r + c2.r) / 2);
221 const uint8_t blend_g =
static_cast<uint8_t
>((c1.g + c2.g) / 2);
222 const uint8_t blend_b =
static_cast<uint8_t
>((c1.b + c2.b) / 2);
223 const uint8_t resolved =
224 find_nearest_in_bank(winner_idx, blend_r, blend_g, blend_b);
225 blend_cache[key] = resolved;
226 blend_cache_valid[key] = 1;
230 auto normalize_pri = [](uint8_t pri) -> uint8_t {
232 return (pri == 0xFF) ? 0 : (pri ? 1 : 0);
235 auto rank_for = [&](
bool is_bg1, uint8_t pri) ->
int {
236 pri = normalize_pri(pri);
243 const auto& bg1_layout_px = bg1_layout.bitmap().data();
244 const auto& bg1_obj_px = bg1_objects.bitmap().data();
245 const auto& bg2_layout_px = bg2_layout.bitmap().data();
246 const auto& bg2_obj_px = bg2_objects.bitmap().data();
247 const auto& bg1_layout_pri = bg1_layout.priority_data();
248 const auto& bg1_obj_pri = bg1_objects.priority_data();
249 const auto& bg2_layout_pri = bg2_layout.priority_data();
250 const auto& bg2_obj_pri = bg2_objects.priority_data();
251 const auto& bg1_obj_cov = bg1_objects.coverage_data();
252 const auto& bg2_obj_cov = bg2_objects.coverage_data();
255 for (
int idx = 0; idx < kPixelCount; ++idx) {
256 uint8_t bg1_pixel = 255;
258 const bool bg1_obj_wrote =
259 bg1_obj_on && ((idx < static_cast<int>(bg1_obj_cov.size()) &&
260 bg1_obj_cov[idx] != 0) ||
263 if (!bg1_revealed_at(bg1_objects, idx)) {
264 bg1_pixel = bg1_obj_px[idx];
265 bg1_pri = bg1_obj_pri[idx];
267 }
else if (bg1_layout_on && !
IsTransparent(bg1_layout_px[idx])) {
268 if (!bg1_revealed_at(bg1_layout, idx)) {
269 bg1_pixel = bg1_layout_px[idx];
270 bg1_pri = bg1_layout_pri[idx];
274 uint8_t bg2_pixel = 255;
276 const bool bg2_obj_wrote =
277 bg2_obj_on && ((idx < static_cast<int>(bg2_obj_cov.size()) &&
278 bg2_obj_cov[idx] != 0) ||
281 bg2_pixel = bg2_obj_px[idx];
282 bg2_pri = bg2_obj_pri[idx];
283 }
else if (bg2_layout_on && !
IsTransparent(bg2_layout_px[idx])) {
284 bg2_pixel = bg2_layout_px[idx];
285 bg2_pri = bg2_layout_pri[idx];
290 dst_data[idx] = bg2_pixel;
295 dst_data[idx] = bg1_pixel;
300 const int r1 = rank_for(
true, bg1_pri);
301 const int r2 = rank_for(
false, bg2_pri);
306 if (bg2_translucent && !pal_lut.empty()) {
307 const bool bg1_wins = (r1 >= r2);
308 const uint8_t winner = bg1_wins ? bg1_pixel : bg2_pixel;
309 const uint8_t other = bg1_wins ? bg2_pixel : bg1_pixel;
310 dst_data[idx] = resolve_blended_index(winner, other);
312 dst_data[idx] = (r1 >= r2) ? bg1_pixel : bg2_pixel;
329 if (!layer_enabled(layer_type, buffer))
332 const auto& src_bitmap = buffer.
bitmap();
335 CopyPaletteIfNeeded(src_bitmap);
337 const auto& src_data = src_bitmap.data();
343 for (
int idx = 0; idx < kPixelCount; ++idx) {
346 if (is_bg1_layer && bg1_revealed_at(buffer, idx)) {
349 uint8_t src_pixel = src_data[idx];
356 switch (blend_mode) {
359 dst_data[idx] = src_pixel;
367 dst_data[idx] = src_pixel;
376 dst_data[idx] = src_pixel;
378 dst_data[idx] = src_pixel;
384 dst_data[idx] = src_pixel;
403 if (!palette_copied) {
404 const auto& bg1_bitmap = room.
bg1_buffer().bitmap();
405 if (bg1_bitmap.surface()) {
406 ApplySDLPaletteToBitmap(bg1_bitmap.surface(), output);
417 SDL_SetColorKey(output.
surface(), SDL_TRUE, 255);
418 SDL_SetSurfaceBlendMode(output.
surface(), SDL_BLENDMODE_BLEND);
424 SDL_SetSurfaceColorMod(output.
surface(), 128, 128, 128);
427 SDL_SetSurfaceColorMod(output.
surface(), 255, 255, 255);