root/src/compositor/hc_flash_shape.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. clean_paths
  2. new_fs_item
  3. build_shape
  4. fs_traverse
  5. compositor_init_hc_flashshape

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2000-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / Scene Compositor sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "nodes_stacks.h"

#include "visual_manager.h"

#ifndef GPAC_DISABLE_3D
#include <gpac/internal/mesh.h>
#endif

typedef struct
{
        GF_Path *path;
        Fixed width;
        u32 fill_col, line_col;
#ifndef GPAC_DISABLE_3D
        GF_Mesh *mesh;
#endif
} FSItem;


typedef struct
{
        GF_Compositor *compositor;
        Drawable *drawable;
        GF_Rect bounds;
        GF_List *items;
        Fixed max_width;
} FSStack;


static void clean_paths(FSStack *stack)
{
        /*delete all path objects*/
        while (gf_list_count(stack->items)) {
                FSItem *it = (FSItem*)gf_list_get(stack->items, 0);
                gf_list_rem(stack->items, 0);
                if (it->path) gf_path_del(it->path);
#ifndef GPAC_DISABLE_3D
                if (it->mesh) mesh_free(it->mesh);
#endif
                gf_free(it);
        }
}

static FSItem *new_fs_item(FSStack *st, u32 line_col, u32 fill_col, Fixed width)
{
        FSItem *item;
        GF_SAFEALLOC(item, FSItem);
        if (!item) return NULL;
        gf_list_add(st->items, item);
        item->fill_col = fill_col;
        item->width = width;
        item->line_col = line_col;
        item->path = gf_path_new();
        return item;
}

#define SFCOL_MAKE_ARGB(c) GF_COL_ARGB_FIXED(FIX_ONE, c.red, c.green, c.blue);

