package util import ( "bytes" "fmt" "image" "image/draw" "image/jpeg" _ "image/png" // Register PNG decoder just in case ) // GenerateThumbnail creates a thumbnail by cropping the original image. // The thumbnail size is 1/2 of the original dimensions. // The crop is centered on the provided coordinates (centerX, centerY), constrained to image bounds. func GenerateThumbnail(srcData []byte, centerX, centerY int) ([]byte, error) { // 1. Decode image img, _, err := image.Decode(bytes.NewReader(srcData)) if err != nil { return nil, fmt.Errorf("decode image failed: %v", err) } bounds := img.Bounds() origW := bounds.Dx() origH := bounds.Dy() // 2. Calculate Target Dimensions (1/2 of original) targetW := origW / 2 targetH := origH / 2 if targetW == 0 || targetH == 0 { return nil, fmt.Errorf("image too small to generate half-size thumbnail") } // 3. Calculate Crop Origin (Top-Left) // cropX = centerX - targetW / 2 cropX := centerX - targetW/2 cropY := centerY - targetH/2 // 4. Constrain to Bounds if cropX < 0 { cropX = 0 } if cropY < 0 { cropY = 0 } if cropX+targetW > origW { cropX = origW - targetW } if cropY+targetH > origH { cropY = origH - targetH } // Double check after adjustment if cropX < 0 { cropX = 0 } if cropY < 0 { cropY = 0 } // 5. Perform Crop cropRect := image.Rect(cropX, cropY, cropX+targetW, cropY+targetH) var dst image.Image // Try to use SubImage if supported for performance type SubImager interface { SubImage(r image.Rectangle) image.Image } if sub, ok := img.(SubImager); ok { dst = sub.SubImage(cropRect) } else { // Fallback: create new image and draw rgba := image.NewRGBA(image.Rect(0, 0, targetW, targetH)) draw.Draw(rgba, rgba.Bounds(), img, cropRect.Min, draw.Src) dst = rgba } // 6. Encode to JPEG var buf bytes.Buffer err = jpeg.Encode(&buf, dst, &jpeg.Options{Quality: 85}) if err != nil { return nil, fmt.Errorf("encode thumbnail failed: %v", err) } return buf.Bytes(), nil } // Helper to process using LeftTop and RightBottom coordinates func GenerateThumbnailFromCoords(srcData []byte, ltX, ltY, rbX, rbY int) ([]byte, error) { // Calculate center based on the provided coordinates centerX := (ltX + rbX) / 2 centerY := (ltY + rbY) / 2 return GenerateThumbnail(srcData, centerX, centerY) }