diff --git a/render/image.go b/render/image.go index 1e9605a..e3dddcb 100644 --- a/render/image.go +++ b/render/image.go @@ -6,6 +6,7 @@ import ( _ "image/gif" // register gif, jpeg, png format handlers _ "image/jpeg" "image/png" + "math" "os" "golang.org/x/image/draw" @@ -63,10 +64,28 @@ func ImgColorFilter(img *image.NRGBA, from, to color.NRGBA) *image.NRGBA { return r } -func ScaleImage(img image.Image, factor int) (scaled *image.NRGBA) { +func ScaleImage(img image.Image, factorX, factorY float64, highQuality bool) (scaled *image.NRGBA) { b := img.Bounds() - scaledBounds := image.Rect(0, 0, b.Max.X*factor, b.Max.Y*factor) + newX := int(math.Ceil(factorX * float64(b.Max.X))) + newY := int(math.Ceil(factorY * float64(b.Max.Y))) + scaledBounds := image.Rect(0, 0, newX, newY) scaledImg := image.NewNRGBA(scaledBounds) - draw.NearestNeighbor.Scale(scaledImg, scaledBounds, img, b, draw.Src, nil) + scaler := draw.NearestNeighbor + if highQuality { + scaler = draw.CatmullRom + } + scaler.Scale(scaledImg, scaledBounds, img, b, draw.Src, nil) return scaledImg } + +func RotateImage90(img *image.NRGBA) (rotated *image.NRGBA) { + b := img.Bounds() + rotated = image.NewNRGBA(image.Rect(0, 0, b.Max.Y, b.Max.X)) + for x := b.Min.X; x < b.Max.X; x++ { + for y := b.Min.Y; y < b.Max.Y; y++ { + col := img.NRGBAAt(x, y) + rotated.SetNRGBA(b.Max.Y-y, x, col) + } + } + return +} diff --git a/render/text.go b/render/text.go index 84a7853..98c13f9 100644 --- a/render/text.go +++ b/render/text.go @@ -16,7 +16,7 @@ func pt(p fixed.Point26_6) image.Point { } } -func RenderText(text string, scale int, texture, bgTex image.Image) *image.NRGBA { +func RenderText(text string, scale float64, texture, bgTex image.Image) *image.NRGBA { face := basicfont.Face7x13 stringBounds, _ := font.BoundString(face, text) @@ -38,5 +38,5 @@ func RenderText(text string, scale int, texture, bgTex image.Image) *image.NRGBA img.Rect = img.Bounds().Sub(img.Bounds().Min) // scale up, as this font is quite small - return ScaleImage(img, scale) + return ScaleImage(img, scale, scale, false) } diff --git a/rpc/repl.go b/rpc/repl.go index eddeeff..cb3d5ea 100644 --- a/rpc/repl.go +++ b/rpc/repl.go @@ -28,7 +28,7 @@ const textMode = "txt" // RunREPL starts reading os.Stdin for commands to apply to the given Fluter func RunREPL(f Fluter) { mode := commandMode - textSize := 10 + textSize := 10.0 var textCol image.Image = image.White var bgCol image.Image = image.Transparent @@ -109,7 +109,7 @@ func RunREPL(f Fluter) { case "txt": if len(args) > 0 { if size, err := strconv.Atoi(args[0]); err == nil { - textSize = size + textSize = float64(size) } } if len(args) > 1 { @@ -138,6 +138,33 @@ func RunREPL(f Fluter) { } } + case "scale", "s": + quality := true + var facX, facY float64 + var err error + if len(args) >= 1 { + facX, err = strconv.ParseFloat(args[0], 64) + if err != nil { + fmt.Println(err) + continue + } + facY = facX + if len(args) >= 2 { + facY, err = strconv.ParseFloat(args[1], 64) + if err != nil { + fmt.Println(err) + continue + } + } + if len(args) > 2 { + quality = false + } + t.Img = render.ScaleImage(t.Img, facX, facY, quality) + } + + case "rotate", "r": + t.Img = render.RotateImage90(t.Img) + // the commands below don't affect the task, so we don't need to apply it to clients -> continue case "metrics": @@ -176,6 +203,8 @@ func printHelp() { i set image txt send text txt [ [ []] enter interactive text mode + scale [ [lofi]] scale content + rotate rotate content 90° draw modes o set order (l,r,t,b,random) of set top-left offset