]> Cypherpunks.ru repositories - gostls13.git/commitdiff
image: add RGBA64Image interface
authorNigel Tao <nigeltao@golang.org>
Sun, 18 Apr 2021 03:38:34 +0000 (13:38 +1000)
committerNigel Tao <nigeltao@golang.org>
Fri, 18 Jun 2021 23:57:09 +0000 (23:57 +0000)
The new RGBA64At method is equivalent to the existing At method (and the
new SetRGBA64 method is equivalent to the existing Set method in the
image/draw package), but they can avoid allocations from converting
concrete color types to the color.Color interface type.

Also update api/go1.17.txt and doc/go1.17.html

Fixes #44808

Change-Id: I8671f3144512b1200fa373840ed6729a5d61bc35
Reviewed-on: https://go-review.googlesource.com/c/go/+/311129
Trust: Nigel Tao <nigeltao@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
api/go1.17.txt
doc/go1.17.html
src/image/draw/draw.go
src/image/image.go
src/image/image_test.go
src/image/ycbcr.go

index 257ca271d3dac4d6dd41792778900fef71e30a0b..c5eb381708cc6bb71d24b7b71a39c31ed98e4892 100644 (file)
@@ -28,6 +28,38 @@ pkg encoding/csv, method (*Reader) FieldPos(int) (int, int)
 pkg go/build, type Context struct, ToolTags []string
 pkg go/parser, const SkipObjectResolution = 64
 pkg go/parser, const SkipObjectResolution Mode
+pkg image, method (*Alpha) RGBA64At(int, int) color.RGBA64
+pkg image, method (*Alpha) SetRGBA64(int, int, color.RGBA64)
+pkg image, method (*Alpha16) RGBA64At(int, int) color.RGBA64
+pkg image, method (*Alpha16) SetRGBA64(int, int, color.RGBA64)
+pkg image, method (*CMYK) RGBA64At(int, int) color.RGBA64
+pkg image, method (*CMYK) SetRGBA64(int, int, color.RGBA64)
+pkg image, method (*Gray) RGBA64At(int, int) color.RGBA64
+pkg image, method (*Gray) SetRGBA64(int, int, color.RGBA64)
+pkg image, method (*Gray16) RGBA64At(int, int) color.RGBA64
+pkg image, method (*Gray16) SetRGBA64(int, int, color.RGBA64)
+pkg image, method (*NRGBA) RGBA64At(int, int) color.RGBA64
+pkg image, method (*NRGBA) SetRGBA64(int, int, color.RGBA64)
+pkg image, method (*NRGBA64) RGBA64At(int, int) color.RGBA64
+pkg image, method (*NRGBA64) SetRGBA64(int, int, color.RGBA64)
+pkg image, method (*NYCbCrA) RGBA64At(int, int) color.RGBA64
+pkg image, method (*Paletted) RGBA64At(int, int) color.RGBA64
+pkg image, method (*Paletted) SetRGBA64(int, int, color.RGBA64)
+pkg image, method (*RGBA) RGBA64At(int, int) color.RGBA64
+pkg image, method (*RGBA) SetRGBA64(int, int, color.RGBA64)
+pkg image, method (*YCbCr) RGBA64At(int, int) color.RGBA64
+pkg image, type RGBA64Image interface { At, Bounds, ColorModel, RGBA64At }
+pkg image, type RGBA64Image interface, At(int, int) color.Color
+pkg image, type RGBA64Image interface, Bounds() Rectangle
+pkg image, type RGBA64Image interface, ColorModel() color.Model
+pkg image, type RGBA64Image interface, RGBA64At(int, int) color.RGBA64
+pkg image/draw, type RGBA64Image interface { At, Bounds, ColorModel, RGBA64At, Set, SetRGBA64 }
+pkg image/draw, type RGBA64Image interface, At(int, int) color.Color
+pkg image/draw, type RGBA64Image interface, Bounds() image.Rectangle
+pkg image/draw, type RGBA64Image interface, ColorModel() color.Model
+pkg image/draw, type RGBA64Image interface, RGBA64At(int, int) color.RGBA64
+pkg image/draw, type RGBA64Image interface, Set(int, int, color.Color)
+pkg image/draw, type RGBA64Image interface, SetRGBA64(int, int, color.RGBA64)
 pkg io/fs, func FileInfoToDirEntry(FileInfo) DirEntry
 pkg math, const MaxFloat64 = 1.79769e+308  // 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368
 pkg math, const MaxInt = 9223372036854775807
