mipmap.go (8271B)
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 mipmap 16 17 import ( 18 "fmt" 19 "math" 20 21 "github.com/hajimehoshi/ebiten/v2/internal/affine" 22 "github.com/hajimehoshi/ebiten/v2/internal/buffered" 23 "github.com/hajimehoshi/ebiten/v2/internal/driver" 24 "github.com/hajimehoshi/ebiten/v2/internal/graphics" 25 "github.com/hajimehoshi/ebiten/v2/internal/shaderir" 26 ) 27 28 func BeginFrame() error { 29 return buffered.BeginFrame() 30 } 31 32 func EndFrame() error { 33 return buffered.EndFrame() 34 } 35 36 // Mipmap is a set of buffered.Image sorted by the order of mipmap level. 37 // The level 0 image is a regular image and higher-level images are used for mipmap. 38 type Mipmap struct { 39 width int 40 height int 41 volatile bool 42 orig *buffered.Image 43 imgs map[int]*buffered.Image 44 } 45 46 func New(width, height int) *Mipmap { 47 return &Mipmap{ 48 width: width, 49 height: height, 50 orig: buffered.NewImage(width, height), 51 } 52 } 53 54 func NewScreenFramebufferMipmap(width, height int) *Mipmap { 55 return &Mipmap{ 56 width: width, 57 height: height, 58 orig: buffered.NewScreenFramebufferImage(width, height), 59 } 60 } 61 62 func (m *Mipmap) SetVolatile(volatile bool) { 63 m.volatile = volatile 64 if m.volatile { 65 m.disposeMipmaps() 66 } 67 m.orig.SetVolatile(volatile) 68 } 69 70 func (m *Mipmap) DumpScreenshot(name string, blackbg bool) error { 71 return m.orig.DumpScreenshot(name, blackbg) 72 } 73 74 func (m *Mipmap) ReplacePixels(pix []byte, x, y, width, height int) error { 75 if err := m.orig.ReplacePixels(pix, x, y, width, height); err != nil { 76 return err 77 } 78 m.disposeMipmaps() 79 return nil 80 } 81 82 func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) { 83 return m.orig.Pixels(x, y, width, height) 84 } 85 86 func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices []float32, indices []uint16, colorm affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool, canSkipMipmap bool) { 87 if len(indices) == 0 { 88 return 89 } 90 91 level := 0 92 // TODO: Do we need to check all the sources' states of being volatile? 93 if !canSkipMipmap && srcs[0] != nil && !srcs[0].volatile && filter != driver.FilterScreen { 94 level = math.MaxInt32 95 for i := 0; i < len(indices)/3; i++ { 96 const n = graphics.VertexFloatNum 97 dx0 := vertices[n*indices[3*i]+0] 98 dy0 := vertices[n*indices[3*i]+1] 99 sx0 := vertices[n*indices[3*i]+2] 100 sy0 := vertices[n*indices[3*i]+3] 101 dx1 := vertices[n*indices[3*i+1]+0] 102 dy1 := vertices[n*indices[3*i+1]+1] 103 sx1 := vertices[n*indices[3*i+1]+2] 104 sy1 := vertices[n*indices[3*i+1]+3] 105 dx2 := vertices[n*indices[3*i+2]+0] 106 dy2 := vertices[n*indices[3*i+2]+1] 107 sx2 := vertices[n*indices[3*i+2]+2] 108 sy2 := vertices[n*indices[3*i+2]+3] 109 if l := mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, filter); level > l { 110 level = l 111 } 112 if l := mipmapLevelFromDistance(dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, filter); level > l { 113 level = l 114 } 115 if l := mipmapLevelFromDistance(dx2, dy2, dx0, dy0, sx2, sy2, sx0, sy0, filter); level > l { 116 level = l 117 } 118 } 119 if level == math.MaxInt32 { 120 panic("mipmap: level must be calculated at least once but not") 121 } 122 } 123 124 var s *buffered.Shader 125 if shader != nil { 126 s = shader.shader 127 } 128 129 var imgs [graphics.ShaderImageNum]*buffered.Image 130 for i, src := range srcs { 131 if src == nil { 132 continue 133 } 134 if level != 0 { 135 if img := src.level(level); img != nil { 136 const n = graphics.VertexFloatNum 137 s := float32(pow2(level)) 138 for i := 0; i < len(vertices)/n; i++ { 139 vertices[i*n+2] /= s 140 vertices[i*n+3] /= s 141 } 142 imgs[i] = img 143 continue 144 } 145 } 146 imgs[i] = src.orig 147 } 148 149 m.orig.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, s, uniforms, evenOdd) 150 m.disposeMipmaps() 151 } 152 153 func (m *Mipmap) setImg(level int, img *buffered.Image) { 154 if m.imgs == nil { 155 m.imgs = map[int]*buffered.Image{} 156 } 157 m.imgs[level] = img 158 } 159 160 func (m *Mipmap) level(level int) *buffered.Image { 161 if level == 0 { 162 panic("ebiten: level must be non-zero at level") 163 } 164 165 if m.volatile { 166 panic("ebiten: mipmap images for a volatile image is not implemented yet") 167 } 168 169 if img, ok := m.imgs[level]; ok { 170 return img 171 } 172 173 var src *buffered.Image 174 var vs []float32 175 var filter driver.Filter 176 switch { 177 case level == 1: 178 src = m.orig 179 vs = graphics.QuadVertices(0, 0, float32(m.width), float32(m.height), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1) 180 filter = driver.FilterLinear 181 case level > 1: 182 src = m.level(level - 1) 183 if src == nil { 184 m.setImg(level, nil) 185 return nil 186 } 187 w := sizeForLevel(m.width, level-1) 188 h := sizeForLevel(m.height, level-1) 189 vs = graphics.QuadVertices(0, 0, float32(w), float32(h), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1) 190 filter = driver.FilterLinear 191 default: 192 panic(fmt.Sprintf("ebiten: invalid level: %d", level)) 193 } 194 is := graphics.QuadIndices() 195 196 w2 := sizeForLevel(m.width, level-1) 197 h2 := sizeForLevel(m.height, level-1) 198 if w2 == 0 || h2 == 0 { 199 m.setImg(level, nil) 200 return nil 201 } 202 // buffered.NewImage panics with a too big size when actual allocation happens. 203 // 4096 should be a safe size in most environments (#1399). 204 // Unfortunately a precise max image size cannot be obtained here since this requires GPU access. 205 if w2 > 4096 || h2 > 4096 { 206 m.setImg(level, nil) 207 return nil 208 } 209 s := buffered.NewImage(w2, h2) 210 s.SetVolatile(m.volatile) 211 212 dstRegion := driver.Region{ 213 X: 0, 214 Y: 0, 215 Width: float32(w2), 216 Height: float32(h2), 217 } 218 s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, filter, driver.AddressUnsafe, dstRegion, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false) 219 m.setImg(level, s) 220 221 return m.imgs[level] 222 } 223 224 func sizeForLevel(x int, level int) int { 225 for i := 0; i < level; i++ { 226 x /= 2 227 if x == 0 { 228 return 0 229 } 230 } 231 return x 232 } 233 234 func (m *Mipmap) MarkDisposed() { 235 m.disposeMipmaps() 236 m.orig.MarkDisposed() 237 m.orig = nil 238 } 239 240 func (m *Mipmap) disposeMipmaps() { 241 for _, img := range m.imgs { 242 if img != nil { 243 img.MarkDisposed() 244 } 245 } 246 for k := range m.imgs { 247 delete(m.imgs, k) 248 } 249 } 250 251 // mipmapLevel returns an appropriate mipmap level for the given distance. 252 func mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1 float32, filter driver.Filter) int { 253 const maxLevel = 6 254 255 if filter == driver.FilterScreen { 256 return 0 257 } 258 259 d := (dx1-dx0)*(dx1-dx0) + (dy1-dy0)*(dy1-dy0) 260 s := (sx1-sx0)*(sx1-sx0) + (sy1-sy0)*(sy1-sy0) 261 if s == 0 { 262 return 0 263 } 264 scale := d / s 265 266 // Scale can be infinite when the specified scale is extremely big (#1398). 267 if math.IsInf(float64(scale), 0) { 268 return 0 269 } 270 271 // Scale can be zero when the specified scale is extremely small (#1398). 272 if scale == 0 { 273 return 0 274 } 275 276 if filter != driver.FilterLinear { 277 return 0 278 } 279 280 level := 0 281 for scale < 0.25 { 282 level++ 283 scale *= 4 284 } 285 286 if level > 0 { 287 // If the image can be scaled into 0 size, adjust the level. (#839) 288 w, h := int(sx1-sx0), int(sy1-sy0) 289 for level >= 0 { 290 s := 1 << uint(level) 291 if (w > 0 && w/s == 0) || (h > 0 && h/s == 0) { 292 level-- 293 continue 294 } 295 break 296 } 297 298 if level < 0 { 299 // As the render source is too small, nothing is rendered. 300 return 0 301 } 302 } 303 304 if level > maxLevel { 305 level = maxLevel 306 } 307 308 return level 309 } 310 311 func pow2(power int) float32 { 312 x := 1 313 return float32(x << uint(power)) 314 } 315 316 type Shader struct { 317 shader *buffered.Shader 318 } 319 320 func NewShader(program *shaderir.Program) *Shader { 321 return &Shader{ 322 shader: buffered.NewShader(program), 323 } 324 } 325 326 func (s *Shader) MarkDisposed() { 327 s.shader.MarkDisposed() 328 s.shader = nil 329 }