mirror of
https://github.com/google/pebble
synced 2026-10-17 16:54:11 +02:00
Import of the watch repository from Pebble
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../test_graphics.h"
|
||||
|
||||
typedef struct stat STAT_T;
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
#define IMAGE_DWORDS_PER_LINE 6
|
||||
|
||||
static void print_framebuffer_as_literal(const char* unit_name, const FrameBuffer* framebuffer, int col, int row, int cols, int rows) {
|
||||
printf("\n-- %s image --\n", unit_name);
|
||||
printf(" uint32_t image[] = {\n");
|
||||
int num_words = 0;
|
||||
for (int j = row; j < row+rows && j < FrameBuffer_MaxY; j++) {
|
||||
bool j_more = j+1 < row+rows && j+1 < FrameBuffer_MaxY;
|
||||
for (int i = col; i < col+cols && i < FRAMEBUFFER_WORDS_PER_ROW; i++) {
|
||||
bool i_more = i+1 < col+cols && i+1 < FRAMEBUFFER_WORDS_PER_ROW;
|
||||
num_words++;
|
||||
if (num_words % IMAGE_DWORDS_PER_LINE == 1) { printf(" "); }
|
||||
printf("0x%x", framebuffer->buffer[j*FRAMEBUFFER_WORDS_PER_ROW+i]);
|
||||
if (i_more || j_more) {
|
||||
printf(",");
|
||||
if (num_words % IMAGE_DWORDS_PER_LINE == 0) { printf("\n"); }
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("\n };\n");
|
||||
}
|
||||
|
||||
static void fread_pbi_header(FILE* file, GBitmap* bitmap) {
|
||||
fread(&bitmap->row_size_bytes, sizeof(bitmap->row_size_bytes), 1, file);
|
||||
fread(&bitmap->info_flags, sizeof(bitmap->info_flags), 1, file);
|
||||
fread(&bitmap->bounds, sizeof(bitmap->bounds), 1, file);
|
||||
}
|
||||
|
||||
static uint32_t* fread_pbi(FILE* file, GBitmap* bitmap) {
|
||||
fread_pbi_header(file, bitmap);
|
||||
size_t image_size = bitmap->row_size_bytes/4 * bitmap->bounds.size.h;
|
||||
uint32_t* buffer = bitmap->addr = (uint32_t*) malloc(image_size*4);
|
||||
if (buffer) {
|
||||
fread(buffer, 4, image_size, file);
|
||||
return buffer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint32_t* read_pbi(const char* filename, GBitmap* bitmap) {
|
||||
char res_path[strlen(CLAR_FIXTURE_PATH) + 1 + strlen(GRAPHICS_FIXTURE_PATH) + 1 + strlen(filename) + 1];
|
||||
sprintf(res_path, "%s/%s/%s", CLAR_FIXTURE_PATH, GRAPHICS_FIXTURE_PATH, filename);
|
||||
FILE* file = fopen(res_path, "r");
|
||||
if (!file) {
|
||||
printf("\ncould not open %s for reading\n", res_path);
|
||||
return NULL;
|
||||
}
|
||||
uint32_t* buffer = fread_pbi(file, bitmap);
|
||||
fclose(file);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void free_pbi(GBitmap* bitmap) {
|
||||
if (bitmap->addr) {
|
||||
free(bitmap->addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void fwrite_screenshot_from_framebuffer(FILE* file, const FrameBuffer* framebuffer) {
|
||||
uint16_t row_size_bytes = (FrameBuffer_MaxX + 31) / 32 * 4;
|
||||
uint16_t info_flags = 1 << 1;
|
||||
GRect bounds = GRect(0, 0, FrameBuffer_MaxX, FrameBuffer_MaxY);
|
||||
fwrite(&row_size_bytes, sizeof(row_size_bytes), 1, file);
|
||||
fwrite(&info_flags, sizeof(info_flags), 1, file);
|
||||
fwrite(&bounds, sizeof(bounds), 1, file);
|
||||
fwrite(framebuffer->buffer, sizeof(framebuffer->buffer[0]), row_size_bytes/4*FrameBuffer_MaxY, file);
|
||||
}
|
||||
|
||||
static bool write_screenshot_from_framebuffer(const char* filename, const FrameBuffer* framebuffer) {
|
||||
FILE* file = fopen(filename, "w");
|
||||
if (!file) {
|
||||
printf("\ncould not open %s for writing a screenshot\n", filename);
|
||||
return false;
|
||||
}
|
||||
fwrite_screenshot_from_framebuffer(file, framebuffer);
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool framebuffer_eq_image_raw(const FrameBuffer* framebuffer, uint32_t* image, int col, int row, int cols, int rows) {
|
||||
for (int j = row; j < row+rows && j < FrameBuffer_MaxY; j++) {
|
||||
for (int i = col; i < col+cols && i < FRAMEBUFFER_WORDS_PER_ROW; i++) {
|
||||
int fb_index = j*FRAMEBUFFER_WORDS_PER_ROW+i;
|
||||
int img_index = (j-row)*cols+(i-col);
|
||||
uint32_t number_of_bits = col < IMAGE_DWORDS_PER_LINE-1 ? 32 : DISP_COLS % 32;
|
||||
uint32_t mask = number_of_bits == 32 ? -1 : ((1 << number_of_bits) - 1);
|
||||
uint32_t fb_part = mask & framebuffer->buffer[fb_index];
|
||||
uint32_t img_part = mask & image[img_index];
|
||||
if (fb_part != img_part) {
|
||||
printf("\nframebuffer[%d] != image[%d], (0x%x, 0x%x) col=%d row=%d\n",
|
||||
fb_index, img_index, framebuffer->buffer[fb_index], image[img_index], i, j);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool framebuffer_eq_image(const char* unit_name, const FrameBuffer* framebuffer, uint32_t* image, int col, int row, int cols, int rows) {
|
||||
#ifndef TEST_GRAPHICS_SILENT
|
||||
print_framebuffer_as_literal(unit_name, framebuffer, col, row, cols, rows);
|
||||
#endif
|
||||
return framebuffer_eq_image_raw(framebuffer, image, col, row, cols, rows);
|
||||
}
|
||||
|
||||
static bool framebuffer_eq(const char* unit_name, const FrameBuffer* framebuffer, FrameBuffer* other) {
|
||||
return framebuffer_eq_image(unit_name, framebuffer, other->buffer, 0, 0, FRAMEBUFFER_WORDS_PER_ROW, FrameBuffer_MaxY);
|
||||
}
|
||||
|
||||
static bool framebuffer_eq_screenshot_raw(const FrameBuffer* framebuffer, const char* filename) {
|
||||
GBitmap bitmap;
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file) {
|
||||
printf("\nfailed to open %s\n", filename);
|
||||
return false;
|
||||
}
|
||||
fread_pbi_header(file, &bitmap);
|
||||
uint32_t buffer[bitmap.row_size_bytes/4 * bitmap.bounds.size.h];
|
||||
fread(buffer, 1, sizeof(buffer), file);
|
||||
fclose(file);
|
||||
if (!framebuffer_eq_image_raw(framebuffer, buffer, 0, 0, bitmap.row_size_bytes/4, bitmap.bounds.size.h)) {
|
||||
printf("\ndoes not match screenshot %s\n", filename);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool framebuffer_eq_screenshot(const FrameBuffer* framebuffer, const char* filename) {
|
||||
char ref_path[strlen(CLAR_FIXTURE_PATH) + 1 + strlen(GRAPHICS_FIXTURE_PATH) + 1 + strlen(filename) + 1];
|
||||
sprintf(ref_path, "%s/%s/%s", CLAR_FIXTURE_PATH, GRAPHICS_FIXTURE_PATH, filename);
|
||||
STAT_T st;
|
||||
if (stat(ref_path, &st) != 0 || !framebuffer_eq_screenshot_raw(framebuffer, ref_path)) {
|
||||
char out_path[strlen(GRAPHICS_FIXTURE_OUT_PATH) + 1 + strlen(filename) + 1];
|
||||
sprintf(out_path, "%s/%s", GRAPHICS_FIXTURE_OUT_PATH,filename);
|
||||
write_screenshot_from_framebuffer(out_path, framebuffer);
|
||||
char cwd[PATH_MAX];
|
||||
if (!getcwd(cwd, PATH_MAX)) {
|
||||
printf("\nfailed to get working directory\n");
|
||||
return false;
|
||||
}
|
||||
printf("\ngenerated %s/%s\n", cwd, out_path);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool framebuffer_is_empty(const char* unit_name, FrameBuffer* framebuffer, GColor color) {
|
||||
for (int j = 0; j < FrameBuffer_MaxY; j++) {
|
||||
for (int i = 0; i < FRAMEBUFFER_WORDS_PER_ROW; i++) {
|
||||
int fb_index = j*FRAMEBUFFER_WORDS_PER_ROW+i;
|
||||
if (framebuffer->buffer[fb_index] != (gcolor_equal(color, GColorBlack) ? 0 : 0xffffffff)) {
|
||||
printf("\nframebuffer[%d] is not empty(%d), has 0x%x, col=%d row=%d\n",
|
||||
fb_index, color.argb, framebuffer->buffer[fb_index], i, j);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../test_graphics.h"
|
||||
#include "../util.h"
|
||||
|
||||
typedef struct stat STAT_T;
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
#define IMAGE_DWORDS_PER_LINE 6
|
||||
|
||||
static void print_framebuffer_as_literal(const char* unit_name, const FrameBuffer* framebuffer,
|
||||
int col, int row, int cols, int rows) {
|
||||
printf("\n-- %s image --\n", unit_name);
|
||||
printf(" uint8_t image[] = {\n");
|
||||
for (int y = row; y < row+rows && y < FrameBuffer_MaxY; ++y) {
|
||||
for (int x = col; x < col + cols && x < FrameBuffer_MaxX; ++x) {
|
||||
printf("0x%x", framebuffer->buffer[y*FRAMEBUFFER_BYTES_PER_ROW + x]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n };\n");
|
||||
}
|
||||
|
||||
static void fread_pbi_header(FILE* file, GBitmap* bitmap) {
|
||||
fread(&bitmap->row_size_bytes, sizeof(bitmap->row_size_bytes), 1, file);
|
||||
fread(&bitmap->info_flags, sizeof(bitmap->info_flags), 1, file);
|
||||
fread(&bitmap->bounds, sizeof(bitmap->bounds), 1, file);
|
||||
bitmap->info.format = GBitmapFormat1Bit;
|
||||
}
|
||||
|
||||
static uint32_t* fread_pbi(FILE* file, GBitmap* bitmap) {
|
||||
fread_pbi_header(file, bitmap);
|
||||
size_t image_size = bitmap->row_size_bytes/4 * bitmap->bounds.size.h;
|
||||
uint32_t* buffer = bitmap->addr = (uint32_t*) malloc(image_size*4);
|
||||
if (buffer) {
|
||||
fread(buffer, 4, image_size, file);
|
||||
return buffer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint32_t* read_pbi(const char* filename, GBitmap* bitmap) {
|
||||
char res_path[strlen(CLAR_FIXTURE_PATH) + 1 + strlen(GRAPHICS_FIXTURE_PATH) + 1 + strlen(filename) + 1];
|
||||
sprintf(res_path, "%s/%s/%s", CLAR_FIXTURE_PATH, GRAPHICS_FIXTURE_PATH, filename);
|
||||
FILE* file = fopen(res_path, "r");
|
||||
if (!file) {
|
||||
printf("\ncould not open %s for reading\n", res_path);
|
||||
return NULL;
|
||||
}
|
||||
uint32_t* buffer = fread_pbi(file, bitmap);
|
||||
fclose(file);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void free_pbi(GBitmap* bitmap) {
|
||||
if (bitmap->addr) {
|
||||
free(bitmap->addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void fwrite_screenshot_from_framebuffer(FILE* file, const FrameBuffer* framebuffer) {
|
||||
uint16_t row_size_bytes = FRAMEBUFFER_BYTES_PER_ROW;
|
||||
uint16_t info_flags = 1 << 1;
|
||||
GRect bounds = GRect(0, 0, FrameBuffer_MaxX, FrameBuffer_MaxY);
|
||||
fwrite(&row_size_bytes, sizeof(row_size_bytes), 1, file);
|
||||
fwrite(&info_flags, sizeof(info_flags), 1, file);
|
||||
fwrite(&bounds, sizeof(bounds), 1, file);
|
||||
fwrite(framebuffer->buffer, sizeof(framebuffer->buffer[0]), FRAMEBUFFER_SIZE_BYTES, file);
|
||||
}
|
||||
|
||||
static bool write_screenshot_from_framebuffer(const char* filename, const FrameBuffer* framebuffer) {
|
||||
FILE* file = fopen(filename, "w");
|
||||
if (!file) {
|
||||
printf("\ncould not open %s for writing a screenshot\n", filename);
|
||||
return false;
|
||||
}
|
||||
fwrite_screenshot_from_framebuffer(file, framebuffer);
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool framebuffer_eq_image_raw(const FrameBuffer* framebuffer, uint8_t* image, int col,
|
||||
int row, int cols, int rows) {
|
||||
const uint8_t *fb = framebuffer->buffer;
|
||||
for (int y = row; y < row+rows && y < FrameBuffer_MaxY; ++y) {
|
||||
for (int x = col; x < col+cols && x < FrameBuffer_MaxX; ++x) {
|
||||
int fb_index = y * FRAMEBUFFER_BYTES_PER_ROW + x;
|
||||
int img_index = (y-row)*FRAMEBUFFER_BYTES_PER_ROW + (x-col);
|
||||
if (fb[fb_index] != image[img_index]) {
|
||||
printf("\nframebuffer[%d] != image[%d], (0x%x, 0x%x) col=%d row=%d\n",
|
||||
fb_index, img_index, fb[fb_index], image[img_index], x, y);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool framebuffer_eq_image(const char* unit_name, const FrameBuffer* framebuffer,
|
||||
uint8_t* image, int col, int row, int cols, int rows) {
|
||||
#ifndef TEST_GRAPHICS_SILENT
|
||||
print_framebuffer_as_literal(unit_name, framebuffer, col, row, cols, rows);
|
||||
#endif
|
||||
return framebuffer_eq_image_raw(framebuffer, image, col, row, cols, rows);
|
||||
}
|
||||
|
||||
static bool framebuffer_eq(const char* unit_name, const FrameBuffer* framebuffer,
|
||||
FrameBuffer* other) {
|
||||
return framebuffer_eq_image(unit_name, framebuffer, other->buffer, 0, 0,
|
||||
FRAMEBUFFER_BYTES_PER_ROW, FrameBuffer_MaxY);
|
||||
}
|
||||
|
||||
static void convert_to_8bit_image(const uint32_t *in, GBitmap *bmp, uint8_t *out) {
|
||||
for (int y = 0; y < bmp->bounds.size.h; ++y) {
|
||||
for (int x = 0; x < bmp->bounds.size.w; ++x) {
|
||||
int in_idx = y * (bmp->row_size_bytes/4) + (x / 32);
|
||||
int bit_idx = x % 32;
|
||||
bool set = in[in_idx] & (1 << bit_idx);
|
||||
|
||||
int out_idx = y * bmp->bounds.size.w + x;
|
||||
out[out_idx] = (set) ? GColorWhite.argb : GColorBlack.argb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool framebuffer_eq_screenshot_raw(const FrameBuffer* framebuffer, const char* filename) {
|
||||
GBitmap bitmap;
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file) {
|
||||
printf("\nfailed to open %s\n", filename);
|
||||
return false;
|
||||
}
|
||||
fread_pbi_header(file, &bitmap);
|
||||
uint32_t buffer[bitmap.row_size_bytes/4 * bitmap.bounds.size.h];
|
||||
fread(buffer, 1, sizeof(buffer), file);
|
||||
fclose(file);
|
||||
|
||||
uint8_t image[bitmap.bounds.size.w * bitmap.bounds.size.h];
|
||||
convert_to_8bit_image(buffer, &bitmap, image);
|
||||
if (!framebuffer_eq_image_raw(framebuffer, image, 0, 0, bitmap.row_size_bytes,
|
||||
bitmap.bounds.size.h)) {
|
||||
bitmap.info.format = GBitmapFormat8Bit;
|
||||
bitmap.row_size_bytes = FRAMEBUFFER_BYTES_PER_ROW;
|
||||
printf("\ndoes not match screenshot %s\n", filename);
|
||||
printf("Good:\n");
|
||||
bitmap.addr = ℑ
|
||||
print_bitmap(&bitmap);
|
||||
printf("Bad:\n");
|
||||
bitmap.addr = (void*)&framebuffer->buffer;
|
||||
print_bitmap(&bitmap);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool framebuffer_eq_screenshot(const FrameBuffer* framebuffer, const char* filename) {
|
||||
char ref_path[strlen(CLAR_FIXTURE_PATH) + 1 + strlen(GRAPHICS_FIXTURE_PATH) + 1 + strlen(filename) + 1];
|
||||
sprintf(ref_path, "%s/%s/%s", CLAR_FIXTURE_PATH, GRAPHICS_FIXTURE_PATH, filename);
|
||||
STAT_T st;
|
||||
if (stat(ref_path, &st) != 0 || !framebuffer_eq_screenshot_raw(framebuffer, ref_path)) {
|
||||
char out_path[strlen(GRAPHICS_FIXTURE_OUT_PATH) + 1 + strlen(filename) + 1];
|
||||
sprintf(out_path, "%s/%s", GRAPHICS_FIXTURE_OUT_PATH,filename);
|
||||
write_screenshot_from_framebuffer(out_path, framebuffer);
|
||||
char cwd[PATH_MAX];
|
||||
if (!getcwd(cwd, PATH_MAX)) {
|
||||
printf("\nfailed to get working directory\n");
|
||||
return false;
|
||||
}
|
||||
printf("\ngenerated %s/%s\n", cwd, out_path);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool framebuffer_is_empty(const char* unit_name, FrameBuffer* framebuffer, GColor color) {
|
||||
for (int j = 0; j < FrameBuffer_MaxY; j++) {
|
||||
for (int i = 0; i < FRAMEBUFFER_BYTES_PER_ROW; i++) {
|
||||
int fb_index = j*FRAMEBUFFER_BYTES_PER_ROW + i;
|
||||
if (framebuffer->buffer[fb_index] != color.argb) {
|
||||
printf("\nframebuffer[%d] is not empty(%d), has 0x%x, col=%d row=%d\n",
|
||||
fb_index, color.argb, framebuffer->buffer[fb_index], i, j);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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 "stubs_app_state.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_language_ui.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_pebble_tasks.h"
|
||||
#include "stubs_print.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_ui_window.h"
|
||||
#include "stubs_unobstructed_area.h"
|
||||
@@ -0,0 +1,920 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/bitblt.h"
|
||||
#include "applib/graphics/bitblt_private.h"
|
||||
#include "applib/graphics/8_bit/framebuffer.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "test_graphics.h"
|
||||
|
||||
static GContext ctx;
|
||||
static FrameBuffer framebuffer;
|
||||
|
||||
// Helpers
|
||||
////////////////////////////////////
|
||||
|
||||
static void prv_set_opacity(GBitmap *bmp, uint8_t opacity) {
|
||||
GRect bounds = bmp->bounds;
|
||||
for (uint32_t idx = 0; idx < bounds.size.w * bounds.size.h; idx++) {
|
||||
((GColor *)(bmp->addr + idx))->a = opacity;
|
||||
}
|
||||
}
|
||||
|
||||
static GColor prv_next_color(GColor color) {
|
||||
GColor8 result = (GColor8){.argb = (color.argb + 1) % 64};
|
||||
result.a = color.a;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
// setup and teardown
|
||||
void test_bitblt__initialize(void) {
|
||||
framebuffer_init(&framebuffer, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
test_graphics_context_init(&ctx, &framebuffer);
|
||||
}
|
||||
|
||||
void test_bitblt__cleanup(void) {
|
||||
}
|
||||
|
||||
// Test images reside in "tests/fw/graphics/test_images/".
|
||||
// The wscript will convert them from PNGs in that directory to PBIs in the build directory.
|
||||
// Naming conventions of these images tends to be '<test_name>.<bitdepth>.png'.
|
||||
// For example:
|
||||
// test_bitblt__8bit_assign would have:
|
||||
// - test_bitblt__8bit_assign.8bit.png
|
||||
// - test_bitblt__8bit_assign-expect.8bit.png
|
||||
|
||||
// Tests assign, from same size to same size.
|
||||
// Setup:
|
||||
// - Source is 10x10, white.
|
||||
// - Dest is 10x10, green.
|
||||
// Result:
|
||||
// - All white.
|
||||
void test_bitblt__8bit_compop(void) {
|
||||
GBitmap *src_bitmap = get_gbitmap_from_pbi("test_bitblt__8bit_assign.8bit.pbi");
|
||||
uint8_t dest_data[src_bitmap->bounds.size.w*src_bitmap->bounds.size.h];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = src_bitmap->bounds.size.w,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = src_bitmap->bounds
|
||||
};
|
||||
// All compositing modes except GCompOpSet should be the same as GCompAssign
|
||||
memset(dest_data, GColorGreen.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__8bit_assign-expect.8bit.pbi"));
|
||||
|
||||
memset(dest_data, GColorGreen.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpAssignInverted, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__8bit_assign-expect.8bit.pbi"));
|
||||
|
||||
memset(dest_data, GColorGreen.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpOr, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__8bit_assign-expect.8bit.pbi"));
|
||||
|
||||
memset(dest_data, GColorGreen.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpAnd, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__8bit_assign-expect.8bit.pbi"));
|
||||
|
||||
memset(dest_data, GColorGreen.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpClear, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__8bit_assign-expect.8bit.pbi"));
|
||||
|
||||
memset(dest_data, GColorGreen.argb, sizeof(dest_data));
|
||||
// Update source bitmap to be semi-transparent
|
||||
uint8_t *src_bitmap_buff = src_bitmap->addr;
|
||||
for (int index = 0; index < src_bitmap->bounds.size.h * src_bitmap->row_size_bytes; index++) {
|
||||
src_bitmap_buff[index] = src_bitmap_buff[index] & 0xbf;
|
||||
}
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpSet, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__8bit_set-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Test GCompOpTint, from same size to size.
|
||||
// Setup:
|
||||
// - Source is a 10x10 square, white.
|
||||
// - Destination is either a black or white square.
|
||||
// Result:
|
||||
// - When source is transparent or tint color is clear, dest is black.
|
||||
// - When source is opaque, dest is blended blue
|
||||
void test_bitblt__8bit_to_8bit_comptint(void) {
|
||||
GBitmap *src_bitmap = get_gbitmap_from_pbi("test_bitblt__8bit_assign.8bit.pbi");
|
||||
GRect bounds = gbitmap_get_bounds(src_bitmap);
|
||||
|
||||
uint8_t dest_data[bounds.size.w * bounds.size.h];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = bounds.size.w,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = src_bitmap->bounds
|
||||
};
|
||||
|
||||
uint8_t expect_bmp_data[bounds.size.w * bounds.size.h];
|
||||
GBitmap expect_bmp = {
|
||||
.addr = expect_bmp_data,
|
||||
.row_size_bytes = bounds.size.w,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = src_bitmap->bounds
|
||||
};
|
||||
|
||||
// Verify that the compositing mode is correctly applied when the source is opaque and
|
||||
// the tint color is not clear.
|
||||
memset(dest_data, GColorClear.argb, sizeof(dest_data));
|
||||
memset(expect_bmp_data, GColorBlue.argb, sizeof(expect_bmp_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpTint, GColorBlue);
|
||||
cl_assert(gbitmap_eq(&dest_bitmap, &expect_bmp, "test_bitblt__8bit_comptint-expect.8bit.pbi"));
|
||||
|
||||
// Rewrite the destination bitmap to be all black.
|
||||
memset(dest_data, GColorBlack.argb, sizeof(dest_data));
|
||||
|
||||
// Verify that if the tint color is clear, than the source bitmap should not affect the destination bitmap
|
||||
memset(expect_bmp_data, GColorBlack.argb, sizeof(expect_bmp_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpTint, GColorClear);
|
||||
cl_assert(gbitmap_eq(&dest_bitmap, &expect_bmp, "test_bitblt__8bit_comptint_clear-expect.8bit.pbi"));
|
||||
|
||||
// Verify that if the source is transparent, it should not affect the destination bitmap
|
||||
prv_set_opacity(src_bitmap, 0);
|
||||
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpTint, GColorRed);
|
||||
cl_assert(gbitmap_eq(&dest_bitmap, &expect_bmp, "test_bitblt__8bit_comptint_clear-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Tests comptint, multiple tint colors and varying opacity of the source image.
|
||||
// Setup:
|
||||
// - Destination is two 128 by 64 blocks expressing the 64 colors; each row expresses
|
||||
// one of the colors stack vertically.
|
||||
// - Source is a set of 4 by 4 blocks each with a 1 pixel wide vertical strip of a color
|
||||
// with an opacity in [0,3] inclusive.
|
||||
// Result:
|
||||
// - Destination should be blended properly
|
||||
void test_bitblt__8bit_comptint_blend(void) {
|
||||
const uint8_t NUM_COLORS = 64;
|
||||
const uint8_t WIDTH = NUM_COLORS * 2;
|
||||
const uint8_t LEGEND_WIDTH = 4;
|
||||
const uint8_t LEGEND_HEIGHT = 4;
|
||||
const uint8_t OFFSET = 1;
|
||||
const uint8_t HEIGHT = WIDTH;
|
||||
const uint8_t NUM_OPACITIES = 4;
|
||||
const uint8_t TOTAL_WIDTH = LEGEND_WIDTH * 2 + WIDTH;
|
||||
const uint8_t TOTAL_HEIGHT = HEIGHT + OFFSET + LEGEND_HEIGHT * 2;
|
||||
|
||||
uint8_t dest_data[TOTAL_WIDTH * TOTAL_HEIGHT];
|
||||
memset(dest_data, GColorWhite.argb, sizeof(dest_data));
|
||||
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = TOTAL_WIDTH,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = (GRect){GPointZero, (GSize){TOTAL_WIDTH, TOTAL_HEIGHT}}
|
||||
};
|
||||
|
||||
GColor color = (GColor){ .a = 3, .r = 0, .g = 0, .b = 0 };
|
||||
uint8_t src_data[] = { color.argb };
|
||||
GBitmap src_bmp = (GBitmap){
|
||||
.addr = src_data,
|
||||
.row_size_bytes = 1,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = GRect(0, 0, 1, 1)
|
||||
};
|
||||
|
||||
for (uint8_t offset_y = 0; offset_y < NUM_COLORS; offset_y++) {
|
||||
const int16_t offset_x = OFFSET + LEGEND_WIDTH + WIDTH;
|
||||
const int16_t x = LEGEND_WIDTH;
|
||||
|
||||
memset(src_data, color.argb, sizeof(src_data));
|
||||
color = prv_next_color(color);
|
||||
|
||||
const int16_t y_upper = LEGEND_HEIGHT + offset_y;
|
||||
const GRect upper_rect = GRect(x, y_upper, WIDTH, 1);
|
||||
const GRect upper_left_legend_rect = GRect(0, y_upper, 3, 1);
|
||||
const GRect upper_right_legend_rect = GRect(offset_x, y_upper, 3, 1);
|
||||
bitblt_bitmap_into_bitmap_tiled(&dest_bitmap, &src_bmp, upper_rect,
|
||||
GPointZero, GCompOpAssign, GColorWhite);
|
||||
bitblt_bitmap_into_bitmap_tiled(&dest_bitmap, &src_bmp, upper_left_legend_rect,
|
||||
GPointZero, GCompOpAssign, GColorWhite);
|
||||
bitblt_bitmap_into_bitmap_tiled(&dest_bitmap, &src_bmp, upper_right_legend_rect,
|
||||
GPointZero, GCompOpAssign, GColorWhite);
|
||||
|
||||
const int16_t y_lower = y_upper + LEGEND_HEIGHT + NUM_COLORS + OFFSET;
|
||||
const GRect lower_rect = GRect(x, y_lower, WIDTH, 1);
|
||||
const GRect lower_left_legend_rect = GRect(0, y_lower, 3, 1);
|
||||
const GRect lower_right_legend_rect = GRect(offset_x, y_lower, 3, 1);
|
||||
bitblt_bitmap_into_bitmap_tiled(&dest_bitmap, &src_bmp, lower_rect,
|
||||
GPointZero, GCompOpAssign, GColorWhite);
|
||||
bitblt_bitmap_into_bitmap_tiled(&dest_bitmap, &src_bmp, lower_left_legend_rect,
|
||||
GPointZero, GCompOpAssign, GColorWhite);
|
||||
bitblt_bitmap_into_bitmap_tiled(&dest_bitmap, &src_bmp, lower_right_legend_rect,
|
||||
GPointZero, GCompOpAssign, GColorWhite);
|
||||
}
|
||||
|
||||
// RGB value should be discarded later on adding them here might relveal bugs.
|
||||
// .a is the important part
|
||||
GColor8 test_blend_colors[] = {
|
||||
(GColor8){.a = 0, .r = 3, .g = 2, .b = 1},
|
||||
(GColor8){.a = 1, .r = 0, .g = 3, .b = 2},
|
||||
(GColor8){.a = 2, .r = 1, .g = 0, .b = 3},
|
||||
(GColor8){.a = 3, .r = 2, .g = 1, .b = 0}
|
||||
};
|
||||
|
||||
// Test image with four pixels of all our suported alpha values
|
||||
GBitmap test_bmp = (GBitmap){
|
||||
.addr = test_blend_colors,
|
||||
.row_size_bytes = 4,
|
||||
.bounds = GRect(0, 0, 4, 1),
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT
|
||||
};
|
||||
|
||||
for (uint8_t rgb_half = 0; rgb_half < NUM_COLORS / 2; rgb_half++) {
|
||||
const int16_t x = rgb_half * NUM_OPACITIES + LEGEND_WIDTH;
|
||||
const int8_t legend_height = 3;
|
||||
|
||||
// Upper row with destination colors from 0..31
|
||||
const GColor upper_tint_color = (GColor){.argb = 0b11000000 | rgb_half};
|
||||
const int16_t y_upper = LEGEND_HEIGHT;
|
||||
const GRect upper_rect = GRect(x, y_upper, test_bmp.bounds.size.w, NUM_COLORS);
|
||||
const GRect upper_legend_rect = GRect(x, y_upper - LEGEND_HEIGHT, test_bmp.bounds.size.w, legend_height);
|
||||
GColor8 upper_legend[4] = { [0 ... 3] = upper_tint_color };
|
||||
test_bmp.addr = upper_legend;
|
||||
bitblt_bitmap_into_bitmap_tiled(&dest_bitmap, &test_bmp, upper_legend_rect, GPointZero, GCompOpAssign, GColorWhite);
|
||||
test_bmp.addr = test_blend_colors;
|
||||
bitblt_bitmap_into_bitmap_tiled(&dest_bitmap, &test_bmp, upper_rect, GPointZero, GCompOpTint, upper_tint_color);
|
||||
|
||||
// Lower row with destination colors from 31..63
|
||||
const int16_t y_lower = y_upper + NUM_COLORS + OFFSET + LEGEND_HEIGHT;
|
||||
const GRect lower_rect = GRect(x, y_lower, test_bmp.bounds.size.w, NUM_COLORS);
|
||||
const GColor lower_tint_color = (GColor){.argb = 0b11000000 | (rgb_half + (NUM_COLORS / 2))};
|
||||
const GRect lower_legend_rect = GRect(x, y_lower - LEGEND_HEIGHT, test_bmp.bounds.size.w, legend_height);
|
||||
GColor8 lower_legend[4] = { [0 ... 3] = lower_tint_color };
|
||||
test_bmp.addr = lower_legend;
|
||||
bitblt_bitmap_into_bitmap_tiled(&dest_bitmap, &test_bmp, lower_legend_rect, GPointZero, GCompOpAssign, GColorWhite);
|
||||
test_bmp.addr = test_blend_colors;
|
||||
bitblt_bitmap_into_bitmap_tiled(&dest_bitmap, &test_bmp, lower_rect, GPointZero, GCompOpTint, lower_tint_color);
|
||||
}
|
||||
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__8bit_comptint_blend-expect.8bit.pbi"));
|
||||
}
|
||||
|
||||
// Tests assign, clipping, makes sure in bottom right corner.
|
||||
// Setup:
|
||||
// - Source is 10x15, black box ((0, 0), (5, 10)), rest is red.
|
||||
// - Dest is White, 50x50.
|
||||
// - Dest offset is set to 5x10 pixels in bottom right corner.
|
||||
// Result:
|
||||
// - White, with 5x10 black box in bottom right corner.
|
||||
void test_bitblt__8bit_clipping(void) {
|
||||
GBitmap *src_bitmap = get_gbitmap_from_pbi("test_bitblt__8bit_clipping.8bit.pbi");
|
||||
uint8_t dest_data[50*50];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = 50,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { { 0, 0 }, { 50, 50 } }
|
||||
};
|
||||
memset(dest_data, GColorWhite.argb, sizeof(dest_data));
|
||||
|
||||
GPoint dest_offset = { dest_bitmap.bounds.size.w-5, dest_bitmap.bounds.size.h-10 };
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, dest_offset, GCompOpAssign, GColorWhite);
|
||||
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__8bit_clipping-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Test horizontal wrapping when dest_rect wider than src_bitmap.
|
||||
// Setup:
|
||||
// - Source 15 x 10, each row has the folling pattern:
|
||||
// - 2px Red
|
||||
// - 13px Black
|
||||
// - Dest Green 50x50
|
||||
// - Dest rect (17, 10) at (0, 0)
|
||||
// Result:
|
||||
// - 2px Red
|
||||
// - 13px Black
|
||||
// - 2px Red
|
||||
// - Rest Blue
|
||||
void test_bitblt__8bit_wrap_x(void) {
|
||||
GBitmap *src_bitmap = get_gbitmap_from_pbi("test_bitblt__8bit_wrap_x.8bit.pbi");
|
||||
|
||||
uint8_t dest_data[50*50];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = 50,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { { 0, 0 }, { 50, 50 } }
|
||||
};
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
|
||||
// 2 wider than src_bitmap, so 2 columns of red will repeat again.
|
||||
GRect dest_rect = GRect(0, 0, src_bitmap->bounds.size.w + 2, src_bitmap->bounds.size.h);
|
||||
|
||||
bitblt_bitmap_into_bitmap_tiled_8bit_to_8bit(
|
||||
&dest_bitmap, src_bitmap, dest_rect, GPointZero, GCompOpAssign, GColorWhite);
|
||||
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__8bit_wrap_x-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Test vertical wrapping when dest_rect taller than src_bitmap.
|
||||
// Setup:
|
||||
// - Source is 25 x 10
|
||||
// - 4 rows red, 2 rows blue, 4 rows black.
|
||||
// - Dest is Green, 50 x 50
|
||||
// - Dest Rect is 10 x 24 at (0, 0)
|
||||
// Result:
|
||||
// - Pattern repeated vertically x2, plus 4 rows of red.
|
||||
void test_bitblt__8bit_wrap_y(void) {
|
||||
GBitmap *src_bitmap = get_gbitmap_from_pbi("test_bitblt__8bit_wrap_y.8bit.pbi");
|
||||
|
||||
uint8_t dest_data[50*50];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = 50,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { { 0, 0 }, { 50, 50 } }
|
||||
};
|
||||
memset(dest_data, GColorGreen.argb, sizeof(dest_data));
|
||||
GRect dest_rect = GRect(0, 0, src_bitmap->bounds.size.w, src_bitmap->bounds.size.h*2 + 4);
|
||||
|
||||
bitblt_bitmap_into_bitmap_tiled_8bit_to_8bit(
|
||||
&dest_bitmap, src_bitmap, dest_rect, GPointZero, GCompOpAssign, GColorWhite);
|
||||
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__8bit_wrap_y-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Test src_origin_offset, shouldn't see any red in dest_bitmap.
|
||||
// This covers src_origin_offset, y-axis and x-axis wraparound.
|
||||
// Setup:
|
||||
// - Source 25x25, 2 columns, 2 rows red, rest is black.
|
||||
// - Source offset starts at (2, 2)
|
||||
// - Dest is blue, 100x100.
|
||||
// - Dest rect is 50x50 at (0,0).
|
||||
// Result:
|
||||
// - No red in dest_bitmap.
|
||||
// - 50x50 black square at (0,0), rest is blue.
|
||||
void test_bitblt__8bit_src_origin_offset_wrap(void) {
|
||||
GBitmap *src_bitmap = get_gbitmap_from_pbi("test_bitblt__8bit_src_origin_offset_wrap.8bit.pbi");
|
||||
uint8_t dest_data[100*100];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = 100,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { { 0, 0 }, { 100, 100 } }
|
||||
};
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
GRect dest_rect = GRect(0, 0, src_bitmap->bounds.size.w*2, src_bitmap->bounds.size.h*2);
|
||||
GPoint src_origin_offset = { 2, 2 }; // Offset past the 2 red rows
|
||||
|
||||
bitblt_bitmap_into_bitmap_tiled_8bit_to_8bit(
|
||||
&dest_bitmap, src_bitmap, dest_rect, src_origin_offset, GCompOpAssign, GColorWhite);
|
||||
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap,
|
||||
"test_bitblt__8bit_src_origin_offset_wrap-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Test 1-bit to 8-bit blitting
|
||||
///////////////////////////////
|
||||
|
||||
// Setup:
|
||||
// - Source is 25x25.
|
||||
// - Source has alternating white / black lines.
|
||||
// - Dest is Blue, 100x100.
|
||||
// - Dest offset is (0,0) to blit to top left corner.
|
||||
// Result:
|
||||
// - 25x25 alternating black / white lines in top left corner.
|
||||
void test_bitblt__1bit_to_8bit_compop(void) {
|
||||
GBitmap *src_bitmap = get_gbitmap_from_pbi("test_bitblt__1bit_to_8bit_assign.1bit.pbi");
|
||||
uint8_t dest_data[100*100];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = 100,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { { 0, 0 }, { 100, 100 } }
|
||||
};
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_assign-expect.8bit.pbi"));
|
||||
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpAssignInverted, GColorWhite);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_assigninverted-expect.8bit.pbi"));
|
||||
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpOr, GColorWhite);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_or-expect.8bit.pbi"));
|
||||
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpAnd, GColorWhite);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_and-expect.8bit.pbi"));
|
||||
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpClear, GColorWhite);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_clear-expect.8bit.pbi"));
|
||||
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, GPointZero, GCompOpSet, GColorWhite);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_set-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Setup:
|
||||
// - Source is an image of a white cross
|
||||
// - Dest is blue, same size as source
|
||||
// Result:
|
||||
// - Destination should be written with a White cross
|
||||
// Description:
|
||||
// - This test verifies that when the bitmap is 1-bit, we treat white as a
|
||||
// non-transparent color
|
||||
void test_bitblt__1bit_to_8bit_compor(void) {
|
||||
GBitmap *src_bitmap = get_gbitmap_from_pbi("test_bitblt__1bit_to_8bit_compor.1bit.pbi");
|
||||
GRect bounds = gbitmap_get_bounds(src_bitmap);
|
||||
|
||||
cl_assert_equal_i(src_bitmap->info.format, GBitmapFormat1Bit);
|
||||
|
||||
uint8_t dest_data[bounds.size.w * bounds.size.h];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = bounds.size.w,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = src_bitmap->bounds
|
||||
};
|
||||
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, (GPoint){0}, GCompOpOr, GColorLightGray);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_compor-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Setup:
|
||||
// - Source is a 1bit image of a white cross with a black background
|
||||
// Result:
|
||||
// - The image names describe the expected result of each destination color / tint color
|
||||
// combination
|
||||
void test_bitblt__1bit_to_8bit_comptint(void) {
|
||||
GBitmap *src_bitmap = get_gbitmap_from_pbi("test_bitblt__1bit_to_8bit_comptint.1bit.pbi");
|
||||
const GRect bounds = gbitmap_get_bounds(src_bitmap);
|
||||
|
||||
cl_assert_equal_i(src_bitmap->info.format, GBitmapFormat1Bit);
|
||||
|
||||
uint8_t dest_data[bounds.size.w * bounds.size.h];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = bounds.size.w,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = src_bitmap->bounds
|
||||
};
|
||||
|
||||
// Image name descriptions
|
||||
// comptint_<cross_color>_on_<background_color>.8bit
|
||||
|
||||
// Destination White
|
||||
memset(dest_data, GColorWhite.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, (GPoint){0}, GCompOpTint, GColorWhite);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_comptint_white_cross_white_corners-expect.8bit.pbi"));
|
||||
|
||||
memset(dest_data, GColorWhite.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, (GPoint){0}, GCompOpTint, GColorBlack);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_comptint_white_cross_black_corners-expect.8bit.pbi"));
|
||||
|
||||
memset(dest_data, GColorWhite.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, (GPoint){0}, GCompOpTint, GColorLightGray);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_comptint_white_cross_lightgray_corners-expect.8bit.pbi"));
|
||||
|
||||
// Destination Black
|
||||
memset(dest_data, GColorBlack.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, (GPoint){0}, GCompOpTint, GColorWhite);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_comptint_black_cross_white_corners-expect.8bit.pbi"));
|
||||
|
||||
memset(dest_data, GColorBlack.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, (GPoint){0}, GCompOpTint, GColorBlack);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_comptint_black_cross_black_corners-expect.8bit.pbi"));
|
||||
|
||||
memset(dest_data, GColorBlack.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, (GPoint){0}, GCompOpTint, GColorLightGray);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_comptint_black_cross_lightgray_corners-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Setup:
|
||||
// - Source is 25x25.
|
||||
// - Source has alternating white / black lines.
|
||||
// - Dest is Blue, 100x100.
|
||||
// - Dest offset is set to 8x10 clipped in the bottom right corner.
|
||||
// Result:
|
||||
// - There should be an 8x10 alternating black & white lines in the bottom right corner.
|
||||
void test_bitblt__1bit_to_8bit_clipping(void) {
|
||||
GBitmap *src_bitmap = get_gbitmap_from_pbi("test_bitblt__1bit_to_8bit_clipping.1bit.pbi");
|
||||
|
||||
uint8_t dest_data[100*100];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = 100,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { { 0, 0 }, { 100, 100 } }
|
||||
};
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
GPoint dest_offset = { dest_bitmap.bounds.size.w-8, dest_bitmap.bounds.size.h-10 };
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, dest_offset, GCompOpAssign, GColorWhite);
|
||||
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_clipping-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Setup:
|
||||
// - Source is 25 x 25.
|
||||
// - Source rows alternating: 1px black, 1px white.
|
||||
// - Dest is Blue, 100x100
|
||||
// - Dest rect is 50 x 25, at (0, 0)
|
||||
// Result:
|
||||
// - 50x20 of alternating stripes, rest is blue.
|
||||
void test_bitblt__1bit_to_8bit_wrap_x(void) {
|
||||
GBitmap *src_bitmap = get_gbitmap_from_pbi("test_bitblt__1bit_to_8bit_wrap_x.1bit.pbi");
|
||||
|
||||
uint8_t dest_data[100*100];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = 100,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { { 0, 0 }, { 100, 100 } }
|
||||
};
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
GRect dest_rect = GRect(0, 0, src_bitmap->bounds.size.w*2, src_bitmap->bounds.size.h);
|
||||
|
||||
bitblt_bitmap_into_bitmap_tiled_1bit_to_8bit(
|
||||
&dest_bitmap, src_bitmap, dest_rect, GPointZero, GCompOpAssign, GColorWhite);
|
||||
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_8bit_wrap_x-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Setup:
|
||||
// - Source is 40 x 30.
|
||||
// - Source has 2 columns, 4 rows of black, rest is white.
|
||||
// - Dest is all blue.
|
||||
// - Source offset (2, 4) past black.
|
||||
// - Destination is 100 x 100
|
||||
// - Destination rect is size of white portion of source.
|
||||
// Result:
|
||||
// - Blue bitmap with white square at 0,0 of dest_rect size.
|
||||
void test_bitblt__1bit_to_8bit_src_origin_offset(void) {
|
||||
GBitmap *src_bitmap =
|
||||
get_gbitmap_from_pbi("test_bitblt__1bit_to_8bit_src_origin_offset.1bit.pbi");
|
||||
uint8_t dest_data[100*100];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = 100,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { { 0, 0 }, { 100, 100 } }
|
||||
};
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
GRect dest_rect = GRect(0, 0, src_bitmap->bounds.size.w - 2, src_bitmap->bounds.size.h - 4);
|
||||
GPoint src_origin_offset = { 2, 4 }; // Offset past the black
|
||||
|
||||
bitblt_bitmap_into_bitmap_tiled_1bit_to_8bit(
|
||||
&dest_bitmap, src_bitmap, dest_rect, src_origin_offset, GCompOpAssign, GColorWhite);
|
||||
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap,
|
||||
"test_bitblt__1bit_to_8bit_src_origin_offset-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Setup:
|
||||
// - Source is a 10x32 white square.
|
||||
// - Dest is all black.
|
||||
// - Dest origin offset set to 15, 18.
|
||||
// - Dest clipped to 10x10
|
||||
// Result:
|
||||
// - Black bitmap with 10x10 white square starting at (15, 10)
|
||||
void test_bitblt__1bit_to_8bit_dest_origin_offset_clip(void) {
|
||||
GBitmap *src_bitmap =
|
||||
get_gbitmap_from_pbi("test_bitblt__1bit_to_8bit_dest_origin_offset_clip.1bit.pbi");
|
||||
|
||||
uint8_t dest_data[100*100];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = 100,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { { 0, 0 }, { 100, 100 } }
|
||||
};
|
||||
memset(dest_data, GColorBlack.argb, sizeof(dest_data));
|
||||
GRect dest_rect = GRect(15, 10, 10, 10);
|
||||
GPoint src_origin_offset = { 0, 0 };
|
||||
|
||||
bitblt_bitmap_into_bitmap_tiled(&dest_bitmap, src_bitmap, dest_rect, src_origin_offset,
|
||||
GCompOpAssign, GColorWhite);
|
||||
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap,
|
||||
"test_bitblt__1bit_to_8bit_dest_origin_offset_clip-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
|
||||
// Setup:
|
||||
// - Source width is 32 pixels (ie. a word in source)
|
||||
// - Source starts with 2 rows and 4 columns of black pixels.
|
||||
// - Dest is all blue.
|
||||
// - Src origin is set to (4, 2)
|
||||
// - dest origin is set to 10, 25
|
||||
// - dest size is twice the height / width of the source.
|
||||
void test_bitblt__1bit_to_8bit_src_origin_offset_wrap(void) {
|
||||
GBitmap *src_bitmap =
|
||||
get_gbitmap_from_pbi("test_bitblt__1bit_to_8bit_src_origin_offset_wrap.1bit.pbi");
|
||||
|
||||
uint8_t dest_data[100*100];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = 100,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { { 0, 0 }, { 100, 100 } }
|
||||
};
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
GRect dest_rect = GRect(10, 25, src_bitmap->bounds.size.w*2, src_bitmap->bounds.size.h*2);
|
||||
GPoint src_origin_offset = { .x = 4, .y = 2 }; // Offset past the black
|
||||
|
||||
bitblt_bitmap_into_bitmap_tiled(&dest_bitmap, src_bitmap, dest_rect, src_origin_offset,
|
||||
GCompOpAssign, GColorWhite);
|
||||
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap,
|
||||
"test_bitblt__1bit_to_8bit_src_origin_offset_wrap-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Setup:
|
||||
// - Source width is not a multiple of 8 (ie. not byte aligned in source)
|
||||
// - Source starts with 2 rows and 4 columns of black pixels.
|
||||
// - Dest is all blue.
|
||||
// - Src origin is set to (4, 2)
|
||||
// - dest origin is set to 22, 6
|
||||
// - dest size is twice the height / width of the source.
|
||||
void test_bitblt__1bit_to_8bit_src_origin_offset_wrap2(void) {
|
||||
GBitmap *src_bitmap =
|
||||
get_gbitmap_from_pbi("test_bitblt__1bit_to_8bit_src_origin_offset_wrap2.1bit.pbi");
|
||||
|
||||
uint8_t dest_data[100*100];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = 100,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { { 0, 0 }, { 100, 100 } }
|
||||
};
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
GRect dest_rect = GRect(21, 6, src_bitmap->bounds.size.w*2, src_bitmap->bounds.size.h*2);
|
||||
GPoint src_origin_offset = { .x = 4, .y = 2 }; // Offset past the black
|
||||
|
||||
bitblt_bitmap_into_bitmap_tiled(&dest_bitmap, src_bitmap, dest_rect, src_origin_offset,
|
||||
GCompOpAssign, GColorWhite);
|
||||
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap,
|
||||
"test_bitblt__1bit_to_8bit_src_origin_offset_wrap2-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Setup:
|
||||
// - Source has 2 lines of white and the rest black.
|
||||
// - Destination all blue.
|
||||
// - Use gbitmap_init_as_sub_bitmap to get a sub-bitmap that starts at y = 2
|
||||
// Result:
|
||||
// - A 48 x 50 black box starting at y=2 in dest, rest is blue.
|
||||
void test_bitblt__bitmap_into_bitmap_sub_bitmap(void) {
|
||||
GBitmap *src_bitmap = get_gbitmap_from_pbi("test_bitblt__bitmap_into_bitmap_sub_bitmap.8bit.pbi");
|
||||
|
||||
GBitmap cropped_src_bitmap;
|
||||
gbitmap_init_as_sub_bitmap(&cropped_src_bitmap, src_bitmap,
|
||||
(GRect) { { 0, 2 }, src_bitmap->bounds.size });
|
||||
|
||||
uint8_t dest_data[50*50];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = 50,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { { 0, 0 }, { 50, 50 } }
|
||||
};
|
||||
memset(dest_data, GColorBlue.argb, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, &cropped_src_bitmap, (GPoint) { 0, 2 },
|
||||
GCompOpAssign, GColorWhite);
|
||||
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap,
|
||||
"test_bitblt__bitmap_into_bitmap_sub_bitmap-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Test:
|
||||
// - source origin offset
|
||||
// - source bounds origin and size
|
||||
// - wrapping into larger destination
|
||||
// - dest rect not at { 0, 0 }
|
||||
// Setup:
|
||||
// - Source has non-zero bounds origin, { 5, 5 }, outside of this is red.
|
||||
// - Source has non-full bounds size, { 10, 10 }, outside of this is white.
|
||||
// - Source has an origin offset, { 3, 6 }, outside of this is blue.
|
||||
// - Rest is black
|
||||
//
|
||||
// - Destination has non-zero dest_rect origin, { 4, 4 }
|
||||
// - Destination has non-full dest_rect size larger than source, { 10, 10 }
|
||||
// - Destination has full size of { 20, 20 }
|
||||
// Result:
|
||||
// - Expect a 10 x 10 black box at { 4, 4 } that is of size { 10, 10 }
|
||||
// - Remainder should be green.
|
||||
void test_bitblt__8bit_bounds_and_origin_offset(void) {
|
||||
GBitmap *src_bitmap = get_gbitmap_from_pbi("test_bitblt__bounds_and_origin_offset.8bit.pbi");
|
||||
src_bitmap->bounds = (GRect) { { 5, 5}, { 10, 10 } };
|
||||
|
||||
uint8_t dest_data[50*50];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = 50,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { { 0, 0 }, { 50, 50 } }
|
||||
};
|
||||
memset(dest_data, GColorGreen.argb, sizeof(dest_data));
|
||||
const GRect dest_rect = GRect(4, 4, 10, 10);
|
||||
const GPoint src_origin_offset = GPoint(3, 6);
|
||||
bitblt_bitmap_into_bitmap_tiled_8bit_to_8bit(&dest_bitmap, src_bitmap, dest_rect,
|
||||
src_origin_offset, GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__bounds_and_origin_offset-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Setup:
|
||||
// - Source is a 1bit image of a white cross with a black background
|
||||
// Result:
|
||||
// - The image names describe the expected result of each destination color / tint color
|
||||
// combination
|
||||
void test_bitblt__1bit_to_1bit_comptint(void) {
|
||||
GBitmap *src_bitmap = get_gbitmap_from_pbi("test_bitblt__1bit_to_1bit_comptint.1bit.pbi");
|
||||
const GRect bounds = gbitmap_get_bounds(src_bitmap);
|
||||
|
||||
cl_assert_equal_i(src_bitmap->info.format, GBitmapFormat1Bit);
|
||||
|
||||
uint8_t dest_data[bounds.size.w * bounds.size.h];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = src_bitmap->row_size_bytes,
|
||||
.info.format = GBitmapFormat1Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = src_bitmap->bounds
|
||||
};
|
||||
|
||||
// Image name descriptions
|
||||
// comptint_<cross_color>_on_<background_color>.1bit
|
||||
|
||||
// Destination White
|
||||
memset(dest_data, 0b11111111, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, (GPoint){0}, GCompOpTint, GColorClear);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_1bit_comptint_white_on_white-expect.1bit.pbi"));
|
||||
|
||||
memset(dest_data, 0b11111111, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, (GPoint){0}, GCompOpTint, GColorWhite);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_1bit_comptint_white_on_white-expect.1bit.pbi"));
|
||||
|
||||
memset(dest_data, 0b11111111, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, (GPoint){0}, GCompOpTint, GColorBlack);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_1bit_comptint_black_on_white-expect.1bit.pbi"));
|
||||
|
||||
// Destination Black
|
||||
memset(dest_data, 0b00000000, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, (GPoint){0}, GCompOpTint, GColorClear);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_1bit_comptint_black_on_black-expect.1bit.pbi"));
|
||||
|
||||
memset(dest_data, 0b00000000, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, (GPoint){0}, GCompOpTint, GColorWhite);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_1bit_comptint_white_on_black-expect.1bit.pbi"));
|
||||
|
||||
memset(dest_data, 0b00000000, sizeof(dest_data));
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, src_bitmap, (GPoint){0}, GCompOpTint, GColorBlack);
|
||||
cl_check(gbitmap_pbi_eq(&dest_bitmap, "test_bitblt__1bit_to_1bit_comptint_black_on_black-expect.1bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Test:
|
||||
// - source origin offset
|
||||
// - source bounds origin and size
|
||||
// - Source bounds origin is beyond (32, y) to pass word boundary.
|
||||
// - Source origin offset is beyond (32, y) to pass another word boundary.
|
||||
// - wrapping into larger destination
|
||||
// - dest rect not at { 0, 0 }
|
||||
// Setup:
|
||||
// - Dest rect at {4, 4}, repeat twice and a bit: {140, 55}
|
||||
// - Source bounds origin at {37, 3), size {63, 23}
|
||||
// - Source origin offset (39, 11)
|
||||
void DISABLED_test_bitblt__1bit_to_8bit_bounds_and_origin_offset(void) {
|
||||
GBitmap *src_bitmap =
|
||||
get_gbitmap_from_pbi("test_bitblt__1bit_to_8bit_bounds_and_origin_offset.1bit.pbi");
|
||||
|
||||
uint8_t dest_data[144*168];
|
||||
GBitmap dest_bitmap = {
|
||||
.addr = dest_data,
|
||||
.row_size_bytes = 144,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { { 0, 0 }, { 144, 168 } }
|
||||
};
|
||||
memset(dest_data, GColorGreen.argb, sizeof(dest_data));
|
||||
const GRect dest_rect = GRect(4, 4, 140, 55);
|
||||
const GPoint src_origin_offset = GPoint(39, 11);
|
||||
src_bitmap->bounds = (GRect) { { 37, 3}, { 63, 23 } };
|
||||
|
||||
bitblt_bitmap_into_bitmap_tiled_1bit_to_8bit(&dest_bitmap, src_bitmap, dest_rect,
|
||||
src_origin_offset, GCompOpAssign, GColorWhite);
|
||||
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap,
|
||||
"test_bitblt__1bit_to_8bit_bounds_and_origin_offset-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// This test is the same as test_bitblt__1bit_to_1bit_bounds_and_origin_offset, except it is for
|
||||
// 1-bit to 1-bit.
|
||||
// FIXME: This is a known legacy-broken case in 1-bit bitblt. See PBL-14671 for more information
|
||||
void DISABLED__test_bitblt__1bit_to_1bit_bounds_and_origin_offset(void) {
|
||||
GBitmap *src_bitmap =
|
||||
get_gbitmap_from_pbi("test_bitblt__1bit_to_1bit_bounds_and_origin_offset.1bit.pbi");
|
||||
|
||||
uint8_t dest_data[src_bitmap->row_size_bytes * src_bitmap->bounds.size.h];
|
||||
GBitmap dest_bitmap = *src_bitmap;
|
||||
dest_bitmap.addr = dest_data;
|
||||
dest_bitmap.bounds = src_bitmap->bounds;
|
||||
memset(dest_data, 0xff, sizeof(dest_data));
|
||||
|
||||
const GRect dest_rect = GRect(4, 4, 140, 55);
|
||||
const GPoint src_origin_offset = GPoint(39, 11);
|
||||
|
||||
src_bitmap->bounds = (GRect) { { 37, 3}, { 63, 23 } };
|
||||
bitblt_bitmap_into_bitmap_tiled_1bit_to_1bit(&dest_bitmap, src_bitmap, dest_rect,
|
||||
src_origin_offset, GCompOpAssign, GColorWhite);
|
||||
|
||||
cl_assert(gbitmap_pbi_eq(&dest_bitmap,
|
||||
"test_bitblt__1bit_to_1bit_bounds_and_origin_offset-expect.8bit.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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 "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/bitblt.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
#include "8bit/test_framebuffer.h"
|
||||
#else
|
||||
Static_assert(false, "These tests are only for color displays");
|
||||
#endif
|
||||
|
||||
static GContext *ctx;
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_bitblt_circular__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
ctx = malloc(sizeof(GContext));
|
||||
test_graphics_context_init(ctx, fb);
|
||||
framebuffer_clear(fb);
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_bitblt_circular__cleanup(void) {
|
||||
free(fb);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
// Reference PNGs reside in "tests/test_images/"
|
||||
// and are created at build time, with the test PBI file generated
|
||||
// by bitmapgen.py from the reference PNG copied to TEST_IMAGES_PATH
|
||||
// covers 1-bit b&w image
|
||||
// covers 1,2,4,8 bit palettized
|
||||
|
||||
// Tests 1-bit black&white PBI loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_bitblt_circular__color_1_bit_bw(void) {
|
||||
const char test_file[] = "test_bitblt_circular__color_1_bit_bw.1bit.pbi";
|
||||
GBitmap *bitmap = setup_pbi_test(test_file);
|
||||
GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx);
|
||||
native_framebuffer->info.format = GBitmapFormat8BitCircular;
|
||||
cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944);
|
||||
bitblt_bitmap_into_bitmap(native_framebuffer, bitmap, GPointZero, GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(native_framebuffer, test_file));
|
||||
}
|
||||
|
||||
// Tests 1-bit red&white palettized PBI loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_bitblt_circular__color_1_bit(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE);
|
||||
GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx);
|
||||
cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular);
|
||||
cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944);
|
||||
bitblt_bitmap_into_bitmap(native_framebuffer, bitmap, GPointZero, GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
// Tests 2-bit palettized PBI loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_bitblt_circular__color_2_bit(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE);
|
||||
GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx);
|
||||
cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular);
|
||||
cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944);
|
||||
bitblt_bitmap_into_bitmap(native_framebuffer, bitmap, GPointZero, GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
// Tests 4-bit bitblt palettized to circular display buffer
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_bitblt_circular__color_4_bit(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE);
|
||||
GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx);
|
||||
cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular);
|
||||
cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944);
|
||||
bitblt_bitmap_into_bitmap(native_framebuffer, bitmap, GPointZero, GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
// Tests 8-bit bitblt to circular display buffer
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_bitblt_circular__color_8_bit(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE);
|
||||
GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx);
|
||||
cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular);
|
||||
cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944);
|
||||
bitblt_bitmap_into_bitmap(native_framebuffer, bitmap, GPointZero, GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
// Tests 8-bit bitblt tiling support to circular display buffer
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_bitblt_circular__color_8_bit_tiling(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_NAMED_PNG_FILE("test_bitblt_circular__tile"));
|
||||
cl_assert(bitmap->info.format == GBitmapFormat8Bit);
|
||||
GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx);
|
||||
cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular);
|
||||
cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944);
|
||||
bitblt_bitmap_into_bitmap_tiled(
|
||||
native_framebuffer, bitmap, native_framebuffer->bounds,
|
||||
GPointZero, GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
// Tests palettized bitblt non-power-of-two tiling support to circular display buffer
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_bitblt_circular__color_8_bit_tiling_palettized(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_NAMED_PNG_FILE("test_bitblt_circular__tile_palettized"));
|
||||
cl_assert(bitmap->info.format == GBitmapFormat2BitPalette);
|
||||
GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx);
|
||||
cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular);
|
||||
cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944);
|
||||
bitblt_bitmap_into_bitmap_tiled(
|
||||
native_framebuffer, bitmap, native_framebuffer->bounds,
|
||||
GPointZero, GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_bitblt_circular__8_bit_converted_circular(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_NAMED_PNG_FILE("test_bitblt_circular__spiral"));
|
||||
cl_assert(bitmap->info.format == GBitmapFormat8Bit);
|
||||
|
||||
// Convert input PNG from rectangular to circular
|
||||
gbitmap_8bit_to_8bit_circular(bitmap);
|
||||
cl_assert(bitmap->info.format == GBitmapFormat8BitCircular);
|
||||
|
||||
GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx);
|
||||
GRect framebuffer_bounds = gbitmap_get_bounds(native_framebuffer);
|
||||
cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular);
|
||||
cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944);
|
||||
|
||||
// Set screen to black to match empty region with test image
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_fill_rect(ctx, &framebuffer_bounds);
|
||||
|
||||
bitblt_bitmap_into_bitmap(native_framebuffer, bitmap, GPointZero, GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_bitblt_circular__8_bit_converted_circular_offset(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_NAMED_PNG_FILE("test_bitblt_circular__spiral"));
|
||||
cl_assert(bitmap->info.format == GBitmapFormat8Bit);
|
||||
|
||||
// Convert input PNG from rectangular to circular
|
||||
gbitmap_8bit_to_8bit_circular(bitmap);
|
||||
cl_assert(bitmap->info.format == GBitmapFormat8BitCircular);
|
||||
|
||||
GBitmap *native_framebuffer = graphics_context_get_bitmap(ctx);
|
||||
GRect framebuffer_bounds = gbitmap_get_bounds(native_framebuffer);
|
||||
cl_assert(native_framebuffer->info.format == GBitmapFormat8BitCircular);
|
||||
cl_assert(DISPLAY_FRAMEBUFFER_BYTES == 25944);
|
||||
|
||||
// Set screen to black to match empty region with test image
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_fill_rect(ctx, &framebuffer_bounds);
|
||||
|
||||
// Shift the output by non-power-of-2 values for testing
|
||||
bitblt_bitmap_into_bitmap(native_framebuffer, bitmap, GPoint(33, 10), GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(native_framebuffer, TEST_PBI_FILE));
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/bitblt.h"
|
||||
#include "applib/graphics/bitblt_private.h"
|
||||
#include "applib/graphics/8_bit/framebuffer.h"
|
||||
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "stubs_compiled_with_legacy2_sdk.h"
|
||||
#include "test_graphics.h"
|
||||
|
||||
// Setup
|
||||
////////////////////////////////////
|
||||
static GContext ctx;
|
||||
static FrameBuffer framebuffer;
|
||||
static uint8_t dest_bitmap_data[FRAMEBUFFER_SIZE_BYTES];
|
||||
static GBitmap dest_bitmap = {
|
||||
.addr = dest_bitmap_data,
|
||||
.row_size_bytes = FRAMEBUFFER_BYTES_PER_ROW,
|
||||
.info.is_bitmap_heap_allocated = false,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = {
|
||||
.size = {
|
||||
.w = DISP_COLS,
|
||||
.h = DISP_ROWS
|
||||
},
|
||||
.origin = { 0, 0 },
|
||||
},
|
||||
};
|
||||
|
||||
// Utilities
|
||||
////////////////////////////////////
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
extern GColor get_bitmap_color(GBitmap *bmp, int x, int y);
|
||||
#endif
|
||||
|
||||
// @param color_index the color index (the value) to put into the bitmap at (x, y).
|
||||
// @param bpp Bits per pixel.
|
||||
// @para line_stride how many bytes per line in the bitmap data.
|
||||
void packed_pixel_set(uint8_t *buf, uint8_t color_index, int16_t x, int16_t y,
|
||||
uint8_t bpp, int16_t line_stride) {
|
||||
const uint8_t ppb = 8 / bpp;
|
||||
uint8_t idx = y*line_stride + (x/(ppb));
|
||||
const uint8_t shift = (8 - bpp) - bpp * (x % ppb);
|
||||
const uint8_t mask = ~(((1 << bpp) - 1) << shift);
|
||||
buf[idx] = buf[idx] & mask;
|
||||
buf[idx] |= ((color_index & ((1 << bpp) - 1)) << shift);
|
||||
}
|
||||
|
||||
static bool prv_check_source_stripe_blit(const uint8_t *data,
|
||||
const GBitmap *src_bmp, GColor surround_color) {
|
||||
for (uint8_t y = 0; y < (FRAMEBUFFER_SIZE_BYTES / FRAMEBUFFER_BYTES_PER_ROW); ++y) {
|
||||
for (uint8_t x = 0; x < FRAMEBUFFER_BYTES_PER_ROW; ++x) {
|
||||
uint8_t color = data[y*FRAMEBUFFER_BYTES_PER_ROW + x];
|
||||
if (y < src_bmp->bounds.size.h && x < src_bmp->bounds.size.w) {
|
||||
if (color != src_bmp->palette[x].argb) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (color != surround_color.argb) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
// setup and teardown
|
||||
void test_bitblt_palette__initialize(void) {
|
||||
framebuffer_init(&framebuffer, &(GSize) { DISP_COLS, DISP_ROWS });
|
||||
test_graphics_context_init(&ctx, &framebuffer);
|
||||
}
|
||||
|
||||
void test_bitblt_palette__cleanup(void) {
|
||||
}
|
||||
|
||||
void test_bitblt_palette__1Bit_color(void) {
|
||||
const int BITS_PER_PIXEL = 1;
|
||||
const int PIXELS_PER_BYTE = (8 / BITS_PER_PIXEL);
|
||||
const int WIDTH = 2;
|
||||
const int HEIGHT = 2;
|
||||
const int ROW_STRIDE = (WIDTH + (PIXELS_PER_BYTE - 1)) / PIXELS_PER_BYTE;
|
||||
|
||||
uint8_t s_data[ROW_STRIDE * HEIGHT];
|
||||
GColor s_palette[1 << 1] = {
|
||||
GColorMelon, GColorIcterine
|
||||
};
|
||||
cl_assert(sizeof(s_palette) == (1 << BITS_PER_PIXEL));
|
||||
GBitmap s_bmp = (GBitmap) {
|
||||
.addr = s_data,
|
||||
.row_size_bytes = ROW_STRIDE,
|
||||
.info.format = GBitmapFormat1BitPalette,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { .size = { WIDTH, HEIGHT } },
|
||||
.palette = s_palette,
|
||||
};
|
||||
memset(s_data, 0, sizeof(s_data));
|
||||
|
||||
for (int y = 0; y < HEIGHT; ++y) {
|
||||
for (int x = 0; x < WIDTH; ++x) {
|
||||
packed_pixel_set(s_data, x /* color_index */, x, y, BITS_PER_PIXEL, s_bmp.row_size_bytes);
|
||||
}
|
||||
}
|
||||
memset(dest_bitmap_data, GColorWhite.argb, sizeof(dest_bitmap_data));
|
||||
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
char print_buf[20];
|
||||
for (int y = 0; y < HEIGHT; ++y) {
|
||||
for (int x = 0; x < WIDTH; ++x) {
|
||||
snprintf(print_buf, sizeof(print_buf), "Failed index = %d, %d", x, y);
|
||||
cl_check_(gcolor_equal(get_bitmap_color(&s_bmp, x, y), s_palette[x]), print_buf);
|
||||
cl_check_(gcolor_equal(get_bitmap_color(&s_bmp, x, y), s_palette[x]), print_buf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_bitblt_palette__4Bit_assign(void) {
|
||||
const int BITS_PER_PIXEL = 4;
|
||||
const int PIXELS_PER_BYTE = (8 / BITS_PER_PIXEL);
|
||||
const int WIDTH = 16;
|
||||
const int HEIGHT = 16;
|
||||
const int ROW_STRIDE = (WIDTH + (PIXELS_PER_BYTE - 1)) / PIXELS_PER_BYTE;
|
||||
|
||||
uint8_t s_data[ROW_STRIDE * HEIGHT];
|
||||
GColor s_palette[1 << 4] = {
|
||||
GColorMelon, GColorIcterine, GColorYellow, GColorSunsetOrange,
|
||||
GColorScreaminGreen, GColorMagenta, GColorOrange, GColorFolly,
|
||||
GColorLimerick, GColorPictonBlue, GColorPurple, GColorCadetBlue,
|
||||
GColorMalachite, GColorGreen, GColorIndigo, GColorVividCerulean
|
||||
};
|
||||
cl_assert(sizeof(s_palette) == (1 << BITS_PER_PIXEL));
|
||||
GBitmap s_bmp = (GBitmap) {
|
||||
.addr = s_data,
|
||||
.row_size_bytes = ROW_STRIDE,
|
||||
.info.format = GBitmapFormat4BitPalette,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { .size = { WIDTH, HEIGHT } },
|
||||
.palette = s_palette,
|
||||
};
|
||||
memset(s_data, 0, sizeof(s_data));
|
||||
|
||||
for (int y = 0; y < HEIGHT; ++y) {
|
||||
for (int x = 0; x < WIDTH; ++x) {
|
||||
packed_pixel_set(s_data, x /* color_index */, x, y, BITS_PER_PIXEL, s_bmp.row_size_bytes);
|
||||
}
|
||||
}
|
||||
memset(dest_bitmap_data, GColorWhite.argb, sizeof(dest_bitmap_data));
|
||||
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
char print_buf[20];
|
||||
for (int y = 0; y < HEIGHT; ++y) {
|
||||
for (int x = 0; x < WIDTH; ++x) {
|
||||
snprintf(print_buf, sizeof(print_buf), "Failed index = %d, %d", x, y);
|
||||
cl_check_(gcolor_equal(get_bitmap_color(&s_bmp, x, y), s_palette[x]), print_buf);
|
||||
cl_check_(gcolor_equal(get_bitmap_color(&s_bmp, x, y), s_palette[x]), print_buf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, &s_bmp, GPointZero, GCompOpAssign, GColorWhite);
|
||||
|
||||
cl_assert(prv_check_source_stripe_blit(dest_bitmap_data, &s_bmp, GColorWhite));
|
||||
}
|
||||
|
||||
static void prv_opaque_2bit_simple(GCompOp compositing_mode) {
|
||||
const int BITS_PER_PIXEL = 2;
|
||||
const int PIXELS_PER_BYTE = (8 / BITS_PER_PIXEL);
|
||||
const int WIDTH = 4;
|
||||
const int HEIGHT = 4;
|
||||
const int ROW_STRIDE = (WIDTH + (PIXELS_PER_BYTE - 1)) / PIXELS_PER_BYTE;
|
||||
|
||||
uint8_t s_data[ROW_STRIDE * HEIGHT];
|
||||
GColor s_palette[] = {
|
||||
GColorRed, GColorWhite, GColorBlack, GColorBlue
|
||||
};
|
||||
|
||||
cl_assert(sizeof(s_palette) == (1 << BITS_PER_PIXEL));
|
||||
|
||||
GBitmap s_bmp = (GBitmap) {
|
||||
.addr = s_data,
|
||||
.row_size_bytes = ROW_STRIDE,
|
||||
.info.format = GBitmapFormat2BitPalette,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { .size = { WIDTH, HEIGHT } },
|
||||
.palette = s_palette,
|
||||
};
|
||||
|
||||
memset(s_data, 0, sizeof(s_data));
|
||||
|
||||
for (int y = 0; y < HEIGHT; ++y) {
|
||||
for (int x = 0; x < WIDTH; ++x) {
|
||||
packed_pixel_set(s_data, x /* color_index */, x, y, BITS_PER_PIXEL, s_bmp.row_size_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
memset(dest_bitmap_data, GColorWhite.argb, sizeof(dest_bitmap_data));
|
||||
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
char print_buf[20];
|
||||
for (int y = 0; y < HEIGHT; ++y) {
|
||||
for (int x = 0; x < WIDTH; ++x) {
|
||||
snprintf(print_buf, sizeof(print_buf), "Failed index = %d, %d", x, y);
|
||||
cl_check_(gcolor_equal(get_bitmap_color(&s_bmp, x, y), s_palette[x]), print_buf);
|
||||
cl_check_(gcolor_equal(get_bitmap_color(&s_bmp, x, y), s_palette[x]), print_buf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, &s_bmp, GPointZero, compositing_mode, GColorWhite);
|
||||
|
||||
switch (compositing_mode) {
|
||||
case GCompOpTint: {
|
||||
// Since this is opaque, the tint_color will be used for the RGB value of the destination
|
||||
// image, so we want to use that as our palette.
|
||||
for (uint8_t idx = 0; idx < WIDTH; idx++) {
|
||||
if (s_palette[idx].a == 3) {
|
||||
s_palette[idx] = GColorWhite;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
cl_assert(prv_check_source_stripe_blit(dest_bitmap_data, &s_bmp, GColorWhite));
|
||||
}
|
||||
|
||||
static void prv_4bit_simple(GCompOp compositing_mode, GColor color, bool transparent) {
|
||||
const int BITS_PER_PIXEL = 4;
|
||||
const int PIXELS_PER_BYTE = (8 / BITS_PER_PIXEL);
|
||||
const int WIDTH = 16;
|
||||
const int HEIGHT = 16;
|
||||
const int ROW_STRIDE = (WIDTH + (PIXELS_PER_BYTE - 1)) / PIXELS_PER_BYTE;
|
||||
|
||||
uint8_t s_data[ROW_STRIDE * HEIGHT];
|
||||
GColor s_palette[] = {
|
||||
GColorMelon, GColorIcterine, GColorYellow, GColorSunsetOrange,
|
||||
GColorScreaminGreen, GColorMagenta, GColorOrange, GColorFolly,
|
||||
GColorLimerick, GColorPictonBlue, GColorPurple, GColorCadetBlue,
|
||||
GColorMalachite, GColorGreen, GColorIndigo, GColorVividCerulean
|
||||
};
|
||||
|
||||
cl_assert(sizeof(s_palette) == (1 << BITS_PER_PIXEL));
|
||||
|
||||
if (transparent) {
|
||||
// Make all of those colors fully transparent
|
||||
for (int i = 0; i < sizeof(s_palette); ++i) {
|
||||
s_palette[i].a = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GBitmap s_bmp = (GBitmap) {
|
||||
.addr = s_data,
|
||||
.row_size_bytes = ROW_STRIDE,
|
||||
.info.format = GBitmapFormat4BitPalette,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = { .size = { WIDTH, HEIGHT } },
|
||||
.palette = s_palette,
|
||||
};
|
||||
|
||||
memset(s_data, 0, sizeof(s_data));
|
||||
|
||||
for (int y = 0; y < HEIGHT; ++y) {
|
||||
for (int x = 0; x < WIDTH; ++x) {
|
||||
packed_pixel_set(s_data, x /* color_index */, x, y, BITS_PER_PIXEL, s_bmp.row_size_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
memset(dest_bitmap_data, GColorWhite.argb, sizeof(dest_bitmap_data));
|
||||
|
||||
bitblt_bitmap_into_bitmap(&dest_bitmap, &s_bmp, GPointZero, compositing_mode, color);
|
||||
|
||||
switch (compositing_mode) {
|
||||
case GCompOpSet: {
|
||||
if (transparent) {
|
||||
memset(s_palette, color.argb, sizeof(s_palette));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GCompOpAssign:
|
||||
break;
|
||||
case GCompOpTint: {
|
||||
for (uint8_t idx = 0; idx < WIDTH; idx++) {
|
||||
if (s_palette[idx].a == 3 || transparent) {
|
||||
s_palette[idx] = color;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
cl_assert(prv_check_source_stripe_blit(dest_bitmap_data, &s_bmp, GColorWhite));
|
||||
}
|
||||
|
||||
void test_bitblt_palette__2Bit_assign_opaque(void) {
|
||||
prv_opaque_2bit_simple(GCompOpAssign);
|
||||
}
|
||||
|
||||
void test_bitblt_palette__2Bit_set_opaque(void) {
|
||||
prv_opaque_2bit_simple(GCompOpSet);
|
||||
}
|
||||
|
||||
void test_bitblt_palette__2Bit_comptint_opaque(void) {
|
||||
prv_opaque_2bit_simple(GCompOpTint);
|
||||
}
|
||||
|
||||
void test_bitblt_palette__4Bit_assign_opaque(void) {
|
||||
prv_4bit_simple(GCompOpAssign, GColorWhite, false /* opaque */);
|
||||
}
|
||||
|
||||
void test_bitblt_palette__4Bit_assign_transparent(void) {
|
||||
prv_4bit_simple(GCompOpAssign, GColorWhite, true /* transparent */);
|
||||
}
|
||||
|
||||
void test_bitblt_palette__4Bit_set_opaque(void) {
|
||||
prv_4bit_simple(GCompOpSet, GColorWhite, false /* opaque */);
|
||||
}
|
||||
|
||||
void test_bitblt_palette__4Bit_set_transparent(void) {
|
||||
prv_4bit_simple(GCompOpSet, GColorWhite, true /* transparent */);
|
||||
}
|
||||
|
||||
void test_bitblt_palette__4Bit_comptint_opaque(void) {
|
||||
prv_4bit_simple(GCompOpTint, GColorBlack, false /* opaque */);
|
||||
prv_4bit_simple(GCompOpTint, GColorBlue, false /* opaque */);
|
||||
}
|
||||
|
||||
void test_bitblt_palette__4Bit_comptint_transparent(void) {
|
||||
prv_4bit_simple(GCompOpTint, GColorWhite, true /* transparent */);
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/graphics_private.h"
|
||||
#include "applib/graphics/bitblt.h"
|
||||
#include "applib/graphics/bitblt_private.h"
|
||||
#include "applib/graphics/1_bit/framebuffer.h"
|
||||
#include "applib/graphics/gtypes.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "test_graphics.h"
|
||||
|
||||
extern void prv_apply_tint_color(GColor *color, GColor tint_color);
|
||||
|
||||
static uint8_t s_dest_data[100 * 100];
|
||||
static GBitmap s_dest_bitmap = {
|
||||
.addr = s_dest_data,
|
||||
.row_size_bytes = 16,
|
||||
.info.format = GBitmapFormat1Bit,
|
||||
.info.version = GBITMAP_VERSION_CURRENT,
|
||||
.bounds = GRect(0, 0, 100, 100)
|
||||
};
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
// setup and teardown
|
||||
void test_bitblt_palette_1bit__initialize(void) {
|
||||
// Set dest bitmap to white
|
||||
memset(s_dest_data, 0b11111111, sizeof(s_dest_data));
|
||||
}
|
||||
|
||||
void test_bitblt_palette_1bit__cleanup(void) {
|
||||
}
|
||||
|
||||
// Test images reside in "tests/fw/graphics/test_images/".
|
||||
// The wscript will convert them from PNGs in that directory to PBIs in the build directory.
|
||||
|
||||
// Tests assign, from same size to same size.
|
||||
// Setup:
|
||||
// - Source is 50x50, the left half is semi transparent orange and the right half is orange.
|
||||
// - Dest is 100x100, white.
|
||||
// Result:
|
||||
// - All dithered gray.
|
||||
void test_bitblt_palette_1bit__1bit_palette_to_1bit_assign(void) {
|
||||
GBitmap *src_bitmap =
|
||||
get_gbitmap_from_pbi("test_bitblt_palette_1bit__1bit_palette_to_1bit.pbi");
|
||||
|
||||
bitblt_bitmap_into_bitmap(&s_dest_bitmap, src_bitmap, GPointZero, GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(&s_dest_bitmap,
|
||||
"test_bitblt_palette_1bit__1bit_palette_to_1bit_assign-expect.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Test images reside in "tests/fw/graphics/test_images/".
|
||||
// The wscript will convert them from PNGs in that directory to PBIs in the build directory.
|
||||
|
||||
// Tests assign, from same size to same size.
|
||||
// Setup:
|
||||
// - Source is 50x50, the left half is semi transparent orange and the right half is orange.
|
||||
// - Dest is 100x100, white.
|
||||
// Result:
|
||||
// - The left half will be white and the right half will be dithered gray.
|
||||
void test_bitblt_palette_1bit__1bit_palette_to_1bit_set(void) {
|
||||
GBitmap *src_bitmap =
|
||||
get_gbitmap_from_pbi("test_bitblt_palette_1bit__1bit_palette_to_1bit.pbi");
|
||||
|
||||
bitblt_bitmap_into_bitmap(&s_dest_bitmap, src_bitmap, GPointZero, GCompOpSet, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(&s_dest_bitmap,
|
||||
"test_bitblt_palette_1bit__1bit_palette_to_1bit_set-expect.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Tests assign, from same size to same size.
|
||||
// Setup:
|
||||
// - Source is 50x50, alternating lines between orange and blue for the top half.
|
||||
// The bottom half is a diagonal orange line over blue.
|
||||
// The left half is semi-transparent and the right half is completely opaque
|
||||
// - Dest is 100x100, white.
|
||||
// Result:
|
||||
// - The top half will be alternating between dithered gray and black lines
|
||||
// The bottom half consists of a diagonal white line on a black background
|
||||
void test_bitblt_palette_1bit__2bit_palette_to_1bit_assign(void) {
|
||||
GBitmap *src_bitmap =
|
||||
get_gbitmap_from_pbi("test_bitblt_palette_1bit__2bit_palette_to_1bit.pbi");
|
||||
|
||||
bitblt_bitmap_into_bitmap(&s_dest_bitmap, src_bitmap, GPointZero, GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(&s_dest_bitmap,
|
||||
"test_bitblt_palette_1bit__2bit_palette_to_1bit-expect.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Tests assign, from same size to same size.
|
||||
// Setup:
|
||||
// - Source is 50x50, alternating lines between orange and blue for the top half.
|
||||
// The bottom half is a diagonal orange line over blue.
|
||||
// The left half is semi-transparent and the right half is completely opaque
|
||||
// - Dest is 100x100, white.
|
||||
// Result:
|
||||
// - The top right half will be alternating between dithered gray and black lines
|
||||
// The bottom right half consists of a diagonal white line on a black background
|
||||
// The left half will be completely white
|
||||
void test_bitblt_palette_1bit__2bit_palette_to_1bit_set(void) {
|
||||
GBitmap *src_bitmap =
|
||||
get_gbitmap_from_pbi("test_bitblt_palette_1bit__2bit_palette_to_1bit.pbi");
|
||||
|
||||
bitblt_bitmap_into_bitmap(&s_dest_bitmap, src_bitmap, GPointZero, GCompOpSet, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(&s_dest_bitmap,
|
||||
"test_bitblt_palette_1bit__2bit_palette_to_1bit_set-expect.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Tests assign, from same size to same size.
|
||||
// Setup:
|
||||
// - Source is 50x50, alternating lines between orange and blue for the top half.
|
||||
// The bottom half is a diagonal orange line over blue.
|
||||
// The left half is semi-transparent and the right half is completely opaque
|
||||
// - Dest is 50x50, white.
|
||||
// Result:
|
||||
// - The image desribed will be tiled in each of the four corners
|
||||
// The top right half will be alternating between dithered gray and black lines
|
||||
// The bottom right half consists of a diagonal white line on a black background
|
||||
// The left half will be completely white
|
||||
void test_bitblt_palette_1bit__2bit_palette_to_1bit_wrap(void) {
|
||||
GBitmap *src_bitmap =
|
||||
get_gbitmap_from_pbi("test_bitblt_palette_1bit__2bit_palette_to_1bit.pbi");
|
||||
|
||||
bitblt_bitmap_into_bitmap_tiled(&s_dest_bitmap, src_bitmap, s_dest_bitmap.bounds, GPointZero, GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(&s_dest_bitmap,
|
||||
"test_bitblt_palette_1bit__2bit_palette_to_1bit_wrap-expect.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
// Tests assign, from same size to same size.
|
||||
// Setup:
|
||||
// - Source is 50x50, alternating lines between orange and blue for the top half.
|
||||
// The bottom half is a diagonal orange line over blue.
|
||||
// The left half is semi-transparent and the right half is completely opaque
|
||||
// - Dest is 100x100, white.
|
||||
// Result:
|
||||
// - The image described below will be at an offset of 20, 20
|
||||
// The top right half will be alternating between dithered gray and black lines
|
||||
// The bottom right half consists of a diagonal white line on a black background
|
||||
// The left half will be completely white
|
||||
void test_bitblt_palette_1bit__2bit_palette_to_1bit_offest(void) {
|
||||
GBitmap *src_bitmap =
|
||||
get_gbitmap_from_pbi("test_bitblt_palette_1bit__2bit_palette_to_1bit.pbi");
|
||||
|
||||
bitblt_bitmap_into_bitmap(&s_dest_bitmap, src_bitmap, GPoint(20,20), GCompOpAssign, GColorWhite);
|
||||
cl_assert(gbitmap_pbi_eq(&s_dest_bitmap,
|
||||
"test_bitblt_palette_1bit__2bit_palette_to_1bit_offest-expect.pbi"));
|
||||
|
||||
gbitmap_destroy(src_bitmap);
|
||||
}
|
||||
|
||||
void test_bitblt_palette_1bit__get_1bit_graphics_grayscale_pattern(void) {
|
||||
cl_assert_equal_i(graphics_private_get_1bit_grayscale_pattern(GColorWhite, 0), 0xFFFFFFFF);
|
||||
cl_assert_equal_i(graphics_private_get_1bit_grayscale_pattern(GColorWhite, 1), 0xFFFFFFFF);
|
||||
cl_assert_equal_i(graphics_private_get_1bit_grayscale_pattern(GColorLightGray, 0), 0x55555555);
|
||||
cl_assert_equal_i(graphics_private_get_1bit_grayscale_pattern(GColorLightGray, 1), 0xAAAAAAAA);
|
||||
cl_assert_equal_i(graphics_private_get_1bit_grayscale_pattern(GColorDarkGray, 0), 0x55555555);
|
||||
cl_assert_equal_i(graphics_private_get_1bit_grayscale_pattern(GColorDarkGray, 1), 0xAAAAAAAA);
|
||||
cl_assert_equal_i(graphics_private_get_1bit_grayscale_pattern(GColorBlack, 0), 0x00000000);
|
||||
cl_assert_equal_i(graphics_private_get_1bit_grayscale_pattern(GColorBlack, 1), 0x00000000);
|
||||
}
|
||||
|
||||
void test_bitblt_palette_1bit__prv_apply_tint_color(void) {
|
||||
GColor color = GColorBlack;
|
||||
GColor tinted_color = GColorBlack;
|
||||
prv_apply_tint_color(&color, GColorClear);
|
||||
cl_assert_equal_i(color.argb, tinted_color.argb);
|
||||
tinted_color = GColorRed;
|
||||
prv_apply_tint_color(&color, GColorRed);
|
||||
cl_assert_equal_i(color.argb, tinted_color.argb);
|
||||
tinted_color.a = 2;
|
||||
color.a = 2;
|
||||
prv_apply_tint_color(&color, GColorRed);
|
||||
cl_assert_equal_i(color.argb, tinted_color.argb);
|
||||
tinted_color.a = 1;
|
||||
color.a = 1;
|
||||
prv_apply_tint_color(&color, GColorRed);
|
||||
cl_assert_equal_i(color.argb, tinted_color.argb);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
#include "applib/graphics/bitblt_private.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
#include "util/math.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "${BIT_DEPTH_NAME}/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
#define ORIGIN_RECT_NO_CLIP GRect(0, 0, 144, 168)
|
||||
#define OFFSET_X 9
|
||||
#define OFFSET_Y 20
|
||||
#define STRIPE_WIDTH 64
|
||||
#define STRIPE_HEIGHT 1
|
||||
|
||||
void prv_test_plot_horizontal_line(GBitmap *framebuffer, GRect area, GColor color) {
|
||||
int y = area.origin.y;
|
||||
int x0 = area.origin.x;
|
||||
int x1 = x0 + area.size.w;
|
||||
|
||||
if (y >= framebuffer->bounds.size.h || y < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int16_t x_min = MAX(MIN(x0, x1), framebuffer->bounds.origin.x);
|
||||
int16_t x_max = MIN(MAX(x0, x1), framebuffer->bounds.origin.x + framebuffer->bounds.size.w);
|
||||
|
||||
for (int i=x_min; i < x_max; i++) {
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
GColor *output = (GColor*)framebuffer->addr + y * framebuffer->row_size_bytes + i;
|
||||
// src_col, dest_col
|
||||
output->argb = gcolor_alpha_blend(color, (*output)).argb;
|
||||
#endif // SCREEN_COLOR_DEPTH_BITS == 8
|
||||
}
|
||||
}
|
||||
|
||||
void test_blending_${BIT_DEPTH_NAME}__photoshop(void) {
|
||||
|
||||
GContext ctx;
|
||||
FrameBuffer *fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) { DISP_COLS, DISP_ROWS });
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
GBitmap *background_0_100 = get_gbitmap_from_pbi("blendtest_0_100_backdrop.pbi");
|
||||
GBitmap *background_33_66 = get_gbitmap_from_pbi("blendtest_33_66_backdrop.pbi");
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_bitmap_in_rect(&ctx, background_0_100, &ORIGIN_RECT_NO_CLIP);
|
||||
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
// Sanity check
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "blendtest_0_100_backdrop.pbi"));
|
||||
|
||||
// 0% alpha channel blend test
|
||||
uint8_t color = 0x0;
|
||||
uint8_t alpha = 0x0;
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_bitmap_in_rect(&ctx, background_0_100, &ORIGIN_RECT_NO_CLIP);
|
||||
|
||||
for (int i=0; i < 64; i++) {
|
||||
prv_test_plot_horizontal_line(&ctx.dest_bitmap,
|
||||
GRect(OFFSET_X, OFFSET_Y + i, STRIPE_WIDTH, STRIPE_HEIGHT),
|
||||
(GColor){.argb = color | alpha});
|
||||
color++;
|
||||
}
|
||||
|
||||
// 100% alpha channel blend test
|
||||
color = 0x0;
|
||||
alpha = 0xC0;
|
||||
|
||||
for (int i=64; i < 128; i++) {
|
||||
prv_test_plot_horizontal_line(&ctx.dest_bitmap,
|
||||
GRect(OFFSET_X, OFFSET_Y + i, STRIPE_WIDTH, STRIPE_HEIGHT),
|
||||
(GColor){.argb = color | alpha});
|
||||
color++;
|
||||
}
|
||||
|
||||
// Compare the results
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "blendtest_0_100.pbi"));
|
||||
|
||||
// Reset canvas
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_bitmap_in_rect(&ctx, background_33_66, &ORIGIN_RECT_NO_CLIP);
|
||||
|
||||
// 33% alpha channel blend test
|
||||
color = 0x0;
|
||||
alpha = 0x40;
|
||||
|
||||
for (int i=0; i < 64; i++) {
|
||||
prv_test_plot_horizontal_line(&ctx.dest_bitmap,
|
||||
GRect(OFFSET_X, OFFSET_Y + i, STRIPE_WIDTH, STRIPE_HEIGHT),
|
||||
(GColor){.argb = color | alpha});
|
||||
color++;
|
||||
}
|
||||
|
||||
// 66% alpha channel blend test
|
||||
color = 0x0;
|
||||
alpha = 0x80;
|
||||
|
||||
for (int i=64; i < 128; i++) {
|
||||
prv_test_plot_horizontal_line(&ctx.dest_bitmap,
|
||||
GRect(OFFSET_X, OFFSET_Y + i, STRIPE_WIDTH, STRIPE_HEIGHT),
|
||||
(GColor){.argb = color | alpha});
|
||||
color++;
|
||||
}
|
||||
|
||||
// Compare the results
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "blendtest_33_66.pbi"));
|
||||
#endif
|
||||
|
||||
|
||||
gbitmap_destroy(background_0_100);
|
||||
gbitmap_destroy(background_33_66);
|
||||
free(fb);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include "util/bitset.h"
|
||||
#include "util/size.h"
|
||||
#include "${BIT_DEPTH_NAME}/test_framebuffer.h"
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
// Setup
|
||||
////////////////////////////////////
|
||||
static FrameBuffer framebuffer;
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
void test_framebuffer_${BIT_DEPTH_NAME}__framebuffer_clear(void) {
|
||||
// clear the framebuffer
|
||||
framebuffer_init(&framebuffer, &(GSize) { DISP_COLS, DISP_ROWS });
|
||||
framebuffer_clear(&framebuffer);
|
||||
|
||||
// check the clearing
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 1
|
||||
for (int i = 0; i < ARRAY_LENGTH(framebuffer.buffer); i++) {
|
||||
cl_assert(framebuffer.buffer[i] == 0xffffffff);
|
||||
}
|
||||
#elif SCREEN_COLOR_DEPTH_BITS == 8
|
||||
for (int i = 0; i < ARRAY_LENGTH(framebuffer.buffer); i++) {
|
||||
cl_assert(framebuffer.buffer[i] == GColorWhite.argb);
|
||||
}
|
||||
GRect expected_dirty = GRect(0, 0, DISP_COLS, DISP_ROWS);
|
||||
#else
|
||||
cl_assert(!memcmp(&framebuffer.dirty_rect, &expected_dirty, sizeof(GRect)));
|
||||
cl_assert(false);
|
||||
#endif
|
||||
|
||||
cl_assert(framebuffer.is_dirty == true);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/graphics_private.h"
|
||||
#include "applib/graphics/graphics_private_raw.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
#include "util/trig.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "8bit/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
#define CLIP_RECT_DRAW_BOX GRect(0, 0, DISP_COLS, DISP_ROWS)
|
||||
#define CLIP_RECT_CLIP_BOX GRect(0, 0, DISP_COLS, DISP_ROWS)
|
||||
|
||||
// Setup and Teardown
|
||||
////////////////////////////////////
|
||||
|
||||
// Setup
|
||||
void test_framebuffer_clipping__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) { DISP_COLS, DISP_ROWS });
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_framebuffer_clipping__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
void test_framebuffer_clipping__off_screen_left_aa_clipping(void) {
|
||||
cl_assert_equal_i(GBITMAP_NATIVE_FORMAT, GBitmapFormat8BitCircular);
|
||||
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, true, 1);
|
||||
|
||||
cl_assert_equal_i(ctx.dest_bitmap.info.format, GBitmapFormat8BitCircular);
|
||||
|
||||
memset(ctx.dest_bitmap.addr, GColorRedARGB8, FRAMEBUFFER_SIZE_BYTES);
|
||||
|
||||
GRect radial_container_rect = ctx.dest_bitmap.bounds;
|
||||
radial_container_rect = grect_inset(radial_container_rect, GEdgeInsets(-10));
|
||||
const uint16_t inset_thickness = radial_container_rect.size.w / 4;
|
||||
graphics_context_set_fill_color(&ctx, GColorGreen);
|
||||
graphics_fill_radial(&ctx, radial_container_rect, GOvalScaleModeFillCircle, inset_thickness,
|
||||
TRIG_MAX_ANGLE / 2, TRIG_MAX_ANGLE);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("off_screen_left_aa_clipping")));
|
||||
};
|
||||
|
||||
void test_framebuffer_clipping__off_screen_right_aa_clipping(void) {
|
||||
cl_assert_equal_i(GBITMAP_NATIVE_FORMAT, GBitmapFormat8BitCircular);
|
||||
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, true, 1);
|
||||
cl_assert_equal_i(ctx.dest_bitmap.info.format, GBitmapFormat8BitCircular);
|
||||
|
||||
memset(ctx.dest_bitmap.addr, GColorRedARGB8, FRAMEBUFFER_SIZE_BYTES);
|
||||
|
||||
GRect radial_container_rect = ctx.dest_bitmap.bounds;
|
||||
radial_container_rect = grect_inset(radial_container_rect, GEdgeInsets(-10));
|
||||
const uint16_t inset_thickness = radial_container_rect.size.w / 4;
|
||||
graphics_context_set_fill_color(&ctx, GColorGreen);
|
||||
graphics_fill_radial(&ctx, radial_container_rect, GOvalScaleModeFillCircle, inset_thickness, 0,
|
||||
TRIG_MAX_ANGLE / 2);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("off_screen_right_aa_clipping")));
|
||||
};
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
#include "util/graphics.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "8bit/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
#define COLOR(x) ((GColor){.argb=GColor ## x ## ARGB8})
|
||||
#define NUM_COLORS 4
|
||||
GColor color_table[NUM_COLORS] = {
|
||||
COLOR(Red),
|
||||
COLOR(Yellow),
|
||||
COLOR(Cyan),
|
||||
COLOR(Black),
|
||||
};
|
||||
|
||||
// Setup
|
||||
void test_framebuffer_duma__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) { DISP_COLS, DISP_ROWS });
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_framebuffer_duma__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
// Intentionally unchecked framebuffer drawing function
|
||||
static void draw_fb_raw(uint8_t *buffer, int offset, GColor8 color) {
|
||||
buffer[offset] = color.argb;
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
void test_framebuffer_duma__draw_within_framebuffer(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// This test should only be running on color displays
|
||||
cl_assert(ctx.dest_bitmap.info.format == GBitmapFormat8Bit ||
|
||||
ctx.dest_bitmap.info.format == GBitmapFormat8BitCircular);
|
||||
|
||||
// This should touch all valid bytes in the framebuffer
|
||||
for (int y = DISP_ROWS - 1; y >= 0; y--) {
|
||||
GColor color = color_table[y % NUM_COLORS];
|
||||
int16_t row_offset = DISP_COLS * y;
|
||||
GBitmapDataRowInfo row_info = gbitmap_get_data_row_info(&ctx.dest_bitmap, y);
|
||||
for (int x = row_info.min_x; x < row_info.max_x; x++) {
|
||||
// Use direct framebuffer access to prove framebuffer-correct positioning
|
||||
row_info.data[x] = color.argb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This test validates that a duma assert is caught when drawing outside of the framebuffer
|
||||
void test_framebuffer_duma__draw_beyond_framebuffer(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// This test should only be running on color displays
|
||||
cl_assert(ctx.dest_bitmap.info.format == GBitmapFormat8Bit ||
|
||||
ctx.dest_bitmap.info.format == GBitmapFormat8BitCircular);
|
||||
|
||||
uint8_t *buffer = (uint8_t*)ctx.dest_bitmap.addr;
|
||||
// Expect this to assert using duma protection, we are writing past framebuffer
|
||||
cl_assert_passert(draw_fb_raw(buffer, FRAMEBUFFER_SIZE_BYTES + 1, GColorWhite));
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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/gtypes.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// stubs
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_graphics_context.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_process_manager.h"
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
void test_gbitmap_data_row_info__get_info_rectangular(void) {
|
||||
uint8_t some_addr;
|
||||
GBitmap bmp = {
|
||||
.addr = &some_addr,
|
||||
.row_size_bytes = 123,
|
||||
.bounds = GRect(1, 2, 3, 4),
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
};
|
||||
|
||||
cl_assert_equal_i(123, gbitmap_format_get_row_size_bytes(123, GBitmapFormat8Bit));
|
||||
cl_assert_equal_i(123, gbitmap_get_bytes_per_row(&bmp));
|
||||
cl_assert_equal_p(&some_addr + 0 * 123, gbitmap_get_data_row_info(&bmp, 0).data);
|
||||
cl_assert_equal_p(&some_addr + 1 * 123, gbitmap_get_data_row_info(&bmp, 1).data);
|
||||
cl_assert_equal_i(0, gbitmap_get_data_row_info(&bmp, 3).min_x);
|
||||
cl_assert_equal_i(3, gbitmap_get_data_row_info(&bmp, 3).max_x);
|
||||
}
|
||||
|
||||
void test_gbitmap_data_row_info__get_info_circular(void) {
|
||||
uint8_t some_addr;
|
||||
GBitmapDataRowInfoInternal infos[] = {
|
||||
{.offset = 1, .min_x = 2, .max_x = 3}, // 0
|
||||
{.offset = 4, .min_x = 5, .max_x = 6}, // 1
|
||||
{.offset = 7, .min_x = 8, .max_x = 9}, // 2 // 0
|
||||
{.offset = 10, .min_x = 11, .max_x = 12}, // 3 // 1
|
||||
{.offset = 13, .min_x = 14, .max_x = 15}, // 4 // 2
|
||||
{.offset = 16, .min_x = 17, .max_x = 18}, // 5 // 3
|
||||
};
|
||||
GBitmap bmp = {
|
||||
.addr = &some_addr,
|
||||
.row_size_bytes = 123,
|
||||
.bounds = GRect(1, 2, 3, 4),
|
||||
.info.format = GBitmapFormat8BitCircular,
|
||||
.data_row_infos = infos,
|
||||
};
|
||||
|
||||
cl_assert_equal_i(0, gbitmap_format_get_row_size_bytes(123, GBitmapFormat8BitCircular));
|
||||
cl_assert_equal_i(123, gbitmap_get_bytes_per_row(&bmp));
|
||||
cl_assert_equal_p(&some_addr + 1, gbitmap_get_data_row_info(&bmp, 0).data);
|
||||
cl_assert_equal_p(&some_addr + 4, gbitmap_get_data_row_info(&bmp, 1).data);
|
||||
cl_assert_equal_i(11, gbitmap_get_data_row_info(&bmp, 3).min_x);
|
||||
cl_assert_equal_i(12, gbitmap_get_data_row_info(&bmp, 3).max_x);
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* 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 "applib/graphics/gtypes.h"
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// stubs
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_graphics_context.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_process_manager.h"
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
const void * const g_gbitmap_spalding_data_row_infos = &g_gbitmap_spalding_data_row_infos;
|
||||
|
||||
void test_gbitmap_formats__create_blank(void) {
|
||||
const GSize s10 = GSize(10, 10);
|
||||
const GSize s180 = GSize(180, 180);
|
||||
GBitmap *bmp = NULL;
|
||||
|
||||
cl_assert((void*)&bmp->palette == (void*)&bmp->data_row_infos); // union with .palette
|
||||
cl_assert(NULL != g_gbitmap_spalding_data_row_infos); // make sure unit-test fixture is ok
|
||||
|
||||
|
||||
#ifdef PLATFORM_TINTIN
|
||||
cl_assert(!process_manager_compiled_with_legacy2_sdk());
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat1Bit);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(NULL == bmp->data_row_infos);
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat8Bit);
|
||||
cl_assert(NULL == bmp);
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat1BitPalette);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat2BitPalette);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat4BitPalette);
|
||||
cl_assert(NULL == bmp);
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat8BitCircular);
|
||||
cl_assert(NULL == bmp);
|
||||
|
||||
bmp = gbitmap_create_blank(s180, GBitmapFormat8BitCircular);
|
||||
cl_assert(NULL == bmp);
|
||||
#endif
|
||||
#ifdef PLATFORM_SNOWY
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat1Bit);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(NULL == bmp->data_row_infos);
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat8Bit);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(NULL == bmp->data_row_infos);
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat1BitPalette);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat2BitPalette);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat4BitPalette);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat8BitCircular);
|
||||
cl_assert(NULL == bmp);
|
||||
|
||||
bmp = gbitmap_create_blank(s180, GBitmapFormat8BitCircular);
|
||||
cl_assert(NULL == bmp);
|
||||
#endif
|
||||
#ifdef PLATFORM_SPALDING
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat1Bit);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(NULL == bmp->data_row_infos);
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat8Bit);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(NULL == bmp->data_row_infos);
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat1BitPalette);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat2BitPalette);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat4BitPalette);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(g_gbitmap_spalding_data_row_infos != bmp->data_row_infos); // union with .palette
|
||||
|
||||
bmp = gbitmap_create_blank(s10, GBitmapFormat8BitCircular);
|
||||
cl_assert(NULL == bmp);
|
||||
|
||||
bmp = gbitmap_create_blank(s180, GBitmapFormat8BitCircular);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(g_gbitmap_spalding_data_row_infos == bmp->data_row_infos);
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_gbitmap_formats__create_blank_with_palette(void) {
|
||||
const GSize s10 = GSize(10, 10);
|
||||
const GSize s180 = GSize(180, 180);
|
||||
GBitmap *bmp;
|
||||
GColor8 *p = (GColor8 *)&p; // some value to test against
|
||||
|
||||
#ifdef PLATFORM_TINTIN
|
||||
cl_assert(!process_manager_compiled_with_legacy2_sdk());
|
||||
cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat1Bit, p, true));
|
||||
cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat8Bit, p, true));
|
||||
|
||||
bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat1BitPalette, p, true);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(p == gbitmap_get_palette(bmp));
|
||||
|
||||
bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat2BitPalette, p, true);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(p == gbitmap_get_palette(bmp));
|
||||
|
||||
bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat4BitPalette, p, true);
|
||||
cl_assert(NULL == bmp);
|
||||
|
||||
cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat8BitCircular, p, true));
|
||||
cl_assert(NULL == gbitmap_create_blank_with_palette(s180, GBitmapFormat8BitCircular, p, true));
|
||||
#endif
|
||||
#ifdef PLATFORM_SNOWY
|
||||
cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat1Bit, p, true));
|
||||
cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat8Bit, p, true));
|
||||
|
||||
bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat1BitPalette, p, true);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(p == gbitmap_get_palette(bmp));
|
||||
|
||||
bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat2BitPalette, p, true);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(p == gbitmap_get_palette(bmp));
|
||||
|
||||
bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat4BitPalette, p, true);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(p == gbitmap_get_palette(bmp));
|
||||
|
||||
cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat8BitCircular, p, true));
|
||||
cl_assert(NULL == gbitmap_create_blank_with_palette(s180, GBitmapFormat8BitCircular, p, true));
|
||||
#endif
|
||||
#ifdef PLATFORM_SPALDING
|
||||
cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat1Bit, p, true));
|
||||
cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat8Bit, p, true));
|
||||
|
||||
bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat1BitPalette, p, true);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(p == gbitmap_get_palette(bmp));
|
||||
|
||||
bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat2BitPalette, p, true);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(p == gbitmap_get_palette(bmp));
|
||||
|
||||
bmp = gbitmap_create_blank_with_palette(s10, GBitmapFormat4BitPalette, p, true);
|
||||
cl_assert(NULL != bmp);
|
||||
cl_assert(p == gbitmap_get_palette(bmp));
|
||||
|
||||
cl_assert(NULL == gbitmap_create_blank_with_palette(s10, GBitmapFormat8BitCircular, p, true));
|
||||
cl_assert(NULL == gbitmap_create_blank_with_palette(s180, GBitmapFormat8BitCircular, p, true));
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_gbitmap_formats__display_framebuffer_bytes(void) {
|
||||
#ifdef PLATFORM_TINTIN
|
||||
const size_t expected = 20 * 168; // 20 * 8 == 144px + 2 bytes padding per scanline
|
||||
#endif
|
||||
#ifdef PLATFORM_SNOWY
|
||||
const size_t expected = 144 * 168;
|
||||
#endif
|
||||
#ifdef PLATFORM_SPALDING
|
||||
// all pixels + 2*76
|
||||
const size_t expected = 25944;
|
||||
#endif
|
||||
cl_assert_equal_i(expected, DISPLAY_FRAMEBUFFER_BYTES);
|
||||
}
|
||||
|
||||
size_t prv_gbitmap_size_for_data(GSize size, GBitmapFormat format);
|
||||
|
||||
void test_gbitmap_formats__size_for_data(void) {
|
||||
cl_assert_equal_i( 40, prv_gbitmap_size_for_data(GSize(13, 10), GBitmapFormat1Bit));
|
||||
cl_assert_equal_i(130, prv_gbitmap_size_for_data(GSize(13, 10), GBitmapFormat8Bit));
|
||||
cl_assert_equal_i( 20, prv_gbitmap_size_for_data(GSize(13, 10), GBitmapFormat1BitPalette));
|
||||
cl_assert_equal_i( 40, prv_gbitmap_size_for_data(GSize(13, 10), GBitmapFormat2BitPalette));
|
||||
cl_assert_equal_i( 70, prv_gbitmap_size_for_data(GSize(13, 10), GBitmapFormat4BitPalette));
|
||||
cl_assert_equal_i( 0, prv_gbitmap_size_for_data(GSize(13, 10), GBitmapFormat8BitCircular));
|
||||
|
||||
const size_t expected = PBL_IF_RECT_ELSE(0, DISPLAY_FRAMEBUFFER_BYTES);
|
||||
cl_assert_equal_i(expected,
|
||||
prv_gbitmap_size_for_data(GSize(180, 180), GBitmapFormat8BitCircular));
|
||||
cl_assert_equal_i(expected,
|
||||
prv_gbitmap_size_for_data(GSize(DISP_COLS, DISP_ROWS), GBitmapFormat8BitCircular));
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* 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/framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
///////////////////////
|
||||
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_graphics_circle.h"
|
||||
#include "stubs_graphics_line.h"
|
||||
#include "stubs_graphics_private.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_process_manager.h"
|
||||
|
||||
const GDrawRawImplementation g_default_draw_implementation;
|
||||
|
||||
// Statics
|
||||
///////////////////////
|
||||
|
||||
static GContext s_ctx;
|
||||
static FrameBuffer s_fb;
|
||||
|
||||
typedef struct MockBitbltBitmapIntoBitmapTiledCallRecording {
|
||||
GBitmap *dest_bitmap;
|
||||
const GBitmap *src_bitmap;
|
||||
GRect dest_rect;
|
||||
GPoint src_origin_offset;
|
||||
GCompOp compositing_mode;
|
||||
GColor8 tint_color;
|
||||
} MockBitbltBitmapIntoBitmapTiledCallRecording;
|
||||
|
||||
typedef struct MockBitbltBitmapIntoBitmapTiledCallRecordings {
|
||||
unsigned int call_count;
|
||||
MockBitbltBitmapIntoBitmapTiledCallRecording last_call;
|
||||
} MockBitbltBitmapIntoBitmapTiledCallRecordings;
|
||||
|
||||
static MockBitbltBitmapIntoBitmapTiledCallRecordings s_bitblt_bitmap_into_bitmap_tiled_calls;
|
||||
|
||||
// Fakes
|
||||
///////////////////////
|
||||
|
||||
GContext *graphics_context_get_current_context(void) {
|
||||
return &s_ctx;
|
||||
}
|
||||
|
||||
void bitblt_bitmap_into_bitmap_tiled(GBitmap* dest_bitmap, const GBitmap *src_bitmap,
|
||||
GRect dest_rect, GPoint src_origin_offset,
|
||||
GCompOp compositing_mode, GColor8 tint_color) {
|
||||
s_bitblt_bitmap_into_bitmap_tiled_calls.call_count++;
|
||||
s_bitblt_bitmap_into_bitmap_tiled_calls.last_call =
|
||||
(MockBitbltBitmapIntoBitmapTiledCallRecording) {
|
||||
.dest_bitmap = dest_bitmap,
|
||||
.src_bitmap = src_bitmap,
|
||||
.dest_rect = dest_rect,
|
||||
.src_origin_offset = src_origin_offset,
|
||||
.compositing_mode = compositing_mode,
|
||||
.tint_color = tint_color,
|
||||
};
|
||||
}
|
||||
|
||||
// Helpers
|
||||
///////////////////////
|
||||
|
||||
static void cl_assert_equal_rect(const GRect a, const GRect b) {
|
||||
cl_assert_equal_i(a.origin.x, b.origin.x);
|
||||
cl_assert_equal_i(a.origin.y, b.origin.y);
|
||||
cl_assert_equal_i(a.size.w, b.size.w);
|
||||
cl_assert_equal_i(a.size.h, b.size.h);
|
||||
}
|
||||
|
||||
// Setup
|
||||
///////////////////////
|
||||
|
||||
void test_gbitmap_processor__initialize(void) {
|
||||
s_fb = (FrameBuffer) {};
|
||||
framebuffer_init(&s_fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
graphics_context_init(&s_ctx, &s_fb, GContextInitializationMode_App);
|
||||
s_bitblt_bitmap_into_bitmap_tiled_calls = (MockBitbltBitmapIntoBitmapTiledCallRecordings) {};
|
||||
}
|
||||
|
||||
void test_gbitmap_processor__cleanup(void) {
|
||||
}
|
||||
|
||||
// Tests
|
||||
///////////////////////
|
||||
|
||||
#define EXPECTED_RECT_IN_PRE_FUNCTION (GRect(4, 3, 2, 1))
|
||||
|
||||
void test_gbitmap_processor__null_arguments(void) {
|
||||
GBitmap bitmap = {0};
|
||||
const GRect rect = EXPECTED_RECT_IN_PRE_FUNCTION;
|
||||
|
||||
// Passing NULL for the processor shouldn't cause any problems
|
||||
graphics_draw_bitmap_in_rect_processed(&s_ctx, &bitmap, &rect, NULL);
|
||||
// And it should try to draw the bitmap
|
||||
cl_assert_equal_i(s_bitblt_bitmap_into_bitmap_tiled_calls.call_count, 1);
|
||||
|
||||
// Passing a processor with NULL functions shouldn't cause any problems
|
||||
GBitmapProcessor processor = {0};
|
||||
graphics_draw_bitmap_in_rect_processed(&s_ctx, &bitmap, &rect, &processor);
|
||||
// And it should once again try to draw the bitmap
|
||||
cl_assert_equal_i(s_bitblt_bitmap_into_bitmap_tiled_calls.call_count, 2);
|
||||
}
|
||||
|
||||
#define EXPECTED_COMPOSITING_MODE_BEFORE_AND_AFTER_PRE_FUNCTION (GCompOpSet)
|
||||
#define EXPECTED_TINT_COLOR_BEFORE_AND_AFTER_PRE_FUNCTION (GColorShockingPink)
|
||||
|
||||
#define COMPOSITING_MODE_TO_SPECIFY_IN_PRE_FUNCTION (GCompOpTint)
|
||||
#define TINT_COLOR_TO_SPECIFY_IN_PRE_FUNCTION (GColorTiffanyBlue)
|
||||
#define RECT_TO_SPECIFY_IN_PRE_FUNCTION (GRect(-50, -50, 100, 100))
|
||||
#define BITMAP_TO_SPECIFY_IN_PRE_FUNCTION ((GBitmap *)1234)
|
||||
|
||||
#define EXPECTED_CLIPPED_RECT_AFTER_DRAWING_BITMAP (GRect(0, 0, 50, 50))
|
||||
|
||||
typedef struct PreAndPostFunctionsTestProcessor {
|
||||
GBitmapProcessor processor;
|
||||
GCompOp previous_compositing_mode;
|
||||
GColor previous_tint_color;
|
||||
} PreAndPostFunctionsTestProcessor;
|
||||
|
||||
static void prv_pre_and_post_functions__pre(GBitmapProcessor *processor, GContext *ctx,
|
||||
const GBitmap **bitmap_to_use,
|
||||
GRect *global_grect_to_use) {
|
||||
PreAndPostFunctionsTestProcessor *processor_with_data =
|
||||
(PreAndPostFunctionsTestProcessor *)processor;
|
||||
|
||||
// Record the existing compositing mode and tint color and check that they are what we expect
|
||||
cl_assert(ctx->draw_state.compositing_mode ==
|
||||
EXPECTED_COMPOSITING_MODE_BEFORE_AND_AFTER_PRE_FUNCTION);
|
||||
processor_with_data->previous_compositing_mode = ctx->draw_state.compositing_mode;
|
||||
cl_assert(gcolor_equal(ctx->draw_state.tint_color,
|
||||
EXPECTED_TINT_COLOR_BEFORE_AND_AFTER_PRE_FUNCTION));
|
||||
processor_with_data->previous_tint_color = ctx->draw_state.tint_color;
|
||||
|
||||
// Set the compositing mode and tint color to different values
|
||||
ctx->draw_state.compositing_mode = COMPOSITING_MODE_TO_SPECIFY_IN_PRE_FUNCTION;
|
||||
ctx->draw_state.tint_color = TINT_COLOR_TO_SPECIFY_IN_PRE_FUNCTION;
|
||||
|
||||
// Check that the rect here is what we gave to graphics_draw_bitmap_in_rect_processed()
|
||||
cl_assert_equal_rect(*global_grect_to_use, EXPECTED_RECT_IN_PRE_FUNCTION);
|
||||
|
||||
// Change the rect
|
||||
*global_grect_to_use = RECT_TO_SPECIFY_IN_PRE_FUNCTION;
|
||||
|
||||
// Change the bitmap
|
||||
*bitmap_to_use = BITMAP_TO_SPECIFY_IN_PRE_FUNCTION;
|
||||
}
|
||||
|
||||
static void prv_pre_and_post_functions__post(GBitmapProcessor *processor, GContext *ctx,
|
||||
const GBitmap *bitmap_used,
|
||||
const GRect *global_clipped_grect_used) {
|
||||
PreAndPostFunctionsTestProcessor *processor_with_data =
|
||||
(PreAndPostFunctionsTestProcessor *)processor;
|
||||
|
||||
// Check that the changes made to the GContext in .pre are still present
|
||||
cl_assert(ctx->draw_state.compositing_mode == COMPOSITING_MODE_TO_SPECIFY_IN_PRE_FUNCTION);
|
||||
cl_assert(gcolor_equal(ctx->draw_state.tint_color, TINT_COLOR_TO_SPECIFY_IN_PRE_FUNCTION));
|
||||
|
||||
// Reverse the changes to the GContext that were made in .pre
|
||||
ctx->draw_state.compositing_mode = processor_with_data->previous_compositing_mode;
|
||||
ctx->draw_state.tint_color = processor_with_data->previous_tint_color;
|
||||
|
||||
// Check that the bitmap here is the bitmap we specified in the .pre function
|
||||
cl_assert_equal_p(bitmap_used, BITMAP_TO_SPECIFY_IN_PRE_FUNCTION);
|
||||
|
||||
// Check that the rect here is the clipped version of the rect we specified in the .pre function
|
||||
cl_assert_equal_rect(*global_clipped_grect_used, EXPECTED_CLIPPED_RECT_AFTER_DRAWING_BITMAP);
|
||||
}
|
||||
|
||||
void test_gbitmap_processor__pre_and_post_functions(void) {
|
||||
GBitmap bitmap = {0};
|
||||
const GRect rect = EXPECTED_RECT_IN_PRE_FUNCTION;
|
||||
|
||||
// Set the compositing mode and tint color to known values
|
||||
s_ctx.draw_state.compositing_mode = EXPECTED_COMPOSITING_MODE_BEFORE_AND_AFTER_PRE_FUNCTION;
|
||||
s_ctx.draw_state.tint_color = EXPECTED_TINT_COLOR_BEFORE_AND_AFTER_PRE_FUNCTION;
|
||||
|
||||
PreAndPostFunctionsTestProcessor processor = (PreAndPostFunctionsTestProcessor) {
|
||||
.processor.pre = prv_pre_and_post_functions__pre,
|
||||
.processor.post = prv_pre_and_post_functions__post,
|
||||
};
|
||||
graphics_draw_bitmap_in_rect_processed(&s_ctx, &bitmap, &rect, &processor.processor);
|
||||
|
||||
// Check that the bitmap was drawn
|
||||
cl_assert_equal_i(s_bitblt_bitmap_into_bitmap_tiled_calls.call_count, 1);
|
||||
|
||||
// Check that the modifications made in the .pre function propagated to the bitmap drawing
|
||||
cl_assert(s_bitblt_bitmap_into_bitmap_tiled_calls.last_call.compositing_mode ==
|
||||
COMPOSITING_MODE_TO_SPECIFY_IN_PRE_FUNCTION);
|
||||
cl_assert(gcolor_equal(s_bitblt_bitmap_into_bitmap_tiled_calls.last_call.tint_color,
|
||||
TINT_COLOR_TO_SPECIFY_IN_PRE_FUNCTION));
|
||||
cl_assert_equal_rect(s_bitblt_bitmap_into_bitmap_tiled_calls.last_call.dest_rect,
|
||||
EXPECTED_CLIPPED_RECT_AFTER_DRAWING_BITMAP);
|
||||
cl_assert_equal_p(s_bitblt_bitmap_into_bitmap_tiled_calls.last_call.src_bitmap,
|
||||
BITMAP_TO_SPECIFY_IN_PRE_FUNCTION);
|
||||
|
||||
// Check that the modifications made to the GContext in the .pre function were reversed in .post
|
||||
cl_assert(s_ctx.draw_state.compositing_mode ==
|
||||
EXPECTED_COMPOSITING_MODE_BEFORE_AND_AFTER_PRE_FUNCTION);
|
||||
cl_assert(gcolor_equal(s_ctx.draw_state.tint_color,
|
||||
EXPECTED_TINT_COLOR_BEFORE_AND_AFTER_PRE_FUNCTION));
|
||||
|
||||
// Note that additional checks are performed in the .pre and .post functions
|
||||
}
|
||||
|
||||
typedef struct PostFunctionCalledEvenIfPreFunctionCausesNothingToBeDrawnTestProcessor {
|
||||
GBitmapProcessor processor;
|
||||
const GBitmap *expected_bitmap_in_post;
|
||||
bool post_func_called;
|
||||
} PostFunctionCalledEvenIfPreFunctionCausesNothingToBeDrawnTestProcessor;
|
||||
|
||||
static void post_function_called_even_if_pre_function_causes_nothing_to_be_drawn__post(
|
||||
GBitmapProcessor *processor, GContext *ctx, const GBitmap *bitmap_used,
|
||||
const GRect *global_clipped_grect_used) {
|
||||
PostFunctionCalledEvenIfPreFunctionCausesNothingToBeDrawnTestProcessor *processor_with_data =
|
||||
(PostFunctionCalledEvenIfPreFunctionCausesNothingToBeDrawnTestProcessor *)processor;
|
||||
|
||||
// Check that the rectangle here is empty to verify that nothing was drawn
|
||||
cl_assert_equal_rect(*global_clipped_grect_used, GRectZero);
|
||||
|
||||
// Check that bitmap_used is what we expect it to be (the expected value is set in .pre)
|
||||
cl_assert_equal_p(bitmap_used, processor_with_data->expected_bitmap_in_post);
|
||||
|
||||
// Record that the .post function was called
|
||||
processor_with_data->post_func_called = true;
|
||||
}
|
||||
|
||||
//! Helper function for testing that the .post function is called even if the .pre function
|
||||
//! causes no bitmap to be drawn
|
||||
static void prv_post_function_called_even_if_pre_function_causes_nothing_to_be_drawn_test(
|
||||
GBitmapProcessorPreFunc pre_func) {
|
||||
GBitmap bitmap = {0};
|
||||
const GRect rect = EXPECTED_RECT_IN_PRE_FUNCTION;
|
||||
|
||||
PostFunctionCalledEvenIfPreFunctionCausesNothingToBeDrawnTestProcessor processor =
|
||||
(PostFunctionCalledEvenIfPreFunctionCausesNothingToBeDrawnTestProcessor) {
|
||||
.processor.pre = pre_func,
|
||||
.processor.post = post_function_called_even_if_pre_function_causes_nothing_to_be_drawn__post,
|
||||
};
|
||||
graphics_draw_bitmap_in_rect_processed(&s_ctx, &bitmap, &rect, &processor.processor);
|
||||
|
||||
// Check that the bitmap was not drawn
|
||||
cl_assert_equal_i(s_bitblt_bitmap_into_bitmap_tiled_calls.call_count, 0);
|
||||
|
||||
// Check that the .post function was called even though the .pre function makes some change
|
||||
// that causes no bitmap to be drawn
|
||||
cl_assert(processor.post_func_called);
|
||||
}
|
||||
|
||||
static void post_function_called_even_if_pre_function_specifies_null_bitmap__pre(
|
||||
GBitmapProcessor *processor, GContext *ctx, const GBitmap **bitmap_to_use,
|
||||
GRect *global_grect_to_use) {
|
||||
PostFunctionCalledEvenIfPreFunctionCausesNothingToBeDrawnTestProcessor *processor_with_data =
|
||||
(PostFunctionCalledEvenIfPreFunctionCausesNothingToBeDrawnTestProcessor *)processor;
|
||||
|
||||
// Change the bitmap to use to NULL to cause nothing to be drawn
|
||||
*bitmap_to_use = NULL;
|
||||
|
||||
// We'll expect the bitmap we set there to be the bitmap passed into .post
|
||||
processor_with_data->expected_bitmap_in_post = *bitmap_to_use;
|
||||
}
|
||||
|
||||
void test_gbitmap_processor__post_function_called_even_if_pre_function_specifies_null_bitmap(void) {
|
||||
prv_post_function_called_even_if_pre_function_causes_nothing_to_be_drawn_test(
|
||||
post_function_called_even_if_pre_function_specifies_null_bitmap__pre);
|
||||
};
|
||||
|
||||
static void post_function_called_even_if_pre_function_specifies_empty_rect__pre(
|
||||
GBitmapProcessor *processor, GContext *ctx, const GBitmap **bitmap_to_use,
|
||||
GRect *global_grect_to_use) {
|
||||
PostFunctionCalledEvenIfPreFunctionCausesNothingToBeDrawnTestProcessor *processor_with_data =
|
||||
(PostFunctionCalledEvenIfPreFunctionCausesNothingToBeDrawnTestProcessor *)processor;
|
||||
|
||||
// Change the rectangle to be empty to cause nothing to be drawn
|
||||
*global_grect_to_use = GRectZero;
|
||||
|
||||
// We'll expect the bitmap we passed into graphics_draw_bitmap_in_rect_processed() in .post
|
||||
// even though nothing will be drawn
|
||||
processor_with_data->expected_bitmap_in_post = *bitmap_to_use;
|
||||
}
|
||||
|
||||
void test_gbitmap_processor__post_function_called_even_if_pre_function_specifies_empty_rect(void) {
|
||||
prv_post_function_called_even_if_pre_function_causes_nothing_to_be_drawn_test(
|
||||
post_function_called_even_if_pre_function_specifies_empty_rect__pre);
|
||||
};
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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/gtypes.h"
|
||||
#include "applib/graphics/gbitmap_pbi.h"
|
||||
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_graphics_context.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_process_manager.h"
|
||||
#include "stubs_passert.h"
|
||||
|
||||
// Stubs
|
||||
///////////////////////
|
||||
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;
|
||||
}
|
||||
|
||||
ResAppNum sys_get_current_resource_num(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint8_t *sys_resource_read_only_bytes(ResAppNum app_num, uint32_t resource_id,
|
||||
size_t *num_bytes_out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Fakes
|
||||
///////////////////////
|
||||
size_t s_resource_size;
|
||||
size_t sys_resource_size(ResAppNum app_num, uint32_t resource_id) {
|
||||
return s_resource_size;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint16_t row_size_bytes;
|
||||
union {
|
||||
uint16_t info_flags;
|
||||
BitmapInfo info;
|
||||
};
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
} FakeBitmapData;
|
||||
|
||||
FakeBitmapData s_fake_bitmap_data;
|
||||
|
||||
size_t sys_resource_load_range(
|
||||
ResAppNum app_num, uint32_t id, uint32_t start_offset, uint8_t *data, size_t num_bytes) {
|
||||
|
||||
BitmapData *bitmap = (BitmapData*) data;
|
||||
*bitmap = (BitmapData) {
|
||||
.row_size_bytes = s_fake_bitmap_data.row_size_bytes,
|
||||
.info_flags = s_fake_bitmap_data.info_flags,
|
||||
.width = s_fake_bitmap_data.width,
|
||||
.height = s_fake_bitmap_data.height
|
||||
};
|
||||
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
|
||||
// Tests
|
||||
///////////////////////
|
||||
|
||||
void test_gbitmap_resource_validation__initialize(void) {
|
||||
s_resource_size = 0;
|
||||
s_fake_bitmap_data = (FakeBitmapData) { 0 };
|
||||
}
|
||||
|
||||
static uint32_t prv_calculate_size(FakeBitmapData *bitmap) {
|
||||
const uint32_t required_size_bytes =
|
||||
offsetof(BitmapData, data) + // header size
|
||||
(bitmap->row_size_bytes * bitmap->height) + // pixel data
|
||||
gbitmap_get_palette_size(bitmap->info.format); // palette data
|
||||
return required_size_bytes;
|
||||
}
|
||||
|
||||
void test_gbitmap_resource_validation__total_size(void) {
|
||||
s_fake_bitmap_data = (FakeBitmapData) {
|
||||
.row_size_bytes = 8,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_1,
|
||||
.width = 8,
|
||||
.height = 1
|
||||
};
|
||||
|
||||
// Set the resource size to be valid.
|
||||
s_resource_size = prv_calculate_size(&s_fake_bitmap_data);
|
||||
|
||||
// We should load it successfully
|
||||
GBitmap bitmap;
|
||||
cl_assert(gbitmap_init_with_resource_system(&bitmap, 0, 0));
|
||||
|
||||
// However, if we corrupt the row_size_bytes field we should fail.
|
||||
s_fake_bitmap_data.row_size_bytes = 12;
|
||||
cl_assert(!gbitmap_init_with_resource_system(&bitmap, 0, 0));
|
||||
|
||||
// Corrupt it the other way, so that there's not enough data
|
||||
s_fake_bitmap_data.row_size_bytes = 4;
|
||||
cl_assert(!gbitmap_init_with_resource_system(&bitmap, 0, 0));
|
||||
|
||||
// Fix it up again
|
||||
s_fake_bitmap_data.row_size_bytes = 8;
|
||||
cl_assert(gbitmap_init_with_resource_system(&bitmap, 0, 0));
|
||||
|
||||
// But now change the palette format to something that requires more space and watch it fail
|
||||
s_fake_bitmap_data.info.format = GBitmapFormat4BitPalette;
|
||||
cl_assert(!gbitmap_init_with_resource_system(&bitmap, 0, 0));
|
||||
|
||||
// But if we have space for the palette, it should pass
|
||||
s_resource_size = prv_calculate_size(&s_fake_bitmap_data);
|
||||
cl_assert(gbitmap_init_with_resource_system(&bitmap, 0, 0));
|
||||
}
|
||||
|
||||
void test_gbitmap_resource_validation__row_size(void) {
|
||||
s_fake_bitmap_data = (FakeBitmapData) {
|
||||
.row_size_bytes = 8,
|
||||
.info.format = GBitmapFormat8Bit,
|
||||
.info.version = GBITMAP_VERSION_1,
|
||||
.width = 8,
|
||||
.height = 1
|
||||
};
|
||||
|
||||
// Set the resource size to be valid.
|
||||
s_resource_size = prv_calculate_size(&s_fake_bitmap_data);
|
||||
|
||||
// We should load it successfully
|
||||
GBitmap bitmap;
|
||||
cl_assert(gbitmap_init_with_resource_system(&bitmap, 0, 0));
|
||||
|
||||
// Vary the width without changing the height to be too large
|
||||
s_fake_bitmap_data.width = 10,
|
||||
cl_assert(!gbitmap_init_with_resource_system(&bitmap, 0, 0));
|
||||
|
||||
// Too small is fine though
|
||||
s_fake_bitmap_data.width = 6,
|
||||
cl_assert(gbitmap_init_with_resource_system(&bitmap, 0, 0));
|
||||
|
||||
// Test with an uneven number of bits and make sure we're rounding correctly
|
||||
s_fake_bitmap_data.info.format = GBitmapFormat1Bit;
|
||||
s_fake_bitmap_data.width = 64;
|
||||
cl_assert(gbitmap_init_with_resource_system(&bitmap, 0, 0));
|
||||
|
||||
s_fake_bitmap_data.info.format = GBitmapFormat1Bit;
|
||||
s_fake_bitmap_data.width = 65;
|
||||
cl_assert(!gbitmap_init_with_resource_system(&bitmap, 0, 0));
|
||||
|
||||
s_fake_bitmap_data.info.format = GBitmapFormat1Bit;
|
||||
s_fake_bitmap_data.width = 63;
|
||||
cl_assert(gbitmap_init_with_resource_system(&bitmap, 0, 0));
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* 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 "applib/graphics/gbitmap_sequence.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <util/size.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
// Test files are Creative Commons 0 (ie. Public Domain) from
|
||||
// http://opengameart.org/content/game-character-blue-flappy-bird-sprite-sheets
|
||||
// http://opengameart.org/content/pixel-puncher-sprites
|
||||
// http://opengameart.org/content/spike-man-monster
|
||||
// http://opengameart.org/content/platformer-baddies
|
||||
// http://opengameart.org/content/greyscale-special-effects-%E2%80%94-various-dimensions-and-flavours
|
||||
// http://opengameart.org/content/bouncing-ball-guy
|
||||
// http://opengameart.org/content/medals-3
|
||||
// http://opengameart.org/content/open-pixel-platformer-tiles-sprites
|
||||
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
#include "applib/graphics/8_bit/framebuffer.c"
|
||||
#elif SCREEN_COLOR_DEPTH_BITS == 1
|
||||
#include "applib/graphics/1_bit/framebuffer.c"
|
||||
#endif
|
||||
|
||||
// Fakes
|
||||
////////////////////////////////////
|
||||
#include "fake_resource_syscalls.h"
|
||||
#include "fake_app_timer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_pebble_tasks.h"
|
||||
#include "stubs_print.h"
|
||||
#include "stubs_queue.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_ui_window.h"
|
||||
|
||||
#define GET_PBI_NAME(x) prv_get_image_name(__func__, x, "pbi")
|
||||
#define GET_APNG_NAME prv_get_image_name(__func__, 0, "apng")
|
||||
|
||||
// Used to work around __func__ not being a string literal (necessary for macro concatenation)
|
||||
static const char *prv_get_image_name(const char* func_name, int index, const char *extension) {
|
||||
char *filename = malloc(PATH_STRING_LENGTH);
|
||||
if (index) {
|
||||
snprintf(filename, PATH_STRING_LENGTH, "%s_%u.%s", func_name, index, extension);
|
||||
} else {
|
||||
snprintf(filename, PATH_STRING_LENGTH, "%s.%s", func_name, extension);
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
// Reference PNGs reside in "tests/test_images/"
|
||||
// and are created at build time, with the files copied to TEST_IMAGES_PATH
|
||||
// and a separate PBI generated by bitmapgen.py from the PNG copied to TEST_IMAGES_PATH
|
||||
|
||||
// Tests APNG file with 2-bit color, 6 frames and loop = infinite
|
||||
// Tests start, stop, reset and loop
|
||||
// Result:
|
||||
// - gbitmaps matches platform decoded APNG
|
||||
void test_gbitmap_sequence__color_2bit_bouncing_ball(void) {
|
||||
#if PLATFROM_SPALDING
|
||||
uint32_t resource_id = sys_resource_load_file_as_resource(TEST_IMAGES_PATH, GET_APNG_NAME);
|
||||
cl_assert(resource_id != UINT32_MAX);
|
||||
|
||||
GBitmapSequence *bitmap_sequence = gbitmap_sequence_create_with_resource(resource_id);
|
||||
cl_assert(bitmap_sequence);
|
||||
|
||||
GBitmap *bitmap = gbitmap_create_blank(gbitmap_sequence_get_bitmap_size(bitmap_sequence),
|
||||
GBitmapFormat8Bit);
|
||||
cl_assert(bitmap);
|
||||
|
||||
gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(1)));
|
||||
|
||||
gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(2)));
|
||||
|
||||
gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(3)));
|
||||
|
||||
gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(4)));
|
||||
|
||||
gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(5)));
|
||||
|
||||
gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(6)));
|
||||
|
||||
// Test loop around
|
||||
gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(1)));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Tests APNG file with 8-bit color, 88 frames of delay 100ms each and loop = 2
|
||||
// Tests gbitmap_sequence_update_bitmap_by_elapsed
|
||||
// Result:
|
||||
// - gbitmaps matches platform decoded APNG
|
||||
void test_gbitmap_sequence__color_8bit_fight(void) {
|
||||
#if PLATFORM_SPALDING
|
||||
bool status = false;
|
||||
uint32_t resource_id = sys_resource_load_file_as_resource(TEST_IMAGES_PATH, GET_APNG_NAME);
|
||||
cl_assert(resource_id != UINT32_MAX);
|
||||
|
||||
GBitmapSequence *bitmap_sequence = gbitmap_sequence_create_with_resource(resource_id);
|
||||
cl_assert(bitmap_sequence);
|
||||
|
||||
GBitmap *bitmap = gbitmap_create_blank(gbitmap_sequence_get_bitmap_size(bitmap_sequence),
|
||||
GBitmapFormat8Bit);
|
||||
cl_assert(bitmap);
|
||||
|
||||
// first frame always shows at the beginning (0 ms)
|
||||
status = gbitmap_sequence_update_bitmap_by_elapsed(bitmap_sequence, bitmap, 0);
|
||||
cl_assert_equal_b(status, true);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(1)));
|
||||
|
||||
// each frame has delay 100ms, so (33 - 1) * 100 ms shows 33rd frame
|
||||
status = gbitmap_sequence_update_bitmap_by_elapsed(bitmap_sequence, bitmap, (33 - 1) * 100);
|
||||
cl_assert_equal_b(status, true);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(33)));
|
||||
|
||||
status = gbitmap_sequence_update_bitmap_by_elapsed(bitmap_sequence, bitmap, (60 - 1) * 100);
|
||||
cl_assert_equal_b(status, true);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(60)));
|
||||
|
||||
// Since we have loop count 2 and 88 frames, see if we can go to elapsed at 2nd loop correctly
|
||||
status = gbitmap_sequence_update_bitmap_by_elapsed(bitmap_sequence, bitmap,
|
||||
88 * 100 + (49 - 1) * 100);
|
||||
cl_assert_equal_b(status, true);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(49)));
|
||||
|
||||
// Since loop only equals 2, make sure that huge elapsed time leaves us at final frame
|
||||
status = gbitmap_sequence_update_bitmap_by_elapsed(bitmap_sequence, bitmap, 12345 * 100);
|
||||
cl_assert_equal_b(status, true);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(88)));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Tests APNG file with 8-bit color, 5 frames and loop = 1
|
||||
// Tests gbitmap_sequence_update_bitmap_by_elapsed with 0 delay frames for 1, 4 and 5
|
||||
// and 2 & 3 have delay 1000ms
|
||||
// Result:
|
||||
// - gbitmaps matches platform decoded APNG
|
||||
void test_gbitmap_sequence__color_8bit_coin(void) {
|
||||
#if PLATFORM_SPALDING
|
||||
uint32_t resource_id = sys_resource_load_file_as_resource(TEST_IMAGES_PATH, GET_APNG_NAME);
|
||||
cl_assert(resource_id != UINT32_MAX);
|
||||
|
||||
GBitmapSequence *bitmap_sequence = gbitmap_sequence_create_with_resource(resource_id);
|
||||
cl_assert(bitmap_sequence);
|
||||
|
||||
GBitmap *bitmap = gbitmap_create_blank(gbitmap_sequence_get_bitmap_size(bitmap_sequence),
|
||||
GBitmapFormat8Bit);
|
||||
cl_assert(bitmap);
|
||||
|
||||
// Since frame 1 has 0 delay, it renders then immediately we render frame 2
|
||||
gbitmap_sequence_update_bitmap_by_elapsed(bitmap_sequence, bitmap, 0);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(2)));
|
||||
|
||||
|
||||
// Frame 2 has delay 1000 ms, so at time 1000 ms, we expect frame 3
|
||||
gbitmap_sequence_update_bitmap_by_elapsed(bitmap_sequence, bitmap, 1 * 1000);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(3)));
|
||||
|
||||
// Frame 3 has delay 1000 ms, frame 4 and 5 have zero delay, so at time 2000 ms == frame 5
|
||||
gbitmap_sequence_update_bitmap_by_elapsed(bitmap_sequence, bitmap, 2 * 1000);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(5)));
|
||||
|
||||
// Since loop only equals 1, make sure that huge elapsed time leaves us at final frame
|
||||
gbitmap_sequence_update_bitmap_by_elapsed(bitmap_sequence, bitmap, 12345 * 1000);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(5)));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Tests APNG file with 8-bit color, 5 frames and loop = 1
|
||||
// Tests gbitmap_sequence_update_bitmap_by_elapsed with 0 delay frames for 1, 4 and 5
|
||||
// and 2 & 3 have delay 1000ms
|
||||
// Result:
|
||||
// - gbitmaps matches platform decoded APNG
|
||||
void test_gbitmap_sequence__color_8bit_coin_round(void) {
|
||||
#if PLATFORM_SPALDING
|
||||
uint32_t resource_id = sys_resource_load_file_as_resource(
|
||||
TEST_IMAGES_PATH, "test_gbitmap_sequence__color_8bit_coin.apng");
|
||||
cl_assert(resource_id != UINT32_MAX);
|
||||
|
||||
GBitmapSequence *bitmap_sequence = gbitmap_sequence_create_with_resource(resource_id);
|
||||
cl_assert(bitmap_sequence);
|
||||
|
||||
GBitmap *bitmap = gbitmap_create_blank(GSize(180, 180), GBitmapFormat8BitCircular);
|
||||
cl_assert(bitmap);
|
||||
|
||||
// Since frame 1 has 0 delay, it renders then immediately we render frame 2
|
||||
gbitmap_sequence_update_bitmap_by_elapsed(bitmap_sequence, bitmap, 0);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(2)));
|
||||
|
||||
// Frame 2 has delay 1000 ms, so at time 1000 ms, we expect frame 3
|
||||
gbitmap_sequence_update_bitmap_by_elapsed(bitmap_sequence, bitmap, 1 * 1000);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(3)));
|
||||
|
||||
// Frame 3 has delay 1000 ms, frame 4 and 5 have zero delay, so at time 2000 ms == frame 5
|
||||
gbitmap_sequence_update_bitmap_by_elapsed(bitmap_sequence, bitmap, 2 * 1000);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(5)));
|
||||
|
||||
// Since loop only equals 1, make sure that huge elapsed time leaves us at final frame
|
||||
gbitmap_sequence_update_bitmap_by_elapsed(bitmap_sequence, bitmap, 12345 * 1000);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(5)));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Tests APNG file with 8-bit color, 5 frames and loop infinite, size 64x64
|
||||
// Tests gbitmap_sequence for bitmap bounds offset and DISPOSE_OP_BACKGROUND
|
||||
// Result:
|
||||
// - gbitmaps matches platform decoded APNG
|
||||
void test_gbitmap_sequence__color_8bit_bounds(void) {
|
||||
#if PLATFORM_SPALDING
|
||||
uint32_t resource_id = sys_resource_load_file_as_resource(TEST_IMAGES_PATH, GET_APNG_NAME);
|
||||
cl_assert(resource_id != UINT32_MAX);
|
||||
|
||||
GRect orig_bounds = GRect(0, 0, 180, 180);
|
||||
int xshift = 71;
|
||||
int yshift = 39;
|
||||
GRect shift_bounds = GRect(xshift, yshift,
|
||||
orig_bounds.size.w - xshift,
|
||||
orig_bounds.size.h - yshift);
|
||||
|
||||
GBitmapSequence *bitmap_sequence = gbitmap_sequence_create_with_resource(resource_id);
|
||||
cl_assert(bitmap_sequence);
|
||||
|
||||
GBitmap *bitmap = gbitmap_create_blank(orig_bounds.size, GBitmapFormat8BitCircular);
|
||||
cl_assert(bitmap);
|
||||
|
||||
// Shift the bounds when updating
|
||||
gbitmap_set_bounds(bitmap, shift_bounds);
|
||||
gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
// set the original bounds to do whole-image comparison
|
||||
gbitmap_set_bounds(bitmap, orig_bounds);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(1)));
|
||||
|
||||
gbitmap_set_bounds(bitmap, shift_bounds);
|
||||
gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
gbitmap_set_bounds(bitmap, orig_bounds);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(2)));
|
||||
|
||||
gbitmap_set_bounds(bitmap, shift_bounds);
|
||||
gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
gbitmap_set_bounds(bitmap, orig_bounds);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(3)));
|
||||
|
||||
gbitmap_set_bounds(bitmap, shift_bounds);
|
||||
gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
gbitmap_set_bounds(bitmap, orig_bounds);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(4)));
|
||||
|
||||
gbitmap_set_bounds(bitmap, shift_bounds);
|
||||
gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
gbitmap_set_bounds(bitmap, orig_bounds);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(5)));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Tests APNG file with 8-bit color, originally broke the updated upng+tinflate code
|
||||
// Result:
|
||||
// - gbitmaps matches platform decoded APNG
|
||||
void test_gbitmap_sequence__color_8bit_yoshi(void) {
|
||||
#if PLATFORM_SPALDING
|
||||
bool status = false;
|
||||
uint32_t resource_id = sys_resource_load_file_as_resource(TEST_IMAGES_PATH, GET_APNG_NAME);
|
||||
cl_assert(resource_id != UINT32_MAX);
|
||||
GBitmapSequence *bitmap_sequence = gbitmap_sequence_create_with_resource(resource_id);
|
||||
GBitmap *bitmap = gbitmap_create_blank(gbitmap_sequence_get_bitmap_size(bitmap_sequence),
|
||||
GBitmapFormat8Bit);
|
||||
|
||||
status = gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
cl_assert_equal_b(status, true);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(1)));
|
||||
|
||||
status = gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
cl_assert_equal_b(status, true);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(2)));
|
||||
|
||||
status = gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
cl_assert_equal_b(status, true);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, GET_PBI_NAME(3)));
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_gbitmap_sequence__1bit_to_1bit_notification(void) {
|
||||
uint32_t resource_id = sys_resource_load_file_as_resource(TEST_IMAGES_PATH, GET_APNG_NAME);
|
||||
cl_assert(resource_id != UINT32_MAX);
|
||||
GBitmapSequence *bitmap_sequence = gbitmap_sequence_create_with_resource(resource_id);
|
||||
GBitmap *bitmap = gbitmap_create_blank(gbitmap_sequence_get_bitmap_size(bitmap_sequence),
|
||||
GBitmapFormat1Bit);
|
||||
|
||||
// We're interested in checking the following frames
|
||||
const int check_frames[] = { 1, 4, 23, 24, 25, 31, 75 };
|
||||
|
||||
int current_frame = 0;
|
||||
for (int i = 0; i < ARRAY_LENGTH(check_frames); ++i) {
|
||||
// Advance to the next frame we're interested in.
|
||||
for (; current_frame < check_frames[i]; ++current_frame) {
|
||||
bool status = gbitmap_sequence_update_bitmap_next_frame(bitmap_sequence, bitmap, NULL);
|
||||
cl_assert_equal_b(status, true);
|
||||
}
|
||||
|
||||
printf("Checking %u\n", current_frame);
|
||||
|
||||
// Don't use GET_PBI_NAME, it doesn't like not using anything other than an integer literal
|
||||
char filename_buffer[128];
|
||||
snprintf(filename_buffer, sizeof(filename_buffer), "%s_%u.pbi", __func__, current_frame);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, filename_buffer));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,815 @@
|
||||
/*
|
||||
* 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(¢er, s_stroke_points, s_path_num_points));
|
||||
cl_assert(prv_compare_points(¢er, 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(¢er, 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(¢er, 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(¢er, 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(¢er, 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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 "util.h" // graphics tests utils
|
||||
|
||||
#include "fake_pbl_malloc.h"
|
||||
#include "fake_resource_syscalls.h"
|
||||
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_compiled_with_legacy2_sdk.h"
|
||||
#include "stubs_graphics_context.h"
|
||||
#include "stubs_gpath.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_memory_layout.h"
|
||||
#include "stubs_passert.h"
|
||||
|
||||
// Stubs
|
||||
void graphics_draw_circle(GContext* ctx, GPoint p, uint16_t radius) {}
|
||||
void graphics_fill_circle(GContext* ctx, GPoint p, uint16_t radius) {}
|
||||
void framebuffer_clear(FrameBuffer* f){}
|
||||
void graphics_context_move_draw_box(GContext* ctx, GPoint offset) {}
|
||||
void graphics_line_draw_precise_stroked(GContext* ctx, GPointPrecise p0, GPointPrecise p1) {}
|
||||
const uint8_t *resource_get_builtin_bytes(ResAppNum app_num, uint32_t resource_id,
|
||||
uint32_t *num_bytes_out) { return NULL; }
|
||||
|
||||
void test_gdraw_command_resources__load_pdci(void) {
|
||||
uint32_t resource_id = sys_resource_load_file_as_resource(TEST_IMAGES_PATH, TEST_PDC_FILE);
|
||||
cl_assert(resource_id != UINT32_MAX);
|
||||
GDrawCommandImage *image = gdraw_command_image_create_with_resource(resource_id);
|
||||
cl_assert(image != NULL);
|
||||
}
|
||||
|
||||
void test_gdraw_command_resources__load_pdcs(void) {
|
||||
uint32_t resource_id = sys_resource_load_file_as_resource(TEST_IMAGES_PATH, TEST_PDC_FILE);
|
||||
cl_assert(resource_id != UINT32_MAX);
|
||||
GDrawCommandSequence *sequence = gdraw_command_sequence_create_with_resource(resource_id);
|
||||
cl_assert(sequence != NULL);
|
||||
}
|
||||
|
||||
// Test that loading an invalid PDC file fails
|
||||
void test_gdraw_command_resources__load_invalid(void) {
|
||||
uint32_t resource_id = sys_resource_load_file_as_resource(TEST_IMAGES_PATH, TEST_PDC_FILE);
|
||||
cl_assert(resource_id != UINT32_MAX);
|
||||
|
||||
// Test Command_Image
|
||||
GDrawCommandImage *image = gdraw_command_image_create_with_resource(resource_id);
|
||||
cl_assert(image == NULL);
|
||||
|
||||
// Test Command Sequence
|
||||
GDrawCommandSequence *sequence = gdraw_command_sequence_create_with_resource(resource_id);
|
||||
cl_assert(sequence == NULL);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* 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_frame.h"
|
||||
#include "applib/graphics/gdraw_command_private.h"
|
||||
#include "applib/graphics/gdraw_command_sequence.h"
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/gpath.h"
|
||||
|
||||
#include "util/size.h"
|
||||
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "stubs_memory_layout.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_syscalls.h"
|
||||
|
||||
// Stubs
|
||||
void graphics_context_set_stroke_color(GContext* ctx, GColor color) {}
|
||||
void graphics_context_set_fill_color(GContext* ctx, GColor color) {}
|
||||
void graphics_context_set_stroke_width(GContext* ctx, uint8_t stroke_width) {}
|
||||
void gpath_draw_stroke(GContext* ctx, GPath* path, bool open) {}
|
||||
void gpath_draw_filled(GContext* ctx, GPath *path) {}
|
||||
void graphics_draw_circle(GContext* ctx, GPoint p, uint16_t radius) {}
|
||||
void graphics_fill_circle(GContext* ctx, GPoint p, uint16_t radius) {}
|
||||
void graphics_context_move_draw_box(GContext* ctx, GPoint offset) {}
|
||||
void graphics_line_draw_precise_stroked(GContext* ctx, GPointPrecise p0, GPointPrecise p1) {}
|
||||
void gpath_fill_precise_internal(GContext *ctx, GPointPrecise *points, size_t num_points) {}
|
||||
void gpath_draw_outline_precise_internal(GContext *ctx, GPointPrecise *points, size_t num_points,
|
||||
bool open) {}
|
||||
typedef uint16_t ResourceId;
|
||||
const uint8_t *resource_get_builtin_bytes(ResAppNum app_num, uint32_t resource_id,
|
||||
uint32_t *num_bytes_out) { return NULL; }
|
||||
|
||||
|
||||
// setup and teardown
|
||||
void test_gdraw_command_sequence__initialize(void) {
|
||||
|
||||
}
|
||||
|
||||
void test_gdraw_command_sequence__cleanup(void) {
|
||||
|
||||
}
|
||||
|
||||
size_t prv_create_test_sequence(GDrawCommandSequence **sequence_ptr) {
|
||||
size_t size = sizeof(GDrawCommandSequence) + (sizeof(GDrawCommandFrame) * 2) +
|
||||
(sizeof(GDrawCommand) * 4) + (sizeof(GPoint) * 9);
|
||||
|
||||
GDrawCommandSequence *sequence = calloc(1, size);
|
||||
*sequence_ptr = sequence;
|
||||
|
||||
*sequence = (GDrawCommandSequence){
|
||||
.version = GDRAW_COMMAND_VERSION,
|
||||
.num_frames = 2,
|
||||
.play_count = 1,
|
||||
};
|
||||
|
||||
GDrawCommandFrame *frame;
|
||||
frame = &sequence->frames[0];
|
||||
*frame = (GDrawCommandFrame) {
|
||||
.duration = 15,
|
||||
};
|
||||
frame->command_list = (GDrawCommandList) {
|
||||
.num_commands = 3
|
||||
};
|
||||
GDrawCommand *command;
|
||||
command = gdraw_command_list_get_command(&frame->command_list, 0);
|
||||
GPoint points1[] = { { 3, 97 }, {5, 5} };
|
||||
*command = (GDrawCommand) {
|
||||
.type = GDrawCommandTypePath,
|
||||
.hidden = false,
|
||||
.stroke_color = GColorRed,
|
||||
.stroke_width = 1,
|
||||
.fill_color = GColorBlue,
|
||||
.path_open = false,
|
||||
.num_points = ARRAY_LENGTH(points1),
|
||||
};
|
||||
memcpy(command->points, points1, sizeof(points1));
|
||||
|
||||
command = gdraw_command_list_get_command(&frame->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(&frame->command_list, 2);
|
||||
GPoint points2[] = { { 6, 7 }, {5, 5}, { 0, 0 } };
|
||||
*command = (GDrawCommand) {
|
||||
.type = GDrawCommandTypePath,
|
||||
.hidden = false,
|
||||
.stroke_color = GColorGreen,
|
||||
.stroke_width = 1,
|
||||
.fill_color = GColorPurple,
|
||||
.path_open = false,
|
||||
.num_points = ARRAY_LENGTH(points2),
|
||||
};
|
||||
memcpy(command->points, points2, sizeof(points2));
|
||||
|
||||
frame = (GDrawCommandFrame *)(command->points + command->num_points);
|
||||
*frame = (GDrawCommandFrame) {
|
||||
.duration = 30,
|
||||
};
|
||||
frame->command_list = (GDrawCommandList) {
|
||||
.num_commands = 1
|
||||
};
|
||||
command = gdraw_command_list_get_command(&frame->command_list, 0);
|
||||
points2[0].x++; // increment x value to distinguish draw command from command in previous frame
|
||||
*command = (GDrawCommand) {
|
||||
.type = GDrawCommandTypePath,
|
||||
.hidden = false,
|
||||
.stroke_color = GColorRed,
|
||||
.stroke_width = 5,
|
||||
.fill_color = GColorBlack,
|
||||
.path_open = false,
|
||||
.num_points = ARRAY_LENGTH(points2),
|
||||
};
|
||||
memcpy(command->points, points2, sizeof(points2));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
// tests
|
||||
void test_gdraw_command_sequence__validate(void) {
|
||||
GDrawCommandSequence *sequence;
|
||||
size_t size = prv_create_test_sequence(&sequence);
|
||||
|
||||
cl_assert_equal_i(size, gdraw_command_sequence_get_data_size(sequence));
|
||||
cl_assert(gdraw_command_sequence_validate(sequence, size));
|
||||
cl_assert(!gdraw_command_sequence_validate(sequence, size - 1));
|
||||
cl_assert(!gdraw_command_sequence_validate(sequence, size + 1));
|
||||
cl_assert(!gdraw_command_sequence_validate(sequence, 0));
|
||||
|
||||
sequence->num_frames = 0;
|
||||
cl_assert(!gdraw_command_sequence_validate(sequence, size));
|
||||
sequence->num_frames = 1;
|
||||
cl_assert(!gdraw_command_sequence_validate(sequence, size));
|
||||
sequence->num_frames = 3;
|
||||
cl_assert(!gdraw_command_sequence_validate(sequence, size));
|
||||
sequence->num_frames = 2;
|
||||
|
||||
sequence->version = 0xFF;
|
||||
cl_assert(!gdraw_command_sequence_validate(sequence, size));
|
||||
|
||||
free(sequence);
|
||||
}
|
||||
|
||||
void test_gdraw_command_sequence__get_frame_by_elapsed(void) {
|
||||
GDrawCommandSequence *sequence;
|
||||
prv_create_test_sequence(&sequence);
|
||||
|
||||
GDrawCommandFrame *frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 0);
|
||||
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 0));
|
||||
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_elapsed(sequence, 14));
|
||||
|
||||
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 15);
|
||||
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 1));;
|
||||
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_elapsed(sequence, 44));
|
||||
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_elapsed(sequence, 45));
|
||||
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_elapsed(sequence, 46));
|
||||
|
||||
// test that frame is skipped when the duration is zero (first frame shown will be the first one
|
||||
// with non-zero duration
|
||||
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 0);
|
||||
frame->duration = 0;
|
||||
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 0);
|
||||
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 1));
|
||||
frame = gdraw_command_sequence_get_frame_by_index(sequence, 0);
|
||||
frame->duration = 15;
|
||||
|
||||
// test that the sequence loops when the play count is greater than 1
|
||||
sequence->play_count = 2;
|
||||
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 45);
|
||||
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 0));
|
||||
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 45 + 15);
|
||||
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 1));
|
||||
|
||||
// test that the sequence loops infinitely when the play count is infinite
|
||||
sequence->play_count = (uint16_t)PLAY_COUNT_INFINITE;
|
||||
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 45 * 5);
|
||||
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 0));
|
||||
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, (45 + 15) * 5);
|
||||
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 1));
|
||||
|
||||
// test that the sequence returns the last frame if the play count is zero
|
||||
sequence->play_count = 0;
|
||||
frame = gdraw_command_sequence_get_frame_by_elapsed(sequence, 1);
|
||||
cl_assert_equal_p(frame, gdraw_command_sequence_get_frame_by_index(sequence, 1));
|
||||
|
||||
free(sequence);
|
||||
}
|
||||
|
||||
void test_gdraw_command_sequence__get_frame_by_index(void) {
|
||||
GDrawCommandSequence *sequence;
|
||||
prv_create_test_sequence(&sequence);
|
||||
|
||||
GDrawCommandFrame *frame = gdraw_command_sequence_get_frame_by_index(sequence, 0);
|
||||
cl_assert_equal_i(frame->duration, 15);
|
||||
cl_assert_equal_i(frame->command_list.num_commands, 3);
|
||||
GDrawCommand *command = gdraw_command_list_get_command(&frame->command_list, 2);
|
||||
cl_assert_equal_i(command->type, GDrawCommandTypePath);
|
||||
cl_assert_equal_i(command->num_points, 3);
|
||||
cl_assert_equal_i(command->stroke_color.argb, GColorGreenARGB8);
|
||||
cl_assert_equal_i(command->fill_color.argb, GColorPurpleARGB8);
|
||||
cl_assert_equal_i(command->points[0].x, 6);
|
||||
|
||||
frame = gdraw_command_sequence_get_frame_by_index(sequence, 1);
|
||||
cl_assert_equal_i(frame->duration, 30);
|
||||
cl_assert_equal_i(frame->command_list.num_commands, 1);
|
||||
command = gdraw_command_list_get_command(&frame->command_list, 0);
|
||||
cl_assert_equal_i(command->type, GDrawCommandTypePath);
|
||||
cl_assert_equal_i(command->num_points, 3);
|
||||
cl_assert_equal_i(command->stroke_color.argb, GColorRedARGB8);
|
||||
cl_assert_equal_i(command->fill_color.argb, GColorBlackARGB8);
|
||||
cl_assert_equal_i(command->points[0].x, 7);
|
||||
|
||||
cl_assert_equal_p(gdraw_command_sequence_get_frame_by_index(sequence, 2), NULL);
|
||||
|
||||
free(sequence);
|
||||
}
|
||||
|
||||
void test_gdraw_command_sequence__clone(void) {
|
||||
cl_assert_equal_p(gdraw_command_sequence_clone(NULL), NULL);
|
||||
|
||||
GDrawCommandSequence *sequence;
|
||||
prv_create_test_sequence(&sequence);
|
||||
|
||||
GDrawCommandSequence *clone = gdraw_command_sequence_clone(sequence);
|
||||
cl_assert(clone != sequence);
|
||||
size_t expected_size = gdraw_command_sequence_get_data_size(sequence);
|
||||
cl_assert_equal_i(gdraw_command_sequence_get_data_size(clone), expected_size);
|
||||
cl_assert_equal_i(0, memcmp(clone, sequence, expected_size));
|
||||
|
||||
free(sequence);
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* 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_private.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/graphics_line.h"
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
#include "applib/ui/animation.h"
|
||||
#include "applib/ui/animation_timing.h"
|
||||
#include "applib/ui/animation_interpolate.h"
|
||||
|
||||
#include "applib/graphics/gdraw_command_transforms.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "test_graphics.h"
|
||||
#include "8bit/test_framebuffer.h"
|
||||
#include "weather_app_resources.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_memory_layout.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_syscalls.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// stubs
|
||||
|
||||
InterpolateInt64Function animation_private_current_interpolate_override(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_gdraw_command_transforms__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_gdraw_command_transforms__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
void test_gdraw_command_transforms__to_square(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
graphics_context_set_fill_color(&ctx, GColorFromHEX(0x55aaff));
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
graphics_fill_rect(&ctx, &ctx.dest_bitmap.bounds);
|
||||
|
||||
GDrawCommandImage *l = weather_app_resource_create_sun();
|
||||
gdraw_command_image_draw(&ctx, l, GPoint(0, 0));
|
||||
|
||||
int32_t dt = ANIMATION_NORMALIZED_MAX / 5;
|
||||
int32_t t = 0;
|
||||
|
||||
l = weather_app_resource_create_sun();
|
||||
gdraw_command_image_attract_to_square(l, t += dt);
|
||||
gdraw_command_image_draw(&ctx, l, GPoint(48, 0));
|
||||
|
||||
l = weather_app_resource_create_sun();
|
||||
gdraw_command_image_attract_to_square(l, t += dt);
|
||||
gdraw_command_image_draw(&ctx, l, GPoint(0, 48));
|
||||
|
||||
l = weather_app_resource_create_sun();
|
||||
gdraw_command_image_attract_to_square(l, t += dt);
|
||||
gdraw_command_image_draw(&ctx, l, GPoint(48, 48));
|
||||
|
||||
l = weather_app_resource_create_sun();
|
||||
gdraw_command_image_attract_to_square(l, t += dt);
|
||||
gdraw_command_image_draw(&ctx, l, GPoint(0, 96));
|
||||
|
||||
l = weather_app_resource_create_sun();
|
||||
gdraw_command_image_attract_to_square(l, t += dt);
|
||||
gdraw_command_image_draw(&ctx, l, GPoint(48, 96));
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "test_gdraw_command_transforms__to_square.8bit.pbi"));
|
||||
}
|
||||
|
||||
// re-enable this "test" to debug per-frame transitions
|
||||
void _test_gdraw_command_transforms__to_square_sequence(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
|
||||
int32_t dt = ANIMATION_NORMALIZED_MAX / 16;
|
||||
int32_t t = 0;
|
||||
|
||||
while (t <= ANIMATION_NORMALIZED_MAX) {
|
||||
graphics_context_set_fill_color(&ctx, GColorFromHEX(0x55aaff));
|
||||
graphics_fill_rect(&ctx, &ctx.dest_bitmap.bounds);
|
||||
GDrawCommandImage *img = weather_app_resource_create_sun();
|
||||
gdraw_command_image_attract_to_square(img, t);
|
||||
gdraw_command_image_draw(&ctx, img, GPoint(48, 96));
|
||||
char fn[20];
|
||||
snprintf(fn, sizeof(fn), "tos_%06d.png", t);
|
||||
tests_write_gbitmap_to_pbi(&ctx.dest_bitmap, fn);
|
||||
free(img);
|
||||
t += dt;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t prv_int_scale_and_translate_to(
|
||||
int16_t value, int16_t size, int16_t from_range, int16_t to_range,
|
||||
int16_t from_min, int16_t to_min, int32_t normalized, InterpolateInt64Function interpolate);
|
||||
|
||||
int64_t prv_default_interpolate(int32_t normalized, int64_t from, int64_t to);
|
||||
|
||||
void test_gdraw_command_transforms__int_scale_to_translate_overflow(void) {
|
||||
InterpolateInt64Function interp = prv_default_interpolate;
|
||||
int y = prv_int_scale_and_translate_to(255, 10, 10, 10, 0, 255, ANIMATION_NORMALIZED_MAX,
|
||||
interp);
|
||||
cl_assert_equal_i(y, 255 + 255);
|
||||
}
|
||||
|
||||
void test_gdraw_command_transforms__int_scale_to_translate_overflow_neg(void) {
|
||||
InterpolateInt64Function interp = prv_default_interpolate;
|
||||
int y = prv_int_scale_and_translate_to(-255, 10, 10, 10, 0, -255, ANIMATION_NORMALIZED_MAX,
|
||||
interp);
|
||||
cl_assert_equal_i(y, -255 + -255);
|
||||
}
|
||||
|
||||
void test_gdraw_command_transforms__int_scale_to_scale_overflow(void) {
|
||||
InterpolateInt64Function interp = prv_default_interpolate;
|
||||
int y = prv_int_scale_and_translate_to(181, 1, 1, 181, 0, 0, ANIMATION_NORMALIZED_MAX,
|
||||
interp);
|
||||
cl_assert_equal_i(y, 181 * 181);
|
||||
}
|
||||
|
||||
void test_gdraw_command_transforms__int_scale_to_scale_overflow_neg(void) {
|
||||
InterpolateInt64Function interp = prv_default_interpolate;
|
||||
int y = prv_int_scale_and_translate_to(-181, 1, 1, 181, 0, 0, ANIMATION_NORMALIZED_MAX,
|
||||
interp);
|
||||
cl_assert_equal_i(y, -181 * 181);
|
||||
}
|
||||
|
||||
void test_gdraw_command_transforms__segmented_scale(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
graphics_context_set_fill_color(&ctx, GColorFromHEX(0x55aaff));
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
graphics_fill_rect(&ctx, &ctx.dest_bitmap.bounds);
|
||||
|
||||
GDrawCommandImage *l = weather_app_resource_create_sun();
|
||||
gdraw_command_image_draw(&ctx, l, GPointZero);
|
||||
|
||||
int32_t dt = ANIMATION_NORMALIZED_MAX / 5;
|
||||
int32_t t = 0;
|
||||
InterpolateInt64Function interp = NULL;
|
||||
|
||||
int16_t s = 48;
|
||||
GRect from = GRect(0, 0, s, s);
|
||||
GRect to = GRect(90, 0, s, s);
|
||||
|
||||
GPointIndexLookup *index_lookup = gdraw_command_list_create_index_lookup_by_distance(
|
||||
gdraw_command_image_get_command_list(l), GPoint(s, s / 2));
|
||||
|
||||
Fixed_S32_16 f = Fixed_S32_16(FIXED_S32_16_ONE.raw_value / 2);
|
||||
|
||||
l = weather_app_resource_create_sun();
|
||||
gdraw_command_image_scale_segmented_to(l, from, to, t += dt, interp, index_lookup, f, false);
|
||||
gdraw_command_image_draw(&ctx, l, GPoint(0, 0));
|
||||
|
||||
l = weather_app_resource_create_sun();
|
||||
gdraw_command_image_scale_segmented_to(l, from, to, t += dt, interp, index_lookup, f, false);
|
||||
gdraw_command_image_draw(&ctx, l, GPoint(0, 48));
|
||||
|
||||
l = weather_app_resource_create_sun();
|
||||
gdraw_command_image_scale_segmented_to(l, from, to, t += dt, interp, index_lookup, f, false);
|
||||
gdraw_command_image_draw(&ctx, l, GPoint(0, 96));
|
||||
|
||||
l = weather_app_resource_create_sun();
|
||||
gdraw_command_image_scale_segmented_to(l, from, to, t += dt, interp, index_lookup, f, false);
|
||||
gdraw_command_image_draw(&ctx, l, GPoint(0, 144));
|
||||
|
||||
l = weather_app_resource_create_sun();
|
||||
gdraw_command_image_scale_segmented_to(l, from, to, t += dt, interp, index_lookup, f, false);
|
||||
gdraw_command_image_draw(&ctx, l, GPoint(0, 192));
|
||||
|
||||
free(index_lookup);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "test_gdraw_command_transforms__segmented_scale.8bit.pbi"));
|
||||
}
|
||||
|
||||
// re-enable this "test" to debug per-frame transitions
|
||||
void _test_gdraw_command_transforms__scale_segmented_sequence(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
|
||||
int32_t dt = ANIMATION_NORMALIZED_MAX / 16;
|
||||
int32_t t = 0;
|
||||
InterpolateInt64Function interp = NULL;
|
||||
|
||||
int16_t s = 48;
|
||||
GRect from = GRect(144 - s * 3 / 4, s / 4, s / 2, s / 2);
|
||||
GRect to = GRect(s / 2, s, 2 * s, 2 * s);
|
||||
|
||||
GDrawCommandImage *img = weather_app_resource_create_cloud();
|
||||
GPointIndexLookup *index_lookup = gdraw_command_list_create_index_lookup_by_distance(
|
||||
gdraw_command_image_get_command_list(img), GPoint(s / 2, s));
|
||||
free(img);
|
||||
|
||||
Fixed_S32_16 f = Fixed_S32_16(FIXED_S32_16_ONE.raw_value / 8);
|
||||
|
||||
while (t <= ANIMATION_NORMALIZED_MAX) {
|
||||
graphics_context_set_fill_color(&ctx, GColorFromHEX(0x55aaff));
|
||||
graphics_fill_rect(&ctx, &ctx.dest_bitmap.bounds);
|
||||
img = weather_app_resource_create_cloud();
|
||||
gdraw_command_image_scale_segmented_to(img, from, to, t, interp, index_lookup, f, false);
|
||||
gdraw_command_image_draw(&ctx, img, GPointZero);
|
||||
char fn[20];
|
||||
snprintf(fn, sizeof(fn), "elo_%06d.png", t);
|
||||
tests_write_gbitmap_to_pbi(&ctx.dest_bitmap, fn);
|
||||
t += dt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include "stubs_compiled_with_legacy2_sdk.h"
|
||||
#include "stubs_app_state.h"
|
||||
|
||||
#define GRAPHICS_FIXTURE_PATH "graphics"
|
||||
#define GRAPHICS_FIXTURE_OUT_PATH ".."
|
||||
|
||||
extern GContext *s_app_state_get_graphics_context;
|
||||
|
||||
static void test_graphics_context_reset(GContext* ctx, FrameBuffer* framebuffer) {
|
||||
graphics_context_init(ctx, framebuffer, GContextInitializationMode_App);
|
||||
framebuffer_clear(framebuffer);
|
||||
}
|
||||
|
||||
static void test_graphics_context_init(GContext* ctx, FrameBuffer* framebuffer) {
|
||||
test_graphics_context_reset(ctx, framebuffer);
|
||||
s_app_state_get_graphics_context = ctx; // needed by app_state_get_graphics_context
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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/gtypes.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_compiled_with_legacy2_sdk.h"
|
||||
#include "stubs_graphics_line.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_logging.h"
|
||||
|
||||
const int FrameBuffer_MaxX = 144;
|
||||
const int FrameBuffer_MaxY = 85;
|
||||
|
||||
// stubs
|
||||
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 cos_lookup(){}
|
||||
void sin_lookup(){}
|
||||
void framebuffer_dirty_all(){}
|
||||
void framebuffer_mark_dirty_rect(){}
|
||||
void graphics_circle_draw_quadrant(GContext* ctx, int x0, int y0, uint16_t radius,
|
||||
GCornerMask quadrant){}
|
||||
void graphics_circle_quadrant_draw_1px_non_aa(GContext* ctx, GPoint p,
|
||||
uint16_t radius, GCornerMask quadrant) {}
|
||||
void graphics_internal_circle_quadrant_fill_aa(GContext* ctx, GPoint p,
|
||||
uint16_t radius, GCornerMask quadrant) {}
|
||||
void graphics_circle_quadrant_draw(GContext* ctx, GPoint p, uint16_t radius,
|
||||
GCornerMask quadrant) {}
|
||||
void graphics_circle_quadrant_fill_non_aa(GContext* ctx, GPoint p,
|
||||
uint16_t radius, GCornerMask quadrant) {}
|
||||
void sys_get_current_resource_num(){}
|
||||
void sys_resource_read_only_bytes(){}
|
||||
void sys_resource_load_range(){}
|
||||
void sys_resource_size(){}
|
||||
int32_t integer_sqrt(int64_t x){ return 0;}
|
||||
|
||||
|
||||
GBitmap framebuffer_get_as_bitmap(FrameBuffer *fb, const GSize *size) {
|
||||
return (GBitmap) {
|
||||
.addr = fb,
|
||||
.row_size_bytes = FrameBuffer_MaxX,
|
||||
.info = (BitmapInfo) {.format = GBitmapFormat8Bit, .version = GBITMAP_VERSION_CURRENT},
|
||||
.bounds = (GRect) { { 0, 0 }, { FrameBuffer_MaxX, FrameBuffer_MaxY } },
|
||||
};
|
||||
}
|
||||
|
||||
// tests
|
||||
void test_graphics_blending__closest_opaque(void) {
|
||||
cl_assert_equal_i(GColorRedARGB8, gcolor_closest_opaque((GColor8){.a= 3, .r = 3}).argb);
|
||||
cl_assert_equal_i(GColorRedARGB8, gcolor_closest_opaque((GColor8){.a= 2, .r = 3}).argb);
|
||||
cl_assert_equal_i(GColorClearARGB8, gcolor_closest_opaque((GColor8){.a= 1, .r = 3}).argb);
|
||||
cl_assert_equal_i(GColorClearARGB8, gcolor_closest_opaque((GColor8){.a= 0, .r = 3}).argb);
|
||||
}
|
||||
|
||||
void test_graphics_blending__ctx_text_color_discards_alpha(void) {
|
||||
GContext ctx = {};
|
||||
graphics_context_set_text_color(&ctx, (GColor8){.a= 3, .r = 3});
|
||||
cl_assert_equal_i(GColorRedARGB8, ctx.draw_state.text_color.argb);
|
||||
graphics_context_set_text_color(&ctx, (GColor8){.a= 2, .r = 3});
|
||||
cl_assert_equal_i(GColorRedARGB8, ctx.draw_state.text_color.argb);
|
||||
graphics_context_set_text_color(&ctx, (GColor8){.a= 1, .r = 3});
|
||||
cl_assert_equal_i(GColorClearARGB8, ctx.draw_state.text_color.argb);
|
||||
graphics_context_set_text_color(&ctx, (GColor8){.a= 0, .r = 3});
|
||||
cl_assert_equal_i(GColorClearARGB8, ctx.draw_state.text_color.argb);
|
||||
}
|
||||
|
||||
void test_graphics_blending__ctx_stroke_color_discards_alpha(void) {
|
||||
GContext ctx = {};
|
||||
graphics_context_set_stroke_color(&ctx, (GColor8){.a= 3, .r = 3});
|
||||
cl_assert_equal_i(GColorRedARGB8, ctx.draw_state.stroke_color.argb);
|
||||
graphics_context_set_stroke_color(&ctx, (GColor8){.a= 2, .r = 3});
|
||||
cl_assert_equal_i(GColorRedARGB8, ctx.draw_state.stroke_color.argb);
|
||||
graphics_context_set_stroke_color(&ctx, (GColor8){.a= 1, .r = 3});
|
||||
cl_assert_equal_i(GColorClearARGB8, ctx.draw_state.stroke_color.argb);
|
||||
graphics_context_set_stroke_color(&ctx, (GColor8){.a= 0, .r = 3});
|
||||
cl_assert_equal_i(GColorClearARGB8, ctx.draw_state.stroke_color.argb);
|
||||
}
|
||||
|
||||
void test_graphics_blending__ctx_fill_color_discards_alpha(void) {
|
||||
GContext ctx = {};
|
||||
graphics_context_set_fill_color(&ctx, (GColor8){.a= 3, .r = 3});
|
||||
cl_assert_equal_i(GColorRedARGB8, ctx.draw_state.fill_color.argb);
|
||||
graphics_context_set_fill_color(&ctx, (GColor8){.a= 2, .r = 3});
|
||||
cl_assert_equal_i(GColorRedARGB8, ctx.draw_state.fill_color.argb);
|
||||
graphics_context_set_fill_color(&ctx, (GColor8){.a= 1, .r = 3});
|
||||
cl_assert_equal_i(GColorClearARGB8, ctx.draw_state.fill_color.argb);
|
||||
graphics_context_set_fill_color(&ctx, (GColor8){.a= 0, .r = 3});
|
||||
cl_assert_equal_i(GColorClearARGB8, ctx.draw_state.fill_color.argb);
|
||||
}
|
||||
@@ -0,0 +1,417 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics_circle.h"
|
||||
#include "util/trig.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "pebble_asserts.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// stubs
|
||||
#include "stubs_process_manager.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_heap.h"
|
||||
|
||||
GBitmap* graphics_capture_frame_buffer(GContext* ctx) {
|
||||
return NULL;
|
||||
}
|
||||
bool graphics_release_frame_buffer(GContext* ctx, GBitmap* buffer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void graphics_draw_pixel(){}
|
||||
void graphics_fill_rect(GContext* ctx, const GRect *rect) {}
|
||||
void graphics_private_draw_horizontal_line(){}
|
||||
void graphics_private_draw_vertical_line(){}
|
||||
void graphics_private_plot_pixel(){}
|
||||
void graphics_private_set_pixel(){}
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
|
||||
static GPointPrecise s_center;
|
||||
static Fixed_S16_3 s_radius;
|
||||
static Fixed_S16_3 s_radius_inner;
|
||||
static Fixed_S16_3 s_radius_outer;
|
||||
static int32_t s_angle_start;
|
||||
static int32_t s_angle_end;
|
||||
|
||||
////////////////////////////
|
||||
|
||||
void test_graphics_circle__initialize(void) {
|
||||
s_center = (GPointPrecise){};
|
||||
s_radius = (Fixed_S16_3){};
|
||||
s_radius_inner = (Fixed_S16_3){};
|
||||
s_radius_outer = (Fixed_S16_3){};
|
||||
}
|
||||
|
||||
void test_graphics_circle__gpoint_from_polar_returns_zero_for_null(void) {
|
||||
const uint16_t radius = 5;
|
||||
const GPoint result = gpoint_from_polar_internal(NULL, radius, 0);
|
||||
cl_assert_equal_gpoint(result, GPointZero);
|
||||
}
|
||||
|
||||
void test_graphics_circle__gpoint_from_polar_returns_correct_points(void) {
|
||||
const uint16_t radius = 5;
|
||||
GPoint result;
|
||||
|
||||
const GPoint origin_center = GPointZero;
|
||||
// 90 degrees should be (5, 0)
|
||||
result = gpoint_from_polar_internal(&origin_center, radius, (TRIG_MAX_ANGLE / 4));
|
||||
cl_assert_equal_gpoint(result, GPoint(5, 0));
|
||||
// 270 (90 * 3) degrees should be (-5, 0)
|
||||
result = gpoint_from_polar_internal(&origin_center, radius, (TRIG_MAX_ANGLE * 3 / 4));
|
||||
cl_assert_equal_gpoint(result, GPoint(-5, 0));
|
||||
|
||||
const GPoint offset_center = GPoint(1, 1);
|
||||
// 90 degrees should be (6, 1)
|
||||
result = gpoint_from_polar_internal(&offset_center, radius, (TRIG_MAX_ANGLE / 4));
|
||||
cl_assert_equal_gpoint(result, GPoint(6, 1));
|
||||
// 270 (90 * 3) degrees should be (-4, 1)
|
||||
result = gpoint_from_polar_internal(&offset_center, radius, (TRIG_MAX_ANGLE * 3 / 4));
|
||||
cl_assert_equal_gpoint(result, GPoint(-4, 1));
|
||||
}
|
||||
|
||||
void test_graphics_circle__gpoint_from_polar_normalizes_input_angles(void) {
|
||||
const uint16_t radius = 5;
|
||||
const GPoint center = GPointZero;
|
||||
|
||||
GPoint result;
|
||||
|
||||
// -180 degrees should be (0, 5)
|
||||
result = gpoint_from_polar_internal(¢er, radius, -(TRIG_MAX_ANGLE / 2));
|
||||
cl_assert_equal_gpoint(result, GPoint(0, 5));
|
||||
|
||||
// -90 degrees should be (-5, 0)
|
||||
result = gpoint_from_polar_internal(¢er, radius, -(TRIG_MAX_ANGLE / 4));
|
||||
cl_assert_equal_gpoint(result, GPoint(-5, 0));
|
||||
|
||||
// -450 degrees (-90 * 5 -> -90) should be (-5, 0)
|
||||
result = gpoint_from_polar_internal(¢er, radius, -(TRIG_MAX_ANGLE * 5 / 4));
|
||||
cl_assert_equal_gpoint(result, GPoint(-5, 0));
|
||||
|
||||
// 450 degrees (90 * 5 -> 90) should be (0, 5)
|
||||
result = gpoint_from_polar_internal(¢er, radius, (TRIG_MAX_ANGLE * 5 / 4));
|
||||
cl_assert_equal_gpoint(result, GPoint(5, 0));
|
||||
|
||||
// 360 degrees (-> 0 degrees) should be (0, -5)
|
||||
result = gpoint_from_polar_internal(¢er, radius, TRIG_MAX_ANGLE);
|
||||
cl_assert_equal_gpoint(result, GPoint(0, -5));
|
||||
|
||||
// -360 degrees (-> 0 degrees) should be (0, -5)
|
||||
result = gpoint_from_polar_internal(¢er, radius, -TRIG_MAX_ANGLE);
|
||||
cl_assert_equal_gpoint(result, GPoint(0, -5));
|
||||
}
|
||||
|
||||
void test_graphics_circle__gpoint_from_polar_correct_scale(void) {
|
||||
// edge cases are covered above, this test only verifies that
|
||||
// the internal implementation correctly scales
|
||||
GPoint result = gpoint_from_polar(GRect(0, 0, 10, 10), GOvalScaleModeFillCircle, 0);
|
||||
cl_assert_equal_gpoint(result, GPoint(4, 0));
|
||||
}
|
||||
|
||||
void test_graphics_circle__grect_centered_from_polar(void) {
|
||||
GOvalScaleMode mode = GOvalScaleModeFillCircle;
|
||||
|
||||
// Basic container rect with GPointZero origin
|
||||
const GRect container_rect1 = GRect(0, 0, 10, 10);
|
||||
const GRect resulting_rect1 = grect_centered_from_polar(container_rect1, mode, 0, GSize(3, 5));
|
||||
cl_assert_equal_grect(resulting_rect1, GRect(3, -2, 3, 5));
|
||||
|
||||
// Container rect is offset from GPointZero
|
||||
const GRect container_rect2 = GRect(2, 2, 4, 4);
|
||||
const GRect resulting_rect2 = grect_centered_from_polar(container_rect2, mode, 0, GSize(2, 4));
|
||||
cl_assert_equal_grect(resulting_rect2, GRect(3, 0, 2, 4));
|
||||
|
||||
// Odd-length width and height for container rect, 180 degree angle
|
||||
const GRect container_rect3 = GRect(2, 2, 5, 5);
|
||||
const GRect resulting_rect3 = grect_centered_from_polar(container_rect3, mode,
|
||||
DEG_TO_TRIGANGLE(180), GSize(2, 4));
|
||||
cl_assert_equal_grect(resulting_rect3, GRect(3, 4, 2, 4));
|
||||
}
|
||||
|
||||
void test_graphics_circle__grect_centered_internal(void) {
|
||||
GPointPrecise p1 = GPointPrecise(0, 0);
|
||||
// GRectZero + standardize
|
||||
cl_assert_equal_grect(GRect(0, 0, 0, 0), grect_centered_internal(&p1, GSize(0, 0)));
|
||||
cl_assert_equal_grect(GRect(0, -1, 1, 2), grect_centered_internal(&p1, GSize(-1, -2)));
|
||||
|
||||
// handles fixed point
|
||||
cl_assert_equal_grect(GRect(-1, -1, 2, 2), grect_centered_internal(&p1, GSize(2, 2)));
|
||||
p1.x.raw_value += FIXED_S16_3_HALF.raw_value;
|
||||
cl_assert_equal_grect(GRect(0, -1, 2, 2), grect_centered_internal(&p1, GSize(2, 2)));
|
||||
|
||||
// Repeat for an offset center point
|
||||
GPointPrecise p2 = GPointPreciseFromGPoint(GPoint(5, 5));
|
||||
|
||||
// GRectZero + standardize
|
||||
cl_assert_equal_grect(GRect(5, 5, 0, 0), grect_centered_internal(&p2, GSize(0, 0)));
|
||||
cl_assert_equal_grect(GRect(5, 4, 1, 2), grect_centered_internal(&p2, GSize(-1, -2)));
|
||||
|
||||
// handles fixed point
|
||||
cl_assert_equal_grect(GRect(4, 4, 2, 2), grect_centered_internal(&p2, GSize(2, 2)));
|
||||
p2.x.raw_value += FIXED_S16_3_HALF.raw_value;
|
||||
cl_assert_equal_grect(GRect(5, 4, 2, 2), grect_centered_internal(&p2, GSize(2, 2)));
|
||||
|
||||
// Repeat for a positive offset center point with 0.5 fractions
|
||||
GPointPrecise p3 = GPointPreciseFromGPoint(GPoint(5, 5));
|
||||
p3.x.raw_value += FIXED_S16_3_HALF.raw_value;
|
||||
p3.y.raw_value += FIXED_S16_3_HALF.raw_value;
|
||||
|
||||
// GRectZero + standardize
|
||||
cl_assert_equal_grect(GRect(6, 6, 0, 0), grect_centered_internal(&p3, GSize(0, 0)));
|
||||
cl_assert_equal_grect(GRect(5, 5, 1, 2), grect_centered_internal(&p3, GSize(-1, -2)));
|
||||
|
||||
// handles fixed point
|
||||
cl_assert_equal_grect(GRect(5, 5, 2, 2), grect_centered_internal(&p3, GSize(2, 2)));
|
||||
p3.x.raw_value += FIXED_S16_3_HALF.raw_value;
|
||||
cl_assert_equal_grect(GRect(5, 5, 2, 2), grect_centered_internal(&p3, GSize(2, 2)));
|
||||
|
||||
// Repeat for a negative offset center point with 0.5 fractions
|
||||
GPointPrecise p4 = GPointPreciseFromGPoint(GPoint(-5, -5));
|
||||
p4.x.raw_value -= FIXED_S16_3_HALF.raw_value;
|
||||
p4.y.raw_value -= FIXED_S16_3_HALF.raw_value;
|
||||
|
||||
// GRectZero + standardize
|
||||
cl_assert_equal_grect(GRect(-5, -5, 0, 0), grect_centered_internal(&p4, GSize(0, 0)));
|
||||
cl_assert_equal_grect(GRect(-6, -6, 1, 2), grect_centered_internal(&p4, GSize(-1, -2)));
|
||||
|
||||
// handles fixed point
|
||||
cl_assert_equal_grect(GRect(-6, -6, 2, 2), grect_centered_internal(&p4, GSize(2, 2)));
|
||||
p4.x.raw_value += FIXED_S16_3_HALF.raw_value;
|
||||
cl_assert_equal_grect(GRect(-6, -6, 2, 2), grect_centered_internal(&p4, GSize(2, 2)));
|
||||
}
|
||||
|
||||
#define cl_assert_fixedS16_3(v, f) \
|
||||
cl_assert_equal_i((v).raw_value, (int)((f) * FIXED_S16_3_ONE.raw_value))
|
||||
|
||||
#define cl_assert_gpoint_precise(p, px, py) \
|
||||
do { \
|
||||
cl_assert_fixedS16_3(p.x, px); \
|
||||
cl_assert_fixedS16_3(p.y, py); \
|
||||
} while(0)
|
||||
|
||||
|
||||
void test_graphics_circle__grect_polar_calc_values_handles_null(void) {
|
||||
GPointPrecise center = {};
|
||||
Fixed_S16_3 radius = {};
|
||||
const GRect r = GRect(0, 0, 3, 5);
|
||||
const GOvalScaleMode mode = GOvalScaleModeFitCircle;
|
||||
|
||||
grect_polar_calc_values(&r, mode, NULL, NULL);
|
||||
grect_polar_calc_values(&r, mode, ¢er, NULL);
|
||||
grect_polar_calc_values(&r, mode, NULL, &radius);
|
||||
|
||||
cl_assert_gpoint_precise(center, 1, 2);
|
||||
cl_assert_fixedS16_3(radius, 1);
|
||||
|
||||
grect_polar_calc_values(NULL, mode, ¢er, &radius);
|
||||
cl_assert_gpoint_precise(center, 1, 2);
|
||||
cl_assert_fixedS16_3(radius, 1);
|
||||
}
|
||||
|
||||
void test_graphics_circle__grect_polar_calc_values_edge_cases(void) {
|
||||
GPointPrecise center = {};
|
||||
Fixed_S16_3 radius = {};
|
||||
const GOvalScaleMode mode = GOvalScaleModeFillCircle;
|
||||
|
||||
GRect r = GRect(0, 5, 0, 0);
|
||||
grect_polar_calc_values(&r, mode, ¢er, &radius);
|
||||
cl_assert_gpoint_precise(center, 0, 5);
|
||||
cl_assert_fixedS16_3(radius, 0);
|
||||
|
||||
// 1 pixel width means radius of 0
|
||||
r = GRect(-1, -5, 1, 1);
|
||||
grect_polar_calc_values(&r, mode, ¢er, &radius);
|
||||
cl_assert_gpoint_precise(center, -1, -5);
|
||||
cl_assert_fixedS16_3(radius, 0);
|
||||
|
||||
// 2 pixel width means: center is 1px from side, 0.5 pixels to center of outer pixels
|
||||
r = GRect(-1, -5, 2, 2);
|
||||
grect_polar_calc_values(&r, mode, ¢er, &radius);
|
||||
cl_assert_gpoint_precise(center, -0.5, -4.5);
|
||||
cl_assert_fixedS16_3(radius, 0.5);
|
||||
}
|
||||
|
||||
void test_graphics_circle__grect_polar_calc_values_standardizes(void) {
|
||||
GPointPrecise center = {};
|
||||
Fixed_S16_3 radius = {};
|
||||
const GOvalScaleMode mode = GOvalScaleModeFitCircle;
|
||||
|
||||
GRect r = GRect(0, 0, 10, 20);
|
||||
grect_polar_calc_values(&r, mode, ¢er, &radius);
|
||||
cl_assert_gpoint_precise(center, 4.5, 9.5);
|
||||
cl_assert_fixedS16_3(radius, 4.5);
|
||||
|
||||
r = GRect(0, 0, -10, -20);
|
||||
grect_polar_calc_values(&r, mode, ¢er, &radius);
|
||||
cl_assert_gpoint_precise(center, -5.5, -10.5);
|
||||
cl_assert_fixedS16_3(radius, 4.5);
|
||||
}
|
||||
|
||||
void test_graphics_circle__grect_polar_calc_values_square(void) {
|
||||
GPointPrecise center = {};
|
||||
Fixed_S16_3 radius = {};
|
||||
GRect r = GRect(0, 0, 5, 5);
|
||||
GOvalScaleMode mode = GOvalScaleModeFitCircle; // irrelevant as we deal with squares
|
||||
|
||||
grect_polar_calc_values(&r, mode, ¢er, &radius);
|
||||
cl_assert_gpoint_precise(center, 2, 2);
|
||||
cl_assert_fixedS16_3(radius, 2);
|
||||
|
||||
r = GRect(0, 0, 6, 6);
|
||||
grect_polar_calc_values(&r, mode, ¢er, &radius);
|
||||
cl_assert_gpoint_precise(center, 2.5, 2.5);
|
||||
cl_assert_fixedS16_3(radius, 2.5);
|
||||
|
||||
r = GRect(0, 0, 10, 10);
|
||||
grect_polar_calc_values(&r, mode, ¢er, &radius);
|
||||
cl_assert_gpoint_precise(center, 4.5, 4.5);
|
||||
cl_assert_fixedS16_3(radius, 4.5);
|
||||
|
||||
r = GRect(1, 1, 9, 9);
|
||||
grect_polar_calc_values(&r, mode, ¢er, &radius);
|
||||
cl_assert_gpoint_precise(center, 5, 5);
|
||||
cl_assert_fixedS16_3(radius, 4);
|
||||
|
||||
r = GRect(2, 2, 8, 8);
|
||||
grect_polar_calc_values(&r, mode, ¢er, &radius);
|
||||
cl_assert_gpoint_precise(center, 5.5, 5.5);
|
||||
cl_assert_fixedS16_3(radius, 3.5);
|
||||
}
|
||||
|
||||
void test_graphics_circle__grect_polar_calc_values_mode(void) {
|
||||
GPointPrecise center = {};
|
||||
Fixed_S16_3 radius = {};
|
||||
GRect r = GRect(0, 0, 144, 168);
|
||||
|
||||
grect_polar_calc_values(&r, GOvalScaleModeFitCircle, ¢er, &radius);
|
||||
cl_assert_gpoint_precise(center, 144 / 2 - 0.5, 168 / 2 - 0.5);
|
||||
cl_assert_fixedS16_3(radius, 144 / 2 - 0.5);
|
||||
|
||||
grect_polar_calc_values(&r, GOvalScaleModeFillCircle, ¢er, &radius);
|
||||
cl_assert_gpoint_precise(center, 144 / 2 - 0.5, 168 / 2 - 0.5);
|
||||
cl_assert_fixedS16_3(radius, 168 / 2 - 0.5);
|
||||
}
|
||||
|
||||
void graphics_draw_arc_precise_internal(GContext *ctx, GPointPrecise center,
|
||||
Fixed_S16_3 radius,
|
||||
int32_t angle_start, int32_t angle_end) {
|
||||
s_center = center;
|
||||
s_radius = radius;
|
||||
}
|
||||
|
||||
void test_graphics_circle__draw_arc(void) {
|
||||
cl_assert_gpoint_precise(s_center, 0, 0);
|
||||
cl_assert_fixedS16_3(s_radius, 0);
|
||||
|
||||
graphics_draw_arc(NULL, GRect(0, 0, 10, 12), GOvalScaleModeFitCircle, 0, 0);
|
||||
cl_assert_gpoint_precise(s_center, 4.5, 5.5);
|
||||
cl_assert_fixedS16_3(s_radius, 4.5);
|
||||
}
|
||||
|
||||
void test_graphics_circle__fill_oval(void) {
|
||||
cl_assert_gpoint_precise(s_center, 0, 0);
|
||||
cl_assert_fixedS16_3(s_radius_inner, 0);
|
||||
cl_assert_fixedS16_3(s_radius_outer, 0);
|
||||
cl_assert_equal_i(s_angle_start, 0);
|
||||
cl_assert_equal_i(s_angle_end, 0);
|
||||
|
||||
graphics_fill_oval(NULL, GRect(0, 0, 10, 12), GOvalScaleModeFitCircle);
|
||||
|
||||
cl_assert_gpoint_precise(s_center, 4.5, 5.5);
|
||||
cl_assert_fixedS16_3(s_radius_outer, 4.5);
|
||||
cl_assert(s_radius_inner.integer <= 0);
|
||||
cl_assert_equal_i(s_angle_start, 0);
|
||||
cl_assert_equal_i(s_angle_end, TRIG_MAX_ANGLE);
|
||||
|
||||
graphics_fill_oval(NULL, GRect(10, 12, -10, -12), GOvalScaleModeFitCircle);
|
||||
|
||||
cl_assert_gpoint_precise(s_center, 4.5, 5.5);
|
||||
cl_assert_fixedS16_3(s_radius_outer, 4.5);
|
||||
cl_assert(s_radius_inner.integer <= 0);
|
||||
cl_assert_equal_i(s_angle_start, 0);
|
||||
cl_assert_equal_i(s_angle_end, TRIG_MAX_ANGLE);
|
||||
|
||||
graphics_fill_oval(NULL, GRect(0, 0, 0, 0), GOvalScaleModeFillCircle);
|
||||
cl_assert_gpoint_precise(s_center, 0.0, 0.0);
|
||||
cl_assert_fixedS16_3(s_radius_outer, 0.0);
|
||||
cl_assert(s_radius_inner.integer <= 0);
|
||||
cl_assert_equal_i(s_angle_start, 0);
|
||||
cl_assert_equal_i(s_angle_end, TRIG_MAX_ANGLE);
|
||||
}
|
||||
|
||||
void graphics_fill_radial_precise_internal(GContext *ctx, GPointPrecise center,
|
||||
Fixed_S16_3 radius_inner, Fixed_S16_3 radius_outer,
|
||||
int32_t angle_start, int32_t angle_end) {
|
||||
s_center = center;
|
||||
s_radius_inner = radius_inner;
|
||||
s_radius_outer = radius_outer;
|
||||
s_angle_start = angle_start;
|
||||
s_angle_end = angle_end;
|
||||
}
|
||||
|
||||
|
||||
void test_graphics_circle__fill_radial(void) {
|
||||
GContext *ctx = (GContext *)&ctx;
|
||||
cl_assert_gpoint_precise(s_center, 0, 0);
|
||||
cl_assert_fixedS16_3(s_radius_inner, 0);
|
||||
cl_assert_fixedS16_3(s_radius_outer, 0);
|
||||
|
||||
graphics_fill_radial(NULL, GRect(0, 0, 10, 12), GOvalScaleModeFitCircle, 3, 0, 0);
|
||||
cl_assert_gpoint_precise(s_center, 4.5, 5.5);
|
||||
cl_assert_fixedS16_3(s_radius_outer, 4.5);
|
||||
cl_assert_fixedS16_3(s_radius_inner, 1.5);
|
||||
}
|
||||
|
||||
void test_graphics_circle__DEG_TO_TRIGANGLE(void) {
|
||||
cl_assert_equal_i(DEG_TO_TRIGANGLE(720), TRIG_MAX_ANGLE * 2);
|
||||
cl_assert_equal_i(DEG_TO_TRIGANGLE(-720), -TRIG_MAX_ANGLE * 2);
|
||||
|
||||
cl_assert_equal_i(DEG_TO_TRIGANGLE(360), TRIG_MAX_ANGLE);
|
||||
cl_assert_equal_i(DEG_TO_TRIGANGLE(-360), -TRIG_MAX_ANGLE);
|
||||
|
||||
cl_assert_equal_i(DEG_TO_TRIGANGLE(180), TRIG_PI);
|
||||
cl_assert_equal_i(DEG_TO_TRIGANGLE(-180), -TRIG_PI);
|
||||
|
||||
cl_assert_equal_i(DEG_TO_TRIGANGLE(90), TRIG_PI / 2);
|
||||
cl_assert_equal_i(DEG_TO_TRIGANGLE(-90), -TRIG_PI / 2);
|
||||
|
||||
cl_assert_equal_i(DEG_TO_TRIGANGLE(0), 0);
|
||||
}
|
||||
|
||||
void test_graphics_circle__TRIGANGLE_TO_DEG(void) {
|
||||
cl_assert_equal_i(TRIGANGLE_TO_DEG(TRIG_MAX_ANGLE * 2), 720);
|
||||
cl_assert_equal_i(TRIGANGLE_TO_DEG(-TRIG_MAX_ANGLE * 2), -720);
|
||||
|
||||
cl_assert_equal_i(TRIGANGLE_TO_DEG(TRIG_MAX_ANGLE), 360);
|
||||
cl_assert_equal_i(TRIGANGLE_TO_DEG(-TRIG_MAX_ANGLE), -360);
|
||||
|
||||
cl_assert_equal_i(TRIGANGLE_TO_DEG(TRIG_PI / 2), 90);
|
||||
cl_assert_equal_i(TRIGANGLE_TO_DEG(-TRIG_PI / 2), -90);
|
||||
|
||||
cl_assert_equal_i(TRIGANGLE_TO_DEG(TRIG_MAX_ANGLE / 2), 180);
|
||||
cl_assert_equal_i(TRIGANGLE_TO_DEG(-TRIG_MAX_ANGLE / 2), -180);
|
||||
|
||||
cl_assert_equal_i(TRIGANGLE_TO_DEG(TRIG_PI), 180);
|
||||
cl_assert_equal_i(TRIGANGLE_TO_DEG(-TRIG_PI), -180);
|
||||
|
||||
cl_assert_equal_i(TRIGANGLE_TO_DEG(0), 0);
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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 "applib/graphics/gtypes.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_process_manager.h"
|
||||
|
||||
void test_graphics_colors__black(void) {
|
||||
cl_assert(GColorBlack.r == 0b00);
|
||||
cl_assert(GColorBlack.g == 0b00);
|
||||
cl_assert(GColorBlack.b == 0b00);
|
||||
cl_assert(GColorBlack.a == 0b11);
|
||||
}
|
||||
|
||||
void test_graphics_colors__white(void) {
|
||||
cl_assert(GColorWhite.r == 0b11);
|
||||
cl_assert(GColorWhite.g == 0b11);
|
||||
cl_assert(GColorWhite.b == 0b11);
|
||||
cl_assert(GColorWhite.a == 0b11);
|
||||
}
|
||||
|
||||
void test_graphics_colors__red(void) {
|
||||
cl_assert(GColorRed.r == 0b11);
|
||||
cl_assert(GColorRed.g == 0b00);
|
||||
cl_assert(GColorRed.b == 0b00);
|
||||
cl_assert(GColorRed.a == 0b11);
|
||||
}
|
||||
|
||||
void test_graphics_colors__green(void) {
|
||||
cl_assert(GColorGreen.r == 0b00);
|
||||
cl_assert(GColorGreen.g == 0b11);
|
||||
cl_assert(GColorGreen.b == 0b00);
|
||||
cl_assert(GColorGreen.a == 0b11);
|
||||
}
|
||||
|
||||
void test_graphics_colors__blue(void) {
|
||||
cl_assert(GColorBlue.r == 0b00);
|
||||
cl_assert(GColorBlue.g == 0b00);
|
||||
cl_assert(GColorBlue.b == 0b11);
|
||||
cl_assert(GColorBlue.a == 0b11);
|
||||
}
|
||||
|
||||
void test_graphics_colors__equal(void) {
|
||||
cl_assert(gcolor_equal(GColorBlue, GColorBlue));
|
||||
cl_assert(gcolor_equal(GColorRed, GColorRed));
|
||||
cl_assert(!gcolor_equal(GColorRed, GColorBlue));
|
||||
|
||||
// Two colors with zero alpha values should be equal regardless of what their RGB channels are
|
||||
GColor8 color1 = GColorBlue;
|
||||
color1.a = 0;
|
||||
GColor8 color2 = GColorRed;
|
||||
color2.a = 0;
|
||||
cl_assert(gcolor_equal(color1, color2));
|
||||
|
||||
// But two colors with semi-transparent alpha values should not be equal if their RGB channels
|
||||
// don't match
|
||||
color1.a = 1;
|
||||
color2.a = 1;
|
||||
cl_assert(!gcolor_equal(color1, color2));
|
||||
}
|
||||
|
||||
void test_graphics_colors__equal__deprecated(void) {
|
||||
cl_assert(gcolor_equal__deprecated(GColorBlue, GColorBlue));
|
||||
cl_assert(gcolor_equal__deprecated(GColorRed, GColorRed));
|
||||
cl_assert(!gcolor_equal__deprecated(GColorRed, GColorBlue));
|
||||
|
||||
// Check the behavior of the deprecated function (which is incorrect) that two colors with
|
||||
// zero alpha values but different RGB values are not considered equal
|
||||
GColor8 color1 = GColorBlue;
|
||||
color1.a = 0;
|
||||
GColor8 color2 = GColorRed;
|
||||
color2.a = 0;
|
||||
cl_assert(!gcolor_equal__deprecated(color1, color2));
|
||||
}
|
||||
|
||||
void test_graphics_colors__inverted_readable_color(void) {
|
||||
GColor8 (*fun)(GColor8) = gcolor_legible_over;
|
||||
|
||||
// transparent colors result in transparen - who has a better idea?
|
||||
cl_assert_equal_i(GColorClearARGB8, fun(GColorClear).argb);
|
||||
|
||||
// // obvious cases
|
||||
cl_assert_equal_i(GColorWhiteARGB8, fun(GColorBlack).argb);
|
||||
cl_assert_equal_i(GColorBlackARGB8, fun(GColorWhite).argb);
|
||||
|
||||
// // expectation as derived from Appfaces_all.psd
|
||||
cl_assert_equal_i(GColorWhiteARGB8, fun(GColorFromHEX(0xff0000)).argb);
|
||||
cl_assert_equal_i(GColorBlackARGB8, fun(GColorFromHEX(0x00aaff)).argb);
|
||||
cl_assert_equal_i(GColorWhiteARGB8, fun(GColorFromHEX(0xaa0055)).argb);
|
||||
cl_assert_equal_i(GColorBlackARGB8, fun(GColorFromHEX(0x55aa55)).argb);
|
||||
cl_assert_equal_i(GColorBlackARGB8, fun(GColorFromHEX(0xff5555)).argb);
|
||||
cl_assert_equal_i(GColorWhiteARGB8, fun(GColorFromHEX(0x0055aa)).argb);
|
||||
cl_assert_equal_i(GColorBlackARGB8, fun(GColorFromHEX(0xff5500)).argb);
|
||||
cl_assert_equal_i(GColorBlackARGB8, fun(GColorFromHEX(0xaaaaaa)).argb);
|
||||
|
||||
// // expectation as derived from Appfaces_generic_colors.psd
|
||||
cl_assert_equal_i(GColorWhiteARGB8, fun(GColorFromHEX(0x0000aa)).argb);
|
||||
cl_assert_equal_i(GColorWhiteARGB8, fun(GColorFromHEX(0x005500)).argb);
|
||||
cl_assert_equal_i(GColorWhiteARGB8, fun(GColorFromHEX(0x550055)).argb);
|
||||
cl_assert_equal_i(GColorWhiteARGB8, fun(GColorFromHEX(0xaa0000)).argb);
|
||||
|
||||
// contradiction with previous case - oh dear...
|
||||
// cl_assert_equal_i(GColorWhiteARGB8, fun(GColorFromHEX(0xff5500)).argb);
|
||||
|
||||
cl_assert_equal_i(GColorWhiteARGB8, fun(GColorFromHEX(0xaa5500)).argb);
|
||||
}
|
||||
|
||||
void test_graphics_colors__grayscale_colors(void) {
|
||||
cl_assert_equal_i(GColorClearARGB8, gcolor_get_grayscale(GColorClear).argb);
|
||||
cl_assert_equal_i(GColorWhiteARGB8, gcolor_get_grayscale(GColorWhite).argb);
|
||||
cl_assert_equal_i(GColorBlackARGB8, gcolor_get_grayscale(GColorBlack).argb);
|
||||
cl_assert_equal_i(GColorLightGrayARGB8, gcolor_get_grayscale(GColorLightGray).argb);
|
||||
cl_assert_equal_i(GColorDarkGrayARGB8, gcolor_get_grayscale(GColorDarkGray).argb);
|
||||
cl_assert_equal_i(GColorWhiteARGB8, gcolor_get_grayscale(GColorYellow).argb);
|
||||
cl_assert_equal_i(GColorBlackARGB8, gcolor_get_grayscale(GColorBlue).argb);
|
||||
cl_assert_equal_i(GColorLightGrayARGB8, gcolor_get_grayscale(GColorTiffanyBlue).argb);
|
||||
cl_assert_equal_i(GColorDarkGrayARGB8, gcolor_get_grayscale(GColorOrange).argb);
|
||||
}
|
||||
|
||||
void test_graphics_colors__bw_colors(void) {
|
||||
cl_assert_equal_i(GColorClearARGB8, gcolor_get_bw(GColorClear).argb);
|
||||
cl_assert_equal_i(GColorWhiteARGB8, gcolor_get_bw(GColorWhite).argb);
|
||||
cl_assert_equal_i(GColorBlackARGB8, gcolor_get_bw(GColorBlack).argb);
|
||||
cl_assert_equal_i(GColorWhiteARGB8, gcolor_get_bw(GColorLightGray).argb);
|
||||
cl_assert_equal_i(GColorBlackARGB8, gcolor_get_bw(GColorDarkGray).argb);
|
||||
cl_assert_equal_i(GColorWhiteARGB8, gcolor_get_bw(GColorYellow).argb);
|
||||
cl_assert_equal_i(GColorBlackARGB8, gcolor_get_bw(GColorBlue).argb);
|
||||
cl_assert_equal_i(GColorWhiteARGB8, gcolor_get_bw(GColorTiffanyBlue).argb);
|
||||
cl_assert_equal_i(GColorBlackARGB8, gcolor_get_bw(GColorOrange).argb);
|
||||
}
|
||||
|
||||
static void prv_test_get_luminance(GColor8 color_to_test, GColor8Component expected_luminance) {
|
||||
cl_assert_equal_i(gcolor_get_luminance(color_to_test), expected_luminance);
|
||||
// Test that alpha doesn't affect luminance
|
||||
GColor8 semitransparent_color = color_to_test;
|
||||
semitransparent_color.a = 1;
|
||||
cl_assert_equal_i(gcolor_get_luminance(semitransparent_color), expected_luminance);
|
||||
GColor8 transparent_color = color_to_test;
|
||||
transparent_color.a = 0;
|
||||
cl_assert_equal_i(gcolor_get_luminance(transparent_color), expected_luminance);
|
||||
}
|
||||
|
||||
void test_graphics_colors__get_luminance(void) {
|
||||
prv_test_get_luminance(GColorBlack, 0);
|
||||
prv_test_get_luminance(GColorWhite, 3);
|
||||
prv_test_get_luminance(GColorYellow, 3);
|
||||
prv_test_get_luminance(GColorRed, 2);
|
||||
prv_test_get_luminance(GColorBlue, 1);
|
||||
}
|
||||
|
||||
void test_graphics_colors__tint_luminance_lookup_table_init(void) {
|
||||
GColor8 tint_color = GColorClear;
|
||||
GColor8 lookup_table_out[GCOLOR8_COMPONENT_NUM_VALUES];
|
||||
|
||||
// Passing in NULL for lookup_table_out should assert
|
||||
cl_assert_passert(gcolor_tint_luminance_lookup_table_init(tint_color, NULL));
|
||||
|
||||
// Setting the tint color to black should result in a gradient from black to white
|
||||
tint_color = GColorBlack;
|
||||
gcolor_tint_luminance_lookup_table_init(tint_color, lookup_table_out);
|
||||
cl_assert(gcolor_equal(lookup_table_out[0], GColorBlack));
|
||||
cl_assert(gcolor_equal(lookup_table_out[1], GColorDarkGray));
|
||||
cl_assert(gcolor_equal(lookup_table_out[2], GColorLightGray));
|
||||
cl_assert(gcolor_equal(lookup_table_out[3], GColorWhite));
|
||||
|
||||
// Setting the tint color to blue should result in a gradient from blue to yellow
|
||||
tint_color = GColorBlue;
|
||||
gcolor_tint_luminance_lookup_table_init(tint_color, lookup_table_out);
|
||||
cl_assert(gcolor_equal(lookup_table_out[0], GColorBlue));
|
||||
cl_assert(gcolor_equal(lookup_table_out[1], GColorLiberty));
|
||||
cl_assert(gcolor_equal(lookup_table_out[2], GColorBrass));
|
||||
cl_assert(gcolor_equal(lookup_table_out[3], GColorYellow));
|
||||
|
||||
// lookup_table_out's entries should all preserve the alpha of tint_color
|
||||
tint_color = GColorBlack;
|
||||
tint_color.a = 1;
|
||||
gcolor_tint_luminance_lookup_table_init(tint_color, lookup_table_out);
|
||||
for (GColor8Component i = 0; i < GCOLOR8_COMPONENT_NUM_VALUES; i++) {
|
||||
cl_assert_equal_i(lookup_table_out[i].a, tint_color.a);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_test_tint_using_luminance_and_perform_lookup_using_color_luminance(
|
||||
GColor8 src_color, GColor8 tint_color, GColor8 expected_result_color) {
|
||||
GColor8 lookup_table[GCOLOR8_COMPONENT_NUM_VALUES];
|
||||
gcolor_tint_luminance_lookup_table_init(tint_color, lookup_table);
|
||||
cl_assert(gcolor_equal(
|
||||
gcolor_perform_lookup_using_color_luminance_and_multiply_alpha(src_color, lookup_table),
|
||||
expected_result_color));
|
||||
cl_assert(gcolor_equal(gcolor_tint_using_luminance_and_multiply_alpha(src_color, tint_color),
|
||||
expected_result_color));
|
||||
}
|
||||
|
||||
void test_graphics_colors__tint_using_luminance_and_perform_lookup_using_color_luminance(void) {
|
||||
// Passing in NULL for lookup_table should assert
|
||||
cl_assert_passert(gcolor_perform_lookup_using_color_luminance_and_multiply_alpha(GColorRed,
|
||||
NULL));
|
||||
|
||||
// A src_color of yellow should have a luminance that picks white from lookup_table
|
||||
// initialized with a tint_color of black
|
||||
prv_test_tint_using_luminance_and_perform_lookup_using_color_luminance(GColorYellow,
|
||||
GColorBlack,
|
||||
GColorWhite);
|
||||
|
||||
// A src_color of red should have a luminance that picks light gray from lookup_table initialized
|
||||
// with a tint_color of black
|
||||
prv_test_tint_using_luminance_and_perform_lookup_using_color_luminance(GColorRed,
|
||||
GColorBlack,
|
||||
GColorLightGray);
|
||||
|
||||
// A src_color of blue should have a luminance that picks dark gray from lookup_table
|
||||
// initialized with a tint_color of black
|
||||
prv_test_tint_using_luminance_and_perform_lookup_using_color_luminance(GColorBlue,
|
||||
GColorBlack,
|
||||
GColorDarkGray);
|
||||
|
||||
// A src_color of black should have a luminance that picks black from lookup_table initialized
|
||||
// with a tint_color of black
|
||||
prv_test_tint_using_luminance_and_perform_lookup_using_color_luminance(GColorBlack,
|
||||
GColorBlack,
|
||||
GColorBlack);
|
||||
}
|
||||
|
||||
void test_graphics_colors__component_multiply(void) {
|
||||
const GColor8Component max_component_value = GCOLOR8_COMPONENT_NUM_VALUES - 1;
|
||||
|
||||
// 0% multiplied by anything is 0%
|
||||
for (GColor8Component i = 0; i <= max_component_value; i++) {
|
||||
cl_assert_equal_i(gcolor_component_multiply(i, 0), 0);
|
||||
cl_assert_equal_i(gcolor_component_multiply(0, i), 0);
|
||||
}
|
||||
|
||||
// Associative property (excluding 0% since we already test it above)
|
||||
for (GColor8Component i = 1; i <= max_component_value; i++) {
|
||||
for (GColor8Component j = 1; j <= max_component_value; j++) {
|
||||
cl_assert_equal_i(gcolor_component_multiply(i, j), gcolor_component_multiply(j, i));
|
||||
}
|
||||
}
|
||||
|
||||
// A few specific combinations
|
||||
cl_assert_equal_i(gcolor_component_multiply(3, 3), 3);
|
||||
cl_assert_equal_i(gcolor_component_multiply(3, 2), 2);
|
||||
cl_assert_equal_i(gcolor_component_multiply(2, 2), 1);
|
||||
cl_assert_equal_i(gcolor_component_multiply(1, 3), 1);
|
||||
cl_assert_equal_i(gcolor_component_multiply(2, 1), 1);
|
||||
}
|
||||
@@ -0,0 +1,847 @@
|
||||
/*
|
||||
* 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 "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "util/attributes.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "${BIT_DEPTH_NAME}/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
// Definitions
|
||||
#define SW_EVEN 4
|
||||
#define SW_ODD 5
|
||||
|
||||
// Includes all possible fields for all shapes
|
||||
// The fields that are not needed for a particular shape will be zeroed out.
|
||||
typedef struct PACKED {
|
||||
char func[30];
|
||||
GContext ctx;
|
||||
GPoint p0;
|
||||
GPoint p1;
|
||||
GRect r0;
|
||||
uint16_t radius;
|
||||
uint8_t quadrant;
|
||||
GCornerMask corner_mask;
|
||||
int16_t major_axis_offset;
|
||||
Fixed_S16_3 offset_start;
|
||||
Fixed_S16_3 offset_stop;
|
||||
bool anti_aliased;
|
||||
uint16_t brightness;
|
||||
GColor color;
|
||||
GBitmap fb;
|
||||
} ArgsForMock;
|
||||
|
||||
static ArgsForMock s_last_args_for_mock;
|
||||
static GContext context;
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
/////////////////////////////////////
|
||||
/// FUNCTION OVERRIDES
|
||||
/////////////////////////////////////
|
||||
void graphics_line_draw_1px_non_aa(GContext* ctx, GPoint p0, GPoint p1) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.p0 = p0,
|
||||
.p1 = p1
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void graphics_line_draw_1px_aa(GContext* ctx, GPoint p0, GPoint p1) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.p0 = p0,
|
||||
.p1 = p1
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void graphics_line_draw_stroked_aa(GContext* ctx, GPoint p0, GPoint p1, uint8_t stroke_width) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.p0 = p0,
|
||||
.p1 = p1
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void graphics_line_draw_stroked_non_aa(GContext* ctx, GPoint p0, GPoint p1, uint8_t stroke_width) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.p0 = p0,
|
||||
.p1 = p1
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void prv_fill_rect_non_aa(GContext* ctx, const GRect *rect, uint16_t radius,
|
||||
GCornerMask corner_mask, GColor fill_color) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.r0 = *rect,
|
||||
.radius = radius,
|
||||
.corner_mask = corner_mask
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void prv_fill_rect_aa(GContext* ctx, const GRect *rect, uint16_t radius,
|
||||
GCornerMask corner_mask, GColor fill_color) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.r0 = *rect,
|
||||
.radius = radius,
|
||||
.corner_mask = corner_mask
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void prv_draw_rect(GContext *ctx, const GRect *rect) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.r0 = *rect,
|
||||
.radius = 0,
|
||||
.corner_mask = GCornerNone
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void prv_draw_rect_aa_stroked(GContext *ctx, const GRect *rect, uint8_t stroke_width) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.r0 = *rect,
|
||||
.radius = 0,
|
||||
.corner_mask = GCornerNone
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void prv_draw_rect_stroked(GContext *ctx, const GRect *rect, uint8_t stroke_width) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.r0 = *rect,
|
||||
.radius = 0,
|
||||
.corner_mask = GCornerNone
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void graphics_circle_draw_1px_non_aa(GContext* ctx, GPoint p, uint16_t radius) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.p0 = p,
|
||||
.radius = radius
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void graphics_circle_draw_1px_aa(GContext* ctx, GPoint p, uint16_t radius) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.p0 = p,
|
||||
.radius = radius
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void graphics_circle_draw_stroked_aa(GContext* ctx, GPoint p, uint16_t radius, uint8_t stroke_width) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.p0 = p,
|
||||
.radius = radius
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void graphics_circle_draw_stroked_non_aa(GContext* ctx, GPoint p, uint16_t radius, uint8_t stroke_width) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.p0 = p,
|
||||
.radius = radius
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void graphics_internal_circle_quadrant_fill_aa(GContext* ctx, GPoint p,
|
||||
uint16_t radius, GCornerMask quadrant) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.p0 = p,
|
||||
.radius = radius,
|
||||
.corner_mask = quadrant
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void graphics_circle_fill_non_aa(GContext* ctx, GPoint p, uint16_t radius) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.p0 = p,
|
||||
.radius = radius
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void prv_draw_round_rect(GContext* ctx, const GRect *rect, uint16_t radius) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.r0 = *rect,
|
||||
.radius = radius,
|
||||
.corner_mask = GCornersAll
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void prv_draw_round_rect_aa(GContext* ctx, const GRect *rect, uint16_t radius) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.r0 = *rect,
|
||||
.radius = radius,
|
||||
.corner_mask = GCornersAll
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void prv_draw_round_rect_aa_stroked(GContext* ctx, const GRect *rect, uint16_t radius,
|
||||
uint8_t stroke_width) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.r0 = *rect,
|
||||
.radius = radius,
|
||||
.corner_mask = GCornersAll
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
void prv_draw_round_rect_stroked(GContext* ctx, const GRect *rect, uint16_t radius, uint8_t stroke_width) {
|
||||
s_last_args_for_mock = (ArgsForMock){
|
||||
.ctx = *ctx,
|
||||
.r0 = *rect,
|
||||
.radius = radius,
|
||||
.corner_mask = GCornersAll
|
||||
};
|
||||
strncpy(s_last_args_for_mock.func, __func__, sizeof(s_last_args_for_mock.func));
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
/// HELPER FUNCTIONS
|
||||
/////////////////////////////////////
|
||||
// Copy over the last arguments - take the largest possible shape
|
||||
static void copy_last_args_for_mock(ArgsForMock *actual_args, ArgsForMock *last_args) {
|
||||
memcpy(actual_args, last_args, sizeof(ArgsForMock));
|
||||
}
|
||||
|
||||
// Zero out the arguments from the last run
|
||||
static void reset_last_args_for_mock() {
|
||||
memset(&s_last_args_for_mock, 0x00, sizeof(s_last_args_for_mock));
|
||||
}
|
||||
|
||||
// Validate the arguments based on the shape that is drawn
|
||||
static bool validate_args(ArgsForMock *actual_args, ArgsForMock *valid_args) {
|
||||
if (memcmp(actual_args, valid_args, sizeof(ArgsForMock)) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// This macro will call the expected code_block and then validate the arguments set in code_block
|
||||
// match what was expected from the last mock call (i.e. if graphics_draw_line was called, then
|
||||
// internally it would call prv_draw_line... and the actual_args would be set to the arguments
|
||||
// that are set in prv_draw_line... so when compared to directly calling prv_draw_line..., the two
|
||||
// should match.
|
||||
#define ASSERT_CALLED(code_block) \
|
||||
do {\
|
||||
ArgsForMock actual_args; \
|
||||
copy_last_args_for_mock(&actual_args, &s_last_args_for_mock); \
|
||||
reset_last_args_for_mock(); \
|
||||
code_block; \
|
||||
bool cmp_result = validate_args(&actual_args, &s_last_args_for_mock); \
|
||||
printf("1 %d %d %d %d\n", actual_args.r0.origin.x, actual_args.r0.origin.y, \
|
||||
actual_args.r0.size.w, actual_args.r0.size.h); \
|
||||
actual_args = s_last_args_for_mock; \
|
||||
printf("2 %d %d %d %d\n", actual_args.r0.origin.x, actual_args.r0.origin.y, \
|
||||
actual_args.r0.size.w, actual_args.r0.size.h); \
|
||||
cl_assert(cmp_result); \
|
||||
} while(0)
|
||||
|
||||
// This is used to make sure nothing has changed when calling code_block
|
||||
// i.e. no prv_... functions have been called
|
||||
// If any prv_... functions are called then s_last_args_for_mock will be updated
|
||||
#define ASSERT_NO_CHANGE(code_block) \
|
||||
do {\
|
||||
ArgsForMock actual_args; \
|
||||
reset_last_args_for_mock(); \
|
||||
copy_last_args_for_mock(&actual_args, &s_last_args_for_mock); \
|
||||
code_block; \
|
||||
bool cmp_result = validate_args(&actual_args, &s_last_args_for_mock); \
|
||||
cl_assert(cmp_result); \
|
||||
} while(0)
|
||||
|
||||
static void setup_test(GContext* ctx, bool antialiased, uint8_t stroke_width, GColor stroke_color,
|
||||
GColor fill_color, bool lock) {
|
||||
graphics_context_set_antialiased(ctx, antialiased);
|
||||
graphics_context_set_stroke_width(ctx, stroke_width);
|
||||
graphics_context_set_stroke_color(ctx, stroke_color);
|
||||
graphics_context_set_fill_color(ctx, fill_color);
|
||||
ctx->lock = lock;
|
||||
reset_last_args_for_mock();
|
||||
}
|
||||
|
||||
// Setup
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
test_graphics_context_init(&context, fb);
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
/// TESTS
|
||||
/////////////////////////////////////
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__set(void) {
|
||||
GDrawState draw_state;
|
||||
GColor color;
|
||||
|
||||
// Stroke Color
|
||||
graphics_context_set_stroke_color(&context, GColorClear);
|
||||
cl_assert_equal_i(context.draw_state.stroke_color.argb, GColorClear.argb);
|
||||
|
||||
color = GColorBlue;
|
||||
graphics_context_set_stroke_color(&context, color);
|
||||
#if PBL_COLOR
|
||||
cl_assert_equal_i(context.draw_state.stroke_color.argb, GColorBlue.argb);
|
||||
#else
|
||||
cl_assert_equal_i(context.draw_state.stroke_color.argb, GColorBlack.argb);
|
||||
#endif
|
||||
|
||||
color.a = 2;
|
||||
graphics_context_set_stroke_color(&context, color);
|
||||
#if PBL_COLOR
|
||||
cl_assert_equal_i(context.draw_state.stroke_color.argb, GColorBlue.argb);
|
||||
#else
|
||||
cl_assert_equal_i(context.draw_state.stroke_color.argb, GColorBlack.argb);
|
||||
#endif
|
||||
|
||||
color.a = 1;
|
||||
graphics_context_set_stroke_color(&context, color);
|
||||
cl_assert_equal_i(context.draw_state.stroke_color.argb, GColorClear.argb);
|
||||
|
||||
color.a = 0;
|
||||
graphics_context_set_stroke_color(&context, color);
|
||||
cl_assert_equal_i(context.draw_state.stroke_color.argb, GColorClear.argb);
|
||||
|
||||
// Stroke Color - 2-bit
|
||||
graphics_context_set_stroke_color_2bit(&context, GColor2Black);
|
||||
cl_assert(gcolor_equal(context.draw_state.stroke_color, GColorBlack));
|
||||
|
||||
// Fill Color
|
||||
graphics_context_set_fill_color(&context, GColorClear);
|
||||
cl_assert_equal_i(context.draw_state.fill_color.argb, GColorClear.argb);
|
||||
|
||||
color = GColorOrange;
|
||||
graphics_context_set_fill_color(&context, color);
|
||||
#if PBL_COLOR
|
||||
cl_assert_equal_i(context.draw_state.fill_color.argb, GColorOrange.argb);
|
||||
#else
|
||||
cl_assert_equal_i(context.draw_state.fill_color.argb, GColorDarkGray.argb);
|
||||
#endif
|
||||
|
||||
color.a = 2;
|
||||
graphics_context_set_fill_color(&context, color);
|
||||
#if PBL_COLOR
|
||||
cl_assert_equal_i(context.draw_state.fill_color.argb, GColorOrange.argb);
|
||||
#else
|
||||
cl_assert_equal_i(context.draw_state.fill_color.argb, GColorDarkGray.argb);
|
||||
#endif
|
||||
|
||||
color.a = 1;
|
||||
graphics_context_set_fill_color(&context, color);
|
||||
cl_assert_equal_i(context.draw_state.fill_color.argb, GColorClear.argb);
|
||||
|
||||
color.a = 0;
|
||||
graphics_context_set_fill_color(&context, color);
|
||||
cl_assert_equal_i(context.draw_state.fill_color.argb, GColorClear.argb);
|
||||
|
||||
// Fill Color - 2-bit
|
||||
graphics_context_set_fill_color_2bit(&context, GColor2White);
|
||||
cl_assert(gcolor_equal(context.draw_state.fill_color, GColorWhite));
|
||||
|
||||
// Compositing Mode
|
||||
graphics_context_set_compositing_mode(&context, GCompOpOr);
|
||||
cl_assert(context.draw_state.compositing_mode == GCompOpOr);
|
||||
|
||||
// Text Color
|
||||
graphics_context_set_text_color(&context, GColorClear);
|
||||
cl_assert_equal_i(context.draw_state.text_color.argb, GColorClear.argb);
|
||||
|
||||
color = GColorYellow;
|
||||
graphics_context_set_text_color(&context, color);
|
||||
#if PBL_COLOR
|
||||
cl_assert_equal_i(context.draw_state.text_color.argb, GColorYellow.argb);
|
||||
#else
|
||||
cl_assert_equal_i(context.draw_state.text_color.argb, GColorWhite.argb);
|
||||
#endif
|
||||
|
||||
color.a = 2;
|
||||
graphics_context_set_text_color(&context, color);
|
||||
#if PBL_COLOR
|
||||
cl_assert_equal_i(context.draw_state.text_color.argb, GColorYellow.argb);
|
||||
#else
|
||||
cl_assert_equal_i(context.draw_state.text_color.argb, GColorWhite.argb);
|
||||
#endif
|
||||
|
||||
color.a = 1;
|
||||
graphics_context_set_text_color(&context, color);
|
||||
cl_assert_equal_i(context.draw_state.text_color.argb, GColorClear.argb);
|
||||
|
||||
color.a = 0;
|
||||
graphics_context_set_text_color(&context, color);
|
||||
cl_assert_equal_i(context.draw_state.text_color.argb, GColorClear.argb);
|
||||
|
||||
// Text Color - 2-bit
|
||||
graphics_context_set_text_color_2bit(&context, GColor2White);
|
||||
cl_assert(gcolor_equal(context.draw_state.text_color, GColorWhite));
|
||||
|
||||
#if PBL_COLOR
|
||||
// Antialiased
|
||||
graphics_context_set_antialiased(&context, true);
|
||||
cl_assert(context.draw_state.antialiased == true);
|
||||
#endif
|
||||
|
||||
// Stroke Width
|
||||
graphics_context_set_stroke_width(&context, 11);
|
||||
cl_assert(context.draw_state.stroke_width == 11);
|
||||
|
||||
// Make sure setting to zero is ignored
|
||||
draw_state = graphics_context_get_drawing_state(&context);
|
||||
graphics_context_set_stroke_width(&context, 0);
|
||||
cl_assert(memcmp(&draw_state, &context.draw_state, sizeof(GDrawState)) == 0);
|
||||
|
||||
// Check draw state
|
||||
memset(&draw_state, 0x5A, sizeof(GDrawState));
|
||||
graphics_context_set_drawing_state(&context, draw_state);
|
||||
cl_assert(memcmp(&draw_state, &context.draw_state, sizeof(GDrawState)) == 0);
|
||||
}
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__draw_antialiased(void) {
|
||||
// Stroke width = 1, antialiased
|
||||
setup_test(&context, true, 1, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10));
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(graphics_line_draw_1px_aa(&context, GPoint(5, 5), GPoint(45, 10)));
|
||||
#else
|
||||
ASSERT_CALLED(graphics_line_draw_1px_non_aa(&context, GPoint(5, 5), GPoint(45, 10)));
|
||||
#endif
|
||||
|
||||
setup_test(&context, true, 1, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_rect(&context, &GRect(10, 20, 40, 10));
|
||||
ASSERT_CALLED(prv_draw_rect(&context, &GRect(10, 20, 40, 10)));
|
||||
|
||||
setup_test(&context, true, 1, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_circle(&context, GPoint(50, 50), 10);
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(graphics_circle_draw_1px_aa(&context, GPoint(50, 50), 10));
|
||||
#else
|
||||
ASSERT_CALLED(graphics_circle_draw_1px_non_aa(&context, GPoint(50, 50), 10));
|
||||
#endif
|
||||
|
||||
setup_test(&context, true, 1, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4);
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(prv_draw_round_rect_aa(&context, &GRect(20, 80, 40, 10), 4));
|
||||
#else
|
||||
ASSERT_CALLED(prv_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__draw_stroke_width_1(void) {
|
||||
// Stroke width 1, non-antialiased
|
||||
setup_test(&context, false, 1, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10));
|
||||
ASSERT_CALLED(graphics_line_draw_1px_non_aa(&context, GPoint(5, 5), GPoint(45, 10)));
|
||||
|
||||
setup_test(&context, false, 1, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_rect(&context, &GRect(10, 20, 40, 10));
|
||||
ASSERT_CALLED(prv_draw_rect(&context, &GRect(10, 20, 40, 10)));
|
||||
|
||||
setup_test(&context, false, 1, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_circle(&context, GPoint(50, 50), 10);
|
||||
ASSERT_CALLED(graphics_circle_draw_1px_non_aa(&context, GPoint(50, 50), 10));
|
||||
|
||||
setup_test(&context, false, 1, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4);
|
||||
ASSERT_CALLED(prv_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4));
|
||||
}
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__draw_stroke_width_2(void) {
|
||||
// Stroke width 2, non-antialiased
|
||||
setup_test(&context, false, 2, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10));
|
||||
ASSERT_CALLED(graphics_line_draw_stroked_non_aa(&context, GPoint(5, 5), GPoint(45, 10), 2));
|
||||
|
||||
setup_test(&context, false, 2, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_rect(&context, &GRect(10, 20, 40, 10));
|
||||
ASSERT_CALLED(prv_draw_rect(&context, &GRect(10, 20, 40, 10)));
|
||||
|
||||
setup_test(&context, false, 2, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_circle(&context, GPoint(50, 50), 10);
|
||||
ASSERT_CALLED(graphics_circle_draw_stroked_non_aa(&context, GPoint(50, 50), 10, 2));
|
||||
|
||||
setup_test(&context, false, 2, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4);
|
||||
ASSERT_CALLED(prv_draw_round_rect_stroked(&context, &GRect(20, 80, 40, 10), 4, 2));
|
||||
}
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__draw_stroke_width_even(void) {
|
||||
// Stroke width even > 2, non-antialiased
|
||||
setup_test(&context, false, SW_EVEN, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10));
|
||||
ASSERT_CALLED(graphics_line_draw_stroked_non_aa(&context, GPoint(5, 5), GPoint(45, 10), SW_EVEN));
|
||||
|
||||
setup_test(&context, false, SW_EVEN, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_rect(&context, &GRect(10, 20, 40, 10));
|
||||
ASSERT_CALLED(prv_draw_rect_stroked(&context, &GRect(10, 20, 40, 10), SW_EVEN));
|
||||
|
||||
setup_test(&context, false, SW_EVEN, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_circle(&context, GPoint(50, 50), 10);
|
||||
ASSERT_CALLED(graphics_circle_draw_stroked_non_aa(&context, GPoint(50, 50), 10, SW_EVEN));
|
||||
|
||||
setup_test(&context, false, SW_EVEN, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4);
|
||||
ASSERT_CALLED(prv_draw_round_rect_stroked(&context, &GRect(20, 80, 40, 10), 4, SW_EVEN));
|
||||
}
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__draw_stroke_width_odd(void) {
|
||||
// Stroke width odd > 1, non-antialiased
|
||||
setup_test(&context, false, SW_ODD, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10));
|
||||
ASSERT_CALLED(graphics_line_draw_stroked_non_aa(&context, GPoint(5, 5), GPoint(45, 10), SW_ODD));
|
||||
|
||||
setup_test(&context, false, SW_ODD, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_rect(&context, &GRect(10, 20, 40, 10));
|
||||
ASSERT_CALLED(prv_draw_rect_stroked(&context, &GRect(10, 20, 40, 10), SW_ODD));
|
||||
|
||||
setup_test(&context, false, SW_ODD, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_circle(&context, GPoint(50, 50), 10);
|
||||
ASSERT_CALLED(graphics_circle_draw_stroked_non_aa(&context, GPoint(50, 50), 10, SW_ODD));
|
||||
|
||||
setup_test(&context, false, SW_ODD, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4);
|
||||
ASSERT_CALLED(prv_draw_round_rect_stroked(&context, &GRect(20, 80, 40, 10), 4, SW_ODD));
|
||||
}
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__draw_antialiased_stroke_width_2(void) {
|
||||
// Stroke width = 2, antialiased
|
||||
setup_test(&context, true, 2, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10));
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(graphics_line_draw_stroked_aa(&context, GPoint(5, 5), GPoint(45, 10), 2));
|
||||
#else
|
||||
ASSERT_CALLED(graphics_line_draw_stroked_non_aa(&context, GPoint(5, 5), GPoint(45, 10), 2));
|
||||
#endif
|
||||
setup_test(&context, true, 2, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_rect(&context, &GRect(10, 20, 40, 10));
|
||||
ASSERT_CALLED(prv_draw_rect(&context, &GRect(10, 20, 40, 10)));
|
||||
|
||||
setup_test(&context, true, 2, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_circle(&context, GPoint(50, 50), 10);
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(graphics_circle_draw_stroked_aa(&context, GPoint(50, 50), 10, 2));
|
||||
#else
|
||||
ASSERT_CALLED(graphics_circle_draw_stroked_non_aa(&context, GPoint(50, 50), 10, 2));
|
||||
#endif
|
||||
|
||||
setup_test(&context, true, 2, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4);
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(prv_draw_round_rect_aa_stroked(&context, &GRect(20, 80, 40, 10), 4, 2));
|
||||
#else
|
||||
ASSERT_CALLED(prv_draw_round_rect_stroked(&context, &GRect(20, 80, 40, 10), 4, 2));
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__draw_antialiased_stroke_width_even(void) {
|
||||
// Stroke width even > 2, antialiased
|
||||
setup_test(&context, true, SW_EVEN, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10));
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(graphics_line_draw_stroked_aa(&context, GPoint(5, 5), GPoint(45, 10), SW_EVEN));
|
||||
#else
|
||||
ASSERT_CALLED(graphics_line_draw_stroked_non_aa(&context, GPoint(5, 5), GPoint(45, 10), SW_EVEN));
|
||||
#endif
|
||||
|
||||
setup_test(&context, true, SW_EVEN, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_rect(&context, &GRect(10, 20, 40, 10));
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(prv_draw_rect_aa_stroked(&context, &GRect(10, 20, 40, 10), SW_EVEN));
|
||||
#else
|
||||
ASSERT_CALLED(prv_draw_rect_stroked(&context, &GRect(10, 20, 40, 10), SW_EVEN));
|
||||
#endif
|
||||
|
||||
setup_test(&context, true, SW_EVEN, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_circle(&context, GPoint(50, 50), 10);
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(graphics_circle_draw_stroked_aa(&context, GPoint(50, 50), 10, SW_EVEN));
|
||||
#else
|
||||
ASSERT_CALLED(graphics_circle_draw_stroked_non_aa(&context, GPoint(50, 50), 10, SW_EVEN));
|
||||
#endif
|
||||
|
||||
setup_test(&context, true, SW_EVEN, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4);
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(prv_draw_round_rect_aa_stroked(&context, &GRect(20, 80, 40, 10), 4, SW_EVEN));
|
||||
#else
|
||||
ASSERT_CALLED(prv_draw_round_rect_stroked(&context, &GRect(20, 80, 40, 10), 4, SW_EVEN));
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__draw_antialiased_stroke_width_odd(void) {
|
||||
// Stroke width odd > 1, antialiased
|
||||
setup_test(&context, true, SW_ODD, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10));
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(graphics_line_draw_stroked_aa(&context, GPoint(5, 5), GPoint(45, 10), SW_ODD));
|
||||
#else
|
||||
ASSERT_CALLED(graphics_line_draw_stroked_non_aa(&context, GPoint(5, 5), GPoint(45, 10), SW_ODD));
|
||||
#endif
|
||||
|
||||
setup_test(&context, true, SW_ODD, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_rect(&context, &GRect(10, 20, 40, 10));
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(prv_draw_rect_aa_stroked(&context, &GRect(10, 20, 40, 10), SW_ODD));
|
||||
#else
|
||||
ASSERT_CALLED(prv_draw_rect_stroked(&context, &GRect(10, 20, 40, 10), SW_ODD));
|
||||
#endif
|
||||
|
||||
setup_test(&context, true, SW_ODD, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_circle(&context, GPoint(50, 50), 10);
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(graphics_circle_draw_stroked_aa(&context, GPoint(50, 50), 10, SW_ODD));
|
||||
#else
|
||||
ASSERT_CALLED(graphics_circle_draw_stroked_non_aa(&context, GPoint(50, 50), 10, SW_ODD));
|
||||
#endif
|
||||
|
||||
setup_test(&context, true, SW_ODD, GColorBlack, GColorBlack, false);
|
||||
graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4);
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(prv_draw_round_rect_aa_stroked(&context, &GRect(20, 80, 40, 10), 4, SW_ODD));
|
||||
#else
|
||||
ASSERT_CALLED(prv_draw_round_rect_stroked(&context, &GRect(20, 80, 40, 10), 4, SW_ODD));
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__fill(void) {
|
||||
// Fill shape, non-antialiased (Stroke width/color N/A)
|
||||
setup_test(&context, false, 5, GColorBlack, GColorBlack, false);
|
||||
graphics_fill_rect(&context, &GRect(10, 20, 40, 10));
|
||||
ASSERT_CALLED(prv_fill_rect_non_aa(&context, &GRect(10, 20, 40, 10), 0, GCornerNone, GColorBlack));
|
||||
|
||||
setup_test(&context, false, 5, GColorBlack, GColorBlack, false);
|
||||
graphics_fill_circle(&context, GPoint(50, 50), 10);
|
||||
ASSERT_CALLED(graphics_circle_fill_non_aa(&context, GPoint(50, 50), 10));
|
||||
|
||||
setup_test(&context, false, 5, GColorBlack, GColorBlack, false);
|
||||
graphics_fill_round_rect(&context, &GRect(20, 80, 40, 10), 4, GCornersAll);
|
||||
ASSERT_CALLED(prv_fill_rect_non_aa(&context, &GRect(20, 80, 40, 10), 4, GCornersAll, GColorBlack));
|
||||
}
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__fill_antialiased(void) {
|
||||
// Fill shape, antialiased (Stroke width/color N/A)
|
||||
|
||||
setup_test(&context, true, 5, GColorBlack, GColorBlack, false);
|
||||
graphics_fill_rect(&context, &GRect(10, 20, 40, 10));
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 1
|
||||
ASSERT_CALLED(prv_fill_rect_non_aa(&context, &GRect(10, 20, 40, 10), 0, GCornerNone, GColorBlack));
|
||||
#else
|
||||
ASSERT_CALLED(prv_fill_rect_aa(&context, &GRect(10, 20, 40, 10), 0, GCornerNone, GColorBlack));
|
||||
#endif
|
||||
|
||||
setup_test(&context, true, 5, GColorBlack, GColorBlack, false);
|
||||
graphics_fill_circle(&context, GPoint(50, 50), 10);
|
||||
#if PBL_COLOR
|
||||
ASSERT_CALLED(graphics_internal_circle_quadrant_fill_aa(&context, GPoint(50, 50), 10, GCornersAll));
|
||||
#else
|
||||
ASSERT_CALLED(graphics_circle_fill_non_aa(&context, GPoint(50, 50), 10));
|
||||
#endif
|
||||
|
||||
setup_test(&context, true, 5, GColorBlack, GColorBlack, false);
|
||||
graphics_fill_round_rect(&context, &GRect(20, 80, 40, 10), 4, GCornersAll);
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 1
|
||||
ASSERT_CALLED(prv_fill_rect_non_aa(&context, &GRect(20, 80, 40, 10), 4, GCornersAll, GColorBlack));
|
||||
#else
|
||||
ASSERT_CALLED(prv_fill_rect_aa(&context, &GRect(20, 80, 40, 10), 4, GCornersAll, GColorBlack));
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__lock(void) {
|
||||
// Test all the setup test combinations as above
|
||||
setup_test(&context, false, 1, GColorBlack, GColorBlack, true);
|
||||
ASSERT_NO_CHANGE(graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_rect(&context, &GRect(10, 20, 40, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_circle(&context, GPoint(50, 50), 10));
|
||||
ASSERT_NO_CHANGE(graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4));
|
||||
|
||||
setup_test(&context, false, 2, GColorBlack, GColorBlack, true);
|
||||
ASSERT_NO_CHANGE(graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_rect(&context, &GRect(10, 20, 40, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_circle(&context, GPoint(50, 50), 10));
|
||||
ASSERT_NO_CHANGE(graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4));
|
||||
|
||||
setup_test(&context, false, SW_EVEN, GColorBlack, GColorBlack, true);
|
||||
ASSERT_NO_CHANGE(graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_rect(&context, &GRect(10, 20, 40, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_circle(&context, GPoint(50, 50), 10));
|
||||
ASSERT_NO_CHANGE(graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4));
|
||||
|
||||
setup_test(&context, false, SW_ODD, GColorBlack, GColorBlack, true);
|
||||
ASSERT_NO_CHANGE(graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_rect(&context, &GRect(10, 20, 40, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_circle(&context, GPoint(50, 50), 10));
|
||||
ASSERT_NO_CHANGE(graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4));
|
||||
ASSERT_NO_CHANGE(graphics_fill_rect(&context, &GRect(10, 20, 40, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_fill_circle(&context, GPoint(50, 50), 10));
|
||||
ASSERT_NO_CHANGE(graphics_fill_round_rect(&context, &GRect(20, 80, 40, 10), 4, GCornersAll));
|
||||
|
||||
setup_test(&context, true, 1, GColorBlack, GColorBlack, true);
|
||||
ASSERT_NO_CHANGE(graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_rect(&context, &GRect(10, 20, 40, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_circle(&context, GPoint(50, 50), 10));
|
||||
ASSERT_NO_CHANGE(graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4));
|
||||
|
||||
setup_test(&context, true, 2, GColorBlack, GColorBlack, true);
|
||||
ASSERT_NO_CHANGE(graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_rect(&context, &GRect(10, 20, 40, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_circle(&context, GPoint(50, 50), 10));
|
||||
ASSERT_NO_CHANGE(graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4));
|
||||
|
||||
setup_test(&context, true, SW_EVEN, GColorBlack, GColorBlack, true);
|
||||
ASSERT_NO_CHANGE(graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_rect(&context, &GRect(10, 20, 40, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_circle(&context, GPoint(50, 50), 10));
|
||||
ASSERT_NO_CHANGE(graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4));
|
||||
|
||||
setup_test(&context, true, SW_ODD, GColorBlack, GColorBlack, true);
|
||||
ASSERT_NO_CHANGE(graphics_draw_line(&context, GPoint(5, 5), GPoint(45, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_rect(&context, &GRect(10, 20, 40, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_draw_circle(&context, GPoint(50, 50), 10));
|
||||
ASSERT_NO_CHANGE(graphics_draw_round_rect(&context, &GRect(20, 80, 40, 10), 4));
|
||||
ASSERT_NO_CHANGE(graphics_fill_rect(&context, &GRect(10, 20, 40, 10)));
|
||||
ASSERT_NO_CHANGE(graphics_fill_circle(&context, GPoint(50, 50), 10));
|
||||
ASSERT_NO_CHANGE(graphics_fill_round_rect(&context, &GRect(20, 80, 40, 10), 4, GCornersAll));
|
||||
}
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__lock_context(void) {
|
||||
GDrawState draw_state;
|
||||
context.lock = true;
|
||||
|
||||
// Stroke Color
|
||||
draw_state = graphics_context_get_drawing_state(&context);
|
||||
graphics_context_set_stroke_color(&context, GColorBlue);
|
||||
cl_assert(memcmp(&draw_state, &context.draw_state, sizeof(GDrawState)) == 0);
|
||||
|
||||
// Fill Color
|
||||
draw_state = graphics_context_get_drawing_state(&context);
|
||||
graphics_context_set_fill_color(&context, GColorGreen);
|
||||
cl_assert(memcmp(&draw_state, &context.draw_state, sizeof(GDrawState)) == 0);
|
||||
|
||||
// Compositing Mode
|
||||
draw_state = graphics_context_get_drawing_state(&context);
|
||||
graphics_context_set_compositing_mode(&context, GCompOpOr);
|
||||
cl_assert(memcmp(&draw_state, &context.draw_state, sizeof(GDrawState)) == 0);
|
||||
|
||||
// Text Color
|
||||
draw_state = graphics_context_get_drawing_state(&context);
|
||||
graphics_context_set_text_color(&context, GColorRed);
|
||||
cl_assert(memcmp(&draw_state, &context.draw_state, sizeof(GDrawState)) == 0);
|
||||
|
||||
// Antialiased
|
||||
draw_state = graphics_context_get_drawing_state(&context);
|
||||
graphics_context_set_antialiased(&context, true);
|
||||
cl_assert(memcmp(&draw_state, &context.draw_state, sizeof(GDrawState)) == 0);
|
||||
|
||||
// Stroke Width
|
||||
draw_state = graphics_context_get_drawing_state(&context);
|
||||
graphics_context_set_stroke_width(&context, 11);
|
||||
cl_assert(memcmp(&draw_state, &context.draw_state, sizeof(GDrawState)) == 0);
|
||||
}
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__lock_framebuffer(void) {
|
||||
FrameBuffer fb = {};
|
||||
framebuffer_init(&fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
GContext ctx = {.dest_bitmap.info = {
|
||||
.format = GBITMAP_NATIVE_FORMAT,
|
||||
.version = GBITMAP_VERSION_CURRENT,
|
||||
}, .parent_framebuffer=&fb};
|
||||
GBitmap *framebuffer = graphics_capture_frame_buffer(&ctx);
|
||||
cl_assert(ctx.lock == true);
|
||||
cl_assert_equal_p(framebuffer, &ctx.dest_bitmap);
|
||||
|
||||
// Test releasing on any platform
|
||||
cl_assert(fb.is_dirty == false);
|
||||
graphics_release_frame_buffer(&ctx, framebuffer);
|
||||
cl_assert(ctx.lock == false);
|
||||
cl_assert(fb.is_dirty == true);
|
||||
};
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__lock_framebuffer_8BitCircular(void) {
|
||||
// Test for locking of requested framebuffer format
|
||||
GContext ctx = {.dest_bitmap.info.format = GBitmapFormat8BitCircular};
|
||||
GBitmap *bmp = graphics_capture_frame_buffer_format(&ctx, GBitmapFormat8BitCircular);
|
||||
cl_assert_equal_p(bmp, &ctx.dest_bitmap);
|
||||
cl_assert(ctx.lock == true);
|
||||
};
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__lock_framebuffer_fails_from_8BitCircular(void) {
|
||||
// Test for locking of 8Bit Circular framebuffer when framebuffer is regular 8Bit
|
||||
GContext ctx = {.dest_bitmap.info.format = GBitmapFormat8BitCircular};
|
||||
GBitmap *bmp = graphics_capture_frame_buffer_format(&ctx, GBitmapFormat8Bit);
|
||||
cl_assert(ctx.lock == false);
|
||||
cl_assert_equal_p(bmp, NULL);
|
||||
};
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__lock_framebuffer_1Bit_on_8BitCircular_must_fail(void) {
|
||||
// Test for locking of 1Bit framebuffer when frambuffer is 8Bit Circular
|
||||
GContext ctx = {.dest_bitmap.info.format = GBitmapFormat8BitCircular};
|
||||
GBitmap *bmp = graphics_capture_frame_buffer_format(&ctx, GBitmapFormat1Bit);
|
||||
cl_assert(ctx.lock == false);
|
||||
cl_assert_equal_p(bmp, NULL);
|
||||
};
|
||||
|
||||
void test_graphics_context_${BIT_DEPTH_NAME}__lock_framebuffer_2BitPalette_must_fail(void) {
|
||||
// Ensure that the code path for unsupported capture formats leaves the GContext unlocked.
|
||||
GContext ctx = {.dest_bitmap.info.format = GBitmapFormat8Bit};
|
||||
GBitmap *bmp = graphics_capture_frame_buffer_format(&ctx, GBitmapFormat2BitPalette);
|
||||
cl_assert(ctx.lock == false);
|
||||
cl_assert_equal_p(bmp, NULL);
|
||||
}
|
||||
@@ -0,0 +1,699 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/graphics_private.h"
|
||||
#include "applib/graphics/graphics_private_raw.h"
|
||||
#include "applib/graphics/graphics_private_raw_mask.h"
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
#include "util/graphics.h"
|
||||
#include "util/trig.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Includes
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "test_graphics_mask.h"
|
||||
#include "8bit/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Setup and Teardown
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static FrameBuffer *s_fb = NULL;
|
||||
static GContext *s_ctx = NULL;
|
||||
static GBitmap *s_dest_bitmap = NULL;
|
||||
|
||||
// Setup
|
||||
void test_graphics_context_mask__initialize(void) {
|
||||
s_fb = malloc(sizeof(*s_fb));
|
||||
s_ctx = malloc(sizeof(*s_ctx));
|
||||
|
||||
framebuffer_init(s_fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
test_graphics_context_init(s_ctx, s_fb);
|
||||
framebuffer_clear(s_fb);
|
||||
}
|
||||
|
||||
void test_graphics_context_mask__cleanup(void) {
|
||||
free(s_ctx);
|
||||
free(s_fb);
|
||||
|
||||
gbitmap_destroy(s_dest_bitmap);
|
||||
s_dest_bitmap = NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////
|
||||
// COMMON HELPERS //
|
||||
///////////////////////////////////////
|
||||
|
||||
#define CHECK_EXPECTED_TEST_IMAGE(ctx) (cl_check(gbitmap_pbi_eq(&ctx->dest_bitmap, TEST_PBI_FILE)))
|
||||
|
||||
static const int16_t num_pixels_per_mask_value = 5;
|
||||
static const int16_t num_mask_values = 4;
|
||||
static const int16_t num_src_colors = 256; // 8-bit color
|
||||
static const int16_t num_dest_colors = 64; // Framebuffer ignores alpha, so only 6-bit color
|
||||
|
||||
static void prv_prepare_canvas(GContext *ctx, GSize desired_size) {
|
||||
s_dest_bitmap = gbitmap_create_blank(desired_size, GBitmapFormat8Bit);
|
||||
|
||||
ctx->dest_bitmap = *s_dest_bitmap;
|
||||
ctx->draw_state.clip_box.size = desired_size;
|
||||
ctx->draw_state.drawing_box.size = desired_size;
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
// RECORDING HORIZONTAL LINE HELPERS //
|
||||
///////////////////////////////////////
|
||||
|
||||
static void prv_prepare_canvas_for_hline_recording_test(GContext *ctx) {
|
||||
const GSize bitmap_size = GSize(num_mask_values * num_pixels_per_mask_value, num_src_colors);
|
||||
prv_prepare_canvas(ctx, bitmap_size);
|
||||
}
|
||||
|
||||
static GDrawMask *prv_create_mask_for_hline_recording_test(GContext *ctx) {
|
||||
// The initial transparency doesn't really matter since we're about to overwrite it
|
||||
const bool transparent = false;
|
||||
GDrawMask *mask = graphics_context_mask_create(ctx, transparent);
|
||||
cl_assert(mask);
|
||||
|
||||
const GSize mask_size = ctx->dest_bitmap.bounds.size;
|
||||
for (int16_t x = 0; x < mask_size.w; x++) {
|
||||
const uint8_t mask_pixel_value = (uint8_t)(x / num_pixels_per_mask_value);
|
||||
for (int16_t y = 0; y < mask_size.h; y++) {
|
||||
test_graphics_context_mask_set_value_for_coordinate(ctx, mask, mask_pixel_value,
|
||||
GPoint(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
typedef void (*HorizontalClippingMaskRecordFunc)(GContext *ctx, int16_t y, int16_t x1, int16_t x2,
|
||||
GColor color);
|
||||
|
||||
static void prv_mask_record_hline_test_pattern(HorizontalClippingMaskRecordFunc record_func) {
|
||||
GContext *ctx = s_ctx;
|
||||
|
||||
prv_prepare_canvas_for_hline_recording_test(ctx);
|
||||
|
||||
graphics_context_set_antialiased(ctx, true);
|
||||
|
||||
GDrawMask *mask = prv_create_mask_for_hline_recording_test(ctx);
|
||||
|
||||
cl_assert(graphics_context_mask_record(ctx, mask));
|
||||
|
||||
for (int16_t y = 0; y < num_src_colors; y++) {
|
||||
GColor src_color = (GColor) { .argb = (uint8_t)y };
|
||||
for (int mask_value_index = 0; mask_value_index < num_mask_values; mask_value_index++) {
|
||||
int16_t x1 = (int16_t)(mask_value_index * num_pixels_per_mask_value);
|
||||
int16_t x2 = (int16_t)(x1 + num_pixels_per_mask_value - 1);
|
||||
record_func(ctx, y, x1, x2, src_color);
|
||||
}
|
||||
}
|
||||
|
||||
cl_assert(graphics_context_mask_record(ctx, NULL));
|
||||
|
||||
test_graphics_context_mask_debug(ctx, mask);
|
||||
|
||||
graphics_context_mask_destroy(ctx, mask);
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
// RECORDING VERTICAL LINE HELPERS //
|
||||
///////////////////////////////////////
|
||||
|
||||
static void prv_prepare_canvas_for_vline_recording_test(GContext *ctx) {
|
||||
const GSize bitmap_size = GSize(num_src_colors, num_mask_values * num_pixels_per_mask_value);
|
||||
prv_prepare_canvas(ctx, bitmap_size);
|
||||
}
|
||||
|
||||
static GDrawMask *prv_create_mask_for_vline_recording_test(GContext *ctx) {
|
||||
// The initial transparency doesn't really matter since we're about to overwrite it
|
||||
const bool transparent = false;
|
||||
GDrawMask *mask = graphics_context_mask_create(ctx, transparent);
|
||||
cl_assert(mask);
|
||||
|
||||
const GSize mask_size = ctx->dest_bitmap.bounds.size;
|
||||
for (int16_t y = 0; y < mask_size.h; y++) {
|
||||
const uint8_t mask_pixel_value = (uint8_t)(y / num_pixels_per_mask_value);
|
||||
for (int16_t x = 0; x < mask_size.w; x++) {
|
||||
test_graphics_context_mask_set_value_for_coordinate(ctx, mask, mask_pixel_value,
|
||||
GPoint(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
typedef void (*VerticalClippingMaskRecordFunc)(GContext *ctx, int16_t x, int16_t y1, int16_t y2,
|
||||
GColor color);
|
||||
|
||||
static void prv_mask_record_vline_test_pattern(VerticalClippingMaskRecordFunc record_func) {
|
||||
GContext *ctx = s_ctx;
|
||||
|
||||
prv_prepare_canvas_for_vline_recording_test(ctx);
|
||||
|
||||
graphics_context_set_antialiased(ctx, true);
|
||||
|
||||
GDrawMask *mask = prv_create_mask_for_vline_recording_test(ctx);
|
||||
|
||||
cl_assert(graphics_context_mask_record(ctx, mask));
|
||||
|
||||
for (int16_t x = 0; x < num_src_colors; x++) {
|
||||
GColor src_color = (GColor) { .argb = (uint8_t)x };
|
||||
for (int mask_value_index = 0; mask_value_index < num_mask_values; mask_value_index++) {
|
||||
int16_t y1 = (int16_t)(mask_value_index * num_pixels_per_mask_value);
|
||||
int16_t y2 = (int16_t)(y1 + num_pixels_per_mask_value - 1);
|
||||
record_func(ctx, x, y1, y2, src_color);
|
||||
}
|
||||
}
|
||||
|
||||
cl_assert(graphics_context_mask_record(ctx, NULL));
|
||||
|
||||
test_graphics_context_mask_debug(ctx, mask);
|
||||
|
||||
graphics_context_mask_destroy(ctx, mask);
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
// APPLYING HORIZONTAL LINE HELPERS //
|
||||
///////////////////////////////////////
|
||||
|
||||
static const int16_t hline_applying_test_column_width = num_mask_values * num_pixels_per_mask_value;
|
||||
|
||||
static void prv_prepare_canvas_for_hline_applying_test(GContext *ctx) {
|
||||
const GSize bitmap_size = GSize(num_dest_colors * hline_applying_test_column_width,
|
||||
num_src_colors);
|
||||
prv_prepare_canvas(ctx, bitmap_size);
|
||||
|
||||
// Fill the canvas so each column (of width hline_applying_test_column_width) is set to one of the
|
||||
// different possible dest_colors
|
||||
for (int16_t y = 0; y < bitmap_size.h; y++) {
|
||||
for (int16_t column_index = 0; column_index < num_dest_colors; column_index++) {
|
||||
const int16_t starting_x = column_index * hline_applying_test_column_width;
|
||||
for (int16_t x = starting_x; x < starting_x + hline_applying_test_column_width; x++) {
|
||||
ctx->draw_state.stroke_color = (GColor) { .argb = (uint8_t)(column_index | 0b11000000) };
|
||||
graphics_draw_pixel(ctx, GPoint(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GDrawMask *prv_create_mask_for_hline_applying_test(GContext *ctx) {
|
||||
// The initial transparency doesn't really matter since we're about to overwrite it
|
||||
const bool transparent = false;
|
||||
GDrawMask *mask = graphics_context_mask_create(ctx, transparent);
|
||||
cl_assert(mask);
|
||||
|
||||
const GSize mask_size = ctx->dest_bitmap.bounds.size;
|
||||
for (int16_t x = 0; x < mask_size.w; x++) {
|
||||
const uint8_t mask_pixel_value = (uint8_t)((x % hline_applying_test_column_width) /
|
||||
num_pixels_per_mask_value);
|
||||
for (int16_t y = 0; y < mask_size.h; y++) {
|
||||
test_graphics_context_mask_set_value_for_coordinate(ctx, mask, mask_pixel_value,
|
||||
GPoint(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
typedef void (*HorizontalClippingMaskApplyFunc)(GContext *ctx, int16_t y, int16_t x1, int16_t x2,
|
||||
GColor color);
|
||||
|
||||
static void prv_mask_apply_hline_test_pattern(HorizontalClippingMaskApplyFunc apply_func) {
|
||||
GContext *ctx = s_ctx;
|
||||
|
||||
prv_prepare_canvas_for_hline_applying_test(ctx);
|
||||
|
||||
graphics_context_set_antialiased(ctx, true);
|
||||
|
||||
GDrawMask *mask = prv_create_mask_for_hline_applying_test(ctx);
|
||||
|
||||
cl_assert(graphics_context_mask_use(ctx, mask));
|
||||
|
||||
for (int16_t y = 0; y < num_src_colors; y++) {
|
||||
GColor src_color = (GColor) { .argb = (uint8_t)y };
|
||||
for (int dest_color_index = 0; dest_color_index < num_dest_colors; dest_color_index++) {
|
||||
for (int mask_value_index = 0; mask_value_index < num_mask_values; mask_value_index++) {
|
||||
int16_t x1 = (int16_t)((dest_color_index * hline_applying_test_column_width) +
|
||||
(mask_value_index * num_pixels_per_mask_value));
|
||||
int16_t x2 = (int16_t)(x1 + num_pixels_per_mask_value - 1);
|
||||
apply_func(ctx, y, x1, x2, src_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
graphics_context_mask_destroy(ctx, mask);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// APPLYING VERTICAL LINE HELPERS //
|
||||
//////////////////////////////////////
|
||||
|
||||
static const int16_t vline_applying_test_row_height = hline_applying_test_column_width;
|
||||
|
||||
static void prv_prepare_canvas_for_vline_applying_test(GContext *ctx) {
|
||||
const GSize bitmap_size = GSize(num_src_colors,
|
||||
num_dest_colors * hline_applying_test_column_width);
|
||||
prv_prepare_canvas(ctx, bitmap_size);
|
||||
|
||||
// Fill the canvas so each row (of width vline_applying_test_row_height) is set to one of the
|
||||
// different possible dest_colors
|
||||
for (int16_t x = 0; x < bitmap_size.w; x++) {
|
||||
for (int16_t row_index = 0; row_index < num_dest_colors; row_index++) {
|
||||
const int16_t starting_y = row_index * vline_applying_test_row_height;
|
||||
for (int16_t y = starting_y; y < starting_y + vline_applying_test_row_height; y++) {
|
||||
ctx->draw_state.stroke_color = (GColor) { .argb = (uint8_t)(row_index | 0b11000000) };
|
||||
graphics_draw_pixel(ctx, GPoint(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GDrawMask *prv_create_mask_for_vline_applying_test(GContext *ctx) {
|
||||
// The initial transparency doesn't really matter since we're about to overwrite it
|
||||
const bool transparent = false;
|
||||
GDrawMask *mask = graphics_context_mask_create(ctx, transparent);
|
||||
cl_assert(mask);
|
||||
|
||||
const GSize mask_size = ctx->dest_bitmap.bounds.size;
|
||||
for (int16_t y = 0; y < mask_size.h; y++) {
|
||||
const uint8_t mask_pixel_value = (uint8_t)((y % vline_applying_test_row_height) /
|
||||
num_pixels_per_mask_value);
|
||||
for (int16_t x = 0; x < mask_size.w; x++) {
|
||||
test_graphics_context_mask_set_value_for_coordinate(ctx, mask, mask_pixel_value,
|
||||
GPoint(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
typedef void (*VerticalClippingMaskApplyFunc)(GContext *ctx, int16_t x, int16_t y1, int16_t y2,
|
||||
GColor color);
|
||||
|
||||
static void prv_mask_apply_vline_test_pattern(VerticalClippingMaskApplyFunc apply_func) {
|
||||
GContext *ctx = s_ctx;
|
||||
|
||||
prv_prepare_canvas_for_vline_applying_test(ctx);
|
||||
|
||||
graphics_context_set_antialiased(ctx, true);
|
||||
|
||||
GDrawMask *mask = prv_create_mask_for_vline_applying_test(ctx);
|
||||
|
||||
cl_assert(graphics_context_mask_use(ctx, mask));
|
||||
|
||||
for (int16_t x = 0; x < num_src_colors; x++) {
|
||||
GColor src_color = (GColor) { .argb = (uint8_t)x };
|
||||
for (int dest_color_index = 0; dest_color_index < num_dest_colors; dest_color_index++) {
|
||||
for (int mask_value_index = 0; mask_value_index < num_mask_values; mask_value_index++) {
|
||||
int16_t y1 = (int16_t)((dest_color_index * vline_applying_test_row_height) +
|
||||
(mask_value_index * num_pixels_per_mask_value));
|
||||
int16_t y2 = (int16_t)(y1 + num_pixels_per_mask_value - 1);
|
||||
apply_func(ctx, x, y1, y2, src_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
graphics_context_mask_destroy(ctx, mask);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Tests
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////
|
||||
// RECORDING HORIZONTAL LINE TESTS //
|
||||
/////////////////////////////////////
|
||||
|
||||
// These tests initialize a mask that has 4 columns, each of width `num_pixels_per_mask_value`.
|
||||
// Each of these columns in the mask is initialized to have one of the 4 possible mask values. Then,
|
||||
// the test iterates over the mask's 256 rows, each of which uses a different src color
|
||||
// (256 possibilities for 8-bit color) to draw a horizontal line (using the test's specified raw
|
||||
// drawing function) for each column into the mask (recording).
|
||||
|
||||
void prv_mask_recording_assign_horizontal_line(GContext *ctx, int16_t y, Fixed_S16_3 x1,
|
||||
Fixed_S16_3 x2, GColor color);
|
||||
|
||||
static void prv_hline_pattern_record_assign_horizontal_line_raw(GContext *ctx, int16_t y,
|
||||
int16_t x1, int16_t x2,
|
||||
GColor color) {
|
||||
// x1 and x2 here will be the integer start/end of the line, so we need to adjust them so we see
|
||||
// the same blending on the first and last pixel
|
||||
x2--;
|
||||
const Fixed_S16_3 x1_fixed = (Fixed_S16_3) {
|
||||
.integer = x1,
|
||||
.fraction = 4,
|
||||
};
|
||||
const Fixed_S16_3 x2_fixed = (Fixed_S16_3) {
|
||||
.integer = x2,
|
||||
.fraction = 4,
|
||||
};
|
||||
prv_mask_recording_assign_horizontal_line(ctx, y, x1_fixed, x2_fixed, color);
|
||||
}
|
||||
|
||||
void test_graphics_context_mask__record_assign_horizontal_line_raw(void) {
|
||||
prv_mask_record_hline_test_pattern(prv_hline_pattern_record_assign_horizontal_line_raw);
|
||||
CHECK_EXPECTED_TEST_IMAGE(s_ctx);
|
||||
};
|
||||
|
||||
void prv_mask_recording_blend_horizontal_line_raw(GContext *ctx, int16_t y, int16_t x1,
|
||||
int16_t x2, GColor color);
|
||||
|
||||
void test_graphics_context_mask__record_blend_horizontal_line_raw(void) {
|
||||
prv_mask_record_hline_test_pattern(prv_mask_recording_blend_horizontal_line_raw);
|
||||
CHECK_EXPECTED_TEST_IMAGE(s_ctx);
|
||||
};
|
||||
|
||||
void prv_mask_recording_assign_horizontal_line_delta_raw(GContext *ctx, int16_t y,
|
||||
Fixed_S16_3 x1, Fixed_S16_3 x2,
|
||||
uint8_t left_aa_offset,
|
||||
uint8_t right_aa_offset,
|
||||
int16_t clip_box_min_x,
|
||||
int16_t clip_box_max_x,
|
||||
GColor color);
|
||||
|
||||
static void prv_hline_pattern_record_assign_horizontal_line_delta_raw(GContext *ctx, int16_t y,
|
||||
int16_t x1, int16_t x2,
|
||||
GColor color) {
|
||||
const Fixed_S16_3 x1_fixed = (Fixed_S16_3) { .integer = x1 };
|
||||
Fixed_S16_3 x2_fixed = (Fixed_S16_3) { .integer = x2 };
|
||||
const uint8_t gradient_width = (uint8_t)((x2 - x1) / 2);
|
||||
x2_fixed.integer -= gradient_width;
|
||||
|
||||
const int16_t clip_box_min_x = ctx->draw_state.clip_box.origin.x;
|
||||
const int16_t clip_box_max_x = (int16_t)(grect_get_max_x(&ctx->draw_state.clip_box) - 1);
|
||||
prv_mask_recording_assign_horizontal_line_delta_raw(ctx, y, x1_fixed, x2_fixed, gradient_width,
|
||||
gradient_width, clip_box_min_x,
|
||||
clip_box_max_x, color);
|
||||
}
|
||||
|
||||
void test_graphics_context_mask__record_assign_horizontal_line_delta_raw(void) {
|
||||
prv_mask_record_hline_test_pattern(prv_hline_pattern_record_assign_horizontal_line_delta_raw);
|
||||
CHECK_EXPECTED_TEST_IMAGE(s_ctx);
|
||||
};
|
||||
|
||||
/////////////////////////////////////
|
||||
// RECORDING VERTICAL LINE TESTS //
|
||||
/////////////////////////////////////
|
||||
|
||||
// These tests initialize a mask that has 4 rows, each of height `num_pixels_per_mask_value`.
|
||||
// Each of these rows in the mask is initialized to have one of the 4 possible mask values. Then,
|
||||
// the test iterates over the mask's 256 columns, each of which uses a different src color
|
||||
// (256 possibilities for 8-bit color) to draw a vertical line (using the test's specified raw
|
||||
// drawing function) for each row into the mask (recording).
|
||||
|
||||
void prv_mask_recording_assign_vertical_line(GContext *ctx, int16_t x, Fixed_S16_3 y1,
|
||||
Fixed_S16_3 y2, GColor color);
|
||||
|
||||
static void prv_vline_pattern_record_assign_vertical_line(GContext *ctx, int16_t x, int16_t y1,
|
||||
int16_t y2, GColor color) {
|
||||
// y1 and y2 here will be the integer start/end of the line, so we need to adjust them so we see
|
||||
// the same blending on the first and last pixel
|
||||
y2--;
|
||||
const Fixed_S16_3 y1_fixed = (Fixed_S16_3) {
|
||||
.integer = y1,
|
||||
.fraction = 4,
|
||||
};
|
||||
const Fixed_S16_3 y2_fixed = (Fixed_S16_3) {
|
||||
.integer = y2,
|
||||
.fraction = 4,
|
||||
};
|
||||
prv_mask_recording_assign_vertical_line(ctx, x, y1_fixed, y2_fixed, color);
|
||||
}
|
||||
|
||||
void test_graphics_context_mask__record_assign_vertical_line_raw(void) {
|
||||
prv_mask_record_vline_test_pattern(prv_vline_pattern_record_assign_vertical_line);
|
||||
CHECK_EXPECTED_TEST_IMAGE(s_ctx);
|
||||
};
|
||||
|
||||
void prv_mask_recording_blend_vertical_line_raw(GContext *ctx, int16_t x, int16_t y1, int16_t y2,
|
||||
GColor color);
|
||||
|
||||
void test_graphics_context_mask__record_blend_vertical_line_raw(void) {
|
||||
prv_mask_record_vline_test_pattern(prv_mask_recording_blend_vertical_line_raw);
|
||||
CHECK_EXPECTED_TEST_IMAGE(s_ctx);
|
||||
};
|
||||
|
||||
/////////////////////////////////////
|
||||
// APPLYING HORIZONTAL LINE TESTS //
|
||||
/////////////////////////////////////
|
||||
|
||||
// These tests initialize a mask that has 4 * 64 columns, each of width `num_pixels_per_mask_value`.
|
||||
// Each quad grouping of these columns in the mask is initialized to have one of the 4 possible mask
|
||||
// values. Additionally, the test initializes the framebuffer to have 64 columns, each of width
|
||||
// `4 * num_pixels_per_mask_value`, where each column is one of the 64 possible dest_colors (the
|
||||
// framebuffer ignores alpha so 6-bit color). Then, the test activates the mask and iterates over
|
||||
// the framebuffer's 256 rows, each of which uses a different src color
|
||||
// (256 possibilities for 8-bit color) to draw a horizontal line (using the test's specified raw
|
||||
// drawing function) for each `num_pixels_per_mask_value`-wide column.
|
||||
|
||||
void prv_assign_horizontal_line_raw(GContext *ctx, int16_t y, Fixed_S16_3 x1, Fixed_S16_3 x2,
|
||||
GColor color);
|
||||
|
||||
static void prv_hline_pattern_apply_assign_horizontal_line_raw(GContext *ctx, int16_t y,
|
||||
int16_t x1, int16_t x2,
|
||||
GColor color) {
|
||||
// x1 and x2 here will be the integer start/end of the line, so we need to adjust them so we see
|
||||
// the same blending on the first and last pixel
|
||||
x2--;
|
||||
const Fixed_S16_3 x1_fixed = (Fixed_S16_3) {
|
||||
.integer = x1,
|
||||
.fraction = 4,
|
||||
};
|
||||
const Fixed_S16_3 x2_fixed = (Fixed_S16_3) {
|
||||
.integer = x2,
|
||||
.fraction = 4,
|
||||
};
|
||||
prv_assign_horizontal_line_raw(ctx, y, x1_fixed, x2_fixed, color);
|
||||
}
|
||||
|
||||
void test_graphics_context_mask__apply_assign_horizontal_line_raw(void) {
|
||||
prv_mask_apply_hline_test_pattern(prv_hline_pattern_apply_assign_horizontal_line_raw);
|
||||
CHECK_EXPECTED_TEST_IMAGE(s_ctx);
|
||||
};
|
||||
|
||||
void prv_blend_horizontal_line_raw(GContext *ctx, int16_t y, int16_t x1, int16_t x2,
|
||||
GColor color);
|
||||
|
||||
void test_graphics_context_mask__apply_blend_horizontal_line_raw(void) {
|
||||
prv_mask_apply_hline_test_pattern(prv_blend_horizontal_line_raw);
|
||||
CHECK_EXPECTED_TEST_IMAGE(s_ctx);
|
||||
};
|
||||
|
||||
void prv_assign_horizontal_line_delta_raw(GContext *ctx, int16_t y,
|
||||
Fixed_S16_3 x1, Fixed_S16_3 x2,
|
||||
uint8_t left_aa_offset, uint8_t right_aa_offset,
|
||||
int16_t clip_box_min_x, int16_t clip_box_max_x,
|
||||
GColor color);
|
||||
|
||||
static void prv_hline_pattern_apply_assign_horizontal_line_delta_raw(GContext *ctx, int16_t y,
|
||||
int16_t x1, int16_t x2,
|
||||
GColor color) {
|
||||
// FIXME PBL-34552: This test produces an incorrect image, see JIRA
|
||||
const Fixed_S16_3 x1_fixed = (Fixed_S16_3) { .integer = x1 };
|
||||
Fixed_S16_3 x2_fixed = (Fixed_S16_3) { .integer = x2 };
|
||||
const uint8_t gradient_width = (uint8_t)((x2 - x1) / 2);
|
||||
x2_fixed.integer -= gradient_width;
|
||||
|
||||
const int16_t clip_box_min_x = ctx->draw_state.clip_box.origin.x;
|
||||
const int16_t clip_box_max_x = (int16_t)(grect_get_max_x(&ctx->draw_state.clip_box) - 1);
|
||||
prv_assign_horizontal_line_delta_raw(ctx, y, x1_fixed, x2_fixed, gradient_width, gradient_width,
|
||||
clip_box_min_x, clip_box_max_x, color);
|
||||
}
|
||||
|
||||
void test_graphics_context_mask__apply_assign_horizontal_line_delta_raw(void) {
|
||||
prv_mask_apply_hline_test_pattern(prv_hline_pattern_apply_assign_horizontal_line_delta_raw);
|
||||
CHECK_EXPECTED_TEST_IMAGE(s_ctx);
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
// APPLYING VERTICAL LINE TESTS //
|
||||
//////////////////////////////////
|
||||
|
||||
// These tests initialize a mask that has 4 * 64 rows, each of height
|
||||
// `vline_applying_test_row_height`. Each quad grouping of these rows in the mask is initialized to
|
||||
// have one of the 4 possible mask values. Additionally, the test initializes the framebuffer to
|
||||
// have 64 rows, each of height `4 * num_pixels_per_mask_value`, where each row is one of the 64
|
||||
// possible dest_colors (the framebuffer ignores alpha so 6-bit color). Then, the test activates the
|
||||
// mask and iterates over the framebuffer's 256 columns, each of which uses a different src color
|
||||
// (256 possibilities for 8-bit color) to draw a vertical line (using the test's specified raw
|
||||
// drawing function) for each `num_pixels_per_mask_value`-tall row.
|
||||
|
||||
void prv_assign_vertical_line_raw(GContext *ctx, int16_t x, Fixed_S16_3 y1, Fixed_S16_3 y2,
|
||||
GColor color);
|
||||
|
||||
static void prv_vline_pattern_apply_assign_vertical_line(GContext *ctx, int16_t x, int16_t y1,
|
||||
int16_t y2, GColor color) {
|
||||
// y1 and y2 here will be the integer start/end of the line, so we need to adjust them so we see
|
||||
// the same blending on the first and last pixel
|
||||
y2--;
|
||||
const Fixed_S16_3 y1_fixed = (Fixed_S16_3) {
|
||||
.integer = y1,
|
||||
.fraction = 4,
|
||||
};
|
||||
const Fixed_S16_3 y2_fixed = (Fixed_S16_3) {
|
||||
.integer = y2,
|
||||
.fraction = 4,
|
||||
};
|
||||
prv_assign_vertical_line_raw(ctx, x, y1_fixed, y2_fixed, color);
|
||||
}
|
||||
|
||||
void test_graphics_context_mask__apply_assign_vertical_line_raw(void) {
|
||||
prv_mask_apply_vline_test_pattern(prv_vline_pattern_apply_assign_vertical_line);
|
||||
CHECK_EXPECTED_TEST_IMAGE(s_ctx);
|
||||
};
|
||||
|
||||
void prv_blend_vertical_line_raw(GContext *ctx, int16_t x, int16_t y1, int16_t y2, GColor color);
|
||||
|
||||
void test_graphics_context_mask__apply_blend_vertical_line_raw(void) {
|
||||
// FIXME PBL-34141: This test produces an incorrect image, see JIRA
|
||||
prv_mask_apply_vline_test_pattern(prv_blend_vertical_line_raw);
|
||||
CHECK_EXPECTED_TEST_IMAGE(s_ctx);
|
||||
};
|
||||
|
||||
/////////////////
|
||||
// BASIC TESTS //
|
||||
/////////////////
|
||||
|
||||
// These tests test the basic functionality of the clipping mask API.
|
||||
|
||||
static void prv_verify_mask_pixel_values(const GContext *ctx, const GDrawMask *mask,
|
||||
uint8_t expected_value) {
|
||||
// Assumes rectangular framebuffer
|
||||
cl_assert(ctx->dest_bitmap.info.format == GBitmapFormat8Bit);
|
||||
|
||||
const GSize framebuffer_size = ctx->dest_bitmap.bounds.size;
|
||||
|
||||
for (int16_t x = 0; x < framebuffer_size.w; x++) {
|
||||
for (int16_t y = 0; y < framebuffer_size.h; y++) {
|
||||
cl_assert_equal_i(test_graphics_context_mask_get_value_for_coordinate(ctx, mask,
|
||||
GPoint(x, y)),
|
||||
expected_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_graphics_context_mask__basic_create(void) {
|
||||
GContext *ctx = s_ctx;
|
||||
|
||||
// Should be safe to call create with NULL values
|
||||
cl_assert_equal_p(graphics_context_mask_create(NULL, NULL), NULL);
|
||||
|
||||
GDrawMask *transparent_mask = graphics_context_mask_create(ctx, true /* transparent */);
|
||||
cl_assert(transparent_mask);
|
||||
// Verify all mask pixels are initialized to be transparent (0)
|
||||
prv_verify_mask_pixel_values(ctx, transparent_mask, 0);
|
||||
graphics_context_mask_destroy(ctx, transparent_mask);
|
||||
|
||||
GDrawMask *opaque_mask = graphics_context_mask_create(ctx, false /* transparent */);
|
||||
cl_assert(opaque_mask);
|
||||
// Verify all mask pixels are initialized to be opaque (3)
|
||||
prv_verify_mask_pixel_values(ctx, opaque_mask, 3);
|
||||
graphics_context_mask_destroy(ctx, opaque_mask);
|
||||
}
|
||||
|
||||
//extern const GDrawRawImplementation g_mask_recording_draw_implementation;
|
||||
|
||||
void test_graphics_context_mask__basic_record(void) {
|
||||
GContext *ctx = s_ctx;
|
||||
|
||||
// Should be safe to call record with NULL values, will return false
|
||||
cl_assert(!graphics_context_mask_record(NULL, NULL));
|
||||
|
||||
// Should start with default draw implementation
|
||||
cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_default_draw_implementation);
|
||||
|
||||
GDrawMask *mask1 = graphics_context_mask_create(ctx, true /* transparent */);
|
||||
cl_assert(mask1);
|
||||
cl_assert(graphics_context_mask_record(ctx, mask1));
|
||||
// Should have switched to mask recording draw implementation
|
||||
cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_mask_recording_draw_implementation);
|
||||
// Should have attached mask1 to GContext
|
||||
cl_assert_equal_p(ctx->draw_state.draw_mask, mask1);
|
||||
|
||||
GDrawMask *mask2 = graphics_context_mask_create(ctx, true /* transparent */);
|
||||
cl_assert(mask2);
|
||||
cl_assert(graphics_context_mask_record(ctx, mask2));
|
||||
// Should still be on mask recording draw implementation
|
||||
cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_mask_recording_draw_implementation);
|
||||
// Should now have mask2 attached to GContext
|
||||
cl_assert_equal_p(ctx->draw_state.draw_mask, mask2);
|
||||
|
||||
// Calling record with NULL should reset draw impl to default and mask in GContext to NULL
|
||||
cl_assert(graphics_context_mask_record(ctx, NULL));
|
||||
cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_default_draw_implementation);
|
||||
cl_assert_equal_p(ctx->draw_state.draw_mask, NULL);
|
||||
|
||||
graphics_context_mask_destroy(ctx, mask1);
|
||||
graphics_context_mask_destroy(ctx, mask2);
|
||||
}
|
||||
|
||||
void test_graphics_context_mask__basic_use(void) {
|
||||
GContext *ctx = s_ctx;
|
||||
|
||||
// Should be safe to call use with NULL values, will return false
|
||||
cl_assert(!graphics_context_mask_use(NULL, NULL));
|
||||
|
||||
// Should start with default draw implementation
|
||||
cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_default_draw_implementation);
|
||||
|
||||
GDrawMask *mask1 = graphics_context_mask_create(ctx, true /* transparent */);
|
||||
cl_assert(mask1);
|
||||
cl_assert(graphics_context_mask_use(ctx, mask1));
|
||||
// Should still be on default draw implementation
|
||||
cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_default_draw_implementation);
|
||||
// Should have attached mask1 to GContext
|
||||
cl_assert_equal_p(ctx->draw_state.draw_mask, mask1);
|
||||
|
||||
GDrawMask *mask2 = graphics_context_mask_create(ctx, true /* transparent */);
|
||||
cl_assert(mask2);
|
||||
cl_assert(graphics_context_mask_use(ctx, mask2));
|
||||
// Should still be on default draw implementation
|
||||
cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_default_draw_implementation);
|
||||
// Should now have mask2 attached to GContext
|
||||
cl_assert_equal_p(ctx->draw_state.draw_mask, mask2);
|
||||
|
||||
// Calling use with NULL should reset draw impl to default and mask in GContext to NULL
|
||||
cl_assert(graphics_context_mask_use(ctx, NULL));
|
||||
cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_default_draw_implementation);
|
||||
cl_assert_equal_p(ctx->draw_state.draw_mask, NULL);
|
||||
|
||||
graphics_context_mask_destroy(ctx, mask1);
|
||||
graphics_context_mask_destroy(ctx, mask2);
|
||||
}
|
||||
|
||||
void test_graphics_context_mask__basic_destroy(void) {
|
||||
// Should be safe to call destroy on NULL values
|
||||
graphics_context_mask_destroy(NULL, NULL);
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* 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/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
#include "applib/ui/bitmap_layer.h"
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "util/size.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
|
||||
#include "test_graphics.h"
|
||||
#include "../graphics/util.h"
|
||||
|
||||
#if (SCREEN_COLOR_DEPTH_BITS == 1)
|
||||
#include "1bit/test_framebuffer.h"
|
||||
#elif (SCREEN_COLOR_DEPTH_BITS == 8)
|
||||
#include "8bit/test_framebuffer.h"
|
||||
#else
|
||||
#error "Unrecognized SCREEN_COLOR_DEPTH_BITS"
|
||||
#endif
|
||||
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
// Setup
|
||||
////////////////////////////////////
|
||||
|
||||
static GBitmap test_image;
|
||||
static GContext s_ctx;
|
||||
static FrameBuffer *s_fb = NULL;
|
||||
|
||||
void test_graphics_draw_bitmap__initialize(void) {
|
||||
s_fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(s_fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
test_graphics_context_init(&s_ctx, s_fb);
|
||||
read_pbi("no_litter_crop.png.pbi", &test_image);
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__cleanup(void) {
|
||||
free(s_fb);
|
||||
free(test_image.addr);
|
||||
}
|
||||
|
||||
// Layout Test Support
|
||||
////////////////////////////////////
|
||||
|
||||
#define TEST_LAYER_SIZE (GSize(64, 110))
|
||||
#define TEST_LAYER_OFFSET_ORIGIN (GPoint(80, 55))
|
||||
|
||||
static GPoint s_layer_test_image_bounds_offset;
|
||||
|
||||
static void prv_layer_test_update_proc(Layer *layer, GContext *ctx) {
|
||||
GRect destination = test_image.bounds;
|
||||
destination.origin = s_layer_test_image_bounds_offset;
|
||||
graphics_context_set_compositing_mode(ctx, GCompOpAssign);
|
||||
graphics_draw_bitmap_in_rect(ctx, &test_image, &destination);
|
||||
}
|
||||
|
||||
static void prv_layer_test(GPoint layer_origin, GPoint image_bounds_offset) {
|
||||
Layer layer;
|
||||
const GRect layer_frame = (GRect) {
|
||||
.origin = layer_origin,
|
||||
.size = TEST_LAYER_SIZE,
|
||||
};
|
||||
layer_init(&layer, &layer_frame);
|
||||
layer_set_update_proc(&layer, prv_layer_test_update_proc);
|
||||
|
||||
s_layer_test_image_bounds_offset = image_bounds_offset;
|
||||
|
||||
layer_render_tree(&layer, &s_ctx);
|
||||
}
|
||||
|
||||
static void prv_origin_layer_test(GPoint image_bounds_offset) {
|
||||
prv_layer_test(GPointZero, image_bounds_offset);
|
||||
}
|
||||
|
||||
static void prv_offset_layer_test(GPoint image_bounds_offset) {
|
||||
prv_layer_test(TEST_LAYER_OFFSET_ORIGIN, image_bounds_offset);
|
||||
}
|
||||
|
||||
static void prv_bitmap_layer_test(GPoint frame_origin, GPoint bounds_origin) {
|
||||
BitmapLayer bitmap_layer;
|
||||
const GRect bitmap_layer_frame = (GRect) {
|
||||
.origin = frame_origin,
|
||||
.size = TEST_LAYER_SIZE,
|
||||
};
|
||||
bitmap_layer_init(&bitmap_layer, &bitmap_layer_frame);
|
||||
Layer *layer = bitmap_layer_get_layer(&bitmap_layer);
|
||||
|
||||
GRect bitmap_layer_bounds = bitmap_layer_frame;
|
||||
bitmap_layer_bounds.origin = bounds_origin;
|
||||
layer_set_bounds(layer, &bitmap_layer_bounds);
|
||||
|
||||
bitmap_layer_set_bitmap(&bitmap_layer, &test_image);
|
||||
bitmap_layer_set_compositing_mode(&bitmap_layer, GCompOpAssign);
|
||||
layer_render_tree(bitmap_layer_get_layer(&bitmap_layer), &s_ctx);
|
||||
}
|
||||
|
||||
static void prv_origin_bitmap_layer_test(GPoint bounds_origin) {
|
||||
prv_bitmap_layer_test(GPointZero, bounds_origin);
|
||||
}
|
||||
|
||||
static void prv_offset_bitmap_layer_test(GPoint bounds_origin) {
|
||||
prv_bitmap_layer_test(TEST_LAYER_OFFSET_ORIGIN, bounds_origin);
|
||||
}
|
||||
|
||||
// Composite Test Support
|
||||
////////////////////////////////////
|
||||
|
||||
static GBitmap *prv_create_bitmap_from_png_file(const char *png_filename_without_extension) {
|
||||
GBitmap *result = NULL;
|
||||
|
||||
char png_file_path[strlen(CLAR_FIXTURE_PATH) + 1 + strlen(GRAPHICS_FIXTURE_PATH) + 1 +
|
||||
strlen(png_filename_without_extension) + 1];
|
||||
sprintf(png_file_path, "%s/%s/%s.png", CLAR_FIXTURE_PATH, GRAPHICS_FIXTURE_PATH,
|
||||
png_filename_without_extension);
|
||||
|
||||
FILE *fp = fopen(png_file_path, "r");
|
||||
if (fp) {
|
||||
// Go to the end of the file to discover the size of the file
|
||||
if (fseek(fp, 0, SEEK_END) != 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
const long end_of_file = ftell(fp);
|
||||
const size_t file_size = (size_t)end_of_file;
|
||||
if (end_of_file == -1) {
|
||||
goto cleanup;
|
||||
}
|
||||
uint8_t *png_data = malloc((size_t)file_size);
|
||||
if (!png_data) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Go back to the start of the file and read it into the buffer
|
||||
if (fseek(fp, 0L, SEEK_SET) != 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
cl_assert_equal_i(fread(png_data, 1, file_size, fp), file_size);
|
||||
|
||||
// Create a GBitmap using the PNG data
|
||||
result = gbitmap_create_from_png_data(png_data, file_size);
|
||||
if (result) {
|
||||
free(png_data);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
fclose(fp);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GBitmap *prv_create_bitmap_from_pbi_file(const char *pbi_filename_without_extension) {
|
||||
// Create a static GBitmap to avoid modifying the read_pbi() function to return a GBitmap
|
||||
static GBitmap result;
|
||||
|
||||
char pbi_filename[PATH_STRING_LENGTH] = {0};
|
||||
snprintf(pbi_filename, PATH_STRING_LENGTH, "%s.pbi", pbi_filename_without_extension);
|
||||
|
||||
if (!read_pbi(pbi_filename, &result)) {
|
||||
return NULL;
|
||||
} else {
|
||||
return &result;
|
||||
}
|
||||
}
|
||||
|
||||
typedef GBitmap *(*CompositeTestGBitmapCreateFunc)(const char *filename);
|
||||
|
||||
typedef struct CompositeTest {
|
||||
const char *test_name;
|
||||
GBitmapFormat expected_test_image_bitmap_format;
|
||||
CompositeTestGBitmapCreateFunc bitmap_create_func;
|
||||
bool need_to_destroy_bitmap;
|
||||
} CompositeTest;
|
||||
|
||||
static const CompositeTest s_composite_tests[] = {
|
||||
{
|
||||
.test_name = "1bitBW",
|
||||
.expected_test_image_bitmap_format = GBitmapFormat1Bit,
|
||||
.bitmap_create_func = prv_create_bitmap_from_pbi_file,
|
||||
.need_to_destroy_bitmap = false,
|
||||
},
|
||||
{
|
||||
.test_name = "2bitTrns",
|
||||
.expected_test_image_bitmap_format = GBitmapFormat2BitPalette,
|
||||
.bitmap_create_func = prv_create_bitmap_from_png_file,
|
||||
.need_to_destroy_bitmap = true,
|
||||
},
|
||||
#if PBL_COLOR
|
||||
{
|
||||
.test_name = "4bitTrns",
|
||||
.expected_test_image_bitmap_format = GBitmapFormat4BitPalette,
|
||||
.bitmap_create_func = prv_create_bitmap_from_png_file,
|
||||
.need_to_destroy_bitmap = true,
|
||||
},
|
||||
{
|
||||
.test_name = "8bitTrns",
|
||||
.expected_test_image_bitmap_format = GBitmapFormat8Bit,
|
||||
.bitmap_create_func = prv_create_bitmap_from_png_file,
|
||||
.need_to_destroy_bitmap = true,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
#define COMPOSITE_TEST_IMAGE_SIZE_WIDTH (100)
|
||||
|
||||
#define COMPOSITE_TEST_OFFSET_X (COMPOSITE_TEST_IMAGE_SIZE_WIDTH / 2)
|
||||
#define COMPOSITE_TEST_OFFSET_Y (0)
|
||||
|
||||
static bool prv_gbitmap_format_and_compositing_mode_combo_is_valid(GBitmapFormat bitmap_format,
|
||||
GCompOp compositing_mode) {
|
||||
return !((bitmap_format != GBitmapFormat1Bit) &&
|
||||
((compositing_mode == GCompOpAssignInverted) ||
|
||||
(compositing_mode == GCompOpOr) ||
|
||||
(compositing_mode == GCompOpAnd) ||
|
||||
(compositing_mode == GCompOpClear)));
|
||||
}
|
||||
|
||||
static void prv_composite_test_draw_bitmap(GContext *ctx, GBitmap *bitmap, GPoint offset,
|
||||
GCompOp compositing_mode) {
|
||||
GRect destination = bitmap->bounds;
|
||||
destination.origin = gpoint_add(destination.origin, offset);
|
||||
graphics_context_set_compositing_mode(ctx, GCompOpAssign);
|
||||
graphics_draw_bitmap_in_rect(ctx, bitmap, &destination);
|
||||
destination.origin = gpoint_add(destination.origin, GPoint(COMPOSITE_TEST_OFFSET_X,
|
||||
COMPOSITE_TEST_OFFSET_Y));
|
||||
graphics_context_set_compositing_mode(ctx, compositing_mode);
|
||||
graphics_draw_bitmap_in_rect(ctx, bitmap, &destination);
|
||||
}
|
||||
|
||||
static void prv_composite_test(const char *unit_test_name, GCompOp compositing_mode) {
|
||||
for (size_t i = 0; i < ARRAY_LENGTH(s_composite_tests); i++) {
|
||||
const CompositeTest *test_data = &s_composite_tests[i];
|
||||
|
||||
// Skip invalid GBitmapFormat and GCompOp combinations
|
||||
if (!prv_gbitmap_format_and_compositing_mode_combo_is_valid(
|
||||
test_data->expected_test_image_bitmap_format, compositing_mode)) {
|
||||
break;
|
||||
}
|
||||
|
||||
framebuffer_clear(s_fb);
|
||||
|
||||
char test_image_filename[PATH_STRING_LENGTH] = {0};
|
||||
snprintf(test_image_filename, PATH_STRING_LENGTH, "test_graphics_draw_bitmap_%s_test_image",
|
||||
test_data->test_name);
|
||||
|
||||
GBitmap *bitmap = test_data->bitmap_create_func(test_image_filename);
|
||||
cl_assert(bitmap);
|
||||
cl_assert(gbitmap_get_format(bitmap) == test_data->expected_test_image_bitmap_format);
|
||||
|
||||
// Draw the two variations of the test image at GPointZero
|
||||
prv_composite_test_draw_bitmap(&s_ctx, bitmap, GPointZero, compositing_mode);
|
||||
|
||||
// Then redraw the two variations offset so the bottom right edge of the right variation
|
||||
// is aligned with the bottom right edge of the framebuffer
|
||||
const GPoint framebuffer_bottom_right_point =
|
||||
GPoint(grect_get_max_x(&s_ctx.dest_bitmap.bounds),
|
||||
grect_get_max_y(&s_ctx.dest_bitmap.bounds));
|
||||
GPoint offset_point = gpoint_sub(framebuffer_bottom_right_point,
|
||||
GPoint(bitmap->bounds.size.w, bitmap->bounds.size.h));
|
||||
offset_point = gpoint_sub(offset_point, GPoint(COMPOSITE_TEST_OFFSET_X,
|
||||
COMPOSITE_TEST_OFFSET_Y));
|
||||
prv_composite_test_draw_bitmap(&s_ctx, bitmap, offset_point, compositing_mode);
|
||||
|
||||
// Check the result
|
||||
char unit_test_result_image_file_base_name[PATH_STRING_LENGTH] = {0};
|
||||
snprintf(unit_test_result_image_file_base_name, PATH_STRING_LENGTH,
|
||||
"%s_%s", unit_test_name, test_data->test_name);
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap,
|
||||
namecat(unit_test_result_image_file_base_name, ".pbi")));
|
||||
|
||||
if (test_data->need_to_destroy_bitmap) {
|
||||
gbitmap_destroy(bitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
void test_graphics_draw_bitmap__origin_layer_inside(void) {
|
||||
prv_origin_layer_test(GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__origin_layer_across_x(void) {
|
||||
prv_origin_layer_test(GPoint(25, 0));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__origin_layer_across_nx(void) {
|
||||
prv_origin_layer_test(GPoint(-25, 0));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__origin_layer_across_y(void) {
|
||||
prv_origin_layer_test(GPoint(0, 40));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__origin_layer_across_ny(void) {
|
||||
prv_origin_layer_test(GPoint(0, -40));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__origin_bitmap_layer_inside(void) {
|
||||
prv_origin_bitmap_layer_test(GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__origin_bitmap_layer_across_x(void) {
|
||||
prv_origin_bitmap_layer_test(GPoint(25, 0));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__origin_bitmap_layer_across_nx(void) {
|
||||
prv_origin_bitmap_layer_test(GPoint(-25, 0));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__origin_bitmap_layer_across_y(void) {
|
||||
prv_origin_bitmap_layer_test(GPoint(0, 75));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__origin_bitmap_layer_across_ny(void) {
|
||||
prv_origin_bitmap_layer_test(GPoint(0, -25));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__offset_layer_inside(void) {
|
||||
prv_offset_layer_test(GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__offset_layer_across_x(void) {
|
||||
prv_offset_layer_test(GPoint(25, 0));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__offset_layer_across_nx(void) {
|
||||
prv_offset_layer_test(GPoint(-25, 0));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__offset_layer_across_y(void) {
|
||||
prv_offset_layer_test(GPoint(0, 40));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__offset_layer_across_ny(void) {
|
||||
prv_offset_layer_test(GPoint(0, -40));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__offset_bitmap_layer_inside(void) {
|
||||
prv_offset_bitmap_layer_test(GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__offset_bitmap_layer_across_x(void) {
|
||||
prv_offset_bitmap_layer_test(GPoint(25, 0));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__offset_bitmap_layer_across_nx(void) {
|
||||
prv_offset_bitmap_layer_test(GPoint(-25, 0));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__offset_bitmap_layer_across_y(void) {
|
||||
prv_offset_bitmap_layer_test(GPoint(0, 75));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__offset_bitmap_layer_across_ny(void) {
|
||||
prv_offset_bitmap_layer_test(GPoint(0, -25));
|
||||
cl_check(gbitmap_pbi_eq(&s_ctx.dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__composite_assign(void) {
|
||||
prv_composite_test(__func__, GCompOpAssign);
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__composite_assign_inverted(void) {
|
||||
prv_composite_test(__func__, GCompOpAssignInverted);
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__composite_or(void) {
|
||||
prv_composite_test(__func__, GCompOpOr);
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__composite_and(void) {
|
||||
prv_composite_test(__func__, GCompOpAnd);
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__composite_clear(void) {
|
||||
prv_composite_test(__func__, GCompOpClear);
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__composite_set(void) {
|
||||
prv_composite_test(__func__, GCompOpSet);
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__composite_tint(void) {
|
||||
graphics_context_set_tint_color(&s_ctx, GColorOrange);
|
||||
prv_composite_test(__func__, GCompOpTint);
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__composite_tint_luminance_black_opaque(void) {
|
||||
graphics_context_set_tint_color(&s_ctx, GColorBlack);
|
||||
prv_composite_test(__func__, GCompOpTintLuminance);
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__composite_tint_luminance_black_semitransparent(void) {
|
||||
GColor tint_color = GColorBlack;
|
||||
tint_color.a = 2;
|
||||
// We have to set the tint color directly in the GContext because
|
||||
// graphics_context_set_tint_color() calls gcolor_closest_opaque() on the color you give it
|
||||
s_ctx.draw_state.tint_color = tint_color;
|
||||
prv_composite_test(__func__, GCompOpTintLuminance);
|
||||
}
|
||||
|
||||
void test_graphics_draw_bitmap__composite_tint_luminance_blue_opaque(void) {
|
||||
graphics_context_set_tint_color(&s_ctx, GColorBlue);
|
||||
prv_composite_test(__func__, GCompOpTintLuminance);
|
||||
}
|
||||
@@ -0,0 +1,926 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/graphics_circle_private.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
#include "util/trig.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
#include "util/size.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
#include "pebble_asserts.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "${BIT_DEPTH_NAME}/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
void inside_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_circle(ctx, GPoint(14, 14), 12);
|
||||
}
|
||||
|
||||
void white_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorWhite);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_circle(ctx, GPoint(14, 14), 12);
|
||||
}
|
||||
|
||||
void clear_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorClear);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_circle(ctx, GPoint(14, 14), 12);
|
||||
}
|
||||
|
||||
void across_x_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_circle(ctx, GPoint(28, 14), 12);
|
||||
}
|
||||
|
||||
void across_nx_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_circle(ctx, GPoint(-14, 14), 12);
|
||||
}
|
||||
|
||||
void across_y_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_circle(ctx, GPoint(14, 28), 12);
|
||||
}
|
||||
|
||||
void across_ny_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_circle(ctx, GPoint(14, -14), 12);
|
||||
}
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__origin_layer(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 28, 28));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_across_x_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_nx_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_across_nx_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_across_y_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_ny_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_across_ny_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
#define RADIUS_BIG 15
|
||||
#define RADIUS_MEDIUM 8
|
||||
#define RADIUS_MIN_CALCULATED 3
|
||||
#define RADIUS_MAX_PRECOMPUTED 2
|
||||
#define RADIUS_SMALL 1
|
||||
#define RADIUS_NONE 0
|
||||
|
||||
#define ORIGIN_RECT_NO_CLIP GRect(0, 0, 144, 168)
|
||||
#define ORIGIN_RECT_CLIP_XY GRect(0, 0, 30, 40)
|
||||
#define ORIGIN_RECT_CLIP_NXNY GRect(0, 0, 30, 40)
|
||||
#define CENTER_OF_ORIGIN_RECT GPoint(20, 25)
|
||||
#define CENTER_OF_ORIGIN_RECT_NXNY GPoint(10, 15)
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__origin_layer_aa(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Big circles
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_origin_aa_r16_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_origin_aa_r16_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_origin_aa_r16_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Medium circles
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_origin_aa_r8_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_origin_aa_r8_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_origin_aa_r8_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Small circles
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_origin_aa_r1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_origin_aa_r1_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_origin_aa_r1_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Testing of the special cases for radius:
|
||||
|
||||
// Radius of 3 - starting point for calculated edges
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_MIN_CALCULATED);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_origin_aa_r3_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Radius of 2 - ending point for precomputed edges
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_MAX_PRECOMPUTED);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_origin_aa_r2_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Radius of 0 - draw a single pixel
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_NONE);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_origin_aa_r0_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__offset_layer(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(10, 15, 28, 28));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_inside_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_across_x_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_nx_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_across_nx_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_across_y_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_ny_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_across_ny_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
#define OFFSET_RECT_NO_CLIP GRect(10, 10, 40, 50)
|
||||
#define OFFSET_RECT_CLIP_XY GRect(10, 10, 30, 40)
|
||||
#define OFFSET_RECT_CLIP_NXNY GRect(0, 0, 30, 40)
|
||||
#define CENTER_OF_OFFSET_RECT GPoint(10, 15)
|
||||
#define CENTER_OF_OFFSET_RECT_NXNY GPoint(0, 5)
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__offset_layer_aa(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Big circles
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_aa_r16_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_aa_r16_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT_NXNY, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_aa_r16_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Medium circles
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_aa_r8_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_aa_r8_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT_NXNY, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_aa_r8_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Small circles
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_aa_r1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_aa_r1_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT_NXNY, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_aa_r1_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
extern void graphics_circle_quadrant_draw_1px_non_aa(GContext* ctx, GPoint p,
|
||||
uint16_t radius,
|
||||
GCornerMask quadrant);
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__quadrants(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_circle_quadrant_draw_1px_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, GCornerTopLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_r8_quad_top_left.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_circle_quadrant_draw_1px_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, GCornerTopRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_r8_quad_top_right.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_circle_quadrant_draw_1px_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, GCornerBottomLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_r8_quad_bottom_left.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_circle_quadrant_draw_1px_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, GCornerBottomRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_r8_quad_bottom_right.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_circle_quadrant_draw_1px_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, GCornersTop);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_r8_quads_top.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_circle_quadrant_draw_1px_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, GCornersBottom);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_r8_quads_bottom.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_circle_quadrant_draw_1px_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, GCornersRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_r8_quads_right.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_circle_quadrant_draw_1px_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, GCornersLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_offset_r8_quads_left.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
extern void graphics_circle_quadrant_draw_1px_aa(GContext* ctx, GPoint p,
|
||||
uint16_t radius,
|
||||
GCornerMask quadrant);
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__quadrants_aa(void) {
|
||||
#if PBL_COLOR
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
typedef struct {
|
||||
char *filename_part;
|
||||
GCornerMask mask;
|
||||
} TestConfig;
|
||||
|
||||
TestConfig test_config[] = {
|
||||
{
|
||||
.filename_part = "quad_top_left",
|
||||
.mask = GCornerTopLeft,
|
||||
},
|
||||
{
|
||||
.filename_part = "quad_top_right",
|
||||
.mask = GCornerTopRight,
|
||||
},
|
||||
{
|
||||
.filename_part = "quad_bottom_right",
|
||||
.mask = GCornerBottomRight,
|
||||
},
|
||||
{
|
||||
.filename_part = "quad_bottom_left",
|
||||
.mask = GCornerBottomLeft,
|
||||
},
|
||||
{
|
||||
.filename_part = "quads_top",
|
||||
.mask = GCornersTop,
|
||||
},
|
||||
{
|
||||
.filename_part = "quads_bottom",
|
||||
.mask = GCornersBottom,
|
||||
},
|
||||
{
|
||||
.filename_part = "quads_right",
|
||||
.mask = GCornersRight,
|
||||
},
|
||||
{
|
||||
.filename_part = "quads_left",
|
||||
.mask = GCornersLeft,
|
||||
},
|
||||
};
|
||||
|
||||
// note: not the prettiest, fast a quick way to render all the scenarios Nitin was interested in
|
||||
for (int i = 0; i < ARRAY_LENGTH(test_config); i++) {
|
||||
TestConfig c = test_config[i];
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
GPoint pt = CENTER_OF_ORIGIN_RECT;
|
||||
// draw multiple quads with different radiuses
|
||||
for (int r = 0; r <= 15; r++) {
|
||||
graphics_circle_quadrant_draw_1px_aa(&ctx, pt, r, c.mask);
|
||||
|
||||
// center point follows a grid
|
||||
pt.x += 30;
|
||||
if (pt.x > 120) {
|
||||
pt.x = CENTER_OF_ORIGIN_RECT.x;
|
||||
pt.y += 30;
|
||||
}
|
||||
}
|
||||
|
||||
// construct file name and create meaningful assert description
|
||||
char filename[100];
|
||||
snprintf(filename, sizeof(filename),
|
||||
"draw_circle_offset_aa_r8_%s.${BIT_DEPTH_NAME}.pbi", c.filename_part);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, filename));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__clear(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 60, 60));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
layer_set_update_proc(&layer, &white_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("white_over_black", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
layer_set_update_proc(&layer, &clear_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
#if PBL_COLOR
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_clear.8bit.pbi"));
|
||||
#else
|
||||
cl_check(framebuffer_is_empty("clear_over_black", ctx.parent_framebuffer, GColorWhite));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Draws circles in a grid pattern while increasing the stroke width for each circle
|
||||
static void prv_draw_circles(GContext *ctx) {
|
||||
GPoint pt = CENTER_OF_ORIGIN_RECT;
|
||||
for (int sw = 0; sw <= 10; sw++) {
|
||||
ctx->draw_state.stroke_width = sw;
|
||||
graphics_draw_circle(ctx, pt, 0);
|
||||
|
||||
// center point follows a grid
|
||||
pt.x += 30;
|
||||
if (pt.x > 120) {
|
||||
pt.x = CENTER_OF_ORIGIN_RECT.x;
|
||||
pt.y += 30;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define TO_TRIG(deg) (((deg) * TRIG_MAX_ANGLE) / 360)
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__zero_swX_black(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Draw black circles with different alpha values in the stroke color with antialiasing disabled
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 0);
|
||||
graphics_context_set_stroke_color(&ctx, GColorBlack);
|
||||
prv_draw_circles(&ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_r0_swX_black.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__zero_aa_swX_black(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Draw black circles with different alpha values in the stroke color with antialiasing enabled
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 0);
|
||||
graphics_context_set_stroke_color(&ctx, GColorBlack);
|
||||
prv_draw_circles(&ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_r0_aa_swX_black.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_circle_8bit__zero_swX_color(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Draw red circles with different alpha values in the stroke color with antialiasing disabled
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 0);
|
||||
graphics_context_set_stroke_color(&ctx, GColorRed);
|
||||
prv_draw_circles(&ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_r0_swX_color.8bit.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_circle_8bit__zero_aa_swX_color(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Draw red circles with different alpha values in the stroke color with antialiasing enabled
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 0);
|
||||
graphics_context_set_stroke_color(&ctx, GColorRed);
|
||||
prv_draw_circles(&ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_r0_aa_swX_color.8bit.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__zero_swX_clear(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Draw clear circles with antialiasing disabled
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 0);
|
||||
graphics_context_set_stroke_color(&ctx, GColorClear);
|
||||
prv_draw_circles(&ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_r0_swX_clear.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__zero_aa_swX_clear(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Draw clear circles with antialiasing enabled
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 0);
|
||||
graphics_context_set_stroke_color(&ctx, GColorClear);
|
||||
prv_draw_circles(&ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_circle_r0_aa_swX_clear.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__arc_stroked(void){
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Letter C
|
||||
uint32_t angle_end = TRIG_MAX_ANGLE + (TRIG_MAX_ANGLE / 8);
|
||||
uint32_t angle_start = (TRIG_MAX_ANGLE / 8) * 3;
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 4);
|
||||
graphics_draw_arc_internal(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_BIG, angle_start, angle_end);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_arc_origin_aa_letter_c.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Stroke width bigger than radius
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, RADIUS_BIG * 2);
|
||||
graphics_draw_arc_internal(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_BIG, angle_start, angle_end);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_arc_origin_aa_stroke_bigger_than_radius.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Negative angle letter C
|
||||
angle_end -= TRIG_MAX_ANGLE;
|
||||
angle_start -= TRIG_MAX_ANGLE;
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 4);
|
||||
graphics_draw_arc_internal(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_BIG, angle_start, angle_end);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_arc_origin_aa_letter_c.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// More negative angle letter C (both angle are actually negative)
|
||||
angle_end -= TRIG_MAX_ANGLE;
|
||||
angle_start -= TRIG_MAX_ANGLE;
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 4);
|
||||
graphics_draw_arc_internal(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_BIG, angle_start, angle_end);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_arc_origin_aa_letter_c.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Incorrect angles (angle_start > angle_end) - should result in empty image
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, RADIUS_BIG * 2);
|
||||
graphics_draw_arc_internal(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_BIG, angle_end, angle_start);
|
||||
cl_check(framebuffer_is_empty(NULL, ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
// table with most popular angles to test
|
||||
typedef struct {
|
||||
char *filename_part;
|
||||
uint32_t angle;
|
||||
} TestAngles;
|
||||
|
||||
TestAngles test_angles[] = {
|
||||
{
|
||||
.filename_part = "__1_degrees",
|
||||
.angle = TRIG_MAX_ANGLE / 360,
|
||||
},
|
||||
{
|
||||
.filename_part = "__6_degrees",
|
||||
.angle = TRIG_MAX_ANGLE / 60,
|
||||
},
|
||||
{
|
||||
.filename_part = "_30_degrees",
|
||||
.angle = TRIG_MAX_ANGLE / 12,
|
||||
},
|
||||
{
|
||||
.filename_part = "_45_degrees",
|
||||
.angle = TRIG_MAX_ANGLE / 8,
|
||||
},
|
||||
{
|
||||
.filename_part = "_90_degrees",
|
||||
.angle = TRIG_MAX_ANGLE / 4,
|
||||
},
|
||||
{
|
||||
.filename_part = "181_degrees",
|
||||
.angle = TRIG_MAX_ANGLE / 2 + TRIG_MAX_ANGLE / 360,
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LENGTH(test_angles); i++) {
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
GPoint pt = CENTER_OF_ORIGIN_RECT;
|
||||
uint16_t radius = 10;
|
||||
uint16_t stroke_width = 1;
|
||||
|
||||
for (int r = 1; r <= 7; r++) {
|
||||
graphics_context_set_stroke_width(&ctx, stroke_width);
|
||||
graphics_draw_arc_internal(&ctx, pt, radius, 0, test_angles[i].angle);
|
||||
|
||||
stroke_width += 1;
|
||||
radius += 3;
|
||||
pt.x += (radius + stroke_width) * 2;
|
||||
if (pt.x > 120) {
|
||||
pt.x = CENTER_OF_ORIGIN_RECT.x;
|
||||
pt.y += (radius + stroke_width) * 2;
|
||||
}
|
||||
}
|
||||
|
||||
char filename[100];
|
||||
snprintf(filename, sizeof(filename),
|
||||
"draw_arc_offset_aa_end_angle_%s.${BIT_DEPTH_NAME}.pbi", test_angles[i].filename_part);
|
||||
cl_check_(gbitmap_pbi_eq(&ctx.dest_bitmap, filename), filename);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAY_LENGTH(test_angles); i++) {
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
GPoint pt = CENTER_OF_ORIGIN_RECT;
|
||||
uint16_t radius = 10;
|
||||
uint16_t stroke_width = 1;
|
||||
|
||||
for (int r = 1; r <= 7; r++) {
|
||||
graphics_context_set_stroke_width(&ctx, stroke_width);
|
||||
graphics_draw_arc_internal(&ctx, pt, radius, test_angles[i].angle, TRIG_MAX_ANGLE);
|
||||
|
||||
stroke_width += 1;
|
||||
radius += 3;
|
||||
pt.x += (radius + stroke_width) * 2;
|
||||
if (pt.x > 120) {
|
||||
pt.x = CENTER_OF_ORIGIN_RECT.x;
|
||||
pt.y += (radius + stroke_width) * 2;
|
||||
}
|
||||
}
|
||||
|
||||
char filename[100];
|
||||
snprintf(filename, sizeof(filename),
|
||||
"draw_arc_offset_aa_start_angle_%s.${BIT_DEPTH_NAME}.pbi", test_angles[i].filename_part);
|
||||
cl_check_(gbitmap_pbi_eq(&ctx.dest_bitmap, filename), filename);
|
||||
}
|
||||
|
||||
// Additional 90° end_angle unit tests:
|
||||
angle_start = TO_TRIG(45);
|
||||
angle_end = TO_TRIG(90);
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 5);
|
||||
GPoint pt = CENTER_OF_ORIGIN_RECT;
|
||||
|
||||
// This test is sponsored by number 4
|
||||
for (int r = 0; r < 4; r++) {
|
||||
graphics_draw_arc_internal(&ctx, pt, RADIUS_BIG, angle_start, angle_end);
|
||||
|
||||
angle_start += TRIG_MAX_ANGLE/4;
|
||||
angle_end += TRIG_MAX_ANGLE/4;
|
||||
pt.x += RADIUS_BIG * 4;
|
||||
if (pt.x > 120) {
|
||||
pt.x = CENTER_OF_ORIGIN_RECT.x;
|
||||
pt.y += RADIUS_BIG * 4;
|
||||
}
|
||||
}
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_arc_aa_end_angle_on_divider.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_circle_8bit__arc_colors(void){
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Letter C
|
||||
uint32_t angle_end = TRIG_MAX_ANGLE + (TRIG_MAX_ANGLE / 8);
|
||||
uint32_t angle_start = (TRIG_MAX_ANGLE / 8) * 3;
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_context_set_fill_color(&ctx, GColorRed);
|
||||
graphics_context_set_stroke_color(&ctx, GColorGreen);
|
||||
graphics_draw_arc_internal(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_BIG, angle_start, angle_end);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_arc_origin_aa_letter_c_color_1px.8bit.pbi"));
|
||||
|
||||
// Letter C SW 4
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 4);
|
||||
graphics_context_set_fill_color(&ctx, GColorRed);
|
||||
graphics_context_set_stroke_color(&ctx, GColorGreen);
|
||||
graphics_draw_arc_internal(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_BIG, angle_start, angle_end);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_arc_origin_aa_letter_c_color_4px.8bit.pbi"));
|
||||
}
|
||||
|
||||
#define DRAWING_SPACING 40
|
||||
char *mode_names[] = {
|
||||
"_90_degrees",
|
||||
"180_degrees",
|
||||
"270_degrees",
|
||||
};
|
||||
|
||||
char *precision_modes[] = {
|
||||
"without_faction",
|
||||
"with_fraction",
|
||||
};
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__radial_precise(void){
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
uint32_t angle_start = TRIG_MAX_ANGLE / 8;
|
||||
uint32_t angle_end = angle_start + (TRIG_MAX_ANGLE / 4);
|
||||
GPointPrecise center = GPointPrecise(CENTER_OF_ORIGIN_RECT.x * 8, CENTER_OF_ORIGIN_RECT.y * 8);
|
||||
Fixed_S16_3 radius = (Fixed_S16_3){.integer = RADIUS_BIG};
|
||||
|
||||
for (int precision_mode = 0; precision_mode < 2; precision_mode++) {
|
||||
for (int mode = 0; mode < 3; mode++) {
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 4);
|
||||
|
||||
for (int angle_offset=0; angle_offset < 4; angle_offset++) {
|
||||
graphics_draw_arc_precise_internal(&ctx, center, radius, angle_start, angle_end);
|
||||
|
||||
center.x.integer += (angle_offset % 2 == 0) ? DRAWING_SPACING : -DRAWING_SPACING;
|
||||
center.y.integer += (angle_offset == 1) ? DRAWING_SPACING : 0;
|
||||
|
||||
angle_start += TRIG_MAX_ANGLE / 4;
|
||||
angle_end += TRIG_MAX_ANGLE / 4;
|
||||
}
|
||||
|
||||
char filename[100];
|
||||
snprintf(filename, sizeof(filename),
|
||||
"draw_arc_origin_aa_precise_%s_%s_.${BIT_DEPTH_NAME}.pbi", precision_modes[precision_mode], mode_names[mode]);
|
||||
cl_check_(gbitmap_pbi_eq(&ctx.dest_bitmap, filename), filename);
|
||||
|
||||
center.y.integer -= DRAWING_SPACING;
|
||||
angle_end += TRIG_MAX_ANGLE / 4;
|
||||
}
|
||||
|
||||
center.x.fraction = 4;
|
||||
center.y.fraction = 4;
|
||||
radius.fraction = 4;
|
||||
angle_start = TRIG_MAX_ANGLE / 8;
|
||||
angle_end = angle_start + (TRIG_MAX_ANGLE / 4);
|
||||
}
|
||||
}
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__calc_draw_config_caps(void) {
|
||||
// ## Section with special cases
|
||||
// Test for no angles
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(0), TO_TRIG(0)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerNone},
|
||||
.full_quadrants = GCornerNone,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerNone}
|
||||
}));
|
||||
|
||||
// Tests for 360° angle
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(0), TO_TRIG(360)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerNone},
|
||||
.full_quadrants = GCornersAll,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerNone}
|
||||
}));
|
||||
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(20), TO_TRIG(380)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerNone},
|
||||
.full_quadrants = GCornersAll,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerNone}
|
||||
}));
|
||||
|
||||
// Tests for overflowing angles
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(20), TO_TRIG(1000)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerNone},
|
||||
.full_quadrants = GCornersAll,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerNone}
|
||||
}));
|
||||
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(360), TO_TRIG(370)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerTopRight},
|
||||
.full_quadrants = GCornerNone,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(10),GCornerTopRight}
|
||||
}));
|
||||
|
||||
// # Section with quadrant filling
|
||||
// Tests for full quadrants
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(0), TO_TRIG(90)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerTopRight},
|
||||
.full_quadrants = GCornerTopRight,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(90),GCornerBottomRight}
|
||||
}));
|
||||
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(90), TO_TRIG(180)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(90),GCornerBottomRight},
|
||||
.full_quadrants = GCornerBottomRight,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(180),GCornerBottomLeft}
|
||||
}));
|
||||
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(180), TO_TRIG(270)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(180),GCornerBottomLeft},
|
||||
.full_quadrants = GCornerBottomLeft,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(270),GCornerTopLeft}
|
||||
}));
|
||||
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(270), TO_TRIG(360)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(270),GCornerTopLeft},
|
||||
.full_quadrants = GCornerTopLeft,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerTopRight}
|
||||
}));
|
||||
|
||||
// Tests for pairs of quadrants
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(0), TO_TRIG(180)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerTopRight},
|
||||
.full_quadrants = GCornersRight,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(180),GCornerBottomLeft}
|
||||
}));
|
||||
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(180), TO_TRIG(360)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(180),GCornerBottomLeft},
|
||||
.full_quadrants = GCornersLeft,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerTopRight}
|
||||
}));
|
||||
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(270), TO_TRIG(360 + 90)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(270),GCornerTopLeft},
|
||||
.full_quadrants = GCornersTop,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(90),GCornerBottomRight}
|
||||
}));
|
||||
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(90), TO_TRIG(270)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(90),GCornerBottomRight},
|
||||
.full_quadrants = GCornersBottom,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(270),GCornerTopLeft}
|
||||
}));
|
||||
|
||||
// Tests for triples of quadrants
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(0), TO_TRIG(270)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerTopRight},
|
||||
.full_quadrants = (GCornersBottom | GCornerTopRight),
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(270),GCornerTopLeft}
|
||||
}));
|
||||
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(90), TO_TRIG(360)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(90),GCornerBottomRight},
|
||||
.full_quadrants = (GCornersBottom | GCornerTopLeft),
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(0),GCornerTopRight}
|
||||
}));
|
||||
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(180), TO_TRIG(360 + 90)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(180),GCornerBottomLeft},
|
||||
.full_quadrants = (GCornersTop | GCornerBottomLeft),
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(90),GCornerBottomRight}
|
||||
}));
|
||||
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(270), TO_TRIG(360 + 180)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(270),GCornerTopLeft},
|
||||
.full_quadrants = (GCornersTop | GCornerBottomRight),
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(180),GCornerBottomLeft}
|
||||
}));
|
||||
|
||||
// ## Section with regular angles
|
||||
// Within same quadrant
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(30), TO_TRIG(60)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(30),GCornerTopRight},
|
||||
.full_quadrants = GCornerNone,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(60),GCornerTopRight}
|
||||
}));
|
||||
|
||||
// Starting quadrant filling up and ending quadrant finishing
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(45), TO_TRIG(270 + 45)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(45),GCornerTopRight},
|
||||
.full_quadrants = GCornersBottom,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(270 + 45),GCornerTopLeft}
|
||||
}));
|
||||
|
||||
// Ending quadrant filling up and starting quadrant finishing
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(90 + 45), TO_TRIG(360 + 45)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(90 + 45),GCornerBottomRight},
|
||||
.full_quadrants = GCornersLeft,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(45),GCornerTopRight}
|
||||
}));
|
||||
|
||||
// ## Section with regular angles but over 360°
|
||||
// Within same quadrant
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(360 + 30), TO_TRIG(360 + 60)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(30),GCornerTopRight},
|
||||
.full_quadrants = GCornerNone,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(60),GCornerTopRight}
|
||||
}));
|
||||
|
||||
// Starting quadrant filling up and ending quadrant finishing
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(360 + 45), TO_TRIG(360 + 270 + 45)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(45),GCornerTopRight},
|
||||
.full_quadrants = GCornersBottom,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(270 + 45),GCornerTopLeft}
|
||||
}));
|
||||
|
||||
// Ending quadrant filling up and starting quadrant finishing
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(360 + 90 + 45), TO_TRIG(360 + 360 + 45)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(90 + 45),GCornerBottomRight},
|
||||
.full_quadrants = GCornersLeft,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(45),GCornerTopRight}
|
||||
}));
|
||||
|
||||
// ## Section with negative angles
|
||||
// Within same quadrant
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(30 - 360), TO_TRIG(60 - 360)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(30)+1,GCornerTopRight},
|
||||
.full_quadrants = GCornerNone,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(60)+1,GCornerTopRight}
|
||||
}));
|
||||
|
||||
// Starting quadrant filling up and ending quadrant finishing
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(45 - 360), TO_TRIG(270 + 45 - 360)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(45),GCornerTopRight},
|
||||
.full_quadrants = GCornersBottom,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(270 + 45),GCornerTopLeft}
|
||||
}));
|
||||
|
||||
// Ending quadrant filling up and starting quadrant finishing
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(90 + 45 - 360), TO_TRIG(360 + 45 - 360)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(90 + 45),GCornerBottomRight},
|
||||
.full_quadrants = GCornersLeft,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(45),GCornerTopRight}
|
||||
}));
|
||||
|
||||
// Pair of quadrants
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(0 - 360), TO_TRIG(180 - 360)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(360),GCornerTopRight},
|
||||
.full_quadrants = GCornersRight,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(180),GCornerBottomLeft}
|
||||
}));
|
||||
|
||||
// Negative to positive
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(-45), TO_TRIG(45)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(270 + 45),GCornerTopLeft},
|
||||
.full_quadrants = GCornerNone,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(45),GCornerTopRight}
|
||||
}));
|
||||
|
||||
// Flipping quadrant issue
|
||||
cl_assert_equal_edc(prv_calc_draw_config_ellipsis(TO_TRIG(70), TO_TRIG(90)), ((EllipsisDrawConfig){
|
||||
.start_quadrant = (EllipsisPartDrawConfig){TO_TRIG(70),GCornerTopRight},
|
||||
.full_quadrants = GCornerNone,
|
||||
.end_quadrant = (EllipsisPartDrawConfig){TO_TRIG(90),GCornerBottomRight}
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/graphics_private.h"
|
||||
#include "applib/graphics/graphics_private_raw.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "8bit/test_framebuffer.h"
|
||||
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
// Fakes
|
||||
////////////////////////////////////
|
||||
#include "fake_gbitmap_get_data_row.h"
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
#define CLIP_RECT_DRAW_BOX GRect(0, 0, DISP_COLS, DISP_ROWS)
|
||||
#define CLIP_RECT_CLIP_BOX GRect(0, 0, DISP_COLS, DISP_ROWS)
|
||||
|
||||
// Setup and Teardown
|
||||
////////////////////////////////////
|
||||
|
||||
// Setup
|
||||
void test_graphics_draw_core__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) { DISP_COLS, DISP_ROWS });
|
||||
// Enable fake data row handling which will override the gbitmap_get_data_row_xxx() functions
|
||||
// with their fake counterparts in fake_gbitmap_get_data_row.c
|
||||
s_fake_data_row_handling = true;
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_graphics_draw_core__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
// Helpers
|
||||
////////////////////////////////////
|
||||
|
||||
// HORIZONTAL LINE HELPERS
|
||||
|
||||
typedef void (*HLinePatternDrawFunction)(GContext *ctx, int16_t y, int16_t x1, int16_t x2,
|
||||
GColor color);
|
||||
|
||||
static void prv_draw_hlines_in_rect(GContext *ctx, HLinePatternDrawFunction draw_func,
|
||||
const GRect *rect, GColor color) {
|
||||
for (int16_t y = 0; y < rect->size.h; y++) {
|
||||
draw_func(ctx, rect->origin.y + y, rect->origin.x , grect_get_max_x(rect) - 1, color);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_draw_hline_test_pattern(GContext *ctx, HLinePatternDrawFunction draw_func) {
|
||||
const GRect *bitmap_bounds = &ctx->dest_bitmap.bounds;
|
||||
// Fill the screen with red
|
||||
graphics_context_set_fill_color(ctx, GColorRed);
|
||||
graphics_fill_rect(ctx, bitmap_bounds);
|
||||
|
||||
const GSize *bitmap_bounds_size = &bitmap_bounds->size;
|
||||
|
||||
// Draw the bottom rectangle blue with 66% opacity
|
||||
// (will appear at the top because data rows are vertically flipped)
|
||||
const GSize bottom_rect_size = GSize(bitmap_bounds_size->w * 4 / 5, bitmap_bounds_size->h / 2);
|
||||
const GRect bottom_rect = (GRect) {
|
||||
.origin = GPoint((bitmap_bounds_size->w - bottom_rect_size.w) / 2, bitmap_bounds_size->h / 2),
|
||||
.size = bottom_rect_size
|
||||
};
|
||||
GColor bottom_rect_color = GColorBlue;
|
||||
bottom_rect_color.a = 2;
|
||||
prv_draw_hlines_in_rect(ctx, draw_func, &bottom_rect, bottom_rect_color);
|
||||
|
||||
// Draw the top rectangles green with 66% opacity
|
||||
// (will appear at the bottom because data rows are vertically flipped)
|
||||
const GSize top_rects_size = GSize(bitmap_bounds_size->w / 4, bitmap_bounds_size->h * 2 / 5);
|
||||
const int16_t top_rects_x_offset = ((bitmap_bounds_size->w / 2) - top_rects_size.w) / 2;
|
||||
GColor top_rects_color = GColorGreen;
|
||||
top_rects_color.a = 2;
|
||||
const GRect top_left_rect = (GRect) {
|
||||
.origin = GPoint(top_rects_x_offset, 0),
|
||||
.size = top_rects_size
|
||||
};
|
||||
prv_draw_hlines_in_rect(ctx, draw_func, &top_left_rect, top_rects_color);
|
||||
const GRect top_right_rect = (GRect) {
|
||||
.origin = GPoint((bitmap_bounds_size->w / 2) + top_rects_x_offset, 0),
|
||||
.size = top_rects_size
|
||||
};
|
||||
prv_draw_hlines_in_rect(ctx, draw_func, &top_right_rect, top_rects_color);
|
||||
}
|
||||
|
||||
// VERTICAL LINE HELPERS
|
||||
|
||||
typedef void (*VLinePatternDrawFunction)(GContext *ctx, int16_t x, int16_t y1, int16_t y2,
|
||||
GColor color);
|
||||
|
||||
static void prv_draw_vlines_in_rect(GContext *ctx, HLinePatternDrawFunction draw_func,
|
||||
const GRect *rect, GColor color) {
|
||||
for (int16_t x = 0; x < rect->size.w; x++) {
|
||||
draw_func(ctx, rect->origin.x + x, rect->origin.y, grect_get_max_y(rect) - 1, color);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_draw_vline_test_pattern(GContext *ctx, HLinePatternDrawFunction draw_func) {
|
||||
const GRect *bitmap_bounds = &ctx->dest_bitmap.bounds;
|
||||
// Fill the screen with red
|
||||
graphics_context_set_fill_color(ctx, GColorRed);
|
||||
graphics_fill_rect(ctx, bitmap_bounds);
|
||||
|
||||
const GSize *bitmap_bounds_size = &bitmap_bounds->size;
|
||||
|
||||
// Draw the left rectangle blue with 66% opacity
|
||||
// (will appear at the top because data rows are vertically flipped)
|
||||
const GRect left_rect = GRect(0, 0, bitmap_bounds_size->w * 2 / 5, bitmap_bounds_size->h);
|
||||
GColor left_rect_color = GColorBlue;
|
||||
left_rect_color.a = 2;
|
||||
prv_draw_vlines_in_rect(ctx, draw_func, &left_rect, left_rect_color);
|
||||
|
||||
// Draw the right rectangles green with 66% opacity (top) and yellow with 66% opacity (bottom)
|
||||
// (green will appear on top and yellow on bottom because data rows are vertically flipped)
|
||||
const GSize right_rects_size = GSize(bitmap_bounds_size->w * 2 / 5, bitmap_bounds_size->h / 4);
|
||||
const int16_t right_rects_x = bitmap_bounds->size.w * 3 / 5;
|
||||
const int16_t right_rects_y_offset = ((bitmap_bounds_size->h / 2) - right_rects_size.h) / 2;
|
||||
GColor top_right_rect_color = GColorGreen;
|
||||
top_right_rect_color.a = 2;
|
||||
const GRect top_left_rect = (GRect) {
|
||||
.origin = GPoint(right_rects_x, right_rects_y_offset),
|
||||
.size = right_rects_size
|
||||
};
|
||||
prv_draw_vlines_in_rect(ctx, draw_func, &top_left_rect, top_right_rect_color);
|
||||
GColor bottom_right_rect_color = GColorYellow;
|
||||
bottom_right_rect_color.a = 2;
|
||||
const GRect top_right_rect = (GRect) {
|
||||
.origin = GPoint(right_rects_x, (bitmap_bounds_size->h / 2) + right_rects_y_offset),
|
||||
.size = right_rects_size
|
||||
};
|
||||
prv_draw_vlines_in_rect(ctx, draw_func, &top_right_rect, bottom_right_rect_color);
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
// HORIZONTAL LINE TESTS
|
||||
// These tests use a pattern of two skinny 66% opacity green rectangles drawn at the top of the
|
||||
// screen and one wide 66% opacity blue rectangle drawn at the bottom of the screen when drawing
|
||||
// horizontal lines. Due to the fake GBitmap data row handling, the result you should see is that
|
||||
// the pattern is clipped to a diamond mask and flipped vertically (i.e. blue rect on top, green
|
||||
// rects on bottom)
|
||||
|
||||
void prv_assign_horizontal_line_raw(GBitmap *framebuffer, int16_t y, Fixed_S16_3 x1,
|
||||
Fixed_S16_3 x2, GColor color);
|
||||
|
||||
static void prv_hline_pattern_assign_horizontal_line_raw(GContext *ctx, int16_t y, int16_t x1,
|
||||
int16_t x2, GColor color) {
|
||||
const Fixed_S16_3 x1_fixed = (Fixed_S16_3) { .integer = x1 };
|
||||
const Fixed_S16_3 x2_fixed = (Fixed_S16_3) { .integer = x2 };
|
||||
prv_assign_horizontal_line_raw(&ctx->dest_bitmap, y, x1_fixed, x2_fixed, color);
|
||||
}
|
||||
|
||||
void test_graphics_draw_core__assign_horizontal_line_raw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, false, 1);
|
||||
|
||||
prv_draw_hline_test_pattern(&ctx, prv_hline_pattern_assign_horizontal_line_raw);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_core_assign_horizontal_line_raw")));
|
||||
};
|
||||
|
||||
void prv_blend_horizontal_line_raw(GBitmap *framebuffer, int16_t y, int16_t x1, int16_t x2,
|
||||
GColor color);
|
||||
|
||||
static void prv_hline_pattern_blend_horizontal_line_raw(GContext *ctx, int16_t y, int16_t x1,
|
||||
int16_t x2, GColor color) {
|
||||
prv_blend_horizontal_line_raw(&ctx->dest_bitmap, y, x1, x2, color);
|
||||
}
|
||||
|
||||
void test_graphics_draw_core__blend_horizontal_line_raw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, false, 1);
|
||||
|
||||
prv_draw_hline_test_pattern(&ctx, prv_hline_pattern_blend_horizontal_line_raw);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_core_blend_horizontal_line_raw")));
|
||||
};
|
||||
|
||||
void prv_assign_horizontal_line_delta_raw(GBitmap *framebuffer, int16_t y,
|
||||
Fixed_S16_3 x1, Fixed_S16_3 x2,
|
||||
uint8_t left_aa_offset, uint8_t right_aa_offset,
|
||||
int16_t clip_box_min_x, int16_t clip_box_max_x,
|
||||
GColor color);
|
||||
|
||||
static void prv_hline_pattern_assign_horizontal_line_delta_raw(GContext *ctx, int16_t y,
|
||||
int16_t x1, int16_t x2,
|
||||
GColor color) {
|
||||
const Fixed_S16_3 x1_fixed = (Fixed_S16_3) { .integer = x1 };
|
||||
Fixed_S16_3 x2_fixed = (Fixed_S16_3) { .integer = x2 };
|
||||
const uint8_t gradient_width = (x2 - x1) / 6;
|
||||
x2_fixed.integer -= gradient_width;
|
||||
prv_assign_horizontal_line_delta_raw(&ctx->dest_bitmap, y, x1_fixed, x2_fixed, gradient_width,
|
||||
gradient_width, ctx->draw_state.clip_box.origin.x,
|
||||
grect_get_max_x(&ctx->draw_state.clip_box) - 1, color);
|
||||
}
|
||||
|
||||
void test_graphics_draw_core__assign_horizontal_line_delta_raw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, false, 1);
|
||||
|
||||
prv_draw_hline_test_pattern(&ctx, prv_hline_pattern_assign_horizontal_line_delta_raw);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_core_assign_horizontal_line_delta_raw")));
|
||||
};
|
||||
|
||||
// VERTICAL LINE TESTS
|
||||
// These tests use a pattern of two skinny 66% opacity rectangles drawn at the right of the
|
||||
// screen (green on top and yellow on bottom) and one tall 66% opacity blue rectangle drawn at the
|
||||
// left of the screen when drawing vertical lines. Due to the fake GBitmap data row handling, the
|
||||
// result you should see is that the pattern is clipped to a diamond mask and flipped vertically
|
||||
// (i.e. green rect on bottom and yellow rect on top) EXCEPT for the prv_assign_vertical_line_raw()
|
||||
// unit test which disables the vertical flipping
|
||||
|
||||
void prv_assign_vertical_line_raw(GBitmap *framebuffer, int16_t x, Fixed_S16_3 y1, Fixed_S16_3 y2,
|
||||
GColor color);
|
||||
|
||||
static void prv_vline_pattern_assign_vertical_line_raw(GContext *ctx, int16_t x, int16_t y1,
|
||||
int16_t y2, GColor color) {
|
||||
const Fixed_S16_3 y1_fixed = (Fixed_S16_3) { .integer = y1 };
|
||||
const Fixed_S16_3 y2_fixed = (Fixed_S16_3) { .integer = y2 };
|
||||
prv_assign_vertical_line_raw(&ctx->dest_bitmap, x, y1_fixed, y2_fixed, color);
|
||||
}
|
||||
|
||||
void test_graphics_draw_core__assign_vertical_line_raw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, false, 1);
|
||||
|
||||
prv_draw_vline_test_pattern(&ctx, prv_vline_pattern_assign_vertical_line_raw);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_core_assign_vertical_line_raw")));
|
||||
};
|
||||
|
||||
void prv_blend_vertical_line_raw(GBitmap *framebuffer, int16_t x, int16_t y1, int16_t y2,
|
||||
GColor color);
|
||||
|
||||
static void prv_vline_pattern_blend_vertical_line_raw(GContext *ctx, int16_t x, int16_t y1,
|
||||
int16_t y2, GColor color) {
|
||||
prv_blend_vertical_line_raw(&ctx->dest_bitmap, x, y1, y2, color);
|
||||
}
|
||||
|
||||
void test_graphics_draw_core__blend_vertical_line_raw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, false, 1);
|
||||
|
||||
prv_draw_vline_test_pattern(&ctx, prv_vline_pattern_blend_vertical_line_raw);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_core_blend_vertical_line_raw")));
|
||||
};
|
||||
|
||||
// PIXEL DRAWING AND COLUMN REPLICATION TESTS
|
||||
|
||||
void prv_replicate_column_row_raw(GBitmap *framebuffer, int16_t src_x, int16_t dst_x1,
|
||||
int16_t dst_x2);
|
||||
void set_pixel_raw_8bit(GContext* ctx, GPoint point);
|
||||
|
||||
void test_graphics_draw_core__set_pixel_raw_8bit_replicate_column_row_raw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, false, 1);
|
||||
|
||||
// Draw a colored gradient of horizontal lines down the left half of the screen
|
||||
const GRect bitmap_bounds = ctx.dest_bitmap.bounds;
|
||||
const uint8_t max_rgb_value = 0b00111111;
|
||||
for (int y = 0; y < bitmap_bounds.size.h; y++) {
|
||||
GColor color = (GColor) {
|
||||
.argb = y * max_rgb_value / (bitmap_bounds.size.h - 1)
|
||||
};
|
||||
color.a = 3; // 100% opacity
|
||||
ctx.draw_state.stroke_color = color;
|
||||
for (int x = 0; x < bitmap_bounds.size.w / 2; x++) {
|
||||
const GPoint point = GPoint(x, y);
|
||||
set_pixel_raw_8bit(&ctx, point);
|
||||
}
|
||||
}
|
||||
|
||||
// Replicate the last column of the colored gradient for the remaining columns of the bitmap
|
||||
prv_replicate_column_row_raw(&ctx.dest_bitmap,
|
||||
(bitmap_bounds.size.w / 2) - 1,
|
||||
(bitmap_bounds.size.w / 2),
|
||||
bitmap_bounds.size.w - 1);
|
||||
|
||||
const bool result =
|
||||
gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_core_set_pixel_raw_8bit_replicate_column_row_raw"));
|
||||
cl_check(result);
|
||||
};
|
||||
|
||||
void test_graphics_draw_core__plot_pixel(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, false, 1);
|
||||
|
||||
const GRect *bitmap_bounds = &ctx.dest_bitmap.bounds;
|
||||
|
||||
// Fill the screen with red
|
||||
graphics_context_set_fill_color(&ctx, GColorRed);
|
||||
graphics_fill_rect(&ctx, bitmap_bounds);
|
||||
|
||||
// Draw 66% opacity blue pixels using graphics_private_plot_pixel() over the entire screen
|
||||
// The expected result is that the entire screen will be purple due to the blending
|
||||
for (int y = 0; y < bitmap_bounds->size.h; y++) {
|
||||
for (int x = 0; x < bitmap_bounds->size.w; x++) {
|
||||
graphics_private_plot_pixel(&ctx.dest_bitmap, &ctx.draw_state.clip_box, x, y, 1, GColorBlue);
|
||||
}
|
||||
}
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("draw_core_plot_pixel")));
|
||||
};
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/graphics_private.h"
|
||||
#include "applib/graphics/graphics_private_raw.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "8bit/test_framebuffer.h"
|
||||
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
// State
|
||||
////////////////////////////////////
|
||||
|
||||
typedef enum {
|
||||
GDrawRawFunctionTypeAssignHorizontalLine,
|
||||
GDrawRawFunctionTypeAssignVerticalLine,
|
||||
GDrawRawFunctionTypeBlendHorizontalLine,
|
||||
GDrawRawFunctionTypeBlendVerticalLine,
|
||||
GDrawRawFunctionTypeAssignHorizontalLineDelta,
|
||||
NumGDrawRawFunctionTypes
|
||||
} GDrawRawFunctionType;
|
||||
|
||||
static unsigned int s_raw_drawing_function_counters[NumGDrawRawFunctionTypes];
|
||||
|
||||
// Setup and Teardown
|
||||
////////////////////////////////////
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_graphics_draw_implementation__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) { DISP_COLS, DISP_ROWS });
|
||||
memset(s_raw_drawing_function_counters, 0, sizeof(s_raw_drawing_function_counters));
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_graphics_draw_implementation__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
// Fake raw drawing functions
|
||||
////////////////////////////////////
|
||||
|
||||
static void prv_fake_raw_assign_horizontal_line(GContext *ctx, int16_t y, Fixed_S16_3 x1,
|
||||
Fixed_S16_3 x2, GColor color) {
|
||||
s_raw_drawing_function_counters[GDrawRawFunctionTypeAssignHorizontalLine]++;
|
||||
}
|
||||
|
||||
static void prv_fake_raw_assign_vertical_line(GContext *ctx, int16_t x, Fixed_S16_3 y1,
|
||||
Fixed_S16_3 y2, GColor color) {
|
||||
s_raw_drawing_function_counters[GDrawRawFunctionTypeAssignVerticalLine]++;
|
||||
}
|
||||
|
||||
static void prv_fake_raw_blend_horizontal_line(GContext *ctx, int16_t y, int16_t x1,
|
||||
int16_t x2, GColor color) {
|
||||
s_raw_drawing_function_counters[GDrawRawFunctionTypeBlendHorizontalLine]++;
|
||||
}
|
||||
|
||||
static void prv_fake_raw_blend_vertical_line(GContext *ctx, int16_t x, int16_t y1,
|
||||
int16_t y2, GColor color) {
|
||||
s_raw_drawing_function_counters[GDrawRawFunctionTypeBlendVerticalLine]++;
|
||||
}
|
||||
|
||||
static void prv_fake_raw_assign_horizontal_line_delta(GContext *ctx, int16_t y,
|
||||
Fixed_S16_3 x1, Fixed_S16_3 x2,
|
||||
uint8_t left_aa_offset,
|
||||
uint8_t right_aa_offset,
|
||||
int16_t clip_box_min_x,
|
||||
int16_t clip_box_max_x, GColor color) {
|
||||
s_raw_drawing_function_counters[GDrawRawFunctionTypeAssignHorizontalLineDelta]++;
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
#define CLIP_RECT_DRAW_BOX GRect(0, 0, DISP_COLS, DISP_ROWS)
|
||||
#define CLIP_RECT_CLIP_BOX GRect(0, 0, DISP_COLS, DISP_ROWS)
|
||||
|
||||
void test_graphics_draw_implementation__fill_circle_aa(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, true, 1);
|
||||
|
||||
// Set the draw implementation functions to the fake ones in this file that just increment some
|
||||
// counters, then call graphics_fill_circle() and check the values of the counters
|
||||
|
||||
ctx.draw_state.draw_implementation = &(GDrawRawImplementation) {
|
||||
.assign_horizontal_line = prv_fake_raw_assign_horizontal_line,
|
||||
.assign_vertical_line = prv_fake_raw_assign_vertical_line,
|
||||
.blend_horizontal_line = prv_fake_raw_blend_horizontal_line,
|
||||
.blend_vertical_line = prv_fake_raw_blend_vertical_line,
|
||||
.assign_horizontal_line_delta = prv_fake_raw_assign_horizontal_line_delta,
|
||||
};
|
||||
|
||||
const GRect bounds = ctx.dest_bitmap.bounds;
|
||||
|
||||
graphics_context_set_fill_color(&ctx, GColorBlack);
|
||||
graphics_fill_circle(&ctx, grect_center_point(&bounds), 5);
|
||||
|
||||
cl_assert_equal_i(11, s_raw_drawing_function_counters[GDrawRawFunctionTypeAssignHorizontalLine]);
|
||||
cl_assert_equal_i(0, s_raw_drawing_function_counters[GDrawRawFunctionTypeAssignVerticalLine]);
|
||||
cl_assert_equal_i(0, s_raw_drawing_function_counters[GDrawRawFunctionTypeBlendHorizontalLine]);
|
||||
cl_assert_equal_i(0, s_raw_drawing_function_counters[GDrawRawFunctionTypeBlendVerticalLine]);
|
||||
cl_assert_equal_i(0,
|
||||
s_raw_drawing_function_counters[GDrawRawFunctionTypeAssignHorizontalLineDelta]);
|
||||
};
|
||||
@@ -0,0 +1,935 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
#include "8bit/test_framebuffer.h"
|
||||
#else
|
||||
#include "1bit/test_framebuffer.h"
|
||||
#endif
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_graphics_draw_line__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_graphics_draw_line__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
void inside_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(5, 35), GPoint(45, 40));
|
||||
}
|
||||
|
||||
void white_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorWhite);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(5, 35), GPoint(45, 40));
|
||||
}
|
||||
|
||||
void clear_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorClear);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(5, 35), GPoint(45, 40));
|
||||
}
|
||||
|
||||
void across_x_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(50, 40), GPoint(70, 35));
|
||||
}
|
||||
|
||||
void across_nx_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(-25, 35), GPoint(15, 40));
|
||||
}
|
||||
|
||||
void across_y_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(40, 50), GPoint(35, 70));
|
||||
}
|
||||
|
||||
void across_ny_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(5, -30), GPoint(45, 30));
|
||||
}
|
||||
|
||||
void test_graphics_draw_line__origin_layer(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 60, 60));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_line_inside_origin_layer")));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_line_across_x_origin_layer")));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_nx_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_line_across_nx_origin_layer")));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_line_across_y_origin_layer")));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_ny_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_line_across_ny_origin_layer")));
|
||||
}
|
||||
|
||||
void test_graphics_draw_line__offset_layer(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(10, 10, 60, 60));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_line_inside_offset_layer")));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_line_across_x_offset_layer")));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_nx_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_line_across_nx_offset_layer")));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_line_across_y_offset_layer")));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_ny_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_line_across_ny_offset_layer")));
|
||||
}
|
||||
|
||||
void test_graphics_draw_line__clear(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 60, 60));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_line_inside_origin_layer")));
|
||||
layer_set_update_proc(&layer, &white_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("white_over_black", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_line_inside_origin_layer")));
|
||||
layer_set_update_proc(&layer, &clear_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("draw_line_clear")));
|
||||
#else
|
||||
cl_check(framebuffer_is_empty("clear_over_black", ctx.parent_framebuffer, GColorWhite));
|
||||
#endif
|
||||
}
|
||||
|
||||
#define MAX_NUM_ROWS 168
|
||||
#define MAX_NUM_COLS 144
|
||||
|
||||
#define ORIGIN_RECT_NO_CLIP GRect(0, 0, MAX_NUM_COLS, MAX_NUM_ROWS)
|
||||
#define ORIGIN_RECT_CLIP_EVEN GRect(10, 10, 60, 60)
|
||||
#define ORIGIN_RECT_CLIP_ODD GRect(11, 11, 61, 61)
|
||||
void test_graphics_draw_line___origin_horizontal_dotted(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Test odd and even rows draw appropriately
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(6, 12), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(6, 23), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(7, 13), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(7, 24), 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_origin_no_clip")));
|
||||
|
||||
// Even rows of different lengths
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 0), MAX_NUM_COLS);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 2), 148);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 4), 0);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 6), 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 8), 2);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 10), 3);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 12), 4);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 14), 20);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 16), 21);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 18), 22);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 20), 143);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 22), 145);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_origin_even_rows_no_clip")));
|
||||
|
||||
// Odd rows of different lengths
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 1), MAX_NUM_COLS);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 3), 148);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 5), 0);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 7), 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 9), 2);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 11), 3);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 13), 4);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 15), 20);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 17), 21);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 19), 22);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 21), 143);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 23), 145);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_origin_odd_rows_no_clip")));
|
||||
|
||||
// Test to make sure drawing on all rows creates checkerboard pattern
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
for (int16_t row = 0; row < MAX_NUM_ROWS; row++) {
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, row), MAX_NUM_COLS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_origin_checkerboard_no_clip")));
|
||||
|
||||
// Clipping on even boundaries - no offset
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_EVEN, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
for (int16_t row = 0; row < MAX_NUM_ROWS; row++) {
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, row), MAX_NUM_COLS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_origin_even_clip")));
|
||||
|
||||
// Clipping on odd boundaries - no offset
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_ODD, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
for (int16_t row = 0; row < MAX_NUM_ROWS; row++) {
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, row), MAX_NUM_COLS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_origin_odd_clip")));
|
||||
}
|
||||
|
||||
#define OFFSET_RECT_EVEN GRect(14, 14, MAX_NUM_COLS, MAX_NUM_ROWS)
|
||||
#define OFFSET_RECT_ODD GRect(15, 15, MAX_NUM_COLS, MAX_NUM_ROWS)
|
||||
#define OFFSET_RECT_CLIP_EVEN GRect(10, 10, 60, 60)
|
||||
#define OFFSET_RECT_CLIP_ODD GRect(11, 11, 61, 61)
|
||||
void test_graphics_draw_line___even_offset_horizontal_dotted(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Test odd and even rows draw appropriately
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_EVEN, OFFSET_RECT_EVEN, false, 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(6, 12), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(6, 23), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(7, 13), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(7, 24), 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_even_offset_no_clip")));
|
||||
|
||||
// Even rows of different lengths
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_EVEN, OFFSET_RECT_EVEN, false, 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 0), MAX_NUM_COLS);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 2), MAX_NUM_COLS + 4);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 4), 0);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 6), 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 8), 2);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 10), 3);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 12), 4);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 14), 20);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 16), 21);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 18), 22);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 20), MAX_NUM_COLS - 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 22), MAX_NUM_COLS + 1);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_even_offset_even_rows_no_clip")));
|
||||
|
||||
// Odd rows of different lengths
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_EVEN, OFFSET_RECT_EVEN, false, 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 1), MAX_NUM_COLS);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 3), MAX_NUM_COLS + 4);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 5), 0);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 7), 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 9), 2);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 11), 3);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 13), 4);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 15), 20);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 17), 21);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 19), 22);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 21), MAX_NUM_COLS - 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 23), MAX_NUM_COLS + 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_even_offset_odd_rows_no_clip")));
|
||||
|
||||
// Test to make sure drawing on all rows creates checkerboard pattern
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_EVEN, OFFSET_RECT_EVEN, false, 1);
|
||||
for (int16_t row = 0; row < MAX_NUM_ROWS; row++) {
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, row), MAX_NUM_COLS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_even_offset_checkerboard_no_clip")));
|
||||
|
||||
// Clipping on even boundaries - no offset
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_EVEN, OFFSET_RECT_EVEN, false, 1);
|
||||
for (int16_t row = 0; row < MAX_NUM_ROWS; row++) {
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, row), MAX_NUM_COLS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_even_offset_even_clip")));
|
||||
|
||||
// Clipping on odd boundaries - no offset
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_ODD, OFFSET_RECT_EVEN, false, 1);
|
||||
for (int16_t row = 0; row < MAX_NUM_ROWS; row++) {
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, row), MAX_NUM_COLS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_even_offset_odd_clip")));
|
||||
}
|
||||
|
||||
void test_graphics_draw_line___odd_offset_horizontal_dotted(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Test odd and even rows draw appropriately
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_ODD, OFFSET_RECT_ODD, false, 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(6, 12), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(6, 23), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(7, 13), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(7, 24), 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_odd_offset_no_clip")));
|
||||
|
||||
// Even rows of different lengths
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_ODD, OFFSET_RECT_ODD, false, 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 0), MAX_NUM_COLS);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 2), MAX_NUM_COLS + 4);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 4), 0);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 6), 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 8), 2);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 10), 3);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 12), 4);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 14), 20);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 16), 21);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 18), 22);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 20), MAX_NUM_COLS - 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 22), MAX_NUM_COLS + 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_odd_offset_even_rows_no_clip")));
|
||||
|
||||
// Odd rows of different lengths
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_ODD, OFFSET_RECT_ODD, false, 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 1), MAX_NUM_COLS);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 3), MAX_NUM_COLS + 4);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 5), 0);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 7), 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 9), 2);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 11), 3);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 13), 4);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 15), 20);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 17), 21);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 19), 22);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 21), MAX_NUM_COLS - 1);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, 23), MAX_NUM_COLS + 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_odd_offset_odd_rows_no_clip")));
|
||||
|
||||
// Test to make sure drawing on all rows creates checkerboard pattern
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_ODD, OFFSET_RECT_ODD, false, 1);
|
||||
for (int16_t row = 0; row < MAX_NUM_ROWS; row++) {
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, row), MAX_NUM_COLS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_odd_offset_checkerboard_no_clip")));
|
||||
|
||||
// Clipping on even boundaries - no offset
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_EVEN, OFFSET_RECT_ODD, false, 1);
|
||||
for (int16_t row = 0; row < MAX_NUM_ROWS; row++) {
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, row), MAX_NUM_COLS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_odd_offset_even_clip")));
|
||||
|
||||
// Clipping on odd boundaries - no offset
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_ODD, OFFSET_RECT_ODD, false, 1);
|
||||
for (int16_t row = 0; row < MAX_NUM_ROWS; row++) {
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(0, row), MAX_NUM_COLS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_horiz_dotted_line_odd_offset_odd_clip")));
|
||||
}
|
||||
|
||||
void test_graphics_draw_line___origin_vertical_dotted(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Test odd and even cols draw appropriately
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(12, 6), 10);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(23, 6), 10);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(13, 7), 10);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(24, 7), 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_origin_no_clip")));
|
||||
|
||||
// Even cols of different lengths
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(0, 0), MAX_NUM_ROWS);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(2, 0), MAX_NUM_ROWS + 4);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(4, 0), 0);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(6, 0), 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(8, 0), 2);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(10, 0), 3);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(12, 0), 4);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(14, 0), 20);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(16, 0), 21);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(18, 0), 22);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(20, 0), MAX_NUM_ROWS - 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(22, 0), MAX_NUM_ROWS + 1 );
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_origin_even_cols_no_clip")));
|
||||
|
||||
// Odd cols of different lengths
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(1, 0), MAX_NUM_ROWS);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(3, 0), MAX_NUM_ROWS + 4);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(5, 0), 0);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(7, 0), 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(9, 0), 2);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(11, 0), 3);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(13, 0), 4);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(15, 0), 20);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(17, 0), 21);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(19, 0), 22);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(21, 0), MAX_NUM_ROWS - 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(23, 0), MAX_NUM_ROWS + 1 );
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_origin_odd_cols_no_clip")));
|
||||
|
||||
// Test to make sure drawing on all cols creates checkerboard pattern
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
for (int16_t col = 0; col < MAX_NUM_COLS; col++) {
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(col, 0), MAX_NUM_ROWS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_origin_checkerboard_no_clip")));
|
||||
|
||||
// Clipping on even boundaries - no offset
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_EVEN, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
for (int16_t col = 0; col < MAX_NUM_COLS; col++) {
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(col, 0), MAX_NUM_ROWS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_origin_even_clip")));
|
||||
|
||||
// Clipping on odd boundaries - no offset
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_ODD, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
for (int16_t col = 0; col < MAX_NUM_COLS; col++) {
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(col, 0), MAX_NUM_ROWS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_origin_odd_clip")));
|
||||
}
|
||||
|
||||
void test_graphics_draw_line___even_offset_vertical_dotted(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Test odd and even cols draw appropriately
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_EVEN, OFFSET_RECT_EVEN, false, 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(12, 6), 10);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(23, 6), 10);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(13, 7), 10);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(24, 7), 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_even_offset_no_clip")));
|
||||
|
||||
// Even cols of different lengths
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_EVEN, OFFSET_RECT_EVEN, false, 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(0, 0), MAX_NUM_ROWS);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(2, 0), MAX_NUM_ROWS + 4);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(4, 0), 0);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(6, 0), 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(8, 0), 2);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(10, 0), 3);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(12, 0), 4);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(14, 0), 20);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(16, 0), 21);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(18, 0), 22);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(20, 0), MAX_NUM_ROWS - 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(22, 0), MAX_NUM_ROWS + 1 );
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_even_offset_even_cols_no_clip")));
|
||||
|
||||
// Odd cols of different lengths
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_EVEN, OFFSET_RECT_EVEN, false, 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(1, 0), MAX_NUM_ROWS);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(3, 0), MAX_NUM_ROWS + 4);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(5, 0), 0);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(7, 0), 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(9, 0), 2);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(11, 0), 3);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(13, 0), 4);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(15, 0), 20);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(17, 0), 21);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(19, 0), 22);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(21, 0), MAX_NUM_ROWS - 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(23, 0), MAX_NUM_ROWS + 1 );
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_even_offset_odd_cols_no_clip")));
|
||||
|
||||
// Test to make sure drawing on all cols creates checkerboard pattern
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_EVEN, OFFSET_RECT_EVEN, false, 1);
|
||||
for (int16_t col = 0; col < MAX_NUM_COLS; col++) {
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(col, 0), MAX_NUM_ROWS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_even_offset_checkerboard_no_clip")));
|
||||
|
||||
// Clipping on even boundaries - no offset
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_EVEN, OFFSET_RECT_EVEN, false, 1);
|
||||
for (int16_t col = 0; col < MAX_NUM_COLS; col++) {
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(col, 0), MAX_NUM_ROWS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_even_offset_even_clip")));
|
||||
|
||||
// Clipping on odd boundaries - no offset
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_ODD, OFFSET_RECT_EVEN, false, 1);
|
||||
for (int16_t col = 0; col < MAX_NUM_COLS; col++) {
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(col, 0), MAX_NUM_ROWS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_even_offset_odd_clip")));
|
||||
}
|
||||
|
||||
void test_graphics_draw_line___odd_offset_vertical_dotted(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Test odd and even cols draw appropriately
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_ODD, OFFSET_RECT_ODD, false, 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(12, 6), 10);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(23, 6), 10);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(13, 7), 10);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(24, 7), 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_odd_offset_no_clip")));
|
||||
|
||||
// Even cols of different lengths
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_ODD, OFFSET_RECT_ODD, false, 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(0, 0), MAX_NUM_ROWS);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(2, 0), MAX_NUM_ROWS + 4);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(4, 0), 0);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(6, 0), 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(8, 0), 2);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(10, 0), 3);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(12, 0), 4);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(14, 0), 20);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(16, 0), 21);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(18, 0), 22);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(20, 0), MAX_NUM_ROWS - 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(22, 0), MAX_NUM_ROWS + 1 );
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_odd_offset_even_cols_no_clip")));
|
||||
|
||||
// Odd cols of different lengths
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_ODD, OFFSET_RECT_ODD, false, 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(1, 0), MAX_NUM_ROWS);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(3, 0), MAX_NUM_ROWS + 4);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(5, 0), 0);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(7, 0), 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(9, 0), 2);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(11, 0), 3);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(13, 0), 4);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(15, 0), 20);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(17, 0), 21);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(19, 0), 22);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(21, 0), MAX_NUM_ROWS - 1);
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(23, 0), MAX_NUM_ROWS + 1 );
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_odd_offset_odd_cols_no_clip")));
|
||||
|
||||
// Test to make sure drawing on all cols creates checkerboard pattern
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_ODD, OFFSET_RECT_ODD, false, 1);
|
||||
for (int16_t col = 0; col < MAX_NUM_COLS; col++) {
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(col, 0), MAX_NUM_ROWS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_odd_offset_checkerboard_no_clip")));
|
||||
|
||||
// Clipping on even boundaries - no offset
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_EVEN, OFFSET_RECT_ODD, false, 1);
|
||||
for (int16_t col = 0; col < MAX_NUM_COLS; col++) {
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(col, 0), MAX_NUM_ROWS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_odd_offset_even_clip")));
|
||||
|
||||
// Clipping on odd boundaries - no offset
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_ODD, OFFSET_RECT_ODD, false, 1);
|
||||
for (int16_t col = 0; col < MAX_NUM_COLS; col++) {
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(col, 0), MAX_NUM_ROWS);
|
||||
}
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
TEST_NAMED_PBI_FILE("draw_vert_dotted_line_odd_offset_odd_clip")));
|
||||
}
|
||||
|
||||
void test_graphics_draw_line__dotted_cross(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Test horizontal and vertical lines cross appropriately
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
// cross - even vert, odd horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(10, 10), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(5, 15), 10);
|
||||
|
||||
// cross - odd vert, even horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(41, 11), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(36, 16), 10);
|
||||
|
||||
// T facing up
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(70, 15), 5);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(65, 15), 10);
|
||||
|
||||
// T facing down
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(101, 11), 5);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(96, 16), 10);
|
||||
|
||||
// cross - even vert, even horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(10, 32), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(4, 36), 10);
|
||||
|
||||
// cross - odd vert, odd horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(41, 33), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(37, 37), 10);
|
||||
|
||||
// T facing up - lined up
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(70, 36), 5);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(66, 36), 10);
|
||||
|
||||
// T facing down - lined up
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(101, 33), 5);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(97, 37), 10);
|
||||
|
||||
|
||||
// T facing left - lined up
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(10, 70), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(5, 76), 5);
|
||||
|
||||
// T facing right - even vert, odd horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(50, 70), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(45, 75), 5);
|
||||
|
||||
// T facing right - odd vert, even horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(71, 71), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(66, 76), 5);
|
||||
|
||||
// T facing left - even vert, odd horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(100, 70), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(101, 75), 5);
|
||||
|
||||
// T facing left - odd vert, even horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(131, 71), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(132, 76), 5);
|
||||
|
||||
|
||||
// T facing right - lined up
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(10, 90), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(10, 96), 5);
|
||||
|
||||
// T facing right - even vert, even horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(50, 90), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(46, 95), 5);
|
||||
|
||||
// T facing right - odd vert, odd horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(71, 91), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(67, 96), 5);
|
||||
|
||||
// T facing left - even vert, even horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(100, 90), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(100, 95), 5);
|
||||
|
||||
// T facing left - odd vert, odd horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(131, 91), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(131, 96), 5);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("draw_dotted_line_cross")));
|
||||
}
|
||||
|
||||
void test_graphics_draw_line_8bit__dotted_cross_color(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Test horizontal and vertical lines cross appropriately
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_context_set_stroke_color(&ctx, GColorRed);
|
||||
// cross - even vert, odd horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(10, 10), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(5, 15), 10);
|
||||
|
||||
// cross - odd vert, even horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(41, 11), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(36, 16), 10);
|
||||
|
||||
// T facing up
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(70, 15), 5);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(65, 15), 10);
|
||||
|
||||
// T facing down
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(101, 11), 5);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(96, 16), 10);
|
||||
|
||||
// cross - even vert, even horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(10, 32), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(4, 36), 10);
|
||||
|
||||
// cross - odd vert, odd horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(41, 33), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(37, 37), 10);
|
||||
|
||||
// T facing up - lined up
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(70, 36), 5);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(66, 36), 10);
|
||||
|
||||
// T facing down - lined up
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(101, 33), 5);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(97, 37), 10);
|
||||
|
||||
|
||||
// T facing left - lined up
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(10, 70), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(5, 76), 5);
|
||||
|
||||
// T facing right - even vert, odd horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(50, 70), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(45, 75), 5);
|
||||
|
||||
// T facing right - odd vert, even horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(71, 71), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(66, 76), 5);
|
||||
|
||||
// T facing left - even vert, odd horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(100, 70), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(101, 75), 5);
|
||||
|
||||
// T facing left - odd vert, even horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(131, 71), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(132, 76), 5);
|
||||
|
||||
|
||||
// T facing right - lined up
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(10, 90), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(10, 96), 5);
|
||||
|
||||
// T facing right - even vert, even horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(50, 90), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(46, 95), 5);
|
||||
|
||||
// T facing right - odd vert, odd horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(71, 91), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(67, 96), 5);
|
||||
|
||||
// T facing left - even vert, even horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(100, 90), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(100, 95), 5);
|
||||
|
||||
// T facing left - odd vert, odd horiz
|
||||
graphics_draw_vertical_line_dotted(&ctx, GPoint(131, 91), 10);
|
||||
graphics_draw_horizontal_line_dotted(&ctx, GPoint(131, 96), 5);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("draw_dotted_line_cross_color")));
|
||||
}
|
||||
|
||||
static void draw_lines_same_point(GContext *ctx) {
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_context_set_stroke_width(ctx, 1);
|
||||
graphics_draw_line(ctx, GPoint(10,10), GPoint(10,10));
|
||||
|
||||
graphics_context_set_antialiased(ctx, true);
|
||||
graphics_context_set_stroke_width(ctx, 1);
|
||||
graphics_draw_line(ctx, GPoint(20,20), GPoint(20,20));
|
||||
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_context_set_stroke_width(ctx, 2);
|
||||
graphics_draw_line(ctx, GPoint(30,30), GPoint(30,30));
|
||||
|
||||
graphics_context_set_antialiased(ctx, true);
|
||||
graphics_context_set_stroke_width(ctx, 2);
|
||||
graphics_draw_line(ctx, GPoint(40,40), GPoint(40,40));
|
||||
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_context_set_stroke_width(ctx, 3);
|
||||
graphics_draw_line(ctx, GPoint(50,50), GPoint(50,50));
|
||||
|
||||
graphics_context_set_antialiased(ctx, true);
|
||||
graphics_context_set_stroke_width(ctx, 3);
|
||||
graphics_draw_line(ctx, GPoint(60,60), GPoint(60,60));
|
||||
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_context_set_stroke_width(ctx, 4);
|
||||
graphics_draw_line(ctx, GPoint(70,70), GPoint(70,70));
|
||||
|
||||
graphics_context_set_antialiased(ctx, true);
|
||||
graphics_context_set_stroke_width(ctx, 4);
|
||||
graphics_draw_line(ctx, GPoint(80,80), GPoint(80,80));
|
||||
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_context_set_stroke_width(ctx, 5);
|
||||
graphics_draw_line(ctx, GPoint(90,90), GPoint(90,90));
|
||||
|
||||
graphics_context_set_antialiased(ctx, true);
|
||||
graphics_context_set_stroke_width(ctx, 5);
|
||||
graphics_draw_line(ctx, GPoint(100,100), GPoint(100,100));
|
||||
}
|
||||
|
||||
void test_graphics_draw_line__same_point(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
|
||||
draw_lines_same_point(&ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("draw_line_same_point")));
|
||||
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
graphics_context_set_stroke_color(&ctx, GColorRed);
|
||||
draw_lines_same_point(&ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("draw_line_same_point_color")));
|
||||
#endif
|
||||
}
|
||||
|
||||
#define CLIP_RECT_DRAW_BOX GRect(10, 10, 40, 40)
|
||||
#define CLIP_RECT_CLIP_BOX GRect(10, 10, 20, 20)
|
||||
#define CLIP_OFFSET 100
|
||||
static void prv_draw_lines(GContext *ctx, uint8_t sw, uint16_t xoffset, uint16_t yoffset) {
|
||||
// Adjust drawing box and clipping box
|
||||
ctx->draw_state.drawing_box = CLIP_RECT_DRAW_BOX;
|
||||
ctx->draw_state.drawing_box.origin.x += xoffset;
|
||||
ctx->draw_state.drawing_box.origin.y += yoffset;
|
||||
ctx->draw_state.clip_box = CLIP_RECT_CLIP_BOX;
|
||||
ctx->draw_state.clip_box.origin.x += xoffset;
|
||||
ctx->draw_state.clip_box.origin.y += yoffset;
|
||||
graphics_context_set_stroke_width(ctx, sw);
|
||||
|
||||
graphics_draw_line(ctx, GPoint(-2, 10), GPoint(2, 10)); // left
|
||||
graphics_draw_line(ctx, GPoint(-2, 5), GPoint(5, -2)); // top left corner
|
||||
|
||||
graphics_draw_line(ctx, GPoint(10, -2), GPoint(10, 2)); // top
|
||||
graphics_draw_line(ctx, GPoint(15, -2), GPoint(22, 5)); // top right corner
|
||||
|
||||
graphics_draw_line(ctx, GPoint(18, 10), GPoint(22, 10)); // right
|
||||
graphics_draw_line(ctx, GPoint(22, 15), GPoint(15, 22)); // bottom right corner
|
||||
|
||||
graphics_draw_line(ctx, GPoint(10, 18), GPoint(10, 22)); // bottom
|
||||
graphics_draw_line(ctx, GPoint(5, 22), GPoint(-2, 15)); // bottom left corner
|
||||
}
|
||||
|
||||
void test_graphics_draw_line__clipping_rect(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, false, 1);
|
||||
graphics_context_set_stroke_color(&ctx, GColorBlack);
|
||||
|
||||
// Draw lines around boundaries of clipping box - AA false, SW 1
|
||||
prv_draw_lines(&ctx, 1, 0, 0);
|
||||
|
||||
// Draw lines around boundaries of clipping box - AA false, SW 2
|
||||
prv_draw_lines(&ctx, 2, CLIP_OFFSET, 0);
|
||||
|
||||
// Draw lines around boundaries of clipping box - AA false, SW 3
|
||||
prv_draw_lines(&ctx, 3, 0, CLIP_OFFSET);
|
||||
|
||||
// Draw lines around boundaries of clipping box - AA false, SW 4
|
||||
prv_draw_lines(&ctx, 4, CLIP_OFFSET, CLIP_OFFSET);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("draw_line_clip_rect")));
|
||||
}
|
||||
|
||||
void test_graphics_draw_line__clipping_rect_aa(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, true, 1);
|
||||
graphics_context_set_stroke_color(&ctx, GColorBlack);
|
||||
|
||||
// Draw lines around boundaries of clipping box - AA true, SW 1
|
||||
prv_draw_lines(&ctx, 1, 0, 0);
|
||||
|
||||
// Draw lines around boundaries of clipping box - AA true, SW 2
|
||||
prv_draw_lines(&ctx, 2, CLIP_OFFSET, 0);
|
||||
|
||||
// Draw lines around boundaries of clipping box - AA true, SW 3
|
||||
prv_draw_lines(&ctx, 3, 0, CLIP_OFFSET);
|
||||
|
||||
// Draw lines around boundaries of clipping box - AA true, SW 4
|
||||
prv_draw_lines(&ctx, 4, CLIP_OFFSET, CLIP_OFFSET);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, TEST_NAMED_PBI_FILE("draw_line_clip_rect_aa")));
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "${BIT_DEPTH_NAME}/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_graphics_draw_pixel_${BIT_DEPTH_NAME}__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_graphics_draw_pixel_${BIT_DEPTH_NAME}__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
void inside_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_pixel(ctx, GPoint(5, 5));
|
||||
}
|
||||
|
||||
void white_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorWhite);
|
||||
graphics_draw_pixel(ctx, GPoint(5, 5));
|
||||
}
|
||||
|
||||
void clear_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorClear);
|
||||
graphics_draw_pixel(ctx, GPoint(5, 5));
|
||||
}
|
||||
|
||||
void outside_x_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_pixel(ctx, GPoint(15, 5));
|
||||
}
|
||||
|
||||
void outside_nx_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_pixel(ctx, GPoint(-5, 5));
|
||||
}
|
||||
|
||||
void outside_y_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_pixel(ctx, GPoint(5, 15));
|
||||
}
|
||||
|
||||
void outside_ny_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_pixel(ctx, GPoint(5, -5));
|
||||
}
|
||||
|
||||
void outside_x_y_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_pixel(ctx, GPoint(15, 15));
|
||||
}
|
||||
|
||||
void outside_nx_y_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_pixel(ctx, GPoint(-5, 15));
|
||||
}
|
||||
|
||||
void outside_x_ny_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_pixel(ctx, GPoint(15, -5));
|
||||
}
|
||||
|
||||
void outside_nx_ny_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_pixel(ctx, GPoint(-5, -5));
|
||||
}
|
||||
|
||||
void test_graphics_draw_pixel_${BIT_DEPTH_NAME}__origin_layer(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 10, 10));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_pixel_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &outside_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("outside_x_origin_layer", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &outside_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("outside_y_origin_layer", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &outside_x_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("outside_x_y_origin_layer", ctx.parent_framebuffer, GColorWhite));
|
||||
}
|
||||
|
||||
void test_graphics_draw_pixel_${BIT_DEPTH_NAME}__offset_layer(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(10, 10, 10, 10));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_pixel_inside_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &outside_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("outside_x_offset_layer", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &outside_nx_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("outside_nx_offset_layer", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &outside_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("outside_y_offset_layer", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &outside_ny_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("outside_ny_offset_layer", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &outside_x_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("outside_x_y_offset_layer", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &outside_nx_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("outside_nx_y_offset_layer", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &outside_x_ny_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("outside_x_ny_offset_layer", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &outside_nx_ny_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("outside_nx_ny_offset_layer", ctx.parent_framebuffer, GColorWhite));
|
||||
}
|
||||
|
||||
void test_graphics_draw_pixel_${BIT_DEPTH_NAME}__clear(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 10, 10));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_pixel_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
layer_set_update_proc(&layer, &white_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("white_over_black", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_pixel_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
layer_set_update_proc(&layer, &clear_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_pixel_clear.8bit.pbi"));
|
||||
#else
|
||||
cl_check(framebuffer_is_empty("clear_over_black", ctx.parent_framebuffer, GColorWhite));
|
||||
#endif
|
||||
}
|
||||
|
||||
#define BOX_OFFSET_X 8
|
||||
#define BOX_OFFSET_Y 4
|
||||
#define COLUMN_OFFSET_X 32
|
||||
// Draws an 8x4 rectangle starting a point p
|
||||
static void prv_draw_box(GContext *ctx, GPoint p) {
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 0, p.y));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 1, p.y));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 2, p.y));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 3, p.y));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 4, p.y));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 5, p.y));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 6, p.y));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 7, p.y));
|
||||
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 0, p.y + 1));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 1, p.y + 1));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 2, p.y + 1));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 3, p.y + 1));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 4, p.y + 1));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 5, p.y + 1));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 6, p.y + 1));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 7, p.y + 1));
|
||||
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 0, p.y + 2));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 1, p.y + 2));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 2, p.y + 2));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 3, p.y + 2));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 4, p.y + 2));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 5, p.y + 2));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 6, p.y + 2));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 7, p.y + 2));
|
||||
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 0, p.y + 3));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 1, p.y + 3));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 2, p.y + 3));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 3, p.y + 3));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 4, p.y + 3));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 5, p.y + 3));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 6, p.y + 3));
|
||||
graphics_draw_pixel(ctx, GPoint(p.x + 7, p.y + 3));
|
||||
}
|
||||
|
||||
// Draws two columns of colors (first 32 colors in first column, second 32 colors in second column)
|
||||
// Offsets the two color columns based on input transparency
|
||||
static void prv_draw_boxes(GContext *ctx, uint8_t transparency) {
|
||||
int column = 3 - transparency;
|
||||
for (uint8_t color_index = 0; color_index < 64; color_index++) {
|
||||
uint8_t color_col = (color_index < 32) ? 0 : 1;
|
||||
ctx->draw_state.stroke_color = (GColor) { .argb = ((transparency << 6) | color_index) };
|
||||
prv_draw_box(ctx, GPoint((4 + (BOX_OFFSET_X * color_col) + (COLUMN_OFFSET_X * column)),
|
||||
(BOX_OFFSET_Y + ((color_index - (32 * color_col)) * 4 ))));
|
||||
}
|
||||
}
|
||||
|
||||
#define ORIGIN_RECT_NO_CLIP GRect(0, 0, 144, 168)
|
||||
void test_graphics_draw_pixel_8bit__transparent(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_context_set_fill_color(&ctx, GColorBlack);
|
||||
graphics_fill_rect(&ctx, &ORIGIN_RECT_NO_CLIP);
|
||||
|
||||
// No transparency
|
||||
prv_draw_boxes(&ctx, 3);
|
||||
|
||||
// 33% transparency
|
||||
prv_draw_boxes(&ctx, 2);
|
||||
|
||||
// 66% transparency - should draw nothing according to current implementation
|
||||
prv_draw_boxes(&ctx, 1);
|
||||
|
||||
// 100% transparency - should draw nothing according to current implementation
|
||||
prv_draw_boxes(&ctx, 0);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_pixel_transparent.8bit.pbi"));
|
||||
}
|
||||
|
||||
|
||||
#define CLIP_RECT_DRAW_BOX GRect(10, 10, 40, 40)
|
||||
#define CLIP_RECT_CLIP_BOX GRect(10, 10, 20, 20)
|
||||
static void prv_draw_pixels(GContext *ctx, bool aa, uint8_t sw) {
|
||||
test_graphics_context_reset(ctx, fb);
|
||||
setup_test_aa_sw(ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, aa, sw);
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
|
||||
// Left boundary
|
||||
graphics_draw_pixel(ctx, GPoint(-1, 5));
|
||||
graphics_draw_pixel(ctx, GPoint(0, 10));
|
||||
graphics_draw_pixel(ctx, GPoint(1, 15));
|
||||
|
||||
// Right boundary
|
||||
graphics_draw_pixel(ctx, GPoint(19, 5));
|
||||
graphics_draw_pixel(ctx, GPoint(20, 10));
|
||||
graphics_draw_pixel(ctx, GPoint(21, 15));
|
||||
|
||||
// Top boundary
|
||||
graphics_draw_pixel(ctx, GPoint(5, -1));
|
||||
graphics_draw_pixel(ctx, GPoint(10, 0));
|
||||
graphics_draw_pixel(ctx, GPoint(15, 1));
|
||||
|
||||
// Bottom boundary
|
||||
graphics_draw_pixel(ctx, GPoint(5, 19));
|
||||
graphics_draw_pixel(ctx, GPoint(10, 20));
|
||||
graphics_draw_pixel(ctx, GPoint(15, 21));
|
||||
}
|
||||
|
||||
void test_graphics_draw_pixel_${BIT_DEPTH_NAME}__clipping_rect(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Draw pixels around boundaries of clipping box - AA false, SW 1
|
||||
prv_draw_pixels(&ctx, false, 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_pixel_clip_rect.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
@@ -0,0 +1,670 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "${BIT_DEPTH_NAME}/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_graphics_draw_rect_${BIT_DEPTH_NAME}__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_graphics_draw_rect_${BIT_DEPTH_NAME}__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
static void inside_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_rect(ctx, &GRect(4, 2, 16, 8));
|
||||
}
|
||||
|
||||
static void white_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorWhite);
|
||||
graphics_draw_rect(ctx, &GRect(4, 2, 16, 8));
|
||||
}
|
||||
|
||||
static void clear_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorClear);
|
||||
graphics_draw_rect(ctx, &GRect(4, 2, 16, 8));
|
||||
}
|
||||
|
||||
static void across_x_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_rect(ctx, &GRect(10, 2, 18, 4));
|
||||
}
|
||||
|
||||
static void across_nx_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_rect(ctx, &GRect(-10, 2, 18, 4));
|
||||
}
|
||||
|
||||
static void across_y_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_rect(ctx, &GRect(4, 5, 18, 10));
|
||||
}
|
||||
|
||||
static void across_ny_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_rect(ctx, &GRect(4, -5, 18, 10));
|
||||
}
|
||||
|
||||
void test_graphics_draw_rect_${BIT_DEPTH_NAME}__origin_layer(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 20, 10));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_across_x_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_nx_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_across_nx_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_across_y_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_ny_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_across_ny_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_rect_${BIT_DEPTH_NAME}__offset_layer(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(10, 15, 20, 10));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_inside_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_across_x_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_nx_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_across_nx_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_across_y_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_ny_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_across_ny_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_rect_${BIT_DEPTH_NAME}__color(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 20, 10));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
layer_set_update_proc(&layer, &white_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("white_over_black", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
layer_set_update_proc(&layer, &clear_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("clear_over_black", ctx.parent_framebuffer, GColorWhite));
|
||||
}
|
||||
|
||||
#define ORIGIN_RECT_NO_CLIP GRect(0, 0, 144, 168)
|
||||
#define ORIGIN_RECT_CLIP_XY GRect(0, 0, 20, 20)
|
||||
#define ORIGIN_RECT_CLIP_NXNY GRect(0, 0, 144, 168)
|
||||
#define ORIGIN_DRAW_RECT_NO_CLIP GRect(6, 6, 30, 40)
|
||||
#define ORIGIN_DRAW_RECT_CLIP_XY GRect(6, 6, 30, 40)
|
||||
#define ORIGIN_DRAW_RECT_CLIP_NXNY GRect(-16, -16, 30, 40)
|
||||
|
||||
void test_graphics_draw_rect_${BIT_DEPTH_NAME}__origin_aa_sw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// AA = true, SW = 1
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw1_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw1_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// AA = true, SW = 2
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 2);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw2_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 2);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw2_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 2);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw2_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// AA = true, SW = 3
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 3);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw3_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 3);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw3_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 3);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
// AA = true, SW = 4
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 4);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw4_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 4);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw4_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 4);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw4_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
#endif
|
||||
|
||||
// AA = true, SW = 5
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 5);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw5_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 5);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw5_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 5);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw5_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
// AA = true, SW = 11
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 11);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw11_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 11);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_aa_sw11_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void test_graphics_draw_rect_${BIT_DEPTH_NAME}__origin_sw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// SW = 1
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, false, 1);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw1_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, false, 1);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw1_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// SW = 2
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 2);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw2_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, false, 2);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw2_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, false, 2);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw2_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// SW = 3
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 3);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw3_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, false, 3);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw3_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, false, 3);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
// SW = 4
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 4);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw4_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, false, 4);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw4_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, false, 4);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw4_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
#endif
|
||||
|
||||
// SW = 5
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 5);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw5_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, false, 5);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw5_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, false, 5);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw5_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
// SW = 11
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 11);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, false, 11);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw11_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, false, 11);
|
||||
graphics_draw_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_origin_sw11_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#define OFFSET_RECT_NO_CLIP GRect(20, 10, 144, 168)
|
||||
#define OFFSET_RECT_CLIP_XY GRect(20, 10, 20, 20)
|
||||
#define OFFSET_RECT_CLIP_NXNY GRect(20, 10, 144, 168)
|
||||
#define OFFSET_DRAW_RECT_NO_CLIP GRect(6, 6, 30, 40)
|
||||
#define OFFSET_DRAW_RECT_CLIP_XY GRect(6, 6, 30, 40)
|
||||
#define OFFSET_DRAW_RECT_CLIP_NXNY GRect(-16, -16, 30, 40)
|
||||
|
||||
void test_graphics_draw_rect_${BIT_DEPTH_NAME}__offset_aa_sw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// AA = true, SW = 1
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 1);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw1_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw1_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// AA = true, SW = 2
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 2);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw2_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 2);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw2_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 2);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw2_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// AA = true, SW = 3
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 3);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw3_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 3);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw3_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 3);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
// AA = true, SW = 4
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 4);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw4_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 4);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw4_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 4);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw4_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
#endif
|
||||
|
||||
// AA = true, SW = 5
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 5);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw5_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 5);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw5_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 5);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw5_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
// AA = true, SW = 11
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 11);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 11);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw11_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 11);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_aa_sw11_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void test_graphics_draw_rect_${BIT_DEPTH_NAME}__offset_sw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// SW = 1
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, false, 1);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw1_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, false, 1);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw1_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// SW = 2
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 2);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw2_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, false, 2);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw2_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, false, 2);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw2_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// SW = 3
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 3);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw3_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, false, 3);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw3_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, false, 3);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
// SW = 4
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 4);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw4_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, false, 4);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw4_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, false, 4);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw4_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
#endif
|
||||
|
||||
// SW = 5
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 5);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw5_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, false, 5);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw5_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, false, 5);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw5_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
// SW = 11
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 11);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, false, 11);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw11_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, false, 11);
|
||||
graphics_draw_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_offset_sw11_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#define BOX_SIZE 8
|
||||
#define CLIP_RECT_DRAW_BOX GRect(10, 10, 140, 30)
|
||||
#define CLIP_RECT_CLIP_BOX GRect(10, 10, 120, 2*BOX_SIZE + 4)
|
||||
#define CLIP_RECT_RECT_BOX GRect(0, 0, BOX_SIZE, BOX_SIZE)
|
||||
#define CLIP_OFFSET 40
|
||||
// yoffset if used to allow for multiple test cases to be drawn and tested on the same test image
|
||||
// nudge is used to move the drawn rectangle just outside each corner the clipping
|
||||
static void prv_draw_rects(GContext *ctx, uint8_t sw, uint16_t yoffset, int16_t nudge) {
|
||||
// Adjust drawing box and clipping box
|
||||
ctx->draw_state.drawing_box = CLIP_RECT_DRAW_BOX;
|
||||
ctx->draw_state.drawing_box.origin.y += yoffset;
|
||||
ctx->draw_state.clip_box = CLIP_RECT_CLIP_BOX;
|
||||
ctx->draw_state.clip_box.origin.y += yoffset;
|
||||
graphics_context_set_stroke_width(ctx, sw);
|
||||
|
||||
// Top left corner of clipping box
|
||||
GRect box = CLIP_RECT_RECT_BOX;
|
||||
box.origin.x -= nudge;
|
||||
box.origin.y -= nudge;
|
||||
graphics_draw_rect(ctx, &box);
|
||||
|
||||
// Top right corner of clipping box
|
||||
box.origin.x = CLIP_RECT_CLIP_BOX.size.w - BOX_SIZE;
|
||||
box.origin.x += nudge;
|
||||
graphics_draw_rect(ctx, &box);
|
||||
|
||||
// Bottom right corner of clipping box
|
||||
box.origin.y = CLIP_RECT_CLIP_BOX.size.h - BOX_SIZE;
|
||||
box.origin.y += nudge;
|
||||
graphics_draw_rect(ctx, &box);
|
||||
|
||||
// Bottom left corner of clipping box
|
||||
box.origin.x = 0;
|
||||
box.origin.x -= nudge;
|
||||
graphics_draw_rect(ctx, &box);
|
||||
}
|
||||
|
||||
void test_graphics_draw_rect_${BIT_DEPTH_NAME}__clipping_rect(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, false, 1);
|
||||
graphics_context_set_stroke_color(&ctx, GColorBlack);
|
||||
|
||||
// Draw rectangles around boundaries of clipping box - AA false
|
||||
prv_draw_rects(&ctx, 1, 0, 0);
|
||||
prv_draw_rects(&ctx, 2, CLIP_OFFSET, 0);
|
||||
prv_draw_rects(&ctx, 3, 2 * CLIP_OFFSET, 0);
|
||||
prv_draw_rects(&ctx, 4, 3 * CLIP_OFFSET, 0);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_clip_rect.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, false, 1);
|
||||
prv_draw_rects(&ctx, 1, 0, 1);
|
||||
prv_draw_rects(&ctx, 2, CLIP_OFFSET, 1);
|
||||
prv_draw_rects(&ctx, 3, 2 * CLIP_OFFSET, 1);
|
||||
prv_draw_rects(&ctx, 4, 3 * CLIP_OFFSET, 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_clip_rect_nudge.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_rect_${BIT_DEPTH_NAME}__clipping_rect_aa(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, true, 1);
|
||||
graphics_context_set_stroke_color(&ctx, GColorBlack);
|
||||
|
||||
// Draw rectangles around boundaries of clipping box - AA true
|
||||
prv_draw_rects(&ctx, 1, 0, 0);
|
||||
prv_draw_rects(&ctx, 2, CLIP_OFFSET, 0);
|
||||
prv_draw_rects(&ctx, 3, 2 * CLIP_OFFSET, 0);
|
||||
prv_draw_rects(&ctx, 4, 3 * CLIP_OFFSET, 0);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_clip_rect_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, true, 1);
|
||||
prv_draw_rects(&ctx, 1, 0, 1);
|
||||
prv_draw_rects(&ctx, 2, CLIP_OFFSET, 1);
|
||||
prv_draw_rects(&ctx, 3, 2 * CLIP_OFFSET, 1);
|
||||
prv_draw_rects(&ctx, 4, 3 * CLIP_OFFSET, 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rect_clip_rect_aa_nudge.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
#define ORIGIN_RECT GRect(0, 0, 144, 4)
|
||||
#define LEFT_EDGE_RECT GRect(5, 4, 59, 4)
|
||||
#define RIGHT_EDGE_RECT GRect(0, 8, 40, 4)
|
||||
#define BOTH_EDGE_RECT GRect(5, 12, 20, 4)
|
||||
#define CLIPPED_RECT GRect(-10,16,20,4)
|
||||
#define OVERLAP_RECT GRect(0,0,20,20)
|
||||
#define CORNER_RADIUS_RECT GRect(5,24,20,20)
|
||||
|
||||
static void prv_draw_dither_rects(GContext* ctx, GColor8 fill_color) {
|
||||
test_graphics_context_init(ctx, fb);
|
||||
graphics_context_set_fill_color(ctx, fill_color);
|
||||
|
||||
graphics_fill_rect(ctx, &ORIGIN_RECT);
|
||||
graphics_fill_rect(ctx, &LEFT_EDGE_RECT);
|
||||
graphics_fill_rect(ctx, &RIGHT_EDGE_RECT);
|
||||
graphics_fill_rect(ctx, &BOTH_EDGE_RECT);
|
||||
graphics_fill_rect(ctx, &CLIPPED_RECT);
|
||||
graphics_fill_round_rect(ctx, &CORNER_RADIUS_RECT, 4, GCornersAll);
|
||||
ctx->draw_state.drawing_box = GRect(100,2, 40, 40);
|
||||
graphics_fill_rect(ctx, &OVERLAP_RECT);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx->dest_bitmap, "draw_multiple_rect_dithered.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_rect_${BIT_DEPTH_NAME}__dithering_gray(void) {
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 1
|
||||
GContext ctx;
|
||||
prv_draw_dither_rects(&ctx, GColorLightGray);
|
||||
prv_draw_dither_rects(&ctx, GColorDarkGray);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* 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 "applib/graphics/framebuffer.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "util/trig.h"
|
||||
|
||||
#include "applib/ui/layer.h"
|
||||
#include "applib/ui/window_private.h"
|
||||
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 1
|
||||
#include "1bit/test_framebuffer.h"
|
||||
#else
|
||||
#include "8bit/test_framebuffer.h"
|
||||
#endif
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Fakes
|
||||
#include "fake_gbitmap_get_data_row.h"
|
||||
|
||||
// Setup
|
||||
////////////////////////////////////
|
||||
static GBitmap *test_image_bw;
|
||||
static GBitmap *test_image_color;
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 1
|
||||
extern bool get_bitmap_bit(GBitmap *bmp, int x, int y);
|
||||
#elif SCREEN_COLOR_DEPTH_BITS == 8
|
||||
extern GColor get_bitmap_color(GBitmap *bmp, int x, int y);
|
||||
#endif
|
||||
|
||||
void test_graphics_draw_rotated_bitmap__initialize(void) {
|
||||
s_fake_data_row_handling = false;
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
test_image_bw = get_gbitmap_from_pbi("test_rotated_bitmap_no_litter.Xbit.pbi");
|
||||
cl_assert(test_image_bw != NULL);
|
||||
|
||||
test_image_color = get_gbitmap_from_pbi("test_rotated_bitmap_redstar.Xbit.pbi");
|
||||
cl_assert(test_image_color != NULL);
|
||||
}
|
||||
|
||||
void test_graphics_draw_rotated_bitmap__cleanup(void) {
|
||||
free(fb);
|
||||
if (test_image_bw) {
|
||||
if (test_image_bw->addr) {
|
||||
free(test_image_bw->addr);
|
||||
}
|
||||
free(test_image_bw);
|
||||
}
|
||||
|
||||
if (test_image_color) {
|
||||
if (test_image_color->addr) {
|
||||
free(test_image_color->addr);
|
||||
}
|
||||
free(test_image_color);
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_test_rotate_bitmap(GContext *ctx, FrameBuffer *fb,
|
||||
GRect clip_box, GRect drawing_box,
|
||||
GCompOp compositing_mode) {
|
||||
test_graphics_context_reset(ctx, fb);
|
||||
|
||||
GDrawState draw_state = {
|
||||
.clip_box = clip_box,
|
||||
.drawing_box = drawing_box,
|
||||
.compositing_mode = compositing_mode,
|
||||
};
|
||||
setup_test_context(ctx,
|
||||
(CTX_FLAG_DS_CLIP_BOX | CTX_FLAG_DS_DRAWING_BOX |
|
||||
CTX_FLAG_DS_COMPOSITING_MODE),
|
||||
&draw_state, NULL);
|
||||
}
|
||||
|
||||
#define ORIGIN_RECT_NO_CLIP GRect(0, 0, DISP_COLS, DISP_ROWS)
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
void test_graphics_draw_rotated_bitmap__get_color(void) {
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 1
|
||||
cl_check(get_bitmap_bit(test_image_bw, 8, 16) == 1);
|
||||
cl_check(get_bitmap_bit(test_image_bw, 8, 24) == 0);
|
||||
cl_check(get_bitmap_bit(test_image_color, 30, 2) == 0);
|
||||
cl_check(get_bitmap_bit(test_image_color, 30, 10) == 0);
|
||||
cl_check(get_bitmap_bit(test_image_color, 30, 30) == 1);
|
||||
#elif SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gcolor_equal(get_bitmap_color(test_image_bw, 8, 16), GColorWhite));
|
||||
cl_check(gcolor_equal(get_bitmap_color(test_image_bw, 8, 24), GColorBlack));
|
||||
cl_check(gcolor_equal(get_bitmap_color(test_image_color, 30, 2), GColorClear));
|
||||
cl_check(gcolor_equal(get_bitmap_color(test_image_color, 30, 10), GColorRed));
|
||||
cl_check(gcolor_equal(get_bitmap_color(test_image_color, 30, 30), GColorScreaminGreen));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void test_graphics_draw_rotated_bitmap__origin_bw_assign(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// No Clip, Angle 0
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_bw,
|
||||
GPointZero, DEG_TO_TRIGANGLE(0), GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rotated_bitmap_origin_bw_assign_0.Xbit.pbi"));
|
||||
|
||||
// Top-left corner rotation point, Angle 45
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_bw,
|
||||
GPointZero, DEG_TO_TRIGANGLE(45), GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"draw_rotated_bitmap_origin_bw_assign_corner_45.Xbit.pbi"));
|
||||
|
||||
// Top-left center rotation point, Angle 45
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_bw,
|
||||
GPoint(27, 40), DEG_TO_TRIGANGLE(45), GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"draw_rotated_bitmap_origin_bw_assign_center_45.Xbit.pbi"));
|
||||
|
||||
}
|
||||
|
||||
void test_graphics_draw_rotated_bitmap__origin_bw_set(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// No Clip, Angle 0
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpSet);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_bw,
|
||||
GPointZero, DEG_TO_TRIGANGLE(0), GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rotated_bitmap_origin_bw_set_0.Xbit.pbi"));
|
||||
|
||||
// Top-left corner rotation point, Angle 45
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpSet);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_bw,
|
||||
GPointZero, DEG_TO_TRIGANGLE(45), GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"draw_rotated_bitmap_origin_bw_set_corner_45.Xbit.pbi"));
|
||||
|
||||
// Top-left center rotation point, Angle 45
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpSet);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_bw,
|
||||
GPoint(27, 40), DEG_TO_TRIGANGLE(45), GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"draw_rotated_bitmap_origin_bw_set_center_45.Xbit.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_rotated_bitmap__offset_bw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// No Clip, Angle 0, Offset
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_bw,
|
||||
GPointZero, DEG_TO_TRIGANGLE(0), GPoint(20, 20));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rotated_bitmap_offset_bw_0.Xbit.pbi"));
|
||||
|
||||
// Top-left corner rotation point, Angle 45
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_bw,
|
||||
GPointZero, DEG_TO_TRIGANGLE(45), GPoint(20, 20));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rotated_bitmap_offset_bw_corner_45.Xbit.pbi"));
|
||||
|
||||
// Top-left center rotation point, Angle 45
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_bw,
|
||||
GPoint(27, 40), DEG_TO_TRIGANGLE(45), GPoint(20, 20));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rotated_bitmap_offset_bw_center_45.Xbit.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_rotated_bitmap__origin_color_assign(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// No Clip, Angle 0
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPointZero, DEG_TO_TRIGANGLE(0), GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rotated_bitmap_origin_color_assign_0.Xbit.pbi"));
|
||||
|
||||
// Top-left corner rotation point, Angle 45
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPointZero, DEG_TO_TRIGANGLE(45), GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"draw_rotated_bitmap_origin_color_assign_corner_45.Xbit.pbi"));
|
||||
|
||||
// Top-left center rotation point, Angle 45
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPoint(30, 30), DEG_TO_TRIGANGLE(45), GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"draw_rotated_bitmap_origin_color_assign_center_45.Xbit.pbi"));
|
||||
|
||||
// Test transparency
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_context_set_fill_color(&ctx, GColorBlue);
|
||||
graphics_fill_rect(&ctx, &GRect(0, 0, 20, 10));
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPoint(30, 30), DEG_TO_TRIGANGLE(45), GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(
|
||||
&ctx.dest_bitmap,
|
||||
"draw_rotated_bitmap_origin_color_assign_center_45_transparent.Xbit.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_rotated_bitmap__origin_color_set(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// No Clip, Angle 0
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpSet);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPointZero, DEG_TO_TRIGANGLE(0), GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rotated_bitmap_origin_color_set_0.Xbit.pbi"));
|
||||
|
||||
// Top-left corner rotation point, Angle 45
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpSet);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPointZero, DEG_TO_TRIGANGLE(45), GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"draw_rotated_bitmap_origin_color_set_corner_45.Xbit.pbi"));
|
||||
|
||||
// Top-left center rotation point, Angle 45
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpSet);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPoint(30, 30), DEG_TO_TRIGANGLE(45), GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"draw_rotated_bitmap_origin_color_set_center_45.Xbit.pbi"));
|
||||
|
||||
// Test transparency
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpSet);
|
||||
graphics_context_set_fill_color(&ctx, GColorBlue);
|
||||
graphics_fill_rect(&ctx, &GRect(0, 0, 20, 10));
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPoint(30, 30), DEG_TO_TRIGANGLE(45), GPointZero);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"draw_rotated_bitmap_origin_color_set_center_45_transparent.Xbit.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_rotated_bitmap__offset_color(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// No Clip, Angle 0
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPointZero, DEG_TO_TRIGANGLE(0), GPoint(20, 20));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rotated_bitmap_offset_color_0.Xbit.pbi"));
|
||||
|
||||
// Top-left corner rotation point, Angle 45
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPointZero, DEG_TO_TRIGANGLE(45), GPoint(20, 20));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rotated_bitmap_offset_color_corner_45.Xbit.pbi"));
|
||||
|
||||
// Top-left center rotation point, Angle 45
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPoint(30, 30), DEG_TO_TRIGANGLE(45), GPoint(20, 20));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rotated_bitmap_offset_color_center_45.Xbit.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_rotated_bitmap__offset_edge(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// bottom edge center rotation point, Angle 2
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPoint(30, 59), DEG_TO_TRIGANGLE(2), GPoint(72, 84));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rotated_bitmap_offset_bottomedge_2.Xbit.pbi"));
|
||||
|
||||
// top edge center rotation point, Angle 2
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPoint(30, 1), DEG_TO_TRIGANGLE(2), GPoint(72, 84));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rotated_bitmap_offset_topedge_2.Xbit.pbi"));
|
||||
|
||||
// left edge center rotation point, Angle 2
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPoint(1, 30), DEG_TO_TRIGANGLE(2), GPoint(72, 84));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rotated_bitmap_offset_leftedge_2.Xbit.pbi"));
|
||||
|
||||
// right edge center rotation point, Angle 2
|
||||
setup_test_rotate_bitmap(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(&ctx, test_image_color,
|
||||
GPoint(59, 30), DEG_TO_TRIGANGLE(2), GPoint(72, 84));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_rotated_bitmap_offset_rightedge_2.Xbit.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_rotated_bitmap__data_row_handling(void) {
|
||||
// Enable fake data row handling which will override the gbitmap_get_data_row_xxx() functions
|
||||
// with their fake counterparts in fake_gbitmap_get_data_row.c
|
||||
s_fake_data_row_handling = true;
|
||||
s_fake_data_row_handling_disable_vertical_flip = true;
|
||||
|
||||
GContext *ctx = malloc(sizeof(GContext));
|
||||
test_graphics_context_init(ctx, fb);
|
||||
framebuffer_clear(fb);
|
||||
|
||||
GBitmap *test_image = get_gbitmap_from_pbi("stamp.Xbit.pbi");
|
||||
cl_assert(test_image != NULL);
|
||||
|
||||
// PBL-24705 grect_center_point is off by 1
|
||||
GPoint center = GPoint(DISP_COLS / 2 - 1, DISP_ROWS / 2 - 1);
|
||||
|
||||
// No Clip, Angle 0
|
||||
setup_test_rotate_bitmap(ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(ctx, test_image, GPoint(
|
||||
test_image->bounds.size.w / 2 - 1,
|
||||
test_image->bounds.size.h / 2 - 1),
|
||||
DEG_TO_TRIGANGLE(0), center);
|
||||
cl_check(gbitmap_pbi_eq(&ctx->dest_bitmap, "draw_rotated_bitmap_stamp_0deg.Xbit.pbi"));
|
||||
|
||||
// Top-left corner rotation point, Angle 45
|
||||
setup_test_rotate_bitmap(ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(ctx, test_image,
|
||||
GPoint(71, 71), DEG_TO_TRIGANGLE(45), center);
|
||||
cl_check(gbitmap_pbi_eq(&ctx->dest_bitmap, "draw_rotated_bitmap_stamp_45deg.Xbit.pbi"));
|
||||
// Top-left corner rotation point, Angle 180
|
||||
setup_test_rotate_bitmap(ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, GCompOpAssign);
|
||||
graphics_draw_rotated_bitmap(ctx, test_image,
|
||||
GPoint(71, 71), DEG_TO_TRIGANGLE(180), center);
|
||||
cl_check(gbitmap_pbi_eq(&ctx->dest_bitmap, "draw_rotated_bitmap_stamp_180deg.Xbit.pbi"));
|
||||
}
|
||||
@@ -0,0 +1,729 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
#include "util/math.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "${BIT_DEPTH_NAME}/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
void inside_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_round_rect(ctx, &GRect(2, 2, 28, 20), 4);
|
||||
}
|
||||
|
||||
void white_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorWhite);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_round_rect(ctx, &GRect(2, 2, 28, 20), 4);
|
||||
}
|
||||
|
||||
void clear_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorClear);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_round_rect(ctx, &GRect(2, 2, 28, 20), 4);
|
||||
}
|
||||
|
||||
void across_x_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_round_rect(ctx, &GRect(16, 2, 28, 20), 4);
|
||||
}
|
||||
|
||||
void across_nx_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_round_rect(ctx, &GRect(-14, 2, 28, 20), 4);
|
||||
}
|
||||
|
||||
void across_y_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_round_rect(ctx, &GRect(2, 12, 28, 20), 4);
|
||||
}
|
||||
|
||||
void across_ny_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_round_rect(ctx, &GRect(2, -10, 28, 20), 4);
|
||||
}
|
||||
|
||||
void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__origin_layer(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 32, 24));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_across_x_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_across_x_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_across_y_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_across_y_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__offset_layer(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(10, 15, 32, 24));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_inside_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_across_x_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_nx_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_across_nx_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_across_y_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_ny_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_across_ny_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__clear(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 32, 24));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
layer_set_update_proc(&layer, &white_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("white_over_black", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
layer_set_update_proc(&layer, &clear_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_clear.8bit.pbi"));
|
||||
#else
|
||||
cl_check(framebuffer_is_empty("clear_over_black", ctx.parent_framebuffer, GColorWhite));
|
||||
#endif
|
||||
}
|
||||
|
||||
#define RADIUS_DEFAULT 4
|
||||
#define RECT_WIDTH 30
|
||||
#define RECT_HEIGHT 40
|
||||
#define ORIGIN_RECT_NO_CLIP GRect(0, 0, 144, 168)
|
||||
#define ORIGIN_RECT_CLIP_XY GRect(0, 0, 20, 20)
|
||||
#define ORIGIN_RECT_CLIP_NXNY GRect(0, 0, 144, 168)
|
||||
#define ORIGIN_DRAW_RECT_NO_CLIP GRect(6, 6, RECT_WIDTH, RECT_HEIGHT)
|
||||
#define ORIGIN_DRAW_RECT_CLIP_XY GRect(6, 6, RECT_WIDTH, RECT_HEIGHT)
|
||||
#define ORIGIN_DRAW_RECT_CLIP_NXNY GRect(-16, -16, RECT_WIDTH, RECT_HEIGHT)
|
||||
|
||||
void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__origin_aa_sw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
// AA = true, SW = 1
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw1_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw1_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// AA = true, SW = 2
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 2);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw2_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 2);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw2_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 2);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw2_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
// AA = true, SW = 3
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 3);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw3_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 3);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw3_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 3);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
// AA = true, SW = 4
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 4);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw4_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 4);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw4_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 4);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw4_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
// AA = true, SW = 5
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 5);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw5_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 5);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw5_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 5);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw5_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// AA = true, SW = 11
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw11_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_aa_sw11_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
}
|
||||
|
||||
void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__origin_sw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// SW = 1
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, false, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw1_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, false, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw1_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// SW = 2
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 2);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw2_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, false, 2);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw2_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, false, 2);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw2_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// SW = 3
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 3);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw3_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, false, 3);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw3_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, false, 3);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
// SW = 4
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 4);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw4_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, false, 4);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw4_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, false, 4);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw4_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
// SW = 5
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 5);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw5_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, false, 5);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw5_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, false, 5);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw5_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// SW = 11
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, false, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw11_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, false, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r4_sw11_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__origin_radius_aa_sw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// AA = true, SW = 1
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 0);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r0_aa_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r1_aa_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 2);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r2_aa_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 3);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r3_aa_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_rmax_aa_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Should not draw anything
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_rmax1_aa_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Should not draw anything
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) + 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_rmax2_aa_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// AA = true, SW = 11
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 0);
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r0_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 1);
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r1_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 2);
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r2_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 3);
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r3_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1);
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_rmax_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_rmax1_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) + 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_rmax2_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__origin_radius_sw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
// AA = false, SW = 1
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 0);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r0_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r1_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 2);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r2_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 3);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r3_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_rmax_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Should not draw anything
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_rmax1_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Should not draw anything
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) + 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_rmax2_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// AA = false, SW = 11
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 0);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r0_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r1_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 2);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r2_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 3);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_r3_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_rmax_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Should not draw anything
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_rmax1_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Should not draw anything
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 11);
|
||||
graphics_draw_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) + 1);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_origin_rmax2_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
#define OFFSET_RECT_NO_CLIP GRect(20, 10, 144, 168)
|
||||
#define OFFSET_RECT_CLIP_XY GRect(20, 10, 20, 20)
|
||||
#define OFFSET_RECT_CLIP_NXNY GRect(20, 10, 144, 168)
|
||||
#define OFFSET_DRAW_RECT_NO_CLIP GRect(6, 6, RECT_WIDTH, RECT_HEIGHT)
|
||||
#define OFFSET_DRAW_RECT_CLIP_XY GRect(6, 6, RECT_WIDTH, RECT_HEIGHT)
|
||||
#define OFFSET_DRAW_RECT_CLIP_NXNY GRect(-16, -16, RECT_WIDTH, RECT_HEIGHT)
|
||||
|
||||
void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__offset_aa_sw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
// AA = true, SW = 1
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 1);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 1);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw1_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw1_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// AA = true, SW = 2
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 2);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw2_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 2);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw2_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 2);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw2_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
// AA = true, SW = 3
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 3);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw3_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 3);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw3_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 3);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
// AA = true, SW = 4
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 4);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw4_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 4);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw4_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 4);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw4_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
// AA = true, SW = 5
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 5);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw5_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 5);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw5_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 5);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw5_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// AA = true, SW = 11
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 11);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 11);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw11_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 11);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_aa_sw11_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_round_rect_${BIT_DEPTH_NAME}__offset_sw(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
// SW = 1
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 1);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, false, 1);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw1_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, false, 1);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw1_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// SW = 2
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 2);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw2_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, false, 2);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw2_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, false, 2);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw2_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
// SW = 3
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 3);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw3_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, false, 3);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw3_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, false, 3);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw3_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
// SW = 4
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 4);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw4_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, false, 4);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw4_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, false, 4);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw4_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
// SW = 5
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 5);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw5_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, false, 5);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw5_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, false, 5);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw5_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// SW = 11
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 11);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_NO_CLIP, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw11_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, false, 11);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_XY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw11_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, false, 11);
|
||||
graphics_draw_round_rect(&ctx, &OFFSET_DRAW_RECT_CLIP_NXNY, RADIUS_DEFAULT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_round_rect_offset_r4_sw11_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
#include "applib/graphics/bitblt_private.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "${BIT_DEPTH_NAME}/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_graphics_draw_stroke_${BIT_DEPTH_NAME}__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_graphics_draw_stroke_${BIT_DEPTH_NAME}__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
void inside_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_stroke_width(ctx, 10);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(5, 35), GPoint(45, 40));
|
||||
}
|
||||
|
||||
void white_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorWhite);
|
||||
graphics_context_set_stroke_width(ctx, 10);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(5, 35), GPoint(45, 40));
|
||||
}
|
||||
|
||||
void clear_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorClear);
|
||||
graphics_context_set_stroke_width(ctx, 10);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(5, 35), GPoint(45, 40));
|
||||
}
|
||||
|
||||
void across_x_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_stroke_width(ctx, 10);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(50, 40), GPoint(70, 35));
|
||||
}
|
||||
|
||||
void across_nx_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_stroke_width(ctx, 10);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(-25, 35), GPoint(15, 40));
|
||||
}
|
||||
|
||||
void across_y_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_stroke_width(ctx, 10);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(40, 50), GPoint(35, 70));
|
||||
}
|
||||
|
||||
void across_ny_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_stroke_width(ctx, 10);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(5, -30), GPoint(45, 30));
|
||||
}
|
||||
|
||||
void across_screen_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_stroke_width(ctx, 10);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(25, 25), GPoint(119, 143));
|
||||
}
|
||||
|
||||
void same_start_stop_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_context_set_stroke_width(ctx, 10);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_draw_line(ctx, GPoint(20, 20), GPoint(20, 20));
|
||||
}
|
||||
|
||||
#define ORIGIN_RECT_NO_CLIP GRect(0, 0, 144, 168)
|
||||
#define ORIGIN_RECT_CLIP_XY GRect(0, 0, 30, 40)
|
||||
#define ORIGIN_RECT_CLIP_NXNY GRect(0, 0, 30, 40)
|
||||
#define START_ON_ORIGIN_RECT GPoint(5, 5)
|
||||
#define END_ON_ORIGIN_RECT GPoint(25, 25)
|
||||
#define START_ON_ORIGIN_RECT_XY GPoint(15, 15)
|
||||
#define END_ON_ORIGIN_RECT_XY GPoint(35, 35)
|
||||
#define START_ON_ORIGIN_RECT_NXNY GPoint(-5, -5)
|
||||
#define END_ON_ORIGIN_RECT_NXNY GPoint(15, 15)
|
||||
#define STROKE_WIDTH 10
|
||||
|
||||
void test_graphics_draw_stroke_${BIT_DEPTH_NAME}__origin_layer(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_WIDTH);
|
||||
graphics_draw_line(&ctx, START_ON_ORIGIN_RECT, END_ON_ORIGIN_RECT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, STROKE_WIDTH);
|
||||
graphics_draw_line(&ctx, START_ON_ORIGIN_RECT_XY, END_ON_ORIGIN_RECT_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_across_x_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, STROKE_WIDTH);
|
||||
graphics_draw_line(&ctx, START_ON_ORIGIN_RECT_NXNY, END_ON_ORIGIN_RECT_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_across_nxny_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_WIDTH);
|
||||
graphics_draw_line(&ctx, END_ON_ORIGIN_RECT, END_ON_ORIGIN_RECT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_same_point_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
#define OFFSET_RECT_NO_CLIP GRect(10, 10, 144, 168)
|
||||
#define OFFSET_RECT_CLIP_XY GRect(10, 10, 30, 40)
|
||||
#define OFFSET_RECT_CLIP_NXNY GRect(10, 10, 30, 40)
|
||||
|
||||
void test_graphics_draw_stroke_${BIT_DEPTH_NAME}__offset_layer(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, STROKE_WIDTH);
|
||||
graphics_draw_line(&ctx, START_ON_ORIGIN_RECT, END_ON_ORIGIN_RECT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_inside_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, STROKE_WIDTH);
|
||||
graphics_draw_line(&ctx, START_ON_ORIGIN_RECT_XY, END_ON_ORIGIN_RECT_XY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_across_x_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, STROKE_WIDTH);
|
||||
graphics_draw_line(&ctx, START_ON_ORIGIN_RECT_NXNY, END_ON_ORIGIN_RECT_NXNY);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_across_nxny_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, STROKE_WIDTH);
|
||||
graphics_draw_line(&ctx, END_ON_ORIGIN_RECT, END_ON_ORIGIN_RECT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_same_point_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
#define COLOR_START_POINT GPoint(5, 35)
|
||||
#define COLOR_END_POINT GPoint(45, 40)
|
||||
|
||||
void test_graphics_draw_stroke_${BIT_DEPTH_NAME}__color(void) {
|
||||
// TODO: Fix blending and reenable this - PBL-16509
|
||||
/*
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 10);
|
||||
graphics_context_set_stroke_color(&ctx, GColorBlack);
|
||||
graphics_draw_line(&ctx, COLOR_START_POINT, COLOR_END_POINT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 10);
|
||||
graphics_context_set_stroke_color(&ctx, GColorClear);
|
||||
graphics_draw_line(&ctx, COLOR_START_POINT, COLOR_END_POINT);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
#include "applib/graphics/bitblt_private.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "${BIT_DEPTH_NAME}/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_graphics_draw_stroke_precise_${BIT_DEPTH_NAME}__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_graphics_draw_stroke_precise_${BIT_DEPTH_NAME}__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
#define ORIGIN_RECT_NO_CLIP GRect(0, 0, 144, 168)
|
||||
#define ORIGIN_RECT_CLIP_XY GRect(0, 0, 30, 40)
|
||||
#define ORIGIN_RECT_CLIP_NXNY GRect(0, 0, 30, 40)
|
||||
#define START_ON_ORIGIN_RECT GPointPrecise(5, 5)
|
||||
#define END_ON_ORIGIN_RECT GPointPrecise(25, 25)
|
||||
#define START_ON_ORIGIN_RECT_XY GPointPrecise(15, 15)
|
||||
#define END_ON_ORIGIN_RECT_XY GPointPrecise(35, 35)
|
||||
#define START_ON_ORIGIN_RECT_NXNY GPointPrecise(-5, -5)
|
||||
#define END_ON_ORIGIN_RECT_NXNY GPointPrecise(15, 15)
|
||||
|
||||
void test_graphics_draw_stroke_precise_${BIT_DEPTH_NAME}__origin_layer_aa(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if PBL_COLOR
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 10);
|
||||
graphics_line_draw_precise_stroked_aa(&ctx, START_ON_ORIGIN_RECT, END_ON_ORIGIN_RECT, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_inside_origin_layer_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 10);
|
||||
graphics_line_draw_precise_stroked_aa(&ctx, START_ON_ORIGIN_RECT_XY, END_ON_ORIGIN_RECT_XY, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_across_x_origin_layer_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 10);
|
||||
graphics_line_draw_precise_stroked_aa(&ctx, START_ON_ORIGIN_RECT_NXNY, END_ON_ORIGIN_RECT_NXNY, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_across_nxny_origin_layer_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 10);
|
||||
graphics_line_draw_precise_stroked_aa(&ctx, END_ON_ORIGIN_RECT, END_ON_ORIGIN_RECT, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_same_point_origin_layer_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_graphics_draw_stroke_precise_${BIT_DEPTH_NAME}__origin_layer_non_aa(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 10);
|
||||
graphics_line_draw_precise_stroked_non_aa(&ctx, START_ON_ORIGIN_RECT, END_ON_ORIGIN_RECT, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_inside_origin_layer_non_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, false, 10);
|
||||
graphics_line_draw_precise_stroked_non_aa(&ctx, START_ON_ORIGIN_RECT_XY, END_ON_ORIGIN_RECT_XY, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_across_x_origin_layer_non_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, false, 10);
|
||||
graphics_line_draw_precise_stroked_non_aa(&ctx, START_ON_ORIGIN_RECT_NXNY, END_ON_ORIGIN_RECT_NXNY, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_across_nxny_origin_layer_non_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 10);
|
||||
graphics_line_draw_precise_stroked_non_aa(&ctx, END_ON_ORIGIN_RECT, END_ON_ORIGIN_RECT, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_same_point_origin_layer_non_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
#define OFFSET_RECT_NO_CLIP GRect(10, 10, 144, 168)
|
||||
#define OFFSET_RECT_CLIP_XY GRect(10, 10, 30, 40)
|
||||
#define OFFSET_RECT_CLIP_NXNY GRect(10, 10, 30, 40)
|
||||
|
||||
void test_graphics_draw_stroke_precise_${BIT_DEPTH_NAME}__offset_layer_aa(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
#if PBL_COLOR
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 10);
|
||||
graphics_line_draw_precise_stroked_aa(&ctx, START_ON_ORIGIN_RECT, END_ON_ORIGIN_RECT, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_inside_offset_layer_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 10);
|
||||
graphics_line_draw_precise_stroked_aa(&ctx, START_ON_ORIGIN_RECT_XY, END_ON_ORIGIN_RECT_XY, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_across_x_offset_layer_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 10);
|
||||
graphics_line_draw_precise_stroked_aa(&ctx, START_ON_ORIGIN_RECT_NXNY, END_ON_ORIGIN_RECT_NXNY, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_across_nxny_offset_layer_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix offset calculation and reenable this: - PBL-16509
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 10);
|
||||
graphics_line_draw_precise_stroked_aa(&ctx, END_ON_ORIGIN_RECT, END_ON_ORIGIN_RECT, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_same_point_offset_layer_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_graphics_draw_stroke_precise_${BIT_DEPTH_NAME}__offset_layer_non_aa(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 10);
|
||||
graphics_line_draw_precise_stroked_non_aa(&ctx, START_ON_ORIGIN_RECT, END_ON_ORIGIN_RECT, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_inside_offset_layer_non_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, false, 10);
|
||||
graphics_line_draw_precise_stroked_non_aa(&ctx, START_ON_ORIGIN_RECT_XY, END_ON_ORIGIN_RECT_XY, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_across_x_offset_layer_non_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, false, 10);
|
||||
graphics_line_draw_precise_stroked_non_aa(&ctx, START_ON_ORIGIN_RECT_NXNY, END_ON_ORIGIN_RECT_NXNY, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_across_nxny_offset_layer_non_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, false, 10);
|
||||
graphics_line_draw_precise_stroked_non_aa(&ctx, END_ON_ORIGIN_RECT, END_ON_ORIGIN_RECT, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_same_point_offset_layer_non_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
#define COLOR_START_POINT GPointPrecise(5, 35)
|
||||
#define COLOR_END_POINT GPointPrecise(45, 40)
|
||||
|
||||
void test_graphics_draw_stroke_${BIT_DEPTH_NAME}__color(void) {
|
||||
// TODO: Fix blending and reenable this - PBL-16509
|
||||
/*
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 10);
|
||||
graphics_context_set_stroke_color(&ctx, GColorBlack);
|
||||
graphics_line_draw_precise_stroked_aa(&ctx, COLOR_START_POINT, COLOR_END_POINT, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 10);
|
||||
graphics_context_set_stroke_color(&ctx, GColorClear);
|
||||
graphics_line_draw_precise_stroked_non_aa(&ctx, COLOR_START_POINT, COLOR_END_POINT, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Following points come from bug reports, causing "plasma effect" where multiple
|
||||
* lines in close vicinity of one spot (~1 pixel) caused artifact instead of
|
||||
* elegant AA circle.
|
||||
*/
|
||||
|
||||
// First pair, distance less than 1px
|
||||
#define CLOSE_POINTS_LESS_THAN_1PX_START (GPointPrecise){{.integer = 71, .fraction = 4}, {.integer = 73, .fraction = 5}}
|
||||
#define CLOSE_POINTS_LESS_THAN_1PX_END (GPointPrecise){{.integer = 71, .fraction = 5}, {.integer = 73, .fraction = 6}}
|
||||
//Second pair, distance around 1px
|
||||
#define CLOSE_POINTS_AROUND_1PX_START (GPointPrecise){{.integer = 71, .fraction = 4}, {.integer = 74, .fraction = 1}}
|
||||
#define CLOSE_POINTS_AROUND_1PX_END (GPointPrecise){{.integer = 71, .fraction = 1}, {.integer = 73, .fraction = 3}}
|
||||
|
||||
void test_graphics_draw_stroke_precise_${BIT_DEPTH_NAME}__close_points_aa(void) {
|
||||
#if PBL_COLOR
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 10);
|
||||
graphics_line_draw_precise_stroked_aa(&ctx, CLOSE_POINTS_LESS_THAN_1PX_START, CLOSE_POINTS_LESS_THAN_1PX_END, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_close_points_less_than_1px_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 10);
|
||||
graphics_line_draw_precise_stroked_aa(&ctx, CLOSE_POINTS_AROUND_1PX_START, CLOSE_POINTS_AROUND_1PX_END, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_close_points_around_1px_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_graphics_draw_stroke_precise_${BIT_DEPTH_NAME}__close_points_non_aa(void) {
|
||||
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 10);
|
||||
graphics_line_draw_precise_stroked_non_aa(&ctx, CLOSE_POINTS_LESS_THAN_1PX_START, CLOSE_POINTS_LESS_THAN_1PX_END, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_close_points_less_than_1px_non_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 10);
|
||||
graphics_line_draw_precise_stroked_non_aa(&ctx, CLOSE_POINTS_AROUND_1PX_START, CLOSE_POINTS_AROUND_1PX_END, 10);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_close_points_around_1px_non_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Following functions will test issue of same starting/ending point for stroke width, where point lies
|
||||
* between pixels due to precise points. This should be fixed by PBL-20783.
|
||||
*/
|
||||
void test_graphics_draw_stroke_precise_${BIT_DEPTH_NAME}__same_point_aa(void) {
|
||||
#if PBL_COLOR
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 10);
|
||||
int radius = 5;
|
||||
int x_offset = 10 * FIXED_S16_3_ONE.raw_value;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
radius += 1;
|
||||
x_offset += radius * FIXED_S16_3_ONE.raw_value + 4 * FIXED_S16_3_ONE.raw_value;
|
||||
GPointPrecise p = GPointPrecise(x_offset, 15 * FIXED_S16_3_ONE.raw_value);
|
||||
for (int j=0; j<9; j++) {
|
||||
graphics_line_draw_precise_stroked_aa(&ctx, p, p, radius);
|
||||
p.x.raw_value += 1;
|
||||
p.y.integer += 16;
|
||||
}
|
||||
}
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_same_points_pattern_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_graphics_draw_stroke_precise_${BIT_DEPTH_NAME}__same_point_non_aa(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 10);
|
||||
int radius = 5;
|
||||
int x_offset = 10 * FIXED_S16_3_ONE.raw_value;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
radius += 1;
|
||||
x_offset += radius * FIXED_S16_3_ONE.raw_value + 4 * FIXED_S16_3_ONE.raw_value;
|
||||
GPointPrecise p = GPointPrecise(x_offset, 15 * FIXED_S16_3_ONE.raw_value);
|
||||
for (int j=0; j<9; j++) {
|
||||
graphics_line_draw_precise_stroked_non_aa(&ctx, p, p, radius);
|
||||
p.x.raw_value += 1;
|
||||
p.y.integer += 16;
|
||||
}
|
||||
}
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_stroke_precise_same_points_pattern_non_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
@@ -0,0 +1,567 @@
|
||||
/*
|
||||
* 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 "fixtures/load_test_resources.h"
|
||||
|
||||
#include "applib/fonts/fonts_private.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/text.h"
|
||||
#include "applib/graphics/text_resources.h"
|
||||
#include "applib/ui/layer.h"
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "resource/resource_ids.auto.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "${BIT_DEPTH_NAME}/test_framebuffer.h"
|
||||
#include "util.h"
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Stubs
|
||||
#include "stubs_analytics.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "stubs_bootbits.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_memory_layout.h"
|
||||
#include "stubs_mutex.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_pebble_tasks.h"
|
||||
#include "stubs_print.h"
|
||||
#include "stubs_prompt.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_syscall_internal.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_system_reset.h"
|
||||
#include "stubs_task_watchdog.h"
|
||||
#include "stubs_ui_window.h"
|
||||
#include "stubs_unobstructed_area.h"
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Fakes
|
||||
#include "fake_gbitmap_get_data_row.h"
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_graphics_draw_text_${BIT_DEPTH_NAME}__initialize(void) {
|
||||
s_fake_data_row_handling = false;
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_graphics_draw_text_${BIT_DEPTH_NAME}__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Tests
|
||||
|
||||
static FontInfo s_font_info;
|
||||
static const char *s_text_buffer = "Text Clipping";
|
||||
|
||||
static void prv_setup_resources(void) {
|
||||
// Setup resources
|
||||
fake_spi_flash_init(0, 0x1000000);
|
||||
pfs_init(false);
|
||||
pfs_format(true /* write erase headers */);
|
||||
load_resource_fixture_in_flash(RESOURCES_FIXTURE_PATH, SYSTEM_RESOURCES_FIXTURE_NAME, false /* is_next */);
|
||||
|
||||
resource_init();
|
||||
}
|
||||
|
||||
// Corner Tests
|
||||
void draw_text_single_line_ellipsis_clip_across_nx_zero_y_offset(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(-44, 0, 72, 32),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_ellipsis_clip_across_ny_descender(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(0, -25, 100, 32),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
// Ellipsis Tests
|
||||
void draw_text_single_line_ellipsis_clip_across_ny(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(4, -18, 72, 32),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_ellipsis_clip_across_y(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(4, 20, 72, 32),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_ellipsis_clip_across_nx(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(-44, 4, 72, 32),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_ellipsis_clip_across_x(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(34, 4, 72, 32),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_ellipsis_clip_outside_ny(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(4, -40, 72, 32),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_ellipsis_clip_outside_y(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(4, 40, 72, 32),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_ellipsis_clip_outside_nx(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(-80, 4, 72, 32),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_ellipsis_clip_outside_x(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(80, 4, 72, 32),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
// Word Wrap Tests
|
||||
void draw_text_single_line_wordwrap_clip_across_ny(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(4, -18, 72, 32),
|
||||
GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_wordwrap_clip_across_ny_second_line(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(4, -46, 72, 50),
|
||||
GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_wordwrap_clip_across_y(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(4, 20, 72, 32),
|
||||
GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_wordwrap_clip_across_y_second_line(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(4, -10, 72, 50),
|
||||
GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_wordwrap_clip_across_nx(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(-44, 4, 72, 32),
|
||||
GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_wordwrap_clip_across_x(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(34, 4, 72, 32),
|
||||
GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_wordwrap_clip_outside_ny(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(4, -40, 72, 32),
|
||||
GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_wordwrap_clip_outside_y(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(4, 40, 72, 32),
|
||||
GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_wordwrap_clip_outside_nx(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(-80, 4, 72, 32),
|
||||
GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void draw_text_single_line_wordwrap_clip_outside_x(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_text_color(ctx, GColorBlack);
|
||||
graphics_draw_text(ctx, s_text_buffer, &s_font_info, GRect(80, 4, 72, 32),
|
||||
GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
void canvas_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
graphics_draw_rect(ctx, &GRect(39, 39, 82, 42));
|
||||
}
|
||||
|
||||
void test_graphics_draw_text_${BIT_DEPTH_NAME}__clipping(void) {
|
||||
GContext ctx;
|
||||
Layer canvas;
|
||||
Layer layer;
|
||||
|
||||
prv_setup_resources();
|
||||
|
||||
uint32_t gothic_24_handle = RESOURCE_ID_GOTHIC_24_BOLD;
|
||||
cl_assert(text_resources_init_font(0, gothic_24_handle, 0, &s_font_info));
|
||||
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
layer_init(&canvas, &GRect(0, 0, 144, 168));
|
||||
layer_set_update_proc(&canvas, &canvas_layer_update_callback);
|
||||
|
||||
layer_init(&layer, &GRect(40, 40, 80, 40));
|
||||
layer_add_child(&canvas, &layer);
|
||||
|
||||
// Corner cases
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_ellipsis_clip_across_nx_zero_y_offset);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_ellipsis_clip_across_nx_zero_y_offset.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_ellipsis_clip_across_ny_descender);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_ellipsis_clip_across_ny_descender.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Ellipsis tests
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_ellipsis_clip_across_ny);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_ellipsis_clip_across_ny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_ellipsis_clip_across_y);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_ellipsis_clip_across_y.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_ellipsis_clip_across_nx);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_ellipsis_clip_across_nx.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_ellipsis_clip_across_x);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_ellipsis_clip_across_x.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_ellipsis_clip_outside_ny);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_ellipsis_clip_outside_ny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_ellipsis_clip_outside_y);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_ellipsis_clip_outside_y.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_ellipsis_clip_outside_nx);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_ellipsis_clip_outside_nx.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_ellipsis_clip_outside_x);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_ellipsis_clip_outside_x.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Word Wrap tests
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_wordwrap_clip_across_ny);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_wordwrap_clip_across_ny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_wordwrap_clip_across_ny_second_line);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_wordwrap_clip_across_ny_second_line.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_wordwrap_clip_across_y);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_wordwrap_clip_across_y.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_wordwrap_clip_across_y_second_line);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_wordwrap_clip_across_y_second_line.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_wordwrap_clip_across_nx);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_wordwrap_clip_across_nx.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_wordwrap_clip_across_x);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_wordwrap_clip_across_x.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_wordwrap_clip_outside_ny);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_wordwrap_clip_outside_ny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_wordwrap_clip_outside_y);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_wordwrap_clip_outside_y.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_wordwrap_clip_outside_nx);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_wordwrap_clip_outside_nx.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &draw_text_single_line_wordwrap_clip_outside_x);
|
||||
layer_render_tree(&canvas, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_single_line_wordwrap_clip_outside_x.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
#define RECT_TEXT_0_0 GRect(0, 0, 140, 1000)
|
||||
#define RECT_TEXT_2_0 GRect(2, 0, 140, 1000)
|
||||
|
||||
void test_graphics_draw_text_${BIT_DEPTH_NAME}__clipping_letters(void) {
|
||||
GContext ctx;
|
||||
|
||||
prv_setup_resources();
|
||||
|
||||
uint32_t gothic_18_handle = RESOURCE_ID_GOTHIC_18_BOLD;
|
||||
cl_assert(text_resources_init_font(0, gothic_18_handle, 0, &s_font_info));
|
||||
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Test when clipping/drawing and text bounds are all at (0, 0)
|
||||
setup_test_aa_sw(&ctx, fb, RECT_TEXT_0_0, RECT_TEXT_0_0, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_TEXT_0_0,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_jja00.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Test when clipping/drawing are at (2, 0) and text bounds is at (0, 0)
|
||||
setup_test_aa_sw(&ctx, fb, RECT_TEXT_2_0, RECT_TEXT_2_0, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_TEXT_0_0,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_jja20.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Test when clipping/drawing and text bounds are all at (2, 0)
|
||||
setup_test_aa_sw(&ctx, fb, RECT_TEXT_2_0, RECT_TEXT_2_0, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_TEXT_2_0,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_jja22.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Test when clipping/drawing and text bounds are all at (2, 0)
|
||||
setup_test_aa_sw(&ctx, fb, RECT_TEXT_2_0, RECT_TEXT_2_0, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "ajj", &s_font_info, RECT_TEXT_2_0,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_ajj22.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Test when clipping/drawing and text bounds are all at (2, 0) - no negative offset
|
||||
setup_test_aa_sw(&ctx, fb, RECT_TEXT_2_0, RECT_TEXT_2_0, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "aaa", &s_font_info, RECT_TEXT_2_0,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_aaa22.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
#define RECT_NULL GRect(0, 0, 0, 0)
|
||||
#define RECT_NULL_W GRect(0, 0, 0, 20)
|
||||
#define RECT_NULL_H GRect(0, 0, 20, 0)
|
||||
#define RECT_NEG GRect(0, 0, -20, -20)
|
||||
#define RECT_NEG_W GRect(0, 0, -20, 0)
|
||||
#define RECT_NEG_H GRect(0, 0, 0, -20)
|
||||
void test_graphics_draw_text_${BIT_DEPTH_NAME}__zero(void) {
|
||||
GContext ctx;
|
||||
|
||||
prv_setup_resources();
|
||||
|
||||
uint32_t gothic_18_handle = RESOURCE_ID_GOTHIC_18_BOLD;
|
||||
cl_assert(text_resources_init_font(0, gothic_18_handle, 0, &s_font_info));
|
||||
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Test zero text bounds size - ensure nothing is rendered
|
||||
setup_test_aa_sw(&ctx, fb, RECT_TEXT_0_0, RECT_TEXT_0_0, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_NULL,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(framebuffer_is_empty("draw_text_null", fb, GColorWhite));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, RECT_TEXT_0_0, RECT_TEXT_0_0, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_NULL_W,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(framebuffer_is_empty("draw_text_null_w", fb, GColorWhite));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, RECT_TEXT_0_0, RECT_TEXT_0_0, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_NULL_H,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(framebuffer_is_empty("draw_text_null_h", fb, GColorWhite));
|
||||
|
||||
// Test negative text bounds size - ensure nothing is rendered
|
||||
setup_test_aa_sw(&ctx, fb, RECT_TEXT_0_0, RECT_TEXT_0_0, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_NEG,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(framebuffer_is_empty("draw_text_neg", fb, GColorWhite));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, RECT_TEXT_0_0, RECT_TEXT_0_0, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_NEG_W,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(framebuffer_is_empty("draw_text_neg_w", fb, GColorWhite));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, RECT_TEXT_0_0, RECT_TEXT_0_0, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_NEG_H,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(framebuffer_is_empty("draw_text_neg_h", fb, GColorWhite));
|
||||
|
||||
// Test null context boxes - ensure nothing is rendered
|
||||
setup_test_aa_sw(&ctx, fb, RECT_NULL, RECT_NULL, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_TEXT_0_0,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(framebuffer_is_empty("draw_text_null_null", fb, GColorWhite));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, RECT_NULL_W, RECT_NULL_W, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_TEXT_0_0,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(framebuffer_is_empty("draw_text_null_null", fb, GColorWhite));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, RECT_NULL_H, RECT_NULL_H, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_TEXT_0_0,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(framebuffer_is_empty("draw_text_null_null", fb, GColorWhite));
|
||||
|
||||
// Test negative context boxes - ensure nothing is rendered
|
||||
setup_test_aa_sw(&ctx, fb, RECT_NEG, RECT_NEG, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_TEXT_0_0,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(framebuffer_is_empty("draw_text_null_null", fb, GColorWhite));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, RECT_NEG, RECT_NEG, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_NEG,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(framebuffer_is_empty("draw_text_null_null", fb, GColorWhite));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, RECT_NEG, RECT_NEG, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_draw_text(&ctx, "jja", &s_font_info, RECT_NULL,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(framebuffer_is_empty("draw_text_null_null", fb, GColorWhite));
|
||||
}
|
||||
|
||||
void test_graphics_draw_text_8bit__color(void) {
|
||||
GContext ctx;
|
||||
|
||||
prv_setup_resources();
|
||||
|
||||
uint32_t gothic_18_handle = RESOURCE_ID_GOTHIC_18_BOLD;
|
||||
cl_assert(text_resources_init_font(0, gothic_18_handle, 0, &s_font_info));
|
||||
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, GRect(0, 0, 144, 168), GRect(0, 0, 144, 168), false, 1);
|
||||
graphics_context_set_fill_color(&ctx, GColorRed);
|
||||
graphics_fill_rect(&ctx, &GRect(0, 0, 144, 168));
|
||||
graphics_context_set_text_color(&ctx, GColorBlue);
|
||||
graphics_draw_text(&ctx, "blue100", &s_font_info, GRect(10, 10, 100, 30),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
ctx.draw_state.text_color.a = 2;
|
||||
graphics_draw_text(&ctx, "blue66", &s_font_info, GRect(10, 40, 100, 30),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
ctx.draw_state.text_color.a = 1;
|
||||
graphics_draw_text(&ctx, "blue33", &s_font_info, GRect(10, 70, 100, 30),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
ctx.draw_state.text_color.a = 0;
|
||||
graphics_draw_text(&ctx, "blue0", &s_font_info, GRect(10, 100, 100, 30),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_color_assign.8bit.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, GRect(0, 0, 144, 168), GRect(0, 0, 144, 168), false, 1);
|
||||
graphics_context_set_fill_color(&ctx, GColorRed);
|
||||
graphics_fill_rect(&ctx, &GRect(0, 0, 144, 168));
|
||||
graphics_context_set_compositing_mode(&ctx, GCompOpSet);
|
||||
graphics_context_set_text_color(&ctx, GColorBlue);
|
||||
graphics_draw_text(&ctx, "blue100", &s_font_info, GRect(10, 10, 100, 30),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
ctx.draw_state.text_color.a = 2;
|
||||
graphics_draw_text(&ctx, "blue66", &s_font_info, GRect(10, 40, 100, 30),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
ctx.draw_state.text_color.a = 1;
|
||||
graphics_draw_text(&ctx, "blue33", &s_font_info, GRect(10, 70, 100, 30),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
ctx.draw_state.text_color.a = 0;
|
||||
graphics_draw_text(&ctx, "blue0", &s_font_info, GRect(10, 100, 100, 30),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_color_set.8bit.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_text_8bit__data_row_offsets(void) {
|
||||
GContext ctx;
|
||||
|
||||
// Setup resources
|
||||
fake_spi_flash_init(0, 0x1000000);
|
||||
pfs_init(false);
|
||||
pfs_format(true /* write erase headers */);
|
||||
load_resource_fixture_in_flash(RESOURCES_FIXTURE_PATH, SYSTEM_RESOURCES_FIXTURE_NAME, false /* is_next */);
|
||||
|
||||
resource_init();
|
||||
|
||||
memset(&s_font_info, 0, sizeof(s_font_info));
|
||||
|
||||
uint32_t gothic_18_handle = RESOURCE_ID_GOTHIC_18_BOLD;
|
||||
cl_assert(text_resources_init_font(0, gothic_18_handle, 0, &s_font_info));
|
||||
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Enable fake data row handling which will override the gbitmap_get_data_row_xxx() functions
|
||||
// with their fake counterparts in fake_gbitmap_get_data_row.c
|
||||
s_fake_data_row_handling = true;
|
||||
|
||||
// The following test uses fake bitmap data row handling to clip the text rendering of a repeated
|
||||
// string of alphabet characters to a diamond mask which is flipped vertically
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, RECT_TEXT_0_0, RECT_TEXT_0_0, false, 1);
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
|
||||
graphics_draw_text(&ctx, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR"
|
||||
"STUVWXYZabcdefghijklmnopqrstuvwxyz", &s_font_info, RECT_TEXT_0_0,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "draw_text_data_row_offsets.8bit.pbi"));
|
||||
}
|
||||
@@ -0,0 +1,484 @@
|
||||
/*
|
||||
* 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 "fixtures/load_test_resources.h"
|
||||
|
||||
#include "applib/fonts/fonts_private.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/text.h"
|
||||
#include "applib/graphics/text_resources.h"
|
||||
#include "applib/ui/layer.h"
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "resource/resource_ids.auto.h"
|
||||
#include "util/size.h"
|
||||
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "8bit/test_framebuffer.h"
|
||||
#include "util.h"
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Stubs
|
||||
#include "stubs_analytics.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "stubs_bootbits.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_memory_layout.h"
|
||||
#include "stubs_mutex.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_pebble_tasks.h"
|
||||
#include "stubs_print.h"
|
||||
#include "stubs_prompt.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_syscall_internal.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_system_reset.h"
|
||||
#include "stubs_task_watchdog.h"
|
||||
#include "stubs_ui_window.h"
|
||||
#include "stubs_unobstructed_area.h"
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Tests
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
static GContext ctx;
|
||||
|
||||
#define NUM_STEPS (5)
|
||||
#define DELTA 20
|
||||
static FontInfo s_font_info;
|
||||
static GBitmap *s_dest_bitmap;
|
||||
static char *s_text = "A B C D E F G "
|
||||
"H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o "
|
||||
"p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R"
|
||||
"S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z";
|
||||
|
||||
void prv_prepare_fb_steps_xy(GSize size, int16_t steps_x, int16_t steps_y) {
|
||||
gbitmap_destroy(s_dest_bitmap);
|
||||
s_dest_bitmap = gbitmap_create_blank(GSize(size.w * steps_x, size.h * steps_y),
|
||||
GBITMAP_NATIVE_FORMAT);
|
||||
ctx.dest_bitmap = *s_dest_bitmap;
|
||||
ctx.draw_state.clip_box = (GRect){.size = size};
|
||||
ctx.draw_state.drawing_box = ctx.draw_state.clip_box;
|
||||
graphics_context_set_text_color(&ctx, GColorBlack);
|
||||
graphics_context_set_fill_color(&ctx, GColorLightGray);
|
||||
memset(s_dest_bitmap->addr, 0xff, s_dest_bitmap->row_size_bytes * s_dest_bitmap->bounds.size.h);
|
||||
}
|
||||
|
||||
void prv_prepare_fb_steps(GSize size) {
|
||||
prv_prepare_fb_steps_xy(size, NUM_STEPS, NUM_STEPS);
|
||||
}
|
||||
|
||||
void test_graphics_draw_text_flow__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
ctx = (GContext){};
|
||||
|
||||
// Setup resources
|
||||
fake_spi_flash_init(0, 0x1000000);
|
||||
pfs_init(false);
|
||||
pfs_format(true /* write erase headers */);
|
||||
load_resource_fixture_in_flash(RESOURCES_FIXTURE_PATH, SYSTEM_RESOURCES_FIXTURE_NAME, false /* is_next */);
|
||||
|
||||
resource_init();
|
||||
|
||||
memset(&s_font_info, 0, sizeof(s_font_info));
|
||||
|
||||
cl_assert(text_resources_init_font(0, RESOURCE_ID_GOTHIC_18_BOLD, 0, &s_font_info));
|
||||
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ctx.dest_bitmap.bounds, ctx.dest_bitmap.bounds, false, 1);
|
||||
|
||||
prv_prepare_fb_steps(GSize(DISP_COLS, DISP_ROWS));
|
||||
}
|
||||
|
||||
void test_graphics_draw_text_flow__cleanup(void) {
|
||||
free(fb);
|
||||
gbitmap_destroy(s_dest_bitmap);
|
||||
s_dest_bitmap = NULL;
|
||||
}
|
||||
|
||||
|
||||
#define RECT_TEXT_0_0 GRect(0, 0, DISP_COLS, DISP_ROWS)
|
||||
GRangeHorizontal perimeter_for_display_round(const GPerimeter *perimeter,
|
||||
const GSize *ctx_size,
|
||||
GRangeVertical vertical_range,
|
||||
uint16_t inset);
|
||||
|
||||
static uint8_t *prv_bitmap_offset_for_steps(GBitmap *bmp, int sx, int sy,
|
||||
int steps_x, int steps_y) {
|
||||
sx += (steps_x - 1) / 2;
|
||||
sy += (steps_y - 1) / 2;
|
||||
|
||||
int16_t step_w = bmp->bounds.size.w / steps_x;
|
||||
int16_t step_h = bmp->bounds.size.h / steps_y;
|
||||
|
||||
return ((uint8_t *)bmp->addr) + (sy * step_h * bmp->row_size_bytes) + (sx * step_w);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
RenderMoveTextBox,
|
||||
RenderMoveDrawBox,
|
||||
} RenderMoveMode;
|
||||
|
||||
void render_steps(TextLayoutExtended *layout, RenderMoveMode mode, int delta, int16_t height,
|
||||
char **texts) {
|
||||
const GTextLayoutCacheRef layout_cache = (GTextLayoutCacheRef const)layout;
|
||||
|
||||
int steps_x = ctx.dest_bitmap.bounds.size.w / ctx.draw_state.clip_box.size.w;
|
||||
int steps_y = ctx.dest_bitmap.bounds.size.h / ctx.draw_state.clip_box.size.h;
|
||||
|
||||
int text_idx = 0;
|
||||
for (int sx = -((steps_x - 1) / 2) ; sx <= steps_x / 2; sx++) {
|
||||
for (int sy = -((steps_y - 1) / 2); sy <= steps_y / 2; sy++) {
|
||||
// as draw_text internally uses absolute coordinates to derive its state we cannot
|
||||
// simply adjust the draw_box to accomplish a side-by-side comparison
|
||||
ctx.dest_bitmap.addr = prv_bitmap_offset_for_steps(s_dest_bitmap, sx, sy, steps_x, steps_y);
|
||||
|
||||
GRect box = {.size = GSize(DISP_COLS, height)};
|
||||
const GPoint origin = GPoint(delta * sx, delta * sy);
|
||||
if (mode == RenderMoveTextBox) {
|
||||
box.origin = origin;
|
||||
ctx.draw_state.drawing_box.origin = GPointZero;
|
||||
} else {
|
||||
ctx.draw_state.drawing_box.origin = origin;
|
||||
}
|
||||
|
||||
graphics_fill_rect(&ctx, &box);
|
||||
graphics_draw_rect(&ctx, &(GRect){.origin = GPoint(-ctx.draw_state.drawing_box.origin.x,
|
||||
-ctx.draw_state.drawing_box.origin.y),
|
||||
.size = ctx.draw_state.clip_box.size});
|
||||
|
||||
char *text = texts ? texts[text_idx++] : s_text;
|
||||
graphics_draw_text(&ctx, text, &s_font_info, box,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, layout_cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_graphics_draw_text_flow__flow_no_paging(void) {
|
||||
TextLayoutExtended layout = {
|
||||
.flow_data = {
|
||||
.perimeter.impl = &(GPerimeter){.callback=perimeter_for_display_round},
|
||||
.perimeter.inset = 8,
|
||||
},
|
||||
};
|
||||
render_steps(&layout, RenderMoveTextBox, DELTA, DISP_ROWS, NULL);
|
||||
cl_check(gbitmap_pbi_eq(s_dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_text_flow__flow_no_paging_draw_box(void) {
|
||||
TextLayoutExtended layout = {
|
||||
.flow_data = {
|
||||
.perimeter.impl = &(GPerimeter){.callback=perimeter_for_display_round},
|
||||
.perimeter.inset = 8,
|
||||
},
|
||||
};
|
||||
render_steps(&layout, RenderMoveDrawBox, DELTA, DISP_ROWS, NULL);
|
||||
// should result in the very same output as if you did a placement via text box
|
||||
cl_check(gbitmap_pbi_eq(s_dest_bitmap, "test_graphics_draw_text_flow__flow_no_paging.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_draw_text_flow__with_origin_zero(void) {
|
||||
TextLayoutExtended layout = {
|
||||
.flow_data = {
|
||||
.perimeter.impl = &(GPerimeter){.callback=perimeter_for_display_round},
|
||||
.perimeter.inset = 8,
|
||||
.paging.page_on_screen.size_h = DISP_ROWS, // setting a page height != enables positioning
|
||||
.paging.origin_on_screen = {0, 0},
|
||||
},
|
||||
};
|
||||
render_steps(&layout, RenderMoveTextBox, DELTA, DISP_ROWS, NULL);
|
||||
cl_check(gbitmap_pbi_eq(s_dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_text_flow__with_origin_non_zero(void) {
|
||||
TextLayoutExtended layout = {
|
||||
.flow_data = {
|
||||
.perimeter.impl = &(GPerimeter){.callback=perimeter_for_display_round},
|
||||
.perimeter.inset = 8,
|
||||
.paging.page_on_screen.size_h = DISP_ROWS, // setting a page height != enables positioning
|
||||
.paging.origin_on_screen = {DELTA, 2 * DELTA},
|
||||
},
|
||||
};
|
||||
render_steps(&layout, RenderMoveTextBox, DELTA, DISP_ROWS, NULL);
|
||||
cl_check(gbitmap_pbi_eq(s_dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_text_flow__with_paging(void) {
|
||||
prv_prepare_fb_steps(GSize(DISP_COLS, 2 * DISP_ROWS));
|
||||
TextLayoutExtended layout = {
|
||||
.flow_data = {
|
||||
.perimeter.impl = &(GPerimeter){.callback=perimeter_for_display_round},
|
||||
.perimeter.inset = 8,
|
||||
.paging.page_on_screen = {
|
||||
.origin_y = 25,
|
||||
.size_h = 100
|
||||
}, // setting a page height != enables positioning
|
||||
},
|
||||
};
|
||||
render_steps(&layout, RenderMoveTextBox, DELTA, 1000, NULL);
|
||||
cl_check(gbitmap_pbi_eq(s_dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_text_flow__avoid_repeat_text_to_avoid_orphans(void) {
|
||||
TextLayoutExtended layout = {
|
||||
.flow_data = {
|
||||
.perimeter.impl = &(GPerimeter){.callback=perimeter_for_display_round},
|
||||
.perimeter.inset = 8,
|
||||
.paging.page_on_screen = {
|
||||
.origin_y = 25,
|
||||
.size_h = 100
|
||||
}, // setting a page height != enables positioning
|
||||
},
|
||||
};
|
||||
|
||||
char first_page_one_line[] = "A B C D E F G H I";
|
||||
char second_page_one_line[] = "A B C D E F G H I J K L M N";
|
||||
char second_page_two_lines[] = "A B C D E F G H I J K L M N O P Q R S T U V";
|
||||
char second_page_full[] = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z "
|
||||
"a b c d e f g h j k l m n o p q r s t u v w x y z "
|
||||
"A";
|
||||
char third_page_one_line[] = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z "
|
||||
"a b c d e f g h j k l m n o p q r s t u v w x y z "
|
||||
"A B C D E F G";
|
||||
char third_page_two_lines[] = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z "
|
||||
"a b c d e f g h j k l m n o p q r s t u v w x y z "
|
||||
"A B C D E F G I J K L M N O P";
|
||||
char third_page_full[] = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z "
|
||||
"a b c d e f g h j k l m n o p q r s t u v w x y z "
|
||||
"A B C D E F G H I J K L M N O P Q R S T U V W X Y Z "
|
||||
"a b c d e f g h j k l m n o p q r s t u";
|
||||
char fourth_page_one_line[] = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z "
|
||||
"a b c d e f g h j k l m n o p q r s t u v w x y z "
|
||||
"A B C D E F G H I J K L M N O P Q R S T U V W X Y Z "
|
||||
"a b c d e f g h j k l m n o p q r s t u v w x y z";
|
||||
char *texts[] = {
|
||||
first_page_one_line,
|
||||
second_page_one_line, second_page_two_lines, second_page_full,
|
||||
third_page_one_line, third_page_two_lines, third_page_full, fourth_page_one_line,
|
||||
};
|
||||
|
||||
const int16_t num_steps = ARRAY_LENGTH(texts);
|
||||
const GSize size = GSize(144, 300);
|
||||
|
||||
prv_prepare_fb_steps_xy(size, num_steps, 1);
|
||||
ctx.draw_state.avoid_text_orphans = true;
|
||||
|
||||
render_steps(&layout, RenderMoveDrawBox, 0, size.h, texts);
|
||||
|
||||
// draw markers to visualize page breaks
|
||||
ctx.draw_state.clip_box = s_dest_bitmap->bounds;
|
||||
ctx.draw_state.drawing_box = s_dest_bitmap->bounds;
|
||||
ctx.dest_bitmap.addr = s_dest_bitmap->addr;
|
||||
graphics_context_set_stroke_color(&ctx, GColorDarkGray);
|
||||
int16_t y = layout.flow_data.paging.page_on_screen.origin_y;
|
||||
while (y < size.h) {
|
||||
graphics_draw_line(&ctx, GPoint(0, y), GPoint(s_dest_bitmap->bounds.size.w, y));
|
||||
y += layout.flow_data.paging.page_on_screen.size_h;
|
||||
}
|
||||
|
||||
cl_check(gbitmap_pbi_eq(s_dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
GRangeHorizontal perimeter_for_circle(GRangeVertical vertical_range, GPoint center, int32_t radius);
|
||||
GRangeHorizontal perimeter_for_display_rect(const GPerimeter *perimeter,
|
||||
const GSize *ctx_size,
|
||||
GRangeVertical vertical_range,
|
||||
uint16_t inset);
|
||||
|
||||
// easiest way to make these dimensions identical to spalding although the tests take
|
||||
// defaults from basalt's screen resolution. The original `perimeter_for_display_round`
|
||||
// uses the platform-specific `DISP_FRAME`
|
||||
static GRangeHorizontal prv_perimeter_for_display_round(const GPerimeter *perimeter,
|
||||
const GSize *ctx_size,
|
||||
GRangeVertical vertical_range,
|
||||
uint16_t inset) {
|
||||
const GRect disp_180_frame = GRect(0, 0, 180, 180);
|
||||
const GPoint center = grect_center_point(&disp_180_frame);
|
||||
const int32_t radius = grect_shortest_side(disp_180_frame) / 2 - inset;
|
||||
return perimeter_for_circle(vertical_range, center, radius);
|
||||
}
|
||||
|
||||
|
||||
void test_graphics_draw_text_flow__draw_text_doom(void) {
|
||||
// text and configuration we see in text_flow demo app
|
||||
cl_assert(text_resources_init_font(0, RESOURCE_ID_GOTHIC_24_BOLD, 0, &s_font_info));
|
||||
TextLayoutExtended layout = {
|
||||
.flow_data = {
|
||||
.perimeter.impl = &(GPerimeter){.callback = prv_perimeter_for_display_round},
|
||||
.perimeter.inset = 8,
|
||||
.paging.page_on_screen = {
|
||||
.origin_y = 48,
|
||||
.size_h = 85
|
||||
},
|
||||
.paging.origin_on_screen.y = 412,
|
||||
},
|
||||
};
|
||||
char text[] = "Dib: You're just jealous...\nZim: This has nothing to do with jelly!\n"
|
||||
"Zim: You dare agree with me? Prepare to meet your horrible doom!";
|
||||
|
||||
prv_prepare_fb_steps_xy(GSize(180, 300), 1, 1);
|
||||
ctx.draw_state.avoid_text_orphans = true;
|
||||
graphics_draw_text(&ctx, text, &s_font_info, GRect(0, 0, 180, 1000),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter,
|
||||
(GTextAttributes *const) &layout);
|
||||
cl_check(gbitmap_pbi_eq(s_dest_bitmap, TEST_PBI_FILE));
|
||||
|
||||
prv_prepare_fb_steps_xy(GSize(180, 300), 1, 1);
|
||||
ctx.draw_state.avoid_text_orphans = true;
|
||||
ctx.draw_state.clip_box.origin.y = 48;
|
||||
ctx.draw_state.clip_box.size.h = 85;
|
||||
ctx.draw_state.drawing_box.origin.y = -183;
|
||||
graphics_draw_text(&ctx, text, &s_font_info, GRect(0, 0, 180, 1000),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter,
|
||||
(GTextAttributes *const) &layout);
|
||||
cl_check(gbitmap_pbi_eq(s_dest_bitmap, namecat(namecat(__func__, "__clipped"), ".pbi")));
|
||||
|
||||
prv_prepare_fb_steps_xy(GSize(180, 300), 1, 1);
|
||||
ctx.draw_state.avoid_text_orphans = false;
|
||||
graphics_draw_text(&ctx, text, &s_font_info, GRect(0, 0, 180, 1000),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter,
|
||||
(GTextAttributes *const) &layout);
|
||||
cl_check(gbitmap_pbi_eq(s_dest_bitmap, namecat(namecat(__func__, "__with_orphan"), ".pbi")));
|
||||
}
|
||||
|
||||
void test_graphics_draw_text_flow__max_used_size_draw_text_doom(void) {
|
||||
cl_assert(text_resources_init_font(0, RESOURCE_ID_GOTHIC_24_BOLD, 0, &s_font_info));
|
||||
|
||||
TextLayoutExtended layout = {
|
||||
.flow_data = {
|
||||
.perimeter.impl = &(GPerimeter){.callback = prv_perimeter_for_display_round},
|
||||
.perimeter.inset = 8,
|
||||
.paging.page_on_screen = {
|
||||
.origin_y = 48,
|
||||
.size_h = 85
|
||||
},
|
||||
.paging.origin_on_screen.y = 412,
|
||||
},
|
||||
};
|
||||
void *layout_ref = (void *)&layout;
|
||||
|
||||
char text[] = "Dib: You're just jealous...\nZim: This has nothing to do with jelly!\n"
|
||||
"Zim: You dare agree with me? Prepare to meet your horrible doom!";
|
||||
|
||||
const GFont font = &s_font_info;
|
||||
const GRect box = GRect(0, 0, 180, 1000);
|
||||
const GTextOverflowMode overflow_mode = GTextOverflowModeTrailingEllipsis;
|
||||
const GTextAlignment text_alignment = GTextAlignmentCenter;
|
||||
const GSize fb_size = GSize(180, 300);
|
||||
const int16_t steps_x = 1;
|
||||
const int16_t steps_y = 1;
|
||||
|
||||
prv_prepare_fb_steps_xy(fb_size, steps_x, steps_y);
|
||||
ctx.draw_state.avoid_text_orphans = true;
|
||||
|
||||
const GSize size_with_orphan_avoidance = graphics_text_layout_get_max_used_size(&ctx,
|
||||
text, font, box,
|
||||
overflow_mode,
|
||||
text_alignment,
|
||||
layout_ref);
|
||||
|
||||
// TODO: PBL-34191 move .avoid_text_orphans from GContext to TextLayout so layout is invalidated
|
||||
// Invalidate the layout so it will be recalculated for the next step
|
||||
layout.hash = 0;
|
||||
|
||||
prv_prepare_fb_steps_xy(fb_size, steps_x, steps_y);
|
||||
ctx.draw_state.avoid_text_orphans = false;
|
||||
|
||||
const GSize size_without_orphan_avoidance = graphics_text_layout_get_max_used_size(&ctx,
|
||||
text, font,
|
||||
box,
|
||||
overflow_mode,
|
||||
text_alignment,
|
||||
layout_ref);
|
||||
|
||||
// We should get different heights because the orphan avoidance algorithm adds an extra line
|
||||
cl_assert_equal_i(size_with_orphan_avoidance.h, 279);
|
||||
cl_assert_equal_i(size_without_orphan_avoidance.h, 255);
|
||||
}
|
||||
|
||||
void test_graphics_draw_text_flow__no_infinite_loop(void) {
|
||||
TextLayoutExtended layout = {
|
||||
.flow_data = {
|
||||
.perimeter.impl = &(GPerimeter){.callback = perimeter_for_display_rect},
|
||||
},
|
||||
};
|
||||
char text[] = "Prevent orhpans for tall-enough pages.";
|
||||
const int16_t line_height = 22;
|
||||
// some more pixels to show that orphan prevention really only applies if there's enough space
|
||||
// for enough *full* lines
|
||||
const int16_t some = 5;
|
||||
prv_prepare_fb_steps_xy(GSize(180, 300), 3, 1);
|
||||
ctx.draw_state.avoid_text_orphans = true;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const int number_of_lines_per_page = i + 1;
|
||||
layout.flow_data.paging.page_on_screen.size_h = number_of_lines_per_page * line_height + some;
|
||||
layout.flow_data.paging.origin_on_screen.y =
|
||||
layout.flow_data.paging.page_on_screen.size_h - line_height;
|
||||
graphics_draw_text(&ctx, text, &s_font_info, GRect(0, 0, 180, 1000),
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter,
|
||||
(GTextAttributes *const) &layout);
|
||||
|
||||
const int16_t second_page_start_y =
|
||||
(layout.flow_data.paging.page_on_screen.size_h - layout.flow_data.paging.origin_on_screen.y);
|
||||
const int16_t second_page_end_y =
|
||||
(second_page_start_y + layout.flow_data.paging.page_on_screen.size_h);
|
||||
graphics_draw_line(&ctx, GPoint(0, second_page_start_y), GPoint(180, second_page_start_y));
|
||||
graphics_draw_line(&ctx, GPoint(0, second_page_end_y), GPoint(180, second_page_end_y));
|
||||
ctx.draw_state.drawing_box.origin.x += 180;
|
||||
ctx.draw_state.clip_box.origin.x += 180;
|
||||
}
|
||||
|
||||
cl_check(gbitmap_pbi_eq(s_dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
void test_graphics_draw_text_flow__no_infinite_loop2(void) {
|
||||
// replicates the bug described in PBL-29267 noticed in the notification app
|
||||
// the following values are those we measured in GDB when it entered the infinite loop
|
||||
cl_assert(text_resources_init_font(0, RESOURCE_ID_GOTHIC_24_BOLD, 0, &s_font_info));
|
||||
TextLayoutExtended layout = {
|
||||
.flow_data = {
|
||||
.perimeter.impl = &(GPerimeter){.callback = prv_perimeter_for_display_round,},
|
||||
.perimeter.inset = 8,
|
||||
.paging = {
|
||||
.origin_on_screen = GPoint(12, 83),
|
||||
.page_on_screen.origin_y = 24,
|
||||
.page_on_screen.size_h = 140,
|
||||
}
|
||||
},
|
||||
};
|
||||
char text[] = "Late again? Can you be on time ever? Seriosly? Dude!!!";
|
||||
prv_prepare_fb_steps_xy(GSize(180, 360), 1, 1);
|
||||
ctx.draw_state.avoid_text_orphans = true;
|
||||
|
||||
GRect box = (GRect){.origin = {.x = 12, .y = 59}, .size = {.w = 156, .h = 2480}};
|
||||
graphics_draw_text(&ctx, text, &s_font_info, box,
|
||||
GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter,
|
||||
(GTextAttributes *const) &layout);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(s_dest_bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
@@ -0,0 +1,846 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/graphics_circle_private.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
#include "util/trig.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
#include "util/size.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
#include "pebble_asserts.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "${BIT_DEPTH_NAME}/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_graphics_fill_circle_${BIT_DEPTH_NAME}__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_graphics_fill_circle_${BIT_DEPTH_NAME}__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
void inside_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_fill_circle(ctx, GPoint(14, 14), 12);
|
||||
}
|
||||
|
||||
void white_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_fill_color(ctx, GColorWhite);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_fill_circle(ctx, GPoint(14, 14), 12);
|
||||
}
|
||||
|
||||
void clear_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_fill_color(ctx, GColorClear);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_fill_circle(ctx, GPoint(14, 14), 12);
|
||||
}
|
||||
|
||||
void across_x_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_fill_circle(ctx, GPoint(28, 14), 12);
|
||||
}
|
||||
|
||||
void across_nx_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_fill_circle(ctx, GPoint(-14, 14), 12);
|
||||
}
|
||||
|
||||
void across_y_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_fill_circle(ctx, GPoint(14, 28), 12);
|
||||
}
|
||||
|
||||
void across_ny_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_fill_circle(ctx, GPoint(14, -14), 12);
|
||||
}
|
||||
|
||||
void test_graphics_fill_circle_${BIT_DEPTH_NAME}__origin_layer(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 28, 28));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_across_x_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_nx_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_across_nx_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_across_y_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_ny_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_across_ny_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
#define RADIUS_BIG 15
|
||||
#define RADIUS_MEDIUM 8
|
||||
#define RADIUS_MIN_CALCULATED 3
|
||||
#define RADIUS_MAX_PRECOMPUTED 2
|
||||
#define RADIUS_SMALL 1
|
||||
#define RADIUS_NONE 0
|
||||
|
||||
#define ORIGIN_RECT_NO_CLIP GRect(0, 0, 144, 168)
|
||||
#define ORIGIN_RECT_CLIP_XY GRect(0, 0, 30, 40)
|
||||
#define ORIGIN_RECT_CLIP_NXNY GRect(0, 0, 30, 40)
|
||||
#define CENTER_OF_ORIGIN_RECT GPoint(20, 25)
|
||||
#define CENTER_OF_ORIGIN_RECT_NXNY GPoint(10, 15)
|
||||
|
||||
void test_graphics_fill_circle_${BIT_DEPTH_NAME}__origin_layer_aa(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Big circles
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_origin_aa_r16_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_origin_aa_r16_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_origin_aa_r16_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Medium circles
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_origin_aa_r8_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_origin_aa_r8_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_origin_aa_r8_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Small circles
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_origin_aa_r1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_origin_aa_r1_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_origin_aa_r1_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Testing of the special cases for radius:
|
||||
|
||||
// Radius of 3 - starting point for calculated edges
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_MIN_CALCULATED);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_origin_aa_r3_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Radius of 2 - ending point for precomputed edges
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_MAX_PRECOMPUTED);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_origin_aa_r2_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// No circle
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_NONE);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_origin_aa_r0_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_fill_circle_${BIT_DEPTH_NAME}__offset_layer(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(10, 15, 28, 28));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_inside_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_across_x_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_nx_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_across_nx_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_across_y_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_ny_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_across_ny_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
#define OFFSET_RECT_NO_CLIP GRect(10, 10, 40, 50)
|
||||
#define OFFSET_RECT_CLIP_XY GRect(10, 10, 30, 40)
|
||||
#define OFFSET_RECT_CLIP_NXNY GRect(0, 0, 30, 40)
|
||||
#define CENTER_OF_OFFSET_RECT GPoint(10, 15)
|
||||
#define CENTER_OF_OFFSET_RECT_NXNY GPoint(0, 5)
|
||||
|
||||
void test_graphics_fill_circle_${BIT_DEPTH_NAME}__offset_layer_aa(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Big circles
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_offset_aa_r16_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_offset_aa_r16_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_OFFSET_RECT_NXNY, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_offset_aa_r16_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Medium circles
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_offset_aa_r8_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_offset_aa_r8_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_OFFSET_RECT_NXNY, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_offset_aa_r8_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Small circles
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_offset_aa_r1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_offset_aa_r1_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, 1);
|
||||
graphics_fill_circle(&ctx, CENTER_OF_OFFSET_RECT_NXNY, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_offset_aa_r1_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_fill_circle_${BIT_DEPTH_NAME}__quadrants_aa(void) {
|
||||
#if PBL_COLOR
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
typedef struct {
|
||||
char *filename_part;
|
||||
GCornerMask mask;
|
||||
} TestConfig;
|
||||
|
||||
TestConfig test_config[] = {
|
||||
{
|
||||
.filename_part = "quad_top_left",
|
||||
.mask = GCornerTopLeft,
|
||||
},
|
||||
{
|
||||
.filename_part = "quad_top_right",
|
||||
.mask = GCornerTopRight,
|
||||
},
|
||||
{
|
||||
.filename_part = "quad_bottom_right",
|
||||
.mask = GCornerBottomRight,
|
||||
},
|
||||
{
|
||||
.filename_part = "quad_bottom_left",
|
||||
.mask = GCornerBottomLeft,
|
||||
},
|
||||
{
|
||||
.filename_part = "quads_top",
|
||||
.mask = GCornersTop,
|
||||
},
|
||||
{
|
||||
.filename_part = "quads_bottom",
|
||||
.mask = GCornersBottom,
|
||||
},
|
||||
{
|
||||
.filename_part = "quads_right",
|
||||
.mask = GCornersRight,
|
||||
},
|
||||
{
|
||||
.filename_part = "quads_left",
|
||||
.mask = GCornersLeft,
|
||||
},
|
||||
};
|
||||
|
||||
// note: not the prettiest, fast a quick way to render all the scenarios Nitin was interested in
|
||||
for (int i = 0; i < ARRAY_LENGTH(test_config); i++) {
|
||||
TestConfig c = test_config[i];
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
GPoint pt = CENTER_OF_ORIGIN_RECT;
|
||||
// draw multiple quads with different radiuses
|
||||
for (int r = 1; r <= 15; r++) {
|
||||
graphics_internal_circle_quadrant_fill_aa(&ctx, pt, r, c.mask);
|
||||
|
||||
// center point follows a grid
|
||||
pt.x += 30;
|
||||
if (pt.x > 120) {
|
||||
pt.x = CENTER_OF_ORIGIN_RECT.x;
|
||||
pt.y += 30;
|
||||
}
|
||||
}
|
||||
|
||||
// construct file name and create meaningful assert description
|
||||
char filename[100];
|
||||
snprintf(filename, sizeof(filename),
|
||||
"fill_circle_offset_aa_%s.${BIT_DEPTH_NAME}.pbi", c.filename_part);
|
||||
cl_check_(gbitmap_pbi_eq(&ctx.dest_bitmap, filename), filename);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_graphics_fill_circle_${BIT_DEPTH_NAME}__color(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 28, 28));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
layer_set_update_proc(&layer, &white_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("white_over_black", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_circle_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
layer_set_update_proc(&layer, &clear_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("clear_over_black", ctx.parent_framebuffer, GColorWhite));
|
||||
}
|
||||
|
||||
#define TO_TRIG(deg) (((deg) * TRIG_MAX_ANGLE) / 360)
|
||||
|
||||
void test_graphics_fill_circle_${BIT_DEPTH_NAME}__radial(void){
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Pacman
|
||||
uint32_t angle_end = TRIG_MAX_ANGLE + (TRIG_MAX_ANGLE / 8);
|
||||
uint32_t angle_start = (TRIG_MAX_ANGLE / 8) * 3;
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_radial_internal(&ctx, CENTER_OF_ORIGIN_RECT, 0, RADIUS_BIG, angle_start, angle_end);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_radial_origin_aa_pacman.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Letter C
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_radial_internal(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, RADIUS_BIG, angle_start,
|
||||
angle_end);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_radial_origin_aa_letter_c.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Negative angles - uses same resource image as result should be identical
|
||||
angle_start -= TRIG_MAX_ANGLE;
|
||||
angle_end -= TRIG_MAX_ANGLE;
|
||||
|
||||
// Pacman
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_radial_internal(&ctx, CENTER_OF_ORIGIN_RECT, 0, RADIUS_BIG, angle_start, angle_end);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_radial_origin_aa_pacman.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Letter C
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_radial_internal(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, RADIUS_BIG, angle_start,
|
||||
angle_end);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_radial_origin_aa_letter_c.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// table with most popular angles to test
|
||||
typedef struct {
|
||||
char *filename_part;
|
||||
uint32_t angle;
|
||||
} TestAngles;
|
||||
|
||||
TestAngles test_angles[] = {
|
||||
{
|
||||
.filename_part = "__1_degrees",
|
||||
.angle = TRIG_MAX_ANGLE / 360,
|
||||
},
|
||||
{
|
||||
.filename_part = "__6_degrees",
|
||||
.angle = TRIG_MAX_ANGLE / 60,
|
||||
},
|
||||
{
|
||||
.filename_part = "_30_degrees",
|
||||
.angle = TRIG_MAX_ANGLE / 12,
|
||||
},
|
||||
{
|
||||
.filename_part = "_45_degrees",
|
||||
.angle = TRIG_MAX_ANGLE / 8,
|
||||
},
|
||||
{
|
||||
.filename_part = "_90_degrees",
|
||||
.angle = TRIG_MAX_ANGLE / 4,
|
||||
},
|
||||
{
|
||||
.filename_part = "181_degrees",
|
||||
.angle = TRIG_MAX_ANGLE / 2 + TRIG_MAX_ANGLE / 360,
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LENGTH(test_angles); i++) {
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
GPoint pt = CENTER_OF_ORIGIN_RECT;
|
||||
uint16_t inner_radius = 0;
|
||||
uint16_t outer_radius = 10;
|
||||
|
||||
for (int r = 1; r <= 8; r++) {
|
||||
graphics_fill_radial_internal(&ctx, pt, inner_radius, outer_radius, 0, test_angles[i].angle);
|
||||
|
||||
inner_radius += 1;
|
||||
outer_radius += 3;
|
||||
pt.x += outer_radius * 2;
|
||||
if (pt.x > 120) {
|
||||
pt.x = CENTER_OF_ORIGIN_RECT.x;
|
||||
pt.y += outer_radius * 2;
|
||||
}
|
||||
}
|
||||
|
||||
char filename[100];
|
||||
snprintf(filename, sizeof(filename),
|
||||
"fill_radial_offset_aa_end_angle_%s.${BIT_DEPTH_NAME}.pbi", test_angles[i].filename_part);
|
||||
cl_check_(gbitmap_pbi_eq(&ctx.dest_bitmap, filename), filename);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAY_LENGTH(test_angles); i++) {
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
GPoint pt = CENTER_OF_ORIGIN_RECT;
|
||||
uint16_t inner_radius = 0;
|
||||
uint16_t outer_radius = 10;
|
||||
|
||||
for (int r = 1; r <= 8; r++) {
|
||||
graphics_fill_radial_internal(&ctx, pt, inner_radius, outer_radius, test_angles[i].angle,
|
||||
TRIG_MAX_ANGLE);
|
||||
|
||||
inner_radius += 1;
|
||||
outer_radius += 3;
|
||||
pt.x += outer_radius * 2;
|
||||
if (pt.x > 120) {
|
||||
pt.x = CENTER_OF_ORIGIN_RECT.x;
|
||||
pt.y += outer_radius * 2;
|
||||
}
|
||||
}
|
||||
|
||||
char filename[100];
|
||||
snprintf(filename, sizeof(filename),
|
||||
"fill_radial_offset_aa_start_angle_%s.${BIT_DEPTH_NAME}.pbi", test_angles[i].filename_part);
|
||||
cl_check_(gbitmap_pbi_eq(&ctx.dest_bitmap, filename), filename);
|
||||
}
|
||||
|
||||
// table with radiuses
|
||||
typedef struct {
|
||||
char *filename_part;
|
||||
int radius;
|
||||
} TestRadiuses;
|
||||
|
||||
TestRadiuses test_radiuses[] = {
|
||||
{
|
||||
.filename_part = "_inner_0",
|
||||
.radius = 0,
|
||||
},
|
||||
{
|
||||
.filename_part = "_inner_20",
|
||||
.radius = 15,
|
||||
}
|
||||
};
|
||||
|
||||
// table with quadrants
|
||||
typedef struct {
|
||||
char *filename_part;
|
||||
int angle_start;
|
||||
int angle_end;
|
||||
} TestQuadrants;
|
||||
|
||||
TestQuadrants test_quadrants[] = {
|
||||
{
|
||||
.filename_part = "_part",
|
||||
.angle_start = (TO_TRIG(-45)),
|
||||
.angle_end = (TO_TRIG(-45)),
|
||||
},
|
||||
{
|
||||
.filename_part = "_two_parts",
|
||||
.angle_start = 0,
|
||||
.angle_end = 0,
|
||||
},
|
||||
{
|
||||
.filename_part = "_quadrant_and_two_parts",
|
||||
.angle_start = 0,
|
||||
.angle_end = (TO_TRIG(90)),
|
||||
}
|
||||
};
|
||||
|
||||
#if PBL_COLOR
|
||||
//Colors table
|
||||
GColor colors[4] = {
|
||||
GColorBlack,
|
||||
GColorRed,
|
||||
GColorBlue,
|
||||
GColorGreen,
|
||||
};
|
||||
#endif
|
||||
|
||||
uint16_t outer_radius = 30;
|
||||
int32_t twelveth_of_angle = TRIG_MAX_ANGLE / 12;
|
||||
int32_t quarter_of_angle = TRIG_MAX_ANGLE / 4;
|
||||
GPoint center = GPoint(72, 84);
|
||||
|
||||
// Cases for quadrant joints
|
||||
for (int i = 0; i < ARRAY_LENGTH(test_radiuses); i++) {
|
||||
for (int j = 0; j < ARRAY_LENGTH(test_quadrants); j++) {
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
int offset_angle = 0;
|
||||
|
||||
for (int r = 0; r < 4; r++) {
|
||||
int offset = (((r + 1) % 4 < 2) ? -20 : 20);
|
||||
GPoint pt = GPoint(center.x + ((r % 2 == 0) ? 0 : offset),
|
||||
center.y + ((r % 2 == 0) ? offset * 2 : 0));
|
||||
|
||||
#if PBL_COLOR
|
||||
graphics_context_set_fill_color(&ctx, colors[r]);
|
||||
#endif
|
||||
|
||||
graphics_fill_radial_internal(&ctx, pt, test_radiuses[i].radius, outer_radius,
|
||||
test_quadrants[j].angle_start + offset_angle -
|
||||
twelveth_of_angle,
|
||||
test_quadrants[j].angle_end + offset_angle +
|
||||
twelveth_of_angle);
|
||||
|
||||
offset_angle += quarter_of_angle;
|
||||
}
|
||||
|
||||
char filename[100];
|
||||
snprintf(filename, sizeof(filename),
|
||||
"fill_radial_aa_joints_%s%s.${BIT_DEPTH_NAME}.pbi",
|
||||
test_radiuses[i].filename_part, test_quadrants[j].filename_part);
|
||||
cl_check_(gbitmap_pbi_eq(&ctx.dest_bitmap, filename), filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_graphics_fill_circle_${BIT_DEPTH_NAME}__radial_precise(void){
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// letter C
|
||||
uint32_t angle_end = TRIG_MAX_ANGLE + (TRIG_MAX_ANGLE / 8);
|
||||
uint32_t angle_start = (TRIG_MAX_ANGLE / 8) * 3;
|
||||
GPointPrecise center = GPointPrecise(CENTER_OF_ORIGIN_RECT.x * 8, CENTER_OF_ORIGIN_RECT.y * 8);
|
||||
Fixed_S16_3 radius_inner = (Fixed_S16_3){.integer = RADIUS_MEDIUM};
|
||||
Fixed_S16_3 radius_outer = (Fixed_S16_3){.integer = RADIUS_BIG};
|
||||
|
||||
// Drawing
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_radial_precise_internal(&ctx, center, radius_inner, radius_outer, angle_start, angle_end);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_radial_origin_aa_precise_letter_c.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
//Make the points utilise precision powers
|
||||
center.x.raw_value += 4;
|
||||
center.y.raw_value += 4;
|
||||
radius_inner.raw_value += 4;
|
||||
radius_outer.raw_value += 4;
|
||||
|
||||
// Drawing
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_radial_precise_internal(&ctx, center, radius_inner, radius_outer, angle_start, angle_end);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_radial_origin_aa_precise_halfs_letter_c.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char *filename_part;
|
||||
int angle_start;
|
||||
int angle_end;
|
||||
} TestRadialAnglesConfigs;
|
||||
|
||||
TestRadialAnglesConfigs test_radial_angles[] = {
|
||||
{
|
||||
.filename_part = "_part",
|
||||
.angle_start = (TO_TRIG(-45)),
|
||||
.angle_end = (TO_TRIG(-45)),
|
||||
},
|
||||
{
|
||||
.filename_part = "_two_parts",
|
||||
.angle_start = 0,
|
||||
.angle_end = 0,
|
||||
},
|
||||
{
|
||||
.filename_part = "_quadrant_and_two_parts",
|
||||
.angle_start = 0,
|
||||
.angle_end = (TO_TRIG(90)),
|
||||
},
|
||||
{
|
||||
.filename_part = "_two_quadrants_and_two_parts",
|
||||
.angle_start = 0,
|
||||
.angle_end = (TO_TRIG(180)),
|
||||
},
|
||||
{
|
||||
.filename_part = "_three_quadrants_and_two_parts",
|
||||
.angle_start = 0,
|
||||
.angle_end = (TO_TRIG(270)),
|
||||
},
|
||||
{
|
||||
.filename_part = "_full",
|
||||
.angle_start = 0,
|
||||
.angle_end = TRIG_MAX_ANGLE,
|
||||
}
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char *filename_part;
|
||||
int16_t width;
|
||||
int16_t height;
|
||||
GOvalScaleMode scale_mode;
|
||||
int16_t inset;
|
||||
} TestRadialGRectConfigs;
|
||||
|
||||
TestRadialGRectConfigs test_radial_rects[] = {
|
||||
{
|
||||
.filename_part = "_even_rect_fill",
|
||||
.width = 40,
|
||||
.height = 40,
|
||||
.scale_mode = GOvalScaleModeFillCircle,
|
||||
.inset = 10,
|
||||
},
|
||||
{
|
||||
.filename_part = "_even_rect_fit",
|
||||
.width = 40,
|
||||
.height = 40,
|
||||
.scale_mode = GOvalScaleModeFitCircle,
|
||||
.inset = 10,
|
||||
},
|
||||
{
|
||||
.filename_part = "_odd_rect_fill",
|
||||
.width = 41,
|
||||
.height = 41,
|
||||
.scale_mode = GOvalScaleModeFillCircle,
|
||||
.inset = 10,
|
||||
},
|
||||
{
|
||||
.filename_part = "_odd_rect_fit",
|
||||
.width = 41,
|
||||
.height = 41,
|
||||
.scale_mode = GOvalScaleModeFitCircle,
|
||||
.inset = 10,
|
||||
},
|
||||
{
|
||||
.filename_part = "_even_rect_fill_no_middle",
|
||||
.width = 40,
|
||||
.height = 40,
|
||||
.scale_mode = GOvalScaleModeFillCircle,
|
||||
.inset = 20,
|
||||
},
|
||||
{
|
||||
.filename_part = "_even_rect_fit_no_middle",
|
||||
.width = 40,
|
||||
.height = 40,
|
||||
.scale_mode = GOvalScaleModeFitCircle,
|
||||
.inset = 20,
|
||||
},
|
||||
{
|
||||
.filename_part = "_odd_rect_fill_no_middle",
|
||||
.width = 41,
|
||||
.height = 41,
|
||||
.scale_mode = GOvalScaleModeFillCircle,
|
||||
.inset = 21,
|
||||
},
|
||||
{
|
||||
.filename_part = "_odd_rect_fit_no_middle",
|
||||
.width = 41,
|
||||
.height = 41,
|
||||
.scale_mode = GOvalScaleModeFitCircle,
|
||||
.inset = 21,
|
||||
},
|
||||
};
|
||||
|
||||
void prv_draw_radial_in_rect_debugged(GContext *ctx, int16_t width, int16_t height,
|
||||
GOvalScaleMode scale_mode, int16_t inset,
|
||||
int32_t angle_start, int32_t angle_end){
|
||||
|
||||
int offset_angle = 0;
|
||||
int32_t twelveth_of_angle = TRIG_MAX_ANGLE / 12;
|
||||
GPoint center = GPoint(72, 84);
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
GRect rect = GRect(center.x - (width/2), center.y - (height / 2), width, height);
|
||||
int offset_x = (((i + 1) % 4 < 2) ? -(width * 2 / 3) : (width * 2 / 3));
|
||||
int offset_y = ((i % 4 < 2) ? -(height * 2 / 3) : (height * 2 / 3));
|
||||
rect.origin.x += offset_x;
|
||||
rect.origin.y += offset_y;
|
||||
|
||||
const GRect bigger_rect = GRect(rect.origin.x - 1, rect.origin.y - 1,
|
||||
rect.size.w + 2, rect.size.h + 2);
|
||||
|
||||
graphics_context_set_stroke_color(ctx, PBL_IF_COLOR_ELSE(GColorGreen, GColorWhite));
|
||||
graphics_draw_rect(ctx, &rect);
|
||||
graphics_context_set_stroke_color(ctx, PBL_IF_COLOR_ELSE(GColorRed, GColorBlack));
|
||||
graphics_draw_rect(ctx, &bigger_rect);
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
|
||||
graphics_fill_radial(ctx, rect, scale_mode, inset,
|
||||
angle_start + offset_angle - twelveth_of_angle,
|
||||
angle_end + offset_angle + twelveth_of_angle);
|
||||
|
||||
offset_angle += TRIG_MAX_ANGLE / 4;
|
||||
}
|
||||
}
|
||||
|
||||
void test_graphics_fill_circle_${BIT_DEPTH_NAME}__radial_grect(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
for (int rect_id=0; rect_id < ARRAY_LENGTH(test_radial_rects); rect_id++) {
|
||||
for (int angle_id=0; angle_id < ARRAY_LENGTH(test_radial_angles); angle_id++) {
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
|
||||
prv_draw_radial_in_rect_debugged(&ctx,
|
||||
test_radial_rects[rect_id].width,
|
||||
test_radial_rects[rect_id].height,
|
||||
test_radial_rects[rect_id].scale_mode,
|
||||
test_radial_rects[rect_id].inset,
|
||||
test_radial_angles[angle_id].angle_start,
|
||||
test_radial_angles[angle_id].angle_end);
|
||||
|
||||
char filename[100];
|
||||
snprintf(filename, sizeof(filename),
|
||||
"fill_radial%s%s.${BIT_DEPTH_NAME}.pbi",
|
||||
test_radial_rects[rect_id].filename_part, test_radial_angles[angle_id].filename_part);
|
||||
cl_check_(gbitmap_pbi_eq(&ctx.dest_bitmap, filename), filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void prv_test_dithering_color(GContext *ctx, GColor color) {
|
||||
const uint32_t angle_end = DEG_TO_TRIGANGLE(405);
|
||||
const uint32_t angle_start = DEG_TO_TRIGANGLE(135);
|
||||
GRect rect = GRect(10, 10, 40, 40);
|
||||
|
||||
setup_test_aa_sw(ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_context_set_fill_color(ctx, color);
|
||||
|
||||
// Circle
|
||||
graphics_fill_radial(ctx, rect, GOvalScaleModeFitCircle, 50, 0, TRIG_MAX_ANGLE);
|
||||
|
||||
// Pacman
|
||||
rect.origin.y += 50;
|
||||
graphics_fill_radial(ctx, rect, GOvalScaleModeFitCircle, 50, angle_start, angle_end);
|
||||
|
||||
// Letter C
|
||||
rect.origin.y += 50;
|
||||
graphics_fill_radial(ctx, rect, GOvalScaleModeFitCircle, 10, angle_start, angle_end);;
|
||||
|
||||
// Following SHOULD NOT be dithered into grayscale:
|
||||
graphics_context_set_stroke_color(ctx, color);
|
||||
|
||||
// Circle:
|
||||
GPoint point = GPoint(95, 56);
|
||||
graphics_draw_circle(ctx, point, 20);
|
||||
|
||||
// Line:
|
||||
GPoint p1 = GPoint(75, 140);
|
||||
GPoint p2 = GPoint(115, 140);
|
||||
graphics_draw_line(ctx, p1, p2);
|
||||
|
||||
// Stroked Circle:
|
||||
graphics_context_set_stroke_width(ctx, 12);
|
||||
point.y += 52;
|
||||
graphics_draw_circle(ctx, point, 20);
|
||||
|
||||
// Stroked Line:
|
||||
p1.y += 10;
|
||||
p2.y += 10;
|
||||
graphics_draw_line(ctx, p1, p2);
|
||||
|
||||
// Stroked line turning into circle:
|
||||
point.y = 20;
|
||||
graphics_draw_circle(ctx, point, 5);
|
||||
}
|
||||
|
||||
void test_graphics_fill_circle_${BIT_DEPTH_NAME}__dithering_grayscale(void) {
|
||||
GContext ctx;
|
||||
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
prv_test_dithering_color(&ctx, GColorWhite);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_radial_dither_GColorWhite.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
prv_test_dithering_color(&ctx, GColorLightGray);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_radial_dither_GColorLightGray.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
prv_test_dithering_color(&ctx, GColorDarkGray);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_radial_dither_GColorDarkGray.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
prv_test_dithering_color(&ctx, GColorBlack);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_radial_dither_GColorBlack.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
prv_test_dithering_color(&ctx, GColorJaegerGreen);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_radial_dither_GColorJaegerGreen.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
prv_test_dithering_color(&ctx, GColorOrange);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_radial_dither_GColorOrange.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
@@ -0,0 +1,585 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
#include "util/math.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "${BIT_DEPTH_NAME}/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_graphics_fill_rect_${BIT_DEPTH_NAME}__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_graphics_fill_rect_${BIT_DEPTH_NAME}__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
void inside_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_fill_rect(ctx, &GRect(4, 2, 16, 8));
|
||||
}
|
||||
|
||||
void across_x_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_fill_round_rect(ctx, &GRect(10, 2, 18, 4), 4, GCornersAll);
|
||||
}
|
||||
|
||||
void across_nx_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_fill_round_rect(ctx, &GRect(-10, 2, 18, 4), 4, GCornersAll);
|
||||
}
|
||||
|
||||
void across_y_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_fill_round_rect(ctx, &GRect(4, 5, 18, 10), 4, GCornersAll);
|
||||
}
|
||||
|
||||
void across_ny_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_fill_round_rect(ctx, &GRect(4, -5, 18, 10), 4, GCornersAll);
|
||||
}
|
||||
|
||||
void corners_all_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_fill_round_rect(ctx, &GRect(2, 2, 28, 20), 4, GCornersAll);
|
||||
}
|
||||
|
||||
void white_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_context_set_fill_color(ctx, GColorWhite);
|
||||
graphics_fill_round_rect(ctx, &GRect(2, 2, 28, 20), 4, GCornersAll);
|
||||
}
|
||||
|
||||
void clear_layer_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_context_set_fill_color(ctx, GColorClear);
|
||||
graphics_fill_round_rect(ctx, &GRect(2, 2, 28, 20), 4, GCornersAll);
|
||||
}
|
||||
|
||||
void corners_clipped_update_callback(Layer* me, GContext* ctx) {
|
||||
graphics_context_set_antialiased(ctx, false);
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_fill_round_rect(ctx, &GRect(-19, 0, 20, 20), 4, GCornersAll);
|
||||
}
|
||||
|
||||
void test_graphics_fill_rect_${BIT_DEPTH_NAME}__origin_layer(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 20, 10));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_inside_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_across_x_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_nx_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_across_nx_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_across_y_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_ny_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_across_ny_origin_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_fill_rect_${BIT_DEPTH_NAME}__offset_layer(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(10, 15, 20, 10));
|
||||
layer_set_update_proc(&layer, &inside_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_inside_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_x_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_across_x_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_nx_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_across_nx_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_y_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_across_y_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &across_ny_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_across_ny_offset_layer.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_fill_rect_${BIT_DEPTH_NAME}__clipped(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 100, 100));
|
||||
layer_set_update_proc(&layer, &corners_clipped_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_clipped.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_fill_rect_${BIT_DEPTH_NAME}__color(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
layer_init(&layer, &GRect(0, 0, 32, 24));
|
||||
layer_set_update_proc(&layer, &corners_all_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_corners_all.${BIT_DEPTH_NAME}.pbi"));
|
||||
layer_set_update_proc(&layer, &white_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("white_over_black", ctx.parent_framebuffer, GColorWhite));
|
||||
|
||||
test_graphics_context_reset(&ctx, fb);
|
||||
layer_set_update_proc(&layer, &corners_all_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_corners_all.${BIT_DEPTH_NAME}.pbi"));
|
||||
layer_set_update_proc(&layer, &clear_layer_update_callback);
|
||||
layer_render_tree(&layer, &ctx);
|
||||
cl_check(framebuffer_is_empty("clear_over_black", ctx.parent_framebuffer, GColorWhite));
|
||||
}
|
||||
|
||||
#define RECT_WIDTH 30
|
||||
#define RECT_HEIGHT 40
|
||||
#define ORIGIN_RECT_NO_CLIP GRect(0, 0, 144, 168)
|
||||
#define ORIGIN_RECT_CLIP_XY GRect(0, 0, 20, 20)
|
||||
#define ORIGIN_RECT_CLIP_NXNY GRect(0, 0, 144, 168)
|
||||
#define ORIGIN_DRAW_RECT_NO_CLIP GRect(6, 6, RECT_WIDTH, RECT_HEIGHT)
|
||||
#define ORIGIN_DRAW_RECT_CLIP_XY GRect(6, 6, RECT_WIDTH, RECT_HEIGHT)
|
||||
#define ORIGIN_DRAW_RECT_CLIP_NXNY GRect(-16, -16, RECT_WIDTH, RECT_HEIGHT)
|
||||
|
||||
void test_graphics_fill_rect_8bit__transparency(void) {
|
||||
GContext ctx;
|
||||
FrameBuffer fb;
|
||||
framebuffer_init(&fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
test_graphics_context_init(&ctx, &fb);
|
||||
|
||||
// AA = true, SW has no effect, all corners
|
||||
setup_test_aa_sw(&ctx, &fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_context_set_fill_color(&ctx, GColorBlue);
|
||||
graphics_fill_rect(&ctx, &GRect(10, 10, 100, 140));
|
||||
graphics_context_set_fill_color(&ctx, GColorRed);
|
||||
ctx.draw_state.fill_color.a = 0;
|
||||
graphics_fill_rect(&ctx, &GRect(20, 20, 70, 20));
|
||||
ctx.draw_state.fill_color.a = 1;
|
||||
graphics_fill_rect(&ctx, &GRect(20, 50, 70, 20));
|
||||
ctx.draw_state.fill_color.a = 2;
|
||||
graphics_fill_rect(&ctx, &GRect(20, 80, 70, 20));
|
||||
ctx.draw_state.fill_color.a = 3;
|
||||
graphics_fill_rect(&ctx, &GRect(20, 110, 70, 20));
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_transparent.8bit.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_fill_rect_${BIT_DEPTH_NAME}__origin_radius(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// AA = true, SW has no effect, all corners
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 0, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r0_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 1, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 2, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r2_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 3, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r3_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 4, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r4_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 5, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r5_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 6, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r6_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 7, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r7_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 8, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r8_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Should draw same as radius 8
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 9, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r9_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_fill_rect_${BIT_DEPTH_NAME}__origin_radius_aa(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// AA = true, SW has no effect, all corners
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 0, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r0_aa_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 1, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r1_aa_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 2, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r2_aa_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Remove these #ifdefs in PBL-15916 when support for non-antialiased rounded rect
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
// TODO: Fix left corners PBL-15915
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 3, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r3_aa_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix left corners PBL-15915
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 4, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r4_aa_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix left corners PBL-15915
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 5, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r5_aa_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix left corners PBL-15915
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 6, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r6_aa_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Fix left corners PBL-15915
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 7, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r7_aa_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 8, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r8_aa_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Remove these #ifdefs in PBL-15916 when support for non-antialiased rounded rect
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, 9, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r9_aa_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_aa_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2), GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax1_aa_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Should draw same as rmax1
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) + 1, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax2_aa_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
// AA = true, SW has no effect, Clip XY, all corners
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, 0, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r0_aa_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Remove these #ifdefs in PBL-15916 when support for non-antialiased rounded rect
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, 1, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r1_aa_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, 2, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r2_aa_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, 3, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r3_aa_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_aa_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2), GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax1_aa_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Should draw same as rmax1
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_XY, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) + 1, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax2_aa_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
|
||||
// AA = true, SW has no effect, Clip NXNY, all corners
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY , ORIGIN_RECT_CLIP_NXNY , true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY , 0, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r0_aa_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// TODO: Remove these #ifdefs in PBL-15916 when support for non-antialiased rounded rect
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY , ORIGIN_RECT_CLIP_NXNY , true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY , 1, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r1_aa_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY , ORIGIN_RECT_CLIP_NXNY , true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY , 2, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r2_aa_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY , ORIGIN_RECT_CLIP_NXNY , true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY , 3, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_r3_aa_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY , ORIGIN_RECT_CLIP_NXNY , true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY , ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_aa_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY , ORIGIN_RECT_CLIP_NXNY , true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY , ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2), GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax1_aa_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Should draw same as rmax1
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY , ORIGIN_RECT_CLIP_NXNY , true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_CLIP_NXNY , ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) + 1, GCornersAll);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax2_aa_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_graphics_fill_rect_${BIT_DEPTH_NAME}__origin_corners(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// TODO: Currently prv_fill_rect only supports up to radius 8, fix in PBL-15916 to support
|
||||
// arbitrary radius values - this affects all tests here
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornersBottom);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_bottom.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornersTop);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_top.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornersLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_left.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornersRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_right.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornerTopLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_topleft.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornerTopRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_topright.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornerBottomLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_bottomleft.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornerBottomRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_bottomright.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_fill_rect_${BIT_DEPTH_NAME}__origin_aa_corners(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// TODO: Remove these #ifdefs in PBL-15916 when support for non-antialiased rounded rect
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornersBottom);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_aa_bottom.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornersTop);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_aa_top.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornersLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_aa_left.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornersRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_aa_right.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornerTopLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_aa_topleft.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornerTopRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_aa_topright.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornerBottomLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_aa_bottomleft.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, 1);
|
||||
graphics_fill_round_rect(&ctx, &ORIGIN_DRAW_RECT_NO_CLIP, ((MIN(RECT_WIDTH, RECT_HEIGHT)) / 2) - 1, GCornerBottomRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_origin_rmax_aa_bottomright.${BIT_DEPTH_NAME}.pbi"));
|
||||
#endif
|
||||
}
|
||||
|
||||
extern uint16_t prv_clamp_corner_radius(GSize size, GCornerMask corner_mask,
|
||||
uint16_t radius);
|
||||
|
||||
void test_graphics_fill_rect__corner_radius(void) {
|
||||
// Test 0 radius cases
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(0, 0), GCornerNone, 0), 0);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(0, 0), GCornerNone, 2), 0);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(0, 0), GCornersAll, 0), 0);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(0, 0), GCornersAll, 8), 0);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(0, 5), GCornersAll, 8), 0);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(4, 0), GCornersAll, 8), 0);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(4, 8), GCornerNone, 8), 0);
|
||||
|
||||
// Test minimum of width and height is taken as radius
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(4, 8), GCornersAll, 4), 2);
|
||||
|
||||
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(1, 10), GCornersAll, 4), 0);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(2, 10), GCornersAll, 4), 1);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(3, 10), GCornersAll, 4), 1);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(4, 10), GCornersAll, 4), 2);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(5, 10), GCornersAll, 4), 2);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(6, 10), GCornersAll, 4), 3);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(7, 10), GCornersAll, 4), 3);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(8, 10), GCornersAll, 4), 4);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(9, 10), GCornersAll, 4), 4);
|
||||
cl_assert_equal_i(prv_clamp_corner_radius(GSize(10, 10), GCornersAll, 4), 4);
|
||||
}
|
||||
|
||||
#define BOX_SIZE 10
|
||||
#define CLIP_RECT_DRAW_BOX GRect(10, 10, 140, 30)
|
||||
#define CLIP_RECT_CLIP_BOX GRect(10, 10, 120, 2*BOX_SIZE + 4)
|
||||
#define CLIP_RECT_RECT_BOX GRect(0, 0, BOX_SIZE, BOX_SIZE)
|
||||
#define CLIP_OFFSET 40
|
||||
// yoffset if used to allow for multiple test cases to be drawn and tested on the same test image
|
||||
// nudge is used to move the rectangle just outside the clipping box on each corner
|
||||
static void prv_fill_rects(GContext *ctx, uint16_t yoffset, int16_t nudge, int16_t radius) {
|
||||
// Adjust drawing box and clipping box
|
||||
ctx->draw_state.drawing_box = CLIP_RECT_DRAW_BOX;
|
||||
ctx->draw_state.drawing_box.origin.y += yoffset;
|
||||
ctx->draw_state.clip_box = CLIP_RECT_CLIP_BOX;
|
||||
ctx->draw_state.clip_box.origin.y += yoffset;
|
||||
|
||||
// Top left corner of clipping box
|
||||
GRect box = CLIP_RECT_RECT_BOX;
|
||||
box.origin.x -= nudge;
|
||||
box.origin.y -= nudge;
|
||||
graphics_fill_round_rect(ctx, &box, radius, GCornersAll);
|
||||
|
||||
// Top right corner of clipping box
|
||||
box.origin.x = CLIP_RECT_CLIP_BOX.size.w - BOX_SIZE;
|
||||
box.origin.x += nudge;
|
||||
graphics_fill_round_rect(ctx, &box, radius, GCornersAll);
|
||||
|
||||
// Bottom right corner of clipping box
|
||||
box.origin.y = CLIP_RECT_CLIP_BOX.size.h - BOX_SIZE;
|
||||
box.origin.y += nudge;
|
||||
graphics_fill_round_rect(ctx, &box, radius, GCornersAll);
|
||||
|
||||
// Bottom left corner of clipping box
|
||||
box.origin.x = 0;
|
||||
box.origin.x -= nudge;
|
||||
graphics_fill_round_rect(ctx, &box, radius, GCornersAll);
|
||||
}
|
||||
|
||||
void test_graphics_fill_rect_${BIT_DEPTH_NAME}__clipping_rect(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, false, 1);
|
||||
graphics_context_set_stroke_color(&ctx, GColorBlack);
|
||||
|
||||
// Fills rectangles around boundaries of clipping box - AA false
|
||||
prv_fill_rects(&ctx, 0, 0, 0);
|
||||
prv_fill_rects(&ctx, CLIP_OFFSET, 1, 0);
|
||||
prv_fill_rects(&ctx, 2 * CLIP_OFFSET, 0, 4);
|
||||
prv_fill_rects(&ctx, 3 * CLIP_OFFSET, 1, 4);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_clip_rect.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_fill_rect_${BIT_DEPTH_NAME}__clipping_rect_aa(void) {
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
setup_test_aa_sw(&ctx, fb, CLIP_RECT_CLIP_BOX, CLIP_RECT_DRAW_BOX, true, 1);
|
||||
graphics_context_set_stroke_color(&ctx, GColorBlack);
|
||||
|
||||
// Fills rectangles around boundaries of clipping box - AA true
|
||||
prv_fill_rects(&ctx, 0, 0, 0);
|
||||
prv_fill_rects(&ctx, CLIP_OFFSET, 1, 0);
|
||||
prv_fill_rects(&ctx, 2 * CLIP_OFFSET, 0, 4);
|
||||
prv_fill_rects(&ctx, 3 * CLIP_OFFSET, 1, 4);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "fill_rect_clip_rect_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
* 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/gtypes.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/gpath.h"
|
||||
#include "util/trig.h"
|
||||
#include "applib/ui/ui.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "util.h"
|
||||
#include "test_graphics.h"
|
||||
#include "${BIT_DEPTH_NAME}/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
static const int16_t SCREEN_WIDTH = 144;
|
||||
static const int16_t SCREEN_HEIGHT = 168;
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
static const GPathInfo s_house_path_info = {
|
||||
.num_points = 11,
|
||||
.points = (GPoint []) {
|
||||
{-40, 0}, {0, -40}, {40, 0}, {28, 0}, {28, 40}, {10, 40},
|
||||
{10, 16}, {-10, 16}, {-10, 40}, {-28, 40}, {-28, 0},
|
||||
},
|
||||
};
|
||||
|
||||
static const GPathInfo s_bolt_path_info = {
|
||||
.num_points = 6,
|
||||
.points = (GPoint []) {{21, 0}, {14, 26}, {28, 26}, {7, 60}, {14, 34}, {0, 34}}
|
||||
};
|
||||
|
||||
static const GPathInfo s_duplicates_path_info = {
|
||||
6,
|
||||
(GPoint[]) {
|
||||
{40, 0},
|
||||
{40, 0},
|
||||
{0, 40},
|
||||
{0, 40},
|
||||
{80, 40},
|
||||
{80, 40}
|
||||
}
|
||||
};
|
||||
|
||||
static const GPathInfo s_single_duplicate_path_info = {
|
||||
2,
|
||||
(GPoint[]) {
|
||||
{40, 0},
|
||||
{40, 0}
|
||||
}
|
||||
};
|
||||
|
||||
static const GPathInfo s_crossing_path_info = {
|
||||
6,
|
||||
(GPoint[]) {
|
||||
{0, 40},
|
||||
{20, 20},
|
||||
{60, 60},
|
||||
{80, 40},
|
||||
{60, 20},
|
||||
{20, 60}
|
||||
}
|
||||
};
|
||||
|
||||
static const GPathInfo s_infinite_path_info = {
|
||||
16,
|
||||
(GPoint[]) {
|
||||
{-50, 0},
|
||||
{-50, -60},
|
||||
{10, -60},
|
||||
{10, -20},
|
||||
{-10, -20},
|
||||
{-10, -40},
|
||||
{-30, -40},
|
||||
{-30, -20},
|
||||
{50, -20},
|
||||
{50, 40},
|
||||
{-10, 40},
|
||||
{-10, 0},
|
||||
{10, 0},
|
||||
{10, 20},
|
||||
{30, 20},
|
||||
{30, 0}
|
||||
}
|
||||
};
|
||||
|
||||
static const GPathInfo s_aa_clipping_path_info = {
|
||||
.num_points = 4,
|
||||
.points = (GPoint []) {{0,0}, {200, 0}, {200, 30}, {0, 30}}
|
||||
};
|
||||
|
||||
static bool s_outline_mode = false;
|
||||
static int s_path_angle = 0;
|
||||
|
||||
static GPath *s_house_path = NULL;
|
||||
static GPath *s_bolt_path = NULL;
|
||||
static GPath *s_duplicates_path = NULL;
|
||||
static GPath *s_single_duplicate_path = NULL;
|
||||
static GPath *s_crossing_path = NULL;
|
||||
static GPath *s_infinite_path = NULL;
|
||||
static GPath *s_current_path = NULL;
|
||||
static GPath *s_aa_clipping_path = NULL;
|
||||
|
||||
static void prv_filled_update_proc(Layer *layer, GContext *ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
|
||||
#if 0 // Guidelines
|
||||
int num_segments = 4;
|
||||
int segment_width = SCREEN_WIDTH / num_segments;
|
||||
int segment_height = SCREEN_HEIGHT / num_segments;
|
||||
for (int i = 1; i < num_segments; ++i) {
|
||||
graphics_draw_line(ctx,
|
||||
GPoint(segment_width * i, 0),
|
||||
GPoint(segment_width * i, SCREEN_HEIGHT));
|
||||
graphics_draw_line(ctx,
|
||||
GPoint(0, segment_height * i),
|
||||
GPoint(SCREEN_WIDTH, segment_height * i));
|
||||
}
|
||||
#endif
|
||||
|
||||
gpath_rotate_to(s_current_path, s_path_angle * TRIG_MAX_ANGLE / 360);
|
||||
|
||||
if (s_outline_mode) {
|
||||
graphics_context_set_stroke_color(ctx, GColorBlack);
|
||||
gpath_draw_outline(ctx, s_current_path);
|
||||
} else {
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
gpath_draw_filled(ctx, s_current_path);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_reset(void) {
|
||||
gpath_move_to(s_house_path, GPoint(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2));
|
||||
s_outline_mode = false;
|
||||
s_path_angle = 0;
|
||||
}
|
||||
|
||||
// setup and teardown
|
||||
void test_graphics_gpath_${BIT_DEPTH_NAME}__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
s_house_path = gpath_create(&s_house_path_info);
|
||||
s_bolt_path = gpath_create(&s_bolt_path_info);
|
||||
s_duplicates_path = gpath_create(&s_duplicates_path_info);
|
||||
s_crossing_path = gpath_create(&s_crossing_path_info);
|
||||
s_infinite_path = gpath_create(&s_infinite_path_info);
|
||||
s_aa_clipping_path = gpath_create(&s_aa_clipping_path_info);
|
||||
prv_reset();
|
||||
}
|
||||
|
||||
void test_graphics_gpath_${BIT_DEPTH_NAME}__cleanup(void) {
|
||||
free(fb);
|
||||
gpath_destroy(s_house_path);
|
||||
gpath_destroy(s_bolt_path);
|
||||
gpath_destroy(s_duplicates_path);
|
||||
gpath_destroy(s_infinite_path);
|
||||
gpath_destroy(s_crossing_path);
|
||||
gpath_destroy(s_aa_clipping_path);
|
||||
}
|
||||
|
||||
// tests
|
||||
void test_graphics_gpath_${BIT_DEPTH_NAME}__filled(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
|
||||
prv_reset();
|
||||
s_current_path = s_house_path;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "gpath_filled.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
void test_graphics_gpath_${BIT_DEPTH_NAME}__filled_clipped(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_house_path;
|
||||
ctx.draw_state.clip_box = GRect(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"gpath_filled_top_clipped.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_house_path;
|
||||
ctx.draw_state.clip_box = GRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"gpath_filled_bottom_clipped.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_house_path;
|
||||
ctx.draw_state.clip_box = GRect(0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"gpath_filled_left_clipped.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_house_path;
|
||||
ctx.draw_state.clip_box = GRect(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"gpath_filled_right_clipped.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
// outside with no clipping -- results should be identical to the regular filled test
|
||||
void test_graphics_gpath_${BIT_DEPTH_NAME}__filled_outside(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
|
||||
printf("-- top\n");
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_house_path;
|
||||
gpath_move_to(s_house_path, GPoint(SCREEN_WIDTH / 2, 0));
|
||||
ctx.draw_state.drawing_box = GRect(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
ctx.draw_state.clip_box = GRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "gpath_filled.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
printf("-- bottom\n");
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_house_path;
|
||||
gpath_move_to(s_house_path, GPoint(SCREEN_WIDTH / 2, SCREEN_HEIGHT));
|
||||
ctx.draw_state.drawing_box = GRect(0, -SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
ctx.draw_state.clip_box = GRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "gpath_filled.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
printf("-- left\n");
|
||||
prv_reset();
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_house_path;
|
||||
gpath_move_to(s_house_path, GPoint(0, SCREEN_HEIGHT / 2));
|
||||
ctx.draw_state.drawing_box = GRect(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
ctx.draw_state.clip_box = GRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "gpath_filled.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
printf("-- right\n");
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_house_path;
|
||||
gpath_move_to(s_house_path, GPoint(SCREEN_WIDTH, SCREEN_HEIGHT / 2));
|
||||
ctx.draw_state.drawing_box = GRect(-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
ctx.draw_state.clip_box = GRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "gpath_filled.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
// AA section
|
||||
void test_graphics_gpath_${BIT_DEPTH_NAME}__filled_aa(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
|
||||
// House path - tests horizontal line edge case
|
||||
prv_reset();
|
||||
s_current_path = s_house_path;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "gpath_filled_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Special case for two points that are duplicates...
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_single_duplicate_path;
|
||||
ctx.draw_state.clip_box = GRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"gpath_filled_single_duplicate_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
}
|
||||
|
||||
void test_graphics_gpath_${BIT_DEPTH_NAME}__filled_clipped_aa(void) {
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_house_path;
|
||||
ctx.draw_state.clip_box = GRect(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"gpath_filled_top_clipped_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_house_path;
|
||||
ctx.draw_state.clip_box = GRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"gpath_filled_bottom_clipped_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_house_path;
|
||||
ctx.draw_state.clip_box = GRect(0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"gpath_filled_left_clipped_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_house_path;
|
||||
ctx.draw_state.clip_box = GRect(SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"gpath_filled_right_clipped_aa.${BIT_DEPTH_NAME}.pbi"));
|
||||
}
|
||||
|
||||
// Additional test to check AA on edges - works only on 8bit
|
||||
void test_graphics_gpath_8bit__filled_bolt_aa(void) {
|
||||
// NOTE: Those tests are being performed only in 8bits due to differences caused by AA,
|
||||
// performing them in both 1bit and 8bit would create differences on the edges
|
||||
// and fail unit tests as a result
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
|
||||
// Bolt path - test antialiased edges
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_bolt_path;
|
||||
ctx.draw_state.clip_box = GRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"gpath_filled_bolt_aa.8bit.pbi"));
|
||||
|
||||
// Duplicate points in GPath test - makes sure theres no division by zero ;)
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_duplicates_path;
|
||||
ctx.draw_state.clip_box = GRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"gpath_filled_duplicates_aa.8bit.pbi"));
|
||||
|
||||
// Crossing path - makes sure algorithm works for path that crosses itself
|
||||
prv_reset();
|
||||
s_current_path = s_crossing_path;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "gpath_filled_crossing_aa.8bit.pbi"));
|
||||
|
||||
// Infinite path - shows an example where path seems to be crossing itself but
|
||||
// in fact it does not
|
||||
prv_reset();
|
||||
s_current_path = s_infinite_path;
|
||||
gpath_move_to(s_infinite_path, GPoint(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2));
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "gpath_filled_infinite_aa.8bit.pbi"));
|
||||
|
||||
// An angle of infinite path - here we see the spacing between the parts
|
||||
prv_reset();
|
||||
s_current_path = s_infinite_path;
|
||||
gpath_move_to(s_infinite_path, GPoint(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2));
|
||||
s_path_angle = 45;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "gpath_filled_infinite_45_aa.8bit.pbi"));
|
||||
|
||||
// Another angle of infinite path
|
||||
prv_reset();
|
||||
s_current_path = s_infinite_path;
|
||||
gpath_move_to(s_infinite_path, GPoint(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2));
|
||||
s_path_angle = 70;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "gpath_filled_infinite_70_aa.8bit.pbi"));
|
||||
|
||||
// House path - two edge cases for tipping points of the path
|
||||
prv_reset();
|
||||
s_current_path = s_house_path;
|
||||
gpath_move_to(s_house_path, GPoint(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2));
|
||||
s_path_angle = 20;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "gpath_filled_house_20_aa.8bit.pbi"));
|
||||
|
||||
// This case demonstrates tipping point that is also the starting point of the path
|
||||
prv_reset();
|
||||
s_current_path = s_house_path;
|
||||
gpath_move_to(s_house_path, GPoint(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2));
|
||||
s_path_angle = 105;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "gpath_filled_house_105_aa.8bit.pbi"));
|
||||
|
||||
// Safety
|
||||
s_path_angle = 0;
|
||||
}
|
||||
|
||||
void test_graphics_gpath_8bit__clipping_aa(void) {
|
||||
// NOTE: This test verifies correct clipping of anti-aliased edges on gpaths, therefore
|
||||
// it works only on 8bit
|
||||
GContext ctx;
|
||||
Layer layer;
|
||||
|
||||
prv_reset();
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
s_current_path = s_aa_clipping_path;
|
||||
s_path_angle = 17;
|
||||
ctx.draw_state.clip_box = GRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2);
|
||||
graphics_context_set_antialiased(&ctx, true);
|
||||
prv_filled_update_proc(NULL, &ctx);
|
||||
// NOTE: Expected result of this test is to have an antialiased stripe go through the screen,
|
||||
// where antialiased edges are being nicely cut off on top and bottom of the stripe
|
||||
// (antialiased gradient would dive into the stripe near screen edges), also top
|
||||
// left corner is intentinally ending just before screen cuts it out to verify that
|
||||
// fractional anti-aliasing is not bleeding into row before (would occur as pixels on
|
||||
// right side of the screen)
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap,
|
||||
"gpath_clipping_aa.8bit.pbi"));
|
||||
|
||||
// Safety
|
||||
s_path_angle = 0;
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
#include "applib/graphics/gtransform.h"
|
||||
#include "util/trig.h"
|
||||
|
||||
#include "util/math_fixed.h"
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "stubs_compiled_with_legacy2_sdk.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_pebble_tasks.h"
|
||||
#include "stubs_print.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_ui_window.h"
|
||||
#include "stubs_unobstructed_area.h"
|
||||
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
/////////////////////////////////
|
||||
/// Generic matrix tests
|
||||
/////////////////////////////////
|
||||
void test_graphics_gtransform_${BIT_DEPTH_NAME}__types_gtransformnumber(void) {
|
||||
GTransform t_c; // matrix to compare against
|
||||
GTransformNumber tn;
|
||||
int32_t test_num;
|
||||
|
||||
tn = GTransformNumberFromNumber(1);
|
||||
test_num = (int32_t)((float)1 * (1 << FIXED_S32_16_PRECISION));
|
||||
cl_assert((memcmp(&tn, &test_num, sizeof(GTransformNumber)) == 0));
|
||||
|
||||
tn = GTransformNumberFromNumber(3.5);
|
||||
test_num = (int32_t)((float)3.5 * (1 << FIXED_S32_16_PRECISION));
|
||||
cl_assert((memcmp(&tn, &test_num, sizeof(GTransformNumber)) == 0));
|
||||
|
||||
tn = GTransformNumberFromNumber(-2);
|
||||
test_num = (int32_t)((float)-2 * (1 << FIXED_S32_16_PRECISION));
|
||||
cl_assert((memcmp(&tn, &test_num, sizeof(GTransformNumber)) == 0));
|
||||
|
||||
tn = GTransformNumberFromNumber(-3.5);
|
||||
test_num = (int32_t)((float)-3.5 * (1 << FIXED_S32_16_PRECISION));
|
||||
cl_assert((memcmp(&tn, &test_num, sizeof(GTransformNumber)) == 0));
|
||||
|
||||
t_c = GTransform(GTransformNumberFromNumber(1), GTransformNumberFromNumber(2),
|
||||
GTransformNumberFromNumber(3), GTransformNumberFromNumber(4),
|
||||
GTransformNumberFromNumber(5), GTransformNumberFromNumber(6));
|
||||
|
||||
int32_t test_array[6] = {1 * (1 << FIXED_S32_16_PRECISION),
|
||||
2 * (1 << FIXED_S32_16_PRECISION),
|
||||
3 * (1 << FIXED_S32_16_PRECISION),
|
||||
4 * (1 << FIXED_S32_16_PRECISION),
|
||||
5 * (1 << FIXED_S32_16_PRECISION),
|
||||
6 * (1 << FIXED_S32_16_PRECISION)};
|
||||
|
||||
cl_assert((memcmp(&t_c, &test_array, sizeof(GTransform)) == 0));
|
||||
cl_assert(gtransform_is_equal(&t_c, (const GTransform*)&test_array));
|
||||
|
||||
t_c = GTransformFromNumbers(1, 2, 3, 4, 5, 6);
|
||||
cl_assert(gtransform_is_equal(&t_c, (const GTransform*)&test_array));
|
||||
|
||||
// Test to make sure implemented rotation calculation is correct
|
||||
int32_t angle = DEG_TO_TRIGANGLE(45);
|
||||
int32_t cosine = cos_lookup(angle);
|
||||
GTransformNumber num = GTransformNumberFromNumber((float)cosine / TRIG_MAX_RATIO);
|
||||
GTransformNumber num2 =
|
||||
(GTransformNumber) { .raw_value = (int32_t)(((int64_t)cosine *
|
||||
GTransformNumberOne.raw_value) / TRIG_MAX_RATIO) };
|
||||
cl_assert(num.raw_value == num2.raw_value);
|
||||
|
||||
}
|
||||
|
||||
void test_graphics_gtransform_${BIT_DEPTH_NAME}__types_precise(void) {
|
||||
GPointPrecise pointP = GPointPreciseFromGPoint(GPoint(2, 5));
|
||||
GPointPrecise pointP_c = GPointPrecise((2 % GPOINT_PRECISE_MAX) << GPOINT_PRECISE_PRECISION,
|
||||
(5 % GPOINT_PRECISE_MAX) << GPOINT_PRECISE_PRECISION);
|
||||
|
||||
cl_assert(gpointprecise_equal(&pointP, &pointP_c));
|
||||
|
||||
GVectorPrecise vectorP = GVectorPreciseFromGVector(GVector(2, 5));
|
||||
GVectorPrecise vectorP_c = GVectorPrecise((2 % GVECTOR_PRECISE_MAX) << GVECTOR_PRECISE_PRECISION,
|
||||
(5 % GVECTOR_PRECISE_MAX) << GVECTOR_PRECISE_PRECISION);
|
||||
|
||||
cl_assert(gvectorprecise_equal(&vectorP, &vectorP_c));
|
||||
}
|
||||
|
||||
void test_graphics_gtransform_${BIT_DEPTH_NAME}__init(void) {
|
||||
GTransform t;
|
||||
GTransform t_c; // matrix to compare against
|
||||
|
||||
// Test Identity Matrix
|
||||
t = GTransformIdentity();
|
||||
t_c = GTransformFromNumbers(1, 0, 0, 1, 0, 0);
|
||||
|
||||
cl_assert(gtransform_is_equal(&t, &t_c));
|
||||
cl_assert(gtransform_is_identity(&t));
|
||||
cl_assert(gtransform_is_identity(&t_c));
|
||||
|
||||
// Test Scale Matrix
|
||||
t = GTransformScale(GTransformNumberFromNumber(2), GTransformNumberFromNumber(5));
|
||||
t_c = GTransformFromNumbers(2, 0, 0, 5, 0, 0);
|
||||
|
||||
cl_assert(gtransform_is_equal(&t, &t_c));
|
||||
cl_assert(gtransform_is_only_scale(&t));
|
||||
cl_assert(gtransform_is_only_scale(&t_c));
|
||||
|
||||
t_c = GTransformScaleFromNumber(2, 5);
|
||||
cl_assert(gtransform_is_equal(&t, &t_c));
|
||||
|
||||
// Test Translation Matrix
|
||||
t = GTransformTranslation(GTransformNumberFromNumber(2), GTransformNumberFromNumber(5));
|
||||
t_c = GTransformFromNumbers(1, 0, 0, 1, 2, 5);
|
||||
|
||||
cl_assert(gtransform_is_equal(&t, &t_c));
|
||||
cl_assert(gtransform_is_only_translation(&t));
|
||||
cl_assert(gtransform_is_only_translation(&t_c));
|
||||
|
||||
t_c = GTransformTranslationFromNumber(2, 5);
|
||||
cl_assert(gtransform_is_equal(&t, &t_c));
|
||||
|
||||
// Test Rotation Matrix
|
||||
int32_t angle = DEG_TO_TRIGANGLE(45);
|
||||
t = GTransformRotation(angle);
|
||||
|
||||
int32_t cosine = cos_lookup(angle);
|
||||
int32_t sine = sin_lookup(angle);
|
||||
|
||||
t_c = GTransform(GTransformNumberFromNumber((float)cosine / TRIG_MAX_RATIO),
|
||||
GTransformNumberFromNumber(-(float)sine / TRIG_MAX_RATIO),
|
||||
GTransformNumberFromNumber((float)sine / TRIG_MAX_RATIO),
|
||||
GTransformNumberFromNumber((float)cosine / TRIG_MAX_RATIO),
|
||||
GTransformNumberZero,
|
||||
GTransformNumberZero);
|
||||
cl_assert(gtransform_is_equal(&t, &t_c));
|
||||
|
||||
angle = DEG_TO_TRIGANGLE(46);
|
||||
cosine = cos_lookup(angle);
|
||||
sine = sin_lookup(angle);
|
||||
|
||||
t_c = GTransform(GTransformNumberFromNumber((float)cosine / TRIG_MAX_RATIO),
|
||||
GTransformNumberFromNumber(-(float)sine / TRIG_MAX_RATIO),
|
||||
GTransformNumberFromNumber((float)sine / TRIG_MAX_RATIO),
|
||||
GTransformNumberFromNumber((float)cosine / TRIG_MAX_RATIO),
|
||||
GTransformNumberZero,
|
||||
GTransformNumberZero);
|
||||
cl_assert(!gtransform_is_equal(&t, &t_c));
|
||||
|
||||
t = GTransformRotation(0); // Should return identity if angle == 0
|
||||
cl_assert(gtransform_is_identity(&t));
|
||||
}
|
||||
|
||||
void test_graphics_gtransform_${BIT_DEPTH_NAME}__concat(void) {
|
||||
GTransform t_new;
|
||||
GTransform t1;
|
||||
GTransform t2;
|
||||
GTransform t_c; // matrix to compare against
|
||||
|
||||
// Test identity concatenation
|
||||
t1 = GTransformIdentity();
|
||||
t2 = GTransformIdentity();
|
||||
t_c = GTransformIdentity();
|
||||
|
||||
gtransform_concat(&t_new, &t1, &t2);
|
||||
cl_assert(gtransform_is_equal(&t_new, &t_c));
|
||||
|
||||
// Test identity concatenation with non-identity
|
||||
t1 = GTransformFromNumbers(1, 2, 3, 4, 5, 6);
|
||||
t2 = GTransformIdentity();
|
||||
t_c = GTransformFromNumbers(1, 2, 3, 4, 5, 6);
|
||||
gtransform_concat(&t_new, &t1, &t2);
|
||||
cl_assert(gtransform_is_equal(&t_new, &t_c));
|
||||
|
||||
// Test pointer re-use
|
||||
gtransform_concat(&t2, &t1, &t2);
|
||||
cl_assert(gtransform_is_equal(&t2, &t_c));
|
||||
|
||||
// Test non-identity concatenation with identity
|
||||
t1 = GTransformIdentity();
|
||||
t2 = GTransformFromNumbers(1, 2, 3, 4, 5, 6);
|
||||
t_c = GTransformFromNumbers(1, 2, 3, 4, 5, 6);
|
||||
gtransform_concat(&t_new, &t1, &t2);
|
||||
cl_assert(gtransform_is_equal(&t_new, &t_c));
|
||||
|
||||
// Test pointer re-use
|
||||
gtransform_concat(&t1, &t1, &t2);
|
||||
cl_assert(gtransform_is_equal(&t1, &t_c));
|
||||
|
||||
|
||||
// Test concatenation of two non-identity matrices
|
||||
t1 = GTransformFromNumbers(3, 5, 7, 11, 13, 17);
|
||||
t2 = GTransformFromNumbers(1, 2, 3, 4, 5, 6);
|
||||
t_c = GTransformFromNumbers(18, 26, 40, 58, 69, 100);
|
||||
gtransform_concat(&t_new, &t1, &t2);
|
||||
cl_assert(gtransform_is_equal(&t_new, &t_c));
|
||||
}
|
||||
|
||||
void test_graphics_gtransform_${BIT_DEPTH_NAME}__scale(void) {
|
||||
GTransform t_new;
|
||||
GTransform t1;
|
||||
GTransform t2;
|
||||
GTransform t_c; // matrix to compare against
|
||||
|
||||
// Test scaling
|
||||
t1 = GTransformFromNumbers(1, 2, 3, 4, 5, 6);
|
||||
t2 = GTransformFromNumbers(1, 2, 3, 4, 5, 6);
|
||||
t_c = GTransformFromNumbers(10, 20, 600, 800, 5, 6);
|
||||
|
||||
gtransform_scale(&t_new, &t1, GTransformNumberFromNumber(10), GTransformNumberFromNumber(200));
|
||||
cl_assert(gtransform_is_equal(&t_new, &t_c));
|
||||
cl_assert(gtransform_is_equal(&t1, &t2)); // ensure t1 has not changed
|
||||
|
||||
gtransform_scale_number(&t_new, &t1, 10, 200);
|
||||
cl_assert(gtransform_is_equal(&t_new, &t_c));
|
||||
cl_assert(gtransform_is_equal(&t1, &t2)); // ensure t1 has not changed
|
||||
|
||||
// Test pointer re-use
|
||||
gtransform_scale(&t1, &t1, GTransformNumberFromNumber(10), GTransformNumberFromNumber(200));
|
||||
cl_assert(gtransform_is_equal(&t1, &t_c));
|
||||
}
|
||||
|
||||
void test_graphics_gtransform_${BIT_DEPTH_NAME}__translation(void) {
|
||||
GTransform t_new;
|
||||
GTransform t1;
|
||||
GTransform t2;
|
||||
GTransform t_c; // matrix to compare against
|
||||
|
||||
// Test translation
|
||||
t1 = GTransformFromNumbers(1, 2, 3, 4, 5, 6);
|
||||
t2 = GTransformFromNumbers(1, 2, 3, 4, 5, 6);
|
||||
t_c = GTransformFromNumbers(1, 2, 3, 4, 615, 826);
|
||||
|
||||
gtransform_translate(&t_new, &t1,
|
||||
GTransformNumberFromNumber(10), GTransformNumberFromNumber(200));
|
||||
cl_assert(gtransform_is_equal(&t_new, &t_c));
|
||||
cl_assert(gtransform_is_equal(&t1, &t2)); // ensure t1 has not changed
|
||||
|
||||
gtransform_translate_number(&t_new, &t1, 10, 200);
|
||||
cl_assert(gtransform_is_equal(&t_new, &t_c));
|
||||
cl_assert(gtransform_is_equal(&t1, &t2)); // ensure t1 has not changed
|
||||
|
||||
// Test pointer re-use
|
||||
gtransform_translate(&t1, &t1, GTransformNumberFromNumber(10), GTransformNumberFromNumber(200));
|
||||
cl_assert(gtransform_is_equal(&t1, &t_c));
|
||||
}
|
||||
|
||||
|
||||
void test_graphics_gtransform_${BIT_DEPTH_NAME}__rotation(void) {
|
||||
GTransform t_new;
|
||||
GTransform t1;
|
||||
GTransform t2;
|
||||
GTransform t_c; // matrix to compare against
|
||||
|
||||
// Test rotation
|
||||
t1 = GTransformFromNumbers(10, 10, 10, 10, 10, 10);
|
||||
t2 = GTransformFromNumbers(10, 10, 10, 10, 10, 10);
|
||||
// Initialize a, b, c, and d based on the expected result
|
||||
// a = b = 10*cos(45) - 10*sin(45)
|
||||
// c = d = 10*sin(45) + 10*cos(45)
|
||||
t_c = GTransform(GTransformNumberFromNumber(0),
|
||||
GTransformNumberFromNumber(0),
|
||||
(Fixed_S32_16){ .raw_value = (int32_t)(923960) },
|
||||
(Fixed_S32_16){ .raw_value = (int32_t)(923960) },
|
||||
GTransformNumberFromNumber(10),
|
||||
GTransformNumberFromNumber(10));
|
||||
|
||||
gtransform_rotate(&t_new, &t1, DEG_TO_TRIGANGLE(45));
|
||||
cl_assert(gtransform_is_equal(&t_new, &t_c));
|
||||
cl_assert(gtransform_is_equal(&t1, &t2)); // ensure t1 has not changed
|
||||
|
||||
// Test pointer re-use
|
||||
gtransform_rotate(&t1, &t1, DEG_TO_TRIGANGLE(45));
|
||||
cl_assert(gtransform_is_equal(&t1, &t_c));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/graphics_private_raw_mask.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
#include "util/graphics.h"
|
||||
|
||||
static uint8_t test_graphics_context_mask_get_value_for_coordinate(const GContext *ctx,
|
||||
const GDrawMask *mask,
|
||||
GPoint p) {
|
||||
const uint8_t mask_pixel_value = graphics_private_raw_mask_get_value(ctx, mask, p);
|
||||
|
||||
cl_assert(WITHIN(mask_pixel_value, 0, (1 << (GDRAWMASK_BITS_PER_PIXEL)) - 1));
|
||||
|
||||
return mask_pixel_value;
|
||||
}
|
||||
|
||||
static void test_graphics_context_mask_set_value_for_coordinate(GContext *ctx, GDrawMask *mask,
|
||||
uint8_t value, GPoint p) {
|
||||
cl_assert(WITHIN(value, 0, (1 << (GDRAWMASK_BITS_PER_PIXEL)) - 1));
|
||||
|
||||
graphics_private_raw_mask_set_value(ctx, mask, p, value);
|
||||
}
|
||||
|
||||
static void test_graphics_context_mask_debug(GContext *ctx, GDrawMask *mask) {
|
||||
GBitmap *framebuffer_bitmap = &ctx->dest_bitmap;
|
||||
|
||||
// Naive approach
|
||||
for (int y = 0; y < framebuffer_bitmap->bounds.size.h; y++) {
|
||||
const GBitmapDataRowInfo current_data_row_info = gbitmap_get_data_row_info(framebuffer_bitmap,
|
||||
y);
|
||||
|
||||
// Iterate over the relevant mask row pixel values and use each to draw our debug image
|
||||
for (int x = current_data_row_info.min_x; x <= current_data_row_info.max_x; x++) {
|
||||
const uint8_t mask_pixel_value =
|
||||
test_graphics_context_mask_get_value_for_coordinate(ctx, mask, GPoint(x, y));
|
||||
const GColor color_lookup[1 << GDRAWMASK_BITS_PER_PIXEL] = {
|
||||
GColorBlack,
|
||||
GColorDarkGray,
|
||||
GColorLightGray,
|
||||
GColorWhite,
|
||||
};
|
||||
const GColor pixel_color = color_lookup[mask_pixel_value];
|
||||
current_data_row_info.data[x] = pixel_color.argb;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "${BIT_DEPTH_NAME}/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_graphics_stroke_circle_${BIT_DEPTH_NAME}__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) {DISP_COLS, DISP_ROWS});
|
||||
}
|
||||
|
||||
// Teardown
|
||||
void test_graphics_stroke_circle_${BIT_DEPTH_NAME}__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
#define RADIUS_BIG 15
|
||||
#define RADIUS_MEDIUM 8
|
||||
#define RADIUS_MIN_CALCULATED 3
|
||||
#define RADIUS_MAX_PRECOMPUTED 2
|
||||
#define RADIUS_SMALL 1
|
||||
#define RADIUS_NONE 0
|
||||
#define STROKE_BIG 10
|
||||
#define STROKE_SMALL 5
|
||||
#define STROKE_THREE 3 // In case of fluctuation
|
||||
|
||||
#define ORIGIN_RECT_NO_CLIP GRect(0, 0, 40, 50)
|
||||
#define ORIGIN_RECT_CLIP_XY GRect(0, 0, 30, 40)
|
||||
#define ORIGIN_RECT_CLIP_NXNY GRect(0, 0, 30, 40)
|
||||
#define CENTER_OF_ORIGIN_RECT GPoint(20, 25)
|
||||
#define CENTER_OF_ORIGIN_RECT_NXNY GPoint(10, 15)
|
||||
|
||||
void test_graphics_stroke_circle_${BIT_DEPTH_NAME}__origin_layer(void) {
|
||||
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Big circles
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_BIG);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_origin_aa_r16_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, STROKE_BIG);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_origin_aa_r16_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, STROKE_BIG);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_origin_aa_r16_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Medium circles
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_SMALL);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_origin_aa_r8_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, STROKE_SMALL);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_origin_aa_r8_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, STROKE_SMALL);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_origin_aa_r8_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Small circles
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_SMALL);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_origin_aa_r1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_XY, ORIGIN_RECT_CLIP_XY, true, STROKE_SMALL);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_origin_aa_r1_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_CLIP_NXNY, ORIGIN_RECT_CLIP_NXNY, true, STROKE_SMALL);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_origin_aa_r1_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Testing of the special cases for radius:
|
||||
|
||||
// Radius of 3 - starting point for calculated edges
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_THREE);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_MIN_CALCULATED);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_origin_aa_r3_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Radius of 2 - ending point for precomputed edges
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_THREE);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_MAX_PRECOMPUTED);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_origin_aa_r2_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// No circle
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_THREE);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_ORIGIN_RECT_NXNY, RADIUS_NONE);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_origin_aa_r0_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
#endif // SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
}
|
||||
|
||||
#define OFFSET_RECT_NO_CLIP GRect(10, 10, 40, 50)
|
||||
#define OFFSET_RECT_CLIP_XY GRect(10, 10, 30, 40)
|
||||
#define OFFSET_RECT_CLIP_NXNY GRect(0, 0, 30, 40)
|
||||
#define CENTER_OF_OFFSET_RECT GPoint(10, 15)
|
||||
#define CENTER_OF_OFFSET_RECT_NXNY GPoint(0, 5)
|
||||
|
||||
void test_graphics_stroke_circle_${BIT_DEPTH_NAME}__offset_layer_aa(void) {
|
||||
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
// Big circles
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, STROKE_BIG);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r16_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, STROKE_BIG);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r16_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, STROKE_BIG);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT_NXNY, RADIUS_BIG);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r16_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Medium circles
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, STROKE_SMALL);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r8_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, STROKE_SMALL);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r8_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, STROKE_SMALL);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT_NXNY, RADIUS_MEDIUM);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r8_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
// Small circles
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_NO_CLIP, OFFSET_RECT_NO_CLIP, true, STROKE_SMALL);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r1_no_clip.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_XY, OFFSET_RECT_CLIP_XY, true, STROKE_SMALL);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r1_clip_xy.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, OFFSET_RECT_CLIP_NXNY, OFFSET_RECT_CLIP_NXNY, true, STROKE_SMALL);
|
||||
graphics_draw_circle(&ctx, CENTER_OF_OFFSET_RECT_NXNY, RADIUS_SMALL);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r1_clip_nxny.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
#endif // SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
}
|
||||
|
||||
extern void graphics_circle_quadrant_draw_stroked_non_aa(
|
||||
GContext* ctx, GPoint p, uint16_t radius, uint8_t stroke_width,
|
||||
GCornerMask quadrant);
|
||||
|
||||
void test_graphics_stroke_circle_${BIT_DEPTH_NAME}__quadrants(void) {
|
||||
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornerTopLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_r8_quad_top_left.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornerTopRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_r8_quad_top_right.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornerBottomLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_r8_quad_bottom_left.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornerBottomRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_r8_quad_bottom_right.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornersTop);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_r8_quads_top.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornersBottom);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_r8_quads_bottom.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornersRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_r8_quads_right.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, false, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_non_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornersLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_r8_quads_left.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
#endif // SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
}
|
||||
|
||||
extern void graphics_circle_quadrant_draw_stroked_aa(
|
||||
GContext* ctx, GPoint p, uint16_t radius, uint8_t stroke_width,
|
||||
GCornerMask quadrant);
|
||||
|
||||
void test_graphics_draw_circle_${BIT_DEPTH_NAME}__quadrants_aa(void) {
|
||||
|
||||
#if SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
GContext ctx;
|
||||
test_graphics_context_init(&ctx, fb);
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornerTopLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r8_quad_top_left.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornerTopRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r8_quad_top_right.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornerBottomLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r8_quad_bottom_left.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornerBottomRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r8_quad_bottom_right.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornersTop);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r8_quads_top.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornersBottom);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r8_quads_bottom.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornersRight);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r8_quads_right.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
setup_test_aa_sw(&ctx, fb, ORIGIN_RECT_NO_CLIP, ORIGIN_RECT_NO_CLIP, true, STROKE_SMALL);
|
||||
graphics_circle_quadrant_draw_stroked_aa(&ctx, CENTER_OF_ORIGIN_RECT, RADIUS_MEDIUM, STROKE_SMALL,GCornersLeft);
|
||||
cl_check(gbitmap_pbi_eq(&ctx.dest_bitmap, "stroke_circle_offset_aa_r8_quads_left.${BIT_DEPTH_NAME}.pbi"));
|
||||
|
||||
#endif // SCREEN_COLOR_DEPTH_BITS == 8
|
||||
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* 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 "apps/system_apps/timeline/text_node.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "pebble_asserts.h"
|
||||
|
||||
// Stubs
|
||||
/////////////////////
|
||||
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_process_manager.h"
|
||||
|
||||
// TODO: PBL-22271 Complete timeline text node unit tests
|
||||
|
||||
// Fakes
|
||||
/////////////////////
|
||||
|
||||
typedef struct GTextNodeTestData {
|
||||
GContext gcontext;
|
||||
GRect clip_box;
|
||||
GTextNode *text_node;
|
||||
GSize max_used_size;
|
||||
} GTextNodeTestData;
|
||||
|
||||
GTextNodeTestData s_data;
|
||||
|
||||
#define TEST_TEXT "DUMMY TEXT"
|
||||
#define TEST_FONT ((void *)0xf0a7f0a7)
|
||||
#define TEST_TEXT_SIZE GSize(50, 18)
|
||||
#define TEST_TEXT_BOX GRect(10, 10, 140, 200)
|
||||
|
||||
void graphics_draw_text(GContext *ctx, const char *text, GFont const font, const GRect box,
|
||||
const GTextOverflowMode overflow_mode, const GTextAlignment alignment,
|
||||
const GTextLayoutCacheRef layout_ref) {
|
||||
GTextNodeText *text_node = (GTextNodeText *)s_data.text_node;
|
||||
cl_assert(text_node);
|
||||
cl_assert_equal_s(text_node->text, text);
|
||||
cl_assert_equal_p(text_node->font, font);
|
||||
cl_assert_equal_p(text_node->overflow, overflow_mode);
|
||||
cl_assert_equal_p(text_node->alignment, alignment);
|
||||
|
||||
TextLayoutExtended *layout = (TextLayoutExtended *)layout_ref;
|
||||
cl_assert_equal_p(text_node->line_spacing_delta, layout->line_spacing_delta);
|
||||
|
||||
layout->max_used_size = s_data.max_used_size;
|
||||
}
|
||||
|
||||
GSize graphics_text_layout_get_max_used_size(GContext *ctx, const char *text,
|
||||
GFont const font, const GRect box,
|
||||
const GTextOverflowMode overflow_mode,
|
||||
const GTextAlignment alignment,
|
||||
GTextLayoutCacheRef layout_ref) {
|
||||
GTextNodeText *text_node = (GTextNodeText *)s_data.text_node;
|
||||
cl_assert(text_node);
|
||||
cl_assert_equal_s(text_node->text, text);
|
||||
cl_assert_equal_p(text_node->font, font);
|
||||
cl_assert_equal_p(text_node->overflow, overflow_mode);
|
||||
cl_assert_equal_p(text_node->alignment, alignment);
|
||||
|
||||
TextLayoutExtended *layout = (TextLayoutExtended *)layout_ref;
|
||||
cl_assert_equal_p(text_node->line_spacing_delta, layout->line_spacing_delta);
|
||||
|
||||
layout->max_used_size = s_data.max_used_size;
|
||||
return layout->max_used_size;
|
||||
}
|
||||
|
||||
// Setup and Teardown
|
||||
////////////////////////////////////
|
||||
|
||||
void test_graphics_text_node__initialize(void) {
|
||||
s_data = (GTextNodeTestData) {};
|
||||
}
|
||||
|
||||
void test_graphics_text_node__cleanup(void) {
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
void test_graphics_text_node__text_draw(void) {
|
||||
GContext *ctx = &s_data.gcontext;
|
||||
GTextNodeText text_node = {};
|
||||
text_node.text = (char *)TEST_TEXT;
|
||||
text_node.font = TEST_FONT;
|
||||
text_node.overflow = GTextOverflowModeTrailingEllipsis;
|
||||
text_node.alignment = GTextAlignmentCenter;
|
||||
|
||||
s_data.text_node = &text_node.node;
|
||||
s_data.max_used_size = TEST_TEXT_SIZE;
|
||||
|
||||
GSize size;
|
||||
graphics_text_node_draw(&text_node.node, ctx, &TEST_TEXT_BOX, NULL, &size);
|
||||
cl_assert(size.w > 0);
|
||||
cl_assert(size.h > 0);
|
||||
cl_assert_equal_i(size.w, TEST_TEXT_SIZE.w);
|
||||
cl_assert_equal_i(size.h, TEST_TEXT_SIZE.h);
|
||||
}
|
||||
|
||||
void test_graphics_text_node__text_size(void) {
|
||||
GContext *ctx = &s_data.gcontext;
|
||||
GTextNodeText text_node = {};
|
||||
text_node.text = (char *)TEST_TEXT;
|
||||
text_node.font = TEST_FONT;
|
||||
text_node.overflow = GTextOverflowModeTrailingEllipsis;
|
||||
text_node.alignment = GTextAlignmentCenter;
|
||||
|
||||
s_data.text_node = &text_node.node;
|
||||
s_data.max_used_size = TEST_TEXT_SIZE;
|
||||
|
||||
GSize size;
|
||||
graphics_text_node_get_size(&text_node.node, ctx, &TEST_TEXT_BOX, NULL, &size);
|
||||
cl_assert(size.w > 0);
|
||||
cl_assert(size.h > 0);
|
||||
cl_assert_equal_i(size.w, TEST_TEXT_SIZE.w);
|
||||
cl_assert_equal_i(size.h, TEST_TEXT_SIZE.h);
|
||||
}
|
||||
|
||||
static int s_num_draw_custom_calls = 0;
|
||||
|
||||
static void prv_draw_custom(GContext *ctx, const GRect *box, const GTextNodeDrawConfig *config,
|
||||
bool render, GSize *size_out, void *user_data) {
|
||||
s_num_draw_custom_calls++;
|
||||
*size_out = TEST_TEXT_SIZE;
|
||||
}
|
||||
|
||||
void test_graphics_text_node__custom_cached_size(void) {
|
||||
GContext *ctx = &s_data.gcontext;
|
||||
GTextNodeCustom custom_node = {};
|
||||
custom_node.node.type = GTextNodeType_Custom;
|
||||
custom_node.callback = prv_draw_custom;
|
||||
|
||||
GSize size;
|
||||
graphics_text_node_get_size(&custom_node.node, ctx, &TEST_TEXT_BOX, NULL, &size);
|
||||
cl_assert(size.w > 0);
|
||||
cl_assert(size.h > 0);
|
||||
cl_assert_equal_i(size.w, TEST_TEXT_SIZE.w);
|
||||
cl_assert_equal_i(size.h, TEST_TEXT_SIZE.h);
|
||||
cl_assert_equal_i(size.w, custom_node.node.cached_size.w);
|
||||
cl_assert_equal_i(size.h, custom_node.node.cached_size.h);
|
||||
cl_assert_equal_i(s_num_draw_custom_calls, 1);
|
||||
|
||||
graphics_text_node_get_size(&custom_node.node, ctx, &TEST_TEXT_BOX, NULL, &size);
|
||||
cl_assert(size.w > 0);
|
||||
cl_assert(size.h > 0);
|
||||
cl_assert_equal_i(size.w, TEST_TEXT_SIZE.w);
|
||||
cl_assert_equal_i(size.h, TEST_TEXT_SIZE.h);
|
||||
cl_assert_equal_i(size.w, custom_node.node.cached_size.w);
|
||||
cl_assert_equal_i(size.h, custom_node.node.cached_size.h);
|
||||
cl_assert_equal_i(s_num_draw_custom_calls, 1);
|
||||
}
|
||||
|
||||
void test_graphics_text_node__create_container_nodes_buffer(void) {
|
||||
GTextNodeHorizontal *h_empty = graphics_text_node_create_horizontal(0);
|
||||
cl_assert_equal_i(h_empty->container.max_nodes, 0);
|
||||
cl_assert_equal_p(NULL, h_empty->container.nodes);
|
||||
|
||||
GTextNodeHorizontal *h_nodes = graphics_text_node_create_horizontal(3);
|
||||
cl_assert_equal_i(h_nodes->container.max_nodes, 3);
|
||||
cl_assert_equal_p(h_nodes + 1, (void *)h_nodes->container.nodes);
|
||||
|
||||
GTextNodeVertical *v_empty = graphics_text_node_create_vertical(0);
|
||||
cl_assert_equal_i(v_empty->container.max_nodes, 0);
|
||||
cl_assert_equal_p(NULL, v_empty->container.nodes);
|
||||
|
||||
GTextNodeVertical *v_nodes = graphics_text_node_create_vertical(3);
|
||||
cl_assert_equal_i(v_nodes->container.max_nodes, 3);
|
||||
cl_assert_equal_p(v_nodes + 1, (void *)v_nodes->container.nodes);
|
||||
}
|
||||
|
||||
void test_graphics_text_node__destroy(void) {
|
||||
const char *str_a = "A";
|
||||
GTextNodeText *text_a = graphics_text_node_create_text(strlen(str_a) + 1);
|
||||
cl_assert(text_a->node.free_on_destroy);
|
||||
strcpy((char *)text_a->text, str_a);
|
||||
|
||||
const char *str_b = task_strdup("B");
|
||||
GTextNodeText *text_b = graphics_text_node_create_text(0);
|
||||
cl_assert(text_b->node.free_on_destroy);
|
||||
text_b->text = (char *)str_b;
|
||||
|
||||
const char *str_c = "C";
|
||||
GTextNodeText text_c = {
|
||||
.text = (char *)str_c,
|
||||
};
|
||||
cl_assert(!text_c.node.free_on_destroy);
|
||||
|
||||
GTextNodeCustom *custom_a = graphics_text_node_create_custom(NULL, NULL);
|
||||
cl_assert(custom_a->node.free_on_destroy);
|
||||
|
||||
GTextNodeHorizontal *horizontal_a = graphics_text_node_create_horizontal(3);
|
||||
cl_assert(horizontal_a->container.node.free_on_destroy);
|
||||
cl_assert_equal_i(horizontal_a->container.max_nodes, 3);
|
||||
cl_assert_equal_i(horizontal_a->container.num_nodes, 0);
|
||||
cl_assert(graphics_text_node_container_add_child(&horizontal_a->container, &text_a->node));
|
||||
cl_assert(graphics_text_node_container_add_child(&horizontal_a->container, &text_b->node));
|
||||
cl_assert(graphics_text_node_container_add_child(&horizontal_a->container, &text_c.node));
|
||||
cl_assert(!graphics_text_node_container_add_child(&horizontal_a->container, &custom_a->node));
|
||||
cl_assert_equal_i(horizontal_a->container.num_nodes, 3);
|
||||
|
||||
GTextNodeVertical *vertical_a = graphics_text_node_create_vertical(2);
|
||||
cl_assert(vertical_a->container.node.free_on_destroy);
|
||||
cl_assert_equal_i(vertical_a->container.max_nodes, 2);
|
||||
cl_assert_equal_i(vertical_a->container.num_nodes, 0);
|
||||
cl_assert(graphics_text_node_container_add_child(&vertical_a->container, &horizontal_a->container.node));
|
||||
cl_assert(graphics_text_node_container_add_child(&vertical_a->container, &custom_a->node));
|
||||
cl_assert(!graphics_text_node_container_add_child(&vertical_a->container, &text_c.node));
|
||||
cl_assert_equal_i(vertical_a->container.num_nodes, 2);
|
||||
|
||||
graphics_text_node_destroy(&vertical_a->container.node);
|
||||
|
||||
task_free((char *)str_b);
|
||||
}
|
||||
|
||||
static void prv_draw_custom_clip(GContext *ctx, const GRect *box,
|
||||
const GTextNodeDrawConfig *config, bool render, GSize *size_out,
|
||||
void *user_data) {
|
||||
cl_assert_equal_grect(ctx->draw_state.clip_box, s_data.clip_box);
|
||||
}
|
||||
|
||||
#define TEST_CLIP_BOX GRect(10, 20, 30, 40)
|
||||
|
||||
void test_graphics_text_node__clip(void) {
|
||||
GContext *ctx = &s_data.gcontext;
|
||||
GTextNodeCustom custom_node = {};
|
||||
custom_node.node.type = GTextNodeType_Custom;
|
||||
custom_node.callback = prv_draw_custom_clip;
|
||||
|
||||
// Clipping off
|
||||
s_data.gcontext.draw_state.clip_box = DISP_FRAME;
|
||||
s_data.clip_box = DISP_FRAME;
|
||||
graphics_text_node_draw(&custom_node.node, ctx, &TEST_CLIP_BOX, NULL, NULL);
|
||||
cl_assert_equal_grect(ctx->draw_state.clip_box, DISP_FRAME);
|
||||
|
||||
// Clipping on
|
||||
custom_node.node.clip = true;
|
||||
s_data.gcontext.draw_state.clip_box = DISP_FRAME;
|
||||
s_data.clip_box = TEST_CLIP_BOX;
|
||||
graphics_text_node_draw(&custom_node.node, ctx, &TEST_CLIP_BOX, NULL, NULL);
|
||||
cl_assert_equal_grect(ctx->draw_state.clip_box, DISP_FRAME);
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/graphics_private.h"
|
||||
#include "applib/graphics/graphics_private_raw.h"
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "applib/ui/window_stack_animation_round.h"
|
||||
#include "applib/ui/layer.h"
|
||||
#include "board/displays/display_spalding.h"
|
||||
#include "services/common/compositor/compositor_transitions.h"
|
||||
#include "util/trig.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper Functions
|
||||
////////////////////////////////////
|
||||
#include "test_graphics.h"
|
||||
#include "8bit/test_framebuffer.h"
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "graphics_common_stubs.h"
|
||||
#include "stubs_applib_resource.h"
|
||||
|
||||
void window_transition_context_appearance_call_all(WindowTransitioningContext *ctx) {}
|
||||
|
||||
void window_render(Window *window, GContext *ctx) {}
|
||||
|
||||
bool compositor_transition_app_to_app_should_be_skipped(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AnimationPrivate *animation_private_animation_find(Animation *handle) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AnimationProgress animation_private_get_animation_progress(const AnimationPrivate *animation) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const AnimationImplementation* animation_get_implementation(Animation *animation_h) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const GDrawRawImplementation g_compositor_transitions_app_fb_draw_implementation = {0};
|
||||
|
||||
void compositor_port_hole_transition_draw_outer_ring(GContext *ctx, int16_t pixels,
|
||||
GColor ring_color) {
|
||||
}
|
||||
|
||||
// Setup and Teardown
|
||||
////////////////////////////////////
|
||||
|
||||
static FrameBuffer *fb = NULL;
|
||||
|
||||
// Setup
|
||||
void test_graphics_window_stack_animation__initialize(void) {
|
||||
fb = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(fb, &(GSize) { DISP_COLS, DISP_ROWS });
|
||||
}
|
||||
|
||||
void test_graphics_window_stack_animation__cleanup(void) {
|
||||
free(fb);
|
||||
}
|
||||
|
||||
// Helpers
|
||||
////////////////////////////////////
|
||||
|
||||
typedef void (*ClippingMaskDrawFunc)(GContext *ctx);
|
||||
|
||||
static void prv_test_clipping_mask(ClippingMaskDrawFunc draw_func, const char *expected_image) {
|
||||
GContext *ctx = malloc(sizeof(GContext));
|
||||
test_graphics_context_init(ctx, fb);
|
||||
framebuffer_clear(fb);
|
||||
|
||||
graphics_context_set_antialiased(ctx, true);
|
||||
|
||||
// Start by filling the framebuffer with green pixels to make things easier to see
|
||||
memset(ctx->dest_bitmap.addr, GColorGreenARGB8, FRAMEBUFFER_SIZE_BYTES);
|
||||
|
||||
const bool transparent = true;
|
||||
GDrawMask *mask = graphics_context_mask_create(ctx, transparent);
|
||||
cl_assert(mask);
|
||||
|
||||
cl_assert(graphics_context_mask_record(ctx, mask));
|
||||
|
||||
draw_func(ctx);
|
||||
|
||||
cl_assert(graphics_context_mask_record(ctx, NULL));
|
||||
cl_assert_equal_p(ctx->draw_state.draw_implementation, &g_default_draw_implementation);
|
||||
|
||||
cl_assert(graphics_context_mask_use(ctx, mask));
|
||||
cl_assert_equal_p(ctx->draw_state.draw_mask, mask);
|
||||
|
||||
graphics_context_set_fill_color(ctx, GColorRed);
|
||||
graphics_fill_rect(ctx, &ctx->dest_bitmap.bounds);
|
||||
|
||||
cl_assert(graphics_context_mask_use(ctx, NULL));
|
||||
cl_assert_equal_p(ctx->draw_state.draw_mask, NULL);
|
||||
|
||||
cl_check(gbitmap_pbi_eq(&ctx->dest_bitmap, TEST_NAMED_PBI_FILE(expected_image)));
|
||||
|
||||
graphics_context_mask_destroy(ctx, mask);
|
||||
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
// NOTE: All of the following tests first fill the framebuffer with green to make it easier to see
|
||||
// incorrect pixels
|
||||
|
||||
// This test records a clipping mask of the first frame of the left "round flip" compositor
|
||||
// transition animation and then clips a full-screen red rectangle to the resulting mask
|
||||
|
||||
static void prv_left_flip_first_frame_clipping(GContext *ctx) {
|
||||
compositor_round_flip_transitions_flip_animation_update(ctx, 0,
|
||||
CompositorTransitionDirectionLeft,
|
||||
GColorWhite);
|
||||
}
|
||||
|
||||
void test_graphics_window_stack_animation__left_flip_first_frame_clipping(void) {
|
||||
prv_test_clipping_mask(prv_left_flip_first_frame_clipping, "left_flip_first_frame_clipping");
|
||||
};
|
||||
|
||||
// This test records a clipping mask of the 1/4 progress frame of the left "round flip" compositor
|
||||
// transition animation and then clips a full-screen red rectangle to the resulting mask
|
||||
|
||||
static void prv_left_flip_first_quarter_frame_clipping(GContext *ctx) {
|
||||
compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX / 4,
|
||||
CompositorTransitionDirectionLeft,
|
||||
GColorWhite);
|
||||
}
|
||||
|
||||
void test_graphics_window_stack_animation__left_flip_first_quarter_frame_clipping(void) {
|
||||
prv_test_clipping_mask(prv_left_flip_first_quarter_frame_clipping,
|
||||
"left_flip_first_quarter_frame_clipping");
|
||||
};
|
||||
|
||||
// This test records a clipping mask of the half progress frame of the left "round flip" compositor
|
||||
// transition animation and then clips a full-screen red rectangle to the resulting mask
|
||||
|
||||
static void prv_left_flip_half_frame_clipping(GContext *ctx) {
|
||||
compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX / 2,
|
||||
CompositorTransitionDirectionLeft,
|
||||
GColorWhite);
|
||||
}
|
||||
|
||||
void test_graphics_window_stack_animation__left_flip_half_frame_clipping(void) {
|
||||
prv_test_clipping_mask(prv_left_flip_half_frame_clipping, "left_flip_half_frame_clipping");
|
||||
};
|
||||
|
||||
// This test records a clipping mask of the 3/4 progress frame of the left "round flip" compositor
|
||||
// transition animation and then clips a full-screen red rectangle to the resulting mask
|
||||
|
||||
static void prv_left_flip_third_quarter_frame_clipping(GContext *ctx) {
|
||||
compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX * 3 / 4,
|
||||
CompositorTransitionDirectionLeft,
|
||||
GColorWhite);
|
||||
}
|
||||
|
||||
void test_graphics_window_stack_animation__left_flip_third_quarter_frame_clipping(void) {
|
||||
prv_test_clipping_mask(prv_left_flip_third_quarter_frame_clipping,
|
||||
"left_flip_third_quarter_frame_clipping");
|
||||
};
|
||||
|
||||
// This test records a clipping mask of the last frame of the left "round flip" compositor
|
||||
// transition animation and then clips a full-screen red rectangle to the resulting mask
|
||||
|
||||
static void prv_left_flip_last_frame_clipping(GContext *ctx) {
|
||||
compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX,
|
||||
CompositorTransitionDirectionLeft,
|
||||
GColorWhite);
|
||||
}
|
||||
|
||||
void test_graphics_window_stack_animation__left_flip_last_frame_clipping(void) {
|
||||
prv_test_clipping_mask(prv_left_flip_last_frame_clipping, "left_flip_last_frame_clipping");
|
||||
};
|
||||
|
||||
// This test records a clipping mask of the first frame of the right "round flip" compositor
|
||||
// transition animation and then clips a full-screen red rectangle to the resulting mask
|
||||
|
||||
static void prv_right_flip_first_frame_clipping(GContext *ctx) {
|
||||
// The first frame is ANIMATION_NORMALIZED_MAX because the right flip animation is actually
|
||||
// played backwards
|
||||
compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX,
|
||||
CompositorTransitionDirectionRight,
|
||||
GColorWhite);
|
||||
}
|
||||
|
||||
void test_graphics_window_stack_animation__right_flip_first_frame_clipping(void) {
|
||||
prv_test_clipping_mask(prv_right_flip_first_frame_clipping, "right_flip_first_frame_clipping");
|
||||
};
|
||||
|
||||
// This test records a clipping mask of the 1/4 progress frame of the right "round flip" compositor
|
||||
// transition animation and then clips a full-screen red rectangle to the resulting mask
|
||||
|
||||
static void prv_right_flip_first_quarter_frame_clipping(GContext *ctx) {
|
||||
// The 1/4 progress frame is ANIMATION_NORMALIZED_MAX * 3/4 because the right flip animation is
|
||||
// actually played backwards
|
||||
compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX * 3 / 4,
|
||||
CompositorTransitionDirectionRight,
|
||||
GColorWhite);
|
||||
}
|
||||
|
||||
void test_graphics_window_stack_animation__right_flip_first_quarter_frame_clipping(void) {
|
||||
prv_test_clipping_mask(prv_right_flip_first_quarter_frame_clipping,
|
||||
"right_flip_first_quarter_frame_clipping");
|
||||
};
|
||||
|
||||
// This test records a clipping mask of the half progress frame of the right "round flip" compositor
|
||||
// transition animation and then clips a full-screen red rectangle to the resulting mask
|
||||
|
||||
static void prv_right_flip_half_frame_clipping(GContext *ctx) {
|
||||
compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX / 2,
|
||||
CompositorTransitionDirectionRight,
|
||||
GColorWhite);
|
||||
}
|
||||
|
||||
void test_graphics_window_stack_animation__right_flip_half_frame_clipping(void) {
|
||||
prv_test_clipping_mask(prv_right_flip_half_frame_clipping, "right_flip_half_frame_clipping");
|
||||
};
|
||||
|
||||
// This test records a clipping mask of the 3/4 progress frame of the right "round flip" compositor
|
||||
// transition animation and then clips a full-screen red rectangle to the resulting mask
|
||||
|
||||
static void prv_right_flip_third_quarter_frame_clipping(GContext *ctx) {
|
||||
// The 3/4 progress frame is ANIMATION_NORMALIZED_MAX * 1/4 because the right flip animation is
|
||||
// actually played backwards
|
||||
compositor_round_flip_transitions_flip_animation_update(ctx, ANIMATION_NORMALIZED_MAX / 4,
|
||||
CompositorTransitionDirectionRight,
|
||||
GColorWhite);
|
||||
}
|
||||
|
||||
void test_graphics_window_stack_animation__right_flip_third_quarter_frame_clipping(void) {
|
||||
prv_test_clipping_mask(prv_right_flip_third_quarter_frame_clipping,
|
||||
"right_flip_third_quarter_frame_clipping");
|
||||
};
|
||||
|
||||
// This test records a clipping mask of the last frame of the right "round flip" compositor
|
||||
// transition animation and then clips a full-screen red rectangle to the resulting mask
|
||||
|
||||
static void prv_right_flip_last_frame_clipping(GContext *ctx) {
|
||||
// The last frame is 0 because the right flip animation is actually played backwards
|
||||
compositor_round_flip_transitions_flip_animation_update(ctx, 0,
|
||||
CompositorTransitionDirectionRight,
|
||||
GColorWhite);
|
||||
}
|
||||
|
||||
void test_graphics_window_stack_animation__right_flip_last_frame_clipping(void) {
|
||||
prv_test_clipping_mask(prv_right_flip_last_frame_clipping, "right_flip_last_frame_clipping");
|
||||
};
|
||||
|
||||
void test_graphics_window_stack_animation__move_pixels_horizontally(void) {
|
||||
GContext *ctx = malloc(sizeof(GContext));
|
||||
test_graphics_context_init(ctx, fb);
|
||||
framebuffer_clear(fb);
|
||||
|
||||
GBitmap *const bitmap = &ctx->dest_bitmap;
|
||||
|
||||
// vertical test pattern
|
||||
for (int16_t y = 0; y < DISP_ROWS; y++) {
|
||||
GBitmapDataRowInfo row_info = prv_gbitmap_get_data_row_info(bitmap, y);
|
||||
for (int x = 0; x < DISP_COLS; x++) {
|
||||
GColor8 color = GColorFromRGB(1 * x * UINT8_MAX / DISP_COLS,
|
||||
2 * x * UINT8_MAX / DISP_COLS,
|
||||
4 * x * UINT8_MAX / DISP_COLS);
|
||||
if (row_info.min_x <= x && x <= row_info.max_x) {
|
||||
row_info.data[x] = color.argb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// nop
|
||||
graphics_private_move_pixels_horizontally(NULL, 50, false);
|
||||
graphics_private_move_pixels_horizontally(bitmap, 0, false);
|
||||
|
||||
graphics_private_move_pixels_horizontally(bitmap, 50, false);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, TEST_NAMED_PBI_FILE("move_horizontal_right")));
|
||||
|
||||
graphics_private_move_pixels_horizontally(bitmap, -100, false);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, TEST_NAMED_PBI_FILE("move_horizontal_left")));
|
||||
|
||||
graphics_private_move_pixels_horizontally(bitmap, 400, false);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, TEST_NAMED_PBI_FILE("move_horizontal_right_too_far")));
|
||||
|
||||
graphics_private_move_pixels_horizontally(bitmap, -400, true);
|
||||
cl_check(gbitmap_pbi_eq(bitmap, TEST_NAMED_PBI_FILE("move_horizontal_left_filled")));
|
||||
|
||||
free(ctx);
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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 "applib/graphics/gtypes.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "pebble_asserts.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// stubs
|
||||
#include "stubs_process_manager.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_heap.h"
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
void test_gtypes__gpoint_scale_by_gsize(void) {
|
||||
GSize from = GSize(10, 20);
|
||||
GSize to = GSize(20, 40);
|
||||
GPoint point = GPoint(10, 10);
|
||||
GPoint result = GPointZero;
|
||||
|
||||
result = gpoint_scale_by_gsize(point, from, to);
|
||||
cl_assert_equal_i(result.x, 20);
|
||||
cl_assert_equal_i(result.y, 20);
|
||||
}
|
||||
|
||||
void test_gtypes__gpoint_scale_by_gsize_from_zero(void) {
|
||||
GSize from = GSizeZero;
|
||||
GSize to = GSize(20, 40);
|
||||
GPoint point = GPoint(10, 10);
|
||||
GPoint result = GPointZero;
|
||||
|
||||
result = gpoint_scale_by_gsize(point, from, to);
|
||||
cl_assert_equal_i(result.x, 0);
|
||||
cl_assert_equal_i(result.y, 0);
|
||||
}
|
||||
|
||||
void test_gtypes__gpoint_scale_by_gsize_to_zero(void) {
|
||||
GSize from = GSize(10, 20);
|
||||
GSize to = GSizeZero;
|
||||
GPoint point = GPoint(10, 10);
|
||||
GPoint result = GPointZero;
|
||||
|
||||
result = gpoint_scale_by_gsize(point, from, to);
|
||||
cl_assert_equal_i(result.x, 0);
|
||||
cl_assert_equal_i(result.y, 0);
|
||||
}
|
||||
|
||||
#define cl_assert_equal_insets(i1, i2) \
|
||||
do { \
|
||||
cl_assert_equal_i(i1.top, i2.top); \
|
||||
cl_assert_equal_i(i1.right, i2.right); \
|
||||
cl_assert_equal_i(i1.bottom, i2.bottom); \
|
||||
cl_assert_equal_i(i1.left, i2.left); \
|
||||
} while(0)
|
||||
|
||||
void test_gtypes__gedge_insets(void) {
|
||||
GEdgeInsets g4 = (GEdgeInsets){.top = 1, .right = 2, .bottom = 3, .left = 4};
|
||||
cl_assert_equal_insets(GEdgeInsets(1, 2, 3, 4), g4);
|
||||
|
||||
GEdgeInsets g3 = (GEdgeInsets){.top = 1, .right = 2, .bottom = 3, .left = 2};
|
||||
cl_assert_equal_insets(GEdgeInsets(1, 2, 3), g3);
|
||||
|
||||
GEdgeInsets g2 = (GEdgeInsets){.top = 1, .right = 2, .bottom = 1, .left = 2};
|
||||
cl_assert_equal_insets(GEdgeInsets(1, 2), g2);
|
||||
|
||||
GEdgeInsets g1 = (GEdgeInsets){.top = 1, .right = 1, .bottom = 1, .left = 1};
|
||||
cl_assert_equal_insets(GEdgeInsets(1), g1);
|
||||
}
|
||||
|
||||
void test_gtypes__grect_longest_side(void) {
|
||||
cl_assert_equal_i(0, grect_longest_side(GRectZero));
|
||||
cl_assert_equal_i(20, grect_longest_side(GRect(0, 0, 10, 20)));
|
||||
cl_assert_equal_i(20, grect_longest_side(GRect(0, 0, 20, 10)));
|
||||
cl_assert_equal_i(20, grect_longest_side(GRect(0, 0, 10, -20)));
|
||||
cl_assert_equal_i(20, grect_longest_side(GRect(0, 0, -20, 10)));
|
||||
}
|
||||
|
||||
void test_gtypes__grect_shortest_side(void) {
|
||||
cl_assert_equal_i(0, grect_shortest_side(GRectZero));
|
||||
cl_assert_equal_i(10, grect_shortest_side(GRect(0, 0, 10, 20)));
|
||||
cl_assert_equal_i(10, grect_shortest_side(GRect(0, 0, 20, 10)));
|
||||
cl_assert_equal_i(10, grect_shortest_side(GRect(0, 0, 10, -20)));
|
||||
cl_assert_equal_i(10, grect_shortest_side(GRect(0, 0, -20, 10)));
|
||||
}
|
||||
|
||||
void test_gtypes__grect_inset(void) {
|
||||
GRect rect = GRect(10, 20, 30, 40);
|
||||
cl_assert_equal_grect(GRect(12, 23, 26, 34), grect_inset_internal(rect, 2, 3));
|
||||
cl_assert_equal_grect(GRect(7, 18, 36, 44), grect_inset_internal(rect, -3, -2));
|
||||
}
|
||||
|
||||
void test_gtypes__grect_inset_standardizes(void) {
|
||||
GRect rect = GRect(100, 100, -30, -40);
|
||||
cl_assert_equal_grect(GRect(70, 60, 30, 40), grect_inset_internal(rect, 0, 0));
|
||||
cl_assert_equal_grect(GRect(72, 63, 26, 34), grect_inset_internal(rect, 2, 3));
|
||||
}
|
||||
|
||||
void test_gtypes__grect_inset_returns_zero_rect_for_large_insets(void) {
|
||||
GRect rect = GRect(10, 20, 30, 40);
|
||||
cl_assert_equal_grect(GRect(25, 20, 0, 40), grect_inset_internal(rect, 15, 0));
|
||||
cl_assert_equal_grect(GRectZero, grect_inset_internal(rect, 16, 0));
|
||||
|
||||
cl_assert_equal_grect(GRect(10, 40, 30, 0), grect_inset_internal(rect, 0, 20));
|
||||
cl_assert_equal_grect(GRectZero, grect_inset_internal(rect, 0, 21));
|
||||
|
||||
cl_assert_equal_grect(GRectZero, grect_inset_internal(rect, 16, 21));
|
||||
}
|
||||
|
||||
void test_gtypes__grect_crop_asserts_for_large_insets(void) {
|
||||
GRect rect = GRect(10, 20, 30, 40);
|
||||
cl_assert_equal_grect(GRect(25, 35, 0, 10), grect_crop(rect, 15));
|
||||
cl_assert_passert(grect_crop(rect, 16));
|
||||
}
|
||||
|
||||
void test_gtypes__pbl_if_rect_else(void) {
|
||||
#if defined(PLATFORM_TINTIN)
|
||||
cl_assert_equal_i(1, PBL_IF_RECT_ELSE(1,2));
|
||||
#elif defined(PLATFORM_SNOWY)
|
||||
cl_assert_equal_i(1, PBL_IF_RECT_ELSE(1,2));
|
||||
#elif defined(PLATFORM_SPALDING)
|
||||
cl_assert_equal_i(2, PBL_IF_RECT_ELSE(1,2));
|
||||
#else
|
||||
#error "unknown platform"
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_gtypes__pbl_if_round_else(void) {
|
||||
#if defined(PLATFORM_TINTIN)
|
||||
cl_assert_equal_i(2, PBL_IF_ROUND_ELSE(1,2));
|
||||
#elif defined(PLATFORM_SNOWY)
|
||||
cl_assert_equal_i(2, PBL_IF_ROUND_ELSE(1,2));
|
||||
#elif defined(PLATFORM_SPALDING)
|
||||
cl_assert_equal_i(1, PBL_IF_ROUND_ELSE(1,2));
|
||||
#else
|
||||
#error "unknown platform"
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_gtypes__pbl_if_bw_else(void) {
|
||||
#if defined(PLATFORM_TINTIN)
|
||||
cl_assert_equal_i(1, PBL_IF_BW_ELSE(1,2));
|
||||
#elif defined(PLATFORM_SNOWY)
|
||||
cl_assert_equal_i(2, PBL_IF_BW_ELSE(1,2));
|
||||
#elif defined(PLATFORM_SPALDING)
|
||||
cl_assert_equal_i(2, PBL_IF_BW_ELSE(1,2));
|
||||
#else
|
||||
#error "unknown platform"
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_gtypes__pbl_if_color_else(void) {
|
||||
#if defined(PLATFORM_TINTIN)
|
||||
cl_assert_equal_i(2, PBL_IF_COLOR_ELSE(1,2));
|
||||
#elif defined(PLATFORM_SNOWY)
|
||||
cl_assert_equal_i(1, PBL_IF_COLOR_ELSE(1,2));
|
||||
#elif defined(PLATFORM_SPALDING)
|
||||
cl_assert_equal_i(1, PBL_IF_COLOR_ELSE(1,2));
|
||||
#else
|
||||
#error "unknown platform"
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_gtypes__color_fallback(void) {
|
||||
#if defined(PLATFORM_TINTIN)
|
||||
cl_assert_equal_i(2, COLOR_FALLBACK(1,2));
|
||||
#elif defined(PLATFORM_SNOWY)
|
||||
cl_assert_equal_i(1, COLOR_FALLBACK(1,2));
|
||||
#elif defined(PLATFORM_SPALDING)
|
||||
cl_assert_equal_i(1, COLOR_FALLBACK(1,2));
|
||||
#else
|
||||
#error "unknown platform"
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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 "applib/graphics/graphics.h"
|
||||
#include "applib/graphics/gbitmap_png.h"
|
||||
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_print.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_ui_window.h"
|
||||
#include "stubs_compiled_with_legacy2_sdk.h"
|
||||
|
||||
extern uint8_t prv_byte_reverse(uint8_t b);
|
||||
|
||||
void test_palettized_conversion__test_byte_reverse(void) {
|
||||
cl_assert(prv_byte_reverse(0b11110000) == 0b00001111);
|
||||
cl_assert(prv_byte_reverse(0b10100101) == 0b10100101);
|
||||
}
|
||||
|
||||
// Each of the following tests depend on a PNG being converted into 1-bit PBI,
|
||||
// as well as loading in a b&w PNG, which internally will be converted to a
|
||||
// 1-bit palettized image.
|
||||
// The palettized PNG loaded in is to be used as the expected value
|
||||
|
||||
#define TEST_1BIT_FILE TEST_PBI_FILE_FMT(1bit)
|
||||
#define TEST_PALETTIZED_FILE TEST_PBI_FILE
|
||||
|
||||
void test_palettized_conversion__create_palettized_from_1bit(void) {
|
||||
GBitmap *img_1bit = get_gbitmap_from_pbi(TEST_1BIT_FILE);
|
||||
cl_assert(img_1bit);
|
||||
GBitmap *img_palettized = gbitmap_create_palettized_from_1bit(img_1bit);
|
||||
cl_assert(img_palettized);
|
||||
|
||||
gbitmap_pbi_eq(img_palettized, TEST_PALETTIZED_FILE);
|
||||
|
||||
gbitmap_destroy(img_palettized);
|
||||
|
||||
GRect test_bounds = GRect(3, 3, 46, 46);
|
||||
img_1bit->bounds = test_bounds;
|
||||
img_palettized = gbitmap_create_palettized_from_1bit(img_1bit);
|
||||
|
||||
cl_assert(gcolor_equal(img_palettized->palette[0], GColorBlack));
|
||||
cl_assert(gcolor_equal(img_palettized->palette[1], GColorWhite));
|
||||
|
||||
gbitmap_pbi_eq_with_bounds(img_palettized, TEST_PALETTIZED_FILE, &test_bounds);
|
||||
|
||||
gbitmap_destroy(img_palettized);
|
||||
gbitmap_destroy(img_1bit);
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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 "applib/graphics/gtypes.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_logging.h"
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
// Reference PNGs reside in "tests/fw/graphics/test_images/"
|
||||
// and are created at build time, with the test PBI file generated
|
||||
// by bitmapgen.py from the reference PNG copied to TEST_IMAGES_PATH
|
||||
// covers 1,2,4,8 bit palettized
|
||||
// covers 1,2,4,8 bit palettized with transparency
|
||||
|
||||
// Tests 1-bit red&white palettized PBI loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_pbi__color_1_bit(void) {
|
||||
GBitmap *bitmap = setup_pbi_test(TEST_PBI_FILE);
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
// Tests 2-bit palettized PBI loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_pbi__color_2_bit(void) {
|
||||
GBitmap *bitmap = setup_pbi_test(TEST_PBI_FILE);
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
// Tests 4-bit palettized PBI loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_pbi__color_4_bit(void) {
|
||||
GBitmap *bitmap = setup_pbi_test(TEST_PBI_FILE);
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
// Tests 8-bit color PBI loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_pbi__color_8_bit(void) {
|
||||
GBitmap *bitmap = setup_pbi_test(TEST_PBI_FILE);
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
// Tests 1-bit transparent palettized PBI loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_pbi__color_1_bit_transparent(void) {
|
||||
GBitmap *bitmap = setup_pbi_test(TEST_PBI_FILE);
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
// Tests 2-bit transparent palettized PBI loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_pbi__color_2_bit_transparent(void) {
|
||||
GBitmap *bitmap = setup_pbi_test(TEST_PBI_FILE);
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
// Tests 4-bit transparent palettized PBI loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_pbi__color_4_bit_transparent(void) {
|
||||
GBitmap *bitmap = setup_pbi_test(TEST_PBI_FILE);
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
// Tests 8-bit transparent PBI loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PBI
|
||||
void test_pbi__color_8_bit_transparent(void) {
|
||||
GBitmap *bitmap = setup_pbi_test(TEST_PBI_FILE);
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 "util.h"
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
|
||||
//// Stubs
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_logging.h"
|
||||
|
||||
// tests
|
||||
void test_pdc__draw_pdc_image(void) {
|
||||
GBitmap *bitmap = setup_pbi_test(TEST_PDC_PBI_FILE);
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE));
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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 "applib/graphics/perimeter.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util/trig.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <math.h>
|
||||
#ifndef M_PI
|
||||
// M_PI doesn't exist in Linux
|
||||
#define M_PI 3.14159265358979323846 /* pi */
|
||||
#endif
|
||||
#define DEG2RAD(a) (M_PI/180*(a))
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_compiled_with_legacy2_sdk.h"
|
||||
|
||||
#define BETWEEN(val, low, high) \
|
||||
(val >= low && val <= high) ? true : false
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
GRangeHorizontal perimeter_for_circle(GRangeVertical vertical_range, GPoint center, int32_t radius);
|
||||
GRangeHorizontal perimeter_for_display_round(const GPerimeter *perimeter,
|
||||
const GSize *ctx_size,
|
||||
GRangeVertical vertical_range,
|
||||
uint16_t inset);
|
||||
GRangeHorizontal perimeter_for_display_rect(const GPerimeter *perimeter,
|
||||
const GSize *ctx_size,
|
||||
GRangeVertical vertical_range,
|
||||
uint16_t inset);
|
||||
|
||||
void test_perimeter__perimeter_for_circle(void) {
|
||||
GRect bounds = GRect(0,0,180,180);
|
||||
GPoint center = grect_center_point(&bounds);
|
||||
int16_t radius = bounds.size.w / 2;
|
||||
|
||||
// test robustness of perimeter_horizontal_range_for_circle
|
||||
for (int y = 0; y < bounds.size.h; y++) {
|
||||
GRangeHorizontal h_range = perimeter_for_circle(
|
||||
(GRangeVertical) {.origin_y = y, .size_h = 0}, center, radius);
|
||||
|
||||
// internally we use integer_sqrt, which causes precision loss
|
||||
// so we need to mirror some of the fixed point truncation here
|
||||
int16_t height = 90 - y;
|
||||
int16_t width = sqrt(radius * radius - height * height);
|
||||
int16_t test_origin = 90 - width;
|
||||
|
||||
// Due to integer math and truncation, we need to test +-1
|
||||
cl_assert(BETWEEN(h_range.origin_x, test_origin - 1, test_origin + 1));
|
||||
cl_assert(BETWEEN(h_range.size_w, (width - 1) * 2, (width + 1) * 2));
|
||||
}
|
||||
}
|
||||
|
||||
#define cl_assert_equal_rangehorizontal(r1, r2) \
|
||||
do { \
|
||||
cl_assert_equal_i((r1).origin_x, (r2).origin_x); \
|
||||
cl_assert_equal_i((r1).size_w, (r2).size_w); \
|
||||
} while(0)
|
||||
|
||||
void test_perimeter__perimeter_for_display_rect(void) {
|
||||
GPerimeter p = {
|
||||
.callback = perimeter_for_display_rect,
|
||||
};
|
||||
const GSize ctx_size = GSize(DISP_COLS, DISP_ROWS);
|
||||
GRangeVertical r = {.origin_y = 10, .size_h = 10};
|
||||
|
||||
GRangeHorizontal expected = (GRangeHorizontal){.origin_x = 0, .size_w = DISP_COLS};
|
||||
cl_assert_equal_rangehorizontal(expected, perimeter_for_display_rect(&p, &ctx_size, r, 0));
|
||||
expected = (GRangeHorizontal){.origin_x = 5, .size_w = DISP_COLS - 10};
|
||||
cl_assert_equal_rangehorizontal(expected, perimeter_for_display_rect(&p, &ctx_size, r, 5));
|
||||
cl_assert_equal_i(0, perimeter_for_display_rect(&p, &ctx_size, r, 500).size_w);
|
||||
}
|
||||
|
||||
void test_perimeter__perimeter_for_display_round(void) {
|
||||
GPerimeter p = {
|
||||
.callback = perimeter_for_display_round,
|
||||
};
|
||||
GRangeVertical r = {.origin_y = 10, .size_h = 10};
|
||||
const GSize ctx_size = GSize(DISP_COLS, DISP_ROWS);
|
||||
const GRect disp = GRect(0, 0, DISP_COLS, DISP_ROWS);
|
||||
|
||||
GRangeHorizontal expected =
|
||||
perimeter_for_circle(r, grect_center_point(&disp), grect_shortest_side(disp) / 2);
|
||||
cl_assert_equal_rangehorizontal(expected, perimeter_for_display_round(&p, &ctx_size, r, 0));
|
||||
expected = perimeter_for_circle(r, grect_center_point(&disp), grect_shortest_side(disp) / 2 - 5);
|
||||
cl_assert_equal_rangehorizontal(expected, perimeter_for_display_round(&p, &ctx_size, r, 5));
|
||||
cl_assert_equal_i(0, perimeter_for_display_round(&p, &ctx_size, r, 500).size_w);
|
||||
}
|
||||
|
||||
void test_perimeter__g_perimeter_for_display(void) {
|
||||
GPerimeterCallback expected = PBL_IF_RECT_ELSE(perimeter_for_display_rect,
|
||||
perimeter_for_display_round);
|
||||
cl_assert_equal_p(expected, g_perimeter_for_display->callback);
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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 "applib/graphics/gbitmap_png.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Stubs
|
||||
////////////////////////////////////
|
||||
#include "stubs_applib_resource.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_heap.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_logging.h"
|
||||
|
||||
// Tests
|
||||
////////////////////////////////////
|
||||
|
||||
// Reference PNGs reside in "tests/fw/graphics/test_images/"
|
||||
// and are created at build time, with the PNG file copied to TEST_IMAGES_PATH
|
||||
// and a separate PBI generated by bitmapgen.py from the PNG copied to TEST_IMAGES_PATH
|
||||
// covers 1,2,4,8 bit palettized
|
||||
// covers 1,2,4,8 bit palettized with transparency
|
||||
// covers 1, 2 bit greyscale
|
||||
// covers 1, 2 bit greyscale with transparency
|
||||
// covers 4 bit greyscale with transparency (black, white, dark_grey, light_grey, transparent)
|
||||
|
||||
|
||||
// Tests 1-bit jazzberry_jam colored palettized PNG loading into gbitmap
|
||||
// tests covers an old bug in image tooling where 1 color was 2 power 0
|
||||
// so bitdepth was detected as 0 (incorrectly)
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__color_1_bit_jazzberry_jam(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(1bitpalette));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(1bitpalette)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat1BitPalette);
|
||||
}
|
||||
|
||||
// Tests 1-bit red&white palettized PNG loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__color_1_bit(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(1bitpalette));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(1bitpalette)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat1BitPalette);
|
||||
}
|
||||
|
||||
// Tests 2-bit palettized PNG loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__color_2_bit(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(2bitpalette));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(2bitpalette)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat2BitPalette);
|
||||
}
|
||||
|
||||
// Tests 4-bit palettized PNG loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__color_4_bit(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(4bitpalette));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(4bitpalette)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat4BitPalette);
|
||||
}
|
||||
|
||||
// Tests 8-bit palettized 64-color PNG loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__color_8_bit(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(8bit));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(8bit)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat8Bit);
|
||||
}
|
||||
|
||||
// Tests 1-bit transparent palettized PNG loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__color_1_bit_transparent(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(1bitpalette));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(1bitpalette)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat1BitPalette);
|
||||
}
|
||||
|
||||
// Tests 2-bit transparent palettized PNG loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__color_2_bit_transparent(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(2bitpalette));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(2bitpalette)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat2BitPalette);
|
||||
}
|
||||
|
||||
// Tests 4-bit transparent palettized PNG loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__color_4_bit_transparent(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(4bitpalette));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(4bitpalette)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat4BitPalette);
|
||||
}
|
||||
|
||||
// Tests 8-bit transparent palettized PNG loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__color_8_bit_transparent(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(8bit));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(8bit)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat8Bit);
|
||||
}
|
||||
|
||||
// Tests 1-bit all black greyscale PNG loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__greyscale_1_bit_black(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(1bitpalette));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(1bitpalette)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat1BitPalette);
|
||||
}
|
||||
|
||||
// Tests 1-bit b&w greyscale PNG loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__greyscale_1_bit(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(1bitpalette));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(1bitpalette)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat1BitPalette);
|
||||
}
|
||||
|
||||
// Tests 2-bit greyscale PNG loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__greyscale_2_bit(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(2bitpalette));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(2bitpalette)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat2BitPalette);
|
||||
}
|
||||
|
||||
// Tests 1-bit transparent greyscale PNG loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__greyscale_1_bit_transparent(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(1bitpalette));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(1bitpalette)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat1BitPalette);
|
||||
}
|
||||
|
||||
// Tests 2-bit transparent greyscale PNG loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__greyscale_2_bit_transparent(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(2bitpalette));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(2bitpalette)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat2BitPalette);
|
||||
}
|
||||
|
||||
// Tests 4-bit transparent greyscale PNG loading into gbitmap
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__greyscale_4_bit_transparent(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(4bitpalette));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(4bitpalette)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat4BitPalette);
|
||||
}
|
||||
|
||||
// Tests 8-bit transparent color PNG with 4-bit number of colors (<= 16 colors)
|
||||
// loading into gbitmap
|
||||
// Checks for bug fixed for PBL-15809, where GBitmap depth was based off
|
||||
// number of colors and not index value bits-per-pixel (bpp).
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__color_8_bit_transparent_bpp_check(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(8bit));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(8bit)));
|
||||
// Make sure bpp detection doesn't fail in the future
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat8Bit);
|
||||
}
|
||||
|
||||
// Tests 8-bit transparent color PNG with 256 Colors
|
||||
// loading into gbitmap
|
||||
// Checks for bug fixed for PBL-16549, where uint8_t limit was 255
|
||||
// causing failure to load palette
|
||||
// Result:
|
||||
// - gbitmap matches platform loaded PNG
|
||||
void test_png__color_256_colors_check(void) {
|
||||
GBitmap *bitmap = setup_png_test(TEST_PNG_FILE_FMT(raw));
|
||||
cl_assert(gbitmap_pbi_eq(bitmap, TEST_PBI_FILE_FMT(raw)));
|
||||
cl_assert_equal_i(gbitmap_get_format(bitmap), GBitmapFormat8Bit);
|
||||
}
|
||||
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "test_graphics.h"
|
||||
#include "applib/graphics/gbitmap_png.h"
|
||||
#include "util/graphics.h"
|
||||
#include "util/math.h"
|
||||
|
||||
#include "clar.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
|
||||
#define PATH_STRING_LENGTH 512
|
||||
|
||||
extern GBitmapDataRowInfo prv_gbitmap_get_data_row_info(const GBitmap *bitmap, uint16_t y);
|
||||
|
||||
#include "util_pbi.h"
|
||||
|
||||
// The following macros append the ~platform (unless default) and the filetype extension
|
||||
// unless file_name contains Xbit, then appends .(native_bitdepth)bit and filetype extension
|
||||
#define TEST_NAMED_PBI_FILE(file_name) namecat(file_name, ".pbi")
|
||||
#define TEST_NAMED_PNG_FILE(file_name) namecat(file_name, ".png")
|
||||
|
||||
// The following macros create a file name based on the function name plus extension
|
||||
#define TEST_PBI_FILE namecat(__func__, ".pbi")
|
||||
#define TEST_PBI_FILE_FMT(fmt) namecat(__func__, "." #fmt ".pbi")
|
||||
#define TEST_PNG_FILE namecat(__func__, ".png")
|
||||
#define TEST_PNG_FILE_FMT(fmt) namecat(__func__, "." #fmt ".png")
|
||||
#define TEST_PDC_FILE namecat(__func__, ".pdc")
|
||||
#define TEST_PDC_PBI_FILE namecat(__func__, ".pdc.pbi")
|
||||
#define TEST_APNG_FILE namecat(__func__, ".apng")
|
||||
#define TEST_PBI_FILE_X(x) namecat(__func__, "_" #x ".pbi")
|
||||
|
||||
bool tests_write_gbitmap_to_pbi(GBitmap *bmp, const char *filename) {
|
||||
char full_path[PATH_STRING_LENGTH];
|
||||
snprintf(full_path, sizeof(full_path), "%s/%s", TEST_OUTPUT_PATH, filename);
|
||||
return write_gbitmap_to_pbi(bmp, full_path, PBI2PNG_EXE);
|
||||
}
|
||||
|
||||
// Used to work around __func__ not being a string literal (necessary for macro concatenation)
|
||||
static const char *namecat(const char* str1, const char* str2){
|
||||
char *filename = malloc(PATH_STRING_LENGTH);
|
||||
filename[0] = '\0';
|
||||
strcat(filename, str1);
|
||||
|
||||
char *filename_xbit = strstr(filename, ".Xbit");
|
||||
|
||||
if (filename_xbit) {
|
||||
// Support using ".Xbit" for the native bitdepth
|
||||
filename_xbit[1] = (SCREEN_COLOR_DEPTH_BITS == 8) ? '8' : '1';
|
||||
printf("filename and filename_xbit %s : %s\n", filename, filename_xbit);
|
||||
} else {
|
||||
#if !PLATFORM_DEFAULT
|
||||
// Add ~platform to files with unit-tests built for a specific platform
|
||||
strcat(filename, "~");
|
||||
strcat(filename, PLATFORM_NAME);
|
||||
#endif
|
||||
}
|
||||
|
||||
strcat(filename, str2);
|
||||
return filename;
|
||||
}
|
||||
|
||||
static char get_terminal_color(uint8_t c) {
|
||||
switch (c) {
|
||||
case GColorBlackARGB8: // black
|
||||
return 'B';
|
||||
case GColorWhiteARGB8: // white
|
||||
return 'W';
|
||||
case GColorRedARGB8: // red
|
||||
return 'R';
|
||||
case GColorGreenARGB8: // green
|
||||
return 'G';
|
||||
case GColorBlueARGB8: // blue
|
||||
return 'b';
|
||||
default:
|
||||
return ' ';
|
||||
}
|
||||
}
|
||||
|
||||
// A simple functon for printing 8-bit gbitmaps to the console.
|
||||
// Makes it easy to quickly review failing test cases.
|
||||
void print_bitmap(const GBitmap *bmp) {
|
||||
printf("Row Size Bytes: %d\n", bmp->row_size_bytes);
|
||||
printf("Bounds: ");
|
||||
printf(GRECT_PRINTF_FORMAT, GRECT_PRINTF_FORMAT_EXPLODE(bmp->bounds));
|
||||
printf("\n");
|
||||
|
||||
GSize size = bmp->bounds.size;
|
||||
uint8_t *data = (uint8_t *)bmp->addr;
|
||||
// Build a coordinate system, 3 rows up top for the col number
|
||||
for (uint8_t y = 0; y < 3; ++y) {
|
||||
printf("\t"); // leave space for row #
|
||||
for (uint8_t x = 0; x < size.w; ++x) {
|
||||
int num = -1;
|
||||
switch (y) {
|
||||
case 0: // hundreds
|
||||
if (x < 100) break;
|
||||
num = (x / 100) % 10;
|
||||
break;
|
||||
case 1: // tens
|
||||
if (x < 10) break;
|
||||
num = (x / 10) % 10;
|
||||
break;
|
||||
case 2: // ones
|
||||
num = x % 10;
|
||||
break;
|
||||
}
|
||||
if (num < 0) {
|
||||
printf(" ");
|
||||
} else {
|
||||
printf("%d", num);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
const uint8_t start_x = bmp->bounds.origin.x;
|
||||
const uint8_t end_x = start_x + bmp->bounds.size.w;
|
||||
const uint8_t start_y = bmp->bounds.origin.y;
|
||||
const uint8_t end_y = start_y + bmp->bounds.size.h;
|
||||
for (uint8_t y = start_y; y < end_y; ++y) {
|
||||
printf("\n%d\t", y);
|
||||
for (uint8_t x = start_x; x < end_x; ++x) {
|
||||
uint8_t color = data[y * bmp->row_size_bytes + x];
|
||||
printf("%c", get_terminal_color(color));
|
||||
}
|
||||
}
|
||||
printf("\n\n\n\n");
|
||||
}
|
||||
|
||||
// Will load the PBI at location $TEST_IMAGES_PATH/filename into a gbitmap.
|
||||
GBitmap *get_gbitmap_from_pbi(const char *filename) {
|
||||
char full_path[PATH_STRING_LENGTH];
|
||||
snprintf(full_path, PATH_STRING_LENGTH, "%s/%s", TEST_IMAGES_PATH, filename);
|
||||
|
||||
// Support using ".Xbit" for the native bitdepth
|
||||
char *filename_xbit = strstr(full_path, ".Xbit");
|
||||
if (filename_xbit) {
|
||||
filename_xbit[1] = (SCREEN_COLOR_DEPTH_BITS == 8) ? '8' : '1';
|
||||
}
|
||||
|
||||
FILE *file = fopen(full_path, "r");
|
||||
if (!file) {
|
||||
printf("Unable to open file: %s\n", full_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GBitmap *bmp = malloc(sizeof(*bmp));
|
||||
*bmp = (GBitmap){0};
|
||||
|
||||
// Read bitmap header
|
||||
fread(&bmp->row_size_bytes, sizeof(bmp->row_size_bytes), 1, file);
|
||||
fread(&bmp->info_flags, sizeof(bmp->info_flags), 1, file);
|
||||
fread(&bmp->bounds, sizeof(bmp->bounds), 1, file);
|
||||
|
||||
size_t data_size = bmp->row_size_bytes * bmp->bounds.size.h;
|
||||
|
||||
bmp->addr = malloc(data_size);
|
||||
bmp->info.is_bitmap_heap_allocated = true;
|
||||
fread(bmp->addr, 1, data_size, file);
|
||||
|
||||
uint8_t palette_size = gbitmap_get_palette_size(gbitmap_get_format(bmp));
|
||||
if (palette_size > 0) {
|
||||
// Allocate palette of GColor8 entries in ARGB8 format
|
||||
bmp->palette = malloc(palette_size * sizeof(GColor8));
|
||||
fread(bmp->palette, 1, palette_size * sizeof(GColor8), file);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return bmp;
|
||||
}
|
||||
|
||||
#define ACTUAL_PBI_FILE_EXTENSION "-actual.pbi"
|
||||
#define EXPECTED_PBI_FILE_EXTENSION "-expected.pbi"
|
||||
#define DIFF_PBI_FILE_EXTENSION "-diff.pbi"
|
||||
#define DIFF_COLOR GColorMagenta
|
||||
|
||||
static GColor8 prv_convert_to_gcolor8(GBitmapFormat format, uint8_t raw_value, GColor *palette) {
|
||||
uint8_t color8 = raw_value;
|
||||
switch (format) {
|
||||
case GBitmapFormat1Bit:
|
||||
color8 = (raw_value) ? GColorWhite.argb : GColorBlack.argb;
|
||||
break;
|
||||
case GBitmapFormat1BitPalette:
|
||||
case GBitmapFormat2BitPalette:
|
||||
case GBitmapFormat4BitPalette:
|
||||
color8 = palette[raw_value].argb;
|
||||
break;
|
||||
case GBitmapFormat8Bit:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return (GColor8)color8;
|
||||
}
|
||||
|
||||
static uint8_t prv_raw_image_get_value_for_format(const uint8_t *raw_image_buffer,
|
||||
uint32_t x, uint32_t y, uint16_t row_stride_bytes, uint8_t bitdepth, GBitmapFormat format) {
|
||||
if (format == GBitmapFormat1Bit){
|
||||
// Retrieve the byte from the image buffer containing the requested pixel
|
||||
uint32_t pixel_in_byte = raw_image_buffer[y * row_stride_bytes + (x / 8)];
|
||||
// Find the index of the pixel in terms of coordinates and aligned_width
|
||||
uint32_t pixel_index = y * (row_stride_bytes * 8) + x;
|
||||
// Shift and mask the requested pixel data from the byte containing it and return
|
||||
return (uint8_t)(pixel_in_byte >> ((pixel_index % 8)) & 1);
|
||||
} else {
|
||||
return raw_image_get_value_for_bitdepth(raw_image_buffer, x, y, row_stride_bytes, bitdepth);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_write_diff_to_file(const char *filename, GBitmap *expected_bmp,
|
||||
GBitmap *actual_bmp, GBitmap *diff_bmp) {
|
||||
// Write the expected output to filename-expected.png
|
||||
char bmp_filename[PATH_STRING_LENGTH];
|
||||
if (expected_bmp) {
|
||||
strncpy(bmp_filename, filename, PATH_STRING_LENGTH - strlen(EXPECTED_PBI_FILE_EXTENSION) - 1);
|
||||
char *ext = strrchr(bmp_filename, '.');
|
||||
strncpy(ext, EXPECTED_PBI_FILE_EXTENSION, strlen(EXPECTED_PBI_FILE_EXTENSION) + 1);
|
||||
cl_assert(tests_write_gbitmap_to_pbi(expected_bmp, bmp_filename));
|
||||
|
||||
// TODO: PBL-20932 Add 1-bit and palletized support
|
||||
if (actual_bmp->info.format == GBitmapFormat8Bit && diff_bmp) {
|
||||
// Only write the diff file if there is an expected image
|
||||
strncpy(bmp_filename, filename, PATH_STRING_LENGTH - strlen(DIFF_PBI_FILE_EXTENSION) - 1);
|
||||
ext = strrchr(bmp_filename, '.');
|
||||
strncpy(ext, DIFF_PBI_FILE_EXTENSION, strlen(DIFF_PBI_FILE_EXTENSION) + 1);
|
||||
cl_assert(tests_write_gbitmap_to_pbi(diff_bmp, bmp_filename));
|
||||
}
|
||||
}
|
||||
|
||||
// Write the actual output to the filename-actual.png
|
||||
strncpy(bmp_filename, filename, PATH_STRING_LENGTH - strlen(ACTUAL_PBI_FILE_EXTENSION) - 1);
|
||||
char *ext = strrchr(bmp_filename, '.');
|
||||
strncpy(ext, ACTUAL_PBI_FILE_EXTENSION, strlen(ACTUAL_PBI_FILE_EXTENSION) + 1);
|
||||
cl_assert(tests_write_gbitmap_to_pbi(actual_bmp, bmp_filename));
|
||||
}
|
||||
|
||||
// declared in gbitmap.c
|
||||
GBitmap *prv_gbitmap_create_blank_internal_no_platform_checks(GSize size, GBitmapFormat format);
|
||||
|
||||
|
||||
// Compare two bitmap and return whether or not they are the same
|
||||
// Note that if both passed bitmaps are NULL, this test will succeed!
|
||||
bool gbitmap_eq(GBitmap *actual_bmp, GBitmap *expected_bmp, const char *filename) {
|
||||
bool rc = false;
|
||||
GBitmap *diff_bmp = NULL;
|
||||
if (!actual_bmp && !expected_bmp) {
|
||||
return true;
|
||||
} else if (!actual_bmp || !expected_bmp) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!grect_equal(&actual_bmp->bounds, &expected_bmp->bounds)) {
|
||||
printf("Unmatched bounds\n");
|
||||
printf("\tExpected: ");
|
||||
printf(GRECT_PRINTF_FORMAT, GRECT_PRINTF_FORMAT_EXPLODE(expected_bmp->bounds));
|
||||
printf("\n\tGot: ");
|
||||
printf(GRECT_PRINTF_FORMAT, GRECT_PRINTF_FORMAT_EXPLODE(actual_bmp->bounds));
|
||||
goto done;
|
||||
}
|
||||
|
||||
uint8_t actual_bmp_palette_size = gbitmap_get_palette_size(gbitmap_get_format(actual_bmp));
|
||||
uint8_t expected_bmp_palette_size = gbitmap_get_palette_size(gbitmap_get_format(expected_bmp));
|
||||
|
||||
uint8_t *expected_bmp_data = (uint8_t *)expected_bmp->addr;
|
||||
|
||||
uint8_t actual_bmp_bpp = gbitmap_get_bits_per_pixel(gbitmap_get_format(actual_bmp));
|
||||
uint8_t expected_bmp_bpp = gbitmap_get_bits_per_pixel(gbitmap_get_format(expected_bmp));
|
||||
|
||||
const int16_t start_y = actual_bmp->bounds.origin.y;
|
||||
const int16_t end_y = start_y + actual_bmp->bounds.size.h;
|
||||
rc = true;
|
||||
|
||||
// Create a bitmap for the diff image - force 8-bit
|
||||
// The diff image contains first the actual image, then the diff image, and then the expected image
|
||||
// These images are separated by one pixel column (transparent)
|
||||
GSize diff_bmp_size = actual_bmp->bounds.size;
|
||||
diff_bmp_size.w = (3 * diff_bmp_size.w) + 2; // 2 pixels to divide the three images
|
||||
diff_bmp = prv_gbitmap_create_blank_internal_no_platform_checks(diff_bmp_size, GBitmapFormat8Bit);
|
||||
if (!diff_bmp) {
|
||||
printf("Unable to create diff bitmap\n");
|
||||
rc = false;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (int y = start_y; y < end_y; ++y) {
|
||||
uint8_t *line = ((uint8_t*)diff_bmp->addr) + (diff_bmp->row_size_bytes * y);
|
||||
|
||||
// TODO: PBL-20932 Add 1-bit and palletized support
|
||||
if (actual_bmp->info.format == GBitmapFormat8Bit) {
|
||||
line[(diff_bmp->row_size_bytes / 3) + 1] = GColorClear.argb; // Separator pixel between images
|
||||
line[(2 * diff_bmp->row_size_bytes / 3) + 1] = GColorClear.argb; // Separator pixel between images
|
||||
}
|
||||
|
||||
// Needs to be prv_gbitmap_get_data_row_info to avoid unit test mocked version
|
||||
const GBitmapDataRowInfo dest_row_info = prv_gbitmap_get_data_row_info(actual_bmp, y);
|
||||
const int16_t start_x = MAX(actual_bmp->bounds.origin.x, dest_row_info.min_x);
|
||||
const int16_t end_x = MIN(grect_get_max_x(&actual_bmp->bounds), dest_row_info.max_x + 1);
|
||||
const int16_t y_line = 0; // line is constant zero below now that we are retrieving row
|
||||
if (end_x < start_x) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int x = start_x; x < end_x; ++x) {
|
||||
uint8_t *actual_bmp_data = dest_row_info.data;
|
||||
uint8_t actual_bmp_val = prv_raw_image_get_value_for_format(actual_bmp_data, x, y_line,
|
||||
actual_bmp->row_size_bytes,
|
||||
actual_bmp_bpp,
|
||||
actual_bmp->info.format);
|
||||
uint8_t expected_bmp_val = prv_raw_image_get_value_for_format(expected_bmp_data, x, y,
|
||||
expected_bmp->row_size_bytes,
|
||||
expected_bmp_bpp,
|
||||
expected_bmp->info.format);
|
||||
GColor8 actual_bmp_color = prv_convert_to_gcolor8(actual_bmp->info.format,
|
||||
actual_bmp_val, actual_bmp->palette);
|
||||
GColor8 expected_bmp_color = prv_convert_to_gcolor8(expected_bmp->info.format,
|
||||
expected_bmp_val, expected_bmp->palette);
|
||||
|
||||
if (!gcolor_equal(actual_bmp_color, expected_bmp_color)) {
|
||||
if (rc) {
|
||||
// Only print out the first mismatch
|
||||
printf("Mismatch at x: %d y: %d\n", x, y);
|
||||
printf("value for end_x was:%d\n", end_x);
|
||||
printf("format was %d\n", actual_bmp->info.format);
|
||||
}
|
||||
rc = false;
|
||||
}
|
||||
|
||||
// TODO: PBL-20932 Add 1-bit and palletized support
|
||||
if (actual_bmp->info.format == GBitmapFormat8Bit) {
|
||||
if (actual_bmp_color.argb != expected_bmp_color.argb) {
|
||||
GColor8 diff_bmp_color = DIFF_COLOR;
|
||||
line[(diff_bmp->row_size_bytes / 3) + x + 1] = diff_bmp_color.argb;
|
||||
} else {
|
||||
line[(diff_bmp->row_size_bytes / 3) + x + 1] = actual_bmp_color.argb;
|
||||
}
|
||||
|
||||
// Fill in the actual and expected pixels on either side of the diff image
|
||||
line[x] = actual_bmp_color.argb;
|
||||
line[(2 * diff_bmp->row_size_bytes / 3) + x + 1] = expected_bmp_color.argb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (!rc) {
|
||||
prv_write_diff_to_file(filename, expected_bmp, actual_bmp, diff_bmp);
|
||||
}
|
||||
gbitmap_destroy(diff_bmp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Compare the given gbitmap to a gbmitmap loaded from a PBI in the filename given.
|
||||
bool gbitmap_pbi_eq_with_bounds(GBitmap *bmp, const char *filename, const GRect *bounds) {
|
||||
GBitmap *pbi_bmp = get_gbitmap_from_pbi(filename);
|
||||
if (pbi_bmp && bounds) {
|
||||
pbi_bmp->bounds = *bounds;
|
||||
}
|
||||
bool rc = gbitmap_eq(bmp, pbi_bmp, filename);
|
||||
gbitmap_destroy(pbi_bmp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool gbitmap_pbi_eq(GBitmap *bmp, const char *filename) {
|
||||
return gbitmap_pbi_eq_with_bounds(bmp, filename, NULL);
|
||||
}
|
||||
|
||||
size_t load_file(const char* filename, uint8_t** data) {
|
||||
char full_path[PATH_STRING_LENGTH];
|
||||
snprintf(full_path, sizeof(full_path), "%s/%s", TEST_IMAGES_PATH, filename);
|
||||
|
||||
FILE *file = fopen(full_path,"rb");
|
||||
if(file == NULL){
|
||||
printf("Error: couldn't open file: %s\n", filename);
|
||||
cl_assert(false);
|
||||
}
|
||||
fseek(file,0,SEEK_END);
|
||||
int data_size = ftell(file);
|
||||
fseek(file,0,SEEK_SET);
|
||||
*data = (unsigned char*)malloc(data_size);
|
||||
fread(*data,1, data_size, file);
|
||||
fclose(file);
|
||||
return data_size;
|
||||
}
|
||||
|
||||
// Mask flags to indicate what to setup in the draw_state of GContext within setup_test_context
|
||||
#define CTX_FLAG_DS_ALL 0x00000010
|
||||
#define CTX_FLAG_DS_CLIP_BOX 0x00000020
|
||||
#define CTX_FLAG_DS_DRAWING_BOX 0x00000040
|
||||
#define CTX_FLAG_DS_STROKE_COLOR 0x00000080
|
||||
#define CTX_FLAG_DS_FILL_COLOR 0x00000100
|
||||
#define CTX_FLAG_DS_TEXT_COLOR 0x00000200
|
||||
#define CTX_FLAG_DS_COMPOSITING_MODE 0x00000400
|
||||
#define CTX_FLAG_DS_ANTIALIASED 0x00000800
|
||||
#define CTX_FLAG_DS_STROKE_WIDTH 0x00001000
|
||||
void setup_test_context(GContext* ctx, uint32_t flags, GDrawState *draw_state, bool *lock) {
|
||||
if (draw_state) {
|
||||
if (flags & CTX_FLAG_DS_CLIP_BOX) {
|
||||
ctx->draw_state.clip_box = draw_state->clip_box;
|
||||
}
|
||||
if (flags & CTX_FLAG_DS_DRAWING_BOX) {
|
||||
ctx->draw_state.drawing_box = draw_state->drawing_box;
|
||||
}
|
||||
if (flags & CTX_FLAG_DS_STROKE_COLOR) {
|
||||
graphics_context_set_stroke_color(ctx, draw_state->stroke_color);
|
||||
}
|
||||
if (flags & CTX_FLAG_DS_FILL_COLOR) {
|
||||
graphics_context_set_fill_color(ctx, draw_state->fill_color);
|
||||
}
|
||||
if (flags & CTX_FLAG_DS_TEXT_COLOR) {
|
||||
graphics_context_set_text_color(ctx, draw_state->text_color);
|
||||
}
|
||||
if (flags & CTX_FLAG_DS_COMPOSITING_MODE) {
|
||||
graphics_context_set_compositing_mode(ctx, draw_state->compositing_mode);
|
||||
}
|
||||
if (flags & CTX_FLAG_DS_ANTIALIASED) {
|
||||
#if PBL_COLOR
|
||||
graphics_context_set_antialiased(ctx, draw_state->antialiased);
|
||||
#endif
|
||||
}
|
||||
if (flags & CTX_FLAG_DS_STROKE_WIDTH) {
|
||||
graphics_context_set_stroke_width(ctx, draw_state->stroke_width);
|
||||
}
|
||||
}
|
||||
|
||||
if (lock) {
|
||||
ctx->lock = lock;
|
||||
}
|
||||
}
|
||||
|
||||
GBitmap* setup_pbi_test(const char *filename) {
|
||||
uint8_t *pbi_data = NULL;
|
||||
size_t pbi_size = 0;
|
||||
pbi_size = load_file(filename, &pbi_data);
|
||||
cl_assert(pbi_size > 0);
|
||||
cl_assert(pbi_data);
|
||||
return gbitmap_create_with_data(pbi_data);
|
||||
}
|
||||
|
||||
static GBitmap* setup_png_test(const char *filename) {
|
||||
uint8_t *png_data = NULL;
|
||||
size_t png_size = 0;
|
||||
png_size = load_file(filename, &png_data);
|
||||
cl_assert(png_size > 0);
|
||||
cl_assert(png_data);
|
||||
return gbitmap_create_from_png_data(png_data, png_size);
|
||||
}
|
||||
|
||||
void setup_test_aa_sw(GContext *ctx, FrameBuffer *fb, GRect clip_box, GRect drawing_box,
|
||||
bool antialiased, uint8_t stroke_width) {
|
||||
test_graphics_context_reset(ctx, fb);
|
||||
|
||||
GDrawState draw_state = {
|
||||
.clip_box = clip_box,
|
||||
.drawing_box = drawing_box,
|
||||
#if PBL_COLOR
|
||||
.antialiased = antialiased,
|
||||
#endif
|
||||
.stroke_width = stroke_width
|
||||
};
|
||||
setup_test_context(ctx,
|
||||
(CTX_FLAG_DS_CLIP_BOX | CTX_FLAG_DS_DRAWING_BOX |
|
||||
CTX_FLAG_DS_ANTIALIASED | CTX_FLAG_DS_STROKE_WIDTH),
|
||||
&draw_state, NULL);
|
||||
}
|
||||
|
||||
#if PLATFORM_SPALDING
|
||||
bool gbitmap_8bit_to_8bit_circular(GBitmap *bitmap) {
|
||||
// Only allow conversion of 8Bit 180x180 rectangular bitmaps to circular
|
||||
if (!bitmap || (gbitmap_get_format(bitmap) != GBitmapFormat8Bit) ||
|
||||
(bitmap->bounds.size.w != DISP_COLS) || (bitmap->bounds.size.h != DISP_COLS)) {
|
||||
return false;
|
||||
}
|
||||
// Using realloc or copying to a new buffer has high memory overhead
|
||||
// attempt to shuffle bytes in place to allow 3rd party watchapps use
|
||||
uint8_t *data = gbitmap_get_data(bitmap);
|
||||
|
||||
// Convert format and link to data_row_infos table
|
||||
bitmap->info.format = GBitmapFormat8BitCircular;
|
||||
bitmap->data_row_infos = g_gbitmap_spalding_data_row_infos;
|
||||
|
||||
for (uint32_t y = 0; y < DISP_ROWS; y++) {
|
||||
GBitmapDataRowInfo row_info = gbitmap_get_data_row_info(bitmap, y);
|
||||
// copy (using memmove for overlapping region) valid bytes between min_x and max_x
|
||||
memmove(&row_info.data[row_info.min_x], &data[y * DISP_COLS + row_info.min_x],
|
||||
row_info.max_x - row_info.min_x + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define PATH_STRING_LENGTH 512
|
||||
|
||||
extern GBitmapDataRowInfo prv_gbitmap_get_data_row_info(const GBitmap *bitmap, uint16_t y);
|
||||
|
||||
bool write_gbitmap_to_pbi(GBitmap *bmp, const char *filepath, const char *pbi2png_path) {
|
||||
char pbi_path[PATH_STRING_LENGTH];
|
||||
char png_path[PATH_STRING_LENGTH];
|
||||
char *ext = NULL;
|
||||
|
||||
strncpy(pbi_path, filepath, sizeof(pbi_path));
|
||||
ext = strrchr(pbi_path, '.');
|
||||
strncpy(ext + 1, "pbi", 3);
|
||||
|
||||
strncpy(png_path, filepath, sizeof(png_path));
|
||||
ext = strrchr(png_path, '.');
|
||||
strncpy(ext + 1, "png", 3);
|
||||
|
||||
FILE *file = fopen(pbi_path, "w");
|
||||
if (!file) {
|
||||
printf("Unable to open file: %s\n", pbi_path);
|
||||
return false;
|
||||
}
|
||||
// Just in case this output bitmap was created by hand.
|
||||
bmp->info.version = GBITMAP_VERSION_CURRENT;
|
||||
|
||||
// PBL-24228 Support Circular PBIs
|
||||
uint16_t info_flags = bmp->info_flags;
|
||||
#ifdef PLATFORM_SPALDING
|
||||
if(bmp->info.format == GBitmapFormat8BitCircular) {
|
||||
// Have to force output format to 8Bit;
|
||||
((BitmapInfo*)&info_flags)->format = GBitmapFormat8Bit;
|
||||
}
|
||||
#endif
|
||||
|
||||
// use entire bounds to include entire image
|
||||
GRect entire_bounds = GRect(0, 0,
|
||||
bmp->bounds.origin.x + bmp->bounds.size.w,
|
||||
bmp->bounds.origin.y + bmp->bounds.size.h);
|
||||
|
||||
fwrite(&bmp->row_size_bytes, sizeof(bmp->row_size_bytes), 1, file);
|
||||
fwrite(&info_flags, 2, 1, file);
|
||||
fwrite(&entire_bounds, sizeof(GRect), 1, file);
|
||||
|
||||
#ifdef PLATFORM_SPALDING
|
||||
if(bmp->info.format == GBitmapFormat8BitCircular) {
|
||||
for (int y = 0; y < entire_bounds.size.h; ++y) {
|
||||
// 8-Bit circular buffer is centered in padded rows, so just grab row and write DISP_COLS
|
||||
const GBitmapDataRowInfo dest_row_info = prv_gbitmap_get_data_row_info(bmp, y);
|
||||
uint8_t *bmp_row = dest_row_info.data;
|
||||
int x = 0;
|
||||
const uint8_t blank = 0;
|
||||
// PBL-24229 missing mask: data outside of circle is garbage from previous and next rows
|
||||
// Pad with blanks before min_x
|
||||
while (x < dest_row_info.min_x) {
|
||||
fwrite(&blank, 1, 1, file);
|
||||
x++;
|
||||
}
|
||||
if (x <= dest_row_info.max_x) {
|
||||
int length = dest_row_info.max_x - x + 1;
|
||||
fwrite(&bmp_row[dest_row_info.min_x], 1, length, file);
|
||||
x += length;
|
||||
}
|
||||
// Pad with blanks after max_x
|
||||
while (x < entire_bounds.size.w) {
|
||||
fwrite(&blank, 1, 1, file);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
size_t data_size = bmp->row_size_bytes * (entire_bounds.size.h);
|
||||
fwrite(bmp->addr, 1, data_size, file);
|
||||
#ifdef PLATFORM_SPALDING
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t palette_size = gbitmap_get_palette_size(gbitmap_get_format(bmp));
|
||||
if (palette_size > 0) {
|
||||
fwrite(bmp->palette, 1, palette_size * sizeof(*bmp->palette), file);
|
||||
}
|
||||
fclose(file);
|
||||
printf("PBI file written to: %s\n", pbi_path);
|
||||
|
||||
int pid = fork();
|
||||
if (pid == 0) {
|
||||
char *args[] = {
|
||||
"python",
|
||||
(char *)pbi2png_path,
|
||||
(char *)pbi_path,
|
||||
(char *)png_path,
|
||||
NULL,
|
||||
};
|
||||
execvp("python", args);
|
||||
|
||||
perror("execv"); // Error internal to execve
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int status;
|
||||
if (wait(&status) >= 0 && !WIFEXITED(status)) {
|
||||
printf("FAILURE: pbi2png.py process exited with %d status. PNG file not written\n",
|
||||
WEXITSTATUS(status));
|
||||
} else {
|
||||
printf("PNG file written to: %s\n", png_path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* 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 "weather_app_resources.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "applib/graphics/gdraw_command_private.h"
|
||||
#include "applib/ui/animation.h"
|
||||
#include "util/size.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if !TINTIN_FORCE_FIT
|
||||
|
||||
#if defined(UNITTEST)
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
GDrawCommandImage *weather_app_resource_create_cloud() {
|
||||
GPoint c0_points[] =
|
||||
{{22, 6}, {28, 1}, {39, 1}, {46, 10}, {46, 23}, {4, 23}, {4, 14}, {12, 6}};
|
||||
GPoint c1_points[] =
|
||||
{{17, 12}, {28, 1}, {39, 1}, {46, 10}, {46, 23}, {4, 23}, {4, 14}, {12, 6}, {22, 6}};
|
||||
|
||||
GPoint c2_points[] = {{ 5, 35}, {17, 23}};
|
||||
GPoint c3_points[] = {{ 9, 43}, {29, 23}};
|
||||
GPoint c4_points[] = {{23, 41}, {41, 23}};
|
||||
|
||||
const uint16_t num_commands = 5;
|
||||
|
||||
GDrawCommandImage *image = task_malloc(
|
||||
sizeof(GDrawCommandImage) +
|
||||
sizeof(GDrawCommand) * num_commands +
|
||||
sizeof(c0_points) + sizeof(c1_points) + sizeof(c2_points)+ sizeof(c3_points) +
|
||||
sizeof(c4_points));
|
||||
|
||||
*image = (GDrawCommandImage){
|
||||
.version = 1,
|
||||
.size = GSize(48, 48),
|
||||
.command_list = (GDrawCommandList) {
|
||||
.num_commands = num_commands,
|
||||
},
|
||||
};
|
||||
|
||||
GDrawCommandList *list = &image->command_list;
|
||||
|
||||
GDrawCommand *c0 = gdraw_command_list_get_command(list, 0);
|
||||
*c0 = (GDrawCommand){
|
||||
.type = GDrawCommandTypePath,
|
||||
.fill_color = GColorWhite,
|
||||
.num_points = ARRAY_LENGTH(c0_points),
|
||||
};
|
||||
memcpy(c0->points, c0_points, sizeof(c0_points));
|
||||
|
||||
GDrawCommand *c1 = gdraw_command_list_get_command(list, 1);
|
||||
*c1 = (GDrawCommand){
|
||||
.type = GDrawCommandTypePath,
|
||||
.path_open = true,
|
||||
.stroke_color = GColorBlack,
|
||||
.stroke_width = 3,
|
||||
.num_points = ARRAY_LENGTH(c1_points),
|
||||
};
|
||||
memcpy(c1->points, c1_points, sizeof(c1_points));
|
||||
|
||||
GDrawCommand *c2 = gdraw_command_list_get_command(list, 2);
|
||||
*c2 = (GDrawCommand){
|
||||
.type = GDrawCommandTypePath,
|
||||
.hidden = false,
|
||||
.stroke_color = GColorBlack,
|
||||
.stroke_width = 3,
|
||||
.path_open = true,
|
||||
.num_points = ARRAY_LENGTH(c2_points),
|
||||
};
|
||||
memcpy(c2->points, c2_points, sizeof(c2_points));
|
||||
|
||||
GDrawCommand *c3 = gdraw_command_list_get_command(list, 3);
|
||||
*c3 = *c2;
|
||||
memcpy(c3->points, c3_points, sizeof(c3_points));
|
||||
|
||||
GDrawCommand *c4 = gdraw_command_list_get_command(list, 4);
|
||||
*c4 = *c2;
|
||||
memcpy(c4->points, c4_points, sizeof(c4_points));
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
#define COPY_POINTS(target, source) \
|
||||
for (size_t i=0; i < ARRAY_LENGTH(source); i++) { \
|
||||
(target)[i] = (source)[i]; \
|
||||
}
|
||||
|
||||
GDrawCommandImage *weather_app_resource_create_cloud_25px(void) {
|
||||
GDrawCommandImage *result = weather_app_resource_create_cloud();
|
||||
result->size = GSize(25, 25);
|
||||
|
||||
GPoint c0_points[] =
|
||||
{ {10, 4}, {13, 1}, {19, 1}, {23, 6}, {23, 13}, {1, 13}, {1, 8}, {4, 4}};
|
||||
GPoint c1_points[] =
|
||||
{{7, 7}, {13, 1}, {19, 1}, {23, 6}, {23, 13}, {1, 13}, {1, 8}, {4, 4}, {10, 4}};
|
||||
GPoint c2_points[] = {{1, 19}, {7, 13}};
|
||||
GPoint c3_points[] = {{3, 24}, {14, 13}};
|
||||
GPoint c4_points[] = {{11, 23}, {21, 13}};
|
||||
COPY_POINTS(gdraw_command_list_get_command(&result->command_list, 0)->points, c0_points)
|
||||
COPY_POINTS(gdraw_command_list_get_command(&result->command_list, 1)->points, c1_points)
|
||||
COPY_POINTS(gdraw_command_list_get_command(&result->command_list, 2)->points, c2_points)
|
||||
COPY_POINTS(gdraw_command_list_get_command(&result->command_list, 3)->points, c3_points)
|
||||
COPY_POINTS(gdraw_command_list_get_command(&result->command_list, 4)->points, c4_points)
|
||||
return result;
|
||||
}
|
||||
|
||||
GDrawCommandImage *weather_app_resource_create_sun(void) {
|
||||
GPoint c0_points[] = {{47, 23}, {5, 23}};
|
||||
GPoint c1_points[] = {{13, 10}, {39, 36}};
|
||||
GPoint c2_points[] = {{26, 2}, {26, 44}};
|
||||
GPoint c3_points[] = {{39, 10}, {13, 36}};
|
||||
GPoint c4_points[] = {{21, 11}, {31, 11}, {39, 18}, {39, 28}, {31, 36}, {21, 36}, {13, 28},
|
||||
{13, 18}};
|
||||
const uint16_t num_commands = 5;
|
||||
|
||||
GDrawCommandImage *image = task_malloc(
|
||||
sizeof(GDrawCommandImage) +
|
||||
sizeof(GDrawCommand) * num_commands +
|
||||
sizeof(c0_points) + sizeof(c1_points) + sizeof(c2_points) + sizeof(c3_points) +
|
||||
sizeof(c4_points));
|
||||
|
||||
*image = (GDrawCommandImage){
|
||||
.version = 1,
|
||||
.size = GSize(48, 48),
|
||||
.command_list = (GDrawCommandList) {
|
||||
.num_commands = num_commands,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
GDrawCommandList *list = &image->command_list;
|
||||
GDrawCommand *c0 = gdraw_command_list_get_command(list, 0);
|
||||
*c0 = (GDrawCommand){
|
||||
.type = GDrawCommandTypePath,
|
||||
.stroke_color = GColorBlack,
|
||||
.stroke_width = 3,
|
||||
.path_open = true,
|
||||
.num_points = ARRAY_LENGTH(c0_points),
|
||||
};
|
||||
memcpy(c0->points, c0_points, sizeof(c0_points));
|
||||
|
||||
GDrawCommand *c1 = gdraw_command_list_get_command(list, 1);
|
||||
*c1 = *c0;
|
||||
memcpy(c1->points, c1_points, sizeof(c1_points));
|
||||
|
||||
GDrawCommand *c2 = gdraw_command_list_get_command(list, 2);
|
||||
*c2 = *c0;
|
||||
memcpy(c2->points, c2_points, sizeof(c2_points));
|
||||
|
||||
GDrawCommand *c3 = gdraw_command_list_get_command(list, 3);
|
||||
*c3 = *c0;
|
||||
memcpy(c3->points, c3_points, sizeof(c3_points));
|
||||
|
||||
GDrawCommand *c4 = gdraw_command_list_get_command(list, 4);
|
||||
*c4 = (GDrawCommand){
|
||||
.type = GDrawCommandTypePath,
|
||||
.stroke_color = GColorBlack,
|
||||
.stroke_width = 3,
|
||||
.fill_color = GColorWhite,
|
||||
.num_points = ARRAY_LENGTH(c4_points),
|
||||
};
|
||||
memcpy(c4->points, c4_points, sizeof(c4_points));
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
GDrawCommandImage *weather_app_resource_create_sun_25px(void) {
|
||||
GDrawCommandImage *result = weather_app_resource_create_sun();
|
||||
result->size = GSize(25, 25);
|
||||
|
||||
GPoint c0_points[] = {{0, 12}, {24, 12}};
|
||||
GPoint c1_points[] = {{12, 0}, {12, 24}};
|
||||
GPoint c2_points[] = {{3, 3}, {21, 21}};
|
||||
GPoint c3_points[] = {{3, 21}, {21, 3}};
|
||||
GPoint c4_points[] = {{9, 4}, {15, 4}, {20, 9}, {20, 15}, {15, 20}, {9, 20}, {4, 15}, {4, 9}};
|
||||
COPY_POINTS(gdraw_command_list_get_command(&result->command_list, 0)->points, c0_points)
|
||||
COPY_POINTS(gdraw_command_list_get_command(&result->command_list, 1)->points, c1_points)
|
||||
COPY_POINTS(gdraw_command_list_get_command(&result->command_list, 2)->points, c2_points)
|
||||
COPY_POINTS(gdraw_command_list_get_command(&result->command_list, 3)->points, c3_points)
|
||||
COPY_POINTS(gdraw_command_list_get_command(&result->command_list, 4)->points, c4_points)
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/graphics/gdraw_command_image.h"
|
||||
|
||||
GDrawCommandImage *weather_app_resource_create_cloud(void);
|
||||
GDrawCommandImage *weather_app_resource_create_sun(void);
|
||||
|
||||
GDrawCommandImage *weather_app_resource_create_cloud_25px(void);
|
||||
GDrawCommandImage *weather_app_resource_create_sun_25px(void);
|
||||
@@ -0,0 +1,703 @@
|
||||
from waftools.pebble_test import clar, get_bitdepth_for_platform
|
||||
import waflib.Utils
|
||||
|
||||
def _process_template(task):
|
||||
from string import Template
|
||||
template = Template(open(task.inputs[0].srcpath(), 'r').read())
|
||||
processed = template.safe_substitute(BIT_DEPTH_NAME=task.generator.bit_depth_name)
|
||||
waflib.Utils.writef(task.outputs[0].srcpath(), processed)
|
||||
|
||||
def graphics_process_test_template(ctx, source, bit_depth):
|
||||
path = ctx.path.find_node(source).srcpath()
|
||||
bit_depth_name = "{}bit".format(bit_depth)
|
||||
|
||||
# Format the output filename.
|
||||
# - ".template.c" => ".auto.c"
|
||||
# - Add bit depth to filename
|
||||
# eg. "test_graphics.template.c" => "test_graphics_8bit.c"
|
||||
split = source.replace("template.", "").split('.')
|
||||
target_name = split[0] + '_' + bit_depth_name + '.' + split[1]
|
||||
|
||||
out_path = ctx.path.get_bld().make_node(target_name)
|
||||
|
||||
ctx(name='generate_test_from_template', rule=_process_template, source=ctx.path.find_node(source),
|
||||
target=out_path, bit_depth_name=bit_depth_name)
|
||||
return out_path
|
||||
|
||||
|
||||
def build(ctx):
|
||||
graphics_sources_ant_glob = \
|
||||
" src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c" \
|
||||
" src/fw/applib/graphics/framebuffer.c" \
|
||||
" src/fw/applib/graphics/gbitmap.c" \
|
||||
" src/fw/applib/graphics/gcolor_definitions.c" \
|
||||
" src/fw/applib/graphics/gtypes.c" \
|
||||
" src/fw/applib/graphics/graphics.c" \
|
||||
" src/fw/applib/graphics/graphics_bitmap.c" \
|
||||
" src/fw/applib/graphics/gtransform.c" \
|
||||
" src/fw/applib/graphics/bitblt.c" \
|
||||
" src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c" \
|
||||
" src/fw/applib/ui/layer.c" \
|
||||
" src/fw/applib/ui/bitmap_layer.c" \
|
||||
" tests/fakes/fake_display.c" \
|
||||
" tests/fakes/fake_gbitmap_png.c" \
|
||||
" src/fw/applib/graphics/graphics_private.c" \
|
||||
" src/fw/applib/graphics/graphics_private_raw.c" \
|
||||
" src/fw/applib/graphics/graphics_circle.c" \
|
||||
" src/fw/applib/graphics/graphics_line.c" \
|
||||
" src/fw/applib/graphics/gpath.c"
|
||||
|
||||
graphics_test_sources = [
|
||||
"test_framebuffer.template.c",
|
||||
"test_graphics_draw_pixel.template.c",
|
||||
"test_graphics_draw_stroke.template.c",
|
||||
"test_graphics_draw_rect.template.c",
|
||||
"test_graphics_fill_rect.template.c",
|
||||
"test_graphics_draw_circle.template.c",
|
||||
"test_graphics_stroke_circle.template.c",
|
||||
"test_graphics_fill_circle.template.c",
|
||||
"test_graphics_draw_round_rect.template.c",
|
||||
"test_graphics_gpath.template.c",
|
||||
"test_graphics_gtransform.template.c",
|
||||
"test_graphics_context.template.c",
|
||||
"test_blending.template.c",
|
||||
"test_graphics_draw_stroke_precise.template.c"
|
||||
]
|
||||
|
||||
for platform in ['tintin', 'snowy']:
|
||||
bit_depth = get_bitdepth_for_platform(ctx, platform)
|
||||
defines = ctx.env.test_image_defines
|
||||
for test in graphics_test_sources:
|
||||
generated_source = graphics_process_test_template(ctx, test, bit_depth)
|
||||
clar(ctx,
|
||||
sources_ant_glob=graphics_sources_ant_glob,
|
||||
test_sources=[generated_source],
|
||||
add_includes=[ctx.path.abspath()],
|
||||
defines=defines,
|
||||
platforms=[platform])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob=" src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/graphics/gbitmap.c"
|
||||
" src/fw/applib/graphics/gbitmap_png.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/graphics.c"
|
||||
" src/fw/applib/graphics/graphics_bitmap.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/applib/ui/bitmap_layer.c"
|
||||
" src/fw/applib/ui/layer.c"
|
||||
" src/fw/applib/vendor/tinflate/tinflate.c"
|
||||
" src/fw/applib/vendor/uPNG/upng.c",
|
||||
test_sources_ant_glob='test_graphics_draw_bitmap.c',
|
||||
defines=ctx.env.test_image_defines,
|
||||
platforms=['snowy', 'silk'])
|
||||
|
||||
templated_graphics_draw_text_sources_ant_glob = \
|
||||
" src/fw/applib/graphics/{depth_dir}/framebuffer.c" \
|
||||
" src/fw/applib/graphics/framebuffer.c" \
|
||||
" src/fw/applib/graphics/gcolor_definitions.c" \
|
||||
" src/fw/applib/graphics/gtypes.c" \
|
||||
" src/fw/applib/graphics/gbitmap.c" \
|
||||
" src/fw/applib/graphics/graphics.c" \
|
||||
" src/fw/applib/graphics/gtransform.c" \
|
||||
" src/fw/applib/graphics/bitblt.c" \
|
||||
" src/fw/applib/graphics/{depth_dir}/bitblt_private.c" \
|
||||
" src/fw/applib/ui/layer.c" \
|
||||
" tests/fakes/fake_gbitmap_get_data_row.c" \
|
||||
" tests/fakes/fake_gbitmap_png.c" \
|
||||
" tests/fakes/fake_display.c" \
|
||||
" src/fw/applib/graphics/text_render.c" \
|
||||
" src/fw/applib/graphics/utf8.c" \
|
||||
" src/fw/applib/graphics/text_layout.c" \
|
||||
" src/fw/applib/graphics/perimeter.c" \
|
||||
" src/fw/applib/graphics/text_resources.c" \
|
||||
" src/fw/flash_region/flash_region.c" \
|
||||
" src/fw/flash_region/filesystem_regions.c" \
|
||||
" src/fw/system/hexdump.c" \
|
||||
" src/fw/applib/fonts/codepoint.c" \
|
||||
" src/fw/resource/resource.c" \
|
||||
" src/fw/resource/resource_storage.c" \
|
||||
" src/fw/resource/resource_storage_builtin.c" \
|
||||
" src/fw/resource/resource_storage_file.c" \
|
||||
" src/fw/resource/resource_storage_flash.c" \
|
||||
" src/fw/services/normal/filesystem/flash_translation.c" \
|
||||
" src/fw/services/normal/filesystem/pfs.c" \
|
||||
" src/fw/services/normal/filesystem/app_file.c" \
|
||||
" src/fw/util/crc8.c" \
|
||||
" src/fw/util/legacy_checksum.c" \
|
||||
" src/fw/drivers/flash/flash_crc.c" \
|
||||
" tests/fakes/fake_fonts.c" \
|
||||
" tests/fakes/fake_rtc.c" \
|
||||
" tests/fakes/fake_spi_flash.c" \
|
||||
" tests/fixtures/resources/builtin_resources.auto.c" \
|
||||
" tests/fixtures/resources/pfs_resource_table.c" \
|
||||
" src/fw/applib/graphics/graphics_private.c" \
|
||||
" src/fw/applib/graphics/graphics_private_raw.c" \
|
||||
" src/fw/applib/graphics/graphics_circle.c" \
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
|
||||
graphics_draw_text_test_sources = [
|
||||
"test_graphics_draw_text.template.c"
|
||||
]
|
||||
|
||||
for platform in ['tintin', 'snowy']:
|
||||
bit_depth = 1 if platform == 'tintin' else 8
|
||||
depth_dir = '{}_bit'.format(bit_depth)
|
||||
graphics_sources_ant_glob = templated_graphics_draw_text_sources_ant_glob.format(depth_dir=depth_dir)
|
||||
|
||||
defines = ctx.env.test_image_defines
|
||||
for test in graphics_draw_text_test_sources:
|
||||
generated_source = graphics_process_test_template(ctx, test, bit_depth)
|
||||
clar(ctx,
|
||||
sources_ant_glob=graphics_sources_ant_glob,
|
||||
test_sources=[generated_source],
|
||||
add_includes=[ctx.path.abspath()],
|
||||
defines=defines,
|
||||
override_includes=['dummy_board'],
|
||||
platforms=[platform])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob=templated_graphics_draw_text_sources_ant_glob.format(depth_dir="8_bit"),
|
||||
test_sources_ant_glob='test_graphics_draw_text_flow.c',
|
||||
defines = ctx.env.test_image_defines,
|
||||
override_includes=['dummy_board'])
|
||||
|
||||
|
||||
# All tests that are bit-depth agnostic should follow.
|
||||
clar(ctx,
|
||||
sources_ant_glob = \
|
||||
" src/fw/applib/graphics/gcolor_definitions.c" \
|
||||
" src/fw/applib/graphics/gtypes.c", \
|
||||
test_sources_ant_glob = "test_graphics_colors.c")
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = \
|
||||
" src/fw/applib/graphics/gtypes.c",
|
||||
test_sources_ant_glob = "test_gtypes.c",
|
||||
platforms=["tintin", "snowy", "spalding"])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = \
|
||||
" src/fw/applib/graphics/gtypes.c" \
|
||||
" src/fw/applib/graphics/graphics_circle.c",
|
||||
test_sources_ant_glob = "test_graphics_circle.c")
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = \
|
||||
" src/fw/applib/graphics/gtypes.c" \
|
||||
" tests/fakes/fake_gbitmap_png.c" \
|
||||
" tests/fakes/fake_resource_syscalls.c" \
|
||||
" src/fw/applib/graphics/gbitmap.c",
|
||||
test_sources_ant_glob = "test_gbitmap_data_row_info.c")
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = \
|
||||
" src/fw/applib/graphics/gbitmap.c" \
|
||||
" src/fw/applib/graphics/gtypes.c" \
|
||||
" tests/fakes/fake_applib_resource.c",
|
||||
test_sources_ant_glob = "test_gbitmap_resource_validation.c")
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = \
|
||||
" src/fw/applib/graphics/gtypes.c" \
|
||||
" tests/fakes/fake_gbitmap_png.c" \
|
||||
" tests/fakes/fake_resource_syscalls.c" \
|
||||
" src/fw/applib/graphics/gbitmap.c",
|
||||
test_sources_ant_glob = "test_gbitmap_formats.c",
|
||||
platforms=["tintin", "snowy", "spalding"])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = \
|
||||
" src/fw/applib/graphics/framebuffer.c" \
|
||||
" src/fw/applib/graphics/gbitmap.c" \
|
||||
" src/fw/applib/graphics/8_bit/framebuffer.c" \
|
||||
" src/fw/applib/graphics/gcolor_definitions.c" \
|
||||
" src/fw/applib/graphics/graphics.c" \
|
||||
" src/fw/applib/graphics/graphics_bitmap.c" \
|
||||
" src/fw/applib/graphics/gtypes.c" \
|
||||
" tests/fakes/fake_gbitmap_png.c" \
|
||||
" tests/fakes/fake_resource_syscalls.c" \
|
||||
" tests/fakes/fake_applib_resource.c" \
|
||||
" tests/stubs/stubs_bitblt.c",
|
||||
test_sources_ant_glob = "test_gbitmap_processor.c")
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = \
|
||||
" src/fw/applib/graphics/gtypes.c" \
|
||||
" src/fw/applib/graphics/gcolor_definitions.c" \
|
||||
" src/fw/applib/graphics/gbitmap.c" \
|
||||
" src/fw/applib/graphics/graphics.c" \
|
||||
" src/fw/applib/graphics/graphics_private.c" \
|
||||
" src/fw/applib/graphics/graphics_private_raw.c" \
|
||||
" src/fw/applib/graphics/bitblt.c" \
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c", \
|
||||
test_sources_ant_glob = "test_graphics_blending.c",
|
||||
platforms=['snowy'])
|
||||
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = \
|
||||
" src/fw/applib/graphics/gcolor_definitions.c" \
|
||||
" src/fw/applib/graphics/gdraw_command.c" \
|
||||
" src/fw/applib/graphics/gdraw_command_list.c" \
|
||||
" src/fw/applib/graphics/gdraw_command_image.c" \
|
||||
" src/fw/applib/graphics/gdraw_command_frame.c",
|
||||
test_sources_ant_glob = "test_gdraw_command.c")
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = \
|
||||
" src/fw/applib/graphics/gcolor_definitions.c" \
|
||||
" src/fw/applib/graphics/gdraw_command.c" \
|
||||
" src/fw/applib/graphics/gdraw_command_list.c" \
|
||||
" src/fw/applib/graphics/gdraw_command_frame.c" \
|
||||
" src/fw/applib/graphics/gdraw_command_sequence.c",
|
||||
test_sources_ant_glob = "test_gdraw_command_sequence.c")
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = \
|
||||
" src/fw/applib/graphics/gcolor_definitions.c" \
|
||||
" src/fw/applib/graphics/gbitmap.c" \
|
||||
" src/fw/applib/graphics/gtypes.c" \
|
||||
" src/fw/applib/graphics/gdraw_command.c" \
|
||||
" src/fw/applib/graphics/gdraw_command_list.c" \
|
||||
" src/fw/applib/graphics/gdraw_command_frame.c" \
|
||||
" src/fw/applib/graphics/gdraw_command_image.c" \
|
||||
" src/fw/applib/graphics/gdraw_command_sequence.c" \
|
||||
" tests/fakes/fake_gbitmap_png.c" \
|
||||
" tests/fakes/fake_resource_syscalls.c" \
|
||||
" tests/fakes/fake_applib_resource.c",
|
||||
runtime_deps=ctx.env.test_pdcs,
|
||||
defines=ctx.env.test_image_defines,
|
||||
test_sources_ant_glob = "test_gdraw_command_resources.c")
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = \
|
||||
" src/fw/applib/graphics/8_bit/framebuffer.c" \
|
||||
" src/fw/applib/graphics/framebuffer.c" \
|
||||
" src/fw/applib/graphics/gcolor_definitions.c" \
|
||||
" src/fw/applib/graphics/gtypes.c" \
|
||||
" src/fw/applib/graphics/gbitmap.c" \
|
||||
" src/fw/applib/graphics/graphics.c" \
|
||||
" src/fw/applib/graphics/graphics_private.c" \
|
||||
" src/fw/applib/graphics/graphics_private_raw.c" \
|
||||
" src/fw/applib/graphics/graphics_line.c" \
|
||||
" src/fw/applib/graphics/graphics_circle.c" \
|
||||
" src/fw/applib/graphics/gpath.c" \
|
||||
" src/fw/applib/graphics/bitblt.c" \
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c" \
|
||||
|
||||
" src/fw/applib/ui/animation_interpolate.c" \
|
||||
" src/fw/applib/ui/animation_timing.c" \
|
||||
|
||||
" tests/fakes/fake_gbitmap_png.c" \
|
||||
|
||||
" tests/fw/graphics/weather_app_resources.c" \
|
||||
|
||||
" src/fw/applib/graphics/gdraw_command.c" \
|
||||
" src/fw/applib/graphics/gdraw_command_frame.c" \
|
||||
" src/fw/applib/graphics/gdraw_command_list.c" \
|
||||
" src/fw/applib/graphics/gdraw_command_image.c" \
|
||||
" src/fw/applib/graphics/gdraw_command_transforms.c", \
|
||||
test_sources_ant_glob = "test_gdraw_command_transforms.c",
|
||||
defines=ctx.env.test_image_defines,
|
||||
override_includes=['dummy_board'])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/vendor/uPNG/upng.c"
|
||||
" src/fw/applib/vendor/tinflate/tinflate.c"
|
||||
" src/fw/applib/graphics/gbitmap.c" \
|
||||
" src/fw/applib/graphics/gbitmap_png.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/8_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/graphics/graphics.c"
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/gtypes.c",
|
||||
runtime_deps=ctx.env.pdcs2png_test_files,
|
||||
test_sources_ant_glob="test_pdc.c",
|
||||
defines=ctx.env.test_image_defines)
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/vendor/uPNG/upng.c"
|
||||
" src/fw/applib/vendor/tinflate/tinflate.c"
|
||||
" src/fw/applib/graphics/gbitmap.c" \
|
||||
" src/fw/applib/graphics/gbitmap_png.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/8_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/graphics/graphics.c"
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/gtypes.c",
|
||||
test_sources_ant_glob="test_png.c",
|
||||
defines=ctx.env.test_image_defines,
|
||||
runtime_deps=filter(lambda x: 'test_png__' in str(x), ctx.env.test_pngs))
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/vendor/uPNG/upng.c"
|
||||
" src/fw/applib/vendor/tinflate/tinflate.c"
|
||||
" src/fw/applib/graphics/gbitmap.c" \
|
||||
" src/fw/applib/graphics/gbitmap_png.c"
|
||||
" src/fw/applib/graphics/gbitmap_sequence.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/graphics/graphics.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/board/displays/display_spalding.c"
|
||||
" tests/fakes/fake_resource_syscalls.c",
|
||||
test_sources_ant_glob="test_gbitmap_sequence.c",
|
||||
defines=ctx.env.test_image_defines,
|
||||
runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis,
|
||||
platforms=['tintin', 'spalding'])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/vendor/uPNG/upng.c"
|
||||
" src/fw/applib/vendor/tinflate/tinflate.c"
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/applib/graphics/gbitmap.c" \
|
||||
" tests/fakes/fake_gbitmap_png.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/8_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/graphics.c",
|
||||
test_sources_ant_glob="test_pbi.c",
|
||||
defines=ctx.env.test_image_defines,
|
||||
runtime_deps=filter(lambda x: 'test_pbi__' in str(x), ctx.env.test_pbis))
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/vendor/uPNG/upng.c"
|
||||
" src/fw/applib/vendor/tinflate/tinflate.c"
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/applib/graphics/gbitmap.c" \
|
||||
" src/fw/applib/graphics/gbitmap_png.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/8_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/graphics.c",
|
||||
test_sources_ant_glob="test_palettized_conversion.c",
|
||||
defines=ctx.env.test_image_defines,
|
||||
runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis)
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob=(
|
||||
"src/fw/applib/graphics/gtypes.c "
|
||||
"src/fw/apps/system_apps/timeline/text_node.c "
|
||||
"tests/stubs/stubs_text_layout.c "
|
||||
),
|
||||
test_sources_ant_glob="test_graphics_text_node.c")
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/graphics/perimeter.c"
|
||||
" src/fw/applib/graphics/gtypes.c",
|
||||
test_sources_ant_glob="test_perimeter.c",
|
||||
test_libs=['m'],
|
||||
defines=defines,
|
||||
platforms=['snowy', 'spalding'])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/applib/graphics/gbitmap.c"
|
||||
" tests/fakes/fake_gbitmap_png.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/ui/layer.c"
|
||||
" src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/graphics.c",
|
||||
test_sources_ant_glob="test_graphics_draw_line.c",
|
||||
defines=ctx.env.test_image_defines,
|
||||
runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis,
|
||||
platforms=['tintin', 'snowy'])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/vendor/uPNG/upng.c"
|
||||
" src/fw/applib/vendor/tinflate/tinflate.c"
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/applib/graphics/gbitmap.c" \
|
||||
" src/fw/applib/graphics/gbitmap_png.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/graphics.c",
|
||||
test_sources_ant_glob="test_bitblt_palette_1bit.c",
|
||||
defines=ctx.env.test_image_defines,
|
||||
runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis,
|
||||
platforms=['tintin'])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/vendor/uPNG/upng.c"
|
||||
" src/fw/applib/vendor/tinflate/tinflate.c"
|
||||
" src/fw/applib/graphics/gbitmap.c"
|
||||
" src/fw/applib/graphics/gbitmap_png.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/8_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/graphics/graphics.c"
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/board/displays/display_spalding.c",
|
||||
test_sources_ant_glob="test_framebuffer_duma.c",
|
||||
defines=ctx.env.test_image_defines,
|
||||
platforms=['snowy', 'spalding'])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/applib/graphics/gbitmap.c"
|
||||
" tests/fakes/fake_gbitmap_png.c"
|
||||
" tests/fakes/fake_gbitmap_get_data_row.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/8_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/ui/layer.c"
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/graphics.c",
|
||||
test_sources_ant_glob="test_graphics_draw_core.c",
|
||||
defines=ctx.env.test_image_defines,
|
||||
runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis,
|
||||
platforms=['snowy'])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/applib/graphics/gbitmap.c"
|
||||
" tests/fakes/fake_gbitmap_png.c"
|
||||
" tests/fakes/fake_gbitmap_get_data_row.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/8_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/ui/layer.c"
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/graphics.c"
|
||||
" src/fw/board/displays/display_spalding.c",
|
||||
test_sources_ant_glob="test_framebuffer_clipping.c",
|
||||
defines=ctx.env.test_image_defines,
|
||||
runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis,
|
||||
platforms=['spalding'])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/applib/graphics/gbitmap.c"
|
||||
" tests/fakes/fake_gbitmap_png.c"
|
||||
" tests/fakes/fake_gbitmap_get_data_row.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/ui/layer.c"
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw_mask.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/graphics_mask.c"
|
||||
" src/fw/applib/graphics/graphics.c"
|
||||
" src/fw/applib/ui/window_stack_animation_round.c"
|
||||
" tests/stubs/stubs_animation.c"
|
||||
" src/fw/services/common/compositor/default/compositor_round_flip_transitions.c"
|
||||
" src/fw/applib/ui/animation_interpolate.c"
|
||||
" src/fw/board/displays/display_spalding.c",
|
||||
test_sources_ant_glob="test_graphics_window_stack_animation.c",
|
||||
defines=ctx.env.test_image_defines + ['CAPABILITY_HAS_MASKING=1'],
|
||||
runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis,
|
||||
platforms=['spalding'])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/applib/graphics/gbitmap.c"
|
||||
" tests/fakes/fake_gbitmap_png.c"
|
||||
" tests/fakes/fake_applib_resource.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/ui/layer.c"
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw_mask.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/graphics_mask.c"
|
||||
" src/fw/applib/graphics/graphics.c"
|
||||
" src/fw/board/displays/display_spalding.c",
|
||||
test_sources_ant_glob="test_graphics_context_mask.c",
|
||||
defines=ctx.env.test_image_defines + ['CAPABILITY_HAS_MASKING=1'],
|
||||
runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis,
|
||||
platforms=['snowy'])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/applib/graphics/gbitmap.c"
|
||||
" tests/fakes/fake_gbitmap_png.c"
|
||||
" tests/fakes/fake_gbitmap_get_data_row.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/8_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/ui/layer.c"
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/graphics.c",
|
||||
test_sources_ant_glob="test_graphics_draw_implementation.c",
|
||||
defines=ctx.env.test_image_defines,
|
||||
runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis)
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/vendor/uPNG/upng.c"
|
||||
" src/fw/applib/vendor/tinflate/tinflate.c"
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/applib/graphics/gbitmap.c"
|
||||
" src/fw/applib/graphics/gbitmap_png.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/8_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/graphics.c"
|
||||
" src/fw/board/displays/display_$PLATFORM.c",
|
||||
test_sources_ant_glob="test_bitblt_circular.c",
|
||||
defines=ctx.env.test_image_defines,
|
||||
runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis,
|
||||
platforms=["spalding"])
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/applib/graphics/gbitmap.c"
|
||||
" tests/fakes/fake_gbitmap_png.c"
|
||||
" tests/fakes/fake_gbitmap_get_data_row.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/${BITDEPTH}_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/ui/layer.c"
|
||||
" src/fw/applib/graphics/${BITDEPTH}_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_bitmap.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/graphics.c",
|
||||
test_sources_ant_glob="test_graphics_draw_rotated_bitmap.c",
|
||||
defines=ctx.env.test_image_defines,
|
||||
runtime_deps=ctx.env.test_pngs + ctx.env.test_pbis,
|
||||
platforms=["tintin", "snowy"])
|
||||
|
||||
###############################
|
||||
# 8-bit specific bitblt tests #
|
||||
###############################
|
||||
graphics_sources_ant_glob = \
|
||||
" src/fw/applib/graphics/gcolor_definitions.c" \
|
||||
" src/fw/applib/graphics/8_bit/framebuffer.c" \
|
||||
" src/fw/applib/graphics/framebuffer.c" \
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c" \
|
||||
" src/fw/applib/graphics/gbitmap.c" \
|
||||
" src/fw/applib/graphics/gtypes.c" \
|
||||
" src/fw/applib/graphics/graphics.c" \
|
||||
" src/fw/applib/graphics/graphics_bitmap.c" \
|
||||
" src/fw/applib/graphics/gtransform.c" \
|
||||
" src/fw/applib/graphics/bitblt.c" \
|
||||
" src/fw/applib/ui/layer.c" \
|
||||
" tests/fakes/fake_gbitmap_png.c" \
|
||||
" tests/fakes/fake_display.c" \
|
||||
" src/fw/applib/graphics/graphics_private.c" \
|
||||
" src/fw/applib/graphics/graphics_private_raw.c" \
|
||||
" src/fw/applib/graphics/graphics_circle.c" \
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
|
||||
graphics_test_sources_8bit = [
|
||||
"test_bitblt.c",
|
||||
"test_bitblt_palette.c"
|
||||
]
|
||||
|
||||
for test in graphics_test_sources_8bit:
|
||||
defines = ctx.env.test_image_defines
|
||||
|
||||
clar(ctx,
|
||||
sources_ant_glob = graphics_sources_ant_glob,
|
||||
test_sources_ant_glob = test,
|
||||
defines=defines)
|
||||
|
||||
# vim:filetype=python
|
||||
Reference in New Issue
Block a user