advanced techniques: graphics | pebble developer retreat 2014

64
GRAPHICS MATT & HEIKO – DEVELOPER EXPERIENCE ENGINEERS ON TWITTER AS @MTHUNGERFORD & @HBEHRENS

Upload: pebble-technology

Post on 07-Jul-2015

969 views

Category:

Technology


0 download

DESCRIPTION

You can find the video recording here: https://www.youtube.com/watch?v=lYoHh19RNy4 Heiko Behrens and Matthew Hungerford present advanced programming techniques for Pebble. This presentation focused on graphics techniques including run-time dithering, offline dithering, pixel manipulations, and frame-buffer drawing. This talk featured the amiga boing ball dithering demo. Day 1 - Video 3B

TRANSCRIPT

Page 1: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

GRAPHICS

MATT & HEIKO – DEVELOPER EXPERIENCE ENGINEERSON TWITTER AS @MTHUNGERFORD & @HBEHRENS

Page 2: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

THE DISPLAY

144x168px @ 175 DPI

black & white only

25+ FPS

Page 3: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

A Pixel’s Journey Dithering

Accessing the Frame Buffer

CONTENT

Page 4: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

A PIXEL’S JOURNEY

Page 5: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

LAYER STACK

Layers are added to the layer_stack from back to front starting from the window layer. !

Layers are then rendered from back to front into the application frame buffer.

Window

Page 6: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

THE COMPOSITOR

Example of a non-fullscreen application (144x152):

Application frame buffer

Compositor frame buffer

Finaldisplay

Page 7: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

GRAPHICS APIS

graphics_draw_text, graphics_draw_bitmap_in_rectLayer-equivalents working directly on GContext

gpath_draw_filled, gpath_draw_outlineDrawings vector graphics

graphics_draw_pixel, graphics_draw_line graphics_draw_circle, graphics_fill_circle, graphics_draw_rect, graphics_draw_round_rect, graphics_fill_rect

Drawings raster graphics

app_timer_register, app_timer_reschedule, app_timer_cancel Animation, PropertyAnimation

Animate graphics

static void custom_update_proc(Layer *layer, GContext *ctx);

Page 8: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Q&A

source: https://www.youtube.com/watch?v=Ej2ELEK-C3Q Where To?

Page 9: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

GRAPHICS APIS

graphics_draw_text, graphics_draw_bitmap_in_rectLayer-equivalents working directly on GContext

gpath_draw_filled, gpath_draw_outlineDrawings vector graphics

graphics_draw_pixel, graphics_draw_line graphics_draw_circle, graphics_fill_circle, graphics_draw_rect, graphics_draw_round_rect, graphics_fill_rect

Drawings

app_timer_register, app_timer_reschedule, app_timer_cancel Animation, PropertyAnimation

Animate graphics

raster graphics

static void custom_update_proc(Layer *layer, GContext *ctx);

Page 10: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

graphics_capture_frame_buffer, graphics_release_frame_buffer

Doing dithering at runtime

Drawings raster graphicsadvanced

Page 11: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

DITHERING

Page 12: Advanced Techniques: Graphics | Pebble Developer Retreat 2014
Page 13: Advanced Techniques: Graphics | Pebble Developer Retreat 2014
Page 14: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Dither is an intentionally applied form of noise used to randomize quantization error, preventing large-scale patterns such as color banding in images.http://en.wikipedia.org/wiki/Dither

256 shades of gray

16 shades of gray + nearest color

16 shades of gray + random noise

16 shades of gray + ordered dithering

Page 15: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

black & white + nearest color

black & white + noise

black & white + ordered dithering

16 shades of gray + ordered dithering

BLACK AND WHITE

16 shades of gray + random noise16 shades of gray + nearest color

256 shades of gray

16 SHADES OF GRAY

Page 16: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

original ordered dithering Floyd-Steinbergdithering

nearest color

Page 17: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

