diff options
Diffstat (limited to 'src/image/color/color.go')
-rw-r--r-- | src/image/color/color.go | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/src/image/color/color.go b/src/image/color/color.go new file mode 100644 index 000000000..ff596a76a --- /dev/null +++ b/src/image/color/color.go @@ -0,0 +1,297 @@ +// 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 color implements a basic color library. +package color + +// Color can convert itself to alpha-premultiplied 16-bits per channel RGBA. +// The conversion may be lossy. +type Color interface { + // RGBA returns the alpha-premultiplied red, green, blue and alpha values + // for the color. Each value ranges within [0, 0xFFFF], but is represented + // by a uint32 so that multiplying by a blend factor up to 0xFFFF will not + // overflow. + RGBA() (r, g, b, a uint32) +} + +// RGBA represents a traditional 32-bit alpha-premultiplied color, +// having 8 bits for each of red, green, blue and alpha. +type RGBA struct { + R, G, B, A uint8 +} + +func (c RGBA) 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(c.A) + a |= a << 8 + return +} + +// RGBA64 represents a 64-bit alpha-premultiplied color, +// having 16 bits for each of red, green, blue and alpha. +type RGBA64 struct { + R, G, B, A uint16 +} + +func (c RGBA64) RGBA() (r, g, b, a uint32) { + return uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A) +} + +// NRGBA represents a non-alpha-premultiplied 32-bit color. +type NRGBA struct { + R, G, B, A uint8 +} + +func (c NRGBA) RGBA() (r, g, b, a uint32) { + r = uint32(c.R) + r |= r << 8 + r *= uint32(c.A) + r /= 0xff + g = uint32(c.G) + g |= g << 8 + g *= uint32(c.A) + g /= 0xff + b = uint32(c.B) + b |= b << 8 + b *= uint32(c.A) + b /= 0xff + a = uint32(c.A) + a |= a << 8 + return +} + +// NRGBA64 represents a non-alpha-premultiplied 64-bit color, +// having 16 bits for each of red, green, blue and alpha. +type NRGBA64 struct { + R, G, B, A uint16 +} + +func (c NRGBA64) RGBA() (r, g, b, a uint32) { + r = uint32(c.R) + r *= uint32(c.A) + r /= 0xffff + g = uint32(c.G) + g *= uint32(c.A) + g /= 0xffff + b = uint32(c.B) + b *= uint32(c.A) + b /= 0xffff + a = uint32(c.A) + return +} + +// Alpha represents an 8-bit alpha color. +type Alpha struct { + A uint8 +} + +func (c Alpha) RGBA() (r, g, b, a uint32) { + a = uint32(c.A) + a |= a << 8 + return a, a, a, a +} + +// Alpha16 represents a 16-bit alpha color. +type Alpha16 struct { + A uint16 +} + +func (c Alpha16) RGBA() (r, g, b, a uint32) { + a = uint32(c.A) + return a, a, a, a +} + +// Gray represents an 8-bit grayscale color. +type Gray struct { + Y uint8 +} + +func (c Gray) RGBA() (r, g, b, a uint32) { + y := uint32(c.Y) + y |= y << 8 + return y, y, y, 0xffff +} + +// Gray16 represents a 16-bit grayscale color. +type Gray16 struct { + Y uint16 +} + +func (c Gray16) RGBA() (r, g, b, a uint32) { + y := uint32(c.Y) + return y, y, y, 0xffff +} + +// Model can convert any Color to one from its own color model. The conversion +// may be lossy. +type Model interface { + Convert(c Color) Color +} + +// ModelFunc returns a Model that invokes f to implement the conversion. +func ModelFunc(f func(Color) Color) Model { + // Note: using *modelFunc as the implementation + // means that callers can still use comparisons + // like m == RGBAModel. This is not possible if + // we use the func value directly, because funcs + // are no longer comparable. + return &modelFunc{f} +} + +type modelFunc struct { + f func(Color) Color +} + +func (m *modelFunc) Convert(c Color) Color { + return m.f(c) +} + +// Models for the standard color types. +var ( + RGBAModel Model = ModelFunc(rgbaModel) + RGBA64Model Model = ModelFunc(rgba64Model) + NRGBAModel Model = ModelFunc(nrgbaModel) + NRGBA64Model Model = ModelFunc(nrgba64Model) + AlphaModel Model = ModelFunc(alphaModel) + Alpha16Model Model = ModelFunc(alpha16Model) + GrayModel Model = ModelFunc(grayModel) + Gray16Model Model = ModelFunc(gray16Model) +) + +func rgbaModel(c Color) Color { + if _, ok := c.(RGBA); ok { + return c + } + r, g, b, a := c.RGBA() + return RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} +} + +func rgba64Model(c Color) Color { + if _, ok := c.(RGBA64); ok { + return c + } + r, g, b, a := c.RGBA() + return RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} +} + +func nrgbaModel(c Color) Color { + if _, ok := c.(NRGBA); ok { + return c + } + r, g, b, a := c.RGBA() + if a == 0xffff { + return NRGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), 0xff} + } + if a == 0 { + return NRGBA{0, 0, 0, 0} + } + // Since Color.RGBA returns a alpha-premultiplied color, we should have r <= a && g <= a && b <= a. + r = (r * 0xffff) / a + g = (g * 0xffff) / a + b = (b * 0xffff) / a + return NRGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} +} + +func nrgba64Model(c Color) Color { + if _, ok := c.(NRGBA64); ok { + return c + } + r, g, b, a := c.RGBA() + if a == 0xffff { + return NRGBA64{uint16(r), uint16(g), uint16(b), 0xffff} + } + if a == 0 { + return NRGBA64{0, 0, 0, 0} + } + // Since Color.RGBA returns a alpha-premultiplied color, we should have r <= a && g <= a && b <= a. + r = (r * 0xffff) / a + g = (g * 0xffff) / a + b = (b * 0xffff) / a + return NRGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} +} + +func alphaModel(c Color) Color { + if _, ok := c.(Alpha); ok { + return c + } + _, _, _, a := c.RGBA() + return Alpha{uint8(a >> 8)} +} + +func alpha16Model(c Color) Color { + if _, ok := c.(Alpha16); ok { + return c + } + _, _, _, a := c.RGBA() + return Alpha16{uint16(a)} +} + +func grayModel(c Color) Color { + if _, ok := c.(Gray); ok { + return c + } + r, g, b, _ := c.RGBA() + y := (299*r + 587*g + 114*b + 500) / 1000 + return Gray{uint8(y >> 8)} +} + +func gray16Model(c Color) Color { + if _, ok := c.(Gray16); ok { + return c + } + r, g, b, _ := c.RGBA() + y := (299*r + 587*g + 114*b + 500) / 1000 + return Gray16{uint16(y)} +} + +// Palette is a palette of colors. +type Palette []Color + +// Convert returns the palette color closest to c in Euclidean R,G,B space. +func (p Palette) 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 Palette) Index(c Color) int { + // A batch version of this computation is in image/draw/draw.go. + + cr, cg, cb, _ := c.RGBA() + ret, bestSSD := 0, uint32(1<<32-1) + for i, v := range p { + vr, vg, vb, _ := v.RGBA() + // We shift by 1 bit to avoid potential uint32 overflow in + // sum-squared-difference. + delta := (int32(cr) - int32(vr)) >> 1 + ssd := uint32(delta * delta) + delta = (int32(cg) - int32(vg)) >> 1 + ssd += uint32(delta * delta) + delta = (int32(cb) - int32(vb)) >> 1 + ssd += uint32(delta * delta) + if ssd < bestSSD { + if ssd == 0 { + return i + } + ret, bestSSD = i, ssd + } + } + return ret +} + +// Standard colors. +var ( + Black = Gray16{0} + White = Gray16{0xffff} + Transparent = Alpha16{0} + Opaque = Alpha16{0xffff} +) |