SlideShare a Scribd company logo
1 of 64
Download to read offline
GRAPHICS 
MATT & HEIKO – DEVELOPER EXPERIENCE ENGINEERS 
ON TWITTER AS @MTHUNGERFORD & @HBEHRENS
THE DISPLAY 
144x168px @ 175 DPI 
black & white only 
25+ FPS
CONTENT 
A Pixel’s Journey 
Dithering 
Accessing the Frame Buffer
A PIXEL’S 
JOURNEY
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
THE COMPOSITOR 
Example of a non-fullscreen application (144x152): 
Application 
frame buffer 
Compositor 
frame buffer 
Final 
display
GRAPHICS APIS 
static void custom_update_proc(Layer *layer, GContext *ctx); 
Layer-equivalents working directly on GContext 
graphics_draw_text, graphics_draw_bitmap_in_rect 
Drawings vector graphics 
gpath_draw_filled, gpath_draw_outline 
Drawings raster graphics 
graphics_draw_pixel, graphics_draw_line 
graphics_draw_circle, graphics_fill_circle, 
graphics_draw_rect, graphics_draw_round_rect, graphics_fill_rect 
Animate graphics 
app_timer_register, app_timer_reschedule, app_timer_cancel 
Animation, PropertyAnimation
Q&A 
source: https://www.youtube.com/watch?v=Ej2ELEK-C3Q Where To?
GRAPHICS APIS 
static void custom_update_proc(Layer *layer, GContext *ctx); 
Layer-equivalents working directly on GContext 
graphics_draw_text, graphics_draw_bitmap_in_rect 
Drawings vector graphics 
gpath_draw_filled, gpath_draw_outline 
Drawings 
raster graphics 
graphics_draw_pixel, graphics_draw_line 
graphics_draw_circle, graphics_fill_circle, 
graphics_draw_rect, graphics_draw_round_rect, graphics_fill_rect 
Animate graphics 
app_timer_register, app_timer_reschedule, app_timer_cancel 
Animation, PropertyAnimation
Drawings advanced raster graphics 
graphics_capture_frame_buffer, graphics_release_frame_buffer 
Doing dithering at runtime
DITHERING
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
256 shades of gray 
16 SHADES OF GRAY 
16 shades of gray + nearest color 
16 shades of gray + random noise 
16 shades of gray + ordered dithering 
BLACK AND WHITE 
black & white + nearest color 
black & white + noise 
black & white + ordered dithering
original ordered dithering Floyd-Steinberg 
dithering 
nearest color
FLOYD-STEINBERG DITHERING 
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 
ORDERED DITHERING (BAYER MATRIX) 
threshold_map_4x4 = 
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
source: Joel Yliluoma’s dithering page - http://bisqwit.iki.fi/story/howto/dither/jy/ original from PSX game “Chrono Cross”
source: Joel Yliluoma’s dithering page - http://bisqwit.iki.fi/story/howto/dither/jy/ nearest neighbor to 12 colors
source: Joel Yliluoma’s dithering page - http://bisqwit.iki.fi/story/howto/dither/jy/ Floyd-Steinberg to 12 colors
OFFLINE 
DITHERING
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
Better Bitmaps (GIMP/Photoshop) 
Open Original Image in the GIMP/Photoshop
Better Bitmaps (GIMP/Photoshop) 
Resize: Image -> Scale Image
Better Bitmaps (GIMP/Photoshop) 
Resize to desired size (<144x168) using sinc filter
Better Bitmaps (GIMP/Photoshop) 
Grayscale: Image -> Mode -> Grayscale
Better Bitmaps (GIMP/Photoshop) 
Brightness-Contrast: Colors -> Brightness-Contrast
Better Bitmaps (GIMP/Photoshop) 
Slide contrast to right to remove colors and balance with brightness
Better Bitmaps (GIMP/Photoshop) 
Posterize: Colors -> Posterize
Better Bitmaps (GIMP/Photoshop) 
Posterize: Select 3 colors (2 for black & white only). 3rd color is Gray.
Better Bitmaps (GIMP/Photoshop) 
Convert to 1-bit (b&w): Image -> Mode -> Indexed
Better Bitmaps (GIMP/Photoshop) 
Select b&w, choose dithering Positioned (Ordered) for dithered gray
Better Bitmaps (GIMP/Photoshop) 
Final Product: Robot with dithered gray_50
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
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)
RUNTIME 
DITHERING
50% GRAY
PRE-DITHERED 
FLOYD 
STEINBERG 
ORDERED 
DITHERING
PRE-DITHERED 
AT RUNTIME
PRE-DITHERED 
AT RUNTIME
PRE-DITHERED 
AT RUNTIME
PRE-DITHERED 
AT RUNTIME
PRE-DITHERED 
AT RUNTIME
50% GRAY
PRE-DITHERED 
FLOYD 
STEINBERG 
ORDERED 
DITHERING
source: Joel Yliluoma’s dithering page - http://bisqwit.iki.fi/story/howto/dither/jy/ reduction to 18 colors
IMPLEMENT 
DITHERING 
AT RUNTIME
ORDERED DITHERING (BAYER MATRIX) 
threshold_map_4x4 = 
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 
ORDERED DITHERING BLACK & WHITE 
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 
foreach y 
foreach x 
newpixel := pixel[x][y] > threshold_map_8x8[x mod 8][y mod 8] ? white : black; 
pixel[x][y] := newpixel 
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; 
}
DOING LIVE DITHERING 
gray_top 
gray_bottom 
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);
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
AVOIDING FLOATS 
float frac = (float)y / h; // 0.0 … 1.0 
row_gray = gray_top * (1 - frac) + frac * gray_bottom 
// 1 * h = h 
// frac * h = y 
uint8_t row_gray = (uint8_t)((gray_top * (h - y) + y * gray_bottom) / h);
DOING LIVE DITHERING 
gray_top 
gray_bottom 
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 DITHERING 
gray_top 
gray_bottom 
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); 
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)); 
} 
! 
! 
! 
! 
! 
! 
} 
! 
}
source: https://www.youtube.com/watch?v=V5O8gj0SQZ0
DOING FAST(!) LIVE DITHERING 
GBitmap 
* graphics_capture_frame_buffer(GContext* ctx) 
Gives you a GBitmap to represent the current frame buffer 
bool graphics_release_frame_buffer(GContext* ctx, GBitmap* buffer) 
…and locks out any other graphic function until you release it
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, stored 
in 8 bits or 1 byte (with value 0x5C). 0 1 0 1 1 1 0 0 
Fragment of 8 pixels in a row, stored 
in 8 bits or 1 byte (with value 0x5C). 0 1 0 1 1 1 0 0 
GBitmap 
typedef struct { 
void *addr; 
uint16_t row_size_bytes; 
// ... 
GRect bounds; 
} GBitmap; 
uint8_t *byte_offset = (uint8_t*)bmp->addr 
; 
byte_offset += x / 8; 
byte_offset *= y * bmp->row_size_bytes;
DOING FAST(!) LIVE DITHERING 
static void update_proc_frame_buffer(Layer *layer, GContext *ctx) { 
GBitmap *fb = graphics_capture_frame_buffer(ctx); 
! 
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); 
! 
! 
! 
! 
uint8_t row_gray_dither_pattern = d i t h e r _ p a t t e r n _ 8 (y, row_gray_value); 
row += fb->row_size_bytes; 
} 
! 
! 
! 
graphics_release_frame_buffer(ctx, fb); 
}
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
DOING FAST(!) LIVE DITHERING 
static void update_proc_frame_buffer(Layer *layer, GContext *ctx) { 
GBitmap *fb = graphics_capture_frame_buffer(ctx); 
! 
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); 
! 
! 
! 
! 
uint8_t row_gray_dither_pattern = d i t h e r _ p a t t e r n _ 8 (y, row_gray_value); 
memset(row, row_gray_dither_pattern, fb->row_size_bytes); 
row += fb->row_size_bytes; 
} 
! 
! 
! 
graphics_release_frame_buffer(ctx, fb); 
}
source: https://www.youtube.com/watch?v=V5O8gj0SQZ0
BOING 
BALL
Q&A 
source: https://www.youtube.com/watch?v=Z2QMG9UwXI0

