#pdr15 - pebble graphics

61
2015 Pebble Developer Retreat Pebble Graphics Matthew Hungerford, Firmware Engineer (Core Graphics)

Upload: pebble-technology

Post on 12-Apr-2017

1.636 views

Category:

Technology


0 download

TRANSCRIPT

2015 Pebble Developer Retreat

Pebble Graphics

● Matthew Hungerford, Firmware Engineer (Core Graphics)

Agenda• 1-Bit Past • Fonts • PNG Images • PDC/SVG Images • APNG Animated Images • Framebuffer Effects • Offscreen Rendering • Animations Vs. Power

Glorious 1 Bit Past

ABCWorking with fonts

Bi-Layer Fonts• Outline provides contrast against varying background colors

• Create a font outline (fontforge)• Load font and font outline• Render both layers to get a bordered font

Convert Hack-Font to Bi-Level

Generate Outline• Expand stroke (easier than outline)• Pen Type Rectangular• 250 width x 250 height

• File->Generate Font• Save as TTF as hack-outline.ttf

Bi-Layer Font Setup//Time Displaychar time_string[] = "12:00";TextLayer* time_outline_layer = NULL;TextLayer* time_text_layer = NULL;static void window_load(Window *window) {... //Setup the time outline display time_outline_layer = text_layer_create(GRect(0, bounds.size.h / 2 - 30 / 2, bounds.size.w, 30)); text_layer_set_text(time_outline_layer, time_string); text_layer_set_font(time_outline_layer, fonts_load_custom_font( resource_get_handle(RESOURCE_ID_FONT_BOXY_OUTLINE_30))); text_layer_set_text_alignment(time_outline_layer, GTextAlignmentCenter); text_layer_set_background_color(time_outline_layer, GColorClear); text_layer_set_text_color(time_outline_layer, GColorBlack); layer_add_child(window_layer, text_layer_get_layer(time_outline_layer)); //Setup the time display time_text_layer = text_layer_create(GRect(0, bounds.size.h / 2 - 30 / 2, bounds.size.w, 30)); text_layer_set_text(time_text_layer, time_string); text_layer_set_font(time_text_layer, fonts_load_custom_font( resource_get_handle(RESOURCE_ID_FONT_BOXY_TEXT_30))); text_layer_set_text_alignment(time_text_layer, GTextAlignmentCenter); text_layer_set_background_color(time_text_layer, GColorClear); text_layer_set_text_color(time_text_layer, GColorWhite); layer_add_child(window_layer, text_layer_get_layer(time_text_layer));}

Hack Time Demo

https://github.com/mhungerford/pebble_hack_font

• 2 Fonts in appinfo.json and resources/ −hack.ttf−hack-outline.ttf

• Should use same string for both layers

Scrims• Fancy name for “Dark Overlays”• Used to draw focus to active content• Improves visibility by increasing contrasts between background and foreground material

Scrims

• Pebble supports semi-transparent bitmaps• Use a 1x1 or 2x2 PNG for mask pattern• Use graphics_draw_bitmap for tiling• Render content on top of scrim

https://github.com/mhungerford/pebble_colorhex

