1818
1919#include <stdint.h>
2020#include <assert.h>
21+ #include <math.h>
22+ #include "fastapprox/fastpow.h"
2123
2224#include "helpers.h"
2325
4345// resultAlpha = topAlpha + (1.0 - topAlpha) * bottomAlpha
4446// resultColor = topColor + (1.0 - topAlpha) * bottomColor
4547//
48+
4649void draw_dab_pixels_BlendMode_Normal (uint16_t * mask ,
4750 uint16_t * rgba ,
4851 uint16_t color_r ,
@@ -66,7 +69,89 @@ void draw_dab_pixels_BlendMode_Normal (uint16_t * mask,
6669 }
6770};
6871
72+ void draw_dab_pixels_BlendMode_Normal_Paint (uint16_t * mask ,
73+ uint16_t * rgba ,
74+ uint16_t color_r ,
75+ uint16_t color_g ,
76+ uint16_t color_b ,
77+ uint16_t opacity ) {
78+
79+ while (1 ) {
80+ for (; mask [0 ]; mask ++ , rgba += 4 ) {
81+ uint32_t opa_a = mask [0 ]* (uint32_t )opacity /(1 <<15 ); // topAlpha
82+ uint32_t opa_b = (1 <<15 )- opa_a ; // bottomAlpha
83+
84+ //alpha-weighted ratio for WGM (sums to 1.0)
85+ float fac_a = (float )opa_a / (opa_a + opa_b * rgba [3 ] / (1 <<15 ));
86+ //fac_a *= fac_a;
87+ float fac_b = 1.0 - fac_a ;
88+
89+ //convert bottom to spectral. Un-premult alpha to obtain reflectance
90+ //color noise is not a problem since low alpha also implies low weight
91+ float spectral_b [10 ] = {0 };
92+ if (rgba [3 ] > 0 ) {
93+ rgb_to_spectral ((float )rgba [0 ] / rgba [3 ], (float )rgba [1 ] / rgba [3 ], (float )rgba [2 ] / rgba [3 ], spectral_b );
94+ } else {
95+ rgb_to_spectral ((float )rgba [0 ]/ (1 <<15 ), (float )rgba [1 ]/ (1 <<15 ), (float )rgba [2 ]/ (1 <<15 ), spectral_b );
96+ }
97+ // convert top to spectral. Already straight color
98+ float spectral_a [10 ] = {0 };
99+ rgb_to_spectral ((float )color_r / (1 <<15 ), (float )color_g / (1 <<15 ), (float )color_b / (1 <<15 ), spectral_a );
100+
101+ // mix to the two spectral reflectances using WGM
102+ float spectral_result [10 ] = {0 };
103+ for (int i = 0 ; i < 10 ; i ++ ) {
104+ spectral_result [i ] = fastpow (spectral_a [i ], fac_a ) * fastpow (spectral_b [i ], fac_b );
105+ }
106+
107+ // convert back to RGB and premultiply alpha
108+ float rgb_result [3 ] = {0 };
109+ spectral_to_rgb (spectral_result , rgb_result );
110+ rgba [3 ] = opa_a + opa_b * rgba [3 ] / (1 <<15 );
111+
112+ for (int i = 0 ; i < 3 ; i ++ ) {
113+ rgba [i ] = (rgb_result [i ] * rgba [3 ]);
114+ }
115+ }
116+ if (!mask [1 ]) break ;
117+ rgba += mask [1 ];
118+ mask += 2 ;
119+ }
120+ };
121+
122+ //Posterize. Basically exactly like GIMP's posterize
123+ //reduces colors by adjustable amount (posterize_num).
124+ //posterize the canvas, then blend that via opacity
125+ //does not affect alpha
126+
127+ void draw_dab_pixels_BlendMode_Posterize (uint16_t * mask ,
128+ uint16_t * rgba ,
129+ uint16_t opacity ,
130+ uint16_t posterize_num ) {
69131
132+ while (1 ) {
133+ for (; mask [0 ]; mask ++ , rgba += 4 ) {
134+
135+ float r = (float )rgba [0 ] / (1 <<15 );
136+ float g = (float )rgba [1 ] / (1 <<15 );
137+ float b = (float )rgba [2 ] / (1 <<15 );
138+
139+ uint32_t post_r = (1 <<15 ) * ROUND (r * posterize_num ) / posterize_num ;
140+ uint32_t post_g = (1 <<15 ) * ROUND (g * posterize_num ) / posterize_num ;
141+ uint32_t post_b = (1 <<15 ) * ROUND (b * posterize_num ) / posterize_num ;
142+
143+ uint32_t opa_a = mask [0 ]* (uint32_t )opacity /(1 <<15 ); // topAlpha
144+ uint32_t opa_b = (1 <<15 )- opa_a ; // bottomAlpha
145+ rgba [0 ] = (opa_a * post_r + opa_b * rgba [0 ])/(1 <<15 );
146+ rgba [1 ] = (opa_a * post_g + opa_b * rgba [1 ])/(1 <<15 );
147+ rgba [2 ] = (opa_a * post_b + opa_b * rgba [2 ])/(1 <<15 );
148+
149+ }
150+ if (!mask [1 ]) break ;
151+ rgba += mask [1 ];
152+ mask += 2 ;
153+ }
154+ };
70155
71156// Colorize: apply the source hue and saturation, retaining the target
72157// brightness. Same thing as in the PDF spec addendum, and upcoming SVG
@@ -82,9 +167,9 @@ void draw_dab_pixels_BlendMode_Normal (uint16_t * mask,
82167// http://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html.
83168// Same as ITU Rec. BT.601 (SDTV) rounded to 2 decimal places.
84169
85- static const float LUMA_RED_COEFF = 0.3 * (1 <<15 );
86- static const float LUMA_GREEN_COEFF = 0.59 * (1 <<15 );
87- static const float LUMA_BLUE_COEFF = 0.11 * (1 <<15 );
170+ static const float LUMA_RED_COEFF = 0.2126 * (1 <<15 );
171+ static const float LUMA_GREEN_COEFF = 0.7152 * (1 <<15 );
172+ static const float LUMA_BLUE_COEFF = 0.0722 * (1 <<15 );
88173
89174// See also http://en.wikipedia.org/wiki/YCbCr
90175
@@ -224,6 +309,59 @@ void draw_dab_pixels_BlendMode_Normal_and_Eraser (uint16_t * mask,
224309 }
225310};
226311
312+ void draw_dab_pixels_BlendMode_Normal_and_Eraser_Paint (uint16_t * mask ,
313+ uint16_t * rgba ,
314+ uint16_t color_r ,
315+ uint16_t color_g ,
316+ uint16_t color_b ,
317+ uint16_t color_a ,
318+ uint16_t opacity ) {
319+
320+ while (1 ) {
321+ for (; mask [0 ]; mask ++ , rgba += 4 ) {
322+ uint32_t opa_a = mask [0 ]* (uint32_t )opacity /(1 <<15 ); // topAlpha
323+ uint32_t opa_b = (1 <<15 )- opa_a ; // bottomAlpha
324+
325+ float fac_a = (float )opa_a / (opa_a + opa_b * rgba [3 ] / (1 <<15 ));
326+ //fac_a *= fac_a;
327+ float fac_b = 1.0 - fac_a ;
328+ //fac_a *= (float)color_a / (1<<15);
329+ float spectral_b [10 ] = {0 };
330+ if (rgba [3 ] > 0 ) {
331+ rgb_to_spectral ((float )rgba [0 ] / rgba [3 ], (float )rgba [1 ] / rgba [3 ], (float )rgba [2 ] / rgba [3 ], spectral_b );
332+ } else {
333+ rgb_to_spectral ((float )rgba [0 ]/ (1 <<15 ), (float )rgba [1 ]/ (1 <<15 ), (float )rgba [2 ]/ (1 <<15 ), spectral_b );
334+ }
335+ // convert top to spectral. Already straight color
336+ float spectral_a [10 ] = {0 };
337+ rgb_to_spectral ((float )color_r / (1 <<15 ), (float )color_g / (1 <<15 ), (float )color_b / (1 <<15 ), spectral_a );
338+
339+ // mix to the two spectral colors using WGM
340+ float spectral_result [10 ] = {0 };
341+ for (int i = 0 ; i < 10 ; i ++ ) {
342+ spectral_result [i ] = fastpow (spectral_a [i ], fac_a ) * fastpow (spectral_b [i ], fac_b );
343+ }
344+ // convert back to RGB
345+ float rgb_result [3 ] = {0 };
346+ spectral_to_rgb (spectral_result , rgb_result );
347+
348+ // apply eraser
349+ opa_a = opa_a * color_a / (1 <<15 );
350+
351+ // calculate alpha normally
352+ rgba [3 ] = opa_a + opa_b * rgba [3 ] / (1 <<15 );
353+
354+ for (int i = 0 ; i < 3 ; i ++ ) {
355+ rgba [i ] = (rgb_result [i ] * rgba [3 ]);
356+ }
357+
358+ }
359+ if (!mask [1 ]) break ;
360+ rgba += mask [1 ];
361+ mask += 2 ;
362+ }
363+ };
364+
227365// This is BlendMode_Normal with locked alpha channel.
228366//
229367void draw_dab_pixels_BlendMode_LockAlpha (uint16_t * mask ,
@@ -251,6 +389,53 @@ void draw_dab_pixels_BlendMode_LockAlpha (uint16_t * mask,
251389 }
252390};
253391
392+ void draw_dab_pixels_BlendMode_LockAlpha_Paint (uint16_t * mask ,
393+ uint16_t * rgba ,
394+ uint16_t color_r ,
395+ uint16_t color_g ,
396+ uint16_t color_b ,
397+ uint16_t opacity ) {
398+
399+ while (1 ) {
400+ for (; mask [0 ]; mask ++ , rgba += 4 ) {
401+
402+ uint32_t opa_a = mask [0 ]* (uint32_t )opacity /(1 <<15 ); // topAlpha
403+ uint32_t opa_b = (1 <<15 )- opa_a ; // bottomAlpha
404+ opa_a *= rgba [3 ];
405+ opa_a /= (1 <<15 );
406+ float fac_a = (float )opa_a / (opa_a + opa_b * rgba [3 ] / (1 <<15 ));
407+ //fac_a *= fac_a;
408+ float fac_b = 1.0 - fac_a ;
409+ float spectral_b [10 ] = {0 };
410+ if (rgba [3 ] > 0 ) {
411+ rgb_to_spectral ((float )rgba [0 ] / rgba [3 ], (float )rgba [1 ] / rgba [3 ], (float )rgba [2 ] / rgba [3 ], spectral_b );
412+ } else {
413+ rgb_to_spectral ((float )rgba [0 ]/ (1 <<15 ), (float )rgba [1 ]/ (1 <<15 ), (float )rgba [2 ]/ (1 <<15 ), spectral_b );
414+ }
415+ // convert top to spectral. Already straight color
416+ float spectral_a [10 ] = {0 };
417+ rgb_to_spectral ((float )color_r / (1 <<15 ), (float )color_g / (1 <<15 ), (float )color_b / (1 <<15 ), spectral_a );
418+
419+ // mix to the two spectral colors using WGM
420+ float spectral_result [10 ] = {0 };
421+ for (int i = 0 ; i < 10 ; i ++ ) {
422+ spectral_result [i ] = fastpow (spectral_a [i ], fac_a ) * fastpow (spectral_b [i ], fac_b );
423+ }
424+ // convert back to RGB
425+ float rgb_result [3 ] = {0 };
426+ spectral_to_rgb (spectral_result , rgb_result );
427+ rgba [3 ] = opa_a + opa_b * rgba [3 ] / (1 <<15 );
428+
429+ for (int i = 0 ; i < 3 ; i ++ ) {
430+ rgba [i ] = (rgb_result [i ] * rgba [3 ]);
431+ }
432+ }
433+ if (!mask [1 ]) break ;
434+ rgba += mask [1 ];
435+ mask += 2 ;
436+ }
437+ };
438+
254439
255440// Sum up the color/alpha components inside the masked region.
256441// Called by get_color().
@@ -299,3 +484,5 @@ void get_color_pixels_accumulate (uint16_t * mask,
299484 * sum_a += a ;
300485};
301486
487+
488+
0 commit comments