void border_scrolled(dt_view_t *view, double x, double y, int which, int up) { dt_develop_t *dev = (dt_develop_t *)view->data; dt_dev_zoom_t zoom; int closeup; float zoom_x, zoom_y; DT_CTL_GET_GLOBAL(zoom, dev_zoom); DT_CTL_GET_GLOBAL(closeup, dev_closeup); DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); if(which > 1) { if(up) zoom_x -= 0.02; else zoom_x += 0.02; } else { if(up) zoom_y -= 0.02; else zoom_y += 0.02; } dt_dev_check_zoom_bounds(dev, &zoom_x, &zoom_y, zoom, closeup, NULL, NULL); DT_CTL_SET_GLOBAL(dev_zoom_x, zoom_x); DT_CTL_SET_GLOBAL(dev_zoom_y, zoom_y); dt_dev_invalidate(dev); dt_control_queue_redraw(); }
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; int closeup; float zoom_x, zoom_y; DT_CTL_GET_GLOBAL(zoom, dev_zoom); DT_CTL_GET_GLOBAL(closeup, dev_closeup); DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); DT_CTL_GET_GLOBAL(zoom_y, 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_CTL_SET_GLOBAL(dev_zoom_x, zoom_x); DT_CTL_SET_GLOBAL(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(); } }
void scrolled(dt_view_t *self, double x, double y, int up, int state) { const int32_t capwd = darktable.thumbnail_width; const int32_t capht = darktable.thumbnail_height; dt_develop_t *dev = (dt_develop_t *)self->data; const int32_t width_i = self->width; const int32_t height_i = self->height; if(width_i > capwd) x += (capwd-width_i) *.5f; if(height_i > capht) y += (capht-height_i)*.5f; int handled = 0; if(dev->gui_module && dev->gui_module->scrolled) handled = dev->gui_module->scrolled(dev->gui_module, x, y, up, state); if(handled) return; // free zoom dt_dev_zoom_t zoom; int closeup, procw, proch; float zoom_x, zoom_y; DT_CTL_GET_GLOBAL(zoom, dev_zoom); DT_CTL_GET_GLOBAL(closeup, dev_closeup); DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); dt_dev_get_processed_size(dev, &procw, &proch); float scale = dt_dev_get_zoom_scale(dev, zoom, closeup ? 2.0 : 1.0, 0); const float minscale = dt_dev_get_zoom_scale(dev, DT_ZOOM_FIT, 1.0, 0); // offset from center now (current zoom_{x,y} points there) float mouse_off_x = x - .5*dev->width, mouse_off_y = y - .5*dev->height; zoom_x += mouse_off_x/(procw*scale); zoom_y += mouse_off_y/(proch*scale); zoom = DT_ZOOM_FREE; closeup = 0; if(up) { if (scale == 1.0f) return; else scale += .1f*(1.0f - minscale); } else { if (scale == minscale) return; else scale -= .1f*(1.0f - minscale); } DT_CTL_SET_GLOBAL(dev_zoom_scale, scale); if(scale > 0.99) zoom = DT_ZOOM_1; if(scale < minscale + 0.01) zoom = DT_ZOOM_FIT; if(zoom != DT_ZOOM_1) { zoom_x -= mouse_off_x/(procw*scale); zoom_y -= mouse_off_y/(proch*scale); } dt_dev_check_zoom_bounds(dev, &zoom_x, &zoom_y, zoom, closeup, NULL, NULL); DT_CTL_SET_GLOBAL(dev_zoom, zoom); DT_CTL_SET_GLOBAL(dev_closeup, closeup); if(zoom != DT_ZOOM_1) { DT_CTL_SET_GLOBAL(dev_zoom_x, zoom_x); DT_CTL_SET_GLOBAL(dev_zoom_y, zoom_y); } dt_dev_invalidate(dev); dt_control_queue_redraw(); }
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(); }
static void _lib_duplicate_thumb_press_callback(GtkWidget *widget, GdkEventButton *event, dt_lib_module_t *self) { dt_lib_duplicate_t *d = (dt_lib_duplicate_t *)self->data; int imgid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),"imgid")); if(event->button == 1) { if(event->type == GDK_BUTTON_PRESS) { dt_develop_t *dev = darktable.develop; if(!dev) return; dt_dev_zoom_t zoom; int closeup; float zoom_x, zoom_y; closeup = dt_control_get_dev_closeup(); float scale = 0; zoom = DT_ZOOM_FIT; scale = dt_dev_get_zoom_scale(dev, DT_ZOOM_FIT, 1.0, 0); 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(); dt_dev_invalidate(darktable.develop); d->imgid = imgid; dt_control_queue_redraw_center(); } else if(event->type == GDK_2BUTTON_PRESS) { // to select the duplicate, we reuse the filmstrip proxy _do_select(imgid); } } }
static gboolean zoom_key_accel(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval, GdkModifierType modifier, gpointer data) { dt_develop_t *dev = darktable.develop; int zoom, closeup; float zoom_x, zoom_y; switch ((long int)data) { case 1: DT_CTL_GET_GLOBAL(zoom, dev_zoom); DT_CTL_GET_GLOBAL(closeup, dev_closeup); if(zoom == DT_ZOOM_1) closeup ^= 1; DT_CTL_SET_GLOBAL(dev_closeup, closeup); DT_CTL_SET_GLOBAL(dev_zoom, DT_ZOOM_1); dt_dev_invalidate(dev); break; case 2: DT_CTL_SET_GLOBAL(dev_zoom, DT_ZOOM_FILL); dt_dev_check_zoom_bounds(dev, &zoom_x, &zoom_y, DT_ZOOM_FILL, 0, NULL, NULL); DT_CTL_SET_GLOBAL(dev_zoom_x, zoom_x); DT_CTL_SET_GLOBAL(dev_zoom_y, zoom_y); DT_CTL_SET_GLOBAL(dev_closeup, 0); dt_dev_invalidate(dev); break; case 3: DT_CTL_SET_GLOBAL(dev_zoom, DT_ZOOM_FIT); DT_CTL_SET_GLOBAL(dev_zoom_x, 0); DT_CTL_SET_GLOBAL(dev_zoom_y, 0); DT_CTL_SET_GLOBAL(dev_closeup, 0); dt_dev_invalidate(dev); break; default: break; } dt_control_queue_redraw_center(); return TRUE; }
static gboolean _lib_navigation_expose_callback(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { const int inset = DT_NAVIGATION_INSET; int width = widget->allocation.width, height = widget->allocation.height; dt_develop_t *dev = darktable.develop; if (dev->preview_dirty) 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_dirty) { 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; int closeup; float zoom_x, zoom_y; DT_CTL_GET_GLOBAL(zoom, dev_zoom); DT_CTL_GET_GLOBAL(closeup, dev_closeup); DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); DT_CTL_GET_GLOBAL(zoom_y, 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: if(cur_scale > min_scale+0.001) { 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); } } /* 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 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; }
void expose(dt_view_t *self, cairo_t *cri, int32_t width_i, int32_t height_i, int32_t pointerx, int32_t pointery) { // startup-time conf parameter: const int32_t capwd = darktable.thumbnail_width; const int32_t capht = darktable.thumbnail_height; // if width or height > max pipeline pixels: center the view and clamp. int32_t width = MIN(width_i, capwd); int32_t height = MIN(height_i, capht); cairo_set_source_rgb (cri, .2, .2, .2); cairo_save(cri); cairo_set_fill_rule(cri, CAIRO_FILL_RULE_EVEN_ODD); cairo_rectangle(cri, 0, 0, width_i, height_i); cairo_rectangle(cri, MAX(1.0, width_i -capwd) *.5f, MAX(1.0, height_i-capht) *.5f, MIN(width, width_i-1), MIN(height, height_i-1)); cairo_fill (cri); cairo_restore(cri); if(width_i > capwd) cairo_translate(cri, -(capwd-width_i) *.5f, 0.0f); if(height_i > capht) cairo_translate(cri, 0.0f, -(capht-height_i)*.5f); cairo_save(cri); dt_develop_t *dev = (dt_develop_t *)self->data; if(dev->gui_synch && !dev->image_loading) { // synch module guis from gtk thread: darktable.gui->reset = 1; GList *modules = dev->iop; while(modules) { dt_iop_module_t *module = (dt_iop_module_t *)(modules->data); dt_iop_gui_update(module); modules = g_list_next(modules); } darktable.gui->reset = 0; dev->gui_synch = 0; } if(dev->image_dirty || dev->pipe->input_timestamp < dev->preview_pipe->input_timestamp) dt_dev_process_image(dev); if(dev->preview_dirty || dev->pipe->input_timestamp > dev->preview_pipe->input_timestamp) dt_dev_process_preview(dev); dt_pthread_mutex_t *mutex = NULL; int wd, ht, stride, closeup; int32_t zoom; float zoom_x, zoom_y; DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); DT_CTL_GET_GLOBAL(zoom, dev_zoom); DT_CTL_GET_GLOBAL(closeup, dev_closeup); static cairo_surface_t *image_surface = NULL; static int image_surface_width = 0, image_surface_height = 0, image_surface_imgid = -1; if(image_surface_width != width || image_surface_height != height || image_surface == NULL) { // create double-buffered image to draw on, to make modules draw more fluently. image_surface_width = width; image_surface_height = height; if(image_surface) cairo_surface_destroy(image_surface); image_surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); image_surface_imgid = -1; // invalidate old stuff } cairo_surface_t *surface; cairo_t *cr = cairo_create(image_surface); // adjust scroll bars { float zx = zoom_x, zy = zoom_y, boxw = 1., boxh = 1.; dt_dev_check_zoom_bounds(dev, &zx, &zy, zoom, closeup, &boxw, &boxh); dt_view_set_scrollbar(self, zx+.5-boxw*.5, 1.0, boxw, zy+.5-boxh*.5, 1.0, boxh); } if(!dev->image_dirty && dev->pipe->input_timestamp >= dev->preview_pipe->input_timestamp) { // draw image mutex = &dev->pipe->backbuf_mutex; dt_pthread_mutex_lock(mutex); wd = dev->pipe->backbuf_width; ht = dev->pipe->backbuf_height; stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, wd); surface = cairo_image_surface_create_for_data (dev->pipe->backbuf, CAIRO_FORMAT_RGB24, wd, ht, stride); cairo_set_source_rgb (cr, .2, .2, .2); cairo_paint(cr); cairo_translate(cr, .5f*(width-wd), .5f*(height-ht)); if(closeup) { const float closeup_scale = 2.0; cairo_scale(cr, closeup_scale, closeup_scale); float boxw = 1, boxh = 1, zx0 = zoom_x, zy0 = zoom_y, zx1 = zoom_x, zy1 = zoom_y, zxm = -1.0, zym = -1.0; dt_dev_check_zoom_bounds(dev, &zx0, &zy0, zoom, 0, &boxw, &boxh); dt_dev_check_zoom_bounds(dev, &zx1, &zy1, zoom, 1, &boxw, &boxh); dt_dev_check_zoom_bounds(dev, &zxm, &zym, zoom, 1, &boxw, &boxh); const float fx = 1.0 - fmaxf(0.0, (zx0 - zx1)/(zx0 - zxm)), fy = 1.0 - fmaxf(0.0, (zy0 - zy1)/(zy0 - zym)); cairo_translate(cr, -wd/(2.0*closeup_scale) * fx, -ht/(2.0*closeup_scale) * fy); } cairo_rectangle(cr, 0, 0, wd, ht); cairo_set_source_surface (cr, surface, 0, 0); cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_FAST); cairo_fill_preserve(cr); cairo_set_line_width(cr, 1.0); cairo_set_source_rgb (cr, .3, .3, .3); cairo_stroke(cr); cairo_surface_destroy (surface); dt_pthread_mutex_unlock(mutex); image_surface_imgid = dev->image_storage.id; } else if(!dev->preview_dirty) { // draw preview mutex = &dev->preview_pipe->backbuf_mutex; dt_pthread_mutex_lock(mutex); wd = dev->preview_pipe->backbuf_width; ht = dev->preview_pipe->backbuf_height; float zoom_scale = dt_dev_get_zoom_scale(dev, zoom, closeup ? 2 : 1, 1); cairo_set_source_rgb (cr, .2, .2, .2); cairo_paint(cr); cairo_rectangle(cr, 0, 0, width, height); cairo_clip(cr); stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, wd); 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, zoom_scale, zoom_scale); cairo_translate(cr, -.5f*wd-zoom_x*wd, -.5f*ht-zoom_y*ht); // avoid to draw the 1px garbage that sometimes shows up in the preview :( cairo_rectangle(cr, 0, 0, wd-1, 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); image_surface_imgid = dev->image_storage.id; } cairo_restore(cri); if(image_surface_imgid == dev->image_storage.id) { cairo_destroy(cr); cairo_set_source_surface(cri, image_surface, 0, 0); cairo_paint(cri); } /* check if we should create a snapshot of view */ if(darktable.develop->proxy.snapshot.request) { /* reset the request */ darktable.develop->proxy.snapshot.request = FALSE; /* validation of snapshot filename */ g_assert(darktable.develop->proxy.snapshot.filename != NULL); /* Store current image surface to snapshot file. FIXME: add checks so that we dont make snapshots of preview pipe image surface. */ cairo_surface_write_to_png(image_surface, darktable.develop->proxy.snapshot.filename); } // Displaying sample areas if enabled if(darktable.lib->proxy.colorpicker.live_samples && darktable.lib->proxy.colorpicker.display_samples) { GSList *samples = darktable.lib->proxy.colorpicker.live_samples; dt_colorpicker_sample_t *sample = NULL; cairo_save(cri); int32_t zoom, closeup; float zoom_x, zoom_y; float wd = dev->preview_pipe->backbuf_width; float ht = dev->preview_pipe->backbuf_height; DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); DT_CTL_GET_GLOBAL(zoom, dev_zoom); DT_CTL_GET_GLOBAL(closeup, dev_closeup); float zoom_scale = dt_dev_get_zoom_scale(dev, zoom, closeup ? 2 : 1, 1); cairo_translate(cri, width/2.0, height/2.0f); cairo_scale(cri, zoom_scale, zoom_scale); cairo_translate(cri, -.5f*wd-zoom_x*wd, -.5f*ht-zoom_y*ht); while(samples) { sample = samples->data; cairo_set_line_width(cri, 1.0/zoom_scale); if(sample == darktable.lib->proxy.colorpicker.selected_sample) cairo_set_source_rgb(cri, .2, 0, 0); else cairo_set_source_rgb(cri, 0, 0, .2); float *box = sample->box; float *point = sample->point; if(sample->size == DT_COLORPICKER_SIZE_BOX) { cairo_rectangle(cri, box[0]*wd, box[1]*ht, (box[2] - box[0])*wd, (box[3] - box[1])*ht); cairo_stroke(cri); cairo_translate(cri, 1.0/zoom_scale, 1.0/zoom_scale); if(sample == darktable.lib->proxy.colorpicker.selected_sample) cairo_set_source_rgb(cri, .8, 0, 0); else cairo_set_source_rgb(cri, 0, 0, .8); cairo_rectangle(cri, box[0]*wd + 1.0/zoom_scale, box[1]*ht, (box[2] - box[0])*wd - 3./zoom_scale, (box[3] - box[1])*ht - 2./zoom_scale); cairo_stroke(cri); } else { cairo_rectangle(cri, point[0] * wd - .01 * wd, point[1] * ht - .01 * wd, .02 * wd, .02 * wd); cairo_stroke(cri); if(sample == darktable.lib->proxy.colorpicker.selected_sample) cairo_set_source_rgb(cri, .8, 0, 0); else cairo_set_source_rgb(cri, 0, 0, .8); cairo_rectangle(cri, (point[0] - 0.01) * wd + 1.0/zoom_scale, point[1] * ht - 0.01 * wd + 1.0/zoom_scale, .02 * wd - 2./zoom_scale, .02 * wd - 2./zoom_scale); cairo_move_to(cri, point[0] * wd, point[1] * ht - .01 * wd + 1./zoom_scale); cairo_line_to(cri, point[0] * wd, point[1] * ht + .01 * wd - 1./zoom_scale); cairo_move_to(cri, point[0] * wd - .01 * wd + 1./zoom_scale, point[1] * ht); cairo_line_to(cri, point[0] * wd + .01 * wd - 1./zoom_scale, point[1] * ht); cairo_stroke(cri); } samples = g_slist_next(samples); } cairo_restore(cri); } // execute module callback hook. if(dev->gui_module && dev->gui_module->request_color_pick) { int32_t zoom, closeup; float zoom_x, zoom_y; float wd = dev->preview_pipe->backbuf_width; float ht = dev->preview_pipe->backbuf_height; DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); DT_CTL_GET_GLOBAL(zoom, dev_zoom); DT_CTL_GET_GLOBAL(closeup, dev_closeup); float zoom_scale = dt_dev_get_zoom_scale(dev, zoom, closeup ? 2 : 1, 1); cairo_translate(cri, width/2.0, height/2.0f); cairo_scale(cri, zoom_scale, zoom_scale); cairo_translate(cri, -.5f*wd-zoom_x*wd, -.5f*ht-zoom_y*ht); // cairo_set_operator(cri, CAIRO_OPERATOR_XOR); cairo_set_line_width(cri, 1.0/zoom_scale); cairo_set_source_rgb(cri, .2, .2, .2); float *box = dev->gui_module->color_picker_box; float *point = dev->gui_module->color_picker_point; if(darktable.lib->proxy.colorpicker.size) { cairo_rectangle(cri, box[0]*wd, box[1]*ht, (box[2] - box[0])*wd, (box[3] - box[1])*ht); cairo_stroke(cri); cairo_translate(cri, 1.0/zoom_scale, 1.0/zoom_scale); cairo_set_source_rgb(cri, .8, .8, .8); cairo_rectangle(cri, box[0]*wd + 1.0/zoom_scale, box[1]*ht, (box[2] - box[0])*wd - 3./zoom_scale, (box[3] - box[1])*ht - 2./zoom_scale); cairo_stroke(cri); } else { cairo_rectangle(cri, point[0] * wd - .01 * wd, point[1] * ht - .01 * wd, .02 * wd, .02 * wd); cairo_stroke(cri); cairo_set_source_rgb(cri, .8, .8, .8); cairo_rectangle(cri, (point[0] - 0.01) * wd + 1.0/zoom_scale, point[1] * ht - 0.01 * wd + 1.0/zoom_scale, .02 * wd - 2./zoom_scale, .02 * wd - 2./zoom_scale); cairo_move_to(cri, point[0] * wd, point[1] * ht - .01 * wd + 1./zoom_scale); cairo_line_to(cri, point[0] * wd, point[1] * ht + .01 * wd - 1./zoom_scale); cairo_move_to(cri, point[0] * wd - .01 * wd + 1./zoom_scale, point[1] * ht); cairo_line_to(cri, point[0] * wd + .01 * wd - 1./zoom_scale, point[1] * ht); cairo_stroke(cri); } } else if(dev->gui_module && dev->gui_module->gui_post_expose) { if(width_i > capwd) pointerx += (capwd-width_i) *.5f; if(height_i > capht) pointery += (capht-height_i)*.5f; dev->gui_module->gui_post_expose(dev->gui_module, cri, width, height, pointerx, pointery); } }
void enter(dt_view_t *self) { /* connect to ui pipe finished signal for redraw */ dt_control_signal_connect(darktable.signals, DT_SIGNAL_DEVELOP_UI_PIPE_FINISHED,G_CALLBACK(_darkroom_ui_pipe_finish_signal_callback), (gpointer)self); dt_print(DT_DEBUG_CONTROL, "[run_job+] 11 %f in darkroom mode\n", dt_get_wtime()); dt_develop_t *dev = (dt_develop_t *)self->data; dev->gui_leaving = 0; dev->gui_module = NULL; select_this_image(dev->image_storage.id); DT_CTL_SET_GLOBAL(dev_zoom, DT_ZOOM_FIT); DT_CTL_SET_GLOBAL(dev_zoom_x, 0); DT_CTL_SET_GLOBAL(dev_zoom_y, 0); DT_CTL_SET_GLOBAL(dev_closeup, 0); // take a copy of the image struct for convenience. dt_dev_load_image(darktable.develop, dev->image_storage.id); /* * Add view specific tool buttons */ /* create favorite plugin preset popup tool */ GtkWidget *favorite_presets = dtgtk_button_new(dtgtk_cairo_paint_presets, CPF_STYLE_FLAT|CPF_DO_NOT_USE_BORDER); g_object_set(G_OBJECT(favorite_presets), "tooltip-text", _("quick access to presets of your favorites"), (char *)NULL); g_signal_connect (G_OBJECT (favorite_presets), "clicked", G_CALLBACK (_darkroom_ui_favorite_presets_popupmenu), NULL); dt_view_manager_view_toolbox_add(darktable.view_manager, favorite_presets); /* add IOP modules to plugin list */ /* create quick styles popup menu tool */ GtkWidget *styles = dtgtk_button_new (dtgtk_cairo_paint_styles,CPF_STYLE_FLAT|CPF_DO_NOT_USE_BORDER); g_signal_connect (G_OBJECT (styles), "clicked", G_CALLBACK (_darkroom_ui_apply_style_popupmenu), NULL); g_object_set (G_OBJECT (styles), "tooltip-text", _("quick access for applying any of your styles"), (char *)NULL); dt_view_manager_view_toolbox_add(darktable.view_manager, styles); /* * add IOP modules to plugin list */ // avoid triggering of events before plugin is ready: darktable.gui->reset = 1; char option[1024]; GList *modules = g_list_last(dev->iop); while(modules) { dt_iop_module_t *module = (dt_iop_module_t *)(modules->data); /* initialize gui if iop have one defined */ if (!dt_iop_is_hidden(module)) { module->gui_init(module); dt_iop_reload_defaults(module); /* add module to right panel */ GtkWidget *expander = dt_iop_gui_get_expander(module); dt_ui_container_add_widget(darktable.gui->ui, DT_UI_CONTAINER_PANEL_RIGHT_CENTER, expander); snprintf(option, 1024, "plugins/darkroom/%s/expanded", module->op); dt_iop_gui_set_expanded(module, dt_conf_get_bool(option)); } /* setup key accelerators */ module->accel_closures = NULL; if(module->connect_key_accels) module->connect_key_accels(module); dt_iop_connect_common_accels(module); modules = g_list_previous(modules); } darktable.gui->reset = 0; /* signal that darktable.develop is initialized and ready to be used */ dt_control_signal_raise(darktable.signals,DT_SIGNAL_DEVELOP_INITIALIZE); // synch gui and flag gegl pipe as dirty // this is done here and not in dt_read_history, as it would else be triggered before module->gui_init. dt_dev_pop_history_items(dev, dev->history_end); /* ensure that filmstrip shows current image */ dt_view_filmstrip_scroll_to_image(darktable.view_manager, dev->image_storage.id, FALSE); // switch on groups as they where last time: dt_dev_modulegroups_set(dev, dt_conf_get_int("plugins/darkroom/groups")); // make signals work again: darktable.gui->reset = 0; // get last active plugin: gchar *active_plugin = dt_conf_get_string("plugins/darkroom/active"); if(active_plugin) { GList *modules = dev->iop; while(modules) { dt_iop_module_t *module = (dt_iop_module_t *)(modules->data); if(!strcmp(module->op, active_plugin)) dt_iop_request_focus(module); modules = g_list_next(modules); } g_free(active_plugin); } // image should be there now. float zoom_x, zoom_y; dt_dev_check_zoom_bounds(dev, &zoom_x, &zoom_y, DT_ZOOM_FIT, 0, NULL, NULL); DT_CTL_SET_GLOBAL(dev_zoom_x, zoom_x); DT_CTL_SET_GLOBAL(dev_zoom_y, zoom_y); /* connect signal for filmstrip image activate */ dt_control_signal_connect(darktable.signals, DT_SIGNAL_VIEWMANAGER_FILMSTRIP_ACTIVATE, G_CALLBACK(_view_darkroom_filmstrip_activate_callback), self); // prefetch next few from first selected image on. dt_view_filmstrip_prefetch(); }
int button_pressed(dt_view_t *self, double x, double y, int which, int type, uint32_t state) { const int32_t capwd = darktable.thumbnail_width; const int32_t capht = darktable.thumbnail_height; dt_develop_t *dev = (dt_develop_t *)self->data; const int32_t width_i = self->width; const int32_t height_i = self->height; if(width_i > capwd) x += (capwd-width_i) *.5f; if(height_i > capht) y += (capht-height_i)*.5f; int handled = 0; if(dev->gui_module && dev->gui_module->request_color_pick && which == 1) { float zoom_x, zoom_y; dt_dev_get_pointer_zoom_pos(dev, x, y, &zoom_x, &zoom_y); if(darktable.lib->proxy.colorpicker.size) { dev->gui_module->color_picker_box[0] = .5f+zoom_x; dev->gui_module->color_picker_box[1] = .5f+zoom_y; dev->gui_module->color_picker_box[2] = .5f+zoom_x; dev->gui_module->color_picker_box[3] = .5f+zoom_y; } else { dev->gui_module->color_picker_point[0] = .5f+zoom_x; dev->gui_module->color_picker_point[1] = .5f+zoom_y; dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH; dt_dev_invalidate_all(dev); } dt_control_queue_redraw(); return 1; } if(dev->gui_module && dev->gui_module->button_pressed) handled = dev->gui_module->button_pressed(dev->gui_module, x, y, which, type, state); if(handled) return handled; if(which == 1 && type == GDK_2BUTTON_PRESS) return 0; if(which == 1) { dt_control_change_cursor(GDK_HAND1); return 1; } if(which == 2) { // zoom to 1:1 2:1 and back dt_dev_zoom_t zoom; int closeup, procw, proch; float zoom_x, zoom_y; DT_CTL_GET_GLOBAL(zoom, dev_zoom); DT_CTL_GET_GLOBAL(closeup, dev_closeup); DT_CTL_GET_GLOBAL(zoom_x, dev_zoom_x); DT_CTL_GET_GLOBAL(zoom_y, dev_zoom_y); dt_dev_get_processed_size(dev, &procw, &proch); const float scale = dt_dev_get_zoom_scale(dev, zoom, closeup ? 2 : 1, 0); zoom_x += (1.0/scale)*(x - .5f*dev->width )/procw; zoom_y += (1.0/scale)*(y - .5f*dev->height)/proch; if(zoom == DT_ZOOM_1) { if(!closeup) closeup = 1; else { zoom = DT_ZOOM_FIT; zoom_x = zoom_y = 0.0f; closeup = 0; } } else zoom = DT_ZOOM_1; dt_dev_check_zoom_bounds(dev, &zoom_x, &zoom_y, zoom, closeup, NULL, NULL); DT_CTL_SET_GLOBAL(dev_zoom, zoom); DT_CTL_SET_GLOBAL(dev_closeup, closeup); DT_CTL_SET_GLOBAL(dev_zoom_x, zoom_x); DT_CTL_SET_GLOBAL(dev_zoom_y, zoom_y); dt_dev_invalidate(dev); return 1; } return 0; }
void mouse_moved(dt_view_t *self, double x, double y, int which) { const int32_t capwd = darktable.thumbnail_width; const int32_t capht = darktable.thumbnail_height; dt_develop_t *dev = (dt_develop_t *)self->data; // if we are not hovering over a thumbnail in the filmstrip -> show metadata of opened image. int32_t mouse_over_id = -1; DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); if(mouse_over_id == -1) { mouse_over_id = dev->image_storage.id; DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, mouse_over_id); } dt_control_t *ctl = darktable.control; const int32_t width_i = self->width; const int32_t height_i = self->height; int32_t offx = 0.0f, offy = 0.0f; if(width_i > capwd) offx = (capwd-width_i) *.5f; if(height_i > capht) offy = (capht-height_i)*.5f; int handled = 0; x += offx; y += offy; if(dev->gui_module && dev->gui_module->request_color_pick && ctl->button_down && ctl->button_down_which == 1) { // module requested a color box float zoom_x, zoom_y, bzoom_x, bzoom_y; dt_dev_get_pointer_zoom_pos(dev, x, y, &zoom_x, &zoom_y); dt_dev_get_pointer_zoom_pos(dev, ctl->button_x + offx, ctl->button_y + offy, &bzoom_x, &bzoom_y); if(darktable.lib->proxy.colorpicker.size) { dev->gui_module->color_picker_box[0] = fmaxf(0.0, fminf(.5f+bzoom_x, .5f+zoom_x)); dev->gui_module->color_picker_box[1] = fmaxf(0.0, fminf(.5f+bzoom_y, .5f+zoom_y)); dev->gui_module->color_picker_box[2] = fminf(1.0, fmaxf(.5f+bzoom_x, .5f+zoom_x)); dev->gui_module->color_picker_box[3] = fminf(1.0, fmaxf(.5f+bzoom_y, .5f+zoom_y)); } else { dev->gui_module->color_picker_point[0] = .5f + zoom_x; dev->gui_module->color_picker_point[1] = .5f + zoom_y; } dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH; dt_dev_invalidate_all(dev); dt_control_queue_redraw(); return; } if(dev->gui_module && dev->gui_module->mouse_moved) handled = dev->gui_module->mouse_moved(dev->gui_module, x, y, which); if(handled) return; if(darktable.control->button_down && darktable.control->button_down_which == 1) { // depending on dev_zoom, adjust dev_zoom_x/y. dt_dev_zoom_t zoom; int closeup; DT_CTL_GET_GLOBAL(zoom, dev_zoom); DT_CTL_GET_GLOBAL(closeup, dev_closeup); int procw, proch; dt_dev_get_processed_size(dev, &procw, &proch); const float scale = dt_dev_get_zoom_scale(dev, zoom, closeup ? 2 : 1, 0); float old_zoom_x, old_zoom_y; DT_CTL_GET_GLOBAL(old_zoom_x, dev_zoom_x); DT_CTL_GET_GLOBAL(old_zoom_y, dev_zoom_y); float zx = old_zoom_x - (1.0/scale)*(x - ctl->button_x - offx)/procw; float zy = old_zoom_y - (1.0/scale)*(y - ctl->button_y - offy)/proch; dt_dev_check_zoom_bounds(dev, &zx, &zy, zoom, closeup, NULL, NULL); DT_CTL_SET_GLOBAL(dev_zoom_x, zx); DT_CTL_SET_GLOBAL(dev_zoom_y, zy); ctl->button_x = x - offx; ctl->button_y = y - offy; dt_dev_invalidate(dev); dt_control_queue_redraw(); } }
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; }