libmx2 2.2.1
C++20 multimedia and game-engine library (SDL2 / OpenGL / Vulkan)
Loading...
Searching...
No Matches
loadpng.cpp
Go to the documentation of this file.
1/**
2 * @file loadpng.cpp
3 * @brief Implementation of png::LoadPNG, png::SavePNG, and png::SavePNG_RGBA utilities.
4 */
5#include"loadpng.hpp"
6#include<png.h>
7#include"SDL.h"
8#include<cstdio>
9#include<cstdlib>
10#include<cctype>
11#include<string>
12#include<iostream>
13#include"tee_stream.hpp"
14
15#if defined(_MSC_VER)
16 #if _MSC_VER >= 1930
17 #define SAFE_FUNC
18 #endif
19#endif
20
21namespace png {
22 SDL_Surface* LoadPNG(const char* file) {
23 auto chkString = [](const std::string &filename) -> bool {
24 std::string lwr;
25 for(size_t i = 0; i < filename.length(); ++i)
26 lwr += tolower(filename[i]);
27 if(lwr.find(".png") == std::string::npos)
28 return false;
29
30 return true;
31 };
32
33 if(chkString(file) == false)
34 return NULL;
35
36 #ifdef SAFE_FUNC
37 FILE* fp = NULL;
38 errno_t err = fopen_s(&fp, file, "rb");
39 if (!(err == 0 && file != NULL)) {
40 mx::system_err << "Failed to open png file: " << file << "\n";
41 return nullptr;
42 }
43 #else
44 FILE* fp = fopen(file, "rb");
45 if (!fp) {
46 mx::system_err << "Failed to open file: " << file << "\n";
47 return nullptr;
48 }
49 #endif
50 png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
51 if (!png) {
52 fclose(fp);
53 return nullptr;
54 }
55
56 png_infop info = png_create_info_struct(png);
57 if (!info) {
58 png_destroy_read_struct(&png, nullptr, nullptr);
59 fclose(fp);
60 return nullptr;
61 }
62
63 if (setjmp(png_jmpbuf(png))) {
64 png_destroy_read_struct(&png, &info, nullptr);
65 fclose(fp);
66 return nullptr;
67 }
68
69 png_init_io(png, fp);
70 png_read_info(png, info);
71
72 int width = png_get_image_width(png, info);
73 int height = png_get_image_height(png, info);
74 png_byte color_type = png_get_color_type(png, info);
75 png_byte bit_depth = png_get_bit_depth(png, info);
76
77 if (bit_depth == 16) {
78 png_set_strip_16(png);
79 }
80
81 if (color_type == PNG_COLOR_TYPE_PALETTE) {
82 png_set_palette_to_rgb(png);
83 }
84
85 if (png_get_valid(png, info, PNG_INFO_tRNS)) {
86 png_set_tRNS_to_alpha(png);
87 }
88
89 if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
90 png_set_gray_to_rgb(png);
91 }
92
93 if (color_type == PNG_COLOR_TYPE_RGB) {
94 png_set_add_alpha(png, 0xFF, PNG_FILLER_AFTER);
95 }
96
97 png_read_update_info(png, info);
98
99 int pitch = png_get_rowbytes(png, info);
100 Uint32 rmask, gmask, bmask, amask;
101
102 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
103 rmask = 0xFF000000;
104 gmask = 0x00FF0000;
105 bmask = 0x0000FF00;
106 amask = 0x000000FF;
107 #else
108 rmask = 0x000000FF;
109 gmask = 0x0000FF00;
110 bmask = 0x00FF0000;
111 amask = 0xFF000000;
112 #endif
113
114 SDL_Surface* surface = SDL_CreateRGBSurface(0, width, height, 32, rmask, gmask, bmask, amask);
115 if (!surface) {
116 png_destroy_read_struct(&png, &info, nullptr);
117 fclose(fp);
118 return nullptr;
119 }
120
121 png_bytep* row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
122 if(row_pointers == NULL) {
123 fprintf(stderr, "Error memory allocation error.\n");
124 if (surface)
125 SDL_FreeSurface(surface);
126 if (png && info)
127 png_destroy_read_struct(&png, &info, nullptr);
128 if (fp)
129 fclose(fp);
130 return NULL;
131 }
132 Uint8* pixels = (Uint8*) surface->pixels;
133
134 for (int y = 0; y < height; ++y) {
135 row_pointers[y] = pixels + y * pitch;
136 }
137
138 png_read_image(png, row_pointers);
139 png_read_end(png, nullptr);
140
141 free(row_pointers);
142 png_destroy_read_struct(&png, &info, nullptr);
143 fclose(fp);
144 return surface;
145 }
146
147
148 bool SavePNG(SDL_Texture* texture, SDL_Renderer* renderer, const char* filename) {
149 int width, height;
150 SDL_QueryTexture(texture, nullptr, nullptr, &width, &height);
151 SDL_Surface* surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, SDL_PIXELFORMAT_RGBA32);
152 if (!surface) {
153 mx::system_err << "mx: Failed to create SDL_Surface: " << SDL_GetError() << "\n";
154 return false;
155 }
156
157 SDL_Texture *old = SDL_GetRenderTarget(renderer);
158
159 SDL_SetRenderTarget(renderer, texture);
160 if (SDL_RenderReadPixels(renderer, nullptr, surface->format->format, surface->pixels, surface->pitch) != 0) {
161 std::cerr << "mx: Failed to read pixels from renderer: " << SDL_GetError() << "\n";
162 SDL_FreeSurface(surface);
163 return false;
164 }
165 SDL_SetRenderTarget(renderer, nullptr);
166 FILE* fp = fopen(filename, "wb");
167 if (!fp) {
168 mx::system_err << "mx: Failed to open file for writing: " << filename << "\n";
169 SDL_FreeSurface(surface);
170 return false;
171 }
172
173 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
174 if (!png) {
175 mx::system_err << "mx: Failed to create PNG write struct." << "\n";
176 fclose(fp);
177 SDL_FreeSurface(surface);
178 return false;
179 }
180
181 png_infop info = png_create_info_struct(png);
182 if (!info) {
183 mx::system_err << "mx: Failed to create PNG info struct." << "\n";
184 png_destroy_write_struct(&png, nullptr);
185 fclose(fp);
186 SDL_FreeSurface(surface);
187 return false;
188 }
189
190 if (setjmp(png_jmpbuf(png))) {
191 mx::system_err << "mx: Error during PNG creation." << "\n";
192 png_destroy_write_struct(&png, &info);
193 fclose(fp);
194 SDL_FreeSurface(surface);
195 return false;
196 }
197
198 png_init_io(png, fp);
199 png_set_IHDR(png, info, surface->w, surface->h, 8, PNG_COLOR_TYPE_RGBA,
200 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
201 png_write_info(png, info);
202
203 uint8_t* pixels = static_cast<uint8_t*>(surface->pixels);
204 png_bytep* row_pointers = new png_bytep[surface->h];
205 for (int y = 0; y < surface->h; ++y) {
206 row_pointers[y] = pixels + y * surface->pitch;
207 }
208
209 png_write_image(png, row_pointers);
210 png_write_end(png, nullptr);
211
212 delete[] row_pointers;
213 png_destroy_write_struct(&png, &info);
214 fclose(fp);
215 SDL_FreeSurface(surface);
216 SDL_SetRenderTarget(renderer, old);
217 return true;
218 }
219
220 bool SavePNG_RGBA(const char *filename, void *buffer, int w, int h) {
221 int pitch = w * 4;
222 FILE* fp = fopen(filename, "wb");
223 if (!fp) {
224 mx::system_err << "mx: Failed to open file for writing: " << filename << "\n";
225 return false;
226 }
227 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
228 if (!png) {
229 mx::system_err << "mx: Failed to create PNG write struct." << "\n";
230 fclose(fp);
231 return false;
232 }
233 png_infop info = png_create_info_struct(png);
234 if (!info) {
235 mx::system_err << "mx: Failed to create PNG info struct." << "\n";
236 png_destroy_write_struct(&png, nullptr);
237 fclose(fp);
238 return false;
239 }
240 if (setjmp(png_jmpbuf(png))) {
241 mx::system_err << "mx: Error during PNG creation." << "\n";
242 png_destroy_write_struct(&png, &info);
243 fclose(fp);
244 return false;
245 }
246 png_init_io(png, fp);
247 png_set_IHDR(png, info, w, h, 8, PNG_COLOR_TYPE_RGBA,
248 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
249 png_write_info(png, info);
250
251 uint8_t* pixels = static_cast<uint8_t*>(buffer);
252 png_bytep* row_pointers = new png_bytep[h];
253 for (int y = 0; y < h; ++y) {
254 row_pointers[y] = pixels + y * pitch;
255 }
256
257 png_write_image(png, row_pointers);
258 png_write_end(png, nullptr);
259
260 delete[] row_pointers;
261 png_destroy_write_struct(&png, &info);
262 fclose(fp);
263 return true;
264 }
265}
PNG image loading and saving utilities via SDL.
TeeStream system_err(std::cerr, error_file)
Global stderr + log tee stream.
Utilities for loading and saving PNG images.
Definition loadpng.cpp:21
bool SavePNG(SDL_Texture *texture, SDL_Renderer *renderer, const char *filename)
Save an SDL_Texture to a PNG file.
Definition loadpng.cpp:148
SDL_Surface * LoadPNG(const char *file)
Load a PNG file into an SDL_Surface.
Definition loadpng.cpp:22
bool SavePNG_RGBA(const char *filename, void *buffer, int w, int h)
Save a raw RGBA pixel buffer to a PNG file.
Definition loadpng.cpp:220
Tee stream utilities that duplicate output to two sinks simultaneously.