20 static void handleError(png_structp png_ptr, png_const_charp error_msg)
22 const auto* operation =
reinterpret_cast<const char*
>(
23 png_get_error_ptr(png_ptr));
24 throw MSXException(
"Error while ", operation,
" PNG: ", error_msg);
27 static void handleWarning(png_structp png_ptr, png_const_charp warning_msg)
29 const auto* operation =
reinterpret_cast<const char*
>(
30 png_get_error_ptr(png_ptr));
31 std::cerr <<
"Warning while " << operation <<
" PNG: "
32 << warning_msg <<
'\n';
79 png_destroy_read_struct(&
ptr,
info ? &
info :
nullptr,
nullptr);
85 png_structp
ptr =
nullptr;
89 static void readData(png_structp ctx, png_bytep area, png_size_t
size)
91 auto* file =
reinterpret_cast<File*
>(png_get_io_ptr(ctx));
102 png.
ptr = png_create_read_struct(
103 PNG_LIBPNG_VER_STRING,
104 const_cast<char*
>(
"decoding"), handleError, handleWarning);
110 png.
info = png_create_info_struct(png.
ptr);
112 throw MSXException(
"Failed to allocate image info struct");
116 png_set_read_fn(png.
ptr, &file, readData);
119 png_read_info(png.
ptr, png.
info);
120 png_uint_32 width, height;
121 int bit_depth, color_type, interlace_type;
122 png_get_IHDR(png.
ptr, png.
info, &width, &height, &bit_depth,
123 &color_type, &interlace_type,
nullptr,
nullptr);
126 png_set_strip_16(png.
ptr);
130 png_set_packing(png.
ptr);
136 png_set_expand(png.
ptr);
139 png_set_filler(png.
ptr, 0xff, PNG_FILLER_AFTER);
150 bool bgr(
true), swapAlpha(
false);
152 int displayIndex = 0;
153 SDL_DisplayMode currentMode;
154 if (SDL_GetCurrentDisplayMode(displayIndex, ¤tMode) == 0) {
156 Uint32 Rmask, Gmask, Bmask, Amask;
157 SDL_PixelFormatEnumToMasks(
158 currentMode.format, &bpp, &Rmask, &Gmask, &Bmask, &Amask);
160 if (Rmask == 0x000000FF &&
161 Gmask == 0x0000FF00 &&
162 Bmask == 0x00FF0000) {
163 bgr =
false; swapAlpha =
false;
164 }
else if (Rmask == 0x00FF0000 &&
165 Gmask == 0x0000FF00 &&
166 Bmask == 0x000000FF) {
167 bgr =
true; swapAlpha =
false;
168 }
else if (Rmask == 0x0000FF00 &&
169 Gmask == 0x00FF0000 &&
170 Bmask == 0xFF000000) {
171 bgr =
false; swapAlpha =
true;
172 }
else if (Rmask == 0xFF000000 &&
173 Gmask == 0x00FF0000 &&
174 Bmask == 0x0000FF00) {
175 bgr =
true; swapAlpha =
true;
179 if (bgr) png_set_bgr (png.
ptr);
180 if (swapAlpha) png_set_swap_alpha(png.
ptr);
185 png_set_gray_to_rgb(png.
ptr);
187 png_read_update_info(png.
ptr, png.
info);
189 png_get_IHDR(png.
ptr, png.
info, &width, &height, &bit_depth,
190 &color_type, &interlace_type,
nullptr,
nullptr);
196 "Attempted to create a surface with excessive width: ",
201 "Attempted to create a surface with excessive height: ",
204 int bpp = png_get_channels(png.
ptr, png.
info) * 8;
205 assert(bpp ==
one_of(24, 32));
206 auto [redMask, grnMask, bluMask, alpMask] = [&]()-> std::tuple<Uint32, Uint32, Uint32, Uint32> {
210 return {0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000};
212 return {0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF};
215 return {0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000};
220 return {0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF};
222 return {0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000};
225 return {0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000};
229 if (bgr) std::swap(redMask, bluMask);
231 redMask, grnMask, bluMask, alpMask);
234 VLA(png_bytep, row_pointers, height);
235 for (
auto row :
xrange(height)) {
236 row_pointers[row] =
reinterpret_cast<png_bytep
>(
241 png_read_image(png.
ptr, row_pointers);
250 "Error while loading PNG file \"",
filename,
"\": ",
264 png_destroy_write_struct(&
ptr,
info ? &
info :
nullptr);
270 png_structp
ptr =
nullptr;
274 static void writeData(png_structp ctx, png_bytep area, png_size_t
size)
276 auto* file =
reinterpret_cast<File*
>(png_get_io_ptr(ctx));
280 static void flushData(png_structp ctx)
282 auto* file =
reinterpret_cast<File*
>(png_get_io_ptr(ctx));
286 static void IMG_SavePNG_RW(
int width,
int height,
const void** row_pointers,
287 const std::string&
filename,
bool color)
293 png.ptr = png_create_write_struct(
294 PNG_LIBPNG_VER_STRING,
295 const_cast<char*
>(
"encoding"), handleError, handleWarning);
297 throw MSXException(
"Failed to allocate main struct");
301 png.info = png_create_info_struct(png.ptr);
304 throw MSXException(
"Failed to allocate image info struct");
308 png_set_write_fn(png.ptr, &file, writeData, flushData);
313 text[0].compression = PNG_TEXT_COMPRESSION_NONE;
314 text[0].key =
const_cast<char*
>(
"Software");
315 text[0].text =
const_cast<char*
>(version.c_str());
316 text[1].compression = PNG_TEXT_COMPRESSION_NONE;
317 text[1].key =
const_cast<char*
>(
"Creation Time");
325 static constexpr
size_t size = (10 + 1 + 8 + 1) + 44;
326 time_t now = time(
nullptr);
327 struct tm* tm = localtime(&now);
329 snprintf(timeStr,
sizeof(timeStr),
"%04d-%02d-%02d %02d:%02d:%02d",
330 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
331 tm->tm_hour, tm->tm_min, tm->tm_sec);
332 text[1].text = timeStr;
334 png_set_text(png.ptr, png.info, text, 2);
336 png_set_IHDR(png.ptr, png.info, width, height, 8,
337 color ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_GRAY,
338 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
339 PNG_FILTER_TYPE_BASE);
342 png_write_info(png.ptr, png.info);
347 reinterpret_cast<png_bytep*
>(
const_cast<void**
>(row_pointers)));
348 png_write_end(png.ptr, png.info);
349 }
catch (MSXException&
e) {
351 "Error while writing PNG file \"",
filename,
"\": ",
359 Endian::BIG ? SDL_PIXELFORMAT_BGR24 : SDL_PIXELFORMAT_RGB24));
363 VLA(
const void*, row_pointers,
image->h);
365 row_pointers[i] = surf24.getLinePtr(i);
371 void save(
unsigned width,
unsigned height,
const void** rowPointers,
376 width, height,
format.getBpp(),
378 for (
auto y :
xrange(height)) {
380 rowPointers[y], width *
format.getBytesPerPixel());
385 void save(
unsigned width,
unsigned height,
386 const void** rowPointers,
const std::string&
filename)
388 IMG_SavePNG_RW(width, height, rowPointers,
filename,
true);
392 const void** rowPointers,
const std::string&
filename)
394 IMG_SavePNG_RW(width, height, rowPointers,
filename,
false);
std::unique_ptr< SDL_PixelFormat, SDLFreeFormat > SDLAllocFormatPtr
Wrapper around a SDL_Surface.
void * getLinePtr(unsigned y)
void read(void *buffer, size_t num)
Read from file.
void write(const void *buffer, size_t num)
Write to file.
void flush()
Force a write of all buffered data to disk.
static std::string full()
void format(SectorAccessibleDisk &disk, bool dos1)
Format the given disk (= a single partition).
Utility functions to hide the complexity of saving to a PNG file.
void saveGrayscale(unsigned width, unsigned height, const void **rowPointers, const std::string &filename)
void save(unsigned width, unsigned height, const void **rowPointers, const std::string &filename)
SDLSurfacePtr load(const std::string &filename, bool want32bpp)
Load the given PNG file in a SDL_Surface.
constexpr const char *const filename
size_t size(std::string_view utf8)
PNGReadHandle(const PNGReadHandle &)=delete
PNGReadHandle & operator=(const PNGReadHandle &)=delete
PNGWriteHandle & operator=(const PNGWriteHandle &)=delete
PNGWriteHandle(const PNGWriteHandle &)=delete
#define VLA(TYPE, NAME, LENGTH)
constexpr auto xrange(T e)