yaze 0.2.0
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 <memory>
10
11#include "absl/status/status.h"
12#include "absl/status/statusor.h"
13#include "absl/strings/str_cat.h"
14#include "app/core/constants.h"
16
17#define SDL_RETURN_IF_ERROR() \
18 if (SDL_GetError() != nullptr) { \
19 return absl::InternalError(SDL_GetError()); \
20 }
21
22namespace yaze {
23namespace app {
24namespace gfx {
25
28
29#if YAZE_LIB_PNG == 1
30
31namespace png_internal {
32
33void PngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length) {
34 std::vector<uint8_t> *p = (std::vector<uint8_t> *)png_get_io_ptr(png_ptr);
35 p->insert(p->end(), data, data + length);
36}
37
38void PngReadCallback(png_structp png_ptr, png_bytep outBytes,
39 png_size_t byteCountToRead) {
40 png_voidp io_ptr = png_get_io_ptr(png_ptr);
41 if (!io_ptr) return;
42
43 std::vector<uint8_t> *png_data =
44 reinterpret_cast<std::vector<uint8_t> *>(io_ptr);
45 static size_t pos = 0; // Position to read from
46
47 if (pos + byteCountToRead <= png_data->size()) {
48 memcpy(outBytes, png_data->data() + pos, byteCountToRead);
49 pos += byteCountToRead;
50 } else {
51 png_error(png_ptr, "Read error in PngReadCallback");
52 }
53}
54
55} // namespace png_internal
56
57bool ConvertSurfaceToPNG(SDL_Surface *surface, std::vector<uint8_t> &buffer) {
58 png_structp png_ptr = png_create_write_struct("1.6.40", NULL, NULL, NULL);
59 if (!png_ptr) {
60 SDL_Log("Failed to create PNG write struct");
61 return false;
62 }
63
64 png_infop info_ptr = png_create_info_struct(png_ptr);
65 if (!info_ptr) {
66 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
67 SDL_Log("Failed to create PNG info struct");
68 return false;
69 }
70
71 if (setjmp(png_jmpbuf(png_ptr))) {
72 png_destroy_write_struct(&png_ptr, &info_ptr);
73 SDL_Log("Error during PNG write");
74 return false;
75 }
76
77 png_set_write_fn(png_ptr, &buffer, png_internal::PngWriteCallback, NULL);
78
79 png_colorp pal_ptr;
80
81 /* Prepare chunks */
82 int colortype = PNG_COLOR_MASK_COLOR;
83 int i = 0;
84 SDL_Palette *pal;
85 if (surface->format->BytesPerPixel > 0 &&
86 surface->format->BytesPerPixel <= 8 && (pal = surface->format->palette)) {
87 SDL_Log("Writing PNG image with palette");
88 colortype |= PNG_COLOR_MASK_PALETTE;
89 pal_ptr = (png_colorp)malloc(pal->ncolors * sizeof(png_color));
90 for (i = 0; i < pal->ncolors; i++) {
91 pal_ptr[i].red = pal->colors[i].r;
92 pal_ptr[i].green = pal->colors[i].g;
93 pal_ptr[i].blue = pal->colors[i].b;
94 }
95 png_set_PLTE(png_ptr, info_ptr, pal_ptr, pal->ncolors);
96 free(pal_ptr);
97 }
98
99 if (surface->format->Amask) { // Check for alpha channel
100 colortype |= PNG_COLOR_MASK_ALPHA;
101 }
102
103 auto depth = surface->format->BitsPerPixel;
104
105 // Set image attributes.
106 png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, depth, colortype,
107 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
108 PNG_FILTER_TYPE_DEFAULT);
109
110 png_set_bgr(png_ptr);
111
112 // Write the image data.
113 std::vector<png_bytep> row_pointers(surface->h);
114 for (int y = 0; y < surface->h; ++y) {
115 row_pointers[y] = (png_bytep)(surface->pixels) + y * surface->pitch;
116 }
117
118 png_set_rows(png_ptr, info_ptr, row_pointers.data());
119
120 SDL_Log("Writing PNG image...");
121 png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
122 SDL_Log("PNG image write complete");
123
124 png_destroy_write_struct(&png_ptr, &info_ptr);
125
126 return true;
127}
128
129void ConvertPngToSurface(const std::vector<uint8_t> &png_data,
130 SDL_Surface **outSurface) {
131 std::vector<uint8_t> data(png_data);
132 png_structp png_ptr = png_create_read_struct("1.6.40", NULL, NULL, NULL);
133 if (!png_ptr) {
134 throw std::runtime_error("Failed to create PNG read struct");
135 }
136
137 png_infop info_ptr = png_create_info_struct(png_ptr);
138 if (!info_ptr) {
139 png_destroy_read_struct(&png_ptr, NULL, NULL);
140 throw std::runtime_error("Failed to create PNG info struct");
141 }
142
143 if (setjmp(png_jmpbuf(png_ptr))) {
144 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
145 throw std::runtime_error("Error during PNG read");
146 }
147
148 // Set our custom read function
149 png_set_read_fn(png_ptr, &data, png_internal::PngReadCallback);
150
151 // Read the PNG info
152 png_read_info(png_ptr, info_ptr);
153
154 uint32_t width = png_get_image_width(png_ptr, info_ptr);
155 uint32_t height = png_get_image_height(png_ptr, info_ptr);
156 png_byte color_type = png_get_color_type(png_ptr, info_ptr);
157 png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr);
158
159 // Apply necessary transformations...
160 // (Same as in your existing code)
161
162 // Update info structure with transformations
163 png_read_update_info(png_ptr, info_ptr);
164
165 // Read the file
166 std::vector<uint8_t> raw_data(width * height *
167 4); // Assuming 4 bytes per pixel (RGBA)
168 std::vector<png_bytep> row_pointers(height);
169 for (size_t y = 0; y < height; y++) {
170 row_pointers[y] = raw_data.data() + y * width * 4;
171 }
172
173 png_read_image(png_ptr, row_pointers.data());
174 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
175
176 // Create an SDL_Surface
177 *outSurface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32,
178 SDL_PIXELFORMAT_RGBA32);
179 if (*outSurface == nullptr) {
180 SDL_Log("SDL_CreateRGBSurfaceWithFormat failed: %s\n", SDL_GetError());
181 return;
182 }
183
184 // Copy the raw data into the SDL_Surface
185 SDL_LockSurface(*outSurface);
186 memcpy((*outSurface)->pixels, raw_data.data(), raw_data.size());
187 SDL_UnlockSurface(*outSurface);
188
189 SDL_Log("Successfully created SDL_Surface from PNG data");
190}
191
192std::vector<uint8_t> Bitmap::GetPngData() {
193 ConvertSurfaceToPNG(surface_.get(), png_data_);
194 return png_data_;
195}
196
197#endif // YAZE_LIB_PNG
198
199namespace {
200
201void GrayscalePalette(SDL_Palette *palette) {
202 for (int i = 0; i < 8; i++) {
203 palette->colors[i].r = i * 31;
204 palette->colors[i].g = i * 31;
205 palette->colors[i].b = i * 31;
206 }
207}
208
209Uint32 GetSnesPixelFormat(int format) {
210 switch (format) {
211 case 0:
212 return SDL_PIXELFORMAT_INDEX8;
213 case 1:
215 case 2:
217 case 3:
219 }
220 return SDL_PIXELFORMAT_INDEX8;
221}
222} // namespace
223
224void Bitmap::SaveSurfaceToFile(std::string_view filename) {
225 SDL_SaveBMP(surface_.get(), filename.data());
226}
227
228Bitmap::Bitmap(int width, int height, int depth, int data_size) {
229 Create(width, height, depth, std::vector<uint8_t>(data_size, 0));
230}
231
232void Bitmap::Create(int width, int height, int depth,
233 const std::vector<uint8_t> &data) {
235}
236
237void Bitmap::Create(int width, int height, int depth, int format,
238 const std::vector<uint8_t> &data) {
239 if (data.empty()) {
240 SDL_Log("Bitmap data is empty\n");
241 active_ = false;
242 return;
243 }
244 active_ = true;
245 width_ = width;
246 height_ = height;
247 depth_ = depth;
248 data_ = data;
249 data_size_ = data.size();
250 if (data_size_ == 0) {
251 SDL_Log("Data provided to Bitmap is empty.\n");
252 return;
253 }
254 pixel_data_ = data_.data();
255 surface_ = std::shared_ptr<SDL_Surface>{
256 SDL_CreateRGBSurfaceWithFormat(0, width_, height_, depth_,
257 GetSnesPixelFormat(format)),
259 if (surface_ == nullptr) {
260 SDL_Log("SDL_CreateRGBSurfaceWithFormat failed: %s\n", SDL_GetError());
261 active_ = false;
262 return;
263 }
264 surface_->pixels = pixel_data_;
265 active_ = true;
266}
267
268void Bitmap::Reformat(int format) {
269 surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
270 SDL_CreateRGBSurfaceWithFormat(0, width_, height_, depth_,
271 GetSnesPixelFormat(format)),
273 surface_->pixels = pixel_data_;
274 active_ = true;
275 auto apply_palette = ApplyPalette(palette_);
276 if (!apply_palette.ok()) {
277 SDL_Log("Failed to apply palette: %s\n", apply_palette.message().data());
278 active_ = false;
279 }
280}
281
282void Bitmap::CreateTexture(SDL_Renderer *renderer) {
283 if (width_ <= 0 || height_ <= 0) {
284 SDL_Log("Invalid texture dimensions: width=%d, height=%d\n", width_,
285 height_);
286 return;
287 }
288
289 texture_ = std::shared_ptr<SDL_Texture>{
290 SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB888,
291 SDL_TEXTUREACCESS_STREAMING, width_, height_),
293 if (texture_ == nullptr) {
294 SDL_Log("SDL_CreateTextureFromSurface failed: %s\n", SDL_GetError());
295 }
296
297 converted_surface_ = std::shared_ptr<SDL_Surface>{
298 SDL_ConvertSurfaceFormat(surface_.get(), SDL_PIXELFORMAT_ARGB8888, 0),
300 if (converted_surface_ == nullptr) {
301 SDL_Log("SDL_ConvertSurfaceFormat failed: %s\n", SDL_GetError());
302 return;
303 }
304
305 SDL_LockTexture(texture_.get(), nullptr, (void **)&texture_pixels,
306 &converted_surface_->pitch);
307 memcpy(texture_pixels, converted_surface_->pixels,
309 SDL_UnlockTexture(texture_.get());
310}
311
312void Bitmap::UpdateTexture(SDL_Renderer *renderer, bool use_sdl_update) {
313 SDL_Surface *converted_surface =
314 SDL_ConvertSurfaceFormat(surface_.get(), SDL_PIXELFORMAT_ARGB8888, 0);
315 if (converted_surface) {
316 converted_surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
318 } else {
319 SDL_Log("SDL_ConvertSurfaceFormat failed: %s\n", SDL_GetError());
320 }
321
322 SDL_LockTexture(texture_.get(), nullptr, (void **)&texture_pixels,
323 &converted_surface_->pitch);
324 if (use_sdl_update) {
325 SDL_UpdateTexture(texture_.get(), nullptr, converted_surface_->pixels,
326 converted_surface_->pitch);
327 } else {
328 memcpy(texture_pixels, converted_surface_->pixels,
330 }
331 SDL_UnlockTexture(texture_.get());
332}
333
334void Bitmap::CreateTexture(std::shared_ptr<SDL_Renderer> renderer) {
335 texture_ = std::shared_ptr<SDL_Texture>{
336 SDL_CreateTextureFromSurface(renderer.get(), surface_.get()),
338}
339
340void Bitmap::UpdateTexture(std::shared_ptr<SDL_Renderer> renderer) {
341 texture_ = std::shared_ptr<SDL_Texture>{
342 SDL_CreateTextureFromSurface(renderer.get(), surface_.get()),
344}
345
346absl::Status Bitmap::ApplyPalette(const SnesPalette &palette) {
347 if (surface_ == nullptr) {
348 return absl::FailedPreconditionError(
349 "Surface is null. Palette not applied");
350 }
351 if (surface_->format == nullptr || surface_->format->palette == nullptr) {
352 return absl::FailedPreconditionError(
353 "Surface format or palette is null. Palette not applied.");
354 }
356
357 SDL_Palette *sdl_palette = surface_->format->palette;
358 if (sdl_palette == nullptr) {
359 return absl::InternalError("Failed to get SDL palette");
360 }
361
362 SDL_UnlockSurface(surface_.get());
363 for (size_t i = 0; i < palette.size(); ++i) {
364 ASSIGN_OR_RETURN(gfx::SnesColor pal_color, palette.GetColor(i));
365 sdl_palette->colors[i].r = pal_color.rgb().x;
366 sdl_palette->colors[i].g = pal_color.rgb().y;
367 sdl_palette->colors[i].b = pal_color.rgb().z;
368 sdl_palette->colors[i].a = pal_color.rgb().w;
369 }
370 SDL_LockSurface(surface_.get());
371 // SDL_RETURN_IF_ERROR()
372 return absl::OkStatus();
373}
374
376 int palette_id) {
377 auto start_index = palette_id * 8;
378 palette_ = palette.sub_palette(start_index, start_index + 8);
379 SDL_UnlockSurface(surface_.get());
380 for (size_t i = 0; i < palette_.size(); ++i) {
381 ASSIGN_OR_RETURN(auto pal_color, palette_.GetColor(i));
382 if (pal_color.is_transparent()) {
383 surface_->format->palette->colors[i].r = 0;
384 surface_->format->palette->colors[i].g = 0;
385 surface_->format->palette->colors[i].b = 0;
386 surface_->format->palette->colors[i].a = 0;
387 } else {
388 surface_->format->palette->colors[i].r = pal_color.rgb().x;
389 surface_->format->palette->colors[i].g = pal_color.rgb().y;
390 surface_->format->palette->colors[i].b = pal_color.rgb().z;
391 surface_->format->palette->colors[i].a = pal_color.rgb().w;
392 }
393 }
394 SDL_LockSurface(surface_.get());
395 // SDL_RETURN_IF_ERROR()
396 return absl::OkStatus();
397}
398
400 size_t index, int length) {
401 if (index < 0 || index >= palette.size()) {
402 return absl::InvalidArgumentError("Invalid palette index");
403 }
404
405 if (length < 0 || length > palette.size()) {
406 return absl::InvalidArgumentError("Invalid palette length");
407 }
408
409 if (index + length > palette.size()) {
410 return absl::InvalidArgumentError("Palette index + length exceeds size");
411 }
412
413 if (surface_ == nullptr) {
414 return absl::FailedPreconditionError(
415 "Surface is null. Palette not applied");
416 }
417
418 auto start_index = index * 7;
419 palette_ = palette.sub_palette(start_index, start_index + 7);
420 std::vector<ImVec4> colors;
421 colors.push_back(ImVec4(0, 0, 0, 0));
422 for (int i = start_index; i < start_index + 7; ++i) {
423 ASSIGN_OR_RETURN(auto pal_color, palette.GetColor(i));
424 colors.push_back(pal_color.rgb());
425 }
426
427 SDL_UnlockSurface(surface_.get());
428 int i = 0;
429 for (auto &each : colors) {
430 surface_->format->palette->colors[i].r = each.x;
431 surface_->format->palette->colors[i].g = each.y;
432 surface_->format->palette->colors[i].b = each.z;
433 surface_->format->palette->colors[i].a = each.w;
434 i++;
435 }
436 SDL_LockSurface(surface_.get());
437 // SDL_RETURN_IF_ERROR()
438 return absl::OkStatus();
439}
440
441void Bitmap::ApplyPalette(const std::vector<SDL_Color> &palette) {
442 SDL_UnlockSurface(surface_.get());
443 for (size_t i = 0; i < palette.size(); ++i) {
444 surface_->format->palette->colors[i].r = palette[i].r;
445 surface_->format->palette->colors[i].g = palette[i].g;
446 surface_->format->palette->colors[i].b = palette[i].b;
447 surface_->format->palette->colors[i].a = palette[i].a;
448 }
449 SDL_LockSurface(surface_.get());
450}
451
452void Bitmap::Get8x8Tile(int tile_index, int x, int y,
453 std::vector<uint8_t> &tile_data,
454 int &tile_data_offset) {
455 int tile_offset = tile_index * (width_ * height_);
456 int tile_x = (x * 8) % width_;
457 int tile_y = (y * 8) % height_;
458 for (int i = 0; i < 8; i++) {
459 int row_offset = tile_offset + ((tile_y + i) * width_);
460 for (int j = 0; j < 8; j++) {
461 int pixel_offset = row_offset + (tile_x + j);
462 int pixel_value = data_[pixel_offset];
463 tile_data[tile_data_offset] = pixel_value;
464 tile_data_offset++;
465 }
466 }
467}
468
469void Bitmap::Get16x16Tile(int tile_index, int x, int y,
470 std::vector<uint8_t> &tile_data,
471 int &tile_data_offset) {
472 int tile_offset = tile_index * (width_ * height_);
473 for (int i = 0; i < 16; i++) {
474 int row_offset = tile_offset + ((i / 8) * (width_ * 8));
475 for (int j = 0; j < 16; j++) {
476 int pixel_offset =
477 row_offset + ((j / 8) * 8) + ((i % 8) * width_) + (j % 8);
478 int pixel_value = data_[pixel_offset];
479 tile_data[tile_data_offset] = pixel_value;
480 tile_data_offset++;
481 }
482 }
483}
484
485void Bitmap::Get16x16Tile(int tile_x, int tile_y,
486 std::vector<uint8_t> &tile_data,
487 int &tile_data_offset) {
488 for (int ty = 0; ty < 16; ty++) {
489 for (int tx = 0; tx < 16; tx++) {
490 // Calculate the pixel position in the bitmap
491 int pixel_x = tile_x + tx;
492 int pixel_y = tile_y + ty;
493 int pixel_offset = pixel_y * width_ + pixel_x;
494 int pixel_value = data_[pixel_offset];
495
496 // Store the pixel value in the tile data
497 tile_data[tile_data_offset++] = pixel_value;
498 }
499 }
500}
501
502void Bitmap::WriteColor(int position, const ImVec4 &color) {
503 // Convert ImVec4 (RGBA) to SDL_Color (RGBA)
504 SDL_Color sdl_color;
505 sdl_color.r = static_cast<Uint8>(color.x * 255);
506 sdl_color.g = static_cast<Uint8>(color.y * 255);
507 sdl_color.b = static_cast<Uint8>(color.z * 255);
508 sdl_color.a = static_cast<Uint8>(color.w * 255);
509
510 // Map SDL_Color to the nearest color index in the surface's palette
511 Uint8 index =
512 SDL_MapRGB(surface_->format, sdl_color.r, sdl_color.g, sdl_color.b);
513
514 // Write the color index to the pixel data
515 pixel_data_[position] = index;
516 modified_ = true;
517}
518
519} // namespace gfx
520} // namespace app
521} // namespace yaze
void Reformat(int format)
Definition bitmap.cc:268
uint8_t * pixel_data_
Definition bitmap.h:210
std::shared_ptr< SDL_Surface > surface_
Definition bitmap.h:217
void Get16x16Tile(int tile_index, int x, int y, std::vector< uint8_t > &tile_data, int &tile_data_offset)
Definition bitmap.cc:469
absl::Status ApplyPaletteWithTransparent(const SnesPalette &palette, size_t index, int length=7)
Definition bitmap.cc:399
void Get8x8Tile(int tile_index, int x, int y, std::vector< uint8_t > &tile_data, int &tile_data_offset)
Definition bitmap.cc:452
std::vector< uint8_t > png_data_
Definition bitmap.h:213
int height() const
Definition bitmap.h:180
void SaveSurfaceToFile(std::string_view filename)
Definition bitmap.cc:224
void CreateTexture(std::shared_ptr< SDL_Renderer > renderer)
Creates the underlying SDL_Texture to be displayed.
Definition bitmap.cc:334
absl::Status ApplyPalette(const SnesPalette &palette)
Copy color data from the SnesPalette into the SDL_Palette.
Definition bitmap.cc:346
std::vector< uint8_t > data_
Definition bitmap.h:211
void Create(int width, int height, int depth, const std::vector< uint8_t > &data)
Creates a bitmap object with the provided graphical data.
Definition bitmap.cc:232
auto converted_surface() const
Definition bitmap.h:188
int width() const
Definition bitmap.h:179
auto data() const
Definition bitmap.h:183
absl::Status ApplyPaletteFromPaletteGroup(const SnesPalette &palette, int palette_id)
Definition bitmap.cc:375
gfx::SnesPalette palette_
Definition bitmap.h:215
std::shared_ptr< SDL_Surface > converted_surface_
Definition bitmap.h:218
auto depth() const
Definition bitmap.h:181
void UpdateTexture(std::shared_ptr< SDL_Renderer > renderer)
Updates the underlying SDL_Texture when it already exists.
Definition bitmap.cc:340
auto palette() const
Definition bitmap.h:175
void WriteColor(int position, const ImVec4 &color)
Definition bitmap.cc:502
std::shared_ptr< SDL_Texture > texture_
Definition bitmap.h:216
SNES Color container.
Definition snes_color.h:38
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
SnesPalette sub_palette(int start, int end) const
absl::StatusOr< SnesColor > GetColor(int i) const
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition constants.h:77
void GrayscalePalette(SDL_Palette *palette)
Definition bitmap.cc:201
constexpr Uint32 SNES_PIXELFORMAT_4BPP
Definition bitmap.h:34
constexpr Uint32 SNES_PIXELFORMAT_2BPP
Definition bitmap.h:30
constexpr Uint32 SNES_PIXELFORMAT_8BPP
Definition bitmap.h:38
Definition common.cc:21
Deleter for SDL_Surface.
Definition sdl_deleter.h:30
Deleter for SDL_Texture.
Definition sdl_deleter.h:21