Skip to content

Commit 7ecd403

Browse files
committed
vector: bug fix: FillRect or other utility functions on a sub-image didn't work correctly
Closes #3330
1 parent cb26a22 commit 7ecd403

File tree

2 files changed

+109
-0
lines changed

2 files changed

+109
-0
lines changed

image.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ type Image struct {
7171
atime atomic.Int64
7272

7373
// usageCallbacks are callbacks that are invoked when the image is used.
74+
// usageCallbacks is valid only when the image is not a sub-image.
7475
usageCallbacks map[int64]func()
7576

7677
// inUsageCallbacks reports whether the image is in usageCallbacks.
@@ -1601,6 +1602,9 @@ func addUsageCallback(img *Image, callback func()) int64 {
16011602
}
16021603

16031604
func (i *Image) addUsageCallback(callback func()) int64 {
1605+
if i.isSubImage() {
1606+
return i.original.addUsageCallback(callback)
1607+
}
16041608
if i.usageCallbacks == nil {
16051609
i.usageCallbacks = map[int64]func(){}
16061610
}
@@ -1615,10 +1619,19 @@ func removeUsageCallback(img *Image, token int64) {
16151619
}
16161620

16171621
func (i *Image) removeUsageCallback(token int64) {
1622+
if i.isSubImage() {
1623+
i.original.removeUsageCallback(token)
1624+
return
1625+
}
16181626
delete(i.usageCallbacks, token)
16191627
}
16201628

16211629
func (i *Image) invokeUsageCallbacks() {
1630+
if i.isSubImage() {
1631+
i.original.invokeUsageCallbacks()
1632+
return
1633+
}
1634+
16221635
if i.inUsageCallbacks {
16231636
return
16241637
}

vector/util_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package vector_test
1616

1717
import (
18+
"image"
1819
"image/color"
1920
"testing"
2021

@@ -44,3 +45,98 @@ func TestStrokeRectAntiAlias(t *testing.T) {
4445
t.Errorf("got: %v, want: %v", got, want)
4546
}
4647
}
48+
49+
// Issue #3330
50+
func TestFillRectSubImage(t *testing.T) {
51+
dst := ebiten.NewImage(16, 16)
52+
53+
dst2 := dst.SubImage(image.Rect(0, 0, 8, 8)).(*ebiten.Image)
54+
vector.FillRect(dst2, 0, 0, 8, 8, color.White, true)
55+
if got, want := dst.At(5, 5), (color.RGBA{0xff, 0xff, 0xff, 0xff}); got != want {
56+
t.Errorf("got: %v, want: %v", got, want)
57+
}
58+
if got, want := dst2.At(5, 5), (color.RGBA{0xff, 0xff, 0xff, 0xff}); got != want {
59+
t.Errorf("got: %v, want: %v", got, want)
60+
}
61+
62+
dst3 := dst2.SubImage(image.Rect(4, 4, 8, 8)).(*ebiten.Image)
63+
vector.FillRect(dst3, 4, 4, 4, 4, color.Black, true)
64+
if got, want := dst.At(5, 5), (color.RGBA{0x00, 0x00, 0x00, 0xff}); got != want {
65+
t.Errorf("got: %v, want: %v", got, want)
66+
}
67+
if got, want := dst2.At(5, 5), (color.RGBA{0x00, 0x00, 0x00, 0xff}); got != want {
68+
t.Errorf("got: %v, want: %v", got, want)
69+
}
70+
if got, want := dst3.At(5, 5), (color.RGBA{0x00, 0x00, 0x00, 0xff}); got != want {
71+
t.Errorf("got: %v, want: %v", got, want)
72+
}
73+
}
74+
75+
// Issue #3330
76+
func TestFillCircleSubImage(t *testing.T) {
77+
dst := ebiten.NewImage(16, 16)
78+
79+
dst2 := dst.SubImage(image.Rect(0, 0, 8, 8)).(*ebiten.Image)
80+
vector.FillCircle(dst2, 4, 4, 4, color.White, true)
81+
if got, want := dst.At(5, 5), (color.RGBA{0xff, 0xff, 0xff, 0xff}); got != want {
82+
t.Errorf("got: %v, want: %v", got, want)
83+
}
84+
if got, want := dst2.At(5, 5), (color.RGBA{0xff, 0xff, 0xff, 0xff}); got != want {
85+
t.Errorf("got: %v, want: %v", got, want)
86+
}
87+
88+
dst3 := dst2.SubImage(image.Rect(4, 4, 8, 8)).(*ebiten.Image)
89+
vector.FillCircle(dst3, 6, 6, 4, color.Black, true)
90+
if got, want := dst.At(5, 5), (color.RGBA{0x00, 0x00, 0x00, 0xff}); got != want {
91+
t.Errorf("got: %v, want: %v", got, want)
92+
}
93+
if got, want := dst2.At(5, 5), (color.RGBA{0x00, 0x00, 0x00, 0xff}); got != want {
94+
t.Errorf("got: %v, want: %v", got, want)
95+
}
96+
if got, want := dst3.At(5, 5), (color.RGBA{0x00, 0x00, 0x00, 0xff}); got != want {
97+
t.Errorf("got: %v, want: %v", got, want)
98+
}
99+
}
100+
101+
// Issue #3330
102+
func TestFillPathSubImage(t *testing.T) {
103+
dst := ebiten.NewImage(16, 16)
104+
105+
dst2 := dst.SubImage(image.Rect(0, 0, 8, 8)).(*ebiten.Image)
106+
var p vector.Path
107+
p.MoveTo(0, 0)
108+
p.LineTo(8, 0)
109+
p.LineTo(8, 8)
110+
p.LineTo(0, 8)
111+
p.Close()
112+
op := &vector.DrawPathOptions{}
113+
op.ColorScale.ScaleWithColor(color.White)
114+
op.AntiAlias = true
115+
vector.FillPath(dst2, &p, nil, op)
116+
if got, want := dst.At(5, 5), (color.RGBA{0xff, 0xff, 0xff, 0xff}); got != want {
117+
t.Errorf("got: %v, want: %v", got, want)
118+
}
119+
if got, want := dst2.At(5, 5), (color.RGBA{0xff, 0xff, 0xff, 0xff}); got != want {
120+
t.Errorf("got: %v, want: %v", got, want)
121+
}
122+
123+
dst3 := dst2.SubImage(image.Rect(4, 4, 8, 8)).(*ebiten.Image)
124+
var p2 vector.Path
125+
p2.MoveTo(4, 4)
126+
p2.LineTo(8, 4)
127+
p2.LineTo(8, 8)
128+
p2.LineTo(4, 8)
129+
p2.Close()
130+
op.ColorScale.Reset()
131+
op.ColorScale.ScaleWithColor(color.Black)
132+
vector.FillPath(dst3, &p2, nil, op)
133+
if got, want := dst.At(5, 5), (color.RGBA{0x00, 0x00, 0x00, 0xff}); got != want {
134+
t.Errorf("got: %v, want: %v", got, want)
135+
}
136+
if got, want := dst2.At(5, 5), (color.RGBA{0x00, 0x00, 0x00, 0xff}); got != want {
137+
t.Errorf("got: %v, want: %v", got, want)
138+
}
139+
if got, want := dst3.At(5, 5), (color.RGBA{0x00, 0x00, 0x00, 0xff}); got != want {
140+
t.Errorf("got: %v, want: %v", got, want)
141+
}
142+
}

0 commit comments

Comments
 (0)