diff --git a/ChangeLog b/ChangeLog index d20e8ebc5..f62a67961 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2004-01-08 19:41 Alexander Malmberg + + * configure.ac: Add a check for libungif. + * configure, Headers/Additions/GNUstepGUI/config.h.in: Regenerate. + + * Source/GNUmakefile: Add NSBitmapImageRep+GIF.m. + + * Source/NSBitmapImageRep+GIF.m, Source/NSBitmapImageRep+GIF.h: + New files with the GIF image loader. + + * Source/NSBitmapImageRep.m (+canInitWithData:) Check if the GIF + loader can handle the data. + (+imageUnfilteredFileTypes): Add ".gif". + (+imageRepsWithData:, -initWithData:): Use the GIF loader to load + GIF images. + 2004-01-08 Fred Kiefer * Source/NSActionCell.m (-initWithCoder:) Don't decode the control diff --git a/Headers/Additions/GNUstepGUI/config.h.in b/Headers/Additions/GNUstepGUI/config.h.in index e627fa3d2..49200f81c 100644 --- a/Headers/Additions/GNUstepGUI/config.h.in +++ b/Headers/Additions/GNUstepGUI/config.h.in @@ -24,6 +24,9 @@ /* Define to 1 if you have the `tiff' library (-ltiff). */ #undef HAVE_LIBTIFF +/* Define to 1 if you have the `ungif' library (-lungif). */ +#undef HAVE_LIBUNGIF + /* Define to 1 if you have the `z' library (-lz). */ #undef HAVE_LIBZ diff --git a/Source/NSBitmapImageRep+GIF.h b/Source/NSBitmapImageRep+GIF.h new file mode 100644 index 000000000..2d17ad2ab --- /dev/null +++ b/Source/NSBitmapImageRep+GIF.h @@ -0,0 +1,42 @@ +/* NSBitmapImageRep+GIF.h + + Functionality for reading GIF images + + Copyright (C) 2003 Free Software Foundation, Inc. + + Written by: Stefan Kleine Stegemann + Date: Nov 2003 + + This file is part of the GNUstep GUI Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _NSBitmapImageRep_GIF_H_include +#define _NSBitmapImageRep_GIF_H_include + +#include "AppKit/NSBitmapImageRep.h" + +@interface NSBitmapImageRep (GIFReading) + ++ (BOOL) _bitmapIsGIF: (NSData *)imageData; +- (id) _initBitmapFromGIF: (NSData *)imageData + errorMessage: (NSString **)errorMsg; + +@end + +#endif // _NSBitmapImageRep_GIF_H_include + diff --git a/Source/NSBitmapImageRep+GIF.m b/Source/NSBitmapImageRep+GIF.m new file mode 100644 index 000000000..3758eeaee --- /dev/null +++ b/Source/NSBitmapImageRep+GIF.m @@ -0,0 +1,348 @@ +/* NSBitmapImageRep+GIF.m + + Methods for reading GIF images + + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + + Written by: Stefan Kleine Stegemann + Date: Nov 2003 + + This file is part of the GNUstep GUI Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "NSBitmapImageRep+GIF.h" + +#if HAVE_LIBUNGIF + +#include + +#include +#include +#include +#include "AppKit/NSGraphics.h" + + +/* ----------------------------------------------------------- + The following types and functions are for interacting with + the gif library. + ----------------------------------------------------------- */ + +/* settings for reading interlaced images */ +static int InterlaceOffset[] = { 0, 4, 2, 1 }; +static int InterlaceJumps[] = { 8, 8, 4, 2 }; + +/* Holds the information for the input function. */ +typedef struct gs_gif_input_src +{ + const void *data; + unsigned length; + unsigned pos; +} gs_gif_input_src; + +/* Provides data for the gif library. */ +static int gs_gif_input(GifFileType *file, GifByteType *buffer, int len) +{ + /* according the the libungif sources, this functions has + to act like fread. */ + int bytesRead; + gs_gif_input_src *src = (gs_gif_input_src *)file->UserData; + + if (src->pos < src->length) + { + if ((src->pos + len) > src->length) + { + bytesRead = (src->length - src->pos); + } + else + { + bytesRead = len; + } + + /* We have to copy the data here, looking at + the libungif source makes this clear. */ + memcpy(buffer, src->data + src->pos, bytesRead); + src->pos = src->pos + bytesRead; + } + else + { + bytesRead = 0; + } + + return bytesRead; +} + + +/* Initialze a new input source to be used with + gs_gif_input. The passed structure has to be + allocated outside this function. */ +static void gs_gif_init_input_source(gs_gif_input_src *src, NSData *data) +{ + src->data = [data bytes]; + src->length = [data length]; + src->pos = 0; +} + + +/* ----------------------------------------------------------- + The gif loading part of NSBitmapImageRep + ----------------------------------------------------------- */ + +@implementation NSBitmapImageRep (GIFReading) + +/* Return YES if this looks like a GIF. */ ++ (BOOL) _bitmapIsGIF: (NSData *)imageData +{ + struct gs_gif_input_src src; + GifFileType* file; + + if (!imageData || ![imageData length]) + { + return NO; + } + + gs_gif_init_input_source(&src, imageData); + file = DGifOpen(&src, gs_gif_input); + if (file == NULL) + { + /* we do not use giferror here because it doesn't + seem to be thread-safe (the error code is a global + variable, so we might get the wrong error here. */ + return NO; + } + + DGifCloseFile(file); + return YES; +} + + +#define SET_ERROR_MSG(msg) \ + if (errorMsg != NULL) \ + {\ + *errorMsg = msg; \ + }\ + NSLog(msg); + +#define GIF_CREATE_ERROR(msg) \ + SET_ERROR_MSG(msg); \ + if (file != NULL) \ + {\ + DGifCloseFile(file); \ + }\ + if (imgBuffer != NULL) \ + {\ + NSZoneFree([self zone], imgBuffer); \ + }\ + RELEASE(self); \ + return nil; + +#define CALL_CHECKED(f, where) \ + gifrc = f; \ + if (gifrc != GIF_OK) \ + {\ + NSString* msg = [NSString stringWithFormat: @"reading gif failed (%@)", \ + where]; \ + GIF_CREATE_ERROR(msg);\ + } + +/* Read a gif image. Assume it is from a gif file. */ +- (id) _initBitmapFromGIF: (NSData *)imageData + errorMessage: (NSString **)errorMsg +{ + struct gs_gif_input_src src; + GifFileType *file = NULL; + GifRecordType recordType; + GifByteType *extension; + GifPixelType *imgBuffer = NULL; + GifPixelType *imgBufferPos; /* a position inside imgBuffer */ + unsigned char *rgbBuffer; /* image convertet to rgb */ + unsigned rgbBufferPos; + unsigned rgbBufferSize; + ColorMapObject *colorMap; + GifColorType *color; + unsigned pixelSize, rowSize; + int extCode; + int gifrc; /* required by CALL_CHECKED */ + int i, j; /* counters */ + int imgHeight = 0, imgWidth = 0, imgRow = 0, imgCol = 0; + + /* open the image */ + gs_gif_init_input_source(&src, imageData); + file = DGifOpen(&src, gs_gif_input); + if (file == NULL) + { + /* we do not use giferror here because it doesn't + seem to be thread-safe (the error code is a global + variable, so we might get the wrong error here. */ + GIF_CREATE_ERROR(@"unable to open gif from data"); + /* Not reached. */ + } + + + /* allocate a buffer for the decoded image */ + pixelSize = sizeof(GifPixelType); + rowSize = file->SWidth * pixelSize; + imgBuffer = NSZoneMalloc([self zone], file->SHeight * rowSize); + if (imgBuffer == NULL) + { + GIF_CREATE_ERROR(@"could not allocate input buffer"); + /* Not reached. */ + } + + + /* set the background color */ + memset(imgBuffer, file->SBackGroundColor, file->SHeight * rowSize); + + + /* read the image */ + do + { + CALL_CHECKED(DGifGetRecordType(file, &recordType), @"GetRecordType"); + switch (recordType) + { + case IMAGE_DESC_RECORD_TYPE: + { + CALL_CHECKED(DGifGetImageDesc(file), @"GetImageDesc"); + + imgWidth = file->Image.Width; + imgHeight = file->Image.Height; + imgRow = file->Image.Top; + imgCol = file->Image.Left; + + if ((file->Image.Left + file->Image.Width > file->SWidth) + || (file->Image.Top + file->Image.Height > file->SHeight)) + { + GIF_CREATE_ERROR(@"image does not fit into screen dimensions"); + } + + if (file->Image.Interlace) + { + for (i = 0; i < 4; i++) + { + for (j = imgRow + InterlaceOffset[i]; j < imgRow + imgHeight; + j = j + InterlaceJumps[i]) + { + imgBufferPos = + imgBuffer + (j * rowSize) + (imgCol * pixelSize); + CALL_CHECKED(DGifGetLine(file, imgBufferPos, imgWidth), + @"GetLine(Interlaced)"); + } + } + } + else + { + for (i = 0; i < imgHeight; i++) + { + imgBufferPos = + imgBuffer + ((imgRow++) * rowSize) + (imgCol * pixelSize); + CALL_CHECKED(DGifGetLine(file, imgBufferPos, imgWidth), + @"GetLine(Non-Interlaced)"); + } + } + + break; + } + + case EXTENSION_RECORD_TYPE: + { + /* ignore extensions */ + CALL_CHECKED(DGifGetExtension(file, &extCode, &extension), @"GetExtension"); + while (extension != NULL) + { + CALL_CHECKED(DGifGetExtensionNext(file, &extension), @"GetExtensionNext"); + } + break; + } + + case TERMINATE_RECORD_TYPE: + default: + { + break; + } + } + } while (recordType != TERMINATE_RECORD_TYPE); + + + /* convert the image to rgb */ + rgbBufferSize = file->SHeight * (file->SWidth * sizeof(unsigned char) * 3); + rgbBuffer = NSZoneMalloc([self zone], rgbBufferSize); + if (rgbBuffer == NULL) + { + GIF_CREATE_ERROR(@"could not allocate image buffer"); + /* Not reached. */ + } + + colorMap = (file->Image.ColorMap ? file->Image.ColorMap : file->SColorMap); + rgbBufferPos = 0; + + for (i = 0; i < file->SHeight; i++) + { + imgBufferPos = imgBuffer + (i * rowSize); + for (j = 0; j < file->SWidth; j++) + { + color = &colorMap->Colors[*(imgBufferPos + (j * pixelSize))]; + rgbBuffer[rgbBufferPos++] = color->Red; + rgbBuffer[rgbBufferPos++] = color->Green; + rgbBuffer[rgbBufferPos++] = color->Blue; + } + } + + NSZoneFree([self zone], imgBuffer); + + + /* initialize self */ + [self initWithBitmapDataPlanes: &rgbBuffer + pixelsWide: file->SWidth + pixelsHigh: file->SHeight + bitsPerSample: 8 + samplesPerPixel: 3 + hasAlpha: NO + isPlanar: NO + colorSpaceName: NSCalibratedRGBColorSpace + bytesPerRow: file->SWidth * 3 + bitsPerPixel: 8 * 3]; + + _imageData = [[NSData alloc] initWithBytesNoCopy: rgbBuffer + length: rgbBufferSize]; + + + /* don't forget to close the gif */ + DGifCloseFile(file); + + return self; +} + +@end + +#else /* !HAVE_LIBUNGIF */ + +@implementation NSBitmapImageRep (GIFReading) ++ (BOOL) _bitmapIsGIF: (NSData *)imageData +{ + return NO; +} +- (id) _initBitmapFromGIF: (NSData *)imageData + errorMessage: (NSString **)errorMsg +{ + RELEASE(self); + return nil; +} +@end + +#endif /* !HAVE_LIBUNGIF */ + diff --git a/Source/NSBitmapImageRep.m b/Source/NSBitmapImageRep.m index c5ff3d517..c5136b023 100644 --- a/Source/NSBitmapImageRep.m +++ b/Source/NSBitmapImageRep.m @@ -32,6 +32,7 @@ #include "AppKit/NSBitmapImageRep.h" +#include "NSBitmapImageRep+GIF.h" #include "NSBitmapImageRep+JPEG.h" #include "NSBitmapImageRep+PNG.h" #include "NSBitmapImageRep+PNM.h" @@ -108,6 +109,9 @@ static BOOL supports_lzw_compression = NO; if ([self _bitmapIsJPEG: data]) return YES; + if ([self _bitmapIsGIF: data]) + return YES; + image = NSTiffOpenDataRead ((char *)[data bytes], [data length]); if (image != NULL) @@ -134,6 +138,9 @@ static BOOL supports_lzw_compression = NO; types = [[NSMutableArray alloc] initWithObjects: @"tiff", @"tif", @"pnm", @"ppm", +#if HAVE_LIBUNGIF + @"gif", +#endif #if HAVE_LIBJPEG @"jpeg", @"jpg", #endif @@ -235,6 +242,20 @@ static BOOL supports_lzw_compression = NO; return a; } + if ([self _bitmapIsGIF: imageData]) + { + NSBitmapImageRep *rep; + NSArray *a; + + rep=[[self alloc] _initBitmapFromGIF: imageData + errorMessage: NULL]; + if (!rep) + return [NSArray array]; + a = [NSArray arrayWithObject: rep]; + DESTROY(rep); + return a; + } + image = NSTiffOpenDataRead((char *)[imageData bytes], [imageData length]); if (image == NULL) { @@ -282,6 +303,10 @@ static BOOL supports_lzw_compression = NO; return [self _initBitmapFromJPEG: imageData errorMessage: NULL]; + if ([isa _bitmapIsGIF: imageData]) + return [self _initBitmapFromGIF: imageData + errorMessage: NULL]; + image = NSTiffOpenDataRead((char *)[imageData bytes], [imageData length]); if (image == NULL) diff --git a/configure b/configure index 06fdbcb57..3035783b6 100755 --- a/configure +++ b/configure @@ -3589,6 +3589,72 @@ echo "$as_me: error: libpng not found." >&2;} fi fi + +echo "$as_me:$LINENO: checking for DGifOpen in -lungif" >&5 +echo $ECHO_N "checking for DGifOpen in -lungif... $ECHO_C" >&6 +if test "${ac_cv_lib_ungif_DGifOpen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lungif $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char DGifOpen (); +int +main () +{ +DGifOpen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ungif_DGifOpen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_ungif_DGifOpen=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_ungif_DGifOpen" >&5 +echo "${ECHO_T}$ac_cv_lib_ungif_DGifOpen" >&6 +if test $ac_cv_lib_ungif_DGifOpen = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBUNGIF 1 +_ACEOF + + LIBS="-lungif $LIBS" + +fi + + + #-------------------------------------------------------------------- # NSSound #-------------------------------------------------------------------- diff --git a/configure.ac b/configure.ac index fbb2d051c..7734cf8b0 100644 --- a/configure.ac +++ b/configure.ac @@ -191,6 +191,9 @@ if test $enable_png = yes; then fi fi +AC_CHECK_LIB(ungif, DGifOpen) + + #-------------------------------------------------------------------- # NSSound #--------------------------------------------------------------------