فهرست منبع

Add support of 16-bit BMP

DarthSim 2 سال پیش
والد
کامیت
d80a203390
2فایلهای تغییر یافته به همراه83 افزوده شده و 10 حذف شده
  1. 2 0
      CHANGELOG.md
  2. 81 10
      vips/bmp.go

+ 2 - 0
CHANGELOG.md

@@ -1,6 +1,8 @@
 # Changelog
 
 ## [Unreleased]
+## Add
+- Add support of 16-bit BMP.
 
 ## [3.6.0] - 2022-06-13
 ### Add

+ 81 - 10
vips/bmp.go

@@ -314,6 +314,52 @@ func (img *Image) decodeBmpRGB(r io.Reader, width, height, bands int, topDown, n
 	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.
@@ -369,18 +415,41 @@ func (img *Image) loadBmp(data []byte, noAlpha bool) error {
 		return errBmpUnsupported
 	}
 
-	// if compression is set to BITFIELDS, but the bitmask is set to the default bitmask
-	// that would be used if compression was set to 0, we can continue as if compression was 0
-	if compression == 3 && infoLen > infoHeaderLen &&
-		readUint32(b[54:58]) == 0xff0000 && readUint32(b[58:62]) == 0xff00 &&
-		readUint32(b[62:66]) == 0xff && readUint32(b[66:70]) == 0xff000000 {
-		compression = 0
-	}
-
 	rle := false
-	if compression == 1 && bpp == 8 || compression == 2 && bpp == 4 {
+	bmp565 := false
+
+	switch {
+	case compression == 0:
+		// Go ahead
+	case compression == 1 && bpp == 8 || compression == 2 && bpp == 4:
 		rle = true
-	} else if compression != 0 {
+	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 errBmpUnsupported
+		}
+	default:
 		return errBmpUnsupported
 	}
 
@@ -415,6 +484,8 @@ func (img *Image) loadBmp(data []byte, noAlpha bool) error {
 	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: