Примеры Обработки Изображений Web Go

Примеры обработки изображений Web Go включая чтение/запись, изменение размера и конвертацию форматов

💻 Чтение/Запись Изображений go

🟢 simple ⭐⭐⭐

Читать и сохранять изображения в различных форматах (PNG, JPEG, GIF) используя пакеты image Go

⏱️ 25 min 🏷️ go, web, image processing
Prerequisites: Intermediate Go, image package
// Web Go Image Read/Save Examples
// Using Go image packages for image I/O operations

package main

import (
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"image/jpeg"
	"image/png"
	"os"
	"path/filepath"
	"strings"
)

// 1. Reading Images

// LoadImage loads an image from file (auto-detects format)
func LoadImage(filePath string) (image.Image, string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, "", fmt.Errorf("error opening file: %v", err)
	}
	defer file.Close()

	// Decode image format
	img, format, err := image.Decode(file)
	if err != nil {
		return nil, "", fmt.Errorf("error decoding image: %v", err)
	}

	return img, format, nil
}

// LoadPNG loads a PNG image
func LoadPNG(filePath string) (image.Image, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, fmt.Errorf("error opening file: %v", err)
	}
	defer file.Close()

	img, err := png.Decode(file)
	if err != nil {
		return nil, fmt.Errorf("error decoding PNG: %v", err)
	}

	return img, nil
}

// LoadJPEG loads a JPEG image
func LoadJPEG(filePath string) (image.Image, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, fmt.Errorf("error opening file: %v", err)
	}
	defer file.Close()

	img, err := jpeg.Decode(file)
	if err != nil {
		return nil, fmt.Errorf("error decoding JPEG: %v", err)
	}

	return img, nil
}

// LoadImageWithOptions loads image with options
func LoadImageWithOptions(filePath string) (image.Image, image.Config, string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, image.Config{}, "", fmt.Errorf("error opening file: %v", err)
	}
	defer file.Close()

	// Get image config without decoding
	config, format, err := image.DecodeConfig(file)
	if err != nil {
		return nil, image.Config{}, "", fmt.Errorf("error decoding config: %v", err)
	}

	// Reset file pointer
	_, err = file.Seek(0, 0)
	if err != nil {
		return nil, image.Config{}, "", fmt.Errorf("error seeking file: %v", err)
	}

	// Decode image
	img, _, err := image.Decode(file)
	if err != nil {
		return nil, image.Config{}, "", fmt.Errorf("error decoding image: %v", err)
	}

	return img, config, format, nil
}

// 2. Saving Images

// SaveImage saves an image to file (auto-detects format from extension)
func SaveImage(img image.Image, filePath string) error {
	// Determine format from extension
	ext := strings.ToLower(filepath.Ext(filePath))
	var format string

	switch ext {
	case ".png":
		format = "png"
	case ".jpg", ".jpeg":
		format = "jpeg"
	case ".gif":
		format = "gif"
	default:
		return fmt.Errorf("unsupported format: %s", ext)
	}

	file, err := os.Create(filePath)
	if err != nil {
		return fmt.Errorf("error creating file: %v", err)
	}
	defer file.Close()

	switch format {
	case "png":
		err = png.Encode(file, img)
	case "jpeg":
		err = jpeg.Encode(file, img, &jpeg.Options{Quality: 95})
	case "gif":
		err = gif.Encode(file, img, nil)
	}

	if err != nil {
		return fmt.Errorf("error encoding image: %v", err)
	}

	return nil
}

// SavePNG saves an image as PNG
func SavePNG(img image.Image, filePath string) error {
	file, err := os.Create(filePath)
	if err != nil {
		return fmt.Errorf("error creating file: %v", err)
	}
	defer file.Close()

	err = png.Encode(file, img)
	if err != nil {
		return fmt.Errorf("error encoding PNG: %v", err)
	}

	return nil
}

// SaveJPEG saves an image as JPEG with quality
func SaveJPEG(img image.Image, filePath string, quality int) error {
	file, err := os.Create(filePath)
	if err != nil {
		return fmt.Errorf("error creating file: %v", err)
	}
	defer file.Close()

	err = jpeg.Encode(file, img, &jpeg.Options{Quality: quality})
	if err != nil {
		return fmt.Errorf("error encoding JPEG: %v", err)
	}

	return nil
}

// 3. Image Information

// ImageInfo holds information about an image
type ImageInfo struct {
	Width     int
	Height    int
	Format    string
	ColorMode string
	FilePath  string
}

