面向猴子编程 GO制作水印

参考网上的文章写了一个制作水印的包,支持文字水印以及图片水印,喜欢的点个赞~~

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
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值