image.go (3442B)
1 // Copyright 2018 The Ebiten Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package opengl 16 17 import ( 18 "github.com/hajimehoshi/ebiten/v2/internal/driver" 19 "github.com/hajimehoshi/ebiten/v2/internal/graphics" 20 ) 21 22 type Image struct { 23 id driver.ImageID 24 graphics *Graphics 25 texture textureNative 26 stencil renderbufferNative 27 framebuffer *framebuffer 28 width int 29 height int 30 screen bool 31 } 32 33 func (i *Image) ID() driver.ImageID { 34 return i.id 35 } 36 37 func (i *Image) IsInvalidated() bool { 38 return !i.graphics.context.isTexture(i.texture) 39 } 40 41 func (i *Image) Dispose() { 42 if i.framebuffer != nil { 43 i.framebuffer.delete(&i.graphics.context) 44 } 45 if !i.texture.equal(*new(textureNative)) { 46 i.graphics.context.deleteTexture(i.texture) 47 } 48 if !i.stencil.equal(*new(renderbufferNative)) { 49 i.graphics.context.deleteRenderbuffer(i.stencil) 50 } 51 52 i.graphics.removeImage(i) 53 } 54 55 func (i *Image) setViewport() error { 56 if err := i.ensureFramebuffer(); err != nil { 57 return err 58 } 59 i.graphics.context.setViewport(i.framebuffer) 60 return nil 61 } 62 63 func (i *Image) Pixels() ([]byte, error) { 64 if err := i.ensureFramebuffer(); err != nil { 65 return nil, err 66 } 67 68 p := i.graphics.context.framebufferPixels(i.framebuffer, i.width, i.height) 69 return p, nil 70 } 71 72 func (i *Image) framebufferSize() (int, int) { 73 if i.screen { 74 // The (default) framebuffer size can't be converted to a power of 2. 75 // On browsers, i.width and i.height are used as viewport size and 76 // Edge can't treat a bigger viewport than the drawing area (#71). 77 return i.width, i.height 78 } 79 return graphics.InternalImageSize(i.width), graphics.InternalImageSize(i.height) 80 } 81 82 func (i *Image) ensureFramebuffer() error { 83 if i.framebuffer != nil { 84 return nil 85 } 86 87 w, h := i.framebufferSize() 88 if i.screen { 89 i.framebuffer = newScreenFramebuffer(&i.graphics.context, w, h) 90 return nil 91 } 92 f, err := newFramebufferFromTexture(&i.graphics.context, i.texture, w, h) 93 if err != nil { 94 return err 95 } 96 i.framebuffer = f 97 return nil 98 } 99 100 func (i *Image) ensureStencilBuffer() error { 101 if !i.stencil.equal(*new(renderbufferNative)) { 102 return nil 103 } 104 105 if err := i.ensureFramebuffer(); err != nil { 106 return err 107 } 108 109 r, err := i.graphics.context.newRenderbuffer(i.framebufferSize()) 110 if err != nil { 111 return err 112 } 113 i.stencil = r 114 115 if err := i.graphics.context.bindStencilBuffer(i.framebuffer.native, i.stencil); err != nil { 116 return err 117 } 118 return nil 119 } 120 121 func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) { 122 if i.screen { 123 panic("opengl: ReplacePixels cannot be called on the screen, that doesn't have a texture") 124 } 125 if len(args) == 0 { 126 return 127 } 128 129 // glFlush is necessary on Android. 130 // glTexSubImage2D didn't work without this hack at least on Nexus 5x and NuAns NEO [Reloaded] (#211). 131 if i.graphics.drawCalled { 132 i.graphics.context.flush() 133 } 134 i.graphics.drawCalled = false 135 i.graphics.context.texSubImage2D(i.texture, args) 136 }