libwebp
| Language | C (usable from C++) |
|---|---|
| License | BSD-3-Clause, with a separate patent grant |
| Repository | chromium.googlesource.com/webm/libwebp (mirror: github.com/webmproject/libwebp) |
| Documentation | API documentation, Container API |
| Depends on | nothing (libjpeg, libpng, libtiff, giflib only for the command-line tools) |
| Read / write | yes / yes, lossy, lossless, alpha, animation, ICC / Exif / XMP |
libwebp is the reference implementation of WebP, maintained by Google’s WebM project. Browsers, Android, ImageMagick, libvips, Pillow, GIMP and most other programs use it. It’s fast (SIMD for SSE2, SSE4.1, NEON, MSA) and supports every feature of the format.
The project builds several libraries:
| Library | Header | Purpose |
|---|---|---|
libwebp |
webp/decode.h, webp/encode.h |
decode and encode one still image |
libsharpyuv |
sharpyuv/sharpyuv.h |
better RGB to YUV conversion, used by libwebp |
libwebpdemux |
webp/demux.h |
read chunks, metadata and animation frames (WebPAnimDecoder) |
libwebpmux |
webp/mux.h |
add or change chunks, build animations (WebPAnimEncoder) |
Installation#
# Debian / Ubuntu
sudo apt install libwebp-dev
# Fedora
sudo dnf install libwebp-devel
# macOS
brew install webp
# Windows (vcpkg)
vcpkg install libwebp
With CMake (libwebp installs a CMake config):
find_package(WebP CONFIG REQUIRED)
target_link_libraries(myapp PRIVATE WebP::webp WebP::webpdemux)
Without CMake: pkg-config --cflags --libs libwebp libwebpdemux.
Basic usage: decode and encode#
The simple API decodes a whole file from memory into RGBA and encodes RGBA back to lossy WebP:
#include <stdio.h>
#include <stdlib.h>
#include <webp/decode.h>
#include <webp/encode.h>
static uint8_t *read_file(const char *path, size_t *size) {
FILE *f = fopen(path, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
*size = (size_t)ftell(f);
fseek(f, 0, SEEK_SET);
uint8_t *data = malloc(*size);
if (data && fread(data, 1, *size, f) != *size) { free(data); data = NULL; }
fclose(f);
return data;
}
int main(void) {
size_t size;
uint8_t *data = read_file("in.webp", &size);
if (!data) return 1;
int width, height;
uint8_t *rgba = WebPDecodeRGBA(data, size, &width, &height);
free(data);
if (!rgba) { fprintf(stderr, "not a valid WebP file\n"); return 1; }
printf("%d x %d\n", width, height);
uint8_t *out = NULL;
size_t out_size = WebPEncodeRGBA(rgba, width, height, width * 4 /*stride*/, 80.0f /*quality*/, &out);
WebPFree(rgba);
if (out_size == 0) { fprintf(stderr, "encode failed\n"); return 1; }
FILE *f = fopen("out.webp", "wb");
if (f) { fwrite(out, 1, out_size, f); fclose(f); }
WebPFree(out);
return 0;
}
Other decode functions return other layouts: WebPDecodeRGB, WebPDecodeBGRA, WebPDecodeARGB, WebPDecodeYUV. Free every buffer libwebp returns with WebPFree.
How to get WebP width, height and features without decoding#
WebPGetFeatures parses only the headers:
WebPBitstreamFeatures features;
if (WebPGetFeatures(data, size, &features) == VP8_STATUS_OK) {
printf("%d x %d, alpha: %d, animated: %d, format: %s\n",
features.width, features.height, features.has_alpha, features.has_animation,
features.format == 1 ? "lossy" : features.format == 2 ? "lossless" : "mixed");
}
WebPGetInfo(data, size, &width, &height) is a shorter version that returns only the size. Both need just the first ~30 bytes of the file.
How to write a lossless WebP#
uint8_t *out = NULL;
size_t out_size = WebPEncodeLosslessRGBA(rgba, width, height, width * 4, &out);
// write out[0..out_size) to a file, then WebPFree(out)
How to set encoder options (method, sharp YUV, near-lossless)#
The advanced API exposes every cwebp option through WebPConfig:
#include <stdio.h>
#include <webp/encode.h>
// returns the encoded size, or 0 on error; the caller frees writer->mem with WebPMemoryWriterClear
size_t encode_advanced(const uint8_t *rgba, int width, int height, WebPMemoryWriter *writer) {
WebPConfig config;
if (!WebPConfigPreset(&config, WEBP_PRESET_PHOTO, 80.0f)) return 0; // or _PICTURE, _DRAWING, _ICON, _TEXT
config.method = 6; // 0 = fast .. 6 = slowest, smallest
config.use_sharp_yuv = 1; // sharper color edges
// lossless instead: WebPConfigLosslessPreset(&config, 9);
if (!WebPValidateConfig(&config)) return 0;
WebPPicture pic;
if (!WebPPictureInit(&pic)) return 0;
pic.width = width;
pic.height = height;
pic.use_argb = config.lossless;
if (!WebPPictureImportRGBA(&pic, rgba, width * 4)) return 0;
WebPMemoryWriterInit(writer);
pic.writer = WebPMemoryWrite;
pic.custom_ptr = writer;
int ok = WebPEncode(&config, &pic);
if (!ok) fprintf(stderr, "encode error %d\n", pic.error_code);
WebPPictureFree(&pic);
return ok ? writer->size : 0;
}
How to decode an animated WebP#
WebPAnimDecoder (in libwebpdemux) composes the frames on the canvas and returns full RGBA frames with timestamps:
#include <stdio.h>
#include <webp/demux.h>
int dump_frames(const uint8_t *data, size_t size) {
WebPAnimDecoderOptions opts;
if (!WebPAnimDecoderOptionsInit(&opts)) return 1;
opts.color_mode = MODE_RGBA;
opts.use_threads = 1;
WebPData webp_data = { data, size };
WebPAnimDecoder *dec = WebPAnimDecoderNew(&webp_data, &opts);
if (!dec) return 1;
WebPAnimInfo info;
WebPAnimDecoderGetInfo(dec, &info);
printf("canvas %u x %u, %u frames, loop %u\n",
info.canvas_width, info.canvas_height, info.frame_count, info.loop_count);
while (WebPAnimDecoderHasMoreFrames(dec)) {
uint8_t *frame; // canvas_width * canvas_height * 4 bytes, owned by the decoder
int timestamp; // end time of this frame in ms
if (!WebPAnimDecoderGetNext(dec, &frame, ×tamp)) break;
printf("frame ends at %d ms\n", timestamp);
}
WebPAnimDecoderDelete(dec);
return 0;
}
To create animations use WebPAnimEncoderNew, WebPAnimEncoderAdd per frame and WebPAnimEncoderAssemble from webp/mux.h.
How to read ICC, Exif and XMP metadata#
#include <webp/demux.h>
WebPData webp_data = { data, size };
WebPDemuxer *demux = WebPDemux(&webp_data);
if (demux) {
uint32_t flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
WebPChunkIterator it;
if ((flags & EXIF_FLAG) && WebPDemuxGetChunk(demux, "EXIF", 1, &it)) {
printf("Exif: %zu bytes\n", it.chunk.size); // it.chunk.bytes points to the data
WebPDemuxReleaseChunkIterator(&it);
}
if ((flags & ICCP_FLAG) && WebPDemuxGetChunk(demux, "ICCP", 1, &it)) {
printf("ICC profile: %zu bytes\n", it.chunk.size);
WebPDemuxReleaseChunkIterator(&it);
}
WebPDemuxDelete(demux);
}
WebPEncode doesn’t write metadata. Add chunks to an encoded file with WebPMuxSetChunk from webp/mux.h (or the webpmux tool).
Tips#
WebPDecodewith aWebPDecoderConfigcan scale (options.use_scaling) and crop while decoding, which is much faster than decoding the full image first.WebPIDecoder(WebPINewDecoder,WebPIAppend) decodes incrementally as bytes arrive from the network.- For untrusted files, check
WebPGetFeaturesdimensions before allocating. libwebp is fuzzed continuously (OSS-Fuzz), but keep it up to date: CVE-2023-4863 was a heap overflow in the lossless decoder.