123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- package vips
- /*
- #include "vips.h"
- */
- import "C"
- import (
- "bytes"
- "encoding/binary"
- "unsafe"
- "github.com/imgproxy/imgproxy/v3/imagedata"
- "github.com/imgproxy/imgproxy/v3/imagemeta"
- "github.com/imgproxy/imgproxy/v3/imagetype"
- )
- func (img *Image) loadIco(data []byte, shrink int, scale float64, pages int) error {
- icoMeta, err := imagemeta.DecodeIcoMeta(bytes.NewReader(data))
- if err != nil {
- return err
- }
- offset := icoMeta.BestImageOffset()
- size := icoMeta.BestImageSize()
- internalData := data[offset : offset+size]
- var internalType imagetype.Type
- meta, err := imagemeta.DecodeMeta(bytes.NewReader(internalData))
- if err != nil {
- // Looks like it's BMP with an incomplete header
- d, err := icoFixBmpHeader(internalData)
- if err != nil {
- return err
- }
- return img.loadBmp(d, false)
- }
- internalType = meta.Format()
- if internalType == imagetype.ICO || !SupportsLoad(internalType) {
- return newVipsErrorf("Can't load %s from ICO", internalType)
- }
- imgdata := imagedata.ImageData{
- Type: internalType,
- Data: internalData,
- }
- return img.Load(&imgdata, shrink, scale, pages)
- }
- func (img *Image) saveAsIco() (*imagedata.ImageData, error) {
- if img.Width() > 256 || img.Height() > 256 {
- return nil, newVipsError("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, Error()
- }
- 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
- }
- imgdata := imagedata.ImageData{
- Type: imagetype.ICO,
- Data: buf.Bytes(),
- }
- return &imgdata, nil
- }
- func icoFixBmpHeader(b []byte) ([]byte, error) {
- buf := new(bytes.Buffer)
- fileSize := uint32(14 + len(b))
- buf.Grow(int(fileSize))
- buf.WriteString("BM")
- if err := binary.Write(buf, binary.LittleEndian, &fileSize); err != nil {
- return nil, err
- }
- reserved := uint32(0)
- if err := binary.Write(buf, binary.LittleEndian, &reserved); err != nil {
- return nil, err
- }
- colorUsed := binary.LittleEndian.Uint32(b[32:36])
- bitCount := binary.LittleEndian.Uint16(b[14:16])
- var pixOffset uint32
- if colorUsed == 0 && bitCount <= 8 {
- pixOffset = 14 + 40 + 4*(1<<bitCount)
- } else {
- pixOffset = 14 + 40 + 4*colorUsed
- }
- if err := binary.Write(buf, binary.LittleEndian, &pixOffset); err != nil {
- return nil, err
- }
- // Write size and width
- buf.Write(b[:8])
- // For some reason ICO stores double height
- height := binary.LittleEndian.Uint32(b[8:12]) / 2
- if err := binary.Write(buf, binary.LittleEndian, &height); err != nil {
- return nil, err
- }
- // Write the rest
- buf.Write(b[12:])
- return buf.Bytes(), nil
- }
|