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