Context
The core refactor in #335 gives us a useful boundary: core tile topology produces lazy, canonical TileSpecs (stable ID/ordinal, request, destination, expected size), while the native app owns acquisition, decode and output. However, the app still couples those concerns around a whole-tile download_tile_and_then operation and TileBuffer.
Today fetch_resource buffers each complete response in a Vec<u8>; processing and image-rs decode then produce a complete DynamicImage. buffer_unordered(parallelism) overlaps fetch+decode across tiles, and a bounded channel overlaps that work with the encoder, so this is not a strict download/decode/encode phase pipeline. But memory can include concurrent response buffers and decoded tiles, up to 1024 queued tiles, plus encoder-specific retention. PNG's PixelStreamer may retain out-of-order tiles until earlier pixels arrive; JPEG/generic outputs retain the full canvas; unknown dimensions buffer tiles; IIIF retiling uses temporary partial tiles; TIFF/ZIF can often consume encoded tiles directly. This is relevant to #273.
Progress labels obscure this overlap: "Requesting the tiles..." can include decode, waiting on encoder capacity and throttling, while "Downloaded all tiles. Finalizing..." can include draining queued tile writes as well as actual encoder finalization. Downstream backpressure can therefore look like slow networking.
Desired properties
- Keep fetch, optional processing, decode/inspection, assembly and encode overlapping where the format permits; avoid introducing phase barriers.
- Bound memory by meaningful work/bytes rather than a large fixed tile count, with backpressure reaching request scheduling.
- Preserve configurable network concurrency, retries/cache behavior, blocking CPU isolation, deterministic placement, missing-tile handling and clean cancellation/error propagation.
- Respect output ordering without globally serializing downloads. Use the core's canonical tile identity/topology where useful, but keep scheduling and I/O policy out of
dezoomify-core.
- Report distinct progress/stalls for acquisition, decode and output, including time blocked downstream.
- Keep fast paths such as encoded JPEG tile passthrough to ZIF/TIFF and direct source-pyramid output.
Questions / possible directions
- Should the app expose explicit bounded stages (acquired payload, decoded/inspected tile, ordered assembly window, encoder sink), or is a smaller task graph with shared byte permits simpler? How should permits account for compressed bytes versus decoded pixels?
- Can known row-major grids schedule a limited look-ahead window so PNG emits scanlines promptly, while random-access encoders continue accepting unordered completion? How should positioned, overlapping and adaptive/unknown-size sources differ?
- Where is the retry boundary? Decode/validation failures currently retry the download too; streaming must not commit irreversible output before an attempt is known usable, unless the sink supports rollback/idempotent replacement.
- Should
Encoder advertise capabilities/ordering requirements (decoded vs encoded, random access vs sequential, known-size requirement) instead of TileBuffer inferring behavior from the extension?
- Could queue completion/error handling and progress become one supervised pipeline, so encoder failure promptly cancels fetch/decode work?
A realistic first target is bounded whole-tile flow: avoid duplicate ownership, use byte/pixel-weighted permits, reduce/reorder the encoder queue, schedule near the next required output region, and make backpressure observable. The theoretical ideal is incremental response bytes feeding an incremental decoder and scanlines feeding an encoder. That may be feasible for selected codec pairs, but many image-rs decoders, metadata extraction, decryption/cache writes and retry validation need seekable or complete input; buffering one tile (in memory or temporary storage) may remain the correct boundary. The architecture should permit specialized streaming/passthrough paths without requiring every codec to support them.
Context
The core refactor in #335 gives us a useful boundary: core tile topology produces lazy, canonical
TileSpecs (stable ID/ordinal, request, destination, expected size), while the native app owns acquisition, decode and output. However, the app still couples those concerns around a whole-tiledownload_tile_and_thenoperation andTileBuffer.Today
fetch_resourcebuffers each complete response in aVec<u8>; processing and image-rs decode then produce a completeDynamicImage.buffer_unordered(parallelism)overlaps fetch+decode across tiles, and a bounded channel overlaps that work with the encoder, so this is not a strict download/decode/encode phase pipeline. But memory can include concurrent response buffers and decoded tiles, up to 1024 queued tiles, plus encoder-specific retention. PNG'sPixelStreamermay retain out-of-order tiles until earlier pixels arrive; JPEG/generic outputs retain the full canvas; unknown dimensions buffer tiles; IIIF retiling uses temporary partial tiles; TIFF/ZIF can often consume encoded tiles directly. This is relevant to #273.Progress labels obscure this overlap: "Requesting the tiles..." can include decode, waiting on encoder capacity and throttling, while "Downloaded all tiles. Finalizing..." can include draining queued tile writes as well as actual encoder finalization. Downstream backpressure can therefore look like slow networking.
Desired properties
dezoomify-core.Questions / possible directions
Encoderadvertise capabilities/ordering requirements (decoded vs encoded, random access vs sequential, known-size requirement) instead ofTileBufferinferring behavior from the extension?A realistic first target is bounded whole-tile flow: avoid duplicate ownership, use byte/pixel-weighted permits, reduce/reorder the encoder queue, schedule near the next required output region, and make backpressure observable. The theoretical ideal is incremental response bytes feeding an incremental decoder and scanlines feeding an encoder. That may be feasible for selected codec pairs, but many image-rs decoders, metadata extraction, decryption/cache writes and retry validation need seekable or complete input; buffering one tile (in memory or temporary storage) may remain the correct boundary. The architecture should permit specialized streaming/passthrough paths without requiring every codec to support them.