Now using webworker to compute current colors
This commit is contained in:
parent
30cdb6d335
commit
e11355193b
12 changed files with 295 additions and 37 deletions
|
@ -33,7 +33,7 @@
|
|||
"karma": "0.12.17",
|
||||
"karma-chrome-launcher": "^0.1.4",
|
||||
"karma-phantomjs-launcher": "^0.1.4",
|
||||
"karma-jasmine": "^0.2.0",
|
||||
"karma-jasmine": "^0.3.5",
|
||||
"nodewebkit": "~0.10.1"
|
||||
},
|
||||
"window": {
|
||||
|
|
52
src/js/model/frame/AsyncCachedFrameProcessor.js
Normal file
52
src/js/model/frame/AsyncCachedFrameProcessor.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.model.frame');
|
||||
|
||||
ns.AsyncCachedFrameProcessor = function (cacheResetInterval) {
|
||||
ns.CachedFrameProcessor.call(this, cacheResetInterval);
|
||||
};
|
||||
|
||||
|
||||
pskl.utils.inherit(ns.AsyncCachedFrameProcessor, ns.CachedFrameProcessor);
|
||||
|
||||
/**
|
||||
* Retrieve the processed frame from the cache, in the (optional) namespace
|
||||
* If the first level cache is empty, attempt to clone it from 2nd level cache. If second level cache is empty process the frame.
|
||||
* @param {pskl.model.Frame} frame
|
||||
* @param {String} namespace
|
||||
* @return {Object} the processed frame
|
||||
*/
|
||||
ns.AsyncCachedFrameProcessor.prototype.get = function (frame, callback, namespace) {
|
||||
var processedFrame = null;
|
||||
namespace = namespace || this.defaultNamespace;
|
||||
|
||||
if (!this.cache_[namespace]) {
|
||||
this.cache_[namespace] = {};
|
||||
}
|
||||
|
||||
var cache = this.cache_[namespace];
|
||||
|
||||
var firstCacheKey = frame.getHash();
|
||||
if (cache[firstCacheKey]) {
|
||||
processedFrame = cache[firstCacheKey];
|
||||
} else {
|
||||
var framePixels = JSON.stringify(frame.getPixels());
|
||||
var secondCacheKey = pskl.utils.hashCode(framePixels);
|
||||
if (cache[secondCacheKey]) {
|
||||
processedFrame = this.outputCloner(cache[secondCacheKey], frame);
|
||||
cache[firstCacheKey] = processedFrame;
|
||||
} else {
|
||||
this.frameProcessor(frame, this.onFrameProcessorComplete.bind(this, callback, cache, firstCacheKey, secondCacheKey));
|
||||
}
|
||||
}
|
||||
|
||||
if (processedFrame) {
|
||||
callback(processedFrame);
|
||||
}
|
||||
};
|
||||
|
||||
ns.AsyncCachedFrameProcessor.prototype.onFrameProcessorComplete = function (callback, cache, firstCacheKey, secondCacheKey, processedFrame) {
|
||||
cache[secondCacheKey] = processedFrame;
|
||||
cache[firstCacheKey] = processedFrame;
|
||||
callback(processedFrame);
|
||||
}
|
||||
})();
|
|
@ -17,6 +17,7 @@
|
|||
this.cacheResetInterval = cacheResetInterval || DEFAULT_CLEAR_INTERVAL;
|
||||
this.frameProcessor = DEFAULT_FRAME_PROCESSOR;
|
||||
this.outputCloner = DEFAULT_OUTPUT_CLONER;
|
||||
this.defaultNamespace = DEFAULT_NAMESPACE;
|
||||
|
||||
window.setInterval(this.clear.bind(this), this.cacheResetInterval);
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
this.cache = {};
|
||||
this.currentColors = [];
|
||||
|
||||
this.cachedFrameProcessor = new pskl.model.frame.CachedFrameProcessor();
|
||||
this.cachedFrameProcessor = new pskl.model.frame.AsyncCachedFrameProcessor();
|
||||
this.cachedFrameProcessor.setFrameProcessor(this.getFrameColors_.bind(this));
|
||||
|
||||
this.colorSorter = new pskl.service.color.ColorSorter();
|
||||
|
@ -44,20 +44,37 @@
|
|||
var colors = this.cache[historyIndex];
|
||||
if (colors) {
|
||||
this.setCurrentColors(colors);
|
||||
} else {
|
||||
this.updateCurrentColors_();
|
||||
}
|
||||
};
|
||||
|
||||
ns.CurrentColorsService.prototype.updateCurrentColors_ = function () {
|
||||
var layers = this.piskelController.getLayers();
|
||||
var frames = layers.map(function (l) {return l.getFrames();}).reduce(function (p, n) {return p.concat(n);});
|
||||
var colors = {};
|
||||
|
||||
frames.forEach(function (f) {
|
||||
var frameColors = this.cachedFrameProcessor.get(f);
|
||||
Object.keys(frameColors).slice(0, Constants.MAX_CURRENT_COLORS_DISPLAYED).forEach(function (color) {
|
||||
colors[color] = true;
|
||||
});
|
||||
}.bind(this));
|
||||
this.currentJob = new pskl.utils.Job({
|
||||
items : frames,
|
||||
args : {
|
||||
colors : {}
|
||||
},
|
||||
process : function (frame, callback) {
|
||||
return this.cachedFrameProcessor.get(frame, callback);
|
||||
}.bind(this),
|
||||
onProcessEnd : function (frameColors) {
|
||||
var colors = this.args.colors;
|
||||
Object.keys(frameColors).slice(0, Constants.MAX_CURRENT_COLORS_DISPLAYED).forEach(function (color) {
|
||||
colors[color] = true;
|
||||
});
|
||||
},
|
||||
onComplete : this.updateCurrentColorsReady_.bind(this)
|
||||
});
|
||||
|
||||
this.currentJob.start();
|
||||
};
|
||||
|
||||
ns.CurrentColorsService.prototype.updateCurrentColorsReady_ = function (args) {
|
||||
var colors = args.colors;
|
||||
|
||||
// Remove transparent color from used colors
|
||||
delete colors[Constants.TRANSPARENT_COLOR];
|
||||
|
@ -69,30 +86,13 @@
|
|||
this.setCurrentColors(currentColors);
|
||||
};
|
||||
|
||||
ns.CurrentColorsService.prototype.getFrameColors_ = function (frame) {
|
||||
var frameColors = {};
|
||||
frame.forEachPixel(function (color, x, y) {
|
||||
var hexColor = this.toHexString_(color);
|
||||
frameColors[hexColor] = true;
|
||||
}.bind(this));
|
||||
return frameColors;
|
||||
};
|
||||
ns.CurrentColorsService.prototype.getFrameColors_ = function (frame, processorCallback) {
|
||||
var frameColorsWorker = new pskl.worker.framecolors.FrameColors(frame,
|
||||
function (event) {processorCallback(event.data.colors);},
|
||||
function () {},
|
||||
function (event) {processorCallback({});}
|
||||
);
|
||||
|
||||
ns.CurrentColorsService.prototype.toHexString_ = function (color) {
|
||||
if (color === Constants.TRANSPARENT_COLOR) {
|
||||
return color;
|
||||
} else {
|
||||
color = color.replace(/\s/g, '');
|
||||
var hexRe = (/^#([a-f0-9]{3}){1,2}$/i);
|
||||
var rgbRe = (/^rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)$/i);
|
||||
if (hexRe.test(color)) {
|
||||
return color.toUpperCase();
|
||||
} else if (rgbRe.test(color)) {
|
||||
var exec = rgbRe.exec(color);
|
||||
return pskl.utils.rgbToHex(exec[1] * 1, exec[2] * 1, exec[3] * 1);
|
||||
} else {
|
||||
console.error('Could not convert color to hex : ', color);
|
||||
}
|
||||
}
|
||||
frameColorsWorker.process();
|
||||
};
|
||||
})();
|
|
@ -14,7 +14,7 @@
|
|||
};
|
||||
|
||||
ns.PaletteImageReader.prototype.onImageLoaded_ = function (image) {
|
||||
var imageProcessor = new pskl.worker.ImageProcessor(image,
|
||||
var imageProcessor = new pskl.worker.imageprocessor.ImageProcessor(image,
|
||||
this.onWorkerSuccess_.bind(this),
|
||||
this.onWorkerStep_.bind(this),
|
||||
this.onWorkerError_.bind(this));
|
||||
|
|
29
src/js/utils/Job.js
Normal file
29
src/js/utils/Job.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.utils');
|
||||
|
||||
ns.Job = function (cfg) {
|
||||
this.args = cfg.args;
|
||||
this.items = cfg.items;
|
||||
|
||||
this.process = cfg.process;
|
||||
this.onProcessEnd = cfg.onProcessEnd;
|
||||
this.onComplete = cfg.onComplete;
|
||||
|
||||
this.completed_ = 0;
|
||||
};
|
||||
|
||||
ns.Job.prototype.start = function () {
|
||||
this.items.forEach(function (item, index) {
|
||||
this.process(item, this.processCallback.bind(this, index));
|
||||
}.bind(this))
|
||||
};
|
||||
|
||||
ns.Job.prototype.processCallback = function (index, args) {
|
||||
this.completed_++;
|
||||
this.onProcessEnd(args, index);
|
||||
|
||||
if (this.completed_ === this.items.length) {
|
||||
this.onComplete(this.args);
|
||||
}
|
||||
}
|
||||
})();
|
32
src/js/worker/framecolors/FrameColors.js
Normal file
32
src/js/worker/framecolors/FrameColors.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.worker.framecolors');
|
||||
|
||||
ns.FrameColors = function (frame, onSuccess, onStep, onError) {
|
||||
this.serializedFrame = JSON.stringify(frame.pixels);
|
||||
|
||||
this.onStep = onStep;
|
||||
this.onSuccess = onSuccess;
|
||||
this.onError = onError;
|
||||
|
||||
this.worker = pskl.utils.WorkerUtils.createWorker(ns.FrameColorsWorker, 'frame-colors');
|
||||
this.worker.onmessage = this.onWorkerMessage.bind(this);
|
||||
};
|
||||
|
||||
ns.FrameColors.prototype.process = function () {
|
||||
this.worker.postMessage({
|
||||
serializedFrame : this.serializedFrame
|
||||
});
|
||||
};
|
||||
|
||||
ns.FrameColors.prototype.onWorkerMessage = function (event) {
|
||||
if (event.data.type === 'STEP') {
|
||||
this.onStep(event);
|
||||
} else if (event.data.type === 'SUCCESS') {
|
||||
this.onSuccess(event);
|
||||
this.worker.terminate();
|
||||
} else if (event.data.type === 'ERROR') {
|
||||
this.onError(event);
|
||||
this.worker.terminate();
|
||||
}
|
||||
};
|
||||
})();
|
66
src/js/worker/framecolors/FrameColorsWorker.js
Normal file
66
src/js/worker/framecolors/FrameColorsWorker.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.worker.framecolors');
|
||||
|
||||
if (Constants.TRANSPARENT_COLOR !== 'rgba(0, 0, 0, 0)') {
|
||||
throw 'Constants.TRANSPARENT_COLOR, please update FrameColorsWorker';
|
||||
}
|
||||
|
||||
ns.FrameColorsWorker = function () {
|
||||
|
||||
var TRANSPARENT_COLOR = 'rgba(0, 0, 0, 0)';
|
||||
|
||||
var toHexString_ = function(color) {
|
||||
if (color === TRANSPARENT_COLOR) {
|
||||
return color;
|
||||
} else {
|
||||
color = color.replace(/\s/g, '');
|
||||
var hexRe = (/^#([a-f0-9]{3}){1,2}$/i);
|
||||
var rgbRe = (/^rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)$/i);
|
||||
if (hexRe.test(color)) {
|
||||
return color.toUpperCase();
|
||||
} else if (rgbRe.test(color)) {
|
||||
var exec = rgbRe.exec(color);
|
||||
return rgbToHex(exec[1] * 1, exec[2] * 1, exec[3] * 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var rgbToHex = function (r, g, b) {
|
||||
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
|
||||
};
|
||||
|
||||
var componentToHex = function (c) {
|
||||
var hex = c.toString(16);
|
||||
return hex.length == 1 ? "0" + hex : hex;
|
||||
};
|
||||
|
||||
var getFrameColors = function (frame) {
|
||||
var frameColors = {};
|
||||
for (var x = 0 ; x < frame.length ; x ++) {
|
||||
for (var y = 0 ; y < frame[x].length ; y++) {
|
||||
var color = frame[x][y];
|
||||
var hexColor = toHexString_(color);
|
||||
frameColors[hexColor] = true;
|
||||
}
|
||||
}
|
||||
return frameColors;
|
||||
};
|
||||
|
||||
this.onmessage = function(event) {
|
||||
try {
|
||||
var data = event.data;
|
||||
var frame = JSON.parse(data.serializedFrame);
|
||||
var colors = getFrameColors(frame);
|
||||
this.postMessage({
|
||||
type : 'SUCCESS',
|
||||
colors : colors
|
||||
});
|
||||
} catch (e) {
|
||||
this.postMessage({
|
||||
type : 'ERROR',
|
||||
message : e.message
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
})();
|
|
@ -8,7 +8,7 @@
|
|||
this.onSuccess = onSuccess;
|
||||
this.onError = onError;
|
||||
|
||||
this.worker = pskl.utils.WorkerUtils.createWorker(ns.HashWorker, 'hash-builder');
|
||||
this.worker = pskl.utils.WorkerUtils.createWorker(ns.HashWorker, 'hash');
|
||||
this.worker.onmessage = this.onWorkerMessage.bind(this);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
(function () {
|
||||
var ns = $.namespace('pskl.worker');
|
||||
var ns = $.namespace('pskl.worker.hash');
|
||||
|
||||
ns.HashBuilder = function () {
|
||||
ns.HashWorker = function () {
|
||||
var hashCode = function(str) {
|
||||
var hash = 0;
|
||||
if (str.length !== 0) {
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
"js/utils/FileUtilsDesktop.js",
|
||||
"js/utils/FrameTransform.js",
|
||||
"js/utils/FrameUtils.js",
|
||||
"js/utils/Job.js",
|
||||
"js/utils/LayerUtils.js",
|
||||
"js/utils/ImageResizer.js",
|
||||
"js/utils/PixelUtils.js",
|
||||
|
@ -58,6 +59,7 @@
|
|||
"js/model/Layer.js",
|
||||
"js/model/piskel/Descriptor.js",
|
||||
"js/model/frame/CachedFrameProcessor.js",
|
||||
"js/model/frame/AsyncCachedFrameProcessor.js",
|
||||
"js/model/Palette.js",
|
||||
"js/model/Piskel.js",
|
||||
|
||||
|
@ -188,6 +190,8 @@
|
|||
"js/devtools/init.js",
|
||||
|
||||
// Workers
|
||||
"js/worker/framecolors/FrameColorsWorker.js",
|
||||
"js/worker/framecolors/FrameColors.js",
|
||||
"js/worker/hash/HashWorker.js",
|
||||
"js/worker/hash/Hash.js",
|
||||
"js/worker/imageprocessor/ImageProcessorWorker.js",
|
||||
|
@ -195,6 +199,7 @@
|
|||
|
||||
// Application controller and initialization
|
||||
"js/app.js",
|
||||
|
||||
// Bonus features !!
|
||||
"js/snippets.js"
|
||||
];
|
73
test/js/utils/JobTest.js
Normal file
73
test/js/utils/JobTest.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
describe("Job for // async", function() {
|
||||
|
||||
beforeEach(function() {});
|
||||
afterEach(function() {});
|
||||
|
||||
it("completes synchronous job", function() {
|
||||
// when
|
||||
var isComplete = false;
|
||||
var result = null;
|
||||
// then
|
||||
var job = new pskl.utils.Job({
|
||||
items : [0,1,2,3,4],
|
||||
args : {
|
||||
store : []
|
||||
},
|
||||
process : function (item, callback) {
|
||||
callback(item+5)
|
||||
},
|
||||
onProcessEnd : function (value, index) {
|
||||
this.args.store[index] = value;
|
||||
},
|
||||
onComplete : function (args) {
|
||||
isComplete = true;
|
||||
result = args.store;
|
||||
}
|
||||
});
|
||||
|
||||
job.start();
|
||||
|
||||
// verify
|
||||
expect(isComplete).toBe(true);
|
||||
expect(result).toEqual([5,6,7,8,9]);
|
||||
});
|
||||
|
||||
describe("async", function () {
|
||||
// when
|
||||
var isComplete = false;
|
||||
var result = null;
|
||||
|
||||
beforeEach(function(done) {
|
||||
// then
|
||||
var job = new pskl.utils.Job({
|
||||
items : [0,1,2,3,4],
|
||||
args : {
|
||||
store : []
|
||||
},
|
||||
process : function (item, callback) {
|
||||
setTimeout(function (item, callback) {
|
||||
callback(item+5);
|
||||
}.bind(this, item, callback), 100 - (item * 20));
|
||||
},
|
||||
onProcessEnd : function (value, index) {
|
||||
console.log('Processed ', index);
|
||||
this.args.store[index] = value;
|
||||
},
|
||||
onComplete : function (args) {
|
||||
isComplete = true;
|
||||
result = args.store;
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
job.start();
|
||||
});
|
||||
it("completes asynchronous job", function() {
|
||||
// verify
|
||||
expect(isComplete).toBe(true);
|
||||
expect(result).toEqual([5,6,7,8,9]);
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
});
|
Loading…
Reference in a new issue