static void build_shape(FSStack *st, GF_Node *node)
{
        GF_FieldInfo field;
        MFVec2f *coords;
        MFInt32 *com, *widthIndex, *lineIndex, *fillIndex, *coordIndex;
        MFFloat *widths;
        MFColor *colors;
        u32 wi, li, fi, ci, command, i, has_ci;
        FSItem *fill_item, *line_item;
        Fixed w;
        SFVec2f cur, pt, ct1= {0,0}, ct2, *pts;
        GF_Rect rc;
        u32 line_col, fill_col;
        Bool need_line, need_fill;

        /*get all fields*/
        gf_node_get_field(node, 0, &field);
        coords = (MFVec2f*)field.far_ptr;
        gf_node_get_field(node, 1, &field);
        com = (MFInt32*)field.far_ptr;
        gf_node_get_field(node, 2, &field);
        widths = (MFFloat*)field.far_ptr;
        gf_node_get_field(node, 3, &field);
        colors = (MFColor*)field.far_ptr;
        gf_node_get_field(node, 4, &field);
        widthIndex = (MFInt32*)field.far_ptr;
        gf_node_get_field(node, 5, &field);
        lineIndex = (MFInt32*)field.far_ptr;
        gf_node_get_field(node, 6, &field);
        fillIndex = (MFInt32*)field.far_ptr;
        gf_node_get_field(node, 7, &field);
        coordIndex = (MFInt32*)field.far_ptr;

        wi = li = fi = ci = 0;
        w = 0;

        st->max_width = 0;
        cur.x = cur.y = 0;
        fill_item = line_item = NULL;
        need_line = need_fill = GF_FALSE;
        cur.x = cur.y = 0;
        has_ci = coordIndex->count ? 1 : 0;
        pts = coords->vals;
        line_col = fill_col = 0;

        /*implicit commands: 0 1 2 3*/

        /*
                if (widthIndex->count) {
                        w = (widthIndex->vals[wi]==-1) ? 0 : widths->vals[widthIndex->vals[wi]];
                        if (!w) {
                                need_line = 0;
                                line_item = NULL;
                        } else {
                                need_line = 1;
                                if (st->max_width<w) st->max_width = w;
                        }
                        wi++;
                }
                if (lineIndex->count) {
                        if (w) {
                                line_col = SFCOL_MAKE_ARGB(colors->vals[lineIndex->vals[li]]);
                                need_line = 1;
                        }
                        li++;
                }
                if (fillIndex->count) {
                        if (fillIndex->vals[fi]==-1) {
                                fill_col = 0;
                                fill_item = NULL;
                        } else {
                                fill_col = SFCOL_MAKE_ARGB(colors->vals[fillIndex->vals[fi]]);
                                need_fill = 1;
                        }
                        fi++;
                }
                if (!coordIndex->count) return;
                cur = coords->vals[coordIndex->vals[ci]];
                ci++;
        */

        for (command=0; command<com->count; command++) {
                switch (com->vals[command]) {
                /*set line width*/
                case 0:
                        if (wi >= widthIndex->count) return;
                        w = (widthIndex->vals[wi]==-1) ? 0 : widths->vals[widthIndex->vals[wi]];
                        if (!w)
                                line_item = NULL;
                        else {
                                need_line = GF_TRUE;
                                if (st->max_width<w) st->max_width = w;
                        }
                        wi++;
                        break;
                /*set line color*/
                case 1:
                        if (li > lineIndex->count) return;
                        if (w) {
                                line_col = SFCOL_MAKE_ARGB(colors->vals[lineIndex->vals[li]]);
                                need_line = GF_TRUE;
                        }
                        li++;
                        break;
                /*set fill color*/
                case 2:
                        if (fi >= fillIndex->count) return;
                        if (fillIndex->vals[fi]==-1) {
                                fill_col = 0;
                                fill_item = NULL;
                        } else {
                                fill_col = SFCOL_MAKE_ARGB(colors->vals[fillIndex->vals[fi]]);
                                need_fill = GF_TRUE;
                        }
                        fi++;
                        break;
                /*moveTo*/
                case 3:
                        if ((has_ci && ci >= coordIndex->count) || (!has_ci && ci >= coords->count) ) return;
                        if (need_fill) {
                                fill_item = new_fs_item(st, 0, fill_col, 0);
                                need_fill = GF_FALSE;
                        }
                        if (need_line) {
                                line_item = new_fs_item(st, line_col, 0, w);
                                need_line = GF_FALSE;
                        }
                        if (has_ci) {
                                pt = pts[coordIndex->vals[ci]];
                        } else {
                                pt = pts[ci];
                        }
                        if (fill_item) gf_path_add_move_to(fill_item->path, pt.x, pt.y);
                        if (line_item) gf_path_add_move_to(line_item->path, pt.x, pt.y);
                        ct1 = pt;
                        cur = pt;
                        ci++;
                        break;
                /*lineTo*/
                case 4:
                        if ((has_ci && ci >= coordIndex->count) || (!has_ci && ci >= coords->count) ) return;
                        if (need_fill) {
                                fill_item = new_fs_item(st, 0, fill_col, 0);
                                gf_path_add_move_to(fill_item->path, cur.x, cur.y);
                                need_fill = GF_FALSE;
                        }
                        if (need_line) {
                                line_item = new_fs_item(st, line_col, 0, w);
                                gf_path_add_move_to(line_item->path, cur.x, cur.y);
                                need_line = GF_FALSE;
                        }
                        if (has_ci) {
                                pt = pts[coordIndex->vals[ci]];
                        } else {
                                pt = pts[ci];
                        }
                        if (fill_item) gf_path_add_line_to(fill_item->path, pt.x, pt.y);
                        if (line_item) gf_path_add_line_to(line_item->path, pt.x, pt.y);
                        cur = pt;
                        ci++;
                        break;
                /*cubic curveTo*/
                case 5:
                        if ((has_ci && ci + 2 >= coordIndex->count) || (!has_ci && ci + 2>= coords->count) ) return;
                        if (need_fill) {
                                fill_item = new_fs_item(st, 0, fill_col, 0);
                                gf_path_add_move_to(fill_item->path, cur.x, cur.y);
                                need_fill = GF_FALSE;
                        }
                        if (need_line) {
                                line_item = new_fs_item(st, line_col, 0, w);
                                gf_path_add_move_to(line_item->path, cur.x, cur.y);
                                need_line = GF_FALSE;
                        }
                        if (has_ci) {
                                ct1 = pts[coordIndex->vals[ci]];
                                ct2 = pts[coordIndex->vals[ci+1]];
                                pt = pts[coordIndex->vals[ci+2]];
                        } else {
                                ct1 = pts[ci];
                                ct2 = pts[ci+1];
                                pt = pts[ci+2];
                        }
                        if (fill_item) gf_path_add_cubic_to(fill_item->path, ct1.x, ct1.y, ct2.x, ct2.y, pt.x, pt.y);
                        if (line_item) gf_path_add_cubic_to(line_item->path, ct1.x, ct1.y, ct2.x, ct2.y, pt.x, pt.y);
                        ct1 = ct2;
                        cur = pt;
                        ci += 3;
                        break;
                /*cubic nextCurveTo*/
                case 6:
                        if ((has_ci && ci + 1 >= coordIndex->count) || (!has_ci && ci + 1>= coords->count) ) return;
                        if (need_fill) {
                                fill_item = new_fs_item(st, 0, fill_col, 0);
                                gf_path_add_move_to(fill_item->path, cur.x, cur.y);
                                need_fill = GF_FALSE;
                        }
                        if (need_line) {
                                line_item = new_fs_item(st, line_col, 0, w);
                                gf_path_add_move_to(line_item->path, cur.x, cur.y);
                                need_line = GF_FALSE;
                        }
                        ct1.x = 2*cur.x - ct1.x;
                        ct1.y = 2*cur.y - ct1.y;
                        if (has_ci) {
                                ct2 = pts[coordIndex->vals[ci]];
                                pt = pts[coordIndex->vals[ci+1]];
                        } else {
                                ct2 = pts[ci];
                                pt = pts[ci+1];
                        }
                        if (fill_item) gf_path_add_cubic_to(fill_item->path, ct1.x, ct1.y, ct2.x, ct2.y, pt.x, pt.y);
                        if (line_item) gf_path_add_cubic_to(line_item->path, ct1.x, ct1.y, ct2.x, ct2.y, pt.x, pt.y);
                        ct1 = ct2;
                        cur = pt;
                        ci += 2;
                        break;
                /*quadratic CurveTo*/
                case 7:
                        if ((has_ci && ci + 1 >= coordIndex->count) || (!has_ci && ci + 1>= coords->count) ) return;
                        if (need_fill) {
                                fill_item = new_fs_item(st, 0, fill_col, 0);
                                gf_path_add_move_to(fill_item->path, cur.x, cur.y);
                                need_fill = GF_FALSE;
                        }
                        if (need_line) {
                                line_item = new_fs_item(st, line_col, 0, w);
                                gf_path_add_move_to(line_item->path, cur.x, cur.y);
                                need_line = GF_FALSE;
                        }
                        if (has_ci) {
                                ct1 = pts[coordIndex->vals[ci]];
                                pt = pts[coordIndex->vals[ci+1]];
                        } else {
                                ct1 = pts[ci];
                                pt = pts[ci+1];
                        }
                        if (fill_item) gf_path_add_quadratic_to(fill_item->path, ct1.x, ct1.y, pt.x, pt.y);
                        if (line_item) gf_path_add_quadratic_to(line_item->path, ct1.x, ct1.y, pt.x, pt.y);
                        cur = pt;
                        ci += 2;
                        break;
                /*quadratic nextCurveTo*/
                case 8:
                        if ((has_ci && ci >= coordIndex->count) || (!has_ci && ci >= coords->count) ) return;
                        if (need_fill) {
                                fill_item = new_fs_item(st, 0, fill_col, 0);
                                gf_path_add_move_to(fill_item->path, cur.x, cur.y);
                                need_fill = GF_FALSE;
                        }
                        if (need_line) {
                                line_item = new_fs_item(st, line_col, 0, w);
                                gf_path_add_move_to(line_item->path, cur.x, cur.y);
                                need_line = GF_FALSE;
                        }
                        ct1.x = 2*cur.x - ct1.x;
                        ct1.y = 2*cur.y - ct1.y;
                        if (has_ci) {
                                pt = pts[coordIndex->vals[ci]];
                        } else {
                                pt = pts[ci];
                        }
                        if (fill_item) gf_path_add_quadratic_to(fill_item->path, ct1.x, ct1.y, pt.x, pt.y);
                        if (line_item) gf_path_add_quadratic_to(line_item->path, ct1.x, ct1.y, pt.x, pt.y);
                        cur = pt;
                        ci += 2;
                        break;
                /*close*/
                case 9:
                        if (fill_item) gf_path_close(fill_item->path);
                        if (line_item) gf_path_close(line_item->path);
                        break;
                }
        }

        /*compute bounds*/
        st->bounds.width = st->bounds.height = 0;
        for (i=0; i<gf_list_count(st->items); i++) {
                line_item = (FSItem*)gf_list_get(st->items, i);
                gf_path_get_bounds(line_item->path, &rc);
                gf_rect_union(&st->bounds, &rc);
        }
}

static void fs_traverse(GF_Node *node, void *rs, Bool is_destroy)
{
        u32 i;
        DrawableContext *ctx;
        FSStack *st = (FSStack *) gf_node_get_private(node);
        GF_TraverseState *tr_state = (GF_TraverseState*)rs;

        if (is_destroy) {
                clean_paths(st);
                drawable_del(st->drawable);
                gf_list_del(st->items);
                gf_free(st);
                return;
        }
        /*check for geometry change*/
        if (gf_node_dirty_get(node)) {
                gf_node_dirty_clear(node, 0);
                /*build*/
                clean_paths(st);
                build_shape(st, node);
        }

        switch (tr_state->traversing_mode) {
        case TRAVERSE_DRAW_2D:
                ctx = tr_state->ctx;
                for (i=0; i<gf_list_count(st->items); i++) {
                        FSItem *item = (FSItem*)gf_list_get(st->items, i);
                        ctx->flags &= ~(CTX_PATH_FILLED | CTX_PATH_STROKE);
                        memset(&ctx->aspect, 0, sizeof(DrawAspect2D));
                        if (item->fill_col) {
                                ctx->aspect.fill_color = item->fill_col;
                        }
                        if (item->width) {
                                ctx->aspect.line_color = item->line_col;
                                ctx->aspect.pen_props.width = item->width;
                        }
                        visual_2d_draw_path(tr_state->visual, item->path, ctx, NULL, NULL, tr_state);
                }
                return;

#ifndef GPAC_DISABLE_3D
        case TRAVERSE_DRAW_3D:
                ctx = tr_state->ctx;
                for (i=0; i<gf_list_count(st->items); i++) {
                        FSItem *item = (FSItem*)gf_list_get(st->items, i);
                        memset(&ctx->aspect, 0, sizeof(DrawAspect2D));
                        if (item->fill_col) {
                                ctx->aspect.fill_color = item->fill_col;
                        }
                        if (item->width) {
                                ctx->aspect.line_color = item->line_col;
                                ctx->aspect.pen_props.width = item->width;
                        }
                        if (!item->mesh) {
                                item->mesh = new_mesh();
                                mesh_from_path(item->mesh, item->path);
                        }
                        st->drawable->mesh = item->mesh;
                        visual_3d_draw_2d_with_aspect(st->drawable, tr_state, &ctx->aspect);
                        st->drawable->mesh = NULL;
                }
                return;
#endif
        case TRAVERSE_PICK:
                /*todo*/
                return;
        case TRAVERSE_GET_BOUNDS:
                tr_state->bounds = st->bounds;
                return;
        case TRAVERSE_SORT:
#ifndef GPAC_DISABLE_3D
                if (tr_state->visual->type_3d) return;
#endif
                /*finalize*/
#ifndef GPAC_DISABLE_VRML
                ctx = drawable_init_context_mpeg4(st->drawable, tr_state);
#else
                ctx = NULL;
#endif
                if (!ctx) return;

                /*force width to max width used for clipper compute*/
                if (st->max_width) {
                        ctx->aspect.pen_props.width = st->max_width;
                }
                drawable_finalize_sort(ctx, tr_state, &st->bounds);
                break;
        }
}


void compositor_init_hc_flashshape(GF_Compositor *compositor, GF_Node *node)
{
        FSStack *stack;

        GF_SAFEALLOC(stack, FSStack);
        if (!stack) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate flashshape proto stack\n"));
                return;
        }
        stack->drawable = drawable_new();
        stack->drawable->node = node;
        stack->drawable->flags = DRAWABLE_USE_TRAVERSE_DRAW;
        stack->items = gf_list_new();

        gf_node_set_private(node, stack);
        gf_node_set_callback_function(node, fs_traverse);
}


/* [<][>][^][v][top][bottom][index][help] */