// GetImageInfo gets information about an image file
func GetImageInfo(filePath string) (*ImageInfo, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, fmt.Errorf("error opening file: %v", err)
	}
	defer file.Close()

	config, format, err := image.DecodeConfig(file)
	if err != nil {
		return nil, fmt.Errorf("error decoding config: %v", err)
	}

	colorMode := "RGBA"
	if config.ColorModel != nil {
		colorMode = config.ColorModel.String()
	}

	return &ImageInfo{
		Width:     config.Width,
		Height:    config.Height,
		Format:    format,
		ColorMode: colorMode,
		FilePath:  filePath,
	}, nil
}

// GetImageDimensions gets width and height of an image
func GetImageDimensions(filePath string) (int, int, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return 0, 0, fmt.Errorf("error opening file: %v", err)
	}
	defer file.Close()

	config, _, err := image.DecodeConfig(file)
	if err != nil {
		return 0, 0, fmt.Errorf("error decoding config: %v", err)
	}

	return config.Width, config.Height, nil
}

// 4. Creating New Images

// CreateRGBAImage creates a new RGBA image
func CreateRGBAImage(width, height int) *image.RGBA {
	return image.NewRGBA(image.Rect(0, 0, width, height))
}

// CreateSolidColorImage creates an image filled with a solid color
func CreateSolidColorImage(width, height int, color color.Color) image.Image {
	img := image.NewRGBA(image.Rect(0, 0, width, height))
	draw.Draw(img, img.Bounds(), &image.Uniform{color}, image.Point{}, draw.Src)
	return img
}

// CreateGradientImage creates a horizontal gradient image
func CreateGradientImage(width, height int, startColor, endColor color.Color) image.Image {
	img := image.NewRGBA(image.Rect(0, 0, width, height))

	r1, g1, b1, a1 := startColor.RGBA()
	r2, g2, b2, a2 := endColor.RGBA()

	for x := 0; x < width; x++ {
		r := uint8((r1 + (r2-r1)*uint32(x)/uint32(width)) >> 8)
		g := uint8((g1 + (g2-g1)*uint32(x)/uint32(width)) >> 8)
		b := uint8((b1 + (b2-b1)*uint32(x)/uint32(width)) >> 8)
		a := uint8((a1 + (a2-a1)*uint32(x)/uint32(width)) >> 8)

		for y := 0; y < height; y++ {
			img.Set(x, y, color.RGBA{r, g, b, a})
		}
	}

	return img
}

// 5. Image Manipulation

// CropImage crops an image to the specified rectangle
func CropImage(img image.Image, rect image.Rectangle) image.Image {
	cropped := image.NewRGBA(rect)
	draw.Draw(cropped, rect, img, rect.Min, draw.Src)
	return cropped
}

// CloneImage creates a copy of an image
func CloneImage(img image.Image) image.Image {
	cloned := image.NewRGBA(img.Bounds())
	draw.Draw(cloned, img.Bounds(), img, image.Point{}, draw.Src)
	return cloned
}

// Rotate180 rotates an image 180 degrees
func Rotate180(img image.Image) image.Image {
	srcBounds := img.Bounds()
	dst := image.NewRGBA(srcBounds)

	for y := 0; y < srcBounds.Dy(); y++ {
		for x := 0; x < srcBounds.Dx(); x++ {
			dst.Set(srcBounds.Dx()-1-x, srcBounds.Dy()-1-y, img.At(x, y))
		}
	}

	return dst
}

// 6. Batch Operations

// LoadMultipleImages loads multiple images
func LoadMultipleImages(filePaths []string) (map[string]image.Image, error) {
	images := make(map[string]image.Image)

	for _, path := range filePaths {
		img, _, err := LoadImage(path)
		if err != nil {
			return nil, fmt.Errorf("error loading %s: %v", path, err)
		}
		images[path] = img
	}

	return images, nil
}

// SaveMultipleImages saves multiple images
func SaveMultipleImages(images map[string]image.Image, directory string) error {
	for filename, img := range images {
		filePath := filepath.Join(directory, filename)
		if err := SaveImage(img, filePath); err != nil {
			return fmt.Errorf("error saving %s: %v", filename, err)
		}
	}

	return nil
}

// ConvertMultipleImages converts multiple images to a different format
func ConvertMultipleImages(inputPaths []string, outputDir, outputFormat string) error {
	for _, inputPath := range inputPaths {
		// Load image
		img, _, err := LoadImage(inputPath)
		if err != nil {
			return err
		}

		// Create output filename
		baseName := filepath.Base(inputPath)
		baseName = strings.TrimSuffix(baseName, filepath.Ext(baseName))
		outputPath := filepath.Join(outputDir, baseName+"."+outputFormat)

		// Save in new format
		if err := SaveImage(img, outputPath); err != nil {
			return err
		}
	}

	return nil
}

// 7. Image Validation

