Skip to content

Commit 0cb97a2

Browse files
committed
Address review feedback
1 parent 10a6d87 commit 0cb97a2

File tree

5 files changed

+35
-63
lines changed

5 files changed

+35
-63
lines changed

src/PenSkin.js

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ const DefaultPenAttributes = {
2525
diameter: 1
2626
};
2727

28+
/**
29+
* Reused memory location for storing a premultiplied pen color.
30+
* @type {FloatArray}
31+
*/
32+
const __premultipliedColor = [0, 0, 0, 0];
33+
2834

2935
/**
3036
* Reused memory location for projection matrices.
@@ -79,9 +85,6 @@ class PenSkin extends Skin {
7985
constructor (id, renderer) {
8086
super(id);
8187

82-
// This silhouette will be updated with data from `gl.readPixels`, which is premultiplied.
83-
this._silhouette.premultiplied = true;
84-
8588
/**
8689
* @private
8790
* @type {RenderWebGL}
@@ -154,13 +157,6 @@ class PenSkin extends Skin {
154157
return true;
155158
}
156159

157-
/**
158-
* @returns {boolean} true if alpha is premultiplied, false otherwise
159-
*/
160-
get hasPremultipliedAlpha () {
161-
return true;
162-
}
163-
164160
/**
165161
* @return {Array<number>} the "native" size, in texels, of this skin. [width, height]
166162
*/
@@ -188,7 +184,7 @@ class PenSkin extends Skin {
188184
clear () {
189185
const gl = this._renderer.gl;
190186
twgl.bindFramebufferInfo(gl, this._framebuffer);
191-
187+
192188
/* Reset framebuffer to transparent black */
193189
gl.clearColor(0, 0, 0, 0);
194190
gl.clear(gl.COLOR_BUFFER_BIT);
@@ -375,6 +371,13 @@ class PenSkin extends Skin {
375371
const radius = diameter / 2;
376372
const yScalar = (0.50001 - (radius / (length + diameter)));
377373

374+
// Premultiply pen color by pen transparency
375+
const penColor = penAttributes.color4f || DefaultPenAttributes.color4f;
376+
__premultipliedColor[0] = penColor[0] * penColor[3];
377+
__premultipliedColor[1] = penColor[1] * penColor[3];
378+
__premultipliedColor[2] = penColor[2] * penColor[3];
379+
__premultipliedColor[3] = penColor[3];
380+
378381
const uniforms = {
379382
u_positionScalar: yScalar,
380383
u_capScale: diameter,
@@ -388,7 +391,7 @@ class PenSkin extends Skin {
388391
twgl.m4.scaling(scalingVector, __modelScalingMatrix),
389392
__modelMatrix
390393
),
391-
u_lineColor: penAttributes.color4f || DefaultPenAttributes.color4f
394+
u_lineColor: __premultipliedColor
392395
};
393396

394397
twgl.setUniforms(currentShader, uniforms);
@@ -648,7 +651,7 @@ class PenSkin extends Skin {
648651
skinImageData.data.set(skinPixels);
649652
skinContext.putImageData(skinImageData, 0, 0);
650653

651-
this._silhouette.update(this._canvas);
654+
this._silhouette.update(this._canvas, true /* isPremultiplied */);
652655

653656
this._silhouetteDirty = false;
654657
}

src/RenderWebGL.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1895,7 +1895,7 @@ class RenderWebGL extends EventEmitter {
18951895
}
18961896
*/
18971897
Drawable.sampleColor4b(vec, drawables[index].drawable, __blendColor);
1898-
// if we are fully transparent, go to the next one "down"
1898+
// Equivalent to gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
18991899
dst[0] += __blendColor[0] * blendAlpha;
19001900
dst[1] += __blendColor[1] * blendAlpha;
19011901
dst[2] += __blendColor[2] * blendAlpha;

src/Silhouette.js

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ let __SilhouetteUpdateCanvas;
2020
* @return {number} Alpha value for x/y position
2121
*/
2222
const getPoint = ({_width: width, _height: height, _colorData: data}, x, y) => {
23-
// 0 if outside bouds, otherwise read from data.
23+
// 0 if outside bounds, otherwise read from data.
2424
if (x >= width || y >= height || x < 0 || y < 0) {
2525
return 0;
2626
}
@@ -47,7 +47,7 @@ const __cornerWork = [
4747
* @return {Uint8ClampedArray} The dst vector.
4848
*/
4949
const getColor4b = ({_width: width, _height: height, _colorData: data}, x, y, dst) => {
50-
// 0 if outside bouds, otherwise read from data.
50+
// 0 if outside bounds, otherwise read from data.
5151
if (x >= width || y >= height || x < 0 || y < 0) {
5252
return dst.fill(0);
5353
}
@@ -71,7 +71,7 @@ const getColor4b = ({_width: width, _height: height, _colorData: data}, x, y, ds
7171
* @return {Uint8ClampedArray} The dst vector.
7272
*/
7373
const getPremultipliedColor4b = ({_width: width, _height: height, _colorData: data}, x, y, dst) => {
74-
// 0 if outside bouds, otherwise read from data.
74+
// 0 if outside bounds, otherwise read from data.
7575
if (x >= width || y >= height || x < 0 || y < 0) {
7676
return dst.fill(0);
7777
}
@@ -103,13 +103,6 @@ class Silhouette {
103103
*/
104104
this._colorData = null;
105105

106-
/**
107-
* Whether or not the color data is premultiplied with its alpha channel.
108-
* If it isn't, it will be multiplied here.
109-
* @type {boolean}
110-
*/
111-
this._isPremultiplied = false;
112-
113106
// By default, silhouettes are assumed not to contain premultiplied image data,
114107
// so when we get a color, we want to multiply it by its alpha channel.
115108
// Point `_getColor` to the version of the function that multiplies.
@@ -118,36 +111,13 @@ class Silhouette {
118111
this.colorAtNearest = this.colorAtLinear = (_, dst) => dst.fill(0);
119112
}
120113

121-
/**
122-
* @returns {boolean} true if the silhouette color data is premultiplied, false if not.
123-
*/
124-
get premultiplied () {
125-
return this._isPremultiplied;
126-
}
127-
128-
/**
129-
* Set the alpha premultiplication state of this silhouette, to ensure proper color values are returned.
130-
* If set to true, the silhouette will assume it is being set with premultiplied color data,
131-
* and will not multiply color values by alpha.
132-
* If set to false, it will multiply color values by alpha.
133-
* @param {boolean} isPremultiplied Whether this silhouette will be populated with premultiplied color data.
134-
*/
135-
set premultiplied (isPremultiplied) {
136-
this._isPremultiplied = isPremultiplied;
137-
138-
if (isPremultiplied) {
139-
this._getColor = getPremultipliedColor4b;
140-
} else {
141-
this._getColor = getColor4b;
142-
}
143-
}
144-
145114
/**
146115
* Update this silhouette with the bitmapData for a skin.
147-
* @param {*} bitmapData An image, canvas or other element that the skin
116+
* @param {ImageData|HTMLCanvasElement|HTMLImageElement} bitmapData An image, canvas or other element that the skin
117+
* @param {boolean} isPremultiplied True if the source bitmap data comes premultiplied (e.g. from readPixels).
148118
* rendering can be queried from.
149119
*/
150-
update (bitmapData) {
120+
update (bitmapData, isPremultiplied = false) {
151121
let imageData;
152122
if (bitmapData instanceof ImageData) {
153123
// If handed ImageData directly, use it directly.
@@ -170,6 +140,12 @@ class Silhouette {
170140
imageData = ctx.getImageData(0, 0, width, height);
171141
}
172142

143+
if (isPremultiplied) {
144+
this._getColor = getPremultipliedColor4b;
145+
} else {
146+
this._getColor = getColor4b;
147+
}
148+
173149
this._colorData = imageData.data;
174150
// delete our custom overriden "uninitalized" color functions
175151
// let the prototype work for itself

src/Skin.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,6 @@ class Skin extends EventEmitter {
7979
return false;
8080
}
8181

82-
/**
83-
* @returns {boolean} true if alpha is premultiplied, false otherwise
84-
*/
85-
get hasPremultipliedAlpha () {
86-
return false;
87-
}
88-
8982
/**
9083
* @return {int} the unique ID for this Skin.
9184
*/

src/shaders/sprite.frag

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,16 @@ uniform sampler2D u_skin;
4343

4444
varying vec2 v_texCoord;
4545

46+
// Add this to divisors to prevent division by 0, which results in NaNs propagating through calculations.
47+
// Smaller values can cause problems on some mobile devices.
48+
const float epsilon = 1e-3;
49+
4650
#if !defined(DRAW_MODE_silhouette) && (defined(ENABLE_color))
4751
// Branchless color conversions based on code from:
4852
// http://www.chilliant.com/rgb2hsv.html by Ian Taylor
4953
// Based in part on work by Sam Hocevar and Emil Persson
5054
// See also: https://en.wikipedia.org/wiki/HSL_and_HSV#Formal_derivation
5155

52-
// Smaller values can cause problems on some mobile devices
53-
const float epsilon = 1e-3;
5456

5557
// Convert an RGB color to Hue, Saturation, and Value.
5658
// All components of input and output are expected to be in the [0,1] range.
@@ -156,7 +158,7 @@ void main()
156158
#if defined(ENABLE_color) || defined(ENABLE_brightness)
157159
// Divide premultiplied alpha values for proper color processing
158160
// Add epsilon to avoid dividing by 0 for fully transparent pixels
159-
gl_FragColor.rgb /= gl_FragColor.a + epsilon;
161+
gl_FragColor.rgb = clamp(gl_FragColor.rgb / (gl_FragColor.a + epsilon), 0.0, 1.0);
160162

161163
#ifdef ENABLE_color
162164
{
@@ -209,9 +211,7 @@ void main()
209211
#endif // DRAW_MODE_silhouette
210212

211213
#else // DRAW_MODE_lineSample
212-
// Pen skins use premultiplied alpha, but u_lineColor is not premultiplied, so multiply it here
213-
vec4 premulColor = vec4(u_lineColor.rgb * u_lineColor.a, u_lineColor.a);
214-
gl_FragColor = premulColor * clamp(
214+
gl_FragColor = u_lineColor * clamp(
215215
// Scale the capScale a little to have an aliased region.
216216
(u_capScale + u_aliasAmount -
217217
u_capScale * 2.0 * distance(v_texCoord, vec2(0.5, 0.5))

0 commit comments

Comments
 (0)