tiff 0.5.2 copy "tiff: ^0.5.2" to clipboard
tiff: ^0.5.2 copied to clipboard

Dart library for reading and writing TIFF and BigTIFF: tags, IFDs, strips/tiles, compression, GeoTIFF/EXIF metadata, plus optional image and Flutter minimap bridges.

0.5.2 #

  • BandedDownsampler.downsampleParallel and TiffDisplayOptimizer.optimizeLargeSourcePyramidLevelsParallel's workerCount and maxBandBytes are now optional (int?) instead of required — and the library itself resolves them when left unset, via TiffAutoDecodeBudget.recommend reading actual idle system memory and CPU count internally, rather than leaving that to be reimplemented by every caller. Pass either explicitly to override just that one; the other still comes from the same recommended budget unless it's overridden too.
  • New levelCount parameter on TiffDisplayOptimizer.optimize, optimizeLargeSourcePyramidLevels, and optimizeLargeSourcePyramidLevelsParallel: build exactly this many pyramid rungs instead of deriving the count from minPyramidDimension (minPyramidDimension is still what picks the count when levelCount is left unset, the existing default behavior — already a sensible "auto" choice, since it stops at a rung small enough to display smoothly without any further downsampling at read time). For TiffOptimizationMode.tiledPyramid this includes the base resolution itself; for TiffOptimizationMode.pyramidLevelsOnly and the two large-source functions (which never re-encode the base) it counts only the smaller rungs — either way, the same number TiffOptimizeProgress.levelCount reports. Throws ArgumentError if it isn't > 0, or if it's small enough to leave no smaller rung to build where a mode requires at least one.

0.5.1 #

  • Faster TiffDisplayOptimizer.optimize/optimizeLargeSourcePyramidLevels, aimed squarely at the pyramid-cache-building path (see TiffOptimizationMode.pyramidLevelsOnly): TileWriter's per-tile scratch buffer was a generic, boxed List<int> (8 bytes/element) filled one sample at a time even for the common 8-bit-RGB case, where 1 byte/element and a bulk setRange copy is all that's needed; ChunkEncoder and PixelPacker copied and repacked each row through more per-element loops than an already-byte-shaped 8-bit row needs. All three now use typed buffers and bulk copies instead, which is where most of a large pyramid's build time was actually going — decoding and downsampling the source itself is comparatively fast. ImageResampler.downsampleRgba8 also gets a dedicated fast path for an exact 2x2 box filter (every pyramid rung after the first is exactly half the size of the one before it) and no longer recomputes each output column's source span on every single output row.
  • TiffOptimizeProgress (returned via optimize's/ optimizeLargeSourcePyramidLevels's onProgress) is richer and far more granular — this is a breaking change to its shape. It used to fire once per pyramid rung plus once at the very end, with the (often largest) final tile-and-compress pass reported as a single opaque step. It now carries a TiffOptimizeStage (decoding, downsampling, or encoding), a level/levelCount locating which pyramid rung an update belongs to, and a stepIndex/stepCount for progress within that stage — e.g. one update per row-band while optimizeLargeSourcePyramidLevels banded-decodes its first rung, and one update per tile while every rung is compressed. fraction is now weighted by how many pixels each phase actually touches rather than by rung count, so it tracks real elapsed time much more closely — a caller driving a progress bar or status message off it gets both a smoother bar and a much better idea of what's actually happening (e.g. "decoding source, band 12/40" vs. an unmoving bar during what used to be the single biggest, totally silent step).
  • New BandedDownsampler.downsampleParallel and TiffDisplayOptimizer.optimizeLargeSourcePyramidLevelsParallel: the same banded-decode-then-box-filter downsample as BandedDownsampler.downsample/ optimizeLargeSourcePyramidLevels, but spreading the decode of a huge source's first pyramid rung across multiple isolates instead of one band at a time on a single core — real multi-core parallelism for what's usually the dominant cost of building a pyramid for a truly large source. Bit-for-bit identical output to the sequential versions given the same maxBandBytes; only which isolate decodes each band (and how long it takes) differs. Both are purely additive — the existing synchronous downsample/optimizeLargeSourcePyramidLevels are unchanged and still the right choice for a source small enough that isolate-spawn overhead wouldn't pay for itself.
  • Small, safe decode-side speedups in the same spirit as the encode-side ones above: ChunkDecoder's per-row unpack now bulk-copies into its output buffer instead of a per-element loop; PixelUnpacker.unpackRow bulk-copies the common 8-bit case instead of one ByteData.getUint8 call per byte; RgbTransform/GrayscaleTransform skip their scale-to-0..255 math entirely for 8-bit sources, where it's always the identity; and BandedDownsampler/ImageResampler.downsampleRgba8's box-filter finalization uses integer round-half-up instead of a double divide and .round() per channel per pixel.