More Related Content

What's hot

What's hot (20)

Computer Science Assignment Help
Computer Science Assignment HelpComputer Science Assignment Help
Computer Science Assignment Help
 
Data made out of functions
Data made out of functionsData made out of functions
Data made out of functions
 
COMPUTER GRAPHICS LAB MANUAL
COMPUTER GRAPHICS LAB MANUALCOMPUTER GRAPHICS LAB MANUAL
COMPUTER GRAPHICS LAB MANUAL
 
All I Needed for Functional Programming I Learned in High School Algebra
All I Needed for Functional Programming I Learned in High School AlgebraAll I Needed for Functional Programming I Learned in High School Algebra
All I Needed for Functional Programming I Learned in High School Algebra
 
Circles graphic
Circles graphicCircles graphic
Circles graphic
 
FEAL - CSAW CTF 2014 Quals Crypto300
FEAL - CSAW CTF 2014 Quals Crypto300FEAL - CSAW CTF 2014 Quals Crypto300
FEAL - CSAW CTF 2014 Quals Crypto300
 
Postfix Evaluations using Stack
Postfix Evaluations using StackPostfix Evaluations using Stack
Postfix Evaluations using Stack
 
Go Containers
Go ContainersGo Containers
Go Containers
 
Python (1)
Python (1)Python (1)
Python (1)
 
