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)
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)
/*************************************/
+#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
--- /dev/null
+/*
+ * Copyright (c) 2018-2022 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+ *
+ * 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; i<rec->commandsCount; i++)
+ _replay_command(ctx, rec, i);
+}
+void vkvg_recording_destroy (VkvgRecording rec) {
+ if (!rec)
+ return;
+ _destroy_recording(rec);
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2018-2022 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+ *
+ * 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; i<floatcount; i++) { \
+ buff = _ensure_recording_buffer (rec, sizeof(float)); \
+ *(float*)buff = (float)va_arg(args, double); \
+ buff = _advance_recording_buffer_unchecked (rec, sizeof(float));\
+ }
+#define STORE_BOOLS(floatcount) \
+ for (i=0; i<floatcount; i++) { \
+ buff = _ensure_recording_buffer (rec, sizeof(bool)); \
+ *(bool*)buff = (bool)va_arg(args, int); \
+ buff = _advance_recording_buffer_unchecked (rec, sizeof(bool)); \
+ }
+
+
+void _record (vkvg_recording_t* rec,...) {
+ va_list args;
+ va_start(args, rec);
+
+ uint32_t cmd = va_arg(args, uint32_t);
+
+ if (rec->commandsCount == 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;
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2018-2022 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+ *
+ * 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
#endif
#endif
-
//todo:this could be used to define a default background
static VkClearValue clearValues[3] = {
{ .color.float32 = {0,0,0,0} },
if (ctx->status)
return;
+ RECORD(ctx, VKVG_CMD_NEW_SUB_PATH);
LOG(VKVG_LOG_INFO_CMD, "\tCMD: new_sub_path:\n");
_finish_path(ctx);
if (ctx->status)
return;
+ RECORD(ctx, VKVG_CMD_NEW_PATH);
LOG(VKVG_LOG_INFO_CMD, "\tCMD: new_path:\n");
_clear_path(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
_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 a1<a2
void vkvg_arc_negative (VkvgContext ctx, float xc, float yc, float radius, float a1, float a2) {
if (ctx->status)
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;
{
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);
{
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);
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);
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;
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)) {
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);
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);
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}}}};
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;
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
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
_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
}
}
-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) {
_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));
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;
}
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
}
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)
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
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);
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);
void vkvg_restore (VkvgContext ctx){
if (ctx->status)
return;
+ RECORD(ctx, VKVG_CMD_RESTORE);
if (ctx->pSavedCtxs == NULL){
ctx->status = VKVG_STATUS_INVALID_RESTORE;
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);
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);
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);
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;
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;
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);
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) {
#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)
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
--- /dev/null
+#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;
+}
+