yaze 0.2.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
3#include <SDL.h>
4#if YAZE_LIB_PNG == 1
5#include <png.h>
6#endif
7
8#include <cstdint>
9#include <span>
10#include <stdexcept>
11
12#include "app/gfx/arena.h"
14
15namespace yaze {
16namespace gfx {
17
18#if YAZE_LIB_PNG == 1
19
20namespace png_internal {
21
22void PngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length) {
23 std::vector<uint8_t> *p = (std::vector<uint8_t> *)png_get_io_ptr(png_ptr);
24 p->insert(p->end(), data, data + length);
25}
26
27void PngReadCallback(png_structp png_ptr, png_bytep outBytes,
28 png_size_t byteCountToRead) {
29 png_voidp io_ptr = png_get_io_ptr(png_ptr);
30 if (!io_ptr) return;
31
32 std::vector<uint8_t> *png_data =
33 reinterpret_cast<std::vector<uint8_t> *>(io_ptr);
34 static size_t pos = 0; // Position to read from
35
36 if (pos + byteCountToRead <= png_data->size()) {
37 memcpy(outBytes, png_data->data() + pos, byteCountToRead);
38 pos += byteCountToRead;
39 } else {
40 png_error(png_ptr, "Read error in PngReadCallback");
41 }
42}
43
44} // namespace png_internal
45
46bool ConvertSurfaceToPng(SDL_Surface *surface, std::vector<uint8_t> &buffer) {
47 png_structp png_ptr = png_create_write_struct("1.6.40", NULL, NULL, NULL);
48 if (!png_ptr) {
49 SDL_Log("Failed to create PNG write struct");
50 return false;
51 }
52
53 png_infop info_ptr = png_create_info_struct(png_ptr);
54 if (!info_ptr) {
55 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
56 SDL_Log("Failed to create PNG info struct");
57 return false;
58 }
59
60 if (setjmp(png_jmpbuf(png_ptr))) {
61 png_destroy_write_struct(&png_ptr, &info_ptr);
62 SDL_Log("Error during PNG write");
63 return false;
64 }
65
66 png_set_write_fn(png_ptr, &buffer, png_internal::PngWriteCallback, NULL);
67
68 png_colorp pal_ptr;
69
70 /* Prepare chunks */
71 int colortype = PNG_COLOR_MASK_COLOR;
72 int i = 0;
73 SDL_Palette *pal;
74 if (surface->format->BytesPerPixel > 0 &&
75 surface->format->BytesPerPixel <= 8 && (pal = surface->format->palette)) {
76 SDL_Log("Writing PNG image with palette");
77 colortype |= PNG_COLOR_MASK_PALETTE;
78 pal_ptr = (png_colorp)malloc(pal->ncolors * sizeof(png_color));
79 for (i = 0; i < pal->ncolors; i++) {
80 pal_ptr[i].red = pal->colors[i].r;
81 pal_ptr[i].green = pal->colors[i].g;
82 pal_ptr[i].blue = pal->colors[i].b;
83 }
84 png_set_PLTE(png_ptr, info_ptr, pal_ptr, pal->ncolors);
85 free(pal_ptr);
86 }
87
88 if (surface->format->Amask) { // Check for alpha channel
89 colortype |= PNG_COLOR_MASK_ALPHA;
90 }
91
92 auto depth = surface->format->BitsPerPixel;
93
94 // Set image attributes.
95 png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, depth, colortype,
96 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
97 PNG_FILTER_TYPE_DEFAULT);
98
99 png_set_bgr(png_ptr);
100
101 // Write the image data.
102 std::vector<png_bytep> row_pointers(surface->h);
103 for (int y = 0; y < surface->h; ++y) {
104 row_pointers[y] = (png_bytep)(surface->pixels) + y * surface->pitch;
105 }
106
107 png_set_rows(png_ptr, info_ptr, row_pointers.data());
108
109 SDL_Log("Writing PNG image...");
110 png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
111 SDL_Log("PNG image write complete");
112
113 png_destroy_write_struct(&png_ptr, &info_ptr);
114
115 return true;
116}
117
118void ConvertPngToSurface(const std::vector<uint8_t> &png_data,
119 SDL_Surface **outSurface) {
120 std::vector<uint8_t> data(png_data);
121 png_structp png_ptr = png_create_read_struct("1.6.40", NULL, NULL, NULL);
122 if (!png_ptr) {
123 throw std::runtime_error("Failed to create PNG read struct");
124 }
125
126 png_infop info_ptr = png_create_info_struct(png_ptr);
127 if (!info_ptr) {
128 png_destroy_read_struct(&png_ptr, NULL, NULL);
129 throw std::runtime_error("Failed to create PNG info struct");
130 }
131
132 if (setjmp(png_jmpbuf(png_ptr))) {
133 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
134 throw std::runtime_error("Error during PNG read");
135 }
136
137 // Set our custom read function
138 png_set_read_fn(png_ptr, &data, png_internal::PngReadCallback);
139
140 // Read the PNG info
141 png_read_info(png_ptr, info_ptr);
142
143 uint32_t width = png_get_image_width(png_ptr, info_ptr);
144 uint32_t height = png_get_image_height(png_ptr, info_ptr);
145 png_byte color_type = png_get_color_type(png_ptr, info_ptr);
146 png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr);
147
148 // Apply necessary transformations...
149 // (Same as in your existing code)
150
151 // Update info structure with transformations
152 png_read_update_info(png_ptr, info_ptr);
153
154 // Read the file
155 std::vector<uint8_t> raw_data(width * height *
156 4); // Assuming 4 bytes per pixel (RGBA)
157 std::vector<png_bytep> row_pointers(height);
158 for (size_t y = 0; y < height; y++) {
159 row_pointers[y] = raw_data.data() + y * width * 4;
160 }
161
162 png_read_image(png_ptr, row_pointers.data());
163 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
164
165 // Create an SDL_Surface
166 *outSurface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32,
167 SDL_PIXELFORMAT_RGBA32);
168 if (*outSurface == nullptr) {
169 SDL_Log("SDL_CreateRGBSurfaceWithFormat failed: %s\n", SDL_GetError());
170 return;
171 }
172
173 // Copy the raw data into the SDL_Surface
174 SDL_LockSurface(*outSurface);
175 memcpy((*outSurface)->pixels, raw_data.data(), raw_data.size());
176 SDL_UnlockSurface(*outSurface);
177
178 SDL_Log("Successfully created SDL_Surface from PNG data");
179}
180
181#endif // YAZE_LIB_PNG
182
183class BitmapError : public std::runtime_error {
184 public:
185 using std::runtime_error::runtime_error;
186};
187
188Uint32 GetSnesPixelFormat(int format) {
189 switch (format) {
190 case 0:
191 return SDL_PIXELFORMAT_INDEX8;
192 case 1:
194 case 2:
196 default:
197 return SDL_PIXELFORMAT_INDEX8;
198 }
199}
200
202 const std::vector<uint8_t> &data)
205}
206
208 const std::vector<uint8_t> &data, const SnesPalette &palette)
209 : width_(width),
211 depth_(depth),
213 data_(data) {
216}
217
218void Bitmap::Create(int width, int height, int depth, std::span<uint8_t> data) {
219 data_ = std::vector<uint8_t>(data.begin(), data.end());
221}
222
223void Bitmap::Create(int width, int height, int depth,
224 const std::vector<uint8_t> &data) {
225 Create(width, height, depth, static_cast<int>(BitmapFormat::kIndexed), data);
226}
227
228void Bitmap::Create(int width, int height, int depth, int format,
229 const std::vector<uint8_t> &data) {
230 if (data.empty()) {
231 SDL_Log("Bitmap data is empty\n");
232 active_ = false;
233 return;
234 }
235 active_ = true;
236 width_ = width;
237 height_ = height;
238 depth_ = depth;
239 if (data.empty()) {
240 SDL_Log("Data provided to Bitmap is empty.\n");
241 return;
242 }
243 data_.reserve(data.size());
244 data_ = data;
245 pixel_data_ = data_.data();
247 GetSnesPixelFormat(format));
248 if (surface_ == nullptr) {
249 SDL_Log("Bitmap::Create.SDL_CreateRGBSurfaceWithFormat failed: %s\n",
250 SDL_GetError());
251 active_ = false;
252 return;
253 }
254 surface_->pixels = pixel_data_;
255 active_ = true;
256}
257
258void Bitmap::Reformat(int format) {
260 GetSnesPixelFormat(format));
261 surface_->pixels = pixel_data_;
262 active_ = true;
264}
265
266void Bitmap::UpdateTexture(SDL_Renderer *renderer) {
267 if (!texture_) {
268 CreateTexture(renderer);
269 return;
270 }
271 memcpy(surface_->pixels, data_.data(), data_.size());
273}
274
275void Bitmap::CreateTexture(SDL_Renderer *renderer) {
276 if (!renderer) {
277 SDL_Log("Invalid renderer passed to CreateTexture");
278 return;
279 }
280
281 if (width_ <= 0 || height_ <= 0) {
282 SDL_Log("Invalid texture dimensions: width=%d, height=%d\n", width_,
283 height_);
284 return;
285 }
286
287 // Get a texture from the Arena
289 if (!texture_) {
290 SDL_Log("Bitmap::CreateTexture failed to allocate texture: %s\n",
291 SDL_GetError());
292 return;
293 }
294
296}
297
299 if (!texture_ || !surface_) {
300 return;
301 }
302
304 modified_ = false;
305}
306
308 if (surface_ == nullptr) {
309 throw BitmapError("Surface is null. Palette not applied");
310 }
311 if (surface_->format == nullptr || surface_->format->palette == nullptr) {
312 throw BitmapError(
313 "Surface format or palette is null. Palette not applied.");
314 }
316
317 SDL_Palette *sdl_palette = surface_->format->palette;
318 if (sdl_palette == nullptr) {
319 throw BitmapError("Failed to get SDL palette");
320 }
321
322 SDL_UnlockSurface(surface_);
323 for (size_t i = 0; i < palette.size(); ++i) {
324 auto pal_color = palette[i];
325 sdl_palette->colors[i].r = pal_color.rgb().x;
326 sdl_palette->colors[i].g = pal_color.rgb().y;
327 sdl_palette->colors[i].b = pal_color.rgb().z;
328 sdl_palette->colors[i].a = pal_color.rgb().w;
329 }
330 SDL_LockSurface(surface_);
331}
332
334 int length) {
335 if (index >= palette.size()) {
336 throw std::invalid_argument("Invalid palette index");
337 }
338
339 if (length < 0 || length > palette.size()) {
340 throw std::invalid_argument("Invalid palette length");
341 }
342
343 if (index + length > palette.size()) {
344 throw std::invalid_argument("Palette index + length exceeds size");
345 }
346
347 if (surface_ == nullptr) {
348 throw BitmapError("Surface is null. Palette not applied");
349 }
350
351 auto start_index = index * 7;
352 palette_ = palette.sub_palette(start_index, start_index + 7);
353 std::vector<ImVec4> colors;
354 colors.push_back(ImVec4(0, 0, 0, 0));
355 for (size_t i = start_index; i < start_index + 7; ++i) {
356 auto &pal_color = palette[i];
357 colors.push_back(pal_color.rgb());
358 }
359
360 SDL_UnlockSurface(surface_);
361 int i = 0;
362 for (const auto &each : colors) {
363 surface_->format->palette->colors[i].r = each.x;
364 surface_->format->palette->colors[i].g = each.y;
365 surface_->format->palette->colors[i].b = each.z;
366 surface_->format->palette->colors[i].a = each.w;
367 i++;
368 }
369 SDL_LockSurface(surface_);
370}
371
372void Bitmap::SetPalette(const std::vector<SDL_Color> &palette) {
373 SDL_UnlockSurface(surface_);
374 for (size_t i = 0; i < palette.size(); ++i) {
375 surface_->format->palette->colors[i].r = palette[i].r;
376 surface_->format->palette->colors[i].g = palette[i].g;
377 surface_->format->palette->colors[i].b = palette[i].b;
378 surface_->format->palette->colors[i].a = palette[i].a;
379 }
380 SDL_LockSurface(surface_);
381}
382
383void Bitmap::WriteToPixel(int position, uint8_t value) {
384 if (pixel_data_ == nullptr) {
385 pixel_data_ = data_.data();
386 }
387 pixel_data_[position] = value;
388 data_[position] = value;
389 modified_ = true;
390}
391
392void Bitmap::WriteColor(int position, const ImVec4 &color) {
393 // Convert ImVec4 (RGBA) to SDL_Color (RGBA)
394 SDL_Color sdl_color;
395 sdl_color.r = static_cast<Uint8>(color.x * 255);
396 sdl_color.g = static_cast<Uint8>(color.y * 255);
397 sdl_color.b = static_cast<Uint8>(color.z * 255);
398 sdl_color.a = static_cast<Uint8>(color.w * 255);
399
400 // Map SDL_Color to the nearest color index in the surface's palette
401 Uint8 index =
402 SDL_MapRGB(surface_->format, sdl_color.r, sdl_color.g, sdl_color.b);
403
404 // Write the color index to the pixel data
405 pixel_data_[position] = index;
406 data_[position] = ConvertRgbToSnes(color);
407
408 modified_ = true;
409}
410
411void Bitmap::Get8x8Tile(int tile_index, int x, int y,
412 std::vector<uint8_t> &tile_data,
413 int &tile_data_offset) {
414 int tile_offset = tile_index * (width_ * height_);
415 int tile_x = (x * 8) % width_;
416 int tile_y = (y * 8) % height_;
417 for (int i = 0; i < 8; i++) {
418 for (int j = 0; j < 8; j++) {
419 int pixel_offset = tile_offset + (tile_y + i) * width_ + tile_x + j;
420 uint8_t pixel_value = data_[pixel_offset];
421 tile_data[tile_data_offset] = pixel_value;
422 tile_data_offset++;
423 }
424 }
425}
426
427void Bitmap::Get16x16Tile(int tile_x, int tile_y,
428 std::vector<uint8_t> &tile_data,
429 int &tile_data_offset) {
430 for (int ty = 0; ty < 16; ty++) {
431 for (int tx = 0; tx < 16; tx++) {
432 // Calculate the pixel position in the bitmap
433 int pixel_x = tile_x + tx;
434 int pixel_y = tile_y + ty;
435 int pixel_offset = (pixel_y * width_) + pixel_x;
436 uint8_t pixel_value = data_[pixel_offset];
437
438 // Store the pixel value in the tile data
439 tile_data_offset++;
440 tile_data[tile_data_offset] = pixel_value;
441 }
442 }
443}
444
445#if YAZE_LIB_PNG == 1
446std::vector<uint8_t> Bitmap::GetPngData() {
447 std::vector<uint8_t> png_data;
448 ConvertSurfaceToPng(surface_, png_data);
449 return png_data;
450}
451#endif
452
453} // namespace gfx
454} // namespace yaze
SDL_Texture * AllocateTexture(SDL_Renderer *renderer, int width, int height)
Definition arena.cc:25
SDL_Surface * AllocateSurface(int width, int height, int depth, int format)
Definition arena.cc:93
void UpdateTexture(SDL_Texture *texture, SDL_Surface *surface)
Definition arena.cc:59
static Arena & Get()
Definition arena.cc:10
const uint8_t * data() const
Definition bitmap.h:155
const SnesPalette & palette() const
Definition bitmap.h:149
SDL_Surface * surface_
Definition bitmap.h:192
void WriteToPixel(int position, uint8_t value)
Write a value to a pixel at the given position.
Definition bitmap.cc:383
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:218
void UpdateTexture(SDL_Renderer *renderer)
Updates the underlying SDL_Texture when it already exists.
Definition bitmap.cc:266
void Reformat(int format)
Reformat the bitmap to use a different pixel format.
Definition bitmap.cc:258
uint8_t * pixel_data_
Definition bitmap.h:183
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.
Definition bitmap.cc:411
void WriteColor(int position, const ImVec4 &color)
Write a color to a pixel at the given position.
Definition bitmap.cc:392
int height() const
Definition bitmap.h:152
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap.
Definition bitmap.cc:307
int width() const
Definition bitmap.h:151
std::vector< uint8_t > data_
Definition bitmap.h:189
int depth() const
Definition bitmap.h:153
SDL_Texture * texture_
Definition bitmap.h:195
void CreateTexture(SDL_Renderer *renderer)
Creates the underlying SDL_Texture to be displayed.
Definition bitmap.cc:275
void SetPaletteWithTransparent(const SnesPalette &palette, size_t index, int length=7)
Set the palette with a transparent color.
Definition bitmap.cc:333
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.
Definition bitmap.cc:427
gfx::SnesPalette palette_
Definition bitmap.h:186
void UpdateTextureData()
Updates the texture data from the surface.
Definition bitmap.cc:298
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
Contains classes for handling graphical data.
Definition arena.cc:8
@ kIndexed
Definition bitmap.h:33
uint16_t ConvertRgbToSnes(const snes_color &color)
Definition snes_color.cc:33
constexpr Uint32 SNES_PIXELFORMAT_8BPP
Definition bitmap.h:28
constexpr Uint32 SNES_PIXELFORMAT_4BPP
Definition bitmap.h:24
Uint32 GetSnesPixelFormat(int format)
Get the SDL pixel format for a given bitmap format.
Definition bitmap.cc:188
Main namespace for the application.
Definition controller.cc:18