This source file includes following definitions.
- apply_feComponentTransfer
- svg_filter_apply
- svg_draw_filter
- svg_traverse_filter
- compositor_init_svg_filter
#include "visual_manager.h"
#ifndef GPAC_DISABLE_SVG
#include "nodes_stacks.h"
#include "texturing.h"
typedef struct
{
GF_TextureHandler txh;
Drawable *drawable;
u8 *data;
u32 alloc_size;
} GF_FilterStack;
void apply_feComponentTransfer(GF_Node *node, GF_TextureHandler *source, GF_Rect *region)
{
GF_ChildNodeItem *l = ((GF_ParentNode*)node)->children;
while (l) {
SVG_Filter_TransferType type = SVG_FILTER_TRANSFER_IDENTITY;
u32 i, j;
GF_List *table = NULL;
Fixed slope = FIX_ONE;
Fixed intercept = 0;
Fixed amplitude = 1;
Fixed exponent = 1;
Fixed offset = 0;
char *ptr = NULL;
GF_DOMAttribute *att = ((SVG_Element *)l->node)->attributes;
while (att) {
switch (att->tag) {
case TAG_SVG_ATT_filter_transfer_type:
type = *(u8*)att->data;
break;
case TAG_SVG_ATT_filter_table_values:
table = *(GF_List **)att->data;
break;
case TAG_SVG_ATT_slope:
slope = ((SVG_Number *)att->data)->value;
break;
case TAG_SVG_ATT_filter_intercept:
intercept = ((SVG_Number *)att->data)->value;
break;
case TAG_SVG_ATT_filter_amplitude:
amplitude = ((SVG_Number *)att->data)->value;
break;
case TAG_SVG_ATT_filter_exponent:
exponent = ((SVG_Number *)att->data)->value;
break;
case TAG_SVG_ATT_offset:
offset = ((SVG_Number *)att->data)->value;
break;
}
att = att->next;
}
if (type==SVG_FILTER_TRANSFER_IDENTITY) {
l = l->next;
continue;
}
switch (gf_node_get_tag(l->node)) {
case TAG_SVG_feFuncR:
if (source->pixelformat==GF_PIXEL_RGBA) ptr = source->data;
else ptr = source->data+2;
break;
case TAG_SVG_feFuncG:
if (source->pixelformat==GF_PIXEL_RGBA) ptr = source->data+1;
else ptr = source->data+1;
break;
case TAG_SVG_feFuncB:
if (source->pixelformat==GF_PIXEL_RGBA) ptr = source->data+2;
else ptr = source->data;
break;
case TAG_SVG_feFuncA:
if (source->pixelformat==GF_PIXEL_RGBA) ptr = source->data+3;
else ptr = source->data+3;
break;
}
if ((type==SVG_FILTER_TRANSFER_LINEAR) && (intercept || (slope!=FIX_ONE)) ) {
intercept *= 255;
assert( ptr );
for (i=0; i<source->height; i++) {
for (j=0; j<source->width; j++) {
Fixed p = (*ptr) * slope + intercept;
ptr[0] = (u8) MIN(MAX(0, p), 255);
ptr += 4;
}
}
} else if (type==SVG_FILTER_TRANSFER_GAMMA) {
assert( ptr );
for (i=0; i<source->height; i++) {
for (j=0; j<source->width; j++) {
Fixed p = 255 * gf_mulfix(amplitude, FLT2FIX( pow( INT2FIX(*ptr)/255, FIX2FLT(exponent) ) ) ) + offset;
ptr[0] = (u8) MIN(MAX(0, p), 255);
ptr += 4;
}
}
} else if ((type==SVG_FILTER_TRANSFER_TABLE) && table && (gf_list_count(table)>=2) ) {
u32 count = gf_list_count(table);
u32 N = count-1;
assert( ptr );
for (i=0; i<source->height; i++) {
for (j=0; j<source->width; j++) {
SVG_Number *vk, *vk1;
Fixed p = INT2FIX(ptr[0]) / 255;
Fixed pN = p*N;
u32 k = FIX2INT(p*N);
if (k==N) k--;
vk = gf_list_get(table, k);
vk1 = gf_list_get(table, k+1);
p = 255 * ( vk->value + gf_mulfix( pN - INT2FIX(k), (vk1->value - vk->value)) );
ptr[0] = (u8) MIN(MAX(0, p), 255);
ptr += 4;
}
}
} else if ((type==SVG_FILTER_TRANSFER_DISCRETE) && table && gf_list_count(table) ) {
u32 count = gf_list_count(table);
assert( ptr );
for (i=0; i<source->height; i++) {
for (j=0; j<source->width; j++) {
SVG_Number *vk;
Fixed p = INT2FIX(ptr[0]) / 255;
Fixed pN = p*count;
u32 k = 0;
while (k<count) {
if ((s32)k+1>pN) break;
k++;
}
if (k) k--;
vk = gf_list_get(table, k);
p = 255 * vk->value;
ptr[0] = (u8) MIN(MAX(0, p), 255);
ptr += 4;
}
}
}
l = l->next;
}
}
void svg_filter_apply(GF_Node *node, GF_TextureHandler *source, GF_Rect *region)
{
GF_ChildNodeItem *l = ((GF_ParentNode*)node)->children;
while (l) {
switch (gf_node_get_tag(l->node)) {
case TAG_SVG_feComponentTransfer:
apply_feComponentTransfer(l->node, source, region);
break;
}
l = l->next;
}
}
void svg_draw_filter(GF_Node *filter, GF_Node *node, GF_TraverseState *tr_state)
{
GF_IRect rc1, rc2;
#ifndef GPAC_DISABLE_3D
u32 type_3d;
#endif
u32 prev_flags;
GF_IRect txrc;
Fixed scale_x, scale_y, temp_x, temp_y;
DrawableContext *ctx, *child_ctx;
GF_SURFACE offscreen_surface, old_surf;
GF_Rect bounds, local_bounds, rc;
GF_Matrix2D backup;
SVGAllAttributes all_atts;
GF_FilterStack *st = gf_node_get_private(filter);
assert(tr_state->traversing_mode==TRAVERSE_SORT);
gf_mx2d_copy(backup, tr_state->transform);
gf_mx2d_init(tr_state->transform);
gf_node_allow_cyclic_traverse(node);
tr_state->traversing_mode = TRAVERSE_GET_BOUNDS;
tr_state->bounds.width = tr_state->bounds.height = 0;
gf_node_traverse(node, tr_state);
local_bounds = bounds = tr_state->bounds;
gf_mx2d_apply_rect(&backup, &bounds);
txrc = gf_rect_pixelize(&bounds);
if (txrc.width%2) txrc.width++;
if (txrc.height%2) txrc.height++;
bounds = gf_rect_ft(&txrc);
tr_state->traversing_mode = TRAVERSE_SORT;
gf_mx2d_copy(tr_state->transform, backup);
if (!bounds.width || !bounds.height) {
return;
}
ctx = drawable_init_context_svg(st->drawable, tr_state);
if (!ctx) return;
st->txh.height = txrc.height;
st->txh.width = txrc.width;
st->txh.stride = txrc.width * 4;
st->txh.pixelformat = GF_PIXEL_ARGB;
#ifndef GPAC_DISABLE_3D
if (tr_state->visual->type_3d) st->txh.pixelformat = GF_PIXEL_RGBA;
#endif
st->txh.transparent = 1;
if (st->txh.stride * st->txh.height > st->alloc_size) {
st->alloc_size = st->txh.stride * st->txh.height;
st->data = (u8*)gf_realloc(st->data, sizeof(u8) * st->alloc_size);
}
memset(st->data, 0x0, sizeof(char) * st->txh.stride * st->txh.height);
st->txh.data = (char *) st->data;
gf_path_reset(st->drawable->path);
gf_path_add_rect_center(st->drawable->path,
bounds.x + bounds.width/2,
bounds.y - bounds.height/2,
bounds.width,
bounds.height);
old_surf = tr_state->visual->raster_surface;
offscreen_surface = tr_state->visual->compositor->rasterizer->surface_new(tr_state->visual->compositor->rasterizer, tr_state->visual->center_coords);
tr_state->visual->raster_surface = offscreen_surface;
gf_mx2d_copy(backup, tr_state->transform);
gf_mx2d_init(tr_state->transform);
tr_state->visual->compositor->rasterizer->surface_attach_to_buffer(offscreen_surface, st->txh.data,
st->txh.width,
st->txh.height,
0,
st->txh.stride,
st->txh.pixelformat);
prev_flags = tr_state->immediate_draw;
tr_state->immediate_draw = 1;
tr_state->traversing_mode = TRAVERSE_SORT;
tr_state->in_svg_filter = 1;
scale_x = gf_divfix(bounds.width, local_bounds.width);
scale_y = gf_divfix(bounds.height, local_bounds.height);
gf_mx2d_init(tr_state->transform);
gf_mx2d_add_scale(&tr_state->transform, scale_x, scale_y);
rc = local_bounds;
gf_mx2d_apply_rect(&tr_state->transform, &rc);
if (tr_state->visual->center_coords) {
temp_x = -rc.x - rc.width/2;
temp_y = rc.height/2 - rc.y;
} else {
temp_x = -rc.x;
temp_y = rc.height - rc.y;
}
gf_mx2d_add_translation(&tr_state->transform, temp_x, temp_y);
rc1 = tr_state->visual->surf_rect;
rc2 = tr_state->visual->top_clipper;
tr_state->visual->surf_rect.width = st->txh.width;
tr_state->visual->surf_rect.height = st->txh.height;
if (tr_state->visual->center_coords) {
tr_state->visual->surf_rect.y = st->txh.height/2;
tr_state->visual->surf_rect.x = -1 * (s32) st->txh.width/2;
} else {
tr_state->visual->surf_rect.y = st->txh.height;
tr_state->visual->surf_rect.x = 0;
}
tr_state->visual->top_clipper = tr_state->visual->surf_rect;
#ifndef GPAC_DISABLE_3D
type_3d = tr_state->visual->type_3d;
tr_state->visual->type_3d=0;
#endif
if (prev_flags) gf_node_allow_cyclic_traverse(node);
gf_node_traverse(node, tr_state);
child_ctx = ctx->next;
while (child_ctx && child_ctx->drawable) {
drawable_reset_bounds(child_ctx->drawable, tr_state->visual);
child_ctx->drawable = NULL;
child_ctx = child_ctx->next;
}
tr_state->visual->cur_context = ctx;
tr_state->in_svg_filter = 0;
tr_state->immediate_draw = prev_flags;
tr_state->visual->compositor->rasterizer->surface_delete(offscreen_surface);
tr_state->visual->raster_surface = old_surf;
tr_state->traversing_mode = TRAVERSE_SORT;
tr_state->visual->surf_rect = rc1;
tr_state->visual->top_clipper = rc2;
#ifndef GPAC_DISABLE_3D
tr_state->visual->type_3d = type_3d ;
#endif
st->txh.transparent = 1;
st->txh.flags |= GF_SR_TEXTURE_NO_GL_FLIP;
gf_sc_texture_set_data(&st->txh);
#ifndef GPAC_DISABLE_3D
if (tr_state->visual->type_3d)
gf_sc_texture_push_image(&st->txh, 0, 0);
else
#endif
gf_sc_texture_push_image(&st->txh, 0, 1);
ctx->flags |= CTX_NO_ANTIALIAS;
ctx->aspect.fill_color = 0;
ctx->aspect.line_color = 0xFF000000;
ctx->aspect.fill_texture = &st->txh;
ctx->flags |= CTX_TEXTURE_DIRTY;
bounds = local_bounds;
gf_svg_flatten_attributes((SVG_Element *)filter, &all_atts);
if (!all_atts.filterUnits || (*all_atts.filterUnits==SVG_GRADIENTUNITS_OBJECT)) {
Fixed v;
v = all_atts.x ? all_atts.x->value : INT2FIX(-10);
bounds.x += gf_mulfix(v, bounds.width);
v = all_atts.y ? all_atts.y->value : INT2FIX(-10);
bounds.y += gf_mulfix(v, bounds.height);
v = all_atts.width ? all_atts.width->value : INT2FIX(120);
bounds.width = gf_mulfix(v, bounds.width);
v = all_atts.height ? all_atts.height->value : INT2FIX(120);
bounds.height = gf_mulfix(v, bounds.height);
} else {
bounds.x = all_atts.x ? all_atts.x->value : 0;
bounds.y = all_atts.y ? all_atts.y->value : 0;
bounds.width = all_atts.width ? all_atts.width->value : bounds.width;
bounds.height = all_atts.height ? all_atts.height->value : 120;
}
gf_mx2d_apply_rect(&backup, &bounds);
svg_filter_apply(filter, &st->txh, &bounds);
#ifndef GPAC_DISABLE_3D
if (tr_state->visual->type_3d) {
if (!st->drawable->mesh) {
st->drawable->mesh = new_mesh();
mesh_from_path(st->drawable->mesh, st->drawable->path);
}
visual_3d_draw_from_context(tr_state->ctx, tr_state);
ctx->drawable = NULL;
return;
}
#endif
gf_mx2d_init(tr_state->transform);
drawable_finalize_sort(ctx, tr_state, NULL);
gf_mx2d_copy(tr_state->transform, backup);
}
static void svg_traverse_filter(GF_Node *node, void *rs, Bool is_destroy)
{
GF_TraverseState *tr_state = (GF_TraverseState *)rs;
GF_FilterStack *st = gf_node_get_private(node);
if (is_destroy) {
drawable_del(st->drawable);
if (st->data) gf_free(st->data);
st->txh.data = NULL;
gf_sc_texture_release(&st->txh);
gf_sc_texture_destroy(&st->txh);
gf_free(st);
return;
}
if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) {
if (! tr_state->visual->DrawBitmap(tr_state->visual, tr_state, tr_state->ctx)) {
visual_2d_texture_path(tr_state->visual, st->drawable->path, tr_state->ctx, tr_state);
}
}
}
void compositor_init_svg_filter(GF_Compositor *compositor, GF_Node *node)
{
GF_FilterStack *stack;
GF_SAFEALLOC(stack, GF_FilterStack);
if (!stack) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate svg filter stack\n"));
return;
}
gf_node_set_private(node, stack);
gf_node_set_callback_function(node, svg_traverse_filter);
gf_sc_texture_setup(&stack->txh, compositor, node);
stack->drawable = drawable_new();
stack->drawable->flags |= DRAWABLE_USE_TRAVERSE_DRAW;
stack->drawable->node = node;
gf_sc_texture_allocate(&stack->txh);
}
#endif