gimppaintcore-stroke.c 11.6 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
2 3
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
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 8 9 10 11 12 13 14
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
15
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 17 18 19
 */

#include "config.h"

20
#include <gdk-pixbuf/gdk-pixbuf.h>
21
#include <gegl.h>
22

23 24
#include "libgimpmath/gimpmath.h"

25 26
#include "paint-types.h"

27
#include "core/gimpboundary.h"
28
#include "core/gimpdrawable.h"
29
#include "core/gimperror.h"
30
#include "core/gimpcoords.h"
31

32 33 34
#include "vectors/gimpstroke.h"
#include "vectors/gimpvectors.h"

35 36
#include "gimppaintcore.h"
#include "gimppaintcore-stroke.h"
37
#include "gimppaintoptions.h"
38

39 40
#include "gimp-intl.h"

41

42
static void gimp_paint_core_stroke_emulate_dynamics (GimpCoords *coords,
Sven Neumann's avatar
Sven Neumann committed
43
                                                     gint        length);
44

45

46 47 48
static const GimpCoords default_coords = GIMP_COORDS_DEFAULT_VALUES;


49
gboolean
50 51 52 53 54
gimp_paint_core_stroke (GimpPaintCore     *core,
                        GimpDrawable      *drawable,
                        GimpPaintOptions  *paint_options,
                        GimpCoords        *strokes,
                        gint               n_strokes,
55
                        gboolean           push_undo,
56
                        GError           **error)
57 58 59
{
  g_return_val_if_fail (GIMP_IS_PAINT_CORE (core), FALSE);
  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
60
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE);
61
  g_return_val_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options), FALSE);
62
  g_return_val_if_fail (strokes != NULL, FALSE);
63
  g_return_val_if_fail (n_strokes > 0, FALSE);
64
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
65

66 67
  if (gimp_paint_core_start (core, drawable, paint_options, &strokes[0],
                             error))
68
    {
69
      gint i;
70

71
      core->last_coords = strokes[0];
72

73
      gimp_paint_core_paint (core, drawable, paint_options,
74
                             GIMP_PAINT_STATE_INIT, 0);
75

76
      gimp_paint_core_paint (core, drawable, paint_options,
77
                             GIMP_PAINT_STATE_MOTION, 0);
78

79 80
      for (i = 1; i < n_strokes; i++)
        {
81 82
          gimp_paint_core_interpolate (core, drawable, paint_options,
                                       &strokes[i], 0);
83
        }
84

85
      gimp_paint_core_paint (core, drawable, paint_options,
86
                             GIMP_PAINT_STATE_FINISH, 0);
87

88
      gimp_paint_core_finish (core, drawable, push_undo);
89 90 91 92 93 94 95 96

      gimp_paint_core_cleanup (core);

      return TRUE;
    }

  return FALSE;
}
97

98
gboolean
99 100 101 102 103 104 105 106 107 108
gimp_paint_core_stroke_boundary (GimpPaintCore      *core,
                                 GimpDrawable       *drawable,
                                 GimpPaintOptions   *paint_options,
                                 gboolean            emulate_dynamics,
                                 const GimpBoundSeg *bound_segs,
                                 gint                n_bound_segs,
                                 gint                offset_x,
                                 gint                offset_y,
                                 gboolean            push_undo,
                                 GError            **error)
109
{
110 111 112 113 114 115 116 117 118
  GimpBoundSeg *stroke_segs;
  gint          n_stroke_segs;
  gint          off_x;
  gint          off_y;
  GimpCoords   *coords;
  gboolean      initialized = FALSE;
  gint          n_coords;
  gint          seg;
  gint          s;
119 120 121

  g_return_val_if_fail (GIMP_IS_PAINT_CORE (core), FALSE);
  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
122
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE);
123 124
  g_return_val_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options), FALSE);
  g_return_val_if_fail (bound_segs != NULL && n_bound_segs > 0, FALSE);
125
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
126

127 128
  stroke_segs = gimp_boundary_sort (bound_segs, n_bound_segs,
                                    &n_stroke_segs);
129 130 131 132

  if (n_stroke_segs == 0)
    return TRUE;

133
  gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
134 135 136 137 138 139 140 141 142 143 144

  off_x -= offset_x;
  off_y -= offset_y;

  coords = g_new0 (GimpCoords, n_bound_segs + 4);

  seg      = 0;
  n_coords = 0;

  /* we offset all coordinates by 0.5 to align the brush with the path */

145 146 147
  coords[n_coords]   = default_coords;
  coords[n_coords].x = (gdouble) (stroke_segs[0].x1 - off_x + 0.5);
  coords[n_coords].y = (gdouble) (stroke_segs[0].y1 - off_y + 0.5);
148 149 150

  n_coords++;

151
  for (s = 0; s < n_stroke_segs; s++)
152 153 154 155 156
    {
      while (stroke_segs[seg].x1 != -1 ||
             stroke_segs[seg].x2 != -1 ||
             stroke_segs[seg].y1 != -1 ||
             stroke_segs[seg].y2 != -1)
157
        {
158 159 160
          coords[n_coords]   = default_coords;
          coords[n_coords].x = (gdouble) (stroke_segs[seg].x1 - off_x + 0.5);
          coords[n_coords].y = (gdouble) (stroke_segs[seg].y1 - off_y + 0.5);
161 162

          n_coords++;
163 164
          seg++;
        }
165 166 167 168 169 170

      /* Close the stroke points up */
      coords[n_coords] = coords[0];

      n_coords++;

171 172 173
      if (emulate_dynamics)
        gimp_paint_core_stroke_emulate_dynamics (coords, n_coords);

Michael Natterer's avatar
Michael Natterer committed
174
      if (initialized ||
175
          gimp_paint_core_start (core, drawable, paint_options, &coords[0],
176
                                 error))
Michael Natterer's avatar
Michael Natterer committed
177
        {
178 179
          gint i;

Michael Natterer's avatar
Michael Natterer committed
180 181
          initialized = TRUE;

182 183
          core->cur_coords  = coords[0];
          core->last_coords = coords[0];
Michael Natterer's avatar
Michael Natterer committed
184

185
          gimp_paint_core_paint (core, drawable, paint_options,
186
                                 GIMP_PAINT_STATE_INIT, 0);
Michael Natterer's avatar
Michael Natterer committed
187

188
          gimp_paint_core_paint (core, drawable, paint_options,
189
                                 GIMP_PAINT_STATE_MOTION, 0);
190

Michael Natterer's avatar
Michael Natterer committed
191 192
          for (i = 1; i < n_coords; i++)
            {
193 194
              gimp_paint_core_interpolate (core, drawable, paint_options,
                                           &coords[i], 0);
Michael Natterer's avatar
Michael Natterer committed
195 196
            }

197
          gimp_paint_core_paint (core, drawable, paint_options,
198
                                 GIMP_PAINT_STATE_FINISH, 0);
Michael Natterer's avatar
Michael Natterer committed
199
        }
200 201 202 203
      else
        {
          break;
        }
204 205 206 207

      n_coords = 0;
      seg++;

208 209 210
      coords[n_coords]   = default_coords;
      coords[n_coords].x = (gdouble) (stroke_segs[seg].x1 - off_x + 0.5);
      coords[n_coords].y = (gdouble) (stroke_segs[seg].y1 - off_y + 0.5);
211 212 213 214

      n_coords++;
    }

Michael Natterer's avatar
Michael Natterer committed
215 216
  if (initialized)
    {
217
      gimp_paint_core_finish (core, drawable, push_undo);
Michael Natterer's avatar
Michael Natterer committed
218 219 220 221

      gimp_paint_core_cleanup (core);
    }

222 223 224
  g_free (coords);
  g_free (stroke_segs);

225
  return initialized;
226 227
}

228
gboolean
229 230 231
gimp_paint_core_stroke_vectors (GimpPaintCore     *core,
                                GimpDrawable      *drawable,
                                GimpPaintOptions  *paint_options,
232
                                gboolean           emulate_dynamics,
233
                                GimpVectors       *vectors,
234
                                gboolean           push_undo,
235
                                GError           **error)
236
{
237 238
  GList    *stroke;
  gboolean  initialized = FALSE;
239
  gboolean  due_to_lack_of_points = FALSE;
240 241
  gint      off_x, off_y;
  gint      vectors_off_x, vectors_off_y;
242 243 244

  g_return_val_if_fail (GIMP_IS_PAINT_CORE (core), FALSE);
  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
245
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE);
246
  g_return_val_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options), FALSE);
247
  g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE);
248
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
249

250 251
  gimp_item_get_offset (GIMP_ITEM (vectors),  &vectors_off_x, &vectors_off_y);
  gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
252 253 254 255

  off_x -= vectors_off_x;
  off_y -= vectors_off_y;

256 257 258
  for (stroke = vectors->strokes->head;
       stroke;
       stroke = stroke->next)
