Golang实现为图片添加水印和文字的示例代码

当然!在 Golang 中为图片添加水印和文字是一个非常常见的需求。下面我将提供一个完整的、可直接运行的示例代码,并详细解释其中的关键步骤。

图片[1]_Golang实现为图片添加水印和文字的示例代码_知途无界

我们将使用 Go 标准库中的 image 包来处理图片,以及 draw 包来合成图像,font 包来处理文字。

核心思路

  1. 打开并解码原始图片​:读取作为底图的图片文件(如 JPEG、PNG)。
  2. 创建绘图上下文​:准备一个可以在原始图片上进行绘制的画布。
  3. 加载字体​:为了渲染清晰的中文文字,我们需要加载一个 TTF 字体文件。这是关键一步,否则中文会显示为乱码或方框。
  4. 绘制文字水印​:设置文字颜色、大小、位置和对齐方式,然后在画布上绘制文字。
  5. 打开并解码水印图片​:读取作为水印的图片(通常是 PNG 格式,支持透明背景)。
  6. 绘制图片水印​:计算水印图片的位置(例如右下角),并将其绘制到原始图片上。
  7. 编码并保存新图片​:将绘制好的最终图像编码为指定格式(如 JPEG 或 PNG)并保存到文件。

示例代码

首先,确保你的项目目录结构如下,并下载一个中文字体文件(如思源黑体、阿里巴巴普惠体等)。

your-project/
├── go.mod
├── main.go
└── fonts/
    └── SourceHanSansCN-Regular.ttf  # 你需要自己下载这个字体文件
└── images/
    ├── background.jpg              # 你的原始图片
    └── watermark.png               # 你的水印图片 (最好是带透明通道的PNG)

以下是 main.go 的完整代码:

package main

import (
	"flag"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"image/jpeg"
	"image/png"
	"io/ioutil"
	"log"
	"os"

	"golang.org/x/image/font"
	"golang.org/x/image/font/opentype"
	"golang.org/x/image/math/fixed"
)

// 命令行参数
var (
	bgImagePath    = flag.String("bg", "images/background.jpg", "背景图片路径")
	textWatermark  = flag.String("text", "机密文件 请勿外传", "文字水印内容")
	watermarkImage = flag.String("logo", "images/watermark.png", "Logo水印图片路径")
	outputPath     = flag.String("output", "output.jpg", "输出图片路径")
	fontPath       = flag.String("font", "fonts/SourceHanSansCN-Regular.ttf", "TTF字体文件路径")
)

func main() {
	flag.Parse()

	// 1. 打开并解码背景图片
	bgFile, err := os.Open(*bgImagePath)
	if err != nil {
		log.Fatalf("打开背景图片失败: %v", err)
	}
	defer bgFile.Close()

	bgImg, _, err := image.Decode(bgFile)
	if err != nil {
		log.Fatalf("解码背景图片失败: %v", err)
	}

	// 2. 创建一个与背景图同样大小的RGBA图像作为画布
	// RGBA 支持透明度,非常适合用于合成
	rgba := image.NewRGBA(bgImg.Bounds())
	draw.Draw(rgba, rgba.Bounds(), bgImg, image.Point{}, draw.Src)

	// 3. 加载字体
	fontBytes, err := ioutil.ReadFile(*fontPath)
	if err != nil {
		log.Fatalf("读取字体文件失败: %v", err)
	}
	
	// 解析TTF字体文件
	ft, err := opentype.Parse(fontBytes)
	if err != nil {
		log.Fatalf("解析字体文件失败: %v", err)
	}

	// 根据字体大小创建字体face
	const fontSize = 40
	face, err := opentype.NewFace(ft, &opentype.FaceOptions{
		Size:    fontSize,
		DPI:     72,
		Hinting: font.HintingFull,
	})
	if err != nil {
		log.Fatalf("创建字体失败: %v", err)
	}

	// 4. 在画布上绘制文字水印
	drawer := &font.Drawer{
		Dst:  rgba,
		Src:  image.NewUniform(color.RGBA{R: 255, G: 0, B: 0, A: 128}), // 半透明的红色
		Face: face,
		Dot:  fixed.P(50, 50+fontSize), // 起始点坐标 (X, Y)。Y坐标是基线位置,所以要加上字体大小。
	}
	// 设置要绘制的文字
	drawer.Dot.X = fixed.I((rgba.Bounds().Dx() - drawer.MeasureString(*textWatermark).Round()) / 2) // 水平居中
	drawer.DrawString(*textWatermark)

	// 5. 打开并解码Logo水印图片
	logoFile, err := os.Open(*watermarkImage)
	if err != nil {
		log.Fatalf("打开Logo水印图片失败: %v", err)
	}
	defer logoFile.Close()

	logoImg, _, err := image.Decode(logoFile)
	if err != nil {
		log.Fatalf("解码Logo水印图片失败: %v", err)
	}

	// 6. 在画布上绘制Logo水印 (例如,放在右下角)
	logoBounds := logoImg.Bounds()
	logoWidth := logoBounds.Dx()
	logoHeight := logoBounds.Dy()

	// 计算放置位置 (距离右边缘和下边缘各20像素)
	position := image.Point{
		X: rgba.Bounds().Dx() - logoWidth - 20,
		Y: rgba.Bounds().Dy() - logoHeight - 20,
	}

	// 使用 draw.Over 模式,使Logo的透明通道生效
	draw.Draw(rgba, image.Rectangle{Min: position, Max: position.Add(logoImg.Bounds().Size())}, logoImg, logoImg.Bounds().Min, draw.Over)

	// 7. 创建输出文件并编码为新图片
	outputFile, err := os.Create(*outputPath)
	if err != nil {
		log.Fatalf("创建输出文件失败: %v", err)
	}
	defer outputFile.Close()

	// 根据输出文件扩展名决定编码器
	switch *outputPath {
	case "output.png":
		err = png.Encode(outputFile, rgba)
	default: // 默认为 jpeg
		// 将RGBA转换为NRGBA以满足JPEG编码器的要求,并设置质量
		jpegImg := image.NewNRGBA(rgba.Bounds())
		draw.Draw(jpegImg, jpegImg.Bounds(), rgba, image.Point{}, draw.Src)
		err = jpeg.Encode(outputFile, jpegImg, &jpeg.Options{Quality: 90})
	}

	if err != nil {
		log.Fatalf("编码输出图片失败: %v", err)
	}

	fmt.Printf("成功生成带水印的图片: %s\n", *outputPath)
}

