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
20 #include <string.h>
21
22 #include <gdk-pixbuf/gdk-pixbuf.h>
23 #include <gegl.h>
24
25 #include "libgimpmath/gimpmath.h"
26
27 #include "paint-types.h"
28
29 #include "operations/layer-modes/gimp-layer-modes.h"
30
31 #include "gegl/gimp-gegl-utils.h"
32
33 #include "core/gimp-palettes.h"
34 #include "core/gimpdrawable.h"
35 #include "core/gimpimage.h"
36 #include "core/gimpimage-undo.h"
37 #include "core/gimppickable.h"
38 #include "core/gimpsymmetry.h"
39 #include "core/gimptempbuf.h"
40
41 #include "gimpinkoptions.h"
42 #include "gimpink.h"
43 #include "gimpink-blob.h"
44 #include "gimpinkundo.h"
45
46 #include "gimp-intl.h"
47
48
49 #define SUBSAMPLE 8
50
51
52 /* local function prototypes */
53
54 static void gimp_ink_finalize (GObject *object);
55
56 static void gimp_ink_paint (GimpPaintCore *paint_core,
57 GimpDrawable *drawable,
58 GimpPaintOptions *paint_options,
59 GimpSymmetry *sym,
60 GimpPaintState paint_state,
61 guint32 time);
62 static GeglBuffer * gimp_ink_get_paint_buffer (GimpPaintCore *paint_core,
63 GimpDrawable *drawable,
64 GimpPaintOptions *paint_options,
65 GimpLayerMode paint_mode,
66 const GimpCoords *coords,
67 gint *paint_buffer_x,
68 gint *paint_buffer_y,
69 gint *paint_width,
70 gint *paint_height);
71 static GimpUndo * gimp_ink_push_undo (GimpPaintCore *core,
72 GimpImage *image,
73 const gchar *undo_desc);
74
75 static void gimp_ink_motion (GimpPaintCore *paint_core,
76 GimpDrawable *drawable,
77 GimpPaintOptions *paint_options,
78 GimpSymmetry *sym,
79 guint32 time);
80
81 static GimpBlob * ink_pen_ellipse (GimpInkOptions *options,
82 gdouble x_center,
83 gdouble y_center,
84 gdouble pressure,
85 gdouble xtilt,
86 gdouble ytilt,
87 gdouble velocity,
88 const GimpMatrix3 *transform);
89
90 static void render_blob (GeglBuffer *buffer,
91 GeglRectangle *rect,
92 GimpBlob *blob);
93
94
G_DEFINE_TYPE(GimpInk,gimp_ink,GIMP_TYPE_PAINT_CORE)95 G_DEFINE_TYPE (GimpInk, gimp_ink, GIMP_TYPE_PAINT_CORE)
96
97 #define parent_class gimp_ink_parent_class
98
99
100 void
101 gimp_ink_register (Gimp *gimp,
102 GimpPaintRegisterCallback callback)
103 {
104 (* callback) (gimp,
105 GIMP_TYPE_INK,
106 GIMP_TYPE_INK_OPTIONS,
107 "gimp-ink",
108 _("Ink"),
109 "gimp-tool-ink");
110 }
111
112 static void
gimp_ink_class_init(GimpInkClass * klass)113 gimp_ink_class_init (GimpInkClass *klass)
114 {
115 GObjectClass *object_class = G_OBJECT_CLASS (klass);
116 GimpPaintCoreClass *paint_core_class = GIMP_PAINT_CORE_CLASS (klass);
117
118 object_class->finalize = gimp_ink_finalize;
119
120 paint_core_class->paint = gimp_ink_paint;
121 paint_core_class->get_paint_buffer = gimp_ink_get_paint_buffer;
122 paint_core_class->push_undo = gimp_ink_push_undo;
123 }
124
125 static void
gimp_ink_init(GimpInk * ink)126 gimp_ink_init (GimpInk *ink)
127 {
128 }
129
130 static void
gimp_ink_finalize(GObject * object)131 gimp_ink_finalize (GObject *object)
132 {
133 GimpInk *ink = GIMP_INK (object);
134
135 if (ink->start_blobs)
136 {
137 g_list_free_full (ink->start_blobs, g_free);
138 ink->start_blobs = NULL;
139 }
140
141 if (ink->last_blobs)
142 {
143 g_list_free_full (ink->last_blobs, g_free);
144 ink->last_blobs = NULL;
145 }
146
147 G_OBJECT_CLASS (parent_class)->finalize (object);
148 }
149
150 static void
gimp_ink_paint(GimpPaintCore * paint_core,GimpDrawable * drawable,GimpPaintOptions * paint_options,GimpSymmetry * sym,GimpPaintState paint_state,guint32 time)151 gimp_ink_paint (GimpPaintCore *paint_core,
152 GimpDrawable *drawable,
153 GimpPaintOptions *paint_options,
154 GimpSymmetry *sym,
155 GimpPaintState paint_state,
156 guint32 time)
157 {
158 GimpInk *ink = GIMP_INK (paint_core);
159 GimpCoords *cur_coords;
160 GimpCoords last_coords;
161
162 gimp_paint_core_get_last_coords (paint_core, &last_coords);
163 cur_coords = gimp_symmetry_get_origin (sym);
164
165 switch (paint_state)
166 {
167 case GIMP_PAINT_STATE_INIT:
168 {
169 GimpContext *context = GIMP_CONTEXT (paint_options);
170 GimpRGB foreground;
171
172 gimp_symmetry_set_stateful (sym, TRUE);
173 gimp_context_get_foreground (context, &foreground);
174 gimp_palettes_add_color_history (context->gimp,
175 &foreground);
176
177 if (cur_coords->x == last_coords.x &&
178 cur_coords->y == last_coords.y)
179 {
180 if (ink->start_blobs)
181 {
182 g_list_free_full (ink->start_blobs, g_free);
183 ink->start_blobs = NULL;
184 }
185
186 if (ink->last_blobs)
187 {
188 g_list_free_full (ink->last_blobs, g_free);
189 ink->last_blobs = NULL;
190 }
191 }
192 else if (ink->last_blobs)
193 {
194 GimpBlob *last_blob;
195 GList *iter;
196 gint i;
197
198 if (ink->start_blobs)
199 {
200 g_list_free_full (ink->start_blobs, g_free);
201 ink->start_blobs = NULL;
202 }
203
204 /* save the start blobs of each stroke for undo otherwise */
205 for (iter = ink->last_blobs, i = 0; iter; iter = g_list_next (iter), i++)
206 {
207 last_blob = g_list_nth_data (ink->last_blobs, i);
208
209 ink->start_blobs = g_list_prepend (ink->start_blobs,
210 gimp_blob_duplicate (last_blob));
211 }
212 ink->start_blobs = g_list_reverse (ink->start_blobs);
213 }
214 }
215 break;
216
217 case GIMP_PAINT_STATE_MOTION:
218 gimp_ink_motion (paint_core, drawable, paint_options, sym, time);
219 break;
220
221 case GIMP_PAINT_STATE_FINISH:
222 gimp_symmetry_set_stateful (sym, FALSE);
223 break;
224 }
225 }
226
227 static GeglBuffer *
gimp_ink_get_paint_buffer(GimpPaintCore * paint_core,GimpDrawable * drawable,GimpPaintOptions * paint_options,GimpLayerMode paint_mode,const GimpCoords * coords,gint * paint_buffer_x,gint * paint_buffer_y,gint * paint_width,gint * paint_height)228 gimp_ink_get_paint_buffer (GimpPaintCore *paint_core,
229 GimpDrawable *drawable,
230 GimpPaintOptions *paint_options,
231 GimpLayerMode paint_mode,
232 const GimpCoords *coords,
233 gint *paint_buffer_x,
234 gint *paint_buffer_y,
235 gint *paint_width,
236 gint *paint_height)
237 {
238 GimpInk *ink = GIMP_INK (paint_core);
239 gint x, y;
240 gint width, height;
241 gint dwidth, dheight;
242 gint x1, y1, x2, y2;
243
244 gimp_blob_bounds (ink->cur_blob, &x, &y, &width, &height);
245
246 dwidth = gimp_item_get_width (GIMP_ITEM (drawable));
247 dheight = gimp_item_get_height (GIMP_ITEM (drawable));
248
249 x1 = CLAMP (x / SUBSAMPLE - 1, 0, dwidth);
250 y1 = CLAMP (y / SUBSAMPLE - 1, 0, dheight);
251 x2 = CLAMP ((x + width) / SUBSAMPLE + 2, 0, dwidth);
252 y2 = CLAMP ((y + height) / SUBSAMPLE + 2, 0, dheight);
253
254 if (paint_width)
255 *paint_width = width / SUBSAMPLE + 3;
256 if (paint_height)
257 *paint_height = height / SUBSAMPLE + 3;
258
259 /* configure the canvas buffer */
260 if ((x2 - x1) && (y2 - y1))
261 {
262 GimpTempBuf *temp_buf;
263 const Babl *format;
264 GimpLayerCompositeMode composite_mode;
265
266 composite_mode = gimp_layer_mode_get_paint_composite_mode (paint_mode);
267
268 format = gimp_layer_mode_get_format (paint_mode,
269 GIMP_LAYER_COLOR_SPACE_AUTO,
270 GIMP_LAYER_COLOR_SPACE_AUTO,
271 composite_mode,
272 gimp_drawable_get_format (drawable));
273
274 temp_buf = gimp_temp_buf_new ((x2 - x1), (y2 - y1),
275 format);
276
277 *paint_buffer_x = x1;
278 *paint_buffer_y = y1;
279
280 if (paint_core->paint_buffer)
281 g_object_unref (paint_core->paint_buffer);
282
283 paint_core->paint_buffer = gimp_temp_buf_create_buffer (temp_buf);
284
285 gimp_temp_buf_unref (temp_buf);
286
287 return paint_core->paint_buffer;
288 }
289
290 return NULL;
291 }
292
293 static GimpUndo *
gimp_ink_push_undo(GimpPaintCore * core,GimpImage * image,const gchar * undo_desc)294 gimp_ink_push_undo (GimpPaintCore *core,
295 GimpImage *image,
296 const gchar *undo_desc)
297 {
298 return gimp_image_undo_push (image, GIMP_TYPE_INK_UNDO,
299 GIMP_UNDO_INK, undo_desc,
300 0,
301 "paint-core", core,
302 NULL);
303 }
304
305 static void
gimp_ink_motion(GimpPaintCore * paint_core,GimpDrawable * drawable,GimpPaintOptions * paint_options,GimpSymmetry * sym,guint32 time)306 gimp_ink_motion (GimpPaintCore *paint_core,
307 GimpDrawable *drawable,
308 GimpPaintOptions *paint_options,
309 GimpSymmetry *sym,
310 guint32 time)
311 {
312 GimpInk *ink = GIMP_INK (paint_core);
313 GimpInkOptions *options = GIMP_INK_OPTIONS (paint_options);
314 GimpContext *context = GIMP_CONTEXT (paint_options);
315 GList *blob_unions = NULL;
316 GList *blobs_to_render = NULL;
317 GeglBuffer *paint_buffer;
318 gint paint_buffer_x;
319 gint paint_buffer_y;
320 GimpLayerMode paint_mode;
321 GimpRGB foreground;
322 GeglColor *color;
323 GimpBlob *last_blob;
324 GimpCoords *coords;
325 gint n_strokes;
326 gint i;
327
328 n_strokes = gimp_symmetry_get_size (sym);
329
330 if (ink->last_blobs &&
331 g_list_length (ink->last_blobs) != n_strokes)
332 {
333 g_list_free_full (ink->last_blobs, g_free);
334 ink->last_blobs = NULL;
335 }
336
337 if (! ink->last_blobs)
338 {
339 if (ink->start_blobs)
340 {
341 g_list_free_full (ink->start_blobs, g_free);
342 ink->start_blobs = NULL;
343 }
344
345 for (i = 0; i < n_strokes; i++)
346 {
347 GimpMatrix3 transform;
348
349 coords = gimp_symmetry_get_coords (sym, i);
350
351 gimp_symmetry_get_matrix (sym, i, &transform);
352
353 last_blob = ink_pen_ellipse (options,
354 coords->x,
355 coords->y,
356 coords->pressure,
357 coords->xtilt,
358 coords->ytilt,
359 100,
360 &transform);
361
362 ink->last_blobs = g_list_prepend (ink->last_blobs,
363 last_blob);
364 ink->start_blobs = g_list_prepend (ink->start_blobs,
365 gimp_blob_duplicate (last_blob));
366 blobs_to_render = g_list_prepend (blobs_to_render, last_blob);
367 }
368 ink->start_blobs = g_list_reverse (ink->start_blobs);
369 ink->last_blobs = g_list_reverse (ink->last_blobs);
370 blobs_to_render = g_list_reverse (blobs_to_render);
371 }
372 else
373 {
374 for (i = 0; i < n_strokes; i++)
375 {
376 GimpBlob *blob;
377 GimpBlob *blob_union = NULL;
378 GimpMatrix3 transform;
379
380 coords = gimp_symmetry_get_coords (sym, i);
381
382 gimp_symmetry_get_matrix (sym, i, &transform);
383
384 blob = ink_pen_ellipse (options,
385 coords->x,
386 coords->y,
387 coords->pressure,
388 coords->xtilt,
389 coords->ytilt,
390 coords->velocity * 100,
391 &transform);
392
393 last_blob = g_list_nth_data (ink->last_blobs, i);
394 blob_union = gimp_blob_convex_union (last_blob, blob);
395
396 g_free (last_blob);
397 g_list_nth (ink->last_blobs, i)->data = blob;
398
399 blobs_to_render = g_list_prepend (blobs_to_render, blob_union);
400 blob_unions = g_list_prepend (blob_unions, blob_union);
401 }
402 blobs_to_render = g_list_reverse (blobs_to_render);
403 }
404
405 paint_mode = gimp_context_get_paint_mode (context);
406
407 gimp_context_get_foreground (context, &foreground);
408 gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable),
409 &foreground, &foreground);
410 color = gimp_gegl_color_new (&foreground);
411
412 for (i = 0; i < n_strokes; i++)
413 {
414 GimpBlob *blob_to_render = g_list_nth_data (blobs_to_render, i);
415
416 coords = gimp_symmetry_get_coords (sym, i);
417
418 ink->cur_blob = blob_to_render;
419 paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable,
420 paint_options,
421 paint_mode,
422 coords,
423 &paint_buffer_x,
424 &paint_buffer_y,
425 NULL, NULL);
426 ink->cur_blob = NULL;
427
428 if (! paint_buffer)
429 continue;
430
431 gegl_buffer_set_color (paint_buffer, NULL, color);
432
433 /* draw the blob directly to the canvas_buffer */
434 render_blob (paint_core->canvas_buffer,
435 GEGL_RECTANGLE (paint_core->paint_buffer_x,
436 paint_core->paint_buffer_y,
437 gegl_buffer_get_width (paint_core->paint_buffer),
438 gegl_buffer_get_height (paint_core->paint_buffer)),
439 blob_to_render);
440
441 /* draw the paint_area using the just rendered canvas_buffer as mask */
442 gimp_paint_core_paste (paint_core,
443 NULL,
444 paint_core->paint_buffer_x,
445 paint_core->paint_buffer_y,
446 drawable,
447 GIMP_OPACITY_OPAQUE,
448 gimp_context_get_opacity (context),
449 paint_mode,
450 GIMP_PAINT_CONSTANT);
451
452 }
453
454 g_object_unref (color);
455
456 g_list_free_full (blob_unions, g_free);
457 }
458
459 static GimpBlob *
ink_pen_ellipse(GimpInkOptions * options,gdouble x_center,gdouble y_center,gdouble pressure,gdouble xtilt,gdouble ytilt,gdouble velocity,const GimpMatrix3 * transform)460 ink_pen_ellipse (GimpInkOptions *options,
461 gdouble x_center,
462 gdouble y_center,
463 gdouble pressure,
464 gdouble xtilt,
465 gdouble ytilt,
466 gdouble velocity,
467 const GimpMatrix3 *transform)
468 {
469 GimpBlobFunc blob_function;
470 gdouble size;
471 gdouble tsin, tcos;
472 gdouble aspect, radmin;
473 gdouble x,y;
474 gdouble tscale;
475 gdouble tscale_c;
476 gdouble tscale_s;
477
478 /* Adjust the size depending on pressure. */
479
480 size = options->size * (1.0 + options->size_sensitivity *
481 (2.0 * pressure - 1.0));
482
483 /* Adjust the size further depending on pointer velocity and
484 * velocity-sensitivity. These 'magic constants' are 'feels
485 * natural' tigert-approved. --ADM
486 */
487
488 if (velocity < 3.0)
489 velocity = 3.0;
490
491 #ifdef VERBOSE
492 g_printerr ("%g (%g) -> ", size, velocity);
493 #endif
494
495 size = (options->vel_sensitivity *
496 ((4.5 * size) / (1.0 + options->vel_sensitivity * (2.0 * velocity)))
497 + (1.0 - options->vel_sensitivity) * size);
498
499 #ifdef VERBOSE
500 g_printerr ("%g\n", (gfloat) size);
501 #endif
502
503 /* Clamp resulting size to sane limits */
504
505 if (size > options->size * (1.0 + options->size_sensitivity))
506 size = options->size * (1.0 + options->size_sensitivity);
507
508 if (size * SUBSAMPLE < 1.0)
509 size = 1.0 / SUBSAMPLE;
510
511 /* Add brush angle/aspect to tilt vectorially */
512
513 /* I'm not happy with the way the brush widget info is combined with
514 * tilt info from the brush. My personal feeling is that
515 * representing both as affine transforms would make the most
516 * sense. -RLL
517 */
518
519 tscale = options->tilt_sensitivity * 10.0;
520 tscale_c = tscale * cos (gimp_deg_to_rad (options->tilt_angle));
521 tscale_s = tscale * sin (gimp_deg_to_rad (options->tilt_angle));
522
523 x = (options->blob_aspect * cos (options->blob_angle) +
524 xtilt * tscale_c - ytilt * tscale_s);
525 y = (options->blob_aspect * sin (options->blob_angle) +
526 ytilt * tscale_c + xtilt * tscale_s);
527
528 #ifdef VERBOSE
529 g_printerr ("angle %g aspect %g; %g %g; %g %g\n",
530 options->blob_angle, options->blob_aspect,
531 tscale_c, tscale_s, x, y);
532 #endif
533
534 aspect = sqrt (SQR (x) + SQR (y));
535
536 if (aspect != 0)
537 {
538 tcos = x / aspect;
539 tsin = y / aspect;
540 }
541 else
542 {
543 tcos = cos (options->blob_angle);
544 tsin = sin (options->blob_angle);
545 }
546
547 gimp_matrix3_transform_point (transform,
548 tcos, tsin,
549 &tcos, &tsin);
550
551 aspect = CLAMP (aspect, 1.0, 10.0);
552
553 radmin = MAX (1.0, SUBSAMPLE * size / aspect);
554
555 switch (options->blob_type)
556 {
557 case GIMP_INK_BLOB_TYPE_CIRCLE:
558 blob_function = gimp_blob_ellipse;
559 break;
560
561 case GIMP_INK_BLOB_TYPE_SQUARE:
562 blob_function = gimp_blob_square;
563 break;
564
565 case GIMP_INK_BLOB_TYPE_DIAMOND:
566 blob_function = gimp_blob_diamond;
567 break;
568
569 default:
570 g_return_val_if_reached (NULL);
571 break;
572 }
573
574 return (* blob_function) (x_center * SUBSAMPLE,
575 y_center * SUBSAMPLE,
576 radmin * aspect * tcos,
577 radmin * aspect * tsin,
578 -radmin * tsin,
579 radmin * tcos);
580 }
581
582
583 /*********************************/
584 /* Rendering functions */
585 /*********************************/
586
587 /* Some of this stuff should probably be combined with the
588 * code it was copied from in paint_core.c; but I wanted
589 * to learn this stuff, so I've kept it simple.
590 *
591 * The following only supports CONSTANT mode. Incremental
592 * would, I think, interact strangely with the way we
593 * do things. But it wouldn't be hard to implement at all.
594 */
595
596 enum
597 {
598 ROW_START,
599 ROW_STOP
600 };
601
602 /* The insertion sort here, for SUBSAMPLE = 8, tends to beat out
603 * qsort() by 4x with CFLAGS=-O2, 2x with CFLAGS=-g
604 */
605 static void
insert_sort(gint * data,gint n)606 insert_sort (gint *data,
607 gint n)
608 {
609 gint i, j, k;
610
611 for (i = 2; i < 2 * n; i += 2)
612 {
613 gint tmp1 = data[i];
614 gint tmp2 = data[i + 1];
615
616 j = 0;
617
618 while (data[j] < tmp1)
619 j += 2;
620
621 for (k = i; k > j; k -= 2)
622 {
623 data[k] = data[k - 2];
624 data[k + 1] = data[k - 1];
625 }
626
627 data[j] = tmp1;
628 data[j + 1] = tmp2;
629 }
630 }
631
632 static void
fill_run(gfloat * dest,gfloat alpha,gint w)633 fill_run (gfloat *dest,
634 gfloat alpha,
635 gint w)
636 {
637 if (alpha == 1.0)
638 {
639 while (w--)
640 {
641 *dest = 1.0;
642 dest++;
643 }
644 }
645 else
646 {
647 while (w--)
648 {
649 *dest = MAX (*dest, alpha);
650 dest++;
651 }
652 }
653 }
654
655 static void
render_blob_line(GimpBlob * blob,gfloat * dest,gint x,gint y,gint width)656 render_blob_line (GimpBlob *blob,
657 gfloat *dest,
658 gint x,
659 gint y,
660 gint width)
661 {
662 gint buf[4 * SUBSAMPLE];
663 gint *data = buf;
664 gint n = 0;
665 gint i, j;
666 gint current = 0; /* number of filled rows at this point
667 * in the scan line
668 */
669 gint last_x;
670
671 /* Sort start and ends for all lines */
672
673 j = y * SUBSAMPLE - blob->y;
674 for (i = 0; i < SUBSAMPLE; i++)
675 {
676 if (j >= blob->height)
677 break;
678
679 if ((j > 0) && (blob->data[j].left <= blob->data[j].right))
680 {
681 data[2 * n] = blob->data[j].left;
682 data[2 * n + 1] = ROW_START;
683 data[2 * SUBSAMPLE + 2 * n] = blob->data[j].right;
684 data[2 * SUBSAMPLE + 2 * n + 1] = ROW_STOP;
685 n++;
686 }
687 j++;
688 }
689
690 /* If we have less than SUBSAMPLE rows, compress */
691 if (n < SUBSAMPLE)
692 {
693 for (i = 0; i < 2 * n; i++)
694 data[2 * n + i] = data[2 * SUBSAMPLE + i];
695 }
696
697 /* Now count start and end separately */
698 n *= 2;
699
700 insert_sort (data, n);
701
702 /* Discard portions outside of tile */
703
704 while ((n > 0) && (data[0] < SUBSAMPLE*x))
705 {
706 if (data[1] == ROW_START)
707 current++;
708 else
709 current--;
710 data += 2;
711 n--;
712 }
713
714 while ((n > 0) && (data[2*(n-1)] >= SUBSAMPLE*(x+width)))
715 n--;
716
717 /* Render the row */
718
719 last_x = 0;
720 for (i = 0; i < n;)
721 {
722 gint cur_x = data[2 * i] / SUBSAMPLE - x;
723 gint pixel;
724
725 /* Fill in portion leading up to this pixel */
726 if (current && cur_x != last_x)
727 fill_run (dest + last_x, (gfloat) current / SUBSAMPLE, cur_x - last_x);
728
729 /* Compute the value for this pixel */
730 pixel = current * SUBSAMPLE;
731
732 while (i<n)
733 {
734 gint tmp_x = data[2 * i] / SUBSAMPLE;
735
736 if (tmp_x - x != cur_x)
737 break;
738
739 if (data[2 * i + 1] == ROW_START)
740 {
741 current++;
742 pixel += ((tmp_x + 1) * SUBSAMPLE) - data[2 * i];
743 }
744 else
745 {
746 current--;
747 pixel -= ((tmp_x + 1) * SUBSAMPLE) - data[2 * i];
748 }
749
750 i++;
751 }
752
753 dest[cur_x] = MAX (dest[cur_x], (gfloat) pixel / (SUBSAMPLE * SUBSAMPLE));
754
755 last_x = cur_x + 1;
756 }
757
758 if (current != 0)
759 fill_run (dest + last_x, (gfloat) current / SUBSAMPLE, width - last_x);
760 }
761
762 static void
render_blob(GeglBuffer * buffer,GeglRectangle * rect,GimpBlob * blob)763 render_blob (GeglBuffer *buffer,
764 GeglRectangle *rect,
765 GimpBlob *blob)
766 {
767 GeglBufferIterator *iter;
768 GeglRectangle *roi;
769
770 iter = gegl_buffer_iterator_new (buffer, rect, 0, babl_format ("Y float"),
771 GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
772 roi = &iter->items[0].roi;
773
774 while (gegl_buffer_iterator_next (iter))
775 {
776 gfloat *d = iter->items[0].data;
777 gint h = roi->height;
778 gint y;
779
780 for (y = 0; y < h; y++, d += roi->width * 1)
781 {
782 render_blob_line (blob, d, roi->x, roi->y + y, roi->width);
783 }
784 }
785 }
786