Initial commit.

This commit is contained in:
Relintai 2022-12-10 17:06:57 +01:00
commit 87568f7d99
11 changed files with 915 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.import
*.d
*.o
*.meta
*.obj
*.pyc
*.bc
*.os

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2022 Péter Magyar
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.

23
README.md Normal file
View File

@ -0,0 +1,23 @@
# webm
A c++ Pandemonium engine module that adds support for loading gifs using https://github.com/hidefromkgb/gif_load .
# Building
1. Get the source code for the engine.
``` git clone https://github.com/Relintai/pandemonium_engine.git pandemonium_engine ```
2. Go into Pandemonium's modules directory.
```
cd ./pandemonium_engine/modules/
```
3. Clone this repository
```
git clone https://github.com/Relintai/gif_loader.git gif_loader
```
4. Build Pandemonium. You can use the official godot docs. [Tutorial](https://docs.godotengine.org/en/latest/development/compiling/index.html)

155
SCsub Normal file
View File

@ -0,0 +1,155 @@
#!/usr/bin/env python
Import("env")
Import("env_modules")
env_webp = env_modules.Clone()
# Thirdparty source files
thirdparty_obj = []
thirdparty_dir = "./thirdparty/libwebp/"
thirdparty_sources = [
"sharpyuv/sharpyuv.c",
"sharpyuv/sharpyuv_csp.c",
"sharpyuv/sharpyuv_dsp.c",
"sharpyuv/sharpyuv_gamma.c",
"sharpyuv/sharpyuv_neon.c",
"sharpyuv/sharpyuv_sse2.c",
"src/dec/alpha_dec.c",
"src/dec/buffer_dec.c",
"src/dec/frame_dec.c",
"src/dec/idec_dec.c",
"src/dec/io_dec.c",
"src/dec/quant_dec.c",
"src/dec/tree_dec.c",
"src/dec/vp8_dec.c",
"src/dec/vp8l_dec.c",
"src/dec/webp_dec.c",
"src/demux/anim_decode.c",
"src/demux/demux.c",
"src/dsp/alpha_processing.c",
"src/dsp/alpha_processing_mips_dsp_r2.c",
"src/dsp/alpha_processing_neon.c",
"src/dsp/alpha_processing_sse2.c",
"src/dsp/alpha_processing_sse41.c",
"src/dsp/cost.c",
"src/dsp/cost_mips32.c",
"src/dsp/cost_mips_dsp_r2.c",
"src/dsp/cost_neon.c",
"src/dsp/cost_sse2.c",
"src/dsp/cpu.c",
"src/dsp/dec.c",
"src/dsp/dec_clip_tables.c",
"src/dsp/dec_mips32.c",
"src/dsp/dec_mips_dsp_r2.c",
"src/dsp/dec_msa.c",
"src/dsp/dec_neon.c",
"src/dsp/dec_sse2.c",
"src/dsp/dec_sse41.c",
"src/dsp/enc.c",
"src/dsp/enc_mips32.c",
"src/dsp/enc_mips_dsp_r2.c",
"src/dsp/enc_msa.c",
"src/dsp/enc_neon.c",
"src/dsp/enc_sse2.c",
"src/dsp/enc_sse41.c",
"src/dsp/filters.c",
"src/dsp/filters_mips_dsp_r2.c",
"src/dsp/filters_msa.c",
"src/dsp/filters_neon.c",
"src/dsp/filters_sse2.c",
"src/dsp/lossless.c",
"src/dsp/lossless_enc.c",
"src/dsp/lossless_enc_mips32.c",
"src/dsp/lossless_enc_mips_dsp_r2.c",
"src/dsp/lossless_enc_msa.c",
"src/dsp/lossless_enc_neon.c",
"src/dsp/lossless_enc_sse2.c",
"src/dsp/lossless_enc_sse41.c",
"src/dsp/lossless_mips_dsp_r2.c",
"src/dsp/lossless_msa.c",
"src/dsp/lossless_neon.c",
"src/dsp/lossless_sse2.c",
"src/dsp/lossless_sse41.c",
"src/dsp/rescaler.c",
"src/dsp/rescaler_mips32.c",
"src/dsp/rescaler_mips_dsp_r2.c",
"src/dsp/rescaler_msa.c",
"src/dsp/rescaler_neon.c",
"src/dsp/rescaler_sse2.c",
"src/dsp/ssim.c",
"src/dsp/ssim_sse2.c",
"src/dsp/upsampling.c",
"src/dsp/upsampling_mips_dsp_r2.c",
"src/dsp/upsampling_msa.c",
"src/dsp/upsampling_neon.c",
"src/dsp/upsampling_sse2.c",
"src/dsp/upsampling_sse41.c",
"src/dsp/yuv.c",
"src/dsp/yuv_mips32.c",
"src/dsp/yuv_mips_dsp_r2.c",
"src/dsp/yuv_neon.c",
"src/dsp/yuv_sse2.c",
"src/dsp/yuv_sse41.c",
"src/enc/alpha_enc.c",
"src/enc/analysis_enc.c",
"src/enc/backward_references_cost_enc.c",
"src/enc/backward_references_enc.c",
"src/enc/config_enc.c",
"src/enc/cost_enc.c",
"src/enc/filter_enc.c",
"src/enc/frame_enc.c",
"src/enc/histogram_enc.c",
"src/enc/iterator_enc.c",
"src/enc/near_lossless_enc.c",
"src/enc/picture_csp_enc.c",
"src/enc/picture_enc.c",
"src/enc/picture_psnr_enc.c",
"src/enc/picture_rescale_enc.c",
"src/enc/picture_tools_enc.c",
"src/enc/predictor_enc.c",
"src/enc/quant_enc.c",
"src/enc/syntax_enc.c",
"src/enc/token_enc.c",
"src/enc/tree_enc.c",
"src/enc/vp8l_enc.c",
"src/enc/webp_enc.c",
"src/mux/anim_encode.c",
"src/mux/muxedit.c",
"src/mux/muxinternal.c",
"src/mux/muxread.c",
"src/utils/bit_reader_utils.c",
"src/utils/bit_writer_utils.c",
"src/utils/color_cache_utils.c",
"src/utils/filters_utils.c",
"src/utils/huffman_encode_utils.c",
"src/utils/huffman_utils.c",
"src/utils/quant_levels_dec_utils.c",
"src/utils/quant_levels_utils.c",
"src/utils/random_utils.c",
"src/utils/rescaler_utils.c",
"src/utils/thread_utils.c",
"src/utils/utils.c",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_webp.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "src/"])
env_thirdparty = env_webp.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env.modules_sources += thirdparty_obj
# Godot source files
module_obj = []
env_webp.add_source_files(module_obj, "*.cpp")
env.modules_sources += module_obj
# Needed to force rebuilding the module files when the thirdparty library is updated.
env.Depends(module_obj, thirdparty_obj)

