readRegion function

Future<Image?> readRegion(
  1. SvsFile svs,
  2. int layerIndex,
  3. int x,
  4. int y,
  5. int width,
  6. int height, {
  7. bool applyColorScheme = true,
  8. bool applyDisplayColor = true,
  9. int? displayColor,
  10. CancellationToken? cancelToken,
})

Reads an arbitrary rectangular region from a resolution layer in the slide.

Supports OpenSlide-style region reading (readRegion(x, y, w, h)) across both Aperio SVS and Hamamatsu formats (NDPI, VMS, VMU).

svs is the open SvsFile. layerIndex is the resolution layer index (0 is baseline / highest resolution). x and y are the pixel coordinates of the top-left corner in the layer. width and height are the dimensions of the region to extract in pixels. applyColorScheme if true, applies appropriate color space conversion. applyDisplayColor if true, applies display color tinting if present. displayColor optional explicit display color override. cancelToken optional cancellation token.

Returns the extracted img.Image, or null if the region or layer is invalid or cancelled.

Implementation

Future<img.Image?> readRegion(
  SvsFile svs,
  int layerIndex,
  int x,
  int y,
  int width,
  int height, {
  bool applyColorScheme = true,
  bool applyDisplayColor = true,
  int? displayColor,
  CancellationToken? cancelToken,
}) async {
  if (width <= 0 || height <= 0 || cancelToken?.isCancelled == true) {
    return null;
  }

  if (svs is HamamatsuFile) {
    svs.ensureOpen();
    if (layerIndex < 0 || layerIndex >= svs.levels.length) return null;
    try {
      final image = await svs.levels[layerIndex].readRegion(
        x,
        y,
        width,
        height,
        applyColorScheme: applyColorScheme,
        cancelToken: cancelToken,
      );
      if (cancelToken?.isCancelled == true) return null;
      if (image != null && applyDisplayColor && displayColor != null && displayColor != 0) {
        return _applyDisplayColorToImage(image, displayColor);
      }
      return image;
    } catch (_) {
      return null;
    }
  }

  final collected = await _getOrCollectTiledLevels(svs);
  if (cancelToken?.isCancelled == true) return null;
  final levels = collected.levels;
  if (layerIndex < 0 || layerIndex >= levels.length) return null;

  final targetLevel = levels[layerIndex];
  final fullData = await _getOrLoadLevelFullData(
    svs,
    targetLevel,
    collected.globalJpegTables,
    collected.globalDisplayColor,
  );
  if (fullData == null || cancelToken?.isCancelled == true) return null;

  final effectiveDisplayColor = displayColor ?? fullData.displayColor;

  final output = img.Image(width: width, height: height);
  output.clear(img.ColorRgb8(255, 255, 255));

  final clipX0 = math.max(0, x);
  final clipY0 = math.max(0, y);
  final clipX1 = math.min(targetLevel.width, x + width);
  final clipY1 = math.min(targetLevel.height, y + height);

  if (clipX0 >= clipX1 || clipY0 >= clipY1) {
    if (applyDisplayColor && effectiveDisplayColor != null && effectiveDisplayColor != 0) {
      return _applyDisplayColorToImage(output, effectiveDisplayColor);
    }
    return output;
  }

  final startTileX = clipX0 ~/ targetLevel.tileWidth;
  final endTileX = (clipX1 - 1) ~/ targetLevel.tileWidth;
  final startTileY = clipY0 ~/ targetLevel.tileHeight;
  final endTileY = (clipY1 - 1) ~/ targetLevel.tileHeight;

  for (var ty = startTileY; ty <= endTileY; ty++) {
    for (var tx = startTileX; tx <= endTileX; tx++) {
      if (cancelToken?.isCancelled == true) return null;
      final tileImg = await extractSvsTileAsImage(
        svs,
        layerIndex,
        tx,
        ty,
        applyColorScheme: applyColorScheme,
        applyDisplayColor: false,
        cancelToken: cancelToken,
      );
      if (tileImg == null) continue;
      if (cancelToken?.isCancelled == true) return null;

      final tileLeft = tx * targetLevel.tileWidth;
      final tileTop = ty * targetLevel.tileHeight;

      final srcX = math.max(0, x - tileLeft);
      final srcY = math.max(0, y - tileTop);
      final dstX = math.max(0, tileLeft - x);
      final dstY = math.max(0, tileTop - y);
      final copyW = math.min(tileImg.width - srcX, width - dstX);
      final copyH = math.min(tileImg.height - srcY, height - dstY);

      if (copyW > 0 && copyH > 0) {
        img.compositeImage(
          output,
          tileImg,
          srcX: srcX,
          srcY: srcY,
          srcW: copyW,
          srcH: copyH,
          dstX: dstX,
          dstY: dstY,
        );
      }
    }
  }

  if (applyDisplayColor && effectiveDisplayColor != null && effectiveDisplayColor != 0) {
    return _applyDisplayColorToImage(output, effectiveDisplayColor);
  }
  return output;
}