Exemple #1
0
// This is basically the same as dt_image_remove() from common/image.c.
// It just does the iteration over all images in the SQL statement
void dt_film_remove(const int id)
{
  sqlite3_stmt *stmt;
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                              "update tagxtag set count = count - 1 where "
                              "(id2 in (select tagid from tagged_images where imgid in "
                              "(select id from images where film_id = ?1))) or (id1 in "
                              "(select tagid from tagged_images where imgid in "
                              "(select id from images where film_id = ?1)))", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                              "delete from tagged_images where imgid in "
                              "(select id from images where film_id = ?1)", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                              "delete from history where imgid in "
                              "(select id from images where film_id = ?1)", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                              "delete from color_labels where imgid in "
                              "(select id from images where film_id = ?1)", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                              "delete from meta_data where id in "
                              "(select id from images where film_id = ?1)", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                              "delete from selected_images where imgid in "
                              "(select id from images where film_id = ?1)", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);

  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                              "select id from images where film_id = ?1", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  while(sqlite3_step(stmt) == SQLITE_ROW)
  {
    const uint32_t imgid = sqlite3_column_int(stmt, 0);
    dt_mipmap_cache_remove(darktable.mipmap_cache, imgid);
    dt_image_cache_remove (darktable.image_cache, imgid);
  }
  sqlite3_finalize(stmt);

  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                              "delete from images where id in "
                              "(select id from images where film_id = ?1)", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);

  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                              "delete from film_rolls where id = ?1", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);
  // dt_control_update_recent_films();
  dt_control_signal_raise(darktable.signals , DT_SIGNAL_FILMROLLS_CHANGED);
}
Exemple #2
0
void dt_imageio_insert_storage(dt_imageio_module_storage_t* storage)
{
  darktable.imageio->plugins_storage = g_list_insert_sorted(darktable.imageio->plugins_storage, storage, dt_imageio_sort_modules_storage);
  dt_control_signal_raise(darktable.signals,DT_SIGNAL_IMAGEIO_STORAGE_CHANGE);
}
Exemple #3
0
void dt_control_queue_redraw_center()
{
  dt_control_signal_raise(darktable.signals, DT_SIGNAL_CONTROL_REDRAW_CENTER);
}
Exemple #4
0
static void
delete_button_clicked (GtkButton *button, gpointer user_data)
{
  dt_lib_module_t *self = (dt_lib_module_t *)user_data;
  dt_lib_tagging_t *d   = (dt_lib_tagging_t *)self->data;

  int res = GTK_RESPONSE_YES;

  guint tagid;
  GtkTreeIter iter;
  GtkTreeModel *model = NULL;
  GtkTreeView *view = d->related;
  GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
  if(!gtk_tree_selection_get_selected(selection, &model, &iter)) return;
  gtk_tree_model_get (model, &iter,
                      DT_LIB_TAGGING_COL_ID, &tagid,
                      -1);

  // First check how many images are affected by the remove
  int count = dt_tag_remove(tagid,FALSE);
  if( count > 0 && dt_conf_get_bool("plugins/lighttable/tagging/ask_before_delete_tag") )
  {
    GtkWidget *dialog;
    GtkWidget *win = dt_ui_main_window(darktable.gui->ui);
    gchar *tagname=dt_tag_get_name(tagid);
    dialog = gtk_message_dialog_new(GTK_WINDOW(win),
                                    GTK_DIALOG_DESTROY_WITH_PARENT,
                                    GTK_MESSAGE_QUESTION,
                                    GTK_BUTTONS_YES_NO,
                                    ngettext("do you really want to delete the tag `%s'?\n%d image is assigned this tag!",
                                        "do you really want to delete the tag `%s'?\n%d images are assigned this tag!", count),
                                    tagname,count);
    gtk_window_set_title(GTK_WINDOW(dialog), _("delete tag?"));
    res = gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog);
    free(tagname);
  }
  if(res != GTK_RESPONSE_YES) return;

  GList *tagged_images = NULL;
  sqlite3_stmt *stmt;
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select imgid from tagged_images where tagid=?1", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, tagid);
  while(sqlite3_step(stmt) == SQLITE_ROW)
  {
    tagged_images = g_list_append(tagged_images, GINT_TO_POINTER(sqlite3_column_int(stmt, 0)));
  }
  sqlite3_finalize(stmt);

  dt_tag_remove(tagid,TRUE);

  GList *list_iter;
  if((list_iter = g_list_first(tagged_images)) != NULL)
  {
    do
    {
      dt_image_synch_xmp(GPOINTER_TO_INT(list_iter->data));
    }
    while((list_iter=g_list_next(list_iter)) != NULL);
  }
  g_list_free(g_list_first(tagged_images));

  update(self, 0);
  update(self, 1);

  dt_control_signal_raise(darktable.signals, DT_SIGNAL_TAG_CHANGED);
}
Exemple #5
0
void gui_init(dt_lib_module_t *self)
{
  /* initialize ui widgets */
  dt_lib_keywords_t *d = (dt_lib_keywords_t *)g_malloc(sizeof(dt_lib_keywords_t));

  memset(d,0,sizeof(dt_lib_keywords_t));
  self->data = (void *)d;
  self->widget = gtk_vbox_new(FALSE, 5);

  /* Create a new scrolled window, with scrollbars only if needed */
  GtkWidget *scrolled_window;
  scrolled_window = gtk_scrolled_window_new (NULL, NULL);

  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                  GTK_POLICY_AUTOMATIC,
                                  GTK_POLICY_AUTOMATIC);


  /* add the treeview to show hirarchy tags*/
  GtkCellRenderer *renderer;

  d->view = GTK_TREE_VIEW (gtk_tree_view_new());
  gtk_widget_set_size_request(GTK_WIDGET(d->view), -1, 300);

  gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(d->view));

  renderer = gtk_cell_renderer_text_new ();
  gtk_tree_view_insert_column_with_attributes(d->view,
      -1,
      "",
      renderer,
      "text", 0,
      NULL);

  gtk_tree_view_set_headers_visible(d->view, FALSE);

  /* setup dnd source and destination within treeview */
  static const GtkTargetEntry dnd_target = { "keywords-reorganize",
                              GTK_TARGET_SAME_WIDGET, 0
                                           };

  gtk_tree_view_enable_model_drag_source(d->view,
                                         GDK_BUTTON1_MASK,
                                         &dnd_target, 1, GDK_ACTION_MOVE);

  gtk_tree_view_enable_model_drag_dest(d->view, &dnd_target, 1, GDK_ACTION_MOVE);

  /* setup drag and drop signals */
  g_signal_connect(G_OBJECT(d->view),"drag-data-received",
                   G_CALLBACK(_lib_keywords_drag_data_received_callback),
                   self);

  g_signal_connect(G_OBJECT(d->view),"drag-data-get",
                   G_CALLBACK(_lib_keywords_drag_data_get_callback),
                   self);

  /* add callback when keyword is activated */
  g_signal_connect(G_OBJECT(d->view), "row-activated",
                   G_CALLBACK(_lib_keywords_add_collection_rule), self);

  gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(scrolled_window), TRUE, TRUE, 0);

  gtk_widget_show_all(GTK_WIDGET(d->view));

  dt_control_signal_connect(darktable.signals,
                            DT_SIGNAL_TAG_CHANGED,
                            G_CALLBACK(_lib_tag_gui_update),
                            self);

  /* raise signal of tags change to refresh keywords tree */
  dt_control_signal_raise(darktable.signals, DT_SIGNAL_TAG_CHANGED);
}
Exemple #6
0
void dt_styles_update(const char *name, const char *newname, const char *newdescription, GList *filter,
                      int imgid, GList *update)
{
    sqlite3_stmt *stmt;
    int id = 0;
    gchar *desc = NULL;

    id = dt_styles_get_id_by_name(name);
    if(id == 0) return;

    desc = dt_styles_get_description(name);

    if((g_strcmp0(name, newname)) || (g_strcmp0(desc, newdescription)))
    {
        DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                                    "UPDATE data.styles SET name=?1, description=?2 WHERE id=?3", -1, &stmt, NULL);
        DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, newname, -1, SQLITE_STATIC);
        DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, newdescription, -1, SQLITE_STATIC);
        DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, id);
        sqlite3_step(stmt);
        sqlite3_finalize(stmt);
    }

    if(filter)
    {
        GList *list = filter;
        char tmp[64];
        char include[2048] = { 0 };
        g_strlcat(include, "num NOT IN (", sizeof(include));
        do
        {
            if(list != g_list_first(list)) g_strlcat(include, ",", sizeof(include));
            snprintf(tmp, sizeof(tmp), "%d", GPOINTER_TO_INT(list->data));
            g_strlcat(include, tmp, sizeof(include));
        } while((list = g_list_next(list)));
        g_strlcat(include, ")", sizeof(include));

        char query[4096] = { 0 };
        snprintf(query, sizeof(query), "DELETE FROM data.style_items WHERE styleid=?1 AND %s", include);
        DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
        DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
        sqlite3_step(stmt);
        sqlite3_finalize(stmt);
    }

    _dt_style_update_from_image(id, imgid, filter, update);

    _dt_style_cleanup_multi_instance(id);

    /* backup style to disk */
    char stylesdir[PATH_MAX] = { 0 };
    dt_loc_get_user_config_dir(stylesdir, sizeof(stylesdir));
    g_strlcat(stylesdir, "/styles", sizeof(stylesdir));
    g_mkdir_with_parents(stylesdir, 00755);

    dt_styles_save_to_file(newname, stylesdir, TRUE);

    /* delete old accelerator and create a new one */
    // TODO: should better use dt_accel_rename_global() to keep the old accel_key untouched, but it seems to be
    // buggy
    if(g_strcmp0(name, newname))
    {
        char tmp_accel[1024];
        snprintf(tmp_accel, sizeof(tmp_accel), C_("accel", "styles/apply %s"), name);
        dt_accel_deregister_global(tmp_accel);

        gchar *tmp_name = g_strdup(newname); // freed by _destroy_style_shortcut_callback
        snprintf(tmp_accel, sizeof(tmp_accel), C_("accel", "styles/apply %s"), newname);
        dt_accel_register_global(tmp_accel, 0, 0);
        GClosure *closure;
        closure = g_cclosure_new(G_CALLBACK(_apply_style_shortcut_callback), tmp_name,
                                 _destroy_style_shortcut_callback);
        dt_accel_connect_global(tmp_accel, closure);
    }

    dt_control_signal_raise(darktable.signals, DT_SIGNAL_STYLE_CHANGED);

    g_free(desc);
}
Exemple #7
0
void dt_styles_apply_to_image(const char *name, gboolean duplicate, int32_t imgid)
{
    int id = 0;
    sqlite3_stmt *stmt;
    int32_t newimgid;

    if((id = dt_styles_get_id_by_name(name)) != 0)
    {
        /* check if we should make a duplicate before applying style */
        if(duplicate)
        {
            newimgid = dt_image_duplicate(imgid);
            if(newimgid != -1) dt_history_copy_and_paste_on_image(imgid, newimgid, FALSE, NULL);
        }
        else
            newimgid = imgid;

        /* merge onto history stack, let's find history offest in destination image */
        /* first trim the stack to get rid of whatever is above the selected entry */
        DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                                    "DELETE FROM main.history WHERE imgid = ?1 AND num >= (SELECT history_end "
                                    "FROM main.images WHERE id = imgid)", -1, &stmt, NULL);
        DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newimgid);
        sqlite3_step(stmt);
        sqlite3_finalize(stmt);

        /* in sqlite ROWID starts at 1, while our num column starts at 0 */
        int32_t offs = -1;
        DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                                    "SELECT IFNULL(MAX(num), -1) FROM main.history WHERE imgid = ?1", -1, &stmt, NULL);
        DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newimgid);
        if(sqlite3_step(stmt) == SQLITE_ROW) offs = sqlite3_column_int(stmt, 0);
        sqlite3_finalize(stmt);

        /* delete all items from the temp styles_items, this table is used only to get a ROWNUM of the results */
        DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "DELETE FROM memory.style_items", NULL, NULL, NULL);

        /* copy history items from styles onto temp table */
        DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "INSERT INTO memory.style_items SELECT * FROM "
                                    "data.style_items WHERE styleid=?1 ORDER BY "
                                    "multi_priority DESC",
                                    -1, &stmt, NULL);
        DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
        sqlite3_step(stmt);
        sqlite3_finalize(stmt);

        /* copy the style items into the history */
        DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                                    "INSERT INTO main.history "
                                    "(imgid,num,module,operation,op_params,enabled,blendop_params,blendop_"
                                    "version,multi_priority,multi_name) SELECT "
                                    "?1,?2+rowid,module,operation,op_params,enabled,blendop_params,blendop_"
                                    "version,multi_priority,multi_name FROM memory.style_items",
                                    -1, &stmt, NULL);
        DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newimgid);
        DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, offs);
        sqlite3_step(stmt);
        sqlite3_finalize(stmt);

        /* always make the whole stack active */
        DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                                    "UPDATE main.images SET history_end = (SELECT MAX(num) + 1 FROM main.history "
                                    "WHERE imgid = ?1) WHERE id = ?1",
                                    -1, &stmt, NULL);
        DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newimgid);
        sqlite3_step(stmt);
        sqlite3_finalize(stmt);

        /* add tag */
        guint tagid = 0;
        gchar ntag[512] = { 0 };
        g_snprintf(ntag, sizeof(ntag), "darktable|style|%s", name);
        if(dt_tag_new(ntag, &tagid)) dt_tag_attach(tagid, newimgid);
        if(dt_tag_new("darktable|changed", &tagid)) dt_tag_attach(tagid, newimgid);

        /* if current image in develop reload history */
        if(dt_dev_is_current_image(darktable.develop, newimgid))
        {
            dt_dev_reload_history_items(darktable.develop);
            dt_dev_modulegroups_set(darktable.develop, dt_dev_modulegroups_get(darktable.develop));
        }

        /* update xmp file */
        dt_image_synch_xmp(newimgid);

        /* remove old obsolete thumbnails */
        dt_mipmap_cache_remove(darktable.mipmap_cache, newimgid);

        /* if we have created a duplicate, reset collected images */
        if(duplicate) dt_control_signal_raise(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED);

        /* redraw center view to update visible mipmaps */
        dt_control_queue_redraw_center();
    }
}
Exemple #8
0
void dt_film_import1(dt_film_t *film)
{
  gboolean recursive = dt_conf_get_bool("ui_last/import_recursive");

  /* first of all gather all images to import */
  GList *images = NULL;
  images = _film_recursive_get_files(film->dirname, recursive, &images);
  if(g_list_length(images) == 0)
  {
    dt_control_log(_("no supported images were found to be imported"));
    return;
  }

  /* we got ourself a list of images, lets sort and start import */
  images = g_list_sort(images,(GCompareFunc)_film_filename_cmp);

  /* let's start import of images */
  gchar message[512] = {0};
  double fraction = 0;
  uint32_t total = g_list_length(images);
  g_snprintf(message, sizeof(message) - 1,
             ngettext("importing %d image","importing %d images", total), total);
  const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message);

  /* loop thru the images and import to current film roll */
  dt_film_t *cfr = film;
  GList *image = g_list_first(images);
  do
  {
    gchar *cdn = g_path_get_dirname((const gchar *)image->data);

    /* check if we need to initialize a new filmroll */
    if(!cfr || g_strcmp0(cfr->dirname, cdn) != 0)
    {

#if GLIB_CHECK_VERSION (2, 26, 0)
      if(cfr && cfr->dir)
      {
        /* check if we can find a gpx data file to be auto applied
           to images in the jsut imported filmroll */
        g_dir_rewind(cfr->dir);
        const gchar *dfn = NULL;
        while ((dfn = g_dir_read_name(cfr->dir)) != NULL)
        {
          /* check if we have a gpx to be auto applied to filmroll */
          if(strcmp(dfn+strlen(dfn)-4,".gpx") == 0 ||
              strcmp(dfn+strlen(dfn)-4,".GPX") == 0)
          {
            gchar *gpx_file = g_build_path (G_DIR_SEPARATOR_S, cfr->dirname, dfn, NULL);
            dt_control_gpx_apply(gpx_file, cfr->id, dt_conf_get_string("plugins/lighttable/geotagging/tz"));
            g_free(gpx_file);
          }
        }
      }
#endif

      /* cleanup previously imported filmroll*/
      if(cfr && cfr!=film)
      {
        if(dt_film_is_empty(cfr->id))
        {
          dt_film_remove(cfr->id);
        }
        dt_film_cleanup(cfr);
        g_free(cfr);
        cfr = NULL;
      }

      /* initialize and create a new film to import to */
      cfr = g_malloc(sizeof(dt_film_t));
      dt_film_init(cfr);
      dt_film_new(cfr, cdn);
    }

    /* import image */
    dt_image_import(cfr->id, (const gchar *)image->data, FALSE);

    fraction+=1.0/total;
    dt_control_backgroundjobs_progress(darktable.control, jid, fraction);

  }
  while( (image = g_list_next(image)) != NULL);

  // only redraw at the end, to not spam the cpu with exposure events
  dt_control_queue_redraw_center();
  dt_control_signal_raise(darktable.signals,DT_SIGNAL_TAG_CHANGED);

  dt_control_backgroundjobs_destroy(darktable.control, jid);
  dt_control_signal_raise(darktable.signals , DT_SIGNAL_FILMROLLS_IMPORTED,film->id);

#if GLIB_CHECK_VERSION (2, 26, 0)
  if(cfr && cfr->dir)
  {
    /* check if we can find a gpx data file to be auto applied
       to images in the just imported filmroll */
    g_dir_rewind(cfr->dir);
    const gchar *dfn = NULL;
    while ((dfn = g_dir_read_name(cfr->dir)) != NULL)
    {
      /* check if we have a gpx to be auto applied to filmroll */
      if(strcmp(dfn+strlen(dfn)-4,".gpx") == 0 ||
          strcmp(dfn+strlen(dfn)-4,".GPX") == 0)
      {
        gchar *gpx_file = g_build_path (G_DIR_SEPARATOR_S, cfr->dirname, dfn, NULL);
        dt_control_gpx_apply(gpx_file, cfr->id, dt_conf_get_string("plugins/lighttable/geotagging/tz"));
        g_free(gpx_file);
      }
    }
  }
