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