/*
 * Copyright (c) 2007 Ivan Leben
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library in the file COPYING;
 * if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include <stdio.h>

#define VG_API_EXPORT
#include "vg/openvg.h"
#include "shContext.h"

/*----------------------------------------------------
 * Returns true (1) if the specified parameter takes
 * a vector of values (more than 1) or false (0)
 * otherwise.
 *----------------------------------------------------*/

int shIsParamVector(SHint type)
{
    return (type == VG_SCISSOR_RECTS ||
            type == VG_STROKE_DASH_PATTERN ||
            type == VG_TILE_FILL_COLOR ||
            type == VG_CLEAR_COLOR ||
            type == VG_PAINT_COLOR ||
            type == VG_PAINT_COLOR_RAMP_STOPS ||
            type == VG_PAINT_LINEAR_GRADIENT ||
            type == VG_PAINT_RADIAL_GRADIENT);
}

/*----------------------------------------------------
 * Returns true (1) if the given integer is a valid
 * enumeration value for the specified parameter or
 * false (0) otherwise.
 *----------------------------------------------------*/

int shIsEnumValid(SHint type, VGint val)
{
    switch (type) {
    case VG_MATRIX_MODE:
        return (val == VG_MATRIX_PATH_USER_TO_SURFACE ||
                val == VG_MATRIX_IMAGE_USER_TO_SURFACE ||
                val == VG_MATRIX_FILL_PAINT_TO_USER ||
                val == VG_MATRIX_STROKE_PAINT_TO_USER);

    case VG_FILL_RULE:
        return (val == VG_EVEN_ODD ||
                val == VG_NON_ZERO)
                   ? VG_TRUE
                   : VG_FALSE;

    case VG_IMAGE_QUALITY:
        return (val == VG_IMAGE_QUALITY_NONANTIALIASED ||
                val == VG_IMAGE_QUALITY_FASTER ||
                val == VG_IMAGE_QUALITY_BETTER);

    case VG_RENDERING_QUALITY:
        return (val == VG_RENDERING_QUALITY_NONANTIALIASED ||
                val == VG_RENDERING_QUALITY_FASTER ||
                val == VG_RENDERING_QUALITY_BETTER);

    case VG_BLEND_MODE:
        return (val == VG_BLEND_SRC ||
                val == VG_BLEND_SRC_OVER ||
                val == VG_BLEND_DST_OVER ||
                val == VG_BLEND_SRC_IN ||
                val == VG_BLEND_DST_IN ||
                val == VG_BLEND_MULTIPLY ||
                val == VG_BLEND_SCREEN ||
                val == VG_BLEND_DARKEN ||
                val == VG_BLEND_LIGHTEN ||
                val == VG_BLEND_ADDITIVE ||
                val == VG_BLEND_SRC_OUT_SH ||
                val == VG_BLEND_DST_OUT_SH ||
                val == VG_BLEND_SRC_ATOP_SH ||
                val == VG_BLEND_DST_ATOP_SH);

    case VG_IMAGE_MODE:
        return (val == VG_DRAW_IMAGE_NORMAL ||
                val == VG_DRAW_IMAGE_MULTIPLY ||
                val == VG_DRAW_IMAGE_STENCIL);

    case VG_STROKE_CAP_STYLE:
        return (val == VG_CAP_BUTT ||
                val == VG_CAP_ROUND ||
                val == VG_CAP_SQUARE);

    case VG_STROKE_JOIN_STYLE:
        return (val == VG_JOIN_MITER ||
                val == VG_JOIN_ROUND ||
                val == VG_JOIN_BEVEL);

    case VG_STROKE_DASH_PHASE_RESET:
    case VG_SCISSORING:
    case VG_MASKING:
        return (val == VG_TRUE ||
                val == VG_FALSE);

    case VG_PIXEL_LAYOUT:
        return (val == VG_PIXEL_LAYOUT_UNKNOWN ||
                val == VG_PIXEL_LAYOUT_RGB_VERTICAL ||
                val == VG_PIXEL_LAYOUT_BGR_VERTICAL ||
                val == VG_PIXEL_LAYOUT_RGB_HORIZONTAL ||
                val == VG_PIXEL_LAYOUT_BGR_HORIZONTAL);

    case VG_FILTER_FORMAT_LINEAR:
    case VG_FILTER_FORMAT_PREMULTIPLIED:
        return (val == VG_TRUE ||
                val == VG_FALSE);

    case VG_PAINT_TYPE:
        return (val == VG_PAINT_TYPE_COLOR ||
                val == VG_PAINT_TYPE_LINEAR_GRADIENT ||
                val == VG_PAINT_TYPE_RADIAL_GRADIENT ||
                val == VG_PAINT_TYPE_PATTERN);

    case VG_PAINT_COLOR_RAMP_SPREAD_MODE:
        return (val == VG_COLOR_RAMP_SPREAD_PAD ||
                val == VG_COLOR_RAMP_SPREAD_REPEAT ||
                val == VG_COLOR_RAMP_SPREAD_REFLECT);

    case VG_PAINT_PATTERN_TILING_MODE:
        return (val == VG_TILE_FILL ||
                val == VG_TILE_PAD ||
                val == VG_TILE_REPEAT ||
                val == VG_TILE_REFLECT);

    case VG_IMAGE_FORMAT:
        return (val >= VG_sRGBX_8888 &&
                val <= VG_lABGR_8888_PRE);

    default:
        return 1;
    }
}

