Coverage Report

Created: 2026-01-23 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/compositor/mpeg4_composite.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 <gpac/internal/scenegraph_dev.h>
29
30
#include "nodes_stacks.h"
31
#include "visual_manager.h"
32
#include "texturing.h"
33
34
#if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_COMPOSITOR)
35
36
#ifdef GPAC_USE_TINYGL
37
#include <GL/oscontext.h>
38
#endif
39
40
typedef struct
41
{
42
  GF_TextureHandler txh;
43
  Fixed sx, sy;
44
  /*the visual object handling the texture*/
45
  GF_VisualManager *visual;
46
  Bool first, unsupported;
47
  GF_List *sensors, *previous_sensors, *temp_sensors, *temp_previous_sensors;
48
  GF_Node *prev_hit_appear;
49
  //revent recursions
50
  Bool in_handle_event;
51
  GF_TraverseState *tr_state;
52
53
#ifdef GPAC_USE_TINYGL
54
  ostgl_context *tgl_ctx;
55
#else
56
  Bool use_fbo;
57
#endif
58
} CompositeTextureStack;
59
60
61
static Bool composite2d_draw_bitmap(GF_VisualManager *visual, GF_TraverseState *tr_state, struct _drawable_context *ctx)
62
0
{
63
0
  u8 alpha = 0xFF;
64
0
  GF_VideoSurface offscreen_dst, video_src;
65
0
  GF_Window src_wnd, dst_wnd;
66
0
  Bool use_blit, has_scale;
67
0
  CompositeTextureStack *st;
68
69
0
  if (visual->compositor->disable_composite_blit) return 0;
70
71
0
  if (!ctx->aspect.fill_texture) return 1;
72
0
  if (ctx->transform.m[0]<0) return 0;
73
0
  if (ctx->transform.m[4]<0) {
74
0
    if (!(ctx->flags & CTX_FLIPED_COORDS)) return 0;
75
0
  } else {
76
0
    if (ctx->flags & CTX_FLIPED_COORDS) return 0;
77
0
  }
78
0
  if (ctx->transform.m[1] || ctx->transform.m[3]) return 0;
79
0
#ifndef GPAC_DISABLE_VRML
80
0
  if ((ctx->flags & CTX_HAS_APPEARANCE) && ctx->appear && ((M_Appearance*)ctx->appear)->textureTransform)
81
0
    return 0;
82
0
#endif
83
84
0
  alpha = GF_COL_A(ctx->aspect.fill_color);
85
  /*THIS IS A HACK, will not work when setting filled=0, transparency and XLineProps*/
86
0
  if (!alpha) alpha = GF_COL_A(ctx->aspect.line_color);
87
0
  if (!alpha) return 1;
88
89
0
  st = (CompositeTextureStack *) gf_node_get_private(visual->offscreen);
90
91
0
  if (!compositor_texture_rectangles(visual, ctx->aspect.fill_texture, &ctx->bi->clip, &ctx->bi->unclip, &src_wnd, &dst_wnd, &use_blit, &has_scale)) return 1;
92
0
  if (! ctx->aspect.fill_texture->data) return 0;
93
94
0
  memset(&video_src, 0, sizeof(GF_VideoSurface));
95
0
  video_src.height = ctx->aspect.fill_texture->height;
96
0
  video_src.width = ctx->aspect.fill_texture->width;
97
0
  video_src.pitch_x = 0;
98
0
  video_src.pitch_y = ctx->aspect.fill_texture->stride;
99
0
  video_src.pixel_format = ctx->aspect.fill_texture->pixelformat;
100
0
#ifdef GF_SR_USE_DEPTH
101
0
  if (ctx->aspect.fill_texture->pixelformat==GF_PIXEL_YUVD) video_src.pixel_format = GF_PIXEL_YUV;
102
0
#endif
103
0
  video_src.video_buffer = ctx->aspect.fill_texture->data;
104
105
0
  memset(&offscreen_dst, 0, sizeof(GF_VideoSurface));
106
0
  offscreen_dst.width = st->txh.width;
107
0
  offscreen_dst.height = st->txh.height;
108
0
  offscreen_dst.pitch_y = st->txh.stride;
109
0
  offscreen_dst.pixel_format = st->txh.pixelformat;
110
0
  offscreen_dst.video_buffer = st->txh.data;
111
112
0
  gf_stretch_bits(&offscreen_dst, &video_src, &dst_wnd, &src_wnd, alpha, 0, tr_state->col_key, ctx->col_mat);
113
0
  return 1;
114
0
}
115
116
static void composite_traverse(GF_Node *node, void *rs, Bool is_destroy)
117
0
{
118
0
  if (is_destroy) {
119
0
    u32 i=0;
120
0
    GF_VisualManager *a_visual;
121
0
    CompositeTextureStack *st = (CompositeTextureStack *) gf_node_get_private(node);
122
    /*unregister visual*/
123
0
    gf_sc_visual_unregister(st->visual->compositor, st->visual);
124
125
    /*We must make sure we don't keep pointers to this composite in the different visuals.
126
      - we must track Appearance nodes at the compositor level to undo the textureTransform while picking
127
      - but we clearly don't want to track destruction of all appearance nodes just to solve this texture delete
128
        => remove the entire compositeTexture appearance state - this may lead to small bugs in interaction logics, however they should
129
        not be too damageable
130
    */
131
0
    st->visual->compositor->hit_appear = NULL;
132
0
    st->visual->compositor->prev_hit_appear = NULL;
133
134
0
    while ( (a_visual = gf_list_enum(st->visual->compositor->visuals, &i))) {
135
0
      if (a_visual->offscreen) {
136
0
        CompositeTextureStack *a_st = (CompositeTextureStack *) gf_node_get_private(a_visual->offscreen);
137
0
        a_st->prev_hit_appear = NULL;
138
0
      }
139
0
    }
140
141
0
    visual_del(st->visual);
142
0
    if (st->txh.data) gf_free(st->txh.data);
143
    /*destroy texture*/
144
0
    gf_sc_texture_destroy(&st->txh);
145
#ifdef GPAC_USE_TINYGL
146
    if (st->tgl_ctx) ostgl_delete_context(st->tgl_ctx);
147
#endif
148
149
0
    gf_list_del(st->sensors);
150
0
    gf_list_del(st->previous_sensors);
151
152
0
    gf_list_del(st->tr_state->vrml_sensors);
153
0
    gf_free(st->tr_state);
154
155
0
    gf_free(st);
156
0
  } else {
157
0
    gf_node_traverse_children(node, rs);
158
0
  }
159
0
}
160
161
static Bool composite_do_bindable(GF_Node *n, GF_TraverseState *tr_state, Bool force_check)
162
0
{
163
0
  GF_Node *btop;
164
0
  Bool ret = 0;
165
0
  switch (gf_node_get_tag(n)) {
166
#ifndef GPAC_DISABLE_3D
167
  case TAG_MPEG4_CompositeTexture3D:
168
  {
169
    M_CompositeTexture3D*c3d = (M_CompositeTexture3D*)n;
170
    if (force_check || gf_node_dirty_get(c3d->background)) {
171
      gf_node_traverse(c3d->background, tr_state);
172
      ret = 1;
173
    }
174
    btop = (GF_Node*)gf_list_get(tr_state->backgrounds, 0);
175
    if (btop != c3d->background) {
176
      gf_node_unregister(c3d->background, n);
177
      gf_node_register(btop, n);
178
      c3d->background = btop;
179
      gf_node_event_out(n, 5/*"background"*/);
180
      ret = 1;
181
    }
182
    if (force_check || gf_node_dirty_get(c3d->viewpoint)) {
183
      gf_node_traverse(c3d->viewpoint, tr_state);
184
      ret = 1;
185
    }
186
    btop = (GF_Node*)gf_list_get(tr_state->viewpoints, 0);
187
    if (btop != c3d->viewpoint) {
188
      gf_node_unregister(c3d->viewpoint, n);
189
      gf_node_register(btop, n);
190
      c3d->viewpoint = btop;
191
      gf_node_event_out(n, 8/*"viewpoint"*/);
192
      ret = 1;
193
    }
194
195
    if (force_check || gf_node_dirty_get(c3d->fog)) {
196
      gf_node_traverse(c3d->fog, tr_state);
197
      ret = 1;
198
    }
199
    btop = (GF_Node*)gf_list_get(tr_state->fogs, 0);
200
    if (btop != c3d->fog) {
201
      gf_node_unregister(c3d->fog, n);
202
      gf_node_register(btop, n);
203
      c3d->fog = btop;
204
      gf_node_event_out(n, 6/*"fog"*/);
205
      ret = 1;
206
    }
207
208
    if (force_check || gf_node_dirty_get(c3d->navigationInfo)) {
209
      gf_node_traverse(c3d->navigationInfo, tr_state);
210
      ret = 1;
211
    }
212
    btop = (GF_Node*)gf_list_get(tr_state->navigations, 0);
213
    if (btop != c3d->navigationInfo) {
214
      gf_node_unregister(c3d->navigationInfo, n);
215
      gf_node_register(btop, n);
216
      c3d->navigationInfo = btop;
217
      gf_node_event_out(n, 7/*"navigationInfo"*/);
218
      ret = 1;
219
    }
220
    return ret;
221
  }
222
#endif
223
0
  case TAG_MPEG4_CompositeTexture2D:
224
0
  {
225
0
    M_CompositeTexture2D *c2d = (M_CompositeTexture2D*)n;
226
0
    if (force_check || gf_node_dirty_get(c2d->background)) {
227
0
      gf_node_traverse(c2d->background, tr_state);
228
0
      ret = 1;
229
0
    }
230
0
    btop = (GF_Node*)gf_list_get(tr_state->backgrounds, 0);
231
0
    if (btop != c2d->background) {
232
0
      gf_node_unregister(c2d->background, n);
233
0
      gf_node_register(btop, n);
234
0
      c2d->background = btop;
235
0
      gf_node_event_out(n, 5/*"background"*/);
236
0
      ret = 1;
237
0
    }
238
239
0
    if (force_check || gf_node_dirty_get(c2d->viewport)) {
240
0
      gf_node_traverse(c2d->viewport, tr_state);
241
0
      ret = 1;
242
0
    }
243
0
    btop = (GF_Node*)gf_list_get(tr_state->viewpoints, 0);
244
0
    if (btop != c2d->viewport) {
245
0
      gf_node_unregister(c2d->viewport, n);
246
0
      gf_node_register(btop, n);
247
0
      c2d->viewport = btop;
248
0
      gf_node_event_out(n, 6/*"viewport"*/);
249
0
      ret = 1;
250
0
    }
251
252
0
    return ret;
253
0
  }
254
0
  }
255
0
  return 0;
256
0
}
257
258
static void composite_update(GF_TextureHandler *txh)
259
0
{
260
0
  s32 w, h;
261
0
  GF_EVGStencil *stencil;
262
0
#ifndef GPAC_USE_GLES1X
263
0
  M_Background2D *back;
264
0
#endif
265
0
  GF_List *sensor_bck;
266
0
  Bool invalidate_all;
267
0
  u32 new_pixel_format;
268
0
  GF_Compositor *compositor = (GF_Compositor *)txh->compositor;
269
0
  CompositeTextureStack *st = (CompositeTextureStack *) gf_node_get_private(txh->owner);
270
271
0
  if (st->unsupported) return;
272
273
  /*
274
    if (compositor->recompute_ar) {
275
      gf_node_dirty_set(txh->owner, 0, 0);
276
      return;
277
    }
278
  */
279
0
  if (!compositor->rebuild_offscreen_textures && (!compositor->text_edit_changed || !st->visual->has_text_edit ) && !gf_node_dirty_get(txh->owner)) {
280
0
    txh->needs_refresh = 0;
281
0
    return;
282
0
  }
283
0
  gf_node_dirty_clear(st->txh.owner, 0);
284
285
  /*in OpenGL_ES, only RGBA can be safelly used with glReadPixels*/
286
#if defined(GPAC_USE_GLES1X)
287
  new_pixel_format = GF_PIXEL_RGBA;
288
289
#else
290
291
0
  back = gf_list_get(st->visual->back_stack, 0);
292
0
  if (back && back->isBound) new_pixel_format = GF_PIXEL_RGB;
293
0
  else new_pixel_format = GF_PIXEL_RGBA;
294
295
#ifdef GPAC_USE_TINYGL
296
  /*TinyGL pixel format is fixed at compile time, we cannot override it !*/
297
  if (st->visual->type_3d) new_pixel_format = GF_PIXEL_RGBA;
298
299
#elif !defined(GPAC_DISABLE_3D)
300
  /*no alpha support in offscreen rendering*/
301
  if (!compositor->visual->type_3d && !compositor->hybrid_opengl && !compositor->fbo_id && (st->visual->type_3d) && !(compositor->video_out->hw_caps & GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA))
302
    new_pixel_format = GF_PIXEL_RGB;
303
#endif
304
305
0
#endif //GPAC_USE_GLES1X
306
307
  /*FIXME - we assume RGB+Depth+bitshape, we should check with the video out module*/
308
#if defined(GF_SR_USE_DEPTH) && !defined(GPAC_DISABLE_3D)
309
  if (st->visual->type_3d && (compositor->video_out->hw_caps & GF_VIDEO_HW_HAS_DEPTH) ) new_pixel_format = GF_PIXEL_RGBDS;
310
#endif
311
312
313
#ifndef GPAC_DISABLE_3D
314
  if (st->visual->type_3d>1) {
315
    w = ((M_CompositeTexture3D*)txh->owner)->pixelWidth;
316
    h = ((M_CompositeTexture3D*)txh->owner)->pixelHeight;
317
  } else
318
#endif
319
0
  {
320
0
    w = ((M_CompositeTexture2D*)txh->owner)->pixelWidth;
321
0
    h = ((M_CompositeTexture2D*)txh->owner)->pixelHeight;
322
0
  }
323
324
  /*internal GPAC hacks for testing color spaces*/
325
0
  if (w<-1) {
326
0
    w = -w;
327
0
    if (h<0) {
328
0
      h = -h;
329
0
      if (new_pixel_format==GF_PIXEL_RGBA) {
330
0
        new_pixel_format=GF_PIXEL_ARGB;
331
0
      } else {
332
0
        new_pixel_format=GF_PIXEL_BGR;
333
0
      }
334
0
    } else {
335
0
      if (new_pixel_format==GF_PIXEL_RGB) {
336
0
        new_pixel_format=GF_PIXEL_RGBX;
337
0
      }
338
0
    }
339
0
  }
340
0
  else if (h<-1) {
341
0
    h = -h;
342
0
    if (new_pixel_format==GF_PIXEL_RGB) {
343
0
      new_pixel_format=GF_PIXEL_RGBX;
344
0
    }
345
0
  }
346
347
0
  if (w<0) w = 0;
348
0
  if (h<0) h = 0;
349
350
351
0
  if (!w || !h) {
352
0
    if (txh->tx_io) {
353
#ifdef GPAC_USE_TINYGL
354
      if (st->tgl_ctx) ostgl_delete_context(st->tgl_ctx);
355
#endif
356
0
      gf_sc_texture_release(txh);
357
0
      if (txh->data) gf_free(txh->data);
358
0
      txh->data = NULL;
359
0
      txh->width = txh->height = txh->stride = 0;
360
0
    }
361
0
    return;
362
0
  }
363
0
  invalidate_all = compositor->rebuild_offscreen_textures;
364
365
  /*rebuild stencil*/
366
0
  if (!txh->tx_io
367
0
          || (w != (s32) txh->width) || ( h != (s32) txh->height)
368
0
          || (new_pixel_format != txh->pixelformat)
369
0
     ) {
370
371
0
    Bool needs_stencil = 1;
372
0
    if (txh->tx_io) {
373
#ifdef GPAC_USE_TINYGL
374
      if (st->tgl_ctx) ostgl_delete_context(st->tgl_ctx);
375
#endif
376
0
      gf_sc_texture_release(txh);
377
0
      if (txh->data)
378
0
        gf_free(txh->data);
379
0
      txh->data = NULL;
380
0
    }
381
382
    /*we don't use rect ext because of no support for texture transforms*/
383
0
    if ((1)
384
#ifndef GPAC_DISABLE_3D
385
            || compositor->gl_caps.npot_texture
386
#endif
387
0
       ) {
388
0
      st->txh.width = w;
389
0
      st->txh.height = h;
390
0
      st->sx = st->sy = FIX_ONE;
391
0
    } else {
392
0
      st->txh.width = 2;
393
0
      while (st->txh.width<(u32)w) st->txh.width*=2;
394
0
      st->txh.height = 2;
395
0
      while (st->txh.height<(u32)h) st->txh.height*=2;
396
397
0
      st->sx = INT2FIX(st->txh.width) / w;
398
0
      st->sy = INT2FIX(st->txh.height) / h;
399
0
    }
400
401
0
    gf_sc_texture_allocate(txh);
402
0
    txh->pixelformat = new_pixel_format;
403
0
    switch (new_pixel_format) {
404
0
    case GF_PIXEL_RGBA:
405
0
    case GF_PIXEL_ARGB:
406
0
      txh->stride = txh->width * 4;
407
0
      txh->transparent = 1;
408
0
      break;
409
0
    case GF_PIXEL_RGB_565:
410
0
      txh->stride = txh->width * 2;
411
0
      txh->transparent = 0;
412
0
      break;
413
0
    case GF_PIXEL_RGBDS:
414
0
      txh->stride = txh->width * 4;
415
0
      txh->transparent = 1;
416
0
      break;
417
0
    case GF_PIXEL_RGB:
418
0
      txh->stride = txh->width * 3;
419
0
      txh->transparent = 0;
420
0
      break;
421
0
    }
422
423
0
    st->visual->width = txh->width;
424
0
    st->visual->height = txh->height;
425
0
    st->use_fbo = GF_FALSE;
426
427
0
    stencil = gf_evg_stencil_new(GF_STENCIL_TEXTURE);
428
429
#ifndef GPAC_DISABLE_3D
430
    if (st->visual->type_3d) {
431
      /*figure out what to do if main visual (eg video out) is not in OpenGL ...*/
432
      if (!compositor->visual->type_3d && !compositor->hybrid_opengl) {
433
        /*create an offscreen window for OpenGL rendering*/
434
        if (!compositor->fbo_id
435
          && ((compositor->offscreen_width < st->txh.width) || (compositor->offscreen_height < st->txh.height))
436
        ) {
437
438
          GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] Offscreen OpenGL is not possible if no OpenGL context is created - use hybridGL mode for compositor\n"));
439
          st->unsupported = GF_TRUE;
440
        }
441
      } else {
442
        needs_stencil = 0;
443
      }
444
    }
445
#endif
446
447
0
    if (needs_stencil) {
448
0
      txh->data = (char*)gf_malloc(sizeof(unsigned char) * txh->stride * txh->height);
449
0
      memset(txh->data, 0, sizeof(unsigned char) * txh->stride * txh->height);
450
451
      /*set stencil texture - we don't check error as an image could not be supported by the rasterizer
452
      but still supported by the blitter (case of RGBD/RGBDS)*/
453
0
      gf_evg_stencil_set_texture(stencil, txh->data, txh->width, txh->height, txh->stride, txh->pixelformat);
454
455
#ifdef GPAC_USE_TINYGL
456
      if (st->visual->type_3d && !compositor->visual->type_3d) {
457
        st->tgl_ctx = ostgl_create_context(txh->width, txh->height, txh->transparent ? 32 : 24, &txh->data, 1);
458
        GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] Creating TinyGL Offscreen context %p (%d %d - pf %s)\n", st->tgl_ctx, txh->width, txh->width, gf_4cc_to_str(txh->pixelformat)));
459
      }
460
#endif
461
0
    }
