<commit_before>// Copyright 2012-2017 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// assumptions
// we've been booted into a ramfs with all this stuff unpacked and ready.
// we don't need a loop device mount because it's all there.
// So we run /go/bin/go build installcommand
// and then exec /buildbin/sh

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"syscall"

	"github.com/u-root/u-root/pkg/cmdline"
	"github.com/u-root/u-root/pkg/uroot/util"
)

var (
	verbose = flag.Bool("v", false, "print all build commands")
	test    = flag.Bool("test", false, "Test mode: don't try to set control tty")
	debug   = func(string, ...interface{}) {}
)

func main() {
	a := []string{"build"}
	flag.Parse()
	log.Printf("Welcome to u-root")
	util.Rootfs()

	if *verbose {
		debug = log.Printf
		a = append(a, "-x")
	}

	// populate buildbin

	// In earlier versions we just had src/cmds. Due to the Go rules it seems we need to
	// embed the URL of the repo everywhere. Yuck.
	c, err := filepath.Glob("/src/github.com/u-root/*/cmds/[a-z]*")
	if err != nil || len(c) == 0 {
		log.Printf("In a break with tradition, you seem to have NO u-root commands: %v", err)
	}
	o, err := filepath.Glob("/src/*/*/*")
	if err != nil {
		log.Printf("Your filepath glob for other commands seems busted: %v", err)
	}
	c = append(c, o...)
	for _, v := range c {
		name := filepath.Base(v)
		if name == "installcommand" || name == "init" {
			continue
		} else {
			destPath := filepath.Join("/buildbin", name)
			source := "/buildbin/installcommand"
			if err := os.Symlink(source, destPath); err != nil {
				log.Printf("Symlink %v -> %v failed; %v", source, destPath, err)
			}
		}
	}

	envs := os.Environ()
	debug("envs %v", envs)
	os.Setenv("GOBIN", "/buildbin")
	a = append(a, "-o", "/buildbin/installcommand", filepath.Join(util.CmdsPath, "installcommand"))
	cmd := exec.Command("go", a...)
	installenvs := envs
	installenvs = append(envs, "GOBIN=/buildbin")
	cmd.Env = installenvs
	cmd.Dir = "/"

	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	debug("Run %v", cmd)
	if err := cmd.Run(); err != nil {
		log.Printf("%v\n", err)
	}

	// Before entering an interactive shell, decrease the loglevel because
	// spamming non-critical logs onto the shell frustrates users. The logs
	// are still accessible through dmesg.
	const sysLogActionConsoleLevel = 8
	const kernNotice = 5 // Only messages more severe than "notice" are printed.
	if _, _, err := syscall.Syscall(syscall.SYS_SYSLOG, sysLogActionConsoleLevel, 0, kernNotice); err != 0 {
		log.Print("Could not set log level")
	}

	// install /env.
	os.Setenv("GOBIN", "/ubin")
	envs = append(envs, "GOBIN=/ubin")
	for _, e := range envs {
		nv := strings.SplitN(e, "=", 2)
		if len(nv) < 2 {
			nv = append(nv, "")
		}
		n := filepath.Join("/env", nv[0])
		if err := ioutil.WriteFile(n, []byte(nv[1]), 0666); err != nil {
			log.Printf("%v: %v", n, err)
		}
	}

	var profile string
	// Some systems wipe out all the environment variables we so carefully craft.
	// There is a way out -- we can put them into /etc/profile.d/uroot if we want.
	// The PATH variable has to change, however.
	path := fmt.Sprintf("%v:%v:%v:%v", util.GoBin(), util.PATHHEAD, "$PATH", util.PATHTAIL)
	for k, v := range util.Env {
		// We're doing the hacky way for now. We can clean this up later.
		if k == "PATH" {
			profile += "export PATH=" + path + "\n"
		} else {
			profile += "export " + k + "=" + v + "\n"
		}
	}

	// The IFS lets us force a rehash every time we type a command, so that when we
	// build uroot commands we don't keep rebuilding them.
	profile += "IFS=`hash -r`\n"

	// IF the profile is used, THEN when the user logs in they will need a
	// private tmpfs. There's no good way to do this on linux. The closest
	// we can get for now is to mount a tmpfs of /go/pkg/%s_%s :-( Same
	// applies to ubin. Each user should have their own.
	profile += fmt.Sprintf("sudo mount -t tmpfs none /go/pkg/%s_%s\n", runtime.GOOS, runtime.GOARCH)
	profile += fmt.Sprintf("sudo mount -t tmpfs none /ubin\n")
	profile += fmt.Sprintf("sudo mount -t tmpfs none /pkg\n")

	// Now here's some good fun. We've set environment variables we want to see used.
	// But on some systems the environment variable we create is completely ignored.
	// Oh, is that you again, tinycore? Well.
	// So we can save the day by writing the profile string to /etc/profile.d/uroot.sh
	// mode, usually, 644.
	// Only bother doing this is /etc/profile.d exists and is a directory.
	if fi, err := os.Stat("/etc/profile.d"); err == nil && fi.IsDir() {
		if err := ioutil.WriteFile("/etc/profile.d/uroot.sh", []byte(profile), 0644); err != nil {
			log.Printf("Trying to write uroot profile failed: %v", err)
		}
	}

	// Start background build.
	if isBgBuildEnabled() {
		go startBgBuild()
	}

	// There may be an inito if we are building on
	// an existing initramfs. So, first, try to
	// run inito and then run our shell
	// inito is always first and we set default flags for it.
	cloneFlags := uintptr(syscall.CLONE_NEWPID)
	cmdList := []string{
		"/inito",
		"/bbin/uinit",
		"/buildbin/uinit",
		"/bbin/sh",
		"/bbin/rush",
		"/buildbin/sh",
		"/buildbin/rush",
	}

	noCmdFound := true
	for _, v := range cmdList {
		if _, err := os.Stat(v); !os.IsNotExist(err) {

			noCmdFound = false

			initFlags := cmdline.GetInitFlagMap()
			// systemd gets upset when it discovers it isn't really process 1
			systemd, present := initFlags["systemd"]
			systemdEnabled, boolErr := strconv.ParseBool(systemd)
			if !present || boolErr != nil || systemdEnabled == false {
				cmd = exec.Command(v)
				cmd.Env = envs
				cmd.Stdin = os.Stdin
				cmd.Stderr = os.Stderr
				cmd.Stdout = os.Stdout
				if *test {
					cmd.SysProcAttr = &syscall.SysProcAttr{Cloneflags: cloneFlags}
				} else {
					cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true, Cloneflags: cloneFlags}
				}
				debug("Run %v", cmd)
				if err := cmd.Run(); err != nil {
					log.Print(err)
				}
			} else {
				debug("Exec %v", v)
				if err := syscall.Exec(v, []string{v}, envs); err != nil {
					log.Print(err)
				}
			}
		}
		// only the first init needs its own PID space.
		cloneFlags = 0
	}

	if noCmdFound {
		log.Printf("init: No suitable executable found in %+v", cmdList)
	}

	log.Printf("init: All commands exited")
	log.Printf("init: Syncing filesystems")
	syscall.Sync()
	log.Printf("init: Exiting...")
}
<commit_msg>init: add a loop to reap dead child processes<commit_after>// Copyright 2012-2017 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// assumptions
// we've been booted into a ramfs with all this stuff unpacked and ready.
// we don't need a loop device mount because it's all there.
// So we run /go/bin/go build installcommand
// and then exec /buildbin/sh

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"syscall"

	"github.com/u-root/u-root/pkg/cmdline"
	"github.com/u-root/u-root/pkg/uroot/util"
)

var (
	verbose = flag.Bool("v", false, "print all build commands")
	test    = flag.Bool("test", false, "Test mode: don't try to set control tty")
	debug   = func(string, ...interface{}) {}
)

