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