Skip to content

08. Async Decompression API

Aaron Boxer edited this page Mar 25, 2026 · 1 revision

8. Async Decompression API

Grok provides an asynchronous decompression API that enables swath-based tile retrieval. This is the pattern used by the GDAL JP2Grok driver for efficient random-access decoding of large tiled images.

Overview

The async API separates the decompression start from result retrieval:

  1. Start async decompression with grk_decompress() (called once)
  2. Wait for specific swath regions with grk_decompress_wait()
  3. Retrieve per-tile decoded data with grk_decompress_get_tile_image()
  4. For subsequent swaths, only call grk_decompress_wait() (no restart needed)

This is particularly efficient for:

  • Large tiled images where only a portion is needed
  • Cloud-hosted images where tiles arrive incrementally
  • Applications that need to process rows/strips progressively

Setup

Enable async mode by setting these fields in grk_decompress_parameters:

grk_decompress_parameters params = {};
params.asynchronous = true;
params.simulate_synchronous = true;
params.core.tile_cache_strategy = GRK_TILE_CACHE_IMAGE;
params.core.skip_allocate_composite = true;  // For multi-tile images

API Reference

grk_decompress_init

grk_object* grk_decompress_init(grk_stream_params* stream_params,
                                grk_decompress_parameters* params);

Initialize the decompressor with stream and parameter configuration.

grk_decompress_read_header

bool grk_decompress_read_header(grk_object* codec, grk_header_info* header_info);

Read the JPEG 2000 header. Populates header_info with image dimensions, tile grid, number of layers, resolutions, etc.

grk_decompress_update

bool grk_decompress_update(grk_decompress_parameters* params, grk_object* codec);

Update decompression parameters after header read (e.g., apply reduce factor, change tile cache strategy).

grk_decompress

bool grk_decompress(grk_object* codec, grk_plugin_tile* tile);

Start decompression. In async mode, this returns immediately and tiles are decoded in background threads. Pass NULL for the tile parameter.

grk_decompress_wait

void grk_decompress_wait(grk_object* codec, grk_wait_swath* swath);

Wait for decompression to complete.

  • If swath is non-null: waits for all tiles covering the swath region, then populates swath->tile_x0/y0/x1/y1 and swath->num_tile_cols with the tile grid indices covering the requested region.
  • If swath is null: waits for the entire decompression to complete.

grk_decompress_get_tile_image

grk_image* grk_decompress_get_tile_image(grk_object* codec, uint16_t tile_index, bool wait);

Get decoded image data for a specific tile. The tile index is computed as:

tile_index = tile_y * num_tile_cols + tile_x

Use the output fields from grk_wait_swath to compute tile indices. Set wait=true to block until the tile is ready.

grk_wait_swath

typedef struct grk_wait_swath {
    uint32_t x0;           // Input: top-left x (pixels)
    uint32_t y0;           // Input: top-left y (pixels)
    uint32_t x1;           // Input: bottom-right x (exclusive)
    uint32_t y1;           // Input: bottom-right y (exclusive)
    uint16_t tile_x0;      // Output: tile column start (inclusive)
    uint16_t tile_y0;      // Output: tile row start (inclusive)
    uint16_t tile_x1;      // Output: tile column end (exclusive)
    uint16_t tile_y1;      // Output: tile row end (exclusive)
    uint16_t num_tile_cols; // Output: total tile columns in image
} grk_wait_swath;

Example: Swath-based Decompression

#include "grok.h"

// Initialize
grk_initialize(NULL, 0, NULL);

grk_decompress_parameters params = {};
params.asynchronous = true;
params.simulate_synchronous = true;
params.core.tile_cache_strategy = GRK_TILE_CACHE_IMAGE;
params.core.skip_allocate_composite = true;

grk_stream_params stream = {};
safe_strcpy(stream.file, "image.jp2");

grk_object* codec = grk_decompress_init(&stream, &params);
grk_header_info header = {};
grk_decompress_read_header(codec, &header);
grk_decompress_update(&params, codec);

// Start async decompression
grk_decompress(codec, NULL);

// Process in swaths
uint32_t swathHeight = header.t_height;
for (uint32_t y = 0; y < header.header_image.y1; y += swathHeight) {
    grk_wait_swath swath = {};
    swath.x0 = 0;
    swath.y0 = y;
    swath.x1 = header.header_image.x1;
    swath.y1 = min(y + swathHeight, header.header_image.y1);

    grk_decompress_wait(codec, &swath);

    // Retrieve tiles in this swath
    for (uint16_t ty = swath.tile_y0; ty < swath.tile_y1; ++ty) {
        for (uint16_t tx = swath.tile_x0; tx < swath.tile_x1; ++tx) {
            uint16_t idx = ty * swath.num_tile_cols + tx;
            grk_image* tile = grk_decompress_get_tile_image(codec, idx, true);
            // Process tile->comps[c].data ...
        }
    }
}

// Cleanup
grk_object_unref(codec);
grk_deinitialize();

A full working example is available in examples/core/core_decompress_async.cpp.

Tile Cache Strategies

Constant Value Description
GRK_TILE_CACHE_NONE 0 No tile caching
GRK_TILE_CACHE_IMAGE 1 Cache final tile image (recommended for async)
GRK_TILE_CACHE_ALL 2 Cache everything

Random Access Markers

For efficient random access into large tiled images, use PLT/TLM markers during compression (-L and -X flags in grk_compress). During decompression, these markers can be selectively disabled with the -m flag:

Flag Value Marker
GRK_RANDOM_ACCESS_PLT 1 PLT (Packet Length in Tile-part)
GRK_RANDOM_ACCESS_TLM 2 TLM (Tile-part Length in Main header)
GRK_RANDOM_ACCESS_PLM 4 PLM (Packet Length in Main header)

Clone this wiki locally