From 676cd7088c79cfccc8104a846becfd62148b6a48 Mon Sep 17 00:00:00 2001 From: Viktor Sokolov Date: Wed, 16 Jul 2025 12:02:20 +0200 Subject: [PATCH] Removed ico.go and bmp.go --- vips/bmp.go | 568 ------------------------------------------------- vips/errors.go | 4 - vips/ico.go | 180 ---------------- 3 files changed, 752 deletions(-) delete mode 100644 vips/bmp.go delete mode 100644 vips/ico.go diff --git a/vips/bmp.go b/vips/bmp.go deleted file mode 100644 index 2c702f9f..00000000 --- a/vips/bmp.go +++ /dev/null @@ -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< 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< 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<>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 -} diff --git a/vips/errors.go b/vips/errors.go index 58c65049..7a64eba4 100644 --- a/vips/errors.go +++ b/vips/errors.go @@ -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 { diff --git a/vips/ico.go b/vips/ico.go deleted file mode 100644 index fa18e7b8..00000000 --- a/vips/ico.go +++ /dev/null @@ -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<