emask

package
v0.0.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 30, 2022 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

The emask subpackage defines the Rasterizer interface used within etxt and provides multiple ready-to-use implementations.

In this context, "Rasterizer" refers to a "glyph mask rasterizer": whenever we want to render text on a screen we first have to rasterize the individual font glyphs, extracted from font files as outlines (sets of lines and curves), and draw them into a raster image (a grid of pixels).

In short, this subpackage allows anyone to pick different rasterizers or implement their own by targeting the Rasterizer interface. This opens the door to the creation of cool effects that may modify the glyph outlines (e.g.: glyph expansion), the rasterization algorithms (e.g.: hinting), the resulting glyph masks (e.g.: blurring) or any combination of the previous.

That said, before you jump into the hype train, notice that some of these effects can also be achieved (often more easily) at later stages with shaders or custom blitting.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Rasterize

func Rasterize(outline sfnt.Segments, rasterizer Rasterizer, dot fixed.Point26_6) (*image.Alpha, error)

A low level method to rasterize glyph masks.

Returned masks have their coordinates adjusted so the mask is drawn at dot origin (0, 0) + the given fractional position by default. To draw it at a specific dot with a matching fractional position, translate the mask by dot.X.Floor() and dot.Y.Floor(). If you don't want to adjust the fractional pixel position, you can call Rasterize with a zero-value fixed.Point26_6{}.

The given drawing coordinate can be your current drawing dot, but as indicated above, only its fractional part will be considered.

The image returned will be nil if the segments are empty or do not include any active lines or curves (e.g.: space glyphs).

Types

type DefaultRasterizer

type DefaultRasterizer struct {
	// contains filtered or unexported fields
}

The DefaultRasterizer is a wrapper to make golang.org/x/image/vector.Rasterizer conform to the Rasterizer interface.

func (*DefaultRasterizer) CacheSignature

func (self *DefaultRasterizer) CacheSignature() uint64

Satisfies the Rasterizer interface.

func (*DefaultRasterizer) CubeTo

func (self *DefaultRasterizer) CubeTo(controlA, controlB, target fixed.Point26_6)

Creates a cubic Bézier curve to the given target passing through the given control points.

func (*DefaultRasterizer) LineTo

func (self *DefaultRasterizer) LineTo(point fixed.Point26_6)

Creates a straight boundary from the current position to the given point.

func (*DefaultRasterizer) MoveTo

func (self *DefaultRasterizer) MoveTo(point fixed.Point26_6)

Moves the current position to the given point.

func (*DefaultRasterizer) QuadTo

func (self *DefaultRasterizer) QuadTo(control, target fixed.Point26_6)

Creates a quadratic Bézier curve (also known as a conic Bézier curve) to the given target passing through the given control point.

func (*DefaultRasterizer) Rasterize

func (self *DefaultRasterizer) Rasterize(outline sfnt.Segments, fract fixed.Point26_6) (*image.Alpha, error)

Satisfies the Rasterizer interface.

func (*DefaultRasterizer) SetHighByte

func (self *DefaultRasterizer) SetHighByte(value uint8)

Satisfies the UserCfgCacheSignature interface.

func (*DefaultRasterizer) SetOnChangeFunc

func (self *DefaultRasterizer) SetOnChangeFunc(onChange func(Rasterizer))

Satisfies the Rasterizer interface.

type EdgeMarkerRasterizer

type EdgeMarkerRasterizer struct {
	// contains filtered or unexported fields
}

An alternative to DefaultRasterizer that avoids using golang.org/x/image/vector.Rasterizer under the hood. Results are visually very similar, but performance is 3 times worse.

The purpose of this rasterizer is to offer a simpler, more readable and well-documented version of the algorithm used by vector.Rasterizer that anyone can edit, adapt or learn from.

The zero-value is usable but will produce jaggy results, as curve segmentation parameters are not configured. Use NewStdEdgeMarkerRasterizer() if you prefer a pre-configured rasterizer. You may also configure the rasterizer manually through EdgeMarkerRasterizer.SetCurveThreshold() and EdgeMarkerRasterizer.SetMaxCurveSplits().

func NewStdEdgeMarkerRasterizer

