1

This code (source is an NSMutableArray with NSImage objects:

NSURL *fileURL = [NSURL fileURLWithPath:aPath];
CGImageDestinationRef dr = CGImageDestinationCreateWithURL((CFURLRef)fileURL,
                                                           kUTTypeAppleICNS,
                                                           1,
                                                           NULL);

for (NSImage *img in source) {

    NSLog(@"%@",img);
    CGImageRef i1 = [img CGImageForProposedRect:NULL context:nil hints:nil];
    CGImageDestinationAddImage(dr, i1, NULL);

}

CGImageDestinationFinalize(dr);
CFRelease(dr);

Resulted in the following output :

2012-12-26 13:48:57.682 Eicon[1131:1b0f] |NSImage 0x1025233b0 Size={11.52, 11.52} Reps=( "NSBitmapImageRep 0x10421fc30 Size={11.52, 11.52} ColorSpace=(not yet loaded) BPS=8 BPP=(not yet loaded) Pixels=1024x1024 Alpha=NO Planar=NO Format=(not yet loaded) CurrentBacking=nil (faulting) CGImageSource=0x104221170"

(...) <- here the other NSImage instances are logged.

ImageIO: |ERROR| CGImageDestinationFinalize image destination does not have enough images

Any ideas why it ends with the error at the end? (BTW, I thought of using IconFamily instead but a lot of the methods it uses are deprecated).

EDIT : Maybe it'd be more important to mention the code I use to generate the images, I wouldn't be surprised if that code has problems (I'm a noob) :

// Get images
[source removeAllObjects];
NSSize sizes[10];
sizes[0] = NSMakeSize(1024,1024);
sizes[1] = NSMakeSize(512,512);
sizes[2] = NSMakeSize(512,512);
sizes[3] = NSMakeSize(256,256);
sizes[4] = NSMakeSize(256,256);
sizes[5] = NSMakeSize(128,128);
sizes[6] = NSMakeSize(64,64);
sizes[7] = NSMakeSize(32,32);
sizes[8] = NSMakeSize(32,32);
sizes[9] = NSMakeSize(16,16);
for (int i=0 ; i<10 ; i++) {
    if ([[NSUserDefaults standardUserDefaults] boolForKey:[NSString stringWithFormat:@"Size%i",i+1]]) {
        NSBitmapImageRep *srcimg = [[NSBitmapImageRep alloc] initWithData:[NSData dataWithContentsOfFile:aFile]];
        [srcimg setPixelsHigh:sizes[i].height];
        [srcimg setPixelsWide:sizes[i].width];
        NSImage *newimg = [[NSImage alloc] initWithSize:NSZeroSize];
        [newimg addRepresentation:srcimg];
        if (![newimg isValid]) {
            [delegate error:@"Invalid file."];
        }
        else [source addObject:newimg];
    }
}
Fatso
  • 1,278
  • 16
  • 46

2 Answers2

3

One part guess at the solution, one part change that will improve the code even if it isn't the solution…

Your number of images in CGImageDestinationCreateWithURL is wrong; you should be passing the number of images you expect to add.

Given a loop, you clearly expect to be adding one or more. Given your output, you are, in fact, adding more than one image.

You said you would add exactly 1 image, and then you added more than one.

My guess is that the error message is unclear/overly specific. The error says:

CGImageDestinationFinalize image destination does not have enough images

But I think what it means is:

CGImageDestinationFinalize image destination has a different number of images than you promised

In other words, the problem isn't that you provided fewer images than expected; it's that you provided more than expected, and CGImageDestination draws no distinction between more and fewer.

Change your CGImageDestinationCreateWithURL call to provide the correct number of images that you'll be adding. Given the code in your question, this should be source.count.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • Okay since this solved it, I'm gonna give you the bounty. Now, when I try to add a 1024x1024 image it says *"ImageIO: _CGImagePluginWriteICNS unsupported image size (1024 x 1024) - scaling factor: 1"* – Fatso Dec 29 '12 at 07:25
  • I have to wait 10 hours to award you the bounty will do that then :) – Fatso Dec 29 '12 at 07:26
  • @Korion: 1024-pixel @ 1x elements in IconFamily images are deprecated as of Mountain Lion. They should be added as 512-point @ 2x (same pixel size, but tagged as 512 @ 2x rather than 1024 @ 1x). – Peter Hosey Dec 29 '12 at 07:46
  • Oh ok! Silly question, but how does one tag an image? – Fatso Dec 29 '12 at 07:59
  • @Korion: You don't. You'd do this by setting the point size of the 1024-px images to 512 by 512; CGImageDestination's IconFamily writer will infer the correct scaling factor/resolution from the ratio of the pixel size over the point size. – Peter Hosey Dec 29 '12 at 08:07
  • Oh okay! I'll update the code today but this seems to be on the right track now. Thanks for all the help Peter, your answers are very well-written! – Fatso Dec 29 '12 at 09:03
  • Hmmm, this is the last problem I have, any idea what code to use? I set the point size to 512x512 and the pixel size to 1024x1024, shouldn't that work? – Fatso Dec 29 '12 at 14:55
  • @Korion: You shouldn't need to set the pixel size. You should only set the point size of the image that bears a 1024×1024 representation. (You might also need to set the size of that rep, as well.) – Peter Hosey Dec 29 '12 at 18:44
  • Even if I set the size of both the image and the rep, and the NSLog statement confirms that this has been done, the error still persists. – Fatso Dec 29 '12 at 19:20
  • It looks like the image doesn't get resized at all. When I use a 512x512 image, it just adds this image 10 times to an ICNS file. – Fatso Dec 29 '12 at 19:33
  • It's not meant to get resized. It was 1024×1024 pixels before and it should remain so; you're simply tagging it as 512×512 points @ 2x rather than 1024×1024 points @ 1x. It adds it 10 times? That shouldn't happen. You should ask a separate question about that. – Peter Hosey Dec 29 '12 at 19:37
  • Yep as @PeterHosey mentioned to elaborate I just count my array of images that its expecting to receive CGImageDestinationRef destination = CGImageDestinationCreateWithURL((CFURLRef)[NSURL fileURLWithPath:self.pathToExportedGif], kUTTypeGIF, images.count,//Count local image array NULL); – Alex McPherson Jan 20 '16 at 13:54
1

Going through -TIFFRepresentation and CGImageSource is highly roundabout. Use the CGImageForProposedRect:context:hints: method instead.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • Yes, I saw that you mentioned this in http://stackoverflow.com/questions/2548059/turning-an-nsimage-into-a-cgimageref, will adapt my code. – Fatso Dec 28 '12 at 11:07
  • @Korion: Please edit your question to show your amended code and what results from it. – Peter Hosey Dec 28 '12 at 20:41