// IsValidImage checks if a file is a valid image
func IsValidImage(filePath string) bool {
	file, err := os.Open(filePath)
	if err != nil {
		return false
	}
	defer file.Close()

	_, _, err = image.Decode(file)
	return err == nil
}

// GetSupportedFormats returns list of supported image formats
func GetSupportedFormats() []string {
	return []string{"png", "jpeg", "gif"}
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go Image Read/Save Examples ===\n")

	// 1. Load image
	fmt.Println("--- 1. Load Image ---")
	img, format, err := LoadImage("input.jpg")
	if err == nil {
		fmt.Printf("Loaded image format: %s\n", format)
		fmt.Printf("Bounds: %v\n", img.Bounds())
	}

	// 2. Get image info
	fmt.Println("\n--- 2. Image Info ---")
	info, err := GetImageInfo("input.jpg")
	if err == nil {
		fmt.Printf("Width: %d\n", info.Width)
		fmt.Printf("Height: %d\n", info.Height)
		fmt.Printf("Format: %s\n", info.Format)
	}

	// 3. Save image
	fmt.Println("\n--- 3. Save Image ---")
	err = SavePNG(img, "output.png")
	if err == nil {
		fmt.Println("Image saved as PNG")
	}

	// 4. Create new image
	fmt.Println("\n--- 4. Create New Image ---")
	newImg := CreateSolidColorImage(400, 300, color.RGBA{255, 0, 0, 255})
	SavePNG(newImg, "red_square.png")
	fmt.Println("Created solid color image")

	// 5. Create gradient
	fmt.Println("\n--- 5. Create Gradient ---")
	gradient := CreateGradientImage(400, 300,
		color.RGBA{0, 0, 255, 255},
		color.RGBA{255, 0, 0, 255})
	SavePNG(gradient, "gradient.png")
	fmt.Println("Created gradient image")

	// 6. Clone and crop
	fmt.Println("\n--- 6. Clone and Crop ---")
	cloned := CloneImage(img)
	cropped := CropImage(cloned, image.Rect(50, 50, 250, 250))
	SavePNG(cropped, "cropped.png")
	fmt.Println("Cropped image")

	// 7. Batch conversion
	fmt.Println("\n--- 7. Batch Conversion ---")
	images := []string{"image1.jpg", "image2.jpg"}
	err = ConvertMultipleImages(images, "output", "png")
	if err == nil {
		fmt.Println("Batch conversion completed")
	}

	// 8. Validation
	fmt.Println("\n--- 8. Validation ---")
	fmt.Printf("Valid image: %v\n", IsValidImage("input.jpg"))

	fmt.Println("\n=== All Image Read/Save Examples Completed ===")
}

💻 Конвертация Форматов go

🟢 simple ⭐⭐⭐

Конвертировать изображения между PNG, JPEG, GIF и другими форматами с контролем качества

⏱️ 25 min 🏷️ go, web, image processing
Prerequisites: Intermediate Go, image package, filepath
// Web Go Image Format Conversion Examples
// Converting between different image formats

package main

import (
	"fmt"
	"image"
	"image/jpeg"
	"image/png"
	"os"
	"path/filepath"
	"strings"
)

// 1. Basic Format Conversion

// ConvertFormat converts an image from one format to another
func ConvertFormat(inputPath, outputPath string) error {
	// Load image
	img, _, err := LoadImage(inputPath)
	if err != nil {
		return fmt.Errorf("error loading image: %v", err)
	}

	// Save in new format
	return SaveImage(img, outputPath)
}

// ConvertToPNG converts any image to PNG
func ConvertToPNG(inputPath, outputPath string) error {
	img, _, err := LoadImage(inputPath)
	if err != nil {
		return fmt.Errorf("error loading image: %v", err)
	}

	return SavePNG(img, outputPath)
}

// ConvertToJPEG converts any image to JPEG with quality setting
func ConvertToJPEG(inputPath, outputPath string, quality int) error {
	img, _, err := LoadImage(inputPath)
	if err != nil {
		return fmt.Errorf("error loading image: %v", err)
	}

	return SaveJPEG(img, outputPath, quality)
}

// 2. Quality Control

// JPEGQualityOptions holds JPEG quality presets
type JPEGQualityOptions struct {
	Low     int // 50-60
	Medium  int // 70-80
	High    int // 85-95
	Maximum int // 100
}

// GetJPEGQualityPresets returns quality presets
func GetJPEGQualityPresets() JPEGQualityOptions {
	return JPEGQualityOptions{
		Low:     60,
		Medium:  75,
		High:    90,
		Maximum: 100,
	}
}

// ConvertToJPEGWithQuality converts to JPEG with specified quality
func ConvertToJPEGWithQuality(inputPath, outputPath string, quality int) error {
	if quality < 1 || quality > 100 {
		return fmt.Errorf("quality must be between 1 and 100")
	}

	img, _, err := LoadImage(inputPath)
	if err != nil {
		return fmt.Errorf("error loading image: %v", err)
	}

	return SaveJPEG(img, outputPath, quality)
}

// ConvertToJPEGMultipleQualities generates JPEG at multiple quality levels
func ConvertToJPEGMultipleQualities(inputPath, outputDir string, qualities []int) error {
	img, _, err := LoadImage(inputPath)
	if err != nil {
		return fmt.Errorf("error loading image: %v", err)
	}

	baseName := filepath.Base(inputPath)
	baseName = strings.TrimSuffix(baseName, filepath.Ext(baseName))

	for _, quality := range qualities {
		outputPath := filepath.Join(outputDir, fmt.Sprintf("%s_q%d.jpg", baseName, quality))
		if err := SaveJPEG(img, outputPath, quality); err != nil {
			return err
		}
	}

	return nil
}

// 3. Batch Conversion

// ConversionOptions holds options for batch conversion
type ConversionOptions struct {
	InputDir    string
	OutputDir   string
	InputFormat string
	OutputFormat string
	Quality     int // For JPEG
}

// ConvertBatch converts all images of input format to output format
func ConvertBatch(options ConversionOptions) ([]string, error) {
	var converted []string

	// Find all files with input format
	pattern := filepath.Join(options.InputDir, "*."+options.InputFormat)
	files, err := filepath.Glob(pattern)
	if err != nil {
		return nil, fmt.Errorf("error finding files: %v", err)
	}

	for _, filePath := range files {
		// Load image
		img, _, err := LoadImage(filePath)
		if err != nil {
			return nil, fmt.Errorf("error loading %s: %v", filePath, err)
		}

		// Generate output path
		baseName := filepath.Base(filePath)
		baseName = strings.TrimSuffix(baseName, filepath.Ext(baseName))
		outputPath := filepath.Join(options.OutputDir, baseName+"."+options.OutputFormat)

		// Save in new format
		switch strings.ToLower(options.OutputFormat) {
		case "png":
			err = SavePNG(img, outputPath)
		case "jpg", "jpeg":
			err = SaveJPEG(img, outputPath, options.Quality)
		default:
			err = SaveImage(img, outputPath)
		}

		if err != nil {
			return nil, fmt.Errorf("error saving %s: %v", outputPath, err)
		}

		converted = append(converted, outputPath)
	}

	return converted, nil
}

// ConvertDirectoryRecursive converts all images in directory recursively
func ConvertDirectoryRecursive(inputDir, outputDir, outputFormat string) error {
	return filepath.Walk(inputDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		// Check if it's an image file
		ext := strings.ToLower(filepath.Ext(path))
		if ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif" {
			// Calculate relative path
			relPath, err := filepath.Rel(inputDir, path)
			if err != nil {
				return err
			}

			// Create output path
			outputPath := filepath.Join(outputDir, relPath)
			outputPath = strings.TrimSuffix(outputPath, ext) + "." + outputFormat

			// Ensure output directory exists
			outputDirPath := filepath.Dir(outputPath)
			if err := os.MkdirAll(outputDirPath, 0755); err != nil {
				return err
			}

			// Convert
			if err := ConvertFormat(path, outputPath); err != nil {
				return fmt.Errorf("error converting %s: %v", path, err)
			}
		}

		return nil
	})
}

// 4. Format Detection

// DetectFormat detects image format from file
func DetectFormat(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", fmt.Errorf("error opening file: %v", err)
	}
	defer file.Close()

	_, format, err := image.Decode(file)
	if err != nil {
		return "", fmt.Errorf("error decoding image: %v", err)
	}

	return format, nil
}

// DetectFormatFromExtension detects format from file extension
func DetectFormatFromExtension(filePath string) string {
	ext := strings.ToLower(filepath.Ext(filePath))
	switch ext {
	case ".png":
		return "png"
	case ".jpg", ".jpeg":
		return "jpeg"
	case ".gif":
		return "gif"
	default:
		return "unknown"
	}
}

// 5. Conversion Utilities

// ConvertWithMetadata converts image and preserves metadata
func ConvertWithMetadata(inputPath, outputPath string) error {
	img, format, err := LoadImage(inputPath)
	if err != nil {
		return fmt.Errorf("error loading image: %v", err)
	}

	// Save in new format
	switch strings.ToLower(filepath.Ext(outputPath)) {
	case ".png":
		err = png.Encode(outputFile, img)
	case ".jpg", ".jpeg":
		err = jpeg.Encode(outputFile, img, &jpeg.Options{Quality: 95})
	}

	return err
}