func NewStdEdgeMarkerRasterizer() *EdgeMarkerRasterizer

Creates a new EdgeMarkerRasterizer with reasonable default values.

func (*EdgeMarkerRasterizer) CacheSignature

func (self *EdgeMarkerRasterizer) CacheSignature() uint64

Satisfies the Rasterizer interface.

func (*EdgeMarkerRasterizer) CubeTo

func (self *EdgeMarkerRasterizer) CubeTo(controlA, controlB, target fixed.Point26_6)

See DefaultRasterizer.CubeTo().

func (*EdgeMarkerRasterizer) LineTo

func (self *EdgeMarkerRasterizer) LineTo(point fixed.Point26_6)

See DefaultRasterizer.LineTo().

func (*EdgeMarkerRasterizer) MoveTo

func (self *EdgeMarkerRasterizer) MoveTo(point fixed.Point26_6)

See DefaultRasterizer.MoveTo().

func (*EdgeMarkerRasterizer) QuadTo

func (self *EdgeMarkerRasterizer) QuadTo(control, target fixed.Point26_6)

See DefaultRasterizer.QuadTo().

func (*EdgeMarkerRasterizer) Rasterize

func (self *EdgeMarkerRasterizer) Rasterize(outline sfnt.Segments, fract fixed.Point26_6) (*image.Alpha, error)

Satisfies the Rasterizer interface.

func (*EdgeMarkerRasterizer) SetCurveThreshold

func (self *EdgeMarkerRasterizer) SetCurveThreshold(threshold float32)

Sets the threshold distance to use when splitting Bézier curves into linear segments. If a linear segment misses the curve by more than the threshold value, the curve will be split. Otherwise, the linear segment will be used to approximate it.

Values very close to zero could prevent the algorithm from converging due to floating point instability, but the MaxCurveSplits cutoff will prevent infinite looping anyway.

Reasonable values range from 0.01 to 1.0. NewStdEdgeMarkerRasterizer() uses 0.1 by default.

func (*EdgeMarkerRasterizer) SetHighByte

func (self *EdgeMarkerRasterizer) SetHighByte(value uint8)

Satisfies the UserCfgCacheSignature interface.

func (*EdgeMarkerRasterizer) SetMaxCurveSplits

func (self *EdgeMarkerRasterizer) SetMaxCurveSplits(maxCurveSplits int)

Sets the maximum amount of times a curve can be recursively split into subsegments while trying to approximate it.

The maximum number of segments that will approximate a curve is 2^maxCurveSplits.

This value is typically used as a cutoff to prevent low curve thresholds from making the curve splitting process too slow, but it can also be used creatively to get jaggy results instead of smooth curves.

Values outside the [0, 255] range will be silently clamped. Reasonable values range from 0 to 10. NewStdEdgeMarkerRasterizer() uses 8 by default.

func (*EdgeMarkerRasterizer) SetOnChangeFunc

func (self *EdgeMarkerRasterizer) SetOnChangeFunc(onChange func(Rasterizer))

Satisfies the Rasterizer interface.

type FauxRasterizer

type FauxRasterizer struct {
	// contains filtered or unexported fields
}

A rasterizer to draw oblique and faux-bold text. For high quality results, please use the font's italic and bold versions directly instead of these fake effects.

In general, the performance of FauxRasterizer without effects is very similar to DefaultRasterizer. Using reasonable skew factors for oblique text tends to increase the rasterization time around 15%, and using faux-bold increases the rasterization time in 60%, but it depends a lot on how extreme the effects are.

This rasterizer was created mostly to serve as an example of how to create modified rasterizers, featuring both modification of glyph control points (oblique) and post-processing of the generated mask (faux-bold).

func (*FauxRasterizer) CacheSignature

func (self *FauxRasterizer) CacheSignature() uint64

Satisfies the Rasterizer interface. The cache signature for the faux rasterizer has the following shape:

  • 0xFF00000000000000 bits for UserCfgCacheSignature's high byte.
  • 0x00FF000000000000 bits being 0xFA (self signature byte).
  • 0x0000F00000000000 bits being 0x1 if italics are enabled.
  • 0x00000F0000000000 bits being 0xB if bold is enabled.
  • 0x00000000FFFF0000 bits encoding the skewing [-1, 1] as [0, 65535], with the zero skewing not having a representation here (signatures are still different due to the "italics-enabled" flag).
  • 0x000000000000FFFF bits encoding the extra bold width in 64ths of a pixel and encoded as a uint16.

