All formats

jxrlib

LanguageC (usable from C++)
LicenseBSD-2-Clause
Repositorygithub.com/4creators/jxrlib (copy of Microsoft's release)
Documentationdoc/JPEGXR_DPK_Spec_1.0.doc in the repository, and the JxrDecApp.c / JxrEncApp.c sources
Depends onnothing
Read / writeyes / yes; all JPEG XR pixel formats (integer, half float, float, fixed point, CMYK, n-channel, alpha); HD Photo files

jxrlib is the JPEG XR “device porting kit” that Microsoft released as open source. It has 2 layers: libjpegxr, the core codec that works on the codestream, and libjxrglue, which handles the TIFF-like container, metadata and pixel format conversion. Applications use the glue layer through the PKCodecFactory, PKImageDecode, PKImageEncode and PKFormatConverter objects, which are C structs of function pointers.

Microsoft no longer develops it, and there’s no official repository; the GitHub copy above is used by vcpkg. Linux distributions package it as libjxr. The API is verbose and documented mainly by the sample apps. The code isn’t hardened against malicious files, so don’t decode untrusted input in a privileged process.

Installation#

Shell
# Debian / Ubuntu (library + JxrEncApp/JxrDecApp)
sudo apt install libjxr-dev libjxr-tools
# macOS
brew install jxrlib
# Windows (vcpkg)
vcpkg install jxrlib

The pkg-config name is libjxr. It adds the include paths and the defines the headers need on non-Windows systems (-D__ANSI__ -DDISABLE_PERF_MEASUREMENT):

Shell
cc decode.c $(pkg-config --cflags --libs libjxr) -o decode

Basic usage: decode a JXR file to RGBA#

The format converter turns the file’s pixel format into the one you ask for and copies pixels into your buffer. jxrlib’s Call() macro jumps to Cleanup when a function returns an error.

C
#include <JXRGlue.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    ERR err = WMP_errSuccess;
    PKCodecFactory *factory = NULL;
    PKImageDecode *decoder = NULL;
    PKFormatConverter *converter = NULL;
    U8 *pixels = NULL;
    I32 width = 0, height = 0;

    Call(PKCreateCodecFactory(&factory, WMP_SDK_VERSION));
    Call(factory->CreateDecoderFromFile("in.jxr", &decoder));
    Call(decoder->GetSize(decoder, &width, &height));
    printf("%d x %d\n", width, height);

    Call(factory->CreateFormatConverter(&converter));
    Call(converter->Initialize(converter, decoder, NULL, GUID_PKPixelFormat32bppRGBA));

    U32 stride = (U32)width * 4;
    pixels = malloc((size_t)stride * height);
    FailIf(pixels == NULL, WMP_errOutOfMemory);
    PKRect rect = {0, 0, width, height};
    Call(converter->Copy(converter, &rect, pixels, stride));
    printf("first pixel: %u %u %u %u\n", pixels[0], pixels[1], pixels[2], pixels[3]);

Cleanup:
    if (err < 0) fprintf(stderr, "jxrlib error %d\n", (int)err);
    free(pixels);
    if (converter) converter->Release(&converter);
    if (decoder) decoder->Release(&decoder);
    if (factory) factory->Release(&factory);
    return err < 0 ? 1 : 0;
}

Not every pair of pixel formats has a converter. HDR files (e.g. 64bpp RGBA half float from Windows screenshots) can’t be converted straight to 8-bit RGBA; Initialize then fails, and you copy the native format with decoder->Copy and tone-map it yourself.

How to get the pixel format and resolution#

C
PKPixelFormatGUID format;
Float dpi_x, dpi_y;
Call(decoder->GetPixelFormat(decoder, &format));
Call(decoder->GetResolution(decoder, &dpi_x, &dpi_y));

PKPixelInfo info;
info.pGUIDPixFmt = &format;
Call(PixelFormatLookup(&info, LOOKUP_FORWARD));
printf("%u channels, %u bits per pixel, alpha: %s\n", (unsigned)info.cChannel, info.cbitUnit,
       (info.grBit & PK_pixfmtHasAlpha) ? "yes" : "no");

if (IsEqualGUID(&format, &GUID_PKPixelFormat64bppRGBAHalf))
    printf("HDR: 16-bit half float RGBA (scRGB)\n");

How to copy pixels in the native format#

PKImageDecode has its own Copy, which decodes without conversion. Allocate cbitUnit / 8 bytes per pixel:

C
U32 bytes_per_pixel = info.cbitUnit / 8;
U32 stride = (U32)width * bytes_per_pixel;
U8 *native = malloc((size_t)stride * height);
PKRect rect = {0, 0, width, height};
Call(decoder->Copy(decoder, &rect, native, stride));

How to convert from the command line#

Shell
JxrDecApp -i in.jxr -o out.tif        # TIFF for >8 bits per channel and float
JxrDecApp -i in.jxr -o out.bmp        # BMP for 8-bit images
JxrDecApp -i in.jxr -o small.tif -T 2 # reduced resolution decode
JxrEncApp -i in.tif -o out.jxr        # lossless (default quality 1.0)
JxrEncApp -i in.tif -o out.jxr -q 0.8 # lossy, quality 0.0 - 1.0

The input of JxrEncApp is BMP (8 bits per channel), TIFF (8 bits and more) or Radiance HDR. Run either tool without arguments to list all options, such as -c for the pixel format, -d for chroma subsampling and -l for the overlap level.

Tips#