#endif
}
Exemple #9
0
// This is basically the same as dt_image_remove() from common/image.c.
// It just does the iteration over all images in the SQL statement
void dt_film_remove(const int id)
{
  // only allowed if local copies have their original accessible

  sqlite3_stmt *stmt;

  gboolean remove_ok = TRUE;
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT id FROM main.images WHERE film_id = ?1", -1,
                              &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);

  while(sqlite3_step(stmt) == SQLITE_ROW)
  {
    int imgid = sqlite3_column_int(stmt, 0);
    if(!dt_image_safe_remove(imgid))
    {
      remove_ok = FALSE;
      break;
    }
  }
  sqlite3_finalize(stmt);

  if(!remove_ok)
  {
    dt_control_log(_("cannot remove film roll having local copies with non accessible originals"));
    return;
  }

  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.tagged_images WHERE imgid IN "
                                                             "(SELECT id FROM main.images WHERE film_id = ?1)",
                              -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.history WHERE imgid IN "
                                                             "(SELECT id FROM main.images WHERE film_id = ?1)",
                              -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.masks_history WHERE imgid IN "
                                                             "(SELECT id FROM main.images WHERE film_id = ?1)",
                              -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.color_labels WHERE imgid IN "
                                                             "(SELECT id FROM main.images WHERE film_id = ?1)",
                              -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.meta_data WHERE id IN "
                                                             "(SELECT id FROM main.images WHERE film_id = ?1)",
                              -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.selected_images WHERE imgid IN "
                                                             "(SELECT id FROM main.images WHERE film_id = ?1)",
                              -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);

  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT id FROM main.images WHERE film_id = ?1", -1,
                              &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  while(sqlite3_step(stmt) == SQLITE_ROW)
  {
    const uint32_t imgid = sqlite3_column_int(stmt, 0);
    dt_image_local_copy_reset(imgid);
    dt_mipmap_cache_remove(darktable.mipmap_cache, imgid);
    dt_image_cache_remove(darktable.image_cache, imgid);
  }
  sqlite3_finalize(stmt);

  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.images WHERE id IN "
                                                             "(SELECT id FROM main.images WHERE film_id = ?1)",
                              -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);

  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "DELETE FROM main.film_rolls WHERE id = ?1", -1,
                              &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);
  // dt_control_update_recent_films();

  dt_tag_update_used_tags();

  dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED);
}
Exemple #10
0
uint32_t dt_image_import(const int32_t film_id, const char *filename, gboolean override_ignore_jpegs)
{
  if(!g_file_test(filename, G_FILE_TEST_IS_REGULAR))
    return 0;
  const char *cc = filename + strlen(filename);
  for(; *cc!='.'&&cc>filename; cc--);
  if(!strcmp(cc, ".dt")) return 0;
  if(!strcmp(cc, ".dttags")) return 0;
  if(!strcmp(cc, ".xmp")) return 0;
  char *ext = g_ascii_strdown(cc+1, -1);
  if(override_ignore_jpegs == FALSE && (!strcmp(ext, "jpg") ||
                                        !strcmp(ext, "jpeg")) && dt_conf_get_bool("ui_last/import_ignore_jpegs"))
    return 0;
  int supported = 0;
  char **extensions = g_strsplit(dt_supported_extensions, ",", 100);
  for(char **i=extensions; *i!=NULL; i++)
    if(!strcmp(ext, *i))
    {
      supported = 1;
      break;
    }
  g_strfreev(extensions);
  if(!supported)
  {
    g_free(ext);
    return 0;
  }
  int rc;
  uint32_t id = 0;
  // select from images; if found => return
  gchar *imgfname;
  imgfname = g_path_get_basename((const gchar*)filename);
  sqlite3_stmt *stmt;
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                              "select id from images where film_id = ?1 and filename = ?2",
                              -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
  DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgfname, strlen(imgfname), SQLITE_STATIC);
  if(sqlite3_step(stmt) == SQLITE_ROW)
  {
    id = sqlite3_column_int(stmt, 0);
    g_free(imgfname);
    sqlite3_finalize(stmt);
    g_free(ext);
    const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, id);
    dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg);
    img->flags &= ~DT_IMAGE_REMOVE;
    dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED);
    dt_image_cache_read_release(darktable.image_cache, img);
    return id;
  }
  sqlite3_finalize(stmt);

  // also need to set the no-legacy bit, to make sure we get the right presets (new ones)
  uint32_t flags = dt_conf_get_int("ui_last/import_initial_rating");
  if(flags > 5)
  {
    flags = 1;
    dt_conf_set_int("ui_last/import_initial_rating", 1);
  }
  flags |= DT_IMAGE_NO_LEGACY_PRESETS;
  // insert dummy image entry in database
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                              "insert into images (id, film_id, filename, caption, description, "
                              "license, sha1sum, flags) values (null, ?1, ?2, '', '', '', '', ?3)",
                              -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
  DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgfname, strlen(imgfname),
                             SQLITE_TRANSIENT);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, flags);
  rc = sqlite3_step(stmt);
  if (rc != SQLITE_DONE) fprintf(stderr, "sqlite3 error %d\n", rc);
  sqlite3_finalize(stmt);

  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                              "select id from images where film_id = ?1 and filename = ?2",
                              -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
  DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgfname, strlen(imgfname),
                             SQLITE_STATIC);
  if(sqlite3_step(stmt) == SQLITE_ROW) id = sqlite3_column_int(stmt, 0);
  sqlite3_finalize(stmt);

  // Try to find out if this should be grouped already.
  gchar *basename = g_strdup(imgfname);
  gchar *cc2 = basename + strlen(basename);
  for(; *cc2!='.'&&cc2>basename; cc2--);
  *cc2='\0';
  gchar *sql_pattern = g_strconcat(basename, ".%", NULL);
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select group_id from images where film_id = ?1 and filename like ?2 and id != ?3", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
  DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, sql_pattern, -1, SQLITE_TRANSIENT);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, id);
  int group_id;
  if(sqlite3_step(stmt) == SQLITE_ROW) group_id = sqlite3_column_int(stmt, 0);
  else                                 group_id = id;
  sqlite3_finalize(stmt);
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "update images set group_id = ?1 where id = ?2", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, group_id);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, id);
  sqlite3_step(stmt);
  sqlite3_finalize(stmt);

  // printf("[image_import] importing `%s' to img id %d\n", imgfname, id);

  // lock as shortly as possible:
  const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, id);
  dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg);
  img->group_id = group_id;

  // read dttags and exif for database queries!
  (void) dt_exif_read(img, filename);
  char dtfilename[DT_MAX_PATH_LEN];
  g_strlcpy(dtfilename, filename, DT_MAX_PATH_LEN);
  dt_image_path_append_version(id, dtfilename, DT_MAX_PATH_LEN);
  char *c = dtfilename + strlen(dtfilename);
  sprintf(c, ".xmp");
  (void)dt_exif_xmp_read(img, dtfilename, 0);

  // write through to db, but not to xmp.
  dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED);
  dt_image_cache_read_release(darktable.image_cache, img);

  // add a tag with the file extension
  guint tagid = 0;
  char tagname[512];
  snprintf(tagname, 512, "darktable|format|%s", ext);
  g_free(ext);
  dt_tag_new(tagname, &tagid);
  dt_tag_attach(tagid,id);

  // Search for sidecar files and import them if found.
  glob_t *globbuf = g_malloc(sizeof(glob_t));

  // Add version wildcard
  gchar *fname = g_strdup(filename);
  gchar pattern[DT_MAX_PATH_LEN];
  g_snprintf(pattern, DT_MAX_PATH_LEN, "%s", filename);
  char *c1 = pattern + strlen(pattern);
  while(*c1 != '.' && c1 > pattern) c1--;
  snprintf(c1, pattern + DT_MAX_PATH_LEN - c1, "_*");
  char *c2 = fname + strlen(fname);
  while(*c2 != '.' && c2 > fname) c2--;
  snprintf(c1+2, pattern + DT_MAX_PATH_LEN - c1 - 2, "%s.xmp", c2);

  if (!glob(pattern, 0, NULL, globbuf))
  {
    for (int i=0; i < globbuf->gl_pathc; i++)
    {
      int newid = -1;
      newid = dt_image_duplicate(id);

      const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, newid);
      dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg);
      (void)dt_exif_xmp_read(img, globbuf->gl_pathv[i], 0);
      dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED);
      dt_image_cache_read_release(darktable.image_cache, img);
    }
    globfree(globbuf);
  }

  g_free(imgfname);
  g_free(fname);
  g_free(basename);
  g_free(sql_pattern);
  g_free(globbuf);

  dt_control_signal_raise(darktable.signals,DT_SIGNAL_IMAGE_IMPORT,id);
  return id;
}
Exemple #11
0
static gboolean async_redraw(gpointer data)
{
    dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED); // just for good measure
    dt_control_queue_redraw();
    return false;
}
Exemple #12
0
int dt_view_manager_switch (dt_view_manager_t *vm, int k)
{
  // Before switching views, restore accelerators if disabled
  if(!darktable.control->key_accelerators_on)
    dt_control_key_accelerators_on(darktable.control);

  // destroy old module list
  int error = 0;
  dt_view_t *v = vm->view + vm->current_view;

  /* Special case when entering nothing (just before leaving dt) */
  if ( k==DT_MODE_NONE )
  {
    /* iterator plugins and cleanup plugins in current view */
    GList *plugins = g_list_last(darktable.lib->plugins);
    while (plugins)
    {
      dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data);

      if (!plugin->views)
        fprintf(stderr,"module %s doesnt have views flags\n",plugin->name());

      /* does this module belong to current view ?*/
      if (plugin->views() & v->view(v) )
      {
        plugin->gui_cleanup(plugin);
        dt_accel_disconnect_list(plugin->accel_closures);
        plugin->accel_closures = NULL;
      }

      /* get next plugin */
      plugins = g_list_previous(plugins);
    }

    /* leave the current view*/
    if(vm->current_view >= 0 && v->leave) v->leave(v);

    /* remove all widets in all containers */
    for(int l=0;l<DT_UI_CONTAINER_SIZE;l++)
      dt_ui_container_clear(darktable.gui->ui, l);

    vm->current_view = -1 ;
    return 0 ;
  }

  int newv = vm->current_view;
  if (k < vm->num_views) newv = k;
  dt_view_t *nv = vm->view + newv;

  if (nv->try_enter) 
    error = nv->try_enter(nv);

  if (!error)
  {
    GList *plugins;
    
    /* cleanup current view before initialization of new  */
    if (vm->current_view >=0)
    {
      /* leave current view */
      if(v->leave) v->leave(v);
      dt_accel_disconnect_list(v->accel_closures);
      v->accel_closures = NULL;

      /* iterator plugins and cleanup plugins in current view */
      plugins = g_list_last(darktable.lib->plugins);
      while (plugins)
      {
        dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data);

        if (!plugin->views)
          fprintf(stderr,"module %s doesnt have views flags\n",plugin->name());

        /* does this module belong to current view ?*/
        if (plugin->views() & v->view(v) )
        {
          plugin->gui_cleanup(plugin);
          dt_accel_disconnect_list(plugin->accel_closures);
          plugin->accel_closures = NULL;
        }

        /* get next plugin */
        plugins = g_list_previous(plugins);
      }

      /* remove all widets in all containers */
      for(int l=0;l<DT_UI_CONTAINER_SIZE;l++)
        dt_ui_container_clear(darktable.gui->ui, l);
    }

    /* change current view to the new view */
    vm->current_view = newv;

    /* restore visible stat of panels for the new view */
    dt_ui_restore_panels(darktable.gui->ui);

    /* lets add plugins related to new view into panels */
    plugins = g_list_last(darktable.lib->plugins);
    while (plugins)
    {
      dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data);
      if( plugin->views() & nv->view(v) )
      {
        /* module should be in this view, lets initialize */
        plugin->gui_init(plugin);

        /* try get the module expander  */
        GtkWidget *w = NULL;
        w = dt_lib_gui_get_expander(plugin);

        if(plugin->connect_key_accels)
          plugin->connect_key_accels(plugin);
        dt_lib_connect_common_accels(plugin);

        /* if we dont got an expander lets add the widget */
        if (!w)
          w = plugin->widget;

        /* add module to it's container */
        dt_ui_container_add_widget(darktable.gui->ui, plugin->container(), w);

      }

      /* lets get next plugin */
      plugins = g_list_previous(plugins);
    }

    /* hide/show modules as last config */
    plugins = g_list_last(darktable.lib->plugins);
    while (plugins)
    {
      dt_lib_module_t *plugin = (dt_lib_module_t *)(plugins->data);
      if(plugin->views() & nv->view(v))
      {
        /* set expanded if last mode was that */
        char var[1024];
        gboolean expanded = FALSE;
        gboolean visible = dt_lib_is_visible(plugin);
        if (plugin->expandable())
        {
          snprintf(var, 1024, "plugins/lighttable/%s/expanded", plugin->plugin_name);
          expanded = dt_conf_get_bool(var);
	  
          /* show expander if visible  */
          if(visible)
          {
            gtk_widget_show_all(GTK_WIDGET(plugin->expander));
            // gtk_widget_show_all(plugin->widget);
          }
          else
          {
            gtk_widget_hide(GTK_WIDGET(plugin->expander));
            // gtk_widget_hide_all(plugin->widget);
          }

          dt_lib_gui_set_expanded(plugin, expanded);
        }
        else
        {
          /* show/hide plugin widget depending on expanded flag or if plugin
             not is expandeable() */
          if(visible)
            gtk_widget_show_all(plugin->widget);
          else
            gtk_widget_hide_all(plugin->widget);
        }
      }

      /* lets get next plugin */
      plugins = g_list_previous(plugins); 
    }

    /* enter view. crucially, do this before initing the plugins below,
       as e.g. modulegroups requires the dr stuff to be inited. */
    if(newv >= 0 && nv->enter) nv->enter(nv);
    if(newv >= 0 && nv->connect_key_accels)
      nv->connect_key_accels(nv);

    /* raise view changed signal */
    dt_control_signal_raise(darktable.signals, DT_SIGNAL_VIEWMANAGER_VIEW_CHANGED);

    /* add endmarkers to left and right center containers */
    GtkWidget *endmarker = gtk_drawing_area_new();
    dt_ui_container_add_widget(darktable.gui->ui, DT_UI_CONTAINER_PANEL_LEFT_CENTER, endmarker);
    g_signal_connect (G_OBJECT (endmarker), "expose-event",
                      G_CALLBACK (dt_control_expose_endmarker), 0);
    gtk_widget_set_size_request(endmarker, -1, 50);
    gtk_widget_show(endmarker);

    endmarker = gtk_drawing_area_new();
    dt_ui_container_add_widget(darktable.gui->ui, DT_UI_CONTAINER_PANEL_RIGHT_CENTER, endmarker);
    g_signal_connect (G_OBJECT (endmarker), "expose-event",
                      G_CALLBACK (dt_control_expose_endmarker), (gpointer)1);
    gtk_widget_set_size_request(endmarker, -1, 50);
    gtk_widget_show(endmarker);
  }

  return error;
}
Exemple #13
0
int32_t dt_control_delete_images_job_run(dt_job_t *job)
{
  int imgid = -1;
  dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param;
  GList *t = t1->index;
  char *imgs = _get_image_list(t);
  int total = g_list_length(t);
  char message[512]= {0};
  double fraction=0;
  snprintf(message, 512, ngettext ("deleting %d image", "deleting %d images", total), total );
  const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message);

  sqlite3_stmt *stmt;

  _set_remove_flag(imgs);

  dt_collection_update(darktable.collection);

  // We need a list of files to regenerate .xmp files if there are duplicates
  GList *list = _get_full_pathname(imgs);

  free(imgs);

  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select count(id) from images where filename in (select filename from images where id = ?1) and film_id in (select film_id from images where id = ?1)", -1, &stmt, NULL);
  while(t)
  {
    imgid = GPOINTER_TO_INT(t->data);
    char filename[DT_MAX_PATH_LEN];
    gboolean from_cache = FALSE;
    dt_image_full_path(imgid, filename, DT_MAX_PATH_LEN, &from_cache);

    int duplicates = 0;
    DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
    if(sqlite3_step(stmt) == SQLITE_ROW)
      duplicates = sqlite3_column_int(stmt, 0);
    sqlite3_reset(stmt);
    sqlite3_clear_bindings(stmt);

    // remove from disk:
    if(duplicates == 1)
    {
      // there are no further duplicates so we can remove the source data file
      (void)g_unlink(filename);
      dt_image_remove(imgid);

      // all sidecar files - including left-overs - can be deleted;
      // left-overs can result when previously duplicates have been REMOVED;
      // no need to keep them as the source data file is gone.
      const int len = DT_MAX_PATH_LEN + 30;
      gchar pattern[len];

      // NULL terminated list of glob patterns; should include "" and can be extended if needed
      static const gchar *glob_patterns[] = { "", "_[0-9][0-9]", "_[0-9][0-9][0-9]", "_[0-9][0-9][0-9][0-9]", NULL };

      const gchar **glob_pattern = glob_patterns;
      GList *files = NULL;
      while(*glob_pattern)
      {
        snprintf(pattern, len, "%s", filename);
        gchar *c1 = pattern + strlen(pattern);
        while(*c1 != '.' && c1 > pattern) c1--;
        snprintf(c1, pattern + len - c1, "%s", *glob_pattern);
        const gchar *c2 = filename + strlen(filename);
        while(*c2 != '.' && c2 > filename) c2--;
        snprintf(c1+strlen(*glob_pattern), pattern + len - c1 - strlen(*glob_pattern), "%s.xmp", c2);

#ifdef __WIN32__
        WIN32_FIND_DATA data;
        HANDLE handle = FindFirstFile(pattern, &data);
        if(handle != INVALID_HANDLE_VALUE)
        {
          do
            files = g_list_append(files, g_strdup(data.cFileName));
          while(FindNextFile(handle, &data));
        }
#else
        glob_t globbuf;
        if(!glob(pattern, 0, NULL, &globbuf))
        {
          for(size_t i=0; i < globbuf.gl_pathc; i++)
            files = g_list_append(files, g_strdup(globbuf.gl_pathv[i]));
          globfree(&globbuf);
        }
#endif

        glob_pattern++;
      }

      GList *file_iter = g_list_first(files);
      while(file_iter != NULL)
      {
        (void)g_unlink(file_iter->data);
        file_iter = g_list_next(file_iter);
      }

      g_list_free_full(files, g_free);
    }
    else
    {
      // don't remove the actual source data if there are further duplicates using it;
      // just delete the xmp file of the duplicate selected.

      dt_image_path_append_version(imgid, filename, DT_MAX_PATH_LEN);
      char *c = filename + strlen(filename);
      sprintf(c, ".xmp");

      dt_image_remove(imgid);
      (void)g_unlink(filename);
    }

    t = g_list_delete_link(t, t);
    fraction=1.0/total;
    dt_control_backgroundjobs_progress(darktable.control, jid, fraction);
  }
  sqlite3_finalize(stmt);

  char *imgname;
  while(list)
  {
    imgname = (char *)list->data;
    dt_image_synch_all_xmp(imgname);
    list = g_list_delete_link(list, list);
  }
  g_list_free(list);
  dt_control_backgroundjobs_destroy(darktable.control, jid);
  dt_film_remove_empty();
  dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED);
  dt_control_queue_redraw_center();
  return 0;
}
Exemple #14
0
int32_t dt_control_remove_images_job_run(dt_job_t *job)
{
  int imgid = -1;
  dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param;
  GList *t = t1->index;
  char *imgs = _get_image_list(t);
  int total = g_list_length(t);
  char message[512]= {0};
  double fraction=0;
  snprintf(message, 512, ngettext ("removing %d image", "removing %d images", total), total );
  const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message);
  sqlite3_stmt *stmt = NULL;

  // check that we can safely remove the image
  gboolean remove_ok = TRUE;
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT id FROM images WHERE id IN (?2) AND flags&?1=?1", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, DT_IMAGE_LOCAL_COPY);
  DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgs, -1, SQLITE_STATIC);

  while(sqlite3_step(stmt) == SQLITE_ROW)
  {
    int imgid = sqlite3_column_int(stmt, 0);
    if (!dt_image_safe_remove(imgid))
    {
      remove_ok = FALSE;
      break;
    }
  }
  sqlite3_finalize(stmt);

  if (!remove_ok)
  {
    dt_control_log(_("cannot remove local copy when the original file is not accessible."));
    dt_control_backgroundjobs_destroy(darktable.control, jid);
    free(imgs);
    return 0;
  }

  // update remove status
  _set_remove_flag(imgs);

  dt_collection_update(darktable.collection);

  // We need a list of files to regenerate .xmp files if there are duplicates
  GList *list = _get_full_pathname(imgs);

  free(imgs);

  while(t)
  {
    imgid = GPOINTER_TO_INT(t->data);
    dt_image_remove(imgid);
    t = g_list_delete_link(t, t);
    fraction=1.0/total;
    dt_control_backgroundjobs_progress(darktable.control, jid, fraction);
  }

  char *imgname;
  while(list)
  {
    imgname = (char *)list->data;
    dt_image_synch_all_xmp(imgname);
    list = g_list_delete_link(list, list);
  }
  dt_control_backgroundjobs_destroy(darktable.control, jid);
  dt_film_remove_empty();
  dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED);
  dt_control_queue_redraw_center();
  return 0;
}
Exemple #15
0
void dt_dev_add_history_item(dt_develop_t *dev, dt_iop_module_t *module, gboolean enable)
{
  if(darktable.gui->reset) return;
  dt_pthread_mutex_lock(&dev->history_mutex);
  if(dev->gui_attached)
  {
    GList *history = g_list_nth (dev->history, dev->history_end);
    while(history)
    {
      GList *next = g_list_next(history);
      dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
      // printf("removing obsoleted history item: %s\n", hist->module->op);
      free(hist->params);
      free(hist->blend_params);
      free(history->data);
      dev->history = g_list_delete_link(dev->history, history);
      history = next;
    }
    history = g_list_nth(dev->history, dev->history_end-1);
    if(!history || module->instance != ((dt_dev_history_item_t *)history->data)->module->instance)
    {
      // new operation, push new item
      // printf("adding new history item %d - %s\n", dev->history_end, module->op);
      // if(history) printf("because item %d - %s is different operation.\n", dev->history_end-1, ((dt_dev_history_item_t *)history->data)->module->op);
      dev->history_end++;
      dt_dev_history_item_t *hist = (dt_dev_history_item_t *)malloc(sizeof(dt_dev_history_item_t));
      if (enable)
      {
        module->enabled = TRUE;
        if(module->off)
        {
          darktable.gui->reset = 1;
          gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(module->off), module->enabled);
          darktable.gui->reset = 0;
        }
      }
      hist->enabled = module->enabled;
      hist->module = module;
      hist->params = malloc(module->params_size);

      /* allocate and set hist blend_params */
      hist->blend_params = malloc(sizeof(dt_develop_blend_params_t));
      memset(hist->blend_params, 0, sizeof(dt_develop_blend_params_t));

      memcpy(hist->params, module->params, module->params_size);
      if(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING)
        memcpy(hist->blend_params, module->blend_params, sizeof(dt_develop_blend_params_t));

      dev->history = g_list_append(dev->history, hist);
      dev->pipe->changed |= DT_DEV_PIPE_SYNCH;
      dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH; // topology remains, as modules are fixed for now.
    }
    else
    {
      // same operation, change params
      // printf("changing same history item %d - %s\n", dev->history_end-1, module->op);
      dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data;
      memcpy(hist->params, module->params, module->params_size);

      if(module->flags() & IOP_FLAGS_SUPPORTS_BLENDING)
        memcpy(hist->blend_params, module->blend_params, sizeof(dt_develop_blend_params_t));

      // if the user changed stuff and the module is still not enabled, do it:
      if(!hist->enabled && !module->enabled)
      {
        module->enabled = 1;
        if(module->off)
        {
          darktable.gui->reset = 1;
          gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(module->off), module->enabled);
          darktable.gui->reset = 0;
        }
      }
      hist->enabled = module->enabled;
      dev->pipe->changed |= DT_DEV_PIPE_TOP_CHANGED;
      dev->preview_pipe->changed |= DT_DEV_PIPE_TOP_CHANGED;
    }
  }
