Skip to content

Commit b6969d7

Browse files
author
David Gillen
committed
Fix for standardizing stretching logic across all filters, standardized code a bit more
1 parent 064034b commit b6969d7

5 files changed

Lines changed: 98 additions & 69 deletions

File tree

src/easeljs/filters/AlphaMapFilter.js

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,24 @@ this.createjs = this.createjs || {};
8585

8686
// private properties:
8787
/**
88-
* @property _alphaMap
88+
* @property _map
8989
* @protected
9090
* @type HTMLImageElement|HTMLCanvasElement
9191
**/
92-
this._alphaMap = null;
92+
this._map = null;
9393

9494
/**
95-
* @property _mapData
95+
* @property _mapCtx
9696
* @protected
97-
* @type Uint8ClampedArray
97+
* @type CanvasRenderingContext2D
9898
**/
99-
this._mapData = null;
99+
this._mapCtx = null;
100+
101+
/**
102+
* @property _mapTexture
103+
* @protected
104+
* @type WebGLTexture
105+
*/
100106
this._mapTexture = null;
101107

102108
this.FRAG_SHADER_BODY = (
@@ -124,7 +130,7 @@ this.createjs = this.createjs || {};
124130

125131
/** docced in super class **/
126132
p.shaderParamSetup = function(gl, stage, shaderProgram) {
127-
if(!this._mapTexture) { this._mapTexture = gl.createTexture(); }
133+
if(this._mapTexture === null) { this._mapTexture = gl.createTexture(); }
128134

129135
gl.activeTexture(gl.TEXTURE1);
130136
gl.bindTexture(gl.TEXTURE_2D, this._mapTexture);
@@ -143,8 +149,6 @@ this.createjs = this.createjs || {};
143149
/** docced in super class **/
144150
p.clone = function () {
145151
var o = new AlphaMapFilter(this.alphaMap);
146-
o._alphaMap = this._alphaMap;
147-
o._mapData = this._mapData;
148152
return o;
149153
};
150154

@@ -156,18 +160,43 @@ this.createjs = this.createjs || {};
156160

157161
// private methods:
158162
/** docced in super class **/
159-
p._applyFilter = function (imageData) {
160-
if (!this.alphaMap) { return true; }
163+
p._applyFilter = function(imageData) {
161164
if (!this._prepAlphaMap()) { return false; }
162165

163-
var data = imageData.data;
164-
var map = this._mapData;
165-
166-
167-
for(var i=0, l=data.length; i<l; i += 4) {
168-
data[i + 3] = map[i] || 0;
166+
var outArray = imageData.data;
167+
var width = imageData.width;
168+
var height = imageData.height;
169+
var rowOffset, pixelStart;
170+
171+
var sampleData = this._mapCtx.getImageData(0,0, this._map.width,this._map.height);
172+
var sampleArray = sampleData.data;
173+
var sampleWidth = sampleData.width;
174+
var sampleHeight = sampleData.height;
175+
var sampleRowOffset, samplePixelStart;
176+
177+
var widthRatio = sampleWidth/width;
178+
var heightRatio = sampleHeight/height;
179+
180+
// performance optimizing lookup
181+
182+
// the x and y need to stretch separately, nesting the for loops simplifies this logic even if the array is flat
183+
for (var i=0; i<height; i++) {
184+
rowOffset = i * width;
185+
sampleRowOffset = ((i*heightRatio) |0) * sampleWidth;
186+
187+
// the arrays are int arrays, so a single pixel is [r,g,b,a, ...],so calculate the start of the pixel
188+
for (var j=0; j<width; j++) {
189+
pixelStart = (rowOffset + j) *4;
190+
samplePixelStart = (sampleRowOffset + ((j*widthRatio) |0)) *4;
191+
192+
// modify the pixels
193+
outArray[pixelStart] = outArray[pixelStart];
194+
outArray[pixelStart+1] = outArray[pixelStart+1];
195+
outArray[pixelStart+2] = outArray[pixelStart+2];
196+
outArray[pixelStart+3] = sampleArray[samplePixelStart];
197+
}
169198
}
170-
199+
171200
return true;
172201
};
173202

@@ -177,10 +206,9 @@ this.createjs = this.createjs || {};
177206
**/
178207
p._prepAlphaMap = function () {
179208
if (!this.alphaMap) { return false; }
180-
if (this.alphaMap == this._alphaMap && this._mapData) { return true; }
209+
if (this.alphaMap === this._map && this._mapCtx) { return true; }
181210

182-
this._mapData = null;
183-
var map = this._alphaMap = this.alphaMap;
211+
var map = this._map = this.alphaMap;
184212
var canvas = map;
185213
var ctx;
186214
if (map instanceof HTMLCanvasElement) {
@@ -193,14 +221,8 @@ this.createjs = this.createjs || {};
193221
ctx.drawImage(map, 0, 0);
194222
}
195223

196-
try {
197-
var imgData = ctx.getImageData(0, 0, map.width, map.height);
198-
} catch (e) {
199-
//if (!this.suppressCrossDomainErrors) throw new Error("unable to access local image data: " + e);
200-
return false;
201-
}
202-
203-
this._mapData = imgData.data;
224+
this._mapCtx = ctx;
225+
204226
return true;
205227
};
206228

src/easeljs/filters/AlphaMaskFilter.js

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -129,26 +129,25 @@ this.createjs = this.createjs || {};
129129
* @param {Number} y The y position to use for the source rect.
130130
* @param {Number} width The width to use for the source rect.
131131
* @param {Number} height The height to use for the source rect.
132-
* @param {CanvasRenderingContext2D} [targetCtx] NOT SUPPORTED IN THIS FILTER. The 2D context to draw the result to. Defaults to the context passed to ctx.
133-
* @param {Number} [targetX] NOT SUPPORTED IN THIS FILTER. The x position to draw the result to. Defaults to the value passed to x.
134-
* @param {Number} [targetY] NOT SUPPORTED IN THIS FILTER. The y position to draw the result to. Defaults to the value passed to y.
132+
* @param {CanvasRenderingContext2D} [targetCtx] The 2D context to draw the result to. Defaults to the context passed to ctx.
135133
* @return {Boolean} If the filter was applied successfully.
136134
**/
137-
p.applyFilter = function (ctx, x, y, width, height, targetCtx, targetX, targetY) {
135+
p.applyFilter = function (ctx, x, y, width, height, targetCtx) {
138136
if (!this.mask) { return true; }
139-
targetCtx = targetCtx || ctx;
140-
if (targetX == null) { targetX = x; }
141-
if (targetY == null) { targetY = y; }
142137

143-
targetCtx.save();
144-
if (ctx != targetCtx) {
145-
// TODO: support targetCtx and targetX/Y
146-
// clearRect, then draw the ctx in?
147-
return false;
138+
if (targetCtx === undefined) { targetCtx = ctx; }
139+
if (targetCtx !== ctx) {
140+
targetCtx.drawImage(ctx.canvas,
141+
0, 0, ctx.canvas.width, ctx.canvas.height,
142+
0, 0, targetCtx.canvas.width, targetCtx.canvas.height
143+
);
148144
}
149145

146+
targetCtx.save();
147+
150148
targetCtx.globalCompositeOperation = "destination-in";
151-
targetCtx.drawImage(this.mask, targetX, targetY);
149+
targetCtx.drawImage(this.mask, 0,0, this.mask.width,this.mask.height, x,y, width,height);
150+
152151
targetCtx.restore();
153152
return true;
154153
};

src/easeljs/filters/ColorMatrix.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ this.createjs = this.createjs||{};
138138

139139
/**
140140
* Create an instance of ColorMatrix using the Greyscale preset.
141-
* This differs from -100 saturation in that it uses a balanced representation per channel instead of luminance.
141+
* Note: -100 saturation accounts for perceived brightness, the greyscale preset treats all channels equally.
142142
* @returns {ColorMatrix}
143143
*/
144144
ColorMatrix.createGreyscalePreset = function() {

src/easeljs/filters/DisplacementFilter.js

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -148,39 +148,51 @@ this.createjs = this.createjs||{};
148148
// private methods:
149149
/** docced in super class **/
150150
p._applyFilter = function(imageData) {
151-
var refPixels = imageData.data.slice();
152-
var outPixels = imageData.data;
151+
var refArray = imageData.data.slice(); // as we're reaching across pixels we need an unmodified clone of the source
152+
153+
var outArray = imageData.data;
153154
var width = imageData.width;
154155
var height = imageData.height;
155-
var offset, pixel;
156+
var rowOffset, pixelStart;
157+
158+
var sampleData = this._dudvCtx.getImageData(0,0, this.dudvMap.width,this.dudvMap.height);
159+
var sampleArray = sampleData.data;
160+
var sampleWidth = sampleData.width;
161+
var sampleHeight = sampleData.height;
162+
var sampleRowOffset, samplePixelStart;
156163

157-
var dudvData = this._dudvCtx.getImageData(0,0, this.dudvMap.width,this.dudvMap.height);
158-
var dudvPixels = dudvData.data;
159-
var dudvWidth = dudvData.width;
160-
var dudvHeight = dudvData.height;
161-
var dudvOffset, dudvPixel;
164+
var widthRatio = sampleWidth/width;
165+
var heightRatio = sampleHeight/height;
166+
var pxRange = 1/255;
162167

168+
// performance optimizing lookup
169+
var distance = this.distance*2;
170+
171+
// the x and y need to stretch separately, nesting the for loops simplifies this logic even if the array is flat
163172
for (var i=0; i<height; i++) {
164-
offset = i * width;
165-
dudvOffset = ((i*(dudvHeight/height))|0) * dudvWidth;
173+
rowOffset = i * width;
174+
sampleRowOffset = ((i*heightRatio) |0) * sampleWidth;
175+
176+
// the arrays are int arrays, so a single pixel is [r,g,b,a, ...],so calculate the start of the pixel
166177
for (var j=0; j<width; j++) {
167-
pixel = (offset + j) *4;
168-
dudvPixel = (dudvOffset + ((j*(dudvWidth/width)) |0) ) *4;
178+
pixelStart = (rowOffset + j) *4;
179+
samplePixelStart = (sampleRowOffset + ((j*widthRatio) |0)) *4;
169180

170-
var deltaPower = dudvPixels[dudvPixel+3] / 255;
171-
var xDelta = ((((dudvPixels[dudvPixel] - 128)/128) * deltaPower) * this.distance) |0;
172-
var yDelta = ((((dudvPixels[dudvPixel+1] - 128)/128) * deltaPower) * this.distance |0);
181+
// modify the pixels
182+
var deltaPower = sampleArray[samplePixelStart+3] * pxRange * distance;
183+
var xDelta = ((sampleArray[samplePixelStart] * pxRange - 0.5) * deltaPower) |0;
184+
var yDelta = ((sampleArray[samplePixelStart+1] * pxRange - 0.5) * deltaPower) |0;
173185

174186
if (j+xDelta < 0) { xDelta = -j; }
175187
if (j+xDelta > width) { xDelta = width-j; }
176188
if (i+yDelta < 0) { yDelta = -i; }
177189
if (i+yDelta > height) { yDelta = height-i; }
178190

179-
var targetPixel = (pixel + xDelta*4) + yDelta*4*width;
180-
outPixels[pixel] = refPixels[targetPixel];
181-
outPixels[pixel+1] = refPixels[targetPixel+1];
182-
outPixels[pixel+2] = refPixels[targetPixel+2];
183-
outPixels[pixel+3] = refPixels[targetPixel+3];
191+
var targetPixelStart = (pixelStart + xDelta*4) + yDelta*4*width;
192+
outArray[pixelStart] = refArray[targetPixelStart];
193+
outArray[pixelStart+1] = refArray[targetPixelStart+1];
194+
outArray[pixelStart+2] = refArray[targetPixelStart+2];
195+
outArray[pixelStart+3] = refArray[targetPixelStart+3];
184196
}
185197
}
186198

src/easeljs/filters/Filter.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,23 +164,19 @@ this.createjs = this.createjs||{};
164164
* @param {Number} y The y position to use for the source rect.
165165
* @param {Number} width The width to use for the source rect.
166166
* @param {Number} height The height to use for the source rect.
167-
* @param {CanvasRenderingContext2D} [targetCtx] The 2D context to draw the result to. Defaults to the context passed to ctx.
168-
* @param {Number} [targetX] The x position to draw the result to. Defaults to the value passed to x.
169-
* @param {Number} [targetY] The y position to draw the result to. Defaults to the value passed to y.
167+
* @param {CanvasRenderingContext2D} [targetCtx=ctx] The 2D context to draw the result to. Defaults to the context passed to ctx.
170168
* @return {Boolean} If the filter was applied successfully.
171169
**/
172-
p.applyFilter = function(ctx, x, y, width, height, targetCtx, targetX, targetY) {
170+
p.applyFilter = function(ctx, x, y, width, height, targetCtx) {
173171
// this is the default behaviour because most filters access pixel data. It is overridden when not needed.
174172
targetCtx = targetCtx || ctx;
175-
if (targetX == null) { targetX = x; }
176-
if (targetY == null) { targetY = y; }
177173
try {
178174
var imageData = ctx.getImageData(x, y, width, height);
179175
} catch (e) {
180176
return false;
181177
}
182178
if (this._applyFilter(imageData)) {
183-
targetCtx.putImageData(imageData, targetX, targetY);
179+
targetCtx.putImageData(imageData, x, y);
184180
return true;
185181
}
186182
return false;

0 commit comments

Comments
 (0)