mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +01:00
Reland(2): graphics: Appropriate rendering of edges on linear filter
This commit is contained in:
parent
362d1c417f
commit
723d153800
15
graphics.go
15
graphics.go
@ -15,6 +15,7 @@
|
||||
package ebiten
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/internal/graphics"
|
||||
"github.com/hajimehoshi/ebiten/internal/opengl"
|
||||
)
|
||||
|
||||
@ -23,22 +24,12 @@ type Filter int
|
||||
|
||||
const (
|
||||
// FilterNearest represents nearest (crisp-edged) filter
|
||||
FilterNearest Filter = iota
|
||||
FilterNearest Filter = Filter(graphics.FilterNearest)
|
||||
|
||||
// FilterLinear represents linear filter
|
||||
FilterLinear
|
||||
FilterLinear Filter = Filter(graphics.FilterLinear)
|
||||
)
|
||||
|
||||
func glFilter(filter Filter) opengl.Filter {
|
||||
switch filter {
|
||||
case FilterNearest:
|
||||
return opengl.Nearest
|
||||
case FilterLinear:
|
||||
return opengl.Linear
|
||||
}
|
||||
panic("not reach")
|
||||
}
|
||||
|
||||
// CompositeMode represents Porter-Duff composition mode.
|
||||
type CompositeMode int
|
||||
|
||||
|
7
image.go
7
image.go
@ -20,6 +20,7 @@ import (
|
||||
"image/color"
|
||||
"runtime"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/internal/graphics"
|
||||
"github.com/hajimehoshi/ebiten/internal/math"
|
||||
"github.com/hajimehoshi/ebiten/internal/opengl"
|
||||
"github.com/hajimehoshi/ebiten/internal/restorable"
|
||||
@ -244,7 +245,7 @@ type DrawImageOptions struct {
|
||||
// Error returned by NewImage is always nil as of 1.5.0-alpha.
|
||||
func NewImage(width, height int, filter Filter) (*Image, error) {
|
||||
checkSize(width, height)
|
||||
r := restorable.NewImage(width, height, glFilter(filter), false)
|
||||
r := restorable.NewImage(width, height, graphics.Filter(filter), false)
|
||||
r.Fill(0, 0, 0, 0)
|
||||
i := &Image{r}
|
||||
runtime.SetFinalizer(i, (*Image).Dispose)
|
||||
@ -268,7 +269,7 @@ func NewImage(width, height int, filter Filter) (*Image, error) {
|
||||
// Error returned by newVolatileImage is always nil as of 1.5.0-alpha.
|
||||
func newVolatileImage(width, height int, filter Filter) *Image {
|
||||
checkSize(width, height)
|
||||
r := restorable.NewImage(width, height, glFilter(filter), true)
|
||||
r := restorable.NewImage(width, height, graphics.Filter(filter), true)
|
||||
r.Fill(0, 0, 0, 0)
|
||||
i := &Image{r}
|
||||
runtime.SetFinalizer(i, (*Image).Dispose)
|
||||
@ -283,7 +284,7 @@ func newVolatileImage(width, height int, filter Filter) *Image {
|
||||
func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
|
||||
size := source.Bounds().Size()
|
||||
checkSize(size.X, size.Y)
|
||||
r := restorable.NewImageFromImage(source, glFilter(filter))
|
||||
r := restorable.NewImageFromImage(source, graphics.Filter(filter))
|
||||
i := &Image{r}
|
||||
runtime.SetFinalizer(i, (*Image).Dispose)
|
||||
return i, nil
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/hajimehoshi/ebiten"
|
||||
"github.com/hajimehoshi/ebiten/ebitenutil"
|
||||
emath "github.com/hajimehoshi/ebiten/internal/math"
|
||||
)
|
||||
|
||||
@ -595,3 +596,28 @@ func BenchmarkDrawImage(b *testing.B) {
|
||||
img0.DrawImage(img1, op)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageLinear(t *testing.T) {
|
||||
src, _ := NewImage(32, 32, FilterLinear)
|
||||
dst, _ := NewImage(64, 64, FilterNearest)
|
||||
src.Fill(color.RGBA{0, 0xff, 0, 0xff})
|
||||
ebitenutil.DrawRect(src, 8, 8, 16, 16, color.RGBA{0xff, 0, 0, 0xff})
|
||||
|
||||
op := &DrawImageOptions{}
|
||||
op.GeoM.Translate(8, 8)
|
||||
op.GeoM.Scale(2, 2)
|
||||
r := image.Rect(8, 8, 24, 24)
|
||||
op.SourceRect = &r
|
||||
dst.DrawImage(src, op)
|
||||
|
||||
for j := 0; j < 64; j++ {
|
||||
for i := 0; i < 64; i++ {
|
||||
c := color.RGBAModel.Convert(dst.At(i, j)).(color.RGBA)
|
||||
got := c.G
|
||||
want := uint8(0)
|
||||
if got != want {
|
||||
t.Errorf("dst At(%d, %d).G: got %#v, want: %#v", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,12 +242,18 @@ func (c *drawImageCommand) Exec(indexOffsetInBytes int) error {
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
_, h := c.dst.Size()
|
||||
proj := f.projectionMatrix(h)
|
||||
theOpenGLState.useProgram(proj, c.src.texture.native, c.color)
|
||||
sw, sh := c.src.Size()
|
||||
sw = emath.NextPowerOf2Int(sw)
|
||||
sh = emath.NextPowerOf2Int(sh)
|
||||
_, dh := c.dst.Size()
|
||||
proj := f.projectionMatrix(dh)
|
||||
theOpenGLState.useProgram(proj, c.src.texture.native, sw, sh, c.color, c.src.texture.filter)
|
||||
// TODO: We should call glBindBuffer here?
|
||||
// The buffer is already bound at begin() but it is counterintuitive.
|
||||
opengl.GetContext().DrawElements(opengl.Triangles, 6*n, indexOffsetInBytes)
|
||||
|
||||
// This is necessary at least on MacBook Pro (a smilar problem at #419)
|
||||
opengl.GetContext().Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -338,7 +344,7 @@ func (c *disposeCommand) Exec(indexOffsetInBytes int) error {
|
||||
type newImageFromImageCommand struct {
|
||||
result *Image
|
||||
img *image.RGBA
|
||||
filter opengl.Filter
|
||||
filter Filter
|
||||
}
|
||||
|
||||
// Exec executes the newImageFromImageCommand.
|
||||
@ -354,12 +360,13 @@ func (c *newImageFromImageCommand) Exec(indexOffsetInBytes int) error {
|
||||
if c.img.Bounds() != image.Rect(0, 0, emath.NextPowerOf2Int(w), emath.NextPowerOf2Int(h)) {
|
||||
panic(fmt.Sprintf("graphics: invalid image bounds: %v", c.img.Bounds()))
|
||||
}
|
||||
native, err := opengl.GetContext().NewTexture(w, h, c.img.Pix, c.filter)
|
||||
native, err := opengl.GetContext().NewTexture(w, h, c.img.Pix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.result.texture = &texture{
|
||||
native: native,
|
||||
filter: c.filter,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -369,7 +376,7 @@ type newImageCommand struct {
|
||||
result *Image
|
||||
width int
|
||||
height int
|
||||
filter opengl.Filter
|
||||
filter Filter
|
||||
}
|
||||
|
||||
// Exec executes a newImageCommand.
|
||||
@ -382,12 +389,13 @@ func (c *newImageCommand) Exec(indexOffsetInBytes int) error {
|
||||
if h < 1 {
|
||||
return errors.New("graphics: height must be equal or more than 1.")
|
||||
}
|
||||
native, err := opengl.GetContext().NewTexture(w, h, nil, c.filter)
|
||||
native, err := opengl.GetContext().NewTexture(w, h, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.result.texture = &texture{
|
||||
native: native,
|
||||
filter: c.filter,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ type Image struct {
|
||||
// MaxImageSize is the maximum of width/height of an image.
|
||||
const MaxImageSize = defaultViewportSize
|
||||
|
||||
func NewImage(width, height int, filter opengl.Filter) *Image {
|
||||
func NewImage(width, height int, filter Filter) *Image {
|
||||
i := &Image{
|
||||
width: width,
|
||||
height: height,
|
||||
@ -48,7 +48,7 @@ func NewImage(width, height int, filter opengl.Filter) *Image {
|
||||
return i
|
||||
}
|
||||
|
||||
func NewImageFromImage(img *image.RGBA, width, height int, filter opengl.Filter) *Image {
|
||||
func NewImageFromImage(img *image.RGBA, width, height int, filter Filter) *Image {
|
||||
i := &Image{
|
||||
width: width,
|
||||
height: height,
|
||||
|
@ -121,6 +121,9 @@ type openGLState struct {
|
||||
lastProjectionMatrix []float32
|
||||
lastColorMatrix []float32
|
||||
lastColorMatrixTranslation []float32
|
||||
lastFilterType Filter
|
||||
lastSourceWidth int
|
||||
lastSourceHeight int
|
||||
}
|
||||
|
||||
var (
|
||||
@ -150,6 +153,9 @@ func (s *openGLState) reset() error {
|
||||
s.lastProjectionMatrix = nil
|
||||
s.lastColorMatrix = nil
|
||||
s.lastColorMatrixTranslation = nil
|
||||
s.lastFilterType = FilterNone
|
||||
s.lastSourceWidth = 0
|
||||
s.lastSourceHeight = 0
|
||||
|
||||
// When context lost happens, deleting programs or buffers is not necessary.
|
||||
// However, it is not assumed that reset is called only when context lost happens.
|
||||
@ -214,7 +220,7 @@ func areSameFloat32Array(a, b []float32) bool {
|
||||
}
|
||||
|
||||
// useProgram uses the program (programTexture).
|
||||
func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, colorM affine.ColorM) {
|
||||
func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, sourceWidth, sourceHeight int, colorM affine.ColorM, filter Filter) {
|
||||
c := opengl.GetContext()
|
||||
program := s.programTexture
|
||||
|
||||
@ -273,6 +279,18 @@ func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, colorM
|
||||
copy(s.lastColorMatrixTranslation, colorMatrixTranslation)
|
||||
}
|
||||
|
||||
if s.lastFilterType != filter {
|
||||
c.UniformInt(program, "filter_type", int(filter))
|
||||
s.lastFilterType = filter
|
||||
}
|
||||
|
||||
if s.lastSourceWidth != sourceWidth || s.lastSourceHeight != sourceHeight {
|
||||
c.UniformFloats(program, "source_size",
|
||||
[]float32{float32(sourceWidth), float32(sourceHeight)})
|
||||
s.lastSourceWidth = sourceWidth
|
||||
s.lastSourceHeight = sourceHeight
|
||||
}
|
||||
|
||||
// We don't have to call gl.ActiveTexture here: GL_TEXTURE0 is the default active texture
|
||||
// See also: https://www.opengl.org/sdk/docs/man2/xhtml/glActiveTexture.xml
|
||||
c.BindTexture(texture)
|
||||
|
@ -63,17 +63,54 @@ precision mediump float;
|
||||
uniform sampler2D texture;
|
||||
uniform mat4 color_matrix;
|
||||
uniform vec4 color_matrix_translation;
|
||||
uniform int filter_type;
|
||||
uniform vec2 source_size;
|
||||
|
||||
varying vec2 varying_tex_coord;
|
||||
varying vec2 varying_tex_coord_min;
|
||||
varying vec2 varying_tex_coord_max;
|
||||
|
||||
vec2 roundTexel(vec2 p) {
|
||||
vec2 factor = 1.0 / (source_size * 256.0);
|
||||
if (factor.x > 0.0) {
|
||||
p.x -= mod(p.x + factor.x * 0.5, factor.x) - factor.x * 0.5;
|
||||
}
|
||||
if (factor.y > 0.0) {
|
||||
p.y -= mod(p.y + factor.y * 0.5, factor.y) - factor.y * 0.5;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
vec4 getColorAt(vec2 pos) {
|
||||
if (pos.x < varying_tex_coord_min.x ||
|
||||
pos.y < varying_tex_coord_min.y ||
|
||||
varying_tex_coord_max.x <= pos.x ||
|
||||
varying_tex_coord_max.y <= pos.y) {
|
||||
return vec4(0, 0, 0, 0);
|
||||
}
|
||||
return texture2D(texture, pos);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec4 color = vec4(0, 0, 0, 0);
|
||||
if (varying_tex_coord_min.x <= varying_tex_coord.x &&
|
||||
varying_tex_coord_min.y <= varying_tex_coord.y &&
|
||||
varying_tex_coord.x < varying_tex_coord_max.x &&
|
||||
varying_tex_coord.y < varying_tex_coord_max.y) {
|
||||
color = texture2D(texture, varying_tex_coord);
|
||||
|
||||
vec2 pos = roundTexel(varying_tex_coord);
|
||||
if (filter_type == 1) {
|
||||
// Nearest neighbor
|
||||
color = getColorAt(pos);
|
||||
} else if (filter_type == 2) {
|
||||
// Bi-linear
|
||||
vec2 texel_size = 1.0 / source_size;
|
||||
pos -= texel_size * 0.5;
|
||||
vec4 c0 = getColorAt(pos);
|
||||
vec4 c1 = getColorAt(pos + vec2(texel_size.x, 0));
|
||||
vec4 c2 = getColorAt(pos + vec2(0, texel_size.y));
|
||||
vec4 c3 = getColorAt(pos + texel_size);
|
||||
float rateX = fract(pos.x * source_size.x);
|
||||
float rateY = fract(pos.y * source_size.y);
|
||||
color = mix(mix(c0, c1, rateX), mix(c2, c3, rateX), rateY);
|
||||
} else {
|
||||
color = vec4(1, 0, 0, 1);
|
||||
}
|
||||
|
||||
// Un-premultiply alpha
|
||||
|
@ -18,7 +18,16 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/internal/opengl"
|
||||
)
|
||||
|
||||
type Filter int
|
||||
|
||||
const (
|
||||
FilterNone Filter = iota
|
||||
FilterNearest
|
||||
FilterLinear
|
||||
)
|
||||
|
||||
// texture represents OpenGL's texture.
|
||||
type texture struct {
|
||||
native opengl.Texture
|
||||
filter Filter
|
||||
}
|
||||
|
@ -38,8 +38,6 @@ func adjustForClearColor(x float32) float32 {
|
||||
}
|
||||
|
||||
var (
|
||||
Nearest Filter
|
||||
Linear Filter
|
||||
VertexShader ShaderType
|
||||
FragmentShader ShaderType
|
||||
ArrayBuffer BufferType
|
||||
|
@ -26,11 +26,13 @@ import (
|
||||
"github.com/go-gl/gl/v2.1/gl"
|
||||
)
|
||||
|
||||
type Texture uint32
|
||||
type Framebuffer uint32
|
||||
type Shader uint32
|
||||
type Program uint32
|
||||
type Buffer uint32
|
||||
type (
|
||||
Texture uint32
|
||||
Framebuffer uint32
|
||||
Shader uint32
|
||||
Program uint32
|
||||
Buffer uint32
|
||||
)
|
||||
|
||||
func (t Texture) equals(other Texture) bool {
|
||||
return t == other
|
||||
@ -40,8 +42,10 @@ func (f Framebuffer) equals(other Framebuffer) bool {
|
||||
return f == other
|
||||
}
|
||||
|
||||
type uniformLocation int32
|
||||
type attribLocation int32
|
||||
type (
|
||||
uniformLocation int32
|
||||
attribLocation int32
|
||||
)
|
||||
|
||||
type programID uint32
|
||||
|
||||
@ -55,8 +59,6 @@ func (p Program) id() programID {
|
||||
}
|
||||
|
||||
func init() {
|
||||
Nearest = gl.NEAREST
|
||||
Linear = gl.LINEAR
|
||||
VertexShader = gl.VERTEX_SHADER
|
||||
FragmentShader = gl.FRAGMENT_SHADER
|
||||
ArrayBuffer = gl.ARRAY_BUFFER
|
||||
@ -137,7 +139,7 @@ func (c *Context) BlendFunc(mode CompositeMode) {
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Context) NewTexture(width, height int, pixels []uint8, filter Filter) (Texture, error) {
|
||||
func (c *Context) NewTexture(width, height int, pixels []uint8) (Texture, error) {
|
||||
var texture Texture
|
||||
if err := c.runOnContextThread(func() error {
|
||||
var t uint32
|
||||
@ -154,10 +156,8 @@ func (c *Context) NewTexture(width, height int, pixels []uint8, filter Filter) (
|
||||
}
|
||||
c.BindTexture(texture)
|
||||
_ = c.runOnContextThread(func() error {
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, int32(filter))
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, int32(filter))
|
||||
//gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP)
|
||||
//gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
|
||||
|
||||
var p interface{}
|
||||
if pixels != nil {
|
||||
@ -408,12 +408,14 @@ func (c *Context) UniformFloats(p Program, location string, v []float32) {
|
||||
_ = c.runOnContextThread(func() error {
|
||||
l := int32(c.locationCache.GetUniformLocation(c, p, location))
|
||||
switch len(v) {
|
||||
case 2:
|
||||
gl.Uniform2fv(l, 1, (*float32)(gl.Ptr(v)))
|
||||
case 4:
|
||||
gl.Uniform4fv(l, 1, (*float32)(gl.Ptr(v)))
|
||||
case 16:
|
||||
gl.UniformMatrix4fv(l, 1, false, (*float32)(gl.Ptr(v)))
|
||||
default:
|
||||
panic("not reach")
|
||||
panic("not reached")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
@ -77,8 +77,6 @@ func (p Program) id() programID {
|
||||
func init() {
|
||||
// Accessing the prototype is rquired on Safari.
|
||||
c := js.Global.Get("WebGLRenderingContext").Get("prototype")
|
||||
Nearest = Filter(c.Get("NEAREST").Int())
|
||||
Linear = Filter(c.Get("LINEAR").Int())
|
||||
VertexShader = ShaderType(c.Get("VERTEX_SHADER").Int())
|
||||
FragmentShader = ShaderType(c.Get("FRAGMENT_SHADER").Int())
|
||||
ArrayBuffer = BufferType(c.Get("ARRAY_BUFFER").Int())
|
||||
@ -159,7 +157,7 @@ func (c *Context) BlendFunc(mode CompositeMode) {
|
||||
gl.BlendFunc(int(s), int(d))
|
||||
}
|
||||
|
||||
func (c *Context) NewTexture(width, height int, pixels []uint8, filter Filter) (Texture, error) {
|
||||
func (c *Context) NewTexture(width, height int, pixels []uint8) (Texture, error) {
|
||||
gl := c.gl
|
||||
t := gl.CreateTexture()
|
||||
if t == nil {
|
||||
@ -168,8 +166,8 @@ func (c *Context) NewTexture(width, height int, pixels []uint8, filter Filter) (
|
||||
gl.PixelStorei(gl.UNPACK_ALIGNMENT, 4)
|
||||
c.BindTexture(Texture{t})
|
||||
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, int(filter))
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, int(filter))
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
|
||||
|
||||
// TODO: Can we use glTexSubImage2D with linear filtering?
|
||||
|
||||
@ -348,12 +346,14 @@ func (c *Context) UniformFloats(p Program, location string, v []float32) {
|
||||
gl := c.gl
|
||||
l := c.locationCache.GetUniformLocation(c, p, location)
|
||||
switch len(v) {
|
||||
case 2:
|
||||
gl.Call("uniform2fv", l.Object, v)
|
||||
case 4:
|
||||
gl.Call("uniform4fv", l.Object, v)
|
||||
case 16:
|
||||
gl.UniformMatrix4fv(l.Object, false, v)
|
||||
default:
|
||||
panic("not reach")
|
||||
panic("not reached")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,11 +25,13 @@ import (
|
||||
mgl "golang.org/x/mobile/gl"
|
||||
)
|
||||
|
||||
type Texture mgl.Texture
|
||||
type Framebuffer mgl.Framebuffer
|
||||
type Shader mgl.Shader
|
||||
type Program mgl.Program
|
||||
type Buffer mgl.Buffer
|
||||
type (
|
||||
Texture mgl.Texture
|
||||
Framebuffer mgl.Framebuffer
|
||||
Shader mgl.Shader
|
||||
Program mgl.Program
|
||||
Buffer mgl.Buffer
|
||||
)
|
||||
|
||||
func (t Texture) equals(other Texture) bool {
|
||||
return t == other
|
||||
@ -39,8 +41,10 @@ func (f Framebuffer) equals(other Framebuffer) bool {
|
||||
return f == other
|
||||
}
|
||||
|
||||
type uniformLocation mgl.Uniform
|
||||
type attribLocation mgl.Attrib
|
||||
type (
|
||||
uniformLocation mgl.Uniform
|
||||
attribLocation mgl.Attrib
|
||||
)
|
||||
|
||||
type programID uint32
|
||||
|
||||
@ -54,8 +58,6 @@ func (p Program) id() programID {
|
||||
}
|
||||
|
||||
func init() {
|
||||
Nearest = mgl.NEAREST
|
||||
Linear = mgl.LINEAR
|
||||
VertexShader = mgl.VERTEX_SHADER
|
||||
FragmentShader = mgl.FRAGMENT_SHADER
|
||||
ArrayBuffer = mgl.ARRAY_BUFFER
|
||||
@ -127,7 +129,7 @@ func (c *Context) BlendFunc(mode CompositeMode) {
|
||||
gl.BlendFunc(mgl.Enum(s), mgl.Enum(d))
|
||||
}
|
||||
|
||||
func (c *Context) NewTexture(width, height int, pixels []uint8, filter Filter) (Texture, error) {
|
||||
func (c *Context) NewTexture(width, height int, pixels []uint8) (Texture, error) {
|
||||
gl := c.gl
|
||||
t := gl.CreateTexture()
|
||||
if t.Value <= 0 {
|
||||
@ -136,8 +138,8 @@ func (c *Context) NewTexture(width, height int, pixels []uint8, filter Filter) (
|
||||
gl.PixelStorei(mgl.UNPACK_ALIGNMENT, 4)
|
||||
c.BindTexture(Texture(t))
|
||||
|
||||
gl.TexParameteri(mgl.TEXTURE_2D, mgl.TEXTURE_MAG_FILTER, int(filter))
|
||||
gl.TexParameteri(mgl.TEXTURE_2D, mgl.TEXTURE_MIN_FILTER, int(filter))
|
||||
gl.TexParameteri(mgl.TEXTURE_2D, mgl.TEXTURE_MAG_FILTER, mgl.NEAREST)
|
||||
gl.TexParameteri(mgl.TEXTURE_2D, mgl.TEXTURE_MIN_FILTER, mgl.NEAREST)
|
||||
|
||||
var p []uint8
|
||||
if pixels != nil {
|
||||
@ -317,12 +319,14 @@ func (c *Context) UniformFloats(p Program, location string, v []float32) {
|
||||
gl := c.gl
|
||||
l := mgl.Uniform(c.locationCache.GetUniformLocation(c, p, location))
|
||||
switch len(v) {
|
||||
case 2:
|
||||
gl.Uniform2fv(l, v)
|
||||
case 4:
|
||||
gl.Uniform4fv(l, v)
|
||||
case 16:
|
||||
gl.UniformMatrix4fv(l, v)
|
||||
default:
|
||||
panic("not reach")
|
||||
panic("not reached")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
package opengl
|
||||
|
||||
type (
|
||||
Filter int
|
||||
ShaderType int
|
||||
BufferType int
|
||||
BufferUsage int
|
||||
|
@ -60,7 +60,7 @@ func (d *drawImageHistoryItem) canMerge(image *Image, colorm *affine.ColorM, mod
|
||||
// Image represents an image that can be restored when GL context is lost.
|
||||
type Image struct {
|
||||
image *graphics.Image
|
||||
filter opengl.Filter
|
||||
filter graphics.Filter
|
||||
|
||||
// baseImage and baseColor are exclusive.
|
||||
basePixels []uint8
|
||||
@ -84,7 +84,7 @@ type Image struct {
|
||||
}
|
||||
|
||||
// NewImage creates an empty image with the given size and filter.
|
||||
func NewImage(width, height int, filter opengl.Filter, volatile bool) *Image {
|
||||
func NewImage(width, height int, filter graphics.Filter, volatile bool) *Image {
|
||||
i := &Image{
|
||||
image: graphics.NewImage(width, height, filter),
|
||||
filter: filter,
|
||||
@ -96,7 +96,7 @@ func NewImage(width, height int, filter opengl.Filter, volatile bool) *Image {
|
||||
}
|
||||
|
||||
// NewImageFromImage creates an image with source image.
|
||||
func NewImageFromImage(source image.Image, filter opengl.Filter) *Image {
|
||||
func NewImageFromImage(source image.Image, filter graphics.Filter) *Image {
|
||||
size := source.Bounds().Size()
|
||||
width, height := size.X, size.Y
|
||||
rgbaImg := CopyImage(source)
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
"github.com/hajimehoshi/ebiten/internal/affine"
|
||||
"github.com/hajimehoshi/ebiten/internal/graphics"
|
||||
"github.com/hajimehoshi/ebiten/internal/opengl"
|
||||
. "github.com/hajimehoshi/ebiten/internal/restorable"
|
||||
)
|
||||
@ -47,7 +48,7 @@ func uint8SliceToColor(b []uint8, index int) color.RGBA {
|
||||
}
|
||||
|
||||
func TestRestore(t *testing.T) {
|
||||
img0 := NewImage(1, 1, opengl.Nearest, false)
|
||||
img0 := NewImage(1, 1, graphics.FilterNearest, false)
|
||||
// Clear images explicitly.
|
||||
// In this 'restorable' layer, reused texture might not be cleared.
|
||||
img0.Fill(0, 0, 0, 0)
|
||||
@ -89,7 +90,7 @@ func TestRestoreChain(t *testing.T) {
|
||||
const num = 10
|
||||
imgs := []*Image{}
|
||||
for i := 0; i < num; i++ {
|
||||
img := NewImage(1, 1, opengl.Nearest, false)
|
||||
img := NewImage(1, 1, graphics.FilterNearest, false)
|
||||
img.Fill(0, 0, 0, 0)
|
||||
imgs = append(imgs, img)
|
||||
}
|
||||
@ -119,13 +120,13 @@ func TestRestoreChain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRestoreOverrideSource(t *testing.T) {
|
||||
img0 := NewImage(1, 1, opengl.Nearest, false)
|
||||
img0 := NewImage(1, 1, graphics.FilterNearest, false)
|
||||
img0.Fill(0, 0, 0, 0)
|
||||
img1 := NewImage(1, 1, opengl.Nearest, false)
|
||||
img1 := NewImage(1, 1, graphics.FilterNearest, false)
|
||||
img1.Fill(0, 0, 0, 0)
|
||||
img2 := NewImage(1, 1, opengl.Nearest, false)
|
||||
img2 := NewImage(1, 1, graphics.FilterNearest, false)
|
||||
img2.Fill(0, 0, 0, 0)
|
||||
img3 := NewImage(1, 1, opengl.Nearest, false)
|
||||
img3 := NewImage(1, 1, graphics.FilterNearest, false)
|
||||
img3.Fill(0, 0, 0, 0)
|
||||
defer func() {
|
||||
img3.Dispose()
|
||||
@ -194,18 +195,18 @@ func TestRestoreComplexGraph(t *testing.T) {
|
||||
base.Pix[1] = 0xff
|
||||
base.Pix[2] = 0xff
|
||||
base.Pix[3] = 0xff
|
||||
img0 := NewImageFromImage(base, opengl.Nearest)
|
||||
img1 := NewImageFromImage(base, opengl.Nearest)
|
||||
img2 := NewImageFromImage(base, opengl.Nearest)
|
||||
img3 := NewImage(4, 1, opengl.Nearest, false)
|
||||
img0 := NewImageFromImage(base, graphics.FilterNearest)
|
||||
img1 := NewImageFromImage(base, graphics.FilterNearest)
|
||||
img2 := NewImageFromImage(base, graphics.FilterNearest)
|
||||
img3 := NewImage(4, 1, graphics.FilterNearest, false)
|
||||
img3.Fill(0, 0, 0, 0)
|
||||
img4 := NewImage(4, 1, opengl.Nearest, false)
|
||||
img4 := NewImage(4, 1, graphics.FilterNearest, false)
|
||||
img4.Fill(0, 0, 0, 0)
|
||||
img5 := NewImage(4, 1, opengl.Nearest, false)
|
||||
img5 := NewImage(4, 1, graphics.FilterNearest, false)
|
||||
img5.Fill(0, 0, 0, 0)
|
||||
img6 := NewImage(4, 1, opengl.Nearest, false)
|
||||
img6 := NewImage(4, 1, graphics.FilterNearest, false)
|
||||
img6.Fill(0, 0, 0, 0)
|
||||
img7 := NewImage(4, 1, opengl.Nearest, false)
|
||||
img7 := NewImage(4, 1, graphics.FilterNearest, false)
|
||||
img7.Fill(0, 0, 0, 0)
|
||||
defer func() {
|
||||
img7.Dispose()
|
||||
@ -298,8 +299,8 @@ func TestRestoreRecursive(t *testing.T) {
|
||||
base.Pix[1] = 0xff
|
||||
base.Pix[2] = 0xff
|
||||
base.Pix[3] = 0xff
|
||||
img0 := NewImageFromImage(base, opengl.Nearest)
|
||||
img1 := NewImage(4, 1, opengl.Nearest, false)
|
||||
img0 := NewImageFromImage(base, graphics.FilterNearest)
|
||||
img1 := NewImage(4, 1, graphics.FilterNearest, false)
|
||||
img1.Fill(0, 0, 0, 0)
|
||||
defer func() {
|
||||
img1.Dispose()
|
||||
|
Loading…
Reference in New Issue
Block a user