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 RGBAimage.RGBA64- 64-bit RGBAimage.NRGBA- Non-premultiplied alphaimage.Gray- 8-bit grayscaleimage.Gray16- 16-bit grayscaleimage.Paletted- Indexed color
Best Practices
- Import format packages - Use blank imports for auto-registration
- Check bounds - Always validate coordinates
- Use appropriate type - Choose RGBA, Gray, etc. based on needs
- Handle errors - Check all decode/encode errors
- Close files - Use defer f.Close()
- Consider memory - Large images consume significant memory