123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- package common
- import (
- "bytes"
- "fmt"
- "github.com/disintegration/imaging"
- "github.com/fogleman/gg"
- "github.com/gogf/gf/v2/util/gconv"
- "github.com/nfnt/resize"
- "github.com/skip2/go-qrcode"
- "image"
- "image/color"
- "image/draw"
- "image/jpeg"
- "image/png"
- "math"
- "net/http"
- )
- func compositeImage(backgroundURL, qrContent string) (err error, imgByte []byte) {
- // 配置参数
- //backgroundURL := "https://images.unsplash.com/photo-1501854140801-50d01698950b" // 示例背景图片
- //qrContent := "https://github.com/zeromicro/go-zero" // 二维码内容
- //outputPath := "output.jpg" // 输出文件路径
- //margin := 20 // 二维码距边缘的边距
- rightMargin := 60
- bottomMargin := 100
- qrSize := 160 // 二维码原始大小(像素)
- cornerRadius := 5.0 // 二维码圆角半径
- borderCornerRadius := 10.0
- borderSize := 10 // 二维码白色边框大小
- shadowBlur := 0.0 // 阴影模糊度
- shadowOpacity := 0.0 // 阴影透明度
- // 创建带效果的二维码并合成到背景右下角
- imgByte, err = createQRComposite(backgroundURL, qrContent, rightMargin, bottomMargin, qrSize, cornerRadius, borderCornerRadius, gconv.Float64(borderSize), shadowBlur, shadowOpacity)
- if err != nil {
- return err, nil
- }
- return nil, imgByte
- }
- // 创建二维码合成图片
- func createQRComposite(backgroundURL, qrContent string, rightMargin, bottomMargin, qrSize int, cornerRadius, borderCornerRadius, borderSize, shadowBlur, shadowOpacity float64) ([]byte, error) {
- // 1. 下载背景图片
- bgImg, imgType, err := downloadImage(backgroundURL)
- if err != nil {
- return nil, err
- }
- // 2. 生成带效果的二维码
- qrImg, err := generateStyledQRCode(qrContent, qrSize, color.Black, color.White, cornerRadius, borderCornerRadius, borderSize, shadowBlur, shadowOpacity)
- if err != nil {
- return nil, err
- }
- // 3. 计算二维码在右下角的位置
- bgBounds := bgImg.Bounds()
- qrBounds := qrImg.Bounds()
- pos := image.Point{
- X: bgBounds.Dx() - qrBounds.Dx() - rightMargin,
- Y: bgBounds.Dy() - qrBounds.Dy() - bottomMargin,
- }
- // 4. 调整二维码大小(如果需要)
- finalQR := qrImg
- if bgBounds.Dx() < 800 {
- // 背景图较小,缩小二维码
- targetWidth := bgBounds.Dx() / 4
- finalQR = resize.Resize(uint(targetWidth), 0, qrImg, resize.Lanczos3)
- // 重新计算位置
- pos = image.Point{
- X: bgBounds.Dx() - finalQR.Bounds().Dx() - rightMargin,
- Y: bgBounds.Dy() - finalQR.Bounds().Dy() - bottomMargin,
- }
- }
- // 5. 合成图片
- resultImg := image.NewRGBA(bgBounds)
- draw.Draw(resultImg, bgBounds, bgImg, image.Point{}, draw.Src)
- draw.Draw(resultImg, finalQR.Bounds().Add(pos), finalQR, image.Point{}, draw.Over)
- // 6. 保存结果
- //return saveImage(outputPath, resultImg, 95)
- imgByte, err := encodeImageToBytes(resultImg, imgType, 90)
- if err != nil {
- return nil, err
- }
- return imgByte, nil
- }
- // 下载图片
- func downloadImage(url string) (image.Image, string, error) {
- resp, err := http.Get(url)
- if err != nil {
- return nil, "", err
- }
- defer resp.Body.Close()
- img, imgType, err := image.Decode(resp.Body)
- if err != nil {
- return nil, "", err
- }
- return img, imgType, nil
- }
- func generateStyledQRCode(content string, size int, fgColor, bgColor color.Color, cornerRadius, borderCornerRadius, borderSize, shadowBlur, shadowOpacity float64) (image.Image, error) {
- // 生成基本二维码
- qr, err := qrcode.New(content, qrcode.Highest)
- if err != nil {
- return nil, err
- }
- qr.ForegroundColor = fgColor
- qr.BackgroundColor = bgColor
- qr.DisableBorder = true
- // 创建基本二维码图片
- baseImg := qr.Image(size)
- // 计算带边框的总大小
- borderWidth := int(math.Ceil(borderSize))
- totalSize := size + 2*borderWidth
- // 创建带透明背景的总画布
- dc := gg.NewContext(totalSize, totalSize)
- dc.SetColor(color.Transparent)
- dc.Clear()
- // 1. 绘制圆角白色边框
- dc.SetColor(color.White)
- dc.DrawRoundedRectangle(0, 0, float64(totalSize), float64(totalSize), borderCornerRadius+borderSize)
- dc.Fill()
- // 2. 创建二维码的圆角蒙版
- qrDC := gg.NewContext(size, size)
- qrDC.SetColor(color.Transparent)
- qrDC.Clear()
- if cornerRadius > 0 {
- qrDC.DrawRoundedRectangle(0, 0, float64(size), float64(size), cornerRadius)
- qrDC.Clip()
- }
- // 3. 绘制二维码到蒙版
- qrDC.DrawImage(baseImg, 0, 0)
- // 4. 将带蒙版的二维码绘制到总画布(居中)
- dc.DrawImage(qrDC.Image(), borderWidth, borderWidth)
- // 添加阴影效果
- if shadowBlur > 0 && shadowOpacity > 0 {
- shadowImg := addShadow(dc.Image(), shadowBlur, shadowOpacity)
- return shadowImg, nil
- }
- return dc.Image(), nil
- }
- // 添加阴影效果
- func addShadow(img image.Image, blur, opacity float64) image.Image {
- bounds := img.Bounds()
- dc := gg.NewContext(bounds.Dx()+int(blur*2), bounds.Dy()+int(blur*2))
- // 绘制阴影
- dc.SetRGBA(0, 0, 0, opacity)
- dc.DrawRectangle(blur, blur, float64(bounds.Dx()), float64(bounds.Dy()))
- dc.Fill()
- // 应用高斯模糊
- shadow := dc.Image()
- blurred := imaging.Blur(shadow, blur)
- // 创建新画布绘制二维码和阴影
- dc = gg.NewContext(bounds.Dx()+int(blur*2), bounds.Dy()+int(blur*2))
- dc.DrawImage(blurred, 0, 0)
- dc.DrawImage(img, int(blur), int(blur))
- return dc.Image()
- }
- // 编码图片为字节流
- func encodeImageToBytes(img image.Image, format string, quality int) ([]byte, error) {
- buf := new(bytes.Buffer)
- switch format {
- case "png":
- if err := png.Encode(buf, img); err != nil {
- return nil, err
- }
- case "jpg", "jpeg":
- if err := jpeg.Encode(buf, img, &jpeg.Options{Quality: quality}); err != nil {
- return nil, err
- }
- default:
- return nil, fmt.Errorf("不支持的图片格式: %s", format)
- }
- return buf.Bytes(), nil
- }
|