imgutils.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. package imgutils
  2. import (
  3. "bytes"
  4. "encoding/base64"
  5. "github.com/golang/freetype"
  6. "github.com/golang/freetype/truetype"
  7. "github.com/nfnt/resize"
  8. "golang.org/x/image/font"
  9. "image"
  10. "image/color"
  11. "image/draw"
  12. _ "image/gif"
  13. "image/jpeg"
  14. _ "image/jpeg"
  15. _ "image/png"
  16. "io"
  17. "net/http"
  18. )
  19. // GetFromBase64 base64->image.image
  20. func GetFromBase64(base64Img string) (image.Image, error) {
  21. empty := image.NewRGBA(image.Rectangle{
  22. Min: image.Point{},
  23. Max: image.Point{},
  24. })
  25. if len(base64Img) == 0 {
  26. return empty, nil
  27. }
  28. imgBytes, err := base64.StdEncoding.DecodeString(base64Img)
  29. if err != nil {
  30. return empty, err
  31. }
  32. img, _, err := image.Decode(bytes.NewBuffer(imgBytes))
  33. if err != nil {
  34. return empty, err
  35. }
  36. return img, nil
  37. }
  38. // GetFromBytes bytes->image.image
  39. func GetFromBytes(imgBytes []byte) (image.Image, error) {
  40. empty := image.NewRGBA(image.Rectangle{
  41. Min: image.Point{},
  42. Max: image.Point{},
  43. })
  44. if len(imgBytes) == 0 {
  45. return empty, nil
  46. }
  47. img, _, err := image.Decode(bytes.NewBuffer(imgBytes))
  48. if err != nil {
  49. return empty, err
  50. }
  51. return img, nil
  52. }
  53. // ResizeImg 缩放原始图片 只给width,height,按比例缩放
  54. func Resize(src image.Image, width, height uint) image.Image {
  55. return resize.Resize(width, height, src, resize.Lanczos3)
  56. }
  57. func TransBackground(src image.Image) image.Image {
  58. newRgba := image.NewRGBA(src.Bounds())
  59. for i := 0; i < src.Bounds().Dx(); i++ {
  60. for j := 0; j < src.Bounds().Dy(); j++ {
  61. colorRgb := src.At(i, j)
  62. r, g, b, a := colorRgb.RGBA()
  63. nR := uint16(r)
  64. nG := uint16(g)
  65. nB := uint16(b)
  66. nA := uint16(a)
  67. if nA == 0 {
  68. nR = 65535
  69. nG = 65535
  70. nB = 65535
  71. nA = 65535
  72. }
  73. newRgba.Set(i, j, color.RGBA64{R: nR, G: nG, B: nB, A: nA})
  74. }
  75. }
  76. return newRgba
  77. }
  78. // 圆形遮罩结构 实现 image.image
  79. type circle struct {
  80. // 圆心位置
  81. p image.Point
  82. // 半径
  83. r int
  84. }
  85. func (c *circle) ColorModel() color.Model {
  86. return color.AlphaModel
  87. }
  88. func (c *circle) Bounds() image.Rectangle {
  89. return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r)
  90. }
  91. func (c *circle) At(x, y int) color.Color {
  92. xx, yy, rr := float64(x-c.p.X)+0.5, float64(y-c.p.Y)+0.5, float64(c.r)
  93. if xx*xx+yy*yy < rr*rr {
  94. return color.Alpha{A: 255} // 半径以内的图案设成完全不透明
  95. }
  96. return color.Alpha{}
  97. }
  98. // CircleMask 从图片中间取圆形遮罩
  99. func CircleMask(src image.Image) image.Image {
  100. c := circle{
  101. p: src.Bounds().Max.Div(2),
  102. r: src.Bounds().Dx() / 2,
  103. }
  104. circleImg := image.NewRGBA(image.Rect(0, 0, src.Bounds().Dx(), src.Bounds().Dy()))
  105. draw.DrawMask(circleImg, circleImg.Bounds(), src, image.Point{}, &c, image.Point{}, draw.Over)
  106. return circleImg
  107. }
  108. // MiddleCoverCoordinate 中间覆盖图片到背景图片坐标计算
  109. func MiddleCoverCoordinate(background image.Image, coverImg image.Image) image.Rectangle {
  110. minPoint := background.Bounds().Max.Div(2).Sub(coverImg.Bounds().Max.Div(2))
  111. maxPoint := minPoint.Add(coverImg.Bounds().Max)
  112. return image.Rectangle{
  113. Min: minPoint,
  114. Max: maxPoint,
  115. }
  116. }
  117. // RGBA RGBA
  118. func RGBA(img image.Image) *image.RGBA {
  119. baseSrcBounds := img.Bounds().Max
  120. newWidth := baseSrcBounds.X
  121. newHeight := baseSrcBounds.Y
  122. // 底板
  123. des := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight))
  124. //首先将一个图片信息存入jpg
  125. draw.Draw(des, des.Bounds(), img, img.Bounds().Min, draw.Over)
  126. return des
  127. }
  128. // DrawTextInfo 图片绘字信息
  129. type DrawTextInfo struct {
  130. Text string
  131. X int
  132. Y int
  133. }
  134. // TextBrush 字体相关
  135. type TextBrush struct {
  136. FontType *truetype.Font
  137. FontSize float64
  138. FontColor *image.Uniform
  139. TextWidth int
  140. DrawInfo *DrawTextInfo
  141. }
  142. func NewTextBrush(font *truetype.Font, FontSize float64, FontColor *image.Uniform, textWidth int, drawInfo *DrawTextInfo) (*TextBrush, error) {
  143. if textWidth <= 0 {
  144. textWidth = 20
  145. }
  146. return &TextBrush{FontType: font, FontSize: FontSize, FontColor: FontColor, TextWidth: textWidth, DrawInfo: drawInfo}, nil
  147. }
  148. func (tb *TextBrush) DrawFontOnRGBA(rgba *image.RGBA) error {
  149. c := freetype.NewContext()
  150. c.SetDPI(72)
  151. c.SetFont(tb.FontType)
  152. c.SetHinting(font.HintingFull)
  153. c.SetFontSize(tb.FontSize)
  154. c.SetClip(rgba.Bounds())
  155. c.SetDst(rgba)
  156. c.SetSrc(tb.FontColor)
  157. _, err := c.DrawString(tb.DrawInfo.Text, freetype.Pt(tb.DrawInfo.X, tb.DrawInfo.Y))
  158. if err != nil {
  159. return err
  160. }
  161. return nil
  162. }
  163. func DrawStringAndSave(img image.Image, infos []*TextBrush) (image.Image, error) {
  164. // 创建画板
  165. desImg := RGBA(img)
  166. for _, brush := range infos {
  167. err := brush.DrawFontOnRGBA(desImg)
  168. if err != nil {
  169. return nil, err
  170. }
  171. }
  172. return desImg, nil
  173. }
  174. func CreateWhiteBackground(w int, h int) *image.RGBA {
  175. newRgba := image.NewRGBA(image.Rectangle{
  176. Min: image.Point{},
  177. Max: image.Point{X: w, Y: h},
  178. })
  179. for i := 0; i < newRgba.Bounds().Dx(); i++ {
  180. for j := 0; j < newRgba.Bounds().Dy(); j++ {
  181. colorRgb := newRgba.At(i, j)
  182. r, g, b, a := colorRgb.RGBA()
  183. nR := uint16(r)
  184. nG := uint16(g)
  185. nB := uint16(b)
  186. nA := uint16(a)
  187. if nA == 0 {
  188. nR = 65535
  189. nG = 65535
  190. nB = 65535
  191. nA = 65535
  192. }
  193. newRgba.Set(i, j, color.RGBA64{R: nR, G: nG, B: nB, A: nA})
  194. }
  195. }
  196. return newRgba
  197. }
  198. func MergeImagesHighLow(highImg image.Image, lowImg image.Image) image.Image {
  199. var x, y int
  200. if highImg.Bounds().Dx() >= lowImg.Bounds().Dx() {
  201. x = highImg.Bounds().Dx()
  202. } else {
  203. x = lowImg.Bounds().Dx()
  204. }
  205. y = highImg.Bounds().Dy() + lowImg.Bounds().Dy()
  206. destDraw := CreateWhiteBackground(x, y)
  207. // 先画风景图
  208. draw.Draw(destDraw, destDraw.Bounds(), highImg, highImg.Bounds().Min, draw.Over)
  209. // 画微信图
  210. draw.Draw(destDraw, image.Rectangle{
  211. Min: image.Point{Y: highImg.Bounds().Max.Y},
  212. Max: destDraw.Bounds().Max,
  213. }, lowImg, lowImg.Bounds().Min, draw.Over)
  214. return destDraw
  215. }
  216. func GetBase64FromUrl(url string) (string, error) {
  217. res, err := http.Get(url)
  218. if err != nil {
  219. return "", err
  220. }
  221. defer func(Body io.ReadCloser) {
  222. err := Body.Close()
  223. if err != nil {
  224. }
  225. }(res.Body)
  226. data, err := io.ReadAll(res.Body)
  227. if err != nil {
  228. return "", err
  229. }
  230. return base64.StdEncoding.EncodeToString(data), nil
  231. }
  232. func ReplaceWechatMiniProgramQrLogo(qr image.Image, logo image.Image) ([]byte, error) {
  233. logoSize := qr.Bounds().Dx() / 3
  234. resizeLogo := resize.Resize(uint(logoSize), uint(logoSize), logo, resize.Bilinear)
  235. // 计算放置图像的位置
  236. posX := (qr.Bounds().Dx() - resizeLogo.Bounds().Dx()) / 2
  237. posY := (qr.Bounds().Dy() - resizeLogo.Bounds().Dy()) / 2
  238. bg := image.NewRGBA(image.Rect(0, 0, 150, 148))
  239. // 将每个像素设置为白色
  240. for x := 0; x < 150; x++ {
  241. for y := 0; y < 148; y++ {
  242. bg.Set(x, y, color.White)
  243. }
  244. }
  245. b := qr.Bounds()
  246. m := image.NewRGBA(b)
  247. draw.Draw(m, b, qr, image.Point{}, draw.Src)
  248. draw.Draw(m, bg.Bounds().Add(image.Point{X: 140, Y: 141}), bg, image.Point{}, draw.Src)
  249. draw.Draw(m, resizeLogo.Bounds().Add(image.Point{X: posX, Y: posY}),
  250. resizeLogo, image.Point{}, draw.Over)
  251. var bs bytes.Buffer
  252. err := jpeg.Encode(&bs, m, &jpeg.Options{Quality: jpeg.DefaultQuality})
  253. if err != nil {
  254. return nil, err
  255. }
  256. return bs.Bytes(), nil
  257. }