34 const std::uint8_t *pixels,
39 const bool swap_16bit_endianness) {
40 if (filename ==
nullptr || pixels ==
nullptr || width <= 0 || height <= 0 || pitch <= 0) {
41 std::cerr <<
"mx: Invalid PNG write parameters.\n";
45 FILE *file = std::fopen(filename,
"wb");
46 if (file ==
nullptr) {
47 std::cerr <<
"mx: Failed to open file for writing: " << filename <<
"\n";
51 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
53 std::cerr <<
"mx: Failed to create PNG write struct.\n";
58 png_infop info = png_create_info_struct(png);
59 if (info ==
nullptr) {
60 std::cerr <<
"mx: Failed to create PNG info struct.\n";
61 png_destroy_write_struct(&png,
nullptr);
66 if (setjmp(png_jmpbuf(png))) {
67 std::cerr <<
"mx: Error during PNG creation.\n";
68 png_destroy_write_struct(&png, &info);
73 png_init_io(png, file);
81 PNG_COMPRESSION_TYPE_DEFAULT,
82 PNG_FILTER_TYPE_DEFAULT);
84 if (bit_depth == 16 && swap_16bit_endianness) {
88 png_write_info(png, info);
90 std::vector<png_bytep> rows(
static_cast<std::size_t
>(height));
91 for (
int y = 0; y < height; ++y) {
92 rows[
static_cast<std::size_t
>(y)] =
const_cast<png_bytep
>(pixels + (y * pitch));
95 png_write_image(png, rows.data());
96 png_write_end(png,
nullptr);
98 png_destroy_write_struct(&png, &info);
108 FILE *fp = std::fopen(file,
"rb");
110 std::cerr <<
"Failed to open PNG file: " << file <<
"\n";
114 png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
115 if (png ==
nullptr) {
120 png_infop info = png_create_info_struct(png);
121 if (info ==
nullptr) {
122 png_destroy_read_struct(&png,
nullptr,
nullptr);
127 if (setjmp(png_jmpbuf(png))) {
128 png_destroy_read_struct(&png, &info,
nullptr);
133 png_init_io(png, fp);
134 png_read_info(png, info);
136 const int width =
static_cast<int>(png_get_image_width(png, info));
137 const int height =
static_cast<int>(png_get_image_height(png, info));
138 const png_byte color_type = png_get_color_type(png, info);
139 const png_byte bit_depth = png_get_bit_depth(png, info);
141 if (width <= 0 || height <= 0) {
142 png_destroy_read_struct(&png, &info,
nullptr);
147 if (bit_depth == 16) {
148 png_set_strip_16(png);
151 if (color_type == PNG_COLOR_TYPE_PALETTE) {
152 png_set_palette_to_rgb(png);
155 if (png_get_valid(png, info, PNG_INFO_tRNS) != 0) {
156 png_set_tRNS_to_alpha(png);
159 if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
160 png_set_gray_to_rgb(png);
163 if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY ||
164 color_type == PNG_COLOR_TYPE_PALETTE) {
165 png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
168 png_read_update_info(png, info);
170 SDL_Surface *
surface = SDL_CreateSurface(width, height, SDL_PIXELFORMAT_RGBA32);
172 png_destroy_read_struct(&png, &info,
nullptr);
177 const png_size_t row_bytes = png_get_rowbytes(png, info);
178 if (row_bytes >
static_cast<png_size_t
>(
surface->pitch)) {
179 std::cerr <<
"mx: Unexpected PNG row stride mismatch for file: " << file <<
"\n";
181 png_destroy_read_struct(&png, &info,
nullptr);
186 auto *pixels =
static_cast<std::uint8_t *
>(
surface->pixels);
187 std::vector<png_bytep> row_pointers(
static_cast<std::size_t
>(height));
188 for (
int y = 0; y < height; ++y) {
189 row_pointers[
static_cast<std::size_t
>(y)] = pixels + (y *
surface->pitch);
192 png_read_image(png, row_pointers.data());
193 png_read_end(png,
nullptr);
195 png_destroy_read_struct(&png, &info,
nullptr);
200 bool SavePNG(SDL_Texture *texture, SDL_Renderer *renderer,
const char *filename) {
201 if (texture ==
nullptr || renderer ==
nullptr || filename ==
nullptr) {
202 std::cerr <<
"mx: Invalid SavePNG parameters.\n";
206 float width_f = 0.0F;
207 float height_f = 0.0F;
208 if (!SDL_GetTextureSize(texture, &width_f, &height_f)) {
209 std::cerr <<
"mx: Failed to query texture size: " << SDL_GetError() <<
"\n";
213 if (!std::isfinite(width_f) || !std::isfinite(height_f) || width_f <= 0.0F || height_f <= 0.0F) {
214 std::cerr <<
"mx: Invalid texture size while saving PNG.\n";
218 const int width =
static_cast<int>(width_f);
219 const int height =
static_cast<int>(height_f);
220 if (width <= 0 || height <= 0) {
221 std::cerr <<
"mx: Invalid integer texture size while saving PNG.\n";
225 SDL_Texture *previous_target = SDL_GetRenderTarget(renderer);
226 if (!SDL_SetRenderTarget(renderer, texture)) {
227 std::cerr <<
"mx: Failed to set render target: " << SDL_GetError() <<
"\n";
231 SDL_Surface *captured = SDL_RenderReadPixels(renderer,
nullptr);
233 if (!SDL_SetRenderTarget(renderer, previous_target)) {
234 std::cerr <<
"mx: Failed to restore render target: " << SDL_GetError() <<
"\n";
237 if (captured ==
nullptr) {
238 std::cerr <<
"mx: Failed to read pixels from renderer: " << SDL_GetError() <<
"\n";
242 std::unique_ptr<SDL_Surface,
decltype(&SDL_DestroySurface)> captured_surface(captured, &SDL_DestroySurface);
244 SDL_Surface *rgba32_surface_raw = captured_surface.get();
245 std::unique_ptr<SDL_Surface,
decltype(&SDL_DestroySurface)> converted_surface(
nullptr, &SDL_DestroySurface);
246 if (captured_surface->format != SDL_PIXELFORMAT_RGBA32) {
247 rgba32_surface_raw = SDL_ConvertSurface(captured_surface.get(), SDL_PIXELFORMAT_RGBA32);
248 if (rgba32_surface_raw ==
nullptr) {
249 std::cerr <<
"mx: Failed to convert surface to RGBA32: " << SDL_GetError() <<
"\n";
252 converted_surface.reset(rgba32_surface_raw);
255 const auto *pixels =
static_cast<const std::uint8_t *
>(rgba32_surface_raw->pixels);
258 rgba32_surface_raw->w,
259 rgba32_surface_raw->h,
260 rgba32_surface_raw->pitch,
bool WriteRgbaPng(const char *filename, const std::uint8_t *pixels, const int width, const int height, const int pitch, const int bit_depth, const bool swap_16bit_endianness)