/*--------------------------------------------------------
 * These two functions check for invalid (erroneous) float
 * input and correct it to acceptable ranges.
 *---------------------------------------------------*/

SHfloat getMaxFloat()
{
    SHfloatint fi;
    fi.i = SH_MAX_FLOAT_BITS;
    return fi.f;
}

SHfloat shValidInputFloat(VGfloat f)
{
    SHfloat max = getMaxFloat();
    if (SH_ISNAN(f)) return 0.0f; /* convert NAN to zero */
    SH_CLAMP(f, -max, max);       /* clamp to valid range */
    return (SHfloat)f;
}

static SHint shValidInputFloat2Int(VGfloat f)
{
    double v = (double)SH_FLOOR(shValidInputFloat(f));
    SH_CLAMP(v, (double)SH_MIN_INT, (double)SH_MAX_INT);
    return (SHint)v;
}

/*---------------------------------------------------
 * Interprets the input value vector as an array of
 * integers and returns the value at given index
 *---------------------------------------------------*/

static SHint shParamToInt(const void* values, SHint floats, SHint index)
{
    if (floats)
        return shValidInputFloat2Int(((const VGfloat*)values)[index]);
    else
        return (SHint)((const VGint*)values)[index];
}

/*---------------------------------------------------
 * Interprets the input value vector as an array of
 * floats and returns the value at given index
 *---------------------------------------------------*/

static VGfloat shParamToFloat(const void* values, SHint floats, SHint index)
{
    if (floats)
        return shValidInputFloat(((const VGfloat*)values)[index]);
    else
        return (SHfloat)((const VGint*)values)[index];
}

/*---------------------------------------------------
 * Interprets the output value vector as an array of
 * integers and sets the value at given index
 *---------------------------------------------------*/

static void shIntToParam(SHint i, SHint count, void* output, SHint floats, SHint index)
{
    if (index >= count)
        return;
    if (floats)
        ((VGfloat*)output)[index] = (VGfloat)i;
    else
        ((VGint*)output)[index] = (VGint)i;
}

/*----------------------------------------------------
 * Interprets the output value vector as an array of
 * floats and sets the value at given index
 *----------------------------------------------------*/

static void shFloatToParam(SHfloat f, SHint count, void* output, SHint floats, SHint index)
{
    if (index >= count)
        return;
    if (floats)
        ((VGfloat*)output)[index] = (VGfloat)f;
    else
        ((VGint*)output)[index] = (VGint)shValidInputFloat2Int(f);
}

/*---------------------------------------------------------
 * Sets a parameter by interpreting the input value vector
 * according to the parameter type and input type.
 *---------------------------------------------------------*/

