~elementary-os/elementaryos/os-patch-mutter-bionic

« back to all changes in this revision

Viewing changes to cogl/cogl-path/cogl-path.c

  • Committer: RabbitBot
  • Date: 2018-04-11 14:49:36 UTC
  • Revision ID: rabbitbot@elementary.io-20180411144936-hgymqa9d8d1xfpbh
Initial import, version 3.28.0-2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Cogl
 
3
 *
 
4
 * A Low Level GPU Graphics and Utilities API
 
5
 *
 
6
 * Copyright (C) 2007,2008,2009,2010,2013 Intel Corporation.
 
7
 *
 
8
 * Permission is hereby granted, free of charge, to any person
 
9
 * obtaining a copy of this software and associated documentation
 
10
 * files (the "Software"), to deal in the Software without
 
11
 * restriction, including without limitation the rights to use, copy,
 
12
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 
13
 * of the Software, and to permit persons to whom the Software is
 
14
 * furnished to do so, subject to the following conditions:
 
15
 *
 
16
 * The above copyright notice and this permission notice shall be
 
17
 * included in all copies or substantial portions of the Software.
 
18
 *
 
19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
20
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
21
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
22
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 
23
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 
24
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 
25
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
26
 * SOFTWARE.
 
27
 *
 
28
 * Authors:
 
29
 *  Ivan Leben    <ivan@openedhand.com>
 
30
 *  Øyvind Kolås  <pippin@linux.intel.com>
 
31
 *  Neil Roberts  <neil@linux.intel.com>
 
32
 *  Robert Bragg  <robert@linux.intel.com>
 
33
 */
 
34
 
 
35
#include "cogl-config.h"
 
36
 
 
37
#include "cogl-util.h"
 
38
#include "cogl-object.h"
 
39
#include "cogl-context-private.h"
 
40
#include "cogl-journal-private.h"
 
41
#include "cogl-pipeline-private.h"
 
42
#include "cogl-framebuffer-private.h"
 
43
#include "cogl-primitive-private.h"
 
44
#include "cogl-texture-private.h"
 
45
#include "cogl-primitives-private.h"
 
46
#include "cogl-private.h"
 
47
#include "cogl-attribute-private.h"
 
48
#include "cogl1-context.h"
 
49
#include "tesselator/tesselator.h"
 
50
 
 
51
#include "cogl-path/cogl-path.h"
 
52
#include "cogl-path-private.h"
 
53
#include "cogl-gtype-private.h"
 
54
 
 
55
#include <string.h>
 
56
#include <math.h>
 
57
 
 
58
#define _COGL_MAX_BEZ_RECURSE_DEPTH 16
 
59
 
 
60
static void _cogl_path_free (CoglPath *path);
 
61
 
 
62
static void _cogl_path_build_fill_attribute_buffer (CoglPath *path);
 
63
static CoglPrimitive *_cogl_path_get_fill_primitive (CoglPath *path);
 
64
static void _cogl_path_build_stroke_attribute_buffer (CoglPath *path);
 
65
 
 
66
COGL_OBJECT_DEFINE (Path, path);
 
67
COGL_GTYPE_DEFINE_CLASS (Path, path);
 
68
 
 
69
static void
 
70
_cogl_path_data_clear_vbos (CoglPathData *data)
 
71
{
 
72
  int i;
 
73
 
 
74
  if (data->fill_attribute_buffer)
 
75
    {
 
76
      cogl_object_unref (data->fill_attribute_buffer);
 
77
      cogl_object_unref (data->fill_vbo_indices);
 
78
 
 
79
      for (i = 0; i < COGL_PATH_N_ATTRIBUTES; i++)
 
80
        cogl_object_unref (data->fill_attributes[i]);
 
81
 
 
82
      data->fill_attribute_buffer = NULL;
 
83
    }
 
84
 
 
85
  if (data->fill_primitive)
 
86
    {
 
87
      cogl_object_unref (data->fill_primitive);
 
88
      data->fill_primitive = NULL;
 
89
    }
 
90
 
 
91
  if (data->stroke_attribute_buffer)
 
92
    {
 
93
      cogl_object_unref (data->stroke_attribute_buffer);
 
94
 
 
95
      for (i = 0; i < data->stroke_n_attributes; i++)
 
96
        cogl_object_unref (data->stroke_attributes[i]);
 
97
 
 
98
      g_free (data->stroke_attributes);
 
99
 
 
100
      data->stroke_attribute_buffer = NULL;
 
101
    }
 
102
}
 
103
 
 
104
static void
 
105
_cogl_path_data_unref (CoglPathData *data)
 
106
{
 
107
  if (--data->ref_count <= 0)
 
108
    {
 
109
      _cogl_path_data_clear_vbos (data);
 
110
 
 
111
      g_array_free (data->path_nodes, TRUE);
 
112
 
 
113
      g_slice_free (CoglPathData, data);
 
114
    }
 
115
}
 
116
 
 
117
static void
 
118
_cogl_path_modify (CoglPath *path)
 
119
{
 
120
  /* This needs to be called whenever the path is about to be modified
 
121
     to implement copy-on-write semantics */
 
122
 
 
123
  /* If there is more than one path using the data then we need to
 
124
     copy the data instead */
 
125
  if (path->data->ref_count != 1)
 
126
    {
 
127
      CoglPathData *old_data = path->data;
 
128
 
 
129
      path->data = g_slice_dup (CoglPathData, old_data);
 
130
      path->data->path_nodes = g_array_new (FALSE, FALSE,
 
131
                                            sizeof (CoglPathNode));
 
132
      g_array_append_vals (path->data->path_nodes,
 
133
                           old_data->path_nodes->data,
 
134
                           old_data->path_nodes->len);
 
135
 
 
136
      path->data->fill_attribute_buffer = NULL;
 
137
      path->data->fill_primitive = NULL;
 
138
      path->data->stroke_attribute_buffer = NULL;
 
139
      path->data->ref_count = 1;
 
140
 
 
141
      _cogl_path_data_unref (old_data);
 
142
    }
 
143
  else
 
144
    /* The path is altered so the vbos will now be invalid */
 
145
    _cogl_path_data_clear_vbos (path->data);
 
146
}
 
147
 
 
148
void
 
149
cogl2_path_set_fill_rule (CoglPath *path,
 
150
                          CoglPathFillRule fill_rule)
 
151
{
 
152
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
153
 
 
154
  if (path->data->fill_rule != fill_rule)
 
155
    {
 
156
      _cogl_path_modify (path);
 
157
 
 
158
      path->data->fill_rule = fill_rule;
 
159
    }
 
160
}
 
161
 
 
162
CoglPathFillRule
 
163
cogl2_path_get_fill_rule (CoglPath *path)
 
164
{
 
165
  _COGL_RETURN_VAL_IF_FAIL (cogl_is_path (path), COGL_PATH_FILL_RULE_NON_ZERO);
 
166
 
 
167
  return path->data->fill_rule;
 
168
}
 
169
 
 
170
static void
 
171
_cogl_path_add_node (CoglPath *path,
 
172
                     CoglBool new_sub_path,
 
173
                     float x,
 
174
                     float y)
 
175
{
 
176
  CoglPathNode new_node;
 
177
  CoglPathData *data;
 
178
 
 
179
  _cogl_path_modify (path);
 
180
 
 
181
  data = path->data;
 
182
 
 
183
  new_node.x = x;
 
184
  new_node.y = y;
 
185
  new_node.path_size = 0;
 
186
 
 
187
  if (new_sub_path || data->path_nodes->len == 0)
 
188
    data->last_path = data->path_nodes->len;
 
189
 
 
190
  g_array_append_val (data->path_nodes, new_node);
 
191
 
 
192
  g_array_index (data->path_nodes, CoglPathNode, data->last_path).path_size++;
 
193
 
 
194
  if (data->path_nodes->len == 1)
 
195
    {
 
196
      data->path_nodes_min.x = data->path_nodes_max.x = x;
 
197
      data->path_nodes_min.y = data->path_nodes_max.y = y;
 
198
    }
 
199
  else
 
200
    {
 
201
      if (x < data->path_nodes_min.x)
 
202
        data->path_nodes_min.x = x;
 
203
      if (x > data->path_nodes_max.x)
 
204
        data->path_nodes_max.x = x;
 
205
      if (y < data->path_nodes_min.y)
 
206
        data->path_nodes_min.y = y;
 
207
      if (y > data->path_nodes_max.y)
 
208
        data->path_nodes_max.y = y;
 
209
    }
 
210
 
 
211
  /* Once the path nodes have been modified then we'll assume it's no
 
212
     longer a rectangle. cogl2_path_rectangle will set this back to
 
213
     TRUE if this has been called from there */
 
214
  data->is_rectangle = FALSE;
 
215
}
 