func main() {
	a := []string{"build"}
	flag.Parse()
	log.Printf("Welcome to u-root")
	util.Rootfs()

	if *verbose {
		debug = log.Printf
		a = append(a, "-x")
	}

	// populate buildbin

	// In earlier versions we just had src/cmds. Due to the Go rules it seems we need to
	// embed the URL of the repo everywhere. Yuck.
	c, err := filepath.Glob("/src/github.com/u-root/*/cmds/[a-z]*")
	if err != nil || len(c) == 0 {
		log.Printf("In a break with tradition, you seem to have NO u-root commands: %v", err)
	}
	o, err := filepath.Glob("/src/*/*/*")
	if err != nil {
		log.Printf("Your filepath glob for other commands seems busted: %v", err)
	}
	c = append(c, o...)
	for _, v := range c {
		name := filepath.Base(v)
		if name == "installcommand" || name == "init" {
			continue
		} else {
			destPath := filepath.Join("/buildbin", name)
			source := "/buildbin/installcommand"
			if err := os.Symlink(source, destPath); err != nil {
				log.Printf("Symlink %v -> %v failed; %v", source, destPath, err)
			}
		}
	}

	envs := os.Environ()
	debug("envs %v", envs)
	os.Setenv("GOBIN", "/buildbin")
	a = append(a, "-o", "/buildbin/installcommand", filepath.Join(util.CmdsPath, "installcommand"))
	cmd := exec.Command("go", a...)
	installenvs := envs
	installenvs = append(envs, "GOBIN=/buildbin")
	cmd.Env = installenvs
	cmd.Dir = "/"

	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	debug("Run %v", cmd)
	if err := cmd.Run(); err != nil {
		log.Printf("%v\n", err)
	}

	// Before entering an interactive shell, decrease the loglevel because
	// spamming non-critical logs onto the shell frustrates users. The logs
	// are still accessible through dmesg.
	const sysLogActionConsoleLevel = 8
	const kernNotice = 5 // Only messages more severe than "notice" are printed.
	if _, _, err := syscall.Syscall(syscall.SYS_SYSLOG, sysLogActionConsoleLevel, 0, kernNotice); err != 0 {
		log.Print("Could not set log level")
	}

	// install /env.
	os.Setenv("GOBIN", "/ubin")
	envs = append(envs, "GOBIN=/ubin")
	for _, e := range envs {
		nv := strings.SplitN(e, "=", 2)
		if len(nv) < 2 {
			nv = append(nv, "")
		}
		n := filepath.Join("/env", nv[0])
		if err := ioutil.WriteFile(n, []byte(nv[1]), 0666); err != nil {
			log.Printf("%v: %v", n, err)
		}
	}

	var profile string
	// Some systems wipe out all the environment variables we so carefully craft.
	// There is a way out -- we can put them into /etc/profile.d/uroot if we want.
	// The PATH variable has to change, however.
	path := fmt.Sprintf("%v:%v:%v:%v", util.GoBin(), util.PATHHEAD, "$PATH", util.PATHTAIL)
	for k, v := range util.Env {
		// We're doing the hacky way for now. We can clean this up later.
		if k == "PATH" {
			profile += "export PATH=" + path + "\n"
		} else {
			profile += "export " + k + "=" + v + "\n"
		}
	}

	// The IFS lets us force a rehash every time we type a command, so that when we
	// build uroot commands we don't keep rebuilding them.
	profile += "IFS=`hash -r`\n"

	// IF the profile is used, THEN when the user logs in they will need a
	// private tmpfs. There's no good way to do this on linux. The closest
	// we can get for now is to mount a tmpfs of /go/pkg/%s_%s :-( Same
	// applies to ubin. Each user should have their own.
	profile += fmt.Sprintf("sudo mount -t tmpfs none /go/pkg/%s_%s\n", runtime.GOOS, runtime.GOARCH)
	profile += fmt.Sprintf("sudo mount -t tmpfs none /ubin\n")
	profile += fmt.Sprintf("sudo mount -t tmpfs none /pkg\n")

	// Now here's some good fun. We've set environment variables we want to see used.
	// But on some systems the environment variable we create is completely ignored.
	// Oh, is that you again, tinycore? Well.
	// So we can save the day by writing the profile string to /etc/profile.d/uroot.sh
	// mode, usually, 644.
	// Only bother doing this is /etc/profile.d exists and is a directory.
	if fi, err := os.Stat("/etc/profile.d"); err == nil && fi.IsDir() {
		if err := ioutil.WriteFile("/etc/profile.d/uroot.sh", []byte(profile), 0644); err != nil {
			log.Printf("Trying to write uroot profile failed: %v", err)
		}
	}

	// Start background build.
	if isBgBuildEnabled() {
		go startBgBuild()
	}

	cmdList := []string{
		"/inito",
		"/bbin/uinit",
		"/buildbin/uinit",
		"/bbin/sh",
		"/bbin/rush",
		"/buildbin/sh",
		"/buildbin/rush",
	}

	var cmdCount int
	// systemd is "special". If we are supposed to run systemd, we're
	// going to exec, and if we're going to exec, we're done here.
	// systemd uber alles.
	initFlags := cmdline.GetInitFlagMap()
	// systemd gets upset when it discovers it isn't really process 1, so
	// we can't start it in its own namespace. I just love systemd.
	systemd, present := initFlags["systemd"]
	systemdEnabled, boolErr := strconv.ParseBool(systemd)
	if present && boolErr == nil && systemdEnabled == true {
		v := cmdList[0]
		debug("Exec %v", v)
		if err := syscall.Exec(v, []string{v}, envs); err != nil {
			log.Printf("Lucky you, systemd failed: %v", err)
		}
		// well, what a shame.
		cmdList = cmdList[1:]
		cmdCount++
	}
	// If the systemd failed for some reason, we might as well do the other things.

	for _, v := range cmdList {
		if _, err := os.Stat(v); os.IsNotExist(err) {
			continue
		}

		// inito is (optionally) created by the u-root command when
		// the u-root initramfs is merged with an existing initramfs that has
		// a /init. The name inito means "original /init"
		// There may be an inito if we are building on
		// an existing initramfs. All initos need their
		// own pid space.
		var cloneFlags uintptr
		if v == "/inito" {
			cloneFlags = uintptr(syscall.CLONE_NEWPID)
		}

		cmdCount++
		cmd = exec.Command(v)
		cmd.Env = envs
		cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
		if *test {
			cmd.SysProcAttr = &syscall.SysProcAttr{Cloneflags: cloneFlags}
		} else {
			cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true, Cloneflags: cloneFlags}
		}
		debug("Run %v", cmd)
		if err := cmd.Start(); err != nil {
			log.Printf("Error starting %v: %v", v, err)
			continue
		}
		for {
			var s syscall.WaitStatus
			var r syscall.Rusage
			if p, err := syscall.Wait4(-1, &s, 0, &r); p == cmd.Process.Pid {
				debug("Shell exited, exit status %d", s.ExitStatus())
				break
			} else if p != -1 {
				debug("Reaped PID %d, exit status %d", p, s.ExitStatus())
			} else {
				debug("Error from Wait4 for orphaned child: %v", err)
				break
			}
		}
		if err := cmd.Process.Release(); err != nil {
			log.Printf("Error releasing %v:%v", v, err)
		}
	}
	if cmdCount == 0 {
		log.Printf("init: No suitable executable found in %+v", cmdList)
	}

	// We need to reap all children before exiting.
	go func() {
		for {
			var s syscall.WaitStatus
			var r syscall.Rusage
			if p, err := syscall.Wait4(-1, &s, 0, &r); err != nil {
				log.Printf("%v: exited with %v, status %v, rusage %v", p, err, s, r)
			}
		}
	}()
	log.Printf("init: All commands exited")
	log.Printf("init: Syncing filesystems")
	syscall.Sync()
	log.Printf("init: Exiting...")
}
<|endoftext|>


<commit_before>package blob

import (
	"bytes"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"time"

	"github.com/anacrolix/missinggo"

	dataPkg "github.com/anacrolix/torrent/data"
	"github.com/anacrolix/torrent/metainfo"
)

const (
	filePerm = 0640
	dirPerm  = 0750
)

type store struct {
	baseDir  string
	capacity int64

	mu        sync.Mutex
	completed map[[20]byte]struct{}
}

func (me *store) OpenTorrent(info *metainfo.Info) dataPkg.Data {
	return &data{info, me}
}

type StoreOption func(*store)

func Capacity(bytes int64) StoreOption {
	return func(s *store) {
		s.capacity = bytes
	}
}

func NewStore(baseDir string, opt ...StoreOption) dataPkg.Store {
	s := &store{baseDir, -1, sync.Mutex{}, nil}
	for _, o := range opt {
		o(s)
	}
	s.initCompleted()
	return s
}

// Turns 40 byte hex string into its equivalent binary byte array.
func hexStringPieceHashArray(s string) (ret [20]byte, ok bool) {
	if len(s) != 40 {
		return
	}
	n, err := hex.Decode(ret[:], []byte(s))
	if err != nil {
		return
	}
	if n != 20 {
		panic(n)
	}
	ok = true
	return
}

func (me *store) initCompleted() {
	fis, err := me.readCompletedDir()
	if err != nil {
		panic(err)
	}
	me.mu.Lock()
	me.completed = make(map[[20]byte]struct{}, len(fis))
	for _, fi := range fis {
		binHash, ok := hexStringPieceHashArray(fi.Name())
		if !ok {
			continue
		}
		me.completed[binHash] = struct{}{}
	}
	me.mu.Unlock()
}

func (me *store) completePieceDirPath() string {
	return filepath.Join(me.baseDir, "complete")
}

func (me *store) path(p metainfo.Piece, completed bool) string {
	return filepath.Join(me.baseDir, func() string {
		if completed {
			return "complete"
		} else {
			return "incomplete"
		}
	}(), fmt.Sprintf("%x", p.Hash()))
}

func sliceToPieceHashArray(b []byte) (ret [20]byte) {
	n := copy(ret[:], b)
	if n != 20 {
		panic(n)
	}
	return
}

func (me *store) pieceComplete(p metainfo.Piece) bool {
	me.mu.Lock()
	defer me.mu.Unlock()
	_, ok := me.completed[sliceToPieceHashArray(p.Hash())]
	return ok
}

func (me *store) pieceWrite(p metainfo.Piece) (f *os.File) {
	if me.pieceComplete(p) {
		return
	}
	name := me.path(p, false)
	os.MkdirAll(filepath.Dir(name), dirPerm)
	f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY, filePerm)
	if err != nil {
		panic(err)
	}
	return
}

// Returns the file for the given piece, if it exists. It could be completed,
// or incomplete.
func (me *store) pieceRead(p metainfo.Piece) (f *os.File) {
	f, err := os.Open(me.path(p, true))
	if err == nil {
		return
	}
	if !os.IsNotExist(err) {
		panic(err)
	}
	// Mark the file not completed, in case we thought it was. TODO: Trigger
	// an asynchronous initCompleted to reinitialize the entire completed map
	// as there are likely other files missing.
	me.mu.Lock()
	delete(me.completed, sliceToPieceHashArray(p.Hash()))
	me.mu.Unlock()
	f, err = os.Open(me.path(p, false))
	if err == nil {
		return
	}
	if !os.IsNotExist(err) {
		panic(err)
	}
	return
}

func (me *store) readCompletedDir() (fis []os.FileInfo, err error) {
	f, err := os.Open(me.completePieceDirPath())
	if err != nil {
		if os.IsNotExist(err) {
			err = nil
		}
		return
	}
	fis, err = f.Readdir(-1)
	f.Close()
	return
}

func (me *store) removeCompleted(name string) (err error) {
	err = os.Remove(filepath.Join(me.completePieceDirPath(), name))
	if os.IsNotExist(err) {
		err = nil
	}
	if err != nil {
		return err
	}
	binHash, ok := hexStringPieceHashArray(name)
	if ok {
		me.mu.Lock()
		delete(me.completed, binHash)
		me.mu.Unlock()
	}
	return
}

type fileInfoSorter struct {
	fis []os.FileInfo
}

func (me fileInfoSorter) Len() int {
	return len(me.fis)
}

func lastTime(fi os.FileInfo) (ret time.Time) {
	ret = fi.ModTime()
	atime := missinggo.FileInfoAccessTime(fi)
	if atime.After(ret) {
		ret = atime
	}
	return
}

func (me fileInfoSorter) Less(i, j int) bool {
	return lastTime(me.fis[i]).Before(lastTime(me.fis[j]))
}

func (me fileInfoSorter) Swap(i, j int) {
	me.fis[i], me.fis[j] = me.fis[j], me.fis[i]
}

func sortFileInfos(fis []os.FileInfo) {
	sorter := fileInfoSorter{fis}
	sort.Sort(sorter)
}

func (me *store) makeSpace(space int64) error {
	if me.capacity < 0 {
		return nil
	}
	if space > me.capacity {
		return errors.New("space requested exceeds capacity")
	}
	fis, err := me.readCompletedDir()
	if err != nil {
		return err
	}
	var size int64
	for _, fi := range fis {
		size += fi.Size()
	}
	sortFileInfos(fis)
	for size > me.capacity-space {
		me.removeCompleted(fis[0].Name())
		size -= fis[0].Size()
		fis = fis[1:]
	}
	return nil
}

func (me *store) PieceCompleted(p metainfo.Piece) (err error) {
	err = me.makeSpace(p.Length())
	if err != nil {
		return
	}
	var (
		incompletePiecePath = me.path(p, false)
		completedPiecePath  = me.path(p, true)
	)
	fSrc, err := os.Open(incompletePiecePath)
	if err != nil {
		return
	}
	defer fSrc.Close()
	os.MkdirAll(filepath.Dir(completedPiecePath), dirPerm)
	fDst, err := os.OpenFile(completedPiecePath, os.O_EXCL|os.O_CREATE|os.O_WRONLY, filePerm)
	if err != nil {
		return
	}
	defer fDst.Close()
	hasher := sha1.New()
	r := io.TeeReader(io.LimitReader(fSrc, p.Length()), hasher)
	_, err = io.Copy(fDst, r)
	if err != nil {
		return
	}
	if !bytes.Equal(hasher.Sum(nil), p.Hash()) {
		err = errors.New("piece incomplete")
		os.Remove(completedPiecePath)
		return
	}
	os.Remove(incompletePiecePath)
	me.mu.Lock()
	me.completed[sliceToPieceHashArray(p.Hash())] = struct{}{}
	me.mu.Unlock()
	return
}
<commit_msg>Match the names used in other blob implementations<commit_after>package blob

import (
	"bytes"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"time"

	"github.com/anacrolix/missinggo"

	dataPkg "github.com/anacrolix/torrent/data"
	"github.com/anacrolix/torrent/metainfo"
)

const (
	filePerm = 0640
	dirPerm  = 0750
)

type store struct {
	baseDir  string
	capacity int64

	mu        sync.Mutex
	completed map[[20]byte]struct{}
}

func (me *store) OpenTorrent(info *metainfo.Info) dataPkg.Data {
	return &data{info, me}
}

type StoreOption func(*store)

