restorable: Refactoring: Use width/height instead of length at Pixels

This commit is contained in:
Hajime Hoshi 2019-07-17 23:03:14 +09:00
parent efb6f9c453
commit b4dddd330a
2 changed files with 96 additions and 80 deletions

View File

@ -28,7 +28,8 @@ import (
type Pixels struct { type Pixels struct {
pixels []byte pixels []byte
length int width int
height int
// color is used only when pixels == nil // color is used only when pixels == nil
color color.RGBA color color.RGBA
@ -36,30 +37,23 @@ type Pixels struct {
func (p *Pixels) CopyFrom(pix []byte, from int) { func (p *Pixels) CopyFrom(pix []byte, from int) {
if p.pixels == nil { if p.pixels == nil {
p.pixels = make([]byte, p.length) p.pixels = make([]byte, 4*p.width*p.height)
} }
copy(p.pixels[from:from+len(pix)], pix) copy(p.pixels[from:from+len(pix)], pix)
} }
func (p *Pixels) At(i int) byte { func (p *Pixels) At(i, j int) (byte, byte, byte, byte) {
if i < 0 || p.length <= i { if i < 0 || p.width <= i {
panic(fmt.Sprintf("restorable: index out of range: %d for length: %d", i, p.length)) panic(fmt.Sprintf("restorable: index out of range: %d for the width: %d", i, p.width))
}
if j < 0 || p.height <= j {
panic(fmt.Sprintf("restorable: index out of range: %d for the height: %d", i, p.height))
} }
if p.pixels != nil { if p.pixels != nil {
return p.pixels[i] idx := 4 * (j*p.width + i)
} return p.pixels[idx], p.pixels[idx+1], p.pixels[idx+2], p.pixels[idx+3]
switch i % 4 {
case 0:
return p.color.R
case 1:
return p.color.G
case 2:
return p.color.B
case 3:
return p.color.A
default:
panic("not reached")
} }
return p.color.R, p.color.G, p.color.B, p.color.A
} }
// drawTrianglesHistoryItem is an item for history of draw-image commands. // drawTrianglesHistoryItem is an item for history of draw-image commands.
@ -165,7 +159,8 @@ func (i *Image) Extend(width, height int) *Image {
// Copy basePixels. // Copy basePixels.
newImg.basePixels = &Pixels{ newImg.basePixels = &Pixels{
pixels: make([]byte, 4*width*height), pixels: make([]byte, 4*width*height),
length: 4 * width * height, width: width,
height: height,
} }
pix := i.basePixels.pixels pix := i.basePixels.pixels
idx := 0 idx := 0
@ -258,7 +253,8 @@ func (i *Image) fill(r, g, b, a byte) {
w, h := i.Size() w, h := i.Size()
i.basePixels = &Pixels{ i.basePixels = &Pixels{
color: color.RGBA{r, g, b, a}, color: color.RGBA{r, g, b, a},
length: 4 * w * h, width: w,
height: h,
} }
i.drawTrianglesHistory = nil i.drawTrianglesHistory = nil
i.stale = false i.stale = false
@ -349,7 +345,8 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
if x == 0 && y == 0 && width == w && height == h { if x == 0 && y == 0 && width == w && height == h {
if i.basePixels == nil { if i.basePixels == nil {
i.basePixels = &Pixels{ i.basePixels = &Pixels{
length: 4 * w * h, width: w,
height: h,
} }
} }
i.basePixels.CopyFrom(pixels, 0) i.basePixels.CopyFrom(pixels, 0)
@ -378,7 +375,8 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
idx := 4 * (y*w + x) idx := 4 * (y*w + x)
if i.basePixels == nil { if i.basePixels == nil {
i.basePixels = &Pixels{ i.basePixels = &Pixels{
length: 4 * w * h, width: w,
height: h,
} }
} }
for j := 0; j < height; j++ { for j := 0; j < height; j++ {
@ -455,8 +453,7 @@ func (i *Image) At(x, y int) (byte, byte, byte, byte) {
return 0, 0, 0, 0 return 0, 0, 0, 0
} }
idx := 4*x + 4*y*w return i.basePixels.At(x, y)
return i.basePixels.At(idx), i.basePixels.At(idx + 1), i.basePixels.At(idx + 2), i.basePixels.At(idx + 3)
} }
// makeStaleIfDependingOn makes the image stale if the image depends on target. // makeStaleIfDependingOn makes the image stale if the image depends on target.
@ -471,10 +468,12 @@ func (i *Image) makeStaleIfDependingOn(target *Image) {
// readPixelsFromGPU reads the pixels from GPU and resolves the image's 'stale' state. // readPixelsFromGPU reads the pixels from GPU and resolves the image's 'stale' state.
func (i *Image) readPixelsFromGPU() { func (i *Image) readPixelsFromGPU() {
w, h := i.Size()
pix := i.image.Pixels() pix := i.image.Pixels()
i.basePixels = &Pixels{ i.basePixels = &Pixels{
pixels: pix, pixels: pix,
length: len(pix), width: w,
height: h,
} }
i.drawTrianglesHistory = nil i.drawTrianglesHistory = nil
i.stale = false i.stale = false
@ -578,7 +577,8 @@ func (i *Image) restore() error {
pix := gimg.Pixels() pix := gimg.Pixels()
i.basePixels = &Pixels{ i.basePixels = &Pixels{
pixels: pix, pixels: pix,
length: len(pix), width: w,
height: h,
} }
} }

View File

@ -45,9 +45,9 @@ func TestMain(m *testing.M) {
os.Exit(code) os.Exit(code)
} }
func pixelsToColor(p *Pixels, index int) color.RGBA { func pixelsToColor(p *Pixels, i, j int) color.RGBA {
i := index * 4 r, g, b, a := p.At(i, j)
return color.RGBA{p.At(i), p.At(i + 1), p.At(i + 2), p.At(i + 3)} return color.RGBA{r, g, b, a}
} }
func abs(x int) int { func abs(x int) int {
@ -80,7 +80,7 @@ func TestRestore(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
want := clr0 want := clr0
got := pixelsToColor(img0.BasePixelsForTesting(), 0) got := pixelsToColor(img0.BasePixelsForTesting(), 0, 0)
if !sameColors(got, want, 1) { if !sameColors(got, want, 1) {
t.Errorf("got %v, want %v", got, want) t.Errorf("got %v, want %v", got, want)
} }
@ -97,11 +97,13 @@ func TestRestoreWithoutDraw(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
for i := 0; i < 1024*1024; i++ { for j := 0; j < 1024; j++ {
want := color.RGBA{0x00, 0x00, 0x00, 0x00} for i := 0; i < 1024; i++ {
got := pixelsToColor(img0.BasePixelsForTesting(), i) want := color.RGBA{0x00, 0x00, 0x00, 0x00}
if !sameColors(got, want, 0) { got := pixelsToColor(img0.BasePixelsForTesting(), i, j)
t.Errorf("got %v, want %v", got, want) if !sameColors(got, want, 0) {
t.Errorf("got %v, want %v", got, want)
}
} }
} }
} }
@ -139,7 +141,7 @@ func TestRestoreChain(t *testing.T) {
} }
want := clr want := clr
for i, img := range imgs { for i, img := range imgs {
got := pixelsToColor(img.BasePixelsForTesting(), 0) got := pixelsToColor(img.BasePixelsForTesting(), 0, 0)
if !sameColors(got, want, 1) { if !sameColors(got, want, 1) {
t.Errorf("%d: got %v, want %v", i, got, want) t.Errorf("%d: got %v, want %v", i, got, want)
} }
@ -186,7 +188,7 @@ func TestRestoreChain2(t *testing.T) {
if i == 8 || i == 9 { if i == 8 || i == 9 {
want = clr7 want = clr7
} }
got := pixelsToColor(img.BasePixelsForTesting(), 0) got := pixelsToColor(img.BasePixelsForTesting(), 0, 0)
if !sameColors(got, want, 1) { if !sameColors(got, want, 1) {
t.Errorf("%d: got %v, want %v", i, got, want) t.Errorf("%d: got %v, want %v", i, got, want)
} }
@ -228,22 +230,22 @@ func TestRestoreOverrideSource(t *testing.T) {
{ {
"0", "0",
clr1, clr1,
pixelsToColor(img0.BasePixelsForTesting(), 0), pixelsToColor(img0.BasePixelsForTesting(), 0, 0),
}, },
{ {
"1", "1",
clr1, clr1,
pixelsToColor(img1.BasePixelsForTesting(), 0), pixelsToColor(img1.BasePixelsForTesting(), 0, 0),
}, },
{ {
"2", "2",
clr0, clr0,
pixelsToColor(img2.BasePixelsForTesting(), 0), pixelsToColor(img2.BasePixelsForTesting(), 0, 0),
}, },
{ {
"3", "3",
clr0, clr0,
pixelsToColor(img3.BasePixelsForTesting(), 0), pixelsToColor(img3.BasePixelsForTesting(), 0, 0),
}, },
} }
for _, c := range testCases { for _, c := range testCases {
@ -365,7 +367,7 @@ func TestRestoreComplexGraph(t *testing.T) {
if c.out[i] == '*' { if c.out[i] == '*' {
want = color.RGBA{0xff, 0xff, 0xff, 0xff} want = color.RGBA{0xff, 0xff, 0xff, 0xff}
} }
got := pixelsToColor(c.image.BasePixelsForTesting(), i) got := pixelsToColor(c.image.BasePixelsForTesting(), i, 0)
if !sameColors(got, want, 1) { if !sameColors(got, want, 1) {
t.Errorf("%s[%d]: got %v, want %v", c.name, i, got, want) t.Errorf("%s[%d]: got %v, want %v", c.name, i, got, want)
} }
@ -426,7 +428,7 @@ func TestRestoreRecursive(t *testing.T) {
if c.out[i] == '*' { if c.out[i] == '*' {
want = color.RGBA{0xff, 0xff, 0xff, 0xff} want = color.RGBA{0xff, 0xff, 0xff, 0xff}
} }
got := pixelsToColor(c.image.BasePixelsForTesting(), i) got := pixelsToColor(c.image.BasePixelsForTesting(), i, 0)
if !sameColors(got, want, 1) { if !sameColors(got, want, 1) {
t.Errorf("%s[%d]: got %v, want %v", c.name, i, got, want) t.Errorf("%s[%d]: got %v, want %v", c.name, i, got, want)
} }
@ -543,55 +545,66 @@ func TestClear(t *testing.T) {
img.ReplacePixels(make([]byte, 4*4*4), 1, 1, 2, 2) img.ReplacePixels(make([]byte, 4*4*4), 1, 1, 2, 2)
cases := []struct { cases := []struct {
Index int i int
Want color.RGBA j int
want color.RGBA
}{ }{
{ {
Index: 0, i: 0,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff}, j: 0,
want: color.RGBA{0xff, 0xff, 0xff, 0xff},
}, },
{ {
Index: 3, i: 3,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff}, j: 0,
want: color.RGBA{0xff, 0xff, 0xff, 0xff},
}, },
{ {
Index: 4, i: 0,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff}, j: 1,
want: color.RGBA{0xff, 0xff, 0xff, 0xff},
}, },
{ {
Index: 5, i: 1,
Want: color.RGBA{0, 0, 0, 0}, j: 1,
want: color.RGBA{0, 0, 0, 0},
}, },
{ {
Index: 7, i: 3,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff}, j: 1,
want: color.RGBA{0xff, 0xff, 0xff, 0xff},
}, },
{ {
Index: 8, i: 0,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff}, j: 2,
want: color.RGBA{0xff, 0xff, 0xff, 0xff},
}, },
{ {
Index: 10, i: 2,
Want: color.RGBA{0, 0, 0, 0}, j: 2,
want: color.RGBA{0, 0, 0, 0},
}, },
{ {
Index: 11, i: 3,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff}, j: 2,
want: color.RGBA{0xff, 0xff, 0xff, 0xff},
}, },
{ {
Index: 12, i: 0,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff}, j: 3,
want: color.RGBA{0xff, 0xff, 0xff, 0xff},
}, },
{ {
Index: 15, i: 3,
Want: color.RGBA{0xff, 0xff, 0xff, 0xff}, j: 3,
want: color.RGBA{0xff, 0xff, 0xff, 0xff},
}, },
} }
for _, c := range cases { for _, c := range cases {
got := pixelsToColor(img.BasePixelsForTesting(), c.Index) got := pixelsToColor(img.BasePixelsForTesting(), c.i, c.j)
want := c.Want want := c.want
if got != want { if got != want {
t.Errorf("base pixel [%d]: got %v, want %v", c.Index, got, want) t.Errorf("base pixel (%d, %d): got %v, want %v", c.i, c.j, got, want)
} }
} }
} }
@ -613,17 +626,20 @@ func TestReplacePixelsOnly(t *testing.T) {
img0.ReplacePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1) img0.ReplacePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1)
// BasePixelsForTesting is available without GPU accessing. // BasePixelsForTesting is available without GPU accessing.
for i := 0; i < w*h; i++ { for j := 0; j < h; j++ {
var want color.RGBA for i := 0; i < w; i++ {
switch { idx := j*w + i
case i == 0: var want color.RGBA
want = color.RGBA{5, 6, 7, 8} switch {
case i%5 == 0: case idx == 0:
want = color.RGBA{1, 2, 3, 4} want = color.RGBA{5, 6, 7, 8}
} case idx%5 == 0:
got := pixelsToColor(img0.BasePixelsForTesting(), i) want = color.RGBA{1, 2, 3, 4}
if !sameColors(got, want, 0) { }
t.Errorf("got %v, want %v", got, want) got := pixelsToColor(img0.BasePixelsForTesting(), i, j)
if !sameColors(got, want, 0) {
t.Errorf("got %v, want %v", got, want)
}
} }
} }
@ -632,7 +648,7 @@ func TestReplacePixelsOnly(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
want := color.RGBA{1, 2, 3, 4} want := color.RGBA{1, 2, 3, 4}
got := pixelsToColor(img1.BasePixelsForTesting(), 0) got := pixelsToColor(img1.BasePixelsForTesting(), 0, 0)
if !sameColors(got, want, 0) { if !sameColors(got, want, 0) {
t.Errorf("got %v, want %v", got, want) t.Errorf("got %v, want %v", got, want)
} }