216
 
 
217
static void
 
218
_cogl_path_stroke_nodes (CoglPath *path,
 
219
                         CoglFramebuffer *framebuffer,
 
220
                         CoglPipeline *pipeline)
 
221
{
 
222
  CoglPathData *data;
 
223
  CoglPipeline *copy = NULL;
 
224
  unsigned int path_start;
 
225
  int path_num = 0;
 
226
  CoglPathNode *node;
 
227
 
 
228
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
229
  _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (framebuffer));
 
230
  _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline));
 
231
 
 
232
  data = path->data;
 
233
 
 
234
  if (data->path_nodes->len == 0)
 
235
    return;
 
236
 
 
237
  if (cogl_pipeline_get_n_layers (pipeline) != 0)
 
238
    {
 
239
      copy = cogl_pipeline_copy (pipeline);
 
240
      _cogl_pipeline_prune_to_n_layers (copy, 0);
 
241
      pipeline = copy;
 
242
    }
 
243
 
 
244
  _cogl_path_build_stroke_attribute_buffer (path);
 
245
 
 
246
  for (path_start = 0;
 
247
       path_start < data->path_nodes->len;
 
248
       path_start += node->path_size)
 
249
    {
 
250
      CoglPrimitive *primitive;
 
251
 
 
252
      node = &g_array_index (data->path_nodes, CoglPathNode, path_start);
 
253
 
 
254
      primitive =
 
255
        cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_LINE_STRIP,
 
256
                                            node->path_size,
 
257
                                            &data->stroke_attributes[path_num],
 
258
                                            1);
 
259
      cogl_primitive_draw (primitive, framebuffer, pipeline);
 
260
      cogl_object_unref (primitive);
 
261
 
 
262
      path_num++;
 
263
    }
 
264
 
 
265
  if (copy)
 
266
    cogl_object_unref (copy);
 
267
}
 
268
 
 
269
void
 
270
_cogl_path_get_bounds (CoglPath *path,
 
271
                       float *min_x,
 
272
                       float *min_y,
 
273
                       float *max_x,
 
274
                       float *max_y)
 
275
{
 
276
  CoglPathData *data = path->data;
 
277
 
 
278
  if (data->path_nodes->len == 0)
 
279
    {
 
280
      *min_x = 0.0f;
 
281
      *min_y = 0.0f;
 
282
      *max_x = 0.0f;
 
283
      *max_y = 0.0f;
 
284
    }
 
285
  else
 
286
    {
 
287
      *min_x = data->path_nodes_min.x;
 
288
      *min_y = data->path_nodes_min.y;
 
289
      *max_x = data->path_nodes_max.x;
 
290
      *max_y = data->path_nodes_max.y;
 
291
    }
 
292
}
 
293
 
 
294
static void
 
295
_cogl_path_fill_nodes_with_clipped_rectangle (CoglPath *path,
 
296
                                              CoglFramebuffer *framebuffer,
 
297
                                              CoglPipeline *pipeline)
 
298
{
 
299
  /* We need at least three stencil bits to combine clips */
 
300
  if (_cogl_framebuffer_get_stencil_bits (framebuffer) >= 3)
 
301
    {
 
302
      static CoglBool seen_warning = FALSE;
 
303
 
 
304
      if (!seen_warning)
 
305
        {
 
306
          g_warning ("Paths can not be filled using materials with "
 
307
                     "sliced textures unless there is a stencil "
 
308
                     "buffer");
 
309
          seen_warning = TRUE;
 
310
        }
 
311
    }
 
312
 
 
313
  cogl_framebuffer_push_path_clip (framebuffer, path);
 
314
  cogl_framebuffer_draw_rectangle (framebuffer,
 
315
                                   pipeline,
 
316
                                   path->data->path_nodes_min.x,
 
317
                                   path->data->path_nodes_min.y,
 
318
                                   path->data->path_nodes_max.x,
 
319
                                   path->data->path_nodes_max.y);
 
320
  cogl_framebuffer_pop_clip (framebuffer);
 
321
}
 
322
 
 
323
static CoglBool
 
324
validate_layer_cb (CoglPipelineLayer *layer, void *user_data)
 
325
{
 
326
  CoglBool *needs_fallback = user_data;
 
327
  CoglTexture *texture = _cogl_pipeline_layer_get_texture (layer);
 
328
 
 
329
  /* If any of the layers of the current pipeline contain sliced
 
330
   * textures or textures with waste then it won't work to draw the
 
331
   * path directly. Instead we fallback to pushing the path as a clip
 
332
   * on the clip-stack and drawing the path's bounding rectangle
 
333
   * instead.
 
334
   */
 
335
 
 
336
  if (texture != NULL && (cogl_texture_is_sliced (texture) ||
 
337
                          !_cogl_texture_can_hardware_repeat (texture)))
 
338
    *needs_fallback = TRUE;
 
339
 
 
340
  return !*needs_fallback;
 
341
}
 
342
 
 
343
static void
 
344
_cogl_path_fill_nodes (CoglPath *path,
 
345
                       CoglFramebuffer *framebuffer,
 
346
                       CoglPipeline *pipeline,
 
347
                       CoglDrawFlags flags)
 
348
{
 
349
  if (path->data->path_nodes->len == 0)
 
350
    return;
 
351
 
 
352
  /* If the path is a simple rectangle then we can divert to using
 
353
     cogl_framebuffer_draw_rectangle which should be faster because it
 
354
     can go through the journal instead of uploading the geometry just
 
355
     for two triangles */
 
356
  if (path->data->is_rectangle && flags == 0)
 
357
    {
 
358
      float x_1, y_1, x_2, y_2;
 
359
 
 
360
      _cogl_path_get_bounds (path, &x_1, &y_1, &x_2, &y_2);
 
361
      cogl_framebuffer_draw_rectangle (framebuffer,
 
362
                                       pipeline,
 
363
                                       x_1, y_1,
 
364
                                       x_2, y_2);
 
365
    }
 
366
  else
 
367
    {
 
368
      CoglBool needs_fallback = FALSE;
 
369
      CoglPrimitive *primitive;
 
370
 
 
371
      _cogl_pipeline_foreach_layer_internal (pipeline,
 
372
                                             validate_layer_cb,
 
373
                                             &needs_fallback);
 
374
      if (needs_fallback)
 
375
        {
 
376
          _cogl_path_fill_nodes_with_clipped_rectangle (path,
 
377
                                                        framebuffer,
 
378
                                                        pipeline);
 
379
          return;
 
380
        }
 
381
 
 
382
      primitive = _cogl_path_get_fill_primitive (path);
 
383
 
 
384
      _cogl_primitive_draw (primitive, framebuffer, pipeline, flags);
 
385
    }
 
386
}
 
387
 
 
388
/* TODO: Update to the protoype used in the Cogl master branch.
 
389
 * This is experimental API but not in sync with the cogl_path_fill()
 
390
 * api in Cogl master which takes explicit framebuffer and pipeline
 
391
 * arguments */
 
392
void
 
393
cogl2_path_fill (CoglPath *path)
 
394
{
 
395
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
396
 
 
397
  _cogl_path_fill_nodes (path,
 
398
                         cogl_get_draw_framebuffer (),
 
399
                         cogl_get_source (),
 
400
                         0 /* flags */);
 
401
}
 
402
 
 
403
/* TODO: Update to the protoype used in the Cogl master branch.
 
404
 * This is experimental API but not in sync with the cogl_path_fill()
 
405
 * api in Cogl master which takes explicit framebuffer and pipeline
 
406
 * arguments */
 
