Added FrameData ToImage()

This commit is contained in:
Quentin Renard
2024-01-24 18:03:00 +01:00
parent 28e6b9d114
commit 0d3468db8f
2 changed files with 119 additions and 36 deletions

View File

@@ -9,6 +9,38 @@ import (
"strings" "strings"
) )
type frameDataImageFormat int
const (
frameDataImageFormatNone frameDataImageFormat = iota
frameDataImageFormatNRGBA
frameDataImageFormatNYCbCrA
frameDataImageFormatYCbCr
)
func frameDataImageFormatFromPixelFormat(pf PixelFormat) frameDataImageFormat {
// Switch on pixel format
switch pf {
// NRGBA
case PixelFormatRgba:
return frameDataImageFormatNRGBA
// NYCbCrA
case PixelFormatYuva420P,
PixelFormatYuva422P,
PixelFormatYuva444P:
return frameDataImageFormatNYCbCrA
// YCbCr
case PixelFormatYuv410P,
PixelFormatYuv411P, PixelFormatYuvj411P,
PixelFormatYuv420P, PixelFormatYuvj420P,
PixelFormatYuv422P, PixelFormatYuvj422P,
PixelFormatYuv440P, PixelFormatYuvj440P,
PixelFormatYuv444P, PixelFormatYuvj444P:
return frameDataImageFormatYCbCr
}
return frameDataImageFormatNone
}
type FrameData struct { type FrameData struct {
f *Frame f *Frame
} }
@@ -44,9 +76,9 @@ func (d *FrameData) Bytes(align int) ([]byte, error) {
return nil, errors.New("astiav: frame type not implemented") return nil, errors.New("astiav: frame type not implemented")
} }
func (d *FrameData) planeData(i int, sizeFunc func(linesize int) int) []byte { func (d *FrameData) planeBytes(i int) []byte {
return bytesFromC(func(size *cUlong) *C.uint8_t { return bytesFromC(func(size *cUlong) *C.uint8_t {
*size = cUlong(sizeFunc(int(d.f.c.linesize[i]))) *size = cUlong(int(d.f.c.linesize[i]) * d.f.Height())
return d.f.c.data[i] return d.f.c.data[i]
}) })
} }
@@ -68,53 +100,99 @@ func (d *FrameData) imageYCbCrSubsampleRatio() image.YCbCrSubsampleRatio {
return image.YCbCrSubsampleRatio444 return image.YCbCrSubsampleRatio444
} }
func (d *FrameData) imageNRGBA() *image.NRGBA { func (d *FrameData) copyPlaneBytes(i int, s *[]uint8) {
return &image.NRGBA{ b := d.planeBytes(0)
Pix: d.planeData(0, func(linesize int) int { return linesize * d.f.Height() }), if len(b) > cap(*s) {
Stride: d.f.Linesize()[0], *s = make([]uint8, len(b))
Rect: image.Rect(0, 0, d.f.Width(), d.f.Height()), }
copy(*s, b)
}
func (d *FrameData) toImageNRGBA(i *image.NRGBA) {
d.copyPlaneBytes(0, &i.Pix)
if v := d.f.Linesize()[0]; i.Stride != v {
i.Stride = v
}
if w, h := d.f.Width(), d.f.Height(); i.Rect.Dy() != w || i.Rect.Dx() != h {
i.Rect = image.Rect(0, 0, w, h)
} }
} }
func (d *FrameData) imageYCbCr() *image.YCbCr { func (d *FrameData) toImageYCbCr(i *image.YCbCr) {
return &image.YCbCr{ d.copyPlaneBytes(0, &i.Y)
Y: d.planeData(0, func(linesize int) int { return linesize * d.f.Height() }), d.copyPlaneBytes(1, &i.Cb)
Cb: d.planeData(1, func(linesize int) int { return linesize * d.f.Height() }), d.copyPlaneBytes(2, &i.Cr)
Cr: d.planeData(2, func(linesize int) int { return linesize * d.f.Height() }), if v := d.f.Linesize()[0]; i.YStride != v {
YStride: d.f.Linesize()[0], i.YStride = v
CStride: d.f.Linesize()[1], }
SubsampleRatio: d.imageYCbCrSubsampleRatio(), if v := d.f.Linesize()[1]; i.CStride != v {
Rect: image.Rect(0, 0, d.f.Width(), d.f.Height()), i.CStride = v
}
if v := d.imageYCbCrSubsampleRatio(); i.SubsampleRatio != v {
i.SubsampleRatio = v
}
if w, h := d.f.Width(), d.f.Height(); i.Rect.Dy() != w || i.Rect.Dx() != h {
i.Rect = image.Rect(0, 0, w, h)
} }
} }
func (d *FrameData) imageNYCbCrA() *image.NYCbCrA { func (d *FrameData) toImageNYCbCrA(i *image.NYCbCrA) {
return &image.NYCbCrA{ d.toImageYCbCr(&i.YCbCr)
YCbCr: *d.imageYCbCr(), d.copyPlaneBytes(3, &i.A)
A: d.planeData(3, func(linesize int) int { return linesize * d.f.Height() }), if v := d.f.Linesize()[3]; i.AStride != v {
AStride: d.f.Linesize()[3], i.AStride = v
} }
} }
func (d *FrameData) Image() (image.Image, error) { func (d *FrameData) Image() (image.Image, error) {
// Switch on pixel format // Switch on image format
switch d.f.PixelFormat() { switch frameDataImageFormatFromPixelFormat(d.f.PixelFormat()) {
// NRGBA // NRGBA
case PixelFormatRgba: case frameDataImageFormatNRGBA:
return d.imageNRGBA(), nil i := &image.NRGBA{}
d.toImageNRGBA(i)
return i, nil
// NYCbCrA // NYCbCrA
case PixelFormatYuva420P, case frameDataImageFormatNYCbCrA:
PixelFormatYuva422P, i := &image.NYCbCrA{}
PixelFormatYuva444P: d.toImageNYCbCrA(i)
return d.imageNYCbCrA(), nil return i, nil
// YCbCr // YCbCr
case PixelFormatYuv410P, case frameDataImageFormatYCbCr:
PixelFormatYuv411P, PixelFormatYuvj411P, i := &image.YCbCr{}
PixelFormatYuv420P, PixelFormatYuvj420P, d.toImageYCbCr(i)
PixelFormatYuv422P, PixelFormatYuvj422P, return i, nil
PixelFormatYuv440P, PixelFormatYuvj440P,
PixelFormatYuv444P, PixelFormatYuvj444P:
return d.imageYCbCr(), nil
} }
return nil, fmt.Errorf("astiav: %s pixel format not handled by the Go standard image package", d.f.PixelFormat()) return nil, fmt.Errorf("astiav: %s pixel format not handled by the Go standard image package", d.f.PixelFormat())
} }
func (d *FrameData) ToImage(dst image.Image) error {
// Switch on image format
switch frameDataImageFormatFromPixelFormat(d.f.PixelFormat()) {
// NRGBA
case frameDataImageFormatNRGBA:
i, ok := dst.(*image.NRGBA)
if !ok {
return errors.New("astiav: image should be *image.NRGBA")
}
d.toImageNRGBA(i)
return nil
// NYCbCrA
case frameDataImageFormatNYCbCrA:
i, ok := dst.(*image.NYCbCrA)
if !ok {
return errors.New("astiav: image should be *image.NYCbCrA")
}
d.toImageNYCbCrA(i)
return nil
// YCbCr
case frameDataImageFormatYCbCr:
i, ok := dst.(*image.YCbCr)
if !ok {
return errors.New("astiav: image should be *image.YCbCr")
}
d.toImageYCbCr(i)
return nil
}
return fmt.Errorf("astiav: %s pixel format not handled by the Go standard image package", d.f.PixelFormat())
}

View File

@@ -1,6 +1,7 @@
package astiav_test package astiav_test
import ( import (
"image"
"image/png" "image/png"
"os" "os"
"testing" "testing"
@@ -12,10 +13,12 @@ import (
func TestFrameData(t *testing.T) { func TestFrameData(t *testing.T) {
for _, v := range []struct { for _, v := range []struct {
ext string ext string
i image.Image
name string name string
}{ }{
{ {
ext: "png", ext: "png",
i: &image.NRGBA{},
name: "image-rgba", name: "image-rgba",
}, },
// TODO Find a way to test yuv and yuva even though result seems to change randomly // TODO Find a way to test yuv and yuva even though result seems to change randomly
@@ -39,9 +42,11 @@ func TestFrameData(t *testing.T) {
i1, err := fd.Image() i1, err := fd.Image()
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, fd.ToImage(v.i))
i2, err := png.Decode(f1) i2, err := png.Decode(f1)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, i1, i2) require.Equal(t, i1, i2)
require.Equal(t, v.i, i2)
}() }()
} }
} }