// ConvertAndResize converts and resizes in one operation
func ConvertAndResize(inputPath, outputPath string, width, height int) error {
	img, _, err := LoadImage(inputPath)
	if err != nil {
		return fmt.Errorf("error loading image: %v", err)
	}

	// Resize
	resized := ResizeBilinear(img, width, height)

	// Save
	return SaveImage(resized, outputPath)
}

// 6. Optimization

// OptimizePNG optimizes PNG by reducing color palette if possible
func OptimizePNG(inputPath, outputPath string) error {
	img, _, err := LoadImage(inputPath)
	if err != nil {
		return fmt.Errorf("error loading image: %v", err)
	}

	// Save with optimal settings
	file, err := os.Create(outputPath)
	if err != nil {
		return fmt.Errorf("error creating file: %v", err)
	}
	defer file.Close()

	return png.Encode(file, img)
}

// OptimizeJPEG optimizes JPEG by finding optimal quality
func OptimizeJPEG(inputPath, outputPath string, minQuality, maxQuality int) error {
	img, _, err := LoadImage(inputPath)
	if err != nil {
		return fmt.Errorf("error loading image: %v", err)
	}

	// Try different qualities
	bestQuality := minQuality
	minSize := int64(^uint64(0) >> 1)

	for quality := minQuality; quality <= maxQuality; quality += 5 {
		tempPath := outputPath + ".tmp"

		file, err := os.Create(tempPath)
		if err != nil {
			continue
		}

		err = jpeg.Encode(file, img, &jpeg.Options{Quality: quality})
		file.Close()

		if err != nil {
			continue
		}

		info, _ := os.Stat(tempPath)
		if info.Size() < minSize {
			minSize = info.Size()
			bestQuality = quality
		}

		os.Remove(tempPath)
	}

	// Save with best quality
	return SaveJPEG(img, outputPath, bestQuality)
}

// 7. Conversion Statistics

// ConversionStats holds statistics about conversion
type ConversionStats struct {
	TotalFiles    int
	SuccessCount  int
	FailureCount  int
	InputSize     int64
	OutputSize    int64
	TimeTaken     string
}

// ConvertBatchWithStats converts and returns statistics
func ConvertBatchWithStats(options ConversionOptions) (*ConversionStats, error) {
	start := time.Now()
	stats := &ConversionStats{}

	files, _ := filepath.Glob(filepath.Join(options.InputDir, "*."+options.InputFormat))
	stats.TotalFiles = len(files)

	for _, filePath := range files {
		info, _ := os.Stat(filePath)
		stats.InputSize += info.Size()

		baseName := filepath.Base(filePath)
		baseName = strings.TrimSuffix(baseName, filepath.Ext(baseName))
		outputPath := filepath.Join(options.OutputDir, baseName+"."+options.OutputFormat)

		err := ConvertFormat(filePath, outputPath)
		if err != nil {
			stats.FailureCount++
		} else {
			stats.SuccessCount++
			info, _ = os.Stat(outputPath)
			stats.OutputSize += info.Size()
		}
	}

	stats.TimeTaken = time.Since(start).String()
	return stats, nil
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go Image Format Conversion Examples ===\n")

	// 1. Basic conversion
	fmt.Println("--- 1. Basic Conversion ---")
	err := ConvertFormat("input.png", "output.jpg")
	if err == nil {
		fmt.Println("Converted PNG to JPEG")
	}

	// 2. Convert with quality
	fmt.Println("\n--- 2. Convert with Quality ---")
	err = ConvertToJPEG("input.png", "output_q80.jpg", 80)
	if err == nil {
		fmt.Println("Converted with quality 80")
	}

	// 3. Multiple quality levels
	fmt.Println("\n--- 3. Multiple Quality Levels ---")
	qualities := []int{60, 75, 90, 100}
	err = ConvertToJPEGMultipleQualities("input.png", "output", qualities)
	if err == nil {
		fmt.Printf("Generated %d quality versions\n", len(qualities))
	}

	// 4. Batch conversion
	fmt.Println("\n--- 4. Batch Conversion ---")
	options := ConversionOptions{
		InputDir:     "input",
		OutputDir:    "output",
		InputFormat:  "png",
		OutputFormat: "jpg",
		Quality:      85,
	}
	converted, err := ConvertBatch(options)
	if err == nil {
		fmt.Printf("Converted %d files\n", len(converted))
	}

	// 5. Format detection
	fmt.Println("\n--- 5. Format Detection ---")
	format, _ := DetectFormat("input.png")
	fmt.Printf("Detected format: %s\n", format)

	// 6. Convert and resize
	fmt.Println("\n--- 6. Convert and Resize ---")
	err = ConvertAndResize("input.png", "output_small.jpg", 200, 200)
	if err == nil {
		fmt.Println("Converted and resized")
	}

	// 7. Optimization
	fmt.Println("\n--- 7. Optimization ---")
	err = OptimizeJPEG("input.jpg", "optimized.jpg", 70, 95)
	if err == nil {
		fmt.Println("Optimized JPEG")
	}

	// 8. Statistics
	fmt.Println("\n--- 8. Conversion Statistics ---")
	stats, _ := ConvertBatchWithStats(options)
	fmt.Printf("Total: %d, Success: %d, Failed: %d\n",
		stats.TotalFiles, stats.SuccessCount, stats.FailureCount)
	fmt.Printf("Size: %d -> %d bytes\n", stats.InputSize, stats.OutputSize)

	fmt.Println("\n=== All Format Conversion Examples Completed ===")
}

