Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Cyril Concolato - Jean le Feuvre
5 : * Copyright (c) Telecom ParisTech 2005-2012
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 : #include "visual_manager.h"
27 :
28 : #ifndef GPAC_DISABLE_SVG
29 : #include "nodes_stacks.h"
30 : #include "texturing.h"
31 :
32 :
33 : enum
34 : {
35 : GF_SR_TEXTURE_GRAD_REGISTERED = 1<<6,
36 : GF_SR_TEXTURE_GRAD_NO_RGB = 1<<7,
37 : };
38 :
39 : typedef struct
40 : {
41 : GF_TextureHandler txh;
42 : Bool linear;
43 : Bool animated;
44 : Fixed *keys;
45 : u32 *cols;
46 : u32 current_frame;
47 : } SVG_GradientStack;
48 :
49 :
50 4 : static void SVG_DestroyPaintServer(GF_Node *node)
51 : {
52 4 : SVG_GradientStack *st = (SVG_GradientStack *) gf_node_get_private(node);
53 4 : if (st) {
54 4 : if (st->cols) gf_free(st->cols);
55 4 : if (st->keys) gf_free(st->keys);
56 4 : gf_sc_texture_destroy(&st->txh);
57 4 : gf_free(st);
58 : }
59 4 : }
60 :
61 :
62 810 : static GF_Node *svg_copy_gradient_attributes_from(GF_Node *node, SVGAllAttributes *all_atts)
63 : {
64 : GF_Node *href_node;
65 : SVGAllAttributes all_href_atts;
66 : GF_FieldInfo info;
67 :
68 : /*check gradient redirection ...*/
69 : href_node = node;
70 1620 : while (href_node && gf_node_get_attribute_by_tag(href_node, TAG_XLINK_ATT_href, GF_FALSE, GF_FALSE, &info) == GF_OK) {
71 300 : XMLRI*iri = (XMLRI*)info.far_ptr;
72 :
73 300 : if (iri->type != XMLRI_ELEMENTID) {
74 300 : GF_SceneGraph *sg = gf_node_get_graph(node);
75 300 : GF_Node *n = gf_sg_find_node_by_name(sg, &(iri->string[1]));
76 300 : if (n) {
77 0 : iri->type = XMLRI_ELEMENTID;
78 0 : iri->target = n;
79 0 : gf_node_register_iri(sg, iri);
80 0 : gf_free(iri->string);
81 0 : iri->string = NULL;
82 : } else {
83 : break;
84 : }
85 : }
86 0 : href_node = (GF_Node*)((XMLRI*)info.far_ptr)->target;
87 0 : if (href_node == node) href_node = NULL;
88 : }
89 810 : if (href_node == node) href_node = NULL;
90 :
91 0 : if (href_node) {
92 0 : gf_svg_flatten_attributes((SVG_Element *)href_node, &all_href_atts);
93 0 : if (!all_atts->gradientUnits) all_atts->gradientUnits = all_href_atts.gradientUnits;
94 0 : if (!all_atts->gradientTransform) all_atts->gradientTransform = all_href_atts.gradientTransform;
95 0 : if (!all_atts->cx) all_atts->cx = all_href_atts.cx;
96 0 : if (!all_atts->cy) all_atts->cy = all_href_atts.cy;
97 0 : if (!all_atts->r) all_atts->r = all_href_atts.r;
98 0 : if (!all_atts->fx) all_atts->fx = all_href_atts.fx;
99 0 : if (!all_atts->fy) all_atts->fy = all_href_atts.fy;
100 0 : if (!all_atts->spreadMethod) all_atts->spreadMethod = all_href_atts.spreadMethod;
101 0 : if (!all_atts->x1) all_atts->x1 = all_href_atts.x1;
102 0 : if (!all_atts->x2) all_atts->x2 = all_href_atts.x2;
103 0 : if (!all_atts->y1) all_atts->y1 = all_href_atts.y1;
104 0 : if (!all_atts->y2) all_atts->y2 = all_href_atts.y2;
105 : }
106 :
107 810 : return href_node;
108 : }
109 :
110 600 : static void svg_gradient_traverse(GF_Node *node, GF_TraverseState *tr_state, Bool real_traverse)
111 : {
112 : u32 count, nb_col;
113 : Bool is_dirty, all_dirty;
114 : Fixed alpha, max_offset;
115 : SVGAllAttributes all_atts;
116 : SVGPropertiesPointers backup_props_1;
117 : u32 backup_flags_1;
118 : GF_Node *href_node;
119 : GF_ChildNodeItem *children;
120 600 : SVG_GradientStack *st = (SVG_GradientStack *) gf_node_get_private(node);
121 :
122 600 : gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
123 600 : href_node = svg_copy_gradient_attributes_from(node, &all_atts);
124 600 : compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props_1, &backup_flags_1);
125 :
126 1050 : if (real_traverse &&
127 450 : ! (tr_state->svg_flags & (GF_SG_SVG_STOPCOLOR_OR_OPACITY_DIRTY|GF_SG_SVG_COLOR_DIRTY))
128 450 : && !gf_node_dirty_get(node)
129 447 : && !st->txh.needs_refresh)
130 : {
131 444 : memcpy(tr_state->svg_props, &backup_props_1, sizeof(SVGPropertiesPointers));
132 444 : tr_state->svg_flags = backup_flags_1;
133 444 : return;
134 : }
135 :
136 : /*for gradients we must traverse the gradient stops to trigger animations, even if the
137 : gradient is not marked as dirty*/
138 156 : all_dirty = tr_state->svg_flags & (GF_SG_SVG_STOPCOLOR_OR_OPACITY_DIRTY|GF_SG_SVG_COLOR_DIRTY);
139 : is_dirty = GF_FALSE;
140 156 : if (gf_node_dirty_get(node)) {
141 : is_dirty = all_dirty = GF_TRUE;
142 4 : gf_node_dirty_clear(node, 0);
143 4 : if (st->cols) gf_free(st->cols);
144 4 : st->cols = NULL;
145 4 : if (st->keys) gf_free(st->keys);
146 4 : st->keys = NULL;
147 :
148 4 : st->animated = gf_node_animation_count(node) ? GF_TRUE : GF_FALSE;
149 : }
150 :
151 156 : children = ((SVG_Element *)node)->children;
152 156 : if (!children && href_node) {
153 0 : children = ((SVG_Element *)href_node)->children;
154 : }
155 :
156 156 : if (!st->cols) {
157 4 : count = gf_node_list_get_count(children);
158 4 : st->cols = (u32*)gf_malloc(sizeof(u32)*count);
159 4 : st->keys = (Fixed*)gf_malloc(sizeof(Fixed)*count);
160 : }
161 : nb_col = 0;
162 : max_offset = 0;
163 612 : while (children) {
164 : SVGPropertiesPointers backup_props_2;
165 : u32 backup_flags_2;
166 : Fixed key;
167 456 : GF_Node *stop = children->node;
168 456 : children = children->next;
169 456 : if (gf_node_get_tag((GF_Node *)stop) != TAG_SVG_stop) continue;
170 :
171 456 : gf_svg_flatten_attributes((SVG_Element*)stop, &all_atts);
172 456 : compositor_svg_traverse_base(stop, &all_atts, tr_state, &backup_props_2, &backup_flags_2);
173 :
174 456 : if (gf_node_animation_count(stop))
175 0 : st->animated = GF_TRUE;
176 :
177 456 : if (all_dirty || gf_node_dirty_get(stop)) {
178 : is_dirty = GF_TRUE;
179 6 : gf_node_dirty_clear(stop, 0);
180 :
181 : alpha = FIX_ONE;
182 6 : if (tr_state->svg_props->stop_opacity && (tr_state->svg_props->stop_opacity->type==SVG_NUMBER_VALUE) )
183 6 : alpha = tr_state->svg_props->stop_opacity->value;
184 :
185 6 : if (tr_state->svg_props->stop_color) {
186 6 : if (tr_state->svg_props->stop_color->color.type == SVG_COLOR_CURRENTCOLOR) {
187 0 : st->cols[nb_col] = GF_COL_ARGB_FIXED(alpha, tr_state->svg_props->color->color.red, tr_state->svg_props->color->color.green, tr_state->svg_props->color->color.blue);
188 : } else {
189 6 : st->cols[nb_col] = GF_COL_ARGB_FIXED(alpha, tr_state->svg_props->stop_color->color.red, tr_state->svg_props->stop_color->color.green, tr_state->svg_props->stop_color->color.blue);
190 : }
191 : } else {
192 0 : st->cols[nb_col] = GF_COL_ARGB_FIXED(alpha, 0, 0, 0);
193 : }
194 :
195 6 : if (all_atts.offset) {
196 6 : key = all_atts.offset->value;
197 6 : if (all_atts.offset->type==SVG_NUMBER_PERCENTAGE) key/=100;
198 : } else {
199 : key=0;
200 : }
201 6 : if (key>max_offset) max_offset=key;
202 : else key = max_offset;
203 6 : st->keys[nb_col] = key;
204 : } else {
205 450 : if (st->keys[nb_col]>max_offset) max_offset = st->keys[nb_col];
206 : }
207 :
208 456 : nb_col++;
209 :
210 456 : memcpy(tr_state->svg_props, &backup_props_2, sizeof(SVGPropertiesPointers));
211 456 : tr_state->svg_flags = backup_flags_2;
212 : }
213 :
214 156 : if (is_dirty) {
215 : u32 i;
216 : GF_EVGStencil * stencil;
217 :
218 4 : if (!st->txh.tx_io) gf_sc_texture_allocate(&st->txh);
219 4 : stencil = gf_sc_texture_get_stencil(&st->txh);
220 4 : if (!stencil) stencil = gf_evg_stencil_new(st->linear ? GF_STENCIL_LINEAR_GRADIENT : GF_STENCIL_RADIAL_GRADIENT);
221 : /*set stencil even if assigned, this invalidates the associated bitmap state in 3D*/
222 4 : gf_sc_texture_set_stencil(&st->txh, stencil);
223 :
224 4 : st->txh.transparent = GF_FALSE;
225 10 : for (i=0; i<nb_col; i++) {
226 6 : if (GF_COL_A(st->cols[i]) != 0xFF) {
227 0 : st->txh.transparent = GF_TRUE;
228 0 : break;
229 : }
230 : }
231 :
232 4 : gf_evg_stencil_set_gradient_interpolation(stencil, st->keys, st->cols, nb_col);
233 4 : gf_evg_stencil_set_gradient_mode(stencil, /*lg->spreadMethod*/ GF_GRADIENT_MODE_PAD);
234 :
235 4 : st->txh.needs_refresh = GF_TRUE;
236 : } else {
237 152 : st->txh.needs_refresh = GF_FALSE;
238 : }
239 :
240 156 : memcpy(tr_state->svg_props, &backup_props_1, sizeof(SVGPropertiesPointers));
241 156 : tr_state->svg_flags = backup_flags_1;
242 : }
243 :
244 :
245 420 : static void svg_update_gradient(SVG_GradientStack *st, GF_ChildNodeItem *children, Bool linear)
246 : {
247 : SVGPropertiesPointers *svgp;
248 420 : GF_Node *node = st->txh.owner;
249 420 : GF_TraverseState *tr_state = st->txh.compositor->traverse_state;
250 :
251 420 : if (!gf_node_dirty_get(node)) {
252 419 : if (st->current_frame==st->txh.compositor->current_frame) return;
253 149 : st->current_frame = st->txh.compositor->current_frame;
254 149 : st->txh.needs_refresh = GF_FALSE;
255 : // if (!st->animated) return;
256 : }
257 :
258 150 : if (!tr_state->svg_props) {
259 0 : GF_SAFEALLOC(svgp, SVGPropertiesPointers);
260 0 : if (svgp) {
261 0 : gf_svg_properties_init_pointers(svgp);
262 0 : tr_state->svg_props = svgp;
263 :
264 0 : svg_gradient_traverse(node, tr_state, GF_FALSE);
265 :
266 0 : gf_svg_properties_reset_pointers(svgp);
267 0 : gf_free(svgp);
268 : }
269 0 : tr_state->svg_props = NULL;
270 : } else {
271 150 : svg_gradient_traverse(node, tr_state, GF_FALSE);
272 : }
273 : }
274 :
275 :
276 456 : static void svg_traverse_gradient(GF_Node *node, void *rs, Bool is_destroy)
277 : {
278 : GF_TraverseState *tr_state = (GF_TraverseState *) rs;
279 :
280 456 : if (is_destroy) {
281 4 : SVG_DestroyPaintServer(node);
282 4 : return;
283 : }
284 452 : if (tr_state->traversing_mode != TRAVERSE_SORT) return;
285 450 : svg_gradient_traverse(node, tr_state, GF_TRUE);
286 : }
287 :
288 : #define GRAD_TEXTURE_SIZE 128
289 : #define GRAD_TEXTURE_HSIZE 64
290 :
291 0 : static GF_Rect compositor_svg_get_gradient_bounds(GF_TextureHandler *txh, SVGAllAttributes *all_atts)
292 : {
293 : GF_Rect rc;
294 0 : if (gf_node_get_tag(txh->owner)==TAG_SVG_radialGradient) {
295 : rc.x = rc.y = rc.width = FIX_ONE/2;
296 0 : if (all_atts->r) {
297 0 : rc.width = 2*all_atts->r->value;
298 0 : if (all_atts->r->type==SVG_NUMBER_PERCENTAGE) rc.width /= 100;
299 : }
300 0 : if (all_atts->cx) {
301 0 : rc.x = all_atts->cx->value;
302 0 : if (all_atts->cx->type==SVG_NUMBER_PERCENTAGE) rc.x /= 100;
303 : }
304 0 : if (all_atts->cy) {
305 0 : rc.y = all_atts->cy->value;
306 0 : if (all_atts->cy->type==SVG_NUMBER_PERCENTAGE) rc.y /= 100;
307 : }
308 : rc.height = rc.width;
309 0 : rc.x -= rc.width/2;
310 0 : rc.y -= rc.height/2;
311 : } else {
312 : rc.x = rc.y = rc.height = 0;
313 : rc.width = FIX_ONE;
314 0 : if (all_atts->x1) {
315 0 : rc.x = all_atts->x1->value;
316 0 : if (all_atts->x1->type==SVG_NUMBER_PERCENTAGE) rc.x /= 100;
317 : }
318 0 : if (all_atts->y1) {
319 0 : rc.y = all_atts->y1->value;
320 0 : if (all_atts->y1->type==SVG_NUMBER_PERCENTAGE) rc.y /= 100;
321 : }
322 0 : if (all_atts->x2) {
323 0 : rc.width = all_atts->x2->value;
324 0 : if (all_atts->x2->type==SVG_NUMBER_PERCENTAGE) rc.width/= 100;
325 : }
326 0 : rc.width -= rc.x;
327 :
328 0 : if (all_atts->y2) {
329 0 : rc.height = all_atts->y2->value;
330 0 : if (all_atts->y2->type==SVG_NUMBER_PERCENTAGE) rc.height /= 100;
331 : }
332 0 : rc.height -= rc.y;
333 :
334 0 : if (!rc.width) rc.width = rc.height;
335 0 : if (!rc.height) rc.height = rc.width;
336 : }
337 0 : rc.y += rc.height;
338 0 : return rc;
339 : }
340 :
341 0 : void compositor_svg_build_gradient_texture(GF_TextureHandler *txh)
342 : {
343 : u32 i;
344 : Fixed size;
345 : GF_Matrix2D mat;
346 : GF_EVGStencil * stencil;
347 : GF_EVGSurface *surface;
348 : GF_EVGStencil * texture2D;
349 : GF_Path *path;
350 : GF_Err e;
351 : Bool transparent;
352 : SVGAllAttributes all_atts;
353 0 : SVG_GradientStack *st = (SVG_GradientStack *) gf_node_get_private(txh->owner);
354 :
355 0 : if (!txh->tx_io) return;
356 :
357 0 : if (!(txh->flags & GF_SR_TEXTURE_GRAD_REGISTERED)) {
358 0 : txh->flags |= GF_SR_TEXTURE_GRAD_REGISTERED;
359 0 : if (gf_list_find(txh->compositor->textures, txh)<0)
360 0 : gf_list_insert(txh->compositor->textures, txh, 0);
361 : }
362 :
363 0 : if (txh->data) {
364 0 : gf_free(txh->data);
365 0 : txh->data = NULL;
366 : }
367 0 : stencil = gf_sc_texture_get_stencil(txh);
368 0 : if (!stencil) return;
369 :
370 : /*init our 2D graphics stuff*/
371 0 : texture2D = gf_evg_stencil_new(GF_STENCIL_TEXTURE);
372 0 : if (!texture2D) return;
373 0 : surface = gf_evg_surface_new(GF_TRUE);
374 0 : if (!surface) {
375 0 : gf_evg_stencil_delete(texture2D);
376 0 : return;
377 : }
378 :
379 0 : transparent = st->txh.transparent;
380 0 : if (st->txh.flags & GF_SR_TEXTURE_GRAD_NO_RGB) transparent = GF_TRUE;
381 :
382 0 : if (transparent) {
383 0 : if (!txh->data) {
384 0 : txh->data = (char *) gf_malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*4);
385 : } else {
386 0 : memset(txh->data, 0, sizeof(char)*txh->stride*txh->height);
387 : }
388 0 : e = gf_evg_stencil_set_texture(texture2D, txh->data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 4*GRAD_TEXTURE_SIZE, GF_PIXEL_ARGB);
389 : } else {
390 0 : if (!txh->data) {
391 0 : txh->data = (char *) gf_malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*3);
392 : }
393 0 : e = gf_evg_stencil_set_texture(texture2D, txh->data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 3*GRAD_TEXTURE_SIZE, GF_PIXEL_RGB);
394 : /*try with ARGB (it actually is needed for GDIplus module since GDIplus cannot handle native RGB texture (it works in BGR)*/
395 0 : if (e) {
396 : /*remember for later use*/
397 0 : st->txh.flags |= GF_SR_TEXTURE_GRAD_NO_RGB;
398 : transparent = GF_TRUE;
399 0 : gf_free(txh->data);
400 0 : txh->data = (char *) gf_malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*4);
401 0 : e = gf_evg_stencil_set_texture(texture2D, txh->data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 4*GRAD_TEXTURE_SIZE, GF_PIXEL_ARGB);
402 : }
403 : }
404 :
405 0 : if (e) {
406 0 : gf_free(txh->data);
407 0 : txh->data = NULL;
408 0 : gf_evg_stencil_delete(texture2D);
409 0 : gf_evg_surface_delete(surface);
410 0 : return;
411 : }
412 0 : e = gf_evg_surface_attach_to_texture(surface, texture2D);
413 0 : if (e) {
414 0 : gf_evg_stencil_delete(texture2D);
415 0 : gf_evg_surface_delete(surface);
416 0 : return;
417 : }
418 :
419 : size = INT2FIX(GRAD_TEXTURE_HSIZE);
420 : /*fill surface*/
421 0 : path = gf_path_new();
422 0 : gf_path_add_move_to(path, -size, -size);
423 0 : gf_path_add_line_to(path, size, -size);
424 0 : gf_path_add_line_to(path, size, size);
425 0 : gf_path_add_line_to(path, -size, size);
426 0 : gf_path_close(path);
427 :
428 0 : gf_mx2d_init(mat);
429 0 : txh->compute_gradient_matrix(txh, NULL, &mat, GF_FALSE);
430 :
431 0 : gf_svg_flatten_attributes((SVG_Element*)txh->owner, &all_atts);
432 :
433 0 : if (all_atts.gradientUnits && (*(SVG_GradientUnit*)all_atts.gradientUnits==SVG_GRADIENTUNITS_OBJECT) ) {
434 0 : if (all_atts.gradientTransform)
435 0 : gf_mx2d_copy(mat, all_atts.gradientTransform->mat);
436 :
437 0 : gf_mx2d_add_scale(&mat, 2*size, 2*size);
438 0 : gf_mx2d_add_translation(&mat, -size, -size);
439 : } else {
440 0 : GF_Rect rc = compositor_svg_get_gradient_bounds(txh, &all_atts);
441 : /*recenter the gradient to use full texture*/
442 0 : gf_mx2d_add_translation(&mat, -rc.x, rc.height-rc.y);
443 0 : gf_mx2d_add_scale(&mat, gf_divfix(2*size, rc.width), gf_divfix(2*size , rc.height));
444 0 : gf_mx2d_add_translation(&mat, -size, -size);
445 : }
446 :
447 0 : gf_evg_stencil_set_matrix(stencil, &mat);
448 0 : gf_evg_surface_set_raster_level(surface, GF_RASTER_HIGH_QUALITY);
449 0 : gf_evg_surface_set_path(surface, path);
450 0 : gf_evg_surface_fill(surface, stencil);
451 0 : gf_evg_surface_delete(surface);
452 0 : gf_evg_stencil_delete(texture2D);
453 0 : gf_path_del(path);
454 :
455 0 : txh->width = GRAD_TEXTURE_SIZE;
456 0 : txh->height = GRAD_TEXTURE_SIZE;
457 0 : txh->transparent = transparent;
458 0 : txh->flags |= GF_SR_TEXTURE_NO_GL_FLIP;
459 :
460 0 : if (transparent) {
461 : u32 j;
462 0 : txh->stride = GRAD_TEXTURE_SIZE*4;
463 :
464 : /*back to RGBA texturing*/
465 0 : txh->pixelformat = GF_PIXEL_RGBA;
466 0 : for (i=0; i<txh->height; i++) {
467 0 : char *data = txh->data + i*txh->stride;
468 0 : for (j=0; j<txh->width; j++) {
469 0 : u32 val = *(u32 *) &data[4*j];
470 0 : data[4*j] = (val>>16) & 0xFF;
471 0 : data[4*j+1] = (val>>8) & 0xFF;
472 0 : data[4*j+2] = (val) & 0xFF;
473 0 : data[4*j+3] = (val>>24) & 0xFF;
474 : }
475 : }
476 : } else {
477 0 : txh->stride = GRAD_TEXTURE_SIZE*3;
478 0 : txh->pixelformat = GF_PIXEL_RGB;
479 : }
480 0 : gf_sc_texture_set_data(txh);
481 0 : return;
482 : }
483 :
484 :
485 : /* linear gradient */
486 :
487 210 : static void SVG_UpdateLinearGradient(GF_TextureHandler *txh)
488 : {
489 210 : SVG_Element *lg = (SVG_Element *) txh->owner;
490 210 : SVG_GradientStack *st = (SVG_GradientStack *) gf_node_get_private(txh->owner);
491 :
492 210 : svg_update_gradient(st, lg->children, GF_TRUE);
493 210 : }
494 :
495 :
496 210 : static void SVG_LG_ComputeMatrix(GF_TextureHandler *txh, GF_Rect *bounds, GF_Matrix2D *mat, Bool for_3d)
497 : {
498 : GF_EVGStencil * stencil;
499 : SFVec2f start, end;
500 : SVGAllAttributes all_atts;
501 210 : SVG_Element *lg = (SVG_Element *) txh->owner;
502 210 : SVG_GradientStack *st = (SVG_GradientStack *) gf_node_get_private(txh->owner);
503 :
504 210 : if (!txh->tx_io) return;
505 210 : stencil = gf_sc_texture_get_stencil(txh);
506 210 : if (!stencil) return;
507 :
508 210 : svg_update_gradient(st, lg->children, GF_TRUE);
509 :
510 210 : gf_svg_flatten_attributes((SVG_Element*)txh->owner, &all_atts);
511 :
512 : /*get "transfered" attributed from xlink:href if any*/
513 210 : svg_copy_gradient_attributes_from(txh->owner, &all_atts);
514 :
515 420 : gf_mx2d_init(*mat);
516 :
517 : /*gradient is a texture, only update the bounds*/
518 210 : if (for_3d) {
519 : GF_Rect rc;
520 0 : if (!all_atts.gradientUnits || (*(SVG_GradientUnit*)all_atts.gradientUnits==SVG_GRADIENTUNITS_OBJECT))
521 : return;
522 :
523 : /*get gradient bounds in local coord system*/
524 0 : rc = compositor_svg_get_gradient_bounds(txh, &all_atts);
525 0 : gf_mx2d_add_scale(mat, gf_divfix(rc.width, bounds->width), gf_divfix(rc.height, bounds->height));
526 :
527 0 : return;
528 : }
529 :
530 210 : if (all_atts.gradientTransform)
531 0 : gf_mx2d_copy(*mat, all_atts.gradientTransform->mat );
532 :
533 210 : if (all_atts.x1) {
534 210 : start.x = all_atts.x1->value;
535 210 : if (all_atts.x1->type==SVG_NUMBER_PERCENTAGE) start.x /= 100;
536 : } else {
537 : start.x = 0;
538 : }
539 210 : if (all_atts.y1) {
540 210 : start.y = all_atts.y1->value;
541 210 : if (all_atts.y1->type==SVG_NUMBER_PERCENTAGE) start.y /= 100;
542 : } else {
543 : start.y = 0;
544 : }
545 210 : if (all_atts.x2) {
546 210 : end.x = all_atts.x2->value;
547 210 : if (all_atts.x2->type==SVG_NUMBER_PERCENTAGE) end.x /= 100;
548 : } else {
549 : end.x = FIX_ONE;
550 : }
551 210 : if (all_atts.y2) {
552 210 : end.y = all_atts.y2->value;
553 210 : if (all_atts.y2->type==SVG_NUMBER_PERCENTAGE) end.y /= 100;
554 : } else {
555 : end.y = 0;
556 : }
557 :
558 210 : gf_evg_stencil_set_gradient_mode(stencil, (GF_GradientMode) all_atts.spreadMethod ? *(SVG_SpreadMethod*)all_atts.spreadMethod : 0);
559 :
560 :
561 210 : if (bounds && (!all_atts.gradientUnits || (*(SVG_GradientUnit*)all_atts.gradientUnits==SVG_GRADIENTUNITS_OBJECT)) ) {
562 : /*move to local coord system - cf SVG spec*/
563 210 : gf_mx2d_add_scale(mat, bounds->width, bounds->height);
564 210 : gf_mx2d_add_translation(mat, bounds->x, bounds->y - bounds->height);
565 : }
566 210 : gf_evg_stencil_set_linear_gradient(stencil, start.x, start.y, end.x, end.y);
567 : }
568 :
569 3 : void compositor_init_svg_linearGradient(GF_Compositor *compositor, GF_Node *node)
570 : {
571 : SVG_GradientStack *st;
572 3 : GF_SAFEALLOC(st, SVG_GradientStack);
573 3 : if (!st) {
574 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate svg gradient stack\n"));
575 : return;
576 : }
577 :
578 : /*!!! Gradients are textures but are not registered as textures with the compositor in order to avoid updating
579 : too many textures each frame - gradients are only registered with the compositor when they are used in OpenGL, in order
580 : to release associated HW resource when no longer used*/
581 3 : st->txh.owner = node;
582 3 : st->txh.compositor = compositor;
583 3 : st->txh.update_texture_fcnt = SVG_UpdateLinearGradient;
584 3 : st->txh.flags = GF_SR_TEXTURE_SVG;
585 :
586 3 : st->txh.compute_gradient_matrix = SVG_LG_ComputeMatrix;
587 3 : st->linear = GF_TRUE;
588 3 : gf_node_set_private(node, st);
589 3 : gf_node_set_callback_function(node, svg_traverse_gradient);
590 : }
591 :
592 : /* radial gradient */
593 :
594 0 : static void SVG_UpdateRadialGradient(GF_TextureHandler *txh)
595 : {
596 0 : SVG_Element *rg = (SVG_Element *) txh->owner;
597 0 : SVG_GradientStack *st = (SVG_GradientStack *) gf_node_get_private(txh->owner);
598 :
599 0 : svg_update_gradient(st, rg->children, GF_FALSE);
600 0 : }
601 :
602 0 : static void SVG_RG_ComputeMatrix(GF_TextureHandler *txh, GF_Rect *bounds, GF_Matrix2D *mat, Bool for_3d)
603 : {
604 : GF_EVGStencil * stencil;
605 : SFVec2f center, focal;
606 : Fixed radius;
607 : SVGAllAttributes all_atts;
608 0 : SVG_Element *rg = (SVG_Element *) txh->owner;
609 0 : SVG_GradientStack *st = (SVG_GradientStack *) gf_node_get_private(txh->owner);
610 :
611 :
612 : /*create gradient brush if needed*/
613 0 : if (!txh->tx_io) return;
614 0 : stencil = gf_sc_texture_get_stencil(txh);
615 0 : if (!stencil) return;
616 :
617 0 : svg_update_gradient(st, rg->children, GF_FALSE);
618 :
619 0 : gf_svg_flatten_attributes((SVG_Element*)txh->owner, &all_atts);
620 :
621 : /*get transfered attributed from xlink:href if any*/
622 0 : svg_copy_gradient_attributes_from(txh->owner, &all_atts);
623 :
624 0 : gf_mx2d_init(*mat);
625 :
626 : /*gradient is a texture, only update the bounds*/
627 0 : if (for_3d && bounds) {
628 : GF_Rect rc;
629 0 : if (!all_atts.gradientUnits || (*(SVG_GradientUnit*)all_atts.gradientUnits==SVG_GRADIENTUNITS_OBJECT))
630 : return;
631 :
632 : /*get gradient bounds in local coord system*/
633 0 : rc = compositor_svg_get_gradient_bounds(txh, &all_atts);
634 0 : gf_mx2d_add_translation(mat, gf_divfix(rc.x-bounds->x, rc.width), gf_divfix(bounds->y - rc.y, rc.height) );
635 0 : gf_mx2d_add_scale(mat, gf_divfix(rc.width, bounds->width), gf_divfix(rc.height, bounds->height));
636 :
637 0 : gf_mx2d_inverse(mat);
638 0 : return;
639 : }
640 :
641 0 : if (all_atts.gradientTransform)
642 0 : gf_mx2d_copy(*mat, all_atts.gradientTransform->mat);
643 :
644 0 : if (all_atts.r) {
645 0 : radius = all_atts.r->value;
646 0 : if (all_atts.r->type==SVG_NUMBER_PERCENTAGE) radius /= 100;
647 : } else {
648 : radius = FIX_ONE/2;
649 : }
650 0 : if (all_atts.cx) {
651 0 : center.x = all_atts.cx->value;
652 0 : if (all_atts.cx->type==SVG_NUMBER_PERCENTAGE) center.x /= 100;
653 : } else {
654 0 : center.x = FIX_ONE/2;
655 : }
656 0 : if (all_atts.cy) {
657 0 : center.y = all_atts.cy->value;
658 0 : if (all_atts.cy->type==SVG_NUMBER_PERCENTAGE) center.y /= 100;
659 : } else {
660 0 : center.y = FIX_ONE/2;
661 : }
662 :
663 0 : gf_evg_stencil_set_gradient_mode(stencil, (GF_GradientMode) all_atts.spreadMethod ? *(SVG_SpreadMethod*)all_atts.spreadMethod : 0);
664 :
665 0 : if (all_atts.fx) {
666 0 : focal.x = all_atts.fx->value;
667 0 : if (all_atts.fx->type==SVG_NUMBER_PERCENTAGE) focal.x /= 100;
668 : } else {
669 0 : focal.x = center.x;
670 : }
671 0 : if (all_atts.fy) {
672 0 : focal.y = all_atts.fy->value;
673 0 : if (all_atts.fy->type==SVG_NUMBER_PERCENTAGE) focal.y /= 100;
674 : } else {
675 0 : focal.y = center.y;
676 : }
677 :
678 : /* clamping fx/fy according to:
679 : http://www.w3.org/TR/SVG11/pservers.html#RadialGradients
680 : If the point defined by fx and fy lies outside the circle defined by cx, cy and r,
681 : then the user agent shall set the focal point to the intersection of the line from (cx, cy)
682 : to (fx, fy) with the circle defined by cx, cy and r.*/
683 : {
684 0 : Fixed norm = gf_v2d_distance(&focal, ¢er);
685 0 : if (norm > radius) {
686 0 : Fixed xdelta = gf_muldiv(radius, focal.x-center.x, norm);
687 0 : Fixed ydelta = gf_muldiv(radius, focal.y-center.y, norm);
688 0 : focal.x = center.x + xdelta;
689 0 : focal.y = center.y + ydelta;
690 : }
691 : }
692 :
693 0 : if (bounds && (!all_atts.gradientUnits || (*(SVG_GradientUnit*)all_atts.gradientUnits==SVG_GRADIENTUNITS_OBJECT)) ) {
694 : /*move to local coord system - cf SVG spec*/
695 0 : gf_mx2d_add_scale(mat, bounds->width, bounds->height);
696 0 : gf_mx2d_add_translation(mat, bounds->x, bounds->y - bounds->height);
697 : }
698 0 : gf_evg_stencil_set_radial_gradient(stencil, center.x, center.y, focal.x, focal.y, radius, radius);
699 : }
700 :
701 1 : void compositor_init_svg_radialGradient(GF_Compositor *compositor, GF_Node *node)
702 : {
703 : SVG_GradientStack *st;
704 1 : GF_SAFEALLOC(st, SVG_GradientStack);
705 1 : if (!st) {
706 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate svg gradient stack\n"));
707 : return;
708 : }
709 :
710 : /*!!! Gradients are textures but are not registered as textures with the compositor in order to avoid updating
711 : too many textures each frame - gradients are only registered with the compositor when they are used in OpenGL, in order
712 : to release associated HW resource when no longer used*/
713 1 : st->txh.owner = node;
714 1 : st->txh.compositor = compositor;
715 1 : st->txh.flags = GF_SR_TEXTURE_SVG;
716 :
717 1 : st->txh.update_texture_fcnt = SVG_UpdateRadialGradient;
718 1 : st->txh.compute_gradient_matrix = SVG_RG_ComputeMatrix;
719 1 : gf_node_set_private(node, st);
720 1 : gf_node_set_callback_function(node, svg_traverse_gradient);
721 : }
722 :
723 :
724 164 : static void svg_traverse_PaintServer(GF_Node *node, void *rs, Bool is_destroy, Bool is_solid_color)
725 : {
726 : SVGPropertiesPointers backup_props;
727 : SVGAllAttributes all_atts;
728 : u32 backup_flags;
729 : u32 styling_size = sizeof(SVGPropertiesPointers);
730 : SVG_Element *elt = (SVG_Element *)node;
731 : GF_TraverseState *tr_state = (GF_TraverseState *) rs;
732 :
733 164 : if (is_destroy) {
734 7 : if (!is_solid_color)
735 0 : SVG_DestroyPaintServer(node);
736 7 : return;
737 : }
738 :
739 157 : gf_svg_flatten_attributes(elt, &all_atts);
740 157 : compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags);
741 :
742 157 : if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
743 : return;
744 : } else {
745 157 : compositor_svg_traverse_children(elt->children, tr_state);
746 : }
747 157 : memcpy(tr_state->svg_props, &backup_props, styling_size);
748 157 : tr_state->svg_flags = backup_flags;
749 : }
750 :
751 : typedef struct
752 : {
753 : u32 current_frame;
754 : Bool is_dirty;
755 : } GF_SolidColorStack;
756 :
757 6 : Bool compositor_svg_solid_color_dirty(GF_Compositor *compositor, GF_Node *node)
758 : {
759 6 : GF_SolidColorStack *st = (GF_SolidColorStack*)gf_node_get_private(node);
760 6 : if (st->current_frame==compositor->current_frame) return st->is_dirty;
761 3 : st->current_frame = compositor->current_frame;
762 3 : st->is_dirty = gf_node_dirty_get(node) ? GF_TRUE : GF_FALSE;
763 3 : gf_node_dirty_clear(node, 0);
764 3 : return st->is_dirty;
765 : }
766 :
767 9 : static void svg_traverse_solidColor(GF_Node *node, void *rs, Bool is_destroy)
768 : {
769 9 : if (is_destroy) {
770 3 : GF_SolidColorStack *st = (GF_SolidColorStack*)gf_node_get_private(node);
771 3 : gf_free(st);
772 3 : return;
773 : }
774 6 : svg_traverse_PaintServer(node, rs, is_destroy, GF_TRUE);
775 : }
776 :
777 :
778 3 : void compositor_init_svg_solidColor(GF_Compositor *compositor, GF_Node *node)
779 : {
780 : GF_SolidColorStack *st;
781 3 : GF_SAFEALLOC(st, GF_SolidColorStack);
782 3 : if (!st) return;
783 3 : gf_node_set_private(node, st);
784 3 : gf_node_set_callback_function(node, svg_traverse_solidColor);
785 : }
786 :
787 158 : static void svg_traverse_stop(GF_Node *node, void *rs, Bool is_destroy)
788 : {
789 158 : svg_traverse_PaintServer(node, rs, is_destroy, GF_TRUE);
790 158 : }
791 :
792 7 : void compositor_init_svg_stop(GF_Compositor *compositor, GF_Node *node)
793 : {
794 7 : gf_node_set_callback_function(node, svg_traverse_stop);
795 7 : }
796 :
797 210 : GF_TextureHandler *compositor_svg_get_gradient_texture(GF_Node *node)
798 : {
799 210 : SVG_GradientStack *st = (SVG_GradientStack*) gf_node_get_private((GF_Node *)node);
800 210 : st->txh.update_texture_fcnt(&st->txh);
801 210 : return &st->txh;
802 : }
803 :
804 :
805 : #endif
806 :
807 :
|