diff --git a/testdata/test.heic b/testdata/test.heic new file mode 100644 index 0000000..ccd31a6 Binary files /dev/null and b/testdata/test.heic differ diff --git a/type.go b/type.go index 260adbf..ea7ef90 100644 --- a/type.go +++ b/type.go @@ -25,6 +25,8 @@ const ( SVG // MAGICK represents the libmagick compatible genetic image type. MAGICK + // HEIF represents the HEIC/HEIF/HVEC image type + HEIF ) // ImageType represents an image type value. @@ -45,6 +47,7 @@ var ImageTypes = map[ImageType]string{ PDF: "pdf", SVG: "svg", MAGICK: "magick", + HEIF: "heif", } // imageMutex is used to provide thread-safe synchronization diff --git a/type_test.go b/type_test.go index 92ac4e5..b62b818 100644 --- a/type_test.go +++ b/type_test.go @@ -19,6 +19,7 @@ func TestDeterminateImageType(t *testing.T) { {"test.pdf", PDF}, {"test.svg", SVG}, {"test.jp2", MAGICK}, + {"test.heic", HEIF}, } for _, file := range files { @@ -46,6 +47,7 @@ func TestDeterminateImageTypeName(t *testing.T) { {"test.pdf", "pdf"}, {"test.svg", "svg"}, {"test.jp2", "magick"}, + {"test.heic", "heif"}, } for _, file := range files { @@ -63,7 +65,7 @@ func TestIsTypeSupported(t *testing.T) { types := []struct { name ImageType }{ - {JPEG}, {PNG}, {WEBP}, {GIF}, {PDF}, + {JPEG}, {PNG}, {WEBP}, {GIF}, {PDF}, {HEIF}, } for _, n := range types { @@ -83,6 +85,7 @@ func TestIsTypeNameSupported(t *testing.T) { {"webp", true}, {"gif", true}, {"pdf", true}, + {"heif", true}, } for _, n := range types { @@ -101,6 +104,9 @@ func TestIsTypeSupportedSave(t *testing.T) { if VipsVersion >= "8.5.0" { types = append(types, struct{ name ImageType }{TIFF}) } + if VipsVersion >= "8.8.0" { + types = append(types, struct{ name ImageType }{HEIF}) + } for _, n := range types { if IsTypeSupportedSave(n.name) == false { @@ -120,6 +126,7 @@ func TestIsTypeNameSupportedSave(t *testing.T) { {"gif", false}, {"pdf", false}, {"tiff", VipsVersion >= "8.5.0"}, + {"heif", VipsVersion >= "8.8.0"}, } for _, n := range types { diff --git a/version.go b/version.go index 69e7efb..6fb0af8 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package bimg // Version represents the current package semantic version. -const Version = "1.0.18" +const Version = "1.0.19" diff --git a/vips.go b/vips.go index 25b77ae..0d93a60 100644 --- a/vips.go +++ b/vips.go @@ -185,6 +185,9 @@ func VipsIsTypeSupported(t ImageType) bool { if t == MAGICK { return int(C.vips_type_find_bridge(C.MAGICK)) != 0 } + if t == HEIF { + return int(C.vips_type_find_bridge(C.HEIF)) != 0 + } return false } @@ -204,6 +207,9 @@ func VipsIsTypeSupportedSave(t ImageType) bool { if t == TIFF { return int(C.vips_type_find_save_bridge(C.TIFF)) != 0 } + if t == HEIF { + return int(C.vips_type_find_save_bridge(C.HEIF)) != 0 + } return false } @@ -431,6 +437,8 @@ func vipsSave(image *C.VipsImage, o vipsSaveOptions) ([]byte, error) { saveErr = C.vips_pngsave_bridge(tmpImage, &ptr, &length, strip, C.int(o.Compression), quality, interlace) case TIFF: saveErr = C.vips_tiffsave_bridge(tmpImage, &ptr, &length) + case HEIF: + saveErr = C.vips_heifsave_bridge(tmpImage, &ptr, &length, strip, quality, lossless) default: saveErr = C.vips_jpegsave_bridge(tmpImage, &ptr, &length, strip, quality, interlace) } @@ -634,6 +642,12 @@ func vipsImageType(buf []byte) ImageType { if IsTypeSupported(MAGICK) && strings.HasSuffix(readImageType(buf), "MagickBuffer") { return MAGICK } + // NOTE: libheif current;y only supports heic sub types; see: + // https://github.com/strukturag/libheif/issues/83#issuecomment-421427091 + if IsTypeSupported(HEIF) && buf[4] == 0x66 && buf[5] == 0x74 && buf[6] == 0x79 && buf[7] == 0x70 && + buf[8] == 0x68 && buf[9] == 0x65 && buf[10] == 0x69 && buf[11] == 0x63 { + return HEIF + } return UNKNOWN } diff --git a/vips.h b/vips.h index e0d623a..747e460 100644 --- a/vips.h +++ b/vips.h @@ -32,7 +32,10 @@ enum types { GIF, PDF, SVG, - MAGICK + MAGICK, +#if (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 8)) + HEIF, +#endif }; typedef struct { @@ -156,6 +159,11 @@ vips_type_find_bridge(int t) { if (t == MAGICK) { return vips_type_find("VipsOperation", "magickload"); } +#if (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 8)) + if (t == HEIF) { + return vips_type_find("VipsOperation", "heifload"); + } +#endif return 0; } @@ -173,6 +181,11 @@ vips_type_find_save_bridge(int t) { if (t == JPEG) { return vips_type_find("VipsOperation", "jpegsave_buffer"); } +#if (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 8)) + if (t == HEIF) { + return vips_type_find("VipsOperation", "heifsave_buffer"); + } +#endif return 0; } @@ -324,6 +337,20 @@ vips_tiffsave_bridge(VipsImage *in, void **buf, size_t *len) { #endif } +int +vips_heifsave_bridge(VipsImage *in, void **buf, size_t *len, int strip, int quality, int lossless) { +#if (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 8)) + return vips_heifsave_buffer(in, buf, len, + "strip", INT_TO_GBOOLEAN(strip), + "Q", quality, + "lossless", INT_TO_GBOOLEAN(lossless), + NULL + ); +#else + return 0; +#endif +} + int vips_is_16bit (VipsInterpretation interpretation) { return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16; @@ -370,6 +397,10 @@ vips_init_image (void *buf, size_t len, int imageType, VipsImage **out) { #endif } else if (imageType == MAGICK) { code = vips_magickload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL); +#endif +#if (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 8)) + } else if (imageType == HEIF) { + code = vips_heifload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL); #endif }