Skip to content

Commit

Permalink
Added changeImageIdCacheSize() function to imageCache to support chan…
Browse files Browse the repository at this point in the history
…ging the size of an imageId in the cache

Added support for an optional decache function on the image object which is called when that image is removed from the cache.
Added support for an optional sharedCacheKey string on the image object which is used by the imageCache to properly handle multi-frame images (or other possible cases where multiple imageId's are managed in one memory block)
Fixed bug where ww/wc was not applied to color images if the current ww/wc matched the default in the image object
Fixed bug where color images were assumed to have a getCanvas() function on the image object
  • Loading branch information
chafey committed Feb 3, 2016
1 parent 31556ce commit 17b4b2b
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 76 deletions.
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cornerstone",
"version": "0.8.4",
"version": "0.9.0",
"description": "HTML5 Medical Image Viewer Component",
"main" : "dist/cornerstone.js",
"ignore": [
Expand Down
2 changes: 1 addition & 1 deletion dist/cornerstone.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*! cornerstone - v0.8.4 - 2015-09-12 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstone */
/*! cornerstone - v0.9.0 - 2016-02-03 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstone */
.cornerstone-enabled-image {

/* prevent text selection from occurring when dragging the mouse on the div */
Expand Down
125 changes: 91 additions & 34 deletions dist/cornerstone.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*! cornerstone - v0.8.4 - 2015-09-12 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstone */
/*! cornerstone - v0.9.0 - 2016-02-03 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstone */
if(typeof cornerstone === 'undefined'){
cornerstone = {
internal : {},
Expand Down Expand Up @@ -468,8 +468,11 @@ if(typeof cornerstone === 'undefined'){

"use strict";

// dictionary of imageId to cachedImage objects
var imageCache = {};

// dictionary of sharedCacheKeys to number of imageId's in cache with this shared cache key
var sharedCacheKeys = {};
// array of cachedImage objects
var cachedImages = [];

var maximumSizeInBytes = 1024 * 1024 * 1024; // 1 GB
Expand Down Expand Up @@ -535,6 +538,7 @@ if(typeof cornerstone === 'undefined'){
var cachedImage = {
loaded : false,
imageId : imageId,
sharedCacheKey: undefined, // the sharedCacheKey for this imageId. undefined by default
imagePromise : imagePromise,
timeStamp : new Date(),
sizeInBytes: 0
Expand All @@ -552,8 +556,23 @@ if(typeof cornerstone === 'undefined'){
if (image.sizeInBytes.toFixed === undefined) {
throw "putImagePromise: image.sizeInBytes is not a number";
}
cachedImage.sizeInBytes = image.sizeInBytes;
cacheSizeInBytes += cachedImage.sizeInBytes;

// If this image has a shared cache key, reference count it and only
// count the image size for the first one added with this sharedCacheKey
if(image.sharedCacheKey) {
cachedImage.sizeInBytes = image.sizeInBytes;
cachedImage.sharedCacheKey = image.sharedCacheKey;
if(sharedCacheKeys[image.sharedCacheKey]) {
sharedCacheKeys[image.sharedCacheKey]++;
} else {
sharedCacheKeys[image.sharedCacheKey] = 1;
cacheSizeInBytes += cachedImage.sizeInBytes;
}
}
else {
cachedImage.sizeInBytes = image.sizeInBytes;
cacheSizeInBytes += cachedImage.sizeInBytes;
}
purgeCacheIfNecessary();
});
}
Expand Down Expand Up @@ -581,9 +600,23 @@ if(typeof cornerstone === 'undefined'){
throw "removeImagePromise: imageId must not be undefined";
}
cachedImages.splice( cachedImages.indexOf(cachedImage), 1);
cacheSizeInBytes -= cachedImage.sizeInBytes;

// If this is using a sharedCacheKey, decrement the cache size only
// if it is the last imageId in the cache with this sharedCacheKey
if(cachedImages.sharedCacheKey) {
if(sharedCacheKeys[cachedImages.sharedCacheKey] === 1) {
cacheSizeInBytes -= cachedImage.sizeInBytes;
delete sharedCacheKeys[cachedImages.sharedCacheKey];
} else {
sharedCacheKeys[cachedImages.sharedCacheKey]--;
}
} else {
cacheSizeInBytes -= cachedImage.sizeInBytes;
}
delete imageCache[imageId];

decache(cachedImage.imagePromise, cachedImage.imageId);

return cachedImage.imagePromise;
}

Expand All @@ -595,15 +628,37 @@ if(typeof cornerstone === 'undefined'){
};
}

function decache(imagePromise, imageId) {
imagePromise.then(function(image) {
if(image.decache) {
image.decache();
}
imagePromise.reject();
delete imageCache[imageId];
}).always(function() {
delete imageCache[imageId];
});
}

function purgeCache() {
while (cachedImages.length > 0) {
var removedCachedImage = cachedImages.pop();
delete imageCache[removedCachedImage.imageId];
removedCachedImage.imagePromise.reject();
var removedCachedImage = cachedImages.pop();
decache(removedCachedImage.imagePromise, removedCachedImage.imageId);
}
cacheSizeInBytes = 0;
}

function changeImageIdCacheSize(imageId, newCacheSize) {
var cacheEntry = imageCache[imageId];
if(cacheEntry) {
cacheEntry.imagePromise.then(function(image) {
var cacheSizeDifference = newCacheSize - image.sizeInBytes;
image.sizeInBytes = newCacheSize;
cacheSizeInBytes += cacheSizeDifference;
});
}
}

// module exports
cornerstone.imageCache = {
putImagePromise : putImagePromise,
Expand All @@ -612,7 +667,8 @@ if(typeof cornerstone === 'undefined'){
setMaximumSizeBytes: setMaximumSizeBytes,
getCacheInfo : getCacheInfo,
purgeCache: purgeCache,
cachedImages: cachedImages
cachedImages: cachedImages,
changeImageIdCacheSize: changeImageIdCacheSize
};

}(cornerstone));
Expand Down Expand Up @@ -1504,38 +1560,39 @@ if(typeof cornerstone === 'undefined'){

function getRenderCanvas(enabledElement, image, invalidated)
{
// apply the lut to the stored pixel data onto the render canvas

if(enabledElement.viewport.voi.windowWidth === enabledElement.image.windowWidth &&
enabledElement.viewport.voi.windowCenter === enabledElement.image.windowCenter &&
enabledElement.viewport.invert === false)
// The ww/wc is identity and not inverted - get a canvas with the image rendered into it for
// fast drawing
if(enabledElement.viewport.voi.windowWidth === 255 &&
enabledElement.viewport.voi.windowCenter === 128 &&
enabledElement.viewport.invert === false &&
image.getCanvas &&
image.getCanvas()
)
{
// the color image voi/invert has not been modified, request the canvas that contains
// it so we can draw it directly to the display canvas
return image.getCanvas();
}
else
{
if(doesImageNeedToBeRendered(enabledElement, image) === false && invalidated !== true) {
return colorRenderCanvas;
}

// 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.
if(colorRenderCanvas.width !== image.width || colorRenderCanvas.height != image.height) {
initializeColorRenderCanvas(image);
}

// get the lut to use
var colorLut = getLut(image, enabledElement.viewport);

// the color image voi/invert has been modified - apply the lut to the underlying
// pixel data and put it into the renderCanvas
cornerstone.storedColorPixelDataToCanvasImageData(image, colorLut, colorRenderCanvasData.data);
colorRenderCanvasContext.putImageData(colorRenderCanvasData, 0, 0);
// apply the lut to the stored pixel data onto the render canvas
if(doesImageNeedToBeRendered(enabledElement, image) === false && invalidated !== true) {
return colorRenderCanvas;
}

// 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.
if(colorRenderCanvas.width !== image.width || colorRenderCanvas.height != image.height) {
initializeColorRenderCanvas(image);
}

// get the lut to use
var colorLut = getLut(image, enabledElement.viewport);

// the color image voi/invert has been modified - apply the lut to the underlying
// pixel data and put it into the renderCanvas
cornerstone.storedColorPixelDataToCanvasImageData(image, colorLut, colorRenderCanvasData.data);
colorRenderCanvasContext.putImageData(colorRenderCanvasData, 0, 0);
return colorRenderCanvas;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion dist/cornerstone.min.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions dist/cornerstone.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions example/colorImage/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ <h1>
maxPixelValue: 255,
slope: 1.0,
intercept: 0,
windowCenter: 127,
windowWidth: 256,
windowCenter: 128,
windowWidth: 255,
render: cornerstone.renderColorImage,
getPixelData: getPixelData,
getImageData: getImageData,
Expand Down
6 changes: 6 additions & 0 deletions example/imageCache/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ <h1>

<button id="purge" class="btn">Purge Cache</button>
<button id="addImage" class="btn">Add Image</button>
<button id="changeImageSize" class="btn">Change Image Size</button>

<div style="width:512px;height:512px;position:relative;color: white;"
class="cornerstone-enabled-image"
Expand Down Expand Up @@ -85,6 +86,11 @@ <h1>
cornerstone.loadAndCacheImage(imageId);
});

$('#changeImageSize').click(function() {
var imageId = "colorImageLoader://1";
cornerstone.imageCache.changeImageIdCacheSize(imageId, 512 * 1024);
});

setInterval(function() {
var cacheInfo = cornerstone.imageCache.getCacheInfo();
//$('#maxCacheSize').val("" + cacheInfo.maximumSizeInBytes);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cornerstone",
"version": "0.8.4",
"version": "0.9.0",
"description": "HTML5 Medical Image Viewer Component",
"keywords": [
"DICOM",
Expand Down
72 changes: 64 additions & 8 deletions src/imageCache.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@

"use strict";

// dictionary of imageId to cachedImage objects
var imageCache = {};

// dictionary of sharedCacheKeys to number of imageId's in cache with this shared cache key
var sharedCacheKeys = {};
// array of cachedImage objects
var cachedImages = [];

var maximumSizeInBytes = 1024 * 1024 * 1024; // 1 GB
Expand Down Expand Up @@ -73,6 +76,7 @@
var cachedImage = {
loaded : false,
imageId : imageId,
sharedCacheKey: undefined, // the sharedCacheKey for this imageId. undefined by default
imagePromise : imagePromise,
timeStamp : new Date(),
sizeInBytes: 0
Expand All @@ -90,8 +94,23 @@
if (image.sizeInBytes.toFixed === undefined) {
throw "putImagePromise: image.sizeInBytes is not a number";
}
cachedImage.sizeInBytes = image.sizeInBytes;
cacheSizeInBytes += cachedImage.sizeInBytes;

// If this image has a shared cache key, reference count it and only
// count the image size for the first one added with this sharedCacheKey
if(image.sharedCacheKey) {
cachedImage.sizeInBytes = image.sizeInBytes;
cachedImage.sharedCacheKey = image.sharedCacheKey;
if(sharedCacheKeys[image.sharedCacheKey]) {
sharedCacheKeys[image.sharedCacheKey]++;
} else {
sharedCacheKeys[image.sharedCacheKey] = 1;
cacheSizeInBytes += cachedImage.sizeInBytes;
}
}
else {
cachedImage.sizeInBytes = image.sizeInBytes;
cacheSizeInBytes += cachedImage.sizeInBytes;
}
purgeCacheIfNecessary();
});
}
Expand Down Expand Up @@ -119,9 +138,23 @@
throw "removeImagePromise: imageId must not be undefined";
}
cachedImages.splice( cachedImages.indexOf(cachedImage), 1);
cacheSizeInBytes -= cachedImage.sizeInBytes;

// If this is using a sharedCacheKey, decrement the cache size only
// if it is the last imageId in the cache with this sharedCacheKey
if(cachedImages.sharedCacheKey) {
if(sharedCacheKeys[cachedImages.sharedCacheKey] === 1) {
cacheSizeInBytes -= cachedImage.sizeInBytes;
delete sharedCacheKeys[cachedImages.sharedCacheKey];
} else {
sharedCacheKeys[cachedImages.sharedCacheKey]--;
}
} else {
cacheSizeInBytes -= cachedImage.sizeInBytes;
}
delete imageCache[imageId];

decache(cachedImage.imagePromise, cachedImage.imageId);

return cachedImage.imagePromise;
}

Expand All @@ -133,15 +166,37 @@
};
}

function decache(imagePromise, imageId) {
imagePromise.then(function(image) {
if(image.decache) {
image.decache();
}
imagePromise.reject();
delete imageCache[imageId];
}).always(function() {
delete imageCache[imageId];
});
}

function purgeCache() {
while (cachedImages.length > 0) {
var removedCachedImage = cachedImages.pop();
delete imageCache[removedCachedImage.imageId];
removedCachedImage.imagePromise.reject();
var removedCachedImage = cachedImages.pop();
decache(removedCachedImage.imagePromise, removedCachedImage.imageId);
}
cacheSizeInBytes = 0;
}

function changeImageIdCacheSize(imageId, newCacheSize) {
var cacheEntry = imageCache[imageId];
if(cacheEntry) {
cacheEntry.imagePromise.then(function(image) {
var cacheSizeDifference = newCacheSize - image.sizeInBytes;
image.sizeInBytes = newCacheSize;
cacheSizeInBytes += cacheSizeDifference;
});
}
}

// module exports
cornerstone.imageCache = {
putImagePromise : putImagePromise,
Expand All @@ -150,7 +205,8 @@
setMaximumSizeBytes: setMaximumSizeBytes,
getCacheInfo : getCacheInfo,
purgeCache: purgeCache,
cachedImages: cachedImages
cachedImages: cachedImages,
changeImageIdCacheSize: changeImageIdCacheSize
};

}(cornerstone));
Loading

0 comments on commit 17b4b2b

Please sign in to comment.