259
    {
260 261 262
      GArray   *coords;
      gboolean  closed;

263 264
      coords = gimp_stroke_interpolate (GIMP_STROKE (stroke->data),
                                        1.0, &closed);
265

266
      if (coords && coords->len)
267
        {
268
          gint i;
269

270
          for (i = 0; i < coords->len; i++)
271
            {
272 273
              g_array_index (coords, GimpCoords, i).x -= off_x;
              g_array_index (coords, GimpCoords, i).y -= off_y;
274
            }
275

276
          if (emulate_dynamics)
277 278
            gimp_paint_core_stroke_emulate_dynamics ((GimpCoords *) coords->data,
                                                     coords->len);
279

Michael Natterer's avatar
Michael Natterer committed
280 281
          if (initialized ||
              gimp_paint_core_start (core, drawable, paint_options,
282
                                     &g_array_index (coords, GimpCoords, 0),
283
                                     error))
284 285
            {
              initialized = TRUE;
286

287 288
              core->cur_coords  = g_array_index (coords, GimpCoords, 0);
              core->last_coords = g_array_index (coords, GimpCoords, 0);
289

290
              gimp_paint_core_paint (core, drawable, paint_options,
291
                                     GIMP_PAINT_STATE_INIT, 0);
292

Michael Natterer's avatar
Michael Natterer committed
293
              gimp_paint_core_paint (core, drawable, paint_options,
294
                                     GIMP_PAINT_STATE_MOTION, 0);
295

Michael Natterer's avatar
Michael Natterer committed
296 297
              for (i = 1; i < coords->len; i++)
                {
298 299 300
                  gimp_paint_core_interpolate (core, drawable, paint_options,
                                               &g_array_index (coords, GimpCoords, i),
                                               0);
Michael Natterer's avatar
Michael Natterer committed
301
                }
302

Michael Natterer's avatar
Michael Natterer committed
303
              gimp_paint_core_paint (core, drawable, paint_options,
304
                                     GIMP_PAINT_STATE_FINISH, 0);
Michael Natterer's avatar
Michael Natterer committed
305
            }
306 307 308 309 310 311 312
          else
            {
              if (coords)
                g_array_free (coords, TRUE);

              break;
            }
313
        }
314 315 316 317
      else
        {
          due_to_lack_of_points = TRUE;
        }
318

319 320
      if (coords)
        g_array_free (coords, TRUE);
321
    }
322

323 324
  if (initialized)
    {
325
      gimp_paint_core_finish (core, drawable, push_undo);
326

327 328
      gimp_paint_core_cleanup (core);
    }
329

330
  if (! initialized && due_to_lack_of_points && *error == NULL)
331
    {
332
      g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
333
                           _("Not enough points to stroke"));
334 335
    }

336
  return initialized;
337
}
338 339

static void
340
gimp_paint_core_stroke_emulate_dynamics (GimpCoords *coords,
Sven Neumann's avatar
Sven Neumann committed
341
                                         gint        length)
342
{
Sven Neumann's avatar
Sven Neumann committed
343
  const gint ramp_length = length / 3;
344 345

  /* Calculate and create pressure ramp parameters */
Sven Neumann's avatar
Sven Neumann committed
346
  if (ramp_length > 0)
347
    {
Sven Neumann's avatar
Sven Neumann committed
348
      gdouble slope = 1.0 / (gdouble) (ramp_length);
349 350 351
      gint    i;

      /* Calculate pressure start ramp */
Sven Neumann's avatar
Sven Neumann committed
352
      for (i = 0; i < ramp_length; i++)
353
        {
Sven Neumann's avatar
Sven Neumann committed
354
          coords[i].pressure =  i * slope;
355
        }
356

357
      /* Calculate pressure end ramp */
Sven Neumann's avatar
Sven Neumann committed
358
      for (i = length - ramp_length; i < length; i++)
359
        {
Sven Neumann's avatar
Sven Neumann committed
360
          coords[i].pressure = 1.0 - (i - (length - ramp_length)) * slope;
361 362 363 364
        }
    }

  /* Calculate and create velocity ramp parameters */
Sven Neumann's avatar
Sven Neumann committed
365
  if (length > 0)
366
    {
Sven Neumann's avatar
Sven Neumann committed
367
      gdouble slope = 1.0 / length;
368 369 370
      gint    i;

      /* Calculate velocity end ramp */
Sven Neumann's avatar
Sven Neumann committed
371
      for (i = 0; i < length; i++)
372
        {
Sven Neumann's avatar
Sven Neumann committed
373
          coords[i].velocity = i * slope;
374 375
        }
    }
376

377
  if (length > 1)
378 379 380
    {
      gint i;
      /* Fill in direction */
381
      for (i = 1; i < length; i++)
382
        {
Jehan's avatar
Jehan committed
383
          coords[i].direction = gimp_coords_direction (&coords[i-1], &coords[i]);
384 385
        }

386
      coords[0].direction = coords[1].direction;
387
    }
388
}