func Capacity(bytes int64) StoreOption {
	return func(s *store) {
		s.capacity = bytes
	}
}

func NewStore(baseDir string, opt ...StoreOption) dataPkg.Store {
	s := &store{baseDir, -1, sync.Mutex{}, nil}
	for _, o := range opt {
		o(s)
	}
	s.initCompleted()
	return s
}

// Turns 40 byte hex string into its equivalent binary byte array.
func hexStringPieceHashArray(s string) (ret [20]byte, ok bool) {
	if len(s) != 40 {
		return
	}
	n, err := hex.Decode(ret[:], []byte(s))
	if err != nil {
		return
	}
	if n != 20 {
		panic(n)
	}
	ok = true
	return
}

func (me *store) initCompleted() {
	fis, err := me.readCompletedDir()
	if err != nil {
		panic(err)
	}
	me.mu.Lock()
	me.completed = make(map[[20]byte]struct{}, len(fis))
	for _, fi := range fis {
		binHash, ok := hexStringPieceHashArray(fi.Name())
		if !ok {
			continue
		}
		me.completed[binHash] = struct{}{}
	}
	me.mu.Unlock()
}

func (me *store) completePieceDirPath() string {
	return filepath.Join(me.baseDir, "completed")
}

func (me *store) path(p metainfo.Piece, completed bool) string {
	return filepath.Join(me.baseDir, func() string {
		if completed {
			return "completed"
		} else {
			return "incomplete"
		}
	}(), fmt.Sprintf("%x", p.Hash()))
}

func sliceToPieceHashArray(b []byte) (ret [20]byte) {
	n := copy(ret[:], b)
	if n != 20 {
		panic(n)
	}
	return
}

func (me *store) pieceComplete(p metainfo.Piece) bool {
	me.mu.Lock()
	defer me.mu.Unlock()
	_, ok := me.completed[sliceToPieceHashArray(p.Hash())]
	return ok
}

func (me *store) pieceWrite(p metainfo.Piece) (f *os.File) {
	if me.pieceComplete(p) {
		return
	}
	name := me.path(p, false)
	os.MkdirAll(filepath.Dir(name), dirPerm)
	f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY, filePerm)
	if err != nil {
		panic(err)
	}
	return
}

// Returns the file for the given piece, if it exists. It could be completed,
// or incomplete.
func (me *store) pieceRead(p metainfo.Piece) (f *os.File) {
	f, err := os.Open(me.path(p, true))
	if err == nil {
		return
	}
	if !os.IsNotExist(err) {
		panic(err)
	}
	// Mark the file not completed, in case we thought it was. TODO: Trigger
	// an asynchronous initCompleted to reinitialize the entire completed map
	// as there are likely other files missing.
	me.mu.Lock()
	delete(me.completed, sliceToPieceHashArray(p.Hash()))
	me.mu.Unlock()
	f, err = os.Open(me.path(p, false))
	if err == nil {
		return
	}
	if !os.IsNotExist(err) {
		panic(err)
	}
	return
}

func (me *store) readCompletedDir() (fis []os.FileInfo, err error) {
	f, err := os.Open(me.completePieceDirPath())
	if err != nil {
		if os.IsNotExist(err) {
			err = nil
		}
		return
	}
	fis, err = f.Readdir(-1)
	f.Close()
	return
}

func (me *store) removeCompleted(name string) (err error) {
	err = os.Remove(filepath.Join(me.completePieceDirPath(), name))
	if os.IsNotExist(err) {
		err = nil
	}
	if err != nil {
		return err
	}
	binHash, ok := hexStringPieceHashArray(name)
	if ok {
		me.mu.Lock()
		delete(me.completed, binHash)
		me.mu.Unlock()
	}
	return
}

type fileInfoSorter struct {
	fis []os.FileInfo
}

func (me fileInfoSorter) Len() int {
	return len(me.fis)
}

func lastTime(fi os.FileInfo) (ret time.Time) {
	ret = fi.ModTime()
	atime := missinggo.FileInfoAccessTime(fi)
	if atime.After(ret) {
		ret = atime
	}
	return
}

func (me fileInfoSorter) Less(i, j int) bool {
	return lastTime(me.fis[i]).Before(lastTime(me.fis[j]))
}

func (me fileInfoSorter) Swap(i, j int) {
	me.fis[i], me.fis[j] = me.fis[j], me.fis[i]
}

func sortFileInfos(fis []os.FileInfo) {
	sorter := fileInfoSorter{fis}
	sort.Sort(sorter)
}

func (me *store) makeSpace(space int64) error {
	if me.capacity < 0 {
		return nil
	}
	if space > me.capacity {
		return errors.New("space requested exceeds capacity")
	}
	fis, err := me.readCompletedDir()
	if err != nil {
		return err
	}
	var size int64
	for _, fi := range fis {
		size += fi.Size()
	}
	sortFileInfos(fis)
	for size > me.capacity-space {
		me.removeCompleted(fis[0].Name())
		size -= fis[0].Size()
		fis = fis[1:]
	}
	return nil
}

func (me *store) PieceCompleted(p metainfo.Piece) (err error) {
	err = me.makeSpace(p.Length())
	if err != nil {
		return
	}
	var (
		incompletePiecePath = me.path(p, false)
		completedPiecePath  = me.path(p, true)
	)
	fSrc, err := os.Open(incompletePiecePath)
	if err != nil {
		return
	}
	defer fSrc.Close()
	os.MkdirAll(filepath.Dir(completedPiecePath), dirPerm)
	fDst, err := os.OpenFile(completedPiecePath, os.O_EXCL|os.O_CREATE|os.O_WRONLY, filePerm)
	if err != nil {
		return
	}
	defer fDst.Close()
	hasher := sha1.New()
	r := io.TeeReader(io.LimitReader(fSrc, p.Length()), hasher)
	_, err = io.Copy(fDst, r)
	if err != nil {
		return
	}
	if !bytes.Equal(hasher.Sum(nil), p.Hash()) {
		err = errors.New("piece incomplete")
		os.Remove(completedPiecePath)
		return
	}
	os.Remove(incompletePiecePath)
	me.mu.Lock()
	me.completed[sliceToPieceHashArray(p.Hash())] = struct{}{}
	me.mu.Unlock()
	return
}
<|endoftext|>


<commit_before>// Copyright 2014 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 graphics

import (
	"fmt"

	"github.com/hajimehoshi/ebiten/internal/affine"
	"github.com/hajimehoshi/ebiten/internal/opengl"
)

type arrayBufferLayoutPart struct {
	// TODO: This struct should belong to a program and know it.
	name      string
	dataType  opengl.DataType
	num       int
	normalize bool
}

type arrayBufferLayout struct {
	parts []arrayBufferLayoutPart
	total int
}

func (a *arrayBufferLayout) totalBytes() int {
	if a.total != 0 {
		return a.total
	}
	t := 0
	for _, p := range a.parts {
		t += p.dataType.SizeInBytes() * p.num
	}
	a.total = t
	return a.total
}

func (a *arrayBufferLayout) newArrayBuffer(c *opengl.Context) opengl.Buffer {
	return c.NewBuffer(opengl.ArrayBuffer, a.totalBytes()*4*maxQuads, opengl.DynamicDraw)
}

func (a *arrayBufferLayout) enable(c *opengl.Context, program opengl.Program) {
	for _, p := range a.parts {
		c.EnableVertexAttribArray(program, p.name)
	}
	total := a.totalBytes()
	offset := 0
	for _, p := range a.parts {
		c.VertexAttribPointer(program, p.name, p.num, p.dataType, p.normalize, total, offset)
		offset += p.dataType.SizeInBytes() * p.num
	}
}

func (a *arrayBufferLayout) disable(c *opengl.Context, program opengl.Program) {
	// TODO: Disabling should be done in reversed order?
	for _, p := range a.parts {
		c.DisableVertexAttribArray(program, p.name)
	}
}

var (
	theArrayBufferLayout = arrayBufferLayout{
		// Note that GL_MAX_VERTEX_ATTRIBS is at least 16.
		parts: []arrayBufferLayoutPart{
			{
				name:      "vertex",
				dataType:  opengl.Float,
				num:       2,
				normalize: false,
			},
			{
				name:      "tex_coord",
				dataType:  opengl.Float,
				num:       2,
				normalize: false,
			},
			{
				name:      "geo_matrix_body",
				dataType:  opengl.Float,
				num:       4,
				normalize: false,
			},
			{
				name:      "geo_matrix_translation",
				dataType:  opengl.Float,
				num:       2,
				normalize: false,
			},
		},
	}
)

type openGLState struct {
	arrayBuffer      opengl.Buffer
	indexBufferQuads opengl.Buffer
	programTexture   opengl.Program

	lastProgram                opengl.Program
	lastProjectionMatrix       []float32
	lastColorMatrix            []float32
	lastColorMatrixTranslation []float32
}

var (
	theOpenGLState openGLState

	zeroBuffer  opengl.Buffer
	zeroProgram opengl.Program
	zeroTexture opengl.Texture
)

const (
	indicesNum = 1 << 16
	maxQuads   = indicesNum / 6
)

func Reset(context *opengl.Context) error {
	return theOpenGLState.reset(context)
}

func (s *openGLState) reset(context *opengl.Context) error {
	if err := context.Reset(); err != nil {
		return err
	}
	s.lastProgram = zeroProgram
	s.lastProjectionMatrix = nil
	s.lastColorMatrix = nil
	s.lastColorMatrixTranslation = nil

	if s.arrayBuffer != zeroBuffer {
		context.DeleteBuffer(s.arrayBuffer)
	}
	if s.indexBufferQuads != zeroBuffer {
		context.DeleteBuffer(s.indexBufferQuads)
	}
	if s.programTexture != zeroProgram {
		context.DeleteProgram(s.programTexture)
	}

	shaderVertexModelviewNative, err := context.NewShader(opengl.VertexShader, shader(context, shaderVertexModelview))
	if err != nil {
		panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
	}
	defer context.DeleteShader(shaderVertexModelviewNative)

	shaderFragmentTextureNative, err := context.NewShader(opengl.FragmentShader, shader(context, shaderFragmentTexture))
	if err != nil {
		panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
	}
	defer context.DeleteShader(shaderFragmentTextureNative)

	s.programTexture, err = context.NewProgram([]opengl.Shader{
		shaderVertexModelviewNative,
		shaderFragmentTextureNative,
	})
	if err != nil {
		return err
	}

	s.arrayBuffer = theArrayBufferLayout.newArrayBuffer(context)

	indices := make([]uint16, 6*maxQuads)
	for i := uint16(0); i < maxQuads; i++ {
		indices[6*i+0] = 4*i + 0
		indices[6*i+1] = 4*i + 1
		indices[6*i+2] = 4*i + 2
		indices[6*i+3] = 4*i + 1
		indices[6*i+4] = 4*i + 2
		indices[6*i+5] = 4*i + 3
	}
	s.indexBufferQuads = context.NewBuffer(opengl.ElementArrayBuffer, indices, opengl.StaticDraw)

	return nil
}

