1/*Package clone provides image cloning function.*/ 2package clone 3 4import ( 5 "image" 6 "image/draw" 7 8 "github.com/anthonynsimon/bild/parallel" 9) 10 11// PadMethod is the method used to fill padded pixels. 12type PadMethod uint8 13 14const ( 15 // NoFill leaves the padded pixels empty. 16 NoFill = iota 17 // EdgeExtend extends the closest edge pixel. 18 EdgeExtend 19 // EdgeWrap wraps around the pixels of an image. 20 EdgeWrap 21) 22 23// AsRGBA returns an RGBA copy of the supplied image. 24func AsRGBA(src image.Image) *image.RGBA { 25 bounds := src.Bounds() 26 img := image.NewRGBA(bounds) 27 draw.Draw(img, bounds, src, bounds.Min, draw.Src) 28 return img 29} 30 31// AsShallowRGBA tries to cast to image.RGBA to get reference. Otherwise makes a copy 32func AsShallowRGBA(src image.Image) *image.RGBA { 33 if rgba, ok := src.(*image.RGBA); ok { 34 return rgba 35 } 36 return AsRGBA(src) 37} 38 39// Pad returns an RGBA copy of the src image parameter with its edges padded 40// using the supplied PadMethod. 41// Parameter padX and padY correspond to the amount of padding to be applied 42// on each side. 43// Parameter m is the PadMethod to fill the new pixels. 44// 45// Usage example: 46// 47// result := Pad(img, 5,5, EdgeExtend) 48// 49func Pad(src image.Image, padX, padY int, m PadMethod) *image.RGBA { 50 var result *image.RGBA 51 52 switch m { 53 case EdgeExtend: 54 result = extend(src, padX, padY) 55 case NoFill: 56 result = noFill(src, padX, padY) 57 case EdgeWrap: 58 result = wrap(src, padX, padY) 59 default: 60 result = extend(src, padX, padY) 61 } 62 63 return result 64} 65 66func noFill(img image.Image, padX, padY int) *image.RGBA { 67 srcBounds := img.Bounds() 68 paddedW, paddedH := srcBounds.Dx()+2*padX, srcBounds.Dy()+2*padY 69 newBounds := image.Rect(0, 0, paddedW, paddedH) 70 fillBounds := image.Rect(padX, padY, padX+srcBounds.Dx(), padY+srcBounds.Dy()) 71 72 dst := image.NewRGBA(newBounds) 73 draw.Draw(dst, fillBounds, img, srcBounds.Min, draw.Src) 74 75 return dst 76} 77 78func extend(img image.Image, padX, padY int) *image.RGBA { 79 dst := noFill(img, padX, padY) 80 paddedW, paddedH := dst.Bounds().Dx(), dst.Bounds().Dy() 81 82 parallel.Line(paddedH, func(start, end int) { 83 for y := start; y < end; y++ { 84 iy := y 85 if iy < padY { 86 iy = padY 87 } else if iy >= paddedH-padY { 88 iy = paddedH - padY - 1 89 } 90 91 for x := 0; x < paddedW; x++ { 92 ix := x 93 if ix < padX { 94 ix = padX 95 } else if x >= paddedW-padX { 96 ix = paddedW - padX - 1 97 } else if iy == y { 98 // This only enters if we are not in a y-padded area or 99 // x-padded area, so nothing to extend here. 100 // So simply jump to the next padded-x index. 101 x = paddedW - padX - 1 102 continue 103 } 104 105 dstPos := y*dst.Stride + x*4 106 edgePos := iy*dst.Stride + ix*4 107 108 dst.Pix[dstPos+0] = dst.Pix[edgePos+0] 109 dst.Pix[dstPos+1] = dst.Pix[edgePos+1] 110 dst.Pix[dstPos+2] = dst.Pix[edgePos+2] 111 dst.Pix[dstPos+3] = dst.Pix[edgePos+3] 112 } 113 } 114 }) 115 116 return dst 117} 118 119func wrap(img image.Image, padX, padY int) *image.RGBA { 120 dst := noFill(img, padX, padY) 121 paddedW, paddedH := dst.Bounds().Dx(), dst.Bounds().Dy() 122 123 parallel.Line(paddedH, func(start, end int) { 124 for y := start; y < end; y++ { 125 iy := y 126 if iy < padY { 127 iy = (paddedH - padY) - ((padY - y) % (paddedH - padY*2)) 128 } else if iy >= paddedH-padY { 129 iy = padY - ((padY - y) % (paddedH - padY*2)) 130 } 131 132 for x := 0; x < paddedW; x++ { 133 ix := x 134 if ix < padX { 135 ix = (paddedW - padX) - ((padX - x) % (paddedW - padX*2)) 136 } else if ix >= paddedW-padX { 137 ix = padX - ((padX - x) % (paddedW - padX*2)) 138 } else if iy == y { 139 // This only enters if we are not in a y-padded area or 140 // x-padded area, so nothing to extend here. 141 // So simply jump to the next padded-x index. 142 x = paddedW - padX - 1 143 continue 144 } 145 146 dstPos := y*dst.Stride + x*4 147 edgePos := iy*dst.Stride + ix*4 148 149 dst.Pix[dstPos+0] = dst.Pix[edgePos+0] 150 dst.Pix[dstPos+1] = dst.Pix[edgePos+1] 151 dst.Pix[dstPos+2] = dst.Pix[edgePos+2] 152 dst.Pix[dstPos+3] = dst.Pix[edgePos+3] 153 } 154 } 155 }) 156 157 return dst 158} 159