mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 18:58:54 +01:00
Add SetScreenSize
This commit is contained in:
parent
9b1aee1a21
commit
249add3979
85
example/windowsize/main.go
Normal file
85
example/windowsize/main.go
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright 2015 Hajime Hoshi
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
"github.com/hajimehoshi/ebiten/ebitenutil"
|
||||
_ "image/jpeg"
|
||||
"log"
|
||||
)
|
||||
|
||||
const (
|
||||
initScreenWidth = 320
|
||||
initScreenHeight = 240
|
||||
)
|
||||
|
||||
var (
|
||||
gophersImage *ebiten.Image
|
||||
screenWidth = initScreenWidth
|
||||
screenHeight = initScreenHeight
|
||||
keyStates = map[ebiten.Key]int{
|
||||
ebiten.KeyUp: 0,
|
||||
ebiten.KeyDown: 0,
|
||||
ebiten.KeyLeft: 0,
|
||||
ebiten.KeyRight: 0,
|
||||
}
|
||||
)
|
||||
|
||||
func update(screen *ebiten.Image) error {
|
||||
for key, _ := range keyStates {
|
||||
if !ebiten.IsKeyPressed(key) {
|
||||
keyStates[key] = 0
|
||||
continue
|
||||
}
|
||||
keyStates[key]++
|
||||
}
|
||||
|
||||
if keyStates[ebiten.KeyUp] == 1 {
|
||||
screenHeight += 16
|
||||
}
|
||||
if keyStates[ebiten.KeyDown] == 1 {
|
||||
screenHeight -= 16
|
||||
}
|
||||
if keyStates[ebiten.KeyLeft] == 1 {
|
||||
screenWidth -= 16
|
||||
}
|
||||
if keyStates[ebiten.KeyRight] == 1 {
|
||||
screenWidth += 16
|
||||
}
|
||||
ebiten.SetScreenSize(screenWidth, screenHeight)
|
||||
|
||||
w, h := gophersImage.Size()
|
||||
w2, h2 := screen.Size()
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Translate(float64(-w+w2)/2, float64(-h+h2)/2)
|
||||
if err := screen.DrawImage(gophersImage, op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ebitenutil.DebugPrint(screen, "Press arrow keys")
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
gophersImage, _, err = ebitenutil.NewImageFromFile("images/gophers.jpg", ebiten.FilterNearest)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := ebiten.Run(update, initScreenWidth, initScreenHeight, 2, "Window Size (Ebiten Demo)"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
@ -26,45 +26,20 @@ func useGLContext(f func(*opengl.Context)) {
|
||||
})
|
||||
}
|
||||
|
||||
func newGraphicsContext(c *opengl.Context, screenWidth, screenHeight, screenScale int) (*graphicsContext, error) {
|
||||
f, err := graphics.NewZeroFramebuffer(c, screenWidth*screenScale, screenHeight*screenScale)
|
||||
if err != nil {
|
||||
func newGraphicsContext(screenWidth, screenHeight, screenScale int) (*graphicsContext, error) {
|
||||
c := &graphicsContext{}
|
||||
if err := c.setSize(screenWidth, screenHeight, screenScale); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
texture, err := graphics.NewTexture(c, screenWidth, screenHeight, c.Nearest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
screenF, err := graphics.NewFramebufferFromTexture(c, texture)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
screen := &Image{framebuffer: screenF, texture: texture}
|
||||
return &graphicsContext{
|
||||
glContext: c,
|
||||
defaultR: &Image{framebuffer: f, texture: nil},
|
||||
screen: screen,
|
||||
screenScale: screenScale,
|
||||
}, nil
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type graphicsContext struct {
|
||||
glContext *opengl.Context
|
||||
screen *Image
|
||||
defaultR *Image
|
||||
screenScale int
|
||||
}
|
||||
|
||||
func (c *graphicsContext) dispose() {
|
||||
// NOTE: Now this method is not used anywhere.
|
||||
framebuffer := c.screen.framebuffer
|
||||
texture := c.screen.texture
|
||||
|
||||
framebuffer.Dispose(c.glContext)
|
||||
texture.Dispose(c.glContext)
|
||||
}
|
||||
|
||||
func (c *graphicsContext) preUpdate() error {
|
||||
return c.screen.Clear()
|
||||
}
|
||||
@ -82,3 +57,34 @@ func (c *graphicsContext) postUpdate() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *graphicsContext) setSize(screenWidth, screenHeight, screenScale int) error {
|
||||
if c.defaultR != nil {
|
||||
c.defaultR.dispose()
|
||||
}
|
||||
if c.screen != nil {
|
||||
c.screen.dispose()
|
||||
}
|
||||
|
||||
var err error
|
||||
useGLContext(func(g *opengl.Context) {
|
||||
f, err := graphics.NewZeroFramebuffer(g, screenWidth*screenScale, screenHeight*screenScale)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
texture, err := graphics.NewTexture(g, screenWidth, screenHeight, g.Nearest)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
screenF, err := graphics.NewFramebufferFromTexture(g, texture)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
screen := &Image{framebuffer: screenF, texture: texture}
|
||||
c.defaultR = &Image{framebuffer: f, texture: nil}
|
||||
c.screen = screen
|
||||
c.screenScale = screenScale
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
12
image.go
12
image.go
@ -169,6 +169,18 @@ func (i *Image) At(x, y int) color.Color {
|
||||
return color.RGBA{r, g, b, a}
|
||||
}
|
||||
|
||||
func (i *Image) dispose() {
|
||||
useGLContext(func(c *opengl.Context) {
|
||||
if i.framebuffer != nil {
|
||||
i.framebuffer.Dispose(c)
|
||||
}
|
||||
if i.texture != nil {
|
||||
i.texture.Dispose(c)
|
||||
}
|
||||
})
|
||||
i.pixels = nil
|
||||
}
|
||||
|
||||
// ReplacePixels replaces the pixels of the image with p.
|
||||
//
|
||||
// The given p must represent RGBA pre-multiplied alpha values. len(p) must equal to 4 * (image width) * (image height).
|
||||
|
@ -85,6 +85,10 @@ func (f *Framebuffer) Size() (width, height int) {
|
||||
}
|
||||
|
||||
func (f *Framebuffer) Dispose(c *opengl.Context) {
|
||||
// Don't delete the default framebuffer.
|
||||
if f.native == opengl.ZeroFramebuffer {
|
||||
return
|
||||
}
|
||||
c.DeleteFramebuffer(f.native)
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,8 @@ type Shader uint32
|
||||
type Program uint32
|
||||
type Buffer uint32
|
||||
|
||||
var ZeroFramebuffer Framebuffer = 0
|
||||
|
||||
// TODO: Remove this after the GopherJS bug was fixed (#159)
|
||||
func (p Program) Equals(other Program) bool {
|
||||
return p == other
|
||||
|
@ -31,6 +31,8 @@ type Framebuffer struct {
|
||||
js.Object
|
||||
}
|
||||
|
||||
var ZeroFramebuffer Framebuffer
|
||||
|
||||
type Shader struct {
|
||||
js.Object
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ func Init() {
|
||||
glfw.WindowHint(glfw.Visible, glfw.False)
|
||||
glfw.WindowHint(glfw.Resizable, glfw.False)
|
||||
|
||||
// As start, create an window with temporary size to create OpenGL context thread.
|
||||
window, err := glfw.CreateWindow(16, 16, "", nil, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -87,9 +88,17 @@ func SwapBuffers() {
|
||||
currentUI.swapBuffers()
|
||||
}
|
||||
|
||||
func SetScreenSize(width, height int) (bool, int) {
|
||||
result := currentUI.setScreenSize(width, height, currentUI.scale)
|
||||
return result, currentUI.actualScale
|
||||
}
|
||||
|
||||
type userInterface struct {
|
||||
window *glfw.Window
|
||||
width int
|
||||
height int
|
||||
scale int
|
||||
actualScale int
|
||||
funcs chan func()
|
||||
}
|
||||
|
||||
@ -105,37 +114,12 @@ func (u *userInterface) start(width, height, scale int, title string) (actualSca
|
||||
x := (videoMode.Width - width*scale) / 2
|
||||
y := (videoMode.Height - height*scale) / 3
|
||||
|
||||
ch := make(chan struct{})
|
||||
window := u.window
|
||||
window.SetFramebufferSizeCallback(func(w *glfw.Window, width, height int) {
|
||||
window.SetFramebufferSizeCallback(nil)
|
||||
close(ch)
|
||||
})
|
||||
window.SetSize(width*scale, height*scale)
|
||||
window.SetTitle(title)
|
||||
window.SetPosition(x, y)
|
||||
window.Show()
|
||||
u.setScreenSize(width, height, scale)
|
||||
u.window.SetTitle(title)
|
||||
u.window.SetPosition(x, y)
|
||||
u.window.Show()
|
||||
|
||||
for {
|
||||
done := false
|
||||
glfw.PollEvents()
|
||||
select {
|
||||
case <-ch:
|
||||
done = true
|
||||
default:
|
||||
}
|
||||
if done {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
u.scale = scale
|
||||
|
||||
// For retina displays, recalculate the scale with the framebuffer size.
|
||||
windowWidth, _ := window.GetFramebufferSize()
|
||||
actualScale = windowWidth / width
|
||||
|
||||
return actualScale, nil
|
||||
return u.actualScale, nil
|
||||
}
|
||||
|
||||
func (u *userInterface) pollEvents() error {
|
||||
@ -167,3 +151,38 @@ func (u *userInterface) isClosed() bool {
|
||||
func (u *userInterface) swapBuffers() {
|
||||
u.window.SwapBuffers()
|
||||
}
|
||||
|
||||
func (u *userInterface) setScreenSize(width, height, scale int) bool {
|
||||
if u.width == width && u.height == height && u.scale == scale {
|
||||
return false
|
||||
}
|
||||
|
||||
ch := make(chan struct{})
|
||||
window := u.window
|
||||
window.SetFramebufferSizeCallback(func(w *glfw.Window, width, height int) {
|
||||
window.SetFramebufferSizeCallback(nil)
|
||||
close(ch)
|
||||
})
|
||||
window.SetSize(width*scale, height*scale)
|
||||
|
||||
for {
|
||||
done := false
|
||||
glfw.PollEvents()
|
||||
select {
|
||||
case <-ch:
|
||||
done = true
|
||||
default:
|
||||
}
|
||||
if done {
|
||||
break
|
||||
}
|
||||
}
|
||||
u.width = width
|
||||
u.height = height
|
||||
u.scale = scale
|
||||
|
||||
// For retina displays, recalculate the scale with the framebuffer size.
|
||||
windowWidth, _ := window.GetFramebufferSize()
|
||||
u.actualScale = windowWidth / width
|
||||
return true
|
||||
}
|
||||
|
@ -45,6 +45,13 @@ func SwapBuffers() {
|
||||
currentUI.swapBuffers()
|
||||
}
|
||||
|
||||
func SetScreenSize(width, height int) (bool, int) {
|
||||
scale := canvas.Get("dataset").Get("ebitenScale").Int()
|
||||
result := currentUI.setScreenSize(width, height, scale)
|
||||
actualScale := canvas.Get("dataset").Get("ebitenActualScale").Int()
|
||||
return result, actualScale
|
||||
}
|
||||
|
||||
var canvas js.Object
|
||||
|
||||
type userInterface struct{}
|
||||
@ -211,13 +218,32 @@ func devicePixelRatio() int {
|
||||
return ratio
|
||||
}
|
||||
|
||||
func (*userInterface) start(width, height, scale int, title string) (actualScale int, err error) {
|
||||
func (u *userInterface) start(width, height, scale int, title string) (actualScale int, err error) {
|
||||
doc := js.Global.Get("document")
|
||||
doc.Set("title", title)
|
||||
actualScale = scale * devicePixelRatio()
|
||||
u.setScreenSize(width, height, scale)
|
||||
canvas.Call("focus")
|
||||
|
||||
actualScale = canvas.Get("dataset").Get("ebitenActualScale").Int()
|
||||
return actualScale, nil
|
||||
}
|
||||
|
||||
func (*userInterface) setScreenSize(width, height, scale int) bool {
|
||||
a := canvas.Get("dataset").Get("ebitenActualScale").Int()
|
||||
if a != 0 {
|
||||
w := canvas.Get("width").Int() / a
|
||||
h := canvas.Get("height").Int() / a
|
||||
s := canvas.Get("dataset").Get("ebitenScale").Int()
|
||||
if w == width && h == height && s == scale {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
actualScale := scale * devicePixelRatio()
|
||||
canvas.Set("width", width*actualScale)
|
||||
canvas.Set("height", height*actualScale)
|
||||
canvas.Get("dataset").Set("ebitenScale", scale)
|
||||
canvas.Get("dataset").Set("ebitenActualScale", actualScale)
|
||||
canvasStyle := canvas.Get("style")
|
||||
|
||||
cssWidth := width * scale
|
||||
@ -227,8 +253,5 @@ func (*userInterface) start(width, height, scale int, title string) (actualScale
|
||||
// CSS calc requires space chars.
|
||||
canvasStyle.Set("left", "calc(50% - "+strconv.Itoa(cssWidth/2)+"px)")
|
||||
canvasStyle.Set("top", "calc(50% - "+strconv.Itoa(cssHeight/2)+"px)")
|
||||
|
||||
canvas.Call("focus")
|
||||
|
||||
return actualScale, nil
|
||||
return true
|
||||
}
|
||||
|
45
run.go
45
run.go
@ -16,16 +16,20 @@ package ebiten
|
||||
|
||||
import (
|
||||
audio "github.com/hajimehoshi/ebiten/exp/audio/internal"
|
||||
"github.com/hajimehoshi/ebiten/internal/graphics/internal/opengl"
|
||||
"github.com/hajimehoshi/ebiten/internal/ui"
|
||||
"time"
|
||||
)
|
||||
|
||||
var fps = 0.0
|
||||
var runContext = &struct {
|
||||
running bool
|
||||
fps float64
|
||||
newScreenWidth int
|
||||
newScreenHeight int
|
||||
}{}
|
||||
|
||||
// CurrentFPS returns the current number of frames per second.
|
||||
func CurrentFPS() float64 {
|
||||
return fps
|
||||
return runContext.fps
|
||||
}
|
||||
|
||||
// Run runs the game.
|
||||
@ -38,16 +42,15 @@ func CurrentFPS() float64 {
|
||||
// but this is not strictly guaranteed.
|
||||
// If you need to care about time, you need to check current time every time f is called.
|
||||
func Run(f func(*Image) error, width, height, scale int, title string) error {
|
||||
runContext.running = true
|
||||
|
||||
actualScale, err := ui.Start(width, height, scale, title)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ui.Terminate()
|
||||
|
||||
var graphicsContext *graphicsContext
|
||||
useGLContext(func(c *opengl.Context) {
|
||||
graphicsContext, err = newGraphicsContext(c, width, height, actualScale)
|
||||
})
|
||||
graphicsContext, err := newGraphicsContext(width, height, actualScale)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -55,6 +58,18 @@ func Run(f func(*Image) error, width, height, scale int, title string) error {
|
||||
frames := 0
|
||||
t := time.Now().UnixNano()
|
||||
for {
|
||||
if 0 < runContext.newScreenWidth || 0 < runContext.newScreenHeight {
|
||||
changed, actualScale := ui.SetScreenSize(runContext.newScreenWidth, runContext.newScreenHeight)
|
||||
if changed {
|
||||
w, h := runContext.newScreenWidth, runContext.newScreenHeight
|
||||
if err := graphicsContext.setSize(w, h, actualScale); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
runContext.newScreenWidth = 0
|
||||
runContext.newScreenHeight = 0
|
||||
|
||||
if err := ui.DoEvents(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -81,9 +96,23 @@ func Run(f func(*Image) error, width, height, scale int, title string) error {
|
||||
now := time.Now().UnixNano()
|
||||
frames++
|
||||
if time.Second <= time.Duration(now-t) {
|
||||
fps = float64(frames) * float64(time.Second) / float64(now-t)
|
||||
runContext.fps = float64(frames) * float64(time.Second) / float64(now-t)
|
||||
t = now
|
||||
frames = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetScreenSize changes the (logical) size of the screen.
|
||||
// This doesn't affect the current scale of the screen.
|
||||
func SetScreenSize(width, height int) {
|
||||
if !runContext.running {
|
||||
panic("SetScreenSize must be called during Run")
|
||||
}
|
||||
runContext.newScreenWidth = width
|
||||
runContext.newScreenHeight = height
|
||||
}
|
||||
|
||||
// TODO: Create SetScreenPosition (for GLFW)
|
||||
|
||||
// TODO: Create SetScreenScale
|
||||
|
Loading…
Reference in New Issue
Block a user