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