pebble/tests/fw/graphics/test_gdraw_command.c
2025-01-27 11:38:16 -08:00

815 lines
28 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "clar.h"
#include "applib/graphics/gdraw_command.h"
#include "applib/graphics/gdraw_command_list.h"
#include "applib/graphics/gdraw_command_image.h"
#include "applib/graphics/gdraw_command_private.h"
#include "applib/graphics/gtypes.h"
#include "applib/graphics/graphics.h"
#include "applib/graphics/graphics_line.h"
#include "applib/graphics/gpath.h"
#include <string.h>
#include "stubs_applib_resource.h"
#include "stubs_app_state.h"
#include "stubs_compiled_with_legacy2_sdk.h"
#include "stubs_heap.h"
#include "stubs_memory_layout.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_resources.h"
#include "stubs_syscalls.h"
extern size_t prv_get_list_max_command_size(GDrawCommandList *command_list);
static GColor s_fill_color;
static GColor s_stroke_color;
static uint8_t s_stroke_width;
static uint16_t s_path_num_points;
static GPoint *s_stroke_points = NULL;
static GPoint *s_fill_points = NULL;
static bool s_path_open;
static uint16_t s_radius;
struct PreciseLine {
GPointPrecise p0;
GPointPrecise p1;
} *s_precise_lines = NULL;
static int s_num_precise_lines;
static int s_path_stroke_count;
static int s_path_stroke_precise_count;
static int s_path_fill_count;
static int s_path_fill_precise_count;
static int s_circle_stroke_count;
static int s_circle_fill_count;
static GPoint s_offset;
static GPoint *prv_copy_points(GPoint *points, uint16_t num_points, GPoint offset) {
s_path_num_points = num_points;
GPoint *copied_points = malloc(num_points * sizeof(GPoint));
cl_assert(copied_points != NULL);
for (int i = 0; i < num_points; i++) {
copied_points[i] = gpoint_add(points[i], offset);
}
return copied_points;
}
static bool prv_compare_points(GPoint *a, GPoint *b, uint16_t num_points) {
return (memcmp(a, b, sizeof(GPoint) * num_points) == 0);
}
// Stubs
void graphics_context_set_stroke_color(GContext* ctx, GColor color) {
s_stroke_color = color;
}
void graphics_context_set_fill_color(GContext* ctx, GColor color) {
s_fill_color = color;
}
void graphics_context_set_antialiased(GContext *ctx, bool enable) {
}
void graphics_context_set_stroke_width(GContext* ctx, uint8_t stroke_width) {
s_stroke_width = stroke_width;
}
void gpath_draw_stroke(GContext* ctx, GPath* path, bool open) {
s_stroke_points = prv_copy_points(path->points, path->num_points, s_offset);
s_path_open = open;
s_path_stroke_count++;
}
void gpath_fill_precise_internal(GContext *ctx, GPointPrecise *points, size_t num_points) {
s_fill_points = prv_copy_points((GPoint*)points, num_points, s_offset);
s_path_fill_precise_count++;
}
void gpath_draw_filled(GContext* ctx, GPath *path) {
s_fill_points = prv_copy_points(path->points, path->num_points, s_offset);
s_path_fill_count++;
}
void gpath_draw_outline_precise_internal(GContext *ctx, GPointPrecise *points, size_t num_points,
bool open) {
s_stroke_points = prv_copy_points((GPoint*)points, num_points, s_offset);
s_path_open = open;
s_path_stroke_precise_count++;
}
void graphics_draw_circle(GContext* ctx, GPoint p, uint16_t radius) {
s_stroke_points = prv_copy_points(&p, 1, s_offset);
s_radius = radius;
s_circle_stroke_count++;
}
void graphics_fill_circle(GContext* ctx, GPoint p, uint16_t radius) {
s_fill_points = prv_copy_points(&p, 1, s_offset);
s_radius = radius;
s_circle_fill_count++;
}
void graphics_context_move_draw_box(GContext* ctx, GPoint offset) {
s_offset = offset;
}
void graphics_line_draw_precise_stroked(GContext* ctx, GPointPrecise p0, GPointPrecise p1) {
s_precise_lines = realloc(s_precise_lines,
(s_num_precise_lines + 1) * sizeof(*s_precise_lines));
s_precise_lines[s_num_precise_lines].p0 = p0;
s_precise_lines[s_num_precise_lines].p1 = p1;
s_num_precise_lines++;
}
void prv_reset(void) {
s_fill_color = GColorClear;
s_stroke_color = GColorClear;
s_stroke_width = 0;
s_path_num_points = 0;
if (s_stroke_points) {
free(s_stroke_points);
s_stroke_points = NULL;
}
if (s_fill_points) {
free(s_fill_points);
s_fill_points = NULL;
}
s_path_open = false;
s_radius = 0;
s_path_stroke_count = 0;
s_path_stroke_precise_count = 0;
s_path_fill_count = 0;
s_path_fill_precise_count = 0;
s_circle_stroke_count = 0;
s_circle_fill_count = 0;
s_offset = (GPoint){ 0 };
if (s_precise_lines) {
free(s_precise_lines);
s_precise_lines = NULL;
}
s_num_precise_lines = 0;
}
// setup and teardown
void test_gdraw_command__initialize(void) {
prv_reset();
}
void test_gdraw_command__cleanup(void) {
if (s_stroke_points) {
free(s_stroke_points);
s_stroke_points = NULL;
}
if (s_fill_points) {
free(s_fill_points);
s_fill_points = NULL;
}
if (s_precise_lines) {
free(s_precise_lines);
s_precise_lines = NULL;
}
}
// tests
void test_gdraw_command__draw_command_stroke(void) {
GDrawCommand *command = malloc(sizeof(GDrawCommand) + (sizeof(GPoint) * 2));
*command = (GDrawCommand){
.type = GDrawCommandTypePath,
.hidden = false,
.stroke_color = GColorRed,
.stroke_width = 1,
.fill_color = GColorBlue,
.path_open = false,
.num_points = 2,
};
GPoint points[] = {{ 3, 97 }, {5, 5} };
memcpy(command->points, points, sizeof(points));
gdraw_command_draw(NULL, command);
cl_assert_equal_i(s_stroke_color.argb, GColorRedARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorBlueARGB8);
cl_assert_equal_i(s_stroke_width, 1);
cl_assert_equal_i(s_path_num_points, 2);
cl_assert_equal_b(s_path_open, false);
cl_assert_equal_i(s_path_fill_count, 1);
cl_assert_equal_i(s_path_stroke_count, 1);
cl_assert(prv_compare_points(points, s_stroke_points, s_path_num_points));
cl_assert(prv_compare_points(points, s_fill_points, s_path_num_points));
prv_reset();
// set stroke width to zero - fill should be drawn, but not outline
gdraw_command_set_stroke_width(command, 0);
gdraw_command_draw(NULL, command);
cl_assert_equal_i(s_stroke_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorBlueARGB8);
cl_assert_equal_i(s_stroke_width, 0);
cl_assert_equal_i(s_path_num_points, 2);
cl_assert_equal_b(s_path_open, false);
cl_assert_equal_i(s_path_fill_count, 1);
cl_assert_equal_i(s_path_stroke_count, 0);
cl_assert(prv_compare_points(points, s_fill_points, s_path_num_points));
cl_assert_equal_p(s_stroke_points, NULL);
prv_reset();
// make fill color transparent (nothing should be drawn because the stroke width is zero and the
// fill is transparent
GColor color = gdraw_command_get_fill_color(command);
color.a = 0;
gdraw_command_set_fill_color(command, color);
gdraw_command_draw(NULL, command);
cl_assert_equal_i(s_stroke_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_stroke_width, 0);
cl_assert_equal_i(s_path_num_points, 0);
cl_assert_equal_b(s_path_open, false);
cl_assert_equal_i(s_path_fill_count, 0);
cl_assert_equal_i(s_path_stroke_count, 0);
cl_assert_equal_p(s_stroke_points, NULL);
cl_assert_equal_p(s_fill_points, NULL);
prv_reset();
// set stroke width to non-zero value. stroke should be drawn, but no fill
gdraw_command_set_stroke_width(command, 2);
gdraw_command_draw(NULL, command);
cl_assert_equal_i(s_stroke_color.argb, GColorRedARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_stroke_width, 2);
cl_assert_equal_i(s_path_num_points, 2);
cl_assert_equal_b(s_path_open, false);
cl_assert_equal_i(s_path_fill_count, 0);
cl_assert_equal_i(s_path_stroke_count, 1);
cl_assert(prv_compare_points(points, s_stroke_points, s_path_num_points));
cl_assert_equal_p(s_fill_points, NULL);
prv_reset();
// set stroke color to be transparent and restore fill - fill should be drawn, but no outline
// should be drawn
gdraw_command_set_fill_color(command, GColorGreen);
color = gdraw_command_get_stroke_color(command);
color.a = 0;
gdraw_command_set_stroke_color(command, color);
gdraw_command_draw(NULL, command);
cl_assert_equal_i(s_stroke_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorGreenARGB8);
cl_assert_equal_i(s_stroke_width, 0);
cl_assert_equal_i(s_path_num_points, 2);
cl_assert_equal_b(s_path_open, false);
cl_assert_equal_i(s_path_fill_count, 1);
cl_assert_equal_i(s_path_stroke_count, 0);
cl_assert_equal_p(s_stroke_points, NULL);
cl_assert(prv_compare_points(points, s_fill_points, s_path_num_points));
prv_reset();
// restore stroke color and change both points
gdraw_command_set_stroke_color(command, GColorPurple);
GPoint points2[] = { { 23, 45 }, { 67, 13} };
gdraw_command_set_point(command, 0, points2[0]);
gdraw_command_set_point(command, 1, points2[1]);
gdraw_command_draw(NULL, command);
cl_assert_equal_i(s_stroke_color.argb, GColorPurpleARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorGreenARGB8);
cl_assert_equal_i(s_stroke_width, 2);
cl_assert_equal_i(s_path_num_points, 2);
cl_assert_equal_b(s_path_open, false);
cl_assert_equal_i(s_path_fill_count, 1);
cl_assert_equal_i(s_path_stroke_count, 1);
cl_assert(prv_compare_points(points2, s_stroke_points, s_path_num_points));
cl_assert(prv_compare_points(points2, s_fill_points, s_path_num_points));
prv_reset();
// set path to be open
gdraw_command_set_path_open(command, true);
gdraw_command_draw(NULL, command);
cl_assert_equal_i(s_stroke_color.argb, GColorPurpleARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorGreenARGB8);
cl_assert_equal_i(s_stroke_width, 2);
cl_assert_equal_i(s_path_num_points, 2);
cl_assert_equal_b(s_path_open, true);
cl_assert_equal_i(s_path_fill_count, 1);
cl_assert_equal_i(s_path_stroke_count, 1);
cl_assert(prv_compare_points(points2, s_stroke_points, s_path_num_points));
cl_assert(prv_compare_points(points2, s_fill_points, s_path_num_points));
prv_reset();
// set command to be hidden - nothing should be drawn
gdraw_command_set_hidden(command, true);
cl_assert_equal_i(s_stroke_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_stroke_width, 0);
cl_assert_equal_i(s_path_num_points, 0);
cl_assert_equal_b(s_path_open, false);
cl_assert_equal_i(s_path_fill_count, 0);
cl_assert_equal_i(s_path_stroke_count, 0);
cl_assert_equal_p(s_stroke_points, NULL);
cl_assert_equal_p(s_fill_points, NULL);
free(command);
}
void test_gdraw_command__draw_precise_path(void) {
GDrawCommand *command = malloc(sizeof(GDrawCommand) + (sizeof(GPoint) * 3 ));
*command = (GDrawCommand){
.type = GDrawCommandTypePrecisePath,
.hidden = false,
.stroke_color = GColorRed,
.stroke_width = 1,
.fill_color = GColorBlue,
.path_open = false,
.num_points = 3,
};
GPointPrecise points[] = {
{ .x.raw_value = 8, .y.raw_value = 17 },
{ .x.raw_value = 4, .y.raw_value = 16 },
{ .x.raw_value = 2,.y.raw_value = 7 }
};
memcpy(command->precise_points, points, sizeof(points));
gdraw_command_draw(NULL, command);
cl_assert_equal_i(1, s_path_fill_precise_count);
cl_assert(prv_compare_points((GPoint*)points, s_fill_points, s_path_num_points));
cl_assert_equal_i(1, s_path_stroke_precise_count);
cl_assert_equal_b(false, s_path_open);
cl_assert(prv_compare_points((GPoint*)points, s_stroke_points, s_path_num_points));
prv_reset();
// change to open path and ensure that only draws 2 lines
command->path_open = true;
gdraw_command_draw(NULL, command);
cl_assert_equal_i(1, s_path_stroke_precise_count);
cl_assert_equal_b(true, s_path_open);
cl_assert(prv_compare_points((GPoint*)points, s_stroke_points, s_path_num_points));
free(command);
}
void test_gdraw_command__draw_circle(void) {
GDrawCommand *command = malloc(sizeof(GDrawCommand) + sizeof(GPoint));
*command = (GDrawCommand){
.type = GDrawCommandTypeCircle,
.hidden = false,
.stroke_color = GColorGreen,
.stroke_width = 1,
.fill_color = GColorOrange,
.radius = 300,
.num_points = 1,
};
GPoint center = { 15, 17 };
command->points[0] = center;
gdraw_command_draw(NULL, command);
cl_assert_equal_i(s_stroke_color.argb, GColorGreenARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorOrangeARGB8);
cl_assert_equal_i(s_stroke_width, 1);
cl_assert_equal_i(s_path_num_points, 1);
cl_assert_equal_b(s_radius, 300);
cl_assert_equal_i(s_circle_fill_count, 1);
cl_assert_equal_i(s_circle_stroke_count, 1);
cl_assert(prv_compare_points(&center, s_stroke_points, s_path_num_points));
cl_assert(prv_compare_points(&center, s_fill_points, s_path_num_points));
prv_reset();
// set stroke width to zero - fill should be drawn, but not outline
gdraw_command_set_stroke_width(command, 0);
gdraw_command_draw(NULL, command);
cl_assert_equal_i(s_stroke_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorOrangeARGB8);
cl_assert_equal_i(s_stroke_width, 0);
cl_assert_equal_i(s_path_num_points, 1);
cl_assert_equal_b(s_radius, 300);
cl_assert_equal_i(s_circle_fill_count, 1);
cl_assert_equal_i(s_circle_stroke_count, 0);
cl_assert_equal_p(s_stroke_points, NULL);
cl_assert(prv_compare_points(&center, s_fill_points, s_path_num_points));
prv_reset();
// make fill color transparent (nothing should be drawn because the stroke width is zero and the
// fill is transparent
GColor color = gdraw_command_get_fill_color(command);
color.a = 0;
gdraw_command_set_fill_color(command, color);
gdraw_command_draw(NULL, command);
cl_assert_equal_i(s_stroke_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_stroke_width, 0);
cl_assert_equal_i(s_path_num_points, 0);
cl_assert_equal_b(s_radius, 0);
cl_assert_equal_i(s_circle_fill_count, 0);
cl_assert_equal_i(s_circle_stroke_count, 0);
cl_assert_equal_p(s_stroke_points, NULL);
cl_assert_equal_p(s_fill_points, NULL);
prv_reset();
// set stroke width to non-zero value. stroke should be drawn, but no fill
gdraw_command_set_stroke_width(command, 2);
gdraw_command_draw(NULL, command);
cl_assert_equal_i(s_stroke_color.argb, GColorGreenARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_stroke_width, 2);
cl_assert_equal_i(s_path_num_points, 1);
cl_assert_equal_b(s_radius, 300);
cl_assert_equal_i(s_circle_fill_count, 0);
cl_assert_equal_i(s_circle_stroke_count, 1);
cl_assert(prv_compare_points(&center, s_stroke_points, s_path_num_points));
cl_assert_equal_p(s_fill_points, NULL);
prv_reset();
// set stroke color to be transparent and restore fill - fill should be drawn, but no outline
// should be drawn
gdraw_command_set_fill_color(command, GColorRed);
color = gdraw_command_get_stroke_color(command);
color.a = 0;
gdraw_command_set_stroke_color(command, color);
gdraw_command_draw(NULL, command);
cl_assert_equal_i(s_stroke_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorRedARGB8);
cl_assert_equal_i(s_stroke_width, 0);
cl_assert_equal_i(s_path_num_points, 1);
cl_assert_equal_b(s_path_open, false);
cl_assert_equal_i(s_circle_fill_count, 1);
cl_assert_equal_i(s_circle_stroke_count, 0);
cl_assert_equal_p(s_stroke_points, NULL);
cl_assert(prv_compare_points(&center, s_fill_points, s_path_num_points));
prv_reset();
// restore stroke color and set radius to zero - only a stroke should be drawn
gdraw_command_set_stroke_color(command, GColorPurple);
gdraw_command_set_radius(command, 0);
gdraw_command_draw(NULL, command);
cl_assert_equal_i(s_stroke_color.argb, GColorPurpleARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_stroke_width, 2);
cl_assert_equal_i(s_path_num_points, 1);
cl_assert_equal_b(s_radius, 0);
cl_assert_equal_i(s_circle_fill_count, 0);
cl_assert_equal_i(s_circle_stroke_count, 1);
cl_assert(prv_compare_points(&center, s_stroke_points, s_path_num_points));
cl_assert_equal_p(s_fill_points, NULL);
prv_reset();
// restore radius and set hidden - nothing should be drawn
gdraw_command_set_radius(command, 300);
gdraw_command_set_hidden(command, true);
gdraw_command_draw(NULL, command);
cl_assert_equal_i(s_stroke_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_fill_color.argb, GColorClearARGB8);
cl_assert_equal_i(s_stroke_width, 0);
cl_assert_equal_i(s_path_num_points, 0);
cl_assert_equal_b(s_radius, 0);
cl_assert_equal_i(s_circle_fill_count, 0);
cl_assert_equal_i(s_circle_stroke_count, 0);
cl_assert_equal_p(s_stroke_points, NULL);
cl_assert_equal_p(s_fill_points, NULL);
free(command);
}
static GDrawCommandList *prv_create_command_list_3(void) {
GDrawCommandList *command_list = malloc(sizeof(GDrawCommandList) + (3 * sizeof(GDrawCommand)) +
(sizeof(GPoint) * 6));
*command_list = (GDrawCommandList) {
.num_commands = 3
};
GDrawCommand *command;
command = gdraw_command_list_get_command(command_list, 0);
*command = (GDrawCommand) {
.type = GDrawCommandTypePath,
.hidden = false,
.stroke_color = GColorRed,
.stroke_width = 1,
.fill_color = GColorBlue,
.path_open = false,
.num_points = 2,
};
GPoint points1[] = { { 3, 97 }, {5, 5} };
memcpy(command_list->commands[0].points, points1, sizeof(points1));
command = gdraw_command_list_get_command(command_list, 1);
*command = (GDrawCommand) {
.type = GDrawCommandTypeCircle,
.hidden = false,
.stroke_color = GColorGreen,
.stroke_width = 1,
.fill_color = GColorOrange,
.radius = 300,
.num_points = 1,
};
command->points[0] = (GPoint) { 1, 2 };
command = gdraw_command_list_get_command(command_list, 2);
*command = (GDrawCommand) {
.type = GDrawCommandTypePath,
.hidden = false,
.stroke_color = GColorGreen,
.stroke_width = 1,
.fill_color = GColorPurple,
.path_open = false,
.num_points = 3,
};
GPoint points2[] = { { 6, 7 }, {5, 5}, { 0, 0 } };
memcpy(command->points, points2, sizeof(points2));
return command_list;
}
void test_gdraw_command__draw_command_list(void) {
GDrawCommandList *command_list = malloc(sizeof(GDrawCommandList) + sizeof(GDrawCommand) +
(sizeof(GPoint) * 2));
*command_list = (GDrawCommandList) {
.num_commands = 1
};
command_list->commands[0] = (GDrawCommand) {
.type = GDrawCommandTypePath,
.hidden = false,
.stroke_color = GColorRed,
.stroke_width = 1,
.fill_color = GColorBlue,
.path_open = false,
.num_points = 2,
};
GPoint points1[] = { { 3, 97 }, {5, 5} };
memcpy(command_list->commands[0].points, points1, sizeof(points1));
GContext *ctx = (GContext *)123; // just a fake internal guard != NULL
gdraw_command_list_draw(ctx, command_list);
cl_assert_equal_i(s_path_stroke_count, 1);
cl_assert_equal_i(s_path_fill_count, 1);
cl_assert_equal_i(s_circle_stroke_count, 0);
cl_assert_equal_i(s_circle_fill_count, 0);
prv_reset();
free(command_list);
command_list = prv_create_command_list_3();
gdraw_command_list_draw(ctx, command_list);
cl_assert_equal_i(s_path_stroke_count, 2);
cl_assert_equal_i(s_path_fill_count, 2);
cl_assert_equal_i(s_circle_stroke_count, 1);
cl_assert_equal_i(s_circle_fill_count, 1);
prv_reset();
gdraw_command_list_get_command(command_list, 2)->hidden = true;
gdraw_command_list_draw(ctx, command_list);
cl_assert_equal_i(s_path_stroke_count, 1);
cl_assert_equal_i(s_path_fill_count, 1);
cl_assert_equal_i(s_circle_stroke_count, 1);
cl_assert_equal_i(s_circle_fill_count, 1);
prv_reset();
gdraw_command_list_get_command(command_list, 0)->hidden = true;
gdraw_command_list_draw(ctx, command_list);
cl_assert_equal_i(s_path_stroke_count, 0);
cl_assert_equal_i(s_path_fill_count, 0);
cl_assert_equal_i(s_circle_stroke_count, 1);
cl_assert_equal_i(s_circle_fill_count, 1);
prv_reset();
gdraw_command_list_get_command(command_list, 1)->hidden = true;
gdraw_command_list_draw(ctx, command_list);
cl_assert_equal_i(s_path_stroke_count, 0);
cl_assert_equal_i(s_path_fill_count, 0);
cl_assert_equal_i(s_circle_stroke_count, 0);
cl_assert_equal_i(s_circle_fill_count, 0);
free(command_list);
}
void test_gdraw_command__validate_list(void) {
GDrawCommandList *command_list = prv_create_command_list_3();
size_t size = sizeof(GDrawCommandList) + (3 * sizeof(GDrawCommand)) +
(sizeof(GPoint) * 6);
cl_assert(gdraw_command_list_validate(command_list, size));
command_list->num_commands = 4;
cl_assert(!gdraw_command_list_validate(command_list, size));
command_list->num_commands = 3;
GDrawCommand *command = gdraw_command_list_get_command(command_list, 0);
command->num_points = 0;
cl_assert(!gdraw_command_list_validate(command_list, size));
command->num_points = 2;
command = gdraw_command_list_get_command(command_list, 2);
command->num_points = 4;
cl_assert(!gdraw_command_list_validate(command_list, size));
command->num_points = 3;
command->type = GDrawCommandTypeCircle;
cl_assert(!gdraw_command_list_validate(command_list, size));
command->type = GDrawCommandTypePrecisePath;
cl_assert(gdraw_command_list_validate(command_list, size));
free(command_list);
}
void test_gdraw_command__validate_image(void) {
size_t size = sizeof(GDrawCommandImage) + (3 * sizeof(GDrawCommand)) +
(sizeof(GPoint) * 6);
GDrawCommandImage *image = malloc(size);
GDrawCommandList *command_list = prv_create_command_list_3();
memcpy(&image->command_list, command_list, sizeof(GDrawCommandList) + (3 * sizeof(GDrawCommand)) +
(sizeof(GPoint) * 6));
free(command_list);
image->version = 1;
image->size = GSize(20, 20);
cl_assert(gdraw_command_image_validate(image, size));
cl_assert(!gdraw_command_image_validate(image, size - 1));
cl_assert(!gdraw_command_image_validate(image, size + 1));
cl_assert_equal_i(size, gdraw_command_image_get_data_size(image));
image->version = 2;
cl_assert(!gdraw_command_image_validate(image, size));
free(image);
}
void test_gdraw_command__clone_image(void) {
cl_assert_equal_p(gdraw_command_image_clone(NULL), NULL);
size_t size = sizeof(GDrawCommandImage) + (3 * sizeof(GDrawCommand)) +
(sizeof(GPoint) * 6);
GDrawCommandImage *image = malloc(size);
memset(image, 0, size);
GDrawCommandList *command_list = prv_create_command_list_3();
memcpy(&image->command_list, command_list, sizeof(GDrawCommandList) + (3 * sizeof(GDrawCommand)) +
(sizeof(GPoint) * 6));
free(command_list);
image->version = 1;
image->size = GSize(20, 20);
GDrawCommandImage *clone = gdraw_command_image_clone(image);
cl_assert(clone != image);
cl_assert_equal_i(gdraw_command_image_get_data_size(clone), size);
cl_assert_equal_i(0, memcmp(clone, image, size));
free(clone);
free(image);
}
void test_gdraw_command__draw_image(void) {
GDrawCommandImage *image = malloc(sizeof(GDrawCommandImage) + sizeof(GDrawCommand) +
(sizeof(GPoint) * 2));
GDrawCommandList *command_list = &image->command_list;
*command_list = (GDrawCommandList) {
.num_commands = 1
};
GDrawCommand *command;
command = gdraw_command_list_get_command(command_list, 0);
*command = (GDrawCommand) {
.type = GDrawCommandTypePath,
.hidden = false,
.stroke_color = GColorRed,
.stroke_width = 1,
.fill_color = GColorBlue,
.path_open = true,
.num_points = 2,
};
GPoint points[] = {{ 6, 1 }, {5, -5} };
memcpy(command_list->commands[0].points, points, sizeof(points));
GContext *ctx = (GContext *)123; // just a fake internal guard != NULL
gdraw_command_image_draw(ctx, image, GPoint(0, 0));
cl_assert_equal_i(s_path_num_points, 2);
cl_assert(prv_compare_points(points, s_stroke_points, s_path_num_points));
cl_assert(prv_compare_points(points, s_fill_points, s_path_num_points));
prv_reset();
gdraw_command_image_draw(ctx, image, GPoint(-1, 1));
cl_assert_equal_i(s_path_num_points, 2);
points[0] = GPoint(5, 2);
points[1] = GPoint(4, -4);
cl_assert(prv_compare_points(points, s_stroke_points, s_path_num_points));
cl_assert(prv_compare_points(points, s_fill_points, s_path_num_points));
free(image);
}
void test_gdraw_command__draw_frame(void) {
GDrawCommandFrame *frame = malloc(sizeof(GDrawCommandFrame) + sizeof(GDrawCommand) +
(sizeof(GPoint) * 2));
GDrawCommandList *command_list = &frame->command_list;
*command_list = (GDrawCommandList) {
.num_commands = 1
};
GDrawCommand *command = gdraw_command_list_get_command(command_list, 0);
*command = (GDrawCommand) {
.type = GDrawCommandTypePath,
.hidden = false,
.stroke_color = GColorRed,
.stroke_width = 1,
.fill_color = GColorBlue,
.path_open = true,
.num_points = 2,
};
GPoint points[] = {{ 1, 1 }, {2, -2} };
memcpy(command_list->commands[0].points, points, sizeof(points));
GContext *ctx = (GContext *)123; // just a fake internal guard != NULL
gdraw_command_frame_draw(ctx, NULL, frame, GPoint(0, 0));
cl_assert_equal_i(s_path_num_points, 2);
cl_assert(prv_compare_points(points, s_stroke_points, s_path_num_points));
cl_assert(prv_compare_points(points, s_fill_points, s_path_num_points));
prv_reset();
gdraw_command_frame_draw(ctx, NULL, frame, GPoint(-1, 1));
cl_assert_equal_i(s_path_num_points, 2);
points[0] = GPoint(0, 2);
points[1] = GPoint(1, -1);
cl_assert(prv_compare_points(points, s_stroke_points, s_path_num_points));
cl_assert(prv_compare_points(points, s_fill_points, s_path_num_points));
free(frame);
}
static int s_iterations = 0;
static bool prv_iterate(GDrawCommand *command, uint32_t index, void *context) {
s_iterations++;
return true;
}
void test_gdraw_command__iterate(void) {
GDrawCommandList *command_list = prv_create_command_list_3();
void *end = gdraw_command_list_iterate_private(command_list, prv_iterate, NULL);
cl_assert_equal_i(s_iterations, 3);
GDrawCommand *command = gdraw_command_list_get_command(command_list, 2);
void *expected_end = command->points + command->num_points;
cl_assert_equal_p(end, expected_end);
free(command_list);
}
typedef struct {
GDrawCommandProcessor processor;
GColor stroke_color;
} SetStrokeColorProcessor;
static void prv_set_stroke_color(GDrawCommandProcessor *processor,
GDrawCommand *processed_command,
const size_t processed_command_max_size,
const GDrawCommandList* list,
const GDrawCommand *command) {
SetStrokeColorProcessor *stroke_processor = (SetStrokeColorProcessor *)processor;
gdraw_command_set_stroke_color(processed_command, stroke_processor->stroke_color);
}
void test_gdraw_command__draw_command_list_processed(void) {
GDrawCommandList *command_list = prv_create_command_list_3();
GColor stroke_color = GColorTiffanyBlue;
SetStrokeColorProcessor stroke_processor = {
.processor = {
.command = prv_set_stroke_color,
},
.stroke_color = stroke_color,
};
// ctx can't be null pass in random pointer to satisfy function
GContext ctx;
gdraw_command_list_draw_processed(&ctx, command_list, (GDrawCommandProcessor *)&stroke_processor);
cl_assert_equal_i(s_stroke_color.argb, GColorTiffanyBlueARGB8);
free(command_list);
}
void test_gdraw_command__get_max_command_size_in_list(void) {
GDrawCommandList *command_list = prv_create_command_list_3();
// The third command in the list is the largest so it's size should be returned by
// prv_get_list_max_command_size
cl_assert_equal_i(gdraw_command_get_data_size(gdraw_command_list_get_command(command_list, 2)),
prv_get_list_max_command_size(command_list));
free(command_list);
}