mirror of
https://github.com/google/pebble
synced 2026-10-18 09:14:11 +02:00
Import of the watch repository from Pebble
This commit is contained in:
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* 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 "transform.h"
|
||||
|
||||
#include "applib/graphics/gdraw_command_private.h"
|
||||
#include "applib/graphics/gdraw_command_transforms.h"
|
||||
#include "applib/ui/animation.h"
|
||||
#include "applib/ui/animation_interpolate.h"
|
||||
#include "applib/ui/animation_timing.h"
|
||||
#include "applib/ui/kino/kino_reel.h"
|
||||
#include "applib/ui/kino/kino_reel_pdci.h"
|
||||
#include "applib/ui/kino/kino_reel_custom.h"
|
||||
#include "applib/applib_malloc.auto.h"
|
||||
#include "syscall/syscall.h"
|
||||
#include "util/math.h"
|
||||
#include "util/net.h"
|
||||
#include "util/struct.h"
|
||||
|
||||
typedef struct {
|
||||
GRect layer_frame;
|
||||
GRect from;
|
||||
GRect to;
|
||||
|
||||
const TransformImpl *impl;
|
||||
void *context;
|
||||
|
||||
int32_t normalized;
|
||||
uint32_t elapsed;
|
||||
uint32_t duration;
|
||||
|
||||
KinoReel *from_reel;
|
||||
KinoReel *to_reel;
|
||||
|
||||
GDrawCommandList *list_copy;
|
||||
GSize list_copy_size;
|
||||
|
||||
bool owns_from_reel;
|
||||
bool owns_to_reel;
|
||||
bool global;
|
||||
} KinoReelTransformData;
|
||||
|
||||
static bool prv_is_currently_from(KinoReelTransformData *data) {
|
||||
return (!data->to_reel || (data->normalized < ANIMATION_NORMALIZED_MAX / 2));
|
||||
}
|
||||
|
||||
static KinoReel *prv_get_current_reel(KinoReelTransformData *data) {
|
||||
return prv_is_currently_from(data) ? data->from_reel : data->to_reel;
|
||||
}
|
||||
|
||||
static GSize prv_get_current_size(KinoReelTransformData *data) {
|
||||
return prv_is_currently_from(data) ? data->from.size : data->to.size;
|
||||
}
|
||||
|
||||
static GPoint prv_get_interpolated_origin(KinoReelTransformData *data) {
|
||||
const GSize size = prv_get_current_size(data);
|
||||
const GPoint offset = interpolate_gpoint(data->normalized, grect_center_point(&data->from),
|
||||
grect_center_point(&data->to));
|
||||
return gpoint_sub(offset, GPoint(size.w / 2, size.h / 2));
|
||||
}
|
||||
|
||||
static bool prv_image_size_eq_rect_size(KinoReelTransformData *data, GRect rect) {
|
||||
GSize size = kino_reel_get_size(prv_get_current_reel(data));
|
||||
return gsize_equal(&size, &rect.size);
|
||||
}
|
||||
|
||||
static void prv_free_list_copy(KinoReelTransformData *data) {
|
||||
applib_free(data->list_copy);
|
||||
data->list_copy = NULL;
|
||||
}
|
||||
|
||||
static GDrawCommandList *prv_get_or_create_list_copy(KinoReelTransformData *data,
|
||||
GDrawCommandList *list) {
|
||||
if (data->list_copy && (gdraw_command_list_get_data_size(data->list_copy) >=
|
||||
gdraw_command_list_get_data_size(list))) {
|
||||
return data->list_copy;
|
||||
}
|
||||
prv_free_list_copy(data);
|
||||
data->list_copy = gdraw_command_list_clone(list);
|
||||
return data->list_copy;
|
||||
}
|
||||
|
||||
static void prv_destructor(KinoReel *reel) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (data->impl->destructor) {
|
||||
data->impl->destructor(data->context);
|
||||
}
|
||||
if (data->owns_from_reel) {
|
||||
kino_reel_destroy(data->from_reel);
|
||||
}
|
||||
if (data->owns_to_reel) {
|
||||
kino_reel_destroy(data->to_reel);
|
||||
}
|
||||
gdraw_command_list_destroy(data->list_copy);
|
||||
applib_free(data);
|
||||
}
|
||||
|
||||
static uint32_t prv_elapsed_getter(KinoReel *reel) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
return scale_int32(data->normalized, ANIMATION_NORMALIZED_MAX, data->duration);
|
||||
}
|
||||
|
||||
static uint32_t prv_get_duration(KinoReelTransformData *data) {
|
||||
uint32_t duration = data->duration;
|
||||
if (data->from_reel) {
|
||||
const uint32_t from_duration = kino_reel_get_duration(data->from_reel);
|
||||
// If we don't have a 'to_reel' then we are looping back to the 'from_reel' so it's okay for
|
||||
// the 'from_reel' to have an infinite duration
|
||||
//
|
||||
// If we have a 'to_reel' then ignore infinite duration requests because we will never get to
|
||||
// it and burn a lot of power along the way!
|
||||
if ((data->to_reel == NULL) || (from_duration != PLAY_DURATION_INFINITE)) {
|
||||
duration = MAX(duration, from_duration);
|
||||
}
|
||||
}
|
||||
|
||||
if (data->to_reel) {
|
||||
// We want to make sure the transform duration is at least as long as the to_reel duration
|
||||
// so that the resource transition runs to completion if it's animated
|
||||
const uint32_t to_duration = kino_reel_get_duration(data->to_reel);
|
||||
duration = MAX(duration, to_duration);
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
static bool prv_elapsed_setter(KinoReel *reel, uint32_t elapsed) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (data->elapsed == elapsed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
|
||||
data->elapsed = elapsed;
|
||||
if (data->from_reel && kino_reel_set_elapsed(data->from_reel, elapsed)) {
|
||||
changed = true;
|
||||
}
|
||||
if (data->to_reel && kino_reel_set_elapsed(data->to_reel, elapsed)) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
const int32_t normalized = animation_timing_clip(
|
||||
scale_int32(elapsed, data->duration, ANIMATION_NORMALIZED_MAX));
|
||||
if (data->normalized == normalized) {
|
||||
return changed;
|
||||
}
|
||||
|
||||
data->normalized = normalized;
|
||||
|
||||
// No position setter is shorthand for always triggering a transform on any position setting
|
||||
bool transform_changed = true;
|
||||
if (data->impl->position_setter &&
|
||||
!data->impl->position_setter(normalized, data->context)) {
|
||||
transform_changed = false;
|
||||
}
|
||||
|
||||
return (changed || transform_changed);
|
||||
}
|
||||
|
||||
static uint32_t prv_duration_getter(KinoReel *reel) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
return prv_get_duration(data);
|
||||
}
|
||||
|
||||
static GSize prv_size_getter(KinoReel *reel) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
return interpolate_gsize(data->normalized, data->from.size, data->to.size);
|
||||
}
|
||||
|
||||
static void prv_transform_list(KinoReelTransformData *data) {
|
||||
KinoReel *reel = prv_get_current_reel(data);
|
||||
GDrawCommandList *source_list = kino_reel_get_gdraw_command_list(reel);
|
||||
GDrawCommandList *list = prv_get_or_create_list_copy(data, source_list);
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
if (!gdraw_command_list_copy(list, gdraw_command_list_get_data_size(source_list),
|
||||
source_list)) {
|
||||
return;
|
||||
}
|
||||
if (data->impl->apply) {
|
||||
const GSize size = kino_reel_get_size(reel);
|
||||
data->impl->apply(list, size, &data->from, &data->to, data->normalized, data->context);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_draw_command_list_processed(GContext *ctx, GDrawCommandList *list, GPoint offset,
|
||||
KinoReelProcessor *processor) {
|
||||
GPoint draw_box_origin = ctx->draw_state.drawing_box.origin;
|
||||
graphics_context_move_draw_box(ctx, offset);
|
||||
gdraw_command_list_draw_processed(ctx, list, NULL_SAFE_FIELD_ACCESS(processor,
|
||||
draw_command_processor,
|
||||
NULL));
|
||||
ctx->draw_state.drawing_box.origin = draw_box_origin;
|
||||
}
|
||||
|
||||
static void prv_draw_reel_or_command_list_processed(
|
||||
GContext *ctx, KinoReel *reel, GDrawCommandList *list, GPoint offset,
|
||||
KinoReelProcessor *processor) {
|
||||
if (list) {
|
||||
prv_draw_command_list_processed(ctx, list, offset, processor);
|
||||
} else {
|
||||
kino_reel_draw_processed(reel, ctx, offset, processor);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_draw_processed_in_local(KinoReelTransformData *data, GContext *ctx, GPoint offset,
|
||||
KinoReelProcessor *processor) {
|
||||
KinoReel *reel = prv_get_current_reel(data);
|
||||
GDrawCommandList *source_list = kino_reel_get_gdraw_command_list(reel);
|
||||
GDrawCommandList *list = prv_get_or_create_list_copy(data, source_list);
|
||||
if (data->global) {
|
||||
offset = gpoint_sub(GPointZero, data->layer_frame.origin);
|
||||
}
|
||||
prv_draw_reel_or_command_list_processed(ctx, reel, list, offset, processor);
|
||||
}
|
||||
|
||||
static void prv_draw_processed_in_global(KinoReelTransformData *data, GContext *ctx, GPoint offset,
|
||||
KinoReelProcessor *processor) {
|
||||
KinoReel *reel = prv_get_current_reel(data);
|
||||
GDrawCommandList *source_list = kino_reel_get_gdraw_command_list(reel);
|
||||
GDrawCommandList *list = prv_get_or_create_list_copy(data, source_list);
|
||||
offset = gpoint_to_local_coordinates(GPointZero, ctx);
|
||||
if (!list) {
|
||||
// There is no list with global coordinates embedded. Instead, interpolate the offset.
|
||||
gpoint_add_eq(&offset, prv_get_interpolated_origin(data));
|
||||
}
|
||||
prv_draw_reel_or_command_list_processed(ctx, reel, list, offset, processor);
|
||||
}
|
||||
|
||||
static void prv_draw_processed_at_rect(KinoReelTransformData *data, GContext *ctx, GPoint offset,
|
||||
GRect rect, KinoReelProcessor *processor) {
|
||||
if (!prv_image_size_eq_rect_size(data, rect)) {
|
||||
prv_transform_list(data);
|
||||
prv_draw_processed_in_local(data, ctx, offset, processor);
|
||||
return;
|
||||
}
|
||||
prv_free_list_copy(data);
|
||||
if (data->global) {
|
||||
offset = gpoint_sub(GPointZero, data->layer_frame.origin);
|
||||
}
|
||||
offset = gpoint_add(offset, rect.origin);
|
||||
KinoReel *reel = prv_get_current_reel(data);
|
||||
GDrawCommandList *source_list = kino_reel_get_gdraw_command_list(reel);
|
||||
prv_draw_reel_or_command_list_processed(ctx, reel, source_list, offset, processor);
|
||||
}
|
||||
|
||||
static void prv_draw_processed_func(KinoReel *reel, GContext *ctx, GPoint offset,
|
||||
KinoReelProcessor *processor) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
|
||||
if (data->normalized == 0) {
|
||||
prv_draw_processed_at_rect(data, ctx, offset, data->from, processor);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->normalized == ANIMATION_NORMALIZED_MAX) {
|
||||
prv_draw_processed_at_rect(data, ctx, offset, data->to, processor);
|
||||
return;
|
||||
}
|
||||
|
||||
prv_transform_list(data);
|
||||
|
||||
if (data->global) {
|
||||
prv_draw_processed_in_global(data, ctx, offset, processor);
|
||||
} else {
|
||||
prv_draw_processed_in_local(data, ctx, offset, processor);
|
||||
}
|
||||
}
|
||||
|
||||
static GDrawCommandList *prv_get_gdraw_command_list(KinoReel *reel) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (data) {
|
||||
KinoReel *reel = prv_get_current_reel(data);
|
||||
return kino_reel_get_gdraw_command_list(reel);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const KinoReelImpl s_kino_reel_impl_transform = {
|
||||
.destructor = prv_destructor,
|
||||
.get_elapsed = prv_elapsed_getter,
|
||||
.set_elapsed = prv_elapsed_setter,
|
||||
.get_duration = prv_duration_getter,
|
||||
.get_size = prv_size_getter,
|
||||
.draw_processed = prv_draw_processed_func,
|
||||
.get_gdraw_command_list = prv_get_gdraw_command_list,
|
||||
};
|
||||
|
||||
KinoReel *kino_reel_transform_create(const TransformImpl *impl, void *context) {
|
||||
KinoReelTransformData *data = applib_malloc(sizeof(KinoReelTransformData));
|
||||
if (!data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*data = (KinoReelTransformData) {
|
||||
.impl = impl,
|
||||
.context = context,
|
||||
.duration = ANIMATION_DEFAULT_DURATION_MS,
|
||||
};
|
||||
|
||||
KinoReel *reel = kino_reel_custom_create(&s_kino_reel_impl_transform, data);
|
||||
if (!reel) {
|
||||
applib_free(data);
|
||||
}
|
||||
return reel;
|
||||
}
|
||||
|
||||
void *kino_reel_transform_get_context(KinoReel *reel) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (data) {
|
||||
return data->context;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void kino_reel_transform_set_from_reel(KinoReel *reel, KinoReel *from_reel,
|
||||
bool take_ownership) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
if (data->owns_from_reel) {
|
||||
kino_reel_destroy(data->from_reel);
|
||||
}
|
||||
data->from_reel = from_reel;
|
||||
data->owns_from_reel = take_ownership;
|
||||
prv_free_list_copy(data);
|
||||
}
|
||||
|
||||
KinoReel *kino_reel_transform_get_from_reel(KinoReel *reel) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (data) {
|
||||
return data->from_reel;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void kino_reel_transform_set_to_reel(KinoReel *reel, KinoReel *to_reel,
|
||||
bool take_ownership) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
if (data->owns_to_reel) {
|
||||
kino_reel_destroy(data->to_reel);
|
||||
}
|
||||
data->to_reel = to_reel;
|
||||
data->owns_to_reel = take_ownership;
|
||||
prv_free_list_copy(data);
|
||||
}
|
||||
|
||||
KinoReel *kino_reel_transform_get_to_reel(KinoReel *reel) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (data) {
|
||||
return data->to_reel;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void kino_reel_transform_set_layer_frame(KinoReel *reel, GRect layer_frame) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (data) {
|
||||
data->layer_frame = layer_frame;
|
||||
}
|
||||
}
|
||||
|
||||
void kino_reel_transform_set_from_frame(KinoReel *reel, GRect from) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (data) {
|
||||
data->from = from;
|
||||
}
|
||||
}
|
||||
|
||||
void kino_reel_transform_set_to_frame(KinoReel *reel, GRect to) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (data) {
|
||||
data->to = to;
|
||||
}
|
||||
}
|
||||
|
||||
GRect kino_reel_transform_get_from_frame(KinoReel *reel) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (data) {
|
||||
return data->from;
|
||||
}
|
||||
return GRectZero;
|
||||
}
|
||||
|
||||
GRect kino_reel_transform_get_to_frame(KinoReel *reel) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (data) {
|
||||
return data->to;
|
||||
}
|
||||
return GRectZero;
|
||||
}
|
||||
|
||||
void kino_reel_transform_set_global(KinoReel *reel, bool global) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (data) {
|
||||
data->global = global;
|
||||
}
|
||||
}
|
||||
|
||||
void kino_reel_transform_set_transform_duration(KinoReel *reel, uint32_t duration) {
|
||||
KinoReelTransformData *data = kino_reel_custom_get_data(reel);
|
||||
if (data) {
|
||||
data->duration = duration;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user