Merge pull request #7549 from gamblor21/gif_displayio_support

Animated GIF support
This commit is contained in:
Scott Shawcroft 2023-02-21 11:39:29 -08:00 committed by GitHub
commit a8b34bd067
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 2214 additions and 13 deletions

View File

@ -0,0 +1,236 @@
//
// GIF Animator
// written by Larry Bank
// bitbank@pobox.com
// Arduino port started 7/5/2020
// Original GIF code written 20+ years ago :)
// The goal of this code is to decode images up to 480x320
// using no more than 22K of RAM (if sent directly to an LCD display)
//
// Copyright 2020 BitBank Software, Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===========================================================================
#include "AnimatedGIF.h"
// Here is all of the actual code...
#include "gif.inl"
//
// Memory initialization
//
int AnimatedGIF::open(uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw)
{
_gif.iError = GIF_SUCCESS;
_gif.pfnRead = readMem;
_gif.pfnSeek = seekMem;
_gif.pfnDraw = pfnDraw;
_gif.pfnOpen = NULL;
_gif.pfnClose = NULL;
_gif.GIFFile.iSize = iDataSize;
_gif.GIFFile.pData = pData;
return GIFInit(&_gif);
} /* open() */
int AnimatedGIF::openFLASH(uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw)
{
_gif.iError = GIF_SUCCESS;
_gif.pfnRead = readFLASH;
_gif.pfnSeek = seekMem;
_gif.pfnDraw = pfnDraw;
_gif.pfnOpen = NULL;
_gif.pfnClose = NULL;
_gif.GIFFile.iSize = iDataSize;
_gif.GIFFile.pData = pData;
return GIFInit(&_gif);
} /* openFLASH() */
//
// Returns the first comment block found (if any)
//
int AnimatedGIF::getComment(char *pDest)
{
int32_t iOldPos;
iOldPos = _gif.GIFFile.iPos; // keep old position
(*_gif.pfnSeek)(&_gif.GIFFile, _gif.iCommentPos);
(*_gif.pfnRead)(&_gif.GIFFile, (uint8_t *)pDest, _gif.sCommentLen);
(*_gif.pfnSeek)(&_gif.GIFFile, iOldPos);
pDest[_gif.sCommentLen] = 0; // zero terminate the string
return (int)_gif.sCommentLen;
} /* getComment() */
//
// Allocate a block of memory to hold the entire canvas (as 8-bpp)
//
int AnimatedGIF::allocFrameBuf(GIF_ALLOC_CALLBACK *pfnAlloc)
{
if (_gif.iCanvasWidth > 0 && _gif.iCanvasHeight > 0 && _gif.pFrameBuffer == NULL)
{
// Allocate a little extra space for the current line
// as RGB565 or RGB888
int iCanvasSize = _gif.iCanvasWidth * (_gif.iCanvasHeight+3);
_gif.pFrameBuffer = (unsigned char *)(*pfnAlloc)(iCanvasSize);
if (_gif.pFrameBuffer == NULL)
return GIF_ERROR_MEMORY;
return GIF_SUCCESS;
}
return GIF_INVALID_PARAMETER;
} /* allocFrameBuf() */
//
// Set the DRAW callback behavior to RAW (default)
// or COOKED (requires allocating a frame buffer)
//
int AnimatedGIF::setDrawType(int iType)
{
if (iType != GIF_DRAW_RAW && iType != GIF_DRAW_COOKED)
return GIF_INVALID_PARAMETER; // invalid drawing mode
_gif.ucDrawType = (uint8_t)iType;
return GIF_SUCCESS;
} /* setDrawType() */
//
// Release the memory used by the frame buffer
//
int AnimatedGIF::freeFrameBuf(GIF_FREE_CALLBACK *pfnFree)
{
if (_gif.pFrameBuffer)
{
(*pfnFree)(_gif.pFrameBuffer);
_gif.pFrameBuffer = NULL;
return GIF_SUCCESS;
}
return GIF_INVALID_PARAMETER;
} /* freeFrameBuf() */
//
// Return a pointer to the frame buffer (if it was allocated)
//
uint8_t * AnimatedGIF::getFrameBuf()
{
return _gif.pFrameBuffer;
} /* getFrameBuf() */
int AnimatedGIF::getCanvasWidth()
{
return _gif.iCanvasWidth;
} /* getCanvasWidth() */
int AnimatedGIF::getCanvasHeight()
{
return _gif.iCanvasHeight;
} /* getCanvasHeight() */
int AnimatedGIF::getLoopCount()
{
return _gif.iRepeatCount;
} /* getLoopCount() */
int AnimatedGIF::getInfo(GIFINFO *pInfo)
{
return GIF_getInfo(&_gif, pInfo);
} /* getInfo() */
int AnimatedGIF::getLastError()
{
return _gif.iError;
} /* getLastError() */
//
// File (SD/MMC) based initialization
//
int AnimatedGIF::open(const char *szFilename, GIF_OPEN_CALLBACK *pfnOpen, GIF_CLOSE_CALLBACK *pfnClose, GIF_READ_CALLBACK *pfnRead, GIF_SEEK_CALLBACK *pfnSeek, GIF_DRAW_CALLBACK *pfnDraw)
{
_gif.iError = GIF_SUCCESS;
_gif.pfnRead = pfnRead;
_gif.pfnSeek = pfnSeek;
_gif.pfnDraw = pfnDraw;
_gif.pfnOpen = pfnOpen;
_gif.pfnClose = pfnClose;
_gif.GIFFile.fHandle = (*pfnOpen)(szFilename, &_gif.GIFFile.iSize);
if (_gif.GIFFile.fHandle == NULL) {
_gif.iError = GIF_FILE_NOT_OPEN;
return 0;
}
return GIFInit(&_gif);
} /* open() */
void AnimatedGIF::close()
{
if (_gif.pfnClose)
(*_gif.pfnClose)(_gif.GIFFile.fHandle);
} /* close() */
void AnimatedGIF::reset()
{
(*_gif.pfnSeek)(&_gif.GIFFile, 0);
} /* reset() */
void AnimatedGIF::begin(unsigned char ucPaletteType)
{
memset(&_gif, 0, sizeof(_gif));
if (ucPaletteType != GIF_PALETTE_RGB565_LE && ucPaletteType != GIF_PALETTE_RGB565_BE && ucPaletteType != GIF_PALETTE_RGB888)
_gif.iError = GIF_INVALID_PARAMETER;
_gif.ucPaletteType = ucPaletteType;
_gif.ucDrawType = GIF_DRAW_RAW; // assume RAW pixel handling
_gif.pFrameBuffer = NULL;
} /* begin() */
//
// Play a single frame
// returns:
// 1 = good result and more frames exist
// 0 = no more frames exist, a frame may or may not have been played: use getLastError() and look for GIF_SUCCESS to know if a frame was played
// -1 = error
int AnimatedGIF::playFrame(bool bSync, int *delayMilliseconds, void *pUser)
{
int rc;
#if !defined( __MACH__ ) && !defined( __LINUX__ )
long lTime = millis();
#endif
if (_gif.GIFFile.iPos >= _gif.GIFFile.iSize-1) // no more data exists
{
(*_gif.pfnSeek)(&_gif.GIFFile, 0); // seek to start
}
if (GIFParseInfo(&_gif, 0))
{
_gif.pUser = pUser;
if (_gif.iError == GIF_EMPTY_FRAME) // don't try to decode it
return 0;
rc = DecodeLZW(&_gif, 0);
if (rc != 0) // problem
return -1;
}
else
{
// The file is "malformed" in that there is a bunch of non-image data after
// the last frame. Return as if all is well, though if needed getLastError()
// can be used to see if a frame was actually processed:
// GIF_SUCCESS -> frame processed, GIF_EMPTY_FRAME -> no frame processed
if (_gif.iError == GIF_EMPTY_FRAME)
{
if (delayMilliseconds)
*delayMilliseconds = 0;
return 0;
}
return -1; // error parsing the frame info, we may be at the end of the file
}
// Return 1 for more frames or 0 if this was the last frame
if (bSync)
{
#if !defined( __MACH__ ) && !defined( __LINUX__ )
lTime = millis() - lTime;
if (lTime < _gif.iFrameDelay) // need to pause a bit
delay(_gif.iFrameDelay - lTime);
#endif // __LINUX__
}
if (delayMilliseconds) // if not NULL, return the frame delay time
*delayMilliseconds = _gif.iFrameDelay;
return (_gif.GIFFile.iPos < _gif.GIFFile.iSize-10);
} /* playFrame() */

