1/**************************************************************************
2 *
3 * Copyright 2009 VMware, Inc.  All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 **************************************************************************/
26
27#include "VG/openvg.h"
28
29#include "vg_context.h"
30#include "handle.h"
31#include "path.h"
32#include "api.h"
33
34#include "pipe/p_context.h"
35
36VGPath vegaCreatePath(VGint pathFormat,
37                      VGPathDatatype datatype,
38                      VGfloat scale, VGfloat bias,
39                      VGint segmentCapacityHint,
40                      VGint coordCapacityHint,
41                      VGbitfield capabilities)
42{
43   struct vg_context *ctx = vg_current_context();
44
45   if (pathFormat != VG_PATH_FORMAT_STANDARD) {
46      vg_set_error(ctx, VG_UNSUPPORTED_PATH_FORMAT_ERROR);
47      return VG_INVALID_HANDLE;
48   }
49   if (datatype < VG_PATH_DATATYPE_S_8 ||
50       datatype > VG_PATH_DATATYPE_F) {
51      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
52      return VG_INVALID_HANDLE;
53   }
54   if (!scale) {
55      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
56      return VG_INVALID_HANDLE;
57   }
58
59   return path_to_handle(path_create(datatype, scale, bias,
60                                     segmentCapacityHint, coordCapacityHint,
61                                     capabilities));
62}
63
64void vegaClearPath(VGPath path, VGbitfield capabilities)
65{
66   struct vg_context *ctx = vg_current_context();
67   struct path *p = 0;
68
69   if (path == VG_INVALID_HANDLE) {
70      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
71      return;
72   }
73
74   p = handle_to_path(path);
75   path_clear(p, capabilities);
76}
77
78void vegaDestroyPath(VGPath p)
79{
80   struct path *path = 0;
81   struct vg_context *ctx = vg_current_context();
82
83   if (p == VG_INVALID_HANDLE) {
84      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
85      return;
86   }
87
88   path = handle_to_path(p);
89   path_destroy(path);
90}
91
92void vegaRemovePathCapabilities(VGPath path,
93                                VGbitfield capabilities)
94{
95   struct vg_context *ctx = vg_current_context();
96   VGbitfield current;
97   struct path *p;
98
99   if (path == VG_INVALID_HANDLE) {
100      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
101      return;
102   }
103
104   p = handle_to_path(path);
105   current = path_capabilities(p);
106   path_set_capabilities(p, (current &
107                             (~(capabilities & VG_PATH_CAPABILITY_ALL))));
108}
109
110VGbitfield vegaGetPathCapabilities(VGPath path)
111{
112   struct vg_context *ctx = vg_current_context();
113   struct path *p = 0;
114
115   if (path == VG_INVALID_HANDLE) {
116      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
117      return 0;
118   }
119   p = handle_to_path(path);
120   return path_capabilities(p);
121}
122
123void vegaAppendPath(VGPath dstPath, VGPath srcPath)
124{
125   struct vg_context *ctx = vg_current_context();
126   struct path *src, *dst;
127
128   if (dstPath == VG_INVALID_HANDLE || srcPath == VG_INVALID_HANDLE) {
129      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
130      return;
131   }
132   src = handle_to_path(srcPath);
133   dst = handle_to_path(dstPath);
134
135   if (!(path_capabilities(src) & VG_PATH_CAPABILITY_APPEND_FROM) ||
136       !(path_capabilities(dst) & VG_PATH_CAPABILITY_APPEND_TO)) {
137      vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
138      return;
139   }
140   path_append_path(dst, src);
141}
142
143void vegaAppendPathData(VGPath dstPath,
144                        VGint numSegments,
145                        const VGubyte * pathSegments,
146                        const void * pathData)
147{
148   struct vg_context *ctx = vg_current_context();
149   struct path *p = 0;
150   VGint i;
151
152   if (dstPath == VG_INVALID_HANDLE) {
153      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
154      return;
155   }
156   if (!pathSegments) {
157      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
158      return;
159   }
160   if (numSegments <= 0) {
161      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
162      return;
163   }
164   for (i = 0; i < numSegments; ++i) {
165      if (pathSegments[i] > VG_LCWARC_TO_REL) {
166         vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
167         return;
168      }
169   }
170
171   p = handle_to_path(dstPath);
172
173   if (!p || !is_aligned_to(p, path_datatype_size(p))) {
174      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
175      return;
176   }
177
178   if (!(path_capabilities(p)&VG_PATH_CAPABILITY_APPEND_TO)) {
179      vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
180      return;
181   }
182
183   path_append_data(p, numSegments, pathSegments, pathData);
184}
185
186void vegaModifyPathCoords(VGPath dstPath,
187                          VGint startIndex,
188                          VGint numSegments,
189                          const void * pathData)
190{
191   struct vg_context *ctx = vg_current_context();
192   struct path *p = 0;
193
194   if (dstPath == VG_INVALID_HANDLE) {
195      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
196      return;
197   }
198   if (startIndex < 0 || numSegments <= 0) {
199      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
200      return;
201   }
202
203   p = handle_to_path(dstPath);
204
205   if (!pathData || !is_aligned_to(pathData, path_datatype_size(p))) {
206      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
207      return;
208   }
209
210   if (startIndex + numSegments > path_num_segments(p)) {
211      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
212      return;
213   }
214   if (!(path_capabilities(p)&VG_PATH_CAPABILITY_MODIFY)) {
215      vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
216      return;
217   }
218   path_modify_coords(p, startIndex, numSegments, pathData);
219}
220
221void vegaTransformPath(VGPath dstPath, VGPath srcPath)
222{
223   struct vg_context *ctx = vg_current_context();
224   struct path *src = 0, *dst = 0;
225
226   if (dstPath == VG_INVALID_HANDLE || srcPath == VG_INVALID_HANDLE) {
227      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
228      return;
229   }
230   src = handle_to_path(srcPath);
231   dst = handle_to_path(dstPath);
232
233   if (!(path_capabilities(src) & VG_PATH_CAPABILITY_TRANSFORM_FROM) ||
234       !(path_capabilities(dst) & VG_PATH_CAPABILITY_TRANSFORM_TO)) {
235      vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
236      return;
237   }
238   path_transform(dst, src);
239}
240
241VGboolean vegaInterpolatePath(VGPath dstPath,
242                              VGPath startPath,
243                              VGPath endPath,
244                              VGfloat amount)
245{
246   struct vg_context *ctx = vg_current_context();
247   struct path *start = 0, *dst = 0, *end = 0;
248
249   if (dstPath == VG_INVALID_HANDLE ||
250       startPath == VG_INVALID_HANDLE ||
251       endPath == VG_INVALID_HANDLE) {
252      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
253      return VG_FALSE;
254   }
255   dst = handle_to_path(dstPath);
256   start = handle_to_path(startPath);
257   end = handle_to_path(endPath);
258
259   if (!(path_capabilities(dst) & VG_PATH_CAPABILITY_INTERPOLATE_TO) ||
260       !(path_capabilities(start) & VG_PATH_CAPABILITY_INTERPOLATE_FROM) ||
261       !(path_capabilities(end) & VG_PATH_CAPABILITY_INTERPOLATE_FROM)) {
262      vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
263      return VG_FALSE;
264   }
265
266   return path_interpolate(dst,
267                           start, end, amount);
268}
269
270VGfloat vegaPathLength(VGPath path,
271                       VGint startSegment,
272                       VGint numSegments)
273{
274   struct vg_context *ctx = vg_current_context();
275   struct path *p = 0;
276
277   if (path == VG_INVALID_HANDLE) {
278      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
279      return -1;
280   }
281   if (startSegment < 0) {
282      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
283      return -1;
284   }
285   if (numSegments <= 0) {
286      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
287      return -1;
288   }
289   p = handle_to_path(path);
290
291   if (!(path_capabilities(p) & VG_PATH_CAPABILITY_PATH_LENGTH)) {
292      vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
293      return -1;
294   }
295   if (startSegment + numSegments > path_num_segments(p)) {
296      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
297      return -1;
298   }
299
300   return path_length(p, startSegment, numSegments);
301}
302
303void vegaPointAlongPath(VGPath path,
304                        VGint startSegment,
305                        VGint numSegments,
306                        VGfloat distance,
307                        VGfloat * x, VGfloat * y,
308                        VGfloat * tangentX,
309                        VGfloat * tangentY)
310{
311   struct vg_context *ctx = vg_current_context();
312   struct path *p = 0;
313   VGbitfield caps;
314
315   if (path == VG_INVALID_HANDLE) {
316      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
317      return;
318   }
319   if (startSegment < 0) {
320      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
321      return;
322   }
323   if (numSegments <= 0) {
324      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
325      return;
326   }
327
328   if (!is_aligned(x) || !is_aligned(y) ||
329       !is_aligned(tangentX) || !is_aligned(tangentY)) {
330      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
331      return;
332   }
333
334   p = handle_to_path(path);
335
336   caps = path_capabilities(p);
337   if (!(caps & VG_PATH_CAPABILITY_POINT_ALONG_PATH) ||
338       !(caps & VG_PATH_CAPABILITY_TANGENT_ALONG_PATH)) {
339      vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
340      return;
341   }
342
343   if (startSegment + numSegments > path_num_segments(p)) {
344      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
345      return;
346   }
347
348   {
349      VGfloat point[2], normal[2];
350      path_point(p, startSegment, numSegments, distance,
351                 point, normal);
352      if (x)
353         *x = point[0];
354      if (y)
355         *y = point[1];
356      if (tangentX)
357         *tangentX = -normal[1];
358      if (tangentY)
359         *tangentY = normal[0];
360   }
361}
362
363void vegaPathBounds(VGPath path,
364                    VGfloat * minX,
365                    VGfloat * minY,
366                    VGfloat * width,
367                    VGfloat * height)
368{
369   struct vg_context *ctx = vg_current_context();
370   struct path *p = 0;
371   VGbitfield caps;
372
373   if (path == VG_INVALID_HANDLE) {
374      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
375      return;
376   }
377
378   if (!minX || !minY || !width || !height) {
379      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
380      return;
381   }
382
383   if (!is_aligned(minX) || !is_aligned(minY) ||
384       !is_aligned(width) || !is_aligned(height)) {
385      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
386      return;
387   }
388
389   p = handle_to_path(path);
390
391   caps = path_capabilities(p);
392   if (!(caps & VG_PATH_CAPABILITY_PATH_BOUNDS)) {
393      vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
394      return;
395   }
396
397   path_bounding_rect(p, minX, minY, width, height);
398}
399
400void vegaPathTransformedBounds(VGPath path,
401                               VGfloat * minX,
402                               VGfloat * minY,
403                               VGfloat * width,
404                               VGfloat * height)
405{
406   struct vg_context *ctx = vg_current_context();
407   struct path *p = 0;
408   VGbitfield caps;
409
410   if (path == VG_INVALID_HANDLE) {
411      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
412      return;
413   }
414
415   if (!minX || !minY || !width || !height) {
416      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
417      return;
418   }
419
420   if (!is_aligned(minX) || !is_aligned(minY) ||
421       !is_aligned(width) || !is_aligned(height)) {
422      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
423      return;
424   }
425
426   p = handle_to_path(path);
427
428   caps = path_capabilities(p);
429   if (!(caps & VG_PATH_CAPABILITY_PATH_TRANSFORMED_BOUNDS)) {
430      vg_set_error(ctx, VG_PATH_CAPABILITY_ERROR);
431      return;
432   }
433
434#if 0
435   /* faster, but seems to have precision problems... */
436   path_bounding_rect(p, minX, minY, width, height);
437   if (*width > 0 && *height > 0) {
438      VGfloat pts[] = {*minX,          *minY,
439                       *minX + *width, *minY,
440                       *minX + *width, *minY + *height,
441                       *minX,          *minY + *height};
442      struct matrix *matrix = &ctx->state.vg.path_user_to_surface_matrix;
443      VGfloat maxX, maxY;
444      matrix_map_point(matrix, pts[0], pts[1], pts + 0, pts + 1);
445      matrix_map_point(matrix, pts[2], pts[3], pts + 2, pts + 3);
446      matrix_map_point(matrix, pts[4], pts[5], pts + 4, pts + 5);
447      matrix_map_point(matrix, pts[6], pts[7], pts + 6, pts + 7);
448      *minX = MIN2(pts[0], MIN2(pts[2], MIN2(pts[4], pts[6])));
449      *minY = MIN2(pts[1], MIN2(pts[3], MIN2(pts[5], pts[7])));
450      maxX = MAX2(pts[0], MAX2(pts[2], MAX2(pts[4], pts[6])));
451      maxY = MAX2(pts[1], MAX2(pts[3], MAX2(pts[5], pts[7])));
452      *width  = maxX - *minX;
453      *height = maxY - *minY;
454   }
455#else
456   {
457      struct path *dst = path_create(VG_PATH_DATATYPE_F, 1.0, 0,
458                                     0, 0, VG_PATH_CAPABILITY_ALL);
459      path_transform(dst, p);
460      path_bounding_rect(dst, minX, minY, width, height);
461      path_destroy(dst);
462   }
463#endif
464}
465
466
467void vegaDrawPath(VGPath path, VGbitfield paintModes)
468{
469   struct vg_context *ctx = vg_current_context();
470   struct path *p = handle_to_path(path);
471
472   if (path == VG_INVALID_HANDLE) {
473      vg_set_error(ctx, VG_BAD_HANDLE_ERROR);
474      return;
475   }
476
477   if (!(paintModes & (VG_STROKE_PATH | VG_FILL_PATH))) {
478      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
479      return;
480   }
481
482   if (path_is_empty(p))
483      return;
484   path_render(p, paintModes,
485         &ctx->state.vg.path_user_to_surface_matrix);
486}
487
488