m3g/m3gcore11/src/m3g_background.c
author hgs
Fri, 24 Sep 2010 16:14:28 +0300
changeset 187 9f66f99ee56f
parent 0 5d03bc08d59c
permissions -rw-r--r--
201026

/*
* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: Background implementation
*
*/


/*!
 * \internal
 * \file
 * \brief Background implementation
 */

#ifndef M3G_CORE_INCLUDE
#   error included by m3g_core.c; do not compile separately.
#endif

#include "m3g_background.h"
#include "m3g_image.h"
#include "m3g_memory.h"
#include "m3g_animationtrack.h"
#include "m3g_rendercontext.h"

/*----------------------------------------------------------------------
 * Internal functions
 *--------------------------------------------------------------------*/

/*!
 * \internal
 * \brief Destroys this Background object.
 *
 * \param obj Background object
 */
static void m3gDestroyBackground(Object *obj)
{
    Background *background = (Background *) obj;
    M3G_VALIDATE_OBJECT(background);

    M3G_ASSIGN_REF(background->image, NULL);
    m3gDestroyObject(obj);
}

/*!
 * \internal
 * \brief Applies background color and image
 * using a textured quad.
 *
 * \param ctx           render context
 * \param background    Background object
 */
static void m3gApplyBackground(RenderContext *ctx, Background *background)
{
    GLbitfield glBits = 0;
    GLfixed temp[4];
    
    if (background->depthClearEnable) {
        glBits |= GL_DEPTH_BUFFER_BIT;
    }

    /* Skip color buffer clearing if the background image
     * fills the whole viewport. This is guaranteed to happen
     * if the crop rectangle is non-zero and both X and Y
     * wrapping modes are REPEAT. */
    
    if (background->colorClearEnable) {
        if (background->image == NULL ||
            background->crop.width == 0 ||
            background->crop.height == 0 ||
            background->modeX == M3G_BORDER ||
            background->modeY == M3G_BORDER)
        {
            glBits |= GL_COLOR_BUFFER_BIT;
            m3gGLColor(background->color, temp);
            glClearColorx(temp[0], temp[1], temp[2], temp[3]);
        }
    }
    
    /* Clear color and/or depth buffer (or neither) */
    glClear(glBits);

    /* Apply background image using a quad that
       fills the viewport */

    if (background->colorClearEnable &&
        background->image != NULL &&
        background->crop.width != 0 && 
        background->crop.height != 0)
    {
        {
            /* Texture coordinates */
            M3Gshort texvert[4 * 2];
            /* Quad that fills the viewport */
            M3Gint vert[4 * 3]   = { -65536,  65536, 0,
                                     -65536, -65536, 0,
                                      65536,  65536, 0,
                                      65536, -65536, 0 };
            Rect rImage, rIntersection;
            M3Gbool intersects;
            Image *imagePow2;

            /* Get power of two image */
            imagePow2 = m3gGetPowerOfTwoImage(background->image);
            /* If NULL -> out of memory */
            if (!imagePow2) {
                return;
            }

            rImage.x = 0;
            rImage.y = 0;
            rImage.width = m3gGetWidth(background->image);
            rImage.height = m3gGetHeight(background->image);

            /* Intersection of source image and crop rectangle */
            intersects = m3gIntersectRectangle(&rIntersection, &rImage, &background->crop);

            /* Setup X vertices and texture S coordinates */
            if (background->modeX == M3G_BORDER) {
                /* If both modes are border and no intersection ->
                   nothing to draw */
                if (background->modeY == M3G_BORDER && !intersects) {
                    return;
                }

                texvert[0 * 2 + 0] = (M3Gshort) rIntersection.x;
                texvert[1 * 2 + 0] = (M3Gshort) rIntersection.x;
                texvert[2 * 2 + 0] = (M3Gshort) (rIntersection.x + rIntersection.width);
                texvert[3 * 2 + 0] = (M3Gshort) (rIntersection.x + rIntersection.width);

                vert[0 * 3 + 0] = -65536 + 2 * 65536 * (rIntersection.x - background->crop.x) / background->crop.width;
                vert[1 * 3 + 0] = vert[0 * 3 + 0];
                vert[2 * 3 + 0] = vert[0 * 3 + 0] + 2 * 65536 * rIntersection.width / background->crop.width;
                vert[3 * 3 + 0] = vert[2 * 3 + 0];
            }
            else {
                /* In repeat mode texture coordinates are directly crop rectangle coordinates */
                texvert[0 * 2 + 0] = (M3Gshort) background->crop.x;
                texvert[1 * 2 + 0] = (M3Gshort) background->crop.x;
                texvert[2 * 2 + 0] = (M3Gshort) (background->crop.x + background->crop.width);
                texvert[3 * 2 + 0] = (M3Gshort) (background->crop.x + background->crop.width);
            }

            /* Setup Y vertices and texture T coordinates */
            if (background->modeY == M3G_BORDER) {
                texvert[0 * 2 + 1] = (M3Gshort) rIntersection.y;
                texvert[1 * 2 + 1] = (M3Gshort) (rIntersection.y + rIntersection.height);
                texvert[2 * 2 + 1] = (M3Gshort) rIntersection.y;
                texvert[3 * 2 + 1] = (M3Gshort) (rIntersection.y + rIntersection.height);


                vert[0 * 3 + 1] =  65536 - 2 * 65536 * (rIntersection.y - background->crop.y) / background->crop.height;
                vert[1 * 3 + 1] = vert[0 * 3 + 1] - 2 * 65536 * rIntersection.height / background->crop.height;
                vert[2 * 3 + 1] = vert[0 * 3 + 1];
                vert[3 * 3 + 1] = vert[1 * 3 + 1];
            }
            else {
                /* In repeat mode texture coordinates are directly crop rectangle coordinates */
                texvert[0 * 2 + 1] = (M3Gshort) background->crop.y;
                texvert[1 * 2 + 1] = (M3Gshort) (background->crop.y + background->crop.height);
                texvert[2 * 2 + 1] = (M3Gshort) background->crop.y;
                texvert[3 * 2 + 1] = (M3Gshort) (background->crop.y + background->crop.height);
            }

            /* Disable unwanted state and depth writes */
            m3gApplyAppearance(NULL, ctx, 0);
            glDepthMask(GL_FALSE);

            /* Disable color array, normals and textures*/
            glDisableClientState(GL_COLOR_ARRAY);
            glDisableClientState(GL_NORMAL_ARRAY);
        
            /* Background image to texture unit 0 */
            glClientActiveTexture(GL_TEXTURE0);
            glActiveTexture(GL_TEXTURE0);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            glTexCoordPointer(2, GL_SHORT, 0, texvert);
            glEnable(GL_TEXTURE_2D);
            glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, (GLfixed) GL_REPLACE);
            m3gBindTextureImage(imagePow2,
                                M3G_FILTER_BASE_LEVEL,
                                m3gIsAccelerated(ctx) ? M3G_FILTER_LINEAR : M3G_FILTER_NEAREST);

            /* Set wrapping */
            if (background->modeX == M3G_REPEAT) {
                glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
            }
            else {
                glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            }
    
            if (background->modeY == M3G_REPEAT) {
                glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
            }
            else {
                glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            }

            /* Texture matrix scale */
            glMatrixMode(GL_TEXTURE);
            glLoadIdentity();
            glScalef(   m3gRcp((M3Gfloat)m3gGetWidth(background->image)),
                        m3gRcp((M3Gfloat)m3gGetHeight(background->image)),
                        1.f);
            glMatrixMode(GL_MODELVIEW);

            /* Load vertices */
            glEnableClientState(GL_VERTEX_ARRAY);
            glVertexPointer(3, GL_FIXED, 0, vert);
        
            /* Set up an identity modelview and projection */
            m3gPushScreenSpace(ctx, M3G_FALSE);

            /* Load indices -> draws the background */
            M3G_BEGIN_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_NGL_DRAW);
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            M3G_END_PROFILE(M3G_INTERFACE(ctx), M3G_PROFILE_NGL_DRAW);
        
            m3gPopSpace(ctx);
            m3gReleaseTextureImage(imagePow2);
        }
    }
}