如何运行

  1. 初始化 Go Module: go mod init image-watermark-example
  2. 获取依赖:
    代码中使用了 golang.org/x/image 下的非标准库,需要手动获取。 go get golang.org/x/image/font go get golang.org/x/image/font/opentype go get golang.org/x/image/math/fixed go get golang.org/x/image/draw # 通常draw在image包内,但为了清晰列出 注意:由于网络原因,直接获取可能会失败。如果失败,请设置 GOPROXY: go env -w GOPROXY=https://goproxy.cn,direct go get ...
  3. 准备文件:
    • 将你的背景图片放到 images/background.jpg
    • 将你的Logo水印(最好是透明PNG)放到 images/watermark.png
    • 下载一个中文字体(如 思源黑体),解压后将 .ttf 文件(如 SourceHanSansCN-Regular.ttf)放到 fonts/ 目录下。
  4. 运行程序: go run main.go 或者使用自定义参数: go run main.go -bg my_bg.png -text "我的水印" -logo my_logo.png -output result.png -font fonts/my_font.ttf

运行成功后,你会在项目根目录下看到生成的 output.jpg 文件,上面已经添加了居中的红色半透明文字水印和右下的Logo水印。

关键点解释

  • ​**image.RGBA**: 我们创建了一个 RGBA 图像作为绘制画布,因为它支持 Alpha 通道(透明度),这对于叠加水印而不完全遮盖原图至关重要。
  • ​**golang.org/x/image/font**: 这是处理字体的关键。标准库的 image 包不支持直接渲染文字,必须借助这个扩展库。
  • 字体加载: opentype.Parseopentype.NewFace 是从 TTF 文件创建可用字体对象的必经之路。​没有这一步,任何非ASCII字符(如中文)都无法正确渲染。​
  • ​**font.Drawer**: 这是一个强大的工具,用于控制文字的绘制。MeasureString 方法可以测量文字的宽度,从而实现居中或右对齐等效果。Dot 属性设置了文字绘制的起始点。
  • ​**draw.Drawdraw.Over**: draw.Draw 是基础绘制函数。draw.Over 操作符会考虑源图像和目标图像的Alpha值,实现正确的“正片叠底”混合效果,对于透明Logo水印是必须的。而 draw.Src 则会直接覆盖目标区域的像素。
  • JPEG 与 PNG: JPEG 不支持透明度,所以在编码为 JPEG 前,我们从 RGBA 转换到了 NRGBA。jpeg.Options 可以设置压缩质量。PNG 则完美支持透明度。
© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞60 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容