24static void handleError(png_structp png_ptr, png_const_charp error_msg)
26 const auto* operation =
reinterpret_cast<const char*
>(
27 png_get_error_ptr(png_ptr));
28 throw MSXException(
"Error while ", operation,
" PNG: ", error_msg);
31static void handleWarning(png_structp png_ptr, png_const_charp warning_msg)
33 const auto* operation =
reinterpret_cast<const char*
>(
34 png_get_error_ptr(png_ptr));
35 std::cerr <<
"Warning while " << operation <<
" PNG: "
36 << warning_msg <<
'\n';
83 png_destroy_read_struct(&
ptr,
info ? &
info :
nullptr,
nullptr);
89 png_structp
ptr =
nullptr;
93static void readData(png_structp ctx, png_bytep area, png_size_t
size)
95 auto* file =
reinterpret_cast<File*
>(png_get_io_ptr(ctx));
106 png.
ptr = png_create_read_struct(
107 PNG_LIBPNG_VER_STRING,
108 const_cast<char*
>(
"decoding"), handleError, handleWarning);
114 png.
info = png_create_info_struct(png.
ptr);
116 throw MSXException(
"Failed to allocate image info struct");
120 png_set_read_fn(png.
ptr, &file, readData);
123 png_read_info(png.
ptr, png.
info);
124 png_uint_32 width, height;
125 int bit_depth, color_type, interlace_type;
126 png_get_IHDR(png.
ptr, png.
info, &width, &height, &bit_depth,
127 &color_type, &interlace_type,
nullptr,
nullptr);
130 png_set_strip_16(png.
ptr);
134 png_set_packing(png.
ptr);
140 png_set_expand(png.
ptr);
143 png_set_filler(png.
ptr, 0xff, PNG_FILLER_AFTER);
149 png_set_gray_to_rgb(png.
ptr);
151 png_read_update_info(png.
ptr, png.
info);
153 png_get_IHDR(png.
ptr, png.
info, &width, &height, &bit_depth,
154 &color_type, &interlace_type,
nullptr,
nullptr);
157 constexpr unsigned MAX_SIZE = 2048;
158 if (width > MAX_SIZE) {
160 "Attempted to create a surface with excessive width: ",
161 width,
", max ", MAX_SIZE);
163 if (height > MAX_SIZE) {
165 "Attempted to create a surface with excessive height: ",
166 height,
", max ", MAX_SIZE);
168 int bpp = png_get_channels(png.
ptr, png.
info) * 8;
169 assert(bpp ==
one_of(24, 32));
173 ((bpp == 32) ? pixelOps.
getAmask() : 0));
176 VLA(png_bytep, rowPointers, height);
177 for (
auto row :
xrange(height)) {
178 rowPointers[row] =
reinterpret_cast<png_bytep
>(
183 png_read_image(png.
ptr, rowPointers.data());
192 "Error while loading PNG file \"", filename,
"\": ",
206 png_destroy_write_struct(&
ptr,
info ? &
info :
nullptr);
212 png_structp
ptr =
nullptr;
216static void writeData(png_structp ctx, png_bytep area, png_size_t
size)
218 auto* file =
reinterpret_cast<File*
>(png_get_io_ptr(ctx));
222static void flushData(png_structp ctx)
224 auto* file =
reinterpret_cast<File*
>(png_get_io_ptr(ctx));
228static void IMG_SavePNG_RW(
size_t width, std::span<const void*> rowPointers,
229 const std::string& filename,
bool color)
231 auto height = rowPointers.size();
238 png.ptr = png_create_write_struct(
239 PNG_LIBPNG_VER_STRING,
240 const_cast<char*
>(
"encoding"), handleError, handleWarning);
242 throw MSXException(
"Failed to allocate main struct");
246 png.info = png_create_info_struct(png.ptr);
249 throw MSXException(
"Failed to allocate image info struct");
253 png_set_write_fn(png.ptr, &file, writeData, flushData);
257 std::array<png_text, 2> text;
258 text[0].compression = PNG_TEXT_COMPRESSION_NONE;
259 text[0].key =
const_cast<char*
>(
"Software");
260 text[0].text =
const_cast<char*
>(version.c_str());
261 text[1].compression = PNG_TEXT_COMPRESSION_NONE;
262 text[1].key =
const_cast<char*
>(
"Creation Time");
270 static constexpr size_t size = (10 + 1 + 8 + 1) + 44;
271 time_t now = time(
nullptr);
272 struct tm* tm = localtime(&now);
273 std::array<char, size> timeStr;
274 snprintf(timeStr.data(),
sizeof(timeStr),
"%04d-%02d-%02d %02d:%02d:%02d",
275 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
276 tm->tm_hour, tm->tm_min, tm->tm_sec);
277 text[1].text = timeStr.data();
279 png_set_text(png.ptr, png.info, text.data(), narrow<int>(text.size()));
281 png_set_IHDR(png.ptr, png.info,
282 narrow<png_uint_32>(width), narrow<png_uint_32>(height),
284 color ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_GRAY,
285 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
286 PNG_FILTER_TYPE_BASE);
289 png_write_info(png.ptr, png.info);
294 reinterpret_cast<png_bytep*
>(
const_cast<void**
>(rowPointers.data())));
295 png_write_end(png.ptr, png.info);
296 }
catch (MSXException&
e) {
298 "Error while writing PNG file \"", filename,
"\": ",
303static void save(SDL_Surface*
image,
const std::string& filename)
306 Endian::BIG ? SDL_PIXELFORMAT_BGR24 : SDL_PIXELFORMAT_RGB24));
310 VLA(
const void*, row_pointers,
image->h);
312 row_pointers[i] = surf24.getLinePtr(i);
315 IMG_SavePNG_RW(
image->w, row_pointers, filename,
true);
318void saveRGBA(
size_t width, std::span<const void*> rowPointers,
319 const std::string& filename)
322 auto height = narrow<unsigned>(rowPointers.size());
323 static constexpr int bpp = 32;
326 narrow<unsigned>(width), height, bpp,
329 for (
auto y :
xrange(height)) {
331 rowPointers[y], width *
sizeof(uint32_t));
333 save(surface.
get(), filename);
337 const std::string& filename)
339 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)
Utility functions to hide the complexity of saving to a PNG file.
void saveRGBA(size_t width, std::span< const void * > rowPointers, const std::string &filename)
void saveGrayscale(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.
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)