• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: NSImageRGBColorTable crash
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: NSImageRGBColorTable crash


  • Subject: Re: NSImageRGBColorTable crash
  • From: Andy Matuschak <email@hidden>
  • Date: Fri, 16 Jul 2004 10:13:24 -0400
  • Resent-date: Fri, 16 Jul 2004 10:35:07 -0400
  • Resent-from: Andy Matuschak <email@hidden>
  • Resent-message-id: <4CB9A3D6-D732-11D8-AA5D-000A95AB8E70@andymatuschak.o rg>
  • Resent-to: email@hidden

Hello. I've spent the last two days trying to figure out a way around this bug, and I've found a number of things:

1. The number of colors in the color table must be a power of two.
2. The number of bytes doesn't have to be 768 - it can be any other power of two * 3.
3. There's an issue with repetition: If you have five colors, it expects you to be using a palette of eight colors and so it expects you to have filled the last three palette entries with black. However, if you have five colors and use a palette of sixteen colors, filling the last eleven with black, it gets confused. This is most likely why you're having problems with writing out 256 entries of black.
4. Most importantly, however, after one fixes everything and conforms to the way GIF expects the table to formatted, it still won't work. Why? Because Cocoa's implementation is broken. I spent many an hour wondering why my colors got all screwed up before I tried something very simple: let's load in a gif, then write it out again using its own color table.

Turns out Cocoa can't round-trip a gif. The implementation is either incomplete or broken. A note would've been nice before I spent all that time. Anyway, I decided to figure out another way to write gifs. I found libungif, which you can download here: http://sourceforge.net/projects/libungif. It's an open-source gif manipulation library that doesn't have any LZW compression for patent reasons. It's MIT license, so you can use it in pretty much any project. Just download it, compile and install. I chose to statically link it so that my users didn't have to download the library, but that's not really necessary. Anyway, here's the method I wrote to take an NSImage, quantize it, and get all the data. It's a little messy after being written at 4:00 AM, but it works:

+ gifDataForImage:anImage
{
id image = [[[NSImage alloc] initWithData:[anImage TIFFRepresentation]] autorelease];
NSSize size = [image size];
[image lockFocus];

// first we find a valid transparent color
id transparentColor = [NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:1];
BOOL found = NO;
while (!found) // I know this is ugly. It works.
{
found = NO;
int i, j;
for (i = 0; i < size.width; i++)
{
for (j = 0; j < size.height; j++)
{
id converted = [NSReadPixel(NSMakePoint(i, j)) colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
if ((unsigned char)([converted redComponent] * 255) == (unsigned char)([transparentColor redComponent] * 255) && (unsigned char)([converted greenComponent] * 255) == (unsigned char)([transparentColor greenComponent] * 255) && (unsigned char)([converted blueComponent] * 255) == (unsigned char)([transparentColor blueComponent] * 255))
{
if ([transparentColor redComponent] < 1)
{
transparentColor = [NSColor colorWithCalibratedRed:[transparentColor redComponent]+0.1 green:[transparentColor greenComponent] blue:[transparentColor blueComponent] alpha:1];
}
else if ([transparentColor greenComponent] < 1)
{
transparentColor = [NSColor colorWithCalibratedRed:[transparentColor redComponent] green:[transparentColor greenComponent]+0.1 blue:[transparentColor blueComponent] alpha:1];
}
else if ([transparentColor blueComponent] < 1)
{
transparentColor = [NSColor colorWithCalibratedRed:[transparentColor redComponent] green:[transparentColor greenComponent] blue:[transparentColor blueComponent]+0.1 alpha:1];
}
found = YES;
}
}
}
}

int colorMapSize = 256;
GifByteType *redBuffer = malloc(size.width * size.height);
GifByteType *greenBuffer = malloc(size.width * size.height);
GifByteType *blueBuffer = malloc(size.width * size.height);
EGifSetGifVersion("89a");

int i, j, count = 0;
for (j = size.height - 1; j >= 0; j--)
{
for (i = 0; i < size.width; i++, count++)
{
NSColor * color = [NSReadPixel(NSMakePoint(i, j)) colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
if ([color alphaComponent] < 0.5) { color = transparentColor; }

redBuffer[count] = (GifByteType)([color redComponent] * 255);
greenBuffer[count] = (GifByteType)([color greenComponent] * 255);
blueBuffer[count] = (GifByteType)([color blueComponent] * 255);
}
}

ColorMapObject *colorMap = MakeMapObject(colorMapSize, NULL);
GifByteType *outputBuffer = malloc(size.width * size.height * sizeof(GifByteType));
QuantizeBuffer(size.width, size.height, &colorMapSize, redBuffer, greenBuffer, blueBuffer, outputBuffer, colorMap->Colors);

GifFileType *gifFile = EGifOpenFileName("/tmp/dummy.gif", NO);

//find the index of the transparent color in the color map
unsigned transparentIndex, bestDelta = 1000;
for (i = 0; i < colorMapSize; i++)
{
unsigned char transRed = (unsigned char)([transparentColor redComponent] * 255);
unsigned char transGreen = (unsigned char)([transparentColor greenComponent] * 255);
unsigned char transBlue = (unsigned char)([transparentColor blueComponent] * 255);
GifColorType color = colorMap->Colors[i];
if (color.Red == transRed && color.Green == transGreen && color.Blue == transBlue)
{
transparentIndex = i;
}
else
{
unsigned int tempDelta = (color.Red < transRed ? transRed - color.Red : color.Red - transRed) +
(color.Green < transGreen ? transGreen - color.Green : color.Green - transGreen) +
(color.Blue < transBlue ? transBlue - color.Blue : color.Blue - transBlue);
if (tempDelta < bestDelta)
{
transparentIndex = i;
bestDelta = tempDelta;
}
}
}

EGifPutScreenDesc(gifFile, size.width, size.height, colorMapSize, 0, colorMap);
unsigned char extension[4] = { 0 };
extension[0] = 0x01; // byte 1 is a flag; 00000001 turns transparency on.
extension[1] = 0x00; // byte 2 is delay time, presumably for animation.
extension[2] = 0x00; // byte 3 is continued delay time.
extension[3] = transparentIndex; // byte 4 is the index of the transparent color in the palette.
EGifPutExtension(gifFile, 0xF9, sizeof(extension), extension); // 0xf9 is the transparency extension magic code
EGifPutImageDesc(gifFile, 0, 0, size.width, size.height, 0, NULL);

GifByteType * position = outputBuffer;
for (i = 0; i < size.height; i++)
{
EGifPutLine(gifFile, position, size.width);
position += (int)size.width;
}
EGifCloseFile(gifFile);

[image unlockFocus];
id finalData = [NSData dataWithContentsOfFile:@"/tmp/dummy.gif"];
remove("/tmp/dummy.gif");
return finalData;
}

Hope this helps.

- Andy Matuschak
Open Sword Group
http://www.opensword.org
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.


  • Prev by Date: Re: When do bound objects get updated?
  • Next by Date: Re: Determining if a method is a class or instance method
  • Previous by thread: WebView Background Color ?
  • Next by thread: creating an NSTableColumn
  • Index(es):
    • Date
    • Thread