1
0
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:
DarthSim 2019-06-03 22:07:03 +06:00
parent fee210fb1b
commit 86c883f04b
6 changed files with 239 additions and 1 deletions

194
heic.go Normal file
View 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)
}

View File

@ -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)

View File

@ -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

View File

@ -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
View File

@ -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
View File

@ -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();