Files
orientator/internal/pipeline/imgrot.go
2026-05-13 19:52:51 +03:00

81 lines
2.0 KiB
Go

package pipeline
import (
"bytes"
"fmt"
"image"
"image/jpeg"
"image/png"
"strings"
"golang.org/x/image/tiff"
_ "golang.org/x/image/webp" // decode-only; we re-encode webp as png
)
// RotateImage rotates src clockwise by angle (must be 0/90/180/270).
func RotateImage(src image.Image, angle int) image.Image {
angle = ((angle % 360) + 360) % 360
if angle == 0 {
return src
}
b := src.Bounds()
w, h := b.Dx(), b.Dy()
switch angle {
case 180:
dst := image.NewRGBA(image.Rect(0, 0, w, h))
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
dst.Set(w-1-x, h-1-y, src.At(b.Min.X+x, b.Min.Y+y))
}
}
return dst
case 90:
dst := image.NewRGBA(image.Rect(0, 0, h, w))
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
dst.Set(h-1-y, x, src.At(b.Min.X+x, b.Min.Y+y))
}
}
return dst
case 270:
dst := image.NewRGBA(image.Rect(0, 0, h, w))
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
dst.Set(y, w-1-x, src.At(b.Min.X+x, b.Min.Y+y))
}
}
return dst
}
return src
}
// EncodeImage re-encodes img using format hint from the original filename / detected format.
// Falls back to PNG when format is not directly writable.
func EncodeImage(img image.Image, format string) ([]byte, string, error) {
var buf bytes.Buffer
switch strings.ToLower(format) {
case "jpeg", "jpg":
if err := jpeg.Encode(&buf, img, &jpeg.Options{Quality: 92}); err != nil {
return nil, "", err
}
return buf.Bytes(), "jpg", nil
case "tiff", "tif":
if err := tiff.Encode(&buf, img, &tiff.Options{Compression: tiff.Deflate}); err != nil {
return nil, "", err
}
return buf.Bytes(), "tiff", nil
case "png", "":
if err := png.Encode(&buf, img); err != nil {
return nil, "", err
}
return buf.Bytes(), "png", nil
case "webp":
// Re-encode to PNG (no webp encoder in stdlib/x/image).
if err := png.Encode(&buf, img); err != nil {
return nil, "", err
}
return buf.Bytes(), "png", nil
}
return nil, "", fmt.Errorf("unsupported format: %s", format)
}