@@ -153,7 +185,7 @@ pkg time, const Layout = "01/02 03:04:05PM '06 -0700"
 pkg time, const Layout ideal-string
 pkg time, func UnixMicro(int64) Time
 pkg time, func UnixMilli(int64) Time
-pkg time, method (Time) IsDST() bool
 pkg time, method (Time) GoString() string
+pkg time, method (Time) IsDST() bool
 pkg time, method (Time) UnixMicro() int64
 pkg time, method (Time) UnixMilli() int64
index 64247094539f37e3efda535e4c18fa3457625b83..945265129579d8a393c7b70fd0077f3b7eaae1ee 100644 (file)
@@ -693,6 +693,19 @@ func Foo() bool {
   </dd>
 </dl><!-- go/format -->
 
+<dl id="image"><dt><a href="/pkg/image/">image</a></dt>
+  <dd>
+    <p><!-- CL 311129 -->
+      The concrete image types (<code>RGBA</code>, <code>Gray16</code> and so on)
+      now implement a new <a href="/pkg/image/#RGBA64Image"><code>RGBA64Image</code></a>
+      interface. Those concrete types, other than the chroma-subsampling
+      related <code>YCbCr</code> and <code>NYCbCrA</code>, also now implement
+      <a href="/pkg/image/draw/#RGBA64Image"><code>draw.RGBA64Image</code></a>, a
+      new interface in the <code>image/draw</code> package.
+    </p>
+  </dd>
+</dl><!-- image -->
+
 <dl id="io/fs"><dt><a href="/pkg/io/fs/">io/fs</a></dt>
   <dd>
     <p><!-- CL 293649 -->
index 8f96aa2d180eb7d774240c242d6fab14e24e90d6..13f6668293814e7b0c4d8e3271164c94d72eeb30 100644 (file)
@@ -23,6 +23,16 @@ type Image interface {
        Set(x, y int, c color.Color)
 }
 
+// RGBA64Image extends both the Image and image.RGBA64Image interfaces with a
+// SetRGBA64 method to change a single pixel. SetRGBA64 is equivalent to
+// calling Set, but it can avoid allocations from converting concrete color
+// types to the color.Color interface type.
+type RGBA64Image interface {
+       image.RGBA64Image
+       Set(x, y int, c color.Color)
+       SetRGBA64(x, y int, c color.RGBA64)
+}
+
 // Quantizer produces a palette for an image.
 type Quantizer interface {
        // Quantize appends up to cap(p) - len(p) colors to p and returns the
index 8adba96ab6eb418f7f2c4457212f153e706e3143..930d9ac6c7bacdb9c98dfa2a7500e565cb42fd2e 100644 (file)
@@ -45,6 +45,17 @@ type Image interface {
        At(x, y int) color.Color
 }
 
+// RGBA64Image is an Image whose pixels can be converted directly to a
+// color.RGBA64.
+type RGBA64Image interface {
+       // RGBA64At returns the RGBA64 color of the pixel at (x, y). It is
+       // equivalent to calling At(x, y).RGBA() and converting the resulting
+       // 32-bit return values to a color.RGBA64, but it can avoid allocations
+       // from converting concrete color types to the color.Color interface type.
+       RGBA64At(x, y int) color.RGBA64
+       Image
+}
+
 // PalettedImage is an image whose colors may come from a limited palette.
 // If m is a PalettedImage and m.ColorModel() returns a color.Palette p,
 // then m.At(x, y) should be equivalent to p[m.ColorIndexAt(x, y)]. If m's
@@ -90,6 +101,24 @@ func (p *RGBA) At(x, y int) color.Color {
        return p.RGBAAt(x, y)
 }
 
+func (p *RGBA) RGBA64At(x, y int) color.RGBA64 {
+       if !(Point{x, y}.In(p.Rect)) {
+               return color.RGBA64{}
+       }
+       i := p.PixOffset(x, y)
+       s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
+       r := uint16(s[0])
+       g := uint16(s[1])
+       b := uint16(s[2])
+       a := uint16(s[3])
+       return color.RGBA64{
+               (r << 8) | r,
+               (g << 8) | g,
+               (b << 8) | b,
+               (a << 8) | a,
+       }
+}
+
 func (p *RGBA) RGBAAt(x, y int) color.RGBA {
        if !(Point{x, y}.In(p.Rect)) {
                return color.RGBA{}
@@ -118,6 +147,18 @@ func (p *RGBA) Set(x, y int, c color.Color) {
        s[3] = c1.A
 }
 
+func (p *RGBA) SetRGBA64(x, y int, c color.RGBA64) {
+       if !(Point{x, y}.In(p.Rect)) {
+               return
+       }
+       i := p.PixOffset(x, y)
+       s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
+       s[0] = uint8(c.R >> 8)
+       s[1] = uint8(c.G >> 8)
+       s[2] = uint8(c.B >> 8)
+       s[3] = uint8(c.A >> 8)
+}
+
 func (p *RGBA) SetRGBA(x, y int, c color.RGBA) {
        if !(Point{x, y}.In(p.Rect)) {
                return
@@ -311,6 +352,11 @@ func (p *NRGBA) At(x, y int) color.Color {
        return p.NRGBAAt(x, y)
 }
 
+func (p *NRGBA) RGBA64At(x, y int) color.RGBA64 {
+       r, g, b, a := p.NRGBAAt(x, y).RGBA()
+       return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
+}
+
 func (p *NRGBA) NRGBAAt(x, y int) color.NRGBA {
        if !(Point{x, y}.In(p.Rect)) {
                return color.NRGBA{}
@@ -339,6 +385,24 @@ func (p *NRGBA) Set(x, y int, c color.Color) {
        s[3] = c1.A
 }
 
+func (p *NRGBA) SetRGBA64(x, y int, c color.RGBA64) {
+       if !(Point{x, y}.In(p.Rect)) {
+               return
+       }
+       r, g, b, a := uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A)
+       if (a != 0) && (a != 0xffff) {
+               r = (r * 0xffff) / a
+               g = (g * 0xffff) / a
+               b = (b * 0xffff) / a
+       }
+       i := p.PixOffset(x, y)
+       s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
+       s[0] = uint8(r >> 8)
+       s[1] = uint8(g >> 8)
+       s[2] = uint8(b >> 8)
+       s[3] = uint8(a >> 8)
+}
+
 func (p *NRGBA) SetNRGBA(x, y int, c color.NRGBA) {
        if !(Point{x, y}.In(p.Rect)) {
                return
@@ -415,6 +479,11 @@ func (p *NRGBA64) At(x, y int) color.Color {
        return p.NRGBA64At(x, y)
 }
 
+func (p *NRGBA64) RGBA64At(x, y int) color.RGBA64 {
+       r, g, b, a := p.NRGBA64At(x, y).RGBA()
+       return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
+}
+
 func (p *NRGBA64) NRGBA64At(x, y int) color.NRGBA64 {
        if !(Point{x, y}.In(p.Rect)) {
                return color.NRGBA64{}
@@ -452,6 +521,28 @@ func (p *NRGBA64) Set(x, y int, c color.Color) {
        s[7] = uint8(c1.A)
 }
 
+func (p *NRGBA64) SetRGBA64(x, y int, c color.RGBA64) {
+       if !(Point{x, y}.In(p.Rect)) {
+               return
+       }
+       r, g, b, a := uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A)
+       if (a != 0) && (a != 0xffff) {
+               r = (r * 0xffff) / a
+               g = (g * 0xffff) / a
+               b = (b * 0xffff) / a
+       }
+       i := p.PixOffset(x, y)
+       s := p.Pix[i : i+8 : i+8] // Small cap improves performance, see https://golang.org/issue/27857
+       s[0] = uint8(r >> 8)
+       s[1] = uint8(r)
+       s[2] = uint8(g >> 8)
+       s[3] = uint8(g)
+       s[4] = uint8(b >> 8)
+       s[5] = uint8(b)
+       s[6] = uint8(a >> 8)
+       s[7] = uint8(a)
+}
+
 func (p *NRGBA64) SetNRGBA64(x, y int, c color.NRGBA64) {
        if !(Point{x, y}.In(p.Rect)) {
                return
@@ -532,6 +623,12 @@ func (p *Alpha) At(x, y int) color.Color {
        return p.AlphaAt(x, y)
 }
 
+func (p *Alpha) RGBA64At(x, y int) color.RGBA64 {
+       a := uint16(p.AlphaAt(x, y).A)
+       a |= a << 8
+       return color.RGBA64{a, a, a, a}
+}
+
 func (p *Alpha) AlphaAt(x, y int) color.Alpha {
        if !(Point{x, y}.In(p.Rect)) {
                return color.Alpha{}
@@ -554,6 +651,14 @@ func (p *Alpha) Set(x, y int, c color.Color) {
        p.Pix[i] = color.AlphaModel.Convert(c).(color.Alpha).A
 }
 
+func (p *Alpha) SetRGBA64(x, y int, c color.RGBA64) {
+       if !(Point{x, y}.In(p.Rect)) {
+               return
+       }
+       i := p.PixOffset(x, y)
+       p.Pix[i] = uint8(c.A >> 8)
+}
+
 func (p *Alpha) SetAlpha(x, y int, c color.Alpha) {
        if !(Point{x, y}.In(p.Rect)) {
                return
@@ -626,6 +731,11 @@ func (p *Alpha16) At(x, y int) color.Color {
        return p.Alpha16At(x, y)
 }
 
+func (p *Alpha16) RGBA64At(x, y int) color.RGBA64 {
+       a := p.Alpha16At(x, y).A
+       return color.RGBA64{a, a, a, a}
+}
+
 func (p *Alpha16) Alpha16At(x, y int) color.Alpha16 {
        if !(Point{x, y}.In(p.Rect)) {
                return color.Alpha16{}
@@ -650,6 +760,15 @@ func (p *Alpha16) Set(x, y int, c color.Color) {
        p.Pix[i+1] = uint8(c1.A)
 }
 
+func (p *Alpha16) SetRGBA64(x, y int, c color.RGBA64) {
+       if !(Point{x, y}.In(p.Rect)) {
+               return
+       }
+       i := p.PixOffset(x, y)
+       p.Pix[i+0] = uint8(c.A >> 8)
+       p.Pix[i+1] = uint8(c.A)
+}
+
 func (p *Alpha16) SetAlpha16(x, y int, c color.Alpha16) {
        if !(Point{x, y}.In(p.Rect)) {
                return
@@ -723,6 +842,12 @@ func (p *Gray) At(x, y int) color.Color {
        return p.GrayAt(x, y)
 }
 
+func (p *Gray) RGBA64At(x, y int) color.RGBA64 {
+       gray := uint16(p.GrayAt(x, y).Y)
+       gray |= gray << 8
+       return color.RGBA64{gray, gray, gray, 0xffff}
+}
+
 func (p *Gray) GrayAt(x, y int) color.Gray {
        if !(Point{x, y}.In(p.Rect)) {
                return color.Gray{}
@@ -745,6 +870,16 @@ func (p *Gray) Set(x, y int, c color.Color) {
        p.Pix[i] = color.GrayModel.Convert(c).(color.Gray).Y
 }
 
+func (p *Gray) SetRGBA64(x, y int, c color.RGBA64) {
+       if !(Point{x, y}.In(p.Rect)) {
+               return
+       }
+       // This formula is the same as in color.grayModel.
+       gray := (19595*uint32(c.R) + 38470*uint32(c.G) + 7471*uint32(c.B) + 1<<15) >> 24
+       i := p.PixOffset(x, y)
+       p.Pix[i] = uint8(gray)
+}
+
 func (p *Gray) SetGray(x, y int, c color.Gray) {
        if !(Point{x, y}.In(p.Rect)) {
                return
@@ -804,6 +939,11 @@ func (p *Gray16) At(x, y int) color.Color {
        return p.Gray16At(x, y)
 }
 
+func (p *Gray16) RGBA64At(x, y int) color.RGBA64 {
+       gray := p.Gray16At(x, y).Y
+       return color.RGBA64{gray, gray, gray, 0xffff}
+}
+
 func (p *Gray16) Gray16At(x, y int) color.Gray16 {
        if !(Point{x, y}.In(p.Rect)) {
                return color.Gray16{}
@@ -828,6 +968,17 @@ func (p *Gray16) Set(x, y int, c color.Color) {
        p.Pix[i+1] = uint8(c1.Y)
 }
 
+func (p *Gray16) SetRGBA64(x, y int, c color.RGBA64) {
+       if !(Point{x, y}.In(p.Rect)) {
+               return
+       }
+       // This formula is the same as in color.gray16Model.
+       gray := (19595*uint32(c.R) + 38470*uint32(c.G) + 7471*uint32(c.B) + 1<<15) >> 16
+       i := p.PixOffset(x, y)
+       p.Pix[i+0] = uint8(gray >> 8)
+       p.Pix[i+1] = uint8(gray)
+}
+
 func (p *Gray16) SetGray16(x, y int, c color.Gray16) {
        if !(Point{x, y}.In(p.Rect)) {
                return
@@ -888,6 +1039,11 @@ func (p *CMYK) At(x, y int) color.Color {
        return p.CMYKAt(x, y)
 }
 
+func (p *CMYK) RGBA64At(x, y int) color.RGBA64 {
+       r, g, b, a := p.CMYKAt(x, y).RGBA()
+       return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
+}
+
 func (p *CMYK) CMYKAt(x, y int) color.CMYK {
        if !(Point{x, y}.In(p.Rect)) {
                return color.CMYK{}
@@ -916,6 +1072,19 @@ func (p *CMYK) Set(x, y int, c color.Color) {
        s[3] = c1.K
 }
 
+func (p *CMYK) SetRGBA64(x, y int, c color.RGBA64) {
+       if !(Point{x, y}.In(p.Rect)) {
+               return
+       }
+       cc, mm, yy, kk := color.RGBToCMYK(uint8(c.R>>8), uint8(c.G>>8), uint8(c.B>>8))
+       i := p.PixOffset(x, y)
+       s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
+       s[0] = cc
+       s[1] = mm
+       s[2] = yy
+       s[3] = kk
+}
+
 func (p *CMYK) SetCMYK(x, y int, c color.CMYK) {
        if !(Point{x, y}.In(p.Rect)) {
                return
@@ -988,6 +1157,26 @@ func (p *Paletted) At(x, y int) color.Color {
        return p.Palette[p.Pix[i]]
 }
 
+func (p *Paletted) RGBA64At(x, y int) color.RGBA64 {
+       if len(p.Palette) == 0 {
+               return color.RGBA64{}
+       }
+       c := color.Color(nil)
+       if !(Point{x, y}.In(p.Rect)) {
+               c = p.Palette[0]
+       } else {
+               i := p.PixOffset(x, y)
+               c = p.Palette[p.Pix[i]]
+       }
+       r, g, b, a := c.RGBA()
+       return color.RGBA64{
+               uint16(r),
+               uint16(g),
+               uint16(b),
+               uint16(a),
+       }
+}
+
 // PixOffset returns the index of the first element of Pix that corresponds to
 // the pixel at (x, y).
 func (p *Paletted) PixOffset(x, y int) int {
@@ -1002,6 +1191,14 @@ func (p *Paletted) Set(x, y int, c color.Color) {
        p.Pix[i] = uint8(p.Palette.Index(c))
 }
 
+func (p *Paletted) SetRGBA64(x, y int, c color.RGBA64) {
+       if !(Point{x, y}.In(p.Rect)) {
+               return
+       }
+       i := p.PixOffset(x, y)
+       p.Pix[i] = uint8(p.Palette.Index(c))
+}
+
 func (p *Paletted) ColorIndexAt(x, y int) uint8 {
        if !(Point{x, y}.In(p.Rect)) {
                return 0
index b9b9bfaa28ca4157adf0c08478e2a733baeb9ac7..c64b6107b740e5c570ad3a38334e4697a8f1f54c 100644 (file)
@@ -6,6 +6,7 @@ package image
 
 import (
        "image/color"
+       "image/color/palette"
        "testing"
 )
 
@@ -191,6 +192,80 @@ func Test16BitsPerColorChannel(t *testing.T) {
        }
 }
 
+func TestRGBA64Image(t *testing.T) {
+       // memset sets every element of s to v.
+       memset := func(s []byte, v byte) {
+               for i := range s {
+                       s[i] = v
+               }
+       }
+
+       r := Rect(0, 0, 3, 2)
+       testCases := []Image{
+               NewAlpha(r),
+               NewAlpha16(r),
+               NewCMYK(r),
+               NewGray(r),
+               NewGray16(r),
+               NewNRGBA(r),
+               NewNRGBA64(r),
+               NewNYCbCrA(r, YCbCrSubsampleRatio444),
+               NewPaletted(r, palette.Plan9),
+               NewRGBA(r),
+               NewRGBA64(r),
+               NewYCbCr(r, YCbCrSubsampleRatio444),
+       }
+       for _, tc := range testCases {
+               switch tc := tc.(type) {
+               // Most of the concrete image types in the testCases implement the
+               // draw.RGBA64Image interface: they have a SetRGBA64 method. We use an
+               // interface literal here, instead of importing "image/draw", to avoid
+               // an import cycle.
+               //
+               // The YCbCr and NYCbCrA types are special-cased. Chroma subsampling
+               // means that setting one pixel can modify neighboring pixels. They
+               // don't have Set or SetRGBA64 methods because that side effect could
+               // be surprising. Here, we just memset the channel buffers instead.
+               case interface {
+                       SetRGBA64(x, y int, c color.RGBA64)
+               }:
+                       tc.SetRGBA64(1, 1, color.RGBA64{0x7FFF, 0x3FFF, 0x0000, 0x7FFF})
+
+               case *NYCbCrA:
+                       memset(tc.YCbCr.Y, 0x77)
+                       memset(tc.YCbCr.Cb, 0x88)
+                       memset(tc.YCbCr.Cr, 0x99)
+                       memset(tc.A, 0xAA)
+
+               case *YCbCr:
+                       memset(tc.Y, 0x77)
+                       memset(tc.Cb, 0x88)
+                       memset(tc.Cr, 0x99)
+
+               default:
+                       t.Errorf("could not initialize pixels for %T", tc)
+                       continue
+               }
+
+               // Check that RGBA64At(x, y) is equivalent to At(x, y).RGBA().
+               rgba64Image, ok := tc.(RGBA64Image)
+               if !ok {
+                       t.Errorf("%T is not an RGBA64Image", tc)
+                       continue
+               }
+               got := rgba64Image.RGBA64At(1, 1)
+               wantR, wantG, wantB, wantA := tc.At(1, 1).RGBA()
+               if (uint32(got.R) != wantR) || (uint32(got.G) != wantG) ||
+                       (uint32(got.B) != wantB) || (uint32(got.A) != wantA) {
+                       t.Errorf("%T:\ngot  (0x%04X, 0x%04X, 0x%04X, 0x%04X)\n"+
+                               "want (0x%04X, 0x%04X, 0x%04X, 0x%04X)", tc,
+                               got.R, got.G, got.B, got.A,
+                               wantR, wantG, wantB, wantA)
+                       continue
+               }
+       }
+}
+
 func BenchmarkAt(b *testing.B) {
        for _, tc := range testImages {
                b.Run(tc.name, func(b *testing.B) {
index fbdffe1bd1babc8c9b93c1b6c2cf46bf6e8559aa..328b90d1525655d079bab6a5f10a1604d9e0c413 100644 (file)
@@ -71,6 +71,11 @@ func (p *YCbCr) At(x, y int) color.Color {
        return p.YCbCrAt(x, y)
 }
 
+func (p *YCbCr) RGBA64At(x, y int) color.RGBA64 {
+       r, g, b, a := p.YCbCrAt(x, y).RGBA()
+       return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
+}
+
 func (p *YCbCr) YCbCrAt(x, y int) color.YCbCr {
        if !(Point{x, y}.In(p.Rect)) {
                return color.YCbCr{}
@@ -210,6 +215,11 @@ func (p *NYCbCrA) At(x, y int) color.Color {
        return p.NYCbCrAAt(x, y)
 }
 
+func (p *NYCbCrA) RGBA64At(x, y int) color.RGBA64 {
+       r, g, b, a := p.NYCbCrAAt(x, y).RGBA()
+       return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
+}
+
 func (p *NYCbCrA) NYCbCrAAt(x, y int) color.NYCbCrA {
        if !(Point{X: x, Y: y}.In(p.Rect)) {
                return color.NYCbCrA{}