hostsupport/hostopenvg/src/riApi.cpp
author Matt Plumtree <matt.plumtree@nokia.com>
Wed, 06 Oct 2010 17:59:01 +0100
branchbug235_bringup_0
changeset 53 c2ef9095503a
parent 24 holdingarea/vg/2D_OpenVG_1_1_SF/ri/src/riApi.cpp@a3f46bb01be2
child 56 40cc73c24bf8
permissions -rw-r--r--
Copy code from the holdingarea into the target locations. Some initial rework of CMakeLists.txt files, but not yet tested.

/*------------------------------------------------------------------------
 *
 * OpenVG 1.1 Reference Implementation
 * -----------------------------------
 *
 * Copyright (c) 2007 The Khronos Group Inc.
 * Portions copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and /or associated documentation files
 * (the "Materials "), to deal in the Materials without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Materials,
 * and to permit persons to whom the Materials are furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Materials.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR
 * THE USE OR OTHER DEALINGS IN THE MATERIALS.
 *
 *//**
 * \file
 * \brief	Implementations of OpenVG API functions.
 * \note	The actual processing is done in Path, Image, Rasterizer and PixelPipe classes.
 *//*-------------------------------------------------------------------*/

#include "openvg.h"
#include "vgext.h"
#include "riContext.h"
#include "riRasterizer.h"
#include "riPixelPipe.h"
#include "riPath.h"
#include <stdio.h>

//==============================================================================================

namespace OpenVGRI
{

/* EGL&OS functions for use in an OpenVG implementation */
// \note To ensure that different EGLs work in parallel, these functions must be implemented.
// They may be wrappers if the underlying EGL is more complicated that miniEGL.
void* eglvgGetCurrentVGContext(void);
bool  eglvgIsInUse(void* image);
bool  eglvgLockSurface(bool read, bool write);
bool  eglvgUnlockSurface();
void  OSAcquireMutex(void);
void  OSReleaseMutex(void);
void  eglvgGetImageDescriptor( void* image, Color::Descriptor &desc, int &width, int &height, int &stride );
void*  eglvgGetImageData( void* image );

#define RI_NO_RETVAL

//this must be the first line in an API function
#define RI_GET_CONTEXT(RETVAL) \
    OSAcquireMutex(); \
    VGContext* context = (VGContext*)eglvgGetCurrentVGContext(); \
    if(!context) \
    { \
        OSReleaseMutex(); \
        return RETVAL;\
    }

#define RI_IF_ERROR(COND, ERRORCODE, RETVAL) \
    if(COND) { context->setError(ERRORCODE); OSReleaseMutex(); return RETVAL; }

//all API functions must call this as their last operation (also functions that don't return values)
//NOTE: don't evaluate anything or read state in RETVAL (it'll be executed after the mutex has been released)
#define RI_RETURN(RETVAL) \
    { OSReleaseMutex(); \
    return RETVAL; }

static bool isAligned(const void* ptr, int alignment)
{
    RI_ASSERT(alignment == 1 || alignment == 2 || alignment == 4);
    if(((RIuintptr)ptr) & (alignment-1))
        return false;
    return true;
}

static bool isAligned(const void* ptr, VGImageFormat format)
{
    RI_ASSERT(isValidImageFormat(format));
    int alignment = Color::formatToDescriptor(format).bitsPerPixel >> 3;
    if(alignment <= 1)
        return true;	//one bit or byte per pixel
    return isAligned(ptr, alignment);
}

bool isValidImageFormat(int f)
{
    if(f < VG_sRGBX_8888 || f > VG_lABGR_8888_PRE)
        return false;
    return true;
}

}	//namespace OpenVGRI

using namespace OpenVGRI;

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgFlush(void)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    //the RI doesn't cache anything, so this is a no-op
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgFinish(void)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    //the RI doesn't cache anything, so this is a no-op
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGErrorCode RI_APIENTRY vgGetError(void)
{
    RI_GET_CONTEXT(VG_NO_CONTEXT_ERROR);
    VGErrorCode error = context->m_error;
    context->m_error = VG_NO_ERROR;
    RI_RETURN(error);
}

VGErrorCode RI_APIENTRY vgPlatsimGetError(void)
{
    RI_GET_CONTEXT(VG_NO_CONTEXT_ERROR);
    VGErrorCode error = context->m_error;
    RI_RETURN(error);
}


/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

namespace OpenVGRI
{

RIfloat inputFloat(VGfloat f)
{
    //this function is used for all floating point input values
    if(RI_ISNAN(f)) return 0.0f;	//convert NaN to zero
    return RI_CLAMP(f, -RI_FLOAT_MAX, RI_FLOAT_MAX);	//clamp +-inf to +-RIfloat max
}

Vector2 inputVector2(const Vector2& v)
{
    return Vector2(inputFloat(v.x), inputFloat(v.y));
}

Color inputColor(const Color& c)
{
    Color r = c;
    r.r = inputFloat(r.r);
    r.g = inputFloat(r.g);
    r.b = inputFloat(r.b);
    r.a = inputFloat(r.a);
    return r;
}

static int inputFloatToInt(VGfloat value)
{
    double v = (double)floor(value);
    v = v > (double)RI_INT32_MAX ? (double)RI_INT32_MAX : v;
    v = v < (double)RI_INT32_MIN ? (double)RI_INT32_MIN : v;
    return (int)v;
}

static int paramToInt(const void* values, bool floats, int count, int i)
{
    RI_ASSERT(i >= 0);
    if(i >= count || !values)
        return 0;
    if(floats)
        return inputFloatToInt(((const VGfloat*)values)[i]);
    return (int)((const VGint*)values)[i];
}

static RIfloat paramToFloat(const void* values, bool floats, int count, int i)
{
    RI_ASSERT(i >= 0);
    if(i >= count || !values)
        return 0.0f;
    if(floats)
        return ((const VGfloat*)values)[i];
    return (RIfloat)((const VGint*)values)[i];
}

static void floatToParam(void* output, bool outputFloats, int count, int i, VGfloat value)
{
    RI_ASSERT(i >= 0);
    RI_ASSERT(output);
    if(i >= count)
        return;
    if(outputFloats)
        ((VGfloat*)output)[i] = value;
    else
        ((VGint*)output)[i] = (VGint)inputFloatToInt(value);
}

static void intToParam(void* output, bool outputFloats, int count, int i, VGint value)
{
    RI_ASSERT(i >= 0);
    RI_ASSERT(output);
    if(i >= count)
        return;
    if(outputFloats)
        ((VGfloat*)output)[i] = (VGfloat)value;
    else
        ((VGint*)output)[i] = value;
}

}	//namespace OpenVGRI

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

