Skip to content

Commit 65c58b9

Browse files
committed
Fixes processing#5174 Animated GIF masking
When the mask method is called on an Image that contains an animated GIF, the mask is applied to all of its frames.
1 parent b725a6c commit 65c58b9

File tree

2 files changed

+94
-2
lines changed

2 files changed

+94
-2
lines changed

src/image/p5.Image.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -657,8 +657,34 @@ p5.Image.prototype.mask = function(p5Image) {
657657
];
658658

659659
this.drawingContext.globalCompositeOperation = 'destination-in';
660-
p5.Image.prototype.copy.apply(this, copyArgs);
660+
if (this.gifProperties) {
661+
const prevFrameData = this.drawingContext.getImageData(
662+
0,
663+
0,
664+
this.width,
665+
this.height
666+
);
667+
for (let i = 0; i < this.gifProperties.frames.length; i++) {
668+
this.drawingContext.clearRect(0, 0, this.width, this.height);
669+
this.drawingContext.putImageData(
670+
this.gifProperties.frames[i].image,
671+
0,
672+
0
673+
);
674+
p5.Image.prototype.copy.apply(this, copyArgs);
675+
this.gifProperties.frames[i].image = this.drawingContext.getImageData(
676+
0,
677+
0,
678+
this.width,
679+
this.height
680+
);
681+
}
682+
this.drawingContext.putImageData(prevFrameData, 0, 0);
683+
} else {
684+
p5.Image.prototype.copy.apply(this, copyArgs);
685+
}
661686
this.drawingContext.globalCompositeOperation = currBlend;
687+
662688
this.setModified(true);
663689
};
664690

test/unit/image/p5.Image.js

+67-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
suite('p5.Image', function() {
1+
suite.only('p5.Image', function() {
22
var myp5;
33

44
setup(function(done) {
@@ -49,4 +49,70 @@ suite('p5.Image', function() {
4949
assert.strictEqual(img.height, 30);
5050
});
5151
});
52+
53+
suite.only('p5.Image.prototype.mask', function() {
54+
test('it should mask the image', function() {
55+
let img = myp5.createImage(10, 10);
56+
img.loadPixels();
57+
for (let i = 0; i < img.height; i++) {
58+
for (let j = 0; j < img.width; j++) {
59+
let alpha = i < 5 ? 255 : 0;
60+
img.set(i, j, myp5.color(0, 0, 0, alpha));
61+
}
62+
}
63+
img.updatePixels();
64+
65+
let mask = myp5.createImage(10, 10);
66+
mask.loadPixels();
67+
for (let i = 0; i < mask.width; i++) {
68+
for (let j = 0; j < mask.height; j++) {
69+
let alpha = j < 5 ? 255 : 0;
70+
mask.set(i, j, myp5.color(0, 0, 0, alpha));
71+
}
72+
}
73+
mask.updatePixels();
74+
75+
img.mask(mask);
76+
img.loadPixels();
77+
for (let i = 0; i < img.width; i++) {
78+
for (let j = 0; j < img.height; j++) {
79+
let alpha = i < 5 && j < 5 ? 255 : 0;
80+
assert.strictEqual(img.get(i, j)[3], alpha);
81+
}
82+
}
83+
});
84+
85+
test('it should mask the animated gif image', function() {
86+
const imagePath = 'unit/assets/nyan_cat.gif';
87+
return new Promise(function(resolve, reject) {
88+
myp5.loadImage(imagePath, resolve, reject);
89+
}).then(function(img) {
90+
let mask = myp5.createImage(img.width, img.height);
91+
mask.loadPixels();
92+
for (let i = 0; i < mask.width; i++) {
93+
for (let j = 0; j < mask.height; j++) {
94+
const alpha = j < img.height < 2 ? 255 : 0;
95+
mask.set(i, j, myp5.color(0, 0, 0, alpha));
96+
}
97+
}
98+
mask.updatePixels();
99+
100+
img.mask(mask);
101+
for (
102+
frameIndex = 0;
103+
frameIndex < img.gifProperties.numFrames;
104+
frameIndex++
105+
) {
106+
const frameData = img.gifProperties.frames[frameIndex].image.data;
107+
for (let i = 0; i < img.width; i++) {
108+
for (let j = 0; j < img.height; j++) {
109+
const index = 4 * (i + j * img.width) + 3;
110+
const alpha = j < img.height < 2 ? 255 : 0;
111+
assert.strictEqual(frameData[index], alpha);
112+
}
113+
}
114+
}
115+
});
116+
});
117+
});
52118
});

0 commit comments

Comments
 (0)