407
void
 
408
cogl2_path_stroke (CoglPath *path)
 
409
{
 
410
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
411
 
 
412
  if (path->data->path_nodes->len == 0)
 
413
    return;
 
414
 
 
415
  _cogl_path_stroke_nodes (path,
 
416
                           cogl_get_draw_framebuffer (),
 
417
                           cogl_get_source ());
 
418
}
 
419
 
 
420
void
 
421
cogl2_path_move_to (CoglPath *path,
 
422
                    float x,
 
423
                    float y)
 
424
{
 
425
  CoglPathData *data;
 
426
 
 
427
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
428
 
 
429
  _cogl_path_add_node (path, TRUE, x, y);
 
430
 
 
431
  data = path->data;
 
432
 
 
433
  data->path_start.x = x;
 
434
  data->path_start.y = y;
 
435
 
 
436
  data->path_pen = data->path_start;
 
437
}
 
438
 
 
439
void
 
440
cogl2_path_rel_move_to (CoglPath *path,
 
441
                        float x,
 
442
                        float y)
 
443
{
 
444
  CoglPathData *data;
 
445
 
 
446
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
447
 
 
448
  data = path->data;
 
449
 
 
450
  cogl2_path_move_to (path,
 
451
                      data->path_pen.x + x,
 
452
                      data->path_pen.y + y);
 
453
}
 
454
 
 
455
void
 
456
cogl2_path_line_to (CoglPath *path,
 
457
                    float x,
 
458
                    float y)
 
459
{
 
460
  CoglPathData *data;
 
461
 
 
462
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
463
 
 
464
  _cogl_path_add_node (path, FALSE, x, y);
 
465
 
 
466
  data = path->data;
 
467
 
 
468
  data->path_pen.x = x;
 
469
  data->path_pen.y = y;
 
470
}
 
471
 
 
472
void
 
473
cogl2_path_rel_line_to (CoglPath *path,
 
474
                        float x,
 
475
                        float y)
 
476
{
 
477
  CoglPathData *data;
 
478
 
 
479
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
480
 
 
481
  data = path->data;
 
482
 
 
483
  cogl2_path_line_to (path,
 
484
                      data->path_pen.x + x,
 
485
                      data->path_pen.y + y);
 
486
}
 
487
 
 
488
void
 
489
cogl2_path_close (CoglPath *path)
 
490
{
 
491
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
492
 
 
493
  _cogl_path_add_node (path, FALSE, path->data->path_start.x,
 
494
                       path->data->path_start.y);
 
495
 
 
496
  path->data->path_pen = path->data->path_start;
 
497
}
 
498
 
 
499
void
 
500
cogl2_path_line (CoglPath *path,
 
501
                 float x_1,
 
502
                 float y_1,
 
503
                 float x_2,
 
504
                 float y_2)
 
505
{
 
506
  cogl2_path_move_to (path, x_1, y_1);
 
507
  cogl2_path_line_to (path, x_2, y_2);
 
508
}
 
509
 
 
510
void
 
511
cogl2_path_polyline (CoglPath *path,
 
512
                     const float *coords,
 
513
                     int num_points)
 
514
{
 
515
  int c = 0;
 
516
 
 
517
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
518
 
 
519
  cogl2_path_move_to (path, coords[0], coords[1]);
 
520
 
 
521
  for (c = 1; c < num_points; ++c)
 
522
    cogl2_path_line_to (path, coords[2*c], coords[2*c+1]);
 
523
}
 
524
 
 
525
void
 
526
cogl2_path_polygon (CoglPath *path,
 
527
                    const float *coords,
 
528
                    int num_points)
 
529
{
 
530
  cogl2_path_polyline (path, coords, num_points);
 
531
  cogl2_path_close (path);
 
532
}
 
533
 
 
534
void
 
535
cogl2_path_rectangle (CoglPath *path,
 
536
                      float x_1,
 
537
                      float y_1,
 
538
                      float x_2,
 
539
                      float y_2)
 
540
{
 
541
  CoglBool is_rectangle;
 
542
 
 
543
  /* If the path was previously empty and the rectangle isn't mirrored
 
544
     then we'll record that this is a simple rectangle path so that we
 
545
     can optimise it */
 
546
  is_rectangle = (path->data->path_nodes->len == 0 &&
 
547
                  x_2 >= x_1 &&
 
548
                  y_2 >= y_1);
 
549
 
 
550
  cogl2_path_move_to (path, x_1, y_1);
 
551
  cogl2_path_line_to (path, x_2, y_1);
 
552
  cogl2_path_line_to (path, x_2, y_2);
 
553
  cogl2_path_line_to (path, x_1, y_2);
 
554
  cogl2_path_close (path);
 
555
 
 
556
  path->data->is_rectangle = is_rectangle;
 
557
}
 
558
 
 
559
CoglBool
 
560
_cogl_path_is_rectangle (CoglPath *path)
 
561
{
 
562
  return path->data->is_rectangle;
 
563
}
 
564
 
 
565
static void
 
566
_cogl_path_arc (CoglPath *path,
 
567
                float center_x,
 
568
                float center_y,
 
569
                float radius_x,
 
570
                float radius_y,
 
571
                float angle_1,
 
572
                float angle_2,
 
573
                float angle_step,
 
574
                unsigned int move_first)
 
575
{
 
576
  float a = 0x0;
 
577
  float cosa = 0x0;
 
578
  float sina = 0x0;
 
579
  float px = 0x0;
 
580
  float py = 0x0;
 
581
 
 
582
  /* Fix invalid angles */
 
583
 
 
584
  if (angle_1 == angle_2 || angle_step == 0x0)
 
585
    return;
 
586
 
 
587
  if (angle_step < 0x0)
 
588
    angle_step = -angle_step;
 
589
 
 
590
  /* Walk the arc by given step */
 
591
 
 
592
  a = angle_1;
 
593
  while (a != angle_2)
 
594
    {
 
595
      cosa = cosf (a * (G_PI/180.0));
 
596
      sina = sinf (a * (G_PI/180.0));
 
597
 
 
598
      px = center_x + (cosa * radius_x);
 
599
      py = center_y + (sina * radius_y);
 
600
 
 
601
      if (a == angle_1 && move_first)
 
602
        cogl2_path_move_to (path, px, py);
 
603
      else
 
604
        cogl2_path_line_to (path, px, py);
 
605
 
 
606
      if (G_LIKELY (angle_2 > angle_1))
 
607
        {
 
608
          a += angle_step;
 
609
          if (a > angle_2)
 
610
            a = angle_2;
 
611
        }
 
612
      else
 
613
        {
 
614
          a -= angle_step;
 
615
          if (a < angle_2)
 
616
            a = angle_2;
 
617
        }
 
618
    }
 
619
 
 
620
  /* Make sure the final point is drawn */
 
621
 
 
622
  cosa = cosf (angle_2 * (G_PI/180.0));
 
623
  sina = sinf (angle_2 * (G_PI/180.0));
 
624
 
 
625
  px = center_x + (cosa * radius_x);
 
626
  py = center_y + (sina * radius_y);
 
627
 
 
628
  cogl2_path_line_to (path, px, py);
 
629
}
 
630
 
 
631
void
 
632
cogl2_path_arc (CoglPath *path,
 
633
                float center_x,
 
634
                float center_y,
 
635
                float radius_x,
 
636
                float radius_y,
 
637
                float angle_1,
 
638
                float angle_2)
 
639
{
 
640
  float angle_step = 10;
 
641
 
 
642
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
643
 
 
644
  /* it is documented that a move to is needed to create a freestanding
 
645
   * arc
 
646
   */
 
647
  _cogl_path_arc (path,
 
648
                  center_x, center_y,
 
649
                  radius_x, radius_y,
 
650
                  angle_1, angle_2,
 
651
                  angle_step, 0 /* no move */);
 
652
}
 
653
 
 
654
 
 
655
static void
 
656
_cogl_path_rel_arc (CoglPath *path,
 
657
                    float center_x,
 
658
                    float center_y,
 
659
                    float radius_x,
 
660
                    float radius_y,
 
661
                    float angle_1,
 
662
                    float angle_2,
 
663
                    float angle_step)
 
