diff --git a/type.go b/type.go index 3356af5..beb5b3d 100644 --- a/type.go +++ b/type.go @@ -2,6 +2,7 @@ package bimg import ( "regexp" + "sync" "unicode/utf8" ) @@ -46,17 +47,28 @@ var ImageTypes = map[ImageType]string{ MAGICK: "magick", } +// imageMutex is used to provide thread-safe synchronization +// for SupportedImageTypes map. +var imageMutex = &sync.RWMutex{} + // SupportedImageTypes stores the optional image type supported // by the current libvips compilation. -var SupportedImageTypes = map[ImageType]bool{ - JPEG: HasJPEGSupport, - PNG: HasPNGSupport, - WEBP: HasWEBPSupport, - TIFF: HasTIFFSupport, - GIF: HasGIFSupport, - SVG: HasSVGSupport, - PDF: HasPDFSupport, - MAGICK: HasMagickSupport, +// Note: lazy evaluation as demand is required due +// to bootstrap runtime limitation with C/libvips world. +var SupportedImageTypes = map[ImageType]bool{} + +// discoverSupportedImageTypes is used to fill SupportedImageTypes map. +func discoverSupportedImageTypes() { + imageMutex.Lock() + SupportedImageTypes[JPEG] = VipsIsTypeSupported(JPEG) + SupportedImageTypes[PNG] = VipsIsTypeSupported(PNG) + SupportedImageTypes[GIF] = VipsIsTypeSupported(GIF) + SupportedImageTypes[WEBP] = VipsIsTypeSupported(WEBP) + SupportedImageTypes[SVG] = VipsIsTypeSupported(SVG) + SupportedImageTypes[TIFF] = VipsIsTypeSupported(TIFF) + SupportedImageTypes[PDF] = VipsIsTypeSupported(PDF) + SupportedImageTypes[MAGICK] = VipsIsTypeSupported(MAGICK) + imageMutex.Unlock() } // isBinary checks if the given buffer is a binary file. @@ -91,21 +103,35 @@ func DetermineImageTypeName(buf []byte) string { // IsImageTypeSupportedByVips returns true if the given image type // is supported by current libvips compilation. func IsImageTypeSupportedByVips(t ImageType) bool { + imageMutex.RLock() + + // Discover supported image types and cache the result + itShouldDiscovery := len(SupportedImageTypes) == 0 + if itShouldDiscovery { + imageMutex.RUnlock() + discoverSupportedImageTypes() + } + + // Check if image type is actually supported isSupported, ok := SupportedImageTypes[t] + if !itShouldDiscovery { + imageMutex.RUnlock() + } + return ok && isSupported } // IsTypeSupported checks if a given image type is supported func IsTypeSupported(t ImageType) bool { _, ok := ImageTypes[t] - return ok + return ok && IsImageTypeSupportedByVips(t) } // IsTypeNameSupported checks if a given image type name is supported func IsTypeNameSupported(t string) bool { - for _, name := range ImageTypes { + for imageType, name := range ImageTypes { if name == t { - return true + return IsImageTypeSupportedByVips(imageType) } } return false diff --git a/vips.go b/vips.go index c494aa3..535f5ad 100644 --- a/vips.go +++ b/vips.go @@ -30,38 +30,6 @@ const VipsMajorVersion = int(C.VIPS_MAJOR_VERSION) // VipsMinorVersion exposes the current libvips minor version number const VipsMinorVersion = int(C.VIPS_MINOR_VERSION) -// HasJPEGSupport exposes if the current libvips compilation -// supports JPEG images. -const HasJPEGSupport = int(C.VIPS_JPEG_SUPPORT) == 1 - -// HasWEBPSupport exposes if the current libvips compilation -// supports WEBP images. -const HasWEBPSupport = int(C.VIPS_WEBP_SUPPORT) == 1 - -// HasPNGSupport exposes if the current libvips compilation -// supports PNG images. -const HasPNGSupport = int(C.VIPS_PNG_SUPPORT) == 1 - -// HasMagickSupport exposes if the current libvips compilation -// supports libmagick bindings. -const HasMagickSupport = int(C.VIPS_MAGICK_SUPPORT) == 1 - -// HasGIFSupport exposes if the current libvips compilation -// supports GIF images. -const HasGIFSupport = int(C.VIPS_GIF_SUPPORT) == 1 - -// HasSVGSupport exposes if the current libvips compilation -// supports SVG images. -const HasSVGSupport = int(C.VIPS_SVG_SUPPORT) == 1 - -// HasPDFSupport exposes if the current libvips compilation -// supports PDF images. -const HasPDFSupport = int(C.VIPS_PDF_SUPPORT) == 1 - -// HasTIFFSupport exposes if the current libvips compilation -// supports TIFF images. -const HasTIFFSupport = int(C.VIPS_TIFF_SUPPORT) == 1 - const ( maxCacheMem = 100 * 1024 * 1024 maxCacheSize = 500 @@ -169,6 +137,33 @@ func VipsMemory() VipsMemoryInfo { } } +// VipsIsTypeSupported returns true if the given image type +// is supported by the current libvips compilation. +func VipsIsTypeSupported(t ImageType) bool { + if t == JPEG { + return int(C.vips_type_find_bridge(C.JPEG)) != 0 + } + if t == WEBP { + return int(C.vips_type_find_bridge(C.WEBP)) != 0 + } + if t == PNG { + return int(C.vips_type_find_bridge(C.PNG)) != 0 + } + if t == GIF { + return int(C.vips_type_find_bridge(C.GIF)) != 0 + } + if t == PDF { + return int(C.vips_type_find_bridge(C.PDF)) != 0 + } + if t == SVG { + return int(C.vips_type_find_bridge(C.SVG)) != 0 + } + if t == TIFF { + return int(C.vips_type_find_bridge(C.TIFF)) != 0 + } + return false +} + func vipsExifOrientation(image *C.VipsImage) int { return int(C.vips_exif_orientation(image)) } @@ -493,30 +488,30 @@ func vipsImageType(buf []byte) ImageType { if len(buf) == 0 { return UNKNOWN } - if buf[0] == 0x89 && buf[1] == 0x50 && buf[2] == 0x4E && buf[3] == 0x47 { return PNG } if buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF { return JPEG } - if buf[8] == 0x57 && buf[9] == 0x45 && buf[10] == 0x42 && buf[11] == 0x50 { + if IsImageTypeSupportedByVips(WEBP) && buf[8] == 0x57 && buf[9] == 0x45 && buf[10] == 0x42 && buf[11] == 0x50 { return WEBP } - if (buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) || - (buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A) { + if IsImageTypeSupportedByVips(TIFF) && + ((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) || + (buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) { return TIFF } - if buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46 { + if IsImageTypeSupportedByVips(GIF) && buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46 { return GIF } - if buf[0] == 0x25 && buf[1] == 0x50 && buf[2] == 0x44 && buf[3] == 0x46 { + if IsImageTypeSupportedByVips(PDF) && buf[0] == 0x25 && buf[1] == 0x50 && buf[2] == 0x44 && buf[3] == 0x46 { return PDF } - if IsSVGImage(buf) { + if IsImageTypeSupportedByVips(SVG) && IsSVGImage(buf) { return SVG } - if HasMagickSupport && strings.HasSuffix(readImageType(buf), "MagickBuffer") { + if strings.HasSuffix(readImageType(buf), "MagickBuffer") { return MAGICK } return UNKNOWN diff --git a/vips.h b/vips.h index ab1054e..c4e6ed8 100644 --- a/vips.h +++ b/vips.h @@ -3,54 +3,6 @@ #include #include -#ifdef VIPS_MAGICK_H -#define VIPS_MAGICK_SUPPORT 1 -#else -#define VIPS_MAGICK_SUPPORT 0 -#endif - -#ifdef HAVE_JPEG -#define VIPS_JPEG_SUPPORT 1 -#else -#define VIPS_JPEG_SUPPORT 0 -#endif - -#ifdef HAVE_PNG -#define VIPS_PNG_SUPPORT 1 -#else -#define VIPS_PNG_SUPPORT 0 -#endif - -#ifdef HAVE_LIBWEBP -#define VIPS_WEBP_SUPPORT 1 -#else -#define VIPS_WEBP_SUPPORT 0 -#endif - -#ifdef HAVE_GIFLIB -#define VIPS_GIF_SUPPORT 1 -#else -#define VIPS_GIF_SUPPORT 0 -#endif - -#ifdef HAVE_RSVG -#define VIPS_SVG_SUPPORT 1 -#else -#define VIPS_SVG_SUPPORT 0 -#endif - -#ifdef HAVE_POPPLER -#define VIPS_PDF_SUPPORT 1 -#else -#define VIPS_PDF_SUPPORT 0 -#endif - -#ifdef HAVE_TIFF -#define VIPS_TIFF_SUPPORT 1 -#else -#define VIPS_TIFF_SUPPORT 0 -#endif - /** * Starting libvips 7.41, VIPS_ANGLE_x has been renamed to VIPS_ANGLE_Dx * "to help python". So we provide the macro to correctly build for versions @@ -163,6 +115,35 @@ vips_shrink_bridge(VipsImage *in, VipsImage **out, double xshrink, double yshrin return vips_shrink(in, out, xshrink, yshrink, NULL); } +int +vips_type_find_bridge(int t) { + if (t == GIF) { + return vips_type_find("VipsOperation", "gifload"); + } + if (t == PDF) { + return vips_type_find("VipsOperation", "pdfload"); + } + if (t == TIFF) { + return vips_type_find("VipsOperation", "tiffload"); + } + if (t == SVG) { + return vips_type_find("VipsOperation", "svgload"); + } + if (t == WEBP) { + return vips_type_find("VipsOperation", "webpload"); + } + if (t == PNG) { + return vips_type_find("VipsOperation", "pngload"); + } + if (t == JPEG) { + return vips_type_find("VipsOperation", "jpegload"); + } + if (t == MAGICK) { + return vips_type_find("VipsOperation", "magickload"); + } + return 0; +} + int vips_rotate(VipsImage *in, VipsImage **out, int angle) { int rotate = VIPS_ANGLE_D0;