GO使用image/draw库给图片添加水印完全解析
一、前言
图片水印是很实用的功能,可以标注图片的来源,防止第三方盗图。
GO实现图片水印功能主要使用绘图库image/draw
。
二、image/draw
主要的方法有
func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op)
- 1
func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op)
- 1
三、image/jpeg
和image/png
image/jpeg
和image/png
的作用是图片的读取和写入。
读取图片
func Decode(r io.Reader) (image.Image, error)
- 1
写入图片
func Encode(w io.Writer, m image.Image, o *Options) error
- 1
两个库的方法名都是一致的。
四、思路分析
和之前PHP写的水印思路没什么区别
- 读取原始图片
- 读取水印图片或者获取水印文字
- 创建新图片,写入原始图片和水印图片或者文字
- 保存图片到文件
五、代码实装
1.读取图片
比如我的原始图片是这个猫猫图
那么作为输入,我应该获取图片,并且判断图片的文件类型,由于我会事先声明只支持jpg和png的图,那么在判断图片不属于jpg或png之后应该返回错误信息。
package main
import (
"os"
imgtype "codechina.csdn.net/diandianxiyu/goimgtype"
)
func main() {
var imgdiandianxiyu_geek string = `./bg.jpg`
imgb, err := os.Open(imgdiandianxiyu_geek)
if err != nil {
panic(err)
}
defer imgb.Close()
datatype, err2 := imgtype.Get(imgdiandianxiyu_geek)
var imgtype string = ""
if err2 != nil {
imgtype = ""
} else {
// 根据文件类型执行响应的操作
switch datatype {
case `image/jpeg`:
imgtype = "jpeg"
case `image/png`:
imgtype = "png"
}
}
if imgtype == "" {
panic("暂不支持文件类型")
}
print(imgtype)
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
jpeg
- 1
由于go不支持原生的获取图片类型方式,所以这个引入了第三方库。
2.根据图片类型使用不同类
如果是jpeg的图片,就在对象初始化的时候用image/jpeg
。
如果是png的图片,就在对象初始化的时候用image/png
var imgbinfo image.Image
if imgtype == "jpeg" {
imgbinfo, _ = jpeg.Decode(imgb)
} else {
imgbinfo, _ = png.Decode(imgb)
}
- 1
- 2
- 3
- 4
- 5
- 6
3.添加图片水印
因为图片水印的图片是已知的,所以我们不需要判断图片类型了
//读取水印图片
watermark, err := os.Open("watermark.png")
if err != nil {
panic(err)
}
defer watermark.Close()
imgwatermark, err := png.Decode(watermark)
if err != nil {
panic(err)
}
offset := image.Pt(imgbinfo.Bounds().Dx()-imgwatermark.Bounds().Dx()-19, imgbinfo.Bounds().Dy()-imgwatermark.Bounds().Dy()-19)
b := imgbinfo.Bounds()
m := image.NewNRGBA(b) //按原图生成新图
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
这里要注意的是,imgbinfo.Bounds().Dx()
指的是原图的宽度,imgbinfo.Bounds().Dy()
指的是原图的高度。
代码的意思是水印图放在右下角19像素的位置上,实际应用中可以根据需求调整。
4.添加文字水印
文字水印必须引入字体文件,如果水印中有中文的话,引入的字体文件必需支持中文。
//文字水印
fontBytes, err := ioutil.ReadFile("Arial Unicode.ttf") //读取字体文件
if err != nil {
log.Println(err)
}
font, err := freetype.ParseFont(fontBytes)
if err != nil {
log.Println(err)
}
f := freetype.NewContext()
f.SetDPI(72) //设置DPI
f.SetFont(font) //设置字体
f.SetFontSize(24) //设置字号
f.SetClip(imgbinfo.Bounds())
f.SetDst(m)
f.SetSrc(image.NewUniform(color.RGBA{R: 255, G: 0, B: 0, A: 255})) //设置颜色
pt := freetype.Pt(10, 50)
_, err = f.DrawString("blog.csdn.net/diandianxiyu_geek 邻座的小雨同学", pt)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
pt
指的是坐标,我设置的是横坐标10和纵坐标的位置。
5. 水印效果
备注:右下角的白色的文字是CSDN的水印,不是代码里的。
6. 完整代码
package main
import (
"image"
"image/color"
"image/draw"
"image/jpeg"
"image/png"
"io/ioutil"
"log"
"os"
"codechina.csdn.net/diandianxiyu/freetype"
imgtype "codechina.csdn.net/diandianxiyu/goimgtype"
)
func main() {
var imgdiandianxiyu_geek string = `./bg.jpg`
imgb, err := os.Open(imgdiandianxiyu_geek)
if err != nil {
panic(err)
}
defer imgb.Close()
datatype, err2 := imgtype.Get(imgdiandianxiyu_geek)
var imgtype string = ""
if err2 != nil {
imgtype = ""
} else {
// 根据文件类型执行响应的操作
switch datatype {
case `image/jpeg`:
imgtype = "jpeg"
case `image/png`:
imgtype = "png"
}
}
if imgtype == "" {
panic("暂不支持文件类型")
}
var imgbinfo image.Image
if imgtype == "jpeg" {
imgbinfo, _ = jpeg.Decode(imgb)
} else {
imgbinfo, _ = png.Decode(imgb)
}
//读取水印图片
watermark, err := os.Open("watermark.png")
if err != nil {
panic(err)
}
defer watermark.Close()
imgwatermark, err := png.Decode(watermark)
if err != nil {
panic(err)
}
offset := image.Pt(imgbinfo.Bounds().Dx()-imgwatermark.Bounds().Dx()-19, imgbinfo.Bounds().Dy()-imgwatermark.Bounds().Dy()-19)
b := imgbinfo.Bounds()
m := image.NewNRGBA(b) //按原图生成新图
//文字水印
fontBytes, err := ioutil.ReadFile("Arial Unicode.ttf") //读取字体文件
if err != nil {
log.Println(err)
}
font, err := freetype.ParseFont(fontBytes)
if err != nil {
log.Println(err)
}
f := freetype.NewContext()
f.SetDPI(72) //设置DPI
f.SetFont(font) //设置字体
f.SetFontSize(24) //设置字号
f.SetClip(imgbinfo.Bounds())
f.SetDst(m)
f.SetSrc(image.NewUniform(color.RGBA{R: 255, G: 0, B: 0, A: 255})) //设置颜色
//新图写入原图和背景图
draw.Draw(m, b, imgbinfo, image.ZP, draw.Src)
draw.Draw(m, imgwatermark.Bounds().Add(offset), imgwatermark, image.ZP, draw.Over)
pt := freetype.Pt(10, 50)
_, err = f.DrawString("blog.csdn.net/diandianxiyu_geek 邻座的小雨同学", pt)
//输出图像
imgw, _ := os.Create("new.jpg")
jpeg.Encode(imgw, m, &jpeg.Options{100})
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
本地引入库
go get codechina.csdn.net/diandianxiyu/freetype
go get codechina.csdn.net/diandianxiyu/goimgtype
- 1
- 2
- 3
六、总结
1. 需要熟悉的库
image/draw
image/jpeg
和image/png
2.处理思路
- 找到合适的库
- 通过文档确认合适的方法
- 完善代码健壮性
七、评论区作业
这个水印工具还有哪些可以优化的点?
欢迎给出你的思路!
文章来源: coderfix.blog.csdn.net,作者:小雨青年,版权归原作者所有,如需转载,请联系作者。
原文链接:coderfix.blog.csdn.net/article/details/119382334
- 点赞
- 收藏
- 关注作者
评论(0)