void finalize_store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *params) { dt_imageio_email_t *d = (dt_imageio_email_t *)params; // All images are exported, generate a mailto uri and startup default mail client gchar uri[4096]= {0}; gchar body[4096]= {0}; gchar attachments[4096]= {0}; gchar *uriFormat=NULL; gchar *subject=_("images exported from darktable"); gchar *imageBodyFormat="%s %s"; // filename, exif oneliner gchar *attachmentFormat=NULL; gchar *attachmentSeparator=""; // If no default handler detected above, we use gtk_show_uri with mailto:// and hopes things goes right.. uriFormat="xdg-email --subject \"%s\" --body \"%s\" %s &"; // subject, body, and list of attachments with format: attachmentFormat=" --attach \"%s\""; while( d->images ) { gchar exif[256]= {0}; _email_attachment_t *attachment=( _email_attachment_t *)d->images->data; gchar *filename = g_path_get_basename( attachment->file ); const dt_image_t *img = dt_image_cache_read_get(darktable.image_cache, attachment->imgid); dt_image_print_exif(img, exif, sizeof(exif)); g_snprintf(body+strlen(body),4096-strlen(body), imageBodyFormat, filename, exif ); g_free(filename); if( strlen( attachments ) ) g_snprintf(attachments+strlen(attachments),4096-strlen(attachments), "%s", attachmentSeparator ); g_snprintf(attachments+strlen(attachments),4096-strlen(attachments), attachmentFormat, attachment->file ); // Free attachment item and remove dt_image_cache_read_release(darktable.image_cache, img); g_free( d->images->data ); d->images = g_list_remove( d->images, d->images->data ); } // build uri and launch before we quit... g_snprintf(uri, sizeof(uri), uriFormat, subject, body, attachments ); fprintf(stderr, "[email] launching `%s'\n", uri); if(system( uri ) < 0) { // TODO: after string freeze is broken again, report to ui: // dt_control_log(_("could not launch email client!")); fprintf(stderr, "[email] could not launch subprocess!\n"); } }
void finalize_store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *params) { dt_imageio_email_t *d = (dt_imageio_email_t *)params; // All images are exported, generate a mailto uri and startup default mail client gchar uri[4096] = { 0 }; gchar body[4096] = { 0 }; gchar attachments[4096] = { 0 }; gchar *uriFormat = "xdg-email --subject \"%s\" --body \"%s\" %s &"; // subject, body format const gchar *subject = _("images exported from darktable"); const gchar *imageBodyFormat = " - %s (%s)\\n"; // filename, exif oneliner gchar *attachmentFormat = " --attach \"%s\""; // list of attachments format gchar *attachmentSeparator = ""; while(d->images) { gchar exif[256] = { 0 }; _email_attachment_t *attachment = (_email_attachment_t *)d->images->data; gchar *filename = g_path_get_basename(attachment->file); const dt_image_t *img = dt_image_cache_get(darktable.image_cache, attachment->imgid, 'r'); dt_image_print_exif(img, exif, sizeof(exif)); g_snprintf(body + strlen(body), sizeof(body) - strlen(body), imageBodyFormat, filename, exif); g_free(filename); if(*attachments) g_snprintf(attachments + strlen(attachments), sizeof(attachments) - strlen(attachments), "%s", attachmentSeparator); g_snprintf(attachments + strlen(attachments), sizeof(attachments) - strlen(attachments), attachmentFormat, attachment->file); // Free attachment item and remove dt_image_cache_read_release(darktable.image_cache, img); g_free(d->images->data); d->images = g_list_remove(d->images, d->images->data); } // build uri and launch before we quit... g_snprintf(uri, sizeof(uri), uriFormat, subject, body, attachments); fprintf(stderr, "[email] launching `%s'\n", uri); if(system(uri) < 0) { dt_control_log(_("could not launch email client!")); } }
static gchar *_watermark_get_svgdoc(dt_iop_module_t *self, dt_iop_watermark_data_t *data, const dt_image_t *image) { gsize length; gchar *svgdoc = NULL; gchar configdir[PATH_MAX] = { 0 }; gchar datadir[PATH_MAX] = { 0 }; gchar *filename; dt_loc_get_datadir(datadir, sizeof(datadir)); dt_loc_get_user_config_dir(configdir, sizeof(configdir)); g_strlcat(datadir, "/watermarks/", sizeof(datadir)); g_strlcat(configdir, "/watermarks/", sizeof(configdir)); g_strlcat(datadir, data->filename, sizeof(datadir)); g_strlcat(configdir, data->filename, sizeof(configdir)); if(g_file_test(configdir, G_FILE_TEST_EXISTS)) filename = configdir; else if(g_file_test(datadir, G_FILE_TEST_EXISTS)) filename = datadir; else return NULL; gchar *svgdata = NULL; char datetime[200]; // EXIF datetime struct tm tt_exif = { 0 }; if(sscanf(image->exif_datetime_taken, "%d:%d:%d %d:%d:%d", &tt_exif.tm_year, &tt_exif.tm_mon, &tt_exif.tm_mday, &tt_exif.tm_hour, &tt_exif.tm_min, &tt_exif.tm_sec) == 6) { tt_exif.tm_year -= 1900; tt_exif.tm_mon--; } // Current datetime struct tm tt_cur = { 0 }; time_t t = time(NULL); (void)localtime_r(&t, &tt_cur); if(g_file_get_contents(filename, &svgdata, &length, NULL)) { // File is loaded lets substitute strings if found... // Darktable internal svgdoc = _string_substitute(svgdata, "$(DARKTABLE.NAME)", PACKAGE_NAME); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } svgdoc = _string_substitute(svgdata, "$(DARKTABLE.VERSION)", PACKAGE_VERSION); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // Simple text from watermark module gchar buffer[1024]; if (data->font[0] && data->text[0]) { g_snprintf(buffer, sizeof(buffer), "%s", data->text); svgdoc = _string_substitute(svgdata, "$(WATERMARK_TEXT)", buffer); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } PangoFontDescription *font = pango_font_description_from_string(data->font); const PangoStyle font_style = pango_font_description_get_style(font); const int font_weight = (int)pango_font_description_get_weight(font); g_snprintf(buffer, sizeof(buffer), "%s", pango_font_description_get_family(font)); svgdoc = _string_substitute(svgdata, "$(WATERMARK_FONT_FAMILY)", buffer); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } switch (font_style) { case PANGO_STYLE_OBLIQUE: g_strlcpy(buffer, "oblique", sizeof(buffer)); break; case PANGO_STYLE_ITALIC: g_strlcpy(buffer, "italic", sizeof(buffer)); break; default: g_strlcpy(buffer, "normal", sizeof(buffer)); break; } svgdoc = _string_substitute(svgdata, "$(WATERMARK_FONT_STYLE)", buffer); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } g_snprintf(buffer, sizeof(buffer), "%d", font_weight); svgdoc = _string_substitute(svgdata, "$(WATERMARK_FONT_WEIGHT)", buffer); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } pango_font_description_free(font); } // watermark color GdkRGBA c = { data->color[0], data->color[1], data->color[2], 1.0f }; g_snprintf(buffer, sizeof(buffer), "%s", gdk_rgba_to_string(&c)); svgdoc = _string_substitute(svgdata, "$(WATERMARK_COLOR)", buffer); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // Current image ID g_snprintf(buffer, sizeof(buffer), "%d", image->id); svgdoc = _string_substitute(svgdata, "$(IMAGE.ID)", buffer); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // Current image dt_image_print_exif(image, buffer, sizeof(buffer)); svgdoc = _string_substitute(svgdata, "$(IMAGE.EXIF)", buffer); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // Image exif // EXIF date svgdoc = _string_substitute(svgdata, "$(EXIF.DATE)", image->exif_datetime_taken); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.SECOND) -- 00..60 strftime(datetime, sizeof(datetime), "%S", &tt_exif); svgdoc = _string_substitute(svgdata, "$(EXIF.DATE.SECOND)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.MINUTE) -- 00..59 strftime(datetime, sizeof(datetime), "%M", &tt_exif); svgdoc = _string_substitute(svgdata, "$(EXIF.DATE.MINUTE)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.HOUR) -- 00..23 strftime(datetime, sizeof(datetime), "%H", &tt_exif); svgdoc = _string_substitute(svgdata, "$(EXIF.DATE.HOUR)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.HOUR_AMPM) -- 01..12 strftime(datetime, sizeof(datetime), "%I %p", &tt_exif); svgdoc = _string_substitute(svgdata, "$(EXIF.DATE.HOUR_AMPM)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.DAY) -- 01..31 strftime(datetime, sizeof(datetime), "%d", &tt_exif); svgdoc = _string_substitute(svgdata, "$(EXIF.DATE.DAY)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.MONTH) -- 01..12 strftime(datetime, sizeof(datetime), "%m", &tt_exif); svgdoc = _string_substitute(svgdata, "$(EXIF.DATE.MONTH)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.SHORT_MONTH) -- Jan, Feb, .., Dec, localized strftime(datetime, sizeof(datetime), "%b", &tt_exif); svgdoc = _string_substitute(svgdata, "$(EXIF.DATE.SHORT_MONTH)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.LONG_MONTH) -- January, February, .., December, localized strftime(datetime, sizeof(datetime), "%B", &tt_exif); svgdoc = _string_substitute(svgdata, "$(EXIF.DATE.LONG_MONTH)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.SHORT_YEAR) -- 12 strftime(datetime, sizeof(datetime), "%y", &tt_exif); svgdoc = _string_substitute(svgdata, "$(EXIF.DATE.SHORT_YEAR)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.LONG_YEAR) -- 2012 strftime(datetime, sizeof(datetime), "%Y", &tt_exif); svgdoc = _string_substitute(svgdata, "$(EXIF.DATE.LONG_YEAR)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // Current date // $(DATE) -- YYYY: dt_gettime_t(datetime, sizeof(datetime), t); svgdoc = _string_substitute(svgdata, "$(DATE)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.SECOND) -- 00..60 strftime(datetime, sizeof(datetime), "%S", &tt_cur); svgdoc = _string_substitute(svgdata, "$(DATE.SECOND)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.MINUTE) -- 00..59 strftime(datetime, sizeof(datetime), "%M", &tt_cur); svgdoc = _string_substitute(svgdata, "$(DATE.MINUTE)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.HOUR) -- 00..23 strftime(datetime, sizeof(datetime), "%H", &tt_cur); svgdoc = _string_substitute(svgdata, "$(DATE.HOUR)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.HOUR_AMPM) -- 01..12 strftime(datetime, sizeof(datetime), "%I %p", &tt_cur); svgdoc = _string_substitute(svgdata, "$(DATE.HOUR_AMPM)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.DAY) -- 01..31 strftime(datetime, sizeof(datetime), "%d", &tt_cur); svgdoc = _string_substitute(svgdata, "$(DATE.DAY)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.MONTH) -- 01..12 strftime(datetime, sizeof(datetime), "%m", &tt_cur); svgdoc = _string_substitute(svgdata, "$(DATE.MONTH)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.SHORT_MONTH) -- Jan, Feb, .., Dec, localized strftime(datetime, sizeof(datetime), "%b", &tt_cur); svgdoc = _string_substitute(svgdata, "$(DATE.SHORT_MONTH)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.LONG_MONTH) -- January, February, .., December, localized strftime(datetime, sizeof(datetime), "%B", &tt_cur); svgdoc = _string_substitute(svgdata, "$(DATE.LONG_MONTH)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.SHORT_YEAR) -- 12 strftime(datetime, sizeof(datetime), "%y", &tt_cur); svgdoc = _string_substitute(svgdata, "$(DATE.SHORT_YEAR)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.LONG_YEAR) -- 2012 strftime(datetime, sizeof(datetime), "%Y", &tt_cur); svgdoc = _string_substitute(svgdata, "$(DATE.LONG_YEAR)", datetime); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } svgdoc = _string_substitute(svgdata, "$(EXIF.MAKER)", image->camera_maker); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } svgdoc = _string_substitute(svgdata, "$(EXIF.MODEL)", image->camera_model); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } svgdoc = _string_substitute(svgdata, "$(EXIF.LENS)", image->exif_lens); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } svgdoc = _string_substitute(svgdata, "$(IMAGE.FILENAME)", image->filename); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } gchar *basename = g_path_get_basename(image->filename); if(g_strrstr(basename, ".")) *(g_strrstr(basename, ".")) = '\0'; svgdoc = _string_substitute(svgdata, "$(IMAGE.BASENAME)", basename); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } g_free(basename); // TODO: auto generate that code? GList *res; res = dt_metadata_get(image->id, "Xmp.dc.creator", NULL); svgdoc = _string_substitute(svgdata, "$(Xmp.dc.creator)", (res ? res->data : "")); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } if(res) { g_list_free_full(res, &g_free); } res = dt_metadata_get(image->id, "Xmp.dc.publisher", NULL); svgdoc = _string_substitute(svgdata, "$(Xmp.dc.publisher)", (res ? res->data : "")); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } if(res) { g_list_free_full(res, &g_free); } res = dt_metadata_get(image->id, "Xmp.dc.title", NULL); svgdoc = _string_substitute(svgdata, "$(Xmp.dc.title)", (res ? res->data : "")); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } if(res) { g_list_free_full(res, &g_free); } res = dt_metadata_get(image->id, "Xmp.dc.description", NULL); svgdoc = _string_substitute(svgdata, "$(Xmp.dc.description)", (res ? res->data : "")); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } if(res) { g_list_free_full(res, &g_free); } res = dt_metadata_get(image->id, "Xmp.dc.rights", NULL); svgdoc = _string_substitute(svgdata, "$(Xmp.dc.rights)", (res ? res->data : "")); if(svgdoc != svgdata) { g_free(svgdata); svgdata = svgdoc; } if(res) { g_list_free_full(res, &g_free); } } return svgdoc; }
static gboolean _lib_histogram_draw_callback(GtkWidget *widget, cairo_t *crf, gpointer user_data) { dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_histogram_t *d = (dt_lib_histogram_t *)self->data; dt_develop_t *dev = darktable.develop; uint32_t *hist = dev->histogram; float hist_max = dev->histogram_type == DT_DEV_HISTOGRAM_LINEAR ? dev->histogram_max : logf(1.0 + dev->histogram_max); const int inset = DT_HIST_INSET; GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); int width = allocation.width, height = allocation.height; cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *cr = cairo_create(cst); gtk_render_background(gtk_widget_get_style_context(widget), cr, 0, 0, allocation.width, allocation.height); cairo_translate(cr, 4 * inset, inset); width -= 2 * 4 * inset; height -= 2 * inset; if(d->mode_x == 0) { d->color_w = 0.06 * width; d->button_spacing = 0.01 * width; d->button_h = 0.06 * width; d->button_y = d->button_spacing; d->mode_w = d->color_w; d->mode_x = width - 3 * (d->color_w + d->button_spacing) - (d->mode_w + d->button_spacing); d->red_x = width - 3 * (d->color_w + d->button_spacing); d->green_x = width - 2 * (d->color_w + d->button_spacing); d->blue_x = width - (d->color_w + d->button_spacing); } // TODO: probably this should move to the configure-event callback! That would be future proof if we ever // (again) allow to resize the side panels. const gint stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); // this code assumes that the first expose comes before the first (preview) pipe is processed and that the // size of the widget doesn't change! if(dev->histogram_waveform_width == 0) { dev->histogram_waveform = (uint32_t *)calloc(height * stride / 4, sizeof(uint32_t)); dev->histogram_waveform_stride = stride; dev->histogram_waveform_height = height; dev->histogram_waveform_width = width; // return TRUE; // there are enough expose events following ... } #if 1 // draw shadow around float alpha = 1.0f; cairo_set_line_width(cr, 0.2); for(int k = 0; k < inset; k++) { cairo_rectangle(cr, -k, -k, width + 2 * k, height + 2 * k); cairo_set_source_rgba(cr, 0, 0, 0, alpha); alpha *= 0.5f; cairo_fill(cr); } cairo_set_line_width(cr, 1.0); #else cairo_set_line_width(cr, 1.0); cairo_set_source_rgb(cr, .1, .1, .1); cairo_rectangle(cr, 0, 0, width, height); cairo_stroke(cr); #endif cairo_rectangle(cr, 0, 0, width, height); cairo_clip(cr); cairo_set_source_rgb(cr, .3, .3, .3); cairo_rectangle(cr, 0, 0, width, height); cairo_fill(cr); if(d->highlight == 1) { cairo_set_source_rgb(cr, .5, .5, .5); cairo_rectangle(cr, 0, 0, .2 * width, height); cairo_fill(cr); } else if(d->highlight == 2) { cairo_set_source_rgb(cr, .5, .5, .5); cairo_rectangle(cr, 0.2 * width, 0, width, height); cairo_fill(cr); } // draw grid cairo_set_line_width(cr, .4); cairo_set_source_rgb(cr, .1, .1, .1); if(dev->histogram_type == DT_DEV_HISTOGRAM_WAVEFORM) dt_draw_waveform_lines(cr, 0, 0, width, height); else dt_draw_grid(cr, 4, 0, 0, width, height); if(hist_max > 0.0f) { cairo_save(cr); if(dev->histogram_type == DT_DEV_HISTOGRAM_WAVEFORM) { // make the color channel selector work: uint8_t *buf = (uint8_t *)malloc(sizeof(uint8_t) * height * stride); uint8_t mask[3] = { d->blue, d->green, d->red }; memcpy(buf, dev->histogram_waveform, sizeof(uint8_t) * height * stride); for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) for(int k = 0; k < 3; k++) { buf[y * stride + x * 4 + k] *= mask[k]; } cairo_surface_t *source = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32, width, height, stride); cairo_set_source_surface(cr, source, 0.0, 0.0); cairo_set_operator(cr, CAIRO_OPERATOR_ADD); cairo_paint(cr); cairo_surface_destroy(source); free(buf); } else { // cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); cairo_translate(cr, 0, height); cairo_scale(cr, width / 63.0, -(height - 10) / hist_max); cairo_set_operator(cr, CAIRO_OPERATOR_ADD); // cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_line_width(cr, 1.); if(d->red) { cairo_set_source_rgba(cr, 1., 0., 0., 0.2); dt_draw_histogram_8(cr, hist, 0, dev->histogram_type == DT_DEV_HISTOGRAM_LINEAR); } if(d->green) { cairo_set_source_rgba(cr, 0., 1., 0., 0.2); dt_draw_histogram_8(cr, hist, 1, dev->histogram_type == DT_DEV_HISTOGRAM_LINEAR); } if(d->blue) { cairo_set_source_rgba(cr, 0., 0., 1., 0.2); dt_draw_histogram_8(cr, hist, 2, dev->histogram_type == DT_DEV_HISTOGRAM_LINEAR); } cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); // cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT); } cairo_restore(cr); } cairo_set_source_rgb(cr, .25, .25, .25); cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); 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, .1 * height * PANGO_SCALE); pango_layout_set_font_description(layout, desc); char exifline[50]; dt_image_print_exif(&dev->image_storage, exifline, 50); pango_layout_set_text(layout, exifline, -1); pango_layout_get_pixel_extents(layout, &ink, NULL); cairo_move_to(cr, .02 * width, .98 * height - ink.height - ink.y); cairo_save(cr); cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.0)); cairo_set_source_rgba(cr, 1, 1, 1, 0.3); pango_cairo_layout_path(cr, layout); cairo_stroke_preserve(cr); cairo_set_source_rgb(cr, .25, .25, .25); cairo_fill(cr); cairo_restore(cr); // buttons to control the display of the histogram: linear/log, r, g, b if(d->highlight != 0) { _draw_mode_toggle(cr, d->mode_x, d->button_y, d->mode_w, d->button_h, dev->histogram_type); cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.4); _draw_color_toggle(cr, d->red_x, d->button_y, d->color_w, d->button_h, d->red); cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 0.4); _draw_color_toggle(cr, d->green_x, d->button_y, d->color_w, d->button_h, d->green); cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 0.4); _draw_color_toggle(cr, d->blue_x, d->button_y, d->color_w, d->button_h, d->blue); } cairo_destroy(cr); cairo_set_source_surface(crf, cst, 0, 0); cairo_paint(crf); cairo_surface_destroy(cst); pango_font_description_free(desc); g_object_unref(layout); return TRUE; }
static gchar * _watermark_get_svgdoc( dt_iop_module_t *self, dt_iop_watermark_data_t *data, const dt_image_t *image) { gsize length; gchar *svgdoc=NULL; gchar configdir[DT_MAX_PATH_LEN]; gchar datadir[DT_MAX_PATH_LEN]; gchar *filename; dt_loc_get_datadir(datadir, DT_MAX_PATH_LEN); dt_loc_get_user_config_dir(configdir, DT_MAX_PATH_LEN); g_strlcat(datadir,"/watermarks/", DT_MAX_PATH_LEN); g_strlcat(configdir,"/watermarks/", DT_MAX_PATH_LEN); g_strlcat(datadir,data->filename, DT_MAX_PATH_LEN); g_strlcat(configdir,data->filename, DT_MAX_PATH_LEN); if (g_file_test(configdir,G_FILE_TEST_EXISTS)) filename=configdir; else if (g_file_test(datadir,G_FILE_TEST_EXISTS)) filename=datadir; else return NULL; gchar *svgdata=NULL; char datetime[200]; // EXIF datetime struct tm tt_exif = {0}; if(sscanf(image->exif_datetime_taken,"%d:%d:%d %d:%d:%d", &tt_exif.tm_year, &tt_exif.tm_mon, &tt_exif.tm_mday, &tt_exif.tm_hour, &tt_exif.tm_min, &tt_exif.tm_sec ) == 6 ) { tt_exif.tm_year-=1900; tt_exif.tm_mon--; } // Current datetime struct tm tt_cur = {0}; time_t t = time(NULL); (void)localtime_r(&t, &tt_cur); if( g_file_get_contents( filename, &svgdata, &length, NULL) ) { // File is loaded lets substitute strings if found... // Darktable internal svgdoc = _string_substitute(svgdata,"$(DARKTABLE.NAME)",PACKAGE_NAME); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } svgdoc = _string_substitute(svgdata,"$(DARKTABLE.VERSION)",PACKAGE_VERSION); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // Current image ID gchar buffer[1024]; g_snprintf(buffer,1024,"%d",image->id); svgdoc = _string_substitute(svgdata,"$(IMAGE.ID)",buffer); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // Current image dt_image_print_exif(image,buffer,1024); svgdoc = _string_substitute(svgdata,"$(IMAGE.EXIF)",buffer); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // Image exif // EXIF date svgdoc = _string_substitute(svgdata,"$(EXIF.DATE)",image->exif_datetime_taken); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.SECOND) -- 00..60 strftime(datetime, sizeof(datetime), "%S", &tt_exif); svgdoc = _string_substitute(svgdata,"$(EXIF.DATE.SECOND)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.MINUTE) -- 00..59 strftime(datetime, sizeof(datetime), "%M", &tt_exif); svgdoc = _string_substitute(svgdata,"$(EXIF.DATE.MINUTE)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.HOUR) -- 00..23 strftime(datetime, sizeof(datetime), "%H", &tt_exif); svgdoc = _string_substitute(svgdata,"$(EXIF.DATE.HOUR)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.HOUR_AMPM) -- 01..12 strftime(datetime, sizeof(datetime), "%I %p", &tt_exif); svgdoc = _string_substitute(svgdata,"$(EXIF.DATE.HOUR_AMPM)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.DAY) -- 01..31 strftime(datetime, sizeof(datetime), "%d", &tt_exif); svgdoc = _string_substitute(svgdata,"$(EXIF.DATE.DAY)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.MONTH) -- 01..12 strftime(datetime, sizeof(datetime), "%m", &tt_exif); svgdoc = _string_substitute(svgdata,"$(EXIF.DATE.MONTH)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.SHORT_MONTH) -- Jan, Feb, .., Dec, localized strftime(datetime, sizeof(datetime), "%b", &tt_exif); svgdoc = _string_substitute(svgdata,"$(EXIF.DATE.SHORT_MONTH)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.LONG_MONTH) -- January, February, .., December, localized strftime(datetime, sizeof(datetime), "%B", &tt_exif); svgdoc = _string_substitute(svgdata,"$(EXIF.DATE.LONG_MONTH)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.SHORT_YEAR) -- 12 strftime(datetime, sizeof(datetime), "%y", &tt_exif); svgdoc = _string_substitute(svgdata,"$(EXIF.DATE.SHORT_YEAR)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(EXIF.DATE.LONG_YEAR) -- 2012 strftime(datetime, sizeof(datetime), "%Y", &tt_exif); svgdoc = _string_substitute(svgdata,"$(EXIF.DATE.LONG_YEAR)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // Current date // $(DATE) -- YYYY: dt_gettime_t(datetime, t); svgdoc = _string_substitute(svgdata,"$(DATE)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.SECOND) -- 00..60 strftime(datetime, sizeof(datetime), "%S", &tt_cur); svgdoc = _string_substitute(svgdata,"$(DATE.SECOND)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.MINUTE) -- 00..59 strftime(datetime, sizeof(datetime), "%M", &tt_cur); svgdoc = _string_substitute(svgdata,"$(DATE.MINUTE)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.HOUR) -- 00..23 strftime(datetime, sizeof(datetime), "%H", &tt_cur); svgdoc = _string_substitute(svgdata,"$(DATE.HOUR)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.HOUR_AMPM) -- 01..12 strftime(datetime, sizeof(datetime), "%I %p", &tt_cur); svgdoc = _string_substitute(svgdata,"$(DATE.HOUR_AMPM)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.DAY) -- 01..31 strftime(datetime, sizeof(datetime), "%d", &tt_cur); svgdoc = _string_substitute(svgdata,"$(DATE.DAY)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.MONTH) -- 01..12 strftime(datetime, sizeof(datetime), "%m", &tt_cur); svgdoc = _string_substitute(svgdata,"$(DATE.MONTH)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.SHORT_MONTH) -- Jan, Feb, .., Dec, localized strftime(datetime, sizeof(datetime), "%b", &tt_cur); svgdoc = _string_substitute(svgdata,"$(DATE.SHORT_MONTH)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.LONG_MONTH) -- January, February, .., December, localized strftime(datetime, sizeof(datetime), "%B", &tt_cur); svgdoc = _string_substitute(svgdata,"$(DATE.LONG_MONTH)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.SHORT_YEAR) -- 12 strftime(datetime, sizeof(datetime), "%y", &tt_cur); svgdoc = _string_substitute(svgdata,"$(DATE.SHORT_YEAR)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // $(DATE.LONG_YEAR) -- 2012 strftime(datetime, sizeof(datetime), "%Y", &tt_cur); svgdoc = _string_substitute(svgdata,"$(DATE.LONG_YEAR)",datetime); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } svgdoc = _string_substitute(svgdata,"$(EXIF.MAKER)",image->exif_maker); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } svgdoc = _string_substitute(svgdata,"$(EXIF.MODEL)",image->exif_model); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } svgdoc = _string_substitute(svgdata,"$(EXIF.LENS)",image->exif_lens); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } svgdoc = _string_substitute(svgdata,"$(IMAGE.FILENAME)",image->filename); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } // TODO: auto generate that code? GList * res; res = dt_metadata_get(image->id, "Xmp.dc.creator", NULL); svgdoc = _string_substitute(svgdata,"$(Xmp.dc.creator)",(res?res->data:"")); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } if( res ) { g_free(res->data); g_list_free(res); } res = dt_metadata_get(image->id, "Xmp.dc.publisher", NULL); svgdoc = _string_substitute(svgdata,"$(Xmp.dc.publisher)",(res?res->data:"")); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } if( res ) { g_free(res->data); g_list_free(res); } res = dt_metadata_get(image->id, "Xmp.dc.title", NULL); svgdoc = _string_substitute(svgdata,"$(Xmp.dc.title)",(res?res->data:"")); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } if( res ) { g_free(res->data); g_list_free(res); } res = dt_metadata_get(image->id, "Xmp.dc.description", NULL); svgdoc = _string_substitute(svgdata,"$(Xmp.dc.description)",(res?res->data:"")); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } if( res ) { g_free(res->data); g_list_free(res); } res = dt_metadata_get(image->id, "Xmp.dc.rights", NULL); svgdoc = _string_substitute(svgdata,"$(Xmp.dc.rights)",(res?res->data:"")); if( svgdoc != svgdata ) { g_free(svgdata); svgdata = svgdoc; } if( res ) { g_free(res->data); g_list_free(res); } } return svgdoc; }
void finalize_store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *params) { dt_imageio_email_t *d = (dt_imageio_email_t *)params; const gchar *imageBodyFormat = " - %s (%s)\\n"; // filename, exif oneliner const gint nb_images = g_list_length(d->images); const gint argc = 5 + (2 * nb_images); char **argv = g_malloc0(sizeof(char *) * (argc + 1)); gchar *body = NULL; argv[0] = "xdg-email"; argv[1] = "--subject"; argv[2] = _("images exported from darktable"); argv[3] = "--body"; int n = 5; for(GList *iter = d->images; iter; iter = g_list_next(iter)) { gchar exif[256] = { 0 }; _email_attachment_t *attachment = (_email_attachment_t *)iter->data; gchar *filename = g_path_get_basename(attachment->file); const dt_image_t *img = dt_image_cache_get(darktable.image_cache, attachment->imgid, 'r'); dt_image_print_exif(img, exif, sizeof(exif)); dt_image_cache_read_release(darktable.image_cache, img); gchar *imgbody = g_strdup_printf(imageBodyFormat, filename, exif); if (body != NULL) { gchar *body_bak = body; body = g_strconcat(body_bak, imgbody, NULL); g_free(body_bak); } else { body = g_strdup(imgbody); } g_free(imgbody); g_free(filename); argv[n] = g_strdup("--attach"); // use attachment->file directly as we need to freed it, and this way it will be // freed as part of the argument release after the spawn below. argv[n+1] = attachment->file; n += 2; } g_list_free_full(d->images, g_free); d->images = NULL; argv[4] = body; argv[argc] = NULL; fprintf(stderr, "[email] launching '"); for (int k=0; k<argc; k++) fprintf(stderr, " %s", argv[k]); fprintf(stderr, "'\n"); gint exit_status = 0; g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, NULL, NULL, &exit_status, NULL); for (int k=4; k<argc; k++) g_free(argv[k]); g_free(argv); if(exit_status) { dt_control_log(_("could not launch email client!")); } }
void dt_view_image_expose( dt_view_image_over_t *image_over, uint32_t imgid, cairo_t *cr, int32_t width, int32_t height, int32_t zoom, int32_t px, int32_t py) { cairo_save (cr); float bgcol = 0.4, fontcol = 0.425, bordercol = 0.1, outlinecol = 0.2; int selected = 0, altered = 0, imgsel; DT_CTL_GET_GLOBAL(imgsel, lib_image_mouse_over_id); // if(img->flags & DT_IMAGE_SELECTED) selected = 1; /* clear and reset statements */ DT_DEBUG_SQLITE3_CLEAR_BINDINGS(darktable.view_manager->statements.is_selected); DT_DEBUG_SQLITE3_CLEAR_BINDINGS(darktable.view_manager->statements.have_history); DT_DEBUG_SQLITE3_RESET(darktable.view_manager->statements.is_selected); DT_DEBUG_SQLITE3_RESET(darktable.view_manager->statements.have_history); /* bind imgid to prepared statments */ DT_DEBUG_SQLITE3_BIND_INT(darktable.view_manager->statements.is_selected, 1, imgid); DT_DEBUG_SQLITE3_BIND_INT(darktable.view_manager->statements.have_history, 1, imgid); /* lets check if imgid is selected */ if(sqlite3_step(darktable.view_manager->statements.is_selected) == SQLITE_ROW) selected = 1; /* lets check if imgid has history */ if(sqlite3_step(darktable.view_manager->statements.have_history) == SQLITE_ROW) altered = 1; const dt_image_t *img = dt_image_cache_read_testget(darktable.image_cache, imgid); if(selected == 1) { outlinecol = 0.4; bgcol = 0.6; fontcol = 0.5; } if(imgsel == imgid) { bgcol = 0.8; // mouse over fontcol = 0.7; outlinecol = 0.6; // if the user points at this image, we really want it: if(!img) img = dt_image_cache_read_get(darktable.image_cache, imgid); } float imgwd = 0.90f; if(zoom == 1) { imgwd = .97f; // cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); } else { double x0 = 1, y0 = 1, rect_width = width-2, rect_height = height-2, radius = 5; double x1, y1, off, off1; x1=x0+rect_width; y1=y0+rect_height; off=radius*0.666; off1 = radius-off; cairo_move_to (cr, x0, y0 + radius); cairo_curve_to (cr, x0, y0+off1, x0+off1 , y0, x0 + radius, y0); cairo_line_to (cr, x1 - radius, y0); cairo_curve_to (cr, x1-off1, y0, x1, y0+off1, x1, y0 + radius); cairo_line_to (cr, x1 , y1 - radius); cairo_curve_to (cr, x1, y1-off1, x1-off1, y1, x1 - radius, y1); cairo_line_to (cr, x0 + radius, y1); cairo_curve_to (cr, x0+off1, y1, x0, y1-off1, x0, y1- radius); cairo_close_path (cr); cairo_set_source_rgb(cr, bgcol, bgcol, bgcol); cairo_fill_preserve(cr); cairo_set_line_width(cr, 0.005*width); cairo_set_source_rgb(cr, outlinecol, outlinecol, outlinecol); cairo_stroke(cr); if(img) { const char *ext = img->filename + strlen(img->filename); while(ext > img->filename && *ext != '.') ext--; ext++; cairo_set_source_rgb(cr, fontcol, fontcol, fontcol); cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size (cr, .25*width); cairo_move_to (cr, .01*width, .24*height); cairo_show_text (cr, ext); } } float scale = 1.0; dt_mipmap_buffer_t buf; dt_mipmap_size_t mip = dt_mipmap_cache_get_matching_size( darktable.mipmap_cache, imgwd*width, imgwd*height); dt_mipmap_cache_read_get( darktable.mipmap_cache, &buf, imgid, mip, 0); cairo_surface_t *surface = NULL; if(buf.buf) { const int32_t stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, buf.width); surface = cairo_image_surface_create_for_data (buf.buf, CAIRO_FORMAT_RGB24, buf.width, buf.height, stride); if(zoom == 1) { scale = fminf( fminf(darktable.thumbnail_width, width) / (float)buf.width, fminf(darktable.thumbnail_height, height) / (float)buf.height ); } else scale = fminf(width*imgwd/(float)buf.width, height*imgwd/(float)buf.height); } // draw centered and fitted: cairo_save(cr); cairo_translate(cr, width/2.0, height/2.0f); cairo_scale(cr, scale, scale); if(buf.buf) { cairo_translate(cr, -.5f*buf.width, -.5f*buf.height); cairo_set_source_surface (cr, surface, 0, 0); if(buf.width <= 8 && buf.height <= 8) cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); cairo_rectangle(cr, 0, 0, buf.width, buf.height); cairo_fill(cr); cairo_surface_destroy (surface); if(zoom == 1) cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BEST); cairo_rectangle(cr, 0, 0, buf.width, buf.height); } // border around image const float border = zoom == 1 ? 16/scale : 2/scale; cairo_set_source_rgb(cr, bordercol, bordercol, bordercol); if(buf.buf && selected) { cairo_set_line_width(cr, 1./scale); if(zoom == 1) { // draw shadow around border cairo_set_source_rgb(cr, 0.2, 0.2, 0.2); cairo_stroke(cr); // cairo_new_path(cr); cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); float alpha = 1.0f; for(int k=0; k<16; k++) { cairo_rectangle(cr, 0, 0, buf.width, buf.height); cairo_new_sub_path(cr); cairo_rectangle(cr, -k/scale, -k/scale, buf.width+2.*k/scale, buf.height+2.*k/scale); cairo_set_source_rgba(cr, 0, 0, 0, alpha); alpha *= 0.6f; cairo_fill(cr); } } else { cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); cairo_new_sub_path(cr); cairo_rectangle(cr, -border, -border, buf.width+2.*border, buf.height+2.*border); cairo_stroke_preserve(cr); cairo_set_source_rgb(cr, 1.0-bordercol, 1.0-bordercol, 1.0-bordercol); cairo_fill(cr); } } else if(buf.buf) { cairo_set_line_width(cr, 1); cairo_stroke(cr); } cairo_restore(cr); if(buf.buf) dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); const float fscale = fminf(width, height); if(imgsel == imgid) { // draw mouseover hover effects, set event hook for mouse button down! *image_over = DT_VIEW_DESERT; cairo_set_line_width(cr, 1.5); cairo_set_source_rgb(cr, outlinecol, outlinecol, outlinecol); cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); float r1, r2; if(zoom != 1) { r1 = 0.05*width; r2 = 0.022*width; } else { r1 = 0.015*fscale; r2 = 0.007*fscale; } float x, y; if(zoom != 1) y = 0.90*height; else y = .12*fscale; if(img) for(int k=0; k<5; k++) { if(zoom != 1) x = (0.41+k*0.12)*width; else x = (.08+k*0.04)*fscale; if((img->flags & 0x7) != 6) //if rejected: draw no stars { dt_view_star(cr, x, y, r1, r2); if((px - x)*(px - x) + (py - y)*(py - y) < r1*r1) { *image_over = DT_VIEW_STAR_1 + k; cairo_fill(cr); } else if((img->flags & 0x7) > k) { cairo_fill_preserve(cr); cairo_set_source_rgb(cr, 1.0-bordercol, 1.0-bordercol, 1.0-bordercol); cairo_stroke(cr); cairo_set_source_rgb(cr, outlinecol, outlinecol, outlinecol); } else cairo_stroke(cr); } } //Image rejected? if(zoom !=1) x = 0.11*width; else x = .04*fscale; if((px - x)*(px - x) + (py - y)*(py - y) < r1*r1) { *image_over = DT_VIEW_REJECT; //mouse sensitive cairo_new_sub_path(cr); cairo_arc(cr, x, y, (r1+r2)*.5, 0, 2.0f*M_PI); cairo_stroke(cr); } else if (img && ((img->flags & 0x7) == 6)) { cairo_set_source_rgb(cr, 1., 0., 0.); cairo_new_sub_path(cr); cairo_arc(cr, x, y, (r1+r2)*.5, 0, 2.0f*M_PI); cairo_stroke(cr); cairo_set_line_width(cr, 2.5); } //reject cross: cairo_move_to(cr, x-r2, y-r2); cairo_line_to(cr, x+r2, y+r2); cairo_move_to(cr, x+r2, y-r2); cairo_line_to(cr, x-r2, y+r2); cairo_close_path(cr); cairo_stroke(cr); cairo_set_source_rgb(cr, outlinecol, outlinecol, outlinecol); cairo_set_line_width(cr, 1.5); // image altered? if(altered) { // align to right float s = (r1+r2)*.5; if(zoom != 1) { x = width*0.9; y = height*0.1; } else x = (.04+7*0.04)*fscale; dt_view_draw_altered(cr, x, y, s); //g_print("px = %d, x = %.4f, py = %d, y = %.4f\n", px, x, py, y); if(img && abs(px-x) <= 1.2*s && abs(py-y) <= 1.2*s) // mouse hovers over the altered-icon -> history tooltip! { darktable.gui->center_tooltip = 1; } } } // kill all paths, in case img was not loaded yet, or is blocked: cairo_new_path(cr); // TODO: make mouse sensitive, just as stars! // TODO: cache in image struct! { // color labels: const float x = zoom == 1 ? (0.07)*fscale : .21*width; const float y = zoom == 1 ? 0.17*fscale: 0.1*height; const float r = zoom == 1 ? 0.01*fscale : 0.03*width; /* clear and reset prepared statement */ DT_DEBUG_SQLITE3_CLEAR_BINDINGS(darktable.view_manager->statements.get_color); DT_DEBUG_SQLITE3_RESET(darktable.view_manager->statements.get_color); /* setup statement and iterate rows */ DT_DEBUG_SQLITE3_BIND_INT(darktable.view_manager->statements.get_color, 1, imgid); while(sqlite3_step(darktable.view_manager->statements.get_color) == SQLITE_ROW) { cairo_save(cr); int col = sqlite3_column_int(darktable.view_manager->statements.get_color, 0); // see src/dtgtk/paint.c dtgtk_cairo_paint_label(cr, x+(3*r*col)-5*r, y-r, r*2, r*2, col); cairo_restore(cr); } } if(img && (zoom == 1)) { // some exif data cairo_set_source_rgb(cr, .7, .7, .7); cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size (cr, .025*fscale); cairo_move_to (cr, .02*fscale, .04*fscale); // cairo_show_text(cr, img->filename); cairo_text_path(cr, img->filename); char exifline[50]; cairo_move_to (cr, .02*fscale, .08*fscale); dt_image_print_exif(img, exifline, 50); cairo_text_path(cr, exifline); cairo_fill_preserve(cr); cairo_set_line_width(cr, 1.0); cairo_set_source_rgb(cr, 0.3, 0.3, 0.3); cairo_stroke(cr); } if(img) dt_image_cache_read_release(darktable.image_cache, img); cairo_restore(cr); // if(zoom == 1) cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT); }
static gboolean _lib_histogram_expose_callback(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_histogram_t *d = (dt_lib_histogram_t *)self->data; dt_develop_t *dev = darktable.develop; float *hist = dev->histogram; float hist_max = dev->histogram_type == DT_DEV_HISTOGRAM_LINEAR?dev->histogram_max:logf(1.0 + dev->histogram_max); const int inset = DT_HIST_INSET; int width = widget->allocation.width, height = widget->allocation.height; cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *cr = cairo_create(cst); 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_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); cairo_translate(cr, 4*inset, inset); width -= 2*4*inset; height -= 2*inset; if(d->mode_x == 0) { d->color_w = 0.06*width; d->button_spacing = 0.01*width; d->button_h = 0.06*width; d->button_y = d->button_spacing; d->mode_w = d->color_w; d->mode_x = width - 3*(d->color_w+d->button_spacing) - (d->mode_w+d->button_spacing); d->red_x = width - 3*(d->color_w+d->button_spacing); d->green_x = width - 2*(d->color_w+d->button_spacing); d->blue_x = width - (d->color_w+d->button_spacing); } // TODO: probably this should move to the configure-event callback! That would be future proof if we ever (again) allow to resize the side panels. const gint stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); // this code assumes that the first expose comes before the first (preview) pipe is processed and that the size of the widget doesn't change! if(dev->histogram_waveform_width == 0) { dev->histogram_waveform = (uint32_t*)calloc(height * stride / 4, sizeof(uint32_t)); dev->histogram_waveform_stride = stride; dev->histogram_waveform_height = height; dev->histogram_waveform_width = width; // return TRUE; // there are enough expose events following ... } #if 1 // draw shadow around float alpha = 1.0f; cairo_set_line_width(cr, 0.2); for(int k=0; k<inset; k++) { cairo_rectangle(cr, -k, -k, width + 2*k, height + 2*k); cairo_set_source_rgba(cr, 0, 0, 0, alpha); alpha *= 0.5f; cairo_fill(cr); } cairo_set_line_width(cr, 1.0); #else cairo_set_line_width(cr, 1.0); cairo_set_source_rgb (cr, .1, .1, .1); cairo_rectangle(cr, 0, 0, width, height); cairo_stroke(cr); #endif cairo_rectangle(cr, 0, 0, width, height); cairo_clip(cr); cairo_set_source_rgb (cr, .3, .3, .3); cairo_rectangle(cr, 0, 0, width, height); cairo_fill(cr); if(d->highlight == 1) { cairo_set_source_rgb (cr, .5, .5, .5); cairo_rectangle(cr, 0, 0, .2*width, height); cairo_fill(cr); } else if(d->highlight == 2) { cairo_set_source_rgb (cr, .5, .5, .5); cairo_rectangle(cr, 0.2*width, 0, width, height); cairo_fill(cr); } // draw grid cairo_set_line_width(cr, .4); cairo_set_source_rgb (cr, .1, .1, .1); if(dev->histogram_type == DT_DEV_HISTOGRAM_WAVEFORM) dt_draw_waveform_lines(cr, 0, 0, width, height); else dt_draw_grid(cr, 4, 0, 0, width, height); if(hist_max > 0) { cairo_save(cr); if(dev->histogram_type == DT_DEV_HISTOGRAM_WAVEFORM) { // make the color channel selector work: uint8_t *buf = (uint8_t*)malloc(sizeof(uint8_t) * height * stride); uint8_t mask[3] = {d->blue, d->green, d->red}; memcpy(buf, dev->histogram_waveform, sizeof(uint8_t) * height * stride); for(int y = 0; y < height; y++) for(int x = 0; x < width; x++) for(int k = 0; k < 3; k++) { buf[y * stride + x * 4 + k] *= mask[k]; } cairo_surface_t *source = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32, width, height, stride); cairo_set_source_surface(cr, source, 0.0, 0.0); cairo_set_operator(cr, CAIRO_OPERATOR_ADD); cairo_paint(cr); cairo_surface_destroy(source); free(buf); } else { // cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); cairo_translate(cr, 0, height); cairo_scale(cr, width/63.0, -(height-10)/hist_max); cairo_set_operator(cr, CAIRO_OPERATOR_ADD); // cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_line_width(cr, 1.); if(d->red) { cairo_set_source_rgba(cr, 1., 0., 0., 0.2); dt_draw_histogram_8(cr, hist, 0, dev->histogram_type); } if(d->green) { cairo_set_source_rgba(cr, 0., 1., 0., 0.2); dt_draw_histogram_8(cr, hist, 1, dev->histogram_type); } if(d->blue) { cairo_set_source_rgba(cr, 0., 0., 1., 0.2); dt_draw_histogram_8(cr, hist, 2, dev->histogram_type); } cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); // cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT); } cairo_restore(cr); } cairo_set_source_rgb(cr, .25, .25, .25); cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size (cr, .1*height); char exifline[50]; cairo_move_to (cr, .02*width, .98*height); dt_image_print_exif(&dev->image_storage, exifline, 50); cairo_save(cr); // cairo_show_text(cr, exifline); cairo_set_line_width(cr, 2.0); cairo_set_source_rgba(cr, 1, 1, 1, 0.3); cairo_text_path(cr, exifline); cairo_stroke_preserve(cr); cairo_set_source_rgb(cr, .25, .25, .25); cairo_fill(cr); cairo_restore(cr); // buttons to control the display of the histogram: linear/log, r, g, b if(d->highlight != 0) { _draw_mode_toggle(cr, d->mode_x, d->button_y, d->mode_w, d->button_h, dev->histogram_type); cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.4); _draw_color_toggle(cr, d->red_x, d->button_y, d->color_w, d->button_h, d->red); cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 0.4); _draw_color_toggle(cr, d->green_x, d->button_y, d->color_w, d->button_h, d->green); cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 0.4); _draw_color_toggle(cr, d->blue_x, d->button_y, d->color_w, d->button_h, d->blue); } 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_histogram_expose_callback(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_histogram_t *d = (dt_lib_histogram_t *)self->data; dt_develop_t *dev = darktable.develop; float *hist = dev->histogram; float hist_max = dev->histogram_max; const int inset = DT_HIST_INSET; int width = widget->allocation.width, height = widget->allocation.height; cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *cr = cairo_create(cst); 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_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); cairo_translate(cr, 4*inset, inset); width -= 2*4*inset; height -= 2*inset; #if 1 // draw shadow around float alpha = 1.0f; cairo_set_line_width(cr, 0.2); for(int k=0; k<inset; k++) { cairo_rectangle(cr, -k, -k, width + 2*k, height + 2*k); cairo_set_source_rgba(cr, 0, 0, 0, alpha); alpha *= 0.5f; cairo_fill(cr); } cairo_set_line_width(cr, 1.0); #else cairo_set_line_width(cr, 1.0); cairo_set_source_rgb (cr, .1, .1, .1); cairo_rectangle(cr, 0, 0, width, height); cairo_stroke(cr); #endif cairo_rectangle(cr, 0, 0, width, height); cairo_clip(cr); cairo_set_source_rgb (cr, .3, .3, .3); cairo_rectangle(cr, 0, 0, width, height); cairo_fill(cr); if(d->highlight == 1) { cairo_set_source_rgb (cr, .5, .5, .5); cairo_rectangle(cr, 0, 0, .2*width, height); cairo_fill(cr); } else if(d->highlight == 2) { cairo_set_source_rgb (cr, .5, .5, .5); cairo_rectangle(cr, 0.2*width, 0, width, height); cairo_fill(cr); } // draw grid cairo_set_line_width(cr, .4); cairo_set_source_rgb (cr, .1, .1, .1); dt_draw_grid(cr, 4, 0, 0, width, height); if(hist_max > 0) { cairo_save(cr); // cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); cairo_translate(cr, 0, height); cairo_scale(cr, width/63.0, -(height-10)/hist_max); cairo_set_operator(cr, CAIRO_OPERATOR_ADD); // cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_line_width(cr, 1.); cairo_set_source_rgba(cr, 1., 0., 0., 0.2); dt_draw_histogram_8(cr, hist, 0); cairo_set_source_rgba(cr, 0., 1., 0., 0.2); dt_draw_histogram_8(cr, hist, 1); cairo_set_source_rgba(cr, 0., 0., 1., 0.2); dt_draw_histogram_8(cr, hist, 2); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); // cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT); cairo_restore(cr); } cairo_set_source_rgb(cr, .25, .25, .25); cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size (cr, .1*height); char exifline[50]; cairo_move_to (cr, .02*width, .98*height); dt_image_print_exif(&dev->image_storage, exifline, 50); cairo_show_text(cr, exifline); /*cairo_text_path(cr, exifline); cairo_fill_preserve(cr); cairo_set_line_width(cr, 1.0); cairo_set_source_rgb(cr, 0.3, 0.3, 0.3); cairo_stroke(cr);*/ 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 dt_view_image_expose( dt_view_image_over_t *image_over, uint32_t imgid, cairo_t *cr, int32_t width, int32_t height, int32_t zoom, int32_t px, int32_t py) { const double start = dt_get_wtime(); // some performance tuning stuff, for your pleasure. // on my machine with 7 image per row it seems grouping has the largest // impact from around 400ms -> 55ms per redraw. #define DRAW_THUMB 1 #define DRAW_COLORLABELS 1 #define DRAW_GROUPING 1 #define DRAW_SELECTED 1 #define DRAW_HISTORY 1 #if DRAW_THUMB == 1 // this function is not thread-safe (gui-thread only), so we // can safely allocate this leaking bit of memory to decompress thumbnails: static int first_time = 1; static uint8_t *scratchmem = NULL; if(first_time) { // scratchmem might still be NULL after this, if compression is off. scratchmem = dt_mipmap_cache_alloc_scratchmem(darktable.mipmap_cache); first_time = 0; } #endif cairo_save (cr); float bgcol = 0.4, fontcol = 0.425, bordercol = 0.1, outlinecol = 0.2; int selected = 0, altered = 0, imgsel = -1, is_grouped = 0; // this is a gui thread only thing. no mutex required: imgsel = darktable.control->global_settings.lib_image_mouse_over_id; #if DRAW_SELECTED == 1 /* clear and reset statements */ DT_DEBUG_SQLITE3_CLEAR_BINDINGS(darktable.view_manager->statements.is_selected); DT_DEBUG_SQLITE3_RESET(darktable.view_manager->statements.is_selected); /* bind imgid to prepared statments */ DT_DEBUG_SQLITE3_BIND_INT(darktable.view_manager->statements.is_selected, 1, imgid); /* lets check if imgid is selected */ if(sqlite3_step(darktable.view_manager->statements.is_selected) == SQLITE_ROW) selected = 1; #endif #if DRAW_HISTORY == 1 DT_DEBUG_SQLITE3_CLEAR_BINDINGS(darktable.view_manager->statements.have_history); DT_DEBUG_SQLITE3_RESET(darktable.view_manager->statements.have_history); DT_DEBUG_SQLITE3_BIND_INT(darktable.view_manager->statements.have_history, 1, imgid); /* lets check if imgid has history */ if(sqlite3_step(darktable.view_manager->statements.have_history) == SQLITE_ROW) altered = 1; #endif const dt_image_t *img = dt_image_cache_read_testget(darktable.image_cache, imgid); #if DRAW_GROUPING == 1 DT_DEBUG_SQLITE3_CLEAR_BINDINGS(darktable.view_manager->statements.get_grouped); DT_DEBUG_SQLITE3_RESET(darktable.view_manager->statements.get_grouped); DT_DEBUG_SQLITE3_BIND_INT(darktable.view_manager->statements.get_grouped, 1, imgid); DT_DEBUG_SQLITE3_BIND_INT(darktable.view_manager->statements.get_grouped, 2, imgid); /* lets check if imgid is in a group */ if(sqlite3_step(darktable.view_manager->statements.get_grouped) == SQLITE_ROW) is_grouped = 1; else if(img && darktable.gui->expanded_group_id == img->group_id) darktable.gui->expanded_group_id = -1; #endif if(selected == 1) { outlinecol = 0.4; bgcol = 0.6; fontcol = 0.5; } if(imgsel == imgid) { bgcol = 0.8; // mouse over fontcol = 0.7; outlinecol = 0.6; // if the user points at this image, we really want it: if(!img) img = dt_image_cache_read_get(darktable.image_cache, imgid); } float imgwd = 0.90f; if(zoom == 1) { imgwd = .97f; // cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); } else { double x0 = 1, y0 = 1, rect_width = width-2, rect_height = height-2, radius = 5; double x1, y1, off, off1; x1=x0+rect_width; y1=y0+rect_height; off=radius*0.666; off1 = radius-off; cairo_move_to (cr, x0, y0 + radius); cairo_curve_to (cr, x0, y0+off1, x0+off1 , y0, x0 + radius, y0); cairo_line_to (cr, x1 - radius, y0); cairo_curve_to (cr, x1-off1, y0, x1, y0+off1, x1, y0 + radius); cairo_line_to (cr, x1 , y1 - radius); cairo_curve_to (cr, x1, y1-off1, x1-off1, y1, x1 - radius, y1); cairo_line_to (cr, x0 + radius, y1); cairo_curve_to (cr, x0+off1, y1, x0, y1-off1, x0, y1- radius); cairo_close_path (cr); cairo_set_source_rgb(cr, bgcol, bgcol, bgcol); cairo_fill_preserve(cr); cairo_set_line_width(cr, 0.005*width); cairo_set_source_rgb(cr, outlinecol, outlinecol, outlinecol); cairo_stroke(cr); if(img) { const char *ext = img->filename + strlen(img->filename); while(ext > img->filename && *ext != '.') ext--; ext++; cairo_set_source_rgb(cr, fontcol, fontcol, fontcol); cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size (cr, .25*width); cairo_text_extents_t text_extends; cairo_text_extents (cr, ext, &text_extends); cairo_move_to (cr, .025*width - text_extends.x_bearing, .24*height); cairo_show_text (cr, ext); } } dt_mipmap_buffer_t buf; dt_mipmap_size_t mip = dt_mipmap_cache_get_matching_size( darktable.mipmap_cache, imgwd*width, imgwd*height); dt_mipmap_cache_read_get( darktable.mipmap_cache, &buf, imgid, mip, 0); #if DRAW_THUMB == 1 float scale = 1.0; // decompress image, if necessary. if compression is off, scratchmem will be == NULL, // so get the real pointer back: uint8_t *buf_decompressed = dt_mipmap_cache_decompress(&buf, scratchmem); cairo_surface_t *surface = NULL; if(buf.buf) { const int32_t stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, buf.width); surface = cairo_image_surface_create_for_data (buf_decompressed, CAIRO_FORMAT_RGB24, buf.width, buf.height, stride); if(zoom == 1) { scale = fminf( fminf(darktable.thumbnail_width, width) / (float)buf.width, fminf(darktable.thumbnail_height, height) / (float)buf.height ); } else scale = fminf(width*imgwd/(float)buf.width, height*imgwd/(float)buf.height); } // draw centered and fitted: cairo_save(cr); cairo_translate(cr, width/2.0, height/2.0f); cairo_scale(cr, scale, scale); if(buf.buf) { cairo_translate(cr, -.5f*buf.width, -.5f*buf.height); cairo_set_source_surface (cr, surface, 0, 0); // set filter no nearest: // in skull mode, we want to see big pixels. // in 1 iir mode for the right mip, we want to see exactly what the pipe gave us, 1:1 pixel for pixel. // in between, filtering just makes stuff go unsharp. if((buf.width <= 8 && buf.height <= 8) || fabsf(scale - 1.0f) < 0.01f) cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); cairo_rectangle(cr, 0, 0, buf.width, buf.height); cairo_fill(cr); cairo_surface_destroy (surface); cairo_rectangle(cr, 0, 0, buf.width, buf.height); } // border around image const float border = zoom == 1 ? 16/scale : 2/scale; cairo_set_source_rgb(cr, bordercol, bordercol, bordercol); if(buf.buf && selected) { cairo_set_line_width(cr, 1./scale); if(zoom == 1) { // draw shadow around border cairo_set_source_rgb(cr, 0.2, 0.2, 0.2); cairo_stroke(cr); // cairo_new_path(cr); cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); float alpha = 1.0f; for(int k=0; k<16; k++) { cairo_rectangle(cr, 0, 0, buf.width, buf.height); cairo_new_sub_path(cr); cairo_rectangle(cr, -k/scale, -k/scale, buf.width+2.*k/scale, buf.height+2.*k/scale); cairo_set_source_rgba(cr, 0, 0, 0, alpha); alpha *= 0.6f; cairo_fill(cr); } } else { cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); cairo_new_sub_path(cr); cairo_rectangle(cr, -border, -border, buf.width+2.*border, buf.height+2.*border); cairo_stroke_preserve(cr); cairo_set_source_rgb(cr, 1.0-bordercol, 1.0-bordercol, 1.0-bordercol); cairo_fill(cr); } } else if(buf.buf) { cairo_set_line_width(cr, 1); cairo_stroke(cr); } cairo_restore(cr); #endif if(buf.buf) dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); const float fscale = fminf(width, height); if(imgsel == imgid) { // draw mouseover hover effects, set event hook for mouse button down! *image_over = DT_VIEW_DESERT; cairo_set_line_width(cr, 1.5); cairo_set_source_rgb(cr, outlinecol, outlinecol, outlinecol); cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); float r1, r2; if(zoom != 1) { r1 = 0.05*width; r2 = 0.022*width; } else { r1 = 0.015*fscale; r2 = 0.007*fscale; } float x, y; if(zoom != 1) y = 0.90*height; else y = .12*fscale; gboolean image_is_rejected = (img && ((img->flags & 0x7) == 6)); if(img) for(int k=0; k<5; k++) { if(zoom != 1) x = (0.41+k*0.12)*width; else x = (.08+k*0.04)*fscale; if(!image_is_rejected) //if rejected: draw no stars { dt_view_star(cr, x, y, r1, r2); if((px - x)*(px - x) + (py - y)*(py - y) < r1*r1) { *image_over = DT_VIEW_STAR_1 + k; cairo_fill(cr); } else if((img->flags & 0x7) > k) { cairo_fill_preserve(cr); cairo_set_source_rgb(cr, 1.0-bordercol, 1.0-bordercol, 1.0-bordercol); cairo_stroke(cr); cairo_set_source_rgb(cr, outlinecol, outlinecol, outlinecol); } else cairo_stroke(cr); } } //Image rejected? if(zoom !=1) x = 0.11*width; else x = .04*fscale; if (image_is_rejected) cairo_set_source_rgb(cr, 1., 0., 0.); if((px - x)*(px - x) + (py - y)*(py - y) < r1*r1) { *image_over = DT_VIEW_REJECT; //mouse sensitive cairo_new_sub_path(cr); cairo_arc(cr, x, y, (r1+r2)*.5, 0, 2.0f*M_PI); cairo_stroke(cr); } if (image_is_rejected) cairo_set_line_width(cr, 2.5); //reject cross: cairo_move_to(cr, x-r2, y-r2); cairo_line_to(cr, x+r2, y+r2); cairo_move_to(cr, x+r2, y-r2); cairo_line_to(cr, x-r2, y+r2); cairo_close_path(cr); cairo_stroke(cr); cairo_set_source_rgb(cr, outlinecol, outlinecol, outlinecol); cairo_set_line_width(cr, 1.5); // image part of a group? if(is_grouped && darktable.gui && darktable.gui->grouping) { // draw grouping icon and border if the current group is expanded // align to the right, left of altered float s = (r1+r2)*.75; float _x, _y; if(zoom != 1) { _x = width*0.9 - s*2.5; _y = height*0.1 - s*.4; } else { _x = (.04+7*0.04-1.1*.04)*fscale; _y = y - (.17*.04)*fscale; } cairo_save(cr); if(img && (imgid != img->group_id)) cairo_set_source_rgb(cr, fontcol, fontcol, fontcol); dtgtk_cairo_paint_grouping(cr, _x, _y, s, s, 23); cairo_restore(cr); // mouse is over the grouping icon if(img && abs(px-_x-.5*s) <= .8*s && abs(py-_y-.5*s) <= .8*s) *image_over = DT_VIEW_GROUP; } // image altered? if(altered) { // align to right float s = (r1+r2)*.5; if(zoom != 1) { x = width*0.9; y = height*0.1; } else x = (.04+7*0.04)*fscale; dt_view_draw_altered(cr, x, y, s); //g_print("px = %d, x = %.4f, py = %d, y = %.4f\n", px, x, py, y); if(img && abs(px-x) <= 1.2*s && abs(py-y) <= 1.2*s) // mouse hovers over the altered-icon -> history tooltip! { darktable.gui->center_tooltip = 1; } } } // kill all paths, in case img was not loaded yet, or is blocked: cairo_new_path(cr); #if DRAW_COLORLABELS == 1 // TODO: make mouse sensitive, just as stars! // TODO: cache in image struct! { // color labels: const float x = zoom == 1 ? (0.07)*fscale : .21*width; const float y = zoom == 1 ? 0.17*fscale: 0.1*height; const float r = zoom == 1 ? 0.01*fscale : 0.03*width; /* clear and reset prepared statement */ DT_DEBUG_SQLITE3_CLEAR_BINDINGS(darktable.view_manager->statements.get_color); DT_DEBUG_SQLITE3_RESET(darktable.view_manager->statements.get_color); /* setup statement and iterate rows */ DT_DEBUG_SQLITE3_BIND_INT(darktable.view_manager->statements.get_color, 1, imgid); while(sqlite3_step(darktable.view_manager->statements.get_color) == SQLITE_ROW) { cairo_save(cr); int col = sqlite3_column_int(darktable.view_manager->statements.get_color, 0); // see src/dtgtk/paint.c dtgtk_cairo_paint_label(cr, x+(3*r*col)-5*r, y-r, r*2, r*2, col); cairo_restore(cr); } } #endif if(img && (zoom == 1)) { // some exif data cairo_set_source_rgb(cr, .7, .7, .7); cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size (cr, .025*fscale); cairo_move_to (cr, .02*fscale, .04*fscale); // cairo_show_text(cr, img->filename); cairo_text_path(cr, img->filename); char exifline[50]; cairo_move_to (cr, .02*fscale, .08*fscale); dt_image_print_exif(img, exifline, 50); cairo_text_path(cr, exifline); cairo_fill_preserve(cr); cairo_set_line_width(cr, 1.0); cairo_set_source_rgb(cr, 0.3, 0.3, 0.3); cairo_stroke(cr); } if(img) dt_image_cache_read_release(darktable.image_cache, img); cairo_restore(cr); // if(zoom == 1) cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT); const double end = dt_get_wtime(); dt_print(DT_DEBUG_PERF, "[lighttable] image expose took %0.04f sec\n", end-start); }