mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
CoreGraphics: Use Image NativeExtensions to avoid dynamic casts in graphics context
This commit is contained in:
parent
d3ca5961e7
commit
c3e8b6bff3
2 changed files with 118 additions and 35 deletions
|
|
@ -280,6 +280,11 @@ public:
|
|||
Span<const Direct2DPixelDataPage> getPages (ComSmartPtr<ID2D1Device1> 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<const Direct2DPixelDataPage> getPages (ComSmartPtr<ID2D1Device1>) const = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename Impl>
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
virtual CGContextRef getCGContext() const = 0;
|
||||
virtual CGImageRef getCGImage (CGColorSpaceRef x) const = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename Impl>
|
||||
class Concrete : public Base
|
||||
{
|
||||
public:
|
||||
|
|
@ -304,6 +314,11 @@ private:
|
|||
Span<const Direct2DPixelDataPage> getPages (ComSmartPtr<ID2D1Device1> 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<int> getTopLeft() const
|
||||
{
|
||||
return self->sourceImage->getNativeExtensions().getTopLeft() + self->area.getTopLeft();
|
||||
|
|
@ -488,6 +515,11 @@ auto ImagePixelData::getNativeExtensions() -> NativeExtensions
|
|||
#if JUCE_WINDOWS
|
||||
Span<const Direct2DPixelDataPage> getPages (ComSmartPtr<ID2D1Device1>) const { return {}; }
|
||||
#endif
|
||||
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
CGContextRef getCGContext() const { return {}; }
|
||||
CGImageRef getCGImage (CGColorSpaceRef) const { return {}; }
|
||||
#endif
|
||||
};
|
||||
|
||||
return NativeExtensions { Wrapped{} };
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ namespace juce
|
|||
class CoreGraphicsPixelData final : public ImagePixelData
|
||||
{
|
||||
public:
|
||||
using Ptr = ReferenceCountedObjectPtr<CoreGraphicsPixelData>;
|
||||
|
||||
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<CoreGraphicsPixelData*> (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<CoreGraphicsPixelData*> (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<CFDataRef> 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<CFDataRef> 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<uint8> 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<int> getTopLeft() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
Ptr self;
|
||||
};
|
||||
|
||||
return NativeExtensions { Wrapped { this } };
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename BuildFilter>
|
||||
bool applyFilterInArea (Rectangle<int> 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<CoreGraphicsPixelData*> (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<CoreGraphicsPixelData*> (image.getPixelData().get()))
|
||||
return cgi->context.get();
|
||||
if (auto ptr = image.getPixelData())
|
||||
return ptr->getNativeExtensions().getCGContext();
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue