mirror of
https://github.com/Relintai/gif_loader.git
synced 2024-11-12 10:25:04 +01:00
Initial commit.
This commit is contained in:
commit
87568f7d99
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.import
|
||||||
|
*.d
|
||||||
|
*.o
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pyc
|
||||||
|
*.bc
|
||||||
|
*.os
|
19
LICENSE
Normal file
19
LICENSE
Normal 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
23
README.md
Normal 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
155
SCsub
Normal 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
6
config.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
def can_build(env, platform):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def configure(env):
|
||||||
|
pass
|
2
gif_loader.cpp
Normal file
2
gif_loader.cpp
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
#include "gif_loader.h"
|
11
gif_loader.h
Normal file
11
gif_loader.h
Normal 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
7
register_types.cpp
Normal 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
7
register_types.h
Normal 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
378
thirdparty/gif_load/README.md
vendored
Normal 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
299
thirdparty/gif_load/gif_load.h
vendored
Normal 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 **/
|
Loading…
Reference in New Issue
Block a user