浏览代码

Parallelize Clone

disintegration 10 年之前
父节点
当前提交
7db23eed87
共有 2 个文件被更改,包括 224 次插入130 次删除
  1. 157 130
      helpers.go
  2. 67 0
      helpers_test.go

+ 157 - 130
helpers.go

@@ -173,186 +173,213 @@ 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()
-	dstBounds := srcBounds.Sub(srcBounds.Min)
-
-	dst := image.NewNRGBA(dstBounds)
-
-	dstMinX := dstBounds.Min.X
-	dstMinY := dstBounds.Min.Y
-
 	srcMinX := srcBounds.Min.X
 	srcMinY := srcBounds.Min.Y
-	srcMaxX := srcBounds.Max.X
-	srcMaxY := srcBounds.Max.Y
 
-	switch src0 := img.(type) {
+	dstBounds := srcBounds.Sub(srcBounds.Min)
+	dstW := dstBounds.Dx()
+	dstH := dstBounds.Dy()
+	dst := image.NewNRGBA(dstBounds)
+
+	switch src := img.(type) {
 
 	case *image.NRGBA:
 		rowSize := srcBounds.Dx() * 4
-		numRows := srcBounds.Dy()
-
-		i0 := dst.PixOffset(dstMinX, dstMinY)
-		j0 := src0.PixOffset(srcMinX, srcMinY)
-
-		di := dst.Stride
-		dj := src0.Stride
-
-		for row := 0; row < numRows; row++ {
-			copy(dst.Pix[i0:i0+rowSize], src0.Pix[j0:j0+rowSize])
-			i0 += di
-			j0 += dj
-		}
+		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])
+			}
+		})
 
 	case *image.NRGBA64:
-		i0 := dst.PixOffset(dstMinX, dstMinY)
-		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
-			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {
+		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++ {
 
-				j := src0.PixOffset(x, y)
+					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]
 
-				dst.Pix[i+0] = src0.Pix[j+0]
-				dst.Pix[i+1] = src0.Pix[j+2]
-				dst.Pix[i+2] = src0.Pix[j+4]
-				dst.Pix[i+3] = src0.Pix[j+6]
+					di += 4
+					si += 8
 
+				}
 			}
-		}
+		})
 
 	case *image.RGBA:
-		i0 := dst.PixOffset(dstMinX, dstMinY)
-		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
-			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {
-
-				j := src0.PixOffset(x, y)
-				a := src0.Pix[j+3]
-				dst.Pix[i+3] = a
-
-				switch a {
-				case 0:
-					dst.Pix[i+0] = 0
-					dst.Pix[i+1] = 0
-					dst.Pix[i+2] = 0
-				case 0xff:
-					dst.Pix[i+0] = src0.Pix[j+0]
-					dst.Pix[i+1] = src0.Pix[j+1]
-					dst.Pix[i+2] = src0.Pix[j+2]
-				default:
-					dst.Pix[i+0] = uint8(uint16(src0.Pix[j+0]) * 0xff / uint16(a))
-					dst.Pix[i+1] = uint8(uint16(src0.Pix[j+1]) * 0xff / uint16(a))
-					dst.Pix[i+2] = uint8(uint16(src0.Pix[j+2]) * 0xff / uint16(a))
+		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:
+						dst.Pix[di+0] = uint8(uint16(src.Pix[si+0]) * 0xff / uint16(a))
+						dst.Pix[di+1] = uint8(uint16(src.Pix[si+1]) * 0xff / uint16(a))
+						dst.Pix[di+2] = uint8(uint16(src.Pix[si+2]) * 0xff / uint16(a))
+					}
+
+					di += 4
+					si += 4
+
 				}
 			}
-		}
+		})
 
 	case *image.RGBA64:
-		i0 := dst.PixOffset(dstMinX, dstMinY)
-		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
-			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {
-
-				j := src0.PixOffset(x, y)
-				a := src0.Pix[j+6]
-				dst.Pix[i+3] = a
-
-				switch a {
-				case 0:
-					dst.Pix[i+0] = 0
-					dst.Pix[i+1] = 0
-					dst.Pix[i+2] = 0
-				case 0xff:
-					dst.Pix[i+0] = src0.Pix[j+0]
-					dst.Pix[i+1] = src0.Pix[j+2]
-					dst.Pix[i+2] = src0.Pix[j+4]
-				default:
-					dst.Pix[i+0] = uint8(uint16(src0.Pix[j+0]) * 0xff / uint16(a))
-					dst.Pix[i+1] = uint8(uint16(src0.Pix[j+2]) * 0xff / uint16(a))
-					dst.Pix[i+2] = uint8(uint16(src0.Pix[j+4]) * 0xff / uint16(a))
+		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:
+						dst.Pix[di+0] = uint8(uint16(src.Pix[si+0]) * 0xff / uint16(a))
+						dst.Pix[di+1] = uint8(uint16(src.Pix[si+2]) * 0xff / uint16(a))
+						dst.Pix[di+2] = uint8(uint16(src.Pix[si+4]) * 0xff / uint16(a))
+					}
+
+					di += 4
+					si += 8
+
 				}
 			}