View File

@ -0,0 +1,216 @@
// Copyright 2020 BitBank Software, Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===========================================================================
#ifndef __ANIMATEDGIF__
#define __ANIMATEDGIF__
#if defined( PICO_BUILD ) || defined( __MACH__ ) || defined( __LINUX__ ) || defined( __MCUXPRESSO )
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#define memcpy_P memcpy
#define PROGMEM
#else
#include <Arduino.h>
#endif
//
// GIF Animator
// Written by Larry Bank
// Copyright (c) 2020 BitBank Software, Inc.
// bitbank@pobox.com
//
// Designed to decode images up to 480x320
// using less than 22K of RAM
//
/* GIF Defines and variables */
#define MAX_CHUNK_SIZE 255
#define LZW_BUF_SIZE (6*MAX_CHUNK_SIZE)
#define LZW_HIGHWATER (4*MAX_CHUNK_SIZE)
#ifdef __LINUX__
#define MAX_WIDTH 2048
#else
#define MAX_WIDTH 320
#endif // __LINUX__
#define FILE_BUF_SIZE 4096
#define PIXEL_FIRST 0
#define PIXEL_LAST 4096
#define LINK_UNUSED 5911 // 0x1717 to use memset
#define LINK_END 5912
#define MAX_HASH 5003
#define MAXMAXCODE 4096
enum {
GIF_PALETTE_RGB565_LE = 0, // little endian (default)
GIF_PALETTE_RGB565_BE, // big endian
GIF_PALETTE_RGB888 // original 24-bpp entries
};
// for compatibility with older code
#define LITTLE_ENDIAN_PIXELS GIF_PALETTE_RGB565_LE
#define BIG_ENDIAN_PIXELS GIF_PALETTE_RGB565_BE
//
// Draw callback pixel type
// RAW = 8-bit palettized pixels requiring transparent pixel handling
// COOKED = 16 or 24-bpp fully rendered pixels ready for display
//
enum {
GIF_DRAW_RAW = 0,
GIF_DRAW_COOKED
};
enum {
GIF_SUCCESS = 0,
GIF_DECODE_ERROR,
GIF_TOO_WIDE,
GIF_INVALID_PARAMETER,
GIF_UNSUPPORTED_FEATURE,
GIF_FILE_NOT_OPEN,
GIF_EARLY_EOF,
GIF_EMPTY_FRAME,
GIF_BAD_FILE,
GIF_ERROR_MEMORY
};
typedef struct gif_file_tag
{
int32_t iPos; // current file position
int32_t iSize; // file size
uint8_t *pData; // memory file pointer
void * fHandle; // class pointer to File/SdFat or whatever you want
} GIFFILE;
typedef struct gif_info_tag
{
int32_t iFrameCount; // total frames in file
int32_t iDuration; // duration of animation in milliseconds
int32_t iMaxDelay; // maximum frame delay
int32_t iMinDelay; // minimum frame delay
} GIFINFO;
typedef struct gif_draw_tag
{
int iX, iY; // Corner offset of this frame on the canvas
int y; // current line being drawn (0 = top line of image)
int iWidth, iHeight; // size of this frame
void *pUser; // user supplied pointer
uint8_t *pPixels; // 8-bit source pixels for this line
uint16_t *pPalette; // little or big-endian RGB565 palette entries (default)
uint8_t *pPalette24; // RGB888 palette (optional)
uint8_t ucTransparent; // transparent color
uint8_t ucHasTransparency; // flag indicating the transparent color is in use
uint8_t ucDisposalMethod; // frame disposal method
uint8_t ucBackground; // background color
uint8_t ucIsGlobalPalette; // Flag to indicate that a global palette, rather than a local palette is being used
} GIFDRAW;
// Callback function prototypes
typedef int32_t (GIF_READ_CALLBACK)(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen);
typedef int32_t (GIF_SEEK_CALLBACK)(GIFFILE *pFile, int32_t iPosition);
typedef void (GIF_DRAW_CALLBACK)(GIFDRAW *pDraw);
typedef void * (GIF_OPEN_CALLBACK)(const char *szFilename, int32_t *pFileSize);
typedef void (GIF_CLOSE_CALLBACK)(void *pHandle);
typedef void * (GIF_ALLOC_CALLBACK)(uint32_t iSize);
typedef void (GIF_FREE_CALLBACK)(void *buffer);
//
// our private structure to hold a GIF image decode state
//
typedef struct gif_image_tag
{
int iWidth, iHeight, iCanvasWidth, iCanvasHeight;
int iX, iY; // GIF corner offset
int iBpp;
int iError; // last error
int iFrameDelay; // delay in milliseconds for this frame
int iRepeatCount; // NETSCAPE animation repeat count. 0=forever
int iXCount, iYCount; // decoding position in image (countdown values)
int iLZWOff; // current LZW data offset
int iLZWSize; // current quantity of data in the LZW buffer
int iCommentPos; // file offset of start of comment data
short sCommentLen; // length of comment
GIF_READ_CALLBACK *pfnRead;
GIF_SEEK_CALLBACK *pfnSeek;
GIF_DRAW_CALLBACK *pfnDraw;
GIF_OPEN_CALLBACK *pfnOpen;
GIF_CLOSE_CALLBACK *pfnClose;
GIFFILE GIFFile;
void *pUser;
unsigned char *pFrameBuffer;
unsigned char *pPixels, *pOldPixels;
unsigned char ucLineBuf[MAX_WIDTH]; // current line
unsigned char ucFileBuf[FILE_BUF_SIZE]; // holds temp data and pixel stack
unsigned short pPalette[384]; // can hold RGB565 or RGB888 - set in begin()
unsigned short pLocalPalette[384]; // color palettes for GIF images
unsigned char ucLZW[LZW_BUF_SIZE]; // holds 6 chunks (6x255) of GIF LZW data packed together
unsigned short usGIFTable[4096];
unsigned char ucGIFPixels[8192];
unsigned char bEndOfFrame;
unsigned char ucGIFBits, ucBackground, ucTransparent, ucCodeStart, ucMap, bUseLocalPalette;
unsigned char ucPaletteType; // RGB565 or RGB888
unsigned char ucDrawType; // RAW or COOKED
} GIFIMAGE;
#ifdef __cplusplus
//
// The GIF class wraps portable C code which does the actual work
//
class AnimatedGIF
{
public:
int open(uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw);
int openFLASH(uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw);
int open(const char *szFilename, GIF_OPEN_CALLBACK *pfnOpen, GIF_CLOSE_CALLBACK *pfnClose, GIF_READ_CALLBACK *pfnRead, GIF_SEEK_CALLBACK *pfnSeek, GIF_DRAW_CALLBACK *pfnDraw);
void close();
void reset();
void begin(unsigned char ucPaletteType = GIF_PALETTE_RGB565_LE);
void begin(int iEndian, unsigned char ucPaletteType) { begin(ucPaletteType); };
int playFrame(bool bSync, int *delayMilliseconds, void *pUser = NULL);
int getCanvasWidth();
int allocFrameBuf(GIF_ALLOC_CALLBACK *pfnAlloc);
int setDrawType(int iType);
int freeFrameBuf(GIF_FREE_CALLBACK *pfnFree);
uint8_t *getFrameBuf();
int getCanvasHeight();
int getLoopCount();
int getInfo(GIFINFO *pInfo);
int getLastError();
int getComment(char *destBuffer);
private:
GIFIMAGE _gif;
};
#else
// C interface
int GIF_openRAM(GIFIMAGE *pGIF, uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw);
int GIF_openFile(GIFIMAGE *pGIF, const char *szFilename, GIF_DRAW_CALLBACK *pfnDraw);
void GIF_close(GIFIMAGE *pGIF);
void GIF_begin(GIFIMAGE *pGIF, unsigned char ucPaletteType);
void GIF_reset(GIFIMAGE *pGIF);
int GIF_playFrame(GIFIMAGE *pGIF, int *delayMilliseconds, void *pUser);
int GIF_getCanvasWidth(GIFIMAGE *pGIF);
int GIF_getCanvasHeight(GIFIMAGE *pGIF);
int GIF_getComment(GIFIMAGE *pGIF, char *destBuffer);
int GIF_getInfo(GIFIMAGE *pGIF, GIFINFO *pInfo);
int GIF_getLastError(GIFIMAGE *pGIF);
int GIF_getLoopCount(GIFIMAGE *pGIF);
#endif // __cplusplus
// Due to unaligned memory causing an exception, we have to do these macros the slow way
#define INTELSHORT(p) ((*p) + (*(p+1)<<8))
#define INTELLONG(p) ((*p) + (*(p+1)<<8) + (*(p+2)<<16) + (*(p+3)<<24))
#define MOTOSHORT(p) (((*(p))<<8) + (*(p+1)))
#define MOTOLONG(p) (((*p)<<24) + ((*(p+1))<<16) + ((*(p+2))<<8) + (*(p+3)))
// Must be a 32-bit target processor
#define REGISTER_WIDTH 32
#endif // __ANIMATEDGIF__

