]> Cypherpunks repositories - gostls13.git/commitdiff
image: add image.CMYK and color.CMYK types.
authorNigel Tao <nigeltao@golang.org>
Fri, 13 Feb 2015 07:12:48 +0000 (18:12 +1100)
committerNigel Tao <nigeltao@golang.org>
Mon, 16 Feb 2015 00:08:49 +0000 (00:08 +0000)
Change-Id: Icf212a4b890725c803b16e76e1a88294b8b62cb8
Reviewed-on: https://go-review.googlesource.com/4800
Reviewed-by: Rob Pike <r@golang.org>
src/image/color/ycbcr.go
src/image/color/ycbcr_test.go
src/image/image.go

index 4c2f29ea021978ce3371c15540a5951c91221844..b7df672784c1858ea38989ce496fecf8e2770219 100644 (file)
@@ -97,3 +97,58 @@ func yCbCrModel(c Color) Color {
        y, u, v := RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8))
        return YCbCr{y, u, v}
 }
+
+// RGBToCMYK converts an RGB triple to a CMYK quadruple.
+func RGBToCMYK(r, g, b uint8) (uint8, uint8, uint8, uint8) {
+       rr := uint32(r)
+       gg := uint32(g)
+       bb := uint32(b)
+       w := rr
+       if w < gg {
+               w = gg
+       }
+       if w < bb {
+               w = bb
+       }
+       if w == 0 {
+               return 0, 0, 0, 255
+       }
+       c := (w - rr) * 255 / w
+       m := (w - gg) * 255 / w
+       y := (w - bb) * 255 / w
+       return uint8(c), uint8(m), uint8(y), uint8(255 - w)
+}
+
+// CMYKToRGB converts a CMYK quadruple to an RGB triple.
+func CMYKToRGB(c, m, y, k uint8) (uint8, uint8, uint8) {
+       w := uint32(255 - k)
+       r := uint32(255-c) * w / 255
+       g := uint32(255-m) * w / 255
+       b := uint32(255-y) * w / 255
+       return uint8(r), uint8(g), uint8(b)
+}
+
+// CMYK represents a fully opaque CMYK color, having 8 bits for each of cyan,
+// magenta, yellow and black.
+//
+// It is not associated with any particular color profile.
+type CMYK struct {
+       C, M, Y, K uint8
+}
+
+func (c CMYK) RGBA() (uint32, uint32, uint32, uint32) {
+       r, g, b := CMYKToRGB(c.C, c.M, c.Y, c.K)
+       return uint32(r) * 0x101, uint32(g) * 0x101, uint32(b) * 0x101, 0xffff
+}
+
+// CMYKModel is the Model for CMYK colors.
+var CMYKModel Model = ModelFunc(cmykModel)
+
+func cmykModel(c Color) Color {
+       if _, ok := c.(CMYK); ok {
+               return c
+       }
+       r, g, b, _ := c.RGBA()
+       cc, mm, yy, kk := RGBToCMYK(uint8(r>>8), uint8(g>>8), uint8(b>>8))
+       return CMYK{cc, mm, yy, kk}
+}
index 92a0e6ff1e7518abfc6cae957ec110dc2aad0297..94c23dd9ea2022450d466442b953db0a661f6531 100644 (file)
@@ -15,9 +15,9 @@ func delta(x, y uint8) uint8 {
        return y - x
 }
 
