20namespace png_internal {
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);
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);
32 std::vector<uint8_t> *png_data =
33 reinterpret_cast<std::vector<uint8_t> *
>(io_ptr);
34 static size_t pos = 0;
36 if (pos + byteCountToRead <= png_data->size()) {
37 memcpy(outBytes, png_data->data() + pos, byteCountToRead);
38 pos += byteCountToRead;
40 png_error(png_ptr,
"Read error in PngReadCallback");
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);
49 SDL_Log(
"Failed to create PNG write struct");
53 png_infop info_ptr = png_create_info_struct(png_ptr);
55 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
56 SDL_Log(
"Failed to create PNG info struct");
60 if (setjmp(png_jmpbuf(png_ptr))) {
61 png_destroy_write_struct(&png_ptr, &info_ptr);
62 SDL_Log(
"Error during PNG write");
66 png_set_write_fn(png_ptr, &buffer, png_internal::PngWriteCallback, NULL);
71 int colortype = PNG_COLOR_MASK_COLOR;
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;
84 png_set_PLTE(png_ptr, info_ptr, pal_ptr, pal->ncolors);
88 if (surface->format->Amask) {
89 colortype |= PNG_COLOR_MASK_ALPHA;
92 auto depth = surface->format->BitsPerPixel;
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);
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;
107 png_set_rows(png_ptr, info_ptr, row_pointers.data());
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");
113 png_destroy_write_struct(&png_ptr, &info_ptr);
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);
123 throw std::runtime_error(
"Failed to create PNG read struct");
126 png_infop info_ptr = png_create_info_struct(png_ptr);
128 png_destroy_read_struct(&png_ptr, NULL, NULL);
129 throw std::runtime_error(
"Failed to create PNG info struct");
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");
138 png_set_read_fn(png_ptr, &data, png_internal::PngReadCallback);
141 png_read_info(png_ptr, info_ptr);
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);
152 png_read_update_info(png_ptr, info_ptr);
155 std::vector<uint8_t> raw_data(width * height *
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;
162 png_read_image(png_ptr, row_pointers.data());
163 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
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());
174 SDL_LockSurface(*outSurface);
175 memcpy((*outSurface)->pixels, raw_data.data(), raw_data.size());
176 SDL_UnlockSurface(*outSurface);
178 SDL_Log(
"Successfully created SDL_Surface from PNG data");
185 using std::runtime_error::runtime_error;
191 return SDL_PIXELFORMAT_INDEX8;
197 return SDL_PIXELFORMAT_INDEX8;
202 const std::vector<uint8_t> &
data)
224 const std::vector<uint8_t> &
data) {
229 const std::vector<uint8_t> &
data) {
231 SDL_Log(
"Bitmap data is empty\n");
240 SDL_Log(
"Data provided to Bitmap is empty.\n");
249 SDL_Log(
"Bitmap::Create.SDL_CreateRGBSurfaceWithFormat failed: %s\n",
277 SDL_Log(
"Invalid renderer passed to CreateTexture");
282 SDL_Log(
"Invalid texture dimensions: width=%d, height=%d\n",
width_,
290 SDL_Log(
"Bitmap::CreateTexture failed to allocate texture: %s\n",
309 throw BitmapError(
"Surface is null. Palette not applied");
313 "Surface format or palette is null. Palette not applied.");
317 SDL_Palette *sdl_palette =
surface_->format->palette;
318 if (sdl_palette ==
nullptr) {
323 for (
size_t i = 0; i <
palette.size(); ++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;
336 throw std::invalid_argument(
"Invalid palette index");
339 if (length < 0 || length >
palette.size()) {
340 throw std::invalid_argument(
"Invalid palette length");
343 if (index + length >
palette.size()) {
344 throw std::invalid_argument(
"Palette index + length exceeds size");
348 throw BitmapError(
"Surface is null. Palette not applied");
351 auto start_index = 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) {
357 colors.push_back(pal_color.rgb());
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;
374 for (
size_t i = 0; i <
palette.size(); ++i) {
388 data_[position] = value;
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);
402 SDL_MapRGB(
surface_->format, sdl_color.r, sdl_color.g, sdl_color.b);
412 std::vector<uint8_t> &tile_data,
413 int &tile_data_offset) {
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;
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++) {
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];
440 tile_data[tile_data_offset] = pixel_value;
446std::vector<uint8_t> Bitmap::GetPngData() {
447 std::vector<uint8_t> png_data;
448 ConvertSurfaceToPng(
surface_, png_data);
SDL_Texture * AllocateTexture(SDL_Renderer *renderer, int width, int height)
SDL_Surface * AllocateSurface(int width, int height, int depth, int format)
void UpdateTexture(SDL_Texture *texture, SDL_Surface *surface)
const uint8_t * data() const
const SnesPalette & palette() const
void WriteToPixel(int position, uint8_t value)
Write a value to a pixel at the given position.
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
void UpdateTexture(SDL_Renderer *renderer)
Updates the underlying SDL_Texture when it already exists.
void Reformat(int format)
Reformat the bitmap to use a different pixel format.
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.
void WriteColor(int position, const ImVec4 &color)
Write a color to a pixel at the given position.
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap.
std::vector< uint8_t > data_
void CreateTexture(SDL_Renderer *renderer)
Creates the underlying SDL_Texture to be displayed.
void SetPaletteWithTransparent(const SnesPalette &palette, size_t index, int length=7)
Set the palette with a transparent color.
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.
gfx::SnesPalette palette_
void UpdateTextureData()
Updates the texture data from the surface.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
Contains classes for handling graphical data.
uint16_t ConvertRgbToSnes(const snes_color &color)
constexpr Uint32 SNES_PIXELFORMAT_8BPP
constexpr Uint32 SNES_PIXELFORMAT_4BPP
Uint32 GetSnesPixelFormat(int format)
Get the SDL pixel format for a given bitmap format.
Main namespace for the application.