diff options
Diffstat (limited to 'src/imlib.c')
-rw-r--r-- | src/imlib.c | 194 |
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 { /* |