462
#if !defined(GPAC_USE_TINYGL) && !defined(GPAC_DISABLE_3D)
463
    else if (compositor->gl_caps.fbo) {
464
      if (gf_sc_texture_setup_fbo(&st->txh)==GF_OK)
465
        st->use_fbo = GF_TRUE;
466
    }
467
#endif
468
469
0
    invalidate_all = 1;
470
0
    gf_sc_texture_set_stencil(txh, stencil);
471
0
  }
472
0
  if (!txh->tx_io) return;
473
474
#ifndef GPAC_DISABLE_3D
475
  if (st->visual->camera.is_3D) {
476
#ifdef GPAC_USE_TINYGL
477
    st->visual->type_3d = 2;
478
#else
479
    if (compositor->visual->type_3d) {
480
      st->visual->type_3d = 2;
481
    } else if (compositor->hybrid_opengl || compositor->fbo_id) {
482
      st->visual->type_3d = 2;
483
    } else if (! (compositor->video_out->hw_caps & GF_VIDEO_HW_OPENGL_OFFSCREEN)) {
484
      st->visual->type_3d = 0;
485
    } else {
486
      st->visual->type_3d = 2;
487
    }
488
#endif
489
  }
490
#endif
491
492
0
  stencil = gf_sc_texture_get_stencil(txh);