-		}
+		})
 
 	case *image.Gray:
-		i0 := dst.PixOffset(dstMinX, dstMinY)
-		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
-			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {
+		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
 
-				j := src0.PixOffset(x, y)
-				c := src0.Pix[j]
-				dst.Pix[i+0] = c
-				dst.Pix[i+1] = c
-				dst.Pix[i+2] = c
-				dst.Pix[i+3] = 0xff
+					di += 4
+					si += 1
 
+				}
 			}
-		}
+		})
 
 	case *image.Gray16:
-		i0 := dst.PixOffset(dstMinX, dstMinY)
-		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
-			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {
+		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++ {
 
-				j := src0.PixOffset(x, y)
-				c := src0.Pix[j]
-				dst.Pix[i+0] = c
-				dst.Pix[i+1] = c
-				dst.Pix[i+2] = c
-				dst.Pix[i+3] = 0xff
+					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.YCbCr:
-		i0 := dst.PixOffset(dstMinX, dstMinY)
-		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
-			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {
-
-				yj := src0.YOffset(x, y)
-				cj := src0.COffset(x, y)
-				r, g, b := color.YCbCrToRGB(src0.Y[yj], src0.Cb[cj], src0.Cr[cj])
-
-				dst.Pix[i+0] = r
-				dst.Pix[i+1] = g
-				dst.Pix[i+2] = b
-				dst.Pix[i+3] = 0xff
+		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
 
+				}
 			}
-		}
+		})
 
 	case *image.Paletted:
-		plen := len(src0.Palette)
+		plen := len(src.Palette)
 		pnew := make([]color.NRGBA, plen)
 		for i := 0; i < plen; i++ {
-			pnew[i] = color.NRGBAModel.Convert(src0.Palette[i]).(color.NRGBA)
+			pnew[i] = color.NRGBAModel.Convert(src.Palette[i]).(color.NRGBA)
 		}
 
-		i0 := dst.PixOffset(dstMinX, dstMinY)
-		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
-			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {
+		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++ {
 
-				j := src0.PixOffset(x, y)
-				c := pnew[src0.Pix[j]]
+					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
 
-				dst.Pix[i+0] = c.R
-				dst.Pix[i+1] = c.G
-				dst.Pix[i+2] = c.B
-				dst.Pix[i+3] = c.A
+					di += 4
+					si += 1
 
+				}
 			}
-		}
+		})
 
 	default:
-		i0 := dst.PixOffset(dstMinX, dstMinY)
-		for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride {
-			for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 {
+		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(x, y)).(color.NRGBA)
+					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
 
-				dst.Pix[i+0] = c.R
-				dst.Pix[i+1] = c.G
-				dst.Pix[i+2] = c.B
-				dst.Pix[i+3] = c.A
+					di += 4
 
+				}
 			}
-		}
+		})
+
 	}
 
 	return dst

+ 67 - 0
helpers_test.go

@@ -248,6 +248,73 @@ func TestClone(t *testing.T) {
 				},
 			},
 		},
