コード例 #1
0
ファイル: navigation.c プロジェクト: lemagoup/darktable
void _lib_navigation_set_position(dt_lib_module_t *self, double x, double y, int wd, int ht)
{
  dt_lib_navigation_t *d = (dt_lib_navigation_t *)self->data;

  dt_dev_zoom_t zoom = dt_control_get_dev_zoom();
  int closeup = dt_control_get_dev_closeup();
  float zoom_x = dt_control_get_dev_zoom_x();
  float zoom_y = dt_control_get_dev_zoom_y();

  if(d->dragging && zoom != DT_ZOOM_FIT)
  {
    const int inset = DT_NAVIGATION_INSET;
    const float width = wd - 2 * inset, height = ht - 2 * inset;
    const dt_develop_t *dev = darktable.develop;
    int iwd, iht;
    dt_dev_get_processed_size(dev, &iwd, &iht);
    zoom_x = fmaxf(
        -.5,
        fminf(((x - inset) / width - .5f) / (iwd * fminf(wd / (float)iwd, ht / (float)iht) / (float)wd), .5));
    zoom_y = fmaxf(
        -.5, fminf(((y - inset) / height - .5f) / (iht * fminf(wd / (float)iwd, ht / (float)iht) / (float)ht),
                   .5));
    dt_dev_check_zoom_bounds(darktable.develop, &zoom_x, &zoom_y, zoom, closeup, NULL, NULL);
    dt_control_set_dev_zoom_x(zoom_x);
    dt_control_set_dev_zoom_y(zoom_y);

    /* redraw myself */
    gtk_widget_queue_draw(self->widget);

    /* redraw pipe */
    dt_dev_invalidate(darktable.develop);
    dt_control_queue_redraw_center();
  }
}
コード例 #2
0
ファイル: snapshots.c プロジェクト: PolarFox/darktable
static void _lib_snapshots_add_button_clicked_callback(GtkWidget *widget, gpointer user_data)
{
  dt_lib_module_t *self = (dt_lib_module_t*)user_data;
  dt_lib_snapshots_t *d = (dt_lib_snapshots_t *)self->data;

  /* backup last snapshot slot */
  dt_lib_snapshot_t last = d->snapshot[d->size-1];

  /* rotate slots down to make room for new one on top */
  for (int k = d->size-1; k > 0; k--)
  {
    GtkWidget *b = d->snapshot[k].button;
    d->snapshot[k] = d->snapshot[k-1];
    d->snapshot[k].button = b;
    gtk_button_set_label(GTK_BUTTON(d->snapshot[k].button),
                         gtk_button_get_label(GTK_BUTTON(d->snapshot[k-1].button)));
  }

  /* update top slot with new snapshot */
  char label[64];
  GtkWidget *b = d->snapshot[0].button;
  d->snapshot[0] = last;
  d->snapshot[0].button = b;
  const gchar *name = _("original");
  if (darktable.develop->history_end > 0)
  {
    dt_iop_module_t *module  = ((dt_dev_history_item_t *)g_list_nth_data(darktable.develop->history,
                                darktable.develop->history_end-1))->module;
    if (module)
      name = module->name();
    else
      name = _("unknown");
  }
  g_snprintf(label,sizeof(label),"%s (%d)", name, darktable.develop->history_end);
  gtk_button_set_label(GTK_BUTTON(d->snapshot[0].button), label);

  dt_lib_snapshot_t *s = d->snapshot + 0;
  s->zoom_y = dt_control_get_dev_zoom_y();
  s->zoom_x = dt_control_get_dev_zoom_x();
  s->zoom = dt_control_get_dev_zoom();
  s->closeup = dt_control_get_dev_closeup();
  s->zoom_scale = dt_control_get_dev_zoom_scale();

  /* update slots used */
  if (d->num_snapshots != d->size)
    d->num_snapshots++;

  /* show active snapshot slots */
  for (uint32_t k=0; k < d->num_snapshots; k++)
    gtk_widget_show(d->snapshot[k].button);

  /* request a new snapshot for top slot */
  dt_dev_snapshot_request(darktable.develop, (const char *)&d->snapshot[0].filename);

}
コード例 #3
0
ファイル: navigation.c プロジェクト: lemagoup/darktable
static void _zoom_preset_change(int val)
{
  // dt_lib_module_t *self = (dt_lib_module_t *)user_data;
  dt_develop_t *dev = darktable.develop;
  if(!dev) return;
  dt_dev_zoom_t zoom;
  int closeup, procw, proch;
  float zoom_x, zoom_y;
  zoom = dt_control_get_dev_zoom();
  closeup = dt_control_get_dev_closeup();
  zoom_x = dt_control_get_dev_zoom_x();
  zoom_y = dt_control_get_dev_zoom_y();
  dt_dev_get_processed_size(dev, &procw, &proch);
  float scale = 0;
  zoom_x = 0.0f; //+= (1.0/scale)*(x - .5f*dev->width )/procw;
  zoom_y = 0.0f; //+= (1.0/scale)*(y - .5f*dev->height)/proch;
  if(val == 0)
  {
    scale = 0.5 * dt_dev_get_zoom_scale(dev, DT_ZOOM_FIT, 1.0, 0);
    zoom = DT_ZOOM_FREE;
  }
  else if(val == 1)
  {
    zoom = DT_ZOOM_FIT;
    scale = dt_dev_get_zoom_scale(dev, DT_ZOOM_FIT, 1.0, 0);
  }
  else if(val == 2)
  {
    scale = dt_dev_get_zoom_scale(dev, DT_ZOOM_1, 1.0, 0);
    zoom = DT_ZOOM_1;
  }
  else if(val == 3)
  {
    scale = 2.0f;
    zoom = DT_ZOOM_FREE;
  }

  dt_dev_check_zoom_bounds(dev, &zoom_x, &zoom_y, zoom, closeup, NULL, NULL);
  dt_control_set_dev_zoom_scale(scale);
  dt_control_set_dev_zoom(zoom);
  dt_control_set_dev_closeup(closeup);
  dt_control_set_dev_zoom_x(zoom_x);
  dt_control_set_dev_zoom_y(zoom_y);
  dt_dev_invalidate(dev);
  dt_control_queue_redraw();
}
コード例 #4
0
ファイル: graduatednd.c プロジェクト: dtorop/darktable
void gui_post_expose(struct dt_iop_module_t *self, cairo_t *cr, int32_t width, int32_t height,
                     int32_t pointerx, int32_t pointery)
{
  dt_develop_t *dev = self->dev;
  dt_iop_graduatednd_gui_data_t *g = (dt_iop_graduatednd_gui_data_t *)self->gui_data;
  dt_iop_graduatednd_params_t *p = (dt_iop_graduatednd_params_t *)self->params;

  float wd = dev->preview_pipe->backbuf_width;
  float ht = dev->preview_pipe->backbuf_height;
  float zoom_y = dt_control_get_dev_zoom_y();
  float zoom_x = dt_control_get_dev_zoom_x();
  dt_dev_zoom_t zoom = dt_control_get_dev_zoom();
  int closeup = dt_control_get_dev_closeup();
  float zoom_scale = dt_dev_get_zoom_scale(dev, zoom, closeup ? 2 : 1, 1);

  cairo_translate(cr, width / 2.0, height / 2.0f);
  cairo_scale(cr, zoom_scale, zoom_scale);
  cairo_translate(cr, -.5f * wd - zoom_x * wd, -.5f * ht - zoom_y * ht);

  // we get the extremities of the line
  if(g->define == 0)
  {
    if(!set_points_from_grad(self, &g->xa, &g->ya, &g->xb, &g->yb, p->rotation, p->offset)) return;
    g->define = 1;
  }

  float xa = g->xa * wd, xb = g->xb * wd, ya = g->ya * ht, yb = g->yb * ht;
  // the lines
  cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
  if(g->selected == 3 || g->dragging == 3)
    cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(5.0) / zoom_scale);
  else
    cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(3.0) / zoom_scale);
  cairo_set_source_rgba(cr, .3, .3, .3, .8);

  cairo_move_to(cr, xa, ya);
  cairo_line_to(cr, xb, yb);
  cairo_stroke(cr);

  if(g->selected == 3 || g->dragging == 3)
    cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.0) / zoom_scale);
  else
    cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0) / zoom_scale);
  cairo_set_source_rgba(cr, .8, .8, .8, .8);
  cairo_move_to(cr, xa, ya);
  cairo_line_to(cr, xb, yb);
  cairo_stroke(cr);

  // the extremities
  float x1, y1, x2, y2;
  float l = sqrt((xb - xa) * (xb - xa) + (yb - ya) * (yb - ya));
  const float ext = wd * 0.01f / zoom_scale;
  x1 = xa + (xb - xa) * ext / l;
  y1 = ya + (yb - ya) * ext / l;
  x2 = (xa + x1) / 2.0;
  y2 = (ya + y1) / 2.0;
  y2 += (x1 - xa);
  x2 -= (y1 - ya);
  cairo_move_to(cr, xa, ya);
  cairo_line_to(cr, x1, y1);
  cairo_line_to(cr, x2, y2);
  cairo_close_path(cr);
  cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0) / zoom_scale);
  if(g->selected == 1 || g->dragging == 1)
    cairo_set_source_rgba(cr, .8, .8, .8, 1.0);
  else
    cairo_set_source_rgba(cr, .8, .8, .8, .5);
  cairo_fill_preserve(cr);
  if(g->selected == 1 || g->dragging == 1)
    cairo_set_source_rgba(cr, .3, .3, .3, 1.0);
  else
    cairo_set_source_rgba(cr, .3, .3, .3, .5);
  cairo_stroke(cr);

  x1 = xb - (xb - xa) * ext / l;
  y1 = yb - (yb - ya) * ext / l;
  x2 = (xb + x1) / 2.0;
  y2 = (yb + y1) / 2.0;
  y2 += (xb - x1);
  x2 -= (yb - y1);
  cairo_move_to(cr, xb, yb);
  cairo_line_to(cr, x1, y1);
  cairo_line_to(cr, x2, y2);
  cairo_close_path(cr);
  cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0) / zoom_scale);
  if(g->selected == 2 || g->dragging == 2)
    cairo_set_source_rgba(cr, .8, .8, .8, 1.0);
  else
    cairo_set_source_rgba(cr, .8, .8, .8, .5);
  cairo_fill_preserve(cr);
  if(g->selected == 2 || g->dragging == 2)
    cairo_set_source_rgba(cr, .3, .3, .3, 1.0);
  else
    cairo_set_source_rgba(cr, .3, .3, .3, .5);
  cairo_stroke(cr);
}
コード例 #5
0
ファイル: navigation.c プロジェクト: lemagoup/darktable
static gboolean _lib_navigation_expose_callback(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
{
  dt_lib_module_t *self = (dt_lib_module_t *)user_data;
  dt_lib_navigation_t *d = (dt_lib_navigation_t *)self->data;

  const int inset = DT_NAVIGATION_INSET;
  GtkAllocation allocation;
  gtk_widget_get_allocation(widget, &allocation);
  int width = allocation.width, height = allocation.height;

  dt_develop_t *dev = darktable.develop;

  if(dev->preview_status != DT_DEV_PIXELPIPE_VALID) return FALSE;

  /* get the current style */
  GtkStyle *style = gtk_rc_get_style_by_paths(gtk_settings_get_default(), NULL, "GtkWidget", GTK_TYPE_WIDGET);
  if(!style) style = gtk_rc_get_style(widget);
  cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
  cairo_t *cr = cairo_create(cst);

  /* fill background */
  cairo_set_source_rgb(cr, style->bg[0].red / 65535.0, style->bg[0].green / 65535.0,
                       style->bg[0].blue / 65535.0);
  cairo_paint(cr);

  width -= 2 * inset;
  height -= 2 * inset;
  cairo_translate(cr, inset, inset);

  /* draw navigation image if available */
  if(dev->preview_pipe->backbuf && (dev->preview_status == DT_DEV_PIXELPIPE_VALID))
  {
    dt_pthread_mutex_t *mutex = &dev->preview_pipe->backbuf_mutex;
    dt_pthread_mutex_lock(mutex);
    const int wd = dev->preview_pipe->backbuf_width;
    const int ht = dev->preview_pipe->backbuf_height;
    const float scale = fminf(width / (float)wd, height / (float)ht);

    const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, wd);
    cairo_surface_t *surface
        = cairo_image_surface_create_for_data(dev->preview_pipe->backbuf, CAIRO_FORMAT_RGB24, wd, ht, stride);
    cairo_translate(cr, width / 2.0, height / 2.0f);
    cairo_scale(cr, scale, scale);
    cairo_translate(cr, -.5f * wd, -.5f * ht);

    // draw shadow around
    float alpha = 1.0f;
    for(int k = 0; k < 4; k++)
    {
      cairo_rectangle(cr, -k / scale, -k / scale, wd + 2 * k / scale, ht + 2 * k / scale);
      cairo_set_source_rgba(cr, 0, 0, 0, alpha);
      alpha *= 0.6f;
      cairo_fill(cr);
    }

    cairo_rectangle(cr, 0, 0, wd - 2, ht - 1);
    cairo_set_source_surface(cr, surface, 0, 0);
    cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_FAST);
    cairo_fill(cr);
    cairo_surface_destroy(surface);

    dt_pthread_mutex_unlock(mutex);

    // draw box where we are
    dt_dev_zoom_t zoom = dt_control_get_dev_zoom();
    int closeup = dt_control_get_dev_closeup();
    float zoom_x = dt_control_get_dev_zoom_x();
    float zoom_y = dt_control_get_dev_zoom_y();
    const float min_scale = dt_dev_get_zoom_scale(dev, DT_ZOOM_FIT, closeup ? 2.0 : 1.0, 0);
    const float cur_scale = dt_dev_get_zoom_scale(dev, zoom, closeup ? 2.0 : 1.0, 0);
    // avoid numerical instability for small resolutions:
    double h, w;
    if(cur_scale > min_scale)
    {
      float boxw = 1, boxh = 1;
      dt_dev_check_zoom_bounds(darktable.develop, &zoom_x, &zoom_y, zoom, closeup, &boxw, &boxh);

      cairo_translate(cr, wd * (.5f + zoom_x), ht * (.5f + zoom_y));
      cairo_set_source_rgb(cr, 0., 0., 0.);
      cairo_set_line_width(cr, 1.f / scale);
      boxw *= wd;
      boxh *= ht;
      cairo_rectangle(cr, -boxw / 2 - 1, -boxh / 2 - 1, boxw + 2, boxh + 2);
      cairo_stroke(cr);
      cairo_set_source_rgb(cr, 1., 1., 1.);
      cairo_rectangle(cr, -boxw / 2, -boxh / 2, boxw, boxh);
      cairo_stroke(cr);
    }
    if(fabsf(cur_scale - min_scale) > 0.001f)
    {
      /* Zoom % */
      cairo_identity_matrix(cr);
      cairo_translate(cr, 0, height);
      cairo_set_source_rgba(cr, 1., 1., 1., 0.5);
      cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
      cairo_set_font_size(cr, DT_PIXEL_APPLY_DPI(11));

      char zoomline[5];
      snprintf(zoomline, sizeof(zoomline), "%.0f%%", cur_scale * 100);

      cairo_text_extents_t ext;
      cairo_text_extents(cr, zoomline, &ext);
      h = d->zoom_h = ext.height;
      w = d->zoom_w = ext.width;

      cairo_move_to(cr, width - w - h * 1.1, 0);

      cairo_save(cr);
      cairo_set_line_width(cr, 2.0);
      cairo_set_source_rgb(cr, style->bg[0].red / 65535.0, style->bg[0].green / 65535.0,
                           style->bg[0].blue / 65535.0);
      cairo_text_path(cr, zoomline);
      cairo_stroke_preserve(cr);
      cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
      cairo_fill(cr);
      cairo_restore(cr);
    }
    else
    {
      // draw the zoom-to-fit icon
      cairo_identity_matrix(cr);
      cairo_translate(cr, 0, height);
      cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
      cairo_text_extents_t ext;
      cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
      cairo_set_font_size(cr, DT_PIXEL_APPLY_DPI(11));
      cairo_text_extents(cr, "100%", &ext); // dummy text, just to get the height
      h = d->zoom_h = ext.height;
      w = h * 1.5;
      float sp = h * 0.6;
      d->zoom_w = w + sp;

      cairo_move_to(cr, width - w - h - sp, -1.0 * h);
      cairo_rectangle(cr, width - w - h - sp, -1.0 * h, w, h);
      cairo_set_source_rgb(cr, 0.2, 0.2, 0.2);
      cairo_fill(cr);

      cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
      cairo_move_to(cr, width - w * 0.8 - h - sp, -1.0 * h);
      cairo_line_to(cr, width - w - h - sp, -1.0 * h);
      cairo_line_to(cr, width - w - h - sp, -0.7 * h);
      cairo_stroke(cr);
      cairo_move_to(cr, width - w - h - sp, -0.3 * h);
      cairo_line_to(cr, width - w - h - sp, 0);
      cairo_line_to(cr, width - w * 0.8 - h - sp, 0);
      cairo_stroke(cr);
      cairo_move_to(cr, width - w * 0.2 - h - sp, 0);
      cairo_line_to(cr, width - h - sp, 0);
      cairo_line_to(cr, width - h - sp, -0.3 * h);
      cairo_stroke(cr);
      cairo_move_to(cr, width - h - sp, -0.7 * h);
      cairo_line_to(cr, width - h - sp, -1.0 * h);
      cairo_line_to(cr, width - w * 0.2 - h - sp, -1.0 * h);
      cairo_stroke(cr);
    }

    cairo_move_to(cr, width - 0.95 * h, -0.9 * h);
    cairo_line_to(cr, width - 0.05 * h, -0.9 * h);
    cairo_line_to(cr, width - 0.5 * h, -0.1 * h);
    cairo_fill(cr);
  }

  /* blit memsurface into widget */
  cairo_destroy(cr);
  cairo_t *cr_pixmap = gdk_cairo_create(gtk_widget_get_window(widget));
  cairo_set_source_surface(cr_pixmap, cst, 0, 0);
  cairo_paint(cr_pixmap);
  cairo_destroy(cr_pixmap);
  cairo_surface_destroy(cst);

  return TRUE;
}
コード例 #6
0
ファイル: circle.c プロジェクト: a3novy/darktable
static void dt_circle_events_post_expose(cairo_t *cr, float zoom_scale, dt_masks_form_gui_t *gui, int index)
{
  double dashed[] = { 4.0, 4.0 };
  dashed[0] /= zoom_scale;
  dashed[1] /= zoom_scale;
  int len = sizeof(dashed) / sizeof(dashed[0]);
  dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);

  // add a preview when creating a circle
  // in creation mode
  if(gui->creation)
  {
    float wd = darktable.develop->preview_pipe->iwidth;
    float ht = darktable.develop->preview_pipe->iheight;

    if(gui->guipoints_count == 0)
    {
      dt_masks_form_t *form = darktable.develop->form_visible;
      if(!form) return;

      float radius1, radius2;
      if(form->type & (DT_MASKS_CLONE | DT_MASKS_NON_CLONE))
      {
        radius1 = dt_conf_get_sanitize_set("plugins/darkroom/spots/circle_size", 0.001f, 0.5f);
        radius2 = dt_conf_get_sanitize_set("plugins/darkroom/spots/circle_border", 0.0005f, 0.5f);
      }
      else
      {
        radius1 = dt_conf_get_sanitize_set("plugins/darkroom/masks/circle/size", 0.001f, 0.5f);
        radius2 = dt_conf_get_sanitize_set("plugins/darkroom/masks/circle/border", 0.0005f, 0.5f);
      }
      radius2 += radius1;
      radius1 *= MIN(wd, ht);
      radius2 *= MIN(wd, ht);

      float xpos, ypos;
      if((gui->posx == -1.f && gui->posy == -1.f) || gui->mouse_leaved_center)
      {
        const float zoom_x = dt_control_get_dev_zoom_x();
        const float zoom_y = dt_control_get_dev_zoom_y();
        xpos = (.5f + zoom_x) * darktable.develop->preview_pipe->backbuf_width;
        ypos = (.5f + zoom_y) * darktable.develop->preview_pipe->backbuf_height;
      }
      else
      {
        xpos = gui->posx;
        ypos = gui->posy;
      }

      cairo_save(cr);

      // draw circle
      cairo_set_dash(cr, dashed, 0, 0);
      cairo_set_line_width(cr, 3.0 / zoom_scale);
      cairo_set_source_rgba(cr, .3, .3, .3, .8);

      cairo_arc(cr, xpos, ypos, radius1, 0, 2.0 * M_PI);

      cairo_stroke_preserve(cr);
      cairo_set_line_width(cr, 1.0 / zoom_scale);
      cairo_set_source_rgba(cr, .8, .8, .8, .8);
      cairo_stroke(cr);

      // draw border
      cairo_set_dash(cr, dashed, len, 0);
      cairo_set_line_width(cr, 1.0 / zoom_scale);
      cairo_set_source_rgba(cr, .3, .3, .3, .8);

      cairo_arc(cr, xpos, ypos, radius2, 0, 2.0 * M_PI);

      cairo_stroke_preserve(cr);
      cairo_set_line_width(cr, 1.0 / zoom_scale);
      cairo_set_source_rgba(cr, .8, .8, .8, .8);
      cairo_set_dash(cr, dashed, len, 4);
      cairo_stroke(cr);

      // draw a cross where the source will be created
      if(form->type & DT_MASKS_CLONE)
      {
        float x = 0.f, y = 0.f;
        dt_masks_calculate_source_pos_value(gui, DT_MASKS_CIRCLE, xpos, ypos, xpos, ypos, &x, &y, FALSE);
        dt_masks_draw_clone_source_pos(cr, zoom_scale, x, y);
      }

      cairo_restore(cr);
    }

    return;
  }

  if(!gpt) return;
  float dx = 0, dy = 0, dxs = 0, dys = 0;
  if((gui->group_selected == index) && gui->form_dragging)
  {
    dx = gui->posx + gui->dx - gpt->points[0];
    dy = gui->posy + gui->dy - gpt->points[1];
  }
  if((gui->group_selected == index) && gui->source_dragging)
  {
    dxs = gui->posx + gui->dx - gpt->source[0];
    dys = gui->posy + gui->dy - gpt->source[1];
  }

  if(gpt->points_count > 6)
  {
    cairo_set_dash(cr, dashed, 0, 0);
    if((gui->group_selected == index) && (gui->form_selected || gui->form_dragging))
      cairo_set_line_width(cr, 5.0 / zoom_scale);
    else
      cairo_set_line_width(cr, 3.0 / zoom_scale);
    cairo_set_source_rgba(cr, .3, .3, .3, .8);
    cairo_move_to(cr, gpt->points[2] + dx, gpt->points[3] + dy);
    for(int i = 2; i < gpt->points_count; i++)
    {
      cairo_line_to(cr, gpt->points[i * 2] + dx, gpt->points[i * 2 + 1] + dy);
    }
    cairo_line_to(cr, gpt->points[2] + dx, gpt->points[3] + dy);
    cairo_stroke_preserve(cr);
    if((gui->group_selected == index) && (gui->form_selected || gui->form_dragging))
      cairo_set_line_width(cr, 2.0 / zoom_scale);
    else
      cairo_set_line_width(cr, 1.0 / zoom_scale);
    cairo_set_source_rgba(cr, .8, .8, .8, .8);
    cairo_stroke(cr);
  }

  // draw border
  if((gui->group_selected == index) && gpt->border_count > 6)
  {
    cairo_set_dash(cr, dashed, len, 0);
    if((gui->group_selected == index) && (gui->border_selected))
      cairo_set_line_width(cr, 2.0 / zoom_scale);
    else
      cairo_set_line_width(cr, 1.0 / zoom_scale);
    cairo_set_source_rgba(cr, .3, .3, .3, .8);

    cairo_move_to(cr, gpt->border[2] + dx, gpt->border[3] + dy);
    for(int i = 2; i < gpt->border_count; i++)
    {
      cairo_line_to(cr, gpt->border[i * 2] + dx, gpt->border[i * 2 + 1] + dy);
    }
    cairo_line_to(cr, gpt->border[2] + dx, gpt->border[3] + dy);

    cairo_stroke_preserve(cr);
    if((gui->group_selected == index) && (gui->border_selected))
      cairo_set_line_width(cr, 2.0 / zoom_scale);
    else
      cairo_set_line_width(cr, 1.0 / zoom_scale);
    cairo_set_source_rgba(cr, .8, .8, .8, .8);
    cairo_set_dash(cr, dashed, len, 4);
    cairo_stroke(cr);
  }

  // draw the source if any
  if(gpt->source_count > 6)
  {
    const float radius = fabs(gpt->points[2] - gpt->points[0]);

    // compute the dest inner circle intersection with the line from source center to dest center.
    float cdx = gpt->source[0] + dxs - gpt->points[0] - dx;
    float cdy = gpt->source[1] + dys - gpt->points[1] - dy;

    // we don't draw the line if source==point
    if(cdx != 0.0 && cdy != 0.0)
    {
      cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
      float cangle = atan(cdx / cdy);

      if(cdy > 0)
        cangle = (M_PI / 2) - cangle;
      else
        cangle = -(M_PI / 2) - cangle;

      // (arrowx,arrowy) is the point of intersection, we move it (factor 1.11) a bit farther than the
      // inner circle to avoid superposition.
      float arrowx = gpt->points[0] + 1.11 * radius * cos(cangle) + dx;
      float arrowy = gpt->points[1] + 1.11 * radius * sin(cangle) + dy;

      cairo_move_to(cr, gpt->source[0] + dxs, gpt->source[1] + dys); // source center
      cairo_line_to(cr, arrowx, arrowy);                             // dest border
      // then draw to line for the arrow itself
      const float arrow_scale = 8.0;
      cairo_move_to(cr, arrowx + arrow_scale * cos(cangle + (0.4)),
                    arrowy + arrow_scale * sin(cangle + (0.4)));
      cairo_line_to(cr, arrowx, arrowy);
      cairo_line_to(cr, arrowx + arrow_scale * cos(cangle - (0.4)),
                    arrowy + arrow_scale * sin(cangle - (0.4)));

      cairo_set_dash(cr, dashed, 0, 0);
      if((gui->group_selected == index) && (gui->form_selected || gui->form_dragging))
        cairo_set_line_width(cr, 2.5 / zoom_scale);
      else
        cairo_set_line_width(cr, 1.5 / zoom_scale);
      cairo_set_source_rgba(cr, .3, .3, .3, .8);
      cairo_stroke_preserve(cr);
      if((gui->group_selected == index) && (gui->form_selected || gui->form_dragging))
        cairo_set_line_width(cr, 1.0 / zoom_scale);
      else
        cairo_set_line_width(cr, 0.5 / zoom_scale);
      cairo_set_source_rgba(cr, .8, .8, .8, .8);
      cairo_stroke(cr);
    }

    // we draw the source
    cairo_set_dash(cr, dashed, 0, 0);
    if((gui->group_selected == index) && (gui->form_selected || gui->form_dragging))
      cairo_set_line_width(cr, 2.5 / zoom_scale);
    else
      cairo_set_line_width(cr, 1.5 / zoom_scale);
    cairo_set_source_rgba(cr, .3, .3, .3, .8);
    cairo_move_to(cr, gpt->source[2] + dxs, gpt->source[3] + dys);
    for(int i = 2; i < gpt->source_count; i++)
    {
      cairo_line_to(cr, gpt->source[i * 2] + dxs, gpt->source[i * 2 + 1] + dys);
    }
    cairo_line_to(cr, gpt->source[2] + dxs, gpt->source[3] + dys);
    cairo_stroke_preserve(cr);
    if((gui->group_selected == index) && (gui->form_selected || gui->form_dragging))
      cairo_set_line_width(cr, 1.0 / zoom_scale);
    else
      cairo_set_line_width(cr, 0.5 / zoom_scale);
    cairo_set_source_rgba(cr, .8, .8, .8, .8);
    cairo_stroke(cr);
  }
}
コード例 #7
-1
ファイル: navigation.c プロジェクト: AdamMajer/darktable
static gboolean _lib_navigation_draw_callback(GtkWidget *widget, cairo_t *crf, gpointer user_data)
{
  dt_lib_module_t *self = (dt_lib_module_t *)user_data;
  dt_lib_navigation_t *d = (dt_lib_navigation_t *)self->data;

  const int inset = DT_NAVIGATION_INSET;
  GtkAllocation allocation;
  gtk_widget_get_allocation(widget, &allocation);
  int width = allocation.width, height = allocation.height;

  dt_develop_t *dev = darktable.develop;

  /* double buffering of image data: only take new data if valid */
  if(dev->preview_pipe->backbuf && dev->preview_status == DT_DEV_PIXELPIPE_VALID)
  {
    /* re-allocate in case of changed image dimensions */
    if(d->buffer == NULL || dev->preview_pipe->backbuf_width != d->wd || dev->preview_pipe->backbuf_height != d->ht)
    {
      g_free(d->buffer);
      d->wd = dev->preview_pipe->backbuf_width;
      d->ht = dev->preview_pipe->backbuf_height;
      d->buffer = g_malloc0((size_t)d->wd * d->ht * 4 * sizeof(unsigned char));
    }

    /* update buffer if new data is available */
    if(d->buffer && dev->preview_pipe->input_timestamp > d->timestamp)
    {
      dt_pthread_mutex_t *mutex = &dev->preview_pipe->backbuf_mutex;
      dt_pthread_mutex_lock(mutex);
      memcpy(d->buffer, dev->preview_pipe->backbuf, (size_t)d->wd * d->ht * 4 * sizeof(unsigned char));
      d->timestamp = dev->preview_pipe->input_timestamp;
      dt_pthread_mutex_unlock(mutex);
    }
  }

  /* get the current style */
  cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
  cairo_t *cr = cairo_create(cst);

  GtkStyleContext *context = gtk_widget_get_style_context(widget);
  gtk_render_background(context, cr, 0, 0, allocation.width, allocation.height);

  width -= 2 * inset;
  height -= 2 * inset;
  cairo_translate(cr, inset, inset);

  /* draw navigation image if available */
  if(d->buffer)
  {
    cairo_save(cr);
    const int wd = d->wd;
    const int ht = d->ht;
    const float scale = fminf(width / (float)wd, height / (float)ht);

    const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, wd);
    cairo_surface_t *surface
        = cairo_image_surface_create_for_data(d->buffer, CAIRO_FORMAT_RGB24, wd, ht, stride);
    cairo_translate(cr, width / 2.0, height / 2.0f);
    cairo_scale(cr, scale, scale);
    cairo_translate(cr, -.5f * wd, -.5f * ht);

    // draw shadow around
    float alpha = 1.0f;
    for(int k = 0; k < 4; k++)
    {
      cairo_rectangle(cr, -k / scale, -k / scale, wd + 2 * k / scale, ht + 2 * k / scale);
      cairo_set_source_rgba(cr, 0, 0, 0, alpha);
      alpha *= 0.6f;
      cairo_fill(cr);
    }

    cairo_rectangle(cr, 0, 0, wd - 2, ht - 1);
    cairo_set_source_surface(cr, surface, 0, 0);
    cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_FAST);
    cairo_fill(cr);
    cairo_surface_destroy(surface);

    // draw box where we are
    dt_dev_zoom_t zoom = dt_control_get_dev_zoom();
    int closeup = dt_control_get_dev_closeup();
    float zoom_x = dt_control_get_dev_zoom_x();
    float zoom_y = dt_control_get_dev_zoom_y();
    const float min_scale = dt_dev_get_zoom_scale(dev, DT_ZOOM_FIT, closeup ? 2.0 : 1.0, 0);
    const float cur_scale = dt_dev_get_zoom_scale(dev, zoom, closeup ? 2.0 : 1.0, 0);
    // avoid numerical instability for small resolutions:
    double h, w;
    if(cur_scale > min_scale)
    {
      float boxw = 1, boxh = 1;
      dt_dev_check_zoom_bounds(darktable.develop, &zoom_x, &zoom_y, zoom, closeup, &boxw, &boxh);
      cairo_translate(cr, wd * (.5f + zoom_x), ht * (.5f + zoom_y));
      cairo_set_source_rgb(cr, 0., 0., 0.);
      cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.f / scale));
      boxw *= wd;
      boxh *= ht;
      cairo_rectangle(cr, -boxw / 2 - 1, -boxh / 2 - 1, boxw + 2, boxh + 2);
      cairo_stroke(cr);
      cairo_set_source_rgb(cr, 1., 1., 1.);
      cairo_rectangle(cr, -boxw / 2, -boxh / 2, boxw, boxh);
      cairo_stroke(cr);
    }
    cairo_restore(cr);
    if(fabsf(cur_scale - min_scale) > 0.001f)
    {
      /* Zoom % */
      PangoLayout *layout;
      PangoRectangle ink;
      PangoFontDescription *desc = pango_font_description_copy_static(darktable.bauhaus->pango_font_desc);
      pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
      layout = pango_cairo_create_layout(cr);
      const float fontsize = DT_PIXEL_APPLY_DPI(11);
      pango_font_description_set_absolute_size(desc, fontsize * PANGO_SCALE);
      pango_layout_set_font_description(layout, desc);
      cairo_translate(cr, 0, height);
      cairo_set_source_rgba(cr, 1., 1., 1., 0.5);
      cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);

      char zoomline[5];
      snprintf(zoomline, sizeof(zoomline), "%.0f%%", cur_scale * 100);

      pango_layout_set_text(layout, zoomline, -1);
      pango_layout_get_pixel_extents(layout, &ink, NULL);
      h = d->zoom_h = ink.height;
      w = d->zoom_w = ink.width;

      cairo_move_to(cr, width - w - h * 1.1 - ink.x, - fontsize);

      cairo_save(cr);
      cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.0));

      GdkRGBA *color;
      gtk_style_context_get(context, gtk_widget_get_state_flags(widget), "background-color", &color, NULL);

      gdk_cairo_set_source_rgba(cr, color);
      pango_cairo_layout_path(cr, layout);
      cairo_stroke_preserve(cr);
      cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
      cairo_fill(cr);
      cairo_restore(cr);

      gdk_rgba_free(color);
      pango_font_description_free(desc);
      g_object_unref(layout);

    }
    else
    {
      // draw the zoom-to-fit icon
      cairo_translate(cr, 0, height);
      cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);

      static int height = -1;
      if(height == -1)
      {
        PangoLayout *layout;
        PangoRectangle ink;
        PangoFontDescription *desc = pango_font_description_copy_static(darktable.bauhaus->pango_font_desc);
        pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
        layout = pango_cairo_create_layout(cr);
        pango_font_description_set_absolute_size(desc, DT_PIXEL_APPLY_DPI(11) * PANGO_SCALE);
        pango_layout_set_font_description(layout, desc);
        pango_layout_set_text(layout, "100%", -1); // dummy text, just to get the height
        pango_layout_get_pixel_extents(layout, &ink, NULL);
        height = ink.height;
        pango_font_description_free(desc);
        g_object_unref(layout);
      }

      h = d->zoom_h = height;
      w = h * 1.5;
      float sp = h * 0.6;
      d->zoom_w = w + sp;

      cairo_move_to(cr, width - w - h - sp, -1.0 * h);
      cairo_rectangle(cr, width - w - h - sp, -1.0 * h, w, h);
      cairo_set_source_rgb(cr, 0.2, 0.2, 0.2);
      cairo_fill(cr);

      cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.0));

      cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
      cairo_move_to(cr, width - w * 0.8 - h - sp, -1.0 * h);
      cairo_line_to(cr, width - w - h - sp, -1.0 * h);
      cairo_line_to(cr, width - w - h - sp, -0.7 * h);
      cairo_stroke(cr);
      cairo_move_to(cr, width - w - h - sp, -0.3 * h);
      cairo_line_to(cr, width - w - h - sp, 0);
      cairo_line_to(cr, width - w * 0.8 - h - sp, 0);
      cairo_stroke(cr);
      cairo_move_to(cr, width - w * 0.2 - h - sp, 0);
      cairo_line_to(cr, width - h - sp, 0);
      cairo_line_to(cr, width - h - sp, -0.3 * h);
      cairo_stroke(cr);
      cairo_move_to(cr, width - h - sp, -0.7 * h);
      cairo_line_to(cr, width - h - sp, -1.0 * h);
      cairo_line_to(cr, width - w * 0.2 - h - sp, -1.0 * h);
      cairo_stroke(cr);
    }

    cairo_move_to(cr, width - 0.95 * h, -0.9 * h);
    cairo_line_to(cr, width - 0.05 * h, -0.9 * h);
    cairo_line_to(cr, width - 0.5 * h, -0.1 * h);
    cairo_fill(cr);
  }

  /* blit memsurface into widget */
  cairo_destroy(cr);
  cairo_set_source_surface(crf, cst, 0, 0);
  cairo_paint(crf);
  cairo_surface_destroy(cst);

  return TRUE;
}