diff options
-rw-r--r-- | include/cru/osx/graphics/quartz/Convert.h | 1 | ||||
-rw-r--r-- | include/cru/osx/graphics/quartz/ImageFactory.h | 2 | ||||
-rw-r--r-- | include/cru/platform/graphics/ImageFactory.h | 4 | ||||
-rw-r--r-- | src/osx/graphics/quartz/Convert.cpp | 11 | ||||
-rw-r--r-- | src/osx/graphics/quartz/ImageFactory.cpp | 51 |
5 files changed, 67 insertions, 2 deletions
diff --git a/include/cru/osx/graphics/quartz/Convert.h b/include/cru/osx/graphics/quartz/Convert.h index 8d486069..be56b959 100644 --- a/include/cru/osx/graphics/quartz/Convert.h +++ b/include/cru/osx/graphics/quartz/Convert.h @@ -20,4 +20,5 @@ CGRect Convert(const Rect& rect); Rect Convert(const CGRect& rect); CGDataProviderRef ConvertStreamToCGDataProvider(io::Stream* stream); +CGDataConsumerRef ConvertStreamToCGDataConsumer(io::Stream* stream); } // namespace cru::platform::graphics::osx::quartz diff --git a/include/cru/osx/graphics/quartz/ImageFactory.h b/include/cru/osx/graphics/quartz/ImageFactory.h index 19e9c8c1..23d14332 100644 --- a/include/cru/osx/graphics/quartz/ImageFactory.h +++ b/include/cru/osx/graphics/quartz/ImageFactory.h @@ -15,6 +15,8 @@ class QuartzImageFactory : public OsxQuartzResource, public: std::unique_ptr<IImage> DecodeFromStream(io::Stream* stream) override; + void EncodeToStream(IImage* image, io::Stream* stream, ImageFormat format, + float quality) override; std::unique_ptr<IImage> CreateBitmap(int width, int height) override; }; } // namespace cru::platform::graphics::osx::quartz diff --git a/include/cru/platform/graphics/ImageFactory.h b/include/cru/platform/graphics/ImageFactory.h index 2339a069..cd868087 100644 --- a/include/cru/platform/graphics/ImageFactory.h +++ b/include/cru/platform/graphics/ImageFactory.h @@ -3,7 +3,7 @@ #include "cru/common/io/Stream.h" namespace cru::platform::graphics { -enum class ImageFormat { Jpeg, Png }; +enum class ImageFormat { Jpeg, Png, Gif }; struct CRU_PLATFORM_GRAPHICS_API IImageFactory : public virtual IGraphicsResource { @@ -15,7 +15,7 @@ struct CRU_PLATFORM_GRAPHICS_API IImageFactory * \param stream The stream to write to. * \param format The format to encode to. * \param quality The quality to encode to. - * \todo Implement on macOS and Windows. + * \todo Implement on Windows. */ virtual void EncodeToStream(IImage* image, io::Stream* stream, ImageFormat format, float quality) = 0; diff --git a/src/osx/graphics/quartz/Convert.cpp b/src/osx/graphics/quartz/Convert.cpp index 993b5b61..df62a206 100644 --- a/src/osx/graphics/quartz/Convert.cpp +++ b/src/osx/graphics/quartz/Convert.cpp @@ -49,4 +49,15 @@ CGDataProviderRef ConvertStreamToCGDataProvider(io::Stream* stream) { &kStreamToCGDataProviderCallbacks); } +const CGDataConsumerCallbacks kStreamToCGDataConsumerCallbacks{ + [](void* info, const void* buffer, size_t count) -> size_t { + return static_cast<io::Stream*>(info)->Write( + static_cast<const std::byte*>(buffer), count); + }, + [](void* info) {}}; + +CGDataConsumerRef ConvertStreamToCGDataConsumer(io::Stream* stream) { + return CGDataConsumerCreate(stream, &kStreamToCGDataConsumerCallbacks); +} + } // namespace cru::platform::graphics::osx::quartz diff --git a/src/osx/graphics/quartz/ImageFactory.cpp b/src/osx/graphics/quartz/ImageFactory.cpp index a8c17719..2c6d4705 100644 --- a/src/osx/graphics/quartz/ImageFactory.cpp +++ b/src/osx/graphics/quartz/ImageFactory.cpp @@ -1,12 +1,16 @@ #include "cru/osx/graphics/quartz/ImageFactory.h" #include "cru/common/Exception.h" +#include "cru/common/platform/osx/Convert.h" #include "cru/osx/graphics/quartz/Convert.h" #include "cru/osx/graphics/quartz/Image.h" +#include "cru/platform/Check.h" #include "cru/platform/graphics/Image.h" #include <ImageIO/ImageIO.h> namespace cru::platform::graphics::osx::quartz { +using cru::platform::osx::Convert; + QuartzImageFactory::QuartzImageFactory(IGraphicsFactory* graphics_factory) : OsxQuartzResource(graphics_factory) {} @@ -28,6 +32,53 @@ std::unique_ptr<IImage> QuartzImageFactory::DecodeFromStream( new QuartzImage(GetGraphicsFactory(), this, cg_image, true)); } +static String GetImageFormatUniformTypeIdentifier(ImageFormat format) { + switch (format) { + case ImageFormat::Png: + return u"public.png"; + case ImageFormat::Jpeg: + return u"public.jpeg"; + case ImageFormat::Gif: + return u"com.compuserve.gif"; + default: + throw Exception(u"Unknown image format."); + } +} + +void QuartzImageFactory::EncodeToStream(IImage* image, io::Stream* stream, + ImageFormat format, float quality) { + if (quality <= 0 || quality > 1) { + throw Exception(u"Invalid quality value."); + } + + auto quartz_image = CheckPlatform<QuartzImage>(image, GetPlatformId()); + auto cg_image = quartz_image->GetCGImage(); + + CFStringRef uti = Convert(GetImageFormatUniformTypeIdentifier(format)); + CGDataConsumerRef data_consumer = ConvertStreamToCGDataConsumer(stream); + CGImageDestinationRef destination = + CGImageDestinationCreateWithDataConsumer(data_consumer, uti, 1, nullptr); + + CFMutableDictionaryRef properties = + CFDictionaryCreateMutable(nullptr, 0, nullptr, nullptr); + CFNumberRef quality_wrap = + CFNumberCreate(nullptr, kCFNumberFloatType, &quality); + CFDictionaryAddValue(properties, kCGImageDestinationLossyCompressionQuality, + quality_wrap); + + CGImageDestinationAddImage(destination, cg_image, properties); + + if (!CGImageDestinationFinalize(destination)) { + throw Exception(u"Failed to finalize image destination."); + } + + CFRelease(quality_wrap); + CFRelease(properties); + CFRelease(destination); + CFRelease(data_consumer); + CFRelease(uti); +} + std::unique_ptr<IImage> QuartzImageFactory::CreateBitmap(int width, int height) { if (width <= 0) throw Exception(u"Image width should be greater than 0."); |