/*!
 * \internal
 * \brief Overloaded Object3D method.
 *
 * \param property      animation property
 * \retval M3G_TRUE     property supported
 * \retval M3G_FALSE    property not supported
 */
static M3Gbool m3gBackgroundIsCompatible(M3Gint property)
{
    switch (property) {
    case M3G_ANIM_ALPHA:
    case M3G_ANIM_COLOR:
    case M3G_ANIM_CROP:
        return M3G_TRUE;
    default:
        return m3gObjectIsCompatible(property);
    }
}

/*!
 * \internal
 * \brief Overloaded Object3D method.
 *
 * \param self          Background object
 * \param property      animation property
 * \param valueSize     size of value array
 * \param value         value array
 */
static void m3gBackgroundUpdateProperty(Object *self,
                                        M3Gint property,
                                        M3Gint valueSize,
                                        const M3Gfloat *value)
{
    Background *background = (Background *)self;
    M3G_VALIDATE_OBJECT(background);
    M3G_ASSERT_PTR(value);
    
    switch (property) {
    case M3G_ANIM_ALPHA:
        M3G_ASSERT(valueSize >= 1);
        background->color = m3gAlpha1f(value[0])
            & (background->color | M3G_ALPHA_MASK);
        break;
    case M3G_ANIM_COLOR:
        M3G_ASSERT(valueSize >= 3);
        background->color = m3gColor3f(value[0], value[1], value[2])
            & (background->color | M3G_RGB_MASK);
        break;
    case M3G_ANIM_CROP:
        M3G_ASSERT(valueSize >= 2);
        background->crop.x = m3gRoundToInt(value[0]);
        background->crop.y = m3gRoundToInt(value[1]);
        if (valueSize > 2) {
            M3G_ASSERT(valueSize >= 4);
            background->crop.width =
                (value[2] < 0) ? 0 : m3gRoundToInt(value[2]);
            background->crop.height =
                (value[3] < 0) ? 0 : m3gRoundToInt(value[3]);
        }
        break;
    default:
        m3gObjectUpdateProperty(self, property, valueSize, value);
    }
}

/*!
 * \internal
 * \brief Overloaded Object3D method.
 *
 * \param self Background object
 * \param references array of reference objects
 * \return number of references
 */
static M3Gint m3gBackgroundDoGetReferences(Object *self, Object **references)
{
    Background *bg = (Background *)self;
    M3Gint num = m3gObjectDoGetReferences(self, references);
    if (bg->image != NULL) {
        if (references != NULL)
            references[num] = (Object *)bg->image;
        num++;
    }
    return num;
}

/*!
 * \internal
 * \brief Overloaded Object3D method.
 */
static Object *m3gBackgroundFindID(Object *self, M3Gint userID)
{
    Background *bg = (Background *)self;
    Object *found = m3gObjectFindID(self, userID);

    if (!found && bg->image) {
        found = m3gFindID((Object*) bg->image, userID);
    }
    return found;
}

/*!
 * \internal
 * \brief Overloaded Object3D method.
 *
 * \param originalObj original Background object
 * \param cloneObj pointer to cloned Background object
 * \param pairs array for all object-duplicate pairs
 * \param numPairs number of pairs
 */
static M3Gbool m3gBackgroundDuplicate(const Object *originalObj,
                                      Object **cloneObj,
                                      Object **pairs,
                                      M3Gint *numPairs)
{
    Background *original = (Background *)originalObj;
    Background *clone = (Background *)m3gCreateBackground(originalObj->interface);
    *cloneObj = (Object *)clone;
    if (*cloneObj == NULL) {
        return M3G_FALSE;
    }

    if (m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs)) {
        clone->color = original->color;
        clone->modeX = original->modeX;
        clone->modeY = original->modeY;
        clone->crop = original->crop;
        clone->colorClearEnable = original->colorClearEnable;
        clone->depthClearEnable = original->depthClearEnable;
        M3G_ASSIGN_REF(clone->image, original->image);
        return M3G_TRUE;
    }
    else {
        return M3G_FALSE;
    }
}

/*!
 * \internal
 * \brief Initializes a Background object. See specification
 * for default values.
 *
 * \param m3g           M3G interface
 * \param background    Background object
 */
static void m3gInitBackground(Interface *m3g, Background *background)
{
    /* Background is derived from Object */
    m3gInitObject(&background->object, m3g, M3G_CLASS_BACKGROUND);

    background->modeX = M3G_BORDER; 
    background->modeY = M3G_BORDER; 
    background->colorClearEnable = M3G_TRUE;
    background->depthClearEnable = M3G_TRUE;
}

/*----------------------------------------------------------------------
 * Virtual function table
 *--------------------------------------------------------------------*/

static const ObjectVFTable m3gvf_Background = {
    m3gObjectApplyAnimation,
    m3gBackgroundIsCompatible,
    m3gBackgroundUpdateProperty,
    m3gBackgroundDoGetReferences,
    m3gBackgroundFindID,
    m3gBackgroundDuplicate,
    m3gDestroyBackground
};


/*----------------------------------------------------------------------
 * Public API functions
 *--------------------------------------------------------------------*/

/*!
 * \brief Creates a Background object.
 *
 * \param interface     M3G interface
 * \retval Background new Background object
 * \retval NULL Background creating failed
 */

/*@access M3GInterface@*/
/*@access M3Gobject@*/
M3G_API M3GBackground m3gCreateBackground(M3GInterface interface)
{
    Interface *m3g = (Interface *) interface;
    M3G_VALIDATE_INTERFACE(m3g);

    {
        Background *background =  m3gAllocZ(m3g, sizeof(Background));
    
        if (background != NULL) {
            m3gInitBackground(m3g, background);
        }

        return (M3GBackground) background;
    }
}

/*!
 * \brief Sets background color.
 *
 * \param handle        Background object
 * \param ARGB          background color as ARGB
 */

/*@access M3Gobject@*/
M3G_API void m3gSetBgColor(M3GBackground handle, M3Guint ARGB)
{
    Background *background = (Background *) handle;
    M3G_VALIDATE_OBJECT(background);
    
    background->color = ARGB;
}

/*!
 * \brief Sets background image x and y mode.
 *
 * \param handle        Background object
 * \param modeX         Image X mode
 * \param modeY         Image Y mode
 */

/*@access M3Gobject@*/
M3G_API void m3gSetBgMode(M3GBackground handle, M3Gint modeX, M3Gint modeY)
{
    Background *background = (Background *) handle;
    M3G_VALIDATE_OBJECT(background);
    
    /* Check for errors */
    if (modeX < M3G_BORDER || modeX > M3G_REPEAT ||
        modeY < M3G_BORDER || modeY > M3G_REPEAT) {
        m3gRaiseError(M3G_INTERFACE(background), M3G_INVALID_VALUE);
        return;
    }

    background->modeX = modeX;
    background->modeY = modeY;
}

/*!
 * \brief Sets background image crop rectangle.
 *
 * \param handle        Background object
 * \param cropX         crop upper left x
 * \param cropY         crop upper left y
 * \param width         crop width
 * \param height        crop height
 */

/*@access M3Gobject@*/
M3G_API void m3gSetBgCrop(M3GBackground handle,
                          M3Gint cropX, M3Gint cropY,
                          M3Gint width, M3Gint height)
{
    Background *background = (Background *) handle;
    M3G_VALIDATE_OBJECT(background);

    /* Check for errors */
    if (width < 0 || height < 0) {
        m3gRaiseError(M3G_INTERFACE(background), M3G_INVALID_VALUE);
        return;
    }

    background->crop.x = cropX;
    background->crop.y = cropY;
    background->crop.width = width;
    background->crop.height = height;
}

/*!
 * \brief Sets background image.
 *
 * \param handle        Background object
 * \param hImage        Image2D object or NULL
 */

/*@access M3Gobject@*/
M3G_API void m3gSetBgImage(M3GBackground handle, M3GImage hImage)
{
    Background *background = (Background *) handle;
    Image *image = (Image *)hImage;
    M3G_VALIDATE_OBJECT(background);

    if (image != NULL) {
        /* Check allowed formats */
        if (m3gGetFormat(image) != M3G_RGB &&
            m3gGetFormat(image) != M3G_RGBA) {
            m3gRaiseError(M3G_INTERFACE(background), M3G_INVALID_VALUE);
            return;
        }

        background->crop.x = 0;
        background->crop.y = 0;
        background->crop.width = m3gGetWidth(image);
        background->crop.height = m3gGetHeight(image);
    }

    M3G_ASSIGN_REF(background->image, image);
}

/*!
 * \brief Gets background image.
 *
 * \param handle        Background object
 * \return              Image2D object or NULL
 */

/*@access M3GObject@*/
M3G_API M3GImage m3gGetBgImage(M3GBackground handle)
{
    Background *bg = (Background *) handle;
    M3G_VALIDATE_OBJECT(bg);

    return (M3GImage) bg->image;
}

/*!
 * \brief Gets background color as ARGB.
 *
 * \param handle        Background object
 * \return              ARGB color
 */

/*@access M3Gobject@*/
M3G_API M3Guint m3gGetBgColor(M3GBackground handle)
{
    Background *background = (Background *) handle;
    M3G_VALIDATE_OBJECT(background);

    return background->color;
}

/*!
 * \brief Gets background image x or y mode.
 *
 * \param handle        Background object
 * \param which         which mode to return
 *                      \arg M3G_GET_MODEX
 *                      \arg M3G_GET_MODEY
 * \return              image x or y mode
 */

/*@access M3Gobject@*/
M3G_API M3Gint m3gGetBgMode(M3GBackground handle, M3Gint which)
{
    Background *background = (Background *) handle;
    M3G_VALIDATE_OBJECT(background);

    switch(which) {
        case M3G_GET_MODEX:
            return background->modeX;
        case M3G_GET_MODEY:
        default:
            return background->modeY;
    }
}

/*!
 * \brief Gets background image crop parameter.
 *
 * \param handle        Background object
 * \param which         which crop parameter to return
 *                      \arg M3G_GET_CROPX
 *                      \arg M3G_GET_CROPY
 *                      \arg M3G_GET_CROPWIDTH
 *                      \arg M3G_GET_CROPHEIGHT
 * \return              image crop parameter
 */

/*@access M3Gobject@*/
M3G_API M3Gint m3gGetBgCrop(M3GBackground handle, M3Gint which)
{
    Background *background = (Background *) handle;
    M3G_VALIDATE_OBJECT(background);

    switch(which) {
        case M3G_GET_CROPX:
            return background->crop.x;
        case M3G_GET_CROPY:
            return background->crop.y;
        case M3G_GET_CROPWIDTH:
            return background->crop.width;
        case M3G_GET_CROPHEIGHT:
        default:
            return background->crop.height;
    }
}

/*!
 * \brief Sets background color or depth clear enable.
 *
 * \param handle        Background object
 * \param which         which clear to enable
 *                      \arg M3G_SETGET_COLORCLEAR
 *                      \arg M3G_SETGET_DEPTHCLEAR
 * \param enable        clear enable/disable
 */

/*@access M3Gobject@*/
M3G_API void m3gSetBgEnable(M3GBackground handle, M3Gint which, M3Gbool enable)
{
    Background *background = (Background *) handle;
    M3G_VALIDATE_OBJECT(background);

    switch(which) {
        case M3G_SETGET_COLORCLEAR:
            background->colorClearEnable = enable;
            break;
        case M3G_SETGET_DEPTHCLEAR:
        default:
            background->depthClearEnable = enable;
            break;
    }
}

/*!
 * \brief Gets background color or depth clear enable.
 *
 * \param handle        Background object
 * \param which         which clear to return
 *                      \arg M3G_SETGET_COLORCLEAR
 *                      \arg M3G_SETGET_DEPTHCLEAR
 * \return              clear enabled
 */

/*@access M3Gobject@*/
M3G_API M3Gbool m3gIsBgEnabled(M3GBackground handle, M3Gint which)
{
    Background *background = (Background *) handle;
    M3G_VALIDATE_OBJECT(background);

    switch(which) {
        case M3G_SETGET_COLORCLEAR:
            return background->colorClearEnable;
        case M3G_SETGET_DEPTHCLEAR:
        default:
            return background->depthClearEnable;
    }
}