29[[noreturn]]
static void handleError(png_structp png_ptr, png_const_charp error_msg)
31 const auto* operation = std::bit_cast<const char*>(
32 png_get_error_ptr(png_ptr));
33 throw MSXException(
"Error while ", operation,
" PNG: ", error_msg);
36static void handleWarning(png_structp png_ptr, png_const_charp warning_msg)
38 const auto* operation = std::bit_cast<const char*>(
39 png_get_error_ptr(png_ptr));
40 std::cerr <<
"Warning while " << operation <<
" PNG: "
41 << warning_msg <<
'\n';
92 png_destroy_read_struct(&
ptr,
info ? &
info :
nullptr,
nullptr);
96 png_structp
ptr =
nullptr;
100static void readData(png_structp ctx, png_bytep area, png_size_t size)
102 auto* file = std::bit_cast<File*>(png_get_io_ptr(ctx));
103 file->read(std::span{area, size});
113 png.
ptr = png_create_read_struct(
114 PNG_LIBPNG_VER_STRING,
115 const_cast<char*
>(
"decoding"), handleError, handleWarning);
121 png.
info = png_create_info_struct(png.
ptr);
123 throw MSXException(
"Failed to allocate image info struct");
127 png_set_read_fn(png.
ptr, &file, readData);
130 png_read_info(png.
ptr, png.
info);
131 png_uint_32 width, height;
132 int bit_depth, color_type, interlace_type;
133 png_get_IHDR(png.
ptr, png.
info, &width, &height, &bit_depth,
134 &color_type, &interlace_type,
nullptr,
nullptr);
137 png_set_strip_16(png.
ptr);
141 png_set_packing(png.
ptr);
147 png_set_expand(png.
ptr);
150 png_set_filler(png.
ptr, 0xff, PNG_FILLER_AFTER);
156 png_set_gray_to_rgb(png.
ptr);
158 png_read_update_info(png.
ptr, png.
info);
160 png_get_IHDR(png.
ptr, png.
info, &width, &height, &bit_depth,
161 &color_type, &interlace_type,
nullptr,
nullptr);
164 constexpr unsigned MAX_SIZE = 2048;
165 if (width > MAX_SIZE) {
167 "Attempted to create a surface with excessive width: ",
168 width,
", max ", MAX_SIZE);
170 if (height > MAX_SIZE) {
172 "Attempted to create a surface with excessive height: ",
173 height,
", max ", MAX_SIZE);
175 int bpp = png_get_channels(png.
ptr, png.
info) * 8;
176 assert(bpp ==
one_of(24, 32));
180 ((bpp == 32) ? pixelOps.
getAmask() : 0));
184 [&](
auto y) {
return std::bit_cast<png_bytep>(surface.
getLinePtr(y)); }));
187 png_read_image(png.
ptr, rowPointers.data());
196 "Error while loading PNG file \"", filename,
"\": ",
214 png_destroy_write_struct(&
ptr,
info ? &
info :
nullptr);
218 png_structp
ptr =
nullptr;
222static void writeData(png_structp ctx, png_bytep area, png_size_t size)
224 auto* file = std::bit_cast<File*>(png_get_io_ptr(ctx));
225 file->write(std::span{area, size});
228static void flushData(png_structp ctx)
230 auto* file = std::bit_cast<File*>(png_get_io_ptr(ctx));
234static void IMG_SavePNG_RW(
size_t width, std::span<const void*> rowPointers,
235 const std::string& filename,
bool color)
237 auto height = rowPointers.size();
238 assert(width <= std::numeric_limits<png_uint_32>::max());
239 assert(height <= std::numeric_limits<png_uint_32>::max());
244 png.ptr = png_create_write_struct(
245 PNG_LIBPNG_VER_STRING,
246 const_cast<char*
>(
"encoding"), handleError, handleWarning);
248 throw MSXException(
"Failed to allocate main struct");
252 png.info = png_create_info_struct(png.ptr);
255 throw MSXException(
"Failed to allocate image info struct");
259 png_set_write_fn(png.ptr, &file, writeData, flushData);
263 std::array<png_text, 2> text;
264 text[0].compression = PNG_TEXT_COMPRESSION_NONE;
265 text[0].key =
const_cast<char*
>(
"Software");
266 text[0].text =
const_cast<char*
>(version.c_str());
267 text[1].compression = PNG_TEXT_COMPRESSION_NONE;
268 text[1].key =
const_cast<char*
>(
"Creation Time");
276 static constexpr size_t size = (10 + 1 + 8 + 1) + 44;
277 time_t now = time(
nullptr);
278 const struct tm* tm = localtime(&now);
279 std::array<char, size> timeStr;
280 snprintf(timeStr.data(),
sizeof(timeStr),
"%04d-%02d-%02d %02d:%02d:%02d",
281 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
282 tm->tm_hour, tm->tm_min, tm->tm_sec);
283 text[1].text = timeStr.data();
285 png_set_text(png.ptr, png.info, text.data(), narrow<int>(text.size()));
287 png_set_IHDR(png.ptr, png.info,
288 narrow<png_uint_32>(width), narrow<png_uint_32>(height),
290 color ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_GRAY,
291 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
292 PNG_FILTER_TYPE_BASE);
295 png_write_info(png.ptr, png.info);
300 std::bit_cast<png_bytep*>(
const_cast<void**
>(rowPointers.data())));
301 png_write_end(png.ptr, png.info);
302 }
catch (MSXException& e) {
304 "Error while writing PNG file \"", filename,
"\": ",
309static void save(SDL_Surface*
image,
const std::string& filename)
312 Endian::BIG ? SDL_PIXELFORMAT_BGR24 : SDL_PIXELFORMAT_RGB24));
317 [&](
auto y) { return surf24.getLinePtr(y); }));
319 IMG_SavePNG_RW(
image->w, rowPointers, filename,
true);
322void saveRGBA(
size_t width, std::span<const uint32_t*> rowPointers,
323 const std::string& filename)
326 auto height = narrow<unsigned>(rowPointers.size());
327 static constexpr int bpp = 32;
330 narrow<unsigned>(width), height, bpp,
333 for (
auto y :
xrange(height)) {
335 rowPointers[y], width *
sizeof(uint32_t));
337 save(surface.
get(), filename);
341 const std::string& filename)
343 std::span rowPointers{std::bit_cast<const void**>(rowPointers_.data()),
344 rowPointers_.size()};
345 IMG_SavePNG_RW(width, rowPointers, filename,
false);