/*Package clone provides image cloning function.*/ package clone import ( "image" "image/draw" "github.com/anthonynsimon/bild/parallel" ) // PadMethod is the method used to fill padded pixels. type PadMethod uint8 const ( // NoFill leaves the padded pixels empty. NoFill = iota // EdgeExtend extends the closest edge pixel. EdgeExtend // EdgeWrap wraps around the pixels of an image. EdgeWrap ) // AsRGBA returns an RGBA copy of the supplied image. func AsRGBA(src image.Image) *image.RGBA { bounds := src.Bounds() img := image.NewRGBA(bounds) draw.Draw(img, bounds, src, bounds.Min, draw.Src) return img } // AsShallowRGBA tries to cast to image.RGBA to get reference. Otherwise makes a copy func AsShallowRGBA(src image.Image) *image.RGBA { if rgba, ok := src.(*image.RGBA); ok { return rgba } return AsRGBA(src) } // Pad returns an RGBA copy of the src image parameter with its edges padded // using the supplied PadMethod. // Parameter padX and padY correspond to the amount of padding to be applied // on each side. // Parameter m is the PadMethod to fill the new pixels. // // Usage example: // // result := Pad(img, 5,5, EdgeExtend) func Pad(src image.Image, padX, padY int, m PadMethod) *image.RGBA { var result *image.RGBA switch m { case EdgeExtend: result = extend(src, padX, padY) case NoFill: result = noFill(src, padX, padY) case EdgeWrap: result = wrap(src, padX, padY) default: result = extend(src, padX, padY) } return result } func noFill(img image.Image, padX, padY int) *image.RGBA { srcBounds := img.Bounds() paddedW, paddedH := srcBounds.Dx()+2*padX, srcBounds.Dy()+2*padY newBounds := image.Rect(0, 0, paddedW, paddedH) fillBounds := image.Rect(padX, padY, padX+srcBounds.Dx(), padY+srcBounds.Dy()) dst := image.NewRGBA(newBounds) draw.Draw(dst, fillBounds, img, srcBounds.Min, draw.Src) return dst } func extend(img image.Image, padX, padY int) *image.RGBA { dst := noFill(img, padX, padY) paddedW, paddedH := dst.Bounds().Dx(), dst.Bounds().Dy() parallel.Line(paddedH, func(start, end int) { for y := start; y < end; y++ { iy := y if iy < padY { iy = padY } else if iy >= paddedH-padY { iy = paddedH - padY - 1 } for x := 0; x < paddedW; x++ { ix := x if ix < padX { ix = padX } else if x >= paddedW-padX { ix = paddedW - padX - 1 } else if iy == y { // This only enters if we are not in a y-padded area or // x-padded area, so nothing to extend here. // So simply jump to the next padded-x index. x = paddedW - padX - 1 continue } dstPos := y*dst.Stride + x*4 edgePos := iy*dst.Stride + ix*4 dst.Pix[dstPos+0] = dst.Pix[edgePos+0] dst.Pix[dstPos+1] = dst.Pix[edgePos+1] dst.Pix[dstPos+2] = dst.Pix[edgePos+2] dst.Pix[dstPos+3] = dst.Pix[edgePos+3] } } }) return dst } func wrap(img image.Image, padX, padY int) *image.RGBA { dst := noFill(img, padX, padY) paddedW, paddedH := dst.Bounds().Dx(), dst.Bounds().Dy() parallel.Line(paddedH, func(start, end int) { for y := start; y < end; y++ { iy := y if iy < padY { iy = (paddedH - padY) - ((padY - y) % (paddedH - padY*2)) } else if iy >= paddedH-padY { iy = padY - ((padY - y) % (paddedH - padY*2)) } for x := 0; x < paddedW; x++ { ix := x if ix < padX { ix = (paddedW - padX) - ((padX - x) % (paddedW - padX*2)) } else if ix >= paddedW-padX { ix = padX - ((padX - x) % (paddedW - padX*2)) } else if iy == y { // This only enters if we are not in a y-padded area or // x-padded area, so nothing to extend here. // So simply jump to the next padded-x index. x = paddedW - padX - 1 continue } dstPos := y*dst.Stride + x*4 edgePos := iy*dst.Stride + ix*4 dst.Pix[dstPos+0] = dst.Pix[edgePos+0] dst.Pix[dstPos+1] = dst.Pix[edgePos+1] dst.Pix[dstPos+2] = dst.Pix[edgePos+2] dst.Pix[dstPos+3] = dst.Pix[edgePos+3] } } }) return dst }