Beispiel #1
0
// http://stackoverflow.com/questions/4631388/transparent-floating-gtkentry
static gboolean
_lib_tagging_tag_key_press(GtkWidget *entry, GdkEventKey *event, dt_lib_module_t *self)
{
  dt_lib_tagging_t *d = (dt_lib_tagging_t*)self->data;
  switch(event->keyval)
  {
    case GDK_Escape:
      gtk_widget_destroy(d->floating_tag_window);
      return TRUE;
    case GDK_Tab:
      return TRUE;
    case GDK_Return:
    case GDK_KP_Enter:
    {
      const gchar *tag = gtk_entry_get_text(GTK_ENTRY(entry));
      /* create new tag */
      guint tid=0;
      dt_tag_new(tag, &tid);
      /* attach tag to images  */
      if(d->floating_tag_imgid > 0) // just a single image
      {
        dt_tag_attach(tid, d->floating_tag_imgid);
        dt_image_synch_xmp(d->floating_tag_imgid);
      }
      else // all selected images
      {
        GList *selected_images = g_list_first(dt_collection_get_selected(darktable.collection));
        if(selected_images)
        {
          GList *iter = selected_images;
          do
          {
            int imgid = GPOINTER_TO_INT(iter->data);
            dt_tag_attach(tid, imgid);
            dt_image_synch_xmp(imgid);
          }
          while( (iter=g_list_next(iter)) !=NULL );
        }
        g_list_free(selected_images);
      }
      update(self, 1);
      update(self, 0);
      gtk_widget_destroy(d->floating_tag_window);
      return TRUE;
    }
  }
  return FALSE; /* event not handled */
}
Beispiel #2
0
void dt_dev_write_history(dt_develop_t *dev)
{
  sqlite3_stmt *stmt;

  gboolean changed = FALSE;
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from history where imgid = ?1", -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dev->image_storage.id);
  sqlite3_step(stmt);
  sqlite3_finalize (stmt);
  GList *history = dev->history;
  for(int i=0; i<dev->history_end && history; i++)
  {
    dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(history->data);
    (void)dt_dev_write_history_item(&dev->image_storage, hist, i);
    history = g_list_next(history);
    changed = TRUE;
  }
  
  /* attach / detach changed tag reflecting actual change */
  guint tagid = 0;
  dt_tag_new("darktable|changed",&tagid); 
  if(changed)
    dt_tag_attach(tagid, dev->image_storage.id);
  else
    dt_tag_detach(tagid, dev->image_storage.id);

}
Beispiel #3
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 */
    int32_t offs = 0;
    DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT MAX(num)+1 FROM 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);

    /* copy history items from styles onto image */
    DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "insert into history (imgid,num,module,operation,op_params,enabled,blendop_params,blendop_version,multi_priority,multi_name) select ?1, num+?2,module,operation,op_params,enabled,blendop_params,blendop_version,multi_priority,multi_name from style_items where styleid=?3", -1, &stmt, NULL);
    DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, newimgid);
    DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, offs);
    DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, id);
    sqlite3_step (stmt);
    sqlite3_finalize (stmt);

    /* add tag */
    guint tagid=0;
    gchar ntag[512]= {0};
    g_snprintf(ntag,512,"darktable|style|%s",name);
    if (dt_tag_new(ntag,&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);

    /* redraw center view to update visible mipmaps */
    dt_control_queue_redraw_center();
  }
}
Beispiel #4
0
int dt_lua_tag_attach(lua_State *L)
{
  dt_lua_image_t imgid = -1;
  dt_lua_tag_t tagid = 0;
  if(luaL_testudata(L,1,"dt_lua_image_t")) {
    luaA_to(L,dt_lua_image_t,&imgid,1);
    luaA_to(L,dt_lua_tag_t,&tagid,2);
  } else {
    luaA_to(L,dt_lua_tag_t,&tagid,1);
    luaA_to(L,dt_lua_image_t,&imgid,2);
  }
  dt_tag_attach(tagid,imgid);
  return 0;
}
Beispiel #5
0
int32_t dt_control_local_copy_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;
  guint tagid = 0;
  const int total = g_list_length(t);
  double fraction=0;
  gboolean is_copy = GPOINTER_TO_INT(job->user_data) == 1;
  char message[512]= {0};

  if (is_copy)
    snprintf(message, 512, ngettext ("creating local copy of %d image", "creating local copies of %d images", total), total);
  else
    snprintf(message, 512, ngettext ("removing local copy of %d image", "removing local copies of %d images", total), total);

  dt_control_log(message);

  dt_tag_new("darktable|local-copy",&tagid);

  /* create a cancellable bgjob ui template */
  const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message);
  dt_control_backgroundjobs_set_cancellable(darktable.control, jid, job);
  const dt_control_t *control = darktable.control;

  while(t && dt_control_job_get_state(job) != DT_JOB_STATE_CANCELLED)
  {
    imgid = GPOINTER_TO_INT(t->data);
    if (GPOINTER_TO_INT(job->user_data) == 1)
    {
      dt_image_local_copy_set(imgid);
      dt_tag_attach(tagid, imgid);
    }
    else
    {
      dt_image_local_copy_reset(imgid);
      dt_tag_detach(tagid, imgid);
    }
    t = g_list_delete_link(t, t);

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

  dt_control_backgroundjobs_destroy(control, jid);
  dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED);
  return 0;
}
Beispiel #6
0
static void
new_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;
  const gchar *tag = gtk_entry_get_text(d->entry);
  /* create new tag */
  guint tid=0;
  dt_tag_new(tag, &tid);

  /** attach tag to selected images  */
  dt_tag_attach(tid,-1);
  dt_image_synch_xmp(-1);

  update(self, 1);
}
Beispiel #7
0
static void
entry_activated (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;
  const gchar *tag = gtk_entry_get_text(d->entry);
  if(!tag || tag[0] == '\0') return;

  /* create new tag */
  guint tid=0;
  dt_tag_new(tag, &tid);

  /** attach tag to selected images  */
  dt_tag_attach(tid,-1);
  dt_image_synch_xmp(-1);

  update(self, 1);
  update(self, 0);
  gtk_entry_set_text(d->entry, "");
}
Beispiel #8
0
static void attach_selected_tag(dt_lib_module_t *self, dt_lib_tagging_t *d)
{
  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)
     && !gtk_tree_model_get_iter_first(model, &iter))
    return;
  guint tagid;
  gtk_tree_model_get(model, &iter, DT_LIB_TAGGING_COL_ID, &tagid, -1);

  int imgsel = -1;
  if(tagid <= 0) return;

  imgsel = dt_view_get_image_to_act_on();

  dt_tag_attach(tagid, imgsel);
  dt_image_synch_xmp(imgsel);

  dt_control_signal_raise(darktable.signals, DT_SIGNAL_TAG_CHANGED);
}
Beispiel #9
0
static void
attach_selected_tag(dt_lib_module_t *self, dt_lib_tagging_t *d)
{
  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) &&
      !gtk_tree_model_get_iter_first(model, &iter)) return;
  guint tagid;
  gtk_tree_model_get (model, &iter,
                      DT_LIB_TAGGING_COL_ID, &tagid,
                      -1);

  int imgsel = -1;
  if(tagid <= 0) return;

  DT_CTL_GET_GLOBAL(imgsel, lib_image_mouse_over_id);

  dt_tag_attach(tagid,imgsel);
  dt_image_synch_xmp(imgsel);
}
Beispiel #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);
    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;
}
Beispiel #11
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 */
    int32_t offs = 0;
    DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT MAX(num)+1 FROM 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 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 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);

    /* add tag */
    guint tagid=0;
    gchar ntag[512]= {0};
    g_snprintf(ntag,512,"darktable|style|%s",name);
    if (dt_tag_new(ntag,&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();
  }
}
Beispiel #12
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);
    }
  }
}
Beispiel #13
0
int32_t dt_control_export_job_run(dt_job_t *job)
{
  long int imgid = -1;
  dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param;
  dt_control_export_t *settings = (dt_control_export_t*)t1->data;
  GList *t = t1->index;
  const int total = g_list_length(t);
  int size = 0;
  dt_imageio_module_format_t  *mformat  = dt_imageio_get_format_by_index(settings->format_index);
  g_assert(mformat);
  dt_imageio_module_storage_t *mstorage = dt_imageio_get_storage_by_index(settings->storage_index);
  g_assert(mstorage);

  // Get max dimensions...
  uint32_t w,h,fw,fh,sw,sh;
  fw=fh=sw=sh=0;
  mstorage->dimension(mstorage, &sw,&sh);
  mformat->dimension(mformat, &fw,&fh);

  if( sw==0 || fw==0) w=sw>fw?sw:fw;
  else w=sw<fw?sw:fw;

  if( sh==0 || fh==0) h=sh>fh?sh:fh;
  else h=sh<fh?sh:fh;

  // get shared storage param struct (global sequence counter, one picasa connection etc)
  dt_imageio_module_data_t *sdata = mstorage->get_params(mstorage, &size);
  if(sdata == NULL)
  {
    dt_control_log(_("failed to get parameters from storage module, aborting export.."));
    g_free(t1->data);
    return 1;
  }
  dt_control_log(ngettext ("exporting %d image..", "exporting %d images..", total), total);
  char message[512]= {0};
  snprintf(message, 512, ngettext ("exporting %d image to %s", "exporting %d images to %s", total), total, mstorage->name() );

  /* create a cancellable bgjob ui template */
  const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message );
  dt_control_backgroundjobs_set_cancellable(darktable.control, jid, job);
  const dt_control_t *control = darktable.control;

  double fraction=0;
