root/src/compositor/mpeg4_gradients.c

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

DEFINITIONS

This source file includes following definitions.
  1. GradientGetMatrix
  2. DestroyGradient
  3. UpdateLinearGradient
  4. LG_ComputeMatrix
  5. BuildLinearGradientTexture
  6. compositor_init_linear_gradient
  7. BuildRadialGradientTexture
  8. UpdateRadialGradient
  9. RG_ComputeMatrix
  10. compositor_init_radial_gradient
  11. compositor_mpeg4_get_gradient_texture
  12. compositor_gradient_update

/*
 *                      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 "texturing.h"

#ifndef GPAC_DISABLE_VRML


#define GRAD_TEXTURE_SIZE       128
#define GRAD_TEXTURE_HSIZE      64

enum
{
        GF_SR_TEXTURE_GRAD_REGISTERED = 1<<6,
        GF_SR_TEXTURE_GRAD_NO_RGB = 1<<7,
};

/*linear/radial gradient*/
typedef struct
{
        GF_TextureHandler txh;
        char *tx_data;
//      Bool no_rgb_support;
} GradientStack;

void GradientGetMatrix(GF_Node *transform, GF_Matrix2D *mat)
{
        gf_mx2d_init(*mat);
        if (transform) {
                switch (gf_node_get_tag(transform) ) {
                case TAG_MPEG4_Transform2D:
                {
                        M_Transform2D *tr = (M_Transform2D *)transform;
                        gf_mx2d_add_scale_at(mat, 0, 0, tr->scale.x, tr->scale.y, tr->scaleOrientation);
                        gf_mx2d_add_rotation(mat, tr->center.x, tr->center.y, tr->rotationAngle);
                        gf_mx2d_add_translation(mat, tr->translation.x, tr->translation.y);
                }
                break;
                case TAG_MPEG4_TransformMatrix2D:
                {
                        M_TransformMatrix2D *tm = (M_TransformMatrix2D*)transform;
                        gf_mx2d_init(*mat);
                        mat->m[0] = tm->mxx;
                        mat->m[1] = tm->mxy;
                        mat->m[2] = tm->tx;
                        mat->m[3] = tm->myx;
                        mat->m[4] = tm->myy;
                        mat->m[5] = tm->ty;
                }
                break;
                default:
                        break;
                }
        }
}

static void DestroyGradient(GF_Node *node, void *rs, Bool is_destroy)
{
        if (is_destroy) {
                GradientStack *st = (GradientStack *) gf_node_get_private(node);
                gf_sc_texture_destroy(&st->txh);
                if (st->tx_data) gf_free(st->tx_data);
                gf_free(st);
        }
}

static void UpdateLinearGradient(GF_TextureHandler *txh)
{
        u32 i, *cols;
        Fixed a;
        Bool const_a;
        GF_STENCIL stencil;
        M_LinearGradient *lg = (M_LinearGradient *) txh->owner;
        GradientStack *st = (GradientStack *) gf_node_get_private(txh->owner);

        if (!gf_node_dirty_get(txh->owner)) {
                txh->needs_refresh = 0;
                return;
        }
        if (lg->key.count > lg->keyValue.count) return;

        if (!txh->tx_io) {
                gf_node_dirty_set(gf_node_get_parent(txh->owner, 0), 0, 1);
                gf_node_dirty_set(txh->owner, 0, 1);
                gf_sc_texture_allocate(txh);
        }

        stencil = gf_sc_texture_get_stencil(txh);
        if (!stencil) stencil = txh->compositor->rasterizer->stencil_new(txh->compositor->rasterizer, GF_STENCIL_LINEAR_GRADIENT);
        /*set stencil even if assigned, this invalidates the associated bitmap state in 3D*/
        gf_sc_texture_set_stencil(txh, stencil);

        gf_node_dirty_clear(txh->owner, 0);
        txh->needs_refresh = 1;

        st->txh.transparent = 0;
        const_a = (lg->opacity.count == 1) ? 1 : 0;
        cols = (u32*)gf_malloc(sizeof(u32) * lg->key.count);
        for (i=0; i<lg->key.count; i++) {
                a = (const_a ? lg->opacity.vals[0] : lg->opacity.vals[i]);
                cols[i] = GF_COL_ARGB_FIXED(a, lg->keyValue.vals[i].red, lg->keyValue.vals[i].green, lg->keyValue.vals[i].blue);
                if (a != FIX_ONE) txh->transparent = 1;
        }
        txh->compositor->rasterizer->stencil_set_gradient_interpolation(stencil, lg->key.vals, cols, lg->key.count);
        gf_free(cols);
        txh->compositor->rasterizer->stencil_set_gradient_mode(stencil, (GF_GradientMode) lg->spreadMethod);
}


