13

I'm using the following code to crop and create a new UIImage out of a bigger one. I've isolated the issue to be with the function CGImageCreateWithImageInRect() which seem to not set some CGImage property the way I want. :-) The problem is that a call to function UIImagePNGRepresentation() fails returning a nil.

CGImageRef origRef = [stillView.image CGImage];
CGImageRef cgCrop = CGImageCreateWithImageInRect( origRef, theRect);
UIImage *imgCrop = [UIImage imageWithCGImage:cgCrop];

...

NSData *data = UIImagePNGRepresentation ( imgCrop);

-- libpng error: No IDATs written into file

Any idea what might wrong or alternative for cropping a rect out of UIImage?

Nimantha
  • 6,405
  • 6
  • 28
  • 69
Ari J.R.
  • 461
  • 3
  • 7
  • I got "libpng error: No IDATs written into file" when I enabled link time optimization. Did you enable this option? Disabling it fixed it for me. – rasmus Jun 23 '12 at 11:03

4 Answers4

3

I had the same problem, but only when testing compatibility on iOS 3.2. On 4.2 it works fine.

In the end I found this http://www.hive05.com/2008/11/crop-an-image-using-the-iphone-sdk/ which works on both, albeit a little more verbose!

I converted this into a category on UIImage:

UIImage+Crop.h

@interface UIImage (Crop)
- (UIImage*) imageByCroppingToRect:(CGRect)rect;
@end

UIImage+Crop.m

@implementation UIImage (Crop)

- (UIImage*) imageByCroppingToRect:(CGRect)rect
{
    //create a context to do our clipping in
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef currentContext = UIGraphicsGetCurrentContext();

    //create a rect with the size we want to crop the image to
    //the X and Y here are zero so we start at the beginning of our
    //newly created context
    CGRect clippedRect = CGRectMake(0, 0, rect.size.width, rect.size.height);
    CGContextClipToRect( currentContext, clippedRect);

    //create a rect equivalent to the full size of the image
    //offset the rect by the X and Y we want to start the crop
    //from in order to cut off anything before them
    CGRect drawRect = CGRectMake(rect.origin.x * -1,
                                 rect.origin.y * -1,
                                 self.size.width,
                                 self.size.height);

    //draw the image to our clipped context using our offset rect
    CGContextTranslateCTM(currentContext, 0.0, rect.size.height);
    CGContextScaleCTM(currentContext, 1.0, -1.0);
    CGContextDrawImage(currentContext, drawRect, self.CGImage);

    //pull the image from our cropped context
    UIImage *cropped = UIGraphicsGetImageFromCurrentImageContext();

    //pop the context to get back to the default
    UIGraphicsEndImageContext();

    //Note: this is autoreleased
    return cropped;
}


@end
cidered
  • 3,241
  • 3
  • 25
  • 20
  • This mechanism can be useful in a context other than when cropping. I had a UIImage that, presumably because of having been defined in an unusual color space, was not accepted by UIImagePNGRepresentation. I used code very similar to that above to regenerate the UIImage in a form acceptable to UIImagePNGRepresentation. – Paul Gardiner Apr 06 '16 at 08:42
1

In a PNG there are various chunks present, some containing palette info, some actual image data and some other information, it's a very interesting standard. The IDAT chunk is the bit that actually contains the image data. If there's no "IDAT written into file" then libpng has had some issue creating a PNG from the input data.

I don't know exactly what your stillView.image is, but what happens when you pass your code a CGImageRef that is certainly valid? What are the actual values in theRect? If your theRect is beyond the bounds of the image then the cgCrop you're trying to use to make the UIImage could easily be nil - or not nil, but containing no image or an image with width and height 0, giving libpng nothing to work with.

Adam Eberbach
  • 12,309
  • 6
  • 62
  • 114
  • Thank you for the answer. stillView is UIImageView. I peeked the property and function return values of cgCrop and imgCrop with gdb p/po commands and both seem to have correct dimension, imgCrop CGImage reference points to cgCrop etc. Hmmm. Will have a more detailed look when back from work. – Ari J.R. Feb 11 '10 at 08:48
1

It seems the solution you are trying should work, but I recommend to use this:

CGImageRef image = [stillView.image CGImage];
CGRect cropZone;

size_t cWitdh = cropZone.size.width;
size_t cHeight = cropZone.size.height;
size_t bitsPerComponent = CGImageGetBitsPerComponent(image);
size_t bytesPerRow = CGImageGetBytesPerRow(image) / CGImageGetWidth(image) * cWidth;
        
//Now we build a Context with those dimensions.
CGContextRef context = CGBitmapContextCreate(nil, cWitdh, cHeight, bitsPerComponent, bytesPerRow, CGColorSpaceCreateDeviceRGB(), CGImageGetBitmapInfo(image));

CGContextDrawImage(context, cropZone, image);

CGImageRef result  = CGBitmapContextCreateImage(context);
UIImage * cropUIImage = [[UIImage alloc] initWithCGImage:tmp];

CGContextRelease(context);
CGImageRelease(mergeResult);
NSData * imgData = UIImagePNGRepresentation ( cropUIImage);
Nimantha
  • 6,405
  • 6
  • 28
  • 69
jsan
  • 733
  • 11
  • 26
  • If your original image is "very large" I wonder if one option would be use the original image wherever you need the subimage, but set a clipping mask before drawing. That would allow you to avoid creating a "copy" from the original image. This approach might have it's own problems, so you'll have to "compare and contrast" the 2 methods. – nielsbot May 02 '11 at 05:11
  • ok--what i'm proposing is basically what cidered proposed. I think sicario's method could potentially use a lot more memory. – nielsbot May 02 '11 at 05:12
0
UIImage *croppedImage = [self imageByCropping:yourImageView.image toRect:heredefineyourRect];
 
    CGSize size = CGSizeMake(croppedImage.size.height, croppedImage.size.width);
    UIGraphicsBeginImageContext(size);
    
    CGPoint pointImg1 = CGPointMake(0,0);
    [croppedImage drawAtPoint:pointImg1 ];

    [[UIImage imageNamed:yourImagenameDefine] drawInRect:CGRectMake(0,532, 150,80) ];//here define your Reactangle
    
    UIImage* result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    croppedImage = result;
    yourCropImageView.image=croppedImage;
    [yourCropImageView.image retain];
Nimantha
  • 6,405
  • 6
  • 28
  • 69
Paras Joshi
  • 20,427
  • 11
  • 57
  • 70