mirror of
https://github.com/gen2brain/cam2ip.git
synced 2026-07-04 05:59:20 +00:00
Harden DIB conversion and add RGB32
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package camera
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
)
|
||||
@@ -37,36 +36,54 @@ func fourcc(b string) uint32 {
|
||||
return uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24)
|
||||
}
|
||||
|
||||
func bmp24ToRgba(data []byte, dst *image.RGBA) error {
|
||||
r := bytes.NewReader(data)
|
||||
|
||||
// bmpToRgba converts a packed DIB byte slice to an image.RGBA. Rows are bottom-up,
|
||||
// 4-byte aligned and in BGR(X) order; bytesPerPixel is 3 (RGB24) or 4 (RGB32).
|
||||
func bmpToRgba(data []byte, dst *image.RGBA, bytesPerPixel int) error {
|
||||
width := dst.Bounds().Dx()
|
||||
height := dst.Bounds().Dy()
|
||||
|
||||
// 3 bytes per pixel, rows 4-byte aligned, stored bottom-up in BGR order.
|
||||
b := make([]byte, (3*width+3)&^3)
|
||||
stride := (bytesPerPixel*width + 3) &^ 3
|
||||
|
||||
for y := height - 1; y >= 0; y-- {
|
||||
_, err := r.Read(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(data) < height*stride {
|
||||
return fmt.Errorf("invalid data length for %d-bit RGB", bytesPerPixel*8)
|
||||
}
|
||||
|
||||
p := dst.Pix[y*dst.Stride : y*dst.Stride+width*4]
|
||||
for i, j := 0, 0; i < len(p); i, j = i+4, j+3 {
|
||||
p[i+0] = b[j+2]
|
||||
p[i+1] = b[j+1]
|
||||
p[i+2] = b[j+0]
|
||||
p[i+3] = 0xFF
|
||||
for y := 0; y < height; y++ {
|
||||
src := data[y*stride:]
|
||||
row := dst.Pix[(height-1-y)*dst.Stride : (height-1-y)*dst.Stride+width*4]
|
||||
|
||||
for i, j := 0, 0; i < len(row); i, j = i+4, j+bytesPerPixel {
|
||||
row[i+0] = src[j+2]
|
||||
row[i+1] = src[j+1]
|
||||
row[i+2] = src[j+0]
|
||||
row[i+3] = 0xFF
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// yuy2ToYCbCr422 converts a YUY2 (YUYV) byte slice to an image.YCbCr with YCbCrSubsampleRatio422 (I422).
|
||||
func yuy2ToYCbCr422(data []byte, dst *image.YCbCr) error {
|
||||
return packedYUV422ToYCbCr(data, dst, 0, 2, 1, 3)
|
||||
// packed422Offsets returns the macropixel byte offsets for a packed 4:2:2 FourCC.
|
||||
func packed422Offsets(format uint32) (y0, y1, cb, cr int, ok bool) {
|
||||
switch format {
|
||||
case yuy2FourCC, yuyvFourCC:
|
||||
return 0, 2, 1, 3, true
|
||||
case uyvyFourCC:
|
||||
return 1, 3, 0, 2, true
|
||||
case yvyuFourCC:
|
||||
return 0, 2, 3, 1, true
|
||||
case vyuyFourCC:
|
||||
return 1, 3, 2, 0, true
|
||||
}
|
||||
|
||||
return 0, 0, 0, 0, false
|
||||
}
|
||||
|
||||
// is422Format reports whether the FourCC is a packed 4:2:2 format.
|
||||
func is422Format(format uint32) bool {
|
||||
_, _, _, _, ok := packed422Offsets(format)
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// packedYUV422ToYCbCr converts packed 4:2:2 to image.YCbCr; y0, y1, cb, cr are the byte offsets within each macropixel.
|
||||
|
||||
@@ -24,6 +24,7 @@ type Camera struct {
|
||||
instance syscall.Handle
|
||||
className string
|
||||
format uint32
|
||||
bpp int
|
||||
}
|
||||
|
||||
// New returns new Camera for given camera index.
|
||||
@@ -120,18 +121,21 @@ func (c *Camera) run(ready chan<- error) {
|
||||
c.format = bi.BmiHeader.BiCompression
|
||||
sendMessage(c.camera, wmCapSetCallbackFrame, 0, syscall.NewCallback(c.callback))
|
||||
|
||||
switch c.format {
|
||||
case 0:
|
||||
if bi.BmiHeader.BiBitCount != 24 {
|
||||
ready <- fmt.Errorf("camera: unsupported format %d; bitcount: %d", c.format, bi.BmiHeader.BiBitCount)
|
||||
rect := image.Rect(0, 0, int(c.opts.Width), int(c.opts.Height))
|
||||
|
||||
switch {
|
||||
case c.format == 0:
|
||||
if bi.BmiHeader.BiBitCount != 24 && bi.BmiHeader.BiBitCount != 32 {
|
||||
ready <- fmt.Errorf("camera: unsupported RGB bitcount %d", bi.BmiHeader.BiBitCount)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
c.rgba = image.NewRGBA(image.Rect(0, 0, int(c.opts.Width), int(c.opts.Height)))
|
||||
case yuy2FourCC, yuyvFourCC:
|
||||
c.ycbcr = image.NewYCbCr(image.Rect(0, 0, int(c.opts.Width), int(c.opts.Height)), image.YCbCrSubsampleRatio422)
|
||||
case mjpgFourCC:
|
||||
c.bpp = int(bi.BmiHeader.BiBitCount) / 8
|
||||
c.rgba = image.NewRGBA(rect)
|
||||
case is422Format(c.format):
|
||||
c.ycbcr = image.NewYCbCr(rect, image.YCbCrSubsampleRatio422)
|
||||
case c.format == mjpgFourCC:
|
||||
default:
|
||||
ready <- fmt.Errorf("camera: unsupported format %d", c.format)
|
||||
|
||||
@@ -160,11 +164,17 @@ func (c *Camera) Read() (img image.Image, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if c.hdr == nil {
|
||||
err = fmt.Errorf("camera: no frame available")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
data := unsafe.Slice((*byte)(unsafe.Pointer(c.hdr.LpData)), c.hdr.DwBufferLength)
|
||||
|
||||
switch c.format {
|
||||
case 0:
|
||||
e := bmp24ToRgba(data, c.rgba)
|
||||
switch {
|
||||
case c.format == 0:
|
||||
e := bmpToRgba(data, c.rgba, c.bpp)
|
||||
if e != nil {
|
||||
err = fmt.Errorf("camera: format %d: can not retrieve frame: %w", c.format, e)
|
||||
|
||||
@@ -172,8 +182,10 @@ func (c *Camera) Read() (img image.Image, err error) {
|
||||
}
|
||||
|
||||
img = c.rgba
|
||||
case yuy2FourCC, yuyvFourCC:
|
||||
e := yuy2ToYCbCr422(data, c.ycbcr)
|
||||
case is422Format(c.format):
|
||||
y0, y1, cb, cr, _ := packed422Offsets(c.format)
|
||||
|
||||
e := packedYUV422ToYCbCr(data, c.ycbcr, y0, y1, cb, cr)
|
||||
if e != nil {
|
||||
err = fmt.Errorf("camera: format %d: can not retrieve frame: %w", c.format, e)
|
||||
|
||||
@@ -181,7 +193,7 @@ func (c *Camera) Read() (img image.Image, err error) {
|
||||
}
|
||||
|
||||
img = c.ycbcr
|
||||
case mjpgFourCC:
|
||||
case c.format == mjpgFourCC:
|
||||
i, e := im.NewDecoder(bytes.NewReader(data)).Decode()
|
||||
if e != nil {
|
||||
err = fmt.Errorf("camera: format %d: can not retrieve frame: %w", c.format, e)
|
||||
|
||||
Reference in New Issue
Block a user