Exemplo n.º 1
0
// 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);
}
Exemplo n.º 2
0
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
}
Exemplo n.º 3
0
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
  }
}
Exemplo n.º 4
0
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);
  }
}
Exemplo n.º 5
0
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
  }
}
Exemplo n.º 6
0
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);
  }
}
Exemplo n.º 7
0
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);
}
Exemplo n.º 8
0
// 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;
}
Exemplo n.º 9
0
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);
}
Exemplo n.º 10
0
// 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);
  }
}
Exemplo n.º 11
0
// 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]);
    }
  }
}
Exemplo n.º 12
0
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);
}
Exemplo n.º 13
0
// 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);
  }
}