0.5.0 #

  • New package:tiff/tiff_minimap.dart entry point, adding a TiffMinimap widget: a ready-made overview-with-viewport-rectangle for panning/zooming a large TIFF page (tap or drag it to jump the main viewer there). It's decode-agnostic — hand it whatever already-decoded ui.Image overview bitmap and native page dimensions your own tiled/banded/isolate-based loading strategy already produces, and it handles the layout, the viewport-rect overlay, and tap/drag navigation. This is the only library entry point that imports package:flutter — every other one (tiff.dart, tiff_io.dart, tiff_image_adapter.dart) stays usable from plain Dart with no Flutter SDK involved. Adding flutter as a package:tiff dependency (needed so this entry point resolves at all, even for a consumer that never imports it) means a plain-Dart project (no Flutter SDK available) can no longer depend on package:tiff — a breaking change for that case.

0.4.0 #

  • TiffDisplayOptimizer.optimize's new TiffOptimizationMode.pyramidLevelsOnly builds the same progressively-halved, tiled rungs as tiledPyramid, but without re-encoding the base resolution itself — the output holds only the smaller rungs, a small fraction of what tiledPyramid writes, since the (by far largest) base-resolution copy is never duplicated. Meant for a caller that wants extra zoom-out levels as a disposable, sidecar cache next to a source that already serves its own base resolution well (e.g. it's already tiled), rather than a full standalone replacement file for it. Throws ArgumentError if the page's longest side is already at or below minPyramidDimension, since there'd be nothing smaller to build.
  • New TiffDisplayOptimizer.optimizeLargeSourcePyramidLevels builds the same output as pyramidLevelsOnly, but for a source too large to safely decode as one whole RGBA8 buffer — optimize always decodes the whole page first regardless of mode, which for a real multi-gigapixel page can itself be too large to hold in memory before any downsampling even starts. This instead derives the first rung at or below maxDirectDecodePixels via the new BandedDownsampler, which decodes the source in row bands (bounded by maxBandBytes) rather than all at once — bit-for-bit equivalent to what ImageResampler.downsampleRgba8 would produce given the whole source at once, just computed in bounded memory. Every rung after that first one reuses the same in-memory halving optimize itself uses, since each is smaller than the last and therefore safe by construction. A rung above maxDirectDecodePixels is never produced at all (not even via banding) — this exists to help only the far zoomed-out end a viewer's own bounded-memory region/tile decode of the source doesn't serve well.