Java programs
Java programsJava programs
Java programs
 
Software Construction Assignment Help
Software Construction Assignment HelpSoftware Construction Assignment Help
Software Construction Assignment Help
 
Csphtp1 07
Csphtp1 07Csphtp1 07
Csphtp1 07
 
EGL 1.4 Reference Card
EGL 1.4 Reference CardEGL 1.4 Reference Card
EGL 1.4 Reference Card
 
Lecture 2 f17
Lecture 2 f17Lecture 2 f17
Lecture 2 f17
 
imager package in R and examples..
imager package in R and examples..imager package in R and examples..
imager package in R and examples..
 
Register Allocation
Register AllocationRegister Allocation
Register Allocation
 
Advance java
Advance javaAdvance java
Advance java
 
Isc computer project final upload last
Isc computer project final upload lastIsc computer project final upload last
Isc computer project final upload last
 
Class list data structure
Class list data structureClass list data structure
Class list data structure
 
Gentle Introduction to Functional Programming
Gentle Introduction to Functional ProgrammingGentle Introduction to Functional Programming
Gentle Introduction to Functional Programming
 

Similar to Advanced Techniques: Graphics | Pebble Developer Retreat 2014

Image processing spatialfiltering
Image processing spatialfilteringImage processing spatialfiltering
Image processing spatialfiltering
John Williams
 
COMPAPPABCA49085rFunrAP__Practical Number 9 & 10.docx
COMPAPPABCA49085rFunrAP__Practical Number 9 & 10.docxCOMPAPPABCA49085rFunrAP__Practical Number 9 & 10.docx
COMPAPPABCA49085rFunrAP__Practical Number 9 & 10.docx
TashiBhutia12
 
14multithreaded Graphics
14multithreaded Graphics14multithreaded Graphics
14multithreaded Graphics
Adil Jafri
 

Similar to Advanced Techniques: Graphics | Pebble Developer Retreat 2014 (20)

Deep learning
Deep learningDeep learning
Deep learning
 
Image processing spatialfiltering
Image processing spatialfilteringImage processing spatialfiltering
Image processing spatialfiltering
 
Pygame presentation
Pygame presentationPygame presentation
Pygame presentation
 
C Graphics Functions
C Graphics FunctionsC Graphics Functions
C Graphics Functions
 
Computer graphics
Computer graphicsComputer graphics
Computer graphics
 
Creating an Uber Clone - Part IV - Transcript.pdf
Creating an Uber Clone - Part IV - Transcript.pdfCreating an Uber Clone - Part IV - Transcript.pdf
Creating an Uber Clone - Part IV - Transcript.pdf
 
2Bytesprog2 course_2014_c9_graph
2Bytesprog2 course_2014_c9_graph2Bytesprog2 course_2014_c9_graph
2Bytesprog2 course_2014_c9_graph
 
Scmad Chapter06
Scmad Chapter06Scmad Chapter06
Scmad Chapter06
 
COMPAPPABCA49085rFunrAP__Practical Number 9 & 10.docx
COMPAPPABCA49085rFunrAP__Practical Number 9 & 10.docxCOMPAPPABCA49085rFunrAP__Practical Number 9 & 10.docx
COMPAPPABCA49085rFunrAP__Practical Number 9 & 10.docx
 
XIX PUG-PE - Pygame game development
XIX PUG-PE - Pygame game developmentXIX PUG-PE - Pygame game development
XIX PUG-PE - Pygame game development
 
Making of-the-logistic-map-bifurcation-diagram
Making of-the-logistic-map-bifurcation-diagramMaking of-the-logistic-map-bifurcation-diagram
Making of-the-logistic-map-bifurcation-diagram
 
