Go image/draw 包详解
概述
image/draw 包提供图像绘制功能,支持将一个图像绘制到另一个图像上。它提供了 Draw、DrawMask 等核心函数,以及 Drawer 接口和 FloydSteinberg 颜色量化器。该包广泛用于图像合成、颜色量化和图像处理场景。
包导入
import "image/draw"
基本使用
1. 简单的图像绘制
package main
import (
"image"
"image/color"
"image/draw"
)
func main() {
// 创建目标图像
dst := image.NewRGBA(image.Rect(0, 0, 200, 200))
// 创建源图像
src := image.NewRGBA(image.Rect(0, 0, 100, 100))
// 填充源图像为红色
draw.Draw(src, src.Bounds(), &image.Uniform{color.RGBA{255, 0, 0, 255}}, image.Point{}, draw.Src)
// 将源图像绘制到目标图像
draw.Draw(dst, image.Rect(0, 0, 100, 100), src, image.Point{}, draw.Src)
}
2. 使用 DrawMask 进行蒙版绘制
package main
import (
"image"
"image/color"
"image/draw"
)
func main() {
dst := image.NewRGBA(image.Rect(0, 0, 200, 200))
src := image.NewRGBA(image.Rect(0, 0, 100, 100))
mask := image.NewRGBA(image.Rect(0, 0, 100, 100))
// 绘制时使用蒙版
draw.DrawMask(dst, image.Rect(0, 0, 100, 100), src, image.Point{}, mask, image.Point{}, draw.Over)
}
一、核心函数
Draw
定义:
func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op)
说明:
- 功能:将源图像
src的一部分绘制到目标图像dst上 - 参数:
dst- 目标图像(必须是*image.RGBA、*image.NRGBA等可写图像)r- 目标图像上的绘制区域矩形src- 源图像sp- 源图像上的起始点(通常设置为image.Point{0, 0})op- 操作类型(draw.Src或draw.Over)
- 绘制规则:对于
r中的每个点(dx, dy),从src的点(sp.X + dx - r.Min.X, sp.Y + dy - r.Min.Y)复制颜色
示例:
package main
import (
"image"
"image/color"
"image/draw"
"image/png"
"os"
)
func main() {
// 创建目标图像(白色背景)
dst := image.NewRGBA(image.Rect(0, 0, 200, 200))
draw.Draw(dst, dst.Bounds(), &image.Uniform{color.RGBA{255, 255, 255, 255}}, image.Point{}, draw.Src)
// 创建源图像(红色方块)
src := image.NewRGBA(image.Rect(0, 0, 100, 100))
draw.Draw(src, src.Bounds(), &image.Uniform{color.RGBA{255, 0, 0, 255}}, image.Point{}, draw.Src)
// 将源图像绘制到目标图像左上角
draw.Draw(dst, image.Rect(0, 0, 100, 100), src, image.Point{}, draw.Src)
// 保存结果
file, _ := os.Create("draw_example.png")
defer file.Close()
png.Encode(file, dst)
}
DrawMask
定义:
func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op)
说明:
- 功能:使用蒙版将源图像绘制到目标图像上
- 参数:
dst- 目标图像r- 目标图像上的绘制区域矩形src- 源图像sp- 源图像上的起始点mask- 蒙版图像(Alpha 通道控制透明度)mp- 蒙版图像上的起始点op- 操作类型
- 工作原理:蒙版的 Alpha 值决定源像素与目标像素的混合比例
- Alpha = 255:完全使用源像素
- Alpha = 0:完全保留目标像素
- Alpha = 128:源像素和目标像素各占 50%
示例:
package main
import (
"image"
"image/color"
"image/draw"
"image/png"
"os"
)
func main() {
// 创建目标图像
dst := image.NewRGBA(image.Rect(0, 0, 200, 200))
draw.Draw(dst, dst.Bounds(), &image.Uniform{color.RGBA{255, 255, 255, 255}}, image.Point{}, draw.Src)
// 创建源图像(蓝色圆形区域)
src := image.NewRGBA(image.Rect(0, 0, 100, 100))
draw.Draw(src, src.Bounds(), &image.Uniform{color.RGBA{0, 0, 255, 255}}, image.Point{}, draw.Src)
// 创建圆形蒙版
mask := image.NewRGBA(image.Rect(0, 0, 100, 100))
for y := 0; y < 100; y++ {
for x := 0; x < 100; x++ {
dx := x - 50
dy := y - 50
if dx*dx+dy*dy <= 50*50 {
mask.Set(x, y, color.RGBA{0, 0, 0, 255}) // 圆形区域内完全不透明
} else {
mask.Set(x, y, color.RGBA{0, 0, 0, 0}) // 圆形区域外完全透明
}
}
}
// 使用蒙版绘制(只显示圆形区域)
draw.DrawMask(dst, image.Rect(50, 50, 150, 150), src, image.Point{}, mask, image.Point{}, draw.Over)
// 保存结果
file, _ := os.Create("drawmask_example.png")
defer file.Close()
png.Encode(file, dst)
}
FowlerNollVo
定义:
func FowlerNollVo(b []byte) uint32
说明:
- 功能:计算字节切片的 Fowler-Noll-Vo 哈希值
- 参数:
b- 要哈希的字节切片 - 返回值:32 位 FNV 哈希值
- 用途:主要用于内部实现,用户通常不需要直接调用
二、接口
Drawer
定义:
type Drawer interface {
Draw(dst Image, dr image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op)
}
说明:
- 功能:定义图像绘制操作的接口
- 参数:
dst- 目标图像dr- 目标矩形src- 源图像sp- 源点mask- 蒙版图像(可为 nil)mp- 蒙版点op- 操作类型
- 实现:
Drawer接口允许自定义绘制逻辑
示例 - 自定义 Drawer:
package main
import (
"image"
"image/draw"
)
// 自定义 Drawer,实现特殊效果
type InvertDrawer struct{}
func (InvertDrawer) Draw(dst draw.Image, dr image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op draw.Op) {
// 自定义绘制逻辑:反转颜色
for y := dr.Min.Y; y < dr.Max.Y; y++ {
for x := dr.Min.X; x < dr.Max.X; x++ {
c := src.At(sp.X+x-dr.Min.X, sp.Y+y-dr.Min.Y)
r, g, b, a := c.RGBA()
dst.Set(x, y, color.RGBA{
R: uint8(255 - r>>8),
G: uint8(255 - g>>8),
B: uint8(255 - b>>8),
A: uint8(a >> 8),
})
}
}
}
Image
定义:
type Image interface {
image.Image
Set(x, y int, c color.Color)
}
说明:
- 功能:表示可写的图像接口
- 嵌入:嵌入
image.Image接口的所有方法 - 新增方法:
Set(x, y int, c color.Color)用于设置像素颜色 - 实现类型:
*image.RGBA*image.NRGBA*image.Alpha*image.Gray*image.Paletted*image.CMYK
示例:
package main
import (
"image"
"image/color"
"image/draw"
)
func main() {
// 所有这些都实现了 draw.Image 接口
var dst1 draw.Image = image.NewRGBA(image.Rect(0, 0, 100, 100))
var dst2 draw.Image = image.NewNRGBA(image.Rect(0, 0, 100, 100))
var dst3 draw.Image = image.NewAlpha(image.Rect(0, 0, 100, 100))
var dst4 draw.Image = image.NewGray(image.Rect(0, 0, 100, 100))
var dst5 draw.Image = image.NewPaletted(image.Rect(0, 0, 100, 100), palette.WebSafe)
var dst6 draw.Image = image.NewCMYK(image.Rect(0, 0, 100, 100))
// 使用 Set 方法设置像素
dst1.Set(50, 50, color.RGBA{255, 0, 0, 255})
}
三、类型
Op
定义:
type Op int8
说明:
- 功能:定义绘制操作类型
- 取值:
Src:源覆盖目标(不考虑透明度)Over:源在目标之上绘制(考虑透明度,alpha 混合)
常量:
const (
Src Op = iota // 源覆盖目标
Over // 源在目标之上(alpha 混合)
)
详细对比:
| 操作 | 公式 | 效果 | 使用场景 |
|---|---|---|---|
Src | dst = src | 源像素直接替换目标像素 | 不透明图像、完全覆盖 |
Over | dst = src * alpha + dst * (1 - alpha) | 源像素与目标像素 alpha 混合 | 半透明图像、叠加效果 |
示例对比:
package main
import (
"image"
"image/color"
"image/draw"
"image/png"
"os"
)
func main() {
// 创建目标图像(蓝色背景)
dst1 := image.NewRGBA(image.Rect(0, 0, 200, 100))
draw.Draw(dst1, dst1.Bounds(), &image.Uniform{color.RGBA{0, 0, 255, 255}}, image.Point{}, draw.Src)
// 创建目标图像(蓝色背景)
dst2 := image.NewRGBA(image.Rect(0, 0, 200, 100))
draw.Draw(dst2, dst2.Bounds(), &image.Uniform{color.RGBA{0, 0, 255, 255}}, image.Point{}, draw.Src)
// 创建半透明红色源图像
src := image.NewRGBA(image.Rect(0, 0, 100, 100))
draw.Draw(src, src.Bounds(), &image.Uniform{color.RGBA{255, 0, 0, 128}}, image.Point{}, draw.Src)
// 使用 Src 操作(左侧)
draw.Draw(dst1, image.Rect(0, 0, 100, 100), src, image.Point{}, draw.Src)
// 使用 Over 操作(右侧)
draw.Draw(dst2, image.Rect(0, 0, 100, 100), src, image.Point{}, draw.Over)
// 保存对比结果
file1, _ := os.Create("draw_src.png")
defer file1.Close()
png.Encode(file1, dst1)
file2, _ := os.Create("draw_over.png")
defer file2.Close()
png.Encode(file2, dst2)
}
四、变量
FloydSteinberg
定义:
var FloydSteinberg Palettizer
说明:
- 功能:Floyd-Steinberg 抖动算法的颜色量化器
- 类型:
Palettizer(实现了color.Quantizer接口) - 用途:将真彩色图像转换为调色板图像时,使用抖动算法减少颜色带状效应
- 工作原理:将量化误差扩散到相邻像素,产生更平滑的过渡效果
使用示例:
package main
import (
"image"
"image/color/palette"
"image/draw"
"image/jpeg"
"image/png"
"os"
)
func main() {
// 打开 JPEG 图像(真彩色)
file, _ := os.Open("input.jpg")
defer file.Close()
src, _ := jpeg.Decode(file)
// 创建使用 WebSafe 调色板的目标图像
dst := image.NewPaletted(src.Bounds(), palette.WebSafe)
// 使用 Floyd-Steinberg 抖动进行绘制
draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.FloydSteinberg)
// 保存为 PNG
outFile, _ := os.Create("output.png")
defer outFile.Close()
png.Encode(outFile, dst)
}
效果对比:
// 不使用抖动(直接量化)
dst1 := image.NewPaletted(bounds, palette.WebSafe)
draw.Draw(dst1, dst1.Bounds(), src, image.Point{}, draw.Src)
// 结果:可能出现颜色带状效应
// 使用 Floyd-Steinberg 抖动
dst2 := image.NewPaletted(bounds, palette.WebSafe)
draw.Draw(dst2, dst2.Bounds(), src, image.Point{}, draw.FloydSteinberg)
// 结果:更平滑的颜色过渡
五、典型示例
示例 1:图像拼接
package main
import (
"image"
"image/color"
"image/draw"
"image/png"
"os"
)
func main() {
// 创建大的画布
canvas := image.NewRGBA(image.Rect(0, 0, 400, 300))
// 填充白色背景
draw.Draw(canvas, canvas.Bounds(), &image.Uniform{color.RGBA{255, 255, 255, 255}}, image.Point{}, draw.Src)
// 创建多个小图像
colors := []color.Color{
color.RGBA{255, 0, 0, 255}, // 红
color.RGBA{0, 255, 0, 255}, // 绿
color.RGBA{0, 0, 255, 255}, // 蓝
color.RGBA{255, 255, 0, 255}, // 黄
}
// 绘制多个方块
for i, c := range colors {
smallImg := image.NewRGBA(image.Rect(0, 0, 100, 100))
draw.Draw(smallImg, smallImg.Bounds(), &image.Uniform{c}, image.Point{}, draw.Src)
// 计算位置
x := (i % 2) * 200
y := (i / 2) * 150
// 绘制到画布
draw.Draw(canvas, image.Rect(x, y, x+100, y+100), smallImg, image.Point{}, draw.Src)
}
// 保存结果
file, _ := os.Create("collage.png")
defer file.Close()
png.Encode(file, canvas)
}
示例 2:图像缩放(最近邻插值)
package main
import (
"image"
"image/draw"
"image/png"
"os"
)
func main() {
// 打开源图像
file, _ := os.Open("input.png")
defer file.Close()
src, _ := png.Decode(file)
// 创建目标图像(放大 2 倍)
srcBounds := src.Bounds()
dstBounds := image.Rect(0, 0, srcBounds.Dx()*2, srcBounds.Dy()*2)
dst := image.NewRGBA(dstBounds)
// 使用 draw.Draw 进行缩放
draw.Draw(dst, dstBounds, src, srcBounds.Min, draw.Src)
// 保存结果
outFile, _ := os.Create("scaled.png")
defer outFile.Close()
png.Encode(outFile, dst)
}
示例 3:添加边框
package main
import (
"image"
"image/color"
"image/draw"
"image/png"
"os"
)
func main() {
// 打开源图像
file, _ := os.Open("input.png")
defer file.Close()
src, _ := png.Decode(file)
// 创建带边框的画布
borderSize := 10
srcBounds := src.Bounds()
canvasBounds := image.Rect(
0, 0,
srcBounds.Dx()+borderSize*2,
srcBounds.Dy()+borderSize*2,
)
canvas := image.NewRGBA(canvasBounds)
// 填充黑色边框
draw.Draw(canvas, canvas.Bounds(), &image.Uniform{color.RGBA{0, 0, 0, 255}}, image.Point{}, draw.Src)
// 绘制源图像到中心
draw.Draw(canvas, image.Rect(borderSize, borderSize, borderSize+srcBounds.Dx(), borderSize+srcBounds.Dy()), src, srcBounds.Min, draw.Src)
// 保存结果
outFile, _ := os.Create("with_border.png")
defer outFile.Close()
png.Encode(outFile, canvas)
}
示例 4:图像水印
package main
import (
"image"
"image/color"
"image/draw"
"image/png"
"os"
)
func main() {
// 打开主图像
file, _ := os.Open("photo.png")
defer file.Close()
photo, _ := png.Decode(file)
// 创建目标图像
dst := image.NewRGBA(photo.Bounds())
draw.Draw(dst, dst.Bounds(), photo, photo.Bounds().Min, draw.Src)
// 创建水印文本(简单示例,实际应使用 font 包)
watermark := image.NewRGBA(image.Rect(0, 0, 200, 50))
draw.Draw(watermark, watermark.Bounds(), &image.Uniform{color.RGBA{255, 255, 255, 128}}, image.Point{}, draw.Src)
// 计算水印位置(右下角)
x := dst.Bounds().Max.X - 200
y := dst.Bounds().Max.Y - 50
// 绘制水印(使用 Over 操作实现半透明效果)
draw.Draw(dst, image.Rect(x, y, x+200, y+50), watermark, image.Point{}, draw.Over)
// 保存结果
outFile, _ := os.Create("watermarked.png")
defer outFile.Close()
png.Encode(outFile, dst)
}
示例 5:图像裁剪
package main
import (
"image"
"image/draw"
"image/png"
"os"
)
func main() {
// 打开源图像
file, _ := os.Open("input.png")
defer file.Close()
src, _ := png.Decode(file)
// 定义裁剪区域
cropRect := image.Rect(50, 50, 150, 150)
// 创建目标图像
dst := image.NewRGBA(cropRect)
// 绘制裁剪区域
draw.Draw(dst, dst.Bounds(), src, cropRect.Min, draw.Src)
// 保存结果
outFile, _ := os.Create("cropped.png")
defer outFile.Close()
png.Encode(outFile, dst)
}
示例 6:使用 FloydSteinberg 创建 GIF
package main
import (
"image"
"image/color/palette"
"image/draw"
"image/gif"
"image/jpeg"
"os"
)
func main() {
// 打开 JPEG 图像
file, _ := os.Open("input.jpg")
defer file.Close()
src, _ := jpeg.Decode(file)
// 创建调色板图像
dst := image.NewPaletted(src.Bounds(), palette.Plan9)
// 使用 Floyd-Steinberg 抖动进行颜色量化
draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.FloydSteinberg)
// 保存为 GIF
outFile, _ := os.Create("output.gif")
defer outFile.Close()
gif.Encode(outFile, dst, nil)
}
六、最佳实践
1. 选择合适的操作类型
// 场景 1:不透明图像覆盖 -> 使用 Src
draw.Draw(dst, rect, src, point, draw.Src)
// 场景 2:半透明图像叠加 -> 使用 Over
draw.Draw(dst, rect, src, point, draw.Over)
// 场景 3:调色板图像绘制 -> 使用 FloydSteinberg
draw.Draw(dst, rect, src, point, draw.FloydSteinberg)
2. 性能优化
// 技巧 1:确保目标图像类型匹配
// 使用 *image.RGBA 通常比 *image.NRGBA 更快
dst := image.NewRGBA(bounds)
// 技巧 2:批量绘制时复用图像
smallImg := image.NewRGBA(image.Rect(0, 0, 100, 100))
for i := 0; i < 100; i++ {
// 修改 smallImg 内容
draw.Draw(dst, rect, smallImg, point, draw.Src)
}
// 技巧 3:避免不必要的内存分配
// 直接在目标图像上操作,而不是创建临时图像
3. 正确使用 DrawMask
// 正确:蒙版与源图像尺寸匹配
mask := image.NewRGBA(src.Bounds())
draw.DrawMask(dst, rect, src, point, mask, point, draw.Over)
// 错误:蒙版尺寸不匹配可能导致意外结果
// 确保蒙版的 Alpha 通道正确设置
4. 颜色量化技巧
// 技巧 1:选择合适的调色板
// - Plan9(256 色):照片类图像
// - WebSafe(216 色):图形/图标
// 技巧 2:使用抖动减少带状效应
draw.Draw(dst, bounds, src, point, draw.FloydSteinberg)
// 技巧 3:对于文本/线条图,可能不需要抖动
draw.Draw(dst, bounds, src, point, draw.Src)
七、与其他包配合
1. 与 image/color/palette 配合
package main
import (
"image"
"image/color/palette"
"image/draw"
"image/png"
"os"
)
func main() {
// 打开真彩色图像
file, _ := os.Open("input.png")
defer file.Close()
src, _ := png.Decode(file)
// 使用 Plan9 调色板创建新图像
dst := image.NewPaletted(src.Bounds(), palette.Plan9)
// 使用 Floyd-Steinberg 抖动进行量化
draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.FloydSteinberg)
// 保存
outFile, _ := os.Create("output.png")
defer outFile.Close()
png.Encode(outFile, dst)
}
2. 与 image/gif 配合
package main
import (
"image"
"image/color/palette"
"image/draw"
"image/gif"
"image/jpeg"
"os"
)
func createGIF(inputPath, outputPath string) {
// 打开 JPEG
file, _ := os.Open(inputPath)
defer file.Close()
src, _ := jpeg.Decode(file)
// 创建调色板图像
dst := image.NewPaletted(src.Bounds(), palette.WebSafe)
draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.FloydSteinberg)
// 保存为 GIF
outFile, _ := os.Create(outputPath)
defer outFile.Close()
gif.Encode(outFile, dst, nil)
}
3. 与 image/png 配合
package main
import (
"image"
"image/draw"
"image/png"
"os"
)
func compositeImages(backgroundPath, foregroundPath, outputPath string) {
// 打开背景图像
bgFile, _ := os.Open(backgroundPath)
defer bgFile.Close()
bg, _ := png.Decode(bgFile)
// 打开前景图像(带透明通道)
fgFile, _ := os.Open(foregroundPath)
defer fgFile.Close()
fg, _ := png.Decode(fgFile)
// 创建目标图像
dst := image.NewRGBA(bg.Bounds())
// 绘制背景
draw.Draw(dst, dst.Bounds(), bg, bg.Bounds().Min, draw.Src)
// 绘制前景(使用 Over 操作)
draw.Draw(dst, fg.Bounds(), fg, fg.Bounds().Min, draw.Over)
// 保存
outFile, _ := os.Create(outputPath)
defer outFile.Close()
png.Encode(outFile, dst)
}
4. 与 image/jpeg 配合
package main
import (
"image"
"image/color"
"image/draw"
"image/jpeg"
"os"
)
func addWatermarkToJPEG(inputPath, outputPath string) {
// 打开 JPEG
file, _ := os.Open(inputPath)
defer file.Close()
src, _ := jpeg.Decode(file)
// 创建目标图像
dst := image.NewRGBA(src.Bounds())
draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.Src)
// 创建半透明水印区域
watermark := image.NewRGBA(image.Rect(0, 0, 200, 50))
draw.Draw(watermark, watermark.Bounds(), &image.Uniform{color.RGBA{255, 255, 255, 100}}, image.Point{}, draw.Src)
// 绘制水印
x := dst.Bounds().Max.X - 200
y := dst.Bounds().Max.Y - 50
draw.Draw(dst, image.Rect(x, y, x+200, y+50), watermark, image.Point{}, draw.Over)
// 保存为 JPEG
outFile, _ := os.Create(outputPath)
defer outFile.Close()
jpeg.Encode(outFile, dst, &jpeg.Options{Quality: 90})
}
八、快速参考
函数总览
| 函数名 | 参数 | 返回值 | 描述 |
|---|---|---|---|
Draw | dst Image, r Rectangle, src Image, sp Point, op Op | 无 | 将源图像绘制到目标图像 |
DrawMask | dst Image, r Rectangle, src Image, sp Point, mask Image, mp Point, op Op | 无 | 使用蒙版绘制图像 |
FowlerNollVo | b []byte | uint32 | 计算 FNV 哈希值 |
接口总览
| 接口名 | 方法 | 描述 |
|---|---|---|
Drawer | Draw(dst, dr, src, sp, mask, mp, op) | 自定义绘制器接口 |
Image | image.Image + Set(x, y, c) | 可写图像接口 |
类型总览
| 类型名 | 底层类型 | 描述 |
|---|---|---|
Op | int8 | 绘制操作类型 |
常量总览
| 常量名 | 类型 | 值 | 描述 |
|---|---|---|---|
Src | Op | 0 | 源覆盖目标 |
Over | Op | 1 | 源在目标之上(alpha 混合) |
变量总览
| 变量名 | 类型 | 描述 |
|---|---|---|
FloydSteinberg | Palettizer | Floyd-Steinberg 抖动算法 |
操作类型对比
| 操作 | 公式 | 透明度处理 | 使用场景 |
|---|---|---|---|
Src | dst = src | 忽略 | 不透明图像 |
Over | dst = src*α + dst*(1-α) | 考虑 | 半透明叠加 |
FloydSteinberg | 特殊实现 | 抖动量化 | 调色板转换 |
九、注意事项
1. 目标图像类型限制
// 注意:Draw 的目标图像必须是可写的
var dst draw.Image = image.NewRGBA(bounds) // ✓ 正确
draw.Draw(dst, rect, src, point, op)
// 错误:image.Image 接口不可写
var src image.Image = loadImage()
draw.Draw(src, rect, src2, point, op) // ✗ 编译错误
2. 矩形区域对齐
// 确保源点和目标矩形正确对应
dstRect := image.Rect(0, 0, 100, 100)
srcPoint := image.Point{0, 0} // 通常设置为源图像的起点
// 如果源点不为 (0,0),需要调整计算
// 绘制的源图像区域为:(srcPoint.X, srcPoint.Y) 到 (srcPoint.X + dstRect.Dx(), srcPoint.Y + dstRect.Dy())
3. 蒙版 Alpha 通道
// 蒙版的 Alpha 值决定混合比例
// Alpha = 255 (0xFF):完全不透明,完全使用源像素
// Alpha = 0 (0x00):完全透明,保留目标像素
// Alpha = 128 (0x80):半透明,源和目标各占 50%
mask.Set(x, y, color.RGBA{0, 0, 0, 128}) // 50% 透明
4. FloydSteinberg 使用限制
// FloydSteinberg 仅适用于调色板图像
dst := image.NewPaletted(bounds, palette.WebSafe)
draw.Draw(dst, bounds, src, point, draw.FloydSteinberg) // ✓ 正确
// 对于非调色板图像,使用 Src 或 Over
dst2 := image.NewRGBA(bounds)
draw.Draw(dst2, bounds, src, point, draw.Src) // ✓ 正确
5. 性能考虑
// 对于大图像操作:
// 1. 使用合适的图像类型(RGBA 通常比 NRGBA 快)
// 2. 避免重复创建相同尺寸的图像
// 3. 批量操作时复用图像对象
// 4. 考虑使用并发处理多个独立区域
十、完整示例:图像合成工具
package main
import (
"image"
"image/color"
"image/color/palette"
"image/draw"
"image/png"
"os"
)
// ImageCompositor 图像合成器
type ImageCompositor struct {
canvas *image.RGBA
}
// NewCompositor 创建新的合成器
func NewCompositor(width, height int, bgColor color.Color) *ImageCompositor {
canvas := image.NewRGBA(image.Rect(0, 0, width, height))
draw.Draw(canvas, canvas.Bounds(), &image.Uniform{bgColor}, image.Point{}, draw.Src)
return &ImageCompositor{canvas: canvas}
}
// DrawImage 绘制图像
func (c *ImageCompositor) DrawImage(img image.Image, x, y int) {
bounds := img.Bounds()
rect := image.Rect(x, y, x+bounds.Dx(), y+bounds.Dy())
draw.Draw(c.canvas, rect, img, bounds.Min, draw.Over)
}
// DrawImageWithMask 使用蒙版绘制图像
func (c *ImageCompositor) DrawImageWithMask(img image.Image, mask image.Image, x, y int) {
bounds := img.Bounds()
rect := image.Rect(x, y, x+bounds.Dx(), y+bounds.Dy())
draw.DrawMask(c.canvas, rect, img, bounds.Min, mask, bounds.Min, draw.Over)
}
// ConvertToPalette 转换为调色板图像
func (c *ImageCompositor) ConvertToPalette(p []color.Color, useDithering bool) *image.Paletted {
dst := image.NewPaletted(c.canvas.Bounds(), p)
if useDithering {
draw.Draw(dst, dst.Bounds(), c.canvas, c.canvas.Bounds().Min, draw.FloydSteinberg)
} else {
draw.Draw(dst, dst.Bounds(), c.canvas, c.canvas.Bounds().Min, draw.Src)
}
return dst
}
// Save 保存图像
func (c *ImageCompositor) Save(filename string) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
return png.Encode(file, c.canvas)
}
func main() {
// 创建合成器
comp := NewCompositor(800, 600, color.RGBA{255, 255, 255, 255})
// 加载并绘制多个图像
// ...
// 保存结果
comp.Save("composite.png")
}
最后更新: 2026-04-04
Go 版本: 1.21+
包文档: https://pkg.go.dev/image/draw