static void shSet(VGContext* context, VGParamType type, SHint count,
                  const void* values, SHint floats)
{
    SHfloat fvalue = 0.0f;
    SHint ivalue = 0;
    VGboolean bvalue = VG_FALSE;
    int i = 0;

    /* Check for negative count */
    SH_RETURN_ERR_IF(count < 0, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);

    /* Check for empty vector */
    SH_RETURN_ERR_IF(!values && count != 0, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);

    /* Pre-convert first value for non-vector params */
    if (count == 1) {
        fvalue = shParamToFloat(values, floats, 0);
        ivalue = shParamToInt(values, floats, 0);
        bvalue = (ivalue ? VG_TRUE : VG_FALSE);
    }

    switch (type) {
    case VG_MATRIX_MODE:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        SH_RETURN_ERR_IF(!shIsEnumValid(type, ivalue), VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->matrixMode = (VGMatrixMode)ivalue;
        break;

    case VG_FILL_RULE:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        SH_RETURN_ERR_IF(!shIsEnumValid(type, ivalue), VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->fillRule = (VGFillRule)ivalue;
        break;

    case VG_IMAGE_QUALITY:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        SH_RETURN_ERR_IF(!shIsEnumValid(type, ivalue), VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->imageQuality = (VGImageQuality)ivalue;
        break;

    case VG_RENDERING_QUALITY:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        SH_RETURN_ERR_IF(!shIsEnumValid(type, ivalue), VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->renderingQuality = (VGRenderingQuality)ivalue;
        break;

    case VG_BLEND_MODE:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        SH_RETURN_ERR_IF(!shIsEnumValid(type, ivalue), VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->blendMode = (VGBlendMode)ivalue;
        break;

    case VG_IMAGE_MODE:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        SH_RETURN_ERR_IF(!shIsEnumValid(type, ivalue), VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->imageMode = (VGImageMode)ivalue;
        break;

    case VG_STROKE_CAP_STYLE:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        SH_RETURN_ERR_IF(!shIsEnumValid(type, ivalue), VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->strokeCapStyle = (VGCapStyle)ivalue;
        break;

    case VG_STROKE_JOIN_STYLE:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        SH_RETURN_ERR_IF(!shIsEnumValid(type, ivalue), VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->strokeJoinStyle = (VGJoinStyle)ivalue;
        break;

    case VG_PIXEL_LAYOUT:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        SH_RETURN_ERR_IF(!shIsEnumValid(type, ivalue), VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->pixelLayout = (VGPixelLayout)ivalue;
        break;

    case VG_FILTER_CHANNEL_MASK:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->filterChannelMask = (VGbitfield)ivalue;
        break;

    case VG_FILTER_FORMAT_LINEAR:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->filterFormatLinear = bvalue;
        break;

    case VG_FILTER_FORMAT_PREMULTIPLIED:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->filterFormatPremultiplied = bvalue;
        break;

    case VG_STROKE_DASH_PHASE_RESET:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->strokeDashPhaseReset = bvalue;
        break;

    case VG_MASKING:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->masking = bvalue;
        break;

    case VG_SCISSORING:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->scissoring = bvalue;
        break;

    case VG_STROKE_LINE_WIDTH:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->strokeLineWidth = fvalue;
        break;

    case VG_STROKE_MITER_LIMIT:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->strokeMiterLimit = SH_MAX(fvalue, 1.0f);
        break;

    case VG_STROKE_DASH_PHASE:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        context->strokeDashPhase = fvalue;
        break;

    case VG_STROKE_DASH_PATTERN:

        /* TODO: limit by the VG_MAX_DASH_COUNT value */
        shFloatArrayClear(&context->strokeDashPattern);
        for (i = 0; i < count; ++i)
            shFloatArrayPushBack(&context->strokeDashPattern,
                                 shParamToFloat(values, floats, i));
        break;
    case VG_TILE_FILL_COLOR:

        SH_RETURN_ERR_IF(count != 4, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        CSET(context->tileFillColor,
             shParamToFloat(values, floats, 0),
             shParamToFloat(values, floats, 1),
             shParamToFloat(values, floats, 2),
             shParamToFloat(values, floats, 3));

        break;
    case VG_CLEAR_COLOR:

        SH_RETURN_ERR_IF(count != 4, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        CSET(context->clearColor,
             shParamToFloat(values, floats, 0),
             shParamToFloat(values, floats, 1),
             shParamToFloat(values, floats, 2),
             shParamToFloat(values, floats, 3));

        break;
    case VG_SCISSOR_RECTS:

        SH_RETURN_ERR_IF(count % 4, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shRectArrayClear(&context->scissor);
        for (i = 0; i < count && i < SH_MAX_SCISSOR_RECTS; i += 4) {
            SHRectangle r;
            r.x = shParamToFloat(values, floats, i + 0);
            r.y = shParamToFloat(values, floats, i + 1);
            r.w = shParamToFloat(values, floats, i + 2);
            r.h = shParamToFloat(values, floats, i + 3);
            shRectArrayPushBackP(&context->scissor, &r);
        }

        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:
        /* Read-only */ break;

    default:
        /* Invalid VGParamType */
        SH_RETURN_ERR(VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
    }

    SH_RETURN(SH_NO_RETVAL);
}

/*--------------------------------------------------
 * Sets a parameter of a single integer value
 *--------------------------------------------------*/

VG_API_CALL void vgSetf(VGParamType type, VGfloat value)
{
    VG_GETCONTEXT(VG_NO_RETVAL);

    /* Check if target vector */
    VG_RETURN_ERR_IF(shIsParamVector(type),
                     VG_ILLEGAL_ARGUMENT_ERROR,
                     VG_NO_RETVAL);

    /* Error code will be set by shSet */
    shSet(context, type, 1, &value, 1);
    VG_RETURN(VG_NO_RETVAL);
}

/*--------------------------------------------------
 * Sets a parameter of a single float value
 *--------------------------------------------------*/

VG_API_CALL void vgSeti(VGParamType type, VGint value)
{
    VG_GETCONTEXT(VG_NO_RETVAL);

    /* Check if target vector */
    VG_RETURN_ERR_IF(shIsParamVector(type),
                     VG_ILLEGAL_ARGUMENT_ERROR,
                     VG_NO_RETVAL);

    /* Error code will be set by shSet */
    shSet(context, type, 1, &value, 0);
    VG_RETURN(VG_NO_RETVAL);
}

/*-------------------------------------------------------
 * Sets a parameter which takes a vector of float values
 *-------------------------------------------------------*/

VG_API_CALL void vgSetfv(VGParamType type, VGint count,
                         const VGfloat* values)
{
    VG_GETCONTEXT(VG_NO_RETVAL);

    /* TODO: check input array alignment */

    /* Error code will be set by shSet */
    shSet(context, type, count, values, 1);
    VG_RETURN(VG_NO_RETVAL);
}

/*---------------------------------------------------------
 * Sets a parameter which takes a vector of integer values
 *---------------------------------------------------------*/

VG_API_CALL void vgSetiv(VGParamType type, VGint count,
                         const VGint* values)
{
    VG_GETCONTEXT(VG_NO_RETVAL);

    /* TODO: check input array alignment */

    /* Error code wil be set by shSet */
    shSet(context, type, count, values, 0);
    VG_RETURN(VG_NO_RETVAL);
}

/*---------------------------------------------------------
 * Outputs a parameter by interpreting the output value
 * vector according to the parameter type and input type.
 *---------------------------------------------------------*/

static void shGet(VGContext* context, VGParamType type, SHint count, void* values, SHint floats)
{
    int i;

    /* Check for invalid array / count */
    SH_RETURN_ERR_IF(!values || count <= 0, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);

    switch (type) {
    case VG_MATRIX_MODE:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->matrixMode, count, values, floats, 0);
        break;

    case VG_FILL_RULE:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->fillRule, count, values, floats, 0);
        break;

    case VG_IMAGE_QUALITY:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->imageQuality, count, values, floats, 0);
        break;

    case VG_RENDERING_QUALITY:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->renderingQuality, count, values, floats, 0);
        break;

    case VG_BLEND_MODE:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->blendMode, count, values, floats, 0);
        break;

    case VG_IMAGE_MODE:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->imageMode, count, values, floats, 0);
        break;

    case VG_STROKE_CAP_STYLE:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->strokeCapStyle, count, values, floats, 0);
        break;

    case VG_STROKE_JOIN_STYLE:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->strokeJoinStyle, count, values, floats, 0);
        break;

    case VG_PIXEL_LAYOUT:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->pixelLayout, count, values, floats, 0);
        break;

    case VG_FILTER_CHANNEL_MASK:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->filterChannelMask, count, values, floats, 0);
        break;

    case VG_FILTER_FORMAT_LINEAR:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->filterFormatLinear, count, values, floats, 0);
        break;

    case VG_FILTER_FORMAT_PREMULTIPLIED:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->filterFormatPremultiplied, count, values, floats, 0);
        break;

    case VG_STROKE_DASH_PHASE_RESET:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->strokeDashPhaseReset, count, values, floats, 0);
        break;

    case VG_MASKING:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->masking, count, values, floats, 0);
        break;

    case VG_SCISSORING:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam((SHint)context->scissoring, count, values, floats, 0);
        break;

    case VG_STROKE_LINE_WIDTH:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shFloatToParam(context->strokeLineWidth, count, values, floats, 0);
        break;

    case VG_STROKE_MITER_LIMIT:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shFloatToParam(context->strokeMiterLimit, count, values, floats, 0);
        break;

    case VG_STROKE_DASH_PHASE:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shFloatToParam(context->strokeDashPhase, count, values, floats, 0);
        break;

    case VG_STROKE_DASH_PATTERN:

        SH_RETURN_ERR_IF(count > context->strokeDashPattern.size,
                         VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);

        for (i = 0; i < context->strokeDashPattern.size; ++i)
            shFloatToParam(context->strokeDashPattern.items[i],
                           count, values, floats, i);

        break;
    case VG_TILE_FILL_COLOR:

        SH_RETURN_ERR_IF(count > 4, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shFloatToParam(context->tileFillColor.r, count, values, floats, 0);
        shFloatToParam(context->tileFillColor.g, count, values, floats, 1);
        shFloatToParam(context->tileFillColor.b, count, values, floats, 2);
        shFloatToParam(context->tileFillColor.a, count, values, floats, 3);

        break;
    case VG_CLEAR_COLOR:

        SH_RETURN_ERR_IF(count > 4, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shFloatToParam(context->clearColor.r, count, values, floats, 0);
        shFloatToParam(context->clearColor.g, count, values, floats, 1);
        shFloatToParam(context->clearColor.b, count, values, floats, 2);
        shFloatToParam(context->clearColor.a, count, values, floats, 3);

        break;
    case VG_SCISSOR_RECTS:

        SH_RETURN_ERR_IF(count > context->scissor.size * 4,
                         VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);

        for (i = 0; i < context->scissor.size; ++i) {
            shIntToParam((SHint)context->scissor.items[i].x, count, values, floats, i * 4 + 0);
            shIntToParam((SHint)context->scissor.items[i].y, count, values, floats, i * 4 + 1);
            shIntToParam((SHint)context->scissor.items[i].w, count, values, floats, i * 4 + 2);
            shIntToParam((SHint)context->scissor.items[i].h, count, values, floats, i * 4 + 3);
        }

        break;

    case VG_MAX_SCISSOR_RECTS:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam(SH_MAX_SCISSOR_RECTS, count, values, floats, 0);
        break;

    case VG_MAX_DASH_COUNT:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam(SH_MAX_DASH_COUNT, count, values, floats, 0);
        break;

    case VG_MAX_COLOR_RAMP_STOPS:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam(SH_MAX_COLOR_RAMP_STOPS, count, values, floats, 0);
        break;

    case VG_MAX_IMAGE_WIDTH:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam(SH_MAX_IMAGE_WIDTH, count, values, floats, 0);
        break;

    case VG_MAX_IMAGE_HEIGHT:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam(SH_MAX_IMAGE_WIDTH, count, values, floats, 0);
        break;

    case VG_MAX_IMAGE_PIXELS:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam(SH_MAX_IMAGE_HEIGHT, count, values, floats, 0);
        break;

    case VG_MAX_IMAGE_BYTES:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam(SH_MAX_IMAGE_BYTES, count, values, floats, 0);
        break;

    case VG_MAX_FLOAT:
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shFloatToParam(getMaxFloat(), count, values, floats, 0);
        break;

    case VG_MAX_KERNEL_SIZE: /* TODO: depends on convolution implementation */
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam(0, count, values, floats, 0);
        break;

    case VG_MAX_SEPARABLE_KERNEL_SIZE: /* TODO: depends on convolution implementation */
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shIntToParam(0, count, values, floats, 0);
        break;

    case VG_MAX_GAUSSIAN_STD_DEVIATION: /* TODO: depends on gaussian blur implementation */
        SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        shFloatToParam(0.0f, count, values, floats, 0);
        break;

    default:
        /* Invalid VGParamType */
        SH_RETURN_ERR(VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
    }

    SH_RETURN(SH_NO_RETVAL);
}

/*---------------------------------------------------------
 * Returns a parameter of a single float value
 *---------------------------------------------------------*/

VG_API_CALL VGfloat vgGetf(VGParamType type)
{
    VGfloat retval = 0.0f;
    VG_GETCONTEXT(retval);

    /* Check if target vector */
    VG_RETURN_ERR_IF(shIsParamVector(type),
                     VG_ILLEGAL_ARGUMENT_ERROR,
                     retval);

    /* Error code will be set by shGet */
    shGet(context, type, 1, &retval, 1);
    VG_RETURN(retval);
}

/*---------------------------------------------------------
 * Returns a parameter of a single integer value
 *---------------------------------------------------------*/

VG_API_CALL VGint vgGeti(VGParamType type)
{
    VGint retval = 0;
    VG_GETCONTEXT(retval);

    /* Check if target vector */
    VG_RETURN_ERR_IF(shIsParamVector(type),
                     VG_ILLEGAL_ARGUMENT_ERROR,
                     retval);

    /* Error code will be set by shGet */
    shGet(context, type, 1, &retval, 0);
    VG_RETURN(retval);
}

/*---------------------------------------------------------
 * Outputs a parameter of a float vector value
 *---------------------------------------------------------*/

VG_API_CALL void vgGetfv(VGParamType type, VGint count, VGfloat* values)
{
    VG_GETCONTEXT(VG_NO_RETVAL);

    /* TODO: check output array alignment */

    /* Error code will be set by shGet */
    shGet(context, type, count, values, 1);
    VG_RETURN(VG_NO_RETVAL);
}

/*---------------------------------------------------------
 * Outputs a parameter of an integer vector value
 *---------------------------------------------------------*/

VG_API_CALL void vgGetiv(VGParamType type, VGint count, VGint* values)
{
    VG_GETCONTEXT(VG_NO_RETVAL);

    /* TODO: check output array alignment */

    /* Error code will be set by shGet */
    shGet(context, type, count, values, 0);
    VG_RETURN(VG_NO_RETVAL);
}

/*---------------------------------------------------------
 * Returns the size of the output array required to
 * receive the whole vector of parameter values
 *---------------------------------------------------------*/

VG_API_CALL VGint vgGetVectorSize(VGParamType type)
{
    int retval = 0;
    VG_GETCONTEXT(retval);

    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:
    case VG_STROKE_CAP_STYLE:
    case VG_STROKE_JOIN_STYLE:
    case VG_PIXEL_LAYOUT:
    case VG_FILTER_CHANNEL_MASK:
    case VG_FILTER_FORMAT_LINEAR:
    case VG_FILTER_FORMAT_PREMULTIPLIED:
    case VG_STROKE_DASH_PHASE_RESET:
    case VG_MASKING:
    case VG_SCISSORING:
    case VG_STROKE_LINE_WIDTH:
    case VG_STROKE_MITER_LIMIT:
    case VG_STROKE_DASH_PHASE:
    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:
        retval = 1;
        break;

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

    case VG_STROKE_DASH_PATTERN:
        retval = context->strokeDashPattern.size;
        break;

    case VG_SCISSOR_RECTS:
        retval = context->scissor.size * 4;
        break;

    default:
        /* Invalid VGParamType */
        VG_RETURN_ERR(VG_ILLEGAL_ARGUMENT_ERROR, retval);
    }

    VG_RETURN(retval);
}

/*-----------------------------------------------------------
 * Sets a resource parameter by interpreting the input value
 * vector according to the parameter type and input type.
 *-----------------------------------------------------------*/

static void shSetParameter(VGContext* context, VGHandle object,
                           SHResourceType rtype, VGint ptype,
                           SHint count, const void* values, SHint floats)
{
    //SHfloat fvalue = 0.0f;
    SHint ivalue = 0;
    //VGboolean bvalue = VG_FALSE;
    int i;

    /* Check for negative count */
    SH_RETURN_ERR_IF(count < 0, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);

    /* Check for empty vector */
    SH_RETURN_ERR_IF(!values && count != 0, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);

    /* Pre-convert first value for non-vector params */
    if (count == 1) {
        //  fvalue = shParamToFloat(values, floats, 0);
        ivalue = shParamToInt(values, floats, 0);
        //  bvalue = (ivalue ? VG_TRUE : VG_FALSE);
    }

    switch (rtype) {
    case SH_RESOURCE_PATH:
        switch (ptype) { /* Path parameters */

        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:
            /* Read-only */ break;

        default:
            /* Invalid VGParamType */
            SH_RETURN_ERR(VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        }
        break;
    case SH_RESOURCE_PAINT:
        switch (ptype) { /* Paint parameters */

        case VG_PAINT_TYPE:
            SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            SH_RETURN_ERR_IF(!shIsEnumValid(ptype, ivalue), VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            ((SHPaint*)object)->type = (VGPaintType)ivalue;
            break;

        case VG_PAINT_COLOR:
            SH_RETURN_ERR_IF(count != 4, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            ((SHPaint*)object)->color.r = shParamToFloat(values, floats, 0);
            ((SHPaint*)object)->color.g = shParamToFloat(values, floats, 1);
            ((SHPaint*)object)->color.b = shParamToFloat(values, floats, 2);
            ((SHPaint*)object)->color.a = shParamToFloat(values, floats, 3);
            break;

        case VG_PAINT_COLOR_RAMP_SPREAD_MODE:
            SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            SH_RETURN_ERR_IF(!shIsEnumValid(ptype, ivalue), VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            ((SHPaint*)object)->spreadMode = (VGColorRampSpreadMode)ivalue;
            break;

        case VG_PAINT_COLOR_RAMP_PREMULTIPLIED:
            SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            SH_RETURN_ERR_IF(!shIsEnumValid(ptype, ivalue), VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            ((SHPaint*)object)->premultiplied = (VGboolean)ivalue;
            break;

        case VG_PAINT_COLOR_RAMP_STOPS: {
            int max;
            SHPaint* paint;
            SHStop stop;
            SH_RETURN_ERR_IF(count % 5, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            max = SH_MIN(count, SH_MAX_COLOR_RAMP_STOPS * 5);
            paint = (SHPaint*)object;
            shStopArrayClear(&paint->instops);

            for (i = 0; i < max; i += 5) {
                stop.offset = shParamToFloat(values, floats, i + 0);
                CSET(stop.color,
                     shParamToFloat(values, floats, i + 1),
                     shParamToFloat(values, floats, i + 2),
                     shParamToFloat(values, floats, i + 3),
                     shParamToFloat(values, floats, i + 4));
                shStopArrayPushBackP(&paint->instops, &stop);
            }

            shValidateInputStops(paint);
            break;
        }

        case VG_PAINT_LINEAR_GRADIENT:
            SH_RETURN_ERR_IF(count != 4, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            for (i = 0; i < 4; ++i)
                ((SHPaint*)object)->linearGradient[i] = shParamToFloat(values, floats, i);
            break;

        case VG_PAINT_RADIAL_GRADIENT:
            SH_RETURN_ERR_IF(count != 5, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            for (i = 0; i < 5; ++i)
                ((SHPaint*)object)->radialGradient[i] = shParamToFloat(values, floats, i);
            break;

        case VG_PAINT_PATTERN_TILING_MODE:
            SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            SH_RETURN_ERR_IF(!shIsEnumValid(ptype, ivalue), VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            ((SHPaint*)object)->tilingMode = (VGTilingMode)ivalue;
            break;

        default:
            /* Invalid VGParamType */
            SH_RETURN_ERR(VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        }
        break;
    case SH_RESOURCE_IMAGE:
        switch (ptype) { /* Image parameters */

        case VG_IMAGE_FORMAT:
        case VG_IMAGE_WIDTH:
        case VG_IMAGE_HEIGHT:
            /* Read-only */ break;

        default:
            /* Invalid VGParamType */
            SH_RETURN_ERR(VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        }
        break;

    default:
        /* Invalid resource handle */
        SH_ASSERT(rtype != SH_RESOURCE_INVALID);
        break;
    }

    SH_RETURN(SH_NO_RETVAL);
}

/*------------------------------------------------------------
 * Sets a resource parameter which takes a single float value
 *------------------------------------------------------------*/

VG_API_CALL void vgSetParameterf(VGHandle object, VGint paramType, VGfloat value)
{
    SHResourceType resType;
    VG_GETCONTEXT(VG_NO_RETVAL);

    /* Validate object */
    resType = shGetResourceType(context, object);
    VG_RETURN_ERR_IF(resType == SH_RESOURCE_INVALID,
                     VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);

    /* Check if param vector */
    VG_RETURN_ERR_IF(shIsParamVector(paramType),
                     VG_ILLEGAL_ARGUMENT_ERROR,
                     VG_NO_RETVAL);

    /* Error code will be set by shSetParam() */
    shSetParameter(context, object, resType, paramType, 1, &value, 1);
    VG_RETURN(VG_NO_RETVAL);
}

/*--------------------------------------------------------------
 * Sets a resource parameter which takes a single integer value
 *--------------------------------------------------------------*/

VG_API_CALL void vgSetParameteri(VGHandle object, VGint paramType, VGint value)
{
    SHResourceType resType;
    VG_GETCONTEXT(VG_NO_RETVAL);

    /* Validate object */
    resType = shGetResourceType(context, object);
    VG_RETURN_ERR_IF(resType == SH_RESOURCE_INVALID,
                     VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);

    /* Check if param vector */
    VG_RETURN_ERR_IF(shIsParamVector(paramType),
                     VG_ILLEGAL_ARGUMENT_ERROR,
                     VG_NO_RETVAL);

    /* Error code will be set by shSetParam() */
    shSetParameter(context, object, resType, paramType, 1, &value, 0);
    VG_RETURN(VG_NO_RETVAL);
}

/*----------------------------------------------------------------
 * Sets a resource parameter which takes a vector of float values
 *----------------------------------------------------------------*/

VG_API_CALL void vgSetParameterfv(VGHandle object, VGint paramType,
                                  VGint count, const VGfloat* values)
{
    SHResourceType resType;
    VG_GETCONTEXT(VG_NO_RETVAL);

    /* Validate object */
    resType = shGetResourceType(context, object);
    VG_RETURN_ERR_IF(resType == SH_RESOURCE_INVALID,
                     VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);

    /* TODO: Check for input array alignment */

    /* Error code will be set by shSetParam() */
    shSetParameter(context, object, resType, paramType, count, values, 1);
    VG_RETURN(VG_NO_RETVAL);
}

/*------------------------------------------------------------------
 * Sets a resource parameter which takes a vector of integer values
 *------------------------------------------------------------------*/

VG_API_CALL void vgSetParameteriv(VGHandle object, VGint paramType,
                                  VGint count, const VGint* values)
{
    SHResourceType resType;
    VG_GETCONTEXT(VG_NO_RETVAL);

    /* Validate object */
    resType = shGetResourceType(context, object);
    VG_RETURN_ERR_IF(resType == SH_RESOURCE_INVALID,
                     VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);

    /* TODO: Check for input array alignment */

    /* Error code will be set by shSetParam() */
    shSetParameter(context, object, resType, paramType, count, values, 0);
    VG_RETURN(VG_NO_RETVAL);
}

/*---------------------------------------------------------------
 * Outputs a resource parameter by interpreting the output value
 * vector according to the parameter type and input type.
 *---------------------------------------------------------------*/

static void shGetParameter(VGContext* context, VGHandle object,
                           SHResourceType rtype, VGint ptype,
                           SHint count, void* values, SHint floats)
{
    int i;

    /* Check for invalid array / count */
    SH_RETURN_ERR_IF(!values || count <= 0, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);

    switch (rtype) {
    case SH_RESOURCE_PATH:
        switch (ptype) { /* Path parameters */

        case VG_PATH_FORMAT:
            SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            shIntToParam(((SHPath*)object)->format, count, values, floats, 0);
            break;

        case VG_PATH_DATATYPE:
            SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            shIntToParam(((SHPath*)object)->datatype, count, values, floats, 0);
            break;

        case VG_PATH_SCALE:
            SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            shFloatToParam(((SHPath*)object)->scale, count, values, floats, 0);
            break;

        case VG_PATH_BIAS:
            SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            shFloatToParam(((SHPath*)object)->bias, count, values, floats, 0);
            break;

        case VG_PATH_NUM_SEGMENTS:
            SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            shIntToParam(((SHPath*)object)->segCount, count, values, floats, 0);
            break;

        case VG_PATH_NUM_COORDS:
            SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            shIntToParam(((SHPath*)object)->dataCount, count, values, floats, 0);
            break;

        default:
            /* Invalid VGParamType */
            SH_RETURN_ERR(VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        }
        break;
    case SH_RESOURCE_PAINT:
        switch (ptype) { /* Paint parameters */

        case VG_PAINT_TYPE:
            SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            shIntToParam(((SHPaint*)object)->type, count, values, floats, 0);
            break;

        case VG_PAINT_COLOR:
            SH_RETURN_ERR_IF(count > 4, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            shFloatToParam(((SHPaint*)object)->color.r, count, values, floats, 0);
            shFloatToParam(((SHPaint*)object)->color.g, count, values, floats, 1);
            shFloatToParam(((SHPaint*)object)->color.b, count, values, floats, 2);
            shFloatToParam(((SHPaint*)object)->color.a, count, values, floats, 3);
            break;

        case VG_PAINT_COLOR_RAMP_SPREAD_MODE:
            SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            shIntToParam(((SHPaint*)object)->spreadMode, count, values, floats, 0);
            break;

        case VG_PAINT_COLOR_RAMP_PREMULTIPLIED:
            SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            shIntToParam(((SHPaint*)object)->spreadMode, count, values, floats, 0);
            break;

        case VG_PAINT_COLOR_RAMP_STOPS: {
            int i;
            SHPaint* paint = (SHPaint*)object;
            SHStop* stop;
            SH_RETURN_ERR_IF(count > paint->stops.size * 5,
                             VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);

            for (i = 0; i < paint->stops.size; ++i) {
                stop = &paint->stops.items[i];
                shFloatToParam(stop->offset, count, values, floats, i * 5 + 0);
                shFloatToParam(stop->color.r, count, values, floats, i * 5 + 1);
                shFloatToParam(stop->color.g, count, values, floats, i * 5 + 2);
                shFloatToParam(stop->color.b, count, values, floats, i * 5 + 3);
                shFloatToParam(stop->color.a, count, values, floats, i * 5 + 4);
            }

            break;
        }

        case VG_PAINT_LINEAR_GRADIENT:
            SH_RETURN_ERR_IF(count > 4, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            for (i = 0; i < 4; ++i)
                shFloatToParam(((SHPaint*)object)->linearGradient[i],
                               count, values, floats, i);
            break;

        case VG_PAINT_RADIAL_GRADIENT:
            SH_RETURN_ERR_IF(count > 5, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            for (i = 0; i < 5; ++i)
                shFloatToParam(((SHPaint*)object)->radialGradient[i],
                               count, values, floats, i);
            break;

        case VG_PAINT_PATTERN_TILING_MODE:
            SH_RETURN_ERR_IF(count != 1, VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
            shIntToParam(((SHPaint*)object)->tilingMode, count, values, floats, 0);
            break;

        default:
            /* Invalid VGParamType */
            SH_RETURN_ERR(VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        }
        break;
    case SH_RESOURCE_IMAGE:
        switch (ptype) { /* Image parameters */

            /* TODO: output image parameters when image implemented */
        case VG_IMAGE_FORMAT:
            break;

        case VG_IMAGE_WIDTH:
            break;

        case VG_IMAGE_HEIGHT:
            break;

        default:
            /* Invalid VGParamType */
            SH_RETURN_ERR(VG_ILLEGAL_ARGUMENT_ERROR, SH_NO_RETVAL);
        }
        break;

    default:
        /* Invalid resource handle */
        SH_ASSERT(rtype != SH_RESOURCE_INVALID);
        break;
    }

    SH_RETURN(SH_NO_RETVAL);
}

/*----------------------------------------------------------
 * Returns a resource parameter of a single float value
 *----------------------------------------------------------*/

VG_API_CALL VGfloat vgGetParameterf(VGHandle object, VGint paramType)
{
    VGfloat retval = 0.0f;
    SHResourceType resType;
    VG_GETCONTEXT(retval);

    /* Validate object */
    resType = shGetResourceType(context, object);
    VG_RETURN_ERR_IF(resType == SH_RESOURCE_INVALID,
                     VG_BAD_HANDLE_ERROR, retval);

    /* Check if param vector */
    VG_RETURN_ERR_IF(shIsParamVector(paramType),
                     VG_ILLEGAL_ARGUMENT_ERROR, retval);

    /* Error code will be set by shGetParameter() */
    shGetParameter(context, object, resType, paramType, 1, &retval, 1);
    VG_RETURN(retval);
}

/*----------------------------------------------------------
 * Returns a resource parameter of a single integer value
 *----------------------------------------------------------*/

VG_API_CALL VGint vgGetParameteri(VGHandle object, VGint paramType)
{
    VGint retval = 0;
    SHResourceType resType;
    VG_GETCONTEXT(retval);

    /* Validate object */
    resType = shGetResourceType(context, object);
    VG_RETURN_ERR_IF(resType == SH_RESOURCE_INVALID,
                     VG_BAD_HANDLE_ERROR, retval);

    /* Check if param vector */
    VG_RETURN_ERR_IF(shIsParamVector(paramType),
                     VG_ILLEGAL_ARGUMENT_ERROR, retval);

    /* Error code will be set by shGetParameter() */
    shGetParameter(context, object, resType, paramType, 1, &retval, 0);
    VG_RETURN(retval);
}

/*----------------------------------------------------------
 * Outputs a resource parameter of a float vector value
 *----------------------------------------------------------*/

VG_API_CALL void vgGetParameterfv(VGHandle object, VGint paramType,
                                  VGint count, VGfloat* values)
{
    SHResourceType resType;
    VG_GETCONTEXT(VG_NO_RETVAL);

    /* Validate object */
    resType = shGetResourceType(context, object);
    VG_RETURN_ERR_IF(resType == SH_RESOURCE_INVALID,
                     VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);

    /* TODO: Check output array alignment */

    /* Error code will be set by shGetParameter() */
    shGetParameter(context, object, resType, paramType, count, values, 1);
    VG_RETURN(VG_NO_RETVAL);
}

/*----------------------------------------------------------
 * Outputs a resource parameter of an integer vector value
 *----------------------------------------------------------*/

VG_API_CALL void vgGetParameteriv(VGHandle object, VGint paramType,
                                  VGint count, VGint* values)
{
    SHResourceType resType;
    VG_GETCONTEXT(VG_NO_RETVAL);

    /* Validate object */
    resType = shGetResourceType(context, object);
    VG_RETURN_ERR_IF(resType == SH_RESOURCE_INVALID,
                     VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);

    /* TODO: Check output array alignment */

    /* Error code will be set by shGetParameter() */
    shGetParameter(context, object, resType, paramType, count, values, 0);
    VG_RETURN(VG_NO_RETVAL);
}

/*---------------------------------------------------------
 * Returns the size of the output array required to
 * receive the whole vector of resource parameter values
 *---------------------------------------------------------*/

VG_API_CALL VGint vgGetParameterVectorSize(VGHandle object, VGint ptype)
{
    int retval = 0;
    SHResourceType rtype;
    VG_GETCONTEXT(retval);

    /* Validate object */
    rtype = shGetResourceType(context, object);
    VG_RETURN_ERR_IF(rtype == SH_RESOURCE_INVALID,
                     VG_BAD_HANDLE_ERROR, retval);

    switch (rtype) {
    case SH_RESOURCE_PATH:
        switch (ptype) { /* Path parameters */

        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:
            retval = 1;
            break;

        default:
            /* Invalid VGParamType */
            VG_RETURN_ERR(VG_ILLEGAL_ARGUMENT_ERROR, retval);
        }
        break;
    case SH_RESOURCE_PAINT:
        switch (ptype) { /* Paint parameters */

        case VG_PAINT_TYPE:
            retval = 1;
            break;

        case VG_PAINT_COLOR:
            retval = 4;
            break;

        case VG_PAINT_COLOR_RAMP_SPREAD_MODE:
            retval = 1;
            break;

        case VG_PAINT_COLOR_RAMP_PREMULTIPLIED:
            retval = 1;
            break;

        case VG_PAINT_COLOR_RAMP_STOPS:
            retval = ((SHPaint*)object)->stops.size * 5;
            break;

        case VG_PAINT_LINEAR_GRADIENT:
            retval = 4;
            break;

        case VG_PAINT_RADIAL_GRADIENT:
            retval = 5;
            break;

        case VG_PAINT_PATTERN_TILING_MODE:
            retval = 1;
            break;

        default:
            /* Invalid VGParamType */
            VG_RETURN_ERR(VG_ILLEGAL_ARGUMENT_ERROR, retval);
        }
        break;
    case SH_RESOURCE_IMAGE:
        switch (ptype) { /* Image parameters */

        case VG_IMAGE_FORMAT:
        case VG_IMAGE_WIDTH:
        case VG_IMAGE_HEIGHT:
            retval = 1;
            break;

        default:
            /* Invalid VGParamType */
            VG_RETURN_ERR(VG_ILLEGAL_ARGUMENT_ERROR, retval);
        }
        break;

    default:
        /* Invalid resource handle */
        SH_ASSERT(rtype != SH_RESOURCE_INVALID);
        break;
    }

    VG_RETURN(retval);
}

VG_API_CALL const VGubyte* vgGetString(VGStringID name)
{
    VG_GETCONTEXT(NULL);

    switch (name) {
    case VG_VENDOR:
        VG_RETURN((const VGubyte*)context->vendor);
    case VG_RENDERER:
        VG_RETURN((const VGubyte*)context->renderer);
    case VG_VERSION:
        VG_RETURN((const VGubyte*)context->version);
    case VG_EXTENSIONS:
        VG_RETURN((const VGubyte*)context->extensions);
    default:
        VG_RETURN(NULL);
    }
}