#if 0
  {
    // debug:
    printf("remaining %d history items:\n", dev->history_end);
    GList *history = dev->history;
    int i = 0;
    while(history)
    {
      dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
      printf("%d %s\n", i, hist->module->op);
      history = g_list_next(history);
      i++;
    }
  }
#endif

  /* invalidate image data*/
  dt_similarity_image_dirty(dev->image_storage.id);
  
  // invalidate buffers and force redraw of darkroom
  dt_dev_invalidate_all(dev);
  dt_pthread_mutex_unlock(&dev->history_mutex);

  if(dev->gui_attached)
  {
    /* signal that history has changed */
    dt_control_signal_raise(darktable.signals, DT_SIGNAL_DEVELOP_HISTORY_CHANGE);

    /* redraw */
    dt_control_queue_redraw_center();
  }
}
Exemple #16
0
int dt_lua_do_chunk(lua_State *L,int nargs,int nresults)
{
  lua_State * new_thread = lua_newthread(L);
  lua_insert(L,-(nargs+2));
  lua_xmove(L,new_thread,nargs+1);
  int thread_result = lua_resume(new_thread, L,nargs);
  do {
    switch(thread_result) {
      case LUA_OK:
        if(darktable.gui!=NULL)
        {
          dt_lua_unlock(false);
          dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED); // just for good measure
          dt_control_queue_redraw();
          dt_lua_lock();
        }
        if(nresults !=LUA_MULTRET) {
          lua_settop(new_thread,nresults);
        }
        int result= lua_gettop(new_thread);
        lua_pop(L,1);
        lua_xmove(new_thread,L,result);
        return result;
      case LUA_YIELD:
        {
          if(lua_gettop(new_thread) == 0) {
            lua_pushstring(new_thread,"no parameter passed to yield");
            goto error;
          }
          yield_type type;
          lua_pushcfunction(new_thread,protected_to_yield);
          lua_pushvalue(new_thread,1);
          thread_result = lua_pcall(new_thread,1,1,0);
          if(thread_result!= LUA_OK) 
            goto error;
          type = lua_tointeger(new_thread,-1);
          lua_pop(new_thread,1);
          switch(type) {
            case WAIT_MS:
              {
                lua_pushcfunction(new_thread,protected_to_int);
                lua_pushvalue(new_thread,2);
                thread_result = lua_pcall(new_thread,1,1,0);
                if(thread_result!= LUA_OK) 
                  goto error;
                int wait_time = lua_tointeger(new_thread,-1);
                lua_pop(new_thread,1);
                dt_lua_unlock(false);
                g_usleep(wait_time*1000);
                dt_lua_lock();
                thread_result = lua_resume(new_thread,L,0);
                break;
              }
            case FILE_READABLE:
              {
                lua_pushcfunction(new_thread,protected_to_userdata);
                lua_pushvalue(new_thread,2);
                lua_pushstring(new_thread,LUA_FILEHANDLE);
                thread_result = lua_pcall(new_thread,2,1,0);
                if(thread_result!= LUA_OK) 
                  goto error;
                luaL_Stream * stream = lua_touserdata(new_thread,-1);
                lua_pop(new_thread,1);
                int myfileno = fileno(stream->f);
                fd_set fdset;
                FD_ZERO(&fdset);
                FD_SET(myfileno,&fdset);
                dt_lua_unlock(false);
                select(myfileno+1,&fdset,NULL,NULL,0);
                dt_lua_lock();
                thread_result = lua_resume(new_thread,L,0);
                break;
              }
            case RUN_COMMAND:
              {
                lua_pushcfunction(new_thread,protected_to_string);
                lua_pushvalue(new_thread,2);
                thread_result = lua_pcall(new_thread,1,1,0);
                if(thread_result!= LUA_OK) 
                  goto error;
                const char* command = lua_tostring(new_thread,-1);
                lua_pop(L,1);
                dt_lua_unlock(false);
                int result = system(command);
                dt_lua_lock();
                lua_pushinteger(L,result);
                thread_result = lua_resume(new_thread,L,1);
                break;
              }
            default:
              lua_pushstring(new_thread,"program error, shouldn't happen");
              goto error;
          }
          break;
        }
      default:
        goto error;
    }
  }while(true);
