stb_image
| Language | C (usable from C++) |
|---|---|
| License | public domain (Unlicense) or MIT, your choice |
| Repository | github.com/nothings/stb |
| Documentation | comments at the top of stb_image.h and stb_image_write.h |
| Depends on | nothing |
| Read / write | yes (baseline and progressive, 8-bit) / yes (baseline only) |
stb_image.h is one header file that decodes JPEG, PNG, GIF, BMP, TGA, PSD, HDR and PNM into a pixel buffer. stb_image_write.h writes JPEG, PNG, BMP, TGA and HDR. They’re popular in games, graphics demos and small tools because there’s nothing to build or link.
For JPEG, the decoder handles baseline and progressive files with any chroma subsampling, but not 12-bit or arithmetic-coded files (same as stock libjpeg). It ignores all metadata: no Exif orientation, no ICC profile, and CMYK images aren’t color managed. The encoder writes baseline JPEG with standard Huffman tables, 4:2:0 subsampling up to quality 90 and 4:4:4 above. Both are slower than libjpeg-turbo. The header warns that it isn’t hardened against malicious files; prefer libjpeg-turbo for untrusted input.
Installation#
Copy the headers into your project. In one .c or .cpp file, define the implementation macro before including:
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
Other files include the headers without the macros. Package managers: vcpkg install stb, Conan stb, Debian/Ubuntu libstb-dev.
To build only the JPEG decoder, define STBI_ONLY_JPEG before the implementation.
Basic usage: load a JPEG and save it again#
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#include <stdio.h>
int main(void) {
int width, height, channels;
// last argument: 0 = keep the file's channels, 3 = force RGB, 4 = force RGBA
unsigned char *pixels = stbi_load("in.jpg", &width, &height, &channels, 3);
if (!pixels) {
fprintf(stderr, "load failed: %s\n", stbi_failure_reason());
return 1;
}
printf("%d x %d, %d channels in file\n", width, height, channels);
// make it grayscale-ish: average channels
for (long i = 0; i < (long)width * height * 3; i += 3) {
unsigned char g = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3;
pixels[i] = pixels[i + 1] = pixels[i + 2] = g;
}
// quality 1..100
if (!stbi_write_jpg("out.jpg", width, height, 3, pixels, 85)) {
fprintf(stderr, "write failed\n");
}
stbi_image_free(pixels);
return 0;
}
How to get JPEG width and height without decoding#
stbi_info reads only the header:
int w, h, comp;
if (stbi_info("in.jpg", &w, &h, &comp))
printf("%d x %d, %d components\n", w, h, comp);
How to decode a JPEG from memory#
int w, h, n;
unsigned char *pixels = stbi_load_from_memory(buf, (int)buf_len, &w, &h, &n, 4); // RGBA
if (pixels) {
// ...
stbi_image_free(pixels);
}
How to write a JPEG to memory#
stbi_write_jpg_to_func calls your function with chunks of output:
#include <stdlib.h>
#include <string.h>
typedef struct { unsigned char *data; size_t size; } Buffer;
static void append(void *ctx, void *data, int size) {
Buffer *b = ctx;
b->data = realloc(b->data, b->size + size);
memcpy(b->data + b->size, data, size);
b->size += size;
}
Buffer out = {0};
stbi_write_jpg_to_func(append, &out, width, height, 3, pixels, 80);
// out.data holds out.size bytes of JPEG
free(out.data);
Tips#
- stb_image doesn’t apply the Exif orientation, so phone photos may load sideways. Read the tag with an Exif library such as TinyEXIF and rotate the pixels yourself.
stbi_set_flip_vertically_on_load(1)returns the bottom row first, as OpenGL textures expect.- For smaller files, re-encode the output with mozjpeg; stb_image_write doesn’t optimize Huffman tables.