Skip to content

Commit

Permalink
Merge pull request #17367 from ralfbrown/imageio_errors
Browse files Browse the repository at this point in the history
improve handling of unsupported images
  • Loading branch information
TurboGit authored Aug 31, 2024
2 parents 009a1e8 + 89b5ce4 commit b85a45f
Show file tree
Hide file tree
Showing 27 changed files with 573 additions and 171 deletions.
5 changes: 4 additions & 1 deletion src/common/darktable.c
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,10 @@ dt_imgid_t dt_load_from_string(const gchar *input,
if(!loaded)
{
id = NO_IMGID;
dt_control_log(_("file `%s' has unknown format!"), filename);
if(buf.loader_status == DT_IMAGEIO_UNSUPPORTED_FORMAT || buf.loader_status == DT_IMAGEIO_UNSUPPORTED_FEATURE)
dt_control_log(_("file `%s' has unsupported format!"), filename);
else
dt_control_log(_("file `%s' has unknown format!"), filename);
}
else
{
Expand Down
1 change: 1 addition & 0 deletions src/common/image.c
Original file line number Diff line number Diff line change
Expand Up @@ -2149,6 +2149,7 @@ void dt_image_init(dt_image_t *img)
dt_mark_colormatrix_invalid(&img->adobe_XYZ_to_CAM[k][i]);

img->job_flags = DT_IMAGE_JOB_NONE;
img->load_status = DT_IMAGEIO_OK;
}

void dt_image_refresh_makermodel(dt_image_t *img)
Expand Down
12 changes: 10 additions & 2 deletions src/common/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,13 @@ typedef enum dt_imageio_retval_t
{
DT_IMAGEIO_OK = 0, // all good :)
DT_IMAGEIO_FILE_NOT_FOUND, // file has been lost
DT_IMAGEIO_LOAD_FAILED, // file either corrupted or in a format
// not supported by the current loader.
DT_IMAGEIO_LOAD_FAILED, // file either corrupted or in a format not supported by the current loader,
// and a more detailed error from among those below is not available.
DT_IMAGEIO_UNSUPPORTED_FORMAT, // the file type is not supported; may be one which is a build-time option
DT_IMAGEIO_UNSUPPORTED_CAMERA, // the file type is supported, but the camera model is not
DT_IMAGEIO_UNSUPPORTED_FEATURE, // the file uses an unsupported feature such as compression type
DT_IMAGEIO_FILE_CORRUPTED, // invalid data was detected while parsing the file
DT_IMAGEIO_IOERROR, // a read error occurred while loading the file
DT_IMAGEIO_CACHE_FULL // buffer allocation for image data failed
} dt_imageio_retval_t;

Expand Down Expand Up @@ -347,6 +352,9 @@ typedef struct dt_image_t
struct dt_cache_entry_t *cache_entry;

dt_image_job_flag_t job_flags;

/* result of attempting to load the image, needed to be able to report why the image can't be displayed */
dt_imageio_retval_t load_status;
} dt_image_t;

// should be in datetime.h, workaround to solve cross references
Expand Down
324 changes: 263 additions & 61 deletions src/common/mipmap_cache.c

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/common/mipmap_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ typedef struct dt_mipmap_buffer_t
float iscale;
uint8_t *buf;
dt_colorspaces_color_profile_type_t color_space;
dt_imageio_retval_t loader_status;
dt_cache_entry_t *cache_entry;
} dt_mipmap_buffer_t;

Expand Down
4 changes: 3 additions & 1 deletion src/develop/develop.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ void dt_dev_process_image_job(dt_develop_t *dev,
port ? DT_MIPMAP_FULL : DT_MIPMAP_F,
port ? DT_MIPMAP_BLOCKING : DT_MIPMAP_BEST_EFFORT,
'r');
dev->image_storage.load_status = buf.loader_status;

dt_show_times(&start, "[dt_dev_process_image_job] loading image.");