for each y from top to bottom for each x from left to right oldpixel := pixel[x][y] newpixel := find_closest_palette_color(oldpixel) pixel[x][y] := newpixel quant_error := oldpixel - newpixel pixel[x+1][y ] := pixel[x+1][y ] + quant_error * 7/16 pixel[x-1][y+1] := pixel[x-1][y+1] + quant_error * 3/16 pixel[x ][y+1] := pixel[x ][y+1] + quant_error * 5/16 pixel[x+1][y+1] := pixel[x+1][y+1] + quant_error * 1/16

FLOYD-STEINBERG DITHERING

foreach y foreach x oldpixel := pixel[x][y] + threshold_map_4x4[x mod 4][y mod 4] newpixel := find_closest_palette_color(oldpixel) pixel[x][y] := newpixel

threshold_map_4x4 =

ORDERED DITHERING (BAYER MATRIX)

Page 18: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

source: Joel Yliluoma’s dithering page - http://bisqwit.iki.fi/story/howto/dither/jy/ original from PSX game “Chrono Cross”

Page 19: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

source: Joel Yliluoma’s dithering page - http://bisqwit.iki.fi/story/howto/dither/jy/ nearest neighbor to 12 colors

Page 20: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

source: Joel Yliluoma’s dithering page - http://bisqwit.iki.fi/story/howto/dither/jy/ Floyd-Steinberg to 12 colors

Page 21: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

OFFLINE DITHERING

Page 22: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

BETTER BITMAPS (GIMP/PHOTOSHOP)

Resize to desired size (<144x168) using sinc filter Convert image to grayscale Modify brightness/contrast to sharpen image Posterize to 3 colors (if dithering to gray_50) or 2 colors for black and white. Undo-Redo brightness/contrast & posterize till desired effect Convert image to black and white with ordered (positional) dithering

Page 23: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Better Bitmaps (GIMP/Photoshop)Open Original Image in the GIMP/Photoshop

Page 24: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Better Bitmaps (GIMP/Photoshop)Resize: Image -> Scale Image

Page 25: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Better Bitmaps (GIMP/Photoshop)Resize to desired size (<144x168) using sinc filter

Page 26: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Better Bitmaps (GIMP/Photoshop)Grayscale: Image -> Mode -> Grayscale

Page 27: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Better Bitmaps (GIMP/Photoshop)Brightness-Contrast: Colors -> Brightness-Contrast

Page 28: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Better Bitmaps (GIMP/Photoshop)Slide contrast to right to remove colors and balance with brightness

Page 29: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Better Bitmaps (GIMP/Photoshop)Posterize: Colors -> Posterize

Page 30: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Better Bitmaps (GIMP/Photoshop)Posterize: Select 3 colors (2 for black & white only). 3rd color is Gray.

Page 31: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Better Bitmaps (GIMP/Photoshop)Convert to 1-bit (b&w): Image -> Mode -> Indexed

Page 32: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Better Bitmaps (GIMP/Photoshop)Select b&w, choose dithering Positioned (Ordered) for dithered gray

Page 33: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Better Bitmaps (GIMP/Photoshop)Final Product: Robot with dithered gray_50

Page 34: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

BETTER BITMAPS (IMAGEMAGICK)

convert orig.png -adaptive-resize '144x168>' -fill '#FFFFFF00' -opaque none -type Grayscale -colorspace Gray -black-threshold 30% -white-threshold 70% -ordered-dither 2x1 -colors 2 -depth 1 -define png:compression-level=9 -define png:compression-strategy=0 -define png:exclude-chunk=all new.png

Page 35: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

DITHERED ANIMATIONS (APNG)

ImageMagick scripting !

Ordered dithering (gray_50) !

(a)PNG compression for 200 frames !

Resource loading and drawing at high FPS (10 FPS in this example)

Page 36: Advanced Techniques: Graphics | Pebble Developer Retreat 2014
Page 37: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

RUNTIME DITHERING

Page 38: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

50% GRAY

Page 39: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

PRE-DITHERED

FLOYD STEINBERG

ORDERED DITHERING

Page 40: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

PRE-DITHERED

AT RUNTIME

Page 41: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

PRE-DITHERED

AT RUNTIME

Page 42: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

PRE-DITHERED

AT RUNTIME

Page 43: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

PRE-DITHERED

AT RUNTIME

Page 44: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

PRE-DITHERED

AT RUNTIME

