| /************************************************************************** |
| * |
| * 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); |
| } |
| |