Exemple #1
0
// load a full-res thumbnail:
int dt_imageio_large_thumbnail(const char *filename, uint8_t **buffer, int32_t *width, int32_t *height, int32_t *orientation)
{
  int ret = 0;
  int res = 1;
  // raw image thumbnail
  libraw_data_t *raw = libraw_init(0);
  libraw_processed_image_t *image = NULL;
  ret = libraw_open_file(raw, filename);
  if(ret) goto libraw_fail;
  ret = libraw_unpack_thumb(raw);
  if(ret) goto libraw_fail;
  ret = libraw_adjust_sizes_info_only(raw);
  if(ret) goto libraw_fail;

  image = libraw_dcraw_make_mem_thumb(raw, &ret);
  if(!image || ret) goto libraw_fail;
  *orientation = raw->sizes.flip;
  if(image->type == LIBRAW_IMAGE_JPEG)
  {
    dt_imageio_jpeg_t jpg;
    if(dt_imageio_jpeg_decompress_header(image->data, image->data_size, &jpg)) goto libraw_fail;
    *buffer = (uint8_t *)malloc((size_t)sizeof(uint8_t)*jpg.width*jpg.height*4);
    if(!*buffer) goto libraw_fail;
    *width = jpg.width;
    *height = jpg.height;
    if(dt_imageio_jpeg_decompress(&jpg, *buffer))
    {
      free(*buffer);
      *buffer = 0;
      goto libraw_fail;
    }
    res = 0;
  }

  // clean up raw stuff.
  libraw_recycle(raw);
  libraw_close(raw);
  free(image);
  if(0)
  {
libraw_fail:
    // fprintf(stderr,"[imageio] %s: %s\n", filename, libraw_strerror(ret));
    libraw_close(raw);
    res = 1;
  }
  return res;
}
EXTERN_C DLLEXPORT int read_raw_image(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument res) {
	int err;
	int check;
	MImage out;
	char * file;
	libraw_data_t *iprc = libraw_init(0);
	libraw_processed_image_t * img;
	WolframImageLibrary_Functions imgFuns = libData->imageLibraryFunctions;

	err = LIBRARY_FUNCTION_ERROR;
	file = MArgument_getUTF8String(Args[0]);

	libraw_open_file(iprc, file);
	libraw_unpack(iprc);

	iprc->params.output_bps = 8;

	check = libraw_dcraw_process(iprc);
	if (check != LIBRAW_SUCCESS) goto cleanup;

	img = libraw_dcraw_make_mem_image(iprc, &check);
	if (img == NULL) goto cleanup;
	if (img->type != LIBRAW_IMAGE_BITMAP || img->colors != 3) goto cleanup;
	
	if (img->bits == 16) {
		raw_t_ubit16 * raw_data = (raw_t_ubit16*)img->data;
		imgFuns->MImage_new2D(img->width, img->height, 3, MImage_Type_Bit16, MImage_CS_RGB, 1, &out);
		memcpy(imgFuns->MImage_getBit16Data(out), raw_data, img->width * img->height * 3 * sizeof(raw_t_ubit16));
	} else if (img->bits == 8) {
		raw_t_ubit8 * raw_data = (raw_t_ubit8*)img->data;
		imgFuns->MImage_new2D(img->width, img->height, 3, MImage_Type_Bit8, MImage_CS_RGB, 1, &out);
		memcpy(imgFuns->MImage_getByteData(out), raw_data, img->width * img->height * 3 * sizeof(raw_t_ubit8));
	} else {
		goto cleanup;
	}
	
	MArgument_setMImage(res, out);
	err = LIBRARY_NO_ERROR;

cleanup:
	libData->UTF8String_disown(file);
	libraw_dcraw_clear_mem(img);
	return err;
}
Exemple #3
0
// thread routine
int process_files(void *q)
{
  int ret;
  int count = 0;
  char outfn[1024], *fn;
  libraw_data_t *iprc = libraw_init(0);

  if (!iprc)
  {
    fprintf(stderr, "Cannot create libraw handle\n");
    return -1;
  }

  while ((fn = get_next_file()))
  {

    iprc->params.half_size = 1; /* dcraw -h */
    iprc->params.use_camera_wb = use_camera_wb;
    iprc->params.use_auto_wb = use_auto_wb;
    iprc->params.output_tiff = tiff_mode;

    ret = libraw_open_file(iprc, fn);
    if (verbose)
      fprintf(stderr, "%s: %s/%s\n", fn, iprc->idata.make, iprc->idata.model);
    HANDLE_ERRORS(ret);

    ret = libraw_unpack(iprc);
    HANDLE_ERRORS(ret);

    ret = libraw_dcraw_process(iprc);
    HANDLE_ERRORS(ret);

    snprintf(outfn, 1023, "%s.%s", fn, tiff_mode ? "tif" : "ppm");

    if (verbose)
      fprintf(stderr, "Writing file %s\n", outfn);
    ret = libraw_dcraw_ppm_tiff_writer(iprc, outfn);
    HANDLE_ERRORS(ret);
    count++;
  }
  libraw_close(iprc);
  printf("Processed %d files\n", count);
  return 0;
}
Exemple #4
0
int main(int ac, char *av[])
{

  if (ac != 3)
  {
    fprintf(stderr, "Usage: %s <input_file> <output_file>\n %d", av[0],ac);
    exit(1);
  }


  libraw_data_t *iprc = libraw_init(0);

  if (!iprc)
  {
    fprintf(stderr, "Cannot create libraw handle\n");
    exit(1);
  }

  iprc->params.half_size = 1; /* dcraw -h */
  iprc->params.use_camera_wb = 1; /* dcraw -w */

  char outfn[1024];
  int ret = libraw_open_file(iprc, av[1]);
  HANDLE_ERROR(ret);

  printf("Processing %s (%s %s)\n", av[1], iprc->idata.make, iprc->idata.model);

  ret = libraw_unpack(iprc);
  HANDLE_ERROR(ret);

  ret = libraw_dcraw_process(iprc);
  HANDLE_ERROR(ret);

  strcpy(outfn, av[2]);
  printf("Writing to %s\n", outfn);

  ret = libraw_dcraw_ppm_tiff_writer(iprc, outfn);
  HANDLE_ERROR(ret);

  libraw_close(iprc);
  return 0;
}
Exemple #5
0
int main(int ac, char *av[])
{
    int i;
    libraw_data_t *iprc = libraw_init(0);

    if(!iprc)
        {
            fprintf(stderr,"Cannot create libraw handle\n");
            exit(1);
        }

    iprc->params.half_size = 1; /* dcraw -h */
	
    for (i=1;i<ac;i++)
        {
            char outfn[1024];
			int ret = libraw_open_file(iprc,av[i]);
            HANDLE_ALL_ERRORS(ret);
            
            printf("Processing %s (%s %s)\n",av[i],iprc->idata.make,iprc->idata.model);

            ret = libraw_unpack(iprc);
            HANDLE_ALL_ERRORS(ret);

            ret = libraw_dcraw_process(iprc);
            HANDLE_ALL_ERRORS(ret);
            
            strcpy(outfn,av[i]);
            strcat(outfn,".ppm");
            printf("Writing to %s\n",outfn);

            ret = libraw_dcraw_ppm_tiff_writer(iprc,outfn);
            HANDLE_FATAL_ERROR(ret);
        }
    libraw_close(iprc);
    return 0;
}
Exemple #6
0
// open a raw file, libraw path:
dt_imageio_retval_t
dt_imageio_open_raw(
    dt_image_t  *img,
    const char  *filename,
    dt_mipmap_cache_allocator_t a)
{
  if(!img->exif_inited)
    (void) dt_exif_read(img, filename);
  int ret;
  libraw_data_t *raw = libraw_init(0);
  libraw_processed_image_t *image = NULL;
  raw->params.half_size = 0; /* dcraw -h */
  raw->params.use_camera_wb = 0;
  raw->params.use_auto_wb = 0;
  raw->params.med_passes = 0;//img->raw_params.med_passes;
  raw->params.no_auto_bright = 1;
  // raw->params.filtering_mode |= LIBRAW_FILTERING_NOBLACKS;
  // raw->params.document_mode = 2; // no color scaling, no black, no max, no wb..?
  raw->params.document_mode = 2; // color scaling (clip,wb,max) and black point, but no demosaic
  raw->params.output_color = 0;
  raw->params.output_bps = 16;
  raw->params.user_flip = -1; // -1 means: use orientation from raw
  raw->params.gamm[0] = 1.0;
  raw->params.gamm[1] = 1.0;
  // raw->params.user_qual = img->raw_params.demosaic_method; // 3: AHD, 2: PPG, 1: VNG
  raw->params.user_qual = 0;
  // raw->params.four_color_rgb = img->raw_params.four_color_rgb;
  raw->params.four_color_rgb = 0;
  raw->params.use_camera_matrix = 0;
  raw->params.green_matching = 0;
  raw->params.highlight = 1;
  raw->params.threshold = 0;
  // raw->params.auto_bright_thr = img->raw_auto_bright_threshold;

  // raw->params.amaze_ca_refine = 0;
  raw->params.fbdd_noiserd    = 0;

  ret = libraw_open_file(raw, filename);
  HANDLE_ERRORS(ret, 0);
  raw->params.user_qual = 0;
  raw->params.half_size = 0;

  ret = libraw_unpack(raw);
  // img->black   = raw->color.black/65535.0;
  // img->maximum = raw->color.maximum/65535.0;
  img->bpp = sizeof(uint16_t);
  // printf("black, max: %d %d %f %f\n", raw->color.black, raw->color.maximum, img->black, img->maximum);
  HANDLE_ERRORS(ret, 1);
  ret = libraw_dcraw_process(raw);
  // ret = libraw_dcraw_document_mode_processing(raw);
  HANDLE_ERRORS(ret, 1);
  image = libraw_dcraw_make_mem_image(raw, &ret);
  HANDLE_ERRORS(ret, 1);

  // fallback for broken exif read in case of phase one H25
  if(!strncmp(img->exif_maker, "Phase One", 9))
    img->orientation = raw->sizes.flip;
  // filters seem only ever to take a useful value after unpack/process
  img->filters = raw->idata.filters;
  img->width  = (img->orientation & 4) ? raw->sizes.height : raw->sizes.width;
  img->height = (img->orientation & 4) ? raw->sizes.width  : raw->sizes.height;
  img->exif_iso = raw->other.iso_speed;
  img->exif_exposure = raw->other.shutter;
  img->exif_aperture = raw->other.aperture;
  img->exif_focal_length = raw->other.focal_len;
  g_strlcpy(img->exif_maker, raw->idata.make, sizeof(img->exif_maker));
  img->exif_maker[sizeof(img->exif_maker) - 1] = 0x0;
  g_strlcpy(img->exif_model, raw->idata.model, sizeof(img->exif_model));
  img->exif_model[sizeof(img->exif_model) - 1] = 0x0;
  dt_gettime_t(img->exif_datetime_taken, raw->other.timestamp);

  void *buf = dt_mipmap_cache_alloc(img, DT_MIPMAP_FULL, a);
  if(!buf)
  {
    libraw_recycle(raw);
    libraw_close(raw);
    free(image);
    return DT_IMAGEIO_CACHE_FULL;
  }
#ifdef _OPENMP
  #pragma omp parallel for schedule(static) default(none) shared(img, image, raw, buf)
#endif
  for(int k=0; k<img->width*img->height; k++)
    ((uint16_t *)buf)[k] = CLAMPS((((uint16_t *)image->data)[k] - raw->color.black)*65535.0f/(float)(raw->color.maximum - raw->color.black), 0, 0xffff);
  // clean up raw stuff.
  libraw_recycle(raw);
  libraw_close(raw);
  free(image);
  raw = NULL;
  image = NULL;

  img->flags &= ~DT_IMAGE_LDR;
  img->flags &= ~DT_IMAGE_HDR;
  img->flags |= DT_IMAGE_RAW;
  return DT_IMAGEIO_OK;
}
Exemple #7
0
void reload_defaults(dt_iop_module_t *module)
{
    // raw images need wb (to convert from uint16_t to float):
    if(dt_image_is_raw(&module->dev->image_storage))
    {
        module->default_enabled = 1;
        module->hide_enable_button = 1;
    }
    else module->default_enabled = 0;
    dt_iop_temperature_params_t tmp = (dt_iop_temperature_params_t)
    {
        5000.0, {1.0, 1.0, 1.0}
    };

    // get white balance coefficients, as shot
    char filename[DT_MAX_PATH_LEN];
    int ret=0;
    /* check if file is raw / hdr */
    if(dt_image_is_raw(&module->dev->image_storage))
    {
        gboolean from_cache = TRUE;
        dt_image_full_path(module->dev->image_storage.id, filename, DT_MAX_PATH_LEN, &from_cache);
        libraw_data_t *raw = libraw_init(0);

        ret = libraw_open_file(raw, filename);
        if(!ret)
        {
            module->default_enabled = 1;

            for(int k=0; k<3; k++) tmp.coeffs[k] = raw->color.cam_mul[k];
            if(tmp.coeffs[0] <= 0.0)
            {
                for(int k=0; k<3; k++) tmp.coeffs[k] = raw->color.pre_mul[k];
            }
            if(tmp.coeffs[0] == 0 || tmp.coeffs[1] == 0 || tmp.coeffs[2] == 0)
            {
                // could not get useful info, try presets:
                char makermodel[1024];
                char *model = makermodel;
                dt_colorspaces_get_makermodel_split(makermodel, 1024, &model,
                                                    module->dev->image_storage.exif_maker,
                                                    module->dev->image_storage.exif_model);
                for(int i=0; i<wb_preset_count; i++)
                {
                    if(!strcmp(wb_preset[i].make,  makermodel) &&
                            !strcmp(wb_preset[i].model, model))
                    {
                        // just take the first preset we find for this camera
                        for(int k=0; k<3; k++) tmp.coeffs[k] = wb_preset[i].channel[k];
                        break;
                    }
                }
                if(tmp.coeffs[0] == 0 || tmp.coeffs[1] == 0 || tmp.coeffs[2] == 0)
                {
                    // final security net: hardcoded default that fits most cams.
                    tmp.coeffs[0] = 2.0f;
                    tmp.coeffs[1] = 1.0f;
                    tmp.coeffs[2] = 1.5f;
                }
            }
            else
            {
                tmp.coeffs[0] /= tmp.coeffs[1];
                tmp.coeffs[2] /= tmp.coeffs[1];
                tmp.coeffs[1] = 1.0f;
            }
            // remember daylight wb used for temperature/tint conversion,
            // assuming it corresponds to CIE daylight (D65)
            if(module->gui_data)
            {
                dt_iop_temperature_gui_data_t *g = (dt_iop_temperature_gui_data_t *)module->gui_data;
                for(int c = 0; c < 3; c++)
                    g->daylight_wb[c] = raw->color.pre_mul[c];

                if(g->daylight_wb[0] == 1.0f &&
                        g->daylight_wb[1] == 1.0f &&
                        g->daylight_wb[2] == 1.0f)
                {
                    // if we didn't find anything for daylight wb, look for a wb preset with appropriate name.
                    // we're normalising that to be D65
                    char makermodel[1024];
                    char *model = makermodel;
                    dt_colorspaces_get_makermodel_split(makermodel, 1024, &model,
                                                        module->dev->image_storage.exif_maker,
                                                        module->dev->image_storage.exif_model);
                    for(int i=0; i<wb_preset_count; i++)
                    {
                        if(!strcmp(wb_preset[i].make,  makermodel) &&
                                !strcmp(wb_preset[i].model, model) &&
                                !strncasecmp(wb_preset[i].name, "daylight", 8))
                        {
                            for(int k=0; k<3; k++)
                                g->daylight_wb[k] = wb_preset[i].channel[k];
                            break;
                        }
                    }
                }
                float temp, tint, mul[3];
                for(int k=0; k<3; k++) mul[k] = g->daylight_wb[k]/tmp.coeffs[k];
                convert_rgb_to_k(mul, &temp, &tint);
                dt_bauhaus_slider_set_default(g->scale_k,    temp);
                dt_bauhaus_slider_set_default(g->scale_tint, tint);
            }

        }
        libraw_close(raw);
    }

    memcpy(module->params, &tmp, sizeof(dt_iop_temperature_params_t));
    memcpy(module->default_params, &tmp, sizeof(dt_iop_temperature_params_t));
}
Exemple #8
0
void reload_defaults(dt_iop_module_t *module)
{
  // raw images need wb (to convert from uint16_t to float):
  if(dt_image_is_raw(&module->dev->image_storage))
  {
    module->default_enabled = 1;
    module->hide_enable_button = 1;
  }
  else module->default_enabled = 0;
  dt_iop_temperature_params_t tmp = (dt_iop_temperature_params_t)
  {
    5000.0, {1.0, 1.0, 1.0}
  };

  // get white balance coefficients, as shot
  char filename[DT_MAX_PATH_LEN];
  int ret=0;
  /* check if file is raw / hdr */
  if(dt_image_is_raw(&module->dev->image_storage))
  {
    dt_image_full_path(module->dev->image_storage.id, filename, DT_MAX_PATH_LEN);
    libraw_data_t *raw = libraw_init(0);

    ret = libraw_open_file(raw, filename);
    if(!ret)
    {
      module->default_enabled = 1;
      for(int k=0; k<3; k++) tmp.coeffs[k] = raw->color.cam_mul[k];
      if(tmp.coeffs[0] <= 0.0)
      {
        for(int k=0; k<3; k++) tmp.coeffs[k] = raw->color.pre_mul[k];
      }
      if(tmp.coeffs[0] == 0 || tmp.coeffs[1] == 0 || tmp.coeffs[2] == 0)
      {
        // could not get useful info, try presets:
        char makermodel[1024];
        char *model = makermodel;
        dt_colorspaces_get_makermodel_split(makermodel, 1024, &model,
                                            module->dev->image_storage.exif_maker,
                                            module->dev->image_storage.exif_model);
        for(int i=0; i<wb_preset_count; i++)
        {
          if(!strcmp(wb_preset[i].make,  makermodel) &&
              !strcmp(wb_preset[i].model, model))
          {
            // just take the first preset we find for this camera
            for(int k=0; k<3; k++) tmp.coeffs[k] = wb_preset[i].channel[k];
            break;
          }
        }
        if(tmp.coeffs[0] == 0 || tmp.coeffs[1] == 0 || tmp.coeffs[2] == 0)
        {
          // final security net: hardcoded default that fits most cams.
          tmp.coeffs[0] = 2.0f;
          tmp.coeffs[1] = 1.0f;
          tmp.coeffs[2] = 1.5f;
        }
      }
      else
      {
        tmp.coeffs[0] /= tmp.coeffs[1];
        tmp.coeffs[2] /= tmp.coeffs[1];
        tmp.coeffs[1] = 1.0f;
      }
    }
    libraw_close(raw);
  }

  memcpy(module->params, &tmp, sizeof(dt_iop_temperature_params_t));
  memcpy(module->default_params, &tmp, sizeof(dt_iop_temperature_params_t));
}
Exemple #9
0
// TODO: use orientation to correctly rotate the image.
// maybe this should be (partly) in common/imageio.[c|h]?
static void _lib_import_update_preview(GtkFileChooser *file_chooser, gpointer data)
{
  GtkWidget *preview;
  char *filename;
  GdkPixbuf *pixbuf = NULL;
  gboolean have_preview = FALSE;

  preview = GTK_WIDGET(data);
  filename = gtk_file_chooser_get_preview_filename(file_chooser);

  if(!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) goto no_preview_fallback;
  // don't create dng thumbnails to avoid crashes in libtiff when these are hdr:
  char *c = filename + strlen(filename);
  while(c > filename && *c != '.') c--;
  if(!strcasecmp(c, ".dng")) goto no_preview_fallback;

  pixbuf = gdk_pixbuf_new_from_file_at_size(filename, 128, 128, NULL);
  have_preview = (pixbuf != NULL);
  if(!have_preview)
  {
    // raw image thumbnail
    int ret;
    libraw_data_t *raw = libraw_init(0);
    libraw_processed_image_t *image = NULL;
    ret = libraw_open_file(raw, filename);
    if(ret) goto libraw_fail;
    ret = libraw_unpack_thumb(raw);
    if(ret) goto libraw_fail;
    ret = libraw_adjust_sizes_info_only(raw);
    if(ret) goto libraw_fail;

    image = libraw_dcraw_make_mem_thumb(raw, &ret);
    if(!image || ret) goto libraw_fail;
//     const int orientation = raw->sizes.flip;

    GdkPixbuf *tmp;
    GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
    have_preview = gdk_pixbuf_loader_write(loader, image->data, image->data_size, NULL);
    tmp = gdk_pixbuf_loader_get_pixbuf(loader);
    gdk_pixbuf_loader_close(loader, NULL);
    float ratio;
    if(image->type == LIBRAW_IMAGE_JPEG)
    {
      // jpeg
      dt_imageio_jpeg_t jpg;
      if(dt_imageio_jpeg_decompress_header(image->data, image->data_size, &jpg)) goto libraw_fail;
      ratio = 1.0*jpg.height/jpg.width;
    }
    else
    {
      // bmp -- totally untested
      ratio = 1.0*image->height/image->width;
    }
    int width = 128, height = 128*ratio;
    pixbuf = gdk_pixbuf_scale_simple(tmp, width, height, GDK_INTERP_BILINEAR);

    if(loader)
      g_object_unref(loader);

    // clean up raw stuff.
    libraw_recycle(raw);
    libraw_close(raw);
    free(image);
    if(0)
    {
libraw_fail:
      // fprintf(stderr,"[imageio] %s: %s\n", filename, libraw_strerror(ret));
      libraw_close(raw);
      have_preview = FALSE;
    }
  }
  if(!have_preview)
  {
no_preview_fallback:
    pixbuf = gdk_pixbuf_new_from_inline(-1, dt_logo_128x128, FALSE, NULL);
    have_preview = TRUE;
  }
  if(have_preview)
    gtk_image_set_from_pixbuf(GTK_IMAGE(preview), pixbuf);
  if(pixbuf)
    g_object_unref(pixbuf);
  g_free(filename);

  gtk_file_chooser_set_preview_widget_active(file_chooser, have_preview);
}
Exemple #10
0
static void
_init_8(
  uint8_t                *buf,
  uint32_t               *width,
  uint32_t               *height,
  const uint32_t          imgid,
  const dt_mipmap_size_t  size)
{
  const uint32_t wd = *width, ht = *height;
  char filename[DT_MAX_PATH_LEN] = {0};

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

  const int altered = dt_image_altered(imgid);
  int res = 1;

  const dt_image_t *cimg = dt_image_cache_read_get(darktable.image_cache, imgid);
  const int orientation = dt_image_orientation(cimg);
  // the orientation for this camera is not read correctly from exiv2, so we need
  // to go the full libraw path (as the thumbnail will be flipped the wrong way round)
  const int incompatible = !strncmp(cimg->exif_maker, "Phase One", 9);
  dt_image_cache_read_release(darktable.image_cache, cimg);


  // first try exif thumbnail, that's smaller and thus faster to load:
  if(!altered && !dt_conf_get_bool("never_use_embedded_thumb") &&
     !dt_exif_thumbnail(filename, buf, wd, ht, orientation, width, height))
  {
    res = 0;
  }
  else if(!altered && !dt_conf_get_bool("never_use_embedded_thumb") && !incompatible)
  {
    // try to load the embedded thumbnail in raw
    int ret;
    memset(filename, 0, DT_MAX_PATH_LEN);
    dt_image_full_path(imgid, filename, DT_MAX_PATH_LEN);

    const char *c = filename + strlen(filename);
    while(*c != '.' && c > filename) c--;
    if(!strcasecmp(c, ".jpg"))
    {
      // try to load jpg
      dt_imageio_jpeg_t jpg;
      if(!dt_imageio_jpeg_read_header(filename, &jpg))
      {
        uint8_t *tmp = (uint8_t *)malloc(sizeof(uint8_t)*jpg.width*jpg.height*4);
        if(!dt_imageio_jpeg_read(&jpg, tmp))
        {
          // scale to fit
          dt_iop_flip_and_zoom_8(tmp, jpg.width, jpg.height, buf, wd, ht, orientation, width, height);
          res = 0;
        }
        free(tmp);
      }
    }
    else
    {
      // raw image thumbnail
      libraw_data_t *raw = libraw_init(0);
      libraw_processed_image_t *image = NULL;
      ret = libraw_open_file(raw, filename);
      if(ret) goto libraw_fail;
      ret = libraw_unpack_thumb(raw);
      if(ret) goto libraw_fail;
      ret = libraw_adjust_sizes_info_only(raw);
      if(ret) goto libraw_fail;

      image = libraw_dcraw_make_mem_thumb(raw, &ret);
      if(!image || ret) goto libraw_fail;
      const int orientation = raw->sizes.flip;
      if(image->type == LIBRAW_IMAGE_JPEG)
      {
        // JPEG: decode (directly rescaled to mip4)
        dt_imageio_jpeg_t jpg;
        if(dt_imageio_jpeg_decompress_header(image->data, image->data_size, &jpg)) goto libraw_fail;
        uint8_t *tmp = (uint8_t *)malloc(sizeof(uint8_t)*jpg.width*jpg.height*4);
        if(dt_imageio_jpeg_decompress(&jpg, tmp))
        {
          free(tmp);
          goto libraw_fail;
        }
        // scale to fit
        dt_iop_flip_and_zoom_8(tmp, jpg.width, jpg.height, buf, wd, ht, orientation, width, height);

        free(tmp);
        res = 0;
      }

      // clean up raw stuff.
      libraw_recycle(raw);
      libraw_close(raw);
      free(image);
      if(0)
      {
libraw_fail:
        // fprintf(stderr,"[imageio] %s: %s\n", filename, libraw_strerror(ret));
        libraw_close(raw);
        res = 1;
      }
    }
  }

  if(res)
  {
    // try the real thing: rawspeed + pixelpipe
    dt_imageio_module_format_t format;
    _dummy_data_t dat;
    format.bpp = _bpp;
    format.write_image = _write_image;
    format.levels = _levels;
    dat.head.max_width  = wd;
    dat.head.max_height = ht;
    dat.buf = buf;
    // export with flags: ignore exif (don't load from disk), don't swap byte order, don't do hq processing, and signal we want thumbnail export
    res = dt_imageio_export_with_flags(imgid, "unused", &format, (dt_imageio_module_data_t *)&dat, 1, 1, 0, 1, NULL);
    if(!res)
    {
      // might be smaller, or have a different aspect than what we got as input.
      *width  = dat.head.width;
      *height = dat.head.height;
    }
  }

  // fprintf(stderr, "[mipmap init 8] export image %u finished (sizes %d %d => %d %d)!\n", imgid, wd, ht, dat.head.width, dat.head.height);

  // any errors?
  if(res)
  {
    // fprintf(stderr, "[mipmap_cache] could not process thumbnail!\n");
    *width = *height = 0;
    return;
  }

  // TODO: various speed optimizations:
  // TODO: also init all smaller mips!
  // TODO: use mipf, but:
  // TODO: if output is cropped, don't use mipf!
}