Page 45: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

50% GRAY

Page 46: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

PRE-DITHERED

FLOYD STEINBERG

ORDERED DITHERING

Page 47: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

source: Joel Yliluoma’s dithering page - http://bisqwit.iki.fi/story/howto/dither/jy/ reduction to 18 colors

Page 48: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

IMPLEMENTDITHERING

AT RUNTIME

Page 49: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

foreach y foreach x oldpixel := pixel[x][y] + threshold_map_4x4[x mod 4][y mod 4] newpixel := find_closest_palette_color(oldpixel) pixel[x][y] := newpixel

threshold_map_4x4 =

ORDERED DITHERING (BAYER MATRIX)

foreach y foreach x newpixel := pixel[x][y] > threshold_map_8x8[x mod 8][y mod 8] ? white : black; pixel[x][y] := newpixel

ORDERED DITHERING BLACK & WHITE

Page 50: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

foreach y foreach x newpixel := pixel[x][y] > threshold_map_8x8[x mod 8][y mod 8] ? white : black; pixel[x][y] := newpixel

ORDERED DITHERING BLACK & WHITE

IMPLEMENTED IN C// Bayer matrix for ordered dithering static const uint8_t ditherMatrix[8][8] = { { 0*4, 32*4, 8*4, 40*4, 2*4, 34*4, 10*4, 42*4}, {48*4, 16*4, 56*4, 24*4, 50*4, 18*4, 58*4, 26*4}, {12*4, 44*4, 4*4, 36*4, 14*4, 46*4, 6*4, 38*4}, {60*4, 28*4, 52*4, 20*4, 62*4, 30*4, 54*4, 22*4}, { 3*4, 35*4, 11*4, 43*4, 1*4, 33*4, 9*4, 41*4}, {51*4, 19*4, 59*4, 27*4, 49*4, 17*4, 57*4, 25*4}, {15*4, 47*4, 7*4, 39*4, 13*4, 45*4, 5*4, 37*4}, {63*4, 31*4, 55*4, 23*4, 61*4, 29*4, 53*4, 21*4} }; !static GColor color_for_gray(int16_t x, int16_t y, uint8_t gray) { return gray > ditherMatrix[y % 8][x % 8] ? GColorWhite : GColorBlack; }

Page 51: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

static GColor color_for_gray(int16_t x, int16_t y, uint8_t gray); !static void update_proc_draw_pixel(Layer *layer, GContext *ctx) {

!!!!!!!}

const int16_t h = window_size.h; for (int16_t y = 0; y < h; y++) {

!!!! }

uint8_t row_gray = (uint8_t)((gray_top * (h - y) + y * gray_bottom) / h);

DOING LIVE DITHERINGgray_top

gray_bottom

Page 52: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

AVOIDING FLOATS frac = 0.0 gray_top * (1 - 0.0) + 0.0 * gray_bottom

frac = 0.25 gray_top * (1 - 0.25) + 0.25 * gray_bottom

frac = 0.75 gray_top * (1 - 0.75) + 0.75 * gray_bottom

frac = 1.0 gray_top * (1 - 1) + 1.0 * gray_bottom

float frac = (float)y / h; // 0.0 … 1.0 row_gray = gray_top * (1 - frac) + frac * gray_bottom

Page 53: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

AVOIDING FLOATS

// 1 * h = h // frac * h = y

uint8_t row_gray = (uint8_t)((gray_top * (h - y) + y * gray_bottom) / h);

float frac = (float)y / h; // 0.0 … 1.0 row_gray = gray_top * (1 - frac) + frac * gray_bottom

Page 54: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

static GColor color_for_gray(int16_t x, int16_t y, uint8_t gray); !static void update_proc_draw_pixel(Layer *layer, GContext *ctx) {

!!!!!!!}

const int16_t h = window_size.h; for (int16_t y = 0; y < h; y++) {

!!!! }

uint8_t row_gray = (uint8_t)((gray_top * (h - y) + y * gray_bottom) / h);

DOING LIVE DITHERINGgray_top

gray_bottom

Page 55: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

DOING LIVE DITHERINGgray_top

gray_bottom