💻 Изменение Размера Изображений go

🟡 intermediate ⭐⭐⭐⭐

Изменять размер изображений с различными методами интерполяции и сохранением соотношения сторон

⏱️ 35 min 🏷️ go, web, image processing
Prerequisites: Intermediate Go, image package, math
// Web Go Image Resizing Examples
// Image resizing with different algorithms

package main

import (
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"math"
)

// 1. Basic Resizing (Nearest Neighbor)

// ResizeNearestNeighbor resizes an image using nearest neighbor algorithm
func ResizeNearestNeighbor(img image.Image, newWidth, newHeight int) image.Image {
	srcBounds := img.Bounds()
	dst := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight))

	xRatio := float64(srcBounds.Dx()) / float64(newWidth)
	yRatio := float64(srcBounds.Dy()) / float64(newHeight)

	for y := 0; y < newHeight; y++ {
		for x := 0; x < newWidth; x++ {
			srcX := int(math.Floor(float64(x) * xRatio))
			srcY := int(math.Floor(float64(y) * yRatio))
			dst.Set(x, y, img.At(srcBounds.Min.X+srcX, srcBounds.Min.Y+srcY))
		}
	}

	return dst
}

// 2. Bilinear Interpolation

// ResizeBilinear resizes an image using bilinear interpolation
func ResizeBilinear(img image.Image, newWidth, newHeight int) image.Image {
	srcBounds := img.Bounds()
	dst := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight))

	xRatio := float64(srcBounds.Dx()-1) / float64(newWidth)
	yRatio := float64(srcBounds.Dy()-1) / float64(newHeight)

	for y := 0; y < newHeight; y++ {
		for x := 0; x < newWidth; x++ {
			srcX := float64(x) * xRatio
			srcY := float64(y) * yRatio

			x1 := int(math.Floor(srcX))
			y1 := int(math.Floor(srcY))
			x2 := x1 + 1
			y2 := y1 + 1

			// Clamp coordinates
			if x2 >= srcBounds.Dx() {
				x2 = srcBounds.Dx() - 1
			}
			if y2 >= srcBounds.Dy() {
				y2 = srcBounds.Dy() - 1
			}

			// Bilinear interpolation
			c1 := img.At(srcBounds.Min.X+x1, srcBounds.Min.Y+y1)
			c2 := img.At(srcBounds.Min.X+x2, srcBounds.Min.Y+y1)
			c3 := img.At(srcBounds.Min.X+x1, srcBounds.Min.Y+y2)
			c4 := img.At(srcBounds.Min.X+x2, srcBounds.Min.Y+y2)

			dx := srcX - float64(x1)
			dy := srcY - float64(y1)

			color := interpolateColor(c1, c2, c3, c4, dx, dy)
			dst.Set(x, y, color)
		}
	}

	return dst
}

func interpolateColor(c1, c2, c3, c4 color.Color, dx, dy float64) color.Color {
	r1, g1, b1, a1 := c1.RGBA()
	r2, g2, b2, a2 := c2.RGBA()
	r3, g3, b3, a3 := c3.RGBA()
	r4, g4, b4, a4 := c4.RGBA()

	// Interpolate top row
	rTop := interpolateValue(r1, r2, dx)
	gTop := interpolateValue(g1, g2, dx)
	bTop := interpolateValue(b1, b2, dx)
	aTop := interpolateValue(a1, a2, dx)

	// Interpolate bottom row
	rBot := interpolateValue(r3, r4, dx)
	gBot := interpolateValue(g3, g4, dx)
	bBot := interpolateValue(b3, b4, dx)
	aBot := interpolateValue(a3, a4, dx)

	// Interpolate vertically
	r := interpolateValue(rTop, rBot, dy)
	g := interpolateValue(gTop, gBot, dy)
	b := interpolateValue(bTop, bBot, dy)
	a := interpolateValue(aTop, aBot, dy)

	return color.RGBA{
		R: uint8(r >> 8),
		G: uint8(g >> 8),
		B: uint8(b >> 8),
		A: uint8(a >> 8),
	}
}

