yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
bitmap.cc
Go to the documentation of this file.
1#include "bitmap.h"
2
4
5#include <cstdint>
6#include <cstring> // for memcpy
7#include <span>
8#include <stdexcept>
9
13#include "util/log.h"
14
15namespace yaze {
16namespace gfx {
17
18class BitmapError : public std::runtime_error {
19 public:
20 using std::runtime_error::runtime_error;
21};
22
33Uint32 GetSnesPixelFormat(int format) {
34 switch (format) {
35 case 0:
36 return SDL_PIXELFORMAT_INDEX8;
37 case 1:
39 case 2:
41 default:
42 return SDL_PIXELFORMAT_INDEX8;
43 }
44}
45
46Bitmap::Bitmap(int width, int height, int depth,
47 const std::vector<uint8_t>& data)
48 : width_(width), height_(height), depth_(depth), data_(data) {
50}
51
52Bitmap::Bitmap(int width, int height, int depth,
53 const std::vector<uint8_t>& data, const SnesPalette& palette)
54 : width_(width),
55 height_(height),
56 depth_(depth),
57 palette_(palette),
58 data_(data) {
61}
62
64 : width_(other.width_),
65 height_(other.height_),
66 depth_(other.depth_),
67 active_(other.active_),
68 modified_(other.modified_),
69 palette_(other.palette_),
70 data_(other.data_) {
71 // Copy the data and recreate surface/texture with simple assignment
72 pixel_data_ = data_.data();
73 if (active_ && !data_.empty()) {
76 if (surface_) {
79 memcpy(surface_->pixels, pixel_data_, data_.size());
81
82 // Apply the copied palette to the new SDL surface
83 if (!palette_.empty()) {
85 }
86 }
87 }
88}
89
91 if (this != &other) {
92 // CRITICAL: Release old resources before replacing to prevent leaks
93 // Queue texture destruction if we have one
94 if (texture_) {
96 this);
97 }
98 // Free old surface through Arena
99 if (surface_) {
101 surface_ = nullptr;
102 }
103
104 width_ = other.width_;
105 height_ = other.height_;
106 depth_ = other.depth_;
107 active_ = other.active_;
108 modified_ = other.modified_;
109 palette_ = other.palette_;
110 data_ = other.data_;
111 // Assign new generation since this is effectively a new bitmap
113
114 // Copy the data and recreate surface/texture
115 pixel_data_ = data_.data();
116 if (active_ && !data_.empty()) {
119 if (surface_) {
122 memcpy(surface_->pixels, pixel_data_, data_.size());
124
125 // Apply the copied palette to the new SDL surface
126 if (!palette_.empty()) {
128 }
129 }
130 }
131 texture_ = nullptr; // Will be recreated on demand
132 }
133 return *this;
134}
135
136Bitmap::Bitmap(Bitmap&& other) noexcept
137 : width_(other.width_),
138 height_(other.height_),
139 depth_(other.depth_),
140 active_(other.active_),
141 modified_(other.modified_),
142 generation_(other.generation_),
143 texture_pixels(other.texture_pixels),
144 pixel_data_(other.pixel_data_),
145 palette_(std::move(other.palette_)),
146 data_(std::move(other.data_)),
147 surface_(other.surface_),
148 texture_(other.texture_) {
149 // Reset the moved-from object
150 other.width_ = 0;
151 other.height_ = 0;
152 other.depth_ = 0;
153 other.active_ = false;
154 other.modified_ = false;
155 other.generation_ = 0;
156 other.texture_pixels = nullptr;
157 other.pixel_data_ = nullptr;
158 other.surface_ = nullptr;
159 other.texture_ = nullptr;
160}
161
162Bitmap& Bitmap::operator=(Bitmap&& other) noexcept {
163 if (this != &other) {
164 // CRITICAL: Release old resources before taking ownership of new ones
165 // Note: We can't queue texture destruction in noexcept move, so we rely on
166 // the Arena's deferred command system to handle stale textures via generation
167 // checking. The old texture will be orphaned but won't cause crashes.
168 // For proper cleanup, prefer copy assignment when explicit resource release
169 // is needed.
170 if (surface_) {
171 Arena::Get().FreeSurface(surface_);
172 }
173
174 width_ = other.width_;
175 height_ = other.height_;
176 depth_ = other.depth_;
177 active_ = other.active_;
178 modified_ = other.modified_;
179 generation_ = other.generation_; // Preserve generation from source
180 texture_pixels = other.texture_pixels;
181 pixel_data_ = other.pixel_data_;
182 palette_ = std::move(other.palette_);
183 data_ = std::move(other.data_);
184 surface_ = other.surface_;
185 texture_ = other.texture_;
186
187 // Reset the moved-from object
188 other.width_ = 0;
189 other.height_ = 0;
190 other.depth_ = 0;
191 other.active_ = false;
192 other.modified_ = false;
193 other.generation_ = 0;
194 other.texture_pixels = nullptr;
195 other.pixel_data_ = nullptr;
196 other.surface_ = nullptr;
197 other.texture_ = nullptr;
198 }
199 return *this;
200}
201
202void Bitmap::Create(int width, int height, int depth, std::span<uint8_t> data) {
203 data_ = std::vector<uint8_t>(data.begin(), data.end());
205}
206
207void Bitmap::Create(int width, int height, int depth,
208 const std::vector<uint8_t>& data) {
209 Create(width, height, depth, static_cast<int>(BitmapFormat::kIndexed), data);
210}
211
226void Bitmap::Create(int width, int height, int depth, int format,
227 const std::vector<uint8_t>& data) {
228 // Treat recreation as a new resource generation before touching either
229 // deferred commands or the current resources. Commands queued for the old
230 // surface/texture will then be discarded as stale by Arena.
232
233 // Preserve an existing texture handle. A caller can queue UPDATE to reuse it
234 // with the new surface. If the caller instead queues CREATE, Arena owns
235 // destroying the old texture immediately before creating its replacement.
236 if (surface_) {
238 surface_ = nullptr;
239 }
240
241 width_ = width;
242 height_ = height;
243 depth_ = depth;
244 data_ = data;
245 pixel_data_ = data_.data();
246 active_ = false;
247
248 if (data.empty()) {
249 SDL_Log("Bitmap data is empty\n");
250 return;
251 }
252
254 GetSnesPixelFormat(format));
255 if (surface_ == nullptr) {
256 SDL_Log("Bitmap::Create.SDL_CreateRGBSurfaceWithFormat failed: %s\n",
257 SDL_GetError());
258 active_ = false;
259 return;
260 }
261
262 // Ensure indexed surfaces have a proper 256-color palette
263 // This fixes issues where SDL3 creates surfaces with smaller default palettes
264 if (format == static_cast<int>(BitmapFormat::kIndexed)) {
266 }
267
268 // CRITICAL FIX: Use proper SDL surface operations instead of direct pointer
269 // assignment Direct assignment breaks SDL's memory management and causes
270 // malloc errors on shutdown
271 if (surface_ && data_.size() > 0) {
273 size_t copy_size = std::min(
274 data_.size(), static_cast<size_t>(surface_->pitch * surface_->h));
277 }
278 active_ = true;
279
280 // Apply the stored palette if one exists
281 if (!palette_.empty()) {
283 }
284}
285
286void Bitmap::Reformat(int format) {
288 GetSnesPixelFormat(format));
289
290 // CRITICAL FIX: Use proper SDL surface operations instead of direct pointer
291 // assignment
292 if (surface_ && data_.size() > 0) {
294 size_t copy_size = std::min(
295 data_.size(), static_cast<size_t>(surface_->pitch * surface_->h));
298 }
299 active_ = true;
301}
302
306
310
328 if (!surface_ || palette_.empty()) {
329 return; // Can't apply without surface or palette
330 }
331
332 // Invalidate palette cache when palette changes
334
335 // For indexed surfaces, ensure palette exists
337 if (sdl_palette == nullptr) {
338 // Non-indexed surface or palette not created - can't apply palette
339 SDL_Log("Warning: Bitmap surface has no palette (non-indexed format?)\n");
340 return;
341 }
342
344
345 // Build SDL color array from SnesPalette
346 // Only set the colors that exist in the palette - don't fill unused entries
347 std::vector<SDL_Color> colors(palette_.size());
348 for (size_t i = 0; i < palette_.size(); ++i) {
349 const auto& pal_color = palette_[i];
350
351 // Get RGB values - stored as 0-255 in ImVec4 (unconventional!)
352 ImVec4 rgb_255 = pal_color.rgb();
353
354 colors[i].r = static_cast<Uint8>(rgb_255.x);
355 colors[i].g = static_cast<Uint8>(rgb_255.y);
356 colors[i].b = static_cast<Uint8>(rgb_255.z);
357
358 // Only apply transparency if explicitly set
359 if (pal_color.is_transparent()) {
360 colors[i].a = 0; // Fully transparent
361 } else {
362 colors[i].a = 255; // Fully opaque
363 }
364 }
365
366 // Apply palette to surface using SDL_SetPaletteColors
367 // Only set the colors we have - leave rest of palette unchanged
368 // This prevents breaking systems that use small palettes (8-16 colors)
369 SDL_SetPaletteColors(sdl_palette, colors.data(), 0,
370 static_cast<int>(palette_.size()));
371
372 // CRITICAL FIX: Enable blending so SDL respects the alpha channel in the palette
373 // Without this, indexed surfaces may ignore transparency
375
377}
378
380 if (!surface_ || data_.empty()) {
381 return;
382 }
383
384 // Copy pixel data from data_ vector to SDL surface
386 if (surface_->pixels && data_.size() > 0) {
387 memcpy(surface_->pixels, data_.data(),
388 std::min(data_.size(),
389 static_cast<size_t>(surface_->pitch * surface_->h)));
390 }
392}
393
394void Bitmap::SetPalette(const SnesPalette& palette) {
395 // Store palette even if surface isn't ready yet
397
398 // Apply it immediately if surface is ready
400
401 // Mark as modified to trigger texture update
402 modified_ = true;
403}
404
423 int sub_palette_index) {
424 if (metadata_.palette_format == 1) {
425 // Sub-palette: need transparent black + 7 colors from palette
426 // Common for 3BPP graphics sheets (title screen, etc.)
427 SetPaletteWithTransparent(palette, sub_palette_index, 7);
428 } else {
429 // Full palette application
430 // Used for 4BPP, Mode 7, and other full-color formats
432 }
433}
434
466void Bitmap::SetPaletteWithTransparent(const SnesPalette& palette, size_t index,
467 int length) {
468 // Store the full palette for reference (not modified)
470
471 // If surface isn't created yet, just store the palette for later
472 if (surface_ == nullptr) {
473 return; // Palette will be applied when surface is created
474 }
475
476 // Validate parameters
477 if (index >= palette.size()) {
478 throw std::invalid_argument("Invalid palette index");
479 }
480
481 if (length < 0 || length > 15) {
482 throw std::invalid_argument(
483 "Invalid palette length (must be 0-15 for SNES palettes)");
484 }
485
486 if (index + length > palette.size()) {
487 throw std::invalid_argument("Palette index + length exceeds size");
488 }
489
490 // Build SNES sub-palette (up to 16 colors: transparent + length entries)
491 std::vector<ImVec4> colors;
492
493 // Color 0: Transparent (SNES hardware requirement)
494 colors.push_back(ImVec4(0, 0, 0, 0)); // Transparent black
495
496 // Colors 1-15: Extract from source palette
497 // NOTE: palette[i].rgb() returns 0-255 values in ImVec4 (unconventional!)
498 for (size_t i = 0;
499 i < static_cast<size_t>(length) && (index + i) < palette.size(); ++i) {
500 const auto& pal_color = palette[index + i];
501 ImVec4 rgb_255 = pal_color.rgb(); // 0-255 range (unconventional storage)
502
503 // Convert to standard ImVec4 0-1 range for SDL
504 colors.push_back(ImVec4(rgb_255.x / 255.0f, rgb_255.y / 255.0f,
505 rgb_255.z / 255.0f, 1.0f)); // Always opaque
506 }
507
508 // Ensure we have exactly 1 + length colors (transparent + requested entries)
509 while (colors.size() < static_cast<size_t>(length + 1)) {
510 colors.push_back(ImVec4(0, 0, 0, 1.0f)); // Fill with opaque black
511 }
512
513 // Update palette cache with full palette (for color lookup)
515
516 // Apply the SNES sub-palette to SDL surface (supports 3bpp=8 and 4bpp=16)
519 if (!sdl_palette) {
520 SDL_Log("Warning: Bitmap surface has no palette (non-indexed format?)\n");
522 return;
523 }
524 const int num_colors = static_cast<int>(colors.size());
525 for (int color_index = 0; color_index < num_colors; ++color_index) {
527 sdl_palette->colors[color_index].r =
528 static_cast<Uint8>(colors[color_index].x * 255.0f);
529 sdl_palette->colors[color_index].g =
530 static_cast<Uint8>(colors[color_index].y * 255.0f);
531 sdl_palette->colors[color_index].b =
532 static_cast<Uint8>(colors[color_index].z * 255.0f);
533 sdl_palette->colors[color_index].a =
534 static_cast<Uint8>(colors[color_index].w * 255.0f);
535 }
536 }
538
539 // CRITICAL FIX: Enable RLE acceleration and set color key for transparency
540 // SDL ignores palette alpha for INDEX8 unless color key is set or blending is enabled
543}
544
545void Bitmap::SetPalette(const std::vector<SDL_Color>& palette) {
546 // CRITICAL: Validate surface and palette before accessing
547 if (!surface_) {
548 return;
549 }
550
551 // Ensure surface has a proper 256-color palette before setting colors
552 // This fixes issues where SDL creates surfaces with smaller default palettes
554
556 if (!sdl_palette) {
557 SDL_Log("Warning: SetPalette - surface has no palette!");
558 return;
559 }
560
561 int max_colors = sdl_palette->ncolors;
562 int colors_to_set = static_cast<int>(palette.size());
563
564 // Debug: Check if palette capacity is sufficient (should be 256 after EnsureSurfacePalette256)
565 if (max_colors < colors_to_set) {
566 SDL_Log(
567 "Warning: SetPalette - SDL palette has %d colors, trying to set %d. "
568 "Colors above %d may not display correctly.",
569 max_colors, colors_to_set, max_colors);
570 colors_to_set = max_colors; // Clamp to available space
571 }
572
574
575 // Use SDL_SetPaletteColors for proper palette setting
576 // This is more reliable than direct array access
578 0) {
579 SDL_Log("Warning: SDL_SetPaletteColors failed: %s", SDL_GetError());
580 // Fall back to manual setting
581 for (int i = 0; i < colors_to_set; ++i) {
582 sdl_palette->colors[i].r = palette[i].r;
583 sdl_palette->colors[i].g = palette[i].g;
584 sdl_palette->colors[i].b = palette[i].b;
585 sdl_palette->colors[i].a = palette[i].a;
586 }
587 }
588
590}
591
592void Bitmap::WriteToPixel(int position, uint8_t value) {
593 // Bounds checking to prevent crashes
594 if (position < 0 || position >= static_cast<int>(data_.size())) {
595 SDL_Log("ERROR: WriteToPixel - position %d out of bounds (size: %zu)",
596 position, data_.size());
597 return;
598 }
599
600 // Safety check: ensure bitmap is active and has valid data
601 if (!active_ || data_.empty()) {
602 SDL_Log(
603 "ERROR: WriteToPixel - bitmap not active or data empty (active=%s, "
604 "size=%zu)",
605 active_ ? "true" : "false", data_.size());
606 return;
607 }
608
609 if (pixel_data_ == nullptr) {
610 pixel_data_ = data_.data();
611 }
612
613 // Safety check: ensure surface exists and is valid
614 if (!surface_ || !surface_->pixels) {
615 SDL_Log(
616 "ERROR: WriteToPixel - surface or pixels are null (surface=%p, "
617 "pixels=%p)",
618 surface_, surface_ ? surface_->pixels : nullptr);
619 return;
620 }
621
622 // Additional validation: ensure pixel_data_ is valid
623 if (pixel_data_ == nullptr) {
624 SDL_Log("ERROR: WriteToPixel - pixel_data_ is null after assignment");
625 return;
626 }
627
628 // CRITICAL FIX: Update both data_ and surface_ properly
629 data_[position] = value;
630 pixel_data_[position] = value;
631
632 // Update surface if it exists
633 if (surface_) {
635 static_cast<uint8_t*>(surface_->pixels)[position] = value;
637 }
638
639 // Mark as modified for traditional update path
640 modified_ = true;
641}
642
643void Bitmap::WriteColor(int position, const ImVec4& color) {
644 // Bounds checking to prevent crashes
645 if (position < 0 || position >= static_cast<int>(data_.size())) {
646 return;
647 }
648
649 // Safety check: ensure bitmap is active and has valid data
650 if (!active_ || data_.empty()) {
651 return;
652 }
653
654 // Safety check: ensure surface exists and is valid
655 if (!surface_ || !surface_->pixels) {
656 return;
657 }
658
659 // Convert ImVec4 (RGBA) to SDL_Color (RGBA)
661 sdl_color.r = static_cast<Uint8>(color.x * 255);
662 sdl_color.g = static_cast<Uint8>(color.y * 255);
663 sdl_color.b = static_cast<Uint8>(color.z * 255);
664 sdl_color.a = static_cast<Uint8>(color.w * 255);
665
666 // Map SDL_Color to the nearest color index in the surface's palette
667 Uint8 index = static_cast<Uint8>(
669
670 // CRITICAL FIX: Update both data_ and surface_ properly
671 if (pixel_data_ == nullptr) {
672 pixel_data_ = data_.data();
673 }
674 data_[position] = ConvertRgbToSnes(color);
675 pixel_data_[position] = index;
676
677 // Update surface if it exists
678 if (surface_) {
680 static_cast<uint8_t*>(surface_->pixels)[position] = index;
682 }
683
684 modified_ = true;
685}
686
687void Bitmap::Get8x8Tile(int tile_index, int x, int y,
688 std::vector<uint8_t>& tile_data,
689 int& tile_data_offset) {
690 int tile_offset = tile_index * (width_ * height_);
691 int tile_x = (x * 8) % width_;
692 int tile_y = (y * 8) % height_;
693 for (int i = 0; i < 8; i++) {
694 for (int j = 0; j < 8; j++) {
695 int pixel_offset = tile_offset + (tile_y + i) * width_ + tile_x + j;
697 tile_data[tile_data_offset] = pixel_value;
699 }
700 }
701}
702
703void Bitmap::Get16x16Tile(int tile_x, int tile_y,
704 std::vector<uint8_t>& tile_data,
705 int& tile_data_offset) {
706 for (int ty = 0; ty < 16; ty++) {
707 for (int tx = 0; tx < 16; tx++) {
708 // Calculate the pixel position in the bitmap
709 int pixel_x = tile_x + tx;
710 int pixel_y = tile_y + ty;
711 int pixel_offset = (pixel_y * width_) + pixel_x;
713
714 // Store the pixel value in the tile data
715 tile_data[tile_data_offset] = pixel_value;
717 }
718 }
719}
720
737void Bitmap::SetPixel(int x, int y, const SnesColor& color) {
739 return; // Bounds check
740 }
741
742 int position = y * width_ + x;
743 if (position >= 0 && position < static_cast<int>(data_.size())) {
744 uint8_t color_index = FindColorIndex(color);
745 data_[position] = color_index;
746
747 // Update pixel_data_ to maintain consistency
748 if (pixel_data_) {
749 pixel_data_[position] = color_index;
750 }
751
752 // Update surface if it exists
753 if (surface_) {
755 static_cast<uint8_t*>(surface_->pixels)[position] = color_index;
757 }
758
759 // Update dirty region for efficient texture updates
761 modified_ = true;
762 }
763}
764
765void Bitmap::Resize(int new_width, int new_height) {
766 if (new_width <= 0 || new_height <= 0) {
767 return; // Invalid dimensions
768 }
769
770 std::vector<uint8_t> new_data(new_width * new_height, 0);
771
772 // Copy existing data, handling size changes
773 if (!data_.empty()) {
774 for (int y = 0; y < std::min(height_, new_height); y++) {
775 for (int x = 0; x < std::min(width_, new_width); x++) {
776 int old_pos = y * width_ + x;
777 int new_pos = y * new_width + x;
778 if (old_pos < (int)data_.size() && new_pos < (int)new_data.size()) {
779 new_data[new_pos] = data_[old_pos];
780 }
781 }
782 }
783 }
784
787 data_ = std::move(new_data);
788 pixel_data_ = data_.data();
789
790 // Recreate surface with new dimensions
793 if (surface_) {
795 memcpy(surface_->pixels, pixel_data_, data_.size());
797 active_ = true;
798 } else {
799 active_ = false;
800 }
801
802 modified_ = true;
803}
804
815uint32_t Bitmap::HashColor(const ImVec4& color) {
816 // Convert float values to integers for consistent hashing
817 uint32_t r = static_cast<uint32_t>(color.x * 255.0F) & 0xFF;
818 uint32_t g = static_cast<uint32_t>(color.y * 255.0F) & 0xFF;
819 uint32_t b = static_cast<uint32_t>(color.z * 255.0F) & 0xFF;
820 uint32_t a = static_cast<uint32_t>(color.w * 255.0F) & 0xFF;
821
822 // Simple hash combining all components
823 return (r << 24) | (g << 16) | (b << 8) | a;
824}
825
837 color_to_index_cache_.clear();
838
839 // Rebuild cache with current palette
840 for (size_t i = 0; i < palette_.size(); i++) {
842 color_to_index_cache_[color_hash] = static_cast<uint8_t>(i);
843 }
844}
845
857uint8_t Bitmap::FindColorIndex(const SnesColor& color) {
858 ScopedTimer timer("palette_lookup_optimized");
859 uint32_t hash = HashColor(color.rgb());
860 auto it = color_to_index_cache_.find(hash);
861 return (it != color_to_index_cache_.end()) ? it->second : 0;
862}
863
864void Bitmap::set_data(const std::vector<uint8_t>& data) {
865 // Validate input data
866 if (data.empty()) {
867 SDL_Log("Warning: set_data called with empty data vector");
868 return;
869 }
870
871 data_ = data;
872 pixel_data_ = data_.data();
873
874 // CRITICAL FIX: Use proper SDL surface operations instead of direct pointer
875 // assignment
876 if (surface_ && !data_.empty()) {
878 memcpy(surface_->pixels, pixel_data_, data_.size());
880 }
881
882 modified_ = true;
883}
884
886 if (!surface_ || !surface_->pixels || data_.empty()) {
887 SDL_Log("ValidateDataSurfaceSync: surface or data is null/empty");
888 return false;
889 }
890
891 // Check if data and surface are synchronized
892 size_t surface_size = static_cast<size_t>(surface_->h * surface_->pitch);
893 size_t data_size = data_.size();
894 size_t compare_size = std::min(data_size, surface_size);
895
896 if (compare_size == 0) {
897 SDL_Log("ValidateDataSurfaceSync: invalid sizes - surface: %zu, data: %zu",
898 surface_size, data_size);
899 return false;
900 }
901
902 // Compare first few bytes to check synchronization
903 if (memcmp(surface_->pixels, data_.data(), compare_size) != 0) {
904 SDL_Log("ValidateDataSurfaceSync: data and surface are not synchronized");
905 return false;
906 }
907
908 return true;
909}
910
911} // namespace gfx
912} // namespace yaze
SDL_Surface * AllocateSurface(int width, int height, int depth, int format)
Definition arena.cc:383
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:36
void FreeSurface(SDL_Surface *surface)
Definition arena.cc:413
static Arena & Get()
Definition arena.cc:21
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:67
const uint8_t * data() const
Definition bitmap.h:398
const SnesPalette & palette() const
Definition bitmap.h:389
SDL_Surface * surface_
SDL surface for rendering (contains the authoritative palette)
Definition bitmap.h:476
Bitmap & operator=(const Bitmap &other)
Copy assignment operator.
Definition bitmap.cc:90
void WriteToPixel(int position, uint8_t value)
Write a value to a pixel at the given position.
Definition bitmap.cc:592
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
Definition bitmap.cc:202
bool ValidateDataSurfaceSync()
Validate that bitmap data and surface pixels are synchronized.
Definition bitmap.cc:885
const std::vector< uint8_t > & vector() const
Definition bitmap.h:402
void UpdateSurfacePixels()
Update SDL surface with current pixel data from data_ vector Call this after modifying pixel data via...
Definition bitmap.cc:379
std::unordered_map< uint32_t, uint8_t > color_to_index_cache_
Definition bitmap.h:482
void Reformat(int format)
Reformat the bitmap to use a different pixel format.
Definition bitmap.cc:286
uint8_t * pixel_data_
Definition bitmap.h:429
static uint32_t HashColor(const ImVec4 &color)
Hash a color for cache lookup.
Definition bitmap.cc:815
void Get8x8Tile(int tile_index, int x, int y, std::vector< uint8_t > &tile_data, int &tile_data_offset)
Extract an 8x8 tile from the bitmap (SNES standard tile size)
Definition bitmap.cc:687
void CreateTexture()
Creates the underlying SDL_Texture to be displayed.
Definition bitmap.cc:303
uint32_t generation_
Definition bitmap.h:422
void WriteColor(int position, const ImVec4 &color)
Write a color to a pixel at the given position.
Definition bitmap.cc:643
int height() const
Definition bitmap.h:395
void set_data(const std::vector< uint8_t > &data)
Definition bitmap.cc:864
void Resize(int new_width, int new_height)
Resize the bitmap to new dimensions (preserves existing data)
Definition bitmap.cc:765
static uint32_t next_generation_
Definition bitmap.h:423
void SetPixel(int x, int y, const SnesColor &color)
Set a pixel at the given x,y coordinates with SNES color.
Definition bitmap.cc:737
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
Definition bitmap.cc:394
int width() const
Definition bitmap.h:394
BitmapMetadata metadata_
Definition bitmap.h:448
void ApplyStoredPalette()
Apply the stored palette to the surface (internal helper)
Definition bitmap.cc:327
std::vector< uint8_t > data_
Definition bitmap.h:451
int depth() const
Definition bitmap.h:396
void InvalidatePaletteCache()
Invalidate the palette lookup cache (call when palette changes)
Definition bitmap.cc:836
void SetPaletteWithTransparent(const SnesPalette &palette, size_t index, int length=7)
Set the palette with a transparent color.
Definition bitmap.cc:466
struct yaze::gfx::Bitmap::DirtyRegion dirty_region_
void ApplyPaletteByMetadata(const SnesPalette &palette, int sub_palette_index=0)
Apply palette using metadata-driven strategy Chooses between SetPalette and SetPaletteWithTransparent...
Definition bitmap.cc:422
TextureHandle texture_
Definition bitmap.h:479
void Get16x16Tile(int tile_x, int tile_y, std::vector< uint8_t > &tile_data, int &tile_data_offset)
Extract a 16x16 tile from the bitmap (SNES metatile size)
Definition bitmap.cc:703
uint8_t FindColorIndex(const SnesColor &color)
Find color index in palette using optimized hash map lookup.
Definition bitmap.cc:857
void UpdateTexture()
Updates the underlying SDL_Texture when it already exists.
Definition bitmap.cc:307
gfx::SnesPalette palette_
Internal SNES palette storage (may be empty!)
Definition bitmap.h:445
RAII timer for automatic timing management.
SNES Color container.
Definition snes_color.h:110
constexpr ImVec4 rgb() const
Get RGB values (WARNING: stored as 0-255 in ImVec4)
Definition snes_color.h:183
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
@ kIndexed
Definition bitmap.h:36
uint16_t ConvertRgbToSnes(const snes_color &color)
Convert RGB (0-255) to SNES 15-bit color.
Definition snes_color.cc:33
constexpr Uint32 SNES_PIXELFORMAT_8BPP
Definition bitmap.h:31
constexpr Uint32 SNES_PIXELFORMAT_4BPP
Definition bitmap.h:27
Uint32 GetSnesPixelFormat(int format)
Convert bitmap format enum to SDL pixel format.
Definition bitmap.cc:33
Uint32 MapRGB(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b)
Map an RGB color to the surface's pixel format.
Definition sdl_compat.h:421
SDL_Palette * GetSurfacePalette(SDL_Surface *surface)
Get the palette attached to a surface.
Definition sdl_compat.h:392
bool EnsureSurfacePalette256(SDL_Surface *surface)
Ensure the surface has a proper 256-color palette for indexed formats.
Definition sdl_compat.h:476
SDL2/SDL3 compatibility layer.
void AddPoint(int x, int y)
Definition bitmap.h:494