// Draws the frame and optionally fills the background of the current date window. void draw_date_window_background(GContext *ctx, unsigned int fg_draw_mode, unsigned int bg_draw_mode, bool opaque_layer) { if (opaque_layer || bg_draw_mode != fg_draw_mode) { if (date_window_mask.bitmap == NULL) { date_window_mask = rle_bwd_create(RESOURCE_ID_DATE_WINDOW_MASK); if (date_window_mask.bitmap == NULL) { trigger_memory_panic(__LINE__); return; } } graphics_context_set_compositing_mode(ctx, draw_mode_table[bg_draw_mode].paint_mask); graphics_draw_bitmap_in_rect(ctx, date_window_mask.bitmap, date_window_box); } if (date_window.bitmap == NULL) { date_window = rle_bwd_create(RESOURCE_ID_DATE_WINDOW); if (date_window.bitmap == NULL) { bwd_destroy(&date_window_mask); trigger_memory_panic(__LINE__); return; } } graphics_context_set_compositing_mode(ctx, draw_mode_table[fg_draw_mode].paint_fg); graphics_draw_bitmap_in_rect(ctx, date_window.bitmap, date_window_box); }
void load_chrono_dial() { #ifdef PBL_PLATFORM_APLITE bwd_destroy(&chrono_dial_white); bwd_destroy(&chrono_dial_black); if (chrono_dial_shows_tenths) { chrono_dial_white = rle_bwd_create(RESOURCE_ID_CHRONO_DIAL_TENTHS_WHITE); chrono_dial_black = rle_bwd_create(RESOURCE_ID_CHRONO_DIAL_TENTHS_BLACK); } else { chrono_dial_white = rle_bwd_create(RESOURCE_ID_CHRONO_DIAL_HOURS_WHITE); chrono_dial_black = rle_bwd_create(RESOURCE_ID_CHRONO_DIAL_HOURS_BLACK); } if (chrono_dial_white.bitmap == NULL || chrono_dial_black.bitmap == NULL) { bwd_destroy(&chrono_dial_white); bwd_destroy(&chrono_dial_black); trigger_memory_panic(__LINE__); } #else // PBL_PLATFORM_APLITE // In Basalt, we only load the "white" image. bwd_destroy(&chrono_dial_white); if (chrono_dial_shows_tenths) { chrono_dial_white = rle_bwd_create(RESOURCE_ID_CHRONO_DIAL_TENTHS_WHITE); } else { chrono_dial_white = rle_bwd_create(RESOURCE_ID_CHRONO_DIAL_HOURS_WHITE); } if (chrono_dial_white.bitmap == NULL) { bwd_destroy(&chrono_dial_white); trigger_memory_panic(__LINE__); return; } // We apply the color-inverting mode if necessary. uint8_t xor_argb8 = config.draw_mode ? 0x3f : 0x00; bwd_adjust_colors(&chrono_dial_white, 0xff, 0x00, xor_argb8); #endif // PBL_PLATFORM_APLITE }
void draw_chrono_dial(GContext *ctx) { // app_log(APP_LOG_LEVEL_INFO, __FILE__, __LINE__, "draw_chrono_dial"); if (config.chrono_dial != CDM_off) { #ifdef PBL_PLATFORM_APLITE BitmapWithData chrono_dial_black; if (chrono_dial_shows_tenths) { chrono_dial_black = rle_bwd_create(RESOURCE_ID_CHRONO_DIAL_TENTHS_BLACK); } else { chrono_dial_black = rle_bwd_create(RESOURCE_ID_CHRONO_DIAL_HOURS_BLACK); } if (chrono_dial_black.bitmap == NULL) { bwd_destroy(&chrono_dial_black); trigger_memory_panic(__LINE__); } #endif // PBL_PLATFORM_APLITE // In Basalt, we only load the "white" image. if (chrono_dial_white.bitmap == NULL) { if (chrono_dial_shows_tenths) { chrono_dial_white = rle_bwd_create(RESOURCE_ID_CHRONO_DIAL_TENTHS_WHITE); } else { chrono_dial_white = rle_bwd_create(RESOURCE_ID_CHRONO_DIAL_HOURS_WHITE); } if (chrono_dial_white.bitmap == NULL) { trigger_memory_panic(__LINE__); return; } // We apply the color scheme as needed. remap_colors_clock(&chrono_dial_white); } int x = chrono_tenth_hand_def.place_x - chrono_dial_size.w / 2; int y = chrono_tenth_hand_def.place_y - chrono_dial_size.h / 2; GRect destination = GRect(x, y, chrono_dial_size.w, chrono_dial_size.h); #ifdef PBL_PLATFORM_APLITE graphics_context_set_compositing_mode(ctx, draw_mode_table[config.draw_mode ^ APLITE_INVERT].paint_fg); graphics_draw_bitmap_in_rect(ctx, chrono_dial_black.bitmap, destination); graphics_context_set_compositing_mode(ctx, draw_mode_table[config.draw_mode ^ APLITE_INVERT].paint_bg); graphics_draw_bitmap_in_rect(ctx, chrono_dial_white.bitmap, destination); #else // PBL_PLATFORM_APLITE graphics_context_set_compositing_mode(ctx, GCompOpSet); graphics_draw_bitmap_in_rect(ctx, chrono_dial_white.bitmap, destination); #endif // PBL_PLATFORM_APLITE if (!keep_assets) { bwd_destroy(&chrono_dial_white); } #ifdef PBL_PLATFORM_APLITE bwd_destroy(&chrono_dial_black); #endif // PBL_PLATFORM_APLITE } }
void push_chrono_digital_handler(ClickRecognizerRef recognizer, void *context) { // app_log(APP_LOG_LEVEL_INFO, __FILE__, __LINE__, "push chrono digital"); if (!chrono_digital_window_showing) { // If we don't already have a chrono_digital_window object // created, create it now. if (chrono_digital_window == NULL) { chrono_digital_window = window_create(); if (chrono_digital_window == NULL) { trigger_memory_panic(__LINE__); return; } struct WindowHandlers chrono_digital_window_handlers; memset(&chrono_digital_window_handlers, 0, sizeof(chrono_digital_window_handlers)); chrono_digital_window_handlers.load = chrono_digital_window_load_handler; chrono_digital_window_handlers.appear = chrono_digital_window_appear_handler; chrono_digital_window_handlers.disappear = chrono_digital_window_disappear_handler; chrono_digital_window_handlers.unload = chrono_digital_window_unload_handler; window_set_window_handlers(chrono_digital_window, chrono_digital_window_handlers); } window_stack_push(chrono_digital_window, true); } }
void chrono_dial_layer_update_callback(Layer *me, GContext *ctx) { // app_log(APP_LOG_LEVEL_INFO, __FILE__, __LINE__, "chrono_dial_layer"); if (config.chrono_dial != CDM_off) { if (chrono_dial_white.bitmap == NULL) { load_chrono_dial(); if (chrono_dial_white.bitmap == NULL) { trigger_memory_panic(__LINE__); return; } } GRect destination = layer_get_bounds(me); destination.origin.x = 0; destination.origin.y = 0; #ifdef PBL_PLATFORM_APLITE graphics_context_set_compositing_mode(ctx, draw_mode_table[config.draw_mode].paint_fg); graphics_draw_bitmap_in_rect(ctx, chrono_dial_black.bitmap, destination); graphics_context_set_compositing_mode(ctx, draw_mode_table[config.draw_mode].paint_bg); graphics_draw_bitmap_in_rect(ctx, chrono_dial_white.bitmap, destination); #else // PBL_PLATFORM_APLITE graphics_context_set_compositing_mode(ctx, GCompOpSet); graphics_draw_bitmap_in_rect(ctx, chrono_dial_white.bitmap, destination); #endif // PBL_PLATFORM_APLITE } }
void push_chrono_digital_handler(ClickRecognizerRef recognizer, void *context) { // app_log(APP_LOG_LEVEL_INFO, __FILE__, __LINE__, "push chrono digital"); if (!chrono_digital_window_showing) { // Release some caches and repack our memory allocations before we // push the window. recreate_all_objects(); // If we don't already have a chrono_digital_window object // created, create it now. if (chrono_digital_window == NULL) { chrono_digital_window = window_create(); if (chrono_digital_window == NULL) { trigger_memory_panic(__LINE__); return; } struct WindowHandlers chrono_digital_window_handlers; memset(&chrono_digital_window_handlers, 0, sizeof(chrono_digital_window_handlers)); chrono_digital_window_handlers.load = chrono_digital_window_load_handler; chrono_digital_window_handlers.appear = chrono_digital_window_appear_handler; chrono_digital_window_handlers.disappear = chrono_digital_window_disappear_handler; chrono_digital_window_handlers.unload = chrono_digital_window_unload_handler; window_set_window_handlers(chrono_digital_window, chrono_digital_window_handlers); } // We'll push without animation, mainly so the pop also comes // without animation, which is important because the main window // needs to be able to grab the framebuffer once it draws, and // there's no way to ask the Pebble SDK when the window has // finished animating into its proper place (to know when it's // safe to grab the framebuffer). window_stack_push(chrono_digital_window, false); } }
void clock_face_layer_update_callback(Layer *me, GContext *ctx) { // app_log(APP_LOG_LEVEL_INFO, __FILE__, __LINE__, "clock_face_layer"); if (memory_panic_count > 5) { // In case we're in extreme memory panic mode--too little // available memory to even keep the clock face resident--we do // nothing in this function. return; } // Load the clock face from the resource file if we haven't already. if (clock_face.bitmap == NULL) { clock_face = rle_bwd_create(clock_face_table[config.face_index]); if (clock_face.bitmap == NULL) { trigger_memory_panic(__LINE__); return; } } // Draw the clock face into the layer. GRect destination = layer_get_bounds(me); destination.origin.x = 0; destination.origin.y = 0; graphics_context_set_compositing_mode(ctx, draw_mode_table[config.draw_mode].paint_assign); graphics_draw_bitmap_in_rect(ctx, clock_face.bitmap, destination); }
// Loads a font from the resource and returns it. It may return // either the intended font, or the fallback font. If it returns // the fallback font, this function automatically triggers a memory // panic alert. GFont safe_load_custom_font(int resource_id) { ResHandle resource = resource_get_handle(resource_id); GFont font = fonts_load_custom_font(resource); if (font == fallback_font) { app_log(APP_LOG_LEVEL_WARNING, __FILE__, __LINE__, "font %d failed to load", resource_id); trigger_memory_panic(__LINE__); } return font; }
void chrono_digital_window_load_handler(struct Window *window) { app_log(APP_LOG_LEVEL_INFO, __FILE__, __LINE__, "chrono digital loads"); GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD); Layer *chrono_digital_window_layer = window_get_root_layer(chrono_digital_window); chrono_digital_current_layer = text_layer_create(GRect(25, 120, 94, 48)); if (chrono_digital_current_layer == NULL) { trigger_memory_panic(__LINE__); return; } int i; for (i = 0; i < CHRONO_MAX_LAPS; ++i) { chrono_digital_laps_layer[i] = text_layer_create(GRect(25, 30 * i, 94, 30)); if (chrono_digital_laps_layer[i] == NULL) { trigger_memory_panic(__LINE__); return; } text_layer_set_text(chrono_digital_laps_layer[i], chrono_laps_buffer[i]); text_layer_set_text_color(chrono_digital_laps_layer[i], GColorBlack); text_layer_set_text_alignment(chrono_digital_laps_layer[i], GTextAlignmentRight); text_layer_set_overflow_mode(chrono_digital_laps_layer[i], GTextOverflowModeFill); text_layer_set_font(chrono_digital_laps_layer[i], font); layer_add_child(chrono_digital_window_layer, (Layer *)chrono_digital_laps_layer[i]); } text_layer_set_text(chrono_digital_current_layer, chrono_current_buffer); text_layer_set_text_color(chrono_digital_current_layer, GColorBlack); text_layer_set_text_alignment(chrono_digital_current_layer, GTextAlignmentRight); text_layer_set_overflow_mode(chrono_digital_current_layer, GTextOverflowModeFill); text_layer_set_font(chrono_digital_current_layer, font); layer_add_child(chrono_digital_window_layer, (Layer *)chrono_digital_current_layer); chrono_digital_line_layer = inverter_layer_create(GRect(0, 121, SCREEN_WIDTH, 1)); if (chrono_digital_line_layer == NULL) { trigger_memory_panic(__LINE__); return; } layer_add_child(chrono_digital_window_layer, (Layer *)chrono_digital_line_layer); }
// Draws a date window with the current lunar phase. void draw_lunar_window(Layer *me, GContext *ctx, DateWindowMode dwm, bool invert, bool opaque_layer) { // The draw_mode is the color to draw the frame of the date window. unsigned int draw_mode = invert ^ config.draw_mode; // The moon_draw_mode is the color to draw the moon within the date window. unsigned int moon_draw_mode = draw_mode; if (config.lunar_background) { // If the user specified an always-black background, that means moon_draw_mode is always 1. moon_draw_mode = 1; } draw_date_window_background(ctx, draw_mode, moon_draw_mode, opaque_layer); if (moon_bitmap.bitmap == NULL) { assert(current_placement.lunar_phase <= 7); if (moon_draw_mode == 0) { moon_bitmap = rle_bwd_create(RESOURCE_ID_MOON_BLACK_0 + current_placement.lunar_phase); } else { moon_bitmap = rle_bwd_create(RESOURCE_ID_MOON_WHITE_0 + current_placement.lunar_phase); } if (moon_bitmap.bitmap == NULL) { trigger_memory_panic(__LINE__); return; } if (config.lunar_direction) { // Draw the moon phases animating from left-to-right, as seen in // the southern hemisphere. (This really means drawing the moon // upside-down, as it would be seen by someone facing north.) flip_bitmap_x(moon_bitmap.bitmap, NULL); flip_bitmap_y(moon_bitmap.bitmap, NULL); } } // Draw the moon in the fg color. This will be black-on-white if // moon_draw_mode = 0, or white-on-black if moon_draw_mode = 1. // Since we have selected the particular moon resource above based // on draw_mode, we will always draw the moon in the correct color, // so that it looks like the moon. (Drawing the moon in the // inverted color would look weird.) graphics_context_set_compositing_mode(ctx, draw_mode_table[moon_draw_mode].paint_black); if (config.lunar_direction) { graphics_draw_bitmap_in_rect(ctx, moon_bitmap.bitmap, date_window_box_offset); } else { graphics_draw_bitmap_in_rect(ctx, moon_bitmap.bitmap, date_window_box); } }
// Draws a given hand on the face, using the vector structures. void draw_vector_hand(struct HandCache *hand_cache, struct HandDef *hand_def, int hand_index, GContext *ctx) { struct VectorHand *vector_hand = hand_def->vector_hand; int gi; if (hand_cache->vector_hand_index != hand_index) { // Force a new path. for (gi = 0; gi < vector_hand->num_groups; ++gi) { if (hand_cache->path[gi] != NULL) { gpath_destroy(hand_cache->path[gi]); hand_cache->path[gi] = NULL; } } hand_cache->vector_hand_index = hand_index; } GPoint center = { hand_def->place_x, hand_def->place_y }; int32_t angle = TRIG_MAX_ANGLE * hand_index / hand_def->num_steps; assert(vector_hand->num_groups <= HAND_CACHE_MAX_GROUPS); for (gi = 0; gi < vector_hand->num_groups; ++gi) { struct VectorHandGroup *group = &vector_hand->group[gi]; if (hand_cache->path[gi] == NULL) { hand_cache->path[gi] = gpath_create(&group->path_info); if (hand_cache->path[gi] == NULL) { trigger_memory_panic(__LINE__); return; } gpath_rotate_to(hand_cache->path[gi], angle); gpath_move_to(hand_cache->path[gi], center); } if (group->fill != 0) { graphics_context_set_fill_color(ctx, draw_mode_table[config.draw_mode].colors[group->fill]); gpath_draw_filled(ctx, hand_cache->path[gi]); } if (group->outline != 0) { graphics_context_set_stroke_color(ctx, draw_mode_table[config.draw_mode].colors[group->outline]); gpath_draw_outline(ctx, hand_cache->path[gi]); } } }
void chrono_digital_window_load_handler(struct Window *window) { app_log(APP_LOG_LEVEL_INFO, __FILE__, __LINE__, "chrono digital loads"); GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD); #ifdef PBL_SDK_3 Layer *chrono_digital_window_layer = window_get_root_layer(chrono_digital_window); chrono_status_bar_layer = status_bar_layer_create(); if (chrono_status_bar_layer == NULL) { trigger_memory_panic(__LINE__); return; } layer_add_child(chrono_digital_window_layer, status_bar_layer_get_layer(chrono_status_bar_layer)); chrono_digital_contents_layer = layer_create(GRect((SCREEN_WIDTH - DIGITAL_LAYER_WIDTH) / 2, STATUS_BAR_LAYER_HEIGHT, DIGITAL_LAYER_WIDTH, SCREEN_HEIGHT - STATUS_BAR_LAYER_HEIGHT)); if (chrono_digital_contents_layer == NULL) { trigger_memory_panic(__LINE__); return; } layer_add_child(chrono_digital_window_layer, chrono_digital_contents_layer); #else // PBL_SDK_3 // On SDK 2.0 and before, we don't create a separate contents layer; // we just use the root layer. Layer *chrono_digital_contents_layer = window_get_root_layer(chrono_digital_window); #endif // PBL_SDK_3 chrono_digital_current_layer = text_layer_create(GRect(25, LAP_HEIGHT * CHRONO_MAX_LAPS, 94, LAP_HEIGHT)); if (chrono_digital_current_layer == NULL) { trigger_memory_panic(__LINE__); return; } int i; for (i = 0; i < CHRONO_MAX_LAPS; ++i) { chrono_digital_laps_layer[i] = text_layer_create(GRect(25, LAP_HEIGHT * i, 94, LAP_HEIGHT)); if (chrono_digital_laps_layer[i] == NULL) { trigger_memory_panic(__LINE__); return; } text_layer_set_text(chrono_digital_laps_layer[i], chrono_laps_buffer[i]); text_layer_set_text_color(chrono_digital_laps_layer[i], GColorBlack); text_layer_set_text_alignment(chrono_digital_laps_layer[i], GTextAlignmentRight); text_layer_set_overflow_mode(chrono_digital_laps_layer[i], GTextOverflowModeFill); text_layer_set_font(chrono_digital_laps_layer[i], font); layer_add_child(chrono_digital_contents_layer, (Layer *)chrono_digital_laps_layer[i]); } text_layer_set_text(chrono_digital_current_layer, chrono_current_buffer); text_layer_set_text_color(chrono_digital_current_layer, GColorBlack); text_layer_set_text_alignment(chrono_digital_current_layer, GTextAlignmentRight); text_layer_set_overflow_mode(chrono_digital_current_layer, GTextOverflowModeFill); text_layer_set_font(chrono_digital_current_layer, font); layer_add_child(chrono_digital_contents_layer, (Layer *)chrono_digital_current_layer); chrono_digital_line_layer = layer_create(GRect(0, LAP_HEIGHT * CHRONO_MAX_LAPS + 1, SCREEN_WIDTH, 1)); if (chrono_digital_line_layer == NULL) { trigger_memory_panic(__LINE__); return; } layer_set_update_proc(chrono_digital_line_layer, &chrono_digital_line_layer_update_callback); layer_add_child(chrono_digital_contents_layer, (Layer *)chrono_digital_line_layer); }
// Draws a given hand on the face, using the bitmap structures. void draw_bitmap_hand(struct HandCache *hand_cache, struct HandDef *hand_def, int hand_index, GContext *ctx) { if (hand_cache->bitmap_hand_index != hand_index) { // Force a new bitmap. if (hand_cache->image.bitmap != NULL) { bwd_destroy(&hand_cache->image); } if (hand_cache->mask.bitmap != NULL) { bwd_destroy(&hand_cache->mask); } hand_cache->bitmap_hand_index = hand_index; } struct BitmapHandTableRow *hand = &hand_def->bitmap_table[hand_index]; int bitmap_index = hand->bitmap_index; struct BitmapHandCenterRow *lookup = &hand_def->bitmap_centers[bitmap_index]; int hand_resource_id = hand_def->resource_id + bitmap_index; int hand_resource_mask_id = hand_def->resource_mask_id + bitmap_index; if (hand_def->resource_id == hand_def->resource_mask_id) { // The hand does not have a mask. Draw the hand on top of the scene. if (hand_cache->image.bitmap == NULL) { if (hand_def->use_rle) { hand_cache->image = rle_bwd_create(hand_resource_id); } else { hand_cache->image = png_bwd_create(hand_resource_id); } if (hand_cache->image.bitmap == NULL) { hand_cache_destroy(hand_cache); trigger_memory_panic(__LINE__); return; } hand_cache->cx = lookup->cx; hand_cache->cy = lookup->cy; if (hand->flip_x) { // To minimize wasteful resource usage, if the hand is symmetric // we can store only the bitmaps for the right half of the clock // face, and flip them for the left half. flip_bitmap_x(hand_cache->image.bitmap, &hand_cache->cx); } if (hand->flip_y) { // We can also do this vertically. flip_bitmap_y(hand_cache->image.bitmap, &hand_cache->cy); } } // We make sure the dimensions of the GRect to draw into // are equal to the size of the bitmap--otherwise the image // will automatically tile. GRect destination = hand_cache->image.bitmap->bounds; // Place the hand's center point at place_x, place_y. destination.origin.x = hand_def->place_x - hand_cache->cx; destination.origin.y = hand_def->place_y - hand_cache->cy; // Specify a compositing mode to make the hands overlay on top of // each other, instead of the background parts of the bitmaps // blocking each other. if (hand_def->paint_black) { // Painting foreground ("white") pixels as black. graphics_context_set_compositing_mode(ctx, draw_mode_table[config.draw_mode].paint_black); } else { // Painting foreground ("white") pixels as white. graphics_context_set_compositing_mode(ctx, draw_mode_table[config.draw_mode].paint_white); } graphics_draw_bitmap_in_rect(ctx, hand_cache->image.bitmap, destination); } else { // The hand has a mask, so use it to draw the hand opaquely. if (hand_cache->image.bitmap == NULL) { if (hand_def->use_rle) { hand_cache->image = rle_bwd_create(hand_resource_id); hand_cache->mask = rle_bwd_create(hand_resource_mask_id); } else { hand_cache->image = png_bwd_create(hand_resource_id); hand_cache->mask = png_bwd_create(hand_resource_mask_id); } if (hand_cache->image.bitmap == NULL || hand_cache->mask.bitmap == NULL) { hand_cache_destroy(hand_cache); trigger_memory_panic(__LINE__); return; } hand_cache->cx = lookup->cx; hand_cache->cy = lookup->cy; if (hand->flip_x) { // To minimize wasteful resource usage, if the hand is symmetric // we can store only the bitmaps for the right half of the clock // face, and flip them for the left half. flip_bitmap_x(hand_cache->image.bitmap, &hand_cache->cx); flip_bitmap_x(hand_cache->mask.bitmap, NULL); } if (hand->flip_y) { // We can also do this vertically. flip_bitmap_y(hand_cache->image.bitmap, &hand_cache->cy); flip_bitmap_y(hand_cache->mask.bitmap, NULL); } } GRect destination = hand_cache->image.bitmap->bounds; destination.origin.x = hand_def->place_x - hand_cache->cx; destination.origin.y = hand_def->place_y - hand_cache->cy; graphics_context_set_compositing_mode(ctx, draw_mode_table[config.draw_mode].paint_white); graphics_draw_bitmap_in_rect(ctx, hand_cache->mask.bitmap, destination); graphics_context_set_compositing_mode(ctx, draw_mode_table[config.draw_mode].paint_black); graphics_draw_bitmap_in_rect(ctx, hand_cache->image.bitmap, destination); } }