Drawing image with fillAlphaChannelWithCurrentBrush


#1

Hello!

I'm on OSX and JUCE v2.1.2 and have a question regarding using large single-channel (gray scale) images as an alpha mask in the following graphics call (emphasis on the last argument "true" -> fillAlphaChannelWithCurrentBrush):

    g.drawImage(image, 0, 0, w, h, sourceX, sourceY, sourceWidth, sourceHeight, true);

 

When I use this method with very large images at a relatively high repaint rate, I get a bas access error on a CFDataCreate call in createImage. Specifically, the call stack as a result of calling the drawImage function call is:

#4  juce::Graphics::drawImage(juce::Image const&, int, int, int, int, int, int, int, int, bool) const at juce/modules/juce_graphics/contexts/juce_GraphicsContext.cpp:674

#3  juce::Graphics::drawImageTransformed(juce::Image const&, juce::AffineTransform const&, bool) const at /juce/modules/juce_graphics/contexts/juce_GraphicsContext.cpp:689

#2 juce::CoreGraphicsContext::clipToImageAlpha(juce::Image const&, juce::AffineTransform const&) at /juce/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm:255

#1 juce::CoreGraphicsImage::createImage(juce::Image const&, CGColorSpace*, bool) at /juce/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm:84

In the clipToImageAlpha method, there is a call to CoreGraphicsImage::createImage which has an argument to copy or reference the image data (mustOutliveSource).  I beleive this argument should be false (currently true).  More specifically:
 
My problem can be resolved by changing the internals of clipToImageAlpha.  Specifically, changing the mustOutliveSource parameter in the createImage call from true to false:
BEFORE
CGImageRef image = CoreGraphicsImage::createImage (singleChannelImage, greyColourSpace, true);
AFTER
CGImageRef image = CoreGraphicsImage::createImage (singleChannelImage, greyColourSpace, false);

 

When I make this change, my bad access error is removed and I do not notice any other associated problems.

So, my question :) ...is there a reason the alpha mask image is copied (mustOutliveSource=true)? Other calls to createImage don't seem to need the copy.

Thanks and let me know if I can clarify anything!

Regards, Nick


#2

Yes, it must be set to outlive the source.It's creating an image for the CGContext to use for clipping, and the context will store and use that image after the function has returned, and after the temporary greyscale image has been freed, so unless the data is copied, the CGContext may use a dangling pointer. In your code you may just be lucky that you've passed a clip image that doesn't get deleted, or perhaps CoreGraphics happens to take an internal copy, but your suggested change is definitely not safe.

Makes more sense to debug the actual problem that's making it crash... You say the image is large - is the data perhaps too large for the CFDataCreate call at line 83? Is that returning a nullptr or something?


#3

Thanks for the response. So, I did a some more digging and...

1) I don't believe the issue is related to a limit on memory allocation. While the image sizes are large (30MB), they are not that large. And...

2) The bad access only arises when there is a translation on the image.  That is, the sourceX parameter is non-zero (i.e. sourceX = 1000) when I call:

drawImage( imageToDraw, destX,  destY, destWidth, destHeight, sourceX, sourceY, sourceWidth, sourceHeight,  fillAlpha = true) 

 

 

So, to explain what I think is going wrong in the createImage function, starting on line 76 in juce_mac_CoreGraphicsContext.mm, 

1) When the input argument "juceImage" is a subimage of an larger image (say a translation), the creation of the Image::BitmapData object on line 79 will take the translation into account and advance pointer to the internal data buffer (srcData.data).  See line 146 in juce_Image.cpp. 

2) After the creation of the Image::BitmapData object, line 84 then tries to copy the srcData.data buffer into a CFDataRef object with the original size of the image (srcData.lineStride * srcData.height).  Because the internal image data buffer was already advanced from the translation, however, the copy walks off the end of the buffer causing the crash.

3) With smaller images and translation, this problem might not arise if there's a small amount of memory padding in the internal buffer or similar.

While I don't have a good enough grasp on the internals of the juce Image object for simple fix, it seems like 1) there needs to be a way to get the head of the internal data buffer of the image (and not an advanced version) and provide that to the CFDataCreate call or 2) find a way to only copy the required subimage data in the CFDataCreate call. I'm currently trying for the latter, but it would be great to hear your thoughts!

Thanks for the help!


#4

Duplicate Comment


#5

Can you give me a code snippet that reproduces it?


#6

Will try to get a simple stand alone/simple example...currently in a large code base. Will try to post in a few hours...


#7

Of course I haven't been able reproduce the problem in a simple standalone and, as a result, I believe the problem is not within JUCE.  If it comes up again, I will repost.  Sorry for the waste of time and thanks for the help.