forked from Qortal/Brooklyn
5667 lines
167 KiB
C
5667 lines
167 KiB
C
|
/*
|
||
|
Copyright (c) 2012, Broadcom Europe Ltd
|
||
|
All rights reserved.
|
||
|
|
||
|
Redistribution and use in source and binary forms, with or without
|
||
|
modification, are permitted provided that the following conditions are met:
|
||
|
* Redistributions of source code must retain the above copyright
|
||
|
notice, this list of conditions and the following disclaimer.
|
||
|
* Redistributions in binary form must reproduce the above copyright
|
||
|
notice, this list of conditions and the following disclaimer in the
|
||
|
documentation and/or other materials provided with the distribution.
|
||
|
* Neither the name of the copyright holder nor the
|
||
|
names of its contributors may be used to endorse or promote products
|
||
|
derived from this software without specific prior written permission.
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
/*=============================================================================
|
||
|
Copyright (c) 2008 Broadcom Europe Limited.
|
||
|
All rights reserved.
|
||
|
|
||
|
Project : khronos
|
||
|
Module : VG client
|
||
|
|
||
|
FILE DESCRIPTION
|
||
|
VG client-side function definitions. Dispatches VG calls via RPC or direct
|
||
|
call. Some functions require support for control messages just over 1kB in
|
||
|
length, 2kB should be fine for all functions.
|
||
|
=============================================================================*/
|
||
|
|
||
|
/*
|
||
|
Potential spec bugs:
|
||
|
|
||
|
vgImageSubData, vgGetImageSubData, vgWritePixels, and vgReadPixels require
|
||
|
the data pointer to be aligned, but do not require the stride to be
|
||
|
aligned. This seems a little useless, so we require that both the pointer
|
||
|
and stride are aligned (unless height is 1, in which case we just require
|
||
|
that the pointer is aligned)
|
||
|
*/
|
||
|
|
||
|
#define VG_VGEXT_PROTOTYPES /* we want the prototypes so the compiler will check that the signatures match */
|
||
|
|
||
|
#include "interface/khronos/common/khrn_client_mangle.h"
|
||
|
#include "interface/khronos/common/khrn_int_common.h"
|
||
|
|
||
|
#include "interface/khronos/common/khrn_client_platform.h"
|
||
|
#include "interface/khronos/common/khrn_client_rpc.h"
|
||
|
#include "interface/khronos/vg/vg_client.h"
|
||
|
|
||
|
#include "interface/khronos/egl/egl_client_config.h"
|
||
|
|
||
|
#include "interface/khronos/common/khrn_int_color.h"
|
||
|
#include "interface/khronos/common/khrn_int_math.h"
|
||
|
#include "interface/khronos/common/khrn_int_util.h"
|
||
|
|
||
|
#include "interface/khronos/include/VG/openvg.h"
|
||
|
#include "interface/khronos/include/VG/vgext.h"
|
||
|
#include "interface/khronos/include/VG/vgu.h"
|
||
|
|
||
|
#include "interface/khronos/vg/vg_int_config.h"
|
||
|
#include "interface/khronos/vg/vg_int_mat3x3.h"
|
||
|
#ifdef RPC_DIRECT
|
||
|
#include "interface/khronos/vg/vg_int_impl.h" /* for _impl function calls */
|
||
|
#endif
|
||
|
#if defined(WIN32) || defined(__mips__)
|
||
|
#include "interface/khronos/common/khrn_int_misc_impl.h"
|
||
|
#endif
|
||
|
|
||
|
#include "interface/khronos/vg/vg_int_util.h"
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
/******************************************************************************
|
||
|
shared state
|
||
|
******************************************************************************/
|
||
|
|
||
|
VG_CLIENT_SHARED_STATE_T *vg_client_shared_state_alloc(void)
|
||
|
{
|
||
|
VG_CLIENT_SHARED_STATE_T *shared_state;
|
||
|
KHR_STATUS_T status;
|
||
|
|
||
|
shared_state = (VG_CLIENT_SHARED_STATE_T *)khrn_platform_malloc(sizeof(VG_CLIENT_SHARED_STATE_T), "VG_CLIENT_SHARED_STATE_T");
|
||
|
if (!shared_state) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
status = platform_mutex_create(&shared_state->mutex);
|
||
|
if (status != KHR_SUCCESS) {
|
||
|
khrn_platform_free(shared_state);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (!khrn_pointer_map_init(&shared_state->objects, 128)) {
|
||
|
platform_mutex_destroy(&shared_state->mutex);
|
||
|
khrn_platform_free(shared_state);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
shared_state->ref_count = 1;
|
||
|
shared_state->stems_count = 0;
|
||
|
|
||
|
return shared_state;
|
||
|
}
|
||
|
|
||
|
static void object_free(void *object);
|
||
|
|
||
|
static void object_free_callback(KHRN_POINTER_MAP_T *pointer_map, uint32_t key, void *object, void *data)
|
||
|
{
|
||
|
UNUSED(pointer_map);
|
||
|
UNUSED(key);
|
||
|
UNUSED(data);
|
||
|
|
||
|
object_free(object);
|
||
|
}
|
||
|
|
||
|
void vg_client_shared_state_free(VG_CLIENT_SHARED_STATE_T *shared_state)
|
||
|
{
|
||
|
vcos_assert(shared_state->ref_count == 0);
|
||
|
khrn_pointer_map_iterate(&shared_state->objects, object_free_callback, NULL);
|
||
|
khrn_pointer_map_term(&shared_state->objects);
|
||
|
platform_mutex_destroy(&shared_state->mutex);
|
||
|
khrn_platform_free(shared_state);
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
state
|
||
|
******************************************************************************/
|
||
|
|
||
|
VG_CLIENT_STATE_T *vg_client_state_alloc(VG_CLIENT_SHARED_STATE_T *shared_state)
|
||
|
{
|
||
|
VGuint i;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = (VG_CLIENT_STATE_T *)khrn_platform_malloc(sizeof(VG_CLIENT_STATE_T), "VG_CLIENT_STATE_T");
|
||
|
if (!state) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
vg_client_shared_state_acquire(shared_state);
|
||
|
state->shared_state = shared_state;
|
||
|
|
||
|
state->render_callback = NULL;
|
||
|
state->flush_callback = NULL;
|
||
|
|
||
|
state->matrix_mode = VG_MATRIX_PATH_USER_TO_SURFACE;
|
||
|
for (i = 0; i != ARR_COUNT(state->matrices); ++i) {
|
||
|
vg_mat3x3_set_identity(&state->matrices[i].client);
|
||
|
vg_mat3x3_set_identity(&state->matrices[i].server);
|
||
|
}
|
||
|
|
||
|
state->fill_rule = VG_EVEN_ODD;
|
||
|
state->stroke_line_width = 1.0f;
|
||
|
state->stroke_cap_style = VG_CAP_BUTT;
|
||
|
state->stroke_join_style = VG_JOIN_MITER;
|
||
|
state->stroke_miter_limit = 4.0f;
|
||
|
state->stroke_dash_pattern_count = 0;
|
||
|
state->stroke_dash_phase = 0.0f;
|
||
|
state->stroke_dash_phase_reset = false;
|
||
|
state->image_quality = VG_IMAGE_QUALITY_FASTER;
|
||
|
state->image_mode = VG_DRAW_IMAGE_NORMAL;
|
||
|
|
||
|
state->scissoring = false;
|
||
|
state->scissor_rects_count = 0;
|
||
|
|
||
|
state->rendering_quality = VG_RENDERING_QUALITY_BETTER;
|
||
|
|
||
|
state->fill_paint = VG_INVALID_HANDLE;
|
||
|
state->stroke_paint = VG_INVALID_HANDLE;
|
||
|
state->tile_fill_color[0] = 0.0f;
|
||
|
state->tile_fill_color[1] = 0.0f;
|
||
|
state->tile_fill_color[2] = 0.0f;
|
||
|
state->tile_fill_color[3] = 0.0f;
|
||
|
state->clear_color[0] = 0.0f;
|
||
|
state->clear_color[1] = 0.0f;
|
||
|
state->clear_color[2] = 0.0f;
|
||
|
state->clear_color[3] = 0.0f;
|
||
|
|
||
|
state->color_transform = false;
|
||
|
state->color_transform_values[0] = 1.0f;
|
||
|
state->color_transform_values[1] = 1.0f;
|
||
|
state->color_transform_values[2] = 1.0f;
|
||
|
state->color_transform_values[3] = 1.0f;
|
||
|
state->color_transform_values[4] = 0.0f;
|
||
|
state->color_transform_values[5] = 0.0f;
|
||
|
state->color_transform_values[6] = 0.0f;
|
||
|
state->color_transform_values[7] = 0.0f;
|
||
|
|
||
|
state->blend_mode = VG_BLEND_SRC_OVER;
|
||
|
state->masking = false;
|
||
|
|
||
|
state->filter_format_linear = false;
|
||
|
state->filter_format_pre = false;
|
||
|
state->filter_channel_mask = VG_RED | VG_GREEN | VG_BLUE | VG_ALPHA;
|
||
|
|
||
|
state->pixel_layout = VG_PIXEL_LAYOUT_UNKNOWN;
|
||
|
|
||
|
return state;
|
||
|
}
|
||
|
|
||
|
void vg_client_state_free(VG_CLIENT_STATE_T *state)
|
||
|
{
|
||
|
vg_client_shared_state_release(state->shared_state);
|
||
|
khrn_platform_free(state);
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
helpers
|
||
|
******************************************************************************/
|
||
|
|
||
|
static void clear_error(void)
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
RPC_CALL0(vgClearError_impl,
|
||
|
thread,
|
||
|
VGCLEARERROR_ID);
|
||
|
}
|
||
|
|
||
|
static void set_error(VGErrorCode error)
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
RPC_CALL1(vgSetError_impl,
|
||
|
thread,
|
||
|
VGSETERROR_ID,
|
||
|
RPC_ENUM(error));
|
||
|
}
|
||
|
|
||
|
static VGErrorCode get_error(void)
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
return (VGErrorCode)RPC_ENUM_RES(RPC_CALL0_RES(vgGetError_impl,
|
||
|
thread,
|
||
|
VGGETERROR_ID));
|
||
|
}
|
||
|
|
||
|
static INLINE bool is_aligned(const void *p, VGuint alignment)
|
||
|
{
|
||
|
vcos_assert(is_power_of_2(alignment));
|
||
|
return ((uintptr_t)p & (alignment - 1)) == 0;
|
||
|
}
|
||
|
|
||
|
static INLINE bool is_aligned_float(const VGfloat *p)
|
||
|
{
|
||
|
return is_aligned(p, alignof(VGfloat));
|
||
|
}
|
||
|
|
||
|
static INLINE bool is_aligned_short(const VGshort *p)
|
||
|
{
|
||
|
return is_aligned(p, alignof(VGshort));
|
||
|
}
|
||
|
|
||
|
static INLINE bool is_aligned_int(const VGint *p)
|
||
|
{
|
||
|
return is_aligned(p, alignof(VGint));
|
||
|
}
|
||
|
|
||
|
static INLINE bool is_aligned_uint(const VGuint *p)
|
||
|
{
|
||
|
return is_aligned(p, alignof(VGuint));
|
||
|
}
|
||
|
|
||
|
static bool is_aligned_path_datatype(const void *p, VGPathDatatype path_datatype)
|
||
|
{
|
||
|
switch (path_datatype) {
|
||
|
case VG_PATH_DATATYPE_S_8: return true;
|
||
|
case VG_PATH_DATATYPE_S_16: return is_aligned_short((const VGshort *)p);
|
||
|
case VG_PATH_DATATYPE_S_32: return is_aligned_int((const VGint *)p);
|
||
|
case VG_PATH_DATATYPE_F: return is_aligned_float((const VGfloat *)p);
|
||
|
default: UNREACHABLE(); return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool is_aligned_image_format(const void *p, VGImageFormat image_format)
|
||
|
{
|
||
|
switch (image_format) {
|
||
|
case VG_sRGBX_8888:
|
||
|
case VG_sRGBA_8888:
|
||
|
case VG_sRGBA_8888_PRE:
|
||
|
case VG_lRGBX_8888:
|
||
|
case VG_lRGBA_8888:
|
||
|
case VG_lRGBA_8888_PRE:
|
||
|
case VG_sXRGB_8888:
|
||
|
case VG_sARGB_8888:
|
||
|
case VG_sARGB_8888_PRE:
|
||
|
case VG_lXRGB_8888:
|
||
|
case VG_lARGB_8888:
|
||
|
case VG_lARGB_8888_PRE:
|
||
|
case VG_sBGRX_8888:
|
||
|
case VG_sBGRA_8888:
|
||
|
case VG_sBGRA_8888_PRE:
|
||
|
case VG_lBGRX_8888:
|
||
|
case VG_lBGRA_8888:
|
||
|
case VG_lBGRA_8888_PRE:
|
||
|
case VG_sXBGR_8888:
|
||
|
case VG_sABGR_8888:
|
||
|
case VG_sABGR_8888_PRE:
|
||
|
case VG_lXBGR_8888:
|
||
|
case VG_lABGR_8888:
|
||
|
case VG_lABGR_8888_PRE:
|
||
|
return is_aligned_uint((const VGuint *)p);
|
||
|
case VG_sRGB_565:
|
||
|
case VG_sRGBA_5551:
|
||
|
case VG_sRGBA_4444:
|
||
|
case VG_sARGB_1555:
|
||
|
case VG_sARGB_4444:
|
||
|
case VG_sBGR_565:
|
||
|
case VG_sBGRA_5551:
|
||
|
case VG_sBGRA_4444:
|
||
|
case VG_sABGR_1555:
|
||
|
case VG_sABGR_4444:
|
||
|
return is_aligned_short((const VGshort *)p);
|
||
|
case VG_sL_8:
|
||
|
case VG_lL_8:
|
||
|
case VG_A_8:
|
||
|
case VG_BW_1:
|
||
|
case VG_A_1:
|
||
|
case VG_A_4:
|
||
|
return true;
|
||
|
default:
|
||
|
UNREACHABLE();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool contains_illegal_segment(const VGubyte *segments, VGint segments_count)
|
||
|
{
|
||
|
for (; segments_count != 0; ++segments, --segments_count) {
|
||
|
switch (*segments & ~VG_RELATIVE) {
|
||
|
case VG_CLOSE_PATH: break;
|
||
|
case VG_MOVE_TO: break;
|
||
|
case VG_LINE_TO: break;
|
||
|
case VG_HLINE_TO: break;
|
||
|
case VG_VLINE_TO: break;
|
||
|
case VG_QUAD_TO: break;
|
||
|
case VG_CUBIC_TO: break;
|
||
|
case VG_SQUAD_TO: break;
|
||
|
case VG_SCUBIC_TO: break;
|
||
|
case VG_SCCWARC_TO: break;
|
||
|
case VG_SCWARC_TO: break;
|
||
|
case VG_LCCWARC_TO: break;
|
||
|
case VG_LCWARC_TO: break;
|
||
|
default: return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static VGuint get_coords_count(const VGubyte *segments, VGint segments_count)
|
||
|
{
|
||
|
VGuint coords_count = 0;
|
||
|
for (; segments_count != 0; ++segments, --segments_count) {
|
||
|
coords_count += get_segment_coords_count(*segments & ~VG_RELATIVE);
|
||
|
}
|
||
|
return coords_count;
|
||
|
}
|
||
|
|
||
|
static VGuint normalise_segment(VGuint segment)
|
||
|
{
|
||
|
switch (segment) {
|
||
|
case VG_CLOSE_PATH: return VG_CLOSE_PATH;
|
||
|
case VG_MOVE_TO: return VG_MOVE_TO;
|
||
|
case VG_LINE_TO: return VG_LINE_TO;
|
||
|
case VG_HLINE_TO: return VG_LINE_TO;
|
||
|
case VG_VLINE_TO: return VG_LINE_TO;
|
||
|
case VG_QUAD_TO: return VG_CUBIC_TO;
|
||
|
case VG_CUBIC_TO: return VG_CUBIC_TO;
|
||
|
case VG_SQUAD_TO: return VG_CUBIC_TO;
|
||
|
case VG_SCUBIC_TO: return VG_CUBIC_TO;
|
||
|
|
||
|
/*
|
||
|
on the client-side, we don't care about the different arc types, just
|
||
|
normalise them all to the same type
|
||
|
*/
|
||
|
|
||
|
case VG_SCCWARC_TO: return VG_SCCWARC_TO;
|
||
|
case VG_SCWARC_TO: return VG_SCCWARC_TO;
|
||
|
case VG_LCCWARC_TO: return VG_SCCWARC_TO;
|
||
|
case VG_LCWARC_TO: return VG_SCCWARC_TO;
|
||
|
default: UNREACHABLE(); return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool interpolate_segments_compatible(const VGubyte *begin_segments, const VGubyte *end_segments, VGuint segments_count)
|
||
|
{
|
||
|
for (; segments_count != 0; ++begin_segments, ++end_segments, --segments_count) {
|
||
|
if (normalise_segment(*begin_segments & ~VG_RELATIVE) != normalise_segment(*end_segments & ~VG_RELATIVE)) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
#ifndef RPC_DIRECT
|
||
|
|
||
|
static VGuint get_log2_image_format_bpp(VGImageFormat image_format)
|
||
|
{
|
||
|
switch (image_format) {
|
||
|
case VG_sRGBX_8888:
|
||
|
case VG_sRGBA_8888:
|
||
|
case VG_sRGBA_8888_PRE:
|
||
|
case VG_lRGBX_8888:
|
||
|
case VG_lRGBA_8888:
|
||
|
case VG_lRGBA_8888_PRE:
|
||
|
case VG_sXRGB_8888:
|
||
|
case VG_sARGB_8888:
|
||
|
case VG_sARGB_8888_PRE:
|
||
|
case VG_lXRGB_8888:
|
||
|
case VG_lARGB_8888:
|
||
|
case VG_lARGB_8888_PRE:
|
||
|
case VG_sBGRX_8888:
|
||
|
case VG_sBGRA_8888:
|
||
|
case VG_sBGRA_8888_PRE:
|
||
|
case VG_lBGRX_8888:
|
||
|
case VG_lBGRA_8888:
|
||
|
case VG_lBGRA_8888_PRE:
|
||
|
case VG_sXBGR_8888:
|
||
|
case VG_sABGR_8888:
|
||
|
case VG_sABGR_8888_PRE:
|
||
|
case VG_lXBGR_8888:
|
||
|
case VG_lABGR_8888:
|
||
|
case VG_lABGR_8888_PRE:
|
||
|
return 5;
|
||
|
case VG_sRGB_565:
|
||
|
case VG_sRGBA_5551:
|
||
|
case VG_sRGBA_4444:
|
||
|
case VG_sARGB_1555:
|
||
|
case VG_sARGB_4444:
|
||
|
case VG_sBGR_565:
|
||
|
case VG_sBGRA_5551:
|
||
|
case VG_sBGRA_4444:
|
||
|
case VG_sABGR_1555:
|
||
|
case VG_sABGR_4444:
|
||
|
return 4;
|
||
|
case VG_sL_8:
|
||
|
case VG_lL_8:
|
||
|
case VG_A_8:
|
||
|
return 3;
|
||
|
case VG_A_4:
|
||
|
return 2;
|
||
|
case VG_BW_1:
|
||
|
case VG_A_1:
|
||
|
return 0;
|
||
|
default:
|
||
|
UNREACHABLE();
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
static VGint param_to_int(
|
||
|
bool are_floats,
|
||
|
const void *values,
|
||
|
VGuint i)
|
||
|
{
|
||
|
vcos_assert(values);
|
||
|
return are_floats ?
|
||
|
float_to_int_floor(clean_float(((const VGfloat *)values)[i])) :
|
||
|
((const VGint *)values)[i];
|
||
|
}
|
||
|
|
||
|
static bool params_to_ints(
|
||
|
VGint *ints,
|
||
|
bool are_floats,
|
||
|
const void *values,
|
||
|
VGuint count)
|
||
|
{
|
||
|
bool changed = false;
|
||
|
VGuint i;
|
||
|
for (i = 0; i != count; ++i) {
|
||
|
VGint x = param_to_int(are_floats, values, i);
|
||
|
if (ints[i] != x) {
|
||
|
changed = true;
|
||
|
ints[i] = x;
|
||
|
}
|
||
|
}
|
||
|
return changed;
|
||
|
}
|
||
|
|
||
|
static VGfloat param_to_float(
|
||
|
bool are_floats,
|
||
|
const void *values,
|
||
|
VGuint i)
|
||
|
{
|
||
|
vcos_assert(values);
|
||
|
return are_floats ?
|
||
|
((const VGfloat *)values)[i] :
|
||
|
(VGfloat)((const VGint *)values)[i];
|
||
|
}
|
||
|
|
||
|
static bool params_to_floats(
|
||
|
VGfloat *floats,
|
||
|
bool are_floats,
|
||
|
const void *values,
|
||
|
VGuint count)
|
||
|
{
|
||
|
bool changed = false;
|
||
|
VGuint i;
|
||
|
for (i = 0; i != count; ++i) {
|
||
|
VGfloat x = param_to_float(are_floats, values, i);
|
||
|
if (!floats_identical(floats[i], x)) {
|
||
|
changed = true;
|
||
|
floats[i] = x;
|
||
|
}
|
||
|
}
|
||
|
return changed;
|
||
|
}
|
||
|
|
||
|
static void int_to_param(
|
||
|
bool are_floats,
|
||
|
void *values,
|
||
|
VGuint i,
|
||
|
VGint value)
|
||
|
{
|
||
|
if (are_floats) {
|
||
|
((VGfloat *)values)[i] = (VGfloat)value;
|
||
|
} else {
|
||
|
((VGint *)values)[i] = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void ints_to_params(
|
||
|
bool are_floats,
|
||
|
void *values,
|
||
|
VGuint count,
|
||
|
const VGint *ints)
|
||
|
{
|
||
|
VGuint i;
|
||
|
for (i = 0; i != count; ++i) {
|
||
|
int_to_param(are_floats, values, i, ints[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void float_to_param(
|
||
|
bool are_floats,
|
||
|
void *values,
|
||
|
VGuint i,
|
||
|
VGfloat value)
|
||
|
{
|
||
|
if (are_floats) {
|
||
|
((VGfloat *)values)[i] = value;
|
||
|
} else {
|
||
|
((VGint *)values)[i] = float_to_int_floor(clean_float(value));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void floats_to_params(
|
||
|
bool are_floats,
|
||
|
void *values,
|
||
|
VGuint count,
|
||
|
const VGfloat *floats)
|
||
|
{
|
||
|
VGuint i;
|
||
|
for (i = 0; i != count; ++i) {
|
||
|
float_to_param(are_floats, values, i, floats[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static VGHandle get_stem(VG_CLIENT_STATE_T *state)
|
||
|
{
|
||
|
VGHandle vg_handle;
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
|
||
|
if (state->shared_state->stems_count == 0) {
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
RPC_HIGH_PRIORITY_BEGIN(thread);
|
||
|
state->shared_state->stems_count = RPC_UINT_RES(RPC_CALL2_OUT_CTRL_RES(vgCreateStems_impl,
|
||
|
thread,
|
||
|
VGCREATESTEMS_ID,
|
||
|
VG_CLIENT_STEMS_COUNT_MAX,
|
||
|
state->shared_state->stems));
|
||
|
RPC_HIGH_PRIORITY_END(thread);
|
||
|
}
|
||
|
vg_handle = (state->shared_state->stems_count == 0) ? VG_INVALID_HANDLE : state->shared_state->stems[--state->shared_state->stems_count];
|
||
|
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
return vg_handle;
|
||
|
}
|
||
|
|
||
|
static void destroy_stem(VGHandle vg_handle)
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
RPC_CALL1(vgDestroyStem_impl,
|
||
|
thread,
|
||
|
VGDESTROYSTEM_ID,
|
||
|
RPC_HANDLE(vg_handle));
|
||
|
}
|
||
|
|
||
|
static VG_CLIENT_FONT_T *font_alloc(void)
|
||
|
{
|
||
|
VG_CLIENT_FONT_T *font = (VG_CLIENT_FONT_T *)khrn_platform_malloc(sizeof(VG_CLIENT_FONT_T), "VG_CLIENT_FONT_T");
|
||
|
if (!font) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
font->object_type = VG_CLIENT_OBJECT_TYPE_FONT;
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
if (!khrn_global_image_map_init(&font->glyph_global_images, 8)) {
|
||
|
khrn_platform_free(font);
|
||
|
return NULL;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return font;
|
||
|
}
|
||
|
|
||
|
static void font_free(VG_CLIENT_FONT_T *font)
|
||
|
{
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
khrn_global_image_map_term(&font->glyph_global_images);
|
||
|
#endif
|
||
|
khrn_platform_free(font);
|
||
|
}
|
||
|
|
||
|
static VG_CLIENT_IMAGE_T *image_alloc(VGImageFormat format, VGint width, VGint height
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
, VGuint global_image_id_0, VGuint global_image_id_1
|
||
|
#endif
|
||
|
)
|
||
|
{
|
||
|
VG_CLIENT_IMAGE_T *image = (VG_CLIENT_IMAGE_T *)khrn_platform_malloc(sizeof(VG_CLIENT_IMAGE_T), "VG_CLIENT_IMAGE_T");
|
||
|
if (!image) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
image->object_type = VG_CLIENT_OBJECT_TYPE_IMAGE;
|
||
|
image->format = format;
|
||
|
image->width = width;
|
||
|
image->height = height;
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
if (global_image_id_0 || global_image_id_1) {
|
||
|
platform_acquire_global_image(global_image_id_0, global_image_id_1);
|
||
|
}
|
||
|
image->global_image_id[0] = global_image_id_0;
|
||
|
image->global_image_id[1] = global_image_id_1;
|
||
|
#endif
|
||
|
|
||
|
return image;
|
||
|
}
|
||
|
|
||
|
static void image_free(VG_CLIENT_IMAGE_T *image)
|
||
|
{
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
if (image->global_image_id[0] || image->global_image_id[1]) {
|
||
|
platform_release_global_image(image->global_image_id[0], image->global_image_id[1]);
|
||
|
}
|
||
|
#endif
|
||
|
khrn_platform_free(image);
|
||
|
}
|
||
|
|
||
|
static VG_CLIENT_MASK_LAYER_T *mask_layer_alloc(VGint width, VGint height)
|
||
|
{
|
||
|
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");
|
||
|
if (!mask_layer) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
mask_layer->object_type = VG_CLIENT_OBJECT_TYPE_MASK_LAYER;
|
||
|
mask_layer->width = width;
|
||
|
mask_layer->height = height;
|
||
|
|
||
|
return mask_layer;
|
||
|
}
|
||
|
|
||
|
static void mask_layer_free(VG_CLIENT_MASK_LAYER_T *mask_layer)
|
||
|
{
|
||
|
khrn_platform_free(mask_layer);
|
||
|
}
|
||
|
|
||
|
static bool need_paint_gradient(VGint param_type)
|
||
|
{
|
||
|
return (param_type == VG_PAINT_COLOR_RAMP_SPREAD_MODE) ||
|
||
|
(param_type == VG_PAINT_COLOR_RAMP_PREMULTIPLIED) ||
|
||
|
(param_type == VG_PAINT_COLOR_RAMP_STOPS) ||
|
||
|
(param_type == VG_PAINT_LINEAR_GRADIENT) ||
|
||
|
(param_type == VG_PAINT_RADIAL_GRADIENT);
|
||
|
}
|
||
|
|
||
|
static VG_CLIENT_PAINT_T *paint_alloc(void)
|
||
|
{
|
||
|
VG_CLIENT_PAINT_T *paint = (VG_CLIENT_PAINT_T *)khrn_platform_malloc(sizeof(VG_CLIENT_PAINT_T), "VG_CLIENT_PAINT_T");
|
||
|
if (!paint) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
paint->object_type = VG_CLIENT_OBJECT_TYPE_PAINT;
|
||
|
paint->type = VG_PAINT_TYPE_COLOR;
|
||
|
paint->color[0] = 0.0f;
|
||
|
paint->color[1] = 0.0f;
|
||
|
paint->color[2] = 0.0f;
|
||
|
paint->color[3] = 1.0f;
|
||
|
paint->gradient = NULL; /* will be allocated when required */
|
||
|
paint->pattern_tiling_mode = VG_TILE_FILL;
|
||
|
paint->pattern = VG_INVALID_HANDLE;
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
paint->pattern_global_image_id[0] = 0;
|
||
|
paint->pattern_global_image_id[1] = 0;
|
||
|
#endif
|
||
|
|
||
|
return paint;
|
||
|
}
|
||
|
|
||
|
static bool paint_alloc_gradient(VG_CLIENT_PAINT_T *paint)
|
||
|
{
|
||
|
VG_CLIENT_PAINT_GRADIENT_T *gradient;
|
||
|
|
||
|
vcos_assert(!paint->gradient);
|
||
|
|
||
|
gradient = (VG_CLIENT_PAINT_GRADIENT_T *)khrn_platform_malloc(sizeof(VG_CLIENT_PAINT_GRADIENT_T), "VG_CLIENT_PAINT_GRADIENT_T");
|
||
|
if (!gradient) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
gradient->linear[0] = 0.0f;
|
||
|
gradient->linear[1] = 0.0f;
|
||
|
gradient->linear[2] = 1.0f;
|
||
|
gradient->linear[3] = 0.0f;
|
||
|
gradient->radial[0] = 0.0f;
|
||
|
gradient->radial[1] = 0.0f;
|
||
|
gradient->radial[2] = 0.0f;
|
||
|
gradient->radial[3] = 0.0f;
|
||
|
gradient->radial[4] = 1.0f;
|
||
|
gradient->ramp_spread_mode = VG_COLOR_RAMP_SPREAD_PAD;
|
||
|
gradient->ramp_pre = true;
|
||
|
gradient->ramp_stops = NULL;
|
||
|
gradient->ramp_stops_count = 0;
|
||
|
|
||
|
paint->gradient = gradient;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static void paint_free(VG_CLIENT_PAINT_T *paint)
|
||
|
{
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
if (paint->pattern_global_image_id[0] || paint->pattern_global_image_id[1]) {
|
||
|
platform_release_global_image(paint->pattern_global_image_id[0], paint->pattern_global_image_id[1]);
|
||
|
}
|
||
|
#endif
|
||
|
if (paint->gradient) {
|
||
|
if (paint->gradient->ramp_stops) {
|
||
|
khrn_platform_free(paint->gradient->ramp_stops);
|
||
|
}
|
||
|
khrn_platform_free(paint->gradient);
|
||
|
}
|
||
|
khrn_platform_free(paint);
|
||
|
}
|
||
|
|
||
|
static bool need_path_segments(VGbitfield caps)
|
||
|
{
|
||
|
/*
|
||
|
need segments for vgModifyPath and interpolating from, but also need for
|
||
|
appending from incase the path being appended to needs segments (we don't
|
||
|
want to have to go to the server to get them)
|
||
|
*/
|
||
|
|
||
|
return !!(caps & (VG_PATH_CAPABILITY_APPEND_FROM |
|
||
|
VG_PATH_CAPABILITY_MODIFY |
|
||
|
VG_PATH_CAPABILITY_TRANSFORM_FROM |
|
||
|
VG_PATH_CAPABILITY_INTERPOLATE_FROM));
|
||
|
}
|
||
|
|
||
|
static VG_CLIENT_PATH_T *path_alloc(VGint format, VGPathDatatype datatype, VGfloat scale, VGfloat bias, VGbitfield caps, VGint segments_capacity)
|
||
|
{
|
||
|
VG_CLIENT_PATH_T *path = (VG_CLIENT_PATH_T *)khrn_platform_malloc(sizeof(VG_CLIENT_PATH_T), "VG_CLIENT_PATH_T");
|
||
|
if (!path) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
path->object_type = VG_CLIENT_OBJECT_TYPE_PATH;
|
||
|
path->format = format;
|
||
|
path->datatype = datatype;
|
||
|
path->scale = scale;
|
||
|
path->bias = bias;
|
||
|
path->caps = caps;
|
||
|
if (need_path_segments(caps)) {
|
||
|
khrn_vector_init(&path->segments, clampi(segments_capacity, 0, 1024));
|
||
|
}
|
||
|
|
||
|
return path;
|
||
|
}
|
||
|
|
||
|
static void path_update_caps(VG_CLIENT_PATH_T *path, VGbitfield caps)
|
||
|
{
|
||
|
if (!need_path_segments(path->caps) && need_path_segments(caps)) {
|
||
|
khrn_vector_init(&path->segments, 0);
|
||
|
}
|
||
|
if (need_path_segments(path->caps) && !need_path_segments(caps)) {
|
||
|
khrn_vector_term(&path->segments);
|
||
|
}
|
||
|
path->caps = caps;
|
||
|
}
|
||
|
|
||
|
static void path_free(VG_CLIENT_PATH_T *path)
|
||
|
{
|
||
|
if (need_path_segments(path->caps)) {
|
||
|
khrn_vector_term(&path->segments);
|
||
|
}
|
||
|
khrn_platform_free(path);
|
||
|
}
|
||
|
|
||
|
static void object_free(void *object)
|
||
|
{
|
||
|
switch (*(VG_CLIENT_OBJECT_TYPE_T *)object) {
|
||
|
case VG_CLIENT_OBJECT_TYPE_FONT: font_free((VG_CLIENT_FONT_T *)object); break;
|
||
|
case VG_CLIENT_OBJECT_TYPE_IMAGE: image_free((VG_CLIENT_IMAGE_T *)object); break;
|
||
|
case VG_CLIENT_OBJECT_TYPE_MASK_LAYER: mask_layer_free((VG_CLIENT_MASK_LAYER_T *)object); break;
|
||
|
case VG_CLIENT_OBJECT_TYPE_PAINT: paint_free((VG_CLIENT_PAINT_T *)object); break;
|
||
|
case VG_CLIENT_OBJECT_TYPE_PATH: path_free((VG_CLIENT_PATH_T *)object); break;
|
||
|
default: UNREACHABLE();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
see nice_handle in middleware/khronos/vg/vg_set.c
|
||
|
*/
|
||
|
|
||
|
static INLINE uint32_t nice_handle(VGHandle vg_handle)
|
||
|
{
|
||
|
return _ror((uint32_t)vg_handle, 31);
|
||
|
}
|
||
|
|
||
|
static bool insert_object(VG_CLIENT_STATE_T *state, VGHandle vg_handle, void *object)
|
||
|
{
|
||
|
void *prev_object = khrn_pointer_map_lookup(&state->shared_state->objects, nice_handle(vg_handle));
|
||
|
if (prev_object) {
|
||
|
object_free(prev_object);
|
||
|
}
|
||
|
return khrn_pointer_map_insert(&state->shared_state->objects, nice_handle(vg_handle), object);
|
||
|
}
|
||
|
|
||
|
static void delete_object(VG_CLIENT_STATE_T *state, VGHandle vg_handle, VG_CLIENT_OBJECT_TYPE_T type)
|
||
|
{
|
||
|
void *object = khrn_pointer_map_lookup(&state->shared_state->objects, nice_handle(vg_handle));
|
||
|
if (object && (*(VG_CLIENT_OBJECT_TYPE_T *)object == type)) {
|
||
|
object_free(object);
|
||
|
khrn_pointer_map_delete(&state->shared_state->objects, nice_handle(vg_handle));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static INLINE void *lookup_object(VG_CLIENT_STATE_T *state, VGHandle vg_handle, VG_CLIENT_OBJECT_TYPE_T type)
|
||
|
{
|
||
|
void *object = khrn_pointer_map_lookup(&state->shared_state->objects, nice_handle(vg_handle));
|
||
|
return (object && (*(VG_CLIENT_OBJECT_TYPE_T *)object == type)) ? object : NULL;
|
||
|
}
|
||
|
|
||
|
static INLINE VG_MAT3X3_SYNC_T *get_matrix_sync(VG_CLIENT_STATE_T *state, VGMatrixMode matrix_mode)
|
||
|
{
|
||
|
vcos_assert(
|
||
|
(matrix_mode >= VG_MATRIX_PATH_USER_TO_SURFACE) &&
|
||
|
(matrix_mode < (VG_MATRIX_PATH_USER_TO_SURFACE + ARR_COUNT(state->matrices))));
|
||
|
return state->matrices + (matrix_mode - VG_MATRIX_PATH_USER_TO_SURFACE);
|
||
|
}
|
||
|
|
||
|
static INLINE bool is_matrix_affine(VGMatrixMode matrix_mode)
|
||
|
{
|
||
|
return matrix_mode != VG_MATRIX_IMAGE_USER_TO_SURFACE;
|
||
|
}
|
||
|
|
||
|
static void sync_matrix(VG_CLIENT_STATE_T *state, VGMatrixMode matrix_mode)
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_MAT3X3_SYNC_T *matrix_sync = get_matrix_sync(state, matrix_mode);
|
||
|
if (!vg_mat3x3_identical(&matrix_sync->server, &matrix_sync->client)) {
|
||
|
RPC_CALL2_IN_CTRL(vgLoadMatrix_impl,
|
||
|
thread,
|
||
|
VGLOADMATRIX_ID,
|
||
|
RPC_ENUM(matrix_mode),
|
||
|
&matrix_sync->client,
|
||
|
sizeof(VG_MAT3X3_T));
|
||
|
matrix_sync->server = matrix_sync->client;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
api misc
|
||
|
******************************************************************************/
|
||
|
|
||
|
VG_API_CALL VGErrorCode VG_API_ENTRY vgGetError(void) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return VG_NO_CONTEXT_ERROR;
|
||
|
}
|
||
|
|
||
|
return get_error();
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgFlush(void) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RPC_CALL0(vgFlush_impl,
|
||
|
thread,
|
||
|
VGFLUSH_ID);
|
||
|
RPC_FLUSH(thread);
|
||
|
|
||
|
#ifdef KHRN_COMMAND_MODE_DISPLAY
|
||
|
{
|
||
|
//Check for single buffered windows surface in which case call surface updater
|
||
|
EGL_SURFACE_T *surface = CLIENT_GET_THREAD_STATE()->openvg.draw;
|
||
|
if (surface->type == WINDOW && surface->buffers==1) {
|
||
|
platform_surface_update(surface->internal_handle);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (state->flush_callback)
|
||
|
state->flush_callback(false);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgFinish(void) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
(void)RPC_UINT_RES(RPC_CALL0_RES(vgFinish_impl,
|
||
|
thread,
|
||
|
VGFINISH_ID)); /* return value ignored -- read performed to ensure blocking */
|
||
|
|
||
|
#ifdef KHRN_COMMAND_MODE_DISPLAY
|
||
|
{
|
||
|
//Check for single buffered windows surface in which case call surface updater
|
||
|
EGL_SURFACE_T *surface = CLIENT_GET_THREAD_STATE()->openvg.draw;
|
||
|
if (surface->type == WINDOW && surface->buffers==1) {
|
||
|
platform_surface_update(surface->internal_handle);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (state->flush_callback)
|
||
|
state->flush_callback(true);
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
api get/set
|
||
|
******************************************************************************/
|
||
|
|
||
|
static void set_iv_server(
|
||
|
VGParamType param_type,
|
||
|
VGint count,
|
||
|
const VGint *values)
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
RPC_CALL3_IN_CTRL(vgSetiv_impl,
|
||
|
thread,
|
||
|
VGSETIV_ID,
|
||
|
RPC_ENUM(param_type),
|
||
|
RPC_INT(count),
|
||
|
values,
|
||
|
count * sizeof(VGint));
|
||
|
}
|
||
|
|
||
|
static void set_fv_server(
|
||
|
VGParamType param_type,
|
||
|
VGint count,
|
||
|
const VGfloat *values)
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
RPC_CALL3_IN_CTRL(vgSetfv_impl,
|
||
|
thread,
|
||
|
VGSETFV_ID,
|
||
|
RPC_ENUM(param_type),
|
||
|
RPC_INT(count),
|
||
|
values,
|
||
|
count * sizeof(VGfloat));
|
||
|
}
|
||
|
|
||
|
static void get_fv_server(
|
||
|
VGParamType param_type,
|
||
|
VGint count,
|
||
|
VGfloat *values)
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
RPC_CALL3_OUT_CTRL(vgGetfv_impl,
|
||
|
thread,
|
||
|
VGGETFV_ID,
|
||
|
RPC_ENUM(param_type),
|
||
|
RPC_INT(count),
|
||
|
values);
|
||
|
}
|
||
|
|
||
|
static void set_parameter_iv_server(
|
||
|
VGHandle vg_handle,
|
||
|
VG_CLIENT_OBJECT_TYPE_T object_type,
|
||
|
VGint param_type,
|
||
|
VGint count,
|
||
|
const VGint *values)
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
RPC_CALL5_IN_CTRL(vgSetParameteriv_impl,
|
||
|
thread,
|
||
|
VGSETPARAMETERIV_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_ENUM(object_type),
|
||
|
RPC_INT(param_type),
|
||
|
RPC_INT(count),
|
||
|
values,
|
||
|
count * sizeof(VGint));
|
||
|
}
|
||
|
|
||
|
static void set_parameter_fv_server(
|
||
|
VGHandle vg_handle,
|
||
|
VG_CLIENT_OBJECT_TYPE_T object_type,
|
||
|
VGint param_type,
|
||
|
VGint count,
|
||
|
const VGfloat *values)
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
RPC_CALL5_IN_CTRL(vgSetParameterfv_impl,
|
||
|
thread,
|
||
|
VGSETPARAMETERFV_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_ENUM(object_type),
|
||
|
RPC_INT(param_type),
|
||
|
RPC_INT(count),
|
||
|
values,
|
||
|
count * sizeof(VGfloat));
|
||
|
}
|
||
|
|
||
|
static bool get_parameter_iv_server(
|
||
|
VGHandle vg_handle,
|
||
|
VG_CLIENT_OBJECT_TYPE_T object_type,
|
||
|
VGint param_type,
|
||
|
VGint count,
|
||
|
VGint *values)
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
return RPC_BOOLEAN_RES(RPC_CALL5_OUT_CTRL_RES(vgGetParameteriv_impl,
|
||
|
thread,
|
||
|
VGGETPARAMETERIV_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_ENUM(object_type),
|
||
|
RPC_INT(param_type),
|
||
|
RPC_INT(count),
|
||
|
values));
|
||
|
}
|
||
|
|
||
|
static void set_ifv(
|
||
|
CLIENT_THREAD_STATE_T *thread,
|
||
|
VG_CLIENT_STATE_T *state,
|
||
|
VGParamType param_type,
|
||
|
VGint count,
|
||
|
bool are_floats,
|
||
|
const void *values)
|
||
|
{
|
||
|
/*
|
||
|
* If 'count' is greater than zero, then our caller must have verified
|
||
|
* that 'values' is non-NULL.
|
||
|
* If 'count' is zero, then 'values' *must not* be dereferenced.
|
||
|
* Note that params_to_floats() and params_to_ints() handle a zero 'count'
|
||
|
* argument correctly and do not dereference 'values' in this case.
|
||
|
* Other functions, e.g. param_to_int() and param_to_float(), require a
|
||
|
* check before calling.
|
||
|
*/
|
||
|
VGint value_i = (count == 1) ? param_to_int(are_floats, values, 0) : 0;
|
||
|
VGfloat value_f = (count == 1) ? param_to_float(are_floats, values, 0) : 0.0f;
|
||
|
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
settable scalar param types
|
||
|
*/
|
||
|
|
||
|
#define CASE(PARAM_TYPE, OK, DO) \
|
||
|
case PARAM_TYPE: \
|
||
|
{ \
|
||
|
if ((count != 1) || !(OK)) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; } \
|
||
|
DO; \
|
||
|
break; \
|
||
|
}
|
||
|
#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); })
|
||
|
#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); })
|
||
|
CASE(VG_MATRIX_MODE, is_matrix_mode((VGMatrixMode)value_i), state->matrix_mode = (VGMatrixMode)value_i)
|
||
|
CASE_I_SERVER(VG_FILL_RULE, is_fill_rule((VGFillRule)value_i), state->fill_rule, (VGFillRule))
|
||
|
CASE_I_SERVER(VG_IMAGE_QUALITY, is_image_quality((VGImageQuality)value_i), state->image_quality, (VGImageQuality))
|
||
|
CASE_I_SERVER(VG_RENDERING_QUALITY, is_rendering_quality((VGRenderingQuality)value_i), state->rendering_quality, (VGRenderingQuality))
|
||
|
CASE_I_SERVER(VG_BLEND_MODE, is_blend_mode((VGBlendMode)value_i), state->blend_mode, (VGBlendMode))
|
||
|
CASE_I_SERVER(VG_IMAGE_MODE, is_image_mode((VGImageMode)value_i), state->image_mode, (VGImageMode))
|
||
|
CASE_I_SERVER(VG_COLOR_TRANSFORM, true, state->color_transform, clean_boolean)
|
||
|
CASE_F_SERVER(VG_STROKE_LINE_WIDTH, true, state->stroke_line_width)
|
||
|
CASE_I_SERVER(VG_STROKE_CAP_STYLE, is_cap_style((VGCapStyle)value_i), state->stroke_cap_style, (VGCapStyle))
|
||
|
CASE_I_SERVER(VG_STROKE_JOIN_STYLE, is_join_style((VGJoinStyle)value_i), state->stroke_join_style, (VGJoinStyle))
|
||
|
CASE_F_SERVER(VG_STROKE_MITER_LIMIT, true, state->stroke_miter_limit)
|
||
|
CASE_F_SERVER(VG_STROKE_DASH_PHASE, true, state->stroke_dash_phase)
|
||
|
CASE_I_SERVER(VG_STROKE_DASH_PHASE_RESET, true, state->stroke_dash_phase_reset, clean_boolean)
|
||
|
CASE_I_SERVER(VG_MASKING, true, state->masking, clean_boolean)
|
||
|
CASE_I_SERVER(VG_SCISSORING, true, state->scissoring, clean_boolean)
|
||
|
CASE(VG_PIXEL_LAYOUT, is_pixel_layout((VGPixelLayout)value_i), state->pixel_layout = (VGPixelLayout)value_i)
|
||
|
CASE_I_SERVER(VG_FILTER_FORMAT_LINEAR, true, state->filter_format_linear, clean_boolean)
|
||
|
CASE_I_SERVER(VG_FILTER_FORMAT_PREMULTIPLIED, true, state->filter_format_pre, clean_boolean)
|
||
|
CASE_I_SERVER(VG_FILTER_CHANNEL_MASK, true, state->filter_channel_mask, )
|
||
|
#undef CASE_F_SERVER
|
||
|
#undef CASE_I_SERVER
|
||
|
#undef CASE
|
||
|
|
||
|
/*
|
||
|
read-only scalar param types
|
||
|
*/
|
||
|
|
||
|
case VG_SCREEN_LAYOUT:
|
||
|
case VG_MAX_SCISSOR_RECTS:
|
||
|
case VG_MAX_DASH_COUNT:
|
||
|
case VG_MAX_KERNEL_SIZE:
|
||
|
case VG_MAX_SEPARABLE_KERNEL_SIZE:
|
||
|
case VG_MAX_COLOR_RAMP_STOPS:
|
||
|
case VG_MAX_IMAGE_WIDTH:
|
||
|
case VG_MAX_IMAGE_HEIGHT:
|
||
|
case VG_MAX_IMAGE_PIXELS:
|
||
|
case VG_MAX_IMAGE_BYTES:
|
||
|
case VG_MAX_FLOAT:
|
||
|
case VG_MAX_GAUSSIAN_STD_DEVIATION:
|
||
|
{
|
||
|
if (count != 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); }
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
vector param types
|
||
|
*/
|
||
|
|
||
|
case VG_SCISSOR_RECTS:
|
||
|
{
|
||
|
if (count & 0x3) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
count = _min(count, VG_CONFIG_MAX_SCISSOR_RECTS * 4);
|
||
|
if (params_to_ints(
|
||
|
state->scissor_rects,
|
||
|
are_floats, values, count) ||
|
||
|
(state->scissor_rects_count != count)) {
|
||
|
state->scissor_rects_count = count;
|
||
|
set_iv_server(VG_SCISSOR_RECTS, count, state->scissor_rects);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case VG_COLOR_TRANSFORM_VALUES:
|
||
|
{
|
||
|
if (count != 8) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
if (params_to_floats(
|
||
|
state->color_transform_values,
|
||
|
are_floats, values, count)) {
|
||
|
set_fv_server(VG_COLOR_TRANSFORM_VALUES, 8, state->color_transform_values);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case VG_STROKE_DASH_PATTERN:
|
||
|
{
|
||
|
count = _min(count, VG_CONFIG_MAX_DASH_COUNT);
|
||
|
if (params_to_floats(
|
||
|
state->stroke_dash_pattern,
|
||
|
are_floats, values, count) ||
|
||
|
(state->stroke_dash_pattern_count != count)) {
|
||
|
state->stroke_dash_pattern_count = count;
|
||
|
set_fv_server(VG_STROKE_DASH_PATTERN, count, state->stroke_dash_pattern);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case VG_TILE_FILL_COLOR:
|
||
|
case VG_CLEAR_COLOR:
|
||
|
{
|
||
|
VGfloat *color;
|
||
|
if (count != 4) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
color = (param_type == VG_TILE_FILL_COLOR) ? state->tile_fill_color : state->clear_color;
|
||
|
if (params_to_floats(
|
||
|
color,
|
||
|
are_floats, values, count)) {
|
||
|
VGint rgba = color_floats_to_rgba_clean(color);
|
||
|
set_iv_server(param_type, 1, &rgba);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case VG_GLYPH_ORIGIN:
|
||
|
{
|
||
|
/*
|
||
|
don't store on client as it can change
|
||
|
*/
|
||
|
|
||
|
VGfloat glyph_origin[2];
|
||
|
if (count != 2) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
(void)params_to_floats(
|
||
|
glyph_origin,
|
||
|
are_floats, values, count); /* ignore return -- it doesn't mean anything in this case */
|
||
|
set_fv_server(VG_GLYPH_ORIGIN, 2, glyph_origin);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void get_ifv(
|
||
|
CLIENT_THREAD_STATE_T *thread,
|
||
|
VG_CLIENT_STATE_T *state,
|
||
|
VGParamType param_type,
|
||
|
VGint count,
|
||
|
bool are_floats,
|
||
|
void *values)
|
||
|
{
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
scalar param types
|
||
|
*/
|
||
|
|
||
|
#define CASE(PARAM_TYPE, FN, VALUE) \
|
||
|
case PARAM_TYPE: \
|
||
|
{ \
|
||
|
if (count > 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; } \
|
||
|
FN(are_floats, values, 0, VALUE); \
|
||
|
break; \
|
||
|
}
|
||
|
#define CASE_I(PARAM_TYPE, VALUE) CASE(PARAM_TYPE, int_to_param, VALUE)
|
||
|
#define CASE_F(PARAM_TYPE, VALUE) CASE(PARAM_TYPE, float_to_param, VALUE)
|
||
|
CASE_I(VG_MATRIX_MODE, state->matrix_mode)
|
||
|
CASE_I(VG_FILL_RULE, state->fill_rule)
|
||
|
CASE_I(VG_IMAGE_QUALITY, state->image_quality)
|
||
|
CASE_I(VG_RENDERING_QUALITY, state->rendering_quality)
|
||
|
CASE_I(VG_BLEND_MODE, state->blend_mode)
|
||
|
CASE_I(VG_IMAGE_MODE, state->image_mode)
|
||
|
CASE_I(VG_COLOR_TRANSFORM, state->color_transform)
|
||
|
CASE_F(VG_STROKE_LINE_WIDTH, state->stroke_line_width)
|
||
|
CASE_I(VG_STROKE_CAP_STYLE, state->stroke_cap_style)
|
||
|
CASE_I(VG_STROKE_JOIN_STYLE, state->stroke_join_style)
|
||
|
CASE_F(VG_STROKE_MITER_LIMIT, state->stroke_miter_limit)
|
||
|
CASE_F(VG_STROKE_DASH_PHASE, state->stroke_dash_phase)
|
||
|
CASE_I(VG_STROKE_DASH_PHASE_RESET, state->stroke_dash_phase_reset)
|
||
|
CASE_I(VG_MASKING, state->masking)
|
||
|
CASE_I(VG_SCISSORING, state->scissoring)
|
||
|
CASE_I(VG_PIXEL_LAYOUT, state->pixel_layout)
|
||
|
CASE_I(VG_SCREEN_LAYOUT, VG_CONFIG_SCREEN_LAYOUT)
|
||
|
CASE_I(VG_FILTER_FORMAT_LINEAR, state->filter_format_linear)
|
||
|
CASE_I(VG_FILTER_FORMAT_PREMULTIPLIED, state->filter_format_pre)
|
||
|
CASE_I(VG_FILTER_CHANNEL_MASK, state->filter_channel_mask)
|
||
|
CASE_I(VG_MAX_SCISSOR_RECTS, VG_CONFIG_MAX_SCISSOR_RECTS)
|
||
|
CASE_I(VG_MAX_DASH_COUNT, VG_CONFIG_MAX_DASH_COUNT)
|
||
|
CASE_I(VG_MAX_KERNEL_SIZE, VG_CONFIG_MAX_KERNEL_SIZE)
|
||
|
CASE_I(VG_MAX_SEPARABLE_KERNEL_SIZE, VG_CONFIG_MAX_SEPARABLE_KERNEL_SIZE)
|
||
|
CASE_I(VG_MAX_COLOR_RAMP_STOPS, VG_CONFIG_MAX_COLOR_RAMP_STOPS)
|
||
|
CASE_I(VG_MAX_IMAGE_WIDTH, VG_CONFIG_MAX_IMAGE_WIDTH)
|
||
|
CASE_I(VG_MAX_IMAGE_HEIGHT, VG_CONFIG_MAX_IMAGE_HEIGHT)
|
||
|
CASE_I(VG_MAX_IMAGE_PIXELS, VG_CONFIG_MAX_IMAGE_PIXELS)
|
||
|
CASE_I(VG_MAX_IMAGE_BYTES, VG_CONFIG_MAX_IMAGE_BYTES)
|
||
|
CASE_F(VG_MAX_FLOAT, VG_CONFIG_MAX_FLOAT)
|
||
|
CASE_F(VG_MAX_GAUSSIAN_STD_DEVIATION, VG_CONFIG_MAX_GAUSSIAN_STD_DEVIATION)
|
||
|
#undef CASE_F
|
||
|
#undef CASE_I
|
||
|
#undef CASE
|
||
|
|
||
|
/*
|
||
|
vector param types
|
||
|
*/
|
||
|
|
||
|
case VG_SCISSOR_RECTS:
|
||
|
{
|
||
|
if ((VGuint)count > state->scissor_rects_count) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
ints_to_params(
|
||
|
are_floats, values, count,
|
||
|
state->scissor_rects);
|
||
|
break;
|
||
|
}
|
||
|
case VG_COLOR_TRANSFORM_VALUES:
|
||
|
{
|
||
|
if (count > 8) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
floats_to_params(
|
||
|
are_floats, values, count,
|
||
|
state->color_transform_values);
|
||
|
break;
|
||
|
}
|
||
|
case VG_STROKE_DASH_PATTERN:
|
||
|
{
|
||
|
if ((VGuint)count > state->stroke_dash_pattern_count) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
floats_to_params(
|
||
|
are_floats, values, count,
|
||
|
state->stroke_dash_pattern);
|
||
|
break;
|
||
|
}
|
||
|
case VG_TILE_FILL_COLOR:
|
||
|
case VG_CLEAR_COLOR:
|
||
|
{
|
||
|
if (count > 4) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
floats_to_params(
|
||
|
are_floats, values, count,
|
||
|
(param_type == VG_TILE_FILL_COLOR) ? state->tile_fill_color : state->clear_color);
|
||
|
break;
|
||
|
}
|
||
|
case VG_GLYPH_ORIGIN:
|
||
|
{
|
||
|
VGfloat glyph_origin[2];
|
||
|
if (count > 2) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
get_fv_server(VG_GLYPH_ORIGIN, 2, glyph_origin);
|
||
|
floats_to_params(
|
||
|
are_floats, values, count,
|
||
|
glyph_origin);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void set_parameter_ifv(
|
||
|
CLIENT_THREAD_STATE_T *thread,
|
||
|
VG_CLIENT_STATE_T *state,
|
||
|
VGHandle vg_handle,
|
||
|
VGint param_type,
|
||
|
VGint count,
|
||
|
bool are_floats,
|
||
|
const void *values)
|
||
|
{
|
||
|
/*
|
||
|
* If 'count' is greater than zero, then our caller must have verified
|
||
|
* that 'values' is non-NULL.
|
||
|
* If 'count' is zero, then 'values' *must not* be dereferenced.
|
||
|
* Note that params_to_floats() and params_to_ints() handle a zero 'count'
|
||
|
* argument correctly and do not dereference 'values' in this case.
|
||
|
* Other functions, e.g. param_to_int() and param_to_float(), require a
|
||
|
* check before calling.
|
||
|
*/
|
||
|
void *object;
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
|
||
|
object = khrn_pointer_map_lookup(&state->shared_state->objects, nice_handle(vg_handle));
|
||
|
if (!object) {
|
||
|
set_error(VG_BAD_HANDLE_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch (*(VG_CLIENT_OBJECT_TYPE_T *)object) {
|
||
|
case VG_CLIENT_OBJECT_TYPE_PATH:
|
||
|
{
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
read-only scalar param types
|
||
|
*/
|
||
|
|
||
|
case VG_PATH_FORMAT:
|
||
|
case VG_PATH_DATATYPE:
|
||
|
case VG_PATH_SCALE:
|
||
|
case VG_PATH_BIAS:
|
||
|
case VG_PATH_NUM_SEGMENTS:
|
||
|
case VG_PATH_NUM_COORDS:
|
||
|
{
|
||
|
if (count != 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); }
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case VG_CLIENT_OBJECT_TYPE_PAINT:
|
||
|
{
|
||
|
VG_CLIENT_PAINT_T *paint = (VG_CLIENT_PAINT_T *)object;
|
||
|
VGint value_i;
|
||
|
|
||
|
if (!paint->gradient &&
|
||
|
need_paint_gradient(param_type) &&
|
||
|
!paint_alloc_gradient(paint)) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
value_i = (count == 1) ? param_to_int(are_floats, values, 0) : 0;
|
||
|
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
settable scalar param types
|
||
|
*/
|
||
|
|
||
|
#define CASE_I_SERVER(PARAM_TYPE, OK, VALUE, FN) \
|
||
|
case PARAM_TYPE: \
|
||
|
{ \
|
||
|
if ((count != 1) || !(OK)) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; } \
|
||
|
if (VALUE != FN(value_i)) { \
|
||
|
VALUE = FN(value_i); \
|
||
|
set_parameter_iv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT, PARAM_TYPE, 1, &value_i); \
|
||
|
} \
|
||
|
break; \
|
||
|
}
|
||
|
CASE_I_SERVER(VG_PAINT_TYPE, is_paint_type((VGPaintType)value_i), paint->type, (VGPaintType))
|
||
|
CASE_I_SERVER(VG_PAINT_COLOR_RAMP_SPREAD_MODE, is_color_ramp_spread_mode((VGColorRampSpreadMode)value_i), paint->gradient->ramp_spread_mode, (VGColorRampSpreadMode))
|
||
|
CASE_I_SERVER(VG_PAINT_COLOR_RAMP_PREMULTIPLIED, true, paint->gradient->ramp_pre, clean_boolean)
|
||
|
CASE_I_SERVER(VG_PAINT_PATTERN_TILING_MODE, is_tiling_mode((VGTilingMode)value_i), paint->pattern_tiling_mode, (VGTilingMode))
|
||
|
#undef CASE_I_SERVER
|
||
|
|
||
|
/*
|
||
|
vector param types
|
||
|
*/
|
||
|
|
||
|
case VG_PAINT_COLOR:
|
||
|
{
|
||
|
if (count != 4) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
if (params_to_floats(
|
||
|
paint->color,
|
||
|
are_floats, values, count)) {
|
||
|
VGint rgba = color_floats_to_rgba_clean(paint->color);
|
||
|
set_parameter_iv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT, VG_PAINT_COLOR, 1, &rgba);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case VG_PAINT_COLOR_RAMP_STOPS:
|
||
|
{
|
||
|
if ((count % 5) != 0) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
count = _min(count, VG_CONFIG_MAX_COLOR_RAMP_STOPS * 5);
|
||
|
if (paint->gradient->ramp_stops_count != count) {
|
||
|
VGfloat *ramp_stops = NULL;
|
||
|
if (count != 0) {
|
||
|
ramp_stops = (VGfloat *)khrn_platform_malloc(count * sizeof(VGfloat), "VG_CLIENT_PAINT_GRADIENT_T.ramp_stops");
|
||
|
if (!ramp_stops) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (paint->gradient->ramp_stops) {
|
||
|
khrn_platform_free(paint->gradient->ramp_stops);
|
||
|
}
|
||
|
paint->gradient->ramp_stops = ramp_stops;
|
||
|
}
|
||
|
if (params_to_floats(
|
||
|
paint->gradient->ramp_stops,
|
||
|
are_floats, values, count) ||
|
||
|
(paint->gradient->ramp_stops_count != count)) {
|
||
|
paint->gradient->ramp_stops_count = count;
|
||
|
set_parameter_fv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT, VG_PAINT_COLOR_RAMP_STOPS, count, paint->gradient->ramp_stops);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case VG_PAINT_LINEAR_GRADIENT:
|
||
|
{
|
||
|
if (count != 4) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
if (params_to_floats(
|
||
|
paint->gradient->linear,
|
||
|
are_floats, values, count)) {
|
||
|
set_parameter_fv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT, VG_PAINT_LINEAR_GRADIENT, 4, paint->gradient->linear);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case VG_PAINT_RADIAL_GRADIENT:
|
||
|
{
|
||
|
if (count != 5) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
if (params_to_floats(
|
||
|
paint->gradient->radial,
|
||
|
are_floats, values, count)) {
|
||
|
set_parameter_fv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT, VG_PAINT_RADIAL_GRADIENT, 5, paint->gradient->radial);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case VG_CLIENT_OBJECT_TYPE_IMAGE:
|
||
|
{
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
read-only scalar param types
|
||
|
*/
|
||
|
|
||
|
case VG_IMAGE_FORMAT:
|
||
|
case VG_IMAGE_WIDTH:
|
||
|
case VG_IMAGE_HEIGHT:
|
||
|
{
|
||
|
if (count != 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); }
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case VG_CLIENT_OBJECT_TYPE_MASK_LAYER:
|
||
|
{
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
read-only scalar param types
|
||
|
*/
|
||
|
|
||
|
case VG_IMAGE_WIDTH:
|
||
|
case VG_IMAGE_HEIGHT:
|
||
|
{
|
||
|
if (count != 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); }
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case VG_CLIENT_OBJECT_TYPE_FONT:
|
||
|
{
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
read-only scalar param types
|
||
|
*/
|
||
|
|
||
|
case VG_FONT_NUM_GLYPHS:
|
||
|
{
|
||
|
if (count != 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); }
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
UNREACHABLE();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
}
|
||
|
|
||
|
static void get_parameter_ifv(
|
||
|
CLIENT_THREAD_STATE_T *thread,
|
||
|
VG_CLIENT_STATE_T *state,
|
||
|
VGHandle vg_handle,
|
||
|
VGint param_type,
|
||
|
VGint count,
|
||
|
bool are_floats,
|
||
|
void *values)
|
||
|
{
|
||
|
void *object;
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
|
||
|
object = khrn_pointer_map_lookup(&state->shared_state->objects, nice_handle(vg_handle));
|
||
|
if (!object) {
|
||
|
set_error(VG_BAD_HANDLE_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#define CASE(PARAM_TYPE, FN, VALUE) \
|
||
|
case PARAM_TYPE: \
|
||
|
{ \
|
||
|
if (count > 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; } \
|
||
|
FN(are_floats, values, 0, VALUE); \
|
||
|
break; \
|
||
|
}
|
||
|
#define CASE_I(PARAM_TYPE, VALUE) CASE(PARAM_TYPE, int_to_param, VALUE)
|
||
|
#define CASE_F(PARAM_TYPE, VALUE) CASE(PARAM_TYPE, float_to_param, VALUE)
|
||
|
|
||
|
switch (*(VG_CLIENT_OBJECT_TYPE_T *)object) {
|
||
|
case VG_CLIENT_OBJECT_TYPE_PATH:
|
||
|
{
|
||
|
VG_CLIENT_PATH_T *path = (VG_CLIENT_PATH_T *)object;
|
||
|
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
scalar param types
|
||
|
*/
|
||
|
|
||
|
CASE_I(VG_PATH_FORMAT, path->format)
|
||
|
CASE_I(VG_PATH_DATATYPE, path->datatype)
|
||
|
CASE_F(VG_PATH_SCALE, path->scale)
|
||
|
CASE_F(VG_PATH_BIAS, path->bias)
|
||
|
case VG_PATH_NUM_SEGMENTS:
|
||
|
case VG_PATH_NUM_COORDS:
|
||
|
{
|
||
|
if (count > 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
if (need_path_segments(path->caps)) {
|
||
|
int_to_param(are_floats, values, 0, (param_type == VG_PATH_NUM_SEGMENTS) ?
|
||
|
path->segments.size :
|
||
|
get_coords_count((const VGubyte *)path->segments.data, path->segments.size));
|
||
|
} else {
|
||
|
VGint value;
|
||
|
if (get_parameter_iv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_PATH, param_type, 1, &value)) {
|
||
|
int_to_param(are_floats, values, 0, value);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case VG_CLIENT_OBJECT_TYPE_PAINT:
|
||
|
{
|
||
|
VG_CLIENT_PAINT_T *paint = (VG_CLIENT_PAINT_T *)object;
|
||
|
|
||
|
if (!paint->gradient &&
|
||
|
need_paint_gradient(param_type) &&
|
||
|
!paint_alloc_gradient(paint)) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
scalar param types
|
||
|
*/
|
||
|
|
||
|
CASE_I(VG_PAINT_TYPE, paint->type)
|
||
|
CASE_I(VG_PAINT_COLOR_RAMP_SPREAD_MODE, paint->gradient->ramp_spread_mode)
|
||
|
CASE_I(VG_PAINT_COLOR_RAMP_PREMULTIPLIED, paint->gradient->ramp_pre)
|
||
|
CASE_I(VG_PAINT_PATTERN_TILING_MODE, paint->pattern_tiling_mode)
|
||
|
|
||
|
/*
|
||
|
vector param types
|
||
|
*/
|
||
|
|
||
|
case VG_PAINT_COLOR:
|
||
|
{
|
||
|
if (count > 4) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
floats_to_params(
|
||
|
are_floats, values, count,
|
||
|
paint->color);
|
||
|
break;
|
||
|
}
|
||
|
case VG_PAINT_COLOR_RAMP_STOPS:
|
||
|
{
|
||
|
if ((VGuint)count > paint->gradient->ramp_stops_count) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
floats_to_params(
|
||
|
are_floats, values, count,
|
||
|
paint->gradient->ramp_stops);
|
||
|
break;
|
||
|
}
|
||
|
case VG_PAINT_LINEAR_GRADIENT:
|
||
|
{
|
||
|
if (count > 4) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
floats_to_params(
|
||
|
are_floats, values, count,
|
||
|
paint->gradient->linear);
|
||
|
break;
|
||
|
}
|
||
|
case VG_PAINT_RADIAL_GRADIENT:
|
||
|
{
|
||
|
if (count > 5) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
floats_to_params(
|
||
|
are_floats, values, count,
|
||
|
paint->gradient->radial);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case VG_CLIENT_OBJECT_TYPE_IMAGE:
|
||
|
{
|
||
|
VG_CLIENT_IMAGE_T *image = (VG_CLIENT_IMAGE_T *)object;
|
||
|
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
scalar param types
|
||
|
*/
|
||
|
|
||
|
CASE_I(VG_IMAGE_FORMAT, image->format)
|
||
|
CASE_I(VG_IMAGE_WIDTH, image->width)
|
||
|
CASE_I(VG_IMAGE_HEIGHT, image->height)
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case VG_CLIENT_OBJECT_TYPE_MASK_LAYER:
|
||
|
{
|
||
|
VG_CLIENT_MASK_LAYER_T *mask_layer = (VG_CLIENT_MASK_LAYER_T *)object;
|
||
|
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
scalar param types
|
||
|
*/
|
||
|
|
||
|
CASE_I(VG_IMAGE_WIDTH, mask_layer->width)
|
||
|
CASE_I(VG_IMAGE_HEIGHT, mask_layer->height)
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case VG_CLIENT_OBJECT_TYPE_FONT:
|
||
|
{
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
scalar param types
|
||
|
*/
|
||
|
|
||
|
case VG_FONT_NUM_GLYPHS:
|
||
|
{
|
||
|
VGint glyphs_count;
|
||
|
if (count > 1) { set_error(VG_ILLEGAL_ARGUMENT_ERROR); break; }
|
||
|
if (get_parameter_iv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_FONT, VG_FONT_NUM_GLYPHS, 1, &glyphs_count)) {
|
||
|
int_to_param(are_floats, values, 0, glyphs_count);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
UNREACHABLE();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#undef CASE_F
|
||
|
#undef CASE_I
|
||
|
#undef CASE
|
||
|
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgSetf(
|
||
|
VGParamType param_type,
|
||
|
VGfloat value) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (is_vector_param_type(param_type)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
set_ifv(thread, state, param_type, 1, true, &value);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgSeti(
|
||
|
VGParamType param_type,
|
||
|
VGint value) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (is_vector_param_type(param_type)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
set_ifv(thread, state, param_type, 1, false, &value);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgSetfv(
|
||
|
VGParamType param_type,
|
||
|
VGint count,
|
||
|
const VGfloat *values) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Verify the arguments, and report an error if any of the following holds:
|
||
|
* - 'count' is negative
|
||
|
* - 'count' is greater than zero and 'values' is NULL
|
||
|
* - 'values' is non-NULL but not aligned on an float boundary
|
||
|
*
|
||
|
* If 'count' is zero, then 'values' will not (and must not) be dereferenced
|
||
|
* in this function or any of it's descendents. The OpenVG Specification
|
||
|
* Version 1.1, section 5.2 Setting and Querying Context Parameter Values
|
||
|
* states:
|
||
|
*
|
||
|
* "If the count parameter is 0, the pointer argument is not dereferenced.
|
||
|
* For example, the call vgSet(VG_STROKE_DASH_PATTERN, 0, (void *) 0)
|
||
|
* sets the dash pattern to a zero-length array (which has the effect of
|
||
|
* disabling dashing) without dereferencing the third parameter."
|
||
|
*
|
||
|
* Automatic checking tools such as Coverity may flag this case ('values'
|
||
|
* not verified non-NULL when 'count' is zero) as a potential NULL pointer
|
||
|
* dereference. In this case it is not.
|
||
|
*/
|
||
|
if ((count < 0) || ((count > 0) && !values) || (values && !is_aligned_float(values))) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
set_ifv(thread, state, param_type, count, true, values);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgSetiv(
|
||
|
VGParamType param_type,
|
||
|
VGint count,
|
||
|
const VGint *values) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Verify the arguments, and report an error if any of the following holds:
|
||
|
* - 'count' is negative
|
||
|
* - 'count' is greater than zero and 'values' is NULL
|
||
|
* - 'values' is non-NULL but not aligned on an integer boundary
|
||
|
*
|
||
|
* If 'count' is zero, then 'values' will not (and must not) be dereferenced
|
||
|
* in this function or any of it's descendents. See the comment in vgSetfv()
|
||
|
* above for more detail.
|
||
|
*
|
||
|
* Automatic checking tools such as Coverity may flag this case ('values'
|
||
|
* not verified non-NULL when 'count' is zero) as a potential NULL pointer
|
||
|
* dereference. In this case it is not.
|
||
|
*/
|
||
|
if ((count < 0) || ((count > 0) && !values) || (values && !is_aligned_int(values))) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
set_ifv(thread, state, param_type, count, false, values);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL VGfloat VG_API_ENTRY vgGetf(
|
||
|
VGParamType param_type) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VGfloat value = 0.0f;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return 0.0f;
|
||
|
}
|
||
|
|
||
|
if (is_vector_param_type(param_type)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return 0.0f;
|
||
|
}
|
||
|
|
||
|
get_ifv(thread, state, param_type, 1, true, &value);
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL VGint VG_API_ENTRY vgGeti(
|
||
|
VGParamType param_type) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VGint value = 0;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (is_vector_param_type(param_type)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
get_ifv(thread, state, param_type, 1, false, &value);
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL VGint VG_API_ENTRY vgGetVectorSize(
|
||
|
VGParamType param_type) VG_API_EXIT
|
||
|
{
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(CLIENT_GET_THREAD_STATE());
|
||
|
if (!state) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
scalar param types
|
||
|
*/
|
||
|
|
||
|
case VG_MATRIX_MODE:
|
||
|
case VG_FILL_RULE:
|
||
|
case VG_IMAGE_QUALITY:
|
||
|
case VG_RENDERING_QUALITY:
|
||
|
case VG_BLEND_MODE:
|
||
|
case VG_IMAGE_MODE:
|
||
|
case VG_COLOR_TRANSFORM:
|
||
|
case VG_STROKE_LINE_WIDTH:
|
||
|
case VG_STROKE_CAP_STYLE:
|
||
|
case VG_STROKE_JOIN_STYLE:
|
||
|
case VG_STROKE_MITER_LIMIT:
|
||
|
case VG_STROKE_DASH_PHASE:
|
||
|
case VG_STROKE_DASH_PHASE_RESET:
|
||
|
case VG_MASKING:
|
||
|
case VG_SCISSORING:
|
||
|
case VG_PIXEL_LAYOUT:
|
||
|
case VG_SCREEN_LAYOUT:
|
||
|
case VG_FILTER_FORMAT_LINEAR:
|
||
|
case VG_FILTER_FORMAT_PREMULTIPLIED:
|
||
|
case VG_FILTER_CHANNEL_MASK:
|
||
|
case VG_MAX_SCISSOR_RECTS:
|
||
|
case VG_MAX_DASH_COUNT:
|
||
|
case VG_MAX_KERNEL_SIZE:
|
||
|
case VG_MAX_SEPARABLE_KERNEL_SIZE:
|
||
|
case VG_MAX_COLOR_RAMP_STOPS:
|
||
|
case VG_MAX_IMAGE_WIDTH:
|
||
|
case VG_MAX_IMAGE_HEIGHT:
|
||
|
case VG_MAX_IMAGE_PIXELS:
|
||
|
case VG_MAX_IMAGE_BYTES:
|
||
|
case VG_MAX_FLOAT:
|
||
|
case VG_MAX_GAUSSIAN_STD_DEVIATION: return 1;
|
||
|
|
||
|
/*
|
||
|
vector param types
|
||
|
*/
|
||
|
|
||
|
case VG_SCISSOR_RECTS: return state->scissor_rects_count;
|
||
|
case VG_COLOR_TRANSFORM_VALUES: return 8;
|
||
|
case VG_STROKE_DASH_PATTERN: return state->stroke_dash_pattern_count;
|
||
|
case VG_TILE_FILL_COLOR:
|
||
|
case VG_CLEAR_COLOR: return 4;
|
||
|
case VG_GLYPH_ORIGIN: return 2;
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default: set_error(VG_ILLEGAL_ARGUMENT_ERROR); return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgGetfv(
|
||
|
VGParamType param_type,
|
||
|
VGint count,
|
||
|
VGfloat *values) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((count <= 0) || !values || !is_aligned_float(values)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
get_ifv(thread, state, param_type, count, true, values);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgGetiv(
|
||
|
VGParamType param_type,
|
||
|
VGint count,
|
||
|
VGint *values) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((count <= 0) || !values || !is_aligned_int(values)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
get_ifv(thread, state, param_type, count, false, values);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgSetParameterf(
|
||
|
VGHandle vg_handle,
|
||
|
VGint param_type,
|
||
|
VGfloat value) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (is_vector_object_param_type(param_type)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
set_parameter_ifv(thread, state, vg_handle, param_type, 1, true, &value);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgSetParameteri(
|
||
|
VGHandle vg_handle,
|
||
|
VGint param_type,
|
||
|
VGint value) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (is_vector_object_param_type(param_type)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
set_parameter_ifv(thread, state, vg_handle, param_type, 1, false, &value);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgSetParameterfv(
|
||
|
VGHandle vg_handle,
|
||
|
VGint param_type,
|
||
|
VGint count,
|
||
|
const VGfloat *values) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Verify the arguments, and report an error if any of the following holds:
|
||
|
* - 'count' is negative
|
||
|
* - 'count' is greater than zero and 'values' is NULL
|
||
|
* - 'values' is non-NULL but not aligned on an float boundary
|
||
|
*
|
||
|
* If 'count' is zero, then 'values' will not (and must not) be dereferenced
|
||
|
* in this function or any of it's descendents. See the comment in vgSetfv()
|
||
|
* above for more detail.
|
||
|
*
|
||
|
* Automatic checking tools such as Coverity may flag this case ('values'
|
||
|
* not verified non-NULL when 'count' is zero) as a potential NULL pointer
|
||
|
* dereference. In this case it is not.
|
||
|
*/
|
||
|
if ((count < 0) || ((count > 0) && !values) || (values && !is_aligned_float(values))) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
set_parameter_ifv(thread, state, vg_handle, param_type, count, true, values);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgSetParameteriv(
|
||
|
VGHandle vg_handle,
|
||
|
VGint param_type,
|
||
|
VGint count,
|
||
|
const VGint *values) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Verify the arguments, and report an error if any of the following holds:
|
||
|
* - 'count' is negative
|
||
|
* - 'count' is greater than zero and 'values' is NULL
|
||
|
* - 'values' is non-NULL but not aligned on an integer boundary
|
||
|
*
|
||
|
* If 'count' is zero, then 'values' will not (and must not) be dereferenced
|
||
|
* in this function or any of it's descendents. See the comment in vgSetfv()
|
||
|
* above for more detail.
|
||
|
*
|
||
|
* Automatic checking tools such as Coverity may flag this case ('values'
|
||
|
* not verified non-NULL when 'count' is zero) as a potential NULL pointer
|
||
|
* dereference. In this case it is not.
|
||
|
*/
|
||
|
if ((count < 0) || ((count > 0) && !values) || (values && !is_aligned_int(values))) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
set_parameter_ifv(thread, state, vg_handle, param_type, count, false, values);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL VGfloat VG_API_ENTRY vgGetParameterf(
|
||
|
VGHandle vg_handle,
|
||
|
VGint param_type) VG_API_EXIT
|
||
|
{
|
||
|
VGfloat value = 0.0f;
|
||
|
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return 0.0f;
|
||
|
}
|
||
|
|
||
|
if (is_vector_object_param_type(param_type)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return 0.0f;
|
||
|
}
|
||
|
|
||
|
get_parameter_ifv(thread, state, vg_handle, param_type, 1, true, &value);
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL VGint VG_API_ENTRY vgGetParameteri(
|
||
|
VGHandle vg_handle,
|
||
|
VGint param_type) VG_API_EXIT
|
||
|
{
|
||
|
VGint value = 0;
|
||
|
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (is_vector_object_param_type(param_type)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
get_parameter_ifv(thread, state, vg_handle, param_type, 1, false, &value);
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL VGint VG_API_ENTRY vgGetParameterVectorSize(
|
||
|
VGHandle vg_handle,
|
||
|
VGint param_type) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
void *object;
|
||
|
VGint count = 0;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
|
||
|
object = khrn_pointer_map_lookup(&state->shared_state->objects, nice_handle(vg_handle));
|
||
|
if (!object) {
|
||
|
set_error(VG_BAD_HANDLE_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
switch (*(VG_CLIENT_OBJECT_TYPE_T *)object) {
|
||
|
case VG_CLIENT_OBJECT_TYPE_PATH:
|
||
|
{
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
scalar param types
|
||
|
*/
|
||
|
|
||
|
case VG_PATH_FORMAT:
|
||
|
case VG_PATH_DATATYPE:
|
||
|
case VG_PATH_SCALE:
|
||
|
case VG_PATH_BIAS:
|
||
|
case VG_PATH_NUM_SEGMENTS:
|
||
|
case VG_PATH_NUM_COORDS:
|
||
|
{
|
||
|
count = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case VG_CLIENT_OBJECT_TYPE_PAINT:
|
||
|
{
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
scalar param types
|
||
|
*/
|
||
|
|
||
|
case VG_PAINT_TYPE:
|
||
|
case VG_PAINT_COLOR_RAMP_SPREAD_MODE:
|
||
|
case VG_PAINT_COLOR_RAMP_PREMULTIPLIED:
|
||
|
case VG_PAINT_PATTERN_TILING_MODE:
|
||
|
{
|
||
|
count = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
vector param types
|
||
|
*/
|
||
|
|
||
|
case VG_PAINT_COLOR:
|
||
|
{
|
||
|
count = 4;
|
||
|
break;
|
||
|
}
|
||
|
case VG_PAINT_COLOR_RAMP_STOPS:
|
||
|
{
|
||
|
VG_CLIENT_PAINT_T *paint = (VG_CLIENT_PAINT_T *)object;
|
||
|
count = paint->gradient ? paint->gradient->ramp_stops_count : 0;
|
||
|
break;
|
||
|
}
|
||
|
case VG_PAINT_LINEAR_GRADIENT:
|
||
|
{
|
||
|
count = 4;
|
||
|
break;
|
||
|
}
|
||
|
case VG_PAINT_RADIAL_GRADIENT:
|
||
|
{
|
||
|
count = 5;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case VG_CLIENT_OBJECT_TYPE_IMAGE:
|
||
|
{
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
scalar param types
|
||
|
*/
|
||
|
|
||
|
case VG_IMAGE_FORMAT:
|
||
|
case VG_IMAGE_WIDTH:
|
||
|
case VG_IMAGE_HEIGHT:
|
||
|
{
|
||
|
count = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case VG_CLIENT_OBJECT_TYPE_MASK_LAYER:
|
||
|
{
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
scalar param types
|
||
|
*/
|
||
|
|
||
|
case VG_IMAGE_WIDTH:
|
||
|
case VG_IMAGE_HEIGHT:
|
||
|
{
|
||
|
count = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case VG_CLIENT_OBJECT_TYPE_FONT:
|
||
|
{
|
||
|
switch (param_type) {
|
||
|
/*
|
||
|
scalar param types
|
||
|
*/
|
||
|
|
||
|
case VG_FONT_NUM_GLYPHS:
|
||
|
{
|
||
|
count = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
invalid param type
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
{
|
||
|
UNREACHABLE();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgGetParameterfv(
|
||
|
VGHandle vg_handle,
|
||
|
VGint param_type,
|
||
|
VGint count,
|
||
|
VGfloat *values) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((count <= 0) || !values || !is_aligned_float(values)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
get_parameter_ifv(thread, state, vg_handle, param_type, count, true, values);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgGetParameteriv(
|
||
|
VGHandle vg_handle,
|
||
|
VGint param_type,
|
||
|
VGint count,
|
||
|
VGint *values) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((count <= 0) || !values || !is_aligned_int(values)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
get_parameter_ifv(thread, state, vg_handle, param_type, count, false, values);
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
api matrices
|
||
|
******************************************************************************/
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgLoadIdentity(void) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_MAT3X3_SYNC_T *matrix_sync;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
matrix_sync = get_matrix_sync(state, state->matrix_mode);
|
||
|
vg_mat3x3_set_identity(&matrix_sync->client);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgLoadMatrix(const VGfloat *matrix) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_MAT3X3_SYNC_T *matrix_sync;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!matrix || !is_aligned_float(matrix)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
matrix_sync = get_matrix_sync(state, state->matrix_mode);
|
||
|
vg_mat3x3_set_clean(&matrix_sync->client, matrix, is_matrix_affine(state->matrix_mode));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgGetMatrix(VGfloat *matrix) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!matrix || !is_aligned_float(matrix)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
vg_mat3x3_get(&get_matrix_sync(state, state->matrix_mode)->client, matrix);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgMultMatrix(const VGfloat *matrix) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_MAT3X3_T a;
|
||
|
VG_MAT3X3_SYNC_T *matrix_sync;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!matrix || !is_aligned_float(matrix)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
vg_mat3x3_set_clean(&a, matrix, is_matrix_affine(state->matrix_mode));
|
||
|
|
||
|
matrix_sync = get_matrix_sync(state, state->matrix_mode);
|
||
|
vg_mat3x3_postmul(&matrix_sync->client, &a);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgTranslate(VGfloat x, VGfloat y) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
VG_MAT3X3_SYNC_T *matrix_sync;
|
||
|
|
||
|
x = clean_float(x);
|
||
|
y = clean_float(y);
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
matrix_sync = get_matrix_sync(state, state->matrix_mode);
|
||
|
vg_mat3x3_postmul_translate(&matrix_sync->client, x, y);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgScale(VGfloat x, VGfloat y) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
VG_MAT3X3_SYNC_T *matrix_sync;
|
||
|
|
||
|
x = clean_float(x);
|
||
|
y = clean_float(y);
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
matrix_sync = get_matrix_sync(state, state->matrix_mode);
|
||
|
vg_mat3x3_postmul_scale(&matrix_sync->client, x, y);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgShear(VGfloat x, VGfloat y) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
VG_MAT3X3_SYNC_T *matrix_sync;
|
||
|
|
||
|
x = clean_float(x);
|
||
|
y = clean_float(y);
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
matrix_sync = get_matrix_sync(state, state->matrix_mode);
|
||
|
vg_mat3x3_postmul_shear(&matrix_sync->client, x, y);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgRotate(VGfloat angle) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
VG_MAT3X3_SYNC_T *matrix_sync;
|
||
|
|
||
|
angle = clean_float(angle);
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
matrix_sync = get_matrix_sync(state, state->matrix_mode);
|
||
|
vg_mat3x3_postmul_rotate(&matrix_sync->client, angle * (PI / 180.0f));
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
api mask/clear
|
||
|
******************************************************************************/
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgMask(
|
||
|
VGHandle vg_handle, /* theoretically image under vg 1.0 */
|
||
|
VGMaskOperation operation,
|
||
|
VGint dst_x, VGint dst_y,
|
||
|
VGint width, VGint height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RPC_CALL6(vgMask_impl,
|
||
|
thread,
|
||
|
VGMASK_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_ENUM(operation),
|
||
|
RPC_INT(dst_x),
|
||
|
RPC_INT(dst_y),
|
||
|
RPC_INT(width),
|
||
|
RPC_INT(height));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgRenderToMask(
|
||
|
VGPath vg_handle,
|
||
|
VGbitfield paint_modes,
|
||
|
VGMaskOperation operation) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
|
||
|
#ifdef VG_NO_STROKING
|
||
|
paint_modes &= ~VG_STROKE_PATH;
|
||
|
if (!paint_modes) { return; }
|
||
|
#endif
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sync_matrix(state, VG_MATRIX_PATH_USER_TO_SURFACE);
|
||
|
|
||
|
RPC_CALL3(vgRenderToMask_impl,
|
||
|
thread,
|
||
|
VGRENDERTOMASK_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_BITFIELD(paint_modes),
|
||
|
RPC_ENUM(operation));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL VGMaskLayer VG_API_ENTRY vgCreateMaskLayer(
|
||
|
VGint width, VGint height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VGHandle vg_handle;
|
||
|
VG_CLIENT_MASK_LAYER_T *mask_layer;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
if ((width <= 0) || (height <= 0) ||
|
||
|
(width > VG_CONFIG_MAX_IMAGE_WIDTH) || (height > VG_CONFIG_MAX_IMAGE_HEIGHT) ||
|
||
|
((width * height) > VG_CONFIG_MAX_IMAGE_PIXELS)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
if (egl_config_get_mask_format(egl_config_to_id(
|
||
|
CLIENT_GET_THREAD_STATE()->openvg.draw->config)) == IMAGE_FORMAT_INVALID) {
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
vg_handle = get_stem(state);
|
||
|
if (vg_handle == VG_INVALID_HANDLE) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
mask_layer = mask_layer_alloc(width, height);
|
||
|
if (!mask_layer) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
destroy_stem(vg_handle);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
if (!insert_object(state, vg_handle, mask_layer)) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
mask_layer_free(mask_layer);
|
||
|
destroy_stem(vg_handle);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL3(vgCreateMaskLayer_impl,
|
||
|
thread,
|
||
|
VGCREATEMASKLAYER_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_INT(width),
|
||
|
RPC_INT(height));
|
||
|
|
||
|
return vg_handle;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgDestroyMaskLayer(
|
||
|
VGMaskLayer vg_handle) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
delete_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_MASK_LAYER);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL1(vgDestroyMaskLayer_impl,
|
||
|
thread,
|
||
|
VGDESTROYMASKLAYER_ID,
|
||
|
RPC_HANDLE(vg_handle));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgFillMaskLayer(
|
||
|
VGMaskLayer vg_handle,
|
||
|
VGint x, VGint y,
|
||
|
VGint width, VGint height,
|
||
|
VGfloat value) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
value = clean_float(value);
|
||
|
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RPC_CALL6(vgFillMaskLayer_impl,
|
||
|
thread,
|
||
|
VGFILLMASKLAYER_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_INT(x), RPC_INT(y),
|
||
|
RPC_INT(width), RPC_INT(height),
|
||
|
RPC_FLOAT(value));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgCopyMask(
|
||
|
VGMaskLayer dst_vg_handle,
|
||
|
VGint dst_x, VGint dst_y,
|
||
|
VGint src_x, VGint src_y,
|
||
|
VGint width, VGint height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RPC_CALL7(vgCopyMask_impl,
|
||
|
thread,
|
||
|
VGCOPYMASK_ID,
|
||
|
RPC_HANDLE(dst_vg_handle),
|
||
|
RPC_INT(dst_x), RPC_INT(dst_y),
|
||
|
RPC_INT(src_x), RPC_INT(src_y),
|
||
|
RPC_INT(width), RPC_INT(height));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgClear(
|
||
|
VGint x, VGint y,
|
||
|
VGint width, VGint height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//TODO: pixmap behaviour can be better optimized to handle clears
|
||
|
if (state->render_callback)
|
||
|
state->render_callback();
|
||
|
|
||
|
RPC_CALL4(vgClear_impl,
|
||
|
thread,
|
||
|
VGCLEAR_ID,
|
||
|
RPC_INT(x),
|
||
|
RPC_INT(y),
|
||
|
RPC_INT(width),
|
||
|
RPC_INT(height));
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
api path
|
||
|
******************************************************************************/
|
||
|
|
||
|
VG_API_CALL VGPath VG_API_ENTRY vgCreatePath(
|
||
|
VGint format,
|
||
|
VGPathDatatype datatype,
|
||
|
VGfloat scale, VGfloat bias,
|
||
|
VGint segments_capacity,
|
||
|
VGint coords_capacity,
|
||
|
VGbitfield caps) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
VGHandle vg_handle;
|
||
|
VG_CLIENT_PATH_T *path;
|
||
|
|
||
|
scale = clean_float(scale);
|
||
|
bias = clean_float(bias);
|
||
|
caps &= VG_PATH_CAPABILITY_ALL;
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
if (!is_path_format(format)) {
|
||
|
set_error(VG_UNSUPPORTED_PATH_FORMAT_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
if (!is_path_datatype(datatype) || is_zero(scale)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
vg_handle = get_stem(state);
|
||
|
if (vg_handle == VG_INVALID_HANDLE) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
path = path_alloc(format, datatype, scale, bias, caps, segments_capacity);
|
||
|
if (!path) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
destroy_stem(vg_handle);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
if (!insert_object(state, vg_handle, path)) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
path_free(path);
|
||
|
destroy_stem(vg_handle);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL8(vgCreatePath_impl,
|
||
|
thread,
|
||
|
VGCREATEPATH_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_INT(format),
|
||
|
RPC_ENUM(datatype),
|
||
|
RPC_FLOAT(scale), RPC_FLOAT(bias),
|
||
|
RPC_INT(segments_capacity),
|
||
|
RPC_INT(coords_capacity),
|
||
|
RPC_BITFIELD(caps));
|
||
|
|
||
|
return vg_handle;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgClearPath(
|
||
|
VGPath vg_handle,
|
||
|
VGbitfield caps) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
VG_CLIENT_PATH_T *path;
|
||
|
|
||
|
caps &= VG_PATH_CAPABILITY_ALL;
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
if (path) {
|
||
|
if (need_path_segments(path->caps) && need_path_segments(caps)) {
|
||
|
khrn_vector_clear(&path->segments);
|
||
|
}
|
||
|
path_update_caps(path, caps);
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL2(vgClearPath_impl,
|
||
|
thread,
|
||
|
VGCLEARPATH_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_BITFIELD(caps));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgDestroyPath(
|
||
|
VGPath vg_handle) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
delete_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL1(vgDestroyPath_impl,
|
||
|
thread,
|
||
|
VGDESTROYPATH_ID,
|
||
|
RPC_HANDLE(vg_handle));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgRemovePathCapabilities(
|
||
|
VGPath vg_handle,
|
||
|
VGbitfield caps) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_PATH_T *path;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
if (path) {
|
||
|
path_update_caps(path, path->caps & ~caps);
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL2(vgRemovePathCapabilities_impl,
|
||
|
thread,
|
||
|
VGREMOVEPATHCAPABILITIES_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_BITFIELD(caps));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL VGbitfield VG_API_ENTRY vgGetPathCapabilities(
|
||
|
VGPath vg_handle) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VGbitfield caps = 0;
|
||
|
VG_CLIENT_PATH_T *path;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
if (path) {
|
||
|
caps = path->caps;
|
||
|
} else {
|
||
|
set_error(VG_BAD_HANDLE_ERROR);
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
return caps;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgAppendPath(
|
||
|
VGPath dst_vg_handle,
|
||
|
VGPath src_vg_handle) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_PATH_T *dst;
|
||
|
VG_CLIENT_PATH_T *src;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
dst = (VG_CLIENT_PATH_T *)lookup_object(state, dst_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
src = (VG_CLIENT_PATH_T *)lookup_object(state, src_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
if (dst && src &&
|
||
|
(dst->caps & VG_PATH_CAPABILITY_APPEND_TO) && (src->caps & VG_PATH_CAPABILITY_APPEND_FROM) &&
|
||
|
need_path_segments(dst->caps)) {
|
||
|
VGuint segments_count = src->segments.size;
|
||
|
if (!khrn_vector_extend(&dst->segments, segments_count)) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
memcpy((VGubyte *)dst->segments.data + (dst->segments.size - segments_count), src->segments.data, segments_count);
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL2(vgAppendPath_impl,
|
||
|
thread,
|
||
|
VGAPPENDPATH_ID,
|
||
|
RPC_HANDLE(dst_vg_handle),
|
||
|
RPC_HANDLE(src_vg_handle));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgAppendPathData(
|
||
|
VGPath vg_handle,
|
||
|
VGint segments_count, const VGubyte *segments,
|
||
|
const void *coords) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_PATH_T *path;
|
||
|
VGPathDatatype datatype;
|
||
|
VGuint datatype_size;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
|
||
|
path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
if (!path) {
|
||
|
set_error(VG_BAD_HANDLE_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
datatype = path->datatype;
|
||
|
|
||
|
if ((segments_count <= 0) || !segments ||
|
||
|
contains_illegal_segment(segments, segments_count) ||
|
||
|
!coords || !is_aligned_path_datatype(coords, datatype)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!(path->caps & VG_PATH_CAPABILITY_APPEND_TO)) {
|
||
|
set_error(VG_PATH_CAPABILITY_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (need_path_segments(path->caps)) {
|
||
|
if (!khrn_vector_extend(&path->segments, segments_count)) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
memcpy((VGubyte *)path->segments.data + (path->segments.size - segments_count), segments, segments_count);
|
||
|
}
|
||
|
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
datatype_size = get_path_datatype_size(datatype);
|
||
|
|
||
|
#ifdef RPC_DIRECT
|
||
|
{
|
||
|
VGuint coords_count = 0;
|
||
|
VGuint i;
|
||
|
for (i = 0; i != segments_count; ++i) {
|
||
|
coords_count += get_segment_coords_count(segments[i] & ~VG_RELATIVE);
|
||
|
}
|
||
|
|
||
|
RPC_CALL6(vgAppendPathData_impl, thread, no_id,
|
||
|
vg_handle,
|
||
|
datatype,
|
||
|
segments_count, segments,
|
||
|
coords_count * datatype_size, coords);
|
||
|
}
|
||
|
#else
|
||
|
/*
|
||
|
split into multiple calls if necessary to avoid overflowing control buffer
|
||
|
*/
|
||
|
|
||
|
while (segments_count != 0) {
|
||
|
#define MESSAGE_SIZE 20
|
||
|
|
||
|
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 */
|
||
|
VGint chunk_segments_count = 0;
|
||
|
VGuint chunk_coords_size = 0;
|
||
|
for (; chunk_segments_count != segments_count; ++chunk_segments_count) {
|
||
|
VGuint segment_coords_size = get_segment_coords_count(segments[chunk_segments_count] & ~VG_RELATIVE) * datatype_size;
|
||
|
if ((rpc_pad_ctrl(chunk_segments_count + 1) +
|
||
|
rpc_pad_ctrl(chunk_coords_size + segment_coords_size)) > size_max) {
|
||
|
/*
|
||
|
can't fit this segment in
|
||
|
*/
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
chunk_coords_size += segment_coords_size;
|
||
|
}
|
||
|
|
||
|
{
|
||
|
uint32_t message[] = {
|
||
|
VGAPPENDPATHDATA_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_ENUM(datatype),
|
||
|
RPC_INT(chunk_segments_count),
|
||
|
RPC_UINT(chunk_coords_size) };
|
||
|
vcos_static_assert(sizeof(message) == MESSAGE_SIZE);
|
||
|
|
||
|
#undef MESSAGE_SIZE
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
|
||
|
rpc_send_ctrl_begin(thread, sizeof(message) + rpc_pad_ctrl(chunk_segments_count) + rpc_pad_ctrl(chunk_coords_size));
|
||
|
rpc_send_ctrl_write(thread, message, sizeof(message));
|
||
|
rpc_send_ctrl_write(thread, (uint32_t *)segments, chunk_segments_count);
|
||
|
rpc_send_ctrl_write(thread, (uint32_t *)coords, chunk_coords_size);
|
||
|
rpc_send_ctrl_end(thread);
|
||
|
}
|
||
|
|
||
|
segments_count -= chunk_segments_count;
|
||
|
segments += chunk_segments_count;
|
||
|
coords = (const uint8_t *)coords + chunk_coords_size;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgModifyPathCoords(
|
||
|
VGPath vg_handle,
|
||
|
VGint segments_i, VGint segments_count,
|
||
|
const void *coords) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_PATH_T *path;
|
||
|
VGPathDatatype datatype;
|
||
|
VGuint datatype_size;
|
||
|
VGuint coords_offset;
|
||
|
VGuint coords_size;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
|
||
|
path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
if (!path) {
|
||
|
set_error(VG_BAD_HANDLE_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!(path->caps & VG_PATH_CAPABILITY_MODIFY)) {
|
||
|
set_error(VG_PATH_CAPABILITY_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
datatype = path->datatype;
|
||
|
|
||
|
if ((segments_i < 0) || (segments_count <= 0) ||
|
||
|
/* will fit in VGuint (may not fit in VGint) */
|
||
|
(((VGuint)(segments_i + segments_count)) > path->segments.size) ||
|
||
|
!coords || !is_aligned_path_datatype(coords, datatype)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
datatype_size = get_path_datatype_size(path->datatype);
|
||
|
coords_offset = get_coords_count((const VGubyte *)path->segments.data, segments_i) * datatype_size;
|
||
|
coords_size = get_coords_count((const VGubyte *)path->segments.data + segments_i, segments_count) * datatype_size;
|
||
|
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
#ifdef RPC_DIRECT
|
||
|
RPC_CALL5(vgModifyPathCoords_impl, thread, no_id,
|
||
|
vg_handle,
|
||
|
datatype,
|
||
|
coords_offset, coords_size, coords);
|
||
|
#else
|
||
|
/*
|
||
|
split into multiple calls if necessary to avoid overflowing control buffer
|
||
|
*/
|
||
|
|
||
|
while (coords_size != 0) {
|
||
|
#define MESSAGE_SIZE 20
|
||
|
|
||
|
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 */
|
||
|
|
||
|
uint32_t message[] = {
|
||
|
VGMODIFYPATHCOORDS_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_ENUM(datatype),
|
||
|
RPC_UINT(coords_offset),
|
||
|
RPC_UINT(chunk_coords_size) };
|
||
|
vcos_static_assert(sizeof(message) == MESSAGE_SIZE);
|
||
|
|
||
|
#undef MESSAGE_SIZE
|
||
|
|
||
|
rpc_send_ctrl_begin(thread, sizeof(message) + rpc_pad_ctrl(chunk_coords_size));
|
||
|
rpc_send_ctrl_write(thread, message, sizeof(message));
|
||
|
rpc_send_ctrl_write(thread, (uint32_t *)coords, chunk_coords_size);
|
||
|
rpc_send_ctrl_end(thread);
|
||
|
|
||
|
coords_size -= chunk_coords_size;
|
||
|
coords_offset += chunk_coords_size;
|
||
|
coords = (const uint8_t *)coords + chunk_coords_size;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgTransformPath(
|
||
|
VGPath dst_vg_handle,
|
||
|
VGPath src_vg_handle) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_PATH_T *dst;
|
||
|
VG_CLIENT_PATH_T *src;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
dst = (VG_CLIENT_PATH_T *)lookup_object(state, dst_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
src = (VG_CLIENT_PATH_T *)lookup_object(state, src_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
if (dst && src &&
|
||
|
(dst->caps & VG_PATH_CAPABILITY_TRANSFORM_TO) && (src->caps & VG_PATH_CAPABILITY_TRANSFORM_FROM) &&
|
||
|
need_path_segments(dst->caps)) {
|
||
|
VGuint segments_count = src->segments.size;
|
||
|
if (!khrn_vector_extend(&dst->segments, segments_count)) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
{
|
||
|
VGubyte *dst_segments = (VGubyte *)dst->segments.data + (dst->segments.size - segments_count);
|
||
|
const VGubyte *src_segments = (const VGubyte *)src->segments.data;
|
||
|
for (; segments_count != 0; ++dst_segments, ++src_segments, --segments_count) {
|
||
|
VGuint segment = *src_segments;
|
||
|
if (((segment & ~VG_RELATIVE) == VG_HLINE_TO) || ((segment & ~VG_RELATIVE) == VG_VLINE_TO)) {
|
||
|
segment = VG_LINE_TO | (segment & VG_RELATIVE);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
on the server, we also flip arc types if the determinant of the path
|
||
|
user to surface matrix is negative, but we don't actually care about
|
||
|
the particular arc type on the client
|
||
|
*/
|
||
|
|
||
|
*dst_segments = (VGubyte)segment;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
sync_matrix(state, VG_MATRIX_PATH_USER_TO_SURFACE);
|
||
|
|
||
|
RPC_CALL2(vgTransformPath_impl,
|
||
|
thread,
|
||
|
VGTRANSFORMPATH_ID,
|
||
|
RPC_HANDLE(dst_vg_handle),
|
||
|
RPC_HANDLE(src_vg_handle));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL VGboolean VG_API_ENTRY vgInterpolatePath(
|
||
|
VGPath dst_vg_handle,
|
||
|
VGPath begin_vg_handle,
|
||
|
VGPath end_vg_handle,
|
||
|
VGfloat t) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
VG_CLIENT_PATH_T *dst;
|
||
|
VG_CLIENT_PATH_T *begin;
|
||
|
VG_CLIENT_PATH_T *end;
|
||
|
|
||
|
t = clean_float(t);
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VG_FALSE;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
dst = (VG_CLIENT_PATH_T *)lookup_object(state, dst_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
begin = (VG_CLIENT_PATH_T *)lookup_object(state, begin_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
end = (VG_CLIENT_PATH_T *)lookup_object(state, end_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
if (dst && begin && end &&
|
||
|
(dst->caps & VG_PATH_CAPABILITY_INTERPOLATE_TO) && (begin->caps & VG_PATH_CAPABILITY_INTERPOLATE_FROM) && (end->caps & VG_PATH_CAPABILITY_INTERPOLATE_FROM)) {
|
||
|
if ((begin->segments.size != end->segments.size) ||
|
||
|
!interpolate_segments_compatible((const VGubyte *)begin->segments.data, (const VGubyte *)end->segments.data, begin->segments.size)) {
|
||
|
/*
|
||
|
incompatible paths. no error -- just return false
|
||
|
*/
|
||
|
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return VG_FALSE;
|
||
|
}
|
||
|
if (need_path_segments(dst->caps)) {
|
||
|
VGuint segments_count = begin->segments.size;
|
||
|
if (!khrn_vector_extend(&dst->segments, segments_count)) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return VG_FALSE;
|
||
|
}
|
||
|
{
|
||
|
VGubyte *dst_segments = (VGubyte *)dst->segments.data + (dst->segments.size - segments_count);
|
||
|
const VGubyte *begin_segments = (const VGubyte *)begin->segments.data;
|
||
|
for (; segments_count != 0; ++dst_segments, ++begin_segments, --segments_count) {
|
||
|
*dst_segments = (VGubyte)normalise_segment(*begin_segments & ~VG_RELATIVE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL4(vgInterpolatePath_impl,
|
||
|
thread,
|
||
|
VGINTERPOLATEPATH_ID,
|
||
|
RPC_HANDLE(dst_vg_handle),
|
||
|
RPC_HANDLE(begin_vg_handle),
|
||
|
RPC_HANDLE(end_vg_handle),
|
||
|
RPC_FLOAT(t));
|
||
|
|
||
|
return VG_TRUE;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL VGfloat VG_API_ENTRY vgPathLength(
|
||
|
VGPath vg_handle,
|
||
|
VGint segments_i, VGint segments_count) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return 0.0f;
|
||
|
}
|
||
|
|
||
|
return RPC_FLOAT_RES(RPC_CALL3_RES(vgPathLength_impl,
|
||
|
thread,
|
||
|
VGPATHLENGTH_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_INT(segments_i), RPC_INT(segments_count)));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgPointAlongPath(
|
||
|
VGPath vg_handle,
|
||
|
VGint segments_i, VGint segments_count,
|
||
|
VGfloat distance,
|
||
|
VGfloat *x, VGfloat *y,
|
||
|
VGfloat *tangent_x, VGfloat *tangent_y) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VGfloat values[4];
|
||
|
|
||
|
distance = clean_float(distance);
|
||
|
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((x && !is_aligned_float(x)) || (y && !is_aligned_float(y)) ||
|
||
|
(tangent_x && !is_aligned_float(tangent_x)) || (tangent_y && !is_aligned_float(tangent_y))) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (RPC_BOOLEAN_RES(RPC_CALL6_OUT_CTRL_RES(vgPointAlongPath_impl,
|
||
|
thread,
|
||
|
VGPOINTALONGPATH_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_INT(segments_i), RPC_INT(segments_count),
|
||
|
RPC_FLOAT(distance),
|
||
|
RPC_BITFIELD(((x && y) << 0) | ((tangent_x && tangent_y) << 1)),
|
||
|
values))) {
|
||
|
/*
|
||
|
no error occurred on the server side
|
||
|
*/
|
||
|
|
||
|
if (x && y) {
|
||
|
*x = values[0];
|
||
|
*y = values[1];
|
||
|
}
|
||
|
if (tangent_x && tangent_y) {
|
||
|
*tangent_x = values[2];
|
||
|
*tangent_y = values[3];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgPathBounds(
|
||
|
VGPath vg_handle,
|
||
|
VGfloat *min_x, VGfloat *min_y,
|
||
|
VGfloat *width, VGfloat *height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VGfloat values[4];
|
||
|
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!min_x || !is_aligned_float(min_x) || !min_y || !is_aligned_float(min_y) ||
|
||
|
!width || !is_aligned_float(width) || !height || !is_aligned_float(height)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (RPC_BOOLEAN_RES(RPC_CALL2_OUT_CTRL_RES(vgPathBounds_impl,
|
||
|
thread,
|
||
|
VGPATHBOUNDS_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
values))) {
|
||
|
/*
|
||
|
no error occurred on the server side
|
||
|
*/
|
||
|
|
||
|
*min_x = values[0];
|
||
|
*min_y = values[1];
|
||
|
*width = values[2];
|
||
|
*height = values[3];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgPathTransformedBounds(
|
||
|
VGPath vg_handle,
|
||
|
VGfloat *min_x, VGfloat *min_y,
|
||
|
VGfloat *width, VGfloat *height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VGfloat values[4];
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!min_x || !is_aligned_float(min_x) || !min_y || !is_aligned_float(min_y) ||
|
||
|
!width || !is_aligned_float(width) || !height || !is_aligned_float(height)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sync_matrix(state, VG_MATRIX_PATH_USER_TO_SURFACE);
|
||
|
|
||
|
if (RPC_BOOLEAN_RES(RPC_CALL2_OUT_CTRL_RES(vgPathTransformedBounds_impl,
|
||
|
thread,
|
||
|
VGPATHTRANSFORMEDBOUNDS_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
values))) {
|
||
|
/*
|
||
|
no error occurred on the server side
|
||
|
*/
|
||
|
|
||
|
*min_x = values[0];
|
||
|
*min_y = values[1];
|
||
|
*width = values[2];
|
||
|
*height = values[3];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgDrawPath(
|
||
|
VGPath vg_handle,
|
||
|
VGbitfield paint_modes) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
|
||
|
#ifdef VG_NO_STROKING
|
||
|
paint_modes &= ~VG_STROKE_PATH;
|
||
|
if (!paint_modes) { return; }
|
||
|
#endif
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sync_matrix(state, VG_MATRIX_PATH_USER_TO_SURFACE);
|
||
|
if (paint_modes & VG_FILL_PATH) {
|
||
|
sync_matrix(state, VG_MATRIX_FILL_PAINT_TO_USER);
|
||
|
}
|
||
|
if (paint_modes & VG_STROKE_PATH) {
|
||
|
sync_matrix(state, VG_MATRIX_STROKE_PAINT_TO_USER);
|
||
|
}
|
||
|
|
||
|
if (state->render_callback)
|
||
|
state->render_callback();
|
||
|
|
||
|
RPC_CALL2(vgDrawPath_impl,
|
||
|
thread,
|
||
|
VGDRAWPATH_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_BITFIELD(paint_modes));
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
api paint
|
||
|
******************************************************************************/
|
||
|
|
||
|
VG_API_CALL VGPaint VG_API_ENTRY vgCreatePaint(void) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VGHandle vg_handle;
|
||
|
VG_CLIENT_PAINT_T *paint;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
vg_handle = get_stem(state);
|
||
|
if (vg_handle == VG_INVALID_HANDLE) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
paint = paint_alloc();
|
||
|
if (!paint) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
destroy_stem(vg_handle);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
if (!insert_object(state, vg_handle, paint)) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
paint_free(paint);
|
||
|
destroy_stem(vg_handle);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL1(vgCreatePaint_impl,
|
||
|
thread,
|
||
|
VGCREATEPAINT_ID,
|
||
|
RPC_HANDLE(vg_handle));
|
||
|
|
||
|
return vg_handle;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgDestroyPaint(
|
||
|
VGPaint vg_handle) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
delete_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL1(vgDestroyPaint_impl,
|
||
|
thread,
|
||
|
VGDESTROYPAINT_ID,
|
||
|
RPC_HANDLE(vg_handle));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgSetPaint(
|
||
|
VGPaint vg_handle,
|
||
|
VGbitfield paint_modes) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!is_paint_modes(paint_modes)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
if ((vg_handle != VG_INVALID_HANDLE) &&
|
||
|
!lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT)) {
|
||
|
set_error(VG_BAD_HANDLE_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
if (((paint_modes & VG_FILL_PATH) && (state->fill_paint != vg_handle)) ||
|
||
|
((paint_modes & VG_STROKE_PATH) && (state->stroke_paint != vg_handle))) {
|
||
|
if (paint_modes & VG_FILL_PATH) {
|
||
|
state->fill_paint = vg_handle;
|
||
|
}
|
||
|
if (paint_modes & VG_STROKE_PATH) {
|
||
|
state->stroke_paint = vg_handle;
|
||
|
}
|
||
|
RPC_CALL2(vgSetPaint_impl,
|
||
|
thread,
|
||
|
VGSETPAINT_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_BITFIELD(paint_modes));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VG_API_CALL VGPaint VG_API_ENTRY vgGetPaint(
|
||
|
VGPaintMode paint_mode) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
if (!is_paint_mode(paint_mode)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
return (paint_mode == VG_FILL_PATH) ? state->fill_paint : state->stroke_paint;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgSetColor(
|
||
|
VGPaint vg_handle,
|
||
|
VGuint rgba) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
VG_CLIENT_PAINT_T *paint;
|
||
|
|
||
|
rgba = khrn_color_rgba_flip(rgba);
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
paint = (VG_CLIENT_PAINT_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT);
|
||
|
if (!paint) {
|
||
|
set_error(VG_BAD_HANDLE_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
if (color_floats_to_rgba_clean(paint->color) != rgba) {
|
||
|
set_parameter_iv_server(vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT, VG_PAINT_COLOR, 1, (const VGint *)&rgba);
|
||
|
}
|
||
|
khrn_color_rgba_to_floats(paint->color, rgba);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL VGuint VG_API_ENTRY vgGetColor(
|
||
|
VGPaint vg_handle) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_PAINT_T *paint;
|
||
|
VGuint rgba;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
paint = (VG_CLIENT_PAINT_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT);
|
||
|
if (!paint) {
|
||
|
set_error(VG_BAD_HANDLE_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return 0;
|
||
|
}
|
||
|
rgba = color_floats_to_rgba_clean(paint->color);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return khrn_color_rgba_flip(rgba);
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgPaintPattern(
|
||
|
VGPaint vg_handle,
|
||
|
VGImage pattern_vg_handle) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_PAINT_T *paint;
|
||
|
VG_CLIENT_IMAGE_T *pattern;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
paint = (VG_CLIENT_PAINT_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PAINT);
|
||
|
pattern = (pattern_vg_handle == VG_INVALID_HANDLE) ? NULL : (VG_CLIENT_IMAGE_T *)lookup_object(state, pattern_vg_handle, VG_CLIENT_OBJECT_TYPE_IMAGE);
|
||
|
if (!paint || ((pattern_vg_handle != VG_INVALID_HANDLE) && !pattern)) {
|
||
|
set_error(VG_BAD_HANDLE_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
if (paint->pattern != pattern_vg_handle) {
|
||
|
paint->pattern = pattern_vg_handle;
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
if (pattern && (pattern->global_image_id[0] || pattern->global_image_id[1])) {
|
||
|
platform_acquire_global_image(pattern->global_image_id[0], pattern->global_image_id[1]);
|
||
|
}
|
||
|
if (paint->pattern_global_image_id[0] || paint->pattern_global_image_id[1]) {
|
||
|
platform_release_global_image(paint->pattern_global_image_id[0], paint->pattern_global_image_id[1]);
|
||
|
}
|
||
|
paint->pattern_global_image_id[0] = pattern ? pattern->global_image_id[0] : 0;
|
||
|
paint->pattern_global_image_id[1] = pattern ? pattern->global_image_id[1] : 0;
|
||
|
#endif
|
||
|
RPC_CALL2(vgPaintPattern_impl,
|
||
|
thread,
|
||
|
VGPAINTPATTERN_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_HANDLE(pattern_vg_handle));
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
api image
|
||
|
******************************************************************************/
|
||
|
|
||
|
VG_API_CALL VGImage VG_API_ENTRY vgCreateImage(
|
||
|
VGImageFormat format,
|
||
|
VGint width, VGint height,
|
||
|
VGbitfield allowed_quality) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VGHandle vg_handle;
|
||
|
VG_CLIENT_IMAGE_T *image;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
if (!is_allowed_quality(allowed_quality) ||
|
||
|
(width <= 0) || (height <= 0) ||
|
||
|
(width > VG_CONFIG_MAX_IMAGE_WIDTH) || (height > VG_CONFIG_MAX_IMAGE_HEIGHT) ||
|
||
|
((width * height) > VG_CONFIG_MAX_IMAGE_PIXELS)) { /* todo: VG_CONFIG_MAX_IMAGE_BYTES */
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
if (!is_image_format(format)) {
|
||
|
set_error(VG_UNSUPPORTED_IMAGE_FORMAT_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
vg_handle = get_stem(state);
|
||
|
if (vg_handle == VG_INVALID_HANDLE) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
image = image_alloc(format, width, height
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
, 0, 0
|
||
|
#endif
|
||
|
);
|
||
|
if (!image) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
destroy_stem(vg_handle);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
if (!insert_object(state, vg_handle, image)) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
image_free(image);
|
||
|
destroy_stem(vg_handle);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL5(vgCreateImage_impl,
|
||
|
thread,
|
||
|
VGCREATEIMAGE_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_ENUM(format),
|
||
|
RPC_INT(width), RPC_INT(height),
|
||
|
RPC_BITFIELD(allowed_quality));
|
||
|
|
||
|
return vg_handle;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgDestroyImage(
|
||
|
VGImage vg_handle) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
delete_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_IMAGE);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL1(vgDestroyImage_impl,
|
||
|
thread,
|
||
|
VGDESTROYIMAGE_ID,
|
||
|
RPC_HANDLE(vg_handle));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgClearImage(
|
||
|
VGImage vg_handle,
|
||
|
VGint x, VGint y,
|
||
|
VGint width, VGint height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RPC_CALL5(vgClearImage_impl,
|
||
|
thread,
|
||
|
VGCLEARIMAGE_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_INT(x), RPC_INT(y),
|
||
|
RPC_INT(width), RPC_INT(height));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgImageSubData(
|
||
|
VGImage vg_handle,
|
||
|
const void *data, VGint data_stride,
|
||
|
VGImageFormat data_format,
|
||
|
VGint dst_x, VGint dst_y,
|
||
|
VGint width, VGint height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_IMAGE_T *dst;
|
||
|
VGint dst_width;
|
||
|
VGint dst_height;
|
||
|
VGint src_x = 0, src_y = 0;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!is_image_format(data_format)) {
|
||
|
set_error(VG_UNSUPPORTED_IMAGE_FORMAT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!data || !is_aligned_image_format(data, data_format) ||
|
||
|
((height != 1) && !is_aligned_image_format((void *)(uintptr_t)data_stride, data_format)) ||
|
||
|
(width <= 0) || (height <= 0)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
|
||
|
dst = (VG_CLIENT_IMAGE_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_IMAGE);
|
||
|
if (!dst) {
|
||
|
set_error(VG_BAD_HANDLE_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dst_width = dst->width;
|
||
|
dst_height = dst->height;
|
||
|
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
khrn_clip_rect2(
|
||
|
&dst_x, &dst_y, &src_x, &src_y, &width, &height,
|
||
|
0, 0, dst_width, dst_height,
|
||
|
0, 0, width, height);
|
||
|
|
||
|
if (width <= 0 || height <= 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
data = (const VGubyte *)data + (src_y * data_stride);
|
||
|
|
||
|
#ifdef RPC_DIRECT
|
||
|
RPC_CALL11(vgImageSubData_impl, thread, no_id,
|
||
|
vg_handle,
|
||
|
dst_width, dst_height,
|
||
|
data, data_stride,
|
||
|
data_format,
|
||
|
src_x,
|
||
|
dst_x, dst_y,
|
||
|
width, height);
|
||
|
#else
|
||
|
{
|
||
|
VGuint log2_bpp = get_log2_image_format_bpp(data_format);
|
||
|
VGuint line_size;
|
||
|
VGuint chunk_height_max;
|
||
|
|
||
|
data = (const VGubyte *)data + ((src_x << log2_bpp) >> 3);
|
||
|
src_x = ((src_x << log2_bpp) & 0x7) >> log2_bpp;
|
||
|
|
||
|
line_size = (((src_x + width) << log2_bpp) + 0x7) >> 3;
|
||
|
|
||
|
/*
|
||
|
split into KHDISPATCH_WORKSPACE_SIZE chunks (or thereabouts)
|
||
|
*/
|
||
|
|
||
|
chunk_height_max = (line_size == 0) ? height : (KHDISPATCH_WORKSPACE_SIZE / line_size);
|
||
|
vcos_assert((height == 0) || (chunk_height_max != 0));
|
||
|
|
||
|
while (height != 0) {
|
||
|
VGint chunk_height = _min(height, chunk_height_max);
|
||
|
|
||
|
uint32_t message[] = {
|
||
|
VGIMAGESUBDATA_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_INT(dst_width), RPC_INT(dst_height),
|
||
|
RPC_UINT(line_size),
|
||
|
RPC_ENUM(data_format),
|
||
|
RPC_INT(src_x),
|
||
|
RPC_INT(dst_x), RPC_INT(dst_y),
|
||
|
RPC_INT(width), RPC_INT(chunk_height) };
|
||
|
|
||
|
rpc_begin(thread);
|
||
|
rpc_send_ctrl_begin(thread, sizeof(message));
|
||
|
rpc_send_ctrl_write(thread, message, sizeof(message));
|
||
|
rpc_send_ctrl_end(thread);
|
||
|
rpc_send_bulk_gather(thread, data, line_size, data_stride, chunk_height);
|
||
|
data = (const VGubyte *)data + (chunk_height * data_stride);
|
||
|
rpc_end(thread);
|
||
|
|
||
|
height -= chunk_height;
|
||
|
dst_y += chunk_height;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgGetImageSubData(
|
||
|
VGImage vg_handle,
|
||
|
void *data, VGint data_stride,
|
||
|
VGImageFormat data_format,
|
||
|
VGint src_x, VGint src_y,
|
||
|
VGint width, VGint height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_IMAGE_T *src;
|
||
|
VGint src_width;
|
||
|
VGint src_height;
|
||
|
VGint dst_x = 0, dst_y = 0;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!is_image_format(data_format)) {
|
||
|
set_error(VG_UNSUPPORTED_IMAGE_FORMAT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!data || !is_aligned_image_format(data, data_format) ||
|
||
|
((height != 1) && !is_aligned_image_format((void *)(uintptr_t)data_stride, data_format)) ||
|
||
|
(width <= 0) || (height <= 0)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
|
||
|
src = (VG_CLIENT_IMAGE_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_IMAGE);
|
||
|
if (!src) {
|
||
|
set_error(VG_BAD_HANDLE_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
src_width = src->width;
|
||
|
src_height = src->height;
|
||
|
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
khrn_clip_rect2(
|
||
|
&dst_x, &dst_y, &src_x, &src_y, &width, &height,
|
||
|
0, 0, width, height,
|
||
|
0, 0, src_width, src_height);
|
||
|
|
||
|
if (width <= 0 || height <= 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
data = (VGubyte *)data + (dst_y * data_stride);
|
||
|
|
||
|
#ifdef RPC_DIRECT
|
||
|
RPC_CALL11(vgGetImageSubData_impl, thread, no_id,
|
||
|
vg_handle,
|
||
|
src_width, src_height,
|
||
|
data, data_stride,
|
||
|
data_format,
|
||
|
dst_x,
|
||
|
src_x, src_y,
|
||
|
width, height);
|
||
|
#else
|
||
|
{
|
||
|
VGuint log2_bpp = get_log2_image_format_bpp(data_format);
|
||
|
VGuint line_size;
|
||
|
VGuint first_mask;
|
||
|
VGuint last_mask;
|
||
|
VGuint chunk_height_max;
|
||
|
|
||
|
data = (VGubyte *)data + ((dst_x << log2_bpp) >> 3);
|
||
|
dst_x = ((dst_x << log2_bpp) & 0x7) >> log2_bpp;
|
||
|
|
||
|
line_size = (((dst_x + width) << log2_bpp) + 0x7) >> 3;
|
||
|
first_mask = (1 << (dst_x << log2_bpp)) - 1;
|
||
|
last_mask = ~((2 << ((((dst_x + width) << log2_bpp) - 1) & 0x7)) - 1) & 0xff;
|
||
|
|
||
|
/*
|
||
|
split into KHDISPATCH_WORKSPACE_SIZE chunks (or thereabouts)
|
||
|
*/
|
||
|
|
||
|
chunk_height_max = (line_size == 0) ? height : (KHDISPATCH_WORKSPACE_SIZE / line_size);
|
||
|
vcos_assert((height == 0) || (chunk_height_max != 0));
|
||
|
|
||
|
while (height != 0) {
|
||
|
VGint chunk_height = _min(height, chunk_height_max);
|
||
|
|
||
|
uint32_t message[] = {
|
||
|
VGGETIMAGESUBDATA_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_INT(src_width), RPC_INT(src_height),
|
||
|
RPC_UINT(line_size),
|
||
|
RPC_ENUM(data_format),
|
||
|
RPC_INT(dst_x),
|
||
|
RPC_INT(src_x), RPC_INT(src_y),
|
||
|
RPC_INT(width), RPC_INT(chunk_height) };
|
||
|
|
||
|
rpc_begin(thread);
|
||
|
rpc_send_ctrl_begin(thread, sizeof(message));
|
||
|
rpc_send_ctrl_write(thread, message, sizeof(message));
|
||
|
rpc_send_ctrl_end(thread);
|
||
|
{
|
||
|
uint32_t len[] = { 0, data_stride, chunk_height, first_mask, last_mask };
|
||
|
rpc_recv(thread, data, len, (RPC_RECV_FLAG_T)(RPC_RECV_FLAG_BULK | RPC_RECV_FLAG_BULK_SCATTER | RPC_RECV_FLAG_LEN));
|
||
|
data = (VGubyte *)data + (chunk_height * data_stride);
|
||
|
if (len[0] == 0) { //The command has failed so no data sent back
|
||
|
rpc_end(thread);
|
||
|
break;
|
||
|
}
|
||
|
vcos_assert(len[0] == line_size);
|
||
|
}
|
||
|
rpc_end(thread);
|
||
|
height -= chunk_height;
|
||
|
src_y += chunk_height;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
VG_API_CALL VGImage VG_API_ENTRY vgChildImage(
|
||
|
VGImage parent_vg_handle,
|
||
|
VGint x, VGint y,
|
||
|
VGint width, VGint height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_IMAGE_T *parent;
|
||
|
VGImageFormat format;
|
||
|
VGint parent_width;
|
||
|
VGint parent_height;
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
VGuint parent_global_image_id_0, parent_global_image_id_1;
|
||
|
#endif
|
||
|
VGHandle vg_handle;
|
||
|
VG_CLIENT_IMAGE_T *image;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
|
||
|
parent = (VG_CLIENT_IMAGE_T *)lookup_object(state, parent_vg_handle, VG_CLIENT_OBJECT_TYPE_IMAGE);
|
||
|
if (!parent) {
|
||
|
set_error(VG_BAD_HANDLE_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
format = parent->format;
|
||
|
parent_width = parent->width;
|
||
|
parent_height = parent->height;
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
parent_global_image_id_0 = parent->global_image_id[0];
|
||
|
parent_global_image_id_1 = parent->global_image_id[1];
|
||
|
#endif
|
||
|
|
||
|
platform_mutex_release(&state->shared_state->mutex); /* mustn't hold this mutex when calling get_stem, so release now and acquire again later */
|
||
|
|
||
|
if ((x < 0) || (y < 0) || (width <= 0) || (height <= 0) ||
|
||
|
/* x + width / y + height will fit in VGuint (may not fit in VGint) */
|
||
|
(((VGuint)(x + width)) > (VGuint)parent_width) ||
|
||
|
(((VGuint)(y + height)) > (VGuint)parent_height)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
vg_handle = get_stem(state);
|
||
|
if (vg_handle == VG_INVALID_HANDLE) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
image = image_alloc(format, width, height
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
, parent_global_image_id_0, parent_global_image_id_1
|
||
|
#endif
|
||
|
);
|
||
|
if (!image) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
destroy_stem(vg_handle);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
if (!insert_object(state, vg_handle, image)) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
image_free(image);
|
||
|
destroy_stem(vg_handle);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL8(vgChildImage_impl,
|
||
|
thread,
|
||
|
VGCHILDIMAGE_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_HANDLE(parent_vg_handle),
|
||
|
RPC_INT(parent_width), RPC_INT(parent_height),
|
||
|
RPC_INT(x), RPC_INT(y),
|
||
|
RPC_INT(width), RPC_INT(height));
|
||
|
|
||
|
return vg_handle;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL VGImage VG_API_ENTRY vgGetParent(
|
||
|
VGImage vg_handle) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
return RPC_HANDLE_RES(RPC_CALL1_RES(vgGetParent_impl,
|
||
|
thread,
|
||
|
VGGETPARENT_ID,
|
||
|
RPC_HANDLE(vg_handle)));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgCopyImage(
|
||
|
VGImage dst_vg_handle, VGint dst_x, VGint dst_y,
|
||
|
VGImage src_vg_handle, VGint src_x, VGint src_y,
|
||
|
VGint width, VGint height,
|
||
|
VGboolean dither) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RPC_CALL9(vgCopyImage_impl,
|
||
|
thread,
|
||
|
VGCOPYIMAGE_ID,
|
||
|
RPC_HANDLE(dst_vg_handle), RPC_INT(dst_x), RPC_INT(dst_y),
|
||
|
RPC_HANDLE(src_vg_handle), RPC_INT(src_x), RPC_INT(src_y),
|
||
|
RPC_INT(width), RPC_INT(height),
|
||
|
RPC_BOOLEAN(clean_boolean(dither)));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgDrawImage(
|
||
|
VGImage vg_handle) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sync_matrix(state, VG_MATRIX_IMAGE_USER_TO_SURFACE);
|
||
|
sync_matrix(state, VG_MATRIX_FILL_PAINT_TO_USER);
|
||
|
|
||
|
if (state->render_callback)
|
||
|
state->render_callback();
|
||
|
|
||
|
RPC_CALL1(vgDrawImage_impl,
|
||
|
thread,
|
||
|
VGDRAWIMAGE_ID,
|
||
|
RPC_HANDLE(vg_handle));
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
api framebuffer
|
||
|
******************************************************************************/
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgSetPixels(
|
||
|
VGint dst_x, VGint dst_y,
|
||
|
VGImage src_vg_handle, VGint src_x, VGint src_y,
|
||
|
VGint width, VGint height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (state->render_callback)
|
||
|
state->render_callback();
|
||
|
|
||
|
RPC_CALL7(vgSetPixels_impl,
|
||
|
thread,
|
||
|
VGSETPIXELS_ID,
|
||
|
RPC_INT(dst_x), RPC_INT(dst_y),
|
||
|
RPC_HANDLE(src_vg_handle), RPC_INT(src_x), RPC_INT(src_y),
|
||
|
RPC_INT(width), RPC_INT(height));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgWritePixels(
|
||
|
const void *data, VGint data_stride,
|
||
|
VGImageFormat data_format,
|
||
|
VGint dst_x, VGint dst_y,
|
||
|
VGint width, VGint height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
EGL_SURFACE_T *frame;
|
||
|
VGint src_x = 0, src_y = 0;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!is_image_format(data_format)) {
|
||
|
set_error(VG_UNSUPPORTED_IMAGE_FORMAT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!data || !is_aligned_image_format(data, data_format) ||
|
||
|
((height != 1) && !is_aligned_image_format((void *)(uintptr_t)data_stride, data_format)) ||
|
||
|
(width <= 0) || (height <= 0)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
frame = CLIENT_GET_THREAD_STATE()->openvg.draw;
|
||
|
|
||
|
khrn_clip_rect2(
|
||
|
&dst_x, &dst_y, &src_x, &src_y, &width, &height,
|
||
|
0, 0, frame->width, frame->height,
|
||
|
0, 0, width, height);
|
||
|
|
||
|
if (width <= 0 || height <= 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
data = (const VGubyte *)data + (src_y * data_stride);
|
||
|
|
||
|
if (state->render_callback)
|
||
|
state->render_callback();
|
||
|
|
||
|
#ifdef RPC_DIRECT
|
||
|
RPC_CALL8(vgWritePixels_impl, thread, no_id,
|
||
|
data, data_stride,
|
||
|
data_format,
|
||
|
src_x,
|
||
|
dst_x, dst_y,
|
||
|
width, height);
|
||
|
#else
|
||
|
{
|
||
|
VGuint log2_bpp = get_log2_image_format_bpp(data_format);
|
||
|
VGuint line_size;
|
||
|
VGuint chunk_height_max;
|
||
|
|
||
|
data = (const VGubyte *)data + ((src_x << log2_bpp) >> 3);
|
||
|
src_x = ((src_x << log2_bpp) & 0x7) >> log2_bpp;
|
||
|
|
||
|
line_size = (((src_x + width) << log2_bpp) + 0x7) >> 3;
|
||
|
|
||
|
/*
|
||
|
split into KHDISPATCH_WORKSPACE_SIZE chunks (or thereabouts)
|
||
|
*/
|
||
|
|
||
|
chunk_height_max = (line_size == 0) ? height : (KHDISPATCH_WORKSPACE_SIZE / line_size);
|
||
|
vcos_assert((height == 0) || (chunk_height_max != 0));
|
||
|
|
||
|
while (height != 0) {
|
||
|
VGint chunk_height = _min(height, chunk_height_max);
|
||
|
|
||
|
uint32_t message[] = {
|
||
|
VGWRITEPIXELS_ID,
|
||
|
RPC_UINT(line_size),
|
||
|
RPC_ENUM(data_format),
|
||
|
RPC_INT(src_x),
|
||
|
RPC_INT(dst_x), RPC_INT(dst_y),
|
||
|
RPC_INT(width), RPC_INT(chunk_height) };
|
||
|
|
||
|
rpc_begin(thread);
|
||
|
rpc_send_ctrl_begin(thread, sizeof(message));
|
||
|
rpc_send_ctrl_write(thread, message, sizeof(message));
|
||
|
rpc_send_ctrl_end(thread);
|
||
|
rpc_send_bulk_gather(thread, data, line_size, data_stride, chunk_height);
|
||
|
data = (const VGubyte *)data + (chunk_height * data_stride);
|
||
|
rpc_end(thread);
|
||
|
|
||
|
height -= chunk_height;
|
||
|
dst_y += chunk_height;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgGetPixels(
|
||
|
VGImage dst_vg_handle, VGint dst_x, VGint dst_y,
|
||
|
VGint src_x, VGint src_y,
|
||
|
VGint width, VGint height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RPC_CALL7(vgGetPixels_impl,
|
||
|
thread,
|
||
|
VGGETPIXELS_ID,
|
||
|
RPC_HANDLE(dst_vg_handle), RPC_INT(dst_x), RPC_INT(dst_y),
|
||
|
RPC_INT(src_x), RPC_INT(src_y),
|
||
|
RPC_INT(width), RPC_INT(height));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgReadPixels(
|
||
|
void *data, VGint data_stride,
|
||
|
VGImageFormat data_format,
|
||
|
VGint src_x, VGint src_y,
|
||
|
VGint width, VGint height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
EGL_SURFACE_T *frame;
|
||
|
VGint dst_x = 0, dst_y = 0;
|
||
|
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!is_image_format(data_format)) {
|
||
|
set_error(VG_UNSUPPORTED_IMAGE_FORMAT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!data || !is_aligned_image_format(data, data_format) ||
|
||
|
((height != 1) && !is_aligned_image_format((void *)(uintptr_t)data_stride, data_format)) ||
|
||
|
(width <= 0) || (height <= 0)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
frame = CLIENT_GET_THREAD_STATE()->openvg.draw;
|
||
|
|
||
|
khrn_clip_rect2(
|
||
|
&dst_x, &dst_y, &src_x, &src_y, &width, &height,
|
||
|
0, 0, width, height,
|
||
|
0, 0, frame->width, frame->height);
|
||
|
|
||
|
if (width <= 0 || height <= 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
data = (VGubyte *)data + (dst_y * data_stride);
|
||
|
|
||
|
#ifdef RPC_DIRECT
|
||
|
RPC_CALL8(vgReadPixels_impl, thread, no_id,
|
||
|
data, data_stride,
|
||
|
data_format,
|
||
|
dst_x,
|
||
|
src_x, src_y,
|
||
|
width, height);
|
||
|
#else
|
||
|
{
|
||
|
VGuint log2_bpp = get_log2_image_format_bpp(data_format);
|
||
|
VGuint line_size;
|
||
|
VGuint first_mask;
|
||
|
VGuint last_mask;
|
||
|
VGuint chunk_height_max;
|
||
|
|
||
|
data = (VGubyte *)data + ((dst_x << log2_bpp) >> 3);
|
||
|
dst_x = ((dst_x << log2_bpp) & 0x7) >> log2_bpp;
|
||
|
|
||
|
line_size = (((dst_x + width) << log2_bpp) + 0x7) >> 3;
|
||
|
first_mask = (1 << (dst_x << log2_bpp)) - 1;
|
||
|
last_mask = ~((2 << ((((dst_x + width) << log2_bpp) - 1) & 0x7)) - 1) & 0xff;
|
||
|
|
||
|
/*
|
||
|
split into KHDISPATCH_WORKSPACE_SIZE chunks (or thereabouts)
|
||
|
*/
|
||
|
|
||
|
chunk_height_max = (line_size == 0) ? height : (KHDISPATCH_WORKSPACE_SIZE / line_size);
|
||
|
vcos_assert((height == 0) || (chunk_height_max != 0));
|
||
|
|
||
|
while (height != 0) {
|
||
|
VGint chunk_height = _min(height, chunk_height_max);
|
||
|
|
||
|
uint32_t message[] = {
|
||
|
VGREADPIXELS_ID,
|
||
|
RPC_UINT(line_size),
|
||
|
RPC_ENUM(data_format),
|
||
|
RPC_INT(dst_x),
|
||
|
RPC_INT(src_x), RPC_INT(src_y),
|
||
|
RPC_INT(width), RPC_INT(chunk_height) };
|
||
|
|
||
|
#ifndef __SYMBIAN32__
|
||
|
rpc_begin(thread);
|
||
|
#endif
|
||
|
rpc_send_ctrl_begin(thread, sizeof(message));
|
||
|
rpc_send_ctrl_write(thread, message, sizeof(message));
|
||
|
rpc_send_ctrl_end(thread);
|
||
|
{
|
||
|
uint32_t len[] = { line_size, data_stride, chunk_height, first_mask, last_mask };
|
||
|
rpc_recv(thread, data, len, (RPC_RECV_FLAG_T)(RPC_RECV_FLAG_BULK | RPC_RECV_FLAG_BULK_SCATTER));
|
||
|
data = (VGubyte *)data + (chunk_height * data_stride);
|
||
|
}
|
||
|
#ifndef __SYMBIAN32__
|
||
|
rpc_end(thread);
|
||
|
#endif
|
||
|
height -= chunk_height;
|
||
|
src_y += chunk_height;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgCopyPixels(
|
||
|
VGint dst_x, VGint dst_y,
|
||
|
VGint src_x, VGint src_y,
|
||
|
VGint width, VGint height) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (state->render_callback)
|
||
|
state->render_callback();
|
||
|
|
||
|
RPC_CALL6(vgCopyPixels_impl,
|
||
|
thread,
|
||
|
VGCOPYPIXELS_ID,
|
||
|
RPC_INT(dst_x), RPC_INT(dst_y),
|
||
|
RPC_INT(src_x), RPC_INT(src_y),
|
||
|
RPC_INT(width), RPC_INT(height));
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
api fonts
|
||
|
******************************************************************************/
|
||
|
|
||
|
VG_API_CALL VGFont VG_API_ENTRY vgCreateFont(
|
||
|
VGint glyphs_capacity) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VGHandle vg_handle;
|
||
|
VG_CLIENT_FONT_T *font;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
if (glyphs_capacity < 0) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
vg_handle = get_stem(state);
|
||
|
if (vg_handle == VG_INVALID_HANDLE) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
font = font_alloc();
|
||
|
if (!font) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
destroy_stem(vg_handle);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
if (!insert_object(state, vg_handle, font)) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
font_free(font);
|
||
|
destroy_stem(vg_handle);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL2(vgCreateFont_impl,
|
||
|
thread,
|
||
|
VGCREATEFONT_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_INT(glyphs_capacity));
|
||
|
|
||
|
return vg_handle;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgDestroyFont(
|
||
|
VGFont vg_handle) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
delete_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_FONT);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL1(vgDestroyFont_impl,
|
||
|
thread,
|
||
|
VGDESTROYFONT_ID,
|
||
|
RPC_HANDLE(vg_handle));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgSetGlyphToPath(
|
||
|
VGFont vg_handle,
|
||
|
VGuint glyph_id,
|
||
|
VGPath path_vg_handle,
|
||
|
VGboolean is_hinted,
|
||
|
VGfloat glyph_origin[2],
|
||
|
VGfloat escapement[2]) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
VG_CLIENT_FONT_T *font;
|
||
|
#endif
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!glyph_origin || !is_aligned_float(glyph_origin) ||
|
||
|
!escapement || !is_aligned_float(escapement)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
font = (VG_CLIENT_FONT_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_FONT);
|
||
|
if (font && ((path_vg_handle == VG_INVALID_HANDLE) || lookup_object(state, path_vg_handle, VG_CLIENT_OBJECT_TYPE_PATH))) {
|
||
|
khrn_global_image_map_delete(&font->glyph_global_images, glyph_id);
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
#endif
|
||
|
|
||
|
RPC_CALL8(vgSetGlyphToPath_impl,
|
||
|
thread,
|
||
|
VGSETGLYPHTOPATH_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_UINT(glyph_id),
|
||
|
RPC_HANDLE(path_vg_handle),
|
||
|
RPC_BOOLEAN(clean_boolean(is_hinted)),
|
||
|
RPC_FLOAT(clean_float(glyph_origin[0])), RPC_FLOAT(clean_float(glyph_origin[1])),
|
||
|
RPC_FLOAT(clean_float(escapement[0])), RPC_FLOAT(clean_float(escapement[1])));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgSetGlyphToImage(
|
||
|
VGFont vg_handle,
|
||
|
VGuint glyph_id,
|
||
|
VGImage image_vg_handle,
|
||
|
VGfloat glyph_origin[2],
|
||
|
VGfloat escapement[2]) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
VG_CLIENT_FONT_T *font;
|
||
|
VG_CLIENT_IMAGE_T *image;
|
||
|
#endif
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!glyph_origin || !is_aligned_float(glyph_origin) ||
|
||
|
!escapement || !is_aligned_float(escapement)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
font = (VG_CLIENT_FONT_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_FONT);
|
||
|
image = (image_vg_handle == VG_INVALID_HANDLE) ? NULL : (VG_CLIENT_IMAGE_T *)lookup_object(state, image_vg_handle, VG_CLIENT_OBJECT_TYPE_IMAGE);
|
||
|
if (font && ((image_vg_handle == VG_INVALID_HANDLE) || image)) {
|
||
|
if (image && (image->global_image_id[0] || image->global_image_id[1])) {
|
||
|
if (!khrn_global_image_map_insert(&font->glyph_global_images, glyph_id,
|
||
|
image->global_image_id[0] | ((uint64_t)image->global_image_id[1] << 32))) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return;
|
||
|
}
|
||
|
} else {
|
||
|
khrn_global_image_map_delete(&font->glyph_global_images, glyph_id);
|
||
|
}
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
#endif
|
||
|
|
||
|
RPC_CALL7(vgSetGlyphToImage_impl,
|
||
|
thread,
|
||
|
VGSETGLYPHTOIMAGE_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_UINT(glyph_id),
|
||
|
RPC_HANDLE(image_vg_handle),
|
||
|
RPC_FLOAT(clean_float(glyph_origin[0])), RPC_FLOAT(clean_float(glyph_origin[1])),
|
||
|
RPC_FLOAT(clean_float(escapement[0])), RPC_FLOAT(clean_float(escapement[1])));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgClearGlyph(
|
||
|
VGFont vg_handle,
|
||
|
VGuint glyph_id) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
VG_CLIENT_FONT_T *font;
|
||
|
#endif
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#if EGL_BRCM_global_image && EGL_KHR_image
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
font = (VG_CLIENT_FONT_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_FONT);
|
||
|
if (font) {
|
||
|
khrn_global_image_map_delete(&font->glyph_global_images, glyph_id);
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
#endif
|
||
|
|
||
|
RPC_CALL2(vgClearGlyph_impl,
|
||
|
thread,
|
||
|
VGCLEARGLYPH_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_UINT(glyph_id));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgDrawGlyph(
|
||
|
VGFont vg_handle,
|
||
|
VGuint glyph_id,
|
||
|
VGbitfield paint_modes,
|
||
|
VGboolean allow_autohinting) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
|
||
|
#ifdef VG_NO_STROKING
|
||
|
paint_modes &= ~VG_STROKE_PATH;
|
||
|
if (!paint_modes) { return; }
|
||
|
#endif
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sync_matrix(state, VG_MATRIX_GLYPH_USER_TO_SURFACE);
|
||
|
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 */
|
||
|
if (paint_modes & VG_STROKE_PATH) {
|
||
|
sync_matrix(state, VG_MATRIX_STROKE_PAINT_TO_USER);
|
||
|
}
|
||
|
|
||
|
if (state->render_callback)
|
||
|
state->render_callback();
|
||
|
|
||
|
RPC_CALL4(vgDrawGlyph_impl,
|
||
|
thread,
|
||
|
VGDRAWGLYPH_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_UINT(glyph_id),
|
||
|
RPC_BITFIELD(paint_modes),
|
||
|
RPC_BOOLEAN(clean_boolean(allow_autohinting)));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgDrawGlyphs(
|
||
|
VGFont vg_handle,
|
||
|
VGint glyphs_count,
|
||
|
const VGuint *glyph_ids,
|
||
|
const VGfloat *adjustments_x,
|
||
|
const VGfloat *adjustments_y,
|
||
|
VGbitfield paint_modes,
|
||
|
VGboolean allow_autohinting) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
|
||
|
#ifdef VG_NO_STROKING
|
||
|
paint_modes &= ~VG_STROKE_PATH;
|
||
|
if (!paint_modes) { return; }
|
||
|
#endif
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((glyphs_count <= 0) || !glyph_ids || !is_aligned_uint(glyph_ids) ||
|
||
|
(adjustments_x && !is_aligned_float(adjustments_x)) ||
|
||
|
(adjustments_y && !is_aligned_float(adjustments_y))) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sync_matrix(state, VG_MATRIX_GLYPH_USER_TO_SURFACE);
|
||
|
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 */
|
||
|
if (paint_modes & VG_STROKE_PATH) {
|
||
|
sync_matrix(state, VG_MATRIX_STROKE_PAINT_TO_USER);
|
||
|
}
|
||
|
|
||
|
if (state->render_callback)
|
||
|
state->render_callback();
|
||
|
|
||
|
#ifdef RPC_DIRECT
|
||
|
RPC_CALL7(vgDrawGlyphs_impl, thread, no_id,
|
||
|
vg_handle,
|
||
|
glyphs_count, glyph_ids,
|
||
|
adjustments_x, adjustments_y,
|
||
|
paint_modes,
|
||
|
clean_boolean(allow_autohinting));
|
||
|
#else
|
||
|
{
|
||
|
/*
|
||
|
split into multiple calls if necessary to avoid overflowing control buffer
|
||
|
|
||
|
todo: if there's an invalid glyph, we're not supposed to draw anything
|
||
|
at all. currently, this works on a per-chunk level (ie we will try to
|
||
|
draw the glyphs in a chunk iff there are no invalid glyphs in the chunk)
|
||
|
*/
|
||
|
|
||
|
VGuint glyph_size = sizeof(VGuint) +
|
||
|
(adjustments_x ? sizeof(VGfloat) : 0) +
|
||
|
(adjustments_y ? sizeof(VGfloat) : 0);
|
||
|
|
||
|
while (glyphs_count != 0) {
|
||
|
#define MESSAGE_SIZE 24
|
||
|
|
||
|
/* 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) */
|
||
|
VGint chunk_glyphs_count = _min(glyphs_count, (rpc_send_ctrl_longest(thread, MESSAGE_SIZE + (8 * glyph_size)) - MESSAGE_SIZE) / glyph_size);
|
||
|
|
||
|
uint32_t message[] = {
|
||
|
VGDRAWGLYPHS_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_INT(chunk_glyphs_count),
|
||
|
RPC_BITFIELD((!!adjustments_x << 0) | (!!adjustments_y << 1)),
|
||
|
RPC_BITFIELD(paint_modes),
|
||
|
RPC_BOOLEAN(clean_boolean(allow_autohinting)) };
|
||
|
vcos_static_assert(sizeof(message) == MESSAGE_SIZE);
|
||
|
|
||
|
#undef MESSAGE_SIZE
|
||
|
|
||
|
rpc_send_ctrl_begin(thread, sizeof(message) + (chunk_glyphs_count * glyph_size));
|
||
|
rpc_send_ctrl_write(thread, message, sizeof(message));
|
||
|
rpc_send_ctrl_write(thread, (uint32_t *)glyph_ids, chunk_glyphs_count * sizeof(VGuint));
|
||
|
if (adjustments_x) { rpc_send_ctrl_write(thread, (uint32_t *)adjustments_x, chunk_glyphs_count * sizeof(VGfloat)); }
|
||
|
if (adjustments_y) { rpc_send_ctrl_write(thread, (uint32_t *)adjustments_y, chunk_glyphs_count * sizeof(VGfloat)); }
|
||
|
rpc_send_ctrl_end(thread);
|
||
|
|
||
|
glyphs_count -= chunk_glyphs_count;
|
||
|
glyph_ids += chunk_glyphs_count;
|
||
|
if (adjustments_x) { adjustments_x += chunk_glyphs_count; }
|
||
|
if (adjustments_y) { adjustments_y += chunk_glyphs_count; }
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
api filters
|
||
|
******************************************************************************/
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgColorMatrix(
|
||
|
VGImage dst_vg_handle, VGImage src_vg_handle,
|
||
|
const VGfloat *matrix) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!matrix || !is_aligned_float(matrix)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RPC_CALL3_IN_CTRL(vgColorMatrix_impl,
|
||
|
thread,
|
||
|
VGCOLORMATRIX_ID,
|
||
|
RPC_HANDLE(dst_vg_handle),
|
||
|
RPC_HANDLE(src_vg_handle),
|
||
|
matrix,
|
||
|
20 * sizeof(VGfloat));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgConvolve(
|
||
|
VGImage dst_vg_handle, VGImage src_vg_handle,
|
||
|
VGint kernel_width, VGint kernel_height,
|
||
|
VGint shift_x, VGint shift_y,
|
||
|
const VGshort *kernel,
|
||
|
VGfloat scale, VGfloat bias,
|
||
|
VGTilingMode tiling_mode) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
scale = clean_float(scale);
|
||
|
bias = clean_float(bias);
|
||
|
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((kernel_width <= 0) || (kernel_height <= 0) ||
|
||
|
(kernel_width > VG_CONFIG_MAX_KERNEL_SIZE) || (kernel_height > VG_CONFIG_MAX_KERNEL_SIZE) ||
|
||
|
!kernel || !is_aligned_short(kernel)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RPC_CALL10_IN_CTRL(vgConvolve_impl,
|
||
|
thread,
|
||
|
VGCONVOLVE_ID,
|
||
|
RPC_HANDLE(dst_vg_handle), RPC_HANDLE(src_vg_handle),
|
||
|
RPC_INT(kernel_width), RPC_INT(kernel_height),
|
||
|
RPC_INT(shift_x), RPC_INT(shift_y),
|
||
|
RPC_FLOAT(scale), RPC_FLOAT(bias),
|
||
|
RPC_ENUM(tiling_mode),
|
||
|
kernel,
|
||
|
kernel_width * kernel_height * sizeof(VGshort));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgSeparableConvolve(
|
||
|
VGImage dst_vg_handle, VGImage src_vg_handle,
|
||
|
VGint kernel_width, VGint kernel_height,
|
||
|
VGint shift_x, VGint shift_y,
|
||
|
const VGshort *kernel_x,
|
||
|
const VGshort *kernel_y,
|
||
|
VGfloat scale, VGfloat bias,
|
||
|
VGTilingMode tiling_mode) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
scale = clean_float(scale);
|
||
|
bias = clean_float(bias);
|
||
|
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((kernel_width <= 0) || (kernel_height <= 0) ||
|
||
|
(kernel_width > VG_CONFIG_MAX_SEPARABLE_KERNEL_SIZE) || (kernel_height > VG_CONFIG_MAX_SEPARABLE_KERNEL_SIZE) ||
|
||
|
!kernel_x || !is_aligned_short(kernel_x) || !kernel_y || !is_aligned_short(kernel_y)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#ifdef RPC_DIRECT
|
||
|
RPC_CALL11(vgSeparableConvolve_impl, thread, no_id,
|
||
|
dst_vg_handle, src_vg_handle,
|
||
|
kernel_width, kernel_height,
|
||
|
shift_x, shift_y,
|
||
|
kernel_x, kernel_y,
|
||
|
scale, bias,
|
||
|
tiling_mode);
|
||
|
#else
|
||
|
{
|
||
|
uint32_t message[] = {
|
||
|
VGSEPARABLECONVOLVE_ID,
|
||
|
RPC_HANDLE(dst_vg_handle), RPC_HANDLE(src_vg_handle),
|
||
|
RPC_INT(kernel_width), RPC_INT(kernel_height),
|
||
|
RPC_INT(shift_x), RPC_INT(shift_y),
|
||
|
RPC_FLOAT(scale), RPC_FLOAT(bias),
|
||
|
RPC_ENUM(tiling_mode) };
|
||
|
|
||
|
VGuint kernel_x_size = kernel_width * sizeof(VGshort);
|
||
|
VGuint kernel_y_size = kernel_height * sizeof(VGshort);
|
||
|
|
||
|
rpc_send_ctrl_begin(thread, sizeof(message) + rpc_pad_ctrl(kernel_x_size) + rpc_pad_ctrl(kernel_y_size));
|
||
|
rpc_send_ctrl_write(thread, message, sizeof(message));
|
||
|
rpc_send_ctrl_write(thread, (uint32_t *)kernel_x, kernel_x_size);
|
||
|
rpc_send_ctrl_write(thread, (uint32_t *)kernel_y, kernel_y_size);
|
||
|
rpc_send_ctrl_end(thread);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgGaussianBlur(
|
||
|
VGImage dst_vg_handle, VGImage src_vg_handle,
|
||
|
VGfloat std_dev_x, VGfloat std_dev_y,
|
||
|
VGTilingMode tiling_mode) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
std_dev_x = clean_float(std_dev_x);
|
||
|
std_dev_y = clean_float(std_dev_y);
|
||
|
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RPC_CALL5(vgGaussianBlur_impl,
|
||
|
thread,
|
||
|
VGGAUSSIANBLUR_ID,
|
||
|
RPC_HANDLE(dst_vg_handle), RPC_HANDLE(src_vg_handle),
|
||
|
RPC_FLOAT(std_dev_x), RPC_FLOAT(std_dev_y),
|
||
|
RPC_ENUM(tiling_mode));
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgLookup(
|
||
|
VGImage dst_vg_handle, VGImage src_vg_handle,
|
||
|
const VGubyte *red_lut,
|
||
|
const VGubyte *green_lut,
|
||
|
const VGubyte *blue_lut,
|
||
|
const VGubyte *alpha_lut,
|
||
|
VGboolean output_linear,
|
||
|
VGboolean output_pre) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!red_lut || !green_lut || !blue_lut || !alpha_lut) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#ifdef RPC_DIRECT
|
||
|
RPC_CALL8(vgLookup_impl, thread, no_id,
|
||
|
dst_vg_handle, src_vg_handle,
|
||
|
red_lut, green_lut, blue_lut, alpha_lut,
|
||
|
clean_boolean(output_linear),
|
||
|
clean_boolean(output_pre));
|
||
|
#else
|
||
|
{
|
||
|
uint32_t message[] = {
|
||
|
VGLOOKUP_ID,
|
||
|
RPC_HANDLE(dst_vg_handle), RPC_HANDLE(src_vg_handle),
|
||
|
RPC_BOOLEAN(clean_boolean(output_linear)),
|
||
|
RPC_BOOLEAN(clean_boolean(output_pre)) };
|
||
|
|
||
|
rpc_send_ctrl_begin(thread, sizeof(message) + 1024);
|
||
|
rpc_send_ctrl_write(thread, message, sizeof(message));
|
||
|
rpc_send_ctrl_write(thread, (uint32_t *)red_lut, 256);
|
||
|
rpc_send_ctrl_write(thread, (uint32_t *)green_lut, 256);
|
||
|
rpc_send_ctrl_write(thread, (uint32_t *)blue_lut, 256);
|
||
|
rpc_send_ctrl_write(thread, (uint32_t *)alpha_lut, 256);
|
||
|
rpc_send_ctrl_end(thread);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
VG_API_CALL void VG_API_ENTRY vgLookupSingle(
|
||
|
VGImage dst_vg_handle, VGImage src_vg_handle,
|
||
|
const VGuint *lut,
|
||
|
VGImageChannel source_channel,
|
||
|
VGboolean output_linear,
|
||
|
VGboolean output_pre) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!lut || !is_aligned_uint(lut)) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RPC_CALL6_IN_CTRL(vgLookupSingle_impl,
|
||
|
thread,
|
||
|
VGLOOKUPSINGLE_ID,
|
||
|
RPC_HANDLE(dst_vg_handle), RPC_HANDLE(src_vg_handle),
|
||
|
RPC_ENUM(source_channel),
|
||
|
RPC_BOOLEAN(clean_boolean(output_linear)),
|
||
|
RPC_BOOLEAN(clean_boolean(output_pre)),
|
||
|
lut,
|
||
|
256 * sizeof(VGuint));
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
api client-side stuff
|
||
|
******************************************************************************/
|
||
|
|
||
|
VG_API_CALL VGHardwareQueryResult VG_API_ENTRY vgHardwareQuery(
|
||
|
VGHardwareQueryType hardware_query_type,
|
||
|
VGint setting) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return (VGHardwareQueryResult)0;
|
||
|
}
|
||
|
|
||
|
if (!is_hardware_query_type(hardware_query_type) ||
|
||
|
((hardware_query_type == VG_IMAGE_FORMAT_QUERY) && !is_image_format((VGImageFormat)setting)) ||
|
||
|
((hardware_query_type == VG_PATH_DATATYPE_QUERY) && !is_path_datatype((VGPathDatatype)setting))) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return (VGHardwareQueryResult)0;
|
||
|
}
|
||
|
|
||
|
return VG_HARDWARE_ACCELERATED;
|
||
|
}
|
||
|
|
||
|
VG_API_CALL const VGubyte * VG_API_ENTRY vgGetString(VGStringID name) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
switch (name) {
|
||
|
case VG_VENDOR: return (const VGubyte *)VG_CONFIG_VENDOR;
|
||
|
case VG_RENDERER: return (const VGubyte *)VG_CONFIG_RENDERER;
|
||
|
case VG_VERSION: return (const VGubyte *)"1.1";
|
||
|
case VG_EXTENSIONS: return (const VGubyte *)VG_CONFIG_EXTENSIONS;
|
||
|
default: return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
api vgu
|
||
|
******************************************************************************/
|
||
|
|
||
|
static VGUErrorCode get_vgu_error(void)
|
||
|
{
|
||
|
switch (get_error()) {
|
||
|
case VG_NO_ERROR: return VGU_NO_ERROR;
|
||
|
case VG_BAD_HANDLE_ERROR: return VGU_BAD_HANDLE_ERROR;
|
||
|
case VG_ILLEGAL_ARGUMENT_ERROR: return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||
|
case VG_OUT_OF_MEMORY_ERROR: return VGU_OUT_OF_MEMORY_ERROR;
|
||
|
case VG_PATH_CAPABILITY_ERROR: return VGU_PATH_CAPABILITY_ERROR;
|
||
|
default: UNREACHABLE(); return (VGUErrorCode)0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguLine(
|
||
|
VGPath vg_handle,
|
||
|
VGfloat p0_x, VGfloat p0_y,
|
||
|
VGfloat p1_x, VGfloat p1_y) VGU_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
VG_CLIENT_PATH_T *path;
|
||
|
|
||
|
p0_x = clean_float(p0_x);
|
||
|
p0_y = clean_float(p0_y);
|
||
|
p1_x = clean_float(p1_x);
|
||
|
p1_y = clean_float(p1_y);
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VGU_NO_ERROR;
|
||
|
}
|
||
|
|
||
|
clear_error();
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
if (path &&
|
||
|
(path->caps & VG_PATH_CAPABILITY_APPEND_TO) &&
|
||
|
need_path_segments(path->caps)) {
|
||
|
VGubyte *segments;
|
||
|
if (!khrn_vector_extend(&path->segments, 2)) {
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return VGU_OUT_OF_MEMORY_ERROR;
|
||
|
}
|
||
|
segments = (VGubyte *)path->segments.data + (path->segments.size - 2);
|
||
|
segments[0] = VG_MOVE_TO_ABS;
|
||
|
segments[1] = VG_LINE_TO_ABS;
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL5(vguLine_impl,
|
||
|
thread,
|
||
|
VGULINE_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_FLOAT(p0_x), RPC_FLOAT(p0_y),
|
||
|
RPC_FLOAT(p1_x), RPC_FLOAT(p1_y));
|
||
|
|
||
|
return get_vgu_error();
|
||
|
}
|
||
|
|
||
|
VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguPolygon(
|
||
|
VGPath vg_handle,
|
||
|
const VGfloat *ps, VGint ps_count,
|
||
|
VGboolean close) VGU_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_PATH_T *path;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VGU_NO_ERROR;
|
||
|
}
|
||
|
|
||
|
clear_error();
|
||
|
|
||
|
if ((ps_count <= 0) || !ps || !is_aligned_float(ps)) {
|
||
|
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
if (path &&
|
||
|
(path->caps & VG_PATH_CAPABILITY_APPEND_TO) &&
|
||
|
need_path_segments(path->caps)) {
|
||
|
VGubyte *segments;
|
||
|
VGuint segments_count = ps_count + (close ? 1 : 0);
|
||
|
if (!khrn_vector_extend(&path->segments, segments_count)) {
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return VGU_OUT_OF_MEMORY_ERROR;
|
||
|
}
|
||
|
segments = (VGubyte *)path->segments.data + (path->segments.size - segments_count);
|
||
|
memset(segments, VG_LINE_TO_ABS, segments_count);
|
||
|
segments[0] = VG_MOVE_TO_ABS;
|
||
|
if (close) { segments[segments_count - 1] = VG_CLOSE_PATH; }
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
#ifdef RPC_DIRECT
|
||
|
RPC_CALL5(vguPolygon_impl, thread, no_id,
|
||
|
vg_handle,
|
||
|
ps, ps_count,
|
||
|
true, clean_boolean(close));
|
||
|
#else
|
||
|
{
|
||
|
/*
|
||
|
split into multiple calls if necessary to avoid overflowing control buffer
|
||
|
*/
|
||
|
|
||
|
bool first = true;
|
||
|
while (ps_count != 0) {
|
||
|
#define MESSAGE_SIZE 20
|
||
|
|
||
|
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 */
|
||
|
bool last = chunk_ps_count == ps_count;
|
||
|
|
||
|
uint32_t message[] = {
|
||
|
VGUPOLYGON_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_INT(chunk_ps_count),
|
||
|
RPC_BOOLEAN(first), RPC_BOOLEAN(last && close) };
|
||
|
vcos_static_assert(sizeof(message) == MESSAGE_SIZE);
|
||
|
|
||
|
#undef MESSAGE_SIZE
|
||
|
|
||
|
rpc_send_ctrl_begin(thread,
|
||
|
sizeof(message) +
|
||
|
(chunk_ps_count * 2 * sizeof(VGfloat)));
|
||
|
rpc_send_ctrl_write(thread, message, sizeof(message));
|
||
|
rpc_send_ctrl_write(thread, (uint32_t *)ps, chunk_ps_count * 2 * sizeof(VGfloat));
|
||
|
rpc_send_ctrl_end(thread);
|
||
|
|
||
|
ps_count -= chunk_ps_count;
|
||
|
ps += 2 * chunk_ps_count;
|
||
|
first = false;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return get_vgu_error();
|
||
|
}
|
||
|
|
||
|
VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguRect(
|
||
|
VGPath vg_handle,
|
||
|
VGfloat x, VGfloat y,
|
||
|
VGfloat width, VGfloat height) VGU_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
VG_CLIENT_PATH_T *path;
|
||
|
|
||
|
x = clean_float(x);
|
||
|
y = clean_float(y);
|
||
|
width = clean_float(width);
|
||
|
height = clean_float(height);
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VGU_NO_ERROR;
|
||
|
}
|
||
|
|
||
|
clear_error();
|
||
|
|
||
|
if (is_le_zero(width) || is_le_zero(height)) {
|
||
|
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
if (path &&
|
||
|
(path->caps & VG_PATH_CAPABILITY_APPEND_TO) &&
|
||
|
need_path_segments(path->caps)) {
|
||
|
VGubyte *segments;
|
||
|
if (!khrn_vector_extend(&path->segments, 5)) {
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return VGU_OUT_OF_MEMORY_ERROR;
|
||
|
}
|
||
|
segments = (VGubyte *)path->segments.data + (path->segments.size - 5);
|
||
|
segments[0] = VG_MOVE_TO_ABS;
|
||
|
segments[1] = VG_HLINE_TO_REL;
|
||
|
segments[2] = VG_VLINE_TO_REL;
|
||
|
segments[3] = VG_HLINE_TO_REL;
|
||
|
segments[4] = VG_CLOSE_PATH;
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL5(vguRect_impl,
|
||
|
thread,
|
||
|
VGURECT_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_FLOAT(x), RPC_FLOAT(y),
|
||
|
RPC_FLOAT(width), RPC_FLOAT(height));
|
||
|
|
||
|
return get_vgu_error();
|
||
|
}
|
||
|
|
||
|
VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguRoundRect(
|
||
|
VGPath vg_handle,
|
||
|
VGfloat x, VGfloat y,
|
||
|
VGfloat width, VGfloat height,
|
||
|
VGfloat arc_width, VGfloat arc_height) VGU_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
VG_CLIENT_PATH_T *path;
|
||
|
|
||
|
x = clean_float(x);
|
||
|
y = clean_float(y);
|
||
|
width = clean_float(width);
|
||
|
height = clean_float(height);
|
||
|
arc_width = clean_float(arc_width);
|
||
|
arc_height = clean_float(arc_height);
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VGU_NO_ERROR;
|
||
|
}
|
||
|
|
||
|
clear_error();
|
||
|
|
||
|
if (is_le_zero(width) || is_le_zero(height)) {
|
||
|
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
if (path &&
|
||
|
(path->caps & VG_PATH_CAPABILITY_APPEND_TO) &&
|
||
|
need_path_segments(path->caps)) {
|
||
|
VGubyte *segments;
|
||
|
if (!khrn_vector_extend(&path->segments, 10)) {
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return VGU_OUT_OF_MEMORY_ERROR;
|
||
|
}
|
||
|
segments = (VGubyte *)path->segments.data + (path->segments.size - 10);
|
||
|
segments[0] = VG_MOVE_TO_ABS;
|
||
|
segments[1] = VG_HLINE_TO_REL;
|
||
|
segments[2] = VG_SCCWARC_TO_REL;
|
||
|
segments[3] = VG_VLINE_TO_REL;
|
||
|
segments[4] = VG_SCCWARC_TO_REL;
|
||
|
segments[5] = VG_HLINE_TO_REL;
|
||
|
segments[6] = VG_SCCWARC_TO_REL;
|
||
|
segments[7] = VG_VLINE_TO_REL;
|
||
|
segments[8] = VG_SCCWARC_TO_REL;
|
||
|
segments[9] = VG_CLOSE_PATH;
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL7(vguRoundRect_impl,
|
||
|
thread,
|
||
|
VGUROUNDRECT_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_FLOAT(x), RPC_FLOAT(y),
|
||
|
RPC_FLOAT(width), RPC_FLOAT(height),
|
||
|
RPC_FLOAT(arc_width), RPC_FLOAT(arc_height));
|
||
|
|
||
|
return get_vgu_error();
|
||
|
}
|
||
|
|
||
|
VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguEllipse(
|
||
|
VGPath vg_handle,
|
||
|
VGfloat x, VGfloat y,
|
||
|
VGfloat width, VGfloat height) VGU_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
VG_CLIENT_PATH_T *path;
|
||
|
|
||
|
x = clean_float(x);
|
||
|
y = clean_float(y);
|
||
|
width = clean_float(width);
|
||
|
height = clean_float(height);
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VGU_NO_ERROR;
|
||
|
}
|
||
|
|
||
|
clear_error();
|
||
|
|
||
|
if (is_le_zero(width) || is_le_zero(height)) {
|
||
|
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
if (path &&
|
||
|
(path->caps & VG_PATH_CAPABILITY_APPEND_TO) &&
|
||
|
need_path_segments(path->caps)) {
|
||
|
VGubyte *segments;
|
||
|
if (!khrn_vector_extend(&path->segments, 4)) {
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return VGU_OUT_OF_MEMORY_ERROR;
|
||
|
}
|
||
|
segments = (VGubyte *)path->segments.data + (path->segments.size - 4);
|
||
|
segments[0] = VG_MOVE_TO_ABS;
|
||
|
segments[1] = VG_SCCWARC_TO_REL;
|
||
|
segments[2] = VG_SCCWARC_TO_REL;
|
||
|
segments[3] = VG_CLOSE_PATH;
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL5(vguEllipse_impl,
|
||
|
thread,
|
||
|
VGUELLIPSE_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_FLOAT(x), RPC_FLOAT(y),
|
||
|
RPC_FLOAT(width), RPC_FLOAT(height));
|
||
|
|
||
|
return get_vgu_error();
|
||
|
}
|
||
|
|
||
|
VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguArc(
|
||
|
VGPath vg_handle,
|
||
|
VGfloat x, VGfloat y,
|
||
|
VGfloat width, VGfloat height,
|
||
|
VGfloat start_angle, VGfloat angle_extent,
|
||
|
VGUArcType arc_type) VGU_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_CLIENT_STATE_T *state;
|
||
|
VGuint angle_o180;
|
||
|
VG_CLIENT_PATH_T *path;
|
||
|
|
||
|
x = clean_float(x);
|
||
|
y = clean_float(y);
|
||
|
width = clean_float(width);
|
||
|
height = clean_float(height);
|
||
|
start_angle = clean_float(start_angle);
|
||
|
angle_extent = clean_float(angle_extent);
|
||
|
|
||
|
state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VGU_NO_ERROR;
|
||
|
}
|
||
|
|
||
|
clear_error();
|
||
|
|
||
|
if (is_le_zero(width) || is_le_zero(height) || !is_arc_type(arc_type)) {
|
||
|
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||
|
}
|
||
|
|
||
|
angle_o180 = float_to_int_zero(absf_(angle_extent) * (1.0f / 180.0f));
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
path = (VG_CLIENT_PATH_T *)lookup_object(state, vg_handle, VG_CLIENT_OBJECT_TYPE_PATH);
|
||
|
if (path &&
|
||
|
(path->caps & VG_PATH_CAPABILITY_APPEND_TO) &&
|
||
|
need_path_segments(path->caps)) {
|
||
|
VGubyte *segments;
|
||
|
VGuint segments_count = 2 + angle_o180;
|
||
|
switch (arc_type) {
|
||
|
case VGU_ARC_OPEN: break;
|
||
|
case VGU_ARC_CHORD: segments_count += 1; break;
|
||
|
case VGU_ARC_PIE: segments_count += 2; break;
|
||
|
default: UNREACHABLE();
|
||
|
}
|
||
|
if (!khrn_vector_extend(&path->segments, segments_count)) {
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
return VGU_OUT_OF_MEMORY_ERROR;
|
||
|
}
|
||
|
segments = (VGubyte *)path->segments.data + (path->segments.size - segments_count);
|
||
|
segments[0] = VG_MOVE_TO_ABS;
|
||
|
memset(segments + 1, VG_SCCWARC_TO_ABS, segments_count - 1); /* don't care about actual arc type on client */
|
||
|
if (arc_type == VGU_ARC_PIE) { segments[segments_count - 2] = VG_LINE_TO_ABS; }
|
||
|
if ((arc_type == VGU_ARC_CHORD) || (arc_type == VGU_ARC_PIE)) { segments[segments_count - 1] = VG_CLOSE_PATH; }
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
RPC_CALL9(vguArc_impl,
|
||
|
thread,
|
||
|
VGUARC_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_FLOAT(x), RPC_FLOAT(y),
|
||
|
RPC_FLOAT(width), RPC_FLOAT(height),
|
||
|
RPC_FLOAT(start_angle), RPC_FLOAT(angle_extent), RPC_UINT(angle_o180),
|
||
|
RPC_ENUM(arc_type));
|
||
|
|
||
|
return get_vgu_error();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
based on "Fundamentals of Texture Mapping and Image Warping" by Paul S Heckbert
|
||
|
*/
|
||
|
|
||
|
static bool warp_square_to_quad(VG_MAT3X3_T *a,
|
||
|
VGfloat p0_x, VGfloat p0_y,
|
||
|
VGfloat p1_x, VGfloat p1_y,
|
||
|
VGfloat p2_x, VGfloat p2_y,
|
||
|
VGfloat p3_x, VGfloat p3_y)
|
||
|
{
|
||
|
VGfloat d1_x = p1_x - p3_x;
|
||
|
VGfloat d1_y = p1_y - p3_y;
|
||
|
VGfloat d2_x = p2_x - p3_x;
|
||
|
VGfloat d2_y = p2_y - p3_y;
|
||
|
|
||
|
VGfloat d = (d1_x * d2_y) - (d1_y * d2_x);
|
||
|
|
||
|
VGfloat oo_d;
|
||
|
VGfloat sum_x;
|
||
|
VGfloat sum_y;
|
||
|
VGfloat g;
|
||
|
VGfloat h;
|
||
|
|
||
|
if (absf_(d) < EPS) {
|
||
|
return false;
|
||
|
}
|
||
|
oo_d = recip_(d);
|
||
|
|
||
|
sum_x = (p0_x + p3_x) - (p1_x + p2_x);
|
||
|
sum_y = (p0_y + p3_y) - (p1_y + p2_y);
|
||
|
|
||
|
g = ((sum_x * d2_y) - (sum_y * d2_x)) * oo_d;
|
||
|
h = ((d1_x * sum_y) - (d1_y * sum_x)) * oo_d;
|
||
|
|
||
|
a->m[0][0] = (p1_x - p0_x) + (g * p1_x);
|
||
|
a->m[0][1] = (p2_x - p0_x) + (h * p2_x);
|
||
|
a->m[0][2] = p0_x;
|
||
|
|
||
|
a->m[1][0] = (p1_y - p0_y) + (g * p1_y);
|
||
|
a->m[1][1] = (p2_y - p0_y) + (h * p2_y);
|
||
|
a->m[1][2] = p0_y;
|
||
|
|
||
|
a->m[2][0] = g;
|
||
|
a->m[2][1] = h;
|
||
|
a->m[2][2] = 1.0f;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool warp_quad_to_square(VG_MAT3X3_T *a,
|
||
|
VGfloat p0_x, VGfloat p0_y,
|
||
|
VGfloat p1_x, VGfloat p1_y,
|
||
|
VGfloat p2_x, VGfloat p2_y,
|
||
|
VGfloat p3_x, VGfloat p3_y)
|
||
|
{
|
||
|
if (!warp_square_to_quad(a, p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y) ||
|
||
|
!vg_mat3x3_is_invertible(a)) {
|
||
|
return false;
|
||
|
}
|
||
|
vg_mat3x3_invert(a);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguComputeWarpQuadToSquare(
|
||
|
VGfloat p0_x, VGfloat p0_y,
|
||
|
VGfloat p1_x, VGfloat p1_y,
|
||
|
VGfloat p2_x, VGfloat p2_y,
|
||
|
VGfloat p3_x, VGfloat p3_y,
|
||
|
VGfloat *matrix) VGU_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_MAT3X3_T a;
|
||
|
|
||
|
p0_x = clean_float(p0_x);
|
||
|
p0_y = clean_float(p0_y);
|
||
|
p1_x = clean_float(p1_x);
|
||
|
p1_y = clean_float(p1_y);
|
||
|
p2_x = clean_float(p2_x);
|
||
|
p2_y = clean_float(p2_y);
|
||
|
p3_x = clean_float(p3_x);
|
||
|
p3_y = clean_float(p3_y);
|
||
|
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return VGU_NO_ERROR;
|
||
|
}
|
||
|
|
||
|
if (!matrix || !is_aligned_float(matrix)) {
|
||
|
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||
|
}
|
||
|
|
||
|
if (!warp_quad_to_square(&a, p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y)) {
|
||
|
return VGU_BAD_WARP_ERROR;
|
||
|
}
|
||
|
vg_mat3x3_get(&a, matrix);
|
||
|
return VGU_NO_ERROR;
|
||
|
}
|
||
|
|
||
|
VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguComputeWarpSquareToQuad(
|
||
|
VGfloat p0_x, VGfloat p0_y,
|
||
|
VGfloat p1_x, VGfloat p1_y,
|
||
|
VGfloat p2_x, VGfloat p2_y,
|
||
|
VGfloat p3_x, VGfloat p3_y,
|
||
|
VGfloat *matrix) VGU_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_MAT3X3_T a;
|
||
|
|
||
|
p0_x = clean_float(p0_x);
|
||
|
p0_y = clean_float(p0_y);
|
||
|
p1_x = clean_float(p1_x);
|
||
|
p1_y = clean_float(p1_y);
|
||
|
p2_x = clean_float(p2_x);
|
||
|
p2_y = clean_float(p2_y);
|
||
|
p3_x = clean_float(p3_x);
|
||
|
p3_y = clean_float(p3_y);
|
||
|
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return VGU_NO_ERROR;
|
||
|
}
|
||
|
|
||
|
if (!matrix || !is_aligned_float(matrix)) {
|
||
|
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||
|
}
|
||
|
|
||
|
if (!warp_square_to_quad(&a, p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y)) {
|
||
|
return VGU_BAD_WARP_ERROR;
|
||
|
}
|
||
|
vg_mat3x3_get(&a, matrix);
|
||
|
return VGU_NO_ERROR;
|
||
|
}
|
||
|
|
||
|
VGU_API_CALL VGUErrorCode VGU_API_ENTRY vguComputeWarpQuadToQuad(
|
||
|
VGfloat p0_x, VGfloat p0_y,
|
||
|
VGfloat p1_x, VGfloat p1_y,
|
||
|
VGfloat p2_x, VGfloat p2_y,
|
||
|
VGfloat p3_x, VGfloat p3_y,
|
||
|
VGfloat q0_x, VGfloat q0_y,
|
||
|
VGfloat q1_x, VGfloat q1_y,
|
||
|
VGfloat q2_x, VGfloat q2_y,
|
||
|
VGfloat q3_x, VGfloat q3_y,
|
||
|
VGfloat *matrix) VGU_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
VG_MAT3X3_T a, b;
|
||
|
|
||
|
p0_x = clean_float(p0_x);
|
||
|
p0_y = clean_float(p0_y);
|
||
|
p1_x = clean_float(p1_x);
|
||
|
p1_y = clean_float(p1_y);
|
||
|
p2_x = clean_float(p2_x);
|
||
|
p2_y = clean_float(p2_y);
|
||
|
p3_x = clean_float(p3_x);
|
||
|
p3_y = clean_float(p3_y);
|
||
|
q0_x = clean_float(q0_x);
|
||
|
q0_y = clean_float(q0_y);
|
||
|
q1_x = clean_float(q1_x);
|
||
|
q1_y = clean_float(q1_y);
|
||
|
q2_x = clean_float(q2_x);
|
||
|
q2_y = clean_float(q2_y);
|
||
|
q3_x = clean_float(q3_x);
|
||
|
q3_y = clean_float(q3_y);
|
||
|
|
||
|
if (!VG_GET_CLIENT_STATE(thread)) {
|
||
|
return VGU_NO_ERROR;
|
||
|
}
|
||
|
|
||
|
if (!matrix || !is_aligned_float(matrix)) {
|
||
|
return VGU_ILLEGAL_ARGUMENT_ERROR;
|
||
|
}
|
||
|
|
||
|
if (!warp_square_to_quad(&a, p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y) ||
|
||
|
!warp_quad_to_square(&b, q0_x, q0_y, q1_x, q1_y, q2_x, q2_y, q3_x, q3_y)) {
|
||
|
return VGU_BAD_WARP_ERROR;
|
||
|
}
|
||
|
vg_mat3x3_postmul(&a, &b);
|
||
|
vg_mat3x3_get(&a, matrix);
|
||
|
return VGU_NO_ERROR;
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
VG_KHR_EGL_image
|
||
|
******************************************************************************/
|
||
|
|
||
|
#if VG_KHR_EGL_image
|
||
|
|
||
|
VG_API_CALL VGImage VG_API_ENTRY vgCreateEGLImageTargetKHR(
|
||
|
VGeglImageKHR src_egl_handle) VG_API_EXIT
|
||
|
{
|
||
|
CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
|
||
|
#if EGL_BRCM_global_image
|
||
|
VGuint global_image_id[2];
|
||
|
#endif
|
||
|
VGuint format_width_height[3];
|
||
|
VGImage vg_handle;
|
||
|
VG_CLIENT_IMAGE_T *image;
|
||
|
|
||
|
VG_CLIENT_STATE_T *state = VG_GET_CLIENT_STATE(thread);
|
||
|
if (!state) {
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
#if EGL_BRCM_global_image
|
||
|
if ((uintptr_t)src_egl_handle & (1 << 31)) {
|
||
|
CLIENT_PROCESS_STATE_T *process = CLIENT_GET_PROCESS_STATE();
|
||
|
uint64_t id;
|
||
|
|
||
|
CLIENT_LOCK();
|
||
|
id = process->inited ? khrn_global_image_map_lookup(&process->global_image_egl_images, (uint32_t)(uintptr_t)src_egl_handle) : 0;
|
||
|
CLIENT_UNLOCK();
|
||
|
if (!id) {
|
||
|
set_error(VG_ILLEGAL_ARGUMENT_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
global_image_id[0] = (VGuint)id;
|
||
|
global_image_id[1] = (VGuint)(id >> 32);
|
||
|
|
||
|
platform_get_global_image_info(global_image_id[0], global_image_id[1],
|
||
|
format_width_height + 0, format_width_height + 1, format_width_height + 2);
|
||
|
|
||
|
if (!(format_width_height[0] & EGL_PIXEL_FORMAT_VG_IMAGE_BRCM) ||
|
||
|
(format_width_height[1] == 0) || (format_width_height[2] == 0) ||
|
||
|
(format_width_height[1] > VG_CONFIG_MAX_IMAGE_WIDTH) || (format_width_height[2] > VG_CONFIG_MAX_IMAGE_HEIGHT)) {
|
||
|
set_error(VG_UNSUPPORTED_IMAGE_FORMAT_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
switch (format_width_height[0] & ~EGL_PIXEL_FORMAT_USAGE_MASK_BRCM) {
|
||
|
case EGL_PIXEL_FORMAT_ARGB_8888_PRE_BRCM: format_width_height[0] = VG_sARGB_8888_PRE; break;
|
||
|
case EGL_PIXEL_FORMAT_ARGB_8888_BRCM: format_width_height[0] = VG_sARGB_8888; break;
|
||
|
case EGL_PIXEL_FORMAT_XRGB_8888_BRCM: format_width_height[0] = VG_sXRGB_8888; break;
|
||
|
case EGL_PIXEL_FORMAT_RGB_565_BRCM: format_width_height[0] = VG_sRGB_565; break;
|
||
|
case EGL_PIXEL_FORMAT_A_8_BRCM: format_width_height[0] = VG_A_8; break;
|
||
|
default: UNREACHABLE();
|
||
|
}
|
||
|
|
||
|
vg_handle = get_stem(state);
|
||
|
if (vg_handle == VG_INVALID_HANDLE) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
RPC_CALL3(vgCreateImageFromGlobalImage_impl,
|
||
|
thread,
|
||
|
VGCREATEIMAGEFROMGLOBALIMAGE_ID,
|
||
|
RPC_HANDLE(vg_handle),
|
||
|
RPC_UINT(global_image_id[0]),
|
||
|
RPC_UINT(global_image_id[1]));
|
||
|
} else {
|
||
|
global_image_id[0] = 0;
|
||
|
global_image_id[1] = 0;
|
||
|
#endif
|
||
|
vg_handle = RPC_HANDLE_RES(RPC_CALL2_OUT_CTRL_RES(vgCreateEGLImageTargetKHR_impl,
|
||
|
thread,
|
||
|
VGCREATEEGLIMAGETARGETKHR_ID,
|
||
|
RPC_EGLID(src_egl_handle),
|
||
|
format_width_height));
|
||
|
if (vg_handle == VG_INVALID_HANDLE) {
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
#if EGL_BRCM_global_image
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
image = image_alloc((VGImageFormat)format_width_height[0], format_width_height[1], format_width_height[2]
|
||
|
#if EGL_BRCM_global_image
|
||
|
, global_image_id[0], global_image_id[1]
|
||
|
#endif
|
||
|
);
|
||
|
if (!image) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
RPC_CALL1(vgDestroyImage_impl,
|
||
|
thread,
|
||
|
VGDESTROYIMAGE_ID,
|
||
|
RPC_HANDLE(vg_handle));
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
platform_mutex_acquire(&state->shared_state->mutex);
|
||
|
if (!insert_object(state, vg_handle, image)) {
|
||
|
set_error(VG_OUT_OF_MEMORY_ERROR);
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
image_free(image);
|
||
|
RPC_CALL1(vgDestroyImage_impl,
|
||
|
thread,
|
||
|
VGDESTROYIMAGE_ID,
|
||
|
RPC_HANDLE(vg_handle));
|
||
|
return VG_INVALID_HANDLE;
|
||
|
}
|
||
|
platform_mutex_release(&state->shared_state->mutex);
|
||
|
|
||
|
return vg_handle;
|
||
|
}
|
||
|
|
||
|
#endif
|