diff --git a/libvips/foreign/magick.c b/libvips/foreign/magick.c index f4a5eab0..75ef879d 100644 --- a/libvips/foreign/magick.c +++ b/libvips/foreign/magick.c @@ -52,12 +52,24 @@ #if defined(HAVE_MAGICK6) || defined(HAVE_MAGICK7) -/* ImageMagick can't detect some formats, like ICO, by examining the contents -- - * ico.c simply does not have a recogniser. +/* ImageMagick can't detect some formats, like ICO and TGA, by examining the contents -- + * ico.c and tga.c simply do not have recognisers. * * For these formats, do the detection ourselves. - * * Return an IM format specifier, or NULL to let IM do the detection. + * + * For sniffing TGAs, we check that there is at least enough room for the header and that + * the preamble contains valid values: + * ----------------------------------------------------------- + * |0x00 | 0-255 idlength, skip | + * |0x01 | 0-1, color map present | + * |0x02 | Any of (0, 1, 2, 3, 9, 10, 11), Image type | + * ----------------------------------------------------------- + * + * References: + * * https://www.dca.fee.unicamp.br/~martino/disciplinas/ea978/tgaffs.pdf + * * http://www.paulbourke.net/dataformats/tga/ + * * https://en.wikipedia.org/wiki/Truevision_TGA#Technical_details */ static const char * magick_sniff( const unsigned char *bytes, size_t length ) @@ -68,6 +80,16 @@ magick_sniff( const unsigned char *bytes, size_t length ) bytes[2] == 1 && bytes[3] == 0 ) return( "ICO" ); + if( length >= 18 && + (bytes[1] == 0 || bytes[1] == 1) && ( + bytes[2] == 0 || + bytes[2] == 1 || + bytes[2] == 2 || + bytes[2] == 3 || + bytes[2] == 9 || + bytes[2] == 10 || + bytes[2] == 11) ) + return( "TGA" ); return( NULL ); } diff --git a/test/test-suite/helpers/helpers.py b/test/test-suite/helpers/helpers.py index 0fa2bab6..c754d06d 100644 --- a/test/test-suite/helpers/helpers.py +++ b/test/test-suite/helpers/helpers.py @@ -41,6 +41,8 @@ DICOM_FILE = os.path.join(IMAGES, "dicom_test_image.dcm") BMP_FILE = os.path.join(IMAGES, "MARBLES.BMP") NIFTI_FILE = os.path.join(IMAGES, "avg152T1_LR_nifti.nii.gz") ICO_FILE = os.path.join(IMAGES, "favicon.ico") +TGA_FILE = os.path.join(IMAGES, "targa.tga") +SGI_FILE = os.path.join(IMAGES, "silicongraphics.sgi") AVIF_FILE = os.path.join(IMAGES, "avif-orientation-6.avif") HEIC_FILE = os.path.join(IMAGES, "heic-orientation-6.heic") RGBA_FILE = os.path.join(IMAGES, "rgba.png") diff --git a/test/test-suite/images/silicongraphics.sgi b/test/test-suite/images/silicongraphics.sgi new file mode 100644 index 00000000..cf29abd1 Binary files /dev/null and b/test/test-suite/images/silicongraphics.sgi differ diff --git a/test/test-suite/images/targa.tga b/test/test-suite/images/targa.tga new file mode 100644 index 00000000..44518783 Binary files /dev/null and b/test/test-suite/images/targa.tga differ diff --git a/test/test-suite/test_foreign.py b/test/test-suite/test_foreign.py index 29f0889a..ebdf944a 100644 --- a/test/test-suite/test_foreign.py +++ b/test/test-suite/test_foreign.py @@ -10,7 +10,7 @@ from helpers import \ JPEG_FILE, SRGB_FILE, MATLAB_FILE, PNG_FILE, TIF_FILE, OME_FILE, \ ANALYZE_FILE, GIF_FILE, WEBP_FILE, EXR_FILE, FITS_FILE, OPENSLIDE_FILE, \ PDF_FILE, SVG_FILE, SVGZ_FILE, SVG_GZ_FILE, GIF_ANIM_FILE, DICOM_FILE, \ - BMP_FILE, NIFTI_FILE, ICO_FILE, AVIF_FILE, TRUNCATED_FILE, \ + BMP_FILE, NIFTI_FILE, ICO_FILE, TGA_FILE, SGI_FILE, AVIF_FILE, TRUNCATED_FILE, \ GIF_ANIM_EXPECTED_PNG_FILE, GIF_ANIM_DISPOSE_BACKGROUND_FILE, \ GIF_ANIM_DISPOSE_BACKGROUND_EXPECTED_PNG_FILE, \ GIF_ANIM_DISPOSE_PREVIOUS_FILE, \ @@ -664,6 +664,22 @@ class TestForeign: assert im.width == 16 assert im.height == 16 + # libvips has its own sniffer for TGA, test that + with open(TGA_FILE, 'rb') as f: + buf = f.read() + im = pyvips.Image.new_from_buffer(buf, "") + assert im.width == 433 + assert im.height == 433 + + # Test SGI/RGB files to sanity check that sniffers + # aren't too broad + with open(SGI_FILE, 'rb') as f: + buf = f.read() + im = pyvips.Image.new_from_buffer(buf, "") + assert im.width == 433 + assert im.height == 433 + + # load should see metadata like eg. icc profiles im = pyvips.Image.magickload(JPEG_FILE) assert len(im.get("icc-profile-data")) == 564