image.PixelFormat, twenty interleaved formats from gray8 to rgba_f32_premul, and image.PixelBuffer, the caller's pixel memory with a stride.
image.convert: conversion and swizzle between any two pixel formats, with SIMD for the common pairs; convert.Converter picks the kernel once for many rows.
image.PixelFormat.cmyk8 and cmyk16: CMYK stored inverted, as Adobe's JPEGs store it (the maximum is no ink); image.convert turns them into color by libjpeg-turbo's naive conversion (SIMD from cmyk8) and color into them by its inverse.
image.PixelFormat.rgb_f32 and gray_f32, float formats beside rgba_f32; image.convert converts them to and from every format, with SIMD between float and 8- or 16-bit samples in the same layout (5–16× the general path for float to bytes).
image.Metadata.primaries (red, green, blue, and white xy, in place of the removed Metadata.chromaticities and image.Chromaticities; PNG's cHRM fills it, and png.EncodeOptions.primaries replaces chromaticities to write it), Metadata.intensity_target, Metadata.jpeg_reconstruction (the JPEG XL file carries a jbrd box), and image.Transfer.bt709, dci, pq, and hlg; a plain gamma stays in Metadata.gamma.
image.Config, image.Frame, image.Limits, image.DecodeOptions, image.Metadata, and image.DecodeError, shared by every decoder; image.exif.orientation reads EXIF orientation.
image.detect names a file's image.Format from its first image.detect_prefix_len bytes, for every phase-1 format.
image.Decoder decodes any supported format behind one interface; Decoder.initDetect picks the format from the input.
image.BlobSink takes blobs whose length is known only at their end: image.Blob.len is ?u64, null until the last piece, and Blob.endsAt says whether a piece ends its blob.
image.Decoder.decodeFrameConfig returns the next frame before decodeFrame draws it, for every format: GIF, ICO, PNG and APNG, and Netpbm read it ahead themselves, and one-frame formats report the canvas.
image.Decoder.finalConfig returns the Config updated with metadata a format finds after its frames, once decodeFrame has returned null.
image.qoi.Decoder, a streaming QOI decoder that writes any pixel format and needs no work memory.
image.bmp and image.ico: BMP decode (every header, 1- to 32-bit, RLE, bitfields; embedded JPEG and PNG reported as error.EmbeddedJpeg and error.EmbeddedPng) and encode (24-bit, 32-bit with alpha, 8-bit indexed); ICO and CUR decode of every entry with hotspots, out-of-order entries through DecodeOptions.seeker, and encode of BMP entries.
image.tga: Decoder reads every TGA image type (color-mapped, true-color, and gray, run-length encoded or not, any origin) and reports the TGA 2.0 footer and extension area through Decoder.trailer; encode, workLen, and EncodeOptions write uncompressed or run-length encoded files.
image.netpbm: PBM, PGM, and PPM (plain and raw), PAM, and PFM; netpbm.Decoder streams any pixel format with no work memory and reads a raw file's concatenated images as frames, and netpbm.encode writes every kind (netpbm.Kind, with EncodeOptions.maxval and the PFM byte order).
image.ico.Decoder.decodeFrameConfig reads the next entry's header and returns its frame before it is drawn.
image.ico.Decoder decodes PNG entries picked with Options.entry through image.png.
image.ico.encode writes 256×256 images as PNG entries (ico.Payload.auto, the default; .bmp and .png pick one), so ico.workLen is png.workLen for them; ico.encodeAll takes a work argument and Image.payload; ico.Decoder decodes a file's only entry when it is a PNG without Options.entry.
image.ico.Decoder decodes PNG entries among several entries without Options.entry, on the canvas; a file with several entries now reports Config.work_len of png.Decoder.maxWorkLen for the canvas width (about 76 KiB at 256), which decodeFrame requires even when every entry is a DIB.
image.png.EncodeOptions.max_depth (png.MaxDepth): .@"8" stores 16-bit and float sources at 8 bits a sample, still reduced, with no more work memory; image.ico.encode uses it, so its PNG entries are never 16-bit.
image.png.Decoder, a streaming PNG and APNG decoder: every color type and bit depth, Adam7, tRNS, APNG frames with their region, disposal, and blend, and gAMA, cHRM, sRGB, pHYs, bKGD, iCCP, and eXIf reported; CRCs of critical chunks and the Adler-32 are checked, image data must fill exactly the frame (except after the last row of an interlaced image one row tall, which is skipped, as in Wuffs), and it needs about 72 KiB plus two rows of work memory.
image.tga.Decoder.applyTrailer and image.png.Decoder.applyTrailer: image.Decoder.finalConfig reports TGA's extension-area gamma, aspect ratio, and key color, and a PNG eXIf that follows the image data with its orientation.
image.png.Decoder checks Limits.max_frames as frames are reached, not against acTL's count: a file whose acTL claims too many frames decodes the frames within the limit, with Config.frame_count null, and fails with error.TooManyFrames at the frame past it; gAMA, cHRM, sRGB, pHYs, bKGD, and iCCP after the image data are ignored, as the specification puts them before it.
image.png.Decoder.Options.ignore_checksums skips the critical chunks' CRCs and the Adler-32, as Wuffs's IGNORE_CHECKSUM quirk does; off by default.
image.jpeg.Decoder, a streaming JPEG decoder whose pixels match libjpeg-turbo's defaults bit for bit: baseline, extended, and progressive DCT images with 8- or 12-bit samples, Huffman- or arithmetic-coded, lossless images (process 14) of 2 to 16 bits (gray16, rgb16, and cmyk16 native past 8 bits), restart intervals, every sampling factor, grayscale, YCbCr, RGB, CMYK, and YCCK (to RGB by default, or to their inks as native cmyk8 with Options.cmyk = .raw), decoding at 1/2, 1/4, or 1/8 size (Options.scale), TIFF's JPEGTables (Options.tables, Options.color), edge tiles (Options.crop, a destination smaller than the frame), and work memory from a container's fields before the stream (Decoder.workLen), and JFIF density, EXIF orientation, and ICC (reassembled from its APP2 segments), EXIF, and XMP reported; lossless arithmetic coding and the hierarchical processes return error.Unsupported, as libjpeg-turbo does not decode them either.
- Decoders pull from a
std.Io.Reader whose buffer holds at least image.min_buffer_len (64) bytes, and read it forward; a format whose offsets can point backward (ICO) moves back only through the image.Seeker the caller passes in DecodeOptions.seeker, which image.Decoder passes through, and fails without one.
image.gif.Decoder, a streaming GIF87a and GIF89a decoder: interlacing, transparency, local and global color tables, animation frames with their region, disposal (restore to previous included), and blend, delays in flicks, and the NETSCAPE2.0 loop count; Decoder.decodeFrameConfig reports the next frame before it is drawn, and it needs about 68 KiB of work memory.
image.gif.Decoder reports Config.loop_count as 1, not null, for a file without a loop extension, as browsers play it once.
image.gif.Decoder.Options.ignore_too_much_data accepts a frame whose LZW data holds more pixels than the frame, dropping them, as Wuffs's IGNORE_TOO_MUCH_PIXEL_DATA quirk does; off by default, when such a frame is error.TooMuchData.
image.gif.Decoder reports ICC profiles and XMP packets to DecodeOptions.blob_sink and Config.metadata, and ones after the first frame through image.Decoder.finalConfig.
image.gif.encode and image.gif.encodeAnimation, with workLen, EncodeOptions, EncodeError, and AnimationFrame: GIF89a encode from any pixel format. An image or frame with at most 256 colors (alpha below 128 counting as one, transparent) is stored exactly; one with more is quantized (Quantizer.wu by default, or .median_cut and .octree, with EncodeOptions.refine k-means passes) and dithered (Dither.floyd_steinberg by default, or .none). Animations have per-frame delays and disposal, the NETSCAPE2.0 loop count (EncodeOptions.loop_count), and frames cropped to what changes (EncodeOptions.crop); EncodeOptions.interlace interlaces. Work memory is about 2.4 MiB for an image of more than 256 pixels (plus a byte a pixel to interlace a dithered one), and 6 KiB otherwise.
image.webp.Decoder, a streaming WebP decoder for lossless (VP8L) and lossy (VP8) files, simple and extended, with ALPH alpha (raw or lossless, every filter) and ICCP, EXIF, and XMP reported; lossy pixels match libwebp's default output exactly, and Options.max_prefix_groups (default 2600) bounds its work memory; animations decode frame by frame into their regions of the caller's canvas, blended as libwebp blends them, with Config.loop_count and Config.background from ANIM and Decoder.decodeFrameConfig for the next frame; ICCP, EXIF, and XMP chunks after the image reach image.Decoder.finalConfig.
image.webp.Decoder.Options.seeker: an animation frame whose ALPH chunk is another size than its ANMF chunk says decodes at its VP8 frame's size, as libwebp decodes it; without a seeker such a frame fails with error.AlphaBehind.
image.tiff.Decoder, a streaming TIFF and BigTIFF decoder: strips and tiles, chunky and planar; uncompressed, PackBits, LZW, Deflate, CCITT modified Huffman, Group 3, and Group 4, and JPEG data (new style, 8- or 12-bit, through image.jpeg, with JPEGTables and the tables one strip's stream defines kept for the next, as libjpeg keeps them, and gray with alpha as two components); predictors 2 and 3; 1- to 32-bit integer and 16- to 64-bit float samples; gray, palette, RGB, CMYK (its inks as cmyk8 or cmyk16, which image.convert turns into color), and subsampled YCbCr; every page a frame with its own size, which Decoder.decodeFrameConfig reports before the page is decoded. IFDs and data behind the reader need DecodeOptions.seeker (error.DataBehind without one); progressive or non-interleaved JPEG streams in TIFF are error.Unsupported.
image.webp.lossy.encode, with workLen, EncodeOptions, EncodeError, and Effort: a lossy WebP (VP8) encoder from any pixel format, EncodeOptions.quality 0 to 100 (default 75) on cwebp's scale, three efforts (.fast, .default, .best), four segments with their own quantizer and loop filter level, alpha in an ALPH chunk compressed by the lossless encoder when that is smaller (EncodeOptions.alpha_compression, on by default; off leaves the lossless encoder's work memory out of workLen), colors under fully transparent pixels treated as free, and ICCP, EXIF, and XMP in a VP8X file; error.EmptyImage for an image without pixels, and error.ImageTooLarge past 16383 pixels a side.
image.png.encode and image.png.encodeAnimation, with workLen, EncodeOptions, and EncodeError: PNG and APNG encode that writes the smallest lossless color type and bit depth (palette, gray, bit depths from 1 to 16, tRNS), chooses each row's filter, has three efforts (Effort.fast, .default, .best), writes APNG frames with region, disposal, and blend, and writes gAMA, cHRM, sRGB, iCCP, pHYs (EncodeOptions.density), and eXIf; work memory is about 831 KiB plus 72 bytes per pixel of width.
image.tiff.encode and image.tiff.encodePages: classic little-endian TIFF, one page or several, uncompressed or PackBits, LZW (the default), or Deflate (EncodeOptions.compression, .effort), Predictor 2, strips of about 8 KiB (.rows_per_strip), gray, gray and alpha, RGB, RGBA (straight or associated), and CMYK at 8 or 16 bits, a palette when it is smaller (.palette), and resolution (.density); files read forward, without a seeker, with uncompressed, PackBits, and LZW strips byte for byte as libtiff writes them; tiff.workLen gives the work memory.
image.EncodeError, the errors every encoder shares; each format's encoder will be encode(writer, pixels, options, work) with workLen for its work memory.
image.jxl.Decoder, a streaming JPEG XL decoder: VarDCT and Modular frames, XYB and YCbCr, progressive passes, the restoration filters, upsampling, patches, splines, noise, animation with blending and reference frames, extra channels, bare codestreams and containers, and the ICC profile, Exif, and xml boxes reported; rgba_f32 and rgba_f32_premul destinations are written from its floats, without a 16-bit step (clamped to [0, 1] for integer images).
image.jxl.Decoder.decodeFrame returns the last frame once its codestream is complete and reads the container boxes after it on the next call, so a broken or cut-off box there fails that call (with the image already returned) instead of losing the image.
image.jxl.Decoder reports Metadata.primaries, Metadata.intensity_target, and the BT.709, DCI, PQ, and HLG transfer functions from the colour encoding; for an XYB image with an ICC profile, linear sRGB, the space its pixels are written in (the profile describes the original).
image.jxl.Decoder decodes a large single-frame VarDCT image (five group rows or more, without patches, splines, noise, upsampling, or chroma subsampling) a group row at a time into the caller's buffer: peak work memory on a 50-megapixel photograph falls from 627 MB to 127 MB, and Config.work_len with it when the image has no alpha.
image.jxl.Decoder.decodeJpeg and jpegWorkLen read a recompressed JPEG's quantization tables, sampling, and quantized coefficients (Decoder.Jpeg) without decoding pixels, or fail with error.NotJpeg (also when the DCT8 table is raw over a denominator other than a JPEG's 1/(8·255), which libjxl will not rebuild either); they are what JPEG reconstruction from jbrd builds on.
image.jxl.JpegReconstructor rebuilds, byte for byte, the JPEG a JPEG XL file was transcoded from (cjxl --lossless_jpeg=1), from its jbrd box and coefficients with no pixels: init, workLen, and run(writer, work); it needs DecodeOptions.seeker (error.NeedsSeeker), and a file without the box is error.NoJbrd.
- The package depends on
compress at commit 61998af (after 0.2.0, for its faster inflate, its lazy deflate levels, and its writer's block scratch in the caller's buffer), which brings fastmem.
image.jxl.encode, workLen, EncodeOptions, and Effort (fast, default, best): lossless JPEG XL (Modular) encoding of any pixel format to a bare codestream, at 8 bits for 8-bit sources and 16 otherwise (CMYK as RGB), with a palette or color transform and a learned tree, decoding bit for bit in libjxl and jxl-oxide.
image.wbmp: WBMP type 0, a streaming wbmp.Decoder that writes any pixel format, and wbmp.encode, which stores gray of at least 128 as white.
image.qoi.encode, qoi.workLen, and qoi.EncodeOptions: a QOI encoder that writes the same bytes as the reference encoder (qoi.h) and needs no work memory.
image.jpeg.Decoder.frameParams returns the frame header after decodeConfig, Decoder.keptTables and Options.prior carry a stream's quantization and Huffman tables into the next as libjpeg does across TIFF strips, Options.tables reads a tables stream as libjpeg reads a header (SOFn checked and dropped, DNL, RSTn, and TEM passed over), and two-component streams decode to native gray_alpha8 or gray_alpha16.
image.jpeg.encode, jpeg.workLen, jpeg.EncodeOptions, and jpeg.Subsampling: a JPEG encoder that writes libjpeg-turbo's bytes for the same settings: baseline or progressive, 4:4:4, 4:2:2, or 4:2:0 (one component for gray sources), standard or optimized Huffman tables, libjpeg's quality scaling, JFIF with EXIF and ICC written when given (error.MetadataTooLarge past what the segments hold), from any pixel format with alpha dropped; a baseline file with standard tables streams an MCU row at a time.
image.webp.encode, webp.workLen, and webp.EncodeOptions: a lossless WebP (VP8L) encoder with the subtract-green, predictor, color, and color-indexing transforms, LZ77 with a color cache, and prefix codes per tile, at effort 0 to 9 like cwebp's -z (default 5); ICC, EXIF, and XMP go in a VP8X file. webp.encodeAlpha writes an alpha plane as the lossless stream of an ALPH chunk.
image.Metadata.cicp (Metadata.Cicp, ITU-T H.273 code points), Metadata.mastering_display (Metadata.MasteringDisplay, xy and nits), and Metadata.content_light_level (Metadata.ContentLightLevel, MaxCLL and MaxFALL in nits): image.png.Decoder reads PNG's cICP, mDCV, and cLLI, and a cICP now decides Metadata.transfer, gamma, and primaries over iCCP, sRGB, gAMA, and cHRM (null for code points they cannot name), and the first of each chunk with the right length decides even when its values are invalid, as in libpng; image.png.EncodeOptions.cicp, mastering_display, and content_light_level write them.
image.Metadata.gain_map: image.jpeg.Decoder flags a JPEG that carries an HDR gain map (Ultra HDR, Adobe's gain map, or ISO 21496-1); the pixels are still the base image's, and the gain map is not decoded.
- Faster where large buffers are cleared, since Zig 0.16 links compiler_rt's byte-at-a-time
memset even with libc: BMP RLE decode 1.57× and ICO decode 1.48× (geomean), JPEG decode 1.14× (progressive 1.26×), JPEG in TIFF 1.21×, TIFF LZW encode 1.11× (2.2× on 16×16), and GIF and WebP lossless encode 1.05–1.15×.
image.jxl.encode writes smaller files at .fast (2.7% on the benchmark corpus) and .default (0.9%, cjxl e7's size) at about 0.8× the speed above icon size, encodes icons 1.2–1.7× faster, and touches far less memory up to a megapixel (peak over the corpus 158 to 124 MiB at .default, 255 to 144 MiB at .best).
image.jxl.encode at .default encodes about 10% faster above icon size, for files 0.15% larger on the benchmark corpus: its tree stops at 1024 leaves.
image.jxl.encode gives each 256×256 group of a larger image its own palette or color transform: screenshots and other mixed images come out 3–10% smaller at .default and .best, and the benchmark corpus 0.8%, 1.4%, and 1.5% smaller at the three efforts.
image.jxl.encode encodes the same files faster: icons 1.3–1.5×, images up to a megapixel 1.06×, 1.22×, and 1.39× at .fast, .default, and .best.
image.jxl.workLen asks for far less work memory for small images: at 16×16, 0.5, 0.8, and 3.8 MiB at .fast, .default, and .best (was 3.8, 5.6, and 8.1), and at 64×64 2.5, 3.4, and 6.8 MiB (was 5.4, 8.8, and 11.8).
image.jxl.encode at .default learns from more of an image above a megapixel (one sample in 16, up to 2^20): such images come out 1.0% smaller on the benchmark corpus (screenshots 1.2–2.3%), for about 9% more time on them and up to 25 MB more workLen (peak over the corpus 126 to 142 MiB); smaller images are unchanged.
image.jxl.encode keeps its learning samples in less memory, for the same files: peak memory over the benchmark corpus falls from 142 to 131 MiB at .default and 143 to 131 MiB at .best, and image.jxl.workLen asks for less (2048×2048 at .default: 161 to 128 MB; 256×256: 21 to 13 MB).
image.png.Decoder decodes an APNG whose default image's fcTL gives a region smaller than the canvas as a frame of the whole canvas, as libpng and libspng show it, instead of failing with error.BadChunk.
image.webp.encodeAlpha writes alpha planes 5–9% smaller and faster at every effort (at effort 1, 8.8% smaller in 18% less time), and effort 9 is now its smallest; the output is a different, still lossless stream.
image.gif.encode quantizes small images faster with Quantizer.wu: 1.16× on 32×32 and 64×64 icons and icon animations, with the same palettes.
image.gif.Dither.floyd_steinberg passes on no error from a pixel within 1 a channel of its palette entry: 1.3% smaller files on photos and screenshots (up to 14% on graphics), with PSNR +0.36 dB and SSIM +0.003.
image.gif.encode stores an image of at most 256 colors 1.14–1.25× faster, reading its pixels once instead of twice up to about 2.4 megapixels, with the same bytes and work memory.
image.webp.encode is faster, up to 1.19× on icons and 1.15× to 1 MP, with the same output except at efforts 1 and 2, which stop searching for matches at 64 pixels (0.1% larger, up to 5% on flat images); webp.workLen asks for 0.17–0.31 MB on a 16×16 icon, from 0.52–4.1 MB.
image.webp.encode is 1.28× faster over the benchmark rows with the same output, 1.17–2.66× on 16 KP–1 MP images at efforts 1 to 9 and up to 2.5× on flat images at effort 5; webp.encodeAlpha is 1.28×, 1.79×, and 2.71× faster at efforts 1, 5, and 8; webp.workLen asks for up to 36 KiB less at efforts 1 to 7 and up to 228 KiB more at 8 and 9.
image.jpeg.encode is faster: baseline color over 1 MP from 0.93× to 1.18× libjpeg-turbo's speed (4:4:4 from 0.98× to 1.26×), and 2.16× over all encode benchmark rows, with the same bytes.
image.convert runs a row's last pixels through its SIMD kernels instead of the per-pixel path, and RGB↔BGR stores whole cache lines: narrow rows convert up to twice as fast, so 24- and 32-bit BMP and TGA decodes of narrow images gain up to 25%; convert.Converter.run may convert in place between formats of one pixel size.
image.png.Decoder computes the chunks' CRC-32 with carry-less multiplication on x86-64 CPUs that have PCLMULQDQ, about 4% of a decode.
image.tiff.encode writes Deflate strips 1.04–1.05× faster and image.png.encode icons 1.03–1.04×, from compress 61998af, which clears its encoder state without compiler_rt's byte-wise memset.
image.tiff.Decoder decodes Deflate photographs 1.02–1.05× faster and image.png.Decoder images past icons 1.03×, from compress e383aa8's inflate loop.
image.tiff.Decoder decodes Deflate data 1.11–1.19× faster (1.02–1.12× libtiff's speed past icons) and image.png.Decoder decodes icons 1.15–1.2× faster and larger images 1.03×, from compress 364ef40's inflate, which builds a block's Huffman tables and reads its header faster.
image.tiff.Decoder decodes LZW data about 1.8× faster past icons (at libtiff's speed), Deflate 1.3–1.4×, PackBits 3–3.4×, and Group 4 2.6–3.2×, and icons of every compression 1.3–3× faster; an LZW page needs 24 KiB more work memory (Config.work_len).
image.png.Decoder unfilters consecutive Average or Paeth rows two at a time, with no more work memory: up to 1.28× faster on Paeth-filtered images such as the 50-megapixel test photo.
image.jxl.Decoder decodes lossy JPEG XL about 1.3× faster (up to 1.8× on large images, 1.04× on 16×16 icons) and lossless 1.16× faster, and image.jxl.JpegReconstructor rebuilds JPEGs 1.35–1.54× faster (up to 2× past icons), with no more memory; lossy pixels can differ by one 16-bit step in under 0.02% of samples, and Decoder.jpegWorkLen asks for less (9.2 MB on a 16×16 file, from 10.0; 25 MB on a 768×512 photo, from 37).
image.jxl.Decoder decodes lossy JPEG XL about 1.14× faster again (1.2–1.3× on 16×16 and 64×64 icons, 1.04–1.06× on large photos), and image.jxl.JpegReconstructor 1.02× faster, with the same pixels and memory.
image.jxl.Decoder decodes lossy JPEG XL about 1.06× faster again (1.08–1.13× on photos) and lossless 1.09× faster, and image.jxl.JpegReconstructor 1.03× faster, with the same pixels and memory; Config.work_len grows by 393 KB.
image.jxl.JpegReconstructor rebuilds baseline JPEGs about 1.27× faster and progressive JPEGs 1.47× faster, with byte-identical output and the same memory.
image.jxl.JpegReconstructor rebuilds progressive JPEGs about 1.04× faster again (up to 1.1×), with byte-identical output and the same memory.
image.jxl.JpegReconstructor rebuilds small JPEGs faster: 16×16 icons about 1.2× (baseline) and 1.15× (progressive), 32×32 1.14× and 1.09×, with byte-identical output and the same memory.
image.jxl.Decoder decodes a lossless (Modular) image of two group rows or more without global transforms a group row at a time, and keeps only two group rows of a streamed lossy image's alpha: a 50-megapixel lossless photograph touches 29 MB of work memory instead of 608 MB, and tall screenshots 11 MB instead of 105–134 MB, with the same pixels and speed; Config.work_len is unchanged, since it must cover frames with global transforms, which decode whole.
image.jxl.Decoder also streams lossless images and lossy alpha whose global transforms are RCTs or palettes without deltas, undoing them a group row at a time (768×512 lossless photographs with a palette touch 3.4 MB of work memory instead of 5.8 MB); only squeezes and delta palettes still decode the frame whole.
image.jxl.Decoder also streams JPEG XL files recompressed from 4:2:0, 4:2:2, and 4:4:0 JPEGs, upsampling their chroma a group row at a time: a 50-megapixel recompressed photo touches 120 MB of work memory instead of 686 MB, and Config.work_len drops from 744 MB to 244 MB.
image.jxl.Decoder also streams lossy images with noise, upsampling (--resampling), or patches, a band at a time: a 4000×2667 photo with --photon_noise_iso touches 81 MB of work memory instead of 260 MB, and Config.work_len drops from 369 MB to 185 MB.
image.jxl.Decoder keeps two group rows of a streamed lossy image instead of three, and streams images from four group rows tall instead of five: the 4000×2667 noise photo touches 69 MB instead of 81 MB, a 50-megapixel photo's peak RSS falls from 507 MiB to 484 MiB, and Config.work_len drops by about a group row (185 MB to 173 MB on the noise photo), at the same speed.
image.jxl.Decoder also streams lossy images with splines: an 800×2200 upsampled image with alpha and splines touches 15 MB of work memory instead of 40 MB; Config.work_len for frames with splines grows by up to 1 MiB plus 4 bytes a pixel, for the splines readied to draw.
image.jxl.Decoder writes a streamed upsampled (--resampling) image 64 output rows at a time instead of a group row: a 4000×2667 2× photo's peak RSS falls from 128 MiB to 107 MiB, and Config.work_len from 161 MB to 139 MB, at the same speed.
image.jxl.Decoder decodes lossless files whose trees test one property, as cjxl -e 2 and -e 3 write them, 1.79× and 1.49× faster (about 2× and 1.6× on photos), with the same pixels and memory.
image.jxl.Decoder writes lossy images to 8-bit destinations (rgba8, rgb8, grey, and their premultiplied forms) without converting through 16 bits, about 1% faster, with the same pixels.
image.jxl.Decoder decodes lossy JPEG XL about 1.5% faster with a cheaper sRGB curve; 0.04% of 16-bit samples move by one step, and no file's largest difference from libjxl grows.
image.png.Decoder expands 8-bit palette images up to 1.23× faster.
image.qoi.encode encodes packed rgba8 and rgb8 sources as one span instead of row by row, and checks one bound per pixel instead of two: about 1.16× faster on icons and images up to 1 MP, up to 1.7× on screenshots of runs and INDEX chunks.
image.qoi.Decoder checks one count per chunk instead of two bounds: about 1.03× faster, up to 1.16× on run- and INDEX-heavy images.
image.qoi.Decoder decodes images of every size with one loop and takes RGBA chunks on their own branch: a stream of distinct icons decodes 1.07–1.75× faster and other images up to 1.28×, while one 16×16 or 32×32 icon decoded again and again is up to 1.5× slower.
image.qoi.Decoder decodes about 1.2× faster, up to 2.2× on screenshots made mostly of INDEX chunks and runs.
image.webp.Decoder decodes lossless (VP8L) files about 1.24× faster (1.37× on icons, up to 2.1× on palette screenshots with long runs), by adding green back and copying backward references in SIMD blocks, building prefix tables without scanning unused lengths, and specializing the pixel loop; no more work memory.
image.webp.lossy.encode writes smaller files: trellis quantization at .default and .best (1.6% and 2.5% smaller at equal PSNR, 4–9% slower at .default above icon sizes and 16% on icons, .best 1.6× slower), and alpha compressed at the lossless encoder's efforts 1, 1, and 8 (3.6% smaller at .fast on images with transparency); .fast runs about 1.35× faster for the same files; with alpha_compression, workLen is about 29 bytes a pixel at .fast and .default and 38 at .best.
image.webp.lossy.encode is faster on small images, for the same files at .fast and .default: a 16×16 icon takes about 0.45× the time at .default and 0.3× at .fast (icons up to 64×64 1.5× and 2.1× faster on average); .best is 1.3–1.8× faster, its first pass now searching as .default does, for files the same size.
image.webp.Decoder decodes lossy (VP8) files about 5% faster again, and lossy animations about 4%, by clearing coefficients with vector stores.
image.webp.Decoder decodes lossy (VP8) files about 1.25× faster (1.31× on icons, 1.20× over 1 MP), with a faster boolean decoder and YUV-to-RGB conversion in 16-bit lanes that keeps the CPU out of 512-bit vectors; pixels are unchanged.
image.netpbm.Decoder reads plain (ASCII) PBM, PGM, and PPM samples about 1.75× faster, and headers about 1.4× faster, which shows on 16×16 files.
image.jxl.Decoder.Jpeg.coefs from Decoder.decodeJpeg holds each block in zigzag order, the order a JPEG's scans code them, instead of natural order (the DC stays first; Jpeg.quant stays in natural order); image.jxl.JpegReconstructor rebuilds progressive JPEGs 2.6% faster and baseline 0.9% faster for it.
image.jxl.Decoder no longer reads past the alpha channel when a lossy (VarDCT) frame with patches and alpha is not a multiple of 8 pixels wide; it read the wrong rows there, or panicked in safe builds.
image.jxl.Decoder decodes lossy grey JPEG XL images with patches correctly; it copied the patches' X into Y and B, so patched pixels came out wrong by up to full scale.