View File

@ -0,0 +1,182 @@
// Copyright 2020 BitBank Software, Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===========================================================================
//
// Modified 2023 by Mark Komus to work for CircuitPython
//
#ifndef __ANIMATEDGIF__
#define __ANIMATEDGIF__
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
//
// GIF Animator
// Written by Larry Bank
// Copyright (c) 2020 BitBank Software, Inc.
// bitbank@pobox.com
//
// Designed to decode images up to 480x320
// using less than 22K of RAM
//
/* GIF Defines and variables */
#define MAX_CHUNK_SIZE 255
#define LZW_BUF_SIZE (6 * MAX_CHUNK_SIZE)
#define LZW_HIGHWATER (4 * MAX_CHUNK_SIZE)
#define MAX_WIDTH 320
#define FILE_BUF_SIZE 4096
#define PIXEL_FIRST 0
#define PIXEL_LAST 4096
#define LINK_UNUSED 5911 // 0x1717 to use memset
#define LINK_END 5912
#define MAX_HASH 5003
#define MAXMAXCODE 4096
enum {
GIF_PALETTE_RGB565_LE = 0, // little endian (default)
GIF_PALETTE_RGB565_BE, // big endian
GIF_PALETTE_RGB888 // original 24-bpp entries
};
// for compatibility with older code
#define LITTLE_ENDIAN_PIXELS GIF_PALETTE_RGB565_LE
#define BIG_ENDIAN_PIXELS GIF_PALETTE_RGB565_BE
//
// Draw callback pixel type
// RAW = 8-bit palettized pixels requiring transparent pixel handling
// COOKED = 16 or 24-bpp fully rendered pixels ready for display
//
enum {
GIF_DRAW_RAW = 0,
GIF_DRAW_COOKED
};
enum {
GIF_SUCCESS = 0,
GIF_DECODE_ERROR,
GIF_TOO_WIDE,
GIF_INVALID_PARAMETER,
GIF_UNSUPPORTED_FEATURE,
GIF_FILE_NOT_OPEN,
GIF_EARLY_EOF,
GIF_EMPTY_FRAME,
GIF_BAD_FILE,
GIF_ERROR_MEMORY
};
typedef struct gif_file_tag
{
int32_t iPos; // current file position
int32_t iSize; // file size
uint8_t *pData; // memory file pointer
void *fHandle; // class pointer to File/SdFat or whatever you want
} GIFFILE;
typedef struct gif_info_tag
{
int32_t iFrameCount; // total frames in file
int32_t iDuration; // duration of animation in milliseconds
int32_t iMaxDelay; // maximum frame delay
int32_t iMinDelay; // minimum frame delay
} GIFINFO;
typedef struct gif_draw_tag
{
int iX, iY; // Corner offset of this frame on the canvas
int y; // current line being drawn (0 = top line of image)
int iWidth, iHeight; // size of this frame
void *pUser; // user supplied pointer
uint8_t *pPixels; // 8-bit source pixels for this line
uint16_t *pPalette; // little or big-endian RGB565 palette entries (default)
uint8_t *pPalette24; // RGB888 palette (optional)
uint8_t ucTransparent; // transparent color
uint8_t ucHasTransparency; // flag indicating the transparent color is in use
uint8_t ucDisposalMethod; // frame disposal method
uint8_t ucBackground; // background color
uint8_t ucIsGlobalPalette; // Flag to indicate that a global palette, rather than a local palette is being used
} GIFDRAW;
// Callback function prototypes
typedef int32_t (GIF_READ_CALLBACK)(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen);
typedef int32_t (GIF_SEEK_CALLBACK)(GIFFILE *pFile, int32_t iPosition);
typedef void (GIF_DRAW_CALLBACK)(GIFDRAW *pDraw);
typedef void * (GIF_OPEN_CALLBACK)(const char *szFilename, int32_t *pFileSize);
typedef void (GIF_CLOSE_CALLBACK)(void *pHandle);
typedef void * (GIF_ALLOC_CALLBACK)(uint32_t iSize);
typedef void (GIF_FREE_CALLBACK)(void *buffer);
//
// our private structure to hold a GIF image decode state
//
typedef struct gif_image_tag
{
int iWidth, iHeight, iCanvasWidth, iCanvasHeight;
int iX, iY; // GIF corner offset
int iBpp;
int iError; // last error
int iFrameDelay; // delay in milliseconds for this frame
int iRepeatCount; // NETSCAPE animation repeat count. 0=forever
int iXCount, iYCount; // decoding position in image (countdown values)
int iLZWOff; // current LZW data offset
int iLZWSize; // current quantity of data in the LZW buffer
int iCommentPos; // file offset of start of comment data
short sCommentLen; // length of comment
GIF_READ_CALLBACK *pfnRead;
GIF_SEEK_CALLBACK *pfnSeek;
GIF_DRAW_CALLBACK *pfnDraw;
GIF_OPEN_CALLBACK *pfnOpen;
GIF_CLOSE_CALLBACK *pfnClose;
GIFFILE GIFFile;
void *pUser;
//unsigned char *pFrameBuffer;
unsigned int *pFrameBuffer;
unsigned char *pPixels, *pOldPixels;
unsigned char ucLineBuf[MAX_WIDTH]; // current line
unsigned char ucFileBuf[FILE_BUF_SIZE]; // holds temp data and pixel stack
unsigned short pPalette[384]; // can hold RGB565 or RGB888 - set in begin()
unsigned short pLocalPalette[384]; // color palettes for GIF images
unsigned char ucLZW[LZW_BUF_SIZE]; // holds 6 chunks (6x255) of GIF LZW data packed together
unsigned short usGIFTable[4096];
unsigned char ucGIFPixels[8192];
unsigned char bEndOfFrame;
unsigned char ucGIFBits, ucBackground, ucTransparent, ucCodeStart, ucMap, bUseLocalPalette;
unsigned char ucPaletteType; // RGB565 or RGB888
unsigned char ucDrawType; // RAW or COOKED
} GIFIMAGE;
// C interface
int GIF_openRAM(GIFIMAGE *pGIF, uint8_t *pData, int iDataSize, GIF_DRAW_CALLBACK *pfnDraw);
int GIF_openFile(GIFIMAGE *pGIF, const char *szFilename, GIF_DRAW_CALLBACK *pfnDraw);
void GIF_close(GIFIMAGE *pGIF);
void GIF_begin(GIFIMAGE *pGIF, unsigned char ucPaletteType);
void GIF_reset(GIFIMAGE *pGIF);
int GIF_playFrame(GIFIMAGE *pGIF, int *delayMilliseconds, void *pUser);
int GIF_getCanvasWidth(GIFIMAGE *pGIF);
int GIF_getCanvasHeight(GIFIMAGE *pGIF);
int GIF_getComment(GIFIMAGE *pGIF, char *destBuffer);
int GIF_getInfo(GIFIMAGE *pGIF, GIFINFO *pInfo);
int GIF_getLastError(GIFIMAGE *pGIF);
int GIF_getLoopCount(GIFIMAGE *pGIF);
int GIF_init(GIFIMAGE *pGIF);
void GIF_setDrawCallback(GIFIMAGE *pGIF, GIF_DRAW_CALLBACK *pfnDraw);
void GIF_scaleHalf(uint16_t *pCurrent, uint16_t *pPrev, int iWidth, int bBigEndian);
// Due to unaligned memory causing an exception, we have to do these macros the slow way
#define INTELSHORT(p) ((*p) + (*(p + 1) << 8))
#define INTELLONG(p) ((*p) + (*(p + 1) << 8) + (*(p + 2) << 16) + (*(p + 3) << 24))
#define MOTOSHORT(p) (((*(p)) << 8) + (*(p + 1)))
#define MOTOLONG(p) (((*p) << 24) + ((*(p + 1)) << 16) + ((*(p + 2)) << 8) + (*(p + 3)))
// Must be a 32-bit target processor
#define REGISTER_WIDTH 32
#endif // __ANIMATEDGIF__