for (int16_t x = 0; x < window_size.w; x++) { graphics_context_set_stroke_color(ctx, color_for_gray(x, y, row_gray)); graphics_draw_pixel(ctx, GPoint(x, y)); }

uint8_t row_gray = (uint8_t)((gray_top * (h - y) + y * gray_bottom) / h);

const int16_t h = window_size.h; for (int16_t y = 0; y < h; y++) {

!!!! }

static GColor color_for_gray(int16_t x, int16_t y, uint8_t gray); !static void update_proc_draw_pixel(Layer *layer, GContext *ctx) {

!!!!!!!}

Page 56: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

source: https://www.youtube.com/watch?v=V5O8gj0SQZ0

Page 57: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

DOING FAST(!) LIVE DITHERING

* graphics_capture_frame_buffer(GContext* ctx)

bool graphics_release_frame_buffer(GContext* ctx, GBitmap* buffer)

GBitmap

Gives you a GBitmap to represent the current frame buffer

…and locks out any other graphic function until you release it

Page 58: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

GBITMAP RECAP

0

4

8

12

16

29px

4 bytes per row

5px

unused

unused

unused

unused

unused

Fragment of 8 pixels in a row, storedin 8 bits or 1 byte (with value 0x5C).0 1 0 1 1 1 0 0

Fragment of 8 pixels in a row, storedin 8 bits or 1 byte (with value 0x5C).0 1 0 1 1 1 0 0

typedef struct { void *addr; uint16_t row_size_bytes; // ... GRect bounds; } GBitmap;

GBitmap

uint8_t *byte_offset ; byte_offset += x / 8;

= (uint8_t*)bmp->addr

byte_offset *= y * bmp->row_size_bytes;

Page 59: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

DOING FAST(!) LIVE DITHERING

static void update_proc_frame_buffer(Layer *layer, GContext *ctx) { GBitmap *fb = graphics_capture_frame_buffer(ctx); !!!!!!!!!!!!! graphics_release_frame_buffer(ctx, fb); }

uint8_t *row = (uint8_t *)fb->addr; row += fb->bounds.origin.y * fb->row_size_bytes; !!!!!!!!!!

const int16_t h = fb->bounds.size.h; for (int16_t y = fb->bounds.origin.y; y < h; y++) { uint8_t row_gray_value = (uint8_t)((gray_top * (h - y) + y * gray_bottom) / h); !!!! row += fb->row_size_bytes; }

uint8_t row_gray_dither_pattern = (y, row_gray_value); dither_pattern_8

Page 60: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

RENDER 8 PIXELS AT ONCE

static uint8_t (int16_t y, uint8_t gray) { return color_for_gray(0, y, gray) << 7 | color_for_gray(1, y, gray) << 6 | color_for_gray(2, y, gray) << 5 | color_for_gray(3, y, gray) << 4 | color_for_gray(4, y, gray) << 3 | color_for_gray(5, y, gray) << 2 | color_for_gray(6, y, gray) << 1 | color_for_gray(7, y, gray) << 0;}

dither_pattern_8

Page 61: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

DOING FAST(!) LIVE DITHERING

static void update_proc_frame_buffer(Layer *layer, GContext *ctx) { GBitmap *fb = graphics_capture_frame_buffer(ctx); !!!!!!!!!!!!! graphics_release_frame_buffer(ctx, fb); }

uint8_t *row = (uint8_t *)fb->addr; row += fb->bounds.origin.y * fb->row_size_bytes; !!!!!!!!!!

const int16_t h = fb->bounds.size.h; for (int16_t y = fb->bounds.origin.y; y < h; y++) { uint8_t row_gray_value = (uint8_t)((gray_top * (h - y) + y * gray_bottom) / h); !!!! row += fb->row_size_bytes; }

uint8_t row_gray_dither_pattern = (y, row_gray_value); dither_pattern_8 memset(row, row_gray_dither_pattern, fb->row_size_bytes);

Page 62: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

source: https://www.youtube.com/watch?v=V5O8gj0SQZ0

Page 63: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

BOING BALL

Page 64: Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Q&A

source: https://www.youtube.com/watch?v=Z2QMG9UwXI0