All files / packages/core/src/RenderingEngine/helpers/cpuFallback/rendering renderGrayscaleImage.ts

67.85% Statements 38/56
50% Branches 15/30
50% Functions 2/4
73.07% Lines 38/52

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167                                                    35x     35x   35x 35x   35x     35x   35x                   35x             35x   35x 35x   35x 35x   35x           35x                                                 35x   35x 35x           35x 35x 35x   35x                             35x           35x   35x         35x   35x     35x 35x               35x     35x   35x   35x 35x 35x 35x   35x   35x    
import storedPixelDataToCanvasImageData from './storedPixelDataToCanvasImageData';
import storedPixelDataToCanvasImageDataPET from './storedPixelDataToCanvasImageDataPET';
import storedPixelDataToCanvasImageDataRGBA from './storedPixelDataToCanvasImageDataRGBA';
import setToPixelCoordinateSystem from './setToPixelCoordinateSystem';
import now from './now';
import getLut from './getLut';
import doesImageNeedToBeRendered from './doesImageNeedToBeRendered';
import initializeRenderCanvas from './initializeRenderCanvas';
import saveLastRendered from './saveLastRendered';
import { IImage, CPUFallbackEnabledElement } from '../../../../types';
 
/**
 * Returns an appropriate canvas to render the Image. If the canvas available in the cache is appropriate
 * it is returned, otherwise adjustments are made. It also sets the color transfer functions.
 *
 * @param {Object} enabledElement The cornerstone enabled element
 * @param {Object} image The image to be rendered
 * @param {Boolean} invalidated Is pixel data valid
 * @param {Boolean} [useAlphaChannel = true] Will an alpha channel be used
 * @returns {HTMLCanvasElement} An appropriate canvas for rendering the image
 * @memberof rendering
 */
function getRenderCanvas(
  enabledElement: CPUFallbackEnabledElement,
  image: IImage,
  invalidated: boolean,
  useAlphaChannel = true
): HTMLCanvasElement {
  const canvasWasColor =
    enabledElement.renderingTools.lastRenderedIsColor === true;
 
  Eif (!enabledElement.renderingTools.renderCanvas || canvasWasColor) {
    enabledElement.renderingTools.renderCanvas =
      document.createElement('canvas');
    initializeRenderCanvas(enabledElement, image);
  }
 
  const renderCanvas = enabledElement.renderingTools.renderCanvas;
 
  Iif (
    doesImageNeedToBeRendered(enabledElement, image) === false &&
    invalidated !== true
  ) {
    return renderCanvas;
  }
 
  // If our render canvas does not match the size of this image reset it
  // NOTE: This might be inefficient if we are updating multiple images of different
  // Sizes frequently.
  Iif (
    renderCanvas.width !== image.width ||
    renderCanvas.height !== image.height
  ) {
    initializeRenderCanvas(enabledElement, image);
  }
 
  image.stats = image.stats || {};
 
  const renderCanvasData = enabledElement.renderingTools.renderCanvasData;
  const renderCanvasContext = enabledElement.renderingTools.renderCanvasContext;
 
  let start = now();
  image.stats.lastLutGenerateTime = now() - start;
 
  const { viewport } = enabledElement;
 
  // If modality is 'PT' and the image is scaled then the results are floating points,
  // and we cannot create a lut for it (cannot have float indices). Therefore,
  // we use a mapping function to get the voiLUT from the values by applying
  // the windowLevel and windowWidth.
  Iif (viewport.modality === 'PT' && image.isPreScaled) {
    const { windowWidth, windowCenter } = viewport.voi;
    const minimum = windowCenter - windowWidth / 2;
    const maximum = windowCenter + windowWidth / 2;
    const range = maximum - minimum;
    const collectedMultiplierTerms = 255.0 / range;
 
    let petVOILutFunction;
 
    if (viewport.invert) {
      petVOILutFunction = (value) =>
        255 - (value - minimum) * collectedMultiplierTerms;
    } else {
      // Note, don't need to math.floor, that is dealt with by setting the value in the Uint8Array.
      petVOILutFunction = (value) =>
        (value - minimum) * collectedMultiplierTerms;
    }
 
    storedPixelDataToCanvasImageDataPET(
      image,
      petVOILutFunction,
      renderCanvasData.data
    );
  } else {
    // Get the lut to use
    const lut = getLut(image, viewport, invalidated);
 
    Eif (useAlphaChannel) {
      storedPixelDataToCanvasImageData(image, lut, renderCanvasData.data);
    } else {
      storedPixelDataToCanvasImageDataRGBA(image, lut, renderCanvasData.data);
    }
  }
 
  start = now();
  renderCanvasContext.putImageData(renderCanvasData, 0, 0);
  image.stats.lastPutImageDataTime = now() - start;
 
  return renderCanvas;
}
 
/**
 * API function to draw a grayscale image to a given enabledElement
 *
 * @param {EnabledElement} enabledElement The Cornerstone Enabled Element to redraw
 * @param {Boolean} invalidated - true if pixel data has been invalidated and cached rendering should not be used
 * @returns {void}
 * @memberof rendering
 */
export function renderGrayscaleImage(
  enabledElement: CPUFallbackEnabledElement,
  invalidated: boolean
): void {
  Iif (enabledElement === undefined) {
    throw new Error(
      'drawImage: enabledElement parameter must not be undefined'
    );
  }
 
  const image = enabledElement.image;
 
  Iif (image === undefined) {
    throw new Error('drawImage: image must be loaded before it can be drawn');
  }
 
  // Get the canvas context and reset the transform
  const context = enabledElement.canvas.getContext('2d');
 
  context.setTransform(1, 0, 0, 1, 0, 0);
 
  // Clear the canvas
  context.fillStyle = 'black';
  context.fillRect(
    0,
    0,
    enabledElement.canvas.width,
    enabledElement.canvas.height
  );
 
  // Turn off image smooth/interpolation if pixelReplication is set in the viewport
  context.imageSmoothingEnabled = !enabledElement.viewport.pixelReplication;
 
  // Save the canvas context state and apply the viewport properties
  setToPixelCoordinateSystem(enabledElement, context);
 
  const renderCanvas = getRenderCanvas(enabledElement, image, invalidated);
 
  const sx = enabledElement.viewport.displayedArea.tlhc.x - 1;
  const sy = enabledElement.viewport.displayedArea.tlhc.y - 1;
  const width = enabledElement.viewport.displayedArea.brhc.x - sx;
  const height = enabledElement.viewport.displayedArea.brhc.y - sy;
 
  context.drawImage(renderCanvas, sx, sy, width, height, 0, 0, width, height);
 
  enabledElement.renderingTools = saveLastRendered(enabledElement);
}