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/color/palette 包详解

概述

image/color/palette 是一个独立的 Go 标准库包,提供预定义的标准调色板。它包含两个导出的变量:Plan9(256 色调色板)和 WebSafe(216 色调色板),这些调色板在颜色量化和 GIF 编码等场景中非常有用。

包导入

import "image/color/palette"

基本使用

1. 使用 Plan9 调色板

package main

import (
    "fmt"
    "image/color/palette"
)

func main() {
    // Plan9 是一个 []color.Color 类型的切片
    fmt.Printf("Plan9 调色板颜色数量:%d\n", len(palette.Plan9))
    
    // 访问第一个颜色
    firstColor := palette.Plan9[0]
    fmt.Printf("第一个颜色:%v\n", firstColor)
}

运行结果:

Plan9 调色板颜色数量:256
第一个颜色:{0 0 0 255}

2. 使用 WebSafe 调色板

package main

import (
    "fmt"
    "image/color/palette"
)

func main()
    // WebSafe 是一个 []color.Color 类型的切片
    fmt.Printf("WebSafe 调色板颜色数量:%d\n", len(palette.WebSafe))
    
    // 访问第一个颜色
    firstColor := palette.WebSafe[0]
    fmt.Printf("第一个颜色:%v\n", firstColor)
}

运行结果:

WebSafe 调色板颜色数量:216
第一个颜色:{0 0 0 255}

一、变量详解

Plan9

定义:

var Plan9 = []color.Color{...}

说明:

  • 颜色数量:256 种颜色
  • 设计原理:将 24 位 RGB 颜色空间划分为 4×4×4 的细分网格
  • 子立方体:每个子立方体包含 4 个阴影级别
  • 颜色分布
    • 16 个灰色阴影
    • 每种原色(红、绿、蓝)13 个阴影
    • 每种二次色(青、品红、黄)13 个阴影
  • 优势:更好地表示连续色调图像
  • 历史:曾用于 Plan 9 操作系统

技术细节:

RGB 空间细分:
- R 轴:4 个级别 (0, 85, 170, 255)
- G 轴:4 个级别 (0, 85, 170, 255)
- B 轴:4 个级别 (0, 85, 170, 255)
- 总计:4 × 4 × 4 = 64 个子立方体
- 每个子立方体:4 个阴影级别
- 总颜色数:64 × 4 = 256

WebSafe

定义:

var WebSafe = []color.Color{...}

说明:

  • 颜色数量:216 种颜色
  • 设计原理:RGB 分量各取 6 个固定值
  • 颜色值:每个分量取以下 6 个值之一:
    • 0x00 (0)
    • 0x33 (51)
    • 0x66 (102)
    • 0x99 (153)
    • 0xCC (204)
    • 0xFF (255)
  • 别名:Netscape Color Cube
  • 历史:由早期 Netscape Navigator 浏览器推广
  • 用途:确保在不同显示器上颜色显示一致

技术细节:

RGB 分量组合:
- R: 6 个值 (0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF)
- G: 6 个值 (0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF)
- B: 6 个值 (0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF)
- 总颜色数:6 × 6 × 6 = 216

二、典型示例

示例 1:创建使用 Plan9 调色板的 GIF 图像

package main

import (
    "image"
    "image/color"
    "image/color/palette"
    "image/gif"
    "os"
)

func main() {
    // 创建 100x100 的灰度图像
    img := image.NewPaletted(
        image.Rect(0, 0, 100, 100),
        palette.Plan9,
    )
    
    // 使用调色板中的颜色填充
    for y := 0; y < 100; y++ {
        for x := 0; x < 100; x++ {
            // 选择调色板中的颜色索引
            colorIndex := uint8((x + y) % len(palette.Plan9))
            img.SetColorIndex(x, y, colorIndex)
        }
    }
    
    // 创建 GIF 文件
    file, _ := os.Create("output_plan9.gif")
    defer file.Close()
    
    // 编码为 GIF
    gif.Encode(file, img, &gif.Options{
        NumColors: 256,
    })
}

示例 2:创建使用 WebSafe 调色板的 GIF 图像

package main

import (
    "image"
    "image/color/palette"
    "image/gif"
    "os"
)

func main() {
    // 创建使用 WebSafe 调色板的图像
    img := image.NewPaletted(
        image.Rect(0, 0, 100, 100),
        palette.WebSafe,
    )
    
    // 创建渐变效果
    for y := 0; y < 100; y++ {
        for x := 0; x < 100; x++ {
            // 根据位置选择 WebSafe 调色板中的颜色
            r := uint8(x * 255 / 100)
            g := uint8(y * 255 / 100)
            b := uint8((x + y) * 255 / 200)
            
            // 找到最接近的 WebSafe 颜色索引
            colorIndex := findClosestColor(r, g, b, palette.WebSafe)
            img.SetColorIndex(x, y, colorIndex)
        }
    }
    
    // 保存为 GIF
    file, _ := os.Create("output_websafe.gif")
    defer file.Close()
    gif.Encode(file, img, nil)
}

