import type { INewMaskData } from "./core/types"; import type { MaskVolume } from "./core/index"; import type { CanvasState } from "./CanvasState"; /** * RenderingUtils — Rendering / slice-buffer helper methods. * * Extracted from CommToolsData. All methods operate on the shared * {@link CanvasState} reference — no independent state is held here * except the reusable ImageData slice buffer. * * The `setEmptyCanvasSize` callback is injected by the owner (DrawToolCore * or NrrdTools) because it lives in SliceRenderPipeline, not here. */ export declare class RenderingUtils { private state; /** Injected callback — set by owner after construction. */ setEmptyCanvasSize: (axis?: "x" | "y" | "z") => void; private _reusableSliceBuffer; private _reusableBufferWidth; private _reusableBufferHeight; /** * Per-layer contour cache. * * Caches the *expensive* part of slice rendering — `getSliceUint8` + * `findLabelsInSlice` + `extractLabelContours` (Path2D build) — keyed by * `${axis}:${sliceIndex}:${volume.version}`. The contours live in voxel * coordinates; zoom only changes the `ctx.scale` transform, so on zoom / * recomposite we reuse the cached Path2D and just re-fill, skipping * marching-squares entirely. A volume edit bumps its version → cache miss * → recompute (correct, automatic). Colors and channel visibility are * applied at fill time and intentionally *not* cached, so toggling them * needs no recompute. * * One entry per layer (the current slice). Switching slice/axis or * editing overwrites it. */ private _contourCache; constructor(state: CanvasState); /** * Get MaskVolume for a specific layer * * @param layer - Layer name: "layer1", "layer2", or "layer3" * @returns MaskVolume instance for the specified layer */ getVolumeForLayer(layer: string): MaskVolume; /** * Get MaskVolume for the currently active layer */ getCurrentVolume(): MaskVolume; /** * Get all MaskVolume instances */ getAllVolumes(): INewMaskData; /** * Get a painted mask image based on current axis and input slice index. * * Reads directly from MaskVolume. */ filterDrawedImage(axis: "x" | "y" | "z", sliceIndex: number): { index: number; image: ImageData; } | undefined; /** * Get or create a reusable ImageData buffer for the given axis. * * Reuses the same buffer across multiple slice renders to avoid * allocating a new ImageData per layer per slice switch. */ getOrCreateSliceBuffer(axis: "x" | "y" | "z"): ImageData | null; /** * Render a layer's slice onto the target canvas as vector contours. * * Uses marching-squares to extract voxel-truthful Path2D contours per * label, then `ctx.fill()` them at the display canvas resolution. This * eliminates the bilinear-upscale blur and zoom-dependent shape drift * that plagued the old putImageData → drawImage pipeline. * * The `buffer` parameter is kept for backward-compatible signature but * is no longer used on this path — callers may pass any valid ImageData. */ renderSliceToCanvas(layer: string, axis: "x" | "y" | "z", sliceIndex: number, _buffer: ImageData, targetCtx: CanvasRenderingContext2D, scaledWidth: number, scaledHeight: number): void; /** * Invalidate the reusable buffer (e.g. when switching datasets). */ invalidateSliceBuffer(): void; /** * Apply the same flip transform used by flipDisplayImageByAxis() to any * canvas context. */ applyMaskFlipForAxis(ctx: CanvasRenderingContext2D, width: number, height: number, axis: "x" | "y" | "z"): void; /** * Composite all layer canvases to the master display canvas. * Only draws layers whose visibility is enabled. */ compositeAllLayers(): void; }