gtsocial-umbx

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

tools.go (6910B)


      1 package imaging
      2 
      3 import (
      4 	"bytes"
      5 	"image"
      6 	"image/color"
      7 	"math"
      8 )
      9 
     10 // New creates a new image with the specified width and height, and fills it with the specified color.
     11 func New(width, height int, fillColor color.Color) *image.NRGBA {
     12 	if width <= 0 || height <= 0 {
     13 		return &image.NRGBA{}
     14 	}
     15 
     16 	c := color.NRGBAModel.Convert(fillColor).(color.NRGBA)
     17 	if (c == color.NRGBA{0, 0, 0, 0}) {
     18 		return image.NewNRGBA(image.Rect(0, 0, width, height))
     19 	}
     20 
     21 	return &image.NRGBA{
     22 		Pix:    bytes.Repeat([]byte{c.R, c.G, c.B, c.A}, width*height),
     23 		Stride: 4 * width,
     24 		Rect:   image.Rect(0, 0, width, height),
     25 	}
     26 }
     27 
     28 // Clone returns a copy of the given image.
     29 func Clone(img image.Image) *image.NRGBA {
     30 	src := newScanner(img)
     31 	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
     32 	size := src.w * 4
     33 	parallel(0, src.h, func(ys <-chan int) {
     34 		for y := range ys {
     35 			i := y * dst.Stride
     36 			src.scan(0, y, src.w, y+1, dst.Pix[i:i+size])
     37 		}
     38 	})
     39 	return dst
     40 }
     41 
     42 // Anchor is the anchor point for image alignment.
     43 type Anchor int
     44 
     45 // Anchor point positions.
     46 const (
     47 	Center Anchor = iota
     48 	TopLeft
     49 	Top
     50 	TopRight
     51 	Left
     52 	Right
     53 	BottomLeft
     54 	Bottom
     55 	BottomRight
     56 )
     57 
     58 func anchorPt(b image.Rectangle, w, h int, anchor Anchor) image.Point {
     59 	var x, y int
     60 	switch anchor {
     61 	case TopLeft:
     62 		x = b.Min.X
     63 		y = b.Min.Y
     64 	case Top:
     65 		x = b.Min.X + (b.Dx()-w)/2
     66 		y = b.Min.Y
     67 	case TopRight:
     68 		x = b.Max.X - w
     69 		y = b.Min.Y
     70 	case Left:
     71 		x = b.Min.X
     72 		y = b.Min.Y + (b.Dy()-h)/2
     73 	case Right:
     74 		x = b.Max.X - w
     75 		y = b.Min.Y + (b.Dy()-h)/2
     76 	case BottomLeft:
     77 		x = b.Min.X
     78 		y = b.Max.Y - h
     79 	case Bottom:
     80 		x = b.Min.X + (b.Dx()-w)/2
     81 		y = b.Max.Y - h
     82 	case BottomRight:
     83 		x = b.Max.X - w
     84 		y = b.Max.Y - h
     85 	default:
     86 		x = b.Min.X + (b.Dx()-w)/2
     87 		y = b.Min.Y + (b.Dy()-h)/2
     88 	}
     89 	return image.Pt(x, y)
     90 }
     91 
     92 // Crop cuts out a rectangular region with the specified bounds
     93 // from the image and returns the cropped image.
     94 func Crop(img image.Image, rect image.Rectangle) *image.NRGBA {
     95 	r := rect.Intersect(img.Bounds()).Sub(img.Bounds().Min)
     96 	if r.Empty() {
     97 		return &image.NRGBA{}
     98 	}
     99 	src := newScanner(img)
    100 	dst := image.NewNRGBA(image.Rect(0, 0, r.Dx(), r.Dy()))
    101 	rowSize := r.Dx() * 4
    102 	parallel(r.Min.Y, r.Max.Y, func(ys <-chan int) {
    103 		for y := range ys {
    104 			i := (y - r.Min.Y) * dst.Stride
    105 			src.scan(r.Min.X, y, r.Max.X, y+1, dst.Pix[i:i+rowSize])
    106 		}
    107 	})
    108 	return dst
    109 }
    110 
    111 // CropAnchor cuts out a rectangular region with the specified size
    112 // from the image using the specified anchor point and returns the cropped image.
    113 func CropAnchor(img image.Image, width, height int, anchor Anchor) *image.NRGBA {
    114 	srcBounds := img.Bounds()
    115 	pt := anchorPt(srcBounds, width, height, anchor)
    116 	r := image.Rect(0, 0, width, height).Add(pt)
    117 	b := srcBounds.Intersect(r)
    118 	return Crop(img, b)
    119 }
    120 
    121 // CropCenter cuts out a rectangular region with the specified size
    122 // from the center of the image and returns the cropped image.
    123 func CropCenter(img image.Image, width, height int) *image.NRGBA {
    124 	return CropAnchor(img, width, height, Center)
    125 }
    126 
    127 // Paste pastes the img image to the background image at the specified position and returns the combined image.
    128 func Paste(background, img image.Image, pos image.Point) *image.NRGBA {
    129 	dst := Clone(background)
    130 	pos = pos.Sub(background.Bounds().Min)
    131 	pasteRect := image.Rectangle{Min: pos, Max: pos.Add(img.Bounds().Size())}
    132 	interRect := pasteRect.Intersect(dst.Bounds())
    133 	if interRect.Empty() {
    134 		return dst
    135 	}
    136 	src := newScanner(img)
    137 	parallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) {
    138 		for y := range ys {
    139 			x1 := interRect.Min.X - pasteRect.Min.X
    140 			x2 := interRect.Max.X - pasteRect.Min.X
    141 			y1 := y - pasteRect.Min.Y
    142 			y2 := y1 + 1
    143 			i1 := y*dst.Stride + interRect.Min.X*4
    144 			i2 := i1 + interRect.Dx()*4
    145 			src.scan(x1, y1, x2, y2, dst.Pix[i1:i2])
    146 		}
    147 	})
    148 	return dst
    149 }
    150 
    151 // PasteCenter pastes the img image to the center of the background image and returns the combined image.
    152 func PasteCenter(background, img image.Image) *image.NRGBA {
    153 	bgBounds := background.Bounds()
    154 	bgW := bgBounds.Dx()
    155 	bgH := bgBounds.Dy()
    156 	bgMinX := bgBounds.Min.X
    157 	bgMinY := bgBounds.Min.Y
    158 
    159 	centerX := bgMinX + bgW/2
    160 	centerY := bgMinY + bgH/2
    161 
    162 	x0 := centerX - img.Bounds().Dx()/2
    163 	y0 := centerY - img.Bounds().Dy()/2
    164 
    165 	return Paste(background, img, image.Pt(x0, y0))
    166 }
    167 
    168 // Overlay draws the img image over the background image at given position
    169 // and returns the combined image. Opacity parameter is the opacity of the img
    170 // image layer, used to compose the images, it must be from 0.0 to 1.0.
    171 //
    172 // Examples:
    173 //
    174 //	// Draw spriteImage over backgroundImage at the given position (x=50, y=50).
    175 //	dstImage := imaging.Overlay(backgroundImage, spriteImage, image.Pt(50, 50), 1.0)
    176 //
    177 //	// Blend two opaque images of the same size.
    178 //	dstImage := imaging.Overlay(imageOne, imageTwo, image.Pt(0, 0), 0.5)
    179 //
    180 func Overlay(background, img image.Image, pos image.Point, opacity float64) *image.NRGBA {
    181 	opacity = math.Min(math.Max(opacity, 0.0), 1.0) // Ensure 0.0 <= opacity <= 1.0.
    182 	dst := Clone(background)
    183 	pos = pos.Sub(background.Bounds().Min)
    184 	pasteRect := image.Rectangle{Min: pos, Max: pos.Add(img.Bounds().Size())}
    185 	interRect := pasteRect.Intersect(dst.Bounds())
    186 	if interRect.Empty() {
    187 		return dst
    188 	}
    189 	src := newScanner(img)
    190 	parallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) {
    191 		scanLine := make([]uint8, interRect.Dx()*4)
    192 		for y := range ys {
    193 			x1 := interRect.Min.X - pasteRect.Min.X
    194 			x2 := interRect.Max.X - pasteRect.Min.X
    195 			y1 := y - pasteRect.Min.Y
    196 			y2 := y1 + 1
    197 			src.scan(x1, y1, x2, y2, scanLine)
    198 			i := y*dst.Stride + interRect.Min.X*4
    199 			j := 0
    200 			for x := interRect.Min.X; x < interRect.Max.X; x++ {
    201 				d := dst.Pix[i : i+4 : i+4]
    202 				r1 := float64(d[0])
    203 				g1 := float64(d[1])
    204 				b1 := float64(d[2])
    205 				a1 := float64(d[3])
    206 
    207 				s := scanLine[j : j+4 : j+4]
    208 				r2 := float64(s[0])
    209 				g2 := float64(s[1])
    210 				b2 := float64(s[2])
    211 				a2 := float64(s[3])
    212 
    213 				coef2 := opacity * a2 / 255
    214 				coef1 := (1 - coef2) * a1 / 255
    215 				coefSum := coef1 + coef2
    216 				coef1 /= coefSum
    217 				coef2 /= coefSum
    218 
    219 				d[0] = uint8(r1*coef1 + r2*coef2)
    220 				d[1] = uint8(g1*coef1 + g2*coef2)
    221 				d[2] = uint8(b1*coef1 + b2*coef2)
    222 				d[3] = uint8(math.Min(a1+a2*opacity*(255-a1)/255, 255))
    223 
    224 				i += 4
    225 				j += 4
    226 			}
    227 		}
    228 	})
    229 	return dst
    230 }
    231 
    232 // OverlayCenter overlays the img image to the center of the background image and
    233 // returns the combined image. Opacity parameter is the opacity of the img
    234 // image layer, used to compose the images, it must be from 0.0 to 1.0.
    235 func OverlayCenter(background, img image.Image, opacity float64) *image.NRGBA {
    236 	bgBounds := background.Bounds()
    237 	bgW := bgBounds.Dx()
    238 	bgH := bgBounds.Dy()
    239 	bgMinX := bgBounds.Min.X
    240 	bgMinY := bgBounds.Min.Y
    241 
    242 	centerX := bgMinX + bgW/2
    243 	centerY := bgMinY + bgH/2
    244 
    245 	x0 := centerX - img.Bounds().Dx()/2
    246 	y0 := centerY - img.Bounds().Dy()/2
    247 
    248 	return Overlay(background, img, image.Point{x0, y0}, opacity)
    249 }