/**************************************************************************
*
* Copyright 2009 VMware, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS 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 NON-INFRINGEMENT.
* IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS 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
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**************************************************************************/
#include "VG/openvg.h"
#include "vg_context.h"
#include "handle.h"
#include "path.h"
#include "api.h"
#include "pipe/p_context.h"
VGPath vegaCreatePath(VGint pathFormat,
VGPathDatatype datatype,
VGfloat scale, VGfloat bias,
VGint segmentCapacityHint,
VGint coordCapacityHint,
VGbitfield capabilities)
{
struct vg_context *ctx = vg_current_context();
if (pathFormat != VG_PATH_FORMAT_STANDARD) {
vg_set_error(ctx, VG_UNSUPPORTED_PATH_FORMAT_ERROR);
return VG_INVALID_HANDLE;
}
if (datatype < VG_PATH_DATATYPE_S_8 ||
datatype > VG_PATH_DATATYPE_F) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return VG_INVALID_HANDLE;
}
if (!scale) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return VG_INVALID_HANDLE;
}
return path_to_handle(path_create(datatype, scale, bias,
segmentCapacityHint, coordCapacityHint,
capabilities));
}
void vegaClearPath(VGPath path, VGbitfield capabilities)
{
struct vg_context *ctx = vg_current_context();
struct path *p = 0;
if (path == VG_INVALID_HANDLE) {
vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
return;
}
p = handle_to_path(path);
path_clear(p, capabilities);
}
void vegaDestroyPath(VGPath p)
{
struct path *path = 0;
struct vg_context *ctx = vg_current_context();
if (p == VG_INVALID_HANDLE) {
vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
return;
}
path = handle_to_path(p);
path_destroy(path);
}
void vegaRemovePathCapabilities(VGPath path,
VGbitfield capabilities)
{
struct vg_context *ctx = vg_current_context();
VGbitfield current;
struct path *p;
if (path == VG_INVALID_HANDLE) {
vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
return;
}
p = handle_to_path(path);
current = path_capabilities(p);
path_set_capabilities(p, (current &
(~(capabilities & VG_PATH_CAPABILITY_ALL))));
}
VGbitfield vegaGetPathCapabilities(VGPath path)
{
struct vg_context *ctx = vg_current_context();
struct path *p = 0;
if (path == VG_INVALID_HANDLE) {
vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
return 0;
}
p = handle_to_path(path);
return path_capabilities(p);
}
void vegaAppendPath(VGPath dstPath, VGPath srcPath)
{
struct vg_context *ctx = vg_current_context();
struct path *src, *dst;
if (dstPath == VG_INVALID_HANDLE || srcPath == VG_INVALID_HANDLE) {
vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
return;
}
src = handle_to_path(srcPath);
dst = handle_to_path(dstPath);
if (!(path_capabilities(src) & VG_PATH_CAPABILITY_APPEND_FROM) ||
!(path_capabilities(dst) & VG_PATH_CAPABILITY_APPEND_TO)) {
vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
return;
}
path_append_path(dst, src);
}
void vegaAppendPathData(VGPath dstPath,
VGint numSegments,
const VGubyte * pathSegments,
const void * pathData)
{
struct vg_context *ctx = vg_current_context();
struct path *p = 0;
VGint i;
if (dstPath == VG_INVALID_HANDLE) {
vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
return;
}
if (!pathSegments) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
if (numSegments <= 0) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
for (i = 0; i < numSegments; ++i) {
if (pathSegments[i] > VG_LCWARC_TO_REL) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
}
p = handle_to_path(dstPath);
if (!p || !is_aligned_to(p, path_datatype_size(p))) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
if (!(path_capabilities(p)&VG_PATH_CAPABILITY_APPEND_TO)) {
vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
return;
}
path_append_data(p, numSegments, pathSegments, pathData);
}
void vegaModifyPathCoords(VGPath dstPath,
VGint startIndex,
VGint numSegments,
const void * pathData)
{
struct vg_context *ctx = vg_current_context();
struct path *p = 0;
if (dstPath == VG_INVALID_HANDLE) {
vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
return;
}
if (startIndex < 0 || numSegments <= 0) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
p = handle_to_path(dstPath);
if (!pathData || !is_aligned_to(pathData, path_datatype_size(p))) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
if (startIndex + numSegments > path_num_segments(p)) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
if (!(path_capabilities(p)&VG_PATH_CAPABILITY_MODIFY)) {
vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
return;
}
path_modify_coords(p, startIndex, numSegments, pathData);
}
void vegaTransformPath(VGPath dstPath, VGPath srcPath)
{
struct vg_context *ctx = vg_current_context();
struct path *src = 0, *dst = 0;
if (dstPath == VG_INVALID_HANDLE || srcPath == VG_INVALID_HANDLE) {
vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
return;
}
src = handle_to_path(srcPath);
dst = handle_to_path(dstPath);
if (!(path_capabilities(src) & VG_PATH_CAPABILITY_TRANSFORM_FROM) ||
!(path_capabilities(dst) & VG_PATH_CAPABILITY_TRANSFORM_TO)) {
vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
return;
}
path_transform(dst, src);
}
VGboolean vegaInterpolatePath(VGPath dstPath,
VGPath startPath,
VGPath endPath,
VGfloat amount)
{
struct vg_context *ctx = vg_current_context();
struct path *start = 0, *dst = 0, *end = 0;
if (dstPath == VG_INVALID_HANDLE ||
startPath == VG_INVALID_HANDLE ||
endPath == VG_INVALID_HANDLE) {
vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
return VG_FALSE;
}
dst = handle_to_path(dstPath);
start = handle_to_path(startPath);
end = handle_to_path(endPath);
if (!(path_capabilities(dst) & VG_PATH_CAPABILITY_INTERPOLATE_TO) ||
!(path_capabilities(start) & VG_PATH_CAPABILITY_INTERPOLATE_FROM) ||
!(path_capabilities(end) & VG_PATH_CAPABILITY_INTERPOLATE_FROM)) {
vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
return VG_FALSE;
}
return path_interpolate(dst,
start, end, amount);
}
VGfloat vegaPathLength(VGPath path,
VGint startSegment,
VGint numSegments)
{
struct vg_context *ctx = vg_current_context();
struct path *p = 0;
if (path == VG_INVALID_HANDLE) {
vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
return -1;
}
if (startSegment < 0) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return -1;
}
if (numSegments <= 0) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return -1;
}
p = handle_to_path(path);
if (!(path_capabilities(p) & VG_PATH_CAPABILITY_PATH_LENGTH)) {
vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
return -1;
}
if (startSegment + numSegments > path_num_segments(p)) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return -1;
}
return path_length(p, startSegment, numSegments);
}
void vegaPointAlongPath(VGPath path,
VGint startSegment,
VGint numSegments,
VGfloat distance,
VGfloat * x, VGfloat * y,
VGfloat * tangentX,
VGfloat * tangentY)
{
struct vg_context *ctx = vg_current_context();
struct path *p = 0;
VGbitfield caps;
if (path == VG_INVALID_HANDLE) {
vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
return;
}
if (startSegment < 0) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
if (numSegments <= 0) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
if (!is_aligned(x) || !is_aligned(y) ||
!is_aligned(tangentX) || !is_aligned(tangentY)) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
p = handle_to_path(path);
caps = path_capabilities(p);
if (!(caps & VG_PATH_CAPABILITY_POINT_ALONG_PATH) ||
!(caps & VG_PATH_CAPABILITY_TANGENT_ALONG_PATH)) {
vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
return;
}
if (startSegment + numSegments > path_num_segments(p)) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
{
VGfloat point[2], normal[2];
path_point(p, startSegment, numSegments, distance,
point, normal);
if (x)
*x = point[0];
if (y)
*y = point[1];
if (tangentX)
*tangentX = -normal[1];
if (tangentY)
*tangentY = normal[0];
}
}
void vegaPathBounds(VGPath path,
VGfloat * minX,
VGfloat * minY,
VGfloat * width,
VGfloat * height)
{
struct vg_context *ctx = vg_current_context();
struct path *p = 0;
VGbitfield caps;
if (path == VG_INVALID_HANDLE) {
vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
return;
}
if (!minX || !minY || !width || !height) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
if (!is_aligned(minX) || !is_aligned(minY) ||
!is_aligned(width) || !is_aligned(height)) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
p = handle_to_path(path);
caps = path_capabilities(p);
if (!(caps & VG_PATH_CAPABILITY_PATH_BOUNDS)) {
vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
return;
}
path_bounding_rect(p, minX, minY, width, height);
}
void vegaPathTransformedBounds(VGPath path,
VGfloat * minX,
VGfloat * minY,
VGfloat * width,
VGfloat * height)
{
struct vg_context *ctx = vg_current_context();
struct path *p = 0;
VGbitfield caps;
if (path == VG_INVALID_HANDLE) {
vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
return;
}
if (!minX || !minY || !width || !height) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
if (!is_aligned(minX) || !is_aligned(minY) ||
!is_aligned(width) || !is_aligned(height)) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
p = handle_to_path(path);
caps = path_capabilities(p);
if (!(caps & VG_PATH_CAPABILITY_PATH_TRANSFORMED_BOUNDS)) {
vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
return;
}
#if 0
/* faster, but seems to have precision problems... */
path_bounding_rect(p, minX, minY, width, height);
if (*width > 0 && *height > 0) {
VGfloat pts[] = {*minX, *minY,
*minX + *width, *minY,
*minX + *width, *minY + *height,
*minX, *minY + *height};
struct matrix *matrix = &ctx->state.vg.path_user_to_surface_matrix;
VGfloat maxX, maxY;
matrix_map_point(matrix, pts[0], pts[1], pts + 0, pts + 1);
matrix_map_point(matrix, pts[2], pts[3], pts + 2, pts + 3);
matrix_map_point(matrix, pts[4], pts[5], pts + 4, pts + 5);
matrix_map_point(matrix, pts[6], pts[7], pts + 6, pts + 7);
*minX = MIN2(pts[0], MIN2(pts[2], MIN2(pts[4], pts[6])));
*minY = MIN2(pts[1], MIN2(pts[3], MIN2(pts[5], pts[7])));
maxX = MAX2(pts[0], MAX2(pts[2], MAX2(pts[4], pts[6])));
maxY = MAX2(pts[1], MAX2(pts[3], MAX2(pts[5], pts[7])));
*width = maxX - *minX;
*height = maxY - *minY;
}
#else
{
struct path *dst = path_create(VG_PATH_DATATYPE_F, 1.0, 0,
0, 0, VG_PATH_CAPABILITY_ALL);
path_transform(dst, p);
path_bounding_rect(dst, minX, minY, width, height);
path_destroy(dst);
}
#endif
}
void vegaDrawPath(VGPath path, VGbitfield paintModes)
{
struct vg_context *ctx = vg_current_context();
struct path *p = handle_to_path(path);
if (path == VG_INVALID_HANDLE) {
vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
return;
}
if (!(paintModes & (VG_STROKE_PATH | VG_FILL_PATH))) {
vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
return;
}
if (path_is_empty(p))
return;
path_render(p, paintModes,
&ctx->state.vg.path_user_to_surface_matrix);
}