summaryrefslogtreecommitdiff
path: root/src/imlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/imlib.c')
-rw-r--r--src/imlib.c194
1 files changed, 169 insertions, 25 deletions
diff --git a/src/imlib.c b/src/imlib.c
index e06a813..9ee4c08 100644
--- a/src/imlib.c
+++ b/src/imlib.c
@@ -164,11 +164,31 @@ int feh_load_image_char(Imlib_Image * im, char *filename)
return(i);
}
-void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err)
+void feh_print_load_error(char *file, winwidget w, Imlib_Load_Error err, enum feh_load_error feh_err)
{
if (err == IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS)
eprintf("%s - Out of file descriptors while loading", file);
else if (!opt.quiet || w) {
+ switch (feh_err) {
+ case LOAD_ERROR_IMLIB:
+ // handled in the next switch/case statement
+ break;
+ case LOAD_ERROR_IMAGEMAGICK:
+ im_weprintf(w, "%s - No ImageMagick loader for that file format", file);
+ break;
+ case LOAD_ERROR_CURL:
+ im_weprintf(w, "%s - libcurl was unable to retrieve the file", file);
+ break;
+ case LOAD_ERROR_DCRAW:
+ im_weprintf(w, "%s - Unable to open preview via dcraw", file);
+ break;
+ case LOAD_ERROR_MAGICBYTES:
+ im_weprintf(w, "%s - Does not look like an image (magic bytes missing)", file);
+ break;
+ }
+ if (feh_err != LOAD_ERROR_IMLIB) {
+ return;
+ }
switch (err) {
case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST:
im_weprintf(w, "%s - File does not exist", file);
@@ -215,9 +235,104 @@ void feh_imlib_print_load_error(char *file, winwidget w, Imlib_Load_Error err)
}
}
+/*
+ * This is a workaround for an Imlib2 regression, causing unloadable image
+ * detection to be excessively slow (and, thus, causing feh to hang for a while
+ * when encountering an unloadable image). We use magic byte detection to
+ * avoid calling Imlib2 for files it probably cannot handle. See
+ * <https://phab.enlightenment.org/T8739> and
+ * <https://github.com/derf/feh/issues/505>.
+ *
+ * Note that this drops support for bz2-compressed files, unless
+ * FEH_SKIP_MAGIC is set
+ */
+int feh_is_image(feh_file * file)
+{
+ unsigned char buf[16];
+ FILE *fh = fopen(file->filename, "r");
+ if (!fh) {
+ return 0;
+ }
+ if (fread(buf, 1, 16, fh) != 16) {
+ fclose(fh);
+ return 0;
+ }
+ fclose(fh);
+
+ if (buf[0] == 0xff && buf[1] == 0xd8) {
+ // JPEG
+ return 1;
+ }
+ if (!memcmp(buf, "\x89PNG\x0d\x0a\x1a\x0a", 8)) {
+ // PNG
+ return 1;
+ }
+ if (buf[0] == 'A' && buf[1] == 'R' && buf[2] == 'G' && buf[3] == 'B') {
+ // ARGB
+ return 1;
+ }
+ if (buf[0] == 'B' && buf[1] == 'M') {
+ // BMP
+ return 1;
+ }
+ if (!memcmp(buf, "farbfeld", 8)) {
+ // farbfeld
+ return 1;
+ }
+ if (buf[0] == 'G' && buf[1] == 'I' && buf[2] == 'F') {
+ // GIF
+ return 1;
+ }
+ if (buf[0] == 0x00 && buf[1] == 0x00 && buf[2] <= 0x02 && buf[3] == 0x00) {
+ // ICO
+ return 1;
+ }
+ if (!memcmp(buf, "FORM", 4)) {
+ // Amiga IFF ILBM
+ return 1;
+ }
+ if (buf[0] == 'P' && buf[1] >= '1' && buf[1] <= '7') {
+ // PNM et al.
+ return 1;
+ }
+ if (strstr(file->filename, ".tga")) {
+ // TGA
+ return 1;
+ }
+ if (!memcmp(buf, "II\x2a\x00", 4) || !memcmp(buf, "MM\x00\x2a", 4)) {
+ // TIFF
+ return 1;
+ }
+ if (!memcmp(buf, "RIFF", 4)) {
+ // might be webp
+ return 1;
+ }
+ if (!memcmp(buf + 4, "ftyphei", 7) || !memcmp(buf + 4, "ftypmif1", 8)) {
+ // HEIC/HEIF - note that this is only supported in imlib2-heic. Ordinary
+ // imlib2 releases do not support heic/heif images as of 2021-01.
+ return 1;
+ }
+ buf[15] = 0;
+ if (strstr((char *)buf, "XPM")) {
+ // XPM
+ return 1;
+ }
+ if (strstr(file->filename, ".bz2") || strstr(file->filename, ".gz")) {
+ // Imlib2 supports compressed images. It relies on the filename to
+ // determine the appropriate loader and does not use magic bytes here.
+ return 1;
+ }
+ // moved to the end as this variable won't be set in most cases
+ if (getenv("FEH_SKIP_MAGIC")) {
+ return 1;
+ }
+ return 0;
+}
+
int feh_load_image(Imlib_Image * im, feh_file * file)
{
Imlib_Load_Error err = IMLIB_LOAD_ERROR_NONE;
+ enum feh_load_error feh_err = LOAD_ERROR_IMLIB;
enum { SRC_IMLIB, SRC_HTTP, SRC_MAGICK, SRC_DCRAW } image_source = SRC_IMLIB;
char *tmpname = NULL;
char *real_filename = NULL;
@@ -230,23 +345,37 @@ int feh_load_image(Imlib_Image * im, feh_file * file)
if (path_is_url(file->filename)) {
image_source = SRC_HTTP;
- if ((tmpname = feh_http_load_image(file->filename)) == NULL)
+ if ((tmpname = feh_http_load_image(file->filename)) == NULL) {
+ feh_err = LOAD_ERROR_CURL;
err = IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST;
+ }
}
- else if (opt.conversion_timeout >= 0 && feh_file_is_raw(file->filename)) {
- image_source = SRC_DCRAW;
- tmpname = feh_dcraw_load_image(file->filename);
- if (!tmpname)
+ else {
+ if (feh_is_image(file)) {
+ *im = imlib_load_image_with_error_return(file->filename, &err);
+ } else {
+ feh_err = LOAD_ERROR_MAGICBYTES;
err = IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT;
+ }
}
- else
- *im = imlib_load_image_with_error_return(file->filename, &err);
if (opt.conversion_timeout >= 0 && (
(err == IMLIB_LOAD_ERROR_UNKNOWN) ||
(err == IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT))) {
- image_source = SRC_MAGICK;
- tmpname = feh_magick_load_image(file->filename);
+ if (feh_file_is_raw(file->filename)) {
+ image_source = SRC_DCRAW;
+ tmpname = feh_dcraw_load_image(file->filename);
+ if (!tmpname) {
+ feh_err = LOAD_ERROR_DCRAW;
+ }
+ } else {
+ image_source = SRC_MAGICK;
+ feh_err = LOAD_ERROR_IMLIB;
+ tmpname = feh_magick_load_image(file->filename);
+ if (!tmpname) {
+ feh_err = LOAD_ERROR_IMAGEMAGICK;
+ }
+ }
}
if (tmpname) {
@@ -269,7 +398,14 @@ int feh_load_image(Imlib_Image * im, feh_file * file)
file->filename = real_filename;
#ifdef HAVE_LIBEXIF
- file->ed = exif_get_data(tmpname);
+ /*
+ * if we're called from within feh_reload_image, file->ed is already
+ * populated.
+ */
+ if (file->ed) {
+ exif_data_unref(file->ed);
+ }
+ file->ed = exif_data_new_from_file(tmpname);
#endif
}
if (!opt.use_conversion_cache && ((image_source != SRC_HTTP) || !opt.keep_http))
@@ -279,8 +415,19 @@ int feh_load_image(Imlib_Image * im, feh_file * file)
// add_file_to_rm_filelist duplicates tmpname
add_file_to_rm_filelist(tmpname);
- if (image_source != SRC_HTTP && !opt.use_conversion_cache)
+ if (!opt.use_conversion_cache)
free(tmpname);
+ } else if (im) {
+#ifdef HAVE_LIBEXIF
+ /*
+ * if we're called from within feh_reload_image, file->ed is already
+ * populated.
+ */
+ if (file->ed) {
+ exif_data_unref(file->ed);
+ }
+ file->ed = exif_data_new_from_file(file->filename);
+#endif
}
if ((err) || (!im)) {
@@ -288,7 +435,7 @@ int feh_load_image(Imlib_Image * im, feh_file * file)
fputs("\n", stderr);
reset_output = 1;
}
- feh_imlib_print_load_error(file->filename, NULL, err);
+ feh_print_load_error(file->filename, NULL, err, feh_err);
D(("Load *failed*\n"));
return(0);
}
@@ -305,14 +452,12 @@ int feh_load_image(Imlib_Image * im, feh_file * file)
#ifdef HAVE_LIBEXIF
int orientation = 0;
- ExifData *exifData = exif_data_new_from_file(file->filename);
- if (exifData) {
- ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
- ExifEntry *exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION);
+ if (file->ed) {
+ ExifByteOrder byteOrder = exif_data_get_byte_order(file->ed);
+ ExifEntry *exifEntry = exif_data_get_entry(file->ed, EXIF_TAG_ORIENTATION);
if (exifEntry && opt.auto_rotate)
orientation = exif_get_short(exifEntry->data, byteOrder);
}
- file->ed = exifData;
if (orientation == 2)
gib_imlib_image_flip_horizontal(*im);
@@ -430,11 +575,9 @@ static int feh_file_is_raw(char *filename)
}
if (childpid == 0) {
- if (opt.quiet) {
- int devnull = open("/dev/null", O_WRONLY);
- dup2(devnull, 1);
- dup2(devnull, 2);
- }
+ int devnull = open("/dev/null", O_WRONLY);
+ dup2(devnull, 1);
+ dup2(devnull, 2);
execlp("dcraw", "dcraw", "-i", filename, NULL);
_exit(1);
} else {
@@ -1087,6 +1230,7 @@ void feh_draw_exif(winwidget w)
info_buf[i], IMLIB_TEXT_TO_RIGHT, 0, 0, 0, 255);
gib_imlib_text_draw(im, fn, NULL, 1, (i * line_height) + 1,
info_buf[i], IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
+ free(info_buf[i]);
}
@@ -1417,8 +1561,8 @@ void feh_edit_inplace(winwidget w, int op)
FEH_FILE(w->file->data)->filename, &err);
gib_imlib_free_image(old);
if (err)
- feh_imlib_print_load_error(FEH_FILE(w->file->data)->filename,
- w, err);
+ feh_print_load_error(FEH_FILE(w->file->data)->filename,
+ w, err, LOAD_ERROR_IMLIB);
feh_reload_image(w, 1, 1);
} else {
/*