mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-04-07 06:50:09 +02:00
Add AVIF support
This commit is contained in:
parent
bdbb496c18
commit
1bd789df4e
@ -1,6 +1,8 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- AVIF support.
|
||||
|
||||
## [2.15.0] - 2020-09-03
|
||||
### Added
|
||||
|
@ -1,4 +1,4 @@
|
||||
ARG BASE_IMAGE_VERSION="v1.0.4"
|
||||
ARG BASE_IMAGE_VERSION="v1.2.0"
|
||||
|
||||
FROM darthsim/imgproxy-base:${BASE_IMAGE_VERSION}
|
||||
LABEL maintainer="Sergey Alexandrovich <darthsim@gmail.com>"
|
||||
|
@ -24,6 +24,7 @@ const (
|
||||
imageTypeICO = imageType(C.ICO)
|
||||
imageTypeSVG = imageType(C.SVG)
|
||||
imageTypeHEIC = imageType(C.HEIC)
|
||||
imageTypeAVIF = imageType(C.AVIF)
|
||||
imageTypeBMP = imageType(C.BMP)
|
||||
imageTypeTIFF = imageType(C.TIFF)
|
||||
|
||||
@ -40,6 +41,7 @@ var (
|
||||
"ico": imageTypeICO,
|
||||
"svg": imageTypeSVG,
|
||||
"heic": imageTypeHEIC,
|
||||
"avif": imageTypeAVIF,
|
||||
"bmp": imageTypeBMP,
|
||||
"tiff": imageTypeTIFF,
|
||||
}
|
||||
@ -52,6 +54,7 @@ var (
|
||||
imageTypeICO: "image/x-icon",
|
||||
imageTypeSVG: "image/svg+xml",
|
||||
imageTypeHEIC: "image/heif",
|
||||
imageTypeAVIF: "image/avif",
|
||||
imageTypeBMP: "image/bmp",
|
||||
imageTypeTIFF: "image/tiff",
|
||||
}
|
||||
@ -64,6 +67,7 @@ var (
|
||||
imageTypeICO: "inline; filename=\"%s.ico\"",
|
||||
imageTypeSVG: "inline; filename=\"%s.svg\"",
|
||||
imageTypeHEIC: "inline; filename=\"%s.heic\"",
|
||||
imageTypeAVIF: "inline; filename=\"%s.avif\"",
|
||||
imageTypeBMP: "inline; filename=\"%s.bmp\"",
|
||||
imageTypeTIFF: "inline; filename=\"%s.tiff\"",
|
||||
}
|
||||
|
@ -1,195 +0,0 @@
|
||||
package imagemeta
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const heicBoxHeaderSize = int64(8)
|
||||
|
||||
var heicBrand = []byte("heic")
|
||||
var heicPict = []byte("pict")
|
||||
|
||||
type heicDimensionsData struct {
|
||||
Width, Height int64
|
||||
}
|
||||
|
||||
func (d *heicDimensionsData) IsFilled() bool {
|
||||
return d.Width > 0 && d.Height > 0
|
||||
}
|
||||
|
||||
func heicReadBoxHeader(r io.Reader) (boxType string, boxDataSize int64, err error) {
|
||||
b := make([]byte, heicBoxHeaderSize)
|
||||
_, err = io.ReadFull(r, b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
boxDataSize = int64(binary.BigEndian.Uint32(b[0:4])) - heicBoxHeaderSize
|
||||
boxType = string(b[4:8])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func heicReadBoxData(r io.Reader, boxDataSize int64) (b []byte, err error) {
|
||||
b = make([]byte, boxDataSize)
|
||||
_, err = io.ReadFull(r, b)
|
||||
return
|
||||
}
|
||||
|
||||
func heicReadFtyp(r io.Reader, boxDataSize int64) error {
|
||||
if boxDataSize < 8 {
|
||||
return errors.New("Invalid ftyp data")
|
||||
}
|
||||
|
||||
data, err := heicReadBoxData(r, boxDataSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if bytes.Equal(data[0:4], heicBrand) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if boxDataSize >= 12 {
|
||||
for i := int64(8); i < boxDataSize; i += 4 {
|
||||
if bytes.Equal(data[i:i+4], heicBrand) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("Image is not compatible with heic")
|
||||
}
|
||||
|
||||
func heicReadMeta(d *heicDimensionsData, r io.Reader, boxDataSize int64) error {
|
||||
if boxDataSize < 4 {
|
||||
return errors.New("Invalid meta data")
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(r, make([]byte, 4)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if boxDataSize > 4 {
|
||||
if err := heicReadBoxes(d, io.LimitReader(r, boxDataSize-4)); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func heicReadHldr(r io.Reader, boxDataSize int64) error {
|
||||
if boxDataSize < 12 {
|
||||
return errors.New("Invalid hdlr data")
|
||||
}
|
||||
|
||||
data, err := heicReadBoxData(r, boxDataSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(data[8:12], heicPict) {
|
||||
return fmt.Errorf("Invalid handler. Expected: pict, actual: %s", data[8:12])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func heicReadIspe(r io.Reader, boxDataSize int64) (w, h int64, err error) {
|
||||
if boxDataSize < 12 {
|
||||
return 0, 0, errors.New("Invalid ispe data")
|
||||
}
|
||||
|
||||
data, err := heicReadBoxData(r, boxDataSize)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
w = int64(binary.BigEndian.Uint32(data[4:8]))
|
||||
h = int64(binary.BigEndian.Uint32(data[8:12]))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func heicReadBoxes(d *heicDimensionsData, r io.Reader) error {
|
||||
for {
|
||||
boxType, boxDataSize, err := heicReadBoxHeader(r)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if boxDataSize < 0 {
|
||||
return errors.New("Invalid box data")
|
||||
}
|
||||
|
||||
// log.Printf("Box type: %s; Box data size: %d", boxType, boxDataSize)
|
||||
|
||||
switch boxType {
|
||||
case "ftyp":
|
||||
if err := heicReadFtyp(r, boxDataSize); err != nil {
|
||||
return err
|
||||
}
|
||||
case "meta":
|
||||
if err := heicReadMeta(d, r, boxDataSize); err != nil {
|
||||
return err
|
||||
}
|
||||
if !d.IsFilled() {
|
||||
return errors.New("Dimensions data wasn't found in meta box")
|
||||
}
|
||||
return nil
|
||||
case "hdlr":
|
||||
if err := heicReadHldr(r, boxDataSize); err != nil {
|
||||
return nil
|
||||
}
|
||||
case "iprp", "ipco":
|
||||
if err := heicReadBoxes(d, io.LimitReader(r, boxDataSize)); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
case "ispe":
|
||||
w, h, err := heicReadIspe(r, boxDataSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if w > d.Width || h > d.Height {
|
||||
d.Width, d.Height = w, h
|
||||
}
|
||||
case "mdat":
|
||||
return errors.New("mdat box occurred before meta box")
|
||||
default:
|
||||
if _, err := heicReadBoxData(r, boxDataSize); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func DecodeHeicMeta(r io.Reader) (Meta, error) {
|
||||
d := new(heicDimensionsData)
|
||||
|
||||
if err := heicReadBoxes(d, r); err != nil && !d.IsFilled() {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &meta{
|
||||
format: "heic",
|
||||
width: int(d.Width),
|
||||
height: int(d.Height),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterFormat("????ftypheic", DecodeHeicMeta)
|
||||
RegisterFormat("????ftypheix", DecodeHeicMeta)
|
||||
RegisterFormat("????ftyphevc", DecodeHeicMeta)
|
||||
RegisterFormat("????ftypheim", DecodeHeicMeta)
|
||||
RegisterFormat("????ftypheis", DecodeHeicMeta)
|
||||
RegisterFormat("????ftyphevm", DecodeHeicMeta)
|
||||
RegisterFormat("????ftyphevs", DecodeHeicMeta)
|
||||
RegisterFormat("????ftypmif1", DecodeHeicMeta)
|
||||
}
|
212
imagemeta/heif.go
Normal file
212
imagemeta/heif.go
Normal file
@ -0,0 +1,212 @@
|
||||
package imagemeta
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const heifBoxHeaderSize = int64(8)
|
||||
|
||||
var heicBrand = []byte("heic")
|
||||
var avifBrand = []byte("avif")
|
||||
var heifPict = []byte("pict")
|
||||
|
||||
type heifData struct {
|
||||
Format string
|
||||
Width, Height int64
|
||||
}
|
||||
|
||||
func (d *heifData) IsFilled() bool {
|
||||
return len(d.Format) > 0 && d.Width > 0 && d.Height > 0
|
||||
}
|
||||
|
||||
func heifReadBoxHeader(r io.Reader) (boxType string, boxDataSize int64, err error) {
|
||||
b := make([]byte, heifBoxHeaderSize)
|
||||
_, err = io.ReadFull(r, b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
boxDataSize = int64(binary.BigEndian.Uint32(b[0:4])) - heifBoxHeaderSize
|
||||
boxType = string(b[4:8])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func heifReadBoxData(r io.Reader, boxDataSize int64) (b []byte, err error) {
|
||||
b = make([]byte, boxDataSize)
|
||||
_, err = io.ReadFull(r, b)
|
||||
return
|
||||
}
|
||||
|
||||
func heifAssignFormat(d *heifData, brand []byte) bool {
|
||||
if bytes.Equal(brand, heicBrand) {
|
||||
d.Format = "heic"
|
||||
return true
|
||||
}
|
||||
|
||||
if bytes.Equal(brand, avifBrand) {
|
||||
d.Format = "avif"
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func heifReadFtyp(d *heifData, r io.Reader, boxDataSize int64) error {
|
||||
if boxDataSize < 8 {
|
||||
return errors.New("Invalid ftyp data")
|
||||
}
|
||||
|
||||
data, err := heifReadBoxData(r, boxDataSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if heifAssignFormat(d, data[0:4]) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if boxDataSize >= 12 {
|
||||
for i := int64(8); i < boxDataSize; i += 4 {
|
||||
if heifAssignFormat(d, data[i:i+4]) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("Image is not compatible with heic/avif")
|
||||
}
|
||||
|
||||
func heifReadMeta(d *heifData, r io.Reader, boxDataSize int64) error {
|
||||
if boxDataSize < 4 {
|
||||
return errors.New("Invalid meta data")
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(r, make([]byte, 4)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if boxDataSize > 4 {
|
||||
if err := heifReadBoxes(d, io.LimitReader(r, boxDataSize-4)); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func heifReadHldr(r io.Reader, boxDataSize int64) error {
|
||||
if boxDataSize < 12 {
|
||||
return errors.New("Invalid hdlr data")
|
||||
}
|
||||
|
||||
data, err := heifReadBoxData(r, boxDataSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(data[8:12], heifPict) {
|
||||
return fmt.Errorf("Invalid handler. Expected: pict, actual: %s", data[8:12])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func heifReadIspe(r io.Reader, boxDataSize int64) (w, h int64, err error) {
|
||||
if boxDataSize < 12 {
|
||||
return 0, 0, errors.New("Invalid ispe data")
|
||||
}
|
||||
|
||||
data, err := heifReadBoxData(r, boxDataSize)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
w = int64(binary.BigEndian.Uint32(data[4:8]))
|
||||
h = int64(binary.BigEndian.Uint32(data[8:12]))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func heifReadBoxes(d *heifData, r io.Reader) error {
|
||||
for {
|
||||
boxType, boxDataSize, err := heifReadBoxHeader(r)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if boxDataSize < 0 {
|
||||
return errors.New("Invalid box data")
|
||||
}
|
||||
|
||||
// log.Printf("Box type: %s; Box data size: %d", boxType, boxDataSize)
|
||||
|
||||
switch boxType {
|
||||
case "ftyp":
|
||||
if err := heifReadFtyp(d, r, boxDataSize); err != nil {
|
||||
return err
|
||||
}
|
||||
case "meta":
|
||||
if err := heifReadMeta(d, r, boxDataSize); err != nil {
|
||||
return err
|
||||
}
|
||||
if !d.IsFilled() {
|
||||
return errors.New("Dimensions data wasn't found in meta box")
|
||||
}
|
||||
return nil
|
||||
case "hdlr":
|
||||
if err := heifReadHldr(r, boxDataSize); err != nil {
|
||||
return nil
|
||||
}
|
||||
case "iprp", "ipco":
|
||||
if err := heifReadBoxes(d, io.LimitReader(r, boxDataSize)); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
case "ispe":
|
||||
w, h, err := heifReadIspe(r, boxDataSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if w > d.Width || h > d.Height {
|
||||
d.Width, d.Height = w, h
|
||||
}
|
||||
case "mdat":
|
||||
return errors.New("mdat box occurred before meta box")
|
||||
default:
|
||||
if _, err := heifReadBoxData(r, boxDataSize); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func DecodeHeifMeta(r io.Reader) (Meta, error) {
|
||||
d := new(heifData)
|
||||
|
||||
if err := heifReadBoxes(d, r); err != nil && !d.IsFilled() {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &meta{
|
||||
format: d.Format,
|
||||
width: int(d.Width),
|
||||
height: int(d.Height),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterFormat("????ftypheic", DecodeHeifMeta)
|
||||
RegisterFormat("????ftypheix", DecodeHeifMeta)
|
||||
RegisterFormat("????ftyphevc", DecodeHeifMeta)
|
||||
RegisterFormat("????ftypheim", DecodeHeifMeta)
|
||||
RegisterFormat("????ftypheis", DecodeHeifMeta)
|
||||
RegisterFormat("????ftyphevm", DecodeHeifMeta)
|
||||
RegisterFormat("????ftyphevs", DecodeHeifMeta)
|
||||
RegisterFormat("????ftypmif1", DecodeHeifMeta)
|
||||
RegisterFormat("????ftypavif", DecodeHeifMeta)
|
||||
}
|
@ -138,7 +138,7 @@ func canScaleOnLoad(imgtype imageType, scale float64) bool {
|
||||
|
||||
func canFitToBytes(imgtype imageType) bool {
|
||||
switch imgtype {
|
||||
case imageTypeJPEG, imageTypeWEBP, imageTypeHEIC, imageTypeTIFF:
|
||||
case imageTypeJPEG, imageTypeWEBP, imageTypeAVIF, imageTypeTIFF:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
14
vips.c
14
vips.c
@ -85,6 +85,8 @@ vips_type_find_load_go(int imgtype) {
|
||||
return vips_type_find("VipsOperation", "svgload_buffer");
|
||||
case (HEIC):
|
||||
return vips_type_find("VipsOperation", "heifload_buffer");
|
||||
case (AVIF):
|
||||
return vips_type_find("VipsOperation", "heifload_buffer");
|
||||
case (BMP):
|
||||
return vips_type_find("VipsOperation", "magickload_buffer");
|
||||
case (TIFF):
|
||||
@ -105,6 +107,8 @@ vips_type_find_save_go(int imgtype) {
|
||||
return vips_type_find("VipsOperation", "webpsave_buffer");
|
||||
case (GIF):
|
||||
return vips_type_find("VipsOperation", "magicksave_buffer");
|
||||
case (AVIF):
|
||||
return vips_type_find("VipsOperation", "heifsave_buffer");
|
||||
case (ICO):
|
||||
return vips_type_find("VipsOperation", "pngsave_buffer");
|
||||
case (BMP):
|
||||
@ -600,6 +604,16 @@ vips_tiffsave_go(VipsImage *in, void **buf, size_t *len, int quality) {
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
vips_avifsave_go(VipsImage *in, void **buf, size_t *len, int quality) {
|
||||
#if VIPS_SUPPORT_HEIF
|
||||
return vips_heifsave_buffer(in, buf, len, "Q", quality, "compression", VIPS_FOREIGN_HEIF_COMPRESSION_AV1, NULL);
|
||||
#else
|
||||
vips_error("vips_avifsave_go", "Saving AVIF is not supported (libvips 8.6+ reuired)");
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
vips_bmpsave_go(VipsImage *in, void **buf, size_t *len) {
|
||||
#if VIPS_SUPPORT_MAGICK
|
||||
|
4
vips.go
4
vips.go
@ -164,7 +164,7 @@ func (img *vipsImage) Load(data []byte, imgtype imageType, shrink int, scale flo
|
||||
err = C.vips_gifload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), C.int(pages), &tmp)
|
||||
case imageTypeSVG:
|
||||
err = C.vips_svgload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), C.double(scale), &tmp)
|
||||
case imageTypeHEIC:
|
||||
case imageTypeHEIC, imageTypeAVIF:
|
||||
err = C.vips_heifload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), &tmp)
|
||||
case imageTypeBMP:
|
||||
err = C.vips_bmpload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), &tmp)
|
||||
@ -205,6 +205,8 @@ func (img *vipsImage) Save(imgtype imageType, quality int, stripMeta bool) ([]by
|
||||
err = C.vips_webpsave_go(img.VipsImage, &ptr, &imgsize, C.int(quality), gbool(stripMeta))
|
||||
case imageTypeGIF:
|
||||
err = C.vips_gifsave_go(img.VipsImage, &ptr, &imgsize)
|
||||
case imageTypeAVIF:
|
||||
err = C.vips_avifsave_go(img.VipsImage, &ptr, &imgsize, C.int(quality))
|
||||
case imageTypeBMP:
|
||||
err = C.vips_bmpsave_go(img.VipsImage, &ptr, &imgsize)
|
||||
case imageTypeTIFF:
|
||||
|
3
vips.h
3
vips.h
@ -13,6 +13,7 @@ enum ImgproxyImageTypes {
|
||||
ICO,
|
||||
SVG,
|
||||
HEIC,
|
||||
AVIF,
|
||||
BMP,
|
||||
TIFF
|
||||
};
|
||||
@ -90,7 +91,7 @@ int vips_jpegsave_go(VipsImage *in, void **buf, size_t *len, int quality, int in
|
||||
int vips_pngsave_go(VipsImage *in, void **buf, size_t *len, int interlace, int quantize, int colors);
|
||||
int vips_webpsave_go(VipsImage *in, void **buf, size_t *len, int quality, gboolean strip);
|
||||
int vips_gifsave_go(VipsImage *in, void **buf, size_t *len);
|
||||
int vips_icosave_go(VipsImage *in, void **buf, size_t *len);
|
||||
int vips_avifsave_go(VipsImage *in, void **buf, size_t *len, int quality);
|
||||
int vips_bmpsave_go(VipsImage *in, void **buf, size_t *len);
|
||||
int vips_tiffsave_go(VipsImage *in, void **buf, size_t *len, int quality);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user