func areSameFloat32Array(a, b []float32) bool {
	if len(a) != len(b) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

type programContext struct {
	state            *openGLState
	program          opengl.Program
	context          *opengl.Context
	projectionMatrix []float32
	texture          opengl.Texture
	colorM           affine.ColorM
}

func (p *programContext) begin() error {
	c := p.context
	if p.state.lastProgram != p.program {
		c.UseProgram(p.program)
		if p.state.lastProgram != zeroProgram {
			theArrayBufferLayout.disable(c, p.state.lastProgram)
		}
		theArrayBufferLayout.enable(c, p.program)

		p.state.lastProgram = p.state.programTexture
		p.state.lastProjectionMatrix = nil
		p.state.lastColorMatrix = nil
		p.state.lastColorMatrixTranslation = nil
		c.BindElementArrayBuffer(p.state.indexBufferQuads)
		c.UniformInt(p.program, "texture", 0)
	}

	if !areSameFloat32Array(p.state.lastProjectionMatrix, p.projectionMatrix) {
		c.UniformFloats(p.program, "projection_matrix", p.projectionMatrix)
		if p.state.lastProjectionMatrix == nil {
			p.state.lastProjectionMatrix = make([]float32, 16)
		}
		copy(p.state.lastProjectionMatrix, p.projectionMatrix)
	}

	e := [4][5]float32{}
	es := p.colorM.UnsafeElements()
	for i := 0; i < 4; i++ {
		for j := 0; j < 5; j++ {
			e[i][j] = float32(es[i*affine.ColorMDim+j])
		}
	}

	colorMatrix := []float32{
		e[0][0], e[1][0], e[2][0], e[3][0],
		e[0][1], e[1][1], e[2][1], e[3][1],
		e[0][2], e[1][2], e[2][2], e[3][2],
		e[0][3], e[1][3], e[2][3], e[3][3],
	}
	if !areSameFloat32Array(p.state.lastColorMatrix, colorMatrix) {
		c.UniformFloats(p.program, "color_matrix", colorMatrix)
		if p.state.lastColorMatrix == nil {
			p.state.lastColorMatrix = make([]float32, 16)
		}
		copy(p.state.lastColorMatrix, colorMatrix)
	}
	colorMatrixTranslation := []float32{
		e[0][4], e[1][4], e[2][4], e[3][4],
	}
	if !areSameFloat32Array(p.state.lastColorMatrixTranslation, colorMatrixTranslation) {
		c.UniformFloats(p.program, "color_matrix_translation", colorMatrixTranslation)
		if p.state.lastColorMatrixTranslation == nil {
			p.state.lastColorMatrixTranslation = make([]float32, 4)
		}
		copy(p.state.lastColorMatrixTranslation, colorMatrixTranslation)
	}

	// 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
	if err := c.BindTexture(p.texture); err != nil {
		return err
	}
	return nil
}
<commit_msg>graphics: Bug fix: Can't delete object after GL context is restored<commit_after>// Copyright 2014 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 graphics

import (
	"fmt"

	"github.com/hajimehoshi/ebiten/internal/affine"
	"github.com/hajimehoshi/ebiten/internal/opengl"
)

type arrayBufferLayoutPart struct {
	// TODO: This struct should belong to a program and know it.
	name      string
	dataType  opengl.DataType
	num       int
	normalize bool
}

type arrayBufferLayout struct {
	parts []arrayBufferLayoutPart
	total int
}

func (a *arrayBufferLayout) totalBytes() int {
	if a.total != 0 {
		return a.total
	}
	t := 0
	for _, p := range a.parts {
		t += p.dataType.SizeInBytes() * p.num
	}
	a.total = t
	return a.total
}

func (a *arrayBufferLayout) newArrayBuffer(c *opengl.Context) opengl.Buffer {
	return c.NewBuffer(opengl.ArrayBuffer, a.totalBytes()*4*maxQuads, opengl.DynamicDraw)
}

func (a *arrayBufferLayout) enable(c *opengl.Context, program opengl.Program) {
	for _, p := range a.parts {
		c.EnableVertexAttribArray(program, p.name)
	}
	total := a.totalBytes()
	offset := 0
	for _, p := range a.parts {
		c.VertexAttribPointer(program, p.name, p.num, p.dataType, p.normalize, total, offset)
		offset += p.dataType.SizeInBytes() * p.num
	}
}

func (a *arrayBufferLayout) disable(c *opengl.Context, program opengl.Program) {
	// TODO: Disabling should be done in reversed order?
	for _, p := range a.parts {
		c.DisableVertexAttribArray(program, p.name)
	}
}

var (
	theArrayBufferLayout = arrayBufferLayout{
		// Note that GL_MAX_VERTEX_ATTRIBS is at least 16.
		parts: []arrayBufferLayoutPart{
			{
				name:      "vertex",
				dataType:  opengl.Float,
				num:       2,
				normalize: false,
			},
			{
				name:      "tex_coord",
				dataType:  opengl.Float,
				num:       2,
				normalize: false,
			},
			{
				name:      "geo_matrix_body",
				dataType:  opengl.Float,
				num:       4,
				normalize: false,
			},
			{
				name:      "geo_matrix_translation",
				dataType:  opengl.Float,
				num:       2,
				normalize: false,
			},
		},
	}
)

type openGLState struct {
	arrayBuffer      opengl.Buffer
	indexBufferQuads opengl.Buffer
	programTexture   opengl.Program

	lastProgram                opengl.Program
	lastProjectionMatrix       []float32
	lastColorMatrix            []float32
	lastColorMatrixTranslation []float32
}

var (
	theOpenGLState openGLState

	zeroBuffer  opengl.Buffer
	zeroProgram opengl.Program
	zeroTexture opengl.Texture
)

const (
	indicesNum = 1 << 16
	maxQuads   = indicesNum / 6
)

func Reset(context *opengl.Context) error {
	return theOpenGLState.reset(context)
}

func (s *openGLState) reset(context *opengl.Context) error {
	if err := context.Reset(); err != nil {
		return err
	}
	s.lastProgram = zeroProgram
	s.lastProjectionMatrix = nil
	s.lastColorMatrix = nil
	s.lastColorMatrixTranslation = nil

	shaderVertexModelviewNative, err := context.NewShader(opengl.VertexShader, shader(context, shaderVertexModelview))
	if err != nil {
		panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
	}
	defer context.DeleteShader(shaderVertexModelviewNative)

	shaderFragmentTextureNative, err := context.NewShader(opengl.FragmentShader, shader(context, shaderFragmentTexture))
	if err != nil {
		panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
	}
	defer context.DeleteShader(shaderFragmentTextureNative)

	s.programTexture, err = context.NewProgram([]opengl.Shader{
		shaderVertexModelviewNative,
		shaderFragmentTextureNative,
	})
	if err != nil {
		return err
	}

	s.arrayBuffer = theArrayBufferLayout.newArrayBuffer(context)

	indices := make([]uint16, 6*maxQuads)
	for i := uint16(0); i < maxQuads; i++ {
		indices[6*i+0] = 4*i + 0
		indices[6*i+1] = 4*i + 1
		indices[6*i+2] = 4*i + 2
		indices[6*i+3] = 4*i + 1
		indices[6*i+4] = 4*i + 2
		indices[6*i+5] = 4*i + 3
	}
	s.indexBufferQuads = context.NewBuffer(opengl.ElementArrayBuffer, indices, opengl.StaticDraw)

	return nil
}

func areSameFloat32Array(a, b []float32) bool {
	if len(a) != len(b) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

type programContext struct {
	state            *openGLState
	program          opengl.Program
	context          *opengl.Context
	projectionMatrix []float32
	texture          opengl.Texture
	colorM           affine.ColorM
}

func (p *programContext) begin() error {
	c := p.context
	if p.state.lastProgram != p.program {
		c.UseProgram(p.program)
		if p.state.lastProgram != zeroProgram {
			theArrayBufferLayout.disable(c, p.state.lastProgram)
		}
		theArrayBufferLayout.enable(c, p.program)

		p.state.lastProgram = p.state.programTexture
		p.state.lastProjectionMatrix = nil
		p.state.lastColorMatrix = nil
		p.state.lastColorMatrixTranslation = nil
		c.BindElementArrayBuffer(p.state.indexBufferQuads)
		c.UniformInt(p.program, "texture", 0)
	}

	if !areSameFloat32Array(p.state.lastProjectionMatrix, p.projectionMatrix) {
		c.UniformFloats(p.program, "projection_matrix", p.projectionMatrix)
		if p.state.lastProjectionMatrix == nil {
			p.state.lastProjectionMatrix = make([]float32, 16)
		}
		copy(p.state.lastProjectionMatrix, p.projectionMatrix)
	}

	e := [4][5]float32{}
	es := p.colorM.UnsafeElements()
	for i := 0; i < 4; i++ {
		for j := 0; j < 5; j++ {
			e[i][j] = float32(es[i*affine.ColorMDim+j])
		}
	}

	colorMatrix := []float32{
		e[0][0], e[1][0], e[2][0], e[3][0],
		e[0][1], e[1][1], e[2][1], e[3][1],
		e[0][2], e[1][2], e[2][2], e[3][2],
		e[0][3], e[1][3], e[2][3], e[3][3],
	}
	if !areSameFloat32Array(p.state.lastColorMatrix, colorMatrix) {
		c.UniformFloats(p.program, "color_matrix", colorMatrix)
		if p.state.lastColorMatrix == nil {
			p.state.lastColorMatrix = make([]float32, 16)
		}
		copy(p.state.lastColorMatrix, colorMatrix)
	}
	colorMatrixTranslation := []float32{
		e[0][4], e[1][4], e[2][4], e[3][4],
	}
	if !areSameFloat32Array(p.state.lastColorMatrixTranslation, colorMatrixTranslation) {
		c.UniformFloats(p.program, "color_matrix_translation", colorMatrixTranslation)
		if p.state.lastColorMatrixTranslation == nil {
			p.state.lastColorMatrixTranslation = make([]float32, 4)
		}
		copy(p.state.lastColorMatrixTranslation, colorMatrixTranslation)
	}

	// 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
	if err := c.BindTexture(p.texture); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Ebiten Authors
//
// 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 restorable

import (
	"errors"
	"image"
	"image/color"
	"runtime"

	"github.com/hajimehoshi/ebiten/internal/affine"
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/math"
	"github.com/hajimehoshi/ebiten/internal/opengl"
)

// MaxImageSize represents the maximum width/height of an image.
const MaxImageSize = graphics.MaxImageSize

// QuadVertexSizeInBytes returns the byte size of vertices for a quadrilateral.
func QuadVertexSizeInBytes() int {
	return graphics.QuadVertexSizeInBytes()
}

// drawImageHistoryItem is an item for history of draw-image commands.
type drawImageHistoryItem struct {
	image    *Image
	vertices []float32
	colorm   affine.ColorM
	mode     opengl.CompositeMode
	filter   graphics.Filter
}

// canMerge returns a boolean value indicating whether the drawImageHistoryItem d
// can be merged with the given conditions.
func (d *drawImageHistoryItem) canMerge(image *Image, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) bool {
	if d.image != image {
		return false
	}
	if !d.colorm.Equals(colorm) {
		return false
	}
	if d.mode != mode {
		return false
	}
	if d.filter != filter {
		return false
	}
	return true
}

// Image represents an image that can be restored when GL context is lost.
type Image struct {
	image *graphics.Image

	basePixels []byte

	// drawImageHistory is a set of draw-image commands.
	// TODO: This should be merged with the similar command queue in package graphics (#433).
	drawImageHistory []*drawImageHistoryItem

	// stale indicates whether the image needs to be synced with GPU as soon as possible.
	stale bool

	// volatile indicates whether the image is cleared whenever a frame starts.
	volatile bool

	// screen indicates whether the image is used as an actual screen.
	screen bool

	offsetX float64
	offsetY float64
}

// NewImage creates an empty image with the given size.
func NewImage(width, height int, volatile bool) *Image {
	i := &Image{
		image:    graphics.NewImage(width, height),
		volatile: volatile,
	}
	theImages.add(i)
	runtime.SetFinalizer(i, (*Image).Dispose)
	return i
}

// NewImageFromImage creates an image with source image.
func NewImageFromImage(source image.Image) *Image {
	size := source.Bounds().Size()
	width, height := size.X, size.Y
	rgbaImg := CopyImage(source)
	w2, h2 := math.NextPowerOf2Int(width), math.NextPowerOf2Int(height)
	p := make([]byte, 4*w2*h2)
	for j := 0; j < height; j++ {
		copy(p[j*w2*4:(j+1)*w2*4], rgbaImg.Pix[j*rgbaImg.Stride:])
	}
	i := &Image{
		image:      graphics.NewImageFromImage(rgbaImg, width, height),
		basePixels: p,
	}
	theImages.add(i)
	runtime.SetFinalizer(i, (*Image).Dispose)
	return i
}

// NewScreenFramebufferImage creates a special image that framebuffer is one for the screen.
func NewScreenFramebufferImage(width, height int, offsetX, offsetY float64) *Image {
	i := &Image{
		image:    graphics.NewScreenFramebufferImage(width, height, offsetX, offsetY),
		volatile: true,
		screen:   true,
		offsetX:  offsetX,
		offsetY:  offsetY,
	}
	theImages.add(i)
	runtime.SetFinalizer(i, (*Image).Dispose)
	return i
}

// BasePixelsForTesting returns the image's basePixels for testing.
func (i *Image) BasePixelsForTesting() []byte {
	return i.basePixels
}

// Size returns the image's size.
func (i *Image) Size() (int, int) {
	return i.image.Size()
}

// makeStale makes the image stale.
func (i *Image) makeStale() {
	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = true
}

var (
	dummyImage  = graphics.NewImage(16, 16)
	clearColorM = &affine.ColorM{}
)

func init() {
	clearColorM.Scale(0, 0, 0, 0)
}

// clearIfVolatile clears the image if the image is volatile.
func (i *Image) clearIfVolatile() {
	if !i.volatile {
		return
	}
	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = false
	if i.image == nil {
		panic("not reached")
	}

	w, h := i.image.Size()
	wf, hf := float32(w), float32(h)
	// For the rule of values, see vertices.go.
	clearVertices := []float32{
		0, 0, 0, 0, 1, 1,
		wf, 0, 1, 0, 0, 1,
		0, hf, 0, 1, 1, 0,
		wf, hf, 1, 1, 0, 0,
	}
	i.image.DrawImage(dummyImage, clearVertices, clearColorM, opengl.CompositeModeCopy, graphics.FilterNearest)
}

// ReplacePixels replaces the image pixels with the given pixels slice.
func (i *Image) ReplacePixels(pixels []byte) {
	theImages.makeStaleIfDependingOn(i)
	i.image.ReplacePixels(pixels)
	i.basePixels = pixels
	i.drawImageHistory = nil
	i.stale = false
}

// DrawImage draws a given image img to the image.
func (i *Image) DrawImage(img *Image, vertices []float32, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) {
	theImages.makeStaleIfDependingOn(i)
	if img.stale || img.volatile || !IsRestoringEnabled() {
		i.makeStale()
	} else {
		i.appendDrawImageHistory(img, vertices, colorm, mode, filter)
	}
	i.image.DrawImage(img.image, vertices, colorm, mode, filter)
}

// appendDrawImageHistory appends a draw-image history item to the image.
func (i *Image) appendDrawImageHistory(image *Image, vertices []float32, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) {
	if i.stale || i.volatile {
		return
	}
	if len(i.drawImageHistory) > 0 {
		last := i.drawImageHistory[len(i.drawImageHistory)-1]
		if last.canMerge(image, colorm, mode, filter) {
			last.vertices = append(last.vertices, vertices...)
			return
		}
	}
	const maxDrawImageHistoryNum = 100
	if len(i.drawImageHistory)+1 > maxDrawImageHistoryNum {
		i.makeStale()
		return
	}
	// All images must be resolved and not stale each after frame.
	// So we don't have to care if image is stale or not here.
	item := &drawImageHistoryItem{
		image:    image,
		vertices: vertices,
		colorm:   *colorm,
		mode:     mode,
		filter:   filter,
	}
	i.drawImageHistory = append(i.drawImageHistory, item)
}

// At returns a color value at (x, y).
//
// Note that this must not be called until context is available.
func (i *Image) At(x, y int) (color.RGBA, error) {
	w, h := i.image.Size()
	w2, h2 := math.NextPowerOf2Int(w), math.NextPowerOf2Int(h)
	if x < 0 || y < 0 || w2 <= x || h2 <= y {
		return color.RGBA{}, nil
	}
	if i.basePixels == nil || i.drawImageHistory != nil || i.stale {
		if err := i.readPixelsFromGPU(i.image); err != nil {
			return color.RGBA{}, err
		}
	}
	idx := 4*x + 4*y*w2
	r, g, b, a := i.basePixels[idx], i.basePixels[idx+1], i.basePixels[idx+2], i.basePixels[idx+3]
	return color.RGBA{r, g, b, a}, nil
}

// makeStaleIfDependingOn makes the image stale if the image depends on target.
func (i *Image) makeStaleIfDependingOn(target *Image) {
	if i.stale {
		return
	}
	if i.dependsOn(target) {
		i.makeStale()
	}
}

// readPixelsFromGPU reads the pixels from GPU and resolves the image's 'stale' state.
func (i *Image) readPixelsFromGPU(image *graphics.Image) error {
	var err error
	i.basePixels, err = image.Pixels()
	if err != nil {
		return err
	}
	i.drawImageHistory = nil
	i.stale = false
	return nil
}

// resolveStale resolves the image's 'stale' state.
func (i *Image) resolveStale() error {
	if !IsRestoringEnabled() {
		return nil
	}
	if i.volatile {
		return nil
	}
	if !i.stale {
		return nil
	}
	return i.readPixelsFromGPU(i.image)
}

// dependsOn returns a boolean value indicating whether the image depends on target.
func (i *Image) dependsOn(target *Image) bool {
	for _, c := range i.drawImageHistory {
		if c.image == target {
			return true
		}
	}
	return false
}

// dependingImages returns all images that is depended by the image.
func (i *Image) dependingImages() map[*Image]struct{} {
	r := map[*Image]struct{}{}
	for _, c := range i.drawImageHistory {
		r[c.image] = struct{}{}
	}
	return r
}

// hasDependency returns a boolean value indicating whether the image depends on another image.
func (i *Image) hasDependency() bool {
	if i.stale {
		return false
	}
	return len(i.drawImageHistory) > 0
}

// Restore restores *graphics.Image from the pixels using its state.
func (i *Image) restore() error {
	w, h := i.image.Size()
	if i.screen {
		// The screen image should also be recreated because framebuffer might
		// be changed.
		i.image = graphics.NewScreenFramebufferImage(w, h, i.offsetX, i.offsetY)
		i.basePixels = nil
		i.drawImageHistory = nil
		i.stale = false
		return nil
	}
	if i.volatile {
		i.image = graphics.NewImage(w, h)
		i.basePixels = nil
		i.drawImageHistory = nil
		i.stale = false
		return nil
	}
	if i.stale {
		// TODO: panic here?
		return errors.New("restorable: pixels must not be stale when restoring")
	}
	w2, h2 := math.NextPowerOf2Int(w), math.NextPowerOf2Int(h)
	img := image.NewRGBA(image.Rect(0, 0, w2, h2))
	if i.basePixels != nil {
		for j := 0; j < h; j++ {
			copy(img.Pix[j*img.Stride:], i.basePixels[j*w2*4:(j+1)*w2*4])
		}
	}
	gimg := graphics.NewImageFromImage(img, w, h)
	for _, c := range i.drawImageHistory {
		// All dependencies must be already resolved.
		if c.image.hasDependency() {
			panic("not reached")
		}
		gimg.DrawImage(c.image.image, c.vertices, &c.colorm, c.mode, c.filter)
	}
	i.image = gimg

	var err error
	i.basePixels, err = gimg.Pixels()
	if err != nil {
		return err
	}
	i.drawImageHistory = nil
	i.stale = false
	return nil
}

// Dispose disposes the image.
//
// After disposing, calling the function of the image causes unexpected results.
func (i *Image) Dispose() {
	theImages.makeStaleIfDependingOn(i)
	i.image.Dispose()
	i.image = nil
	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = false
	theImages.remove(i)
	runtime.SetFinalizer(i, nil)
}

// IsInvalidated returns a boolean value indicating whether the image is invalidated.
//
// If an image is invalidated, GL context is lost and all the images should be restored asap.
func (i *Image) IsInvalidated() (bool, error) {
	// FlushCommands is required because c.offscreen.impl might not have an actual texture.
	if err := graphics.FlushCommands(); err != nil {
		return false, err
	}

	if !IsRestoringEnabled() {
		return false, nil
	}
	return i.image.IsInvalidated(), nil
}
<commit_msg>restorable: Bug fix: Clear screen framebuffer on fullscreen mode<commit_after>// Copyright 2016 The Ebiten Authors
//
// 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 restorable

import (
	"errors"
	"image"
	"image/color"
	"runtime"

	"github.com/hajimehoshi/ebiten/internal/affine"
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/math"
	"github.com/hajimehoshi/ebiten/internal/opengl"
)

// MaxImageSize represents the maximum width/height of an image.
const MaxImageSize = graphics.MaxImageSize

// QuadVertexSizeInBytes returns the byte size of vertices for a quadrilateral.
func QuadVertexSizeInBytes() int {
	return graphics.QuadVertexSizeInBytes()
}

// drawImageHistoryItem is an item for history of draw-image commands.
type drawImageHistoryItem struct {
	image    *Image
	vertices []float32
	colorm   affine.ColorM
	mode     opengl.CompositeMode
	filter   graphics.Filter
}

// canMerge returns a boolean value indicating whether the drawImageHistoryItem d
// can be merged with the given conditions.
func (d *drawImageHistoryItem) canMerge(image *Image, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) bool {
	if d.image != image {
		return false
	}
	if !d.colorm.Equals(colorm) {
		return false
	}
	if d.mode != mode {
		return false
	}
	if d.filter != filter {
		return false
	}
	return true
}

// Image represents an image that can be restored when GL context is lost.
type Image struct {
	image *graphics.Image

	basePixels []byte

	// drawImageHistory is a set of draw-image commands.
	// TODO: This should be merged with the similar command queue in package graphics (#433).
	drawImageHistory []*drawImageHistoryItem

	// stale indicates whether the image needs to be synced with GPU as soon as possible.
	stale bool

	// volatile indicates whether the image is cleared whenever a frame starts.
	volatile bool

	// screen indicates whether the image is used as an actual screen.
	screen bool

	offsetX float64
	offsetY float64
}

// NewImage creates an empty image with the given size.
func NewImage(width, height int, volatile bool) *Image {
	i := &Image{
		image:    graphics.NewImage(width, height),
		volatile: volatile,
	}
	theImages.add(i)
	runtime.SetFinalizer(i, (*Image).Dispose)
	return i
}

// NewImageFromImage creates an image with source image.
func NewImageFromImage(source image.Image) *Image {
	size := source.Bounds().Size()
	width, height := size.X, size.Y
	rgbaImg := CopyImage(source)
	w2, h2 := math.NextPowerOf2Int(width), math.NextPowerOf2Int(height)
	p := make([]byte, 4*w2*h2)
	for j := 0; j < height; j++ {
		copy(p[j*w2*4:(j+1)*w2*4], rgbaImg.Pix[j*rgbaImg.Stride:])
	}
	i := &Image{
		image:      graphics.NewImageFromImage(rgbaImg, width, height),
		basePixels: p,
	}
	theImages.add(i)
	runtime.SetFinalizer(i, (*Image).Dispose)
	return i
}

// NewScreenFramebufferImage creates a special image that framebuffer is one for the screen.
func NewScreenFramebufferImage(width, height int, offsetX, offsetY float64) *Image {
	i := &Image{
		image:    graphics.NewScreenFramebufferImage(width, height, offsetX, offsetY),
		volatile: true,
		screen:   true,
		offsetX:  offsetX,
		offsetY:  offsetY,
	}
	theImages.add(i)
	runtime.SetFinalizer(i, (*Image).Dispose)
	return i
}

// BasePixelsForTesting returns the image's basePixels for testing.
func (i *Image) BasePixelsForTesting() []byte {
	return i.basePixels
}

// Size returns the image's size.
func (i *Image) Size() (int, int) {
	return i.image.Size()
}

// makeStale makes the image stale.
func (i *Image) makeStale() {
	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = true
}

var (
	dummyImage  = graphics.NewImage(16, 16)
	clearColorM = &affine.ColorM{}
)

func init() {
	clearColorM.Scale(0, 0, 0, 0)
}

// clearIfVolatile clears the image if the image is volatile.
func (i *Image) clearIfVolatile() {
	if !i.volatile {
		return
	}
	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = false
	if i.image == nil {
		panic("not reached")
	}

	w, h := i.image.Size()
	x0 := -float32(i.offsetX)
	y0 := -float32(i.offsetY)
	// TODO: This is an ad-hoc way to fix the problem #513.
	// Fix this for better calculation.
	x1 := float32(w) + float32(i.offsetX)
	y1 := float32(h) + float32(i.offsetY)
	// For the rule of values, see vertices.go.
	clearVertices := []float32{
		x0, y0, 0, 0, 1, 1,
		x1, y0, 1, 0, 0, 1,
		x0, y1, 0, 1, 1, 0,
		x1, y1, 1, 1, 0, 0,
	}
	i.image.DrawImage(dummyImage, clearVertices, clearColorM, opengl.CompositeModeCopy, graphics.FilterNearest)
}

// ReplacePixels replaces the image pixels with the given pixels slice.
func (i *Image) ReplacePixels(pixels []byte) {
	theImages.makeStaleIfDependingOn(i)
	i.image.ReplacePixels(pixels)
	i.basePixels = pixels
	i.drawImageHistory = nil
	i.stale = false
}

// DrawImage draws a given image img to the image.
func (i *Image) DrawImage(img *Image, vertices []float32, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) {
	theImages.makeStaleIfDependingOn(i)
	if img.stale || img.volatile || !IsRestoringEnabled() {
		i.makeStale()
	} else {
		i.appendDrawImageHistory(img, vertices, colorm, mode, filter)
	}
	i.image.DrawImage(img.image, vertices, colorm, mode, filter)
}

// appendDrawImageHistory appends a draw-image history item to the image.
func (i *Image) appendDrawImageHistory(image *Image, vertices []float32, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) {
	if i.stale || i.volatile {
		return
	}
	if len(i.drawImageHistory) > 0 {
		last := i.drawImageHistory[len(i.drawImageHistory)-1]
		if last.canMerge(image, colorm, mode, filter) {
			last.vertices = append(last.vertices, vertices...)
			return
		}
	}
	const maxDrawImageHistoryNum = 100
	if len(i.drawImageHistory)+1 > maxDrawImageHistoryNum {
		i.makeStale()
		return
	}
	// All images must be resolved and not stale each after frame.
	// So we don't have to care if image is stale or not here.
	item := &drawImageHistoryItem{
		image:    image,
		vertices: vertices,
		colorm:   *colorm,
		mode:     mode,
		filter:   filter,
	}
	i.drawImageHistory = append(i.drawImageHistory, item)
}

// At returns a color value at (x, y).
//
// Note that this must not be called until context is available.
func (i *Image) At(x, y int) (color.RGBA, error) {
	w, h := i.image.Size()
	w2, h2 := math.NextPowerOf2Int(w), math.NextPowerOf2Int(h)
	if x < 0 || y < 0 || w2 <= x || h2 <= y {
		return color.RGBA{}, nil
	}
	if i.basePixels == nil || i.drawImageHistory != nil || i.stale {
		if err := i.readPixelsFromGPU(i.image); err != nil {
			return color.RGBA{}, err
		}
	}
	idx := 4*x + 4*y*w2
	r, g, b, a := i.basePixels[idx], i.basePixels[idx+1], i.basePixels[idx+2], i.basePixels[idx+3]
	return color.RGBA{r, g, b, a}, nil
}

// makeStaleIfDependingOn makes the image stale if the image depends on target.
func (i *Image) makeStaleIfDependingOn(target *Image) {
	if i.stale {
		return
	}
	if i.dependsOn(target) {
		i.makeStale()
	}
}

// readPixelsFromGPU reads the pixels from GPU and resolves the image's 'stale' state.
func (i *Image) readPixelsFromGPU(image *graphics.Image) error {
	var err error
	i.basePixels, err = image.Pixels()
	if err != nil {
		return err
	}
	i.drawImageHistory = nil
	i.stale = false
	return nil
}

// resolveStale resolves the image's 'stale' state.
func (i *Image) resolveStale() error {
	if !IsRestoringEnabled() {
		return nil
	}
	if i.volatile {
		return nil
	}
	if !i.stale {
		return nil
	}
	return i.readPixelsFromGPU(i.image)
}

// dependsOn returns a boolean value indicating whether the image depends on target.
func (i *Image) dependsOn(target *Image) bool {
	for _, c := range i.drawImageHistory {
		if c.image == target {
			return true
		}
	}
	return false
}

// dependingImages returns all images that is depended by the image.
func (i *Image) dependingImages() map[*Image]struct{} {
	r := map[*Image]struct{}{}
	for _, c := range i.drawImageHistory {
		r[c.image] = struct{}{}
	}
	return r
}

// hasDependency returns a boolean value indicating whether the image depends on another image.
func (i *Image) hasDependency() bool {
	if i.stale {
		return false
	}
	return len(i.drawImageHistory) > 0
}

// Restore restores *graphics.Image from the pixels using its state.
func (i *Image) restore() error {
	w, h := i.image.Size()
	if i.screen {
		// The screen image should also be recreated because framebuffer might
		// be changed.
		i.image = graphics.NewScreenFramebufferImage(w, h, i.offsetX, i.offsetY)
		i.basePixels = nil
		i.drawImageHistory = nil
		i.stale = false
		return nil
	}
	if i.volatile {
		i.image = graphics.NewImage(w, h)
		i.basePixels = nil
		i.drawImageHistory = nil
		i.stale = false
		return nil
	}
	if i.stale {
		// TODO: panic here?
		return errors.New("restorable: pixels must not be stale when restoring")
	}
	w2, h2 := math.NextPowerOf2Int(w), math.NextPowerOf2Int(h)
	img := image.NewRGBA(image.Rect(0, 0, w2, h2))
	if i.basePixels != nil {
		for j := 0; j < h; j++ {
			copy(img.Pix[j*img.Stride:], i.basePixels[j*w2*4:(j+1)*w2*4])
		}
	}
	gimg := graphics.NewImageFromImage(img, w, h)
	for _, c := range i.drawImageHistory {
		// All dependencies must be already resolved.
		if c.image.hasDependency() {
			panic("not reached")
		}
		gimg.DrawImage(c.image.image, c.vertices, &c.colorm, c.mode, c.filter)
	}
	i.image = gimg

	var err error
	i.basePixels, err = gimg.Pixels()
	if err != nil {
		return err
	}
	i.drawImageHistory = nil
	i.stale = false
	return nil
}

// Dispose disposes the image.
//
// After disposing, calling the function of the image causes unexpected results.
func (i *Image) Dispose() {
	theImages.makeStaleIfDependingOn(i)
	i.image.Dispose()
	i.image = nil
	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = false
	theImages.remove(i)
	runtime.SetFinalizer(i, nil)
}

// IsInvalidated returns a boolean value indicating whether the image is invalidated.
//
// If an image is invalidated, GL context is lost and all the images should be restored asap.
func (i *Image) IsInvalidated() (bool, error) {
	// FlushCommands is required because c.offscreen.impl might not have an actual texture.
	if err := graphics.FlushCommands(); err != nil {
		return false, err
	}

	if !IsRestoringEnabled() {
		return false, nil
	}
	return i.image.IsInvalidated(), nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Ebiten Authors
//
// 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 restorable

import (
	"errors"
	"fmt"
	"image/color"

	"github.com/hajimehoshi/ebiten/internal/affine"
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/graphicscommand"
)

// drawImageHistoryItem is an item for history of draw-image commands.
type drawImageHistoryItem struct {
	image    *Image
	vertices []float32
	indices  []uint16
	colorm   *affine.ColorM
	mode     graphics.CompositeMode
	filter   graphics.Filter
}

// Image represents an image that can be restored when GL context is lost.
type Image struct {
	image *graphicscommand.Image

	basePixels []byte

	// drawImageHistory is a set of draw-image commands.
	// TODO: This should be merged with the similar command queue in package graphics (#433).
	drawImageHistory []*drawImageHistoryItem

	// stale indicates whether the image needs to be synced with GPU as soon as possible.
	stale bool

	// volatile indicates whether the image is cleared whenever a frame starts.
	volatile bool

	// screen indicates whether the image is used as an actual screen.
	screen bool

	w2 int
	h2 int
}

var dummyImage = newImageWithoutInit(16, 16, false)

// newImageWithoutInit creates an image without initialization.
//
// Note that Dispose is not called automatically.
func newImageWithoutInit(width, height int, volatile bool) *Image {
	i := &Image{
		image:    graphicscommand.NewImage(width, height),
		volatile: volatile,
	}
	theImages.add(i)
	return i
}

// NewImage creates an empty image with the given size.
//
// The returned image is cleared.
//
// Note that Dispose is not called automatically.
func NewImage(width, height int, volatile bool) *Image {
	i := newImageWithoutInit(width, height, volatile)
	i.ReplacePixels(nil, 0, 0, width, height)
	return i
}

// NewScreenFramebufferImage creates a special image that framebuffer is one for the screen.
//
// The returned image is cleared.
//
// Note that Dispose is not called automatically.
func NewScreenFramebufferImage(width, height int) *Image {
	i := &Image{
		image:    graphicscommand.NewScreenFramebufferImage(width, height),
		volatile: false,
		screen:   true,
	}
	theImages.add(i)
	i.ReplacePixels(nil, 0, 0, width, height)
	return i
}

func (i *Image) IsVolatile() bool {
	return i.volatile
}

// BasePixelsForTesting returns the image's basePixels for testing.
func (i *Image) BasePixelsForTesting() []byte {
	return i.basePixels
}

// Size returns the image's size.
func (i *Image) Size() (int, int) {
	return i.image.Size()
}

// SizePowerOf2 returns the next power of 2 values for the size.
func (i *Image) SizePowerOf2() (int, int) {
	if i.w2 == 0 || i.h2 == 0 {
		w, h := i.image.Size()
		i.w2 = graphics.NextPowerOf2Int(w)
		i.h2 = graphics.NextPowerOf2Int(h)
	}
	return i.w2, i.h2
}

// makeStale makes the image stale.
func (i *Image) makeStale() {
	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = true

	// Don't have to call makeStale recursively here.
	// Restoring is done after topological sorting is done.
	// If an image depends on another stale image, this means that
	// the former image can be restored from the latest state of the latter image.
}

// ReplacePixels replaces the image pixels with the given pixels slice.
//
// If pixels is nil, ReplacePixels clears the specified reagion.
func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
	w, h := i.image.Size()
	if width <= 0 || height <= 0 {
		panic("restorable: width/height must be positive")
	}
	if x < 0 || y < 0 || w <= x || h <= y || x+width <= 0 || y+height <= 0 || w < x+width || h < y+height {
		panic(fmt.Sprintf("restorable: out of range x: %d, y: %d, width: %d, height: %d", x, y, width, height))
	}

	// TODO: Avoid making other images stale if possible. (#514)
	// For this purpuse, images should remember which part of that is used for DrawImage.
	theImages.makeStaleIfDependingOn(i)

	if pixels != nil {
		i.image.ReplacePixels(pixels, x, y, width, height)
	} else {
		// There are not 'drawImageHistoryItem's for this image and dummyImage.
		// This means dummyImage might not be restored yet when this image is restored.
		// However, that's ok since this image will be stale or have updated pixel data
		// and this image can be restored without dummyImage.
		w, h := dummyImage.Size()
		colorm := (*affine.ColorM)(nil).Scale(0, 0, 0, 0)
		vs := graphics.QuadVertices(w, h, 0, 0, w, h,
			float32(width)/float32(w), 0, 0, float32(height)/float32(h),
			float32(x), float32(y),
			1, 1, 1, 1)
		is := graphics.QuadIndices()
		i.image.DrawImage(dummyImage.image, vs, is, colorm, graphics.CompositeModeCopy, graphics.FilterNearest)
	}

	if x == 0 && y == 0 && width == w && height == h {
		if pixels != nil {
			if i.basePixels == nil {
				i.basePixels = make([]byte, 4*w*h)
			}
			copy(i.basePixels, pixels)
		} else {
			// If basePixels is nil, the restored pixels are cleared.
			// See restore() implementation.
			i.basePixels = nil
		}
		i.drawImageHistory = nil
		i.stale = false
		return
	}
	if i.basePixels == nil {
		i.makeStale()
		return
	}
	if len(i.drawImageHistory) > 0 {
		i.makeStale()
		return
	}
	idx := 4 * (y*w + x)
	if pixels != nil {
		for j := 0; j < height; j++ {
			copy(i.basePixels[idx:idx+4*width], pixels[4*j*width:4*(j+1)*width])
			idx += 4 * w
		}
	} else {
		zeros := make([]byte, 4*width)
		for j := 0; j < height; j++ {
			copy(i.basePixels[idx:idx+4*width], zeros)
			idx += 4 * w
		}
	}
	i.stale = false
}

// DrawImage draws a given image img to the image.
func (i *Image) DrawImage(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode graphics.CompositeMode, filter graphics.Filter) {
	if len(vertices) == 0 {
		return
	}
	theImages.makeStaleIfDependingOn(i)

	if img.stale || img.volatile || i.screen || !IsRestoringEnabled() {
		i.makeStale()
	} else {
		i.appendDrawImageHistory(img, vertices, indices, colorm, mode, filter)
	}
	i.image.DrawImage(img.image, vertices, indices, colorm, mode, filter)
}

// appendDrawImageHistory appends a draw-image history item to the image.
func (i *Image) appendDrawImageHistory(image *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode graphics.CompositeMode, filter graphics.Filter) {
	if i.stale || i.volatile || i.screen {
		return
	}
	const maxDrawImageHistoryNum = 100
	if len(i.drawImageHistory)+1 > maxDrawImageHistoryNum {
		i.makeStale()
		return
	}
	// All images must be resolved and not stale each after frame.
	// So we don't have to care if image is stale or not here.
	item := &drawImageHistoryItem{
		image:    image,
		vertices: vertices,
		indices:  indices,
		colorm:   colorm,
		mode:     mode,
		filter:   filter,
	}
	i.drawImageHistory = append(i.drawImageHistory, item)
}

// At returns a color value at (x, y).
//
// Note that this must not be called until context is available.
func (i *Image) At(x, y int) color.RGBA {
	w, h := i.image.Size()
	if x < 0 || y < 0 || w <= x || h <= y {
		return color.RGBA{}
	}

	if i.basePixels == nil || i.drawImageHistory != nil || i.stale {
		graphicscommand.FlushCommands()
		i.readPixelsFromGPU()
		i.drawImageHistory = nil
		i.stale = false
	}

	// Even after readPixelsFromGPU, basePixels might be nil when OpenGL error happens.
	if i.basePixels == nil {
		return color.RGBA{}
	}

	idx := 4*x + 4*y*w
	r, g, b, a := i.basePixels[idx], i.basePixels[idx+1], i.basePixels[idx+2], i.basePixels[idx+3]
	return color.RGBA{r, g, b, a}
}

// makeStaleIfDependingOn makes the image stale if the image depends on target.
func (i *Image) makeStaleIfDependingOn(target *Image) {
	if i.stale {
		return
	}
	if i.dependsOn(target) {
		i.makeStale()
	}
}

// readPixelsFromGPU reads the pixels from GPU and resolves the image's 'stale' state.
func (i *Image) readPixelsFromGPU() {
	i.basePixels = i.image.Pixels()
	i.drawImageHistory = nil
	i.stale = false
}

// resolveStale resolves the image's 'stale' state.
func (i *Image) resolveStale() {
	if !IsRestoringEnabled() {
		return
	}

	if i.volatile {
		return
	}
	if i.screen {
		return
	}
	if !i.stale {
		return
	}
	i.readPixelsFromGPU()
}

// dependsOn returns a boolean value indicating whether the image depends on target.
func (i *Image) dependsOn(target *Image) bool {
	for _, c := range i.drawImageHistory {
		if c.image == target {
			return true
		}
	}
	return false
}

// dependingImages returns all images that is depended by the image.
func (i *Image) dependingImages() map[*Image]struct{} {
	r := map[*Image]struct{}{}
	for _, c := range i.drawImageHistory {
		r[c.image] = struct{}{}
	}
	return r
}

// hasDependency returns a boolean value indicating whether the image depends on another image.
func (i *Image) hasDependency() bool {
	if i.stale {
		return false
	}
	return len(i.drawImageHistory) > 0
}

// Restore restores *graphicscommand.Image from the pixels using its state.
func (i *Image) restore() error {
	w, h := i.image.Size()
	if i.screen {
		// The screen image should also be recreated because framebuffer might
		// be changed.
		i.image = graphicscommand.NewScreenFramebufferImage(w, h)
		i.basePixels = nil
		i.drawImageHistory = nil
		i.stale = false
		return nil
	}
	if i.volatile {
		i.image = graphicscommand.NewImage(w, h)
		i.basePixels = nil
		i.drawImageHistory = nil
		i.stale = false
		return nil
	}
	if i.stale {
		// TODO: panic here?
		return errors.New("restorable: pixels must not be stale when restoring")
	}
	gimg := graphicscommand.NewImage(w, h)
	if i.basePixels != nil {
		gimg.ReplacePixels(i.basePixels, 0, 0, w, h)
	} else {
		// Clear the image explicitly.
		pix := make([]uint8, w*h*4)
		gimg.ReplacePixels(pix, 0, 0, w, h)
	}
	for _, c := range i.drawImageHistory {
		// All dependencies must be already resolved.
		if c.image.hasDependency() {
			panic("not reached")
		}
		gimg.DrawImage(c.image.image, c.vertices, c.indices, c.colorm, c.mode, c.filter)
	}
	i.image = gimg

	i.basePixels = gimg.Pixels()
	i.drawImageHistory = nil
	i.stale = false
	return nil
}

// Dispose disposes the image.
//
// After disposing, calling the function of the image causes unexpected results.
func (i *Image) Dispose() {
	theImages.remove(i)

	i.image.Dispose()
	i.image = nil
	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = false
}

// IsInvalidated returns a boolean value indicating whether the image is invalidated.
//
// If an image is invalidated, GL context is lost and all the images should be restored asap.
func (i *Image) IsInvalidated() (bool, error) {
	// FlushCommands is required because c.offscreen.impl might not have an actual texture.
	graphicscommand.FlushCommands()
	if !IsRestoringEnabled() {
		return false, nil
	}

	return i.image.IsInvalidated(), nil
}
<commit_msg>restorable: Refactoring<commit_after>// Copyright 2016 The Ebiten Authors
//
// 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 restorable

import (
	"errors"
	"fmt"
	"image/color"

	"github.com/hajimehoshi/ebiten/internal/affine"
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/graphicscommand"
)

// drawImageHistoryItem is an item for history of draw-image commands.
type drawImageHistoryItem struct {
	image    *Image
	vertices []float32
	indices  []uint16
	colorm   *affine.ColorM
	mode     graphics.CompositeMode
	filter   graphics.Filter
}

// Image represents an image that can be restored when GL context is lost.
type Image struct {
	image *graphicscommand.Image

	basePixels []byte

	// drawImageHistory is a set of draw-image commands.
	// TODO: This should be merged with the similar command queue in package graphics (#433).
	drawImageHistory []*drawImageHistoryItem

	// stale indicates whether the image needs to be synced with GPU as soon as possible.
	stale bool

	// volatile indicates whether the image is cleared whenever a frame starts.
	volatile bool

	// screen indicates whether the image is used as an actual screen.
	screen bool

	w2 int
	h2 int
}

var dummyImage = newImageWithoutInit(16, 16, false)

// newImageWithoutInit creates an image without initialization.
//
// Note that Dispose is not called automatically.
func newImageWithoutInit(width, height int, volatile bool) *Image {
	i := &Image{
		image:    graphicscommand.NewImage(width, height),
		volatile: volatile,
	}
	theImages.add(i)
	return i
}

// NewImage creates an empty image with the given size.
//
// The returned image is cleared.
//
// Note that Dispose is not called automatically.
func NewImage(width, height int, volatile bool) *Image {
	i := newImageWithoutInit(width, height, volatile)
	i.ReplacePixels(nil, 0, 0, width, height)
	return i
}

// NewScreenFramebufferImage creates a special image that framebuffer is one for the screen.
//
// The returned image is cleared.
//
// Note that Dispose is not called automatically.
func NewScreenFramebufferImage(width, height int) *Image {
	i := &Image{
		image:    graphicscommand.NewScreenFramebufferImage(width, height),
		volatile: false,
		screen:   true,
	}
	theImages.add(i)
	i.ReplacePixels(nil, 0, 0, width, height)
	return i
}

func (i *Image) IsVolatile() bool {
	return i.volatile
}

// BasePixelsForTesting returns the image's basePixels for testing.
func (i *Image) BasePixelsForTesting() []byte {
	return i.basePixels
}

// Size returns the image's size.
func (i *Image) Size() (int, int) {
	return i.image.Size()
}

// SizePowerOf2 returns the next power of 2 values for the size.
func (i *Image) SizePowerOf2() (int, int) {
	if i.w2 == 0 || i.h2 == 0 {
		w, h := i.image.Size()
		i.w2 = graphics.NextPowerOf2Int(w)
		i.h2 = graphics.NextPowerOf2Int(h)
	}
	return i.w2, i.h2
}

// makeStale makes the image stale.
func (i *Image) makeStale() {
	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = true

	// Don't have to call makeStale recursively here.
	// Restoring is done after topological sorting is done.
	// If an image depends on another stale image, this means that
	// the former image can be restored from the latest state of the latter image.
}

// ReplacePixels replaces the image pixels with the given pixels slice.
//
// If pixels is nil, ReplacePixels clears the specified reagion.
func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
	w, h := i.image.Size()
	if width <= 0 || height <= 0 {
		panic("restorable: width/height must be positive")
	}
	if x < 0 || y < 0 || w <= x || h <= y || x+width <= 0 || y+height <= 0 || w < x+width || h < y+height {
		panic(fmt.Sprintf("restorable: out of range x: %d, y: %d, width: %d, height: %d", x, y, width, height))
	}

	// TODO: Avoid making other images stale if possible. (#514)
	// For this purpuse, images should remember which part of that is used for DrawImage.
	theImages.makeStaleIfDependingOn(i)

	if pixels != nil {
		i.image.ReplacePixels(pixels, x, y, width, height)
	} else {
		// There are not 'drawImageHistoryItem's for this image and dummyImage.
		// This means dummyImage might not be restored yet when this image is restored.
		// However, that's ok since this image will be stale or have updated pixel data
		// and this image can be restored without dummyImage.
		w, h := dummyImage.Size()
		vs := graphics.QuadVertices(w, h, 0, 0, w, h,
			float32(width)/float32(w), 0, 0, float32(height)/float32(h),
			float32(x), float32(y),
			0, 0, 0, 0)
		is := graphics.QuadIndices()
		i.image.DrawImage(dummyImage.image, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest)
	}

	if x == 0 && y == 0 && width == w && height == h {
		if pixels != nil {
			if i.basePixels == nil {
				i.basePixels = make([]byte, 4*w*h)
			}
			copy(i.basePixels, pixels)
		} else {
			// If basePixels is nil, the restored pixels are cleared.
			// See restore() implementation.
			i.basePixels = nil
		}
		i.drawImageHistory = nil
		i.stale = false
		return
	}
	if i.basePixels == nil {
		i.makeStale()
		return
	}
	if len(i.drawImageHistory) > 0 {
		i.makeStale()
		return
	}
	idx := 4 * (y*w + x)
	if pixels != nil {
		for j := 0; j < height; j++ {
			copy(i.basePixels[idx:idx+4*width], pixels[4*j*width:4*(j+1)*width])
			idx += 4 * w
		}
	} else {
		zeros := make([]byte, 4*width)
		for j := 0; j < height; j++ {
			copy(i.basePixels[idx:idx+4*width], zeros)
			idx += 4 * w
		}
	}
	i.stale = false
}

// DrawImage draws a given image img to the image.
func (i *Image) DrawImage(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode graphics.CompositeMode, filter graphics.Filter) {
	if len(vertices) == 0 {
		return
	}
	theImages.makeStaleIfDependingOn(i)

	if img.stale || img.volatile || i.screen || !IsRestoringEnabled() {
		i.makeStale()
	} else {
		i.appendDrawImageHistory(img, vertices, indices, colorm, mode, filter)
	}
	i.image.DrawImage(img.image, vertices, indices, colorm, mode, filter)
}

// appendDrawImageHistory appends a draw-image history item to the image.
func (i *Image) appendDrawImageHistory(image *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode graphics.CompositeMode, filter graphics.Filter) {
	if i.stale || i.volatile || i.screen {
		return
	}
	const maxDrawImageHistoryNum = 100
	if len(i.drawImageHistory)+1 > maxDrawImageHistoryNum {
		i.makeStale()
		return
	}
	// All images must be resolved and not stale each after frame.
	// So we don't have to care if image is stale or not here.
	item := &drawImageHistoryItem{
		image:    image,
		vertices: vertices,
		indices:  indices,
		colorm:   colorm,
		mode:     mode,
		filter:   filter,
	}
	i.drawImageHistory = append(i.drawImageHistory, item)
}

// At returns a color value at (x, y).
//
// Note that this must not be called until context is available.
func (i *Image) At(x, y int) color.RGBA {
	w, h := i.image.Size()
	if x < 0 || y < 0 || w <= x || h <= y {
		return color.RGBA{}
	}

	if i.basePixels == nil || i.drawImageHistory != nil || i.stale {
		graphicscommand.FlushCommands()
		i.readPixelsFromGPU()
		i.drawImageHistory = nil
		i.stale = false
	}

	// Even after readPixelsFromGPU, basePixels might be nil when OpenGL error happens.
	if i.basePixels == nil {
		return color.RGBA{}
	}

	idx := 4*x + 4*y*w
	r, g, b, a := i.basePixels[idx], i.basePixels[idx+1], i.basePixels[idx+2], i.basePixels[idx+3]
	return color.RGBA{r, g, b, a}
}

// makeStaleIfDependingOn makes the image stale if the image depends on target.
func (i *Image) makeStaleIfDependingOn(target *Image) {
	if i.stale {
		return
	}
	if i.dependsOn(target) {
		i.makeStale()
	}
}

// readPixelsFromGPU reads the pixels from GPU and resolves the image's 'stale' state.
func (i *Image) readPixelsFromGPU() {
	i.basePixels = i.image.Pixels()
	i.drawImageHistory = nil
	i.stale = false
}

// resolveStale resolves the image's 'stale' state.
func (i *Image) resolveStale() {
	if !IsRestoringEnabled() {
		return
	}

	if i.volatile {
		return
	}
	if i.screen {
		return
	}
	if !i.stale {
		return
	}
	i.readPixelsFromGPU()
}

// dependsOn returns a boolean value indicating whether the image depends on target.
func (i *Image) dependsOn(target *Image) bool {
	for _, c := range i.drawImageHistory {
		if c.image == target {
			return true
		}
	}
	return false
}

// dependingImages returns all images that is depended by the image.
func (i *Image) dependingImages() map[*Image]struct{} {
	r := map[*Image]struct{}{}
	for _, c := range i.drawImageHistory {
		r[c.image] = struct{}{}
	}
	return r
}

// hasDependency returns a boolean value indicating whether the image depends on another image.
func (i *Image) hasDependency() bool {
	if i.stale {
		return false
	}
	return len(i.drawImageHistory) > 0
}

// Restore restores *graphicscommand.Image from the pixels using its state.
func (i *Image) restore() error {
	w, h := i.image.Size()
	if i.screen {
		// The screen image should also be recreated because framebuffer might
		// be changed.
		i.image = graphicscommand.NewScreenFramebufferImage(w, h)
		i.basePixels = nil
		i.drawImageHistory = nil
		i.stale = false
		return nil
	}
	if i.volatile {
		i.image = graphicscommand.NewImage(w, h)
		i.basePixels = nil
		i.drawImageHistory = nil
		i.stale = false
		return nil
	}
	if i.stale {
		// TODO: panic here?
		return errors.New("restorable: pixels must not be stale when restoring")
	}
	gimg := graphicscommand.NewImage(w, h)
	if i.basePixels != nil {
		gimg.ReplacePixels(i.basePixels, 0, 0, w, h)
	} else {
		// Clear the image explicitly.
		pix := make([]uint8, w*h*4)
		gimg.ReplacePixels(pix, 0, 0, w, h)
	}
	for _, c := range i.drawImageHistory {
		// All dependencies must be already resolved.
		if c.image.hasDependency() {
			panic("not reached")
		}
		gimg.DrawImage(c.image.image, c.vertices, c.indices, c.colorm, c.mode, c.filter)
	}
	i.image = gimg

	i.basePixels = gimg.Pixels()
	i.drawImageHistory = nil
	i.stale = false
	return nil
}

// Dispose disposes the image.
//
// After disposing, calling the function of the image causes unexpected results.
func (i *Image) Dispose() {
	theImages.remove(i)

	i.image.Dispose()
	i.image = nil
	i.basePixels = nil
	i.drawImageHistory = nil
	i.stale = false
}

// IsInvalidated returns a boolean value indicating whether the image is invalidated.
//
// If an image is invalidated, GL context is lost and all the images should be restored asap.
func (i *Image) IsInvalidated() (bool, error) {
	// FlushCommands is required because c.offscreen.impl might not have an actual texture.
	graphicscommand.FlushCommands()
	if !IsRestoringEnabled() {
		return false, nil
	}

	return i.image.IsInvalidated(), nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/dihedron/go-openstack/openstack"
)

type MyOpts struct {
	Query   *MyQuery   `request:"params,omitempty"`
	Headers *MyHeaders `request:"headers"`
	Entity  *MyEntity  `request:"entity,omitempty"`
	Ignore  *string    `json:"ignore"`
}

type MyQuery struct {
	Query1 *int    `url:"query1,omitempty"`
	Query2 *string `url:"query2,omitempty"`
	Query3 *string `url:"query3,omitempty"`
}

type MyHeaders struct {
	Header1 *string   `header:"X-Subject-Token,omitempty"`
	Header2 *[]string `header:"X-Auth-Token,omitempty"`
}

type MyEntity struct {
	Name    *string `json:"name,omitempty"`
	Surname *string `json:"surname,omitempty"`
}

func Convert(opts interface{}) {
	// TypeOf returns the reflection Type that represents the dynamic type of variable.
	// If variable is a nil interface value, TypeOf returns nil.
	t := reflect.TypeOf(opts).Elem()

	// Get the type and kind of our user variable
	fmt.Println("Type:", t.Name())
	fmt.Println("Kind:", t.Kind())

	// Iterate over all available fields and read the tag value
	for i := 0; i < t.NumField(); i++ {
		// Get the field, returns https://golang.org/pkg/reflect/#StructField
		field := t.Field(i)

		// Get the field tag value
		tag := field.Tag.Get("request")
		if len(strings.TrimSpace(tag)) > 0 {
			values := strings.Split(tag, ",")
			fmt.Printf("%d. %v (%v), tag: '%v'\n", i+1, field.Name, field.Type.Name(), values)
		} else {
			fmt.Printf("%d. %v (%v), no tag\n", i+1, field.Name, field.Type.Name())
		}

	}
}

func test1() {
	opts := &MyOpts{
		Query: &MyQuery{
			Query1: openstack.Int(1),
			Query2: openstack.String("value2"),
			Query3: openstack.String("value3"),
		},
		Headers: &MyHeaders{
			Header1: openstack.String("subject_token_abccdefghijklmnopqrstuvwxyz"),
			Header2: &[]string{
				"header_value_1",
				"header_value_2",
			},
		},
		Entity: &MyEntity{
			Name:    openstack.String("name"),
			Surname: openstack.String("surname"),
		},
	}
	Convert(opts)
}

// CreateTokenOpts contains the set of parameters and options used to
// perform an authentication (create an authentication token).
type MyComplexOpts struct {
	NoCatalog        bool    `api:"query" url:"nocatalog,omitempty"`
	AllowExpired     bool    `api:"query" url:"allow_expired,omitempty"`
	AuthToken        *string `api:"header" header:"X-Auth-Token"`
	Method           string  `api:"entity" snl:""`
	UserID           *string
	UserName         *string
	UserDomainID     *string
	UserDomainName   *string
	UserPassword     *string
	TokenID          *string
	ScopeProjectID   *string
	ScopeProjectName *string
	ScopeDomainID    *string
	ScopeDomainName  *string
	UnscopedToken    *bool
}
<commit_msg>Removed reflect tests.<commit_after><|endoftext|>