mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-02-02 11:34:20 +02:00
HEIC images support
This commit is contained in:
parent
fee210fb1b
commit
86c883f04b
194
heic.go
Normal file
194
heic.go
Normal file
@ -0,0 +1,194 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"io"
|
||||
)
|
||||
|
||||
const heifBoxHeaderSize = int64(8)
|
||||
|
||||
var heicBrand = []byte("heic")
|
||||
var heicPict = []byte("pict")
|
||||
|
||||
type heifDimensionsData struct {
|
||||
Width, Height int64
|
||||
}
|
||||
|
||||
func (d *heifDimensionsData) IsFilled() bool {
|
||||
return d.Width > 0 && d.Height > 0
|
||||
}
|
||||
|
||||
func heifReadBoxHeader(r io.Reader) (boxType string, boxDataSize int64, err error) {
|
||||
b := make([]byte, heifBoxHeaderSize)
|
||||
_, err = r.Read(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 = r.Read(b)
|
||||
return
|
||||
}
|
||||
|
||||
func heifReadFtyp(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 bytes.Compare(data[0:4], heicBrand) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if boxDataSize >= 12 {
|
||||
for i := int64(8); i < boxDataSize; i += 4 {
|
||||
if bytes.Compare(data[i:i+4], heicBrand) == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("Image is not compatible with heic")
|
||||
}
|
||||
|
||||
func heifReadMeta(d *heifDimensionsData, r io.Reader, boxDataSize int64) error {
|
||||
if boxDataSize < 4 {
|
||||
return errors.New("Invalid meta data")
|
||||
}
|
||||
|
||||
if _, err := r.Read(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.Compare(data[8:12], heicPict) != 0 {
|
||||
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 *heifDimensionsData, 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(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 wan'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 heifDecodeConfig(r io.Reader) (image.Config, error) {
|
||||
d := new(heifDimensionsData)
|
||||
|
||||
if err := heifReadBoxes(d, r); err != nil && !d.IsFilled() {
|
||||
return image.Config{}, err
|
||||
}
|
||||
|
||||
return image.Config{
|
||||
ColorModel: color.NRGBAModel,
|
||||
Width: int(d.Width),
|
||||
Height: int(d.Height),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func heifDecode(r io.Reader) (image.Image, error) {
|
||||
return image.NewRGBA(image.Rect(0, 0, 1, 1)), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
image.RegisterFormat("heic", "????ftyp", heifDecode, heifDecodeConfig)
|
||||
}
|
10
process.go
10
process.go
@ -83,6 +83,9 @@ func initVips() {
|
||||
if int(C.vips_type_find_load_go(C.int(imageTypeSVG))) != 0 {
|
||||
vipsTypeSupportLoad[imageTypeSVG] = true
|
||||
}
|
||||
if int(C.vips_type_find_load_go(C.int(imageTypeHEIC))) != 0 {
|
||||
vipsTypeSupportLoad[imageTypeHEIC] = true
|
||||
}
|
||||
|
||||
// we load ICO with github.com/mat/besticon/ico and send decoded data to vips
|
||||
vipsTypeSupportLoad[imageTypeICO] = true
|
||||
@ -102,6 +105,9 @@ func initVips() {
|
||||
if int(C.vips_type_find_save_go(C.int(imageTypeICO))) != 0 {
|
||||
vipsTypeSupportSave[imageTypeICO] = true
|
||||
}
|
||||
if int(C.vips_type_find_save_go(C.int(imageTypeHEIC))) != 0 {
|
||||
vipsTypeSupportSave[imageTypeHEIC] = true
|
||||
}
|
||||
|
||||
if conf.JpegProgressive {
|
||||
cConf.JpegProgressive = C.int(1)
|
||||
@ -688,6 +694,8 @@ func vipsLoadImage(data []byte, imgtype imageType, shrink int, scale float64, pa
|
||||
}
|
||||
|
||||
img = C.vips_image_new_from_memory_copy(unsafe.Pointer(&rawData[0]), C.size_t(width*height*4), C.int(width), C.int(height), 4, C.VIPS_FORMAT_UCHAR)
|
||||
case imageTypeHEIC:
|
||||
err = C.vips_heifload_go(unsafe.Pointer(&data[0]), C.size_t(len(data)), &img)
|
||||
}
|
||||
if err != 0 {
|
||||
return nil, vipsError()
|
||||
@ -718,6 +726,8 @@ func vipsSaveImage(img *C.VipsImage, imgtype imageType, quality int) ([]byte, co
|
||||
err = C.vips_gifsave_go(img, &ptr, &imgsize)
|
||||
case imageTypeICO:
|
||||
err = C.vips_icosave_go(img, &ptr, &imgsize)
|
||||
case imageTypeHEIC:
|
||||
err = C.vips_heifsave_go(img, &ptr, &imgsize, C.int(quality))
|
||||
}
|
||||
if err != 0 {
|
||||
C.g_free_go(&ptr)
|
||||
|
@ -30,6 +30,7 @@ const (
|
||||
imageTypeGIF = imageType(C.GIF)
|
||||
imageTypeICO = imageType(C.ICO)
|
||||
imageTypeSVG = imageType(C.SVG)
|
||||
imageTypeHEIC = imageType(C.HEIC)
|
||||
)
|
||||
|
||||
type processingHeaders struct {
|
||||
@ -47,6 +48,7 @@ var imageTypes = map[string]imageType{
|
||||
"gif": imageTypeGIF,
|
||||
"ico": imageTypeICO,
|
||||
"svg": imageTypeSVG,
|
||||
"heic": imageTypeHEIC,
|
||||
}
|
||||
|
||||
type gravityType int
|
||||
|
@ -26,6 +26,7 @@ var (
|
||||
imageTypeWEBP: "image/webp",
|
||||
imageTypeGIF: "image/gif",
|
||||
imageTypeICO: "image/x-icon",
|
||||
imageTypeHEIC: "image/heif",
|
||||
}
|
||||
|
||||
contentDispositionsFmt = map[imageType]string{
|
||||
@ -34,6 +35,7 @@ var (
|
||||
imageTypeWEBP: "inline; filename=\"%s.webp\"",
|
||||
imageTypeGIF: "inline; filename=\"%s.gif\"",
|
||||
imageTypeICO: "inline; filename=\"%s.ico\"",
|
||||
imageTypeHEIC: "inline; filename=\"%s.heic\"",
|
||||
}
|
||||
|
||||
imgproxyIsRunningMsg = []byte("imgproxy is running")
|
||||
|
27
vips.c
27
vips.c
@ -25,6 +25,9 @@
|
||||
#define VIPS_SUPPORT_WEBP_ANIMATION \
|
||||
(VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 8))
|
||||
|
||||
#define VIPS_SUPPORT_HEIF \
|
||||
(VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 8))
|
||||
|
||||
#define VIPS_SUPPORT_BUILTIN_ICC \
|
||||
(VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 8))
|
||||
|
||||
@ -71,6 +74,8 @@ vips_type_find_load_go(int imgtype) {
|
||||
return vips_type_find("VipsOperation", "gifload_buffer");
|
||||
case (SVG):
|
||||
return vips_type_find("VipsOperation", "svgload_buffer");
|
||||
case (HEIC):
|
||||
return vips_type_find("VipsOperation", "heifload_buffer");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -89,6 +94,8 @@ vips_type_find_save_go(int imgtype) {
|
||||
return vips_type_find("VipsOperation", "magicksave_buffer");
|
||||
case (ICO):
|
||||
return vips_type_find("VipsOperation", "magicksave_buffer");
|
||||
case (HEIC):
|
||||
return vips_type_find("VipsOperation", "heifsave_buffer");
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -144,6 +151,16 @@ vips_svgload_go(void *buf, size_t len, double scale, VipsImage **out) {
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
vips_heifload_go(void *buf, size_t len, VipsImage **out) {
|
||||
#if VIPS_SUPPORT_HEIF
|
||||
return vips_heifload_buffer(buf, len, out, "access", VIPS_ACCESS_SEQUENTIAL, "autorotate", 1, NULL);
|
||||
#else
|
||||
vips_error("vips_heifload_go", "Loading HEIF is not supported");
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
vips_get_exif_orientation(VipsImage *image) {
|
||||
const char *orientation;
|
||||
@ -549,6 +566,16 @@ vips_icosave_go(VipsImage *in, void **buf, size_t *len) {
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
vips_heifsave_go(VipsImage *in, void **buf, size_t *len, int quality) {
|
||||
#if VIPS_SUPPORT_HEIF
|
||||
return vips_heifsave_buffer(in, buf, len, "Q", quality, NULL);
|
||||
#else
|
||||
vips_error("vips_heifsave_go", "Saving HEIF is not supported");
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
vips_cleanup() {
|
||||
vips_error_clear();
|
||||
|
5
vips.h
5
vips.h
@ -10,7 +10,8 @@ enum ImgproxyImageTypes {
|
||||
WEBP,
|
||||
GIF,
|
||||
ICO,
|
||||
SVG
|
||||
SVG,
|
||||
HEIC
|
||||
};
|
||||
|
||||
int vips_initialize();
|
||||
@ -28,6 +29,7 @@ int vips_pngload_go(void *buf, size_t len, VipsImage **out);
|
||||
int vips_webpload_go(void *buf, size_t len, double scale, int pages, VipsImage **out);
|
||||
int vips_gifload_go(void *buf, size_t len, int pages, VipsImage **out);
|
||||
int vips_svgload_go(void *buf, size_t len, double scale, VipsImage **out);
|
||||
int vips_heifload_go(void *buf, size_t len, VipsImage **out);
|
||||
|
||||
int vips_get_exif_orientation(VipsImage *image);
|
||||
void vips_strip_meta(VipsImage *image);
|
||||
@ -80,5 +82,6 @@ int vips_pngsave_go(VipsImage *in, void **buf, size_t *len, int interlace, int q
|
||||
int vips_webpsave_go(VipsImage *in, void **buf, size_t *len, int quality);
|
||||
int vips_gifsave_go(VipsImage *in, void **buf, size_t *len);
|
||||
int vips_icosave_go(VipsImage *in, void **buf, size_t *len);
|
||||
int vips_heifsave_go(VipsImage *in, void **buf, size_t *len, int quality);
|
||||
|
||||
void vips_cleanup();
|
||||
|
Loading…
x
Reference in New Issue
Block a user