mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-10-03 18:05:50 +02:00
Removed ico.go and bmp.go
This commit is contained in:
committed by
Sergei Aleksandrovich
parent
01439f43b0
commit
676cd7088c
568
vips/bmp.go
568
vips/bmp.go
@@ -1,568 +0,0 @@
|
||||
// Based on https://cs.opensource.google/go/x/image/+/6944b10b:bmp/reader.go
|
||||
// and https://cs.opensource.google/go/x/image/+/6944b10b:bmp/writer.go
|
||||
package vips
|
||||
|
||||
/*
|
||||
#include "vips.h"
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"unsafe"
|
||||
|
||||
"github.com/imgproxy/imgproxy/v3/imagedata"
|
||||
"github.com/imgproxy/imgproxy/v3/imagetype"
|
||||
"github.com/imgproxy/imgproxy/v3/imath"
|
||||
)
|
||||
|
||||
type bmpHeader struct {
|
||||
sigBM [2]byte
|
||||
fileSize uint32
|
||||
resverved [2]uint16
|
||||
pixOffset uint32
|
||||
dibHeaderSize uint32
|
||||
width uint32
|
||||
height uint32
|
||||
colorPlane uint16
|
||||
bpp uint16
|
||||
compression uint32
|
||||
imageSize uint32
|
||||
xPixelsPerMeter uint32
|
||||
yPixelsPerMeter uint32
|
||||
colorUse uint32
|
||||
colorImportant uint32
|
||||
}
|
||||
|
||||
func readUint16(b []byte) uint16 {
|
||||
return uint16(b[0]) | uint16(b[1])<<8
|
||||
}
|
||||
|
||||
func readUint32(b []byte) uint32 {
|
||||
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
}
|
||||
|
||||
func prepareBmpCanvas(width, height, bands int) (*C.VipsImage, []byte, error) {
|
||||
var tmp *C.VipsImage
|
||||
|
||||
if C.vips_black_go(&tmp, C.int(width), C.int(height), C.int(bands)) != 0 {
|
||||
return nil, nil, Error()
|
||||
}
|
||||
|
||||
data := unsafe.Pointer(C.vips_image_get_data(tmp))
|
||||
datalen := int(tmp.Bands) * int(tmp.Xsize) * int(tmp.Ysize)
|
||||
|
||||
return tmp, ptrToBytes(data, datalen), nil
|
||||
}
|
||||
|
||||
func bmpClearOnPanic(img **C.VipsImage) {
|
||||
if rerr := recover(); rerr != nil {
|
||||
C.clear_image(img)
|
||||
panic(rerr)
|
||||
}
|
||||
}
|
||||
|
||||
func bmpSetBitDepth(img *Image, colors int) {
|
||||
var bitdepth int
|
||||
|
||||
switch {
|
||||
case colors > 16:
|
||||
bitdepth = 8
|
||||
case colors > 4:
|
||||
bitdepth = 4
|
||||
case colors > 2:
|
||||
bitdepth = 2
|
||||
}
|
||||
|
||||
img.SetInt("palette-bit-depth", bitdepth)
|
||||
}
|
||||
|
||||
// decodeBmpPaletted reads an 8/4/2/1 bit-per-pixel BMP image from r.
|
||||
// If topDown is false, the image rows will be read bottom-up.
|
||||
func (img *Image) decodeBmpPaletted(r io.Reader, width, height, bpp int, palette []Color, topDown bool) error {
|
||||
tmp, imgData, err := prepareBmpCanvas(width, height, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer bmpClearOnPanic(&tmp)
|
||||
|
||||
// Each row is 4-byte aligned.
|
||||
cap := 8 / bpp
|
||||
b := make([]byte, ((width+cap-1)/cap+3)&^3)
|
||||
|
||||
y0, y1, yDelta := height-1, -1, -1
|
||||
if topDown {
|
||||
y0, y1, yDelta = 0, height, +1
|
||||
}
|
||||
|
||||
stride := width * 3
|
||||
|
||||
for y := y0; y != y1; y += yDelta {
|
||||
if _, err = io.ReadFull(r, b); err != nil {
|
||||
C.clear_image(&tmp)
|
||||
return err
|
||||
}
|
||||
|
||||
p := imgData[y*stride : (y+1)*stride]
|
||||
|
||||
j, bit := 0, 8-bpp
|
||||
for i := 0; i < len(p); i += 3 {
|
||||
pind := (b[j] >> bit) & (1<<bpp - 1)
|
||||
|
||||
if bit == 0 {
|
||||
bit = 8 - bpp
|
||||
j++
|
||||
} else {
|
||||
bit -= bpp
|
||||
}
|
||||
|
||||
c := palette[pind]
|
||||
|
||||
p[i+0] = c.R
|
||||
p[i+1] = c.G
|
||||
p[i+2] = c.B
|
||||
}
|
||||
}
|
||||
|
||||
C.swap_and_clear(&img.VipsImage, tmp)
|
||||
|
||||
bmpSetBitDepth(img, len(palette))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeBmpRLE reads an 8/4 bit-per-pixel RLE-encoded BMP image from r.
|
||||
func (img *Image) decodeBmpRLE(r io.Reader, width, height, bpp int, palette []Color) error {
|
||||
tmp, imgData, err := prepareBmpCanvas(width, height, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer bmpClearOnPanic(&tmp)
|
||||
|
||||
b := make([]byte, 256)
|
||||
|
||||
readPair := func() (byte, byte, error) {
|
||||
_, err := io.ReadFull(r, b[:2])
|
||||
return b[0], b[1], err
|
||||
}
|
||||
|
||||
x, y := 0, height-1
|
||||
cap := 8 / bpp
|
||||
|
||||
Loop:
|
||||
for {
|
||||
b1, b2, err := readPair()
|
||||
if err != nil {
|
||||
C.clear_image(&tmp)
|
||||
return err
|
||||
}
|
||||
|
||||
if b1 == 0 {
|
||||
switch b2 {
|
||||
case 0: // End of line
|
||||
x, y = 0, y-1
|
||||
if y < 0 {
|
||||
// We should probably return an error here,
|
||||
// but it's safier to just stop decoding
|
||||
break Loop
|
||||
}
|
||||
case 1: // End of file
|
||||
break Loop
|
||||
case 2:
|
||||
dx, dy, err := readPair()
|
||||
if err != nil {
|
||||
C.clear_image(&tmp)
|
||||
return err
|
||||
}
|
||||
|
||||
x = imath.Min(x+int(dx), width)
|
||||
y -= int(dy)
|
||||
if y < 0 {
|
||||
break Loop
|
||||
}
|
||||
default:
|
||||
pixelsCount := int(b2)
|
||||
|
||||
n := ((pixelsCount+cap-1)/cap + 1) &^ 1
|
||||
if _, err := io.ReadFull(r, b[:n]); err != nil {
|
||||
C.clear_image(&tmp)
|
||||
return err
|
||||
}
|
||||
|
||||
pixelsCount = imath.Min(pixelsCount, width-x)
|
||||
|
||||
if pixelsCount > 0 {
|
||||
start := (y*width + x) * 3
|
||||
p := imgData[start : start+pixelsCount*3]
|
||||
|
||||
j, bit := 0, 8-bpp
|
||||
for i := 0; i < len(p); i += 3 {
|
||||
pind := (b[j] >> bit) & (1<<bpp - 1)
|
||||
|
||||
if bit == 0 {
|
||||
bit = 8 - bpp
|
||||
j++
|
||||
} else {
|
||||
bit -= bpp
|
||||
}
|
||||
|
||||
c := palette[pind]
|
||||
|
||||
p[i+0] = c.R
|
||||
p[i+1] = c.G
|
||||
p[i+2] = c.B
|
||||
}
|
||||
|
||||
x += pixelsCount
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pixelsCount := imath.Min(int(b1), width-x)
|
||||
|
||||
if pixelsCount > 0 {
|
||||
start := (y*width + x) * 3
|
||||
p := imgData[start : start+pixelsCount*3]
|
||||
|
||||
bit := 8 - bpp
|
||||
for i := 0; i < len(p); i += 3 {
|
||||
pind := (b2 >> bit) & (1<<bpp - 1)
|
||||
|
||||
if bit == 0 {
|
||||
bit = 8 - bpp
|
||||
} else {
|
||||
bit -= bpp
|
||||
}
|
||||
|
||||
c := palette[pind]
|
||||
|
||||
p[i+0] = c.R
|
||||
p[i+1] = c.G
|
||||
p[i+2] = c.B
|
||||
}
|
||||
|
||||
x += pixelsCount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
C.swap_and_clear(&img.VipsImage, tmp)
|
||||
|
||||
bmpSetBitDepth(img, len(palette))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeBmpRGB reads a 24/32 bit-per-pixel BMP image from r.
|
||||
// If topDown is false, the image rows will be read bottom-up.
|
||||
func (img *Image) decodeBmpRGB(r io.Reader, width, height, bands int, topDown, noAlpha bool) error {
|
||||
if bands != 3 && bands != 4 {
|
||||
return newVipsError("unsupported BMP image")
|
||||
}
|
||||
|
||||
imgBands := 3
|
||||
if bands == 4 && !noAlpha {
|
||||
// Create RGBA image only when source has 4 bands and the last one is alpha
|
||||
imgBands = 4
|
||||
}
|
||||
|
||||
tmp, imgData, err := prepareBmpCanvas(width, height, imgBands)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer bmpClearOnPanic(&tmp)
|
||||
|
||||
// Each row is 4-byte aligned.
|
||||
b := make([]byte, (bands*width+3)&^3)
|
||||
|
||||
y0, y1, yDelta := height-1, -1, -1
|
||||
if topDown {
|
||||
y0, y1, yDelta = 0, height, +1
|
||||
}
|
||||
|
||||
stride := width * imgBands
|
||||
|
||||
for y := y0; y != y1; y += yDelta {
|
||||
if _, err = io.ReadFull(r, b); err != nil {
|
||||
C.clear_image(&tmp)
|
||||
return err
|
||||
}
|
||||
|
||||
p := imgData[y*stride : (y+1)*stride]
|
||||
for i, j := 0, 0; i < len(p); i, j = i+imgBands, j+bands {
|
||||
// BMP images are stored in BGR order rather than RGB order.
|
||||
p[i+0] = b[j+2]
|
||||
p[i+1] = b[j+1]
|
||||
p[i+2] = b[j+0]
|
||||
|
||||
if imgBands == 4 {
|
||||
p[i+3] = b[j+3]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
C.swap_and_clear(&img.VipsImage, tmp)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeBmpRGB16 reads a 16 bit-per-pixel BMP image from r.
|
||||
// If topDown is false, the image rows will be read bottom-up.
|
||||
func (img *Image) decodeBmpRGB16(r io.Reader, width, height int, topDown, bmp565 bool) error {
|
||||
tmp, imgData, err := prepareBmpCanvas(width, height, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer bmpClearOnPanic(&tmp)
|
||||
|
||||
// Each row is 4-byte aligned.
|
||||
b := make([]byte, (2*width+3)&^3)
|
||||
|
||||
y0, y1, yDelta := height-1, -1, -1
|
||||
if topDown {
|
||||
y0, y1, yDelta = 0, height, +1
|
||||
}
|
||||
|
||||
stride := width * 3
|
||||
|
||||
for y := y0; y != y1; y += yDelta {
|
||||
if _, err = io.ReadFull(r, b); err != nil {
|
||||
C.clear_image(&tmp)
|
||||
return err
|
||||
}
|
||||
|
||||
p := imgData[y*stride : (y+1)*stride]
|
||||
for i, j := 0, 0; i < len(p); i, j = i+3, j+2 {
|
||||
pixel := readUint16(b[j:])
|
||||
|
||||
if bmp565 {
|
||||
p[i+0] = uint8((pixel&0xF800)>>11) << 3
|
||||
p[i+1] = uint8((pixel&0x7E0)>>5) << 2
|
||||
} else {
|
||||
p[i+0] = uint8((pixel&0x7C00)>>10) << 3
|
||||
p[i+1] = uint8((pixel&0x3E0)>>5) << 3
|
||||
}
|
||||
p[i+2] = uint8(pixel&0x1F) << 3
|
||||
}
|
||||
}
|
||||
|
||||
C.swap_and_clear(&img.VipsImage, tmp)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (img *Image) loadBmp(data []byte, noAlpha bool) error {
|
||||
// We only support those BMP images that are a BITMAPFILEHEADER
|
||||
// immediately followed by a BITMAPINFOHEADER.
|
||||
const (
|
||||
fileHeaderLen = 14
|
||||
infoHeaderLen = 40
|
||||
v4InfoHeaderLen = 108
|
||||
v5InfoHeaderLen = 124
|
||||
)
|
||||
|
||||
r := bytes.NewReader(data)
|
||||
|
||||
var b [1024]byte
|
||||
if _, err := io.ReadFull(r, b[:fileHeaderLen+4]); err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if string(b[:2]) != "BM" {
|
||||
return newVipsError("not a BMP image")
|
||||
}
|
||||
|
||||
offset := readUint32(b[10:14])
|
||||
infoLen := readUint32(b[14:18])
|
||||
if infoLen != infoHeaderLen && infoLen != v4InfoHeaderLen && infoLen != v5InfoHeaderLen {
|
||||
return newVipsError("unsupported BMP image")
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(r, b[fileHeaderLen+4:fileHeaderLen+infoLen]); err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
width := int(int32(readUint32(b[18:22])))
|
||||
height := int(int32(readUint32(b[22:26])))
|
||||
topDown := false
|
||||
|
||||
if height < 0 {
|
||||
height, topDown = -height, true
|
||||
}
|
||||
if width <= 0 || height <= 0 {
|
||||
return newVipsError("unsupported BMP image")
|
||||
}
|
||||
|
||||
// We only support 1 plane and 8, 24 or 32 bits per pixel
|
||||
planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34])
|
||||
|
||||
if planes != 1 {
|
||||
return newVipsError("unsupported BMP image")
|
||||
}
|
||||
|
||||
rle := false
|
||||
bmp565 := false
|
||||
|
||||
switch {
|
||||
case compression == 0:
|
||||
// Go ahead
|
||||
case compression == 1 && bpp == 8 || compression == 2 && bpp == 4:
|
||||
rle = true
|
||||
case compression == 3 && infoLen >= infoHeaderLen:
|
||||
if infoLen == infoHeaderLen {
|
||||
// Color mask is stored after the info header
|
||||
if _, err := io.ReadFull(r, b[54:66]); err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
rmask := readUint32(b[54:58])
|
||||
gmask := readUint32(b[58:62])
|
||||
bmask := readUint32(b[62:66])
|
||||
amask := readUint32(b[66:70])
|
||||
|
||||
switch {
|
||||
case bpp == 16 && rmask == 0xF800 && gmask == 0x7E0 && bmask == 0x1F:
|
||||
bmp565 = true
|
||||
case bpp == 16 && rmask == 0x7C00 && gmask == 0x3E0 && bmask == 0x1F:
|
||||
// Go ahead, it's a regular 16 bit image
|
||||
case bpp == 32 && rmask == 0xff0000 && gmask == 0xff00 && bmask == 0xff && amask == 0xff000000:
|
||||
// Go ahead, it's a regular 32-bit image
|
||||
default:
|
||||
return newVipsError("unsupported BMP image")
|
||||
}
|
||||
default:
|
||||
return newVipsError("unsupported BMP image")
|
||||
}
|
||||
|
||||
var palette []Color
|
||||
if bpp <= 8 {
|
||||
palColors := readUint32(b[46:50])
|
||||
if palColors == 0 {
|
||||
palColors = 1 << bpp
|
||||
}
|
||||
|
||||
_, err := io.ReadFull(r, b[:palColors*4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
palette = make([]Color, palColors)
|
||||
for i := range palette {
|
||||
// BMP images are stored in BGR order rather than RGB order.
|
||||
// Every 4th byte is padding.
|
||||
palette[i] = Color{b[4*i+2], b[4*i+1], b[4*i+0]}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := r.Seek(int64(offset), io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rle {
|
||||
return img.decodeBmpRLE(r, width, height, int(bpp), palette)
|
||||
}
|
||||
|
||||
switch bpp {
|
||||
case 1, 2, 4, 8:
|
||||
return img.decodeBmpPaletted(r, width, height, int(bpp), palette, topDown)
|
||||
case 16:
|
||||
return img.decodeBmpRGB16(r, width, height, topDown, bmp565)
|
||||
case 24:
|
||||
return img.decodeBmpRGB(r, width, height, 3, topDown, true)
|
||||
case 32:
|
||||
if infoLen >= 70 {
|
||||
// Alpha mask is empty, so no alpha here
|
||||
noAlpha = readUint32(b[66:70]) == 0
|
||||
}
|
||||
|
||||
return img.decodeBmpRGB(r, width, height, 4, topDown, noAlpha)
|
||||
}
|
||||
|
||||
return newVipsError("unsupported BMP image")
|
||||
}
|
||||
|
||||
func (img *Image) saveAsBmp() (*imagedata.ImageData, error) {
|
||||
width, height := img.Width(), img.Height()
|
||||
|
||||
h := &bmpHeader{
|
||||
sigBM: [2]byte{'B', 'M'},
|
||||
fileSize: 14 + 40,
|
||||
resverved: [2]uint16{0, 0},
|
||||
pixOffset: 14 + 40,
|
||||
dibHeaderSize: 40,
|
||||
width: uint32(width),
|
||||
height: uint32(height),
|
||||
colorPlane: 1,
|
||||
bpp: 24,
|
||||
compression: 0,
|
||||
xPixelsPerMeter: 2835,
|
||||
yPixelsPerMeter: 2835,
|
||||
colorUse: 0,
|
||||
colorImportant: 0,
|
||||
}
|
||||
|
||||
lineSize := (width*3 + 3) &^ 3
|
||||
|
||||
h.imageSize = uint32(height * lineSize)
|
||||
h.fileSize += h.imageSize
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
buf.Grow(int(h.fileSize))
|
||||
|
||||
if err := binary.Write(buf, binary.LittleEndian, h); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := img.CopyMemory(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := unsafe.Pointer(C.vips_image_get_data(img.VipsImage))
|
||||
datalen := int(img.VipsImage.Bands) * int(img.VipsImage.Xsize) * int(img.VipsImage.Ysize)
|
||||
imgData := ptrToBytes(data, datalen)
|
||||
|
||||
bands := int(img.VipsImage.Bands)
|
||||
stride := width * bands
|
||||
|
||||
line := make([]byte, lineSize)
|
||||
|
||||
for y := height - 1; y >= 0; y-- {
|
||||
min := y * stride
|
||||
max := min + stride
|
||||
|
||||
for i, j := min, 0; i < max; i, j = i+bands, j+3 {
|
||||
line[j+0] = imgData[i+2]
|
||||
line[j+1] = imgData[i+1]
|
||||
line[j+2] = imgData[i+0]
|
||||
|
||||
if bands == 4 && imgData[i+3] < 255 {
|
||||
line[j+0] = byte(int(line[j+0]) * int(imgData[i+3]) / 255)
|
||||
line[j+1] = byte(int(line[j+1]) * int(imgData[i+3]) / 255)
|
||||
line[j+2] = byte(int(line[j+2]) * int(imgData[i+3]) / 255)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := buf.Write(line); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &imagedata.ImageData{
|
||||
Type: imagetype.BMP,
|
||||
Data: buf.Bytes(),
|
||||
}, nil
|
||||
}
|
@@ -15,10 +15,6 @@ func newVipsError(msg string) error {
|
||||
return ierrors.Wrap(VipsError(msg), 1)
|
||||
}
|
||||
|
||||
func newVipsErrorf(format string, args ...interface{}) error {
|
||||
return ierrors.Wrap(VipsError(fmt.Sprintf(format, args...)), 1)
|
||||
}
|
||||
|
||||
func (e VipsError) Error() string { return string(e) }
|
||||
|
||||
func newColorError(format string, args ...interface{}) error {
|
||||
|
180
vips/ico.go
180
vips/ico.go
@@ -1,180 +0,0 @@
|
||||
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")
|
||||
}
|
||||
|
||||
target := C.vips_target_new_to_memory()
|
||||
|
||||
imgsize := C.size_t(0)
|
||||
|
||||
defer func() {
|
||||
C.vips_unref_target(target)
|
||||
}()
|
||||
|
||||
if C.vips_pngsave_go(img.VipsImage, target, 0, 0, 256) != 0 {
|
||||
return nil, Error()
|
||||
}
|
||||
|
||||
var blob_ptr = C.vips_blob_get(target.blob, &imgsize)
|
||||
var ptr unsafe.Pointer = unsafe.Pointer(blob_ptr)
|
||||
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user