diff -r 39e5f73667ba -r c2ef9095503a hostsupport/hostopenvg/src/riApi.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hostsupport/hostopenvg/src/riApi.cpp Wed Oct 06 17:59:01 2010 +0100 @@ -0,0 +1,3839 @@ +/*------------------------------------------------------------------------ + * + * 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 + +//============================================================================================== + +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 scissor; + for(int i=0;im_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 inputStrokeDashPattern; + Array strokeDashPattern; + for(int i=0;im_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;im_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;im_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;im_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 colorRampStops; + Array inputColorRampStops; + RIfloat prevOffset = -RI_FLOAT_MAX; + bool valid = true; + for(int i=0;i= 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;im_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 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((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* 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* scissors = !context->m_scissoring ? NULL : &context->m_scissor; + Image input(Color::formatToDescriptor(dataFormat), width, height, dataStride, const_cast((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* 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;ifindGlyph(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;ifindGlyph(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 +}