package img import ( "bytes" "encoding/base64" "image" "image/color" "image/jpeg" _ "image/jpeg" "image/png" _ "image/png" "math" "os" "strconv" "github.com/disintegration/imaging" "github.com/spf13/cast" ) type ImageInfo struct { Path string `json:"path"` Base64 string `json:"base64"` Type string `json:"type"` Width int `json:"width"` Height int `json:"height"` Image image.Image `json:"-"` } type ImageEffect struct { Name string `json:"name"` Settings map[string]any `json:"settings"` } func GetImageInfo(file string) *ImageInfo { content, err := os.ReadFile(file) if err != nil { return nil } reader, err := os.Open(file) if err != nil { return nil } defer reader.Close() i, t, err := image.Decode(reader) if err != nil { return nil } info := ImageInfo{ Path: file, Base64: base64.StdEncoding.EncodeToString(content), Type: t, Width: i.Bounds().Dx(), Height: i.Bounds().Dy(), Image: i, } return &info } func ApplyFilters(src *ImageInfo, effects []ImageEffect) *ImageInfo { base := GetImageInfo(src.Path) result := base.Image quality := 100 for _, effect := range effects { if effect.Name == "scale-width" { width := cast.ToFloat64(effect.Settings["value"]) height := cast.ToInt(math.Round(float64(result.Bounds().Dy()) * width / float64(result.Bounds().Dx()))) result = imaging.Resize( result, cast.ToInt(width), cast.ToInt(height), imaging.Lanczos, ) } else if effect.Name == "scale-height" { height := cast.ToFloat64(effect.Settings["value"]) width := cast.ToInt(math.Round(float64(result.Bounds().Dx()) * height / float64(result.Bounds().Dy()))) result = imaging.Resize( result, cast.ToInt(width), cast.ToInt(height), imaging.Lanczos, ) } else if effect.Name == "crop" { result = imaging.Crop( result, image.Rect( cast.ToInt(effect.Settings["x0"]), cast.ToInt(effect.Settings["y0"]), cast.ToInt(effect.Settings["x1"]), cast.ToInt(effect.Settings["y1"]), ), ) } else if effect.Name == "crop-center" { result = imaging.CropCenter( result, cast.ToInt(effect.Settings["width"]), cast.ToInt(effect.Settings["height"]), ) } else if effect.Name == "resize" { result = imaging.Resize( result, cast.ToInt(effect.Settings["width"]), cast.ToInt(effect.Settings["height"]), imaging.Lanczos, ) } else if effect.Name == "brightness" { result = imaging.AdjustBrightness(result, cast.ToFloat64(effect.Settings["value"])) } else if effect.Name == "contrast" { result = imaging.AdjustContrast(result, cast.ToFloat64(effect.Settings["value"])) } else if effect.Name == "saturation" { result = imaging.AdjustSaturation(result, cast.ToFloat64(effect.Settings["value"])) } else if effect.Name == "gamma" { result = imaging.AdjustGamma(result, cast.ToFloat64(effect.Settings["value"])) } else if effect.Name == "blur" { result = imaging.Blur(result, cast.ToFloat64(effect.Settings["value"])) } else if effect.Name == "flip-v" { result = imaging.FlipV(result) } else if effect.Name == "flip-h" { result = imaging.FlipH(result) } else if effect.Name == "rotate-90" { result = imaging.Rotate90(result) } else if effect.Name == "rotate-180" { result = imaging.Rotate180(result) } else if effect.Name == "rotate-270" { result = imaging.Rotate270(result) } else if effect.Name == "quality" { quality = cast.ToInt(effect.Settings["value"]) } else if effect.Name == "rotate" { values, err := strconv.ParseUint(string(effect.Settings["backgroundColor"].(string)[1:]), 16, 32) if err == nil { result = imaging.Rotate( result, cast.ToFloat64(effect.Settings["value"]), color.RGBA{ R: uint8(values >> 16), G: uint8((values >> 8) & 0xFF), B: uint8(values & 0xFF), A: 255, }, ) } } } buff := new(bytes.Buffer) if src.Type == "jpeg" { err := jpeg.Encode(buff, result, &jpeg.Options{ Quality: quality, }) if err != nil { return nil } } else { err := png.Encode(buff, result) if err != nil { return nil } } return &ImageInfo{ Path: "", Base64: base64.StdEncoding.EncodeToString(buff.Bytes()), Type: base.Type, Width: result.Bounds().Dx(), Height: result.Bounds().Dy(), Image: result, } }