// findClosestColor 找到调色板中最接近的颜色
func findClosestColor(r, g, b uint8, pal []color.Color) uint8 {
    minDistance := float64(1000000)
    closestIndex := uint8(0)
    
    for i, c := range pal {
        rc, gc, bc, _ := c.RGBA()
        rc >>= 8
        gc >>= 8
        bc >>= 8
        
        // 计算欧几里得距离
        distance := float64((int(r) - int(rc)) * (int(r) - int(rc)) +
            (int(g) - int(gc)) * (int(g) - int(gc)) +
            (int(b) - int(bc)) * (int(b) - int(bc)))
        
        if distance < minDistance {
            minDistance = distance
            closestIndex = uint8(i)
        }
    }
    
    return closestIndex
}

示例 3:比较两种调色板的颜色分布

package main

import (
    "fmt"
    "image/color/palette"
)

func main() {
    fmt.Println("=== Plan9 调色板 ===")
    fmt.Printf("总颜色数:%d\n", len(palette.Plan9))
    
    // 显示前 16 个颜色
    fmt.Println("\n前 16 个颜色 (RGBA):")
    for i := 0; i < 16 && i < len(palette.Plan9); i++ {
        r, g, b, a := palette.Plan9[i].RGBA()
        fmt.Printf("[%2d] R:%3d G:%3d B:%3d A:%3d\n", 
            i, r>>8, g>>8, b>>8, a>>8)
    }
    
    fmt.Println("\n=== WebSafe 调色板 ===")
    fmt.Printf("总颜色数:%d\n", len(palette.WebSafe))
    
    // 显示前 16 个颜色
    fmt.Println("\n前 16 个颜色 (RGBA):")
    for i := 0; i < 16 && i < len(palette.WebSafe); i++ {
        r, g, b, a := palette.WebSafe[i].RGBA()
        fmt.Printf("[%2d] R:%3d G:%3d B:%3d A:%3d\n", 
            i, r>>8, g>>8, b>>8, a>>8)
    }
}

运行结果:

=== Plan9 调色板 ===
总颜色数:256

前 16 个颜色 (RGBA):
[ 0] R:  0 G:  0 B:  0 A:255
[ 1] R:  0 G:  0 B: 85 A:255
[ 2] R:  0 G:  0 B:170 A:255
[ 3] R:  0 G:  0 B:255 A:255
[ 4] R:  0 G: 85 B:  0 A:255
[ 5] R:  0 G: 85 B: 85 A:255
[ 6] R:  0 G: 85 B:170 A:255
[ 7] R:  0 G: 85 B:255 A:255
[ 8] R:  0 G:170 B:  0 A:255
[ 9] R:  0 G:170 B: 85 A:255
[10] R:  0 G:170 B:170 A:255
[11] R:  0 G:170 B:255 A:255
[12] R:  0 G:255 B:  0 A:255
[13] R:  0 G:255 B: 85 A:255
[14] R:  0 G:255 B:170 A:255
[15] R:  0 G:255 B:255 A:255

=== WebSafe 调色板 ===
总颜色数:216

前 16 个颜色 (RGBA):
[ 0] R:  0 G:  0 B:  0 A:255
[ 1] R:  0 G:  0 B: 51 A:255
[ 2] R:  0 G:  0 B:102 A:255
[ 3] R:  0 G:  0 B:153 A:255
[ 4] R:  0 G:  0 B:204 A:255
[ 5] R:  0 G:  0 B:255 A:255
[ 6] R:  0 G: 51 B:  0 A:255
[ 7] R:  0 G: 51 B: 51 A:255
[ 8] R:  0 G: 51 B:102 A:255
[ 9] R:  0 G: 51 B:153 A:255
[10] R:  0 G: 51 B:204 A:255
[11] R:  0 G: 51 B:255 A:255
[12] R:  0 G:102 B:  0 A:255
[13] R:  0 G:102 B: 51 A:255
[14] R:  0 G:102 B:102 A:255
[15] R:  0 G:102 B:153 A:255

示例 4:颜色量化 - 将真彩色图像转换为调色板图像

package main

import (
    "image"
    "image/color"
    "image/color/palette"
    "image/draw"
    "image/gif"
    "image/png"
    "os"
)

