diff --git a/data.go b/data.go index 5df4a69..54c172b 100644 --- a/data.go +++ b/data.go @@ -20,6 +20,7 @@ const ( optFormatJPEG = "jpeg" optFormatPNG = "png" optFormatTIFF = "tiff" + optPrimitivePrefix = "p" optRotatePrefix = "r" optQualityPrefix = "q" optSignaturePrefix = "s" @@ -80,6 +81,11 @@ type Options struct { // Automatically find good crop points based on image content. SmartCrop bool + + Primitive struct { + Count int + Mode int + } } func (o Options) String() string { @@ -123,6 +129,9 @@ func (o Options) String() string { if o.SmartCrop { opts = append(opts, optSmartCrop) } + if o.Primitive.Count != 0 { + opts = append(opts, fmt.Sprintf("%s%d:%d", optPrimitivePrefix, o.Primitive.Count, o.Primitive.Mode)) + } sort.Strings(opts) return strings.Join(opts, ",") } @@ -132,7 +141,7 @@ func (o Options) String() string { // the presence of other fields (like Fit). A non-empty Format value is // assumed to involve a transformation. func (o Options) transform() bool { - return o.Width != 0 || o.Height != 0 || o.Rotate != 0 || o.FlipHorizontal || o.FlipVertical || o.Quality != 0 || o.Format != "" || o.CropX != 0 || o.CropY != 0 || o.CropWidth != 0 || o.CropHeight != 0 + return o.Width != 0 || o.Height != 0 || o.Rotate != 0 || o.FlipHorizontal || o.FlipVertical || o.Quality != 0 || o.Format != "" || o.CropX != 0 || o.CropY != 0 || o.CropWidth != 0 || o.CropHeight != 0 || o.SmartCrop || o.Primitive.Count != 0 } // ParseOptions parses str as a list of comma separated transformation options. @@ -271,6 +280,13 @@ func ParseOptions(str string) Options { case strings.HasPrefix(opt, optCropHeight): value := strings.TrimPrefix(opt, optCropHeight) options.CropHeight, _ = strconv.ParseFloat(value, 64) + case strings.HasPrefix(opt, optPrimitivePrefix): + value := strings.TrimPrefix(opt, optPrimitivePrefix) + parts := strings.Split(value, ":") + if len(parts) >= 2 { + options.Primitive.Count, _ = strconv.Atoi(parts[0]) + options.Primitive.Mode, _ = strconv.Atoi(parts[1]) + } case strings.Contains(opt, optSizeDelimiter): size := strings.SplitN(opt, optSizeDelimiter, 2) if w := size[0]; w != "" { diff --git a/go.mod b/go.mod index 27a28de..a1acc0c 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,9 @@ require ( github.com/disintegration/imaging v1.6.2 github.com/dnaeon/go-vcr v1.0.1 // indirect github.com/fcjr/aia-transport-go v1.2.2 + github.com/fogleman/gg v1.3.0 // indirect + github.com/fogleman/primitive v0.0.0-20190214200932-673f57e7b1b5 + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/gomodule/redigo v2.0.0+incompatible github.com/gorilla/mux v1.8.0 github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 diff --git a/go.sum b/go.sum index ba8a2b7..1704c2d 100644 --- a/go.sum +++ b/go.sum @@ -100,6 +100,10 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fcjr/aia-transport-go v1.2.2 h1:sIZqXcM+YhTd2BDtkV2OJaqbcIVcPv1oKru3VJPIPc8= github.com/fcjr/aia-transport-go v1.2.2/go.mod h1:onSqSq3tGkM14WusDx7q9FTheS9R1KBtD+QBWI6zG/w= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fogleman/primitive v0.0.0-20190214200932-673f57e7b1b5 h1:/oy2PBMZq4jeEfqM5OCuvWYu+6yZEb0SSPbwL1fCaXw= +github.com/fogleman/primitive v0.0.0-20190214200932-673f57e7b1b5/go.mod h1:Tm6t8LbdhSCXNfpjTwoL1mdjCnyKHkMyf6PqQXo7Or8= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -113,6 +117,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= diff --git a/transform.go b/transform.go index 103e20f..e7804ff 100644 --- a/transform.go +++ b/transform.go @@ -13,8 +13,10 @@ import ( "io" "log" "math" + "runtime" "github.com/disintegration/imaging" + "github.com/fogleman/primitive/primitive" "github.com/muesli/smartcrop" "github.com/muesli/smartcrop/nfnt" "github.com/prometheus/client_golang/prometheus" @@ -309,5 +311,25 @@ func transformImage(m image.Image, opt Options) image.Image { m = imaging.FlipH(m) } + if opt.Primitive.Count > 0 { + model := transformPrimitive(m, opt) + m = model.Context.Image() + } + return m } + +func transformPrimitive(m image.Image, opt Options) *primitive.Model { + // set size to the longest of height or width + size := m.Bounds().Size().X + if h := m.Bounds().Size().Y; size < h { + size = h + } + + bg := primitive.MakeColor(primitive.AverageImageColor(m)) + model := primitive.NewModel(m, bg, size, runtime.NumCPU()) + for i := 0; i < opt.Primitive.Count; i++ { + model.Step(primitive.ShapeType(opt.Primitive.Mode), 128, 0) + } + return model +}