func (*FauxRasterizer) CubeTo

func (self *FauxRasterizer) CubeTo(controlA, controlB, target fixed.Point26_6)

See DefaultRasterizer.CubeTo().

func (*FauxRasterizer) GetExtraWidth

func (self *FauxRasterizer) GetExtraWidth() float64

Gets the extra width (in pixels, possibly fractional) used for the faux-bold style.

func (*FauxRasterizer) GetSkewFactor

func (self *FauxRasterizer) GetSkewFactor() float64

Gets the skewing factor [-1.0, 1.0] used for the oblique style.

func (*FauxRasterizer) LineTo

func (self *FauxRasterizer) LineTo(point fixed.Point26_6)

See DefaultRasterizer.LineTo().

func (*FauxRasterizer) MoveTo

func (self *FauxRasterizer) MoveTo(point fixed.Point26_6)

See DefaultRasterizer.MoveTo().

func (*FauxRasterizer) QuadTo

func (self *FauxRasterizer) QuadTo(control, target fixed.Point26_6)

See DefaultRasterizer.QuadTo().

func (*FauxRasterizer) Rasterize

func (self *FauxRasterizer) Rasterize(outline sfnt.Segments, fract fixed.Point26_6) (*image.Alpha, error)

Satisfies the Rasterizer interface.

func (*FauxRasterizer) SetAuxOnChangeFunc

func (self *FauxRasterizer) SetAuxOnChangeFunc(onChange func(*FauxRasterizer))

Like FauxRasterizer.SetOnChangeFunc, but not reserved for internal Renderer use. This is provided so you can link a custom esizer.Sizer to the rasterizer and get notified when its configuration changes.

func (*FauxRasterizer) SetExtraWidth

func (self *FauxRasterizer) SetExtraWidth(extraWidth float64)

Sets the extra width for the faux-bold. Values outside the [0, 1024] range will be clamped. Fractional values are allowed, but internally the decimal part will be quantized to 1/64ths of a pixel.

Important: when extra width is used for faux-bold, the glyphs will become wider. If you want to adapt the positioning of the glyphs to account for this widening, you can use an esizer.AdvancePadSizer, link the rasterizer to it through SetAuxOnChangeFunc and update the padding with the value of FauxRasterizer.GetExtraWidth(), for example.

func (*FauxRasterizer) SetHighByte

func (self *FauxRasterizer) SetHighByte(value uint8)

Satisfies the UserCfgCacheSignature interface.

func (*FauxRasterizer) SetOnChangeFunc

func (self *FauxRasterizer) SetOnChangeFunc(onChange func(Rasterizer))

Satisfies the Rasterizer interface.

func (*FauxRasterizer) SetSkewFactor

func (self *FauxRasterizer) SetSkewFactor(factor float64)

Sets the oblique skewing factor. Values outside the [-1, 1] range will be clamped. -1 corresponds to a counter-clockwise angle of 45 degrees from the vertical. 1 corresponds to -45 degrees.

In general, most italic fonts have an italic angle between -6 and -9 degrees, which would correspond to skew factors in the [0.13, 0.2] range.

type OutlineRasterizer

type OutlineRasterizer struct {
	// contains filtered or unexported fields
}

NOTICE: work in progress. There are a couple big clipping issues that will easily cause panics, and most likely still a few minor bugs on polygon filling edge cases. Clipping is tricky due to Bézier cusps and similar situations where two lines end up joining at very tight angles, but also in some edge cases where natural path intersections will go outside the intended paths area due to thickness becoming larger than path length and multiple paths colliding and stuff.

The algorithm is also quite slow and there's much room for improvement, but I'm still focusing on the baseline implementation.

In web API terms, the line cap is "butt" and the line joint is "miter".

func NewOutlineRasterizer

func NewOutlineRasterizer(outlineThickness float64) *OutlineRasterizer

func (*OutlineRasterizer) CacheSignature

func (self *OutlineRasterizer) CacheSignature() uint64

