From 4ba34829e7bbf19fe396abcb392df5bbbe81cb46 Mon Sep 17 00:00:00 2001 From: Isamu Mogi Date: Thu, 12 May 2016 10:52:30 +0900 Subject: [PATCH] Add RGB encoder --- webp/encode.go | 2 ++ webp/rgb_image.go | 80 +++++++++++++++++++++++++++++++++++++++++++++++ webp/webp_test.go | 80 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 webp/rgb_image.go diff --git a/webp/encode.go b/webp/encode.go index 210398f..561b56f 100644 --- a/webp/encode.go +++ b/webp/encode.go @@ -118,6 +118,8 @@ func EncodeRGBA(w io.Writer, img image.Image, c Config) (err error) { pic.writer = C.WebPWriterFunction(C.writeWebP) switch p := img.(type) { + case *RGBImage: + C.WebPPictureImportRGB(pic, (*C.uint8_t)(&p.Pix[0]), C.int(p.Stride)) case *image.RGBA: C.WebPPictureImportRGBA(pic, (*C.uint8_t)(&p.Pix[0]), C.int(p.Stride)) case *image.NRGBA: diff --git a/webp/rgb_image.go b/webp/rgb_image.go new file mode 100644 index 0000000..5632f70 --- /dev/null +++ b/webp/rgb_image.go @@ -0,0 +1,80 @@ +// Package rgb provides RGB image which implements image.Image interface. +package webp + +import ( + "image" + "image/color" +) + +// Image represent image data which has RGB colors. +// Image is compatible with image.RGBA, but does not have alpha channel to reduce using memory. +type RGBImage struct { + // Pix holds the image's stream, in R, G, B order. + Pix []uint8 + // Stride is the Pix stride (in bytes) between vertically adjacent pixels. + Stride int + // Rect is the image's bounds. + Rect image.Rectangle +} + +// NewRGBImage allocates and returns RGB image +func NewRGBImage(r image.Rectangle) *RGBImage { + w, h := r.Dx(), r.Dy() + return &RGBImage{Pix: make([]uint8, 3*w*h), Stride: 3 * w, Rect: r} +} + +// ColorModel returns RGB color model. +func (p *RGBImage) ColorModel() color.Model { + return ColorModel +} + +// Bounds implements image.Image.At +func (p *RGBImage) Bounds() image.Rectangle { + return p.Rect +} + +// At implements image.Image.At +func (p *RGBImage) At(x, y int) color.Color { + return p.RGBAAt(x, y) +} + +// RGBAAt returns the color of the pixel at (x, y) as RGBA. +func (p *RGBImage) RGBAAt(x, y int) color.RGBA { + if !(image.Point{x, y}.In(p.Rect)) { + return color.RGBA{} + } + i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*3 + return color.RGBA{p.Pix[i+0], p.Pix[i+1], p.Pix[i+2], 0xFF} +} + +// ColorModel is RGB color model instance +var ColorModel = color.ModelFunc(rgbModel) + +func rgbModel(c color.Color) color.Color { + if _, ok := c.(RGB); ok { + return c + } + r, g, b, _ := c.RGBA() + return RGB{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)} +} + +// RGB color +type RGB struct { + R, G, B uint8 +} + +// RGBA implements Color.RGBA +func (c RGB) RGBA() (r, g, b, a uint32) { + r = uint32(c.R) + r |= r << 8 + g = uint32(c.G) + g |= g << 8 + b = uint32(c.B) + b |= b << 8 + a = uint32(0xFFFF) + return +} + +// Make sure Image implements image.Image. +// See https://golang.org/doc/effective_go.html#blank_implements. +var _ image.Image = new(RGBImage) diff --git a/webp/webp_test.go b/webp/webp_test.go index 60370ae..679e825 100644 --- a/webp/webp_test.go +++ b/webp/webp_test.go @@ -9,6 +9,7 @@ import ( "github.com/harukasan/go-libwebp/test/util" "github.com/harukasan/go-libwebp/webp" + "image/color" ) func TestMain(m *testing.M) { @@ -170,6 +171,28 @@ func TestEncodeRGBA(t *testing.T) { } } +func TestEncodeRGB(t *testing.T) { + img := util.ReadPNG("yellow-rose-3.png") + + config := webp.Config{ + Preset: webp.PresetDefault, + Quality: 100, + Method: 6, + } + + f := util.CreateFile("TestEncodeRGB.webp") + w := bufio.NewWriter(f) + defer func() { + w.Flush() + f.Close() + }() + + if err := webp.EncodeRGBA(w, img, config); err != nil { + t.Errorf("Got Error: %v", err) + return + } +} + func TestEncodeYUVA(t *testing.T) { data := util.ReadFile("cosmos.webp") options := &webp.DecoderOptions{} @@ -198,3 +221,60 @@ func TestEncodeYUVA(t *testing.T) { return } } + +func TestImageInterface(t *testing.T) { + rect := image.Rect(0, 0, 100, 100) + img := webp.NewRGBImage(rect) + + if got := img.ColorModel(); got != webp.ColorModel { + t.Errorf("ColorModel() should return rgb.ColorModel, got: %v", got) + } + + if got := img.Bounds(); got != rect { + t.Errorf("Bounds() should return %v, got: %v", rect, got) + } + + black := color.RGBA{0x00, 0x00, 0x00, 0xFF} + if got := img.At(0, 0); got != black { + t.Errorf("At(0, 0) should return %v, got: %v", black, got) + } + + blank := color.RGBA{} + if got := img.At(-1, -1); got != blank { + t.Errorf("At(0, 0) should return %v, got: %v", blank, got) + } +} + +func TestConvertFromRGBA(t *testing.T) { + rgba := color.RGBA{0x11, 0x22, 0x33, 0xFF} + expect := webp.RGB{0x11, 0x22, 0x33} + if got := webp.ColorModel.Convert(rgba); got != expect { + t.Errorf("got: %v, expect: %v", got, expect) + } +} + +func TestConvertFromRGB(t *testing.T) { + c := webp.RGB{0x11, 0x22, 0x33} + if got := webp.ColorModel.Convert(c); got != c { + t.Errorf("got: %v, expect: %v", got, c) + } +} + +func TestColorRGBA(t *testing.T) { + c := webp.RGB{0x11, 0x22, 0x33} + r, g, b, a := uint32(0x1111), uint32(0x2222), uint32(0x3333), uint32(0xFFFF) + + gotR, gotG, gotB, gotA := c.RGBA() + if gotR != r { + t.Errorf("got R: %v, expect R: %v", gotR, r) + } + if gotG != g { + t.Errorf("got G: %v, expect G: %v", gotG, g) + } + if gotB != b { + t.Errorf("got B: %v, expect B: %v", gotB, b) + } + if gotA != a { + t.Errorf("got A: %v, expect A: %v", gotA, a) + } +}