2
* GPAC - Multimedia Framework C SDK
4
* Authors: Cyril Concolato - Jean le Feuvre
5
* Copyright (c) 2005-200X ENST
8
* This file is part of GPAC / SVG Rendering sub-project
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)
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.
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.
26
#include "svg_stacks.h"
27
#include "visualsurface2d.h"
29
#ifndef GPAC_DISABLE_SVG
31
#include <gpac/internal/scenegraph_dev.h>
34
int processDanaeAudio(void *param, unsigned int scene_time);
37
/************************/
38
/* Generic URI handling */
39
/************************/
41
Bool SVG_SetMFURLFromURI(GF_Renderer *sr, MFURL *mfurl, SVG_IRI *iri)
45
if (!iri->iri) return 0;
47
gf_sg_vrml_mf_reset(mfurl, GF_SG_VRML_MFURL);
49
GF_SAFEALLOC(mfurl->vals, sizeof(SFURL))
53
if (!strncmp(iri->iri, "data:", 5)) {
54
const char *cache_dir = gf_cfg_get_key(sr->user->config, "General", "CacheDirectory");
55
ret = gf_svg_store_embedded_data(iri, cache_dir, "embedded_");
58
sfurl->url = strdup(iri->iri);
62
static Bool SVG_check_url_change(MFURL *url, SVG_IRI *iri)
64
if (url->count && !iri->iri) return 1;
65
if (!url->count && iri->iri) return 1;
66
if (!url->count) return 0;
67
if (!strcmp(url->vals[0].url, iri->iri)) return 0;
72
static void SVG_ComputeAR(Fixed objw, Fixed objh, Fixed viewportw, Fixed viewporth,
73
SVG_PreserveAspectRatio *par, GF_Matrix2D *par_mat)
75
gf_mx2d_init(*par_mat);
76
if (par->meetOrSlice == SVG_MEETORSLICE_MEET) {
78
case SVG_PRESERVEASPECTRATIO_NONE:
80
case SVG_PRESERVEASPECTRATIO_XMINYMIN:
82
case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
84
case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
86
case SVG_PRESERVEASPECTRATIO_XMINYMID:
88
case SVG_PRESERVEASPECTRATIO_XMIDYMID:
90
case SVG_PRESERVEASPECTRATIO_XMAXYMID:
92
case SVG_PRESERVEASPECTRATIO_XMINYMAX:
94
case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
96
case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
103
static void SVG_Draw_bitmap(DrawableContext *ctx)
105
GF_ColorMatrix *cmat;
109
sr = ctx->surface->render;
112
alpha = GF_COL_A(ctx->aspect.fill_color);
115
if (!ctx->cmat.identity) cmat = &ctx->cmat;
116
//else if (ctx->h_texture->has_cmat) cmat = NULL;
118
/*this is not a native texture, use graphics*/
119
if (!ctx->h_texture->data || ctx->transform.m[1] || ctx->transform.m[3]) {
122
if (!ctx->surface->SupportsFormat || !ctx->surface->DrawBitmap ) use_blit = 0;
123
/*format not supported directly, try with brush*/
124
else if (!ctx->surface->SupportsFormat(ctx->surface, ctx->h_texture->pixelformat) ) use_blit = 0;
127
/*no HW, fall back to the graphics driver*/
129
VS2D_TexturePath(ctx->surface, ctx->node->path, ctx);
133
/*direct rendering, render without clippers */
134
if (ctx->surface->render->top_effect->trav_flags & TF_RENDER_DIRECT) {
135
ctx->surface->DrawBitmap(ctx->surface, ctx->h_texture, &ctx->clip, &ctx->unclip, alpha, NULL, cmat);
137
/*render bitmap for all dirty rects*/
141
for (i=0; i<ctx->surface->to_redraw.count; i++) {
142
/*there's an opaque region above, don't draw*/
143
if (ctx->surface->draw_node_index<ctx->surface->to_redraw.opaque_node_index[i]) continue;
145
gf_irect_intersect(&clip, &ctx->surface->to_redraw.list[i]);
146
if (clip.width && clip.height) {
147
ctx->surface->DrawBitmap(ctx->surface, ctx->h_texture, &clip, &ctx->unclip, alpha, NULL, cmat);
153
static void SVG_BuildGraph_image(SVG_image_stack *st)
156
SVGimageElement *img = (SVGimageElement *)st->graph->owner;
157
gf_path_get_bounds(st->graph->path, &rc);
158
drawable_reset_path(st->graph);
159
gf_path_add_rect_center(st->graph->path, img->x.value+img->width.value/2, img->y.value+img->height.value/2, img->width.value, img->height.value);
160
gf_path_get_bounds(st->graph->path, &new_rc);
161
/*change in visual aspect*/
162
if (!gf_rect_equal(rc, new_rc)) st->graph->node_changed = 1;
163
gf_node_dirty_clear(st->graph->owner, GF_SG_SVG_GEOMETRY_DIRTY);
167
static void SVG_BuildGraph_video(SVG_video_stack *st)
170
SVGvideoElement *video = (SVGvideoElement *)st->graph->owner;
171
gf_path_get_bounds(st->graph->path, &rc);
172
drawable_reset_path(st->graph);
173
gf_path_add_rect_center(st->graph->path, video->x.value+video->width.value/2, video->y.value+video->height.value/2, video->width.value, video->height.value);
174
gf_path_get_bounds(st->graph->path, &new_rc);
175
/*change in visual aspect*/
176
if (!gf_rect_equal(rc, new_rc)) st->graph->node_changed = 1;
177
gf_node_dirty_clear(st->graph->owner, GF_SG_SVG_GEOMETRY_DIRTY);
180
static void SVG_Render_bitmap(GF_Node *node, void *rs)
182
/*video stack is just an extension of image stack, type-casting is OK*/
183
SVG_image_stack *st = (SVG_image_stack*)gf_node_get_private(node);
184
RenderEffect2D *eff = (RenderEffect2D *)rs;
185
SVGPropertiesPointers backup_props;
186
GF_Matrix2D backup_matrix;
188
DrawableContext *ctx;
190
SVG_Render_base(node, (RenderEffect2D *)rs, &backup_props);
192
if (gf_node_dirty_get(node)) {
193
if (gf_node_get_tag(node)==TAG_SVG_image) {
194
SVG_BuildGraph_image(gf_node_get_private(node));
195
m = &((SVGimageElement *)node)->transform;
197
SVG_BuildGraph_video(gf_node_get_private(node));
198
m = &((SVGvideoElement *)node)->transform;
201
/*if open and changed, stop and play*/
202
if (SVG_check_url_change(&st->txurl, & ((SVGElement *)node)->xlink->href)) {
203
const char *cache_dir = gf_cfg_get_key(st->txh.compositor->user->config, "General", "CacheDirectory");
204
gf_svg_store_embedded_data(& ((SVGElement *)node)->xlink->href, cache_dir, "embedded_");
206
if (SVG_check_url_change(&st->txurl, & ((SVGElement *)node)->xlink->href)) {
207
SVG_SetMFURLFromURI(st->txh.compositor, &(st->txurl), & ((SVGElement*)node)->xlink->href);
208
if (st->txh.is_open) gf_sr_texture_stop(&st->txh);
209
fprintf(stdout, "URL changed to %s\n", st->txurl.vals[0].url);
210
gf_sr_texture_play(&st->txh, &st->txurl);
213
gf_node_dirty_clear(node, 0);
215
if (gf_node_get_tag(node)==TAG_SVG_image) {
216
m = &((SVGimageElement *)node)->transform;
218
m = &((SVGvideoElement *)node)->transform;
222
/*FIXME: setup aspect ratio*/
224
if (eff->trav_flags & TF_RENDER_GET_BOUNDS) {
225
gf_mx2d_pre_multiply(&eff->transform, m);
226
if (*(eff->svg_props->display) != SVG_DISPLAY_NONE) {
227
gf_path_get_bounds(st->graph->path, &eff->bounds);
229
memcpy(eff->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
233
if (*(eff->svg_props->display) == SVG_DISPLAY_NONE ||
234
*(eff->svg_props->visibility) == SVG_VISIBILITY_HIDDEN) {
235
memcpy(eff->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
239
gf_mx2d_copy(backup_matrix, eff->transform);
240
gf_mx2d_pre_multiply(&eff->transform, m);
242
ctx = SVG_drawable_init_context(st->graph, eff);
243
if (!ctx || !ctx->h_texture ) return;
245
gf_path_get_bounds(ctx->node->path, &ctx->original);
247
/*even if set this is not true*/
248
ctx->aspect.has_line = 0;
249
/*this is to make sure we don't fill the path if the texture is transparent*/
250
ctx->aspect.filled = 0;
251
ctx->aspect.pen_props.width = 0;
252
ctx->no_antialias = 1;
254
/*if rotation, transparent*/
255
ctx->transparent = 0;
256
if (ctx->transform.m[1] || ctx->transform.m[3]) {
257
ctx->transparent = 1;
258
ctx->no_antialias = 0;
260
else if (ctx->h_texture->transparent)
261
ctx->transparent = 1;
262
else if (eff->svg_props->opacity && (eff->svg_props->opacity->type==SVG_NUMBER_VALUE) && (eff->svg_props->opacity->value!=FIX_ONE)) {
263
ctx->transparent = 1;
264
ctx->aspect.fill_alpha = FIX2INT(0xFF * eff->svg_props->opacity->value);
265
ctx->aspect.fill_color = ctx->aspect.fill_alpha << 24;
268
/*bounds are stored when building graph*/
269
drawable_finalize_render(ctx, eff);
270
gf_mx2d_copy(eff->transform, backup_matrix);
271
memcpy(eff->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
274
static Bool SVG_PointOver_bitmap(DrawableContext *ctx, Fixed x, Fixed y, u32 check_type)
280
/*********************/
281
/* SVG image element */
282
/*********************/
284
static void SVG_Update_image(GF_TextureHandler *txh)
286
SVGimageElement *txnode = (SVGimageElement *) txh->owner;
287
MFURL *txurl = &(((SVG_image_stack *)gf_node_get_private((GF_Node *)txnode))->txurl);
289
/*setup texture if needed*/
290
if (!txh->is_open && txurl->count) {
291
gf_sr_texture_play(txh, txurl);
293
gf_sr_texture_update_frame(txh, 0);
294
/*URL is present but not opened - redraw till fetch*/
295
if (txh->stream && !txh->hwtx) gf_sr_invalidate(txh->compositor, NULL);
298
static void SVG_Destroy_image(GF_Node *node)
300
SVG_image_stack *st = (SVG_image_stack *)gf_node_get_private(node);
302
gf_sr_texture_destroy(&st->txh);
303
gf_sg_mfurl_del(st->txurl);
305
drawable_del(st->graph);
309
void SVG_Init_image(Render2D *sr, GF_Node *node)
312
GF_SAFEALLOC(st, sizeof(SVG_image_stack))
313
st->graph = drawable_new();
315
gf_sr_traversable_setup(st->graph, node, sr->compositor);
316
st->graph->Draw = SVG_Draw_bitmap;
317
st->graph->IsPointOver = SVG_PointOver_bitmap;
319
gf_sr_texture_setup(&st->txh, sr->compositor, node);
320
st->txh.update_texture_fcnt = SVG_Update_image;
323
/* builds the MFURL to be used by the texture */
324
SVG_SetMFURLFromURI(sr->compositor, &(st->txurl), & ((SVGimageElement*)node)->xlink->href);
326
gf_node_set_private(node, st);
327
gf_node_set_render_function(node, SVG_Render_bitmap);
328
gf_node_set_predestroy_function(node, SVG_Destroy_image);
332
/*********************/
333
/* SVG video element */
334
/*********************/
336
static void SVG_Activate_video(SVG_video_stack *stack, SVGvideoElement *video)
338
MFURL *txurl = &(((SVG_video_stack *)gf_node_get_private((GF_Node *)video))->txurl);
340
if (!stack->txh.is_open) {
341
gf_sr_texture_play(&stack->txh, txurl);
343
gf_mo_set_speed(stack->txh.stream, FIX_ONE);
347
static void SVG_Deactivate_video(SVG_video_stack *stack, SVGvideoElement *video)
350
stack->time_handle.needs_unregister = 1;
352
if (stack->txh.is_open) {
353
gf_sr_texture_stop(&stack->txh);
358
static void SVG_Update_video(GF_TextureHandler *txh)
360
//SVGvideoElement *txnode = (SVGvideoElement *) txh->owner;
361
SVG_video_stack *st = (SVG_video_stack *) gf_node_get_private(txh->owner);
362
//MFURL *txurl = &(st->txurl);
364
/*setup texture if needed*/
365
if (!txh->is_open) return;
366
if (!st->isActive && st->first_frame_fetched) return;
368
/*when fetching the first frame disable resync*/
369
gf_sr_texture_update_frame(txh, 0);
371
if (txh->stream_finished) {
373
if (MT_GetLoop(st, txnode)) {
374
gf_sr_texture_restart(txh);
376
//if active deactivate
377
else if (txnode->isActive && gf_mo_should_deactivate(st->txh.stream) ) {
378
MT_Deactivate(st, txnode);
382
/* first frame is fetched */
383
if (!st->first_frame_fetched && (txh->needs_refresh) ) {
384
st->first_frame_fetched = 1;
386
txnode->duration_changed = gf_mo_get_duration(txh->stream);
387
gf_node_event_out_str(txh->owner, "duration_changed");
389
/*stop stream if needed
390
if (!txnode->isActive && txh->is_open) {
391
gf_sr_texture_stop(txh);
392
//make sure the refresh flag is not cleared
393
txh->needs_refresh = 1;
397
/*we have no choice but retraversing the graph until we're inactive since the movie framerate and
398
the renderer framerate are likely to be different */
399
if (!txh->stream_finished)
400
gf_sr_invalidate(txh->compositor, NULL);
404
static void SVG_UpdateTime_video(GF_TimeNode *st)
407
SVGvideoElement *video = (SVGvideoElement *)st->obj;
408
SVG_video_stack *stack = (SVG_video_stack *)gf_node_get_private(st->obj);
409
MFURL *txurl = &(stack->txurl);
411
/*not active, store start time and speed */
412
if (!stack->isActive) {
413
//stack->start_time = mt->startTime;
414
stack->start_time = 0;
417
sceneTime = gf_node_get_scene_time(st->obj);
419
if (sceneTime < stack->start_time //||
420
/*special case if we're getting active AFTER stoptime
421
(!mt->isActive && (mt->stopTime > stack->start_time) && (time>=mt->stopTime)) */
423
/*opens stream only at first access to fetch first frame*/
424
if (stack->fetch_first_frame) {
425
stack->fetch_first_frame = 0;
426
if (!stack->txh.is_open)
427
gf_sr_texture_play(&stack->txh, txurl);
433
if (MT_GetSpeed(stack, mt) && mt->isActive) {
434
// if stoptime is reached (>startTime) deactivate
435
if ((mt->stopTime > stack->start_time) && (time >= mt->stopTime) ) {
436
MT_Deactivate(stack, mt);
442
if (!stack->isActive) SVG_Activate_video(stack, video);
445
static void SVG_Destroy_video(GF_Node *node)
447
SVG_video_stack *st = (SVG_video_stack *)gf_node_get_private(node);
448
gf_sr_texture_destroy(&st->txh);
449
gf_sg_mfurl_del(st->txurl);
451
if (st->time_handle.is_registered) gf_sr_unregister_time_node(st->txh.compositor, &st->time_handle);
452
drawable_del(st->graph);
456
void SVG_Init_video(Render2D *sr, GF_Node *node)
459
GF_SAFEALLOC(st, sizeof(SVG_video_stack))
460
st->graph = drawable_new();
462
gf_sr_traversable_setup(st->graph, node, sr->compositor);
463
st->graph->Draw = SVG_Draw_bitmap;
464
st->graph->IsPointOver = SVG_PointOver_bitmap;
466
gf_sr_texture_setup(&st->txh, sr->compositor, node);
467
st->txh.update_texture_fcnt = SVG_Update_video;
469
st->time_handle.UpdateTimeNode = SVG_UpdateTime_video;
470
st->time_handle.obj = node;
471
st->fetch_first_frame = 1;
473
/* create an MFURL from the SVG iri */
474
SVG_SetMFURLFromURI(sr->compositor, &(st->txurl), & ((SVGvideoElement *)node)->xlink->href);
476
gf_sr_register_time_node(st->txh.compositor, &st->time_handle);
478
gf_node_set_private(node, st);
479
gf_node_set_render_function(node, SVG_Render_bitmap);
480
gf_node_set_predestroy_function(node, SVG_Destroy_video);
483
/*********************/
484
/* SVG audio element */
485
/*********************/
489
GF_TimeNode time_handle;
500
static void SVG_Activate_audio(SVG_audio_stack *st, SVGaudioElement *audio)
502
MFURL *aurl = &(((SVG_audio_stack *)gf_node_get_private((GF_Node*)audio))->aurl);
504
gf_sr_audio_open(&st->input, aurl);
506
gf_mo_set_speed(st->input.stream, FIX_ONE);
507
/*rerender all graph to get parent audio group*/
508
gf_sr_invalidate(st->input.compositor, NULL);
511
static void SVG_Deactivate_audio(SVG_audio_stack *st)
513
gf_sr_audio_stop(&st->input);
515
st->time_handle.needs_unregister = 1;
518
static void SVG_Render_audio(GF_Node *node, void *rs)
520
SVGPropertiesPointers backup_props;
521
RenderEffect2D *eff = (RenderEffect2D*)rs;
522
SVG_audio_stack *st = (SVG_audio_stack *)gf_node_get_private(node);
526
if (!st->dmo) st->dmo = getDanaeMediaOjbectFromUrl(st->comp->danae_session, st->aurl.vals[0].url, 2);
528
processDanaeAudio(st->dmo, (u32) (gf_node_get_scene_time(node)*1000));
529
st->comp->draw_next_frame = 1;
534
/*check end of stream*/
535
if (st->input.stream && st->input.stream_finished) {
536
if (gf_mo_get_loop(st->input.stream, 0)) {
537
gf_sr_audio_restart(&st->input);
538
} else if (st->is_active /*&& gf_mo_should_deactivate(st->input.stream)*/) {
539
SVG_Deactivate_audio(st);
543
gf_sr_audio_register(&st->input, (GF_BaseEffect*)rs);
546
/*for heritage and anims*/
547
SVG_Render_base(node, (RenderEffect2D *)rs, &backup_props);
550
st->input.is_muted = 0;
551
if ((eff->trav_flags & GF_SR_TRAV_SWITCHED_OFF)
552
|| (*(eff->svg_props->display) == SVG_DISPLAY_NONE)
553
|| (*(eff->svg_props->visibility) == SVG_VISIBILITY_HIDDEN) ) {
555
st->input.is_muted = 1;
558
memcpy(eff->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
563
static void SVG_UpdateTime_audio(GF_TimeNode *tn)
566
SVGaudioElement *audio = (SVGaudioElement *)tn->obj;
567
SVG_audio_stack *st = (SVG_audio_stack *)gf_node_get_private(tn->obj);
569
if (! st->is_active) {
570
//st->start_time = as->startTime;
572
//st->input.speed = as->speed;
574
sceneTime = gf_node_get_scene_time(tn->obj);
575
//if ((sceneTime<st->start_time) || (st->start_time<0)) return;
578
if (st->input.input_ifce.GetSpeed(st->input.input_ifce.callback) && st->is_active) {
579
if ( (as->stopTime > st->start_time) && (time>=as->stopTime)) {
580
SVG_Deactivate_audio(st, audio);
585
if (!st->is_active) SVG_Activate_audio(st, audio);
588
static void SVG_Destroy_audio(GF_Node *node)
590
SVG_audio_stack *st = (SVG_audio_stack *)gf_node_get_private(node);
591
gf_sr_audio_stop(&st->input);
592
gf_sr_audio_unregister(&st->input);
593
if (st->time_handle.is_registered) {
594
gf_sr_unregister_time_node(st->input.compositor, &st->time_handle);
596
gf_sg_mfurl_del(st->aurl);
598
if (st->dmo) releaseDanaeMediaObject(st->dmo);
603
void SVG_Init_audio(Render2D *sr, GF_Node *node)
606
GF_SAFEALLOC(st, sizeof(SVG_audio_stack))
608
gf_sr_audio_setup(&st->input, sr->compositor, node);
610
st->time_handle.UpdateTimeNode = SVG_UpdateTime_audio;
611
st->time_handle.obj = node;
613
/* creates an MFURL from the URI of the SVG element */
614
SVG_SetMFURLFromURI(sr->compositor, &(st->aurl), & ((SVGaudioElement *)node)->xlink->href);
616
gf_node_set_private(node, st);
617
gf_node_set_render_function(node, SVG_Render_audio);
618
gf_node_set_predestroy_function(node, SVG_Destroy_audio);
620
gf_sr_register_time_node(sr->compositor, &st->time_handle);
622
st->comp = sr->compositor;
626
#endif //GPAC_DISABLE_SVG