/* * 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) { if (!rec) return; for (uint32_t i = 0; i < rec->commandsCount; i++) { if (rec->commands[i].cmd == VKVG_CMD_SET_SOURCE) vkvg_pattern_destroy((VkvgPattern)(rec->buffer + rec->commands[i].dataOffset)); else if (rec->commands[i].cmd == VKVG_CMD_SET_SOURCE_SURFACE) vkvg_surface_destroy((VkvgSurface)(rec->buffer + rec->commands[i].dataOffset + 2 * sizeof(float))); } 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(count) \ for (i = 0; i < count; i++) { \ buff = _ensure_recording_buffer(rec, sizeof(bool)); \ *(bool *)buff = (bool)va_arg(args, int); \ _advance_recording_buffer_unchecked(rec, sizeof(bool)); \ } #define STORE_UINT32(count) \ for (i = 0; i < count; i++) { \ buff = _ensure_recording_buffer(rec, sizeof(uint32_t)); \ *(uint32_t *)buff = (uint32_t)va_arg(args, uint32_t); \ buff = _advance_recording_buffer_unchecked(rec, sizeof(uint32_t)); \ } 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; char *buff; int i = 0; if (cmd & VKVG_CMD_PATH_COMMANDS) { if ((cmd & VKVG_CMD_PATHPROPS_COMMANDS) == VKVG_CMD_PATHPROPS_COMMANDS) { switch (r->cmd) { case VKVG_CMD_SET_LINE_WIDTH: case VKVG_CMD_SET_MITER_LIMIT: STORE_FLOATS(1); break; case VKVG_CMD_SET_LINE_JOIN: STORE_UINT32(1); break; case VKVG_CMD_SET_LINE_CAP: STORE_UINT32(1); break; case VKVG_CMD_SET_OPERATOR: STORE_UINT32(1); break; case VKVG_CMD_SET_FILL_RULE: STORE_UINT32(1); break; case VKVG_CMD_SET_DASH: break; } } else { 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_RECTANGLE: 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: case VKVG_CMD_REL_ELLIPTICAL_ARC_TO: STORE_FLOATS(5); STORE_BOOLS(2); break; case VKVG_CMD_NEW_PATH: case VKVG_CMD_NEW_SUB_PATH: case VKVG_CMD_CLOSE_PATH: break; } } } else if (!(r->cmd & VKVG_CMD_DRAW_COMMANDS)) { if (r->cmd & VKVG_CMD_TRANSFORM_COMMANDS) { switch (r->cmd) { case VKVG_CMD_TRANSLATE: case VKVG_CMD_SCALE: STORE_FLOATS(2); break; case VKVG_CMD_ROTATE: STORE_FLOATS(1); break; case VKVG_CMD_IDENTITY_MATRIX: break; case VKVG_CMD_SET_MATRIX: case VKVG_CMD_TRANSFORM: { buff = _ensure_recording_buffer(rec, sizeof(vkvg_matrix_t)); vkvg_matrix_t *mat = (vkvg_matrix_t *)va_arg(args, vkvg_matrix_t *); memcpy(buff, mat, sizeof(vkvg_matrix_t)); buff = _advance_recording_buffer_unchecked(rec, sizeof(vkvg_matrix_t)); } break; } } else if (r->cmd & VKVG_CMD_PATTERN_COMMANDS) { switch (r->cmd) { case VKVG_CMD_SET_SOURCE_RGBA: STORE_FLOATS(4); break; case VKVG_CMD_SET_SOURCE_RGB: STORE_FLOATS(3); break; case VKVG_CMD_SET_SOURCE_COLOR: STORE_UINT32(1); break; case VKVG_CMD_SET_SOURCE: { buff = _ensure_recording_buffer(rec, sizeof(VkvgPattern)); VkvgPattern pat = (VkvgPattern)va_arg(args, VkvgPattern); vkvg_pattern_reference(pat); VkvgPattern *pPat = (VkvgPattern *)buff; *pPat = pat; _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 surf = (VkvgSurface)va_arg(args, VkvgSurface); vkvg_surface_reference(surf); *(VkvgSurface *)buff = surf; _advance_recording_buffer_unchecked(rec, sizeof(VkvgSurface)); } break; } } else if (r->cmd & VKVG_CMD_TEXT_COMMANDS) { char *txt; int txtLen; switch (r->cmd) { case VKVG_CMD_SET_FONT_SIZE: STORE_UINT32(1); break; case VKVG_CMD_SHOW_TEXT: case VKVG_CMD_SET_FONT_FACE: txt = (char *)va_arg(args, char *); txtLen = strlen(txt); buff = _ensure_recording_buffer(rec, txtLen * sizeof(char)); strcpy(buff, txt); _advance_recording_buffer_unchecked(rec, txtLen * sizeof(char)); break; case VKVG_CMD_SET_FONT_PATH: 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); uint32_t *uints = (uint32_t *)floats; if (r->cmd & VKVG_CMD_PATH_COMMANDS) { if ((r->cmd & VKVG_CMD_RELATIVE_COMMANDS) == VKVG_CMD_RELATIVE_COMMANDS) { switch (r->cmd) { case VKVG_CMD_REL_MOVE_TO: vkvg_rel_move_to(ctx, floats[0], floats[1]); return; case VKVG_CMD_REL_LINE_TO: vkvg_rel_line_to(ctx, floats[0], floats[1]); return; case VKVG_CMD_REL_CURVE_TO: vkvg_rel_curve_to(ctx, floats[0], floats[1], floats[2], floats[3], floats[4], floats[5]); return; case VKVG_CMD_REL_QUADRATIC_TO: vkvg_rel_quadratic_to(ctx, floats[0], floats[1], floats[2], floats[3]); return; 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]); } return; } } else if ((r->cmd & VKVG_CMD_PATHPROPS_COMMANDS) == VKVG_CMD_PATHPROPS_COMMANDS) { switch (r->cmd) { case VKVG_CMD_SET_LINE_WIDTH: vkvg_set_line_width(ctx, floats[0]); return; case VKVG_CMD_SET_MITER_LIMIT: vkvg_set_miter_limit(ctx, floats[0]); return; case VKVG_CMD_SET_LINE_JOIN: vkvg_set_line_join(ctx, (vkvg_line_join_t)uints[0]); return; case VKVG_CMD_SET_LINE_CAP: vkvg_set_line_cap(ctx, (vkvg_line_cap_t)uints[0]); return; case VKVG_CMD_SET_OPERATOR: vkvg_set_operator(ctx, (vkvg_operator_t)uints[0]); return; case VKVG_CMD_SET_FILL_RULE: vkvg_set_fill_rule(ctx, (vkvg_fill_rule_t)uints[0]); return; case VKVG_CMD_SET_DASH: vkvg_set_dash(ctx, &floats[2], uints[0], floats[1]); return; } } else { switch (r->cmd) { case VKVG_CMD_NEW_PATH: vkvg_new_path(ctx); return; case VKVG_CMD_NEW_SUB_PATH: vkvg_new_sub_path(ctx); return; case VKVG_CMD_CLOSE_PATH: vkvg_close_path(ctx); return; case VKVG_CMD_RECTANGLE: vkvg_rectangle(ctx, floats[0], floats[1], floats[2], floats[3]); return; case VKVG_CMD_ARC: vkvg_arc(ctx, floats[0], floats[1], floats[2], floats[3], floats[4]); return; case VKVG_CMD_ARC_NEG: vkvg_arc(ctx, floats[0], floats[1], floats[2], floats[3], floats[4]); return; /*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]); return; case VKVG_CMD_LINE_TO: vkvg_line_to(ctx, floats[0], floats[1]); return; case VKVG_CMD_CURVE_TO: vkvg_curve_to(ctx, floats[0], floats[1], floats[2], floats[3], floats[4], floats[5]); return; 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]); } return; case VKVG_CMD_QUADRATIC_TO: vkvg_quadratic_to(ctx, floats[0], floats[1], floats[2], floats[3]); return; } } } else if (r->cmd & VKVG_CMD_DRAW_COMMANDS) { switch (r->cmd) { case VKVG_CMD_PAINT: vkvg_paint(ctx); return; case VKVG_CMD_FILL: vkvg_fill(ctx); return; case VKVG_CMD_STROKE: vkvg_stroke(ctx); return; case VKVG_CMD_CLIP: vkvg_clip(ctx); return; case VKVG_CMD_CLEAR: vkvg_clear(ctx); return; case VKVG_CMD_FILL_PRESERVE: vkvg_fill_preserve(ctx); return; case VKVG_CMD_STROKE_PRESERVE: vkvg_stroke_preserve(ctx); return; case VKVG_CMD_CLIP_PRESERVE: vkvg_clip_preserve(ctx); return; } } else if (r->cmd & VKVG_CMD_TRANSFORM_COMMANDS) { switch (r->cmd) { case VKVG_CMD_TRANSLATE: vkvg_translate(ctx, floats[0], floats[1]); return; case VKVG_CMD_SCALE: vkvg_scale(ctx, floats[0], floats[1]); return; case VKVG_CMD_ROTATE: vkvg_rotate(ctx, floats[0]); return; case VKVG_CMD_IDENTITY_MATRIX: vkvg_identity_matrix(ctx); return; case VKVG_CMD_TRANSFORM: { vkvg_matrix_t *mat = (vkvg_matrix_t *)&floats[0]; vkvg_transform(ctx, mat); } return; case VKVG_CMD_SET_MATRIX: { vkvg_matrix_t *mat = (vkvg_matrix_t *)&floats[0]; vkvg_set_matrix(ctx, mat); } return; } } else if (r->cmd & VKVG_CMD_PATTERN_COMMANDS) { switch (r->cmd) { case VKVG_CMD_SET_SOURCE_RGB: vkvg_set_source_rgb(ctx, floats[0], floats[1], floats[2]); return; case VKVG_CMD_SET_SOURCE_RGBA: vkvg_set_source_rgba(ctx, floats[0], floats[1], floats[2], floats[3]); return; case VKVG_CMD_SET_SOURCE_COLOR: vkvg_set_source_color(ctx, uints[0]); return; case VKVG_CMD_SET_SOURCE: { VkvgPattern pat = *((VkvgPattern *)(rec->buffer + r->dataOffset)); vkvg_set_source(ctx, pat); } return; case VKVG_CMD_SET_SOURCE_SURFACE: { VkvgSurface surf = *((VkvgSurface *)&floats[2]); vkvg_set_source_surface(ctx, surf, floats[0], floats[1]); } return; } } else if (r->cmd & VKVG_CMD_TEXT_COMMANDS) { char *txt = (char *)floats; switch (r->cmd) { case VKVG_CMD_SET_FONT_SIZE: vkvg_set_font_size(ctx, uints[0]); return; case VKVG_CMD_SET_FONT_FACE: vkvg_select_font_face(ctx, txt); return; /*case VKVG_CMD_SET_FONT_PATH: vkvg_load_font_from_path (ctx, txt); return; */ case VKVG_CMD_SHOW_TEXT: vkvg_show_text(ctx, txt); return; } } else { switch (r->cmd) { case VKVG_CMD_SAVE: vkvg_save(ctx); return; case VKVG_CMD_RESTORE: vkvg_restore(ctx); return; } } LOG(VKVG_LOG_ERR, "[REPLAY] unimplemented command: %.4x\n", r->cmd); }