mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +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 (
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
"image"
|
||||
"image/png"
|
||||
"os"
|
||||
)
|
||||
|
||||
@ -38,3 +39,15 @@ func NewImageFromFile(path string, filter ebiten.Filter) (*ebiten.Image, image.I
|
||||
}
|
||||
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
|
||||
)
|
||||
|
||||
type Game struct {
|
||||
var (
|
||||
count int
|
||||
tmpRenderTarget *ebiten.Image
|
||||
ebitenImage *ebiten.Image
|
||||
}
|
||||
saved bool
|
||||
)
|
||||
|
||||
func (g *Game) Update(r *ebiten.Image) error {
|
||||
g.count++
|
||||
g.count %= 600
|
||||
diff := float64(g.count) * 0.2
|
||||
func Update(r *ebiten.Image) error {
|
||||
count++
|
||||
count %= 600
|
||||
diff := float64(count) * 0.2
|
||||
switch {
|
||||
case 480 < g.count:
|
||||
case 480 < count:
|
||||
diff = 0
|
||||
case 240 < g.count:
|
||||
diff = float64(480-g.count) * 0.2
|
||||
case 240 < count:
|
||||
diff = float64(480-count) * 0.2
|
||||
}
|
||||
_ = diff
|
||||
|
||||
if err := g.tmpRenderTarget.Clear(); err != nil {
|
||||
if err := tmpRenderTarget.Clear(); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
geo := ebiten.TranslateGeometry(15+float64(i)*(diff), 20)
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -62,25 +62,32 @@ func (g *Game) Update(r *ebiten.Image) error {
|
||||
for i := 0; i < 10; i++ {
|
||||
geo := ebiten.TranslateGeometry(0, float64(i)*(diff))
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if !saved && ebiten.IsKeyPressed(ebiten.KeySpace) {
|
||||
if err := ebitenutil.SaveImageAsPNG("out.png", r); err != nil {
|
||||
return err
|
||||
}
|
||||
saved = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
g := new(Game)
|
||||
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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
g.tmpRenderTarget, err = ebiten.NewImage(screenWidth, screenHeight, ebiten.FilterNearest)
|
||||
tmpRenderTarget, err = ebiten.NewImage(screenWidth, screenHeight, ebiten.FilterNearest)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
41
image.go
41
image.go
@ -21,6 +21,7 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/internal"
|
||||
"github.com/hajimehoshi/ebiten/internal/opengl"
|
||||
"github.com/hajimehoshi/ebiten/internal/opengl/internal/shader"
|
||||
"image"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
@ -61,12 +62,8 @@ func (i *innerImage) drawImage(image *innerImage, parts []ImagePart, geo Geometr
|
||||
}
|
||||
w, h := image.texture.Size()
|
||||
quads := textureQuads(parts, w, h)
|
||||
targetNativeTexture := gl.Texture(0)
|
||||
if i.texture != nil {
|
||||
targetNativeTexture = i.texture.Native()
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@ -104,6 +101,7 @@ type syncer interface {
|
||||
type Image struct {
|
||||
syncer syncer
|
||||
inner *innerImage
|
||||
pixels []uint8
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (i *Image) Clear() (err error) {
|
||||
i.pixels = nil
|
||||
i.syncer.Sync(func() {
|
||||
err = i.inner.Clear()
|
||||
})
|
||||
@ -121,6 +120,7 @@ func (i *Image) Clear() (err error) {
|
||||
|
||||
// Fill fills the image with a solid color.
|
||||
func (i *Image) Fill(clr color.Color) (err error) {
|
||||
i.pixels = nil
|
||||
i.syncer.Sync(func() {
|
||||
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) {
|
||||
i.pixels = nil
|
||||
i.syncer.Sync(func() {
|
||||
err = i.inner.drawImage(image, parts, geo, color)
|
||||
})
|
||||
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
|
||||
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() {
|
||||
initialize()
|
||||
})
|
||||
@ -52,11 +52,6 @@ func DrawTexture(native gl.Texture, target gl.Texture, projectionMatrix [4][4]fl
|
||||
gl.ActiveTexture(gl.TEXTURE0)
|
||||
native.Bind(gl.TEXTURE_2D)
|
||||
|
||||
if 0 < target {
|
||||
gl.ActiveTexture(gl.TEXTURE1)
|
||||
target.Bind(gl.TEXTURE_2D)
|
||||
}
|
||||
|
||||
vertexAttrLocation := getAttributeLocation(program, "vertex")
|
||||
texCoordAttrLocation := getAttributeLocation(program, "tex_coord")
|
||||
|
||||
|
@ -17,6 +17,8 @@ limitations under the License.
|
||||
package opengl
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-gl/gl"
|
||||
"github.com/hajimehoshi/ebiten/internal"
|
||||
"image"
|
||||
@ -79,12 +81,24 @@ func createNativeTexture(textureWidth, textureHeight int, pixels []uint8, filter
|
||||
func NewTexture(width, height int, filter int) (*Texture, error) {
|
||||
w := internal.NextPowerOf2Int(width)
|
||||
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)
|
||||
return &Texture{native, width, height}, nil
|
||||
}
|
||||
|
||||
func NewTextureFromImage(img image.Image, filter int) (*Texture, error) {
|
||||
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)
|
||||
size := adjustedImage.Bounds().Size()
|
||||
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() {
|
||||
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 {
|
||||
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) {
|
||||
@ -166,7 +166,7 @@ func (u *ui) newImage(width, height int, filter int) (*Image, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Image{u, innerImage}, nil
|
||||
return &Image{syncer: u, inner: innerImage}, nil
|
||||
}
|
||||
|
||||
func (u *ui) run() {
|
||||
|
Loading…
Reference in New Issue
Block a user