diff --git a/imageimpl.go b/imageimpl.go index 152895f4c..92c19df12 100644 --- a/imageimpl.go +++ b/imageimpl.go @@ -28,26 +28,16 @@ import ( "github.com/hajimehoshi/ebiten/internal/loop" ) -type drawImageHistoryItem struct { - image *Image - vertices []int16 - geom GeoM - colorm ColorM - mode opengl.CompositeMode -} - type imageImpl struct { - image *graphics.Image - disposed bool - width int - height int - filter Filter - pixels []uint8 - baseColor color.Color - drawImageHistory []*drawImageHistoryItem - volatile bool - screen bool - m sync.Mutex + image *graphics.Image + disposed bool + width int + height int + filter Filter + restoringInfo imageRestoringInfo + volatile bool + screen bool + m sync.Mutex } func newImageImpl(width, height int, filter Filter, volatile bool) (*imageImpl, error) { @@ -61,8 +51,9 @@ func newImageImpl(width, height int, filter Filter, volatile bool) (*imageImpl, height: height, filter: filter, volatile: volatile, - pixels: make([]uint8, width*height*4), } + i.restoringInfo.imageImpl = i + i.restoringInfo.resetWithPixels(make([]uint8, width*height*4)) runtime.SetFinalizer(i, (*imageImpl).Dispose) return i, nil } @@ -93,8 +84,9 @@ func newImageImplFromImage(source image.Image, filter Filter) (*imageImpl, error width: w, height: h, filter: filter, - pixels: pixels, } + i.restoringInfo.imageImpl = i + i.restoringInfo.resetWithPixels(pixels) runtime.SetFinalizer(i, (*imageImpl).Dispose) return i, nil } @@ -110,8 +102,9 @@ func newScreenImageImpl(width, height int) (*imageImpl, error) { height: height, volatile: true, screen: true, - pixels: make([]uint8, width*height*4), } + i.restoringInfo.imageImpl = i + i.restoringInfo.resetWithPixels(make([]uint8, width*height*4)) runtime.SetFinalizer(i, (*imageImpl).Dispose) return i, nil } @@ -122,9 +115,7 @@ func (i *imageImpl) Fill(clr color.Color) error { if i.disposed { return errors.New("ebiten: image is already disposed") } - i.pixels = nil - i.baseColor = clr - i.drawImageHistory = nil + i.restoringInfo.fill(clr) return i.image.Fill(clr) } @@ -137,9 +128,7 @@ func (i *imageImpl) clearIfVolatile() error { if !i.volatile { return nil } - i.pixels = nil - i.baseColor = nil - i.drawImageHistory = nil + i.restoringInfo.clear() return i.image.Fill(color.Transparent) } @@ -181,7 +170,7 @@ func (i *imageImpl) DrawImage(image *Image, options *DrawImageOptions) error { colorm: options.ColorM, mode: opengl.CompositeMode(options.CompositeMode), } - i.drawImageHistory = append(i.drawImageHistory, c) + i.restoringInfo.appendDrawImageHistory(c) geom := &options.GeoM colorm := &options.ColorM mode := opengl.CompositeMode(options.CompositeMode) @@ -200,26 +189,11 @@ func (i *imageImpl) At(x, y int, context *opengl.Context) color.Color { if i.disposed { return color.Transparent } - if i.pixels == nil || i.drawImageHistory != nil { - var err error - i.pixels, err = i.image.Pixels(context) - if err != nil { - panic(err) - } - i.drawImageHistory = nil + clr, err := i.restoringInfo.at(x, y, context) + if err != nil { + panic(err) } - idx := 4*x + 4*y*i.width - r, g, b, a := i.pixels[idx], i.pixels[idx+1], i.pixels[idx+2], i.pixels[idx+3] - return color.RGBA{r, g, b, a} -} - -func (i *imageImpl) hasHistoryWith(target *Image) bool { - for _, c := range i.drawImageHistory { - if c.image == target { - return true - } - } - return false + return clr } func (i *imageImpl) resetHistoryIfNeeded(target *Image, context *opengl.Context) error { @@ -228,26 +202,16 @@ func (i *imageImpl) resetHistoryIfNeeded(target *Image, context *opengl.Context) if i.disposed { return nil } - if i.drawImageHistory == nil { - return nil + if err := i.restoringInfo.resetHistoryIfNeeded(target, context); err != nil { + return err } - if !i.hasHistoryWith(target) { - return nil - } - var err error - i.pixels, err = i.image.Pixels(context) - if err != nil { - return nil - } - i.baseColor = nil - i.drawImageHistory = nil return nil } func (i *imageImpl) hasHistory() bool { i.m.Lock() defer i.m.Unlock() - return i.drawImageHistory != nil + return i.restoringInfo.hasHistory() } func (i *imageImpl) restore(context *opengl.Context) error { @@ -274,42 +238,11 @@ func (i *imageImpl) restore(context *opengl.Context) error { } return nil } - img := image.NewRGBA(image.Rect(0, 0, i.width, i.height)) - if i.pixels != nil { - for j := 0; j < i.height; j++ { - copy(img.Pix[j*img.Stride:], i.pixels[j*i.width*4:(j+1)*i.width*4]) - } - } else if i.baseColor != nil { - r32, g32, b32, a32 := i.baseColor.RGBA() - r, g, b, a := uint8(r32), uint8(g32), uint8(b32), uint8(a32) - for idx := 0; idx < len(img.Pix)/4; idx++ { - img.Pix[4*idx] = r - img.Pix[4*idx+1] = g - img.Pix[4*idx+2] = b - img.Pix[4*idx+3] = a - } - } var err error - i.image, err = graphics.NewImageFromImage(img, glFilter(i.filter)) + i.image, err = i.restoringInfo.restore(context) if err != nil { return err } - for _, c := range i.drawImageHistory { - if c.image.impl.hasHistory() { - panic("not reach") - } - if err := i.image.DrawImage(c.image.impl.image, c.vertices, &c.geom, &c.colorm, c.mode); err != nil { - return err - } - } - if 0 < len(i.drawImageHistory) { - i.pixels, err = i.image.Pixels(context) - if err != nil { - return err - } - } - i.baseColor = nil - i.drawImageHistory = nil return nil } @@ -326,9 +259,7 @@ func (i *imageImpl) Dispose() error { } i.image = nil i.disposed = true - i.pixels = nil - i.baseColor = nil - i.drawImageHistory = nil + i.restoringInfo.clear() runtime.SetFinalizer(i, nil) return nil } @@ -339,12 +270,7 @@ func (i *imageImpl) ReplacePixels(p []uint8) error { } i.m.Lock() defer i.m.Unlock() - if i.pixels == nil { - i.pixels = make([]uint8, i.width*i.height*4) - } - copy(i.pixels, p) - i.baseColor = nil - i.drawImageHistory = nil + i.restoringInfo.resetWithPixels(p) if i.disposed { return errors.New("ebiten: image is already disposed") } diff --git a/imagerestoringinfo.go b/imagerestoringinfo.go new file mode 100644 index 000000000..bc352526b --- /dev/null +++ b/imagerestoringinfo.go @@ -0,0 +1,146 @@ +// Copyright 2016 Hajime Hoshi +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ebiten + +import ( + "image" + "image/color" + + "github.com/hajimehoshi/ebiten/internal/graphics" + "github.com/hajimehoshi/ebiten/internal/graphics/opengl" +) + +type drawImageHistoryItem struct { + image *Image + vertices []int16 + geom GeoM + colorm ColorM + mode opengl.CompositeMode +} + +type imageRestoringInfo struct { + imageImpl *imageImpl + pixels []uint8 + baseColor color.Color + drawImageHistory []*drawImageHistoryItem +} + +func (i *imageRestoringInfo) resetWithPixels(pixels []uint8) { + if i.pixels == nil { + i.pixels = make([]uint8, len(pixels)) + } + copy(i.pixels, pixels) + i.baseColor = nil + i.drawImageHistory = nil +} + +func (i *imageRestoringInfo) clear() { + i.pixels = nil + i.baseColor = nil + i.drawImageHistory = nil +} + +func (i *imageRestoringInfo) fill(clr color.Color) { + i.pixels = nil + i.baseColor = clr + i.drawImageHistory = nil +} + +func (i *imageRestoringInfo) appendDrawImageHistory(item *drawImageHistoryItem) { + i.drawImageHistory = append(i.drawImageHistory, item) +} + +func (i *imageRestoringInfo) at(x, y int, context *opengl.Context) (color.Color, error) { + if i.pixels == nil || i.drawImageHistory != nil { + var err error + i.pixels, err = i.imageImpl.image.Pixels(context) + if err != nil { + return nil, err + } + i.baseColor = nil + i.drawImageHistory = nil + } + idx := 4*x + 4*y*i.imageImpl.width + r, g, b, a := i.pixels[idx], i.pixels[idx+1], i.pixels[idx+2], i.pixels[idx+3] + return color.RGBA{r, g, b, a}, nil +} + +func (i *imageRestoringInfo) hasHistoryWith(target *Image) bool { + for _, c := range i.drawImageHistory { + if c.image == target { + return true + } + } + return false +} + +func (i *imageRestoringInfo) resetHistoryIfNeeded(target *Image, context *opengl.Context) error { + if i.drawImageHistory == nil { + return nil + } + if !i.hasHistoryWith(target) { + return nil + } + var err error + i.pixels, err = i.imageImpl.image.Pixels(context) + if err != nil { + return err + } + i.baseColor = nil + i.drawImageHistory = nil + return nil +} + +func (i *imageRestoringInfo) hasHistory() bool { + return i.drawImageHistory != nil +} + +func (i *imageRestoringInfo) restore(context *opengl.Context) (*graphics.Image, error) { + w, h := i.imageImpl.width, i.imageImpl.height + img := image.NewRGBA(image.Rect(0, 0, w, h)) + if i.pixels != nil { + for j := 0; j < h; j++ { + copy(img.Pix[j*img.Stride:], i.pixels[j*w*4:(j+1)*w*4]) + } + } else if i.baseColor != nil { + r32, g32, b32, a32 := i.baseColor.RGBA() + r, g, b, a := uint8(r32), uint8(g32), uint8(b32), uint8(a32) + for idx := 0; idx < len(img.Pix)/4; idx++ { + img.Pix[4*idx] = r + img.Pix[4*idx+1] = g + img.Pix[4*idx+2] = b + img.Pix[4*idx+3] = a + } + } + gimg, err := graphics.NewImageFromImage(img, glFilter(i.imageImpl.filter)) + if err != nil { + return nil, err + } + for _, c := range i.drawImageHistory { + if c.image.impl.hasHistory() { + panic("not reach") + } + if err := gimg.DrawImage(c.image.impl.image, c.vertices, &c.geom, &c.colorm, c.mode); err != nil { + return nil, err + } + } + i.pixels, err = gimg.Pixels(context) + if err != nil { + return nil, err + } + i.baseColor = nil + i.drawImageHistory = nil + return gimg, nil +}