Skip to content

Commit 2a1defb

Browse files
committed
DM-10369: client side mask color change
- Processing image on the client side to set mask color - Add image processing framework that can be used in future of colobar changing - Processed images can be added to store as data png - include new functons in WebUtil: loadImage and requestIdleFrame - small clean up of PlotViewUtil
1 parent 04299da commit 2a1defb

14 files changed

+683
-367
lines changed

src/firefly/js/util/Color.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ const colours = {
3232

3333

3434
function toRGBA(/* String */ color, /* Number */ alpha) {
35-
var num = parseInt(color, 16);
35+
const num = parseInt(color[0]==='#' ? color.substr(1) : color, 16);
3636
return [num >> 16, num >> 8 & 255, num & 255, alpha];
3737
}
3838

3939
function toRGB(/* String */ color) {
40-
var num = parseInt(color, 16);
41-
return [num >> 16, num >> 8 & 255, num & 255];
40+
const num = parseInt(color[0]==='#' ? color.substr(1) : color, 16);
41+
return [num >> 16, num >> 8 & 255, num & 255];
4242
}
4343

4444
function toHex (/* Number */ red, /* Number */ green, /* Number */ blue) {

src/firefly/js/util/WebUtil.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,19 @@ export function loadScript(scriptName) {
4646
return loadPromise;
4747
}
4848

49-
50-
49+
/**
50+
* Create an image with a promise that resolves when the image is loaded
51+
* @param {string} src the source of the image typically a url or local image data
52+
* @return {Promise} a promised that will resolved with the loaded image object
53+
*/
54+
export function loadImage(src) {
55+
return new Promise( (resolve, reject) => {
56+
const im = new Image();
57+
im.src= src;
58+
im.onload= () => resolve(im);
59+
im.onerror= (ev) => reject(ev);
60+
});
61+
}
5162

5263
/**
5364
* Returns a string where all characters that are not valid for a complete URL have been escaped.
@@ -542,3 +553,22 @@ export function isBooleanString(val) {
542553
return s === 'true' || s === 'false';
543554
}
544555

556+
/**
557+
* A shim about requestIdleFrame
558+
* @param callback the callback to happens when idle
559+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
560+
* @function
561+
*/
562+
export const requestIdleCallback =
563+
window.requestIdleCallback ||
564+
function (callback) {
565+
return setTimeout(() => {
566+
const start = Date.now();
567+
callback({
568+
didTimeout: false,
569+
timeRemaining: () => Math.max(0, 50 - (Date.now() - start))
570+
});
571+
}, 1);
572+
};
573+
574+
export const cancelIdleCallback = window.cancelIdleCallback || ((id) => clearTimeout(id));

src/firefly/js/visualize/ImagePlotCntlr.js

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ const WCS_MATCH=`${PLOTS_PREFIX}.wcsMatch`;
141141
const PLOT_PROGRESS_UPDATE= `${PLOTS_PREFIX}.PlotProgressUpdate`;
142142
const API_TOOLS_VIEW= `${PLOTS_PREFIX}.apiToolsView`;
143143

144+
const ADD_PROCESSED_TILES= `${PLOTS_PREFIX}.addProcessedTiles`;
145+
144146
/** Action Type: enable/disable wcs matching*/
145147
export const IMAGE_PLOT_KEY= 'allPlots';
146148

@@ -205,6 +207,8 @@ const initState= function() {
205207
wcsMatchCenterWP: null,
206208
wcsMatchType: false,
207209
mpwWcsPrimId: null,
210+
211+
processedTiles: []
208212
};
209213

210214
};
@@ -213,6 +217,35 @@ const initState= function() {
213217
//============ EXPORTS ===========
214218

215219

220+
/**
221+
* @global
222+
* @public
223+
* @typedef {Object} ProcessedTiles
224+
*
225+
* @prop {string} plotId
226+
* @prop {string} plotImageId
227+
* @prop {string} imageOverlayId
228+
* @prop {number} zoomFactor
229+
* @prop {Array.<ClientTile>} clientTileAry
230+
*/
231+
232+
/**
233+
* @global
234+
* @public
235+
* @typedef {Object} ClientTile
236+
*
237+
* @prop {Object} tileAttributes
238+
* @prop {String} dataUrl
239+
* @prop {number} width - width of this tile
240+
* @prop {number} height - height of this tile
241+
* @prop {number} index - index of this tile
242+
* @prop {string} url - original file key to use in the service to retrieve this tile
243+
* @prop {number} xoff - pixel offset of this tile
244+
* @prop {number} yoff - pixel offset of this tile
245+
*/
246+
247+
248+
216249

217250
/*---------------------------- REDUCERS -----------------------------*/
218251

@@ -256,7 +289,7 @@ export default {
256289
RESTORE_DEFAULTS, CHANGE_PLOT_ATTRIBUTE,EXPANDED_AUTO_PLAY,
257290
DELETE_PLOT_VIEW, CHANGE_ACTIVE_PLOT_VIEW, CHANGE_PRIME_PLOT,
258291
PLOT_MASK, PLOT_MASK_START, PLOT_MASK_FAIL, PLOT_MASK_LAZY_LOAD, DELETE_OVERLAY_PLOT,
259-
OVERLAY_PLOT_CHANGE_ATTRIBUTES, WCS_MATCH
292+
OVERLAY_PLOT_CHANGE_ATTRIBUTES, WCS_MATCH, ADD_PROCESSED_TILES
260293
};
261294

262295

@@ -789,6 +822,16 @@ export function dispatchExpandedAutoPlay(autoPlayOn) {
789822
}
790823

791824

825+
/**
826+
*
827+
* @param plotId
828+
* @param plotImageId
829+
* @param clientTileAry
830+
*/
831+
export function dispatchAddProcessedTiles(plotId, imageOverlayId, plotImageId, zoomFactor, clientTileAry) {
832+
flux.process({ type: ADD_PROCESSED_TILES, payload: {plotId, imageOverlayId, plotImageId, zoomFactor, clientTileAry} });
833+
}
834+
792835
//======================================== Action Creators =============================
793836
//======================================== Action Creators =============================
794837
//======================================== Action Creators =============================
@@ -929,6 +972,7 @@ function changePointSelectionActionCreator(rawAction) {
929972

930973

931974

975+
932976
//======================================== Reducer =============================
933977
//======================================== Reducer =============================
934978
//======================================== Reducer =============================
@@ -979,6 +1023,7 @@ function reducer(state=initState(), action={}) {
9791023
case PLOT_PROGRESS_UPDATE :
9801024
case OVERLAY_PLOT_CHANGE_ATTRIBUTES :
9811025
case CHANGE_PRIME_PLOT :
1026+
case ADD_PROCESSED_TILES:
9821027
retState= plotChangeReducer(state,action);
9831028
break;
9841029

@@ -1115,6 +1160,7 @@ function deletePlotView(state,action) {
11151160
if (state.mpwWcsPrimId===plotId) {
11161161
state.mpwWcsPrimId= state.prevActivePlotId;
11171162
}
1163+
state.processedTiles= state.processedTiles.filter( (d) => d.plotId!==plotId);// remove old client tile data
11181164
return state;
11191165
}
11201166

src/firefly/js/visualize/ImageViewerLayout.jsx

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import React, {Component,PropTypes} from 'react';
66
import sCompare from 'react-addons-shallow-compare';
77
import {xor,isEmpty,get, isString, isFunction, throttle} from 'lodash';
8-
import {TileDrawerCanvas} from './iv/TileDrawerCanvas.jsx';
8+
import {ImageRender} from './iv/ImageRender.jsx';
99
import {EventLayer} from './iv/EventLayer.jsx';
1010
import {ImageViewerStatus} from './iv/ImageViewerStatus.jsx';
1111
import {makeScreenPt, makeDevicePt} from './Point.js';
@@ -31,6 +31,7 @@ import {
3131
dispatchChangeActivePlotView,
3232
dispatchUpdateViewSize} from './ImagePlotCntlr.js';
3333
import {fireMouseCtxChange, makeMouseStatePayload, MouseState} from './VisMouseSync.js';
34+
import Color from '../util/Color.js';
3435

3536
const DEFAULT_CURSOR= 'crosshair';
3637

@@ -316,19 +317,43 @@ function makeTileDrawers(pv) {
316317

317318
const plot= primePlot(pv);
318319
const rootDrawer= (
319-
<TileDrawerCanvas opacity={1} plot={plot} plotView={pv} key={'TileDrawer:'+pv.plotId} />
320+
<ImageRender opacity={1} plot={plot} plotView={pv} key={'TileDrawer:'+pv.plotId} />
320321
);
321322
const drawers= pv.overlayPlotViews.filter( (opv) => opv.visible && opv.plot).map( (opv) => {
322323
return (
323-
<TileDrawerCanvas opacity={opv.opacity} plot={opv.plot} plotView={pv}
324-
key={'TileDrawer-overlay:'+opv.imageOverlayId}
324+
<ImageRender opacity={opv.opacity} plot={opv.plot} plotView={pv}
325+
tileAttributes={opv.colorAttributes}
326+
shouldProcess={(im, newData, imState, nextImState) => {
327+
if (newData) return imState.color!== imState.srcImageColor;
328+
else return imState.color!==nextImState.color;
329+
}}
330+
processor={makeMaskColorProcessor(opv.colorAttributes.color)}
331+
key={'TileDrawer-overlay:'+opv.imageOverlayId}
325332
/>
326333
);
327334
});
328335
drawers.unshift(rootDrawer);
329336
return drawers;
330337
}
331338

339+
340+
function makeMaskColorProcessor(colorStr) {
341+
return (imageData) => {
342+
const cAry= Color.toRGB(colorStr);
343+
const {data}= imageData;
344+
const len= data.length;
345+
for(let i= 0; i<len; i+=4) {
346+
if (data[i] || data[i+1] || data[i+2]) {
347+
data[i] = cAry[0];
348+
data[i+1]= cAry[1];
349+
data[i+2]= cAry[2];
350+
}
351+
}
352+
return {imageData, compressible:true};
353+
};
354+
}
355+
356+
332357
/**
333358
*
334359
* @param {object} props

0 commit comments

Comments
 (0)