|
@@ -168,213 +168,266 @@ func New(width, height int, fillColor color.Color) *image.NRGBA {
|
|
|
|
|
|
// Clone returns a copy of the given image.
|
|
|
func Clone(img image.Image) *image.NRGBA {
|
|
|
- srcBounds := img.Bounds()
|
|
|
- srcMinX := srcBounds.Min.X
|
|
|
- srcMinY := srcBounds.Min.Y
|
|
|
-
|
|
|
- dstBounds := srcBounds.Sub(srcBounds.Min)
|
|
|
- dstW := dstBounds.Dx()
|
|
|
- dstH := dstBounds.Dy()
|
|
|
+ dstBounds := img.Bounds().Sub(img.Bounds().Min)
|
|
|
dst := image.NewNRGBA(dstBounds)
|
|
|
|
|
|
switch src := img.(type) {
|
|
|
-
|
|
|
case *image.NRGBA:
|
|
|
- rowSize := srcBounds.Dx() * 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])
|
|
|
+ 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
|
|
|
}
|
|
|
- })
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- case *image.NRGBA64:
|
|
|
- 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++ {
|
|
|
+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+2]
|
|
|
- dst.Pix[di+2] = src.Pix[si+4]
|
|
|
- dst.Pix[di+3] = src.Pix[si+6]
|
|
|
- di += 4
|
|
|
- si += 8
|
|
|
+ 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)
|
|
|
}
|
|
|
- }
|
|
|
- })
|
|
|
|
|
|
- case *image.RGBA:
|
|
|
- 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
|
|
|
- }
|
|
|
+ di += 4
|
|
|
+ si += 4
|
|
|
}
|
|
|
- })
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- case *image.RGBA64:
|
|
|
- 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 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)
|
|
|
}
|
|
|
- }
|
|
|
- })
|
|
|
|
|
|
- case *image.Gray:
|
|
|
- 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 += 1
|
|
|
- }
|
|
|
+ di += 4
|
|
|
+ si += 8
|
|
|
}
|
|
|
- })
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- case *image.Gray16:
|
|
|
- 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 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++
|
|
|
}
|
|
|
- })
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- case *image.YCbCr:
|
|
|
- parallel(dstH, func(partStart, partEnd int) {
|
|
|
- for dstY := partStart; dstY < partEnd; dstY++ {
|
|
|
- di := dst.PixOffset(0, dstY)
|
|
|
- for dstX := 0; dstX < dstW; dstX++ {
|
|
|
- srcX := srcMinX + dstX
|
|
|
- srcY := srcMinY + dstY
|
|
|
- siy := src.YOffset(srcX, srcY)
|
|
|
- sic := src.COffset(srcX, srcY)
|
|
|
- r, g, b := color.YCbCrToRGB(src.Y[siy], src.Cb[sic], src.Cr[sic])
|
|
|
- dst.Pix[di+0] = r
|
|
|
- dst.Pix[di+1] = g
|
|
|
- dst.Pix[di+2] = b
|
|
|
- dst.Pix[di+3] = 0xff
|
|
|
- di += 4
|
|
|
- }
|
|
|
+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
|
|
|
}
|
|
|
- })
|
|
|
-
|
|
|
- case *image.Paletted:
|
|
|
- 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 += 1
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- default:
|
|
|
- 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(img.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
|
|
|
- }
|
|
|
+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++ {
|
|
|
+ di := dst.PixOffset(0, dstY)
|
|
|
+ for dstX := 0; dstX < dstW; dstX++ {
|
|
|
+ srcX := srcMinX + dstX
|
|
|
+ srcY := srcMinY + dstY
|
|
|
+ siy := src.YOffset(srcX, srcY)
|
|
|
+ sic := src.COffset(srcX, srcY)
|
|
|
+ r, g, b := color.YCbCrToRGB(src.Y[siy], src.Cb[sic], src.Cr[sic])
|
|
|
+ dst.Pix[di+0] = r
|
|
|
+ dst.Pix[di+1] = g
|
|
|
+ dst.Pix[di+2] = b
|
|
|
+ dst.Pix[di+3] = 0xff
|
|
|
+ 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++
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- return dst
|
|
|
+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 {
|
|
|
- srcBounds := img.Bounds()
|
|
|
- if srcBounds.Min.X == 0 && srcBounds.Min.Y == 0 {
|
|
|
- if src0, ok := img.(*image.NRGBA); ok {
|
|
|
- return src0
|
|
|
- }
|
|
|
+ if img, ok := img.(*image.NRGBA); ok && img.Bounds().Min.Eq(image.ZP) {
|
|
|
+ return img
|
|
|
}
|
|
|
return Clone(img)
|
|
|
}
|