qq_bot/service/xibao/image_gen.go
2025-05-16 01:58:44 +08:00

198 lines
5.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package xibao
import (
"image"
"image/color"
"log"
"os"
"strings"
"git.lxtend.com/lixiangwuxian/imagedd/sprite"
"git.lxtend.com/lixiangwuxian/imagedd/text2img"
"git.lxtend.com/qqbot/message"
"git.lxtend.com/qqbot/util"
"github.com/fogleman/gg"
"golang.org/x/image/font/opentype"
)
func GenerateCongratulationImage(text string, inputFile, outputFile string, isGood bool) {
// 加载喜报背景图片
im, err := gg.LoadImage(inputFile) // 需要提前准备的背景图片
if err != nil {
log.Print("无法加载喜报图片:", err)
return
}
// 创建与背景图片大小相同的画布
width := im.Bounds().Dx()
height := im.Bounds().Dy()
dc := gg.NewContext(width, height)
// 将背景图片绘制到画布上
dc.DrawImage(im, 0, 0)
// 判断是否为图片
if imgUrl, ok := isImageCQ(text); ok {
filePath, err := util.DownloadFile(imgUrl, "/tmp/qqbot", false)
if err != nil {
log.Print("无法下载图片:", err)
return
}
imC, err := gg.LoadImage(filePath)
if err != nil {
log.Print("无法加载图片:", err)
return
}
widthC := imC.Bounds().Dx()
heightC := imC.Bounds().Dy()
dcC := gg.NewContext(widthC, heightC)
dcC.DrawImage(imC, 0, 0)
// 保存原始状态
dcC.Push()
scaleFactor := 1.0
// 根据高度比例进行缩放
if float64(heightC) > float64(height)/1.8 {
scaleFactor = (float64(height) / 1.8) / float64(heightC)
}
// 根据宽度比例进行缩放
if float64(widthC) > float64(width)/1.1 {
scaleFactor = (float64(width) / 1.1) / float64(widthC)
}
// 针对宽高较小的情况进行缩放
if heightC < height/5 && widthC < width/4 {
if float64(widthC)*(float64(height)/5)/float64(heightC) > float64(width/4) {
scaleFactor = (float64(height) / 5) / float64(heightC)
// log.Print(fmt.Sprintf("图片高度过小,已缩放至高度的 %f 倍", scaleFactor))
} else {
scaleFactor = (float64(width) / 4) / float64(widthC)
// log.Print(fmt.Sprintf("图片宽度过小,已缩放至宽度的 %f 倍", scaleFactor))
}
}
// 应用缩放比例
// 计算缩放后的图像尺寸
newWidth := int(float64(widthC) * scaleFactor)
newHeight := int(float64(heightC) * scaleFactor)
// 创建一个新的上下文,使用缩放后的尺寸
dcScaledC := gg.NewContext(newWidth, newHeight)
// 将原始图像缩放并绘制到新的上下文中
dcScaledC.Scale(scaleFactor, scaleFactor)
dcScaledC.DrawImage(imC, 0, 0)
// 在最终画布上绘制缩放后的图像
dc.DrawImageAnchored(dcScaledC.Image(), width/2, height/2, 0.5, 0.5)
dc.SavePNG(outputFile)
return
}
// 设置字体和大小,字体文件需要自备,放在合适的路径
// if err := dc.LoadFontFace("./resource/emoji.ttf", 96); err != nil {
// log.Print("无法加载字体:", err)
// return
// }
fontBytes, err := os.ReadFile("./resource/emoji.ttf")
if err != nil {
log.Printf("failed to load font: %v", err)
}
// 解析字体
font, err := opentype.Parse(fontBytes)
if err != nil {
log.Printf("failed to parse font: %v", err)
}
// 设置字体大小和 DPI
const fontSize = 48
const dpi = 72
// 创建 font.Face用于渲染文本
face, err := opentype.NewFace(font, &opentype.FaceOptions{
Size: fontSize,
DPI: dpi,
Hinting: 0,
})
if err != nil {
log.Printf("failed to create font face: %v", err)
return
}
dc.SetFontFace(face)
if err := dc.LoadFontFace("./resource/font.ttf", 96); err != nil {
log.Print("无法加载字体:", err)
return
}
if isGood {
// 设置文本颜色为红色
dc.SetRGB(0.8, 0, 0)
} else {
dc.SetRGB(0, 0, 0.8)
}
// 将文本按 \n 分割为多行
lines := strings.Split(text, "\n")
// 设置初始绘制的 y 位置,可以根据需要调整
startY := float64(height) / 2
lineHeight := 120.0 // 行高,可以根据字体大小调整
// 居中绘制每一行
for i, line := range lines {
i = i - len(lines)/2
x := float64(width) / 2
y := startY + float64(i)*lineHeight
dc.DrawStringAnchored(line, x, y, 0.5, 0.5)
}
// 将生成的图片保存为输出文件
err = dc.SavePNG(outputFile)
if err != nil {
log.Print("无法保存生成的图片:", err)
}
}
func GenerateCongratulationImageNew(text string, inputFile, outputFile string, isGood bool) {
baseboard := sprite.NewNamedSpriteBoard()
backgroundImageSprite, err := sprite.LoadSpriteFromFile("bg", inputFile)
backgroundImageSprite.Index = 0
if err != nil {
log.Print("无法加载背景图片:", err)
return
}
baseboard.AddSprite(backgroundImageSprite)
var textColor color.RGBA
if isGood {
// 设置文本颜色为红色
textColor = color.RGBA{R: 255 * 0.8, G: 0, B: 0, A: 255}
} else {
textColor = color.RGBA{R: 0, G: 0, B: 255 * 0.8, A: 255}
}
textImage, err := text2img.RenderTextToTrimmedImage(nil, text, 96, textColor, 0, 0)
if err != nil {
log.Print("无法渲染文本:", err)
return
}
textSprite := sprite.Sprite{
Name: "text",
Images: []image.Image{textImage},
Position: image.Point{X: backgroundImageSprite.GetCurrentImage().Bounds().Dx()/2 - textImage.Bounds().Dx()/2, Y: backgroundImageSprite.GetCurrentImage().Bounds().Dy()/2 - textImage.Bounds().Dy()/2},
Index: 1,
}
baseboard.AddSprite(&textSprite)
baseboard.SaveToApng(outputFile)
}
func isImageCQ(text string) (string, bool) {
imgMsg := message.ImageMessage{}
if err := imgMsg.ParseMessage(text); err == nil {
return imgMsg.Data.URL, true
}
return "", false
}