Skip to content

Commit

Permalink
Fix ericdrowell#750 improve cache quality in high resolution devices
Browse files Browse the repository at this point in the history
  • Loading branch information
kzhdev committed Feb 3, 2014
1 parent 79f86f2 commit beda79e
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 18 deletions.
21 changes: 16 additions & 5 deletions src/Canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
|| context.backingStorePixelRatio
|| 1,
_pixelRatio = devicePixelRatio / backingStoreRatio;
Kinetic.pixelRatio = Kinetic.pixelRatio || _pixelRatio;

/**
* Canvas Renderer constructor
Expand All @@ -34,9 +35,8 @@
init: function(config) {
config = config || {};

var pixelRatio = config.pixelRatio || Kinetic.pixelRatio || _pixelRatio;
this.pixelRatio = config.pixelRatio || Kinetic.pixelRatio || _pixelRatio;

this.pixelRatio = pixelRatio;
this._canvas = document.createElement('canvas');

// set inline styles
Expand Down Expand Up @@ -67,10 +67,10 @@
return this.pixelRatio;
},
/**
* get pixel ratio
* set pixel ratio
* @method
* @memberof Kinetic.Canvas.prototype
* @param {Number} pixelRatio KineticJS automatically handles pixel ratio adustments in order to render crisp drawings
* @param {Number} pixelRatio KineticJS automatically handles pixel ratio adjustments in order to render crisp drawings
* on all devices. Most desktops, low end tablets, and low end phones, have device pixel ratios
* of 1. Some high end tablets and phones, like iPhones and iPads (not the mini) have a device pixel ratio
* of 2. Some Macbook Pros, and iMacs also have a device pixel ratio of 2. Some high end Android devices have pixel
Expand All @@ -79,8 +79,12 @@
* ratio for special situations, or, if you don't want the pixel ratio to be taken into account, you can set it to 1.
*/
setPixelRatio: function(pixelRatio) {
// get the original widht and height, take into account pixel ratio
var width = this.getWidth() / this.pixelRatio,
height = this.getHeight() / this.pixelRatio;

this.pixelRatio = pixelRatio;
this.setSize(this.getWidth(), this.getHeight());
this.setSize(width, height);
},
/**
* set width
Expand Down Expand Up @@ -195,7 +199,14 @@
Kinetic.Canvas.call(this, config);
this.context = new Kinetic.HitContext(this);
this.setSize(width, height);
this.context._context.scale(this.pixelRatio, this.pixelRatio);
};
Kinetic.HitCanvas.prototype = {
setPixelRatio: function(pixelRatio) {
Kinetic.Canvas.prototype.setPixelRatio.call(this, pixelRatio);
this.context._context.scale(pixelRatio, pixelRatio);
}
}
Kinetic.Util.extend(Kinetic.HitCanvas, Kinetic.Canvas);

})();
24 changes: 16 additions & 8 deletions src/Node.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,10 @@
height = conf.height || this.height(),
drawBorder = conf.drawBorder || false,
cachedSceneCanvas = new Kinetic.SceneCanvas({
pixelRatio: 1,
width: width,
height: height
}),
cachedFilterCanvas = new Kinetic.SceneCanvas({
pixelRatio: 1,
width: width,
height: height
}),
Expand Down Expand Up @@ -204,9 +202,14 @@
return this;
},
_drawCachedSceneCanvas: function(context) {
var cachedScenCanvas = this._getCachedSceneCanvas(),
pixelRatio = cachedScenCanvas.getPixelRatio();
context.save();
context._applyTransform(this);
context.drawImage(this._getCachedSceneCanvas()._canvas, 0, 0);
// If canvas pixelRatio not equal to 1, the cached image will be enlarged,
// we need to scale the cached image back to its original size
context.drawImage(cachedScenCanvas._canvas, 0, 0, cachedScenCanvas.getWidth() / pixelRatio,
cachedScenCanvas.getHeight() / pixelRatio);
context.restore();
},
_getCachedSceneCanvas: function() {
Expand All @@ -215,6 +218,7 @@
sceneCanvas = cachedCanvas.scene,
filterCanvas = cachedCanvas.filter,
filterContext = filterCanvas.getContext(),
pixelRation = sceneCanvas.getPixelRatio(),
len, imageData, n, filter;

if (filters) {
Expand All @@ -223,7 +227,8 @@
len = filters.length;
filterContext.clear();
// copy cached canvas onto filter context
filterContext.drawImage(sceneCanvas._canvas, 0, 0);
filterContext.drawImage(sceneCanvas._canvas, 0, 0, sceneCanvas.getWidth() / pixelRatio,
sceneCanvas.getHeight() / pixelRatio);
imageData = filterContext.getImageData(0, 0, filterCanvas.getWidth(), filterCanvas.getHeight());

// apply filters to filter context
Expand All @@ -248,12 +253,16 @@
},
_drawCachedHitCanvas: function(context) {
var cachedCanvas = this._cache.canvas,
hitCanvas = cachedCanvas.hit;
hitCanvas = cachedCanvas.hit,
pixelRatio = hitCanvas.getPixelRatio();

context.save();
context._applyTransform(this);
context.drawImage(hitCanvas._canvas, 0, 0);
context.restore();
// If canvas pixelRatio not equal to 1, the cached image will be enlarged,
// we need to scale the cached image back to its original size
context.drawImage(hitCanvas._canvas, 0, 0, hitCanvas.getWidth() / pixelRatio,
hitCanvas.getHeight() / pixelRatio);
context.restore();
},
/**
* bind events to the node. KineticJS supports mouseover, mousemove,
Expand Down Expand Up @@ -1206,7 +1215,6 @@
canvas = new Kinetic.SceneCanvas({
width: config.width || this.getWidth() || (stage ? stage.getWidth() : 0),
height: config.height || this.getHeight() || (stage ? stage.getHeight() : 0),
pixelRatio: 1
}),
context = canvas.getContext();

Expand Down
2 changes: 1 addition & 1 deletion src/Stage.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
Kinetic.Util.addMethods(Kinetic.Stage, {
___init: function(config) {
this.nodeType = STAGE;
Kinetic.pixelRatio = config.pixelRatio || Kinetic.pixelRatio;
// call super constructor
Kinetic.Container.call(this, config);
this._id = Kinetic.idCounter++;
Expand Down Expand Up @@ -199,7 +200,6 @@
canvas = new Kinetic.SceneCanvas({
width: config.width || this.getWidth(),
height: config.height || this.getHeight(),
pixelRatio: 1
}),
_context = canvas.getContext()._context,
layers = this.children;
Expand Down
17 changes: 13 additions & 4 deletions test/unit/Node-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1878,8 +1878,11 @@ suite('Node', function() {

circle.setOpacity(0.5);
layer.add(circle);
layer.getCanvas().setPixelRatio(1);

stage.add(layer);


var sceneTrace = layer.getContext().getTrace();
//console.log(sceneTrace);

Expand Down Expand Up @@ -2586,6 +2589,7 @@ suite('Node', function() {

group.add(circle);
layer.add(group);
layer.getCanvas().setPixelRatio(1);
stage.add(layer);

assert.equal(circle.transformsEnabled(), 'all');
Expand All @@ -2608,6 +2612,7 @@ suite('Node', function() {
var layer = new Kinetic.Layer({
x: 100
});
layer.getCanvas().setPixelRatio(1);
var group = new Kinetic.Group({
x: 100
});
Expand Down Expand Up @@ -2823,6 +2828,7 @@ suite('Node', function() {

group.add(circle);
layer.add(group);
layer.getCanvas().setPixelRatio(1);
stage.add(layer);

assert.equal(circle._cache.canvas, undefined);
Expand Down Expand Up @@ -2851,7 +2857,7 @@ suite('Node', function() {

//console.log(layer.getContext().getTrace());

assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();transform(1,0,0,1,74,74);beginPath();arc(0,0,70,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);drawImage([object HTMLCanvasElement],0,0);restore();');
assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();transform(1,0,0,1,74,74);beginPath();arc(0,0,70,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();clearRect(0,0,578,200);save();transform(1,0,0,1,0,0);drawImage([object HTMLCanvasElement],0,0,148,148);restore();');

//console.log(circle._cache.canvas.scene.getContext().getTrace());

Expand Down Expand Up @@ -2944,6 +2950,7 @@ suite('Node', function() {
test('cache group', function(){
var stage = addStage();
var layer = new Kinetic.Layer();
layer.getCanvas().setPixelRatio(1);
var group = new Kinetic.Group({
x: 100,
y: 100,
Expand Down Expand Up @@ -3008,7 +3015,7 @@ suite('Node', function() {
//console.log('---before second draw')
layer.draw();

assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();transform(1,0,0,1,100,30);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1,0,0,1,170,100);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=red;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1,0,0,1,100,170);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=blue;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1,0,0,1,30,100);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=yellow;fill();lineWidth=4;strokeStyle=black;stroke();restore();clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);drawImage([object HTMLCanvasElement],0,0);restore();clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);drawImage([object HTMLCanvasElement],0,0);restore();');
assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();transform(1,0,0,1,100,30);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1,0,0,1,170,100);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=red;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1,0,0,1,100,170);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=blue;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1,0,0,1,30,100);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=yellow;fill();lineWidth=4;strokeStyle=black;stroke();restore();clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);drawImage([object HTMLCanvasElement],0,0,80,80);restore();clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);drawImage([object HTMLCanvasElement],0,0,80,80);restore();');

showHit(layer);
});
Expand Down Expand Up @@ -3059,6 +3066,7 @@ suite('Node', function() {

group.add(top).add(right).add(bottom).add(left);
layer.add(group);
layer.getCanvas().setPixelRatio(1);
stage.add(layer);

//console.log('---before cache')
Expand All @@ -3085,7 +3093,7 @@ suite('Node', function() {
layer.draw();

//console.log(layer.getContext().getTrace())
assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();transform(1.879,0.684,-0.684,1.879,147.883,-31.557);beginPath();arc(0,0,50,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1.879,0.684,-0.684,1.879,231.557,147.883);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=red;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1.879,0.684,-0.684,1.879,52.117,231.557);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=blue;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1.879,0.684,-0.684,1.879,-31.557,52.117);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=yellow;fill();lineWidth=4;strokeStyle=black;stroke();restore();clearRect(0,0,578,200);save();transform(1.879,0.684,-0.684,1.879,-24.316,-166.596);drawImage([object HTMLCanvasElement],0,0);restore();clearRect(0,0,578,200);save();transform(1.879,0.684,-0.684,1.879,-24.316,-166.596);drawImage([object HTMLCanvasElement],0,0);restore();');
assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();transform(1.879,0.684,-0.684,1.879,147.883,-31.557);beginPath();arc(0,0,50,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1.879,0.684,-0.684,1.879,231.557,147.883);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=red;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1.879,0.684,-0.684,1.879,52.117,231.557);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=blue;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1.879,0.684,-0.684,1.879,-31.557,52.117);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=yellow;fill();lineWidth=4;strokeStyle=black;stroke();restore();clearRect(0,0,578,200);save();transform(1.879,0.684,-0.684,1.879,-24.316,-166.596);drawImage([object HTMLCanvasElement],0,0,208,208);restore();clearRect(0,0,578,200);save();transform(1.879,0.684,-0.684,1.879,-24.316,-166.596);drawImage([object HTMLCanvasElement],0,0,208,208);restore();');
showHit(layer);
});

Expand Down Expand Up @@ -3132,6 +3140,7 @@ suite('Node', function() {

group.add(top).add(right).add(bottom).add(left);
layer.add(group);
layer.getCanvas().setPixelRatio(1);
stage.add(layer);

assert.equal(layer._cache.canvas, undefined);
Expand All @@ -3149,7 +3158,7 @@ suite('Node', function() {
layer.draw();

//console.log(layer.getContext().getTrace());
assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();transform(1,0,0,1,100,30);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1,0,0,1,170,100);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=red;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1,0,0,1,100,170);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=blue;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1,0,0,1,30,100);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=yellow;fill();lineWidth=4;strokeStyle=black;stroke();restore();clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);drawImage([object HTMLCanvasElement],0,0);restore();clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);drawImage([object HTMLCanvasElement],0,0);restore();');
assert.equal(layer.getContext().getTrace(), 'clearRect(0,0,578,200);save();transform(1,0,0,1,100,30);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=green;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1,0,0,1,170,100);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=red;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1,0,0,1,100,170);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=blue;fill();lineWidth=4;strokeStyle=black;stroke();restore();save();transform(1,0,0,1,30,100);beginPath();arc(0,0,30,0,6.283,false);closePath();fillStyle=yellow;fill();lineWidth=4;strokeStyle=black;stroke();restore();clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);drawImage([object HTMLCanvasElement],0,0,80,80);restore();clearRect(0,0,578,200);save();transform(1,0,0,1,100,100);drawImage([object HTMLCanvasElement],0,0,80,80);restore();');


// make sure that the hit graph is also rendered after caching the layer
Expand Down

0 comments on commit beda79e

Please sign in to comment.