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                                                      33x   33x   33x 33x   33x     33x   33x                   33x             33x   33x 33x   33x 33x   33x           33x                                                 33x   33x 33x           33x 33x 33x   33x                             33x           33x   33x         33x   33x     33x 33x               33x     33x   33x   33x 33x 33x 33x   33x   33x    
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);
}