#ifdef _OPENMP
  // limit this to num threads = num full buffers - 1 (keep one for darkroom mode)
  // use min of user request and mipmap cache entries
  const int full_entries = dt_conf_get_int ("parallel_export");
  // GCC won't accept that this variable is used in a macro, considers
  // it set but not used, which makes for instance Fedora break.
  const __attribute__((__unused__)) int num_threads = MAX(1, MIN(full_entries, 8));
#if !defined(__SUNOS__) && !defined(__NetBSD__)
  #pragma omp parallel default(none) private(imgid, size) shared(control, fraction, w, h, stderr, mformat, mstorage, t, sdata, job, jid, darktable, settings) num_threads(num_threads) if(num_threads > 1)
#else
  #pragma omp parallel private(imgid, size) shared(control, fraction, w, h, mformat, mstorage, t, sdata, job, jid, darktable, settings) num_threads(num_threads) if(num_threads > 1)
#endif
  {
#endif
    // get a thread-safe fdata struct (one jpeg struct per thread etc):
    dt_imageio_module_data_t *fdata = mformat->get_params(mformat, &size);
    fdata->max_width = settings->max_width;
    fdata->max_height = settings->max_height;
    fdata->max_width = (w!=0 && fdata->max_width >w)?w:fdata->max_width;
    fdata->max_height = (h!=0 && fdata->max_height >h)?h:fdata->max_height;
    strcpy(fdata->style,settings->style);
    int num = 0;
    // Invariant: the tagid for 'darktable|changed' will not change while this function runs. Is this a sensible assumption?
    guint tagid = 0,
          etagid = 0;
    dt_tag_new("darktable|changed",&tagid);
    dt_tag_new("darktable|exported",&etagid);

    while(t && dt_control_job_get_state(job) != DT_JOB_STATE_CANCELLED)
    {
#ifdef _OPENMP
      #pragma omp critical
#endif
      {
        if(!t)
          imgid = 0;
        else
        {
          imgid = (long int)t->data;
          t = g_list_delete_link(t, t);
          num = total - g_list_length(t);
        }
      }
      // remove 'changed' tag from image
      dt_tag_detach(tagid, imgid);
      // make sure the 'exported' tag is set on the image
      dt_tag_attach(etagid, imgid);
      // check if image still exists:
      char imgfilename[DT_MAX_PATH_LEN];
      const dt_image_t *image = dt_image_cache_read_get(darktable.image_cache, (int32_t)imgid);
      if(image)
      {
        dt_image_full_path(image->id, imgfilename, DT_MAX_PATH_LEN);
        if(!g_file_test(imgfilename, G_FILE_TEST_IS_REGULAR))
        {
          dt_control_log(_("image `%s' is currently unavailable"), image->filename);
          fprintf(stderr, _("image `%s' is currently unavailable"), imgfilename);
          // dt_image_remove(imgid);
          dt_image_cache_read_release(darktable.image_cache, image);
        }
        else
        {
          dt_image_cache_read_release(darktable.image_cache, image);
          mstorage->store(sdata, imgid, mformat, fdata, num, total, settings->high_quality);
        }
      }
#ifdef _OPENMP
      #pragma omp critical
#endif
      {
        fraction+=1.0/total;
        dt_control_backgroundjobs_progress(control, jid, fraction);
      }
    }
#ifdef _OPENMP
    #pragma omp barrier
    #pragma omp master
#endif
    {
      dt_control_backgroundjobs_destroy(control, jid);
      if(mstorage->finalize_store) mstorage->finalize_store(mstorage, sdata);
      mstorage->free_params(mstorage, sdata);
    }
    // all threads free their fdata
    mformat->free_params (mformat, fdata);
#ifdef _OPENMP
  }
#endif
  g_free(t1->data);
  return 0;
}
Beispiel #14
0
void leave(dt_view_t *self)
{
  /* disconnect from filmstrip image activate */
  dt_control_signal_disconnect(darktable.signals,
                               G_CALLBACK(_view_darkroom_filmstrip_activate_callback),
                               (gpointer)self);

  /* disconnect from pipe finish signal */
  dt_control_signal_disconnect(darktable.signals,
                               G_CALLBACK(_darkroom_ui_pipe_finish_signal_callback),
                               (gpointer)self);

  // store groups for next time:
  dt_conf_set_int("plugins/darkroom/groups", dt_dev_modulegroups_get(darktable.develop));

  // store last active plugin:
  if(darktable.develop->gui_module)
    dt_conf_set_string("plugins/darkroom/active", darktable.develop->gui_module->op);
  else
    dt_conf_set_string("plugins/darkroom/active", "");

  dt_develop_t *dev = (dt_develop_t *)self->data;
  // tag image as changed
  // TODO: only tag the image when there was a real change.
  guint tagid = 0;
  dt_tag_new("darktable|changed",&tagid);
  dt_tag_attach(tagid, dev->image_storage.id);
  // commit image ops to db
  dt_dev_write_history(dev);

  // be sure light table will regenerate the thumbnail:
  // TODO: only if changed!
  // if()
  {
    dt_mipmap_cache_remove(darktable.mipmap_cache, dev->image_storage.id);
    // dump new xmp data
    dt_image_synch_xmp(dev->image_storage.id);
  }

  // clear gui.
  dev->gui_leaving = 1;
  dt_pthread_mutex_lock(&dev->history_mutex);
  dt_dev_pixelpipe_cleanup_nodes(dev->pipe);
  dt_dev_pixelpipe_cleanup_nodes(dev->preview_pipe);

  while(dev->history)
  {
    dt_dev_history_item_t *hist = (dt_dev_history_item_t *)(dev->history->data);
    // printf("removing history item %d - %s, data %f %f\n", hist->module->instance, hist->module->op, *(float *)hist->params, *((float *)hist->params+1));
    free(hist->params);
    hist->params = NULL;
    free(hist);
    dev->history = g_list_delete_link(dev->history, dev->history);
  }

  while(dev->iop)
  {
    dt_iop_module_t *module = (dt_iop_module_t *)(dev->iop->data);
    if (!dt_iop_is_hidden(module))
      dt_iop_gui_cleanup_module(module);

    dt_dev_cleanup_module_accels(module);
    module->accel_closures = NULL;
    dt_iop_cleanup_module(module) ;
    free(module);
    dev->iop = g_list_delete_link(dev->iop, dev->iop);
  }

  dt_pthread_mutex_unlock(&dev->history_mutex);

  dt_print(DT_DEBUG_CONTROL, "[run_job-] 11 %f in darkroom mode\n", dt_get_wtime());
}