493
0
  if (!stencil) return;
494
495
#ifdef GPAC_USE_TINYGL
496
  if (st->tgl_ctx)
497
    ostgl_make_current(st->tgl_ctx, 0);
498
#elif !defined(GPAC_DISABLE_3D)
499
  if (st->use_fbo) {
500
    gf_sc_texture_enable_fbo(&st->txh, GF_TRUE);
501
  }
502
#endif
503
504
0
  sensor_bck = st->tr_state->vrml_sensors;
505
0
  memset(st->tr_state, 0, sizeof(GF_TraverseState));
506
0
  st->tr_state->vrml_sensors = sensor_bck;
507
0
  st->tr_state->visual = st->visual;
508
#ifndef GPAC_DISABLE_3D
509
  st->tr_state->camera = &st->visual->camera;
510
#endif
511
0
  st->tr_state->invalidate_all = invalidate_all;
512
513
0
  st->tr_state->immediate_draw = st->visual->compositor->traverse_state->immediate_draw;
514
515
0
  gf_mx2d_init(st->tr_state->transform);
516
0
  gf_cmx_init(&st->tr_state->color_mat);
517
518
0
  st->tr_state->backgrounds = st->visual->back_stack;
519
0
  st->tr_state->viewpoints = st->visual->view_stack;
