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_) {
78 memcpy(surface_->pixels, pixel_data_, data_.size());
80
81 // Apply the copied palette to the new SDL surface
82 if (!palette_.empty()) {
84 }
85 }
86 }
87}
88
90 if (this != &other) {
91 // CRITICAL: Release old resources before replacing to prevent leaks
92 // Queue texture destruction if we have one
93 if (texture_) {
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 data_ = other.data_;
109 // Assign new generation since this is effectively a new bitmap
111
112 // Copy the data and recreate surface/texture
113 pixel_data_ = data_.data();
114 if (active_ && !data_.empty()) {
117 if (surface_) {
119 memcpy(surface_->pixels, pixel_data_, data_.size());
121
122 // Apply the copied palette to the new SDL surface
123 if (!palette_.empty()) {
125 }
126 }
127 }
128 texture_ = nullptr; // Will be recreated on demand
129 }
130 return *this;
131}
132
133Bitmap::Bitmap(Bitmap&& other) noexcept
134 : width_(other.width_),
135 height_(other.height_),
136 depth_(other.depth_),
137 active_(other.active_),
138 modified_(other.modified_),
139 generation_(other.generation_),
140 texture_pixels(other.texture_pixels),
141 pixel_data_(other.pixel_data_),
142 palette_(std::move(other.palette_)),
143 data_(std::move(other.data_)),
144 surface_(other.surface_),
145 texture_(other.texture_) {
146 // Reset the moved-from object
147 other.width_ = 0;
148 other.height_ = 0;
149 other.depth_ = 0;
150 other.active_ = false;
151 other.modified_ = false;
152 other.generation_ = 0;
153 other.texture_pixels = nullptr;
154 other.pixel_data_ = nullptr;
155 other.surface_ = nullptr;
156 other.texture_ = nullptr;
157}
158
159Bitmap& Bitmap::operator=(Bitmap&& other) noexcept {
160 if (this != &other) {
161 // CRITICAL: Release old resources before taking ownership of new ones
162 // Note: We can't queue texture destruction in noexcept move, so we rely on
163 // the Arena's deferred command system to handle stale textures via generation
164 // checking. The old texture will be orphaned but won't cause crashes.
165 // For proper cleanup, prefer copy assignment when explicit resource release
166 // is needed.
167 if (surface_) {
168 Arena::Get().FreeSurface(surface_);
169 }
170
171 width_ = other.width_;
172 height_ = other.height_;
173 depth_ = other.depth_;
174 active_ = other.active_;
175 modified_ = other.modified_;
176 generation_ = other.generation_; // Preserve generation from source
177 texture_pixels = other.texture_pixels;
178 pixel_data_ = other.pixel_data_;
179 palette_ = std::move(other.palette_);
180 data_ = std::move(other.data_);
181 surface_ = other.surface_;
182 texture_ = other.texture_;
183
184 // Reset the moved-from object
185 other.width_ = 0;
186 other.height_ = 0;
187 other.depth_ = 0;
188 other.active_ = false;
189 other.modified_ = false;
190 other.generation_ = 0;
191 other.texture_pixels = nullptr;
192 other.pixel_data_ = nullptr;
193 other.surface_ = nullptr;
194 other.texture_ = nullptr;
195 }
196 return *this;
197}
198
199void Bitmap::Create(int width, int height, int depth, std::span<uint8_t> data) {
200 data_ = std::vector<uint8_t>(data.begin(), data.end());
202}
203
204void Bitmap::Create(int width, int height, int depth,
205 const std::vector<uint8_t>& data) {
206 Create(width, height, depth, static_cast<int>(BitmapFormat::kIndexed), data);
207}
208
223void Bitmap::Create(int width, int height, int depth, int format,
224 const std::vector<uint8_t>& data) {
225 if (data.empty()) {
226 SDL_Log("Bitmap data is empty\n");
227 active_ = false;
228 return;
229 }
230 active_ = true;
231 // Assign new generation for staleness detection in deferred texture commands
233 width_ = width;
234 height_ = height;
235 depth_ = depth;
236 if (data.empty()) {
237 SDL_Log("Data provided to Bitmap is empty.\n");
238 return;
239 }
240 data_.reserve(data.size());
241 data_ = data;
242 pixel_data_ = data_.data();
244 GetSnesPixelFormat(format));
245 if (surface_ == nullptr) {
246 SDL_Log("Bitmap::Create.SDL_CreateRGBSurfaceWithFormat failed: %s\n",
247 SDL_GetError());
248 active_ = false;
249 return;
250 }
251
252 // Ensure indexed surfaces have a proper 256-color palette
253 // This fixes issues where SDL3 creates surfaces with smaller default palettes
254 if (format == static_cast<int>(BitmapFormat::kIndexed)) {
256 }
257
258 // CRITICAL FIX: Use proper SDL surface operations instead of direct pointer
259 // assignment Direct assignment breaks SDL's memory management and causes
260 // malloc errors on shutdown
261 if (surface_ && data_.size() > 0) {
263 size_t copy_size = std::min(data_.size(), static_cast<size_t>(surface_->pitch * surface_->h));
266 }
267 active_ = true;
268
269 // Apply the stored palette if one exists
270 if (!palette_.empty()) {
272 }
273}
274
275void Bitmap::Reformat(int format) {
277 GetSnesPixelFormat(format));
278
279 // CRITICAL FIX: Use proper SDL surface operations instead of direct pointer
280 // assignment
281 if (surface_ && data_.size() > 0) {
283 size_t copy_size = std::min(data_.size(), static_cast<size_t>(surface_->pitch * surface_->h));
286 }
287 active_ = true;
289}
290
294
298
316 if (!surface_ || palette_.empty()) {
317 return; // Can't apply without surface or palette
318 }
319
320 // Invalidate palette cache when palette changes
322
323 // For indexed surfaces, ensure palette exists
325 if (sdl_palette == nullptr) {
326 // Non-indexed surface or palette not created - can't apply palette
327 SDL_Log("Warning: Bitmap surface has no palette (non-indexed format?)\n");
328 return;
329 }
330
332
333 // Build SDL color array from SnesPalette
334 // Only set the colors that exist in the palette - don't fill unused entries
335 std::vector<SDL_Color> colors(palette_.size());
336 for (size_t i = 0; i < palette_.size(); ++i) {
337 const auto& pal_color = palette_[i];
338
339 // Get RGB values - stored as 0-255 in ImVec4 (unconventional!)
340 ImVec4 rgb_255 = pal_color.rgb();
341
342 colors[i].r = static_cast<Uint8>(rgb_255.x);
343 colors[i].g = static_cast<Uint8>(rgb_255.y);
344 colors[i].b = static_cast<Uint8>(rgb_255.z);
345
346 // Only apply transparency if explicitly set
347 if (pal_color.is_transparent()) {
348 colors[i].a = 0; // Fully transparent
349 } else {
350 colors[i].a = 255; // Fully opaque
351 }
352 }
353
354 // Apply palette to surface using SDL_SetPaletteColors
355 // Only set the colors we have - leave rest of palette unchanged
356 // This prevents breaking systems that use small palettes (8-16 colors)
357 SDL_SetPaletteColors(sdl_palette, colors.data(), 0,
358 static_cast<int>(palette_.size()));
359
360 // CRITICAL FIX: Enable blending so SDL respects the alpha channel in the palette
361 // Without this, indexed surfaces may ignore transparency
363
365}
366
368 if (!surface_ || data_.empty()) {
369 return;
370 }
371
372 // Copy pixel data from data_ vector to SDL surface
374 if (surface_->pixels && data_.size() > 0) {
375 memcpy(surface_->pixels, data_.data(),
376 std::min(data_.size(),
377 static_cast<size_t>(surface_->pitch * surface_->h)));
378 }
380}
381
382void Bitmap::SetPalette(const SnesPalette& palette) {
383 // Store palette even if surface isn't ready yet
385
386 // Apply it immediately if surface is ready
388
389 // Mark as modified to trigger texture update
390 modified_ = true;
391}
392
411 int sub_palette_index) {
412 if (metadata_.palette_format == 1) {
413 // Sub-palette: need transparent black + 7 colors from palette
414 // Common for 3BPP graphics sheets (title screen, etc.)
415 SetPaletteWithTransparent(palette, sub_palette_index, 7);
416 } else {
417 // Full palette application
418 // Used for 4BPP, Mode 7, and other full-color formats
420 }
421}
422
454void Bitmap::SetPaletteWithTransparent(const SnesPalette& palette, size_t index,
455 int length) {
456 // Store the full palette for reference (not modified)
458
459 // If surface isn't created yet, just store the palette for later
460 if (surface_ == nullptr) {
461 return; // Palette will be applied when surface is created
462 }
463
464 // Validate parameters
465 if (index >= palette.size()) {
466 throw std::invalid_argument("Invalid palette index");
467 }
468
469 if (length < 0 || length > 15) {
470 throw std::invalid_argument(
471 "Invalid palette length (must be 0-15 for SNES palettes)");
472 }
473
474 if (index + length > palette.size()) {
475 throw std::invalid_argument("Palette index + length exceeds size");
476 }
477
478 // Build SNES sub-palette (up to 16 colors: transparent + length entries)
479 std::vector<ImVec4> colors;
480
481 // Color 0: Transparent (SNES hardware requirement)
482 colors.push_back(ImVec4(0, 0, 0, 0)); // Transparent black
483
484 // Colors 1-15: Extract from source palette
485 // NOTE: palette[i].rgb() returns 0-255 values in ImVec4 (unconventional!)
486 for (size_t i = 0; i < static_cast<size_t>(length) &&
487 (index + i) < palette.size();
488 ++i) {
489 const auto& pal_color = palette[index + i];
490 ImVec4 rgb_255 = pal_color.rgb(); // 0-255 range (unconventional storage)
491
492 // Convert to standard ImVec4 0-1 range for SDL
493 colors.push_back(ImVec4(rgb_255.x / 255.0f, rgb_255.y / 255.0f,
494 rgb_255.z / 255.0f, 1.0f)); // Always opaque
495 }
496
497 // Ensure we have exactly 1 + length colors (transparent + requested entries)
498 while (colors.size() < static_cast<size_t>(length + 1)) {
499 colors.push_back(ImVec4(0, 0, 0, 1.0f)); // Fill with opaque black
500 }
501
502 // Update palette cache with full palette (for color lookup)
504
505 // Apply the SNES sub-palette to SDL surface (supports 3bpp=8 and 4bpp=16)
508 if (!sdl_palette) {
509 SDL_Log("Warning: Bitmap surface has no palette (non-indexed format?)\n");
511 return;
512 }
513 const int num_colors = static_cast<int>(colors.size());
514 for (int color_index = 0; color_index < num_colors; ++color_index) {
516 sdl_palette->colors[color_index].r =
517 static_cast<Uint8>(colors[color_index].x * 255.0f);
518 sdl_palette->colors[color_index].g =
519 static_cast<Uint8>(colors[color_index].y * 255.0f);
520 sdl_palette->colors[color_index].b =
521 static_cast<Uint8>(colors[color_index].z * 255.0f);
522 sdl_palette->colors[color_index].a =
523 static_cast<Uint8>(colors[color_index].w * 255.0f);
524 }
525 }
527
528 // CRITICAL FIX: Enable RLE acceleration and set color key for transparency
529 // SDL ignores palette alpha for INDEX8 unless color key is set or blending is enabled
532}
533
534void Bitmap::SetPalette(const std::vector<SDL_Color>& palette) {
535 // CRITICAL: Validate surface and palette before accessing
536 if (!surface_) {
537 return;
538 }
539
540 // Ensure surface has a proper 256-color palette before setting colors
541 // This fixes issues where SDL creates surfaces with smaller default palettes
543
545 if (!sdl_palette) {
546 SDL_Log("Warning: SetPalette - surface has no palette!");
547 return;
548 }
549
550 int max_colors = sdl_palette->ncolors;
551 int colors_to_set = static_cast<int>(palette.size());
552
553 // Debug: Check if palette capacity is sufficient (should be 256 after EnsureSurfacePalette256)
554 if (max_colors < colors_to_set) {
555 SDL_Log("Warning: SetPalette - SDL palette has %d colors, trying to set %d. "
556 "Colors above %d may not display correctly.",
557 max_colors, colors_to_set, max_colors);
558 colors_to_set = max_colors; // Clamp to available space
559 }
560
562
563 // Use SDL_SetPaletteColors for proper palette setting
564 // This is more reliable than direct array access
566 SDL_Log("Warning: SDL_SetPaletteColors failed: %s", SDL_GetError());
567 // Fall back to manual setting
568 for (int i = 0; i < colors_to_set; ++i) {
569 sdl_palette->colors[i].r = palette[i].r;
570 sdl_palette->colors[i].g = palette[i].g;
571 sdl_palette->colors[i].b = palette[i].b;
572 sdl_palette->colors[i].a = palette[i].a;
573 }
574 }
575
577}
578
579void Bitmap::WriteToPixel(int position, uint8_t value) {
580 // Bounds checking to prevent crashes
581 if (position < 0 || position >= static_cast<int>(data_.size())) {
582 SDL_Log("ERROR: WriteToPixel - position %d out of bounds (size: %zu)",
583 position, data_.size());
584 return;
585 }
586
587 // Safety check: ensure bitmap is active and has valid data
588 if (!active_ || data_.empty()) {
589 SDL_Log(
590 "ERROR: WriteToPixel - bitmap not active or data empty (active=%s, "
591 "size=%zu)",
592 active_ ? "true" : "false", data_.size());
593 return;
594 }
595
596 if (pixel_data_ == nullptr) {
597 pixel_data_ = data_.data();
598 }
599
600 // Safety check: ensure surface exists and is valid
601 if (!surface_ || !surface_->pixels) {
602 SDL_Log(
603 "ERROR: WriteToPixel - surface or pixels are null (surface=%p, "
604 "pixels=%p)",
605 surface_, surface_ ? surface_->pixels : nullptr);
606 return;
607 }
608
609 // Additional validation: ensure pixel_data_ is valid
610 if (pixel_data_ == nullptr) {
611 SDL_Log("ERROR: WriteToPixel - pixel_data_ is null after assignment");
612 return;
613 }
614
615 // CRITICAL FIX: Update both data_ and surface_ properly
616 data_[position] = value;
617 pixel_data_[position] = value;
618
619 // Update surface if it exists
620 if (surface_) {
622 static_cast<uint8_t*>(surface_->pixels)[position] = value;
624 }
625
626 // Mark as modified for traditional update path
627 modified_ = true;
628}
629
630void Bitmap::WriteColor(int position, const ImVec4& color) {
631 // Bounds checking to prevent crashes
632 if (position < 0 || position >= static_cast<int>(data_.size())) {
633 return;
634 }
635
636 // Safety check: ensure bitmap is active and has valid data
637 if (!active_ || data_.empty()) {
638 return;
639 }
640
641 // Safety check: ensure surface exists and is valid
642 if (!surface_ || !surface_->pixels) {
643 return;
644 }
645
646 // Convert ImVec4 (RGBA) to SDL_Color (RGBA)
648 sdl_color.r = static_cast<Uint8>(color.x * 255);
649 sdl_color.g = static_cast<Uint8>(color.y * 255);
650 sdl_color.b = static_cast<Uint8>(color.z * 255);
651 sdl_color.a = static_cast<Uint8>(color.w * 255);
652
653 // Map SDL_Color to the nearest color index in the surface's palette
654 Uint8 index = static_cast<Uint8>(
656
657 // CRITICAL FIX: Update both data_ and surface_ properly
658 if (pixel_data_ == nullptr) {
659 pixel_data_ = data_.data();
660 }
661 data_[position] = ConvertRgbToSnes(color);
662 pixel_data_[position] = index;
663
664 // Update surface if it exists
665 if (surface_) {
667 static_cast<uint8_t*>(surface_->pixels)[position] = index;
669 }
670
671 modified_ = true;
672}
673
674void Bitmap::Get8x8Tile(int tile_index, int x, int y,
675 std::vector<uint8_t>& tile_data,
676 int& tile_data_offset) {
677 int tile_offset = tile_index * (width_ * height_);
678 int tile_x = (x * 8) % width_;
679 int tile_y = (y * 8) % height_;
680 for (int i = 0; i < 8; i++) {
681 for (int j = 0; j < 8; j++) {
682 int pixel_offset = tile_offset + (tile_y + i) * width_ + tile_x + j;
684 tile_data[tile_data_offset] = pixel_value;
686 }
687 }
688}
689
690void Bitmap::Get16x16Tile(int tile_x, int tile_y,
691 std::vector<uint8_t>& tile_data,
692 int& tile_data_offset) {
693 for (int ty = 0; ty < 16; ty++) {
694 for (int tx = 0; tx < 16; tx++) {
695 // Calculate the pixel position in the bitmap
696 int pixel_x = tile_x + tx;
697 int pixel_y = tile_y + ty;
698 int pixel_offset = (pixel_y * width_) + pixel_x;
700
701 // Store the pixel value in the tile data
703 tile_data[tile_data_offset] = pixel_value;
704 }
705 }
706}
707
724void Bitmap::SetPixel(int x, int y, const SnesColor& color) {
726 return; // Bounds check
727 }
728
729 int position = y * width_ + x;
730 if (position >= 0 && position < static_cast<int>(data_.size())) {
731 uint8_t color_index = FindColorIndex(color);
732 data_[position] = color_index;
733
734 // Update pixel_data_ to maintain consistency
735 if (pixel_data_) {
736 pixel_data_[position] = color_index;
737 }
738
739 // Update surface if it exists
740 if (surface_) {
742 static_cast<uint8_t*>(surface_->pixels)[position] = color_index;
744 }
745
746 // Update dirty region for efficient texture updates
748 modified_ = true;
749 }
750}
751
752void Bitmap::Resize(int new_width, int new_height) {
753 if (new_width <= 0 || new_height <= 0) {
754 return; // Invalid dimensions
755 }
756
757 std::vector<uint8_t> new_data(new_width * new_height, 0);
758
759 // Copy existing data, handling size changes
760 if (!data_.empty()) {
761 for (int y = 0; y < std::min(height_, new_height); y++) {
762 for (int x = 0; x < std::min(width_, new_width); x++) {
763 int old_pos = y * width_ + x;
764 int new_pos = y * new_width + x;
765 if (old_pos < (int)data_.size() && new_pos < (int)new_data.size()) {
766 new_data[new_pos] = data_[old_pos];
767 }
768 }
769 }
770 }
771
774 data_ = std::move(new_data);
775 pixel_data_ = data_.data();
776
777 // Recreate surface with new dimensions
780 if (surface_) {
782 memcpy(surface_->pixels, pixel_data_, data_.size());
784 active_ = true;
785 } else {
786 active_ = false;
787 }
788
789 modified_ = true;
790}
791
802uint32_t Bitmap::HashColor(const ImVec4& color) {
803 // Convert float values to integers for consistent hashing
804 uint32_t r = static_cast<uint32_t>(color.x * 255.0F) & 0xFF;
805 uint32_t g = static_cast<uint32_t>(color.y * 255.0F) & 0xFF;
806 uint32_t b = static_cast<uint32_t>(color.z * 255.0F) & 0xFF;
807 uint32_t a = static_cast<uint32_t>(color.w * 255.0F) & 0xFF;
808
809 // Simple hash combining all components
810 return (r << 24) | (g << 16) | (b << 8) | a;
811}
812
824 color_to_index_cache_.clear();
825
826 // Rebuild cache with current palette
827 for (size_t i = 0; i < palette_.size(); i++) {
829 color_to_index_cache_[color_hash] = static_cast<uint8_t>(i);
830 }
831}
832
844uint8_t Bitmap::FindColorIndex(const SnesColor& color) {
845 ScopedTimer timer("palette_lookup_optimized");
846 uint32_t hash = HashColor(color.rgb());
847 auto it = color_to_index_cache_.find(hash);
848 return (it != color_to_index_cache_.end()) ? it->second : 0;
849}
850
851void Bitmap::set_data(const std::vector<uint8_t>& data) {
852 // Validate input data
853 if (data.empty()) {
854 SDL_Log("Warning: set_data called with empty data vector");
855 return;
856 }
857
858 data_ = data;
859 pixel_data_ = data_.data();
860
861 // CRITICAL FIX: Use proper SDL surface operations instead of direct pointer
862 // assignment
863 if (surface_ && !data_.empty()) {
865 memcpy(surface_->pixels, pixel_data_, data_.size());
867 }
868
869 modified_ = true;
870}
871
873 if (!surface_ || !surface_->pixels || data_.empty()) {
874 SDL_Log("ValidateDataSurfaceSync: surface or data is null/empty");
875 return false;
876 }
877
878 // Check if data and surface are synchronized
879 size_t surface_size = static_cast<size_t>(surface_->h * surface_->pitch);
880 size_t data_size = data_.size();
881 size_t compare_size = std::min(data_size, surface_size);
882
883 if (compare_size == 0) {
884 SDL_Log("ValidateDataSurfaceSync: invalid sizes - surface: %zu, data: %zu",
885 surface_size, data_size);
886 return false;
887 }
888
889 // Compare first few bytes to check synchronization
890 if (memcmp(surface_->pixels, data_.data(), compare_size) != 0) {
891 SDL_Log("ValidateDataSurfaceSync: data and surface are not synchronized");
892 return false;
893 }
894
895 return true;
896}
897
898} // namespace gfx
899} // namespace yaze
SDL_Surface * AllocateSurface(int width, int height, int depth, int format)
Definition arena.cc:250
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:34
void FreeSurface(SDL_Surface *surface)
Definition arena.cc:280
static Arena & Get()
Definition arena.cc:19
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:67
const uint8_t * data() const
Definition bitmap.h:377
const SnesPalette & palette() const
Definition bitmap.h:368
SDL_Surface * surface_
SDL surface for rendering (contains the authoritative palette)
Definition bitmap.h:455
Bitmap & operator=(const Bitmap &other)
Copy assignment operator.
Definition bitmap.cc:89
void WriteToPixel(int position, uint8_t value)
Write a value to a pixel at the given position.
Definition bitmap.cc:579
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:199
bool ValidateDataSurfaceSync()
Validate that bitmap data and surface pixels are synchronized.
Definition bitmap.cc:872
const std::vector< uint8_t > & vector() const
Definition bitmap.h:381
void UpdateSurfacePixels()
Update SDL surface with current pixel data from data_ vector Call this after modifying pixel data via...
Definition bitmap.cc:367
std::unordered_map< uint32_t, uint8_t > color_to_index_cache_
Definition bitmap.h:461
void Reformat(int format)
Reformat the bitmap to use a different pixel format.
Definition bitmap.cc:275
uint8_t * pixel_data_
Definition bitmap.h:408
static uint32_t HashColor(const ImVec4 &color)
Hash a color for cache lookup.
Definition bitmap.cc:802
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:674
void CreateTexture()
Creates the underlying SDL_Texture to be displayed.
Definition bitmap.cc:291
uint32_t generation_
Definition bitmap.h:401
void WriteColor(int position, const ImVec4 &color)
Write a color to a pixel at the given position.
Definition bitmap.cc:630
int height() const
Definition bitmap.h:374
void set_data(const std::vector< uint8_t > &data)
Definition bitmap.cc:851
void Resize(int new_width, int new_height)
Resize the bitmap to new dimensions (preserves existing data)
Definition bitmap.cc:752
static uint32_t next_generation_
Definition bitmap.h:402
void SetPixel(int x, int y, const SnesColor &color)
Set a pixel at the given x,y coordinates with SNES color.
Definition bitmap.cc:724
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
Definition bitmap.cc:382
int width() const
Definition bitmap.h:373
BitmapMetadata metadata_
Definition bitmap.h:427
void ApplyStoredPalette()
Apply the stored palette to the surface (internal helper)
Definition bitmap.cc:315
std::vector< uint8_t > data_
Definition bitmap.h:430
int depth() const
Definition bitmap.h:375
void InvalidatePaletteCache()
Invalidate the palette lookup cache (call when palette changes)
Definition bitmap.cc:823
void SetPaletteWithTransparent(const SnesPalette &palette, size_t index, int length=7)
Set the palette with a transparent color.
Definition bitmap.cc:454
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:410
TextureHandle texture_
Definition bitmap.h:458
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:690
uint8_t FindColorIndex(const SnesColor &color)
Find color index in palette using optimized hash map lookup.
Definition bitmap.cc:844
void UpdateTexture()
Updates the underlying SDL_Texture when it already exists.
Definition bitmap.cc:295
gfx::SnesPalette palette_
Internal SNES palette storage (may be empty!)
Definition bitmap.h:424
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:402
SDL_Palette * GetSurfacePalette(SDL_Surface *surface)
Get the palette attached to a surface.
Definition sdl_compat.h:375
bool EnsureSurfacePalette256(SDL_Surface *surface)
Ensure the surface has a proper 256-color palette for indexed formats.
Definition sdl_compat.h:454
SDL2/SDL3 compatibility layer.
void AddPoint(int x, int y)
Definition bitmap.h:473