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

312 lines
No EOL
8.8 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 "test_jerry_port_common.h"
#define DO_NOT_STUB_LEGACY2 1
#include "test_rocky_common.h"
#include "applib/graphics/gtypes.h"
#include "applib/rockyjs/api/rocky_api.h"
#include "applib/rockyjs/api/rocky_api_global.h"
#include "applib/rockyjs/api/rocky_api_graphics.h"
#include "applib/rockyjs/api/rocky_api_graphics_text.h"
#include "applib/rockyjs/pbl_jerry_port.h"
#include "util/trig.h"
#include "applib/graphics/framebuffer.h"
#include "../graphics/util.h"
// Standard
#include "string.h"
// Fakes
#include "fake_app_timer.h"
#include "fake_time.h"
// Stubs
#include "stubs_app_manager.h"
#include "stubs_app_state.h"
#include "stubs_logging.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_resources.h"
#include "stubs_sleep.h"
#include "stubs_serial.h"
#include "stubs_syscalls.h"
#include "stubs_sys_exit.h"
size_t heap_bytes_free(void) {
return 123456;
}
bool gbitmap_init_with_png_data(GBitmap *bitmap, const uint8_t *data, size_t data_size) {
return false;
}
bool gbitmap_png_data_is_png(const uint8_t *data, size_t data_size) {
return false;
}
void layer_get_unobstructed_bounds(const Layer *layer, GRect *bounds_out) {
*bounds_out = layer->bounds;
}
void layer_mark_dirty(Layer *layer) {}
static Window s_app_window_stack_get_top_window;
Window *app_window_stack_get_top_window() {
return &s_app_window_stack_get_top_window;
}
// no text rendering in this test
void rocky_api_graphics_text_init(void) {}
void rocky_api_graphics_text_deinit(void) {}
void rocky_api_graphics_text_add_canvas_methods(jerry_value_t obj) {}
void rocky_api_graphics_text_reset_state(void) {}
//GBitmap s_bitmap;
//uint8_t s_bitmap_data[DISPLAY_FRAMEBUFFER_BYTES];
GContext s_context;
FrameBuffer *s_framebuffer;
GBitmap *s_pixels;
static void prv_init_gcontext(GSize size) {
graphics_context_init(&s_context, s_framebuffer, GContextInitializationMode_App);
framebuffer_clear(s_framebuffer);
if (s_pixels) {
gbitmap_destroy(s_pixels);
}
s_pixels = gbitmap_create_blank(size, GBITMAP_NATIVE_FORMAT);
memset(s_pixels->addr, 0xff, size.h * s_pixels->row_size_bytes);
s_context.dest_bitmap = *s_pixels;
s_context.draw_state.clip_box = (GRect){.size = size};
s_context.draw_state.drawing_box = s_context.draw_state.clip_box;
s_app_state_get_graphics_context = &s_context;
}
void test_rocky_api_graphics_rendering__initialize(void) {
fake_app_timer_init();
rocky_runtime_context_init();
jerry_init(JERRY_INIT_EMPTY);
s_framebuffer = malloc(sizeof(FrameBuffer));
framebuffer_init(s_framebuffer, &(GSize) { DISP_COLS, DISP_ROWS });
s_app_window_stack_get_top_window = (Window){};
prv_init_gcontext((GSize) { DISP_COLS, DISP_ROWS });
s_app_event_loop_callback = NULL;
}
void test_rocky_api_graphics_rendering__cleanup(void) {
fake_app_timer_deinit();
// some tests deinitialize the engine, avoid double de-init
if (app_state_get_rocky_runtime_context() != NULL) {
jerry_cleanup();
rocky_runtime_context_deinit();
}
gbitmap_destroy(s_pixels);
s_pixels = NULL;
free(s_framebuffer);
}
static const RockyGlobalAPI *s_graphics_api[] = {
&GRAPHIC_APIS,
NULL,
};
jerry_value_t prv_create_canvas_context_2d_for_layer(Layer *layer);
static const jerry_value_t prv_global_init_and_set_ctx(void) {
rocky_global_init(s_graphics_api);
// make this easily testable by putting it int JS context as global
Layer l = {.bounds = GRect(0, 0, 144, 168)};
const jerry_value_t ctx = prv_create_canvas_context_2d_for_layer(&l);
jerry_set_object_field(jerry_get_global_object(), "ctx", ctx);
return ctx;
}
void test_rocky_api_graphics_rendering__lines(void) {
prv_global_init_and_set_ctx();
// taken from http://fiddle.jshell.net/9298zub9/2/
EXECUTE_SCRIPT(
"var t1 = 10;\n"
"var b1 = 20.5;\n"
"var t2 = 30.5;\n"
"var b2 = 40;\n"
" \n"
"for (var i = 1; i <= 5; i++) {\n"
" ctx.beginPath();\n"
" var x1 = 20 * i;\n"
" var x2 = x1 + 10.5; \n"
" ctx.moveTo(x1, t1);\n"
" ctx.lineTo(x1, b1);\n"
" ctx.moveTo(x2, t1);\n"
" ctx.lineTo(x2, b1);\n"
"\n"
" ctx.moveTo(x1, t2);\n"
" ctx.lineTo(x1, b2);\n"
" ctx.moveTo(x2, t2);\n"
" ctx.lineTo(x2, b2);\n"
"\n"
" ctx.lineWidth = i;\n"
" ctx.stroke();\n"
"}\n"
"for (var i = 1; i <= 5; i++) {\n"
" ctx.beginPath();\n"
" var y1 = 40 + i * 20;\n"
" var y2 = y1 + 10.5;\n"
" ctx.moveTo(t1, y1);\n"
" ctx.lineTo(b1, y1);\n"
" ctx.moveTo(t1, y2);\n"
" ctx.lineTo(b1, y2);\n"
" \n"
" ctx.moveTo(t2, y1);\n"
" ctx.lineTo(b2, y1);\n"
" ctx.moveTo(t2, y2);\n"
" ctx.lineTo(b2, y2);\n"
"\n"
" ctx.lineWidth = i;\n"
" ctx.stroke();\n"
"}\n"
"for (var i = 1; i <= 5; i++) {\n"
" ctx.beginPath();\n"
" var xx = 50;\n"
" var yy = 50;\n"
" var d = 15 * i;\n"
" ctx.moveTo(xx, yy + d);\n"
" ctx.lineTo(xx + d, yy);\n"
"\n"
" ctx.lineWidth = i;\n"
" ctx.stroke();\n"
"}"
);
const bool eq_result =
gbitmap_pbi_eq(&s_context.dest_bitmap, TEST_NAMED_PBI_FILE("rocky_rendering_lines"));
cl_check(eq_result);
}
void test_rocky_api_graphics_rendering__rect(void) {
prv_init_gcontext(GSize(500, 150));
prv_global_init_and_set_ctx();
// taken from http://fiddle.jshell.net/a5gjzb7c/6/
EXECUTE_SCRIPT(
"function render(x, y, f) {\n"
" f(x + 10, y + 10, 10, 10);\n"
" f(x + 30.2, y + 10, 10, 10.2);\n"
" f(x + 50.5, y + 10, 10, 10);\n"
" f(x + 70.7, y + 10, 10.5, 10.8);\n"
" f(x + 10, y + 30.5, 10, 10);\n"
" f(x + 30.2, y + 30.5, 10, 10.2);\n"
" f(x + 50.5, y + 30.5, 10, 10);\n"
" f(x + 70.7, y + 30.5, 10.5, 10.8);\n"
" \n"
" f(x + 90, y + 10, 0, 0);\n"
" f(x + 110, y + 10, 0.5, 0.5);\n"
" f(x + 90, y + 30, -2, -2);\n"
" f(x + 110, y + 30, -5.5, -6);\n"
"}"
"\n"
"for (var i = 0; i <= 3; i++) {\n"
" ctx.lineWidth = i;\n"
" var x = 120 * i;\n"
" render(x, 0, ctx[i == 0 ? 'fillRect' : 'strokeRect'].bind(ctx));\n"
" render(x, 50, function(x,y,w,h) {\n"
" ctx.beginPath();\n"
" ctx.rect(x, y, w, h);\n"
" ctx[i == 0? 'fill' : 'stroke'](); \n"
" });\n"
" render(x, 100, function r(x, y, w, h) {\n"
" ctx.beginPath();\n"
" ctx.moveTo(x, y);\n"
" ctx.lineTo(x + w, y);\n"
" ctx.lineTo(x + w, y + h);\n"
" ctx.lineTo(x, y + h);\n"
" ctx.lineTo(x, y);\n"
" ctx[i == 0? 'fill' : 'stroke'](); \n"
" });\n"
"}"
);
const bool eq_result =
gbitmap_pbi_eq(&s_context.dest_bitmap, TEST_NAMED_PBI_FILE("rocky_rendering_rect"));
cl_check(eq_result);
}
void test_rocky_api_graphics_rendering__arc(void) {
prv_init_gcontext(GSize(500, 300));
prv_global_init_and_set_ctx();
// http://fiddle.jshell.net/uopr1ez2/2/
EXECUTE_SCRIPT(
"var xx = 200;\n"
"\n"
"function f(x, y, r, a1, a2) {\n"
" ctx.beginPath();\n"
" ctx.arc(x, y, r, a1, a2, false);\n"
" ctx.stroke();\n"
"\n"
" ctx.rockyFillRadial(x + xx, y, 0, r, a1, a2);\n"
"}\n"
"\n"
"function g(x, y, a1, a2) {\n"
" f(x, y, 5, a1, a2);\n"
" f(x, y, 15.5, a1, a2);\n"
" f(x, y, 25.2, a1, a2);\n"
" f(x, y, 34.8, a1, a2);\n"
"}\n"
"\n"
"function h(x, y, a1, a2) {\n"
" for (var i = 0; i < 4; i++) {\n"
" ctx.lineWidth = i + 1;\n"
" g(x, y + 40 * i, a1, a2);\n"
" }\n"
"}\n"
"\n"
"h(2, 2, 0, 0.5 * Math.PI);\n"
"h(50.5, 2.5, 0, 0.5 * Math.PI);\n"
"h(100.2, 2.2, 0, 0.5 * Math.PI);\n"
"h(150.8, 2.8, 0, 0.5 * Math.PI);\n"
"\n"
"ctx.lineWidth = 1;\n"
"f(20, 200, 10, 0, 2 * Math.PI);\n"
"f(60.5, 200, 10, 0, 2 * Math.PI);\n"
"f(100.5, 200.5, 10, 0, 2 * Math.PI);\n"
"f(140, 200.5, 10, 0, 2 * Math.PI);\n"
"\n"
"f(20, 240, 11, 0, 2 * Math.PI);\n"
"f(60.5, 240, 11, 0, 2 * Math.PI);\n"
"f(100.5, 240.5, 11, 0, 2 * Math.PI);\n"
"f(140, 240.5, 11, 0, 2 * Math.PI);\n"
"\n"
"f(20, 280, 11, 0, -0.5 * Math.PI);"
);
const bool eq_result =
gbitmap_pbi_eq(&s_context.dest_bitmap, TEST_NAMED_PBI_FILE("rocky_rendering_arc"));
cl_check(eq_result);
}