In my previous post I described how you can access framebuffer of Pebble screen via familiar X,Y coordinates. To reiterate: you capture framebuffer as a bitmap, and access bitmap as 2-dimentional matrix:
#define WINDOW_WIDTH 144
GBitmap *fb = graphics_capture_frame_buffer(ctx);
uint8_t (*fb_matrix)[WINDOW_WIDTH] = (uint8_t (*)[WINDOW_WIDTH]) gbitmap_get_data(fb);
After that you can access specific pixel on the screen via coordinates, e.g. fb_matrix[120][60]
will represent pixel and coordinates Y = 120, X = 60
This works fine on Pebble Time (Basalt platform) where every pixel represented by a byte. But what about classic Pebble (I think this term is becoming quite popular, but to avoid confusion I will call it Aplite platform). On Aplite every byte in the framebuffer represent 8 pixels and the above approach doesn’t work.
Fortunately there’s a universal solution.
First distinction we have to make is in screen width in bytes. On Basalt since every pixel is a byte, 144px = 144 bytes. Aplite uses 20 bytes per row. Hense we change the above definition to
#ifdef PBL_COLOR #define WINDOW_WIDTH 144 #else #define WINDOW_WIDTH 20 #endif
To make the approach truly universal we will define a single function to set pixel for both Basalt and Applite as well as a single function to get pixel for the both. And what value they would be setting/returning will depend on conditional compiling. Ok, here we go, first function to set pixel:
void set_pixel(uint8_t (*fb_a)[WINDOW_WIDTH], int y, int x, uint8_t color) { #ifdef PBL_COLOR fb_a[y][x] = color; #else fb_a[y][x / 8] ^= (-color ^ fb_a[y][x / 8]) & (1 << (x % 8)); #endif }
We passing 4 parameters to the function: 2-d matrix representing frambuffer, Y and X coordinates of the pixel and color to set the pixel to. Both Basalt and Aplite will call the function in the same way, e.g. set_pixel(fb_matrix, 120, 60, 0);
As you can see if Basalt code is calling the function – the function simple assigns color value to byte at given coordinates. But if Aplite code is calling it – the function determines both the byte that needs to be set as well as bit within the byte
Similarly to get pixel we will use a single function:
uint8_t get_pixel(uint8_t (*fb_a)[WINDOW_WIDTH], int y, int x) { #ifdef PBL_COLOR return fb_a[y][x]; // in Basalt - simple get entire byte #else return (fb_a[y][x / 8] >> (x % 8)) & 1; // in Applite - get the bit #endif }
Again if Basalt is calling this function – the function simple returns byte from requested coordinates, while Applite will receive value of specific bit. Note of caution: Even though Aplite versions of functions deal with values of bits – they use unsigned int to carry those values, so bit values of 0 and 1 will be represented by uint8_t 0 and 1. Keep that in mind if you plan on bitwise operations of those values.
Credits: I thank very much this StackOverflow answer that provided a lot of useful information about setting and getting single bits.
Useful Links:
- EffectLayer Library for Pebble that uses this approach to apply effects to Classic Pebble Screen
- Demo Of “Rotate 90 Degrees” effect of the EffectLayer by Ron.