520
0
  st->tr_state->pixel_metrics = gf_sg_use_pixel_metrics(gf_node_get_graph(st->txh.owner));
521
0
  st->tr_state->min_hsize = INT2FIX( MIN(txh->width, txh->height) ) / 2;
522
0
  st->tr_state->vp_size.x = INT2FIX(txh->width);
523
0
  st->tr_state->vp_size.y = INT2FIX(txh->height);
524
525
0
  composite_do_bindable(st->txh.owner, st->tr_state, st->first);
526
0
  st->first = 0;
527
528
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] Entering draw cycle\n"));
529
530
0
  txh->needs_refresh = visual_draw_frame(st->visual, st->txh.owner, st->tr_state, 0);
531
0
  txh->transparent = (st->visual->last_had_back==2) ? 0 : 1;
532
533
0
  if (!compositor->edited_text && st->visual->has_text_edit)
534
0
    st->visual->has_text_edit = 0;
535
536
537
#if 0
538
  /*set active viewport in image coordinates top-left=(0, 0), not in BIFS*/
539
  if (gf_list_count(st->visual->view_stack)) {
540
    M_Viewport *vp = (M_Viewport *)gf_list_get(st->visual->view_stack, 0);
541
542
    if (vp->isBound) {
543
      SFVec2f size = vp->size;
544
      if (size.x >=0 && size.y>=0) {
545
        /*FIXME - we need tracking of VP changes*/
546
        txh->needs_refresh = 1;
547
      }
548
    }
549
  }
