root/src/compositor/visual_manager_2d_draw.c

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

DEFINITIONS

This source file includes following definitions.
  1. visual_2d_init_raster
  2. visual_2d_release_raster
  3. visual_2d_clear_surface
  4. draw_clipper
  5. visual_2d_fill_path
  6. visual_2d_set_options
  7. visual_2d_get_texture_transform
  8. visual_2d_draw_gradient
  9. visual_2d_texture_path_text
  10. visual_2d_flush_hybgl_canvas
  11. visual_2d_texture_path_opengl_auto
  12. visual_2d_texture_path_extended
  13. visual_2d_texture_path
  14. visual_2d_draw_path_extended
  15. visual_2d_draw_path
  16. visual_2d_fill_rect
  17. visual_2d_fill_irect

/*
 *                      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 "visual_manager.h"
#include "drawable.h"
#include "nodes_stacks.h"
#include "texturing.h"
#include <gpac/options.h>

//#define SKIP_DRAW

GF_Err visual_2d_init_raster(GF_VisualManager *visual)
{
        GF_Raster2D *raster = visual->compositor->rasterizer;
        if (!visual->raster_surface) {
                visual->raster_surface = raster->surface_new(raster, visual->center_coords);
                if (!visual->raster_surface) return GF_IO_ERR;
        }
        return visual->GetSurfaceAccess(visual);
}

void visual_2d_release_raster(GF_VisualManager *visual)
{
        if (visual->raster_surface) {
                if (visual->compositor->rasterizer->surface_flush)
                        visual->compositor->rasterizer->surface_flush(visual->raster_surface);

                visual->ReleaseSurfaceAccess(visual);
        }
}


void visual_2d_clear_surface(GF_VisualManager *visual, GF_IRect *rc, u32 BackColor, u32 is_offscreen)
{
#ifdef SKIP_DRAW
        return;
#endif
        if (! visual->CheckAttached(visual) ) return;

        if (!BackColor && !visual->offscreen) {
                if (!visual->compositor->user || !(visual->compositor->user->init_flags & GF_TERM_WINDOW_TRANSPARENT)) {
                        BackColor = visual->compositor->back_color;
                }
        }

        visual->compositor->rasterizer->surface_clear(visual->raster_surface, rc, BackColor);
}

static void draw_clipper(GF_VisualManager *visual, struct _drawable_context *ctx)
{
        GF_PenSettings clipset;
        GF_Path *clippath, *cliper;
        GF_Raster2D *raster = visual->compositor->rasterizer;

        if (ctx->flags & CTX_IS_BACKGROUND) return;

        memset(&clipset, 0, sizeof(GF_PenSettings));
        clipset.width = 2*FIX_ONE;

        clippath = gf_path_new();
        gf_path_add_rect_center(clippath, ctx->bi->unclip.x + ctx->bi->unclip.width/2, ctx->bi->unclip.y - ctx->bi->unclip.height/2, ctx->bi->unclip.width, ctx->bi->unclip.height);
        cliper = gf_path_get_outline(clippath, clipset);
        gf_path_del(clippath);
        raster->surface_set_matrix(visual->raster_surface, NULL);
        raster->surface_set_clipper(visual->raster_surface, NULL);
        raster->surface_set_path(visual->raster_surface, cliper);
        raster->stencil_set_brush_color(visual->raster_brush, 0xFF000000);
        raster->surface_fill(visual->raster_surface, visual->raster_brush);
        gf_path_del(cliper);
}

static void visual_2d_fill_path(GF_VisualManager *visual, DrawableContext *ctx, GF_STENCIL stencil, GF_TraverseState *tr_state, Bool is_erase)
{
        Bool has_modif = GF_FALSE;
        GF_IRect clip;
        GF_Raster2D *raster = visual->compositor->rasterizer;

        /*background & direct drawing : use ctx clip*/
        if ((ctx->flags & CTX_IS_BACKGROUND) || tr_state->immediate_draw) {
                if (ctx->bi->clip.width && ctx->bi->clip.height) {
                        GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redrawing node %s [%s] (direct draw)\n", gf_node_get_log_name(ctx->drawable->node), gf_node_get_class_name(ctx->drawable->node) ));

                        if (stencil) {
                                raster->surface_set_clipper(visual->raster_surface, &ctx->bi->clip);
                                raster->surface_fill(visual->raster_surface, stencil);
                        } else {
                                raster->surface_clear(visual->raster_surface, &ctx->bi->clip, 0);
                        }

                        has_modif = GF_TRUE;
                }
        }
        /*indirect drawing, draw path in all dirty areas*/
        else {
                u32 i;
                for (i=0; i<visual->to_redraw.count; i++) {
                        /*there's an opaque region above, don't draw*/
#ifdef TRACK_OPAQUE_REGIONS
                        if (!is_erase && (visual->draw_node_index<visual->to_redraw.list[i].opaque_node_index)) continue;
#endif
                        clip = ctx->bi->clip;
                        gf_irect_intersect(&clip, &visual->to_redraw.list[i].rect);
                        if (clip.width && clip.height) {
                                GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redrawing node %s [%s] (indirect draw @ dirty rect idx %d)\n", gf_node_get_log_name(ctx->drawable->node), gf_node_get_class_name(ctx->drawable->node), i));
                                if (stencil) {
                                        raster->surface_set_clipper(visual->raster_surface, &clip);
                                        raster->surface_fill(visual->raster_surface, stencil);
                                } else {
                                        raster->surface_clear(visual->raster_surface, &clip, 0);
                                }
                                has_modif = 1;
                        }
                }
        }
