Universal access to Pebble framebuffer on Basalt and Aplite via coordinates

Rotate Effect on Aplite 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:

Leave a Reply

Your email address will not be published. Required fields are marked *