1/* 2Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com> 3 4Permission to use, copy, modify, and/or distribute this software for any purpose 5with or without fee is hereby granted, provided that the above copyright notice 6and this permission notice appear in all copies. 7 8THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 10FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 12OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 13TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 14THIS SOFTWARE. 15*/ 16 17package resize 18 19import ( 20 "image" 21 "image/color" 22) 23 24// ycc is an in memory YCbCr image. The Y, Cb and Cr samples are held in a 25// single slice to increase resizing performance. 26type ycc struct { 27 // Pix holds the image's pixels, in Y, Cb, Cr order. The pixel at 28 // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*3]. 29 Pix []uint8 30 // Stride is the Pix stride (in bytes) between vertically adjacent pixels. 31 Stride int 32 // Rect is the image's bounds. 33 Rect image.Rectangle 34 // SubsampleRatio is the subsample ratio of the original YCbCr image. 35 SubsampleRatio image.YCbCrSubsampleRatio 36} 37 38// PixOffset returns the index of the first element of Pix that corresponds to 39// the pixel at (x, y). 40func (p *ycc) PixOffset(x, y int) int { 41 return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*3 42} 43 44func (p *ycc) Bounds() image.Rectangle { 45 return p.Rect 46} 47 48func (p *ycc) ColorModel() color.Model { 49 return color.YCbCrModel 50} 51 52func (p *ycc) At(x, y int) color.Color { 53 if !(image.Point{x, y}.In(p.Rect)) { 54 return color.YCbCr{} 55 } 56 i := p.PixOffset(x, y) 57 return color.YCbCr{ 58 p.Pix[i+0], 59 p.Pix[i+1], 60 p.Pix[i+2], 61 } 62} 63 64func (p *ycc) Opaque() bool { 65 return true 66} 67 68// SubImage returns an image representing the portion of the image p visible 69// through r. The returned value shares pixels with the original image. 70func (p *ycc) SubImage(r image.Rectangle) image.Image { 71 r = r.Intersect(p.Rect) 72 if r.Empty() { 73 return &ycc{SubsampleRatio: p.SubsampleRatio} 74 } 75 i := p.PixOffset(r.Min.X, r.Min.Y) 76 return &ycc{ 77 Pix: p.Pix[i:], 78 Stride: p.Stride, 79 Rect: r, 80 SubsampleRatio: p.SubsampleRatio, 81 } 82} 83 84// newYCC returns a new ycc with the given bounds and subsample ratio. 85func newYCC(r image.Rectangle, s image.YCbCrSubsampleRatio) *ycc { 86 w, h := r.Dx(), r.Dy() 87 buf := make([]uint8, 3*w*h) 88 return &ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: s} 89} 90 91// YCbCr converts ycc to a YCbCr image with the same subsample ratio 92// as the YCbCr image that ycc was generated from. 93func (p *ycc) YCbCr() *image.YCbCr { 94 ycbcr := image.NewYCbCr(p.Rect, p.SubsampleRatio) 95 var off int 96 97 switch ycbcr.SubsampleRatio { 98 case image.YCbCrSubsampleRatio422: 99 for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ { 100 yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride 101 cy := (y - ycbcr.Rect.Min.Y) * ycbcr.CStride 102 for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ { 103 xx := (x - ycbcr.Rect.Min.X) 104 yi := yy + xx 105 ci := cy + xx/2 106 ycbcr.Y[yi] = p.Pix[off+0] 107 ycbcr.Cb[ci] = p.Pix[off+1] 108 ycbcr.Cr[ci] = p.Pix[off+2] 109 off += 3 110 } 111 } 112 case image.YCbCrSubsampleRatio420: 113 for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ { 114 yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride 115 cy := (y/2 - ycbcr.Rect.Min.Y/2) * ycbcr.CStride 116 for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ { 117 xx := (x - ycbcr.Rect.Min.X) 118 yi := yy + xx 119 ci := cy + xx/2 120 ycbcr.Y[yi] = p.Pix[off+0] 121 ycbcr.Cb[ci] = p.Pix[off+1] 122 ycbcr.Cr[ci] = p.Pix[off+2] 123 off += 3 124 } 125 } 126 case image.YCbCrSubsampleRatio440: 127 for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ { 128 yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride 129 cy := (y/2 - ycbcr.Rect.Min.Y/2) * ycbcr.CStride 130 for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ { 131 xx := (x - ycbcr.Rect.Min.X) 132 yi := yy + xx 133 ci := cy + xx 134 ycbcr.Y[yi] = p.Pix[off+0] 135 ycbcr.Cb[ci] = p.Pix[off+1] 136 ycbcr.Cr[ci] = p.Pix[off+2] 137 off += 3 138 } 139 } 140 default: 141 // Default to 4:4:4 subsampling. 142 for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ { 143 yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride 144 cy := (y - ycbcr.Rect.Min.Y) * ycbcr.CStride 145 for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ { 146 xx := (x - ycbcr.Rect.Min.X) 147 yi := yy + xx 148 ci := cy + xx 149 ycbcr.Y[yi] = p.Pix[off+0] 150 ycbcr.Cb[ci] = p.Pix[off+1] 151 ycbcr.Cr[ci] = p.Pix[off+2] 152 off += 3 153 } 154 } 155 } 156 return ycbcr 157} 158 159// imageYCbCrToYCC converts a YCbCr image to a ycc image for resizing. 160func imageYCbCrToYCC(in *image.YCbCr) *ycc { 161 w, h := in.Rect.Dx(), in.Rect.Dy() 162 r := image.Rect(0, 0, w, h) 163 buf := make([]uint8, 3*w*h) 164 p := ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: in.SubsampleRatio} 165 var off int 166 167 switch in.SubsampleRatio { 168 case image.YCbCrSubsampleRatio422: 169 for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ { 170 yy := (y - in.Rect.Min.Y) * in.YStride 171 cy := (y - in.Rect.Min.Y) * in.CStride 172 for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ { 173 xx := (x - in.Rect.Min.X) 174 yi := yy + xx 175 ci := cy + xx/2 176 p.Pix[off+0] = in.Y[yi] 177 p.Pix[off+1] = in.Cb[ci] 178 p.Pix[off+2] = in.Cr[ci] 179 off += 3 180 } 181 } 182 case image.YCbCrSubsampleRatio420: 183 for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ { 184 yy := (y - in.Rect.Min.Y) * in.YStride 185 cy := (y/2 - in.Rect.Min.Y/2) * in.CStride 186 for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ { 187 xx := (x - in.Rect.Min.X) 188 yi := yy + xx 189 ci := cy + xx/2 190 p.Pix[off+0] = in.Y[yi] 191 p.Pix[off+1] = in.Cb[ci] 192 p.Pix[off+2] = in.Cr[ci] 193 off += 3 194 } 195 } 196 case image.YCbCrSubsampleRatio440: 197 for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ { 198 yy := (y - in.Rect.Min.Y) * in.YStride 199 cy := (y/2 - in.Rect.Min.Y/2) * in.CStride 200 for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ { 201 xx := (x - in.Rect.Min.X) 202 yi := yy + xx 203 ci := cy + xx 204 p.Pix[off+0] = in.Y[yi] 205 p.Pix[off+1] = in.Cb[ci] 206 p.Pix[off+2] = in.Cr[ci] 207 off += 3 208 } 209 } 210 default: 211 // Default to 4:4:4 subsampling. 212 for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ { 213 yy := (y - in.Rect.Min.Y) * in.YStride 214 cy := (y - in.Rect.Min.Y) * in.CStride 215 for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ { 216 xx := (x - in.Rect.Min.X) 217 yi := yy + xx 218 ci := cy + xx 219 p.Pix[off+0] = in.Y[yi] 220 p.Pix[off+1] = in.Cb[ci] 221 p.Pix[off+2] = in.Cr[ci] 222 off += 3 223 } 224 } 225 } 226 return &p 227} 228