CIImage (TIFFRepresentation) memory leak

  • Hi,

    I'm applying a Core Image filter to an image in a NSImageView
    subclass. The drawing works fine, except it leaks memory, not sure
    why. This is my drawRect method. Anything wrong with it? Thanks.

    - (void) drawRect:(NSRect)rect
    {

            [[self image] setFlipped:YES];
    CIImage *image = [CIImage imageWithData:[[self image] TIFFRepresentation]];
    if (image !=nil) {
      CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"];
      [filter setDefaults];
      [filter setValue: image forKey: @"inputImage"];
      [filter setValue: [NSNumber numberWithFloat:0.7] forKey: @"inputRadius"];
      image = [filter valueForKey: @"outputImage"];

      filter = [CIFilter filterWithName:@"CIPerspectiveTransform"];
      [filter setDefaults];
      [filter setValue: image forKey: @"inputImage"];
      [filter setValue:[CIVector vectorWithX:NSMinX(rect) Y:NSMinY(rect) +
    bLeft ] forKey:@"inputBottomLeft"];
      [filter setValue:[CIVector vectorWithX:NSMaxX(rect) Y:NSMinY(rect) +
    bRight ] forKey:@"inputBottomRight"];
      [filter setValue:[CIVector vectorWithX:NSMinX(rect) Y:NSMaxY(rect) +
    tLeft ] forKey:@"inputTopLeft"];
      [filter setValue:[CIVector vectorWithX:NSMaxX(rect) Y:NSMaxY(rect) +
    tRight ] forKey:@"inputTopRight"];
      image = [filter valueForKey: @"outputImage"];

      CGRect cg;
      CIContext *context = [[NSGraphicsContext currentContext] CIContext];
      cg = CGRectMake(NSMinX(rect), NSMinY(rect), NSWidth(rect), NSHeight(rect));
      [context drawImage:image
        atPoint:cg.origin
        fromRect:cg];

    }
    }
  • I narrowed this down by removing the filters, and the leak is still
    there. And what a leak! Everytime I draw the view the memory usage
    increases with the size of the drawn image. Yet this exact same code
    is used in the Core Image Programming Guide. So I figure it's a bug in
    Core Image. But how can I avoid it?

    - (void) drawRect:(NSRect)rect
    {
            CIImage *image = [CIImage imageWithData:[[self image]
    TIFFRepresentation]];
          if (image !=nil) {

                    CGRect cg;
                    CIContext *context = [[NSGraphicsContext
    currentContext] CIContext];
                    cg = CGRectMake(NSMinX(rect), NSMinY(rect),
    NSWidth(rect), NSHeight(rect));
                    [context drawImage:image
                                            atPoint:cg.origin
                                            fromRect:cg];

            }
    }

    On Mon, Mar 10, 2008 at 5:33 PM,  <slasktrattenator...> wrote:
    > Hi,
    >
    > I'm applying a Core Image filter to an image in a NSImageView
    > subclass. The drawing works fine, except it leaks memory, not sure
    > why. This is my drawRect method. Anything wrong with it? Thanks.
    >
    >
    > - (void) drawRect:(NSRect)rect
    > {
    >
    > [[self image] setFlipped:YES];
    > CIImage *image = [CIImage imageWithData:[[self image] TIFFRepresentation]];
    > if (image !=nil) {
    > CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"];
    > [filter setDefaults];
    > [filter setValue: image forKey: @"inputImage"];
    > [filter setValue: [NSNumber numberWithFloat:0.7] forKey: @"inputRadius"];
    > image = [filter valueForKey: @"outputImage"];
    >
    > filter = [CIFilter filterWithName:@"CIPerspectiveTransform"];
    > [filter setDefaults];
    > [filter setValue: image forKey: @"inputImage"];
    > [filter setValue:[CIVector vectorWithX:NSMinX(rect) Y:NSMinY(rect) +
    > bLeft ] forKey:@"inputBottomLeft"];
    > [filter setValue:[CIVector vectorWithX:NSMaxX(rect) Y:NSMinY(rect) +
    > bRight ] forKey:@"inputBottomRight"];
    > [filter setValue:[CIVector vectorWithX:NSMinX(rect) Y:NSMaxY(rect) +
    > tLeft ] forKey:@"inputTopLeft"];
    > [filter setValue:[CIVector vectorWithX:NSMaxX(rect) Y:NSMaxY(rect) +
    > tRight ] forKey:@"inputTopRight"];
    > image = [filter valueForKey: @"outputImage"];
    >
    > CGRect cg;
    > CIContext *context = [[NSGraphicsContext currentContext] CIContext];
    > cg = CGRectMake(NSMinX(rect), NSMinY(rect), NSWidth(rect), NSHeight(rect));
    > [context drawImage:image
    > atPoint:cg.origin
    > fromRect:cg];
    >
    > }
    > }
    >
  • If all the code you're using is present, it looks to me like you're not releasing the object pointed at by image; the object returned by imageWithData is not an auto-released object.

    > I narrowed this down by removing the filters, and the leak is still
    > there. And what a leak! Everytime I draw the view the memory usage
    > increases with the size of the drawn image. Yet this exact same code
    > is used in the Core Image Programming Guide. So I figure it's a bug in
    > Core Image. But how can I avoid it?
    >
    >
    > - (void) drawRect:(NSRect)rect
    > {
    > CIImage *image = [CIImage imageWithData:[[self image]
    > TIFFRepresentation]];
    > if (image !=nil) {
    >
    > CGRect cg;
    > CIContext *context = [[NSGraphicsContext
    > currentContext] CIContext];
    > cg = CGRectMake(NSMinX(rect), NSMinY(rect),
    > NSWidth(rect), NSHeight(rect));
    > [context drawImage:image
    > atPoint:cg.origin
    > fromRect:cg];
    >
    > }
    > }
    >
    > On Mon, Mar 10, 2008 at 5:33 PM,  <slasktrattenator...>  wrote:
    >> Hi,
    >>
    >> I'm applying a Core Image filter to an image in a NSImageView
    >> subclass. The drawing works fine, except it leaks memory, not sure
    >> why. This is my drawRect method. Anything wrong with it? Thanks.
    >>
    >>
    >> - (void) drawRect:(NSRect)rect
    >> {
    >>
    >> [[self image] setFlipped:YES];
    >> CIImage *image = [CIImage imageWithData:[[self image]
    > TIFFRepresentation]];
    >> if (image !=nil) {
    >> CIFilter *filter = [CIFilter
    > filterWithName:@"CIGaussianBlur"];
    >> [filter setDefaults];
    >> [filter setValue: image forKey: @"inputImage"];
    >> [filter setValue: [NSNumber numberWithFloat:0.7] forKey:
    > @"inputRadius"];
    >> image = [filter valueForKey: @"outputImage"];
    >>
    >> filter = [CIFilter
    > filterWithName:@"CIPerspectiveTransform"];
    >> [filter setDefaults];
    >> [filter setValue: image forKey: @"inputImage"];
    >> [filter setValue:[CIVector vectorWithX:NSMinX(rect)
    > Y:NSMinY(rect) +
    >> bLeft ] forKey:@"inputBottomLeft"];
    >> [filter setValue:[CIVector vectorWithX:NSMaxX(rect)
    > Y:NSMinY(rect) +
    >> bRight ] forKey:@"inputBottomRight"];
    >> [filter setValue:[CIVector vectorWithX:NSMinX(rect)
    > Y:NSMaxY(rect) +
    >> tLeft ] forKey:@"inputTopLeft"];
    >> [filter setValue:[CIVector vectorWithX:NSMaxX(rect)
    > Y:NSMaxY(rect) +
    >> tRight ] forKey:@"inputTopRight"];
    >> image = [filter valueForKey: @"outputImage"];
    >>
    >> CGRect cg;
    >> CIContext *context = [[NSGraphicsContext currentContext]
    > CIContext];
    >> cg = CGRectMake(NSMinX(rect), NSMinY(rect), NSWidth(rect),
    > NSHeight(rect));
    >> [context drawImage:image
    >> atPoint:cg.origin
    >> fromRect:cg];
    >>
    >> }
    >> }
    >>

  • On Mar 10, 2008, at 1:21 PM, Gary L. Wade wrote:

    > If all the code you're using is present, it looks to me like you're
    > not releasing the object pointed at by image; the object returned by
    > imageWithData is not an auto-released object.

    It's not? Why not? I thought convenience methods like that did not
    transfer ownership to the caller. Isn't the convention that the method
    should say alloc, new or copy to signify the granting of ownership?

    --Brady
  • Oh really? Hm... *looks away in shame*. That makes me feel kind of
    stupid.  I was under the impression it was an autoreleased object as
    there is no "init", "copy" or "new" in it's name, and the docs don't
    mention anything about the user being responsible for releasing it.
    How are you supposed to know this?

    Anyway, releasing the object seems to get rid of the leak, but after
    releasing it I cannot get the view to update it's content. I set a new
    image in the view, I can see the drawRect method is being called, the
    new CIImage is being created, but it's not being drawn, the view just
    keeps showing the first image... :-/

    What am I missing?

    On Mon, Mar 10, 2008 at 9:21 PM, Gary L. Wade
    <garywade...> wrote:
    > If all the code you're using is present, it looks to me like you're not releasing the object pointed at by image; the object returned by imageWithData is not an auto-released object.
    >
    >
    >
    >> I narrowed this down by removing the filters, and the leak is still
    >> there. And what a leak! Everytime I draw the view the memory usage
    >> increases with the size of the drawn image. Yet this exact same code
    >> is used in the Core Image Programming Guide. So I figure it's a bug in
    >> Core Image. But how can I avoid it?
    >>
    >>
    >> - (void) drawRect:(NSRect)rect
    >> {
    >> CIImage *image = [CIImage imageWithData:[[self image]
    >> TIFFRepresentation]];
    >> if (image !=nil) {
    >>
    >> CGRect cg;
    >> CIContext *context = [[NSGraphicsContext
    >> currentContext] CIContext];
    >> cg = CGRectMake(NSMinX(rect), NSMinY(rect),
    >> NSWidth(rect), NSHeight(rect));
    >> [context drawImage:image
    >> atPoint:cg.origin
    >> fromRect:cg];
    >>
    >> }
    >> }
    >>
    >> On Mon, Mar 10, 2008 at 5:33 PM,  <slasktrattenator...> wrote:
    >>> Hi,
    >>>
    >>> I'm applying a Core Image filter to an image in a NSImageView
    >>> subclass. The drawing works fine, except it leaks memory, not sure
    >>> why. This is my drawRect method. Anything wrong with it? Thanks.
    >>>
    >>>
    >>> - (void) drawRect:(NSRect)rect
    >>> {
    >>>
    >>> [[self image] setFlipped:YES];
    >>> CIImage *image = [CIImage imageWithData:[[self image]
    >> TIFFRepresentation]];
    >>> if (image !=nil) {
    >>> CIFilter *filter = [CIFilter
    >> filterWithName:@"CIGaussianBlur"];
    >>> [filter setDefaults];
    >>> [filter setValue: image forKey: @"inputImage"];
    >>> [filter setValue: [NSNumber numberWithFloat:0.7] forKey:
    >> @"inputRadius"];
    >>> image = [filter valueForKey: @"outputImage"];
    >>>
    >>> filter = [CIFilter
    >> filterWithName:@"CIPerspectiveTransform"];
    >>> [filter setDefaults];
    >>> [filter setValue: image forKey: @"inputImage"];
    >>> [filter setValue:[CIVector vectorWithX:NSMinX(rect)
    >> Y:NSMinY(rect) +
    >>> bLeft ] forKey:@"inputBottomLeft"];
    >>> [filter setValue:[CIVector vectorWithX:NSMaxX(rect)
    >> Y:NSMinY(rect) +
    >>> bRight ] forKey:@"inputBottomRight"];
    >>> [filter setValue:[CIVector vectorWithX:NSMinX(rect)
    >> Y:NSMaxY(rect) +
    >>> tLeft ] forKey:@"inputTopLeft"];
    >>> [filter setValue:[CIVector vectorWithX:NSMaxX(rect)
    >> Y:NSMaxY(rect) +
    >>> tRight ] forKey:@"inputTopRight"];
    >>> image = [filter valueForKey: @"outputImage"];
    >>>
    >>> CGRect cg;
    >>> CIContext *context = [[NSGraphicsContext currentContext]
    >> CIContext];
    >>> cg = CGRectMake(NSMinX(rect), NSMinY(rect), NSWidth(rect),
    >> NSHeight(rect));
    >>> [context drawImage:image
    >>> atPoint:cg.origin
    >>> fromRect:cg];
    >>>
    >>> }
    >>> }
    >>>


    >
  • "If all the code you're using is present, it looks to
    me like you're not releasing the object pointed at by
    image; the object returned by imageWithData is not an
    auto-released object."

    I just checked documentation
    (http://developer.apple.com/documentation/GraphicsImaging/Reference/QuartzCo
    reFramework/Classes/CIImage_Class/Reference/Reference.html#//apple_ref/occ/
    clm/CIImage/imageWithData
    :)
    and it does not mention that a convenience method
    +imageWithData: would return a retained object. And
    there is -initWithData: which I think implies that as
    with other Cocoa and similar frameworks based on ObjC
    +somethingWithSometingOther: returns an autoreleased
    instance while -initWithSomethingOther: is an
    initializer returning non-autoreleased instance. If
    this is different for CIImage it should be mentioned
    in the docs.

    Gorazd

          Looking for the perfect gift? Give the gift of Flickr!

    http://www.flickr.com/gift/
  • On 11/03/2008, at 6:03 AM, <slasktrattenator...> wrote:

    > I narrowed this down by removing the filters, and the leak is still
    > there. And what a leak! Everytime I draw the view the memory usage
    > increases with the size of the drawn image. Yet this exact same code
    > is used in the Core Image Programming Guide. So I figure it's a bug in
    > Core Image. But how can I avoid it?

    I had problems with this too, and I use a workaround I found somewhere
    where you render to a CGImageRef in the context of the current window.
    Here's a dump of the code:

    //theImage is an existing NSImage
    CIImage *outputImage = [CIImage imageWithData:[theImage
    TIFFRepresentation]];

    //to draw the image processed by Core Image, we need to draw into an
    on-screen graphics context
    //this works around a bug in CIImage where drawing in off-screen
    graphics contexts causes a huge memory leak

    //get the current window's graphics context so that we have an on-
    screen context
    //usually we would use any view's window but generically you can just
    ask for the main window
    CIContext *ciContext = [[[NSApp mainWindow] graphicsContext] CIContext];
    if(ciContext == nil)
    {
    NSLog(@"The CIContext of the main window could not be accessed.
    Bailing out of the image creation process.");
    return;
    }

    CGAffineTransform transform;
    transform = CGAffineTransformMakeTranslation(0.0,[outputImage
    extent].size.height);
    transform = CGAffineTransformScale(transform, 1.0, -1.0);
    outputImage = [outputImage imageByApplyingTransform:transform];

    //render the CIIimage into a CGImageRef in the on-screen context
    CGImageRef cgImage = [ciContext createCGImage:outputImage fromRect:
    [outputImage extent]];
    // Draw the CGImageRef into the current context
    if (cgImage != NULL)
    {
    CGContextDrawImage ([[NSGraphicsContext currentContext]
    graphicsPort], [outputImage extent], cgImage);
    CGImageRelease (cgImage);
    }

    HTH

    --
    Rob Keniger
  • Thanks a bunch! That worked great. No more leaking.

    On Tue, Mar 11, 2008 at 4:46 AM, Rob Keniger <rob...> wrote:
    > I had problems with this too, and I use a workaround I found somewhere
    > where you render to a CGImageRef in the context of the current window.
    > Here's a dump of the code:
    >
    > //theImage is an existing NSImage
    > CIImage *outputImage = [CIImage imageWithData:[theImage
    > TIFFRepresentation]];
    >
    > //to draw the image processed by Core Image, we need to draw into an
    > on-screen graphics context
    > //this works around a bug in CIImage where drawing in off-screen
    > graphics contexts causes a huge memory leak
    >
    > //get the current window's graphics context so that we have an on-
    > screen context
    > //usually we would use any view's window but generically you can just
    > ask for the main window
    > CIContext *ciContext = [[[NSApp mainWindow] graphicsContext] CIContext];
    > if(ciContext == nil)
    > {
    > NSLog(@"The CIContext of the main window could not be accessed.
    > Bailing out of the image creation process.");
    > return;
    > }
    >
    > CGAffineTransform transform;
    > transform = CGAffineTransformMakeTranslation(0.0,[outputImage
    > extent].size.height);
    > transform = CGAffineTransformScale(transform, 1.0, -1.0);
    > outputImage = [outputImage imageByApplyingTransform:transform];
    >
    > //render the CIIimage into a CGImageRef in the on-screen context
    > CGImageRef cgImage = [ciContext createCGImage:outputImage fromRect:
    > [outputImage extent]];
    > // Draw the CGImageRef into the current context
    > if (cgImage != NULL)
    > {
    > CGContextDrawImage ([[NSGraphicsContext currentContext]
    > graphicsPort], [outputImage extent], cgImage);
    > CGImageRelease (cgImage);
    > }
    >
    > HTH
    >
    > --
    > Rob Keniger
    >