0.3.0 #

  • Decoded, unpacked samples (TiffRasterBuffer.samples, and the equivalent intermediate buffer in ChunkDecoder.decodeChunk/PixelUnpacker.unpackRow) are now backed by Uint8List/Uint16List/Uint32List (picked by bitsPerSample via the new allocateSampleBuffer) instead of a generic List<int>, which costs a full 8-byte machine word per element in Dart regardless of how few bits the value actually needs. For the common case — 8-bit samples, e.g. any RGB whole-slide-image scanner file — this is a 4x memory reduction per decoded chunk (7 bytes/pixel instead of 28 for 3-channel RGB, once the final RGBA8 conversion buffer is counted too). TiffChunkPlan's own per-pixel cost estimate is updated to match, so a memory budget computed from it (directly, or via the TiffAutoDecodeBudget below) now reflects what a decode actually allocates — previously the inflated estimate could force chunks well below a page's native tile/strip height purely because of how the intermediate buffer happened to be represented, not how much data it actually held, which is exactly what forces redundant redecoding (see 0.2.0's note on TiffChunkPlan.forBudget) and collapses TiffChunkPlan.recommendedWorkerCount to a single worker. samples's type stays List<int> (the public field/sampleAt API is unchanged) — only what backs it changed.
  • TiffAutoDecodeBudget.recommend (package:tiff/tiff_io.dart) picks a (maxBytesPerChunk, workerCount) pair for TiffParallelDecoder.decodeBanded sized to both a page's metadata and the machine actually running it, so a caller no longer has to hardcode a number that's either too small for a big, idle multi-core machine or too large for a small/mobile one — TiffChunkPlan/TiffChunkPlan.recommendedWorkerCount need a caller to already know both by design (see 0.2.0's note on why neither reads OS/CPU state itself). Backed by the new SystemMemoryInfo.probe() (a best-effort system memory reading via sysctl/vm_stat on macOS, /proc/meminfo on Linux, or wmic on Windows — null on mobile, or any probe failure, since none of those expose system-wide memory to an app) and Platform.numberOfProcessors. That memory reading is a one-time snapshot, not a promise it stays free for the whole decode — a real machine keeps allocating elsewhere the entire time a large decode runs — so recommend deliberately spends less than the snapshot reports available: a reserve (reserveFraction/reserveBytes, whichever is larger) is set aside for the rest of the machine up front, and what's left is further divided by doubleBufferSafetyFactor to cover a worker's previous chunk not necessarily being garbage-collected before its next chunk is decoded. Skipping either margin let a generous reading, taken on an already-loaded machine, size a decode large enough to push the whole system into memory pressure. The resulting aggregate budget is divided across up to cpuCount chunks before TiffChunkPlan.forBudget sizes them, rather than handed to forBudget whole — otherwise, on a budget too small to fit more than one full tile/strip-aligned chunk at a time (a wide whole-slide-image page easily), recommendedWorkerCount always came back 1 regardless of how many cores were free, since chunk size (not worker count) was what used up the budget. Dividing first only shrinks chunks below the native tile/strip height (trading some redundant redecoding of the same tile/strip for spreading that work across otherwise-idle cores concurrently, rather than paying for it serially on one core) when the budget genuinely can't fit cpuCount full-sized chunks; it has no effect once it can, since forBudget already caps chunk height at the native tile/strip height on its own.

0.2.0 #

  • TiffChunkPlan.forBudget computes how to decode a page in horizontal chunks that are both bounded by a caller-supplied byte budget and aligned to a whole number of tile/strip rows — decoding one row at a time (the natural choice for a tight memory budget) means TiffImage.decodeRegionRgba8 redecodes the same underlying tile/strip once per row that overlaps it (500x-plus for a real whole-slide-image file's 512px tiles), since it has no cross-call cache; forBudget avoids that by only shrinking the chunk below one tile/strip if the budget truly forces it. TiffChunkPlan.recommendedWorkerCount derives how many such chunks can run concurrently within an aggregate memory budget and a CPU-count cap. Both are pure — no I/O, no process/OS memory reading — the budget itself is always an explicit parameter the caller computes however it likes.
  • TiffParallelDecoder.decodeBanded (package:tiff/tiff_io.dart) decodes a page in horizontal bands across a pool of isolates, using TiffChunkPlan internally — each worker opens its own file handle (a decoded page can't cross an isolate boundary) and delivers bands back to the caller's own isolate as they finish, so the caller can write them out, feed a pyramid builder, or anything else per band without that work needing to be isolate-safe itself. Worker count, per-chunk budget, and band height are all caller-supplied.
  • TiffInitialView.forViewport computes a centered region and display zoom sized for a viewer's viewport and a per-device decode budget, so a viewer's first frame decodes a screen-sized crop instead of the whole page.
  • TiffDisplayOptimizer.optimize rewrites a page as tiled (and optionally pyramided) RGB, as a deliberate one-off "prepare this file" step run before a viewer opens it — not during interactive display — so a source TIFF that's strip-organized, single-resolution, or both no longer forces a viewer to decode more than it needs to just to pan or zoom out. Its onProgress callback reports a TiffOptimizeProgress (concrete completedSteps/totalSteps counts, plus the same as a fraction) once per pyramid rung and a final time after encoding, so a caller can show real "step 2 of 5" progress instead of an indefinite spinner for a large page. ImageResampler.downsampleRgba8 (box filtering) is the pyramid-level resampler behind it, and is usable standalone too.
  • Fixed: JPEG-in-TIFF (Compression 6/7) decoding now handles a page whose JPEG scan is split across strip/tile boundaries instead of each strip/tile being a self-contained JPEG the way TIFF Technical Note 2 describes and this package otherwise assumes — some encoders write pages this way, and even the first chunk (SOF and all) is just as much a fragment as the others, since its own entropy data has nowhere near an EOI. Previously this surfaced as package:image's ImageException: Only single frame JPEGs supported when decoding the second and later chunks. Each chunk is now checked upfront for its own SOF and trailing EOI — a chunk missing either is treated as part of one continuous scan and the whole page's chunks are reassembled into a single JPEG stream and decoded together instead; a chunk that has both but still fails to decode (corrupt data, a dimension mismatch, ...) surfaces that error directly rather than being incorrectly merged with unrelated, perfectly valid chunks (which used to be possible before this check was added, and would surface as a confusing ImageException: Duplicate JPG frame data found. on a page where every chunk actually is independently self-contained, e.g. a real whole-slide- image scanner file).
  • Fixed: a strip/tile with a byte count of 0 (a "sparse" chunk — some whole-slide-image scanners never store background tiles at all, and rely on the reader leaving that area at its default fill value) is now skipped outright instead of being treated as a JPEG chunk that failed the self-contained check above. Previously a single sparse chunk anywhere in a JPEG-compressed page made every chunk get funneled into the whole-page stitched-JPEG fallback — including chunks that were perfectly valid, independent JPEGs on their own — which then failed with ImageException: Duplicate JPG frame data found. once more than one real frame ended up concatenated together. This is the shape a real Philips/Aperio-style whole-slide-image TIFF commonly takes.

0.1.0 #

Initial release.

  • Read and write Classic TIFF (32-bit offsets) and BigTIFF (64-bit offsets), with automatic promotion to BigTIFF when a written image's pixel data would exceed the 4 GiB Classic offset limit.
  • Full IFD/tag parsing, covering every baseline TIFF 6.0 tag type plus BigTIFF's LONG8/SLONG8/IFD8.
  • Strip- and tile-organized pixel data, both reading and writing (edge tiles are cropped on read and zero-padded on write automatically).
  • Compression: None, PackBits, LZW, and Deflate/ZIP, read and write; CCITT Group 3/4 fax and JPEG, read only (see the README for details on both). LZW decoding also transparently handles "old-style" (pre-TIFF6, LSB-first) data from legacy encoders, auto-detected the same way libtiff does. JPEG-in-TIFF decoding correctly treats the JPEG codec's own YCbCr->RGB conversion as final, rather than re-applying the PhotometricInterpretation tag's YCbCr transform on top of already-RGB samples. CCITT Group 4/2D decoding correctly preserves zero-length runs (some encoders pair a zero-length white run with a zero-length black run), which previously desynced the reference-line tracking used by later rows.
  • Horizontal differencing predictor, read and write.
  • RGBA8 color conversion covering WhiteIsZero/BlackIsZero, RGB(+alpha), Palette/ColorMap, CMYK, and non-subsampled YCbCr.
  • Brightness/contrast/gamma adjustment for decoded RGBA8 pixel data (ImageAdjustments).
  • File-backed decoding (package:tiff/tiff_io.dart) that streams strips/tiles from disk instead of loading a whole file into memory, plus region-of-interest decoding (decodeRegion/decodeRegionRgba8) that skips chunks outside a requested crop — the way to preview or tile through a multi-gigabyte page without materializing the whole image in memory.
  • Multi-page reading and writing via IFD chains.
  • GeoTIFF, EXIF, and GPS metadata parsing.
  • Optional package:image bridge (package:tiff/tiff_image_adapter.dart) for converting to/from image.Image and for decoding JPEG-compressed pages.
0
likes
160
points
493
downloads

Documentation

API reference

Publisher

verified publishermonlycute.id.vn

Weekly Downloads

Dart library for reading and writing TIFF and BigTIFF: tags, IFDs, strips/tiles, compression, GeoTIFF/EXIF metadata, plus optional image and Flutter minimap bridges.

Repository (GitHub)
View/report issues

Topics

#tiff #bigtiff #image #codec

Funding

Consider supporting this project:

ko-fi.com

License

Apache-2.0 (license)

Dependencies

archive, flutter, image

More

Packages that depend on tiff