LCOV - code coverage report
Current view: top level - compositor - svg_paint_servers.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 188 404 46.5 %
Date: 2021-04-29 23:48:07 Functions: 16 20 80.0 %

          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, &center);
     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             : 

Generated by: LCOV version 1.13