error:
  if(darktable.unmuted & DT_DEBUG_LUA) {
    dt_print(DT_DEBUG_LUA,"LUA ERROR : %s", lua_tostring(new_thread,-1));
    luaL_traceback(L,new_thread,"",0);
    const char * error = lua_tostring(L,-1);
    dt_print(DT_DEBUG_LUA,error);
    lua_pop(L,1);
  }
  lua_pop(L,1);
  if(nresults !=LUA_MULTRET)
  {
    for(int i= 0 ; i < nresults; i++)
    {
      lua_pushnil(L);
    }
    return nresults;
  }
  return 0;
}
Exemple #17
0
void dt_dev_read_history(dt_develop_t *dev)
{
  if(!dev->image_storage.id) return;
  sqlite3_stmt *stmt;
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select imgid, num, module, operation, op_params, enabled, blendop_params, blendop_version from history where imgid = ?1 order by num", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dev->image_storage.id);
  dev->history_end = 0;
  while(sqlite3_step(stmt) == SQLITE_ROW)
  {
    // db record:
    // 0-img, 1-num, 2-module_instance, 3-operation char, 4-params blob, 5-enabled, 6-blend_params, 7-blendop_version
    dt_dev_history_item_t *hist = (dt_dev_history_item_t *)malloc(sizeof(dt_dev_history_item_t));
    hist->enabled = sqlite3_column_int(stmt, 5);

    GList *modules = dev->iop;
    const char *opname = (const char *)sqlite3_column_text(stmt, 3);
    if(!opname)
    {
      fprintf(stderr, "[dev_read_history] database history for image `%s' seems to be corrupted!\n", dev->image_storage.filename);
      free(hist);
      continue;
    }
   
    hist->module = NULL;
    while(opname && modules)
    {
      dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
      if(!strcmp(module->op, opname))
      {
        hist->module = module;
        break;
      }
      modules = g_list_next(modules);
    }
    if(!hist->module)
    {
      fprintf(stderr, "[dev_read_history] the module `%s' requested by image `%s' is not installed on this computer!\n", opname, dev->image_storage.filename);
      free(hist);
      continue;
    }
    int modversion = sqlite3_column_int(stmt, 2);
    assert(strcmp((char *)sqlite3_column_text(stmt, 3), hist->module->op) == 0);
    hist->params = malloc(hist->module->params_size);
    hist->blend_params = malloc(sizeof(dt_develop_blend_params_t));
    if(hist->module->version() != modversion || hist->module->params_size != sqlite3_column_bytes(stmt, 4) ||
        strcmp((char *)sqlite3_column_text(stmt, 3), hist->module->op))
    {
      if(!hist->module->legacy_params ||
          hist->module->legacy_params(hist->module, sqlite3_column_blob(stmt, 4), labs(modversion), hist->params, labs(hist->module->version())))
      {
        free(hist->params);
        free(hist->blend_params);
        fprintf(stderr, "[dev_read_history] module `%s' version mismatch: history is %d, dt %d.\n", hist->module->op, modversion, hist->module->version());
        const char *fname = dev->image_storage.filename + strlen(dev->image_storage.filename);
        while(fname > dev->image_storage.filename && *fname != '/') fname --;
        if(fname > dev->image_storage.filename) fname++;
        dt_control_log(_("%s: module `%s' version mismatch: %d != %d"), fname, hist->module->op, hist->module->version(), modversion);
        free(hist);
        continue;
      }
    }
    else
    {
      memcpy(hist->params, sqlite3_column_blob(stmt, 4), hist->module->params_size);
    }

    const void *blendop_params = sqlite3_column_blob(stmt, 6);
    int bl_length = sqlite3_column_bytes(stmt, 6);
    int blendop_version = sqlite3_column_int(stmt, 7);

    if (blendop_params && (blendop_version == dt_develop_blend_version()) && (bl_length == sizeof(dt_develop_blend_params_t)))
    {
      memcpy(hist->blend_params, blendop_params, sizeof(dt_develop_blend_params_t));
    }
    else if (blendop_params && dt_develop_blend_legacy_params(hist->module, blendop_params, blendop_version, hist->blend_params, dt_develop_blend_version(), bl_length) == 0)
    {
      // do nothing
    }
    else
    {
      memcpy(hist->blend_params, hist->module->default_blendop_params, sizeof(dt_develop_blend_params_t));
    }

    // memcpy(hist->module->params, hist->params, hist->module->params_size);
    // hist->module->enabled = hist->enabled;
    // printf("[dev read history] img %d number %d for operation %d - %s params %f %f\n", sqlite3_column_int(stmt, 0), sqlite3_column_int(stmt, 1), instance, hist->module->op, *(float *)hist->params, *(((float*)hist->params)+1));
    dev->history = g_list_append(dev->history, hist);
    dev->history_end ++;
  }

  if(dev->gui_attached)
  {
    dev->pipe->changed |= DT_DEV_PIPE_SYNCH;
    dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH; // again, fixed topology for now.
    dt_dev_invalidate_all(dev);

    /* signal history changed */
    dt_control_signal_raise(darktable.signals,DT_SIGNAL_DEVELOP_HISTORY_CHANGE);
  }
  sqlite3_finalize (stmt);
}
Exemple #18
0
void
dt_mipmap_cache_read_get(
  dt_mipmap_cache_t *cache,
  dt_mipmap_buffer_t *buf,
  const uint32_t imgid,
  const dt_mipmap_size_t mip,
  const dt_mipmap_get_flags_t flags)
{
  const uint32_t key = get_key(imgid, mip);
  if(flags == DT_MIPMAP_TESTLOCK)
  {
    // simple case: only get and lock if it's there.
    struct dt_mipmap_buffer_dsc* dsc = (struct dt_mipmap_buffer_dsc*)dt_cache_read_testget(&cache->mip[mip].cache, key);
    if(dsc)
    {
      buf->width  = dsc->width;
      buf->height = dsc->height;
      buf->imgid  = imgid;
      buf->size   = mip;
      // skip to next 8-byte alignment, for sse buffers.
      buf->buf    = (uint8_t *)(dsc+1);
    }
    else
    {
      // set to NULL if failed.
      buf->width = buf->height = 0;
      buf->imgid = 0;
      buf->size  = DT_MIPMAP_NONE;
      buf->buf   = NULL;
    }
  }
  else if(flags == DT_MIPMAP_PREFETCH)
  {
    // and opposite: prefetch without locking
    if(mip > DT_MIPMAP_FULL || mip < DT_MIPMAP_0) return;
    dt_job_t j;
    dt_image_load_job_init(&j, imgid, mip);
    // if the job already exists, make it high-priority, if not, add it:
    if(dt_control_revive_job(darktable.control, &j) < 0)
      dt_control_add_job(darktable.control, &j);
  }
  else if(flags == DT_MIPMAP_BLOCKING)
  {
    // simple case: blocking get
    struct dt_mipmap_buffer_dsc* dsc = (struct dt_mipmap_buffer_dsc*)dt_cache_read_get(&cache->mip[mip].cache, key);
    if(!dsc)
    {
      // should never happen for anything but full images which have been moved.
      assert(mip == DT_MIPMAP_FULL || mip == DT_MIPMAP_F);
      // fprintf(stderr, "[mipmap cache get] no data in cache for imgid %u size %d!\n", imgid, mip);
      // sorry guys, no image for you :(
      buf->width = buf->height = 0;
      buf->imgid = 0;
      buf->size  = DT_MIPMAP_NONE;
      buf->buf   = NULL;
    }
    else
    {
      // fprintf(stderr, "[mipmap cache get] found data in cache for imgid %u size %d\n", imgid, mip);
      // uninitialized?
      //assert(dsc->flags & DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE || dsc->size == 0);
      if(dsc->flags & DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE)
      {
        __sync_fetch_and_add (&(cache->mip[mip].stats_fetches), 1);
        // fprintf(stderr, "[mipmap cache get] now initializing buffer for img %u mip %d!\n", imgid, mip);
        // we're write locked here, as requested by the alloc callback.
        // now fill it with data:
        if(mip == DT_MIPMAP_FULL)
        {
          // load the image:
          // make sure we access the r/w lock as shortly as possible!
          dt_image_t buffered_image;
          const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid);
          buffered_image = *cimg;
          // dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg);
          // dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED);
          dt_image_cache_read_release(darktable.image_cache, cimg);

          char filename[DT_MAX_PATH_LEN];
          gboolean from_cache = TRUE;
          dt_image_full_path(buffered_image.id, filename, DT_MAX_PATH_LEN, &from_cache);

          dt_mipmap_cache_allocator_t a = (dt_mipmap_cache_allocator_t)&dsc;
          struct dt_mipmap_buffer_dsc* prvdsc = dsc;
          dt_imageio_retval_t ret = dt_imageio_open(&buffered_image, filename, a);
          if(dsc != prvdsc)
          {
            // fprintf(stderr, "[mipmap cache] realloc %p\n", data);
            // write back to cache, too.
            // in case something went wrong, still keep the buffer and return it to the hashtable
            // so we don't produce mem leaks or unnecessary mem fragmentation.
            dt_cache_realloc(&cache->mip[mip].cache, key, 1, (void*)dsc);
          }
          if(ret != DT_IMAGEIO_OK)
          {
            // fprintf(stderr, "[mipmap read get] error loading image: %d\n", ret);
            //
            // we can only return a zero dimension buffer if the buffer has been allocated.
            // in case dsc couldn't be allocated and points to the static buffer, it contains
            // a dead image already.
            if((void *)dsc != (void *)dt_mipmap_cache_static_dead_image) dsc->width = dsc->height = 0;
          }
          else
          {
            // swap back new image data:
            cimg = dt_image_cache_read_get(darktable.image_cache, imgid);
            dt_image_t *img = dt_image_cache_write_get(darktable.image_cache, cimg);
            *img = buffered_image;
            // fprintf(stderr, "[mipmap read get] initializing full buffer img %u with %u %u -> %d %d (%p)\n", imgid, data[0], data[1], img->width, img->height, data);
            // don't write xmp for this (we only changed db stuff):
            dt_image_cache_write_release(darktable.image_cache, img, DT_IMAGE_CACHE_RELAXED);
            dt_image_cache_read_release(darktable.image_cache, img);
          }
        }
        else if(mip == DT_MIPMAP_F)
        {
          _init_f((float *)(dsc+1), &dsc->width, &dsc->height, imgid);
        }
        else
        {
          // 8-bit thumbs, possibly need to be compressed:
          if(cache->compression_type)
          {
            // get per-thread temporary storage without malloc from a separate cache:
            const int key = dt_control_get_threadid();
            // const void *cbuf =
            dt_cache_read_get(&cache->scratchmem.cache, key);
            uint8_t *scratchmem = (uint8_t *)dt_cache_write_get(&cache->scratchmem.cache, key);
            _init_8(scratchmem, &dsc->width, &dsc->height, imgid, mip);
            buf->width  = dsc->width;
            buf->height = dsc->height;
            buf->imgid  = imgid;
            buf->size   = mip;
            buf->buf = (uint8_t *)(dsc+1);
            dt_mipmap_cache_compress(buf, scratchmem);
            dt_cache_write_release(&cache->scratchmem.cache, key);
            dt_cache_read_release(&cache->scratchmem.cache, key);
          }
          else
          {
            _init_8((uint8_t *)(dsc+1), &dsc->width, &dsc->height, imgid, mip);
          }
        }
        dsc->flags &= ~DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE;
        // drop the write lock
        dt_cache_write_release(&cache->mip[mip].cache, key);
        /* raise signal that mipmaps has been flushed to cache */
        dt_control_signal_raise(darktable.signals, DT_SIGNAL_DEVELOP_MIPMAP_UPDATED);
      }
      buf->width  = dsc->width;
      buf->height = dsc->height;
      buf->imgid  = imgid;
      buf->size   = mip;
      buf->buf = (uint8_t *)(dsc+1);
      if(dsc->width == 0 || dsc->height == 0)
      {
        // fprintf(stderr, "[mipmap cache get] got a zero-sized image for img %u mip %d!\n", imgid, mip);
        if(mip < DT_MIPMAP_F)       dead_image_8(buf);
        else if(mip == DT_MIPMAP_F) dead_image_f(buf);
        else buf->buf = NULL; // full images with NULL buffer have to be handled, indicates `missing image'
      }
    }
  }
  else if(flags == DT_MIPMAP_BEST_EFFORT)
  {
    __sync_fetch_and_add (&(cache->mip[mip].stats_requests), 1);
    // best-effort, might also return NULL.
    // never decrease mip level for float buffer or full image:
    dt_mipmap_size_t min_mip = (mip >= DT_MIPMAP_F) ? mip : DT_MIPMAP_0;
    for(int k=mip; k>=min_mip && k>=0; k--)
    {
      // already loaded?
      dt_mipmap_cache_read_get(cache, buf, imgid, k, DT_MIPMAP_TESTLOCK);
      if(buf->buf && buf->width > 0 && buf->height > 0)
      {
        if(mip != k) __sync_fetch_and_add (&(cache->mip[k].stats_standin), 1);
        return;
      }
      // didn't succeed the first time? prefetch for later!
      if(mip == k)
      {
        __sync_fetch_and_add (&(cache->mip[mip].stats_near_match), 1);
        dt_mipmap_cache_read_get(cache, buf, imgid, mip, DT_MIPMAP_PREFETCH);
      }
    }
    __sync_fetch_and_add (&(cache->mip[mip].stats_misses), 1);
    // fprintf(stderr, "[mipmap cache get] image not found in cache: imgid %u mip %d!\n", imgid, mip);
    // nothing found :(
    buf->buf   = NULL;
    buf->imgid = 0;
    buf->size  = DT_MIPMAP_NONE;
    buf->width = buf->height = 0;
  }
}
Exemple #19
0
gboolean dt_styles_create_from_image(const char *name, const char *description, int32_t imgid, GList *filter)
{
    int id = 0;
    sqlite3_stmt *stmt;

    /* first create the style header */
    if(!dt_styles_create_style_header(name, description)) return FALSE;

    if((id = dt_styles_get_id_by_name(name)) != 0)
    {
        /* create the style_items from source image history stack */
        if(filter)
        {
            GList *list = filter;
            char tmp[64];
            char include[2048] = { 0 };
            g_strlcat(include, "num IN (", sizeof(include));
            do
            {
                if(list != g_list_first(list)) g_strlcat(include, ",", sizeof(include));
                snprintf(tmp, sizeof(tmp), "%d", GPOINTER_TO_INT(list->data));
                g_strlcat(include, tmp, sizeof(include));
            } while((list = g_list_next(list)));
            g_strlcat(include, ")", sizeof(include));
            char query[4096] = { 0 };
            snprintf(query, sizeof(query), "INSERT INTO data.style_items "
                     "(styleid,num,module,operation,op_params,enabled,blendop_params,blendop_"
                     "version,multi_priority,multi_name) SELECT ?1, "
                     "num,module,operation,op_params,enabled,blendop_params,blendop_version,"
                     "multi_priority,multi_name FROM main.history WHERE imgid=?2 AND %s",
                     include);
            DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
        }
        else
            DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                                        "INSERT INTO data.style_items "
                                        "(styleid,num,module,operation,op_params,enabled,blendop_params,blendop_"
                                        "version,multi_priority,multi_name) SELECT ?1, "
                                        "num,module,operation,op_params,enabled,blendop_params,blendop_version,"
                                        "multi_priority,multi_name FROM main.history WHERE imgid=?2",
                                        -1, &stmt, NULL);
        DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
        DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
        sqlite3_step(stmt);
        sqlite3_finalize(stmt);

        _dt_style_cleanup_multi_instance(id);

        /* backup style to disk */
        char stylesdir[PATH_MAX] = { 0 };
        dt_loc_get_user_config_dir(stylesdir, sizeof(stylesdir));
        g_strlcat(stylesdir, "/styles", sizeof(stylesdir));
        g_mkdir_with_parents(stylesdir, 00755);

        dt_styles_save_to_file(name, stylesdir, FALSE);

        char tmp_accel[1024];
        gchar *tmp_name = g_strdup(name); // freed by _destroy_style_shortcut_callback
        snprintf(tmp_accel, sizeof(tmp_accel), C_("accel", "styles/apply %s"), name);
        dt_accel_register_global(tmp_accel, 0, 0);
        GClosure *closure;
        closure = g_cclosure_new(G_CALLBACK(_apply_style_shortcut_callback), tmp_name,
                                 _destroy_style_shortcut_callback);
        dt_accel_connect_global(tmp_accel, closure);
        dt_control_signal_raise(darktable.signals, DT_SIGNAL_STYLE_CHANGED);
        return TRUE;
    }
    return FALSE;
}
Exemple #20
0
// internal function: to avoid exif blob reading + 8-bit byteorder flag + high-quality override
int dt_imageio_export_with_flags(const uint32_t imgid, const char *filename,
                                 dt_imageio_module_format_t *format, dt_imageio_module_data_t *format_params,
                                 const int32_t ignore_exif, const int32_t display_byteorder,
                                 const gboolean high_quality, const gboolean upscale, const int32_t thumbnail_export,
                                 const char *filter, const gboolean copy_metadata,
                                 dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename,
                                 dt_iop_color_intent_t icc_intent,
                                 dt_imageio_module_storage_t *storage,
                                 dt_imageio_module_data_t *storage_params, int num, int total)
{
  dt_develop_t dev;
  dt_dev_init(&dev, 0);
  dt_dev_load_image(&dev, imgid);

  const int buf_is_downscaled
      = (thumbnail_export && dt_conf_get_bool("plugins/lighttable/low_quality_thumbnails"));

  dt_mipmap_buffer_t buf;
  if(buf_is_downscaled)
    dt_mipmap_cache_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_F, DT_MIPMAP_BLOCKING, 'r');
  else
    dt_mipmap_cache_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING, 'r');

  const dt_image_t *img = &dev.image_storage;

  if(!buf.buf || !buf.width || !buf.height)
  {
    fprintf(stderr, "allocation failed???\n");
    dt_control_log(_("image `%s' is not available!"), img->filename);
    goto error_early;
  }

  const int wd = img->width;
  const int ht = img->height;
  const float max_scale = upscale ? 100.0 : 1.0;

  int res = 0;

  dt_times_t start;
  dt_get_times(&start);
  dt_dev_pixelpipe_t pipe;
  res = thumbnail_export ? dt_dev_pixelpipe_init_thumbnail(&pipe, wd, ht)
                         : dt_dev_pixelpipe_init_export(&pipe, wd, ht, format->levels(format_params));
  if(!res)
  {
    dt_control_log(
        _("failed to allocate memory for %s, please lower the threads used for export or buy more memory."),
        thumbnail_export ? C_("noun", "thumbnail export") : C_("noun", "export"));
    goto error;
  }

  //  If a style is to be applied during export, add the iop params into the history
  if(!thumbnail_export && format_params->style[0] != '\0')
  {
    GList *style_items = dt_styles_get_item_list(format_params->style, TRUE, -1);
    if(!style_items)
    {
      dt_control_log(_("cannot find the style '%s' to apply during export."), format_params->style);
      goto error;
    }

    // remove everything above history_end
    GList *history = g_list_nth(dev.history, dev.history_end);
    while(history)
    {
      GList *next = g_list_next(history);
      dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
      free(hist->params);
      free(hist->blend_params);
      free(history->data);
      dev.history = g_list_delete_link(dev.history, history);
      history = next;
    }

    // Add each params
    for(GList *iter = style_items; iter; iter = g_list_next(iter))
    {
      dt_style_item_t *s = (dt_style_item_t *)iter->data;

      for(GList *module = dev.iop; module; module = g_list_next(module))
      {
        dt_iop_module_t *m = (dt_iop_module_t *)module->data;

        if(!strcmp(m->op, s->operation))
        {
          dt_dev_history_item_t *h = malloc(sizeof(dt_dev_history_item_t));
          dt_iop_module_t *style_module = m;

          if((format_params->style_append && !(m->flags() & IOP_FLAGS_ONE_INSTANCE)) || m->multi_priority != s->multi_priority)
          {
            // dt_dev_module_duplicate() doesn't work here, it's trying too hard to be clever
            style_module = (dt_iop_module_t *)calloc(1, sizeof(dt_iop_module_t));
            if(style_module && !dt_iop_load_module(style_module, m->so, m->dev))
            {
              style_module->instance = m->instance;
              style_module->multi_priority = s->multi_priority;
              snprintf(style_module->multi_name, sizeof(style_module->multi_name), "%s", s->name);
              dev.iop = g_list_insert_sorted(dev.iop, style_module, sort_plugins);
            }
            else
            {
              free(h);
              goto error;
            }
          }

          h->params = s->params;
          h->blend_params = s->blendop_params;
          h->enabled = s->enabled;
          h->module = style_module;
          h->multi_priority = s->multi_priority;
          g_strlcpy(h->multi_name, s->name, sizeof(h->multi_name));

          if(m->legacy_params && (s->module_version != m->version()))
          {
            void *new_params = malloc(m->params_size);
            m->legacy_params(m, h->params, s->module_version, new_params, labs(m->version()));

            free(h->params);
            h->params = new_params;
          }

          dev.history_end++;
          dev.history = g_list_append(dev.history, h);

          // make sure that dt_style_item_free doesn't free data we still use
          s->params = NULL;
          s->blendop_params = NULL;

          break;
        }
      }
    }
    g_list_free_full(style_items, dt_style_item_free);
  }

  dt_dev_pixelpipe_set_icc(&pipe, icc_type, icc_filename, icc_intent);
  dt_dev_pixelpipe_set_input(&pipe, &dev, (float *)buf.buf, buf.width, buf.height, buf.iscale);
  dt_dev_pixelpipe_create_nodes(&pipe, &dev);
  dt_dev_pixelpipe_synch_all(&pipe, &dev);

  if(filter)
  {
    if(!strncmp(filter, "pre:", 4)) dt_dev_pixelpipe_disable_after(&pipe, filter + 4);
    if(!strncmp(filter, "post:", 5)) dt_dev_pixelpipe_disable_before(&pipe, filter + 5);
  }

  dt_dev_pixelpipe_get_dimensions(&pipe, &dev, pipe.iwidth, pipe.iheight, &pipe.processed_width,
                                  &pipe.processed_height);

  dt_show_times(&start, "[export] creating pixelpipe", NULL);

  // find output color profile for this image:
  int sRGB = 1;
  if(icc_type == DT_COLORSPACE_SRGB)
  {
    sRGB = 1;
  }
  else if(icc_type == DT_COLORSPACE_NONE)
  {
    GList *modules = dev.iop;
    dt_iop_module_t *colorout = NULL;
    while(modules)
    {
      colorout = (dt_iop_module_t *)modules->data;
      if(colorout->get_p && strcmp(colorout->op, "colorout") == 0)
      {
        const dt_colorspaces_color_profile_type_t *type = colorout->get_p(colorout->params, "type");
        sRGB = (!type || *type == DT_COLORSPACE_SRGB);
        break; // colorout can't have > 1 instance
      }
      modules = g_list_next(modules);
    }
  }
  else
  {
    sRGB = 0;
  }

  // get only once at the beginning, in case the user changes it on the way:
  const gboolean high_quality_processing
      = ((format_params->max_width == 0 || format_params->max_width >= pipe.processed_width)
         && (format_params->max_height == 0 || format_params->max_height >= pipe.processed_height))
            ? FALSE
            : high_quality;

  const int width = format_params->max_width;
  const int height = format_params->max_height;
  const double scalex = width > 0 ? fminf(width / (double)pipe.processed_width, max_scale) : 1.0;
  const double scaley = height > 0 ? fminf(height / (double)pipe.processed_height, max_scale) : 1.0;
  const double scale = fminf(scalex, scaley);

  const int processed_width = scale * pipe.processed_width + .5f;
  const int processed_height = scale * pipe.processed_height + .5f;

  const int bpp = format->bpp(format_params);

  dt_get_times(&start);
  if(high_quality_processing)
  {
    /*
     * if high quality processing was requested, downsampling will be done
     * at the very end of the pipe (just before border and watermark)
     */
    dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, processed_width, processed_height, scale);
  }
  else
  {
    // else, downsampling will be right after demosaic

    // so we need to turn temporarily disable in-pipe late downsampling iop.

    // find the finalscale module
    dt_dev_pixelpipe_iop_t *finalscale = NULL;
    {
      GList *nodes = g_list_last(pipe.nodes);
      while(nodes)
      {
        dt_dev_pixelpipe_iop_t *node = (dt_dev_pixelpipe_iop_t *)(nodes->data);
        if(!strcmp(node->module->op, "finalscale"))
        {
          finalscale = node;
          break;
        }
        nodes = g_list_previous(nodes);
      }
    }

    if(finalscale) finalscale->enabled = 0;

    // do the processing (8-bit with special treatment, to make sure we can use openmp further down):
    if(bpp == 8)
      dt_dev_pixelpipe_process(&pipe, &dev, 0, 0, processed_width, processed_height, scale);
    else
      dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, processed_width, processed_height, scale);

    if(finalscale) finalscale->enabled = 1;
  }
  dt_show_times(&start, thumbnail_export ? "[dev_process_thumbnail] pixel pipeline processing"
                                         : "[dev_process_export] pixel pipeline processing",
                NULL);

  uint8_t *outbuf = pipe.backbuf;

  // downconversion to low-precision formats:
  if(bpp == 8)
  {
    if(display_byteorder)
    {
      if(high_quality_processing)
      {
        const float *const inbuf = (float *)outbuf;
        for(size_t k = 0; k < (size_t)processed_width * processed_height; k++)
        {
          // convert in place, this is unfortunately very serial..
          const uint8_t r = CLAMP(inbuf[4 * k + 2] * 0xff, 0, 0xff);
          const uint8_t g = CLAMP(inbuf[4 * k + 1] * 0xff, 0, 0xff);
          const uint8_t b = CLAMP(inbuf[4 * k + 0] * 0xff, 0, 0xff);
          outbuf[4 * k + 0] = r;
          outbuf[4 * k + 1] = g;
          outbuf[4 * k + 2] = b;
        }
      }
      // else processing output was 8-bit already, and no need to swap order
    }
    else // need to flip
    {
      // ldr output: char
      if(high_quality_processing)
      {
        const float *const inbuf = (float *)outbuf;
        for(size_t k = 0; k < (size_t)processed_width * processed_height; k++)
        {
          // convert in place, this is unfortunately very serial..
          const uint8_t r = CLAMP(inbuf[4 * k + 0] * 0xff, 0, 0xff);
          const uint8_t g = CLAMP(inbuf[4 * k + 1] * 0xff, 0, 0xff);
          const uint8_t b = CLAMP(inbuf[4 * k + 2] * 0xff, 0, 0xff);
          outbuf[4 * k + 0] = r;
          outbuf[4 * k + 1] = g;
          outbuf[4 * k + 2] = b;
        }
      }
      else
      { // !display_byteorder, need to swap:
        uint8_t *const buf8 = pipe.backbuf;
#ifdef _OPENMP
#pragma omp parallel for default(none) schedule(static)
#endif
        // just flip byte order
        for(size_t k = 0; k < (size_t)processed_width * processed_height; k++)
        {
          uint8_t tmp = buf8[4 * k + 0];
          buf8[4 * k + 0] = buf8[4 * k + 2];
          buf8[4 * k + 2] = tmp;
        }
      }
    }
  }
  else if(bpp == 16)
  {
    // uint16_t per color channel
    float *buff = (float *)outbuf;
    uint16_t *buf16 = (uint16_t *)outbuf;
    for(int y = 0; y < processed_height; y++)
      for(int x = 0; x < processed_width; x++)
      {
        // convert in place
        const size_t k = (size_t)processed_width * y + x;
        for(int i = 0; i < 3; i++) buf16[4 * k + i] = CLAMP(buff[4 * k + i] * 0x10000, 0, 0xffff);
      }
  }
  // else output float, no further harm done to the pixels :)

  format_params->width = processed_width;
  format_params->height = processed_height;

  if(!ignore_exif)
  {
    int length;
    uint8_t *exif_profile = NULL; // Exif data should be 65536 bytes max, but if original size is close to that,
                                  // adding new tags could make it go over that... so let it be and see what
                                  // happens when we write the image
    char pathname[PATH_MAX] = { 0 };
    gboolean from_cache = TRUE;
    dt_image_full_path(imgid, pathname, sizeof(pathname), &from_cache);
    // last param is dng mode, it's false here
    length = dt_exif_read_blob(&exif_profile, pathname, imgid, sRGB, processed_width, processed_height, 0);

    res = format->write_image(format_params, filename, outbuf, icc_type, icc_filename, exif_profile, length, imgid,
                              num, total);

    free(exif_profile);
  }
  else
  {
    res = format->write_image(format_params, filename, outbuf, icc_type, icc_filename, NULL, 0, imgid, num, total);
  }

  dt_dev_pixelpipe_cleanup(&pipe);
  dt_dev_cleanup(&dev);
  dt_mipmap_cache_release(darktable.mipmap_cache, &buf);

  /* now write xmp into that container, if possible */
  if(copy_metadata && (format->flags(format_params) & FORMAT_FLAGS_SUPPORT_XMP))
  {
    dt_exif_xmp_attach(imgid, filename);
    // no need to cancel the export if this fail
  }

  if(!thumbnail_export && strcmp(format->mime(format_params), "memory")
    && !(format->flags(format_params) & FORMAT_FLAGS_NO_TMPFILE))
  {
#ifdef USE_LUA
    //Synchronous calling of lua intermediate-export-image events
    dt_lua_lock();

    lua_State *L = darktable.lua_state.state;

    luaA_push(L, dt_lua_image_t, &imgid);

    lua_pushstring(L, filename);

    luaA_push_type(L, format->parameter_lua_type, format_params);
 
    if (storage)
      luaA_push_type(L, storage->parameter_lua_type, storage_params);
    else
      lua_pushnil(L);

    dt_lua_event_trigger(L, "intermediate-export-image", 4);
  
    dt_lua_unlock();
#endif

    dt_control_signal_raise(darktable.signals, DT_SIGNAL_IMAGE_EXPORT_TMPFILE, imgid, filename, format,
                            format_params, storage, storage_params);
  }

  return res;

