/EffectLayer

Visual Effect Layer for Pebble

Primary LanguageCMIT LicenseMIT

EffectLayer is a visual effect layer for Pebble Smartwatch. User places the layer over screen at set coordinates and special effects are applied to that area of the screen. Effects work both on Aplite/Classic Pebble and Basalt/Pebble time unless specified (e.g. Blur works only on Basalt)

To use the library place the source files in your SRC directory and add #include "effect_layer.h" to your source. EffectLayer library is implemented in efficient pay-to-play way so only effects that you actualy use get compiled into your binary.

Inverter Bitmap Mask Text Mask

Currently supported effects:

  • Invert
  • Invert b/w only
  • Invert brightness, preserve hue
  • Vertical Mirror
  • Horizontal Mirror
  • Rotate 90 degrees (counter- or clock-wise)
  • Blur
  • Zoom
  • Lens
  • Mask
  • FPS
  • Shadow
  • Outline
  • Colorize (replace a color with another)
  • Color Swap

Functions

EffectLayer* effect_layer_create(GRect frame) - creates effect layer and returns pointer to it. Parameter is GRect with coordinates and layer size, for example EffectLayer* effect_layer = effect_layer_create(GRect(0, 0, 144, 168)); will cover entire screen

void effect_layer_destroy(EffectLayer *effect_layer) - destroys effect layer and frees memory.

Layer* effect_layer_get_layer(EffectLayer *effect_layer) - gets underlying Layer of the effect layer for basic manipulations such as addin to Window root layer or layer's changing frame or bounds

Layer* effect_layer_set_frame(EffectLayer *effect_layer, GRect frame) - shortcut to set the frame of the effect layer.

void effect_layer_add_effect(EffectLayer effect_layer, effect_cb effect, void* param) - adds effect to the layer. Parameters are - effect layer that you created previously, name of the function to perform effect, optional data to pass to the effect. User can call this function multiple times on the same effect layer, adding multiple effects, so there's no need to create multiple layers. At this time library has following built in effect functions to represent the effects listed above:

  • effect_invert
  • effect_mirror_vertical
  • effect_mirror_horizontal
  • effect_rotate_90_degrees
  • effect_blur
  • effect_zoom
  • effect_lens
  • effect_mask
  • effect_fps
  • effect_shadow
  • effect_outline
  • effect_colorize
  • effect_colorswap

void effect_layer_remove_effect(EffectLayer *effect_layer) - removes last added effect from the layer. parameters is effect layer that you created previously.

Usage

effect_layer_add_effect(my_effect_layer, effect_invert, NULL); - adds inverter effect

effect_layer_add_effect(my_effect_layer, effect_invert_bw_only, NULL); - Inverts pixels within the layer bounds if the pixels equal GColorBlack or GColorWhite. All other pixels are left untouched allowing for 2.x style black and white themes with splashes of color added for Pebble Times.

effect_layer_add_effect(my_effect_layer, effect_invert_brightness, NULL); - Inverts the perceived brightness of pixels within the layer bounds, except for pixels that equal GColorBlack and GColorWhite (can be used with effect_invert_bw_only to invert black and white pixels). The hue of the pixels is left mostly intact and was initially intended to maintain good contrast of color in cases where an app/watchface has both a light and dark mode with color UI elements without having to have multiple sets of color resources/coding. Works best when color elements are all designed to have good contrast on either a light or dark background and then the effect layer is hidden for that background and shown for the opposite background.

effect_layer_add_effect(my_effect_layer, effect_mirror_vertical, NULL); - adds vertical mirror effect

effect_layer_add_effect(my_effect_layer, effect_mirror_horizontal, NULL); - adds horizontal mirror effect

effect_layer_add_effect(my_effect_layer, effect_rotate_90_degrees, (void *)true); - rotates 90 degrees counterclockwise

effect_layer_add_effect(my_effect_layer, effect_rotate_90_degrees, (void *)false); - rotates 90 degrees clockwise

effect_layer_add_effect(my_effect_layer, effect_blur, (void *)my_radius); - Creates blur effect (on Basalt/Pebble Time only), where my_radius is uint_8t radius of the blur.

effect_layer_add_effect(my_effect_layer, effect_zoom, EL_ZOOM(x,y)); - Zooms screen area under layer by x and y

effect_layer_add_effect(my_effect_layer, effect_lens, EL_LENS(f,d)); - Applies Lens effect, where f & d are focal distance of the lens and distane to object respectfully.

effect_layer_add_effect(my_effect_layer, effect_mask, mask); - Applies Mask, achieving various transparency effects. See this article on usage examples.

effect_layer_add_effect(my_effect_layer, effect_fps, EffectFPS); - displays average FPS of the app

effect_layer_add_effect(my_effect_layer, effect_shadow, &shadow); - adds shadow of a given color to objects on screen of given color. shadow is a parameter of type EffectOffset:

typedef struct {
   GColor orig_color; //color of pixel being ofset
   GColor offset_color; //new color of pixel at offset coords
   int8_t offset_x; // horizontal ofset
   int8_t offset_y; // vertical offset
} EffectOffset;

For example if you want to give all red text under layer a yellow shadow 2 pixels long, you parameter would be:

EffectOffset shadow;
shadow.orig_color = GColorRed;
shadow.offset_color = GColorYellow;
shadow.offset_y = 2;  
shadow.offset_x = 2;

You can specify option in the parameter shadow.option = 1; to display long continuous shadow (to use this on Aplite you also need to specify shadow.aplite_visited array to keep track of set pixels. See https://github.com/ygalanter/Color_Shadow/ watchface for example implementation.

effect_layer_add_effect(my_effect_layer, effect_outline, &shadow); - Similar to the Shadow effect, however the color is added in all directions.

effect_layer_add_effect(my_effect_layer, effect_colorize, &colorpair); - Replaces the first color with the second, useful for 'painting' a b&w watchface. colorpair is a parameter of type EffectColorpair:

typedef struct {
  GColor firstColor;  // first color (target for colorize, one of set in colorswap)
  GColor secondColor; // second color (new color for colorize, other of set in colorswap)
} EffectColorpair;

For example if you want to take your black and white watchface, and make it black and green, your parameter for the above call would be:

EffectColorpair colorpair;
colorpair.firstColor = GColorWhite;
colorpair.secondColor = GColorGreen;

effect_layer_add_effect(my_effect_layer, effect_colorswap, &colorpair); - Similar to the Colorize effect, but also replaces the second color with the first, effectively like a targeted invert (useful for when you have two colors and just want them flipped, but they're either not a direct invert, or you don't want to affect other colors)

Extensions

Library is user-extendable, if you define your function in format

void effect_my_own_cool_effect(GContext* ctx, GRect position, void* param) { /* cool effect code */ }

you can add it to EffectLayer in manner similar to above:

effect_layer_add_effect(my_effect_layer, effect_my_own_cool_effect, param);

Usage examples

Simple Striped watchface uses Mask effect to show background image thru transparent text
TV Time watchaface uses inverter effect for battery info and blur effect for antialiasing fonts
Long Shadow watchface uses shadow effect in "long" mode