Adds AVIF support

* This adds a new type AVIF to the supported type list if libvips >= 8.9.0
 * Calls libheif through libvips with the AV1 compression set to save AVIF images.
master
Lars Fronius 5 years ago
parent 4f683f98d8
commit 83c14048e0
No known key found for this signature in database
GPG Key ID: 9799DF92255C2D0C

@ -11,7 +11,7 @@ env:
# - LIBVIPS=8.7.4
# - LIBVIPS=8.8.4
# - LIBVIPS=8.9.2
- LIBVIPS=8.10.0
- LIBVIPS=8.10.1
matrix:
allow_failures:
@ -26,7 +26,7 @@ install:
- docker build -t h2non/bimg:ci --build-arg LIBVIPS_VERSION=$LIBVIPS .
script:
- docker run h2non/bimg:ci sh -c 'export LD_LIBRARY_PATH=/vips/lib:$LD_LIBRARY_PATH; export PKG_CONFIG_PATH=/vips/lib/pkgconfig:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig:/usr/X11/lib/pkgconfig; go vet . && golint . && go test -v -race -covermode=atomic -coverprofile=coverage.out'
- docker run h2non/bimg:ci sh -c 'export LD_LIBRARY_PATH=/vips/lib:/usr/local/lib:$LD_LIBRARY_PATH; export PKG_CONFIG_PATH=/vips/lib/pkgconfig:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig:/usr/X11/lib/pkgconfig; go vet . && golint . && go test -v -race -covermode=atomic -coverprofile=coverage.out'
# after_success:
# - goveralls -coverprofile=coverage.out -service=travis-ci

@ -2,6 +2,7 @@ FROM golang:1.14
LABEL maintainer "tomas@aparicio.me"
ARG LIBVIPS_VERSION=8.9.2
ARG LIBHEIF_VERSION=1.9.1
ARG GOLANGCILINT_VERSION=1.29.0
# Installs libvips + required libraries
@ -13,7 +14,18 @@ RUN DEBIAN_FRONTEND=noninteractive \
gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg62-turbo-dev libpng-dev \
libwebp-dev libtiff5-dev libgif-dev libexif-dev libxml2-dev libpoppler-glib-dev \
swig libmagickwand-dev libpango1.0-dev libmatio-dev libopenslide-dev libcfitsio-dev \
libgsf-1-dev fftw3-dev liborc-0.4-dev librsvg2-dev libimagequant-dev libheif-dev && \
libgsf-1-dev fftw3-dev liborc-0.4-dev librsvg2-dev libimagequant-dev && \
cd /tmp && \
curl -fsSLO https://github.com/strukturag/libheif/releases/download/v${LIBHEIF_VERSION}/libheif-${LIBHEIF_VERSION}.tar.gz && \
tar zvxf libheif-${LIBHEIF_VERSION}.tar.gz && \
cd /tmp/libheif-${LIBHEIF_VERSION} && \
./configure --prefix=/vips && \
make && \
make install && \
echo '/vips/lib' > /etc/ld.so.conf.d/vips.conf && \
ldconfig -v && \
export LD_LIBRARY_PATH="/vips/lib:$LD_LIBRARY_PATH" && \
export PKG_CONFIG_PATH="/vips/lib/pkgconfig:$PKG_CONFIG_PATH" && \
cd /tmp && \
curl -fsSLO https://github.com/libvips/libvips/releases/download/v${LIBVIPS_VERSION}/vips-${LIBVIPS_VERSION}.tar.gz && \
tar zvxf vips-${LIBVIPS_VERSION}.tar.gz && \
@ -32,6 +44,8 @@ RUN DEBIAN_FRONTEND=noninteractive \
make install && \
ldconfig
ENV LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH"
# Install runtime dependencies
# RUN DEBIAN_FRONTEND=noninteractive \
# apt-get update && \
@ -62,4 +76,4 @@ COPY . .
# apt-get clean && \
# rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
CMD [ "/bin/bash" ]
CMD [ "/bin/bash" ]