func main() {
    // 打开 PNG 图像
    file, _ := os.Open("input.png")
    defer file.Close()
    srcImg, _ := png.Decode(file)
    
    // 使用 Plan9 调色板创建新的调色板图像
    dstImg := image.NewPaletted(srcImg.Bounds(), palette.Plan9)
    
    // 绘制图像并自动进行颜色量化
    draw.Draw(dstImg, dstImg.Bounds(), srcImg, image.Point{}, draw.Src)
    
    // 保存为 GIF
    outFile, _ := os.Create("output_quantized.gif")
    defer outFile.Close()
    gif.Encode(outFile, dstImg, nil)
}

三、实际应用场景

1. GIF 动画制作

package main

import (
    "image"
    "image/color/palette"
    "image/gif"
    "os"
)

func main() {
    // 创建多帧 GIF 动画
    var frames []*image.Paletted
    var delays []int
    
    // 生成 10 帧动画
    for frame := 0; frame < 10; frame++ {
        img := image.NewPaletted(
            image.Rect(0, 0, 100, 100),
            palette.WebSafe,
        )
        
        // 每帧绘制不同内容
        for y := 0; y < 100; y++ {
            for x := 0; x < 100; x++ {
                colorIndex := uint8((x + y + frame*10) % len(palette.WebSafe))
                img.SetColorIndex(x, y, colorIndex)
            }
        }
        
        frames = append(frames, img)
        delays = append(delays, 10) // 每帧延迟 10ms
    }
    
    // 创建 GIF 动画
    file, _ := os.Create("animation.gif")
    defer file.Close()
    
    gif.EncodeAll(file, &gif.GIF{
        Image: frames,
        Delay: delays,
    })
}

2. 颜色映射可视化

package main

import (
    "image"
    "image/color/palette"
    "image/draw"
    "image/png"
    "os"
)

func main() {
    // 创建显示调色板所有颜色的图像
    width := 256
    height := 4 // Plan9 有 256 色,分成 4 行显示
    
    img := image.NewRGBA(image.Rect(0, 0, width, height*16))
    
    // 绘制 Plan9 调色板的颜色样本
    for i, c := range palette.Plan9 {
        x := (i % width) 
        y := (i / width) * 16
        
        rect := image.Rect(x, y, x+16, y+16)
        draw.Draw(img, rect, &image.Uniform{c}, image.Point{}, draw.Src)
    }
    
    // 保存为 PNG
    file, _ := os.Create("palette_plan9.png")
    defer file.Close()
    png.Encode(file, img)
}

3. 性能优化 - 减少颜色数量

package main

import (
    "image"
    "image/color/palette"
    "image/draw"
    "image/jpeg"
    "image/png"
    "os"
)

func compressImage(inputPath, outputPath string) error {
    // 打开原始图像
    file, err := os.Open(inputPath)
    if err != nil {
        return err
    }
    defer file.Close()
    
    srcImg, _, err := image.Decode(file)
    if err != nil {
        return err
    }
    
    // 使用 WebSafe 调色板(仅 216 色)
    dstImg := image.NewPaletted(srcImg.Bounds(), palette.WebSafe)
    draw.Draw(dstImg, dstImg.Bounds(), srcImg, image.Point{}, draw.Src)
    
    // 保存为 PNG(调色板图像会自动压缩)
    outFile, err := os.Create(outputPath)
    if err != nil {
        return err
    }
    defer outFile.Close()
    
    return png.Encode(outFile, dstImg)
}

四、最佳实践

1. 选择合适的调色板

// 场景 1:需要更多颜色细节 -> 使用 Plan9 (256 色)
img1 := image.NewPaletted(bounds, palette.Plan9)

// 场景 2:需要网络兼容性 -> 使用 WebSafe (216 色)
img2 := image.NewPaletted(bounds, palette.WebSafe)

// 场景 3:照片类图像 -> Plan9 更好(连续色调)
// 场景 4:图形/图标 -> WebSafe 足够(颜色较少)

2. 颜色量化技巧

// 技巧 1:使用 draw.Draw 自动进行颜色量化
srcImg := loadFullColorImage()
dstImg := image.NewPaletted(bounds, palette.Plan9)
draw.Draw(dstImg, dstImg.Bounds(), srcImg, image.Point{}, draw.Src)

// 技巧 2:手动选择最接近的颜色(更精确但更慢)
func findClosestColor(target color.Color, pal []color.Color) uint8 {
    // 实现颜色距离计算
}

3. GIF 编码优化

// 优化 1:指定颜色数量
gif.Encode(file, img, &gif.Options{
    NumColors: 256,  // 最多使用 256 色
    Quantizer: nil,  // 使用默认量化器
    Drawer: nil,     // 使用默认绘制器
})

// 优化 2:使用自定义调色板
gif.Encode(file, img, &gif.Options{
    NumColors: len(palette.WebSafe),
})

五、与其他包配合

1. 与 image/color 配合

package main

