1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2024-11-24 08:12:38 +02:00

Save ICO without ImageMagick

This commit is contained in:
DarthSim 2020-07-22 16:39:16 +06:00
parent ce31fb8cdc
commit 494af926df
4 changed files with 89 additions and 16 deletions

View File

@ -1,6 +1,8 @@
# Changelog
## [Unreleased]
### Fix
- Fix ICO saving.
## [2.14.0] - 2020-07-17
### Added

View File

@ -3,6 +3,7 @@ package main
import (
"math"
"strings"
"unsafe"
)
func maxInt(a, b int) int {
@ -49,3 +50,7 @@ func trimAfter(s string, sep byte) string {
}
return s[:i]
}
func ptrToBytes(ptr unsafe.Pointer, size int) []byte {
return (*[math.MaxInt32]byte)(ptr)[:int(size):int(size)]
}

14
vips.c
View File

@ -106,7 +106,7 @@ vips_type_find_save_go(int imgtype) {
case (GIF):
return vips_type_find("VipsOperation", "magicksave_buffer");
case (ICO):
return vips_type_find("VipsOperation", "magicksave_buffer");
return vips_type_find("VipsOperation", "pngsave_buffer");
case (BMP):
return vips_type_find("VipsOperation", "magicksave_buffer");
case (TIFF):
@ -585,16 +585,6 @@ vips_gifsave_go(VipsImage *in, void **buf, size_t *len) {
#endif
}
int
vips_icosave_go(VipsImage *in, void **buf, size_t *len) {
#if VIPS_SUPPORT_MAGICK
return vips_magicksave_buffer(in, buf, len, "format", "ico", NULL);
#else
vips_error("vips_icosave_go", "Saving ICO is not supported (libvips 8.7+ reuired)");
return 1;
#endif
}
int
vips_tiffsave_go(VipsImage *in, void **buf, size_t *len, int quality) {
#if VIPS_SUPPORT_TIFF
@ -608,7 +598,7 @@ vips_tiffsave_go(VipsImage *in, void **buf, size_t *len, int quality) {
int
vips_bmpsave_go(VipsImage *in, void **buf, size_t *len) {
#if VIPS_SUPPORT_MAGICK
return vips_magicksave_buffer(in, buf, len, "format", "bmp", "quality", NULL);
return vips_magicksave_buffer(in, buf, len, "format", "bmp", NULL);
#else
vips_error("vips_bmpsave_go", "Saving BMP is not supported");
return 1;

84
vips.go
View File

@ -8,9 +8,11 @@ package main
*/
import "C"
import (
"bytes"
"context"
"encoding/binary"
"errors"
"fmt"
"math"
"os"
"runtime"
"unsafe"
@ -179,6 +181,11 @@ func (img *vipsImage) Load(data []byte, imgtype imageType, shrink int, scale flo
}
func (img *vipsImage) Save(imgtype imageType, quality int, stripMeta bool) ([]byte, context.CancelFunc, error) {
if imgtype == imageTypeICO {
b, err := img.SaveAsIco()
return b, func() {}, err
}
var ptr unsafe.Pointer
cancel := func() {
@ -198,8 +205,6 @@ 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 imageTypeICO:
err = C.vips_icosave_go(img.VipsImage, &ptr, &imgsize)
case imageTypeBMP:
err = C.vips_bmpsave_go(img.VipsImage, &ptr, &imgsize)
case imageTypeTIFF:
@ -210,11 +215,82 @@ func (img *vipsImage) Save(imgtype imageType, quality int, stripMeta bool) ([]by
return nil, cancel, vipsError()
}
b := (*[math.MaxInt32]byte)(ptr)[:int(imgsize):int(imgsize)]
b := ptrToBytes(ptr, int(imgsize))
return b, cancel, nil
}
func (img *vipsImage) SaveAsIco() ([]byte, error) {
if img.Width() > 256 || img.Height() > 256 {
return nil, errors.New("Image dimensions is too big. Max dimension size for ICO is 256")
}
var ptr unsafe.Pointer
imgsize := C.size_t(0)
defer func() {
C.g_free_go(&ptr)
}()
if C.vips_pngsave_go(img.VipsImage, &ptr, &imgsize, 0, 0, 256) != 0 {
return nil, vipsError()
}
b := ptrToBytes(ptr, int(imgsize))
buf := new(bytes.Buffer)
buf.Grow(22 + int(imgsize))
// ICONDIR header
if _, err := buf.Write([]byte{0, 0, 1, 0, 1, 0}); err != nil {
return nil, err
}
// ICONDIRENTRY
if _, err := buf.Write([]byte{
byte(img.Width() % 256),
byte(img.Height() % 256),
}); err != nil {
return nil, err
}
// Number of colors. Not supported in our case
if err := buf.WriteByte(0); err != nil {
return nil, err
}
// Reserved
if err := buf.WriteByte(0); err != nil {
return nil, err
}
// Color planes. Always 1 in our case
if _, err := buf.Write([]byte{1, 0}); err != nil {
return nil, err
}
// Bits per pixel
if img.HasAlpha() {
if _, err := buf.Write([]byte{32, 0}); err != nil {
return nil, err
}
} else {
if _, err := buf.Write([]byte{24, 0}); err != nil {
return nil, err
}
}
// Image data size
if err := binary.Write(buf, binary.LittleEndian, uint32(imgsize)); err != nil {
return nil, err
}
// Image data offset. Always 22 in our case
if _, err := buf.Write([]byte{22, 0, 0, 0}); err != nil {
return nil, err
}
if _, err := buf.Write(b); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func (img *vipsImage) Clear() {
if img.VipsImage != nil {
C.clear_image(&img.VipsImage)