1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/*=============================================================================
29Copyright (c) 2008 Broadcom Europe Limited.
30All rights reserved.
31
32Project : khronos
33Module : VG client
34
35FILE DESCRIPTION
36VG client-side function definitions. Dispatches VG calls via RPC or direct
37call. Some functions require support for control messages just over 1kB in
38length, 2kB should be fine for all functions.
39=============================================================================*/
40
41/*
42 Potential spec bugs:
43
44 vgImageSubData, vgGetImageSubData, vgWritePixels, and vgReadPixels require
45 the data pointer to be aligned, but do not require the stride to be
46 aligned. This seems a little useless, so we require that both the pointer
47 and stride are aligned (unless height is 1, in which case we just require
48 that the pointer is aligned)
49*/
50
51#define VG_VGEXT_PROTOTYPES /* we want the prototypes so the compiler will check that the signatures match */
52
53#include "interface/khronos/common/khrn_client_mangle.h"
54#include "interface/khronos/common/khrn_int_common.h"
55
56#include "interface/khronos/common/khrn_client_platform.h"
57#include "interface/khronos/common/khrn_client_rpc.h"
58#include "interface/khronos/vg/vg_client.h"
59
60#include "interface/khronos/egl/egl_client_config.h"
61
62#include "interface/khronos/common/khrn_int_color.h"
63#include "interface/khronos/common/khrn_int_math.h"
64#include "interface/khronos/common/khrn_int_util.h"
65
66#include "interface/khronos/include/VG/openvg.h"
67#include "interface/khronos/include/VG/vgext.h"
68#include "interface/khronos/include/VG/vgu.h"
69
70#include "interface/khronos/vg/vg_int_config.h"
71#include "interface/khronos/vg/vg_int_mat3x3.h"
72#ifdef RPC_DIRECT
73 #include "interface/khronos/vg/vg_int_impl.h" /* for _impl function calls */
74#endif
75#if defined(WIN32) || defined(__mips__)
76#include "interface/khronos/common/khrn_int_misc_impl.h"
77#endif
78
79#include "interface/khronos/vg/vg_int_util.h"
80
81#include <stdlib.h>
82#include <string.h>
83
84/******************************************************************************
85shared state
86******************************************************************************/
87
88VG_CLIENT_SHARED_STATE_T *vg_client_shared_state_alloc(void)
89{
90 VG_CLIENT_SHARED_STATE_T *shared_state;
91 KHR_STATUS_T status;
92
93 shared_state = (VG_CLIENT_SHARED_STATE_T *)khrn_platform_malloc(sizeof(VG_CLIENT_SHARED_STATE_T), "VG_CLIENT_SHARED_STATE_T");
94 if (!shared_state) {
95 return NULL;
96 }
97
98 status = platform_mutex_create(&shared_state->mutex);
99 if (status != KHR_SUCCESS) {
100 khrn_platform_free(shared_state);
101 return NULL;
102 }
103
104 if (!khrn_pointer_map_init(&shared_state->objects, 128)) {
105 platform_mutex_destroy(&shared_state->mutex);
106 khrn_platform_free(shared_state);
107 return NULL;
108 }
109
110 shared_state->ref_count = 1;
111 shared_state->stems_count = 0;
112
113 return shared_state;
114}
115
116static void object_free(void *object);
117
118static void object_free_callback(KHRN_POINTER_MAP_T *pointer_map, uint32_t key, void *object, void *data)
119{
120 UNUSED(pointer_map);
121 UNUSED(key);
122 UNUSED(data);
123
124 object_free(object);
125}
126
127void vg_client_shared_state_free(VG_CLIENT_SHARED_STATE_T *shared_state)
128{
129 vcos_assert(shared_state->ref_count == 0);
130 khrn_pointer_map_iterate(&shared_state->objects, object_free_callback, NULL);
131 khrn_pointer_map_term(&shared_state->objects);
132 platform_mutex_destroy(&shared_state->mutex);
133 khrn_platform_free(shared_state);
134}
135
136/******************************************************************************
137state
138******************************************************************************/
139
140VG_CLIENT_STATE_T *vg_client_state_alloc(VG_CLIENT_SHARED_STATE_T *shared_state)
141{
142 VGuint i;
143
144 VG_CLIENT_STATE_T *state = (VG_CLIENT_STATE_T *)khrn_platform_malloc(sizeof(VG_CLIENT_STATE_T), "VG_CLIENT_STATE_T");
145 if (!state) {
146 return NULL;
147 }
148
149 vg_client_shared_state_acquire(shared_state);
150 state->shared_state = shared_state;
151
152 state->render_callback = NULL;
153 state->flush_callback = NULL;
154
155 state->matrix_mode = VG_MATRIX_PATH_USER_TO_SURFACE;
156 for (i = 0; i != ARR_COUNT(state->matrices); ++i) {
157 vg_mat3x3_set_identity(&state->matrices[i].client);
158 vg_mat3x3_set_identity(&state->matrices[i].server);
159 }
160
161 state->fill_rule = VG_EVEN_ODD;
162 state->stroke_line_width = 1.0f;
163 state->stroke_cap_style = VG_CAP_BUTT;
164 state->stroke_join_style = VG_JOIN_MITER;
165 state->stroke_miter_limit = 4.0f;
166 state->stroke_dash_pattern_count = 0;
167 state->stroke_dash_phase = 0.0f;
168 state->stroke_dash_phase_reset = false;
169 state->image_quality = VG_IMAGE_QUALITY_FASTER;
170 state->image_mode = VG_DRAW_IMAGE_NORMAL;
171
172 state->scissoring = false;
173 state->scissor_rects_count = 0;
174
175 state->rendering_quality = VG_RENDERING_QUALITY_BETTER;
176
177 state->fill_paint = VG_INVALID_HANDLE;
178 state->stroke_paint = VG_INVALID_HANDLE;
179 state->tile_fill_color[0] = 0.0f;
180 state->tile_fill_color[1] = 0.0f;
181 state->tile_fill_color[2] = 0.0f;
182 state->tile_fill_color[3] = 0.0f;
183 state->clear_color[0] = 0.0f;
184 state->clear_color[1] = 0.0f;
185 state->clear_color[2] = 0.0f;
186 state->clear_color[3] = 0.0f;
187
188 state->color_transform = false;
189 state->color_transform_values[0] = 1.0f;
190 state->color_transform_values[1] = 1.0f;
191 state->color_transform_values[2] = 1.0f;
192 state->color_transform_values[3] = 1.0f;
193 state->color_transform_values[4] = 0.0f;
194 state->color_transform_values[5] = 0.0f;
195 state->color_transform_values[6] = 0.0f;
196 state->color_transform_values[7] = 0.0f;
197
198 state->blend_mode = VG_BLEND_SRC_OVER;
199 state->masking = false;
200
201 state->filter_format_linear = false;
202 state->filter_format_pre = false;
203 state->filter_channel_mask = VG_RED | VG_GREEN | VG_BLUE | VG_ALPHA;
204
205 state->pixel_layout = VG_PIXEL_LAYOUT_UNKNOWN;
206
207 return state;
208}
209
210void vg_client_state_free(VG_CLIENT_STATE_T *state)
211{
212 vg_client_shared_state_release(state->shared_state);
213 khrn_platform_free(state);
214}
215
216/******************************************************************************
217helpers
218******************************************************************************/
219
220static void clear_error(void)
221{
222 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
223 RPC_CALL0(vgClearError_impl,
224 thread,
225 VGCLEARERROR_ID);
226}
227
228static void set_error(VGErrorCode error)
229{
230 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
231 RPC_CALL1(vgSetError_impl,
232 thread,
233 VGSETERROR_ID,
234 RPC_ENUM(error));
235}
236
237static VGErrorCode get_error(void)
238{
239 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
240 return (VGErrorCode)RPC_ENUM_RES(RPC_CALL0_RES(vgGetError_impl,
241 thread,
242 VGGETERROR_ID));
243}
244
245static INLINE bool is_aligned(const void *p, VGuint alignment)
246{
247 vcos_assert(is_power_of_2(alignment));
248 return ((uintptr_t)p & (alignment - 1)) == 0;
249}
250
251static INLINE bool is_aligned_float(const VGfloat *p)
252{
253 return is_aligned(p, alignof(VGfloat));
254}
255
256static INLINE bool is_aligned_short(const VGshort *p)
257{
258 return is_aligned(p, alignof(VGshort));
259}
260
261static INLINE bool is_aligned_int(const VGint *p)
262{
263 return is_aligned(p, alignof(VGint));
264}
265
266static INLINE bool is_aligned_uint(const VGuint *p)
267{
268 return is_aligned(p, alignof(VGuint));
269}
270
271static bool is_aligned_path_datatype(const void *p, VGPathDatatype path_datatype)
272{
273 switch (path_datatype) {
274 case VG_PATH_DATATYPE_S_8: return true;
275 case VG_PATH_DATATYPE_S_16: return is_aligned_short((const VGshort *)p);
276 case VG_PATH_DATATYPE_S_32: return is_aligned_int((const VGint *)p);
277 case VG_PATH_DATATYPE_F: return is_aligned_float((const VGfloat *)p);
278 default: UNREACHABLE(); return false;
279 }
280}
281
282static bool is_aligned_image_format(const void *p, VGImageFormat image_format)
283{
284 switch (image_format) {
285 case VG_sRGBX_8888:
286 case VG_sRGBA_8888:
287 case VG_sRGBA_8888_PRE:
288 case VG_lRGBX_8888:
289 case VG_lRGBA_8888:
290 case VG_lRGBA_8888_PRE:
291 case VG_sXRGB_8888:
292 case VG_sARGB_8888:
293 case VG_sARGB_8888_PRE:
294 case VG_lXRGB_8888:
295 case VG_lARGB_8888:
296 case VG_lARGB_8888_PRE:
297 case VG_sBGRX_8888:
298 case VG_sBGRA_8888:
299 case VG_sBGRA_8888_PRE:
300 case VG_lBGRX_8888:
301 case VG_lBGRA_8888:
302 case VG_lBGRA_8888_PRE:
303 case VG_sXBGR_8888:
304 case VG_sABGR_8888:
305 case VG_sABGR_8888_PRE:
306 case VG_lXBGR_8888:
307 case VG_lABGR_8888:
308 case VG_lABGR_8888_PRE:
309 return is_aligned_uint((const VGuint *)p);
310 case VG_sRGB_565:
311 case VG_sRGBA_5551:
312 case VG_sRGBA_4444:
313 case VG_sARGB_1555:
314 case VG_sARGB_4444:
315 case VG_sBGR_565:
316 case VG_sBGRA_5551:
317 case VG_sBGRA_4444:
318 case VG_sABGR_1555:
319 case VG_sABGR_4444:
320 return is_aligned_short((const VGshort *)p);
321 case VG_sL_8:
322 case VG_lL_8:
323 case VG_A_8:
324 case VG_BW_1:
325 case VG_A_1:
326 case VG_A_4:
327 return true;
328 default:
329 UNREACHABLE();
330 return false;
331 }
332}
333
334static bool contains_illegal_segment(const VGubyte *segments, VGint segments_count)
335{
336 for (; segments_count != 0; ++segments, --segments_count) {
337 switch (*segments & ~VG_RELATIVE) {
338 case VG_CLOSE_PATH: break;
339 case VG_MOVE_TO: break;
340 case VG_LINE_TO: break;
341 case VG_HLINE_TO: break;
342 case VG_VLINE_TO: break;
343 case VG_QUAD_TO: break;
344 case VG_CUBIC_TO: break;
345 case VG_SQUAD_TO: break;
346 case VG_SCUBIC_TO: break;
347 case VG_SCCWARC_TO: break;
348 case VG_SCWARC_TO: break;
349 case VG_LCCWARC_TO: break;
350 case VG_LCWARC_TO: break;
351 default: return true;
352 }
353 }
354 return false;
355}
356
357static VGuint get_coords_count(const VGubyte *segments, VGint segments_count)
358{
359 VGuint coords_count = 0;
360 for (; segments_count != 0; ++segments, --segments_count) {
361 coords_count += get_segment_coords_count(*segments & ~VG_RELATIVE);
362 }
363 return coords_count;
364}
365
366static VGuint normalise_segment(VGuint segment)
367{
368 switch (segment) {
369 case VG_CLOSE_PATH: return VG_CLOSE_PATH;
370 case VG_MOVE_TO: return VG_MOVE_TO;
371 case VG_LINE_TO: return VG_LINE_TO;
372 case VG_HLINE_TO: return VG_LINE_TO;
373 case VG_VLINE_TO: return VG_LINE_TO;
374 case VG_QUAD_TO: return VG_CUBIC_TO;
375 case VG_CUBIC_TO: return VG_CUBIC_TO;
376 case VG_SQUAD_TO: return VG_CUBIC_TO;
377 case VG_SCUBIC_TO: return VG_CUBIC_TO;
378
379 /*
380 on the client-side, we don't care about the different arc types, just
381 normalise them all to the same type
382 */
383
384 case VG_SCCWARC_TO: return VG_SCCWARC_TO;
385 case VG_SCWARC_TO: return VG_SCCWARC_TO;
386 case VG_LCCWARC_TO: return VG_SCCWARC_TO;
387 case VG_LCWARC_TO: return VG_SCCWARC_TO;
388 default: UNREACHABLE(); return 0;
389 }
390}
391
392static bool interpolate_segments_compatible(const VGubyte *begin_segments, const VGubyte *end_segments, VGuint segments_count)
393{
394 for (; segments_count != 0; ++begin_segments, ++end_segments, --segments_count) {
395 if (normalise_segment(*begin_segments & ~VG_RELATIVE) != normalise_segment(*end_segments & ~VG_RELATIVE)) {
396 return false;
397 }
398 }
399 return true;
400}
401
402#ifndef RPC_DIRECT
403
404static VGuint get_log2_image_format_bpp(VGImageFormat image_format)
405{
406 switch (image_format) {
407 case VG_sRGBX_8888:
408 case VG_sRGBA_8888:
409 case VG_sRGBA_8888_PRE:
410 case VG_lRGBX_8888:
411 case VG_lRGBA_8888:
412 case VG_lRGBA_8888_PRE:
413 case VG_sXRGB_8888:
414 case VG_sARGB_8888:
415 case VG_sARGB_8888_PRE:
416 case VG_lXRGB_8888:
417 case VG_lARGB_8888:
418 case VG_lARGB_8888_PRE:
419 case VG_sBGRX_8888:
420 case VG_sBGRA_8888:
421 case VG_sBGRA_8888_PRE:
422 case VG_lBGRX_8888:
423 case VG_lBGRA_8888:
424 case VG_lBGRA_8888_PRE:
425 case VG_sXBGR_8888:
426 case VG_sABGR_8888:
427 case VG_sABGR_8888_PRE:
428 case VG_lXBGR_8888:
429 case VG_lABGR_8888:
430 case VG_lABGR_8888_PRE:
431 return 5;
432 case VG_sRGB_565:
433 case VG_sRGBA_5551:
434 case VG_sRGBA_4444:
435 case VG_sARGB_1555:
436 case VG_sARGB_4444:
437 case VG_sBGR_565:
438 case VG_sBGRA_5551:
439 case VG_sBGRA_4444:
440 case VG_sABGR_1555:
441 case VG_sABGR_4444:
442 return 4;
443 case VG_sL_8:
444 case VG_lL_8:
445 case VG_A_8:
446 return 3;
447 case VG_A_4:
448 return 2;
449 case VG_BW_1:
450 case VG_A_1:
451 return 0;
452 default:
453 UNREACHABLE();
454 return 0;
455 }
456}
457
458#endif
459
460static VGint param_to_int(
461 bool are_floats,
462 const void *values,
463 VGuint i)
464{
465 vcos_assert(values);
466 return are_floats ?
467 float_to_int_floor(clean_float(((const VGfloat *)values)[i])) :
468 ((const VGint *)values)[i];
469}
470
471static bool params_to_ints(
472 VGint *ints,
473 bool are_floats,
474 const void *values,
475 VGuint count)
476{
477 bool changed = false;
478 VGuint i;
479 for (i = 0; i != count; ++i) {
480 VGint x = param_to_int(are_floats, values, i);
481 if (ints[i] != x) {
482 changed = true;
483 ints[i] = x;
484 }
485 }
486 return changed;
487}
488
489static VGfloat param_to_float(
490 bool are_floats,
491 const void *values,
492 VGuint i)
493{
494 vcos_assert(values);
495 return are_floats ?
496 ((const VGfloat *)values)[i] :
497 (VGfloat)((const VGint *)values)[i];
498}
499
500static bool params_to_floats(
501 VGfloat *floats,
502 bool are_floats,
503 const void *values,
504 VGuint count)
505{
506 bool changed = false;
507 VGuint i;
508 for (i = 0; i != count; ++i) {
509 VGfloat x = param_to_float(are_floats, values, i);
510 if (!floats_identical(floats[i], x)) {
511 changed = true;
512 floats[i] = x;
513 }
514 }
515 return changed;
516}
517
518static void int_to_param(
519 bool are_floats,
520 void *values,
521 VGuint i,
522 VGint value)
523{
524 if (are_floats) {
525 ((VGfloat *)values)[i] = (VGfloat)value;
526 } else {
527 ((VGint *)values)[i] = value;
528 }
529}
530
531static void ints_to_params(
532 bool are_floats,
533 void *values,
534 VGuint count,
535 const VGint *ints)
536{
537 VGuint i;
538 for (i = 0; i != count; ++i) {
539 int_to_param(are_floats, values, i, ints[i]);
540 }
541}
542
543static void float_to_param(
544 bool are_floats,
545 void *values,
546 VGuint i,
547 VGfloat value)
548{
549 if (are_floats) {
550 ((VGfloat *)values)[i] = value;
551 } else {
552 ((VGint *)values)[i] = float_to_int_floor(clean_float(value));
553 }
554}
555
556static void floats_to_params(
557 bool are_floats,
558 void *values,
559 VGuint count,
560 const VGfloat *floats)
561{
562 VGuint i;
563 for (i = 0; i != count; ++i) {
564 float_to_param(are_floats, values, i, floats[i]);
565 }
566}
567
568static VGHandle get_stem(VG_CLIENT_STATE_T *state)
569{
570 VGHandle vg_handle;
571
572 platform_mutex_acquire(&state->shared_state->mutex);
573
574 if (state->shared_state->stems_count == 0) {
575 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
576 RPC_HIGH_PRIORITY_BEGIN(thread);
577 state->shared_state->stems_count = RPC_UINT_RES(RPC_CALL2_OUT_CTRL_RES(vgCreateStems_impl,
578 thread,
579 VGCREATESTEMS_ID,
580 VG_CLIENT_STEMS_COUNT_MAX,
581 state->shared_state->stems));
582 RPC_HIGH_PRIORITY_END(thread);
583 }
584 vg_handle = (state->shared_state->stems_count == 0) ? VG_INVALID_HANDLE : state->shared_state->stems[--state->shared_state->stems_count];
585
586 platform_mutex_release(&state->shared_state->mutex);
587
588 return vg_handle;
589}
590
591static void destroy_stem(VGHandle vg_handle)
592{
593 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
594 RPC_CALL1(vgDestroyStem_impl,
595 thread,
596 VGDESTROYSTEM_ID,
597 RPC_HANDLE(vg_handle));
598}
599
600static VG_CLIENT_FONT_T *font_alloc(void)
601{
602 VG_CLIENT_FONT_T *font = (VG_CLIENT_FONT_T *)khrn_platform_malloc(sizeof(VG_CLIENT_FONT_T), "VG_CLIENT_FONT_T");
603 if (!font) {
604 return NULL;
605 }
606
607 font->object_type = VG_CLIENT_OBJECT_TYPE_FONT;
608#if EGL_BRCM_global_image && EGL_KHR_image
609 if (!khrn_global_image_map_init(&font->glyph_global_images, 8)) {
610 khrn_platform_free(font);
611 return NULL;
612 }
613#endif
614
615 return font;
616}
617
618static void font_free(VG_CLIENT_FONT_T *font)
619{
620#if EGL_BRCM_global_image && EGL_KHR_image
621 khrn_global_image_map_term(&font->glyph_global_images);
622#endif
623 khrn_platform_free(font);
624}
625
626static VG_CLIENT_IMAGE_T *image_alloc(VGImageFormat format, VGint width, VGint height
627#if EGL_BRCM_global_image && EGL_KHR_image
628 , VGuint global_image_id_0, VGuint global_image_id_1
629#endif
630 )
631{
632 VG_CLIENT_IMAGE_T *image = (VG_CLIENT_IMAGE_T *)khrn_platform_malloc(sizeof(VG_CLIENT_IMAGE_T), "VG_CLIENT_IMAGE_T");
633 if (!image) {
634 return NULL;
635 }
636
637 image->object_type = VG_CLIENT_OBJECT_TYPE_IMAGE;
638 image->format = format;
639 image->width = width;
640 image->height = height;
641#if EGL_BRCM_global_image && EGL_KHR_image
642 if (global_image_id_0 || global_image_id_1) {
643 platform_acquire_global_image(global_image_id_0, global_image_id_1);
644 }
645 image->global_image_id[0] = global_image_id_0;
646 image->global_image_id[1] = global_image_id_1;
647#endif
648
649 return image;
650}
651
652static void image_free(VG_CLIENT_IMAGE_T *image)
653{
654#if EGL_BRCM_global_image && EGL_KHR_image
655 if (image->global_image_id[0] || image->global_image_id[1]) {
656 platform_release_global_image(image->global_image_id[0], image->global_image_id[1]);
657 }
658#endif
659 khrn_platform_free(image);
660}
661
662static VG_CLIENT_MASK_LAYER_T *mask_layer_alloc(VGint width, VGint height)
663{
664 VG_CLIENT_MASK_LAYER_T *mask_layer = (VG_CLIENT_MASK_LAYER_T *)khrn_platform_malloc(sizeof(VG_CLIENT_MASK_LAYER_T), "VG_CLIENT_MASK_LAYER_T");
665 if (!mask_layer) {
666 return NULL;
667 }
668
669 mask_layer->object_type = VG_CLIENT_OBJECT_TYPE_MASK_LAYER;
670 mask_layer->width = width;
671 mask_layer->height = height;
672
673 return mask_layer;
674}
675
676static void mask_layer_free(VG_CLIENT_MASK_LAYER_T *mask_layer)
677{
678 khrn_platform_free(mask_layer);
679}
680
681static bool need_paint_gradient(VGint param_type)
682{
683 return (param_type == VG_PAINT_COLOR_RAMP_SPREAD_MODE) ||
684 (param_type == VG_PAINT_COLOR_RAMP_PREMULTIPLIED) ||
685 (param_type == VG_PAINT_COLOR_RAMP_STOPS) ||
686 (param_type == VG_PAINT_LINEAR_GRADIENT) ||
687 (param_type == VG_PAINT_RADIAL_GRADIENT);
688}
689
690static VG_CLIENT_PAINT_T *paint_alloc(void)
691{
692 VG_CLIENT_PAINT_T *paint = (VG_CLIENT_PAINT_T *)khrn_platform_malloc(sizeof(VG_CLIENT_PAINT_T), "VG_CLIENT_PAINT_T");
693 if (!paint) {
694 return NULL;
695 }
696
697 paint->object_type = VG_CLIENT_OBJECT_TYPE_PAINT;
698 paint->type = VG_PAINT_TYPE_COLOR;
699 paint->color[0] = 0.0f;
700 paint->color[1] = 0.0f;
701 paint->color[2] = 0.0f;
702 paint->color[3] = 1.0f;
703 paint->gradient = NULL; /* will be allocated when required */
704 paint->pattern_tiling_mode = VG_TILE_FILL;
705 paint->pattern = VG_INVALID_HANDLE;
706#if EGL_BRCM_global_image && EGL_KHR_image
707 paint->pattern_global_image_id[0] = 0;
708 paint->pattern_global_image_id[1] = 0;
709#endif
710
711 return paint;
712}
713
714static bool paint_alloc_gradient(VG_CLIENT_PAINT_T *paint)
715{
716 VG_CLIENT_PAINT_GRADIENT_T *gradient;
717
718 vcos_assert(!paint->gradient);
719
720 gradient = (VG_CLIENT_PAINT_GRADIENT_T *)khrn_platform_malloc(sizeof(VG_CLIENT_PAINT_GRADIENT_T), "VG_CLIENT_PAINT_GRADIENT_T");
721 if (!gradient) {
722 return false;
723 }
724
725 gradient->linear[0] = 0.0f;
726 gradient->linear[1] = 0.0f;
727 gradient->linear[2] = 1.0f;
728 gradient->linear[3] = 0.0f;
729 gradient->radial[0] = 0.0f;
730 gradient->radial[1] = 0.0f;
731 gradient->radial[2] = 0.0f;
732 gradient->radial[3] = 0.0f;
733 gradient->radial[4] = 1.0f;
734 gradient->ramp_spread_mode = VG_COLOR_RAMP_SPREAD_PAD;
735 gradient->ramp_pre = true;
736 gradient->ramp_stops = NULL;
737 gradient->ramp_stops_count = 0;
738
739 paint->gradient = gradient;
740
741 return true;
742}
743
744static void paint_free(VG_CLIENT_PAINT_T *paint)
745{
746#if EGL_BRCM_global_image && EGL_KHR_image
747 if (paint->pattern_global_image_id[0] || paint->pattern_global_image_id[1]) {
748 platform_release_global_image(paint->pattern_global_image_id[0], paint->pattern_global_image_id[1]);
749 }
750#endif
751 if (paint->gradient) {
752 if (paint->gradient->ramp_stops) {
753 khrn_platform_free(paint->gradient->ramp_stops);
754 }
755 khrn_platform_free(paint->gradient);
756 }
757 khrn_platform_free(paint);
758}
759
760static bool need_path_segments(VGbitfield caps)
761{
762 /*
763 need segments for vgModifyPath and interpolating from, but also need for
764 appending from incase the path being appended to needs segments (we don't
765 want to have to go to the server to get them)
766 */
767
768 return !!(caps & (VG_PATH_CAPABILITY_APPEND_FROM |
769 VG_PATH_CAPABILITY_MODIFY |
770 VG_PATH_CAPABILITY_TRANSFORM_FROM |
771 VG_PATH_CAPABILITY_INTERPOLATE_FROM));
772}
773
774static VG_CLIENT_PATH_T *path_alloc(VGint format, VGPathDatatype datatype, VGfloat scale, VGfloat bias, VGbitfield caps, VGint segments_capacity)
775{
776 VG_CLIENT_PATH_T *path = (VG_CLIENT_PATH_T *)khrn_platform_malloc(sizeof(VG_CLIENT_PATH_T), "VG_CLIENT_PATH_T");
777 if (!path) {
778 return NULL;
779 }
780
781 path->object_type = VG_CLIENT_OBJECT_TYPE_PATH;
782 path->format = format;
783 path->datatype = datatype;
784 path->scale = scale;
785 path->bias = bias;
786 path->caps = caps;
787 if (need_path_segments(caps)) {
788 khrn_vector_init(&path->segments, clampi(segments_capacity, 0, 1024));
789 }
790
791 return path;
792}
793
794static void path_update_caps(VG_CLIENT_PATH_T *path, VGbitfield caps)
795{
796 if (!need_path_segments(path->caps) && need_path_segments(caps)) {
797 khrn_vector_init(&path->segments, 0);
798 }
799 if (need_path_segments(path->caps) && !need_path_segments(caps)) {
800 khrn_vector_term(&path->segments);
801 }
802 path->caps = caps;
803}
804
805static void path_free(VG_CLIENT_PATH_T *path)
806{
807 if (need_path_segments(path->caps)) {
808 khrn_vector_term(&path->segments);
809 }
810 khrn_platform_free(path);
811}
812
813static void object_free(void *object)
814{
815 switch (*(VG_CLIENT_OBJECT_TYPE_T *)object) {
816 case VG_CLIENT_OBJECT_TYPE_FONT: font_free((VG_CLIENT_FONT_T *)object); break;
817 case VG_CLIENT_OBJECT_TYPE_IMAGE: image_free((VG_CLIENT_IMAGE_T *)object); break;
818 case VG_CLIENT_OBJECT_TYPE_MASK_LAYER: mask_layer_free((VG_CLIENT_MASK_LAYER_T *)object); break;
819 case VG_CLIENT_OBJECT_TYPE_PAINT: paint_free((VG_CLIENT_PAINT_T *)object); break;
820 case VG_CLIENT_OBJECT_TYPE_PATH: path_free((VG_CLIENT_PATH_T *)object); break;
821 default: UNREACHABLE();
822 }
823}
824
825/*
826 see nice_handle in middleware/khronos/vg/vg_set.c
827*/
828
829static INLINE uint32_t nice_handle(VGHandle vg_handle)
830{
831 return _ror((uint32_t)vg_handle, 31);
832}
833
834static bool insert_object(VG_CLIENT_STATE_T *state, VGHandle vg_handle, void *object)
835{
836 void *prev_object = khrn_pointer_map_lookup(&state->shared_state->objects, nice_handle(vg_handle));
837 if (prev_object) {
838 object_free(prev_object);
839 }
840 return khrn_pointer_map_insert(&state->shared_state->objects, nice_handle(vg_handle), object);
841}
842
843static void delete_object(VG_CLIENT_STATE_T *state, VGHandle vg_handle, VG_CLIENT_OBJECT_TYPE_T type)
844{
845 void *object = khrn_pointer_map_lookup(&state->shared_state->objects, nice_handle(vg_handle));
846 if (object && (*(VG_CLIENT_OBJECT_TYPE_T *)object == type)) {
847 object_free(object);
848 khrn_pointer_map_delete(&state->shared_state->objects, nice_handle(vg_handle));
849 }
850}
851
852static INLINE void *lookup_object(VG_CLIENT_STATE_T *state, VGHandle vg_handle, VG_CLIENT_OBJECT_TYPE_T type)
853{
854 void *object = khrn_pointer_map_lookup(&state->shared_state->objects, nice_handle(vg_handle));
855 return (object && (*(VG_CLIENT_OBJECT_TYPE_T *)object == type)) ? object : NULL;
856}
857
858static INLINE VG_MAT3X3_SYNC_T *get_matrix_sync(VG_CLIENT_STATE_T *state, VGMatrixMode matrix_mode)
859{
860 vcos_assert(
861 (matrix_mode >= VG_MATRIX_PATH_USER_TO_SURFACE) &&
862 (matrix_mode < (VG_MATRIX_PATH_USER_TO_SURFACE + ARR_COUNT(state->matrices))));
863 return state->matrices + (matrix_mode - VG_MATRIX_PATH_USER_TO_SURFACE);
864}
865
866static INLINE bool is_matrix_affine(VGMatrixMode matrix_mode)
867{
868 return matrix_mode != VG_MATRIX_IMAGE_USER_TO_SURFACE;
869}
870
871static void sync_matrix(VG_CLIENT_STATE_T *state, VGMatrixMode matrix_mode)
872{
873 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
874 VG_MAT3X3_SYNC_T *matrix_sync = get_matrix_sync(state, matrix_mode);
875 if (!vg_mat3x3_identical(&matrix_sync->server, &matrix_sync->client)) {
876 RPC_CALL2_IN_CTRL(vgLoadMatrix_impl,
877 thread,
878 VGLOADMATRIX_ID,
879 RPC_ENUM(matrix_mode),
880 &matrix_sync->client,
881 sizeof(VG_MAT3X3_T));
882 matrix_sync->server = matrix_sync->client;
883 }
884}
885
886/******************************************************************************
887api misc
888******************************************************************************/
889
890VG_API_CALL VGErrorCode VG_API_ENTRY vgGetError(void) VG_API_EXIT
891{
892 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
893 if (!VG_GET_CLIENT_STATE(thread)) {
894 return VG_NO_CONTEXT_ERROR;
895 }
896
897 return get_error();
898}
899
900VG_API_CALL void VG_API_ENTRY vgFlush(void) VG_API_EXIT
901{
902 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
903 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
904 if (!state) {
905 return;
906 }
907
908 RPC_CALL0(vgFlush_impl,
909 thread,
910 VGFLUSH_ID);
911 RPC_FLUSH(thread);
912
913#ifdef KHRN_COMMAND_MODE_DISPLAY
914 {
915 //Check for single buffered windows surface in which case call surface updater
916 EGL_SURFACE_T *surface = CLIENT_GET_THREAD_STATE()->openvg.draw;
917 if (surface->type == WINDOW && surface->buffers==1) {
918 platform_surface_update(surface->internal_handle);
919 }
920 }
921#endif
922
923 if (state->flush_callback)
924 state->flush_callback(false);
925}
926
927VG_API_CALL void VG_API_ENTRY vgFinish(void) VG_API_EXIT
928{
929 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
930 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
931 if (!state) {
932 return;
933 }
934
935 (void)RPC_UINT_RES(RPC_CALL0_RES(vgFinish_impl,
936 thread,
937 VGFINISH_ID)); /* return value ignored -- read performed to ensure blocking */
938
939#ifdef KHRN_COMMAND_MODE_DISPLAY
940 {
941 //Check for single buffered windows surface in which case call surface updater
942 EGL_SURFACE_T *surface = CLIENT_GET_THREAD_STATE()->openvg.draw;
943 if (surface->type == WINDOW && surface->buffers==1) {
944 platform_surface_update(surface->internal_handle);
945 }
946 }
947#endif
948
949 if (state->flush_callback)
950 state->flush_callback(true);
951}
952
953/******************************************************************************
954api get/set
955******************************************************************************/
956
957static void set_iv_server(
958 VGParamType param_type,
959 VGint count,
960 const VGint *values)
961{
962 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
963 RPC_CALL3_IN_CTRL(vgSetiv_impl,
964 thread,
965 VGSETIV_ID,
966 RPC_ENUM(param_type),
967 RPC_INT(count),
968 values,
969 count * sizeof(VGint));
970}
971
972static void set_fv_server(
973 VGParamType param_type,
974 VGint count,
975 const VGfloat *values)
976{
977 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
978 RPC_CALL3_IN_CTRL(vgSetfv_impl,
979 thread,
980 VGSETFV_ID,
981 RPC_ENUM(param_type),
982 RPC_INT(count),
983 values,
984 count * sizeof(VGfloat));
985}
986
987static void get_fv_server(
988 VGParamType param_type,
989 VGint count,
990 VGfloat *values)
991{
992 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
993 RPC_CALL3_OUT_CTRL(vgGetfv_impl,
994 thread,
995 VGGETFV_ID,
996 RPC_ENUM(param_type),
997 RPC_INT(count),
998 values);
999}
1000
1001static void set_parameter_iv_server(
1002 VGHandle vg_handle,
1003 VG_CLIENT_OBJECT_TYPE_T object_type,
1004 VGint param_type,
1005 VGint count,
1006 const VGint *values)
1007{
1008 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
1009 RPC_CALL5_IN_CTRL(vgSetParameteriv_impl,
1010 thread,
1011 VGSETPARAMETERIV_ID,
1012 RPC_HANDLE(vg_handle),
1013 RPC_ENUM(object_type),
1014 RPC_INT(param_type),
1015 RPC_INT(count),
1016 values,
1017 count * sizeof(VGint));
1018}
1019
1020static void set_parameter_fv_server(
1021 VGHandle vg_handle,
1022 VG_CLIENT_OBJECT_TYPE_T object_type,
1023 VGint param_type,
1024 VGint count,
1025 const VGfloat *values)
1026{
1027 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
1028 RPC_CALL5_IN_CTRL(vgSetParameterfv_impl,
1029 thread,
1030 VGSETPARAMETERFV_ID,
1031 RPC_HANDLE(vg_handle),
1032 RPC_ENUM(object_type),
1033 RPC_INT(param_type),
1034 RPC_INT(count),
1035 values,
1036 count * sizeof(VGfloat));
1037}
1038
1039static bool get_parameter_iv_server(
1040 VGHandle vg_handle,
1041 VG_CLIENT_OBJECT_TYPE_T object_type,
1042 VGint param_type,
1043 VGint count,
1044 VGint *values)
1045{
1046 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
1047 return RPC_BOOLEAN_RES(RPC_CALL5_OUT_CTRL_RES(vgGetParameteriv_impl,
1048 thread,
1049 VGGETPARAMETERIV_ID,
1050 RPC_HANDLE(vg_handle),
1051 RPC_ENUM(object_type),
1052 RPC_INT(param_type),
1053 RPC_INT(count),
1054 values));
1055}
1056
1057static void set_ifv(
1058 CLIENT_THREAD_STATE_T *thread,
1059 VG_CLIENT_STATE_T *state,
1060 VGParamType param_type,
1061 VGint count,
1062 bool are_floats,
1063 const void *values)
1064{
1065 /*
1066 * If 'count' is greater than zero, then our caller must have verified
1067 * that 'values' is non-NULL.
1068 * If 'count' is zero, then 'values' *must not* be dereferenced.
1069 * Note that params_to_floats() and params_to_ints() handle a zero 'count'
1070 * argument correctly and do not dereference 'values' in this case.
1071 * Other functions, e.g. param_to_int() and param_to_float(), require a
1072 * check before calling.
1073 */
1074 VGint value_i = (count == 1) ? param_to_int(are_floats, values, 0) : 0;
1075 VGfloat value_f = (count == 1) ? param_to_float(are_floats, values, 0) : 0.0f;
1076
1077 switch (param_type) {
1078 /*
1079 settable scalar param types
1080 */
1081
1082 #define CASE(PARAM_TYPE, OK, DO) \
1083 case PARAM_TYPE: \
1084 { \
1085 if ((count != 1) || !(OK)) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; } \
1086 DO; \
1087 break; \
1088 }
1089 #define CASE_I_SERVER(PARAM_TYPE, OK, VALUE, FN) CASE(PARAM_TYPE, OK, if (VALUE != FN(value_i)) { VALUE = FN(value_i); set_iv_server(PARAM_TYPE, 1, &value_i); })
1090 #define CASE_F_SERVER(PARAM_TYPE, OK, VALUE) CASE(PARAM_TYPE, OK, if (!floats_identical(VALUE, value_f)) { VALUE = value_f; set_fv_server(PARAM_TYPE, 1, &value_f); })
1091 CASE(VG_MATRIX_MODE, is_matrix_mode((VGMatrixMode)value_i), state->matrix_mode = (VGMatrixMode)value_i)
1092 CASE_I_SERVER(VG_FILL_RULE, is_fill_rule((VGFillRule)value_i), state->fill_rule, (VGFillRule))
1093 CASE_I_SERVER(VG_IMAGE_QUALITY, is_image_quality((VGImageQuality)value_i), state->image_quality, (VGImageQuality))
1094 CASE_I_SERVER(VG_RENDERING_QUALITY, is_rendering_quality((VGRenderingQuality)value_i), state->rendering_quality, (VGRenderingQuality))
1095 CASE_I_SERVER(VG_BLEND_MODE, is_blend_mode((VGBlendMode)value_i), state->blend_mode, (VGBlendMode))
1096 CASE_I_SERVER(VG_IMAGE_MODE, is_image_mode((VGImageMode)value_i), state->image_mode, (VGImageMode))
1097 CASE_I_SERVER(VG_COLOR_TRANSFORM, true, state->color_transform, clean_boolean)
1098 CASE_F_SERVER(VG_STROKE_LINE_WIDTH, true, state->stroke_line_width)
1099 CASE_I_SERVER(VG_STROKE_CAP_STYLE, is_cap_style((VGCapStyle)value_i), state->stroke_cap_style, (VGCapStyle))
1100 CASE_I_SERVER(VG_STROKE_JOIN_STYLE, is_join_style((VGJoinStyle)value_i), state->stroke_join_style, (VGJoinStyle))
1101 CASE_F_SERVER(VG_STROKE_MITER_LIMIT, true, state->stroke_miter_limit)
1102 CASE_F_SERVER(VG_STROKE_DASH_PHASE, true, state->stroke_dash_phase)
1103 CASE_I_SERVER(VG_STROKE_DASH_PHASE_RESET, true, state->stroke_dash_phase_reset, clean_boolean)
1104 CASE_I_SERVER(VG_MASKING, true, state->masking, clean_boolean)
1105 CASE_I_SERVER(VG_SCISSORING, true, state->scissoring, clean_boolean)
1106 CASE(VG_PIXEL_LAYOUT, is_pixel_layout((VGPixelLayout)value_i), state->pixel_layout = (VGPixelLayout)value_i)
1107 CASE_I_SERVER(VG_FILTER_FORMAT_LINEAR, true, state->filter_format_linear, clean_boolean)
1108 CASE_I_SERVER(VG_FILTER_FORMAT_PREMULTIPLIED, true, state->filter_format_pre, clean_boolean)
1109 CASE_I_SERVER(VG_FILTER_CHANNEL_MASK, true, state->filter_channel_mask, )
1110 #undef CASE_F_SERVER
1111 #undef CASE_I_SERVER
1112 #undef CASE
1113
1114 /*
1115 read-only scalar param types
1116 */
1117
1118 case VG_SCREEN_LAYOUT:
1119 case VG_MAX_SCISSOR_RECTS:
1120 case VG_MAX_DASH_COUNT:
1121 case VG_MAX_KERNEL_SIZE:
1122 case VG_MAX_SEPARABLE_KERNEL_SIZE:
1123 case VG_MAX_COLOR_RAMP_STOPS:
1124 case VG_MAX_IMAGE_WIDTH:
1125 case VG_MAX_IMAGE_HEIGHT:
1126 case VG_MAX_IMAGE_PIXELS:
1127 case VG_MAX_IMAGE_BYTES:
1128 case VG_MAX_FLOAT:
1129 case VG_MAX_GAUSSIAN_STD_DEVIATION:
1130 {
1131 if (count != 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); }
1132 break;
1133 }
1134
1135 /*
1136 vector param types
1137 */
1138
1139 case VG_SCISSOR_RECTS:
1140 {
1141 if (count & 0x3) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1142 count = _min(count, VG_CONFIG_MAX_SCISSOR_RECTS * 4);
1143 if (params_to_ints(
1144 state->scissor_rects,
1145 are_floats, values, count) ||
1146 (state->scissor_rects_count != count)) {
1147 state->scissor_rects_count = count;
1148 set_iv_server(VG_SCISSOR_RECTS, count, state->scissor_rects);
1149 }
1150 break;
1151 }
1152 case VG_COLOR_TRANSFORM_VALUES:
1153 {
1154 if (count != 8) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1155 if (params_to_floats(
1156 state->color_transform_values,
1157 are_floats, values, count)) {
1158 set_fv_server(VG_COLOR_TRANSFORM_VALUES, 8, state->color_transform_values);
1159 }
1160 break;
1161 }
1162 case VG_STROKE_DASH_PATTERN:
1163 {
1164 count = _min(count, VG_CONFIG_MAX_DASH_COUNT);
1165 if (params_to_floats(
1166 state->stroke_dash_pattern,
1167 are_floats, values, count) ||
1168 (state->stroke_dash_pattern_count != count)) {
1169 state->stroke_dash_pattern_count = count;
1170 set_fv_server(VG_STROKE_DASH_PATTERN, count, state->stroke_dash_pattern);
1171 }
1172 break;
1173 }
1174 case VG_TILE_FILL_COLOR:
1175 case VG_CLEAR_COLOR:
1176 {
1177 VGfloat *color;
1178 if (count != 4) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1179 color = (param_type == VG_TILE_FILL_COLOR) ? state->tile_fill_color : state->clear_color;
1180 if (params_to_floats(
1181 color,
1182 are_floats, values, count)) {
1183 VGint rgba = color_floats_to_rgba_clean(color);
1184 set_iv_server(param_type, 1, &rgba);
1185 }
1186 break;
1187 }
1188 case VG_GLYPH_ORIGIN:
1189 {
1190 /*
1191 don't store on client as it can change
1192 */
1193
1194 VGfloat glyph_origin[2];
1195 if (count != 2) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1196 (void)params_to_floats(
1197 glyph_origin,
1198 are_floats, values, count); /* ignore return -- it doesn't mean anything in this case */
1199 set_fv_server(VG_GLYPH_ORIGIN, 2, glyph_origin);
1200 break;
1201 }
1202
1203 /*
1204 invalid param type
1205 */
1206
1207 default:
1208 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1209 }
1210}
1211
1212static void get_ifv(
1213 CLIENT_THREAD_STATE_T *thread,
1214 VG_CLIENT_STATE_T *state,
1215 VGParamType param_type,
1216 VGint count,
1217 bool are_floats,
1218 void *values)
1219{
1220 switch (param_type) {
1221 /*
1222 scalar param types
1223 */
1224
1225 #define CASE(PARAM_TYPE, FN, VALUE) \
1226 case PARAM_TYPE: \
1227 { \
1228 if (count > 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; } \
1229 FN(are_floats, values, 0, VALUE); \
1230 break; \
1231 }
1232 #define CASE_I(PARAM_TYPE, VALUE) CASE(PARAM_TYPE, int_to_param, VALUE)
1233 #define CASE_F(PARAM_TYPE, VALUE) CASE(PARAM_TYPE, float_to_param, VALUE)
1234 CASE_I(VG_MATRIX_MODE, state->matrix_mode)
1235 CASE_I(VG_FILL_RULE, state->fill_rule)
1236 CASE_I(VG_IMAGE_QUALITY, state->image_quality)
1237 CASE_I(VG_RENDERING_QUALITY, state->rendering_quality)
1238 CASE_I(VG_BLEND_MODE, state->blend_mode)
1239 CASE_I(VG_IMAGE_MODE, state->image_mode)
1240 CASE_I(VG_COLOR_TRANSFORM, state->color_transform)
1241 CASE_F(VG_STROKE_LINE_WIDTH, state->stroke_line_width)
1242 CASE_I(VG_STROKE_CAP_STYLE, state->stroke_cap_style)
1243 CASE_I(VG_STROKE_JOIN_STYLE, state->stroke_join_style)
1244 CASE_F(VG_STROKE_MITER_LIMIT, state->stroke_miter_limit)
1245 CASE_F(VG_STROKE_DASH_PHASE, state->stroke_dash_phase)
1246 CASE_I(VG_STROKE_DASH_PHASE_RESET, state->stroke_dash_phase_reset)
1247 CASE_I(VG_MASKING, state->masking)
1248 CASE_I(VG_SCISSORING, state->scissoring)
1249 CASE_I(VG_PIXEL_LAYOUT, state->pixel_layout)
1250 CASE_I(VG_SCREEN_LAYOUT, VG_CONFIG_SCREEN_LAYOUT)
1251 CASE_I(VG_FILTER_FORMAT_LINEAR, state->filter_format_linear)
1252 CASE_I(VG_FILTER_FORMAT_PREMULTIPLIED, state->filter_format_pre)
1253 CASE_I(VG_FILTER_CHANNEL_MASK, state->filter_channel_mask)
1254 CASE_I(VG_MAX_SCISSOR_RECTS, VG_CONFIG_MAX_SCISSOR_RECTS)
1255 CASE_I(VG_MAX_DASH_COUNT, VG_CONFIG_MAX_DASH_COUNT)
1256 CASE_I(VG_MAX_KERNEL_SIZE, VG_CONFIG_MAX_KERNEL_SIZE)
1257 CASE_I(VG_MAX_SEPARABLE_KERNEL_SIZE, VG_CONFIG_MAX_SEPARABLE_KERNEL_SIZE)
1258 CASE_I(VG_MAX_COLOR_RAMP_STOPS, VG_CONFIG_MAX_COLOR_RAMP_STOPS)
1259 CASE_I(VG_MAX_IMAGE_WIDTH, VG_CONFIG_MAX_IMAGE_WIDTH)
1260 CASE_I(VG_MAX_IMAGE_HEIGHT, VG_CONFIG_MAX_IMAGE_HEIGHT)
1261 CASE_I(VG_MAX_IMAGE_PIXELS, VG_CONFIG_MAX_IMAGE_PIXELS)
1262 CASE_I(VG_MAX_IMAGE_BYTES, VG_CONFIG_MAX_IMAGE_BYTES)
1263 CASE_F(VG_MAX_FLOAT, VG_CONFIG_MAX_FLOAT)
1264 CASE_F(VG_MAX_GAUSSIAN_STD_DEVIATION, VG_CONFIG_MAX_GAUSSIAN_STD_DEVIATION)
1265 #undef CASE_F
1266 #undef CASE_I
1267 #undef CASE
1268
1269 /*
1270 vector param types
1271 */
1272
1273 case VG_SCISSOR_RECTS:
1274 {
1275 if ((VGuint)count > state->scissor_rects_count) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1276 ints_to_params(
1277 are_floats, values, count,
1278 state->scissor_rects);
1279 break;
1280 }
1281 case VG_COLOR_TRANSFORM_VALUES:
1282 {
1283 if (count > 8) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1284 floats_to_params(
1285 are_floats, values, count,
1286 state->color_transform_values);
1287 break;
1288 }
1289 case VG_STROKE_DASH_PATTERN:
1290 {
1291 if ((VGuint)count > state->stroke_dash_pattern_count) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1292 floats_to_params(
1293 are_floats, values, count,
1294 state->stroke_dash_pattern);
1295 break;
1296 }
1297 case VG_TILE_FILL_COLOR:
1298 case VG_CLEAR_COLOR:
1299 {
1300 if (count > 4) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1301 floats_to_params(
1302 are_floats, values, count,
1303 (param_type == VG_TILE_FILL_COLOR) ? state->tile_fill_color : state->clear_color);
1304 break;
1305 }
1306 case VG_GLYPH_ORIGIN:
1307 {
1308 VGfloat glyph_origin[2];
1309 if (count > 2) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1310 get_fv_server(VG_GLYPH_ORIGIN, 2, glyph_origin);
1311 floats_to_params(
1312 are_floats, values, count,
1313 glyph_origin);
1314 break;
1315 }
1316
1317 /*
1318 invalid param type
1319 */
1320
1321 default:
1322 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1323 }
1324}
1325
1326static void set_parameter_ifv(
1327 CLIENT_THREAD_STATE_T *thread,
1328 VG_CLIENT_STATE_T *state,
1329 VGHandle vg_handle,
1330 VGint param_type,
1331 VGint count,
1332 bool are_floats,
1333 const void *values)
1334{
1335 /*
1336 * If 'count' is greater than zero, then our caller must have verified
1337 * that 'values' is non-NULL.
1338 * If 'count' is zero, then 'values' *must not* be dereferenced.
1339 * Note that params_to_floats() and params_to_ints() handle a zero 'count'
1340 * argument correctly and do not dereference 'values' in this case.
1341 * Other functions, e.g. param_to_int() and param_to_float(), require a
1342 * check before calling.
1343 */
1344 void *object;
1345
1346 platform_mutex_acquire(&state->shared_state->mutex);
1347
1348 object = khrn_pointer_map_lookup(&state->shared_state->objects, nice_handle(vg_handle));
1349 if (!object) {
1350 set_error(VG_BAD_HANDLE_ERROR);
1351 platform_mutex_release(&state->shared_state->mutex);
1352 return;
1353 }
1354
1355 switch (*(VG_CLIENT_OBJECT_TYPE_T *)object) {
1356 case VG_CLIENT_OBJECT_TYPE_PATH:
1357 {
1358 switch (param_type) {
1359 /*
1360 read-only scalar param types
1361 */
1362
1363 case VG_PATH_FORMAT:
1364 case VG_PATH_DATATYPE:
1365 case VG_PATH_SCALE:
1366 case VG_PATH_BIAS:
1367 case VG_PATH_NUM_SEGMENTS:
1368 case VG_PATH_NUM_COORDS:
1369 {
1370 if (count != 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); }
1371 break;
1372 }
1373
1374 /*
1375 invalid param type
1376 */
1377
1378 default:
1379 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1380 }
1381
1382 break;
1383 }
1384 case VG_CLIENT_OBJECT_TYPE_PAINT:
1385 {
1386 VG_CLIENT_PAINT_T *paint = (VG_CLIENT_PAINT_T *)object;
1387 VGint value_i;
1388
1389 if (!paint->gradient &&
1390 need_paint_gradient(param_type) &&
1391 !paint_alloc_gradient(paint)) {
1392 set_error(VG_OUT_OF_MEMORY_ERROR);
1393 break;
1394 }
1395
1396 value_i = (count == 1) ? param_to_int(are_floats, values, 0) : 0;
1397
1398 switch (param_type) {
1399 /*
1400 settable scalar param types
1401 */
1402
1403 #define CASE_I_SERVER(PARAM_TYPE, OK, VALUE, FN) \
1404 case PARAM_TYPE: \
1405 { \
1406 if ((count != 1) || !(OK)) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; } \
1407 if (VALUE != FN(value_i)) { \
1408 VALUE = FN(value_i); \
1409 set_parameter_iv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT, PARAM_TYPE, 1, &value_i); \
1410 } \
1411 break; \
1412 }
1413 CASE_I_SERVER(VG_PAINT_TYPE, is_paint_type((VGPaintType)value_i), paint->type, (VGPaintType))
1414 CASE_I_SERVER(VG_PAINT_COLOR_RAMP_SPREAD_MODE, is_color_ramp_spread_mode((VGColorRampSpreadMode)value_i), paint->gradient->ramp_spread_mode, (VGColorRampSpreadMode))
1415 CASE_I_SERVER(VG_PAINT_COLOR_RAMP_PREMULTIPLIED, true, paint->gradient->ramp_pre, clean_boolean)
1416 CASE_I_SERVER(VG_PAINT_PATTERN_TILING_MODE, is_tiling_mode((VGTilingMode)value_i), paint->pattern_tiling_mode, (VGTilingMode))
1417 #undef CASE_I_SERVER
1418
1419 /*
1420 vector param types
1421 */
1422
1423 case VG_PAINT_COLOR:
1424 {
1425 if (count != 4) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1426 if (params_to_floats(
1427 paint->color,
1428 are_floats, values, count)) {
1429 VGint rgba = color_floats_to_rgba_clean(paint->color);
1430 set_parameter_iv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT, VG_PAINT_COLOR, 1, &rgba);
1431 }
1432 break;
1433 }
1434 case VG_PAINT_COLOR_RAMP_STOPS:
1435 {
1436 if ((count % 5) != 0) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1437 count = _min(count, VG_CONFIG_MAX_COLOR_RAMP_STOPS * 5);
1438 if (paint->gradient->ramp_stops_count != count) {
1439 VGfloat *ramp_stops = NULL;
1440 if (count != 0) {
1441 ramp_stops = (VGfloat *)khrn_platform_malloc(count * sizeof(VGfloat), "VG_CLIENT_PAINT_GRADIENT_T.ramp_stops");
1442 if (!ramp_stops) {
1443 set_error(VG_OUT_OF_MEMORY_ERROR);
1444 break;
1445 }
1446 }
1447 if (paint->gradient->ramp_stops) {
1448 khrn_platform_free(paint->gradient->ramp_stops);
1449 }
1450 paint->gradient->ramp_stops = ramp_stops;
1451 }
1452 if (params_to_floats(
1453 paint->gradient->ramp_stops,
1454 are_floats, values, count) ||
1455 (paint->gradient->ramp_stops_count != count)) {
1456 paint->gradient->ramp_stops_count = count;
1457 set_parameter_fv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT, VG_PAINT_COLOR_RAMP_STOPS, count, paint->gradient->ramp_stops);
1458 }
1459 break;
1460 }
1461 case VG_PAINT_LINEAR_GRADIENT:
1462 {
1463 if (count != 4) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1464 if (params_to_floats(
1465 paint->gradient->linear,
1466 are_floats, values, count)) {
1467 set_parameter_fv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT, VG_PAINT_LINEAR_GRADIENT, 4, paint->gradient->linear);
1468 }
1469 break;
1470 }
1471 case VG_PAINT_RADIAL_GRADIENT:
1472 {
1473 if (count != 5) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1474 if (params_to_floats(
1475 paint->gradient->radial,
1476 are_floats, values, count)) {
1477 set_parameter_fv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT, VG_PAINT_RADIAL_GRADIENT, 5, paint->gradient->radial);
1478 }
1479 break;
1480 }
1481
1482 /*
1483 invalid param type
1484 */
1485
1486 default:
1487 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1488 }
1489
1490 break;
1491 }
1492 case VG_CLIENT_OBJECT_TYPE_IMAGE:
1493 {
1494 switch (param_type) {
1495 /*
1496 read-only scalar param types
1497 */
1498
1499 case VG_IMAGE_FORMAT:
1500 case VG_IMAGE_WIDTH:
1501 case VG_IMAGE_HEIGHT:
1502 {
1503 if (count != 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); }
1504 break;
1505 }
1506
1507 /*
1508 invalid param type
1509 */
1510
1511 default:
1512 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1513 }
1514
1515 break;
1516 }
1517 case VG_CLIENT_OBJECT_TYPE_MASK_LAYER:
1518 {
1519 switch (param_type) {
1520 /*
1521 read-only scalar param types
1522 */
1523
1524 case VG_IMAGE_WIDTH:
1525 case VG_IMAGE_HEIGHT:
1526 {
1527 if (count != 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); }
1528 break;
1529 }
1530
1531 /*
1532 invalid param type
1533 */
1534
1535 default:
1536 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1537 }
1538
1539 break;
1540 }
1541 case VG_CLIENT_OBJECT_TYPE_FONT:
1542 {
1543 switch (param_type) {
1544 /*
1545 read-only scalar param types
1546 */
1547
1548 case VG_FONT_NUM_GLYPHS:
1549 {
1550 if (count != 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); }
1551 break;
1552 }
1553
1554 /*
1555 invalid param type
1556 */
1557
1558 default:
1559 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1560 }
1561
1562 break;
1563 }
1564 default:
1565 {
1566 UNREACHABLE();
1567 }
1568 }
1569
1570 platform_mutex_release(&state->shared_state->mutex);
1571}
1572
1573static void get_parameter_ifv(
1574 CLIENT_THREAD_STATE_T *thread,
1575 VG_CLIENT_STATE_T *state,
1576 VGHandle vg_handle,
1577 VGint param_type,
1578 VGint count,
1579 bool are_floats,
1580 void *values)
1581{
1582 void *object;
1583
1584 platform_mutex_acquire(&state->shared_state->mutex);
1585
1586 object = khrn_pointer_map_lookup(&state->shared_state->objects, nice_handle(vg_handle));
1587 if (!object) {
1588 set_error(VG_BAD_HANDLE_ERROR);
1589 platform_mutex_release(&state->shared_state->mutex);
1590 return;
1591 }
1592
1593 #define CASE(PARAM_TYPE, FN, VALUE) \
1594 case PARAM_TYPE: \
1595 { \
1596 if (count > 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; } \
1597 FN(are_floats, values, 0, VALUE); \
1598 break; \
1599 }
1600 #define CASE_I(PARAM_TYPE, VALUE) CASE(PARAM_TYPE, int_to_param, VALUE)
1601 #define CASE_F(PARAM_TYPE, VALUE) CASE(PARAM_TYPE, float_to_param, VALUE)
1602
1603 switch (*(VG_CLIENT_OBJECT_TYPE_T *)object) {
1604 case VG_CLIENT_OBJECT_TYPE_PATH:
1605 {
1606 VG_CLIENT_PATH_T *path = (VG_CLIENT_PATH_T *)object;
1607
1608 switch (param_type) {
1609 /*
1610 scalar param types
1611 */
1612
1613 CASE_I(VG_PATH_FORMAT, path->format)
1614 CASE_I(VG_PATH_DATATYPE, path->datatype)
1615 CASE_F(VG_PATH_SCALE, path->scale)
1616 CASE_F(VG_PATH_BIAS, path->bias)
1617 case VG_PATH_NUM_SEGMENTS:
1618 case VG_PATH_NUM_COORDS:
1619 {
1620 if (count > 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1621 if (need_path_segments(path->caps)) {
1622 int_to_param(are_floats, values, 0, (param_type == VG_PATH_NUM_SEGMENTS) ?
1623 path->segments.size :
1624 get_coords_count((const VGubyte *)path->segments.data, path->segments.size));
1625 } else {
1626 VGint value;
1627 if (get_parameter_iv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_PATH, param_type, 1, &value)) {
1628 int_to_param(are_floats, values, 0, value);
1629 }
1630 }
1631 break;
1632 }
1633
1634 /*
1635 invalid param type
1636 */
1637
1638 default:
1639 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1640 }
1641
1642 break;
1643 }
1644 case VG_CLIENT_OBJECT_TYPE_PAINT:
1645 {
1646 VG_CLIENT_PAINT_T *paint = (VG_CLIENT_PAINT_T *)object;
1647
1648 if (!paint->gradient &&
1649 need_paint_gradient(param_type) &&
1650 !paint_alloc_gradient(paint)) {
1651 set_error(VG_OUT_OF_MEMORY_ERROR);
1652 break;
1653 }
1654
1655 switch (param_type) {
1656 /*
1657 scalar param types
1658 */
1659
1660 CASE_I(VG_PAINT_TYPE, paint->type)
1661 CASE_I(VG_PAINT_COLOR_RAMP_SPREAD_MODE, paint->gradient->ramp_spread_mode)
1662 CASE_I(VG_PAINT_COLOR_RAMP_PREMULTIPLIED, paint->gradient->ramp_pre)
1663 CASE_I(VG_PAINT_PATTERN_TILING_MODE, paint->pattern_tiling_mode)
1664
1665 /*
1666 vector param types
1667 */
1668
1669 case VG_PAINT_COLOR:
1670 {
1671 if (count > 4) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1672 floats_to_params(
1673 are_floats, values, count,
1674 paint->color);
1675 break;
1676 }
1677 case VG_PAINT_COLOR_RAMP_STOPS:
1678 {
1679 if ((VGuint)count > paint->gradient->ramp_stops_count) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1680 floats_to_params(
1681 are_floats, values, count,
1682 paint->gradient->ramp_stops);
1683 break;
1684 }
1685 case VG_PAINT_LINEAR_GRADIENT:
1686 {
1687 if (count > 4) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1688 floats_to_params(
1689 are_floats, values, count,
1690 paint->gradient->linear);
1691 break;
1692 }
1693 case VG_PAINT_RADIAL_GRADIENT:
1694 {
1695 if (count > 5) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1696 floats_to_params(
1697 are_floats, values, count,
1698 paint->gradient->radial);
1699 break;
1700 }
1701
1702 /*
1703 invalid param type
1704 */
1705
1706 default:
1707 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1708 }
1709
1710 break;
1711 }
1712 case VG_CLIENT_OBJECT_TYPE_IMAGE:
1713 {
1714 VG_CLIENT_IMAGE_T *image = (VG_CLIENT_IMAGE_T *)object;
1715
1716 switch (param_type) {
1717 /*
1718 scalar param types
1719 */
1720
1721 CASE_I(VG_IMAGE_FORMAT, image->format)
1722 CASE_I(VG_IMAGE_WIDTH, image->width)
1723 CASE_I(VG_IMAGE_HEIGHT, image->height)
1724
1725 /*
1726 invalid param type
1727 */
1728
1729 default:
1730 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1731 }
1732
1733 break;
1734 }
1735 case VG_CLIENT_OBJECT_TYPE_MASK_LAYER:
1736 {
1737 VG_CLIENT_MASK_LAYER_T *mask_layer = (VG_CLIENT_MASK_LAYER_T *)object;
1738
1739 switch (param_type) {
1740 /*
1741 scalar param types
1742 */
1743
1744 CASE_I(VG_IMAGE_WIDTH, mask_layer->width)
1745 CASE_I(VG_IMAGE_HEIGHT, mask_layer->height)
1746
1747 /*
1748 invalid param type
1749 */
1750
1751 default:
1752 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1753 }
1754
1755 break;
1756 }
1757 case VG_CLIENT_OBJECT_TYPE_FONT:
1758 {
1759 switch (param_type) {
1760 /*
1761 scalar param types
1762 */
1763
1764 case VG_FONT_NUM_GLYPHS:
1765 {
1766 VGint glyphs_count;
1767 if (count > 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
1768 if (get_parameter_iv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_FONT, VG_FONT_NUM_GLYPHS, 1, &glyphs_count)) {
1769 int_to_param(are_floats, values, 0, glyphs_count);
1770 }
1771 break;
1772 }
1773
1774 /*
1775 invalid param type
1776 */
1777
1778 default:
1779 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1780 }
1781
1782 break;
1783 }
1784 default:
1785 {
1786 UNREACHABLE();
1787 }
1788 }
1789
1790 #undef CASE_F
1791 #undef CASE_I
1792 #undef CASE
1793
1794 platform_mutex_release(&state->shared_state->mutex);
1795}
1796
1797VG_API_CALL void VG_API_ENTRY vgSetf(
1798 VGParamType param_type,
1799 VGfloat value) VG_API_EXIT
1800{
1801 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
1802 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
1803 if (!state) {
1804 return;
1805 }
1806
1807 if (is_vector_param_type(param_type)) {
1808 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1809 return;
1810 }
1811
1812 set_ifv(thread, state, param_type, 1, true, &value);
1813}
1814
1815VG_API_CALL void VG_API_ENTRY vgSeti(
1816 VGParamType param_type,
1817 VGint value) VG_API_EXIT
1818{
1819 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
1820 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
1821 if (!state) {
1822 return;
1823 }
1824
1825 if (is_vector_param_type(param_type)) {
1826 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1827 return;
1828 }
1829
1830 set_ifv(thread, state, param_type, 1, false, &value);
1831}
1832
1833VG_API_CALL void VG_API_ENTRY vgSetfv(
1834 VGParamType param_type,
1835 VGint count,
1836 const VGfloat *values) VG_API_EXIT
1837{
1838 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
1839 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
1840 if (!state) {
1841 return;
1842 }
1843
1844 /*
1845 * Verify the arguments, and report an error if any of the following holds:
1846 * - 'count' is negative
1847 * - 'count' is greater than zero and 'values' is NULL
1848 * - 'values' is non-NULL but not aligned on an float boundary
1849 *
1850 * If 'count' is zero, then 'values' will not (and must not) be dereferenced
1851 * in this function or any of it's descendents. The OpenVG Specification
1852 * Version 1.1, section 5.2 Setting and Querying Context Parameter Values
1853 * states:
1854 *
1855 * "If the count parameter is 0, the pointer argument is not dereferenced.
1856 * For example, the call vgSet(VG_STROKE_DASH_PATTERN, 0, (void *) 0)
1857 * sets the dash pattern to a zero-length array (which has the effect of
1858 * disabling dashing) without dereferencing the third parameter."
1859 *
1860 * Automatic checking tools such as Coverity may flag this case ('values'
1861 * not verified non-NULL when 'count' is zero) as a potential NULL pointer
1862 * dereference. In this case it is not.
1863 */
1864 if ((count < 0) || ((count > 0) && !values) || (values && !is_aligned_float(values))) {
1865 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1866 return;
1867 }
1868
1869 set_ifv(thread, state, param_type, count, true, values);
1870}
1871
1872VG_API_CALL void VG_API_ENTRY vgSetiv(
1873 VGParamType param_type,
1874 VGint count,
1875 const VGint *values) VG_API_EXIT
1876{
1877 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
1878 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
1879 if (!state) {
1880 return;
1881 }
1882
1883 /*
1884 * Verify the arguments, and report an error if any of the following holds:
1885 * - 'count' is negative
1886 * - 'count' is greater than zero and 'values' is NULL
1887 * - 'values' is non-NULL but not aligned on an integer boundary
1888 *
1889 * If 'count' is zero, then 'values' will not (and must not) be dereferenced
1890 * in this function or any of it's descendents. See the comment in vgSetfv()
1891 * above for more detail.
1892 *
1893 * Automatic checking tools such as Coverity may flag this case ('values'
1894 * not verified non-NULL when 'count' is zero) as a potential NULL pointer
1895 * dereference. In this case it is not.
1896 */
1897 if ((count < 0) || ((count > 0) && !values) || (values && !is_aligned_int(values))) {
1898 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1899 return;
1900 }
1901
1902 set_ifv(thread, state, param_type, count, false, values);
1903}
1904
1905VG_API_CALL VGfloat VG_API_ENTRY vgGetf(
1906 VGParamType param_type) VG_API_EXIT
1907{
1908 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
1909 VGfloat value = 0.0f;
1910
1911 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
1912 if (!state) {
1913 return 0.0f;
1914 }
1915
1916 if (is_vector_param_type(param_type)) {
1917 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1918 return 0.0f;
1919 }
1920
1921 get_ifv(thread, state, param_type, 1, true, &value);
1922 return value;
1923}
1924
1925VG_API_CALL VGint VG_API_ENTRY vgGeti(
1926 VGParamType param_type) VG_API_EXIT
1927{
1928 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
1929 VGint value = 0;
1930
1931 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
1932 if (!state) {
1933 return 0;
1934 }
1935
1936 if (is_vector_param_type(param_type)) {
1937 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
1938 return 0;
1939 }
1940
1941 get_ifv(thread, state, param_type, 1, false, &value);
1942 return value;
1943}
1944
1945VG_API_CALL VGint VG_API_ENTRY vgGetVectorSize(
1946 VGParamType param_type) VG_API_EXIT
1947{
1948
1949 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(CLIENT_GET_THREAD_STATE());
1950 if (!state) {
1951 return 0;
1952 }
1953
1954 switch (param_type) {
1955 /*
1956 scalar param types
1957 */
1958
1959 case VG_MATRIX_MODE:
1960 case VG_FILL_RULE:
1961 case VG_IMAGE_QUALITY:
1962 case VG_RENDERING_QUALITY:
1963 case VG_BLEND_MODE:
1964 case VG_IMAGE_MODE:
1965 case VG_COLOR_TRANSFORM:
1966 case VG_STROKE_LINE_WIDTH:
1967 case VG_STROKE_CAP_STYLE:
1968 case VG_STROKE_JOIN_STYLE:
1969 case VG_STROKE_MITER_LIMIT:
1970 case VG_STROKE_DASH_PHASE:
1971 case VG_STROKE_DASH_PHASE_RESET:
1972 case VG_MASKING:
1973 case VG_SCISSORING:
1974 case VG_PIXEL_LAYOUT:
1975 case VG_SCREEN_LAYOUT:
1976 case VG_FILTER_FORMAT_LINEAR:
1977 case VG_FILTER_FORMAT_PREMULTIPLIED:
1978 case VG_FILTER_CHANNEL_MASK:
1979 case VG_MAX_SCISSOR_RECTS:
1980 case VG_MAX_DASH_COUNT:
1981 case VG_MAX_KERNEL_SIZE:
1982 case VG_MAX_SEPARABLE_KERNEL_SIZE:
1983 case VG_MAX_COLOR_RAMP_STOPS:
1984 case VG_MAX_IMAGE_WIDTH:
1985 case VG_MAX_IMAGE_HEIGHT:
1986 case VG_MAX_IMAGE_PIXELS:
1987 case VG_MAX_IMAGE_BYTES:
1988 case VG_MAX_FLOAT:
1989 case VG_MAX_GAUSSIAN_STD_DEVIATION: return 1;
1990
1991 /*
1992 vector param types
1993 */
1994
1995 case VG_SCISSOR_RECTS: return state->scissor_rects_count;
1996 case VG_COLOR_TRANSFORM_VALUES: return 8;
1997 case VG_STROKE_DASH_PATTERN: return state->stroke_dash_pattern_count;
1998 case VG_TILE_FILL_COLOR:
1999 case VG_CLEAR_COLOR: return 4;
2000 case VG_GLYPH_ORIGIN: return 2;
2001
2002 /*
2003 invalid param type
2004 */
2005
2006 default: set_error(VG_ILLEGAL_ARGUMENT_ERROR); return 0;
2007 }
2008}
2009
2010VG_API_CALL void VG_API_ENTRY vgGetfv(
2011 VGParamType param_type,
2012 VGint count,
2013 VGfloat *values) VG_API_EXIT
2014{
2015 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2016 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2017 if (!state) {
2018 return;
2019 }
2020
2021 if ((count <= 0) || !values || !is_aligned_float(values)) {
2022 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2023 return;
2024 }
2025
2026 get_ifv(thread, state, param_type, count, true, values);
2027}
2028
2029VG_API_CALL void VG_API_ENTRY vgGetiv(
2030 VGParamType param_type,
2031 VGint count,
2032 VGint *values) VG_API_EXIT
2033{
2034 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2035 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2036 if (!state) {
2037 return;
2038 }
2039
2040 if ((count <= 0) || !values || !is_aligned_int(values)) {
2041 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2042 return;
2043 }
2044
2045 get_ifv(thread, state, param_type, count, false, values);
2046}
2047
2048VG_API_CALL void VG_API_ENTRY vgSetParameterf(
2049 VGHandle vg_handle,
2050 VGint param_type,
2051 VGfloat value) VG_API_EXIT
2052{
2053 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2054 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2055 if (!state) {
2056 return;
2057 }
2058
2059 if (is_vector_object_param_type(param_type)) {
2060 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2061 return;
2062 }
2063
2064 set_parameter_ifv(thread, state, vg_handle, param_type, 1, true, &value);
2065}
2066
2067VG_API_CALL void VG_API_ENTRY vgSetParameteri(
2068 VGHandle vg_handle,
2069 VGint param_type,
2070 VGint value) VG_API_EXIT
2071{
2072 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2073 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2074 if (!state) {
2075 return;
2076 }
2077
2078 if (is_vector_object_param_type(param_type)) {
2079 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2080 return;
2081 }
2082
2083 set_parameter_ifv(thread, state, vg_handle, param_type, 1, false, &value);
2084}
2085
2086VG_API_CALL void VG_API_ENTRY vgSetParameterfv(
2087 VGHandle vg_handle,
2088 VGint param_type,
2089 VGint count,
2090 const VGfloat *values) VG_API_EXIT
2091{
2092 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2093 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2094 if (!state) {
2095 return;
2096 }
2097
2098 /*
2099 * Verify the arguments, and report an error if any of the following holds:
2100 * - 'count' is negative
2101 * - 'count' is greater than zero and 'values' is NULL
2102 * - 'values' is non-NULL but not aligned on an float boundary
2103 *
2104 * If 'count' is zero, then 'values' will not (and must not) be dereferenced
2105 * in this function or any of it's descendents. See the comment in vgSetfv()
2106 * above for more detail.
2107 *
2108 * Automatic checking tools such as Coverity may flag this case ('values'
2109 * not verified non-NULL when 'count' is zero) as a potential NULL pointer
2110 * dereference. In this case it is not.
2111 */
2112 if ((count < 0) || ((count > 0) && !values) || (values && !is_aligned_float(values))) {
2113 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2114 return;
2115 }
2116
2117 set_parameter_ifv(thread, state, vg_handle, param_type, count, true, values);
2118}
2119
2120VG_API_CALL void VG_API_ENTRY vgSetParameteriv(
2121 VGHandle vg_handle,
2122 VGint param_type,
2123 VGint count,
2124 const VGint *values) VG_API_EXIT
2125{
2126 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2127 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2128 if (!state) {
2129 return;
2130 }
2131
2132 /*
2133 * Verify the arguments, and report an error if any of the following holds:
2134 * - 'count' is negative
2135 * - 'count' is greater than zero and 'values' is NULL
2136 * - 'values' is non-NULL but not aligned on an integer boundary
2137 *
2138 * If 'count' is zero, then 'values' will not (and must not) be dereferenced
2139 * in this function or any of it's descendents. See the comment in vgSetfv()
2140 * above for more detail.
2141 *
2142 * Automatic checking tools such as Coverity may flag this case ('values'
2143 * not verified non-NULL when 'count' is zero) as a potential NULL pointer
2144 * dereference. In this case it is not.
2145 */
2146 if ((count < 0) || ((count > 0) && !values) || (values && !is_aligned_int(values))) {
2147 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2148 return;
2149 }
2150
2151 set_parameter_ifv(thread, state, vg_handle, param_type, count, false, values);
2152}
2153
2154VG_API_CALL VGfloat VG_API_ENTRY vgGetParameterf(
2155 VGHandle vg_handle,
2156 VGint param_type) VG_API_EXIT
2157{
2158 VGfloat value = 0.0f;
2159
2160 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2161 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2162 if (!state) {
2163 return 0.0f;
2164 }
2165
2166 if (is_vector_object_param_type(param_type)) {
2167 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2168 return 0.0f;
2169 }
2170
2171 get_parameter_ifv(thread, state, vg_handle, param_type, 1, true, &value);
2172 return value;
2173}
2174
2175VG_API_CALL VGint VG_API_ENTRY vgGetParameteri(
2176 VGHandle vg_handle,
2177 VGint param_type) VG_API_EXIT
2178{
2179 VGint value = 0;
2180
2181 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2182 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2183 if (!state) {
2184 return 0;
2185 }
2186
2187 if (is_vector_object_param_type(param_type)) {
2188 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2189 return 0;
2190 }
2191
2192 get_parameter_ifv(thread, state, vg_handle, param_type, 1, false, &value);
2193 return value;
2194}
2195
2196VG_API_CALL VGint VG_API_ENTRY vgGetParameterVectorSize(
2197 VGHandle vg_handle,
2198 VGint param_type) VG_API_EXIT
2199{
2200 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2201 void *object;
2202 VGint count = 0;
2203
2204 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2205 if (!state) {
2206 return 0;
2207 }
2208
2209 platform_mutex_acquire(&state->shared_state->mutex);
2210
2211 object = khrn_pointer_map_lookup(&state->shared_state->objects, nice_handle(vg_handle));
2212 if (!object) {
2213 set_error(VG_BAD_HANDLE_ERROR);
2214 platform_mutex_release(&state->shared_state->mutex);
2215 return 0;
2216 }
2217
2218 switch (*(VG_CLIENT_OBJECT_TYPE_T *)object) {
2219 case VG_CLIENT_OBJECT_TYPE_PATH:
2220 {
2221 switch (param_type) {
2222 /*
2223 scalar param types
2224 */
2225
2226 case VG_PATH_FORMAT:
2227 case VG_PATH_DATATYPE:
2228 case VG_PATH_SCALE:
2229 case VG_PATH_BIAS:
2230 case VG_PATH_NUM_SEGMENTS:
2231 case VG_PATH_NUM_COORDS:
2232 {
2233 count = 1;
2234 break;
2235 }
2236
2237 /*
2238 invalid param type
2239 */
2240
2241 default:
2242 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2243 }
2244
2245 break;
2246 }
2247 case VG_CLIENT_OBJECT_TYPE_PAINT:
2248 {
2249 switch (param_type) {
2250 /*
2251 scalar param types
2252 */
2253
2254 case VG_PAINT_TYPE:
2255 case VG_PAINT_COLOR_RAMP_SPREAD_MODE:
2256 case VG_PAINT_COLOR_RAMP_PREMULTIPLIED:
2257 case VG_PAINT_PATTERN_TILING_MODE:
2258 {
2259 count = 1;
2260 break;
2261 }
2262
2263 /*
2264 vector param types
2265 */
2266
2267 case VG_PAINT_COLOR:
2268 {
2269 count = 4;
2270 break;
2271 }
2272 case VG_PAINT_COLOR_RAMP_STOPS:
2273 {
2274 VG_CLIENT_PAINT_T *paint = (VG_CLIENT_PAINT_T *)object;
2275 count = paint->gradient ? paint->gradient->ramp_stops_count : 0;
2276 break;
2277 }
2278 case VG_PAINT_LINEAR_GRADIENT:
2279 {
2280 count = 4;
2281 break;
2282 }
2283 case VG_PAINT_RADIAL_GRADIENT:
2284 {
2285 count = 5;
2286 break;
2287 }
2288
2289 /*
2290 invalid param type
2291 */
2292
2293 default:
2294 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2295 }
2296
2297 break;
2298 }
2299 case VG_CLIENT_OBJECT_TYPE_IMAGE:
2300 {
2301 switch (param_type) {
2302 /*
2303 scalar param types
2304 */
2305
2306 case VG_IMAGE_FORMAT:
2307 case VG_IMAGE_WIDTH:
2308 case VG_IMAGE_HEIGHT:
2309 {
2310 count = 1;
2311 break;
2312 }
2313
2314 /*
2315 invalid param type
2316 */
2317
2318 default:
2319 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2320 }
2321
2322 break;
2323 }
2324 case VG_CLIENT_OBJECT_TYPE_MASK_LAYER:
2325 {
2326 switch (param_type) {
2327 /*
2328 scalar param types
2329 */
2330
2331 case VG_IMAGE_WIDTH:
2332 case VG_IMAGE_HEIGHT:
2333 {
2334 count = 1;
2335 break;
2336 }
2337
2338 /*
2339 invalid param type
2340 */
2341
2342 default:
2343 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2344 }
2345
2346 break;
2347 }
2348 case VG_CLIENT_OBJECT_TYPE_FONT:
2349 {
2350 switch (param_type) {
2351 /*
2352 scalar param types
2353 */
2354
2355 case VG_FONT_NUM_GLYPHS:
2356 {
2357 count = 1;
2358 break;
2359 }
2360
2361 /*
2362 invalid param type
2363 */
2364
2365 default:
2366 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2367 }
2368
2369 break;
2370 }
2371 default:
2372 {
2373 UNREACHABLE();
2374 }
2375 }
2376
2377 platform_mutex_release(&state->shared_state->mutex);
2378 return count;
2379}
2380
2381VG_API_CALL void VG_API_ENTRY vgGetParameterfv(
2382 VGHandle vg_handle,
2383 VGint param_type,
2384 VGint count,
2385 VGfloat *values) VG_API_EXIT
2386{
2387 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2388 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2389 if (!state) {
2390 return;
2391 }
2392
2393 if ((count <= 0) || !values || !is_aligned_float(values)) {
2394 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2395 return;
2396 }
2397
2398 get_parameter_ifv(thread, state, vg_handle, param_type, count, true, values);
2399}
2400
2401VG_API_CALL void VG_API_ENTRY vgGetParameteriv(
2402 VGHandle vg_handle,
2403 VGint param_type,
2404 VGint count,
2405 VGint *values) VG_API_EXIT
2406{
2407 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2408 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2409 if (!state) {
2410 return;
2411 }
2412
2413 if ((count <= 0) || !values || !is_aligned_int(values)) {
2414 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2415 return;
2416 }
2417
2418 get_parameter_ifv(thread, state, vg_handle, param_type, count, false, values);
2419}
2420
2421/******************************************************************************
2422api matrices
2423******************************************************************************/
2424
2425VG_API_CALL void VG_API_ENTRY vgLoadIdentity(void) VG_API_EXIT
2426{
2427 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2428 VG_MAT3X3_SYNC_T *matrix_sync;
2429
2430 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2431 if (!state) {
2432 return;
2433 }
2434
2435 matrix_sync = get_matrix_sync(state, state->matrix_mode);
2436 vg_mat3x3_set_identity(&matrix_sync->client);
2437}
2438
2439VG_API_CALL void VG_API_ENTRY vgLoadMatrix(const VGfloat *matrix) VG_API_EXIT
2440{
2441 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2442 VG_MAT3X3_SYNC_T *matrix_sync;
2443
2444 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2445 if (!state) {
2446 return;
2447 }
2448
2449 if (!matrix || !is_aligned_float(matrix)) {
2450 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2451 return;
2452 }
2453
2454 matrix_sync = get_matrix_sync(state, state->matrix_mode);
2455 vg_mat3x3_set_clean(&matrix_sync->client, matrix, is_matrix_affine(state->matrix_mode));
2456}
2457
2458VG_API_CALL void VG_API_ENTRY vgGetMatrix(VGfloat *matrix) VG_API_EXIT
2459{
2460 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2461 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2462 if (!state) {
2463 return;
2464 }
2465
2466 if (!matrix || !is_aligned_float(matrix)) {
2467 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2468 return;
2469 }
2470
2471 vg_mat3x3_get(&get_matrix_sync(state, state->matrix_mode)->client, matrix);
2472}
2473
2474VG_API_CALL void VG_API_ENTRY vgMultMatrix(const VGfloat *matrix) VG_API_EXIT
2475{
2476 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2477 VG_MAT3X3_T a;
2478 VG_MAT3X3_SYNC_T *matrix_sync;
2479
2480 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2481 if (!state) {
2482 return;
2483 }
2484
2485 if (!matrix || !is_aligned_float(matrix)) {
2486 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2487 return;
2488 }
2489
2490 vg_mat3x3_set_clean(&a, matrix, is_matrix_affine(state->matrix_mode));
2491
2492 matrix_sync = get_matrix_sync(state, state->matrix_mode);
2493 vg_mat3x3_postmul(&matrix_sync->client, &a);
2494}
2495
2496VG_API_CALL void VG_API_ENTRY vgTranslate(VGfloat x, VGfloat y) VG_API_EXIT
2497{
2498 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2499 VG_CLIENT_STATE_T *state;
2500 VG_MAT3X3_SYNC_T *matrix_sync;
2501
2502 x = clean_float(x);
2503 y = clean_float(y);
2504
2505 state = VG_GET_CLIENT_STATE(thread);
2506 if (!state) {
2507 return;
2508 }
2509
2510 matrix_sync = get_matrix_sync(state, state->matrix_mode);
2511 vg_mat3x3_postmul_translate(&matrix_sync->client, x, y);
2512}
2513
2514VG_API_CALL void VG_API_ENTRY vgScale(VGfloat x, VGfloat y) VG_API_EXIT
2515{
2516 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2517 VG_CLIENT_STATE_T *state;
2518 VG_MAT3X3_SYNC_T *matrix_sync;
2519
2520 x = clean_float(x);
2521 y = clean_float(y);
2522
2523 state = VG_GET_CLIENT_STATE(thread);
2524 if (!state) {
2525 return;
2526 }
2527
2528 matrix_sync = get_matrix_sync(state, state->matrix_mode);
2529 vg_mat3x3_postmul_scale(&matrix_sync->client, x, y);
2530}
2531
2532VG_API_CALL void VG_API_ENTRY vgShear(VGfloat x, VGfloat y) VG_API_EXIT
2533{
2534 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2535 VG_CLIENT_STATE_T *state;
2536 VG_MAT3X3_SYNC_T *matrix_sync;
2537
2538 x = clean_float(x);
2539 y = clean_float(y);
2540
2541 state = VG_GET_CLIENT_STATE(thread);
2542 if (!state) {
2543 return;
2544 }
2545
2546 matrix_sync = get_matrix_sync(state, state->matrix_mode);
2547 vg_mat3x3_postmul_shear(&matrix_sync->client, x, y);
2548}
2549
2550VG_API_CALL void VG_API_ENTRY vgRotate(VGfloat angle) VG_API_EXIT
2551{
2552 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2553 VG_CLIENT_STATE_T *state;
2554 VG_MAT3X3_SYNC_T *matrix_sync;
2555
2556 angle = clean_float(angle);
2557
2558 state = VG_GET_CLIENT_STATE(thread);
2559 if (!state) {
2560 return;
2561 }
2562
2563 matrix_sync = get_matrix_sync(state, state->matrix_mode);
2564 vg_mat3x3_postmul_rotate(&matrix_sync->client, angle * (PI / 180.0f));
2565}
2566
2567/******************************************************************************
2568api mask/clear
2569******************************************************************************/
2570
2571VG_API_CALL void VG_API_ENTRY vgMask(
2572 VGHandle vg_handle, /* theoretically image under vg 1.0 */
2573 VGMaskOperation operation,
2574 VGint dst_x, VGint dst_y,
2575 VGint width, VGint height) VG_API_EXIT
2576{
2577 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2578 if (!VG_GET_CLIENT_STATE(thread)) {
2579 return;
2580 }
2581
2582 RPC_CALL6(vgMask_impl,
2583 thread,
2584 VGMASK_ID,
2585 RPC_HANDLE(vg_handle),
2586 RPC_ENUM(operation),
2587 RPC_INT(dst_x),
2588 RPC_INT(dst_y),
2589 RPC_INT(width),
2590 RPC_INT(height));
2591}
2592
2593VG_API_CALL void VG_API_ENTRY vgRenderToMask(
2594 VGPath vg_handle,
2595 VGbitfield paint_modes,
2596 VGMaskOperation operation) VG_API_EXIT
2597{
2598 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2599 VG_CLIENT_STATE_T *state;
2600
2601#ifdef VG_NO_STROKING
2602 paint_modes &= ~VG_STROKE_PATH;
2603 if (!paint_modes) { return; }
2604#endif
2605
2606 state = VG_GET_CLIENT_STATE(thread);
2607 if (!state) {
2608 return;
2609 }
2610
2611 sync_matrix(state, VG_MATRIX_PATH_USER_TO_SURFACE);
2612
2613 RPC_CALL3(vgRenderToMask_impl,
2614 thread,
2615 VGRENDERTOMASK_ID,
2616 RPC_HANDLE(vg_handle),
2617 RPC_BITFIELD(paint_modes),
2618 RPC_ENUM(operation));
2619}
2620
2621VG_API_CALL VGMaskLayer VG_API_ENTRY vgCreateMaskLayer(
2622 VGint width, VGint height) VG_API_EXIT
2623{
2624 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2625 VGHandle vg_handle;
2626 VG_CLIENT_MASK_LAYER_T *mask_layer;
2627
2628 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2629 if (!state) {
2630 return VG_INVALID_HANDLE;
2631 }
2632
2633 if ((width <= 0) || (height <= 0) ||
2634 (width > VG_CONFIG_MAX_IMAGE_WIDTH) || (height > VG_CONFIG_MAX_IMAGE_HEIGHT) ||
2635 ((width * height) > VG_CONFIG_MAX_IMAGE_PIXELS)) {
2636 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2637 return VG_INVALID_HANDLE;
2638 }
2639
2640 if (egl_config_get_mask_format(egl_config_to_id(
2641 CLIENT_GET_THREAD_STATE()->openvg.draw->config)) == IMAGE_FORMAT_INVALID) {
2642 return VG_INVALID_HANDLE;
2643 }
2644
2645 vg_handle = get_stem(state);
2646 if (vg_handle == VG_INVALID_HANDLE) {
2647 set_error(VG_OUT_OF_MEMORY_ERROR);
2648 return VG_INVALID_HANDLE;
2649 }
2650
2651 mask_layer = mask_layer_alloc(width, height);
2652 if (!mask_layer) {
2653 set_error(VG_OUT_OF_MEMORY_ERROR);
2654 destroy_stem(vg_handle);
2655 return VG_INVALID_HANDLE;
2656 }
2657
2658 platform_mutex_acquire(&state->shared_state->mutex);
2659 if (!insert_object(state, vg_handle, mask_layer)) {
2660 set_error(VG_OUT_OF_MEMORY_ERROR);
2661 platform_mutex_release(&state->shared_state->mutex);
2662 mask_layer_free(mask_layer);
2663 destroy_stem(vg_handle);
2664 return VG_INVALID_HANDLE;
2665 }
2666 platform_mutex_release(&state->shared_state->mutex);
2667
2668 RPC_CALL3(vgCreateMaskLayer_impl,
2669 thread,
2670 VGCREATEMASKLAYER_ID,
2671 RPC_HANDLE(vg_handle),
2672 RPC_INT(width),
2673 RPC_INT(height));
2674
2675 return vg_handle;
2676}
2677
2678VG_API_CALL void VG_API_ENTRY vgDestroyMaskLayer(
2679 VGMaskLayer vg_handle) VG_API_EXIT
2680{
2681 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2682 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2683 if (!state) {
2684 return;
2685 }
2686
2687 platform_mutex_acquire(&state->shared_state->mutex);
2688 delete_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_MASK_LAYER);
2689 platform_mutex_release(&state->shared_state->mutex);
2690
2691 RPC_CALL1(vgDestroyMaskLayer_impl,
2692 thread,
2693 VGDESTROYMASKLAYER_ID,
2694 RPC_HANDLE(vg_handle));
2695}
2696
2697VG_API_CALL void VG_API_ENTRY vgFillMaskLayer(
2698 VGMaskLayer vg_handle,
2699 VGint x, VGint y,
2700 VGint width, VGint height,
2701 VGfloat value) VG_API_EXIT
2702{
2703 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2704 value = clean_float(value);
2705
2706 if (!VG_GET_CLIENT_STATE(thread)) {
2707 return;
2708 }
2709
2710 RPC_CALL6(vgFillMaskLayer_impl,
2711 thread,
2712 VGFILLMASKLAYER_ID,
2713 RPC_HANDLE(vg_handle),
2714 RPC_INT(x), RPC_INT(y),
2715 RPC_INT(width), RPC_INT(height),
2716 RPC_FLOAT(value));
2717}
2718
2719VG_API_CALL void VG_API_ENTRY vgCopyMask(
2720 VGMaskLayer dst_vg_handle,
2721 VGint dst_x, VGint dst_y,
2722 VGint src_x, VGint src_y,
2723 VGint width, VGint height) VG_API_EXIT
2724{
2725 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2726 if (!VG_GET_CLIENT_STATE(thread)) {
2727 return;
2728 }
2729
2730 RPC_CALL7(vgCopyMask_impl,
2731 thread,
2732 VGCOPYMASK_ID,
2733 RPC_HANDLE(dst_vg_handle),
2734 RPC_INT(dst_x), RPC_INT(dst_y),
2735 RPC_INT(src_x), RPC_INT(src_y),
2736 RPC_INT(width), RPC_INT(height));
2737}
2738
2739VG_API_CALL void VG_API_ENTRY vgClear(
2740 VGint x, VGint y,
2741 VGint width, VGint height) VG_API_EXIT
2742{
2743 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2744 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2745 if (!state) {
2746 return;
2747 }
2748
2749 //TODO: pixmap behaviour can be better optimized to handle clears
2750 if (state->render_callback)
2751 state->render_callback();
2752
2753 RPC_CALL4(vgClear_impl,
2754 thread,
2755 VGCLEAR_ID,
2756 RPC_INT(x),
2757 RPC_INT(y),
2758 RPC_INT(width),
2759 RPC_INT(height));
2760}
2761
2762/******************************************************************************
2763api path
2764******************************************************************************/
2765
2766VG_API_CALL VGPath VG_API_ENTRY vgCreatePath(
2767 VGint format,
2768 VGPathDatatype datatype,
2769 VGfloat scale, VGfloat bias,
2770 VGint segments_capacity,
2771 VGint coords_capacity,
2772 VGbitfield caps) VG_API_EXIT
2773{
2774 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2775 VG_CLIENT_STATE_T *state;
2776 VGHandle vg_handle;
2777 VG_CLIENT_PATH_T *path;
2778
2779 scale = clean_float(scale);
2780 bias = clean_float(bias);
2781 caps &= VG_PATH_CAPABILITY_ALL;
2782
2783 state = VG_GET_CLIENT_STATE(thread);
2784 if (!state) {
2785 return VG_INVALID_HANDLE;
2786 }
2787
2788 if (!is_path_format(format)) {
2789 set_error(VG_UNSUPPORTED_PATH_FORMAT_ERROR);
2790 return VG_INVALID_HANDLE;
2791 }
2792 if (!is_path_datatype(datatype) || is_zero(scale)) {
2793 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
2794 return VG_INVALID_HANDLE;
2795 }
2796
2797 vg_handle = get_stem(state);
2798 if (vg_handle == VG_INVALID_HANDLE) {
2799 set_error(VG_OUT_OF_MEMORY_ERROR);
2800 return VG_INVALID_HANDLE;
2801 }
2802
2803 path = path_alloc(format, datatype, scale, bias, caps, segments_capacity);
2804 if (!path) {
2805 set_error(VG_OUT_OF_MEMORY_ERROR);
2806 destroy_stem(vg_handle);
2807 return VG_INVALID_HANDLE;
2808 }
2809
2810 platform_mutex_acquire(&state->shared_state->mutex);
2811 if (!insert_object(state, vg_handle, path)) {
2812 set_error(VG_OUT_OF_MEMORY_ERROR);
2813 platform_mutex_release(&state->shared_state->mutex);
2814 path_free(path);
2815 destroy_stem(vg_handle);
2816 return VG_INVALID_HANDLE;
2817 }
2818 platform_mutex_release(&state->shared_state->mutex);
2819
2820 RPC_CALL8(vgCreatePath_impl,
2821 thread,
2822 VGCREATEPATH_ID,
2823 RPC_HANDLE(vg_handle),
2824 RPC_INT(format),
2825 RPC_ENUM(datatype),
2826 RPC_FLOAT(scale), RPC_FLOAT(bias),
2827 RPC_INT(segments_capacity),
2828 RPC_INT(coords_capacity),
2829 RPC_BITFIELD(caps));
2830
2831 return vg_handle;
2832}
2833
2834VG_API_CALL void VG_API_ENTRY vgClearPath(
2835 VGPath vg_handle,
2836 VGbitfield caps) VG_API_EXIT
2837{
2838 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2839 VG_CLIENT_STATE_T *state;
2840 VG_CLIENT_PATH_T *path;
2841
2842 caps &= VG_PATH_CAPABILITY_ALL;
2843
2844 state = VG_GET_CLIENT_STATE(thread);
2845 if (!state) {
2846 return;
2847 }
2848
2849 platform_mutex_acquire(&state->shared_state->mutex);
2850 path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
2851 if (path) {
2852 if (need_path_segments(path->caps) && need_path_segments(caps)) {
2853 khrn_vector_clear(&path->segments);
2854 }
2855 path_update_caps(path, caps);
2856 }
2857 platform_mutex_release(&state->shared_state->mutex);
2858
2859 RPC_CALL2(vgClearPath_impl,
2860 thread,
2861 VGCLEARPATH_ID,
2862 RPC_HANDLE(vg_handle),
2863 RPC_BITFIELD(caps));
2864}
2865
2866VG_API_CALL void VG_API_ENTRY vgDestroyPath(
2867 VGPath vg_handle) VG_API_EXIT
2868{
2869 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2870 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2871 if (!state) {
2872 return;
2873 }
2874
2875 platform_mutex_acquire(&state->shared_state->mutex);
2876 delete_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
2877 platform_mutex_release(&state->shared_state->mutex);
2878
2879 RPC_CALL1(vgDestroyPath_impl,
2880 thread,
2881 VGDESTROYPATH_ID,
2882 RPC_HANDLE(vg_handle));
2883}
2884
2885VG_API_CALL void VG_API_ENTRY vgRemovePathCapabilities(
2886 VGPath vg_handle,
2887 VGbitfield caps) VG_API_EXIT
2888{
2889 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2890 VG_CLIENT_PATH_T *path;
2891
2892 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2893 if (!state) {
2894 return;
2895 }
2896
2897 platform_mutex_acquire(&state->shared_state->mutex);
2898 path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
2899 if (path) {
2900 path_update_caps(path, path->caps & ~caps);
2901 }
2902 platform_mutex_release(&state->shared_state->mutex);
2903
2904 RPC_CALL2(vgRemovePathCapabilities_impl,
2905 thread,
2906 VGREMOVEPATHCAPABILITIES_ID,
2907 RPC_HANDLE(vg_handle),
2908 RPC_BITFIELD(caps));
2909}
2910
2911VG_API_CALL VGbitfield VG_API_ENTRY vgGetPathCapabilities(
2912 VGPath vg_handle) VG_API_EXIT
2913{
2914 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2915 VGbitfield caps = 0;
2916 VG_CLIENT_PATH_T *path;
2917
2918 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2919 if (!state) {
2920 return 0;
2921 }
2922
2923 platform_mutex_acquire(&state->shared_state->mutex);
2924 path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
2925 if (path) {
2926 caps = path->caps;
2927 } else {
2928 set_error(VG_BAD_HANDLE_ERROR);
2929 }
2930 platform_mutex_release(&state->shared_state->mutex);
2931
2932 return caps;
2933}
2934
2935VG_API_CALL void VG_API_ENTRY vgAppendPath(
2936 VGPath dst_vg_handle,
2937 VGPath src_vg_handle) VG_API_EXIT
2938{
2939 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2940 VG_CLIENT_PATH_T *dst;
2941 VG_CLIENT_PATH_T *src;
2942
2943 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2944 if (!state) {
2945 return;
2946 }
2947
2948 platform_mutex_acquire(&state->shared_state->mutex);
2949 dst = (VG_CLIENT_PATH_T *)lookup_object(state, dst_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
2950 src = (VG_CLIENT_PATH_T *)lookup_object(state, src_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
2951 if (dst && src &&
2952 (dst->caps & VG_PATH_CAPABILITY_APPEND_TO) && (src->caps & VG_PATH_CAPABILITY_APPEND_FROM) &&
2953 need_path_segments(dst->caps)) {
2954 VGuint segments_count = src->segments.size;
2955 if (!khrn_vector_extend(&dst->segments, segments_count)) {
2956 set_error(VG_OUT_OF_MEMORY_ERROR);
2957 platform_mutex_release(&state->shared_state->mutex);
2958 return;
2959 }
2960 memcpy((VGubyte *)dst->segments.data + (dst->segments.size - segments_count), src->segments.data, segments_count);
2961 }
2962 platform_mutex_release(&state->shared_state->mutex);
2963
2964 RPC_CALL2(vgAppendPath_impl,
2965 thread,
2966 VGAPPENDPATH_ID,
2967 RPC_HANDLE(dst_vg_handle),
2968 RPC_HANDLE(src_vg_handle));
2969}
2970
2971VG_API_CALL void VG_API_ENTRY vgAppendPathData(
2972 VGPath vg_handle,
2973 VGint segments_count, const VGubyte *segments,
2974 const void *coords) VG_API_EXIT
2975{
2976 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
2977 VG_CLIENT_PATH_T *path;
2978 VGPathDatatype datatype;
2979 VGuint datatype_size;
2980
2981 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
2982 if (!state) {
2983 return;
2984 }
2985
2986 platform_mutex_acquire(&state->shared_state->mutex);
2987
2988 path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
2989 if (!path) {
2990 set_error(VG_BAD_HANDLE_ERROR);
2991 platform_mutex_release(&state->shared_state->mutex);
2992 return;
2993 }
2994
2995 datatype = path->datatype;
2996
2997 if ((segments_count <= 0) || !segments ||
2998 contains_illegal_segment(segments, segments_count) ||
2999 !coords || !is_aligned_path_datatype(coords, datatype)) {
3000 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
3001 platform_mutex_release(&state->shared_state->mutex);
3002 return;
3003 }
3004
3005 if (!(path->caps & VG_PATH_CAPABILITY_APPEND_TO)) {
3006 set_error(VG_PATH_CAPABILITY_ERROR);
3007 platform_mutex_release(&state->shared_state->mutex);
3008 return;
3009 }
3010
3011 if (need_path_segments(path->caps)) {
3012 if (!khrn_vector_extend(&path->segments, segments_count)) {
3013 set_error(VG_OUT_OF_MEMORY_ERROR);
3014 platform_mutex_release(&state->shared_state->mutex);
3015 return;
3016 }
3017 memcpy((VGubyte *)path->segments.data + (path->segments.size - segments_count), segments, segments_count);
3018 }
3019
3020 platform_mutex_release(&state->shared_state->mutex);
3021
3022 datatype_size = get_path_datatype_size(datatype);
3023
3024 #ifdef RPC_DIRECT
3025 {
3026 VGuint coords_count = 0;
3027 VGuint i;
3028 for (i = 0; i != segments_count; ++i) {
3029 coords_count += get_segment_coords_count(segments[i] & ~VG_RELATIVE);
3030 }
3031
3032 RPC_CALL6(vgAppendPathData_impl, thread, no_id,
3033 vg_handle,
3034 datatype,
3035 segments_count, segments,
3036 coords_count * datatype_size, coords);
3037 }
3038 #else
3039 /*
3040 split into multiple calls if necessary to avoid overflowing control buffer
3041 */
3042
3043 while (segments_count != 0) {
3044 #define MESSAGE_SIZE 20
3045
3046 VGuint size_max = rpc_send_ctrl_longest(thread, MESSAGE_SIZE + rpc_pad_ctrl(1) + rpc_pad_ctrl(6 * datatype_size)) - MESSAGE_SIZE; /* fit at least one segment */
3047 VGint chunk_segments_count = 0;
3048 VGuint chunk_coords_size = 0;
3049 for (; chunk_segments_count != segments_count; ++chunk_segments_count) {
3050 VGuint segment_coords_size = get_segment_coords_count(segments[chunk_segments_count] & ~VG_RELATIVE) * datatype_size;
3051 if ((rpc_pad_ctrl(chunk_segments_count + 1) +
3052 rpc_pad_ctrl(chunk_coords_size + segment_coords_size)) > size_max) {
3053 /*
3054 can't fit this segment in
3055 */
3056
3057 break;
3058 }
3059 chunk_coords_size += segment_coords_size;
3060 }
3061
3062 {
3063 uint32_t message[] = {
3064 VGAPPENDPATHDATA_ID,
3065 RPC_HANDLE(vg_handle),
3066 RPC_ENUM(datatype),
3067 RPC_INT(chunk_segments_count),
3068 RPC_UINT(chunk_coords_size) };
3069 vcos_static_assert(sizeof(message) == MESSAGE_SIZE);
3070
3071 #undef MESSAGE_SIZE
3072 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3073
3074 rpc_send_ctrl_begin(thread, sizeof(message) + rpc_pad_ctrl(chunk_segments_count) + rpc_pad_ctrl(chunk_coords_size));
3075 rpc_send_ctrl_write(thread, message, sizeof(message));
3076 rpc_send_ctrl_write(thread, (uint32_t *)segments, chunk_segments_count);
3077 rpc_send_ctrl_write(thread, (uint32_t *)coords, chunk_coords_size);
3078 rpc_send_ctrl_end(thread);
3079 }
3080
3081 segments_count -= chunk_segments_count;
3082 segments += chunk_segments_count;
3083 coords = (const uint8_t *)coords + chunk_coords_size;
3084 }
3085 #endif
3086}
3087
3088VG_API_CALL void VG_API_ENTRY vgModifyPathCoords(
3089 VGPath vg_handle,
3090 VGint segments_i, VGint segments_count,
3091 const void *coords) VG_API_EXIT
3092{
3093 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3094 VG_CLIENT_PATH_T *path;
3095 VGPathDatatype datatype;
3096 VGuint datatype_size;
3097 VGuint coords_offset;
3098 VGuint coords_size;
3099
3100 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
3101 if (!state) {
3102 return;
3103 }
3104
3105 platform_mutex_acquire(&state->shared_state->mutex);
3106
3107 path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
3108 if (!path) {
3109 set_error(VG_BAD_HANDLE_ERROR);
3110 platform_mutex_release(&state->shared_state->mutex);
3111 return;
3112 }
3113
3114 if (!(path->caps & VG_PATH_CAPABILITY_MODIFY)) {
3115 set_error(VG_PATH_CAPABILITY_ERROR);
3116 platform_mutex_release(&state->shared_state->mutex);
3117 return;
3118 }
3119
3120 datatype = path->datatype;
3121
3122 if ((segments_i < 0) || (segments_count <= 0) ||
3123 /* will fit in VGuint (may not fit in VGint) */
3124 (((VGuint)(segments_i + segments_count)) > path->segments.size) ||
3125 !coords || !is_aligned_path_datatype(coords, datatype)) {
3126 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
3127 platform_mutex_release(&state->shared_state->mutex);
3128 return;
3129 }
3130
3131 datatype_size = get_path_datatype_size(path->datatype);
3132 coords_offset = get_coords_count((const VGubyte *)path->segments.data, segments_i) * datatype_size;
3133 coords_size = get_coords_count((const VGubyte *)path->segments.data + segments_i, segments_count) * datatype_size;
3134
3135 platform_mutex_release(&state->shared_state->mutex);
3136
3137 #ifdef RPC_DIRECT
3138 RPC_CALL5(vgModifyPathCoords_impl, thread, no_id,
3139 vg_handle,
3140 datatype,
3141 coords_offset, coords_size, coords);
3142 #else
3143 /*
3144 split into multiple calls if necessary to avoid overflowing control buffer
3145 */
3146
3147 while (coords_size != 0) {
3148 #define MESSAGE_SIZE 20
3149
3150 VGuint chunk_coords_size = _min(coords_size, rpc_send_ctrl_longest(thread, MESSAGE_SIZE + rpc_pad_ctrl(4)) - MESSAGE_SIZE); /* fit at least one coordinate */
3151
3152 uint32_t message[] = {
3153 VGMODIFYPATHCOORDS_ID,
3154 RPC_HANDLE(vg_handle),
3155 RPC_ENUM(datatype),
3156 RPC_UINT(coords_offset),
3157 RPC_UINT(chunk_coords_size) };
3158 vcos_static_assert(sizeof(message) == MESSAGE_SIZE);
3159
3160 #undef MESSAGE_SIZE
3161
3162 rpc_send_ctrl_begin(thread, sizeof(message) + rpc_pad_ctrl(chunk_coords_size));
3163 rpc_send_ctrl_write(thread, message, sizeof(message));
3164 rpc_send_ctrl_write(thread, (uint32_t *)coords, chunk_coords_size);
3165 rpc_send_ctrl_end(thread);
3166
3167 coords_size -= chunk_coords_size;
3168 coords_offset += chunk_coords_size;
3169 coords = (const uint8_t *)coords + chunk_coords_size;
3170 }
3171 #endif
3172}
3173
3174VG_API_CALL void VG_API_ENTRY vgTransformPath(
3175 VGPath dst_vg_handle,
3176 VGPath src_vg_handle) VG_API_EXIT
3177{
3178 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3179 VG_CLIENT_PATH_T *dst;
3180 VG_CLIENT_PATH_T *src;
3181
3182 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
3183 if (!state) {
3184 return;
3185 }
3186
3187 platform_mutex_acquire(&state->shared_state->mutex);
3188 dst = (VG_CLIENT_PATH_T *)lookup_object(state, dst_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
3189 src = (VG_CLIENT_PATH_T *)lookup_object(state, src_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
3190 if (dst && src &&
3191 (dst->caps & VG_PATH_CAPABILITY_TRANSFORM_TO) && (src->caps & VG_PATH_CAPABILITY_TRANSFORM_FROM) &&
3192 need_path_segments(dst->caps)) {
3193 VGuint segments_count = src->segments.size;
3194 if (!khrn_vector_extend(&dst->segments, segments_count)) {
3195 set_error(VG_OUT_OF_MEMORY_ERROR);
3196 platform_mutex_release(&state->shared_state->mutex);
3197 return;
3198 }
3199 {
3200 VGubyte *dst_segments = (VGubyte *)dst->segments.data + (dst->segments.size - segments_count);
3201 const VGubyte *src_segments = (const VGubyte *)src->segments.data;
3202 for (; segments_count != 0; ++dst_segments, ++src_segments, --segments_count) {
3203 VGuint segment = *src_segments;
3204 if (((segment & ~VG_RELATIVE) == VG_HLINE_TO) || ((segment & ~VG_RELATIVE) == VG_VLINE_TO)) {
3205 segment = VG_LINE_TO | (segment & VG_RELATIVE);
3206 }
3207
3208 /*
3209 on the server, we also flip arc types if the determinant of the path
3210 user to surface matrix is negative, but we don't actually care about
3211 the particular arc type on the client
3212 */
3213
3214 *dst_segments = (VGubyte)segment;
3215 }
3216 }
3217 }
3218 platform_mutex_release(&state->shared_state->mutex);
3219
3220 sync_matrix(state, VG_MATRIX_PATH_USER_TO_SURFACE);
3221
3222 RPC_CALL2(vgTransformPath_impl,
3223 thread,
3224 VGTRANSFORMPATH_ID,
3225 RPC_HANDLE(dst_vg_handle),
3226 RPC_HANDLE(src_vg_handle));
3227}
3228
3229VG_API_CALL VGboolean VG_API_ENTRY vgInterpolatePath(
3230 VGPath dst_vg_handle,
3231 VGPath begin_vg_handle,
3232 VGPath end_vg_handle,
3233 VGfloat t) VG_API_EXIT
3234{
3235 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3236 VG_CLIENT_STATE_T *state;
3237 VG_CLIENT_PATH_T *dst;
3238 VG_CLIENT_PATH_T *begin;
3239 VG_CLIENT_PATH_T *end;
3240
3241 t = clean_float(t);
3242
3243 state = VG_GET_CLIENT_STATE(thread);
3244 if (!state) {
3245 return VG_FALSE;
3246 }
3247
3248 platform_mutex_acquire(&state->shared_state->mutex);
3249 dst = (VG_CLIENT_PATH_T *)lookup_object(state, dst_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
3250 begin = (VG_CLIENT_PATH_T *)lookup_object(state, begin_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
3251 end = (VG_CLIENT_PATH_T *)lookup_object(state, end_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
3252 if (dst && begin && end &&
3253 (dst->caps & VG_PATH_CAPABILITY_INTERPOLATE_TO) && (begin->caps & VG_PATH_CAPABILITY_INTERPOLATE_FROM) && (end->caps & VG_PATH_CAPABILITY_INTERPOLATE_FROM)) {
3254 if ((begin->segments.size != end->segments.size) ||
3255 !interpolate_segments_compatible((const VGubyte *)begin->segments.data, (const VGubyte *)end->segments.data, begin->segments.size)) {
3256 /*
3257 incompatible paths. no error -- just return false
3258 */
3259
3260 platform_mutex_release(&state->shared_state->mutex);
3261 return VG_FALSE;
3262 }
3263 if (need_path_segments(dst->caps)) {
3264 VGuint segments_count = begin->segments.size;
3265 if (!khrn_vector_extend(&dst->segments, segments_count)) {
3266 set_error(VG_OUT_OF_MEMORY_ERROR);
3267 platform_mutex_release(&state->shared_state->mutex);
3268 return VG_FALSE;
3269 }
3270 {
3271 VGubyte *dst_segments = (VGubyte *)dst->segments.data + (dst->segments.size - segments_count);
3272 const VGubyte *begin_segments = (const VGubyte *)begin->segments.data;
3273 for (; segments_count != 0; ++dst_segments, ++begin_segments, --segments_count) {
3274 *dst_segments = (VGubyte)normalise_segment(*begin_segments & ~VG_RELATIVE);
3275 }
3276 }
3277 }
3278 }
3279 platform_mutex_release(&state->shared_state->mutex);
3280
3281 RPC_CALL4(vgInterpolatePath_impl,
3282 thread,
3283 VGINTERPOLATEPATH_ID,
3284 RPC_HANDLE(dst_vg_handle),
3285 RPC_HANDLE(begin_vg_handle),
3286 RPC_HANDLE(end_vg_handle),
3287 RPC_FLOAT(t));
3288
3289 return VG_TRUE;
3290}
3291
3292VG_API_CALL VGfloat VG_API_ENTRY vgPathLength(
3293 VGPath vg_handle,
3294 VGint segments_i, VGint segments_count) VG_API_EXIT
3295{
3296 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3297 if (!VG_GET_CLIENT_STATE(thread)) {
3298 return 0.0f;
3299 }
3300
3301 return RPC_FLOAT_RES(RPC_CALL3_RES(vgPathLength_impl,
3302 thread,
3303 VGPATHLENGTH_ID,
3304 RPC_HANDLE(vg_handle),
3305 RPC_INT(segments_i), RPC_INT(segments_count)));
3306}
3307
3308VG_API_CALL void VG_API_ENTRY vgPointAlongPath(
3309 VGPath vg_handle,
3310 VGint segments_i, VGint segments_count,
3311 VGfloat distance,
3312 VGfloat *x, VGfloat *y,
3313 VGfloat *tangent_x, VGfloat *tangent_y) VG_API_EXIT
3314{
3315 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3316 VGfloat values[4];
3317
3318 distance = clean_float(distance);
3319
3320 if (!VG_GET_CLIENT_STATE(thread)) {
3321 return;
3322 }
3323
3324 if ((x && !is_aligned_float(x)) || (y && !is_aligned_float(y)) ||
3325 (tangent_x && !is_aligned_float(tangent_x)) || (tangent_y && !is_aligned_float(tangent_y))) {
3326 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
3327 return;
3328 }
3329
3330 if (RPC_BOOLEAN_RES(RPC_CALL6_OUT_CTRL_RES(vgPointAlongPath_impl,
3331 thread,
3332 VGPOINTALONGPATH_ID,
3333 RPC_HANDLE(vg_handle),
3334 RPC_INT(segments_i), RPC_INT(segments_count),
3335 RPC_FLOAT(distance),
3336 RPC_BITFIELD(((x && y) << 0) | ((tangent_x && tangent_y) << 1)),
3337 values))) {
3338 /*
3339 no error occurred on the server side
3340 */
3341
3342 if (x && y) {
3343 *x = values[0];
3344 *y = values[1];
3345 }
3346 if (tangent_x && tangent_y) {
3347 *tangent_x = values[2];
3348 *tangent_y = values[3];
3349 }
3350 }
3351}
3352
3353VG_API_CALL void VG_API_ENTRY vgPathBounds(
3354 VGPath vg_handle,
3355 VGfloat *min_x, VGfloat *min_y,
3356 VGfloat *width, VGfloat *height) VG_API_EXIT
3357{
3358 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3359 VGfloat values[4];
3360
3361 if (!VG_GET_CLIENT_STATE(thread)) {
3362 return;
3363 }
3364
3365 if (!min_x || !is_aligned_float(min_x) || !min_y || !is_aligned_float(min_y) ||
3366 !width || !is_aligned_float(width) || !height || !is_aligned_float(height)) {
3367 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
3368 return;
3369 }
3370
3371 if (RPC_BOOLEAN_RES(RPC_CALL2_OUT_CTRL_RES(vgPathBounds_impl,
3372 thread,
3373 VGPATHBOUNDS_ID,
3374 RPC_HANDLE(vg_handle),
3375 values))) {
3376 /*
3377 no error occurred on the server side
3378 */
3379
3380 *min_x = values[0];
3381 *min_y = values[1];
3382 *width = values[2];
3383 *height = values[3];
3384 }
3385}
3386
3387VG_API_CALL void VG_API_ENTRY vgPathTransformedBounds(
3388 VGPath vg_handle,
3389 VGfloat *min_x, VGfloat *min_y,
3390 VGfloat *width, VGfloat *height) VG_API_EXIT
3391{
3392 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3393 VGfloat values[4];
3394
3395 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
3396 if (!state) {
3397 return;
3398 }
3399
3400 if (!min_x || !is_aligned_float(min_x) || !min_y || !is_aligned_float(min_y) ||
3401 !width || !is_aligned_float(width) || !height || !is_aligned_float(height)) {
3402 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
3403 return;
3404 }
3405
3406 sync_matrix(state, VG_MATRIX_PATH_USER_TO_SURFACE);
3407
3408 if (RPC_BOOLEAN_RES(RPC_CALL2_OUT_CTRL_RES(vgPathTransformedBounds_impl,
3409 thread,
3410 VGPATHTRANSFORMEDBOUNDS_ID,
3411 RPC_HANDLE(vg_handle),
3412 values))) {
3413 /*
3414 no error occurred on the server side
3415 */
3416
3417 *min_x = values[0];
3418 *min_y = values[1];
3419 *width = values[2];
3420 *height = values[3];
3421 }
3422}
3423
3424VG_API_CALL void VG_API_ENTRY vgDrawPath(
3425 VGPath vg_handle,
3426 VGbitfield paint_modes) VG_API_EXIT
3427{
3428 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3429 VG_CLIENT_STATE_T *state;
3430
3431#ifdef VG_NO_STROKING
3432 paint_modes &= ~VG_STROKE_PATH;
3433 if (!paint_modes) { return; }
3434#endif
3435
3436 state = VG_GET_CLIENT_STATE(thread);
3437 if (!state) {
3438 return;
3439 }
3440
3441 sync_matrix(state, VG_MATRIX_PATH_USER_TO_SURFACE);
3442 if (paint_modes & VG_FILL_PATH) {
3443 sync_matrix(state, VG_MATRIX_FILL_PAINT_TO_USER);
3444 }
3445 if (paint_modes & VG_STROKE_PATH) {
3446 sync_matrix(state, VG_MATRIX_STROKE_PAINT_TO_USER);
3447 }
3448
3449 if (state->render_callback)
3450 state->render_callback();
3451
3452 RPC_CALL2(vgDrawPath_impl,
3453 thread,
3454 VGDRAWPATH_ID,
3455 RPC_HANDLE(vg_handle),
3456 RPC_BITFIELD(paint_modes));
3457}
3458
3459/******************************************************************************
3460api paint
3461******************************************************************************/
3462
3463VG_API_CALL VGPaint VG_API_ENTRY vgCreatePaint(void) VG_API_EXIT
3464{
3465 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3466 VGHandle vg_handle;
3467 VG_CLIENT_PAINT_T *paint;
3468
3469 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
3470 if (!state) {
3471 return VG_INVALID_HANDLE;
3472 }
3473
3474 vg_handle = get_stem(state);
3475 if (vg_handle == VG_INVALID_HANDLE) {
3476 set_error(VG_OUT_OF_MEMORY_ERROR);
3477 return VG_INVALID_HANDLE;
3478 }
3479
3480 paint = paint_alloc();
3481 if (!paint) {
3482 set_error(VG_OUT_OF_MEMORY_ERROR);
3483 destroy_stem(vg_handle);
3484 return VG_INVALID_HANDLE;
3485 }
3486
3487 platform_mutex_acquire(&state->shared_state->mutex);
3488 if (!insert_object(state, vg_handle, paint)) {
3489 set_error(VG_OUT_OF_MEMORY_ERROR);
3490 platform_mutex_release(&state->shared_state->mutex);
3491 paint_free(paint);
3492 destroy_stem(vg_handle);
3493 return VG_INVALID_HANDLE;
3494 }
3495 platform_mutex_release(&state->shared_state->mutex);
3496
3497 RPC_CALL1(vgCreatePaint_impl,
3498 thread,
3499 VGCREATEPAINT_ID,
3500 RPC_HANDLE(vg_handle));
3501
3502 return vg_handle;
3503}
3504
3505VG_API_CALL void VG_API_ENTRY vgDestroyPaint(
3506 VGPaint vg_handle) VG_API_EXIT
3507{
3508 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3509 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
3510 if (!state) {
3511 return;
3512 }
3513
3514 platform_mutex_acquire(&state->shared_state->mutex);
3515 delete_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT);
3516 platform_mutex_release(&state->shared_state->mutex);
3517
3518 RPC_CALL1(vgDestroyPaint_impl,
3519 thread,
3520 VGDESTROYPAINT_ID,
3521 RPC_HANDLE(vg_handle));
3522}
3523
3524VG_API_CALL void VG_API_ENTRY vgSetPaint(
3525 VGPaint vg_handle,
3526 VGbitfield paint_modes) VG_API_EXIT
3527{
3528 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3529 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
3530 if (!state) {
3531 return;
3532 }
3533
3534 if (!is_paint_modes(paint_modes)) {
3535 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
3536 return;
3537 }
3538
3539 platform_mutex_acquire(&state->shared_state->mutex);
3540 if ((vg_handle != VG_INVALID_HANDLE) &&
3541 !lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT)) {
3542 set_error(VG_BAD_HANDLE_ERROR);
3543 platform_mutex_release(&state->shared_state->mutex);
3544 return;
3545 }
3546 platform_mutex_release(&state->shared_state->mutex);
3547
3548 if (((paint_modes & VG_FILL_PATH) && (state->fill_paint != vg_handle)) ||
3549 ((paint_modes & VG_STROKE_PATH) && (state->stroke_paint != vg_handle))) {
3550 if (paint_modes & VG_FILL_PATH) {
3551 state->fill_paint = vg_handle;
3552 }
3553 if (paint_modes & VG_STROKE_PATH) {
3554 state->stroke_paint = vg_handle;
3555 }
3556 RPC_CALL2(vgSetPaint_impl,
3557 thread,
3558 VGSETPAINT_ID,
3559 RPC_HANDLE(vg_handle),
3560 RPC_BITFIELD(paint_modes));
3561 }
3562}
3563
3564VG_API_CALL VGPaint VG_API_ENTRY vgGetPaint(
3565 VGPaintMode paint_mode) VG_API_EXIT
3566{
3567 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3568 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
3569 if (!state) {
3570 return VG_INVALID_HANDLE;
3571 }
3572
3573 if (!is_paint_mode(paint_mode)) {
3574 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
3575 return VG_INVALID_HANDLE;
3576 }
3577
3578 return (paint_mode == VG_FILL_PATH) ? state->fill_paint : state->stroke_paint;
3579}
3580
3581VG_API_CALL void VG_API_ENTRY vgSetColor(
3582 VGPaint vg_handle,
3583 VGuint rgba) VG_API_EXIT
3584{
3585 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3586 VG_CLIENT_STATE_T *state;
3587 VG_CLIENT_PAINT_T *paint;
3588
3589 rgba = khrn_color_rgba_flip(rgba);
3590
3591 state = VG_GET_CLIENT_STATE(thread);
3592 if (!state) {
3593 return;
3594 }
3595
3596 platform_mutex_acquire(&state->shared_state->mutex);
3597 paint = (VG_CLIENT_PAINT_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT);
3598 if (!paint) {
3599 set_error(VG_BAD_HANDLE_ERROR);
3600 platform_mutex_release(&state->shared_state->mutex);
3601 return;
3602 }
3603 if (color_floats_to_rgba_clean(paint->color) != rgba) {
3604 set_parameter_iv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT, VG_PAINT_COLOR, 1, (const VGint *)&rgba);
3605 }
3606 khrn_color_rgba_to_floats(paint->color, rgba);
3607 platform_mutex_release(&state->shared_state->mutex);
3608}
3609
3610VG_API_CALL VGuint VG_API_ENTRY vgGetColor(
3611 VGPaint vg_handle) VG_API_EXIT
3612{
3613 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3614 VG_CLIENT_PAINT_T *paint;
3615 VGuint rgba;
3616
3617 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
3618 if (!state) {
3619 return 0;
3620 }
3621
3622 platform_mutex_acquire(&state->shared_state->mutex);
3623 paint = (VG_CLIENT_PAINT_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT);
3624 if (!paint) {
3625 set_error(VG_BAD_HANDLE_ERROR);
3626 platform_mutex_release(&state->shared_state->mutex);
3627 return 0;
3628 }
3629 rgba = color_floats_to_rgba_clean(paint->color);
3630 platform_mutex_release(&state->shared_state->mutex);
3631 return khrn_color_rgba_flip(rgba);
3632}
3633
3634VG_API_CALL void VG_API_ENTRY vgPaintPattern(
3635 VGPaint vg_handle,
3636 VGImage pattern_vg_handle) VG_API_EXIT
3637{
3638 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3639 VG_CLIENT_PAINT_T *paint;
3640 VG_CLIENT_IMAGE_T *pattern;
3641
3642 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
3643 if (!state) {
3644 return;
3645 }
3646
3647 platform_mutex_acquire(&state->shared_state->mutex);
3648 paint = (VG_CLIENT_PAINT_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT);
3649 pattern = (pattern_vg_handle == VG_INVALID_HANDLE) ? NULL : (VG_CLIENT_IMAGE_T *)lookup_object(state, pattern_vg_handle, VG_CLIENT_OBJECT_TYPE_IMAGE);
3650 if (!paint || ((pattern_vg_handle != VG_INVALID_HANDLE) && !pattern)) {
3651 set_error(VG_BAD_HANDLE_ERROR);
3652 platform_mutex_release(&state->shared_state->mutex);
3653 return;
3654 }
3655 if (paint->pattern != pattern_vg_handle) {
3656 paint->pattern = pattern_vg_handle;
3657#if EGL_BRCM_global_image && EGL_KHR_image
3658 if (pattern && (pattern->global_image_id[0] || pattern->global_image_id[1])) {
3659 platform_acquire_global_image(pattern->global_image_id[0], pattern->global_image_id[1]);
3660 }
3661 if (paint->pattern_global_image_id[0] || paint->pattern_global_image_id[1]) {
3662 platform_release_global_image(paint->pattern_global_image_id[0], paint->pattern_global_image_id[1]);
3663 }
3664 paint->pattern_global_image_id[0] = pattern ? pattern->global_image_id[0] : 0;
3665 paint->pattern_global_image_id[1] = pattern ? pattern->global_image_id[1] : 0;
3666#endif
3667 RPC_CALL2(vgPaintPattern_impl,
3668 thread,
3669 VGPAINTPATTERN_ID,
3670 RPC_HANDLE(vg_handle),
3671 RPC_HANDLE(pattern_vg_handle));
3672 }
3673 platform_mutex_release(&state->shared_state->mutex);
3674}
3675
3676/******************************************************************************
3677api image
3678******************************************************************************/
3679
3680VG_API_CALL VGImage VG_API_ENTRY vgCreateImage(
3681 VGImageFormat format,
3682 VGint width, VGint height,
3683 VGbitfield allowed_quality) VG_API_EXIT
3684{
3685 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3686 VGHandle vg_handle;
3687 VG_CLIENT_IMAGE_T *image;
3688
3689 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
3690 if (!state) {
3691 return VG_INVALID_HANDLE;
3692 }
3693
3694 if (!is_allowed_quality(allowed_quality) ||
3695 (width <= 0) || (height <= 0) ||
3696 (width > VG_CONFIG_MAX_IMAGE_WIDTH) || (height > VG_CONFIG_MAX_IMAGE_HEIGHT) ||
3697 ((width * height) > VG_CONFIG_MAX_IMAGE_PIXELS)) { /* todo: VG_CONFIG_MAX_IMAGE_BYTES */
3698 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
3699 return VG_INVALID_HANDLE;
3700 }
3701 if (!is_image_format(format)) {
3702 set_error(VG_UNSUPPORTED_IMAGE_FORMAT_ERROR);
3703 return VG_INVALID_HANDLE;
3704 }
3705
3706 vg_handle = get_stem(state);
3707 if (vg_handle == VG_INVALID_HANDLE) {
3708 set_error(VG_OUT_OF_MEMORY_ERROR);
3709 return VG_INVALID_HANDLE;
3710 }
3711
3712 image = image_alloc(format, width, height
3713#if EGL_BRCM_global_image && EGL_KHR_image
3714 , 0, 0
3715#endif
3716 );
3717 if (!image) {
3718 set_error(VG_OUT_OF_MEMORY_ERROR);
3719 destroy_stem(vg_handle);
3720 return VG_INVALID_HANDLE;
3721 }
3722
3723 platform_mutex_acquire(&state->shared_state->mutex);
3724 if (!insert_object(state, vg_handle, image)) {
3725 set_error(VG_OUT_OF_MEMORY_ERROR);
3726 platform_mutex_release(&state->shared_state->mutex);
3727 image_free(image);
3728 destroy_stem(vg_handle);
3729 return VG_INVALID_HANDLE;
3730 }
3731 platform_mutex_release(&state->shared_state->mutex);
3732
3733 RPC_CALL5(vgCreateImage_impl,
3734 thread,
3735 VGCREATEIMAGE_ID,
3736 RPC_HANDLE(vg_handle),
3737 RPC_ENUM(format),
3738 RPC_INT(width), RPC_INT(height),
3739 RPC_BITFIELD(allowed_quality));
3740
3741 return vg_handle;
3742}
3743
3744VG_API_CALL void VG_API_ENTRY vgDestroyImage(
3745 VGImage vg_handle) VG_API_EXIT
3746{
3747 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3748 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
3749 if (!state) {
3750 return;
3751 }
3752
3753 platform_mutex_acquire(&state->shared_state->mutex);
3754 delete_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_IMAGE);
3755 platform_mutex_release(&state->shared_state->mutex);
3756
3757 RPC_CALL1(vgDestroyImage_impl,
3758 thread,
3759 VGDESTROYIMAGE_ID,
3760 RPC_HANDLE(vg_handle));
3761}
3762
3763VG_API_CALL void VG_API_ENTRY vgClearImage(
3764 VGImage vg_handle,
3765 VGint x, VGint y,
3766 VGint width, VGint height) VG_API_EXIT
3767{
3768 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3769 if (!VG_GET_CLIENT_STATE(thread)) {
3770 return;
3771 }
3772
3773 RPC_CALL5(vgClearImage_impl,
3774 thread,
3775 VGCLEARIMAGE_ID,
3776 RPC_HANDLE(vg_handle),
3777 RPC_INT(x), RPC_INT(y),
3778 RPC_INT(width), RPC_INT(height));
3779}
3780
3781VG_API_CALL void VG_API_ENTRY vgImageSubData(
3782 VGImage vg_handle,
3783 const void *data, VGint data_stride,
3784 VGImageFormat data_format,
3785 VGint dst_x, VGint dst_y,
3786 VGint width, VGint height) VG_API_EXIT
3787{
3788 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3789 VG_CLIENT_IMAGE_T *dst;
3790 VGint dst_width;
3791 VGint dst_height;
3792 VGint src_x = 0, src_y = 0;
3793
3794 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
3795 if (!state) {
3796 return;
3797 }
3798
3799 if (!is_image_format(data_format)) {
3800 set_error(VG_UNSUPPORTED_IMAGE_FORMAT_ERROR);
3801 return;
3802 }
3803
3804 if (!data || !is_aligned_image_format(data, data_format) ||
3805 ((height != 1) && !is_aligned_image_format((void *)(uintptr_t)data_stride, data_format)) ||
3806 (width <= 0) || (height <= 0)) {
3807 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
3808 return;
3809 }
3810
3811 platform_mutex_acquire(&state->shared_state->mutex);
3812
3813 dst = (VG_CLIENT_IMAGE_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_IMAGE);
3814 if (!dst) {
3815 set_error(VG_BAD_HANDLE_ERROR);
3816 platform_mutex_release(&state->shared_state->mutex);
3817 return;
3818 }
3819
3820 dst_width = dst->width;
3821 dst_height = dst->height;
3822
3823 platform_mutex_release(&state->shared_state->mutex);
3824
3825 khrn_clip_rect2(
3826 &dst_x, &dst_y, &src_x, &src_y, &width, &height,
3827 0, 0, dst_width, dst_height,
3828 0, 0, width, height);
3829
3830 if (width <= 0 || height <= 0) {
3831 return;
3832 }
3833
3834 data = (const VGubyte *)data + (src_y * data_stride);
3835
3836 #ifdef RPC_DIRECT
3837 RPC_CALL11(vgImageSubData_impl, thread, no_id,
3838 vg_handle,
3839 dst_width, dst_height,
3840 data, data_stride,
3841 data_format,
3842 src_x,
3843 dst_x, dst_y,
3844 width, height);
3845 #else
3846 {
3847 VGuint log2_bpp = get_log2_image_format_bpp(data_format);
3848 VGuint line_size;
3849 VGuint chunk_height_max;
3850
3851 data = (const VGubyte *)data + ((src_x << log2_bpp) >> 3);
3852 src_x = ((src_x << log2_bpp) & 0x7) >> log2_bpp;
3853
3854 line_size = (((src_x + width) << log2_bpp) + 0x7) >> 3;
3855
3856 /*
3857 split into KHDISPATCH_WORKSPACE_SIZE chunks (or thereabouts)
3858 */
3859
3860 chunk_height_max = (line_size == 0) ? height : (KHDISPATCH_WORKSPACE_SIZE / line_size);
3861 vcos_assert((height == 0) || (chunk_height_max != 0));
3862
3863 while (height != 0) {
3864 VGint chunk_height = _min(height, chunk_height_max);
3865
3866 uint32_t message[] = {
3867 VGIMAGESUBDATA_ID,
3868 RPC_HANDLE(vg_handle),
3869 RPC_INT(dst_width), RPC_INT(dst_height),
3870 RPC_UINT(line_size),
3871 RPC_ENUM(data_format),
3872 RPC_INT(src_x),
3873 RPC_INT(dst_x), RPC_INT(dst_y),
3874 RPC_INT(width), RPC_INT(chunk_height) };
3875
3876 rpc_begin(thread);
3877 rpc_send_ctrl_begin(thread, sizeof(message));
3878 rpc_send_ctrl_write(thread, message, sizeof(message));
3879 rpc_send_ctrl_end(thread);
3880 rpc_send_bulk_gather(thread, data, line_size, data_stride, chunk_height);
3881 data = (const VGubyte *)data + (chunk_height * data_stride);
3882 rpc_end(thread);
3883
3884 height -= chunk_height;
3885 dst_y += chunk_height;
3886 }
3887 }
3888 #endif
3889}
3890
3891VG_API_CALL void VG_API_ENTRY vgGetImageSubData(
3892 VGImage vg_handle,
3893 void *data, VGint data_stride,
3894 VGImageFormat data_format,
3895 VGint src_x, VGint src_y,
3896 VGint width, VGint height) VG_API_EXIT
3897{
3898 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
3899 VG_CLIENT_IMAGE_T *src;
3900 VGint src_width;
3901 VGint src_height;
3902 VGint dst_x = 0, dst_y = 0;
3903
3904 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
3905 if (!state) {
3906 return;
3907 }
3908
3909 if (!is_image_format(data_format)) {
3910 set_error(VG_UNSUPPORTED_IMAGE_FORMAT_ERROR);
3911 return;
3912 }
3913
3914 if (!data || !is_aligned_image_format(data, data_format) ||
3915 ((height != 1) && !is_aligned_image_format((void *)(uintptr_t)data_stride, data_format)) ||
3916 (width <= 0) || (height <= 0)) {
3917 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
3918 return;
3919 }
3920
3921 platform_mutex_acquire(&state->shared_state->mutex);
3922
3923 src = (VG_CLIENT_IMAGE_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_IMAGE);
3924 if (!src) {
3925 set_error(VG_BAD_HANDLE_ERROR);
3926 platform_mutex_release(&state->shared_state->mutex);
3927 return;
3928 }
3929
3930 src_width = src->width;
3931 src_height = src->height;
3932
3933 platform_mutex_release(&state->shared_state->mutex);
3934
3935 khrn_clip_rect2(
3936 &dst_x, &dst_y, &src_x, &src_y, &width, &height,
3937 0, 0, width, height,
3938 0, 0, src_width, src_height);
3939
3940 if (width <= 0 || height <= 0) {
3941 return;
3942 }
3943
3944 data = (VGubyte *)data + (dst_y * data_stride);
3945
3946 #ifdef RPC_DIRECT
3947 RPC_CALL11(vgGetImageSubData_impl, thread, no_id,
3948 vg_handle,
3949 src_width, src_height,
3950 data, data_stride,
3951 data_format,
3952 dst_x,
3953 src_x, src_y,
3954 width, height);
3955 #else
3956 {
3957 VGuint log2_bpp = get_log2_image_format_bpp(data_format);
3958 VGuint line_size;
3959 VGuint first_mask;
3960 VGuint last_mask;
3961 VGuint chunk_height_max;
3962
3963 data = (VGubyte *)data + ((dst_x << log2_bpp) >> 3);
3964 dst_x = ((dst_x << log2_bpp) & 0x7) >> log2_bpp;
3965
3966 line_size = (((dst_x + width) << log2_bpp) + 0x7) >> 3;
3967 first_mask = (1 << (dst_x << log2_bpp)) - 1;
3968 last_mask = ~((2 << ((((dst_x + width) << log2_bpp) - 1) & 0x7)) - 1) & 0xff;
3969
3970 /*
3971 split into KHDISPATCH_WORKSPACE_SIZE chunks (or thereabouts)
3972 */
3973
3974 chunk_height_max = (line_size == 0) ? height : (KHDISPATCH_WORKSPACE_SIZE / line_size);
3975 vcos_assert((height == 0) || (chunk_height_max != 0));
3976
3977 while (height != 0) {
3978 VGint chunk_height = _min(height, chunk_height_max);
3979
3980 uint32_t message[] = {
3981 VGGETIMAGESUBDATA_ID,
3982 RPC_HANDLE(vg_handle),
3983 RPC_INT(src_width), RPC_INT(src_height),
3984 RPC_UINT(line_size),
3985 RPC_ENUM(data_format),
3986 RPC_INT(dst_x),
3987 RPC_INT(src_x), RPC_INT(src_y),
3988 RPC_INT(width), RPC_INT(chunk_height) };
3989
3990 rpc_begin(thread);
3991 rpc_send_ctrl_begin(thread, sizeof(message));
3992 rpc_send_ctrl_write(thread, message, sizeof(message));
3993 rpc_send_ctrl_end(thread);
3994 {
3995 uint32_t len[] = { 0, data_stride, chunk_height, first_mask, last_mask };
3996 rpc_recv(thread, data, len, (RPC_RECV_FLAG_T)(RPC_RECV_FLAG_BULK | RPC_RECV_FLAG_BULK_SCATTER | RPC_RECV_FLAG_LEN));
3997 data = (VGubyte *)data + (chunk_height * data_stride);
3998 if (len[0] == 0) { //The command has failed so no data sent back
3999 rpc_end(thread);
4000 break;
4001 }
4002 vcos_assert(len[0] == line_size);
4003 }
4004 rpc_end(thread);
4005 height -= chunk_height;
4006 src_y += chunk_height;
4007 }
4008 }
4009 #endif
4010}
4011
4012VG_API_CALL VGImage VG_API_ENTRY vgChildImage(
4013 VGImage parent_vg_handle,
4014 VGint x, VGint y,
4015 VGint width, VGint height) VG_API_EXIT
4016{
4017 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4018 VG_CLIENT_IMAGE_T *parent;
4019 VGImageFormat format;
4020 VGint parent_width;
4021 VGint parent_height;
4022#if EGL_BRCM_global_image && EGL_KHR_image
4023 VGuint parent_global_image_id_0, parent_global_image_id_1;
4024#endif
4025 VGHandle vg_handle;
4026 VG_CLIENT_IMAGE_T *image;
4027
4028 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
4029 if (!state) {
4030 return VG_INVALID_HANDLE;
4031 }
4032
4033 platform_mutex_acquire(&state->shared_state->mutex);
4034
4035 parent = (VG_CLIENT_IMAGE_T *)lookup_object(state, parent_vg_handle, VG_CLIENT_OBJECT_TYPE_IMAGE);
4036 if (!parent) {
4037 set_error(VG_BAD_HANDLE_ERROR);
4038 platform_mutex_release(&state->shared_state->mutex);
4039 return VG_INVALID_HANDLE;
4040 }
4041
4042 format = parent->format;
4043 parent_width = parent->width;
4044 parent_height = parent->height;
4045#if EGL_BRCM_global_image && EGL_KHR_image
4046 parent_global_image_id_0 = parent->global_image_id[0];
4047 parent_global_image_id_1 = parent->global_image_id[1];
4048#endif
4049
4050 platform_mutex_release(&state->shared_state->mutex); /* mustn't hold this mutex when calling get_stem, so release now and acquire again later */
4051
4052 if ((x < 0) || (y < 0) || (width <= 0) || (height <= 0) ||
4053 /* x + width / y + height will fit in VGuint (may not fit in VGint) */
4054 (((VGuint)(x + width)) > (VGuint)parent_width) ||
4055 (((VGuint)(y + height)) > (VGuint)parent_height)) {
4056 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
4057 return VG_INVALID_HANDLE;
4058 }
4059
4060 vg_handle = get_stem(state);
4061 if (vg_handle == VG_INVALID_HANDLE) {
4062 set_error(VG_OUT_OF_MEMORY_ERROR);
4063 return VG_INVALID_HANDLE;
4064 }
4065
4066 image = image_alloc(format, width, height
4067#if EGL_BRCM_global_image && EGL_KHR_image
4068 , parent_global_image_id_0, parent_global_image_id_1
4069#endif
4070 );
4071 if (!image) {
4072 set_error(VG_OUT_OF_MEMORY_ERROR);
4073 destroy_stem(vg_handle);
4074 return VG_INVALID_HANDLE;
4075 }
4076
4077 platform_mutex_acquire(&state->shared_state->mutex);
4078 if (!insert_object(state, vg_handle, image)) {
4079 set_error(VG_OUT_OF_MEMORY_ERROR);
4080 platform_mutex_release(&state->shared_state->mutex);
4081 image_free(image);
4082 destroy_stem(vg_handle);
4083 return VG_INVALID_HANDLE;
4084 }
4085 platform_mutex_release(&state->shared_state->mutex);
4086
4087 RPC_CALL8(vgChildImage_impl,
4088 thread,
4089 VGCHILDIMAGE_ID,
4090 RPC_HANDLE(vg_handle),
4091 RPC_HANDLE(parent_vg_handle),
4092 RPC_INT(parent_width), RPC_INT(parent_height),
4093 RPC_INT(x), RPC_INT(y),
4094 RPC_INT(width), RPC_INT(height));
4095
4096 return vg_handle;
4097}
4098
4099VG_API_CALL VGImage VG_API_ENTRY vgGetParent(
4100 VGImage vg_handle) VG_API_EXIT
4101{
4102 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4103 if (!VG_GET_CLIENT_STATE(thread)) {
4104 return VG_INVALID_HANDLE;
4105 }
4106
4107 return RPC_HANDLE_RES(RPC_CALL1_RES(vgGetParent_impl,
4108 thread,
4109 VGGETPARENT_ID,
4110 RPC_HANDLE(vg_handle)));
4111}
4112
4113VG_API_CALL void VG_API_ENTRY vgCopyImage(
4114 VGImage dst_vg_handle, VGint dst_x, VGint dst_y,
4115 VGImage src_vg_handle, VGint src_x, VGint src_y,
4116 VGint width, VGint height,
4117 VGboolean dither) VG_API_EXIT
4118{
4119 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4120 if (!VG_GET_CLIENT_STATE(thread)) {
4121 return;
4122 }
4123
4124 RPC_CALL9(vgCopyImage_impl,
4125 thread,
4126 VGCOPYIMAGE_ID,
4127 RPC_HANDLE(dst_vg_handle), RPC_INT(dst_x), RPC_INT(dst_y),
4128 RPC_HANDLE(src_vg_handle), RPC_INT(src_x), RPC_INT(src_y),
4129 RPC_INT(width), RPC_INT(height),
4130 RPC_BOOLEAN(clean_boolean(dither)));
4131}
4132
4133VG_API_CALL void VG_API_ENTRY vgDrawImage(
4134 VGImage vg_handle) VG_API_EXIT
4135{
4136 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4137 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
4138 if (!state) {
4139 return;
4140 }
4141
4142 sync_matrix(state, VG_MATRIX_IMAGE_USER_TO_SURFACE);
4143 sync_matrix(state, VG_MATRIX_FILL_PAINT_TO_USER);
4144
4145 if (state->render_callback)
4146 state->render_callback();
4147
4148 RPC_CALL1(vgDrawImage_impl,
4149 thread,
4150 VGDRAWIMAGE_ID,
4151 RPC_HANDLE(vg_handle));
4152}
4153
4154/******************************************************************************
4155api framebuffer
4156******************************************************************************/
4157
4158VG_API_CALL void VG_API_ENTRY vgSetPixels(
4159 VGint dst_x, VGint dst_y,
4160 VGImage src_vg_handle, VGint src_x, VGint src_y,
4161 VGint width, VGint height) VG_API_EXIT
4162{
4163 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4164 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
4165 if (!state) {
4166 return;
4167 }
4168
4169 if (state->render_callback)
4170 state->render_callback();
4171
4172 RPC_CALL7(vgSetPixels_impl,
4173 thread,
4174 VGSETPIXELS_ID,
4175 RPC_INT(dst_x), RPC_INT(dst_y),
4176 RPC_HANDLE(src_vg_handle), RPC_INT(src_x), RPC_INT(src_y),
4177 RPC_INT(width), RPC_INT(height));
4178}
4179
4180VG_API_CALL void VG_API_ENTRY vgWritePixels(
4181 const void *data, VGint data_stride,
4182 VGImageFormat data_format,
4183 VGint dst_x, VGint dst_y,
4184 VGint width, VGint height) VG_API_EXIT
4185{
4186 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4187 EGL_SURFACE_T *frame;
4188 VGint src_x = 0, src_y = 0;
4189
4190 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
4191 if (!state) {
4192 return;
4193 }
4194
4195 if (!is_image_format(data_format)) {
4196 set_error(VG_UNSUPPORTED_IMAGE_FORMAT_ERROR);
4197 return;
4198 }
4199
4200 if (!data || !is_aligned_image_format(data, data_format) ||
4201 ((height != 1) && !is_aligned_image_format((void *)(uintptr_t)data_stride, data_format)) ||
4202 (width <= 0) || (height <= 0)) {
4203 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
4204 return;
4205 }
4206
4207 frame = CLIENT_GET_THREAD_STATE()->openvg.draw;
4208
4209 khrn_clip_rect2(
4210 &dst_x, &dst_y, &src_x, &src_y, &width, &height,
4211 0, 0, frame->width, frame->height,
4212 0, 0, width, height);
4213
4214 if (width <= 0 || height <= 0) {
4215 return;
4216 }
4217
4218 data = (const VGubyte *)data + (src_y * data_stride);
4219
4220 if (state->render_callback)
4221 state->render_callback();
4222
4223 #ifdef RPC_DIRECT
4224 RPC_CALL8(vgWritePixels_impl, thread, no_id,
4225 data, data_stride,
4226 data_format,
4227 src_x,
4228 dst_x, dst_y,
4229 width, height);
4230 #else
4231 {
4232 VGuint log2_bpp = get_log2_image_format_bpp(data_format);
4233 VGuint line_size;
4234 VGuint chunk_height_max;
4235
4236 data = (const VGubyte *)data + ((src_x << log2_bpp) >> 3);
4237 src_x = ((src_x << log2_bpp) & 0x7) >> log2_bpp;
4238
4239 line_size = (((src_x + width) << log2_bpp) + 0x7) >> 3;
4240
4241 /*
4242 split into KHDISPATCH_WORKSPACE_SIZE chunks (or thereabouts)
4243 */
4244
4245 chunk_height_max = (line_size == 0) ? height : (KHDISPATCH_WORKSPACE_SIZE / line_size);
4246 vcos_assert((height == 0) || (chunk_height_max != 0));
4247
4248 while (height != 0) {
4249 VGint chunk_height = _min(height, chunk_height_max);
4250
4251 uint32_t message[] = {
4252 VGWRITEPIXELS_ID,
4253 RPC_UINT(line_size),
4254 RPC_ENUM(data_format),
4255 RPC_INT(src_x),
4256 RPC_INT(dst_x), RPC_INT(dst_y),
4257 RPC_INT(width), RPC_INT(chunk_height) };
4258
4259 rpc_begin(thread);
4260 rpc_send_ctrl_begin(thread, sizeof(message));
4261 rpc_send_ctrl_write(thread, message, sizeof(message));
4262 rpc_send_ctrl_end(thread);
4263 rpc_send_bulk_gather(thread, data, line_size, data_stride, chunk_height);
4264 data = (const VGubyte *)data + (chunk_height * data_stride);
4265 rpc_end(thread);
4266
4267 height -= chunk_height;
4268 dst_y += chunk_height;
4269 }
4270 }
4271 #endif
4272}
4273
4274VG_API_CALL void VG_API_ENTRY vgGetPixels(
4275 VGImage dst_vg_handle, VGint dst_x, VGint dst_y,
4276 VGint src_x, VGint src_y,
4277 VGint width, VGint height) VG_API_EXIT
4278{
4279 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4280 if (!VG_GET_CLIENT_STATE(thread)) {
4281 return;
4282 }
4283
4284 RPC_CALL7(vgGetPixels_impl,
4285 thread,
4286 VGGETPIXELS_ID,
4287 RPC_HANDLE(dst_vg_handle), RPC_INT(dst_x), RPC_INT(dst_y),
4288 RPC_INT(src_x), RPC_INT(src_y),
4289 RPC_INT(width), RPC_INT(height));
4290}
4291
4292VG_API_CALL void VG_API_ENTRY vgReadPixels(
4293 void *data, VGint data_stride,
4294 VGImageFormat data_format,
4295 VGint src_x, VGint src_y,
4296 VGint width, VGint height) VG_API_EXIT
4297{
4298 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4299 EGL_SURFACE_T *frame;
4300 VGint dst_x = 0, dst_y = 0;
4301
4302 if (!VG_GET_CLIENT_STATE(thread)) {
4303 return;
4304 }
4305
4306 if (!is_image_format(data_format)) {
4307 set_error(VG_UNSUPPORTED_IMAGE_FORMAT_ERROR);
4308 return;
4309 }
4310
4311 if (!data || !is_aligned_image_format(data, data_format) ||
4312 ((height != 1) && !is_aligned_image_format((void *)(uintptr_t)data_stride, data_format)) ||
4313 (width <= 0) || (height <= 0)) {
4314 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
4315 return;
4316 }
4317
4318 frame = CLIENT_GET_THREAD_STATE()->openvg.draw;
4319
4320 khrn_clip_rect2(
4321 &dst_x, &dst_y, &src_x, &src_y, &width, &height,
4322 0, 0, width, height,
4323 0, 0, frame->width, frame->height);
4324
4325 if (width <= 0 || height <= 0) {
4326 return;
4327 }
4328
4329 data = (VGubyte *)data + (dst_y * data_stride);
4330
4331 #ifdef RPC_DIRECT
4332 RPC_CALL8(vgReadPixels_impl, thread, no_id,
4333 data, data_stride,
4334 data_format,
4335 dst_x,
4336 src_x, src_y,
4337 width, height);
4338 #else
4339 {
4340 VGuint log2_bpp = get_log2_image_format_bpp(data_format);
4341 VGuint line_size;
4342 VGuint first_mask;
4343 VGuint last_mask;
4344 VGuint chunk_height_max;
4345
4346 data = (VGubyte *)data + ((dst_x << log2_bpp) >> 3);
4347 dst_x = ((dst_x << log2_bpp) & 0x7) >> log2_bpp;
4348
4349 line_size = (((dst_x + width) << log2_bpp) + 0x7) >> 3;
4350 first_mask = (1 << (dst_x << log2_bpp)) - 1;
4351 last_mask = ~((2 << ((((dst_x + width) << log2_bpp) - 1) & 0x7)) - 1) & 0xff;
4352
4353 /*
4354 split into KHDISPATCH_WORKSPACE_SIZE chunks (or thereabouts)
4355 */
4356
4357 chunk_height_max = (line_size == 0) ? height : (KHDISPATCH_WORKSPACE_SIZE / line_size);
4358 vcos_assert((height == 0) || (chunk_height_max != 0));
4359
4360 while (height != 0) {
4361 VGint chunk_height = _min(height, chunk_height_max);
4362
4363 uint32_t message[] = {
4364 VGREADPIXELS_ID,
4365 RPC_UINT(line_size),
4366 RPC_ENUM(data_format),
4367 RPC_INT(dst_x),
4368 RPC_INT(src_x), RPC_INT(src_y),
4369 RPC_INT(width), RPC_INT(chunk_height) };
4370
4371#ifndef __SYMBIAN32__
4372 rpc_begin(thread);
4373#endif
4374 rpc_send_ctrl_begin(thread, sizeof(message));
4375 rpc_send_ctrl_write(thread, message, sizeof(message));
4376 rpc_send_ctrl_end(thread);
4377 {
4378 uint32_t len[] = { line_size, data_stride, chunk_height, first_mask, last_mask };
4379 rpc_recv(thread, data, len, (RPC_RECV_FLAG_T)(RPC_RECV_FLAG_BULK | RPC_RECV_FLAG_BULK_SCATTER));
4380 data = (VGubyte *)data + (chunk_height * data_stride);
4381 }
4382#ifndef __SYMBIAN32__
4383 rpc_end(thread);
4384#endif
4385 height -= chunk_height;
4386 src_y += chunk_height;
4387 }
4388 }
4389 #endif
4390}
4391
4392VG_API_CALL void VG_API_ENTRY vgCopyPixels(
4393 VGint dst_x, VGint dst_y,
4394 VGint src_x, VGint src_y,
4395 VGint width, VGint height) VG_API_EXIT
4396{
4397 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4398 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
4399 if (!state) {
4400 return;
4401 }
4402
4403 if (state->render_callback)
4404 state->render_callback();
4405
4406 RPC_CALL6(vgCopyPixels_impl,
4407 thread,
4408 VGCOPYPIXELS_ID,
4409 RPC_INT(dst_x), RPC_INT(dst_y),
4410 RPC_INT(src_x), RPC_INT(src_y),
4411 RPC_INT(width), RPC_INT(height));
4412}
4413
4414/******************************************************************************
4415api fonts
4416******************************************************************************/
4417
4418VG_API_CALL VGFont VG_API_ENTRY vgCreateFont(
4419 VGint glyphs_capacity) VG_API_EXIT
4420{
4421 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4422 VGHandle vg_handle;
4423 VG_CLIENT_FONT_T *font;
4424
4425 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
4426 if (!state) {
4427 return VG_INVALID_HANDLE;
4428 }
4429
4430 if (glyphs_capacity < 0) {
4431 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
4432 return VG_INVALID_HANDLE;
4433 }
4434
4435 vg_handle = get_stem(state);
4436 if (vg_handle == VG_INVALID_HANDLE) {
4437 set_error(VG_OUT_OF_MEMORY_ERROR);
4438 return VG_INVALID_HANDLE;
4439 }
4440
4441 font = font_alloc();
4442 if (!font) {
4443 set_error(VG_OUT_OF_MEMORY_ERROR);
4444 destroy_stem(vg_handle);
4445 return VG_INVALID_HANDLE;
4446 }
4447
4448 platform_mutex_acquire(&state->shared_state->mutex);
4449 if (!insert_object(state, vg_handle, font)) {
4450 set_error(VG_OUT_OF_MEMORY_ERROR);
4451 platform_mutex_release(&state->shared_state->mutex);
4452 font_free(font);
4453 destroy_stem(vg_handle);
4454 return VG_INVALID_HANDLE;
4455 }
4456 platform_mutex_release(&state->shared_state->mutex);
4457
4458 RPC_CALL2(vgCreateFont_impl,
4459 thread,
4460 VGCREATEFONT_ID,
4461 RPC_HANDLE(vg_handle),
4462 RPC_INT(glyphs_capacity));
4463
4464 return vg_handle;
4465}
4466
4467VG_API_CALL void VG_API_ENTRY vgDestroyFont(
4468 VGFont vg_handle) VG_API_EXIT
4469{
4470 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4471 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
4472 if (!state) {
4473 return;
4474 }
4475
4476 platform_mutex_acquire(&state->shared_state->mutex);
4477 delete_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_FONT);
4478 platform_mutex_release(&state->shared_state->mutex);
4479
4480 RPC_CALL1(vgDestroyFont_impl,
4481 thread,
4482 VGDESTROYFONT_ID,
4483 RPC_HANDLE(vg_handle));
4484}
4485
4486VG_API_CALL void VG_API_ENTRY vgSetGlyphToPath(
4487 VGFont vg_handle,
4488 VGuint glyph_id,
4489 VGPath path_vg_handle,
4490 VGboolean is_hinted,
4491 VGfloat glyph_origin[2],
4492 VGfloat escapement[2]) VG_API_EXIT
4493{
4494 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4495#if EGL_BRCM_global_image && EGL_KHR_image
4496 VG_CLIENT_FONT_T *font;
4497#endif
4498
4499 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
4500 if (!state) {
4501 return;
4502 }
4503
4504 if (!glyph_origin || !is_aligned_float(glyph_origin) ||
4505 !escapement || !is_aligned_float(escapement)) {
4506 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
4507 return;
4508 }
4509
4510#if EGL_BRCM_global_image && EGL_KHR_image
4511 platform_mutex_acquire(&state->shared_state->mutex);
4512 font = (VG_CLIENT_FONT_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_FONT);
4513 if (font && ((path_vg_handle == VG_INVALID_HANDLE) || lookup_object(state, path_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH))) {
4514 khrn_global_image_map_delete(&font->glyph_global_images, glyph_id);
4515 }
4516 platform_mutex_release(&state->shared_state->mutex);
4517#endif
4518
4519 RPC_CALL8(vgSetGlyphToPath_impl,
4520 thread,
4521 VGSETGLYPHTOPATH_ID,
4522 RPC_HANDLE(vg_handle),
4523 RPC_UINT(glyph_id),
4524 RPC_HANDLE(path_vg_handle),
4525 RPC_BOOLEAN(clean_boolean(is_hinted)),
4526 RPC_FLOAT(clean_float(glyph_origin[0])), RPC_FLOAT(clean_float(glyph_origin[1])),
4527 RPC_FLOAT(clean_float(escapement[0])), RPC_FLOAT(clean_float(escapement[1])));
4528}
4529
4530VG_API_CALL void VG_API_ENTRY vgSetGlyphToImage(
4531 VGFont vg_handle,
4532 VGuint glyph_id,
4533 VGImage image_vg_handle,
4534 VGfloat glyph_origin[2],
4535 VGfloat escapement[2]) VG_API_EXIT
4536{
4537 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4538#if EGL_BRCM_global_image && EGL_KHR_image
4539 VG_CLIENT_FONT_T *font;
4540 VG_CLIENT_IMAGE_T *image;
4541#endif
4542
4543 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
4544 if (!state) {
4545 return;
4546 }
4547
4548 if (!glyph_origin || !is_aligned_float(glyph_origin) ||
4549 !escapement || !is_aligned_float(escapement)) {
4550 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
4551 return;
4552 }
4553
4554#if EGL_BRCM_global_image && EGL_KHR_image
4555 platform_mutex_acquire(&state->shared_state->mutex);
4556 font = (VG_CLIENT_FONT_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_FONT);
4557 image = (image_vg_handle == VG_INVALID_HANDLE) ? NULL : (VG_CLIENT_IMAGE_T *)lookup_object(state, image_vg_handle, VG_CLIENT_OBJECT_TYPE_IMAGE);
4558 if (font && ((image_vg_handle == VG_INVALID_HANDLE) || image)) {
4559 if (image && (image->global_image_id[0] || image->global_image_id[1])) {
4560 if (!khrn_global_image_map_insert(&font->glyph_global_images, glyph_id,
4561 image->global_image_id[0] | ((uint64_t)image->global_image_id[1] << 32))) {
4562 set_error(VG_OUT_OF_MEMORY_ERROR);
4563 platform_mutex_release(&state->shared_state->mutex);
4564 return;
4565 }
4566 } else {
4567 khrn_global_image_map_delete(&font->glyph_global_images, glyph_id);
4568 }
4569 }
4570 platform_mutex_release(&state->shared_state->mutex);
4571#endif
4572
4573 RPC_CALL7(vgSetGlyphToImage_impl,
4574 thread,
4575 VGSETGLYPHTOIMAGE_ID,
4576 RPC_HANDLE(vg_handle),
4577 RPC_UINT(glyph_id),
4578 RPC_HANDLE(image_vg_handle),
4579 RPC_FLOAT(clean_float(glyph_origin[0])), RPC_FLOAT(clean_float(glyph_origin[1])),
4580 RPC_FLOAT(clean_float(escapement[0])), RPC_FLOAT(clean_float(escapement[1])));
4581}
4582
4583VG_API_CALL void VG_API_ENTRY vgClearGlyph(
4584 VGFont vg_handle,
4585 VGuint glyph_id) VG_API_EXIT
4586{
4587 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4588#if EGL_BRCM_global_image && EGL_KHR_image
4589 VG_CLIENT_FONT_T *font;
4590#endif
4591
4592 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
4593 if (!state) {
4594 return;
4595 }
4596
4597#if EGL_BRCM_global_image && EGL_KHR_image
4598 platform_mutex_acquire(&state->shared_state->mutex);
4599 font = (VG_CLIENT_FONT_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_FONT);
4600 if (font) {
4601 khrn_global_image_map_delete(&font->glyph_global_images, glyph_id);
4602 }
4603 platform_mutex_release(&state->shared_state->mutex);
4604#endif
4605
4606 RPC_CALL2(vgClearGlyph_impl,
4607 thread,
4608 VGCLEARGLYPH_ID,
4609 RPC_HANDLE(vg_handle),
4610 RPC_UINT(glyph_id));
4611}
4612
4613VG_API_CALL void VG_API_ENTRY vgDrawGlyph(
4614 VGFont vg_handle,
4615 VGuint glyph_id,
4616 VGbitfield paint_modes,
4617 VGboolean allow_autohinting) VG_API_EXIT
4618{
4619 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4620 VG_CLIENT_STATE_T *state;
4621
4622#ifdef VG_NO_STROKING
4623 paint_modes &= ~VG_STROKE_PATH;
4624 if (!paint_modes) { return; }
4625#endif
4626
4627 state = VG_GET_CLIENT_STATE(thread);
4628 if (!state) {
4629 return;
4630 }
4631
4632 sync_matrix(state, VG_MATRIX_GLYPH_USER_TO_SURFACE);
4633 sync_matrix(state, VG_MATRIX_FILL_PAINT_TO_USER); /* image glyphs will use, so have to send even if paint_modes doesn't have VG_FILL_PATH */
4634 if (paint_modes & VG_STROKE_PATH) {
4635 sync_matrix(state, VG_MATRIX_STROKE_PAINT_TO_USER);
4636 }
4637
4638 if (state->render_callback)
4639 state->render_callback();
4640
4641 RPC_CALL4(vgDrawGlyph_impl,
4642 thread,
4643 VGDRAWGLYPH_ID,
4644 RPC_HANDLE(vg_handle),
4645 RPC_UINT(glyph_id),
4646 RPC_BITFIELD(paint_modes),
4647 RPC_BOOLEAN(clean_boolean(allow_autohinting)));
4648}
4649
4650VG_API_CALL void VG_API_ENTRY vgDrawGlyphs(
4651 VGFont vg_handle,
4652 VGint glyphs_count,
4653 const VGuint *glyph_ids,
4654 const VGfloat *adjustments_x,
4655 const VGfloat *adjustments_y,
4656 VGbitfield paint_modes,
4657 VGboolean allow_autohinting) VG_API_EXIT
4658{
4659 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4660 VG_CLIENT_STATE_T *state;
4661
4662#ifdef VG_NO_STROKING
4663 paint_modes &= ~VG_STROKE_PATH;
4664 if (!paint_modes) { return; }
4665#endif
4666
4667 state = VG_GET_CLIENT_STATE(thread);
4668 if (!state) {
4669 return;
4670 }
4671
4672 if ((glyphs_count <= 0) || !glyph_ids || !is_aligned_uint(glyph_ids) ||
4673 (adjustments_x && !is_aligned_float(adjustments_x)) ||
4674 (adjustments_y && !is_aligned_float(adjustments_y))) {
4675 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
4676 return;
4677 }
4678
4679 sync_matrix(state, VG_MATRIX_GLYPH_USER_TO_SURFACE);
4680 sync_matrix(state, VG_MATRIX_FILL_PAINT_TO_USER); /* image glyphs will use, so have to send even if paint_modes doesn't have VG_FILL_PATH */
4681 if (paint_modes & VG_STROKE_PATH) {
4682 sync_matrix(state, VG_MATRIX_STROKE_PAINT_TO_USER);
4683 }
4684
4685 if (state->render_callback)
4686 state->render_callback();
4687
4688 #ifdef RPC_DIRECT
4689 RPC_CALL7(vgDrawGlyphs_impl, thread, no_id,
4690 vg_handle,
4691 glyphs_count, glyph_ids,
4692 adjustments_x, adjustments_y,
4693 paint_modes,
4694 clean_boolean(allow_autohinting));
4695 #else
4696 {
4697 /*
4698 split into multiple calls if necessary to avoid overflowing control buffer
4699
4700 todo: if there's an invalid glyph, we're not supposed to draw anything
4701 at all. currently, this works on a per-chunk level (ie we will try to
4702 draw the glyphs in a chunk iff there are no invalid glyphs in the chunk)
4703 */
4704
4705 VGuint glyph_size = sizeof(VGuint) +
4706 (adjustments_x ? sizeof(VGfloat) : 0) +
4707 (adjustments_y ? sizeof(VGfloat) : 0);
4708
4709 while (glyphs_count != 0) {
4710 #define MESSAGE_SIZE 24
4711
4712 /* at least 8 glyphs. this is a hack for the cts -- it checks that we don't draw anything if there's an invalid glyph (see above) */
4713 VGint chunk_glyphs_count = _min(glyphs_count, (rpc_send_ctrl_longest(thread, MESSAGE_SIZE + (8 * glyph_size)) - MESSAGE_SIZE) / glyph_size);
4714
4715 uint32_t message[] = {
4716 VGDRAWGLYPHS_ID,
4717 RPC_HANDLE(vg_handle),
4718 RPC_INT(chunk_glyphs_count),
4719 RPC_BITFIELD((!!adjustments_x << 0) | (!!adjustments_y << 1)),
4720 RPC_BITFIELD(paint_modes),
4721 RPC_BOOLEAN(clean_boolean(allow_autohinting)) };
4722 vcos_static_assert(sizeof(message) == MESSAGE_SIZE);
4723
4724 #undef MESSAGE_SIZE
4725
4726 rpc_send_ctrl_begin(thread, sizeof(message) + (chunk_glyphs_count * glyph_size));
4727 rpc_send_ctrl_write(thread, message, sizeof(message));
4728 rpc_send_ctrl_write(thread, (uint32_t *)glyph_ids, chunk_glyphs_count * sizeof(VGuint));
4729 if (adjustments_x) { rpc_send_ctrl_write(thread, (uint32_t *)adjustments_x, chunk_glyphs_count * sizeof(VGfloat)); }
4730 if (adjustments_y) { rpc_send_ctrl_write(thread, (uint32_t *)adjustments_y, chunk_glyphs_count * sizeof(VGfloat)); }
4731 rpc_send_ctrl_end(thread);
4732
4733 glyphs_count -= chunk_glyphs_count;
4734 glyph_ids += chunk_glyphs_count;
4735 if (adjustments_x) { adjustments_x += chunk_glyphs_count; }
4736 if (adjustments_y) { adjustments_y += chunk_glyphs_count; }
4737 }
4738 }
4739 #endif
4740}
4741
4742/******************************************************************************
4743api filters
4744******************************************************************************/
4745
4746VG_API_CALL void VG_API_ENTRY vgColorMatrix(
4747 VGImage dst_vg_handle, VGImage src_vg_handle,
4748 const VGfloat *matrix) VG_API_EXIT
4749{
4750 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4751 if (!VG_GET_CLIENT_STATE(thread)) {
4752 return;
4753 }
4754
4755 if (!matrix || !is_aligned_float(matrix)) {
4756 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
4757 return;
4758 }
4759
4760 RPC_CALL3_IN_CTRL(vgColorMatrix_impl,
4761 thread,
4762 VGCOLORMATRIX_ID,
4763 RPC_HANDLE(dst_vg_handle),
4764 RPC_HANDLE(src_vg_handle),
4765 matrix,
4766 20 * sizeof(VGfloat));
4767}
4768
4769VG_API_CALL void VG_API_ENTRY vgConvolve(
4770 VGImage dst_vg_handle, VGImage src_vg_handle,
4771 VGint kernel_width, VGint kernel_height,
4772 VGint shift_x, VGint shift_y,
4773 const VGshort *kernel,
4774 VGfloat scale, VGfloat bias,
4775 VGTilingMode tiling_mode) VG_API_EXIT
4776{
4777 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4778 scale = clean_float(scale);
4779 bias = clean_float(bias);
4780
4781 if (!VG_GET_CLIENT_STATE(thread)) {
4782 return;
4783 }
4784
4785 if ((kernel_width <= 0) || (kernel_height <= 0) ||
4786 (kernel_width > VG_CONFIG_MAX_KERNEL_SIZE) || (kernel_height > VG_CONFIG_MAX_KERNEL_SIZE) ||
4787 !kernel || !is_aligned_short(kernel)) {
4788 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
4789 return;
4790 }
4791
4792 RPC_CALL10_IN_CTRL(vgConvolve_impl,
4793 thread,
4794 VGCONVOLVE_ID,
4795 RPC_HANDLE(dst_vg_handle), RPC_HANDLE(src_vg_handle),
4796 RPC_INT(kernel_width), RPC_INT(kernel_height),
4797 RPC_INT(shift_x), RPC_INT(shift_y),
4798 RPC_FLOAT(scale), RPC_FLOAT(bias),
4799 RPC_ENUM(tiling_mode),
4800 kernel,
4801 kernel_width * kernel_height * sizeof(VGshort));
4802}
4803
4804VG_API_CALL void VG_API_ENTRY vgSeparableConvolve(
4805 VGImage dst_vg_handle, VGImage src_vg_handle,
4806 VGint kernel_width, VGint kernel_height,
4807 VGint shift_x, VGint shift_y,
4808 const VGshort *kernel_x,
4809 const VGshort *kernel_y,
4810 VGfloat scale, VGfloat bias,
4811 VGTilingMode tiling_mode) VG_API_EXIT
4812{
4813 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4814 scale = clean_float(scale);
4815 bias = clean_float(bias);
4816
4817 if (!VG_GET_CLIENT_STATE(thread)) {
4818 return;
4819 }
4820
4821 if ((kernel_width <= 0) || (kernel_height <= 0) ||
4822 (kernel_width > VG_CONFIG_MAX_SEPARABLE_KERNEL_SIZE) || (kernel_height > VG_CONFIG_MAX_SEPARABLE_KERNEL_SIZE) ||
4823 !kernel_x || !is_aligned_short(kernel_x) || !kernel_y || !is_aligned_short(kernel_y)) {
4824 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
4825 return;
4826 }
4827
4828 #ifdef RPC_DIRECT
4829 RPC_CALL11(vgSeparableConvolve_impl, thread, no_id,
4830 dst_vg_handle, src_vg_handle,
4831 kernel_width, kernel_height,
4832 shift_x, shift_y,
4833 kernel_x, kernel_y,
4834 scale, bias,
4835 tiling_mode);
4836 #else
4837 {
4838 uint32_t message[] = {
4839 VGSEPARABLECONVOLVE_ID,
4840 RPC_HANDLE(dst_vg_handle), RPC_HANDLE(src_vg_handle),
4841 RPC_INT(kernel_width), RPC_INT(kernel_height),
4842 RPC_INT(shift_x), RPC_INT(shift_y),
4843 RPC_FLOAT(scale), RPC_FLOAT(bias),
4844 RPC_ENUM(tiling_mode) };
4845
4846 VGuint kernel_x_size = kernel_width * sizeof(VGshort);
4847 VGuint kernel_y_size = kernel_height * sizeof(VGshort);
4848
4849 rpc_send_ctrl_begin(thread, sizeof(message) + rpc_pad_ctrl(kernel_x_size) + rpc_pad_ctrl(kernel_y_size));
4850 rpc_send_ctrl_write(thread, message, sizeof(message));
4851 rpc_send_ctrl_write(thread, (uint32_t *)kernel_x, kernel_x_size);
4852 rpc_send_ctrl_write(thread, (uint32_t *)kernel_y, kernel_y_size);
4853 rpc_send_ctrl_end(thread);
4854 }
4855 #endif
4856}
4857
4858VG_API_CALL void VG_API_ENTRY vgGaussianBlur(
4859 VGImage dst_vg_handle, VGImage src_vg_handle,
4860 VGfloat std_dev_x, VGfloat std_dev_y,
4861 VGTilingMode tiling_mode) VG_API_EXIT
4862{
4863 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4864 std_dev_x = clean_float(std_dev_x);
4865 std_dev_y = clean_float(std_dev_y);
4866
4867 if (!VG_GET_CLIENT_STATE(thread)) {
4868 return;
4869 }
4870
4871 RPC_CALL5(vgGaussianBlur_impl,
4872 thread,
4873 VGGAUSSIANBLUR_ID,
4874 RPC_HANDLE(dst_vg_handle), RPC_HANDLE(src_vg_handle),
4875 RPC_FLOAT(std_dev_x), RPC_FLOAT(std_dev_y),
4876 RPC_ENUM(tiling_mode));
4877}
4878
4879VG_API_CALL void VG_API_ENTRY vgLookup(
4880 VGImage dst_vg_handle, VGImage src_vg_handle,
4881 const VGubyte *red_lut,
4882 const VGubyte *green_lut,
4883 const VGubyte *blue_lut,
4884 const VGubyte *alpha_lut,
4885 VGboolean output_linear,
4886 VGboolean output_pre) VG_API_EXIT
4887{
4888 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4889 if (!VG_GET_CLIENT_STATE(thread)) {
4890 return;
4891 }
4892
4893 if (!red_lut || !green_lut || !blue_lut || !alpha_lut) {
4894 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
4895 return;
4896 }
4897
4898 #ifdef RPC_DIRECT
4899 RPC_CALL8(vgLookup_impl, thread, no_id,
4900 dst_vg_handle, src_vg_handle,
4901 red_lut, green_lut, blue_lut, alpha_lut,
4902 clean_boolean(output_linear),
4903 clean_boolean(output_pre));
4904 #else
4905 {
4906 uint32_t message[] = {
4907 VGLOOKUP_ID,
4908 RPC_HANDLE(dst_vg_handle), RPC_HANDLE(src_vg_handle),
4909 RPC_BOOLEAN(clean_boolean(output_linear)),
4910 RPC_BOOLEAN(clean_boolean(output_pre)) };
4911
4912 rpc_send_ctrl_begin(thread, sizeof(message) + 1024);
4913 rpc_send_ctrl_write(thread, message, sizeof(message));
4914 rpc_send_ctrl_write(thread, (uint32_t *)red_lut, 256);
4915 rpc_send_ctrl_write(thread, (uint32_t *)green_lut, 256);
4916 rpc_send_ctrl_write(thread, (uint32_t *)blue_lut, 256);
4917 rpc_send_ctrl_write(thread, (uint32_t *)alpha_lut, 256);
4918 rpc_send_ctrl_end(thread);
4919 }
4920 #endif
4921}
4922
4923VG_API_CALL void VG_API_ENTRY vgLookupSingle(
4924 VGImage dst_vg_handle, VGImage src_vg_handle,
4925 const VGuint *lut,
4926 VGImageChannel source_channel,
4927 VGboolean output_linear,
4928 VGboolean output_pre) VG_API_EXIT
4929{
4930 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4931 if (!VG_GET_CLIENT_STATE(thread)) {
4932 return;
4933 }
4934
4935 if (!lut || !is_aligned_uint(lut)) {
4936 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
4937 return;
4938 }
4939
4940 RPC_CALL6_IN_CTRL(vgLookupSingle_impl,
4941 thread,
4942 VGLOOKUPSINGLE_ID,
4943 RPC_HANDLE(dst_vg_handle), RPC_HANDLE(src_vg_handle),
4944 RPC_ENUM(source_channel),
4945 RPC_BOOLEAN(clean_boolean(output_linear)),
4946 RPC_BOOLEAN(clean_boolean(output_pre)),
4947 lut,
4948 256 * sizeof(VGuint));
4949}
4950
4951/******************************************************************************
4952api client-side stuff
4953******************************************************************************/
4954
4955VG_API_CALL VGHardwareQueryResult VG_API_ENTRY vgHardwareQuery(
4956 VGHardwareQueryType hardware_query_type,
4957 VGint setting) VG_API_EXIT
4958{
4959 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4960 if (!VG_GET_CLIENT_STATE(thread)) {
4961 return (VGHardwareQueryResult)0;
4962 }
4963
4964 if (!is_hardware_query_type(hardware_query_type) ||
4965 ((hardware_query_type == VG_IMAGE_FORMAT_QUERY) && !is_image_format((VGImageFormat)setting)) ||
4966 ((hardware_query_type == VG_PATH_DATATYPE_QUERY) && !is_path_datatype((VGPathDatatype)setting))) {
4967 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
4968 return (VGHardwareQueryResult)0;
4969 }
4970
4971 return VG_HARDWARE_ACCELERATED;
4972}
4973
4974VG_API_CALL const VGubyte * VG_API_ENTRY vgGetString(VGStringID name) VG_API_EXIT
4975{
4976 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
4977 if (!VG_GET_CLIENT_STATE(thread)) {
4978 return NULL;
4979 }
4980
4981 switch (name) {
4982 case VG_VENDOR: return (const VGubyte *)VG_CONFIG_VENDOR;
4983 case VG_RENDERER: return (const VGubyte *)VG_CONFIG_RENDERER;
4984 case VG_VERSION: return (const VGubyte *)"1.1";
4985 case VG_EXTENSIONS: return (const VGubyte *)VG_CONFIG_EXTENSIONS;
4986 default: return NULL;
4987 }
4988}
4989
4990/******************************************************************************
4991api vgu
4992******************************************************************************/
4993
4994static VGUErrorCode get_vgu_error(void)
4995{
4996 switch (get_error()) {
4997 case VG_NO_ERROR: return VGU_NO_ERROR;
4998 case VG_BAD_HANDLE_ERROR: return VGU_BAD_HANDLE_ERROR;
4999 case VG_ILLEGAL_ARGUMENT_ERROR: return VGU_ILLEGAL_ARGUMENT_ERROR;
5000 case VG_OUT_OF_MEMORY_ERROR: return VGU_OUT_OF_MEMORY_ERROR;
5001 case VG_PATH_CAPABILITY_ERROR: return VGU_PATH_CAPABILITY_ERROR;
5002 default: UNREACHABLE(); return (VGUErrorCode)0;
5003 }
5004}
5005
5006VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguLine(
5007 VGPath vg_handle,
5008 VGfloat p0_x, VGfloat p0_y,
5009 VGfloat p1_x, VGfloat p1_y) VGU_API_EXIT
5010{
5011 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
5012 VG_CLIENT_STATE_T *state;
5013 VG_CLIENT_PATH_T *path;
5014
5015 p0_x = clean_float(p0_x);
5016 p0_y = clean_float(p0_y);
5017 p1_x = clean_float(p1_x);
5018 p1_y = clean_float(p1_y);
5019
5020 state = VG_GET_CLIENT_STATE(thread);
5021 if (!state) {
5022 return VGU_NO_ERROR;
5023 }
5024
5025 clear_error();
5026
5027 platform_mutex_acquire(&state->shared_state->mutex);
5028 path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
5029 if (path &&
5030 (path->caps & VG_PATH_CAPABILITY_APPEND_TO) &&
5031 need_path_segments(path->caps)) {
5032 VGubyte *segments;
5033 if (!khrn_vector_extend(&path->segments, 2)) {
5034 platform_mutex_release(&state->shared_state->mutex);
5035 return VGU_OUT_OF_MEMORY_ERROR;
5036 }
5037 segments = (VGubyte *)path->segments.data + (path->segments.size - 2);
5038 segments[0] = VG_MOVE_TO_ABS;
5039 segments[1] = VG_LINE_TO_ABS;
5040 }
5041 platform_mutex_release(&state->shared_state->mutex);
5042
5043 RPC_CALL5(vguLine_impl,
5044 thread,
5045 VGULINE_ID,
5046 RPC_HANDLE(vg_handle),
5047 RPC_FLOAT(p0_x), RPC_FLOAT(p0_y),
5048 RPC_FLOAT(p1_x), RPC_FLOAT(p1_y));
5049
5050 return get_vgu_error();
5051}
5052
5053VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguPolygon(
5054 VGPath vg_handle,
5055 const VGfloat *ps, VGint ps_count,
5056 VGboolean close) VGU_API_EXIT
5057{
5058 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
5059 VG_CLIENT_PATH_T *path;
5060
5061 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
5062 if (!state) {
5063 return VGU_NO_ERROR;
5064 }
5065
5066 clear_error();
5067
5068 if ((ps_count <= 0) || !ps || !is_aligned_float(ps)) {
5069 return VGU_ILLEGAL_ARGUMENT_ERROR;
5070 }
5071
5072 platform_mutex_acquire(&state->shared_state->mutex);
5073 path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
5074 if (path &&
5075 (path->caps & VG_PATH_CAPABILITY_APPEND_TO) &&
5076 need_path_segments(path->caps)) {
5077 VGubyte *segments;
5078 VGuint segments_count = ps_count + (close ? 1 : 0);
5079 if (!khrn_vector_extend(&path->segments, segments_count)) {
5080 platform_mutex_release(&state->shared_state->mutex);
5081 return VGU_OUT_OF_MEMORY_ERROR;
5082 }
5083 segments = (VGubyte *)path->segments.data + (path->segments.size - segments_count);
5084 memset(segments, VG_LINE_TO_ABS, segments_count);
5085 segments[0] = VG_MOVE_TO_ABS;
5086 if (close) { segments[segments_count - 1] = VG_CLOSE_PATH; }
5087 }
5088 platform_mutex_release(&state->shared_state->mutex);
5089
5090 #ifdef RPC_DIRECT
5091 RPC_CALL5(vguPolygon_impl, thread, no_id,
5092 vg_handle,
5093 ps, ps_count,
5094 true, clean_boolean(close));
5095 #else
5096 {
5097 /*
5098 split into multiple calls if necessary to avoid overflowing control buffer
5099 */
5100
5101 bool first = true;
5102 while (ps_count != 0) {
5103 #define MESSAGE_SIZE 20
5104
5105 VGint chunk_ps_count = _min(ps_count, (rpc_send_ctrl_longest(thread, MESSAGE_SIZE + (2 * sizeof(VGfloat))) - MESSAGE_SIZE) / (2 * sizeof(VGfloat))); /* at least one p */
5106 bool last = chunk_ps_count == ps_count;
5107
5108 uint32_t message[] = {
5109 VGUPOLYGON_ID,
5110 RPC_HANDLE(vg_handle),
5111 RPC_INT(chunk_ps_count),
5112 RPC_BOOLEAN(first), RPC_BOOLEAN(last && close) };
5113 vcos_static_assert(sizeof(message) == MESSAGE_SIZE);
5114
5115 #undef MESSAGE_SIZE
5116
5117 rpc_send_ctrl_begin(thread,
5118 sizeof(message) +
5119 (chunk_ps_count * 2 * sizeof(VGfloat)));
5120 rpc_send_ctrl_write(thread, message, sizeof(message));
5121 rpc_send_ctrl_write(thread, (uint32_t *)ps, chunk_ps_count * 2 * sizeof(VGfloat));
5122 rpc_send_ctrl_end(thread);
5123
5124 ps_count -= chunk_ps_count;
5125 ps += 2 * chunk_ps_count;
5126 first = false;
5127 }
5128 }
5129 #endif
5130
5131 return get_vgu_error();
5132}
5133
5134VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguRect(
5135 VGPath vg_handle,
5136 VGfloat x, VGfloat y,
5137 VGfloat width, VGfloat height) VGU_API_EXIT
5138{
5139 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
5140 VG_CLIENT_STATE_T *state;
5141 VG_CLIENT_PATH_T *path;
5142
5143 x = clean_float(x);
5144 y = clean_float(y);
5145 width = clean_float(width);
5146 height = clean_float(height);
5147
5148 state = VG_GET_CLIENT_STATE(thread);
5149 if (!state) {
5150 return VGU_NO_ERROR;
5151 }
5152
5153 clear_error();
5154
5155 if (is_le_zero(width) || is_le_zero(height)) {
5156 return VGU_ILLEGAL_ARGUMENT_ERROR;
5157 }
5158
5159 platform_mutex_acquire(&state->shared_state->mutex);
5160 path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
5161 if (path &&
5162 (path->caps & VG_PATH_CAPABILITY_APPEND_TO) &&
5163 need_path_segments(path->caps)) {
5164 VGubyte *segments;
5165 if (!khrn_vector_extend(&path->segments, 5)) {
5166 platform_mutex_release(&state->shared_state->mutex);
5167 return VGU_OUT_OF_MEMORY_ERROR;
5168 }
5169 segments = (VGubyte *)path->segments.data + (path->segments.size - 5);
5170 segments[0] = VG_MOVE_TO_ABS;
5171 segments[1] = VG_HLINE_TO_REL;
5172 segments[2] = VG_VLINE_TO_REL;
5173 segments[3] = VG_HLINE_TO_REL;
5174 segments[4] = VG_CLOSE_PATH;
5175 }
5176 platform_mutex_release(&state->shared_state->mutex);
5177
5178 RPC_CALL5(vguRect_impl,
5179 thread,
5180 VGURECT_ID,
5181 RPC_HANDLE(vg_handle),
5182 RPC_FLOAT(x), RPC_FLOAT(y),
5183 RPC_FLOAT(width), RPC_FLOAT(height));
5184
5185 return get_vgu_error();
5186}
5187
5188VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguRoundRect(
5189 VGPath vg_handle,
5190 VGfloat x, VGfloat y,
5191 VGfloat width, VGfloat height,
5192 VGfloat arc_width, VGfloat arc_height) VGU_API_EXIT
5193{
5194 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
5195 VG_CLIENT_STATE_T *state;
5196 VG_CLIENT_PATH_T *path;
5197
5198 x = clean_float(x);
5199 y = clean_float(y);
5200 width = clean_float(width);
5201 height = clean_float(height);
5202 arc_width = clean_float(arc_width);
5203 arc_height = clean_float(arc_height);
5204
5205 state = VG_GET_CLIENT_STATE(thread);
5206 if (!state) {
5207 return VGU_NO_ERROR;
5208 }
5209
5210 clear_error();
5211
5212 if (is_le_zero(width) || is_le_zero(height)) {
5213 return VGU_ILLEGAL_ARGUMENT_ERROR;
5214 }
5215
5216 platform_mutex_acquire(&state->shared_state->mutex);
5217 path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
5218 if (path &&
5219 (path->caps & VG_PATH_CAPABILITY_APPEND_TO) &&
5220 need_path_segments(path->caps)) {
5221 VGubyte *segments;
5222 if (!khrn_vector_extend(&path->segments, 10)) {
5223 platform_mutex_release(&state->shared_state->mutex);
5224 return VGU_OUT_OF_MEMORY_ERROR;
5225 }
5226 segments = (VGubyte *)path->segments.data + (path->segments.size - 10);
5227 segments[0] = VG_MOVE_TO_ABS;
5228 segments[1] = VG_HLINE_TO_REL;
5229 segments[2] = VG_SCCWARC_TO_REL;
5230 segments[3] = VG_VLINE_TO_REL;
5231 segments[4] = VG_SCCWARC_TO_REL;
5232 segments[5] = VG_HLINE_TO_REL;
5233 segments[6] = VG_SCCWARC_TO_REL;
5234 segments[7] = VG_VLINE_TO_REL;
5235 segments[8] = VG_SCCWARC_TO_REL;
5236 segments[9] = VG_CLOSE_PATH;
5237 }
5238 platform_mutex_release(&state->shared_state->mutex);
5239
5240 RPC_CALL7(vguRoundRect_impl,
5241 thread,
5242 VGUROUNDRECT_ID,
5243 RPC_HANDLE(vg_handle),
5244 RPC_FLOAT(x), RPC_FLOAT(y),
5245 RPC_FLOAT(width), RPC_FLOAT(height),
5246 RPC_FLOAT(arc_width), RPC_FLOAT(arc_height));
5247
5248 return get_vgu_error();
5249}
5250
5251VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguEllipse(
5252 VGPath vg_handle,
5253 VGfloat x, VGfloat y,
5254 VGfloat width, VGfloat height) VGU_API_EXIT
5255{
5256 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
5257 VG_CLIENT_STATE_T *state;
5258 VG_CLIENT_PATH_T *path;
5259
5260 x = clean_float(x);
5261 y = clean_float(y);
5262 width = clean_float(width);
5263 height = clean_float(height);
5264
5265 state = VG_GET_CLIENT_STATE(thread);
5266 if (!state) {
5267 return VGU_NO_ERROR;
5268 }
5269
5270 clear_error();
5271
5272 if (is_le_zero(width) || is_le_zero(height)) {
5273 return VGU_ILLEGAL_ARGUMENT_ERROR;
5274 }
5275
5276 platform_mutex_acquire(&state->shared_state->mutex);
5277 path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
5278 if (path &&
5279 (path->caps & VG_PATH_CAPABILITY_APPEND_TO) &&
5280 need_path_segments(path->caps)) {
5281 VGubyte *segments;
5282 if (!khrn_vector_extend(&path->segments, 4)) {
5283 platform_mutex_release(&state->shared_state->mutex);
5284 return VGU_OUT_OF_MEMORY_ERROR;
5285 }
5286 segments = (VGubyte *)path->segments.data + (path->segments.size - 4);
5287 segments[0] = VG_MOVE_TO_ABS;
5288 segments[1] = VG_SCCWARC_TO_REL;
5289 segments[2] = VG_SCCWARC_TO_REL;
5290 segments[3] = VG_CLOSE_PATH;
5291 }
5292 platform_mutex_release(&state->shared_state->mutex);
5293
5294 RPC_CALL5(vguEllipse_impl,
5295 thread,
5296 VGUELLIPSE_ID,
5297 RPC_HANDLE(vg_handle),
5298 RPC_FLOAT(x), RPC_FLOAT(y),
5299 RPC_FLOAT(width), RPC_FLOAT(height));
5300
5301 return get_vgu_error();
5302}
5303
5304VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguArc(
5305 VGPath vg_handle,
5306 VGfloat x, VGfloat y,
5307 VGfloat width, VGfloat height,
5308 VGfloat start_angle, VGfloat angle_extent,
5309 VGUArcType arc_type) VGU_API_EXIT
5310{
5311 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
5312 VG_CLIENT_STATE_T *state;
5313 VGuint angle_o180;
5314 VG_CLIENT_PATH_T *path;
5315
5316 x = clean_float(x);
5317 y = clean_float(y);
5318 width = clean_float(width);
5319 height = clean_float(height);
5320 start_angle = clean_float(start_angle);
5321 angle_extent = clean_float(angle_extent);
5322
5323 state = VG_GET_CLIENT_STATE(thread);
5324 if (!state) {
5325 return VGU_NO_ERROR;
5326 }
5327
5328 clear_error();
5329
5330 if (is_le_zero(width) || is_le_zero(height) || !is_arc_type(arc_type)) {
5331 return VGU_ILLEGAL_ARGUMENT_ERROR;
5332 }
5333
5334 angle_o180 = float_to_int_zero(absf_(angle_extent) * (1.0f / 180.0f));
5335
5336 platform_mutex_acquire(&state->shared_state->mutex);
5337 path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
5338 if (path &&
5339 (path->caps & VG_PATH_CAPABILITY_APPEND_TO) &&
5340 need_path_segments(path->caps)) {
5341 VGubyte *segments;
5342 VGuint segments_count = 2 + angle_o180;
5343 switch (arc_type) {
5344 case VGU_ARC_OPEN: break;
5345 case VGU_ARC_CHORD: segments_count += 1; break;
5346 case VGU_ARC_PIE: segments_count += 2; break;
5347 default: UNREACHABLE();
5348 }
5349 if (!khrn_vector_extend(&path->segments, segments_count)) {
5350 platform_mutex_release(&state->shared_state->mutex);
5351 return VGU_OUT_OF_MEMORY_ERROR;
5352 }
5353 segments = (VGubyte *)path->segments.data + (path->segments.size - segments_count);
5354 segments[0] = VG_MOVE_TO_ABS;
5355 memset(segments + 1, VG_SCCWARC_TO_ABS, segments_count - 1); /* don't care about actual arc type on client */
5356 if (arc_type == VGU_ARC_PIE) { segments[segments_count - 2] = VG_LINE_TO_ABS; }
5357 if ((arc_type == VGU_ARC_CHORD) || (arc_type == VGU_ARC_PIE)) { segments[segments_count - 1] = VG_CLOSE_PATH; }
5358 }
5359 platform_mutex_release(&state->shared_state->mutex);
5360
5361 RPC_CALL9(vguArc_impl,
5362 thread,
5363 VGUARC_ID,
5364 RPC_HANDLE(vg_handle),
5365 RPC_FLOAT(x), RPC_FLOAT(y),
5366 RPC_FLOAT(width), RPC_FLOAT(height),
5367 RPC_FLOAT(start_angle), RPC_FLOAT(angle_extent), RPC_UINT(angle_o180),
5368 RPC_ENUM(arc_type));
5369
5370 return get_vgu_error();
5371}
5372
5373/*
5374 based on "Fundamentals of Texture Mapping and Image Warping" by Paul S Heckbert
5375*/
5376
5377static bool warp_square_to_quad(VG_MAT3X3_T *a,
5378 VGfloat p0_x, VGfloat p0_y,
5379 VGfloat p1_x, VGfloat p1_y,
5380 VGfloat p2_x, VGfloat p2_y,
5381 VGfloat p3_x, VGfloat p3_y)
5382{
5383 VGfloat d1_x = p1_x - p3_x;
5384 VGfloat d1_y = p1_y - p3_y;
5385 VGfloat d2_x = p2_x - p3_x;
5386 VGfloat d2_y = p2_y - p3_y;
5387
5388 VGfloat d = (d1_x * d2_y) - (d1_y * d2_x);
5389
5390 VGfloat oo_d;
5391 VGfloat sum_x;
5392 VGfloat sum_y;
5393 VGfloat g;
5394 VGfloat h;
5395
5396 if (absf_(d) < EPS) {
5397 return false;
5398 }
5399 oo_d = recip_(d);
5400
5401 sum_x = (p0_x + p3_x) - (p1_x + p2_x);
5402 sum_y = (p0_y + p3_y) - (p1_y + p2_y);
5403
5404 g = ((sum_x * d2_y) - (sum_y * d2_x)) * oo_d;
5405 h = ((d1_x * sum_y) - (d1_y * sum_x)) * oo_d;
5406
5407 a->m[0][0] = (p1_x - p0_x) + (g * p1_x);
5408 a->m[0][1] = (p2_x - p0_x) + (h * p2_x);
5409 a->m[0][2] = p0_x;
5410
5411 a->m[1][0] = (p1_y - p0_y) + (g * p1_y);
5412 a->m[1][1] = (p2_y - p0_y) + (h * p2_y);
5413 a->m[1][2] = p0_y;
5414
5415 a->m[2][0] = g;
5416 a->m[2][1] = h;
5417 a->m[2][2] = 1.0f;
5418
5419 return true;
5420}
5421
5422static bool warp_quad_to_square(VG_MAT3X3_T *a,
5423 VGfloat p0_x, VGfloat p0_y,
5424 VGfloat p1_x, VGfloat p1_y,
5425 VGfloat p2_x, VGfloat p2_y,
5426 VGfloat p3_x, VGfloat p3_y)
5427{
5428 if (!warp_square_to_quad(a, p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y) ||
5429 !vg_mat3x3_is_invertible(a)) {
5430 return false;
5431 }
5432 vg_mat3x3_invert(a);
5433 return true;
5434}
5435
5436VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguComputeWarpQuadToSquare(
5437 VGfloat p0_x, VGfloat p0_y,
5438 VGfloat p1_x, VGfloat p1_y,
5439 VGfloat p2_x, VGfloat p2_y,
5440 VGfloat p3_x, VGfloat p3_y,
5441 VGfloat *matrix) VGU_API_EXIT
5442{
5443 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
5444 VG_MAT3X3_T a;
5445
5446 p0_x = clean_float(p0_x);
5447 p0_y = clean_float(p0_y);
5448 p1_x = clean_float(p1_x);
5449 p1_y = clean_float(p1_y);
5450 p2_x = clean_float(p2_x);
5451 p2_y = clean_float(p2_y);
5452 p3_x = clean_float(p3_x);
5453 p3_y = clean_float(p3_y);
5454
5455 if (!VG_GET_CLIENT_STATE(thread)) {
5456 return VGU_NO_ERROR;
5457 }
5458
5459 if (!matrix || !is_aligned_float(matrix)) {
5460 return VGU_ILLEGAL_ARGUMENT_ERROR;
5461 }
5462
5463 if (!warp_quad_to_square(&a, p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y)) {
5464 return VGU_BAD_WARP_ERROR;
5465 }
5466 vg_mat3x3_get(&a, matrix);
5467 return VGU_NO_ERROR;
5468}
5469
5470VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguComputeWarpSquareToQuad(
5471 VGfloat p0_x, VGfloat p0_y,
5472 VGfloat p1_x, VGfloat p1_y,
5473 VGfloat p2_x, VGfloat p2_y,
5474 VGfloat p3_x, VGfloat p3_y,
5475 VGfloat *matrix) VGU_API_EXIT
5476{
5477 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
5478 VG_MAT3X3_T a;
5479
5480 p0_x = clean_float(p0_x);
5481 p0_y = clean_float(p0_y);
5482 p1_x = clean_float(p1_x);
5483 p1_y = clean_float(p1_y);
5484 p2_x = clean_float(p2_x);
5485 p2_y = clean_float(p2_y);
5486 p3_x = clean_float(p3_x);
5487 p3_y = clean_float(p3_y);
5488
5489 if (!VG_GET_CLIENT_STATE(thread)) {
5490 return VGU_NO_ERROR;
5491 }
5492
5493 if (!matrix || !is_aligned_float(matrix)) {
5494 return VGU_ILLEGAL_ARGUMENT_ERROR;
5495 }
5496
5497 if (!warp_square_to_quad(&a, p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y)) {
5498 return VGU_BAD_WARP_ERROR;
5499 }
5500 vg_mat3x3_get(&a, matrix);
5501 return VGU_NO_ERROR;
5502}
5503
5504VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguComputeWarpQuadToQuad(
5505 VGfloat p0_x, VGfloat p0_y,
5506 VGfloat p1_x, VGfloat p1_y,
5507 VGfloat p2_x, VGfloat p2_y,
5508 VGfloat p3_x, VGfloat p3_y,
5509 VGfloat q0_x, VGfloat q0_y,
5510 VGfloat q1_x, VGfloat q1_y,
5511 VGfloat q2_x, VGfloat q2_y,
5512 VGfloat q3_x, VGfloat q3_y,
5513 VGfloat *matrix) VGU_API_EXIT
5514{
5515 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
5516 VG_MAT3X3_T a, b;
5517
5518 p0_x = clean_float(p0_x);
5519 p0_y = clean_float(p0_y);
5520 p1_x = clean_float(p1_x);
5521 p1_y = clean_float(p1_y);
5522 p2_x = clean_float(p2_x);
5523 p2_y = clean_float(p2_y);
5524 p3_x = clean_float(p3_x);
5525 p3_y = clean_float(p3_y);
5526 q0_x = clean_float(q0_x);
5527 q0_y = clean_float(q0_y);
5528 q1_x = clean_float(q1_x);
5529 q1_y = clean_float(q1_y);
5530 q2_x = clean_float(q2_x);
5531 q2_y = clean_float(q2_y);
5532 q3_x = clean_float(q3_x);
5533 q3_y = clean_float(q3_y);
5534
5535 if (!VG_GET_CLIENT_STATE(thread)) {
5536 return VGU_NO_ERROR;
5537 }
5538
5539 if (!matrix || !is_aligned_float(matrix)) {
5540 return VGU_ILLEGAL_ARGUMENT_ERROR;
5541 }
5542
5543 if (!warp_square_to_quad(&a, p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y) ||
5544 !warp_quad_to_square(&b, q0_x, q0_y, q1_x, q1_y, q2_x, q2_y, q3_x, q3_y)) {
5545 return VGU_BAD_WARP_ERROR;
5546 }
5547 vg_mat3x3_postmul(&a, &b);
5548 vg_mat3x3_get(&a, matrix);
5549 return VGU_NO_ERROR;
5550}
5551
5552/******************************************************************************
5553VG_KHR_EGL_image
5554******************************************************************************/
5555
5556#if VG_KHR_EGL_image
5557
5558VG_API_CALL VGImage VG_API_ENTRY vgCreateEGLImageTargetKHR(
5559 VGeglImageKHR src_egl_handle) VG_API_EXIT
5560{
5561 CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
5562#if EGL_BRCM_global_image
5563 VGuint global_image_id[2];
5564#endif
5565 VGuint format_width_height[3];
5566 VGImage vg_handle;
5567 VG_CLIENT_IMAGE_T *image;
5568
5569 VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
5570 if (!state) {
5571 return VG_INVALID_HANDLE;
5572 }
5573
5574#if EGL_BRCM_global_image
5575 if ((uintptr_t)src_egl_handle & (1 << 31)) {
5576 CLIENT_PROCESS_STATE_T *process = CLIENT_GET_PROCESS_STATE();
5577 uint64_t id;
5578
5579 CLIENT_LOCK();
5580 id = process->inited ? khrn_global_image_map_lookup(&process->global_image_egl_images, (uint32_t)(uintptr_t)src_egl_handle) : 0;
5581 CLIENT_UNLOCK();
5582 if (!id) {
5583 set_error(VG_ILLEGAL_ARGUMENT_ERROR);
5584 return VG_INVALID_HANDLE;
5585 }
5586 global_image_id[0] = (VGuint)id;
5587 global_image_id[1] = (VGuint)(id >> 32);
5588
5589 platform_get_global_image_info(global_image_id[0], global_image_id[1],
5590 format_width_height + 0, format_width_height + 1, format_width_height + 2);
5591
5592 if (!(format_width_height[0] & EGL_PIXEL_FORMAT_VG_IMAGE_BRCM) ||
5593 (format_width_height[1] == 0) || (format_width_height[2] == 0) ||
5594 (format_width_height[1] > VG_CONFIG_MAX_IMAGE_WIDTH) || (format_width_height[2] > VG_CONFIG_MAX_IMAGE_HEIGHT)) {
5595 set_error(VG_UNSUPPORTED_IMAGE_FORMAT_ERROR);
5596 return VG_INVALID_HANDLE;
5597 }
5598
5599 switch (format_width_height[0] & ~EGL_PIXEL_FORMAT_USAGE_MASK_BRCM) {
5600 case EGL_PIXEL_FORMAT_ARGB_8888_PRE_BRCM: format_width_height[0] = VG_sARGB_8888_PRE; break;
5601 case EGL_PIXEL_FORMAT_ARGB_8888_BRCM: format_width_height[0] = VG_sARGB_8888; break;
5602 case EGL_PIXEL_FORMAT_XRGB_8888_BRCM: format_width_height[0] = VG_sXRGB_8888; break;
5603 case EGL_PIXEL_FORMAT_RGB_565_BRCM: format_width_height[0] = VG_sRGB_565; break;
5604 case EGL_PIXEL_FORMAT_A_8_BRCM: format_width_height[0] = VG_A_8; break;
5605 default: UNREACHABLE();
5606 }
5607
5608 vg_handle = get_stem(state);
5609 if (vg_handle == VG_INVALID_HANDLE) {
5610 set_error(VG_OUT_OF_MEMORY_ERROR);
5611 return VG_INVALID_HANDLE;
5612 }
5613
5614 RPC_CALL3(vgCreateImageFromGlobalImage_impl,
5615 thread,
5616 VGCREATEIMAGEFROMGLOBALIMAGE_ID,
5617 RPC_HANDLE(vg_handle),
5618 RPC_UINT(global_image_id[0]),
5619 RPC_UINT(global_image_id[1]));
5620 } else {
5621 global_image_id[0] = 0;
5622 global_image_id[1] = 0;
5623#endif
5624 vg_handle = RPC_HANDLE_RES(RPC_CALL2_OUT_CTRL_RES(vgCreateEGLImageTargetKHR_impl,
5625 thread,
5626 VGCREATEEGLIMAGETARGETKHR_ID,
5627 RPC_EGLID(src_egl_handle),
5628 format_width_height));
5629 if (vg_handle == VG_INVALID_HANDLE) {
5630 return VG_INVALID_HANDLE;
5631 }
5632#if EGL_BRCM_global_image
5633 }
5634#endif
5635
5636 image = image_alloc((VGImageFormat)format_width_height[0], format_width_height[1], format_width_height[2]
5637#if EGL_BRCM_global_image
5638 , global_image_id[0], global_image_id[1]
5639#endif
5640 );
5641 if (!image) {
5642 set_error(VG_OUT_OF_MEMORY_ERROR);
5643 RPC_CALL1(vgDestroyImage_impl,
5644 thread,
5645 VGDESTROYIMAGE_ID,
5646 RPC_HANDLE(vg_handle));
5647 return VG_INVALID_HANDLE;
5648 }
5649
5650 platform_mutex_acquire(&state->shared_state->mutex);
5651 if (!insert_object(state, vg_handle, image)) {
5652 set_error(VG_OUT_OF_MEMORY_ERROR);
5653 platform_mutex_release(&state->shared_state->mutex);
5654 image_free(image);
5655 RPC_CALL1(vgDestroyImage_impl,
5656 thread,
5657 VGDESTROYIMAGE_ID,
5658 RPC_HANDLE(vg_handle));
5659 return VG_INVALID_HANDLE;
5660 }
5661 platform_mutex_release(&state->shared_state->mutex);
5662
5663 return vg_handle;
5664}
5665
5666#endif
5667