diff --git a/src/pkg/image/image.go b/src/pkg/image/image.go index bf75a51261c..f4c38d28a6d 100644 --- a/src/pkg/image/image.go +++ b/src/pkg/image/image.go @@ -563,14 +563,19 @@ func (p PalettedColorModel) Convert(c Color) Color { if len(p) == 0 { return nil } + return p[p.Index(c)] +} + +// Index returns the index of the palette color closest to c in Euclidean +// R,G,B space. +func (p PalettedColorModel) Index(c Color) int { cr, cg, cb, _ := c.RGBA() // Shift by 1 bit to avoid potential uint32 overflow in sum-squared-difference. cr >>= 1 cg >>= 1 cb >>= 1 - result := Color(nil) - bestSSD := uint32(1<<32 - 1) - for _, v := range p { + ret, bestSSD := 0, uint32(1<<32-1) + for i, v := range p { vr, vg, vb, _ := v.RGBA() vr >>= 1 vg >>= 1 @@ -578,11 +583,10 @@ func (p PalettedColorModel) Convert(c Color) Color { dr, dg, db := diff(cr, vr), diff(cg, vg), diff(cb, vb) ssd := (dr * dr) + (dg * dg) + (db * db) if ssd < bestSSD { - bestSSD = ssd - result = v + ret, bestSSD = i, ssd } } - return result + return ret } // A Paletted is an in-memory image backed by a 2-D slice of uint8 values and a PalettedColorModel. @@ -610,6 +614,13 @@ func (p *Paletted) At(x, y int) Color { return p.Palette[p.Pix[y*p.Stride+x]] } +func (p *Paletted) Set(x, y int, c Color) { + if !(Point{x, y}.In(p.Rect)) { + return + } + p.Pix[y*p.Stride+x] = uint8(p.Palette.Index(c)) +} + func (p *Paletted) ColorIndexAt(x, y int) uint8 { if !(Point{x, y}.In(p.Rect)) { return 0 diff --git a/src/pkg/image/image_test.go b/src/pkg/image/image_test.go new file mode 100644 index 00000000000..17e314795ef --- /dev/null +++ b/src/pkg/image/image_test.go @@ -0,0 +1,71 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package image + +import ( + "testing" +) + +func cmp(t *testing.T, cm ColorModel, c0, c1 Color) bool { + r0, g0, b0, a0 := cm.Convert(c0).RGBA() + r1, g1, b1, a1 := cm.Convert(c1).RGBA() + return r0 == r1 && g0 == g1 && b0 == b1 && a0 == a1 +} + +func TestImage(t *testing.T) { + type buffered interface { + Image + Set(int, int, Color) + SubImage(Rectangle) Image + } + testImage := []Image{ + NewRGBA(10, 10), + NewRGBA64(10, 10), + NewNRGBA(10, 10), + NewNRGBA64(10, 10), + NewAlpha(10, 10), + NewAlpha16(10, 10), + NewGray(10, 10), + NewGray16(10, 10), + NewPaletted(10, 10, PalettedColorModel{ + Transparent, + Opaque, + }), + } + for _, m := range testImage { + b := m.(buffered) + if !Rect(0, 0, 10, 10).Eq(b.Bounds()) { + t.Errorf("%T: want bounds %v, got %v", b, Rect(0, 0, 10, 10), b.Bounds()) + continue + } + if !cmp(t, b.ColorModel(), Transparent, b.At(6, 3)) { + t.Errorf("%T: at (6, 3), want a zero color, got %v", b, b.At(6, 3)) + continue + } + b.Set(6, 3, Opaque) + if !cmp(t, b.ColorModel(), Opaque, b.At(6, 3)) { + t.Errorf("%T: at (6, 3), want a non-zero color, got %v", b, b.At(6, 3)) + continue + } + b = b.SubImage(Rect(3, 2, 9, 8)).(buffered) + if !Rect(3, 2, 9, 8).Eq(b.Bounds()) { + t.Errorf("%T: sub-image want bounds %v, got %v", b, Rect(3, 2, 9, 8), b.Bounds()) + continue + } + if !cmp(t, b.ColorModel(), Opaque, b.At(6, 3)) { + t.Errorf("%T: sub-image at (6, 3), want a non-zero color, got %v", b, b.At(6, 3)) + continue + } + if !cmp(t, b.ColorModel(), Transparent, b.At(3, 3)) { + t.Errorf("%T: sub-image at (3, 3), want a zero color, got %v", b, b.At(3, 3)) + continue + } + b.Set(3, 3, Opaque) + if !cmp(t, b.ColorModel(), Opaque, b.At(3, 3)) { + t.Errorf("%T: sub-image at (3, 3), want a non-zero color, got %v", b, b.At(3, 3)) + continue + } + } +}