From: Jean-Philippe Bruyère Date: Sat, 8 Jan 2022 15:27:27 +0000 (+0100) Subject: base implementation of recording infrastructure X-Git-Tag: v0.3.0-beta~22 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=0c57f9be08fb6c2f535edc16202f39ef61fd80ce;p=jp%2Fvkvg.git base implementation of recording infrastructure --- diff --git a/CMakeLists.txt b/CMakeLists.txt index a4067fe..4afbcde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,11 @@ IF (ENABLE_PROFILING) ADD_DEFINITIONS(${CMAKE_CXX_FLAGS} "-falign-loops -falign-labels") ENDIF() +OPTION(VKVG_RECORDING "enable experimental recording functions" ON) +IF (VKVG_RECORDING) + ADD_DEFINITIONS (-DVKVG_RECORDING) +ENDIF () + OPTION(VKVG_PREMULT_ALPHA "use premultiplied alpha for internal rendering" ON) IF (VKVG_PREMULT_ALPHA) ADD_DEFINITIONS (-DVKVG_PREMULT_ALPHA) @@ -214,6 +219,11 @@ ELSE() LIST (APPEND VKVG_OBJ_SRC ${NSVG_SRC}) ENDIF() +IF (VKVG_RECORDING) + FILE(GLOB RECORDING_SRC src/recording/*.c) + LIST (APPEND VKVG_SRC ${RECORDING_SRC}) +ENDIF() + CONFIGURE_FILE(vkvg.pc.in vkvg.pc @ONLY) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/vkvg.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) diff --git a/include/vkvg.h b/include/vkvg.h index 4557dfb..9fc397e 100644 --- a/include/vkvg.h +++ b/include/vkvg.h @@ -1748,6 +1748,19 @@ void vkvg_set_source_color_name (VkvgContext ctx, const char* color); /*************************************/ +#ifdef VKVG_RECORDING +typedef struct _vkvg_recording_t* VkvgRecording; + +vkvg_public +void vkvg_start_recording (VkvgContext ctx); +VkvgRecording vkvg_stop_recording (VkvgContext ctx); +void vkvg_replay (VkvgContext ctx, VkvgRecording rec); +uint32_t vkvg_recording_get_count(VkvgRecording rec); +void vkvg_recording_destroy (VkvgRecording rec); + + +#endif + #ifdef __cplusplus } #endif diff --git a/src/recording/vkvg_record.c b/src/recording/vkvg_record.c new file mode 100644 index 0000000..18fb85a --- /dev/null +++ b/src/recording/vkvg_record.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018-2022 Jean-Philippe Bruyère + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "vkvg.h" +#include "vkvg_context_internal.h" +#include "vkvg_record_internal.h" + +#ifdef VKVG_RECORDING +void vkvg_start_recording (VkvgContext ctx) { + if (ctx->status) + return; + _start_recording(ctx); +} +VkvgRecording vkvg_stop_recording (VkvgContext ctx) { + if (ctx->status) + return NULL; + return _stop_recording (ctx); +} +uint32_t vkvg_recording_get_count (VkvgRecording rec) { + if (!rec) + return 0; + return rec->commandsCount; +} +void vkvg_replay (VkvgContext ctx, VkvgRecording rec) { + if (!rec) + return; + for (uint32_t i=0; icommandsCount; i++) + _replay_command(ctx, rec, i); +} +void vkvg_recording_destroy (VkvgRecording rec) { + if (!rec) + return; + _destroy_recording(rec); +} +#endif diff --git a/src/recording/vkvg_record_internal.c b/src/recording/vkvg_record_internal.c new file mode 100644 index 0000000..83a6288 --- /dev/null +++ b/src/recording/vkvg_record_internal.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2018-2022 Jean-Philippe Bruyère + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vkvg.h" +#include "vkvg_record_internal.h" +#include "vkvg_context_internal.h" + +#define VKVG_RECORDING_INIT_BUFFER_SIZE_TRESHOLD 64 +#define VKVG_RECORDING_INIT_BUFFER_SIZE 1024 +#define VKVG_RECORDING_INIT_COMMANDS_COUNT 64 + +vkvg_recording_t* _new_recording () { + + vkvg_recording_t* rec = (vkvg_recording_t*)calloc(1,sizeof (vkvg_recording_t)); + + rec->commandsReservedCount = VKVG_RECORDING_INIT_COMMANDS_COUNT; + rec->bufferReservedSize = VKVG_RECORDING_INIT_BUFFER_SIZE; + rec->commands = (vkvg_record_t*)malloc(rec->commandsReservedCount * sizeof (vkvg_record_t)); + rec->buffer = malloc (rec->bufferReservedSize); + + return rec; +} +void _destroy_recording (vkvg_recording_t* rec) { + free(rec->commands); + free(rec->buffer); + free(rec); +} +void _start_recording (VkvgContext ctx) { + if (ctx->recording) + _destroy_recording(ctx->recording); + ctx->recording = _new_recording(); +} +vkvg_recording_t* _stop_recording (VkvgContext ctx) { + vkvg_recording_t* rec = ctx->recording; + if (!rec) + return NULL; + if (!rec->commandsCount) { + _destroy_recording(rec); + ctx->recording = NULL; + return NULL; + } + /*rec->buffer = realloc(rec->buffer, rec->bufferSize); + rec->commands = (vkvg_record_t*)realloc(rec->commands, rec->commandsCount * sizeof (vkvg_record_t));*/ + ctx->recording = NULL; + return rec; +} +void* _ensure_recording_buffer (vkvg_recording_t* rec, size_t size) { + if (rec->bufferReservedSize >= rec->bufferSize - VKVG_RECORDING_INIT_BUFFER_SIZE_TRESHOLD - size) { + rec->bufferReservedSize += VKVG_RECORDING_INIT_BUFFER_SIZE; + rec->buffer = realloc(rec->buffer, rec->bufferReservedSize); + } + return rec->buffer + rec->bufferSize; +} +void* _advance_recording_buffer_unchecked (vkvg_recording_t* rec, size_t size) { + rec->bufferSize += size; + return rec->buffer + rec->bufferSize; +} + +#define STORE_FLOATS(floatcount) \ + for (i=0; icommandsCount == rec->commandsReservedCount) { + rec->commandsReservedCount += VKVG_RECORDING_INIT_COMMANDS_COUNT; + rec->commands = (vkvg_record_t*)realloc(rec->commands, rec->commandsReservedCount * sizeof (vkvg_record_t)); + } + vkvg_record_t* r = &rec->commands[rec->commandsCount++]; + r->cmd = cmd; + r->dataOffset = rec->bufferSize; + + void* buff; + + + int i = 0; + switch (cmd) { + case VKVG_CMD_MOVE_TO: + case VKVG_CMD_LINE_TO: + case VKVG_CMD_REL_MOVE_TO: + case VKVG_CMD_REL_LINE_TO: + STORE_FLOATS(2); + break; + case VKVG_CMD_SAVE: + case VKVG_CMD_RESTORE: + case VKVG_CMD_NEW_PATH: + case VKVG_CMD_NEW_SUB_PATH: + case VKVG_CMD_CLOSE_PATH: + case VKVG_CMD_PAINT: + case VKVG_CMD_FILL: + case VKVG_CMD_STROKE: + case VKVG_CMD_CLIP: + case VKVG_CMD_CLEAR: + case VKVG_CMD_FILL_PRESERVE: + case VKVG_CMD_STROKE_PRESERVE: + case VKVG_CMD_CLIP_PRESERVE: + break; + case VKVG_CMD_RECTANGLE: + //case VKVG_CMD_ELLIPSE: + case VKVG_CMD_SET_SOURCE_RGBA: + case VKVG_CMD_QUADRATIC_TO: + case VKVG_CMD_REL_QUADRATIC_TO: + STORE_FLOATS(4); + break; + case VKVG_CMD_ARC: + case VKVG_CMD_ARC_NEG: + STORE_FLOATS(5); + break; + case VKVG_CMD_CURVE_TO: + case VKVG_CMD_REL_CURVE_TO: + STORE_FLOATS(6); + break; + case VKVG_CMD_ELLIPTICAL_ARC_TO: + STORE_FLOATS(5); + STORE_BOOLS(2); + break; + case VKVG_CMD_SET_SOURCE_RGB: + STORE_FLOATS(3); + break; + case VKVG_CMD_REL_ELLIPTICAL_ARC_TO: + STORE_FLOATS(5); + STORE_BOOLS(2); + break; + case VKVG_CMD_SET_SOURCE: + buff = _ensure_recording_buffer (rec, sizeof(VkvgPattern)); + *(VkvgPattern*)buff = va_arg(args, VkvgPattern); + _advance_recording_buffer_unchecked (rec, sizeof(VkvgPattern)); + break; + case VKVG_CMD_SET_SOURCE_SURFACE: + STORE_FLOATS(2); + buff = _ensure_recording_buffer (rec, sizeof(VkvgSurface)); + *(VkvgSurface*)buff = va_arg(args, VkvgSurface); + _advance_recording_buffer_unchecked (rec, sizeof(VkvgSurface)); + break; + } + va_end(args); +} +void _replay_command (VkvgContext ctx, VkvgRecording rec, uint32_t index) { + vkvg_record_t* r = &rec->commands[index]; + float* floats = (float*)(rec->buffer + r->dataOffset); + switch (r->cmd) { + case VKVG_CMD_SAVE: + vkvg_save (ctx); + break; + case VKVG_CMD_RESTORE: + vkvg_restore (ctx); + break; + case VKVG_CMD_NEW_PATH: + vkvg_new_path (ctx); + break; + case VKVG_CMD_NEW_SUB_PATH: + vkvg_new_sub_path (ctx); + break; + case VKVG_CMD_CLOSE_PATH: + vkvg_close_path (ctx); + break; + case VKVG_CMD_PAINT: + vkvg_paint (ctx); + break; + case VKVG_CMD_FILL: + vkvg_fill (ctx); + break; + case VKVG_CMD_STROKE: + vkvg_stroke (ctx); + break; + case VKVG_CMD_CLIP: + vkvg_clip (ctx); + break; + case VKVG_CMD_CLEAR: + vkvg_clear (ctx); + break; + case VKVG_CMD_FILL_PRESERVE: + vkvg_fill_preserve (ctx); + break; + case VKVG_CMD_STROKE_PRESERVE: + vkvg_stroke_preserve (ctx); + break; + case VKVG_CMD_CLIP_PRESERVE: + vkvg_clip_preserve (ctx); + break; + case VKVG_CMD_RECTANGLE: + vkvg_rectangle (ctx, floats[0], floats[1], floats[2], floats[3]); + break; + case VKVG_CMD_ARC: + vkvg_arc (ctx, floats[0], floats[1], floats[2], floats[3], floats[4]); + break; + case VKVG_CMD_ARC_NEG: + vkvg_arc (ctx, floats[0], floats[1], floats[2], floats[3], floats[4]); + break; + /*case VKVG_CMD_ELLIPSE: + vkvg_ellipse (ctx, floats[0], floats[1], floats[2], floats[3], floats[4]); + break;*/ + case VKVG_CMD_MOVE_TO: + vkvg_move_to(ctx, floats[0], floats[1]); + break; + case VKVG_CMD_LINE_TO: + vkvg_line_to(ctx, floats[0], floats[1]); + break; + case VKVG_CMD_CURVE_TO: + vkvg_curve_to (ctx, floats[0], floats[1], floats[2], floats[3], floats[4], floats[5]); + break; + case VKVG_CMD_ELLIPTICAL_ARC_TO: + { + bool* flags = (bool*)&floats[5]; + vkvg_elliptic_arc_to (ctx, floats[0], floats[1], flags[0], flags[1], floats[2], floats[3], floats[4]); + } + break; + case VKVG_CMD_QUADRATIC_TO: + vkvg_quadratic_to (ctx, floats[0], floats[1], floats[2], floats[3]); + break; + + case VKVG_CMD_REL_MOVE_TO: + vkvg_rel_move_to(ctx, floats[0], floats[1]); + break; + case VKVG_CMD_REL_LINE_TO: + vkvg_rel_line_to(ctx, floats[0], floats[1]); + break; + case VKVG_CMD_REL_CURVE_TO: + vkvg_rel_curve_to (ctx, floats[0], floats[1], floats[2], floats[3], floats[4], floats[5]); + break; + case VKVG_CMD_REL_QUADRATIC_TO: + vkvg_rel_quadratic_to (ctx, floats[0], floats[1], floats[2], floats[3]); + break; + case VKVG_CMD_REL_ELLIPTICAL_ARC_TO: + { + bool* flags = (bool*)&floats[5]; + vkvg_rel_elliptic_arc_to (ctx, floats[0], floats[1], flags[0], flags[1], floats[2], floats[3], floats[4]); + } + break; + case VKVG_CMD_SET_SOURCE_RGB: + vkvg_set_source_rgb (ctx, floats[0], floats[1], floats[2]); + break; + case VKVG_CMD_SET_SOURCE_RGBA: + vkvg_set_source_rgba (ctx, floats[0], floats[1], floats[2], floats[3]); + break; + case VKVG_CMD_SET_SOURCE: + { + VkvgPattern pat = (VkvgPattern)(rec->buffer + r->dataOffset); + vkvg_set_source (ctx, pat); + } + break; + case VKVG_CMD_SET_SOURCE_SURFACE: + { + VkvgSurface surf = (VkvgSurface)&floats[2]; + vkvg_set_source_surface (ctx, surf, floats[0], floats[1]); + } + break; + } +} + diff --git a/src/recording/vkvg_record_internal.h b/src/recording/vkvg_record_internal.h new file mode 100644 index 0000000..64b6ee4 --- /dev/null +++ b/src/recording/vkvg_record_internal.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018-2022 Jean-Philippe Bruyère + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef VKVG_RECORD_INTERNAL_H +#define VKVG_RECORD_INTERNAL_H + +#include "vkvg.h" +#include "vkvg_internal.h" + +#define VKVG_CMD_SAVE 0x0001 +#define VKVG_CMD_RESTORE 0x0002 + +#define VKVG_CMD_PATH_COMMANDS 0x0100 +#define VKVG_CMD_DRAW_COMMANDS 0x0200 +#define VKVG_CMD_RELATIVE_COMMANDS 0x0400|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_PRESERVE_COMMANDS 0x0400|VKVG_CMD_DRAW_COMMANDS +#define VKVG_CMD_PATTERN_COMMANDS 0x0800 +#define VKVG_CMD_TRANSFORM_COMMANDS 0x2000 +#define VKVG_CMD_TEXT_COMMANDS 0x4000 + +#define VKVG_CMD_NEW_PATH 0x0001|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_NEW_SUB_PATH 0x0002|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_CLOSE_PATH 0x0003|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_MOVE_TO 0x0004|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_LINE_TO 0x0005|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_RECTANGLE 0x0006|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_ARC 0x0007|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_ARC_NEG 0x0008|VKVG_CMD_PATH_COMMANDS +//#define VKVG_CMD_ELLIPSE 0x0009|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_CURVE_TO 0x000A|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_QUADRATIC_TO 0x000B|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_ELLIPTICAL_ARC_TO 0x000C|VKVG_CMD_PATH_COMMANDS + +#define VKVG_CMD_SET_LINE_WIDTH 0x1001|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_SET_LINE_JOIN 0x1002|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_SET_LINE_CAP 0x1003|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_SET_OPERATOR 0x1004|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_SET_FILL_RULE 0x1005|VKVG_CMD_PATH_COMMANDS +#define VKVG_CMD_SET_DASH 0x1006|VKVG_CMD_PATH_COMMANDS + +#define VKVG_CMD_TRANSLATE 0x0001|VKVG_CMD_TRANSFORM_COMMANDS +#define VKVG_CMD_ROTATE 0x0002|VKVG_CMD_TRANSFORM_COMMANDS +#define VKVG_CMD_SCALE 0x0003|VKVG_CMD_TRANSFORM_COMMANDS +#define VKVG_CMD_TRANSFORM 0x0004|VKVG_CMD_TRANSFORM_COMMANDS +#define VKVG_CMD_IDENTITY_MATRIX 0x0005|VKVG_CMD_TRANSFORM_COMMANDS + +#define VKVG_CMD_SET_MATRIX 0x0006|VKVG_CMD_TRANSFORM_COMMANDS + +#define VKVG_CMD_SET_FONT_SIZE 0x0001|VKVG_CMD_TEXT_COMMANDS +#define VKVG_CMD_SET_FONT_FACE 0x0002|VKVG_CMD_TEXT_COMMANDS +#define VKVG_CMD_SET_FONT_PATH 0x0003|VKVG_CMD_TEXT_COMMANDS +#define VKVG_CMD_SHOW_TEXT 0x0004|VKVG_CMD_TEXT_COMMANDS + +#define VKVG_CMD_REL_MOVE_TO VKVG_CMD_MOVE_TO |VKVG_CMD_RELATIVE_COMMANDS +#define VKVG_CMD_REL_LINE_TO VKVG_CMD_LINE_TO |VKVG_CMD_RELATIVE_COMMANDS +#define VKVG_CMD_REL_CURVE_TO VKVG_CMD_CURVE_TO |VKVG_CMD_RELATIVE_COMMANDS +#define VKVG_CMD_REL_QUADRATIC_TO VKVG_CMD_QUADRATIC_TO |VKVG_CMD_RELATIVE_COMMANDS +#define VKVG_CMD_REL_ELLIPTICAL_ARC_TO VKVG_CMD_ELLIPTICAL_ARC_TO |VKVG_CMD_RELATIVE_COMMANDS + +#define VKVG_CMD_PAINT 0x0001|VKVG_CMD_DRAW_COMMANDS +#define VKVG_CMD_FILL 0x0002|VKVG_CMD_DRAW_COMMANDS +#define VKVG_CMD_STROKE 0x0003|VKVG_CMD_DRAW_COMMANDS +#define VKVG_CMD_CLIP 0x0004|VKVG_CMD_DRAW_COMMANDS +#define VKVG_CMD_CLEAR 0x0005|VKVG_CMD_DRAW_COMMANDS + +#define VKVG_CMD_FILL_PRESERVE VKVG_CMD_FILL |VKVG_CMD_PRESERVE_COMMANDS +#define VKVG_CMD_STROKE_PRESERVE VKVG_CMD_STROKE |VKVG_CMD_PRESERVE_COMMANDS +#define VKVG_CMD_CLIP_PRESERVE VKVG_CMD_CLIP |VKVG_CMD_PRESERVE_COMMANDS + +#define VKVG_CMD_SET_SOURCE_RGB 0x0001|VKVG_CMD_PATTERN_COMMANDS +#define VKVG_CMD_SET_SOURCE_RGBA 0x0002|VKVG_CMD_PATTERN_COMMANDS +#define VKVG_CMD_SET_SOURCE 0x0003|VKVG_CMD_PATTERN_COMMANDS +#define VKVG_CMD_SET_SOURCE_SURFACE 0x0004|VKVG_CMD_PATTERN_COMMANDS + + + +typedef struct _vkvg_record_t{ + uint16_t cmd; + size_t dataOffset; +}vkvg_record_t; + +typedef struct _vkvg_recording_t{ + vkvg_record_t* commands; + uint32_t commandsCount; + uint32_t commandsReservedCount; + size_t bufferSize; + size_t bufferReservedSize; + void* buffer; +}vkvg_recording_t; + + +void _start_recording (VkvgContext ctx); +vkvg_recording_t* _stop_recording (VkvgContext ctx); +void _destroy_recording (vkvg_recording_t* rec); +void _replay_command (VkvgContext ctx, VkvgRecording rec, uint32_t index); +void _record (vkvg_recording_t* rec,...); + +#define RECORD(ctx,...) {\ + if (ctx->recording) \ + _record (ctx->recording,__VA_ARGS__);\ +} + + +#endif diff --git a/src/vkvg_context.c b/src/vkvg_context.c index 7f7936b..aaa3949 100644 --- a/src/vkvg_context.c +++ b/src/vkvg_context.c @@ -35,7 +35,6 @@ const float DBG_LAB_COLOR_CLIP[4] = {0,1,1,1}; #endif #endif - //todo:this could be used to define a default background static VkClearValue clearValues[3] = { { .color.float32 = {0,0,0,0} }, @@ -314,6 +313,7 @@ void vkvg_new_sub_path (VkvgContext ctx){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_NEW_SUB_PATH); LOG(VKVG_LOG_INFO_CMD, "\tCMD: new_sub_path:\n"); _finish_path(ctx); @@ -322,6 +322,7 @@ void vkvg_new_path (VkvgContext ctx){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_NEW_PATH); LOG(VKVG_LOG_INFO_CMD, "\tCMD: new_path:\n"); _clear_path(ctx); @@ -330,6 +331,7 @@ void vkvg_close_path (VkvgContext ctx){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_CLOSE_PATH); LOG(VKVG_LOG_INFO_CMD, "\tCMD: close_path:\n"); if (ctx->pathes[ctx->pathPtr] & PATH_CLOSED_BIT) //already closed @@ -350,36 +352,41 @@ void vkvg_close_path (VkvgContext ctx){ _finish_path(ctx); } +void _line_to (VkvgContext ctx, float x, float y) { + vec2 p = {x,y}; + if (!_current_path_is_empty (ctx)){ + //prevent adding the same point + if (vec2_equ (_get_current_position (ctx), p)) + return; + } + _add_point (ctx, x, y); +} void vkvg_rel_line_to (VkvgContext ctx, float dx, float dy){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_REL_LINE_TO, dx, dy); LOG(VKVG_LOG_INFO_CMD, "\tCMD: rel_line_to:\n"); if (_current_path_is_empty(ctx)) _add_point(ctx, 0, 0); vec2 cp = _get_current_position(ctx); - vkvg_line_to(ctx, cp.x + dx, cp.y + dy); + _line_to(ctx, cp.x + dx, cp.y + dy); } void vkvg_line_to (VkvgContext ctx, float x, float y) { if (ctx->status) return; + RECORD(ctx, VKVG_CMD_LINE_TO, x, y); LOG(VKVG_LOG_INFO_CMD, "\tCMD: line_to:\n"); - - vec2 p = {x,y}; - if (!_current_path_is_empty (ctx)){ - //prevent adding the same point - if (vec2_equ (_get_current_position (ctx), p)) - return; - } - _add_point (ctx, x, y); + _line_to(ctx, x, y); } void vkvg_arc (VkvgContext ctx, float xc, float yc, float radius, float a1, float a2){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_ARC, xc, yc, radius, a1, a2); LOG(VKVG_LOG_INFO_CMD, "\tCMD: arc:\n"); while (a2 < a1)//positive arc must have a1status) return; + RECORD(ctx, VKVG_CMD_ARC_NEG, xc, yc, radius, a1, a2); LOG(VKVG_LOG_INFO_CMD, "\tCMD: arc_neg:\n"); while (a2 > a1) a2 -= 2.f*M_PIF; @@ -478,6 +486,7 @@ void vkvg_rel_move_to (VkvgContext ctx, float x, float y) { if (ctx->status) return; + RECORD(ctx, VKVG_CMD_REL_MOVE_TO, x, y); LOG(VKVG_LOG_INFO_CMD, "\tCMD: rel_mote_to:\n"); if (_current_path_is_empty(ctx)) _add_point(ctx, 0, 0); @@ -488,6 +497,7 @@ void vkvg_move_to (VkvgContext ctx, float x, float y) { if (ctx->status) return; + RECORD(ctx, VKVG_CMD_MOVE_TO, x, y); LOG(VKVG_LOG_INFO_CMD, "\tCMD: move_to:\n"); _finish_path(ctx); _add_point (ctx, x, y); @@ -504,6 +514,7 @@ void vkvg_get_current_point (VkvgContext ctx, float* x, float* y) { void vkvg_rel_quadratic_to (VkvgContext ctx, float x1, float y1, float x2, float y2) { if (ctx->status) return; + RECORD(ctx, VKVG_CMD_REL_QUADRATIC_TO, x1, y1, x2, y2); LOG(VKVG_LOG_INFO_CMD, "\tCMD: rel_quadratic_to:\n"); vec2 cp = _get_current_position(ctx); vkvg_quadratic_to (ctx, cp.x + x1, cp.y + y1, cp.x + x2, cp.y + y2); @@ -512,6 +523,7 @@ const double quadraticFact = 2.0/3.0; void vkvg_quadratic_to (VkvgContext ctx, float x1, float y1, float x2, float y2) { if (ctx->status) return; + RECORD(ctx, VKVG_CMD_QUADRATIC_TO, x1, y1, x2, y2); LOG(VKVG_LOG_INFO_CMD, "\tCMD: quadratic_to:\n"); float x0, y0; @@ -530,6 +542,7 @@ void vkvg_quadratic_to (VkvgContext ctx, float x1, float y1, float x2, float y2) void vkvg_curve_to (VkvgContext ctx, float x1, float y1, float x2, float y2, float x3, float y3) { if (ctx->status) return; + RECORD(ctx, VKVG_CMD_CURVE_TO, x1, y1, x2, y2, x3, y3); LOG(VKVG_LOG_INFO_CMD, "\tCMD: curve_to:\n"); //prevent running _recursive_bezier when all 4 curve points are equal if (EQUF(x1,x2) && EQUF(x2,x3) && EQUF(y1,y2) && EQUF(y2,y3)) { @@ -557,6 +570,7 @@ void vkvg_curve_to (VkvgContext ctx, float x1, float y1, float x2, float y2, flo void vkvg_rel_curve_to (VkvgContext ctx, float x1, float y1, float x2, float y2, float x3, float y3) { if (ctx->status) return; + RECORD(ctx, VKVG_CMD_REL_CURVE_TO, x1, y1, x2, y2, x3, y3); LOG(VKVG_LOG_INFO_CMD, "\tCMD: rel_curve_to:\n"); vec2 cp = _get_current_position(ctx); vkvg_curve_to (ctx, cp.x + x1, cp.y + y1, cp.x + x2, cp.y + y2, cp.x + x3, cp.y + y3); @@ -572,6 +586,7 @@ void vkvg_fill_rectangle (VkvgContext ctx, float x, float y, float w, float h){ vkvg_status_t vkvg_rectangle (VkvgContext ctx, float x, float y, float w, float h){ if (ctx->status) return ctx->status; + RECORD(ctx, VKVG_CMD_RECTANGLE, x, y, w, h); LOG(VKVG_LOG_INFO_CMD, "\tCMD: rectangle:\n"); _finish_path (ctx); @@ -632,6 +647,20 @@ void vkvg_rounded_rectangle2 (VkvgContext ctx, float x, float y, float w, float vkvg_close_path(ctx); } +void vkvg_path_extents (VkvgContext ctx, float *x1, float *y1, float *x2, float *y2) { + if (ctx->status) + return; + + _finish_path(ctx); + + if (!ctx->pathPtr) {//no path + *x1 = *x2 = *y1 = *y2 = 0; + return; + } + + _vkvg_path_extents(ctx, false, x1, y1, x2, y2); +} + static const VkClearAttachment clearStencil = {VK_IMAGE_ASPECT_STENCIL_BIT, 1, {{{0}}}}; static const VkClearAttachment clearColorAttach = {VK_IMAGE_ASPECT_COLOR_BIT, 0, {{{0}}}}; @@ -653,6 +682,7 @@ void vkvg_reset_clip (VkvgContext ctx){ void vkvg_clear (VkvgContext ctx){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_CLEAR); _emit_draw_cmd_undrawn_vertices(ctx); if (!ctx->cmdStarted) { ctx->renderPassBeginInfo.renderPass = ctx->pSurf->dev->renderPass_ClearAll; @@ -662,24 +692,7 @@ void vkvg_clear (VkvgContext ctx){ VkClearAttachment ca[2] = {clearColorAttach, clearStencil}; vkCmdClearAttachments(ctx->cmd, 2, ca, 1, &ctx->clearRect); } - -void vkvg_clip (VkvgContext ctx){ - vkvg_clip_preserve(ctx); - _clear_path(ctx); -} -void vkvg_stroke (VkvgContext ctx) -{ - vkvg_stroke_preserve(ctx); - _clear_path(ctx); -} -void vkvg_fill (VkvgContext ctx){ - vkvg_fill_preserve(ctx); - _clear_path(ctx); -} -void vkvg_clip_preserve (VkvgContext ctx){ - if (ctx->status) - return; - +void _clip_preserve (VkvgContext ctx){ _finish_path(ctx); if (!ctx->pathPtr)//nothing to clip @@ -719,25 +732,7 @@ void vkvg_clip_preserve (VkvgContext ctx){ vkh_cmd_label_end (ctx->cmd); #endif } - - -void vkvg_path_extents (VkvgContext ctx, float *x1, float *y1, float *x2, float *y2) { - if (ctx->status) - return; - - _finish_path(ctx); - - if (!ctx->pathPtr) {//no path - *x1 = *x2 = *y1 = *y2 = 0; - return; - } - - _vkvg_path_extents(ctx, false, x1, y1, x2, y2); -} -void vkvg_fill_preserve (VkvgContext ctx){ - if (ctx->status) - return; - +void _fill_preserve (VkvgContext ctx){ _finish_path(ctx); if (!ctx->pathPtr)//nothing to fill @@ -763,12 +758,8 @@ void vkvg_fill_preserve (VkvgContext ctx){ _ensure_renderpass_is_started(ctx); _fill_non_zero(ctx); } - -void vkvg_stroke_preserve (VkvgContext ctx) +void _stroke_preserve (VkvgContext ctx) { - if (ctx->status) - return; - _finish_path(ctx); if (!ctx->pathPtr)//nothing to stroke @@ -889,10 +880,52 @@ void vkvg_stroke_preserve (VkvgContext ctx) } } -void vkvg_paint (VkvgContext ctx){ + +void vkvg_clip (VkvgContext ctx){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_CLIP); + _clip_preserve(ctx); + _clear_path(ctx); +} +void vkvg_stroke (VkvgContext ctx) +{ + if (ctx->status) + return; + RECORD(ctx, VKVG_CMD_STROKE); + _stroke_preserve(ctx); + _clear_path(ctx); +} +void vkvg_fill (VkvgContext ctx){ + if (ctx->status) + return; + RECORD(ctx, VKVG_CMD_FILL); + _fill_preserve(ctx); + _clear_path(ctx); +} +void vkvg_clip_preserve (VkvgContext ctx) { + if (ctx->status) + return; + RECORD(ctx, VKVG_CMD_CLIP_PRESERVE); + _clip_preserve (ctx); +} +void vkvg_fill_preserve (VkvgContext ctx) { + if (ctx->status) + return; + RECORD(ctx, VKVG_CMD_FILL_PRESERVE); + _fill_preserve (ctx); +} +void vkvg_stroke_preserve (VkvgContext ctx) { + if (ctx->status) + return; + RECORD(ctx, VKVG_CMD_STROKE_PRESERVE); + _stroke_preserve (ctx); +} +void vkvg_paint (VkvgContext ctx){ + if (ctx->status) + return; + RECORD(ctx, VKVG_CMD_PAINT); _finish_path (ctx); if (ctx->pathPtr) { @@ -910,18 +943,24 @@ void vkvg_set_source_color (VkvgContext ctx, uint32_t c) { _update_cur_pattern (ctx, NULL); } void vkvg_set_source_rgb (VkvgContext ctx, float r, float g, float b) { - vkvg_set_source_rgba (ctx, r, g, b, 1); + if (ctx->status) + return; + RECORD(ctx, VKVG_CMD_SET_SOURCE_RGB, r, g, b); + ctx->curColor = CreateRgbaf(r,g,b,1); + _update_cur_pattern (ctx, NULL); } void vkvg_set_source_rgba (VkvgContext ctx, float r, float g, float b, float a) { if (ctx->status) return; + RECORD(ctx, VKVG_CMD_SET_SOURCE_RGBA, r, g, b, a); ctx->curColor = CreateRgbaf(r,g,b,a); _update_cur_pattern (ctx, NULL); } void vkvg_set_source_surface(VkvgContext ctx, VkvgSurface surf, float x, float y){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_SET_SOURCE_SURFACE, x, y, surf); ctx->pushConsts.source.x = x; ctx->pushConsts.source.y = y; _update_cur_pattern (ctx, vkvg_pattern_create_for_surface(surf)); @@ -930,21 +969,26 @@ void vkvg_set_source_surface(VkvgContext ctx, VkvgSurface surf, float x, float y void vkvg_set_source (VkvgContext ctx, VkvgPattern pat){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_SET_SOURCE, pat); _update_cur_pattern (ctx, pat); vkvg_pattern_reference (pat); } void vkvg_set_line_width (VkvgContext ctx, float width){ + RECORD(ctx, VKVG_CMD_SET_LINE_WIDTH, width); ctx->lineWidth = width; } void vkvg_set_line_cap (VkvgContext ctx, vkvg_line_cap_t cap){ + RECORD(ctx, VKVG_CMD_SET_LINE_CAP, cap); ctx->lineCap = cap; } void vkvg_set_line_join (VkvgContext ctx, vkvg_line_join_t join){ + RECORD(ctx, VKVG_CMD_SET_LINE_JOIN, join); ctx->lineJoin = join; } void vkvg_set_operator (VkvgContext ctx, vkvg_operator_t op){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_SET_OPERATOR, op); if (op == ctx->curOperator) return; @@ -957,6 +1001,7 @@ void vkvg_set_operator (VkvgContext ctx, vkvg_operator_t op){ } void vkvg_set_fill_rule (VkvgContext ctx, vkvg_fill_rule_t fr){ #ifndef __APPLE__ + RECORD(ctx, VKVG_CMD_SET_FILL_RULE, fr); ctx->curFillRule = fr; #endif } @@ -971,6 +1016,7 @@ void vkvg_set_dash (VkvgContext ctx, const float* dashes, uint32_t num_dashes, f return; if (ctx->dashCount > 0) free (ctx->dashes); + RECORD(ctx, VKVG_CMD_SET_DASH, num_dashes, offset, dashes); ctx->dashCount = num_dashes; ctx->dashOffset = offset; if (ctx->dashCount == 0) @@ -1004,17 +1050,20 @@ VkvgPattern vkvg_get_source (VkvgContext ctx){ void vkvg_select_font_face (VkvgContext ctx, const char* name){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_SET_FONT_FACE, name); _select_font_face (ctx, name); } void vkvg_load_font_from_path (VkvgContext ctx, const char* path, const char* name){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_SET_FONT_PATH, name); _add_new_font_identity(ctx, path, name); _select_font_face (ctx, name); } void vkvg_set_font_size (VkvgContext ctx, uint32_t size){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_SET_FONT_SIZE, size); #ifdef VKVG_USE_FREETYPE long newSize = size << 6; #else @@ -1034,6 +1083,7 @@ void vkvg_set_text_direction (vkvg_context* ctx, vkvg_direction_t direction){ void vkvg_show_text (VkvgContext ctx, const char* text){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_SHOW_TEXT, text); LOG(VKVG_LOG_INFO_CMD, "CMD: show_text:\n"); //_ensure_renderpass_is_started(ctx); _show_text (ctx, text); @@ -1074,6 +1124,7 @@ void vkvg_font_extents (VkvgContext ctx, vkvg_font_extents_t* extents) { void vkvg_save (VkvgContext ctx){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_SAVE); LOG(VKVG_LOG_INFO, "SAVE CONTEXT: ctx = %p\n", ctx); _flush_cmd_buff (ctx); @@ -1185,6 +1236,7 @@ void vkvg_save (VkvgContext ctx){ void vkvg_restore (VkvgContext ctx){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_RESTORE); if (ctx->pSavedCtxs == NULL){ ctx->status = VKVG_STATUS_INVALID_RESTORE; @@ -1301,6 +1353,7 @@ void vkvg_restore (VkvgContext ctx){ void vkvg_translate (VkvgContext ctx, float dx, float dy){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_TRANSLATE, dx, dy); LOG(VKVG_LOG_INFO_CMD, "CMD: translate:\n"); _emit_draw_cmd_undrawn_vertices(ctx); vkvg_matrix_translate (&ctx->pushConsts.mat, dx, dy); @@ -1309,6 +1362,7 @@ void vkvg_translate (VkvgContext ctx, float dx, float dy){ void vkvg_scale (VkvgContext ctx, float sx, float sy){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_SCALE, sx, sy); LOG(VKVG_LOG_INFO_CMD, "CMD: scale:\n"); _emit_draw_cmd_undrawn_vertices(ctx); vkvg_matrix_scale (&ctx->pushConsts.mat, sx, sy); @@ -1317,6 +1371,7 @@ void vkvg_scale (VkvgContext ctx, float sx, float sy){ void vkvg_rotate (VkvgContext ctx, float radians){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_ROTATE, radians); LOG(VKVG_LOG_INFO_CMD, "CMD: rotate:\n"); _emit_draw_cmd_undrawn_vertices(ctx); vkvg_matrix_rotate (&ctx->pushConsts.mat, radians); @@ -1325,6 +1380,7 @@ void vkvg_rotate (VkvgContext ctx, float radians){ void vkvg_transform (VkvgContext ctx, const vkvg_matrix_t* matrix) { if (ctx->status) return; + RECORD(ctx, VKVG_CMD_TRANSFORM, matrix); LOG(VKVG_LOG_INFO_CMD, "CMD: transform:\n"); _emit_draw_cmd_undrawn_vertices(ctx); vkvg_matrix_t res; @@ -1335,6 +1391,7 @@ void vkvg_transform (VkvgContext ctx, const vkvg_matrix_t* matrix) { void vkvg_identity_matrix (VkvgContext ctx) { if (ctx->status) return; + RECORD(ctx, VKVG_CMD_IDENTITY_MATRIX); LOG(VKVG_LOG_INFO_CMD, "CMD: identity_matrix:\n"); _emit_draw_cmd_undrawn_vertices(ctx); vkvg_matrix_t im = VKVG_IDENTITY_MATRIX; @@ -1344,6 +1401,7 @@ void vkvg_identity_matrix (VkvgContext ctx) { void vkvg_set_matrix (VkvgContext ctx, const vkvg_matrix_t* matrix){ if (ctx->status) return; + RECORD(ctx, VKVG_CMD_SET_MATRIX, matrix); LOG(VKVG_LOG_INFO_CMD, "CMD: set_matrix:\n"); _emit_draw_cmd_undrawn_vertices(ctx); ctx->pushConsts.mat = (*matrix); @@ -1353,22 +1411,24 @@ void vkvg_get_matrix (VkvgContext ctx, const vkvg_matrix_t* matrix){ memcpy ((void*)matrix, &ctx->pushConsts.mat, sizeof(vkvg_matrix_t)); } -void vkvg_elliptic_arc_to (VkvgContext ctx, float x2, float y2, bool largeArc, bool counterClockWise, float rx, float ry, float phi) { +void vkvg_elliptic_arc_to (VkvgContext ctx, float x2, float y2, bool largeArc, bool sweepFlag, float rx, float ry, float phi) { if (ctx->status) return; + RECORD(ctx, VKVG_CMD_ELLIPTICAL_ARC_TO, x2, y2, rx, ry, phi, largeArc, sweepFlag); LOG(VKVG_LOG_INFO_CMD, "\tCMD: elliptic_arc_to:\n"); float x1, y1; vkvg_get_current_point(ctx, &x1, &y1); - _elliptic_arc(ctx, x1, y1, x2, y2, largeArc, counterClockWise, rx, ry, phi); + _elliptic_arc(ctx, x1, y1, x2, y2, largeArc, sweepFlag, rx, ry, phi); } -void vkvg_rel_elliptic_arc_to (VkvgContext ctx, float x2, float y2, bool largeArc, bool counterClockWise, float rx, float ry, float phi) { +void vkvg_rel_elliptic_arc_to (VkvgContext ctx, float x2, float y2, bool largeArc, bool sweepFlag, float rx, float ry, float phi) { if (ctx->status) return; - LOG(VKVG_LOG_INFO_CMD, "\tCMD: rel_elliptic_arc_to: x2:%10.5f y2:%10.5f large:%d sweep:%d rx:%10.5f ry:%10.5f phi:%10.5f \n", x2,y2,largeArc,counterClockWise,rx,ry,phi); + RECORD(ctx, VKVG_CMD_REL_ELLIPTICAL_ARC_TO, x2, y2, rx, ry, phi, largeArc, sweepFlag); + LOG(VKVG_LOG_INFO_CMD, "\tCMD: rel_elliptic_arc_to: x2:%10.5f y2:%10.5f large:%d sweep:%d rx:%10.5f ry:%10.5f phi:%10.5f \n", x2,y2,largeArc,sweepFlag,rx,ry,phi); float x1, y1; vkvg_get_current_point(ctx, &x1, &y1); - _elliptic_arc(ctx, x1, y1, x2+x1, y2+y1, largeArc, counterClockWise, rx, ry, phi); + _elliptic_arc(ctx, x1, y1, x2+x1, y2+y1, largeArc, sweepFlag, rx, ry, phi); } void vkvg_ellipse (VkvgContext ctx, float radiusX, float radiusY, float x, float y, float rotationAngle) { diff --git a/src/vkvg_context_internal.h b/src/vkvg_context_internal.h index 3183cda..de0a56b 100644 --- a/src/vkvg_context_internal.h +++ b/src/vkvg_context_internal.h @@ -28,6 +28,12 @@ #include "vkh.h" #include "vkvg_fonts.h" +#if VKVG_RECORDING +#include "vkvg_record_internal.h" +#else +#define RECORD(ctx,cmd,...) +#endif + #define VKVG_PTS_SIZE 1024 #define VKVG_VBO_SIZE (VKVG_PTS_SIZE * 4) #define VKVG_IBO_SIZE (VKVG_VBO_SIZE * 6) @@ -131,6 +137,10 @@ typedef struct _vkvg_context_t { uint32_t tesselator_idx_counter; #endif +#if VKVG_RECORDING + vkvg_recording_t* recording; +#endif + vkvg_buff uboGrad; //uniform buff obj holdings gradient infos //vk buffers, holds data until flush diff --git a/tests/recording.c b/tests/recording.c new file mode 100644 index 0000000..495ec4a --- /dev/null +++ b/tests/recording.c @@ -0,0 +1,48 @@ +#include "test.h" + +#if VKVG_RECORDING +void test(){ + VkvgContext ctx = _initCtx(surf); + + vkvg_scale(ctx,0.5,0.5); + vkvg_start_recording(ctx); + + vkvg_set_source_rgba(ctx,0.1f,0.9f,0.1f,1.0f); + vkvg_move_to(ctx,100,100); + vkvg_rel_line_to(ctx,50,200); + vkvg_rel_line_to(ctx,150,-100); + vkvg_rel_line_to(ctx,100,200); + vkvg_rel_line_to(ctx,-100,100); + vkvg_rel_line_to(ctx,-10,-100); + vkvg_rel_line_to(ctx,-190,-50); + vkvg_rel_line_to(ctx,300,50); + vkvg_rel_line_to(ctx,50,0); + vkvg_rel_line_to(ctx,0, 50); + vkvg_rel_line_to(ctx,50,0); + vkvg_rel_line_to(ctx,0, 50); + vkvg_rel_line_to(ctx,50,0); + vkvg_rel_line_to(ctx,0, 50); + vkvg_rel_line_to(ctx,50,0); + vkvg_rel_line_to(ctx,0, 50); + vkvg_close_path(ctx); + + vkvg_stroke_preserve(ctx); + vkvg_fill(ctx); + + VkvgRecording rec = vkvg_stop_recording(ctx); + + vkvg_translate(ctx,400,0); + vkvg_replay(ctx, rec); + + vkvg_destroy(ctx); + vkvg_recording_destroy(rec); +} +#endif +int main(int argc, char *argv[]) { + no_test_size = true; +#if VKVG_RECORDING + PERFORM_TEST (test, argc, argv); +#endif + return 0; +} +