refactor(ports): share the rotated size of a transform
- commit
- c1aa5992139ded78df42ed8f38b33918abd142cf
- author
- brice · · 2026-10-03 19:15 UTC
- parent
- 1c259ae
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
10 files · +44 -41
M internal/adapters/vulkan/compose.go +2 -4
| @@ -332,10 +332,8 @@ func (r *Renderer) contentDraw(rect, full image.Rectangle, w, h int, source [4]f | |||
| 332 | 332 | source = [4]float32{0, 0, float32(w), float32(h)} | |
| 333 | 333 | } | |
| 334 | 334 | // The crop in the surface's axes: its sides swap for a rotation. | |
| 335 | - | cw, ch := float64(source[2]), float64(source[3]) | |
| 336 | - | if t.Rotated() { | |
| 337 | - | cw, ch = ch, cw | |
| 338 | - | } | |
| 335 | + | iw, ih := t.Size(int(source[2]), int(source[3])) | |
| 336 | + | cw, ch := float64(iw), float64(ih) | |
| 339 | 337 | // src maps a target point to buffer pixels: affine, so its value at | |
| 340 | 338 | // the target origin and its steps along x and y define it. | |
| 341 | 339 | src := func(x, y float64) (float64, float64) { | |
| @@ -332,10 +332,8 @@ func (r *Renderer) contentDraw(rect, full image.Rectangle, w, h int, source [4]f | |||||
| 332 | source = [4]float32{0, 0, float32(w), float32(h)} | 332 | source = [4]float32{0, 0, float32(w), float32(h)} | ||
| 333 | } | 333 | } | ||
| 334 | // The crop in the surface's axes: its sides swap for a rotation. | 334 | // The crop in the surface's axes: its sides swap for a rotation. | ||
| 335 | - | cw, ch := float64(source[2]), float64(source[3]) | 335 | + | iw, ih := t.Size(int(source[2]), int(source[3])) |
| 336 | - | if t.Rotated() { | 336 | + | cw, ch := float64(iw), float64(ih) |
| 337 | - | cw, ch = ch, cw | |||
| 338 | - | } | |||
| 339 | // src maps a target point to buffer pixels: affine, so its value at | 337 | // src maps a target point to buffer pixels: affine, so its value at | ||
| 340 | // the target origin and its steps along x and y define it. | 338 | // the target origin and its steps along x and y define it. | ||
| 341 | src := func(x, y float64) (float64, float64) { | 339 | src := func(x, y float64) (float64, float64) { | ||
M internal/adapters/vulkan/damage_test.go +1 -4
| @@ -40,10 +40,7 @@ func TestRendererDamageMatchesFullRedraw(t *testing.T) { | |||
| 40 | 40 | // out (its scene is 64×48; a rotated output's target is 48×64). | |
| 41 | 41 | func damageMatchesFullRedraw(t *testing.T, tr, out ports.BufferTransform) { | |
| 42 | 42 | const w, h = 32, 32 | |
| 43 | - | tw, th := 64, 48 | |
| 44 | - | if out.Rotated() { | |
| 45 | - | tw, th = th, tw | |
| 46 | - | } | |
| 43 | + | tw, th := out.Size(64, 48) | |
| 47 | 44 | newR := func() *Renderer { | |
| 48 | 45 | r, err := New(tw, th) | |
| 49 | 46 | if err != nil { | |
| @@ -40,10 +40,7 @@ func TestRendererDamageMatchesFullRedraw(t *testing.T) { | |||||
| 40 | // out (its scene is 64×48; a rotated output's target is 48×64). | 40 | // out (its scene is 64×48; a rotated output's target is 48×64). | ||
| 41 | func damageMatchesFullRedraw(t *testing.T, tr, out ports.BufferTransform) { | 41 | func damageMatchesFullRedraw(t *testing.T, tr, out ports.BufferTransform) { | ||
| 42 | const w, h = 32, 32 | 42 | const w, h = 32, 32 | ||
| 43 | - | tw, th := 64, 48 | 43 | + | tw, th := out.Size(64, 48) |
| 44 | - | if out.Rotated() { | |||
| 45 | - | tw, th = th, tw | |||
| 46 | - | } | |||
| 47 | newR := func() *Renderer { | 44 | newR := func() *Renderer { | ||
| 48 | r, err := New(tw, th) | 45 | r, err := New(tw, th) | ||
| 49 | if err != nil { | 46 | if err != nil { | ||
M internal/adapters/vulkan/orient.go +1 -4
| @@ -9,10 +9,7 @@ import ( | |||
| 9 | 9 | // sceneSize is the size of a scene in scene-physical pixels: the target's, | |
| 10 | 10 | // swapped when the output transform t rotates by 90 or 270 degrees. | |
| 11 | 11 | func (r *Renderer) sceneSize(t ports.BufferTransform) (int, int) { | |
| 12 | - | if t.Rotated() { | |
| 13 | - | return r.height, r.width | |
| 14 | - | } | |
| 15 | - | return r.width, r.height | |
| 12 | + | return t.Size(r.width, r.height) | |
| 16 | 13 | } | |
| 17 | 14 | ||
| 18 | 15 | // orient maps draws built in scene space to the target, in place and without | |
| @@ -9,10 +9,7 @@ import ( | |||||
| 9 | // sceneSize is the size of a scene in scene-physical pixels: the target's, | 9 | // sceneSize is the size of a scene in scene-physical pixels: the target's, | ||
| 10 | // swapped when the output transform t rotates by 90 or 270 degrees. | 10 | // swapped when the output transform t rotates by 90 or 270 degrees. | ||
| 11 | func (r *Renderer) sceneSize(t ports.BufferTransform) (int, int) { | 11 | func (r *Renderer) sceneSize(t ports.BufferTransform) (int, int) { | ||
| 12 | - | if t.Rotated() { | 12 | + | return t.Size(r.width, r.height) |
| 13 | - | return r.height, r.width | |||
| 14 | - | } | |||
| 15 | - | return r.width, r.height | |||
| 16 | } | 13 | } | ||
| 17 | 14 | ||||
| 18 | // orient maps draws built in scene space to the target, in place and without | 15 | // orient maps draws built in scene space to the target, in place and without | ||
M internal/adapters/vulkan/scene.go +1 -3
| @@ -326,9 +326,7 @@ func (w *sceneWalk) surfaceRects(content *ports.SurfaceContent, x, y float64, cl | |||
| 326 | 326 | if content.Source[2] > 0 { | |
| 327 | 327 | sourceW, sourceH = int(content.Source[2]), int(content.Source[3]) | |
| 328 | 328 | } | |
| 329 | - | if content.Transform.Rotated() { | |
| 330 | - | sourceW, sourceH = sourceH, sourceW | |
| 331 | - | } | |
| 329 | + | sourceW, sourceH = content.Transform.Size(sourceW, sourceH) | |
| 332 | 330 | if near(full.Dx(), sourceW) && near(full.Dy(), sourceH) { | |
| 333 | 331 | full.Max = full.Min.Add(image.Pt(sourceW, sourceH)) | |
| 334 | 332 | } | |
| @@ -326,9 +326,7 @@ func (w *sceneWalk) surfaceRects(content *ports.SurfaceContent, x, y float64, cl | |||||
| 326 | if content.Source[2] > 0 { | 326 | if content.Source[2] > 0 { | ||
| 327 | sourceW, sourceH = int(content.Source[2]), int(content.Source[3]) | 327 | sourceW, sourceH = int(content.Source[2]), int(content.Source[3]) | ||
| 328 | } | 328 | } | ||
| 329 | - | if content.Transform.Rotated() { | 329 | + | sourceW, sourceH = content.Transform.Size(sourceW, sourceH) |
| 330 | - | sourceW, sourceH = sourceH, sourceW | |||
| 331 | - | } | |||
| 332 | if near(full.Dx(), sourceW) && near(full.Dy(), sourceH) { | 330 | if near(full.Dx(), sourceW) && near(full.Dy(), sourceH) { | ||
| 333 | full.Max = full.Min.Add(image.Pt(sourceW, sourceH)) | 331 | full.Max = full.Min.Add(image.Pt(sourceW, sourceH)) | ||
| 334 | } | 332 | } | ||
M internal/adapters/vulkan/transform_test.go +1 -4
| @@ -37,10 +37,7 @@ func TestRendererBufferTransform(t *testing.T) { | |||
| 37 | 37 | c.Source = [4]float32{1, 0, 2, 2} | |
| 38 | 38 | bx0, bw = 1, 2 | |
| 39 | 39 | } | |
| 40 | - | sw, sh := bw, bh | |
| 41 | - | if tr.Rotated() { | |
| 42 | - | sw, sh = bh, bw | |
| 43 | - | } | |
| 40 | + | sw, sh := tr.Size(bw, bh) | |
| 44 | 41 | c.Transform, c.LogicalW, c.LogicalH = tr, sw, sh | |
| 45 | 42 | scene := ports.Scene{Background: "#000000", Windows: []ports.SceneWindow{{ID: 1, Rect: ports.Rect{W: sw, H: sh}}}} | |
| 46 | 43 | if err := render(r, scene, map[ports.WindowID]ports.SurfaceContent{1: *c}); err != nil { | |
| @@ -37,10 +37,7 @@ func TestRendererBufferTransform(t *testing.T) { | |||||
| 37 | c.Source = [4]float32{1, 0, 2, 2} | 37 | c.Source = [4]float32{1, 0, 2, 2} | ||
| 38 | bx0, bw = 1, 2 | 38 | bx0, bw = 1, 2 | ||
| 39 | } | 39 | } | ||
| 40 | - | sw, sh := bw, bh | 40 | + | sw, sh := tr.Size(bw, bh) |
| 41 | - | if tr.Rotated() { | |||
| 42 | - | sw, sh = bh, bw | |||
| 43 | - | } | |||
| 44 | c.Transform, c.LogicalW, c.LogicalH = tr, sw, sh | 41 | c.Transform, c.LogicalW, c.LogicalH = tr, sw, sh | ||
| 45 | scene := ports.Scene{Background: "#000000", Windows: []ports.SceneWindow{{ID: 1, Rect: ports.Rect{W: sw, H: sh}}}} | 42 | scene := ports.Scene{Background: "#000000", Windows: []ports.SceneWindow{{ID: 1, Rect: ports.Rect{W: sw, H: sh}}}} | ||
| 46 | if err := render(r, scene, map[ports.WindowID]ports.SurfaceContent{1: *c}); err != nil { | 43 | if err := render(r, scene, map[ports.WindowID]ports.SurfaceContent{1: *c}); err != nil { | ||
M internal/adapters/wayland/screencopy.go +1 -10
| @@ -30,20 +30,11 @@ func captureRegion(o *output, logical image.Rectangle) image.Rectangle { | |||
| 30 | 30 | if scale <= 0 { | |
| 31 | 31 | scale = 1 | |
| 32 | 32 | } | |
| 33 | - | sw, sh := sceneSize(o) | |
| 33 | + | sw, sh := o.place.Transform.Size(o.place.Info.Width, o.place.Info.Height) | |
| 34 | 34 | p := image.Rect(int(math.Floor(float64(logical.Min.X)*scale)), int(math.Floor(float64(logical.Min.Y)*scale)), int(math.Ceil(float64(logical.Max.X)*scale)), int(math.Ceil(float64(logical.Max.Y)*scale))) | |
| 35 | 35 | return o.place.Transform.RectToBuffer(p, sw, sh).Intersect(image.Rect(0, 0, o.place.Info.Width, o.place.Info.Height)) | |
| 36 | 36 | } | |
| 37 | 37 | ||
| 38 | - | // sceneSize is the scene-physical size of the output: the mode size, swapped | |
| 39 | - | // for a 90° or 270° transform. | |
| 40 | - | func sceneSize(o *output) (int, int) { | |
| 41 | - | w, h := o.place.Info.Width, o.place.Info.Height | |
| 42 | - | if o.place.Transform.Rotated() { | |
| 43 | - | return h, w | |
| 44 | - | } | |
| 45 | - | return w, h | |
| 46 | - | } | |
| 47 | 38 | func captureTime(t time.Time) (uint32, uint32, uint32) { | |
| 48 | 39 | sec := uint64(t.Unix()) | |
| 49 | 40 | return uint32(sec >> 32), uint32(sec), uint32(t.Nanosecond()) | |
| @@ -30,20 +30,11 @@ func captureRegion(o *output, logical image.Rectangle) image.Rectangle { | |||||
| 30 | if scale <= 0 { | 30 | if scale <= 0 { | ||
| 31 | scale = 1 | 31 | scale = 1 | ||
| 32 | } | 32 | } | ||
| 33 | - | sw, sh := sceneSize(o) | 33 | + | sw, sh := o.place.Transform.Size(o.place.Info.Width, o.place.Info.Height) |
| 34 | p := image.Rect(int(math.Floor(float64(logical.Min.X)*scale)), int(math.Floor(float64(logical.Min.Y)*scale)), int(math.Ceil(float64(logical.Max.X)*scale)), int(math.Ceil(float64(logical.Max.Y)*scale))) | 34 | p := image.Rect(int(math.Floor(float64(logical.Min.X)*scale)), int(math.Floor(float64(logical.Min.Y)*scale)), int(math.Ceil(float64(logical.Max.X)*scale)), int(math.Ceil(float64(logical.Max.Y)*scale))) | ||
| 35 | return o.place.Transform.RectToBuffer(p, sw, sh).Intersect(image.Rect(0, 0, o.place.Info.Width, o.place.Info.Height)) | 35 | return o.place.Transform.RectToBuffer(p, sw, sh).Intersect(image.Rect(0, 0, o.place.Info.Width, o.place.Info.Height)) | ||
| 36 | } | 36 | } | ||
| 37 | 37 | ||||
| 38 | - | // sceneSize is the scene-physical size of the output: the mode size, swapped | |||
| 39 | - | // for a 90° or 270° transform. | |||
| 40 | - | func sceneSize(o *output) (int, int) { | |||
| 41 | - | w, h := o.place.Info.Width, o.place.Info.Height | |||
| 42 | - | if o.place.Transform.Rotated() { | |||
| 43 | - | return h, w | |||
| 44 | - | } | |||
| 45 | - | return w, h | |||
| 46 | - | } | |||
| 47 | func captureTime(t time.Time) (uint32, uint32, uint32) { | 38 | func captureTime(t time.Time) (uint32, uint32, uint32) { | ||
| 48 | sec := uint64(t.Unix()) | 39 | sec := uint64(t.Unix()) | ||
| 49 | return uint32(sec >> 32), uint32(sec), uint32(t.Nanosecond()) | 40 | return uint32(sec >> 32), uint32(sec), uint32(t.Nanosecond()) | ||
M internal/core/screens.go +1 -4
| @@ -35,10 +35,7 @@ func (s *screen) name() string { return s.info.Name } | |||
| 35 | 35 | // modeSize is the physical mode size in the screen's logical orientation: | |
| 36 | 36 | // width and height swap for a 90/270 transform. | |
| 37 | 37 | func (s *screen) modeSize() (w, h int) { | |
| 38 | - | if s.transform.Rotated() { | |
| 39 | - | return s.info.Height, s.info.Width | |
| 40 | - | } | |
| 41 | - | return s.info.Width, s.info.Height | |
| 38 | + | return s.transform.Size(s.info.Width, s.info.Height) | |
| 42 | 39 | } | |
| 43 | 40 | ||
| 44 | 41 | // rect is the screen's place in the global logical layout. | |
| @@ -35,10 +35,7 @@ func (s *screen) name() string { return s.info.Name } | |||||
| 35 | // modeSize is the physical mode size in the screen's logical orientation: | 35 | // modeSize is the physical mode size in the screen's logical orientation: | ||
| 36 | // width and height swap for a 90/270 transform. | 36 | // width and height swap for a 90/270 transform. | ||
| 37 | func (s *screen) modeSize() (w, h int) { | 37 | func (s *screen) modeSize() (w, h int) { | ||
| 38 | - | if s.transform.Rotated() { | 38 | + | return s.transform.Size(s.info.Width, s.info.Height) |
| 39 | - | return s.info.Height, s.info.Width | |||
| 40 | - | } | |||
| 41 | - | return s.info.Width, s.info.Height | |||
| 42 | } | 39 | } | ||
| 43 | 40 | ||||
| 44 | // rect is the screen's place in the global logical layout. | 41 | // rect is the screen's place in the global logical layout. | ||
M internal/ports/client_commands.go +2 -5
| @@ -73,11 +73,8 @@ func (o OutputPlacement) ToTarget(x, y float64) (float64, float64) { | |||
| 73 | 73 | s = 1 | |
| 74 | 74 | } | |
| 75 | 75 | px, py := (x-float64(o.X))*s, (y-float64(o.Y))*s | |
| 76 | - | sw, sh := float64(o.Info.Width), float64(o.Info.Height) | |
| 77 | - | if o.Transform.Rotated() { | |
| 78 | - | sw, sh = sh, sw | |
| 79 | - | } | |
| 80 | - | return o.Transform.ToBuffer(px, py, sw, sh) | |
| 76 | + | sw, sh := o.Transform.Size(o.Info.Width, o.Info.Height) | |
| 77 | + | return o.Transform.ToBuffer(px, py, float64(sw), float64(sh)) | |
| 81 | 78 | } | |
| 82 | 79 | ||
| 83 | 80 | // Layout is the global arrangement of outputs in logical pixels. | |
| @@ -73,11 +73,8 @@ func (o OutputPlacement) ToTarget(x, y float64) (float64, float64) { | |||||
| 73 | s = 1 | 73 | s = 1 | ||
| 74 | } | 74 | } | ||
| 75 | px, py := (x-float64(o.X))*s, (y-float64(o.Y))*s | 75 | px, py := (x-float64(o.X))*s, (y-float64(o.Y))*s | ||
| 76 | - | sw, sh := float64(o.Info.Width), float64(o.Info.Height) | 76 | + | sw, sh := o.Transform.Size(o.Info.Width, o.Info.Height) |
| 77 | - | if o.Transform.Rotated() { | 77 | + | return o.Transform.ToBuffer(px, py, float64(sw), float64(sh)) |
| 78 | - | sw, sh = sh, sw | |||
| 79 | - | } | |||
| 80 | - | return o.Transform.ToBuffer(px, py, sw, sh) | |||
| 81 | } | 78 | } | ||
| 82 | 79 | ||||
| 83 | // Layout is the global arrangement of outputs in logical pixels. | 80 | // Layout is the global arrangement of outputs in logical pixels. | ||
M internal/ports/scene.go +9 -0
| @@ -210,6 +210,15 @@ type BufferTransform uint8 | |||
| 210 | 210 | // Rotated reports whether width and height swap between buffer and surface. | |
| 211 | 211 | func (t BufferTransform) Rotated() bool { return t&1 != 0 } | |
| 212 | 212 | ||
| 213 | + | // Size returns the size of a w×h space seen through t: width and height swap | |
| 214 | + | // when t is Rotated. | |
| 215 | + | func (t BufferTransform) Size(w, h int) (int, int) { | |
| 216 | + | if t.Rotated() { | |
| 217 | + | return h, w | |
| 218 | + | } | |
| 219 | + | return w, h | |
| 220 | + | } | |
| 221 | + | ||
| 213 | 222 | // ToBuffer maps a point of a w×h surface (logical, before scale) to the | |
| 214 | 223 | // same point of its buffer, in the buffer's untransformed w'×h' axes, where | |
| 215 | 224 | // w' and h' swap when the transform is Rotated. | |
| @@ -210,6 +210,15 @@ type BufferTransform uint8 | |||||
| 210 | // Rotated reports whether width and height swap between buffer and surface. | 210 | // Rotated reports whether width and height swap between buffer and surface. | ||
| 211 | func (t BufferTransform) Rotated() bool { return t&1 != 0 } | 211 | func (t BufferTransform) Rotated() bool { return t&1 != 0 } | ||
| 212 | 212 | ||||
| 213 | + | // Size returns the size of a w×h space seen through t: width and height swap | |||
| 214 | + | // when t is Rotated. | |||
| 215 | + | func (t BufferTransform) Size(w, h int) (int, int) { | |||
| 216 | + | if t.Rotated() { | |||
| 217 | + | return h, w | |||
| 218 | + | } | |||
| 219 | + | return w, h | |||
| 220 | + | } | |||
| 221 | + | ||||
| 213 | // ToBuffer maps a point of a w×h surface (logical, before scale) to the | 222 | // ToBuffer maps a point of a w×h surface (logical, before scale) to the | ||
| 214 | // same point of its buffer, in the buffer's untransformed w'×h' axes, where | 223 | // same point of its buffer, in the buffer's untransformed w'×h' axes, where | ||
| 215 | // w' and h' swap when the transform is Rotated. | 224 | // w' and h' swap when the transform is Rotated. | ||
M internal/ports/transform_test.go +25 -3
| @@ -57,10 +57,32 @@ func TestBufferTransformInvert(t *testing.T) { | |||
| 57 | 57 | } | |
| 58 | 58 | } | |
| 59 | 59 | ||
| 60 | + | // RectToBuffer is the bounding box of the rect's corners under ToBuffer. | |
| 60 | 61 | func TestRectToBuffer(t *testing.T) { | |
| 61 | - | r := image.Rect(0, 0, 2, 1) | |
| 62 | - | assert.Equal(t, image.Rect(0, 5, 1, 7), BufferTransform(1).RectToBuffer(r, 7, 3)) | |
| 63 | - | assert.Equal(t, r, BufferTransform(0).RectToBuffer(r, 7, 3)) | |
| 62 | + | const w, h = 7, 3 | |
| 63 | + | r := image.Rect(1, 0, 3, 2) | |
| 64 | + | assert.Equal(t, image.Rect(0, 5, 1, 7), BufferTransform(1).RectToBuffer(image.Rect(0, 0, 2, 1), w, h)) | |
| 65 | + | for tr := BufferTransform(0); tr < 8; tr++ { | |
| 66 | + | got := tr.RectToBuffer(r, w, h) | |
| 67 | + | x0, y0 := tr.ToBuffer(float64(r.Min.X), float64(r.Min.Y), w, h) | |
| 68 | + | x1, y1 := tr.ToBuffer(float64(r.Max.X), float64(r.Max.Y), w, h) | |
| 69 | + | x2, y2 := tr.ToBuffer(float64(r.Min.X), float64(r.Max.Y), w, h) | |
| 70 | + | x3, y3 := tr.ToBuffer(float64(r.Max.X), float64(r.Min.Y), w, h) | |
| 71 | + | want := image.Rect(int(min(x0, x1, x2, x3)), int(min(y0, y1, y2, y3)), int(max(x0, x1, x2, x3)), int(max(y0, y1, y2, y3))) | |
| 72 | + | assert.Equal(t, want, got, "transform %d", tr) | |
| 73 | + | assert.Equal(t, r.Dx()*r.Dy(), got.Dx()*got.Dy(), "transform %d area", tr) | |
| 74 | + | } | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | func TestBufferTransformSize(t *testing.T) { | |
| 78 | + | for tr := BufferTransform(0); tr < 8; tr++ { | |
| 79 | + | w, h := tr.Size(7, 3) | |
| 80 | + | if tr.Rotated() { | |
| 81 | + | assert.Equal(t, [2]int{3, 7}, [2]int{w, h}, "transform %d", tr) | |
| 82 | + | } else { | |
| 83 | + | assert.Equal(t, [2]int{7, 3}, [2]int{w, h}, "transform %d", tr) | |
| 84 | + | } | |
| 85 | + | } | |
| 64 | 86 | } | |
| 65 | 87 | ||
| 66 | 88 | func TestOutputPlacementToTarget(t *testing.T) { | |
| @@ -57,10 +57,32 @@ func TestBufferTransformInvert(t *testing.T) { | |||||
| 57 | } | 57 | } | ||
| 58 | } | 58 | } | ||
| 59 | 59 | ||||
| 60 | + | // RectToBuffer is the bounding box of the rect's corners under ToBuffer. | |||
| 60 | func TestRectToBuffer(t *testing.T) { | 61 | func TestRectToBuffer(t *testing.T) { | ||
| 61 | - | r := image.Rect(0, 0, 2, 1) | 62 | + | const w, h = 7, 3 |
| 62 | - | assert.Equal(t, image.Rect(0, 5, 1, 7), BufferTransform(1).RectToBuffer(r, 7, 3)) | 63 | + | r := image.Rect(1, 0, 3, 2) |
| 63 | - | assert.Equal(t, r, BufferTransform(0).RectToBuffer(r, 7, 3)) | 64 | + | assert.Equal(t, image.Rect(0, 5, 1, 7), BufferTransform(1).RectToBuffer(image.Rect(0, 0, 2, 1), w, h)) |
| 65 | + | for tr := BufferTransform(0); tr < 8; tr++ { | |||
| 66 | + | got := tr.RectToBuffer(r, w, h) | |||
| 67 | + | x0, y0 := tr.ToBuffer(float64(r.Min.X), float64(r.Min.Y), w, h) | |||
| 68 | + | x1, y1 := tr.ToBuffer(float64(r.Max.X), float64(r.Max.Y), w, h) | |||
| 69 | + | x2, y2 := tr.ToBuffer(float64(r.Min.X), float64(r.Max.Y), w, h) | |||
| 70 | + | x3, y3 := tr.ToBuffer(float64(r.Max.X), float64(r.Min.Y), w, h) | |||
| 71 | + | want := image.Rect(int(min(x0, x1, x2, x3)), int(min(y0, y1, y2, y3)), int(max(x0, x1, x2, x3)), int(max(y0, y1, y2, y3))) | |||
| 72 | + | assert.Equal(t, want, got, "transform %d", tr) | |||
| 73 | + | assert.Equal(t, r.Dx()*r.Dy(), got.Dx()*got.Dy(), "transform %d area", tr) | |||
| 74 | + | } | |||
| 75 | + | } | |||
| 76 | + | ||||
| 77 | + | func TestBufferTransformSize(t *testing.T) { | |||
| 78 | + | for tr := BufferTransform(0); tr < 8; tr++ { | |||
| 79 | + | w, h := tr.Size(7, 3) | |||
| 80 | + | if tr.Rotated() { | |||
| 81 | + | assert.Equal(t, [2]int{3, 7}, [2]int{w, h}, "transform %d", tr) | |||
| 82 | + | } else { | |||
| 83 | + | assert.Equal(t, [2]int{7, 3}, [2]int{w, h}, "transform %d", tr) | |||
| 84 | + | } | |||
| 85 | + | } | |||
| 64 | } | 86 | } | ||
| 65 | 87 | ||||
| 66 | func TestOutputPlacementToTarget(t *testing.T) { | 88 | func TestOutputPlacementToTarget(t *testing.T) { | ||