error:
  dt_dev_pixelpipe_cleanup(&pipe);
error_early:
  dt_dev_cleanup(&dev);
  dt_mipmap_cache_release(darktable.mipmap_cache, &buf);
  return 1;
}
Exemple #21
0
void dt_similarity_match_image(uint32_t imgid,dt_similarity_t *data)
{
  sqlite3_stmt *stmt;
  gboolean all_ok_for_match = TRUE;
  dt_similarity_histogram_t orginal_histogram,test_histogram;
  dt_similarity_lightmap_t orginal_lightmap,test_lightmap;
 
  /* create temporary mem table for matches */
  DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "create temporary table if not exists similar_images (id integer,score real)", NULL, NULL, NULL);
  DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "delete from similar_images", NULL, NULL, NULL);
  
  /* 
   * get the histogram and lightmap data for image to match against 
   */
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select histogram,lightmap from images where id = ?1", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
  if (sqlite3_step(stmt) == SQLITE_ROW)
  {
    /* get the histogram data */
    uint32_t size = sqlite3_column_bytes(stmt,0);
    if (size!=sizeof(dt_similarity_histogram_t)) 
    {
      all_ok_for_match = FALSE;
      dt_control_log(_("this image has not been indexed yet."));
    } 
    else 
      memcpy(&orginal_histogram, sqlite3_column_blob(stmt, 0), sizeof(dt_similarity_histogram_t));

    /* get the lightmap data */
    size = sqlite3_column_bytes(stmt,1);
    if (size!=sizeof(dt_similarity_lightmap_t)) 
    {
      all_ok_for_match = FALSE;
      dt_control_log(_("this image has not been indexed yet."));
    } 
    else 
      memcpy(&orginal_lightmap, sqlite3_column_blob(stmt, 1), sizeof(dt_similarity_lightmap_t));
    
  }
  else
  {
    all_ok_for_match = FALSE;
    dt_control_log(_("this image has not been indexed yet."));
  }
  sqlite3_reset(stmt);
  sqlite3_clear_bindings(stmt);
  
  
  /*
   * if all ok lets begin matching
   */
  if (all_ok_for_match) 
  {
    char query[4096]={0};

    /* add target image with 100.0 in score into result to ensure it always shown in top */
    sprintf(query,"insert into similar_images(id,score) values(%d,%f)",imgid,100.0);
    DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL);
    

    /* set an extended collection query for viewing the result of match */
    dt_collection_set_extended_where(darktable.collection, ", similar_images where images.id = similar_images.id order by similar_images.score desc");
    dt_collection_set_query_flags( darktable.collection, 
            dt_collection_get_query_flags(darktable.collection) | COLLECTION_QUERY_USE_ONLY_WHERE_EXT);
    dt_collection_update(darktable.collection);
    dt_control_signal_raise(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED);  

        
    /* loop thru images and generate score table */
    DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select id,histogram,lightmap from images", -1, &stmt, NULL);
    while (sqlite3_step(stmt) == SQLITE_ROW)
    {
      float score_histogram=0, score_lightmap=0, score_colormap=0;
      
      /* verify size of histogram and lightmap blob of test image */
      if ( 
                  (sqlite3_column_bytes(stmt,1) == sizeof(dt_similarity_histogram_t)) && 
                  (sqlite3_column_bytes(stmt,2) == sizeof(dt_similarity_lightmap_t))
      )
      {
        /*
         * Get the histogram blob and calculate the similarity score
         */
        memcpy(&test_histogram, sqlite3_column_blob(stmt, 1), sizeof(dt_similarity_histogram_t));
        score_histogram = _similarity_match_histogram_rgb(data, &orginal_histogram, &test_histogram);
         
        /*
         * Get the lightmap blob and calculate the similarity score
         *  1.08 is a tuned constant that works well with threshold
         */
        memcpy(&test_lightmap, sqlite3_column_blob(stmt, 2), sizeof(dt_similarity_lightmap_t));
        score_lightmap = _similarity_match_lightmap(data, &orginal_lightmap, &test_lightmap);
        
        /*
         * then calculate the colormap similarity score
         */
        score_colormap = _similarity_match_colormap(data, &orginal_lightmap, &test_lightmap);
       
        
        /* 
         * calculate the similarity score
         */
	float score =  (pow(score_histogram, data->histogram_weight) *
			     pow(score_lightmap, data->lightmap_weight) *
			     pow(score_colormap, data->redmap_weight));
       
        // fprintf(stderr,"score: %f, histo: %f, light: %f, color: %f\n",score,score_histogram,score_lightmap,score_colormap);

        
        /* 
         * If current images scored, lets add it to similar_images table 
         */
        if(score >= 0.92)
        {
          sprintf(query,"insert into similar_images(id,score) values(%d,%f)",sqlite3_column_int(stmt, 0),score);
          DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL);
                    
          /* lets redraw the view */
          dt_control_queue_redraw_center();
        }
      } else
        fprintf(stderr,"Image has inconsisten similarity matching data..\n");
    }
  }
  sqlite3_finalize (stmt);  

}
Exemple #22
0
void
dt_collection_update_query(const dt_collection_t *collection)
{
  char query[1024], confname[200];
  gchar *complete_query = NULL;

  const int _n_r = dt_conf_get_int("plugins/lighttable/collect/num_rules");
  const int num_rules = CLAMP(_n_r, 1, 10);
  char *conj[] = {"and", "or", "and not"};

  complete_query = dt_util_dstrcat(complete_query, "(");

  for(int i=0; i<num_rules; i++)
  {
    snprintf(confname, sizeof(confname), "plugins/lighttable/collect/item%1d", i);
    const int property = dt_conf_get_int(confname);
    snprintf(confname, sizeof(confname), "plugins/lighttable/collect/string%1d", i);
    gchar *text = dt_conf_get_string(confname);
    if(!text) break;
    snprintf(confname, sizeof(confname), "plugins/lighttable/collect/mode%1d", i);
    const int mode = dt_conf_get_int(confname);
    gchar *escaped_text = dt_util_str_replace(text, "'", "''");

    get_query_string(property, escaped_text, query, sizeof(query));

    if(i > 0)
      complete_query = dt_util_dstrcat(complete_query, " %s %s", conj[mode], query);
    else
      complete_query = dt_util_dstrcat(complete_query, "%s", query);

    g_free(escaped_text);
    g_free(text);
  }

  complete_query = dt_util_dstrcat(complete_query, ")");

  // printf("complete query: `%s'\n", complete_query);

  /* set the extended where and the use of it in the query */
  dt_collection_set_extended_where (collection, complete_query);
  dt_collection_set_query_flags (collection, (dt_collection_get_query_flags (collection) | COLLECTION_QUERY_USE_WHERE_EXT));

  /* remove film id from default filter */
  dt_collection_set_filter_flags (collection, (dt_collection_get_filter_flags (collection) & ~COLLECTION_FILTER_FILM_ID));

  /* update query and at last the visual */
  dt_collection_update (collection);

  /* free string */
  g_free(complete_query);

  // remove from selected images where not in this query.
  sqlite3_stmt *stmt = NULL;
  const gchar *cquery = dt_collection_get_query(collection);
  complete_query = NULL;
  if(cquery && cquery[0] != '\0')
  {
    complete_query = dt_util_dstrcat(complete_query, "delete from selected_images where imgid not in (%s)", cquery);
    DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), complete_query, -1, &stmt, NULL);
    DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, 0);
    DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, -1);
    sqlite3_step(stmt);
    sqlite3_finalize(stmt);

    /* free allocated strings */
    g_free(complete_query);
  }


  /* raise signal of collection change, only if this is an original */
  if (!collection->clone)
    dt_control_signal_raise(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED);

}
Exemple #23
0
// internal function: to avoid exif blob reading + 8-bit byteorder flag + high-quality override
int dt_imageio_export_with_flags(
  const uint32_t              imgid,
  const char                 *filename,
  dt_imageio_module_format_t *format,
  dt_imageio_module_data_t   *format_params,
  const int32_t               ignore_exif,
  const int32_t               display_byteorder,
  const gboolean              high_quality,
  const int32_t               thumbnail_export,
  const char                 *filter,
  const gboolean              copy_metadata,
  dt_imageio_module_storage_t *storage,
  dt_imageio_module_data_t   *storage_params)
{
  dt_develop_t dev;
  dt_dev_init(&dev, 0);
  dt_mipmap_buffer_t buf;
  if(thumbnail_export && dt_conf_get_bool("plugins/lighttable/low_quality_thumbnails"))
    dt_mipmap_cache_read_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_F, DT_MIPMAP_BLOCKING);
  else
    dt_mipmap_cache_read_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING);
  dt_dev_load_image(&dev, imgid);
  const dt_image_t *img = &dev.image_storage;
  const int wd = img->width;
  const int ht = img->height;

  int res = 0;

  dt_times_t start;
  dt_get_times(&start);
  dt_dev_pixelpipe_t pipe;
  res = thumbnail_export ? dt_dev_pixelpipe_init_thumbnail(&pipe, wd, ht) : dt_dev_pixelpipe_init_export(&pipe, wd, ht, format->levels(format_params));
  if(!res)
  {
    dt_control_log(_("failed to allocate memory for %s, please lower the threads used for export or buy more memory."), thumbnail_export ? C_("noun", "thumbnail export") : C_("noun", "export"));
    dt_dev_cleanup(&dev);
    dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf);
    return 1;
  }

  if(!buf.buf)
  {
    dt_control_log(_("image `%s' is not available!"), img->filename);
    dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf);
    dt_dev_cleanup(&dev);
    return 1;
  }

  //  If a style is to be applied during export, add the iop params into the history
  if (!thumbnail_export && format_params->style[0] != '\0')
  {
    GList *stls;

    GList *modules = dev.iop;
    dt_iop_module_t *m = NULL;

    if ((stls=dt_styles_get_item_list(format_params->style, TRUE, -1)) == 0)
    {
      dt_control_log(_("cannot find the style '%s' to apply during export."), format_params->style);
      dt_dev_cleanup(&dev);
      dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf);
      return 1;
    }

    //  Add each params
    while (stls)
    {
      dt_style_item_t *s = (dt_style_item_t *) stls->data;

      modules = dev.iop;
      while (modules)
      {
        m = (dt_iop_module_t *)modules->data;

        //  since the name in the style is returned with a possible multi-name, just check the start of the name
        if (strncmp(m->op, s->name, strlen(m->op)) == 0)
        {
          dt_dev_history_item_t *h = malloc(sizeof(dt_dev_history_item_t));

          h->params = s->params;
          h->blend_params = s->blendop_params;
          h->enabled = s->enabled;
          h->module = m;
          h->multi_priority = 1;
          g_strlcpy(h->multi_name, "", sizeof(h->multi_name));

          if(m->legacy_params && (s->module_version != m->version()))
          {
            void *new_params = malloc(m->params_size);
            m->legacy_params (m, h->params, s->module_version, new_params, labs(m->version()));

            free (h->params);
            h->params = new_params;
          }

          dev.history_end++;
          dev.history = g_list_append(dev.history, h);
          break;
        }
        modules = g_list_next(modules);
      }
      stls = g_list_next(stls);
    }
  }

  dt_dev_pixelpipe_set_input(&pipe, &dev, (float *)buf.buf, buf.width, buf.height, 1.0);
  dt_dev_pixelpipe_create_nodes(&pipe, &dev);
  dt_dev_pixelpipe_synch_all(&pipe, &dev);
  dt_dev_pixelpipe_get_dimensions(&pipe, &dev, pipe.iwidth, pipe.iheight, &pipe.processed_width, &pipe.processed_height);
  if(filter)
  {
    if(!strncmp(filter, "pre:", 4))
      dt_dev_pixelpipe_disable_after(&pipe, filter+4);
    if(!strncmp(filter, "post:", 5))
      dt_dev_pixelpipe_disable_before(&pipe, filter+5);
  }
  dt_show_times(&start, "[export] creating pixelpipe", NULL);

  // find output color profile for this image:
  int sRGB = 1;
  gchar *overprofile = dt_conf_get_string("plugins/lighttable/export/iccprofile");
  if(overprofile && !strcmp(overprofile, "sRGB"))
  {
    sRGB = 1;
  }
  else if(!overprofile || !strcmp(overprofile, "image"))
  {
    GList *modules = dev.iop;
    dt_iop_module_t *colorout = NULL;
    while (modules)
    {
      colorout = (dt_iop_module_t *)modules->data;
      if(colorout->get_p && strcmp(colorout->op, "colorout") == 0)
      {
        const char *iccprofile = colorout->get_p(colorout->params, "iccprofile");
        if(!strcmp(iccprofile, "sRGB")) sRGB = 1;
        else sRGB = 0;
      }
      modules = g_list_next(modules);
    }
  }
  else
  {
    sRGB = 0;
  }
  g_free(overprofile);

  // get only once at the beginning, in case the user changes it on the way:
  const gboolean high_quality_processing = ((format_params->max_width  == 0 || format_params->max_width  >= pipe.processed_width ) &&
      (format_params->max_height == 0 || format_params->max_height >= pipe.processed_height)) ? FALSE :
      high_quality;
  const int width  = high_quality_processing ? 0 : format_params->max_width;
  const int height = high_quality_processing ? 0 : format_params->max_height;
  const double scalex = width  > 0 ? fminf(width /(double)pipe.processed_width,  1.0) : 1.0;
  const double scaley = height > 0 ? fminf(height/(double)pipe.processed_height, 1.0) : 1.0;
  const double scale = fminf(scalex, scaley);
  int processed_width  = scale*pipe.processed_width  + .5f;
  int processed_height = scale*pipe.processed_height + .5f;
  const int bpp = format->bpp(format_params);

  // downsampling done last, if high quality processing was requested:
  uint8_t *outbuf = pipe.backbuf;
  uint8_t *moutbuf = NULL; // keep track of alloc'ed memory
  dt_get_times(&start);
  if(high_quality_processing)
  {
    dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, processed_width, processed_height, scale);
    const double scalex = format_params->max_width  > 0 ? fminf(format_params->max_width /(double)pipe.processed_width,  1.0) : 1.0;
    const double scaley = format_params->max_height > 0 ? fminf(format_params->max_height/(double)pipe.processed_height, 1.0) : 1.0;
    const double scale = fminf(scalex, scaley);
    processed_width  = scale*pipe.processed_width  + .5f;
    processed_height = scale*pipe.processed_height + .5f;
    moutbuf = (uint8_t *)dt_alloc_align(64, (size_t)sizeof(float)*processed_width*processed_height*4);
    outbuf = moutbuf;
    // now downscale into the new buffer:
    dt_iop_roi_t roi_in, roi_out;
    roi_in.x = roi_in.y = roi_out.x = roi_out.y = 0;
    roi_in.scale = 1.0;
    roi_out.scale = scale;
    roi_in.width = pipe.processed_width;
    roi_in.height = pipe.processed_height;
    roi_out.width = processed_width;
    roi_out.height = processed_height;
    dt_iop_clip_and_zoom((float *)outbuf, (float *)pipe.backbuf, &roi_out, &roi_in, processed_width, pipe.processed_width);
  }
  else
  {
    // do the processing (8-bit with special treatment, to make sure we can use openmp further down):
    if(bpp == 8)
      dt_dev_pixelpipe_process(&pipe, &dev, 0, 0, processed_width, processed_height, scale);
    else
      dt_dev_pixelpipe_process_no_gamma(&pipe, &dev, 0, 0, processed_width, processed_height, scale);
    outbuf = pipe.backbuf;
  }
  dt_show_times(&start, thumbnail_export ? "[dev_process_thumbnail] pixel pipeline processing" : "[dev_process_export] pixel pipeline processing", NULL);

  // downconversion to low-precision formats:
  if(bpp == 8)
  {
    if(display_byteorder)
    {
      if(high_quality_processing)
      {
        const float *const inbuf = (float *)outbuf;
        for(size_t k=0; k<(size_t)processed_width*processed_height; k++)
        {
          // convert in place, this is unfortunately very serial..
          const uint8_t r = CLAMP(inbuf[4*k+2]*0xff, 0, 0xff);
          const uint8_t g = CLAMP(inbuf[4*k+1]*0xff, 0, 0xff);
          const uint8_t b = CLAMP(inbuf[4*k+0]*0xff, 0, 0xff);
          outbuf[4*k+0] = r;
          outbuf[4*k+1] = g;
          outbuf[4*k+2] = b;
        }
      }
      // else processing output was 8-bit already, and no need to swap order
    }
    else // need to flip 
    {
      // ldr output: char
      if(high_quality_processing)
      {
        const float *const inbuf = (float *)outbuf;
        for(size_t k=0; k<(size_t)processed_width*processed_height; k++)
        {
          // convert in place, this is unfortunately very serial..
          const uint8_t r = CLAMP(inbuf[4*k+0]*0xff, 0, 0xff);
          const uint8_t g = CLAMP(inbuf[4*k+1]*0xff, 0, 0xff);
          const uint8_t b = CLAMP(inbuf[4*k+2]*0xff, 0, 0xff);
          outbuf[4*k+0] = r;
          outbuf[4*k+1] = g;
          outbuf[4*k+2] = b;
        }
      }
      else
      { // !display_byteorder, need to swap:
        uint8_t *const buf8 = pipe.backbuf;
#ifdef _OPENMP
#pragma omp parallel for default(none) shared(processed_width, processed_height) schedule(static)
#endif
        // just flip byte order
        for(size_t k=0; k<(size_t)processed_width*processed_height; k++)
        {
          uint8_t tmp = buf8[4*k+0];
          buf8[4*k+0] = buf8[4*k+2];
          buf8[4*k+2] = tmp;
        }
      }
    }
  }
  else if(bpp == 16)
  {
    // uint16_t per color channel
    float    *buff  = (float *)   outbuf;
    uint16_t *buf16 = (uint16_t *)outbuf;
    for(int y=0; y<processed_height; y++) for(int x=0; x<processed_width ; x++)
      {
        // convert in place
        const size_t k = (size_t)processed_width*y + x;
        for(int i=0; i<3; i++) buf16[4*k+i] = CLAMP(buff[4*k+i]*0x10000, 0, 0xffff);
      }
  }
  // else output float, no further harm done to the pixels :)

  format_params->width  = processed_width;
  format_params->height = processed_height;

  if(!ignore_exif)
  {
    int length;
    uint8_t exif_profile[65535]; // C++ alloc'ed buffer is uncool, so we waste some bits here.
    char pathname[PATH_MAX];
    gboolean from_cache = TRUE;
    dt_image_full_path(imgid, pathname, sizeof(pathname), &from_cache);
    // last param is dng mode, it's false here
    length = dt_exif_read_blob(exif_profile, pathname, imgid, sRGB, processed_width, processed_height, 0);

    res = format->write_image (format_params, filename, outbuf, exif_profile, length, imgid);
  }
  else
  {
    res = format->write_image (format_params, filename, outbuf, NULL, 0, imgid);
  }

  dt_dev_pixelpipe_cleanup(&pipe);
  dt_dev_cleanup(&dev);
  dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf);
  dt_free_align(moutbuf);
  /* now write xmp into that container, if possible */
  if(copy_metadata && (format->flags(format_params) & FORMAT_FLAGS_SUPPORT_XMP)) {
    dt_exif_xmp_attach(imgid, filename);
    // no need to cancel the export if this fail
  }


  if(!thumbnail_export && strcmp(format->mime(format_params), "memory"))
  {
    dt_control_signal_raise(darktable.signals,DT_SIGNAL_IMAGE_EXPORT_TMPFILE,imgid,filename,format,format_params,storage,storage_params);
  }
  return res;
}
Exemple #24
0
void dt_lightroom_import (int imgid, dt_develop_t *dev, gboolean iauto)
{
    gboolean refresh_needed = FALSE;
    char imported[256] = {0};

    // Get full pathname
    char *pathname = dt_get_lightroom_xmp(imgid);

    if (!pathname)
    {
        if (!iauto) dt_control_log(_("cannot find lightroom XMP!"));
        return;
    }

    // Load LR xmp

    xmlDocPtr doc;
    xmlNodePtr entryNode;

    // Parse xml document

    doc = xmlParseEntity(pathname);

    if (doc == NULL)
    {
        g_free(pathname);
        return;
    }

    // Enter first node, xmpmeta

    entryNode = xmlDocGetRootElement(doc);

    if (entryNode == NULL)
    {
        g_free(pathname);
        xmlFreeDoc(doc);
        return;
    }

    if (xmlStrcmp(entryNode->name, (const xmlChar *)"xmpmeta"))
    {
        if (!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname);
        g_free(pathname);
        return;
    }

    // Check that this is really a Lightroom document

    xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc);

    if (xpathCtx == NULL)
    {
        g_free(pathname);
        xmlFreeDoc(doc);
        return;
    }

    xmlXPathRegisterNs(xpathCtx, BAD_CAST "stEvt", BAD_CAST "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#");

    xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression((const xmlChar *)"//@stEvt:softwareAgent", xpathCtx);

    if (xpathObj == NULL)
    {
        if (!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname);
        xmlXPathFreeContext(xpathCtx);
        g_free(pathname);
        xmlFreeDoc(doc);
        return;
    }

    xmlNodeSetPtr xnodes = xpathObj->nodesetval;

    if (xnodes != NULL && xnodes->nodeNr > 0)
    {
        xmlNodePtr xnode = xnodes->nodeTab[0];
        xmlChar *value = xmlNodeListGetString(doc, xnode->xmlChildrenNode, 1);

        if (!strstr((char *)value,"Lightroom"))
        {
            xmlXPathFreeContext(xpathCtx);
            xmlXPathFreeObject(xpathObj);
            xmlFreeDoc(doc);
            xmlFree(value);
            if (!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname);
            g_free(pathname);
            return;
        }
        xmlFree(value);
    }
    else
    {
        xmlXPathFreeObject(xpathObj);
        xmlXPathFreeContext(xpathCtx);
        if (!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname);
        g_free(pathname);
        return;
    }

    xmlXPathFreeObject(xpathObj);
    xmlXPathFreeContext(xpathCtx);

    // Go safely to Description node

    if (entryNode)
        entryNode = entryNode->xmlChildrenNode;
    if (entryNode)
        entryNode = entryNode->next;
    if (entryNode)
        entryNode = entryNode->xmlChildrenNode;
    if (entryNode)
        entryNode = entryNode->next;

    if (!entryNode || xmlStrcmp(entryNode->name, (const xmlChar *)"Description"))
    {
        if (!iauto) dt_control_log(_("`%s' not a lightroom XMP!"), pathname);
        g_free(pathname);
        return;
    }
    g_free(pathname);

    //  Look for attributes in the Description

    dt_iop_clipping_params_t pc;
    memset(&pc, 0, sizeof(pc));
    gboolean has_crop = FALSE;

    dt_iop_flip_params_t pf;
    memset(&pf, 0, sizeof(pf));
    gboolean has_flip = FALSE;

    dt_iop_exposure_params_t pe;
    memset(&pe, 0, sizeof(pe));
    gboolean has_exposure = FALSE;

    dt_iop_vignette_params_t pv;
    memset(&pv, 0, sizeof(pv));
    gboolean has_vignette = FALSE;

    dt_iop_grain_params_t pg;
    memset(&pg, 0, sizeof(pg));
    gboolean has_grain = FALSE;

    dt_iop_spots_params_t ps;
    memset(&ps, 0, sizeof(ps));
    gboolean has_spots = FALSE;

    typedef enum lr_curve_kind_t
    {
        linear = 0,
        medium_contrast = 1,
        string_contrast = 2,
        custom = 3
    } lr_curve_kind_t;