View File

@ -0,0 +1,5 @@
This library is from the AnimatedGIF Arduino GIF decoder by Larry Bank.
Released under the Apache License 2.0
[AnimatedGIF](https://github.com/bitbank2/AnimatedGIF)
It has been modified for use in CircuitPython by Mark Komus.

1043
lib/AnimatedGIF/gif.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -37,6 +37,7 @@ CIRCUITPY_USB_MIDI = 0
CIRCUITPY_VECTORIO = 0 CIRCUITPY_VECTORIO = 0
CIRCUITPY_BUSDEVICE = 0 CIRCUITPY_BUSDEVICE = 0
CIRCUITPY_BITMAPTOOLS = 0 CIRCUITPY_BITMAPTOOLS = 0
CIRCUITPY_GIFIO = 0
CIRCUITPY_WATCHDOG = 0 CIRCUITPY_WATCHDOG = 0
CIRCUITPY_AUDIOIO = 1 CIRCUITPY_AUDIOIO = 1

View File

@ -26,7 +26,6 @@ CFLAGS += -DCIRCUITPY_QRIO=1
$(BUILD)/lib/quirc/lib/%.o: CFLAGS += -Wno-shadow -Wno-sign-compare -include shared-module/qrio/quirc_alloc.h $(BUILD)/lib/quirc/lib/%.o: CFLAGS += -Wno-shadow -Wno-sign-compare -include shared-module/qrio/quirc_alloc.h
SRC_BITMAP := \ SRC_BITMAP := \
$(patsubst ../../%,%,$(wildcard ../../shared-bindings/gifio/*.c ../../shared-module/gifio/*.c)) \
shared/runtime/context_manager_helpers.c \ shared/runtime/context_manager_helpers.c \
displayio_min.c \ displayio_min.c \
shared-bindings/aesio/aes.c \ shared-bindings/aesio/aes.c \

View File

@ -597,6 +597,7 @@ SRC_SHARED_MODULE_ALL = \
getpass/__init__.c \ getpass/__init__.c \
gifio/__init__.c \ gifio/__init__.c \
gifio/GifWriter.c \ gifio/GifWriter.c \
gifio/OnDiskGif.c \
imagecapture/ParallelImageCapture.c \ imagecapture/ParallelImageCapture.c \
ipaddress/IPv4Address.c \ ipaddress/IPv4Address.c \
ipaddress/__init__.c \ ipaddress/__init__.c \
@ -702,6 +703,13 @@ SRC_MOD += $(addprefix lib/protomatter/src/, \
$(BUILD)/lib/protomatter/src/core.o: CFLAGS += -include "shared-module/rgbmatrix/allocator.h" -DCIRCUITPY -Wno-missing-braces -Wno-missing-prototypes $(BUILD)/lib/protomatter/src/core.o: CFLAGS += -include "shared-module/rgbmatrix/allocator.h" -DCIRCUITPY -Wno-missing-braces -Wno-missing-prototypes
endif endif
ifeq ($(CIRCUITPY_GIFIO),1)
SRC_MOD += $(addprefix lib/AnimatedGIF/, \
gif.c \
)
$(BUILD)/lib/AnimatedGIF/gif.o: CFLAGS += -DCIRCUITPY
endif
ifeq ($(CIRCUITPY_ZLIB),1) ifeq ($(CIRCUITPY_ZLIB),1)
SRC_MOD += $(addprefix lib/uzlib/, \ SRC_MOD += $(addprefix lib/uzlib/, \
tinflate.c \ tinflate.c \

View File

@ -248,7 +248,8 @@ CIRCUITPY_GETPASS ?= $(CIRCUITPY_FULL_BUILD)
CFLAGS += -DCIRCUITPY_GETPASS=$(CIRCUITPY_GETPASS) CFLAGS += -DCIRCUITPY_GETPASS=$(CIRCUITPY_GETPASS)
ifeq ($(CIRCUITPY_DISPLAYIO),1) ifeq ($(CIRCUITPY_DISPLAYIO),1)
CIRCUITPY_GIFIO ?= $(CIRCUITPY_CAMERA) #CIRCUITPY_GIFIO ?= $(CIRCUITPY_CAMERA)
CIRCUITPY_GIFIO ?= 1
else else
CIRCUITPY_GIFIO ?= 0 CIRCUITPY_GIFIO ?= 0
endif endif

View File

@ -0,0 +1,205 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Mark Komus
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "shared-bindings/gifio/OnDiskGif.h"
#include <stdint.h>
#include "py/runtime.h"
#include "py/objproperty.h"
#include "supervisor/shared/translate/translate.h"
#include "shared-bindings/gifio/OnDiskGif.h"
//| class OnDiskGif:
//| """Loads one frame of a GIF into memory at a time.
//|
//| .. code-block:: Python
//|
//| import board
//| import gifio
//| import displayio
//| import time
//|
//| splash = displayio.Group()
//| board.DISPLAY.show(splash)
//|
//| odg = gifio.OnDiskGif('/sample.gif')
//| odg.next_frame() # Load the first frame
//| face = displayio.TileGrid(odg, pixel_shader=displayio.ColorConverter(input_colorspace=displayio.Colorspace.RGB565))
//| splash.append(face)
//| board.DISPLAY.refresh()
//|
//| # Wait forever
//| while True:
//| gif.next_frame()
//| time.sleep(0.1)"""
//|
//| def __init__(self, file: str) -> None:
//| """Create an OnDiskGif object with the given file.
//|
//| :param file file: The name of the GIF file.
//|
//| """
//| ...
STATIC mp_obj_t gifio_ondiskgif_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
mp_arg_check_num(n_args, n_kw, 1, 1, false);
mp_obj_t arg = all_args[0];
if (mp_obj_is_str(arg)) {
arg = mp_call_function_2(MP_OBJ_FROM_PTR(&mp_builtin_open_obj), arg, MP_ROM_QSTR(MP_QSTR_rb));
}
if (!mp_obj_is_type(arg, &mp_type_fileio)) {
mp_raise_TypeError(translate("file must be a file opened in byte mode"));
}
gifio_ondiskgif_t *self = m_new_obj(gifio_ondiskgif_t);
self->base.type = &gifio_ondiskgif_type;
common_hal_gifio_ondiskgif_construct(self, MP_OBJ_TO_PTR(arg));
return MP_OBJ_FROM_PTR(self);
}
//| width: int
//| """Width of the gif. (read only)"""
STATIC mp_obj_t gifio_ondiskgif_obj_get_width(mp_obj_t self_in) {
gifio_ondiskgif_t *self = MP_OBJ_TO_PTR(self_in);
return MP_OBJ_NEW_SMALL_INT(common_hal_gifio_ondiskgif_get_width(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(gifio_ondiskgif_get_width_obj, gifio_ondiskgif_obj_get_width);
MP_PROPERTY_GETTER(gifio_ondiskgif_width_obj,
(mp_obj_t)&gifio_ondiskgif_get_width_obj);
//| height: int
//| """Height of the gif. (read only)"""
STATIC mp_obj_t gifio_ondiskgif_obj_get_height(mp_obj_t self_in) {
gifio_ondiskgif_t *self = MP_OBJ_TO_PTR(self_in);
return MP_OBJ_NEW_SMALL_INT(common_hal_gifio_ondiskgif_get_height(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(gifio_ondiskgif_get_height_obj, gifio_ondiskgif_obj_get_height);
MP_PROPERTY_GETTER(gifio_ondiskgif_height_obj,
(mp_obj_t)&gifio_ondiskgif_get_height_obj);
//| bitmap: displayio.Bitmap
//| """The bitmap used to hold the current frame."""
STATIC mp_obj_t gifio_ondiskgif_obj_get_bitmap(mp_obj_t self_in) {
gifio_ondiskgif_t *self = MP_OBJ_TO_PTR(self_in);
return common_hal_gifio_ondiskgif_get_bitmap(self);
}
MP_DEFINE_CONST_FUN_OBJ_1(gifio_ondiskgif_get_bitmap_obj, gifio_ondiskgif_obj_get_bitmap);
MP_PROPERTY_GETTER(gifio_ondiskgif_bitmap_obj,
(mp_obj_t)&gifio_ondiskgif_get_bitmap_obj);
//| def next_frame(self) -> float:
//| """Loads the next frame. Returns expected delay before the next frame in seconds."""
STATIC mp_obj_t gifio_ondiskgif_obj_next_frame(mp_obj_t self_in) {
gifio_ondiskgif_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_float((float)common_hal_gifio_ondiskgif_next_frame(self, true) / 1000);
}
MP_DEFINE_CONST_FUN_OBJ_1(gifio_ondiskgif_next_frame_obj, gifio_ondiskgif_obj_next_frame);
//| duration: float
//| """Returns the total duration of the GIF in seconds. (read only)"""
STATIC mp_obj_t gifio_ondiskgif_obj_get_duration(mp_obj_t self_in) {
gifio_ondiskgif_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_float((float)common_hal_gifio_ondiskgif_get_duration(self) / 1000);
}
MP_DEFINE_CONST_FUN_OBJ_1(gifio_ondiskgif_get_duration_obj, gifio_ondiskgif_obj_get_duration);
MP_PROPERTY_GETTER(gifio_ondiskgif_duration_obj,
(mp_obj_t)&gifio_ondiskgif_get_duration_obj);
//| frame_count: int
//| """Returns the number of frames in the GIF. (read only)"""
STATIC mp_obj_t gifio_ondiskgif_obj_get_frame_count(mp_obj_t self_in) {
gifio_ondiskgif_t *self = MP_OBJ_TO_PTR(self_in);
return MP_OBJ_NEW_SMALL_INT(common_hal_gifio_ondiskgif_get_frame_count(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(gifio_ondiskgif_get_frame_count_obj, gifio_ondiskgif_obj_get_frame_count);
MP_PROPERTY_GETTER(gifio_ondiskgif_frame_count_obj,
(mp_obj_t)&gifio_ondiskgif_get_frame_count_obj);
//| min_delay: float
//| """The minimum delay found between frames. (read only)"""
STATIC mp_obj_t gifio_ondiskgif_obj_get_min_delay(mp_obj_t self_in) {
gifio_ondiskgif_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_float((float)common_hal_gifio_ondiskgif_get_min_delay(self) / 1000);
}
MP_DEFINE_CONST_FUN_OBJ_1(gifio_ondiskgif_get_min_delay_obj, gifio_ondiskgif_obj_get_min_delay);
MP_PROPERTY_GETTER(gifio_ondiskgif_min_delay_obj,
(mp_obj_t)&gifio_ondiskgif_get_min_delay_obj);
//| max_delay: float
//| """The maximum delay found between frames. (read only)"""
//|
STATIC mp_obj_t gifio_ondiskgif_obj_get_max_delay(mp_obj_t self_in) {
gifio_ondiskgif_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_float((float)common_hal_gifio_ondiskgif_get_max_delay(self) / 1000);
}
MP_DEFINE_CONST_FUN_OBJ_1(gifio_ondiskgif_get_max_delay_obj, gifio_ondiskgif_obj_get_max_delay);
MP_PROPERTY_GETTER(gifio_ondiskgif_max_delay_obj,
(mp_obj_t)&gifio_ondiskgif_get_max_delay_obj);
STATIC const mp_rom_map_elem_t gifio_ondiskgif_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&gifio_ondiskgif_height_obj) },
{ MP_ROM_QSTR(MP_QSTR_bitmap), MP_ROM_PTR(&gifio_ondiskgif_bitmap_obj) },
{ MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&gifio_ondiskgif_width_obj) },
{ MP_ROM_QSTR(MP_QSTR_next_frame), MP_ROM_PTR(&gifio_ondiskgif_next_frame_obj) },
{ MP_ROM_QSTR(MP_QSTR_duration), MP_ROM_PTR(&gifio_ondiskgif_duration_obj) },
{ MP_ROM_QSTR(MP_QSTR_frame_count), MP_ROM_PTR(&gifio_ondiskgif_frame_count_obj) },
{ MP_ROM_QSTR(MP_QSTR_min_delay), MP_ROM_PTR(&gifio_ondiskgif_min_delay_obj) },
{ MP_ROM_QSTR(MP_QSTR_max_delay), MP_ROM_PTR(&gifio_ondiskgif_max_delay_obj) },
};
STATIC MP_DEFINE_CONST_DICT(gifio_ondiskgif_locals_dict, gifio_ondiskgif_locals_dict_table);
const mp_obj_type_t gifio_ondiskgif_type = {
{ &mp_type_type },
.name = MP_QSTR_OnDiskGif,
.make_new = gifio_ondiskgif_make_new,
.locals_dict = (mp_obj_dict_t *)&gifio_ondiskgif_locals_dict,
};

View File

@ -0,0 +1,48 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Mark Komus
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_ONDISKGIF_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_ONDISKGIF_H
#include "shared-module/gifio/OnDiskGif.h"
#include "extmod/vfs_fat.h"
extern const mp_obj_type_t gifio_ondiskgif_type;
void common_hal_gifio_ondiskgif_construct(gifio_ondiskgif_t *self, pyb_file_obj_t *file);
uint32_t common_hal_gifio_ondiskgif_get_pixel(gifio_ondiskgif_t *bitmap,
int16_t x, int16_t y);
uint16_t common_hal_gifio_ondiskgif_get_height(gifio_ondiskgif_t *self);
mp_obj_t common_hal_gifio_ondiskgif_get_bitmap(gifio_ondiskgif_t *self);
uint16_t common_hal_gifio_ondiskgif_get_width(gifio_ondiskgif_t *self);
uint8_t common_hal_gifio_ondiskgif_next_frame(gifio_ondiskgif_t *self, bool setDirty);
int32_t common_hal_gifio_ondiskgif_get_duration(gifio_ondiskgif_t *self);
int32_t common_hal_gifio_ondiskgif_get_frame_count(gifio_ondiskgif_t *self);
int32_t common_hal_gifio_ondiskgif_get_min_delay(gifio_ondiskgif_t *self);
int32_t common_hal_gifio_ondiskgif_get_max_delay(gifio_ondiskgif_t *self);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_ONDISKGIF_H

View File

@ -27,6 +27,7 @@
#include "py/runtime.h" #include "py/runtime.h"
#include "py/mphal.h" #include "py/mphal.h"
#include "shared-bindings/gifio/GifWriter.h" #include "shared-bindings/gifio/GifWriter.h"
#include "shared-bindings/gifio/OnDiskGif.h"
#include "shared-bindings/util.h" #include "shared-bindings/util.h"
//| """Access GIF-format images //| """Access GIF-format images
@ -34,6 +35,7 @@
STATIC const mp_rom_map_elem_t gifio_module_globals_table[] = { STATIC const mp_rom_map_elem_t gifio_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_gifio) }, { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_gifio) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_GifWriter), MP_ROM_PTR(&gifio_gifwriter_type)}, { MP_OBJ_NEW_QSTR(MP_QSTR_GifWriter), MP_ROM_PTR(&gifio_gifwriter_type)},
{ MP_ROM_QSTR(MP_QSTR_OnDiskGif), MP_ROM_PTR(&gifio_ondiskgif_type) },
}; };
STATIC MP_DEFINE_CONST_DICT(gifio_module_globals, gifio_module_globals_table); STATIC MP_DEFINE_CONST_DICT(gifio_module_globals, gifio_module_globals_table);

View File

@ -0,0 +1,204 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Mark Komus
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "shared-bindings/gifio/OnDiskGif.h"
#include "shared-bindings/displayio/Bitmap.h"
#include <string.h>
#include "py/mperrno.h"
#include "py/runtime.h"
static int32_t GIFReadFile(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen) {
// mp_printf(&mp_plat_print, "GifReadFile len %d ", iLen);
uint32_t iBytesRead;
iBytesRead = iLen;
pyb_file_obj_t *f = pFile->fHandle;
// Note: If you read a file all the way to the last byte, seek() stops working
if ((pFile->iSize - pFile->iPos) < iLen) {
iBytesRead = pFile->iSize - pFile->iPos - 1; // <-- ugly work-around
}
if (iBytesRead <= 0) {
return 0;
}
UINT bytes_read;
if (f_read(&f->fp, pBuf, iBytesRead, &bytes_read) != FR_OK) {
mp_raise_OSError(MP_EIO);
}
pFile->iPos = f->fp.fptr;
// mp_printf(&mp_plat_print, " now at %d\n", pFile->iPos);
return bytes_read;
} /* GIFReadFile() */
static int32_t GIFSeekFile(GIFFILE *pFile, int32_t iPosition) {
// mp_printf(&mp_plat_print, "GifSeekFile %d ", iPosition);
pyb_file_obj_t *f = pFile->fHandle;
f_lseek(&f->fp, iPosition);
pFile->iPos = f->fp.fptr;
// mp_printf(&mp_plat_print, " now at %d\n", pFile->iPos);
return pFile->iPos;
} /* GIFSeekFile() */
static void GIFDraw(GIFDRAW *pDraw) {
// Called for every scan line of the image as it decodes
// The pixels delivered are the 8-bit native GIF output
// The palette is either RGB565 or the original 24-bit RGB values
// depending on the pixel type selected with gif.begin()
displayio_bitmap_t *bitmap = (displayio_bitmap_t *)pDraw->pUser;
uint8_t *s;
uint16_t *d;
int iWidth = pDraw->iWidth;
if (iWidth + pDraw->iX > bitmap->width) {
iWidth = bitmap->width - pDraw->iX;
}
if (pDraw->iY + pDraw->y >= bitmap->height || pDraw->iX >= bitmap->width || iWidth < 1) {
return;
}
int32_t row_start = (pDraw->y + pDraw->iY) * bitmap->stride;
uint32_t *row = bitmap->data + row_start;
s = pDraw->pPixels;
d = (uint16_t *)row;
uint16_t *pPal;
pPal = (uint16_t *)pDraw->pPalette;
if (pDraw->ucDisposalMethod == 2) { // restore to background color
// Not supported currently. Need to reset the area the previous frame occupied
// to the background color before the previous frame was drawn
// See: https://github.com/bitbank2/AnimatedGIF/issues/3
// To workaround clear the gif.bitmap object yourself as required.
}
uint8_t c, ucTransparent = pDraw->ucTransparent;
d += pDraw->iX;
if (pDraw->ucHasTransparency == 1) {
for (int x = 0; x < iWidth; x++)
{
c = *s++;
if (c != ucTransparent) {
*d = pPal[c];
}
d++;
}
} else {
for (int x = 0; x < iWidth; x++)
{
c = *s++;
*d++ = pPal[c];
}
}
}
void common_hal_gifio_ondiskgif_construct(gifio_ondiskgif_t *self, pyb_file_obj_t *file) {
// mp_printf(&mp_plat_print, "Begin OnDiskGif\n");
self->file = file;
GIF_begin(&self->gif, GIF_PALETTE_RGB565_BE);
self->gif.iError = GIF_SUCCESS;
self->gif.pfnRead = GIFReadFile;
self->gif.pfnSeek = GIFSeekFile;
self->gif.pfnDraw = GIFDraw;
self->gif.pfnClose = NULL;
self->gif.pfnOpen = NULL;
self->gif.GIFFile.fHandle = self->file;
f_rewind(&self->file->fp);
self->gif.GIFFile.iSize = (int32_t)f_size(&self->file->fp);
int result = GIF_init(&self->gif);
if (result != 1) {
mp_arg_error_invalid(MP_QSTR_file);
}
displayio_bitmap_t *bitmap = m_new_obj(displayio_bitmap_t);
bitmap->base.type = &displayio_bitmap_type;
common_hal_displayio_bitmap_construct(bitmap, self->gif.iCanvasWidth, self->gif.iCanvasHeight, 16);
self->bitmap = bitmap;
GIFINFO info;
GIF_getInfo(&self->gif, &info);
self->duration = info.iDuration;
self->frame_count = info.iFrameCount;
self->min_delay = info.iMinDelay;
self->max_delay = info.iMaxDelay;
// mp_printf(&mp_plat_print, "GIF_init returned %d %x\n", result, bitmap->data);
}
uint16_t common_hal_gifio_ondiskgif_get_height(gifio_ondiskgif_t *self) {
return (uint16_t)self->gif.iCanvasHeight;
}
uint16_t common_hal_gifio_ondiskgif_get_width(gifio_ondiskgif_t *self) {
return (uint16_t)self->gif.iCanvasWidth;
}
mp_obj_t common_hal_gifio_ondiskgif_get_bitmap(gifio_ondiskgif_t *self) {
return MP_OBJ_FROM_PTR(self->bitmap);
}
int32_t common_hal_gifio_ondiskgif_get_duration(gifio_ondiskgif_t *self) {
return self->duration;
}
int32_t common_hal_gifio_ondiskgif_get_frame_count(gifio_ondiskgif_t *self) {
return self->frame_count;
}
int32_t common_hal_gifio_ondiskgif_get_min_delay(gifio_ondiskgif_t *self) {
return self->min_delay;
}
int32_t common_hal_gifio_ondiskgif_get_max_delay(gifio_ondiskgif_t *self) {
return self->max_delay;
}
uint8_t common_hal_gifio_ondiskgif_next_frame(gifio_ondiskgif_t *self, bool setDirty) {
int nextDelay = 0;
int result = GIF_playFrame(&self->gif, &nextDelay, self->bitmap);
if ((result >= 0) && (setDirty)) {
displayio_area_t dirty_area = {
.x1 = 0,
.y1 = 0,
.x2 = self->bitmap->width,
.y2 = self->bitmap->height,
};
displayio_bitmap_set_dirty_area(self->bitmap, &dirty_area);
}
return nextDelay;
}

View File

@ -0,0 +1,51 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Mark Komus
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_ONDISKGIF_H
#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_ONDISKGIF_H
#include <stdbool.h>
#include <stdint.h>
#include "py/obj.h"
#include "lib/AnimatedGIF/AnimatedGIF_circuitpy.h"
#include "shared-module/displayio/Bitmap.h"
#include "extmod/vfs_fat.h"
typedef struct {
mp_obj_base_t base;
GIFIMAGE gif;
pyb_file_obj_t *file;
displayio_bitmap_t *bitmap;
int32_t duration;
int32_t frame_count;
int32_t min_delay;
int32_t max_delay;
} gifio_ondiskgif_t;
#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_ONDISKGIF_H

View File

@ -33,17 +33,17 @@ builtins micropython _asyncio _thread
_uasyncio aesio array binascii _uasyncio aesio array binascii
bitmaptools btree cexample cmath bitmaptools btree cexample cmath
collections cppexample displayio errno collections cppexample displayio errno
ffi framebuf gc gifio ffi framebuf gc hashlib
hashlib json math qrio json math qrio rainbowio
rainbowio re sys termios re sys termios traceback
traceback ubinascii uctypes uerrno ubinascii uctypes uerrno uheapq
uheapq uio ujson ulab uio ujson ulab ulab.numpy
ulab.numpy ulab.numpy.fft ulab.numpy.linalg ulab.numpy.fft ulab.numpy.linalg ulab.scipy
ulab.scipy ulab.scipy.linalg ulab.scipy.linalg ulab.scipy.optimize
ulab.scipy.optimize ulab.scipy.signal ulab.scipy.signal ulab.scipy.special
ulab.scipy.special ulab.utils uos ulab.utils uos urandom ure
urandom ure uselect ustruct uselect ustruct utime utimeq
utime utimeq uzlib zlib uzlib zlib
ime ime
utime utimeq utime utimeq