Scrim Font SetupGBitmap *mask = NULL;static void init() {} ... mask = gbitmap_create_with_resource(RESOURCE_ID_MASK);}static void digital_update_proc(Layer *layer, GContext *ctx) { GRect bounds = layer_get_bounds(layer); graphics_context_set_fill_color(ctx, GColorBlack); graphics_context_set_compositing_mode(ctx, GCompOpSet); GPoint box_point = grect_center_point(&bounds); // Size box to width of wday GSize box_size = graphics_text_layout_get_content_size(time_string, lcd_time_font, bounds, GTextOverflowModeWordWrap, GTextAlignmentCenter); box_size.w += 2; // Padding graphics_draw_bitmap_in_rect(ctx, mask, GRect(box_point.x - box_size.w / 2, bounds.origin.y, box_size.w, bounds.size.h)); graphics_context_set_text_color(ctx, GColorCyan); graphics_draw_text(ctx, time_string, lcd_time_font, GRect(bounds.origin.x + 2, bounds.origin.y - 2, bounds.size.w, bounds.size.h - 2), GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);}

Still ImagesWhy PNG8 is so great!

PNG8• Palette format allowing from 1 to 256 colors, including varying levels of transparency (something GIF lacks).

• Filtered to improve compression• Compressed using DEFLATE (aka. zip)• Allows 1,2,4 and 8 bit depths (reducing memory by 8x, 4x, 2x and 1x respectively)

• Compression reduces images from 32.4KB to ~3-8KB

Image Format Comparison• Both PNG and GIF support transparency

• GIF's transparency is 0% or 100%, there is no blending

• GIF selects 1 color to be fully transparent.

• PNG stores transparency per color

• Free and Open Source• Available for Linux, OSX, Windows• Provides:−Scaling & cropping −adding text, borders and shapes−Convert JPG/BMP/GIF to PNG8−Color reduction and dithering

Command Line Image Processing (ImageMagick)

Round Images

# Extend image and make everything outside radius 90 circle transparentconvert my.png -gravity center -extent '180x180' my.png convert -size 180x180 xc:none -fill my.png -draw "circle 90,90 90,1" my.png # Or# Extend image and use pebble hardware mask to make offscreen region transparentconvert my.png -gravity center -extent '180x180' my.png convert my.png -alpha set mask.png -compose DstIn -composite my.png

Mimi Comic• Creative Commons Comic• Over 500 strips were created• Temp mascot for the EFF• Converted using imagemagick• Circle “Coins” for Pebble Time Round• Contrast tweaks for color• Background color detection and extending

• Best of baked in (like xkcd comic)• Additional Daily Comics on Timeline

PDC (SVGs for Pebble)• Provides support for a vector graphics image format on Pebble• Converted from simple SVGs using svg2pdc.py• Allows vector features at runtime such as:−Scaling−Translating−Changing colors−Stretching

Inkscape SVG tutorialBeing free doesn't make it easy!

Download Vector Graphic from Pixabay.com (Creative Commons Art Site)

Open SVG in Inkscape (Free Vector Software)

Select object to remove from SVG

Select object to remove from SVG

Select object to remove from SVG

File→Document Properties: Page : Resize Page to Drawing

Object→Transform : Scale image to fit pebble

Notice image now smaller than page

File→Document Properties: Page : Resize Page to Drawing Again

Edit→Select All : Object→Ungroup

Object→Transform : Disable relative move and apply

Save Image as Plain SVG

SVG to PDC Conversion

git clone http://github.com/pebble-examples/cards-example

git clone http://github.com/pebble-examples/pdc-image

# Create or convert and SVG using Inkscape or Illustrator# Save test.svg in TinySVG 1.1 format with collapsed groups# Copy test.svg to cards-example/toolspython svg2pdc.py test.svg # Watch output values for negative or too large # Copy test.pdc to pdc-image/resources/commands# Update appinfo.json to use test.pdc for DRAW_COMMAND resource pebble build && pebble install --cloud

SVG Robots On Pebble thanks to PDC

Illustrator SVG tutorialTiny… Tiny… TinySVG

Find cool vector art on pixabay.com

Open SVG in illustrator

Scale image to fit screen (<180x180)

Edit→Select all, Object->Ungroup

Object→Artboards→Fit to Selected Art

File→SaveAs : SVG Tiny 1.1, decimal place 1, click “SVG Code”

Verify SVG looks similar to this, no groups within groups, simple paths and basic elements, no translation matrix.Click “OK” to save.

SVG to PDC Conversion

git clone http://github.com/pebble-examples/cards-example

git clone http://github.com/pebble-examples/pdc-image

# Create or convert and SVG using Inkscape or Illustrator# Save test.svg in TinySVG 1.1 format with collapsed groups# Copy test.svg to cards-example/toolspython svg2pdc.py test.svg # Watch output values for negative or too large # Copy test.pdc to pdc-image/resources/commands# Update appinfo.json to use test.pdc for DRAW_COMMAND resource pebble build && pebble install --cloud

SVG Rocket on Watch

Lively WatchfacesIf Time is always moving,

Why isn't your watchface?

APNG (Animated PNG)Pros:• Created by Mozilla as an open, patent free alternative to GIF

• Now supported on iOS natively• Compression and memory benefits of PNG

• Additional frames only require the portion of image that changes

Cons:● APNGs are hard to generate● Very few tools support APNG● Most tools support GIFs

PebbleGIF• Resizes and converts GIF to APNG• Automatically generates simple digital watchface using GIF

• Limited to 144x144 images

• Conversion done using gifsicle, gif2apng (modded) via wscript during build −Cherie is talking about waf later today

• gifsicle is an opensource gif optimizer/resizer• gif2apng modified source is included on repo

https://github.com/mhungerford/pebbleGIF

PebbleGIF Examples

https://github.com/mhungerford/pebbleGIF

Framebuffer EffectsPebble provides APIs to access both the app's main window as a framebuffer, as well as direct access to the pixels in Gbitmaps.

This access lets you go beyond the Pebble APIs for drawing, allowing you to push new graphical concepts into your apps.

https://github.com/mhungerford/pebble_fireworks

Offscreen RenderingSimilar to framebuffer access, EXCEPT this can use pebble drawing commands to draw on offscreen buffers (GBitmap in this case).

This has the benefit that images can be composited over time, while still using Pebble's antialiased line drawing and other graphics functions.

Text Overlays• With offscreen rendering, even text can be rendered to a GBitmap

• Since only bitmaps are currently drawn transparently, this can be used to render text, modify the alpha values, and placed back on the screen in transparent form.

https://github.com/mhungerford/pebble_offscreen_rendering_text_demo

Offscreen Rendering Code static void offscreen_layer_update(Layer* layer, GContext *ctx) { GRect bounds = layer_get_bounds(layer); // Capture the graphics context framebuffer GBitmap *framebuffer = graphics_capture_frame_buffer(ctx); // backup old framebuffer format data uint8_t *orig_addr = gbitmap_get_data(framebuffer); GBitmapFormat orig_format = gbitmap_get_format(framebuffer); uint16_t orig_stride = gbitmap_get_bytes_per_row(framebuffer); // Release the framebuffer now that we are free to modify it graphics_release_frame_buffer(ctx, framebuffer); // Create the offscreen framebuffer GBitmap

GRect offscreen_bounds = GRect(0, 0, 80, 32); GBitmap *offscreen_bitmap = gbitmap_create_blank(offscreen_bounds.size, GBitmapFormat8Bit); // replace screen bitmap with our offscreen render bitmap gbitmap_set_data(framebuffer, gbitmap_get_data(offscreen_bitmap), gbitmap_get_format(offscreen_bitmap),

gbitmap_get_bytes_per_row(offscreen_bitmap), false); // Draw to the offscreen bitmap graphics_draw_text(ctx, "Hello", font, offscreen_bounds, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); // Make the offscreen_bitmap transparent (user provided function) bitmap_make_transparent(offscreen_bitmap); // restore original context bitmap gbitmap_set_data(framebuffer, orig_addr, orig_format, orig_stride, false); // draw the offscreen bitmap rotated 180 deg to the screen to have rotated transparent text graphics_draw_rotated_bitmap(ctx, offscreen_bitmap, grect_center_point(&offscreen_bounds),

TRIG_MAX_ANGLE / 2, grect_center_point(&bounds)); gbitmap_destroy(offscreen_bitmap);}

Animations Vs. PowerWith great power (usage) comes great responsibility.

Flick to Animate

https://github.com/mhungerford/pebble_glancing_demo

• Subscribe to the tap-service• When user flicks wrist for backlight, triggers animations.• Very power conservative, as animations are intentionally triggered.

• Decent user experience, watch only animates when user consciously decides via flick.

Glancing Demo

https://github.com/mhungerford/pebble_glancing_demo

• Allows animations without using flick (tap service)• Simple library modeled after accel_data_service• Similar to gestures, recognizes when watch goes from hanging downward to being looked at, or wrist rotation away and back

• Triggers callback for glancing state that can be used for animations, seconds hands, unpausing game, or data refresh from web.

• Can control backlight

Questions?