~eliasnaur/gio

6faed7e7245c2622b00852ce3e7e7e8cf98b6134 — Chris Waldon 7 months ago 60db802
widget: fix image scaling

Commit 94d242d broke the widget.Image's Scale field so
that it no longer had any effect on the actual size of
the displayed image. This commit fixes that, as well as
adding tests to confirm that the widget.Image type
scales appropriately with DPI changes and its own Scale
field.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2 files changed, 72 insertions(+), 1 deletions(-)

M widget/image.go
A widget/image_test.go
M widget/image.go => widget/image.go +6 -1
@@ 5,6 5,7 @@ package widget
import (
	"image"

	"gioui.org/f32"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/op/clip"


@@ 22,10 23,12 @@ type Image struct {
	Scale float32
}

const defaultScale = float32(160.0 / 72.0)

func (im Image) Layout(gtx layout.Context) layout.Dimensions {
	scale := im.Scale
	if scale == 0 {
		scale = 160.0 / 72.0
		scale = defaultScale
	}
	size := im.Src.Size()
	wf, hf := float32(size.X), float32(size.Y)


@@ 33,6 36,8 @@ func (im Image) Layout(gtx layout.Context) layout.Dimensions {
	cs := gtx.Constraints
	d := cs.Constrain(image.Pt(w, h))
	stack := op.Save(gtx.Ops)
	pixelScale := scale * gtx.Metric.PxPerDp
	op.Affine(f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(pixelScale, pixelScale))).Add(gtx.Ops)
	clip.Rect(image.Rectangle{Max: d}).Add(gtx.Ops)
	im.Src.Add(gtx.Ops)
	paint.PaintOp{}.Add(gtx.Ops)

A widget/image_test.go => widget/image_test.go +66 -0
@@ 0,0 1,66 @@
// SPDX-License-Identifier: Unlicense OR MIT

package widget

import (
	"image"
	"testing"

	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/op/paint"
)

func TestImageScale(t *testing.T) {
	var ops op.Ops
	gtx := layout.Context{
		Ops: &ops,
		Constraints: layout.Constraints{
			Max: image.Pt(50, 50),
		},
	}
	imgSize := image.Pt(10, 10)
	img := image.NewNRGBA(image.Rectangle{Max: imgSize})
	imgOp := paint.NewImageOp(img)

	// Ensure the default scales correctly.
	dims := Image{Src: imgOp}.Layout(gtx)
	expectedSize := imgSize
	expectedSize.X = int(float32(expectedSize.X) * defaultScale)
	expectedSize.Y = int(float32(expectedSize.Y) * defaultScale)
	if dims.Size != expectedSize {
		t.Fatalf("non-scaled image is wrong size, expected %v, got %v", expectedSize, dims.Size)
	}

	// Ensure scaling the image via the Scale field works.
	currentScale := float32(0.5)
	dims = Image{Src: imgOp, Scale: float32(currentScale)}.Layout(gtx)
	expectedSize = imgSize
	expectedSize.X = int(float32(expectedSize.X) * currentScale)
	expectedSize.Y = int(float32(expectedSize.Y) * currentScale)
	if dims.Size != expectedSize {
		t.Fatalf(".5 scale image is wrong size, expected %v, got %v", expectedSize, dims.Size)
	}

	// Ensure the image responds to changes in DPI.
	currentScale = float32(1)
	gtx.Metric.PxPerDp = 2
	dims = Image{Src: imgOp, Scale: float32(currentScale)}.Layout(gtx)
	expectedSize = imgSize
	expectedSize.X = int(float32(expectedSize.X) * currentScale * gtx.Metric.PxPerDp)
	expectedSize.Y = int(float32(expectedSize.Y) * currentScale * gtx.Metric.PxPerDp)
	if dims.Size != expectedSize {
		t.Fatalf("HiDPI non-scaled image is wrong size, expected %v, got %v", expectedSize, dims.Size)
	}

	// Ensure scaling the image responds to changes in DPI.
	currentScale = float32(.5)
	gtx.Metric.PxPerDp = 2
	dims = Image{Src: imgOp, Scale: float32(currentScale)}.Layout(gtx)
	expectedSize = imgSize
	expectedSize.X = int(float32(expectedSize.X) * currentScale * gtx.Metric.PxPerDp)
	expectedSize.Y = int(float32(expectedSize.Y) * currentScale * gtx.Metric.PxPerDp)
	if dims.Size != expectedSize {
		t.Fatalf("HiDPI .5 scale image is wrong size, expected %v, got %v", expectedSize, dims.Size)
	}
}