Merge pull request #7549 from gamblor21/gif_displayio_support
Animated GIF support
This commit is contained in:
commit
a8b34bd067
236
lib/AnimatedGIF/AnimatedGIF.cpp
Normal file
236
lib/AnimatedGIF/AnimatedGIF.cpp
Normal 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() */
|
216
lib/AnimatedGIF/AnimatedGIF.h
Normal file
216
lib/AnimatedGIF/AnimatedGIF.h
Normal 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__
|
182
lib/AnimatedGIF/AnimatedGIF_circuitpy.h
Normal file
182
lib/AnimatedGIF/AnimatedGIF_circuitpy.h
Normal 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__
|
5
lib/AnimatedGIF/README.md
Normal file
5
lib/AnimatedGIF/README.md
Normal 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
1043
lib/AnimatedGIF/gif.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -37,6 +37,7 @@ CIRCUITPY_USB_MIDI = 0
|
||||
CIRCUITPY_VECTORIO = 0
|
||||
CIRCUITPY_BUSDEVICE = 0
|
||||
CIRCUITPY_BITMAPTOOLS = 0
|
||||
CIRCUITPY_GIFIO = 0
|
||||
CIRCUITPY_WATCHDOG = 0
|
||||
|
||||
CIRCUITPY_AUDIOIO = 1
|
||||
|
@ -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
|
||||
|
||||
SRC_BITMAP := \
|
||||
$(patsubst ../../%,%,$(wildcard ../../shared-bindings/gifio/*.c ../../shared-module/gifio/*.c)) \
|
||||
shared/runtime/context_manager_helpers.c \
|
||||
displayio_min.c \
|
||||
shared-bindings/aesio/aes.c \
|
||||
|
@ -597,6 +597,7 @@ SRC_SHARED_MODULE_ALL = \
|
||||
getpass/__init__.c \
|
||||
gifio/__init__.c \
|
||||
gifio/GifWriter.c \
|
||||
gifio/OnDiskGif.c \
|
||||
imagecapture/ParallelImageCapture.c \
|
||||
ipaddress/IPv4Address.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
|
||||
endif
|
||||
|
||||
ifeq ($(CIRCUITPY_GIFIO),1)
|
||||
SRC_MOD += $(addprefix lib/AnimatedGIF/, \
|
||||
gif.c \
|
||||
)
|
||||
$(BUILD)/lib/AnimatedGIF/gif.o: CFLAGS += -DCIRCUITPY
|
||||
endif
|
||||
|
||||
ifeq ($(CIRCUITPY_ZLIB),1)
|
||||
SRC_MOD += $(addprefix lib/uzlib/, \
|
||||
tinflate.c \
|
||||
|
@ -248,7 +248,8 @@ CIRCUITPY_GETPASS ?= $(CIRCUITPY_FULL_BUILD)
|
||||
CFLAGS += -DCIRCUITPY_GETPASS=$(CIRCUITPY_GETPASS)
|
||||
|
||||
ifeq ($(CIRCUITPY_DISPLAYIO),1)
|
||||
CIRCUITPY_GIFIO ?= $(CIRCUITPY_CAMERA)
|
||||
#CIRCUITPY_GIFIO ?= $(CIRCUITPY_CAMERA)
|
||||
CIRCUITPY_GIFIO ?= 1
|
||||
else
|
||||
CIRCUITPY_GIFIO ?= 0
|
||||
endif
|
||||
|
205
shared-bindings/gifio/OnDiskGif.c
Normal file
205
shared-bindings/gifio/OnDiskGif.c
Normal 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,
|
||||
};
|
48
shared-bindings/gifio/OnDiskGif.h
Normal file
48
shared-bindings/gifio/OnDiskGif.h
Normal 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
|
@ -27,6 +27,7 @@
|
||||
#include "py/runtime.h"
|
||||
#include "py/mphal.h"
|
||||
#include "shared-bindings/gifio/GifWriter.h"
|
||||
#include "shared-bindings/gifio/OnDiskGif.h"
|
||||
#include "shared-bindings/util.h"
|
||||
|
||||
//| """Access GIF-format images
|
||||
@ -34,6 +35,7 @@
|
||||
STATIC const mp_rom_map_elem_t gifio_module_globals_table[] = {
|
||||
{ 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_ROM_QSTR(MP_QSTR_OnDiskGif), MP_ROM_PTR(&gifio_ondiskgif_type) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(gifio_module_globals, gifio_module_globals_table);
|
||||
|
204
shared-module/gifio/OnDiskGif.c
Normal file
204
shared-module/gifio/OnDiskGif.c
Normal 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;
|
||||
}
|
51
shared-module/gifio/OnDiskGif.h
Normal file
51
shared-module/gifio/OnDiskGif.h
Normal 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
|
@ -33,17 +33,17 @@ builtins micropython _asyncio _thread
|
||||
_uasyncio aesio array binascii
|
||||
bitmaptools btree cexample cmath
|
||||
collections cppexample displayio errno
|
||||
ffi framebuf gc gifio
|
||||
hashlib json math qrio
|
||||
rainbowio re sys termios
|
||||
traceback ubinascii uctypes uerrno
|
||||
uheapq uio ujson ulab
|
||||
ulab.numpy ulab.numpy.fft ulab.numpy.linalg
|
||||
ulab.scipy ulab.scipy.linalg
|
||||
ulab.scipy.optimize ulab.scipy.signal
|
||||
ulab.scipy.special ulab.utils uos
|
||||
urandom ure uselect ustruct
|
||||
utime utimeq uzlib zlib
|
||||
ffi framebuf gc hashlib
|
||||
json math qrio rainbowio
|
||||
re sys termios traceback
|
||||
ubinascii uctypes uerrno uheapq
|
||||
uio ujson ulab ulab.numpy
|
||||
ulab.numpy.fft ulab.numpy.linalg ulab.scipy
|
||||
ulab.scipy.linalg ulab.scipy.optimize
|
||||
ulab.scipy.signal ulab.scipy.special
|
||||
ulab.utils uos urandom ure
|
||||
uselect ustruct utime utimeq
|
||||
uzlib zlib
|
||||
ime
|
||||
|
||||
utime utimeq
|
||||
|
Loading…
Reference in New Issue
Block a user