perf(GIF): Faster GIF library drop-in replacement (#8743)

Co-authored-by: André <andre_miguel_costa@hotmail.com>
This commit is contained in:
Liam Howatt
2025-09-18 11:02:13 +02:00
committed by GitHub
parent b9e5694185
commit e702df19a5
17 changed files with 2130 additions and 1172 deletions
+4 -4
View File
@@ -14,10 +14,6 @@ For the licenses, see the corresponding `LICENSE.txt` file in each librarys f
- Source: https://github.com/freetype/freetype
- Note: Only the interfaces are used; FreeType itself is not part of LVGL.
**GifDec (GIF decoder library)**
- Path: src/libs/gifdec
- Source: https://github.com/lecram/gifdec
**LodePNG (PNG decoder)**
- Path: src/libs/lodepng
- Source: https://github.com/lvandeve/lodepng
@@ -61,3 +57,7 @@ For the licenses, see the corresponding `LICENSE.txt` file in each librarys f
**FT800-FT813 (EVE GPU driver)**
- Path src/libs/FT800-FT813
- Source: https://github.com/RudolphRiedel/FT800-FT813
**AnimatedGIF (GIF decoder library)**
- Path: src/libs/gif/AnimatedGIF
- Source: https://github.com/bitbank2/AnimatedGIF
+22 -9
View File
@@ -6,8 +6,8 @@ GIF Decoder
**GIF Decoder** is an LVGL extension that enables you to use GIF images in your LVGL UI.
For a detailed introduction, see: https://github.com/lecram/gifdec .
The implementation uses the `AnimatedGIF <https://github.com/bitbank2/AnimatedGIF/tree/master>`__
library.
Usage
@@ -16,6 +16,22 @@ Usage
Once enabled in ``lv_conf.h`` by setting :c:macro:`LV_USE_GIF` to ``1``,
:cpp:expr:`lv_gif_create(parent)` can be used to create a gif widget.
Set the color format of the gif framebuffer with :cpp:expr:`lv_gif_set_color_format(widget, color_format)`.
Use :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888` for gifs with transparency.
It is set to :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888` by default.
Significant RAM can be saved by using a smaller color format.
A color format conversion can be saved during rendering if the color format matches the display
color format.
This function can be called before :cpp:func:`lv_gif_set_src` to prevent the initial default ARGB8888
framebuffer from being allocated.
The supported color formats are:
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB565`
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB565_SWAPPED`
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB888`
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888`
:cpp:expr:`lv_gif_set_src(widget, src)` works very similarly to :cpp:func:`lv_image_set_src`.
As source, it also accepts images as variables (:c:struct:`lv_image_dsc_t`) or files.
@@ -43,15 +59,12 @@ from files. To do so, follow the instructions in :ref:`file_system`.
Memory Requirements
*******************
To decode and display a GIF animation the following amount of RAM (in bytes) is
required for each of the following color depths:
.. |times| unicode:: U+000D7 .. MULTIPLICATION SIGN
- :c:macro:`LV_COLOR_DEPTH` ``8``: 3 |times| image width |times| image height
- :c:macro:`LV_COLOR_DEPTH` ``16``: 4 |times| image width |times| image height
- :c:macro:`LV_COLOR_DEPTH` ``32``: 5 |times| image width |times| image height
To decode and display a GIF animation ~25 kB of RAM is needed plus
(color format pixel size + 1) |times| image width |times| image height.
RGB565 has a pixel size of 2, RGB888 has a pixel size of 3, and
ARGB8888 has a pixel size of 4.
.. _gif_example:
+2
View File
@@ -10,10 +10,12 @@ void lv_example_gif_1(void)
lv_obj_t * img;
img = lv_gif_create(lv_screen_active());
lv_gif_set_color_format(img, LV_COLOR_FORMAT_ARGB8888);
lv_gif_set_src(img, &img_bulb_gif);
lv_obj_align(img, LV_ALIGN_LEFT_MID, 20, 0);
img = lv_gif_create(lv_screen_active());
lv_gif_set_color_format(img, LV_COLOR_FORMAT_ARGB8888);
/* Assuming a File system is attached to letter 'A'
* E.g. set LV_USE_FS_STDIO 'A' in lv_conf.h */
lv_gif_set_src(img, "A:lvgl/examples/libs/gif/bulb.gif");
-1
View File
@@ -32,7 +32,6 @@ extern "C" {
#include "src/others/xml/lv_xml_private.h"
#include "src/libs/qrcode/lv_qrcode_private.h"
#include "src/libs/barcode/lv_barcode_private.h"
#include "src/libs/gif/lv_gif_private.h"
#include "src/draw/lv_draw_triangle_private.h"
#include "src/draw/lv_draw_private.h"
#include "src/draw/lv_draw_rect_private.h"
+1 -2
View File
@@ -37,8 +37,7 @@
--exclude=../src/lv_conf_internal.h
--exclude=../src/core/lv_obj_style_gen.c
--exclude=../src/core/lv_obj_style_gen.h
--exclude=../src/libs/gif/gifdec.c
--exclude=../src/libs/gif/gifdec.h
--exclude=../src/libs/gif/AnimatedGIF
--exclude=../src/libs/lodepng/lodepng.c
--exclude=../src/libs/lodepng/lodepng.h
--exclude=../src/libs/qrcode/qrcodegen.c
+204
View File
@@ -0,0 +1,204 @@
Copyright 2020 BitBank Software, Inc. All rights reserved.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
+227
View File
@@ -0,0 +1,227 @@
// 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__
#include "../../../../misc/lv_fs.h"
#include "../../../../lv_conf_internal.h"
#include LV_STDINT_INCLUDE
#include LV_LIMITS_INCLUDE
#include "../../../../stdlib/lv_string.h"
//
// GIF Animator
// Written by Larry Bank
// Copyright (c) 2020 BitBank Software, Inc.
// bitbank@pobox.com
//
// Designed to decode images up to 480x320 on MCUs
// using less than 22K of RAM
// ...and decode any sized image when more RAM is available
//
// ** NEW **
// Turbo mode added Feb 18, 2024. This option decodes images
// up to 30x faster if there is enough RAM (48K + full framebuffer)
//
/* GIF Defines and variables */
#define MAX_CHUNK_SIZE 255
//
// These 2 macros can be changed to limit the amount of RAM
// required by the decoder. For example, decoding 1-bit images to
// a 128x32 display will not need a max code size of 12 nor a palette
// with 256 entries
//
#define TURBO_BUFFER_SIZE 0x6100
// If you intend to decode generic GIFs, you want this value to be 12. If you are using GIFs solely for animations in
// your own project, and you control the GIFs you intend to play, then you can save additional RAM here:
// the decoder must reserve a minimum of 4 byte * (1<<MAX_CODE_SIZE) for the dictionary, but based on implementation
// actually reserves 5 byte * (1<<MAX_CODE_SIZE). Small or low colour GIFs may inherently not require a large
// dictionary. For larger GIFs, the en(!)coder can "voluntarily" choose not to utilize the entire dictionary. I.e.,
// by preparing (specially encoding) the GIFs, you can save >10kB RAM, but you will not be able to decode arbitrary
// images anymore. One application to craft such GIFs can be found here (use option -d)
// https://create.stephan-brumme.com/flexigif-lossless-gif-lzw-optimization/
#define MAX_CODE_SIZE 12
#define MAX_COLORS 256
#define MAX_WIDTH 480
#define LZW_BUF_SIZE (6*MAX_CHUNK_SIZE)
#define LZW_HIGHWATER (4*MAX_CHUNK_SIZE)
// This buffer is used to store the pixel sequence in reverse order
// it needs to be large enough to hold the longest possible
// sequence (1<<MAX_CODE_SIZE)
#define FILE_BUF_SIZE (1<<MAX_CODE_SIZE)
#define PIXEL_FIRST 0
#define PIXEL_LAST (1<<MAX_CODE_SIZE)
#define LINK_UNUSED 5911 // 0x1717 to use memset
#define LINK_END 5912
#define MAX_HASH 5003
// expanded LZW buffer for Turbo mode
#define LZW_BUF_SIZE_TURBO (LZW_BUF_SIZE + (2<<MAX_CODE_SIZE) + (PIXEL_LAST*2) + MAX_WIDTH)
#define LZW_HIGHWATER_TURBO ((LZW_BUF_SIZE_TURBO * 14) / 16)
//
// Pixel types
//
enum {
GIF_PALETTE_RGB565_LE = 0, // little endian (default)
GIF_PALETTE_RGB565_BE, // big endian
GIF_PALETTE_RGB888, // original 24-bpp entries
GIF_PALETTE_RGB8888, // 32-bit (alpha = 0xff or 0x00)
GIF_PALETTE_1BPP, // 1-bit per pixel (horizontal, MSB on left)
GIF_PALETTE_1BPP_OLED // 1-bit per pixel (vertical, LSB on top)
};
// for compatibility with older code
#define LITTLE_ENDIAN_PIXELS GIF_PALETTE_RGB565_LE
#define BIG_ENDIAN_PIXELS GIF_PALETTE_RGB565_BE
//
// Draw types
//
// RAW = 8-bit palettized pixels requiring transparent pixel handling and conversion through the palette.
// Each line is sent to the GIFDraw callback as 8-bit pixels. If a framebuffer exists, the lines will be
// written there too. The GIFDraw callback is optional if there is a framebuffer allocated.
//
// COOKED = 16/24/32-bpp fully rendered pixels ready for display. This requires a full frame buffer with extra
// room for the fully rendered pixels at the end of the 8-bit pixel buffer. For example, a 160x120
// canvas size with 24-bit output would require (160*120 + 3*160) bytes.
// Each prepared line is sent to the GIFDraw callback as a row of 16/24/32-bit pixels.
//
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
lv_fs_file_t 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
int iCanvasWidth; // need this to know where to place output in a fully cooked bitmap
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 ucPaletteType; // type of palette entries
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)(lv_fs_file_t *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
{
uint16_t iWidth, iHeight, iCanvasWidth, iCanvasHeight;
uint16_t iX, iY; // GIF corner offset
uint16_t iBpp;
int16_t iError; // last error
uint16_t iFrameDelay; // delay in milliseconds for this frame
int16_t iRepeatCount; // NETSCAPE animation repeat count. 0=forever
uint16_t 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
unsigned char bEndOfFrame;
unsigned char ucGIFBits, ucBackground, ucTransparent, ucCodeStart, ucMap, bUseLocalPalette;
unsigned char ucPaletteType; // RGB565 or RGB888
unsigned char ucDrawType; // RAW or COOKED
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 *pTurboBuffer;
unsigned char *pPixels, *pOldPixels;
unsigned char ucFileBuf[FILE_BUF_SIZE]; // holds temp data and pixel stack
unsigned short pPalette[(MAX_COLORS * 3)/2]; // can hold RGB565 or RGB888 - set in begin()
unsigned short pLocalPalette[(MAX_COLORS * 3)/2]; // color palettes for GIF images
unsigned char ucLZW[LZW_BUF_SIZE]; // holds de-chunked LZW data
// These next 3 are used in Turbo mode to have a larger ucLZW buffer
unsigned short usGIFTable[1<<MAX_CODE_SIZE];
unsigned char ucGIFPixels[(PIXEL_LAST*2)];
unsigned char ucLineBuf[MAX_WIDTH]; // current line
} 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);
#define REGISTER_WIDTH 32
#ifdef ALLOWS_UNALIGNED
#define INTELSHORT(p) (*(uint16_t *)p)
#define INTELLONG(p) (*(uint32_t *)p)
#else
// 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))
#endif // ALLOWS_UNALIGNED
#define BIGINT int32_t
#define BIGUINT uint32_t
#endif // __ANIMATEDGIF__
File diff suppressed because it is too large Load Diff
-2
View File
@@ -1,2 +0,0 @@
All of the source code and documentation for gifdec is released into the
public domain and provided without warranty of any kind.
File diff suppressed because it is too large Load Diff
-71
View File
@@ -1,71 +0,0 @@
#ifndef GIFDEC_H
#define GIFDEC_H
#ifdef __cplusplus
extern "C" {
#endif
#include "../../misc/lv_fs.h"
#if LV_USE_GIF
#include <stdint.h>
typedef struct _gd_Palette {
int size;
uint8_t colors[0x100 * 3];
} gd_Palette;
typedef struct _gd_GCE {
uint16_t delay;
uint8_t tindex;
uint8_t disposal;
int input;
int transparency;
} gd_GCE;
typedef struct _gd_GIF {
lv_fs_file_t fd;
const char * data;
uint8_t is_file;
uint32_t f_rw_p;
int32_t anim_start;
uint16_t width, height;
uint16_t depth;
int32_t loop_count;
gd_GCE gce;
gd_Palette * palette;
gd_Palette lct, gct;
void (*plain_text)(
struct _gd_GIF * gif, uint16_t tx, uint16_t ty,
uint16_t tw, uint16_t th, uint8_t cw, uint8_t ch,
uint8_t fg, uint8_t bg
);
void (*comment)(struct _gd_GIF * gif);
void (*application)(struct _gd_GIF * gif, char id[8], char auth[3]);
uint16_t fx, fy, fw, fh;
uint8_t bgindex;
uint8_t * canvas, * frame;
#if LV_GIF_CACHE_DECODE_DATA
uint8_t *lzw_cache;
#endif
} gd_GIF;
gd_GIF * gd_open_gif_file(const char * fname);
gd_GIF * gd_open_gif_data(const void * data);
void gd_render_frame(gd_GIF * gif, uint8_t * buffer);
int gd_get_frame(gd_GIF * gif);
void gd_rewind(gd_GIF * gif);
void gd_close_gif(gd_GIF * gif);
#endif /*LV_USE_GIF*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* GIFDEC_H */
-140
View File
@@ -1,140 +0,0 @@
/**
* @file gifdec_mve.h
*
*/
#ifndef GIFDEC_MVE_H
#define GIFDEC_MVE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include "../../misc/lv_color.h"
/*********************
* DEFINES
*********************/
#define GIFDEC_FILL_BG(dst, w, h, stride, color, opa) \
_gifdec_fill_bg_mve(dst, w, h, stride, color, opa)
#define GIFDEC_RENDER_FRAME(dst, w, h, stride, frame, pattern, tindex) \
_gifdec_render_frame_mve(dst, w, h, stride, frame, pattern, tindex)
/**********************
* MACROS
**********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
static inline void _gifdec_fill_bg_mve(uint8_t * dst, uint16_t w, uint16_t h, uint16_t stride, uint8_t * color,
uint8_t opa)
{
lv_color32_t c = lv_color32_make(*(color + 0), *(color + 1), *(color + 2), opa);
uint32_t color_32 = *(uint32_t *)&c;
__asm volatile(
".p2align 2 \n"
"vdup.32 q0, %[src] \n"
"3: \n"
"mov r0, %[dst] \n"
"wlstp.32 lr, %[w], 1f \n"
"2: \n"
"vstrw.32 q0, [r0], #16 \n"
"letp lr, 2b \n"
"1: \n"
"add %[dst], %[iTargetStride] \n"
"subs %[h], #1 \n"
"bne 3b \n"
: [dst] "+r"(dst),
[h] "+r"(h)
: [src] "r"(color_32),
[w] "r"(w),
[iTargetStride] "r"(stride * sizeof(uint32_t))
: "r0", "q0", "memory", "r14", "cc");
}
static inline void _gifdec_render_frame_mve(uint8_t * dst, uint16_t w, uint16_t h, uint16_t stride, uint8_t * frame,
uint8_t * pattern, uint16_t tindex)
{
if(w == 0 || h == 0) {
return;
}
__asm volatile(
"vmov.u16 q3, #255 \n"
"vshl.u16 q3, q3, #8 \n" /* left shift 8 for a*/
"mov r0, #2 \n"
"vidup.u16 q6, r0, #4 \n" /* [2, 6, 10, 14, 18, 22, 26, 30] */
"mov r0, #0 \n"
"vidup.u16 q7, r0, #4 \n" /* [0, 4, 8, 12, 16, 20, 24, 28] */
"3: \n"
"mov r1, %[dst] \n"
"mov r2, %[frame] \n"
"wlstp.16 lr, %[w], 1f \n"
"2: \n"
"mov r0, #3 \n"
"vldrb.u16 q4, [r2], #8 \n"
"vmul.u16 q5, q4, r0 \n"
"mov r0, #1 \n"
"vldrb.u16 q2, [%[pattern], q5] \n" /* load 8 pixel r*/
"vadd.u16 q5, q5, r0 \n"
"vldrb.u16 q1, [%[pattern], q5] \n" /* load 8 pixel g*/
"vadd.u16 q5, q5, r0 \n"
"vldrb.u16 q0, [%[pattern], q5] \n" /* load 8 pixel b*/
"vshl.u16 q1, q1, #8 \n" /* left shift 8 for g*/
"vorr.u16 q0, q0, q1 \n" /* make 8 pixel gb*/
"vorr.u16 q1, q2, q3 \n" /* make 8 pixel ar*/
"vcmp.i16 ne, q4, %[tindex] \n"
"vpstt \n"
"vstrht.16 q0, [r1, q7] \n"
"vstrht.16 q1, [r1, q6] \n"
"add r1, r1, #32 \n"
"letp lr, 2b \n"
"1: \n"
"mov r0, %[stride], LSL #2 \n"
"add %[dst], r0 \n"
"add %[frame], %[stride] \n"
"subs %[h], #1 \n"
"bne 3b \n"
: [dst] "+r"(dst),
[frame] "+r"(frame),
[h] "+r"(h)
: [pattern] "r"(pattern),
[w] "r"(w),
[stride] "r"(stride),
[tindex] "r"(tindex)
: "r0", "r1", "r2", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "memory", "r14", "cc");
}
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*GIFDEC_MVE_H*/
+154 -57
View File
@@ -6,13 +6,13 @@
/*********************
* INCLUDES
*********************/
#include "lv_gif_private.h"
#include "lv_gif.h"
#if LV_USE_GIF
#include "../../misc/lv_timer_private.h"
#include "../../misc/cache/lv_cache.h"
#include "../../core/lv_obj_class_private.h"
#include "gifdec.h"
#include "../../widgets/image/lv_image_private.h"
#include "AnimatedGIF/src/AnimatedGIF.h"
/*********************
* DEFINES
@@ -23,11 +23,26 @@
* TYPEDEFS
**********************/
/* the type of the AnimatedGIF pallete type passed to `GIF_begin` */
typedef unsigned char animatedgif_color_format_t;
typedef struct {
lv_image_t img;
GIFIMAGE gif;
const void * src;
lv_color_format_t color_format;
lv_timer_t * timer;
lv_image_dsc_t imgdsc;
int32_t loop_count;
uint32_t is_open : 1;
} lv_gif_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_gif_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_gif_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void initialize(lv_gif_t * gifobj);
static void next_frame_task_cb(lv_timer_t * t);
/**********************
@@ -59,63 +74,52 @@ lv_obj_t * lv_gif_create(lv_obj_t * parent)
return obj;
}
void lv_gif_set_src(lv_obj_t * obj, const void * src)
void lv_gif_set_color_format(lv_obj_t * obj, lv_color_format_t color_format)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
gd_GIF * gif = gifobj->gif;
/*Close previous gif if any*/
if(gif != NULL) {
lv_image_cache_drop(lv_image_get_src(obj));
gd_close_gif(gif);
gifobj->gif = NULL;
gifobj->imgdsc.data = NULL;
}
if(lv_image_src_get_type(src) == LV_IMAGE_SRC_VARIABLE) {
const lv_image_dsc_t * img_dsc = src;
gif = gd_open_gif_data(img_dsc->data);
}
else if(lv_image_src_get_type(src) == LV_IMAGE_SRC_FILE) {
gif = gd_open_gif_file(src);
}
if(gif == NULL) {
LV_LOG_WARN("Couldn't load the source");
if(gifobj->color_format == color_format) {
return;
}
gifobj->gif = gif;
gifobj->imgdsc.data = gif->canvas;
gifobj->imgdsc.header.magic = LV_IMAGE_HEADER_MAGIC;
gifobj->imgdsc.header.flags = LV_IMAGE_FLAGS_MODIFIABLE;
gifobj->imgdsc.header.cf = LV_COLOR_FORMAT_ARGB8888;
gifobj->imgdsc.header.h = gif->height;
gifobj->imgdsc.header.w = gif->width;
gifobj->imgdsc.header.stride = gif->width * 4;
gifobj->imgdsc.data_size = gif->width * gif->height * 4;
switch(color_format) {
case LV_COLOR_FORMAT_RGB565:
case LV_COLOR_FORMAT_RGB565_SWAPPED:
case LV_COLOR_FORMAT_RGB888:
case LV_COLOR_FORMAT_ARGB8888:
break;
default:
LV_LOG_WARN("gif widget does not support this color format");
return;
}
gifobj->last_call = lv_tick_get();
gifobj->color_format = color_format;
lv_image_set_src(obj, &gifobj->imgdsc);
if(gifobj->src != NULL) {
initialize(gifobj);
}
}
lv_timer_resume(gifobj->timer);
lv_timer_reset(gifobj->timer);
void lv_gif_set_src(lv_obj_t * obj, const void * src)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
next_frame_task_cb(gifobj->timer);
gifobj->src = src;
initialize(gifobj);
}
void lv_gif_restart(lv_obj_t * obj)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
if(gifobj->gif == NULL) {
if(!gifobj->is_open) {
LV_LOG_WARN("Gif resource not loaded correctly");
return;
}
gd_rewind(gifobj->gif);
GIF_reset(&gifobj->gif);
gifobj->loop_count = -1; /* match the behavior of the old library */
lv_timer_resume(gifobj->timer);
lv_timer_reset(gifobj->timer);
}
@@ -130,7 +134,7 @@ void lv_gif_resume(lv_obj_t * obj)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
if(gifobj->gif == NULL) {
if(!gifobj->is_open) {
LV_LOG_WARN("Gif resource not loaded correctly");
return;
}
@@ -142,30 +146,30 @@ bool lv_gif_is_loaded(lv_obj_t * obj)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
return (gifobj->gif != NULL);
return gifobj->is_open;
}
int32_t lv_gif_get_loop_count(lv_obj_t * obj)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
if(gifobj->gif == NULL) {
if(!gifobj->is_open) {
return -1;
}
return gifobj->gif->loop_count;
return gifobj->loop_count;
}
void lv_gif_set_loop_count(lv_obj_t * obj, int32_t count)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
if(gifobj->gif == NULL) {
if(!gifobj->is_open) {
LV_LOG_WARN("Gif resource not loaded correctly");
return;
}
gifobj->gif->loop_count = count;
gifobj->loop_count = count;
}
/**********************
@@ -178,7 +182,8 @@ static void lv_gif_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
lv_gif_t * gifobj = (lv_gif_t *) obj;
gifobj->gif = NULL;
gifobj->color_format = LV_COLOR_FORMAT_ARGB8888;
gifobj->is_open = 0;
gifobj->timer = lv_timer_create(next_frame_task_cb, 10, obj);
lv_timer_pause(gifobj->timer);
}
@@ -190,29 +195,121 @@ static void lv_gif_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
lv_image_cache_drop(lv_image_get_src(obj));
if(gifobj->gif)
gd_close_gif(gifobj->gif);
if(gifobj->is_open) {
void * framebuffer = gifobj->gif.pFrameBuffer;
GIF_close(&gifobj->gif);
lv_free(framebuffer);
}
lv_timer_delete(gifobj->timer);
}
static void initialize(lv_gif_t * gifobj)
{
GIFIMAGE * gif = &gifobj->gif;
/*Close previous gif if any*/
if(gifobj->is_open) {
lv_image_cache_drop(lv_image_get_src((lv_obj_t *) gifobj));
void * framebuffer = gif->pFrameBuffer;
GIF_close(gif);
lv_free(framebuffer);
gifobj->is_open = 0;
gifobj->imgdsc.data = NULL;
}
animatedgif_color_format_t decoder_cf;
uint32_t pixel_size_bytes;
switch(gifobj->color_format) {
case LV_COLOR_FORMAT_RGB565:
decoder_cf = GIF_PALETTE_RGB565_LE;
pixel_size_bytes = 2;
break;
case LV_COLOR_FORMAT_RGB565_SWAPPED:
decoder_cf = GIF_PALETTE_RGB565_BE;
pixel_size_bytes = 2;
break;
case LV_COLOR_FORMAT_RGB888:
decoder_cf = GIF_PALETTE_RGB888;
pixel_size_bytes = 3;
break;
case LV_COLOR_FORMAT_ARGB8888:
decoder_cf = GIF_PALETTE_RGB8888;
pixel_size_bytes = 4;
break;
default:
return;
}
GIF_begin(gif, decoder_cf);
if(lv_image_src_get_type(gifobj->src) == LV_IMAGE_SRC_VARIABLE) {
const lv_image_dsc_t * img_dsc = gifobj->src;
gifobj->is_open = GIF_openRAM(gif, (uint8_t *) img_dsc->data, img_dsc->data_size, NULL);
}
else if(lv_image_src_get_type(gifobj->src) == LV_IMAGE_SRC_FILE) {
gifobj->is_open = GIF_openFile(gif, gifobj->src, NULL);
}
if(gifobj->is_open == 0) {
LV_LOG_WARN("Couldn't load the source");
return;
}
uint32_t width = GIF_getCanvasWidth(gif);
uint32_t height = GIF_getCanvasHeight(gif);
gif->pFrameBuffer = lv_malloc(width * height * (pixel_size_bytes + 1));
gif->ucDrawType = GIF_DRAW_COOKED;
LV_ASSERT_MALLOC(gif->pFrameBuffer);
if(gif->pFrameBuffer == NULL) {
LV_LOG_WARN("Couldn't allocate a buffer for a GIF");
GIF_close(gif);
gifobj->is_open = 0;
return;
}
gifobj->imgdsc.data = gif->pFrameBuffer + width * height;
gifobj->imgdsc.header.magic = LV_IMAGE_HEADER_MAGIC;
gifobj->imgdsc.header.flags = LV_IMAGE_FLAGS_MODIFIABLE;
gifobj->imgdsc.header.cf = gifobj->color_format;
gifobj->imgdsc.header.h = height;
gifobj->imgdsc.header.w = width;
gifobj->imgdsc.header.stride = width * pixel_size_bytes;
gifobj->imgdsc.data_size = width * height * pixel_size_bytes;
lv_image_set_src((lv_obj_t *) gifobj, &gifobj->imgdsc);
gifobj->loop_count = GIF_getLoopCount(&gifobj->gif);
lv_timer_resume(gifobj->timer);
lv_timer_reset(gifobj->timer);
next_frame_task_cb(gifobj->timer);
}
static void next_frame_task_cb(lv_timer_t * t)
{
lv_obj_t * obj = t->user_data;
lv_gif_t * gifobj = (lv_gif_t *) obj;
uint32_t elaps = lv_tick_elaps(gifobj->last_call);
if(elaps < gifobj->gif->gce.delay * 10) return;
gifobj->last_call = lv_tick_get();
int has_next = gd_get_frame(gifobj->gif);
if(has_next == 0) {
int ms_delay_next;
int has_next = GIF_playFrame(&gifobj->gif, &ms_delay_next, gifobj);
if(has_next <= 0) {
/*It was the last repeat*/
lv_result_t res = lv_obj_send_event(obj, LV_EVENT_READY, NULL);
lv_timer_pause(t);
if(gifobj->loop_count > 0) {
if(gifobj->loop_count == 1) {
lv_timer_pause(t);
}
else {
gifobj->loop_count--;
}
}
if(res != LV_RESULT_OK) return;
}
gd_render_frame(gifobj->gif, (uint8_t *)gifobj->imgdsc.data);
else {
lv_timer_set_period(gifobj->timer, ms_delay_next);
}
lv_image_cache_drop(lv_image_get_src(obj));
lv_obj_invalidate(obj);
+10 -4
View File
@@ -16,14 +16,11 @@ extern "C" {
#include "../../lv_conf_internal.h"
#include "../../misc/lv_types.h"
#include "../../draw/lv_draw_buf.h"
#include "../../widgets/image/lv_image.h"
#include "../../core/lv_obj_class.h"
#include LV_STDBOOL_INCLUDE
#include LV_STDINT_INCLUDE
#if LV_USE_GIF
#include "gifdec.h"
#include "../../misc/lv_color.h"
/*********************
* DEFINES
@@ -46,6 +43,15 @@ LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_gif_class;
*/
lv_obj_t * lv_gif_create(lv_obj_t * parent);
/**
* Set the color format of the internally allocated framebuffer that the gif
* will be decoded to. The default is LV_COLOR_FORMAT_ARGB8888.
* Call this before `lv_gif_set_src` to avoid reallocating the framebuffer.
* @param obj pointer to a gif object
* @param color_format the color format of the gif framebuffer
*/
void lv_gif_set_color_format(lv_obj_t * obj, lv_color_format_t color_format);
/**
* Set the gif data to display on the object
* @param obj pointer to a gif object
-57
View File
@@ -1,57 +0,0 @@
/**
* @file lv_gif_private.h
*
*/
#ifndef LV_GIF_PRIVATE_H
#define LV_GIF_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../widgets/image/lv_image_private.h"
#include "lv_gif.h"
#if LV_USE_GIF
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_gif_t {
lv_image_t img;
gd_GIF * gif;
lv_timer_t * timer;
lv_image_dsc_t imgdsc;
uint32_t last_call;
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#endif /* LV_USE_GIF */
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GIF_PRIVATE_H*/
-2
View File
@@ -292,8 +292,6 @@ typedef struct _lv_file_explorer_t lv_file_explorer_t;
typedef struct _lv_barcode_t lv_barcode_t;
typedef struct _lv_gif_t lv_gif_t;
typedef struct _lv_qrcode_t lv_qrcode_t;
typedef struct _lv_freetype_outline_vector_t lv_freetype_outline_vector_t;
+2
View File
@@ -522,6 +522,7 @@ def run_tests(
lvgl_src_dir = os.path.join(lvgl_test_dir, "..", "src")
lv_conf_path = os.path.join(lvgl_test_dir, "src", lv_conf_name)
lvgl_h_path = os.path.join(lvgl_test_dir, "..", "lvgl.h")
lvgl_private_h_path = os.path.join(lvgl_test_dir, "..", "lvgl_private.h")
commands_ini_path = os.path.join(build_dir, "commands.ini")
docker_image_name = perf_test_options[options_name]["image_name"]
@@ -533,6 +534,7 @@ def run_tests(
volume(lvgl_src_dir, so3_usr_lib("lvgl/src")),
volume(lv_conf_path, so3_usr_lib("lv_conf.h")),
volume(lvgl_h_path, so3_usr_lib("lvgl/lvgl.h")),
volume(lvgl_private_h_path, so3_usr_lib("lvgl/lvgl_private.h")),
# We also need to add the current "lvgl.h" and mount it in the correct path
# As there's a `#include "../../lvgl.h"` in the `unity_support.h` file
volume(lvgl_h_path, "/so3/usr/lvgl.h"),