twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

mipmap.go (10253B)


      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 	"image/color"
     20 	"math"
     21 
     22 	"github.com/hajimehoshi/ebiten/v2/internal/affine"
     23 	"github.com/hajimehoshi/ebiten/v2/internal/buffered"
     24 	"github.com/hajimehoshi/ebiten/v2/internal/driver"
     25 	"github.com/hajimehoshi/ebiten/v2/internal/graphics"
     26 	"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
     27 )
     28 
     29 var graphicsDriver driver.Graphics
     30 
     31 func SetGraphicsDriver(graphics driver.Graphics) {
     32 	graphicsDriver = graphics
     33 }
     34 
     35 func BeginFrame() error {
     36 	return buffered.BeginFrame()
     37 }
     38 
     39 func EndFrame() error {
     40 	return buffered.EndFrame()
     41 }
     42 
     43 // Mipmap is a set of buffered.Image sorted by the order of mipmap level.
     44 // The level 0 image is a regular image and higher-level images are used for mipmap.
     45 type Mipmap struct {
     46 	width    int
     47 	height   int
     48 	volatile bool
     49 	orig     *buffered.Image
     50 	imgs     map[int]*buffered.Image
     51 }
     52 
     53 func New(width, height int) *Mipmap {
     54 	return &Mipmap{
     55 		width:  width,
     56 		height: height,
     57 		orig:   buffered.NewImage(width, height),
     58 		imgs:   map[int]*buffered.Image{},
     59 	}
     60 }
     61 
     62 func NewScreenFramebufferMipmap(width, height int) *Mipmap {
     63 	return &Mipmap{
     64 		width:  width,
     65 		height: height,
     66 		orig:   buffered.NewScreenFramebufferImage(width, height),
     67 		imgs:   map[int]*buffered.Image{},
     68 	}
     69 }
     70 
     71 func (m *Mipmap) SetVolatile(volatile bool) {
     72 	m.volatile = volatile
     73 	if m.volatile {
     74 		m.disposeMipmaps()
     75 	}
     76 	m.orig.SetVolatile(volatile)
     77 }
     78 
     79 func (m *Mipmap) Dump(name string, blackbg bool) error {
     80 	return m.orig.Dump(name, blackbg)
     81 }
     82 
     83 func (m *Mipmap) Fill(clr color.RGBA) {
     84 	m.orig.Fill(clr)
     85 	m.disposeMipmaps()
     86 }
     87 
     88 func (m *Mipmap) ReplacePixels(pix []byte, x, y, width, height int) error {
     89 	if err := m.orig.ReplacePixels(pix, x, y, width, height); err != nil {
     90 		return err
     91 	}
     92 	m.disposeMipmaps()
     93 	return nil
     94 }
     95 
     96 func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) {
     97 	return m.orig.Pixels(x, y, width, height)
     98 }
     99 
    100 func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, canSkipMipmap bool) {
    101 	if len(indices) == 0 {
    102 		return
    103 	}
    104 
    105 	level := 0
    106 	// TODO: Do we need to check all the sources' states of being volatile?
    107 	if !canSkipMipmap && srcs[0] != nil && !srcs[0].volatile && filter != driver.FilterScreen {
    108 		level = math.MaxInt32
    109 		for i := 0; i < len(indices)/3; i++ {
    110 			const n = graphics.VertexFloatNum
    111 			dx0 := vertices[n*indices[3*i]+0]
    112 			dy0 := vertices[n*indices[3*i]+1]
    113 			sx0 := vertices[n*indices[3*i]+2]
    114 			sy0 := vertices[n*indices[3*i]+3]
    115 			dx1 := vertices[n*indices[3*i+1]+0]
    116 			dy1 := vertices[n*indices[3*i+1]+1]
    117 			sx1 := vertices[n*indices[3*i+1]+2]
    118 			sy1 := vertices[n*indices[3*i+1]+3]
    119 			dx2 := vertices[n*indices[3*i+2]+0]
    120 			dy2 := vertices[n*indices[3*i+2]+1]
    121 			sx2 := vertices[n*indices[3*i+2]+2]
    122 			sy2 := vertices[n*indices[3*i+2]+3]
    123 			if l := mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, filter); level > l {
    124 				level = l
    125 			}
    126 			if l := mipmapLevelFromDistance(dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, filter); level > l {
    127 				level = l
    128 			}
    129 			if l := mipmapLevelFromDistance(dx2, dy2, dx0, dy0, sx2, sy2, sx0, sy0, filter); level > l {
    130 				level = l
    131 			}
    132 		}
    133 		if level == math.MaxInt32 {
    134 			panic("mipmap: level must be calculated at least once but not")
    135 		}
    136 	}
    137 
    138 	if colorm != nil && colorm.ScaleOnly() {
    139 		body, _ := colorm.UnsafeElements()
    140 		cr := body[0]
    141 		cg := body[5]
    142 		cb := body[10]
    143 		ca := body[15]
    144 		colorm = nil
    145 		const n = graphics.VertexFloatNum
    146 		for i := 0; i < len(vertices)/n; i++ {
    147 			vertices[i*n+4] *= cr
    148 			vertices[i*n+5] *= cg
    149 			vertices[i*n+6] *= cb
    150 			vertices[i*n+7] *= ca
    151 		}
    152 	}
    153 
    154 	var s *buffered.Shader
    155 	if shader != nil {
    156 		s = shader.shader
    157 	}
    158 
    159 	var imgs [graphics.ShaderImageNum]*buffered.Image
    160 	for i, src := range srcs {
    161 		if src == nil {
    162 			continue
    163 		}
    164 		if level != 0 {
    165 			if img := src.level(level); img != nil {
    166 				const n = graphics.VertexFloatNum
    167 				s := float32(pow2(level))
    168 				for i := 0; i < len(vertices)/n; i++ {
    169 					vertices[i*n+2] /= s
    170 					vertices[i*n+3] /= s
    171 				}
    172 				imgs[i] = img
    173 				continue
    174 			}
    175 		}
    176 		imgs[i] = src.orig
    177 	}
    178 
    179 	m.orig.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, subimageOffsets, s, uniforms)
    180 	m.disposeMipmaps()
    181 }
    182 
    183 func (m *Mipmap) level(level int) *buffered.Image {
    184 	if level == 0 {
    185 		panic("ebiten: level must be non-zero at level")
    186 	}
    187 
    188 	if m.volatile {
    189 		panic("ebiten: mipmap images for a volatile image is not implemented yet")
    190 	}
    191 
    192 	if img, ok := m.imgs[level]; ok {
    193 		return img
    194 	}
    195 
    196 	var src *buffered.Image
    197 	var vs []float32
    198 	var filter driver.Filter
    199 	switch {
    200 	case level == 1:
    201 		src = m.orig
    202 		vs = graphics.QuadVertices(0, 0, float32(m.width), float32(m.height), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1, false)
    203 		filter = driver.FilterLinear
    204 	case level > 1:
    205 		src = m.level(level - 1)
    206 		if src == nil {
    207 			m.imgs[level] = nil
    208 			return nil
    209 		}
    210 		w := sizeForLevel(m.width, level-1)
    211 		h := sizeForLevel(m.height, level-1)
    212 		vs = graphics.QuadVertices(0, 0, float32(w), float32(h), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1, false)
    213 		filter = driver.FilterLinear
    214 	case level == -1:
    215 		src = m.orig
    216 		vs = graphics.QuadVertices(0, 0, float32(m.width), float32(m.height), 2, 0, 0, 2, 0, 0, 1, 1, 1, 1, false)
    217 		filter = driver.FilterNearest
    218 	case level < -1:
    219 		src = m.level(level + 1)
    220 		if src == nil {
    221 			m.imgs[level] = nil
    222 			return nil
    223 		}
    224 		w := sizeForLevel(m.width, level-1)
    225 		h := sizeForLevel(m.height, level-1)
    226 		vs = graphics.QuadVertices(0, 0, float32(w), float32(h), 2, 0, 0, 2, 0, 0, 1, 1, 1, 1, false)
    227 		filter = driver.FilterNearest
    228 	default:
    229 		panic(fmt.Sprintf("ebiten: invalid level: %d", level))
    230 	}
    231 	is := graphics.QuadIndices()
    232 
    233 	w2 := sizeForLevel(m.width, level-1)
    234 	h2 := sizeForLevel(m.height, level-1)
    235 	if w2 == 0 || h2 == 0 {
    236 		m.imgs[level] = nil
    237 		return nil
    238 	}
    239 	// buffered.NewImage panics with a too big size when actual allocation happens.
    240 	// 4096 should be a safe size in most environments (#1399).
    241 	// Unfortunately a precise max image size cannot be obtained here since this requires GPU access.
    242 	if w2 > 4096 || h2 > 4096 {
    243 		m.imgs[level] = nil
    244 		return nil
    245 	}
    246 	s := buffered.NewImage(w2, h2)
    247 	s.SetVolatile(m.volatile)
    248 	s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
    249 	m.imgs[level] = s
    250 
    251 	return m.imgs[level]
    252 }
    253 
    254 func sizeForLevel(x int, level int) int {
    255 	if level > 0 {
    256 		for i := 0; i < level; i++ {
    257 			x /= 2
    258 			if x == 0 {
    259 				return 0
    260 			}
    261 		}
    262 	} else {
    263 		for i := 0; i < -level; i++ {
    264 			x *= 2
    265 		}
    266 	}
    267 	return x
    268 }
    269 
    270 func (m *Mipmap) MarkDisposed() {
    271 	m.disposeMipmaps()
    272 	m.orig.MarkDisposed()
    273 	m.orig = nil
    274 }
    275 
    276 func (m *Mipmap) disposeMipmaps() {
    277 	for _, img := range m.imgs {
    278 		if img != nil {
    279 			img.MarkDisposed()
    280 		}
    281 	}
    282 	for k := range m.imgs {
    283 		delete(m.imgs, k)
    284 	}
    285 }
    286 
    287 // mipmapLevel returns an appropriate mipmap level for the given distance.
    288 func mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1 float32, filter driver.Filter) int {
    289 	const maxLevel = 6
    290 
    291 	if filter == driver.FilterScreen {
    292 		return 0
    293 	}
    294 
    295 	d := (dx1-dx0)*(dx1-dx0) + (dy1-dy0)*(dy1-dy0)
    296 	s := (sx1-sx0)*(sx1-sx0) + (sy1-sy0)*(sy1-sy0)
    297 	if s == 0 {
    298 		return 0
    299 	}
    300 	scale := d / s
    301 
    302 	// Scale can be infinite when the specified scale is extremely big (#1398).
    303 	if math.IsInf(float64(scale), 0) {
    304 		if filter == driver.FilterNearest {
    305 			return -maxLevel
    306 		}
    307 		return 0
    308 	}
    309 
    310 	// Scale can be zero when the specified scale is extremely small (#1398).
    311 	if scale == 0 {
    312 		return 0
    313 	}
    314 
    315 	// Use 'negative' mipmap to render edges correctly (#611, #907).
    316 	// It looks like 128 is the enlargement factor that causes edge missings to pass the test TestImageStretch,
    317 	// but we use 32 here for environments where the float precision is low (#1044, #1270).
    318 	var tooBigScale float32 = 32
    319 
    320 	if scale >= tooBigScale*tooBigScale {
    321 		// If the filter is not nearest, the target needs to be rendered with graduation. Don't use mipmaps.
    322 		if filter != driver.FilterNearest {
    323 			return 0
    324 		}
    325 
    326 		const mipmapMaxSize = 1024
    327 		w, h := sx1-sx0, sy1-sy0
    328 		if w >= mipmapMaxSize || h >= mipmapMaxSize {
    329 			return 0
    330 		}
    331 
    332 		level := 0
    333 		for scale >= tooBigScale*tooBigScale {
    334 			level--
    335 			scale /= 4
    336 			w *= 2
    337 			h *= 2
    338 			if w >= mipmapMaxSize || h >= mipmapMaxSize {
    339 				break
    340 			}
    341 		}
    342 
    343 		// If tooBigScale is 32, level -6 means that the maximum scale is 32 * 2^6 = 2048. This should be
    344 		// enough.
    345 		if level < -maxLevel {
    346 			level = -maxLevel
    347 		}
    348 		return level
    349 	}
    350 
    351 	if filter != driver.FilterLinear {
    352 		return 0
    353 	}
    354 
    355 	level := 0
    356 	for scale < 0.25 {
    357 		level++
    358 		scale *= 4
    359 	}
    360 
    361 	if level > 0 {
    362 		// If the image can be scaled into 0 size, adjust the level. (#839)
    363 		w, h := int(sx1-sx0), int(sy1-sy0)
    364 		for level >= 0 {
    365 			s := 1 << uint(level)
    366 			if (w > 0 && w/s == 0) || (h > 0 && h/s == 0) {
    367 				level--
    368 				continue
    369 			}
    370 			break
    371 		}
    372 
    373 		if level < 0 {
    374 			// As the render source is too small, nothing is rendered.
    375 			return 0
    376 		}
    377 	}
    378 
    379 	if level > maxLevel {
    380 		level = maxLevel
    381 	}
    382 
    383 	return level
    384 }
    385 
    386 func pow2(power int) float32 {
    387 	if power >= 0 {
    388 		x := 1
    389 		return float32(x << uint(power))
    390 	}
    391 
    392 	x := float32(1)
    393 	for i := 0; i < -power; i++ {
    394 		x /= 2
    395 	}
    396 	return x
    397 }
    398 
    399 type Shader struct {
    400 	shader *buffered.Shader
    401 }
    402 
    403 func NewShader(program *shaderir.Program) *Shader {
    404 	return &Shader{
    405 		shader: buffered.NewShader(program),
    406 	}
    407 }
    408 
    409 func (s *Shader) MarkDisposed() {
    410 	s.shader.MarkDisposed()
    411 	s.shader = nil
    412 }