@ -352,8 +352,8 @@ func TestImageAutoRotate(t *testing.T) {
}
tests := []struct {
file string
orientation int
file string
orientation int
}{
{"exif/Landscape_1.jpg", 1},
{"exif/Landscape_2.jpg", 1},

@ -51,7 +51,7 @@ func resizer(buf []byte, o Options) ([]byte, error) {
}
// If JPEG or HEIF image, retrieve the buffer
if rotated && (imageType == JPEG || imageType == HEIF) && !o.NoAutoRotate {
if rotated && (imageType == JPEG || imageType == HEIF || imageType == AVIF) && !o.NoAutoRotate {
buf, err = getImageBuffer(image)
if err != nil {
return nil, err

@ -30,6 +30,8 @@ const (
MAGICK
// HEIF represents the HEIC/HEIF/HVEC image type
HEIF
// AVIF represents the AVIF image type.
AVIF
)
var (
@ -48,6 +50,7 @@ var ImageTypes = map[ImageType]string{
SVG: "svg",
MAGICK: "magick",
HEIF: "heif",
AVIF: "avif",
}
// imageMutex is used to provide thread-safe synchronization

@ -22,6 +22,7 @@ func TestDeterminateImageType(t *testing.T) {
{"test.heic", HEIF},
{"test2.heic", HEIF},
{"test3.heic", HEIF},
{"test.avif", AVIF},
}
for _, file := range files {
@ -51,12 +52,16 @@ func TestDeterminateImageTypeName(t *testing.T) {
{"test.svg", "svg"},
// {"test.jp2", "magick"},
{"test.heic", "heif"},
{"test.avif", "avif"},
}
for _, file := range files {
if file.expected == "heif" && VipsMajorVersion <= 8 && VipsMinorVersion < 8 {
continue
}
if file.expected == "avif" && VipsMajorVersion <= 8 && VipsMinorVersion < 9 {
continue
}
img, _ := os.Open(path.Join("testdata", file.name))
buf, _ := ioutil.ReadAll(img)
@ -73,13 +78,16 @@ func TestIsTypeSupported(t *testing.T) {
types := []struct {
name ImageType
}{
{JPEG}, {PNG}, {WEBP}, {GIF}, {PDF}, {HEIF},
{JPEG}, {PNG}, {WEBP}, {GIF}, {PDF}, {HEIF}, {AVIF},
}
for _, n := range types {
if n.name == HEIF && VipsMajorVersion <= 8 && VipsMinorVersion < 8 {
continue
}
if n.name == AVIF && VipsMajorVersion <= 8 && VipsMinorVersion < 9 {
continue
}
if IsTypeSupported(n.name) == false {
t.Fatalf("Image type %s is not valid", ImageTypes[n.name])
}
@ -97,12 +105,16 @@ func TestIsTypeNameSupported(t *testing.T) {
{"gif", true},
{"pdf", true},
{"heif", true},
{"avif", true},
}
for _, n := range types {
if n.name == "heif" && VipsMajorVersion <= 8 && VipsMinorVersion < 8 {
continue
}
if n.name == "avif" && VipsMajorVersion <= 8 && VipsMinorVersion < 9 {
continue
}
if IsTypeNameSupported(n.name) != n.expected {
t.Fatalf("Image type %s is not valid", n.name)
}
@ -121,6 +133,9 @@ func TestIsTypeSupportedSave(t *testing.T) {
if VipsVersion >= "8.8.0" {
types = append(types, struct{ name ImageType }{HEIF})
}
if VipsVersion >= "8.9.0" {
types = append(types, struct{ name ImageType }{AVIF})
}
for _, n := range types {
if IsTypeSupportedSave(n.name) == false {
@ -141,6 +156,7 @@ func TestIsTypeNameSupportedSave(t *testing.T) {
{"pdf", false},
{"tiff", VipsVersion >= "8.5.0"},
{"heif", VipsVersion >= "8.8.0"},
{"avif", VipsVersion >= "8.9.0"},
}
for _, n := range types {

@ -190,6 +190,9 @@ func VipsIsTypeSupported(t ImageType) bool {
if t == HEIF {
return int(C.vips_type_find_bridge(C.HEIF)) != 0
}
if t == AVIF {
return int(C.vips_type_find_bridge(C.HEIF)) != 0
}
return false
}
@ -212,6 +215,9 @@ func VipsIsTypeSupportedSave(t ImageType) bool {
if t == HEIF {
return int(C.vips_type_find_save_bridge(C.HEIF)) != 0
}
if t == AVIF {
return int(C.vips_type_find_save_bridge(C.HEIF)) != 0
}
return false
}
@ -501,6 +507,8 @@ func vipsSave(image *C.VipsImage, o vipsSaveOptions) ([]byte, error) {
saveErr = C.vips_tiffsave_bridge(tmpImage, &ptr, &length)
case HEIF:
saveErr = C.vips_heifsave_bridge(tmpImage, &ptr, &length, strip, quality, lossless)
case AVIF:
saveErr = C.vips_avifsave_bridge(tmpImage, &ptr, &length, strip, quality, lossless)
default:
saveErr = C.vips_jpegsave_bridge(tmpImage, &ptr, &length, strip, quality, interlace)
}
@ -735,6 +743,10 @@ func vipsImageType(buf []byte) ImageType {
// This is a HEIFS file, ftyphevc
return HEIF
}
if IsTypeSupported(HEIF) && buf[4] == 0x66 && buf[5] == 0x74 && buf[6] == 0x79 && buf[7] == 0x70 &&
buf[8] == 0x61 && buf[9] == 0x76 && buf[10] == 0x69 && buf[11] == 0x66 {
return AVIF
}
return UNKNOWN
}

@ -34,6 +34,7 @@ enum types {
SVG,
MAGICK,
HEIF,
AVIF
};
typedef struct {
@ -335,7 +336,7 @@ vips_pngsave_bridge(VipsImage *in, void **buf, size_t *len, int strip, int compr
"compression", compression,
"interlace", INT_TO_GBOOLEAN(interlace),
"filter", VIPS_FOREIGN_PNG_FILTER_ALL,
"palette", INT_TO_GBOOLEAN(palette),
"palette", INT_TO_GBOOLEAN(palette),
NULL
);
#else
@ -367,6 +368,21 @@ vips_tiffsave_bridge(VipsImage *in, void **buf, size_t *len) {
#endif
}
int
vips_avifsave_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 >= 9))
return vips_heifsave_buffer(in, buf, len,
"strip", INT_TO_GBOOLEAN(strip),
"Q", quality,
"lossless", INT_TO_GBOOLEAN(lossless),
"compression", VIPS_FOREIGN_HEIF_COMPRESSION_AV1,
NULL
);
#else
return 0;
#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))

@ -58,6 +58,22 @@ func TestVipsSaveTiff(t *testing.T) {
}
}
func TestVipsSafeAvif(t *testing.T) {
if !IsTypeSupportedSave(AVIF) {
t.Skipf("Format %#v is not supported", ImageTypes[AVIF])
}
image, _, _ := vipsRead(readImage("test.jpg"))
options := vipsSaveOptions{Quality: 95, Type: AVIF}
buf, err := vipsSave(image, options)
if err != nil {
t.Fatalf("Error saving image type %v: %v", ImageTypes[AVIF], err)
}
if len(buf) == 0 {
t.Fatalf("Empty saved '%v' image", ImageTypes[AVIF])
}
}
func TestVipsRotate(t *testing.T) {
files := []struct {
name string

Loading…
Cancel
Save