5454
5555#define GRID_SIZE 256.0
5656
57- /* Named indices for the smudge bucket arrays */
58- enum {
59- SMUDGE_R , SMUDGE_G , SMUDGE_B , SMUDGE_A ,
60- PREV_COL_R , PREV_COL_G , PREV_COL_B , PREV_COL_A ,
61- PREV_COL_RECENTNESS ,
62- SMUDGE_BUCKET_SIZE
63- };
57+ #define SMUDGE_BUCKET_SIZE (sizeof(SmudgeBucket) / sizeof(float))
58+
59+ /**
60+ * SmudgeBucket:
61+ *
62+ * Optionally used by MyPaintBrush.
63+ *
64+ * Usually used for brushes with multiple offset dabs, where each
65+ * dab is assigned its own bucket containing a smudge state.
66+ */
67+ typedef struct {
68+ float smudge_r ;
69+ float smudge_g ;
70+ float smudge_b ;
71+ float smudge_a ;
72+ float prev_col_r ;
73+ float prev_col_g ;
74+ float prev_col_b ;
75+ float prev_col_a ;
76+ float prev_col_recentness ;
77+ float smudge_bucket_size ;
78+ } SmudgeBucket ;
6479
6580/* The Brush class stores two things:
6681 b) settings: constant during a stroke (eg. size, spacing, dynamics, color selected by the user)
@@ -94,7 +109,7 @@ struct MyPaintBrush {
94109 // smudge bucket array: part of the state, but stored separately.
95110 // Usually used for brushes with multiple offset dabs, where each
96111 // dab is assigned its own bucket containing a smudge state.
97- float * smudge_buckets ;
112+ SmudgeBucket * smudge_buckets ;
98113 int num_buckets ;
99114 int min_bucket_used ;
100115 int max_bucket_used ;
@@ -158,7 +173,7 @@ brush_reset(MyPaintBrush *self)
158173 int min_index = self -> min_bucket_used ;
159174 if (min_index != -1 ) {
160175 int max_index = self -> max_bucket_used ;
161- size_t num_bytes = (max_index - min_index + 1 ) * sizeof (self -> smudge_buckets [0 ]) * SMUDGE_BUCKET_SIZE ;
176+ size_t num_bytes = (max_index - min_index + 1 ) * sizeof (self -> smudge_buckets [0 ]);
162177 memset (self -> smudge_buckets + min_index , 0 , num_bytes );
163178 self -> min_bucket_used = -1 ;
164179 self -> max_bucket_used = -1 ;
@@ -195,7 +210,7 @@ mypaint_brush_new_with_buckets(int num_smudge_buckets)
195210 }
196211
197212 if (num_smudge_buckets > 0 ) {
198- float * bucket_array = malloc (num_smudge_buckets * SMUDGE_BUCKET_SIZE * sizeof (float ));
213+ SmudgeBucket * bucket_array = malloc (num_smudge_buckets * sizeof (SmudgeBucket ));
199214 if (!bucket_array ) {
200215 free (self );
201216 return NULL ;
@@ -460,16 +475,16 @@ mypaint_brush_set_smudge_bucket_state(
460475 float prev_color_recentness )
461476{
462477 if (self -> num_buckets > bucket_index ) {
463- float * bucket = & self -> smudge_buckets [bucket_index ];
464- bucket [ SMUDGE_R ] = r ;
465- bucket [ SMUDGE_G ] = g ;
466- bucket [ SMUDGE_B ] = b ;
467- bucket [ SMUDGE_A ] = a ;
468- bucket [ PREV_COL_R ] = prev_r ;
469- bucket [ PREV_COL_G ] = prev_g ;
470- bucket [ PREV_COL_B ] = prev_b ;
471- bucket [ PREV_COL_A ] = prev_a ;
472- bucket [ PREV_COL_RECENTNESS ] = prev_color_recentness ;
478+ SmudgeBucket * bucket = & self -> smudge_buckets [bucket_index ];
479+ bucket -> smudge_r = r ;
480+ bucket -> smudge_g = g ;
481+ bucket -> smudge_b = b ;
482+ bucket -> smudge_a = a ;
483+ bucket -> prev_col_r = prev_r ;
484+ bucket -> prev_col_g = prev_g ;
485+ bucket -> prev_col_b = prev_b ;
486+ bucket -> prev_col_a = prev_a ;
487+ bucket -> prev_col_recentness = prev_color_recentness ;
473488 return TRUE;
474489 }
475490 return FALSE;
@@ -492,16 +507,16 @@ mypaint_brush_get_smudge_bucket_state(
492507 float * prev_color_recentness )
493508{
494509 if (self -> num_buckets > bucket_index ) {
495- float * bucket = & self -> smudge_buckets [bucket_index ];
496- * r = bucket [ SMUDGE_R ] ;
497- * g = bucket [ SMUDGE_G ] ;
498- * b = bucket [ SMUDGE_B ] ;
499- * a = bucket [ SMUDGE_A ] ;
500- * prev_r = bucket [ PREV_COL_R ] ;
501- * prev_g = bucket [ PREV_COL_G ] ;
502- * prev_b = bucket [ PREV_COL_B ] ;
503- * prev_a = bucket [ PREV_COL_A ] ;
504- * prev_color_recentness = bucket [ PREV_COL_RECENTNESS ] ;
510+ SmudgeBucket * bucket = & self -> smudge_buckets [bucket_index ];
511+ * r = bucket -> smudge_r ;
512+ * g = bucket -> smudge_g ;
513+ * b = bucket -> smudge_b ;
514+ * a = bucket -> smudge_a ;
515+ * prev_r = bucket -> prev_col_r ;
516+ * prev_g = bucket -> prev_col_g ;
517+ * prev_b = bucket -> prev_col_b ;
518+ * prev_a = bucket -> prev_col_a ;
519+ * prev_color_recentness = bucket -> prev_col_recentness ;
505520 return TRUE;
506521 }
507522 return FALSE;
@@ -903,9 +918,12 @@ void print_inputs(MyPaintBrush *self, float* inputs)
903918 STATE (self , ACTUAL_ELLIPTICAL_DAB_ANGLE ) = mod_arith (SETTING (self , ELLIPTICAL_DAB_ANGLE ) - viewrotation + 180.0 , 180.0 ) - 180.0 ;
904919 }
905920
906- float * fetch_smudge_bucket (MyPaintBrush * self ) {
921+ SmudgeBucket * fetch_smudge_bucket (MyPaintBrush * self ) {
907922 if (!self -> smudge_buckets || !self -> num_buckets ) {
908- return & STATE (self , SMUDGE_RA );
923+ float * smudge_state = & STATE (self , SMUDGE_RA );
924+ // Not sure this can be done in general.
925+ // static_assert (sizeof (SmudgeBucket) == sizeof(smudge_state) * SMUDGE_BUCKET_SIZE, "SmudgeBucket includes padding");
926+ return (SmudgeBucket * ) smudge_state ;
909927 }
910928 const int bucket_index = CLAMP (roundf (SETTING (self , SMUDGE_BUCKET )), 0 , self -> num_buckets - 1 );
911929 if (self -> min_bucket_used == -1 || self -> min_bucket_used > bucket_index ) {
@@ -914,12 +932,12 @@ void print_inputs(MyPaintBrush *self, float* inputs)
914932 if (self -> max_bucket_used < bucket_index ) {
915933 self -> max_bucket_used = bucket_index ;
916934 }
917- return & self -> smudge_buckets [bucket_index * SMUDGE_BUCKET_SIZE ];
935+ return & self -> smudge_buckets [bucket_index ];
918936 }
919937
920938 gboolean
921939 update_smudge_color (
922- const MyPaintBrush * self , MyPaintSurface * surface , float * const smudge_bucket , const float smudge_length , int px ,
940+ const MyPaintBrush * self , MyPaintSurface * surface , SmudgeBucket * const smudge_bucket , const float smudge_length , int px ,
923941 int py , const float radius , const float legacy_smudge , const float paint_factor )
924942 {
925943
@@ -934,16 +952,16 @@ void print_inputs(MyPaintBrush *self, float* inputs)
934952 float r , g , b , a ;
935953 const float smudge_length_log = SETTING (self , SMUDGE_LENGTH_LOG );
936954
937- const float recentness = smudge_bucket [ PREV_COL_RECENTNESS ] * update_factor ;
938- smudge_bucket [ PREV_COL_RECENTNESS ] = recentness ;
955+ const float recentness = smudge_bucket -> prev_col_recentness * update_factor ;
956+ smudge_bucket -> prev_col_recentness = recentness ;
939957
940958 const float margin = 0.0000000000000001 ;
941959 if (recentness < MIN (1.0 , powf (0.5 * update_factor , smudge_length_log ) + margin )) {
942960 if (recentness == 0.0 ) {
943961 // first initialization of smudge color (initiate with color sampled from canvas)
944962 update_factor = 0.0 ;
945963 }
946- smudge_bucket [ PREV_COL_RECENTNESS ] = 1.0 ;
964+ smudge_bucket -> prev_col_recentness = 1.0 ;
947965
948966 const float radius_log = SETTING (self , SMUDGE_RADIUS_LOG );
949967 const float smudge_radius = CLAMP (radius * expf (radius_log ), ACTUAL_RADIUS_MIN , ACTUAL_RADIUS_MAX );
@@ -960,45 +978,45 @@ void print_inputs(MyPaintBrush *self, float* inputs)
960978 if ((smudge_op_lim > 0.0 && a < smudge_op_lim ) || (smudge_op_lim < 0.0 && a > - smudge_op_lim )) {
961979 return TRUE; // signals the caller to return early
962980 }
963- smudge_bucket [ PREV_COL_R ] = r ;
964- smudge_bucket [ PREV_COL_G ] = g ;
965- smudge_bucket [ PREV_COL_B ] = b ;
966- smudge_bucket [ PREV_COL_A ] = a ;
981+ smudge_bucket -> prev_col_r = r ;
982+ smudge_bucket -> prev_col_g = g ;
983+ smudge_bucket -> prev_col_b = b ;
984+ smudge_bucket -> prev_col_a = a ;
967985 } else {
968- r = smudge_bucket [ PREV_COL_R ] ;
969- g = smudge_bucket [ PREV_COL_G ] ;
970- b = smudge_bucket [ PREV_COL_B ] ;
971- a = smudge_bucket [ PREV_COL_A ] ;
986+ r = smudge_bucket -> prev_col_r ;
987+ g = smudge_bucket -> prev_col_g ;
988+ b = smudge_bucket -> prev_col_b ;
989+ a = smudge_bucket -> prev_col_a ;
972990 }
973991
974992 if (legacy_smudge ) {
975993 const float fac_old = update_factor ;
976994 const float fac_new = (1.0 - update_factor ) * a ;
977- smudge_bucket [ SMUDGE_R ] = fac_old * smudge_bucket [ SMUDGE_R ] + fac_new * r ;
978- smudge_bucket [ SMUDGE_G ] = fac_old * smudge_bucket [ SMUDGE_G ] + fac_new * g ;
979- smudge_bucket [ SMUDGE_B ] = fac_old * smudge_bucket [ SMUDGE_B ] + fac_new * b ;
980- smudge_bucket [ SMUDGE_A ] = CLAMP ((fac_old * smudge_bucket [ SMUDGE_A ] + fac_new ), 0.0 , 1.0 );
995+ smudge_bucket -> smudge_r = fac_old * smudge_bucket -> smudge_r + fac_new * r ;
996+ smudge_bucket -> smudge_g = fac_old * smudge_bucket -> smudge_g + fac_new * g ;
997+ smudge_bucket -> smudge_b = fac_old * smudge_bucket -> smudge_b + fac_new * b ;
998+ smudge_bucket -> smudge_a = CLAMP ((fac_old * smudge_bucket -> smudge_a + fac_new ), 0.0 , 1.0 );
981999 } else if (a > WGM_EPSILON * 10 ) {
982- float prev_smudge_color [4 ] = {smudge_bucket [ SMUDGE_R ] , smudge_bucket [ SMUDGE_G ] , smudge_bucket [ SMUDGE_B ] ,
983- smudge_bucket [ SMUDGE_A ] };
1000+ float prev_smudge_color [4 ] = {smudge_bucket -> smudge_r , smudge_bucket -> smudge_g , smudge_bucket -> smudge_b ,
1001+ smudge_bucket -> smudge_a };
9841002 float sampled_color [4 ] = {r , g , b , a };
9851003
9861004 float * smudge_new = mix_colors (prev_smudge_color , sampled_color , update_factor , paint_factor );
987- smudge_bucket [ SMUDGE_R ] = smudge_new [SMUDGE_R ];
988- smudge_bucket [ SMUDGE_G ] = smudge_new [SMUDGE_G ];
989- smudge_bucket [ SMUDGE_B ] = smudge_new [SMUDGE_B ];
990- smudge_bucket [ SMUDGE_A ] = smudge_new [SMUDGE_A ];
1005+ smudge_bucket -> smudge_r = smudge_new [0 ];
1006+ smudge_bucket -> smudge_g = smudge_new [1 ];
1007+ smudge_bucket -> smudge_b = smudge_new [2 ];
1008+ smudge_bucket -> smudge_a = smudge_new [3 ];
9911009 } else {
9921010 // To avoid color noise from spectral mixing with a low alpha,
9931011 // we'll just decrease the alpha of the existing smudge color.
994- smudge_bucket [ SMUDGE_A ] = (smudge_bucket [ SMUDGE_A ] + a ) / 2 ;
1012+ smudge_bucket -> smudge_a = (smudge_bucket -> smudge_a + a ) / 2 ;
9951013 }
9961014 return FALSE; // signals the caller to not return early (the default)
9971015 }
9981016
9991017 float
10001018 apply_smudge (
1001- const float * const smudge_bucket , const float smudge_value , const gboolean legacy_smudge ,
1019+ const SmudgeBucket * const smudge_bucket , const float smudge_value , const gboolean legacy_smudge ,
10021020 const float paint_factor , float * color_r , float * color_g , float * color_b )
10031021 {
10041022 float smudge_factor = MIN (1.0 , smudge_value );
@@ -1007,22 +1025,22 @@ void print_inputs(MyPaintBrush *self, float* inputs)
10071025 // dab will do erasing towards that transparency level.
10081026 // see also ../doc/smudge_math.png
10091027 const float eraser_target_alpha =
1010- CLAMP ((1.0 - smudge_factor ) + smudge_factor * smudge_bucket [ SMUDGE_A ] , 0.0 , 1.0 );
1028+ CLAMP ((1.0 - smudge_factor ) + smudge_factor * smudge_bucket -> smudge_a , 0.0 , 1.0 );
10111029
10121030 if (eraser_target_alpha > 0 ) {
10131031 if (legacy_smudge ) {
10141032 const float col_factor = 1.0 - smudge_factor ;
1015- * color_r = (smudge_factor * smudge_bucket [ SMUDGE_R ] + col_factor * * color_r ) / eraser_target_alpha ;
1016- * color_g = (smudge_factor * smudge_bucket [ SMUDGE_G ] + col_factor * * color_g ) / eraser_target_alpha ;
1017- * color_b = (smudge_factor * smudge_bucket [ SMUDGE_B ] + col_factor * * color_b ) / eraser_target_alpha ;
1033+ * color_r = (smudge_factor * smudge_bucket -> smudge_r + col_factor * * color_r ) / eraser_target_alpha ;
1034+ * color_g = (smudge_factor * smudge_bucket -> smudge_g + col_factor * * color_g ) / eraser_target_alpha ;
1035+ * color_b = (smudge_factor * smudge_bucket -> smudge_b + col_factor * * color_b ) / eraser_target_alpha ;
10181036 } else {
1019- float smudge_color [4 ] = {smudge_bucket [ SMUDGE_R ] , smudge_bucket [ SMUDGE_G ] , smudge_bucket [ SMUDGE_B ] ,
1020- smudge_bucket [ SMUDGE_A ] };
1037+ float smudge_color [4 ] = {smudge_bucket -> smudge_r , smudge_bucket -> smudge_g , smudge_bucket -> smudge_b ,
1038+ smudge_bucket -> smudge_a };
10211039 float brush_color [4 ] = {* color_r , * color_g , * color_b , 1.0 };
10221040 float * color_new = mix_colors (smudge_color , brush_color , smudge_factor , paint_factor );
1023- * color_r = color_new [SMUDGE_R ];
1024- * color_g = color_new [SMUDGE_G ];
1025- * color_b = color_new [SMUDGE_B ];
1041+ * color_r = color_new [0 ];
1042+ * color_g = color_new [1 ];
1043+ * color_b = color_new [2 ];
10261044 }
10271045 } else {
10281046 // we are only erasing; the color does (should) not matter
@@ -1123,7 +1141,7 @@ gboolean prepare_and_draw_dab (MyPaintBrush *self, MyPaintSurface * surface, gbo
11231141 const float smudge_length = SETTING (self , SMUDGE_LENGTH );
11241142 if (smudge_length < 1.0 && // default smudge length is 0.5, so the smudge factor is checked as well
11251143 (SETTING (self , SMUDGE ) != 0.0 || !mypaint_mapping_is_constant (self -> settings [MYPAINT_BRUSH_SETTING_SMUDGE ]))) {
1126- float * const bucket = fetch_smudge_bucket (self );
1144+ SmudgeBucket * const bucket = fetch_smudge_bucket (self );
11271145 gboolean return_early = update_smudge_color (
11281146 self , surface , bucket , smudge_length , ROUND (x ), ROUND (y ), radius , legacy_smudge , paint_factor );
11291147 if (return_early ) {
@@ -1135,7 +1153,7 @@ gboolean prepare_and_draw_dab (MyPaintBrush *self, MyPaintSurface * surface, gbo
11351153 const float smudge_value = SETTING (self , SMUDGE );
11361154
11371155 if (smudge_value > 0.0 ) {
1138- float * const bucket = fetch_smudge_bucket (self );
1156+ SmudgeBucket * const bucket = fetch_smudge_bucket (self );
11391157 eraser_target_alpha =
11401158 apply_smudge (bucket , smudge_value , legacy_smudge , paint_factor , & color_h , & color_s , & color_v );
11411159 }
0 commit comments