webpdec.h: single-file WebP decoder

libwebp 1.6.0's decode + demux path (lossy VP8, lossless VP8L, VP8X
container, ALPH alpha, and the WebPDemux/WebPAnimDecoder animation API)
amalgamated into one stb-style header, built as portable C99 with all
SIMD, threads and host file I/O disabled.

amalgamate.py regenerates webpdec.h from a pinned, auto-cloned libwebp
checkout; tests/run.sh checks decode output bit-exactly against
reference libwebp.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 03:28:11 +00:00
commit 4da9a48d7c
10 changed files with 22045 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
# Upstream libwebp checkout -- fetched on demand by amalgamate.py
/libwebp/
# Build artifacts
*.o
*.raw
*.ppm
__pycache__/
# webpdec.h IS committed (it's the deliverable, regenerate with amalgamate.py)
+30
View File
@@ -0,0 +1,30 @@
Copyright (c) 2010, Google Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Google nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+23
View File
@@ -0,0 +1,23 @@
Additional IP Rights Grant (Patents)
------------------------------------
"These implementations" means the copyrightable works that implement the WebM
codecs distributed by Google as part of the WebM Project.
Google 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, transfer, and otherwise
run, modify and propagate the contents of these implementations of WebM, where
such license applies only to those patent claims, both currently owned by
Google and acquired in the future, licensable by Google that are necessarily
infringed by these implementations of WebM. This grant does not include claims
that would be infringed only as a consequence of further modification of these
implementations. If you or your agent or exclusive licensee institute or order
or agree to the institution of patent litigation or any other patent
enforcement activity against any entity (including a cross-claim or
counterclaim in a lawsuit) alleging that any of these implementations of WebM
or any code incorporated within any of these implementations of WebM
constitute direct or contributory patent infringement, or inducement of
patent infringement, then any patent rights granted to you under this License
for these implementations of WebM shall terminate as of the date such
litigation is filed.
+114
View File
@@ -0,0 +1,114 @@
# webpdec.h — single-file WebP decoder
A standalone, dependency-free **WebP decoder** in one C header, in the spirit of
the [stb](https://github.com/nothings/stb) libraries. It is libwebp's decode +
demux path — *not* a reimplementation — amalgamated into a single file and built
as portable C99 with **all SIMD, threads and host file I/O disabled**. So it is
as correct as upstream libwebp (it *is* libwebp) while staying trivial to drop
into any project.
Handles everything real WebP files use:
- **Lossy** (VP8) and **lossless** (VP8L)
- The **VP8X** extended container
- The **ALPH** alpha chunk (transparency on lossy images)
- **Animation** + metadata via the demux API (`WebPDemux`, `WebPAnimDecoder`)
## Usage
In exactly **one** translation unit:
```c
#define WEBPDEC_IMPLEMENTATION
#include "webpdec.h"
```
Everywhere else, just `#include "webpdec.h"`. The public API is the standard
libwebp decode + demux API.
Still image:
```c
int w, h;
uint8_t *rgba = WebPDecodeRGBA(data, len, &w, &h); /* R,G,B,A, top-to-bottom */
if (rgba) {
/* ... w*h*4 bytes ... */
WebPFree(rgba);
}
```
Animation (all frames, with blending/disposal handled for you):
```c
WebPData wd = { data, len };
WebPAnimDecoderOptions opt; WebPAnimDecoderOptionsInit(&opt);
opt.color_mode = MODE_RGBA;
WebPAnimDecoder *dec = WebPAnimDecoderNew(&wd, &opt);
WebPAnimInfo info; WebPAnimDecoderGetInfo(dec, &info); /* canvas, frame_count */
while (WebPAnimDecoderHasMoreFrames(dec)) {
uint8_t *rgba; int timestamp_ms;
WebPAnimDecoderGetNext(dec, &rgba, &timestamp_ms); /* rgba owned by dec */
}
WebPAnimDecoderDelete(dec);
```
`WebPGetInfo`, `WebPDecodeRGB/BGRA/ARGB`, the advanced `WebPDecode` +
`WebPDecoderConfig` path (including decode-time downscaling via
`config.options.use_scaling`), and the full `WebPDemux*` chunk/metadata
iterator are all available — see the declarations near the top of `webpdec.h`.
Link with `-lm` (libwebp uses `pow()` for gamma tables). That's the only
dependency.
## What's not included
Encoding and the mux (assembly/write) API — this is decode-only, by design.
## Portability
`webpdec.h` defines `HAVE_CONFIG_H` and bakes in an empty `config.h`, which is
libwebp's own switch for "no detected SIMD": every architecture dispatcher falls
back to the reference C path. This works even on SIMD-capable targets — e.g. on
ARMv8 where the toolchain defines `__ARM_NEON`, NEON still stays off. No
pthreads, no `<stdio.h>` file calls. Tested decoding bit-exactly against
upstream libwebp on x86-64 and aarch64.
## Repo layout
```
webpdec.h the single-file library (committed; this is all you need)
amalgamate.py regenerates webpdec.h from libwebp
UPSTREAM_COMMIT the exact pinned libwebp revision
LICENSE, PATENTS upstream BSD-style license + patent grant
tests/ test_decode.c, test_demux.c, run.sh
libwebp/ upstream checkout (git-ignored; fetched on demand)
```
## Regenerating
```sh
python3 amalgamate.py # writes webpdec.h (+ refreshes UPSTREAM_COMMIT)
```
With no `libwebp/` present it shallow-clones libwebp at the pinned commit, so a
fresh checkout just works — no submodules, no manual steps. The amalgamation is
`LIBWEBPDECODER_OBJS` + `DEMUX_OBJS` minus every `*_sse2/_neon/_msa/...` SIMD
translation unit; the few single-TU symbol clashes are namespaced by the script
(the upstream tree is left pristine, so regeneration is byte-for-byte
reproducible).
## Tests
```sh
sh tests/run.sh
```
With [Pillow](https://python-pillow.org/) installed it generates lossy /
lossless / alpha / animated samples and checks decode output **bit-exactly**
against reference libwebp; otherwise pass it `.webp` paths to decode.
## License
BSD-style, identical to libwebp. See `LICENSE` and `PATENTS`. Copyright Google
LLC and the WebM project authors; this file only mechanically concatenates their
sources.
+3
View File
@@ -0,0 +1,3 @@
libwebp b43b2caa710c0c997c066cb32c7fea1391fad70a (v1.6.0)
upstream: https://chromium.googlesource.com/webm/libwebp
decode + demux amalgamation (SIMD/threads/file-IO disabled) via amalgamate.py
+209
View File
@@ -0,0 +1,209 @@
#!/usr/bin/env python3
"""
amalgamate.py -- generate webpdec.h, a single-file WebP decoder.
Collapses libwebp's *decode + demux* path (lossy VP8, lossless VP8L, the VP8X
extended container, the ALPH alpha chunk, and the WebPDemux / WebPAnimDecoder
animation+metadata API) into one stb-style header, built as portable C99 with
all SIMD, threads and host file I/O disabled.
Self-contained: with no arguments it clones libwebp at the pinned commit (into
./libwebp, git-ignored), writes the empty config.h that forces the generic-C
path, applies the few single-translation-unit symbol renames itself, and writes
./webpdec.h. A fresh checkout + `python3 amalgamate.py` just works.
python3 amalgamate.py [OUTPUT.h]
Output layout:
#ifndef WEBPDEC_H <- public API (types/decode/demux)
...declarations...
#endif
#ifdef WEBPDEC_IMPLEMENTATION
#define HAVE_CONFIG_H <- forces the portable C path (SIMD off)
...all decode+demux .c files + their internal headers, inlined once each...
#endif
"""
import os
import re
import subprocess
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(HERE, "libwebp")
UPSTREAM_URL = "https://chromium.googlesource.com/webm/libwebp"
PINNED_COMMIT = "b43b2caa710c0c997c066cb32c7fea1391fad70a" # v1.6.0
VERSION = "1.6.0"
# ---- the decoder-only, portable-C object set (LIBWEBPDECODER_OBJS + DEMUX_OBJS,
# minus every *_sse2/_sse41/_neon/_msa/_mips* SIMD translation unit) -------
DEC = [
"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",
]
DSP = [
"src/dsp/alpha_processing.c", "src/dsp/cpu.c", "src/dsp/dec.c",
"src/dsp/dec_clip_tables.c", "src/dsp/filters.c", "src/dsp/lossless.c",
"src/dsp/rescaler.c", "src/dsp/upsampling.c", "src/dsp/yuv.c",
]
UTILS = [
"src/utils/bit_reader_utils.c", "src/utils/color_cache_utils.c",
"src/utils/filters_utils.c", "src/utils/huffman_utils.c",
"src/utils/palette.c", "src/utils/quant_levels_dec_utils.c",
"src/utils/random_utils.c", "src/utils/rescaler_utils.c",
"src/utils/thread_utils.c", "src/utils/utils.c",
]
# Demux + animation: pure RIFF chunk parser (demux.c) and the frame compositor
# WebPAnimDecoder (anim_decode.c). anim_decode reuses WebPDecode and calls no
# WebPMux* function, so no muxer/encoder TU is needed.
DEMUX = ["src/demux/demux.c", "src/demux/anim_decode.c"]
SOURCES = DEC + DSP + UTILS + DEMUX
# demux.h declares both the WebPDemux and WebPAnimDecoder public APIs and pulls
# in decode.h + mux_types.h, so it covers the whole public surface.
PUBLIC = ["src/webp/types.h", "src/webp/decode.h", "src/webp/demux.h"]
# A handful of file-local symbols share a name across these TUs, which only
# matters once everything is one translation unit. We give the colliding copy
# a private prefix by wrapping just that source file's body in #define/#undef --
# leaving the upstream tree pristine (so a fresh clone regenerates identically).
RENAME_PREFIX = "webpdec_priv_"
RENAMES = {
"src/utils/thread_utils.c": ["ChangeState"],
"src/utils/quant_levels_dec_utils.c": ["clip_8b"],
"src/demux/demux.c": [
"MemBuffer", "InitMemBuffer", "MemDataSize", "RemapMemBuffer", "ParseVP8X",
],
}
INCLUDE_RE = re.compile(r'^\s*#\s*include\s+"([^"]+)"\s*(?://.*|/\*.*)?$')
def ensure_libwebp():
"""Clone libwebp at the pinned commit if it isn't already present."""
if os.path.isfile(os.path.join(SRC, "src/dec/webp_dec.c")):
return
sys.stderr.write("libwebp not found; cloning %s @ %s ...\n"
% (UPSTREAM_URL, PINNED_COMMIT[:12]))
try:
subprocess.check_call(["git", "clone", "--depth", "1", UPSTREAM_URL, SRC])
head = subprocess.check_output(
["git", "-C", SRC, "rev-parse", "HEAD"]).decode().strip()
if head != PINNED_COMMIT:
subprocess.check_call(
["git", "-C", SRC, "fetch", "--depth", "1", "origin", PINNED_COMMIT])
subprocess.check_call(
["git", "-C", SRC, "checkout", "--detach", PINNED_COMMIT])
except Exception as e:
sys.exit("error: could not fetch libwebp (%s).\nClone it manually:\n"
" git clone %s %s\n git -C %s checkout %s"
% (e, UPSTREAM_URL, SRC, SRC, PINNED_COMMIT))
def write_config_h():
"""Empty config.h: with HAVE_CONFIG_H defined this forces libwebp's
generic-C path (no WEBP_HAVE_* => no SIMD), even on SIMD-capable hosts."""
with open(os.path.join(SRC, "src/webp/config.h"), "w") as f:
f.write("/* intentionally empty: forces libwebp's generic-C path */\n")
emitted = set()
def resolve(inc, curfile):
cands = []
if inc.startswith("src/"):
cands.append(os.path.join(SRC, inc))
cands.append(os.path.join(os.path.dirname(curfile), inc))
cands.append(os.path.join(SRC, inc))
for c in cands:
c = os.path.normpath(c)
if os.path.isfile(c):
return c
return None
def inline(path, out):
path = os.path.normpath(path)
if path in emitted:
return
emitted.add(path)
rel = os.path.relpath(path, SRC)
names = RENAMES.get(rel.replace(os.sep, "/"))
out.append("/* >>> %s */\n" % rel)
if names:
for nm in names:
out.append("#define %s %s%s\n" % (nm, RENAME_PREFIX, nm))
with open(path, "r") as f:
for line in f:
m = INCLUDE_RE.match(line)
if m:
tgt = resolve(m.group(1), path)
if tgt is not None:
inline(tgt, out)
continue
out.append(line)
if names:
for nm in names:
out.append("#undef %s\n" % nm)
def main(out_path):
ensure_libwebp()
write_config_h()
out = []
out.append("""\
/*
* webpdec.h -- single-file WebP decoder (stb-style).
*
* libwebp's decode + demux path -- lossy VP8, lossless VP8L, the VP8X extended
* container, the ALPH alpha chunk, plus the demux/animation API (WebPDemux,
* WebPAnimDecoder) -- amalgamated into one file and built as portable C99 with
* all SIMD, threads and host file I/O disabled. The public API is the standard
* libwebp one (WebPDecodeRGBA, WebPDemux*, WebPAnimDecoder*).
*
* Usage: in exactly ONE translation unit,
* #define WEBPDEC_IMPLEMENTATION
* #include "webpdec.h"
* Everywhere else, just #include "webpdec.h". Decode-only (no encoder/muxer).
* Link with -lm.
*
* Generated by amalgamate.py from libwebp %s (see UPSTREAM_COMMIT).
* BSD-style license (see LICENSE) + PATENTS grant. Copyright Google LLC.
* DO NOT EDIT BY HAND -- rerun `python3 amalgamate.py`.
*/
""" % VERSION)
out.append("#ifndef WEBPDEC_H\n#define WEBPDEC_H\n")
for h in PUBLIC:
inline(os.path.join(SRC, h), out)
out.append("#endif /* WEBPDEC_H */\n\n")
out.append("#ifdef WEBPDEC_IMPLEMENTATION\n")
out.append("#define HAVE_CONFIG_H\n")
for c in SOURCES:
inline(os.path.join(SRC, c), out)
out.append("#endif /* WEBPDEC_IMPLEMENTATION */\n")
text = "".join(out)
with open(out_path, "w") as f:
f.write(text)
# refresh the recorded upstream revision
with open(os.path.join(HERE, "UPSTREAM_COMMIT"), "w") as f:
f.write("libwebp %s (v%s)\nupstream: %s\n"
"decode + demux amalgamation (SIMD/threads/file-IO disabled) "
"via amalgamate.py\n" % (PINNED_COMMIT, VERSION, UPSTREAM_URL))
print("wrote %s (%d lines, %d KB) from libwebp %s"
% (os.path.relpath(out_path, HERE), text.count("\n"),
len(text) // 1024, PINNED_COMMIT[:12]))
if __name__ == "__main__":
out = sys.argv[1] if len(sys.argv) > 1 else os.path.join(HERE, "webpdec.h")
main(out)
Executable
+59
View File
@@ -0,0 +1,59 @@
#!/bin/sh
# Build and run the webpdec.h tests. If Pillow is available, samples are
# generated and decode output is checked bit-exactly against reference libwebp;
# otherwise the tests just decode whatever .webp files are passed/present.
#
# sh tests/run.sh
set -e
cd "$(dirname "$0")/.." # repo root
CC=${CC:-cc}
CFLAGS="-O2 -std=c99 -Wall -I."
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
# Generate samples with Pillow if present (covers lossy, lossless, alpha,
# odd dimensions, and a multi-frame animation).
HAVE_PIL=0
if python3 - "$TMP" >/dev/null 2>&1 <<'PY'
import sys
from PIL import Image
d = sys.argv[1]
w, h = 137, 83
img = Image.new("RGBA", (w, h))
px = img.load()
for y in range(h):
for x in range(w):
px[x, y] = ((x*255)//w, (y*255)//h, (x*y) % 256, (x+y) % 256)
img.save(d+"/lossless.webp", lossless=True)
img.save(d+"/lossy.webp", quality=90)
frames = [Image.new("RGBA", (48, 32), (i*50 % 256, 80, 200, 255)) for i in range(5)]
frames[0].save(d+"/anim.webp", save_all=True, append_images=frames[1:],
duration=[100, 120, 140, 160, 180], loop=0, lossless=True)
PY
then HAVE_PIL=1; fi
echo "== build =="
$CC $CFLAGS -o "$TMP/test_decode" tests/test_decode.c -lm
$CC $CFLAGS -o "$TMP/test_demux" tests/test_demux.c -lm
if [ "$HAVE_PIL" = 1 ]; then
echo "== decode (still) =="
for k in lossless lossy; do
"$TMP/test_decode" "$TMP/$k.webp" "$TMP/$k.raw"
python3 - "$TMP/$k.webp" "$TMP/$k.raw" <<'PY'
import sys
from PIL import Image
ref = Image.open(sys.argv[1]).convert("RGBA").tobytes()
mine = open(sys.argv[2], "rb").read()
assert ref == mine, "%s: pixels differ from reference libwebp!" % sys.argv[1]
print(" %s: bit-exact vs reference libwebp" % sys.argv[1].split('/')[-1])
PY
done
echo "== demux + animation =="
"$TMP/test_demux" "$TMP/anim.webp"
else
echo "(Pillow not found -- decoding any .webp passed as args)"
for f in "$@"; do "$TMP/test_decode" "$f"; done
fi
echo "ALL TESTS PASSED"
+54
View File
@@ -0,0 +1,54 @@
/* test harness: decode a .webp to RGBA, report, dump a PPM for eyeballing */
#include <stdio.h>
#include <stdlib.h>
#define WEBPDEC_IMPLEMENTATION
#include "webpdec.h"
int main(int argc, char **argv) {
if (argc < 2) { fprintf(stderr, "usage: %s file.webp [out.ppm]\n", argv[0]); return 2; }
FILE *f = fopen(argv[1], "rb");
if (!f) { perror("open"); return 1; }
fseek(f, 0, SEEK_END); long n = ftell(f); fseek(f, 0, SEEK_SET);
unsigned char *buf = malloc(n);
if (fread(buf, 1, n, f) != (size_t)n) { perror("read"); return 1; }
fclose(f);
int w = 0, h = 0;
if (!WebPGetInfo(buf, n, &w, &h)) { fprintf(stderr, "not a webp / bad header\n"); return 1; }
printf("WebPGetInfo: %dx%d\n", w, h);
uint8_t *rgba = WebPDecodeRGBA(buf, n, &w, &h);
if (!rgba) { fprintf(stderr, "decode FAILED\n"); return 1; }
printf("decoded: %dx%d, RGBA (R,G,B,A top-to-bottom)\n", w, h);
/* sample a few pixels */
long center = ((long)h/2 * w + w/2) * 4;
printf("center px RGBA = %d %d %d %d\n",
rgba[center], rgba[center+1], rgba[center+2], rgba[center+3]);
/* checksum + alpha presence */
unsigned long sum = 0; int has_alpha = 0;
for (long i = 0; i < (long)w*h; i++) {
sum += rgba[i*4] + rgba[i*4+1] + rgba[i*4+2];
if (rgba[i*4+3] != 255) has_alpha = 1;
}
printf("rgb checksum=%lu non-opaque-alpha=%s\n", sum, has_alpha ? "yes" : "no");
if (argc >= 3) { /* .raw => raw RGBA dump; else opaque PPM */
const char *p = argv[2];
size_t L = 0; while (p[L]) L++;
FILE *o = fopen(argv[2], "wb");
if (L >= 4 && p[L-4]=='.' && p[L-3]=='r' && p[L-2]=='a' && p[L-1]=='w') {
fwrite(rgba, 4, (size_t)w*h, o);
} else {
fprintf(o, "P6\n%d %d\n255\n", w, h);
for (long i = 0; i < (long)w*h; i++) fwrite(&rgba[i*4], 1, 3, o);
}
fclose(o);
printf("wrote %s\n", argv[2]);
}
WebPFree(rgba);
free(buf);
return 0;
}
+56
View File
@@ -0,0 +1,56 @@
/* exercises the demux + animation API folded into the single header */
#include <stdio.h>
#include <stdlib.h>
#define WEBPDEC_IMPLEMENTATION
#include "webpdec.h"
static unsigned char* slurp(const char* p, size_t* n){
FILE* f=fopen(p,"rb"); if(!f) return 0;
fseek(f,0,SEEK_END); long L=ftell(f); fseek(f,0,SEEK_SET);
unsigned char* b=malloc(L); if(fread(b,1,L,f)!=(size_t)L){} fclose(f); *n=L; return b;
}
int main(int argc,char**argv){
size_t n; unsigned char* buf=slurp(argv[1],&n);
if(!buf){ printf("no file\n"); return 1; }
WebPData data = { buf, n };
/* --- demux: report canvas + frame/loop count --- */
WebPDemuxer* dmux = WebPDemux(&data);
if(!dmux){ printf("WebPDemux failed\n"); return 1; }
unsigned cw = WebPDemuxGetI(dmux, WEBP_FF_CANVAS_WIDTH);
unsigned ch = WebPDemuxGetI(dmux, WEBP_FF_CANVAS_HEIGHT);
unsigned nf = WebPDemuxGetI(dmux, WEBP_FF_FRAME_COUNT);
unsigned lp = WebPDemuxGetI(dmux, WEBP_FF_LOOP_COUNT);
unsigned fl = WebPDemuxGetI(dmux, WEBP_FF_FORMAT_FLAGS);
printf("demux: canvas=%ux%u frames=%u loop=%u flags=0x%x\n", cw,ch,nf,lp,fl);
WebPDemuxDelete(dmux);
/* --- animation: decode every frame via WebPAnimDecoder --- */
WebPAnimDecoderOptions opt;
WebPAnimDecoderOptionsInit(&opt);
opt.color_mode = MODE_RGBA;
WebPAnimDecoder* adec = WebPAnimDecoderNew(&data, &opt);
if(!adec){ printf("WebPAnimDecoderNew failed\n"); return 1; }
WebPAnimInfo info;
WebPAnimDecoderGetInfo(adec, &info);
printf("anim: %ux%u frames=%u loops=%u bgcolor=0x%08x\n",
info.canvas_width, info.canvas_height, info.frame_count,
info.loop_count, info.bgcolor);
int prev = 0, got = 0;
while (WebPAnimDecoderHasMoreFrames(adec)) {
uint8_t* frgba; int ts;
if (!WebPAnimDecoderGetNext(adec, &frgba, &ts)) { printf("GetNext failed\n"); break; }
long c = ((long)info.canvas_height/2*info.canvas_width + info.canvas_width/2)*4;
printf(" frame %d ts=%dms (+%dms) centerRGBA=%d,%d,%d,%d\n",
got, ts, ts-prev, frgba[c],frgba[c+1],frgba[c+2],frgba[c+3]);
prev = ts; got++;
}
printf("decoded %d/%u frames -> %s\n", got, info.frame_count,
(got==(int)info.frame_count)?"OK":"MISMATCH");
WebPAnimDecoderDelete(adec);
free(buf);
return 0;
}
+21487
View File
File diff suppressed because it is too large Load Diff