github.com/Kintar/etxt@v0.0.0-20221224033739-2fc69f000137/feed.go (about)

     1  package etxt
     2  
     3  import "golang.org/x/image/math/fixed"
     4  
     5  // TODO: add tests comparing with Draw().
     6  
     7  // Feeds are the lowest level mechanism to draw text in etxt,
     8  // allowing the user to issue each glyph draw call individually
     9  // and modifying positions or configurations in between.
    10  //
    11  // As a rule of thumb, you should only resort to feeds if
    12  // neither renderer's Draw* nor Traverse* methods give you
    13  // enough control to do what you want. Make sure you are
    14  // well acquainted with those methods first.
    15  //
    16  // Valid Feeds can only be created through [Renderer.NewFeed]().
    17  type Feed struct {
    18  	renderer       *Renderer       // associated renderer
    19  	Position       fixed.Point26_6 // the feed's working position
    20  	PrevGlyphIndex GlyphIndex      // previous glyph index. used for kern.
    21  	HasPrevGlyph   bool            // false after line breaks and others. used for kern.
    22  	LineBreakX     fixed.Int26_6   // the x coordinate set after a line break
    23  }
    24  
    25  // Draws the given rune and advances the Feed's position.
    26  //
    27  // The drawing configuration is taken from the Feed's associated renderer.
    28  //
    29  // Quantization will be checked before every Draw operation and adjusted
    30  // if necessary (even vertical quantization).
    31  func (self *Feed) Draw(codePoint rune) {
    32  	self.DrawGlyph(self.renderer.getGlyphIndex(codePoint))
    33  }
    34  
    35  // Same as Draw, but taking a glyph index instead of a rune.
    36  func (self *Feed) DrawGlyph(glyphIndex GlyphIndex) {
    37  	self.traverseGlyph(glyphIndex,
    38  		func(dot fixed.Point26_6) {
    39  			mask := self.renderer.LoadGlyphMask(glyphIndex, dot)
    40  			self.renderer.DefaultDrawFunc(dot, mask, glyphIndex)
    41  		})
    42  }
    43  
    44  // Draws the given shape and advances the Feed's position based on
    45  // the image bounds width (rect.Dx). Notice that shapes are not cached,
    46  // so they may be expensive to use. See also DrawImage.
    47  // func (self *Feed) DrawShape(shape emask.Shape) {
    48  //
    49  // }
    50  
    51  // TODO: ebiten version vs gtxt version
    52  // func (self *Feed) DrawImage(img image.Image)
    53  
    54  // Advances the Feed's position without drawing anything.
    55  func (self *Feed) Advance(codePoint rune) {
    56  	// TODO: do we *reaally* need this method? Maybe it is superfluous.
    57  	self.AdvanceGlyph(self.renderer.getGlyphIndex(codePoint))
    58  }
    59  
    60  // Advances the Feed's position without drawing anything.
    61  func (self *Feed) AdvanceGlyph(glyphIndex GlyphIndex) {
    62  	self.traverseGlyph(glyphIndex, func(fixed.Point26_6) {})
    63  }
    64  
    65  // Advances the Feed's position with a line break.
    66  func (self *Feed) LineBreak() {
    67  	self.Position.X = self.renderer.quantizeX(self.Position.X, self.renderer.direction) // *
    68  	self.Position.Y = self.renderer.quantizeY(self.Position.Y)
    69  	self.Position.Y = self.renderer.applyLineAdvance(self.Position)
    70  	self.Position.X = self.LineBreakX
    71  	self.HasPrevGlyph = false
    72  	// * required because applyLineAdvance may call NotifyFractChange later
    73  }
    74  
    75  // Private traverse method used for Draw and Advance.
    76  func (self *Feed) traverseGlyph(glyphIndex GlyphIndex, f func(fixed.Point26_6)) {
    77  	// By spec, we always quantize. While this could be done only if
    78  	// !self.HasPrevGlyph, users may modify the Y position manually and then
    79  	// be bitten by some combinations of caching and quantization modes.
    80  	// While I could also just blame me, I decided to be nicer.
    81  	self.Position.Y = self.renderer.quantizeY(self.Position.Y)
    82  
    83  	// create the glyph pair and send it to the proper traverse function
    84  	gpair := glyphPair{glyphIndex, self.PrevGlyphIndex, self.HasPrevGlyph}
    85  	switch self.renderer.direction {
    86  	case RightToLeft:
    87  		self.Position = self.renderer.traverseGlyphRTL(self.Position, gpair, f)
    88  	case LeftToRight:
    89  		self.Position = self.renderer.traverseGlyphLTR(self.Position, gpair, f)
    90  	default:
    91  		panic("unhandled switch case")
    92  	}
    93  
    94  	self.HasPrevGlyph = true
    95  	self.PrevGlyphIndex = glyphIndex
    96  }