gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

transform.go (6746B)


      1 package imaging
      2 
      3 import (
      4 	"image"
      5 	"image/color"
      6 	"math"
      7 )
      8 
      9 // FlipH flips the image horizontally (from left to right) and returns the transformed image.
     10 func FlipH(img image.Image) *image.NRGBA {
     11 	src := newScanner(img)
     12 	dstW := src.w
     13 	dstH := src.h
     14 	rowSize := dstW * 4
     15 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
     16 	parallel(0, dstH, func(ys <-chan int) {
     17 		for dstY := range ys {
     18 			i := dstY * dst.Stride
     19 			srcY := dstY
     20 			src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize])
     21 			reverse(dst.Pix[i : i+rowSize])
     22 		}
     23 	})
     24 	return dst
     25 }
     26 
     27 // FlipV flips the image vertically (from top to bottom) and returns the transformed image.
     28 func FlipV(img image.Image) *image.NRGBA {
     29 	src := newScanner(img)
     30 	dstW := src.w
     31 	dstH := src.h
     32 	rowSize := dstW * 4
     33 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
     34 	parallel(0, dstH, func(ys <-chan int) {
     35 		for dstY := range ys {
     36 			i := dstY * dst.Stride
     37 			srcY := dstH - dstY - 1
     38 			src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize])
     39 		}
     40 	})
     41 	return dst
     42 }
     43 
     44 // Transpose flips the image horizontally and rotates 90 degrees counter-clockwise.
     45 func Transpose(img image.Image) *image.NRGBA {
     46 	src := newScanner(img)
     47 	dstW := src.h
     48 	dstH := src.w
     49 	rowSize := dstW * 4
     50 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
     51 	parallel(0, dstH, func(ys <-chan int) {
     52 		for dstY := range ys {
     53 			i := dstY * dst.Stride
     54 			srcX := dstY
     55 			src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
     56 		}
     57 	})
     58 	return dst
     59 }
     60 
     61 // Transverse flips the image vertically and rotates 90 degrees counter-clockwise.
     62 func Transverse(img image.Image) *image.NRGBA {
     63 	src := newScanner(img)
     64 	dstW := src.h
     65 	dstH := src.w
     66 	rowSize := dstW * 4
     67 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
     68 	parallel(0, dstH, func(ys <-chan int) {
     69 		for dstY := range ys {
     70 			i := dstY * dst.Stride
     71 			srcX := dstH - dstY - 1
     72 			src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
     73 			reverse(dst.Pix[i : i+rowSize])
     74 		}
     75 	})
     76 	return dst
     77 }
     78 
     79 // Rotate90 rotates the image 90 degrees counter-clockwise and returns the transformed image.
     80 func Rotate90(img image.Image) *image.NRGBA {
     81 	src := newScanner(img)
     82 	dstW := src.h
     83 	dstH := src.w
     84 	rowSize := dstW * 4
     85 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
     86 	parallel(0, dstH, func(ys <-chan int) {
     87 		for dstY := range ys {
     88 			i := dstY * dst.Stride
     89 			srcX := dstH - dstY - 1
     90 			src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
     91 		}
     92 	})
     93 	return dst
     94 }
     95 
     96 // Rotate180 rotates the image 180 degrees counter-clockwise and returns the transformed image.
     97 func Rotate180(img image.Image) *image.NRGBA {
     98 	src := newScanner(img)
     99 	dstW := src.w
    100 	dstH := src.h
    101 	rowSize := dstW * 4
    102 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
    103 	parallel(0, dstH, func(ys <-chan int) {
    104 		for dstY := range ys {
    105 			i := dstY * dst.Stride
    106 			srcY := dstH - dstY - 1
    107 			src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize])
    108 			reverse(dst.Pix[i : i+rowSize])
    109 		}
    110 	})
    111 	return dst
    112 }
    113 
    114 // Rotate270 rotates the image 270 degrees counter-clockwise and returns the transformed image.
    115 func Rotate270(img image.Image) *image.NRGBA {
    116 	src := newScanner(img)
    117 	dstW := src.h
    118 	dstH := src.w
    119 	rowSize := dstW * 4
    120 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
    121 	parallel(0, dstH, func(ys <-chan int) {
    122 		for dstY := range ys {
    123 			i := dstY * dst.Stride
    124 			srcX := dstY
    125 			src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
    126 			reverse(dst.Pix[i : i+rowSize])
    127 		}
    128 	})
    129 	return dst
    130 }
    131 
    132 // Rotate rotates an image by the given angle counter-clockwise .
    133 // The angle parameter is the rotation angle in degrees.
    134 // The bgColor parameter specifies the color of the uncovered zone after the rotation.
    135 func Rotate(img image.Image, angle float64, bgColor color.Color) *image.NRGBA {
    136 	angle = angle - math.Floor(angle/360)*360
    137 
    138 	switch angle {
    139 	case 0:
    140 		return Clone(img)
    141 	case 90:
    142 		return Rotate90(img)
    143 	case 180:
    144 		return Rotate180(img)
    145 	case 270:
    146 		return Rotate270(img)
    147 	}
    148 
    149 	src := toNRGBA(img)
    150 	srcW := src.Bounds().Max.X
    151 	srcH := src.Bounds().Max.Y
    152 	dstW, dstH := rotatedSize(srcW, srcH, angle)
    153 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
    154 
    155 	if dstW <= 0 || dstH <= 0 {
    156 		return dst
    157 	}
    158 
    159 	srcXOff := float64(srcW)/2 - 0.5
    160 	srcYOff := float64(srcH)/2 - 0.5
    161 	dstXOff := float64(dstW)/2 - 0.5
    162 	dstYOff := float64(dstH)/2 - 0.5
    163 
    164 	bgColorNRGBA := color.NRGBAModel.Convert(bgColor).(color.NRGBA)
    165 	sin, cos := math.Sincos(math.Pi * angle / 180)
    166 
    167 	parallel(0, dstH, func(ys <-chan int) {
    168 		for dstY := range ys {
    169 			for dstX := 0; dstX < dstW; dstX++ {
    170 				xf, yf := rotatePoint(float64(dstX)-dstXOff, float64(dstY)-dstYOff, sin, cos)
    171 				xf, yf = xf+srcXOff, yf+srcYOff
    172 				interpolatePoint(dst, dstX, dstY, src, xf, yf, bgColorNRGBA)
    173 			}
    174 		}
    175 	})
    176 
    177 	return dst
    178 }
    179 
    180 func rotatePoint(x, y, sin, cos float64) (float64, float64) {
    181 	return x*cos - y*sin, x*sin + y*cos
    182 }
    183 
    184 func rotatedSize(w, h int, angle float64) (int, int) {
    185 	if w <= 0 || h <= 0 {
    186 		return 0, 0
    187 	}
    188 
    189 	sin, cos := math.Sincos(math.Pi * angle / 180)
    190 	x1, y1 := rotatePoint(float64(w-1), 0, sin, cos)
    191 	x2, y2 := rotatePoint(float64(w-1), float64(h-1), sin, cos)
    192 	x3, y3 := rotatePoint(0, float64(h-1), sin, cos)
    193 
    194 	minx := math.Min(x1, math.Min(x2, math.Min(x3, 0)))
    195 	maxx := math.Max(x1, math.Max(x2, math.Max(x3, 0)))
    196 	miny := math.Min(y1, math.Min(y2, math.Min(y3, 0)))
    197 	maxy := math.Max(y1, math.Max(y2, math.Max(y3, 0)))
    198 
    199 	neww := maxx - minx + 1
    200 	if neww-math.Floor(neww) > 0.1 {
    201 		neww++
    202 	}
    203 	newh := maxy - miny + 1
    204 	if newh-math.Floor(newh) > 0.1 {
    205 		newh++
    206 	}
    207 
    208 	return int(neww), int(newh)
    209 }
    210 
    211 func interpolatePoint(dst *image.NRGBA, dstX, dstY int, src *image.NRGBA, xf, yf float64, bgColor color.NRGBA) {
    212 	j := dstY*dst.Stride + dstX*4
    213 	d := dst.Pix[j : j+4 : j+4]
    214 
    215 	x0 := int(math.Floor(xf))
    216 	y0 := int(math.Floor(yf))
    217 	bounds := src.Bounds()
    218 	if !image.Pt(x0, y0).In(image.Rect(bounds.Min.X-1, bounds.Min.Y-1, bounds.Max.X, bounds.Max.Y)) {
    219 		d[0] = bgColor.R
    220 		d[1] = bgColor.G
    221 		d[2] = bgColor.B
    222 		d[3] = bgColor.A
    223 		return
    224 	}
    225 
    226 	xq := xf - float64(x0)
    227 	yq := yf - float64(y0)
    228 	points := [4]image.Point{
    229 		{x0, y0},
    230 		{x0 + 1, y0},
    231 		{x0, y0 + 1},
    232 		{x0 + 1, y0 + 1},
    233 	}
    234 	weights := [4]float64{
    235 		(1 - xq) * (1 - yq),
    236 		xq * (1 - yq),
    237 		(1 - xq) * yq,
    238 		xq * yq,
    239 	}
    240 
    241 	var r, g, b, a float64
    242 	for i := 0; i < 4; i++ {
    243 		p := points[i]
    244 		w := weights[i]
    245 		if p.In(bounds) {
    246 			i := p.Y*src.Stride + p.X*4
    247 			s := src.Pix[i : i+4 : i+4]
    248 			wa := float64(s[3]) * w
    249 			r += float64(s[0]) * wa
    250 			g += float64(s[1]) * wa
    251 			b += float64(s[2]) * wa
    252 			a += wa
    253 		} else {
    254 			wa := float64(bgColor.A) * w
    255 			r += float64(bgColor.R) * wa
    256 			g += float64(bgColor.G) * wa
    257 			b += float64(bgColor.B) * wa
    258 			a += wa
    259 		}
    260 	}
    261 	if a != 0 {
    262 		aInv := 1 / a
    263 		d[0] = clamp(r * aInv)
    264 		d[1] = clamp(g * aInv)
    265 		d[2] = clamp(b * aInv)
    266 		d[3] = clamp(a)
    267 	}
    268 }