-// Test that a subset of RGB space can be converted to YCbCr and back to within
-// 1/256 tolerance.
-func TestRoundtrip(t *testing.T) {
+// TestYCbCrRoundtrip tests that a subset of RGB space can be converted to YCbCr
+// and back to within 1/256 tolerance.
+func TestYCbCrRoundtrip(t *testing.T) {
        for r := 0; r < 255; r += 7 {
                for g := 0; g < 255; g += 5 {
                        for b := 0; b < 255; b += 3 {
@@ -31,3 +31,20 @@ func TestRoundtrip(t *testing.T) {
                }
        }
 }
+
+// TestCMYKRoundtrip tests that a subset of RGB space can be converted to CMYK
+// and back to within 1/256 tolerance.
+func TestCMYKRoundtrip(t *testing.T) {
+       for r := 0; r < 255; r += 7 {
+               for g := 0; g < 255; g += 5 {
+                       for b := 0; b < 255; b += 3 {
+                               r0, g0, b0 := uint8(r), uint8(g), uint8(b)
+                               c, m, y, k := RGBToCMYK(r0, g0, b0)
+                               r1, g1, b1 := CMYKToRGB(c, m, y, k)
+                               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)
+                               }
+                       }
+               }
+       }
+}
index 951cc8ae054eac9f989bc78bfcbf26b468f8a01a..a5993d217a7cfc977f665b35e16be9f421d2f2dc 100644 (file)
@@ -826,6 +826,92 @@ func NewGray16(r Rectangle) *Gray16 {
        return &Gray16{pix, 2 * w, r}
 }
 
+// CMYK is an in-memory image whose At method returns color.CMYK values.
+type CMYK struct {
+       // Pix holds the image's pixels, in C, M, Y, K order. The pixel at
+       // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4].
+       Pix []uint8
+       // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
+       Stride int
+       // Rect is the image's bounds.
+       Rect Rectangle
+}
+
+func (p *CMYK) ColorModel() color.Model { return color.CMYKModel }
+
+func (p *CMYK) Bounds() Rectangle { return p.Rect }
+
+func (p *CMYK) At(x, y int) color.Color {
+       return p.CMYKAt(x, y)
+}
+
+func (p *CMYK) CMYKAt(x, y int) color.CMYK {
+       if !(Point{x, y}.In(p.Rect)) {
+               return color.CMYK{}
+       }
+       i := p.PixOffset(x, y)
+       return color.CMYK{p.Pix[i+0], p.Pix[i+1], p.Pix[i+2], p.Pix[i+3]}
+}
+
+// PixOffset returns the index of the first element of Pix that corresponds to
+// the pixel at (x, y).
+func (p *CMYK) PixOffset(x, y int) int {
+       return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4
+}
+
+func (p *CMYK) Set(x, y int, c color.Color) {
+       if !(Point{x, y}.In(p.Rect)) {
+               return
+       }
+       i := p.PixOffset(x, y)
+       c1 := color.CMYKModel.Convert(c).(color.CMYK)
+       p.Pix[i+0] = c1.C
+       p.Pix[i+1] = c1.M
+       p.Pix[i+2] = c1.Y
+       p.Pix[i+3] = c1.K
+}
+
+func (p *CMYK) SetCMYK(x, y int, c color.CMYK) {
+       if !(Point{x, y}.In(p.Rect)) {
+               return
+       }
+       i := p.PixOffset(x, y)
+       p.Pix[i+0] = c.C
+       p.Pix[i+1] = c.M
+       p.Pix[i+2] = c.Y
+       p.Pix[i+3] = c.K
+}
+
+// SubImage returns an image representing the portion of the image p visible
+// through r. The returned value shares pixels with the original image.
+func (p *CMYK) SubImage(r Rectangle) Image {
+       r = r.Intersect(p.Rect)
+       // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
+       // either r1 or r2 if the intersection is empty. Without explicitly checking for
+       // this, the Pix[i:] expression below can panic.
+       if r.Empty() {
+               return &CMYK{}
+       }
+       i := p.PixOffset(r.Min.X, r.Min.Y)
+       return &CMYK{
+               Pix:    p.Pix[i:],
+               Stride: p.Stride,
+               Rect:   r,
+       }
+}
+
+// Opaque scans the entire image and reports whether it is fully opaque.
+func (p *CMYK) Opaque() bool {
+       return true
+}
+
+// NewCMYK returns a new CMYK with the given bounds.
+func NewCMYK(r Rectangle) *CMYK {
+       w, h := r.Dx(), r.Dy()
+       buf := make([]uint8, 4*w*h)
+       return &CMYK{buf, 4 * w, r}
+}
+
 // Paletted is an in-memory image of uint8 indices into a given palette.
 type Paletted struct {
        // Pix holds the image's pixels, as palette indices. The pixel at