+ cairo_t *cr = gc->cr;
+ cairo_text_extents_t extents;
+ double dx, dy;
+ char buffer[80];
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(buffer, sizeof(buffer), fmt, args);
+ va_end(args);
+
+ cairo_text_extents(cr, buffer, &extents);
+ dx = 0;
+ if (tro->allign == CENTER)
+ dx = -(extents.width/2 + extents.x_bearing);
+ dy = extents.height * 1.2;
+
+ move_to(gc, x, y);
+ cairo_rel_move_to(cr, dx, dy);
+
+ cairo_text_path(cr, buffer);
+ cairo_set_source_rgb(cr, 0, 0, 0);
+ cairo_stroke(cr);
+
+ move_to(gc, x, y);
+ cairo_rel_move_to(cr, dx, dy);
+
+ cairo_set_source_rgb(cr, tro->r, tro->g, tro->b);
+ cairo_show_text(cr, buffer);
+}
+
+/*
+ * Find the next maximum point in a 10-minute window.
+ *
+ * We exit early if we hit "enough" of a depth reversal,
+ * which is roughly 10 feet.
+ */
+static struct sample *next_minmax(struct sample *sample, struct sample *end, int minmax)
+{
+ const int enough = 3000;
+ struct sample *result;
+ int timelimit, depthlimit;
+
+ if (sample >= end)
+ return 0;
+
+ timelimit = 24*60*60;
+ depthlimit = sample->depth.mm;
+ result = NULL;
+
+ for (;;) {
+ int time, depth;
+
+ sample++;
+ if (sample >= end)
+ return NULL;
+ time = sample->time.seconds;
+ depth = sample->depth.mm;
+ if (time > timelimit)
+ break;
+
+ if (minmax) {
+ if (depth <= depthlimit) {
+ if (depthlimit - depth > enough)
+ break;
+ continue;
+ }
+ } else {
+ if (depth >= depthlimit) {
+ if (depth - depthlimit > enough)
+ break;
+ continue;
+ }
+ }
+
+ result = sample;
+ depthlimit = depth;
+ /* Look up to ten minutes into the future */
+ timelimit = time + 600;
+ }
+ return result;
+}
+
+static void render_depth_sample(struct graphics_context *gc, struct sample *sample)
+{
+ text_render_options_t tro = {1.0, 0.2, 0.2, CENTER};
+ int sec = sample->time.seconds;
+ depth_t depth = sample->depth;
+ const char *fmt;
+ double d;
+
+ switch (output_units.length) {
+ case METERS:
+ d = depth.mm / 1000.0;
+ fmt = "%.1f";
+ break;
+ case FEET:
+ d = to_feet(depth);
+ fmt = "%.0f";
+ break;
+ }
+ plot_text(gc, &tro, sec, depth.mm, fmt, d);
+}
+
+static void plot_text_samples(struct graphics_context *gc, struct sample *a, struct sample *b)
+{
+ struct sample *max, *min;
+
+ if (b < a)
+ return;
+ if (b->time.seconds - a->time.seconds < 3*60)
+ return;
+
+ max = next_minmax(a, b, 1);
+ if (max) {
+ render_depth_sample(gc, max);
+ min = next_minmax(max, b, 0);
+ if (min) {
+ plot_text_samples(gc, min, b);
+ return;
+ }
+ }
+}
+
+static void plot_depth_text(struct dive *dive, struct graphics_context *gc)
+{
+ struct sample *sample, *end;
+ int maxtime, maxdepth;
+
+ /* Get plot scaling limits */
+ maxtime = round_seconds_up(dive->duration.seconds);
+ maxdepth = round_depth_up(dive->maxdepth);
+
+ gc->scalex = maxtime;
+ gc->scaley = maxdepth;
+
+ cairo_set_font_size(gc->cr, 14);
+
+ /*
+ * We never take the last sample into account.
+ * It should be a surface event anyway, although
+ * there are buggy cases where it isn't..
+ */
+ sample = dive->sample;
+ end = dive->sample + dive->samples - 1;
+
+ plot_text_samples(gc, sample, end);
+}
+
+static void plot_depth_profile(struct dive *dive, struct graphics_context *gc)
+{
+ cairo_t *cr = gc->cr;