Exemplo n.º 1
0
int32_t dt_camera_capture_job_run(dt_job_t *job)
{
  dt_camera_capture_t *t=(dt_camera_capture_t*)job->param;
  int total = t->brackets ? t->count * t->brackets : t->count;
  char message[512]= {0};
  double fraction=0;
  snprintf(message, 512, ngettext ("capturing %d image", "capturing %d images", total), total );
  
  /* try to get exp program mode for nikon */
  char *expprogram = (char *)dt_camctl_camera_get_property(darktable.camctl, NULL, "expprogram");
  
  /* if fail, lets try fetching mode for cannon */
  if(!expprogram) 
    expprogram = (char *)dt_camctl_camera_get_property(darktable.camctl, NULL, "autoexposuremode");

  /* Fetch all values for shutterspeed and initialize current value */
  GList *values=NULL;
  gconstpointer orginal_value=NULL;
  const char *cvalue = dt_camctl_camera_get_property(darktable.camctl, NULL, "shutterspeed");
  const char *value = dt_camctl_camera_property_get_first_choice(darktable.camctl, NULL, "shutterspeed");
  
  /* get values for bracketing */
  if (t->brackets && expprogram && expprogram[0]=='M' && value && cvalue)
  {
    do
    {
      // Add value to list
      values = g_list_append(values, g_strdup(value));
      // Check if current values is the same as orginal value, then lets store item ptr
      if (strcmp(value,cvalue) == 0)
        orginal_value = g_list_last(values)->data;
    }
    while ((value = dt_camctl_camera_property_get_next_choice(darktable.camctl, NULL, "shutterspeed")) != NULL);
  }
  else
  {
    /* if this was an itended bracket capture bail out */
    if(t->brackets)
    {
      dt_control_log(_("please set your camera to manual mode first!"));
      return 1;
    }
  }

  /* create the bgjob plate */
  const guint *jid  = dt_control_backgroundjobs_create(darktable.control, 0, message);

  GList *current_value = g_list_find(values,orginal_value);
  for(int i=0; i<t->count; i++)
  {
    for(int b=0; b<(t->brackets*2)+1; b++)
    {
      // If bracket capture, lets set change shutterspeed
      if (t->brackets)
      {
        if (b == 0)
        {
          // First bracket, step down time with (steps*brackets), also check so we never set the longest shuttertime which would be bulb mode
          for(int s=0; s<(t->steps*t->brackets); s++)
            if (g_list_next(current_value) && g_list_next(g_list_next(current_value)))
              current_value = g_list_next(current_value);
        }
        else
        {
          // Step up with (steps)
          for(int s=0; s<t->steps; s++)
            if(g_list_previous(current_value))
              current_value = g_list_previous(current_value);
        }
      }

      // set the time property for bracked capture
      if (t->brackets && current_value)
        dt_camctl_camera_set_property(darktable.camctl, NULL, "shutterspeed", current_value->data);

      // Capture image
      dt_camctl_camera_capture(darktable.camctl,NULL);

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

    // lets reset to orginal value before continue
    if (t->brackets)
    {
      current_value = g_list_find(values,orginal_value);
      dt_camctl_camera_set_property(darktable.camctl, NULL, "shutterspeed", current_value->data);
    }

    // Delay if active
    if(t->delay)
      g_usleep(t->delay*G_USEC_PER_SEC);

  }

  dt_control_backgroundjobs_destroy(darktable.control, jid);


  // free values
  if(values)
  {
    for(int i=0; i<g_list_length(values); i++)
      g_free(g_list_nth_data(values,i));

    g_list_free(values);
  }

  return 0;
}
Exemplo n.º 2
0
static void
_init_f(
    float          *out,
    uint32_t       *width,
    uint32_t       *height,
    const uint32_t  imgid)
{
  const uint32_t wd = *width, ht = *height;

  /* do not even try to process file if it isnt available */
  char filename[2048] = {0};
  dt_image_full_path(imgid, filename, 2048);
  if (strlen(filename) == 0 || !g_file_test(filename, G_FILE_TEST_EXISTS))
  {
    *width = *height = 0;
    return;
  }

  dt_mipmap_buffer_t buf;
  dt_mipmap_cache_read_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING);

  // lock image after we have the buffer, we might need to lock the image struct for
  // writing during raw loading, to write to width/height.
  const dt_image_t *image = dt_image_cache_read_get(darktable.image_cache, imgid);

  dt_iop_roi_t roi_in, roi_out;
  roi_in.x = roi_in.y = 0;
  roi_in.width = image->width;
  roi_in.height = image->height;
  roi_in.scale = 1.0f;
  
  roi_out.x = roi_out.y = 0;
  roi_out.scale = fminf(wd/(float)image->width, ht/(float)image->height);
  roi_out.width  = roi_out.scale * roi_in.width;
  roi_out.height = roi_out.scale * roi_in.height;

  if(!buf.buf)
  {
    dt_control_log(_("image `%s' is not available!"), image->filename);
    dt_image_cache_read_release(darktable.image_cache, image);
    *width = *height = 0;
    return;
  }

  assert(!buffer_is_broken(&buf));

  if(image->filters)
  {
    // demosaic during downsample
    if(image->bpp == sizeof(float))
      dt_iop_clip_and_zoom_demosaic_half_size_f(
          out, (const float *)buf.buf,
          &roi_out, &roi_in, roi_out.width, roi_in.width,
          dt_image_flipped_filter(image), 1.0f);
    else
      dt_iop_clip_and_zoom_demosaic_half_size(
          out, (const uint16_t *)buf.buf,
          &roi_out, &roi_in, roi_out.width, roi_in.width,
          dt_image_flipped_filter(image));
  }
  else
  {
    // downsample
    dt_iop_clip_and_zoom(out, (const float *)buf.buf,
          &roi_out, &roi_in, roi_out.width, roi_in.width);
  }
  dt_image_cache_read_release(darktable.image_cache, image);
  dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf);

  *width  = roi_out.width;
  *height = roi_out.height;
}
Exemplo n.º 3
0
int32_t dt_camera_import_job_run(dt_job_t *job)
{
  dt_camera_import_t *t = (dt_camera_import_t *)job->param;
  dt_control_log(_("starting to import images from camera"));

  // Setup a new filmroll to import images to....
  t->film=(dt_film_t*)g_malloc(sizeof(dt_film_t));

  dt_film_init(t->film);

  gchar* fixed_path = dt_util_fix_path(t->path);
  g_free(t->path);
  t->path = fixed_path;
  dt_variables_expand( t->vp, t->path, FALSE );
  sprintf(t->film->dirname,"%s",dt_variables_get_result(t->vp));

  dt_pthread_mutex_lock(&t->film->images_mutex);
  t->film->ref++;
  dt_pthread_mutex_unlock(&t->film->images_mutex);

  // Create recursive directories, abort if no access
  if( g_mkdir_with_parents(t->film->dirname,0755) == -1 )
  {
    dt_control_log(_("failed to create import path `%s', import aborted."), t->film->dirname);
    return 1;
  }

  // Import path is ok, lets actually create the filmroll in database..
  if(dt_film_new(t->film,t->film->dirname) > 0)
  {
    int total = g_list_length( t->images );
    char message[512]= {0};
    sprintf(message, ngettext ("importing %d image from camera", "importing %d images from camera", total), total );
    t->bgj = dt_control_backgroundjobs_create(darktable.control, 0, message);

    // Switch to new filmroll
    dt_film_open(t->film->id);
    dt_ctl_switch_mode_to(DT_LIBRARY);

    // register listener
    dt_camctl_listener_t listener= {0};
    listener.data=t;
    listener.image_downloaded=_camera_image_downloaded;
    listener.request_image_path=_camera_import_request_image_path;
    listener.request_image_filename=_camera_import_request_image_filename;

    //  start download of images
    dt_camctl_register_listener(darktable.camctl,&listener);
    dt_camctl_import(darktable.camctl,t->camera,t->images,dt_conf_get_bool("plugins/capture/camera/import/delete_originals"));
    dt_camctl_unregister_listener(darktable.camctl,&listener);
    dt_control_backgroundjobs_destroy(darktable.control, t->bgj);
    dt_variables_params_destroy(t->vp);
  }
  else
    dt_control_log(_("failed to create filmroll for camera import, import aborted."));

  dt_pthread_mutex_lock(&t->film->images_mutex);
  t->film->ref--;
  dt_pthread_mutex_unlock(&t->film->images_mutex);
  return 0;
}
Exemplo n.º 4
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.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_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED);
}
Exemplo n.º 5
0
int dt_gui_hist_dialog_new(dt_gui_hist_dialog_t *d, int imgid, gboolean iscopy)
{
  int res;
  GtkWidget *window = dt_ui_main_window(darktable.gui->ui);

  GtkDialog *dialog = GTK_DIALOG(gtk_dialog_new_with_buttons(
      _("select parts"), GTK_WINDOW(window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, _("_cancel"),
      GTK_RESPONSE_CANCEL, _("select _all"), GTK_RESPONSE_YES, _("select _none"), GTK_RESPONSE_NONE, _("_ok"),
      GTK_RESPONSE_OK, NULL));
#ifdef GDK_WINDOWING_QUARTZ
  dt_osx_disallow_fullscreen(GTK_WIDGET(dialog));
#endif

  GtkContainer *content_area = GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
  GtkBox *box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 3));
  gtk_widget_set_margin_start(GTK_WIDGET(box), DT_PIXEL_APPLY_DPI(5));
  gtk_widget_set_margin_end(GTK_WIDGET(box), DT_PIXEL_APPLY_DPI(5));
  gtk_widget_set_margin_top(GTK_WIDGET(box), DT_PIXEL_APPLY_DPI(5));
  gtk_widget_set_margin_bottom(GTK_WIDGET(box), DT_PIXEL_APPLY_DPI(5));
  gtk_container_add(content_area, GTK_WIDGET(box));

  /* create the list of items */
  d->items = GTK_TREE_VIEW(gtk_tree_view_new());
  GtkListStore *liststore
      = gtk_list_store_new(DT_HIST_ITEMS_NUM_COLS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_UINT);

  /* enabled */
  GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();
  gtk_cell_renderer_toggle_set_activatable(GTK_CELL_RENDERER_TOGGLE(renderer), TRUE);
  g_object_set_data(G_OBJECT(renderer), "column", (gint *)DT_HIST_ITEMS_COL_ENABLED);
  g_signal_connect(renderer, "toggled", G_CALLBACK(_gui_hist_item_toggled), d);

  gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(d->items), -1, _("include"), renderer, "active",
                                              DT_HIST_ITEMS_COL_ENABLED, NULL);

  /* name */
  renderer = gtk_cell_renderer_text_new();
  g_object_set_data(G_OBJECT(renderer), "column", (gint *)DT_HIST_ITEMS_COL_NAME);
  g_object_set(renderer, "xalign", 0.0, (gchar *)0);
  gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(d->items), -1, _("item"), renderer, "text",
                                              DT_HIST_ITEMS_COL_NAME, NULL);


  gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(d->items)), GTK_SELECTION_SINGLE);
  gtk_tree_view_set_model(GTK_TREE_VIEW(d->items), GTK_TREE_MODEL(liststore));

  gtk_box_pack_start(box, GTK_WIDGET(d->items), TRUE, TRUE, 0);

  /* fill list with history items */
  GtkTreeIter iter;
  GList *items = dt_history_get_items(imgid, FALSE);
  if(items)
  {
    do
    {
      dt_history_item_t *item = (dt_history_item_t *)items->data;

      gtk_list_store_append(GTK_LIST_STORE(liststore), &iter);
      gtk_list_store_set(GTK_LIST_STORE(liststore), &iter, DT_HIST_ITEMS_COL_ENABLED,
                         iscopy ? TRUE : _gui_is_set(d->selops, item->num), DT_HIST_ITEMS_COL_NAME,
                         item->name, DT_HIST_ITEMS_COL_NUM, (guint)item->num, -1);

    } while((items = g_list_next(items)));
    g_list_free_full(items, dt_history_item_free);
  }
  else
  {
    dt_control_log(_("can't copy history out of unaltered image"));
    return GTK_RESPONSE_CANCEL;
  }

  g_object_unref(liststore);

  g_signal_connect(dialog, "response", G_CALLBACK(_gui_hist_copy_response), d);

  gtk_widget_show_all(GTK_WIDGET(dialog));

  while(1)
  {
    res = gtk_dialog_run(GTK_DIALOG(dialog));
    if(res == GTK_RESPONSE_CANCEL || res == GTK_RESPONSE_DELETE_EVENT || res == GTK_RESPONSE_OK) break;
  }

  gtk_widget_destroy(GTK_WIDGET(dialog));
  return res;
}
Exemplo n.º 6
0
int
store (dt_imageio_module_storage_t *self, dt_imageio_module_data_t *sdata, const int imgid, dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata,
       const int num, const int total, const gboolean high_quality)
{
  gint result = 0;
  dt_storage_flickr_params_t *p=(dt_storage_flickr_params_t *)sdata;
  flickcurl_upload_status *photo_status;
  gint tags=0;

  const char *ext = format->extension(fdata);

  // Let's upload image...

  /* construct a temporary file name */
  char fname[PATH_MAX]= {0};
  dt_loc_get_tmp_dir (fname, sizeof(fname));
  g_strlcat (fname,"/darktable.XXXXXX.", sizeof(fname));
  g_strlcat(fname, ext, sizeof(fname));

  char *caption = NULL;
  char *description = NULL;


  gint fd=g_mkstemp(fname);
  fprintf(stderr,"tempfile: %s\n",fname);
  if(fd==-1)
  {
    dt_control_log("failed to create temporary image for flickr export");
    return 1;
  }
  close(fd);
  const dt_image_t *img = dt_image_cache_read_get(darktable.image_cache, imgid);

  // If title is not existing, then use the filename without extension. If not, then use title instead
  GList *title = dt_metadata_get(img->id, "Xmp.dc.title", NULL);
  if(title != NULL)
  {
    caption = g_strdup(title->data);
    g_list_free_full(title, &g_free);
  }
  else
  {
    caption = g_path_get_basename(img->filename);
    (g_strrstr(caption,"."))[0]='\0'; // chop extension...
  }

  GList *desc = dt_metadata_get(img->id, "Xmp.dc.description", NULL);
  if(desc != NULL)
  {
    description = desc->data;
  }
  dt_image_cache_read_release(darktable.image_cache, img);

  if(dt_imageio_export(imgid, fname, format, fdata, high_quality,FALSE,self,sdata) != 0)
  {
    fprintf(stderr, "[imageio_storage_flickr] could not export to file: `%s'!\n", fname);
    dt_control_log(_("could not export to file `%s'!"), fname);
    result = 1;
    goto cleanup;
  }

#ifdef _OPENMP
  #pragma omp critical
#endif
  {
    //TODO: Check if this could be done in threads, so we enhance export time by using
    //      upload time for one image to export another image to disk.
    // Upload image
    // Do we export tags?
    if( p->export_tags == TRUE )
      tags = imgid;
    photo_status = _flickr_api_upload_photo( p, fname, caption, description, tags );
  }

  if( !photo_status )
  {
    fprintf(stderr, "[imageio_storage_flickr] could not upload to flickr!\n");
    dt_control_log(_("could not upload to flickr!"));
    result = 1;
    goto cleanup;
  }

//  int fail = 0;
  // A photoset is only created if we have an album title set
  if( p->flickr_api->current_album == NULL && p->flickr_api->new_album == TRUE)
  {
    char *photoset_id;
    photoset_id = _flickr_api_create_photoset(p->flickr_api, photo_status->photoid);

    if( photoset_id == NULL)
    {
      dt_control_log("failed to create flickr album");
//      fail = 1;
    }
    else
    {
//      p->flickr_api->new_album = FALSE;
      p->flickr_api->current_album = flickcurl_photosets_getInfo(p->flickr_api->fc,photoset_id);
    }
  }

//  if(fail) return 1;
// TODO: What to do if photoset creation fails?

  // Add to gallery, if needed
  if (p->flickr_api->current_album != NULL && p->flickr_api->new_album != TRUE)
  {
    flickcurl_photosets_addPhoto (p->flickr_api->fc, p->flickr_api->current_album->id, photo_status->photoid);
    // TODO: Check for errors adding photo to gallery
  }
  else
  {
    if (p->flickr_api->current_album != NULL && p->flickr_api->new_album == TRUE)
    {
      p->flickr_api->new_album = FALSE;
    }
  }

cleanup:

  // And remove from filesystem..
  unlink( fname );
  g_free( caption );
  if(desc)
    g_list_free_full(desc, &g_free);

  if (!result)
  {
    //this makes sense only if the export was successful
    dt_control_log(_("%d/%d exported to flickr webalbum"), num, total );
  }
  return result;
}
Exemplo n.º 7
0
void
dt_styles_create_from_style (const char *name, const char *newname, const char *description, GList *filter)
{
    sqlite3_stmt *stmt;
    int id=0;
    int oldid=0;

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

    /* create the style header */
    if (!dt_styles_create_style_header(newname, description)) return;

    if ((id=dt_styles_get_id_by_name(newname)) != 0)
    {
        if (filter)
        {
            GList *list=filter;
            char tmp[64];
            char include[2048]= {0};
            g_strlcat(include,"num in (", 2048);
            do
            {
                if(list!=g_list_first(list))
                    g_strlcat(include,",", 2048);
                sprintf(tmp,"%ld",(long int)list->data);
                g_strlcat(include,tmp, 2048);
            }
            while ((list=g_list_next(list)));
            g_strlcat(include,")", 2048);
            char query[4096]= {0};

            sprintf(query,"insert into style_items (styleid,num,module,operation,op_params,enabled,blendop_params,blendop_version) select ?1, num,module,operation,op_params,enabled,blendop_params,blendop_version from style_items where styleid=?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 style_items (styleid,num,module,operation,op_params,enabled,blendop_params,blendop_version) select ?1, num,module,operation,op_params,enabled,blendop_params,blendop_version from style_items where style_id=?2", -1, &stmt, NULL);
        DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
        DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, oldid);
        sqlite3_step (stmt);
        sqlite3_finalize (stmt);

        /* backup style to disk */
        char stylesdir[1024];
        dt_loc_get_user_config_dir(stylesdir, 1024);
        g_strlcat(stylesdir,"/styles",1024);
        g_mkdir_with_parents(stylesdir,00755);

        dt_styles_save_to_file(newname,stylesdir,FALSE);

        char tmp_accel[1024];
        gchar* tmp_name = g_strdup(newname); // freed by _destro_style_shortcut_callback
        snprintf(tmp_accel,1024,"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_log(_("style named '%s' successfully created"),newname);
    }
}
Exemplo n.º 8
0
int dt_control_key_pressed_override(guint key, guint state)
{
  dt_control_accels_t *accels = &darktable.control->accels;

  // TODO: if darkroom mode
  // did a : vim-style command start?
  static GList *autocomplete = NULL;
  static char vimkey_input[256];
  if(darktable.control->vimkey_cnt)
  {
    guchar unichar = gdk_keyval_to_unicode(key);
    if(key == GDK_KEY_Return)
    {
      if(!strcmp(darktable.control->vimkey, ":q"))
      {
        dt_control_quit();
      }
      else
      {
        dt_bauhaus_vimkey_exec(darktable.control->vimkey);
      }
      darktable.control->vimkey[0] = 0;
      darktable.control->vimkey_cnt = 0;
      dt_control_log_ack_all();
      g_list_free(autocomplete);
      autocomplete = NULL;
    }
    else if(key == GDK_KEY_Escape)
    {
      darktable.control->vimkey[0] = 0;
      darktable.control->vimkey_cnt = 0;
      dt_control_log_ack_all();
      g_list_free(autocomplete);
      autocomplete = NULL;
    }
    else if(key == GDK_KEY_BackSpace)
    {
      darktable.control->vimkey_cnt
          -= (darktable.control->vimkey + darktable.control->vimkey_cnt)
             - g_utf8_prev_char(darktable.control->vimkey + darktable.control->vimkey_cnt);
      darktable.control->vimkey[darktable.control->vimkey_cnt] = 0;
      if(darktable.control->vimkey_cnt == 0)
        dt_control_log_ack_all();
      else
        dt_control_log(darktable.control->vimkey);
      g_list_free(autocomplete);
      autocomplete = NULL;
    }
    else if(key == GDK_KEY_Tab)
    {
      // TODO: also support :preset and :get?
      // auto complete:
      if(darktable.control->vimkey_cnt < 5)
      {
        snprintf(darktable.control->vimkey, sizeof(darktable.control->vimkey), ":set ");
        darktable.control->vimkey_cnt = 5;
      }
      else if(!autocomplete)
      {
        // TODO: handle '.'-separated things separately
        // this is a static list, and tab cycles through the list
        g_strlcpy(vimkey_input, darktable.control->vimkey + 5, sizeof(vimkey_input));
        autocomplete = dt_bauhaus_vimkey_complete(darktable.control->vimkey + 5);
        autocomplete = g_list_append(autocomplete, vimkey_input); // remember input to cycle back
      }
      if(autocomplete)
      {
        // pop first.
        // the paths themselves are owned by bauhaus,
        // no free required.
        snprintf(darktable.control->vimkey, sizeof(darktable.control->vimkey), ":set %s",
                 (char *)autocomplete->data);
        autocomplete = g_list_remove(autocomplete, autocomplete->data);
        darktable.control->vimkey_cnt = strlen(darktable.control->vimkey);
      }
      dt_control_log(darktable.control->vimkey);
    }
    else if(g_unichar_isprint(unichar)) // printable unicode character
    {
      gchar utf8[6];
      gint char_width = g_unichar_to_utf8(unichar, utf8);
      if(darktable.control->vimkey_cnt + 1 + char_width < 256)
      {
        g_utf8_strncpy(darktable.control->vimkey + darktable.control->vimkey_cnt, utf8, 1);
        darktable.control->vimkey_cnt += char_width;
        darktable.control->vimkey[darktable.control->vimkey_cnt] = 0;
        dt_control_log(darktable.control->vimkey);
        g_list_free(autocomplete);
        autocomplete = NULL;
      }
    }
    else if(key == GDK_KEY_Up)
    {
      // TODO: step history up and copy to vimkey
    }
    else if(key == GDK_KEY_Down)
    {
      // TODO: step history down and copy to vimkey
    }
    return 1;
  }
  else if(key == ':' && darktable.control->key_accelerators_on)
  {
    darktable.control->vimkey[0] = ':';
    darktable.control->vimkey[1] = 0;
    darktable.control->vimkey_cnt = 1;
    dt_control_log(darktable.control->vimkey);
    return 1;
  }

  /* check if key accelerators are enabled*/
  if(darktable.control->key_accelerators_on != 1) return 0;

  if(key == accels->global_sideborders.accel_key && state == accels->global_sideborders.accel_mods)
  {
    /* toggle panel viewstate */
    dt_ui_toggle_panels_visibility(darktable.gui->ui);

    /* trigger invalidation of centerview to reprocess pipe */
    dt_dev_invalidate(darktable.develop);
    gtk_widget_queue_draw(dt_ui_center(darktable.gui->ui));
    return 1;
  }
  else if(key == accels->global_header.accel_key && state == accels->global_header.accel_mods)
  {
    char key[512];
    const dt_view_t *cv = dt_view_manager_get_current_view(darktable.view_manager);

    /* do nothing if in collapse panel state
       TODO: reconsider adding this check to ui api */
    g_snprintf(key, sizeof(key), "%s/ui/panel_collaps_state", cv->module_name);
    if(dt_conf_get_int(key)) return 0;

    /* toggle the header visibility state */
    g_snprintf(key, sizeof(key), "%s/ui/show_header", cv->module_name);
    gboolean header = !dt_conf_get_bool(key);
    dt_conf_set_bool(key, header);

    /* show/hide the actual header panel */
    dt_ui_panel_show(darktable.gui->ui, DT_UI_PANEL_TOP, header, TRUE);
    gtk_widget_queue_draw(dt_ui_center(darktable.gui->ui));
    return 1;
  }
  return 0;
}
Exemplo n.º 9
0
int
dt_history_copy_and_paste_on_image (int32_t imgid, int32_t dest_imgid, gboolean merge, GList *ops)
{
  sqlite3_stmt *stmt;
  if(imgid==dest_imgid) return 1;

  if(imgid==-1)
  {
    dt_control_log(_("you need to copy history from an image before you paste it onto another"));
    return 1;
  }

  /* if merge onto history stack, lets find history offest in destination image */
  int32_t offs = 0;
  if (merge)
  {
    /* apply on top of history stack */
    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, dest_imgid);
    if (sqlite3_step (stmt) == SQLITE_ROW) offs = sqlite3_column_int (stmt, 0);
  }
  else
  {
    /* replace history stack */
    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, dest_imgid);
    sqlite3_step (stmt);
  }
  sqlite3_finalize (stmt);

  //  prepare SQL request
  char req[2048];
  strcpy (req, "insert into history (imgid, num, module, operation, op_params, enabled, blendop_params, blendop_version, multi_name, multi_priority) select ?1, num+?2, module, operation, op_params, enabled, blendop_params, blendop_version, multi_name, multi_priority from history where imgid = ?3");

  //  Add ops selection if any format: ... and num in (val1, val2)
  if (ops)
  {
    GList *l = ops;
    int first = 1;
    strcat (req, " and num in (");

    while (l)
    {
      long unsigned int value = (long unsigned int)l->data;
      char v[30];

      if (!first) strcat (req, ",");
      snprintf (v, 30, "%lu", value);
      strcat (req, v);
      first=0;
      l = g_list_next(l);
    }
    strcat (req, ")");
  }

  /* add the history items to stack offest */
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), req, -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, offs);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, imgid);
  sqlite3_step (stmt);
  sqlite3_finalize (stmt);

  if (merge && ops)
    _dt_history_cleanup_multi_instance(dest_imgid, offs);

  //we have to copy masks too
  //what to do with existing masks ?
  if (merge)
  {
    //there's very little chance that we will have same shapes id.
    //but we may want to handle this case anyway
    //and it's not trivial at all !
  }
  else
  {
    //let's remove all existing shapes
    DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "delete from mask where imgid = ?1", -1, &stmt, NULL);
    DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid);
    sqlite3_step (stmt);
    sqlite3_finalize (stmt);
  }

  //let's copy now
  strcpy (req, "insert into mask (imgid, formid, form, name, version, points, points_count, source) select ?1, formid, form, name, version, points, points_count, source from mask where imgid = ?2");
  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), req, -1, &stmt, NULL);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, dest_imgid);
  DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, imgid);
  sqlite3_step (stmt);
  sqlite3_finalize (stmt);
  
  /* if current image in develop reload history */
  if (dt_dev_is_current_image(darktable.develop, dest_imgid))
  {
    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(dest_imgid);

  dt_mipmap_cache_remove(darktable.mipmap_cache, dest_imgid);

  return 0;
}
Exemplo n.º 10
0
int32_t dt_camera_capture_job_run(dt_job_t *job)
{
  dt_camera_capture_t *t=(dt_camera_capture_t*)job->param;
  int total;
  char message[512]= {0};
  double fraction=0;

  total = t->total = t->brackets ? t->count * t->brackets : t->count;
  snprintf(message, 512, ngettext ("capturing %d image", "capturing %d images", total), total );

  pthread_mutex_init(&t->mutex, NULL);
  pthread_cond_init(&t->done, NULL);

  // register listener
  dt_camctl_listener_t *listener;
  listener = g_malloc(sizeof(dt_camctl_listener_t));
  memset(listener, 0, sizeof(dt_camctl_listener_t));
  listener->data=t;
  listener->image_downloaded=_camera_capture_image_downloaded;
  listener->request_image_path=_camera_request_image_path;
  listener->request_image_filename=_camera_request_image_filename;
  dt_camctl_register_listener(darktable.camctl, listener);

  /* try to get exp program mode for nikon */
  char *expprogram = (char *)dt_camctl_camera_get_property(darktable.camctl, NULL, "expprogram");

  /* if fail, lets try fetching mode for cannon */
  if(!expprogram)
    expprogram = (char *)dt_camctl_camera_get_property(darktable.camctl, NULL, "autoexposuremode");

  /* Fetch all values for shutterspeed and initialize current value */
  GList *values=NULL;
  gconstpointer original_value=NULL;
  const char *cvalue = dt_camctl_camera_get_property(darktable.camctl, NULL, "shutterspeed");
  const char *value = dt_camctl_camera_property_get_first_choice(darktable.camctl, NULL, "shutterspeed");

  /* get values for bracketing */
  if (t->brackets && expprogram && expprogram[0]=='M' && value && cvalue)
  {
    do
    {
      // Add value to list
      values = g_list_append(values, g_strdup(value));
      // Check if current values is the same as original value, then lets store item ptr
      if (strcmp(value,cvalue) == 0)
        original_value = g_list_last(values)->data;
    }
    while ((value = dt_camctl_camera_property_get_next_choice(darktable.camctl, NULL, "shutterspeed")) != NULL);
  }
  else
  {
    /* if this was an intended bracket capture bail out */
    if(t->brackets)
    {
      dt_control_log(_("please set your camera to manual mode first!"));
      return 1;
    }
  }

  /* create the bgjob plate */
  const guint *jid  = dt_control_backgroundjobs_create(darktable.control, 0, message);

  GList *current_value = g_list_find(values,original_value);
  for(uint32_t i=0; i<t->count; i++)
  {
    // Delay if active
    if(t->delay)
      g_usleep(t->delay*G_USEC_PER_SEC);

    for(uint32_t b=0; b<(t->brackets*2)+1; b++)
    {
      // If bracket capture, lets set change shutterspeed
      if (t->brackets)
      {
        if (b == 0)
        {
          // First bracket, step down time with (steps*brackets), also check so we never set the longest shuttertime which would be bulb mode
          for(uint32_t s=0; s<(t->steps*t->brackets); s++)
            if (g_list_next(current_value) && g_list_next(g_list_next(current_value)))
              current_value = g_list_next(current_value);
        }
        else
        {
          // Step up with (steps)
          for(uint32_t s=0; s<t->steps; s++)
            if(g_list_previous(current_value))
              current_value = g_list_previous(current_value);
        }
      }

      // set the time property for bracket capture
      if (t->brackets && current_value)
        dt_camctl_camera_set_property_string(darktable.camctl, NULL, "shutterspeed", current_value->data);

      // Capture image
      dt_camctl_camera_capture(darktable.camctl,NULL);

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

    // lets reset to original value before continue
    if (t->brackets)
    {
      current_value = g_list_find(values,original_value);
      dt_camctl_camera_set_property_string(darktable.camctl, NULL, "shutterspeed", current_value->data);
    }
  }

  /* wait for last image capture before exiting job */
  pthread_mutex_lock(&t->mutex);
  pthread_cond_wait(&t->done, &t->mutex);
  pthread_mutex_unlock(&t->mutex);
  pthread_mutex_destroy(&t->mutex);
  pthread_cond_destroy(&t->done);

  /* cleanup */
  dt_control_backgroundjobs_destroy(darktable.control, jid);
  dt_import_session_destroy(t->shared.session);
  dt_camctl_unregister_listener(darktable.camctl, listener);
  g_free(listener);

  // free values
  if(values)
  {
    g_list_free_full(values, g_free);
  }

  return 0;
}
Exemplo n.º 11
0
static void _lib_import_single_image_callback(GtkWidget *widget,gpointer user_data)
{
  GtkWidget *win = dt_ui_main_window(darktable.gui->ui);
  GtkWidget *filechooser = gtk_file_chooser_dialog_new (_("import image"),
                           GTK_WINDOW (win),
                           GTK_FILE_CHOOSER_ACTION_OPEN,
                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                           GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
                           (char *)NULL);

  gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(filechooser), TRUE);

  char *last_directory = dt_conf_get_string("ui_last/import_last_directory");
  if(last_directory != NULL)
    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER (filechooser), last_directory);

  char *cp, **extensions, ext[1024];
  GtkFileFilter *filter;
  filter = GTK_FILE_FILTER(gtk_file_filter_new());
  extensions = g_strsplit(dt_supported_extensions, ",", 100);
  for(char **i=extensions; *i!=NULL; i++)
  {
    snprintf(ext, 1024, "*.%s", *i);
    gtk_file_filter_add_pattern(filter, ext);
    gtk_file_filter_add_pattern(filter, cp=g_ascii_strup(ext, -1));
    g_free(cp);
  }
  g_strfreev(extensions);
  gtk_file_filter_set_name(filter, _("supported images"));
  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);

  filter = GTK_FILE_FILTER(gtk_file_filter_new());
  gtk_file_filter_add_pattern(filter, "*");
  gtk_file_filter_set_name(filter, _("all files"));
  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);

  GtkWidget *preview = gtk_image_new();
  gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(filechooser), preview);
  g_signal_connect(filechooser, "update-preview", G_CALLBACK (_lib_import_update_preview), preview);

  dt_lib_import_metadata_t metadata;
  gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (filechooser), _lib_import_get_extra_widget(&metadata, FALSE));

  if (gtk_dialog_run (GTK_DIALOG (filechooser)) == GTK_RESPONSE_ACCEPT)
  {
    dt_conf_set_string("ui_last/import_last_directory", gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER (filechooser)));
    _lib_import_evaluate_extra_widget(&metadata, FALSE);

    char *filename = NULL;
    dt_film_t film;
    GSList *list = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (filechooser));
    GSList *it = list;
    int id = 0;
    int filmid = 0;

    /* reset filter so that view isn't empty */
    dt_view_filter_reset(darktable.view_manager, TRUE);

    while(it)
    {
      filename = (char *)it->data;
      gchar *directory = g_path_get_dirname((const gchar *)filename);
      filmid = dt_film_new(&film, directory);
      id = dt_image_import(filmid, filename, TRUE);
      if(!id) dt_control_log(_("error loading file `%s'"), filename);
      g_free (filename);
      g_free (directory);
      it = g_slist_next(it);
    }

    if(id)
    {
      dt_film_open(filmid);
      // make sure buffers are loaded (load full for testing)
      dt_mipmap_buffer_t buf;
      dt_mipmap_cache_read_get(darktable.mipmap_cache, &buf, id, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING);
      if(!buf.buf)
      {
        dt_control_log(_("file has unknown format!"));
      }
      else
      {
        dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf);
        dt_control_set_mouse_over_id(id);
        dt_ctl_switch_mode_to(DT_DEVELOP);
      }
    }
  }
  gtk_widget_destroy(metadata.frame);
  gtk_widget_destroy (filechooser);
  gtk_widget_queue_draw(dt_ui_center(darktable.gui->ui));
}
Exemplo n.º 12
0
int store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *sdata, const int imgid,
          dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata, const int num, const int total,
          const gboolean high_quality, const gboolean upscale, dt_colorspaces_color_profile_type_t icc_type,
          const gchar *icc_filename, dt_iop_color_intent_t icc_intent)
{
  dt_storage_piwigo_gui_data_t *ui = self->gui_data;

  gint result = 0;

  const char *ext = format->extension(fdata);

  // Let's upload image...

  /* construct a temporary file name */
  char fname[PATH_MAX] = { 0 };
  dt_loc_get_tmp_dir(fname, sizeof(fname));
  g_strlcat(fname, "/darktable.XXXXXX.", sizeof(fname));
  g_strlcat(fname, ext, sizeof(fname));

  char *caption = NULL;
  char *description = NULL;
  char *author = NULL;

  gint fd = g_mkstemp(fname);
  if(fd == -1)
  {
    dt_control_log("failed to create temporary image for piwigo export");
    fprintf(stderr, "failed to create tempfile: %s\n", fname);
    return 1;
  }
  close(fd);
  const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r');

  // If title is not existing, then use the filename without extension. If not, then use title instead
  GList *title = dt_metadata_get(img->id, "Xmp.dc.title", NULL);
  if(title != NULL)
  {
    caption = g_strdup(title->data);
    g_list_free_full(title, &g_free);
  }
  else
  {
    caption = g_path_get_basename(img->filename);
    (g_strrstr(caption, "."))[0] = '\0'; // chop extension...
  }

  GList *desc = dt_metadata_get(img->id, "Xmp.dc.description", NULL);
  if(desc != NULL)
  {
    description = g_strdup(desc->data);
    g_list_free_full(desc, &g_free);
  }
  dt_image_cache_read_release(darktable.image_cache, img);

  GList *auth = dt_metadata_get(img->id, "Xmp.dc.creator", NULL);
  if(auth != NULL)
  {
    author = g_strdup(auth->data);
    g_list_free_full(auth, &g_free);
  }

  if(dt_imageio_export(imgid, fname, format, fdata, high_quality, upscale, FALSE, icc_type, icc_filename, icc_intent,
                       self, sdata, num, total) != 0)
  {
    fprintf(stderr, "[imageio_storage_piwigo] could not export to file: `%s'!\n", fname);
    dt_control_log(_("could not export to file `%s'!"), fname);
    result = 1;
    goto cleanup;
  }

  dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
  {
    gboolean status = TRUE;
    dt_storage_piwigo_params_t *p = (dt_storage_piwigo_params_t *)sdata;

    if(p->export_tags)
    {
      GList *tags_list = dt_tag_get_list(imgid);
      if(p->tags) g_free(p->tags);
      p->tags = dt_util_glist_to_str(",", tags_list);
      g_list_free_full(tags_list, g_free);
    }

    if(p->new_album)
    {
      status = _piwigo_api_create_new_album(p);
      if(!status) dt_control_log(_("cannot create a new piwigo album!"));
    }

    if(status)
    {
      status = _piwigo_api_upload_photo(p, fname, author, caption, description);
      if(!status)
      {
        fprintf(stderr, "[imageio_storage_piwigo] could not upload to piwigo!\n");
        dt_control_log(_("could not upload to piwigo!"));
        result = 1;
      }
      else if (p->new_album)
      {
        // we do not want to create more albums when multiple upload
        p->new_album = FALSE;
        _piwigo_refresh_albums(ui, p->album);
      }
    }
  }
  dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);

