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:
42 return SDL_PIXELFORMAT_RGBA32;
43 default:
44 return SDL_PIXELFORMAT_INDEX8;
45 }
46}
47
48Bitmap::Bitmap(int width, int height, int depth,
49 const std::vector<uint8_t>& data)
50 : width_(width), height_(height), depth_(depth), data_(data) {
52}
53
54Bitmap::Bitmap(int width, int height, int depth,
55 const std::vector<uint8_t>& data, const SnesPalette& palette)
56 : width_(width),
57 height_(height),
58 depth_(depth),
59 palette_(palette),
60 data_(data) {
63}
64
66 : width_(other.width_),
67 height_(other.height_),
68 depth_(other.depth_),
69 active_(other.active_),
70 modified_(other.modified_),
71 palette_(other.palette_),
72 metadata_(other.metadata_),
73 data_(other.data_) {
74 // Copy the data and recreate surface/texture with simple assignment
75 pixel_data_ = data_.data();
76 if (active_ && !data_.empty()) {
81 if (surface_) {
83 CopySurfaceState(other);
84 }
85 }
86}
87
89 if (this != &other) {
90 // CRITICAL: Release old resources before replacing to prevent leaks
91 // Queue texture destruction if we have one
92 if (texture_) {
94 this);
95 }
96 // Free old surface through Arena
97 if (surface_) {
99 surface_ = nullptr;
100 }
101
102 width_ = other.width_;
103 height_ = other.height_;
104 depth_ = other.depth_;
105 active_ = other.active_;
106 modified_ = other.modified_;
107 palette_ = other.palette_;
108 metadata_ = other.metadata_;
109 data_ = other.data_;
110 // Assign new generation since this is effectively a new bitmap
112
113 // Copy the data and recreate surface/texture
114 pixel_data_ = data_.data();
115 if (active_ && !data_.empty()) {
120 if (surface_) {
122 CopySurfaceState(other);
123 }
124 }
125 texture_ = nullptr; // Will be recreated on demand
126 }
127 return *this;
128}
129
130Bitmap::Bitmap(Bitmap&& other) noexcept
131 : width_(other.width_),
132 height_(other.height_),
133 depth_(other.depth_),
134 active_(other.active_),
135 modified_(other.modified_),
136 generation_(other.generation_),
137 texture_pixels(other.texture_pixels),
138 pixel_data_(other.pixel_data_),
139 palette_(std::move(other.palette_)),
140 metadata_(std::move(other.metadata_)),
141 data_(std::move(other.data_)),
142 surface_(other.surface_),
143 texture_(other.texture_) {
144 // Reset the moved-from object
145 other.width_ = 0;
146 other.height_ = 0;
147 other.depth_ = 0;
148 other.active_ = false;
149 other.modified_ = false;
150 other.generation_ = 0;
151 other.texture_pixels = nullptr;
152 other.pixel_data_ = nullptr;
153 other.surface_ = nullptr;
154 other.texture_ = nullptr;
155}
156
157Bitmap& Bitmap::operator=(Bitmap&& other) noexcept {
158 if (this != &other) {
159 // CRITICAL: Release old resources before taking ownership of new ones
160 // Note: We can't queue texture destruction in noexcept move, so we rely on
161 // the Arena's deferred command system to handle stale textures via generation
162 // checking. The old texture will be orphaned but won't cause crashes.
163 // For proper cleanup, prefer copy assignment when explicit resource release
164 // is needed.
165 if (surface_) {
166 Arena::Get().FreeSurface(surface_);
167 }
168
169 width_ = other.width_;
170 height_ = other.height_;
171 depth_ = other.depth_;
172 active_ = other.active_;
173 modified_ = other.modified_;
174 generation_ = other.generation_; // Preserve generation from source
175 texture_pixels = other.texture_pixels;
176 pixel_data_ = other.pixel_data_;
177 palette_ = std::move(other.palette_);
178 metadata_ = std::move(other.metadata_);
179 data_ = std::move(other.data_);
180 surface_ = other.surface_;
181 texture_ = other.texture_;
182
183 // Reset the moved-from object
184 other.width_ = 0;
185 other.height_ = 0;
186 other.depth_ = 0;
187 other.active_ = false;
188 other.modified_ = false;
189 other.generation_ = 0;
190 other.texture_pixels = nullptr;
191 other.pixel_data_ = nullptr;
192 other.surface_ = nullptr;
193 other.texture_ = nullptr;
194 }
195 return *this;
196}
197
198void Bitmap::Create(int width, int height, int depth, std::span<uint8_t> data) {
199 data_ = std::vector<uint8_t>(data.begin(), data.end());
201}
202
203void Bitmap::Create(int width, int height, int depth,
204 const std::vector<uint8_t>& data) {
207}
208
223void Bitmap::Create(int width, int height, int depth, int format,
224 const std::vector<uint8_t>& data) {
225 // Treat recreation as a new resource generation before touching either
226 // deferred commands or the current resources. Commands queued for the old
227 // surface/texture will then be discarded as stale by Arena.
229
230 // Preserve an existing texture handle. A caller can queue UPDATE to reuse it
231 // with the new surface. If the caller instead queues CREATE, Arena owns
232 // destroying the old texture immediately before creating its replacement.
233 if (surface_) {
235 surface_ = nullptr;
236 }
237
238 width_ = width;
239 height_ = height;
240 depth_ = depth;
241 data_ = data;
242 pixel_data_ = data_.data();
243 active_ = false;
244
245 if (data.empty()) {
246 SDL_Log("Bitmap data is empty\n");
247 return;
248 }
249
251 GetSnesPixelFormat(format));
252 if (surface_ == nullptr) {
253 SDL_Log("Bitmap::Create.SDL_CreateRGBSurfaceWithFormat failed: %s\n",
254 SDL_GetError());
255 active_ = false;
256 return;
257 }
258
259 // Pooled surfaces may retain the previous view's presentation state.
261 SDL_SetSurfaceColorMod(surface_, 255, 255, 255);
264
265 // Ensure indexed surfaces have a proper 256-color palette
266 // This fixes issues where SDL3 creates surfaces with smaller default palettes
267 if (format == static_cast<int>(BitmapFormat::kIndexed)) {
269 }
270
272 active_ = true;
273
274 // Apply the stored palette if one exists
275 if (IsIndexed() && !palette_.empty()) {
277 }
278}
279
280void Bitmap::Reformat(int format) {
282 GetSnesPixelFormat(format));
283
284 // CRITICAL FIX: Use proper SDL surface operations instead of direct pointer
285 // assignment
286 if (surface_ && data_.size() > 0) {
288 size_t copy_size = std::min(
289 data_.size(), static_cast<size_t>(surface_->pitch * surface_->h));
292 }
293 active_ = true;
295}
296
300
304
322 if (!surface_ || !IsIndexed() || palette_.empty()) {
323 return; // Can't apply without surface or palette
324 }
325
326 // Invalidate palette cache when palette changes
328
329 // For indexed surfaces, ensure palette exists
331 if (sdl_palette == nullptr) {
332 // Non-indexed surface or palette not created - can't apply palette
333 SDL_Log("Warning: Bitmap surface has no palette (non-indexed format?)\n");
334 return;
335 }
336
338
339 // Build SDL color array from SnesPalette
340 // Only set the colors that exist in the palette - don't fill unused entries
341 std::vector<SDL_Color> colors(palette_.size());
342 for (size_t i = 0; i < palette_.size(); ++i) {
343 const auto& pal_color = palette_[i];
344
345 // Get RGB values - stored as 0-255 in ImVec4 (unconventional!)
346 ImVec4 rgb_255 = pal_color.rgb();
347
348 colors[i].r = static_cast<Uint8>(rgb_255.x);
349 colors[i].g = static_cast<Uint8>(rgb_255.y);
350 colors[i].b = static_cast<Uint8>(rgb_255.z);
351
352 // Only apply transparency if explicitly set
353 if (pal_color.is_transparent()) {
354 colors[i].a = 0; // Fully transparent
355 } else {
356 colors[i].a = 255; // Fully opaque
357 }
358 }
359
360 // Apply palette to surface using SDL_SetPaletteColors
361 // Only set the colors we have - leave rest of palette unchanged
362 // This prevents breaking systems that use small palettes (8-16 colors)
363 SDL_SetPaletteColors(sdl_palette, colors.data(), 0,
364 static_cast<int>(palette_.size()));
365
366 // CRITICAL FIX: Enable blending so SDL respects the alpha channel in the palette
367 // Without this, indexed surfaces may ignore transparency
369
371}
372
374 if (!surface_ || !surface_->pixels || data_.empty()) {
375 return;
376 }
377 const size_t row_bytes = static_cast<size_t>(width_) * (IsIndexed() ? 1 : 4);
379 for (int y = 0; y < height_; ++y) {
380 const size_t offset = static_cast<size_t>(y) * row_bytes;
381 if (offset >= data_.size())
382 break;
383 memcpy(static_cast<uint8_t*>(surface_->pixels) + y * surface_->pitch,
384 data_.data() + offset,
385 std::min({row_bytes, data_.size() - offset,
386 static_cast<size_t>(surface_->pitch)}));
387 }
389}
390
392 if (!surface_ || !other.surface_)
393 return;
394 if (const auto* palette = platform::GetSurfacePalette(other.surface_)) {
395 SetPalette(std::vector<SDL_Color>(palette->colors,
396 palette->colors + palette->ncolors));
397 }
398 Uint32 key;
399 if (SDL_GetColorKey(other.surface_, &key) == 0) {
401 } else {
403 }
404 Uint8 r, g, b, a;
405 SDL_GetSurfaceColorMod(other.surface_, &r, &g, &b);
412}
413
414SDL_Color Bitmap::ReadColor(int x, int y) const {
415 if (x < 0 || y < 0 || x >= width_ || y >= height_)
416 return {0, 0, 0, 0};
417 const size_t pixel = static_cast<size_t>(y) * width_ + x;
418 if (!IsIndexed()) {
419 const size_t offset = pixel * 4;
420 if (offset + 3 >= data_.size())
421 return {0, 0, 0, 0};
422 return {data_[offset], data_[offset + 1], data_[offset + 2],
423 data_[offset + 3]};
424 }
426 if (pixel >= data_.size() || !palette || data_[pixel] >= palette->ncolors)
427 return {0, 0, 0, 0};
428 SDL_Color color = palette->colors[data_[pixel]];
429 Uint32 key;
430 if (SDL_GetColorKey(surface_, &key) == 0 && key == data_[pixel])
431 color.a = 0;
432 return color;
433}
434
435std::vector<uint8_t> Bitmap::ToRgba() const {
436 if (width_ <= 0 || height_ <= 0)
437 return {};
438 std::vector<uint8_t> result(static_cast<size_t>(width_) * height_ * 4);
439 for (int y = 0; y < height_; ++y) {
440 for (int x = 0; x < width_; ++x) {
441 const auto color = ReadColor(x, y);
442 const size_t i = (static_cast<size_t>(y) * width_ + x) * 4;
443 result[i] = color.r;
444 result[i + 1] = color.g;
445 result[i + 2] = color.b;
446 result[i + 3] = color.a;
447 }
448 }
449 return result;
450}
451
452void Bitmap::SetPalette(const SnesPalette& palette) {
453 // Store palette even if surface isn't ready yet
455
456 // Apply it immediately if surface is ready
458
459 // Mark as modified to trigger texture update
460 modified_ = true;
461}
462
481 int sub_palette_index) {
482 if (metadata_.palette_format == 1) {
483 // Sub-palette: need transparent black + 7 colors from palette
484 // Common for 3BPP graphics sheets (title screen, etc.)
486 } else {
487 // Full palette application
488 // Used for 4BPP, Mode 7, and other full-color formats
490 }
491}
492
524void Bitmap::SetPaletteWithTransparent(const SnesPalette& palette, size_t index,
525 int length) {
526 // Store the full palette for reference (not modified)
528
529 // If surface isn't created yet, just store the palette for later
530 if (surface_ == nullptr) {
531 return; // Palette will be applied when surface is created
532 }
533
534 // Validate parameters
535 if (index >= palette.size()) {
536 throw std::invalid_argument("Invalid palette index");
537 }
538
539 if (length < 0 || length > 15) {
540 throw std::invalid_argument(
541 "Invalid palette length (must be 0-15 for SNES palettes)");
542 }
543
544 if (index + length > palette.size()) {
545 throw std::invalid_argument("Palette index + length exceeds size");
546 }
547
548 // Build SNES sub-palette (up to 16 colors: transparent + length entries)
549 std::vector<ImVec4> colors;
550
551 // Color 0: Transparent (SNES hardware requirement)
552 colors.push_back(ImVec4(0, 0, 0, 0)); // Transparent black
553
554 // Colors 1-15: Extract from source palette
555 // NOTE: palette[i].rgb() returns 0-255 values in ImVec4 (unconventional!)
556 for (size_t i = 0;
557 i < static_cast<size_t>(length) && (index + i) < palette.size(); ++i) {
558 const auto& pal_color = palette[index + i];
559 ImVec4 rgb_255 = pal_color.rgb(); // 0-255 range (unconventional storage)
560
561 // Convert to standard ImVec4 0-1 range for SDL
562 colors.push_back(ImVec4(rgb_255.x / 255.0f, rgb_255.y / 255.0f,
563 rgb_255.z / 255.0f, 1.0f)); // Always opaque
564 }
565
566 // Ensure we have exactly 1 + length colors (transparent + requested entries)
567 while (colors.size() < static_cast<size_t>(length + 1)) {
568 colors.push_back(ImVec4(0, 0, 0, 1.0f)); // Fill with opaque black
569 }
570
571 // Update palette cache with full palette (for color lookup)
573
574 // Apply the SNES sub-palette to SDL surface (supports 3bpp=8 and 4bpp=16)
577 if (!sdl_palette) {
578 SDL_Log("Warning: Bitmap surface has no palette (non-indexed format?)\n");
580 return;
581 }
582 const int num_colors = static_cast<int>(colors.size());
583 for (int color_index = 0; color_index < num_colors; ++color_index) {
585 sdl_palette->colors[color_index].r =
586 static_cast<Uint8>(colors[color_index].x * 255.0f);
587 sdl_palette->colors[color_index].g =
588 static_cast<Uint8>(colors[color_index].y * 255.0f);
589 sdl_palette->colors[color_index].b =
590 static_cast<Uint8>(colors[color_index].z * 255.0f);
591 sdl_palette->colors[color_index].a =
592 static_cast<Uint8>(colors[color_index].w * 255.0f);
593 }
594 }
596
597 // CRITICAL FIX: Enable RLE acceleration and set color key for transparency
598 // SDL ignores palette alpha for INDEX8 unless color key is set or blending is enabled
601}
602
603void Bitmap::SetPalette(const std::vector<SDL_Color>& palette) {
604 // Render palette changes also invalidate derived lookup and GPU caches.
605 if (!surface_ || !IsIndexed()) {
606 return;
607 }
608
610 modified_ = true;
611
612 // Ensure surface has a proper 256-color palette before setting colors
613 // This fixes issues where SDL creates surfaces with smaller default palettes
615
617 if (!sdl_palette) {
618 SDL_Log("Warning: SetPalette - surface has no palette!");
619 return;
620 }
621
622 int max_colors = sdl_palette->ncolors;
623 int colors_to_set = static_cast<int>(palette.size());
624
625 // Debug: Check if palette capacity is sufficient (should be 256 after EnsureSurfacePalette256)
626 if (max_colors < colors_to_set) {
627 SDL_Log(
628 "Warning: SetPalette - SDL palette has %d colors, trying to set %d. "
629 "Colors above %d may not display correctly.",
630 max_colors, colors_to_set, max_colors);
631 colors_to_set = max_colors; // Clamp to available space
632 }
633
634 // Use SDL_SetPaletteColors for proper palette setting
635 // This is more reliable than direct array access
637 0) {
638 SDL_Log("Warning: SDL_SetPaletteColors failed: %s", SDL_GetError());
639 // Fall back to manual setting
640 for (int i = 0; i < colors_to_set; ++i) {
641 sdl_palette->colors[i].r = palette[i].r;
642 sdl_palette->colors[i].g = palette[i].g;
643 sdl_palette->colors[i].b = palette[i].b;
644 sdl_palette->colors[i].a = palette[i].a;
645 }
646 }
647}
648
649void Bitmap::WriteToPixel(int position, uint8_t value) {
650 if (!IsIndexed())
651 return; // Presentation composites are not editable source indices.
652
653 // Bounds checking to prevent crashes
654 if (position < 0 || position >= static_cast<int>(data_.size())) {
655 SDL_Log("ERROR: WriteToPixel - position %d out of bounds (size: %zu)",
656 position, data_.size());
657 return;
658 }
659
660 // Safety check: ensure bitmap is active and has valid data
661 if (!active_ || data_.empty()) {
662 SDL_Log(
663 "ERROR: WriteToPixel - bitmap not active or data empty (active=%s, "
664 "size=%zu)",
665 active_ ? "true" : "false", data_.size());
666 return;
667 }
668
669 if (pixel_data_ == nullptr) {
670 pixel_data_ = data_.data();
671 }
672
673 // Safety check: ensure surface exists and is valid
674 if (!surface_ || !surface_->pixels) {
675 SDL_Log(
676 "ERROR: WriteToPixel - surface or pixels are null (surface=%p, "
677 "pixels=%p)",
678 surface_, surface_ ? surface_->pixels : nullptr);
679 return;
680 }
681
682 // Additional validation: ensure pixel_data_ is valid
683 if (pixel_data_ == nullptr) {
684 SDL_Log("ERROR: WriteToPixel - pixel_data_ is null after assignment");
685 return;
686 }
687
688 // CRITICAL FIX: Update both data_ and surface_ properly
689 data_[position] = value;
690 pixel_data_[position] = value;
691
692 // Update surface if it exists
693 if (surface_) {
695 static_cast<uint8_t*>(
696 surface_->pixels)[(position / width_) * surface_->pitch +
697 position % width_] = value;
699 }
700
701 // Mark as modified for traditional update path
702 modified_ = true;
703}
704
705void Bitmap::WriteColor(int position, const ImVec4& color) {
706 if (!IsIndexed() || !surface_)
707 return;
708 // RGB-to-index selection is an explicit authoring operation. The selected
709 // index, never the SNES color word, is what belongs in the pixel buffer.
710 const auto index = static_cast<uint8_t>(platform::MapRGB(
711 surface_, static_cast<Uint8>(color.x * 255),
712 static_cast<Uint8>(color.y * 255), static_cast<Uint8>(color.z * 255)));
713 WriteToPixel(position, index);
714}
715
716void Bitmap::Get8x8Tile(int tile_index, int x, int y,
717 std::vector<uint8_t>& tile_data,
718 int& tile_data_offset) {
719 int tile_offset = tile_index * (width_ * height_);
720 int tile_x = (x * 8) % width_;
721 int tile_y = (y * 8) % height_;
722 for (int i = 0; i < 8; i++) {
723 for (int j = 0; j < 8; j++) {
724 int pixel_offset = tile_offset + (tile_y + i) * width_ + tile_x + j;
726 tile_data[tile_data_offset] = pixel_value;
728 }
729 }
730}
731
732void Bitmap::Get16x16Tile(int tile_x, int tile_y,
733 std::vector<uint8_t>& tile_data,
734 int& tile_data_offset) {
735 for (int ty = 0; ty < 16; ty++) {
736 for (int tx = 0; tx < 16; tx++) {
737 // Calculate the pixel position in the bitmap
738 int pixel_x = tile_x + tx;
739 int pixel_y = tile_y + ty;
740 int pixel_offset = (pixel_y * width_) + pixel_x;
742
743 // Store the pixel value in the tile data
744 tile_data[tile_data_offset] = pixel_value;
746 }
747 }
748}
749
766void Bitmap::SetPixel(int x, int y, const SnesColor& color) {
767 if (!IsIndexed())
768 return; // Presentation composites are not editable source indices.
769
771 return; // Bounds check
772 }
773
774 int position = y * width_ + x;
775 if (position >= 0 && position < static_cast<int>(data_.size())) {
776 uint8_t color_index = FindColorIndex(color);
777 data_[position] = color_index;
778
779 // Update pixel_data_ to maintain consistency
780 if (pixel_data_) {
781 pixel_data_[position] = color_index;
782 }
783
784 // Update surface if it exists
785 if (surface_) {
787 static_cast<uint8_t*>(surface_->pixels)[y * surface_->pitch + x] =
788 color_index;
790 }
791
792 // Update dirty region for efficient texture updates
794 modified_ = true;
795 }
796}
797
798void Bitmap::Resize(int new_width, int new_height) {
799 if (!IsIndexed())
800 return;
801 if (new_width <= 0 || new_height <= 0) {
802 return; // Invalid dimensions
803 }
804
805 std::vector<uint8_t> new_data(new_width * new_height, 0);
806
807 // Copy existing data, handling size changes
808 if (!data_.empty()) {
809 for (int y = 0; y < std::min(height_, new_height); y++) {
810 for (int x = 0; x < std::min(width_, new_width); x++) {
811 int old_pos = y * width_ + x;
812 int new_pos = y * new_width + x;
813 if (old_pos < (int)data_.size() && new_pos < (int)new_data.size()) {
814 new_data[new_pos] = data_[old_pos];
815 }
816 }
817 }
818 }
819
822 data_ = std::move(new_data);
823 pixel_data_ = data_.data();
824
825 // Recreate surface with new dimensions
830 if (surface_) {
832 memcpy(surface_->pixels, pixel_data_, data_.size());
834 active_ = true;
835 } else {
836 active_ = false;
837 }
838
839 modified_ = true;
840}
841
852uint32_t Bitmap::HashColor(const ImVec4& color) {
853 // Convert float values to integers for consistent hashing
854 uint32_t r = static_cast<uint32_t>(color.x * 255.0F) & 0xFF;
855 uint32_t g = static_cast<uint32_t>(color.y * 255.0F) & 0xFF;
856 uint32_t b = static_cast<uint32_t>(color.z * 255.0F) & 0xFF;
857 uint32_t a = static_cast<uint32_t>(color.w * 255.0F) & 0xFF;
858
859 // Simple hash combining all components
860 return (r << 24) | (g << 16) | (b << 8) | a;
861}
862
874 color_to_index_cache_.clear();
875
876 // Rebuild cache with current palette
877 for (size_t i = 0; i < palette_.size(); i++) {
879 color_to_index_cache_[color_hash] = static_cast<uint8_t>(i);
880 }
881}
882
894uint8_t Bitmap::FindColorIndex(const SnesColor& color) {
895 ScopedTimer timer("palette_lookup_optimized");
896 uint32_t hash = HashColor(color.rgb());
897 auto it = color_to_index_cache_.find(hash);
898 return (it != color_to_index_cache_.end()) ? it->second : 0;
899}
900
901void Bitmap::set_data(const std::vector<uint8_t>& data) {
902 // Validate input data
903 if (data.empty()) {
904 SDL_Log("Warning: set_data called with empty data vector");
905 return;
906 }
907
908 data_ = data;
909 pixel_data_ = data_.data();
910
912
913 modified_ = true;
914}
915
917 if (!surface_ || !surface_->pixels || data_.empty()) {
918 SDL_Log("ValidateDataSurfaceSync: surface or data is null/empty");
919 return false;
920 }
921
922 const size_t row_bytes = static_cast<size_t>(width_) * (IsIndexed() ? 1 : 4);
923 if (data_.size() < row_bytes * height_ ||
924 static_cast<size_t>(surface_->pitch) < row_bytes)
925 return false;
926 for (int y = 0; y < height_; ++y) {
927 if (memcmp(static_cast<uint8_t*>(surface_->pixels) + y * surface_->pitch,
928 data_.data() + y * row_bytes, row_bytes) != 0)
929 return false;
930 }
931
932 return true;
933}
934
935} // namespace gfx
936} // namespace yaze
SDL_Surface * AllocateSurface(int width, int height, int depth, int format)
Definition arena.cc:443
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:39
void FreeSurface(SDL_Surface *surface)
Definition arena.cc:475
static Arena & Get()
Definition arena.cc:24
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:70
const uint8_t * data() const
Definition bitmap.h:407
const SnesPalette & palette() const
Definition bitmap.h:393
SDL_Surface * surface_
SDL surface for rendering (contains the authoritative palette)
Definition bitmap.h:507
Bitmap & operator=(const Bitmap &other)
Copy assignment operator.
Definition bitmap.cc:88
void WriteToPixel(int position, uint8_t value)
Write a value to a pixel at the given position.
Definition bitmap.cc:649
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:198
bool ValidateDataSurfaceSync()
Validate that bitmap data and surface pixels are synchronized.
Definition bitmap.cc:916
const std::vector< uint8_t > & vector() const
Definition bitmap.h:411
void UpdateSurfacePixels()
Update SDL surface with current pixel data from data_ vector Call this after modifying pixel data via...
Definition bitmap.cc:373
std::unordered_map< uint32_t, uint8_t > color_to_index_cache_
Definition bitmap.h:513
void Reformat(int format)
Reformat the bitmap to use a different pixel format.
Definition bitmap.cc:280
bool IsIndexed() const
Definition bitmap.h:401
uint8_t * pixel_data_
Definition bitmap.h:460
static uint32_t HashColor(const ImVec4 &color)
Hash a color for cache lookup.
Definition bitmap.cc:852
void CopySurfaceState(const Bitmap &other)
Definition bitmap.cc:391
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:716
void CreateTexture()
Creates the underlying SDL_Texture to be displayed.
Definition bitmap.cc:297
uint32_t generation_
Definition bitmap.h:453
void WriteColor(int position, const ImVec4 &color)
Write a color to a pixel at the given position.
Definition bitmap.cc:705
int height() const
Definition bitmap.h:399
void set_data(const std::vector< uint8_t > &data)
Definition bitmap.cc:901
void Resize(int new_width, int new_height)
Resize the bitmap to new dimensions (preserves existing data)
Definition bitmap.cc:798
static uint32_t next_generation_
Definition bitmap.h:454
void SetPixel(int x, int y, const SnesColor &color)
Set a pixel at the given x,y coordinates with SNES color.
Definition bitmap.cc:766
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
Definition bitmap.cc:452
int width() const
Definition bitmap.h:398
BitmapMetadata metadata_
Definition bitmap.h:479
SDL_Color ReadColor(int x, int y) const
Definition bitmap.cc:414
void ApplyStoredPalette()
Apply the stored palette to the surface (internal helper)
Definition bitmap.cc:321
std::vector< uint8_t > data_
Definition bitmap.h:482
int depth() const
Definition bitmap.h:400
void InvalidatePaletteCache()
Invalidate the palette lookup cache (call when palette changes)
Definition bitmap.cc:873
void SetPaletteWithTransparent(const SnesPalette &palette, size_t index, int length=7)
Set the palette with a transparent color.
Definition bitmap.cc:524
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:480
TextureHandle texture_
Definition bitmap.h:510
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:732
uint8_t FindColorIndex(const SnesColor &color)
Find color index in palette using optimized hash map lookup.
Definition bitmap.cc:894
void UpdateTexture()
Updates the underlying SDL_Texture when it already exists.
Definition bitmap.cc:301
gfx::SnesPalette palette_
Internal SNES palette storage (may be empty!)
Definition bitmap.h:476
std::vector< uint8_t > ToRgba() const
Definition bitmap.cc:435
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).
@ kRgba
Definition bitmap.h:41
@ kIndexed
Definition bitmap.h:38
constexpr Uint32 SNES_PIXELFORMAT_8BPP
Definition bitmap.h:33
constexpr Uint32 SNES_PIXELFORMAT_4BPP
Definition bitmap.h:29
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:427
SDL_Palette * GetSurfacePalette(SDL_Surface *surface)
Get the palette attached to a surface.
Definition sdl_compat.h:398
bool EnsureSurfacePalette256(SDL_Surface *surface)
Ensure the surface has a proper 256-color palette for indexed formats.
Definition sdl_compat.h:482
SDL2/SDL3 compatibility layer.
void AddPoint(int x, int y)
Definition bitmap.h:525