static void LG_ComputeMatrix(GF_TextureHandler *txh, GF_Rect *bounds, GF_Matrix2D *mat, Bool for_3d)
{
        GF_STENCIL stencil;
        M_LinearGradient *lg = (M_LinearGradient *) txh->owner;

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

        if (lg->key.count<2) return;
        if (lg->key.count != lg->keyValue.count) return;

        /*create gradient brush if needed*/
        if (!txh->tx_io) return;

        GradientGetMatrix((GF_Node *) lg->transform, mat);

        /*translate to the center of the bounds*/
        gf_mx2d_add_translation(mat, gf_divfix(bounds->x, bounds->width), gf_divfix(bounds->y - bounds->height, bounds->height));
        /*scale back to object coordinates - the gradient is still specified in texture coordinates
        i order to avoid overflows in fixed point*/
        gf_mx2d_add_scale(mat, bounds->width, bounds->height);

        txh->compositor->rasterizer->stencil_set_linear_gradient(stencil, lg->startPoint.x, lg->startPoint.y, lg->endPoint.x, lg->endPoint.y);
}

//TODO - replace this with shader-based code ...

static void BuildLinearGradientTexture(GF_TextureHandler *txh)
{
        u32 i;
        SFVec2f start, end;
        u32 *cols;
        Fixed a;
        Bool const_a;
        GF_Matrix2D mat;
        GF_STENCIL stenc;
        GF_SURFACE surface;
        GF_STENCIL texture2D;
        GF_Path *path;
        GF_Err e;
        Bool transparent;
        u32 pix_fmt = 0;
        M_LinearGradient *lg = (M_LinearGradient *) txh->owner;
        GradientStack *st = (GradientStack *) gf_node_get_private(txh->owner);
        GF_Raster2D *raster = txh->compositor->rasterizer;

        if (!txh->tx_io) return;

        if (!(txh->flags & GF_SR_TEXTURE_GRAD_REGISTERED)) {
                txh->flags |= GF_SR_TEXTURE_GRAD_REGISTERED;
                if (gf_list_find(txh->compositor->textures, txh)<0)
                        gf_list_insert(txh->compositor->textures, txh, 0);
        }

        if (lg->key.count<2) return;
        if (lg->key.count != lg->keyValue.count) return;

        start = lg->startPoint;
        end = lg->endPoint;

        transparent = (lg->opacity.count==1) ? (lg->opacity.vals[0]!=FIX_ONE) : 1;

        /*init our 2D graphics stuff*/
        texture2D = raster->stencil_new(raster, GF_STENCIL_TEXTURE);
        if (!texture2D) return;
        surface = raster->surface_new(raster, 1);
        if (!surface) {
                raster->stencil_delete(texture2D);
                return;
        }

        if (st->txh.flags & GF_SR_TEXTURE_GRAD_NO_RGB) transparent = 1;

        if (st->tx_data && (st->txh.transparent != transparent)) {
                gf_free(st->tx_data);
                st->tx_data = NULL;
        }

        if (transparent) {
                if (!st->tx_data) {
                        st->tx_data = (char *) gf_malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*4);
                }
                memset(st->tx_data, 0, sizeof(char)*txh->stride*txh->height);

                pix_fmt = GF_PIXEL_RGBA;
                e = raster->stencil_set_texture(texture2D, st->tx_data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 4*GRAD_TEXTURE_SIZE, pix_fmt, pix_fmt, 1);
                if (e) {
                        pix_fmt = GF_PIXEL_ARGB;
                        e = raster->stencil_set_texture(texture2D, st->tx_data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 4*GRAD_TEXTURE_SIZE, pix_fmt, pix_fmt, 1);
                }
        } else {
                if (!st->tx_data) {
                        st->tx_data = (char *) gf_malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*3);
                }
                e = raster->stencil_set_texture(texture2D, st->tx_data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 3*GRAD_TEXTURE_SIZE, GF_PIXEL_RGB_24, GF_PIXEL_RGB_24, 1);
                /*try with ARGB (it actually is needed for GDIplus module since GDIplus cannot handle native RGB texture (it works in BGR)*/
                if (e) {
                        /*remember for later use*/
                        st->txh.flags |= GF_SR_TEXTURE_GRAD_NO_RGB;
                        transparent = 1;
                        gf_free(st->tx_data);
                        st->tx_data = (char *) gf_malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*4);
                        e = raster->stencil_set_texture(texture2D, st->tx_data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 4*GRAD_TEXTURE_SIZE, GF_PIXEL_ARGB, GF_PIXEL_ARGB, 1);
                }
        }
        st->txh.transparent = transparent;

        if (e) {
                gf_free(st->tx_data);
                raster->stencil_delete(texture2D);
                raster->surface_delete(surface);
                return;
        }
        e = raster->surface_attach_to_texture(surface, texture2D);
        if (e) {
                raster->stencil_delete(texture2D);
                raster->surface_delete(surface);
                return;
        }

        /*create & setup gradient*/
        stenc = raster->stencil_new(raster, GF_STENCIL_LINEAR_GRADIENT);
        if (!stenc) {
                raster->stencil_delete(texture2D);
                raster->surface_delete(surface);
                return;
        }
        /*move line to object space*/
        start.x *= GRAD_TEXTURE_SIZE;
        end.x *= GRAD_TEXTURE_SIZE;
        start.y *= GRAD_TEXTURE_SIZE;
        end.y *= GRAD_TEXTURE_SIZE;
        raster->stencil_set_linear_gradient(stenc, start.x, start.y, end.x, end.y);
        const_a = (lg->opacity.count == 1) ? 1 : 0;
        cols = (u32*)gf_malloc(sizeof(u32) * lg->key.count);
        for (i=0; i<lg->key.count; i++) {
                a = (const_a ? lg->opacity.vals[0] : lg->opacity.vals[i]);
                cols[i] = GF_COL_ARGB_FIXED(a, lg->keyValue.vals[i].red, lg->keyValue.vals[i].green, lg->keyValue.vals[i].blue);
        }
        raster->stencil_set_gradient_interpolation(stenc, lg->key.vals, cols, lg->key.count);
        gf_free(cols);
        raster->stencil_set_gradient_mode(stenc, (GF_GradientMode)lg->spreadMethod);

        /*fill surface*/
        path = gf_path_new();
        gf_path_add_move_to(path, -INT2FIX(GRAD_TEXTURE_HSIZE), -INT2FIX(GRAD_TEXTURE_HSIZE));
        gf_path_add_line_to(path, INT2FIX(GRAD_TEXTURE_HSIZE), -INT2FIX(GRAD_TEXTURE_HSIZE));
        gf_path_add_line_to(path, INT2FIX(GRAD_TEXTURE_HSIZE), INT2FIX(GRAD_TEXTURE_HSIZE));
        gf_path_add_line_to(path, -INT2FIX(GRAD_TEXTURE_HSIZE), INT2FIX(GRAD_TEXTURE_HSIZE));
        gf_path_close(path);

        /*add gradient transform*/
        GradientGetMatrix(lg->transform, &mat);

        /*move transform to object space*/
        mat.m[2] *= GRAD_TEXTURE_SIZE;
        mat.m[5] *= GRAD_TEXTURE_SIZE;
        /*translate to the center of the bounds*/
        gf_mx2d_add_translation(&mat, -INT2FIX(GRAD_TEXTURE_HSIZE), -INT2FIX(GRAD_TEXTURE_HSIZE));
        /*back to GL bottom->up order*/
        gf_mx2d_add_scale(&mat, FIX_ONE, -FIX_ONE);
        raster->stencil_set_matrix(stenc, &mat);

        raster->surface_set_raster_level(surface, GF_RASTER_HIGH_QUALITY);
        raster->surface_set_path(surface, path);
        raster->surface_fill(surface, stenc);
        raster->stencil_delete(stenc);
        raster->surface_delete(surface);
        raster->stencil_delete(texture2D);
        gf_path_del(path);

        txh->data = st->tx_data;
        txh->width = GRAD_TEXTURE_SIZE;
        txh->height = GRAD_TEXTURE_SIZE;
        txh->transparent = transparent;
        if (transparent) {
                u32 j;
                txh->stride = GRAD_TEXTURE_SIZE*4;
                txh->pixelformat = GF_PIXEL_RGBA;

                /*back to RGBA texturing*/
                if (pix_fmt != GF_PIXEL_RGBA) {
                        for (i=0; i<txh->height; i++) {
                                char *data = txh->data + i*txh->stride;
                                for (j=0; j<txh->width; j++) {
                                        u32 val = *(u32 *) &data[4*j];
                                        data[4*j] = (val>>16) & 0xFF;
                                        data[4*j+1] = (val>>8) & 0xFF;
                                        data[4*j+2] = (val) & 0xFF;
                                        data[4*j+3] = (val>>24) & 0xFF;
                                }
                        }
                }
        } else {
                txh->stride = GRAD_TEXTURE_SIZE*3;
                txh->pixelformat = GF_PIXEL_RGB_24;
        }
        txh->flags |= GF_SR_TEXTURE_NO_GL_FLIP;
        gf_sc_texture_set_data(txh);
}

void compositor_init_linear_gradient(GF_Compositor *compositor, GF_Node *node)
{
        GradientStack *st;
        GF_SAFEALLOC(st, GradientStack);
        if (!st) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate gradient stack\n"));
                return;
        }

        /*!!! Gradients are textures but are not registered as textures with the compositor in order to avoid updating
        too many textures each frame - gradients are only registered with the compositor when they are used in OpenGL, in order
        to release associated HW resource when no longer used*/
        st->txh.owner = node;
        st->txh.compositor = compositor;
        st->txh.update_texture_fcnt = UpdateLinearGradient;
        st->txh.compute_gradient_matrix = LG_ComputeMatrix;

        gf_node_set_private(node, st);
        gf_node_set_callback_function(node, DestroyGradient);
}


static void BuildRadialGradientTexture(GF_TextureHandler *txh)
{
        u32 i;
        SFVec2f center, focal;
        u32 *cols;
        Fixed a, radius;
        Bool const_a;
        GF_Matrix2D mat;
        GF_STENCIL stenc;
        GF_SURFACE surface;
        GF_STENCIL texture2D;
        GF_Path *path;
        GF_Err e;
        u32 pix_fmt = 0;
        Bool transparent;
        M_RadialGradient *rg = (M_RadialGradient*) txh->owner;
        GradientStack *st = (GradientStack *) gf_node_get_private(txh->owner);
        GF_Raster2D *raster = txh->compositor->rasterizer;


        if (!txh->tx_io) return;

        if (!(txh->flags & GF_SR_TEXTURE_GRAD_REGISTERED)) {
                txh->flags |= GF_SR_TEXTURE_GRAD_REGISTERED;
                if (gf_list_find(txh->compositor->textures, txh)<0)
                        gf_list_insert(txh->compositor->textures, txh, 0);
        }

        if (rg->key.count<2) return;
        if (rg->key.count != rg->keyValue.count) return;

        transparent = (rg->opacity.count==1) ? ((rg->opacity.vals[0]!=FIX_ONE) ? 1 : 0) : 1;

        /*init our 2D graphics stuff*/
        texture2D = raster->stencil_new(raster, GF_STENCIL_TEXTURE);
        if (!texture2D) return;
        surface = raster->surface_new(raster, 1);
        if (!surface) {
                raster->stencil_delete(texture2D);
                return;
        }

        if (st->txh.flags & GF_SR_TEXTURE_GRAD_NO_RGB) transparent = 1;

        if (st->tx_data && (st->txh.transparent != transparent)) {
                gf_free(st->tx_data);
                st->tx_data = NULL;
        }

        if (transparent) {
                if (!st->tx_data) {
                        st->tx_data = (char *) gf_malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*4);
                }
                memset(st->tx_data, 0, sizeof(char)*txh->stride*txh->height);

                pix_fmt = GF_PIXEL_RGBA;

                e = raster->stencil_set_texture(texture2D, st->tx_data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 4*GRAD_TEXTURE_SIZE, pix_fmt, pix_fmt, 1);

                if (e) {
                        pix_fmt = GF_PIXEL_ARGB;
                        e = raster->stencil_set_texture(texture2D, st->tx_data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 4*GRAD_TEXTURE_SIZE, pix_fmt, pix_fmt, 1);
                }
        } else {
                if (!st->tx_data) {
                        st->tx_data = (char *) gf_malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*3);
                }
                e = raster->stencil_set_texture(texture2D, st->tx_data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 3*GRAD_TEXTURE_SIZE, GF_PIXEL_RGB_24, GF_PIXEL_RGB_24, 1);
                /*try with ARGB (it actually is needed for GDIplus module since GDIplus cannot handle native RGB texture (it works in BGR)*/
                if (e) {
                        /*remember for later use*/
                        st->txh.flags |= GF_SR_TEXTURE_GRAD_NO_RGB;
                        transparent = 1;
                        gf_free(st->tx_data);
                        st->tx_data = (char *) gf_malloc(sizeof(char)*GRAD_TEXTURE_SIZE*GRAD_TEXTURE_SIZE*4);
                        e = raster->stencil_set_texture(texture2D, st->tx_data, GRAD_TEXTURE_SIZE, GRAD_TEXTURE_SIZE, 4*GRAD_TEXTURE_SIZE, GF_PIXEL_ARGB, GF_PIXEL_ARGB, 1);
                }
        }
        st->txh.transparent = transparent;

        if (e) {
                gf_free(st->tx_data);
                raster->stencil_delete(texture2D);
                raster->surface_delete(surface);
                return;
        }
        e = raster->surface_attach_to_texture(surface, texture2D);
        if (e) {
                raster->stencil_delete(texture2D);
                raster->surface_delete(surface);
                return;
        }

        /*create & setup gradient*/
        stenc = raster->stencil_new(raster, GF_STENCIL_RADIAL_GRADIENT);
        if (!stenc) {
                raster->stencil_delete(texture2D);
                raster->surface_delete(surface);
        }

        center = rg->center;
        focal = rg->focalPoint;
        radius = rg->radius;

        /*move circle to object space*/
        center.x *= GRAD_TEXTURE_SIZE;
        center.y *= GRAD_TEXTURE_SIZE;
        focal.x *= GRAD_TEXTURE_SIZE;
        focal.y *= GRAD_TEXTURE_SIZE;
        radius *= GRAD_TEXTURE_SIZE;

        raster->stencil_set_radial_gradient(stenc, center.x, center.y, focal.x, focal.y, radius, radius);

        const_a = (rg->opacity.count == 1) ? 1 : 0;
        cols = (u32*) gf_malloc(sizeof(u32) * rg->key.count);
        for (i=0; i<rg->key.count; i++) {
                a = (const_a ? rg->opacity.vals[0] : rg->opacity.vals[i]);
                cols[i] = GF_COL_ARGB_FIXED(a, rg->keyValue.vals[i].red, rg->keyValue.vals[i].green, rg->keyValue.vals[i].blue);
        }
        raster->stencil_set_gradient_interpolation(stenc, rg->key.vals, cols, rg->key.count);
        gf_free(cols);
        raster->stencil_set_gradient_mode(stenc, (GF_GradientMode)rg->spreadMethod);

        /*fill surface*/
        path = gf_path_new();
        gf_path_add_move_to(path, -INT2FIX(GRAD_TEXTURE_HSIZE), -INT2FIX(GRAD_TEXTURE_HSIZE));
        gf_path_add_line_to(path, INT2FIX(GRAD_TEXTURE_HSIZE), -INT2FIX(GRAD_TEXTURE_HSIZE));
        gf_path_add_line_to(path, INT2FIX(GRAD_TEXTURE_HSIZE), INT2FIX(GRAD_TEXTURE_HSIZE));
        gf_path_add_line_to(path, -INT2FIX(GRAD_TEXTURE_HSIZE), INT2FIX(GRAD_TEXTURE_HSIZE));
        gf_path_close(path);

        /*add gradient transform*/
        GradientGetMatrix(rg->transform, &mat);
        /*move transform to object space*/
        mat.m[2] *= GRAD_TEXTURE_SIZE;
        mat.m[5] *= GRAD_TEXTURE_SIZE;
        /*translate to the center of the bounds*/
        gf_mx2d_add_translation(&mat, -INT2FIX(GRAD_TEXTURE_HSIZE), -INT2FIX(GRAD_TEXTURE_HSIZE));
        /*back to GL bottom->up order*/
        gf_mx2d_add_scale(&mat, FIX_ONE, -FIX_ONE);
        raster->stencil_set_matrix(stenc, &mat);

        raster->surface_set_raster_level(surface, GF_RASTER_HIGH_QUALITY);
        raster->surface_set_path(surface, path);
        raster->surface_fill(surface, stenc);
        raster->stencil_delete(stenc);
        raster->surface_delete(surface);
        raster->stencil_delete(texture2D);
        gf_path_del(path);

        txh->data = st->tx_data;
        txh->width = GRAD_TEXTURE_SIZE;
        txh->height = GRAD_TEXTURE_SIZE;
        txh->transparent = transparent;
        if (transparent) {
                u32 j;
                txh->stride = GRAD_TEXTURE_SIZE*4;
                txh->pixelformat = GF_PIXEL_RGBA;

                /*back to RGBA texturing*/
                if (pix_fmt == GF_PIXEL_ARGB) {
                        for (i=0; i<txh->height; i++) {
                                char *data = txh->data + i*txh->stride;
                                for (j=0; j<txh->width; j++) {
                                        u32 val = *(u32 *) &data[4*j];
                                        data[4*j] = (val>>16) & 0xFF;
                                        data[4*j+1] = (val>>8) & 0xFF;
                                        data[4*j+2] = (val) & 0xFF;
                                        data[4*j+3] = (val>>24) & 0xFF;
                                }
                        }
                }
        } else {
                txh->stride = GRAD_TEXTURE_SIZE*3;
                txh->pixelformat = GF_PIXEL_RGB_24;
        }
//      tx_set_blend_enable(txh, 1);
        txh->flags |= GF_SR_TEXTURE_NO_GL_FLIP;
        gf_sc_texture_set_data(txh);
        return;
}

static void UpdateRadialGradient(GF_TextureHandler *txh)
{
        Bool const_a;
        GF_STENCIL stencil;
        u32 i, *cols;
        Fixed a;
        M_RadialGradient *rg = (M_RadialGradient*) txh->owner;
        GradientStack *st = (GradientStack *) gf_node_get_private(txh->owner);

        if (!gf_node_dirty_get(txh->owner)) {
                txh->needs_refresh = 0;
                return;
        }
        if (rg->key.count > rg->keyValue.count) return;

        if (!txh->tx_io) gf_sc_texture_allocate(txh);

        stencil = gf_sc_texture_get_stencil(txh);
        if (!stencil)stencil = txh->compositor->rasterizer->stencil_new(txh->compositor->rasterizer, GF_STENCIL_RADIAL_GRADIENT);
        /*set stencil even if assigned, this invalidates the associated bitmap state in 3D*/
        gf_sc_texture_set_stencil(txh, stencil);

        gf_node_dirty_clear(txh->owner, 0);
        txh->needs_refresh = 1;

        st->txh.transparent = 0;
        for (i=0; i<rg->opacity.count; i++) {
                if (rg->opacity.vals[i] != FIX_ONE) {
                        st->txh.transparent = 1;
                        break;
                }
        }

        const_a = (rg->opacity.count == 1) ? 1 : 0;
        cols = (u32*)gf_malloc(sizeof(u32) * rg->key.count);
        for (i=0; i<rg->key.count; i++) {
                a = (const_a ? rg->opacity.vals[0] : rg->opacity.vals[i]);
                cols[i] = GF_COL_ARGB_FIXED(a, rg->keyValue.vals[i].red, rg->keyValue.vals[i].green, rg->keyValue.vals[i].blue);
        }
        txh->compositor->rasterizer->stencil_set_gradient_interpolation(stencil, rg->key.vals, cols, rg->key.count);
        gf_free(cols);

        txh->compositor->rasterizer->stencil_set_gradient_mode(stencil, (GF_GradientMode) rg->spreadMethod);

}

static void RG_ComputeMatrix(GF_TextureHandler *txh, GF_Rect *bounds, GF_Matrix2D *mat, Bool for_3d)
{
        GF_STENCIL stencil;
        M_RadialGradient *rg = (M_RadialGradient *) txh->owner;

        if (rg->key.count<2) return;
        if (rg->key.count != rg->keyValue.count) return;

        if (!txh->tx_io) return;

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

        GradientGetMatrix((GF_Node *) rg->transform, mat);

        txh->compositor->rasterizer->stencil_set_radial_gradient(stencil, rg->center.x, rg->center.y, rg->focalPoint.x, rg->focalPoint.y, rg->radius, rg->radius);
        /*move to center of bounds*/
        gf_mx2d_add_translation(mat, gf_divfix(bounds->x, bounds->width), gf_divfix(bounds->y - bounds->height, bounds->height));
        /*scale back to object coordinates - the gradient is still specified in texture coordinates
        i order to avoid overflows in fixed point*/
        gf_mx2d_add_scale(mat, bounds->width, bounds->height);
}


void compositor_init_radial_gradient(GF_Compositor *compositor, GF_Node *node)
{
        GradientStack *st;
        GF_SAFEALLOC(st, GradientStack);
        if (!st) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate gradient stack\n"));
                return;
        }

        /*!!! Gradients are textures but are not registered as textures with the compositor in order to avoid updating
        too many textures each frame - gradients are only registered with the compositor when they are used in OpenGL, in order
        to release associated HW resource when no longer used*/
        st->txh.owner = node;
        st->txh.compositor = compositor;
        st->txh.update_texture_fcnt = UpdateRadialGradient;
        st->txh.compute_gradient_matrix = RG_ComputeMatrix;

        gf_node_set_private(node, st);
        gf_node_set_callback_function(node, DestroyGradient);
}

GF_TextureHandler *compositor_mpeg4_get_gradient_texture(GF_Node *node)
{
        GradientStack *st = (GradientStack*) gf_node_get_private(node);
        st->txh.update_texture_fcnt(&st->txh);
        return &st->txh;
}

#endif /*GPAC_DISABLE_VRML*/

void compositor_gradient_update(GF_TextureHandler *txh)
{
        switch (gf_node_get_tag(txh->owner) ) {
#ifndef GPAC_DISABLE_VRML
        case TAG_MPEG4_RadialGradient:
                BuildRadialGradientTexture(txh);
                break;
        case TAG_MPEG4_LinearGradient:
                BuildLinearGradientTexture(txh);
                break;
#endif

#ifndef GPAC_DISABLE_SVG
        case TAG_SVG_linearGradient:
        case TAG_SVG_radialGradient:
                compositor_svg_build_gradient_texture(txh);
                break;
#endif
        }
}


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