mirror of
https://github.com/willnorris/imageproxy.git
synced 2025-01-06 22:40:34 -05:00
312 lines
7.4 KiB
Go
312 lines
7.4 KiB
Go
package imaging
|
|
|
|
import (
|
|
"image"
|
|
"image/color"
|
|
)
|
|
|
|
// Clone returns a copy of the given image.
|
|
func Clone(img image.Image) *image.NRGBA {
|
|
dstBounds := img.Bounds().Sub(img.Bounds().Min)
|
|
dst := image.NewNRGBA(dstBounds)
|
|
|
|
switch src := img.(type) {
|
|
case *image.NRGBA:
|
|
copyNRGBA(dst, src)
|
|
case *image.NRGBA64:
|
|
copyNRGBA64(dst, src)
|
|
case *image.RGBA:
|
|
copyRGBA(dst, src)
|
|
case *image.RGBA64:
|
|
copyRGBA64(dst, src)
|
|
case *image.Gray:
|
|
copyGray(dst, src)
|
|
case *image.Gray16:
|
|
copyGray16(dst, src)
|
|
case *image.YCbCr:
|
|
copyYCbCr(dst, src)
|
|
case *image.Paletted:
|
|
copyPaletted(dst, src)
|
|
default:
|
|
copyImage(dst, src)
|
|
}
|
|
|
|
return dst
|
|
}
|
|
|
|
func copyNRGBA(dst *image.NRGBA, src *image.NRGBA) {
|
|
srcMinX := src.Rect.Min.X
|
|
srcMinY := src.Rect.Min.Y
|
|
dstW := dst.Rect.Dx()
|
|
dstH := dst.Rect.Dy()
|
|
rowSize := dstW * 4
|
|
parallel(dstH, func(partStart, partEnd int) {
|
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
|
di := dst.PixOffset(0, dstY)
|
|
si := src.PixOffset(srcMinX, srcMinY+dstY)
|
|
copy(dst.Pix[di:di+rowSize], src.Pix[si:si+rowSize])
|
|
}
|
|
})
|
|
}
|
|
|
|
func copyNRGBA64(dst *image.NRGBA, src *image.NRGBA64) {
|
|
srcMinX := src.Rect.Min.X
|
|
srcMinY := src.Rect.Min.Y
|
|
dstW := dst.Rect.Dx()
|
|
dstH := dst.Rect.Dy()
|
|
parallel(dstH, func(partStart, partEnd int) {
|
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
|
di := dst.PixOffset(0, dstY)
|
|
si := src.PixOffset(srcMinX, srcMinY+dstY)
|
|
for dstX := 0; dstX < dstW; dstX++ {
|
|
dst.Pix[di+0] = src.Pix[si+0]
|
|
dst.Pix[di+1] = src.Pix[si+2]
|
|
dst.Pix[di+2] = src.Pix[si+4]
|
|
dst.Pix[di+3] = src.Pix[si+6]
|
|
di += 4
|
|
si += 8
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func copyRGBA(dst *image.NRGBA, src *image.RGBA) {
|
|
srcMinX := src.Rect.Min.X
|
|
srcMinY := src.Rect.Min.Y
|
|
dstW := dst.Rect.Dx()
|
|
dstH := dst.Rect.Dy()
|
|
parallel(dstH, func(partStart, partEnd int) {
|
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
|
di := dst.PixOffset(0, dstY)
|
|
si := src.PixOffset(srcMinX, srcMinY+dstY)
|
|
for dstX := 0; dstX < dstW; dstX++ {
|
|
a := src.Pix[si+3]
|
|
dst.Pix[di+3] = a
|
|
|
|
switch a {
|
|
case 0:
|
|
dst.Pix[di+0] = 0
|
|
dst.Pix[di+1] = 0
|
|
dst.Pix[di+2] = 0
|
|
case 0xff:
|
|
dst.Pix[di+0] = src.Pix[si+0]
|
|
dst.Pix[di+1] = src.Pix[si+1]
|
|
dst.Pix[di+2] = src.Pix[si+2]
|
|
default:
|
|
var tmp uint16
|
|
tmp = uint16(src.Pix[si+0]) * 0xff / uint16(a)
|
|
dst.Pix[di+0] = uint8(tmp)
|
|
tmp = uint16(src.Pix[si+1]) * 0xff / uint16(a)
|
|
dst.Pix[di+1] = uint8(tmp)
|
|
tmp = uint16(src.Pix[si+2]) * 0xff / uint16(a)
|
|
dst.Pix[di+2] = uint8(tmp)
|
|
}
|
|
|
|
di += 4
|
|
si += 4
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func copyRGBA64(dst *image.NRGBA, src *image.RGBA64) {
|
|
srcMinX := src.Rect.Min.X
|
|
srcMinY := src.Rect.Min.Y
|
|
dstW := dst.Rect.Dx()
|
|
dstH := dst.Rect.Dy()
|
|
parallel(dstH, func(partStart, partEnd int) {
|
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
|
di := dst.PixOffset(0, dstY)
|
|
si := src.PixOffset(srcMinX, srcMinY+dstY)
|
|
for dstX := 0; dstX < dstW; dstX++ {
|
|
a := src.Pix[si+6]
|
|
dst.Pix[di+3] = a
|
|
|
|
switch a {
|
|
case 0:
|
|
dst.Pix[di+0] = 0
|
|
dst.Pix[di+1] = 0
|
|
dst.Pix[di+2] = 0
|
|
case 0xff:
|
|
dst.Pix[di+0] = src.Pix[si+0]
|
|
dst.Pix[di+1] = src.Pix[si+2]
|
|
dst.Pix[di+2] = src.Pix[si+4]
|
|
default:
|
|
var tmp uint16
|
|
tmp = uint16(src.Pix[si+0]) * 0xff / uint16(a)
|
|
dst.Pix[di+0] = uint8(tmp)
|
|
tmp = uint16(src.Pix[si+2]) * 0xff / uint16(a)
|
|
dst.Pix[di+1] = uint8(tmp)
|
|
tmp = uint16(src.Pix[si+4]) * 0xff / uint16(a)
|
|
dst.Pix[di+2] = uint8(tmp)
|
|
}
|
|
|
|
di += 4
|
|
si += 8
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func copyGray(dst *image.NRGBA, src *image.Gray) {
|
|
srcMinX := src.Rect.Min.X
|
|
srcMinY := src.Rect.Min.Y
|
|
dstW := dst.Rect.Dx()
|
|
dstH := dst.Rect.Dy()
|
|
parallel(dstH, func(partStart, partEnd int) {
|
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
|
di := dst.PixOffset(0, dstY)
|
|
si := src.PixOffset(srcMinX, srcMinY+dstY)
|
|
for dstX := 0; dstX < dstW; dstX++ {
|
|
c := src.Pix[si]
|
|
dst.Pix[di+0] = c
|
|
dst.Pix[di+1] = c
|
|
dst.Pix[di+2] = c
|
|
dst.Pix[di+3] = 0xff
|
|
di += 4
|
|
si++
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func copyGray16(dst *image.NRGBA, src *image.Gray16) {
|
|
srcMinX := src.Rect.Min.X
|
|
srcMinY := src.Rect.Min.Y
|
|
dstW := dst.Rect.Dx()
|
|
dstH := dst.Rect.Dy()
|
|
parallel(dstH, func(partStart, partEnd int) {
|
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
|
di := dst.PixOffset(0, dstY)
|
|
si := src.PixOffset(srcMinX, srcMinY+dstY)
|
|
for dstX := 0; dstX < dstW; dstX++ {
|
|
c := src.Pix[si]
|
|
dst.Pix[di+0] = c
|
|
dst.Pix[di+1] = c
|
|
dst.Pix[di+2] = c
|
|
dst.Pix[di+3] = 0xff
|
|
di += 4
|
|
si += 2
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func copyYCbCr(dst *image.NRGBA, src *image.YCbCr) {
|
|
srcMinX := src.Rect.Min.X
|
|
srcMinY := src.Rect.Min.Y
|
|
dstW := dst.Rect.Dx()
|
|
dstH := dst.Rect.Dy()
|
|
parallel(dstH, func(partStart, partEnd int) {
|
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
|
srcY := srcMinY + dstY
|
|
di := dst.PixOffset(0, dstY)
|
|
for dstX := 0; dstX < dstW; dstX++ {
|
|
srcX := srcMinX + dstX
|
|
|
|
siy := (srcY-srcMinY)*src.YStride + (srcX - srcMinX)
|
|
|
|
var sic int
|
|
switch src.SubsampleRatio {
|
|
case image.YCbCrSubsampleRatio444:
|
|
sic = (srcY-srcMinY)*src.CStride + (srcX - srcMinX)
|
|
case image.YCbCrSubsampleRatio422:
|
|
sic = (srcY-srcMinY)*src.CStride + (srcX/2 - srcMinX/2)
|
|
case image.YCbCrSubsampleRatio420:
|
|
sic = (srcY/2-srcMinY/2)*src.CStride + (srcX/2 - srcMinX/2)
|
|
case image.YCbCrSubsampleRatio440:
|
|
sic = (srcY/2-srcMinY/2)*src.CStride + (srcX - srcMinX)
|
|
default:
|
|
sic = src.COffset(srcX, srcY)
|
|
}
|
|
|
|
y := int32(src.Y[siy])
|
|
cb := int32(src.Cb[sic]) - 128
|
|
cr := int32(src.Cr[sic]) - 128
|
|
|
|
r := (y<<16 + 91881*cr + 1<<15) >> 16
|
|
if r > 255 {
|
|
r = 255
|
|
} else if r < 0 {
|
|
r = 0
|
|
}
|
|
|
|
g := (y<<16 - 22554*cb - 46802*cr + 1<<15) >> 16
|
|
if g > 255 {
|
|
g = 255
|
|
} else if g < 0 {
|
|
g = 0
|
|
}
|
|
|
|
b := (y<<16 + 116130*cb + 1<<15) >> 16
|
|
if b > 255 {
|
|
b = 255
|
|
} else if b < 0 {
|
|
b = 0
|
|
}
|
|
|
|
dst.Pix[di+0] = uint8(r)
|
|
dst.Pix[di+1] = uint8(g)
|
|
dst.Pix[di+2] = uint8(b)
|
|
dst.Pix[di+3] = 255
|
|
|
|
di += 4
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func copyPaletted(dst *image.NRGBA, src *image.Paletted) {
|
|
srcMinX := src.Rect.Min.X
|
|
srcMinY := src.Rect.Min.Y
|
|
dstW := dst.Rect.Dx()
|
|
dstH := dst.Rect.Dy()
|
|
plen := len(src.Palette)
|
|
pnew := make([]color.NRGBA, plen)
|
|
for i := 0; i < plen; i++ {
|
|
pnew[i] = color.NRGBAModel.Convert(src.Palette[i]).(color.NRGBA)
|
|
}
|
|
parallel(dstH, func(partStart, partEnd int) {
|
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
|
di := dst.PixOffset(0, dstY)
|
|
si := src.PixOffset(srcMinX, srcMinY+dstY)
|
|
for dstX := 0; dstX < dstW; dstX++ {
|
|
c := pnew[src.Pix[si]]
|
|
dst.Pix[di+0] = c.R
|
|
dst.Pix[di+1] = c.G
|
|
dst.Pix[di+2] = c.B
|
|
dst.Pix[di+3] = c.A
|
|
di += 4
|
|
si++
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func copyImage(dst *image.NRGBA, src image.Image) {
|
|
srcMinX := src.Bounds().Min.X
|
|
srcMinY := src.Bounds().Min.Y
|
|
dstW := dst.Bounds().Dx()
|
|
dstH := dst.Bounds().Dy()
|
|
parallel(dstH, func(partStart, partEnd int) {
|
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
|
di := dst.PixOffset(0, dstY)
|
|
for dstX := 0; dstX < dstW; dstX++ {
|
|
c := color.NRGBAModel.Convert(src.At(srcMinX+dstX, srcMinY+dstY)).(color.NRGBA)
|
|
dst.Pix[di+0] = c.R
|
|
dst.Pix[di+1] = c.G
|
|
dst.Pix[di+2] = c.B
|
|
dst.Pix[di+3] = c.A
|
|
di += 4
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// toNRGBA converts any image type to *image.NRGBA with min-point at (0, 0).
|
|
func toNRGBA(img image.Image) *image.NRGBA {
|
|
if img, ok := img.(*image.NRGBA); ok && img.Bounds().Min.Eq(image.ZP) {
|
|
return img
|
|
}
|
|
return Clone(img)
|
|
}
|