twitchapon-anim

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

image.go (7504B)


      1 // Copyright 2019 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 buffered
     16 
     17 import (
     18 	"fmt"
     19 	"image"
     20 	"image/color"
     21 
     22 	"github.com/hajimehoshi/ebiten/v2/internal/affine"
     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 	"github.com/hajimehoshi/ebiten/v2/internal/shareable"
     27 )
     28 
     29 type Image struct {
     30 	img    *shareable.Image
     31 	width  int
     32 	height int
     33 
     34 	hasFill   bool
     35 	fillColor color.RGBA
     36 
     37 	pixels               []byte
     38 	needsToResolvePixels bool
     39 }
     40 
     41 func BeginFrame() error {
     42 	if err := shareable.BeginFrame(); err != nil {
     43 		return err
     44 	}
     45 	return flushDelayedCommands()
     46 }
     47 
     48 func EndFrame() error {
     49 	return shareable.EndFrame()
     50 }
     51 
     52 func NewImage(width, height int) *Image {
     53 	i := &Image{}
     54 	i.initialize(width, height)
     55 	return i
     56 }
     57 
     58 func (i *Image) initialize(width, height int) {
     59 	if maybeCanAddDelayedCommand() {
     60 		if tryAddDelayedCommand(func() error {
     61 			i.initialize(width, height)
     62 			return nil
     63 		}) {
     64 			return
     65 		}
     66 	}
     67 	i.img = shareable.NewImage(width, height)
     68 	i.width = width
     69 	i.height = height
     70 }
     71 
     72 func (i *Image) SetVolatile(volatile bool) {
     73 	if maybeCanAddDelayedCommand() {
     74 		if tryAddDelayedCommand(func() error {
     75 			i.SetVolatile(volatile)
     76 			return nil
     77 		}) {
     78 			return
     79 		}
     80 	}
     81 	i.img.SetVolatile(volatile)
     82 }
     83 
     84 func NewScreenFramebufferImage(width, height int) *Image {
     85 	i := &Image{}
     86 	i.initializeAsScreenFramebuffer(width, height)
     87 	return i
     88 }
     89 
     90 func (i *Image) initializeAsScreenFramebuffer(width, height int) {
     91 	if maybeCanAddDelayedCommand() {
     92 		if tryAddDelayedCommand(func() error {
     93 			i.initializeAsScreenFramebuffer(width, height)
     94 			return nil
     95 		}) {
     96 			return
     97 		}
     98 	}
     99 
    100 	i.img = shareable.NewScreenFramebufferImage(width, height)
    101 	i.width = width
    102 	i.height = height
    103 }
    104 
    105 func (i *Image) invalidatePendingPixels() {
    106 	i.pixels = nil
    107 	i.needsToResolvePixels = false
    108 	i.hasFill = false
    109 }
    110 
    111 func (i *Image) resolvePendingPixels(keepPendingPixels bool) {
    112 	if i.needsToResolvePixels && i.hasFill {
    113 		panic("buffered: needsToResolvePixels and hasFill must not be true at the same time")
    114 	}
    115 	if i.needsToResolvePixels {
    116 		i.img.ReplacePixels(i.pixels)
    117 		if !keepPendingPixels {
    118 			i.pixels = nil
    119 		}
    120 		i.needsToResolvePixels = false
    121 	}
    122 	i.resolvePendingFill()
    123 }
    124 
    125 func (i *Image) resolvePendingFill() {
    126 	if !i.hasFill {
    127 		return
    128 	}
    129 	i.img.Fill(i.fillColor)
    130 	i.hasFill = false
    131 }
    132 
    133 func (i *Image) MarkDisposed() {
    134 	if maybeCanAddDelayedCommand() {
    135 		if tryAddDelayedCommand(func() error {
    136 			i.MarkDisposed()
    137 			return nil
    138 		}) {
    139 			return
    140 		}
    141 	}
    142 	i.invalidatePendingPixels()
    143 	i.img.MarkDisposed()
    144 }
    145 
    146 func (img *Image) Pixels(x, y, width, height int) (pix []byte, err error) {
    147 	checkDelayedCommandsFlushed("Pixels")
    148 
    149 	if !image.Rect(x, y, x+width, y+height).In(image.Rect(0, 0, img.width, img.height)) {
    150 		return nil, fmt.Errorf("buffered: out of range")
    151 	}
    152 
    153 	pix = make([]byte, 4*width*height)
    154 
    155 	// If there are pixels or pending fillling that needs to be resolved, use this rather than resolving.
    156 	// Resolving them needs to access GPU and is expensive (#1137).
    157 	if img.hasFill {
    158 		for i := 0; i < len(pix)/4; i++ {
    159 			pix[4*i] = img.fillColor.R
    160 			pix[4*i+1] = img.fillColor.G
    161 			pix[4*i+2] = img.fillColor.B
    162 			pix[4*i+3] = img.fillColor.A
    163 		}
    164 		return pix, nil
    165 	}
    166 
    167 	if img.pixels == nil {
    168 		pix, err := img.img.Pixels(0, 0, img.width, img.height)
    169 		if err != nil {
    170 			return nil, err
    171 		}
    172 		img.pixels = pix
    173 	}
    174 
    175 	for j := 0; j < height; j++ {
    176 		copy(pix[4*j*width:4*(j+1)*width], img.pixels[4*((j+y)*img.width+x):])
    177 	}
    178 	return pix, nil
    179 }
    180 
    181 func (i *Image) Dump(name string, blackbg bool) error {
    182 	checkDelayedCommandsFlushed("Dump")
    183 	return i.img.Dump(name, blackbg)
    184 }
    185 
    186 func (i *Image) Fill(clr color.RGBA) {
    187 	if maybeCanAddDelayedCommand() {
    188 		if tryAddDelayedCommand(func() error {
    189 			i.Fill(clr)
    190 			return nil
    191 		}) {
    192 			return
    193 		}
    194 	}
    195 
    196 	// Defer filling the image so that successive fillings will be merged into one (#1134).
    197 	i.invalidatePendingPixels()
    198 	i.fillColor = clr
    199 	i.hasFill = true
    200 }
    201 
    202 func (i *Image) ReplacePixels(pix []byte, x, y, width, height int) error {
    203 	if l := 4 * width * height; len(pix) != l {
    204 		panic(fmt.Sprintf("buffered: len(pix) was %d but must be %d", len(pix), l))
    205 	}
    206 
    207 	if maybeCanAddDelayedCommand() {
    208 		copied := make([]byte, len(pix))
    209 		copy(copied, pix)
    210 		if tryAddDelayedCommand(func() error {
    211 			i.ReplacePixels(copied, x, y, width, height)
    212 			return nil
    213 		}) {
    214 			return nil
    215 		}
    216 	}
    217 
    218 	if x == 0 && y == 0 && width == i.width && height == i.height {
    219 		i.invalidatePendingPixels()
    220 
    221 		// Call ReplacePixels immediately. If a lot of new images are created but they are used at different
    222 		// timings, pixels are sent to GPU at different timings, which is very inefficient.
    223 		i.img.ReplacePixels(pix)
    224 		return nil
    225 	}
    226 
    227 	i.resolvePendingFill()
    228 
    229 	// TODO: Can we use (*restorable.Image).ReplacePixels?
    230 	if i.pixels == nil {
    231 		pix, err := i.img.Pixels(0, 0, i.width, i.height)
    232 		if err != nil {
    233 			return err
    234 		}
    235 		i.pixels = pix
    236 	}
    237 	i.replacePendingPixels(pix, x, y, width, height)
    238 	return nil
    239 }
    240 
    241 func (i *Image) replacePendingPixels(pix []byte, x, y, width, height int) {
    242 	for j := 0; j < height; j++ {
    243 		copy(i.pixels[4*((j+y)*i.width+x):], pix[4*j*width:4*(j+1)*width])
    244 	}
    245 	i.needsToResolvePixels = true
    246 }
    247 
    248 // DrawTriangles draws the src image with the given vertices.
    249 //
    250 // Copying vertices and indices is the caller's responsibility.
    251 func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, 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{}) {
    252 	for _, src := range srcs {
    253 		if i == src {
    254 			panic("buffered: Image.DrawTriangles: source images must be different from the receiver")
    255 		}
    256 	}
    257 
    258 	if maybeCanAddDelayedCommand() {
    259 		if tryAddDelayedCommand(func() error {
    260 			// Arguments are not copied. Copying is the caller's responsibility.
    261 			i.DrawTriangles(srcs, vertices, indices, colorm, mode, filter, address, sourceRegion, subimageOffsets, shader, uniforms)
    262 			return nil
    263 		}) {
    264 			return
    265 		}
    266 	}
    267 
    268 	var s *shareable.Shader
    269 	var imgs [graphics.ShaderImageNum]*shareable.Image
    270 	if shader == nil {
    271 		// Fast path for rendering without a shader (#1355).
    272 		img := srcs[0]
    273 		img.resolvePendingPixels(true)
    274 		imgs[0] = img.img
    275 	} else {
    276 		for i, img := range srcs {
    277 			if img == nil {
    278 				continue
    279 			}
    280 			img.resolvePendingPixels(true)
    281 			imgs[i] = img.img
    282 		}
    283 		s = shader.shader
    284 	}
    285 	i.resolvePendingPixels(false)
    286 
    287 	i.img.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, subimageOffsets, s, uniforms)
    288 	i.invalidatePendingPixels()
    289 }
    290 
    291 type Shader struct {
    292 	shader *shareable.Shader
    293 }
    294 
    295 func NewShader(program *shaderir.Program) *Shader {
    296 	return &Shader{
    297 		shader: shareable.NewShader(program),
    298 	}
    299 }
    300 
    301 func (s *Shader) MarkDisposed() {
    302 	s.shader.MarkDisposed()
    303 	s.shader = nil
    304 }