#ifndef GPAC_DISABLE_3D
        if (!is_erase)
                visual->nb_objects_on_canvas_since_last_ogl_flush++;
#endif
        if (has_modif) {
                visual->has_modif = 1;
#ifndef GPAC_DISABLE_3D
                if (!visual->offscreen && visual->compositor->hybrid_opengl && !is_erase)
                        ra_union_rect(&visual->hybgl_drawn, &ctx->bi->clip);
#endif
        }
}

static void visual_2d_set_options(GF_Compositor *compositor, GF_SURFACE rend, Bool forText, Bool no_antialias)
{
        GF_Raster2D *raster = compositor->rasterizer;
        if (no_antialias) {
                raster->surface_set_raster_level(rend, GF_RASTER_HIGH_SPEED);
        } else {
                switch (compositor->antiAlias) {
                case GF_ANTIALIAS_NONE:
                        raster->surface_set_raster_level(rend, GF_RASTER_HIGH_SPEED);
                        break;
                case GF_ANTIALIAS_TEXT:
                        if (forText) {
                                raster->surface_set_raster_level(rend, GF_RASTER_HIGH_QUALITY);
                        } else {
                                raster->surface_set_raster_level(rend, compositor->high_speed ? GF_RASTER_HIGH_QUALITY : GF_RASTER_MID);
                        }
                        break;
                case GF_ANTIALIAS_FULL:
                default:
                        raster->surface_set_raster_level(rend, GF_RASTER_HIGH_QUALITY);
                        break;
                }
        }
}


#ifndef GPAC_DISABLE_VRML
static void visual_2d_get_texture_transform(GF_Node *__appear, GF_TextureHandler *txh, GF_Matrix2D *mat, Bool line_texture, Fixed final_width, Fixed final_height)
{
        u32 node_tag;
        M_Appearance *appear;
        GF_Node *txtrans = NULL;
        gf_mx2d_init(*mat);

        if (!__appear || !txh) return;
        appear = (M_Appearance *)__appear;

        if (!line_texture) {
                if (!appear->textureTransform) return;
                txtrans = appear->textureTransform;
        } else {
                if (gf_node_get_tag(appear->material) != TAG_MPEG4_Material2D) return;
                if (gf_node_get_tag(((M_Material2D *)appear->material)->lineProps) != TAG_MPEG4_XLineProperties) return;
                txtrans = ((M_XLineProperties *) ((M_Material2D *)appear->material)->lineProps)->textureTransform;
        }
        if (!txtrans) return;

        /*gradient doesn't need bounds info in texture transform*/
        if (txh->compute_gradient_matrix) {
                final_width = final_height = FIX_ONE;
        }
        node_tag = gf_node_get_tag(txtrans);
        if (node_tag==TAG_MPEG4_TextureTransform) {
                /*VRML: Tc' = -C � S � R � C � T � Tc*/
                M_TextureTransform *txt = (M_TextureTransform *) txtrans;
                SFVec2f scale = txt->scale;
                if (!scale.x) scale.x = FIX_ONE/100;
                if (!scale.y) scale.y = FIX_ONE/100;

                gf_mx2d_add_translation(mat, -gf_mulfix(txt->center.x, final_width), -gf_mulfix(txt->center.y, final_height) );
                gf_mx2d_add_scale(mat, scale.x, scale.y);
                gf_mx2d_add_rotation(mat, 0, 0, txt->rotation);
                gf_mx2d_add_translation(mat, gf_mulfix(txt->center.x, final_width), gf_mulfix(txt->center.y, final_height) );
                gf_mx2d_add_translation(mat, gf_mulfix(txt->translation.x, final_width), gf_mulfix(txt->translation.y, final_height) );
                /*and inverse the matrix (this is texture transform, cf VRML)*/
                gf_mx2d_inverse(mat);
                return;
        }
        if (node_tag==TAG_MPEG4_TransformMatrix2D) {
                tr_mx2d_get_matrix((GF_Node *) txtrans, mat);
                mat->m[2] = gf_mulfix(mat->m[2], final_width);
                mat->m[5] = gf_mulfix(mat->m[5], final_height);
                gf_mx2d_inverse(mat);
                return;
        }
}
#endif /*GPAC_DISABLE_VRML*/

static void visual_2d_draw_gradient(GF_VisualManager *visual, GF_Path *path, GF_TextureHandler *txh, struct _drawable_context *ctx, GF_TraverseState *tr_state, GF_Matrix2D *ext_mx, GF_Rect *orig_bounds)
{
        GF_Rect rc;
        GF_STENCIL stencil;
        GF_Matrix2D g_mat;
        GF_Raster2D *raster = visual->compositor->rasterizer;

        if (!txh) txh = ctx->aspect.fill_texture;

        gf_path_get_bounds(path, &rc);
        if (!rc.width || !rc.height || !txh->tx_io) return;

        if (orig_bounds) {
                txh->compute_gradient_matrix(txh, orig_bounds, &g_mat, 0);
        } else {
                txh->compute_gradient_matrix(txh, &rc, &g_mat, 0);
        }
        stencil = gf_sc_texture_get_stencil(txh);
        if (!stencil) return;

#ifndef GPAC_DISABLE_VRML
        if (ctx->flags & CTX_HAS_APPEARANCE) {
                GF_Matrix2D txt_mat;
                visual_2d_get_texture_transform(ctx->appear, txh, &txt_mat, (txh == ctx->aspect.fill_texture) ? 0 : 1, INT2FIX(txh->width), INT2FIX(txh->height));
                gf_mx2d_add_matrix(&g_mat, &txt_mat);
        }
#endif

        /*move to bottom-left corner of bounds */
        if (ext_mx) gf_mx2d_add_matrix(&g_mat, ext_mx);
        if (orig_bounds) gf_mx2d_add_translation(&g_mat, (orig_bounds->x), (orig_bounds->y - orig_bounds->height));

        gf_mx2d_add_matrix(&g_mat, &ctx->transform);

        raster->stencil_set_matrix(stencil, &g_mat);
        raster->stencil_set_color_matrix(stencil, ctx->col_mat);

        /*MPEG-4/VRML context or no fill info*/
        if (ctx->flags & CTX_HAS_APPEARANCE || !ctx->aspect.fill_color)
                raster->stencil_set_alpha(stencil, 0xFF);
        else
                raster->stencil_set_alpha(stencil, GF_COL_A(ctx->aspect.fill_color) );

        raster->surface_set_matrix(visual->raster_surface, &ctx->transform);
        txh->flags |= GF_SR_TEXTURE_USED;

        raster->surface_set_path(visual->raster_surface, path);
        visual_2d_fill_path(visual, ctx, stencil, tr_state, 0);
        raster->surface_set_path(visual->raster_surface, NULL);

        ctx->flags |= CTX_PATH_FILLED;
}



void visual_2d_texture_path_text(GF_VisualManager *visual, DrawableContext *txt_ctx, GF_Path *path, GF_Rect *object_bounds, GF_TextureHandler *txh, GF_TraverseState *tr_state)
{
        GF_STENCIL stencil;
        Fixed sS, sT;
        GF_Matrix2D gf_mx2d_txt;
        GF_Rect orig_rc;
        u8 alpha, r, g, b;
        GF_ColorMatrix cmat;
        GF_Raster2D *raster;

        if (! visual->CheckAttached(visual) ) return;

        raster = visual->compositor->rasterizer;

        stencil = gf_sc_texture_get_stencil(txh);
        if (!stencil) return;

        visual_2d_set_options(visual->compositor, visual->raster_surface, 0, 1);

        /*get original bounds*/
        orig_rc = *object_bounds;

        /*get scaling ratio so that active texture view is stretched to original bounds (std 2D shape texture mapping in MPEG4)*/
        sS = gf_divfix(orig_rc.width, INT2FIX(txh->width));
        sT = gf_divfix(orig_rc.height, INT2FIX(txh->height));

        gf_mx2d_init(gf_mx2d_txt);
        gf_mx2d_add_scale(&gf_mx2d_txt, sS, sT);

        /*move to bottom-left corner of bounds */
        gf_mx2d_add_translation(&gf_mx2d_txt, (orig_rc.x), (orig_rc.y - orig_rc.height));

        /*move to final coordinate system*/
        gf_mx2d_add_matrix(&gf_mx2d_txt, &txt_ctx->transform);

        /*set path transform, except for background2D node which is directly build in the final coord system*/
        raster->stencil_set_matrix(stencil, &gf_mx2d_txt);

        alpha = GF_COL_A(txt_ctx->aspect.fill_color);
        r = GF_COL_R(txt_ctx->aspect.fill_color);
        g = GF_COL_G(txt_ctx->aspect.fill_color);
        b = GF_COL_B(txt_ctx->aspect.fill_color);

        /*if col do a cxmatrix*/
        if (!r && !g && !b) {
                raster->stencil_set_alpha(stencil, alpha);
        } else {
                raster->stencil_set_alpha(stencil, 0xFF);
                memset(cmat.m, 0, sizeof(Fixed) * 20);
                cmat.m[4] = INT2FIX(r)/255;
                cmat.m[9] = INT2FIX(g)/255;
                cmat.m[14] = INT2FIX(b)/255;
                cmat.m[18] = INT2FIX(alpha)/255;
                cmat.identity = 0;
                raster->stencil_set_color_matrix(stencil, &cmat);
        }

        raster->surface_set_matrix(visual->raster_surface, &txt_ctx->transform);
        txh->flags |= GF_SR_TEXTURE_USED;

        /*push path*/
        raster->surface_set_path(visual->raster_surface, path);

        visual_2d_fill_path(visual, txt_ctx, stencil, tr_state, 0);
        raster->surface_set_path(visual->raster_surface, NULL);
        txt_ctx->flags |= CTX_PATH_FILLED;
}


