From 0ceaff2b65c50b75ad8cc5b2d12e7b3f864092e5 Mon Sep 17 00:00:00 2001 From: Marcin Bukat Date: Fri, 2 Nov 2012 13:03:58 +0100 Subject: imageviewer: gif viewer based on giflib-5.0.2 This adds ability to view gif images in rockbox. Works both on color and gray/monochrome targets (greylib). Aspect correction is supported as well. Limitations: - animated gifs are restricted to 32 frames - animated gifs loop always (loopcount is ignored) - plain text extension is not supported - animated gifs with interframe delay = 0 are treated as still images (web browsers usually treat delay 0 as 100ms to prevent exhaustive CPU load by such images) Change-Id: I61501f801ddcd403410e38d83e6bddc9883e7ede --- apps/plugins/CATEGORIES | 1 + apps/plugins/imageviewer/SUBDIRS | 1 + apps/plugins/imageviewer/bmp/bmp.c | 3 +- apps/plugins/imageviewer/gif/SOURCES | 6 + apps/plugins/imageviewer/gif/dgif_lib.c | 1169 ++++++++++++++++++++++++ apps/plugins/imageviewer/gif/gif.c | 241 +++++ apps/plugins/imageviewer/gif/gif.make | 30 + apps/plugins/imageviewer/gif/gif_decoder.c | 491 ++++++++++ apps/plugins/imageviewer/gif/gif_decoder.h | 37 + apps/plugins/imageviewer/gif/gif_err.c | 99 ++ apps/plugins/imageviewer/gif/gif_hash.h | 39 + apps/plugins/imageviewer/gif/gif_lib.h | 308 +++++++ apps/plugins/imageviewer/gif/gif_lib_private.h | 59 ++ apps/plugins/imageviewer/gif/gifalloc.c | 405 ++++++++ apps/plugins/imageviewer/gif/rb_glue.h | 44 + apps/plugins/imageviewer/image_decoder.c | 6 +- apps/plugins/imageviewer/image_decoder.h | 1 + apps/plugins/imageviewer/imageviewer.c | 64 +- apps/plugins/imageviewer/imageviewer.h | 7 +- apps/plugins/imageviewer/jpeg/jpeg.c | 3 +- apps/plugins/imageviewer/png/png.c | 3 +- apps/plugins/imageviewer/ppm/ppm.c | 3 +- apps/plugins/viewers.config | 1 + 23 files changed, 3006 insertions(+), 15 deletions(-) create mode 100644 apps/plugins/imageviewer/gif/SOURCES create mode 100644 apps/plugins/imageviewer/gif/dgif_lib.c create mode 100644 apps/plugins/imageviewer/gif/gif.c create mode 100644 apps/plugins/imageviewer/gif/gif.make create mode 100644 apps/plugins/imageviewer/gif/gif_decoder.c create mode 100644 apps/plugins/imageviewer/gif/gif_decoder.h create mode 100644 apps/plugins/imageviewer/gif/gif_err.c create mode 100644 apps/plugins/imageviewer/gif/gif_hash.h create mode 100644 apps/plugins/imageviewer/gif/gif_lib.h create mode 100644 apps/plugins/imageviewer/gif/gif_lib_private.h create mode 100644 apps/plugins/imageviewer/gif/gifalloc.c create mode 100644 apps/plugins/imageviewer/gif/rb_glue.h diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES index c7f194fa94..54a316e84b 100644 --- a/apps/plugins/CATEGORIES +++ b/apps/plugins/CATEGORIES @@ -70,6 +70,7 @@ pictureflow,demos pitch_detector,apps plasma,demos png,viewers +gif,viewers pong,games ppm,viewers properties,viewers diff --git a/apps/plugins/imageviewer/SUBDIRS b/apps/plugins/imageviewer/SUBDIRS index 0f8d953939..1f7b4d8b52 100644 --- a/apps/plugins/imageviewer/SUBDIRS +++ b/apps/plugins/imageviewer/SUBDIRS @@ -4,3 +4,4 @@ png #ifdef HAVE_LCD_COLOR ppm #endif +gif diff --git a/apps/plugins/imageviewer/bmp/bmp.c b/apps/plugins/imageviewer/bmp/bmp.c index b7efbb7e2c..f7e55dbe62 100644 --- a/apps/plugins/imageviewer/bmp/bmp.c +++ b/apps/plugins/imageviewer/bmp/bmp.c @@ -230,8 +230,9 @@ static int load_image(char *filename, struct image_info *info, return PLUGIN_OK; } -static int get_image(struct image_info *info, int ds) +static int get_image(struct image_info *info, int frame, int ds) { + (void)frame; struct t_disp* p_disp = &disp[ds]; /* short cut */ info->width = bmp.width/ds; diff --git a/apps/plugins/imageviewer/gif/SOURCES b/apps/plugins/imageviewer/gif/SOURCES new file mode 100644 index 0000000000..032b65c73d --- /dev/null +++ b/apps/plugins/imageviewer/gif/SOURCES @@ -0,0 +1,6 @@ +../../../../lib/tlsf/src/tlsf.c +dgif_lib.c +gifalloc.c +gif_decoder.c +gif.c +gif_err.c diff --git a/apps/plugins/imageviewer/gif/dgif_lib.c b/apps/plugins/imageviewer/gif/dgif_lib.c new file mode 100644 index 0000000000..6dc52991a5 --- /dev/null +++ b/apps/plugins/imageviewer/gif/dgif_lib.c @@ -0,0 +1,1169 @@ +/****************************************************************************** + +dgif_lib.c - GIF decoding + +The functions here and in egif_lib.c are partitioned carefully so that +if you only require one of read and write capability, only one of these +two modules will be linked. Preserve this property! + +*****************************************************************************/ + +#include +#include +#include +#include +/* #include */ +/* #include */ +#include + +#ifdef _WIN32 +#include +#endif /* _WIN32 */ + +#include "gif_lib.h" +#include "gif_lib_private.h" + +/* compose unsigned little endian value */ +#define UNSIGNED_LITTLE_ENDIAN(lo, hi) ((lo) | ((hi) << 8)) + +/* avoid extra function call in case we use fread (TVT) */ +#define READ(_gif,_buf,_len) \ + (((GifFilePrivateType*)_gif->Private)->Read ? \ + ((GifFilePrivateType*)_gif->Private)->Read(_gif,_buf,_len) : \ + fread(_buf,1,_len,((GifFilePrivateType*)_gif->Private)->File)) + +static int DGifGetWord(GifFileType *GifFile, GifWord *Word); +static int DGifSetupDecompress(GifFileType *GifFile); +static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, + int LineLen); +static int DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode); +static int DGifDecompressInput(GifFileType *GifFile, int *Code); +static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, + GifByteType *NextByte); + +/****************************************************************************** + Open a new GIF file for read, given by its name. + Returns dynamically allocated GifFileType pointer which serves as the GIF + info record. +******************************************************************************/ +GifFileType * +DGifOpenFileName(const char *FileName, int *Error) +{ + int FileHandle; + GifFileType *GifFile; + + if ((FileHandle = rb->open(FileName, O_RDONLY)) == -1) { + if (Error != NULL) + *Error = D_GIF_ERR_OPEN_FAILED; + return NULL; + } + + GifFile = DGifOpenFileHandle(FileHandle, Error); + // cppcheck-suppress resourceLeak + return GifFile; +} + +/****************************************************************************** + Update a new GIF file, given its file handle. + Returns dynamically allocated GifFileType pointer which serves as the GIF + info record. +******************************************************************************/ +GifFileType * +DGifOpenFileHandle(int FileHandle, int *Error) +{ + char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + int f; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + (void)rb->close(FileHandle); + return NULL; + } + + /*@i1@*/memset(GifFile, '\0', sizeof(GifFileType)); + + /* Belt and suspenders, in case the null pointer isn't zero */ + GifFile->SavedImages = NULL; + GifFile->SColorMap = NULL; + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (Private == NULL) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + (void)rb->close(FileHandle); + free((char *)GifFile); + return NULL; + } +#ifdef _WIN32 + _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ +#endif /* _WIN32 */ + + f = fdopen(FileHandle, "rb"); /* Make it into a stream: */ + + /*@-mustfreeonly@*/ + GifFile->Private = (void *)Private; + Private->FileHandle = FileHandle; + Private->File = f; + Private->FileState = FILE_STATE_READ; + Private->Read = NULL; /* don't use alternate input method (TVT) */ + GifFile->UserData = NULL; /* TVT */ + /*@=mustfreeonly@*/ + + /* Let's see if this is a GIF file: */ + if (READ(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) { + if (Error != NULL) + *Error = D_GIF_ERR_READ_FAILED; + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + /* Check for GIF prefix at start of file */ + Buf[GIF_STAMP_LEN] = 0; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_GIF_FILE; + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + GifFile->Error = 0; + + /* What version of GIF? */ + Private->gif89 = (Buf[GIF_VERSION_POS] == '9'); + + return GifFile; +} + +/****************************************************************************** + GifFileType constructor with user supplied input function (TVT) +******************************************************************************/ +GifFileType * +DGifOpen(void *userData, InputFunc readFunc, int *Error) +{ + char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + /* Belt and suspenders, in case the null pointer isn't zero */ + GifFile->SavedImages = NULL; + GifFile->SColorMap = NULL; + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (!Private) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + free((char *)GifFile); + return NULL; + } + + GifFile->Private = (void *)Private; + Private->FileHandle = 0; + Private->File = 0; /* NULL */ + Private->FileState = FILE_STATE_READ; + + Private->Read = readFunc; /* TVT */ + GifFile->UserData = userData; /* TVT */ + + /* Lets see if this is a GIF file: */ + if (READ(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) { + if (Error != NULL) + *Error = D_GIF_ERR_READ_FAILED; + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + /* Check for GIF prefix at start of file */ + Buf[GIF_STAMP_LEN] = '\0'; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_GIF_FILE; + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + GifFile->Error = 0; + + /* What version of GIF? */ + Private->gif89 = (Buf[GIF_VERSION_POS] == '9'); + + return GifFile; +} + +/****************************************************************************** + This routine should be called before any other DGif calls. Note that + this routine is called automatically from DGif file open routines. +******************************************************************************/ +int +DGifGetScreenDesc(GifFileType *GifFile) +{ + int BitsPerPixel; + bool SortFlag; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + /* Put the screen descriptor into the file: */ + if (DGifGetWord(GifFile, &GifFile->SWidth) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR) + return GIF_ERROR; + + if (READ(GifFile, Buf, 3) != 3) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + return GIF_ERROR; + } + GifFile->SColorResolution = (((Buf[0] & 0x70) + 1) >> 4) + 1; + SortFlag = (Buf[0] & 0x08) != 0; + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->SBackGroundColor = Buf[1]; + GifFile->AspectByte = Buf[2]; + if (Buf[0] & 0x80) { /* Do we have global color map? */ + int i; + + GifFile->SColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->SColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the global color map: */ + GifFile->SColorMap->SortFlag = SortFlag; + for (i = 0; i < GifFile->SColorMap->ColorCount; i++) { + if (READ(GifFile, Buf, 3) != 3) { + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + GifFile->SColorMap->Colors[i].Red = Buf[0]; + GifFile->SColorMap->Colors[i].Green = Buf[1]; + GifFile->SColorMap->Colors[i].Blue = Buf[2]; + } + } else { + GifFile->SColorMap = NULL; + } + + return GIF_OK; +} + +/****************************************************************************** + This routine should be called before any attempt to read an image. +******************************************************************************/ +int +DGifGetRecordType(GifFileType *GifFile, GifRecordType* Type) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (READ(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + switch (Buf) { + case DESCRIPTOR_INTRODUCER: + *Type = IMAGE_DESC_RECORD_TYPE; + break; + case EXTENSION_INTRODUCER: + *Type = EXTENSION_RECORD_TYPE; + break; + default: + *Type = TERMINATE_RECORD_TYPE; + break; + } + + return GIF_OK; +} + +/****************************************************************************** + This routine should be called before any attempt to read an image. + Note it is assumed the Image desc. header has been read. +******************************************************************************/ +int +DGifGetImageDesc(GifFileType *GifFile) +{ + unsigned int BitsPerPixel; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; +/* SavedImage *sp; */ + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifGetWord(GifFile, &GifFile->Image.Left) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Top) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Width) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR) + return GIF_ERROR; + if (READ(GifFile, Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + return GIF_ERROR; + } + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->Image.Interlace = (Buf[0] & 0x40) ? true : false; + + /* Setup the colormap */ + if (GifFile->Image.ColorMap) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + /* Does this image have local color map? */ + if (Buf[0] & 0x80) { + int i; + + GifFile->Image.ColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->Image.ColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the image local color map: */ + for (i = 0; i < GifFile->Image.ColorMap->ColorCount; i++) { + if (READ(GifFile, Buf, 3) != 3) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFile->Image.ColorMap = NULL; + return GIF_ERROR; + } + GifFile->Image.ColorMap->Colors[i].Red = Buf[0]; + GifFile->Image.ColorMap->Colors[i].Green = Buf[1]; + GifFile->Image.ColorMap->Colors[i].Blue = Buf[2]; + } + } + +#if 0 + if (GifFile->SavedImages) { + if ((GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages, + sizeof(SavedImage) * + (GifFile->ImageCount + 1))) == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } else { + if ((GifFile->SavedImages = + (SavedImage *) malloc(sizeof(SavedImage))) == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + + sp = &GifFile->SavedImages[GifFile->ImageCount]; + memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc)); + if (GifFile->Image.ColorMap != NULL) { + sp->ImageDesc.ColorMap = GifMakeMapObject( + GifFile->Image.ColorMap->ColorCount, + GifFile->Image.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + sp->RasterBits = (unsigned char *)NULL; + sp->ExtensionBlockCount = 0; + sp->ExtensionBlocks = (ExtensionBlock *) NULL; + + GifFile->ImageCount++; +#endif + Private->PixelCount = (long)GifFile->Image.Width * + (long)GifFile->Image.Height; + + /* Reset decompress algorithm parameters. */ + (void)DGifSetupDecompress(GifFile); + + return GIF_OK; +} + +/****************************************************************************** + Get one full scanned line (Line) of length LineLen from GIF file. +******************************************************************************/ +int +DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) +{ + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (!LineLen) + LineLen = GifFile->Image.Width; + + if ((Private->PixelCount -= LineLen) > 0xffff0000UL) { + GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + + if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably won't be called any more, so let's clean up + * everything before we return: need to flush out all the + * rest of image until an empty block (size 0) + * detected. We use GetCodeNext. + */ + do + if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR) + return GIF_ERROR; + while (Dummy != NULL) ; + } + return GIF_OK; + } else + return GIF_ERROR; +} + +/****************************************************************************** + Put one pixel (Pixel) into GIF file. +******************************************************************************/ +int +DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel) +{ + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + if (--Private->PixelCount > 0xffff0000UL) + { + GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + + if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably won't be called any more, so let's clean up + * everything before we return: need to flush out all the + * rest of image until an empty block (size 0) + * detected. We use GetCodeNext. + */ + do + if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR) + return GIF_ERROR; + while (Dummy != NULL) ; + } + return GIF_OK; + } else + return GIF_ERROR; +} + +/****************************************************************************** + Get an extension block (see GIF manual) from GIF file. This routine only + returns the first data block, and DGifGetExtensionNext should be called + after this one until NULL extension is returned. + The Extension should NOT be freed by the user (not dynamically allocated). + Note it is assumed the Extension description header has been read. +******************************************************************************/ +int +DGifGetExtension(GifFileType *GifFile, int *ExtCode, GifByteType **Extension) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (READ(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *ExtCode = Buf; + + return DGifGetExtensionNext(GifFile, Extension); +} + +/****************************************************************************** + Get a following extension block (see GIF manual) from GIF file. This + routine should be called until NULL Extension is returned. + The Extension should NOT be freed by the user (not dynamically allocated). +******************************************************************************/ +int +DGifGetExtensionNext(GifFileType *GifFile, GifByteType ** Extension) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (READ(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + if (Buf > 0) { + *Extension = Private->Buf; /* Use private unused buffer. */ + (*Extension)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */ + /* coverity[tainted_data] */ + if (READ(GifFile, &((*Extension)[1]), Buf) != Buf) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else + *Extension = NULL; + + return GIF_OK; +} + +/****************************************************************************** + Extract a Graphics Control Block from raw extension data +******************************************************************************/ + +int DGifExtensionToGCB(const size_t GifExtensionLength, + const GifByteType *GifExtension, + GraphicsControlBlock *GCB) +{ + if (GifExtensionLength != 4) { + return GIF_ERROR; + } + + GCB->DisposalMode = (GifExtension[0] >> 2) & 0x07; + GCB->UserInputFlag = (GifExtension[0] & 0x02) != 0; + GCB->DelayTime = UNSIGNED_LITTLE_ENDIAN(GifExtension[1], GifExtension[2]); + if (GifExtension[0] & 0x01) + GCB->TransparentColor = (int)GifExtension[3]; + else + GCB->TransparentColor = NO_TRANSPARENT_COLOR; + + return GIF_OK; +} + +/****************************************************************************** + Extract the Graphics Control Block for a saved image, if it exists. +******************************************************************************/ + +int DGifSavedExtensionToGCB(GifFileType *GifFile, + int ImageIndex, GraphicsControlBlock *GCB) +{ + int i; + + if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) + return GIF_ERROR; + + GCB->DisposalMode = DISPOSAL_UNSPECIFIED; + GCB->UserInputFlag = false; + GCB->DelayTime = 0; + GCB->TransparentColor = NO_TRANSPARENT_COLOR; + + for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) { + ExtensionBlock *ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; + if (ep->Function == GRAPHICS_EXT_FUNC_CODE) + return DGifExtensionToGCB(ep->ByteCount, ep->Bytes, GCB); + } + + return GIF_ERROR; +} + +/****************************************************************************** + This routine should be called last, to close the GIF file. +******************************************************************************/ +int +DGifCloseFile(GifFileType *GifFile) +{ + GifFilePrivateType *Private; + + if (GifFile == NULL || GifFile->Private == NULL) + return GIF_ERROR; + + if (GifFile->Image.ColorMap) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + + if (GifFile->Image.GCB) { + free(GifFile->Image.GCB); + GifFile->Image.GCB = NULL; + } + + if (GifFile->SColorMap) { + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + } + + if (GifFile->SavedImages) { + GifFreeSavedImages(GifFile); + GifFile->SavedImages = NULL; + } + + GifFreeExtensions(&GifFile->ExtensionBlockCount, &GifFile->ExtensionBlocks); + + Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (Private->File && (fclose(Private->File) != 0)) { + GifFile->Error = D_GIF_ERR_CLOSE_FAILED; + return GIF_ERROR; + } + + free((char *)GifFile->Private); + + /* + * Without the #ifndef, we get spurious warnings because Coverity mistakenly + * thinks the GIF structure is freed on an error return. + */ +#ifndef __COVERITY__ + free(GifFile); +#endif /* __COVERITY__ */ + + return GIF_OK; +} + +/****************************************************************************** + Get 2 bytes (word) from the given file: +******************************************************************************/ +static int +DGifGetWord(GifFileType *GifFile, GifWord *Word) +{ + unsigned char c[2]; + + if (READ(GifFile, c, 2) != 2) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + *Word = (GifWord)UNSIGNED_LITTLE_ENDIAN(c[0], c[1]); + return GIF_OK; +} + +/****************************************************************************** + Get the image code in compressed form. This routine can be called if the + information needed to be piped out as is. Obviously this is much faster + than decoding and encoding again. This routine should be followed by calls + to DGifGetCodeNext, until NULL block is returned. + The block should NOT be freed by the user (not dynamically allocated). +******************************************************************************/ +int +DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock) +{ + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + *CodeSize = Private->BitsPerPixel; + + return DGifGetCodeNext(GifFile, CodeBlock); +} + +/****************************************************************************** + Continue to get the image code in compressed form. This routine should be + called until NULL block is returned. + The block should NOT be freed by the user (not dynamically allocated). +******************************************************************************/ +int +DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + /* coverity[tainted_data_argument] */ + if (READ(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + /* coverity[lower_bounds] */ + if (Buf > 0) { + *CodeBlock = Private->Buf; /* Use private unused buffer. */ + (*CodeBlock)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */ + /* coverity[tainted_data] */ + if (READ(GifFile, &((*CodeBlock)[1]), Buf) != Buf) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else { + *CodeBlock = NULL; + Private->Buf[0] = 0; /* Make sure the buffer is empty! */ + Private->PixelCount = 0; /* And local info. indicate image read. */ + } + + return GIF_OK; +} + +/****************************************************************************** + Setup the LZ decompression for this image: +******************************************************************************/ +static int +DGifSetupDecompress(GifFileType *GifFile) +{ + int i, BitsPerPixel; + GifByteType CodeSize; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + READ(GifFile, &CodeSize, 1); /* Read Code size from file. */ + BitsPerPixel = CodeSize; + + Private->Buf[0] = 0; /* Input Buffer empty. */ + Private->BitsPerPixel = BitsPerPixel; + Private->ClearCode = (1 << BitsPerPixel); + Private->EOFCode = Private->ClearCode + 1; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */ + Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */ + Private->StackPtr = 0; /* No pixels on the pixel stack. */ + Private->LastCode = NO_SUCH_CODE; + Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ + Private->CrntShiftDWord = 0; + + Prefix = Private->Prefix; + for (i = 0; i <= LZ_MAX_CODE; i++) + Prefix[i] = NO_SUCH_CODE; + + return GIF_OK; +} + +/****************************************************************************** + The LZ decompression routine: + This version decompress the given GIF file into Line of length LineLen. + This routine can be called few times (one per scan line, for example), in + order the complete the whole image. +******************************************************************************/ +static int +DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) +{ + int i = 0; + int j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr; + GifByteType *Stack, *Suffix; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + StackPtr = Private->StackPtr; + Prefix = Private->Prefix; + Suffix = Private->Suffix; + Stack = Private->Stack; + EOFCode = Private->EOFCode; + ClearCode = Private->ClearCode; + LastCode = Private->LastCode; + + if (StackPtr > LZ_MAX_CODE) { + return GIF_ERROR; + } + + if (StackPtr != 0) { + /* Let pop the stack off before continueing to read the GIF file: */ + while (StackPtr != 0 && i < LineLen) + Line[i++] = Stack[--StackPtr]; + } + + while (i < LineLen) { /* Decode LineLen items. */ + if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR) + return GIF_ERROR; + + if (CrntCode == EOFCode) { + /* Note however that usually we will not be here as we will stop + * decoding as soon as we got all the pixel, or EOF code will + * not be read at all, and DGifGetLine/Pixel clean everything. */ + GifFile->Error = D_GIF_ERR_EOF_TOO_SOON; + return GIF_ERROR; + } else if (CrntCode == ClearCode) { + /* We need to start over again: */ + for (j = 0; j <= LZ_MAX_CODE; j++) + Prefix[j] = NO_SUCH_CODE; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + LastCode = Private->LastCode = NO_SUCH_CODE; + } else { + /* Its regular code - if in pixel range simply add it to output + * stream, otherwise trace to codes linked list until the prefix + * is in pixel range: */ + if (CrntCode < ClearCode) { + /* This is simple - its pixel scalar, so add it to output: */ + Line[i++] = CrntCode; + } else { + /* Its a code to needed to be traced: trace the linked list + * until the prefix is a pixel, while pushing the suffix + * pixels on our stack. If we done, pop the stack in reverse + * (thats what stack is good for!) order to output. */ + if (Prefix[CrntCode] == NO_SUCH_CODE) { + /* Only allowed if CrntCode is exactly the running code: + * In that case CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! */ + if (CrntCode == Private->RunningCode - 2) { + CrntPrefix = LastCode; + Suffix[Private->RunningCode - 2] = + Stack[StackPtr++] = DGifGetPrefixChar(Prefix, + LastCode, + ClearCode); + } else { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + } else + CrntPrefix = CrntCode; + + /* Now (if image is O.K.) we should not get a NO_SUCH_CODE + * during the trace. As we might loop forever, in case of + * defective image, we use StackPtr as loop counter and stop + * before overflowing Stack[]. */ + while (StackPtr < LZ_MAX_CODE && + CrntPrefix > ClearCode && CrntPrefix <= LZ_MAX_CODE) { + Stack[StackPtr++] = Suffix[CrntPrefix]; + CrntPrefix = Prefix[CrntPrefix]; + } + if (StackPtr >= LZ_MAX_CODE || CrntPrefix > LZ_MAX_CODE) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + /* Push the last character on stack: */ + Stack[StackPtr++] = CrntPrefix; + + /* Now lets pop all the stack into output: */ + while (StackPtr != 0 && i < LineLen) + Line[i++] = Stack[--StackPtr]; + } + if (LastCode != NO_SUCH_CODE) { + Prefix[Private->RunningCode - 2] = LastCode; + + if (CrntCode == Private->RunningCode - 2) { + /* Only allowed if CrntCode is exactly the running code: + * In that case CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! */ + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, LastCode, ClearCode); + } else { + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, CrntCode, ClearCode); + } + } + LastCode = CrntCode; + } + } + + Private->LastCode = LastCode; + Private->StackPtr = StackPtr; + + return GIF_OK; +} + +/****************************************************************************** + Routine to trace the Prefixes linked list until we get a prefix which is + not code, but a pixel value (less than ClearCode). Returns that pixel value. + If image is defective, we might loop here forever, so we limit the loops to + the maximum possible if image O.k. - LZ_MAX_CODE times. +******************************************************************************/ +static int +DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode) +{ + int i = 0; + + while (Code > ClearCode && i++ <= LZ_MAX_CODE) { + if (Code > LZ_MAX_CODE) { + return NO_SUCH_CODE; + } + Code = Prefix[Code]; + } + return Code; +} + +/****************************************************************************** + Interface for accessing the LZ codes directly. Set Code to the real code + (12bits), or to -1 if EOF code is returned. +******************************************************************************/ +int +DGifGetLZCodes(GifFileType *GifFile, int *Code) +{ + GifByteType *CodeBlock; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifDecompressInput(GifFile, Code) == GIF_ERROR) + return GIF_ERROR; + + if (*Code == Private->EOFCode) { + /* Skip rest of codes (hopefully only NULL terminating block): */ + do { + if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) + return GIF_ERROR; + } while (CodeBlock != NULL) ; + + *Code = -1; + } else if (*Code == Private->ClearCode) { + /* We need to start over again: */ + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + } + + return GIF_OK; +} + +/****************************************************************************** + The LZ decompression input routine: + This routine is responsable for the decompression of the bit stream from + 8 bits (bytes) packets, into the real codes. + Returns GIF_OK if read successfully. +******************************************************************************/ +static int +DGifDecompressInput(GifFileType *GifFile, int *Code) +{ + static const unsigned short CodeMasks[] = { + 0x0000, 0x0001, 0x0003, 0x0007, + 0x000f, 0x001f, 0x003f, 0x007f, + 0x00ff, 0x01ff, 0x03ff, 0x07ff, + 0x0fff + }; + + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + GifByteType NextByte; + + /* The image can't contain more than LZ_BITS per code. */ + if (Private->RunningBits > LZ_BITS) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + + while (Private->CrntShiftState < Private->RunningBits) { + /* Needs to get more bytes from input stream for next code: */ + if (DGifBufferedInput(GifFile, Private->Buf, &NextByte) == GIF_ERROR) { + return GIF_ERROR; + } + Private->CrntShiftDWord |= + ((unsigned long)NextByte) << Private->CrntShiftState; + Private->CrntShiftState += 8; + } + *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits]; + + Private->CrntShiftDWord >>= Private->RunningBits; + Private->CrntShiftState -= Private->RunningBits; + + /* If code cannot fit into RunningBits bits, must raise its size. Note + * however that codes above 4095 are used for special signaling. + * If we're using LZ_BITS bits already and we're at the max code, just + * keep using the table as it is, don't increment Private->RunningCode. + */ + if (Private->RunningCode < LZ_MAX_CODE + 2 && + ++Private->RunningCode > Private->MaxCode1 && + Private->RunningBits < LZ_BITS) { + Private->MaxCode1 <<= 1; + Private->RunningBits++; + } + return GIF_OK; +} + +/****************************************************************************** + This routines read one GIF data block at a time and buffers it internally + so that the decompression routine could access it. + The routine returns the next byte from its internal buffer (or read next + block in if buffer empty) and returns GIF_OK if succesful. +******************************************************************************/ +static int +DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, GifByteType *NextByte) +{ + if (Buf[0] == 0) { + /* Needs to read the next buffer - this one is empty: */ + if (READ(GifFile, Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + /* There shouldn't be any empty data blocks here as the LZW spec + * says the LZW termination code should come first. Therefore we + * shouldn't be inside this routine at that point. + */ + if (Buf[0] == 0) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + /* There shouldn't be any empty data blocks here as the LZW spec + * says the LZW termination code should come first. Therefore we + * shouldn't be inside this routine at that point. + */ + if (Buf[0] == 0) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + if (READ(GifFile, &Buf[1], Buf[0]) != Buf[0]) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *NextByte = Buf[1]; + Buf[1] = 2; /* We use now the second place as last char read! */ + Buf[0]--; + } else { + *NextByte = Buf[Buf[1]++]; + Buf[0]--; + } + + return GIF_OK; +} + +#if 0 +/****************************************************************************** + This routine reads an entire GIF into core, hanging all its state info off + the GifFileType pointer. Call DGifOpenFileName() or DGifOpenFileHandle() + first to initialize I/O. Its inverse is EGifSpew(). +*******************************************************************************/ +int +DGifSlurp(GifFileType *GifFile) +{ + size_t ImageSize; + GifRecordType RecordType; + SavedImage *sp; + GifByteType *ExtData; + int ExtFunction; + + GifFile->ExtensionBlocks = NULL; + GifFile->ExtensionBlockCount = 0; + + do { + if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) + return (GIF_ERROR); + + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFile) == GIF_ERROR) + return (GIF_ERROR); + + sp = &GifFile->SavedImages[GifFile->ImageCount - 1]; + /* Allocate memory for the image */ + if (sp->ImageDesc.Width < 0 && sp->ImageDesc.Height < 0 && + sp->ImageDesc.Width > (INT_MAX / sp->ImageDesc.Height)) { + return GIF_ERROR; + } + ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height; + + if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) { + return GIF_ERROR; + } + sp->RasterBits = (unsigned char *)malloc(ImageSize * + sizeof(GifPixelType)); + + if (sp->RasterBits == NULL) { + return GIF_ERROR; + } + + if (sp->ImageDesc.Interlace) { + int i, j; + /* + * The way an interlaced image should be read - + * offsets and jumps... + */ + int InterlacedOffset[] = { 0, 4, 2, 1 }; + int InterlacedJumps[] = { 8, 8, 4, 2 }; + /* Need to perform 4 passes on the image */ + for (i = 0; i < 4; i++) + for (j = InterlacedOffset[i]; + j < sp->ImageDesc.Height; + j += InterlacedJumps[i]) { + if (DGifGetLine(GifFile, + sp->RasterBits+j*sp->ImageDesc.Width, + sp->ImageDesc.Width) == GIF_ERROR) + return GIF_ERROR; + } + } + else { + if (DGifGetLine(GifFile,sp->RasterBits,ImageSize)==GIF_ERROR) + return (GIF_ERROR); + } + + if (GifFile->ExtensionBlocks) { + sp->ExtensionBlocks = GifFile->ExtensionBlocks; + sp->ExtensionBlockCount = GifFile->ExtensionBlockCount; + + GifFile->ExtensionBlocks = NULL; + GifFile->ExtensionBlockCount = 0; + } + break; + + case EXTENSION_RECORD_TYPE: + if (DGifGetExtension(GifFile,&ExtFunction,&ExtData) == GIF_ERROR) + return (GIF_ERROR); + /* Create an extension block with our data */ + if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount, + &GifFile->ExtensionBlocks, + ExtFunction, ExtData[0], &ExtData[1]) + == GIF_ERROR) + return (GIF_ERROR); + while (ExtData != NULL) { + if (DGifGetExtensionNext(GifFile, &ExtData) == GIF_ERROR) + return (GIF_ERROR); + /* Continue the extension block */ + if (ExtData != NULL) + if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount, + &GifFile->ExtensionBlocks, + CONTINUE_EXT_FUNC_CODE, + ExtData[0], &ExtData[1]) == GIF_ERROR) + return (GIF_ERROR); + } + break; + + case TERMINATE_RECORD_TYPE: + break; + + default: /* Should be trapped by DGifGetRecordType */ + break; + } + } while (RecordType != TERMINATE_RECORD_TYPE); + + return (GIF_OK); +} +#endif +/* end */ diff --git a/apps/plugins/imageviewer/gif/gif.c b/apps/plugins/imageviewer/gif/gif.c new file mode 100644 index 0000000000..672735af94 --- /dev/null +++ b/apps/plugins/imageviewer/gif/gif.c @@ -0,0 +1,241 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (c) 2012 Marcin Bukat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "plugin.h" +#include "lcd.h" +#include +#include "../imageviewer.h" +#include "bmp.h" +#include "gif_decoder.h" +#include "gif_lib.h" + +/* decoder context struct */ +static struct gif_decoder decoder; + +static char print[32]; /* use a common snprintf() buffer */ + +/* decompressed image in the possible sizes (1,2,4,8), wasting the other */ +/* max 32 frames */ +static unsigned char *disp[GIF_MAX_FRAMES][9]; +static unsigned char *disp_buf; + +#if defined(HAVE_LCD_COLOR) +#define resize_bitmap smooth_resize_bitmap +#else +#define resize_bitmap grey_resize_bitmap +#endif + +#if defined(USEGSLIB) && (CONFIG_PLATFORM & PLATFORM_HOSTED) +/* hack: fix error "undefined reference to `_grey_info'". */ +GREY_INFO_STRUCT +#endif /* USEGSLIB */ + +static void draw_image_rect(struct image_info *info, + int x, int y, int width, int height) +{ + unsigned char **pdisp = (unsigned char **)info->data; + +#ifdef HAVE_LCD_COLOR + rb->lcd_bitmap_part((fb_data *)*pdisp, info->x + x, info->y + y, + STRIDE(SCREEN_MAIN, info->width, info->height), + x + MAX(0, (LCD_WIDTH-info->width)/2), + y + MAX(0, (LCD_HEIGHT-info->height)/2), + width, height); +#else + mylcd_ub_gray_bitmap_part(*pdisp, + info->x + x, info->y + y, info->width, + x + MAX(0, (LCD_WIDTH-info->width)/2), + y + MAX(0, (LCD_HEIGHT-info->height)/2), + width, height); +#endif +} + +static int img_mem(int ds) +{ + struct gif_decoder *p_decoder = &decoder; + return p_decoder->native_img_size/ds; +} + +static int load_image(char *filename, struct image_info *info, + unsigned char *buf, ssize_t *buf_size) +{ + int w, h; + long time = 0; /* measured ticks */ + struct gif_decoder *p_decoder = &decoder; + + unsigned char *memory, *memory_max; + size_t memory_size; + + /* cleanup */ + memset(&disp, 0, sizeof(disp)); + + /* align buffer */ + memory = (unsigned char *)((intptr_t)(buf + 3) & ~3); + memory_max = (unsigned char *)((intptr_t)(memory + *buf_size) & ~3); + memory_size = memory_max - memory; + +#ifdef DISK_SPINDOWN + if (iv->running_slideshow && iv->immediate_ata_off) { + /* running slideshow and time is long enough: power down disk */ + rb->storage_sleep(); + } +#endif + + /* initialize decoder context struct, set buffer decoder is free + * to use. + */ + gif_decoder_init(p_decoder, memory, memory_size); + + /* populate internal data from gif file control structs */ + gif_open(filename, p_decoder); + + if (!p_decoder->error) + { + + if (!iv->running_slideshow) + { + rb->lcd_putsf(0, 2, "image %dx%d", + p_decoder->width, + p_decoder->height); + rb->lcd_putsf(0, 3, "decoding %d*%d", + p_decoder->width, + p_decoder->height); + rb->lcd_update(); + } + + /* the actual decoding */ + time = *rb->current_tick; + +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(true); +#endif + gif_decode(p_decoder, iv->cb_progress); + +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(false); +#endif + time = *rb->current_tick - time; + } + + if (!iv->running_slideshow && !p_decoder->error) + { + rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ); + rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */ + rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print); + rb->lcd_update(); + } + + if (p_decoder->error) + { + rb->splashf(HZ, "%s", GifErrorString(p_decoder->error)); + return PLUGIN_ERROR; + } + + info->x_size = p_decoder->width; + info->y_size = p_decoder->height; + info->frames_count = p_decoder->frames_count; + info->delay = p_decoder->delay; + + //p_decoder->native_img_size = (p_decoder->native_img_size + 3) & ~3; + disp_buf = p_decoder->mem + + ((p_decoder->native_img_size*p_decoder->frames_count + 3) & ~3); + + *buf_size = memory_max - disp_buf; + + return PLUGIN_OK; +} + +static int get_image(struct image_info *info, int frame, int ds) +{ + unsigned char **p_disp = &disp[frame][ds]; /* short cut */ + struct gif_decoder *p_decoder = &decoder; + + info->width = p_decoder->width / ds; + info->height = p_decoder->height / ds; + info->data = p_disp; + + if (*p_disp != NULL) + { + /* we still have it */ + return PLUGIN_OK; + } + + /* assign image buffer */ + if (ds > 1) + { + if (!iv->running_slideshow) + { + rb->lcd_putsf(0, 3, "resizing %d*%d", info->width, info->height); + rb->lcd_update(); + } + struct bitmap bmp_src, bmp_dst; + + /* size of the scalled image */ + int size = img_mem(ds); + + if (disp_buf + size >= p_decoder->mem + p_decoder->mem_size) + { + /* have to discard the current */ + int i; + for (i=1; i<=8; i++) + disp[frame][i] = NULL; /* invalidate all bitmaps */ + + /* start again from the beginning of the buffer */ + disp_buf = p_decoder->mem + + p_decoder->native_img_size*p_decoder->frames_count; + } + + *p_disp = disp_buf; + disp_buf += size; + + bmp_src.width = p_decoder->width; + bmp_src.height = p_decoder->height; + bmp_src.data = p_decoder->mem + p_decoder->native_img_size*frame; + + bmp_dst.width = info->width; + bmp_dst.height = info->height; + bmp_dst.data = *p_disp; + +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(true); +#endif + resize_bitmap(&bmp_src, &bmp_dst); + +#ifdef HAVE_ADJUSTABLE_CPU_FREQ + rb->cpu_boost(false); +#endif + } + else + { + *p_disp = p_decoder->mem + p_decoder->native_img_size*frame; + } + + return PLUGIN_OK; +} + +const struct image_decoder image_decoder = { + true, + img_mem, + load_image, + get_image, + draw_image_rect, +}; + +IMGDEC_HEADER diff --git a/apps/plugins/imageviewer/gif/gif.make b/apps/plugins/imageviewer/gif/gif.make new file mode 100644 index 0000000000..8260524ffe --- /dev/null +++ b/apps/plugins/imageviewer/gif/gif.make @@ -0,0 +1,30 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id$ +# + +GIFSRCDIR := $(IMGVSRCDIR)/gif +GIFBUILDDIR := $(IMGVBUILDDIR)/gif + +GIF_SRC := $(call preprocess, $(GIFSRCDIR)/SOURCES) +GIF_OBJ := $(call c2obj, $(GIF_SRC)) + +OTHER_SRC += $(GIF_SRC) + +ROCKS += $(GIFBUILDDIR)/gif.ovl + +$(GIFBUILDDIR)/gif.refmap: $(GIF_OBJ) +$(GIFBUILDDIR)/gif.link: $(PLUGIN_LDS) $(GIFBUILDDIR)/gif.refmap +$(GIFBUILDDIR)/gif.ovl: $(GIF_OBJ) + +#-Os breaks decoder - dunno why +GIFFLAGS = $(IMGDECFLAGS) -O0 + +# Compile PNG plugin with extra flags (adapted from ZXBox) +$(GIFBUILDDIR)/%.o: $(GIFSRCDIR)/%.c $(GIFSRCDIR)/gif.make + $(SILENT)mkdir -p $(dir $@) + $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(GIFFLAGS) -c $< -o $@ diff --git a/apps/plugins/imageviewer/gif/gif_decoder.c b/apps/plugins/imageviewer/gif/gif_decoder.c new file mode 100644 index 0000000000..ca9579aae3 --- /dev/null +++ b/apps/plugins/imageviewer/gif/gif_decoder.c @@ -0,0 +1,491 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (c) 2012 Marcin Bukat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include "bmp.h" +#if LCD_DEPTH < 8 +#include +#endif + +#include "gif_lib.h" +#include "gif_decoder.h" + +#ifndef resize_bitmap +#if defined(HAVE_LCD_COLOR) +#define resize_bitmap smooth_resize_bitmap +#else +#define resize_bitmap grey_resize_bitmap +#endif +#endif + +#if defined(HAVE_LCD_COLOR) +typedef struct uint8_rgb pixel_t; +#define NATIVE_SZ (GifFile->SWidth*GifFile->SHeight*FB_DATA_SZ) +#define PIXEL_TRANSPARENT 0x00 +#else +typedef unsigned char pixel_t; +#define NATIVE_SZ (GifFile->SWidth*GifFile->SHeight) +#define PIXEL_TRANSPARENT 0xff +#endif + +#define PIXELS_SZ (GifFile->SWidth*GifFile->SHeight*sizeof(pixel_t)) + +static GifFileType *GifFile; + +static void gif2pixels(GifPixelType *Line, pixel_t *out, + int Row, int Col, int Width) +{ + int x; +#ifndef HAVE_LCD_COLOR + struct uint8_rgb rgb; +#endif + + GifColorType *ColorMapEntry; + + /* Color map to use */ + ColorMapObject *ColorMap = (GifFile->Image.ColorMap ? + GifFile->Image.ColorMap : + GifFile->SColorMap); + + pixel_t *pixel = out + ((Row * GifFile->SWidth) + Col); + + for (x = 0; x < Width; x++, pixel++) + { + ColorMapEntry = &ColorMap->Colors[Line[x]]; + + if (GifFile->Image.GCB && + GifFile->Image.GCB->TransparentColor == Line[x]) + continue; + +#ifdef HAVE_LCD_COLOR + pixel->red = ColorMapEntry->Red; + pixel->green = ColorMapEntry->Green; + pixel->blue = ColorMapEntry->Blue; +#else + rgb.red = ColorMapEntry->Red; + rgb.green = ColorMapEntry->Green; + rgb.blue = ColorMapEntry->Blue; + + *pixel = brightness(rgb); +#endif + } +} + +static void pixels2native(struct scaler_context *ctx, + pixel_t *pixels_buffer, + int Row) +{ +#ifdef HAVE_LCD_COLOR + const struct custom_format *cformat = &format_native; +#else + const struct custom_format *cformat = &format_grey; +#endif + + void (*output_row_8)(uint32_t, void*, struct scaler_context*) = + cformat->output_row_8; + + output_row_8(Row, (void *)(pixels_buffer + (Row*ctx->bm->width)), ctx); +} + +void gif_decoder_init(struct gif_decoder *d, void *mem, size_t size) +{ + memset(d, 0, sizeof(struct gif_decoder)); + + d->mem = mem; + d->mem_size = size; + + /* mem allocator init */ + init_memory_pool(d->mem_size, d->mem); +} + +void gif_open(char *filename, struct gif_decoder *d) +{ + if ((GifFile = DGifOpenFileName(filename, &d->error)) == NULL) + return; + + d->width = GifFile->SWidth; + d->height = GifFile->SHeight; + d->frames_count = 0; +} + +static void set_canvas_background(GifPixelType *Line, pixel_t *out, + GifFileType *GifFile) +{ + int i; + + /* Reading Gif spec it seems one should always use background color + * in canvas but most real files omit this and sets background color to 0 + * (which IS valid index). We can choose to either conform to standard + * (and wrongly display most of gifs with transparency) or stick to + * common practise and treat background color 0 as transparent. + * I preffer the second. + */ + if (GifFile->SColorMap && GifFile->SBackGroundColor != 0) + { + memset(Line, GifFile->SBackGroundColor, GifFile->SWidth); + + for(i=0; iSHeight; i++) + gif2pixels(Line, out, i, 0, GifFile->SWidth); + } + else + { + memset(out, PIXEL_TRANSPARENT, PIXELS_SZ); + } + +} + +/* var names adhere to giflib coding style */ +void gif_decode(struct gif_decoder *d, + void (*pf_progress)(int current, int total)) +{ + int i, j; + + int Size; + int Row; + int Col; + int Width; + int Height; + int ExtCode; + + GifPixelType *Line; + + GifRecordType RecordType; + GifByteType *Extension; + + unsigned char *out = NULL; + + /* The way Interlaced image should + * be read - offsets and jumps + */ + const char InterlacedOffset[] = { 0, 4, 2, 1 }; + const char InterlacedJumps[] = { 8, 8, 4, 2 }; + + /* used for color conversion */ + struct bitmap bm; + struct scaler_context ctx = { + .bm = &bm, + .dither = 0 + }; + + /* initialize struct */ + memset(&bm, 0, sizeof(struct bitmap)); + + Size = GifFile->SWidth * sizeof(GifPixelType); /* Size in bytes one row.*/ + Line = (GifPixelType *)malloc(Size); + if (Line == NULL) + { + /* error allocating temp space */ + d->error = D_GIF_ERR_NOT_ENOUGH_MEM; + return; + } + + /* We use two pixel buffers if dispose method asks + * for restoration of the previous state. + * We only swap the indexes leaving data in place. + */ + int buf_idx = 0; + pixel_t *pixels_buffer[2]; + pixels_buffer[0] = (pixel_t *)malloc(PIXELS_SZ); + pixels_buffer[1] = NULL; + + if (pixels_buffer[0] == NULL) + { + d->error = D_GIF_ERR_NOT_ENOUGH_MEM; + return; + } + + /* Global background color */ + set_canvas_background(Line, pixels_buffer[0], GifFile); + + bm.width = GifFile->SWidth; + bm.height = GifFile->SHeight; + d->native_img_size = NATIVE_SZ; + + if (pf_progress != NULL) + pf_progress(0, 100); + + /* Scan the content of the GIF file and load the image(s) in: */ + do + { + if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) + { + d->error = GifFile->Error; + return; + } + + switch (RecordType) + { + case IMAGE_DESC_RECORD_TYPE: + + if (DGifGetImageDesc(GifFile) == GIF_ERROR) + { + d->error = GifFile->Error; + return; + } + + /* Image Position relative to canvas */ + Row = GifFile->Image.Top; + Col = GifFile->Image.Left; + Width = GifFile->Image.Width; + Height = GifFile->Image.Height; + + /* Check Color map to use */ + if (GifFile->Image.ColorMap == NULL && + GifFile->SColorMap == NULL) + { + d->error = D_GIF_ERR_NO_COLOR_MAP; + return; + } + + /* sanity check */ + if (GifFile->Image.Left+GifFile->Image.Width>GifFile->SWidth || + GifFile->Image.Top+GifFile->Image.Height>GifFile->SHeight) + { + d->error = D_GIF_ERR_DATA_TOO_BIG; + return; + } + + if (GifFile->Image.GCB && + GifFile->Image.GCB->DisposalMode == DISPOSE_PREVIOUS) + { + /* We need to take a snapshot before processing the image + * in order to restore canvas to previous state after + * rendering + */ + buf_idx ^= 1; + + if (pixels_buffer[buf_idx] == NULL) + pixels_buffer[buf_idx] = (pixel_t *)malloc(PIXELS_SZ); + } + + if (GifFile->Image.Interlace) + { + /* Need to perform 4 passes on the image */ + for (i = 0; i < 4; i++) + { + for (j = Row + InterlacedOffset[i]; + j < Row + Height; + j += InterlacedJumps[i]) + { + if (DGifGetLine(GifFile, Line, Width) == GIF_ERROR) + { + d->error = GifFile->Error; + return; + } + + gif2pixels(Line, pixels_buffer[buf_idx], + Row + j, Col, Width); + } + + pf_progress(25*(i+1), 100); + } + } + else + { + for (i = 0; i < Height; i++) + { + /* load single line into buffer */ + if (DGifGetLine(GifFile, Line, Width) == GIF_ERROR) + { + d->error = GifFile->Error; + return; + } + + gif2pixels(Line, pixels_buffer[buf_idx], + Row + i, Col, Width); + + pf_progress(100*(i+1)/Height, 100); + } + } + + /* allocate space for new frame */ + out = realloc(out, d->native_img_size*(d->frames_count + 1)); + if (out == NULL) + { + d->error = D_GIF_ERR_NOT_ENOUGH_MEM; + return; + } + + bm.data = out + d->native_img_size*d->frames_count; + + /* animated gif */ + if (GifFile->Image.GCB && GifFile->Image.GCB->DelayTime != 0) + { + for (i=0; i < ctx.bm->height; i++) + pixels2native(&ctx, (void *)pixels_buffer[buf_idx], i); + + /* restore to the background color */ + switch (GifFile->Image.GCB->DisposalMode) + { + case DISPOSE_BACKGROUND: + set_canvas_background(Line, pixels_buffer[buf_idx], + GifFile); + break; + + case DISPOSE_PREVIOUS: + buf_idx ^= 1; + break; + + default: + /* DISPOSAL_UNSPECIFIED + * DISPOSE_DO_NOT + */ + break; + } + + d->frames_count++; + + if (d->frames_count > GIF_MAX_FRAMES) + { + d->error = D_GIF_ERR_NOT_ENOUGH_MEM; + return; + } + } + + break; + + case EXTENSION_RECORD_TYPE: + if (DGifGetExtension(GifFile, &ExtCode, &Extension) == + GIF_ERROR) + { + d->error = GifFile->Error; + return; + } + + if (ExtCode == GRAPHICS_EXT_FUNC_CODE) + { + if (GifFile->Image.GCB == NULL) + GifFile->Image.GCB = (GraphicsControlBlock *) + malloc(sizeof(GraphicsControlBlock)); + + if (DGifExtensionToGCB(Extension[0], + Extension + 1, + GifFile->Image.GCB) == GIF_ERROR) + { + d->error = GifFile->Error; + return; + } + d->delay = GifFile->Image.GCB->DelayTime; + } + + /* Skip anything else */ + while (Extension != NULL) + { + if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) + { + d->error = GifFile->Error; + return; + } + } + break; + + /* including TERMINATE_RECORD_TYPE */ + default: + break; + } + + } while (RecordType != TERMINATE_RECORD_TYPE); + + /* free all internal allocated data */ + if (DGifCloseFile(GifFile) == GIF_ERROR) + { + d->error = GifFile->Error; + return; + } + + /* not animated gif */ + if (d->frames_count == 0) + { + for (i=0; i < ctx.bm->height; i++) + pixels2native(&ctx, (void *)pixels_buffer[buf_idx], i); + + d->frames_count++; + } + + free(pixels_buffer[0]); + if (pixels_buffer[1]) + free(pixels_buffer[1]); + + free(Line); + + /* WARNING !!!! */ + /* GifFile object is trashed from now on, DONT use it */ + /* Move bitmap in native format to the front of the buff */ + memmove(d->mem, out, d->frames_count*d->native_img_size); + + /* correct aspect ratio */ +#if (LCD_PIXEL_ASPECT_HEIGHT != 1 || LCD_PIXEL_ASPECT_WIDTH != 1) + struct bitmap img_src, img_dst; /* scaler vars */ + struct dim dim_src, dim_dst; /* recalc_dimensions vars */ + size_t c_native_img_size; /* size of the image after correction */ + + dim_src.width = bm.width; + dim_src.height = bm.height; + + dim_dst.width = bm.width; + dim_dst.height = bm.height; + + /* defined in apps/recorder/resize.c */ + if (!recalc_dimension(&dim_dst, &dim_src)) + { + /* calculate 'corrected' image size */ +#ifdef HAVE_LCD_COLOR + c_native_img_size = dim_dst.width * dim_dst.height * FB_DATA_SZ; +#else + c_native_img_size = dim_dst.width * dim_dst.height; +#endif + + /* check memory constraints + * do the correction only if there is enough + * free memory + */ + if (d->native_img_size*d->frames_count + c_native_img_size <= + d->mem_size) + { + img_dst.width = dim_dst.width; + img_dst.height = dim_dst.height; + img_dst.data = (unsigned char *)d->mem + + d->native_img_size*d->frames_count; + + for (i = 0; i < d->frames_count; i++) + { + img_src.width = dim_src.width; + img_src.height = dim_src.height; + img_src.data = (unsigned char *)d->mem + i*d->native_img_size; + + /* scale the bitmap to correct physical + * pixel dimentions + */ + resize_bitmap(&img_src, &img_dst); + + /* copy back corrected image */ + memmove(d->mem + i*c_native_img_size, + img_dst.data, + c_native_img_size); + } + + /* update decoder struct */ + d->width = img_dst.width; + d->height = img_dst.height; + d->native_img_size = c_native_img_size; + } + } +#endif +} diff --git a/apps/plugins/imageviewer/gif/gif_decoder.h b/apps/plugins/imageviewer/gif/gif_decoder.h new file mode 100644 index 0000000000..444f54acf9 --- /dev/null +++ b/apps/plugins/imageviewer/gif/gif_decoder.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (c) 2012 Marcin Bukat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#define GIF_MAX_FRAMES 32 + +struct gif_decoder { + unsigned char *mem; + size_t mem_size; + int width; + int height; + int frames_count; + int delay; + size_t native_img_size; + int error; +}; + +void gif_decoder_init(struct gif_decoder *decoder, void *mem, size_t size); +void gif_open(char *filename, struct gif_decoder *d); +void gif_decode(struct gif_decoder *d, void (*pf_progress)(int current, int total)); + diff --git a/apps/plugins/imageviewer/gif/gif_err.c b/apps/plugins/imageviewer/gif/gif_err.c new file mode 100644 index 0000000000..abfb579b29 --- /dev/null +++ b/apps/plugins/imageviewer/gif/gif_err.c @@ -0,0 +1,99 @@ +/***************************************************************************** + +gif_err.c - handle error reporting for the GIF library. + +****************************************************************************/ + +/* #include */ + +#include "gif_lib.h" +#include "gif_lib_private.h" + +/***************************************************************************** + Return a string description of the last GIF error +*****************************************************************************/ +char * +GifErrorString(int ErrorCode) +{ + char *Err; + + switch (ErrorCode) { +#if 0 + case E_GIF_ERR_OPEN_FAILED: + Err = "Failed to open given file"; + break; + case E_GIF_ERR_WRITE_FAILED: + Err = "Failed to write to given file"; + break; + case E_GIF_ERR_HAS_SCRN_DSCR: + Err = "Screen descriptor has already been set"; + break; + case E_GIF_ERR_HAS_IMAG_DSCR: + Err = "Image descriptor is still active"; + break; + case E_GIF_ERR_NO_COLOR_MAP: + Err = "Neither global nor local color map"; + break; + case E_GIF_ERR_DATA_TOO_BIG: + Err = "Number of pixels bigger than width * height"; + break; + case E_GIF_ERR_NOT_ENOUGH_MEM: + Err = "Failed to allocate required memory"; + break; + case E_GIF_ERR_DISK_IS_FULL: + Err = "Write failed (disk full?)"; + break; + case E_GIF_ERR_CLOSE_FAILED: + Err = "Failed to close given file"; + break; + case E_GIF_ERR_NOT_WRITEABLE: + Err = "Given file was not opened for write"; + break; +#endif + case D_GIF_ERR_OPEN_FAILED: + Err = "Failed to open given file"; + break; + case D_GIF_ERR_READ_FAILED: + Err = "Failed to read from given file"; + break; + case D_GIF_ERR_NOT_GIF_FILE: + Err = "Data is not in GIF format"; + break; + case D_GIF_ERR_NO_SCRN_DSCR: + Err = "No screen descriptor detected"; + break; + case D_GIF_ERR_NO_IMAG_DSCR: + Err = "No Image Descriptor detected"; + break; + case D_GIF_ERR_NO_COLOR_MAP: + Err = "Neither global nor local color map"; + break; + case D_GIF_ERR_WRONG_RECORD: + Err = "Wrong record type detected"; + break; + case D_GIF_ERR_DATA_TOO_BIG: + Err = "Number of pixels bigger than width * height"; + break; + case D_GIF_ERR_NOT_ENOUGH_MEM: + Err = "Failed to allocate required memory"; + break; + case D_GIF_ERR_CLOSE_FAILED: + Err = "Failed to close given file"; + break; + case D_GIF_ERR_NOT_READABLE: + Err = "Given file was not opened for read"; + break; + case D_GIF_ERR_IMAGE_DEFECT: + Err = "Image is defective, decoding aborted"; + break; + case D_GIF_ERR_EOF_TOO_SOON: + Err = "Image EOF detected before image complete"; + break; + default: + Err = NULL; + break; + } + return Err; +} + +/* end */ diff --git a/apps/plugins/imageviewer/gif/gif_hash.h b/apps/plugins/imageviewer/gif/gif_hash.h new file mode 100644 index 0000000000..a236bb56d1 --- /dev/null +++ b/apps/plugins/imageviewer/gif/gif_hash.h @@ -0,0 +1,39 @@ +/****************************************************************************** + +gif_hash.h - magfic constants and declarations for GIF LZW + +******************************************************************************/ + +#ifndef _GIF_HASH_H_ +#define _GIF_HASH_H_ + +//#include +#include + +#define HT_SIZE 8192 /* 12bits = 4096 or twice as big! */ +#define HT_KEY_MASK 0x1FFF /* 13bits keys */ +#define HT_KEY_NUM_BITS 13 /* 13bits keys */ +#define HT_MAX_KEY 8191 /* 13bits - 1, maximal code possible */ +#define HT_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ + +/* The 32 bits of the long are divided into two parts for the key & code: */ +/* 1. The code is 12 bits as our compression algorithm is limited to 12bits */ +/* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits. */ +/* The key is the upper 20 bits. The code is the lower 12. */ +#define HT_GET_KEY(l) (l >> 12) +#define HT_GET_CODE(l) (l & 0x0FFF) +#define HT_PUT_KEY(l) (l << 12) +#define HT_PUT_CODE(l) (l & 0x0FFF) + +typedef struct GifHashTableType { + uint32_t HTable[HT_SIZE]; +} GifHashTableType; + +GifHashTableType *_InitHashTable(void); +void _ClearHashTable(GifHashTableType *HashTable); +void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code); +int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key); + +#endif /* _GIF_HASH_H_ */ + +/* end */ diff --git a/apps/plugins/imageviewer/gif/gif_lib.h b/apps/plugins/imageviewer/gif/gif_lib.h new file mode 100644 index 0000000000..0b77821b26 --- /dev/null +++ b/apps/plugins/imageviewer/gif/gif_lib.h @@ -0,0 +1,308 @@ +/****************************************************************************** + +gif_lib.h - service library for decoding and encoding GIF images + +*****************************************************************************/ + +#ifndef _GIF_LIB_H_ +#define _GIF_LIB_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GIFLIB_MAJOR 5 +#define GIFLIB_MINOR 0 +#define GIFLIB_RELEASE 2 + +#define GIF_ERROR 0 +#define GIF_OK 1 + +#include +#include "rb_glue.h" + +#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */ +#define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1 +#define GIF_VERSION_POS 3 /* Version first character in stamp. */ +#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp. */ +#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp. */ + +typedef unsigned char GifPixelType; +typedef unsigned char *GifRowType; +typedef unsigned char GifByteType; +typedef unsigned int GifPrefixType; +typedef int GifWord; + +/****************************************************************************** + GIF89 structures +******************************************************************************/ + +typedef struct GraphicsControlBlock { + int DisposalMode; +#define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */ +#define DISPOSE_DO_NOT 1 /* Leave image in place */ +#define DISPOSE_BACKGROUND 2 /* Set area too background color */ +#define DISPOSE_PREVIOUS 3 /* Restore to previous content */ + bool UserInputFlag; /* User confirmation required before disposal */ + int DelayTime; /* pre-display delay in 0.01sec units */ + int TransparentColor; /* Palette index for transparency, -1 if none */ +#define NO_TRANSPARENT_COLOR -1 +} GraphicsControlBlock; + +typedef struct GifColorType { + GifByteType Red, Green, Blue; +} GifColorType; + +typedef struct ColorMapObject { + int ColorCount; + int BitsPerPixel; + bool SortFlag; + GifColorType *Colors; /* on malloc(3) heap */ +} ColorMapObject; + +typedef struct GifImageDesc { + GifWord Left, Top, Width, Height; /* Current image dimensions. */ + bool Interlace; /* Sequential/Interlaced lines. */ + ColorMapObject *ColorMap; /* The local color map */ + GraphicsControlBlock *GCB; /* Graphic control block */ +} GifImageDesc; + +typedef struct ExtensionBlock { + int ByteCount; + GifByteType *Bytes; /* on malloc(3) heap */ + int Function; /* The block function code */ +#define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */ +#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */ +#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */ +#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */ +#define APPLICATION_EXT_FUNC_CODE 0xff /* application block */ +} ExtensionBlock; + +typedef struct SavedImage { + GifImageDesc ImageDesc; + GifByteType *RasterBits; /* on malloc(3) heap */ + int ExtensionBlockCount; /* Count of extensions before image */ + ExtensionBlock *ExtensionBlocks; /* Extensions before image */ +} SavedImage; + +typedef struct GifFileType { + GifWord SWidth, SHeight; /* Size of virtual canvas */ + GifWord SColorResolution; /* How many colors can we generate? */ + GifWord SBackGroundColor; /* Background color for virtual canvas */ + GifByteType AspectByte; /* Used to compute pixel aspect ratio */ + ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */ + int ImageCount; /* Number of current image (both APIs) */ + GifImageDesc Image; /* Current image (low-level API) */ + SavedImage *SavedImages; /* Image sequence (high-level API) */ + int ExtensionBlockCount; /* Count extensions past last image */ + ExtensionBlock *ExtensionBlocks; /* Extensions past last image */ + int Error; /* Last error condition reported */ + void *UserData; /* hook to attach user data (TVT) */ + void *Private; /* Don't mess with this! */ +} GifFileType; + +#define GIF_ASPECT_RATIO(n) ((n)+15.0/64.0) + +typedef enum { + UNDEFINED_RECORD_TYPE, + SCREEN_DESC_RECORD_TYPE, + IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */ + EXTENSION_RECORD_TYPE, /* Begin with '!' */ + TERMINATE_RECORD_TYPE /* Begin with ';' */ +} GifRecordType; + +/* func type to read gif data from arbitrary sources (TVT) */ +typedef int (*InputFunc) (GifFileType *, GifByteType *, int); + +/* func type to write gif data to arbitrary targets. + * Returns count of bytes written. (MRB) + */ +typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int); + +/****************************************************************************** + GIF encoding routines +******************************************************************************/ + +/* Main entry points */ +GifFileType *EGifOpenFileName(const char *GifFileName, + const bool GifTestExistence, int *Error); +GifFileType *EGifOpenFileHandle(const int GifFileHandle, int *Error); +GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *Error); +int EGifSpew(GifFileType * GifFile); +char *EGifGetGifVersion(GifFileType *GifFile); /* new in 5.x */ +int EGifCloseFile(GifFileType * GifFile); + +#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */ +#define E_GIF_ERR_WRITE_FAILED 2 +#define E_GIF_ERR_HAS_SCRN_DSCR 3 +#define E_GIF_ERR_HAS_IMAG_DSCR 4 +#define E_GIF_ERR_NO_COLOR_MAP 5 +#define E_GIF_ERR_DATA_TOO_BIG 6 +#define E_GIF_ERR_NOT_ENOUGH_MEM 7 +#define E_GIF_ERR_DISK_IS_FULL 8 +#define E_GIF_ERR_CLOSE_FAILED 9 +#define E_GIF_ERR_NOT_WRITEABLE 10 + +/* These are legacy. You probably do not want to call them directly */ +int EGifPutScreenDesc(GifFileType *GifFile, + const int GifWidth, const int GifHeight, + const int GifColorRes, + const int GifBackGround, + const ColorMapObject *GifColorMap); +int EGifPutImageDesc(GifFileType *GifFile, + const int GifLeft, const int GifTop, + const int GifWidth, const int GifHeight, + const bool GifInterlace, + const ColorMapObject *GifColorMap); +void EGifSetGifVersion(GifFileType *GifFile, const bool gif89); +int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine, + int GifLineLen); +int EGifPutPixel(GifFileType *GifFile, const GifPixelType GifPixel); +int EGifPutComment(GifFileType *GifFile, const char *GifComment); +int EGifPutExtensionLeader(GifFileType *GifFile, const int GifExtCode); +int EGifPutExtensionBlock(GifFileType *GifFile, + const int GifExtLen, const void *GifExtension); +int EGifPutExtensionTrailer(GifFileType *GifFile); +int EGifPutExtension(GifFileType *GifFile, const int GifExtCode, + const int GifExtLen, + const void *GifExtension); +int EGifPutCode(GifFileType *GifFile, int GifCodeSize, + const GifByteType *GifCodeBlock); +int EGifPutCodeNext(GifFileType *GifFile, + const GifByteType *GifCodeBlock); + +/****************************************************************************** + GIF decoding routines +******************************************************************************/ + +/* Main entry points */ +GifFileType *DGifOpenFileName(const char *GifFileName, int *Error); +GifFileType *DGifOpenFileHandle(int GifFileHandle, int *Error); +int DGifSlurp(GifFileType * GifFile); +GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, int *Error); /* new one (TVT) */ +int DGifCloseFile(GifFileType * GifFile); + +#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */ +#define D_GIF_ERR_READ_FAILED 102 +#define D_GIF_ERR_NOT_GIF_FILE 103 +#define D_GIF_ERR_NO_SCRN_DSCR 104 +#define D_GIF_ERR_NO_IMAG_DSCR 105 +#define D_GIF_ERR_NO_COLOR_MAP 106 +#define D_GIF_ERR_WRONG_RECORD 107 +#define D_GIF_ERR_DATA_TOO_BIG 108 +#define D_GIF_ERR_NOT_ENOUGH_MEM 109 +#define D_GIF_ERR_CLOSE_FAILED 110 +#define D_GIF_ERR_NOT_READABLE 111 +#define D_GIF_ERR_IMAGE_DEFECT 112 +#define D_GIF_ERR_EOF_TOO_SOON 113 + +/* These are legacy. You probably do not want to call them directly */ +int DGifGetScreenDesc(GifFileType *GifFile); +int DGifGetRecordType(GifFileType *GifFile, GifRecordType *GifType); +int DGifGetImageDesc(GifFileType *GifFile); +int DGifGetLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen); +int DGifGetPixel(GifFileType *GifFile, GifPixelType GifPixel); +int DGifGetComment(GifFileType *GifFile, char *GifComment); +int DGifGetExtension(GifFileType *GifFile, int *GifExtCode, + GifByteType **GifExtension); +int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **GifExtension); +int DGifGetCode(GifFileType *GifFile, int *GifCodeSize, + GifByteType **GifCodeBlock); +int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock); +int DGifGetLZCodes(GifFileType *GifFile, int *GifCode); + + +/****************************************************************************** + Color table quantization (deprecated) +******************************************************************************/ +int GifQuantizeBuffer(unsigned int Width, unsigned int Height, + int *ColorMapSize, GifByteType * RedInput, + GifByteType * GreenInput, GifByteType * BlueInput, + GifByteType * OutputBuffer, + GifColorType * OutputColorMap); + +/****************************************************************************** + Error handling and reporting. +******************************************************************************/ +extern char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */ + +/***************************************************************************** + Everything below this point is new after version 1.2, supporting `slurp + mode' for doing I/O in two big belts with all the image-bashing in core. +******************************************************************************/ + +/****************************************************************************** + Color map handling from gif_alloc.c +******************************************************************************/ + +extern ColorMapObject *GifMakeMapObject(int ColorCount, + const GifColorType *ColorMap); +extern void GifFreeMapObject(ColorMapObject *Object); +extern ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1, + const ColorMapObject *ColorIn2, + GifPixelType ColorTransIn2[]); +extern int GifBitSize(int n); + +/****************************************************************************** + Support for the in-core structures allocation (slurp mode). +******************************************************************************/ + +extern void GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]); +extern int GifAddExtensionBlock(int *ExtensionBlock_Count, + ExtensionBlock **ExtensionBlocks, + int Function, + unsigned int Len, unsigned char ExtData[]); +extern void GifFreeExtensions(int *ExtensionBlock_Count, + ExtensionBlock **ExtensionBlocks); +extern SavedImage *GifMakeSavedImage(GifFileType *GifFile, + const SavedImage *CopyFrom); +extern void GifFreeSavedImages(GifFileType *GifFile); + +/****************************************************************************** + 5.x functions for GIF89 graphics control blocks +******************************************************************************/ + +int DGifExtensionToGCB(const size_t GifExtensionLength, + const GifByteType *GifExtension, + GraphicsControlBlock *GCB); +size_t EGifGCBToExtension(const GraphicsControlBlock *GCB, + GifByteType *GifExtension); + +int DGifSavedExtensionToGCB(GifFileType *GifFile, + int ImageIndex, + GraphicsControlBlock *GCB); +int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, + GifFileType *GifFile, + int ImageIndex); + +/****************************************************************************** + The library's internal utility font +******************************************************************************/ + +#define GIF_FONT_WIDTH 8 +#define GIF_FONT_HEIGHT 8 +extern const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH]; + +extern void GifDrawText8x8(SavedImage *Image, + const int x, const int y, + const char *legend, const int color); + +extern void GifDrawBox(SavedImage *Image, + const int x, const int y, + const int w, const int d, const int color); + +extern void GifDrawRectangle(SavedImage *Image, + const int x, const int y, + const int w, const int d, const int color); + +extern void GifDrawBoxedText8x8(SavedImage *Image, + const int x, const int y, + const char *legend, + const int border, const int bg, const int fg); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _GIF_LIB_H */ + +/* end */ diff --git a/apps/plugins/imageviewer/gif/gif_lib_private.h b/apps/plugins/imageviewer/gif/gif_lib_private.h new file mode 100644 index 0000000000..4e95aa4ee5 --- /dev/null +++ b/apps/plugins/imageviewer/gif/gif_lib_private.h @@ -0,0 +1,59 @@ +/**************************************************************************** + +gif_lib_private.h - internal giflib routines and structures + +****************************************************************************/ + +#ifndef _GIF_LIB_PRIVATE_H +#define _GIF_LIB_PRIVATE_H + +#include "gif_lib.h" +#include "gif_hash.h" + +#define EXTENSION_INTRODUCER 0x21 +#define DESCRIPTOR_INTRODUCER 0x2c +#define TERMINATOR_INTRODUCER 0x3b + +#define LZ_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ +#define LZ_BITS 12 + +#define FLUSH_OUTPUT 4096 /* Impossible code, to signal flush. */ +#define FIRST_CODE 4097 /* Impossible code, to signal first. */ +#define NO_SUCH_CODE 4098 /* Impossible code, to signal empty. */ + +#define FILE_STATE_WRITE 0x01 +#define FILE_STATE_SCREEN 0x02 +#define FILE_STATE_IMAGE 0x04 +#define FILE_STATE_READ 0x08 + +#define IS_READABLE(Private) (Private->FileState & FILE_STATE_READ) +#define IS_WRITEABLE(Private) (Private->FileState & FILE_STATE_WRITE) + +typedef struct GifFilePrivateType { + GifWord FileState, FileHandle, /* Where all this data goes to! */ + BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */ + ClearCode, /* The CLEAR LZ code. */ + EOFCode, /* The EOF LZ code. */ + RunningCode, /* The next code algorithm can generate. */ + RunningBits, /* The number of bits required to represent RunningCode. */ + MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. */ + LastCode, /* The code before the current code. */ + CrntCode, /* Current algorithm code. */ + StackPtr, /* For character stack (see below). */ + CrntShiftState; /* Number of bits in CrntShiftDWord. */ + unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */ + unsigned long PixelCount; /* Number of pixels in image. */ + int File; /* File as stream. (converted to int in rb) */ + InputFunc Read; /* function to read gif input (TVT) */ + OutputFunc Write; /* function to write gif output (MRB) */ + GifByteType Buf[256]; /* Compressed input is buffered here. */ + GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */ + GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */ + GifPrefixType Prefix[LZ_MAX_CODE + 1]; + GifHashTableType *HashTable; + bool gif89; +} GifFilePrivateType; + +#endif /* _GIF_LIB_PRIVATE_H */ + +/* end */ diff --git a/apps/plugins/imageviewer/gif/gifalloc.c b/apps/plugins/imageviewer/gif/gifalloc.c new file mode 100644 index 0000000000..5810559320 --- /dev/null +++ b/apps/plugins/imageviewer/gif/gifalloc.c @@ -0,0 +1,405 @@ +/***************************************************************************** + + GIF construction tools + +****************************************************************************/ + +#include +/* #include */ +#include + +#include "gif_lib.h" + +#ifndef MAX +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + +/****************************************************************************** + Miscellaneous utility functions +******************************************************************************/ + +/* return smallest bitfield size n will fit in */ +int +GifBitSize(int n) +{ + register int i; + + for (i = 1; i <= 8; i++) + if ((1 << i) >= n) + break; + return (i); +} + +/****************************************************************************** + Color map object functions +******************************************************************************/ + +/* + * Allocate a color map of given size; initialize with contents of + * ColorMap if that pointer is non-NULL. + */ +ColorMapObject * +GifMakeMapObject(int ColorCount, const GifColorType *ColorMap) +{ + ColorMapObject *Object; + + /*** FIXME: Our ColorCount has to be a power of two. Is it necessary to + * make the user know that or should we automatically round up instead? */ + if (ColorCount != (1 << GifBitSize(ColorCount))) { + return ((ColorMapObject *) NULL); + } + + Object = (ColorMapObject *)malloc(sizeof(ColorMapObject)); + if (Object == (ColorMapObject *) NULL) { + return ((ColorMapObject *) NULL); + } + + Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType)); + if (Object->Colors == (GifColorType *) NULL) { + free(Object); + return ((ColorMapObject *) NULL); + } + + Object->ColorCount = ColorCount; + Object->BitsPerPixel = GifBitSize(ColorCount); + + if (ColorMap != NULL) { + memcpy((char *)Object->Colors, + (char *)ColorMap, ColorCount * sizeof(GifColorType)); + } + + return (Object); +} + +/******************************************************************************* +Free a color map object +*******************************************************************************/ +void +GifFreeMapObject(ColorMapObject *Object) +{ + if (Object != NULL) { + (void)free(Object->Colors); + (void)free(Object); + } +} + +/* #ifdef DEBUG */ +#if 0 +void +DumpColorMap(ColorMapObject *Object, + FILE * fp) +{ + if (Object != NULL) { + int i, j, Len = Object->ColorCount; + + for (i = 0; i < Len; i += 4) { + for (j = 0; j < 4 && j < Len; j++) { + (void)fprintf(fp, "%3d: %02x %02x %02x ", i + j, + Object->Colors[i + j].Red, + Object->Colors[i + j].Green, + Object->Colors[i + j].Blue); + } + (void)fprintf(fp, "\n"); + } + } +} +#endif /* DEBUG */ + +/******************************************************************************* + Compute the union of two given color maps and return it. If result can't + fit into 256 colors, NULL is returned, the allocated union otherwise. + ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are + copied iff they didn't exist before. ColorTransIn2 maps the old + ColorIn2 into the ColorUnion color map table./ +*******************************************************************************/ +ColorMapObject * +GifUnionColorMap(const ColorMapObject *ColorIn1, + const ColorMapObject *ColorIn2, + GifPixelType ColorTransIn2[]) +{ + int i, j, CrntSlot, RoundUpTo, NewGifBitSize; + ColorMapObject *ColorUnion; + + /* + * We don't worry about duplicates within either color map; if + * the caller wants to resolve those, he can perform unions + * with an empty color map. + */ + + /* Allocate table which will hold the result for sure. */ + ColorUnion = GifMakeMapObject(MAX(ColorIn1->ColorCount, + ColorIn2->ColorCount) * 2, NULL); + + if (ColorUnion == NULL) + return (NULL); + + /* + * Copy ColorIn1 to ColorUnion. + */ + for (i = 0; i < ColorIn1->ColorCount; i++) + ColorUnion->Colors[i] = ColorIn1->Colors[i]; + CrntSlot = ColorIn1->ColorCount; + + /* + * Potentially obnoxious hack: + * + * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end + * of table 1. This is very useful if your display is limited to + * 16 colors. + */ + while (ColorIn1->Colors[CrntSlot - 1].Red == 0 + && ColorIn1->Colors[CrntSlot - 1].Green == 0 + && ColorIn1->Colors[CrntSlot - 1].Blue == 0) + CrntSlot--; + + /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */ + for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) { + /* Let's see if this color already exists: */ + for (j = 0; j < ColorIn1->ColorCount; j++) + if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i], + sizeof(GifColorType)) == 0) + break; + + if (j < ColorIn1->ColorCount) + ColorTransIn2[i] = j; /* color exists in Color1 */ + else { + /* Color is new - copy it to a new slot: */ + ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i]; + ColorTransIn2[i] = CrntSlot++; + } + } + + if (CrntSlot > 256) { + GifFreeMapObject(ColorUnion); + return ((ColorMapObject *) NULL); + } + + NewGifBitSize = GifBitSize(CrntSlot); + RoundUpTo = (1 << NewGifBitSize); + + if (RoundUpTo != ColorUnion->ColorCount) { + register GifColorType *Map = ColorUnion->Colors; + + /* + * Zero out slots up to next power of 2. + * We know these slots exist because of the way ColorUnion's + * start dimension was computed. + */ + for (j = CrntSlot; j < RoundUpTo; j++) + Map[j].Red = Map[j].Green = Map[j].Blue = 0; + + /* perhaps we can shrink the map? */ + if (RoundUpTo < ColorUnion->ColorCount) + ColorUnion->Colors = (GifColorType *)realloc(Map, + sizeof(GifColorType) * RoundUpTo); + } + + ColorUnion->ColorCount = RoundUpTo; + ColorUnion->BitsPerPixel = NewGifBitSize; + + return (ColorUnion); +} + +/******************************************************************************* + Apply a given color translation to the raster bits of an image +*******************************************************************************/ +void +GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]) +{ + register int i; + register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width; + + for (i = 0; i < RasterSize; i++) + Image->RasterBits[i] = Translation[Image->RasterBits[i]]; +} + +/****************************************************************************** + Extension record functions +******************************************************************************/ +int +GifAddExtensionBlock(int *ExtensionBlockCount, + ExtensionBlock **ExtensionBlocks, + int Function, + unsigned int Len, + unsigned char ExtData[]) +{ + ExtensionBlock *ep; + + if (*ExtensionBlocks == NULL) + *ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock)); + else + *ExtensionBlocks = (ExtensionBlock *)realloc(*ExtensionBlocks, + sizeof(ExtensionBlock) * + (*ExtensionBlockCount + 1)); + + if (*ExtensionBlocks == NULL) + return (GIF_ERROR); + + ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++]; + + ep->Function = Function; + ep->ByteCount=Len; + ep->Bytes = (GifByteType *)malloc(ep->ByteCount); + if (ep->Bytes == NULL) + return (GIF_ERROR); + + if (ExtData != NULL) { + memcpy(ep->Bytes, ExtData, Len); + } + + return (GIF_OK); +} + +void +GifFreeExtensions(int *ExtensionBlockCount, + ExtensionBlock **ExtensionBlocks) +{ + ExtensionBlock *ep; + + if (*ExtensionBlocks == NULL) + return; + + for (ep = *ExtensionBlocks; + ep < (*ExtensionBlocks + *ExtensionBlockCount); + ep++) + (void)free((char *)ep->Bytes); + (void)free((char *)*ExtensionBlocks); + *ExtensionBlocks = NULL; + *ExtensionBlockCount = 0; +} + +/****************************************************************************** + Image block allocation functions +******************************************************************************/ + +/* Private Function: + * Frees the last image in the GifFile->SavedImages array + */ +void +FreeLastSavedImage(GifFileType *GifFile) +{ + SavedImage *sp; + + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) + return; + + /* Remove one SavedImage from the GifFile */ + GifFile->ImageCount--; + sp = &GifFile->SavedImages[GifFile->ImageCount]; + + /* Deallocate its Colormap */ + if (sp->ImageDesc.ColorMap != NULL) { + GifFreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } + + /* Deallocate the image data */ + if (sp->RasterBits != NULL) + free((char *)sp->RasterBits); + + /* Deallocate any extensions */ + GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks); + + /*** FIXME: We could realloc the GifFile->SavedImages structure but is + * there a point to it? Saves some memory but we'd have to do it every + * time. If this is used in GifFreeSavedImages then it would be inefficient + * (The whole array is going to be deallocated.) If we just use it when + * we want to free the last Image it's convenient to do it here. + */ +} + +/* + * Append an image block to the SavedImages array + */ +SavedImage * +GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom) +{ + SavedImage *sp; + + if (GifFile->SavedImages == NULL) + GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage)); + else + GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages, + sizeof(SavedImage) * (GifFile->ImageCount + 1)); + + if (GifFile->SavedImages == NULL) + return ((SavedImage *)NULL); + else { + sp = &GifFile->SavedImages[GifFile->ImageCount++]; + memset((char *)sp, '\0', sizeof(SavedImage)); + + if (CopyFrom != NULL) { + memcpy((char *)sp, CopyFrom, sizeof(SavedImage)); + + /* + * Make our own allocated copies of the heap fields in the + * copied record. This guards against potential aliasing + * problems. + */ + + /* first, the local color map */ + if (sp->ImageDesc.ColorMap != NULL) { + sp->ImageDesc.ColorMap = GifMakeMapObject( + CopyFrom->ImageDesc.ColorMap->ColorCount, + CopyFrom->ImageDesc.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + } + + /* next, the raster */ + sp->RasterBits = (unsigned char *)malloc(sizeof(GifPixelType) * + CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width); + if (sp->RasterBits == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + memcpy(sp->RasterBits, CopyFrom->RasterBits, + sizeof(GifPixelType) * CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width); + + /* finally, the extension blocks */ + if (sp->ExtensionBlocks != NULL) { + sp->ExtensionBlocks = (ExtensionBlock *)malloc( + sizeof(ExtensionBlock) * + CopyFrom->ExtensionBlockCount); + if (sp->ExtensionBlocks == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks, + sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount); + } + } + + return (sp); + } +} + +void +GifFreeSavedImages(GifFileType *GifFile) +{ + SavedImage *sp; + + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { + return; + } + for (sp = GifFile->SavedImages; + sp < GifFile->SavedImages + GifFile->ImageCount; sp++) { + if (sp->ImageDesc.ColorMap != NULL) { + GifFreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } + + if (sp->RasterBits != NULL) + free((char *)sp->RasterBits); + + GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks); + } + free((char *)GifFile->SavedImages); + GifFile->SavedImages = NULL; +} + +/* end */ diff --git a/apps/plugins/imageviewer/gif/rb_glue.h b/apps/plugins/imageviewer/gif/rb_glue.h new file mode 100644 index 0000000000..6cd46b0e56 --- /dev/null +++ b/apps/plugins/imageviewer/gif/rb_glue.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (c) 2012 Marcin Bukat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "plugin.h" +#include + +#undef memset +#define memset(a,b,c) rb->memset((a),(b),(c)) +#undef memmove +#define memmove(a,b,c) rb->memmove((a),(b),(c)) +#undef memcmp +#define memcmp(a,b,c) rb->memcmp((a),(b),(c)) +#undef strncmp +#define strncmp(a,b,c) rb->strncmp((a),(b),(c)) + +#define fread(ptr, size, nmemb, stream) rb->read(stream, ptr, size*nmemb) +#define fclose(stream) rb->close(stream) +#define fdopen(a,b) ((a)) + +#define malloc(a) tlsf_malloc((a)) +#define free(a) tlsf_free((a)) +#define realloc(a, b) tlsf_realloc((a),(b)) +#define calloc(a,b) tlsf_calloc((a),(b)) + +#ifndef SIZE_MAX +#define SIZE_MAX INT_MAX +#endif diff --git a/apps/plugins/imageviewer/image_decoder.c b/apps/plugins/imageviewer/image_decoder.c index 416bda9e42..eab1c01dbc 100644 --- a/apps/plugins/imageviewer/image_decoder.c +++ b/apps/plugins/imageviewer/image_decoder.c @@ -28,8 +28,9 @@ static const char *decoder_names[MAX_IMAGE_TYPES] = { "jpeg", "png", #ifdef HAVE_LCD_COLOR - "ppm" + "ppm", #endif + "gif" }; /* Check file type by magic number or file extension @@ -53,6 +54,7 @@ enum image_type get_image_type(const char *name, bool quiet) #ifdef HAVE_LCD_COLOR { ".ppm", IMAGE_PPM }, #endif + { ".gif", IMAGE_GIF }, }; static const struct { char *magic; /* magic number */ @@ -66,6 +68,8 @@ enum image_type get_image_type(const char *name, bool quiet) { "P3", 2, IMAGE_PPM }, { "P6", 2, IMAGE_PPM }, #endif + { "GIF87a", 6, IMAGE_GIF }, + { "GIF89a", 6, IMAGE_GIF }, }; enum image_type type = IMAGE_UNKNOWN; diff --git a/apps/plugins/imageviewer/image_decoder.h b/apps/plugins/imageviewer/image_decoder.h index 3267d2af6e..c27ed33e8e 100644 --- a/apps/plugins/imageviewer/image_decoder.h +++ b/apps/plugins/imageviewer/image_decoder.h @@ -32,6 +32,7 @@ enum image_type { #ifdef HAVE_LCD_COLOR IMAGE_PPM, #endif + IMAGE_GIF, MAX_IMAGE_TYPES }; diff --git a/apps/plugins/imageviewer/imageviewer.c b/apps/plugins/imageviewer/imageviewer.c index 044c835d00..39507a1fcf 100644 --- a/apps/plugins/imageviewer/imageviewer.c +++ b/apps/plugins/imageviewer/imageviewer.c @@ -550,17 +550,38 @@ static void pan_view_down(struct image_info *info) /* interactively scroll around the image */ static int scroll_bmp(struct image_info *info) { + static long ss_timeout = 0; + int button; #if defined(IMGVIEW_ZOOM_PRE) || defined(IMGVIEW_MENU_PRE) int lastbutton = BUTTON_NONE; #endif + if (!ss_timeout && iv_api.slideshow_enabled) + ss_timeout = *rb->current_tick + settings.ss_timeout * HZ; + while (true) { if (iv_api.slideshow_enabled) - button = rb->button_get_w_tmo(settings.ss_timeout * HZ); + { + if (info->frames_count > 1 && info->delay && + settings.ss_timeout * HZ > info->delay) + { + /* animated content and delay between subsequent frames + * is shorter then slideshow delay + */ + button = rb->button_get_w_tmo(info->delay); + } + else + button = rb->button_get_w_tmo(settings.ss_timeout * HZ); + } else - button = rb->button_get(true); + { + if (info->frames_count > 1 && info->delay) + button = rb->button_get_w_tmo(info->delay); + else + button = rb->button_get(true); + } iv_api.running_slideshow = false; @@ -595,9 +616,28 @@ static int scroll_bmp(struct image_info *info) case BUTTON_NONE: if (iv_api.slideshow_enabled && entries > 1) { - iv_api.running_slideshow = true; - return change_filename(DIR_NEXT); + if (info->frames_count > 1) + { + /* animations */ + if (TIME_AFTER(*rb->current_tick, ss_timeout)) + { + iv_api.running_slideshow = true; + ss_timeout = 0; + return change_filename(DIR_NEXT); + } + else + return NEXT_FRAME; + } + else + { + /* still picture */ + iv_api.running_slideshow = true; + return change_filename(DIR_NEXT); + } } + else + return NEXT_FRAME; + break; #ifdef IMGVIEW_SLIDE_SHOW @@ -838,9 +878,11 @@ static int load_and_show(char* filename, struct image_info *info) cx = info->x_size/ds/2; /* center the view */ cy = info->y_size/ds/2; + /* used to loop through subimages in animated gifs */ + int frame = 0; do /* loop the image prepare and decoding when zoomed */ { - status = imgdec->get_image(info, ds); /* decode or fetch from cache */ + status = imgdec->get_image(info, frame, ds); /* decode or fetch from cache */ if (status == PLUGIN_ERROR) { file_pt[curfile] = NULL; @@ -849,7 +891,7 @@ static int load_and_show(char* filename, struct image_info *info) set_view(info, cx, cy); - if(!iv_api.running_slideshow) + if(!iv_api.running_slideshow && (info->frames_count == 1)) { rb->lcd_putsf(0, 3, "showing %dx%d", info->width, info->height); rb->lcd_update(); @@ -870,6 +912,7 @@ static int load_and_show(char* filename, struct image_info *info) while (1) { status = scroll_bmp(info); + if (status == ZOOM_IN) { if (ds > ds_min || (imgdec->unscaled_avail && ds > 1)) @@ -899,16 +942,19 @@ static int load_and_show(char* filename, struct image_info *info) else continue; } + + /* next frame in animated content */ + if (status == NEXT_FRAME) + frame = (frame + 1)%info->frames_count; + break; } -#ifdef USEGSLIB - grey_show(false); /* switch off overlay */ -#endif rb->lcd_clear_display(); } while (status > PLUGIN_OTHER); #ifdef USEGSLIB + grey_show(false); /* switch off overlay */ rb->lcd_update(); #endif return status; diff --git a/apps/plugins/imageviewer/imageviewer.h b/apps/plugins/imageviewer/imageviewer.h index 9cc1a9117a..19b5db15bb 100644 --- a/apps/plugins/imageviewer/imageviewer.h +++ b/apps/plugins/imageviewer/imageviewer.h @@ -58,6 +58,7 @@ enum { ZOOM_IN, ZOOM_OUT, + NEXT_FRAME, }; #if (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(HAVE_DISK_STORAGE) @@ -82,6 +83,8 @@ struct image_info { int x_size, y_size; /* set size of loaded image in load_image(). */ int width, height; /* set size of resized image in get_image(). */ int x, y; /* display position */ + int frames_count; /* number of subframes */ + int delay; /* delay expressed in ticks between frames */ void *data; /* use freely in decoder. not touched in ui. */ }; @@ -115,6 +118,7 @@ struct image_decoder { /* return needed size of buffer to store downscaled image by ds. * this is used to calculate min downscale. */ int (*img_mem)(int ds); + /* load image from filename. use the passed buffer to store loaded, decoded * or resized image later, so save it to local variables if needed. * set width and height of info properly. also, set buf_size to remaining @@ -125,7 +129,8 @@ struct image_decoder { /* downscale loaded image by ds. use the buffer passed to load_image to * reszie image and/or store resized image. * return PLUGIN_ERROR for error. ui will skip to next image. */ - int (*get_image)(struct image_info *info, int ds); + int (*get_image)(struct image_info *info, int frame, int ds); + /* draw part of image */ void (*draw_image_rect)(struct image_info *info, int x, int y, int width, int height); diff --git a/apps/plugins/imageviewer/jpeg/jpeg.c b/apps/plugins/imageviewer/jpeg/jpeg.c index 511a7054e1..3d8c9c3ac8 100644 --- a/apps/plugins/imageviewer/jpeg/jpeg.c +++ b/apps/plugins/imageviewer/jpeg/jpeg.c @@ -190,8 +190,9 @@ static int load_image(char *filename, struct image_info *info, return PLUGIN_OK; } -static int get_image(struct image_info *info, int ds) +static int get_image(struct image_info *info, int frame, int ds) { + (void)frame; int w, h; /* used to center output */ int size; /* decompressed image size */ long time; /* measured ticks */ diff --git a/apps/plugins/imageviewer/png/png.c b/apps/plugins/imageviewer/png/png.c index 1916f9c55f..29b6713585 100644 --- a/apps/plugins/imageviewer/png/png.c +++ b/apps/plugins/imageviewer/png/png.c @@ -226,8 +226,9 @@ static int load_image(char *filename, struct image_info *info, return PLUGIN_OK; } -static int get_image(struct image_info *info, int ds) +static int get_image(struct image_info *info, int frame, int ds) { + (void)frame; unsigned char **p_disp = &disp[ds]; /* short cut */ LodePNG_Decoder *p_decoder = &decoder; diff --git a/apps/plugins/imageviewer/ppm/ppm.c b/apps/plugins/imageviewer/ppm/ppm.c index 20200d812e..5e03f8c335 100644 --- a/apps/plugins/imageviewer/ppm/ppm.c +++ b/apps/plugins/imageviewer/ppm/ppm.c @@ -150,8 +150,9 @@ static int load_image(char *filename, struct image_info *info, return PLUGIN_OK; } -static int get_image(struct image_info *info, int ds) +static int get_image(struct image_info *info, int frame, int ds) { + (void)frame; unsigned char **p_disp = &disp[ds]; /* short cut */ struct ppm_info *p_ppm = &ppm; diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config index a7aec614e1..24c888b956 100644 --- a/apps/plugins/viewers.config +++ b/apps/plugins/viewers.config @@ -23,6 +23,7 @@ png,viewers/imageviewer,2 #ifdef HAVE_LCD_COLOR ppm,viewers/imageviewer,2 #endif +gif,viewers/imageviewer,2 ucl,viewers/rockbox_flash,3 rvf,viewers/video,4 mp3,viewers/vbrfix,5 -- cgit v1.2.3