Beispiel #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;
}
Beispiel #2
0
// load a full-res thumbnail:
int dt_imageio_large_thumbnail(const char *filename, uint8_t **buffer, int32_t *width, int32_t *height,
                               dt_colorspaces_color_profile_type_t *color_space)
{
  int res = 1;

  uint8_t *buf = NULL;
  char *mime_type = NULL;
  size_t bufsize;

  // get the biggest thumb from exif
  if(dt_exif_get_thumbnail(filename, &buf, &bufsize, &mime_type)) goto error;

  if(strcmp(mime_type, "image/jpeg") == 0)
  {
    // Decompress the JPG into our own memory format
    dt_imageio_jpeg_t jpg;
    if(dt_imageio_jpeg_decompress_header(buf, bufsize, &jpg)) goto error;
    *buffer = (uint8_t *)malloc((size_t)sizeof(uint8_t) * jpg.width * jpg.height * 4);
    if(!*buffer) goto error;

    *width = jpg.width;
    *height = jpg.height;
    // TODO: check if the embedded thumbs have a color space set! currently we assume that it's always sRGB
    *color_space = DT_COLORSPACE_SRGB;
    if(dt_imageio_jpeg_decompress(&jpg, *buffer))
    {
      free(*buffer);
      *buffer = NULL;
      goto error;
    }

    res = 0;
  }
  else
  {
#ifdef HAVE_GRAPHICSMAGICK
    ExceptionInfo exception;
    Image *image = NULL;
    ImageInfo *image_info = NULL;

    GetExceptionInfo(&exception);
    image_info = CloneImageInfo((ImageInfo *)NULL);

    image = BlobToImage(image_info, buf, bufsize, &exception);

    if(exception.severity != UndefinedException) CatchException(&exception);

    if(!image)
    {
      fprintf(stderr, "[dt_imageio_large_thumbnail GM] thumbnail not found?\n");
      goto error_gm;
    }

    *width = image->columns;
    *height = image->rows;
    *color_space = DT_COLORSPACE_SRGB; // FIXME: this assumes that embedded thumbnails are always srgb

    *buffer = (uint8_t *)malloc((size_t)sizeof(uint8_t) * image->columns * image->rows * 4);
    if(!*buffer) goto error_gm;

    for(uint32_t row = 0; row < image->rows; row++)
    {
      uint8_t *bufprt = *buffer + (size_t)4 * row * image->columns;
      int gm_ret = DispatchImage(image, 0, row, image->columns, 1, "RGBP", CharPixel, bufprt, &exception);

      if(exception.severity != UndefinedException) CatchException(&exception);

      if(gm_ret != MagickPass)
      {
        fprintf(stderr, "[dt_imageio_large_thumbnail GM] error_gm reading thumbnail\n");
        free(*buffer);
        *buffer = NULL;
        goto error_gm;
      }
    }

    // fprintf(stderr, "[dt_imageio_large_thumbnail GM] successfully decoded thumbnail\n");
    res = 0;

  error_gm:
    if(image) DestroyImage(image);
    if(image_info) DestroyImageInfo(image_info);
    DestroyExceptionInfo(&exception);
    if(res) goto error;
#else
    fprintf(stderr, "[dt_imageio_large_thumbnail] error: The thumbnail image is not in JPEG format, but DT "
                    "was built without GraphicsMagick. Please rebuild DT with GraphicsMagick support "
                    "enabled.\n");
#endif
  }

  if(res)
  {
    fprintf(
        stderr,
        "[dt_imageio_large_thumbnail] error: Not a supported thumbnail image format or broken thumbnail: %s\n",
        mime_type);
    goto error;
  }

error:
  free(mime_type);
  free(buf);
  return res;
}
Beispiel #3
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!
}
Beispiel #4
0
static int
dt_mipmap_cache_deserialize(dt_mipmap_cache_t *cache)
{
  int32_t rd = 0;
  const dt_mipmap_size_t mip = DT_MIPMAP_2;
  uint8_t *blob = NULL;
  FILE *f = NULL;
  int file_width[mip+1], file_height[mip+1];

  gchar dbfilename[DT_MAX_PATH_LEN];
  if (dt_mipmap_cache_get_filename(dbfilename, sizeof(dbfilename)))
  {
    fprintf(stderr, "[mipmap_cache] could not retrieve cache filename; not deserializing\n");
    return 1;
  }
  if (!strcmp(dbfilename, ":memory:"))
  {
    // fprintf(stderr, "[mipmap_cache] library is in memory; not deserializing\n");
    return 0;
  }

  // drop any old cache if the database is new. in that case newly imported images will probably mapped to old thumbnails
  if(dt_database_is_new(darktable.db) && g_file_test(dbfilename, G_FILE_TEST_IS_REGULAR))
  {
    fprintf(stderr, "[mipmap_cache] database is new, dropping old cache `%s'\n", dbfilename);
    goto read_finalize;
  }

  f = fopen(dbfilename, "rb");
  if(!f)
  {
    if (errno == ENOENT)
    {
      fprintf(stderr, "[mipmap_cache] cache is empty, file `%s' doesn't exist\n", dbfilename);
    }
    else
    {
      fprintf(stderr, "[mipmap_cache] failed to open the cache from `%s'\n", dbfilename);
    }
    goto read_finalize;
  }

  // read version info:
  const int32_t magic = DT_MIPMAP_CACHE_FILE_MAGIC + DT_MIPMAP_CACHE_FILE_VERSION;
  int32_t magic_file = 0;
  rd = fread(&magic_file, sizeof(int32_t), 1, f);
  if(rd != 1) goto read_error;
  if(magic_file == DT_MIPMAP_CACHE_FILE_MAGIC + 22)
  {
    // same format, but compression was broken in 22 and below
  }
  else if(magic_file != magic)
  {
    if(magic_file > DT_MIPMAP_CACHE_FILE_MAGIC && magic_file < magic)
      fprintf(stderr, "[mipmap_cache] cache version too old, dropping `%s' cache\n", dbfilename);
    else
      fprintf(stderr, "[mipmap_cache] invalid cache file, dropping `%s' cache\n", dbfilename);
    goto read_finalize;
  }

  // also read compression type and yell out on missmatch.
  int32_t compression = -1;
  rd = fread(&compression, sizeof(int32_t), 1, f);
  if(rd != 1) goto read_error;
  if(compression != cache->compression_type)
  {
    fprintf(stderr, "[mipmap_cache] cache is %s, but settings say we should use %s, dropping `%s' cache\n",
            compression == 0 ? "uncompressed" : (compression == 1 ? "low quality compressed" : "high quality compressed"),
            cache->compression_type == 0 ? "no compression" : (cache->compression_type == 1 ? "low quality compression" : "high quality compression"),
            dbfilename);
    goto read_finalize;
  }
  if(compression && (magic_file == DT_MIPMAP_CACHE_FILE_MAGIC + 22))
  {
    // compression is enabled and we have the affected version. can't read that.
    fprintf(stderr, "[mipmap_cache] dropping compressed cache v22 to regenerate mips without artifacts.\n");
    goto read_finalize;
  }

  for (int i=0; i<=mip; i++)
  {
    rd = fread(&file_width[i], sizeof(int32_t), 1, f);
    if(rd != 1) goto read_error;
    rd = fread(&file_height[i], sizeof(int32_t), 1, f);
    if(rd != 1) goto read_error;
    if(file_width[i]  != cache->mip[i].max_width ||
        file_height[i] != cache->mip[i].max_height)
    {
      fprintf(stderr, "[mipmap_cache] cache settings changed, dropping `%s' cache\n", dbfilename);
      goto read_finalize;
    }
  }

  if(cache->compression_type) blob = NULL;
  else blob = malloc(sizeof(uint32_t)*file_width[mip]*file_height[mip]);

  while(!feof(f))
  {
    int level = 0;
    rd = fread(&level, sizeof(int), 1, f);
    if (level > mip) break;

    int32_t key = 0;
    rd = fread(&key, sizeof(int32_t), 1, f);
    if(rd != 1) break; // first value is break only, goes to eof.
    int32_t length = 0;
    rd = fread(&length, sizeof(int32_t), 1, f);
    if(rd != 1) goto read_error;

    uint8_t *data = (uint8_t *)dt_cache_read_get(&cache->mip[level].cache, key);
    struct dt_mipmap_buffer_dsc* dsc = (struct dt_mipmap_buffer_dsc*)data;
    if(dsc->flags & DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE)
    {
      if(cache->compression_type)
      {
        int32_t wd, ht;
        rd = fread(&wd, sizeof(int32_t), 1, f);
        if(rd != 1) goto read_error;
        rd = fread(&ht, sizeof(int32_t), 1, f);
        if(rd != 1) goto read_error;
        dsc->width = wd;
        dsc->height = ht;
        if(length != compressed_buffer_size(cache->compression_type, wd, ht)) goto read_error;
        // directly read from disk into cache:
        rd = fread(data + sizeof(*dsc), 1, length, f);
        if(rd != length) goto read_error;
      }
      else
      {
        // jpg too large?
        if(length > sizeof(uint32_t)*file_width[mip]*file_height[mip]) goto read_error;
        rd = fread(blob, sizeof(uint8_t), length, f);
        if(rd != length) goto read_error;
        // no compression, the image is still compressed on disk, as jpg
        dt_imageio_jpeg_t jpg;
        if(dt_imageio_jpeg_decompress_header(blob, length, &jpg) ||
            (jpg.width > file_width[level] || jpg.height > file_height[level]) ||
            dt_imageio_jpeg_decompress(&jpg, data+sizeof(*dsc)))
        {
          fprintf(stderr, "[mipmap_cache] failed to decompress thumbnail for image %d!\n", get_imgid(key));
        }
        dsc->width = jpg.width;
        dsc->height = jpg.height;
      }
      dsc->flags &= ~DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE;
      // these come write locked in case idata[3] == 1, so release that!
      dt_cache_write_release(&cache->mip[level].cache, key);
    }
    dt_cache_read_release(&cache->mip[level].cache, key);
  }

  fclose(f);
  free(blob);
  return 0;

read_error:
  fprintf(stderr, "[mipmap_cache] failed to recover the cache from `%s'\n", dbfilename);
read_finalize:
  if(f) fclose(f);
  free(blob);
  g_unlink(dbfilename);
  return 1;
}
Beispiel #5
0
// callback for the cache backend to initialize payload pointers
void dt_mipmap_cache_allocate_dynamic(void *data, dt_cache_entry_t *entry)
{
  dt_mipmap_cache_t *cache = (dt_mipmap_cache_t *)data;
  // for full image buffers
  struct dt_mipmap_buffer_dsc *dsc = entry->data;
  const dt_mipmap_size_t mip = get_size(entry->key);
  // alloc mere minimum for the header + broken image buffer:
  if(!dsc)
  {
    if(mip <= DT_MIPMAP_F)
    {
      // these are fixed-size:
      entry->data = dt_alloc_align(16, cache->buffer_size[mip]);
    }
    else
    {
      entry->data = dt_alloc_align(16, sizeof(*dsc) + sizeof(float) * 4 * 64);
    }
    // fprintf(stderr, "[mipmap cache] alloc dynamic for key %u %p\n", key, *buf);
    if(!(entry->data))
    {
      fprintf(stderr, "[mipmap cache] memory allocation failed!\n");
      exit(1);
    }
    dsc = entry->data;
    if(mip <= DT_MIPMAP_F)
    {
      dsc->width = cache->max_width[mip];
      dsc->height = cache->max_height[mip];
      dsc->size = cache->buffer_size[mip];
      dsc->color_space = DT_COLORSPACE_NONE;
    }
    else
    {
      dsc->width = 0;
      dsc->height = 0;
      dsc->color_space = DT_COLORSPACE_NONE;
      dsc->size = sizeof(*dsc) + sizeof(float) * 4 * 64;
    }
  }
  assert(dsc->size >= sizeof(*dsc));

  int loaded_from_disk = 0;
  if(mip < DT_MIPMAP_F)
  {
    if(cache->cachedir[0] && dt_conf_get_bool("cache_disk_backend"))
    {
      // try and load from disk, if successful set flag
      char filename[PATH_MAX] = {0};
      snprintf(filename, sizeof(filename), "%s.d/%d/%d.jpg", cache->cachedir, mip, get_imgid(entry->key));
      FILE *f = fopen(filename, "rb");
      if(f)
      {
        long len = 0;
        uint8_t *blob = 0;
        fseek(f, 0, SEEK_END);
        len = ftell(f);
        if(len <= 0) goto read_error; // coverity madness
        blob = (uint8_t *)malloc(len);
        if(!blob) goto read_error;
        fseek(f, 0, SEEK_SET);
        int rd = fread(blob, sizeof(uint8_t), len, f);
        if(rd != len) goto read_error;
        dt_colorspaces_color_profile_type_t color_space;
        dt_imageio_jpeg_t jpg;
        if(dt_imageio_jpeg_decompress_header(blob, len, &jpg)
           || (jpg.width > cache->max_width[mip] || jpg.height > cache->max_height[mip])
           || ((color_space = dt_imageio_jpeg_read_color_space(&jpg)) == DT_COLORSPACE_NONE) // pointless test to keep it in the if clause
           || dt_imageio_jpeg_decompress(&jpg, entry->data + sizeof(*dsc)))
        {
          fprintf(stderr, "[mipmap_cache] failed to decompress thumbnail for image %d from `%s'!\n", get_imgid(entry->key), filename);
          goto read_error;
        }
        dsc->width = jpg.width;
        dsc->height = jpg.height;
        dsc->color_space = color_space;
        loaded_from_disk = 1;
        if(0)
        {
read_error:
          g_unlink(filename);
        }
        free(blob);
        fclose(f);
      }
    }
  }

  if(!loaded_from_disk)
    dsc->flags = DT_MIPMAP_BUFFER_DSC_FLAG_GENERATE;
  else dsc->flags = 0;

  // cost is just flat one for the buffer, as the buffers might have different sizes,
  // to make sure quota is meaningful.
  if(mip >= DT_MIPMAP_F) entry->cost = 1;
  else entry->cost = cache->buffer_size[mip];
}