mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
CoreGraphics: Fix bug where subsection images were rendered incorrectly
This commit is contained in:
parent
a381fdf81d
commit
56ea531298
3 changed files with 92 additions and 21 deletions
|
|
@ -871,4 +871,68 @@ Graphics::ScopedSaveState::~ScopedSaveState()
|
||||||
context.restoreState();
|
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
|
} // namespace juce
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,7 @@ public:
|
||||||
|
|
||||||
#if JUCE_MAC || JUCE_IOS
|
#if JUCE_MAC || JUCE_IOS
|
||||||
CGContextRef getCGContext() const { return impl->getCGContext(); }
|
CGContextRef getCGContext() const { return impl->getCGContext(); }
|
||||||
CGImageRef getCGImage (CGColorSpaceRef x) const { return impl->getCGImage (x); }
|
CFUniquePtr<CGImageRef> getCGImage (CGColorSpaceRef x) const { return impl->getCGImage (x); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -297,7 +297,7 @@ private:
|
||||||
|
|
||||||
#if JUCE_MAC || JUCE_IOS
|
#if JUCE_MAC || JUCE_IOS
|
||||||
virtual CGContextRef getCGContext() const = 0;
|
virtual CGContextRef getCGContext() const = 0;
|
||||||
virtual CGImageRef getCGImage (CGColorSpaceRef x) const = 0;
|
virtual CFUniquePtr<CGImageRef> getCGImage (CGColorSpaceRef x) const = 0;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -316,7 +316,7 @@ private:
|
||||||
|
|
||||||
#if JUCE_MAC || JUCE_IOS
|
#if JUCE_MAC || JUCE_IOS
|
||||||
CGContextRef getCGContext() const override { return impl.getCGContext(); }
|
CGContextRef getCGContext() const override { return impl.getCGContext(); }
|
||||||
CGImageRef getCGImage (CGColorSpaceRef x) const override { return impl.getCGImage (x); }
|
CFUniquePtr<CGImageRef> getCGImage (CGColorSpaceRef x) const override { return impl.getCGImage (x); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -417,9 +417,12 @@ public:
|
||||||
return self->sourceImage->getNativeExtensions().getCGContext();
|
return self->sourceImage->getNativeExtensions().getCGContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
CGImageRef getCGImage (CGColorSpaceRef colourSpace) const
|
CFUniquePtr<CGImageRef> getCGImage (CGColorSpaceRef colourSpace) const
|
||||||
{
|
{
|
||||||
return self->sourceImage->getNativeExtensions().getCGImage (colourSpace);
|
const auto& parentNative = self->sourceImage->getNativeExtensions();
|
||||||
|
const auto parentImage = parentNative.getCGImage (colourSpace);
|
||||||
|
return CFUniquePtr<CGImageRef> { CGImageCreateWithImageInRect (parentImage.get(),
|
||||||
|
makeCGRect (self->area + parentNative.getTopLeft())) };
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -518,7 +521,7 @@ auto ImagePixelData::getNativeExtensions() -> NativeExtensions
|
||||||
|
|
||||||
#if JUCE_MAC || JUCE_IOS
|
#if JUCE_MAC || JUCE_IOS
|
||||||
CGContextRef getCGContext() const { return {}; }
|
CGContextRef getCGContext() const { return {}; }
|
||||||
CGImageRef getCGImage (CGColorSpaceRef) const { return {}; }
|
CFUniquePtr<CGImageRef> getCGImage (CGColorSpaceRef) const { return {}; }
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -144,9 +144,9 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
static CGImageRef getCachedImageRef (const Image& juceImage, CGColorSpaceRef colourSpace)
|
static CFUniquePtr<CGImageRef> getCachedImageRef (const Image& juceImage, CGColorSpaceRef colourSpace)
|
||||||
{
|
{
|
||||||
auto* cgim = std::invoke ([&]() -> CGImageRef
|
auto cgim = std::invoke ([&]() -> CFUniquePtr<CGImageRef>
|
||||||
{
|
{
|
||||||
if (auto ptr = juceImage.getPixelData())
|
if (auto ptr = juceImage.getPixelData())
|
||||||
return ptr->getNativeExtensions().getCGImage (colourSpace);
|
return ptr->getNativeExtensions().getCGImage (colourSpace);
|
||||||
|
|
@ -155,7 +155,7 @@ public:
|
||||||
});
|
});
|
||||||
|
|
||||||
if (cgim != nullptr)
|
if (cgim != nullptr)
|
||||||
return CGImageRetain (cgim);
|
return cgim;
|
||||||
|
|
||||||
const Image::BitmapData srcData (juceImage, Image::BitmapData::readOnly);
|
const Image::BitmapData srcData (juceImage, Image::BitmapData::readOnly);
|
||||||
|
|
||||||
|
|
@ -163,13 +163,17 @@ public:
|
||||||
CFUniquePtr<CFDataRef> data (CFDataCreate (nullptr, (const UInt8*) srcData.data, (CFIndex) usableSize));
|
CFUniquePtr<CFDataRef> data (CFDataCreate (nullptr, (const UInt8*) srcData.data, (CFIndex) usableSize));
|
||||||
detail::DataProviderPtr provider { CGDataProviderCreateWithCFData (data.get()) };
|
detail::DataProviderPtr provider { CGDataProviderCreateWithCFData (data.get()) };
|
||||||
|
|
||||||
return CGImageCreate ((size_t) srcData.width,
|
return CFUniquePtr<CGImageRef> { CGImageCreate ((size_t) srcData.width,
|
||||||
(size_t) srcData.height,
|
(size_t) srcData.height,
|
||||||
8,
|
8,
|
||||||
(size_t) srcData.pixelStride * 8,
|
(size_t) srcData.pixelStride * 8,
|
||||||
(size_t) srcData.lineStride,
|
(size_t) srcData.lineStride,
|
||||||
colourSpace, getCGImageFlags (juceImage.getFormat()), provider.get(),
|
colourSpace,
|
||||||
nullptr, true, kCGRenderingIntentDefault);
|
getCGImageFlags (juceImage.getFormat()),
|
||||||
|
provider.get(),
|
||||||
|
nullptr,
|
||||||
|
true,
|
||||||
|
kCGRenderingIntentDefault) };
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
@ -185,10 +189,10 @@ public:
|
||||||
HeapBlock<uint8> data;
|
HeapBlock<uint8> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
CGImageRef getCGImage (CGColorSpaceRef colourSpace)
|
CFUniquePtr<CGImageRef> getCGImage (CGColorSpaceRef colourSpace)
|
||||||
{
|
{
|
||||||
if (cachedImageRef != nullptr)
|
if (cachedImageRef != nullptr)
|
||||||
return cachedImageRef.get();
|
return CFUniquePtr<CGImageRef> { CGImageRetain (cachedImageRef.get()) };
|
||||||
|
|
||||||
const Image::BitmapData srcData { Image { this }, Image::BitmapData::readOnly };
|
const Image::BitmapData srcData { Image { this }, Image::BitmapData::readOnly };
|
||||||
|
|
||||||
|
|
@ -205,7 +209,7 @@ public:
|
||||||
colourSpace, getCGImageFlags (pixelFormat), provider.get(),
|
colourSpace, getCGImageFlags (pixelFormat), provider.get(),
|
||||||
nullptr, true, kCGRenderingIntentDefault));
|
nullptr, true, kCGRenderingIntentDefault));
|
||||||
|
|
||||||
return cachedImageRef.get();
|
return CFUniquePtr<CGImageRef> { CGImageRetain (cachedImageRef.get()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageDataContainer::Ptr imageData = new ImageDataContainer();
|
ImageDataContainer::Ptr imageData = new ImageDataContainer();
|
||||||
|
|
@ -222,7 +226,7 @@ public:
|
||||||
return self->context.get();
|
return self->context.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
CGImageRef getCGImage (CGColorSpaceRef x) const
|
CFUniquePtr<CGImageRef> getCGImage (CGColorSpaceRef x) const
|
||||||
{
|
{
|
||||||
return self->getCGImage (x);
|
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)
|
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)
|
CGContextRef juce_getImageContext (const Image& image)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue