Introduce ImageParts

This commit is contained in:
Hajime Hoshi 2015-01-05 00:42:20 +09:00
parent 8994ffad9b
commit cdc29210db
5 changed files with 135 additions and 72 deletions

View File

@ -15,12 +15,11 @@
package blocks
import (
"errors"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/ebitenutil"
"image"
"image/color"
"math"
"strings"
)
var imageFont *ebiten.Image
@ -41,43 +40,51 @@ func textWidth(str string) int {
return charWidth * len(str)
}
var fontImageParts = make([]ebiten.ImagePart, 0, 256)
type fontImageParts string
func (f fontImageParts) Len() int {
return len(f)
}
func (f fontImageParts) Dst(i int) (x0, y0, x1, y1 int) {
x := i - strings.LastIndex(string(f)[:i], "\n") - 1
y := strings.Count(string(f)[:i], "\n")
x *= charWidth
y *= charHeight
if x < 0 {
return 0, 0, 0, 0
}
return x, y, x + charWidth, y + charHeight
}
func (f fontImageParts) Src(i int) (x0, y0, x1, y1 int) {
code := int(f[i])
if code == '\n' {
return 0, 0, 0, 0
}
x := (code % 16) * charWidth
y := ((code - 32) / 16) * charHeight
return x, y, x + charWidth, y + charHeight
}
func drawText(rt *ebiten.Image, str string, ox, oy, scale int, c color.Color) error {
if cap(fontImageParts) < len(str) {
return errors.New("str is too long")
}
parts := fontImageParts[:0]
locationX, locationY := 0, 0
for _, c := range str {
if c == '\n' {
locationX = 0
locationY += charHeight
continue
}
code := int(c)
x := (code % 16) * charWidth
y := ((code - 32) / 16) * charHeight
parts = append(parts, ebiten.ImagePart{
Dst: image.Rect(locationX, locationY, locationX+charWidth, locationY+charHeight),
Src: image.Rect(x, y, x+charWidth, y+charHeight),
})
locationX += charWidth
}
options := &ebiten.DrawImageOptions{
Parts: parts,
ImageParts: fontImageParts(str),
}
options.GeoM.Scale(float64(scale), float64(scale))
options.GeoM.Translate(float64(ox), float64(oy))
c2 := color.NRGBA64Model.Convert(c).(color.NRGBA64)
ur, ug, ub, ua := c.RGBA()
const max = math.MaxUint16
r := float64(c2.R) / max
g := float64(c2.G) / max
b := float64(c2.B) / max
a := float64(c2.A) / max
r := float64(ur) / max
g := float64(ug) / max
b := float64(ub) / max
a := float64(ua) / max
if 0 < a {
r /= a
g /= a
b /= a
}
options.ColorM.Scale(r, g, b, a)
return rt.DrawImage(imageFont, options)

View File

@ -17,7 +17,6 @@ package blocks
import (
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/ebitenutil"
"image"
)
var imageBlocks *ebiten.Image
@ -143,28 +142,36 @@ const blockHeight = 10
const fieldBlockNumX = 10
const fieldBlockNumY = 20
var blocksImageParts = make([]ebiten.ImagePart, 0, fieldBlockNumX*fieldBlockNumY)
type blocksImageParts [][]BlockType
func (b blocksImageParts) Len() int {
return len(b) * len(b[0])
}
func (b blocksImageParts) Dst(i int) (x0, y0, x1, y1 int) {
i, j := i%len(b), i/len(b)
x := i * blockWidth
y := j * blockHeight
return x, y, x + blockWidth, y + blockHeight
}
func (b blocksImageParts) Src(i int) (x0, y0, x1, y1 int) {
i, j := i%len(b), i/len(b)
block := b[i][j]
if block == BlockTypeNone {
return 0, 0, 0, 0
}
x := (int(block) - 1) * blockWidth
return x, 0, x + blockWidth, blockHeight
}
func drawBlocks(r *ebiten.Image, blocks [][]BlockType, x, y int, clr ebiten.ColorM) error {
parts := blocksImageParts[:0]
for i, blockCol := range blocks {
for j, block := range blockCol {
if block == BlockTypeNone {
continue
}
locationX := i*blockWidth + x
locationY := j*blockHeight + y
srcX := (int(block) - 1) * blockWidth
parts = append(parts, ebiten.ImagePart{
Dst: image.Rect(locationX, locationY, locationX+blockWidth, locationY+blockHeight),
Src: image.Rect(srcX, 0, srcX+blockWidth, blockHeight),
})
}
op := &ebiten.DrawImageOptions{
ImageParts: blocksImageParts(blocks),
ColorM: clr,
}
return r.DrawImage(imageBlocks, &ebiten.DrawImageOptions{
Parts: parts,
ColorM: clr,
})
op.GeoM.Translate(float64(x), float64(y))
return r.DrawImage(imageBlocks, op)
}
func (p *Piece) InitialPosition() (int, int) {

View File

@ -49,22 +49,56 @@ func (i *innerImage) Fill(c *opengl.Context, clr color.Color) error {
return i.framebuffer.Fill(c, r, g, b, a)
}
// TODO: Remove this in the future.
type imageParts []ImagePart
func (p imageParts) Len() int {
return len(p)
}
func (p imageParts) Dst(i int) (x0, y0, x1, y1 int) {
dst := &p[i].Dst
return dst.Min.X, dst.Min.Y, dst.Max.X, dst.Max.Y
}
func (p imageParts) Src(i int) (x0, y0, x1, y1 int) {
src := &p[i].Src
return src.Min.X, src.Min.Y, src.Max.X, src.Max.Y
}
type wholeImage struct {
width int
height int
}
func (w *wholeImage) Len() int {
return 1
}
func (w *wholeImage) Dst(i int) (x0, y0, x1, y1 int) {
return 0, 0, w.width, w.height
}
func (w *wholeImage) Src(i int) (x0, y0, x1, y1 int) {
return 0, 0, w.width, w.height
}
func (i *innerImage) drawImage(c *opengl.Context, img *innerImage, options *DrawImageOptions) error {
if options == nil {
options = &DrawImageOptions{}
}
parts := options.Parts
parts := options.ImageParts
if parts == nil {
w, h := img.size()
parts = []ImagePart{
{
Dst: image.Rect(0, 0, w, h),
Src: image.Rect(0, 0, w, h),
},
dparts := options.Parts
if dparts != nil {
parts = imageParts(dparts)
} else {
w, h := img.size()
parts = &wholeImage{w, h}
}
}
w, h := img.size()
quads := &textureQuads{parts, w, h}
quads := &textureQuads{parts: parts, width: w, height: h}
return i.framebuffer.DrawTexture(c, img.texture, quads, &options.GeoM, &options.ColorM)
}
@ -77,26 +111,24 @@ func v(y float32, height int) float32 {
}
type textureQuads struct {
parts []ImagePart
parts ImageParts
width int
height int
}
func (t *textureQuads) Len() int {
return len(t.parts)
return t.parts.Len()
}
func (t *textureQuads) Vertex(i int) (x0, y0, x1, y1 float32) {
p := &t.parts[i]
dst := &p.Dst
return float32(dst.Min.X), float32(dst.Min.Y), float32(dst.Max.X), float32(dst.Max.Y)
ix0, iy0, ix1, iy1 := t.parts.Dst(i)
return float32(ix0), float32(iy0), float32(ix1), float32(iy1)
}
func (t *textureQuads) Texture(i int) (u0, v0, u1, v1 float32) {
p := &t.parts[i]
src := &p.Src
x0, y0, x1, y1 := t.parts.Src(i)
w, h := t.width, t.height
return u(float32(src.Min.X), w), v(float32(src.Min.Y), h), u(float32(src.Max.X), w), v(float32(src.Max.Y), h)
return u(float32(x0), w), v(float32(y0), h), u(float32(x1), w), v(float32(y1), h)
}
// Image represents an image.
@ -184,14 +216,25 @@ func (i *Image) At(x, y int) color.Color {
return color.RGBA{r, g, b, a}
}
// Deprecated: Use ImageParts instead.
type ImagePart struct {
Dst image.Rectangle
Src image.Rectangle
}
// An ImageParts represents the parts of the destination image and the parts of the source image.
type ImageParts interface {
Len() int
Dst(i int) (x0, y0, x1, y1 int)
Src(i int) (x0, y0, x1, y1 int)
}
// A DrawImageOptions represents options to render an image on an image.
type DrawImageOptions struct {
Parts []ImagePart
GeoM GeoM
ColorM ColorM
ImageParts ImageParts
GeoM GeoM
ColorM ColorM
// Deprecated: Use ImageParts instead.
Parts []ImagePart
}

View File

@ -76,6 +76,9 @@ func DrawTexture(c *opengl.Context, texture opengl.Texture, projectionMatrix *[4
for i := 0; i < quads.Len(); i++ {
x0, y0, x1, y1 := quads.Vertex(i)
u0, v0, u1, v1 := quads.Texture(i)
if x0 == x1 || y0 == y1 || u0 == u1 || v0 == v1 {
continue
}
vertices = append(vertices,
x0, y0, u0, v0,
x1, y0, u1, v0,
@ -83,7 +86,10 @@ func DrawTexture(c *opengl.Context, texture opengl.Texture, projectionMatrix *[4
x1, y1, u1, v1,
)
}
if len(vertices) == 0 {
return nil
}
c.BufferSubData(c.ArrayBuffer, vertices)
c.DrawElements(6 * quads.Len())
c.DrawElements(6 * len(vertices) / 16)
return nil
}

View File

@ -56,7 +56,7 @@ If you want to use GopherJS, execute this:
## Versioning
* We adopted [Semantic Versioning](http://semver.org/)
* We obey [Semantic Versioning](http://semver.org/) basically
## License