cleanup:

  // And remove from filesystem..
  g_unlink(fname);
  g_free(caption);
  g_free(description);
  g_free(author);

  if(!result)
  {
    // this makes sense only if the export was successful
    dt_control_log(ngettext("%d/%d exported to piwigo webalbum", "%d/%d exported to piwigo webalbum", num),
                   num, total);
  }
  return result;
}
Exemplo n.º 13
0
/** Refresh albums */
static void _piwigo_refresh_albums(dt_storage_piwigo_gui_data_t *ui, const gchar *select_album)
{
  gtk_widget_set_sensitive(GTK_WIDGET(ui->album_list), FALSE);
  gtk_widget_set_sensitive(GTK_WIDGET(ui->parent_album_list), FALSE);

  if(ui->api == NULL || ui->api->authenticated == FALSE)
  {
    _piwigo_authenticate(ui);
    if(ui->api == NULL || !ui->api->authenticated) return;
  }

  gchar *to_select;
  int index = 0;

  // get the new album name, it will be checked in the
  if(select_album == NULL)
  {
    to_select = g_strdup(dt_bauhaus_combobox_get_text(ui->album_list));
    if(to_select)
    {
      // cut the count of picture in album to get the name only
      gchar *p = to_select;
      while(*p)
      {
        if(*p == ' ' && *(p+1) == '(')
        {
          *p = '\0';
          break;
        }
        p++;
      }
    }
  }
  else
    to_select = g_strdup(select_album);

  // First clear the combobox except first 2 items (none / create new album)
  dt_bauhaus_combobox_clear(ui->album_list);
  dt_bauhaus_combobox_clear(ui->parent_album_list);
  g_list_free(ui->albums);
  ui->albums = NULL;

  GList *args = NULL;

  args = _piwigo_query_add_arguments(args, "method", "pwg.categories.getList");
  args = _piwigo_query_add_arguments(args, "cat_id", "0");
  args = _piwigo_query_add_arguments(args, "recursive", "true");

  _piwigo_api_post(ui->api, args, NULL, FALSE);

  g_list_free(args);

  if(ui->api->response && !ui->api->error_occured)
  {
    dt_bauhaus_combobox_add(ui->album_list, _("create new album"));
    dt_bauhaus_combobox_add(ui->parent_album_list, _("---"));

    JsonObject *result = json_node_get_object(json_object_get_member(ui->api->response, "result"));
    JsonArray *albums = json_object_get_array_member(result, "categories");

    if(json_array_get_length(albums)>0 && index==0) index = 1;
    if(index > json_array_get_length(albums) - 1) index = json_array_get_length(albums) - 1;

    for(int i = 0; i < json_array_get_length(albums); i++)
    {
      char data[MAX_ALBUM_NAME_SIZE] = { 0 };
      JsonObject *album = json_array_get_object_element(albums, i);

      _piwigo_album_t *new_album = g_malloc0(sizeof(struct _piwigo_album_t));

      g_strlcpy(new_album->name, json_object_get_string_member(album, "name"), sizeof(new_album->name));
      new_album->id = json_object_get_int_member(album, "id");
      new_album->size = json_object_get_int_member(album, "nb_images");
      const int isroot = json_object_get_null_member(album, "id_uppercat");
      int indent = 0;

      if(!isroot)
      {
        const char *hierarchy = json_object_get_string_member(album, "uppercats");
        char const *p = hierarchy;
        while(*p++) if(*p == ',') indent++;
      }

      snprintf(data, sizeof(data), "%*c%s (%"PRId64")", indent * 3, ' ', new_album->name, new_album->size);

      if(to_select && !strcmp(new_album->name, to_select)) index = i + 1;

      g_strlcpy(new_album->label, data, sizeof(new_album->label));

      ui->albums = g_list_append(ui->albums, new_album);

      dt_bauhaus_combobox_add_aligned(ui->album_list, data, DT_BAUHAUS_COMBOBOX_ALIGN_LEFT);
      dt_bauhaus_combobox_add_aligned(ui->parent_album_list, data, DT_BAUHAUS_COMBOBOX_ALIGN_LEFT);
    }
  }
  else
    dt_control_log(_("cannot refresh albums"));

  g_free(to_select);

  gtk_widget_set_sensitive(GTK_WIDGET(ui->album_list), TRUE);
  gtk_widget_set_sensitive(GTK_WIDGET(ui->parent_album_list), TRUE);
  dt_bauhaus_combobox_set(ui->album_list, index);
}
Exemplo n.º 14
0
void
dt_styles_save_to_file(const char *style_name,const char *filedir,gboolean overwrite)
{
    int rc = 0;
    char stylename[520];
    sqlite3_stmt *stmt;

    snprintf(stylename,512,"%s/%s.dtstyle",filedir,style_name);

    // check if file exists
    if( g_file_test(stylename, G_FILE_TEST_EXISTS) == TRUE )
    {
        if(overwrite)
        {
            if(unlink(stylename))
            {
                dt_control_log(_("failed to overwrite style file for %s"),style_name);
                return;
            }
        }
        else
        {
            dt_control_log(_("style file for %s exists"),style_name);
            return;
        }
    }

    if ( !dt_styles_exists (style_name) ) return;

    xmlTextWriterPtr writer = xmlNewTextWriterFilename(stylename, 0);
    if (writer == NULL)
    {
        fprintf(stderr,"[dt_styles_save_to_file] Error creating the xml writer\n, path: %s", stylename);
        return;
    }
    rc = xmlTextWriterStartDocument(writer, NULL, "ISO-8859-1", NULL);
    if (rc < 0)
    {
        fprintf(stderr,"[dt_styles_save_to_file]: Error on encoding setting");
        return;
    }
    xmlTextWriterStartElement(writer, BAD_CAST "darktable_style");
    xmlTextWriterWriteAttribute(writer, BAD_CAST "version", BAD_CAST "1.0");

    xmlTextWriterStartElement(writer, BAD_CAST "info");
    xmlTextWriterWriteFormatElement(writer, BAD_CAST "name", "%s", style_name);
    xmlTextWriterWriteFormatElement(writer, BAD_CAST "description", "%s", dt_styles_get_description(style_name));
    xmlTextWriterEndElement(writer);

    xmlTextWriterStartElement(writer, BAD_CAST "style");
    DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select num,module,operation,op_params,enabled,blendop_params,blendop_version from style_items where styleid =?1",-1, &stmt,NULL);
    DT_DEBUG_SQLITE3_BIND_INT(stmt,1,dt_styles_get_id_by_name(style_name));
    while (sqlite3_step (stmt) == SQLITE_ROW)
    {
        xmlTextWriterStartElement(writer, BAD_CAST "plugin");
        xmlTextWriterWriteFormatElement(writer, BAD_CAST "num", "%d", sqlite3_column_int(stmt,0));
        xmlTextWriterWriteFormatElement(writer, BAD_CAST "module", "%d", sqlite3_column_int(stmt,1));
        xmlTextWriterWriteFormatElement(writer, BAD_CAST "operation", "%s", sqlite3_column_text(stmt,2));
        xmlTextWriterWriteFormatElement(writer, BAD_CAST "op_params", "%s", dt_style_encode(stmt,3));
        xmlTextWriterWriteFormatElement(writer, BAD_CAST "enabled", "%d", sqlite3_column_int(stmt,4));
        xmlTextWriterWriteFormatElement(writer, BAD_CAST "blendop_params", "%s", dt_style_encode(stmt,5));
        xmlTextWriterWriteFormatElement(writer, BAD_CAST "blendop_version", "%d", sqlite3_column_int(stmt,6));
        xmlTextWriterEndElement(writer);
    }
    sqlite3_finalize(stmt);
    xmlTextWriterEndDocument(writer);
    xmlFreeTextWriter(writer);
    dt_control_log(_("style %s was sucessfully saved"),style_name);
}
Exemplo n.º 15
0
/** Invoked when camera error appear */
static void _camera_error_callback(const dt_camera_t *camera, dt_camera_error_t error, void *user_data)
{
  dt_control_log(_("connection with camera lost, exiting tethering mode"));
  g_idle_add(_bailout_of_tethering, user_data);
}
Exemplo n.º 16
0
/** Initializes a new pwstorage context. */
const dt_pwstorage_t *dt_pwstorage_new()
{
/* add password storage capabilities */
#ifdef HAVE_LIBSECRET
  dt_capabilities_add("libsecret");
#endif
#ifdef HAVE_KWALLET
  dt_capabilities_add("kwallet");
#endif

  dt_pwstorage_t *pwstorage = g_malloc(sizeof(dt_pwstorage_t));
  dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_new] Creating new context %p\n", pwstorage);

  if(pwstorage == NULL) return NULL;

  gchar *_backend_str = dt_conf_get_string("plugins/pwstorage/pwstorage_backend");
  gint _backend = PW_STORAGE_BACKEND_NONE;

  if(strcmp(_backend_str, "auto") == 0)
  {
    const gchar *desktop = getenv("XDG_CURRENT_DESKTOP");
    if(g_strcmp0(desktop, "KDE") == 0)
      _backend = PW_STORAGE_BACKEND_KWALLET;
    else if(g_strcmp0(desktop, "GNOME") == 0)
      _backend = PW_STORAGE_BACKEND_LIBSECRET;
    else if(g_strcmp0(desktop, "Unity") == 0)
      _backend = PW_STORAGE_BACKEND_LIBSECRET;
    else if(g_strcmp0(desktop, "XFCE") == 0)
      _backend = PW_STORAGE_BACKEND_LIBSECRET;

    dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_new] autodetected storage backend.\n");
  }
  else if(strcmp(_backend_str, "none") == 0)
    _backend = PW_STORAGE_BACKEND_NONE;
