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(); } }
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); }
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(); }
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); }
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; }
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); } }
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; }