diff --git a/example/blocks/blocks/font.go b/example/blocks/blocks/font.go index 6f889a517..c935c5020 100644 --- a/example/blocks/blocks/font.go +++ b/example/blocks/blocks/font.go @@ -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) diff --git a/example/blocks/blocks/piece.go b/example/blocks/blocks/piece.go index ed98d8eca..26d02effe 100644 --- a/example/blocks/blocks/piece.go +++ b/example/blocks/blocks/piece.go @@ -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) { diff --git a/image.go b/image.go index 5108d64b1..67c75c540 100644 --- a/image.go +++ b/image.go @@ -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 } diff --git a/internal/graphics/internal/shader/drawtexture.go b/internal/graphics/internal/shader/drawtexture.go index 23c108866..e38859abf 100644 --- a/internal/graphics/internal/shader/drawtexture.go +++ b/internal/graphics/internal/shader/drawtexture.go @@ -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 } diff --git a/readme.md b/readme.md index b8fa5907e..53a5a68a9 100644 --- a/readme.md +++ b/readme.md @@ -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