Seeing Like Software
Seeing Like SoftwareSeeing Like Software
Seeing Like Software
 
14multithreaded Graphics
14multithreaded Graphics14multithreaded Graphics
14multithreaded Graphics
 
Introduction to graphics programming in c
Introduction to graphics programming in cIntroduction to graphics programming in c
Introduction to graphics programming in c
 
SCIPY-SYMPY.pdf
SCIPY-SYMPY.pdfSCIPY-SYMPY.pdf
SCIPY-SYMPY.pdf
 
Gems of GameplayKit. UA Mobile 2017.
Gems of GameplayKit. UA Mobile 2017.Gems of GameplayKit. UA Mobile 2017.
Gems of GameplayKit. UA Mobile 2017.
 
Sergey Shelpuk & Olha Romaniuk - “Deep learning, Tensorflow, and Fashion: how...
Sergey Shelpuk & Olha Romaniuk - “Deep learning, Tensorflow, and Fashion: how...Sergey Shelpuk & Olha Romaniuk - “Deep learning, Tensorflow, and Fashion: how...
Sergey Shelpuk & Olha Romaniuk - “Deep learning, Tensorflow, and Fashion: how...
 
Computer Graphics Unit 1
Computer Graphics Unit 1Computer Graphics Unit 1
Computer Graphics Unit 1
 
Extreme dxt compression
Extreme dxt compressionExtreme dxt compression
Extreme dxt compression
 
Triangle Visibility buffer
Triangle Visibility bufferTriangle Visibility buffer
Triangle Visibility buffer
 

More from Pebble Technology

Overlay & Libraries | Pebble Meetup Oct. 2014
Overlay & Libraries | Pebble Meetup Oct. 2014Overlay & Libraries | Pebble Meetup Oct. 2014
Overlay & Libraries | Pebble Meetup Oct. 2014
Pebble Technology
 

More from Pebble Technology (20)

#PDR15 - Awesome Appstore Assets
#PDR15 - Awesome Appstore Assets#PDR15 - Awesome Appstore Assets
#PDR15 - Awesome Appstore Assets
 
#PDR15 - Smartstrap Workshop
#PDR15 - Smartstrap Workshop#PDR15 - Smartstrap Workshop
#PDR15 - Smartstrap Workshop
 
#PDR15 - Data Analytics and Pebble
#PDR15 - Data Analytics and Pebble#PDR15 - Data Analytics and Pebble
#PDR15 - Data Analytics and Pebble
 
#PDR15 - Best Use Cases For Timeline
#PDR15 - Best Use Cases For Timeline#PDR15 - Best Use Cases For Timeline
#PDR15 - Best Use Cases For Timeline
 
#PDR15 - waf, wscript and Your Pebble App
#PDR15 - waf, wscript and Your Pebble App#PDR15 - waf, wscript and Your Pebble App
#PDR15 - waf, wscript and Your Pebble App
 
#PDR15 - PebbleKit iOS 3.0
#PDR15 - PebbleKit iOS 3.0#PDR15 - PebbleKit iOS 3.0
#PDR15 - PebbleKit iOS 3.0
 
#PDR15 - Voice API
#PDR15 - Voice API#PDR15 - Voice API
#PDR15 - Voice API
 
#PDR15 - Pebble Graphics
#PDR15 - Pebble Graphics#PDR15 - Pebble Graphics
#PDR15 - Pebble Graphics
 
#PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk
#PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk#PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk
#PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk
 
#PDR15 - Designing for Pebble
#PDR15 - Designing for Pebble#PDR15 - Designing for Pebble
#PDR15 - Designing for Pebble
 
#PDR15 Kick-Off
#PDR15 Kick-Off#PDR15 Kick-Off
#PDR15 Kick-Off
 
Pebble Slate Workshop
Pebble Slate WorkshopPebble Slate Workshop
Pebble Slate Workshop
 
Overlay & Libraries | Pebble Meetup Oct. 2014
Overlay & Libraries | Pebble Meetup Oct. 2014Overlay & Libraries | Pebble Meetup Oct. 2014
Overlay & Libraries | Pebble Meetup Oct. 2014
 
Connecting Pebble to the World
Connecting Pebble to the WorldConnecting Pebble to the World
Connecting Pebble to the World
 
Guest Presentation - Strap | Pebble Developer Retreat 2014
Guest Presentation - Strap | Pebble Developer Retreat 2014Guest Presentation - Strap | Pebble Developer Retreat 2014
Guest Presentation - Strap | Pebble Developer Retreat 2014
 
