static void bitmapLayerUpdate(struct Layer *layer, GContext *ctx){ GBitmap *framebuffer; const GBitmap *graphic = bitmap_layer_get_bitmap((BitmapLayer *)layer); int height; uint8_t finalBits; uint8_t *bfr, *bitmap;; // printf("bitmap layer update proc\n"); framebuffer = graphics_capture_frame_buffer(ctx); height = graphic->bounds.size.h; // APP_LOG(APP_LOG_LEVEL_DEBUG, "bitmaplayerupdate: %d, height: %d", framebuffer->row_size_bytes, height); for (int yindex =0; yindex < height; yindex++){ // APP_LOG(APP_LOG_LEVEL_DEBUG, "----- yindex %d", yindex); for ( int xindex = 0; xindex < graphic->row_size_bytes; xindex++){ bfr = (((uint8_t*)framebuffer->addr)+(yindex * framebuffer->row_size_bytes)+xindex); bitmap = (((uint8_t*)graphic->addr)+(yindex * graphic->row_size_bytes)+xindex); finalBits = *bitmap ^ *bfr; // APP_LOG(APP_LOG_LEVEL_DEBUG, "bfr: %0x, bitmsp: %0x, finalBits: %x", (unsigned int)bfr, (unsigned int)bitmap, finalBits ); *bfr = finalBits; } } graphics_release_frame_buffer(ctx, framebuffer); }
/* * Draws linear gradient using direct frame buffer access. * * This function isn't being used directly by any layer. Instead, update_proc_frame_buffer_1() * and update_proc_frame_buffer_20() will call it before they draw additional information. */ static void update_proc_frame_buffer(Layer *layer, GContext *ctx) { // obtains direct access to the frame buffer, // determines gray_ values per row // calculates dithering pattern for the row, and // sets all pixels of the row at once // obtain frame buffer (must be released, again using graphics_release_frame_buffer!) GBitmap *fb = graphics_capture_frame_buffer(ctx); // during the loop, row always points to the byte of the first 8 pixels of the current row uint8_t *row = (uint8_t *)fb->addr; row += fb->bounds.origin.y * fb->row_size_bytes; for (int16_t y = fb->bounds.origin.y; y < fb->bounds.size.h; y++) { const uint8_t row_gray = gray_for_row(y); // as our dither pattern repeats itself after 8 pixels (8x8 dither matrix), we can store the // whole pattern of this row in a single byte and set all pixels with a single call to memset uint8_t row_gray_dither_pattern = dither_pattern_8(y, row_gray); memset(row, row_gray_dither_pattern, fb->row_size_bytes); // move row pointer to the start of the next row row += fb->row_size_bytes; } // do not forget to release the frame buffer after usage. Other graphics_* functions are blocked // while it is capture. graphics_release_frame_buffer(ctx, fb); }
void gpath_draw_outline_antialiased(GContext* ctx, GPath *path, GColor8 stroke_color){ if(path->num_points == 0) return; GPoint offset = path->offset; int32_t rotation = path->rotation; GBitmap* bitmap = graphics_capture_frame_buffer(ctx); int32_t s = sin_lookup(rotation); int32_t c = cos_lookup(rotation); GPoint p = path->points[path->num_points-1]; GPoint p1; p1.x = (p.x * c - p.y * s) / TRIG_MAX_RATIO + offset.x; p1.y = (p.x * s + p.y * c) / TRIG_MAX_RATIO + offset.y; for(uint32_t i=0; i<path->num_points; i++){ GPoint p2; p2.x = (path->points[i].x * c - path->points[i].y * s) / TRIG_MAX_RATIO + offset.x; p2.y = (path->points[i].x * s + path->points[i].y * c) / TRIG_MAX_RATIO + offset.y; draw_line_antialias_(bitmap, p1.x, p1.y, p2.x, p2.y, stroke_color); p1 = p2; } graphics_release_frame_buffer(ctx, bitmap); }
static void offscreen_layer_update(Layer* layer, GContext *ctx) { if (data_loaded) { GRect bounds = layer_get_bounds(layer); // Draw the night slice const GRect entire_screen = GRect(0, 0, 180, 180); graphics_context_set_fill_color(ctx, GColorWhite); graphics_fill_radial( ctx, entire_screen, GOvalScaleModeFillCircle, 90, DEG_TO_TRIGANGLE(degreeify(hour_set, minute_set)), DEG_TO_TRIGANGLE(degreeify(hour_rise, minute_rise)) ); // Capture the graphics context framebuffer GBitmap *framebuffer = graphics_capture_frame_buffer(ctx); bitmap_make_transparent(stars, framebuffer); // Release the framebuffer now that we are free to modify it graphics_release_frame_buffer(ctx, framebuffer); } }
void graphics_draw_line_antialiased(GContext* ctx, GPoint p0, GPoint p1, GColor8 stroke_color){ if(p0.x == p1.x || p0.y == p1.y){ graphics_draw_line(ctx, p0, p1); } else { GBitmap* bitmap = graphics_capture_frame_buffer(ctx); draw_line_antialias_(bitmap, p0.x, p0.y, p1.x, p1.y, stroke_color); graphics_release_frame_buffer(ctx, bitmap); } }
static void update_proc_round_frame_buffer(Layer *layer, GContext *ctx) { GBitmap *fb = graphics_capture_frame_buffer(ctx); GRect bounds = layer_get_bounds(layer); for(int y = 0; y < bounds.size.h; y++) { GBitmapDataRowInfo info = gbitmap_get_data_row_info(fb, y); for(int x = info.min_x; x < info.max_x; x++) { memset(&info.data[x], rand(), 1); } } graphics_release_frame_buffer(ctx, fb); }
static void layer_update_proc(Layer *layer, GContext *ctx) { int length_red, length_blue, length_green, x, y; GColor bar_color; // Get the framebuffer GBitmap *fb = graphics_capture_frame_buffer(ctx); // Manipulate the image data... GBitmapDataRowInfo info = gbitmap_get_data_row_info(fb, BAR_BEGINNING); length_red = s_temperature * (info.max_x - info.min_x) / RANGE_RED; length_green = s_batt * (info.max_x - info.min_x) / RANGE_GREEN; length_blue = s_pop * (info.max_x - info.min_x) / RANGE_BLUE; int temp_red = length_red + (info.max_x - info.min_x); // int bar_length = info.max_x - info.min_x; // Iterate over desired rows for(y = BAR_BEGINNING; y < BAR_END; y++) { // Get this row's range and data info = gbitmap_get_data_row_info(fb, y); // Iterate over all visible columns if (0 <= length_red) { for(x = info.min_x; x < info.max_x; x++) { bar_color.argb = 0b11000000; if (x < length_red) { bar_color.argb = bar_color.argb | 0b00110000; } if (x < length_green) { bar_color.argb = bar_color.argb | 0b00001100; } if (x < length_blue) { bar_color.argb = bar_color.argb | 0b00000011; } // Manipulate the pixel at x,y set_pixel_color(info, GPoint(x, y), bar_color); } } else { for(x = info.min_x; x < info.max_x; x++) { bar_color.argb = 0b11000000; if (x > temp_red) { bar_color.argb = bar_color.argb | 0b00110000; } if (x < length_green) { bar_color.argb = bar_color.argb | 0b00001100; } if (x < length_blue) { bar_color.argb = bar_color.argb | 0b00000011; } // Manipulate the pixel at x,y set_pixel_color(info, GPoint(x, y), bar_color); } } } // Finally, release the framebuffer graphics_release_frame_buffer(ctx, fb); }
static void bg_update_proc(Layer *layer, GContext *ctx) { if(!is_encoding) { is_encoding = true; GBitmap *fb = graphics_capture_frame_buffer(ctx); APP_LOG(APP_LOG_LEVEL_DEBUG, "Captured framebuffer"); bmpData(fb); APP_LOG(APP_LOG_LEVEL_DEBUG, "Converted bitmap"); png_encode((const unsigned char*)imagedata); APP_LOG(APP_LOG_LEVEL_DEBUG, "Encoded PNG"); graphics_release_frame_buffer(ctx, fb); APP_LOG(APP_LOG_LEVEL_DEBUG, "Released framebuffer"); //is_encoding = false; } }
static void update_proc_frame_buffer(Layer *layer, GContext *ctx){ #if defined(PBL_ROUND) update_proc_round_frame_buffer(layer, ctx); #else GBitmap *fb = graphics_capture_frame_buffer(ctx); if (fb!=NULL){ GRect bounds=gbitmap_get_bounds(fb); uint8_t *pos = gbitmap_get_data(fb); for (int16_t y = bounds.origin.y; y < bounds.size.h; y++) { for (int16_t x = bounds.origin.x; x < gbitmap_get_bytes_per_row(fb); x++){ memset(++pos, rand(), 1); } } graphics_release_frame_buffer(ctx, fb); } #endif }
static void render_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); #if 0 // BUG: Currently not working GBitmap *render_bitmap = gbitmap_create_blank(bounds.size, GBitmapFormat8BitCircular); #else GBitmap *render_bitmap = gbitmap_create_blank(bounds.size, GBitmapFormat8Bit); #endif //replace screen bitmap with our offscreen render bitmap gbitmap_set_data(framebuffer, gbitmap_get_data(render_bitmap), gbitmap_get_format(render_bitmap), gbitmap_get_bytes_per_row(render_bitmap), false); graphics_context_set_compositing_mode(ctx, GCompOpAssign); digital_update_proc(layer, ctx); //restore original context bitmap gbitmap_set_data(framebuffer, orig_addr, orig_format, orig_stride, false); //draw the bitmap to the screen graphics_context_set_compositing_mode(ctx, GCompOpSet); // Make the render_bitmap transparent bitmap_make_semi_transparent(render_bitmap); graphics_draw_bitmap_in_rect(ctx, render_bitmap, bounds); gbitmap_destroy(render_bitmap); }
static void draw_row_callback(GContext *ctx, Layer *cell_layer, MenuIndex *idx, void *context) { GRect bounds = layer_get_bounds(cell_layer); #if defined(PBL_ROUND) // get info of pixel row in the middle of menu row GBitmap *fb = graphics_capture_frame_buffer(ctx); GPoint sc_coord = layer_convert_point_to_screen(cell_layer, GPoint(0, bounds.size.h/2)); GBitmapDataRowInfo info = gbitmap_get_data_row_info(fb, sc_coord.y); graphics_release_frame_buffer(ctx, fb); // adapt bounds for round displays bounds.origin.x = info.min_x + PADDING; bounds.size.w = info.max_x - info.min_x - PADDING; #endif GRect frame = GRect( bounds.origin.x + ICON_SIZE + 3*PADDING, 0, bounds.size.w - 2*ICON_SIZE - PADDING, bounds.size.h/2 ); // draw direction // expand frame width if countdown on the right is small if(deps_items[idx->row].countdown > 0 && deps_items[idx->row].countdown < 10) { frame.size.w += 10; } graphics_draw_text(ctx, deps_items[idx->row].direction, fonts_get_system_font(FONT_KEY_GOTHIC_14), frame, GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL); // draw time of scheduled departure plus delay frame.origin.y += 12; graphics_draw_text(ctx, deps_items[idx->row].time, fonts_get_system_font(FONT_KEY_GOTHIC_18), frame, GTextOverflowModeTrailingEllipsis, GTextAlignmentLeft, NULL); // draw time until real time departure frame.origin.x = bounds.origin.x + bounds.size.w - ICON_SIZE - PADDING; frame.origin.y = 0; frame.size.w = ICON_SIZE; frame.size.h = ICON_SIZE; if(deps_items[idx->row].countdown == 0) { // draw icon if departure is imminent char* icon_number; if (strcmp(deps_items[idx->row].icon, "bus") == 0) { icon_number = "1"; } else if (strcmp(deps_items[idx->row].icon, "tram") == 0) { icon_number = "2"; } else if (strcmp(deps_items[idx->row].icon, "train") == 0) { icon_number = "3"; } else if (strcmp(deps_items[idx->row].icon, "boat") == 0) { icon_number = "4"; } else if (strcmp(deps_items[idx->row].icon, "funicular") == 0) { icon_number = "5"; } else if (strcmp(deps_items[idx->row].icon, "cable_car") == 0) { icon_number = "6"; } else { icon_number = ""; } frame.origin.x = bounds.origin.x + bounds.size.w - ICON_SIZE; frame.origin.y = 0; frame.size.w = ICON_SIZE+2; frame.size.h = ICON_SIZE; graphics_draw_text(ctx, icon_number, s_icons, frame, GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL); } else { static char s_buff[16]; if(deps_items[idx->row].countdown > 60) { strncpy(s_buff, ">1h", 16); } else if(deps_items[idx->row].countdown > 0) { snprintf(s_buff, sizeof(s_buff), "%d'", deps_items[idx->row].countdown); } graphics_draw_text(ctx, s_buff, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), frame, GTextOverflowModeFill, GTextAlignmentRight, NULL); } // draw line icon with colors frame.origin.x = bounds.origin.x + PADDING; frame.origin.y = (bounds.size.h / 2) - (ICON_SIZE / 2); frame.size.w = ICON_SIZE; frame.size.h = ICON_SIZE; GColor color_bg; // correct some coloring switch(deps_items[idx->row].color_bg) { case 9090335 : color_bg = GColorSpringBud; break; case 12703135 : color_bg = GColorInchworm; break; default : color_bg = GColorFromHEX(deps_items[idx->row].color_bg); } GColor color_fg = GColorFromHEX(deps_items[idx->row].color_fg); graphics_context_set_fill_color(ctx, COLOR_FALLBACK(color_bg, GColorClear)); graphics_fill_rect(ctx, frame, 3, GCornersAll); if(!gcolor_equal(color_bg, GColorWhite) || menu_cell_layer_is_highlighted(cell_layer)) { graphics_context_set_stroke_color(ctx, COLOR_FALLBACK(GColorWhite, GColorClear)); } graphics_draw_round_rect(ctx, frame, 3); graphics_context_set_text_color(ctx, COLOR_FALLBACK(color_fg, GColorBlack)); char * name = deps_items[idx->row].name; GFont font; if(strlen(name) == 1) { frame.origin.x += 1; frame.origin.y += 3; /*font = fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD);*/ font = s_helvetic_bold; } else if(strlen(name) == 2) { // correct position if 2nd digit is "1" if (strstr(name+1, "1") != NULL) { frame.origin.x += 2; } frame.origin.y += 3; /*font = fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD);*/ font = s_helvetic_bold; /*if(strlen(name) == 1) { frame.origin.x += 1; }*/ } else if(strlen(name) == 3){ frame.origin.y += 3; font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD); } else { frame.origin.y += 6; font = fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD); } graphics_draw_text(ctx, name, font, frame, GTextOverflowModeFill, GTextAlignmentCenter, NULL); }
static void gpath_draw_filled_custom(GContext* ctx, GPath *path, GColor8 fill_color){ if(path->num_points == 0) return; GPoint offset = path->offset; int32_t rotation = path->rotation; int32_t s = sin_lookup(rotation); int32_t c = cos_lookup(rotation); // Rotate each point of the gpath and memorize the min/max GPoint* points_rot = malloc(sizeof(GPoint) * path->num_points); GPoint top_right = (GPoint){(1 << 15)-1,(1 << 15)-1}; GPoint bottom_left= (GPoint){-(1 << 15),-(1 << 15)}; for(uint32_t i=0; i<path->num_points; i++){ points_rot[i].x = (path->points[i].x * c - path->points[i].y * s) / TRIG_MAX_RATIO + offset.x; points_rot[i].y = (path->points[i].x * s + path->points[i].y * c) / TRIG_MAX_RATIO + offset.y; if(points_rot[i].x > bottom_left.x) bottom_left.x = points_rot[i].x; if(points_rot[i].x < top_right.x) top_right.x = points_rot[i].x; if(points_rot[i].y > bottom_left.y) bottom_left.y = points_rot[i].y; if(points_rot[i].y < top_right.y) top_right.y = points_rot[i].y; } // Create an array bitmap pebble v2 style (1 bit equals 1 pixel) int32_t bytes_per_row = (bottom_left.x - top_right.x + 1) / 8 + ((bottom_left.x - top_right.x + 1) % 8 == 0 ? 0 : 1); int32_t h = bottom_left.y - top_right.y + 1; uint8_t* pixels = malloc(bytes_per_row * h); memset(pixels, 0, bytes_per_row * h); // And draw the outline path in this 1 bit image GPoint prev_p = points_rot[path->num_points - 1]; GPoint p; for(uint32_t i=0; i<path->num_points; i++){ p = points_rot[i]; bmpDrawLine(pixels, bytes_per_row, prev_p.x - top_right.x, prev_p.y - top_right.y, p.x - top_right.x, p.y - top_right.y); prev_p = p; } free(points_rot); // Compute the starting point for the flow fill algorithm // TODO tobe improved GPoint start; start.x = (points_rot[0].x + points_rot[1].x) / 2; start.y = (points_rot[0].y + points_rot[1].y) / 2; if(points_rot[0].x < points_rot[1].x){ if(points_rot[0].y < points_rot[1].y){ start.x--; start.y++; } else { start.x++; start.y++; } } else { if(points_rot[0].y < points_rot[1].y){ start.x--; start.y--; } else { start.x++; start.y--; } } // Capture the frame buffer GBitmap* bitmap = graphics_capture_frame_buffer(ctx); // flood fill the gpath floodFill(bitmap, pixels, bytes_per_row, start, top_right, fill_color); // Release the frame buffer graphics_release_frame_buffer(ctx, bitmap); //Release the working variables free(pixels); }