#ifndef GPAC_DISABLE_3D

void visual_2d_flush_hybgl_canvas(GF_VisualManager *visual, GF_TextureHandler *txh, struct _drawable_context *ctx, GF_TraverseState *tr_state)
{
        Bool line_texture = GF_FALSE;
        u32 i;
        u32 prev_color;
        Bool transparent, had_flush = 0;
        u32 nb_obj_left_on_canvas = visual->nb_objects_on_canvas_since_last_ogl_flush;
        u8 alpha;

        if (! visual->hybgl_drawn.count)
                return;

        //we have drawn things on the canvas before this object, flush canvas to GPU

        if (txh && (txh==ctx->aspect.line_texture)) {
                line_texture = GF_TRUE;
                alpha = GF_COL_A(ctx->aspect.line_color);
                prev_color = ctx->aspect.line_color;
                ctx->aspect.line_texture = NULL;
                ctx->aspect.line_color = 0;
        } else {
                alpha = GF_COL_A(ctx->aspect.fill_color);
                if (!alpha) alpha = GF_COL_A(ctx->aspect.line_color);
                prev_color = ctx->aspect.fill_color;
                ctx->aspect.fill_texture = NULL;
                ctx->aspect.fill_color = 0;

        }
        transparent = txh ? (txh->transparent || (alpha!=0xFF)) : GF_TRUE;
        //clear wherever we have overlap
        for (i=0; i<visual->hybgl_drawn.count; i++) {
                GF_IRect rc = ctx->bi->clip;
                gf_irect_intersect(&ctx->bi->clip, &visual->hybgl_drawn.list[i].rect);
                if (ctx->bi->clip.width && ctx->bi->clip.height) {
                        //if something behind this, flush canvas to gpu
                        if (transparent) {
                                if (!had_flush) {
                                        //flush the complete area below this object, regardless of intersections
                                        compositor_2d_hybgl_flush_video(visual->compositor, tr_state->immediate_draw ? NULL : &rc);
                                        had_flush = 1;
                                }
                                //if object was not completely in the flush region we will need to flush the canvas
                                if ( gf_irect_inside(&rc, &visual->hybgl_drawn.list[i].rect)) {
                                        //it may happen that we had no object on the canvas but syil have their bounds (we only drew textures)
                                        if (nb_obj_left_on_canvas)
                                                nb_obj_left_on_canvas--;
                                }
                        }
                        //immediate mode flush, erase all canvas (we just completely flusged it)
                        if (tr_state->immediate_draw && had_flush && !tr_state->immediate_for_defer) {
                                visual->compositor->rasterizer->surface_clear(visual->raster_surface, NULL, 0);
                        }
                        //defer mode, erase all part of the canvas below us
                        else if (txh) {
                                visual_2d_draw_path_extended(visual, ctx->drawable->path, ctx, NULL, NULL, tr_state, NULL, NULL, GF_TRUE);
                        } else {
                                visual->compositor->rasterizer->surface_clear(visual->raster_surface, &ctx->bi->clip, 0);
                        }
                }
                ctx->bi->clip = rc;
        }
        if (line_texture) {
                ctx->aspect.line_color = prev_color;
                ctx->aspect.line_texture = txh;
        } else {
                ctx->aspect.fill_color = prev_color;
                ctx->aspect.fill_texture = txh;
        }

        if (had_flush) {
                visual->nb_objects_on_canvas_since_last_ogl_flush = nb_obj_left_on_canvas;
        }
}

void visual_2d_texture_path_opengl_auto(GF_VisualManager *visual, GF_Path *path, GF_TextureHandler *txh, struct _drawable_context *ctx, GF_Rect *orig_bounds, GF_Matrix2D *ext_mx, GF_TraverseState *tr_state)
{
        GF_Rect clipper;
        GF_Matrix mx;
        u32 prev_mode = tr_state->traversing_mode;
        u32 prev_type_3d = tr_state->visual->type_3d;

        visual_2d_flush_hybgl_canvas(visual, txh, ctx, tr_state);

        tr_state->visual->type_3d = 4;
        tr_state->appear = ctx->appear;
        if (ctx->col_mat) gf_cmx_copy(&tr_state->color_mat, ctx->col_mat);//

        tr_state->traversing_mode=TRAVERSE_DRAW_3D;
        //in hybridGL the 2D camera is always setup as centered-coords, we have to insert flip+translation in case of top-left origin 
        if (tr_state->visual->center_coords) {
                gf_mx_from_mx2d(&tr_state->model_matrix, &ctx->transform);
        } else {
                gf_mx_init(tr_state->model_matrix);
                gf_mx_add_scale(&tr_state->model_matrix, FIX_ONE, -FIX_ONE, FIX_ONE);
                gf_mx_add_translation(&tr_state->model_matrix, -tr_state->camera->width/2, -tr_state->camera->height/2, 0);

                gf_mx_from_mx2d(&mx, &ctx->transform);
                gf_mx_add_matrix(&tr_state->model_matrix, &mx);
        }

        clipper.x = INT2FIX(ctx->bi->clip.x);
        clipper.y = INT2FIX(ctx->bi->clip.y);
        clipper.width = INT2FIX(ctx->bi->clip.width);
        clipper.height = INT2FIX(ctx->bi->clip.height);
        visual_3d_set_clipper_2d(tr_state->visual, clipper, NULL);

        gf_node_allow_cyclic_traverse(ctx->drawable->node);
        gf_node_traverse(ctx->drawable->node, tr_state);

        tr_state->visual->type_3d=prev_type_3d;
        tr_state->traversing_mode=prev_mode;
        if (ctx->col_mat) gf_cmx_init(&tr_state->color_mat);

        ctx->flags |= CTX_PATH_FILLED;

        visual_3d_reset_clipper_2d(tr_state->visual);
}
#endif


void visual_2d_texture_path_extended(GF_VisualManager *visual, GF_Path *path, GF_TextureHandler *txh, struct _drawable_context *ctx, GF_Rect *orig_bounds, GF_Matrix2D *ext_mx, GF_TraverseState *tr_state)
{
        Fixed sS, sT;
        u32 tx_tile;
        GF_STENCIL tx_raster;
        GF_Matrix2D mx_texture;
        GF_Rect orig_rc;
        GF_Raster2D *raster;

        if (! visual->CheckAttached(visual) ) return;

        raster = visual->compositor->rasterizer;

        if (!txh) txh = ctx->aspect.fill_texture;
        if (!txh) return;
        if (!txh->tx_io) {
                gf_node_dirty_set(txh->owner, 0, 1);

                txh->needs_refresh=1;
                return;
        }


        /*this is gradient draw*/
        if (txh->compute_gradient_matrix) {
                visual_2d_draw_gradient(visual, path, txh, ctx, tr_state, ext_mx, orig_bounds);
                return;
        }


#ifndef GPAC_DISABLE_3D
        if (visual->compositor->hybrid_opengl) {
                visual_2d_texture_path_opengl_auto(visual, path, txh, ctx, orig_bounds, ext_mx, tr_state);
                return;
        }
#endif

        if (txh->flags & GF_SR_TEXTURE_PRIVATE_MEDIA) {
                GF_Window src, dst;
                visual_2d_fill_path(visual, ctx, NULL, tr_state, 0);

                /*if texture not ready, update the size before computing output rectangles */
                if (!txh->width || !txh->height) {
                        gf_mo_get_visual_info(txh->stream, &txh->width, &txh->height, &txh->stride, &txh->pixel_ar, &txh->pixelformat, &txh->is_flipped);
                        /*in case the node is an MPEG-4 bitmap, force stack rebuild at next frame */
                        gf_node_dirty_set(ctx->drawable->node, GF_SG_NODE_DIRTY, 1);
                }

                if (compositor_texture_rectangles(visual, txh, &ctx->bi->clip, &ctx->bi->unclip, &src, &dst, NULL, NULL)) {
                        if (txh->stream && gf_mo_set_position(txh->stream, &src, &dst)) {
                                gf_mo_get_visual_info(txh->stream, &txh->width, &txh->height, &txh->stride, &txh->pixel_ar, &txh->pixelformat, &txh->is_flipped);
                                /*force dirty flag to get called again*/
                                gf_node_dirty_set(ctx->drawable->node, GF_SG_NODE_DIRTY, 1);
                                gf_sc_next_frame_state(visual->compositor, GF_SC_DRAW_FRAME);
                        }
                }



                return;
        }

        if (!gf_sc_texture_push_image(txh, 0, 1)) return;
        tx_raster = gf_sc_texture_get_stencil(txh);

        /*setup quality even for background (since quality concerns images)*/
        visual_2d_set_options(visual->compositor, visual->raster_surface, ctx->flags & CTX_IS_TEXT, ctx->flags & CTX_NO_ANTIALIAS);

        /*get original bounds*/
        if (orig_bounds) {
                orig_rc = *orig_bounds;
        } else {
                gf_path_get_bounds(path, &orig_rc);
        }

        /*get scaling ratio so that active texture view is stretched to original bounds (std 2D shape texture mapping in MPEG4)*/
        sS = orig_rc.width / txh->width;
        sT = orig_rc.height / txh->height;

        gf_mx2d_init(mx_texture);
        gf_mx2d_add_scale(&mx_texture, sS, sT);

#ifndef GPAC_DISABLE_VRML
        /*apply texture transform*/
        if (ctx->flags & CTX_HAS_APPEARANCE) {
                GF_Matrix2D tex_trans;
                visual_2d_get_texture_transform(ctx->appear, txh, &tex_trans, (txh == ctx->aspect.fill_texture) ? 0 : 1, txh->width * sS, txh->height * sT);
                gf_mx2d_add_matrix(&mx_texture, &tex_trans);
        }
#endif

        /*move to bottom-left corner of bounds */
        gf_mx2d_add_translation(&mx_texture, (orig_rc.x), (orig_rc.y - orig_rc.height));

        if (ext_mx) gf_mx2d_add_matrix(&mx_texture, ext_mx);

        /*move to final coordinate system (except background which is built directly in final coord system)*/
        if (!(ctx->flags & CTX_IS_BACKGROUND) ) gf_mx2d_add_matrix(&mx_texture, &ctx->transform);

        /*set path transform*/
        raster->stencil_set_matrix(tx_raster, &mx_texture);


        tx_tile = 0;
        if (txh->flags & GF_SR_TEXTURE_REPEAT_S) tx_tile |= GF_TEXTURE_REPEAT_S;
        if (txh->flags & GF_SR_TEXTURE_REPEAT_T) tx_tile |= GF_TEXTURE_REPEAT_T;
        if (ctx->flags & CTX_FLIPED_COORDS)
                tx_tile |= GF_TEXTURE_FLIP;
        raster->stencil_set_tiling(tx_raster, (GF_TextureTiling) tx_tile);

        if (!(ctx->flags & CTX_IS_BACKGROUND) ) {
                u8 a = GF_COL_A(ctx->aspect.fill_color);
                if (!a) a = GF_COL_A(ctx->aspect.line_color);
                /*texture alpha scale is the original material transparency, NOT the one after color transform*/
                raster->stencil_set_alpha(tx_raster, a );
                raster->stencil_set_color_matrix(tx_raster, ctx->col_mat);

                raster->surface_set_matrix(visual->raster_surface, &ctx->transform);
        } else {
                raster->surface_set_matrix(visual->raster_surface, NULL);
        }
        txh->flags |= GF_SR_TEXTURE_USED;

        /*push path & draw*/
        raster->surface_set_path(visual->raster_surface, path);
        visual_2d_fill_path(visual, ctx, tx_raster, tr_state, 0);
        raster->surface_set_path(visual->raster_surface, NULL);



        ctx->flags |= CTX_PATH_FILLED;
}

void visual_2d_texture_path(GF_VisualManager *visual, GF_Path *path, struct _drawable_context *ctx, GF_TraverseState *tr_state)
{
#ifdef SKIP_DRAW
        return;
#endif
        if (! visual->CheckAttached(visual) ) return;

        if ((ctx->flags & CTX_PATH_FILLED) || !ctx->aspect.fill_texture || visual->compositor->is_hidden) return;

        /*this is ambiguous in the spec, what if the material is filled and the texture is transparent ?
        let's draw, it's nicer */
#if 0
        if (GF_COL_A(ctx->aspect.fill_color) && ctx->aspect.fill_texture->transparent) {
                visual_2d_draw_path(visual, path, ctx, NULL, NULL);
                ctx->flags &= ~CTX_PATH_FILLED;
        }
#endif

        visual_2d_texture_path_extended(visual, path, NULL, ctx, NULL, NULL, tr_state);
}

#define ADAPTATION_SIZE         0


void visual_2d_draw_path_extended(GF_VisualManager *visual, GF_Path *path, DrawableContext *ctx, GF_STENCIL brush, GF_STENCIL pen, GF_TraverseState *tr_state, GF_Rect *orig_bounds, GF_Matrix2D *ext_mx, Bool is_erase)
{
        Bool dofill, dostrike;
        GF_Raster2D *raster = visual->compositor->rasterizer;
#ifdef SKIP_DRAW
        return;
#endif
        if (! visual->CheckAttached(visual) ) return;

        if ((ctx->flags & CTX_PATH_FILLED) && (ctx->flags & CTX_PATH_STROKE) ) {
                if (visual->compositor->draw_bvol) draw_clipper(visual, ctx);
                return;
        }

        if (! (ctx->flags & CTX_IS_BACKGROUND) )
                visual_2d_set_options(visual->compositor, visual->raster_surface, ctx->flags & CTX_IS_TEXT, ctx->flags & CTX_NO_ANTIALIAS);

        dofill = dostrike = 0;
        if (!(ctx->flags & CTX_PATH_FILLED) && (is_erase || GF_COL_A(ctx->aspect.fill_color)) ) {
                dofill = 1;
                if (!brush) {
                        brush = visual->raster_brush;
                        raster->stencil_set_brush_color(brush, ctx->aspect.fill_color);
                }
        }


        /*compute width based on transform and top_level transform*/
        if (!(ctx->flags & CTX_PATH_STROKE) && ctx->aspect.pen_props.width) {
                dostrike = 1;
        } else if (!dofill) {
                return;
        }

        /*set path transform, except for background2D node which is directly build in the final coord system*/
        raster->surface_set_matrix(visual->raster_surface, (ctx->flags & CTX_IS_BACKGROUND) ? NULL : &ctx->transform);

        /*fill path*/
        if (dofill) {
#if ADAPTATION_SIZE
                if ((ctx->bi->clip.width<ADAPTATION_SIZE) && (ctx->bi->clip.height<ADAPTATION_SIZE)) {
                        raster->surface_clear(visual->raster_surface, &ctx->bi->clip, ctx->aspect.fill_color);
                } else
#endif
                {
                        /*push path*/
                        raster->surface_set_path(visual->raster_surface, path);
                        visual_2d_fill_path(visual, ctx, brush, tr_state, is_erase);
                        raster->surface_set_path(visual->raster_surface, NULL);
                }
        }

        if (dostrike) {
#if ADAPTATION_SIZE
                if ((ctx->bi->clip.width<ADAPTATION_SIZE) && (ctx->bi->clip.height<ADAPTATION_SIZE)) {
                } else
#endif
                {
                        StrikeInfo2D *si;

                        if (!pen) {
                                pen = visual->raster_brush;
                                raster->stencil_set_brush_color(pen, ctx->aspect.line_color);
                        }

                        si = drawable_get_strikeinfo(visual->compositor, ctx->drawable, &ctx->aspect, ctx->appear, path, ctx->flags, NULL);
                        if (si && si->outline) {
                                if (ctx->aspect.line_texture) {
                                        visual_2d_texture_path_extended(visual, si->outline, ctx->aspect.line_texture, ctx, orig_bounds, ext_mx, tr_state);
                                } else {
                                        raster->surface_set_path(visual->raster_surface, si->outline);
                                        visual_2d_fill_path(visual, ctx, pen, tr_state, 0);
                                }
                                /*that's ugly, but we cannot cache path outline for IFS2D/ILS2D*/
                                if (path && !(ctx->flags & CTX_IS_TEXT) && (path!=ctx->drawable->path) ) {
                                        gf_path_del(si->outline);
                                        si->outline = NULL;
                                }
                        }
//                      drawable_reset_path_outline(ctx->drawable);
                }
        }

        if (visual->compositor->draw_bvol) draw_clipper(visual, ctx);
}

void visual_2d_draw_path(GF_VisualManager *visual, GF_Path *path, DrawableContext *ctx, GF_STENCIL brush, GF_STENCIL pen, GF_TraverseState *tr_state)
{
        visual_2d_draw_path_extended(visual, path, ctx, brush, pen, tr_state, NULL, NULL, GF_FALSE);
}

void visual_2d_fill_rect(GF_VisualManager *visual, DrawableContext *ctx, GF_Rect *_rc, u32 color, u32 strike_color, GF_TraverseState *tr_state)
{
        GF_Path *path;
        GF_Rect *rc;
        GF_Raster2D *raster = visual->compositor->rasterizer;
#ifdef SKIP_DRAW
        return;
#endif

        if (! visual->CheckAttached(visual) ) return;

        if (!color && !strike_color) return;

        if ((ctx->flags & CTX_PATH_FILLED) && (ctx->flags & CTX_PATH_STROKE) ) {
                if (visual->compositor->draw_bvol) draw_clipper(visual, ctx);
                return;
        }

        /*no aa*/
        visual_2d_set_options(visual->compositor, visual->raster_surface, 0, 1);
        if (_rc) {
                rc = _rc;
                raster->surface_set_matrix(visual->raster_surface, &ctx->transform);
        }
        else {
                rc = &ctx->bi->unclip;
                raster->surface_set_matrix(visual->raster_surface, NULL);
        }

        path = gf_path_new();
        gf_path_add_move_to(path, rc->x, rc->y-rc->height);
        gf_path_add_line_to(path, rc->x+rc->width, rc->y-rc->height);
        gf_path_add_line_to(path, rc->x+rc->width, rc->y);
        gf_path_add_line_to(path, rc->x, rc->y);
        gf_path_close(path);


        if (color) {
                /*push path*/
                raster->surface_set_path(visual->raster_surface, path);
                raster->stencil_set_brush_color(visual->raster_brush, color);
                visual_2d_fill_path(visual, ctx, visual->raster_brush, tr_state, 0);
                raster->surface_set_path(visual->raster_surface, NULL);
        }
        if (strike_color) {
                GF_Path *outline;
                GF_PenSettings pen;
                memset(&pen, 0, sizeof(GF_PenSettings));
                pen.width = 1;
                pen.join = GF_LINE_JOIN_BEVEL;
                pen.dash = GF_DASH_STYLE_DOT;
                raster->stencil_set_brush_color(visual->raster_brush, strike_color);
                outline = gf_path_get_outline(path,  pen);
                outline->flags &= ~GF_PATH_FILL_ZERO_NONZERO;
                raster->surface_set_path(visual->raster_surface, outline);
                visual_2d_fill_path(visual, ctx, visual->raster_brush, tr_state, 0);
                raster->surface_set_path(visual->raster_surface, NULL);
                gf_path_del(outline);
        }

        gf_path_del(path);
}

void visual_2d_fill_irect(GF_VisualManager *visual, GF_IRect *rc, u32 fill, u32 strike)
{
        GF_Path *path;
        GF_Path *outline;
        GF_PenSettings pen;
        GF_Raster2D *raster = visual->compositor->rasterizer;
#ifdef SKIP_DRAW
        return;
#endif

        if (!rc) return;

        if (! visual->CheckAttached(visual) ) return;

        if (!fill && !strike ) return;

        /*no aa*/
        visual_2d_set_options(visual->compositor, visual->raster_surface, 0, 1);
        raster->surface_set_matrix(visual->raster_surface, NULL);

        raster->surface_set_raster_level(visual->raster_surface, GF_RASTER_HIGH_SPEED);
        raster->surface_set_matrix(visual->raster_surface, NULL);

        path = gf_path_new();
        gf_path_add_move_to(path, INT2FIX(rc->x-1), INT2FIX(rc->y+2-rc->height));
        gf_path_add_line_to(path, INT2FIX(rc->x+rc->width-2), INT2FIX(rc->y+2-rc->height));
        gf_path_add_line_to(path, INT2FIX(rc->x+rc->width), INT2FIX(rc->y));
        gf_path_add_line_to(path, INT2FIX(rc->x), INT2FIX(rc->y));
        gf_path_close(path);

        if (fill) {
                raster->surface_set_path(visual->raster_surface, path);
                raster->stencil_set_brush_color(visual->raster_brush, fill);

                raster->surface_set_clipper(visual->raster_surface, rc);
                raster->surface_fill(visual->raster_surface, visual->raster_brush);

                raster->surface_set_path(visual->raster_surface, NULL);
        }

        if (strike) {
                memset(&pen, 0, sizeof(GF_PenSettings));
                pen.width = 2;
                pen.align = GF_PATH_LINE_INSIDE;
                pen.join = GF_LINE_JOIN_BEVEL;
                outline = gf_path_get_outline(path, pen);
                outline->flags &= ~GF_PATH_FILL_ZERO_NONZERO;

                raster->surface_set_path(visual->raster_surface, outline);
                raster->stencil_set_brush_color(visual->raster_brush, strike);

                raster->surface_set_clipper(visual->raster_surface, rc);
                raster->surface_fill(visual->raster_surface, visual->raster_brush);

                raster->surface_set_path(visual->raster_surface, NULL);
                gf_path_del(outline);
        }
        gf_path_del(path);
#ifndef GPAC_DISABLE_3D
        if (!visual->offscreen && visual->compositor->hybrid_opengl)
                ra_union_rect(&visual->hybgl_drawn, rc);
#endif
}

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