summaryrefslogtreecommitdiff
path: root/src/pkg/image
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-05-04 01:04:51 +0200
committerOndřej Surý <ondrej@sury.org>2011-05-04 01:04:51 +0200
commit14cda8f405d55947c0a3fae0852b04af8405eae0 (patch)
treefa304ad78ef1a8166b3dcd964e1a63091d4c9db2 /src/pkg/image
parentc1ba1a0fec4aed430709030f98a3bdb90bfeea16 (diff)
downloadgolang-upstream/57.tar.gz
Imported Upstream version 57upstream/57
Diffstat (limited to 'src/pkg/image')
-rw-r--r--src/pkg/image/image.go58
-rw-r--r--src/pkg/image/jpeg/writer.go32
-rw-r--r--src/pkg/image/jpeg/writer_test.go28
-rw-r--r--src/pkg/image/png/reader.go22
-rw-r--r--src/pkg/image/png/writer.go29
-rw-r--r--src/pkg/image/png/writer_test.go40
6 files changed, 186 insertions, 23 deletions
diff --git a/src/pkg/image/image.go b/src/pkg/image/image.go
index 5f398a304..222d21ade 100644
--- a/src/pkg/image/image.go
+++ b/src/pkg/image/image.go
@@ -51,6 +51,13 @@ func (p *RGBA) Set(x, y int, c Color) {
p.Pix[y*p.Stride+x] = toRGBAColor(c).(RGBAColor)
}
+func (p *RGBA) SetRGBA(x, y int, c RGBAColor) {
+ if !p.Rect.Contains(Point{x, y}) {
+ return
+ }
+ p.Pix[y*p.Stride+x] = c
+}
+
// Opaque scans the entire image and returns whether or not it is fully opaque.
func (p *RGBA) Opaque() bool {
if p.Rect.Empty() {
@@ -103,6 +110,13 @@ func (p *RGBA64) Set(x, y int, c Color) {
p.Pix[y*p.Stride+x] = toRGBA64Color(c).(RGBA64Color)
}
+func (p *RGBA64) SetRGBA64(x, y int, c RGBA64Color) {
+ if !p.Rect.Contains(Point{x, y}) {
+ return
+ }
+ p.Pix[y*p.Stride+x] = c
+}
+
// Opaque scans the entire image and returns whether or not it is fully opaque.
func (p *RGBA64) Opaque() bool {
if p.Rect.Empty() {
@@ -155,6 +169,13 @@ func (p *NRGBA) Set(x, y int, c Color) {
p.Pix[y*p.Stride+x] = toNRGBAColor(c).(NRGBAColor)
}
+func (p *NRGBA) SetNRGBA(x, y int, c NRGBAColor) {
+ if !p.Rect.Contains(Point{x, y}) {
+ return
+ }
+ p.Pix[y*p.Stride+x] = c
+}
+
// Opaque scans the entire image and returns whether or not it is fully opaque.
func (p *NRGBA) Opaque() bool {
if p.Rect.Empty() {
@@ -207,6 +228,13 @@ func (p *NRGBA64) Set(x, y int, c Color) {
p.Pix[y*p.Stride+x] = toNRGBA64Color(c).(NRGBA64Color)
}
+func (p *NRGBA64) SetNRGBA64(x, y int, c NRGBA64Color) {
+ if !p.Rect.Contains(Point{x, y}) {
+ return
+ }
+ p.Pix[y*p.Stride+x] = c
+}
+
// Opaque scans the entire image and returns whether or not it is fully opaque.
func (p *NRGBA64) Opaque() bool {
if p.Rect.Empty() {
@@ -252,13 +280,20 @@ func (p *Alpha) At(x, y int) Color {
return p.Pix[y*p.Stride+x]
}
-func (p *Alpha) Set(x, y int, c Color) {
+func (p *Alpha) Set(x, y int, c AlphaColor) {
if !p.Rect.Contains(Point{x, y}) {
return
}
p.Pix[y*p.Stride+x] = toAlphaColor(c).(AlphaColor)
}
+func (p *Alpha) SetAlpha(x, y int, c AlphaColor) {
+ if !p.Rect.Contains(Point{x, y}) {
+ return
+ }
+ p.Pix[y*p.Stride+x] = c
+}
+
// Opaque scans the entire image and returns whether or not it is fully opaque.
func (p *Alpha) Opaque() bool {
if p.Rect.Empty() {
@@ -311,6 +346,13 @@ func (p *Alpha16) Set(x, y int, c Color) {
p.Pix[y*p.Stride+x] = toAlpha16Color(c).(Alpha16Color)
}
+func (p *Alpha16) SetAlpha16(x, y int, c Alpha16Color) {
+ if !p.Rect.Contains(Point{x, y}) {
+ return
+ }
+ p.Pix[y*p.Stride+x] = c
+}
+
// Opaque scans the entire image and returns whether or not it is fully opaque.
func (p *Alpha16) Opaque() bool {
if p.Rect.Empty() {
@@ -363,6 +405,13 @@ func (p *Gray) Set(x, y int, c Color) {
p.Pix[y*p.Stride+x] = toGrayColor(c).(GrayColor)
}
+func (p *Gray) SetGray(x, y int, c GrayColor) {
+ if !p.Rect.Contains(Point{x, y}) {
+ return
+ }
+ p.Pix[y*p.Stride+x] = c
+}
+
// Opaque scans the entire image and returns whether or not it is fully opaque.
func (p *Gray) Opaque() bool {
return true
@@ -401,6 +450,13 @@ func (p *Gray16) Set(x, y int, c Color) {
p.Pix[y*p.Stride+x] = toGray16Color(c).(Gray16Color)
}
+func (p *Gray16) SetGray16(x, y int, c Gray16Color) {
+ if !p.Rect.Contains(Point{x, y}) {
+ return
+ }
+ p.Pix[y*p.Stride+x] = c
+}
+
// Opaque scans the entire image and returns whether or not it is fully opaque.
func (p *Gray16) Opaque() bool {
return true
diff --git a/src/pkg/image/jpeg/writer.go b/src/pkg/image/jpeg/writer.go
index 505cce04f..52b3dc4e2 100644
--- a/src/pkg/image/jpeg/writer.go
+++ b/src/pkg/image/jpeg/writer.go
@@ -391,6 +391,31 @@ func toYCbCr(m image.Image, p image.Point, yBlock, cbBlock, crBlock *block) {
}
}
+// rgbaToYCbCr is a specialized version of toYCbCr for image.RGBA images.
+func rgbaToYCbCr(m *image.RGBA, p image.Point, yBlock, cbBlock, crBlock *block) {
+ b := m.Bounds()
+ xmax := b.Max.X - 1
+ ymax := b.Max.Y - 1
+ for j := 0; j < 8; j++ {
+ sj := p.Y + j
+ if sj > ymax {
+ sj = ymax
+ }
+ yoff := sj * m.Stride
+ for i := 0; i < 8; i++ {
+ sx := p.X + i
+ if sx > xmax {
+ sx = xmax
+ }
+ col := &m.Pix[yoff+sx]
+ yy, cb, cr := ycbcr.RGBToYCbCr(col.R, col.G, col.B)
+ yBlock[8*j+i] = int(yy)
+ cbBlock[8*j+i] = int(cb)
+ crBlock[8*j+i] = int(cr)
+ }
+ }
+}
+
// scale scales the 16x16 region represented by the 4 src blocks to the 8x8
// dst block.
func scale(dst *block, src *[4]block) {
@@ -431,13 +456,18 @@ func (e *encoder) writeSOS(m image.Image) {
prevDCY, prevDCCb, prevDCCr int
)
bounds := m.Bounds()
+ rgba, _ := m.(*image.RGBA)
for y := bounds.Min.Y; y < bounds.Max.Y; y += 16 {
for x := bounds.Min.X; x < bounds.Max.X; x += 16 {
for i := 0; i < 4; i++ {
xOff := (i & 1) * 8
yOff := (i & 2) * 4
p := image.Point{x + xOff, y + yOff}
- toYCbCr(m, p, &yBlock, &cbBlock[i], &crBlock[i])
+ if rgba != nil {
+ rgbaToYCbCr(rgba, p, &yBlock, &cbBlock[i], &crBlock[i])
+ } else {
+ toYCbCr(m, p, &yBlock, &cbBlock[i], &crBlock[i])
+ }
prevDCY = e.writeBlock(&yBlock, 0, prevDCY)
}
scale(&cBlock, &cbBlock)
diff --git a/src/pkg/image/jpeg/writer_test.go b/src/pkg/image/jpeg/writer_test.go
index 00922dd5c..7aec70f01 100644
--- a/src/pkg/image/jpeg/writer_test.go
+++ b/src/pkg/image/jpeg/writer_test.go
@@ -8,6 +8,8 @@ import (
"bytes"
"image"
"image/png"
+ "io/ioutil"
+ "rand"
"os"
"testing"
)
@@ -85,3 +87,29 @@ func TestWriter(t *testing.T) {
}
}
}
+
+func BenchmarkEncodeRGBOpaque(b *testing.B) {
+ b.StopTimer()
+ img := image.NewRGBA(640, 480)
+ // Set all pixels to 0xFF alpha to force opaque mode.
+ bo := img.Bounds()
+ rnd := rand.New(rand.NewSource(123))
+ for y := bo.Min.Y; y < bo.Max.Y; y++ {
+ for x := bo.Min.X; x < bo.Max.X; x++ {
+ img.Set(x, y, image.RGBAColor{
+ uint8(rnd.Intn(256)),
+ uint8(rnd.Intn(256)),
+ uint8(rnd.Intn(256)),
+ 255})
+ }
+ }
+ if !img.Opaque() {
+ panic("expected image to be opaque")
+ }
+ b.SetBytes(640 * 480 * 4)
+ b.StartTimer()
+ options := &Options{Quality: 90}
+ for i := 0; i < b.N; i++ {
+ Encode(ioutil.Discard, img, options)
+ }
+}
diff --git a/src/pkg/image/png/reader.go b/src/pkg/image/png/reader.go
index b30a951c1..8c76afa72 100644
--- a/src/pkg/image/png/reader.go
+++ b/src/pkg/image/png/reader.go
@@ -378,7 +378,7 @@ func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) {
for x := 0; x < d.width; x += 8 {
b := cdat[x/8]
for x2 := 0; x2 < 8 && x+x2 < d.width; x2++ {
- gray.Set(x+x2, y, image.GrayColor{(b >> 7) * 0xff})
+ gray.SetGray(x+x2, y, image.GrayColor{(b >> 7) * 0xff})
b <<= 1
}
}
@@ -386,7 +386,7 @@ func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) {
for x := 0; x < d.width; x += 4 {
b := cdat[x/4]
for x2 := 0; x2 < 4 && x+x2 < d.width; x2++ {
- gray.Set(x+x2, y, image.GrayColor{(b >> 6) * 0x55})
+ gray.SetGray(x+x2, y, image.GrayColor{(b >> 6) * 0x55})
b <<= 2
}
}
@@ -394,22 +394,22 @@ func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) {
for x := 0; x < d.width; x += 2 {
b := cdat[x/2]
for x2 := 0; x2 < 2 && x+x2 < d.width; x2++ {
- gray.Set(x+x2, y, image.GrayColor{(b >> 4) * 0x11})
+ gray.SetGray(x+x2, y, image.GrayColor{(b >> 4) * 0x11})
b <<= 4
}
}
case cbG8:
for x := 0; x < d.width; x++ {
- gray.Set(x, y, image.GrayColor{cdat[x]})
+ gray.SetGray(x, y, image.GrayColor{cdat[x]})
}
case cbGA8:
for x := 0; x < d.width; x++ {
ycol := cdat[2*x+0]
- nrgba.Set(x, y, image.NRGBAColor{ycol, ycol, ycol, cdat[2*x+1]})
+ nrgba.SetNRGBA(x, y, image.NRGBAColor{ycol, ycol, ycol, cdat[2*x+1]})
}
case cbTC8:
for x := 0; x < d.width; x++ {
- rgba.Set(x, y, image.RGBAColor{cdat[3*x+0], cdat[3*x+1], cdat[3*x+2], 0xff})
+ rgba.SetRGBA(x, y, image.RGBAColor{cdat[3*x+0], cdat[3*x+1], cdat[3*x+2], 0xff})
}
case cbP1:
for x := 0; x < d.width; x += 8 {
@@ -456,25 +456,25 @@ func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) {
}
case cbTCA8:
for x := 0; x < d.width; x++ {
- nrgba.Set(x, y, image.NRGBAColor{cdat[4*x+0], cdat[4*x+1], cdat[4*x+2], cdat[4*x+3]})
+ nrgba.SetNRGBA(x, y, image.NRGBAColor{cdat[4*x+0], cdat[4*x+1], cdat[4*x+2], cdat[4*x+3]})
}
case cbG16:
for x := 0; x < d.width; x++ {
ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1])
- gray16.Set(x, y, image.Gray16Color{ycol})
+ gray16.SetGray16(x, y, image.Gray16Color{ycol})
}
case cbGA16:
for x := 0; x < d.width; x++ {
ycol := uint16(cdat[4*x+0])<<8 | uint16(cdat[4*x+1])
acol := uint16(cdat[4*x+2])<<8 | uint16(cdat[4*x+3])
- nrgba64.Set(x, y, image.NRGBA64Color{ycol, ycol, ycol, acol})
+ nrgba64.SetNRGBA64(x, y, image.NRGBA64Color{ycol, ycol, ycol, acol})
}
case cbTC16:
for x := 0; x < d.width; x++ {
rcol := uint16(cdat[6*x+0])<<8 | uint16(cdat[6*x+1])
gcol := uint16(cdat[6*x+2])<<8 | uint16(cdat[6*x+3])
bcol := uint16(cdat[6*x+4])<<8 | uint16(cdat[6*x+5])
- rgba64.Set(x, y, image.RGBA64Color{rcol, gcol, bcol, 0xffff})
+ rgba64.SetRGBA64(x, y, image.RGBA64Color{rcol, gcol, bcol, 0xffff})
}
case cbTCA16:
for x := 0; x < d.width; x++ {
@@ -482,7 +482,7 @@ func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) {
gcol := uint16(cdat[8*x+2])<<8 | uint16(cdat[8*x+3])
bcol := uint16(cdat[8*x+4])<<8 | uint16(cdat[8*x+5])
acol := uint16(cdat[8*x+6])<<8 | uint16(cdat[8*x+7])
- nrgba64.Set(x, y, image.NRGBA64Color{rcol, gcol, bcol, acol})
+ nrgba64.SetNRGBA64(x, y, image.NRGBA64Color{rcol, gcol, bcol, acol})
}
}
diff --git a/src/pkg/image/png/writer.go b/src/pkg/image/png/writer.go
index 081d06bf5..2d593f6a7 100644
--- a/src/pkg/image/png/writer.go
+++ b/src/pkg/image/png/writer.go
@@ -263,7 +263,12 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error {
defer zw.Close()
bpp := 0 // Bytes per pixel.
+
+ // Used by fast paths for common image types
var paletted *image.Paletted
+ var rgba *image.RGBA
+ rgba, _ = m.(*image.RGBA)
+
switch cb {
case cbG8:
bpp = 1
@@ -303,12 +308,24 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error {
cr[0][x+1] = c.Y
}
case cbTC8:
- for x := b.Min.X; x < b.Max.X; x++ {
- // We have previously verified that the alpha value is fully opaque.
- r, g, b, _ := m.At(x, y).RGBA()
- cr[0][3*x+1] = uint8(r >> 8)
- cr[0][3*x+2] = uint8(g >> 8)
- cr[0][3*x+3] = uint8(b >> 8)
+ // We have previously verified that the alpha value is fully opaque.
+ cr0 := cr[0]
+ if rgba != nil {
+ yoff := y * rgba.Stride
+ xoff := 3*b.Min.X + 1
+ for _, color := range rgba.Pix[yoff+b.Min.X : yoff+b.Max.X] {
+ cr0[xoff] = color.R
+ cr0[xoff+1] = color.G
+ cr0[xoff+2] = color.B
+ xoff += 3
+ }
+ } else {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ r, g, b, _ := m.At(x, y).RGBA()
+ cr0[3*x+1] = uint8(r >> 8)
+ cr0[3*x+2] = uint8(g >> 8)
+ cr0[3*x+3] = uint8(b >> 8)
+ }
}
case cbP8:
rowOffset := y * paletted.Stride
diff --git a/src/pkg/image/png/writer_test.go b/src/pkg/image/png/writer_test.go
index 4d9929f31..6b054aaa8 100644
--- a/src/pkg/image/png/writer_test.go
+++ b/src/pkg/image/png/writer_test.go
@@ -5,10 +5,10 @@
package png
import (
- "bytes"
"fmt"
"image"
"io"
+ "io/ioutil"
"os"
"testing"
)
@@ -81,10 +81,42 @@ func BenchmarkEncodePaletted(b *testing.B) {
image.RGBAColor{0, 0, 0, 255},
image.RGBAColor{255, 255, 255, 255},
})
+ b.SetBytes(640 * 480 * 1)
b.StartTimer()
- buffer := new(bytes.Buffer)
for i := 0; i < b.N; i++ {
- buffer.Reset()
- Encode(buffer, img)
+ Encode(ioutil.Discard, img)
+ }
+}
+
+func BenchmarkEncodeRGBOpaque(b *testing.B) {
+ b.StopTimer()
+ img := image.NewRGBA(640, 480)
+ // Set all pixels to 0xFF alpha to force opaque mode.
+ bo := img.Bounds()
+ for y := bo.Min.Y; y < bo.Max.Y; y++ {
+ for x := bo.Min.X; x < bo.Max.X; x++ {
+ img.Set(x, y, image.RGBAColor{0, 0, 0, 255})
+ }
+ }
+ if !img.Opaque() {
+ panic("expected image to be opaque")
+ }
+ b.SetBytes(640 * 480 * 4)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ Encode(ioutil.Discard, img)
+ }
+}
+
+func BenchmarkEncodeRGBA(b *testing.B) {
+ b.StopTimer()
+ img := image.NewRGBA(640, 480)
+ if img.Opaque() {
+ panic("expected image to not be opaque")
+ }
+ b.SetBytes(640 * 480 * 4)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ Encode(ioutil.Discard, img)
}
}