mirror of https://github.com/gogits/gogs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
6.5 KiB
227 lines
6.5 KiB
/* |
|
Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com> |
|
|
|
Permission to use, copy, modify, and/or distribute this software for any purpose |
|
with or without fee is hereby granted, provided that the above copyright notice |
|
and this permission notice appear in all copies. |
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH |
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND |
|
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, |
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS |
|
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
|
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF |
|
THIS SOFTWARE. |
|
*/ |
|
|
|
package resize |
|
|
|
import ( |
|
"image" |
|
"image/color" |
|
) |
|
|
|
// ycc is an in memory YCbCr image. The Y, Cb and Cr samples are held in a |
|
// single slice to increase resizing performance. |
|
type ycc struct { |
|
// Pix holds the image's pixels, in Y, Cb, Cr order. The pixel at |
|
// (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*3]. |
|
Pix []uint8 |
|
// Stride is the Pix stride (in bytes) between vertically adjacent pixels. |
|
Stride int |
|
// Rect is the image's bounds. |
|
Rect image.Rectangle |
|
// SubsampleRatio is the subsample ratio of the original YCbCr image. |
|
SubsampleRatio image.YCbCrSubsampleRatio |
|
} |
|
|
|
// PixOffset returns the index of the first element of Pix that corresponds to |
|
// the pixel at (x, y). |
|
func (p *ycc) PixOffset(x, y int) int { |
|
return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*3 |
|
} |
|
|
|
func (p *ycc) Bounds() image.Rectangle { |
|
return p.Rect |
|
} |
|
|
|
func (p *ycc) ColorModel() color.Model { |
|
return color.YCbCrModel |
|
} |
|
|
|
func (p *ycc) At(x, y int) color.Color { |
|
if !(image.Point{x, y}.In(p.Rect)) { |
|
return color.YCbCr{} |
|
} |
|
i := p.PixOffset(x, y) |
|
return color.YCbCr{ |
|
p.Pix[i+0], |
|
p.Pix[i+1], |
|
p.Pix[i+2], |
|
} |
|
} |
|
|
|
func (p *ycc) Opaque() bool { |
|
return true |
|
} |
|
|
|
// 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 *ycc) SubImage(r image.Rectangle) image.Image { |
|
r = r.Intersect(p.Rect) |
|
if r.Empty() { |
|
return &ycc{SubsampleRatio: p.SubsampleRatio} |
|
} |
|
i := p.PixOffset(r.Min.X, r.Min.Y) |
|
return &ycc{ |
|
Pix: p.Pix[i:], |
|
Stride: p.Stride, |
|
Rect: r, |
|
SubsampleRatio: p.SubsampleRatio, |
|
} |
|
} |
|
|
|
// newYCC returns a new ycc with the given bounds and subsample ratio. |
|
func newYCC(r image.Rectangle, s image.YCbCrSubsampleRatio) *ycc { |
|
w, h := r.Dx(), r.Dy() |
|
buf := make([]uint8, 3*w*h) |
|
return &ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: s} |
|
} |
|
|
|
// YCbCr converts ycc to a YCbCr image with the same subsample ratio |
|
// as the YCbCr image that ycc was generated from. |
|
func (p *ycc) YCbCr() *image.YCbCr { |
|
ycbcr := image.NewYCbCr(p.Rect, p.SubsampleRatio) |
|
var off int |
|
|
|
switch ycbcr.SubsampleRatio { |
|
case image.YCbCrSubsampleRatio422: |
|
for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ { |
|
yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride |
|
cy := (y - ycbcr.Rect.Min.Y) * ycbcr.CStride |
|
for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ { |
|
xx := (x - ycbcr.Rect.Min.X) |
|
yi := yy + xx |
|
ci := cy + xx/2 |
|
ycbcr.Y[yi] = p.Pix[off+0] |
|
ycbcr.Cb[ci] = p.Pix[off+1] |
|
ycbcr.Cr[ci] = p.Pix[off+2] |
|
off += 3 |
|
} |
|
} |
|
case image.YCbCrSubsampleRatio420: |
|
for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ { |
|
yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride |
|
cy := (y/2 - ycbcr.Rect.Min.Y/2) * ycbcr.CStride |
|
for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ { |
|
xx := (x - ycbcr.Rect.Min.X) |
|
yi := yy + xx |
|
ci := cy + xx/2 |
|
ycbcr.Y[yi] = p.Pix[off+0] |
|
ycbcr.Cb[ci] = p.Pix[off+1] |
|
ycbcr.Cr[ci] = p.Pix[off+2] |
|
off += 3 |
|
} |
|
} |
|
case image.YCbCrSubsampleRatio440: |
|
for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ { |
|
yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride |
|
cy := (y/2 - ycbcr.Rect.Min.Y/2) * ycbcr.CStride |
|
for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ { |
|
xx := (x - ycbcr.Rect.Min.X) |
|
yi := yy + xx |
|
ci := cy + xx |
|
ycbcr.Y[yi] = p.Pix[off+0] |
|
ycbcr.Cb[ci] = p.Pix[off+1] |
|
ycbcr.Cr[ci] = p.Pix[off+2] |
|
off += 3 |
|
} |
|
} |
|
default: |
|
// Default to 4:4:4 subsampling. |
|
for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ { |
|
yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride |
|
cy := (y - ycbcr.Rect.Min.Y) * ycbcr.CStride |
|
for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ { |
|
xx := (x - ycbcr.Rect.Min.X) |
|
yi := yy + xx |
|
ci := cy + xx |
|
ycbcr.Y[yi] = p.Pix[off+0] |
|
ycbcr.Cb[ci] = p.Pix[off+1] |
|
ycbcr.Cr[ci] = p.Pix[off+2] |
|
off += 3 |
|
} |
|
} |
|
} |
|
return ycbcr |
|
} |
|
|
|
// imageYCbCrToYCC converts a YCbCr image to a ycc image for resizing. |
|
func imageYCbCrToYCC(in *image.YCbCr) *ycc { |
|
w, h := in.Rect.Dx(), in.Rect.Dy() |
|
r := image.Rect(0, 0, w, h) |
|
buf := make([]uint8, 3*w*h) |
|
p := ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: in.SubsampleRatio} |
|
var off int |
|
|
|
switch in.SubsampleRatio { |
|
case image.YCbCrSubsampleRatio422: |
|
for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ { |
|
yy := (y - in.Rect.Min.Y) * in.YStride |
|
cy := (y - in.Rect.Min.Y) * in.CStride |
|
for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ { |
|
xx := (x - in.Rect.Min.X) |
|
yi := yy + xx |
|
ci := cy + xx/2 |
|
p.Pix[off+0] = in.Y[yi] |
|
p.Pix[off+1] = in.Cb[ci] |
|
p.Pix[off+2] = in.Cr[ci] |
|
off += 3 |
|
} |
|
} |
|
case image.YCbCrSubsampleRatio420: |
|
for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ { |
|
yy := (y - in.Rect.Min.Y) * in.YStride |
|
cy := (y/2 - in.Rect.Min.Y/2) * in.CStride |
|
for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ { |
|
xx := (x - in.Rect.Min.X) |
|
yi := yy + xx |
|
ci := cy + xx/2 |
|
p.Pix[off+0] = in.Y[yi] |
|
p.Pix[off+1] = in.Cb[ci] |
|
p.Pix[off+2] = in.Cr[ci] |
|
off += 3 |
|
} |
|
} |
|
case image.YCbCrSubsampleRatio440: |
|
for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ { |
|
yy := (y - in.Rect.Min.Y) * in.YStride |
|
cy := (y/2 - in.Rect.Min.Y/2) * in.CStride |
|
for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ { |
|
xx := (x - in.Rect.Min.X) |
|
yi := yy + xx |
|
ci := cy + xx |
|
p.Pix[off+0] = in.Y[yi] |
|
p.Pix[off+1] = in.Cb[ci] |
|
p.Pix[off+2] = in.Cr[ci] |
|
off += 3 |
|
} |
|
} |
|
default: |
|
// Default to 4:4:4 subsampling. |
|
for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ { |
|
yy := (y - in.Rect.Min.Y) * in.YStride |
|
cy := (y - in.Rect.Min.Y) * in.CStride |
|
for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ { |
|
xx := (x - in.Rect.Min.X) |
|
yi := yy + xx |
|
ci := cy + xx |
|
p.Pix[off+0] = in.Y[yi] |
|
p.Pix[off+1] = in.Cb[ci] |
|
p.Pix[off+2] = in.Cr[ci] |
|
off += 3 |
|
} |
|
} |
|
} |
|
return &p |
|
}
|
|
|