23static void handleError(png_structp png_ptr, png_const_charp error_msg)
25 const auto* operation =
reinterpret_cast<const char*
>(
26 png_get_error_ptr(png_ptr));
27 throw MSXException(
"Error while ", operation,
" PNG: ", error_msg);
30static void handleWarning(png_structp png_ptr, png_const_charp warning_msg)
32 const auto* operation =
reinterpret_cast<const char*
>(
33 png_get_error_ptr(png_ptr));
34 std::cerr <<
"Warning while " << operation <<
" PNG: "
35 << warning_msg <<
'\n';
82 png_destroy_read_struct(&
ptr,
info ? &
info :
nullptr,
nullptr);
88 png_structp
ptr =
nullptr;
92static void readData(png_structp ctx, png_bytep area, png_size_t
size)
94 auto* file =
reinterpret_cast<File*
>(png_get_io_ptr(ctx));
105 png.
ptr = png_create_read_struct(
106 PNG_LIBPNG_VER_STRING,
107 const_cast<char*
>(
"decoding"), handleError, handleWarning);
113 png.
info = png_create_info_struct(png.
ptr);
115 throw MSXException(
"Failed to allocate image info struct");
119 png_set_read_fn(png.
ptr, &file, readData);
122 png_read_info(png.
ptr, png.
info);
123 png_uint_32 width, height;
124 int bit_depth, color_type, interlace_type;
125 png_get_IHDR(png.
ptr, png.
info, &width, &height, &bit_depth,
126 &color_type, &interlace_type,
nullptr,
nullptr);
129 png_set_strip_16(png.
ptr);
133 png_set_packing(png.
ptr);
139 png_set_expand(png.
ptr);
142 png_set_filler(png.
ptr, 0xff, PNG_FILLER_AFTER);
153 bool bgr(
true), swapAlpha(
false);
155 int displayIndex = 0;
156 SDL_DisplayMode currentMode;
157 if (SDL_GetCurrentDisplayMode(displayIndex, ¤tMode) == 0) {
159 Uint32 Rmask, Gmask, Bmask, Amask;
160 SDL_PixelFormatEnumToMasks(
161 currentMode.format, &bpp, &Rmask, &Gmask, &Bmask, &Amask);
163 if (Rmask == 0x000000FF &&
164 Gmask == 0x0000FF00 &&
165 Bmask == 0x00FF0000) {
166 bgr =
false; swapAlpha =
false;
167 }
else if (Rmask == 0x00FF0000 &&
168 Gmask == 0x0000FF00 &&
169 Bmask == 0x000000FF) {
170 bgr =
true; swapAlpha =
false;
171 }
else if (Rmask == 0x0000FF00 &&
172 Gmask == 0x00FF0000 &&
173 Bmask == 0xFF000000) {
174 bgr =
false; swapAlpha =
true;
175 }
else if (Rmask == 0xFF000000 &&
176 Gmask == 0x00FF0000 &&
177 Bmask == 0x0000FF00) {
178 bgr =
true; swapAlpha =
true;
182 if (bgr) png_set_bgr (png.
ptr);
183 if (swapAlpha) png_set_swap_alpha(png.
ptr);
188 png_set_gray_to_rgb(png.
ptr);
190 png_read_update_info(png.
ptr, png.
info);
192 png_get_IHDR(png.
ptr, png.
info, &width, &height, &bit_depth,
193 &color_type, &interlace_type,
nullptr,
nullptr);
196 constexpr unsigned MAX_SIZE = 2048;
197 if (width > MAX_SIZE) {
199 "Attempted to create a surface with excessive width: ",
200 width,
", max ", MAX_SIZE);
202 if (height > MAX_SIZE) {
204 "Attempted to create a surface with excessive height: ",
205 height,
", max ", MAX_SIZE);
207 int bpp = png_get_channels(png.
ptr, png.
info) * 8;
208 assert(bpp ==
one_of(24, 32));
209 auto [redMask, grnMask, bluMask, alpMask] = [&]()-> std::tuple<Uint32, Uint32, Uint32, Uint32> {
213 return {0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000};
215 return {0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF};
218 return {0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000};
223 return {0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF};
225 return {0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000};
228 return {0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000};
234 redMask, grnMask, bluMask, alpMask);
237 VLA(png_bytep, rowPointers, height);
238 for (
auto row :
xrange(height)) {
239 rowPointers[row] =
reinterpret_cast<png_bytep
>(
244 png_read_image(png.
ptr, rowPointers.data());
253 "Error while loading PNG file \"", filename,
"\": ",
267 png_destroy_write_struct(&
ptr,
info ? &
info :
nullptr);
273 png_structp
ptr =
nullptr;
277static void writeData(png_structp ctx, png_bytep area, png_size_t
size)
279 auto* file =
reinterpret_cast<File*
>(png_get_io_ptr(ctx));
283static void flushData(png_structp ctx)
285 auto* file =
reinterpret_cast<File*
>(png_get_io_ptr(ctx));
289static void IMG_SavePNG_RW(
size_t width, std::span<const void*> rowPointers,
290 const std::string& filename,
bool color)
292 auto height = rowPointers.size();
299 png.ptr = png_create_write_struct(
300 PNG_LIBPNG_VER_STRING,
301 const_cast<char*
>(
"encoding"), handleError, handleWarning);
303 throw MSXException(
"Failed to allocate main struct");
307 png.info = png_create_info_struct(png.ptr);
310 throw MSXException(
"Failed to allocate image info struct");
314 png_set_write_fn(png.ptr, &file, writeData, flushData);
318 std::array<png_text, 2> text;
319 text[0].compression = PNG_TEXT_COMPRESSION_NONE;
320 text[0].key =
const_cast<char*
>(
"Software");
321 text[0].text =
const_cast<char*
>(version.c_str());
322 text[1].compression = PNG_TEXT_COMPRESSION_NONE;
323 text[1].key =
const_cast<char*
>(
"Creation Time");
331 static constexpr size_t size = (10 + 1 + 8 + 1) + 44;
332 time_t now = time(
nullptr);
333 struct tm* tm = localtime(&now);
334 std::array<char, size> timeStr;
335 snprintf(timeStr.data(),
sizeof(timeStr),
"%04d-%02d-%02d %02d:%02d:%02d",
336 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
337 tm->tm_hour, tm->tm_min, tm->tm_sec);
338 text[1].text = timeStr.data();
340 png_set_text(png.ptr, png.info, text.data(), narrow<int>(text.size()));
342 png_set_IHDR(png.ptr, png.info,
343 narrow<png_uint_32>(width), narrow<png_uint_32>(height),
345 color ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_GRAY,
346 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
347 PNG_FILTER_TYPE_BASE);
350 png_write_info(png.ptr, png.info);
355 reinterpret_cast<png_bytep*
>(
const_cast<void**
>(rowPointers.data())));
356 png_write_end(png.ptr, png.info);
357 }
catch (MSXException&
e) {
359 "Error while writing PNG file \"", filename,
"\": ",
364static void save(SDL_Surface*
image,
const std::string& filename)
367 Endian::BIG ? SDL_PIXELFORMAT_BGR24 : SDL_PIXELFORMAT_RGB24));
371 VLA(
const void*, row_pointers,
image->h);
373 row_pointers[i] = surf24.getLinePtr(i);
376 IMG_SavePNG_RW(
image->w, row_pointers, filename,
true);
379void save(
size_t width, std::span<const void*> rowPointers,
380 const PixelFormat& format,
const std::string& filename)
383 auto height = narrow<unsigned>(rowPointers.size());
385 narrow<unsigned>(width), height,
format.getBpp(),
387 for (
auto y :
xrange(height)) {
389 rowPointers[y], width *
format.getBytesPerPixel());
394void save(
size_t width, std::span<const void*> rowPointers,
395 const std::string& filename)
397 IMG_SavePNG_RW(width, rowPointers, filename,
true);
401 const std::string& filename)
403 IMG_SavePNG_RW(width, rowPointers, filename,
false);
std::unique_ptr< SDL_PixelFormat, SDLFreeFormat > SDLAllocFormatPtr
Wrapper around a SDL_Surface.
void * getLinePtr(unsigned y)
void read(std::span< uint8_t > buffer)
Read from file.
void write(std::span< const uint8_t > buffer)
Write to file.
static std::string full()
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
void format(SectorAccessibleDisk &disk, MSXBootSectorType bootType)
Format the given disk (= a single partition).
Utility functions to hide the complexity of saving to a PNG file.
void saveGrayscale(size_t width, std::span< const void * > rowPointers, const std::string &filename)
void save(size_t width, std::span< const void * > rowPointers, const std::string &filename)
SDLSurfacePtr load(const std::string &filename, bool want32bpp)
Load the given PNG file in a SDL_Surface.
void swap(openmsx::MemBuffer< T > &l, openmsx::MemBuffer< T > &r) noexcept
size_t size(std::string_view utf8)
PNGReadHandle & operator=(const PNGReadHandle &)=delete
PNGReadHandle(const PNGReadHandle &)=delete
PNGWriteHandle & operator=(const PNGWriteHandle &)=delete
PNGWriteHandle(const PNGWriteHandle &)=delete
#define VLA(TYPE, NAME, LENGTH)
constexpr auto xrange(T e)