| @@ -27,60 +27,6 @@ | |||||
| namespace juce | namespace juce | ||||
| { | { | ||||
| #if (JUCE_IOS && defined (__IPHONE_12_0)) | |||||
| #define JUCE_CORE_GRAPHICS_NEEDS_DELAYED_GARBAGE_COLLECTION 1 | |||||
| #endif | |||||
| #if JUCE_CORE_GRAPHICS_NEEDS_DELAYED_GARBAGE_COLLECTION | |||||
| class CoreGraphicsImageGarbageCollector : public Timer, | |||||
| public DeletedAtShutdown | |||||
| { | |||||
| public: | |||||
| CoreGraphicsImageGarbageCollector() | |||||
| { | |||||
| // TODO: Add an assertion here telling JUCE developers to move to the | |||||
| // latest SDK if/when the CoreGraphics memory handling is fixed. | |||||
| } | |||||
| ~CoreGraphicsImageGarbageCollector() | |||||
| { | |||||
| clearSingletonInstance(); | |||||
| } | |||||
| JUCE_DECLARE_SINGLETON (CoreGraphicsImageGarbageCollector, false) | |||||
| void addItem (HeapBlock<uint8>&& data) | |||||
| { | |||||
| ScopedLock lock (queueLock); | |||||
| queue.emplace_back (Time::getApproximateMillisecondCounter(), std::move (data)); | |||||
| if (! isTimerRunning()) | |||||
| startTimer (timeDelta); | |||||
| } | |||||
| void timerCallback() override | |||||
| { | |||||
| ScopedLock lock (queueLock); | |||||
| auto cutoffTime = Time::getApproximateMillisecondCounter() - timeDelta; | |||||
| auto it = std::find_if (queue.begin(), queue.end(), | |||||
| [cutoffTime](const std::pair<uint32, HeapBlock<uint8>>& x) { return x.first > cutoffTime; }); | |||||
| queue.erase (queue.begin(), it); | |||||
| queue.empty() ? stopTimer() : startTimer (timeDelta); | |||||
| } | |||||
| private: | |||||
| CriticalSection queueLock; | |||||
| std::vector<std::pair<uint32, HeapBlock<uint8>>> queue; | |||||
| static constexpr uint32 timeDelta = 50; | |||||
| }; | |||||
| JUCE_IMPLEMENT_SINGLETON (CoreGraphicsImageGarbageCollector) | |||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| class CoreGraphicsImage : public ImagePixelData | class CoreGraphicsImage : public ImagePixelData | ||||
| { | { | ||||
| @@ -100,12 +46,12 @@ public: | |||||
| numComponents += (size_t) lineStride; | numComponents += (size_t) lineStride; | ||||
| #endif | #endif | ||||
| imageData.allocate (numComponents, clearImage); | |||||
| imageDataHolder->data.allocate (numComponents, clearImage); | |||||
| CGColorSpaceRef colourSpace = (format == Image::SingleChannel) ? CGColorSpaceCreateDeviceGray() | CGColorSpaceRef colourSpace = (format == Image::SingleChannel) ? CGColorSpaceCreateDeviceGray() | ||||
| : CGColorSpaceCreateDeviceRGB(); | : CGColorSpaceCreateDeviceRGB(); | ||||
| context = CGBitmapContextCreate (imageData, (size_t) width, (size_t) height, 8, (size_t) lineStride, | |||||
| context = CGBitmapContextCreate (imageDataHolder->data, (size_t) width, (size_t) height, 8, (size_t) lineStride, | |||||
| colourSpace, getCGImageFlags (format)); | colourSpace, getCGImageFlags (format)); | ||||
| CGColorSpaceRelease (colourSpace); | CGColorSpaceRelease (colourSpace); | ||||
| @@ -115,10 +61,6 @@ public: | |||||
| { | { | ||||
| freeCachedImageRef(); | freeCachedImageRef(); | ||||
| CGContextRelease (context); | CGContextRelease (context); | ||||
| #if JUCE_CORE_GRAPHICS_NEEDS_DELAYED_GARBAGE_COLLECTION | |||||
| CoreGraphicsImageGarbageCollector::getInstance()->addItem (std::move (imageData)); | |||||
| #endif | |||||
| } | } | ||||
| LowLevelGraphicsContext* createLowLevelContext() override | LowLevelGraphicsContext* createLowLevelContext() override | ||||
| @@ -130,7 +72,7 @@ public: | |||||
| void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override | void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override | ||||
| { | { | ||||
| bitmap.data = imageData + x * pixelStride + y * lineStride; | |||||
| bitmap.data = imageDataHolder->data + x * pixelStride + y * lineStride; | |||||
| bitmap.pixelFormat = pixelFormat; | bitmap.pixelFormat = pixelFormat; | ||||
| bitmap.lineStride = lineStride; | bitmap.lineStride = lineStride; | ||||
| bitmap.pixelStride = pixelStride; | bitmap.pixelStride = pixelStride; | ||||
| @@ -145,7 +87,7 @@ public: | |||||
| ImagePixelData::Ptr clone() override | ImagePixelData::Ptr clone() override | ||||
| { | { | ||||
| auto im = new CoreGraphicsImage (pixelFormat, width, height, false); | auto im = new CoreGraphicsImage (pixelFormat, width, height, false); | ||||
| memcpy (im->imageData, imageData, (size_t) (lineStride * height)); | |||||
| memcpy (im->imageDataHolder->data, imageDataHolder->data, (size_t) (lineStride * height)); | |||||
| return *im; | return *im; | ||||
| } | } | ||||
| @@ -165,10 +107,7 @@ public: | |||||
| CGImageRef ref = createImage (juceImage, colourSpace, false); | CGImageRef ref = createImage (juceImage, colourSpace, false); | ||||
| if (cgim != nullptr) | if (cgim != nullptr) | ||||
| { | |||||
| CGImageRetain (ref); | |||||
| cgim->cachedImageRef = ref; | |||||
| } | |||||
| cgim->cachedImageRef = CGImageRetain (ref); | |||||
| return ref; | return ref; | ||||
| } | } | ||||
| @@ -186,7 +125,18 @@ public: | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| provider = CGDataProviderCreateWithData (nullptr, srcData.data, (size_t) srcData.lineStride * (size_t) srcData.height, nullptr); | |||||
| auto* imageDataContainer = [](const Image& img) -> HeapBlockContainer::Ptr* | |||||
| { | |||||
| if (auto* cgim = dynamic_cast<CoreGraphicsImage*> (img.getPixelData())) | |||||
| return new HeapBlockContainer::Ptr (cgim->imageDataHolder); | |||||
| return nullptr; | |||||
| } (juceImage); | |||||
| provider = CGDataProviderCreateWithData (imageDataContainer, | |||||
| srcData.data, | |||||
| (size_t) srcData.lineStride * (size_t) srcData.height, | |||||
| [] (void * __nullable info, const void*, size_t) { delete (HeapBlockContainer::Ptr*) info; }); | |||||
| } | } | ||||
| CGImageRef imageRef = CGImageCreate ((size_t) srcData.width, | CGImageRef imageRef = CGImageCreate ((size_t) srcData.width, | ||||
| @@ -204,7 +154,14 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| CGContextRef context; | CGContextRef context; | ||||
| CGImageRef cachedImageRef = {}; | CGImageRef cachedImageRef = {}; | ||||
| HeapBlock<uint8> imageData; | |||||
| struct HeapBlockContainer : public ReferenceCountedObject | |||||
| { | |||||
| using Ptr = ReferenceCountedObjectPtr<HeapBlockContainer>; | |||||
| HeapBlock<uint8> data; | |||||
| }; | |||||
| HeapBlockContainer::Ptr imageDataHolder = new HeapBlockContainer(); | |||||
| int pixelStride, lineStride; | int pixelStride, lineStride; | ||||
| private: | private: | ||||
| @@ -883,22 +840,31 @@ void CoreGraphicsContext::applyTransform (const AffineTransform& transform) cons | |||||
| #if USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER | #if USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER | ||||
| Image juce_loadWithCoreImage (InputStream& input) | Image juce_loadWithCoreImage (InputStream& input) | ||||
| { | { | ||||
| MemoryBlock data; | |||||
| input.readIntoMemoryBlock (data, -1); | |||||
| struct MemoryBlockHolder : public ReferenceCountedObject | |||||
| { | |||||
| using Ptr = ReferenceCountedObjectPtr<MemoryBlockHolder>; | |||||
| MemoryBlock block; | |||||
| }; | |||||
| MemoryBlockHolder::Ptr memBlockHolder = new MemoryBlockHolder(); | |||||
| input.readIntoMemoryBlock (memBlockHolder->block, -1); | |||||
| #if JUCE_IOS | #if JUCE_IOS | ||||
| JUCE_AUTORELEASEPOOL | JUCE_AUTORELEASEPOOL | ||||
| #endif | #endif | ||||
| { | { | ||||
| #if JUCE_IOS | #if JUCE_IOS | ||||
| if (UIImage* uiImage = [UIImage imageWithData: [NSData dataWithBytesNoCopy: data.getData() | |||||
| length: data.getSize() | |||||
| if (UIImage* uiImage = [UIImage imageWithData: [NSData dataWithBytesNoCopy: memBlockHolder->block.getData() | |||||
| length: memBlockHolder->block.getSize() | |||||
| freeWhenDone: NO]]) | freeWhenDone: NO]]) | ||||
| { | { | ||||
| CGImageRef loadedImage = uiImage.CGImage; | CGImageRef loadedImage = uiImage.CGImage; | ||||
| #else | #else | ||||
| auto provider = CGDataProviderCreateWithData (nullptr, data.getData(), data.getSize(), nullptr); | |||||
| auto provider = CGDataProviderCreateWithData (new MemoryBlockHolder::Ptr (memBlockHolder), | |||||
| memBlockHolder->block.getData(), | |||||
| memBlockHolder->block.getSize(), | |||||
| [] (void * __nullable info, const void*, size_t) { delete (MemoryBlockHolder::Ptr*) info; }); | |||||
| auto imageSource = CGImageSourceCreateWithDataProvider (provider, nullptr); | auto imageSource = CGImageSourceCreateWithDataProvider (provider, nullptr); | ||||
| CGDataProviderRelease (provider); | CGDataProviderRelease (provider); | ||||