+		{
+			"Clone YCbCr 444",
+			&image.YCbCr{
+				Y:              []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff},
+				Cb:             []uint8{0x55, 0xd4, 0xff, 0x8e, 0x2c, 0x01, 0x6b, 0xaa, 0xc0, 0x95, 0x56, 0x40, 0x80, 0x80, 0x80, 0x80},
+				Cr:             []uint8{0xff, 0xeb, 0x6b, 0x36, 0x15, 0x95, 0xc0, 0xb5, 0x76, 0x41, 0x4b, 0x8c, 0x80, 0x80, 0x80, 0x80},
+				YStride:        4,
+				CStride:        4,
+				SubsampleRatio: image.YCbCrSubsampleRatio444,
+				Rect:           image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}},
+			},
+			&image.NRGBA{
+				Pix:    []uint8{0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x49, 0xe1, 0xca, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0xff, 0x0, 0xff, 0x7f, 0x0, 0x0, 0xff, 0x7f, 0x0, 0x7f, 0xff, 0x0, 0x0, 0x7f, 0xff, 0x0, 0x7f, 0x7f, 0xff, 0x0, 0x7f, 0x0, 0xff, 0x82, 0x7f, 0x0, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff},
+				Stride: 16,
+				Rect:   image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}},
+			},
+		},
+		{
+			"Clone YCbCr 440",
+			&image.YCbCr{
+				Y:              []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff},
+				Cb:             []uint8{0x2c, 0x01, 0x6b, 0xaa, 0x80, 0x80, 0x80, 0x80},
+				Cr:             []uint8{0x15, 0x95, 0xc0, 0xb5, 0x80, 0x80, 0x80, 0x80},
+				YStride:        4,
+				CStride:        4,
+				SubsampleRatio: image.YCbCrSubsampleRatio440,
+				Rect:           image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}},
+			},
+			&image.NRGBA{
+				Pix:    []uint8{0x0, 0xb5, 0x0, 0xff, 0x86, 0x86, 0x0, 0xff, 0x77, 0x0, 0x0, 0xff, 0xfb, 0x7d, 0xfb, 0xff, 0x0, 0xff, 0x1, 0xff, 0xff, 0xff, 0x1, 0xff, 0x80, 0x0, 0x1, 0xff, 0x7e, 0x0, 0x7e, 0xff, 0xe, 0xe, 0xe, 0xff, 0x59, 0x59, 0x59, 0xff, 0x4b, 0x4b, 0x4b, 0xff, 0x71, 0x71, 0x71, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff},
+				Stride: 16,
+				Rect:   image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}},
+			},
+		},
+		{
+			"Clone YCbCr 422",
+			&image.YCbCr{
+				Y:              []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff},
+				Cb:             []uint8{0xd4, 0x8e, 0x01, 0xaa, 0x95, 0x40, 0x80, 0x80},
+				Cr:             []uint8{0xeb, 0x36, 0x95, 0xb5, 0x41, 0x8c, 0x80, 0x80},
+				YStride:        4,
+				CStride:        2,
+				SubsampleRatio: image.YCbCrSubsampleRatio422,
+				Rect:           image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}},
+			},
+			&image.NRGBA{
+				Pix:    []uint8{0xe2, 0x0, 0xe1, 0xff, 0xff, 0x0, 0xfe, 0xff, 0x0, 0x4d, 0x36, 0xff, 0x49, 0xe1, 0xca, 0xff, 0xb3, 0xb3, 0x0, 0xff, 0xff, 0xff, 0x1, 0xff, 0x70, 0x0, 0x70, 0xff, 0x7e, 0x0, 0x7e, 0xff, 0x0, 0x34, 0x33, 0xff, 0x1, 0x7f, 0x7e, 0xff, 0x5c, 0x58, 0x0, 0xff, 0x82, 0x7e, 0x0, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff},
+				Stride: 16,
+				Rect:   image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}},
+			},
+		},
+		{
+			"Clone YCbCr 420",
+			&image.YCbCr{
+				Y:       []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff},
+				Cb:      []uint8{0x01, 0xaa, 0x80, 0x80},
+				Cr:      []uint8{0x95, 0xb5, 0x80, 0x80},
+				YStride: 4, CStride: 2,
+				SubsampleRatio: image.YCbCrSubsampleRatio420,
+				Rect:           image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}},
+			},
+			&image.NRGBA{
+				Pix:    []uint8{0x69, 0x69, 0x0, 0xff, 0x86, 0x86, 0x0, 0xff, 0x67, 0x0, 0x67, 0xff, 0xfb, 0x7d, 0xfb, 0xff, 0xb3, 0xb3, 0x0, 0xff, 0xff, 0xff, 0x1, 0xff, 0x70, 0x0, 0x70, 0xff, 0x7e, 0x0, 0x7e, 0xff, 0xe, 0xe, 0xe, 0xff, 0x59, 0x59, 0x59, 0xff, 0x4b, 0x4b, 0x4b, 0xff, 0x71, 0x71, 0x71, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff},
+				Stride: 16,
+				Rect:   image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}},
+			},
+		},
 		{
 			"Clone Paletted",
 			&image.Paletted{