diff --git a/modules/juce_graphics/contexts/juce_GraphicsContext.cpp b/modules/juce_graphics/contexts/juce_GraphicsContext.cpp index 2ce834b0c8..cdc432977f 100644 --- a/modules/juce_graphics/contexts/juce_GraphicsContext.cpp +++ b/modules/juce_graphics/contexts/juce_GraphicsContext.cpp @@ -871,4 +871,68 @@ Graphics::ScopedSaveState::~ScopedSaveState() context.restoreState(); } +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class GraphicsTests : public UnitTest +{ +public: + GraphicsTests() : UnitTest ("Graphics", UnitTestCategories::graphics) {} + + void runTest() override + { + beginTest ("Render image subsection"); + { + renderImageSubsection (NativeImageType{}, NativeImageType{}); + } + } + +private: + void renderImageSubsection (const ImageType& sourceType, const ImageType& targetType) + { + const auto sourceColour = Colours::cyan; + const auto sourceOffset = 49; + + const Image source { Image::ARGB, 50, 50, true, sourceType }; + const Image target { Image::ARGB, 50, 50, true, targetType }; + + const auto subsection = source.getClippedImage (Rectangle { sourceOffset, sourceOffset, 1, 1 }); + + Image::BitmapData { subsection, Image::BitmapData::writeOnly }.setPixelColour (0, 0, sourceColour); + + { + // Render the subsection image so that it fills 'target' + Graphics g { target }; + g.drawImage (subsection, + 0, 0, target.getWidth(), target.getHeight(), + 0, 0, 1, 1); + } + + { + // Check that all pixels in 'target' match the bottom right pixel of 'source' + const Image::BitmapData bitmap { target, Image::BitmapData::readOnly }; + + int numFailures = 0; + + for (auto y = 0; y < bitmap.height; ++y) + { + for (auto x = 0; x < bitmap.width; ++x) + { + const auto targetColour = bitmap.getPixelColour (x, y); + + if (targetColour != sourceColour) + ++numFailures; + } + } + + expect (numFailures == 0); + } + } +}; + +static GraphicsTests graphicsTests; + +#endif + } // namespace juce diff --git a/modules/juce_graphics/images/juce_Image.cpp b/modules/juce_graphics/images/juce_Image.cpp index 905a696d63..17f445b094 100644 --- a/modules/juce_graphics/images/juce_Image.cpp +++ b/modules/juce_graphics/images/juce_Image.cpp @@ -282,7 +282,7 @@ public: #if JUCE_MAC || JUCE_IOS CGContextRef getCGContext() const { return impl->getCGContext(); } - CGImageRef getCGImage (CGColorSpaceRef x) const { return impl->getCGImage (x); } + CFUniquePtr getCGImage (CGColorSpaceRef x) const { return impl->getCGImage (x); } #endif private: @@ -297,7 +297,7 @@ private: #if JUCE_MAC || JUCE_IOS virtual CGContextRef getCGContext() const = 0; - virtual CGImageRef getCGImage (CGColorSpaceRef x) const = 0; + virtual CFUniquePtr getCGImage (CGColorSpaceRef x) const = 0; #endif }; @@ -316,7 +316,7 @@ private: #if JUCE_MAC || JUCE_IOS CGContextRef getCGContext() const override { return impl.getCGContext(); } - CGImageRef getCGImage (CGColorSpaceRef x) const override { return impl.getCGImage (x); } + CFUniquePtr getCGImage (CGColorSpaceRef x) const override { return impl.getCGImage (x); } #endif private: @@ -417,9 +417,12 @@ public: return self->sourceImage->getNativeExtensions().getCGContext(); } - CGImageRef getCGImage (CGColorSpaceRef colourSpace) const + CFUniquePtr getCGImage (CGColorSpaceRef colourSpace) const { - return self->sourceImage->getNativeExtensions().getCGImage (colourSpace); + const auto& parentNative = self->sourceImage->getNativeExtensions(); + const auto parentImage = parentNative.getCGImage (colourSpace); + return CFUniquePtr { CGImageCreateWithImageInRect (parentImage.get(), + makeCGRect (self->area + parentNative.getTopLeft())) }; } #endif @@ -518,7 +521,7 @@ auto ImagePixelData::getNativeExtensions() -> NativeExtensions #if JUCE_MAC || JUCE_IOS CGContextRef getCGContext() const { return {}; } - CGImageRef getCGImage (CGColorSpaceRef) const { return {}; } + CFUniquePtr getCGImage (CGColorSpaceRef) const { return {}; } #endif }; diff --git a/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm b/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm index 51fad5e43c..52f1081c39 100644 --- a/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm +++ b/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm @@ -144,9 +144,9 @@ public: } //============================================================================== - static CGImageRef getCachedImageRef (const Image& juceImage, CGColorSpaceRef colourSpace) + static CFUniquePtr getCachedImageRef (const Image& juceImage, CGColorSpaceRef colourSpace) { - auto* cgim = std::invoke ([&]() -> CGImageRef + auto cgim = std::invoke ([&]() -> CFUniquePtr { if (auto ptr = juceImage.getPixelData()) return ptr->getNativeExtensions().getCGImage (colourSpace); @@ -155,7 +155,7 @@ public: }); if (cgim != nullptr) - return CGImageRetain (cgim); + return cgim; const Image::BitmapData srcData (juceImage, Image::BitmapData::readOnly); @@ -163,13 +163,17 @@ public: 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, - 8, - (size_t) srcData.pixelStride * 8, - (size_t) srcData.lineStride, - colourSpace, getCGImageFlags (juceImage.getFormat()), provider.get(), - nullptr, true, kCGRenderingIntentDefault); + return CFUniquePtr { CGImageCreate ((size_t) srcData.width, + (size_t) srcData.height, + 8, + (size_t) srcData.pixelStride * 8, + (size_t) srcData.lineStride, + colourSpace, + getCGImageFlags (juceImage.getFormat()), + provider.get(), + nullptr, + true, + kCGRenderingIntentDefault) }; } //============================================================================== @@ -185,10 +189,10 @@ public: HeapBlock data; }; - CGImageRef getCGImage (CGColorSpaceRef colourSpace) + CFUniquePtr getCGImage (CGColorSpaceRef colourSpace) { if (cachedImageRef != nullptr) - return cachedImageRef.get(); + return CFUniquePtr { CGImageRetain (cachedImageRef.get()) }; const Image::BitmapData srcData { Image { this }, Image::BitmapData::readOnly }; @@ -205,7 +209,7 @@ public: colourSpace, getCGImageFlags (pixelFormat), provider.get(), nullptr, true, kCGRenderingIntentDefault)); - return cachedImageRef.get(); + return CFUniquePtr { CGImageRetain (cachedImageRef.get()) }; } ImageDataContainer::Ptr imageData = new ImageDataContainer(); @@ -222,7 +226,7 @@ public: return self->context.get(); } - CGImageRef getCGImage (CGColorSpaceRef x) const + CFUniquePtr getCGImage (CGColorSpaceRef x) const { return self->getCGImage (x); } @@ -1200,7 +1204,7 @@ Image juce_createImageFromCIImage (CIImage* im, int w, int h) CGImageRef juce_createCoreGraphicsImage (const Image& juceImage, CGColorSpaceRef colourSpace) { - return CoreGraphicsPixelData::getCachedImageRef (juceImage, colourSpace); + return CoreGraphicsPixelData::getCachedImageRef (juceImage, colourSpace).release(); } CGContextRef juce_getImageContext (const Image& image)