#ifdef HAVE_LIBSECRET
  else if(strcmp(_backend_str, "libsecret") == 0)
    _backend = PW_STORAGE_BACKEND_LIBSECRET;
#endif
#ifdef HAVE_KWALLET
  else if(strcmp(_backend_str, "kwallet") == 0)
    _backend = PW_STORAGE_BACKEND_KWALLET;
#endif
  else if(strcmp(_backend_str, "gnome keyring") == 0)
  {
    fprintf(stderr, "[pwstorage_new] GNOME Keyring backend is no longer supported.\n");
    dt_control_log(_("GNOME Keyring backend is no longer supported. configure a different one"));
    _backend = PW_STORAGE_BACKEND_NONE;
  }

  g_free(_backend_str);

  switch(_backend)
  {
    default:
      dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_new] unknown storage backend. Using none.\n");
    case PW_STORAGE_BACKEND_NONE:
      pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_NONE;
      pwstorage->backend_context = NULL;
      dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_new] no storage backend. not storing username/password. "
                                   "please change in preferences, core tab.\n");
      break;
    case PW_STORAGE_BACKEND_LIBSECRET:
#ifdef HAVE_LIBSECRET
      dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_new] using libsecret backend for username/password storage");
      pwstorage->backend_context = (void *)dt_pwstorage_libsecret_new();
      if(pwstorage->backend_context == NULL)
      {
        dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_new] error starting libsecret. using no storage backend.\n");
        pwstorage->backend_context = NULL;
        pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_NONE;
      }
      else
      {
        pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_LIBSECRET;
      }
      break;
#else
      dt_print(DT_DEBUG_PWSTORAGE,
               "[pwstorage_new] libsecret backend not available. using no storage backend.\n");
      pwstorage->backend_context = NULL;
      pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_NONE;
#endif
    case PW_STORAGE_BACKEND_KWALLET:
#ifdef HAVE_KWALLET
      dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_new] using kwallet backend for username/password storage");
      pwstorage->backend_context = (void *)dt_pwstorage_kwallet_new();
      if(pwstorage->backend_context == NULL)
      {
        dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_new] error starting kwallet. using no storage backend.\n");
        pwstorage->backend_context = NULL;
        pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_NONE;
      }
      else
      {
        pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_KWALLET;
      }
      dt_print(DT_DEBUG_PWSTORAGE, "  done.\n");
      break;
#else
      dt_print(DT_DEBUG_PWSTORAGE,
               "[pwstorage_new] kwallet backend not available. using no storage backend.\n");
      pwstorage->backend_context = NULL;
      pwstorage->pw_storage_backend = PW_STORAGE_BACKEND_NONE;
#endif
  }

  switch(pwstorage->pw_storage_backend)
  {
    case PW_STORAGE_BACKEND_NONE:
      dt_conf_set_string("plugins/pwstorage/pwstorage_backend", "none");
      break;
    case PW_STORAGE_BACKEND_LIBSECRET:
      dt_conf_set_string("plugins/pwstorage/pwstorage_backend", "libsecret");
      break;
    case PW_STORAGE_BACKEND_KWALLET:
      dt_conf_set_string("plugins/pwstorage/pwstorage_backend", "kwallet");
      break;
  }

  return pwstorage;
}
Exemplo n.º 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);
}
Exemplo n.º 18
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 *)"SplitToningShadowHue"))
    {
      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 *)"SplitToningShadowSaturation"))
    {
      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)
  {
    dt_control_log(ngettext("%s has been imported", "%s have been imported", n_import), imported);

    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);
    }
  }
}
Exemplo n.º 19
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_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 *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);
      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
    while(stls)
    {
      dt_style_item_t *s = (dt_style_item_t *)stls->data;
      gboolean module_found = FALSE;

      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));
          dt_iop_module_t *sty_module = m;

          if(format_params->style_append && !(m->flags() & IOP_FLAGS_ONE_INSTANCE))
          {
            sty_module = dt_dev_module_duplicate(m->dev, m, 0);
            if(!sty_module)
            {
              free(h);
              goto error;
            }
          }

          h->params = s->params;
          h->blend_params = s->blendop_params;
          h->enabled = s->enabled;
          h->module = sty_module;
          h->multi_priority = 1;
          g_strlcpy(h->multi_name, "<style>", 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);
          module_found = TRUE;
          g_free(s->name);
          break;
        }
        modules = g_list_next(modules);
      }
      if(!module_found) dt_style_item_free(s);
      stls = g_list_next(stls);
    }
    g_list_free(stls);
  }

  dt_dev_pixelpipe_set_input(&pipe, &dev, (float *)buf.buf, buf.width, buf.height,
                             buf_is_downscaled ? dev.image_storage.width / (float)buf.width : 1.0f,
                             buf.pre_monochrome_demosaiced);
  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;
  int icctype = dt_conf_get_int("plugins/lighttable/export/icctype");
  if(icctype == DT_COLORSPACE_SRGB)
  {
    sRGB = 1;
  }
  else if(icctype == 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 = 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, 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);
  int processed_width = scale * pipe.processed_width + .5f;
  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)
     */
    const double scalex = format_params->max_width > 0
                              ? fminf(format_params->max_width / (double)pipe.processed_width, max_scale)
                              : 1.0;
    const double scaley = format_params->max_height > 0
                              ? fminf(format_params->max_height / (double)pipe.processed_height, max_scale)
                              : 1.0;
    const double scale = fminf(scalex, scaley);
    processed_width = scale * pipe.processed_width + .5f;
    processed_height = scale * pipe.processed_height + .5f;

    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.
    GList *finalscalep = g_list_last(pipe.nodes);
    dt_dev_pixelpipe_iop_t *finalscale = (dt_dev_pixelpipe_iop_t *)finalscalep->data;
    while(strcmp(finalscale->module->op, "finalscale"))
    {
      finalscale = NULL;
      finalscalep = g_list_previous(finalscalep);
      if(!finalscalep) break;
      finalscale = (dt_dev_pixelpipe_iop_t *)finalscalep->data;
    }
    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) 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] = { 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, exif_profile, length, imgid, num, total);
  }
  else
  {
    res = format->write_image(format_params, filename, outbuf, 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))
  {
    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;
}
Exemplo n.º 20
0
int store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *sdata, const int imgid,
          dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata, const int num, const int total,
          const gboolean high_quality, const gboolean upscale)
{
  dt_imageio_disk_t *d = (dt_imageio_disk_t *)sdata;

  char filename[PATH_MAX] = { 0 };
  char dirname[PATH_MAX] = { 0 };
  gboolean from_cache = FALSE;
  dt_image_full_path(imgid, dirname, sizeof(dirname), &from_cache);
  int fail = 0;
  // we're potentially called in parallel. have sequence number synchronized:
  dt_pthread_mutex_lock(&darktable.plugin_threadsafe);
  {

    // if filenamepattern is a directory just let att ${FILE_NAME} as default..
    if(g_file_test(d->filename, G_FILE_TEST_IS_DIR)
       || ((d->filename + strlen(d->filename))[0] == '/' || (d->filename + strlen(d->filename))[0] == '\\'))
      snprintf(d->filename + strlen(d->filename), sizeof(d->filename) - strlen(d->filename), "$(FILE_NAME)");

    // avoid braindead export which is bound to overwrite at random:
    if(total > 1 && !g_strrstr(d->filename, "$"))
    {
      snprintf(d->filename + strlen(d->filename), sizeof(d->filename) - strlen(d->filename), "_$(SEQUENCE)");
    }

    gchar *fixed_path = dt_util_fix_path(d->filename);
    g_strlcpy(d->filename, fixed_path, sizeof(d->filename));
    g_free(fixed_path);

    d->vp->filename = dirname;
    d->vp->jobcode = "export";
    d->vp->imgid = imgid;
    d->vp->sequence = num;
    dt_variables_expand(d->vp, d->filename, TRUE);

    gchar *result_filename = dt_variables_get_result(d->vp);
    g_strlcpy(filename, result_filename, sizeof(filename));
    g_free(result_filename);

    g_strlcpy(dirname, filename, sizeof(dirname));

    const char *ext = format->extension(fdata);
    char *c = dirname + strlen(dirname);
    for(; c > dirname && *c != '/'; c--)
      ;
    if(*c == '/')
    {
      if(c > dirname) // /.../.../foo
        c[0] = '\0';
      else // /foo
        c[1] = '\0';
    }
    else if(c == dirname) // foo
    {
      c[0] = '.';
      c[1] = '\0';
    }

    if(g_mkdir_with_parents(dirname, 0755))
    {
      fprintf(stderr, "[imageio_storage_disk] could not create directory: `%s'!\n", dirname);
      dt_control_log(_("could not create directory `%s'!"), dirname);
      fail = 1;
      goto failed;
    }
    if(g_access(dirname, W_OK | X_OK) != 0)
    {
      fprintf(stderr, "[imageio_storage_disk] could not write to directory: `%s'!\n", dirname);
      dt_control_log(_("could not write to directory `%s'!"), dirname);
      fail = 1;
      goto failed;
    }

    c = filename + strlen(filename);
    // remove everything after the last '.'. this destroys any file name with dots in it since $(FILE_NAME)
    // already comes without the original extension.
    //     for(; c>filename && *c != '.' && *c != '/' ; c--);
    //     if(c <= filename || *c=='/') c = filename + strlen(filename);

    sprintf(c, ".%s", ext);

  /* prevent overwrite of files */
  failed:
    if(!d->overwrite)
    {
      int seq = 1;
      if(!fail && g_file_test(filename, G_FILE_TEST_EXISTS))
      {
        do
        {
          sprintf(c, "_%.2d.%s", seq, ext);
          seq++;
        } while(g_file_test(filename, G_FILE_TEST_EXISTS));
      }
    }
  } // end of critical block
  dt_pthread_mutex_unlock(&darktable.plugin_threadsafe);
  if(fail) return 1;

  /* export image to file */
  if(dt_imageio_export(imgid, filename, format, fdata, high_quality, upscale, TRUE, self, sdata, num, total) != 0)
  {
    fprintf(stderr, "[imageio_storage_disk] could not export to file: `%s'!\n", filename);
    dt_control_log(_("could not export to file `%s'!"), filename);
    return 1;
  }

  printf("[export_job] exported to `%s'\n", filename);
  char *trunc = filename + strlen(filename) - 32;
  if(trunc < filename) trunc = filename;
  dt_control_log(ngettext("%d/%d exported to `%s%s'", "%d/%d exported to `%s%s'", num),
                 num, total, trunc != filename ? ".." : "", trunc);
  return 0;
}