bmp: optimize decoding and encoding 0xH sized images.
It should be quick regardless of how large H is. Fixes golang/go#10746 Change-Id: Icde36047e88d9786e64f44724b7ba8b38db2a33f Reviewed-on: https://go-review.googlesource.com/9836 Reviewed-by: Nigel Tao <nigeltao@golang.org>
This commit is contained in:
parent
4a3ed0c124
commit
f28211f6e1
|
@ -29,8 +29,11 @@ func readUint32(b []byte) uint32 {
|
|||
// decodePaletted reads an 8 bit-per-pixel BMP image from r.
|
||||
// If topDown is false, the image rows will be read bottom-up.
|
||||
func decodePaletted(r io.Reader, c image.Config, topDown bool) (image.Image, error) {
|
||||
var tmp [4]byte
|
||||
paletted := image.NewPaletted(image.Rect(0, 0, c.Width, c.Height), c.ColorModel.(color.Palette))
|
||||
if c.Width == 0 || c.Height == 0 {
|
||||
return paletted, nil
|
||||
}
|
||||
var tmp [4]byte
|
||||
y0, y1, yDelta := c.Height-1, -1, -1
|
||||
if topDown {
|
||||
y0, y1, yDelta = 0, c.Height, +1
|
||||
|
@ -55,6 +58,9 @@ func decodePaletted(r io.Reader, c image.Config, topDown bool) (image.Image, err
|
|||
// If topDown is false, the image rows will be read bottom-up.
|
||||
func decodeRGB(r io.Reader, c image.Config, topDown bool) (image.Image, error) {
|
||||
rgba := image.NewRGBA(image.Rect(0, 0, c.Width, c.Height))
|
||||
if c.Width == 0 || c.Height == 0 {
|
||||
return rgba, nil
|
||||
}
|
||||
// There are 3 bytes per pixel, and each row is 4-byte aligned.
|
||||
b := make([]byte, (3*c.Width+3)&^3)
|
||||
y0, y1, yDelta := c.Height-1, -1, -1
|
||||
|
@ -81,6 +87,9 @@ func decodeRGB(r io.Reader, c image.Config, topDown bool) (image.Image, error) {
|
|||
// If topDown is false, the image rows will be read bottom-up.
|
||||
func decodeNRGBA(r io.Reader, c image.Config, topDown bool) (image.Image, error) {
|
||||
rgba := image.NewNRGBA(image.Rect(0, 0, c.Width, c.Height))
|
||||
if c.Width == 0 || c.Height == 0 {
|
||||
return rgba, nil
|
||||
}
|
||||
y0, y1, yDelta := c.Height-1, -1, -1
|
||||
if topDown {
|
||||
y0, y1, yDelta = 0, c.Height, +1
|
||||
|
|
|
@ -6,6 +6,7 @@ package bmp
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"image"
|
||||
"io"
|
||||
)
|
||||
|
@ -89,6 +90,9 @@ func encode(w io.Writer, m image.Image, step int) error {
|
|||
// Encode writes the image m to w in BMP format.
|
||||
func Encode(w io.Writer, m image.Image) error {
|
||||
d := m.Bounds().Size()
|
||||
if d.X < 0 || d.Y < 0 {
|
||||
return errors.New("bmp: negative bounds")
|
||||
}
|
||||
h := &header{
|
||||
sigBM: [2]byte{'B', 'M'},
|
||||
fileSize: 14 + 40,
|
||||
|
@ -146,6 +150,10 @@ func Encode(w io.Writer, m image.Image) error {
|
|||
}
|
||||
}
|
||||
|
||||
if d.X == 0 || d.Y == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch m := m.(type) {
|
||||
case *image.Gray:
|
||||
return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step)
|
||||
|
|
|
@ -6,10 +6,12 @@ package bmp
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func openImage(filename string) (image.Image, error) {
|
||||
|
@ -41,6 +43,39 @@ func TestEncode(t *testing.T) {
|
|||
compare(t, img0, img1)
|
||||
}
|
||||
|
||||
// TestZeroWidthVeryLargeHeight tests that encoding and decoding a degenerate
|
||||
// image with zero width but over one billion pixels in height is faster than
|
||||
// naively calling an io.Reader or io.Writer method once per row.
|
||||
func TestZeroWidthVeryLargeHeight(t *testing.T) {
|
||||
c := make(chan error, 1)
|
||||
go func() {
|
||||
b := image.Rect(0, 0, 0, 0x3fffffff)
|
||||
var buf bytes.Buffer
|
||||
if err := Encode(&buf, image.NewRGBA(b)); err != nil {
|
||||
c <- err
|
||||
return
|
||||
}
|
||||
m, err := Decode(&buf)
|
||||
if err != nil {
|
||||
c <- err
|
||||
return
|
||||
}
|
||||
if got := m.Bounds(); got != b {
|
||||
c <- fmt.Errorf("bounds: got %v, want %v", got, b)
|
||||
return
|
||||
}
|
||||
c <- nil
|
||||
}()
|
||||
select {
|
||||
case err := <-c:
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
case <-time.After(3 * time.Second):
|
||||
t.Fatalf("timed out")
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkEncode benchmarks the encoding of an image.
|
||||
func BenchmarkEncode(b *testing.B) {
|
||||
img, err := openImage("video-001.bmp")
|
||||
|
|
Loading…
Reference in New Issue
Block a user