Skip to content

Commit

Permalink
perf(BrushTool, drawBrush): Delete unused brush pixelData. Fix render…
Browse files Browse the repository at this point in the history
…ing flicker.

Now when you erase pixels from a pixelData mask, the pixelData memory is freed up if you erase the
last pixel of that mask on the slice. This is also makes it easier for tools like dcmjs to just
hoover up all the slices that have non-blank masks on them. Secondly I fixed a small flicker edge
case with the brush imageBitmapCache.
  • Loading branch information
James Petts committed Mar 15, 2019
1 parent 7c38bec commit 8019326
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 20 deletions.
38 changes: 27 additions & 11 deletions src/tools/brush/BrushTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import brushUtils from './../../util/brush/index.js';
import EVENTS from '../../events.js';

const { drawBrushPixels, getCircle } = brushUtils;
const { state } = store.modules.brush;

const brushModule = store.modules.brush;

/**
* @public
Expand Down Expand Up @@ -70,10 +71,10 @@ export default class BrushTool extends BaseBrushTool {
}

// Draw the hover overlay on top of the pixel data
const radius = state.radius;
const radius = brushModule.state.radius;
const context = eventData.canvasContext;
const element = eventData.element;
const drawId = state.drawColorId;
const drawId = brushModule.state.drawColorId;
const color = this._getBrushColor(drawId);

context.setTransform(1, 0, 0, 1, 0, 0);
Expand Down Expand Up @@ -141,7 +142,7 @@ function _overlappingStrategy(evt) {
return;
}

const radius = state.radius;
const radius = brushModule.state.radius;
const pointerArray = getCircle(radius, rows, columns, x, y);

_drawMainColor(eventData, toolData, pointerArray);
Expand All @@ -167,13 +168,13 @@ function _nonOverlappingStrategy(evt) {
}

const toolData = toolState.data;
const segmentationIndex = state.drawColorId;
const segmentationIndex = brushModule.state.drawColorId;

if (x < 0 || x > columns || y < 0 || y > rows) {
return;
}

const radius = state.radius;
const radius = brushModule.state.radius;
const pointerArray = getCircle(radius, rows, columns, x, y);

const numberOfColors = BaseBrushTool.getNumberOfColors();
Expand All @@ -185,7 +186,7 @@ function _nonOverlappingStrategy(evt) {
}

if (toolData[i] && toolData[i].pixelData) {
drawBrushPixels(pointerArray, toolData[i].pixelData, columns, true);
drawBrushPixels(pointerArray, toolData[i], columns, true);
toolData[i].invalidated = true;
}
}
Expand All @@ -196,7 +197,7 @@ function _nonOverlappingStrategy(evt) {
function _drawMainColor(eventData, toolData, pointerArray) {
const shouldErase = _isCtrlDown(eventData);
const columns = eventData.image.columns;
const segmentationIndex = state.drawColorId;
const segmentationIndex = brushModule.state.drawColorId;

if (shouldErase && !toolData[segmentationIndex]) {
// Erase command, yet no data yet, just return.
Expand All @@ -208,17 +209,32 @@ function _drawMainColor(eventData, toolData, pointerArray) {
}

if (!toolData[segmentationIndex].pixelData) {
const enabledElement = external.cornerstone.getEnabledElement(
eventData.element
);
const enabledElementUID = enabledElement.uuid;

// Clear cache for this color to avoid flickering.
const imageBitmapCacheForElement = brushModule.getters.imageBitmapCacheForElement(
enabledElementUID
);

if (imageBitmapCacheForElement) {
imageBitmapCacheForElement[segmentationIndex] = null;
}

// Add a new pixelData array.
toolData[segmentationIndex].pixelData = new Uint8ClampedArray(
eventData.image.width * eventData.image.height
);
}

const pixelData = toolData[segmentationIndex].pixelData;
const toolDataI = toolData[segmentationIndex];

// Draw / Erase the active color.
drawBrushPixels(pointerArray, pixelData, columns, shouldErase);
drawBrushPixels(pointerArray, toolDataI, columns, shouldErase);

toolData[segmentationIndex].invalidated = true;
toolDataI.invalidated = true;
}

function _isCtrlDown(eventData) {
Expand Down
20 changes: 11 additions & 9 deletions src/util/brush/drawBrush.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,26 @@ import { draw, fillBox } from '../../drawing/index.js';
* @name drawBrushPixels
*
* @param {Object[]} pointerArray The array of points to draw.
* @param {number[]} storedPixels The brush mask to modify.
* @param {number} columns The number of columns in the mask.
* @param {number[]} toolData The cornerstoneTools annotation to modify.
* @param {number} columns The number of columns in the mask.
* @param {boolean} [shouldErase = false] If true the modified mask pixels will be set to 0, rather than 1.
* @returns {void}
*/
function drawBrushPixels(
pointerArray,
storedPixels,
columns,
shouldErase = false
) {
function drawBrushPixels(pointerArray, toolData, columns, shouldErase = false) {
const getPixelIndex = (x, y) => y * columns + x;

const pixelData = toolData.pixelData;

pointerArray.forEach(point => {
const spIndex = getPixelIndex(point[0], point[1]);

storedPixels[spIndex] = shouldErase ? 0 : 1;
pixelData[spIndex] = shouldErase ? 0 : 1;
});

// If Erased the last pixel, delete the pixelData array.
if (shouldErase && !pixelData.some(element => element !== 0)) {
delete toolData.pixelData;
}
}

/**
Expand Down

0 comments on commit 8019326

Please sign in to comment.