php-io-extensions / imgdec
PNG, JPEG and TIFF into straight RGBA8 in C, through libpng, libjpeg and libtiff
Package info
github.com/php-io-extensions/imgdec
Language:C
Type:php-ext
Ext name:ext-imgdec
pkg:composer/php-io-extensions/imgdec
Requires
- php: >=8.4
Requires (Dev)
- pestphp/pest: ^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-10 02:36:24 UTC
README
PNG, JPEG and TIFF bytes into straight (not premultiplied) RGBA8 pixels, in C, through libpng, libjpeg and libtiff. Written against the Zend API.
PHP's gd decodes images too, but it keeps alpha in 7 bits, doesn't read TIFF, and gets its pixels
out only through imagecolorat(), one call per pixel. ext-imgdec answers every pixel as one string,
in one call, ready to hand to a framebuffer or a GPU texture. A 512 × 512 map tile takes about 1 ms
instead of 25–55 ms through gd.
ext-imgdec bytes → RGBA8 ← this package
→ venusian-surface/images app('images')->decode(): a framebuffer, 'extended' driver
→ canvases, panels, GPU engines draw it
Requirements
- PHP 8.4 or newer, NTS or ZTS
- Linux or macOS
- libpng 1.6+, libjpeg (libjpeg-turbo) and libtiff 4.5+, with their headers, found through
pkg-config:- macOS:
brew install pkg-config libpng jpeg-turbo libtiff - Debian, Raspberry Pi OS, Ubuntu:
sudo apt install pkg-config libpng-dev libjpeg-dev libtiff-dev
- macOS:
venusian buildreads the system packages fromextra.venusian.systemin composer.json: apt packages to build with and the run-time ones a.debdepends on or recommends, and the Homebrew ones.
Installation
With PIE:
pie install php-io-extensions/imgdec
From a checkout, with the bundled installers. Each one checks for the three libraries, builds,
installs imgdec.so into the PHP's extension_dir, writes 30-imgdec.ini into its conf.d
directory, and checks that the extension loads:
./install-macos.sh # Homebrew php@8.4 and php@8.4-zts ./install-debian-trixie.sh # the php on PATH: Debian trixie, Raspberry Pi OS, Ubuntu 24.04+ ./install-macos.sh /path/to/bin/php # specific PHP binaries
By hand:
phpize && ./configure --enable-imgdec && make && make install echo 'extension=imgdec' > "$(php -r 'echo PHP_CONFIG_FILE_SCAN_DIR;')/30-imgdec.ini"
Usage
Decode a file
['width' => $width, 'height' => $height, 'rgba8' => $pixels] = imgdec_png(file_get_contents('legend.png')); // Four bytes a pixel, rows top to bottom: the pixel at (x, y) is $red = ord($pixels[($y * $width + $x) * 4]); $alpha = ord($pixels[($y * $width + $x) * 4 + 3]);
Decode what a server sends
$jpeg = file_get_contents('https://gibs.earthdata.nasa.gov/wmts/epsg4326/best/MODIS_Terra_CorrectedReflectance_TrueColor/default/2021-09-21/250m/2/1/2.jpeg'); $tile = imgdec_jpeg($jpeg); // ['width' => 512, 'height' => 512, 'rgba8' => <1048576 bytes>]
Pick the function by the file's first bytes: \x89PNG for PNG, \xFF\xD8\xFF for JPEG, II*\0
or MM\0* for TIFF.
Tell a broken file from an unsupported one
try { imgdec_tiff($bytes); } catch (ImgdecException $e) { match ($e->getCode()) { IMGDEC_CORRUPT => 'not a readable image', // the library's own message IMGDEC_UNSUPPORTED => 'a layout the rules leave out', // e.g. "photometric 5" for CMYK TIFF IMGDEC_TOO_LARGE => 'past the size limits', }; }
Functions
| Function | Reads | Answers |
|---|---|---|
imgdec_png(string $data): array |
PNG, through libpng | ['width' => int, 'height' => int, 'rgba8' => string] |
imgdec_jpeg(string $data): array |
JPEG, through libjpeg | the same |
imgdec_tiff(string $data): array |
classic TIFF, through libtiff | the same |
rgba8 is width × height × 4 bytes: red, green, blue, alpha, straight alpha, rows top to bottom.
Rules
- PNG: palettes and grey expanded to RGB, tRNS made alpha (matched on all 16 bits), 16-bit samples cut to their high byte, interlacing undone, no gamma applied, opaque images given alpha 255.
- JPEG: libjpeg's default decode; grey made RGB; CMYK and YCCK converted as gd converts them
(
(255 − c) × (255 − k) / 255), the samples inverted first when an Adobe marker says so; alpha 255. A warning, such as data that stops early, does not stop a decode. - TIFF: the first image; any compression libtiff reads; chunky or separate planes; strips or
tiles; orientation top-left; grey of either polarity at 1, 2, 4, 8 or 16 bits, RGB at 8 or 16, a
palette at 1, 2, 4 or 8 (colour map entries cut to their high byte); unsigned samples. A first
extra sample that is alpha is kept; associated alpha is divided out
(
c = round(c × 255 ÷ a), capped at 255). Anything else isIMGDEC_UNSUPPORTED, with the reason.
Exceptions and constants
ImgdecException extends Exception; its code is one of:
| Constant | Value | Meaning |
|---|---|---|
IMGDEC_CORRUPT |
1 | The bytes are not a readable image |
IMGDEC_UNSUPPORTED |
2 | A readable image in a layout the rules leave out |
IMGDEC_TOO_LARGE |
3 | Past one of the limits below |
IMGDEC_MAX_SIDE |
65535 | The longest side an image may have |
IMGDEC_MAX_PIXELS |
67108864 | The most pixels one image may hold (8192 × 8192) |
The limits are checked from the header, before any pixel buffer exists. A wrong argument type is a
TypeError.
Upgrading
This is the first release.
Testing
composer install php -d extension=/path/to/modules/imgdec.so vendor/bin/pest
The suite makes its own images with gd and an inline TIFF writer. Surface's tests/Images also
holds the extension to its PHP decoder, byte for byte.
Security
ext-imgdec parses image files, which often come from the network, through libpng, libjpeg and libtiff. See SECURITY.md for what it guards against and how to report a vulnerability.
License
MIT. See LICENSE.