GO使用image/draw库给图片添加水印完全解析

举报
小雨青年 发表于 2022/03/28 22:31:28 2022/03/28
【摘要】 目录 一、前言二、`image/draw`三、`image/jpeg`和`image/png`四、思路分析五、代码实装1.读取图片2.根据图片类型使用不同类3.添加图片水印4.添加文字水印5. 水...

一、前言

图片水印是很实用的功能,可以标注图片的来源,防止第三方盗图。

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/jpegimage/png

image/jpegimage/png的作用是图片的读取和写入。

读取图片

func Decode(r io.Reader) (image.Image, error)

  
 
  • 1

写入图片

func Encode(w io.Writer, m image.Image, o *Options) error

  
 
  • 1

两个库的方法名都是一致的。

四、思路分析

和之前PHP写的水印思路没什么区别

  1. 读取原始图片
  2. 读取水印图片或者获取水印文字
  3. 创建新图片,写入原始图片和水印图片或者文字
  4. 保存图片到文件

五、代码实装

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/jpegimage/png

2.处理思路

  • 找到合适的库
  • 通过文档确认合适的方法
  • 完善代码健壮性

七、评论区作业

这个水印工具还有哪些可以优化的点?

欢迎给出你的思路!

文章来源: coderfix.blog.csdn.net,作者:小雨青年,版权归原作者所有,如需转载,请联系作者。

原文链接:coderfix.blog.csdn.net/article/details/119382334

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。