Battery Life | Pebble Developer Retreat 2014
Battery Life | Pebble Developer Retreat 2014Battery Life | Pebble Developer Retreat 2014
Battery Life | Pebble Developer Retreat 2014
 
Thomas Sarlandie Kickoff Talk | Pebble Developer Retreat 2014
Thomas Sarlandie Kickoff Talk | Pebble Developer Retreat 2014Thomas Sarlandie Kickoff Talk | Pebble Developer Retreat 2014
Thomas Sarlandie Kickoff Talk | Pebble Developer Retreat 2014
 
Advanced Techniques: Size | Pebble Developer Retreat 2014
Advanced Techniques: Size | Pebble Developer Retreat 2014Advanced Techniques: Size | Pebble Developer Retreat 2014
Advanced Techniques: Size | Pebble Developer Retreat 2014
 
Pebble wearables devcon
Pebble wearables devconPebble wearables devcon
Pebble wearables devcon
 
Announcing Pebble SDK 2.0
Announcing Pebble SDK 2.0Announcing Pebble SDK 2.0
Announcing Pebble SDK 2.0
 

Recently uploaded

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
vu2urc
 

Recently uploaded (20)

Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 

Advanced Techniques: Graphics | Pebble Developer Retreat 2014

  • 1. GRAPHICS MATT & HEIKO – DEVELOPER EXPERIENCE ENGINEERS ON TWITTER AS @MTHUNGERFORD & @HBEHRENS
  • 2. THE DISPLAY 144x168px @ 175 DPI black & white only 25+ FPS
  • 3. CONTENT A Pixel’s Journey Dithering Accessing the Frame Buffer
  • 5. 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
  • 6. THE COMPOSITOR Example of a non-fullscreen application (144x152): Application frame buffer Compositor frame buffer Final display
  • 7. GRAPHICS APIS static void custom_update_proc(Layer *layer, GContext *ctx); Layer-equivalents working directly on GContext graphics_draw_text, graphics_draw_bitmap_in_rect Drawings vector graphics gpath_draw_filled, gpath_draw_outline Drawings raster graphics graphics_draw_pixel, graphics_draw_line graphics_draw_circle, graphics_fill_circle, graphics_draw_rect, graphics_draw_round_rect, graphics_fill_rect Animate graphics app_timer_register, app_timer_reschedule, app_timer_cancel Animation, PropertyAnimation
  • 9. GRAPHICS APIS static void custom_update_proc(Layer *layer, GContext *ctx); Layer-equivalents working directly on GContext graphics_draw_text, graphics_draw_bitmap_in_rect Drawings vector graphics gpath_draw_filled, gpath_draw_outline Drawings raster graphics graphics_draw_pixel, graphics_draw_line graphics_draw_circle, graphics_fill_circle, graphics_draw_rect, graphics_draw_round_rect, graphics_fill_rect Animate graphics app_timer_register, app_timer_reschedule, app_timer_cancel Animation, PropertyAnimation
  • 10. Drawings advanced raster graphics graphics_capture_frame_buffer, graphics_release_frame_buffer Doing dithering at runtime
  • 12.
  • 13.
  • 14. 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
  • 15. 256 shades of gray 16 SHADES OF GRAY 16 shades of gray + nearest color 16 shades of gray + random noise 16 shades of gray + ordered dithering BLACK AND WHITE black & white + nearest color black & white + noise black & white + ordered dithering
  • 16. original ordered dithering Floyd-Steinberg dithering nearest color
  • 17. FLOYD-STEINBERG DITHERING 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 ORDERED DITHERING (BAYER MATRIX) threshold_map_4x4 = 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
  • 18. source: Joel Yliluoma’s dithering page - http://bisqwit.iki.fi/story/howto/dither/jy/ original from PSX game “Chrono Cross”
  • 19. source: Joel Yliluoma’s dithering page - http://bisqwit.iki.fi/story/howto/dither/jy/ nearest neighbor to 12 colors
  • 20. source: Joel Yliluoma’s dithering page - http://bisqwit.iki.fi/story/howto/dither/jy/ Floyd-Steinberg to 12 colors
  • 22. 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
  • 23. Better Bitmaps (GIMP/Photoshop) Open Original Image in the GIMP/Photoshop
  • 24. Better Bitmaps (GIMP/Photoshop) Resize: Image -> Scale Image
  • 25. Better Bitmaps (GIMP/Photoshop) Resize to desired size (<144x168) using sinc filter
  • 26. Better Bitmaps (GIMP/Photoshop) Grayscale: Image -> Mode -> Grayscale
  • 27. Better Bitmaps (GIMP/Photoshop) Brightness-Contrast: Colors -> Brightness-Contrast
  • 28. Better Bitmaps (GIMP/Photoshop) Slide contrast to right to remove colors and balance with brightness
  • 29. Better Bitmaps (GIMP/Photoshop) Posterize: Colors -> Posterize
  • 30. Better Bitmaps (GIMP/Photoshop) Posterize: Select 3 colors (2 for black & white only). 3rd color is Gray.
  • 31. Better Bitmaps (GIMP/Photoshop) Convert to 1-bit (b&w): Image -> Mode -> Indexed
  • 32. Better Bitmaps (GIMP/Photoshop) Select b&w, choose dithering Positioned (Ordered) for dithered gray
  • 33. Better Bitmaps (GIMP/Photoshop) Final Product: Robot with dithered gray_50
  • 34. 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
  • 35. 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)
  • 36.
  • 39. PRE-DITHERED FLOYD STEINBERG ORDERED DITHERING
  • 46. PRE-DITHERED FLOYD STEINBERG ORDERED DITHERING
  • 47. source: Joel Yliluoma’s dithering page - http://bisqwit.iki.fi/story/howto/dither/jy/ reduction to 18 colors
  • 49. ORDERED DITHERING (BAYER MATRIX) threshold_map_4x4 = 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 ORDERED DITHERING BLACK & WHITE foreach y foreach x newpixel := pixel[x][y] > threshold_map_8x8[x mod 8][y mod 8] ? white : black; pixel[x][y] := newpixel
  • 50. ORDERED DITHERING BLACK & WHITE foreach y foreach x newpixel := pixel[x][y] > threshold_map_8x8[x mod 8][y mod 8] ? white : black; pixel[x][y] := newpixel 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; }
  • 51. DOING LIVE DITHERING gray_top gray_bottom 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);
  • 52. 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
  • 53. AVOIDING FLOATS float frac = (float)y / h; // 0.0 … 1.0 row_gray = gray_top * (1 - frac) + frac * gray_bottom // 1 * h = h // frac * h = y uint8_t row_gray = (uint8_t)((gray_top * (h - y) + y * gray_bottom) / h);
  • 54. DOING LIVE DITHERING gray_top gray_bottom 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);
  • 55. DOING LIVE DITHERING gray_top gray_bottom 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); 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)); } ! ! ! ! ! ! } ! }
  • 57. DOING FAST(!) LIVE DITHERING GBitmap * graphics_capture_frame_buffer(GContext* ctx) Gives you a GBitmap to represent the current frame buffer bool graphics_release_frame_buffer(GContext* ctx, GBitmap* buffer) …and locks out any other graphic function until you release it
  • 58. 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, stored in 8 bits or 1 byte (with value 0x5C). 0 1 0 1 1 1 0 0 Fragment of 8 pixels in a row, stored in 8 bits or 1 byte (with value 0x5C). 0 1 0 1 1 1 0 0 GBitmap typedef struct { void *addr; uint16_t row_size_bytes; // ... GRect bounds; } GBitmap; uint8_t *byte_offset = (uint8_t*)bmp->addr ; byte_offset += x / 8; byte_offset *= y * bmp->row_size_bytes;
  • 59. DOING FAST(!) LIVE DITHERING static void update_proc_frame_buffer(Layer *layer, GContext *ctx) { GBitmap *fb = graphics_capture_frame_buffer(ctx); ! 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); ! ! ! ! uint8_t row_gray_dither_pattern = d i t h e r _ p a t t e r n _ 8 (y, row_gray_value); row += fb->row_size_bytes; } ! ! ! graphics_release_frame_buffer(ctx, fb); }
  • 60. 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
  • 61. DOING FAST(!) LIVE DITHERING static void update_proc_frame_buffer(Layer *layer, GContext *ctx) { GBitmap *fb = graphics_capture_frame_buffer(ctx); ! 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); ! ! ! ! uint8_t row_gray_dither_pattern = d i t h e r _ p a t t e r n _ 8 (y, row_gray_value); memset(row, row_gray_dither_pattern, fb->row_size_bytes); row += fb->row_size_bytes; } ! ! ! graphics_release_frame_buffer(ctx, fb); }