|
@@ -0,0 +1,278 @@
|
|
|
+package imgutils
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "encoding/base64"
|
|
|
+ "github.com/golang/freetype"
|
|
|
+ "github.com/golang/freetype/truetype"
|
|
|
+ "github.com/nfnt/resize"
|
|
|
+ "golang.org/x/image/font"
|
|
|
+ "image"
|
|
|
+ "image/color"
|
|
|
+ "image/draw"
|
|
|
+ _ "image/gif"
|
|
|
+ _ "image/jpeg"
|
|
|
+ _ "image/png"
|
|
|
+ "io"
|
|
|
+ "net/http"
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+func GetImageFromBase64(base64Img string) (image.Image, error) {
|
|
|
+ empty := image.NewRGBA(image.Rectangle{
|
|
|
+ Min: image.Point{},
|
|
|
+ Max: image.Point{},
|
|
|
+ })
|
|
|
+ if len(base64Img) == 0 {
|
|
|
+ return empty, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ imgBytes, err := base64.StdEncoding.DecodeString(base64Img)
|
|
|
+ if err != nil {
|
|
|
+ return empty, err
|
|
|
+ }
|
|
|
+
|
|
|
+ img, _, err := image.Decode(bytes.NewBuffer(imgBytes))
|
|
|
+ if err != nil {
|
|
|
+ return empty, err
|
|
|
+ }
|
|
|
+
|
|
|
+ return img, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func GetImageFromBytes(imgBytes []byte) (image.Image, error) {
|
|
|
+ empty := image.NewRGBA(image.Rectangle{
|
|
|
+ Min: image.Point{},
|
|
|
+ Max: image.Point{},
|
|
|
+ })
|
|
|
+ if len(imgBytes) == 0 {
|
|
|
+ return empty, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ img, _, err := image.Decode(bytes.NewBuffer(imgBytes))
|
|
|
+ if err != nil {
|
|
|
+ return empty, err
|
|
|
+ }
|
|
|
+
|
|
|
+ return img, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func ResizeImg(src image.Image, width, height uint) image.Image {
|
|
|
+ return resize.Resize(width, height, src, resize.Lanczos3)
|
|
|
+}
|
|
|
+
|
|
|
+func TransBackground(src image.Image) image.Image {
|
|
|
+ newRgba := image.NewRGBA(src.Bounds())
|
|
|
+ for i := 0; i < src.Bounds().Dx(); i++ {
|
|
|
+ for j := 0; j < src.Bounds().Dy(); j++ {
|
|
|
+ colorRgb := src.At(i, j)
|
|
|
+ r, g, b, a := colorRgb.RGBA()
|
|
|
+ nR := uint16(r)
|
|
|
+ nG := uint16(g)
|
|
|
+ nB := uint16(b)
|
|
|
+ nA := uint16(a)
|
|
|
+ if nA == 0 {
|
|
|
+ nR = 65535
|
|
|
+ nG = 65535
|
|
|
+ nB = 65535
|
|
|
+ nA = 65535
|
|
|
+ }
|
|
|
+ newRgba.Set(i, j, color.RGBA64{R: nR, G: nG, B: nB, A: nA})
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return newRgba
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type circle struct {
|
|
|
+
|
|
|
+ p image.Point
|
|
|
+
|
|
|
+
|
|
|
+ r int
|
|
|
+}
|
|
|
+
|
|
|
+func (c *circle) ColorModel() color.Model {
|
|
|
+ return color.AlphaModel
|
|
|
+}
|
|
|
+
|
|
|
+func (c *circle) Bounds() image.Rectangle {
|
|
|
+ return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r)
|
|
|
+}
|
|
|
+
|
|
|
+func (c *circle) At(x, y int) color.Color {
|
|
|
+ xx, yy, rr := float64(x-c.p.X)+0.5, float64(y-c.p.Y)+0.5, float64(c.r)
|
|
|
+ if xx*xx+yy*yy < rr*rr {
|
|
|
+ return color.Alpha{A: 255}
|
|
|
+ }
|
|
|
+ return color.Alpha{}
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func CircleImage(src image.Image) image.Image {
|
|
|
+ c := circle{
|
|
|
+ p: src.Bounds().Max.Div(2),
|
|
|
+ r: src.Bounds().Dx() / 2,
|
|
|
+ }
|
|
|
+
|
|
|
+ circleImg := image.NewRGBA(image.Rect(0, 0, src.Bounds().Dx(), src.Bounds().Dy()))
|
|
|
+ draw.DrawMask(circleImg, circleImg.Bounds(), src, image.Point{}, &c, image.Point{}, draw.Over)
|
|
|
+
|
|
|
+ return circleImg
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func MiddleCoverCoordinate(background, coverImg image.Image) image.Rectangle {
|
|
|
+ minPoint := background.Bounds().Max.Div(2).Sub(coverImg.Bounds().Max.Div(2))
|
|
|
+ maxPoint := minPoint.Add(coverImg.Bounds().Max)
|
|
|
+
|
|
|
+ return image.Rectangle{
|
|
|
+ Min: minPoint,
|
|
|
+ Max: maxPoint,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func Image2RGBA(img image.Image) *image.RGBA {
|
|
|
+ baseSrcBounds := img.Bounds().Max
|
|
|
+
|
|
|
+ newWidth := baseSrcBounds.X
|
|
|
+ newHeight := baseSrcBounds.Y
|
|
|
+
|
|
|
+
|
|
|
+ des := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight))
|
|
|
+
|
|
|
+
|
|
|
+ draw.Draw(des, des.Bounds(), img, img.Bounds().Min, draw.Over)
|
|
|
+
|
|
|
+ return des
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type DrawTextInfo struct {
|
|
|
+ Text string
|
|
|
+ X int
|
|
|
+ Y int
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type TextBrush struct {
|
|
|
+ FontType *truetype.Font
|
|
|
+ FontSize float64
|
|
|
+ FontColor *image.Uniform
|
|
|
+ TextWidth int
|
|
|
+ DrawInfo *DrawTextInfo
|
|
|
+}
|
|
|
+
|
|
|
+func NewTextBrush(font *truetype.Font, FontSize float64, FontColor *image.Uniform, textWidth int, drawInfo *DrawTextInfo) (*TextBrush, error) {
|
|
|
+ if textWidth <= 0 {
|
|
|
+ textWidth = 20
|
|
|
+ }
|
|
|
+
|
|
|
+ return &TextBrush{FontType: font, FontSize: FontSize, FontColor: FontColor, TextWidth: textWidth, DrawInfo: drawInfo}, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (tb *TextBrush) DrawFontOnRGBA(rgba *image.RGBA) error {
|
|
|
+ c := freetype.NewContext()
|
|
|
+ c.SetDPI(72)
|
|
|
+ c.SetFont(tb.FontType)
|
|
|
+ c.SetHinting(font.HintingFull)
|
|
|
+ c.SetFontSize(tb.FontSize)
|
|
|
+ c.SetClip(rgba.Bounds())
|
|
|
+ c.SetDst(rgba)
|
|
|
+ c.SetSrc(tb.FontColor)
|
|
|
+
|
|
|
+ _, err := c.DrawString(tb.DrawInfo.Text, freetype.Pt(tb.DrawInfo.X, tb.DrawInfo.Y))
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func DrawStringOnImageAndSave(img image.Image, infos []*TextBrush) (image.Image, error) {
|
|
|
+
|
|
|
+ desImg := Image2RGBA(img)
|
|
|
+ for _, brush := range infos {
|
|
|
+ err := brush.DrawFontOnRGBA(desImg)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return desImg, nil
|
|
|
+}
|
|
|
+
|
|
|
+func CreateWhiteBackgroundImg(w, h int) *image.RGBA {
|
|
|
+ newRgba := image.NewRGBA(image.Rectangle{
|
|
|
+ Min: image.Point{},
|
|
|
+ Max: image.Point{X: w, Y: h},
|
|
|
+ })
|
|
|
+
|
|
|
+ for i := 0; i < newRgba.Bounds().Dx(); i++ {
|
|
|
+ for j := 0; j < newRgba.Bounds().Dy(); j++ {
|
|
|
+ colorRgb := newRgba.At(i, j)
|
|
|
+ r, g, b, a := colorRgb.RGBA()
|
|
|
+ nR := uint16(r)
|
|
|
+ nG := uint16(g)
|
|
|
+ nB := uint16(b)
|
|
|
+ nA := uint16(a)
|
|
|
+ if nA == 0 {
|
|
|
+ nR = 65535
|
|
|
+ nG = 65535
|
|
|
+ nB = 65535
|
|
|
+ nA = 65535
|
|
|
+ }
|
|
|
+ newRgba.Set(i, j, color.RGBA64{R: nR, G: nG, B: nB, A: nA})
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return newRgba
|
|
|
+}
|
|
|
+
|
|
|
+func MergeImagesHighLow(highImg, lowImg image.Image) image.Image {
|
|
|
+ var x, y int
|
|
|
+
|
|
|
+ if highImg.Bounds().Dx() >= lowImg.Bounds().Dx() {
|
|
|
+ x = highImg.Bounds().Dx()
|
|
|
+ } else {
|
|
|
+ x = lowImg.Bounds().Dx()
|
|
|
+ }
|
|
|
+
|
|
|
+ y = highImg.Bounds().Dy() + lowImg.Bounds().Dy()
|
|
|
+ destDraw := CreateWhiteBackgroundImg(x, y)
|
|
|
+
|
|
|
+
|
|
|
+ draw.Draw(destDraw, destDraw.Bounds(), highImg, highImg.Bounds().Min, draw.Over)
|
|
|
+
|
|
|
+
|
|
|
+ draw.Draw(destDraw, image.Rectangle{
|
|
|
+ Min: image.Point{Y: highImg.Bounds().Max.Y},
|
|
|
+ Max: destDraw.Bounds().Max,
|
|
|
+ }, lowImg, lowImg.Bounds().Min, draw.Over)
|
|
|
+
|
|
|
+ return destDraw
|
|
|
+}
|
|
|
+
|
|
|
+func GetUrlBase64(url string) (string, error) {
|
|
|
+ res, err := http.Get(url)
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ defer func(Body io.ReadCloser) {
|
|
|
+ err := Body.Close()
|
|
|
+ if err != nil {
|
|
|
+
|
|
|
+ }
|
|
|
+ }(res.Body)
|
|
|
+
|
|
|
+ data, err := io.ReadAll(res.Body)
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ return base64.StdEncoding.EncodeToString(data), nil
|
|
|
+}
|