550
#endif
551
552
0
  if (txh->needs_refresh) {
553
#ifndef GPAC_DISABLE_3D
554
555
#ifndef GPAC_USE_TINYGL
556
    if (st->use_fbo) {
557
      gf_sc_texture_enable_fbo(&st->txh, GF_FALSE);
558
    } else
559
#endif
560
    //no FBO, for composite 3D, store current buffer to texture
561
    if (st->visual->camera.is_3D && (st->visual->compositor->visual->type_3d || st->visual->compositor->hybrid_opengl)) {
562
#ifndef GPAC_USE_TINYGL
563
      gf_sc_copy_to_texture(&st->txh);
564
#endif
565
    }
566
    else if (st->visual->camera.is_3D) {
567
      if (st->visual->compositor->visual->type_3d) {
568
#ifndef GPAC_USE_TINYGL
569
        gf_sc_copy_to_texture(&st->txh);
570
#else
571
        /*in TinyGL we only need to push associated bitmap to the texture*/
572
        gf_sc_texture_push_image(&st->txh, 0, 0);
573
#endif
574
      } else {
575
#ifndef GPAC_USE_TINYGL
576
        gf_sc_copy_to_stencil(&st->txh);
577
#else
578
        if (txh->pixelformat==GF_PIXEL_RGBDS)
579
          gf_get_tinygl_depth(&st->txh);
580
#endif
581
      }
582
    } else
583
#endif
584
0
    {
585
0
      gf_sc_texture_set_stencil(txh, stencil);
586
0
    }
587
0
    gf_sc_invalidate(st->txh.compositor, NULL);
588
0
  }
