Skip to content

Commit 387ff4f

Browse files
committed
BitmapData.processPixelRGB lets you perform a custom callback on every pixel in the BitmapData.
1 parent c88fa2b commit 387ff4f

2 files changed

Lines changed: 116 additions & 50 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ Version 2.0.4 - "Mos Shirare" - in development
9191
* Group.classType allows you to change the type of object that Group.create or createMultiple makes to your own custom class.
9292
* Game.scratch is a single handy BitmapData instance that can be used as a visual scratch-pad, for off-screen bitmap manipulation (and is used as such by BitmapData itself).
9393
* Device.support32bit is a new boolean that sets if the context supports 32bit pixel manipulation using array buffer views or not.
94+
* BitmapData.processPixelRGB lets you perform a custom callback on every pixel in the BitmapData.
9495

9596

9697
### Bug Fixes

src/gameobjects/BitmapData.js

Lines changed: 115 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,9 @@ Phaser.BitmapData = function (game, key, width, height) {
8383
this.data = this.imageData.data;
8484

8585
/**
86-
* @property {Uint32Array} pixels - A Uint32Array view into BitmapData.buffer.
86+
* @property {Int32Array} pixels - An Int32Array view into BitmapData.buffer.
8787
*/
88-
this.pixels = new Uint32Array(this.buffer);
88+
this.pixels = new Int32Array(this.buffer);
8989

9090
/**
9191
* @property {PIXI.BaseTexture} baseTexture - The PIXI.BaseTexture.
@@ -221,7 +221,7 @@ Phaser.BitmapData.prototype = {
221221

222222
/**
223223
* This re-creates the BitmapData.imageData from the current context.
224-
* It then re-builds the ArrayBuffer, the data Uint8ClampedArray reference and the pixels Uint32Array.
224+
* It then re-builds the ArrayBuffer, the data Uint8ClampedArray reference and the pixels Int32Array.
225225
* If not given the dimensions defaults to the full size of the context.
226226
*
227227
* @method Phaser.BitmapData#update
@@ -233,7 +233,7 @@ Phaser.BitmapData.prototype = {
233233

234234
/**
235235
* This re-creates the BitmapData.imageData from the current context.
236-
* It then re-builds the ArrayBuffer, the data Uint8ClampedArray reference and the pixels Uint32Array.
236+
* It then re-builds the ArrayBuffer, the data Uint8ClampedArray reference and the pixels Int32Array.
237237
* If not given the dimensions defaults to the full size of the context.
238238
*
239239
* @method Phaser.BitmapData#refreshBuffer
@@ -261,64 +261,127 @@ Phaser.BitmapData.prototype = {
261261
}
262262

263263
this.data = this.imageData.data;
264-
this.pixels = new Uint32Array(this.buffer);
264+
this.pixels = new Int32Array(this.buffer);
265265

266266
},
267267

268268
/**
269-
* Sets the color of the given pixel to the specified red, green, blue and alpha values.
269+
* Scans through the area specified in this BitmapData and sends a color object for every pixel to the given callback.
270+
* The callback will be sent a single object with 6 properties: `{ r: number, g: number, b: number, a: number, color: number, rgba: string }`.
271+
* Where r, g, b and a are integers between 0 and 255 representing the color component values for red, green, blue and alpha.
272+
* The `color` property is an Int32 of the full color. Note the endianess of this will change per system.
273+
* The `rgba` property is a CSS style rgba() string which can be used with context.fillStyle calls, among others.
274+
* The callback must return either `false`, in which case no change will be made to the pixel, or a new color object.
275+
* If a new color object is returned the pixel will be set to the r, g, b and a color values given within it.
270276
*
271-
* @method Phaser.BitmapData#replaceRGB
272-
* @param {number} x - The X coordinate of the pixel to be set.
273-
* @param {number} y - The Y coordinate of the pixel to be set.
274-
* @param {number} red - The red color value, between 0 and 0xFF (255).
275-
* @param {number} green - The green color value, between 0 and 0xFF (255).
276-
* @param {number} blue - The blue color value, between 0 and 0xFF (255).
277-
* @param {number} alpha - The alpha color value, between 0 and 0xFF (255).
277+
* @method Phaser.BitmapData#processPixelRGB
278+
* @param {function} callback - The callback that will be sent each pixel color object to be processed.
279+
* @param {object} callbackContext - The context under which the callback will be called.
280+
* @param {number} [x=0] - The x coordinate of the top-left of the region to process from.
281+
* @param {number} [y=0] - The y coordinate of the top-left of the region to process from.
282+
* @param {number} [width] - The width of the region to process.
283+
* @param {number} [height] - The height of the region to process.
278284
*/
279-
replaceRGB: function (sourceR, sourceG, sourceB, sourceA, destR, destG, destB, destA, region) {
285+
processPixelRGB: function (callback, callbackContext, x, y, width, height) {
280286

281-
var tx = 0;
282-
var ty = 0;
283-
var w = this.width;
284-
var h = this.height;
287+
if (typeof x === 'undefined') { x = 0; }
288+
if (typeof y === 'undefined') { y = 0; }
289+
if (typeof width === 'undefined') { width = this.width; }
290+
if (typeof height === 'undefined') { height = this.height; }
285291

286-
if (region instanceof Phaser.Rectangle)
292+
var w = x + width;
293+
var h = y + height;
294+
var pixel = { r: 0, g: 0, b: 0, a: 0, rgba: '' };
295+
var result = { r: 0, g: 0, b: 0, a: 0 };
296+
var color = 0;
297+
var dirty = false;
298+
299+
for (var ty = y; ty < h; ty++)
287300
{
288-
tx = region.x;
289-
ty = region.y;
290-
w = region.width;
291-
h = region.height;
292-
}
301+
for (var tx = x; tx < w; tx++)
302+
{
303+
color = this.getPixel32(tx, ty);
304+
this.unpackPixel(color, pixel);
305+
result = callback.call(callbackContext, pixel, color);
293306

294-
// for (var x = tx; x < w)
307+
if (result !== false && result !== null)
308+
{
309+
this.setPixel32(tx, ty, result.r, result.g, result.b, result.a, false);
310+
dirty = true;
311+
}
312+
}
313+
}
295314

296-
if (x >= 0 && x <= this.width && y >= 0 && y <= this.height)
315+
if (dirty)
297316
{
298-
this.pixels[y * this.width + x] = (alpha << 24) | (blue << 16) | (green << 8) | red;
317+
this.context.putImageData(this.imageData, 0, 0);
318+
this.dirty = true;
319+
}
299320

321+
},
300322

301-
// this.imageData.data.set(this.data8);
323+
/**
324+
* Replaces all pixels matching the given RGBA values with the new RGBA values in the given region,
325+
* or the whole BitmapData if no region provided.
326+
*
327+
* @method Phaser.BitmapData#replaceRGB
328+
* @param {number} r1 - The red color value to be replaced. Between 0 and 255.
329+
* @param {number} g1 - The green color value to be replaced. Between 0 and 255.
330+
* @param {number} b1 - The blue color value to be replaced. Between 0 and 255.
331+
* @param {number} a1 - The alpha color value to be replaced. Between 0 and 255.
332+
* @param {number} r2 - The red color value that is the replacement color. Between 0 and 255.
333+
* @param {number} g2 - The green color value that is the replacement color. Between 0 and 255.
334+
* @param {number} b2 - The blue color value that is the replacement color. Between 0 and 255.
335+
* @param {number} a2 - The alpha color value that is the replacement color. Between 0 and 255.
336+
* @param {Phaser.Rectangle} [region] - The area to perform the search over. If not given it will replace over the whole BitmapData.
337+
*/
338+
replaceRGB: function (r1, g1, b1, a1, r2, g2, b2, a2, region) {
339+
340+
var sx = 0;
341+
var sy = 0;
342+
var w = this.width;
343+
var h = this.height;
344+
var source = this.packPixel(r1, g1, b1, a1);
302345

303-
this.context.putImageData(this.imageData, 0, 0);
346+
if (region !== undefined && region instanceof Phaser.Rectangle)
347+
{
348+
sx = region.x;
349+
sy = region.y;
350+
w = region.width;
351+
h = region.height;
352+
}
304353

305-
this.dirty = true;
354+
for (var y = 0; y < h; y++)
355+
{
356+
for (var x = 0; x < w; x++)
357+
{
358+
if (this.getPixel32(sx + x, sy + y) === source)
359+
{
360+
this.setPixel32(sx + x, sy + y, r2, g2, b2, a2, false);
361+
}
362+
}
306363
}
307364

365+
this.context.putImageData(this.imageData, 0, 0);
366+
this.dirty = true;
367+
308368
},
309369

310370
/**
311371
* Sets the color of the given pixel to the specified red, green, blue and alpha values.
312372
*
313373
* @method Phaser.BitmapData#setPixel32
314-
* @param {number} x - The X coordinate of the pixel to be set.
315-
* @param {number} y - The Y coordinate of the pixel to be set.
374+
* @param {number} x - The x coordinate of the pixel to be set. Must lay within the dimensions of this BitmapData.
375+
* @param {number} y - The y coordinate of the pixel to be set. Must lay within the dimensions of this BitmapData.
316376
* @param {number} red - The red color value, between 0 and 0xFF (255).
317377
* @param {number} green - The green color value, between 0 and 0xFF (255).
318378
* @param {number} blue - The blue color value, between 0 and 0xFF (255).
319379
* @param {number} alpha - The alpha color value, between 0 and 0xFF (255).
380+
* @param {boolean} [immediate=true] - If `true` the context.putImageData will be called and the dirty flag set.
320381
*/
321-
setPixel32: function (x, y, red, green, blue, alpha) {
382+
setPixel32: function (x, y, red, green, blue, alpha, immediate) {
383+
384+
if (typeof immediate === 'undefined') { immediate = true; }
322385

323386
if (x >= 0 && x <= this.width && y >= 0 && y <= this.height)
324387
{
@@ -331,9 +394,11 @@ Phaser.BitmapData.prototype = {
331394
this.pixels[y * this.width + x] = (red << 24) | (green << 16) | (blue << 8) | alpha;
332395
}
333396

334-
this.context.putImageData(this.imageData, 0, 0);
335-
336-
this.dirty = true;
397+
if (immediate)
398+
{
399+
this.context.putImageData(this.imageData, 0, 0);
400+
this.dirty = true;
401+
}
337402
}
338403

339404
},
@@ -342,15 +407,17 @@ Phaser.BitmapData.prototype = {
342407
* Sets the color of the given pixel to the specified red, green and blue values.
343408
*
344409
* @method Phaser.BitmapData#setPixel
345-
* @param {number} x - The X coordinate of the pixel to be set.
346-
* @param {number} y - The Y coordinate of the pixel to be set.
347-
* @param {number} red - The red color value (between 0 and 255)
348-
* @param {number} green - The green color value (between 0 and 255)
349-
* @param {number} blue - The blue color value (between 0 and 255)
410+
* @param {number} x - The x coordinate of the pixel to be set. Must lay within the dimensions of this BitmapData.
411+
* @param {number} y - The y coordinate of the pixel to be set. Must lay within the dimensions of this BitmapData.
412+
* @param {number} red - The red color value, between 0 and 0xFF (255).
413+
* @param {number} green - The green color value, between 0 and 0xFF (255).
414+
* @param {number} blue - The blue color value, between 0 and 0xFF (255).
415+
* @param {number} alpha - The alpha color value, between 0 and 0xFF (255).
416+
* @param {boolean} [immediate=true] - If `true` the context.putImageData will be called and the dirty flag set.
350417
*/
351-
setPixel: function (x, y, red, green, blue) {
418+
setPixel: function (x, y, red, green, blue, immediate) {
352419

353-
this.setPixel32(x, y, red, green, blue, 255);
420+
this.setPixel32(x, y, red, green, blue, 255, immediate);
354421

355422
},
356423

@@ -410,10 +477,7 @@ Phaser.BitmapData.prototype = {
410477
*/
411478
getPixelRGB: function (x, y) {
412479

413-
if (x >= 0 && x <= this.width && y >= 0 && y <= this.height)
414-
{
415-
return this.unpackPixel(this.pixels[y * this.width + x]);
416-
}
480+
return this.unpackPixel(this.getPixel32(x, y));
417481

418482
},
419483

@@ -468,14 +532,14 @@ Phaser.BitmapData.prototype = {
468532
* @author Matt DesLauriers (@mattdesl)
469533
* @method Phaser.BitmapData#unpackPixel
470534
* @param {number} rgba - The integer, packed in endian order by packPixel.
471-
* @param {object} out - The color object with `r, g, b, a` properties, or null.
472-
* @return {object} A color representing the pixel at that location.
535+
* @param {object} out - The color object with `r, g, b, a, rgba and color` properties, or null.
536+
* @return {object} A color object.
473537
*/
474538
unpackPixel: function (rgba, out) {
475539

476540
if (!out)
477541
{
478-
out = { r: 0, g: 0, b: 0, a: 0, rgba: '' };
542+
out = { r: 0, g: 0, b: 0, a: 0, color: 0, rgba: '' };
479543
}
480544

481545
if (this.littleEndian)
@@ -493,6 +557,7 @@ Phaser.BitmapData.prototype = {
493557
out.a = ((rgba & 0x000000ff));
494558
}
495559

560+
out.color = rgba;
496561
out.rgba = 'rgba(' + out.r + ',' + out.g + ',' + out.b + ',' + out.a + ')';
497562

498563
return out;

0 commit comments

Comments
 (0)