664
{
 
665
  CoglPathData *data;
 
666
 
 
667
  data = path->data;
 
668
 
 
669
  _cogl_path_arc (path,
 
670
                  data->path_pen.x + center_x,
 
671
                  data->path_pen.y + center_y,
 
672
                  radius_x, radius_y,
 
673
                  angle_1, angle_2,
 
674
                  angle_step, 0 /* no move */);
 
675
}
 
676
 
 
677
void
 
678
cogl2_path_ellipse (CoglPath *path,
 
679
                    float center_x,
 
680
                    float center_y,
 
681
                    float radius_x,
 
682
                    float radius_y)
 
683
{
 
684
  float angle_step = 10;
 
685
 
 
686
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
687
 
 
688
  /* FIXME: if shows to be slow might be optimized
 
689
   * by mirroring just a quarter of it */
 
690
 
 
691
  _cogl_path_arc (path,
 
692
                  center_x, center_y,
 
693
                  radius_x, radius_y,
 
694
                  0, 360,
 
695
                  angle_step, 1 /* move first */);
 
696
 
 
697
  cogl2_path_close (path);
 
698
}
 
699
 
 
700
void
 
701
cogl2_path_round_rectangle (CoglPath *path,
 
702
                            float x_1,
 
703
                            float y_1,
 
704
                            float x_2,
 
705
                            float y_2,
 
706
                            float radius,
 
707
                            float arc_step)
 
708
{
 
709
  float inner_width = x_2 - x_1 - radius * 2;
 
710
  float inner_height = y_2 - y_1 - radius * 2;
 
711
 
 
712
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
713
 
 
714
  cogl2_path_move_to (path, x_1, y_1 + radius);
 
715
  _cogl_path_rel_arc (path,
 
716
                      radius, 0,
 
717
                      radius, radius,
 
718
                      180,
 
719
                      270,
 
720
                      arc_step);
 
721
 
 
722
  cogl2_path_line_to (path,
 
723
                     path->data->path_pen.x + inner_width,
 
724
                     path->data->path_pen.y);
 
725
  _cogl_path_rel_arc (path,
 
726
                      0, radius,
 
727
                      radius, radius,
 
728
                      -90,
 
729
                      0,
 
730
                      arc_step);
 
731
 
 
732
  cogl2_path_line_to (path,
 
733
                     path->data->path_pen.x,
 
734
                     path->data->path_pen.y + inner_height);
 
735
 
 
736
  _cogl_path_rel_arc (path,
 
737
                      -radius, 0,
 
738
                      radius, radius,
 
739
                      0,
 
740
                      90,
 
741
                      arc_step);
 
742
 
 
743
  cogl2_path_line_to (path,
 
744
                     path->data->path_pen.x - inner_width,
 
745
                     path->data->path_pen.y);
 
746
  _cogl_path_rel_arc (path,
 
747
                      0, -radius,
 
748
                      radius, radius,
 
749
                      90,
 
750
                      180,
 
751
                      arc_step);
 
752
 
 
753
  cogl2_path_close (path);
 
754
}
 
755
 
 
756
static void
 
757
_cogl_path_bezier3_sub (CoglPath *path,
 
758
                        CoglBezCubic *cubic)
 
759
{
 
760
  CoglBezCubic cubics[_COGL_MAX_BEZ_RECURSE_DEPTH];
 
761
  CoglBezCubic *cleft;
 
762
  CoglBezCubic *cright;
 
763
  CoglBezCubic *c;
 
764
  floatVec2 dif1;
 
765
  floatVec2 dif2;
 
766
  floatVec2 mm;
 
767
  floatVec2 c1;
 
768
  floatVec2 c2;
 
769
  floatVec2 c3;
 
770
  floatVec2 c4;
 
771
  floatVec2 c5;
 
772
  int cindex;
 
773
 
 
774
  /* Put first curve on stack */
 
775
  cubics[0] = *cubic;
 
776
  cindex =  0;
 
777
 
 
778
  while (cindex >= 0)
 
779
    {
 
780
      c = &cubics[cindex];
 
781
 
 
782
 
 
783
      /* Calculate distance of control points from their
 
784
       * counterparts on the line between end points */
 
785
      dif1.x = (c->p2.x * 3) - (c->p1.x * 2) - c->p4.x;
 
786
      dif1.y = (c->p2.y * 3) - (c->p1.y * 2) - c->p4.y;
 
787
      dif2.x = (c->p3.x * 3) - (c->p4.x * 2) - c->p1.x;
 
788
      dif2.y = (c->p3.y * 3) - (c->p4.y * 2) - c->p1.y;
 
789
 
 
790
      if (dif1.x < 0)
 
791
        dif1.x = -dif1.x;
 
792
      if (dif1.y < 0)
 
793
        dif1.y = -dif1.y;
 
794
      if (dif2.x < 0)
 
795
        dif2.x = -dif2.x;
 
796
      if (dif2.y < 0)
 
797
        dif2.y = -dif2.y;
 
798
 
 
799
 
 
800
      /* Pick the greatest of two distances */
 
801
      if (dif1.x < dif2.x) dif1.x = dif2.x;
 
802
      if (dif1.y < dif2.y) dif1.y = dif2.y;
 
803
 
 
804
      /* Cancel if the curve is flat enough */
 
805
      if (dif1.x + dif1.y <= 1.0 ||
 
806
          cindex == _COGL_MAX_BEZ_RECURSE_DEPTH-1)
 
807
        {
 
808
          /* Add subdivision point (skip last) */
 
809
          if (cindex == 0)
 
810
            return;
 
811
 
 
812
          _cogl_path_add_node (path, FALSE, c->p4.x, c->p4.y);
 
813
 
 
814
          --cindex;
 
815
 
 
816
          continue;
 
817
        }
 
818
 
 
819
      /* Left recursion goes on top of stack! */
 
820
      cright = c; cleft = &cubics[++cindex];
 
821
 
 
822
      /* Subdivide into 2 sub-curves */
 
823
      c1.x = ((c->p1.x + c->p2.x) / 2);
 
824
      c1.y = ((c->p1.y + c->p2.y) / 2);
 
825
      mm.x = ((c->p2.x + c->p3.x) / 2);
 
826
      mm.y = ((c->p2.y + c->p3.y) / 2);
 
827
      c5.x = ((c->p3.x + c->p4.x) / 2);
 
828
      c5.y = ((c->p3.y + c->p4.y) / 2);
 
829
 
 
830
      c2.x = ((c1.x + mm.x) / 2);
 
831
      c2.y = ((c1.y + mm.y) / 2);
 
832
      c4.x = ((mm.x + c5.x) / 2);
 
833
      c4.y = ((mm.y + c5.y) / 2);
 
834
 
 
835
      c3.x = ((c2.x + c4.x) / 2);
 
836
      c3.y = ((c2.y + c4.y) / 2);
 
837
 
 
838
      /* Add left recursion to stack */
 
839
      cleft->p1 = c->p1;
 
840
      cleft->p2 = c1;
 
841
      cleft->p3 = c2;
 
842
      cleft->p4 = c3;
 
843
 
 
844
      /* Add right recursion to stack */
 
845
      cright->p1 = c3;
 
846
      cright->p2 = c4;
 
847
      cright->p3 = c5;
 
848
      cright->p4 = c->p4;
 
849
    }
 
850
}
 
851
 
 
852
void
 
853
cogl2_path_curve_to (CoglPath *path,
 
854
                     float x_1,
 
855
                     float y_1,
 
856
                     float x_2,
 
857
                     float y_2,
 
858
                     float x_3,
 
859
                     float y_3)
 
860
{
 
861
  CoglBezCubic cubic;
 
862
 
 
863
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
864
 
 
865
  /* Prepare cubic curve */
 
866
  cubic.p1 = path->data->path_pen;
 
867
  cubic.p2.x = x_1;
 
868
  cubic.p2.y = y_1;
 
869
  cubic.p3.x = x_2;
 
870
  cubic.p3.y = y_2;
 
871
  cubic.p4.x = x_3;
 
872
  cubic.p4.y = y_3;
 
873
 
 
874
  /* Run subdivision */
 
875
  _cogl_path_bezier3_sub (path, &cubic);
 
876
 
 
877
  /* Add last point */
 
878
  _cogl_path_add_node (path, FALSE, cubic.p4.x, cubic.p4.y);
 
879
  path->data->path_pen = cubic.p4;
 
880
}
 
881
 
 
882
void
 
883
cogl2_path_rel_curve_to (CoglPath *path,
 
884
                         float x_1,
 
885
                         float y_1,
 
886
                         float x_2,
 
887
                         float y_2,
 
888
                         float x_3,
 
889
                         float y_3)
 
890
{
 
891
  CoglPathData *data;
 
892
 
 
893
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
894
 
 
895
  data = path->data;
 
896
 
 
897
  cogl2_path_curve_to (path,
 
898
                       data->path_pen.x + x_1,
 
899
                       data->path_pen.y + y_1,
 
900
                       data->path_pen.x + x_2,
 
901
                       data->path_pen.y + y_2,
 
902
                       data->path_pen.x + x_3,
 
903
                       data->path_pen.y + y_3);
 
904
}
 
905
 
 
906
CoglPath *
 
907
cogl2_path_new (void)
 
908
{
 
909
  CoglPath *path;
 
910
  CoglPathData *data;
 
911
 
 
912
  _COGL_GET_CONTEXT (ctx, NULL);
 
913
 
 
914
  path = g_slice_new (CoglPath);
 
915
  data = path->data = g_slice_new (CoglPathData);
 
916
 
 
917
  data->ref_count = 1;
 
918
  data->context = ctx;
 
919
  data->fill_rule = COGL_PATH_FILL_RULE_EVEN_ODD;
 
920
  data->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode));
 
921
  data->last_path = 0;
 
922
  data->fill_attribute_buffer = NULL;
 
923
  data->stroke_attribute_buffer = NULL;
 
924
  data->fill_primitive = NULL;
 
925
  data->is_rectangle = FALSE;
 
926
 
 
927
  return _cogl_path_object_new (path);
 
928
}
 
929
 
 
930
CoglPath *
 
931
cogl_path_copy (CoglPath *old_path)
 
932
{
 
933
  CoglPath *new_path;
 
934
 
 
935
  _COGL_RETURN_VAL_IF_FAIL (cogl_is_path (old_path), NULL);
 
936
 
 
937
  new_path = g_slice_new (CoglPath);
 
938
  new_path->data = old_path->data;
 
939
  new_path->data->ref_count++;
 
940
 
 
941
  return _cogl_path_object_new (new_path);
 
942
}
 
943
 
 
944
static void
 
945
_cogl_path_free (CoglPath *path)
 
946
{
 
947
  _cogl_path_data_unref (path->data);
 
948
  g_slice_free (CoglPath, path);
 
949
}
 
950
 
 
951
/* If second order beziers were needed the following code could
 
952
 * be re-enabled:
 
953
 */
 
954
#if 0
 
955
 
 
956
static void
 
957
_cogl_path_bezier2_sub (CoglPath *path,
 
958
                        CoglBezQuad *quad)
 
959
{
 
960
  CoglBezQuad quads[_COGL_MAX_BEZ_RECURSE_DEPTH];
 
961
  CoglBezQuad *qleft;
 
962
  CoglBezQuad *qright;
 
963
  CoglBezQuad *q;
 
964
  floatVec2 mid;
 
965
  floatVec2 dif;
 
966
  floatVec2 c1;
 
967
  floatVec2 c2;
 
968
  floatVec2 c3;
 
969
  int qindex;
 
970
 
 
971
  /* Put first curve on stack */
 
972
  quads[0] = *quad;
 
973
  qindex =  0;
 
974
 
 
975
  /* While stack is not empty */
 
976
  while (qindex >= 0)
 
977
    {
 
978
 
 
979
      q = &quads[qindex];
 
980
 
 
981
      /* Calculate distance of control point from its
 
982
       * counterpart on the line between end points */
 
983
      mid.x = ((q->p1.x + q->p3.x) / 2);
 
984
      mid.y = ((q->p1.y + q->p3.y) / 2);
 
985
      dif.x = (q->p2.x - mid.x);
 
986
      dif.y = (q->p2.y - mid.y);
 
987
      if (dif.x < 0) dif.x = -dif.x;
 
988
      if (dif.y < 0) dif.y = -dif.y;
 
989
 
 
990
      /* Cancel if the curve is flat enough */
 
991
      if (dif.x + dif.y <= 1.0 ||
 
992
          qindex == _COGL_MAX_BEZ_RECURSE_DEPTH - 1)
 
993
        {
 
994
          /* Add subdivision point (skip last) */
 
995
          if (qindex == 0) return;
 
996
          _cogl_path_add_node (path, FALSE, q->p3.x, q->p3.y);
 
997
          --qindex; continue;
 
998
        }
 
999
 
 
1000
      /* Left recursion goes on top of stack! */
 
1001
      qright = q; qleft = &quads[++qindex];
 
1002
 
 
1003
      /* Subdivide into 2 sub-curves */
 
1004
      c1.x = ((q->p1.x + q->p2.x) / 2);
 
1005
      c1.y = ((q->p1.y + q->p2.y) / 2);
 
1006
      c3.x = ((q->p2.x + q->p3.x) / 2);
 
1007
      c3.y = ((q->p2.y + q->p3.y) / 2);
 
1008
      c2.x = ((c1.x + c3.x) / 2);
 
1009
      c2.y = ((c1.y + c3.y) / 2);
 
1010
 
 
1011
      /* Add left recursion onto stack */
 
1012
      qleft->p1 = q->p1;
 
1013
      qleft->p2 = c1;
 
1014
      qleft->p3 = c2;
 
1015
 
 
1016
      /* Add right recursion onto stack */
 
1017
      qright->p1 = c2;
 
1018
      qright->p2 = c3;
 
1019
      qright->p3 = q->p3;
 
1020
    }
 
1021
}
 
1022
 
 
1023
void
 
1024
cogl_path_curve2_to (CoglPath *path,
 
1025
                     float x_1,
 
1026
                     float y_1,
 
1027
                     float x_2,
 
1028
                     float y_2)
 
1029
{
 
1030
  CoglBezQuad quad;
 
1031
 
 
1032
  /* Prepare quadratic curve */
 
1033
  quad.p1 = path->data->path_pen;
 
1034
  quad.p2.x = x_1;
 
1035
  quad.p2.y = y_1;
 
1036
  quad.p3.x = x_2;
 
1037
  quad.p3.y = y_2;
 
1038
 
 
1039
  /* Run subdivision */
 
1040
  _cogl_path_bezier2_sub (&quad);
 
1041
 
 
1042
  /* Add last point */
 
1043
  _cogl_path_add_node (FALSE, quad.p3.x, quad.p3.y);
 
1044
  path->data->path_pen = quad.p3;
 
1045
}
 
1046
 
 
1047
void
 
1048
cogl_rel_curve2_to (CoglPath *path,
 
1049
                    float x_1,
 
1050
                    float y_1,
 
1051
                    float x_2,
 
1052
                    float y_2)
 
1053
{
 
1054
  CoglPathData *data;
 
1055
 
 
1056
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
1057
 
 
1058
  data = path->data;
 
1059
 
 
1060
  cogl_path_curve2_to (data->path_pen.x + x_1,
 
1061
                       data->path_pen.y + y_1,
 
1062
                       data->path_pen.x + x_2,
 
1063
                       data->path_pen.y + y_2);
 
1064
}
 
1065
 
 
1066
#endif
 
1067
 
 
1068
typedef struct _CoglPathTesselator CoglPathTesselator;
 
1069
typedef struct _CoglPathTesselatorVertex CoglPathTesselatorVertex;
 
1070
 
 
1071
struct _CoglPathTesselator
 
1072
{
 
1073
  GLUtesselator *glu_tess;
 
1074
  GLenum primitive_type;
 
1075
  int vertex_number;
 
1076
  /* Array of CoglPathTesselatorVertex. This needs to grow when the
 
1077
     combine callback is called */
 
1078
  GArray *vertices;
 
1079
  /* Array of integers for the indices into the vertices array. Each
 
1080
     element will either be uint8_t, uint16_t or uint32_t depending on
 
1081
     the number of vertices */
 
1082
  GArray *indices;
 
1083
  CoglIndicesType indices_type;
 
1084
  /* Indices used to split fans and strips */
 
1085
  int index_a, index_b;
 
1086
};
 
1087
 
 
1088
struct _CoglPathTesselatorVertex
 
1089
{
 
1090
  float x, y, s, t;
 
1091
};
 
1092
 
 
1093
static void
 
1094
_cogl_path_tesselator_begin (GLenum type,
 
1095
                             CoglPathTesselator *tess)
 
1096
{
 
1097
  g_assert (type == GL_TRIANGLES ||
 
1098
            type == GL_TRIANGLE_FAN ||
 
1099
            type == GL_TRIANGLE_STRIP);
 
1100
 
 
1101
  tess->primitive_type = type;
 
1102
  tess->vertex_number = 0;
 
1103
}
 
1104
 
 
1105
static CoglIndicesType
 
1106
_cogl_path_tesselator_get_indices_type_for_size (int n_vertices)
 
1107
{
 
1108
  if (n_vertices <= 256)
 
1109
    return COGL_INDICES_TYPE_UNSIGNED_BYTE;
 
1110
  else if (n_vertices <= 65536)
 
1111
    return COGL_INDICES_TYPE_UNSIGNED_SHORT;
 
1112
  else
 
1113
    return COGL_INDICES_TYPE_UNSIGNED_INT;
 
1114
}
 
1115
 
 
1116
static void
 
1117
_cogl_path_tesselator_allocate_indices_array (CoglPathTesselator *tess)
 
1118
{
 
1119
  switch (tess->indices_type)
 
1120
    {
 
1121
    case COGL_INDICES_TYPE_UNSIGNED_BYTE:
 
1122
      tess->indices = g_array_new (FALSE, FALSE, sizeof (uint8_t));
 
1123
      break;
 
1124
 
 
1125
    case COGL_INDICES_TYPE_UNSIGNED_SHORT:
 
1126
      tess->indices = g_array_new (FALSE, FALSE, sizeof (uint16_t));
 
1127
      break;
 
1128
 
 
1129
    case COGL_INDICES_TYPE_UNSIGNED_INT:
 
1130
      tess->indices = g_array_new (FALSE, FALSE, sizeof (uint32_t));
 
1131
      break;
 
1132
    }
 
1133
}
 
1134
 
 
1135
static void
 
1136
_cogl_path_tesselator_add_index (CoglPathTesselator *tess, int vertex_index)
 
1137
{
 
1138
  switch (tess->indices_type)
 
1139
    {
 
1140
    case COGL_INDICES_TYPE_UNSIGNED_BYTE:
 
1141
      {
 
1142
        uint8_t val = vertex_index;
 
1143
        g_array_append_val (tess->indices, val);
 
1144
      }
 
1145
      break;
 
1146
 
 
1147
    case COGL_INDICES_TYPE_UNSIGNED_SHORT:
 
1148
      {
 
1149
        uint16_t val = vertex_index;
 
1150
        g_array_append_val (tess->indices, val);
 
1151
      }
 
1152
      break;
 
1153
 
 
1154
    case COGL_INDICES_TYPE_UNSIGNED_INT:
 
1155
      {
 
1156
        uint32_t val = vertex_index;
 
1157
        g_array_append_val (tess->indices, val);
 
1158
      }
 
1159
      break;
 
1160
    }
 
1161
}
 
1162
 
 
1163
static void
 
1164
_cogl_path_tesselator_vertex (void *vertex_data,
 
1165
                              CoglPathTesselator *tess)
 
1166
{
 
1167
  int vertex_index;
 
1168
 
 
1169
  vertex_index = GPOINTER_TO_INT (vertex_data);
 
1170
 
 
1171
  /* This tries to convert all of the primitives into GL_TRIANGLES
 
1172
     with indices to share vertices */
 
1173
  switch (tess->primitive_type)
 
1174
    {
 
1175
    case GL_TRIANGLES:
 
1176
      /* Directly use the vertex */
 
1177
      _cogl_path_tesselator_add_index (tess, vertex_index);
 
1178
      break;
 
1179
 
 
1180
    case GL_TRIANGLE_FAN:
 
1181
      if (tess->vertex_number == 0)
 
1182
        tess->index_a = vertex_index;
 
1183
      else if (tess->vertex_number == 1)
 
1184
        tess->index_b = vertex_index;
 
1185
      else
 
1186
        {
 
1187
          /* Create a triangle with the first vertex, the previous
 
1188
             vertex and this vertex */
 
1189
          _cogl_path_tesselator_add_index (tess, tess->index_a);
 
1190
          _cogl_path_tesselator_add_index (tess, tess->index_b);
 
1191
          _cogl_path_tesselator_add_index (tess, vertex_index);
 
1192
          /* Next time we will use this vertex as the previous
 
1193
             vertex */
 
1194
          tess->index_b = vertex_index;
 
1195
        }
 
1196
      break;
 
1197
 
 
1198
    case GL_TRIANGLE_STRIP:
 
1199
      if (tess->vertex_number == 0)
 
1200
        tess->index_a = vertex_index;
 
1201
      else if (tess->vertex_number == 1)
 
1202
        tess->index_b = vertex_index;
 
1203
      else
 
1204
        {
 
1205
          _cogl_path_tesselator_add_index (tess, tess->index_a);
 
1206
          _cogl_path_tesselator_add_index (tess, tess->index_b);
 
1207
          _cogl_path_tesselator_add_index (tess, vertex_index);
 
1208
          if (tess->vertex_number & 1)
 
1209
            tess->index_b = vertex_index;
 
1210
          else
 
1211
            tess->index_a = vertex_index;
 
1212
        }
 
1213
      break;
 
1214
 
 
1215
    default:
 
1216
      g_assert_not_reached ();
 
1217
    }
 
1218
 
 
1219
  tess->vertex_number++;
 
1220
}
 
1221
 
 
1222
static void
 
1223
_cogl_path_tesselator_end (CoglPathTesselator *tess)
 
1224
{
 
1225
  tess->primitive_type = GL_FALSE;
 
1226
}
 
1227
 
 
1228
static void
 
1229
_cogl_path_tesselator_combine (double coords[3],
 
1230
                               void *vertex_data[4],
 
1231
                               float weight[4],
 
1232
                               void **out_data,
 
1233
                               CoglPathTesselator *tess)
 
1234
{
 
1235
  CoglPathTesselatorVertex *vertex;
 
1236
  CoglIndicesType new_indices_type;
 
1237
  int i;
 
1238
 
 
1239
  /* Add a new vertex to the array */
 
1240
  g_array_set_size (tess->vertices, tess->vertices->len + 1);
 
1241
  vertex = &g_array_index (tess->vertices,
 
1242
                           CoglPathTesselatorVertex,
 
1243
                           tess->vertices->len - 1);
 
1244
  /* The data is just the index to the vertex */
 
1245
  *out_data = GINT_TO_POINTER (tess->vertices->len - 1);
 
1246
  /* Set the coordinates of the new vertex */
 
1247
  vertex->x = coords[0];
 
1248
  vertex->y = coords[1];
 
1249
  /* Generate the texture coordinates as the weighted average of the
 
1250
     four incoming coordinates */
 
1251
  vertex->s = 0.0f;
 
1252
  vertex->t = 0.0f;
 
1253
  for (i = 0; i < 4; i++)
 
1254
    {
 
1255
      CoglPathTesselatorVertex *old_vertex =
 
1256
        &g_array_index (tess->vertices, CoglPathTesselatorVertex,
 
1257
                        GPOINTER_TO_INT (vertex_data[i]));
 
1258
      vertex->s += old_vertex->s * weight[i];
 
1259
      vertex->t += old_vertex->t * weight[i];
 
1260
    }
 
1261
 
 
1262
  /* Check if we've reached the limit for the data type of our indices */
 
1263
  new_indices_type =
 
1264
    _cogl_path_tesselator_get_indices_type_for_size (tess->vertices->len);
 
1265
  if (new_indices_type != tess->indices_type)
 
1266
    {
 
1267
      CoglIndicesType old_indices_type = new_indices_type;
 
1268
      GArray *old_vertices = tess->indices;
 
1269
 
 
1270
      /* Copy the indices to an array of the new type */
 
1271
      tess->indices_type = new_indices_type;
 
1272
      _cogl_path_tesselator_allocate_indices_array (tess);
 
1273
 
 
1274
      switch (old_indices_type)
 
1275
        {
 
1276
        case COGL_INDICES_TYPE_UNSIGNED_BYTE:
 
1277
          for (i = 0; i < old_vertices->len; i++)
 
1278
            _cogl_path_tesselator_add_index (tess,
 
1279
                                             g_array_index (old_vertices,
 
1280
                                                            uint8_t, i));
 
1281
          break;
 
1282
 
 
1283
        case COGL_INDICES_TYPE_UNSIGNED_SHORT:
 
1284
          for (i = 0; i < old_vertices->len; i++)
 
1285
            _cogl_path_tesselator_add_index (tess,
 
1286
                                             g_array_index (old_vertices,
 
1287
                                                            uint16_t, i));
 
1288
          break;
 
1289
 
 
1290
        case COGL_INDICES_TYPE_UNSIGNED_INT:
 
1291
          for (i = 0; i < old_vertices->len; i++)
 
1292
            _cogl_path_tesselator_add_index (tess,
 
1293
                                             g_array_index (old_vertices,
 
1294
                                                            uint32_t, i));
 
1295
          break;
 
1296
        }
 
1297
 
 
1298
      g_array_free (old_vertices, TRUE);
 
1299
    }
 
1300
}
 
1301
 
 
1302
static void
 
1303
_cogl_path_build_fill_attribute_buffer (CoglPath *path)
 
1304
{
 
1305
  CoglPathTesselator tess;
 
1306
  unsigned int path_start = 0;
 
1307
  CoglPathData *data = path->data;
 
1308
  int i;
 
1309
 
 
1310
  /* If we've already got a vbo then we don't need to do anything */
 
1311
  if (data->fill_attribute_buffer)
 
1312
    return;
 
1313
 
 
1314
  tess.primitive_type = FALSE;
 
1315
 
 
1316
  /* Generate a vertex for each point on the path */
 
1317
  tess.vertices = g_array_new (FALSE, FALSE, sizeof (CoglPathTesselatorVertex));
 
1318
  g_array_set_size (tess.vertices, data->path_nodes->len);
 
1319
  for (i = 0; i < data->path_nodes->len; i++)
 
1320
    {
 
1321
      CoglPathNode *node =
 
1322
        &g_array_index (data->path_nodes, CoglPathNode, i);
 
1323
      CoglPathTesselatorVertex *vertex =
 
1324
        &g_array_index (tess.vertices, CoglPathTesselatorVertex, i);
 
1325
 
 
1326
      vertex->x = node->x;
 
1327
      vertex->y = node->y;
 
1328
 
 
1329
      /* Add texture coordinates so that a texture would be drawn to
 
1330
         fit the bounding box of the path and then cropped by the
 
1331
         path */
 
1332
      if (data->path_nodes_min.x == data->path_nodes_max.x)
 
1333
        vertex->s = 0.0f;
 
1334
      else
 
1335
        vertex->s = ((node->x - data->path_nodes_min.x)
 
1336
                     / (data->path_nodes_max.x - data->path_nodes_min.x));
 
1337
      if (data->path_nodes_min.y == data->path_nodes_max.y)
 
1338
        vertex->t = 0.0f;
 
1339
      else
 
1340
        vertex->t = ((node->y - data->path_nodes_min.y)
 
1341
                     / (data->path_nodes_max.y - data->path_nodes_min.y));
 
1342
    }
 
1343
 
 
1344
  tess.indices_type =
 
1345
    _cogl_path_tesselator_get_indices_type_for_size (data->path_nodes->len);
 
1346
  _cogl_path_tesselator_allocate_indices_array (&tess);
 
1347
 
 
1348
  tess.glu_tess = gluNewTess ();
 
1349
 
 
1350
  if (data->fill_rule == COGL_PATH_FILL_RULE_EVEN_ODD)
 
1351
    gluTessProperty (tess.glu_tess, GLU_TESS_WINDING_RULE,
 
1352
                     GLU_TESS_WINDING_ODD);
 
1353
  else
 
1354
    gluTessProperty (tess.glu_tess, GLU_TESS_WINDING_RULE,
 
1355
                     GLU_TESS_WINDING_NONZERO);
 
1356
 
 
1357
  /* All vertices are on the xy-plane */
 
1358
  gluTessNormal (tess.glu_tess, 0.0, 0.0, 1.0);
 
1359
 
 
1360
  gluTessCallback (tess.glu_tess, GLU_TESS_BEGIN_DATA,
 
1361
                   _cogl_path_tesselator_begin);
 
1362
  gluTessCallback (tess.glu_tess, GLU_TESS_VERTEX_DATA,
 
1363
                   _cogl_path_tesselator_vertex);
 
1364
  gluTessCallback (tess.glu_tess, GLU_TESS_END_DATA,
 
1365
                   _cogl_path_tesselator_end);
 
1366
  gluTessCallback (tess.glu_tess, GLU_TESS_COMBINE_DATA,
 
1367
                   _cogl_path_tesselator_combine);
 
1368
 
 
1369
  gluTessBeginPolygon (tess.glu_tess, &tess);
 
1370
 
 
1371
  while (path_start < data->path_nodes->len)
 
1372
    {
 
1373
      CoglPathNode *node =
 
1374
        &g_array_index (data->path_nodes, CoglPathNode, path_start);
 
1375
 
 
1376
      gluTessBeginContour (tess.glu_tess);
 
1377
 
 
1378
      for (i = 0; i < node->path_size; i++)
 
1379
        {
 
1380
          double vertex[3] = { node[i].x, node[i].y, 0.0 };
 
1381
          gluTessVertex (tess.glu_tess, vertex,
 
1382
                         GINT_TO_POINTER (i + path_start));
 
1383
        }
 
1384
 
 
1385
      gluTessEndContour (tess.glu_tess);
 
1386
 
 
1387
      path_start += node->path_size;
 
1388
    }
 
1389
 
 
1390
  gluTessEndPolygon (tess.glu_tess);
 
1391
 
 
1392
  gluDeleteTess (tess.glu_tess);
 
1393
 
 
1394
  data->fill_attribute_buffer =
 
1395
    cogl_attribute_buffer_new (data->context,
 
1396
                               sizeof (CoglPathTesselatorVertex) *
 
1397
                               tess.vertices->len,
 
1398
                               tess.vertices->data);
 
1399
  g_array_free (tess.vertices, TRUE);
 
1400
 
 
1401
  data->fill_attributes[0] =
 
1402
    cogl_attribute_new (data->fill_attribute_buffer,
 
1403
                        "cogl_position_in",
 
1404
                        sizeof (CoglPathTesselatorVertex),
 
1405
                        G_STRUCT_OFFSET (CoglPathTesselatorVertex, x),
 
1406
                        2, /* n_components */
 
1407
                        COGL_ATTRIBUTE_TYPE_FLOAT);
 
1408
  data->fill_attributes[1] =
 
1409
    cogl_attribute_new (data->fill_attribute_buffer,
 
1410
                        "cogl_tex_coord0_in",
 
1411
                        sizeof (CoglPathTesselatorVertex),
 
1412
                        G_STRUCT_OFFSET (CoglPathTesselatorVertex, s),
 
1413
                        2, /* n_components */
 
1414
                        COGL_ATTRIBUTE_TYPE_FLOAT);
 
1415
 
 
1416
  data->fill_vbo_indices = cogl_indices_new (data->context,
 
1417
                                             tess.indices_type,
 
1418
                                             tess.indices->data,
 
1419
                                             tess.indices->len);
 
1420
  data->fill_vbo_n_indices = tess.indices->len;
 
1421
  g_array_free (tess.indices, TRUE);
 
1422
}
 
1423
 
 
1424
static CoglPrimitive *
 
1425
_cogl_path_get_fill_primitive (CoglPath *path)
 
1426
{
 
1427
  if (path->data->fill_primitive)
 
1428
    return path->data->fill_primitive;
 
1429
 
 
1430
  _cogl_path_build_fill_attribute_buffer (path);
 
1431
 
 
1432
  path->data->fill_primitive =
 
1433
    cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES,
 
1434
                                        path->data->fill_vbo_n_indices,
 
1435
                                        path->data->fill_attributes,
 
1436
                                        COGL_PATH_N_ATTRIBUTES);
 
1437
  cogl_primitive_set_indices (path->data->fill_primitive,
 
1438
                              path->data->fill_vbo_indices,
 
1439
                              path->data->fill_vbo_n_indices);
 
1440
 
 
1441
  return path->data->fill_primitive;
 
1442
}
 
1443
 
 
1444
static CoglClipStack *
 
1445
_cogl_clip_stack_push_from_path (CoglClipStack *stack,
 
1446
                                 CoglPath *path,
 
1447
                                 CoglMatrixEntry *modelview_entry,
 
1448
                                 CoglMatrixEntry *projection_entry,
 
1449
                                 const float *viewport)
 
1450
{
 
1451
  float x_1, y_1, x_2, y_2;
 
1452
 
 
1453
  _cogl_path_get_bounds (path, &x_1, &y_1, &x_2, &y_2);
 
1454
 
 
1455
  /* If the path is a simple rectangle then we can divert to pushing a
 
1456
     rectangle clip instead which usually won't involve the stencil
 
1457
     buffer */
 
1458
  if (_cogl_path_is_rectangle (path))
 
1459
    return _cogl_clip_stack_push_rectangle (stack,
 
1460
                                            x_1, y_1,
 
1461
                                            x_2, y_2,
 
1462
                                            modelview_entry,
 
1463
                                            projection_entry,
 
1464
                                            viewport);
 
1465
  else
 
1466
    {
 
1467
      CoglPrimitive *primitive = _cogl_path_get_fill_primitive (path);
 
1468
 
 
1469
      return _cogl_clip_stack_push_primitive (stack,
 
1470
                                              primitive,
 
1471
                                              x_1, y_1, x_2, y_2,
 
1472
                                              modelview_entry,
 
1473
                                              projection_entry,
 
1474
                                              viewport);
 
1475
    }
 
1476
}
 
1477
 
 
1478
void
 
1479
cogl_framebuffer_push_path_clip (CoglFramebuffer *framebuffer,
 
1480
                                 CoglPath *path)
 
1481
{
 
1482
  CoglMatrixEntry *modelview_entry =
 
1483
    _cogl_framebuffer_get_modelview_entry (framebuffer);
 
1484
  CoglMatrixEntry *projection_entry =
 
1485
    _cogl_framebuffer_get_projection_entry (framebuffer);
 
1486
  /* XXX: It would be nicer if we stored the private viewport as a
 
1487
   * vec4 so we could avoid this redundant copy. */
 
1488
  float viewport[] = {
 
1489
      framebuffer->viewport_x,
 
1490
      framebuffer->viewport_y,
 
1491
      framebuffer->viewport_width,
 
1492
      framebuffer->viewport_height
 
1493
  };
 
1494
 
 
1495
  framebuffer->clip_stack =
 
1496
    _cogl_clip_stack_push_from_path (framebuffer->clip_stack,
 
1497
                                     path,
 
1498
                                     modelview_entry,
 
1499
                                     projection_entry,
 
1500
                                     viewport);
 
1501
 
 
1502
  if (framebuffer->context->current_draw_buffer == framebuffer)
 
1503
    framebuffer->context->current_draw_buffer_changes |=
 
1504
      COGL_FRAMEBUFFER_STATE_CLIP;
 
1505
}
 
1506
 
 
1507
/* XXX: deprecated */
 
1508
void
 
1509
cogl_clip_push_from_path (CoglPath *path)
 
1510
{
 
1511
  cogl_framebuffer_push_path_clip (cogl_get_draw_framebuffer (), path);
 
1512
}
 
1513
 
 
1514
static void
 
1515
_cogl_path_build_stroke_attribute_buffer (CoglPath *path)
 
1516
{
 
1517
  CoglPathData *data = path->data;
 
1518
  CoglBuffer *buffer;
 
1519
  unsigned int n_attributes = 0;
 
1520
  unsigned int path_start;
 
1521
  CoglPathNode *node;
 
1522
  floatVec2 *buffer_p;
 
1523
  unsigned int i;
 
1524
 
 
1525
  /* If we've already got a cached vbo then we don't need to do anything */
 
1526
  if (data->stroke_attribute_buffer)
 
1527
    return;
 
1528
 
 
1529
  data->stroke_attribute_buffer =
 
1530
    cogl_attribute_buffer_new_with_size (data->context,
 
1531
                                         data->path_nodes->len *
 
1532
                                         sizeof (floatVec2));
 
1533
 
 
1534
  buffer = COGL_BUFFER (data->stroke_attribute_buffer);
 
1535
  buffer_p = _cogl_buffer_map_for_fill_or_fallback (buffer);
 
1536
 
 
1537
  /* Copy the vertices in and count the number of sub paths. Each sub
 
1538
     path will form a separate attribute so we can paint the disjoint
 
1539
     line strips */
 
1540
  for (path_start = 0;
 
1541
       path_start < data->path_nodes->len;
 
1542
       path_start += node->path_size)
 
1543
    {
 
1544
      node = &g_array_index (data->path_nodes, CoglPathNode, path_start);
 
1545
 
 
1546
      for (i = 0; i < node->path_size; i++)
 
1547
        {
 
1548
          buffer_p[path_start + i].x = node[i].x;
 
1549
          buffer_p[path_start + i].y = node[i].y;
 
1550
        }
 
1551
 
 
1552
      n_attributes++;
 
1553
    }
 
1554
 
 
1555
  _cogl_buffer_unmap_for_fill_or_fallback (buffer);
 
1556
 
 
1557
  data->stroke_attributes = g_new (CoglAttribute *, n_attributes);
 
1558
 
 
1559
  /* Now we can loop the sub paths again to create the attributes */
 
1560
  for (i = 0, path_start = 0;
 
1561
       path_start < data->path_nodes->len;
 
1562
       i++, path_start += node->path_size)
 
1563
    {
 
1564
      node = &g_array_index (data->path_nodes, CoglPathNode, path_start);
 
1565
 
 
1566
      data->stroke_attributes[i] =
 
1567
        cogl_attribute_new (data->stroke_attribute_buffer,
 
1568
                            "cogl_position_in",
 
1569
                            sizeof (floatVec2),
 
1570
                            path_start * sizeof (floatVec2),
 
1571
                            2, /* n_components */
 
1572
                            COGL_ATTRIBUTE_TYPE_FLOAT);
 
1573
    }
 
1574
 
 
1575
  data->stroke_n_attributes = n_attributes;
 
1576
}
 
1577
 
 
1578
/* XXX: deprecated */
 
1579
void
 
1580
cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer,
 
1581
                            CoglPipeline *pipeline,
 
1582
                            CoglPath *path)
 
1583
{
 
1584
  _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (framebuffer));
 
1585
  _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline));
 
1586
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
1587
 
 
1588
  _cogl_path_fill_nodes (path, framebuffer, pipeline, 0 /* flags */);
 
1589
}
 
1590
 
 
1591
/* XXX: deprecated */
 
1592
void
 
1593
cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer,
 
1594
                              CoglPipeline *pipeline,
 
1595
                              CoglPath *path)
 
1596
{
 
1597
  _COGL_RETURN_IF_FAIL (cogl_is_framebuffer (framebuffer));
 
1598
  _COGL_RETURN_IF_FAIL (cogl_is_pipeline (pipeline));
 
1599
  _COGL_RETURN_IF_FAIL (cogl_is_path (path));
 
1600
 
 
1601
  _cogl_path_stroke_nodes (path, framebuffer, pipeline);
 
1602
}