mirror of
https://github.com/thelastoutpostworkshop/ESP32ManyRoundScreenTest.git
synced 2025-09-26 23:09:22 +08:00
modified Animated GIF library
This commit is contained in:
parent
cf337dd7c5
commit
2941ac7127
245
AnimatedGIF.cpp
Normal file
245
AnimatedGIF.cpp
Normal file
@ -0,0 +1,245 @@
|
||||
//
|
||||
// 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.h"
|
||||
|
||||
//
|
||||
// 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;
|
||||
|
||||
// Modified from the original library to use RAM instead of static memory
|
||||
_gif.usGIFTable = (unsigned short *)malloc(4096 * sizeof(unsigned short));
|
||||
if (_gif.usGIFTable == NULL)
|
||||
{
|
||||
// Handle memory allocation failure
|
||||
return GIF_ERROR_MEMORY; // or an appropriate error code
|
||||
}
|
||||
|
||||
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::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);
|
||||
|
||||
// Modified from the original library
|
||||
free(_gif.usGIFTable);
|
||||
_gif.usGIFTable = NULL;
|
||||
} /* 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() */
|
213
AnimatedGIF.h
Normal file
213
AnimatedGIF.h
Normal file
@ -0,0 +1,213 @@
|
||||
// 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
|
||||
} 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 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 short *usGIFTable;
|
||||
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 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);
|
||||
#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__
|
@ -1,33 +1,38 @@
|
||||
// Animated GIF with Multiple Displays
|
||||
// Animated GIF with Round Display
|
||||
//
|
||||
// ESP32 40MHz SPI single frame rendering performance
|
||||
// Note: no DMA performance gain on smaller images or transparent pixel GIFs
|
||||
//
|
||||
#define USE_DMA // ESP32 ~1.25x single frame rendering performance boost for badgers.h
|
||||
// Note: Do not use SPI DMA if reading GIF images from SPI SD card on same bus as TFT
|
||||
#define NORMAL_SPEED // Comment out for rame rate for render speed test
|
||||
|
||||
#include <SPI.h>
|
||||
#include <TFT_eSPI.h> // Install this library with the Arduino IDE Library Manager
|
||||
#include <AnimatedGIF.h> // Install this library with the Arduino IDE Library Manager
|
||||
#include <TFT_eSPI.h>
|
||||
#include "AnimatedGIF.h"
|
||||
|
||||
// Examples images
|
||||
#include "images/hyperspace.h"
|
||||
#include "images/nostromo.h"
|
||||
#include "images/hud_1.h"
|
||||
#include "images/hud_2.h"
|
||||
#include "images/hud_5.h"
|
||||
#include "images/hud_6.h"
|
||||
#include "images/hud_7.h"
|
||||
#include "images/darthvader.h"
|
||||
#include "images/x_wing.h"
|
||||
#include "images/hud_1.h"
|
||||
#include "images/hud_6.h"
|
||||
#include "images/hud_2.h"
|
||||
|
||||
const int NUM_DISPLAYS = 4; // Adjust this value based on the number of displays
|
||||
AnimatedGIF gifs[NUM_DISPLAYS];
|
||||
const int NUM_DISPLAYS = 6; // Adjust this value based on the number of displays
|
||||
const int CS_PINS[NUM_DISPLAYS] = {19, 22, 21,32,33,25}; // Add more pins if you have more displays
|
||||
|
||||
// Define CS pins for all displays
|
||||
const int CS_PINS[NUM_DISPLAYS] = {19, 22, 21, 32}; // Add more pins if you have more displays
|
||||
// const int CS_PINS[NUM_DISPLAYS] = {19, 22, 21, 32, 33}; // Add more pins if you have more displays
|
||||
AnimatedGIF gif_1;
|
||||
AnimatedGIF gif_2;
|
||||
AnimatedGIF gif_3;
|
||||
AnimatedGIF gif_4;
|
||||
AnimatedGIF gif_5;
|
||||
AnimatedGIF gif_6;
|
||||
|
||||
// Define the images for all displays
|
||||
const uint8_t *GIF_IMAGES[NUM_DISPLAYS] = {x_wing, darthvader, hud_6, hud_1}; // Add more images if needed
|
||||
const size_t GIF_SIZES[NUM_DISPLAYS] = {sizeof(x_wing), sizeof(darthvader), sizeof(hud_6), sizeof(hud_1)}; // Add more sizes if needed
|
||||
|
||||
//Free Heap with 3 Animated GIF : 241248
|
||||
//Free Heap with 4 Animated GIF : 217332
|
||||
// ld.exe: region `dram0_0_seg' overflowed by 17664 bytes
|
||||
// ld.exe: region `dram0_0_seg' overflowed by 17264 bytes
|
||||
|
||||
TFT_eSPI tft;
|
||||
TFT_eSPI tft = TFT_eSPI();
|
||||
|
||||
void setup()
|
||||
{
|
||||
@ -37,44 +42,49 @@ void setup()
|
||||
for (int i = 0; i < NUM_DISPLAYS; i++)
|
||||
{
|
||||
pinMode(CS_PINS[i], OUTPUT);
|
||||
digitalWrite(CS_PINS[i], HIGH); // Deselect the display
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_DISPLAYS; i++)
|
||||
{
|
||||
digitalWrite(CS_PINS[i], LOW); // Select the display
|
||||
digitalWrite(CS_PINS[i], LOW); // select the display
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.setRotation(2); // Adjust Rotation of your screen (0-3)
|
||||
gifs[i].begin(BIG_ENDIAN_PIXELS);
|
||||
if (!gifs[i].open((uint8_t *)GIF_IMAGES[i], GIF_SIZES[i], GIFDraw))
|
||||
{
|
||||
Serial.printf("Failed to open GIF%d\n", i + 1);
|
||||
Serial.printf("Last Error=%d", gifs[i].getLastError());
|
||||
Serial.printf("Image size=%d", sizeof(GIF_IMAGES[i]));
|
||||
}
|
||||
digitalWrite(CS_PINS[i], HIGH); // Deselect the display
|
||||
}
|
||||
Serial.printf("Free Heap = %ld",ESP.getFreeHeap());
|
||||
}
|
||||
|
||||
openGif(&gif_1, hyperspace, sizeof(hyperspace));
|
||||
openGif(&gif_2, hud_6, sizeof(hud_6));
|
||||
openGif(&gif_3, nostromo, sizeof(nostromo));
|
||||
openGif(&gif_4, x_wing, sizeof(x_wing));
|
||||
openGif(&gif_5, hud_2, sizeof(hud_2));
|
||||
openGif(&gif_6, hud_7, sizeof(hud_7));
|
||||
}
|
||||
void loop()
|
||||
{
|
||||
for (int i = 0; i < NUM_DISPLAYS; i++)
|
||||
playGif(&gif_1, 0);
|
||||
playGif(&gif_2, 1);
|
||||
playGif(&gif_3, 2);
|
||||
playGif(&gif_4, 3);
|
||||
playGif(&gif_5, 4);
|
||||
playGif(&gif_6, 5);
|
||||
}
|
||||
|
||||
void openGif(AnimatedGIF *gif, const uint8_t *gifImage, int gifSize)
|
||||
{
|
||||
gif->begin(BIG_ENDIAN_PIXELS);
|
||||
if (!gif->open((uint8_t *)gifImage, gifSize, GIFDraw))
|
||||
{
|
||||
digitalWrite(CS_PINS[i], LOW); // Select the display
|
||||
Serial.printf("Could not open gif \n");
|
||||
}
|
||||
}
|
||||
|
||||
void playGif(AnimatedGIF *gif, int screenIndex)
|
||||
{
|
||||
digitalWrite(CS_PINS[screenIndex], LOW); // Select the display
|
||||
tft.startWrite();
|
||||
|
||||
int res = gifs[i].playFrame(false, NULL);
|
||||
int res = gif->playFrame(false, NULL);
|
||||
if (res == 0)
|
||||
{
|
||||
// If no more frames are available, reset the GIF to the beginning
|
||||
gifs[i].reset();
|
||||
gifs[i].playFrame(false, NULL);
|
||||
gif->reset();
|
||||
gif->playFrame(false, NULL);
|
||||
}
|
||||
|
||||
tft.endWrite();
|
||||
|
||||
digitalWrite(CS_PINS[i], HIGH); // Deselect the display
|
||||
}
|
||||
digitalWrite(CS_PINS[screenIndex], HIGH); // Deselect the display
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user