func interpolateValue(v1, v2 uint32, t float64) uint32 {
	return uint32(float64(v1)*(1-t) + float64(v2)*t)
}

// 3. Aspect Ratio Preservation

// ResizeFit resizes image to fit within dimensions while preserving aspect ratio
func ResizeFit(img image.Image, maxWidth, maxHeight int) image.Image {
	srcBounds := img.Bounds()
	srcWidth := srcBounds.Dx()
	srcHeight := srcBounds.Dy()

	// Calculate scaling factors
	widthRatio := float64(maxWidth) / float64(srcWidth)
	heightRatio := float64(maxHeight) / float64(srcHeight)
	scale := math.Min(widthRatio, heightRatio)

	newWidth := int(float64(srcWidth) * scale)
	newHeight := int(float64(srcHeight) * scale)

	return ResizeBilinear(img, newWidth, newHeight)
}

// ResizeFill resizes image to fill dimensions while preserving aspect ratio (crops overflow)
func ResizeFill(img image.Image, targetWidth, targetHeight int) image.Image {
	srcBounds := img.Bounds()
	srcWidth := srcBounds.Dx()
	srcHeight := srcBounds.Dy()

	// Calculate scaling factors
	widthRatio := float64(targetWidth) / float64(srcWidth)
	heightRatio := float64(targetHeight) / float64(srcHeight)
	scale := math.Max(widthRatio, heightRatio)

	resizedWidth := int(float64(srcWidth) * scale)
	resizedHeight := int(float64(srcHeight) * scale)

	resized := ResizeBilinear(img, resizedWidth, resizedHeight)

	// Crop to target size
	xOffset := (resizedWidth - targetWidth) / 2
	yOffset := (resizedHeight - targetHeight) / 2

	return CropImage(resized, image.Rect(xOffset, yOffset, xOffset+targetWidth, yOffset+targetHeight))
}

// ResizeExact resizes image to exact dimensions (may distort)
func ResizeExact(img image.Image, width, height int) image.Image {
	return ResizeBilinear(img, width, height)
}

// 4. Thumbnail Generation

// GenerateThumbnail generates a thumbnail with max dimensions
func GenerateThumbnail(img image.Image, maxSize int) image.Image {
	srcBounds := img.Bounds()
	srcWidth := srcBounds.Dx()
	srcHeight := srcBounds.Dy()

	// Determine dimensions
	var width, height int
	if srcWidth > srcHeight {
		width = maxSize
		height = int(float64(srcHeight) * float64(maxSize) / float64(srcWidth))
	} else {
		height = maxSize
		width = int(float64(srcWidth) * float64(maxSize) / float64(srcHeight))
	}

	return ResizeBilinear(img, width, height)
}

// GenerateSquareThumbnail generates a square thumbnail (crops center)
func GenerateSquareThumbnail(img image.Image, size int) image.Image {
	return ResizeFill(img, size, size)
}

// 5. Scale by Percentage

// ScaleByPercentage scales image by percentage
func ScaleByPercentage(img image.Image, percentage float64) image.Image {
	srcBounds := img.Bounds()
	newWidth := int(float64(srcBounds.Dx()) * percentage / 100.0)
	newHeight := int(float64(srcBounds.Dy()) * percentage / 100.0)

	return ResizeBilinear(img, newWidth, newHeight)
}

// ScaleByFactor scales image by factor
func ScaleByFactor(img image.Image, factor float64) image.Image {
	srcBounds := img.Bounds()
	newWidth := int(float64(srcBounds.Dx()) * factor)
	newHeight := int(float64(srcBounds.Dy()) * factor)

	return ResizeBilinear(img, newWidth, newHeight)
}

// 6. Batch Resizing

// ResizeBatch resizes multiple images
func ResizeBatch(inputPaths []string, outputDir string, width, height int) error {
	for _, inputPath := range inputPaths {
		img, _, err := LoadImage(inputPath)
		if err != nil {
			return err
		}

		resized := ResizeBilinear(img, width, height)

		baseName := filepath.Base(inputPath)
		baseName = strings.TrimSuffix(baseName, filepath.Ext(baseName))
		outputPath := filepath.Join(outputDir, baseName+"_resized.png")

		if err := SavePNG(resized, outputPath); err != nil {
			return err
		}
	}

	return nil
}