import (
    "image/color"
    "image/color/palette"
)

// 将颜色转换为调色板索引
func colorToIndex(c color.Color, pal []color.Color) uint8 {
    cr, cg, cb, _ := c.RGBA()
    
    minDist := uint32(1000000)
    index := uint8(0)
    
    for i, pc := range pal {
        pr, pg, pb, _ := pc.RGBA()
        
        dist := (cr-pr)*(cr-pr) + (cg-pg)*(cg-pg) + (cb-pb)*(cb-pb)
        
        if dist < minDist {
            minDist = dist
            index = uint8(i)
        }
    }
    
    return index
}

2. 与 image/draw 配合

package main

import (
    "image"
    "image/color/palette"
    "image/draw"
)

func main() {
    // 创建源图像
    src := image.NewRGBA(image.Rect(0, 0, 100, 100))
    
    // 创建目标调色板图像
    dst := image.NewPaletted(image.Rect(0, 0, 100, 100), palette.Plan9)
    
    // 使用 draw.Draw 进行颜色量化
    draw.Draw(dst, dst.Bounds(), src, image.Point{}, draw.Src)
}

3. 与 image/gif 配合

package main

import (
    "image"
    "image/color/palette"
    "image/gif"
    "os"
)

func createGIF() {
    // 创建调色板图像
    img := image.NewPaletted(image.Rect(0, 0, 200, 200), palette.WebSafe)
    
    // 填充颜色
    for i := range img.Pix {
        img.Pix[i] = uint8(i % len(palette.WebSafe))
    }
    
    // 编码为 GIF
    file, _ := os.Create("output.gif")
    defer file.Close()
    gif.Encode(file, img, nil)
}

六、快速参考

变量总览

变量名类型颜色数描述
Plan9[]color.Color256Plan 9 操作系统的 256 色调色板
WebSafe[]color.Color216Web 安全色调色板(Netscape Color Cube)

颜色分布对比

特性Plan9WebSafe
总颜色数256216
RGB 细分4×4×46×6×6
灰色阴影16 个6 个
原色阴影13 个/色6 个/色
适用场景连续色调图像网络图形
历史来源Plan 9 操作系统Netscape Navigator

使用场景推荐

场景推荐调色板原因
照片转换Plan9更好的连续色调表示
GIF 动画Plan9/WebSafe取决于颜色需求
网页图形WebSafe跨平台颜色一致性
图标/LogoWebSafe颜色数量足够
艺术图像Plan9更多颜色细节

七、注意事项

1. 调色板限制

// 注意:调色板图像最多支持 256 色
img := image.NewPaletted(bounds, palette.Plan9)  // ✓ 256 色
img := image.NewPaletted(bounds, palette.WebSafe) // ✓ 216 色

// 如果自定义调色板超过 256 色,GIF 编码会失败

2. 颜色精度损失

// 从真彩色转换到调色板会有精度损失
srcImg := loadTrueColorImage()  // 数百万色
dstImg := image.NewPaletted(bounds, palette.WebSafe) // 仅 216 色
draw.Draw(dstImg, dstImg.Bounds(), srcImg, image.Point{}, draw.Src)
// 某些颜色可能无法精确表示

3. 性能考虑

// 颜色量化是计算密集型操作
// 对于大图像,考虑:
// 1. 使用更快的量化算法
// 2. 降低图像分辨率
// 3. 使用更少的颜色数量

八、完整示例:创建调色板颜色样本图

package main

import (
    "image"
    "image/color"
    "image/color/palette"
    "image/draw"
    "image/png"
    "os"
)

func main() {
    // 创建显示两种调色板的图像
    width := 256
    height := 16 * 2 // 两行,每行 16 像素高
    
    img := image.NewRGBA(image.Rect(0, 0, width, height))
    
    // 绘制 Plan9 调色板(上半部分)
    for i, c := range palette.Plan9 {
        if i >= 256 {
            break
        }
        x := i % 16 * 16
        y := i / 16 * 16
        rect := image.Rect(x, y, x+16, y+16)
        draw.Draw(img, rect, &image.Uniform{c}, image.Point{}, draw.Src)
    }
    
    // 绘制 WebSafe 调色板(下半部分)
    for i, c := range palette.WebSafe {
        if i >= 216 {
            break
        }
        x := i % 16 * 16
        y := 16 + i/16*8
        rect := image.Rect(x, y, x+16, y+8)
        draw.Draw(img, rect, &image.Uniform{c}, image.Point{}, draw.Src)
    }
    
    // 保存图像
    file, _ := os.Create("palette_comparison.png")
    defer file.Close()
    png.Encode(file, img)
}

最后更新: 2026-04-04
Go 版本: 1.21+
包文档: https://pkg.go.dev/image/color/palette