github.com/Kintar/etxt@v0.0.0-20221224033739-2fc69f000137/ebiten_yes.go (about) 1 //go:build !gtxt 2 3 package etxt 4 5 import "image" 6 import "image/color" 7 8 import "golang.org/x/image/math/fixed" 9 import "github.com/hajimehoshi/ebiten/v2" 10 11 // Alias to allow compiling the package without Ebitengine (gtxt version). 12 // 13 // Without Ebitengine, TargetImage defaults to [image/draw.Image]. 14 type TargetImage = *ebiten.Image 15 16 // A GlyphMask is the image that results from rasterizing a glyph. 17 // You rarely need to use GlyphMasks directly unless using advanced 18 // functions. 19 // 20 // Without Ebitengine (gtxt version), GlyphMask defaults to [*image.Alpha]. 21 // The image bounds are adjusted to allow drawing the glyph at its 22 // intended position. In particular, bounds.Min.Y is typically 23 // negative, with y = 0 corresponding to the glyph's baseline, y < 0 24 // to the ascending portions and y > 0 to the descending ones. 25 // 26 // With Ebitengine, GlyphMask defaults to *ebiten.Image. 27 type GlyphMask = *ebiten.Image 28 29 // Mix modes specify how to compose colors when drawing glyphs 30 // on the renderer's target: 31 // - Without Ebitengine, the mix modes can be MixOver, MixReplace, 32 // MixAdd, MixSub, MixMultiply, MixCut and MixFiftyFifty. 33 // - With Ebitengine, mix modes are Ebitengine's composite modes. 34 // 35 // I only ever change mix modes to make cutout text, but there's a 36 // lot of weird people out there, what can I say. 37 type MixMode = ebiten.CompositeMode 38 39 const defaultMixMode = ebiten.CompositeModeSourceOver 40 41 // The default glyph drawing function used in renderers. Do not confuse with 42 // the main [Renderer.Draw]() function. DefaultDrawFunc is a low level function, 43 // rarely necessary except when paired with [Renderer.Traverse]*() operations. 44 func (self *Renderer) DefaultDrawFunc(dot fixed.Point26_6, mask GlyphMask, _ GlyphIndex) { 45 if mask == nil { 46 return 47 } // spaces and empty glyphs will be nil 48 49 // TODO: switch to DrawTriangles to reduce overhead? 50 // DrawTriangles(vertices []Vertex, indices []uint16, img *Image, options *DrawTrianglesOptions) 51 opts := ebiten.DrawImageOptions{} 52 srcRect := mask.Bounds() 53 opts.GeoM.Translate(float64(dot.X.Floor()+srcRect.Min.X), float64(dot.Y.Floor()+srcRect.Min.Y)) 54 opts.ColorM.Scale(colorToFloat64(self.mainColor)) 55 opts.CompositeMode = self.mixMode 56 self.target.DrawImage(mask, &opts) 57 } 58 59 // Convert a color to its float64 [0, 1.0] components. 60 // This could actually be memorized to make DefaultDrawFunc work better 61 // in most cases, but I don't know if it's worth the extra complexity. 62 func colorToFloat64(subject color.Color) (float64, float64, float64, float64) { 63 rgbaColor, isRGBA := subject.(color.RGBA) 64 if isRGBA { 65 r, g, b, a := rgbaColor.R, rgbaColor.G, rgbaColor.B, rgbaColor.A 66 return float64(r) / 255, float64(g) / 255, float64(b) / 255, float64(a) / 255 67 } else { 68 r, g, b, a := subject.RGBA() 69 return float64(r) / 65535, float64(g) / 65535, float64(b) / 65535, float64(a) / 65535 70 } 71 } 72 73 // helper function required when working with ebitengine images 74 func convertAlphaImageToGlyphMask(alpha *image.Alpha) GlyphMask { 75 if alpha == nil { 76 return nil 77 } 78 79 // NOTICE: since ebiten doesn't have good support for alpha images, 80 // this is quite a pain, but not much we can do from here. 81 rgba := image.NewRGBA(alpha.Rect) 82 pixels := rgba.Pix 83 index := 0 84 for _, value := range alpha.Pix { 85 // NOTE: we could actually skip when value == 0, no? benchmark? 86 pixels[index+0] = value 87 pixels[index+1] = value 88 pixels[index+2] = value 89 pixels[index+3] = value 90 index += 4 91 } 92 return ebiten.NewImageFromImageWithOptions(rgba, &ebiten.NewImageFromImageOptions{PreserveBounds: true}) 93 }