// GenerateThumbnailsBatch generates thumbnails for multiple images
func GenerateThumbnailsBatch(inputPaths []string, outputDir string, size int) error {
	for _, inputPath := range inputPaths {
		img, _, err := LoadImage(inputPath)
		if err != nil {
			return err
		}

		thumbnail := GenerateThumbnail(img, size)

		baseName := filepath.Base(inputPath)
		baseName = strings.TrimSuffix(baseName, filepath.Ext(baseName))
		outputPath := filepath.Join(outputDir, baseName+"_thumb.png")

		if err := SavePNG(thumbnail, outputPath); err != nil {
			return err
		}
	}

	return nil
}

// 7. Special Resizing Operations

// ResizeToWidth resizes to specific width (maintains aspect ratio)
func ResizeToWidth(img image.Image, width int) image.Image {
	srcBounds := img.Bounds()
	srcWidth := srcBounds.Dx()
	srcHeight := srcBounds.Dy()

	ratio := float64(width) / float64(srcWidth)
	height := int(float64(srcHeight) * ratio)

	return ResizeBilinear(img, width, height)
}

// ResizeToHeight resizes to specific height (maintains aspect ratio)
func ResizeToHeight(img image.Image, height int) image.Image {
	srcBounds := img.Bounds()
	srcWidth := srcBounds.Dx()
	srcHeight := srcBounds.Dy()

	ratio := float64(height) / float64(srcHeight)
	width := int(float64(srcWidth) * ratio)

	return ResizeBilinear(img, width, height)
}

// ResizeMultipleScales generates multiple scaled versions
func ResizeMultipleScales(img image.Image, scales []float64) map[float64]image.Image {
	results := make(map[float64]image.Image)

	for _, scale := range scales {
		results[scale] = ScaleByFactor(img, scale)
	}

	return results
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go Image Resizing Examples ===\n")

	// Load image
	img, _, err := LoadImage("input.jpg")
	if err != nil {
		fmt.Printf("Error loading image: %v\n", err)
		return
	}

	// 1. Nearest neighbor resize
	fmt.Println("--- 1. Nearest Neighbor Resize ---")
	nearest := ResizeNearestNeighbor(img, 200, 200)
	SavePNG(nearest, "nearest.png")
	fmt.Println("Resized using nearest neighbor")

	// 2. Bilinear resize
	fmt.Println("\n--- 2. Bilinear Resize ---")
	bilinear := ResizeBilinear(img, 300, 300)
	SavePNG(bilinear, "bilinear.png")
	fmt.Println("Resized using bilinear interpolation")

	// 3. Resize fit
	fmt.Println("\n--- 3. Resize Fit ---")
	fit := ResizeFit(img, 200, 200)
	SavePNG(fit, "fit.png")
	fmt.Printf("Fit dimensions: %v\n", fit.Bounds().Dx(), fit.Bounds().Dy())

	// 4. Resize fill
	fmt.Println("\n--- 4. Resize Fill ---")
	fill := ResizeFill(img, 200, 200)
	SavePNG(fill, "fill.png")
	fmt.Println("Resized with fill (crop)")

	// 5. Thumbnail
	fmt.Println("\n--- 5. Thumbnail ---")
	thumbnail := GenerateThumbnail(img, 150)
	SavePNG(thumbnail, "thumbnail.png")
	fmt.Printf("Thumbnail dimensions: %v\n", thumbnail.Bounds())

	// 6. Square thumbnail
	fmt.Println("\n--- 6. Square Thumbnail ---")
	squareThumb := GenerateSquareThumbnail(img, 150)
	SavePNG(squareThumb, "square_thumb.png")
	fmt.Println("Created square thumbnail")

	// 7. Scale by percentage
	fmt.Println("\n--- 7. Scale by Percentage ---")
	scaled := ScaleByPercentage(img, 50.0)
	SavePNG(scaled, "scaled_50.png")
	fmt.Printf("Scaled to 50%%: %v\n", scaled.Bounds())

	// 8. Resize to width
	fmt.Println("\n--- 8. Resize to Width ---")
	widthResized := ResizeToWidth(img, 300)
	SavePNG(widthResized, "width_300.png")
	fmt.Printf("Resized to width 300: %v\n", widthResized.Bounds())

	// 9. Multiple scales
	fmt.Println("\n--- 9. Multiple Scales ---")
	scales := []float64{0.25, 0.5, 0.75, 1.5}
	multi := ResizeMultipleScales(img, scales)
	fmt.Printf("Generated %d scaled versions\n", len(multi))

	// 10. Batch resize
	fmt.Println("\n--- 10. Batch Resize ---")
	images := []string{"img1.jpg", "img2.jpg", "img3.jpg"}
	err = ResizeBatch(images, "resized", 200, 200)
	if err == nil {
		fmt.Println("Batch resize completed")
	}

	fmt.Println("\n=== All Image Resizing Examples Completed ===")
}