Skip to content

Instantly share code, notes, and snippets.

@reusee
Created September 14, 2012 17:41
Show Gist options
  • Save reusee/3723451 to your computer and use it in GitHub Desktop.
Save reusee/3723451 to your computer and use it in GitHub Desktop.
image viewer written by golang
package main
import (
"fmt"
sf "gosfml2"
"time"
"path/filepath"
"os"
"flag"
"math/rand"
)
const (
FRAMERATE_LIMIT = 60
SCROLL_PIXELS = 30
SCALE_FACTOR = 0.1
ROTATE_ANGLE = 2
)
var RANDOM = flag.Bool("r", false, "random order")
func main() {
fmt.Printf("")
rand.Seed(time.Now().UnixNano())
flag.Parse()
root := flag.Arg(0)
if root == "" {
root = "."
}
files := make([]string, 0)
filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
if !f.IsDir() {
files = append(files, path)
}
return nil
})
if len(files) == 0 {
return
}
if *RANDOM {
perm := rand.Perm(len(files))
for i, v := range perm {
files[i], files[v] = files[v], files[i]
}
}
store := newResourceStore(files)
settings := sf.ContextSettings{AntialiasingLevel: 32}
videoMode := sf.VideoMode{}
videoMode.SetAsDesktopVideoMode()
window := sf.NewRenderWindow(videoMode, "hello", sf.Style_DefaultStyle, &settings)
window.SetFramerateLimit(FRAMERATE_LIMIT)
window_size := window.GetSize()
width, height := window_size.X, window_size.Y
widthF, heightF := float32(width), float32(height)
setNewViewedSprite := func(sprite *sf.Sprite) {
localBounds := sprite.GetLocalBounds()
sprite.SetOrigin(sf.Vector2f{localBounds.Width / 2, localBounds.Height / 2})
sprite.SetPosition(sf.Vector2f{widthF / 2, heightF / 2})
globalBounds := sprite.GetGlobalBounds()
w_ratio, h_ratio := widthF / globalBounds.Width, heightF / globalBounds.Height
ratio := w_ratio
if h_ratio < w_ratio {
ratio = h_ratio
}
sprite.Scale(sf.Vector2f{ratio, ratio})
sprite.SetRotation(0)
}
sprite := store.getNext()
setNewViewedSprite(sprite)
ticker := time.NewTicker(time.Second / FRAMERATE_LIMIT)
allowFlip := true
flipSprite := func(sprite *sf.Sprite) {
if sprite == nil {
window.Close()
}
setNewViewedSprite(sprite)
allowFlip = false
time.AfterFunc(time.Millisecond * 200, func() { allowFlip = true })
}
for window.IsOpen() {
select {
case <-ticker.C:
for event := window.PollEvent(); event != nil; event = window.PollEvent() {
switch event.(type) {
case *sf.EventClosed:
window.Close()
}
}
switch {
// scroll
case sf.KeyboardIsKeyPressed(sf.Key_A):
sprite.Move(sf.Vector2f{SCROLL_PIXELS, 0})
case sf.KeyboardIsKeyPressed(sf.Key_D):
sprite.Move(sf.Vector2f{-SCROLL_PIXELS, 0})
case sf.KeyboardIsKeyPressed(sf.Key_W):
sprite.Move(sf.Vector2f{0, SCROLL_PIXELS})
case sf.KeyboardIsKeyPressed(sf.Key_S):
sprite.Move(sf.Vector2f{0, -SCROLL_PIXELS})
// reset
case sf.KeyboardIsKeyPressed(sf.Key_M):
setNewViewedSprite(sprite)
// quit
case sf.KeyboardIsKeyPressed(sf.Key_Q):
window.Close()
// scale and rotate
case sf.KeyboardIsKeyPressed(sf.Key_J), sf.KeyboardIsKeyPressed(sf.Key_Z):
sprite.Scale(sf.Vector2f{SCALE_FACTOR + 1.0, SCALE_FACTOR + 1.0})
case sf.KeyboardIsKeyPressed(sf.Key_K), sf.KeyboardIsKeyPressed(sf.Key_X):
sprite.Scale(sf.Vector2f{1.0 - SCALE_FACTOR, 1.0 - SCALE_FACTOR})
case sf.KeyboardIsKeyPressed(sf.Key_N), sf.KeyboardIsKeyPressed(sf.Key_E):
sprite.SetScale(sf.Vector2f{1.0, 1.0})
case sf.KeyboardIsKeyPressed(sf.Key_U), sf.KeyboardIsKeyPressed(sf.Key_R):
sprite.Rotate(-ROTATE_ANGLE)
case sf.KeyboardIsKeyPressed(sf.Key_I):
sprite.Rotate(ROTATE_ANGLE)
// navigation
case sf.KeyboardIsKeyPressed(sf.Key_Space) && allowFlip:
sprite = store.getNext()
flipSprite(sprite)
case sf.KeyboardIsKeyPressed(sf.Key_C) && allowFlip:
sprite = store.getPrev()
flipSprite(sprite)
case sf.KeyboardIsKeyPressed(sf.Key_F) && allowFlip:
sprite = store.getRand()
flipSprite(sprite)
}
window.Clear(sf.Color_Black)
window.Draw(sprite, nil)
window.Display()
}
}
}
type ResourceStore struct {
paths []string
store map[string]*Resource
index int
locks map[string]chan bool
}
type Resource struct {
state int
sprite *sf.Sprite
}
const (
State_NotVisited = iota
State_Invalid
State_Valid
)
func newResourceStore(files []string) *ResourceStore {
store := new(ResourceStore)
store.paths = files
store.store = make(map[string]*Resource)
store.locks = make(map[string]chan bool)
for _, f := range files {
store.store[f] = &Resource{state: State_NotVisited}
store.locks[f] = make(chan bool, 1)
store.locks[f] <- true
}
store.index = -1
return store
}
func (store *ResourceStore) getNext() *sf.Sprite {
return store.get(1)
}
func (store *ResourceStore) getPrev() *sf.Sprite {
return store.get(-1)
}
func (store *ResourceStore) getRand() *sf.Sprite {
store.index = rand.Intn(len(store.paths))
return store.get(1)
}
func (store *ResourceStore) get(step int) (ret *sf.Sprite) {
//defer func() {
// endIndex := store.index + 5
// if endIndex >= len(store.paths) {
// endIndex = len(store.paths) - 1
// }
// for _, path := range store.paths[store.index + 1: endIndex] {
// go store.load(path)
// }
//}()
store.index += step
for i := 0; i < len(store.paths); i++ {
sprite := store.load(store.paths[store.index])
if sprite == nil {
store.index += step
switch {
case store.index >= len(store.paths):
store.index = 0
case store.index < 0:
store.index = len(store.paths) - 1
}
continue
}
return sprite
}
return nil
}
func (store *ResourceStore) load(path string) (ret *sf.Sprite) {
//<-store.locks[path]
//defer func() { store.locks[path] <- true }()
cur := store.store[path]
if cur.state == State_Valid {
return cur.sprite
}
if cur.state == State_Invalid {
return nil
}
texture := sf.NewTextureFromFile(path)
if !texture.IsValid() {
cur.state = State_Invalid
return nil
}
texture.SetSmooth(true)
sprite := sf.NewSprite(texture)
cur.sprite = sprite
cur.state = State_Valid
return cur.sprite
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment