#define VKVG_LOG_ERR 0x10
#define VKVG_LOG_DEBUG 0x20
#define VKVG_LOG_INFO 0x40
-#define VKVG_LOG_INFO_PATH 0x41
+#define VKVG_LOG_INFO_PTS 0x41
+#define VKVG_LOG_INFO_PATH 0x42
#define VKVG_LOG_DBG_ARRAYS 0x80
#define VKVG_LOG_FULL 0xff
VKVG_STATUS_INVALID_VISUAL, /*!< */
VKVG_STATUS_FILE_NOT_FOUND, /*!< */
VKVG_STATUS_INVALID_DASH, /*!< invalid value for a dash setting */
- VKVG_STATUS_INVALID_RECT, /*!< invalid value for a dash setting */
+ VKVG_STATUS_INVALID_RECT, /*!< rectangle with height or width equal to 0. */
}vkvg_status_t;
typedef enum {
#ifdef DEBUG
static vec2 debugLinePoints[1000];
static uint32_t dlpCount = 0;
+#if defined (VKVG_DBG_UTILS)
+const float DBG_LAB_COLOR_SAV[4] = {1,0,1,1};
+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] = {
- { {{0}} },
- { {{1.0f, 0}} },
- { {{0}} }
+ { .color.float32 = {0,0,0,0} },
+ { .depthStencil = {1.0f, 0} },
+ { .color.float32 = {0,0,0,0} }
};
VkvgContext vkvg_create(VkvgSurface surf)
ctx->indexCache = (VKVG_IBO_INDEX_TYPE*)malloc(ctx->sizeIndices * sizeof(VKVG_IBO_INDEX_TYPE));
ctx->savedStencils = malloc(0);
- ctx->selectedFont.fontFile = (char*)calloc(FONT_FILE_NAME_MAX_SIZE,sizeof(char));
+ ctx->selectedFontName = (char*)calloc(FONT_NAME_MAX_SIZE, sizeof(char));
+ ctx->selectedCharSize = 10 << 6;
ctx->currentFont = NULL;
- if (!ctx->points || !ctx->pathes || !ctx->vertexCache || !ctx->indexCache || !ctx->savedStencils || !ctx->selectedFont.fontFile) {
+ if (!ctx->points || !ctx->pathes || !ctx->vertexCache || !ctx->indexCache || !ctx->savedStencils || !ctx->selectedFontName) {
dev->status = VKVG_STATUS_NO_MEMORY;
if (ctx->points)
free(ctx->points);
free(ctx->indexCache);
if (ctx->savedStencils)
free(ctx->savedStencils);
- if (ctx->selectedFont.fontFile)
- free(ctx->selectedFont.fontFile);
+ if (ctx->selectedFontName)
+ free(ctx->selectedFontName);
return NULL;
}
vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_BUFFER, (uint64_t)ctx->vertices.buffer, "CTX Vertex Buff");
#endif
+ //force run of one renderpass (even empty) to perform clear load op
+ //_start_cmd_for_render_pass(ctx);
+
return ctx;
}
void vkvg_flush (VkvgContext ctx){
+ if (ctx->status)
+ return;
_flush_cmd_buff(ctx);
_wait_flush_fence(ctx);
/*
//TODO:check this for source counter
//vkh_image_destroy (ctx->source);
- free(ctx->selectedFont.fontFile);
+ free(ctx->selectedFontName);
free(ctx->pathes);
free(ctx->points);
if (ctx->dashCount > 0)
return ctx->references;
}
void vkvg_new_sub_path (VkvgContext ctx){
+ if (ctx->status)
+ return;
_finish_path(ctx);
}
void vkvg_new_path (VkvgContext ctx){
+ if (ctx->status)
+ return;
_clear_path(ctx);
}
void vkvg_close_path (VkvgContext ctx){
+ if (ctx->status)
+ return;
+ if (ctx->pathes[ctx->pathPtr] & PATH_CLOSED_BIT) //already closed
+ return;
//check if at least 3 points are present
if (ctx->pathes[ctx->pathPtr] < 3)
return;
//prevent closing on the same point
if (vec2_equ(ctx->points[ctx->pointCount-1],
- ctx->points[ctx->pointCount - ctx->pathes[ctx->pathPtr]]))
+ ctx->points[ctx->pointCount - ctx->pathes[ctx->pathPtr]])) {
+ if (ctx->pathes[ctx->pathPtr] < 4)//ensure enough points left for closing
+ return;
_remove_last_point(ctx);
+ }
ctx->pathes[ctx->pathPtr] |= PATH_CLOSED_BIT;
_finish_path(ctx);
}
void vkvg_rel_line_to (VkvgContext ctx, float dx, float dy){
+ if (ctx->status)
+ return;
if (_current_path_is_empty(ctx)){
ctx->status = VKVG_STATUS_NO_CURRENT_POINT;
return;
}
void vkvg_line_to (VkvgContext ctx, float x, float y)
{
+ if (ctx->status)
+ return;
vec2 p = {x,y};
if (!_current_path_is_empty (ctx)){
//prevent adding the same point
_add_point (ctx, x, y);
}
void vkvg_arc (VkvgContext ctx, float xc, float yc, float radius, float a1, float a2){
+ if (ctx->status)
+ return;
while (a2 < a1)//positive arc must have a1<a2
a2 += 2.f*M_PIF;
_set_curve_end(ctx);
}
void vkvg_arc_negative (VkvgContext ctx, float xc, float yc, float radius, float a1, float a2) {
+ if (ctx->status)
+ return;
while (a2 > a1)
a2 -= 2.f*M_PIF;
if (a1 - a2 > a1 + 2.f * M_PIF) //limit arc to 2PI
}
void vkvg_rel_move_to (VkvgContext ctx, float x, float y)
{
+ if (ctx->status)
+ return;
if (_current_path_is_empty(ctx)){
ctx->status = VKVG_STATUS_NO_CURRENT_POINT;
return;
}
void vkvg_move_to (VkvgContext ctx, float x, float y)
{
+ if (ctx->status)
+ return;
_finish_path(ctx);
_add_point (ctx, x, y);
}
void vkvg_curve_to (VkvgContext ctx, float x1, float y1, float x2, float y2, float x3, float y3) {
+ if (ctx->status)
+ return;
//prevent running _recursive_bezier when all 4 curve points are equal
if (EQUF(x1,x2) && EQUF(x2,x3) && EQUF(y1,y2) && EQUF(y2,y3)) {
if (_current_path_is_empty(ctx) || (EQUF(_get_current_position(ctx).x,x1) && EQUF(_get_current_position(ctx).y,y1)))
_set_curve_end (ctx);
}
void vkvg_rel_curve_to (VkvgContext ctx, float x1, float y1, float x2, float y2, float x3, float y3) {
+ if (ctx->status)
+ return;
if (_current_path_is_empty(ctx)){
ctx->status = VKVG_STATUS_NO_CURRENT_POINT;
return;
vkvg_curve_to (ctx, cp.x + x1, cp.y + y1, cp.x + x2, cp.y + y2, cp.x + x3, cp.y + y3);
}
void vkvg_fill_rectangle (VkvgContext ctx, float x, float y, float w, float h){
+ if (ctx->status)
+ return;
_vao_add_rectangle (ctx,x,y,w,h);
//_record_draw_cmd(ctx);
}
void vkvg_rectangle (VkvgContext ctx, float x, float y, float w, float h){
+ if (ctx->status)
+ return;
_finish_path (ctx);
if (w <= 0 || h <= 0) {
static const VkClearAttachment clearColorAttach = {VK_IMAGE_ASPECT_COLOR_BIT, 0, {{{0}}}};
void vkvg_reset_clip (VkvgContext ctx){
+ if (ctx->status)
+ return;
_emit_draw_cmd_undrawn_vertices(ctx);
if (!ctx->cmdStarted) {
//if command buffer is not already started and in a renderpass, we use the renderpass
vkCmdClearAttachments(ctx->cmd, 1, &clearStencil, 1, &ctx->clearRect);
}
void vkvg_clear (VkvgContext ctx){
+ if (ctx->status)
+ return;
_emit_draw_cmd_undrawn_vertices(ctx);
if (!ctx->cmdStarted) {
ctx->renderPassBeginInfo.renderPass = ctx->pSurf->dev->renderPass_ClearAll;
_clear_path(ctx);
}
void vkvg_clip_preserve (VkvgContext ctx){
- if (ctx->pathPtr == 0 && _current_path_is_empty(ctx))//nothing to clip
+ if (ctx->status)
return;
_emit_draw_cmd_undrawn_vertices(ctx);
_finish_path(ctx);
+ vkvg_close_path (ctx);
+
LOG(VKVG_LOG_INFO, "CLIP: ctx = %p; path cpt = %d;\n", ctx, ctx->pathPtr / 2);
_ensure_renderpass_is_started(ctx);
+#if defined(DEBUG) && defined (VKVG_DBG_UTILS)
+ vkh_cmd_label_start(ctx->cmd, "clip", DBG_LAB_COLOR_CLIP);
+#endif
+
if (ctx->curFillRule == VKVG_FILL_RULE_EVEN_ODD){
_poly_fill (ctx);
CmdBindPipeline (ctx->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pSurf->dev->pipelineClipping);
_bind_draw_pipeline (ctx);
CmdSetStencilCompareMask (ctx->cmd, VK_STENCIL_FRONT_AND_BACK, STENCIL_CLIP_BIT);
+
+#if defined(DEBUG) && defined (VKVG_DBG_UTILS)
+ vkh_cmd_label_end (ctx->cmd);
+#endif
}
void vkvg_fill_preserve (VkvgContext ctx){
- if (ctx->pathPtr == 0 && _current_path_is_empty(ctx))//nothing to fill
+ if (ctx->status)
return;
_finish_path(ctx);
+ vkvg_close_path (ctx);
+
LOG(VKVG_LOG_INFO, "FILL: ctx = %p; path cpt = %d;\n", ctx, ctx->pathPtr / 2);
if (ctx->curFillRule == VKVG_FILL_RULE_EVEN_ODD){
void vkvg_stroke_preserve (VkvgContext ctx)
{
+ if (ctx->status)
+ return;
+
if (ctx->pathPtr == 0 && _current_path_is_empty(ctx))//nothing to stroke
return;
_finish_path(ctx);
}
void vkvg_paint (VkvgContext ctx){
- if (ctx->pathPtr || ctx->segmentPtr){//path to fill
- vkvg_fill(ctx);
+ if (ctx->status)
return;
+
+ if (!_current_path_is_empty(ctx)){//maybe path to fill
+ vkvg_close_path(ctx);
+ if (ctx->pathes[ctx->pathPtr] & PATH_CLOSED_BIT) {
+ vkvg_fill(ctx);
+ return;
+ }
+ _clear_path (ctx);
}
_ensure_renderpass_is_started (ctx);
_draw_full_screen_quad (ctx, true);
}
void vkvg_set_source_rgba (VkvgContext ctx, float r, float g, float b, float a)
{
+ if (ctx->status)
+ return;
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;
_update_cur_pattern (ctx, vkvg_pattern_create_for_surface(surf));
ctx->pushConsts.source.x = x;
ctx->pushConsts.source.y = y;
ctx->pushCstDirty = true;
}
void vkvg_set_source (VkvgContext ctx, VkvgPattern pat){
+ if (ctx->status)
+ return;
_update_cur_pattern (ctx, pat);
vkvg_pattern_reference (pat);
}
ctx->lineJoin = join;
}
void vkvg_set_operator (VkvgContext ctx, vkvg_operator_t op){
+ if (ctx->status)
+ return;
if (op == ctx->curOperator)
return;
return ctx->lineWidth;
}
void vkvg_set_dash (VkvgContext ctx, const float* dashes, uint32_t num_dashes, float offset){
+ if (ctx->status)
+ return;
if (ctx->dashCount > 0)
free (ctx->dashes);
ctx->dashCount = num_dashes;
}
void vkvg_select_font_face (VkvgContext ctx, const char* name){
+ if (ctx->status)
+ return;
_select_font_face (ctx, name);
}
void vkvg_select_font_path (VkvgContext ctx, const char* path){
- _select_font_path (ctx, path);
+ if (ctx->status)
+ return;
+ //_select_font_path (ctx, path);
}
void vkvg_set_font_size (VkvgContext ctx, uint32_t size){
- _set_font_size (ctx,size);
+ if (ctx->status)
+ return;
+ ctx->selectedCharSize = size << 6;
+ ctx->currentFont = NULL;
}
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;
//_ensure_renderpass_is_started(ctx);
_show_text (ctx, text);
//_flush_undrawn_vertices (ctx);
}
VkvgText vkvg_text_run_create (VkvgContext ctx, const char* text) {
+ if (ctx->status)
+ return NULL;
VkvgText tr = (vkvg_text_run_t*)calloc(1, sizeof(vkvg_text_run_t));
_create_text_run(ctx, text, tr);
return tr;
free (textRun);
}
void vkvg_show_text_run (VkvgContext ctx, VkvgText textRun) {
+ if (ctx->status)
+ return;
_show_text_run(ctx, textRun);
}
void vkvg_text_run_get_extents (VkvgText textRun, vkvg_text_extents_t* extents) {
}
void vkvg_text_extents (VkvgContext ctx, const char* text, vkvg_text_extents_t* extents) {
+ if (ctx->status)
+ return;
_text_extents(ctx, text, extents);
}
void vkvg_font_extents (VkvgContext ctx, vkvg_font_extents_t* extents) {
+ if (ctx->status)
+ return;
_font_extents(ctx, extents);
}
vkh_cmd_begin (ctx->cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
ctx->cmdStarted = true;
+#if defined(DEBUG) && defined (VKVG_DBG_UTILS)
+ vkh_cmd_label_start(ctx->cmd, "new save/restore stencil", DBG_LAB_COLOR_SAV);
+#endif
+
vkh_image_set_layout (ctx->cmd, ctx->pSurf->stencil, VK_IMAGE_ASPECT_STENCIL_BIT,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT);
+#if defined(DEBUG) && defined (VKVG_DBG_UTILS)
+ vkh_cmd_label_end (ctx->cmd);
+#endif
+
VK_CHECK_RESULT(vkEndCommandBuffer(ctx->cmd));
_wait_and_submit_cmd(ctx);
}
_start_cmd_for_render_pass (ctx);
+#if defined(DEBUG) && defined (VKVG_DBG_UTILS)
+ vkh_cmd_label_start(ctx->cmd, "save rp", DBG_LAB_COLOR_SAV);
+#endif
+
CmdBindPipeline (ctx->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pSurf->dev->pipelineClipping);
CmdSetStencilReference (ctx->cmd, VK_STENCIL_FRONT_AND_BACK, STENCIL_CLIP_BIT|curSaveBit);
_bind_draw_pipeline (ctx);
CmdSetStencilCompareMask(ctx->cmd, VK_STENCIL_FRONT_AND_BACK, STENCIL_CLIP_BIT);
+#if defined(DEBUG) && defined (VKVG_DBG_UTILS)
+ vkh_cmd_label_end (ctx->cmd);
+#endif
+
sav->dashOffset = ctx->dashOffset;
sav->dashCount = ctx->dashCount;
if (ctx->dashCount > 0) {
sav->lineWidth = ctx->lineWidth;
sav->curFillRule= ctx->curFillRule;
- sav->selectedFont = ctx->selectedFont;
- sav->selectedFont.fontFile = (char*)calloc(FONT_FILE_NAME_MAX_SIZE,sizeof(char));
- strcpy (sav->selectedFont.fontFile, ctx->selectedFont.fontFile);
+ sav->selectedCharSize = ctx->selectedCharSize;
+ sav->selectedFontName = (char*)calloc(FONT_NAME_MAX_SIZE,sizeof(char));
+ strcpy (sav->selectedFontName, ctx->selectedFontName);
sav->currentFont = ctx->currentFont;
sav->textDirection= ctx->textDirection;
sav->pushConsts = ctx->pushConsts;
- sav->pattern = ctx->pattern;
+ //sav->pattern = ctx->pattern;//TODO:pattern sav must be imutable (copy?)
sav->pNext = ctx->pSavedCtxs;
ctx->pSavedCtxs = sav;
ctx->curSavBit++;
- if (ctx->pattern)
- vkvg_pattern_reference (ctx->pattern);
+ /*if (ctx->pattern)
+ vkvg_pattern_reference (ctx->pattern);*/
}
void vkvg_restore (VkvgContext ctx){
+ if (ctx->status)
+ return;
+
if (ctx->pSavedCtxs == NULL){
ctx->status = VKVG_STATUS_INVALID_RESTORE;
return;
LOG(VKVG_LOG_INFO, "RESTORE CONTEXT: ctx = %p\n", ctx);
+ _flush_cmd_buff (ctx);
+ _wait_flush_fence (ctx);
+
vkvg_context_save_t* sav = ctx->pSavedCtxs;
ctx->pSavedCtxs = sav->pNext;
ctx->pushConsts = sav->pushConsts;
- if (sav->pattern)
- _update_cur_pattern (ctx, sav->pattern);
-
- _flush_cmd_buff (ctx);
- _wait_flush_fence (ctx);
+ /*if (sav->pattern)
+ _update_cur_pattern (ctx, sav->pattern);*/
ctx->curSavBit--;
_start_cmd_for_render_pass (ctx);
+#if defined(DEBUG) && defined (VKVG_DBG_UTILS)
+ vkh_cmd_label_start(ctx->cmd, "restore rp", DBG_LAB_COLOR_SAV);
+#endif
+
CmdBindPipeline (ctx->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pSurf->dev->pipelineClipping);
CmdSetStencilReference (ctx->cmd, VK_STENCIL_FRONT_AND_BACK, STENCIL_CLIP_BIT|curSaveBit);
_bind_draw_pipeline (ctx);
CmdSetStencilCompareMask (ctx->cmd, VK_STENCIL_FRONT_AND_BACK, STENCIL_CLIP_BIT);
+#if defined(DEBUG) && defined (VKVG_DBG_UTILS)
+ vkh_cmd_label_end (ctx->cmd);
+#endif
+
_flush_cmd_buff (ctx);
_wait_flush_fence (ctx);
vkh_cmd_begin (ctx->cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
ctx->cmdStarted = true;
+#if defined(DEBUG) && defined (VKVG_DBG_UTILS)
+ vkh_cmd_label_start(ctx->cmd, "additional stencil copy while restoring", DBG_LAB_COLOR_SAV);
+#endif
+
vkh_image_set_layout (ctx->cmd, ctx->pSurf->stencil, VK_IMAGE_ASPECT_STENCIL_BIT,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT);
+#if defined(DEBUG) && defined (VKVG_DBG_UTILS)
+ vkh_cmd_label_end (ctx->cmd);
+#endif
+
VK_CHECK_RESULT(vkEndCommandBuffer(ctx->cmd));
_wait_and_submit_cmd (ctx);
_wait_flush_fence (ctx);
ctx->lineJoin = sav->lineJoint;
ctx->curFillRule= sav->curFillRule;
- ctx->selectedFont.charSize = sav->selectedFont.charSize;
- strcpy (ctx->selectedFont.fontFile, sav->selectedFont.fontFile);
+ ctx->selectedCharSize = sav->selectedCharSize;
+ strcpy (ctx->selectedFontName, sav->selectedFontName);
ctx->currentFont = sav->currentFont;
ctx->textDirection= sav->textDirection;
}
void vkvg_translate (VkvgContext ctx, float dx, float dy){
+ if (ctx->status)
+ return;
_emit_draw_cmd_undrawn_vertices(ctx);
vkvg_matrix_translate (&ctx->pushConsts.mat, dx, dy);
_set_mat_inv_and_vkCmdPush (ctx);
}
void vkvg_scale (VkvgContext ctx, float sx, float sy){
+ if (ctx->status)
+ return;
_emit_draw_cmd_undrawn_vertices(ctx);
vkvg_matrix_scale (&ctx->pushConsts.mat, sx, sy);
_set_mat_inv_and_vkCmdPush (ctx);
}
void vkvg_rotate (VkvgContext ctx, float radians){
+ if (ctx->status)
+ return;
_emit_draw_cmd_undrawn_vertices(ctx);
vkvg_matrix_rotate (&ctx->pushConsts.mat, radians);
_set_mat_inv_and_vkCmdPush (ctx);
}
void vkvg_transform (VkvgContext ctx, const vkvg_matrix_t* matrix) {
+ if (ctx->status)
+ return;
_emit_draw_cmd_undrawn_vertices(ctx);
vkvg_matrix_t res;
vkvg_matrix_multiply (&res, &ctx->pushConsts.mat, matrix);
_set_mat_inv_and_vkCmdPush (ctx);
}
void vkvg_identity_matrix (VkvgContext ctx) {
+ if (ctx->status)
+ return;
_emit_draw_cmd_undrawn_vertices(ctx);
vkvg_matrix_t im = VKVG_IDENTITY_MATRIX;
ctx->pushConsts.mat = im;
_set_mat_inv_and_vkCmdPush (ctx);
}
void vkvg_set_matrix (VkvgContext ctx, const vkvg_matrix_t* matrix){
+ if (ctx->status)
+ return;
_emit_draw_cmd_undrawn_vertices(ctx);
ctx->pushConsts.mat = (*matrix);
_set_mat_inv_and_vkCmdPush (ctx);
return ctx->pathes[ptrPath] & PATH_CLOSED_BIT;
}
void _resetMinMax (VkvgContext ctx) {
+ LOG(VKVG_LOG_INFO_PTS, "_resetMinMax (scissor)\n");
ctx->xMin = ctx->yMin = FLT_MAX;
ctx->xMax = ctx->yMax = FLT_MIN;
}
//that speed up fill drastically.
vkvg_matrix_transform_point (&ctx->pushConsts.mat, &x, &y);
+ LOG(VKVG_LOG_INFO_PTS, "_add_point transformed: (%f, %f)\n", x, y);
+
if (x < ctx->xMin)
ctx->xMin = x;
if (x > ctx->xMax)
filter = VK_FILTER_NEAREST;
break;
}
- vkh_image_create_sampler(ctx->source, filter, filter,
- VK_SAMPLER_MIPMAP_MODE_NEAREST, addrMode);
+ vkh_image_create_sampler (ctx->source, filter, filter,
+ VK_SAMPLER_MIPMAP_MODE_NEAREST, addrMode);
- _update_descriptor_set (ctx, ctx->source, ctx->dsSrc);
+ _update_descriptor_set (ctx, ctx->source, ctx->dsSrc);
vec4 srcRect = {{0},{0},{(float)surf->width},{(float)surf->height}};
ctx->pushConsts.source = srcRect;
//transform control point with current ctx matrix
vkvg_gradient_t grad = {0};
- memcpy(&grad, pat->data, sizeof(vkvg_gradient_t));
+ memcpy (&grad, pat->data, sizeof(vkvg_gradient_t));
- vkvg_matrix_transform_point(&ctx->pushConsts.mat, &grad.cp[0].x, &grad.cp[0].y);
- vkvg_matrix_transform_point(&ctx->pushConsts.mat, &grad.cp[1].x, &grad.cp[1].y);
+ vkvg_matrix_transform_point (&ctx->pushConsts.mat, &grad.cp[0].x, &grad.cp[0].y);
+ vkvg_matrix_transform_point (&ctx->pushConsts.mat, &grad.cp[1].x, &grad.cp[1].y);
//to do, scale radial radiuses in cp[2]
- memcpy(ctx->uboGrad.allocInfo.pMappedData , &grad, sizeof(vkvg_gradient_t));
+ memcpy (ctx->uboGrad.allocInfo.pMappedData , &grad, sizeof(vkvg_gradient_t));
break;
}
ctx->pushConsts.patternType = newPatternType;
ctx->pushCstDirty = true;
if (lastPat)
- vkvg_pattern_destroy (lastPat);
+ vkvg_pattern_destroy (lastPat);
}
void _update_descriptor_set (VkvgContext ctx, VkhImage img, VkDescriptorSet ds){
_wait_flush_fence(ctx);//descriptorSet update invalidate cmd buffs
void _free_ctx_save (vkvg_context_save_t* sav){
if (sav->dashCount > 0)
free (sav->dashes);
- free(sav->selectedFont.fontFile);
+ free(sav->selectedFontName);
free (sav);
}
vkvg_line_join_t lineJoint;
vkvg_fill_rule_t curFillRule;
+ FT_F26Dot6 selectedCharSize; /* Font size*/
+ char* selectedFontName;
_vkvg_font_t selectedFont; //hold current face and size before cache addition
_vkvg_font_t* currentFont; //font ready for lookup
vkvg_direction_t textDirection;
vkvg_line_join_t lineJoin;
vkvg_fill_rule_t curFillRule;
- _vkvg_font_t selectedFont; //hold current face and size before cache addition
+ FT_F26Dot6 selectedCharSize; /* Font size*/
+ char* selectedFontName;
+ //_vkvg_font_t selectedFont; //hold current face and size before cache addition
_vkvg_font_t* currentFont; //font pointing to cached fonts ready for lookup
vkvg_direction_t textDirection;
free (cache->hostBuff);
for (int i = 0; i < cache->fontsCount; ++i) {
- _vkvg_font_t f = cache->fonts[i];
+ _vkvg_font_t* f = &cache->fonts[i];
- for (int g = 0; g < f.face->num_glyphs; ++g) {
- if (f.charLookup[g]!=NULL)
- free(f.charLookup[g]);
+ for (int g = 0; g < f->face->num_glyphs; ++g) {
+ if (f->charLookup[g]!=NULL)
+ free(f->charLookup[g]);
}
- FT_Done_Face (f.face);
- hb_font_destroy (f.hb_font);
+ FT_Done_Face (f->face);
+ hb_font_destroy (f->hb_font);
- free(f.charLookup);
- free(f.fontFile);
+ free(f->charLookup);
+ free(f->fontFile);
+ for (uint32_t j = 0; j < f->fcNamesCount; j++)
+ free (f->fcNames[j]);
+ if (f->fcNamesCount > 0)
+ free (f->fcNames);
}
free(cache->fonts);
dev->fontCache->stagingX += bmpWidth;
return cr;
}
-//set current font size for context
-void _set_font_size (VkvgContext ctx, uint32_t size){
- ctx->selectedFont.charSize = size << 6;
- ctx->currentFont = NULL;
-}
-
void _select_font_path (VkvgContext ctx, const char* fontFile){
- memset (ctx->selectedFont.fontFile, 0, FONT_FILE_NAME_MAX_SIZE);
- memcpy (ctx->selectedFont.fontFile, fontFile, FONT_FILE_NAME_MAX_SIZE);
ctx->currentFont = NULL;
}
//select current font for context
void _select_font_face (VkvgContext ctx, const char* name){
- _font_cache_t* cache = (_font_cache_t*)ctx->pSurf->dev->fontCache;
-
- char* fontFile;
+ strcpy (ctx->selectedFontName, name);
+}
+void _font_add_fc_name (_vkvg_font_t* font, const char* fcname) {
+ if (++font->fcNamesCount == 1)
+ font->fcNames = (char**) malloc (sizeof(char*));
+ else
+ font->fcNames = (char**) realloc (font->fcNames, font->fcNamesCount * sizeof(char*));
+ font->fcNames[font->fcNamesCount-1] = (char*)calloc(FONT_NAME_MAX_SIZE, sizeof (char));
+ strcpy (font->fcNames[font->fcNamesCount-1], fcname);
+}
- //make pattern from font name
- FcPattern* pat = FcNameParse((const FcChar8*)name);
+//try to find font in cache with same font file path and font size as selected in context.
+_vkvg_font_t* _tryFindVkvgFont (VkvgContext ctx){
+ _font_cache_t* cache = (_font_cache_t*)ctx->pSurf->dev->fontCache;
+ //try find font already resolved with fontconfig by font name
+ for (int i = 0; i < cache->fontsCount; ++i) {
+ for (uint32_t j = 0; j < cache->fonts[i].fcNamesCount; j++) {
+ if (strcmp (cache->fonts[i].fcNames[j], ctx->selectedFontName) == 0 && cache->fonts[i].charSize == ctx->selectedCharSize)
+ return &cache->fonts[i];
+ }
+ }
+ //try resolve name with fontconfig
+ FcPattern* pat = FcNameParse((const FcChar8*)ctx->selectedFontName);
FcConfigSubstitute(cache->config, pat, FcMatchPattern);
FcDefaultSubstitute(pat);
// find the font
FcPattern* font = FcFontMatch(cache->config, pat, &result);
if (font)
{
- if (FcPatternGetString(font, FC_FILE, 0, (FcChar8 **)&fontFile) == FcResultMatch)
- _select_font_path (ctx, fontFile);
+ char* fontFile;
+ if (FcPatternGetString(font, FC_FILE, 0, (FcChar8 **)&fontFile) == FcResultMatch) {
+ //try find font in cache by path
+ for (int i = 0; i < cache->fontsCount; ++i) {
+ if (strcmp (cache->fonts[i].fontFile, fontFile) == 0 && cache->fonts[i].charSize == ctx->selectedCharSize) {
+ _font_add_fc_name (&cache->fonts[i], ctx->selectedFontName);
+ FcPatternDestroy(pat);
+ FcPatternDestroy(font);
+ return &cache->fonts[i];
+ }
+ }
+ //if not found, create a new vkvg_font
+ cache->fontsCount++;
+
+ if (cache->fontsCount == 1)
+ cache->fonts = (_vkvg_font_t*) malloc (cache->fontsCount * sizeof(_vkvg_font_t));
+ else
+ cache->fonts = (_vkvg_font_t*) realloc (cache->fonts, cache->fontsCount * sizeof(_vkvg_font_t));
+
+ _vkvg_font_t* nf = &cache->fonts[cache->fontsCount-1];
+ memset (nf, 0, sizeof(_vkvg_font_t));
+
+ nf->charSize = ctx->selectedCharSize;
+ nf->fontFile = (char*)malloc (FONT_FILE_NAME_MAX_SIZE * sizeof(char));
+ memcpy (nf->fontFile, fontFile, FONT_FILE_NAME_MAX_SIZE);
+ _font_add_fc_name (nf, ctx->selectedFontName);
+ FcPatternDestroy(pat);
+ FcPatternDestroy(font);
+ return nf;
+ }
}
FcPatternDestroy(pat);
FcPatternDestroy(font);
-}
-//try to find font in cache with same font file path and font size as selected in context.
-_vkvg_font_t* _tryFindVkvgFont (VkvgContext ctx){
- _font_cache_t* cache = (_font_cache_t*)ctx->pSurf->dev->fontCache;
- for (int i = 0; i < cache->fontsCount; ++i) {
- if (strcmp (cache->fonts[i].fontFile, ctx->selectedFont.fontFile)==0 && cache->fonts[i].charSize == ctx->selectedFont.charSize)
- return &cache->fonts[i];
- }
+
return NULL;
}
//try to find corresponding font in cache (defined by context selectedFont) and create a new font entry if not found.
void _update_current_font (VkvgContext ctx) {
VkvgDevice dev = ctx->pSurf->dev;
+
if (ctx->currentFont == NULL){
- if (ctx->selectedFont.fontFile[0] == 0) {
- ctx->selectedFont.charSize = 10 << 6;
+ if (ctx->selectedFontName[0] == 0)
_select_font_face (ctx, "sans");
- }
+
ctx->currentFont = _tryFindVkvgFont (ctx);
- if (ctx->currentFont == NULL){
+
+ if (ctx->currentFont->face == NULL){
//create new font in cache
_font_cache_t* cache = dev->fontCache;
- cache->fontsCount++;
- if (cache->fontsCount == 1)
- cache->fonts = (_vkvg_font_t*) malloc (cache->fontsCount * sizeof(_vkvg_font_t));
- else
- cache->fonts = (_vkvg_font_t*) realloc (cache->fonts, cache->fontsCount * sizeof(_vkvg_font_t));
-
- _vkvg_font_t nf = ctx->selectedFont;
- if (nf.charSize == 0)
- nf.charSize = defaultFontCharSize;
- nf.fontFile = (char*)calloc(FONT_FILE_NAME_MAX_SIZE,sizeof(char));
- strcpy (nf.fontFile, ctx->selectedFont.fontFile);
- FT_CHECK_RESULT(FT_New_Face(cache->library, nf.fontFile, 0, &nf.face));
- FT_CHECK_RESULT(FT_Set_Char_Size(nf.face, 0, nf.charSize, dev->hdpi, dev->vdpi ));
- nf.hb_font = hb_ft_font_create(nf.face, NULL);
- nf.charLookup = (_char_ref**)calloc(nf.face->num_glyphs,sizeof(_char_ref*));
+ FT_CHECK_RESULT(FT_New_Face(cache->library, ctx->currentFont->fontFile, 0, &ctx->currentFont->face));
+ FT_CHECK_RESULT(FT_Set_Char_Size(ctx->currentFont->face, 0, ctx->currentFont->charSize, dev->hdpi, dev->vdpi ));
+ ctx->currentFont->hb_font = hb_ft_font_create(ctx->currentFont->face, NULL);
+ ctx->currentFont->charLookup = (_char_ref**)calloc(ctx->currentFont->face->num_glyphs,sizeof(_char_ref*));
//nf.curLine.height = (nf.face->bbox.xMax - nf.face->bbox.xMin) >> 6;
- if (FT_IS_SCALABLE(nf.face))
- nf.curLine.height = FT_MulFix(nf.face->height, nf.face->size->metrics.y_scale) >> 6;// nf.face->size->metrics.height >> 6;
+ if (FT_IS_SCALABLE(ctx->currentFont->face))
+ ctx->currentFont->curLine.height = FT_MulFix(ctx->currentFont->face->height, ctx->currentFont->face->size->metrics.y_scale) >> 6;// nf.face->size->metrics.height >> 6;
else
- nf.curLine.height = nf.face->height >> 6;
+ ctx->currentFont->curLine.height = ctx->currentFont->face->height >> 6;
- _init_next_line_in_tex_cache (dev, &nf);
- cache->fonts[cache->fontsCount-1] = nf;
- ctx->currentFont = &cache->fonts[cache->fontsCount-1];
+ _init_next_line_in_tex_cache (dev, ctx->currentFont);
}
}
}
void _font_extents (VkvgContext ctx, vkvg_font_extents_t *extents) {
_update_current_font (ctx);
+ if (ctx->status)
+ return;
+
//TODO: ensure correct metrics are returned (scalled/unscalled, etc..)
FT_BBox* bbox = &ctx->currentFont->face->bbox;
FT_Size_Metrics* metrics = &ctx->currentFont->face->size->metrics;
void _text_extents (VkvgContext ctx, const char* text, vkvg_text_extents_t *extents) {
_update_current_font (ctx);
+ if (ctx->status)
+ return;
+
vkvg_text_run_t tr = {0};
_create_text_run (ctx, text, &tr);
_update_current_font (ctx);
+ if (ctx->status)
+ return;
+
textRun->hbBuf = _get_hb_buffer (ctx->currentFont, text);
textRun->font = ctx->currentFont;
textRun->dev = ctx->pSurf->dev;
vkvg_text_run_t tr = {0};
_create_text_run (ctx, text, &tr);
+ if (ctx->status)
+ return;
+
_show_text_run (ctx, &tr);
_destroy_text_run (&tr);
#define FONT_PAGE_SIZE 1024
#define FONT_CACHE_INIT_LAYERS 1
#define FONT_FILE_NAME_MAX_SIZE 1024
+#define FONT_NAME_MAX_SIZE 128
#include "vkvg_internal.h"
#include "vkvg.h"
}_tex_ref_t;
// Loaded font structure, holds informations for glyphes upload in cache and the lookup table of characters.
typedef struct {
+ char** fcNames; /* Resolved Input names to this font by fontConfig */
+ uint32_t fcNamesCount; /* Count of resolved names by fontConfig */
char* fontFile; /* Font file full path*/
FT_F26Dot6 charSize; /* Font size*/
hb_font_t* hb_font; /* HarfBuzz font instance*/
VkFence uploadFence; /* Signaled when upload is finished */
_vkvg_font_t* fonts; /* Loaded fonts structure array */
- uint8_t fontsCount; /* Loaded fonts array count*/
+ int32_t fontsCount; /* Loaded fonts array count*/
}_font_cache_t;
// Precompute everything necessary to draw one line of text, usefull to draw the same text multiple times.
typedef struct _vkvg_text_run_t {
//Select current font for context from font name, create new font entry in cache if required
void _select_font_face (VkvgContext ctx, const char* name);
void _select_font_path (VkvgContext ctx, const char* fontFile);
-//Set current font size for context
-void _set_font_size (VkvgContext ctx, uint32_t size);
//Draw text
void _show_text (VkvgContext ctx, const char* text);
//Get text dimmensions
surf->height = height;
_create_surface_images (surf);
- _clear_surface(surf, VK_IMAGE_ASPECT_COLOR_BIT);
uint32_t imgSize = width * height * 4;
VkImageSubresourceLayers imgSubResLayers = {VK_IMAGE_ASPECT_COLOR_BIT,0,0,1};
vkvg_buff buff = {0};
vkvg_buffer_create(dev, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU, imgSize, &buff);
- memcpy (buff.allocInfo.pMappedData, img, imgSize);
+ memcpy (buff.allocInfo.pMappedData, img, imgSize);
VkCommandBuffer cmd = dev->cmd;
vkvg_buffer_destroy (&buff);
vkh_image_destroy (stagImg);
+ surf->new = false;
+
//create tmp context with rendering pipeline to create the multisample img
VkvgContext ctx = vkvg_create (surf);
#include "test.h"
+
+const char* imgPath = "data/miroir.jpg";
void paint () {
VkvgContext ctx = vkvg_create(surf);
- VkvgSurface imgSurf = vkvg_surface_create_from_image(device, "data/miroir.jpg");
- //vkvg_surface_write_to_png(imgSurf, "/tmp/test.png");
+ VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
vkvg_set_source_surface(ctx, imgSurf, 0, 0);
vkvg_paint(ctx);
}
void paint_offset () {
VkvgContext ctx = vkvg_create(surf);
- VkvgSurface imgSurf = vkvg_surface_create_from_image(device, "data/miroir.jpg");
+ VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
vkvg_set_source_surface(ctx, imgSurf, 100, 100);
vkvg_paint(ctx);
void paint_with_scale(){
VkvgContext ctx = vkvg_create(surf);
vkvg_scale (ctx, 0.2f,0.2f);
- VkvgSurface imgSurf = vkvg_surface_create_from_image(device, "data/miroir.jpg");
+ VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
vkvg_set_source_surface(ctx, imgSurf, 0, 0);
vkvg_paint(ctx);
void paint_pattern () {
VkvgContext ctx = vkvg_create(surf);
- VkvgSurface imgSurf = vkvg_surface_create_from_image(device, "data/miroir.jpg");
+ VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
VkvgPattern pat = vkvg_pattern_create_for_surface(imgSurf);
vkvg_set_source(ctx, pat);
vkvg_paint(ctx);
}
void paint_patt_repeat () {
VkvgContext ctx = vkvg_create(surf);
- VkvgSurface imgSurf = vkvg_surface_create_from_image(device, "data/miroir.jpg");
+ VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
VkvgPattern pat = vkvg_pattern_create_for_surface(imgSurf);
vkvg_pattern_set_extend(pat,VKVG_EXTEND_REPEAT);
vkvg_set_source(ctx, pat);
void paint_patt_repeat_scalled () {
VkvgContext ctx = vkvg_create(surf);
vkvg_scale (ctx, 0.2f,0.2f);
- VkvgSurface imgSurf = vkvg_surface_create_from_image(device, "data/miroir.jpg");
+ VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
VkvgPattern pat = vkvg_pattern_create_for_surface(imgSurf);
vkvg_pattern_set_extend(pat,VKVG_EXTEND_REPEAT);
vkvg_set_source(ctx, pat);
}
void paint_patt_pad () {
VkvgContext ctx = vkvg_create(surf);
- VkvgSurface imgSurf = vkvg_surface_create_from_image(device, "data/miroir.jpg");
+ VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
VkvgPattern pat = vkvg_pattern_create_for_surface(imgSurf);
vkvg_pattern_set_extend(pat,VKVG_EXTEND_PAD);
vkvg_set_source(ctx, pat);
void test(){
VkvgContext ctx = vkvg_create(surf);
vkvg_set_fill_rule(ctx,VKVG_FILL_RULE_EVEN_ODD);
- VkvgSurface imgSurf = vkvg_surface_create_from_image(device, "data/miroir.jpg");
+ VkvgSurface imgSurf = vkvg_surface_create_from_image(device, imgPath);
vkvg_translate(ctx,200,200);
//vkvg_rotate(ctx,M_PI_4);
#include "test.h"
vkvg_fill_rule_t fillrule = VKVG_FILL_RULE_NON_ZERO;
-float lineWidth = 20.f;
+float lineWidth = 10.f;
VkvgSurface createSurf (uint32_t width, uint32_t height) {
VkvgSurface s = vkvg_surface_create(device, width, height);
vkvg_set_fill_rule(ctx,fillrule);
vkvg_set_line_width(ctx,lineWidth);
float hlw = lineWidth/2.f;
- vkvg_rectangle(ctx,hlw,hlw,(float)width-hlw,(float)height-hlw);
+ /*
vkvg_set_source_rgba(ctx,0,1,0,0.5);
- vkvg_fill_preserve(ctx);
+ vkvg_fill_preserve(ctx);*/
+ vkvg_set_source_rgba(ctx,1,0,0,0.5);
+ vkvg_paint(ctx);
vkvg_set_source_rgba(ctx,0,0,1,0.5);
+ vkvg_rectangle(ctx,hlw,hlw,(float)width-lineWidth,(float)height-lineWidth);
vkvg_stroke(ctx);
vkvg_destroy(ctx);
return s;
void paint_with_offset(){
VkvgSurface src = createSurf(256,256);
VkvgContext ctx = vkvg_create(surf);
+ vkvg_set_source_rgba(ctx,0,1,0,0.5);
+ vkvg_paint(ctx);
vkvg_set_fill_rule(ctx,fillrule);
vkvg_set_source_surface(ctx, src, 100, 100);
vkvg_paint(ctx);
void print(VkvgContext ctx, float penY, uint32_t size) {
vkvg_set_font_size(ctx,size);
- vkvg_move_to(ctx, 10,penY);
+ vkvg_move_to(ctx, 10, penY);
vkvg_show_text (ctx,txt);
}
float penY = 10.f;
- for (uint32_t size=4;size<39;size++) {
+ for (uint32_t size = 4; size < 39; size++) {
print(ctx,(float)penY,size);
penY+=size;
}
//vkvg_select_font_face(ctx, "/usr/local/share/fonts/DroidSansMono.ttf");
//vkvg_select_font_face(ctx, "/usr/share/fonts/truetype/unifont/unifont.ttf");
- vkvg_set_font_size(ctx,12);
- vkvg_select_font_face(ctx, "droid");
+ vkvg_set_font_size (ctx, 12);
+ vkvg_select_font_face (ctx, "droid");
vkvg_font_extents_t fe;
- vkvg_font_extents (ctx,&fe);
- vkvg_move_to(ctx, penX,penY);
- vkvg_set_source_rgba(ctx,fg.r,fg.g,fg.b,fg.a);
+ vkvg_font_extents (ctx, &fe);
+ vkvg_move_to (ctx, penX,penY);
+ vkvg_set_source_rgba (ctx, fg.r, fg.g, fg.b, fg.a);
vkvg_text_extents_t te;
- vkvg_text_extents(ctx,"abcdefghijk",&te);
- vkvg_show_text (ctx,"abcdefghijk");
- penX+= te.x_advance;
+ vkvg_text_extents (ctx, "abcdefghijk", &te);
+ vkvg_show_text (ctx, "abcdefghijk");
+ penX += te.x_advance;
vkvg_move_to(ctx, penX,penY);
vkvg_show_text (ctx,"*abcdefghijk2");
- penY+=2.f*size;
+ penY += 2.f*size;
vkvg_select_font_face(ctx, "times");
vkvg_move_to(ctx, penX,penY);
vkvg_destroy(ctx);
}
-void random_text () {
+void single_font_and_size () {
+ VkvgContext ctx = vkvg_create(surf);
+ vkvg_clear(ctx);
+ for (uint32_t i=0; i<test_size; i++) {
+ randomize_color(ctx);
+ float x = rndf() * test_width;
+ float y = rndf() * test_height;
+ vkvg_select_font_face(ctx,"mono");
+ vkvg_set_font_size(ctx, 20);
+ vkvg_move_to(ctx,x,y);
+ vkvg_show_text(ctx,"This is a test string!");
+ }
+ vkvg_destroy(ctx);
+}
+
+void random_size () {
VkvgContext ctx = vkvg_create(surf);
vkvg_clear(ctx);
vkvg_select_font_face(ctx,"mono");
for (uint32_t i=0; i<test_size; i++) {
randomize_color(ctx);
- float x = (float)rand()/RAND_MAX * test_width;
- float y = (float)rand()/RAND_MAX * test_height;
- uint32_t c = (uint32_t)((float)rand()/RAND_MAX * 80)+1;
+ float x = rndf() * test_width;
+ float y = rndf() * test_height;
+ uint32_t c = (uint32_t)(rndf() * 120)+1;
vkvg_set_font_size(ctx, c);
vkvg_move_to(ctx,x,y);
}
vkvg_destroy(ctx);
}
+const char* const fonts[] =
+ { "mono", "droid", "times", "arial", "times:bold"};
+
+void random_font_and_size () {
+ VkvgContext ctx = vkvg_create(surf);
+ vkvg_clear(ctx);
+
+ for (uint32_t i=0; i<test_size; i++) {
+ randomize_color(ctx);
+ float x = rndf() * test_width;
+ float y = rndf() * test_height;
+ uint32_t c = (uint32_t)(rndf() * 80)+1;
+ uint32_t f = (uint32_t)(rndf() * 4);
+
+ vkvg_set_font_size(ctx, c);
+ vkvg_select_font_face(ctx, fonts[f]);
+ vkvg_move_to(ctx,x,y);
+ vkvg_show_text(ctx,"This is a test string!");
+ }
+ vkvg_destroy(ctx);
+}
+
int main(int argc, char *argv[]) {
no_test_size = true;
//vkvg_log_level = VKVG_LOG_INFO;
- PERFORM_TEST (random_text, argc, argv);
+ PERFORM_TEST (single_font_and_size, argc, argv);
+ PERFORM_TEST (random_size, argc, argv);
+ PERFORM_TEST (random_font_and_size, argc, argv);
PERFORM_TEST (test, argc, argv);
PERFORM_TEST (test1, argc, argv);
PERFORM_TEST (test2, argc, argv);