Add SetScreenSize

This commit is contained in:
Hajime Hoshi 2015-02-09 10:25:00 +09:00
parent 9b1aee1a21
commit 249add3979
9 changed files with 258 additions and 76 deletions

View 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)
}
}

View File

@ -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
}

View File

@ -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).

View File

@ -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)
}

View File

@ -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

View File

@ -31,6 +31,8 @@ type Framebuffer struct {
js.Object
}
var ZeroFramebuffer Framebuffer
type Shader struct {
js.Object
}

View File

@ -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
}

View File

@ -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
View File

@ -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