@cornerstonejs/core
Version:
Cornerstone3D Core
172 lines (171 loc) • 5.98 kB
JavaScript
import CanvasProperties from './CanvasProperties.js';
import CanvasMapper from './CanvasMapper.js';
export default class CanvasActor {
constructor(viewport, derivedImage) {
this.canvasProperties = new CanvasProperties(this);
this.visibility = false;
this.mapper = new CanvasMapper(this);
this.rasterDirty = true;
this.hasCachedRaster = false;
this.className = 'CanvasActor';
this.derivedImage = derivedImage;
this.viewport = viewport;
}
renderRLE(viewport, context, voxelManager) {
const { width, height } = this.image;
let { canvas } = this;
if (!canvas || canvas.width !== width || canvas.height !== height) {
this.canvas = canvas = new window.OffscreenCanvas(width, height);
this.cachedImageData = undefined;
this.rasterDirty = true;
}
if (!this.rasterDirty) {
if (this.hasCachedRaster) {
context.drawImage(canvas, 0, 0);
}
return;
}
const localContext = canvas.getContext('2d');
let imageData = this.cachedImageData;
if (!imageData) {
imageData = localContext.createImageData(width, height);
this.cachedImageData = imageData;
}
const { data: imageArray } = imageData;
imageArray.fill(0);
const { map } = voxelManager;
let hasVisiblePixels = false;
for (let y = 0; y < height; y++) {
const row = map.getRun(y, 0);
if (!row) {
continue;
}
const baseOffset = (y * width) << 2;
for (const run of row) {
const { start, end, value: segmentIndex } = run;
if (segmentIndex === 0) {
continue;
}
hasVisiblePixels = true;
let startOffset = baseOffset + (start << 2);
for (let i = start; i < end; i++) {
this.canvasProperties.getColorBytes(segmentIndex, imageArray, startOffset);
startOffset += 4;
}
}
}
if (!hasVisiblePixels) {
localContext.clearRect(0, 0, width, height);
this.hasCachedRaster = false;
this.rasterDirty = false;
return;
}
localContext.putImageData(imageData, 0, 0);
this.hasCachedRaster = true;
this.rasterDirty = false;
context.drawImage(canvas, 0, 0);
}
setMapper(mapper) {
this.mapper = mapper;
}
render(viewport, context) {
if (!this.visibility) {
return;
}
const image = this.image || this.getImage();
const { width, height } = image;
const data = image.getScalarData();
if (!data) {
return;
}
const { voxelManager } = image;
if (voxelManager?.map?.getRun) {
this.renderRLE(viewport, context, voxelManager);
return;
}
let { canvas } = this;
if (!canvas || canvas.width !== width || canvas.height !== height) {
this.canvas = canvas = new window.OffscreenCanvas(width, height);
this.cachedImageData = undefined;
this.rasterDirty = true;
}
if (!this.rasterDirty) {
if (this.hasCachedRaster) {
context.drawImage(canvas, 0, 0);
}
return;
}
const localContext = canvas.getContext('2d');
let imageData = this.cachedImageData;
if (!imageData) {
imageData = localContext.createImageData(width, height);
this.cachedImageData = imageData;
}
const { data: imageArray } = imageData;
imageArray.fill(0);
let offset = 0;
let destOffset = 0;
let hasVisiblePixels = false;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const segmentIndex = data[offset++];
if (segmentIndex) {
hasVisiblePixels = true;
this.canvasProperties.getColorBytes(segmentIndex, imageArray, destOffset);
}
destOffset += 4;
}
}
if (!hasVisiblePixels) {
localContext.clearRect(0, 0, width, height);
this.hasCachedRaster = false;
this.rasterDirty = false;
return;
}
localContext.putImageData(imageData, 0, 0);
this.hasCachedRaster = true;
this.rasterDirty = false;
context.drawImage(canvas, 0, 0);
}
getClassName() {
return this.className;
}
getProperty() {
return this.canvasProperties;
}
setVisibility(visibility) {
this.visibility = visibility;
}
getMapper() {
return this.mapper;
}
isA(actorType) {
return actorType === this.className;
}
modified() {
this.rasterDirty = true;
}
getImage() {
if (this.image) {
return this.image;
}
this.image = { ...this.derivedImage };
const imageData = this.viewport.getImageData();
Object.assign(this.image, {
worldToIndex: (worldPos) => imageData.imageData.worldToIndex(worldPos),
indexToWorld: (index, destPoint) => imageData.imageData.indexToWorld(index, destPoint),
getDimensions: () => imageData.dimensions,
getScalarData: () => this.derivedImage?.getPixelData(),
getDirection: () => imageData.direction,
getSpacing: () => imageData.spacing,
setOrigin: () => null,
setDerivedImage: (image) => {
this.derivedImage = image;
this.image = null;
this.modified();
},
modified: () => null,
});
return this.image;
}
}