|
|
|
@ -4255,6 +4255,102 @@ namespace olc |
|
|
|
|
|
|
|
|
|
olc::rcode SaveImageResource(const olc::Sprite* spr, const std::string& sImageFile) override |
|
|
|
|
{ |
|
|
|
|
if (spr == nullptr) |
|
|
|
|
return olc::rcode::FAIL; |
|
|
|
|
|
|
|
|
|
// RAII wrapper for FILE pointer
|
|
|
|
|
struct FileWrapper |
|
|
|
|
{ |
|
|
|
|
FILE* f = nullptr; |
|
|
|
|
|
|
|
|
|
FileWrapper(const std::string& sImageFile) |
|
|
|
|
{ |
|
|
|
|
f = fopen(sImageFile.c_str(), "wb"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
FileWrapper(const FileWrapper&) = delete; |
|
|
|
|
FileWrapper(FileWrapper&&) = delete; |
|
|
|
|
FileWrapper& operator=(const FileWrapper&) = delete; |
|
|
|
|
FileWrapper& operator=(FileWrapper&&) = delete; |
|
|
|
|
|
|
|
|
|
~FileWrapper() |
|
|
|
|
{ |
|
|
|
|
if (f != nullptr) |
|
|
|
|
fclose(f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool Good() const |
|
|
|
|
{ |
|
|
|
|
return f != nullptr; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// RAII wrapper for png_struct and png_info pointers
|
|
|
|
|
struct PNGStructWrapper |
|
|
|
|
{ |
|
|
|
|
struct PNGError |
|
|
|
|
{}; |
|
|
|
|
|
|
|
|
|
png_structp png_ptr = nullptr; |
|
|
|
|
png_infop info_ptr = nullptr; |
|
|
|
|
|
|
|
|
|
PNGStructWrapper() |
|
|
|
|
{ |
|
|
|
|
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, PNGStructWrapper::user_error_handling_func, PNGStructWrapper::user_warn_func); |
|
|
|
|
if (png_ptr != nullptr) |
|
|
|
|
info_ptr = png_create_info_struct(png_ptr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
PNGStructWrapper(const PNGStructWrapper&) = delete; |
|
|
|
|
PNGStructWrapper(PNGStructWrapper&&) = delete; |
|
|
|
|
PNGStructWrapper& operator=(const PNGStructWrapper&) = delete; |
|
|
|
|
PNGStructWrapper& operator=(PNGStructWrapper&&) = delete; |
|
|
|
|
|
|
|
|
|
~PNGStructWrapper() |
|
|
|
|
{ |
|
|
|
|
png_destroy_write_struct(&png_ptr, &info_ptr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool Good() const |
|
|
|
|
{ |
|
|
|
|
return png_ptr != nullptr && info_ptr != nullptr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void user_error_handling_func(png_structp, png_const_charp error_msg) { |
|
|
|
|
fprintf(stderr, "%s\n", error_msg); |
|
|
|
|
throw PNGError{}; // Function must not return control to caller
|
|
|
|
|
} |
|
|
|
|
static void user_warn_func(png_structp, png_const_charp warning_msg) { |
|
|
|
|
fprintf(stderr, "%s\n", warning_msg); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
FileWrapper file(sImageFile); |
|
|
|
|
if (!file.Good()) |
|
|
|
|
{ |
|
|
|
|
return olc::rcode::FAIL; |
|
|
|
|
} |
|
|
|
|
PNGStructWrapper png; |
|
|
|
|
if (!png.Good()) |
|
|
|
|
{ |
|
|
|
|
return olc::rcode::FAIL; |
|
|
|
|
} |
|
|
|
|
png_init_io(png.png_ptr, file.f); |
|
|
|
|
png_set_IHDR(png.png_ptr, png.info_ptr, spr->width, spr->height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); |
|
|
|
|
std::unique_ptr<png_bytep[]> png_rows = std::make_unique<png_bytep[]>(spr->height); |
|
|
|
|
for (int y = 0; y < spr->height; y++) |
|
|
|
|
png_rows[y] = (png_bytep)(spr->pColData.data() + std::ptrdiff_t(y) * spr->width); |
|
|
|
|
|
|
|
|
|
png_set_rows(png.png_ptr, png.info_ptr, png_rows.get()); |
|
|
|
|
png_write_png(png.png_ptr, png.info_ptr, PNG_TRANSFORM_IDENTITY, NULL); |
|
|
|
|
} |
|
|
|
|
catch (PNGStructWrapper::PNGError&) |
|
|
|
|
{ |
|
|
|
|
return olc::rcode::FAIL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return olc::rcode::OK; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|