589
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] Leaving draw cycle\n"));
590
0
}
591
592
593
GF_Err composite_get_video_access(GF_VisualManager *visual)
594
0
{
595
0
  GF_EVGStencil *stencil;
596
0
  GF_Err e;
597
0
  CompositeTextureStack *st = (CompositeTextureStack *) gf_node_get_private(visual->offscreen);
598
599
0
  if (!st->txh.tx_io || !visual->raster_surface) return GF_BAD_PARAM;
600
0
  stencil = gf_sc_texture_get_stencil(&st->txh);
601
0
  if (!stencil) return GF_BAD_PARAM;
602
0
  e = gf_evg_surface_attach_to_texture(visual->raster_surface, stencil);
603
0
  if (!e) visual->is_attached = 1;
604
0
  return e;
605
0
}
606
607
void composite_release_video_access(GF_VisualManager *visual)
608
0
{
609
0
}
610
611
Bool composite_check_visual_attached(GF_VisualManager *visual)
612
0
{
613
0
  return visual->is_attached;
614
0
}
615
616
void compositor_init_compositetexture2d(GF_Compositor *compositor, GF_Node *node)
617
0
{
618
0
  M_CompositeTexture2D *c2d = (M_CompositeTexture2D *)node;
619
0
  CompositeTextureStack *st;
620
0
  GF_SAFEALLOC(st, CompositeTextureStack);
621
622
0
  if (!st) {
623
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate composite texture stack\n"));
624
0
    return;
625
0
  }
626
627
0
  GF_SAFEALLOC(st->tr_state, GF_TraverseState);
628
0
  if (!st->tr_state) {
629
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate composite texture state\n"));
630
0
    return;
631
0
  }
632
0
  st->tr_state->vrml_sensors = gf_list_new();
633
634
0
  st->sensors = gf_list_new();
635
0
  st->previous_sensors = gf_list_new();
636
0
  gf_sc_texture_setup(&st->txh, compositor, node);
637
  /*remove texture from compositor and add it at the end, so that any sub-textures are handled before*/
638
0
  gf_list_del_item(compositor->textures, &st->txh);
639
0
  gf_list_add(compositor->textures, &st->txh);
640
641
0
  st->txh.update_texture_fcnt = composite_update;
642
643
0
  if ((c2d->repeatSandT==1) || (c2d->repeatSandT==3) ) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S;
644
0
  if (c2d->repeatSandT>1) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T;
645
646
  /*create composite visual*/
647
0
  st->visual = visual_new(compositor);
648
0
  st->visual->offscreen = node;
649
0
  st->visual->GetSurfaceAccess = composite_get_video_access;
650
0
  st->visual->ReleaseSurfaceAccess = composite_release_video_access;
651
0
  st->visual->DrawBitmap = composite2d_draw_bitmap;
652
0
  st->visual->CheckAttached = composite_check_visual_attached;
653
654
0
  st->visual->raster_surface = gf_evg_surface_new(1);
655
656
657
0
  st->first = 1;
658
0
  st->visual->compositor = compositor;
659
0
  gf_node_set_private(node, st);
660
0
  gf_node_set_callback_function(node, composite_traverse);
661
0
  gf_sc_visual_register(compositor, st->visual);
662
0
}
663
664
665
#ifndef GPAC_DISABLE_3D
666
void compositor_init_compositetexture3d(GF_Compositor *compositor, GF_Node *node)
667
{
668
  M_CompositeTexture3D *c3d = (M_CompositeTexture3D *)node;
669
  CompositeTextureStack *st;
670
671
  if (!gf_sc_check_gl_support(compositor)) {
672
    GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Compositor] Driver disabled, cannot render 3D composite textures\n"));
673
    return;
674
  }
675
676
  GF_SAFEALLOC(st, CompositeTextureStack);
677
  if (!st) {
678
    GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate composite texture stack\n"));
679
    return;
680
  }
681
  GF_SAFEALLOC(st->tr_state, GF_TraverseState);
682
  if (!st->tr_state) {
683
    GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate composite texture state\n"));
684
    return;
685
  }
686
687
  st->tr_state->vrml_sensors = gf_list_new();
688
689
  st->sensors = gf_list_new();
690
  st->previous_sensors = gf_list_new();
691
  gf_sc_texture_setup(&st->txh, compositor, node);
692
  /*remove texture from compositor and add it at the end, so that any sub-textures are handled before*/
693
  gf_list_del_item(compositor->textures, &st->txh);
694
  gf_list_add(compositor->textures, &st->txh);
695
696
  st->txh.update_texture_fcnt = composite_update;
697
698
  if (c3d->repeatS) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S;
699
  if (c3d->repeatT) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T;
700
701
  /*create composite visual*/
702
  st->visual = visual_new(compositor);
703
  st->visual->offscreen = node;
704
  st->visual->GetSurfaceAccess = composite_get_video_access;
705
  st->visual->ReleaseSurfaceAccess = composite_release_video_access;
706
  st->visual->CheckAttached = composite_check_visual_attached;
707
708
  st->visual->camera.is_3D = 1;
709
  st->first = 1;
710
  st->visual->compositor = compositor;
711
  gf_node_set_private(node, st);
