Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Go image/draw 包详解

概述

image/draw 包提供图像绘制功能,支持将一个图像绘制到另一个图像上。它提供了 DrawDrawMask 等核心函数,以及 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.Srcdraw.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 混合)
)

详细对比:

操作公式效果使用场景
Srcdst = src源像素直接替换目标像素不透明图像、完全覆盖
Overdst = 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})
}

八、快速参考

函数总览

函数名参数返回值描述
Drawdst Image, r Rectangle, src Image, sp Point, op Op将源图像绘制到目标图像
DrawMaskdst Image, r Rectangle, src Image, sp Point, mask Image, mp Point, op Op使用蒙版绘制图像
FowlerNollVob []byteuint32计算 FNV 哈希值

接口总览

接口名方法描述
DrawerDraw(dst, dr, src, sp, mask, mp, op)自定义绘制器接口
Imageimage.Image + Set(x, y, c)可写图像接口

类型总览

类型名底层类型描述
Opint8绘制操作类型

常量总览

常量名类型描述
SrcOp0源覆盖目标
OverOp1源在目标之上(alpha 混合)

变量总览

变量名类型描述
FloydSteinbergPalettizerFloyd-Steinberg 抖动算法

操作类型对比

操作公式透明度处理使用场景
Srcdst = src忽略不透明图像
Overdst = 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