Expand Down Expand Up @@ -525,7 +526,8 @@ static inline void _dt_dev_load_raw(dt_develop_t *dev,
dev->image_storage = *image;
dt_image_cache_read_release(darktable.image_cache, image);

dev->requested_id = dev->image_storage.id;
// dev->requested_id = (dev->image_storage.load_status == DT_IMAGEIO_OK) ? dev->image_storage.id : 0;
dev->requested_id = dev->image_storage.id;
}

void dt_dev_reload_image(dt_develop_t *dev,
Expand Down
6 changes: 3 additions & 3 deletions src/dtgtk/thumbnail.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
This file is part of darktable,
Copyright (C) 2019-2023 darktable developers.
Copyright (C) 2019-2024 darktable developers.
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -681,11 +681,11 @@ static gboolean _event_image_draw(GtkWidget *widget,
cairo_scale(cr2, scale, scale);

cairo_set_source_surface(cr2, tmp_surface, 0, 0);
// set filter no nearest: in skull mode, we want to see big
// set filter to nearest: in skull/error mode, we want to see big
// pixels. in 1 iir mode for the right mip, we want to see
// exactly what the pipe gave us, 1:1 pixel for pixel. in
// between, filtering just makes stuff go unsharp.
if((buf_width <= 8 && buf_height <= 8) || fabsf(scale - 1.0f) < 0.01f)
if((buf_width <= 30 && buf_height <= 30) || fabsf(scale - 1.0f) < 0.01f)
cairo_pattern_set_filter(cairo_get_source(cr2), CAIRO_FILTER_NEAREST);
else
cairo_pattern_set_filter(cairo_get_source(cr2), darktable.gui->filter_image);
Expand Down
34 changes: 22 additions & 12 deletions src/imageio/imageio.c
Original file line number Diff line number Diff line change
Expand Up @@ -790,9 +790,15 @@ gboolean dt_imageio_export_with_flags(const dt_imgid_t imgid,
if(!buf.buf || !buf.width || !buf.height)
{
dt_print(DT_DEBUG_ALWAYS,
"[dt_imageio_export_with_flags] mipmap allocation for `%s' failed\n",
filename);
dt_control_log(_("image `%s' is not available!"), img->filename);
"[dt_imageio_export_with_flags] mipmap allocation for `%s' failed (status %d)\n",
filename,img->load_status);
if(img->load_status == DT_IMAGEIO_FILE_NOT_FOUND)
dt_control_log(_("image `%s' is not available!"), img->filename);
else if(img->load_status == DT_IMAGEIO_LOAD_FAILED || img->load_status == DT_IMAGEIO_IOERROR ||
img->load_status == DT_IMAGEIO_CACHE_FULL)
dt_control_log(_("unable to load image `%s'!"), img->filename);
else
dt_control_log(_("image '%s' not supported"), img->filename);
goto error_early;
}

Expand Down Expand Up @@ -1308,7 +1314,7 @@ dt_imageio_retval_t dt_imageio_open(dt_image_t *img,
/* first of all, check if file exists, don't bother to test loading
* if not exists */
if(!g_file_test(filename, G_FILE_TEST_IS_REGULAR))
return !DT_IMAGEIO_OK;
return DT_IMAGEIO_FILE_NOT_FOUND;

const int32_t was_hdr = (img->flags & DT_IMAGE_HDR);
const int32_t was_bw = dt_image_monochrome_flags(img);
Expand Down Expand Up @@ -1344,16 +1350,20 @@ dt_imageio_retval_t dt_imageio_open(dt_image_t *img,
ret = dt_imageio_open_rawspeed(img, filename, buf);
}

/* fallback that tries to open file via LibRaw to support Canon CR3 */
if(ret != DT_IMAGEIO_OK && ret != DT_IMAGEIO_CACHE_FULL)
ret = dt_imageio_open_libraw(img, filename, buf);
// if rawspeed tried but failed to load a known filetype, skip the attempts to try other loaders
if(ret != DT_IMAGEIO_UNSUPPORTED_FEATURE && ret != DT_IMAGEIO_FILE_CORRUPTED && ret != DT_IMAGEIO_IOERROR)
{
/* fallback that tries to open file via LibRaw to support Canon CR3 */
if(ret != DT_IMAGEIO_OK && ret != DT_IMAGEIO_CACHE_FULL)
ret = dt_imageio_open_libraw(img, filename, buf);

if(ret != DT_IMAGEIO_OK && ret != DT_IMAGEIO_CACHE_FULL)
ret = dt_imageio_open_qoi(img, filename, buf);
if(ret != DT_IMAGEIO_OK && ret != DT_IMAGEIO_CACHE_FULL)
ret = dt_imageio_open_qoi(img, filename, buf);

/* fallback that tries to open file via GraphicsMagick */
if(ret != DT_IMAGEIO_OK && ret != DT_IMAGEIO_CACHE_FULL)
ret = dt_imageio_open_exotic(img, filename, buf);
/* fallback that tries to open file via GraphicsMagick */
if(ret != DT_IMAGEIO_OK && ret != DT_IMAGEIO_CACHE_FULL)
ret = dt_imageio_open_exotic(img, filename, buf);
}

if((ret == DT_IMAGEIO_OK) && !was_hdr && (img->flags & DT_IMAGE_HDR))
dt_imageio_set_hdr_tag(img);
Expand Down
4 changes: 2 additions & 2 deletions src/imageio/imageio_avif.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ dt_imageio_retval_t dt_imageio_open_avif(dt_image_t *img,
// We shouldn't expect AVIF images in files with an extension other than .avif
char *ext = g_strrstr(filename, ".");
if(ext && g_ascii_strcasecmp(ext, ".avif"))
return DT_IMAGEIO_LOAD_FAILED;
return DT_IMAGEIO_UNSUPPORTED_FORMAT;

dt_imageio_retval_t ret;
avifImage *avif_image = avifImageCreateEmpty();
Expand All @@ -63,7 +63,7 @@ dt_imageio_retval_t dt_imageio_open_avif(dt_image_t *img,
if(result != AVIF_RESULT_OK)
{
dt_print(DT_DEBUG_IMAGEIO, "[avif_open] failed to parse `%s': %s\n", filename, avifResultToString(result));
ret = DT_IMAGEIO_LOAD_FAILED;
ret = DT_IMAGEIO_FILE_CORRUPTED;
goto out;
}

Expand Down
4 changes: 2 additions & 2 deletions src/imageio/imageio_exr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ dt_imageio_retval_t dt_imageio_open_exr(dt_image_t *img,

/* verify openexr image */
if(!Imf::isOpenExrFile((const char *)filename, isTiled))
return DT_IMAGEIO_LOAD_FAILED;
return DT_IMAGEIO_UNSUPPORTED_FORMAT;

/* open exr file */
try
Expand Down Expand Up @@ -106,7 +106,7 @@ dt_imageio_retval_t dt_imageio_open_exr(dt_image_t *img,
dt_print(DT_DEBUG_ALWAYS,
"[exr_open] error: only images with RGB(A) channels are supported,"
" skipping `%s'\n", img->filename);
return DT_IMAGEIO_LOAD_FAILED;
return DT_IMAGEIO_UNSUPPORTED_FEATURE;
}

if(!img->exif_inited)
Expand Down
12 changes: 7 additions & 5 deletions src/imageio/imageio_heif.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
This file is part of darktable,
Copyright (C) 2021-2023 darktable developers.
Copyright (C) 2021-2024 darktable developers.
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -62,20 +62,22 @@ dt_imageio_retval_t dt_imageio_open_heif(dt_image_t *img,
err = heif_context_read_from_file(ctx, filename, NULL);
if(err.code != heif_error_Ok)
{
ret = DT_IMAGEIO_LOAD_FAILED;
if(err.code == heif_error_Unsupported_feature && err.subcode == heif_suberror_Unsupported_codec)
{
/* we want to feedback this to the user, so output to stderr */
dt_print(DT_DEBUG_ALWAYS,
"[imageio_heif] Unsupported codec for `%s'. "
"Check if your libheif is built with HEVC and/or AV1 decoding support.\n",
filename);
ret = DT_IMAGEIO_UNSUPPORTED_FEATURE;
}
else if(err.code != heif_error_Unsupported_filetype && err.subcode != heif_suberror_No_ftyp_box)
{
/* print debug info only if genuine HEIF */
dt_print(DT_DEBUG_IMAGEIO, "Failed to read HEIF file [%s]: %s\n", filename, err.message);
ret = DT_IMAGEIO_UNSUPPORTED_FORMAT;
}
ret = DT_IMAGEIO_LOAD_FAILED;
goto out;
}

Expand All @@ -86,7 +88,7 @@ dt_imageio_retval_t dt_imageio_open_heif(dt_image_t *img,
dt_print(DT_DEBUG_IMAGEIO,
"No images found in HEIF file [%s]\n",
filename);
ret = DT_IMAGEIO_LOAD_FAILED;
ret = DT_IMAGEIO_FILE_CORRUPTED;
goto out;
}

Expand All @@ -97,7 +99,7 @@ dt_imageio_retval_t dt_imageio_open_heif(dt_image_t *img,
dt_print(DT_DEBUG_IMAGEIO,
"Failed to read primary image from HEIF file [%s]\n",
filename);
ret = DT_IMAGEIO_LOAD_FAILED;
ret = DT_IMAGEIO_UNSUPPORTED_FEATURE;
goto out;
}

Expand Down Expand Up @@ -169,7 +171,7 @@ dt_imageio_retval_t dt_imageio_open_heif(dt_image_t *img,
dt_print(DT_DEBUG_IMAGEIO,
"Failed to decode HEIF file [%s]\n",
filename);
ret = DT_IMAGEIO_LOAD_FAILED;
ret = DT_IMAGEIO_FILE_CORRUPTED;
goto out;
}

Expand Down
24 changes: 13 additions & 11 deletions src/imageio/imageio_j2k.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ dt_imageio_retval_t dt_imageio_open_j2k(dt_image_t *img, const char *filename, d
g_strlcpy(parameters.infile, filename, sizeof(parameters.infile));

parameters.decod_format = get_file_format(filename);
if(parameters.decod_format == -1) return DT_IMAGEIO_LOAD_FAILED;
if(parameters.decod_format == -1)
return DT_IMAGEIO_UNSUPPORTED_FORMAT;

if(!img->exif_inited) (void)dt_exif_read(img, filename);

Expand All @@ -101,7 +102,7 @@ dt_imageio_retval_t dt_imageio_open_j2k(dt_image_t *img, const char *filename, d
{
fclose(fsrc);
dt_print(DT_DEBUG_ALWAYS, "[j2k_open] Error: fread returned a number of elements different from the expected.\n");
return DT_IMAGEIO_FILE_NOT_FOUND;
return DT_IMAGEIO_FILE_CORRUPTED;
}
fclose(fsrc);

Expand All @@ -116,7 +117,7 @@ dt_imageio_retval_t dt_imageio_open_j2k(dt_image_t *img, const char *filename, d
else // this will also reject jpt files.
{
dt_print(DT_DEBUG_ALWAYS, "[j2k_open] Error: `%s' has unsupported file format.\n", filename);
return DT_IMAGEIO_LOAD_FAILED;
return DT_IMAGEIO_UNSUPPORTED_FORMAT;
}


Expand All @@ -130,7 +131,7 @@ dt_imageio_retval_t dt_imageio_open_j2k(dt_image_t *img, const char *filename, d
codec = OPJ_CODEC_JPT;
else
{
return DT_IMAGEIO_LOAD_FAILED; // can't happen
return DT_IMAGEIO_UNSUPPORTED_FEATURE; // can't happen
}

d_codec = opj_create_decompress(codec);
Expand Down Expand Up @@ -178,7 +179,7 @@ dt_imageio_retval_t dt_imageio_open_j2k(dt_image_t *img, const char *filename, d
opj_stream_destroy(d_stream);
opj_destroy_codec(d_codec);
opj_image_destroy(image);
return DT_IMAGEIO_LOAD_FAILED;
return DT_IMAGEIO_IOERROR;
}

/* Get the decoded image */
Expand All @@ -188,7 +189,7 @@ dt_imageio_retval_t dt_imageio_open_j2k(dt_image_t *img, const char *filename, d
opj_destroy_codec(d_codec);
opj_stream_destroy(d_stream);
opj_image_destroy(image);
return DT_IMAGEIO_LOAD_FAILED;
return DT_IMAGEIO_FILE_CORRUPTED;
}

/* Close the byte stream */
Expand All @@ -197,7 +198,7 @@ dt_imageio_retval_t dt_imageio_open_j2k(dt_image_t *img, const char *filename, d
if(!image)
{
dt_print(DT_DEBUG_ALWAYS, "[j2k_open] Error: failed to decode image `%s'\n", filename);
ret = DT_IMAGEIO_LOAD_FAILED;
ret = DT_IMAGEIO_FILE_CORRUPTED;
goto end_of_the_world;
}

Expand All @@ -223,7 +224,7 @@ dt_imageio_retval_t dt_imageio_open_j2k(dt_image_t *img, const char *filename, d
if(image->numcomps == 0 || image->x1 == 0 || image->y1 == 0)
{
dt_print(DT_DEBUG_ALWAYS, "[j2k_open] Error: invalid raw image parameters in `%s'\n", filename);
ret = DT_IMAGEIO_LOAD_FAILED;
ret = DT_IMAGEIO_FILE_CORRUPTED;
goto end_of_the_world;
}

Expand All @@ -232,14 +233,14 @@ dt_imageio_retval_t dt_imageio_open_j2k(dt_image_t *img, const char *filename, d
if(image->comps[i].w != image->x1 || image->comps[i].h != image->y1)
{
dt_print(DT_DEBUG_ALWAYS, "[j2k_open] Error: some component has different size in `%s'\n", filename);
ret = DT_IMAGEIO_LOAD_FAILED;
ret = DT_IMAGEIO_FILE_CORRUPTED;
goto end_of_the_world;
}
if(image->comps[i].prec > 16)
{
dt_print(DT_DEBUG_ALWAYS, "[j2k_open] Error: precision %d is larger than 16 in `%s'\n", image->comps[1].prec,
filename);
ret = DT_IMAGEIO_LOAD_FAILED;
ret = DT_IMAGEIO_UNSUPPORTED_FEATURE;
goto end_of_the_world;
}
}
Expand Down Expand Up @@ -331,7 +332,8 @@ int dt_imageio_j2k_read_profile(const char *filename, uint8_t **out)
g_strlcpy(parameters.infile, filename, sizeof(parameters.infile));

parameters.decod_format = get_file_format(filename);
if(parameters.decod_format == -1) return DT_IMAGEIO_LOAD_FAILED;
if(parameters.decod_format == -1)
return DT_IMAGEIO_UNSUPPORTED_FORMAT;

/* read the input file and put it in memory */
/* ---------------------------------------- */
Expand Down
9 changes: 5 additions & 4 deletions src/imageio/imageio_jpeg.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
This file is part of darktable,
Copyright (C) 2009-2023 darktable developers.
Copyright (C) 2009-2024 darktable developers.
darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -768,21 +768,22 @@ dt_imageio_retval_t dt_imageio_open_jpeg(dt_image_t *img,

if(memcmp(first3bytes, jpeg_magicbytes, 3) != 0)
{
return DT_IMAGEIO_LOAD_FAILED;
return DT_IMAGEIO_UNSUPPORTED_FORMAT;
}

if(!img->exif_inited) (void)dt_exif_read(img, filename);

dt_imageio_jpeg_t jpg;
if(dt_imageio_jpeg_read_header(filename, &jpg)) return DT_IMAGEIO_LOAD_FAILED;
if(dt_imageio_jpeg_read_header(filename, &jpg))
return DT_IMAGEIO_FILE_CORRUPTED;
img->width = jpg.width;
img->height = jpg.height;

uint8_t *tmp = dt_alloc_align_uint8(4 * jpg.width * jpg.height);
if(dt_imageio_jpeg_read(&jpg, tmp))
{
dt_free_align(tmp);
return DT_IMAGEIO_LOAD_FAILED;
return DT_IMAGEIO_FILE_CORRUPTED;
}

img->buf_dsc.channels = 4;
Expand Down
Loading

0 comments on commit b85a45f

Please sign in to comment.