mirror of
https://github.com/google/pebble.git
synced 2025-03-22 03:32:20 +00:00
345 lines
14 KiB
C
345 lines
14 KiB
C
/*
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "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));
|
|
}
|
|
}
|