1
0
mirror of https://github.com/golang/go synced 2024-11-24 01:10:12 -07:00

image/color: have CMYK.RGBA work in 16-bit color, per the Color interface.

Change-Id: I3621527c924a43724032f80a072505c60d929ab3
Reviewed-on: https://go-review.googlesource.com/8180
Reviewed-by: Rob Pike <r@golang.org>
This commit is contained in:
Nigel Tao 2015-03-27 18:05:13 +11:00
parent 32e75bace0
commit 0def13ac3f
2 changed files with 63 additions and 15 deletions

View File

@ -158,11 +158,11 @@ func RGBToCMYK(r, g, b uint8) (uint8, uint8, uint8, uint8) {
// CMYKToRGB converts a CMYK quadruple to an RGB triple. // CMYKToRGB converts a CMYK quadruple to an RGB triple.
func CMYKToRGB(c, m, y, k uint8) (uint8, uint8, uint8) { func CMYKToRGB(c, m, y, k uint8) (uint8, uint8, uint8) {
w := uint32(0xff - k) w := uint32(0xffff - uint32(k)*0x101)
r := uint32(0xff-c) * w / 0xff r := uint32(0xffff-uint32(c)*0x101) * w / 0xffff
g := uint32(0xff-m) * w / 0xff g := uint32(0xffff-uint32(m)*0x101) * w / 0xffff
b := uint32(0xff-y) * w / 0xff b := uint32(0xffff-uint32(y)*0x101) * w / 0xffff
return uint8(r), uint8(g), uint8(b) return uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)
} }
// CMYK represents a fully opaque CMYK color, having 8 bits for each of cyan, // CMYK represents a fully opaque CMYK color, having 8 bits for each of cyan,
@ -174,8 +174,14 @@ type CMYK struct {
} }
func (c CMYK) RGBA() (uint32, uint32, uint32, uint32) { func (c CMYK) RGBA() (uint32, uint32, uint32, uint32) {
r, g, b := CMYKToRGB(c.C, c.M, c.Y, c.K) // This code is a copy of the CMYKToRGB function above, except that it
return uint32(r) * 0x101, uint32(g) * 0x101, uint32(b) * 0x101, 0xffff // returns values in the range [0, 0xffff] instead of [0, 0xff].
w := uint32(0xffff - uint32(c.K)*0x101)
r := uint32(0xffff-uint32(c.C)*0x101) * w / 0xffff
g := uint32(0xffff-uint32(c.M)*0x101) * w / 0xffff
b := uint32(0xffff-uint32(c.Y)*0x101) * w / 0xffff
return uint32(r), uint32(g), uint32(b), 0xffff
} }
// CMYKModel is the Model for CMYK colors. // CMYKModel is the Model for CMYK colors.

View File

