diff --git a/modules/juce_graphics/images/juce_Image.cpp b/modules/juce_graphics/images/juce_Image.cpp index 8b1f45cedf..905a696d63 100644 --- a/modules/juce_graphics/images/juce_Image.cpp +++ b/modules/juce_graphics/images/juce_Image.cpp @@ -280,6 +280,11 @@ public: Span getPages (ComSmartPtr x) const { return impl->getPages (x); } #endif + #if JUCE_MAC || JUCE_IOS + CGContextRef getCGContext() const { return impl->getCGContext(); } + CGImageRef getCGImage (CGColorSpaceRef x) const { return impl->getCGImage (x); } + #endif + private: struct Base { @@ -289,9 +294,14 @@ private: #if JUCE_WINDOWS virtual Span getPages (ComSmartPtr) const = 0; #endif - }; - template + #if JUCE_MAC || JUCE_IOS + virtual CGContextRef getCGContext() const = 0; + virtual CGImageRef getCGImage (CGColorSpaceRef x) const = 0; + #endif + }; + + template class Concrete : public Base { public: @@ -304,6 +314,11 @@ private: Span getPages (ComSmartPtr x) const override { return impl.getPages (x); } #endif + #if JUCE_MAC || JUCE_IOS + CGContextRef getCGContext() const override { return impl.getCGContext(); } + CGImageRef getCGImage (CGColorSpaceRef x) const override { return impl.getCGImage (x); } + #endif + private: Impl impl; }; @@ -396,6 +411,18 @@ public: } #endif + #if JUCE_MAC || JUCE_IOS + CGContextRef getCGContext() const + { + return self->sourceImage->getNativeExtensions().getCGContext(); + } + + CGImageRef getCGImage (CGColorSpaceRef colourSpace) const + { + return self->sourceImage->getNativeExtensions().getCGImage (colourSpace); + } + #endif + Point getTopLeft() const { return self->sourceImage->getNativeExtensions().getTopLeft() + self->area.getTopLeft(); @@ -488,6 +515,11 @@ auto ImagePixelData::getNativeExtensions() -> NativeExtensions #if JUCE_WINDOWS Span getPages (ComSmartPtr) const { return {}; } #endif + + #if JUCE_MAC || JUCE_IOS + CGContextRef getCGContext() const { return {}; } + CGImageRef getCGImage (CGColorSpaceRef) const { return {}; } + #endif }; return NativeExtensions { Wrapped{} }; diff --git a/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm b/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm index 0010ddbf5f..51fad5e43c 100644 --- a/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm +++ b/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm @@ -42,6 +42,8 @@ namespace juce class CoreGraphicsPixelData final : public ImagePixelData { public: + using Ptr = ReferenceCountedObjectPtr; + CoreGraphicsPixelData (const Image::PixelFormat format, int w, int h, bool clearImage) : ImagePixelData (format, w, h) { @@ -144,37 +146,22 @@ public: //============================================================================== static CGImageRef getCachedImageRef (const Image& juceImage, CGColorSpaceRef colourSpace) { - auto cgim = dynamic_cast (juceImage.getPixelData().get()); + auto* cgim = std::invoke ([&]() -> CGImageRef + { + if (auto ptr = juceImage.getPixelData()) + return ptr->getNativeExtensions().getCGImage (colourSpace); - if (cgim != nullptr && cgim->cachedImageRef != nullptr) - return CGImageRetain (cgim->cachedImageRef.get()); - - CGImageRef ref = createImage (juceImage, colourSpace); + return {}; + }); if (cgim != nullptr) - cgim->cachedImageRef.reset (CGImageRetain (ref)); + return CGImageRetain (cgim); - return ref; - } - - static CGImageRef createImage (const Image& juceImage, CGColorSpaceRef colourSpace) - { const Image::BitmapData srcData (juceImage, Image::BitmapData::readOnly); - const auto provider = [&] - { - if (auto* cgim = dynamic_cast (juceImage.getPixelData().get())) - { - return detail::DataProviderPtr { CGDataProviderCreateWithData (new ImageDataContainer::Ptr (cgim->imageData), - srcData.data, - srcData.size, - [] (void * __nullable info, const void*, size_t) { delete (ImageDataContainer::Ptr*) info; }) }; - } - - const auto usableSize = jmin ((size_t) srcData.lineStride * (size_t) srcData.height, srcData.size); - CFUniquePtr data (CFDataCreate (nullptr, (const UInt8*) srcData.data, (CFIndex) usableSize)); - return detail::DataProviderPtr { CGDataProviderCreateWithCFData (data.get()) }; - }(); + const auto usableSize = jmin ((size_t) srcData.lineStride * (size_t) srcData.height, srcData.size); + CFUniquePtr data (CFDataCreate (nullptr, (const UInt8*) srcData.data, (CFIndex) usableSize)); + detail::DataProviderPtr provider { CGDataProviderCreateWithCFData (data.get()) }; return CGImageCreate ((size_t) srcData.width, (size_t) srcData.height, @@ -198,9 +185,59 @@ public: HeapBlock data; }; + CGImageRef getCGImage (CGColorSpaceRef colourSpace) + { + if (cachedImageRef != nullptr) + return cachedImageRef.get(); + + const Image::BitmapData srcData { Image { this }, Image::BitmapData::readOnly }; + + detail::DataProviderPtr provider { CGDataProviderCreateWithData (new ImageDataContainer::Ptr (imageData), + srcData.data, + srcData.size, + [] (void * __nullable info, const void*, size_t) { delete (ImageDataContainer::Ptr*) info; }) }; + + cachedImageRef.reset (CGImageCreate ((size_t) srcData.width, + (size_t) srcData.height, + 8, + (size_t) srcData.pixelStride * 8, + (size_t) srcData.lineStride, + colourSpace, getCGImageFlags (pixelFormat), provider.get(), + nullptr, true, kCGRenderingIntentDefault)); + + return cachedImageRef.get(); + } + ImageDataContainer::Ptr imageData = new ImageDataContainer(); int pixelStride, lineStride; + NativeExtensions getNativeExtensions() override + { + struct Wrapped + { + explicit Wrapped (Ptr selfIn) : self (selfIn) {} + + CGContextRef getCGContext() const + { + return self->context.get(); + } + + CGImageRef getCGImage (CGColorSpaceRef x) const + { + return self->getCGImage (x); + } + + Point getTopLeft() const + { + return {}; + } + + Ptr self; + }; + + return NativeExtensions { Wrapped { this } }; + } + private: template bool applyFilterInArea (Rectangle area, BuildFilter&& buildFilter) @@ -460,7 +497,7 @@ void CoreGraphicsContext::clipToImageAlpha (const Image& sourceImage, const Affi if (sourceImage.getFormat() != Image::SingleChannel) singleChannelImage = sourceImage.convertedToFormat (Image::SingleChannel); - auto image = detail::ImagePtr { CoreGraphicsPixelData::createImage (singleChannelImage, greyColourSpace.get()) }; + detail::ImagePtr image { CoreGraphicsPixelData::getCachedImageRef (singleChannelImage, greyColourSpace.get()) }; flip(); auto t = AffineTransform::verticalFlip ((float) sourceImage.getHeight()).followedBy (transform); @@ -1117,11 +1154,25 @@ Image juce_loadWithCoreImage (InputStream& input) (int) CGImageGetHeight (loadedImage), hasAlphaChan)); - auto cgImage = dynamic_cast (image.getPixelData().get()); - jassert (cgImage != nullptr); // if USE_COREGRAPHICS_RENDERING is set, the CoreGraphicsPixelData class should have been used. + auto* context = std::invoke ([&]() -> CGContextRef + { + auto ptr = image.getPixelData(); - CGContextDrawImage (cgImage->context.get(), convertToCGRect (image.getBounds()), loadedImage); - CGContextFlush (cgImage->context.get()); + if (ptr == nullptr) + return {}; + + return ptr->getNativeExtensions().getCGContext(); + }); + + if (context == nullptr) + { + // if USE_COREGRAPHICS_RENDERING is set, the CoreGraphicsPixelData class should have been used. + jassertfalse; + return {}; + } + + CGContextDrawImage (context, convertToCGRect (image.getBounds()), loadedImage); + CGContextFlush (context); // Because it's impossible to create a truly 24-bit CG image, this flag allows a user // to find out whether the file they just loaded the image from had an alpha channel or not. @@ -1149,13 +1200,13 @@ Image juce_createImageFromCIImage (CIImage* im, int w, int h) CGImageRef juce_createCoreGraphicsImage (const Image& juceImage, CGColorSpaceRef colourSpace) { - return CoreGraphicsPixelData::createImage (juceImage, colourSpace); + return CoreGraphicsPixelData::getCachedImageRef (juceImage, colourSpace); } CGContextRef juce_getImageContext (const Image& image) { - if (auto cgi = dynamic_cast (image.getPixelData().get())) - return cgi->context.get(); + if (auto ptr = image.getPixelData()) + return ptr->getNativeExtensions().getCGContext(); jassertfalse; return {};