From 7bdeb9664966c0f81c2b76abc83225bd17c055ce Mon Sep 17 00:00:00 2001 From: kevle Date: Thu, 10 Jun 2021 20:40:47 +0200 Subject: [PATCH 1/3] Add SaveImageResource implementation for GDI --- olcPixelGameEngine.h | 48 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/olcPixelGameEngine.h b/olcPixelGameEngine.h index 48a9be5..aa11acd 100644 --- a/olcPixelGameEngine.h +++ b/olcPixelGameEngine.h @@ -710,7 +710,7 @@ namespace olc ImageLoader() = default; virtual ~ImageLoader() = default; virtual olc::rcode LoadImageResource(olc::Sprite* spr, const std::string& sImageFile, olc::ResourcePack* pack) = 0; - virtual olc::rcode SaveImageResource(olc::Sprite* spr, const std::string& sImageFile) = 0; + virtual olc::rcode SaveImageResource(const olc::Sprite* spr, const std::string& sImageFile) = 0; }; @@ -728,6 +728,7 @@ namespace olc public: olc::rcode LoadFromFile(const std::string& sImageFile, olc::ResourcePack* pack = nullptr); + olc::rcode SaveToFile(const std::string& sImageFile) const; public: int32_t width = 0; @@ -1364,6 +1365,10 @@ namespace olc UNUSED(pack); return loader->LoadImageResource(this, sImageFile, pack); } + olc::rcode Sprite::SaveToFile(const std::string& sImageFile) const + { + return loader->SaveImageResource(this, sImageFile); + } olc::Sprite* Sprite::Duplicate() { @@ -4091,9 +4096,42 @@ namespace olc return olc::rcode::OK; } - olc::rcode SaveImageResource(olc::Sprite* spr, const std::string& sImageFile) override + olc::rcode SaveImageResource(const olc::Sprite* spr, const std::string& sImageFile) override { - return olc::rcode::OK; + if (spr == nullptr) + return olc::rcode::FAIL; + + UINT num = 0; + UINT size = 0; + if (Gdiplus::GetImageEncodersSize(&num, &size) != Gdiplus::Status::Ok) + return olc::rcode::FAIL; + + // Round up or use one more element + int requiredSpace = size / num + 1; + std::vector infos(requiredSpace); + if (Gdiplus::GetImageEncoders(num, size, &infos[0]) != Gdiplus::Status::Ok) + return olc::rcode::FAIL; + infos.resize(num); + + // Search for PNG codec + auto pngIt = std::find_if(infos.begin(), infos.end(), [](const Gdiplus::ImageCodecInfo& ci) { + return wcscmp(ci.MimeType, L"image/png") == 0; + }); + + if (pngIt == infos.end()) return olc::rcode::FAIL; + + Gdiplus::Bitmap bmp(spr->width, spr->height, PixelFormat32bppARGB); + + for (int y = 0; y < spr->height; y++) + for (int x = 0; x < spr->width; x++) + { + Pixel p = spr->GetPixel(x, y); + Gdiplus::Color c(p.a, p.r, p.g, p.b); + if (bmp.SetPixel(x, y, c) != Gdiplus::Status::Ok) + return olc::rcode::FAIL; + } + + return bmp.Save(ConvertS2W(sImageFile).c_str(), &pngIt->Clsid) == Gdiplus::Status::Ok ? olc::rcode::OK : olc::rcode::FAIL; } }; } @@ -4215,7 +4253,7 @@ namespace olc return olc::rcode::FAIL; } - olc::rcode SaveImageResource(olc::Sprite* spr, const std::string& sImageFile) override + olc::rcode SaveImageResource(const olc::Sprite* spr, const std::string& sImageFile) override { return olc::rcode::OK; } @@ -4278,7 +4316,7 @@ namespace olc return olc::rcode::OK; } - olc::rcode SaveImageResource(olc::Sprite* spr, const std::string& sImageFile) override + olc::rcode SaveImageResource(const olc::Sprite* spr, const std::string& sImageFile) override { return olc::rcode::OK; } From 84cedb4fedcc652a0f71c3212037d2592cdd1827 Mon Sep 17 00:00:00 2001 From: kevle Date: Thu, 10 Jun 2021 20:41:41 +0200 Subject: [PATCH 2/3] Add SaveImageResource implementation for libpng --- olcPixelGameEngine.h | 96 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/olcPixelGameEngine.h b/olcPixelGameEngine.h index aa11acd..ae23f0b 100644 --- a/olcPixelGameEngine.h +++ b/olcPixelGameEngine.h @@ -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_rows = std::make_unique(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; } }; From d11438bdbdb9b700c00e11f2a0f4e065538c5ab9 Mon Sep 17 00:00:00 2001 From: kevle Date: Thu, 10 Jun 2021 20:43:13 +0200 Subject: [PATCH 3/3] Add SaveImageResource implementation for stb_image --- olcPixelGameEngine.h | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/olcPixelGameEngine.h b/olcPixelGameEngine.h index ae23f0b..d1bf212 100644 --- a/olcPixelGameEngine.h +++ b/olcPixelGameEngine.h @@ -137,7 +137,15 @@ Before including the olcPixelGameEngine.h header file. stb_image.h works on many systems and can be downloaded here: https://github.com/nothings/stb/blob/master/stb_image.h + If you want to write png images, you will also need "stb_image_write.h", which can be + downloaded here: https://github.com/nothings/stb/blob/master/stb_image_write.h + To use stb_image_write.h specify: + + #define OLC_IMAGE_STB_WRITE + + Before including the olcPixelGameEngine.h header file. Note that this only is meaningful + if you already use stb_image.h. Multiple cpp file projects? @@ -441,7 +449,7 @@ int main() #if defined(_WIN32) #define OLC_IMAGE_GDI #endif - #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__) + #if (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__)) && !defined(OLC_IMAGE_STB) #define OLC_IMAGE_LIBPNG #endif #endif @@ -4375,7 +4383,19 @@ namespace olc #if defined(OLC_IMAGE_STB) #define STB_IMAGE_IMPLEMENTATION +#if defined(_WIN32) && defined(UNICODE) +#define STBI_WINDOWS_UTF8 +#endif #include "stb_image.h" + +#if defined(OLC_IMAGE_STB_WRITE) +#define STB_IMAGE_WRITE_IMPLEMENTATION +#if defined(_WIN32) && defined(UNICODE) +#define STBIW_WINDOWS_UTF8 +#endif +#include "stb_image_write.h" +#endif + namespace olc { class ImageLoader_STB : public olc::ImageLoader @@ -4414,7 +4434,14 @@ namespace olc olc::rcode SaveImageResource(const olc::Sprite* spr, const std::string& sImageFile) override { - return olc::rcode::OK; +#if !defined(OLC_IMAGE_STB_WRITE) + return olc::rcode::FAIL; +#else + if (spr == nullptr) + return olc::rcode::FAIL; + int res = stbi_write_png(sImageFile.c_str(), spr->width, spr->height, 4, spr->pColData.data(), spr->width * sizeof(olc::Pixel)); + return res == 0 ? olc::rcode::OK : olc::rcode::FAIL; +#endif } }; }