1 /* libmypaint - The MyPaint Brush Library
2  * Copyright (C) 2007-2014 Martin Renold <martinxyz@gmx.ch> et. al
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include "config.h"
18 
19 #include <stdlib.h>
20 #include <assert.h>
21 #include <math.h>
22 #include "fastapprox/fastpow.h"
23 
24 #include "brushmodes.h"
25 #include "helpers.h"
26 
27 // parameters to those methods:
28 //
29 // rgba: A pointer to 16bit rgba data with premultiplied alpha.
30 //       The range of each components is limited from 0 to 2^15.
31 //
32 // mask: Contains the dab shape, that is, the intensity of the dab at
33 //       each pixel. Usually rendering is done for one tile at a
34 //       time. The mask is LRE encoded to jump quickly over regions
35 //       that are not affected by the dab.
36 //
37 // opacity: overall strength of the blending mode. Has the same
38 //          influence on the dab as the values inside the mask.
39 
40 
41 // We are manipulating pixels with premultiplied alpha directly.
42 // This is an "over" operation (opa = topAlpha).
43 // In the formula below, topColor is assumed to be premultiplied.
44 //
45 //               opa_a      <   opa_b      >
46 // resultAlpha = topAlpha + (1.0 - topAlpha) * bottomAlpha
47 // resultColor = topColor + (1.0 - topAlpha) * bottomColor
48 //
49 
draw_dab_pixels_BlendMode_Normal(uint16_t * mask,uint16_t * rgba,uint16_t color_r,uint16_t color_g,uint16_t color_b,uint16_t opacity)50 void draw_dab_pixels_BlendMode_Normal (uint16_t * mask,
51                                        uint16_t * rgba,
52                                        uint16_t color_r,
53                                        uint16_t color_g,
54                                        uint16_t color_b,
55                                        uint16_t opacity) {
56 
57   while (1) {
58     for (; mask[0]; mask++, rgba+=4) {
59       uint32_t opa_a = mask[0]*(uint32_t)opacity/(1<<15); // topAlpha
60       uint32_t opa_b = (1<<15)-opa_a; // bottomAlpha
61       rgba[3] = opa_a + opa_b * rgba[3] / (1<<15);
62       rgba[0] = (opa_a*color_r + opa_b*rgba[0])/(1<<15);
63       rgba[1] = (opa_a*color_g + opa_b*rgba[1])/(1<<15);
64       rgba[2] = (opa_a*color_b + opa_b*rgba[2])/(1<<15);
65 
66     }
67     if (!mask[1]) break;
68     rgba += mask[1];
69     mask += 2;
70   }
71 };
72 
draw_dab_pixels_BlendMode_Normal_Paint(uint16_t * mask,uint16_t * rgba,uint16_t color_r,uint16_t color_g,uint16_t color_b,uint16_t opacity)73 void draw_dab_pixels_BlendMode_Normal_Paint (uint16_t * mask,
74                                        uint16_t * rgba,
75                                        uint16_t color_r,
76                                        uint16_t color_g,
77                                        uint16_t color_b,
78                                        uint16_t opacity) {
79 
80   // convert top to spectral.  Already straight color
81   float spectral_a[10] = {0};
82   rgb_to_spectral((float)color_r / (1 << 15), (float)color_g / (1 << 15), (float)color_b / (1 << 15), spectral_a);
83   // pigment-mode does not like very low opacity, probably due to rounding
84   // errors with int->float->int round-trip.  Once we convert to pure
85   // float engine this might be fixed.  For now enforce a minimum opacity:
86   opacity = MAX(opacity, 150);
87 
88   while (1) {
89     for (; mask[0]; mask++, rgba+=4) {
90       uint32_t opa_a = mask[0]*(uint32_t)opacity/(1<<15); // topAlpha
91       uint32_t opa_b = (1<<15)-opa_a; // bottomAlpha
92       // optimization- if background has 0 alpha we can just do normal additive
93       // blending since there is nothing to mix with.
94       if (rgba[3] <= 0) {
95         rgba[3] = opa_a + opa_b * rgba[3] / (1<<15);
96         rgba[0] = (opa_a*color_r + opa_b*rgba[0])/(1<<15);
97         rgba[1] = (opa_a*color_g + opa_b*rgba[1])/(1<<15);
98         rgba[2] = (opa_a*color_b + opa_b*rgba[2])/(1<<15);
99         continue;
100       }
101       //alpha-weighted ratio for WGM (sums to 1.0)
102       float fac_a = (float)opa_a / (opa_a + opa_b * rgba[3] / (1<<15));
103       float fac_b = 1.0 - fac_a;
104 
105       //convert bottom to spectral.  Un-premult alpha to obtain reflectance
106       //color noise is not a problem since low alpha also implies low weight
107       float spectral_b[10] = {0};
108 
109       rgb_to_spectral((float)rgba[0] / rgba[3], (float)rgba[1] / rgba[3], (float)rgba[2] / rgba[3], spectral_b);
110 
111       // mix to the two spectral reflectances using WGM
112       float spectral_result[10] = {0};
113       for (int i=0; i<10; i++) {
114         spectral_result[i] = fastpow(spectral_a[i], fac_a) * fastpow(spectral_b[i], fac_b);
115       }
116 
117       // convert back to RGB and premultiply alpha
118       float rgb_result[3] = {0};
119       spectral_to_rgb(spectral_result, rgb_result);
120       rgba[3] = opa_a + opa_b * rgba[3] / (1<<15);
121 
122       for (int i=0; i<3; i++) {
123         rgba[i] =(rgb_result[i] * rgba[3]) + 0.5;
124       }
125     }
126     if (!mask[1]) break;
127     rgba += mask[1];
128     mask += 2;
129   }
130 };
131 
132 //Posterize.  Basically exactly like GIMP's posterize
133 //reduces colors by adjustable amount (posterize_num).
134 //posterize the canvas, then blend that via opacity
135 //does not affect alpha
136 
draw_dab_pixels_BlendMode_Posterize(uint16_t * mask,uint16_t * rgba,uint16_t opacity,uint16_t posterize_num)137 void draw_dab_pixels_BlendMode_Posterize (uint16_t * mask,
138                                        uint16_t * rgba,
139                                        uint16_t opacity,
140                                        uint16_t posterize_num) {
141 
142   while (1) {
143     for (; mask[0]; mask++, rgba+=4) {
144 
145       float r = (float)rgba[0] / (1<<15);
146       float g = (float)rgba[1] / (1<<15);
147       float b = (float)rgba[2] / (1<<15);
148 
149       uint32_t post_r = (1<<15) * ROUND(r * posterize_num) / posterize_num;
150       uint32_t post_g = (1<<15) * ROUND(g * posterize_num) / posterize_num;
151       uint32_t post_b = (1<<15) * ROUND(b * posterize_num) / posterize_num;
152 
153       uint32_t opa_a = mask[0]*(uint32_t)opacity/(1<<15); // topAlpha
154       uint32_t opa_b = (1<<15)-opa_a; // bottomAlpha
155       rgba[0] = (opa_a*post_r + opa_b*rgba[0])/(1<<15);
156       rgba[1] = (opa_a*post_g + opa_b*rgba[1])/(1<<15);
157       rgba[2] = (opa_a*post_b + opa_b*rgba[2])/(1<<15);
158 
159     }
160     if (!mask[1]) break;
161     rgba += mask[1];
162     mask += 2;
163   }
164 };
165 
166 // Colorize: apply the source hue and saturation, retaining the target
167 // brightness. Same thing as in the PDF spec addendum, and upcoming SVG
168 // compositing drafts. Colorize should be used at either 1.0 or 0.0, values in
169 // between probably aren't very useful. This blend mode retains the target
170 // alpha, and any pure whites and blacks in the target layer.
171 
172 #define MAX3(a, b, c) ((a)>(b)?MAX((a),(c)):MAX((b),(c)))
173 #define MIN3(a, b, c) ((a)<(b)?MIN((a),(c)):MIN((b),(c)))
174 
175 // For consistency, these are the values used by MyPaint's Color and
176 // Luminosity layer blend modes, which in turn are defined by
177 // http://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html.
178 // Same as ITU Rec. BT.601 (SDTV) rounded to 2 decimal places.
179 
180 static const float LUMA_RED_COEFF   = 0.2126 * (1<<15);
181 static const float LUMA_GREEN_COEFF = 0.7152 * (1<<15);
182 static const float LUMA_BLUE_COEFF  = 0.0722 * (1<<15);
183 
184 // See also http://en.wikipedia.org/wiki/YCbCr
185 
186 
187 /* Returns the sRGB luminance of an RGB triple, expressed as scaled ints. */
188 
189 #define LUMA(r,g,b) \
190    ((r)*LUMA_RED_COEFF + (g)*LUMA_GREEN_COEFF + (b)*LUMA_BLUE_COEFF)
191 
192 
193 /*
194  * Sets the output RGB triple's luminance to that of the input, retaining its
195  * colour. Inputs and outputs are scaled ints having factor 2**-15, and must
196  * not store premultiplied alpha.
197  */
198 
199 inline static void
set_rgb16_lum_from_rgb16(const uint16_t topr,const uint16_t topg,const uint16_t topb,uint16_t * botr,uint16_t * botg,uint16_t * botb)200 set_rgb16_lum_from_rgb16(const uint16_t topr,
201                          const uint16_t topg,
202                          const uint16_t topb,
203                          uint16_t *botr,
204                          uint16_t *botg,
205                          uint16_t *botb)
206 {
207     // Spec: SetLum()
208     // Colours potentially can go out of band to both sides, hence the
209     // temporary representation inflation.
210     const uint16_t botlum = LUMA(*botr, *botg, *botb) / (1<<15);
211     const uint16_t toplum = LUMA(topr, topg, topb) / (1<<15);
212     const int16_t diff = botlum - toplum;
213     int32_t r = topr + diff;
214     int32_t g = topg + diff;
215     int32_t b = topb + diff;
216 
217     // Spec: ClipColor()
218     // Clip out of band values
219     int32_t lum = LUMA(r, g, b) / (1<<15);
220     int32_t cmin = MIN3(r, g, b);
221     int32_t cmax = MAX3(r, g, b);
222     if (cmin < 0) {
223         r = lum + (((r - lum) * lum) / (lum - cmin));
224         g = lum + (((g - lum) * lum) / (lum - cmin));
225         b = lum + (((b - lum) * lum) / (lum - cmin));
226     }
227     if (cmax > (1<<15)) {
228         r = lum + (((r - lum) * ((1<<15)-lum)) / (cmax - lum));
229         g = lum + (((g - lum) * ((1<<15)-lum)) / (cmax - lum));
230         b = lum + (((b - lum) * ((1<<15)-lum)) / (cmax - lum));
231     }
232 #ifdef HEAVY_DEBUG
233     assert((0 <= r) && (r <= (1<<15)));
234     assert((0 <= g) && (g <= (1<<15)));
235     assert((0 <= b) && (b <= (1<<15)));
236 #endif
237 
238     *botr = r;
239     *botg = g;
240     *botb = b;
241 }
242 
243 
244 // The method is an implementation of that described in the official Adobe "PDF
245 // Blend Modes: Addendum" document, dated January 23, 2006; specifically it's
246 // the "Color" nonseparable blend mode. We do however use different
247 // coefficients for the Luma value.
248 
249 void
draw_dab_pixels_BlendMode_Color(uint16_t * mask,uint16_t * rgba,uint16_t color_r,uint16_t color_g,uint16_t color_b,uint16_t opacity)250 draw_dab_pixels_BlendMode_Color (uint16_t *mask,
251                                  uint16_t *rgba, // b=bottom, premult
252                                  uint16_t color_r,  // }
253                                  uint16_t color_g,  // }-- a=top, !premult
254                                  uint16_t color_b,  // }
255                                  uint16_t opacity)
256 {
257   while (1) {
258     for (; mask[0]; mask++, rgba+=4) {
259       // De-premult
260       uint16_t r, g, b;
261       const uint16_t a = rgba[3];
262       r = g = b = 0;
263       if (rgba[3] != 0) {
264         r = ((1<<15)*((uint32_t)rgba[0])) / a;
265         g = ((1<<15)*((uint32_t)rgba[1])) / a;
266         b = ((1<<15)*((uint32_t)rgba[2])) / a;
267       }
268 
269       // Apply luminance
270       set_rgb16_lum_from_rgb16(color_r, color_g, color_b, &r, &g, &b);
271 
272       // Re-premult
273       r = ((uint32_t) r) * a / (1<<15);
274       g = ((uint32_t) g) * a / (1<<15);
275       b = ((uint32_t) b) * a / (1<<15);
276 
277       // And combine as normal.
278       uint32_t opa_a = mask[0] * opacity / (1<<15); // topAlpha
279       uint32_t opa_b = (1<<15) - opa_a; // bottomAlpha
280       rgba[0] = (opa_a*r + opa_b*rgba[0])/(1<<15);
281       rgba[1] = (opa_a*g + opa_b*rgba[1])/(1<<15);
282       rgba[2] = (opa_a*b + opa_b*rgba[2])/(1<<15);
283     }
284     if (!mask[1]) break;
285     rgba += mask[1];
286     mask += 2;
287   }
288 };
289 
290 // This blend mode is used for smudging and erasing.  Smudging
291 // allows to "drag" around transparency as if it was a color.  When
292 // smuding over a region that is 60% opaque the result will stay 60%
293 // opaque (color_a=0.6).  For normal erasing color_a is set to 0.0
294 // and color_r/g/b will be ignored. This function can also do normal
295 // blending (color_a=1.0).
296 //
draw_dab_pixels_BlendMode_Normal_and_Eraser(uint16_t * mask,uint16_t * rgba,uint16_t color_r,uint16_t color_g,uint16_t color_b,uint16_t color_a,uint16_t opacity)297 void draw_dab_pixels_BlendMode_Normal_and_Eraser (uint16_t * mask,
298                                                   uint16_t * rgba,
299                                                   uint16_t color_r,
300                                                   uint16_t color_g,
301                                                   uint16_t color_b,
302                                                   uint16_t color_a,
303                                                   uint16_t opacity) {
304 
305   while (1) {
306     for (; mask[0]; mask++, rgba+=4) {
307       uint32_t opa_a = mask[0]*(uint32_t)opacity/(1<<15); // topAlpha
308       uint32_t opa_b = (1<<15)-opa_a; // bottomAlpha
309       opa_a = opa_a * color_a / (1<<15);
310       rgba[3] = opa_a + opa_b * rgba[3] / (1<<15);
311       rgba[0] = (opa_a*color_r + opa_b*rgba[0])/(1<<15);
312       rgba[1] = (opa_a*color_g + opa_b*rgba[1])/(1<<15);
313       rgba[2] = (opa_a*color_b + opa_b*rgba[2])/(1<<15);
314 
315     }
316     if (!mask[1]) break;
317     rgba += mask[1];
318     mask += 2;
319   }
320 };
321 
322 
323 // Fast sigmoid-like function with constant offsets, used to get a
324 // fairly smooth transition between additive and spectral blending.
spectral_blend_factor(float x)325 float spectral_blend_factor(float x) {
326   const float ver_fac = 1.65; // vertical compression factor
327   const float hor_fac = 8.0f; // horizontal compression factor
328   const float hor_offs = 3.0f; // horizontal offset (slightly left of center)
329   const float b = x * hor_fac - hor_offs;
330   return 0.5 + b / (1 + fabsf(b) * ver_fac);
331 }
332 
draw_dab_pixels_BlendMode_Normal_and_Eraser_Paint(uint16_t * mask,uint16_t * rgba,uint16_t color_r,uint16_t color_g,uint16_t color_b,uint16_t color_a,uint16_t opacity)333 void draw_dab_pixels_BlendMode_Normal_and_Eraser_Paint (uint16_t * mask,
334                                                   uint16_t * rgba,
335                                                   uint16_t color_r,
336                                                   uint16_t color_g,
337                                                   uint16_t color_b,
338                                                   uint16_t color_a,
339                                                   uint16_t opacity) {
340 
341   // Convert input color to spectral, it is not premultiplied
342   float spectral_a[10] = {0};
343   rgb_to_spectral(
344     (float)color_r / (1<<15),
345     (float)color_g / (1<<15),
346     (float)color_b / (1<<15),
347     spectral_a
348     );
349 
350   while (1) {
351     for (; mask[0]; mask++, rgba+=4) {
352       const uint32_t opa_a = mask[0]*(uint32_t)opacity/(1<<15); // topAlpha
353       const uint32_t opa_b = (1<<15)-opa_a; // bottomAlpha
354       const uint32_t opa_a2 = opa_a * color_a / (1<<15); // erase-adjusted alpha
355       const uint32_t opa_out = opa_a2 + opa_b * rgba[3] / (1<<15);
356 
357       uint32_t rgb[3] = {0, 0, 0};
358 
359       // Spectral blending does not handle low transparency well, so we try to patch that
360       // up by using mostly additive mixing for lower canvas alphas, gradually moving to
361       // full spectral blending at mostly opaque pixels.
362       //
363       // This does not solve all problems with low opacity, and it creates some new ones
364       // when mixing bright low-opacity colors into dark low-opacity colors, but the new
365       // artifacts are not as tough to deal with as the old dark-fringe artifacts.
366       float spectral_factor = CLAMP(spectral_blend_factor((float)rgba[3] / (1<<15)), 0.0f, 1.0f);
367       float additive_factor = 1.0 - spectral_factor;
368 
369       if (additive_factor) {
370         rgb[0] = (opa_a2 * color_r + opa_b * rgba[0]) / (1 << 15);
371         rgb[1] = (opa_a2 * color_g + opa_b * rgba[1]) / (1 << 15);
372         rgb[2] = (opa_a2 * color_b + opa_b * rgba[2]) / (1 << 15);
373       }
374 
375       if (spectral_factor && rgba[3] != 0) {
376         // Convert straightened tile pixel color to a spectral
377         float spectral_b[10] = {0};
378         rgb_to_spectral(
379           (float)rgba[0] / rgba[3],
380           (float)rgba[1] / rgba[3],
381           (float)rgba[2] / rgba[3],
382           spectral_b
383           );
384 
385         float fac_a = (float)opa_a / (opa_a + opa_b * rgba[3] / (1 << 15));
386         fac_a *= (float)color_a / (1 << 15);
387         float fac_b = 1.0 - fac_a;
388 
389         // Mix input and tile pixel colors using WGM
390         float spectral_result[10] = {0};
391         for (int i = 0; i < 10; i++) {
392           spectral_result[i] =
393               fastpow(spectral_a[i], fac_a) * fastpow(spectral_b[i], fac_b);
394         }
395 
396         // Convert back to RGB
397         float rgb_result[3] = {0};
398         spectral_to_rgb(spectral_result, rgb_result);
399 
400         for (int i = 0; i < 3; i++) {
401           rgb[i] = (additive_factor * rgb[i]) + (spectral_factor * rgb_result[i] * opa_out);
402         }
403       }
404 
405       rgba[3] = opa_out;
406       for (int i = 0; i < 3; i++) {
407         rgba[i] = rgb[i];
408       }
409     }
410     if (!mask[1]) break;
411     rgba += mask[1];
412     mask += 2;
413   }
414 };
415 
416 // This is BlendMode_Normal with locked alpha channel.
417 //
draw_dab_pixels_BlendMode_LockAlpha(uint16_t * mask,uint16_t * rgba,uint16_t color_r,uint16_t color_g,uint16_t color_b,uint16_t opacity)418 void draw_dab_pixels_BlendMode_LockAlpha (uint16_t * mask,
419                                           uint16_t * rgba,
420                                           uint16_t color_r,
421                                           uint16_t color_g,
422                                           uint16_t color_b,
423                                           uint16_t opacity) {
424 
425   while (1) {
426     for (; mask[0]; mask++, rgba+=4) {
427       uint32_t opa_a = mask[0]*(uint32_t)opacity/(1<<15); // topAlpha
428       uint32_t opa_b = (1<<15)-opa_a; // bottomAlpha
429 
430       opa_a *= rgba[3];
431       opa_a /= (1<<15);
432 
433       rgba[0] = (opa_a*color_r + opa_b*rgba[0])/(1<<15);
434       rgba[1] = (opa_a*color_g + opa_b*rgba[1])/(1<<15);
435       rgba[2] = (opa_a*color_b + opa_b*rgba[2])/(1<<15);
436     }
437     if (!mask[1]) break;
438     rgba += mask[1];
439     mask += 2;
440   }
441 };
442 
draw_dab_pixels_BlendMode_LockAlpha_Paint(uint16_t * mask,uint16_t * rgba,uint16_t color_r,uint16_t color_g,uint16_t color_b,uint16_t opacity)443 void draw_dab_pixels_BlendMode_LockAlpha_Paint (uint16_t * mask,
444                                           uint16_t * rgba,
445                                           uint16_t color_r,
446                                           uint16_t color_g,
447                                           uint16_t color_b,
448                                           uint16_t opacity) {
449 
450   // convert top to spectral.  Already straight color
451   float spectral_a[10] = {0};
452   rgb_to_spectral((float)color_r / (1<<15), (float)color_g / (1<<15), (float)color_b / (1<<15), spectral_a);
453   opacity = MAX(opacity, 150);
454 
455   while (1) {
456     for (; mask[0]; mask++, rgba+=4) {
457       uint32_t opa_a = mask[0]*(uint32_t)opacity/(1<<15); // topAlpha
458       uint32_t opa_b = (1<<15)-opa_a; // bottomAlpha
459       opa_a *= rgba[3];
460       opa_a /= (1<<15);
461       if (rgba[3] <= 0) {
462         rgba[0] = (opa_a*color_r + opa_b*rgba[0])/(1<<15);
463         rgba[1] = (opa_a*color_g + opa_b*rgba[1])/(1<<15);
464         rgba[2] = (opa_a*color_b + opa_b*rgba[2])/(1<<15);
465         continue;
466       }
467       float fac_a = (float)opa_a / (opa_a + opa_b * rgba[3] / (1<<15));
468       float fac_b = 1.0 - fac_a;
469       float spectral_b[10] = {0};
470       rgb_to_spectral((float)rgba[0] / rgba[3], (float)rgba[1] / rgba[3], (float)rgba[2] / rgba[3], spectral_b);
471 
472       // mix to the two spectral colors using WGM
473       float spectral_result[10] = {0};
474       for (int i=0; i<10; i++) {
475         spectral_result[i] = fastpow(spectral_a[i], fac_a) * fastpow(spectral_b[i], fac_b);
476       }
477       // convert back to RGB
478       float rgb_result[3] = {0};
479       spectral_to_rgb(spectral_result, rgb_result);
480       rgba[3] = opa_a + opa_b * rgba[3] / (1<<15);
481 
482       for (int i=0; i<3; i++) {
483         rgba[i] =(rgb_result[i] * rgba[3]) + 0.5;
484       }
485     }
486     if (!mask[1]) break;
487     rgba += mask[1];
488     mask += 2;
489   }
490 };
491 
get_color_pixels_legacy(uint16_t * mask,uint16_t * rgba,float * sum_weight,float * sum_r,float * sum_g,float * sum_b,float * sum_a)492 void get_color_pixels_legacy (
493     uint16_t * mask,
494     uint16_t * rgba,
495     float * sum_weight,
496     float * sum_r,
497     float * sum_g,
498     float * sum_b,
499     float * sum_a
500     )
501 {
502     // The sum of a 64x64 tile fits into a 32 bit integer, but the sum
503     // of an arbitrary number of tiles may not fit. We assume that we
504     // are processing a single tile at a time, so we can use integers.
505     // But for the result we need floats.
506     uint32_t weight = 0;
507     uint32_t r = 0;
508     uint32_t g = 0;
509     uint32_t b = 0;
510     uint32_t a = 0;
511 
512     while (1) {
513         for (; mask[0]; mask++, rgba+=4) {
514             uint32_t opa = mask[0];
515             weight += opa;
516             r      += opa*rgba[0]/(1<<15);
517             g      += opa*rgba[1]/(1<<15);
518             b      += opa*rgba[2]/(1<<15);
519             a      += opa*rgba[3]/(1<<15);
520 
521         }
522         if (!mask[1]) break;
523         rgba += mask[1];
524         mask += 2;
525     }
526 
527     // convert integer to float outside the performance critical loop
528     *sum_weight += weight;
529     *sum_r += r;
530     *sum_g += g;
531     *sum_b += b;
532     *sum_a += a;
533 };
534 
535 // Sum up the color/alpha components inside the masked region.
536 // Called by get_color().
537 //
538 // The sample interval guarantees that every n pixels are sampled in
539 // the provided mask segment.
540 // Setting the interval to 1 means that all pixels will be sampled,
541 // but note that this may result in large rounding errors.
542 //
543 // The sample rate is the probability of any pixel being sampled,
544 // with the exception of the guaranteed ones. Range: 0.0..1.0.
545 // The random sample rate can be set to 0, in which case no random
546 // sampling will occur.
get_color_pixels_accumulate(uint16_t * mask,uint16_t * rgba,float * sum_weight,float * sum_r,float * sum_g,float * sum_b,float * sum_a,float paint,uint16_t sample_interval,float random_sample_rate)547 void get_color_pixels_accumulate (uint16_t * mask,
548                                   uint16_t * rgba,
549                                   float * sum_weight,
550                                   float * sum_r,
551                                   float * sum_g,
552                                   float * sum_b,
553                                   float * sum_a,
554                                   float paint,
555                                   uint16_t sample_interval,
556                                   float random_sample_rate
557                                   ) {
558   // Fall back to legacy sampling if using static 0 paint setting
559   // Indicated by passing a negative paint factor (normal range 0..1)
560   if (paint < 0.0) {
561       get_color_pixels_legacy(mask, rgba, sum_weight, sum_r, sum_g, sum_b, sum_a);
562       return;
563   }
564 
565   // Sample the canvas as additive and subtractive
566   // According to paint parameter
567   // Average the results normally
568   // Only sample a partially random subset of pixels
569 
570   float avg_spectral[10] = {0};
571   float avg_rgb[3] = {*sum_r, *sum_g, *sum_b};
572   if (paint > 0.0f) {
573     rgb_to_spectral(*sum_r, *sum_g, *sum_b, avg_spectral);
574   }
575 
576   // Rolling counter determining which pixels to sample
577   // This sampling _is_ biased (but hopefully not too bad).
578   // Ideally, the selection of pixels to be sampled should
579   // be determined before this function is called.
580   uint16_t interval_counter = 0;
581   const int random_sample_threshold = (int)(random_sample_rate * RAND_MAX);
582 
583   while (1) {
584     for (; mask[0]; mask++, rgba+=4) {
585       // Sample every n pixels, and a percentage of the rest.
586       // At least one pixel (the first) will always be sampled.
587       if (interval_counter == 0 || rand() < random_sample_threshold) {
588 
589         float a = (float)mask[0] * rgba[3] / (1 << 30);
590         float alpha_sums = a + *sum_a;
591         *sum_weight += (float)mask[0] / (1 << 15);
592         float fac_a, fac_b;
593         fac_a = fac_b = 1.0f;
594         if (alpha_sums > 0.0f) {
595           fac_a = a / alpha_sums;
596           fac_b = 1.0 - fac_a;
597         }
598         if (paint > 0.0f && rgba[3] > 0) {
599           float spectral[10] = {0};
600           rgb_to_spectral((float)rgba[0] / rgba[3], (float)rgba[1] / rgba[3], (float)rgba[2] / rgba[3], spectral);
601 
602           for (int i = 0; i < 10; i++) {
603             avg_spectral[i] = fastpow(spectral[i], fac_a) * fastpow(avg_spectral[i], fac_b);
604           }
605         }
606         if (paint < 1.0f && rgba[3] > 0) {
607           for (int i = 0; i < 3; i++) {
608             avg_rgb[i] = (float)rgba[i] * fac_a / rgba[3] + (float)avg_rgb[i] * fac_b;
609           }
610         }
611         *sum_a += a;
612       }
613       interval_counter = (interval_counter + 1) % sample_interval;
614     }
615     if (!mask[1]) break;
616     rgba += mask[1];
617     mask += 2;
618   }
619   // Convert the spectral average to rgb and write the result
620   // back weighted with the rgb average.
621   float spec_rgb[3] = {0};
622   spectral_to_rgb(avg_spectral, spec_rgb);
623 
624   *sum_r = spec_rgb[0] * paint + (1.0 - paint) * avg_rgb[0];
625   *sum_g = spec_rgb[1] * paint + (1.0 - paint) * avg_rgb[1];
626   *sum_b = spec_rgb[2] * paint + (1.0 - paint) * avg_rgb[2];
627 };
628