@ -18,14 +18,34 @@ func delta(x, y uint8) uint8 {
// TestYCbCrRoundtrip tests that a subset of RGB space can be converted to YCbCr // TestYCbCrRoundtrip tests that a subset of RGB space can be converted to YCbCr
// and back to within 1/256 tolerance. // and back to within 1/256 tolerance.
func TestYCbCrRoundtrip(t *testing.T) { func TestYCbCrRoundtrip(t *testing.T) {
for r := 0; r < 255; r += 7 { for r := 0; r < 256; r += 7 {
for g := 0; g < 255; g += 5 { for g := 0; g < 256; g += 5 {
for b := 0; b < 255; b += 3 { for b := 0; b < 256; b += 3 {
r0, g0, b0 := uint8(r), uint8(g), uint8(b) r0, g0, b0 := uint8(r), uint8(g), uint8(b)
y, cb, cr := RGBToYCbCr(r0, g0, b0) y, cb, cr := RGBToYCbCr(r0, g0, b0)
r1, g1, b1 := YCbCrToRGB(y, cb, cr) r1, g1, b1 := YCbCrToRGB(y, cb, cr)
if delta(r0, r1) > 1 || delta(g0, g1) > 1 || delta(b0, b1) > 1 { if delta(r0, r1) > 1 || delta(g0, g1) > 1 || delta(b0, b1) > 1 {
t.Fatalf("r0, g0, b0 = %d, %d, %d r1, g1, b1 = %d, %d, %d", r0, g0, b0, r1, g1, b1) t.Fatalf("\nr0, g0, b0 = %d, %d, %d\nr1, g1, b1 = %d, %d, %d", r0, g0, b0, r1, g1, b1)
}
}
}
}
}
// TestYCbCrToRGBConsistency tests that calling the RGBA method (16 bit color)
// then truncating to 8 bits is equivalent to calling the YCbCrToRGB function (8
// bit color).
func TestYCbCrToRGBConsistency(t *testing.T) {
for y := 0; y < 256; y += 7 {
for cb := 0; cb < 256; cb += 5 {
for cr := 0; cr < 256; cr += 3 {
x := YCbCr{uint8(y), uint8(cb), uint8(cr)}
r0, g0, b0, _ := x.RGBA()
r1, g1, b1 := uint8(r0>>8), uint8(g0>>8), uint8(b0>>8)
r2, g2, b2 := YCbCrToRGB(x.Y, x.Cb, x.Cr)
if r1 != r2 || g1 != g2 || b1 != b2 {
t.Fatalf("y, cb, cr = %d, %d, %d\nr1, g1, b1 = %d, %d, %d\nr2, g2, b2 = %d, %d, %d",
y, cb, cr, r1, g1, b1, r2, g2, b2)
} }
} }
} }
@ -35,14 +55,36 @@ func TestYCbCrRoundtrip(t *testing.T) {
// TestCMYKRoundtrip tests that a subset of RGB space can be converted to CMYK // TestCMYKRoundtrip tests that a subset of RGB space can be converted to CMYK
// and back to within 1/256 tolerance. // and back to within 1/256 tolerance.
func TestCMYKRoundtrip(t *testing.T) { func TestCMYKRoundtrip(t *testing.T) {
for r := 0; r < 255; r += 7 { for r := 0; r < 256; r += 7 {
for g := 0; g < 255; g += 5 { for g := 0; g < 256; g += 5 {
for b := 0; b < 255; b += 3 { for b := 0; b < 256; b += 3 {
r0, g0, b0 := uint8(r), uint8(g), uint8(b) r0, g0, b0 := uint8(r), uint8(g), uint8(b)
c, m, y, k := RGBToCMYK(r0, g0, b0) c, m, y, k := RGBToCMYK(r0, g0, b0)
r1, g1, b1 := CMYKToRGB(c, m, y, k) r1, g1, b1 := CMYKToRGB(c, m, y, k)
if delta(r0, r1) > 1 || delta(g0, g1) > 1 || delta(b0, b1) > 1 { if delta(r0, r1) > 1 || delta(g0, g1) > 1 || delta(b0, b1) > 1 {
t.Fatalf("r0, g0, b0 = %d, %d, %d r1, g1, b1 = %d, %d, %d", r0, g0, b0, r1, g1, b1) t.Fatalf("\nr0, g0, b0 = %d, %d, %d\nr1, g1, b1 = %d, %d, %d", r0, g0, b0, r1, g1, b1)
}
}
}
}
}
// TestCMYKToRGBConsistency tests that calling the RGBA method (16 bit color)
// then truncating to 8 bits is equivalent to calling the CMYKToRGB function (8
// bit color).
func TestCMYKToRGBConsistency(t *testing.T) {
for c := 0; c < 256; c += 7 {
for m := 0; m < 256; m += 5 {
for y := 0; y < 256; y += 3 {
for k := 0; k < 256; k += 11 {
x := CMYK{uint8(c), uint8(m), uint8(y), uint8(k)}
r0, g0, b0, _ := x.RGBA()
r1, g1, b1 := uint8(r0>>8), uint8(g0>>8), uint8(b0>>8)
r2, g2, b2 := CMYKToRGB(x.C, x.M, x.Y, x.K)
if r1 != r2 || g1 != g2 || b1 != b2 {
t.Fatalf("c, m, y, k = %d, %d, %d, %d\nr1, g1, b1 = %d, %d, %d\nr2, g2, b2 = %d, %d, %d",
c, m, y, k, r1, g1, b1, r2, g2, b2)
}
} }
} }
} }