Satisfies the Rasterizer interface.

func (*OutlineRasterizer) CubeTo

func (self *OutlineRasterizer) CubeTo(controlA, controlB, target fixed.Point26_6)

Satisfies the unexported vectorTracer interface.

func (*OutlineRasterizer) LineTo

func (self *OutlineRasterizer) LineTo(point fixed.Point26_6)

Satisfies the unexported vectorTracer interface.

func (*OutlineRasterizer) MoveTo

func (self *OutlineRasterizer) MoveTo(point fixed.Point26_6)

Satisfies the unexported vectorTracer interface.

func (*OutlineRasterizer) QuadTo

func (self *OutlineRasterizer) QuadTo(control, target fixed.Point26_6)

Satisfies the unexported vectorTracer interface.

func (*OutlineRasterizer) Rasterize

func (self *OutlineRasterizer) Rasterize(outline sfnt.Segments, fract fixed.Point26_6) (*image.Alpha, error)

Satisfies the Rasterizer interface.

func (*OutlineRasterizer) SetHighByte

func (self *OutlineRasterizer) SetHighByte(value uint8)

Satisfies the UserCfgCacheSignature interface.

func (*OutlineRasterizer) SetMarginFactor

func (self *OutlineRasterizer) SetMarginFactor(factor float64)

When two lines of the outline connect at a tight angle, the resulting vertex may extend far beyond 'thickness' distance. The margin factor allows setting a limit, in multiples of 'thickness', to adjust the paths so they don't extend further away than intended.

The default value is 2. Valid values range from 1 to 16.

func (*OutlineRasterizer) SetOnChangeFunc

func (self *OutlineRasterizer) SetOnChangeFunc(onChange func(Rasterizer))

Satisfies the Rasterizer interface.

func (*OutlineRasterizer) SetThickness

func (self *OutlineRasterizer) SetThickness(thickness float64)

Sets the outline thickness. Values must be in the [0.1, 1024] range.

type Rasterizer

type Rasterizer interface {
	// Rasterizes the given outline to an alpha mask. The outline must be
	// drawn at the given fractional position (always positive coords between
	// 0 and 0:63 (= 0.984375)).
	//
	// Notice that rasterizers might create masks bigger than Segments.Bounds()
	// to account for their own special effects, but they still can't affect
	// glyph bounds or advances (see esizer.Sizer for that).
	Rasterize(sfnt.Segments, fixed.Point26_6) (*image.Alpha, error)

	// The cache signature returns an uint64 that can be used with glyph
	// caches in order to tell rasterizers apart. When using multiple
	// mask rasterizers with a single cache, you normally want to make sure
	// that their cache signatures are different. As a practical standard,
	// implementers of mask rasterizers are encouraged to leave at least
	// the 8 highest bits to be configurable by users through the
	// UserCfgCacheSignature interface.
	CacheSignature() uint64

	// Sets the function to be called when the Rasterizer configuration
	// or cache signature changes. This is a reserved function that only
	// a Renderer should call internally in order to connect its cache
	// handler to the rasterizer changes.
	SetOnChangeFunc(func(Rasterizer))
}

Rasterizer is an interface for 2D vector graphics rasterization to an alpha mask. This interface is offered as an open alternative to the concrete golang.org/x/image/vector.Rasterizer type (as used by golang.org/x/image/font/opentype), allowing anyone to target it and use its own rasterizer for text rendering.

Mask rasterizers can't be used concurrently and must tolerate coordinates out of bounds.

type Shape

type Shape struct {
	// contains filtered or unexported fields
}

A helper type to assist the creation of shapes that can later be converted to sfnt.Segments and rasterized with the Rasterize() method. Notice that this is actually unrelated to fonts, but once you have some rasterizers it's nice to have a way to play with them manually. Notice also that since Rasterize() is a CPU process, working with big shapes (based on their bounding rectangle) can be quite expensive.

Despite what the names of the methods might lead you to believe, shapes are not created by "drawing lines", but rather by defining a set of boundaries that enclose an area. If you get unexpected results using shapes, come back to think about this.

