mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 18:58:54 +01:00
Implement image.Image at ebiten.Image (#35)
This commit is contained in:
parent
095c3ca380
commit
cd9efd3932
@ -19,6 +19,7 @@ package ebitenutil
|
|||||||
import (
|
import (
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
"image"
|
"image"
|
||||||
|
"image/png"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -38,3 +39,15 @@ func NewImageFromFile(path string, filter ebiten.Filter) (*ebiten.Image, image.I
|
|||||||
}
|
}
|
||||||
return img2, img, err
|
return img2, img, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SaveImageAsPNG(path string, img *ebiten.Image) error {
|
||||||
|
file, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
if err := png.Encode(file, img); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -29,31 +29,31 @@ const (
|
|||||||
screenHeight = 240
|
screenHeight = 240
|
||||||
)
|
)
|
||||||
|
|
||||||
type Game struct {
|
var (
|
||||||
count int
|
count int
|
||||||
tmpRenderTarget *ebiten.Image
|
tmpRenderTarget *ebiten.Image
|
||||||
ebitenImage *ebiten.Image
|
ebitenImage *ebiten.Image
|
||||||
}
|
saved bool
|
||||||
|
)
|
||||||
|
|
||||||
func (g *Game) Update(r *ebiten.Image) error {
|
func Update(r *ebiten.Image) error {
|
||||||
g.count++
|
count++
|
||||||
g.count %= 600
|
count %= 600
|
||||||
diff := float64(g.count) * 0.2
|
diff := float64(count) * 0.2
|
||||||
switch {
|
switch {
|
||||||
case 480 < g.count:
|
case 480 < count:
|
||||||
diff = 0
|
diff = 0
|
||||||
case 240 < g.count:
|
case 240 < count:
|
||||||
diff = float64(480-g.count) * 0.2
|
diff = float64(480-count) * 0.2
|
||||||
}
|
}
|
||||||
_ = diff
|
|
||||||
|
|
||||||
if err := g.tmpRenderTarget.Clear(); err != nil {
|
if err := tmpRenderTarget.Clear(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
geo := ebiten.TranslateGeometry(15+float64(i)*(diff), 20)
|
geo := ebiten.TranslateGeometry(15+float64(i)*(diff), 20)
|
||||||
clr := ebiten.ScaleColor(1.0, 1.0, 1.0, 0.5)
|
clr := ebiten.ScaleColor(1.0, 1.0, 1.0, 0.5)
|
||||||
if err := ebiten.DrawWholeImage(g.tmpRenderTarget, g.ebitenImage, geo, clr); err != nil {
|
if err := ebiten.DrawWholeImage(tmpRenderTarget, ebitenImage, geo, clr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,25 +62,32 @@ func (g *Game) Update(r *ebiten.Image) error {
|
|||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
geo := ebiten.TranslateGeometry(0, float64(i)*(diff))
|
geo := ebiten.TranslateGeometry(0, float64(i)*(diff))
|
||||||
clr := ebiten.ColorMatrixI()
|
clr := ebiten.ColorMatrixI()
|
||||||
if err := ebiten.DrawWholeImage(r, g.tmpRenderTarget, geo, clr); err != nil {
|
if err := ebiten.DrawWholeImage(r, tmpRenderTarget, geo, clr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !saved && ebiten.IsKeyPressed(ebiten.KeySpace) {
|
||||||
|
if err := ebitenutil.SaveImageAsPNG("out.png", r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
saved = true
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
g := new(Game)
|
|
||||||
var err error
|
var err error
|
||||||
g.ebitenImage, _, err = ebitenutil.NewImageFromFile("images/ebiten.png", ebiten.FilterNearest)
|
ebitenImage, _, err = ebitenutil.NewImageFromFile("images/ebiten.png", ebiten.FilterNearest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
g.tmpRenderTarget, err = ebiten.NewImage(screenWidth, screenHeight, ebiten.FilterNearest)
|
tmpRenderTarget, err = ebiten.NewImage(screenWidth, screenHeight, ebiten.FilterNearest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := ebiten.Run(g.Update, screenWidth, screenHeight, 2, "Alpha Blending (Ebiten Demo)"); err != nil {
|
if err := ebiten.Run(Update, screenWidth, screenHeight, 2, "Alpha Blending (Ebiten Demo)"); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
41
image.go
41
image.go
@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten/internal"
|
"github.com/hajimehoshi/ebiten/internal"
|
||||||
"github.com/hajimehoshi/ebiten/internal/opengl"
|
"github.com/hajimehoshi/ebiten/internal/opengl"
|
||||||
"github.com/hajimehoshi/ebiten/internal/opengl/internal/shader"
|
"github.com/hajimehoshi/ebiten/internal/opengl/internal/shader"
|
||||||
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -61,12 +62,8 @@ func (i *innerImage) drawImage(image *innerImage, parts []ImagePart, geo Geometr
|
|||||||
}
|
}
|
||||||
w, h := image.texture.Size()
|
w, h := image.texture.Size()
|
||||||
quads := textureQuads(parts, w, h)
|
quads := textureQuads(parts, w, h)
|
||||||
targetNativeTexture := gl.Texture(0)
|
|
||||||
if i.texture != nil {
|
|
||||||
targetNativeTexture = i.texture.Native()
|
|
||||||
}
|
|
||||||
projectionMatrix := i.framebuffer.ProjectionMatrix()
|
projectionMatrix := i.framebuffer.ProjectionMatrix()
|
||||||
shader.DrawTexture(image.texture.Native(), targetNativeTexture, projectionMatrix, quads, &geo, &color)
|
shader.DrawTexture(image.texture.Native(), projectionMatrix, quads, &geo, &color)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +101,7 @@ type syncer interface {
|
|||||||
type Image struct {
|
type Image struct {
|
||||||
syncer syncer
|
syncer syncer
|
||||||
inner *innerImage
|
inner *innerImage
|
||||||
|
pixels []uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the size of the image.
|
// Size returns the size of the image.
|
||||||
@ -113,6 +111,7 @@ func (i *Image) Size() (width, height int) {
|
|||||||
|
|
||||||
// Clear resets the pixels of the image into 0.
|
// Clear resets the pixels of the image into 0.
|
||||||
func (i *Image) Clear() (err error) {
|
func (i *Image) Clear() (err error) {
|
||||||
|
i.pixels = nil
|
||||||
i.syncer.Sync(func() {
|
i.syncer.Sync(func() {
|
||||||
err = i.inner.Clear()
|
err = i.inner.Clear()
|
||||||
})
|
})
|
||||||
@ -121,6 +120,7 @@ func (i *Image) Clear() (err error) {
|
|||||||
|
|
||||||
// Fill fills the image with a solid color.
|
// Fill fills the image with a solid color.
|
||||||
func (i *Image) Fill(clr color.Color) (err error) {
|
func (i *Image) Fill(clr color.Color) (err error) {
|
||||||
|
i.pixels = nil
|
||||||
i.syncer.Sync(func() {
|
i.syncer.Sync(func() {
|
||||||
err = i.inner.Fill(clr)
|
err = i.inner.Fill(clr)
|
||||||
})
|
})
|
||||||
@ -133,8 +133,39 @@ func (i *Image) DrawImage(image *Image, parts []ImagePart, geo GeometryMatrix, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) drawImage(image *innerImage, parts []ImagePart, geo GeometryMatrix, color ColorMatrix) (err error) {
|
func (i *Image) drawImage(image *innerImage, parts []ImagePart, geo GeometryMatrix, color ColorMatrix) (err error) {
|
||||||
|
i.pixels = nil
|
||||||
i.syncer.Sync(func() {
|
i.syncer.Sync(func() {
|
||||||
err = i.inner.drawImage(image, parts, geo, color)
|
err = i.inner.drawImage(image, parts, geo, color)
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bounds returns the bounds of the image.
|
||||||
|
func (i *Image) Bounds() image.Rectangle {
|
||||||
|
w, h := i.inner.size()
|
||||||
|
return image.Rect(0, 0, w, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ColorModel returns the color model of the image.
|
||||||
|
func (i *Image) ColorModel() color.Model {
|
||||||
|
return color.RGBAModel
|
||||||
|
}
|
||||||
|
|
||||||
|
// At returns the color of the image at (x, y).
|
||||||
|
// This method may read pixels from GPU to VRAM and be slow.
|
||||||
|
func (i *Image) At(x, y int) color.Color {
|
||||||
|
if i.pixels == nil {
|
||||||
|
i.syncer.Sync(func() {
|
||||||
|
var err error
|
||||||
|
i.pixels, err = i.inner.texture.Pixels()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
w, _ := i.inner.size()
|
||||||
|
w = internal.NextPowerOf2Int(w)
|
||||||
|
idx := 4*x + 4*y*w
|
||||||
|
r, g, b, a := i.pixels[idx], i.pixels[idx+1], i.pixels[idx+2], i.pixels[idx+3]
|
||||||
|
return color.RGBA{r, g, b, a}
|
||||||
|
}
|
||||||
|
@ -38,7 +38,7 @@ type Matrix interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use VBO
|
// TODO: Use VBO
|
||||||
func DrawTexture(native gl.Texture, target gl.Texture, projectionMatrix [4][4]float64, quads []TextureQuad, geo Matrix, color Matrix) {
|
func DrawTexture(native gl.Texture, projectionMatrix [4][4]float64, quads []TextureQuad, geo Matrix, color Matrix) {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
initialize()
|
initialize()
|
||||||
})
|
})
|
||||||
@ -52,11 +52,6 @@ func DrawTexture(native gl.Texture, target gl.Texture, projectionMatrix [4][4]fl
|
|||||||
gl.ActiveTexture(gl.TEXTURE0)
|
gl.ActiveTexture(gl.TEXTURE0)
|
||||||
native.Bind(gl.TEXTURE_2D)
|
native.Bind(gl.TEXTURE_2D)
|
||||||
|
|
||||||
if 0 < target {
|
|
||||||
gl.ActiveTexture(gl.TEXTURE1)
|
|
||||||
target.Bind(gl.TEXTURE_2D)
|
|
||||||
}
|
|
||||||
|
|
||||||
vertexAttrLocation := getAttributeLocation(program, "vertex")
|
vertexAttrLocation := getAttributeLocation(program, "vertex")
|
||||||
texCoordAttrLocation := getAttributeLocation(program, "tex_coord")
|
texCoordAttrLocation := getAttributeLocation(program, "tex_coord")
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||||||
package opengl
|
package opengl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/go-gl/gl"
|
"github.com/go-gl/gl"
|
||||||
"github.com/hajimehoshi/ebiten/internal"
|
"github.com/hajimehoshi/ebiten/internal"
|
||||||
"image"
|
"image"
|
||||||
@ -79,12 +81,24 @@ func createNativeTexture(textureWidth, textureHeight int, pixels []uint8, filter
|
|||||||
func NewTexture(width, height int, filter int) (*Texture, error) {
|
func NewTexture(width, height int, filter int) (*Texture, error) {
|
||||||
w := internal.NextPowerOf2Int(width)
|
w := internal.NextPowerOf2Int(width)
|
||||||
h := internal.NextPowerOf2Int(height)
|
h := internal.NextPowerOf2Int(height)
|
||||||
|
if w < 4 {
|
||||||
|
return nil, errors.New("width must be equal or more than 4.")
|
||||||
|
}
|
||||||
|
if h < 4 {
|
||||||
|
return nil, errors.New("height must be equal or more than 4.")
|
||||||
|
}
|
||||||
native := createNativeTexture(w, h, nil, filter)
|
native := createNativeTexture(w, h, nil, filter)
|
||||||
return &Texture{native, width, height}, nil
|
return &Texture{native, width, height}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTextureFromImage(img image.Image, filter int) (*Texture, error) {
|
func NewTextureFromImage(img image.Image, filter int) (*Texture, error) {
|
||||||
origSize := img.Bounds().Size()
|
origSize := img.Bounds().Size()
|
||||||
|
if origSize.X < 4 {
|
||||||
|
return nil, errors.New("width must be equal or more than 4.")
|
||||||
|
}
|
||||||
|
if origSize.Y < 4 {
|
||||||
|
return nil, errors.New("height must be equal or more than 4.")
|
||||||
|
}
|
||||||
adjustedImage := adjustImageForTexture(img)
|
adjustedImage := adjustImageForTexture(img)
|
||||||
size := adjustedImage.Bounds().Size()
|
size := adjustedImage.Bounds().Size()
|
||||||
native := createNativeTexture(size.X, size.Y, adjustedImage.Pix, filter)
|
native := createNativeTexture(size.X, size.Y, adjustedImage.Pix, filter)
|
||||||
@ -94,3 +108,15 @@ func NewTextureFromImage(img image.Image, filter int) (*Texture, error) {
|
|||||||
func (t *Texture) Dispose() {
|
func (t *Texture) Dispose() {
|
||||||
t.native.Delete()
|
t.native.Delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Texture) Pixels() ([]uint8, error) {
|
||||||
|
w, h := internal.NextPowerOf2Int(t.width), internal.NextPowerOf2Int(t.height)
|
||||||
|
pixels := make([]uint8, 4*w*h)
|
||||||
|
t.native.Bind(gl.TEXTURE_2D)
|
||||||
|
gl.GetTexImage(gl.TEXTURE_2D, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
|
||||||
|
if e := gl.GetError(); e != gl.NO_ERROR {
|
||||||
|
// TODO: Use glu.ErrorString
|
||||||
|
return nil, errors.New(fmt.Sprintf("gl error: %d", e))
|
||||||
|
}
|
||||||
|
return pixels, nil
|
||||||
|
}
|
||||||
|
4
ui.go
4
ui.go
@ -148,7 +148,7 @@ func (u *ui) newImageFromImage(img image.Image, filter int) (*Image, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Image{u, innerImage}, nil
|
return &Image{syncer: u, inner: innerImage}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) newImage(width, height int, filter int) (*Image, error) {
|
func (u *ui) newImage(width, height int, filter int) (*Image, error) {
|
||||||
@ -166,7 +166,7 @@ func (u *ui) newImage(width, height int, filter int) (*Image, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Image{u, innerImage}, nil
|
return &Image{syncer: u, inner: innerImage}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) run() {
|
func (u *ui) run() {
|
||||||
|
Loading…
Reference in New Issue
Block a user