diff --git a/README.md b/README.md index c59ae68..f81ab0a 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,13 @@ For the `SetFrameBuffer()` method, the `buffer` parameter must have a length of ## Images -Vidio provides some convenience functions for reading and writing to images using an array of bytes. Currently, only `png` and `jpeg` formats are supported. +Vidio provides some convenience functions for reading and writing to images using an array of bytes. Currently, only `png` and `jpeg` formats are supported. When reading images, an optional `buffer` can be passed in to avoid array reallocation. + +```go +Read(filename string, buffer ...[]byte) (int, int, []byte, error) +Write(filename string, width, height int, buffer []byte) error +``` +``` ```go w, h, img, err := vidio.Read("input.png") diff --git a/imageio.go b/imageio.go index cbe7d5c..167ce71 100644 --- a/imageio.go +++ b/imageio.go @@ -1,6 +1,8 @@ package vidio import ( + "errors" + "fmt" "image" "os" "strings" @@ -13,7 +15,7 @@ import ( ) // Reads an image from a file. Currently only supports png and jpeg. -func Read(filename string) (int, int, []byte, error) { +func Read(filename string, buffer ...[]byte) (int, int, []byte, error) { f, err := os.Open(filename) if err != nil { return 0, 0, nil, err @@ -28,7 +30,16 @@ func Read(filename string) (int, int, []byte, error) { bounds := image.Bounds().Max size := bounds.X * bounds.Y * 3 - data := make([]byte, size) + var data []byte + if len(buffer) > 0 { + if len(buffer[0]) < size { + errmsg := fmt.Sprintf("Buffer size (%d) is smaller than image size (%d)", len(buffer[0]), size) + return 0, 0, nil, errors.New(errmsg) + } + data = buffer[0] + } else { + data = make([]byte, size, size) + } index := 0 for h := 0; h < bounds.Y; h++ { @@ -47,17 +58,18 @@ func Read(filename string) (int, int, []byte, error) { } // Writes an image to a file. Currently only supports png and jpeg. -func Write(filename string, width, height int, data []byte) error { +func Write(filename string, width, height int, buffer []byte) error { f, err := os.Create(filename) if err != nil { return err } defer f.Close() + image := image.NewRGBA(image.Rect(0, 0, width, height)) index := 0 for h := 0; h < height; h++ { for w := 0; w < width; w++ { - r, g, b := data[index], data[index+1], data[index+2] + r, g, b := buffer[index], buffer[index+1], buffer[index+2] image.Set(w, h, color.RGBA{r, g, b, 255}) index += 3 } diff --git a/test/bananas.jpg b/test/bananas.jpg new file mode 100644 index 0000000..c8228af Binary files /dev/null and b/test/bananas.jpg differ diff --git a/vidio_test.go b/vidio_test.go index b01b3b1..e959ecb 100644 --- a/vidio_test.go +++ b/vidio_test.go @@ -6,7 +6,7 @@ import ( "testing" ) -func assertEquals(expected, actual interface{}) { +func assertEquals(actual, expected interface{}) { if expected != actual { panic(fmt.Sprintf("Expected %v, got %v", expected, actual)) } @@ -226,3 +226,42 @@ At least one output file must be specified`, fmt.Println("Webcam Parsing Test Passed") } + +func TestImageRead(t *testing.T) { + w, h, img, err := Read("test/bananas.jpg") + if err != nil { + panic(err) + } + + assertEquals(w, 200) + assertEquals(h, 133) + assertEquals(len(img), 200*133*3) + // [255 221 189 255 221 189 255 222 186 255] + assertEquals(img[0], uint8(255)) + assertEquals(img[1], uint8(221)) + assertEquals(img[2], uint8(189)) + assertEquals(img[3], uint8(255)) + assertEquals(img[4], uint8(221)) + assertEquals(img[5], uint8(189)) + assertEquals(img[6], uint8(255)) + assertEquals(img[7], uint8(222)) + assertEquals(img[8], uint8(186)) + assertEquals(img[9], uint8(255)) + + fmt.Println("Image Reading Test Passed") +} + +func TestImageWrite(t *testing.T) { + w, h, img, err := Read("test/bananas.jpg") + if err != nil { + panic(err) + } + err = Write("test/bananas-out.png", w, h, img) + if err != nil { + panic(err) + } + + os.Remove("test/bananas-out.png") + + fmt.Println("Image Writing Test Passed") +}