From f79bc6bfd4ec7df9a4187477903b58dcbf85cccd Mon Sep 17 00:00:00 2001 From: Jacob Gunther Date: Sat, 13 Jul 2024 11:39:28 -0500 Subject: [PATCH] Add option to make most renders square --- backbody.go | 4 ++ backbody_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++++++ body.go | 4 ++ body_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++++++ cmd/main.go | 4 +- face_test.go | 4 ++ frontbody.go | 4 ++ frontbody_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++++++ head.go | 4 ++ head_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++++++ leftbody.go | 4 ++ leftbody_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++++++ options.go | 1 + rightbody.go | 4 ++ rightbody_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++++++ util.go | 28 +++++++++++ 16 files changed, 756 insertions(+), 1 deletion(-) diff --git a/backbody.go b/backbody.go index 2c88334..04a6441 100644 --- a/backbody.go +++ b/backbody.go @@ -63,5 +63,9 @@ func RenderBackBody(img image.Image, opts Options) *image.NRGBA { // Right Leg composite(output, backRightLeg, 8, 20) + if opts.Square { + output = squareAndCenter(output) + } + return scale(output, opts.Scale) } diff --git a/backbody_test.go b/backbody_test.go index e3f6009..acf2648 100644 --- a/backbody_test.go +++ b/backbody_test.go @@ -19,6 +19,7 @@ func TestBackBodySteve(t *testing.T) { Scale: scale, Overlay: true, Slim: false, + Square: false, }) if output.Bounds().Dx() < 1 { @@ -55,6 +56,7 @@ func BenchmarkBackBodySteve(b *testing.B) { Scale: defaultBenchmarkRenderScale, Overlay: true, Slim: false, + Square: false, }) } } @@ -69,6 +71,7 @@ func TestBackBodyAlex(t *testing.T) { Scale: scale, Overlay: true, Slim: true, + Square: false, }) if output.Bounds().Dx() < 1 { @@ -105,6 +108,119 @@ func BenchmarkBackBodyAlex(b *testing.B) { Scale: defaultBenchmarkRenderScale, Overlay: true, Slim: true, + Square: false, + }) + } +} + +func TestBackBodySteveSquare(t *testing.T) { + rawSkin := skin.GetDefaultSkin(false) + + for i := 0; i <= 8; i++ { + scale := 1 << i + + output := skin.RenderBackBody(rawSkin, skin.Options{ + Scale: scale, + Overlay: true, + Slim: false, + Square: true, + }) + + if output.Bounds().Dx() < 1 { + t.Fatalf("result image width is %d pixels\n", output.Bounds().Dx()) + } + + if output.Bounds().Dy() < 1 { + t.Fatalf("result image height is %d pixels\n", output.Bounds().Dy()) + } + + if output.Bounds().Size().X != output.Bounds().Size().Y { + t.Fatalf("result image is not square (%s)\n", output.Bounds().Size()) + } + + if writeRenders { + f, err := os.OpenFile(fmt.Sprintf("backbody_steve_test_%d_square.png", scale), os.O_CREATE|os.O_RDWR, 0777) + + if err != nil { + t.Fatal(err) + } + + if err = png.Encode(f, output); err != nil { + t.Fatal(err) + } + + if err = f.Close(); err != nil { + t.Fatal(err) + } + } + } +} + +func BenchmarkBackBodySteveSquare(b *testing.B) { + rawSkin := skin.GetDefaultSkin(false) + + for n := 0; n <= b.N; n++ { + skin.RenderBackBody(rawSkin, skin.Options{ + Scale: defaultBenchmarkRenderScale, + Overlay: true, + Slim: false, + Square: true, + }) + } +} + +func TestBackBodyAlexSquare(t *testing.T) { + rawSkin := skin.GetDefaultSkin(true) + + for i := 0; i <= 8; i++ { + scale := 1 << i + + output := skin.RenderBackBody(rawSkin, skin.Options{ + Scale: scale, + Overlay: true, + Slim: true, + Square: true, + }) + + if output.Bounds().Dx() < 1 { + t.Fatalf("result image width is %d pixels\n", output.Bounds().Dx()) + } + + if output.Bounds().Dy() < 1 { + t.Fatalf("result image height is %d pixels\n", output.Bounds().Dy()) + } + + if output.Bounds().Size().X != output.Bounds().Size().Y { + t.Fatalf("result image is not square (%s)\n", output.Bounds().Size()) + } + + if writeRenders { + f, err := os.OpenFile(fmt.Sprintf("backbody_alex_test_%d_square.png", scale), os.O_CREATE|os.O_RDWR, 0777) + + if err != nil { + t.Fatal(err) + } + + if err = png.Encode(f, output); err != nil { + t.Fatal(err) + } + + if err = f.Close(); err != nil { + t.Fatal(err) + } + } + } +} + +func BenchmarkBackBodyAlexSquare(b *testing.B) { + rawSkin := skin.GetDefaultSkin(true) + + for n := 0; n <= b.N; n++ { + skin.RenderBackBody(rawSkin, skin.Options{ + Scale: defaultBenchmarkRenderScale, + Overlay: true, + Slim: true, + Square: true, }) } } diff --git a/body.go b/body.go index a1f8b72..4ced343 100644 --- a/body.go +++ b/body.go @@ -97,5 +97,9 @@ func RenderBody(img *image.NRGBA, opts Options) *image.NRGBA { // Right Side of Head compositeTransform(output, scale(rightHead, opts.Scale), sideMatrix, 2*scaleDouble, 3*scaleDouble) + if opts.Square { + return squareAndCenter(output) + } + return output } diff --git a/body_test.go b/body_test.go index 578155e..defce2a 100644 --- a/body_test.go +++ b/body_test.go @@ -19,6 +19,7 @@ func TestFullBodySteve(t *testing.T) { Scale: scale, Overlay: true, Slim: false, + Square: false, }) if output.Bounds().Dx() < 1 { @@ -55,6 +56,7 @@ func BenchmarkFullBodySteve(b *testing.B) { Scale: defaultBenchmarkRenderScale, Overlay: true, Slim: false, + Square: false, }) } } @@ -69,6 +71,7 @@ func TestFullBodyAlex(t *testing.T) { Scale: scale, Overlay: true, Slim: true, + Square: false, }) if output.Bounds().Dx() < 1 { @@ -105,6 +108,119 @@ func BenchmarkFullBodyAlex(b *testing.B) { Scale: defaultBenchmarkRenderScale, Overlay: true, Slim: true, + Square: false, + }) + } +} + +func TestFullBodySteveSquare(t *testing.T) { + rawSkin := skin.GetDefaultSkin(false) + + for i := 0; i <= 8; i++ { + scale := 1 << i + + output := skin.RenderBody(rawSkin, skin.Options{ + Scale: scale, + Overlay: true, + Slim: false, + Square: true, + }) + + if output.Bounds().Dx() < 1 { + t.Fatalf("result image width is %d pixels\n", output.Bounds().Dx()) + } + + if output.Bounds().Dy() < 1 { + t.Fatalf("result image height is %d pixels\n", output.Bounds().Dy()) + } + + if output.Bounds().Size().X != output.Bounds().Size().Y { + t.Fatalf("result image is not square (%s)\n", output.Bounds().Size()) + } + + if writeRenders { + f, err := os.OpenFile(fmt.Sprintf("fullbody_steve_test_%d_square.png", scale), os.O_CREATE|os.O_RDWR, 0777) + + if err != nil { + t.Fatal(err) + } + + if err = png.Encode(f, output); err != nil { + t.Fatal(err) + } + + if err = f.Close(); err != nil { + t.Fatal(err) + } + } + } +} + +func BenchmarkFullBodySteveSquare(b *testing.B) { + rawSkin := skin.GetDefaultSkin(false) + + for n := 0; n <= b.N; n++ { + skin.RenderBody(rawSkin, skin.Options{ + Scale: defaultBenchmarkRenderScale, + Overlay: true, + Slim: false, + Square: true, + }) + } +} + +func TestFullBodyAlexSquare(t *testing.T) { + rawSkin := skin.GetDefaultSkin(true) + + for i := 0; i <= 8; i++ { + scale := 1 << i + + output := skin.RenderBody(rawSkin, skin.Options{ + Scale: scale, + Overlay: true, + Slim: true, + Square: true, + }) + + if output.Bounds().Dx() < 1 { + t.Fatalf("result image width is %d pixels\n", output.Bounds().Dx()) + } + + if output.Bounds().Dy() < 1 { + t.Fatalf("result image height is %d pixels\n", output.Bounds().Dy()) + } + + if output.Bounds().Size().X != output.Bounds().Size().Y { + t.Fatalf("result image is not square (%s)\n", output.Bounds().Size()) + } + + if writeRenders { + f, err := os.OpenFile(fmt.Sprintf("fullbody_alex_test_%d_square.png", scale), os.O_CREATE|os.O_RDWR, 0777) + + if err != nil { + t.Fatal(err) + } + + if err = png.Encode(f, output); err != nil { + t.Fatal(err) + } + + if err = f.Close(); err != nil { + t.Fatal(err) + } + } + } +} + +func BenchmarkFullBodyAlexSquare(b *testing.B) { + rawSkin := skin.GetDefaultSkin(true) + + for n := 0; n <= b.N; n++ { + skin.RenderBody(rawSkin, skin.Options{ + Scale: defaultBenchmarkRenderScale, + Overlay: true, + Slim: true, + Square: true, }) } } diff --git a/cmd/main.go b/cmd/main.go index 2a2866c..e9ffe2c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -22,6 +22,7 @@ type Options struct { Scale int `short:"s" long:"scale" description:"The scale of the rendered output image" default:"16"` NoOverlay bool `short:"O" long:"no-overlay" description:"Disables the overlay layer of the resulting image"` Slim bool `short:"S" long:"slim" description:"Enable this option if the input skin image is slim"` + Square bool `long:"square" description:"Forces the output image to be square"` Output string `short:"o" long:"output" description:"The file to write the output image to" default:"output.png"` Verbose bool `short:"V" long:"verbose" description:"Prints extra debug information"` } @@ -110,11 +111,12 @@ func main() { } var ( - result *image.NRGBA + result *image.NRGBA = nil options skin.Options = skin.Options{ Scale: opts.Scale, Overlay: !opts.NoOverlay, Slim: opts.Slim, + Square: opts.Square, } ) diff --git a/face_test.go b/face_test.go index 20e321f..a18da58 100644 --- a/face_test.go +++ b/face_test.go @@ -37,6 +37,7 @@ func TestFaceSteve(t *testing.T) { Scale: scale, Overlay: true, Slim: false, + Square: false, }) if output.Bounds().Dx() < 1 { @@ -73,6 +74,7 @@ func BenchmarkFaceSteve(b *testing.B) { Scale: defaultBenchmarkRenderScale, Overlay: true, Slim: false, + Square: false, }) } } @@ -87,6 +89,7 @@ func TestFaceAlex(t *testing.T) { Scale: scale, Overlay: true, Slim: true, + Square: false, }) if output.Bounds().Dx() < 1 { @@ -123,6 +126,7 @@ func BenchmarkFaceAlex(b *testing.B) { Scale: defaultBenchmarkRenderScale, Overlay: true, Slim: true, + Square: false, }) } } diff --git a/frontbody.go b/frontbody.go index 984bf13..0d07d11 100644 --- a/frontbody.go +++ b/frontbody.go @@ -63,5 +63,9 @@ func RenderFrontBody(img *image.NRGBA, opts Options) *image.NRGBA { // Right Leg composite(output, rightLeg, 4, 20) + if opts.Square { + output = squareAndCenter(output) + } + return scale(output, opts.Scale) } diff --git a/frontbody_test.go b/frontbody_test.go index 4ac4669..df680f1 100644 --- a/frontbody_test.go +++ b/frontbody_test.go @@ -19,6 +19,7 @@ func TestFrontBodySteve(t *testing.T) { Scale: scale, Overlay: true, Slim: false, + Square: false, }) if output.Bounds().Dx() < 1 { @@ -55,6 +56,7 @@ func BenchmarkFrontBodySteve(b *testing.B) { Scale: defaultBenchmarkRenderScale, Overlay: true, Slim: false, + Square: false, }) } } @@ -69,6 +71,7 @@ func TestFrontBodyAlex(t *testing.T) { Scale: scale, Overlay: true, Slim: true, + Square: false, }) if output.Bounds().Dx() < 1 { @@ -105,6 +108,119 @@ func BenchmarkFrontBodyAlex(b *testing.B) { Scale: defaultBenchmarkRenderScale, Overlay: true, Slim: true, + Square: false, + }) + } +} + +func TestFrontBodySteveSquare(t *testing.T) { + rawSkin := skin.GetDefaultSkin(false) + + for i := 0; i <= 8; i++ { + scale := 1 << i + + output := skin.RenderFrontBody(rawSkin, skin.Options{ + Scale: scale, + Overlay: true, + Slim: false, + Square: true, + }) + + if output.Bounds().Dx() < 1 { + t.Fatalf("result image width is %d pixels\n", output.Bounds().Dx()) + } + + if output.Bounds().Dy() < 1 { + t.Fatalf("result image height is %d pixels\n", output.Bounds().Dy()) + } + + if output.Bounds().Size().X != output.Bounds().Size().Y { + t.Fatalf("result image is not square (%s)\n", output.Bounds().Size()) + } + + if writeRenders { + f, err := os.OpenFile(fmt.Sprintf("frontbody_steve_test_%d_square.png", scale), os.O_CREATE|os.O_RDWR, 0777) + + if err != nil { + t.Fatal(err) + } + + if err = png.Encode(f, output); err != nil { + t.Fatal(err) + } + + if err = f.Close(); err != nil { + t.Fatal(err) + } + } + } +} + +func BenchmarkFrontBodySteveSquare(b *testing.B) { + rawSkin := skin.GetDefaultSkin(false) + + for n := 0; n <= b.N; n++ { + skin.RenderFrontBody(rawSkin, skin.Options{ + Scale: defaultBenchmarkRenderScale, + Overlay: true, + Slim: false, + Square: true, + }) + } +} + +func TestFrontBodyAlexSquare(t *testing.T) { + rawSkin := skin.GetDefaultSkin(true) + + for i := 0; i <= 8; i++ { + scale := 1 << i + + output := skin.RenderFrontBody(rawSkin, skin.Options{ + Scale: scale, + Overlay: true, + Slim: true, + Square: true, + }) + + if output.Bounds().Dx() < 1 { + t.Fatalf("result image width is %d pixels\n", output.Bounds().Dx()) + } + + if output.Bounds().Dy() < 1 { + t.Fatalf("result image height is %d pixels\n", output.Bounds().Dy()) + } + + if output.Bounds().Size().X != output.Bounds().Size().Y { + t.Fatalf("result image is not square (%s)\n", output.Bounds().Size()) + } + + if writeRenders { + f, err := os.OpenFile(fmt.Sprintf("frontbody_alex_test_%d_square.png", scale), os.O_CREATE|os.O_RDWR, 0777) + + if err != nil { + t.Fatal(err) + } + + if err = png.Encode(f, output); err != nil { + t.Fatal(err) + } + + if err = f.Close(); err != nil { + t.Fatal(err) + } + } + } +} + +func BenchmarkFrontBodyAlexSquare(b *testing.B) { + rawSkin := skin.GetDefaultSkin(true) + + for n := 0; n <= b.N; n++ { + skin.RenderFrontBody(rawSkin, skin.Options{ + Scale: defaultBenchmarkRenderScale, + Overlay: true, + Slim: true, + Square: true, }) } } diff --git a/head.go b/head.go index 32a1248..9259370 100644 --- a/head.go +++ b/head.go @@ -37,5 +37,9 @@ func RenderHead(img *image.NRGBA, opts Options) *image.NRGBA { // Right Head compositeTransform(output, scale(rightHead, opts.Scale), sideMatrix, 0, 4*scaleDouble) + if opts.Square { + return squareAndCenter(output) + } + return output } diff --git a/head_test.go b/head_test.go index aaf46d7..e5d1592 100644 --- a/head_test.go +++ b/head_test.go @@ -19,6 +19,7 @@ func TestHeadSteve(t *testing.T) { Scale: scale, Overlay: true, Slim: false, + Square: false, }) if output.Bounds().Dx() < 1 { @@ -55,6 +56,7 @@ func BenchmarkHeadSteve(b *testing.B) { Scale: defaultBenchmarkRenderScale, Overlay: true, Slim: false, + Square: false, }) } } @@ -69,6 +71,7 @@ func TestHeadAlex(t *testing.T) { Scale: scale, Overlay: true, Slim: true, + Square: false, }) if output.Bounds().Dx() < 1 { @@ -105,6 +108,119 @@ func BenchmarkHeadAlex(b *testing.B) { Scale: defaultBenchmarkRenderScale, Overlay: true, Slim: true, + Square: false, + }) + } +} + +func TestHeadSteveSquare(t *testing.T) { + rawSkin := skin.GetDefaultSkin(false) + + for i := 0; i <= 8; i++ { + scale := 1 << i + + output := skin.RenderHead(rawSkin, skin.Options{ + Scale: scale, + Overlay: true, + Slim: false, + Square: true, + }) + + if output.Bounds().Dx() < 1 { + t.Fatalf("result image width is %d pixels\n", output.Bounds().Dx()) + } + + if output.Bounds().Dy() < 1 { + t.Fatalf("result image height is %d pixels\n", output.Bounds().Dy()) + } + + if output.Bounds().Size().X != output.Bounds().Size().Y { + t.Fatalf("result image is not square (%s)\n", output.Bounds().Size()) + } + + if writeRenders { + f, err := os.OpenFile(fmt.Sprintf("head_steve_test_%d_square.png", scale), os.O_CREATE|os.O_RDWR, 0777) + + if err != nil { + t.Fatal(err) + } + + if err = png.Encode(f, output); err != nil { + t.Fatal(err) + } + + if err = f.Close(); err != nil { + t.Fatal(err) + } + } + } +} + +func BenchmarkHeadSteveSquare(b *testing.B) { + rawSkin := skin.GetDefaultSkin(false) + + for n := 0; n <= b.N; n++ { + skin.RenderHead(rawSkin, skin.Options{ + Scale: defaultBenchmarkRenderScale, + Overlay: true, + Slim: false, + Square: true, + }) + } +} + +func TestHeadAlexSquare(t *testing.T) { + rawSkin := skin.GetDefaultSkin(true) + + for i := 0; i <= 8; i++ { + scale := 1 << i + + output := skin.RenderHead(rawSkin, skin.Options{ + Scale: scale, + Overlay: true, + Slim: true, + Square: true, + }) + + if output.Bounds().Dx() < 1 { + t.Fatalf("result image width is %d pixels\n", output.Bounds().Dx()) + } + + if output.Bounds().Dy() < 1 { + t.Fatalf("result image height is %d pixels\n", output.Bounds().Dy()) + } + + if output.Bounds().Size().X != output.Bounds().Size().Y { + t.Fatalf("result image is not square (%s)\n", output.Bounds().Size()) + } + + if writeRenders { + f, err := os.OpenFile(fmt.Sprintf("head_alex_test_%d_square.png", scale), os.O_CREATE|os.O_RDWR, 0777) + + if err != nil { + t.Fatal(err) + } + + if err = png.Encode(f, output); err != nil { + t.Fatal(err) + } + + if err = f.Close(); err != nil { + t.Fatal(err) + } + } + } +} + +func BenchmarkHeadAlexSquare(b *testing.B) { + rawSkin := skin.GetDefaultSkin(true) + + for n := 0; n <= b.N; n++ { + skin.RenderHead(rawSkin, skin.Options{ + Scale: defaultBenchmarkRenderScale, + Overlay: true, + Slim: true, + Square: true, }) } } diff --git a/leftbody.go b/leftbody.go index f9e4d95..49d599a 100644 --- a/leftbody.go +++ b/leftbody.go @@ -47,5 +47,9 @@ func RenderLeftBody(img *image.NRGBA, opts Options) *image.NRGBA { // Left Leg composite(output, leftLeftLeg, 6, 20) + if opts.Square { + output = squareAndCenter(output) + } + return scale(output, opts.Scale) } diff --git a/leftbody_test.go b/leftbody_test.go index 257fc24..203c866 100644 --- a/leftbody_test.go +++ b/leftbody_test.go @@ -19,6 +19,7 @@ func TestLeftBodySteve(t *testing.T) { Scale: scale, Overlay: true, Slim: false, + Square: false, }) if output.Bounds().Dx() < 1 { @@ -55,6 +56,7 @@ func BenchmarkLeftBodySteve(b *testing.B) { Scale: defaultBenchmarkRenderScale, Overlay: true, Slim: false, + Square: false, }) } } @@ -69,6 +71,7 @@ func TestLeftBodyAlex(t *testing.T) { Scale: scale, Overlay: true, Slim: true, + Square: false, }) if output.Bounds().Dx() < 1 { @@ -105,6 +108,119 @@ func BenchmarkLeftBodyAlex(b *testing.B) { Scale: defaultBenchmarkRenderScale, Overlay: true, Slim: true, + Square: false, + }) + } +} + +func TestLeftBodySteveSquare(t *testing.T) { + rawSkin := skin.GetDefaultSkin(false) + + for i := 0; i <= 8; i++ { + scale := 1 << i + + output := skin.RenderLeftBody(rawSkin, skin.Options{ + Scale: scale, + Overlay: true, + Slim: false, + Square: true, + }) + + if output.Bounds().Dx() < 1 { + t.Fatalf("result image width is %d pixels\n", output.Bounds().Dx()) + } + + if output.Bounds().Dy() < 1 { + t.Fatalf("result image height is %d pixels\n", output.Bounds().Dy()) + } + + if output.Bounds().Size().X != output.Bounds().Size().Y { + t.Fatalf("result image is not square (%s)\n", output.Bounds().Size()) + } + + if writeRenders { + f, err := os.OpenFile(fmt.Sprintf("leftbody_steve_test_%d_square.png", scale), os.O_CREATE|os.O_RDWR, 0777) + + if err != nil { + t.Fatal(err) + } + + if err = png.Encode(f, output); err != nil { + t.Fatal(err) + } + + if err = f.Close(); err != nil { + t.Fatal(err) + } + } + } +} + +func BenchmarkLeftBodySteveSquare(b *testing.B) { + rawSkin := skin.GetDefaultSkin(false) + + for n := 0; n <= b.N; n++ { + skin.RenderLeftBody(rawSkin, skin.Options{ + Scale: defaultBenchmarkRenderScale, + Overlay: true, + Slim: false, + Square: true, + }) + } +} + +func TestLeftBodyAlexSquare(t *testing.T) { + rawSkin := skin.GetDefaultSkin(true) + + for i := 0; i <= 8; i++ { + scale := 1 << i + + output := skin.RenderLeftBody(rawSkin, skin.Options{ + Scale: scale, + Overlay: true, + Slim: true, + Square: true, + }) + + if output.Bounds().Dx() < 1 { + t.Fatalf("result image width is %d pixels\n", output.Bounds().Dx()) + } + + if output.Bounds().Dy() < 1 { + t.Fatalf("result image height is %d pixels\n", output.Bounds().Dy()) + } + + if output.Bounds().Size().X != output.Bounds().Size().Y { + t.Fatalf("result image is not square (%s)\n", output.Bounds().Size()) + } + + if writeRenders { + f, err := os.OpenFile(fmt.Sprintf("leftbody_alex_test_%d_square.png", scale), os.O_CREATE|os.O_RDWR, 0777) + + if err != nil { + t.Fatal(err) + } + + if err = png.Encode(f, output); err != nil { + t.Fatal(err) + } + + if err = f.Close(); err != nil { + t.Fatal(err) + } + } + } +} + +func BenchmarkLeftBodyAlexSquare(b *testing.B) { + rawSkin := skin.GetDefaultSkin(true) + + for n := 0; n <= b.N; n++ { + skin.RenderLeftBody(rawSkin, skin.Options{ + Scale: defaultBenchmarkRenderScale, + Overlay: true, + Slim: true, + Square: true, }) } } diff --git a/options.go b/options.go index 990e07b..333744a 100644 --- a/options.go +++ b/options.go @@ -5,4 +5,5 @@ type Options struct { Scale int Overlay bool Slim bool + Square bool } diff --git a/rightbody.go b/rightbody.go index a1c853a..1cfdb6e 100644 --- a/rightbody.go +++ b/rightbody.go @@ -38,5 +38,9 @@ func RenderRightBody(img *image.NRGBA, opts Options) *image.NRGBA { // Right Leg composite(output, rightRightLeg, 6, 20) + if opts.Square { + output = squareAndCenter(output) + } + return scale(output, opts.Scale) } diff --git a/rightbody_test.go b/rightbody_test.go index dfaaae1..8ea8256 100644 --- a/rightbody_test.go +++ b/rightbody_test.go @@ -19,6 +19,7 @@ func TestRightBodySteve(t *testing.T) { Scale: scale, Overlay: true, Slim: false, + Square: false, }) if output.Bounds().Dx() < 1 { @@ -55,6 +56,7 @@ func BenchmarkRightBodySteve(b *testing.B) { Scale: defaultBenchmarkRenderScale, Overlay: true, Slim: false, + Square: false, }) } } @@ -69,6 +71,7 @@ func TestRightBodyAlex(t *testing.T) { Scale: scale, Overlay: true, Slim: true, + Square: false, }) if output.Bounds().Dx() < 1 { @@ -105,6 +108,119 @@ func BenchmarkRightBodyAlex(b *testing.B) { Scale: defaultBenchmarkRenderScale, Overlay: true, Slim: true, + Square: false, + }) + } +} + +func TestRightBodySteveSquare(t *testing.T) { + rawSkin := skin.GetDefaultSkin(false) + + for i := 0; i <= 8; i++ { + scale := 1 << i + + output := skin.RenderRightBody(rawSkin, skin.Options{ + Scale: scale, + Overlay: true, + Slim: false, + Square: true, + }) + + if output.Bounds().Dx() < 1 { + t.Fatalf("result image width is %d pixels\n", output.Bounds().Dx()) + } + + if output.Bounds().Dy() < 1 { + t.Fatalf("result image height is %d pixels\n", output.Bounds().Dy()) + } + + if output.Bounds().Size().X != output.Bounds().Size().Y { + t.Fatalf("result image is not square (%s)\n", output.Bounds().Size()) + } + + if writeRenders { + f, err := os.OpenFile(fmt.Sprintf("rightbody_steve_test_%d_square.png", scale), os.O_CREATE|os.O_RDWR, 0777) + + if err != nil { + t.Fatal(err) + } + + if err = png.Encode(f, output); err != nil { + t.Fatal(err) + } + + if err = f.Close(); err != nil { + t.Fatal(err) + } + } + } +} + +func BenchmarkRightBodySteveSquare(b *testing.B) { + rawSkin := skin.GetDefaultSkin(false) + + for n := 0; n <= b.N; n++ { + skin.RenderRightBody(rawSkin, skin.Options{ + Scale: defaultBenchmarkRenderScale, + Overlay: true, + Slim: false, + Square: true, + }) + } +} + +func TestRightBodyAlexSquare(t *testing.T) { + rawSkin := skin.GetDefaultSkin(true) + + for i := 0; i <= 8; i++ { + scale := 1 << i + + output := skin.RenderRightBody(rawSkin, skin.Options{ + Scale: scale, + Overlay: true, + Slim: true, + Square: true, + }) + + if output.Bounds().Dx() < 1 { + t.Fatalf("result image width is %d pixels\n", output.Bounds().Dx()) + } + + if output.Bounds().Dy() < 1 { + t.Fatalf("result image height is %d pixels\n", output.Bounds().Dy()) + } + + if output.Bounds().Size().X != output.Bounds().Size().Y { + t.Fatalf("result image is not square (%s)\n", output.Bounds().Size()) + } + + if writeRenders { + f, err := os.OpenFile(fmt.Sprintf("rightbody_alex_test_%d_square.png", scale), os.O_CREATE|os.O_RDWR, 0777) + + if err != nil { + t.Fatal(err) + } + + if err = png.Encode(f, output); err != nil { + t.Fatal(err) + } + + if err = f.Close(); err != nil { + t.Fatal(err) + } + } + } +} + +func BenchmarkRightBodyAlexSquare(b *testing.B) { + rawSkin := skin.GetDefaultSkin(true) + + for n := 0; n <= b.N; n++ { + skin.RenderRightBody(rawSkin, skin.Options{ + Scale: defaultBenchmarkRenderScale, + Overlay: true, + Slim: true, + Square: true, }) } } diff --git a/util.go b/util.go index 6f29cd5..6b90d61 100644 --- a/util.go +++ b/util.go @@ -313,6 +313,34 @@ func rotate270(img *image.NRGBA) *image.NRGBA { return output } +func squareAndCenter(img *image.NRGBA) *image.NRGBA { + var ( + size int = max(img.Rect.Size().X, img.Rect.Size().Y) + offsetX int = int((float64(size) - float64(img.Rect.Size().X)) / 2) + offsetY int = int((float64(size) - float64(img.Rect.Size().Y)) / 2) + + output *image.NRGBA = image.NewNRGBA(image.Rect(0, 0, size, size)) + ) + + composite(output, img, offsetX, offsetY) + + return output +} + +func max[T int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64](values ...T) T { + result := values[0] + + for _, v := range values { + if result > v { + continue + } + + result = v + } + + return result +} + // Credit: https://github.com/LapisBlue/Lapitar/blob/55ede80ce4ebb5ecc2b968164afb40f61b4cc509/mc/uuid.go#L23 func isEven(c uint8) bool { switch {