712
  gf_node_set_callback_function(node, composite_traverse);
713
  gf_sc_visual_register(compositor, st->visual);
714
715
  camera_invalidate(&st->visual->camera);
716
}
717
#endif
718
719
GF_TextureHandler *compositor_get_composite_texture(GF_Node *node)
720
0
{
721
0
  CompositeTextureStack *st = (CompositeTextureStack*) gf_node_get_private(node);
722
0
  return &st->txh;
723
0
}
724
725
Bool compositor_compositetexture_handle_event(GF_Compositor *compositor, GF_Node *composite_appear, GF_Event *ev, Bool is_flush)
726
0
{
727
0
  GF_Ray ray;
728
0
  Fixed dist;
729
0
  Bool had_text_sel=0;
730
0
  GF_Matrix mx;
731
0
  GF_ChildNodeItem *children, *l;
732
0
  Bool res;
733
0
  SFVec3f txcoord, loc_pt, world_pt;
734
0
  GF_Matrix l2w_mx, w2l_mx;
735
0
  CompositeTextureStack *stack;
736
0
  GF_Node *appear, *prev_appear;
737
0
  GF_List *sensor_bck;
738
0
  M_Appearance *ap = (M_Appearance *)composite_appear;
739
0
  if (!ap || !ap->texture) return 0;
740
741
0
  if (ev->type > GF_EVENT_MOUSEMOVE) return 0;
742
0
  stack = gf_node_get_private(ap->texture);
743
0
  if (!stack->txh.tx_io) return 0;
744
745
0
  if (stack->in_handle_event)
746
0
    return 0;
747
748
0
  children = NULL;
749
0
  stack->in_handle_event = GF_TRUE;
750
751
0
  if (!is_flush) {
752
0
    txcoord.x = compositor->hit_texcoords.x;
753
0
    txcoord.y = compositor->hit_texcoords.y;
754
0
    txcoord.z = 0;
755
0
    if (gf_sc_texture_get_transform(&stack->txh, ap->textureTransform, &mx, 1)) {
756
      /*tx coords are inverted when mapping, thus applying directly the matrix will give us the
757
      untransformed coords*/
758
0
      gf_mx_apply_vec(&mx, &txcoord);
759
0
      while (txcoord.x<0) txcoord.x += FIX_ONE;
760
0
      while (txcoord.x>FIX_ONE) txcoord.x -= FIX_ONE;
761
0
      while (txcoord.y<0) txcoord.y += FIX_ONE;
762
0
      while (txcoord.y>FIX_ONE) txcoord.y -= FIX_ONE;
763
0
    }
764
765
    /*convert to tx space*/
766
0
    ev->mouse.x = FIX2INT( (txcoord.x - FIX_ONE/2) * stack->visual->width + FIX_ONE/2);
767
0
    ev->mouse.y = FIX2INT( (txcoord.y - FIX_ONE/2) * stack->visual->height + FIX_ONE/2);
768
769
0
    sensor_bck = stack->tr_state->vrml_sensors;
770
0
    memset(stack->tr_state, 0, sizeof(GF_TraverseState));
771
0
    stack->tr_state->vrml_sensors = sensor_bck;
772
773
0
    stack->tr_state->visual = stack->visual;
774
0
    stack->tr_state->traversing_mode = TRAVERSE_PICK;
775
0
    stack->tr_state->pixel_metrics = gf_sg_use_pixel_metrics(gf_node_get_graph(ap->texture));
776
0
    stack->tr_state->vp_size.x = INT2FIX(stack->txh.width);
777
0
    stack->tr_state->vp_size.y = INT2FIX(stack->txh.height);
778
0
    stack->tr_state->color_mat.identity = 1;
779
780
0
    gf_mx2d_init(stack->tr_state->transform);
781
#ifndef GPAC_DISABLE_3D
782
    gf_mx_init(stack->tr_state->model_matrix);
783
#endif
784
    /*collect sensors but not anchors*/
785
0
    l = children = ((M_CompositeTexture2D*)ap->texture)->children;
786
0
    while (l) {
787
0
      GF_SensorHandler *hsens = compositor_mpeg4_get_sensor_handler_ex(l->node, GF_TRUE);
788
0
      if (hsens) gf_list_add(stack->tr_state->vrml_sensors, hsens);
789
0
      l = l->next;
790
0
    }
791
0
  }
792
793
0
  stack->temp_sensors = compositor->sensors;
794
0
  stack->temp_previous_sensors = compositor->previous_sensors;
795
0
  compositor->sensors = stack->sensors;
796
0
  compositor->previous_sensors = stack->previous_sensors;
797
798
0
  ray = compositor->hit_world_ray;
799
0
  dist = compositor->hit_square_dist;
800
0
  prev_appear = compositor->prev_hit_appear;
801
802
  /*protect against destrucion in case of self-destroy*/
803
0
  if (prev_appear) {
804
0
    gf_node_register(prev_appear, NULL);
805
0
  }
806
0
  compositor->prev_hit_appear = stack->prev_hit_appear;
807
0
  appear = compositor->hit_appear;
808
0
  compositor->hit_appear = NULL;
809
810
  /*also backup current hit state in case we hit a node in the texture but don't consume the event*/
811
0
  loc_pt = compositor->hit_local_point;
812
0
  world_pt = compositor->hit_world_point;
813
0
  gf_mx_copy(l2w_mx, compositor->hit_local_to_world);
814
0
  gf_mx_copy(w2l_mx, compositor->hit_world_to_local);
815
816
0
  if (compositor->text_selection) had_text_sel=1;
817
818
0
  if (is_flush) {
819
0
    res = 0;
820
0
    gf_list_reset(stack->sensors);
821
0
    gf_sc_exec_event_vrml(compositor, ev);
822
0
  } else {
823
0
    res = visual_execute_event(stack->visual, stack->tr_state, ev, children);
824
0
  }
825
826
0
  if (!had_text_sel && compositor->edited_text) {
827
0
    stack->visual->has_text_edit = 1;
828
0
  } else if (!compositor->text_selection) {
829
0
    stack->visual->has_text_edit = 0;
830
0
  }
831
832
0
  if (!res) {
833
0
    compositor->hit_local_point = loc_pt;
834
0
    gf_mx_copy(compositor->hit_local_to_world, l2w_mx);
835
0
    gf_mx_copy(compositor->hit_world_to_local, w2l_mx);
836
0
  }
837
838
0
  compositor->hit_world_point = world_pt;
839
0
  compositor->hit_world_ray = ray;
840
0
  compositor->hit_square_dist = dist;
841
842
0
  stack->sensors = compositor->sensors;
843
0
  stack->previous_sensors = compositor->previous_sensors;
844
0
  compositor->sensors = stack->temp_sensors;
845
0
  stack->temp_sensors = NULL;
846
0
  compositor->previous_sensors = stack->temp_previous_sensors;
847
0
  stack->temp_previous_sensors = NULL;
848
849
850
0
  if (!is_flush) {
851
#ifndef GPAC_DISABLE_3D
852
    if (stack->tr_state->layer3d) compositor->traverse_state->layer3d = stack->tr_state->layer3d;
853
#endif
854
0
  }
855
856
0
  stack->prev_hit_appear = compositor->prev_hit_appear;
857
858
  //finally unregister the node, this may destroy the stack !
859
0
  if (prev_appear) {
860
0
    if (prev_appear->sgprivate->num_instances>1) {
861
0
      compositor->prev_hit_appear = prev_appear;
862
0
      compositor->hit_appear = appear;
863
0
    } else {
864
0
      compositor->prev_hit_appear = NULL;
865
0
      compositor->hit_appear = NULL;
866
0
    }
867
0
    gf_node_unregister(prev_appear, NULL);
868
0
  } else {
869
0
    compositor->prev_hit_appear = prev_appear;
870
0
    compositor->hit_appear = appear;
871
0
  }
872
0
  stack->in_handle_event = GF_FALSE;
873
874
0
  return res;
875
0
}
876
877
void compositor_compositetexture_sensor_delete(GF_Node *composite_appear, GF_SensorHandler *hdl)
878
0
{
879
0
  CompositeTextureStack *stack = gf_node_get_private(composite_appear);
880
0
  gf_list_del_item(stack->previous_sensors, hdl);
881
0
  gf_list_del_item(stack->sensors, hdl);
882
0
  if (stack->temp_sensors)
883
0
    gf_list_del_item(stack->temp_sensors, hdl);
884
0
  if (stack->temp_previous_sensors)
885
0
    gf_list_del_item(stack->temp_previous_sensors, hdl);
886
0
}
887
888
889
void compositor_adjust_scale(GF_Node *node, Fixed *sx, Fixed *sy)
890
0
{
891
0
  switch (gf_node_get_tag(node)) {
892
0
  case TAG_MPEG4_CompositeTexture2D:
893
0
  case TAG_MPEG4_CompositeTexture3D:
894
0
  {
895
0
    CompositeTextureStack *st = (CompositeTextureStack *) gf_node_get_private(node);
896
0
    (*sx) = gf_divfix(*sx, st->sx);
897
0
    (*sy) = gf_divfix(*sy, st->sy);
898
0
    break;
899
0
  }
900
0
  default:
901
0
    return;
902
0
  }
903
0
}
904
905
Bool compositor_is_composite_texture(GF_Node *appear)
906
0
{
907
0
  M_Appearance *ap = NULL;
908
0
  u32 tag;
909
0
  if (!appear) return 0;
910
911
0
  tag = gf_node_get_tag(appear);
912
0
  if (tag==TAG_MPEG4_Appearance) ap = (M_Appearance *)appear;
913
0
#ifndef GPAC_DISABLE_X3D
914
0
  else if (tag==TAG_X3D_Appearance) ap = (M_Appearance *)appear;
915
0
#endif
916
0
  if (!ap) return 0;
917
0
  if (!ap->texture) return 0;
918
0
  switch (gf_node_get_tag(((M_Appearance *)appear)->texture)) {
919
0
  case TAG_MPEG4_CompositeTexture2D:
920
0
  case TAG_MPEG4_CompositeTexture3D:
921
0
    return 1;
922
0
  }
923
0
  return 0;
924
0
}
925
926
#endif //!defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_COMPOSITOR)