6
config.py Normal file
View File

@ -0,0 +1,6 @@
def can_build(env, platform):
return True
def configure(env):
pass

2
gif_loader.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "gif_loader.h"

11
gif_loader.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef GIF_LOADER_H
#define GIF_LOADER_H
#include "core/object/reference.h"
class GIFLoader : public Reference {
public:
};
#endif

7
register_types.cpp Normal file
View File

@ -0,0 +1,7 @@
#include "register_types.h"
void register_gif_loader_types() {
}
void unregister_gif_loader_types() {
}

7
register_types.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef GIF_LOADER_REGISTER_TYPES_H
#define GIF_LOADER_REGISTER_TYPES_H
void register_gif_loader_types();
void unregister_gif_loader_types();
#endif

378
thirdparty/gif_load/README.md vendored Normal file
View File

@ -0,0 +1,378 @@
# gif_load
74b674de2704bc1b20fc3bf482a5ee3e774b67c5
https://github.com/hidefromkgb/gif_load
This is an ANSI C compatible animated GIF loader in a single header file of
less than 300 lines of code (less than 200 without empty lines and comments).
It defines 1 new struct and 1 new enum, and requires 1 function call to load
a GIF. 'ANSI C compatible' means that it builds fine with `-pedantic -ansi`
compiler flags but includes `stdint.h` which is unavailable prior to C99.
`gif_load` is free and unencumbered software released into the public domain,
blah blah. See the header file for details.
There are no strict dependencies on the standard C library. The only external
function used by default is `realloc()` (both for freeing and allocation), but
it\`s possible to override it by defining a macro called `GIF_MGET(m,s,a,c)`
prior to including the header; `m` stands for a `uint8_t*`-typed pointer to
the memory block being allocated or freed, `s` is the target block size, typed
`unsigned long`, `a` is the value of the fifth parameter passed to `GIF_Load()`
(mainly used to hold a pointer to some user-defined structure if need be; see
below), typed `void*`, and `c` equals 0 on freeing and 1 on allocation. For
example, `GIF_MGET` might be defined as follows if `malloc()` / `free()` pair
is to be used instead of `realloc()`:
```c
#include <stdlib.h>
#define GIF_MGET(m,s,a,c) if (c) m = (uint8_t*)malloc(s); else free(m);
#include "gif_load.h"
```
Loading GIFs immediately from disk is not supported: target files must
be read or otherwise mapped into RAM by the caller.
The main function that does the actual loading is called `GIF_Load()`.
It requires a callback function to create the animation structure of
any user-defined format. This function, further referred to as the
'frame writer callback', will be executed once every frame.
Aditionally, the main function accepts a second callback used to process GIF
[application-specific extensions](https://stackoverflow.com/a/28486261/7019311),
i.e. metadata; in the vast majority of GIFs no such extensions are present, so
this callback is optional.
Both frame writer callback and metadata callback need 2 parameters:
1. callback-specific data
2. pointer to a `struct GIF_WHDR` that encapsulates GIF frame information
(callbacks may alter any fields at will, as the structure passed to them is a
proxy that is discarded after every call):
* `GIF_WHDR::xdim` - global GIF width, always constant across frames
(further referred to as 'ACAF'); [0; 65535]
* `GIF_WHDR::ydim` - global GIF height, ACAF; [0; 65535]
* `GIF_WHDR::clrs` - number of colors in the current palette (local palettes
are not that rare so it may vary across frames, further
referred to as 'MVAF'); {2; 4; 8; 16; 32; 64; 128; 256}
* `GIF_WHDR::bkgd` - 0-based background color index for the current palette,
ACAF (sic ACAF, as this index is set globally)
* `GIF_WHDR::tran` - 0-based transparent color index for the current palette
(or 1 when transparency is disabled), MVAF
* `GIF_WHDR::intr` - boolean flag indicating whether the current frame is
[interlaced](https://en.wikipedia.org/wiki/GIF#Interlacing);
deinterlacing it is up to the caller (see the examples
below), MVAF
* `GIF_WHDR::mode` - next frame (sic next, not current) blending mode, MVAF:
[`GIF_NONE`:] no blending, mainly used in single-frame
GIFs, functionally equivalent to `GIF_CURR`;
[`GIF_CURR`:] leave the current image state as is;
[`GIF_BKGD`:] restore the background color (or
transparency, in case `GIF_WHDR::tran`1) in the
boundaries of the current frame; [`GIF_PREV`:] restore
the last image whose mode differed from this one,
functionally equivalent to `GIF_BKGD` when assigned to
the first frame in a GIF; *N.B.:* if right before a
`GIF_PREV` frame came a `GIF_BKGD` one, the state to
be restored is before a certain part of the resulting
image was filled with the background color, not after!
* `GIF_WHDR::frxd` - current frame width, MVAF; [0; 65535]
* `GIF_WHDR::fryd` - current frame height, MVAF; [0; 65535]
* `GIF_WHDR::frxo` - current frame horizontal offset, MVAF; [0; 65535]
* `GIF_WHDR::fryo` - current frame vertical offset, MVAF; [0; 65535]
* `GIF_WHDR::time` - next frame delay in GIF time units (1 unit = 10 msec),
MVAF; negative values are possible here, they mean
that the frame requires user input to advance, and the
actual delay equals (`time` + 1) GIF time units: zero
delay + user input = wait for input indefinitely,
nonzero delay + user input = wait for either input or
timeout (whichever comes first); *N.B.:* user input
requests can be safely ignored, disregarding the GIF
standard
* `GIF_WHDR::ifrm` - 0-based index of the current frame, always varies
across frames (further referred to as 'AVAF')
* `GIF_WHDR::nfrm` - total frame count, negative if the GIF data supplied
is incomplete, ACAF during a single `GIF_Load()` call
but may vary across `GIF_Load()` calls
* `GIF_WHDR::bptr` - [frame writer:] pixel indices for the current frame,
ACAF (it is only the pointer address that is constant;
the pixel indices stored inside = MVAF); [metadata
callback:] app metadata header (8+3 bytes) followed by
a GIF chunk (1 byte designating length L, then L bytes
of metadata, and so forth; L = 0 means end of chunk),
AVAF
* `GIF_WHDR::cpal` - the current palette containing 3 `uint8_t` values for
each of the colors: `R` for the red channel, `G` for
green and `B` for blue; this pointer is guaranteed
to be the same across frames if and only if the global
palette is used for those frames (local palettes are
strictly frame-specific, even when they contain the
same number of identical colors in identical order),
MVAF
Neither of the two callbacks needs to return a value, thus having `void` for
a return type.
`GIF_Load()`, in its turn, needs 6 parameters:
1. a pointer to GIF data in RAM
2. GIF data size; may be larger than the actual data if the GIF has a proper
ending mark
3. a pointer to the frame writer callback
4. a pointer to the metadata callback; may be left empty
5. callback-specific data
6. number of frames to skip before executing the callback; useful to resume
loading the partial file
Partial GIFs are also supported, but only at a granularity of one frame. For
example, if the file ends in the middle of the fifth frame, no attempt would
be made to recover the upper half, and the resulting animation will only
contain 4 frames. When more data is available the loader might be called
again, this time with the `skip` parameter equalling 4 to skip those 4 frames.
Note that the metadata callback is not affected by `skip` and gets called
again every time the frames between which the metadata was written are
skipped.
The return value of the function above, if positive, equals the total number
of frames in the animation and indicates that the GIF data stream ended with
a proper termination mark. Negative return value is the number of frames
loaded per current call multiplied by 1, suggesting that the GIF data stream
being decoded is still incomplete. Zero, in its turn, means that the call
could not decode any more frames.
`gif_load` is endian-aware. If the target machine can be big-endian the user
has to determine that manually and add `#define GIF_BIGE 1` to the source
prior to the header being included if that\`s the case, or otherwise define
the endianness to be used (0 = little, 1 = big), e.g. by declaring a helper
function and setting `GIF_BIGE` to expand into its call, or by passing it as a
compiler parameter (e.g. `-DGIF_BIGE=1` for GCC / Clang). Although GIF data is
little-endian, all multibyte integers passed to the user through `long`-typed
fields of `GIF_WHDR` have correct byte order regardless of the endianness of
the target machine, provided that `GIF_BIGE` is set correctly. Most other
data, e.g. pixel indices of a frame, consists of single bytes and thus does
not require endianness correction. One notable exception is GIF application
metadata which is passed as the raw chunk of bytes (for details see the
description of `GIF_WHDR::bptr` provided above), and then it\`s the
callback\`s job to parse it and decide whether to decode and how to do that.
There is a possibility to build `gif_load` as a shared library. `GIF_EXTR` is
a convenience macro to be defined so that the `GIF_Load()` function gets an
entry in the export table of the library. See the Python example for further
information.
# C / C++ usage example
Here is an example of how to use `GIF_Load()` to transform an animated GIF
file into a 32-bit uncompressed TGA. For the sake of simplicity all frames
are concatenated one below the other and no attempt is made to keep all of
them if the resulting height exceeds the TGA height limit which is 0xFFFF.
```c
#include "gif_load.h"
#include <fcntl.h>
#ifdef _MSC_VER
/** MSVC is definitely not my favourite compiler... >_< **/
#pragma warning(disable:4996)
#include <io.h>
#else
#include <unistd.h>
#endif
#ifndef O_BINARY
#define O_BINARY 0
#endif
#pragma pack(push, 1)
typedef struct {
void *data, *pict, *prev;
unsigned long size, last;
int uuid;
} STAT; /** #pragma avoids -Wpadded on 64-bit machines **/
#pragma pack(pop)
void Frame(void*, struct GIF_WHDR*); /** keeps -Wmissing-prototypes happy **/
void Frame(void *data, struct GIF_WHDR *whdr) {
uint32_t *pict, *prev, x, y, yoff, iter, ifin, dsrc, ddst;
uint8_t head[18] = {0};
STAT *stat = (STAT*)data;
#define BGRA(i) ((whdr->bptr[i] == whdr->tran)? 0 : \
((uint32_t)(whdr->cpal[whdr->bptr[i]].R << ((GIF_BIGE)? 8 : 16)) \
| (uint32_t)(whdr->cpal[whdr->bptr[i]].G << ((GIF_BIGE)? 16 : 8)) \
| (uint32_t)(whdr->cpal[whdr->bptr[i]].B << ((GIF_BIGE)? 24 : 0)) \
| ((GIF_BIGE)? 0xFF : 0xFF000000)))
if (!whdr->ifrm) {
/** TGA doesn`t support heights over 0xFFFF, so we have to trim: **/
whdr->nfrm = ((whdr->nfrm < 0)? -whdr->nfrm : whdr->nfrm) * whdr->ydim;
whdr->nfrm = (whdr->nfrm < 0xFFFF)? whdr->nfrm : 0xFFFF;
/** this is the very first frame, so we must write the header **/
head[ 2] = 2;
head[12] = (uint8_t)(whdr->xdim );
head[13] = (uint8_t)(whdr->xdim >> 8);
head[14] = (uint8_t)(whdr->nfrm );
head[15] = (uint8_t)(whdr->nfrm >> 8);
head[16] = 32; /** 32 bits depth **/
head[17] = 0x20; /** top-down flag **/
write(stat->uuid, head, 18UL);
ddst = (uint32_t)(whdr->xdim * whdr->ydim);
stat->pict = calloc(sizeof(uint32_t), ddst);
stat->prev = calloc(sizeof(uint32_t), ddst);
}
/** [TODO:] the frame is assumed to be inside global bounds,
however it might exceed them in some GIFs; fix me. **/
pict = (uint32_t*)stat->pict;
ddst = (uint32_t)(whdr->xdim * whdr->fryo + whdr->frxo);
ifin = (!(iter = (whdr->intr)? 0 : 4))? 4 : 5; /** interlacing support **/
for (dsrc = (uint32_t)-1; iter < ifin; iter++)
for (yoff = 16U >> ((iter > 1)? iter : 1), y = (8 >> iter) & 7;
y < (uint32_t)whdr->fryd; y += yoff)
for (x = 0; x < (uint32_t)whdr->frxd; x++)
if (whdr->tran != (long)whdr->bptr[++dsrc])
pict[(uint32_t)whdr->xdim * y + x + ddst] = BGRA(dsrc);
write(stat->uuid, pict, sizeof(uint32_t) * (uint32_t)whdr->xdim
* (uint32_t)whdr->ydim);
if ((whdr->mode == GIF_PREV) && !stat->last) {
whdr->frxd = whdr->xdim;
whdr->fryd = whdr->ydim;
whdr->mode = GIF_BKGD;
ddst = 0;
}
else {
stat->last = (whdr->mode == GIF_PREV)?
stat->last : (unsigned long)(whdr->ifrm + 1);
pict = (uint32_t*)((whdr->mode == GIF_PREV)? stat->pict : stat->prev);
prev = (uint32_t*)((whdr->mode == GIF_PREV)? stat->prev : stat->pict);
for (x = (uint32_t)(whdr->xdim * whdr->ydim); --x;
pict[x - 1] = prev[x - 1]);
}
if (whdr->mode == GIF_BKGD) /** cutting a hole for the next frame **/
for (whdr->bptr[0] = (uint8_t)((whdr->tran >= 0)?
whdr->tran : whdr->bkgd), y = 0,
pict = (uint32_t*)stat->pict; y < (uint32_t)whdr->fryd; y++)
for (x = 0; x < (uint32_t)whdr->frxd; x++)
pict[(uint32_t)whdr->xdim * y + x + ddst] = BGRA(0);
#undef BGRA
}
int main(int argc, char *argv[]) {
STAT stat = {0};
if (argc < 3)
write(1, "arguments: <in>.gif <out>.tga (1 or more times)\n", 48UL);
for (stat.uuid = 2, argc -= (~argc & 1); argc >= 3; argc -= 2) {
if ((stat.uuid = open(argv[argc - 2], O_RDONLY | O_BINARY)) <= 0)
return 1;
stat.size = (unsigned long)lseek(stat.uuid, 0UL, 2 /** SEEK_END **/);
lseek(stat.uuid, 0UL, 0 /** SEEK_SET **/);
read(stat.uuid, stat.data = realloc(0, stat.size), stat.size);
close(stat.uuid);
unlink(argv[argc - 1]);
stat.uuid = open(argv[argc - 1], O_CREAT | O_WRONLY | O_BINARY, 0644);
if (stat.uuid > 0) {
GIF_Load(stat.data, (long)stat.size, Frame, 0, (void*)&stat, 0L);
stat.pict = realloc(stat.pict, 0L);
stat.prev = realloc(stat.prev, 0L);
close(stat.uuid);
stat.uuid = 0;
}
stat.data = realloc(stat.data, 0L);
}
return stat.uuid;
}
```
# Python usage example
Here is an example of how to use `GIF_Load()` from Python 2.x or 3.x.
*N.B.:* The implementation shown here complies to the GIF standard much
better than the one PIL has (at least as of 2018-02-07): for example
it preserves transparency and supports local frame palettes.
First of all, `gif_load.h` has to be built as a shared library:
Linux / macOS:
```bash
# Only works when executed from the directory where gif_load.h resides
rm gif_load.so 2>/dev/null
uname -s | grep -q ^Darwin && CC=clang || CC=gcc
$CC -pedantic -ansi -s -DGIF_EXTR=extern -xc gif_load.h -o gif_load.so \
-shared -fPIC -Wl,--version-script=<(echo "{global:GIF_Load;local:*;};")
```
Windows:
```bash
rem Only works when executed from the directory where gif_load.h resides
del gif_load.exp gif_load.lib gif_load.dll
cl /LD /Zl /DGIF_EXTR=__declspec(dllexport) /Tc gif_load.h msvcrt.lib /Fegif_load.dll
```
Then the loading function can be called using CTypes
([Python 2 reference](https://docs.python.org/2/library/ctypes.html),
[Python 3 reference](https://docs.python.org/3/library/ctypes.html)):
```python
from PIL import Image
def GIF_Load(file):
from platform import system
from ctypes import string_at, Structure, c_long as cl, c_ubyte, \
py_object, pointer, POINTER as PT, CFUNCTYPE, CDLL
class GIF_WHDR(Structure): _fields_ = \
[("xdim", cl), ("ydim", cl), ("clrs", cl), ("bkgd", cl),
("tran", cl), ("intr", cl), ("mode", cl), ("frxd", cl), ("fryd", cl),
("frxo", cl), ("fryo", cl), ("time", cl), ("ifrm", cl), ("nfrm", cl),
("bptr", PT(c_ubyte)), ("cpal", PT(c_ubyte))]
def intr(y, x, w, base, tran): base.paste(tran.crop((0, y, x, y + 1)), w)
def skew(i, r): return r >> ((7 - (i & 2)) >> (1 + (i & 1)))
def fill(w, d, p):
retn = Image.new("L", d, w.bkgd) if (w.tran < 0) else \
Image.new("RGBA", d)
if (w.tran < 0):
retn.putpalette(p)
return retn
def WriteFunc(d, w):
cpal = string_at(w[0].cpal, w[0].clrs * 3)
list = d.contents.value[0]
if (len(list) == 0):
list.append(Image.new("RGBA", (w[0].xdim, w[0].ydim)))
tail = len(list) - 1
base = Image.frombytes("L", (w[0].frxd, w[0].fryd),
string_at(w[0].bptr, w[0].frxd * w[0].fryd))
if (w[0].intr != 0):
tran = base.copy()
[intr(skew(y, y) + (skew(y, w[0].fryd - 1) + 1, 0)[(y & 7) == 0],
w[0].frxd, (0, y), base, tran) for y in range(w[0].fryd)]
tran = Image.eval(base, lambda indx: (255, 0)[indx == w[0].tran])
base.putpalette(cpal)
list[tail].paste(base, (w[0].frxo, w[0].fryo), tran)
list[tail].info = {"delay" : w[0].time}
if (w[0].ifrm != (w[0].nfrm - 1)):
trgt = (tail, d.contents.value[1])[w[0].mode == 3]
list.append(list[trgt].copy() if (trgt >= 0) else
fill(w[0], (w[0].xdim, w[0].ydim), cpal))
if (w[0].mode != 3):
d.contents.value[1] = w[0].ifrm
if (w[0].mode == 2):
list[tail + 1].paste(fill(w[0], (w[0].frxd, w[0].fryd), cpal),
(w[0].frxo, w[0].fryo))
try: file = open(file, "rb")
except IOError: return []
file.seek(0, 2)
size = file.tell()
file.seek(0, 0)
list = [[], -1]
CDLL(("%s.so", "%s.dll")[system() == "Windows"] % "./gif_load"). \
GIF_Load(file.read(), size,
CFUNCTYPE(None, PT(py_object), PT(GIF_WHDR))(WriteFunc),
None, pointer(py_object(list)), 0)
file.close()
return list[0]
def GIF_Save(file, fext):
list = GIF_Load("%s.gif" % file)
[pict.save("%s_f%d.%s" % (file, indx, fext))
for (indx, pict) in enumerate(list)]
GIF_Save("insert_gif_name_here_without_extension", "png")
```

299
thirdparty/gif_load/gif_load.h vendored Normal file
View File

@ -0,0 +1,299 @@
#ifndef GIF_LOAD_H
#define GIF_LOAD_H
/** gif_load: A slim, fast and header-only GIF loader written in C.
Original author: hidefromkgb (hidefromkgb@gmail.com)
_________________________________________________________________________
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
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 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.
_________________________________________________________________________
**/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h> /** imports uint8_t, uint16_t and uint32_t **/
#ifndef GIF_MGET
#include <stdlib.h>
#define GIF_MGET(m,s,a,c) m = (uint8_t*)realloc((c)? 0 : m, (c)? s : 0UL);
#endif
#ifndef GIF_BIGE
#define GIF_BIGE 0
#endif
#ifndef GIF_EXTR
#define GIF_EXTR static
#endif
#define _GIF_SWAP(h) ((GIF_BIGE)? ((uint16_t)(h << 8) | (h >> 8)) : h)
#pragma pack(push, 1)
struct GIF_WHDR { /** ======== frame writer info: ======== **/
long xdim, ydim, clrs, /** global dimensions, palette size **/
bkgd, tran, /** background index, transparent index **/
intr, mode, /** interlace flag, frame blending mode **/
frxd, fryd, frxo, fryo, /** current frame dimensions and offset **/
time, ifrm, nfrm; /** delay, frame number, frame count **/
uint8_t *bptr; /** frame pixel indices or metadata **/
struct { /** [==== GIF RGB palette element: ====] **/
uint8_t R, G, B; /** [color values - red, green, blue ] **/
} *cpal; /** current palette **/
};
#pragma pack(pop)
enum {GIF_NONE = 0, GIF_CURR = 1, GIF_BKGD = 2, GIF_PREV = 3};
/** [ internal function, do not use ] **/
static long _GIF_SkipChunk(uint8_t **buff, long size) {
long skip;
for (skip = 2, ++size, ++(*buff); ((size -= skip) > 0) && (skip > 1);
*buff += (skip = 1 + **buff));
return size;
}
/** [ internal function, do not use ] **/
static long _GIF_LoadHeader(unsigned gflg, uint8_t **buff, void **rpal,
unsigned fflg, long *size, long flen) {
if (flen && (!(*buff += flen) || ((*size -= flen) <= 0)))
return -2; /** v--[ 0x80: "palette is present" flag ]--, **/
if (flen && (fflg & 0x80)) { /** local palette has priority | **/
*rpal = *buff; /** [ 3L: 3 uint8_t color channels ]--, | **/
*buff += (flen = 2 << (fflg & 7)) * 3L; /** <--| | **/
return ((*size -= flen * 3L) > 0)? flen : -1; /** <--' | **/
} /** no local palette found, checking for the global one | **/
return (gflg & 0x80)? (2 << (gflg & 7)) : 0; /** <-----' **/
}
/** [ internal function, do not use ] **/
static long _GIF_LoadFrame(uint8_t **buff, long *size,
uint8_t *bptr, uint8_t *blen) {
typedef uint16_t GIF_H;
const long GIF_HLEN = sizeof(GIF_H), /** to rid the scope of sizeof **/
GIF_CLEN = 1 << 12; /** code table length: 4096 items **/
GIF_H accu, mask; /** bit accumulator / bit mask **/
long ctbl, iter, /** last code table index / index string iterator **/
prev, curr, /** codes from the stream: previous / current **/
ctsz, ccsz, /** code table bit sizes: min LZW / current **/
bseq, bszc; /** counters: block sequence / bit size **/
uint32_t *code = (uint32_t*)bptr - GIF_CLEN; /** code table pointer **/
/** preparing initial values **/
if ((--(*size) <= GIF_HLEN) || !*++(*buff))
return -4; /** unexpected end of the stream: insufficient size **/
mask = (GIF_H)((1 << (ccsz = (ctsz = *(*buff - 1)) + 1)) - 1);
if ((ctsz < 2) || (ctsz > 8))
return -3; /** min LZW size is out of its nominal [2; 8] bounds **/
if ((ctbl = (1L << ctsz)) != (mask & _GIF_SWAP(*(GIF_H*)(*buff + 1))))
return -2; /** initial code is not equal to min LZW size **/
for (curr = ++ctbl; curr; code[--curr] = 0); /** actual color codes **/
/** getting codes from stream (--size makes up for end-of-stream mark) **/
for (--(*size), bszc = -ccsz, prev = curr = 0;
((*size -= (bseq = *(*buff)++) + 1) >= 0) && bseq; *buff += bseq)
for (; bseq > 0; bseq -= GIF_HLEN, *buff += GIF_HLEN)
for (accu = (GIF_H)(_GIF_SWAP(*(GIF_H*)*buff)
& ((bseq < GIF_HLEN)? ((1U << (8 * bseq)) - 1U) : ~0U)),
curr |= accu << (ccsz + bszc), accu = (GIF_H)(accu >> -bszc),
bszc += 8 * ((bseq < GIF_HLEN)? bseq : GIF_HLEN);
bszc >= 0; bszc -= ccsz, prev = curr, curr = accu,
accu = (GIF_H)(accu >> ccsz))
if (((curr &= mask) & ~1L) == (1L << ctsz)) {
if (~(ctbl = curr + 1) & 1) /** end-of-data code (ED). **/
/** -1: no end-of-stream mark after ED; 1: decoded **/
return (*((*buff += bseq + 1) - 1))? -1 : 1;
mask = (GIF_H)((1 << (ccsz = ctsz + 1)) - 1);
} /** ^- table drop code (TD). TD = 1 << ctsz, ED = TD + 1 **/
else { /** single-pixel (SP) or multi-pixel (MP) code. **/
if (ctbl < GIF_CLEN) { /** is the code table full? **/
if ((ctbl == mask) && (ctbl < GIF_CLEN - 1)) {
mask = (GIF_H)(mask + mask + 1);
ccsz++; /** yes; extending **/
} /** prev = TD? => curr < ctbl = prev **/
code[ctbl] = (uint32_t)prev + (code[prev] & 0xFFF000);
} /** appending SP / MP decoded pixels to the frame **/
prev = (long)code[iter = (ctbl > curr)? curr : prev];
if ((bptr += (prev = (prev >> 12) & 0xFFF)) > blen)
continue; /** skipping pixels above frame capacity **/
for (prev++; (iter &= 0xFFF) >> ctsz;
*bptr-- = (uint8_t)((iter = (long)code[iter]) >> 24));
(bptr += prev)[-prev] = (uint8_t)iter;
if (ctbl < GIF_CLEN) { /** appending the code table **/
if (ctbl == curr)
*bptr++ = (uint8_t)iter;
else if (ctbl < curr)
return -5; /** wrong code in the stream **/
code[ctbl++] += ((uint32_t)iter << 24) + 0x1000;
}
} /** 0: no ED before end-of-stream mark; -4: see above **/
return (++(*size) >= 0)? 0 : -4; /** ^- N.B.: 0 error is recoverable **/
}
/** _________________________________________________________________________
The main loading function. Returns the total number of frames if the data
includes proper GIF ending, and otherwise it returns the number of frames
loaded per current call, multiplied by -1. So, the data may be incomplete
and in this case the function can be called again when more data arrives,
just remember to keep SKIP up to date.
_________________________________________________________________________
DATA: raw data chunk, may be partial
SIZE: size of the data chunk that`s currently present
GWFR: frame writer function, MANDATORY
EAMF: metadata reader function, set to 0 if not needed
ANIM: implementation-specific data (e.g. a structure or a pointer to it)
SKIP: number of frames to skip before resuming
**/
GIF_EXTR long GIF_Load(void *data, long size,
void (*gwfr)(void*, struct GIF_WHDR*),
void (*eamf)(void*, struct GIF_WHDR*),
void *anim, long skip) {
const long GIF_BLEN = (1 << 12) * sizeof(uint32_t);
const uint8_t GIF_EHDM = 0x21, /** extension header mark **/
GIF_FHDM = 0x2C, /** frame header mark **/
GIF_EOFM = 0x3B, /** end-of-file mark **/
GIF_EGCM = 0xF9, /** extension: graphics control mark **/
GIF_EAMM = 0xFF; /** extension: app metadata mark **/
#pragma pack(push, 1)
struct GIF_GHDR { /** ========== GLOBAL GIF HEADER: ========== **/
uint8_t head[6]; /** 'GIF87a' / 'GIF89a' header signature **/
uint16_t xdim, ydim; /** total image width, total image height **/
uint8_t flgs; /** FLAGS:
GlobalPlt bit 7 1: global palette exists
0: local in each frame
ClrRes bit 6-4 bits/channel = ClrRes+1
[reserved] bit 3 0
PixelBits bit 2-0 |Plt| = 2 * 2^PixelBits
**/
uint8_t bkgd, aspr; /** background color index, aspect ratio **/
} *ghdr = (struct GIF_GHDR*)data;
struct GIF_FHDR { /** ======= GIF FRAME MASTER HEADER: ======= **/
uint16_t frxo, fryo; /** offset of this frame in a "full" image **/
uint16_t frxd, fryd; /** frame width, frame height **/
uint8_t flgs; /** FLAGS:
LocalPlt bit 7 1: local palette exists
0: global is used
Interlaced bit 6 1: interlaced frame
0: non-interlaced frame
Sorted bit 5 usually 0
[reserved] bit 4-3 [undefined]
PixelBits bit 2-0 |Plt| = 2 * 2^PixelBits
**/
} *fhdr;
struct GIF_EGCH { /** ==== [EXT] GRAPHICS CONTROL HEADER: ==== **/
uint8_t flgs; /** FLAGS:
[reserved] bit 7-5 [undefined]
BlendMode bit 4-2 000: not set; static GIF
001: leave result as is
010: restore background
011: restore previous
1--: [undefined]
UserInput bit 1 1: show frame till input
0: default; ~99% of GIFs
TransColor bit 0 1: got transparent color
0: frame is fully opaque
**/
uint16_t time; /** delay in GIF time units; 1 unit = 10 ms **/
uint8_t tran; /** transparent color index **/
} *egch = 0;
#pragma pack(pop)
struct GIF_WHDR wtmp, whdr = {0};
long desc, blen;
uint8_t *buff;
/** checking if the stream is not empty and has a 'GIF8[79]a' signature,
the data has sufficient size and frameskip value is non-negative **/
if (!ghdr || (size <= (long)sizeof(*ghdr)) || (*(buff = ghdr->head) != 71)
|| (buff[1] != 73) || (buff[2] != 70) || (buff[3] != 56) || (skip < 0)
|| ((buff[4] != 55) && (buff[4] != 57)) || (buff[5] != 97) || !gwfr)
return 0;
buff = (uint8_t*)(ghdr + 1) /** skipping the global header and palette **/
+ _GIF_LoadHeader(ghdr->flgs, 0, 0, 0, 0, 0L) * 3L;
if ((size -= buff - (uint8_t*)ghdr) <= 0)
return 0;
whdr.xdim = _GIF_SWAP(ghdr->xdim);
whdr.ydim = _GIF_SWAP(ghdr->ydim);
for (whdr.bptr = buff, whdr.bkgd = ghdr->bkgd, blen = --size;
(blen >= 0) && ((desc = *whdr.bptr++) != GIF_EOFM); /** sic: '>= 0' **/
blen = _GIF_SkipChunk(&whdr.bptr, blen) - 1) /** count all frames **/
if (desc == GIF_FHDM) {
fhdr = (struct GIF_FHDR*)whdr.bptr;
if (_GIF_LoadHeader(ghdr->flgs, &whdr.bptr, (void**)&whdr.cpal,
fhdr->flgs, &blen, sizeof(*fhdr)) <= 0)
break;
whdr.frxd = _GIF_SWAP(fhdr->frxd);
whdr.fryd = _GIF_SWAP(fhdr->fryd);
whdr.frxo = (whdr.frxd > whdr.frxo)? whdr.frxd : whdr.frxo;
whdr.fryo = (whdr.fryd > whdr.fryo)? whdr.fryd : whdr.fryo;
whdr.ifrm++;
}
blen = whdr.frxo * whdr.fryo * (long)sizeof(*whdr.bptr);
GIF_MGET(whdr.bptr, (unsigned long)(blen + GIF_BLEN + 2), anim, 1)
whdr.nfrm = (desc != GIF_EOFM)? -whdr.ifrm : whdr.ifrm;
for (whdr.bptr += GIF_BLEN, whdr.ifrm = -1; blen /** load all frames **/
&& (skip < ((whdr.nfrm < 0)? -whdr.nfrm : whdr.nfrm)) && (size >= 0);
size = (desc != GIF_EOFM)? ((desc != GIF_FHDM) || (skip > whdr.ifrm))?
_GIF_SkipChunk(&buff, size) - 1 : size - 1 : -1)
if ((desc = *buff++) == GIF_FHDM) { /** found a frame **/
whdr.intr = !!((fhdr = (struct GIF_FHDR*)buff)->flgs & 0x40);
*(void**)&whdr.cpal = (void*)(ghdr + 1); /** interlaced? -^ **/
whdr.clrs = _GIF_LoadHeader(ghdr->flgs, &buff, (void**)&whdr.cpal,
fhdr->flgs, &size, sizeof(*fhdr));
if ((skip <= ++whdr.ifrm) && ((whdr.clrs <= 0)
|| (_GIF_LoadFrame(&buff, &size,
whdr.bptr, whdr.bptr + blen) < 0)))
size = -(whdr.ifrm--) - 1; /** failed to load the frame **/
else if (skip <= whdr.ifrm) {
whdr.frxd = _GIF_SWAP(fhdr->frxd);
whdr.fryd = _GIF_SWAP(fhdr->fryd);
whdr.frxo = _GIF_SWAP(fhdr->frxo);
whdr.fryo = _GIF_SWAP(fhdr->fryo);
whdr.time = (egch)? _GIF_SWAP(egch->time) : 0;
whdr.tran = (egch && (egch->flgs & 0x01))? egch->tran : -1;
whdr.time = (egch && (egch->flgs & 0x02))? -whdr.time - 1
: whdr.time;
whdr.mode = (egch && !(egch->flgs & 0x10))?
(egch->flgs & 0x0C) >> 2 : GIF_NONE;
egch = 0;
wtmp = whdr;
gwfr(anim, &wtmp); /** passing the frame to the caller **/
}
}
else if (desc == GIF_EHDM) { /** found an extension **/
if (*buff == GIF_EGCM) /** graphics control ext. **/
egch = (struct GIF_EGCH*)(buff + 1 + 1);
else if ((*buff == GIF_EAMM) && eamf) { /** app metadata ext. **/
wtmp = whdr;
wtmp.bptr = buff + 1 + 1; /** just passing the raw chunk **/
eamf(anim, &wtmp);
}
}
whdr.bptr -= GIF_BLEN; /** for excess pixel codes ----v (here & above) **/
GIF_MGET(whdr.bptr, (unsigned long)(blen + GIF_BLEN + 2), anim, 0)
return (whdr.nfrm < 0)? (skip - whdr.ifrm - 1) : (whdr.ifrm + 1);
}
#undef _GIF_SWAP
#ifdef __cplusplus
}
#endif
#endif /** GIF_LOAD_H **/