Coverage Report

Created: 2025-11-10 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/compositor/mpeg4_geometry_2d.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2000-2023
6
 *          All rights reserved
7
 *
8
 *  This file is part of GPAC / Scene Compositor sub-project
9
 *
10
 *  GPAC is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU Lesser General Public License as published by
12
 *  the Free Software Foundation; either version 2, or (at your option)
13
 *  any later version.
14
 *
15
 *  GPAC is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU Lesser General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU Lesser General Public
21
 *  License along with this library; see the file COPYING.  If not, write to
22
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
 *
24
 */
25
26
27
28
#include "nodes_stacks.h"
29
#include "visual_manager.h"
30
31
#if !defined(GPAC_DISABLE_COMPOSITOR)
32
Bool compositor_get_2d_plane_intersection(GF_Ray *ray, SFVec3f *res)
33
0
{
34
0
  GF_Plane p;
35
0
  Fixed t, t2;
36
0
  if (!ray->dir.x && !ray->dir.y) {
37
0
    res->x = ray->orig.x;
38
0
    res->y = ray->orig.y;
39
0
    res->z = 0;
40
0
    return 1;
41
0
  }
42
0
  p.normal.x = p.normal.y = 0;
43
0
  p.normal.z = FIX_ONE;
44
0
  p.d = 0;
45
0
  t2 = gf_vec_dot(p.normal, ray->dir);
46
0
  if (t2 == 0) return 0;
47
0
  t = - gf_divfix(gf_vec_dot(p.normal, ray->orig) + p.d, t2);
48
0
  if (t<0) return 0;
49
0
  *res = gf_vec_scale(ray->dir, t);
50
0
  gf_vec_add(*res, ray->orig, *res);
51
0
  return 1;
52
0
}
53
54
#ifndef GPAC_DISABLE_VRML
55
56
/*
57
    Shape
58
*/
59
static void TraverseShape(GF_Node *node, void *rs, Bool is_destroy)
60
0
{
61
0
  GF_TraverseState *tr_state;
62
0
  M_Shape *shape;
63
0
  if (is_destroy ) return;
64
65
0
  tr_state = (GF_TraverseState *)rs;
66
#ifndef GPAC_DISABLE_3D
67
  if (tr_state->traversing_mode==TRAVERSE_LIGHTING) return;
68
#endif
69
70
0
  shape = (M_Shape *) node;
71
0
  if (!shape->geometry) return;
72
73
  /*reset this node dirty flag (because bitmap may trigger bounds invalidation on the fly)*/
74
0
  gf_node_dirty_clear(node, 0);
75
76
77
  /*check traverse mode, and take care of switch-off flag*/
78
0
  if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
79
0
    tr_state->appear = (GF_Node *) shape->appearance;
80
81
    /*this is done regardless of switch flag*/
82
0
    gf_node_traverse((GF_Node *) shape->geometry, tr_state);
83
84
0
    if (tr_state->appear) {
85
      /*apply line width*/
86
0
      GF_Node *m = ((M_Appearance *)tr_state->appear)->material;
87
0
      if (m && (gf_node_get_tag(m)==TAG_MPEG4_Material2D) ) {
88
0
        DrawAspect2D asp;
89
0
        Fixed width = 0;
90
0
        asp.line_scale = FIX_ONE;
91
0
        m = ((M_Material2D *)m)->lineProps;
92
0
        if (m) {
93
0
          switch (gf_node_get_tag(m)) {
94
0
          case TAG_MPEG4_LineProperties:
95
0
            width = ((M_LineProperties *) m)->width;
96
0
            drawable_compute_line_scale(tr_state, &asp);
97
0
            break;
98
0
          case TAG_MPEG4_XLineProperties:
99
0
            if ( ((M_XLineProperties *) m)->isCenterAligned)
100
0
              width = ((M_XLineProperties *) m)->width;
101
0
            if ( ((M_XLineProperties *) m)->isScalable)
102
0
              drawable_compute_line_scale(tr_state, &asp);
103
0
            break;
104
0
          }
105
0
          width = gf_mulfix(width, asp.line_scale);
106
0
          tr_state->bounds.width += width;
107
0
          tr_state->bounds.height += width;
108
0
          tr_state->bounds.y += width/2;
109
0
          tr_state->bounds.x -= width/2;
110
0
        }
111
0
      }
112
0
      tr_state->appear = NULL;
113
0
    }
114
0
  } else {
115
0
    if (tr_state->switched_off) return;
116
117
0
    tr_state->appear = (GF_Node *) shape->appearance;
118
119
0
    switch (tr_state->traversing_mode) {
120
0
    case TRAVERSE_SORT:
121
#ifndef GPAC_DISABLE_3D
122
      if (tr_state->visual->type_3d)
123
        visual_3d_register_context(tr_state, shape->geometry);
124
      else
125
#endif
126
0
        gf_node_traverse((GF_Node *) shape->geometry, tr_state);
127
0
      break;
128
0
    case TRAVERSE_PICK:
129
0
      gf_node_traverse((GF_Node *) shape->geometry, tr_state);
130
0
      break;
131
#ifndef GPAC_DISABLE_3D
132
    /*if we're here we passed culler already*/
133
    case TRAVERSE_DRAW_3D:
134
      if (!tr_state->visual->type_3d && tr_state->visual->compositor->hybrid_opengl) {
135
        tr_state->visual->compositor->root_visual_setup=0;
136
        tr_state->visual->compositor->force_type_3d=1;
137
      }
138
      gf_node_traverse((GF_Node *) shape->geometry, tr_state);
139
      break;
140
    case TRAVERSE_COLLIDE:
141
      visual_3d_vrml_drawable_collide(shape->geometry, tr_state);
142
      break;
143
#endif
144
0
    }
145
146
0
    tr_state->appear = NULL;
147
0
  }
148
0
}
149
150
void compositor_init_shape(GF_Compositor *compositor, GF_Node *node)
151
0
{
152
0
  gf_node_set_callback_function(node, TraverseShape);
153
0
}
154
155
static void circle_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state)
156
0
{
157
0
  if (gf_node_dirty_get(node)) {
158
0
    Fixed a = ((M_Circle *) node)->radius * 2;
159
0
    drawable_reset_path(stack);
160
0
    gf_path_add_ellipse(stack->path, 0, 0, a, a);
161
0
    gf_node_dirty_clear(node, 0);
162
0
    drawable_mark_modified(stack, tr_state);
163
0
  }
164
0
}
165
static void TraverseCircle(GF_Node *node, void *rs, Bool is_destroy)
166
0
{
167
0
  DrawableContext *ctx;
168
0
  Drawable *stack = (Drawable *)gf_node_get_private(node);
169
0
  GF_TraverseState *tr_state = (GF_TraverseState *)rs;
170
171
0
  if (is_destroy) {
172
0
    drawable_node_del(node);
173
0
    return;
174
0
  }
175
176
0
  circle_check_changes(node, stack, tr_state);
177
178
0
  switch (tr_state->traversing_mode) {
179
#ifndef GPAC_DISABLE_3D
180
  case TRAVERSE_DRAW_3D:
181
    if (!stack->mesh) {
182
      Fixed a = ((M_Circle *) node)->radius * 2;
183
      stack->mesh = new_mesh();
184
      mesh_new_ellipse(stack->mesh, a, a, tr_state->visual->compositor->fast);
185
    }
186
    visual_3d_draw_2d(stack, tr_state);
187
    return;
188
#endif
189
0
  case TRAVERSE_GET_BOUNDS:
190
0
    gf_path_get_bounds(stack->path, &tr_state->bounds);
191
0
    return;
192
0
  case TRAVERSE_PICK:
193
0
    vrml_drawable_pick(stack, tr_state);
194
0
    return;
195
0
  case TRAVERSE_SORT:
196
#ifndef GPAC_DISABLE_3D
197
    if (tr_state->visual->type_3d) return;
198
#endif
199
0
    ctx = drawable_init_context_mpeg4(stack, tr_state);
200
0
    if (!ctx) return;
201
0
    drawable_finalize_sort(ctx, tr_state, NULL);
202
0
    return;
203
0
  }
204
0
}
205
206
void compositor_init_circle(GF_Compositor *compositor, GF_Node *node)
207
0
{
208
0
  drawable_stack_new(compositor, node);
209
0
  gf_node_set_callback_function(node, TraverseCircle);
210
0
}
211
212
static void ellipse_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state)
213
0
{
214
0
  if (gf_node_dirty_get(node)) {
215
0
    drawable_reset_path(stack);
216
0
    gf_path_add_ellipse(stack->path, 0, 0, ((M_Ellipse *) node)->radius.x*2, ((M_Ellipse *) node)->radius.y*2);
217
0
    gf_node_dirty_clear(node, 0);
218
0
    drawable_mark_modified(stack, tr_state);
219
0
  }
220
0
}
221
static void TraverseEllipse(GF_Node *node, void *rs, Bool is_destroy)
222
0
{
223
0
  DrawableContext *ctx;
224
0
  Drawable *stack = (Drawable *)gf_node_get_private(node);
225
0
  GF_TraverseState *tr_state = (GF_TraverseState *)rs;
226
227
0
  if (is_destroy) {
228
0
    drawable_node_del(node);
229
0
    return;
230
0
  }
231
232
0
  ellipse_check_changes(node, stack, tr_state);
233
234
0
  switch (tr_state->traversing_mode) {
235
#ifndef GPAC_DISABLE_3D
236
  case TRAVERSE_DRAW_3D:
237
    if (!stack->mesh) {
238
      stack->mesh = new_mesh();
239
      mesh_new_ellipse(stack->mesh, ((M_Ellipse *) node)->radius.x * 2, ((M_Ellipse *) node)->radius.y * 2, tr_state->visual->compositor->fast);
240
    }
241
    visual_3d_draw_2d(stack, tr_state);
242
    return;
243
#endif
244
0
  case TRAVERSE_PICK:
245
0
    vrml_drawable_pick(stack, tr_state);
246
0
    return;
247
0
  case TRAVERSE_GET_BOUNDS:
248
0
    gf_path_get_bounds(stack->path, &tr_state->bounds);
249
0
    return;
250
0
  case TRAVERSE_SORT:
251
#ifndef GPAC_DISABLE_3D
252
    if (tr_state->visual->type_3d) return;
253
#endif
254
0
    ctx = drawable_init_context_mpeg4(stack, tr_state);
255
0
    if (!ctx) return;
256
0
    drawable_finalize_sort(ctx, tr_state, NULL);
257
0
    break;
258
0
  }
259
0
}
260
261
void compositor_init_ellipse(GF_Compositor  *compositor, GF_Node *node)
262
0
{
263
0
  drawable_stack_new(compositor, node);
264
0
  gf_node_set_callback_function(node, TraverseEllipse);
265
0
}
266
267
static void compositor_2d_draw_rectangle(GF_TraverseState *tr_state)
268
0
{
269
0
  DrawableContext *ctx = tr_state->ctx;
270
271
0
  if (ctx->aspect.fill_texture && ctx->aspect.fill_texture->data
272
#ifndef GPAC_DISABLE_3D
273
          && !tr_state->visual->compositor->hybrid_opengl
274
#endif
275
0
     ) {
276
0
    Bool res;
277
278
    /*get image size WITHOUT line size or antialias margin*/
279
0
    if ( !(ctx->flags & CTX_NO_ANTIALIAS) ) {
280
0
      GF_Rect orig_unclip;
281
0
      GF_IRect orig_clip;
282
0
      orig_unclip = ctx->bi->unclip;
283
0
      orig_clip = ctx->bi->clip;
284
285
0
      gf_path_get_bounds(ctx->drawable->path, &ctx->bi->unclip);
286
0
      gf_mx2d_apply_rect(&ctx->transform, &ctx->bi->unclip);
287
0
      ctx->bi->clip = gf_rect_pixelize(&ctx->bi->unclip);
288
0
      gf_irect_intersect(&ctx->bi->clip, &orig_clip);
289
290
0
      res = tr_state->visual->DrawBitmap(tr_state->visual, tr_state, ctx);
291
292
      /*strike path*/
293
0
      ctx->bi->unclip = orig_unclip;
294
0
      ctx->bi->clip = orig_clip;
295
0
      if (res) {
296
0
        ctx->flags |= CTX_PATH_FILLED;
297
0
        visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state);
298
0
      }
299
0
    } else {
300
0
      res = tr_state->visual->DrawBitmap(tr_state->visual, tr_state, ctx);
301
0
    }
302
    /*if failure retry with raster*/
303
0
    if (res) return;
304
0
  }