Shapes by themselves do not care about the direction you use to define the segments (clockwise/counter-clockwise), but rasterizers that use the segments most often do. For example, if you define two squares one inside the other, both in the same order (e.g: top-left to top-right, top-right to bottom right...) the rasterized result will be a single square. If you define them following opposite directions, instead, the result will be the difference between the two squares.

func NewShape

func NewShape(commandsCount int) Shape

Creates a new Shape object. The commandsCount is used to indicate the initial capacity of its internal segments buffer.

func (*Shape) CubeTo

func (self *Shape) CubeTo(cx1, cy1, cx2, cy2, x, y int)

Creates a cubic Bézier curve to (x, y) with (cx1, cy1) and (cx2, cy2) as the control points. See golang.org/x/image/vector.Rasterizer operations and golang.org/x/image/font/sfnt.Segment.

func (*Shape) CubeToFract

func (self *Shape) CubeToFract(cx1, cy1, cx2, cy2, x, y fixed.Int26_6)

Like Shape.CubeTo, but with fixed.Int26_6 coordinates.

func (*Shape) HasInvertY

func (self *Shape) HasInvertY() bool

Returns whether Shape.InvertY is active or inactive.

func (*Shape) InvertY

func (self *Shape) InvertY(active bool)

Let's say you want to draw a triangle pointing up, similar to an "A". By default, you would move to (0, 0) and then draw lines to (k, 2*k), (2*k, 0) and back to (0, 0).

If you set InvertY to true, the previous shape will draw a triangle pointing down instead, similar to a "V". This is a convenient flag that makes it easier to work on different contexts (e.g., font glyphs are defined with the ascenders going into the negative y plane).

InvertY can also be used creatively or to switch between clockwise and counter-clockwise directions when drawing symmetrical shapes that have their center at (0, 0).

func (*Shape) LineTo

func (self *Shape) LineTo(x, y int)

Creates a straight boundary from the current position to (x, y). See golang.org/x/image/vector.Rasterizer operations and golang.org/x/image/font/sfnt.Segment.

func (*Shape) LineToFract

func (self *Shape) LineToFract(x, y fixed.Int26_6)

Like Shape.LineTo, but with fixed.Int26_6 coordinates.

func (*Shape) MoveTo

func (self *Shape) MoveTo(x, y int)

Moves the current position to (x, y). See golang.org/x/image/vector.Rasterizer operations and golang.org/x/image/font/sfnt.Segment.

func (*Shape) MoveToFract

func (self *Shape) MoveToFract(x, y fixed.Int26_6)

Like Shape.MoveTo, but with fixed.Int26_6 coordinates.

func (*Shape) Paint

func (self *Shape) Paint(drawColor, backColor color.Color) *image.RGBA

A helper method to rasterize the current shape with the default rasterizer. You could then export the result to a png file, e.g.:

file, _ := os.Create("my_ugly_shape.png")
_ = png.Encode(file, shape.Paint(color.White, color.Black))
// ...maybe even checking errors and closing the file ;)

func (*Shape) QuadTo

func (self *Shape) QuadTo(ctrlX, ctrlY, x, y int)

Creates a quadratic Bézier curve (also known as a conic Bézier curve) to (x, y) with (ctrlX, ctrlY) as the control point. See golang.org/x/image/vector.Rasterizer operations and golang.org/x/image/font/sfnt.Segment.

func (*Shape) QuadToFract

func (self *Shape) QuadToFract(ctrlX, ctrlY, x, y fixed.Int26_6)

Like Shape.QuadTo, but with fixed.Int26_6 coordinates.

func (*Shape) Reset

func (self *Shape) Reset()

Resets the shape segments. Be careful to not be holding the segments from Shape.Segments() when calling this (they may be overriden soon).

func (*Shape) Segments

func (self *Shape) Segments() sfnt.Segments

Gets the shape information as sfnt.Segments. The underlying data is referenced both by the Shape and the sfnt.Segments, so be careful what you do with it.

type UserCfgCacheSignature

type UserCfgCacheSignature interface {
	// Sets the highest byte of 'signature' to the given value, like:
	//   signature = (signature & 0x00FFFFFFFFFFFFFF) | (uint64(value) << 56)
	SetHighByte(value uint8)
}

See Rasterizer's CacheSignature() documentation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL