summaryrefslogtreecommitdiff
path: root/apps/plugins/imageviewer
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/imageviewer')
-rw-r--r--apps/plugins/imageviewer/SUBDIRS1
-rw-r--r--apps/plugins/imageviewer/bmp/bmp.c3
-rw-r--r--apps/plugins/imageviewer/gif/SOURCES6
-rw-r--r--apps/plugins/imageviewer/gif/dgif_lib.c1169
-rw-r--r--apps/plugins/imageviewer/gif/gif.c241
-rw-r--r--apps/plugins/imageviewer/gif/gif.make30
-rw-r--r--apps/plugins/imageviewer/gif/gif_decoder.c491
-rw-r--r--apps/plugins/imageviewer/gif/gif_decoder.h37
-rw-r--r--apps/plugins/imageviewer/gif/gif_err.c99
-rw-r--r--apps/plugins/imageviewer/gif/gif_hash.h39
-rw-r--r--apps/plugins/imageviewer/gif/gif_lib.h308
-rw-r--r--apps/plugins/imageviewer/gif/gif_lib_private.h59
-rw-r--r--apps/plugins/imageviewer/gif/gifalloc.c405
-rw-r--r--apps/plugins/imageviewer/gif/rb_glue.h44
-rw-r--r--apps/plugins/imageviewer/image_decoder.c6
-rw-r--r--apps/plugins/imageviewer/image_decoder.h1
-rw-r--r--apps/plugins/imageviewer/imageviewer.c64
-rw-r--r--apps/plugins/imageviewer/imageviewer.h7
-rw-r--r--apps/plugins/imageviewer/jpeg/jpeg.c3
-rw-r--r--apps/plugins/imageviewer/png/png.c3
-rw-r--r--apps/plugins/imageviewer/ppm/ppm.c3
21 files changed, 3004 insertions, 15 deletions
diff --git a/apps/plugins/imageviewer/SUBDIRS b/apps/plugins/imageviewer/SUBDIRS
index 0f8d953..1f7b4d8 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 b7efbb7..f7e55db 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 0000000..032b65c
--- /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 0000000..6dc5299
--- /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 <stdlib.h>
+#include <limits.h>
+#include <stdint.h>
+#include <fcntl.h>
+/* #include <unistd.h> */
+/* #include <stdio.h> */
+#include <string.h>
+
+#ifdef _WIN32
+#include <io.h>
+#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 0000000..672735a
--- /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 <lib/pluginlib_bmp.h>
+#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 0000000..8260524
--- /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 0000000..ca9579a
--- /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 <lib/pluginlib_bmp.h>
+#include "bmp.h"
+#if LCD_DEPTH < 8
+#include <lib/grey.h>
+#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; i<GifFile->SHeight; 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 0000000..444f54a
--- /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 0000000..abfb579
--- /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 <stdio.h> */
+
+#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 0000000..a236bb5
--- /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 <unistd.h>
+#include <stdint.h>
+
+#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 0000000..0b77821
--- /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 <stdbool.h>
+#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 0000000..4e95aa4
--- /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 0000000..5810559
--- /dev/null
+++ b/apps/plugins/imageviewer/gif/gifalloc.c
@@ -0,0 +1,405 @@
+/*****************************************************************************
+
+ GIF construction tools
+
+****************************************************************************/
+
+#include <stdlib.h>
+/* #include <stdio.h> */
+#include <string.h>
+
+#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 0000000..6cd46b0
--- /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 <tlsf.h>
+
+#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 416bda9..eab1c01 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 3267d2a..c27ed33 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 044c835..39507a1 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 9cc1a91..19b5db1 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 511a705..3d8c9c3 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 1916f9c..29b6713 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 20200d8..5e03f8c 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;