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/jpeg" _ "image/png" "io" "net/http" ) // GetFromBase64 base64->image.image func GetFromBase64(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 } // GetFromBytes bytes->image.image func GetFromBytes(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 } // ResizeImg 缩放原始图片 只给width,height,按比例缩放 func Resize(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 } // 圆形遮罩结构 实现 image.image 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{} } // CircleMask 从图片中间取圆形遮罩 func CircleMask(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 } // MiddleCoverCoordinate 中间覆盖图片到背景图片坐标计算 func MiddleCoverCoordinate(background image.Image, 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, } } // RGBA RGBA func RGBA(img image.Image) *image.RGBA { baseSrcBounds := img.Bounds().Max newWidth := baseSrcBounds.X newHeight := baseSrcBounds.Y // 底板 des := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight)) //首先将一个图片信息存入jpg draw.Draw(des, des.Bounds(), img, img.Bounds().Min, draw.Over) return des } // DrawTextInfo 图片绘字信息 type DrawTextInfo struct { Text string X int Y int } // TextBrush 字体相关 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 DrawStringAndSave(img image.Image, infos []*TextBrush) (image.Image, error) { // 创建画板 desImg := RGBA(img) for _, brush := range infos { err := brush.DrawFontOnRGBA(desImg) if err != nil { return nil, err } } return desImg, nil } func CreateWhiteBackground(w int, 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 image.Image, 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 := CreateWhiteBackground(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 GetBase64FromUrl(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 } func ReplaceWechatMiniProgramQrLogo(qr image.Image, logo image.Image) ([]byte, error) { logoSize := qr.Bounds().Dx() / 3 resizeLogo := resize.Resize(uint(logoSize), uint(logoSize), logo, resize.Bilinear) // 计算放置图像的位置 posX := (qr.Bounds().Dx() - resizeLogo.Bounds().Dx()) / 2 posY := (qr.Bounds().Dy() - resizeLogo.Bounds().Dy()) / 2 bg := image.NewRGBA(image.Rect(0, 0, 150, 148)) // 将每个像素设置为白色 for x := 0; x < 150; x++ { for y := 0; y < 148; y++ { bg.Set(x, y, color.White) } } b := qr.Bounds() m := image.NewRGBA(b) draw.Draw(m, b, qr, image.Point{}, draw.Src) draw.Draw(m, bg.Bounds().Add(image.Point{X: 140, Y: 141}), bg, image.Point{}, draw.Src) draw.Draw(m, resizeLogo.Bounds().Add(image.Point{X: posX, Y: posY}), resizeLogo, image.Point{}, draw.Over) var bs bytes.Buffer err := jpeg.Encode(&bs, m, &jpeg.Options{Quality: jpeg.DefaultQuality}) if err != nil { return nil, err } return bs.Bytes(), nil }