static void setifv(VGContext* context, VGParamType type, VGint count, const void* values, bool floats)
{
    RI_ASSERT(context);
    RI_ASSERT(!count || (count && values));

    int ivalue = paramToInt(values, floats, count, 0);
    RIfloat fvalue = paramToFloat(values, floats, count, 0);

    switch(type)
    {
    case VG_MATRIX_MODE:
        if(count != 1 || ivalue < VG_MATRIX_PATH_USER_TO_SURFACE || ivalue > VG_MATRIX_GLYPH_USER_TO_SURFACE)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_matrixMode = (VGMatrixMode)ivalue;
        break;

    case VG_FILL_RULE:
        if(count != 1 || ivalue < VG_EVEN_ODD || ivalue > VG_NON_ZERO)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_fillRule = (VGFillRule)ivalue;
        break;

    case VG_IMAGE_QUALITY:
        if(count != 1 || !(ivalue == VG_IMAGE_QUALITY_NONANTIALIASED || ivalue == VG_IMAGE_QUALITY_FASTER || ivalue == VG_IMAGE_QUALITY_BETTER))	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_imageQuality = (VGImageQuality)ivalue;
        break;

    case VG_RENDERING_QUALITY:
        if(count != 1 || !(ivalue == VG_RENDERING_QUALITY_NONANTIALIASED || ivalue == VG_RENDERING_QUALITY_FASTER || ivalue == VG_RENDERING_QUALITY_BETTER))	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_renderingQuality = (VGRenderingQuality)ivalue;
        break;

    case VG_BLEND_MODE:
        if(count != 1 || ivalue < VG_BLEND_SRC || ivalue > VG_BLEND_ADDITIVE)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_blendMode = (VGBlendMode)ivalue;
        break;

    case VG_IMAGE_MODE:
        if(count != 1 || ivalue < VG_DRAW_IMAGE_NORMAL || ivalue > VG_DRAW_IMAGE_STENCIL)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_imageMode = (VGImageMode)ivalue;
        break;

    case VG_SCISSOR_RECTS:
    {
        if(count & 3)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }	//count must be a multiple of four
        try
        {
            Array<OpenVGRI::Rectangle> scissor;
            for(int i=0;i<RI_INT_MIN(count, RI_MAX_SCISSOR_RECTANGLES*4);i+=4)
            {
                OpenVGRI::Rectangle s;
                s.x = paramToInt(values, floats, count, i+0);
                s.y = paramToInt(values, floats, count, i+1);
                s.width = paramToInt(values, floats, count, i+2);
                s.height = paramToInt(values, floats, count, i+3);
                scissor.push_back(s);	//throws bad_alloc
            }
            context->m_scissor.swap(scissor);	//replace context data
        }
        catch(std::bad_alloc)
        {
            context->setError(VG_OUT_OF_MEMORY_ERROR);
        }
        break;
    }

    case VG_COLOR_TRANSFORM:
        if(count != 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_colorTransform = ivalue ? VG_TRUE : VG_FALSE;
        break;

    case VG_COLOR_TRANSFORM_VALUES:
        if(count != 8 || !values) { context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        {
            for(int i=0;i<8;i++)
            {
                context->m_inputColorTransformValues[i] = paramToFloat(values, floats, count, i);
                context->m_colorTransformValues[i] = inputFloat(context->m_inputColorTransformValues[i]);
            }
        }
        break;

    case VG_STROKE_LINE_WIDTH:
        if(count != 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_inputStrokeLineWidth = fvalue;
        context->m_strokeLineWidth = inputFloat(fvalue);
        break;

    case VG_STROKE_CAP_STYLE:
        if(count != 1 || ivalue < VG_CAP_BUTT || ivalue > VG_CAP_SQUARE)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_strokeCapStyle = (VGCapStyle)ivalue;
        break;

    case VG_STROKE_JOIN_STYLE:
        if(count != 1 || ivalue < VG_JOIN_MITER || ivalue > VG_JOIN_BEVEL)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_strokeJoinStyle = (VGJoinStyle)ivalue;
        break;

    case VG_STROKE_MITER_LIMIT:
        if(count != 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_inputStrokeMiterLimit = fvalue;
        context->m_strokeMiterLimit = inputFloat(fvalue);
        break;

    case VG_STROKE_DASH_PATTERN:
    {
        try
        {
            Array<RIfloat> inputStrokeDashPattern;
            Array<RIfloat> strokeDashPattern;
            for(int i=0;i<RI_INT_MIN(count, RI_MAX_DASH_COUNT);i++)
            {
                RIfloat v = paramToFloat(values, floats, count, i);
                inputStrokeDashPattern.push_back(v);	//throws bad_alloc
                strokeDashPattern.push_back(inputFloat(v));	//throws bad_alloc
            }
            context->m_inputStrokeDashPattern.swap(inputStrokeDashPattern);	//replace context data
            context->m_strokeDashPattern.swap(strokeDashPattern);	//replace context data
        }
        catch(std::bad_alloc)
        {
            context->setError(VG_OUT_OF_MEMORY_ERROR);
        }
        break;
    }

    case VG_STROKE_DASH_PHASE:
        if(count != 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_inputStrokeDashPhase = fvalue;
        context->m_strokeDashPhase = inputFloat(fvalue);
        break;

    case VG_STROKE_DASH_PHASE_RESET:
        if(count != 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_strokeDashPhaseReset = ivalue ? VG_TRUE : VG_FALSE;
        break;

    case VG_TILE_FILL_COLOR:
        if(count != 4 || !values) { context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_inputTileFillColor.set(paramToFloat(values, floats, count, 0),
                                     paramToFloat(values, floats, count, 1),
                                     paramToFloat(values, floats, count, 2),
                                     paramToFloat(values, floats, count, 3),
                                     Color::sRGBA);
        context->m_tileFillColor = inputColor(context->m_inputTileFillColor);
        break;

    case VG_GLYPH_ORIGIN:
        if(count != 2 || !values) { context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_inputGlyphOrigin.x = paramToFloat(values, floats, count, 0);
        context->m_inputGlyphOrigin.y = paramToFloat(values, floats, count, 1);
        context->m_glyphOrigin = inputVector2(context->m_inputGlyphOrigin);
        break;

    case VG_CLEAR_COLOR:
        if(count != 4 || !values) { context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_inputClearColor.set(paramToFloat(values, floats, count, 0),
                                  paramToFloat(values, floats, count, 1),
                                  paramToFloat(values, floats, count, 2),
                                  paramToFloat(values, floats, count, 3),
                                  Color::sRGBA);
        context->m_clearColor = inputColor(context->m_inputClearColor);
        break;

    case VG_MASKING:
        if(count != 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_masking = ivalue ? VG_TRUE : VG_FALSE;
        break;

    case VG_SCISSORING:
        if(count != 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_scissoring = ivalue ? VG_TRUE : VG_FALSE;
        break;

    case VG_PIXEL_LAYOUT:
        if(count != 1 || ivalue < VG_PIXEL_LAYOUT_UNKNOWN || ivalue > VG_PIXEL_LAYOUT_BGR_HORIZONTAL)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_pixelLayout = (VGPixelLayout)ivalue;
        break;

    case VG_SCREEN_LAYOUT:
        if(count != 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        break;	//setting read-only values has no effect

    case VG_FILTER_FORMAT_LINEAR:
        if(count != 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_filterFormatLinear = ivalue ? VG_TRUE : VG_FALSE;
        break;

    case VG_FILTER_FORMAT_PREMULTIPLIED:
        if(count != 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        context->m_filterFormatPremultiplied = ivalue ? VG_TRUE : VG_FALSE;
        break;

    case VG_FILTER_CHANNEL_MASK:
        if(count != 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        //undefined bits are ignored
        context->m_filterChannelMask = (VGbitfield)ivalue;
        break;

    case VG_MAX_SCISSOR_RECTS:
    case VG_MAX_DASH_COUNT:
    case VG_MAX_KERNEL_SIZE:
    case VG_MAX_SEPARABLE_KERNEL_SIZE:
    case VG_MAX_COLOR_RAMP_STOPS:
    case VG_MAX_IMAGE_WIDTH:
    case VG_MAX_IMAGE_HEIGHT:
    case VG_MAX_IMAGE_PIXELS:
    case VG_MAX_IMAGE_BYTES:
    case VG_MAX_FLOAT:
    case VG_MAX_GAUSSIAN_STD_DEVIATION:
        if(count != 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        break;	//setting read-only values has no effect

    default:
        context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
        break;
    }
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgSetf(VGParamType type, VGfloat value)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(type == VG_SCISSOR_RECTS || type == VG_STROKE_DASH_PATTERN || type == VG_TILE_FILL_COLOR ||
                type == VG_CLEAR_COLOR, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);	//vector type value
    VGfloat values[1] = {value};
    setifv(context, type, 1, values, true);
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgSeti(VGParamType type, VGint value)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(type == VG_SCISSOR_RECTS || type == VG_STROKE_DASH_PATTERN || type == VG_TILE_FILL_COLOR ||
                type == VG_CLEAR_COLOR, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);	//vector type value
    VGint values[1] = {value};
    setifv(context, type, 1, values, false);
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgSetiv(VGParamType type, VGint count, const VGint * values)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(count < 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR((!values && count > 0) || (values && !isAligned(values,4)), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    setifv(context, type, count, values, false);
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgSetfv(VGParamType type, VGint count, const VGfloat * values)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(count < 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR((!values && count > 0) || (values && !isAligned(values,4)), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    setifv(context, type, count, values, true);
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

static void getifv(VGContext* context, VGParamType type, VGint count, void* values, bool floats)
{
    switch(type)
    {
    case VG_MATRIX_MODE:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_matrixMode);
        break;

    case VG_FILL_RULE:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_fillRule);
        break;

    case VG_IMAGE_QUALITY:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_imageQuality);
        break;

    case VG_RENDERING_QUALITY:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_renderingQuality);
        break;

    case VG_BLEND_MODE:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_blendMode);
        break;

    case VG_IMAGE_MODE:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_imageMode);
        break;

    case VG_SCISSOR_RECTS:
    {
        if(count > context->m_scissor.size()*4)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        for(int i=0;i<context->m_scissor.size();i++)
        {
            intToParam(values, floats, count, i*4+0, context->m_scissor[i].x);
            intToParam(values, floats, count, i*4+1, context->m_scissor[i].y);
            intToParam(values, floats, count, i*4+2, context->m_scissor[i].width);
            intToParam(values, floats, count, i*4+3, context->m_scissor[i].height);
        }
        break;
    }

    case VG_COLOR_TRANSFORM:
        if(count != 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_colorTransform);
        break;

    case VG_COLOR_TRANSFORM_VALUES:
        if(count > 8) { context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        {
            for(int i=0;i<count;i++)
            {
                floatToParam(values, floats, count, i, context->m_inputColorTransformValues[i]);
            }
        }
        break;

    case VG_STROKE_LINE_WIDTH:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        floatToParam(values, floats, count, 0, context->m_inputStrokeLineWidth);
        break;

    case VG_STROKE_CAP_STYLE:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_strokeCapStyle);
        break;

    case VG_STROKE_JOIN_STYLE:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_strokeJoinStyle);
        break;

    case VG_STROKE_MITER_LIMIT:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        floatToParam(values, floats, count, 0, context->m_inputStrokeMiterLimit);
        break;

    case VG_STROKE_DASH_PATTERN:
    {
        if(count > context->m_inputStrokeDashPattern.size())	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        for(int i=0;i<context->m_inputStrokeDashPattern.size();i++)
            floatToParam(values, floats, count, i, context->m_inputStrokeDashPattern[i]);
        break;
    }

    case VG_STROKE_DASH_PHASE:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        floatToParam(values, floats, count, 0, context->m_inputStrokeDashPhase);
        break;

    case VG_STROKE_DASH_PHASE_RESET:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_strokeDashPhaseReset);
        break;

    case VG_TILE_FILL_COLOR:
        if(count > 4)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        floatToParam(values, floats, count, 0, context->m_inputTileFillColor.r);
        floatToParam(values, floats, count, 1, context->m_inputTileFillColor.g);
        floatToParam(values, floats, count, 2, context->m_inputTileFillColor.b);
        floatToParam(values, floats, count, 3, context->m_inputTileFillColor.a);
        break;

    case VG_CLEAR_COLOR:
        if(count > 4)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        floatToParam(values, floats, count, 0, context->m_inputClearColor.r);
        floatToParam(values, floats, count, 1, context->m_inputClearColor.g);
        floatToParam(values, floats, count, 2, context->m_inputClearColor.b);
        floatToParam(values, floats, count, 3, context->m_inputClearColor.a);
        break;

    case VG_GLYPH_ORIGIN:
        if(count > 2)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        floatToParam(values, floats, count, 0, context->m_inputGlyphOrigin.x);
        floatToParam(values, floats, count, 1, context->m_inputGlyphOrigin.y);
        break;

    case VG_MASKING:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_masking);
        break;

    case VG_SCISSORING:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_scissoring);
        break;

    case VG_PIXEL_LAYOUT:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_pixelLayout);
        break;

    case VG_SCREEN_LAYOUT:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, VG_PIXEL_LAYOUT_UNKNOWN);
        break;

    case VG_FILTER_FORMAT_LINEAR:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_filterFormatLinear);
        break;

    case VG_FILTER_FORMAT_PREMULTIPLIED:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_filterFormatPremultiplied);
        break;

    case VG_FILTER_CHANNEL_MASK:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, context->m_filterChannelMask);
        break;

    case VG_MAX_SCISSOR_RECTS:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, RI_MAX_SCISSOR_RECTANGLES);
        break;

    case VG_MAX_DASH_COUNT:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, RI_MAX_DASH_COUNT);
        break;

    case VG_MAX_KERNEL_SIZE:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, RI_MAX_KERNEL_SIZE);
        break;

    case VG_MAX_SEPARABLE_KERNEL_SIZE:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, RI_MAX_SEPARABLE_KERNEL_SIZE);
        break;

    case VG_MAX_COLOR_RAMP_STOPS:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, RI_MAX_COLOR_RAMP_STOPS);
        break;

    case VG_MAX_IMAGE_WIDTH:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, RI_MAX_IMAGE_WIDTH);
        break;

    case VG_MAX_IMAGE_HEIGHT:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, RI_MAX_IMAGE_HEIGHT);
        break;

    case VG_MAX_IMAGE_PIXELS:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, RI_MAX_IMAGE_PIXELS);
        break;

    case VG_MAX_IMAGE_BYTES:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, RI_MAX_IMAGE_BYTES);
        break;

    case VG_MAX_FLOAT:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        floatToParam(values, floats, count, 0, RI_FLOAT_MAX);
        break;

    case VG_MAX_GAUSSIAN_STD_DEVIATION:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        floatToParam(values, floats, count, 0, RI_MAX_GAUSSIAN_STD_DEVIATION);
        break;

    default:
        context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
        break;
    }
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGfloat RI_APIENTRY vgGetf(VGParamType type)
{
    RI_GET_CONTEXT(0.0f);
    RI_IF_ERROR(type == VG_SCISSOR_RECTS || type == VG_STROKE_DASH_PATTERN || type == VG_TILE_FILL_COLOR ||
                type == VG_CLEAR_COLOR, VG_ILLEGAL_ARGUMENT_ERROR, 0.0f);	//vector type value
    RIfloat ret = 0.0f;
    getifv(context, type, 1, &ret, true);
    RI_RETURN(ret);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGint RI_APIENTRY vgGeti(VGParamType type)
{
    RI_GET_CONTEXT(0);
    RI_IF_ERROR(type == VG_SCISSOR_RECTS || type == VG_STROKE_DASH_PATTERN || type == VG_TILE_FILL_COLOR ||
                type == VG_CLEAR_COLOR, VG_ILLEGAL_ARGUMENT_ERROR, 0);	//vector type value
    VGint ret = 0;
    getifv(context, type, 1, &ret, false);
    RI_RETURN(ret);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgGetiv(VGParamType type, VGint count, VGint * values)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(count <= 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!values || !isAligned(values,4), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    getifv(context, type, count, values, false);
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgGetfv(VGParamType type, VGint count, VGfloat * values)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(count <= 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!values || !isAligned(values,4), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    getifv(context, type, count, values, true);
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGint RI_APIENTRY vgGetVectorSize(VGParamType type)
{
    RI_GET_CONTEXT(0);
    VGint ret = 0;
    switch(type)
    {
    case VG_MATRIX_MODE:
    case VG_FILL_RULE:
    case VG_IMAGE_QUALITY:
    case VG_RENDERING_QUALITY:
    case VG_BLEND_MODE:
    case VG_IMAGE_MODE:
        ret = 1;
        break;

    case VG_SCISSOR_RECTS:
        ret = 4*context->m_scissor.size();
        break;

    case VG_COLOR_TRANSFORM:
        ret = 1;
        break;

    case VG_COLOR_TRANSFORM_VALUES:
        ret = 8;
        break;

    case VG_STROKE_LINE_WIDTH:
    case VG_STROKE_CAP_STYLE:
    case VG_STROKE_JOIN_STYLE:
    case VG_STROKE_MITER_LIMIT:
        ret = 1;
        break;

    case VG_STROKE_DASH_PATTERN:
        ret = context->m_inputStrokeDashPattern.size();
        break;

    case VG_STROKE_DASH_PHASE:
    case VG_STROKE_DASH_PHASE_RESET:
        ret = 1;
        break;

    case VG_TILE_FILL_COLOR:
    case VG_CLEAR_COLOR:
        ret = 4;
        break;

    case VG_GLYPH_ORIGIN:
        ret = 2;
        break;

    case VG_MASKING:
    case VG_SCISSORING:
    case VG_PIXEL_LAYOUT:
    case VG_SCREEN_LAYOUT:
    case VG_FILTER_FORMAT_LINEAR:
    case VG_FILTER_FORMAT_PREMULTIPLIED:
    case VG_FILTER_CHANNEL_MASK:
    case VG_MAX_SCISSOR_RECTS:
    case VG_MAX_DASH_COUNT:
    case VG_MAX_KERNEL_SIZE:
    case VG_MAX_SEPARABLE_KERNEL_SIZE:
    case VG_MAX_COLOR_RAMP_STOPS:
    case VG_MAX_IMAGE_WIDTH:
    case VG_MAX_IMAGE_HEIGHT:
    case VG_MAX_IMAGE_PIXELS:
    case VG_MAX_IMAGE_BYTES:
    case VG_MAX_FLOAT:
    case VG_MAX_GAUSSIAN_STD_DEVIATION:
        ret = 1;
        break;

    default:
        context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
        break;
    }
    RI_RETURN(ret);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

static void setPaintParameterifv(VGContext* context, Paint* paint, VGPaintParamType paramType, VGint count, const void* values, bool floats)
{
    RI_ASSERT(context);
    RI_ASSERT(paint);

    int ivalue = paramToInt(values, floats, count, 0);

    switch(paramType)
    {
    case VG_PAINT_TYPE:
        if(count != 1 || ivalue < VG_PAINT_TYPE_COLOR || ivalue > VG_PAINT_TYPE_PATTERN)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        paint->m_paintType = (VGPaintType)ivalue;
        break;

    case VG_PAINT_COLOR:
        RI_TRACE("\n***** Setting solid color to paint.\n");
        RI_TRACE("** ptr: %x\n", (int)paint);
        if(count != 4)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        paint->m_inputPaintColor.set(paramToFloat(values, floats, count, 0),
                                     paramToFloat(values, floats, count, 1),
                                     paramToFloat(values, floats, count, 2),
                                     paramToFloat(values, floats, count, 3),
                                     Color::sRGBA);
        RI_TRACE("** input solid color: [%.3f %.3f %.3f %.4f]\n", paint->m_inputPaintColor.r, paint->m_inputPaintColor.g, paint->m_inputPaintColor.b, paint->m_inputPaintColor.a);
        paint->setColor(inputColor(paint->m_inputPaintColor));
        RI_TRACE("** -> [%.3f %.3f %.3f %.3f]\n", paint->m_paintColor.r, paint->m_paintColor.g, paint->m_paintColor.b, paint->m_paintColor.a);
        break;

    case VG_PAINT_COLOR_RAMP_SPREAD_MODE:
        if(count != 1 || ivalue < VG_COLOR_RAMP_SPREAD_PAD || ivalue > VG_COLOR_RAMP_SPREAD_REFLECT)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        paint->m_colorRampSpreadMode = (VGColorRampSpreadMode)ivalue;
        break;

    case VG_PAINT_COLOR_RAMP_STOPS:
    {
        int numStops = count/5;
        if(count != numStops*5)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }	//count must be a multiple of five
        try
        {
            Array<Paint::GradientStop> colorRampStops;
            Array<Paint::GradientStop> inputColorRampStops;
            RIfloat prevOffset = -RI_FLOAT_MAX;
            bool valid = true;
            for(int i=0;i<RI_INT_MIN(numStops, RI_MAX_COLOR_RAMP_STOPS);i++)	//NOTE: ignores the final stop if there is not enough parameters
            {
                Paint::GradientStop gs;
                gs.offset = paramToFloat(values, floats, count, i*5);
                gs.color.set(paramToFloat(values, floats, count, i*5+1),
                             paramToFloat(values, floats, count, i*5+2),
                             paramToFloat(values, floats, count, i*5+3),
                             paramToFloat(values, floats, count, i*5+4),
                             Color::sRGBA);
                inputColorRampStops.push_back(gs);

                if(gs.offset < prevOffset)
                    valid = false;	//decreasing sequence, ignore it

                if(gs.offset >= 0.0f && gs.offset <= 1.0f)
                {
                    gs.color.clamp();

                    if(!colorRampStops.size() && gs.offset > 0.0f)
                    {	//the first valid stop is not at 0, replicate the first one
                        RIfloat tmp = gs.offset;
                        gs.offset = 0.0f;
                        colorRampStops.push_back(gs);	//throws bad_alloc
                        gs.offset = tmp;
                    }
                    colorRampStops.push_back(gs);	//throws bad_alloc
                }
                prevOffset = gs.offset;
            }
            if(valid && colorRampStops.size() && colorRampStops[colorRampStops.size()-1].offset < 1.0f)
            {	//there is at least one stop, but the last one is not at 1, replicate the last one
                Paint::GradientStop gs = colorRampStops[colorRampStops.size()-1];
                gs.offset = 1.0f;
                colorRampStops.push_back(gs);	//throws bad_alloc
            }
            if(!valid || !colorRampStops.size())
            {	//there are no valid stops, add implicit stops
                colorRampStops.clear();
                Paint::GradientStop gs;
                gs.offset = 0.0f;
                gs.color.set(0,0,0,1,Color::sRGBA);
                colorRampStops.push_back(gs);	//throws bad_alloc
                gs.offset = 1.0f;
                gs.color.set(1,1,1,1,Color::sRGBA);
                colorRampStops.push_back(gs);	//throws bad_alloc
            }
            RI_ASSERT(colorRampStops.size() >= 2 && colorRampStops.size() <= RI_MAX_COLOR_RAMP_STOPS);
            //paint->m_colorRampStops.swap(colorRampStops);	//set paint array
            //paint->m_inputColorRampStops.swap(inputColorRampStops);	//set paint array
            paint->setGradientStops(inputColorRampStops, colorRampStops);
            //paint->generateLUT();
        }
        catch(std::bad_alloc)
        {
            context->setError(VG_OUT_OF_MEMORY_ERROR);
        }
        break;
    }

    case VG_PAINT_COLOR_RAMP_PREMULTIPLIED:
        if(count != 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        paint->m_colorRampPremultiplied = ivalue ? VG_TRUE : VG_FALSE;
        break;

    case VG_PAINT_LINEAR_GRADIENT:
        if(count != 4)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        paint->m_inputLinearGradientPoint0.set(paramToFloat(values, floats, count, 0),
                                          paramToFloat(values, floats, count, 1));
        paint->m_inputLinearGradientPoint1.set(paramToFloat(values, floats, count, 2),
                                          paramToFloat(values, floats, count, 3));
        paint->setLinearGradient(
            inputVector2(paint->m_inputLinearGradientPoint0),
            inputVector2(paint->m_inputLinearGradientPoint1));

        break;

    case VG_PAINT_RADIAL_GRADIENT:
        if(count != 5)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        paint->m_inputRadialGradientCenter.set(paramToFloat(values, floats, count, 0),
                                          paramToFloat(values, floats, count, 1));
        paint->m_inputRadialGradientFocalPoint.set(paramToFloat(values, floats, count, 2),
                                              paramToFloat(values, floats, count, 3));
        paint->m_inputRadialGradientRadius = paramToFloat(values, floats, count, 4);
        paint->setRadialGradient(
            inputVector2(paint->m_inputRadialGradientCenter),
            inputVector2(paint->m_inputRadialGradientFocalPoint),
            inputFloat(paint->m_inputRadialGradientRadius));
        //paint->m_radialGradientCenter = inputVector2(paint->m_inputRadialGradientCenter);
        //paint->m_radialGradientFocalPoint = inputVector2(paint->m_inputRadialGradientFocalPoint);
        //paint->m_radialGradientRadius = inputFloat(paint->m_inputRadialGradientRadius);
        break;

    case VG_PAINT_PATTERN_TILING_MODE:
        if(count != 1 || ivalue < VG_TILE_FILL || ivalue > VG_TILE_REFLECT)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        paint->m_patternTilingMode = (VGTilingMode)ivalue;
        break;

    default:
        context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
        break;
    }
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgSetParameterf(VGHandle object, VGint paramType, VGfloat value)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    bool isImage = context->isValidImage(object);
    bool isPath = context->isValidPath(object);
    bool isPaint = context->isValidPaint(object);
    bool isMaskLayer = context->isValidMaskLayer(object);
    bool isFont = context->isValidFont(object);
    RI_IF_ERROR(!isImage && !isPath && !isPaint && !isMaskLayer && !isFont, VG_BAD_HANDLE_ERROR, RI_NO_RETVAL); //invalid object handle
    RI_IF_ERROR(paramType == VG_PAINT_COLOR || paramType == VG_PAINT_COLOR_RAMP_STOPS || paramType == VG_PAINT_LINEAR_GRADIENT ||
                paramType == VG_PAINT_RADIAL_GRADIENT, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL); //vector valued parameter
    VGfloat values[1] = {value};
    if(isImage)
    {	//read only, the function does nothing
        RI_ASSERT(!isPath && !isPaint && !isMaskLayer && !isFont);
        if(paramType < VG_IMAGE_FORMAT || paramType > VG_IMAGE_HEIGHT)
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    else if(isPath)
    {	//read only, the function does nothing
        RI_ASSERT(!isImage && !isPaint && !isMaskLayer && !isFont);
        if(paramType < VG_PATH_FORMAT || paramType > VG_PATH_NUM_COORDS)
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    else if(isPaint)
    {
        RI_ASSERT(!isImage && !isPath && !isMaskLayer && !isFont);
        setPaintParameterifv(context, (Paint*)object, (VGPaintParamType)paramType, 1, values, true);
    }
    else if(isMaskLayer)
    {
        RI_ASSERT(!isImage && !isPath && !isPaint && !isFont);
        context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    else
    {	//read only, the function does nothing
        RI_ASSERT(!isImage && !isPath && !isPaint && !isMaskLayer && isFont);
        if (paramType != VG_FONT_NUM_GLYPHS)
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgSetParameteri(VGHandle object, VGint paramType, VGint value)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    bool isImage = context->isValidImage(object);
    bool isPath = context->isValidPath(object);
    bool isPaint = context->isValidPaint(object);
    bool isMaskLayer = context->isValidMaskLayer(object);
    bool isFont = context->isValidFont(object);
    RI_IF_ERROR(!isImage && !isPath && !isPaint && !isMaskLayer && !isFont, VG_BAD_HANDLE_ERROR, RI_NO_RETVAL); //invalid object handle
    RI_IF_ERROR(paramType == VG_PAINT_COLOR || paramType == VG_PAINT_COLOR_RAMP_STOPS || paramType == VG_PAINT_LINEAR_GRADIENT ||
                paramType == VG_PAINT_RADIAL_GRADIENT, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);	//vector valued parameter
    VGint values[1] = {value};
    if(isImage)
    {	//read only, the function does nothing
        RI_ASSERT(!isPath && !isPaint && !isMaskLayer && !isFont);
        if(paramType < VG_IMAGE_FORMAT || paramType > VG_IMAGE_HEIGHT)
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    else if(isPath)
    {	//read only, the function does nothing
        RI_ASSERT(!isImage && !isPaint && !isMaskLayer && !isFont);
        if(paramType < VG_PATH_FORMAT || paramType > VG_PATH_NUM_COORDS)
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    else if(isPaint)
    {
        RI_ASSERT(!isImage && !isPath && !isMaskLayer && !isFont);
        setPaintParameterifv(context, (Paint*)object, (VGPaintParamType)paramType, 1, values, false);
    }
    else if(isMaskLayer)
    {
        RI_ASSERT(!isImage && !isPath && !isPaint && !isFont);
        context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    else
    {	//read only, the function does nothing
        RI_ASSERT(!isImage && !isPath && !isPaint && !isMaskLayer && isFont);
        if (paramType != VG_FONT_NUM_GLYPHS)
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgSetParameterfv(VGHandle object, VGint paramType, VGint count, const VGfloat * values)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(count < 0 || (!values && count > 0) || (values && !isAligned(values,4)), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    bool isImage = context->isValidImage(object);
    bool isPath = context->isValidPath(object);
    bool isPaint = context->isValidPaint(object);
    bool isMaskLayer = context->isValidMaskLayer(object);
    bool isFont = context->isValidFont(object);
    RI_IF_ERROR(!isImage && !isPath && !isPaint && !isMaskLayer && !isFont, VG_BAD_HANDLE_ERROR, RI_NO_RETVAL); //invalid object handle
    if(isImage)
    {	//read only, the function does nothing
        RI_ASSERT(!isPath && !isPaint && !isMaskLayer && !isFont);
        if(paramType < VG_IMAGE_FORMAT || paramType > VG_IMAGE_HEIGHT)
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    else if(isPath)
    {	//read only, the function does nothing
        RI_ASSERT(!isImage && !isPaint && !isMaskLayer && !isFont);
        if(paramType < VG_PATH_FORMAT || paramType > VG_PATH_NUM_COORDS)
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    else if(isPaint)
    {
        RI_ASSERT(!isImage && !isPath && !isMaskLayer && !isFont);
        setPaintParameterifv(context, (Paint*)object, (VGPaintParamType)paramType, count, values, true);
    }
    else if(isMaskLayer)
    {
        RI_ASSERT(!isImage && !isPath && !isPaint && !isFont);
        context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    else
    {	//read only, the function does nothing
        RI_ASSERT(!isImage && !isPath && !isPaint && !isMaskLayer && isFont);
        if (paramType != VG_FONT_NUM_GLYPHS)
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgSetParameteriv(VGHandle object, VGint paramType, VGint count, const VGint * values)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(count < 0 || (!values && count > 0) || (values && !isAligned(values,4)), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    bool isImage = context->isValidImage(object);
    bool isPath = context->isValidPath(object);
    bool isPaint = context->isValidPaint(object);
    bool isMaskLayer = context->isValidMaskLayer(object);
    bool isFont = context->isValidFont(object);
    RI_IF_ERROR(!isImage && !isPath && !isPaint && !isMaskLayer && !isFont, VG_BAD_HANDLE_ERROR, RI_NO_RETVAL); //invalid object handle
    if(isImage)
    {	//read only, the function does nothing
        RI_ASSERT(!isPath && !isPaint && !isMaskLayer && !isFont);
        if(paramType < VG_IMAGE_FORMAT || paramType > VG_IMAGE_HEIGHT)
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    else if(isPath)
    {	//read only, the function does nothing
        RI_ASSERT(!isImage && !isPaint && !isMaskLayer && !isFont);
        if(paramType < VG_PATH_FORMAT || paramType > VG_PATH_NUM_COORDS)
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    else if(isPaint)
    {
        RI_ASSERT(!isImage && !isPath && !isMaskLayer && !isFont);
        setPaintParameterifv(context, (Paint*)object, (VGPaintParamType)paramType, count, values, false);
    }
    else if(isMaskLayer)
    {
        RI_ASSERT(!isImage && !isPath && !isPaint && !isFont);
        context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    else
    {	//read only, the function does nothing
        RI_ASSERT(!isImage && !isPath && !isPaint && !isMaskLayer && isFont);
        if (paramType != VG_FONT_NUM_GLYPHS)
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

static void getPaintParameterifv(VGContext* context, Paint* paint, VGPaintParamType type, VGint count, void* values, bool floats)
{
    switch(type)
    {
    case VG_PAINT_TYPE:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, paint->m_paintType);
        break;

    case VG_PAINT_COLOR:
        if(count > 4)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        floatToParam(values, floats, count, 0, paint->m_inputPaintColor.r);
        floatToParam(values, floats, count, 1, paint->m_inputPaintColor.g);
        floatToParam(values, floats, count, 2, paint->m_inputPaintColor.b);
        floatToParam(values, floats, count, 3, paint->m_inputPaintColor.a);
        break;

    case VG_PAINT_COLOR_RAMP_SPREAD_MODE:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, paint->m_colorRampSpreadMode);
        break;

    case VG_PAINT_COLOR_RAMP_STOPS:
        {
            if(count > paint->m_inputColorRampStops.size()*5)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
            int j = 0;
            for(int i=0;i<paint->m_inputColorRampStops.size();i++)
            {
                floatToParam(values, floats, count, j++, paint->m_inputColorRampStops[i].offset);
                floatToParam(values, floats, count, j++, paint->m_inputColorRampStops[i].color.r);
                floatToParam(values, floats, count, j++, paint->m_inputColorRampStops[i].color.g);
                floatToParam(values, floats, count, j++, paint->m_inputColorRampStops[i].color.b);
                floatToParam(values, floats, count, j++, paint->m_inputColorRampStops[i].color.a);
            }
            break;
        }

    case VG_PAINT_COLOR_RAMP_PREMULTIPLIED:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, paint->m_colorRampPremultiplied);
        break;

    case VG_PAINT_LINEAR_GRADIENT:
        if(count > 4)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        floatToParam(values, floats, count, 0, paint->m_inputLinearGradientPoint0.x);
        floatToParam(values, floats, count, 1, paint->m_inputLinearGradientPoint0.y);
        floatToParam(values, floats, count, 2, paint->m_inputLinearGradientPoint1.x);
        floatToParam(values, floats, count, 3, paint->m_inputLinearGradientPoint1.y);
        break;

    case VG_PAINT_RADIAL_GRADIENT:
        if(count > 5)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        floatToParam(values, floats, count, 0, paint->m_inputRadialGradientCenter.x);
        floatToParam(values, floats, count, 1, paint->m_inputRadialGradientCenter.y);
        floatToParam(values, floats, count, 2, paint->m_inputRadialGradientFocalPoint.x);
        floatToParam(values, floats, count, 3, paint->m_inputRadialGradientFocalPoint.y);
        floatToParam(values, floats, count, 4, paint->m_inputRadialGradientRadius);
        break;

    case VG_PAINT_PATTERN_TILING_MODE:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, paint->m_patternTilingMode);
        break;

    default:
        context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
        break;
    }
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

static void getPathParameterifv(VGContext* context, Path* path, VGPathParamType type, VGint count, void* values, bool floats)
{
    switch(type)
    {
    case VG_PATH_FORMAT:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, path->getFormat());
        break;

    case VG_PATH_DATATYPE:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, path->getDatatype());
        break;

    case VG_PATH_SCALE:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        floatToParam(values, floats, count, 0, path->getScale());
        break;

    case VG_PATH_BIAS:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        floatToParam(values, floats, count, 0, path->getBias());
        break;

    case VG_PATH_NUM_SEGMENTS:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, path->getNumSegments());
        break;

    case VG_PATH_NUM_COORDS:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, path->getNumCoordinates());
        break;

    default:
        context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
        break;
    }
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

static void getImageParameterifv(VGContext* context, Image* image, VGImageParamType type, VGint count, void* values, bool floats)
{
    switch(type)
    {
    case VG_IMAGE_FORMAT:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        RI_ASSERT(isValidImageFormat(image->getDescriptor().vgFormat));
        intToParam(values, floats, count, 0, image->getDescriptor().vgFormat);
        break;

    case VG_IMAGE_WIDTH:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, image->getWidth());
        break;

    case VG_IMAGE_HEIGHT:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, image->getHeight());
        break;

    default:
        context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
        break;
    }
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

static void getFontParameterifv(VGContext* context, Font* font, VGFontParamType type, VGint count, void* values, bool floats)
{
    switch(type)
    {
    case VG_FONT_NUM_GLYPHS:
        if(count > 1)	{ context->setError(VG_ILLEGAL_ARGUMENT_ERROR); return; }
        intToParam(values, floats, count, 0, font->getNumGlyphs());
        break;

    default:
        context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid VGParamType
        break;
    }
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGfloat RI_APIENTRY vgGetParameterf(VGHandle object, VGint paramType)
{
    RI_GET_CONTEXT(0.0f);
    RI_IF_ERROR(paramType == VG_PAINT_COLOR || paramType == VG_PAINT_COLOR_RAMP_STOPS || paramType == VG_PAINT_LINEAR_GRADIENT ||
                paramType == VG_PAINT_RADIAL_GRADIENT, VG_ILLEGAL_ARGUMENT_ERROR, 0.0f);	//vector valued parameter
    bool isImage = context->isValidImage(object);
    bool isPath = context->isValidPath(object);
    bool isPaint = context->isValidPaint(object);
    bool isFont = context->isValidFont(object);
    RI_IF_ERROR(!isImage && !isPath && !isPaint && !isFont, VG_BAD_HANDLE_ERROR, 0.0f);	//invalid object handle
    VGfloat ret = 0.0f;
    if(isImage)
    {
        RI_ASSERT(!isPath && !isPaint && !isFont);
        getImageParameterifv(context, (Image*)object, (VGImageParamType)paramType, 1, &ret, true);
    }
    else if(isPath)
    {
        RI_ASSERT(!isImage && !isPaint && !isFont);
        getPathParameterifv(context, (Path*)object, (VGPathParamType)paramType, 1, &ret, true);
    }
    else if(isPaint)
    {
        RI_ASSERT(!isImage && !isPath && !isFont);
        getPaintParameterifv(context, (Paint*)object, (VGPaintParamType)paramType, 1, &ret, true);
    }
    else
    {
        RI_ASSERT(!isImage && !isPath && !isPaint && isFont);
        getFontParameterifv(context, (Font*)object, (VGFontParamType)paramType, 1, &ret, true);
    }
    RI_RETURN(ret);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGint RI_APIENTRY vgGetParameteri(VGHandle object, VGint paramType)
{
    RI_GET_CONTEXT(0);
    RI_IF_ERROR(paramType == VG_PAINT_COLOR || paramType == VG_PAINT_COLOR_RAMP_STOPS || paramType == VG_PAINT_LINEAR_GRADIENT ||
                paramType == VG_PAINT_RADIAL_GRADIENT, VG_ILLEGAL_ARGUMENT_ERROR, 0);	//vector valued parameter
    bool isImage = context->isValidImage(object);
    bool isPath = context->isValidPath(object);
    bool isPaint = context->isValidPaint(object);
    bool isFont = context->isValidFont(object);
    RI_IF_ERROR(!isImage && !isPath && !isPaint && !isFont, VG_BAD_HANDLE_ERROR, 0);	//invalid object handle
    VGint ret = 0;
    if(isImage)
    {
        RI_ASSERT(!isPath && !isPaint && !isFont);
        getImageParameterifv(context, (Image*)object, (VGImageParamType)paramType, 1, &ret, false);
    }
    else if(isPath)
    {
        RI_ASSERT(!isImage && !isPaint && !isFont);
        getPathParameterifv(context, (Path*)object, (VGPathParamType)paramType, 1, &ret, false);
    }
    else if(isPaint)
    {
        RI_ASSERT(!isImage && !isPath && !isFont);
        getPaintParameterifv(context, (Paint*)object, (VGPaintParamType)paramType, 1, &ret, false);
    }
    else
    {
        RI_ASSERT(!isImage && !isPath && !isPaint && isFont);
        getFontParameterifv(context, (Font*)object, (VGFontParamType)paramType, 1, &ret, false);
    }
    RI_RETURN(ret);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgGetParameterfv(VGHandle object, VGint paramType, VGint count, VGfloat * values)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(count <= 0 || !values || !isAligned(values,4), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    bool isImage = context->isValidImage(object);
    bool isPath = context->isValidPath(object);
    bool isPaint = context->isValidPaint(object);
    bool isFont = context->isValidFont(object);
    RI_IF_ERROR(!isImage && !isPath && !isPaint && !isFont, VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid object handle
    if(isImage)
    {
        RI_ASSERT(!isPath && !isPaint && !isFont);
        getImageParameterifv(context, (Image*)object, (VGImageParamType)paramType, count, values, true);
    }
    else if(isPath)
    {
        RI_ASSERT(!isImage && !isPaint && !isFont);
        getPathParameterifv(context, (Path*)object, (VGPathParamType)paramType, count, values, true);
    }
    else if(isPaint)
    {
        RI_ASSERT(!isImage && !isPath && !isFont);
        getPaintParameterifv(context, (Paint*)object, (VGPaintParamType)paramType, count, values, true);
    }
    else
    {
        RI_ASSERT(!isImage && !isPath && !isPaint && isFont);
        getFontParameterifv(context, (Font*)object, (VGFontParamType)paramType, count, values, true);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgGetParameteriv(VGHandle object, VGint paramType, VGint count, VGint * values)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(count <= 0 || !values || !isAligned(values,4), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    bool isImage = context->isValidImage(object);
    bool isPath = context->isValidPath(object);
    bool isPaint = context->isValidPaint(object);
    bool isFont = context->isValidFont(object);
    RI_IF_ERROR(!isImage && !isPath && !isPaint && !isFont, VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid object handle
    if(isImage)
    {
        RI_ASSERT(!isPath && !isPaint && !isFont);
        getImageParameterifv(context, (Image*)object, (VGImageParamType)paramType, count, values, false);
    }
    else if(isPath)
    {
        RI_ASSERT(!isImage && !isPaint && !isFont);
        getPathParameterifv(context, (Path*)object, (VGPathParamType)paramType, count, values, false);
    }
    else if(isPaint)
    {
        RI_ASSERT(!isImage && !isPath && !isFont);
        getPaintParameterifv(context, (Paint*)object, (VGPaintParamType)paramType, count, values, false);
    }
    else
    {
        RI_ASSERT(!isImage && !isPath && !isPaint && isFont);
        getFontParameterifv(context, (Font*)object, (VGFontParamType)paramType, count, values, false);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGint RI_APIENTRY vgGetParameterVectorSize(VGHandle object, VGint paramType)
{
    RI_GET_CONTEXT(0);
    bool isImage = context->isValidImage(object);
    bool isPath = context->isValidPath(object);
    bool isPaint = context->isValidPaint(object);
    bool isFont = context->isValidFont(object);
    RI_IF_ERROR(!isImage && !isPath && !isPaint && !isFont, VG_BAD_HANDLE_ERROR, 0);	//invalid object handle
    int ret = 0;
    if(isImage)
    {
        RI_ASSERT(!isPath && !isPaint && !isFont);
        switch(paramType)
        {
        case VG_IMAGE_FORMAT:
        case VG_IMAGE_WIDTH:
        case VG_IMAGE_HEIGHT:
            ret = 1;
            break;

        default:
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid paramType
            break;
        }
    }
    else if(isPath)
    {
        RI_ASSERT(!isImage && !isPaint && !isFont);
        switch(paramType)
        {
        case VG_PATH_FORMAT:
        case VG_PATH_DATATYPE:
        case VG_PATH_SCALE:
        case VG_PATH_BIAS:
        case VG_PATH_NUM_SEGMENTS:
        case VG_PATH_NUM_COORDS:
            ret = 1;
            break;

        default:
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid paramType
            break;
        }
    }
    else if(isPaint)
    {
        RI_ASSERT(!isImage && !isPath && !isFont);
        switch(paramType)
        {
        case VG_PAINT_TYPE:
        case VG_PAINT_COLOR_RAMP_SPREAD_MODE:
        case VG_PAINT_PATTERN_TILING_MODE:
            ret = 1;
            break;

        case VG_PAINT_COLOR:
        case VG_PAINT_LINEAR_GRADIENT:
            ret = 4;
            break;

        case VG_PAINT_COLOR_RAMP_STOPS:
            ret = ((Paint*)object)->m_inputColorRampStops.size() * 5;
            break;

        case VG_PAINT_COLOR_RAMP_PREMULTIPLIED:
            ret = 1;
            break;

        case VG_PAINT_RADIAL_GRADIENT:
            ret = 5;
            break;

        default:
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid paramType
            break;
        }
    }
    else
    {
        RI_ASSERT(!isImage && !isPath && !isPaint && isFont);
        switch(paramType)
        {
        case VG_FONT_NUM_GLYPHS:
            ret = 1;
            break;

        default:
            context->setError(VG_ILLEGAL_ARGUMENT_ERROR);	//invalid paramType
            break;
        }
    }
    RI_RETURN(ret);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

static Matrix3x3* getCurrentMatrix(VGContext* context)
{
    RI_ASSERT(context);
    switch(context->m_matrixMode)
    {
    case VG_MATRIX_PATH_USER_TO_SURFACE:
        return &context->m_pathUserToSurface;

    case VG_MATRIX_IMAGE_USER_TO_SURFACE:
        return &context->m_imageUserToSurface;

    case VG_MATRIX_FILL_PAINT_TO_USER:
        return &context->m_fillPaintToUser;

    case VG_MATRIX_STROKE_PAINT_TO_USER:
        return &context->m_strokePaintToUser;

    default:
        RI_ASSERT(context->m_matrixMode == VG_MATRIX_GLYPH_USER_TO_SURFACE);
        return &context->m_glyphUserToSurface;
    }
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgLoadIdentity(void)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    Matrix3x3* d = getCurrentMatrix(context);
    d->identity();
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgLoadMatrix(const VGfloat * m)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!m || !isAligned(m,4), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    Matrix3x3* d = getCurrentMatrix(context);
    d->set(inputFloat(m[0]), inputFloat(m[3]), inputFloat(m[6]),
           inputFloat(m[1]), inputFloat(m[4]), inputFloat(m[7]),
           inputFloat(m[2]), inputFloat(m[5]), inputFloat(m[8]));
    if(context->m_matrixMode != VG_MATRIX_IMAGE_USER_TO_SURFACE)
    {
        (*d)[2].set(0,0,1);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgGetMatrix(VGfloat * m)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!m || !isAligned(m,4), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    Matrix3x3* d = getCurrentMatrix(context);
    m[0] = (*d)[0][0];
    m[1] = (*d)[1][0];
    m[2] = (*d)[2][0];
    m[3] = (*d)[0][1];
    m[4] = (*d)[1][1];
    m[5] = (*d)[2][1];
    m[6] = (*d)[0][2];
    m[7] = (*d)[1][2];
    m[8] = (*d)[2][2];
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgMultMatrix(const VGfloat * m)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!m || !isAligned(m,4), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    Matrix3x3 n(inputFloat(m[0]), inputFloat(m[3]), inputFloat(m[6]),
                inputFloat(m[1]), inputFloat(m[4]), inputFloat(m[7]),
                inputFloat(m[2]), inputFloat(m[5]), inputFloat(m[8]));
    if(context->m_matrixMode != VG_MATRIX_IMAGE_USER_TO_SURFACE)
        n[2].set(0,0,1);

    Matrix3x3* d = getCurrentMatrix(context);
    *d *= n;

    d->validate();
    //d->assertValid();

    if(context->m_matrixMode != VG_MATRIX_IMAGE_USER_TO_SURFACE)
    {
        (*d)[2].set(0,0,1);	//force affinity
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgTranslate(VGfloat tx, VGfloat ty)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    Matrix3x3 n(1, 0, inputFloat(tx),
                0, 1, inputFloat(ty),
                0, 0, 1 );
    Matrix3x3* d = getCurrentMatrix(context);
    *d *= n;

    d->validate();
    //d->assertValid();

    if(context->m_matrixMode != VG_MATRIX_IMAGE_USER_TO_SURFACE)
    {
        (*d)[2].set(0,0,1);	//force affinity
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgScale(VGfloat sx, VGfloat sy)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    Matrix3x3 n(inputFloat(sx), 0,              0,
                0,              inputFloat(sy), 0,
                0,              0,              1 );
    Matrix3x3* d = getCurrentMatrix(context);
    *d *= n;

    d->validate();
    //d->assertValid();

    if(context->m_matrixMode != VG_MATRIX_IMAGE_USER_TO_SURFACE)
    {
        (*d)[2].set(0,0,1);	//force affinity
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgShear(VGfloat shx, VGfloat shy)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    Matrix3x3 n(1,               inputFloat(shx), 0,
                inputFloat(shy), 1,               0,
                0,               0,               1);
    Matrix3x3* d = getCurrentMatrix(context);
    *d *= n;

    d->validate();
    //d->assertValid();

    if(context->m_matrixMode != VG_MATRIX_IMAGE_USER_TO_SURFACE)
    {
        (*d)[2].set(0,0,1);	//force affinity
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgRotate(VGfloat angle)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RIfloat a = RI_DEG_TO_RAD(inputFloat(angle));
    Matrix3x3 n((RIfloat)cos(a), -(RIfloat)sin(a), 0,
                (RIfloat)sin(a),  (RIfloat)cos(a), 0,
                0,              0,             1 );
    Matrix3x3* d = getCurrentMatrix(context);
    *d *= n;

    d->validate();
    //d->assertValid();

    if(context->m_matrixMode != VG_MATRIX_IMAGE_USER_TO_SURFACE)
    {
        (*d)[2].set(0,0,1);	//force affinity
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgMask(VGHandle mask, VGMaskOperation operation, VGint x, VGint y, VGint width, VGint height)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    const bool isImage = context->isValidImage(mask);
    const bool isMaskLayer = context->isValidMaskLayer(mask);
    const bool needInput = (operation != VG_CLEAR_MASK) && (operation != VG_FILL_MASK);
    RI_IF_ERROR(operation != VG_CLEAR_MASK && operation != VG_FILL_MASK && !isImage && !isMaskLayer, VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(operation != VG_CLEAR_MASK && operation != VG_FILL_MASK && isImage && eglvgIsInUse((Image*)mask), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(operation < VG_CLEAR_MASK || operation > VG_SUBTRACT_MASK, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(width <= 0 || height <= 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    Drawable* drawable = context->getCurrentDrawable();
    RI_IF_ERROR(isMaskLayer && drawable->getNumSamples() != ((Surface*)mask)->getNumSamples(), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    if(!drawable || !drawable->getMaskBuffer())
    {
        RI_RETURN(RI_NO_RETVAL);	//no EGL surface is current at the moment or context has no mask buffer
    }

    if(isImage)
        drawable->getMaskBuffer()->mask(context->getBlitter(), (Image*)mask, operation, x, y, width, height);
    else
    {
        RI_ASSERT(!isMaskLayer ? !needInput : true);
        drawable->getMaskBuffer()->mask(context->getBlitter(), (!mask || !needInput) ? NULL : ((Surface*)mask)->getImage(), operation, x, y, width, height);
    }

    RI_RETURN(RI_NO_RETVAL);
}

void RI_APIENTRY vgRenderToMask(VGPath path, VGbitfield paintModes, VGMaskOperation operation)
{
    Path* iPath = (Path*)path;
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidPath(path), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid path handle
    RI_IF_ERROR(!paintModes || (paintModes & ~(VG_FILL_PATH | VG_STROKE_PATH)), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);	//invalid paint mode
    RI_IF_ERROR(operation < VG_CLEAR_MASK || operation > VG_SUBTRACT_MASK, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    Drawable* curr = context->getCurrentDrawable();
    const Surface* mask = curr->getMaskBuffer();
    if(!curr || !curr->getMaskBuffer())
    {
        RI_RETURN(RI_NO_RETVAL);	//no EGL surface is current at the moment or context has no mask buffer
    }

    if(operation == VG_CLEAR_MASK || operation == VG_FILL_MASK)
    {
        //RI_ASSERT(false); // should call fill!
        Image* dummy = NULL;
        curr->getMaskBuffer()->mask(context->getBlitter(), dummy, operation, 0, 0, curr->getWidth(), curr->getHeight());
        RI_RETURN(RI_NO_RETVAL);
    }

    try
    {
        // Create a temporary image to render the path on for some mask operations.
        // \todo tero: There are optimizations that can do all the mask operations in one pass, but it
        // takes too much time to implement them now.
        Drawable *tmpDrawable = NULL;
        
        try 
        {
            if (operation == VG_INTERSECT_MASK)
            {
                tmpDrawable = RI_NEW(Drawable, (Color::formatToDescriptor(VG_A_8), curr->getWidth(), curr->getHeight(), 1, 0));
            }
        } catch (std::bad_alloc e)
        {
            throw e;
        }
         
        //Drawable drawable(Color::formatToDescriptor(VG_A_8), curr->getWidth(), curr->getHeight(), curr->getNumSamples(), 1);    //TODO 0 mask bits (mask buffer is not used)

        Rasterizer& rasterizer = context->m_rasterizer;
        rasterizer.clear();

        rasterizer.setScissoring(context->m_scissoring ? true : false);
        if(context->m_scissoring)
            rasterizer.setScissor(context->m_scissor);	//throws bad_alloc

        PixelPipe& pixelPipe = context->m_pixelPipe;

        // \hack If the mask operation is intersect, we render normally to tempbuffer!
        if (!tmpDrawable)
            pixelPipe.setRenderToMask(true);
        else
            pixelPipe.setRenderToMask(false);

        bool skipFill = false;

        if (operation == VG_SET_MASK)
        {
            Surface *maskSurface = curr->getMaskBuffer();
            maskSurface->clear(Color(), 0, 0, maskSurface->getWidth(), maskSurface->getHeight());
            skipFill = true;
        }

        skipFill = skipFill && (paintModes & VG_STROKE_PATH);

        pixelPipe.setMaskOperation(operation);
        pixelPipe.setDrawable(tmpDrawable ? tmpDrawable : curr);
        pixelPipe.setImage(NULL, VG_DRAW_IMAGE_NORMAL);
        pixelPipe.setMask(false);
        pixelPipe.setPaint(NULL);   //use default paint (solid color alpha = 1)
        pixelPipe.setBlendMode(VG_BLEND_SRC_OVER);   //write solid color * coverage to dest
        bool aa = context->m_renderingQuality == VG_RENDERING_QUALITY_NONANTIALIASED ? false : true;
        rasterizer.setAntiAliasing(aa);

        Matrix3x3 userToSurface = context->m_pathUserToSurface;
        userToSurface[2].set(0,0,1);	//force affinity

        if((paintModes & VG_FILL_PATH) && (!skipFill))
        {
            rasterizer.clear();
            rasterizer.setup(0, 0, mask->getWidth(), mask->getHeight(), context->m_fillRule, &pixelPipe);
            pixelPipe.prepareSpanUniforms(aa);
            iPath->fill(userToSurface, rasterizer);	//throws bad_alloc
            rasterizer.fill();	//throws bad_alloc
            //curr->getMaskBuffer()->mask(drawable.getColorBuffer(), operation, 0, 0, drawable.getWidth(), drawable.getHeight());
            if (tmpDrawable)
            {
                RI_ASSERT(operation == VG_INTERSECT_MASK);
                curr->getMaskBuffer()->mask(context->getBlitter(), tmpDrawable->getColorBuffer()->getImage(), operation, 0, 0, tmpDrawable->getWidth(), tmpDrawable->getHeight());

                if (paintModes & VG_STROKE_PATH)
                {
                    Surface* tmpSurf = tmpDrawable->getColorBuffer();
                    tmpSurf->clear(Color(), 0, 0, 
                        tmpSurf->getWidth(), tmpSurf->getHeight());
                }
                else
                {
                    RI_DELETE(tmpDrawable);
                    tmpDrawable = NULL;
                }
            }

        }

        if((paintModes & VG_STROKE_PATH) && context->m_strokeLineWidth > 0.0f)
        {
            pixelPipe.prepareSpanUniforms(aa);
            rasterizer.clear();
            rasterizer.setup(0, 0, mask->getWidth(), mask->getHeight(), VG_NON_ZERO, &pixelPipe);

            iPath->stroke(userToSurface, rasterizer, context->m_strokeDashPattern, context->m_strokeDashPhase, context->m_strokeDashPhaseReset ? true : false,
                 context->m_strokeLineWidth, context->m_strokeCapStyle, context->m_strokeJoinStyle, RI_MAX(context->m_strokeMiterLimit, 1.0f));	//throws bad_alloc
            rasterizer.fill();	//throws bad_alloc
            //curr->getMaskBuffer()->mask(drawable.getColorBuffer(), operation, 0, 0, drawable.getWidth(), drawable.getHeight());

            if (tmpDrawable)
            {
                RI_ASSERT(operation == VG_INTERSECT_MASK);
                curr->getMaskBuffer()->mask(context->getBlitter(), tmpDrawable->getColorBuffer()->getImage(), operation, 0, 0, tmpDrawable->getWidth(), tmpDrawable->getHeight());
                RI_DELETE(tmpDrawable);
                tmpDrawable = NULL;
            }
        }
        RI_ASSERT(!tmpDrawable);

    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGMaskLayer RI_APIENTRY vgCreateMaskLayer(VGint width, VGint height)
{
    RI_GET_CONTEXT(VG_INVALID_HANDLE);
    RI_IF_ERROR(width <= 0 || height <= 0, VG_ILLEGAL_ARGUMENT_ERROR, VG_INVALID_HANDLE);
    RI_IF_ERROR(width > RI_MAX_IMAGE_WIDTH || height > RI_MAX_IMAGE_HEIGHT || width*height > RI_MAX_IMAGE_PIXELS ||
                width*height > RI_MAX_IMAGE_BYTES, VG_ILLEGAL_ARGUMENT_ERROR, VG_INVALID_HANDLE);
    Drawable* curr = context->getCurrentDrawable();
    if(!curr || !curr->getMaskBuffer())
        RI_RETURN(VG_INVALID_HANDLE);   //no current drawing surface

    Surface* layer = NULL;
    try
    {
        layer = RI_NEW(Surface, (Color::formatToDescriptor(VG_A_8), width, height, curr->getNumSamples()));	//throws bad_alloc
        RI_ASSERT(layer);
        context->m_maskLayerManager->addResource(layer, context);	//throws bad_alloc
        layer->clear(Color(1,1,1,1,Color::sRGBA), 0, 0, width, height);
        RI_RETURN((VGMaskLayer)layer);
    }
    catch(std::bad_alloc)
    {
        RI_DELETE(layer);
        context->setError(VG_OUT_OF_MEMORY_ERROR);
        RI_RETURN(VG_INVALID_HANDLE);
    }
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgDestroyMaskLayer(VGMaskLayer maskLayer)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidMaskLayer(maskLayer), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid handle

    context->m_maskLayerManager->removeResource((Surface*)maskLayer);
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgFillMaskLayer(VGMaskLayer maskLayer, VGint x, VGint y, VGint width, VGint height, VGfloat value)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidMaskLayer(maskLayer), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid handle
    RI_IF_ERROR(value < 0.0f || value > 1.0f, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    Surface* layer = (Surface*)maskLayer;
    RI_IF_ERROR(width <= 0 || height <= 0 || x < 0 || y < 0 || x > layer->getWidth()-width || y > layer->getHeight()-height, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    layer->clear(Color(1,1,1,value,Color::sRGBA), x, y, width, height);
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgCopyMask(VGMaskLayer maskLayer, VGint dx, VGint dy, VGint sx, VGint sy, VGint width, VGint height)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidMaskLayer(maskLayer), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid handle
    Drawable* drawable = context->getCurrentDrawable();
    if(!drawable || !drawable->getMaskBuffer())
    {
        RI_RETURN(RI_NO_RETVAL);	//no EGL surface is current at the moment or context has no mask buffer
    }
    Surface* layer = (Surface*)maskLayer;
    RI_IF_ERROR(width <= 0 || height <= 0 || drawable->getNumSamples() != layer->getNumSamples(), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    try
    {   //copy drawing surface mask to mask layer
        // \note Removed support for msaa
        layer->m_image->blit(context, drawable->getMaskBuffer()->getImage(), sx, sy, dx, dy, width, height);
        //layer->blit(drawable->getMaskBuffer(), sx, sy, dx, dy, width, height);	//throws bad_alloc
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgClear(VGint x, VGint y, VGint width, VGint height)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(width <= 0 || height <= 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    Drawable* drawable = context->getCurrentDrawable();
    if(!drawable)
    {
        RI_RETURN(RI_NO_RETVAL);	//no EGL surface is current at the moment
    }

    try
    {
        if(context->m_scissoring)
            drawable->getColorBuffer()->clear(context->m_clearColor, x, y, width, height, &context->m_scissor);	//throws bad_alloc
        else
            drawable->getColorBuffer()->clear(context->m_clearColor, x, y, width, height);
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGPath RI_APIENTRY vgCreatePath(VGint pathFormat, VGPathDatatype datatype, VGfloat scale, VGfloat bias, VGint segmentCapacityHint, VGint coordCapacityHint, VGbitfield capabilities)
{
    RI_GET_CONTEXT(VG_INVALID_HANDLE);
    RI_IF_ERROR(pathFormat != VG_PATH_FORMAT_STANDARD, VG_UNSUPPORTED_PATH_FORMAT_ERROR, VG_INVALID_HANDLE);
    RI_IF_ERROR(datatype < VG_PATH_DATATYPE_S_8 || datatype > VG_PATH_DATATYPE_F, VG_ILLEGAL_ARGUMENT_ERROR, VG_INVALID_HANDLE);
    RIfloat s = inputFloat(scale);
    RIfloat b = inputFloat(bias);
    RI_IF_ERROR(s == 0.0f, VG_ILLEGAL_ARGUMENT_ERROR, VG_INVALID_HANDLE);
    capabilities &= VG_PATH_CAPABILITY_ALL;	//undefined bits are ignored

    Path* path = NULL;
    try
    {
        path = RI_NEW(Path, (pathFormat, datatype, s, b, segmentCapacityHint, coordCapacityHint, capabilities));	//throws bad_alloc
        RI_ASSERT(path);
        context->m_pathManager->addResource(path, context);	//throws bad_alloc
        RI_RETURN((VGPath)path);
    }
    catch(std::bad_alloc)
    {
        RI_DELETE(path);
        context->setError(VG_OUT_OF_MEMORY_ERROR);
        RI_RETURN(VG_INVALID_HANDLE);
    }
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgClearPath(VGPath path, VGbitfield capabilities)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidPath(path), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid path handle
    capabilities &= VG_PATH_CAPABILITY_ALL;	//undefined bits are ignored
    ((Path*)path)->clear(capabilities);
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgDestroyPath(VGPath path)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidPath(path), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid path handle

    context->m_pathManager->removeResource((Path*)path);

    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgRemovePathCapabilities(VGPath path, VGbitfield capabilities)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidPath(path), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid path handle
    capabilities &= VG_PATH_CAPABILITY_ALL;	//undefined bits are ignored

    VGbitfield caps = ((Path*)path)->getCapabilities();
    caps &= ~capabilities;
    ((Path*)path)->setCapabilities(caps);
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGbitfield RI_APIENTRY vgGetPathCapabilities(VGPath path)
{
    RI_GET_CONTEXT(0);
    RI_IF_ERROR(!context->isValidPath(path), VG_BAD_HANDLE_ERROR, 0);	//invalid path handle
    VGbitfield ret = ((Path*)path)->getCapabilities();
    RI_RETURN(ret);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgAppendPathData(VGPath dstPath, VGint numSegments, const VGubyte * pathSegments, const void * pathData)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidPath(dstPath), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid path handle
    Path* p = (Path*)dstPath;
    RI_IF_ERROR(!(p->getCapabilities() & VG_PATH_CAPABILITY_APPEND_TO), VG_PATH_CAPABILITY_ERROR, RI_NO_RETVAL);	//no append cap
    RI_IF_ERROR(numSegments <= 0 || !pathSegments || !pathData, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);	//no segments or data
    RI_IF_ERROR((p->getDatatype() == VG_PATH_DATATYPE_S_16 && !isAligned(pathData,2)) ||
                ((p->getDatatype() == VG_PATH_DATATYPE_S_32 || p->getDatatype() == VG_PATH_DATATYPE_F) && !isAligned(pathData,4)),
                VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);	//invalid alignment
    for(int i=0;i<numSegments;i++)
    {
        VGPathSegment c = (VGPathSegment)(pathSegments[i] & 0x1e);
        RI_IF_ERROR(c < VG_CLOSE_PATH || c > VG_LCWARC_TO, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);	//invalid segment
        RI_IF_ERROR(c & ~0x1f, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);	//reserved bits are nonzero
    }

    try
    {
        p->appendData((const RIuint8*)pathSegments, numSegments, (const RIuint8*)pathData);	//throws bad_alloc
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgModifyPathCoords(VGPath dstPath, VGint startIndex, VGint numSegments, const void * pathData)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidPath(dstPath), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid path handle
    Path* p = (Path*)dstPath;
    RI_IF_ERROR(!(p->getCapabilities() & VG_PATH_CAPABILITY_MODIFY), VG_PATH_CAPABILITY_ERROR, RI_NO_RETVAL);	//no modify cap
    RI_IF_ERROR(!pathData || startIndex < 0 || numSegments <= 0 || RI_INT_ADDSATURATE(startIndex, numSegments) > p->getNumSegments(), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);	//no segments
    RI_IF_ERROR((p->getDatatype() == VG_PATH_DATATYPE_S_16 && !isAligned(pathData,2)) ||
                ((p->getDatatype() == VG_PATH_DATATYPE_S_32 || p->getDatatype() == VG_PATH_DATATYPE_F) && !isAligned(pathData,4)),
                VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);	//invalid alignment
    p->modifyCoords(startIndex, numSegments, (const RIuint8*)pathData);
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgAppendPath(VGPath dstPath, VGPath srcPath)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidPath(dstPath) || !context->isValidPath(srcPath), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid path handle
    RI_IF_ERROR(!(((Path*)dstPath)->getCapabilities() & VG_PATH_CAPABILITY_APPEND_TO) ||
                !(((Path*)srcPath)->getCapabilities() & VG_PATH_CAPABILITY_APPEND_FROM), VG_PATH_CAPABILITY_ERROR, RI_NO_RETVAL);	//invalid caps

    try
    {
        ((Path*)dstPath)->append((Path*)srcPath);	//throws bad_alloc
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgTransformPath(VGPath dstPath, VGPath srcPath)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidPath(dstPath) || !context->isValidPath(srcPath), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid path handle
    RI_IF_ERROR(!(((Path*)dstPath)->getCapabilities() & VG_PATH_CAPABILITY_TRANSFORM_TO) ||
                !(((Path*)srcPath)->getCapabilities() & VG_PATH_CAPABILITY_TRANSFORM_FROM), VG_PATH_CAPABILITY_ERROR, RI_NO_RETVAL);	//invalid caps
    try
    {
        ((Path*)dstPath)->transform((Path*)srcPath, context->m_pathUserToSurface);	//throws bad_alloc
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

static bool drawPath(VGContext* context, VGPath path, const Matrix3x3& userToSurfaceMatrix, VGbitfield paintModes)
{
    Path* iPath = (Path*)path;
    //set up rendering surface and mask buffer
    Drawable* drawable = context->getCurrentDrawable();
    if(!drawable)
        return false;   //no EGL surface is current at the moment

    Rasterizer& rasterizer = context->m_rasterizer;
    rasterizer.clear();

    if(context->m_scissoring)
    {
        rasterizer.setScissor(context->m_scissor);	//throws bad_alloc
    }
    rasterizer.setScissoring(context->m_scissoring ? true : false);

    PixelPipe& pixelPipe = context->m_pixelPipe;
    pixelPipe.setRenderToMask(false);
    pixelPipe.setDrawable(drawable);
    pixelPipe.setImage(NULL, VG_DRAW_IMAGE_NORMAL);
    pixelPipe.setMask(context->m_masking ? true : false);
    pixelPipe.setBlendMode(context->m_blendMode);
    pixelPipe.setTileFillColor(context->m_tileFillColor);
    pixelPipe.setImageQuality(context->m_imageQuality);
    pixelPipe.setColorTransform(context->m_colorTransform ? true : false, context->m_colorTransformValues);
    bool aa = context->m_renderingQuality == VG_RENDERING_QUALITY_NONANTIALIASED ? false : true;
    rasterizer.setAntiAliasing(aa);

    Matrix3x3 userToSurface = userToSurfaceMatrix;
    userToSurface[2].set(0,0,1);	//force affinity

    if(paintModes & VG_FILL_PATH)
    {
        // \todo Reorganize so that pipe.setpaint handles/requires the matrix setup?
        pixelPipe.setPaint((Paint*)context->m_fillPaint);

        Matrix3x3 paintToSurfaceMatrix = userToSurface * context->m_fillPaintToUser;
        Matrix3x3 surfaceToPaintMatrix = paintToSurfaceMatrix; 
        if(surfaceToPaintMatrix.invert())
        {
            surfaceToPaintMatrix[2].set(0,0,1);		//force affinity
            pixelPipe.setSurfaceToPaintMatrix(surfaceToPaintMatrix);
            pixelPipe.prepareSpanUniforms(aa);

            rasterizer.clear();
            rasterizer.setup(0, 0, drawable->getWidth(), drawable->getHeight(), context->m_fillRule, &pixelPipe);
            iPath->fill(userToSurface, rasterizer);	//throws bad_alloc
            rasterizer.fill();	//throws bad_alloc
        }
    }

    if(paintModes & VG_STROKE_PATH && context->m_strokeLineWidth > 0.0f)
    {
        pixelPipe.setPaint((Paint*)context->m_strokePaint);

        Matrix3x3 paintToSurfaceMatrix = userToSurface * context->m_strokePaintToUser;
        Matrix3x3 surfaceToPaintMatrix = paintToSurfaceMatrix; 
        if(surfaceToPaintMatrix.invert())
        {
            surfaceToPaintMatrix[2].set(0,0,1);		//force affinity
            pixelPipe.setSurfaceToPaintMatrix(surfaceToPaintMatrix);
            pixelPipe.prepareSpanUniforms(aa);

            rasterizer.clear();
            rasterizer.setup(0, 0, drawable->getWidth(), drawable->getHeight(), VG_NON_ZERO, &pixelPipe);
            iPath->stroke(userToSurface, rasterizer, context->m_strokeDashPattern, context->m_strokeDashPhase, context->m_strokeDashPhaseReset ? true : false,
                 context->m_strokeLineWidth, context->m_strokeCapStyle, context->m_strokeJoinStyle, RI_MAX(context->m_strokeMiterLimit, 1.0f));	//throws bad_alloc
            rasterizer.fill();	//throws bad_alloc

        }
    }
    return true;
}

void RI_APIENTRY vgDrawPath(VGPath path, VGbitfield paintModes)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidPath(path), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid path handle
    RI_IF_ERROR(!paintModes || (paintModes & ~(VG_FILL_PATH | VG_STROKE_PATH)), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);	//invalid paint mode

    try
    {
		eglvgLockSurface(0, 1);
        if(!drawPath(context, path, context->m_pathUserToSurface, paintModes))
        {
			eglvgUnlockSurface();
            RI_RETURN(RI_NO_RETVAL);
        }
		eglvgUnlockSurface();
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGfloat RI_APIENTRY vgPathLength(VGPath path, VGint startSegment, VGint numSegments)
{
    RI_GET_CONTEXT(-1.0f);
    RI_IF_ERROR(!context->isValidPath(path), VG_BAD_HANDLE_ERROR, -1.0f);	//invalid path handle
    Path* p = (Path*)path;
    RI_IF_ERROR(!(p->getCapabilities() & VG_PATH_CAPABILITY_PATH_LENGTH), VG_PATH_CAPABILITY_ERROR, -1.0f);	//invalid caps
    RI_IF_ERROR(startSegment < 0 || numSegments <= 0 || RI_INT_ADDSATURATE(startSegment, numSegments) > p->getNumSegments(), VG_ILLEGAL_ARGUMENT_ERROR, -1.0f);
    RIfloat pathLength = -1.0f;
    try
    {
        pathLength = p->getPathLength(startSegment, numSegments);	//throws bad_alloc
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(pathLength);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgPointAlongPath(VGPath path, VGint startSegment, VGint numSegments, VGfloat distance, VGfloat * x, VGfloat * y, VGfloat * tangentX, VGfloat * tangentY)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidPath(path), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid path handle
    Path* p = (Path*)path;
    RI_IF_ERROR((x && y && !(p->getCapabilities() & VG_PATH_CAPABILITY_POINT_ALONG_PATH)) ||
                (tangentX && tangentY && !(p->getCapabilities() & VG_PATH_CAPABILITY_TANGENT_ALONG_PATH)), VG_PATH_CAPABILITY_ERROR, RI_NO_RETVAL);	//invalid caps
    RI_IF_ERROR(startSegment < 0 || numSegments <= 0 || RI_INT_ADDSATURATE(startSegment, numSegments) > p->getNumSegments(), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!isAligned(x,4) || !isAligned(y,4) || !isAligned(tangentX,4) || !isAligned(tangentY,4), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    try
    {
        Vector2 point, tangent;
        p->getPointAlong(startSegment, numSegments, distance, point, tangent);	//throws bad_alloc
        if(x && y)
        {
            *x = point.x;
            *y = point.y;
        }
        if(tangentX && tangentY)
        {
            tangent.normalize();
            *tangentX = tangent.x;
            *tangentY = tangent.y;
        }
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgPathBounds(VGPath path, VGfloat * minx, VGfloat * miny, VGfloat * width, VGfloat * height)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidPath(path), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid path handle
    RI_IF_ERROR(!(((Path*)path)->getCapabilities() & VG_PATH_CAPABILITY_PATH_BOUNDS), VG_PATH_CAPABILITY_ERROR, RI_NO_RETVAL);	//invalid caps
    RI_IF_ERROR(!minx || !miny || !width || !height, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!isAligned(minx,4) || !isAligned(miny,4) || !isAligned(width,4) || !isAligned(height,4), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    try
    {
        RIfloat pminx,pminy,pmaxx,pmaxy;
        ((Path*)path)->getPathBounds(pminx, pminy, pmaxx, pmaxy);	//throws bad_alloc
        *minx = pminx;
        *miny = pminy;
        *width = pmaxx - pminx;
        *height = pmaxy - pminy;
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgPathTransformedBounds(VGPath path, VGfloat * minx, VGfloat * miny, VGfloat * width, VGfloat * height)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidPath(path), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid path handle
    RI_IF_ERROR(!(((Path*)path)->getCapabilities() & VG_PATH_CAPABILITY_PATH_TRANSFORMED_BOUNDS), VG_PATH_CAPABILITY_ERROR, RI_NO_RETVAL);	//invalid caps
    RI_IF_ERROR(!minx || !miny || !width || !height, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!isAligned(minx,4) || !isAligned(miny,4) || !isAligned(width,4) || !isAligned(height,4), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    try
    {
        RIfloat pminx, pminy, pmaxx, pmaxy;
        ((Path*)path)->getPathTransformedBounds(context->m_pathUserToSurface, pminx, pminy, pmaxx, pmaxy);	//throws bad_alloc
        *minx = pminx;
        *miny = pminy;
        *width = pmaxx - pminx;
        *height = pmaxy - pminy;
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGboolean RI_APIENTRY vgInterpolatePath(VGPath dstPath, VGPath startPath, VGPath endPath, VGfloat amount)
{
    RI_GET_CONTEXT(VG_FALSE);
    RI_IF_ERROR(!context->isValidPath(dstPath) || !context->isValidPath(startPath) || !context->isValidPath(endPath), VG_BAD_HANDLE_ERROR, VG_FALSE);	//invalid path handle
    RI_IF_ERROR(!(((Path*)dstPath)->getCapabilities() & VG_PATH_CAPABILITY_INTERPOLATE_TO) ||
                !(((Path*)startPath)->getCapabilities() & VG_PATH_CAPABILITY_INTERPOLATE_FROM) ||
                !(((Path*)endPath)->getCapabilities() & VG_PATH_CAPABILITY_INTERPOLATE_FROM), VG_PATH_CAPABILITY_ERROR, VG_FALSE);	//invalid caps
    VGboolean ret = VG_FALSE;
    try
    {
        if(((Path*)dstPath)->interpolate((const Path*)startPath, (const Path*)endPath, inputFloat(amount)))	//throws bad_alloc
            ret = VG_TRUE;
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(ret);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGPaint RI_APIENTRY vgCreatePaint(void)
{
    RI_GET_CONTEXT(VG_INVALID_HANDLE);
    Paint* paint = NULL;
    RI_TRACE("\n***** vgCreatePaint\n");
    try
    {
        paint = RI_NEW(Paint, ());	//throws bad_alloc
        RI_ASSERT(paint);
        context->m_paintManager->addResource(paint, context);	//throws bad_alloc
        RI_TRACE("===== vgCreatePaint: Created paint obj: %x\n", (int)paint);
        RI_RETURN((VGPaint)paint);
    }
    catch(std::bad_alloc)
    {
        RI_DELETE(paint);
        context->setError(VG_OUT_OF_MEMORY_ERROR);
        RI_RETURN(VG_INVALID_HANDLE);
    }
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgDestroyPaint(VGPaint paint)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidPaint(paint), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid paint handle

    context->m_paintManager->removeResource((Paint*)paint);

    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgSetPaint(VGPaint paint, VGbitfield paintModes)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_TRACE("\n***** vgSetPaint: %x mode: %d\n", (int)paint, (int)paintModes);
    RI_IF_ERROR(paint && !context->isValidPaint(paint), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid paint handle
    RI_IF_ERROR(!paintModes || paintModes & ~(VG_FILL_PATH | VG_STROKE_PATH), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);	//invalid paint mode

    context->releasePaint(paintModes);

    if(paintModes & VG_FILL_PATH)
    {
        if(paint)
            ((Paint*)paint)->addReference();
        context->m_fillPaint = paint;
    }
    if(paintModes & VG_STROKE_PATH)
    {
        if(paint)
            ((Paint*)paint)->addReference();
        context->m_strokePaint = paint;
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgSetColor(VGPaint paint, VGuint rgba)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_TRACE("\n***** vgSetColor: %x, rgba: %x\n", paint, rgba);
    RI_IF_ERROR(!context->isValidPaint(paint), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid paint handle
    Paint* p = (Paint*)paint;
    p->m_inputPaintColor.unpack(rgba, Color::formatToDescriptor(VG_sRGBA_8888));
    p->setColor(inputColor(p->m_inputPaintColor));
    RI_TRACE("** -> [%.3f %.3f %.3f %.3f]\n", p->m_paintColor.r, p->m_paintColor.g, p->m_paintColor.b, p->m_paintColor.a);
//    p->m_paintColor = inputColor(p->m_inputPaintColor);
//    p->m_paintColor.clamp();
//    p->m_paintColor.premultiply();
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGuint RI_APIENTRY vgGetColor(VGPaint paint)
{
    RI_GET_CONTEXT(0);
    RI_IF_ERROR(!context->isValidPaint(paint), VG_BAD_HANDLE_ERROR, 0);	//invalid paint handle
    Color tempColor = ((Paint*)paint)->m_inputPaintColor;
    tempColor.clamp();
    unsigned int ret = tempColor.pack(Color::formatToDescriptor(VG_sRGBA_8888));
    RI_RETURN(ret);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGPaint RI_APIENTRY vgGetPaint(VGPaintMode paintMode)
{
    RI_GET_CONTEXT(VG_INVALID_HANDLE);
    RI_IF_ERROR(paintMode != VG_FILL_PATH && paintMode != VG_STROKE_PATH, VG_ILLEGAL_ARGUMENT_ERROR, VG_INVALID_HANDLE);	//invalid paint mode

    if(paintMode == VG_FILL_PATH)
    {
        RI_RETURN(context->m_fillPaint);
    }
    RI_RETURN(context->m_strokePaint);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgPaintPattern(VGPaint paint, VGImage image)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidPaint(paint) || (image != VG_INVALID_HANDLE && !context->isValidImage(image)), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid handle
    Image* img = (Image*)image;
    Paint* pnt = (Paint*)paint;
    RI_IF_ERROR(image != VG_INVALID_HANDLE && eglvgIsInUse(img), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);
    Image* pattern = pnt->m_pattern;
    if(pattern)
    {
        pattern->removeInUse();
        if(!pattern->removeReference())
            RI_DELETE(pattern);
    }
    pnt->m_pattern = img;

    if(img)
    {
        img->addReference();
        img->addInUse();
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGImage RI_APIENTRY vgCreateImage(VGImageFormat format, VGint width, VGint height, VGbitfield allowedQuality)
{
    RI_GET_CONTEXT(VG_INVALID_HANDLE);
    RI_IF_ERROR(!isValidImageFormat(format), VG_UNSUPPORTED_IMAGE_FORMAT_ERROR, VG_INVALID_HANDLE);
    RI_IF_ERROR(width <= 0 || height <= 0 || !allowedQuality ||
                (allowedQuality & ~(VG_IMAGE_QUALITY_NONANTIALIASED | VG_IMAGE_QUALITY_FASTER | VG_IMAGE_QUALITY_BETTER)), VG_ILLEGAL_ARGUMENT_ERROR, VG_INVALID_HANDLE);
    RI_IF_ERROR(width > RI_MAX_IMAGE_WIDTH || height > RI_MAX_IMAGE_HEIGHT || width*height > RI_MAX_IMAGE_PIXELS ||
                ((width*Color::formatToDescriptor(format).bitsPerPixel+7)/8)*height > RI_MAX_IMAGE_BYTES, VG_ILLEGAL_ARGUMENT_ERROR, VG_INVALID_HANDLE);

    Image* image = NULL;
    try
    {
        image = RI_NEW(Image, (Color::formatToDescriptor(format), width, height, allowedQuality));	//throws bad_alloc
        RI_ASSERT(image);
        context->m_imageManager->addResource(image, context);	//throws bad_alloc
        RI_RETURN((VGImage)image);
    }
    catch(std::bad_alloc)
    {
        RI_DELETE(image);
        context->setError(VG_OUT_OF_MEMORY_ERROR);
        RI_RETURN(VG_INVALID_HANDLE);
    }
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgDestroyImage(VGImage image)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidImage(image), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid image handle

    context->m_imageManager->removeResource((Image*)image);

    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgClearImage(VGImage image, VGint x, VGint y, VGint width, VGint height)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidImage(image), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);
    Image* img = (Image*)image;
    RI_IF_ERROR(eglvgIsInUse(img), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(width <= 0 || height <= 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
	eglvgLockSurface(0, 1);
    img->clear(context->m_clearColor, x, y, width, height);
	eglvgUnlockSurface();
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgImageSubData(VGImage image, const void * data, VGint dataStride, VGImageFormat dataFormat, VGint x, VGint y, VGint width, VGint height)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidImage(image), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);
    Image* img = (Image*)image;
    RI_IF_ERROR(eglvgIsInUse(img), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!isValidImageFormat(dataFormat), VG_UNSUPPORTED_IMAGE_FORMAT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!data || !isAligned(data, dataFormat) || width <= 0 || height <= 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    {
        Image input(Color::formatToDescriptor(dataFormat), width, height, dataStride, const_cast<RIuint8*>((const RIuint8*)data));
        input.addReference();
        try
        {
            img->blit(context, &input, 0, 0, x, y, width, height);	//throws bad_alloc
        }
        catch(std::bad_alloc)
        {
        }
        input.removeReference();
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgGetImageSubData(VGImage image, void * data, VGint dataStride, VGImageFormat dataFormat, VGint x, VGint y, VGint width, VGint height)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidImage(image), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);
    Image* img = (Image*)image;
    RI_IF_ERROR(eglvgIsInUse(img), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!isValidImageFormat(dataFormat), VG_UNSUPPORTED_IMAGE_FORMAT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!data || !isAligned(data, dataFormat) || width <= 0 || height <= 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    {
        Image output(Color::formatToDescriptor(dataFormat), width, height, dataStride, (RIuint8*)data);
        output.addReference();
        try
        {
            output.blit(context, img, x, y, 0, 0, width, height);	//throws bad_alloc
        }
        catch(std::bad_alloc)
        {
        }
        output.removeReference();
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGImage RI_APIENTRY vgChildImage(VGImage parent, VGint x, VGint y, VGint width, VGint height)
{
    RI_GET_CONTEXT(VG_INVALID_HANDLE);
    RI_IF_ERROR(!context->isValidImage(parent), VG_BAD_HANDLE_ERROR, VG_INVALID_HANDLE);
    Image* p = (Image*)parent;
    RI_IF_ERROR(eglvgIsInUse((Image*)parent), VG_IMAGE_IN_USE_ERROR, VG_INVALID_HANDLE);
    RI_IF_ERROR(x < 0 || x >= p->getWidth() || y < 0 || y >= p->getHeight() ||
                width <= 0 || height <= 0 || RI_INT_ADDSATURATE(x, width) > p->getWidth() || RI_INT_ADDSATURATE(y, height) > p->getHeight(), VG_ILLEGAL_ARGUMENT_ERROR, VG_INVALID_HANDLE);

    Image* child = NULL;
    try
    {
        child = RI_NEW(Image, (p, x, y, width, height));	//throws bad_alloc
        RI_ASSERT(child);
        context->m_imageManager->addResource(child, context);	//throws bad_alloc
        RI_RETURN((VGImage)child);
    }
    catch(std::bad_alloc)
    {
        RI_DELETE(child);
        context->setError(VG_OUT_OF_MEMORY_ERROR);
        RI_RETURN(VG_INVALID_HANDLE);
    }
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGImage RI_APIENTRY vgGetParent(VGImage image)
{
    RI_GET_CONTEXT(VG_INVALID_HANDLE);
    RI_IF_ERROR(!context->isValidImage(image), VG_BAD_HANDLE_ERROR, VG_INVALID_HANDLE);
    VGImage ret = image;	//if image has no ancestors, image is returned.

    //The vgGetParent function returns the closest valid ancestor (i.e., one that has not been the target of a vgDestroyImage call)
    // of the given image.
    Image* im = ((Image*)image)->getParent();
    for(;im;im = im->getParent())
    {
        if(context->isValidImage((VGImage)im))
        {	//the parent is valid and alive
            ret = (VGImage)im;
            break;
        }
    }
    RI_RETURN(ret);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgCopyImage(VGImage dst, VGint dx, VGint dy, VGImage src, VGint sx, VGint sy, VGint width, VGint height, VGboolean dither)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidImage(dst) || !context->isValidImage(src), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(eglvgIsInUse((Image*)dst) || eglvgIsInUse((Image*)src), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(width <= 0 || height <= 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    try
    {
        ((Image*)dst)->blit(context, (Image*)src, sx, sy, dx, dy, width, height, NULL, dither ? true : false);	//throws bad_alloc
    }
    catch(std::bad_alloc)
    {
    }
    RI_RETURN(RI_NO_RETVAL);
}
 

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

static bool drawImage(VGContext* context, VGImage image, const Matrix3x3& userToSurfaceMatrix)
{
    Drawable* drawable = context->getCurrentDrawable();
    if(!drawable)
        return false;   //no EGL surface is current at the moment

    Image* img = (Image*)image;
    //transform image corners into the surface space
    Vector3 p0(0, 0, 1);
    Vector3 p1(0, (RIfloat)img->getHeight(), 1);
    Vector3 p2((RIfloat)img->getWidth(), (RIfloat)img->getHeight(), 1);
    Vector3 p3((RIfloat)img->getWidth(), 0, 1);
    p0 = userToSurfaceMatrix * p0;
    p1 = userToSurfaceMatrix * p1;
    p2 = userToSurfaceMatrix * p2;
    p3 = userToSurfaceMatrix * p3;
    if(p0.z <= 0.0f || p1.z <= 0.0f || p2.z <= 0.0f || p3.z <= 0.0f)
        return false;

    //projection
    p0 *= 1.0f/p0.z;
    p1 *= 1.0f/p1.z;
    p2 *= 1.0f/p2.z;
    p3 *= 1.0f/p3.z;

    Rasterizer& rasterizer = context->m_rasterizer;
    rasterizer.clear();

    rasterizer.setScissoring(context->m_scissoring ? true : false);
    if(context->m_scissoring)
        rasterizer.setScissor(context->m_scissor);	//throws bad_alloc

    PixelPipe& pixelPipe = context->m_pixelPipe;
    pixelPipe.setTileFillColor(context->m_tileFillColor);
    pixelPipe.setPaint((Paint*)context->m_fillPaint);
    pixelPipe.setImageQuality(context->m_imageQuality);
    pixelPipe.setBlendMode(context->m_blendMode);
    pixelPipe.setRenderToMask(false);
    pixelPipe.setDrawable(drawable);
    pixelPipe.setMask(context->m_masking ? true : false);
    pixelPipe.setColorTransform(context->m_colorTransform ? true : false, context->m_colorTransformValues);
    const bool aa = context->m_renderingQuality == VG_RENDERING_QUALITY_NONANTIALIASED ? false : true;
    rasterizer.setAntiAliasing(aa);

    Matrix3x3 surfaceToImageMatrix = userToSurfaceMatrix;
    Matrix3x3 surfaceToPaintMatrix = userToSurfaceMatrix * context->m_fillPaintToUser;
    if(surfaceToImageMatrix.invert() && surfaceToPaintMatrix.invert())
    {
        VGImageMode imode = context->m_imageMode;
        if(!surfaceToPaintMatrix.isAffine())
            imode = VG_DRAW_IMAGE_NORMAL;	//if paint matrix is not affine, always use normal image mode
        surfaceToPaintMatrix[2].set(0,0,1);	//force affine

        pixelPipe.setImage(img, imode);
        pixelPipe.setSurfaceToPaintMatrix(surfaceToPaintMatrix);
        pixelPipe.setSurfaceToImageMatrix(surfaceToImageMatrix);
        pixelPipe.prepareSpanUniforms(aa);
        rasterizer.setup(0, 0, drawable->getWidth(), drawable->getHeight(), VG_EVEN_ODD, &pixelPipe);

        rasterizer.addEdge(Vector2(p0.x,p0.y), Vector2(p1.x,p1.y));	//throws bad_alloc
        rasterizer.addEdge(Vector2(p1.x,p1.y), Vector2(p2.x,p2.y));	//throws bad_alloc
        rasterizer.addEdge(Vector2(p2.x,p2.y), Vector2(p3.x,p3.y));	//throws bad_alloc
        rasterizer.addEdge(Vector2(p3.x,p3.y), Vector2(p0.x,p0.y));	//throws bad_alloc

        rasterizer.fill();	//throws bad_alloc
    }
    return true;
}

void RI_APIENTRY vgDrawImage(VGImage image)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidImage(image), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);
    Image* img = (Image*)image;
    RI_IF_ERROR(eglvgIsInUse(img), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);

    try
    {
        if(!drawImage(context, image, context->m_imageUserToSurface))
        {
            RI_RETURN(RI_NO_RETVAL);
        }
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgSetPixels(VGint dx, VGint dy, VGImage src, VGint sx, VGint sy, VGint width, VGint height)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidImage(src), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(eglvgIsInUse((Image*)src), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(width <= 0 || height <= 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);

    Drawable* drawable = context->getCurrentDrawable();
    Array<OpenVGRI::Rectangle>* scissors = !context->m_scissoring ? NULL : &context->m_scissor;
    if(!drawable)
    {
        RI_RETURN(RI_NO_RETVAL);	//no EGL surface is current at the moment
    }
    try
    {
        drawable->getColorBuffer()->m_image->blit(context, (Image*)src, sx, sy, dx, dy, width, height, scissors, false);	//throws bad_alloc
        //drawable->getColorBuffer()->blit(*(Image*)src, sx, sy, dx, dy, width, height);	//throws bad_alloc
    }
    catch(std::bad_alloc)
    {
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgWritePixels(const void * data, VGint dataStride, VGImageFormat dataFormat, VGint dx, VGint dy, VGint width, VGint height)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!isValidImageFormat(dataFormat), VG_UNSUPPORTED_IMAGE_FORMAT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!data || !isAligned(data, dataFormat) || width <= 0 || height <= 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    Drawable* drawable = context->getCurrentDrawable();
    if(!drawable)
    {
        RI_RETURN(RI_NO_RETVAL);	//no EGL surface is current at the moment
    }
    {
        Array<OpenVGRI::Rectangle>* scissors = !context->m_scissoring ? NULL : &context->m_scissor;
        Image input(Color::formatToDescriptor(dataFormat), width, height, dataStride, const_cast<RIuint8*>((const RIuint8*)data));
        input.addReference();
        try
        {
            drawable->getColorBuffer()->m_image->blit(context, &input, 0, 0, dx, dy, width, height, scissors, false);	//throws bad_alloc
        }
        catch(std::bad_alloc)
        {
            // TEROP: out-of-mem?
        }
        input.removeReference();
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgGetPixels(VGImage dst, VGint dx, VGint dy, VGint sx, VGint sy, VGint width, VGint height)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidImage(dst), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(eglvgIsInUse((Image*)dst), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(width <= 0 || height <= 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    Drawable* drawable = context->getCurrentDrawable();
    if(!drawable)
    {
        RI_RETURN(RI_NO_RETVAL);	//no EGL surface is current at the moment
    }
    try
    {
        ((Image*)dst)->blit(context, drawable->getColorBuffer()->m_image, sx, sy, dx, dy, width, height);	//throws bad_alloc
    }
    catch(std::bad_alloc)
    {
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgReadPixels(void* data, VGint dataStride, VGImageFormat dataFormat, VGint sx, VGint sy, VGint width, VGint height)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!isValidImageFormat(dataFormat), VG_UNSUPPORTED_IMAGE_FORMAT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!data || !isAligned(data, dataFormat) || width <= 0 || height <= 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    Drawable* drawable = context->getCurrentDrawable();
    if(!drawable)
    {
        RI_RETURN(RI_NO_RETVAL);	//no EGL surface is current at the moment
    }
    {
        Image output(Color::formatToDescriptor(dataFormat), width, height, dataStride, (RIuint8*)data);
        output.addReference();
        try
        {
            output.blit(context, drawable->getColorBuffer()->m_image, sx, sy, 0, 0, width, height);	//throws bad_alloc
        }
        catch(std::bad_alloc)
        {
        }
        output.removeReference();
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgCopyPixels(VGint dx, VGint dy, VGint sx, VGint sy, VGint width, VGint height)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(width <= 0 || height <= 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    Drawable* drawable = context->getCurrentDrawable();
    Array<OpenVGRI::Rectangle>* scissors = !context->m_scissoring ? NULL : &context->m_scissor;
    if(!drawable)
    {
        RI_RETURN(RI_NO_RETVAL);	//no EGL surface is current at the moment
    }
    try
    {
        drawable->getColorBuffer()->m_image->blit(context, drawable->getColorBuffer()->m_image, sx, sy, dx, dy, width, height, scissors, false);	//throws bad_alloc
        //drawable->getColorBuffer()->blit(drawable->getColorBuffer(), sx, sy, dx, dy, width, height);	//throws bad_alloc
    }
    catch(std::bad_alloc)
    {
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgColorMatrix(VGImage dst, VGImage src, const VGfloat * matrix)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidImage(dst) || !context->isValidImage(src), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);
    Image* d = (Image*)dst;
    Image* s = (Image*)src;
    RI_IF_ERROR(eglvgIsInUse(d) || eglvgIsInUse(s), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(d->overlaps(s), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!matrix || !isAligned(matrix,4), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    unsigned int channelMask = context->m_filterChannelMask & (VG_RED|VG_GREEN|VG_BLUE|VG_ALPHA);	//undefined bits are ignored

    RIfloat m[20];
    for(int i=0;i<20;i++)
    {
        m[i] = inputFloat(matrix[i]);
    }
    try
    {
        d->colorMatrix(*s, m, context->m_filterFormatLinear ? true : false, context->m_filterFormatPremultiplied ? true : false, channelMask);
    }
    catch(std::bad_alloc)
    {
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgConvolve(VGImage dst, VGImage src, VGint kernelWidth, VGint kernelHeight, VGint shiftX, VGint shiftY, const VGshort * kernel, VGfloat scale, VGfloat bias, VGTilingMode tilingMode)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidImage(dst) || !context->isValidImage(src), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);
    Image* d = (Image*)dst;
    Image* s = (Image*)src;
    RI_IF_ERROR(eglvgIsInUse(d) || eglvgIsInUse(s), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(d->overlaps(s), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!kernel || !isAligned(kernel,2) || kernelWidth <= 0 || kernelHeight <= 0 || kernelWidth > RI_MAX_KERNEL_SIZE || kernelHeight > RI_MAX_KERNEL_SIZE, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(tilingMode < VG_TILE_FILL || tilingMode > VG_TILE_REFLECT, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    unsigned int channelMask = context->m_filterChannelMask & (VG_RED|VG_GREEN|VG_BLUE|VG_ALPHA);	//undefined bits are ignored
    try
    {
        d->convolve(*s, kernelWidth, kernelHeight, shiftX, shiftY, (const RIint16*)kernel, inputFloat(scale), inputFloat(bias), tilingMode, context->m_tileFillColor, context->m_filterFormatLinear ? true : false, context->m_filterFormatPremultiplied ? true : false, channelMask);
    }
    catch(std::bad_alloc)
    {
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgSeparableConvolve(VGImage dst, VGImage src, VGint kernelWidth, VGint kernelHeight, VGint shiftX, VGint shiftY, const VGshort * kernelX, const VGshort * kernelY, VGfloat scale, VGfloat bias, VGTilingMode tilingMode)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidImage(dst) || !context->isValidImage(src), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);
    Image* d = (Image*)dst;
    Image* s = (Image*)src;
    RI_IF_ERROR(eglvgIsInUse(d) || eglvgIsInUse(s), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(d->overlaps(s), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!kernelX || !kernelY || !isAligned(kernelX,2) || !isAligned(kernelY,2) || kernelWidth <= 0 || kernelHeight <= 0 || kernelWidth > RI_MAX_SEPARABLE_KERNEL_SIZE || kernelHeight > RI_MAX_SEPARABLE_KERNEL_SIZE, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(tilingMode < VG_TILE_FILL || tilingMode > VG_TILE_REFLECT, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    unsigned int channelMask = context->m_filterChannelMask & (VG_RED|VG_GREEN|VG_BLUE|VG_ALPHA);	//undefined bits are ignored
    try
    {
        d->separableConvolve(*s, kernelWidth, kernelHeight, shiftX, shiftY, (const RIint16*)kernelX, (const RIint16*)kernelY,
                                         inputFloat(scale), inputFloat(bias), tilingMode, context->m_tileFillColor, context->m_filterFormatLinear ? true : false,
                                         context->m_filterFormatPremultiplied ? true : false, channelMask);
    }
    catch(std::bad_alloc)
    {
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgGaussianBlur(VGImage dst, VGImage src, VGfloat stdDeviationX, VGfloat stdDeviationY, VGTilingMode tilingMode)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidImage(dst) || !context->isValidImage(src), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);
    Image* d = (Image*)dst;
    Image* s = (Image*)src;
    RI_IF_ERROR(eglvgIsInUse(d) || eglvgIsInUse(s), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(d->overlaps(s), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RIfloat sx = inputFloat(stdDeviationX);
    RIfloat sy = inputFloat(stdDeviationY);
    RI_IF_ERROR(sx <= 0.0f || sy <= 0.0f || sx > (RIfloat)RI_MAX_GAUSSIAN_STD_DEVIATION || sy > (RIfloat)RI_MAX_GAUSSIAN_STD_DEVIATION, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(tilingMode < VG_TILE_FILL || tilingMode > VG_TILE_REFLECT, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    unsigned int channelMask = context->m_filterChannelMask & (VG_RED|VG_GREEN|VG_BLUE|VG_ALPHA);	//undefined bits are ignored
    try
    {
        d->gaussianBlur(*s, sx, sy, tilingMode, context->m_tileFillColor, context->m_filterFormatLinear ? true : false,
                        context->m_filterFormatPremultiplied ? true : false, channelMask);
    }
    catch(std::bad_alloc)
    {
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgLookup(VGImage dst, VGImage src, const VGubyte * redLUT, const VGubyte * greenLUT, const VGubyte * blueLUT, const VGubyte * alphaLUT, VGboolean outputLinear, VGboolean outputPremultiplied)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidImage(dst) || !context->isValidImage(src), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);
    Image* d = (Image*)dst;
    Image* s = (Image*)src;
    RI_IF_ERROR(eglvgIsInUse(d) || eglvgIsInUse(s), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(d->overlaps(s), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!redLUT || !greenLUT || !blueLUT || !alphaLUT, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    unsigned int channelMask = context->m_filterChannelMask & (VG_RED|VG_GREEN|VG_BLUE|VG_ALPHA);	//undefined bits are ignored
    try
    {
        d->lookup(*s, (const RIuint8*)redLUT, (const RIuint8*)greenLUT, (const RIuint8*)blueLUT, (const RIuint8*)alphaLUT,
                  outputLinear ? true : false, outputPremultiplied ? true : false, context->m_filterFormatLinear ? true : false,
                  context->m_filterFormatPremultiplied ? true : false, channelMask);
    }
    catch(std::bad_alloc)
    {
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgLookupSingle(VGImage dst, VGImage src, const VGuint * lookupTable, VGImageChannel sourceChannel, VGboolean outputLinear, VGboolean outputPremultiplied)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidImage(dst) || !context->isValidImage(src), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);
    Image* d = (Image*)dst;
    Image* s = (Image*)src;
    RI_IF_ERROR(eglvgIsInUse(d) || eglvgIsInUse(s), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(d->overlaps(s), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(!lookupTable || !isAligned(lookupTable,4), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    const Color::Descriptor& desc = s->getDescriptor();
    RI_ASSERT(Color::isValidDescriptor(desc));
    //give an error if src is in rgb format and the source channel is not valid
    RI_IF_ERROR((!desc.isLuminance() && !desc.isAlphaOnly()) && (sourceChannel != VG_RED && sourceChannel != VG_GREEN && sourceChannel != VG_BLUE && sourceChannel != VG_ALPHA), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    unsigned int channelMask = context->m_filterChannelMask & (VG_RED|VG_GREEN|VG_BLUE|VG_ALPHA);	//undefined bits are ignored
    try
    {
        d->lookupSingle(*s, (const RIuint32*)lookupTable, sourceChannel, outputLinear ? true : false, outputPremultiplied ? true : false,
                        context->m_filterFormatLinear ? true : false, context->m_filterFormatPremultiplied ? true : false, channelMask);
    }
    catch(std::bad_alloc)
    {
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGHardwareQueryResult RI_APIENTRY vgHardwareQuery(VGHardwareQueryType key, VGint setting)
{
    RI_GET_CONTEXT(VG_HARDWARE_UNACCELERATED);
    RI_IF_ERROR(key != VG_IMAGE_FORMAT_QUERY && key != VG_PATH_DATATYPE_QUERY, VG_ILLEGAL_ARGUMENT_ERROR, VG_HARDWARE_UNACCELERATED);
    RI_IF_ERROR(key == VG_IMAGE_FORMAT_QUERY && !isValidImageFormat(setting), VG_ILLEGAL_ARGUMENT_ERROR, VG_HARDWARE_UNACCELERATED);
    RI_IF_ERROR(key == VG_PATH_DATATYPE_QUERY && (setting < VG_PATH_DATATYPE_S_8 || setting > VG_PATH_DATATYPE_F), VG_ILLEGAL_ARGUMENT_ERROR, VG_HARDWARE_UNACCELERATED);
    RI_RETURN(VG_HARDWARE_UNACCELERATED);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/
     
const VGubyte * RI_APIENTRY vgGetString(VGStringID name)
{
    static const VGubyte vendor[] = "Nokia";
    static const VGubyte renderer[] = {"OpenVG 1.1 Faster Reference Implementation May 13 2008"};
    static const VGubyte version[] = "1.1";
    static const VGubyte extensions[] = "";
    const VGubyte* r = NULL;
    RI_GET_CONTEXT(NULL);
    switch(name)
    {
    case VG_VENDOR:
        r = vendor;
        break;
    case VG_RENDERER:
        r = renderer;
        break;
    case VG_VERSION:
        r = version;
        break;
    case VG_EXTENSIONS:
        r = extensions;
        break;
    default:
        break;
    }
    RI_RETURN(r);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

VGFont RI_APIENTRY vgCreateFont(VGint glyphCapacityHint)
{
    RI_GET_CONTEXT(VG_INVALID_HANDLE);
    RI_IF_ERROR(glyphCapacityHint < 0, VG_ILLEGAL_ARGUMENT_ERROR, VG_INVALID_HANDLE);

    Font* font = NULL;
    try
    {
        font = RI_NEW(Font, (glyphCapacityHint));	//throws bad_alloc
        RI_ASSERT(font);
        context->m_fontManager->addResource(font, context);	//throws bad_alloc
        RI_RETURN((VGFont)font);
    }
    catch(std::bad_alloc)
    {
        RI_DELETE(font);
        context->setError(VG_OUT_OF_MEMORY_ERROR);
        RI_RETURN(VG_INVALID_HANDLE);
    }
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgDestroyFont(VGFont font)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidFont(font), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid font handle

    context->m_fontManager->removeResource((Font*)font);

    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgSetGlyphToPath(VGFont font, VGuint glyphIndex, VGPath path, VGboolean isHinted, const VGfloat glyphOrigin[2], const VGfloat escapement[2])
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidFont(font), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid font handle
    RI_IF_ERROR(path != VG_INVALID_HANDLE && !context->isValidPath(path), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid path handle
    RI_IF_ERROR(!glyphOrigin || !escapement || !isAligned(glyphOrigin,sizeof(VGfloat)) || !isAligned(escapement,sizeof(VGfloat)), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    Font* f = (Font*)font;

    try
    {
        f->setGlyphToPath(glyphIndex, path, isHinted ? true : false, Vector2(inputFloat(glyphOrigin[0]), inputFloat(glyphOrigin[1])), Vector2(inputFloat(escapement[0]), inputFloat(escapement[1])));
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgSetGlyphToImage(VGFont font, VGuint glyphIndex, VGImage image, const VGfloat glyphOrigin[2], const VGfloat escapement[2])
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidFont(font), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid font handle
    if(image != VG_INVALID_HANDLE)
    {
        RI_IF_ERROR(!context->isValidImage(image), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid image handle
        RI_IF_ERROR(eglvgIsInUse((Image*)image), VG_IMAGE_IN_USE_ERROR, RI_NO_RETVAL); //image in use
    }
    RI_IF_ERROR(!glyphOrigin || !escapement || !isAligned(glyphOrigin,sizeof(VGfloat)) || !isAligned(escapement,sizeof(VGfloat)), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    Font* f = (Font*)font;

    try
    {
        f->setGlyphToImage(glyphIndex, image, Vector2(inputFloat(glyphOrigin[0]), inputFloat(glyphOrigin[1])), Vector2(inputFloat(escapement[0]), inputFloat(escapement[1])));
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgClearGlyph(VGFont font, VGuint glyphIndex)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidFont(font), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid font handle
    Font* f = (Font*)font;
    Font::Glyph* g = f->findGlyph(glyphIndex);
    RI_IF_ERROR(!g, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);   //glyphIndex not defined

    f->clearGlyph(g);

    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgDrawGlyph(VGFont font, VGuint glyphIndex, VGbitfield paintModes, VGboolean allowAutoHinting)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidFont(font), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid font handle
    RI_IF_ERROR(paintModes & ~(VG_FILL_PATH | VG_STROKE_PATH), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);	//invalid paint mode
    Font* f = (Font*)font;
    Font::Glyph* g = f->findGlyph(glyphIndex);
    RI_IF_ERROR(!g, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);   //glyphIndex not defined
    RI_UNREF(allowAutoHinting); //RI doesn't implement autohinting

    try
    {
        if(paintModes)
        {
            Matrix3x3 userToSurfaceMatrix = context->m_glyphUserToSurface;
            Vector2 t = context->m_glyphOrigin - g->m_origin;
            Matrix3x3 n(1, 0, t.x,
                        0, 1, t.y,
                        0, 0, 1 );
            userToSurfaceMatrix *= n;
            userToSurfaceMatrix[2].set(0,0,1);		//force affinity

            bool ret = true;
			eglvgLockSurface(0, 1);
            if(g->m_image != VG_INVALID_HANDLE)
                ret = drawImage(context, g->m_image, userToSurfaceMatrix);
            else if(g->m_path != VG_INVALID_HANDLE)
                ret = drawPath(context, g->m_path, userToSurfaceMatrix, paintModes);
			eglvgUnlockSurface();
            if(!ret)
            {
                RI_RETURN(RI_NO_RETVAL);
            }
        }

        context->m_glyphOrigin += g->m_escapement;
        context->m_inputGlyphOrigin = context->m_glyphOrigin;
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }

    RI_RETURN(RI_NO_RETVAL);
}

/*-------------------------------------------------------------------*//*!
* \brief
* \param
* \return
* \note
*//*-------------------------------------------------------------------*/

void RI_APIENTRY vgDrawGlyphs(VGFont font, VGint glyphCount, const VGuint *glyphIndices, const VGfloat *adjustments_x, const VGfloat *adjustments_y, VGbitfield paintModes, VGboolean allowAutoHinting)
{
    RI_GET_CONTEXT(RI_NO_RETVAL);
    RI_IF_ERROR(!context->isValidFont(font), VG_BAD_HANDLE_ERROR, RI_NO_RETVAL);	//invalid font handle
    RI_IF_ERROR(!glyphIndices || !isAligned(glyphIndices, sizeof(VGuint)) || glyphCount <= 0, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR((adjustments_x && !isAligned(adjustments_x, sizeof(VGfloat))) || (adjustments_y && !isAligned(adjustments_y, sizeof(VGfloat))), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);
    RI_IF_ERROR(paintModes & ~(VG_FILL_PATH | VG_STROKE_PATH), VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);	//invalid paint mode
    Font* f = (Font*)font;
    for(int i=0;i<glyphCount;i++)
    {
        Font::Glyph* g = f->findGlyph(glyphIndices[i]);
        RI_IF_ERROR(!g, VG_ILLEGAL_ARGUMENT_ERROR, RI_NO_RETVAL);   //glyphIndex not defined
    }
    RI_UNREF(allowAutoHinting); //RI doesn't implement autohinting

    try
    {
        for(int i=0;i<glyphCount;i++)
        {
            Font::Glyph* g = f->findGlyph(glyphIndices[i]);

            if(paintModes)
            {
                Matrix3x3 userToSurfaceMatrix = context->m_glyphUserToSurface;
                Vector2 t = context->m_glyphOrigin - g->m_origin;
                Matrix3x3 n(1, 0, t.x,
                            0, 1, t.y,
                            0, 0, 1 );
                userToSurfaceMatrix *= n;
                userToSurfaceMatrix[2].set(0,0,1);		//force affinity

                bool ret = true;
                if(g->m_image != VG_INVALID_HANDLE)
                    ret = drawImage(context, g->m_image, userToSurfaceMatrix);
                else if(g->m_path != VG_INVALID_HANDLE)
                    ret = drawPath(context, g->m_path, userToSurfaceMatrix, paintModes);
                if(!ret)
                {
                    RI_RETURN(RI_NO_RETVAL);
                }
            }

            context->m_glyphOrigin += g->m_escapement;
            if(adjustments_x)
                context->m_glyphOrigin.x += inputFloat(adjustments_x[i]);
            if(adjustments_y)
                context->m_glyphOrigin.y += inputFloat(adjustments_y[i]);
            context->m_inputGlyphOrigin = context->m_glyphOrigin;
        }
    }
    catch(std::bad_alloc)
    {
        context->setError(VG_OUT_OF_MEMORY_ERROR);
    }
	RI_RETURN(RI_NO_RETVAL);
}

VGint vgePathCoordsSizeInBytes(VGPath path, VGint startIndex, VGint numSegments)
{
    RI_GET_CONTEXT( 0 );
    RI_IF_ERROR(!context->isValidPath(path), VG_BAD_HANDLE_ERROR, 0);
    VGint ret = ((OpenVGRI::Path*)path)->coordsSizeInBytes( startIndex, numSegments );
    RI_RETURN(ret);
}


VGImage vgCreateEGLImageTargetKHR(VGeglImageKHR image)
{
#if defined(USE_FULL_EGL)
    RI_GET_CONTEXT( 0 );
    VGImage ret;
    OpenVGRI::Color::Descriptor desc;
    OpenVGRI::RIuint8* data;
    // Get data for VGImage
    int width;
    int height;
    int stride;

    eglvgGetImageDescriptor( image, desc, width, height, stride );
    // There is some error.
    // EGLImage is null or EGLImage target is EGL_VG_PARENT_IMAGE_KHR.
    RI_IF_ERROR(!width || !height || !stride, VG_ILLEGAL_ARGUMENT_ERROR, NULL);
    // Data is created in EGLImage class.
    data = (OpenVGRI::RIuint8*)eglvgGetImageData( image );    
    // Create VGImage
    // allowedQuality = VG_IMAGE_QUALITY_NONANTIALIASED | VG_IMAGE_QUALITY_FASTER | VG_IMAGE_QUALITY_BETTER
    ret = vgCreateImage( desc.vgFormat, width, height, VG_IMAGE_QUALITY_NONANTIALIASED );
    // If VGImage is not created raise error and return null
    RI_IF_ERROR(!ret, VG_UNSUPPORTED_IMAGE_FORMAT_ERROR, NULL);
    // Set data for VGImage.
    // This will copy that data-object.
    vgImageSubData( ret, 
                    data, 
                    stride, 
                    desc.vgFormat, 
                    0, 
                    0,
                    width, 
                    height);
    // Return VGImage
    RI_RETURN(ret);
#else
    (void)image;
    return VG_INVALID_HANDLE;
#endif //USE_FULL_EGL
}