Add internal/shareable

This commit is contained in:
Hajime Hoshi 2018-03-10 23:48:10 +09:00
parent 1556db74fa
commit df3960a97c
2 changed files with 57 additions and 48 deletions

View File

@ -23,7 +23,7 @@ import (
"github.com/hajimehoshi/ebiten/internal/graphics" "github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/graphicsutil" "github.com/hajimehoshi/ebiten/internal/graphicsutil"
"github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/restorable" "github.com/hajimehoshi/ebiten/internal/shareable"
) )
// emptyImage is an empty image used for filling other images with a uniform color. // emptyImage is an empty image used for filling other images with a uniform color.
@ -51,7 +51,7 @@ type Image struct {
// See strings.Builder for similar examples. // See strings.Builder for similar examples.
addr *Image addr *Image
shareableImagePart *shareableImagePart shareableImagePart *shareable.ImagePart
filter Filter filter Filter
} }
@ -154,13 +154,6 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) error {
if img.isDisposed() { if img.isDisposed() {
panic("ebiten: the given image to DrawImage must not be disposed") panic("ebiten: the given image to DrawImage must not be disposed")
} }
i.shareableImagePart.ensureNotShared()
// Compare i and img after ensuring i is not shared, or
// i and img might share the same texture even though i != img.
if i == img {
panic("ebiten: Image.DrawImage: img must be different from the receiver")
}
if i.isDisposed() { if i.isDisposed() {
return nil return nil
} }
@ -357,7 +350,7 @@ type DrawImageOptions struct {
// //
// Error returned by NewImage is always nil as of 1.5.0-alpha. // Error returned by NewImage is always nil as of 1.5.0-alpha.
func NewImage(width, height int, filter Filter) (*Image, error) { func NewImage(width, height int, filter Filter) (*Image, error) {
s := newSharedImagePart(width, height) s := shareable.NewImagePart(width, height)
i := &Image{ i := &Image{
shareableImagePart: s, shareableImagePart: s,
filter: filter, filter: filter,
@ -369,7 +362,7 @@ func NewImage(width, height int, filter Filter) (*Image, error) {
// newImageWithoutInit creates an empty image without initialization. // newImageWithoutInit creates an empty image without initialization.
func newImageWithoutInit(width, height int) *Image { func newImageWithoutInit(width, height int) *Image {
s := newSharedImagePart(width, height) s := shareable.NewImagePart(width, height)
i := &Image{ i := &Image{
shareableImagePart: s, shareableImagePart: s,
filter: FilterDefault, filter: FilterDefault,
@ -394,14 +387,9 @@ func newImageWithoutInit(width, height int) *Image {
// //
// Error returned by newVolatileImage is always nil as of 1.5.0-alpha. // Error returned by newVolatileImage is always nil as of 1.5.0-alpha.
func newVolatileImage(width, height int, filter Filter) *Image { func newVolatileImage(width, height int, filter Filter) *Image {
r := restorable.NewImage(width, height, true)
i := &Image{ i := &Image{
shareableImagePart: &shareableImagePart{ shareableImagePart: shareable.NewVolatileImagePart(width, height),
shareableImage: &shareableImage{ filter: filter,
restorable: r,
},
},
filter: filter,
} }
i.fill(0, 0, 0, 0) i.fill(0, 0, 0, 0)
runtime.SetFinalizer(i, (*Image).Dispose) runtime.SetFinalizer(i, (*Image).Dispose)
@ -421,7 +409,7 @@ func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
width, height := size.X, size.Y width, height := size.X, size.Y
s := newSharedImagePart(width, height) s := shareable.NewImagePart(width, height)
i := &Image{ i := &Image{
shareableImagePart: s, shareableImagePart: s,
filter: filter, filter: filter,
@ -433,14 +421,9 @@ func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
} }
func newImageWithScreenFramebuffer(width, height int) *Image { func newImageWithScreenFramebuffer(width, height int) *Image {
r := restorable.NewScreenFramebufferImage(width, height)
i := &Image{ i := &Image{
shareableImagePart: &shareableImagePart{ shareableImagePart: shareable.NewScreenFramebufferImagePart(width, height),
shareableImage: &shareableImage{ filter: FilterDefault,
restorable: r,
},
},
filter: FilterDefault,
} }
runtime.SetFinalizer(i, (*Image).Dispose) runtime.SetFinalizer(i, (*Image).Dispose)
return i return i

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package ebiten package shareable
import ( import (
"fmt" "fmt"
@ -26,24 +26,24 @@ import (
"github.com/hajimehoshi/ebiten/internal/sync" "github.com/hajimehoshi/ebiten/internal/sync"
) )
type shareableImage struct { type Image struct {
restorable *restorable.Image restorable *restorable.Image
page *packing.Page page *packing.Page
} }
var ( var (
// theSharedImages is a set of actually shared images. // theSharedImages is a set of actually shared images.
theSharedImages = []*shareableImage{} theSharedImages = []*Image{}
) )
type shareableImagePart struct { type ImagePart struct {
shareableImage *shareableImage shareableImage *Image
// If node is nil, the image is not shared. // If node is nil, the image is not shared.
node *packing.Node node *packing.Node
} }
func (s *shareableImagePart) ensureNotShared() { func (s *ImagePart) ensureNotShared() {
if s.node == nil { if s.node == nil {
return return
} }
@ -53,12 +53,12 @@ func (s *shareableImagePart) ensureNotShared() {
newImg.DrawImage(s.shareableImage.restorable, x, y, w, h, nil, nil, opengl.CompositeModeCopy, graphics.FilterNearest) newImg.DrawImage(s.shareableImage.restorable, x, y, w, h, nil, nil, opengl.CompositeModeCopy, graphics.FilterNearest)
s.Dispose() s.Dispose()
s.shareableImage = &shareableImage{ s.shareableImage = &Image{
restorable: newImg, restorable: newImg,
} }
} }
func (s *shareableImagePart) region() (x, y, width, height int) { func (s *ImagePart) region() (x, y, width, height int) {
if s.node == nil { if s.node == nil {
w, h := s.shareableImage.restorable.Size() w, h := s.shareableImage.restorable.Size()
return 0, 0, w, h return 0, 0, w, h
@ -66,12 +66,20 @@ func (s *shareableImagePart) region() (x, y, width, height int) {
return s.node.Region() return s.node.Region()
} }
func (s *shareableImagePart) Size() (width, height int) { func (s *ImagePart) Size() (width, height int) {
_, _, w, h := s.region() _, _, w, h := s.region()
return w, h return w, h
} }
func (s *shareableImagePart) DrawImage(img *shareableImagePart, sx0, sy0, sx1, sy1 int, geom *affine.GeoM, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) { func (s *ImagePart) DrawImage(img *ImagePart, sx0, sy0, sx1, sy1 int, geom *affine.GeoM, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) {
s.ensureNotShared()
// Compare i and img after ensuring i is not shared, or
// i and img might share the same texture even though i != img.
if s.shareableImage.restorable == img.shareableImage.restorable {
panic("shareable: Image.DrawImage: img must be different from the receiver")
}
dx, dy, _, _ := img.region() dx, dy, _, _ := img.region()
sx0 += dx sx0 += dx
sy0 += dy sy0 += dy
@ -80,15 +88,15 @@ func (s *shareableImagePart) DrawImage(img *shareableImagePart, sx0, sy0, sx1, s
s.shareableImage.restorable.DrawImage(img.shareableImage.restorable, sx0, sy0, sx1, sy1, geom, colorm, mode, filter) s.shareableImage.restorable.DrawImage(img.shareableImage.restorable, sx0, sy0, sx1, sy1, geom, colorm, mode, filter)
} }
func (s *shareableImagePart) ReplacePixels(p []byte) { func (s *ImagePart) ReplacePixels(p []byte) {
x, y, w, h := s.region() x, y, w, h := s.region()
if l := 4 * w * h; len(p) != l { if l := 4 * w * h; len(p) != l {
panic(fmt.Sprintf("ebiten: len(p) was %d but must be %d", len(p), l)) panic(fmt.Sprintf("shareable: len(p) was %d but must be %d", len(p), l))
} }
s.shareableImage.restorable.ReplacePixels(p, x, y, w, h) s.shareableImage.restorable.ReplacePixels(p, x, y, w, h)
} }
func (s *shareableImagePart) At(x, y int) (color.Color, error) { func (s *ImagePart) At(x, y int) (color.Color, error) {
ox, oy, w, h := s.region() ox, oy, w, h := s.region()
if x < 0 || y < 0 || x >= w || y >= h { if x < 0 || y < 0 || x >= w || y >= h {
return color.RGBA{}, nil return color.RGBA{}, nil
@ -96,11 +104,11 @@ func (s *shareableImagePart) At(x, y int) (color.Color, error) {
return s.shareableImage.restorable.At(x+ox, y+oy) return s.shareableImage.restorable.At(x+ox, y+oy)
} }
func (s *shareableImagePart) isDisposed() bool { func (s *ImagePart) isDisposed() bool {
return s.shareableImage == nil return s.shareableImage == nil
} }
func (s *shareableImagePart) Dispose() { func (s *ImagePart) Dispose() {
if s.isDisposed() { if s.isDisposed() {
return return
} }
@ -133,36 +141,36 @@ func (s *shareableImagePart) Dispose() {
theSharedImages = append(theSharedImages[:index], theSharedImages[index+1:]...) theSharedImages = append(theSharedImages[:index], theSharedImages[index+1:]...)
} }
func (s *shareableImagePart) IsInvalidated() (bool, error) { func (s *ImagePart) IsInvalidated() (bool, error) {
return s.shareableImage.restorable.IsInvalidated() return s.shareableImage.restorable.IsInvalidated()
} }
var shareableImageLock sync.Mutex var shareableImageLock sync.Mutex
func newSharedImagePart(width, height int) *shareableImagePart { func NewImagePart(width, height int) *ImagePart {
const maxSize = 2048 const maxSize = 2048
shareableImageLock.Lock() shareableImageLock.Lock()
defer shareableImageLock.Unlock() defer shareableImageLock.Unlock()
if width > maxSize || height > maxSize { if width > maxSize || height > maxSize {
s := &shareableImage{ s := &Image{
restorable: restorable.NewImage(width, height, false), restorable: restorable.NewImage(width, height, false),
} }
return &shareableImagePart{ return &ImagePart{
shareableImage: s, shareableImage: s,
} }
} }
for _, s := range theSharedImages { for _, s := range theSharedImages {
if n := s.page.Alloc(width, height); n != nil { if n := s.page.Alloc(width, height); n != nil {
return &shareableImagePart{ return &ImagePart{
shareableImage: s, shareableImage: s,
node: n, node: n,
} }
} }
} }
s := &shareableImage{ s := &Image{
restorable: restorable.NewImage(maxSize, maxSize, false), restorable: restorable.NewImage(maxSize, maxSize, false),
page: packing.NewPage(maxSize), page: packing.NewPage(maxSize),
} }
@ -172,8 +180,26 @@ func newSharedImagePart(width, height int) *shareableImagePart {
if n == nil { if n == nil {
panic("not reached") panic("not reached")
} }
return &shareableImagePart{ return &ImagePart{
shareableImage: s, shareableImage: s,
node: n, node: n,
} }
} }
func NewVolatileImagePart(width, height int) *ImagePart {
r := restorable.NewImage(width, height, true)
return &ImagePart{
shareableImage: &Image{
restorable: r,
},
}
}
func NewScreenFramebufferImagePart(width, height int) *ImagePart {
r := restorable.NewScreenFramebufferImage(width, height)
return &ImagePart{
shareableImage: &Image{
restorable: r,
},
}
}