#define MAX_PTS 20
    dt_iop_tonecurve_params_t ptc;
    memset(&ptc, 0, sizeof(ptc));
    int ptc_value[4] = {0, 0, 0, 0};
    float ptc_split[3] = {0.0, 0.0, 0.0};
    lr_curve_kind_t curve_kind = linear;
    int curve_pts[MAX_PTS][2];
    int n_pts = 0;

    dt_iop_colorzones_params_t pcz;
    memset(&pcz, 0, sizeof(pcz));
    gboolean has_colorzones = FALSE;

    dt_iop_splittoning_params_t pst;
    memset(&pst, 0, sizeof(pst));
    gboolean has_splittoning = FALSE;

    dt_iop_bilat_params_t pbl;
    memset(&pbl, 0, sizeof(pbl));
    gboolean has_bilat = FALSE;

    gboolean has_tags = FALSE;

    int rating = 0;
    gboolean has_rating = FALSE;

    gdouble lat = 0, lon = 0;
    gboolean has_gps = FALSE;

    int color = 0;
    gboolean has_colorlabel = FALSE;

    float fratio = 0;                // factor ratio image
    float crop_roundness = 0;        // from lightroom
    int n_import = 0;                // number of iop imported
    const float hfactor = 3.0 / 9.0; // hue factor adjustment (use 3 out of 9 boxes in colorzones)
    const float lfactor = 4.0 / 9.0; // lightness factor adjustment (use 4 out of 9 boxes in colorzones)
    int iwidth = 0, iheight = 0;     // image width / height
    int orientation = 1;

    xmlAttr* attribute = entryNode->properties;

    while(attribute && attribute->name && attribute->children)
    {
        xmlChar* value = xmlNodeListGetString(entryNode->doc, attribute->children, 1);
        if (!xmlStrcmp(attribute->name, (const xmlChar *) "CropTop"))
            pc.cy = g_ascii_strtod((char *)value, NULL);
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "CropRight"))
            pc.cw = g_ascii_strtod((char *)value, NULL);
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "CropLeft"))
            pc.cx = g_ascii_strtod((char *)value, NULL);
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "CropBottom"))
            pc.ch = g_ascii_strtod((char *)value, NULL);
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "CropAngle"))
            pc.angle = -g_ascii_strtod((char *)value, NULL);
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ImageWidth"))
            iwidth = atoi((char *)value);
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ImageLength"))
            iheight = atoi((char *)value);
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "Orientation"))
        {
            orientation = atoi((char *)value);
            if (dev!=NULL &&
                    ((dev->image_storage.orientation == 6 && orientation != 6)
                     || (dev->image_storage.orientation == 5 && orientation != 8)
                     || (dev->image_storage.orientation == 0 && orientation != 1))) has_flip = TRUE;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HasCrop"))
        {
            if (!xmlStrcmp(value, (const xmlChar *)"True"))
                has_crop = TRUE;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "Blacks2012"))
        {
            int v = atoi((char *)value);
            if (v != 0)
            {
                has_exposure = TRUE;
                pe.black = lr2dt_blacks((float)v);
            }
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "Exposure2012"))
        {
            float v = g_ascii_strtod((char *)value, NULL);
            if (v != 0.0)
            {
                has_exposure = TRUE;
                pe.exposure = lr2dt_exposure(v);
            }
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "PostCropVignetteAmount"))
        {
            int v = atoi((char *)value);
            if (v != 0)
            {
                has_vignette = TRUE;
                pv.brightness = lr2dt_vignette_gain((float)v);
            }
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "PostCropVignetteMidpoint"))
        {
            int v = atoi((char *)value);
            pv.scale = lr2dt_vignette_midpoint((float)v);
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "PostCropVignetteStyle"))
        {
            int v = atoi((char *)value);
            if (v == 1) // Highlight Priority
                pv.saturation = -0.300;
            else // Color Priority & Paint Overlay
                pv.saturation = -0.200;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "PostCropVignetteFeather"))
        {
            int v = atoi((char *)value);
            if (v != 0)
                pv.falloff_scale = (float)v;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "PostCropVignetteRoundness"))
        {
            int v = atoi((char *)value);
            crop_roundness = (float)v;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "GrainAmount"))
        {
            int v = atoi((char *)value);
            if (v != 0)
            {
                has_grain = TRUE;
                pg.strength = lr2dt_grain_amount((float)v);
            }
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "GrainFrequency"))
        {
            int v = atoi((char *)value);
            if (v != 0)
                pg.scale = lr2dt_grain_frequency((float)v);
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ParametricShadows"))
        {
            ptc_value[0] = atoi((char *)value);
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ParametricDarks"))
        {
            ptc_value[1] = atoi((char *)value);
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ParametricLights"))
        {
            ptc_value[2] = atoi((char *)value);
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ParametricHighlights"))
        {
            ptc_value[3] = atoi((char *)value);
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ParametricShadowSplit"))
        {
            ptc_split[0] = g_ascii_strtod((char *)value, NULL) / 100.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ParametricMidtoneSplit"))
        {
            ptc_split[1] = g_ascii_strtod((char *)value, NULL) / 100.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ParametricHighlightSplit"))
        {
            ptc_split[2] = g_ascii_strtod((char *)value, NULL) / 100.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "ToneCurveName2012"))
        {
            if (!xmlStrcmp(value, (const xmlChar *)"Linear"))
                curve_kind = linear;
            else if (!xmlStrcmp(value, (const xmlChar *)"Medium Contrast"))
                curve_kind = medium_contrast;
            else if (!xmlStrcmp(value, (const xmlChar *)"Strong Contrast"))
                curve_kind = medium_contrast;
            else if (!xmlStrcmp(value, (const xmlChar *)"Custom"))
                curve_kind = custom;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentRed"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[1][0] = 0.5 + (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentOrange"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[1][1] = 0.5 + (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentYellow"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[1][2] = 0.5 + (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentGreen"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[1][3] = 0.5 + (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentAqua"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[1][4] = 0.5 + (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentBlue"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[1][5] = 0.5 + (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentPurple"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[1][6] = 0.5 + (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SaturationAdjustmentMagenta"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[1][7] = 0.5 + (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentRed"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[0][0] = 0.5 + lfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentOrange"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[0][1] = 0.5 + lfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentYellow"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[0][2] = 0.5 + lfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentGreen"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[0][3] = 0.5 + lfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentAqua"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[0][4] = 0.5 + lfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentBlue"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[0][5] = 0.5 + lfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentPurple"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[0][6] = 0.5 + lfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "LuminanceAdjustmentMagenta"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[0][7] = 0.5 + lfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentRed"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[2][0] = 0.5 + hfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentOrange"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[2][1] = 0.5 + hfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentYellow"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[2][2] = 0.5 + hfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentGreen"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[2][3] = 0.5 + hfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentAqua"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[2][4] = 0.5 + hfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentBlue"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[2][5] = 0.5 + hfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentPurple"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[2][6] = 0.5 + hfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "HueAdjustmentMagenta"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_colorzones = TRUE;
            pcz.equalizer_y[2][7] = 0.5 + hfactor * (float)v / 200.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SplitToningShawowHue"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_splittoning = TRUE;
            pst.shadow_hue = (float)v / 255.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SplitToningShawowSaturation"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_splittoning = TRUE;
            pst.shadow_saturation = (float)v / 100.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SplitToningHighlightHue"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_splittoning = TRUE;
            pst.highlight_hue = (float)v / 255.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SplitToningHighlightSaturation"))
        {
            int v = atoi((char *)value);
            if (v!=0)
                has_splittoning = TRUE;
            pst.highlight_saturation = (float)v / 100.0;
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "SplitToningBalance"))
        {
            float v = g_ascii_strtod((char *)value, NULL);
            pst.balance = lr2dt_splittoning_balance(v);
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "Clarity2012"))
        {
            int v = atoi((char *)value);
            if (v!=0)
            {
                has_bilat = TRUE;
                pbl.detail = lr2dt_clarity((float)v);
            }
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "Rating"))
        {
            int v = atoi((char *)value);
            if (v!=0)
            {
                rating = v;
                has_rating = TRUE;
            }
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "GPSLatitude"))
        {
            int deg;
            double msec;
            char d;

            if (sscanf((const char *)value, "%d,%lf%c", &deg, &msec, &d))
            {
                lat = deg + msec / 60.0;
                if (d == 'S') lat = -lat;
                has_gps = TRUE;
            }
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "GPSLongitude"))
        {
            int deg;
            double msec;
            char d;

            if (sscanf((const char *)value, "%d,%lf%c", &deg, &msec, &d))
            {
                lon = deg + msec / 60.0;
                if (d == 'W') lon = -lon;
                has_gps = TRUE;
            }
        }
        else if (!xmlStrcmp(attribute->name, (const xmlChar *) "Label"))
        {
            for(int i=0; value[i]; i++)
                value[i] = tolower(value[i]);

            if (!strcmp((char *)value, _("red")))
                color = 0;
            else if (!strcmp((char *)value, _("yellow")))
                color = 1;
            else if (!strcmp((char *)value, _("green")))
                color = 2;
            else if (!strcmp((char *)value, _("blue")))
                color = 3;
            else
                // just an else here to catch all other cases as on lightroom one can
                // change the names of labels. So purple and the user's defined labels
                // will be mapped to purple on darktable.
                color = 4;

            has_colorlabel = TRUE;
        }

        xmlFree(value);
        attribute = attribute->next;
    }

    //  Look for tags (subject/Bag/* and RetouchInfo/seq/*)

    entryNode = entryNode->xmlChildrenNode;
    if (entryNode) entryNode = entryNode->next;

    while (entryNode)
    {
        if (dev == NULL
                && (!xmlStrcmp(entryNode->name, (const xmlChar *) "subject")
                    ||!xmlStrcmp(entryNode->name, (const xmlChar *) "hierarchicalSubject")))
        {
            xmlNodePtr tagNode = entryNode;

            tagNode = tagNode->xmlChildrenNode;
            tagNode = tagNode->next;
            tagNode = tagNode->xmlChildrenNode;
            tagNode = tagNode->next;

            while (tagNode)
            {
                if (!xmlStrcmp(tagNode->name, (const xmlChar *) "li"))
                {
                    xmlChar *value= xmlNodeListGetString(doc, tagNode->xmlChildrenNode, 1);
                    guint tagid = 0;

                    if (!dt_tag_exists((char *)value, &tagid))
                        dt_tag_new((char *)value, &tagid);

                    dt_tag_attach(tagid, imgid);
                    has_tags = TRUE;
                    xmlFree(value);
                }
                tagNode = tagNode->next;
            }
        }
        else if (dev != NULL && !xmlStrcmp(entryNode->name, (const xmlChar *) "RetouchInfo"))
        {
            xmlNodePtr riNode = entryNode;

            riNode = riNode->xmlChildrenNode;
            riNode = riNode->next;
            riNode = riNode->xmlChildrenNode;
            riNode = riNode->next;

            while (riNode)
            {
                if (!xmlStrcmp(riNode->name, (const xmlChar *) "li"))
                {
                    xmlChar *value= xmlNodeListGetString(doc, riNode->xmlChildrenNode, 1);
                    spot_t *p = &ps.spot[ps.num_spots];
                    if (sscanf((const char *)value, "centerX = %f, centerY = %f, radius = %f, sourceState = %*[a-zA-Z], sourceX = %f, sourceY = %f", &(p->x), &(p->y), &(p->radius), &(p->xc), &(p->yc)))
                    {
                        ps.num_spots++;
                        has_spots = TRUE;
                    }
                    xmlFree(value);
                }
                if (ps.num_spots == MAX_SPOTS) break;
                riNode = riNode->next;
            }
        }
        else if (dev != NULL && !xmlStrcmp(entryNode->name, (const xmlChar *) "ToneCurvePV2012"))
        {
            xmlNodePtr tcNode = entryNode;

            tcNode = tcNode->xmlChildrenNode;
            tcNode = tcNode->next;
            tcNode = tcNode->xmlChildrenNode;
            tcNode = tcNode->next;

            while (tcNode)
            {
                if (!xmlStrcmp(tcNode->name, (const xmlChar *) "li"))
                {
                    xmlChar *value= xmlNodeListGetString(doc, tcNode->xmlChildrenNode, 1);

                    if (sscanf((const char *)value, "%d, %d", &(curve_pts[n_pts][0]), &(curve_pts[n_pts][1])))
                        n_pts++;
                    xmlFree(value);
                }
                if (n_pts == MAX_PTS) break;
                tcNode = tcNode->next;
            }
        }
        entryNode = entryNode->next;
    }

    xmlFreeDoc(doc);

    //  Integrates into the history all the imported iop

    if (dev != NULL && dt_image_is_raw(&dev->image_storage))
    {
        // set colorin to cmatrix which is the default from Adobe (so closer to what Lightroom does)
        dt_iop_colorin_params_t pci = (dt_iop_colorin_params_t)
        {
            "cmatrix", DT_INTENT_PERCEPTUAL
        };

        dt_add_hist (imgid, "colorin", (dt_iop_params_t *)&pci, sizeof(dt_iop_colorin_params_t), imported, sizeof(imported), LRDT_COLORIN_VERSION, &n_import);
        refresh_needed=TRUE;
    }

    if (dev != NULL && has_crop)
    {
        pc.k_sym = 0;
        pc.k_apply = 0;
        pc.crop_auto = 0;
        pc.k_h = pc.k_v = 0;
        pc.k_type = 0;
        pc.kxa = pc.kxd = 0.2f;
        pc.kxc = pc.kxb = 0.8f;
        pc.kya = pc.kyb = 0.2f;
        pc.kyc = pc.kyd = 0.8f;
        float tmp;

        if (has_crop)
        {
            // adjust crop data according to the rotation

            switch (dev->image_storage.orientation)
            {
            case 5: // portrait - counter-clockwise
                tmp = pc.ch;
                pc.ch = 1.0 - pc.cx;
                pc.cx = pc.cy;
                pc.cy = 1.0 - pc.cw;
                pc.cw = tmp;
                break;
            case 6: // portrait - clockwise
                tmp = pc.ch;
                pc.ch = pc.cw;
                pc.cw = 1.0 - pc.cy;
                pc.cy = pc.cx;
                pc.cx = 1.0 - tmp;
                break;
            default:
                break;
            }

            if (pc.angle != 0)
            {
                const float rangle = -pc.angle * (3.141592 / 180);
                float x, y;

                // do the rotation (rangle) using center of image (0.5, 0.5)

                x = pc.cx - 0.5;
                y = 0.5 - pc.cy;
                pc.cx = 0.5 + x * cos(rangle) - y * sin(rangle);
                pc.cy = 0.5 - (x * sin(rangle) + y * cos(rangle));

                x = pc.cw - 0.5;
                y = 0.5 - pc.ch;
                pc.cw = 0.5 + x * cos(rangle) - y * sin(rangle);
                pc.ch = 0.5 - (x * sin(rangle) + y * cos(rangle));
            }
        }
        else
        {
            pc.angle = 0;
            pc.cx = 0;
            pc.cy = 0;
            pc.cw = 1;
            pc.ch = 1;
        }

        fratio = (pc.cw - pc.cx) / (pc.ch - pc.cy);

        dt_add_hist (imgid, "clipping", (dt_iop_params_t *)&pc, sizeof(dt_iop_clipping_params_t), imported, sizeof(imported), LRDT_CLIPPING_VERSION, &n_import);
        refresh_needed=TRUE;
    }

    if (dev != NULL && has_flip)
    {
        pf.orientation = 0;

        if (dev->image_storage.orientation == 5)
            // portrait
            switch (orientation)
            {
            case 8:
                pf.orientation = 0;
                break;
            case 3:
                pf.orientation = 5;
                break;
            case 6:
                pf.orientation = 3;
                break;
            case 1:
                pf.orientation = 6;
                break;

            // with horizontal flip
            case 7:
                pf.orientation = 1;
                break;
            case 2:
                pf.orientation = 4;
                break;
            case 5:
                pf.orientation = 2;
                break;
            case 4:
                pf.orientation = 7;
                break;
            }

        else if (dev->image_storage.orientation == 6)
            // portrait
            switch (orientation)
            {
            case 8:
                pf.orientation = 3;
                break;
            case 3:
                pf.orientation = 6;
                break;
            case 6:
                pf.orientation = 0;
                break;
            case 1:
                pf.orientation = 5;
                break;

            // with horizontal flip
            case 7:
                pf.orientation = 2;
                break;
            case 2:
                pf.orientation = 7;
                break;
            case 5:
                pf.orientation = 1;
                break;
            case 4:
                pf.orientation = 4;
                break;
            }

        else
            // landscape
            switch (orientation)
            {
            case 8:
                pf.orientation = 5;
                break;
            case 3:
                pf.orientation = 3;
                break;
            case 6:
                pf.orientation = 6;
                break;
            case 1:
                pf.orientation = 0;
                break;

            // with horizontal flip
            case 7:
                pf.orientation = 7;
                break;
            case 2:
                pf.orientation = 1;
                break;
            case 5:
                pf.orientation = 4;
                break;
            case 4:
                pf.orientation = 2;
                break;
            }

        dt_add_hist (imgid, "flip", (dt_iop_params_t *)&pf, sizeof(dt_iop_flip_params_t), imported, sizeof(imported), LRDT_FLIP_VERSION, &n_import);
        refresh_needed=TRUE;
    }

    if (dev != NULL && has_exposure)
    {
        dt_add_hist (imgid, "exposure", (dt_iop_params_t *)&pe, sizeof(dt_iop_exposure_params_t), imported, sizeof(imported), LRDT_EXPOSURE_VERSION, &n_import);
        refresh_needed=TRUE;
    }

    if (dev != NULL && has_grain)
    {
        pg.channel = 0;

        dt_add_hist (imgid, "grain", (dt_iop_params_t *)&pg, sizeof(dt_iop_grain_params_t), imported, sizeof(imported), LRDT_GRAIN_VERSION, &n_import);
        refresh_needed=TRUE;
    }

    if (dev != NULL && has_vignette)
    {
        const float base_ratio = 1.325 / 1.5;

        pv.autoratio = FALSE;
        pv.dithering = DITHER_8BIT;
        pv.center.x = 0.0;
        pv.center.y = 0.0;
        pv.shape = 1.0;

        // defensive code, should not happen, but just in case future Lr version
        // has not ImageWidth/ImageLength XML tag.
        if (iwidth == 0 || iheight == 0)
            pv.whratio = base_ratio;
        else
            pv.whratio = base_ratio * ((float)iwidth / (float)iheight);

        if (has_crop)
            pv.whratio = pv.whratio * fratio;

        //  Adjust scale and ratio based on the roundness. On Lightroom changing
        //  the roundness change the width and the height of the vignette.

        if (crop_roundness > 0)
        {
            float newratio = pv.whratio - (pv.whratio - 1) * (crop_roundness / 100.0);
            float dscale = (1 - (newratio / pv.whratio)) / 2.0;

            pv.scale -= dscale * 100.0;
            pv.whratio = newratio;
        }

        dt_add_hist (imgid, "vignette", (dt_iop_params_t *)&pv, sizeof(dt_iop_vignette_params_t), imported, sizeof(imported), LRDT_VIGNETTE_VERSION, &n_import);
        refresh_needed=TRUE;
    }

    if (dev != NULL && has_spots)
    {
        // Check for orientation, rotate when in portrait mode
        if (orientation > 4)
            for (int k=0; k<ps.num_spots; k++)
            {
                float tmp = ps.spot[k].y;
                ps.spot[k].y  = 1.0 - ps.spot[k].x;
                ps.spot[k].x = tmp;
                tmp = ps.spot[k].yc;
                ps.spot[k].yc  = 1.0 - ps.spot[k].xc;
                ps.spot[k].xc = tmp;
            }

        dt_add_hist (imgid, "spots", (dt_iop_params_t *)&ps, sizeof(dt_iop_spots_params_t), imported, sizeof(imported), LRDT_SPOTS_VERSION, &n_import);
        refresh_needed=TRUE;
    }

    if (curve_kind != linear || ptc_value[0] != 0 || ptc_value[1] != 0 || ptc_value[2] != 0 || ptc_value[3] != 0)
    {
        ptc.tonecurve_nodes[ch_L] = 6;
        ptc.tonecurve_nodes[ch_a] = 7;
        ptc.tonecurve_nodes[ch_b] = 7;
        ptc.tonecurve_type[ch_L] = CUBIC_SPLINE;
        ptc.tonecurve_type[ch_a] = CUBIC_SPLINE;
        ptc.tonecurve_type[ch_b] = CUBIC_SPLINE;
        ptc.tonecurve_autoscale_ab = 1;
        ptc.tonecurve_preset = 0;

        float linear_ab[7] = {0.0, 0.08, 0.3, 0.5, 0.7, 0.92, 1.0};

        // linear a, b curves
        for(int k=0; k<7; k++) ptc.tonecurve[ch_a][k].x = linear_ab[k];
        for(int k=0; k<7; k++) ptc.tonecurve[ch_a][k].y = linear_ab[k];
        for(int k=0; k<7; k++) ptc.tonecurve[ch_b][k].x = linear_ab[k];
        for(int k=0; k<7; k++) ptc.tonecurve[ch_b][k].y = linear_ab[k];

        // Set the base tonecurve

        if (curve_kind == linear)
        {
            ptc.tonecurve[ch_L][0].x = 0.0;
            ptc.tonecurve[ch_L][0].y = 0.0;
            ptc.tonecurve[ch_L][1].x = ptc_split[0] / 2.0;
            ptc.tonecurve[ch_L][1].y = ptc_split[0] / 2.0;
            ptc.tonecurve[ch_L][2].x = ptc_split[1] - (ptc_split[1] - ptc_split[0]) / 2.0;
            ptc.tonecurve[ch_L][2].y = ptc_split[1] - (ptc_split[1] - ptc_split[0]) / 2.0;
            ptc.tonecurve[ch_L][3].x = ptc_split[1] + (ptc_split[2] - ptc_split[1]) / 2.0;
            ptc.tonecurve[ch_L][3].y = ptc_split[1] + (ptc_split[2] - ptc_split[1]) / 2.0;
            ptc.tonecurve[ch_L][4].x = ptc_split[2] + (1.0 - ptc_split[2]) / 2.0;
            ptc.tonecurve[ch_L][4].y = ptc_split[2] + (1.0 - ptc_split[2]) / 2.0;
            ptc.tonecurve[ch_L][5].x = 1.0;
            ptc.tonecurve[ch_L][5].y = 1.0;
        }
        else
        {
            for (int k=0; k<6; k++)
            {
                ptc.tonecurve[ch_L][k].x = curve_pts[k][0] / 255.0;
                ptc.tonecurve[ch_L][k].y = curve_pts[k][1] / 255.0;
            }
        }

        if (curve_kind != custom)
        {
            // set shadows/darks/lights/highlight adjustments

            ptc.tonecurve[ch_L][1].y += ptc.tonecurve[ch_L][1].y * ((float)ptc_value[0] / 100.0);
            ptc.tonecurve[ch_L][2].y += ptc.tonecurve[ch_L][1].y * ((float)ptc_value[1] / 100.0);
            ptc.tonecurve[ch_L][3].y += ptc.tonecurve[ch_L][1].y * ((float)ptc_value[2] / 100.0);
            ptc.tonecurve[ch_L][4].y += ptc.tonecurve[ch_L][1].y * ((float)ptc_value[3] / 100.0);

            if (ptc.tonecurve[ch_L][1].y > ptc.tonecurve[ch_L][2].y)
                ptc.tonecurve[ch_L][1].y = ptc.tonecurve[ch_L][2].y;
            if (ptc.tonecurve[ch_L][3].y > ptc.tonecurve[ch_L][4].y)
                ptc.tonecurve[ch_L][4].y = ptc.tonecurve[ch_L][3].y;
        }

        dt_add_hist (imgid, "tonecurve",  (dt_iop_params_t *)&ptc, sizeof(dt_iop_tonecurve_params_t), imported, sizeof(imported), LRDT_TONECURVE_VERSION, &n_import);
        refresh_needed=TRUE;
    }

    if (dev != NULL && has_colorzones)
    {
        pcz.channel = DT_IOP_COLORZONES_h;

        for (int i=0; i<3; i++)
            for (int k=0; k<8; k++)
                pcz.equalizer_x[i][k] = k/(DT_IOP_COLORZONES_BANDS-1.0);

        dt_add_hist (imgid, "colorzones", (dt_iop_params_t *)&pcz, sizeof(dt_iop_colorzones_params_t), imported, sizeof(imported), LRDT_COLORZONES_VERSION, &n_import);
        refresh_needed=TRUE;
    }

    if (dev != NULL && has_splittoning)
    {
        pst.compress = 50.0;

        dt_add_hist (imgid, "splittoning", (dt_iop_params_t *)&pst, sizeof(dt_iop_splittoning_params_t), imported, sizeof(imported), LRDT_SPLITTONING_VERSION, &n_import);
        refresh_needed=TRUE;
    }

    if (dev != NULL && has_bilat)
    {
        pbl.sigma_r = 100.0;
        pbl.sigma_s = 100.0;

        dt_add_hist (imgid, "bilat", (dt_iop_params_t *)&pbl, sizeof(dt_iop_bilat_params_t), imported, sizeof(imported), LRDT_BILAT_VERSION, &n_import);
        refresh_needed=TRUE;
    }

    if (has_tags)
    {
        if (imported[0]) g_strlcat(imported, ", ", sizeof(imported));
        g_strlcat(imported, _("tags"), sizeof(imported));
        n_import++;
    }

    if (dev == NULL && has_rating)
    {
        dt_ratings_apply_to_image(imgid, rating);

        if (imported[0]) g_strlcat(imported, ", ", sizeof(imported));
        g_strlcat(imported, _("rating"), sizeof(imported));
        n_import++;
    }

    if (dev == NULL && has_gps)
    {
        dt_image_set_location(imgid, lon, lat);

        if (imported[0]) g_strlcat(imported, ", ", sizeof(imported));
        g_strlcat(imported, _("geotagging"), sizeof(imported));
        n_import++;
    }

    if (dev == NULL && has_colorlabel)
    {
        dt_colorlabels_set_label(imgid, color);

        if (imported[0]) g_strlcat(imported, ", ", sizeof(imported));
        g_strlcat(imported, _("color label"), sizeof(imported));
        n_import++;
    }

    if(dev != NULL && refresh_needed && dev->gui_attached)
    {
        char message[512];

        g_snprintf
        (message, sizeof(message),
         ngettext("%s has been imported", "%s have been imported", n_import), imported);
        dt_control_log(message);

        if (!iauto)
        {
            /* signal history changed */
            dt_dev_reload_history_items(dev);
            dt_dev_modulegroups_set(darktable.develop, dt_dev_modulegroups_get(darktable.develop));
            /* update xmp file */
            dt_image_synch_xmp(imgid);
            dt_control_signal_raise(darktable.signals,DT_SIGNAL_DEVELOP_HISTORY_CHANGE);
        }
    }
}
Exemple #25
0
void dt_film_import1(dt_job_t *job, dt_film_t *film)
{
  gboolean recursive = dt_conf_get_bool("ui_last/import_recursive");

  /* first of all gather all images to import */
  GList *images = NULL;
  images = _film_recursive_get_files(film->dirname, recursive, &images);
  if(g_list_length(images) == 0)
  {
    dt_control_log(_("no supported images were found to be imported"));
    return;
  }

#ifdef USE_LUA
  /* pre-sort image list for easier handling in Lua code */
  images = g_list_sort(images, (GCompareFunc)_film_filename_cmp);

  dt_lua_lock();
  lua_State *L = darktable.lua_state.state;
  {
    GList *elt = images;
    lua_newtable(L);
    while(elt)
    {
      lua_pushstring(L, elt->data);
      luaL_ref(L, -2);
      elt = g_list_next(elt);
    }
  }
  lua_pushvalue(L, -1);
  dt_lua_event_trigger(L, "pre-import", 1);
  {
    g_list_free_full(images, g_free);
    // recreate list of images
    images = NULL;
    lua_pushnil(L); /* first key */
    while(lua_next(L, -2) != 0)
    {
      /* uses 'key' (at index -2) and 'value' (at index -1) */
      void *filename = strdup(luaL_checkstring(L, -1));
      lua_pop(L, 1);
      images = g_list_prepend(images, filename);
    }
  }

  lua_pop(L, 1); // remove the table again from the stack

  dt_lua_unlock();
#endif

  if(g_list_length(images) == 0)
  {
    // no error message, lua probably emptied the list on purpose
    return;
  }

  /* we got ourself a list of images, lets sort and start import */
  images = g_list_sort(images, (GCompareFunc)_film_filename_cmp);

  /* let's start import of images */
  gchar message[512] = { 0 };
  double fraction = 0;
  guint total = g_list_length(images);
  g_snprintf(message, sizeof(message) - 1, ngettext("importing %d image", "importing %d images", total), total);
  dt_control_job_set_progress_message(job, message);


  /* loop thru the images and import to current film roll */
  dt_film_t *cfr = film;
  GList *image = g_list_first(images);
  do
  {
    gchar *cdn = g_path_get_dirname((const gchar *)image->data);

    /* check if we need to initialize a new filmroll */
    if(!cfr || g_strcmp0(cfr->dirname, cdn) != 0)
    {
      // FIXME: maybe refactor into function and call it?
      if(cfr && cfr->dir)
      {
        /* check if we can find a gpx data file to be auto applied
           to images in the jsut imported filmroll */
        g_dir_rewind(cfr->dir);
        const gchar *dfn = NULL;
        while((dfn = g_dir_read_name(cfr->dir)) != NULL)
        {
          /* check if we have a gpx to be auto applied to filmroll */
          size_t len = strlen(dfn);
          if(strcmp(dfn + len - 4, ".gpx") == 0 || strcmp(dfn + len - 4, ".GPX") == 0)
          {
            gchar *gpx_file = g_build_path(G_DIR_SEPARATOR_S, cfr->dirname, dfn, NULL);
            gchar *tz = dt_conf_get_string("plugins/lighttable/geotagging/tz");
            dt_control_gpx_apply(gpx_file, cfr->id, tz);
            g_free(gpx_file);
            g_free(tz);
          }
        }
      }

      /* cleanup previously imported filmroll*/
      if(cfr && cfr != film)
      {
        if(dt_film_is_empty(cfr->id))
        {
          dt_film_remove(cfr->id);
        }
        dt_film_cleanup(cfr);
        free(cfr);
        cfr = NULL;
      }

      /* initialize and create a new film to import to */
      cfr = malloc(sizeof(dt_film_t));
      dt_film_init(cfr);
      dt_film_new(cfr, cdn);
    }

    g_free(cdn);

    /* import image */
    dt_image_import(cfr->id, (const gchar *)image->data, FALSE);

    fraction += 1.0 / total;
    dt_control_job_set_progress(job, fraction);


  } while((image = g_list_next(image)) != NULL);

  g_list_free_full(images, g_free);

  // only redraw at the end, to not spam the cpu with exposure events
  dt_control_queue_redraw_center();
  dt_control_signal_raise(darktable.signals, DT_SIGNAL_TAG_CHANGED);

  dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_IMPORTED, film->id);

  // FIXME: maybe refactor into function and call it?
  if(cfr && cfr->dir)
  {
    /* check if we can find a gpx data file to be auto applied
       to images in the just imported filmroll */
    g_dir_rewind(cfr->dir);
    const gchar *dfn = NULL;
    while((dfn = g_dir_read_name(cfr->dir)) != NULL)
    {
      /* check if we have a gpx to be auto applied to filmroll */
      size_t len = strlen(dfn);
      if(strcmp(dfn + len - 4, ".gpx") == 0 || strcmp(dfn + len - 4, ".GPX") == 0)
      {
        gchar *gpx_file = g_build_path(G_DIR_SEPARATOR_S, cfr->dirname, dfn, NULL);
        gchar *tz = dt_conf_get_string("plugins/lighttable/geotagging/tz");
        dt_control_gpx_apply(gpx_file, cfr->id, tz);
        g_free(gpx_file);
        g_free(tz);
      }
    }
  }

  /* cleanup previously imported filmroll*/
  if(cfr && cfr != film)
  {
    dt_film_cleanup(cfr);
    free(cfr);
  }
}
Exemple #26
0
void dt_dev_read_history(dt_develop_t *dev)
{
  if(dev->image_storage.id <= 0) return;
  if(!dev->iop) return;

  // maybe prepend auto-presets to history before loading it:
  auto_apply_presets(dev);

  sqlite3_stmt *stmt;
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
                              "select imgid, num, module, operation, op_params, enabled, blendop_params, blendop_version, multi_priority, multi_name from history where imgid = ?1 order by num", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dev->image_storage.id);
  dev->history_end = 0;
  while(sqlite3_step(stmt) == SQLITE_ROW)
  {
    // db record:
    // 0-img, 1-num, 2-module_instance, 3-operation char, 4-params blob, 5-enabled, 6-blend_params, 7-blendop_version, 8 multi_priority, 9 multi_name
    dt_dev_history_item_t *hist = (dt_dev_history_item_t *)malloc(sizeof(dt_dev_history_item_t));
    hist->enabled = sqlite3_column_int(stmt, 5);

    GList *modules = dev->iop;
    const char *opname = (const char *)sqlite3_column_text(stmt, 3);
    int multi_priority = sqlite3_column_int(stmt, 8);
    const char *multi_name = (const char *)sqlite3_column_text(stmt, 9);
    if(!opname)
    {
      fprintf(stderr, "[dev_read_history] database history for image `%s' seems to be corrupted!\n", dev->image_storage.filename);
      free(hist);
      continue;
    }

    hist->module = NULL;
    dt_iop_module_t *find_op = NULL;
    while(opname && modules)
    {
      dt_iop_module_t *module = (dt_iop_module_t *)modules->data;
      if(!strcmp(module->op, opname))
      {
        if (module->multi_priority == multi_priority)
        {
          hist->module = module;
          if(multi_name && strcmp(module->multi_name, multi_name))
            snprintf(module->multi_name, 128, "%s", multi_name);
          break;
        }
        else if (multi_priority > 0)
        {
          //we just say that we find the name, so we just have to add new instance of this module
          find_op = module;
        }
      }
      modules = g_list_next(modules);
    }
    if (!hist->module && find_op)
    {
      //we have to add a new instance of this module and set index to modindex
      dt_iop_module_t *new_module    = (dt_iop_module_t *)malloc(sizeof(dt_iop_module_t));
      if (!dt_iop_load_module(new_module, find_op->so, dev))
      {
        new_module->multi_priority = multi_priority;

        snprintf(new_module->multi_name,128,"%s",multi_name);

        dev->iop = g_list_insert_sorted(dev->iop, new_module, sort_plugins);

        new_module->instance = find_op->instance;
        hist->module = new_module;
      }
    }

    if(!hist->module && opname)
    {
      fprintf(stderr, "[dev_read_history] the module `%s' requested by image `%s' is not installed on this computer!\n", opname, dev->image_storage.filename);
      free(hist);
      continue;
    }

    if(hist->module->flags() & IOP_FLAGS_NO_HISTORY_STACK)
    {
      free(hist);
      continue;
    }

    int modversion = sqlite3_column_int(stmt, 2);
    assert(strcmp((char *)sqlite3_column_text(stmt, 3), hist->module->op) == 0);
    hist->params = malloc(hist->module->params_size);
    hist->blend_params = malloc(sizeof(dt_develop_blend_params_t));
    snprintf(hist->multi_name,128,"%s",multi_name);
    hist->multi_priority = multi_priority;

    const void *blendop_params = sqlite3_column_blob(stmt, 6);
    int bl_length = sqlite3_column_bytes(stmt, 6);
    int blendop_version = sqlite3_column_int(stmt, 7);

    if (blendop_params && (blendop_version == dt_develop_blend_version()) && (bl_length == sizeof(dt_develop_blend_params_t)))
    {
      memcpy(hist->blend_params, blendop_params, sizeof(dt_develop_blend_params_t));
    }
    else if (blendop_params && dt_develop_blend_legacy_params(hist->module, blendop_params, blendop_version, hist->blend_params, dt_develop_blend_version(), bl_length) == 0)
    {
      // do nothing
    }
    else
    {
      memcpy(hist->blend_params, hist->module->default_blendop_params, sizeof(dt_develop_blend_params_t));
    }

    if(hist->module->version() != modversion || hist->module->params_size != sqlite3_column_bytes(stmt, 4) ||
        strcmp((char *)sqlite3_column_text(stmt, 3), hist->module->op))
    {
      if(!hist->module->legacy_params ||
          hist->module->legacy_params(hist->module, sqlite3_column_blob(stmt, 4), labs(modversion), hist->params, labs(hist->module->version())))
      {
        free(hist->params);
        free(hist->blend_params);
        fprintf(stderr, "[dev_read_history] module `%s' version mismatch: history is %d, dt %d.\n", hist->module->op, modversion, hist->module->version());
        const char *fname = dev->image_storage.filename + strlen(dev->image_storage.filename);
        while(fname > dev->image_storage.filename && *fname != '/') fname --;
        if(fname > dev->image_storage.filename) fname++;
        dt_control_log(_("%s: module `%s' version mismatch: %d != %d"), fname, hist->module->op, hist->module->version(), modversion);
        free(hist);
        continue;
      }
    }
    else
    {
      memcpy(hist->params, sqlite3_column_blob(stmt, 4), hist->module->params_size);
    }

    // make sure that always-on modules are always on. duh.
    if(hist->module->default_enabled == 1 && hist->module->hide_enable_button == 1)
    {
      hist->enabled = 1;
    }

    // memcpy(hist->module->params, hist->params, hist->module->params_size);
    // hist->module->enabled = hist->enabled;
    // printf("[dev read history] img %d number %d for operation %d - %s params %f %f\n", sqlite3_column_int(stmt, 0), sqlite3_column_int(stmt, 1), instance, hist->module->op, *(float *)hist->params, *(((float*)hist->params)+1));
    dev->history = g_list_append(dev->history, hist);
    dev->history_end ++;
  }

  if(dev->gui_attached)
  {
    dev->pipe->changed |= DT_DEV_PIPE_SYNCH;
    dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH; // again, fixed topology for now.
    dt_dev_invalidate_all(dev);

    /* signal history changed */
    dt_control_signal_raise(darktable.signals,DT_SIGNAL_DEVELOP_HISTORY_CHANGE);
  }
  sqlite3_finalize (stmt);
}
Exemple #27
0
void dt_control_queue_redraw()
{
  dt_control_signal_raise(darktable.signals, DT_SIGNAL_CONTROL_REDRAW_ALL);
}
Exemple #28
0
static gboolean _lib_filmstrip_button_press_callback(GtkWidget *w, GdkEventButton *e, gpointer user_data)
{
  dt_lib_module_t *self = (dt_lib_module_t *)user_data;
  dt_lib_filmstrip_t *strip = (dt_lib_filmstrip_t *)self->data;

  int32_t mouse_over_id = strip->mouse_over_id;
  strip->select = DT_LIB_FILMSTRIP_SELECT_NONE;

  if (e->button == 1)
  {
    if(e->type == GDK_BUTTON_PRESS)
    {
      /* let check if any thumb controls was clicked */
      switch(strip->image_over)
      {
        case DT_VIEW_DESERT:
          /* is this an activation of image */
          if ((e->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK)) == 0)
            strip->select = DT_LIB_FILMSTRIP_SELECT_SINGLE;
          else if ((e->state & (GDK_CONTROL_MASK)) == GDK_CONTROL_MASK)
            strip->select = DT_LIB_FILMSTRIP_SELECT_TOGGLE;
          else if ((e->state & (GDK_SHIFT_MASK)) == GDK_SHIFT_MASK)
            strip->select = DT_LIB_FILMSTRIP_SELECT_RANGE;
          if(strip->select != DT_LIB_FILMSTRIP_SELECT_NONE)
          {
            strip->select_id = mouse_over_id;
            return TRUE;
          }
          break;
        case DT_VIEW_REJECT:
        case DT_VIEW_STAR_1:
        case DT_VIEW_STAR_2:
        case DT_VIEW_STAR_3:
        case DT_VIEW_STAR_4:
        case DT_VIEW_STAR_5:
        {
          int offset = 0;
          if(mouse_over_id == strip->activated_image)
            offset = dt_collection_image_offset(mouse_over_id);

          const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, mouse_over_id);
          dt_image_t *image = dt_image_cache_write_get(darktable.image_cache, cimg);
          if(strip->image_over == DT_VIEW_STAR_1 && ((image->flags & 0x7) == 1)) image->flags &= ~0x7;
          else if(strip->image_over == DT_VIEW_REJECT && ((image->flags & 0x7) == 6)) image->flags &= ~0x7;
          else
          {
            image->flags &= ~0x7;
            image->flags |= strip->image_over;
          }
          dt_image_cache_write_release(darktable.image_cache, image, DT_IMAGE_CACHE_SAFE);
          dt_image_cache_read_release(darktable.image_cache, image);


          dt_collection_hint_message(darktable.collection); // More than this, we need to redraw all

          if(mouse_over_id == strip->activated_image)
            if(_lib_filmstrip_imgid_in_collection(darktable.collection, mouse_over_id) == 0)
              dt_view_filmstrip_scroll_relative(0, offset);

          gtk_widget_queue_draw(darktable.view_manager->proxy.filmstrip.module->widget);
          return TRUE;
        }

        default:
          return FALSE;
      }
    }
    else if(e->type == GDK_2BUTTON_PRESS)
    {
      if (mouse_over_id > 0)
      {
        strip->activated_image = mouse_over_id;
        dt_control_signal_raise(darktable.signals, DT_SIGNAL_VIEWMANAGER_FILMSTRIP_ACTIVATE);
        return TRUE;
      }
    }
  }

  return FALSE;
}
Exemple #29
0
// Get the display ICC profile of the monitor associated with the widget.
// For X display, uses the ICC profile specifications version 0.2 from
// http://burtonini.com/blog/computers/xicc
// Based on code from Gimp's modules/cdisplay_lcms.c
void dt_ctl_set_display_profile()
{
  if(!dt_control_running()) return;
  // make sure that no one gets a broken profile
  // FIXME: benchmark if the try is really needed when moving/resizing the window. Maybe we can just lock it and block
  if(pthread_rwlock_trywrlock(&darktable.control->xprofile_lock))
    return; // we are already updating the profile. Or someone is reading right now. Too bad we can't distinguish that. Whatever ...

  GtkWidget *widget = dt_ui_center(darktable.gui->ui);
  guint8 *buffer = NULL;
  gint buffer_size = 0;
  gchar *profile_source = NULL;

#if defined GDK_WINDOWING_X11

  // we will use the xatom no matter what configured when compiled without colord
  gboolean use_xatom = TRUE;
#if defined USE_COLORDGTK
  gboolean use_colord = TRUE;
  gchar *display_profile_source = dt_conf_get_string("ui_last/display_profile_source");
  if(display_profile_source)
  {
    if(!strcmp(display_profile_source, "xatom"))
      use_colord = FALSE;
    else if(!strcmp(display_profile_source, "colord"))
      use_xatom = FALSE;
    g_free(display_profile_source);
  }
#endif

  /* let's have a look at the xatom, just in case ... */
  if(use_xatom)
  {
    GdkScreen *screen = gtk_widget_get_screen(widget);
    if ( screen==NULL )
      screen = gdk_screen_get_default();
    int monitor = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window(widget));
    char *atom_name;
    if (monitor > 0)
      atom_name = g_strdup_printf("_ICC_PROFILE_%d", monitor);
    else
      atom_name = g_strdup("_ICC_PROFILE");

    profile_source = g_strdup_printf("xatom %s", atom_name);

    GdkAtom type = GDK_NONE;
    gint format = 0;
    gdk_property_get(gdk_screen_get_root_window(screen),
                    gdk_atom_intern(atom_name, FALSE), GDK_NONE,
                    0, 64 * 1024 * 1024, FALSE,
                    &type, &format, &buffer_size, &buffer);
    g_free(atom_name);
  }

#ifdef USE_COLORDGTK
  /* also try to get the profile from colord. this will set the value asynchronously! */
  if(use_colord)
  {
    CdWindow *window = cd_window_new();
    GtkWidget *center_widget = dt_ui_center(darktable.gui->ui);
    cd_window_get_profile(window, center_widget, NULL, dt_ctl_get_display_profile_colord_callback, NULL);
  }
#endif

#elif defined GDK_WINDOWING_QUARTZ
  GdkScreen *screen = gtk_widget_get_screen(widget);
  if ( screen==NULL )
    screen = gdk_screen_get_default();
  int monitor = gdk_screen_get_monitor_at_window(screen, gtk_widget_get_window(widget));

  CGDirectDisplayID ids[monitor + 1];
  uint32_t total_ids;
  CMProfileRef prof = NULL;
  if(CGGetOnlineDisplayList(monitor + 1, &ids[0], &total_ids) == kCGErrorSuccess && total_ids == monitor + 1)
    CMGetProfileByAVID(ids[monitor], &prof);
  if ( prof!=NULL )
  {
    CFDataRef data;
    data = CMProfileCopyICCData(NULL, prof);
    CMCloseProfile(prof);

    UInt8 *tmp_buffer = (UInt8 *) g_malloc(CFDataGetLength(data));
    CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), tmp_buffer);

    buffer = (guint8 *) tmp_buffer;
    buffer_size = CFDataGetLength(data);

    CFRelease(data);
  }
  profile_source = g_strdup("osx color profile api");
#elif defined G_OS_WIN32
  (void)widget;
  HDC hdc = GetDC (NULL);
  if ( hdc!=NULL )
  {
    DWORD len = 0;
    GetICMProfile (hdc, &len, NULL);
    gchar *path = g_new (gchar, len);

    if (GetICMProfile (hdc, &len, path))
    {
      gsize size;
      g_file_get_contents(path, (gchar**)&buffer, &size, NULL);
      buffer_size = size;
    }
    g_free (path);
    ReleaseDC (NULL, hdc);
  }
  profile_source = g_strdup("windows color profile api");
#endif

  int profile_changed = buffer_size > 0 &&
                        (darktable.control->xprofile_size != buffer_size || memcmp(darktable.control->xprofile_data, buffer, buffer_size) != 0);
  if(profile_changed)
  {
    cmsHPROFILE profile = NULL;
    char name[512];
    // thanks to ufraw for this!
    g_free(darktable.control->xprofile_data);
    darktable.control->xprofile_data = buffer;
    darktable.control->xprofile_size = buffer_size;
    profile = cmsOpenProfileFromMem(buffer, buffer_size);
    if(profile)
    {
      dt_colorspaces_get_profile_name(profile, "en", "US", name, sizeof(name));
      cmsCloseProfile(profile);
    }
    dt_print(DT_DEBUG_CONTROL, "[color profile] we got a new screen profile `%s' from the %s (size: %d)\n", *name?name:"(unknown)", profile_source, buffer_size);
  }
  pthread_rwlock_unlock(&darktable.control->xprofile_lock);
  if(profile_changed)
    dt_control_signal_raise(darktable.signals, DT_SIGNAL_CONTROL_PROFILE_CHANGED);
  g_free(profile_source);
}
Exemple #30
0
int dt_film_import(const char *dirname)
{
  int v = dt_film_import_blocking(dirname,0);
  dt_control_signal_raise(darktable.signals , DT_SIGNAL_FILMROLLS_IMPORTED);
  return v;
}