Skip to main content
The image package implements basic 2D image processing. It provides image types, color models, and encoding/decoding for various image formats.

Core Types

type Image interface {
    ColorModel() color.Model
    Bounds() Rectangle
    At(x, y int) color.Color
}

type Rectangle struct {
    Min, Max Point
}

type Point struct {
    X, Y int
}

Creating Images

import "image"

// Create new RGBA image
img := image.NewRGBA(image.Rect(0, 0, 200, 100))

// Create with specific size
width, height := 640, 480
img := image.NewRGBA(image.Rectangle{
    Min: image.Point{0, 0},
    Max: image.Point{width, height},
})

Drawing on Images

import (
    "image"
    "image/color"
)

func drawRect() *image.RGBA {
    img := image.NewRGBA(image.Rect(0, 0, 300, 200))
    
    // Fill with blue
    blue := color.RGBA{0, 0, 255, 255}
    for y := 0; y < 200; y++ {
        for x := 0; x < 300; x++ {
            img.Set(x, y, blue)
        }
    }
    
    return img
}

// Draw rectangle
func fillRect(img *image.RGBA, rect image.Rectangle, c color.Color) {
    for y := rect.Min.Y; y < rect.Max.Y; y++ {
        for x := rect.Min.X; x < rect.Max.X; x++ {
            img.Set(x, y, c)
        }
    }
}

Encoding/Decoding

PNG

import (
    "image/png"
    "os"
)

// Encode to PNG
func savePNG(img image.Image, filename string) error {
    f, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer f.Close()
    
    return png.Encode(f, img)
}

// Decode PNG
func loadPNG(filename string) (image.Image, error) {
    f, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    defer f.Close()
    
    return png.Decode(f)
}

JPEG

import "image/jpeg"

func saveJPEG(img image.Image, filename string, quality int) error {
    f, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer f.Close()
    
    return jpeg.Encode(f, img, &jpeg.Options{Quality: quality})
}

func loadJPEG(filename string) (image.Image, error) {
    f, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    defer f.Close()
    
    return jpeg.Decode(f)
}

GIF

import "image/gif"

func saveGIF(img image.Image, filename string) error {
    f, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer f.Close()
    
    return gif.Encode(f, img, nil)
}

Generic Decode

import (
    "image"
    _ "image/gif"
    _ "image/jpeg"
    _ "image/png"
)

func loadImage(filename string) (image.Image, string, error) {
    f, err := os.Open(filename)
    if err != nil {
        return nil, "", err
    }
    defer f.Close()
    
    return image.Decode(f) // Returns image, format name, error
}

Image Manipulation

Resize/Crop

func crop(img image.Image, rect image.Rectangle) *image.RGBA {
    cropped := image.NewRGBA(rect)
    
    for y := rect.Min.Y; y < rect.Max.Y; y++ {
        for x := rect.Min.X; x < rect.Max.X; x++ {
            cropped.Set(x-rect.Min.X, y-rect.Min.Y, img.At(x, y))
        }
    }
    
    return cropped
}

Grayscale Conversion

import "image/color"

func toGrayscale(img image.Image) *image.Gray {
    bounds := img.Bounds()
    gray := image.NewGray(bounds)
    
    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            gray.Set(x, y, img.At(x, y))
        }
    }
    
    return gray
}

Flip/Rotate

func flipHorizontal(img image.Image) *image.RGBA {
    bounds := img.Bounds()
    flipped := image.NewRGBA(bounds)
    
    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            flipped.Set(bounds.Max.X-1-x, y, img.At(x, y))
        }
    }
    
    return flipped
}

func flipVertical(img image.Image) *image.RGBA {
    bounds := img.Bounds()
    flipped := image.NewRGBA(bounds)
    
    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            flipped.Set(x, bounds.Max.Y-1-y, img.At(x, y))
        }
    }
    
    return flipped
}

Color Operations

import "image/color"

// Adjust brightness
func adjustBrightness(img image.Image, delta int) *image.RGBA {
    bounds := img.Bounds()
    adjusted := image.NewRGBA(bounds)
    
    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            oldColor := img.At(x, y)
            r, g, b, a := oldColor.RGBA()
            
            newColor := color.RGBA{
                R: uint8(clamp(int(r>>8) + delta)),
                G: uint8(clamp(int(g>>8) + delta)),
                B: uint8(clamp(int(b>>8) + delta)),
                A: uint8(a >> 8),
            }
            
            adjusted.Set(x, y, newColor)
        }
    }
    
    return adjusted
}

func clamp(v int) int {
    if v < 0 {
        return 0
    }
    if v > 255 {
        return 255
    }
    return v
}

Practical Examples

Image Thumbnail Generator

func generateThumbnail(inputPath, outputPath string, maxWidth, maxHeight int) error {
    img, _, err := loadImage(inputPath)
    if err != nil {
        return err
    }
    
    bounds := img.Bounds()
    scale := math.Min(
        float64(maxWidth)/float64(bounds.Dx()),
        float64(maxHeight)/float64(bounds.Dy()),
    )
    
    if scale >= 1 {
        return savePNG(img, outputPath)
    }
    
    newWidth := int(float64(bounds.Dx()) * scale)
    newHeight := int(float64(bounds.Dy()) * scale)
    
    thumbnail := resize(img, newWidth, newHeight)
    return savePNG(thumbnail, outputPath)
}

Watermark

func addWatermark(base image.Image, watermark image.Image, x, y int) *image.RGBA {
    bounds := base.Bounds()
    result := image.NewRGBA(bounds)
    
    // Copy base image
    for py := bounds.Min.Y; py < bounds.Max.Y; py++ {
        for px := bounds.Min.X; px < bounds.Max.X; px++ {
            result.Set(px, py, base.At(px, py))
        }
    }
    
    // Overlay watermark
    wBounds := watermark.Bounds()
    for py := wBounds.Min.Y; py < wBounds.Max.Y; py++ {
        for px := wBounds.Min.X; px < wBounds.Max.X; px++ {
            result.Set(x+px, y+py, watermark.At(px, py))
        }
    }
    
    return result
}

Image Types

  • image.RGBA - 32-bit RGBA
  • image.RGBA64 - 64-bit RGBA
  • image.NRGBA - Non-premultiplied alpha
  • image.Gray - 8-bit grayscale
  • image.Gray16 - 16-bit grayscale
  • image.Paletted - Indexed color

Best Practices

  1. Import format packages - Use blank imports for auto-registration
  2. Check bounds - Always validate coordinates
  3. Use appropriate type - Choose RGBA, Gray, etc. based on needs
  4. Handle errors - Check all decode/encode errors
  5. Close files - Use defer f.Close()
  6. Consider memory - Large images consume significant memory

Build docs developers (and LLMs) love