305
306
0
  visual_2d_texture_path(tr_state->visual, ctx->drawable->path, ctx, tr_state);
307
0
  visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state);
308
0
}
309
310
static void rectangle_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state)
311
0
{
312
  /*if modified update node - we don't update for other traversing mode in order not to mess up the dirty
313
  rect tracking (otherwise we would miss geometry changes with same bounds)*/
314
0
  if (gf_node_dirty_get(node)) {
315
0
    drawable_reset_path(stack);
316
0
    gf_path_add_rect_center(stack->path, 0, 0, ((M_Rectangle *) node)->size.x, ((M_Rectangle *) node)->size.y);
317
0
    gf_node_dirty_clear(node, 0);
318
0
    drawable_mark_modified(stack, tr_state);
319
0
  }
320
0
}
321
322
Bool rectangle_check_adaptation(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state)
323
0
{
324
0
  GF_TextureHandler *txh;
325
0
  GF_MediaObjectVRInfo vrinfo;
326
0
  s32 gaze_x, gaze_y;
327
0
  Bool is_visible = GF_FALSE;
328
0
  if (! tr_state->visual->compositor->gazer_enabled)
329
0
    return GF_TRUE;
330
331
0
  if (!tr_state->appear || ! ((M_Appearance *)tr_state->appear)->texture)
332
0
    return GF_TRUE;
333
  
334
0
  txh = gf_sc_texture_get_handler( ((M_Appearance *) tr_state->appear)->texture );
335
0
  if (!txh->stream) return GF_TRUE;
336
337
0
  if (! gf_mo_get_srd_info(txh->stream, &vrinfo))
338
0
    return GF_TRUE;
339
340
0
  if (!vrinfo.srd_w && !vrinfo.srd_h && vrinfo.is_tiled_srd) {
341
0
    if (txh->stream->srd_full_w && txh->stream->srd_full_h) {
342
0
      gaze_x = tr_state->visual->compositor->gaze_x;
343
0
      gaze_x *= txh->stream->srd_full_w;
344
0
      gaze_x /= tr_state->visual->width;
345
0
      gaze_x += txh->stream->srd_map_ox;
346
347
0
      gaze_y = tr_state->visual->compositor->gaze_y;
348
0
      gaze_y *= txh->stream->srd_full_h;
349
0
      gaze_y /= tr_state->visual->height;
350
0
      gaze_y += txh->stream->srd_map_oy;
351
352
0
      gf_mo_hint_gaze(txh->stream, gaze_x, gaze_y);
353
0
    }
354
355
0
    return GF_TRUE;
356
0
  }
357
358
0
  gaze_x = tr_state->visual->compositor->gaze_x;
359
0
  gaze_x *= vrinfo.srd_max_x;
360
0
  gaze_x /= tr_state->visual->width;
361
362
0
  gaze_y = tr_state->visual->compositor->gaze_y;
363
0
  gaze_y *= vrinfo.srd_max_y;
364
0
  gaze_y /= tr_state->visual->height;
365
366
  //simple test condition: only keep the first row
367
0
  if ((gaze_x>=vrinfo.srd_x) && (gaze_x<=vrinfo.srd_x+vrinfo.srd_w) && (gaze_y>=vrinfo.srd_y) && (gaze_y<=vrinfo.srd_y+vrinfo.srd_h)) {
368
369
0
    GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Texture %d Partial plane is under gaze coord %d %d\n", txh->stream->OD_ID, tr_state->visual->compositor->gaze_x, tr_state->visual->compositor->gaze_y));
370
0
    is_visible = GF_TRUE;
371
0
  }
372
373
0
  if (vrinfo.has_full_coverage) {
374
0
    if (is_visible) {
375
0
      if (!txh->is_open) {
376
0
        GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Texture %d stopped on visible partial plane - starting it\n", txh->stream->OD_ID));
377
0
        if (txh->stream->odm)
378
0
          txh->stream->odm->disable_buffer_at_next_play = GF_TRUE;
379
380
0
        gf_sc_texture_play(txh, NULL);
381
0
      }
382
0
      if (! txh->data)  return GF_FALSE;
383
0
      return GF_TRUE;
384
0
    } else {
385
0
      if (txh->is_open) {
386
0
        GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Texure %d playing on hidden partial plane - stopping it\n", txh->stream->OD_ID));
387
0
        gf_sc_texture_stop_no_unregister(txh);
388
0
      }
389
0
      return GF_FALSE;
390
0
    }
391
0
  } else {
392
0
    if (is_visible) {
393
0
      gf_mo_hint_quality_degradation(txh->stream, 0);
394
0
      if (! txh->data)  return GF_FALSE;
395
0
    } else {
396
0
      gf_mo_hint_quality_degradation(txh->stream, 100);
397
0
    }
398
0
  }
399
0
  return GF_TRUE;
400
0
}
401
402
static void TraverseRectangle(GF_Node *node, void *rs, Bool is_destroy)
403
0
{
404
0
  DrawableContext *ctx;
405
0
  Drawable *stack = (Drawable *)gf_node_get_private(node);
406
0
  GF_TraverseState *tr_state = (GF_TraverseState *)rs;
407
408
0
  if (is_destroy) {
409
0
    drawable_node_del(node);
410
0
    return;
411
0
  }
412
413
0
  rectangle_check_changes(node, stack, tr_state);
414
415
0
  if (! rectangle_check_adaptation(node, stack, tr_state))
416
0
    return;
417
418
0
  switch (tr_state->traversing_mode) {
419
0
  case TRAVERSE_DRAW_2D:
420
0
    compositor_2d_draw_rectangle(tr_state);
421
0
    return;
422
#ifndef GPAC_DISABLE_3D
423
  case TRAVERSE_DRAW_3D:
424
    if (!stack->mesh) {
425
      stack->mesh = new_mesh();
426
      mesh_new_rectangle(stack->mesh, ((M_Rectangle *) node)->size, NULL, 0);
427
    }
428
    visual_3d_draw_2d(stack, tr_state);
429
    return;
430
#endif
431
0
  case TRAVERSE_PICK:
432
0
    vrml_drawable_pick(stack, tr_state);
433
0
    return;
434
0
  case TRAVERSE_GET_BOUNDS:
435
0
    gf_path_get_bounds(stack->path, &tr_state->bounds);
436
0
    return;
437
0
  case TRAVERSE_SORT:
438
#ifndef GPAC_DISABLE_3D
439
    if (tr_state->visual->type_3d) return;
440
#endif
441
0
    break;
442
0
  default:
443
0
    return;
444
0
  }
445
446
0
  ctx = drawable_init_context_mpeg4(stack, tr_state);
447
0
  if (!ctx) return;
448
449
  /*if rotated, object is transparent (doesn't fill bounds) and antialias must be used*/
450
0
  if (tr_state->transform.m[1] || tr_state->transform.m[3]) {
451
0
  }
452
0
  else {
453
454
    /*if alpha or not filled, transparent*/
455
0
    if (ctx->aspect.fill_color && (GF_COL_A(ctx->aspect.fill_color) != 0xFF)) {
456
0
    }
457
    /*if texture transparent, transparent*/
458
0
    else if (ctx->aspect.fill_texture && ctx->aspect.fill_texture->transparent) {
459
0
    }
460
    /*TODO check matrix for alpha*/
461
0
    else if (!tr_state->color_mat.identity) {
462
0
    }
463
    /*otherwise, not transparent*/
464
0
    else {
465
0
      ctx->flags &= ~CTX_IS_TRANSPARENT;
466
0
    }
467
    /*if no line width, we skip antialiasing*/
468
0
    if (!ctx->aspect.pen_props.width) ctx->flags |= CTX_NO_ANTIALIAS;
469
0
  }
470
0
  drawable_finalize_sort(ctx, tr_state, NULL);
471
0
}
472
473
void compositor_init_rectangle(GF_Compositor  *compositor, GF_Node *node)
474
0
{
475
0
  Drawable *stack = drawable_stack_new(compositor, node);
476
0
  stack->flags = DRAWABLE_USE_TRAVERSE_DRAW;
477
0
  gf_node_set_callback_function(node, TraverseRectangle);
478
0
}
479
480
481
0
#define CHECK_VALID_C2D(nbPts) if (!idx && cur_index+nbPts>=pt_count) { gf_path_reset(stack->path); return; }
482
//#define CHECK_VALID_C2D(nbPts)
483
0
#define GET_IDX(_i) ((idx && (idx->count>_i) && (idx->vals[_i]>=0) ) ? (u32) idx->vals[_i] : _i)
484
485
void curve2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state, MFInt32 *idx)
486
0
{
487
0
  M_Curve2D *c2D;
488
0
  SFVec2f orig, ct_orig, ct_end, end;
489
0
  u32 cur_index, i, remain, type_count, pt_count;
490
0
  SFVec2f *pts;
491
0
  M_Coordinate2D *coord;
492
493
0
  c2D = (M_Curve2D *)node;
494
0
  coord = (M_Coordinate2D *)c2D->point;
495
0
  drawable_reset_path(stack);
496
0
  if (!coord) return;
497
498
0
  stack->path->fineness = c2D->fineness;
499
0
  if (tr_state->visual->compositor->fast)  stack->path->fineness /= 2;
500
501
502
0
  pts = coord->point.vals;
503
0
  if (!pts)
504
0
    return;
505
506
0
  cur_index = c2D->type.count ? 1 : 0;
507
  /*if the first type is a moveTo skip initial moveTo*/
508
0
  i=0;
509
0
  if (cur_index) {
510
0
    while (c2D->type.vals[i]==0) i++;
511
0
  }
512
0
  ct_orig = orig = pts[ GET_IDX(i) ];
513
514
0
  gf_path_add_move_to(stack->path, orig.x, orig.y);
515
516
0
  pt_count = coord->point.count;
517
0
  type_count = c2D->type.count;
518
0
  for (; i<type_count; i++) {
519
520
0
    switch (c2D->type.vals[i]) {
521
    /*moveTo, 1 point*/
522
0
    case 0:
523
0
      CHECK_VALID_C2D(0);
524
0
      orig = pts[ GET_IDX(cur_index) ];
525
0
      if (i) gf_path_add_move_to(stack->path, orig.x, orig.y);
526
0
      cur_index += 1;
527
0
      break;
528
    /*lineTo, 1 point*/
529
0
    case 1:
530
0
      CHECK_VALID_C2D(0);
531
0
      end = pts[ GET_IDX(cur_index) ];
532
0
      gf_path_add_line_to(stack->path, end.x, end.y);
533
0
      orig = end;
534
0
      cur_index += 1;
535
0
      break;
536
    /*curveTo, 3 points*/
537
0
    case 2:
538
0
      CHECK_VALID_C2D(2);
539
0
      ct_orig = pts[ GET_IDX(cur_index) ];
540
0
      ct_end = pts[ GET_IDX(cur_index+1) ];
541
0
      end = pts[ GET_IDX(cur_index+2) ];
542
0
      gf_path_add_cubic_to(stack->path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y);
543
0
      cur_index += 3;
544
0
      ct_orig = ct_end;
545
0
      orig = end;
546
0
      break;
547
    /*nextCurveTo, 2 points (cf spec)*/
548
0
    case 3:
549
0
      CHECK_VALID_C2D(1);
550
0
      ct_orig.x = 2*orig.x - ct_orig.x;
551
0
      ct_orig.y = 2*orig.y - ct_orig.y;
552
0
      ct_end = pts[ GET_IDX(cur_index) ];
553
0
      end = pts[ GET_IDX(cur_index+1) ];
554
0
      gf_path_add_cubic_to(stack->path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y);
555
0
      cur_index += 2;
556
0
      ct_orig = ct_end;
557
0
      orig = end;
558
0
      break;
559
560
    /*all XCurve2D specific*/
561
562
    /*CW and CCW ArcTo*/
563
0
    case 4:
564
0
    case 5:
565
0
      CHECK_VALID_C2D(2);
566
0
      ct_orig = pts[ GET_IDX(cur_index) ];
567
0
      ct_end = pts[ GET_IDX(cur_index+1) ];
568
0
      end = pts[ GET_IDX(cur_index+2) ];
569
0
      gf_path_add_arc_to(stack->path, end.x, end.y, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, (c2D->type.vals[i]==5) ? 1 : 0);
570
0
      cur_index += 3;
571
0
      ct_orig = ct_end;
572
0
      orig = end;
573
0
      break;
574
    /*ClosePath*/
575
0
    case 6:
576
0
      gf_path_close(stack->path);
577
0
      break;
578
    /*quadratic CurveTo, 2 points*/
579
0
    case 7:
580
0
      CHECK_VALID_C2D(1);
581
0
      ct_end = pts[ GET_IDX(cur_index) ];
582
0
      end = pts[ GET_IDX(cur_index+1) ];
583
0
      gf_path_add_quadratic_to(stack->path, ct_end.x, ct_end.y, end.x, end.y);
584
0
      cur_index += 2;
585
0
      ct_orig = ct_end;
586
0
      orig = end;
587
0
      break;
588
0
    }
589
0
  }
590
591
  /*what's left is an N-bezier spline*/
592
0
  if (!idx && (pt_count > cur_index) ) {
593
    /*first moveto*/
594
0
    if (!cur_index) cur_index++;
595
596
0
    remain = pt_count - cur_index;
597
598
0
    if (remain>1)
599
0
      gf_path_add_bezier(stack->path, &pts[cur_index], remain);
600
0
  }
601
602
0
  gf_node_dirty_clear(node, 0);
603
0
  drawable_mark_modified(stack, tr_state);
604
0
}
605
606
static void TraverseCurve2D(GF_Node *node, void *rs, Bool is_destroy)
607
0
{
608
0
  DrawableContext *ctx;
609
0
  Drawable *stack = (Drawable *)gf_node_get_private(node);
610
0
  GF_TraverseState *tr_state = (GF_TraverseState *)rs;
611
612
0
  if (is_destroy) {
613
0
    drawable_node_del(node);
614
0
    return;
615
0
  }
616
0
  if (gf_node_dirty_get(node)) {
617
0
    curve2d_check_changes(node, stack, tr_state, NULL);
618
0
  }
619
620
0
  switch (tr_state->traversing_mode) {
621
#ifndef GPAC_DISABLE_3D
622
  case TRAVERSE_DRAW_3D:
623
    if (!stack->mesh) {
624
      stack->mesh = new_mesh();
625
      mesh_from_path(stack->mesh, stack->path);
626
    }
627
    visual_3d_draw_2d(stack, tr_state);
628
    return;
629
#endif
630
0
  case TRAVERSE_PICK:
631
0
    vrml_drawable_pick(stack, tr_state);
632
0
    return;
633
0
  case TRAVERSE_GET_BOUNDS:
634
0
    gf_path_get_bounds(stack->path, &tr_state->bounds);
635
0
    return;
636
0
  case TRAVERSE_SORT:
637
#ifndef GPAC_DISABLE_3D
638
    if (tr_state->visual->type_3d) return;
639
#endif
640
0
    ctx = drawable_init_context_mpeg4(stack, tr_state);
641
0
    if (!ctx) return;
642
0
    drawable_finalize_sort(ctx, tr_state, NULL);
643
0
    return;
644
0
  }
645
0
}
646
647
void compositor_init_curve2d(GF_Compositor  *compositor, GF_Node *node)
648
0
{
649
0
  drawable_stack_new(compositor, node);
650
0
  gf_node_set_callback_function(node, TraverseCurve2D);
651
0
}
652
653
654
655
/*
656
  Note on point set 2D: this is a very bad node and should be avoided in DEF/USE, since the size
657
  of the rectangle representing the pixel shall always be 1 pixel w/h, therefore
658
  the path object is likely not the same depending on transformation context...
659
660
*/
661
662
static void get_point_size(GF_Matrix2D *mat, Fixed *w, Fixed *h)
663
0
{
664
0
  GF_Point2D pt;
665
0
  pt.x = mat->m[0] + mat->m[1];
666
0
  pt.y = mat->m[3] + mat->m[4];
667
0
  *w = *h = gf_divfix(FLT2FIX(1.41421356f) , gf_v2d_len(&pt));
668
0
}
669
670
static void pointset2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state)
671
0
{
672
0
  u32 i;
673
0
  Fixed w, h;
674
0
  M_Coordinate2D *coord;
675
676
0
  if (!gf_node_dirty_get(node)) return;
677
0
  coord = (M_Coordinate2D *) ((M_PointSet2D *)node)->coord;
678
679
0
  drawable_reset_path(stack);
680
681
0
  get_point_size(&tr_state->transform, &w, &h);
682
  /*for PS2D don't add to avoid too  much antialiasing, just try to fill the given pixel*/
683
0
  for (i=0; i < coord->point.count; i++)
684
0
    gf_path_add_rect(stack->path, coord->point.vals[i].x, coord->point.vals[i].y, w, h);
685
686
0
  stack->path->flags |= GF_PATH_FILL_ZERO_NONZERO;
687
688
0
  gf_node_dirty_clear(node, 0);
689
0
  drawable_mark_modified(stack, tr_state);
690
0
}
691
692
static void PointSet2D_Draw(GF_Node *node, GF_TraverseState *tr_state)
693
0
{
694
0
  GF_Path *path;
695
0
  Fixed alpha, w, h;
696
0
  u32 i;
697
0
  SFColor col;
698
0
  DrawableContext *ctx = tr_state->ctx;
699
0
  M_PointSet2D *ps2D = (M_PointSet2D *)node;
700
0
  M_Coordinate2D *coord = (M_Coordinate2D*) ps2D->coord;
701
0
  M_Color *color = (M_Color *) ps2D->color;
702
703
  /*never outline PS2D*/
704
0
  ctx->flags |= CTX_PATH_STROKE;
705
0
  if (!color || color->color.count<coord->point.count) {
706
    /*no texturing*/
707
0
    visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state);
708
0
    return;
709
0
  }
710
711
0
  get_point_size(&ctx->transform, &w, &h);
712
713
0
  path = gf_path_new();
714
0
  alpha = INT2FIX(GF_COL_A(ctx->aspect.line_color)) / 255;
715
0
  for (i = 0; i < coord->point.count; i++) {
716
0
    col = color->color.vals[i];
717
0
    ctx->aspect.line_color = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue);
718
0
    gf_path_add_rect_center(path, coord->point.vals[i].x, coord->point.vals[i].y, w, h);
719
0
    visual_2d_draw_path(tr_state->visual, path, ctx, NULL, NULL, tr_state);
720
0
    gf_path_reset(path);
721
0
    ctx->flags &= ~CTX_PATH_FILLED;
722
0
  }
723
0
  gf_path_del(path);
724
0
}
725
726
static void TraversePointSet2D(GF_Node *node, void *rs, Bool is_destroy)
727
0
{
728
0
  DrawableContext *ctx;
729
0
  M_PointSet2D *ps2D = (M_PointSet2D *)node;
730
0
  Drawable *stack = (Drawable *)gf_node_get_private(node);
731
0
  GF_TraverseState *tr_state = (GF_TraverseState *)rs;
732
733
0
  if (is_destroy) {
734
0
    drawable_node_del(node);
735
0
    return;
736
0
  }
737
738
0
  if (!ps2D->coord) return;
739
740
0
  pointset2d_check_changes(node, stack, tr_state);
741
742
0
  switch (tr_state->traversing_mode) {
743
0
  case TRAVERSE_DRAW_2D:
744
0
    PointSet2D_Draw(node, tr_state);
745
0
    return;
746
#ifndef GPAC_DISABLE_3D
747
  case TRAVERSE_DRAW_3D:
748
  {
749
    DrawAspect2D asp;
750
    if (!stack->mesh) {
751
      stack->mesh = new_mesh();
752
      mesh_new_ps(stack->mesh, ps2D->coord, ps2D->color);
753
    }
754
    memset(&asp, 0, sizeof(DrawAspect2D));
755
    drawable_get_aspect_2d_mpeg4(node, &asp, tr_state);
756
    visual_3d_set_material_2d_argb(tr_state->visual, asp.fill_color);
757
    visual_3d_mesh_paint(tr_state, stack->mesh);
758
    return;
759
  }
760
#endif
761
0
  case TRAVERSE_GET_BOUNDS:
762
0
    gf_path_get_bounds(stack->path, &tr_state->bounds);
763
0
    return;
764
0
  case TRAVERSE_PICK:
765
0
    return;
766
0
  case TRAVERSE_SORT:
767
#ifndef GPAC_DISABLE_3D
768
    if (tr_state->visual->type_3d) return;
769
#endif
770
0
    ctx = drawable_init_context_mpeg4(stack, tr_state);
771
0
    if (!ctx) return;
772
0
    drawable_finalize_sort(ctx, tr_state, NULL);
773
0
    break;
774
0
  default:
775
0
    return;
776
0
  }
777
0
}
778
779
780
void compositor_init_pointset2d(GF_Compositor  *compositor, GF_Node *node)
781
0
{
782
0
  Drawable *stack = drawable_stack_new(compositor, node);
783
0
  stack->flags = DRAWABLE_USE_TRAVERSE_DRAW;
784
0
  gf_node_set_callback_function(node, TraversePointSet2D);
785
0
}
786
787
static void TraverseBitWrapper(GF_Node *node, void *rs, Bool is_destroy)
788
0
{
789
0
  GF_TraverseState *tr_state;
790
0
  M_BitWrapper *bitWrap;
791
792
0
  if (is_destroy) {
793
0
    gf_node_set_private(node, NULL);
794
0
    return;
795
0
  }
796
#ifdef GPAC_ENABLE_COVERAGE
797
  if (!rs) return;
798
#endif
799
800
0
  tr_state = (GF_TraverseState *)rs;
801
  // Traverse the node here
802
0
  bitWrap = (M_BitWrapper *)node;
803
0
  gf_node_traverse(bitWrap->node, tr_state);
804
0
}
805
806
void compositor_init_bitwrapper(GF_Compositor *compositor, GF_Node *node)
807
0
{
808
0
  M_BitWrapper *bit;
809
0
  bit = (M_BitWrapper *)node;
810
0
  if (!bit->node) return;
811
0
  gf_node_set_private(node, gf_node_get_private(bit->node));
812
0
  gf_node_set_callback_function(node, TraverseBitWrapper);
813
814
#ifdef GPAC_ENABLE_COVERAGE
815
  if (gf_sys_is_cov_mode()) {
816
    TraverseBitWrapper(node, NULL, GF_FALSE);
817
  }
818
#endif
819
0
}
820
821
822
#endif /*GPAC_DISABLE_VRML*/
823
#endif //!defined(GPAC_DISABLE_COMPOSITOR)