参考网上的文章写了一个制作水印的包,支持文字水印以及图片水印,喜欢的点个赞~~
package watermark
import (
"github.com/golang/freetype"
"go.uber.org/zap/buffer"
"image"
"image/color"
"image/draw"
"image/jpeg"
"image/png"
"io/ioutil"
"os"
)
const (
TopLeft int = iota
TopRight
BottomLeft
BottomRight
Center
)
var (
FontColorBlack = RGBA{R: 0, G: 0, B: 0, A: 255}
FontColorRed = RGBA{R: 255, G: 0, B: 0, A: 255}
FontColorGreen = RGBA{R: 0, G: 255, B: 0, A: 255}
FontColorBlue = RGBA{R: 0, G: 0, B: 255, A: 255}
FontColorWhite = RGBA{R: 255, G: 255, B: 255, A: 255}
)
type Water struct{}
type FontInfo struct {
Size float64 //文字大小
Text string //文字内容
Position int //文字存放位置
Dx int //文字x轴留白距离
Dy int //文字y轴留白距离
RGBA
}
type RGBA struct {
R uint8
G uint8
B uint8
A uint8
}
// Demo 根据传入的信息绘制文字水印(例子)
func (w *Water) Demo(savePath, filePath, ttfPath string, fonts []FontInfo, photos []PhotoInfo) ([]byte, error) {
img, imgType, err := w.New(filePath)
if err != nil {
return nil, err
}
img, err = w.BatchFontWatermark(img, fonts, ttfPath) //添加文字水印
if err != nil {
return nil, err
}
img, err = w.BatchPhotoWatermark(img, photos)
if err != nil {
return nil, err
}
err = w.Create(img, savePath, imgType)
b, err := w.GetBuffer(img)
if err != nil {
return nil, err
}
return b, err
}
// MakeFonts 创建需要打水印的文字结构体切片
func (w *Water) MakeFonts(infos ...FontInfo) []FontInfo {
fonts := make([]FontInfo, 0)
for _, info := range infos {
fonts = append(fonts, info)
}
return fonts
}
// MakePhotos 创建需要打水印的图片结构体切片
func (w *Water) MakePhotos(infos ...PhotoInfo) []PhotoInfo {
photos := make([]PhotoInfo, 0)
for _, info := range infos {
photos = append(photos, info)
}
return photos
}
// New 创建一个需要打水印的对象
func (w *Water) New(filePath string) (*image.NRGBA, string, error) {
imgFile, err := os.Open(filePath)
if err != nil {
return nil, "", err
}
defer imgFile.Close()
_, imgType, err := image.DecodeConfig(imgFile)
if err != nil {
return nil, "", err
}
imgFile, err = os.Open(filePath)
if err != nil {
return nil, "", err
}
defer imgFile.Close()
// 根据文件类型生成对应的对象
var staticImg image.Image
if imgType == "png" {
staticImg, _ = png.Decode(imgFile)
} else {
staticImg, _ = jpeg.Decode(imgFile)
}
img := image.NewNRGBA(staticImg.Bounds())
for y := 0; y < img.Bounds().Dy(); y++ {
for x := 0; x < img.Bounds().Dx(); x++ {
img.Set(x, y, staticImg.At(x, y))
}
}
return img, imgType, nil
}
// Create 生成新的图片文件
func (w *Water) Create(img *image.NRGBA, savePath, imgType string) error {
//保存到新文件中
newfile, err := os.Create(savePath)
if err != nil {
return err
}
defer newfile.Close()
if imgType == "png" {
err = png.Encode(newfile, img)
} else {
err = jpeg.Encode(newfile, img, &jpeg.Options{100})
}
return nil
}
// GetBuffer 获取图片的byte
func (w *Water) GetBuffer(img *image.NRGBA) ([]byte, error) {
var b buffer.Buffer
err := png.Encode(&b, img)
if err != nil {
return nil, err
}
return b.Bytes(), nil
}
// BatchFontWatermark 批量追加文字水印(同一字体)
func (w *Water) BatchFontWatermark(img *image.NRGBA, fonts []FontInfo, ttfPath string) (*image.NRGBA, error) {
if len(fonts) == 0 {
return img, nil
}
//拷贝一个字体文件到运行目录
fontBytes, err := ioutil.ReadFile(ttfPath)
if err != nil {
return img, err
}
font, err := freetype.ParseFont(fontBytes)
if err != nil {
return img, err
}
for _, t := range fonts {
info := t.Text
f := freetype.NewContext()
f.SetDPI(108)
f.SetFont(font)
f.SetFontSize(t.Size)
f.SetClip(img.Bounds())
f.SetDst(img)
f.SetSrc(image.NewUniform(color.RGBA{R: t.R, G: t.G, B: t.B, A: t.A}))
x := 0
y := 0
switch int(t.Position) {
case 0:
x = t.Dx
y = t.Dy + int(f.PointToFixed(t.Size)>>6)
case 1:
x = img.Bounds().Dx() - len(info)*4 - t.Dx
y = t.Dy + int(f.PointToFixed(t.Size)>>6)
case 2:
x = t.Dx
y = img.Bounds().Dy() - t.Dy
case 3:
x = img.Bounds().Dx() - len(info)*4 - t.Dx
y = img.Bounds().Dy() - t.Dy
case 4:
x = (img.Bounds().Dx() - len(info)*4) / 2
y = (img.Bounds().Dy() - t.Dy) / 2
default:
x = t.Dx
y = t.Dy + int(f.PointToFixed(t.Size)>>6)
}
pt := freetype.Pt(x, y)
_, err = f.DrawString(info, pt)
if err != nil {
return img, err
}
}
return img, err
}
type PhotoInfo struct {
Path string //图片路径
Dx int //图片位置
Dy int //图片位置
}
// BatchPhotoWatermark 根据传入的信息绘制图片水印
func (w *Water) BatchPhotoWatermark(img *image.NRGBA, photos []PhotoInfo) (*image.NRGBA, error) {
for _, photo := range photos {
watermark, err := os.Open(photo.Path)
if err != nil {
return nil, err
}
defer watermark.Close()
_, imgType, err := image.DecodeConfig(watermark)
if err != nil {
return nil, err
}
watermark, err = os.Open(photo.Path)
if err != nil {
return nil, err
}
defer watermark.Close()
var imgwatermark image.Image
switch imgType {
case "png":
imgwatermark, err = png.Decode(watermark)
case "jpeg":
imgwatermark, err = jpeg.Decode(watermark)
default:
return nil, err
}
if imgwatermark == nil {
return nil, err
}
offset := image.Pt(photo.Dx, photo.Dy)
draw.Draw(img, imgwatermark.Bounds().Add(offset), imgwatermark, image.ZP, draw.Over)
}
return img, nil
}
单元测试代码~~~
package watermark
import (
"fmt"
"testing"
)
func TestWater_Draw(t *testing.T) {
var (
savePath = "./image/test.png" // 打完水印后保存的目录
filePath = "./image/base.png" // 基础图片的位置
ttfPath = "./font/NotoSansSC-Regular.ttf" //存放字体TTL的位置
)
w := new(Water)
fonts := w.MakeFonts(
FontInfo{
Size: 12,
Text: "heiheihei",
Position: TopLeft,
Dx: 104,
Dy: 101,
RGBA: FontColorBlack,
},
FontInfo{
Size: 12,
Text: "lalalala",
Position: TopLeft,
Dx: 104,
Dy: 200,
RGBA: FontColorBlack,
},
)
photos := w.MakePhotos(
PhotoInfo{
Path: "./image/dog.jpg", // 水印图片
Dx: 132,
Dy: 510,
},
)
b, err := w.Demo(savePath, filePath, ttfPath, fonts, photos)
if err != nil {
fmt.Println(err)
}
t.Log("图片的buffer长度为", len(b))
return
}