2009-01-09 19:18:31 +00:00
|
|
|
/* Copyright (c) 2009 Christopher J. W. Lloyd
|
2009-01-07 19:18:45 +00:00
|
|
|
|
2020-05-11 15:52:05 +00:00
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
|
|
this software and associated documentation files (the "Software"), to deal in
|
|
|
|
the Software without restriction, including without limitation the rights to
|
|
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
|
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
|
|
subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
|
|
copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
|
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
|
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
2009-01-07 19:18:45 +00:00
|
|
|
|
2010-03-31 14:36:14 +00:00
|
|
|
#import <Onyx2D/O2ColorSpace.h>
|
2020-05-11 15:52:05 +00:00
|
|
|
#import <Onyx2D/O2DataProvider.h>
|
2010-03-31 14:36:14 +00:00
|
|
|
#import <Onyx2D/O2Image.h>
|
2020-05-11 15:52:05 +00:00
|
|
|
#import <Onyx2D/O2ImageSource_GIF.h>
|
2009-01-07 19:18:45 +00:00
|
|
|
|
2020-05-11 15:52:05 +00:00
|
|
|
static int readCallback(GifFileType *ft, GifByteType *buf, int count) {
|
|
|
|
NSInputStream *stream = (NSInputStream *) ft->UserData;
|
|
|
|
return [stream read: buf maxLength: count];
|
2020-02-18 08:51:17 +00:00
|
|
|
}
|
|
|
|
|
2009-11-18 04:48:58 +00:00
|
|
|
@implementation O2ImageSource_GIF
|
2009-01-07 19:18:45 +00:00
|
|
|
|
2020-05-11 15:52:05 +00:00
|
|
|
+ (BOOL) isPresentInDataProvider: (O2DataProvider *) provider {
|
|
|
|
enum { signatureLength = 4 };
|
|
|
|
unsigned char signature[signatureLength] = {'G', 'I', 'F', '8'};
|
|
|
|
unsigned char check[signatureLength];
|
|
|
|
NSInteger i, size = [provider getBytes: check
|
|
|
|
range: NSMakeRange(0, signatureLength)];
|
|
|
|
|
|
|
|
if (size != signatureLength)
|
|
|
|
return NO;
|
|
|
|
|
|
|
|
for (i = 0; i < signatureLength; i++)
|
|
|
|
if (signature[i] != check[i])
|
|
|
|
return NO;
|
|
|
|
|
|
|
|
return YES;
|
2009-01-07 19:18:45 +00:00
|
|
|
}
|
|
|
|
|
2020-05-11 15:52:05 +00:00
|
|
|
- initWithDataProvider: (O2DataProvider *) provider
|
2020-05-12 00:04:26 +00:00
|
|
|
options: (NSDictionary *) options
|
|
|
|
{
|
2020-05-11 15:52:05 +00:00
|
|
|
[super initWithDataProvider: provider options: options];
|
|
|
|
|
|
|
|
NSInputStream *stream = [_provider inputStream];
|
|
|
|
if ((_gif = DGifOpen(stream, readCallback, NULL)) == NULL) {
|
|
|
|
[self dealloc];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
DGifSlurp(_gif);
|
|
|
|
return self;
|
2009-01-07 19:18:45 +00:00
|
|
|
}
|
|
|
|
|
2020-05-11 15:52:05 +00:00
|
|
|
- (void) dealloc {
|
|
|
|
if (_gif != NULL)
|
|
|
|
DGifCloseFile(_gif, NULL);
|
2009-01-09 19:18:31 +00:00
|
|
|
|
2020-05-11 15:52:05 +00:00
|
|
|
[super dealloc];
|
2009-01-07 19:18:45 +00:00
|
|
|
}
|
|
|
|
|
2020-05-11 15:52:05 +00:00
|
|
|
- (CFStringRef) type {
|
|
|
|
return (CFStringRef) @"com.compuserve.gif";
|
2014-02-17 17:46:37 +00:00
|
|
|
}
|
|
|
|
|
2020-05-11 15:52:05 +00:00
|
|
|
- (NSUInteger) count {
|
|
|
|
return _gif->ImageCount;
|
2009-01-07 19:18:45 +00:00
|
|
|
}
|
|
|
|
|
2020-05-11 15:52:05 +00:00
|
|
|
- (CFDictionaryRef) copyPropertiesAtIndex: (NSUInteger) index
|
2020-05-12 00:04:26 +00:00
|
|
|
options: (CFDictionaryRef) options
|
|
|
|
{
|
2020-05-11 15:52:05 +00:00
|
|
|
return (CFDictionaryRef)[@{
|
|
|
|
kO2ImagePropertyDPIHeight : @(72),
|
|
|
|
kO2ImagePropertyDPIHeight : @(72)
|
|
|
|
} retain];
|
2009-01-07 19:18:45 +00:00
|
|
|
}
|
|
|
|
|
2020-05-11 15:52:05 +00:00
|
|
|
- (O2Image *) createImageAtIndex: (NSUInteger) index
|
2020-05-12 00:04:26 +00:00
|
|
|
options: (NSDictionary *) options
|
|
|
|
{
|
2020-05-11 15:52:05 +00:00
|
|
|
|
|
|
|
if (index >= _gif->ImageCount)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
SavedImage *gifImage = _gif->SavedImages + index;
|
|
|
|
ColorMapObject *colorMap = _gif->SColorMap;
|
|
|
|
if (colorMap == NULL)
|
|
|
|
colorMap = gifImage->ImageDesc.ColorMap;
|
|
|
|
int bgColorIndex = (_gif->SColorMap == NULL) ? -1 : _gif->SBackGroundColor;
|
|
|
|
int colorCount = colorMap->ColorCount;
|
|
|
|
GifColorType *colorLUT = colorMap->Colors;
|
|
|
|
unsigned char *gifRaster = gifImage->RasterBits;
|
|
|
|
O2ColorSpaceRef colorSpace = O2ColorSpaceCreateDeviceRGB();
|
|
|
|
size_t width = gifImage->ImageDesc.Width;
|
|
|
|
size_t height = gifImage->ImageDesc.Height;
|
|
|
|
size_t bytesPerRow = width * sizeof(O2rgba8u_BE);
|
|
|
|
size_t bitsPerComponent = 8;
|
|
|
|
size_t bitsPerPixel = 32;
|
|
|
|
O2rgba8u_BE *pixels = NSZoneMalloc(NULL, bytesPerRow * height);
|
|
|
|
O2rgba8u_BE *scanline = pixels;
|
|
|
|
BOOL interlace = gifImage->ImageDesc.Interlace;
|
|
|
|
int interlacePass = 1;
|
|
|
|
int interlaceDelta = 8;
|
|
|
|
NSData *bitmap = [[NSData alloc] initWithBytesNoCopy: pixels
|
|
|
|
length: bytesPerRow * height];
|
|
|
|
|
|
|
|
GraphicsControlBlock gcb;
|
|
|
|
gcb.TransparentColor = NO_TRANSPARENT_COLOR;
|
|
|
|
DGifSavedExtensionToGCB(_gif, index, &gcb);
|
|
|
|
|
|
|
|
size_t r, c;
|
|
|
|
|
|
|
|
for (r = 0; r < height; r++) {
|
|
|
|
|
|
|
|
for (c = 0; c < width; c++) {
|
|
|
|
unsigned char colorIndex = *gifRaster;
|
|
|
|
|
|
|
|
if (colorIndex == bgColorIndex) {
|
|
|
|
scanline->a = 0;
|
|
|
|
scanline->r = 0;
|
|
|
|
scanline->g = 0;
|
|
|
|
scanline->b = 0;
|
|
|
|
} else if (colorIndex < colorCount) {
|
|
|
|
GifColorType *gifColor = colorLUT + colorIndex;
|
|
|
|
|
|
|
|
scanline->a = 255;
|
|
|
|
if (gcb.TransparentColor != NO_TRANSPARENT_COLOR &&
|
|
|
|
gcb.TransparentColor == colorIndex)
|
|
|
|
scanline->a = 0;
|
|
|
|
|
|
|
|
scanline->r = gifColor->Red;
|
|
|
|
scanline->g = gifColor->Green;
|
|
|
|
scanline->b = gifColor->Blue;
|
|
|
|
} else {
|
|
|
|
scanline->a = 0;
|
|
|
|
scanline->r = 0;
|
|
|
|
scanline->g = 0;
|
|
|
|
scanline->b = 0;
|
|
|
|
}
|
|
|
|
scanline++;
|
|
|
|
gifRaster++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (interlace) {
|
|
|
|
scanline += (interlaceDelta - 1) * width;
|
|
|
|
if (scanline >= pixels + width * height) {
|
|
|
|
interlacePass++;
|
|
|
|
|
|
|
|
switch (interlacePass) {
|
|
|
|
case 2:
|
|
|
|
scanline = pixels + width * 4;
|
|
|
|
interlaceDelta = 8;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
scanline = pixels + width * 2;
|
|
|
|
interlaceDelta = 4;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
scanline = pixels + width * 1;
|
|
|
|
interlaceDelta = 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-01-09 19:18:31 +00:00
|
|
|
}
|
2020-05-11 15:52:05 +00:00
|
|
|
|
|
|
|
O2DataProvider *provider =
|
2020-05-12 18:51:39 +00:00
|
|
|
O2DataProviderCreateWithCFData((CFDataRef) bitmap);
|
2020-05-11 15:52:05 +00:00
|
|
|
O2BitmapInfo info =
|
2020-05-12 18:51:39 +00:00
|
|
|
kO2BitmapByteOrder32Big | kO2ImageAlphaPremultipliedLast;
|
2020-05-11 15:52:05 +00:00
|
|
|
|
|
|
|
O2Image *result =
|
2020-05-12 18:51:39 +00:00
|
|
|
[[O2Image alloc] initWithWidth: width
|
|
|
|
height: height
|
|
|
|
bitsPerComponent: bitsPerComponent
|
|
|
|
bitsPerPixel: bitsPerPixel
|
|
|
|
bytesPerRow: bytesPerRow
|
|
|
|
colorSpace: colorSpace
|
|
|
|
bitmapInfo: info
|
|
|
|
decoder: NULL
|
|
|
|
provider: provider
|
|
|
|
decode: NULL
|
|
|
|
interpolate: NO
|
|
|
|
renderingIntent: kO2RenderingIntentDefault];
|
2020-05-11 15:52:05 +00:00
|
|
|
|
|
|
|
[colorSpace release];
|
|
|
|
[provider release];
|
|
|
|
[bitmap release];
|
|
|
|
|
|
|
|
return result;
|
2009-01-07 19:18:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|