1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19 #include <gegl.h>
20
21 #include <mypaint-surface.h>
22
23 #include "paint-types.h"
24
25 #include "libgimpmath/gimpmath.h"
26
27 #include <cairo.h>
28 #include <gdk-pixbuf/gdk-pixbuf.h>
29 #include "libgimpcolor/gimpcolor.h"
30
31 #include "gimpmybrushoptions.h"
32 #include "gimpmybrushsurface.h"
33
34
35 struct _GimpMybrushSurface
36 {
37 MyPaintSurface surface;
38 GeglBuffer *buffer;
39 GeglBuffer *paint_mask;
40 gint paint_mask_x;
41 gint paint_mask_y;
42 GeglRectangle dirty;
43 GimpComponentMask component_mask;
44 GimpMybrushOptions *options;
45 };
46
47 /* --- Taken from mypaint-tiled-surface.c --- */
48 static inline float
calculate_rr(int xp,int yp,float x,float y,float aspect_ratio,float sn,float cs,float one_over_radius2)49 calculate_rr (int xp,
50 int yp,
51 float x,
52 float y,
53 float aspect_ratio,
54 float sn,
55 float cs,
56 float one_over_radius2)
57 {
58 /* code duplication, see brush::count_dabs_to() */
59 const float yy = (yp + 0.5f - y);
60 const float xx = (xp + 0.5f - x);
61 const float yyr=(yy*cs-xx*sn)*aspect_ratio;
62 const float xxr=yy*sn+xx*cs;
63 const float rr = (yyr*yyr + xxr*xxr) * one_over_radius2;
64 /* rr is in range 0.0..1.0*sqrt(2) */
65 return rr;
66 }
67
68 static inline float
calculate_r_sample(float x,float y,float aspect_ratio,float sn,float cs)69 calculate_r_sample (float x,
70 float y,
71 float aspect_ratio,
72 float sn,
73 float cs)
74 {
75 const float yyr=(y*cs-x*sn)*aspect_ratio;
76 const float xxr=y*sn+x*cs;
77 const float r = (yyr*yyr + xxr*xxr);
78 return r;
79 }
80
81 static inline float
sign_point_in_line(float px,float py,float vx,float vy)82 sign_point_in_line (float px,
83 float py,
84 float vx,
85 float vy)
86 {
87 return (px - vx) * (-vy) - (vx) * (py - vy);
88 }
89
90 static inline void
closest_point_to_line(float lx,float ly,float px,float py,float * ox,float * oy)91 closest_point_to_line (float lx,
92 float ly,
93 float px,
94 float py,
95 float *ox,
96 float *oy)
97 {
98 const float l2 = lx*lx + ly*ly;
99 const float ltp_dot = px*lx + py*ly;
100 const float t = ltp_dot / l2;
101 *ox = lx * t;
102 *oy = ly * t;
103 }
104
105
106 /* This works by taking the visibility at the nearest point
107 * and dividing by 1.0 + delta.
108 *
109 * - nearest point: point where the dab has more influence
110 * - farthest point: point at a fixed distance away from
111 * the nearest point
112 * - delta: how much occluded is the farthest point relative
113 * to the nearest point
114 */
115 static inline float
calculate_rr_antialiased(int xp,int yp,float x,float y,float aspect_ratio,float sn,float cs,float one_over_radius2,float r_aa_start)116 calculate_rr_antialiased (int xp,
117 int yp,
118 float x,
119 float y,
120 float aspect_ratio,
121 float sn,
122 float cs,
123 float one_over_radius2,
124 float r_aa_start)
125 {
126 /* calculate pixel position and borders in a way
127 * that the dab's center is always at zero */
128 float pixel_right = x - (float)xp;
129 float pixel_bottom = y - (float)yp;
130 float pixel_center_x = pixel_right - 0.5f;
131 float pixel_center_y = pixel_bottom - 0.5f;
132 float pixel_left = pixel_right - 1.0f;
133 float pixel_top = pixel_bottom - 1.0f;
134
135 float nearest_x, nearest_y; /* nearest to origin, but still inside pixel */
136 float farthest_x, farthest_y; /* farthest from origin, but still inside pixel */
137 float r_near, r_far, rr_near, rr_far;
138 float center_sign, rad_area_1, visibilityNear, delta, delta2;
139
140 /* Dab's center is inside pixel? */
141 if( pixel_left<0 && pixel_right>0 &&
142 pixel_top<0 && pixel_bottom>0 )
143 {
144 nearest_x = 0;
145 nearest_y = 0;
146 r_near = rr_near = 0;
147 }
148 else
149 {
150 closest_point_to_line( cs, sn, pixel_center_x, pixel_center_y, &nearest_x, &nearest_y );
151 nearest_x = CLAMP( nearest_x, pixel_left, pixel_right );
152 nearest_y = CLAMP( nearest_y, pixel_top, pixel_bottom );
153 /* XXX: precision of "nearest" values could be improved
154 * by intersecting the line that goes from nearest_x/Y to 0
155 * with the pixel's borders here, however the improvements
156 * would probably not justify the perdormance cost.
157 */
158 r_near = calculate_r_sample( nearest_x, nearest_y, aspect_ratio, sn, cs );
159 rr_near = r_near * one_over_radius2;
160 }
161
162 /* out of dab's reach? */
163 if( rr_near > 1.0f )
164 return rr_near;
165
166 /* check on which side of the dab's line is the pixel center */
167 center_sign = sign_point_in_line( pixel_center_x, pixel_center_y, cs, -sn );
168
169 /* radius of a circle with area=1
170 * A = pi * r * r
171 * r = sqrt(1/pi)
172 */
173 rad_area_1 = sqrtf( 1.0f / M_PI );
174
175 /* center is below dab */
176 if( center_sign < 0 )
177 {
178 farthest_x = nearest_x - sn*rad_area_1;
179 farthest_y = nearest_y + cs*rad_area_1;
180 }
181 /* above dab */
182 else
183 {
184 farthest_x = nearest_x + sn*rad_area_1;
185 farthest_y = nearest_y - cs*rad_area_1;
186 }
187
188 r_far = calculate_r_sample( farthest_x, farthest_y, aspect_ratio, sn, cs );
189 rr_far = r_far * one_over_radius2;
190
191 /* check if we can skip heavier AA */
192 if( r_far < r_aa_start )
193 return (rr_far+rr_near) * 0.5f;
194
195 /* calculate AA approximate */
196 visibilityNear = 1.0f - rr_near;
197 delta = rr_far - rr_near;
198 delta2 = 1.0f + delta;
199 visibilityNear /= delta2;
200
201 return 1.0f - visibilityNear;
202 }
203 /* -- end mypaint code */
204
205 static inline float
calculate_alpha_for_rr(float rr,float hardness,float slope1,float slope2)206 calculate_alpha_for_rr (float rr,
207 float hardness,
208 float slope1,
209 float slope2)
210 {
211 if (rr > 1.0f)
212 return 0.0f;
213 else if (rr <= hardness)
214 return 1.0f + rr * slope1;
215 else
216 return rr * slope2 - slope2;
217 }
218
219 static GeglRectangle
calculate_dab_roi(float x,float y,float radius)220 calculate_dab_roi (float x,
221 float y,
222 float radius)
223 {
224 int x0 = floor (x - radius);
225 int x1 = ceil (x + radius);
226 int y0 = floor (y - radius);
227 int y1 = ceil (y + radius);
228
229 return *GEGL_RECTANGLE (x0, y0, x1 - x0, y1 - y0);
230 }
231
232 static void
gimp_mypaint_surface_get_color(MyPaintSurface * base_surface,float x,float y,float radius,float * color_r,float * color_g,float * color_b,float * color_a)233 gimp_mypaint_surface_get_color (MyPaintSurface *base_surface,
234 float x,
235 float y,
236 float radius,
237 float *color_r,
238 float *color_g,
239 float *color_b,
240 float *color_a)
241 {
242 GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface;
243 GeglRectangle dabRect;
244
245 if (radius < 1.0f)
246 radius = 1.0f;
247
248 dabRect = calculate_dab_roi (x, y, radius);
249
250 *color_r = 0.0f;
251 *color_g = 0.0f;
252 *color_b = 0.0f;
253 *color_a = 0.0f;
254
255 if (dabRect.width > 0 || dabRect.height > 0)
256 {
257 const float one_over_radius2 = 1.0f / (radius * radius);
258 float sum_weight = 0.0f;
259 float sum_r = 0.0f;
260 float sum_g = 0.0f;
261 float sum_b = 0.0f;
262 float sum_a = 0.0f;
263
264 /* Read in clamp mode to avoid transparency bleeding in at the edges */
265 GeglBufferIterator *iter = gegl_buffer_iterator_new (surface->buffer, &dabRect, 0,
266 babl_format ("R'aG'aB'aA float"),
267 GEGL_BUFFER_READ,
268 GEGL_ABYSS_CLAMP, 2);
269 if (surface->paint_mask)
270 {
271 GeglRectangle mask_roi = dabRect;
272 mask_roi.x -= surface->paint_mask_x;
273 mask_roi.y -= surface->paint_mask_y;
274 gegl_buffer_iterator_add (iter, surface->paint_mask, &mask_roi, 0,
275 babl_format ("Y float"),
276 GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
277 }
278
279 while (gegl_buffer_iterator_next (iter))
280 {
281 float *pixel = (float *)iter->items[0].data;
282 float *mask;
283 int iy, ix;
284
285 if (surface->paint_mask)
286 mask = iter->items[1].data;
287 else
288 mask = NULL;
289
290 for (iy = iter->items[0].roi.y; iy < iter->items[0].roi.y + iter->items[0].roi.height; iy++)
291 {
292 float yy = (iy + 0.5f - y);
293 for (ix = iter->items[0].roi.x; ix < iter->items[0].roi.x + iter->items[0].roi.width; ix++)
294 {
295 /* pixel_weight == a standard dab with hardness = 0.5, aspect_ratio = 1.0, and angle = 0.0 */
296 float xx = (ix + 0.5f - x);
297 float rr = (yy * yy + xx * xx) * one_over_radius2;
298 float pixel_weight = 0.0f;
299 if (rr <= 1.0f)
300 pixel_weight = 1.0f - rr;
301 if (mask)
302 pixel_weight *= *mask;
303
304 sum_r += pixel_weight * pixel[RED];
305 sum_g += pixel_weight * pixel[GREEN];
306 sum_b += pixel_weight * pixel[BLUE];
307 sum_a += pixel_weight * pixel[ALPHA];
308 sum_weight += pixel_weight;
309
310 pixel += 4;
311 if (mask)
312 mask += 1;
313 }
314 }
315 }
316
317 if (sum_a > 0.0f && sum_weight > 0.0f)
318 {
319 sum_r /= sum_weight;
320 sum_g /= sum_weight;
321 sum_b /= sum_weight;
322 sum_a /= sum_weight;
323
324 sum_r /= sum_a;
325 sum_g /= sum_a;
326 sum_b /= sum_a;
327
328 /* FIXME: Clamping is wrong because GEGL allows alpha > 1, this should probably re-multipy things */
329 *color_r = CLAMP(sum_r, 0.0f, 1.0f);
330 *color_g = CLAMP(sum_g, 0.0f, 1.0f);
331 *color_b = CLAMP(sum_b, 0.0f, 1.0f);
332 *color_a = CLAMP(sum_a, 0.0f, 1.0f);
333 }
334 }
335
336 }
337
338 static int
gimp_mypaint_surface_draw_dab(MyPaintSurface * base_surface,float x,float y,float radius,float color_r,float color_g,float color_b,float opaque,float hardness,float color_a,float aspect_ratio,float angle,float lock_alpha,float colorize)339 gimp_mypaint_surface_draw_dab (MyPaintSurface *base_surface,
340 float x,
341 float y,
342 float radius,
343 float color_r,
344 float color_g,
345 float color_b,
346 float opaque,
347 float hardness,
348 float color_a,
349 float aspect_ratio,
350 float angle,
351 float lock_alpha,
352 float colorize)
353 {
354 GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface;
355 GeglBufferIterator *iter;
356 GeglRectangle dabRect;
357 GimpComponentMask component_mask = surface->component_mask;
358
359 const float one_over_radius2 = 1.0f / (radius * radius);
360 const double angle_rad = angle / 360 * 2 * M_PI;
361 const float cs = cos(angle_rad);
362 const float sn = sin(angle_rad);
363 float normal_mode;
364 float segment1_slope;
365 float segment2_slope;
366 float r_aa_start;
367
368 hardness = CLAMP (hardness, 0.0f, 1.0f);
369 segment1_slope = -(1.0f / hardness - 1.0f);
370 segment2_slope = -hardness / (1.0f - hardness);
371 aspect_ratio = MAX (1.0f, aspect_ratio);
372
373 r_aa_start = radius - 1.0f;
374 r_aa_start = MAX (r_aa_start, 0);
375 r_aa_start = (r_aa_start * r_aa_start) / aspect_ratio;
376
377 normal_mode = opaque * (1.0f - colorize);
378 colorize = opaque * colorize;
379
380 /* FIXME: This should use the real matrix values to trim aspect_ratio dabs */
381 dabRect = calculate_dab_roi (x, y, radius);
382 gegl_rectangle_intersect (&dabRect, &dabRect, gegl_buffer_get_extent (surface->buffer));
383
384 if (dabRect.width <= 0 || dabRect.height <= 0)
385 return 0;
386
387 gegl_rectangle_bounding_box (&surface->dirty, &surface->dirty, &dabRect);
388
389 iter = gegl_buffer_iterator_new (surface->buffer, &dabRect, 0,
390 babl_format ("R'G'B'A float"),
391 GEGL_BUFFER_READWRITE,
392 GEGL_ABYSS_NONE, 2);
393 if (surface->paint_mask)
394 {
395 GeglRectangle mask_roi = dabRect;
396 mask_roi.x -= surface->paint_mask_x;
397 mask_roi.y -= surface->paint_mask_y;
398 gegl_buffer_iterator_add (iter, surface->paint_mask, &mask_roi, 0,
399 babl_format ("Y float"),
400 GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
401 }
402
403 while (gegl_buffer_iterator_next (iter))
404 {
405 float *pixel = (float *)iter->items[0].data;
406 float *mask;
407 int iy, ix;
408
409 if (surface->paint_mask)
410 mask = iter->items[1].data;
411 else
412 mask = NULL;
413
414 for (iy = iter->items[0].roi.y; iy < iter->items[0].roi.y + iter->items[0].roi.height; iy++)
415 {
416 for (ix = iter->items[0].roi.x; ix < iter->items[0].roi.x + iter->items[0].roi.width; ix++)
417 {
418 float rr, base_alpha, alpha, dst_alpha, r, g, b, a;
419 if (radius < 3.0f)
420 rr = calculate_rr_antialiased (ix, iy, x, y, aspect_ratio, sn, cs, one_over_radius2, r_aa_start);
421 else
422 rr = calculate_rr (ix, iy, x, y, aspect_ratio, sn, cs, one_over_radius2);
423 base_alpha = calculate_alpha_for_rr (rr, hardness, segment1_slope, segment2_slope);
424 alpha = base_alpha * normal_mode;
425 if (mask)
426 alpha *= *mask;
427 dst_alpha = pixel[ALPHA];
428 /* a = alpha * color_a + dst_alpha * (1.0f - alpha);
429 * which converts to: */
430 a = alpha * (color_a - dst_alpha) + dst_alpha;
431 r = pixel[RED];
432 g = pixel[GREEN];
433 b = pixel[BLUE];
434
435 if (a > 0.0f)
436 {
437 /* By definition the ratio between each color[] and pixel[] component in a non-pre-multipled blend always sums to 1.0f.
438 * Originally this would have been "(color[n] * alpha * color_a + pixel[n] * dst_alpha * (1.0f - alpha)) / a",
439 * instead we only calculate the cheaper term. */
440 float src_term = (alpha * color_a) / a;
441 float dst_term = 1.0f - src_term;
442 r = color_r * src_term + r * dst_term;
443 g = color_g * src_term + g * dst_term;
444 b = color_b * src_term + b * dst_term;
445 }
446
447 if (colorize > 0.0f && base_alpha > 0.0f)
448 {
449 alpha = base_alpha * colorize;
450 a = alpha + dst_alpha - alpha * dst_alpha;
451 if (a > 0.0f)
452 {
453 GimpHSL pixel_hsl, out_hsl;
454 GimpRGB pixel_rgb = {color_r, color_g, color_b};
455 GimpRGB out_rgb = {r, g, b};
456 float src_term = alpha / a;
457 float dst_term = 1.0f - src_term;
458
459 gimp_rgb_to_hsl (&pixel_rgb, &pixel_hsl);
460 gimp_rgb_to_hsl (&out_rgb, &out_hsl);
461
462 out_hsl.h = pixel_hsl.h;
463 out_hsl.s = pixel_hsl.s;
464 gimp_hsl_to_rgb (&out_hsl, &out_rgb);
465
466 r = (float)out_rgb.r * src_term + r * dst_term;
467 g = (float)out_rgb.g * src_term + g * dst_term;
468 b = (float)out_rgb.b * src_term + b * dst_term;
469 }
470 }
471
472 if (surface->options->no_erasing)
473 a = MAX (a, pixel[ALPHA]);
474
475 if (component_mask != GIMP_COMPONENT_MASK_ALL)
476 {
477 if (component_mask & GIMP_COMPONENT_MASK_RED)
478 pixel[RED] = r;
479 if (component_mask & GIMP_COMPONENT_MASK_GREEN)
480 pixel[GREEN] = g;
481 if (component_mask & GIMP_COMPONENT_MASK_BLUE)
482 pixel[BLUE] = b;
483 if (component_mask & GIMP_COMPONENT_MASK_ALPHA)
484 pixel[ALPHA] = a;
485 }
486 else
487 {
488 pixel[RED] = r;
489 pixel[GREEN] = g;
490 pixel[BLUE] = b;
491 pixel[ALPHA] = a;
492 }
493
494 pixel += 4;
495 if (mask)
496 mask += 1;
497 }
498 }
499 }
500
501 return 1;
502 }
503
504 static void
gimp_mypaint_surface_begin_atomic(MyPaintSurface * base_surface)505 gimp_mypaint_surface_begin_atomic (MyPaintSurface *base_surface)
506 {
507
508 }
509
510 static void
gimp_mypaint_surface_end_atomic(MyPaintSurface * base_surface,MyPaintRectangle * roi)511 gimp_mypaint_surface_end_atomic (MyPaintSurface *base_surface,
512 MyPaintRectangle *roi)
513 {
514 GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface;
515
516 roi->x = surface->dirty.x;
517 roi->y = surface->dirty.y;
518 roi->width = surface->dirty.width;
519 roi->height = surface->dirty.height;
520 surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0);
521 }
522
523 static void
gimp_mypaint_surface_destroy(MyPaintSurface * base_surface)524 gimp_mypaint_surface_destroy (MyPaintSurface *base_surface)
525 {
526 GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface;
527
528 g_clear_object (&surface->buffer);
529 g_clear_object (&surface->paint_mask);
530 }
531
532 GimpMybrushSurface *
gimp_mypaint_surface_new(GeglBuffer * buffer,GimpComponentMask component_mask,GeglBuffer * paint_mask,gint paint_mask_x,gint paint_mask_y,GimpMybrushOptions * options)533 gimp_mypaint_surface_new (GeglBuffer *buffer,
534 GimpComponentMask component_mask,
535 GeglBuffer *paint_mask,
536 gint paint_mask_x,
537 gint paint_mask_y,
538 GimpMybrushOptions *options)
539 {
540 GimpMybrushSurface *surface = g_malloc0 (sizeof (GimpMybrushSurface));
541
542 mypaint_surface_init ((MyPaintSurface *)surface);
543
544 surface->surface.get_color = gimp_mypaint_surface_get_color;
545 surface->surface.draw_dab = gimp_mypaint_surface_draw_dab;
546 surface->surface.begin_atomic = gimp_mypaint_surface_begin_atomic;
547 surface->surface.end_atomic = gimp_mypaint_surface_end_atomic;
548 surface->surface.destroy = gimp_mypaint_surface_destroy;
549 surface->component_mask = component_mask;
550 surface->options = options;
551 surface->buffer = g_object_ref (buffer);
552 if (paint_mask)
553 surface->paint_mask = g_object_ref (paint_mask);
554
555 surface->paint_mask_x = paint_mask_x;
556 surface->paint_mask_y = paint_mask_y;
557 surface->dirty = *GEGL_RECTANGLE (0, 0, 0, 0);
558
559 return surface;
560 }
561