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

69.81% Statements 37/53
51.85% Branches 14/27
50% Functions 2/4
69.81% Lines 37/53

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                                                          24x   24x 24x   24x     24x   24x                   24x             24x   24x 24x   24x 24x   24x           24x                                                 24x   24x 24x           24x 24x 24x   24x                             24x           24x   24x         24x   24x     24x 24x               24x     24x   24x   24x 24x 24x 24x   24x   24x    
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);
 
    if (useAlphaChannel) {
      storedPixelDataToCanvasImageData(image, lut, renderCanvasData.data);
    } else E{
      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);
}