mirror of
				https://github.com/esimov/pigo.git
				synced 2025-10-31 03:16:25 +08:00 
			
		
		
		
	Include vendor dependencies
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -3,5 +3,4 @@ | |||||||
| *.jpg | *.jpg | ||||||
| *.jpeg | *.jpeg | ||||||
| pigo | pigo | ||||||
| vendor |  | ||||||
| packages | packages | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								vendor/github.com/fogleman/gg/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/fogleman/gg/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | *.png | ||||||
|  |  | ||||||
							
								
								
									
										19
									
								
								vendor/github.com/fogleman/gg/LICENSE.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/fogleman/gg/LICENSE.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | Copyright (C) 2016 Michael Fogleman | ||||||
|  |  | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  |  | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  |  | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | SOFTWARE. | ||||||
							
								
								
									
										220
									
								
								vendor/github.com/fogleman/gg/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								vendor/github.com/fogleman/gg/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,220 @@ | |||||||
|  | # Go Graphics | ||||||
|  |  | ||||||
|  | `gg` is a library for rendering 2D graphics in pure Go. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Installation | ||||||
|  |  | ||||||
|  |     go get github.com/fogleman/gg | ||||||
|  |  | ||||||
|  | ## GoDoc | ||||||
|  |  | ||||||
|  | https://godoc.org/github.com/fogleman/gg | ||||||
|  |  | ||||||
|  | ## Hello, Circle! | ||||||
|  |  | ||||||
|  | Look how easy! | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | package main | ||||||
|  |  | ||||||
|  | import "github.com/fogleman/gg" | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  |     dc := gg.NewContext(1000, 1000) | ||||||
|  |     dc.DrawCircle(500, 500, 400) | ||||||
|  |     dc.SetRGB(0, 0, 0) | ||||||
|  |     dc.Fill() | ||||||
|  |     dc.SavePNG("out.png") | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Examples | ||||||
|  |  | ||||||
|  | There are [lots of examples](https://github.com/fogleman/gg/tree/master/examples) included. They're mostly for testing the code, but they're good for learning, too. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Creating Contexts | ||||||
|  |  | ||||||
|  | There are a few ways of creating a context. | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | NewContext(width, height int) *Context | ||||||
|  | NewContextForImage(im image.Image) *Context | ||||||
|  | NewContextForRGBA(im *image.RGBA) *Context | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Drawing Functions | ||||||
|  |  | ||||||
|  | Ever used a graphics library that didn't have functions for drawing rectangles | ||||||
|  | or circles? What a pain! | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | DrawPoint(x, y, r float64) | ||||||
|  | DrawLine(x1, y1, x2, y2 float64) | ||||||
|  | DrawRectangle(x, y, w, h float64) | ||||||
|  | DrawRoundedRectangle(x, y, w, h, r float64) | ||||||
|  | DrawCircle(x, y, r float64) | ||||||
|  | DrawArc(x, y, r, angle1, angle2 float64) | ||||||
|  | DrawEllipse(x, y, rx, ry float64) | ||||||
|  | DrawEllipticalArc(x, y, rx, ry, angle1, angle2 float64) | ||||||
|  | DrawRegularPolygon(n int, x, y, r, rotation float64) | ||||||
|  | DrawImage(im image.Image, x, y int) | ||||||
|  | DrawImageAnchored(im image.Image, x, y int, ax, ay float64) | ||||||
|  | SetPixel(x, y int) | ||||||
|  |  | ||||||
|  | MoveTo(x, y float64) | ||||||
|  | LineTo(x, y float64) | ||||||
|  | QuadraticTo(x1, y1, x2, y2 float64) | ||||||
|  | CubicTo(x1, y1, x2, y2, x3, y3 float64) | ||||||
|  | ClosePath() | ||||||
|  | ClearPath() | ||||||
|  | NewSubPath() | ||||||
|  |  | ||||||
|  | Clear() | ||||||
|  | Stroke() | ||||||
|  | Fill() | ||||||
|  | StrokePreserve() | ||||||
|  | FillPreserve() | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | It is often desired to center an image at a point. Use `DrawImageAnchored` with `ax` and `ay` set to 0.5 to do this. Use 0 to left or top align. Use 1 to right or bottom align. `DrawStringAnchored` does the same for text, so you don't need to call `MeasureString` yourself. | ||||||
|  |  | ||||||
|  | ## Text Functions | ||||||
|  |  | ||||||
|  | It will even do word wrap for you! | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | DrawString(s string, x, y float64) | ||||||
|  | DrawStringAnchored(s string, x, y, ax, ay float64) | ||||||
|  | DrawStringWrapped(s string, x, y, ax, ay, width, lineSpacing float64, align Align) | ||||||
|  | MeasureString(s string) (w, h float64) | ||||||
|  | WordWrap(s string, w float64) []string | ||||||
|  | SetFontFace(fontFace font.Face) | ||||||
|  | LoadFontFace(path string, points float64) error | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Color Functions | ||||||
|  |  | ||||||
|  | Colors can be set in several different ways for your convenience. | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | SetRGB(r, g, b float64) | ||||||
|  | SetRGBA(r, g, b, a float64) | ||||||
|  | SetRGB255(r, g, b int) | ||||||
|  | SetRGBA255(r, g, b, a int) | ||||||
|  | SetColor(c color.Color) | ||||||
|  | SetHexColor(x string) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Stroke & Fill Options | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | SetLineWidth(lineWidth float64) | ||||||
|  | SetLineCap(lineCap LineCap) | ||||||
|  | SetLineJoin(lineJoin LineJoin) | ||||||
|  | SetDash(dashes ...float64) | ||||||
|  | SetFillRule(fillRule FillRule) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Gradients & Patterns | ||||||
|  |  | ||||||
|  | `gg` supports linear and radial gradients and surface patterns. You can also implement your own patterns. | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | SetFillStyle(pattern Pattern) | ||||||
|  | SetStrokeStyle(pattern Pattern) | ||||||
|  | NewSolidPattern(color color.Color) | ||||||
|  | NewLinearGradient(x0, y0, x1, y1 float64) | ||||||
|  | NewRadialGradient(x0, y0, r0, x1, y1, r1 float64) | ||||||
|  | NewSurfacePattern(im image.Image, op RepeatOp) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Transformation Functions | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | Identity() | ||||||
|  | Translate(x, y float64) | ||||||
|  | Scale(x, y float64) | ||||||
|  | Rotate(angle float64) | ||||||
|  | Shear(x, y float64) | ||||||
|  | ScaleAbout(sx, sy, x, y float64) | ||||||
|  | RotateAbout(angle, x, y float64) | ||||||
|  | ShearAbout(sx, sy, x, y float64) | ||||||
|  | TransformPoint(x, y float64) (tx, ty float64) | ||||||
|  | InvertY() | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | It is often desired to rotate or scale about a point that is not the origin. The functions `RotateAbout`, `ScaleAbout`, `ShearAbout` are provided as a convenience. | ||||||
|  |  | ||||||
|  | `InvertY` is provided in case Y should increase from bottom to top vs. the default top to bottom. | ||||||
|  |  | ||||||
|  | Note: transforms do not currently affect `DrawImage` or `DrawString`. | ||||||
|  |  | ||||||
|  | ## Stack Functions | ||||||
|  |  | ||||||
|  | Save and restore the state of the context. These can be nested. | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | Push() | ||||||
|  | Pop() | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Clipping Functions | ||||||
|  |  | ||||||
|  | Use clipping regions to restrict drawing operations to an area that you | ||||||
|  | defined using paths. | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | Clip() | ||||||
|  | ClipPreserve() | ||||||
|  | ResetClip() | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Helper Functions | ||||||
|  |  | ||||||
|  | Sometimes you just don't want to write these yourself. | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | Radians(degrees float64) float64 | ||||||
|  | Degrees(radians float64) float64 | ||||||
|  | LoadImage(path string) (image.Image, error) | ||||||
|  | LoadPNG(path string) (image.Image, error) | ||||||
|  | SavePNG(path string, im image.Image) error | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## How Do it Do? | ||||||
|  |  | ||||||
|  | `gg` is mostly a wrapper around `github.com/golang/freetype/raster`. The goal | ||||||
|  | is to provide some more functionality and a nicer API that will suffice for | ||||||
|  | most use cases. | ||||||
|  |  | ||||||
|  | ## Another Example | ||||||
|  |  | ||||||
|  | See the output of this example below. | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | package main | ||||||
|  |  | ||||||
|  | import "github.com/fogleman/gg" | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	const S = 1024 | ||||||
|  | 	dc := gg.NewContext(S, S) | ||||||
|  | 	dc.SetRGBA(0, 0, 0, 0.1) | ||||||
|  | 	for i := 0; i < 360; i += 15 { | ||||||
|  | 		dc.Push() | ||||||
|  | 		dc.RotateAbout(gg.Radians(float64(i)), S/2, S/2) | ||||||
|  | 		dc.DrawEllipse(S/2, S/2, S*7/16, S/8) | ||||||
|  | 		dc.Fill() | ||||||
|  | 		dc.Pop() | ||||||
|  | 	} | ||||||
|  | 	dc.SavePNG("out.png") | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										59
									
								
								vendor/github.com/fogleman/gg/bezier.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								vendor/github.com/fogleman/gg/bezier.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | |||||||
|  | package gg | ||||||
|  |  | ||||||
|  | import "math" | ||||||
|  |  | ||||||
|  | func quadratic(x0, y0, x1, y1, x2, y2, t float64) (x, y float64) { | ||||||
|  | 	u := 1 - t | ||||||
|  | 	a := u * u | ||||||
|  | 	b := 2 * u * t | ||||||
|  | 	c := t * t | ||||||
|  | 	x = a*x0 + b*x1 + c*x2 | ||||||
|  | 	y = a*y0 + b*y1 + c*y2 | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func QuadraticBezier(x0, y0, x1, y1, x2, y2 float64) []Point { | ||||||
|  | 	l := (math.Hypot(x1-x0, y1-y0) + | ||||||
|  | 		math.Hypot(x2-x1, y2-y1)) | ||||||
|  | 	n := int(l + 0.5) | ||||||
|  | 	if n < 4 { | ||||||
|  | 		n = 4 | ||||||
|  | 	} | ||||||
|  | 	d := float64(n) - 1 | ||||||
|  | 	result := make([]Point, n) | ||||||
|  | 	for i := 0; i < n; i++ { | ||||||
|  | 		t := float64(i) / d | ||||||
|  | 		x, y := quadratic(x0, y0, x1, y1, x2, y2, t) | ||||||
|  | 		result[i] = Point{x, y} | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func cubic(x0, y0, x1, y1, x2, y2, x3, y3, t float64) (x, y float64) { | ||||||
|  | 	u := 1 - t | ||||||
|  | 	a := u * u * u | ||||||
|  | 	b := 3 * u * u * t | ||||||
|  | 	c := 3 * u * t * t | ||||||
|  | 	d := t * t * t | ||||||
|  | 	x = a*x0 + b*x1 + c*x2 + d*x3 | ||||||
|  | 	y = a*y0 + b*y1 + c*y2 + d*y3 | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func CubicBezier(x0, y0, x1, y1, x2, y2, x3, y3 float64) []Point { | ||||||
|  | 	l := (math.Hypot(x1-x0, y1-y0) + | ||||||
|  | 		math.Hypot(x2-x1, y2-y1) + | ||||||
|  | 		math.Hypot(x3-x2, y3-y2)) | ||||||
|  | 	n := int(l + 0.5) | ||||||
|  | 	if n < 4 { | ||||||
|  | 		n = 4 | ||||||
|  | 	} | ||||||
|  | 	d := float64(n) - 1 | ||||||
|  | 	result := make([]Point, n) | ||||||
|  | 	for i := 0; i < n; i++ { | ||||||
|  | 		t := float64(i) / d | ||||||
|  | 		x, y := cubic(x0, y0, x1, y1, x2, y2, x3, y3, t) | ||||||
|  | 		result[i] = Point{x, y} | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
							
								
								
									
										768
									
								
								vendor/github.com/fogleman/gg/context.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										768
									
								
								vendor/github.com/fogleman/gg/context.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,768 @@ | |||||||
|  | // Package gg provides a simple API for rendering 2D graphics in pure Go. | ||||||
|  | package gg | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"image" | ||||||
|  | 	"image/color" | ||||||
|  | 	"image/draw" | ||||||
|  | 	"image/png" | ||||||
|  | 	"io" | ||||||
|  | 	"math" | ||||||
|  |  | ||||||
|  | 	"github.com/golang/freetype/raster" | ||||||
|  | 	"golang.org/x/image/font" | ||||||
|  | 	"golang.org/x/image/font/basicfont" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type LineCap int | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	LineCapRound LineCap = iota | ||||||
|  | 	LineCapButt | ||||||
|  | 	LineCapSquare | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type LineJoin int | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	LineJoinRound LineJoin = iota | ||||||
|  | 	LineJoinBevel | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type FillRule int | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	FillRuleWinding FillRule = iota | ||||||
|  | 	FillRuleEvenOdd | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Align int | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	AlignLeft Align = iota | ||||||
|  | 	AlignCenter | ||||||
|  | 	AlignRight | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	defaultFillStyle   = NewSolidPattern(color.White) | ||||||
|  | 	defaultStrokeStyle = NewSolidPattern(color.Black) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Context struct { | ||||||
|  | 	width         int | ||||||
|  | 	height        int | ||||||
|  | 	im            *image.RGBA | ||||||
|  | 	mask          *image.Alpha | ||||||
|  | 	color         color.Color | ||||||
|  | 	fillPattern   Pattern | ||||||
|  | 	strokePattern Pattern | ||||||
|  | 	strokePath    raster.Path | ||||||
|  | 	fillPath      raster.Path | ||||||
|  | 	start         Point | ||||||
|  | 	current       Point | ||||||
|  | 	hasCurrent    bool | ||||||
|  | 	dashes        []float64 | ||||||
|  | 	lineWidth     float64 | ||||||
|  | 	lineCap       LineCap | ||||||
|  | 	lineJoin      LineJoin | ||||||
|  | 	fillRule      FillRule | ||||||
|  | 	fontFace      font.Face | ||||||
|  | 	fontHeight    float64 | ||||||
|  | 	matrix        Matrix | ||||||
|  | 	stack         []*Context | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewContext creates a new image.RGBA with the specified width and height | ||||||
|  | // and prepares a context for rendering onto that image. | ||||||
|  | func NewContext(width, height int) *Context { | ||||||
|  | 	return NewContextForRGBA(image.NewRGBA(image.Rect(0, 0, width, height))) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewContextForImage copies the specified image into a new image.RGBA | ||||||
|  | // and prepares a context for rendering onto that image. | ||||||
|  | func NewContextForImage(im image.Image) *Context { | ||||||
|  | 	return NewContextForRGBA(imageToRGBA(im)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewContextForRGBA prepares a context for rendering onto the specified image. | ||||||
|  | // No copy is made. | ||||||
|  | func NewContextForRGBA(im *image.RGBA) *Context { | ||||||
|  | 	return &Context{ | ||||||
|  | 		width:         im.Bounds().Size().X, | ||||||
|  | 		height:        im.Bounds().Size().Y, | ||||||
|  | 		im:            im, | ||||||
|  | 		color:         color.Transparent, | ||||||
|  | 		fillPattern:   defaultFillStyle, | ||||||
|  | 		strokePattern: defaultStrokeStyle, | ||||||
|  | 		lineWidth:     1, | ||||||
|  | 		fillRule:      FillRuleWinding, | ||||||
|  | 		fontFace:      basicfont.Face7x13, | ||||||
|  | 		fontHeight:    13, | ||||||
|  | 		matrix:        Identity(), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Image returns the image that has been drawn by this context. | ||||||
|  | func (dc *Context) Image() image.Image { | ||||||
|  | 	return dc.im | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Width returns the width of the image in pixels. | ||||||
|  | func (dc *Context) Width() int { | ||||||
|  | 	return dc.width | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Height returns the height of the image in pixels. | ||||||
|  | func (dc *Context) Height() int { | ||||||
|  | 	return dc.height | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SavePNG encodes the image as a PNG and writes it to disk. | ||||||
|  | func (dc *Context) SavePNG(path string) error { | ||||||
|  | 	return SavePNG(path, dc.im) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // EncodePNG encodes the image as a PNG and writes it to the provided io.Writer. | ||||||
|  | func (dc *Context) EncodePNG(w io.Writer) error { | ||||||
|  | 	return png.Encode(w, dc.im) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetDash sets the current dash pattern to use. Call with zero arguments to | ||||||
|  | // disable dashes. The values specify the lengths of each dash, with | ||||||
|  | // alternating on and off lengths. | ||||||
|  | func (dc *Context) SetDash(dashes ...float64) { | ||||||
|  | 	dc.dashes = dashes | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) SetLineWidth(lineWidth float64) { | ||||||
|  | 	dc.lineWidth = lineWidth | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) SetLineCap(lineCap LineCap) { | ||||||
|  | 	dc.lineCap = lineCap | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) SetLineCapRound() { | ||||||
|  | 	dc.lineCap = LineCapRound | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) SetLineCapButt() { | ||||||
|  | 	dc.lineCap = LineCapButt | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) SetLineCapSquare() { | ||||||
|  | 	dc.lineCap = LineCapSquare | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) SetLineJoin(lineJoin LineJoin) { | ||||||
|  | 	dc.lineJoin = lineJoin | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) SetLineJoinRound() { | ||||||
|  | 	dc.lineJoin = LineJoinRound | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) SetLineJoinBevel() { | ||||||
|  | 	dc.lineJoin = LineJoinBevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) SetFillRule(fillRule FillRule) { | ||||||
|  | 	dc.fillRule = fillRule | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) SetFillRuleWinding() { | ||||||
|  | 	dc.fillRule = FillRuleWinding | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) SetFillRuleEvenOdd() { | ||||||
|  | 	dc.fillRule = FillRuleEvenOdd | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Color Setters | ||||||
|  |  | ||||||
|  | func (dc *Context) setFillAndStrokeColor(c color.Color) { | ||||||
|  | 	dc.color = c | ||||||
|  | 	dc.fillPattern = NewSolidPattern(c) | ||||||
|  | 	dc.strokePattern = NewSolidPattern(c) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetFillStyle sets current fill style | ||||||
|  | func (dc *Context) SetFillStyle(pattern Pattern) { | ||||||
|  | 	// if pattern is SolidPattern, also change dc.color(for dc.Clear, dc.drawString) | ||||||
|  | 	if fillStyle, ok := pattern.(*solidPattern); ok { | ||||||
|  | 		dc.color = fillStyle.color | ||||||
|  | 	} | ||||||
|  | 	dc.fillPattern = pattern | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetStrokeStyle sets current stroke style | ||||||
|  | func (dc *Context) SetStrokeStyle(pattern Pattern) { | ||||||
|  | 	dc.strokePattern = pattern | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetColor sets the current color(for both fill and stroke). | ||||||
|  | func (dc *Context) SetColor(c color.Color) { | ||||||
|  | 	dc.setFillAndStrokeColor(c) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetHexColor sets the current color using a hex string. The leading pound | ||||||
|  | // sign (#) is optional. Both 3- and 6-digit variations are supported. 8 digits | ||||||
|  | // may be provided to set the alpha value as well. | ||||||
|  | func (dc *Context) SetHexColor(x string) { | ||||||
|  | 	r, g, b, a := parseHexColor(x) | ||||||
|  | 	dc.SetRGBA255(r, g, b, a) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetRGBA255 sets the current color. r, g, b, a values should be between 0 and | ||||||
|  | // 255, inclusive. | ||||||
|  | func (dc *Context) SetRGBA255(r, g, b, a int) { | ||||||
|  | 	dc.color = color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)} | ||||||
|  | 	dc.setFillAndStrokeColor(dc.color) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetRGB255 sets the current color. r, g, b values should be between 0 and 255, | ||||||
|  | // inclusive. Alpha will be set to 255 (fully opaque). | ||||||
|  | func (dc *Context) SetRGB255(r, g, b int) { | ||||||
|  | 	dc.SetRGBA255(r, g, b, 255) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetRGBA sets the current color. r, g, b, a values should be between 0 and 1, | ||||||
|  | // inclusive. | ||||||
|  | func (dc *Context) SetRGBA(r, g, b, a float64) { | ||||||
|  | 	dc.color = color.NRGBA{ | ||||||
|  | 		uint8(r * 255), | ||||||
|  | 		uint8(g * 255), | ||||||
|  | 		uint8(b * 255), | ||||||
|  | 		uint8(a * 255), | ||||||
|  | 	} | ||||||
|  | 	dc.setFillAndStrokeColor(dc.color) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetRGB sets the current color. r, g, b values should be between 0 and 1, | ||||||
|  | // inclusive. Alpha will be set to 1 (fully opaque). | ||||||
|  | func (dc *Context) SetRGB(r, g, b float64) { | ||||||
|  | 	dc.SetRGBA(r, g, b, 1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Path Manipulation | ||||||
|  |  | ||||||
|  | // MoveTo starts a new subpath within the current path starting at the | ||||||
|  | // specified point. | ||||||
|  | func (dc *Context) MoveTo(x, y float64) { | ||||||
|  | 	if dc.hasCurrent { | ||||||
|  | 		dc.fillPath.Add1(dc.start.Fixed()) | ||||||
|  | 	} | ||||||
|  | 	x, y = dc.TransformPoint(x, y) | ||||||
|  | 	p := Point{x, y} | ||||||
|  | 	dc.strokePath.Start(p.Fixed()) | ||||||
|  | 	dc.fillPath.Start(p.Fixed()) | ||||||
|  | 	dc.start = p | ||||||
|  | 	dc.current = p | ||||||
|  | 	dc.hasCurrent = true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LineTo adds a line segment to the current path starting at the current | ||||||
|  | // point. If there is no current point, it is equivalent to MoveTo(x, y) | ||||||
|  | func (dc *Context) LineTo(x, y float64) { | ||||||
|  | 	if !dc.hasCurrent { | ||||||
|  | 		dc.MoveTo(x, y) | ||||||
|  | 	} else { | ||||||
|  | 		x, y = dc.TransformPoint(x, y) | ||||||
|  | 		p := Point{x, y} | ||||||
|  | 		dc.strokePath.Add1(p.Fixed()) | ||||||
|  | 		dc.fillPath.Add1(p.Fixed()) | ||||||
|  | 		dc.current = p | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // QuadraticTo adds a quadratic bezier curve to the current path starting at | ||||||
|  | // the current point. If there is no current point, it first performs | ||||||
|  | // MoveTo(x1, y1) | ||||||
|  | func (dc *Context) QuadraticTo(x1, y1, x2, y2 float64) { | ||||||
|  | 	if !dc.hasCurrent { | ||||||
|  | 		dc.MoveTo(x1, y1) | ||||||
|  | 	} | ||||||
|  | 	x1, y1 = dc.TransformPoint(x1, y1) | ||||||
|  | 	x2, y2 = dc.TransformPoint(x2, y2) | ||||||
|  | 	p1 := Point{x1, y1} | ||||||
|  | 	p2 := Point{x2, y2} | ||||||
|  | 	dc.strokePath.Add2(p1.Fixed(), p2.Fixed()) | ||||||
|  | 	dc.fillPath.Add2(p1.Fixed(), p2.Fixed()) | ||||||
|  | 	dc.current = p2 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CubicTo adds a cubic bezier curve to the current path starting at the | ||||||
|  | // current point. If there is no current point, it first performs | ||||||
|  | // MoveTo(x1, y1). Because freetype/raster does not support cubic beziers, | ||||||
|  | // this is emulated with many small line segments. | ||||||
|  | func (dc *Context) CubicTo(x1, y1, x2, y2, x3, y3 float64) { | ||||||
|  | 	if !dc.hasCurrent { | ||||||
|  | 		dc.MoveTo(x1, y1) | ||||||
|  | 	} | ||||||
|  | 	x0, y0 := dc.current.X, dc.current.Y | ||||||
|  | 	x1, y1 = dc.TransformPoint(x1, y1) | ||||||
|  | 	x2, y2 = dc.TransformPoint(x2, y2) | ||||||
|  | 	x3, y3 = dc.TransformPoint(x3, y3) | ||||||
|  | 	points := CubicBezier(x0, y0, x1, y1, x2, y2, x3, y3) | ||||||
|  | 	previous := dc.current.Fixed() | ||||||
|  | 	for _, p := range points[1:] { | ||||||
|  | 		f := p.Fixed() | ||||||
|  | 		if f == previous { | ||||||
|  | 			// TODO: this fixes some rendering issues but not all | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		previous = f | ||||||
|  | 		dc.strokePath.Add1(f) | ||||||
|  | 		dc.fillPath.Add1(f) | ||||||
|  | 		dc.current = p | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ClosePath adds a line segment from the current point to the beginning | ||||||
|  | // of the current subpath. If there is no current point, this is a no-op. | ||||||
|  | func (dc *Context) ClosePath() { | ||||||
|  | 	if dc.hasCurrent { | ||||||
|  | 		dc.strokePath.Add1(dc.start.Fixed()) | ||||||
|  | 		dc.fillPath.Add1(dc.start.Fixed()) | ||||||
|  | 		dc.current = dc.start | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ClearPath clears the current path. There is no current point after this | ||||||
|  | // operation. | ||||||
|  | func (dc *Context) ClearPath() { | ||||||
|  | 	dc.strokePath.Clear() | ||||||
|  | 	dc.fillPath.Clear() | ||||||
|  | 	dc.hasCurrent = false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewSubPath starts a new subpath within the current path. There is no current | ||||||
|  | // point after this operation. | ||||||
|  | func (dc *Context) NewSubPath() { | ||||||
|  | 	if dc.hasCurrent { | ||||||
|  | 		dc.fillPath.Add1(dc.start.Fixed()) | ||||||
|  | 	} | ||||||
|  | 	dc.hasCurrent = false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Path Drawing | ||||||
|  |  | ||||||
|  | func (dc *Context) capper() raster.Capper { | ||||||
|  | 	switch dc.lineCap { | ||||||
|  | 	case LineCapButt: | ||||||
|  | 		return raster.ButtCapper | ||||||
|  | 	case LineCapRound: | ||||||
|  | 		return raster.RoundCapper | ||||||
|  | 	case LineCapSquare: | ||||||
|  | 		return raster.SquareCapper | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) joiner() raster.Joiner { | ||||||
|  | 	switch dc.lineJoin { | ||||||
|  | 	case LineJoinBevel: | ||||||
|  | 		return raster.BevelJoiner | ||||||
|  | 	case LineJoinRound: | ||||||
|  | 		return raster.RoundJoiner | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) stroke(painter raster.Painter) { | ||||||
|  | 	path := dc.strokePath | ||||||
|  | 	if len(dc.dashes) > 0 { | ||||||
|  | 		path = dashed(path, dc.dashes) | ||||||
|  | 	} else { | ||||||
|  | 		// TODO: this is a temporary workaround to remove tiny segments | ||||||
|  | 		// that result in rendering issues | ||||||
|  | 		path = rasterPath(flattenPath(path)) | ||||||
|  | 	} | ||||||
|  | 	r := raster.NewRasterizer(dc.width, dc.height) | ||||||
|  | 	r.UseNonZeroWinding = true | ||||||
|  | 	r.AddStroke(path, fix(dc.lineWidth), dc.capper(), dc.joiner()) | ||||||
|  | 	r.Rasterize(painter) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) fill(painter raster.Painter) { | ||||||
|  | 	path := dc.fillPath | ||||||
|  | 	if dc.hasCurrent { | ||||||
|  | 		path = make(raster.Path, len(dc.fillPath)) | ||||||
|  | 		copy(path, dc.fillPath) | ||||||
|  | 		path.Add1(dc.start.Fixed()) | ||||||
|  | 	} | ||||||
|  | 	r := raster.NewRasterizer(dc.width, dc.height) | ||||||
|  | 	r.UseNonZeroWinding = dc.fillRule == FillRuleWinding | ||||||
|  | 	r.AddPath(path) | ||||||
|  | 	r.Rasterize(painter) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // StrokePreserve strokes the current path with the current color, line width, | ||||||
|  | // line cap, line join and dash settings. The path is preserved after this | ||||||
|  | // operation. | ||||||
|  | func (dc *Context) StrokePreserve() { | ||||||
|  | 	painter := newPatternPainter(dc.im, dc.mask, dc.strokePattern) | ||||||
|  | 	dc.stroke(painter) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Stroke strokes the current path with the current color, line width, | ||||||
|  | // line cap, line join and dash settings. The path is cleared after this | ||||||
|  | // operation. | ||||||
|  | func (dc *Context) Stroke() { | ||||||
|  | 	dc.StrokePreserve() | ||||||
|  | 	dc.ClearPath() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // FillPreserve fills the current path with the current color. Open subpaths | ||||||
|  | // are implicity closed. The path is preserved after this operation. | ||||||
|  | func (dc *Context) FillPreserve() { | ||||||
|  | 	painter := newPatternPainter(dc.im, dc.mask, dc.fillPattern) | ||||||
|  | 	dc.fill(painter) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Fill fills the current path with the current color. Open subpaths | ||||||
|  | // are implicity closed. The path is cleared after this operation. | ||||||
|  | func (dc *Context) Fill() { | ||||||
|  | 	dc.FillPreserve() | ||||||
|  | 	dc.ClearPath() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ClipPreserve updates the clipping region by intersecting the current | ||||||
|  | // clipping region with the current path as it would be filled by dc.Fill(). | ||||||
|  | // The path is preserved after this operation. | ||||||
|  | func (dc *Context) ClipPreserve() { | ||||||
|  | 	clip := image.NewAlpha(image.Rect(0, 0, dc.width, dc.height)) | ||||||
|  | 	painter := raster.NewAlphaOverPainter(clip) | ||||||
|  | 	dc.fill(painter) | ||||||
|  | 	if dc.mask == nil { | ||||||
|  | 		dc.mask = clip | ||||||
|  | 	} else { | ||||||
|  | 		mask := image.NewAlpha(image.Rect(0, 0, dc.width, dc.height)) | ||||||
|  | 		draw.DrawMask(mask, mask.Bounds(), clip, image.ZP, dc.mask, image.ZP, draw.Over) | ||||||
|  | 		dc.mask = mask | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Clip updates the clipping region by intersecting the current | ||||||
|  | // clipping region with the current path as it would be filled by dc.Fill(). | ||||||
|  | // The path is cleared after this operation. | ||||||
|  | func (dc *Context) Clip() { | ||||||
|  | 	dc.ClipPreserve() | ||||||
|  | 	dc.ClearPath() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ResetClip clears the clipping region. | ||||||
|  | func (dc *Context) ResetClip() { | ||||||
|  | 	dc.mask = nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Convenient Drawing Functions | ||||||
|  |  | ||||||
|  | // Clear fills the entire image with the current color. | ||||||
|  | func (dc *Context) Clear() { | ||||||
|  | 	src := image.NewUniform(dc.color) | ||||||
|  | 	draw.Draw(dc.im, dc.im.Bounds(), src, image.ZP, draw.Src) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetPixel sets the color of the specified pixel using the current color. | ||||||
|  | func (dc *Context) SetPixel(x, y int) { | ||||||
|  | 	dc.im.Set(x, y, dc.color) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DrawPoint is like DrawCircle but ensures that a circle of the specified | ||||||
|  | // size is drawn regardless of the current transformation matrix. The position | ||||||
|  | // is still transformed, but not the shape of the point. | ||||||
|  | func (dc *Context) DrawPoint(x, y, r float64) { | ||||||
|  | 	dc.Push() | ||||||
|  | 	tx, ty := dc.TransformPoint(x, y) | ||||||
|  | 	dc.Identity() | ||||||
|  | 	dc.DrawCircle(tx, ty, r) | ||||||
|  | 	dc.Pop() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) DrawLine(x1, y1, x2, y2 float64) { | ||||||
|  | 	dc.MoveTo(x1, y1) | ||||||
|  | 	dc.LineTo(x2, y2) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) DrawRectangle(x, y, w, h float64) { | ||||||
|  | 	dc.NewSubPath() | ||||||
|  | 	dc.MoveTo(x, y) | ||||||
|  | 	dc.LineTo(x+w, y) | ||||||
|  | 	dc.LineTo(x+w, y+h) | ||||||
|  | 	dc.LineTo(x, y+h) | ||||||
|  | 	dc.ClosePath() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) DrawRoundedRectangle(x, y, w, h, r float64) { | ||||||
|  | 	x0, x1, x2, x3 := x, x+r, x+w-r, x+w | ||||||
|  | 	y0, y1, y2, y3 := y, y+r, y+h-r, y+h | ||||||
|  | 	dc.NewSubPath() | ||||||
|  | 	dc.MoveTo(x1, y0) | ||||||
|  | 	dc.LineTo(x2, y0) | ||||||
|  | 	dc.DrawArc(x2, y1, r, Radians(270), Radians(360)) | ||||||
|  | 	dc.LineTo(x3, y2) | ||||||
|  | 	dc.DrawArc(x2, y2, r, Radians(0), Radians(90)) | ||||||
|  | 	dc.LineTo(x1, y3) | ||||||
|  | 	dc.DrawArc(x1, y2, r, Radians(90), Radians(180)) | ||||||
|  | 	dc.LineTo(x0, y1) | ||||||
|  | 	dc.DrawArc(x1, y1, r, Radians(180), Radians(270)) | ||||||
|  | 	dc.ClosePath() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) DrawEllipticalArc(x, y, rx, ry, angle1, angle2 float64) { | ||||||
|  | 	const n = 16 | ||||||
|  | 	for i := 0; i < n; i++ { | ||||||
|  | 		p1 := float64(i+0) / n | ||||||
|  | 		p2 := float64(i+1) / n | ||||||
|  | 		a1 := angle1 + (angle2-angle1)*p1 | ||||||
|  | 		a2 := angle1 + (angle2-angle1)*p2 | ||||||
|  | 		x0 := x + rx*math.Cos(a1) | ||||||
|  | 		y0 := y + ry*math.Sin(a1) | ||||||
|  | 		x1 := x + rx*math.Cos(a1+(a2-a1)/2) | ||||||
|  | 		y1 := y + ry*math.Sin(a1+(a2-a1)/2) | ||||||
|  | 		x2 := x + rx*math.Cos(a2) | ||||||
|  | 		y2 := y + ry*math.Sin(a2) | ||||||
|  | 		cx := 2*x1 - x0/2 - x2/2 | ||||||
|  | 		cy := 2*y1 - y0/2 - y2/2 | ||||||
|  | 		if i == 0 && !dc.hasCurrent { | ||||||
|  | 			dc.MoveTo(x0, y0) | ||||||
|  | 		} | ||||||
|  | 		dc.QuadraticTo(cx, cy, x2, y2) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) DrawEllipse(x, y, rx, ry float64) { | ||||||
|  | 	dc.NewSubPath() | ||||||
|  | 	dc.DrawEllipticalArc(x, y, rx, ry, 0, 2*math.Pi) | ||||||
|  | 	dc.ClosePath() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) DrawArc(x, y, r, angle1, angle2 float64) { | ||||||
|  | 	dc.DrawEllipticalArc(x, y, r, r, angle1, angle2) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) DrawCircle(x, y, r float64) { | ||||||
|  | 	dc.NewSubPath() | ||||||
|  | 	dc.DrawEllipticalArc(x, y, r, r, 0, 2*math.Pi) | ||||||
|  | 	dc.ClosePath() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) DrawRegularPolygon(n int, x, y, r, rotation float64) { | ||||||
|  | 	angle := 2 * math.Pi / float64(n) | ||||||
|  | 	rotation -= math.Pi / 2 | ||||||
|  | 	if n%2 == 0 { | ||||||
|  | 		rotation += angle / 2 | ||||||
|  | 	} | ||||||
|  | 	dc.NewSubPath() | ||||||
|  | 	for i := 0; i < n; i++ { | ||||||
|  | 		a := rotation + angle*float64(i) | ||||||
|  | 		dc.LineTo(x+r*math.Cos(a), y+r*math.Sin(a)) | ||||||
|  | 	} | ||||||
|  | 	dc.ClosePath() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DrawImage draws the specified image at the specified point. | ||||||
|  | // Currently, rotation and scaling transforms are not supported. | ||||||
|  | func (dc *Context) DrawImage(im image.Image, x, y int) { | ||||||
|  | 	dc.DrawImageAnchored(im, x, y, 0, 0) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DrawImageAnchored draws the specified image at the specified anchor point. | ||||||
|  | // The anchor point is x - w * ax, y - h * ay, where w, h is the size of the | ||||||
|  | // image. Use ax=0.5, ay=0.5 to center the image at the specified point. | ||||||
|  | func (dc *Context) DrawImageAnchored(im image.Image, x, y int, ax, ay float64) { | ||||||
|  | 	s := im.Bounds().Size() | ||||||
|  | 	x -= int(ax * float64(s.X)) | ||||||
|  | 	y -= int(ay * float64(s.Y)) | ||||||
|  | 	p := image.Pt(x, y) | ||||||
|  | 	r := image.Rectangle{p, p.Add(s)} | ||||||
|  | 	if dc.mask == nil { | ||||||
|  | 		draw.Draw(dc.im, r, im, image.ZP, draw.Over) | ||||||
|  | 	} else { | ||||||
|  | 		draw.DrawMask(dc.im, r, im, image.ZP, dc.mask, p, draw.Over) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Text Functions | ||||||
|  |  | ||||||
|  | func (dc *Context) SetFontFace(fontFace font.Face) { | ||||||
|  | 	dc.fontFace = fontFace | ||||||
|  | 	dc.fontHeight = float64(fontFace.Metrics().Height) / 64 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) LoadFontFace(path string, points float64) error { | ||||||
|  | 	face, err := LoadFontFace(path, points) | ||||||
|  | 	if err == nil { | ||||||
|  | 		dc.fontFace = face | ||||||
|  | 		dc.fontHeight = points * 72 / 96 | ||||||
|  | 	} | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *Context) drawString(im *image.RGBA, s string, x, y float64) { | ||||||
|  | 	d := &font.Drawer{ | ||||||
|  | 		Dst:  im, | ||||||
|  | 		Src:  image.NewUniform(dc.color), | ||||||
|  | 		Face: dc.fontFace, | ||||||
|  | 		Dot:  fixp(x, y), | ||||||
|  | 	} | ||||||
|  | 	d.DrawString(s) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DrawString draws the specified text at the specified point. | ||||||
|  | // Currently, rotation and scaling transforms are not supported. | ||||||
|  | func (dc *Context) DrawString(s string, x, y float64) { | ||||||
|  | 	dc.DrawStringAnchored(s, x, y, 0, 0) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DrawStringAnchored draws the specified text at the specified anchor point. | ||||||
|  | // The anchor point is x - w * ax, y - h * ay, where w, h is the size of the | ||||||
|  | // text. Use ax=0.5, ay=0.5 to center the text at the specified point. | ||||||
|  | func (dc *Context) DrawStringAnchored(s string, x, y, ax, ay float64) { | ||||||
|  | 	w, h := dc.MeasureString(s) | ||||||
|  | 	x, y = dc.TransformPoint(x, y) | ||||||
|  | 	x -= ax * w | ||||||
|  | 	y += ay * h | ||||||
|  | 	if dc.mask == nil { | ||||||
|  | 		dc.drawString(dc.im, s, x, y) | ||||||
|  | 	} else { | ||||||
|  | 		im := image.NewRGBA(image.Rect(0, 0, dc.width, dc.height)) | ||||||
|  | 		dc.drawString(im, s, x, y) | ||||||
|  | 		draw.DrawMask(dc.im, dc.im.Bounds(), im, image.ZP, dc.mask, image.ZP, draw.Over) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DrawStringWrapped word-wraps the specified string to the given max width | ||||||
|  | // and then draws it at the specified anchor point using the given line | ||||||
|  | // spacing and text alignment. | ||||||
|  | func (dc *Context) DrawStringWrapped(s string, x, y, ax, ay, width, lineSpacing float64, align Align) { | ||||||
|  | 	lines := dc.WordWrap(s, width) | ||||||
|  | 	h := float64(len(lines)) * dc.fontHeight * lineSpacing | ||||||
|  | 	h -= (lineSpacing - 1) * dc.fontHeight | ||||||
|  | 	x -= ax * width | ||||||
|  | 	y -= ay * h | ||||||
|  | 	switch align { | ||||||
|  | 	case AlignLeft: | ||||||
|  | 		ax = 0 | ||||||
|  | 	case AlignCenter: | ||||||
|  | 		ax = 0.5 | ||||||
|  | 		x += width / 2 | ||||||
|  | 	case AlignRight: | ||||||
|  | 		ax = 1 | ||||||
|  | 		x += width | ||||||
|  | 	} | ||||||
|  | 	ay = 1 | ||||||
|  | 	for _, line := range lines { | ||||||
|  | 		dc.DrawStringAnchored(line, x, y, ax, ay) | ||||||
|  | 		y += dc.fontHeight * lineSpacing | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MeasureString returns the rendered width and height of the specified text | ||||||
|  | // given the current font face. | ||||||
|  | func (dc *Context) MeasureString(s string) (w, h float64) { | ||||||
|  | 	d := &font.Drawer{ | ||||||
|  | 		Face: dc.fontFace, | ||||||
|  | 	} | ||||||
|  | 	a := d.MeasureString(s) | ||||||
|  | 	return float64(a >> 6), dc.fontHeight | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WordWrap wraps the specified string to the given max width and current | ||||||
|  | // font face. | ||||||
|  | func (dc *Context) WordWrap(s string, w float64) []string { | ||||||
|  | 	return wordWrap(dc, s, w) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Transformation Matrix Operations | ||||||
|  |  | ||||||
|  | // Identity resets the current transformation matrix to the identity matrix. | ||||||
|  | // This results in no translating, scaling, rotating, or shearing. | ||||||
|  | func (dc *Context) Identity() { | ||||||
|  | 	dc.matrix = Identity() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Translate updates the current matrix with a translation. | ||||||
|  | func (dc *Context) Translate(x, y float64) { | ||||||
|  | 	dc.matrix = dc.matrix.Translate(x, y) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Scale updates the current matrix with a scaling factor. | ||||||
|  | // Scaling occurs about the origin. | ||||||
|  | func (dc *Context) Scale(x, y float64) { | ||||||
|  | 	dc.matrix = dc.matrix.Scale(x, y) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ScaleAbout updates the current matrix with a scaling factor. | ||||||
|  | // Scaling occurs about the specified point. | ||||||
|  | func (dc *Context) ScaleAbout(sx, sy, x, y float64) { | ||||||
|  | 	dc.Translate(x, y) | ||||||
|  | 	dc.Scale(sx, sy) | ||||||
|  | 	dc.Translate(-x, -y) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Rotate updates the current matrix with a clockwise rotation. | ||||||
|  | // Rotation occurs about the origin. Angle is specified in radians. | ||||||
|  | func (dc *Context) Rotate(angle float64) { | ||||||
|  | 	dc.matrix = dc.matrix.Rotate(angle) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RotateAbout updates the current matrix with a clockwise rotation. | ||||||
|  | // Rotation occurs about the specified point. Angle is specified in radians. | ||||||
|  | func (dc *Context) RotateAbout(angle, x, y float64) { | ||||||
|  | 	dc.Translate(x, y) | ||||||
|  | 	dc.Rotate(angle) | ||||||
|  | 	dc.Translate(-x, -y) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Shear updates the current matrix with a shearing angle. | ||||||
|  | // Shearing occurs about the origin. | ||||||
|  | func (dc *Context) Shear(x, y float64) { | ||||||
|  | 	dc.matrix = dc.matrix.Shear(x, y) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ShearAbout updates the current matrix with a shearing angle. | ||||||
|  | // Shearing occurs about the specified point. | ||||||
|  | func (dc *Context) ShearAbout(sx, sy, x, y float64) { | ||||||
|  | 	dc.Translate(x, y) | ||||||
|  | 	dc.Shear(sx, sy) | ||||||
|  | 	dc.Translate(-x, -y) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TransformPoint multiplies the specified point by the current matrix, | ||||||
|  | // returning a transformed position. | ||||||
|  | func (dc *Context) TransformPoint(x, y float64) (tx, ty float64) { | ||||||
|  | 	return dc.matrix.TransformPoint(x, y) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // InvertY flips the Y axis so that Y grows from bottom to top and Y=0 is at | ||||||
|  | // the bottom of the image. | ||||||
|  | func (dc *Context) InvertY() { | ||||||
|  | 	dc.Translate(0, float64(dc.height)) | ||||||
|  | 	dc.Scale(1, -1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Stack | ||||||
|  |  | ||||||
|  | // Push saves the current state of the context for later retrieval. These | ||||||
|  | // can be nested. | ||||||
|  | func (dc *Context) Push() { | ||||||
|  | 	x := *dc | ||||||
|  | 	dc.stack = append(dc.stack, &x) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Pop restores the last saved context state from the stack. | ||||||
|  | func (dc *Context) Pop() { | ||||||
|  | 	before := *dc | ||||||
|  | 	s := dc.stack | ||||||
|  | 	x, s := s[len(s)-1], s[:len(s)-1] | ||||||
|  | 	*dc = *x | ||||||
|  | 	dc.mask = before.mask | ||||||
|  | 	dc.strokePath = before.strokePath | ||||||
|  | 	dc.fillPath = before.fillPath | ||||||
|  | 	dc.start = before.start | ||||||
|  | 	dc.current = before.current | ||||||
|  | 	dc.hasCurrent = before.hasCurrent | ||||||
|  | } | ||||||
							
								
								
									
										202
									
								
								vendor/github.com/fogleman/gg/gradient.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/github.com/fogleman/gg/gradient.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | |||||||
|  | package gg | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"image/color" | ||||||
|  | 	"math" | ||||||
|  | 	"sort" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type stop struct { | ||||||
|  | 	pos   float64 | ||||||
|  | 	color color.Color | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type stops []stop | ||||||
|  |  | ||||||
|  | // Len satisfies the Sort interface. | ||||||
|  | func (s stops) Len() int { | ||||||
|  | 	return len(s) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Less satisfies the Sort interface. | ||||||
|  | func (s stops) Less(i, j int) bool { | ||||||
|  | 	return s[i].pos < s[j].pos | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Swap satisfies the Sort interface. | ||||||
|  | func (s stops) Swap(i, j int) { | ||||||
|  | 	s[i], s[j] = s[j], s[i] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Gradient interface { | ||||||
|  | 	Pattern | ||||||
|  | 	AddColorStop(offset float64, color color.Color) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Linear Gradient | ||||||
|  | type linearGradient struct { | ||||||
|  | 	x0, y0, x1, y1 float64 | ||||||
|  | 	stops          stops | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *linearGradient) ColorAt(x, y int) color.Color { | ||||||
|  | 	if len(g.stops) == 0 { | ||||||
|  | 		return color.Transparent | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fx, fy := float64(x), float64(y) | ||||||
|  | 	x0, y0, x1, y1 := g.x0, g.y0, g.x1, g.y1 | ||||||
|  | 	dx, dy := x1-x0, y1-y0 | ||||||
|  |  | ||||||
|  | 	// Horizontal | ||||||
|  | 	if dy == 0 && dx != 0 { | ||||||
|  | 		return getColor((fx-x0)/dx, g.stops) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Vertical | ||||||
|  | 	if dx == 0 && dy != 0 { | ||||||
|  | 		return getColor((fy-y0)/dy, g.stops) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Dot product | ||||||
|  | 	s0 := dx*(fx-x0) + dy*(fy-y0) | ||||||
|  | 	if s0 < 0 { | ||||||
|  | 		return g.stops[0].color | ||||||
|  | 	} | ||||||
|  | 	// Calculate distance to (x0,y0) alone (x0,y0)->(x1,y1) | ||||||
|  | 	mag := math.Hypot(dx, dy) | ||||||
|  | 	u := ((fx-x0)*-dy + (fy-y0)*dx) / (mag * mag) | ||||||
|  | 	x2, y2 := x0+u*-dy, y0+u*dx | ||||||
|  | 	d := math.Hypot(fx-x2, fy-y2) / mag | ||||||
|  | 	return getColor(d, g.stops) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *linearGradient) AddColorStop(offset float64, color color.Color) { | ||||||
|  | 	g.stops = append(g.stops, stop{pos: offset, color: color}) | ||||||
|  | 	sort.Sort(g.stops) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewLinearGradient(x0, y0, x1, y1 float64) Gradient { | ||||||
|  | 	g := &linearGradient{ | ||||||
|  | 		x0: x0, y0: y0, | ||||||
|  | 		x1: x1, y1: y1, | ||||||
|  | 	} | ||||||
|  | 	return g | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Radial Gradient | ||||||
|  | type circle struct { | ||||||
|  | 	x, y, r float64 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type radialGradient struct { | ||||||
|  | 	c0, c1, cd circle | ||||||
|  | 	a, inva    float64 | ||||||
|  | 	mindr      float64 | ||||||
|  | 	stops      stops | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func dot3(x0, y0, z0, x1, y1, z1 float64) float64 { | ||||||
|  | 	return x0*x1 + y0*y1 + z0*z1 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *radialGradient) ColorAt(x, y int) color.Color { | ||||||
|  | 	if len(g.stops) == 0 { | ||||||
|  | 		return color.Transparent | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// copy from pixman's pixman-radial-gradient.c | ||||||
|  |  | ||||||
|  | 	dx, dy := float64(x)+0.5-g.c0.x, float64(y)+0.5-g.c0.y | ||||||
|  | 	b := dot3(dx, dy, g.c0.r, g.cd.x, g.cd.y, g.cd.r) | ||||||
|  | 	c := dot3(dx, dy, -g.c0.r, dx, dy, g.c0.r) | ||||||
|  |  | ||||||
|  | 	if g.a == 0 { | ||||||
|  | 		if b == 0 { | ||||||
|  | 			return color.Transparent | ||||||
|  | 		} | ||||||
|  | 		t := 0.5 * c / b | ||||||
|  | 		if t*g.cd.r >= g.mindr { | ||||||
|  | 			return getColor(t, g.stops) | ||||||
|  | 		} | ||||||
|  | 		return color.Transparent | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	discr := dot3(b, g.a, 0, b, -c, 0) | ||||||
|  | 	if discr >= 0 { | ||||||
|  | 		sqrtdiscr := math.Sqrt(discr) | ||||||
|  | 		t0 := (b + sqrtdiscr) * g.inva | ||||||
|  | 		t1 := (b - sqrtdiscr) * g.inva | ||||||
|  |  | ||||||
|  | 		if t0*g.cd.r >= g.mindr { | ||||||
|  | 			return getColor(t0, g.stops) | ||||||
|  | 		} else if t1*g.cd.r >= g.mindr { | ||||||
|  | 			return getColor(t1, g.stops) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return color.Transparent | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *radialGradient) AddColorStop(offset float64, color color.Color) { | ||||||
|  | 	g.stops = append(g.stops, stop{pos: offset, color: color}) | ||||||
|  | 	sort.Sort(g.stops) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewRadialGradient(x0, y0, r0, x1, y1, r1 float64) Gradient { | ||||||
|  | 	c0 := circle{x0, y0, r0} | ||||||
|  | 	c1 := circle{x1, y1, r1} | ||||||
|  | 	cd := circle{x1 - x0, y1 - y0, r1 - r0} | ||||||
|  | 	a := dot3(cd.x, cd.y, -cd.r, cd.x, cd.y, cd.r) | ||||||
|  | 	var inva float64 | ||||||
|  | 	if a != 0 { | ||||||
|  | 		inva = 1.0 / a | ||||||
|  | 	} | ||||||
|  | 	mindr := -c0.r | ||||||
|  | 	g := &radialGradient{ | ||||||
|  | 		c0:    c0, | ||||||
|  | 		c1:    c1, | ||||||
|  | 		cd:    cd, | ||||||
|  | 		a:     a, | ||||||
|  | 		inva:  inva, | ||||||
|  | 		mindr: mindr, | ||||||
|  | 	} | ||||||
|  | 	return g | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getColor(pos float64, stops stops) color.Color { | ||||||
|  | 	if pos <= 0.0 || len(stops) == 1 { | ||||||
|  | 		return stops[0].color | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	last := stops[len(stops)-1] | ||||||
|  |  | ||||||
|  | 	if pos >= last.pos { | ||||||
|  | 		return last.color | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for i, stop := range stops[1:] { | ||||||
|  | 		if pos < stop.pos { | ||||||
|  | 			pos = (pos - stops[i].pos) / (stop.pos - stops[i].pos) | ||||||
|  | 			return colorLerp(stops[i].color, stop.color, pos) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return last.color | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func colorLerp(c0, c1 color.Color, t float64) color.Color { | ||||||
|  | 	r0, g0, b0, a0 := c0.RGBA() | ||||||
|  | 	r1, g1, b1, a1 := c1.RGBA() | ||||||
|  |  | ||||||
|  | 	return color.NRGBA{ | ||||||
|  | 		lerp(r0, r1, t), | ||||||
|  | 		lerp(g0, g1, t), | ||||||
|  | 		lerp(b0, b1, t), | ||||||
|  | 		lerp(a0, a1, t), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func lerp(a, b uint32, t float64) uint8 { | ||||||
|  | 	return uint8(int32(float64(a)*(1.0-t)+float64(b)*t) >> 8) | ||||||
|  | } | ||||||
							
								
								
									
										88
									
								
								vendor/github.com/fogleman/gg/matrix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								vendor/github.com/fogleman/gg/matrix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | |||||||
|  | package gg | ||||||
|  |  | ||||||
|  | import "math" | ||||||
|  |  | ||||||
|  | type Matrix struct { | ||||||
|  | 	XX, YX, XY, YY, X0, Y0 float64 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Identity() Matrix { | ||||||
|  | 	return Matrix{ | ||||||
|  | 		1, 0, | ||||||
|  | 		0, 1, | ||||||
|  | 		0, 0, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Translate(x, y float64) Matrix { | ||||||
|  | 	return Matrix{ | ||||||
|  | 		1, 0, | ||||||
|  | 		0, 1, | ||||||
|  | 		x, y, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Scale(x, y float64) Matrix { | ||||||
|  | 	return Matrix{ | ||||||
|  | 		x, 0, | ||||||
|  | 		0, y, | ||||||
|  | 		0, 0, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Rotate(angle float64) Matrix { | ||||||
|  | 	c := math.Cos(angle) | ||||||
|  | 	s := math.Sin(angle) | ||||||
|  | 	return Matrix{ | ||||||
|  | 		c, s, | ||||||
|  | 		-s, c, | ||||||
|  | 		0, 0, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Shear(x, y float64) Matrix { | ||||||
|  | 	return Matrix{ | ||||||
|  | 		1, y, | ||||||
|  | 		x, 1, | ||||||
|  | 		0, 0, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a Matrix) Multiply(b Matrix) Matrix { | ||||||
|  | 	return Matrix{ | ||||||
|  | 		a.XX*b.XX + a.YX*b.XY, | ||||||
|  | 		a.XX*b.YX + a.YX*b.YY, | ||||||
|  | 		a.XY*b.XX + a.YY*b.XY, | ||||||
|  | 		a.XY*b.YX + a.YY*b.YY, | ||||||
|  | 		a.X0*b.XX + a.Y0*b.XY + b.X0, | ||||||
|  | 		a.X0*b.YX + a.Y0*b.YY + b.Y0, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a Matrix) TransformVector(x, y float64) (tx, ty float64) { | ||||||
|  | 	tx = a.XX*x + a.XY*y | ||||||
|  | 	ty = a.YX*x + a.YY*y | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a Matrix) TransformPoint(x, y float64) (tx, ty float64) { | ||||||
|  | 	tx = a.XX*x + a.XY*y + a.X0 | ||||||
|  | 	ty = a.YX*x + a.YY*y + a.Y0 | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a Matrix) Translate(x, y float64) Matrix { | ||||||
|  | 	return Translate(x, y).Multiply(a) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a Matrix) Scale(x, y float64) Matrix { | ||||||
|  | 	return Scale(x, y).Multiply(a) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a Matrix) Rotate(angle float64) Matrix { | ||||||
|  | 	return Rotate(angle).Multiply(a) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a Matrix) Shear(x, y float64) Matrix { | ||||||
|  | 	return Shear(x, y).Multiply(a) | ||||||
|  | } | ||||||
							
								
								
									
										140
									
								
								vendor/github.com/fogleman/gg/path.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								vendor/github.com/fogleman/gg/path.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | |||||||
|  | package gg | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/golang/freetype/raster" | ||||||
|  | 	"golang.org/x/image/math/fixed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func flattenPath(p raster.Path) [][]Point { | ||||||
|  | 	var result [][]Point | ||||||
|  | 	var path []Point | ||||||
|  | 	var cx, cy float64 | ||||||
|  | 	for i := 0; i < len(p); { | ||||||
|  | 		switch p[i] { | ||||||
|  | 		case 0: | ||||||
|  | 			if len(path) > 0 { | ||||||
|  | 				result = append(result, path) | ||||||
|  | 				path = nil | ||||||
|  | 			} | ||||||
|  | 			x := unfix(p[i+1]) | ||||||
|  | 			y := unfix(p[i+2]) | ||||||
|  | 			path = append(path, Point{x, y}) | ||||||
|  | 			cx, cy = x, y | ||||||
|  | 			i += 4 | ||||||
|  | 		case 1: | ||||||
|  | 			x := unfix(p[i+1]) | ||||||
|  | 			y := unfix(p[i+2]) | ||||||
|  | 			path = append(path, Point{x, y}) | ||||||
|  | 			cx, cy = x, y | ||||||
|  | 			i += 4 | ||||||
|  | 		case 2: | ||||||
|  | 			x1 := unfix(p[i+1]) | ||||||
|  | 			y1 := unfix(p[i+2]) | ||||||
|  | 			x2 := unfix(p[i+3]) | ||||||
|  | 			y2 := unfix(p[i+4]) | ||||||
|  | 			points := QuadraticBezier(cx, cy, x1, y1, x2, y2) | ||||||
|  | 			path = append(path, points...) | ||||||
|  | 			cx, cy = x2, y2 | ||||||
|  | 			i += 6 | ||||||
|  | 		case 3: | ||||||
|  | 			x1 := unfix(p[i+1]) | ||||||
|  | 			y1 := unfix(p[i+2]) | ||||||
|  | 			x2 := unfix(p[i+3]) | ||||||
|  | 			y2 := unfix(p[i+4]) | ||||||
|  | 			x3 := unfix(p[i+5]) | ||||||
|  | 			y3 := unfix(p[i+6]) | ||||||
|  | 			points := CubicBezier(cx, cy, x1, y1, x2, y2, x3, y3) | ||||||
|  | 			path = append(path, points...) | ||||||
|  | 			cx, cy = x3, y3 | ||||||
|  | 			i += 8 | ||||||
|  | 		default: | ||||||
|  | 			panic("bad path") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if len(path) > 0 { | ||||||
|  | 		result = append(result, path) | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func dashPath(paths [][]Point, dashes []float64) [][]Point { | ||||||
|  | 	var result [][]Point | ||||||
|  | 	if len(dashes) == 0 { | ||||||
|  | 		return paths | ||||||
|  | 	} | ||||||
|  | 	if len(dashes) == 1 { | ||||||
|  | 		dashes = append(dashes, dashes[0]) | ||||||
|  | 	} | ||||||
|  | 	for _, path := range paths { | ||||||
|  | 		if len(path) < 2 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		previous := path[0] | ||||||
|  | 		pathIndex := 1 | ||||||
|  | 		dashIndex := 0 | ||||||
|  | 		segmentLength := 0.0 | ||||||
|  | 		var segment []Point | ||||||
|  | 		segment = append(segment, previous) | ||||||
|  | 		for pathIndex < len(path) { | ||||||
|  | 			dashLength := dashes[dashIndex] | ||||||
|  | 			point := path[pathIndex] | ||||||
|  | 			d := previous.Distance(point) | ||||||
|  | 			maxd := dashLength - segmentLength | ||||||
|  | 			if d > maxd { | ||||||
|  | 				t := maxd / d | ||||||
|  | 				p := previous.Interpolate(point, t) | ||||||
|  | 				segment = append(segment, p) | ||||||
|  | 				if dashIndex%2 == 0 && len(segment) > 1 { | ||||||
|  | 					result = append(result, segment) | ||||||
|  | 				} | ||||||
|  | 				segment = nil | ||||||
|  | 				segment = append(segment, p) | ||||||
|  | 				segmentLength = 0 | ||||||
|  | 				previous = p | ||||||
|  | 				dashIndex = (dashIndex + 1) % len(dashes) | ||||||
|  | 			} else { | ||||||
|  | 				segment = append(segment, point) | ||||||
|  | 				previous = point | ||||||
|  | 				segmentLength += d | ||||||
|  | 				pathIndex++ | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if dashIndex%2 == 0 && len(segment) > 1 { | ||||||
|  | 			result = append(result, segment) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func rasterPath(paths [][]Point) raster.Path { | ||||||
|  | 	var result raster.Path | ||||||
|  | 	for _, path := range paths { | ||||||
|  | 		var previous fixed.Point26_6 | ||||||
|  | 		for i, point := range path { | ||||||
|  | 			f := point.Fixed() | ||||||
|  | 			if i == 0 { | ||||||
|  | 				result.Start(f) | ||||||
|  | 			} else { | ||||||
|  | 				dx := f.X - previous.X | ||||||
|  | 				dy := f.Y - previous.Y | ||||||
|  | 				if dx < 0 { | ||||||
|  | 					dx = -dx | ||||||
|  | 				} | ||||||
|  | 				if dy < 0 { | ||||||
|  | 					dy = -dy | ||||||
|  | 				} | ||||||
|  | 				if dx+dy > 8 { | ||||||
|  | 					// TODO: this is a hack for cases where two points are | ||||||
|  | 					// too close - causes rendering issues with joins / caps | ||||||
|  | 					result.Add1(f) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			previous = f | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func dashed(path raster.Path, dashes []float64) raster.Path { | ||||||
|  | 	return rasterPath(dashPath(flattenPath(path), dashes)) | ||||||
|  | } | ||||||
							
								
								
									
										123
									
								
								vendor/github.com/fogleman/gg/pattern.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								vendor/github.com/fogleman/gg/pattern.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | |||||||
|  | package gg | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"image" | ||||||
|  | 	"image/color" | ||||||
|  |  | ||||||
|  | 	"github.com/golang/freetype/raster" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type RepeatOp int | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	RepeatBoth RepeatOp = iota | ||||||
|  | 	RepeatX | ||||||
|  | 	RepeatY | ||||||
|  | 	RepeatNone | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Pattern interface { | ||||||
|  | 	ColorAt(x, y int) color.Color | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Solid Pattern | ||||||
|  | type solidPattern struct { | ||||||
|  | 	color color.Color | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *solidPattern) ColorAt(x, y int) color.Color { | ||||||
|  | 	return p.color | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewSolidPattern(color color.Color) Pattern { | ||||||
|  | 	return &solidPattern{color: color} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Surface Pattern | ||||||
|  | type surfacePattern struct { | ||||||
|  | 	im image.Image | ||||||
|  | 	op RepeatOp | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *surfacePattern) ColorAt(x, y int) color.Color { | ||||||
|  | 	b := p.im.Bounds() | ||||||
|  | 	switch p.op { | ||||||
|  | 	case RepeatX: | ||||||
|  | 		if y >= b.Dy() { | ||||||
|  | 			return color.Transparent | ||||||
|  | 		} | ||||||
|  | 	case RepeatY: | ||||||
|  | 		if x >= b.Dx() { | ||||||
|  | 			return color.Transparent | ||||||
|  | 		} | ||||||
|  | 	case RepeatNone: | ||||||
|  | 		if x >= b.Dx() || y >= b.Dy() { | ||||||
|  | 			return color.Transparent | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	x = x%b.Dx() + b.Min.X | ||||||
|  | 	y = y%b.Dy() + b.Min.Y | ||||||
|  | 	return p.im.At(x, y) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewSurfacePattern(im image.Image, op RepeatOp) Pattern { | ||||||
|  | 	return &surfacePattern{im: im, op: op} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type patternPainter struct { | ||||||
|  | 	im   *image.RGBA | ||||||
|  | 	mask *image.Alpha | ||||||
|  | 	p    Pattern | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Paint satisfies the Painter interface. | ||||||
|  | func (r *patternPainter) Paint(ss []raster.Span, done bool) { | ||||||
|  | 	b := r.im.Bounds() | ||||||
|  | 	for _, s := range ss { | ||||||
|  | 		if s.Y < b.Min.Y { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if s.Y >= b.Max.Y { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if s.X0 < b.Min.X { | ||||||
|  | 			s.X0 = b.Min.X | ||||||
|  | 		} | ||||||
|  | 		if s.X1 > b.Max.X { | ||||||
|  | 			s.X1 = b.Max.X | ||||||
|  | 		} | ||||||
|  | 		if s.X0 >= s.X1 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		const m = 1<<16 - 1 | ||||||
|  | 		y := s.Y - r.im.Rect.Min.Y | ||||||
|  | 		x0 := s.X0 - r.im.Rect.Min.X | ||||||
|  | 		// RGBAPainter.Paint() in $GOPATH/src/github.com/golang/freetype/raster/paint.go | ||||||
|  | 		i0 := (s.Y-r.im.Rect.Min.Y)*r.im.Stride + (s.X0-r.im.Rect.Min.X)*4 | ||||||
|  | 		i1 := i0 + (s.X1-s.X0)*4 | ||||||
|  | 		for i, x := i0, x0; i < i1; i, x = i+4, x+1 { | ||||||
|  | 			ma := s.Alpha | ||||||
|  | 			if r.mask != nil { | ||||||
|  | 				ma = ma * uint32(r.mask.AlphaAt(x, y).A) / 255 | ||||||
|  | 				if ma == 0 { | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			c := r.p.ColorAt(x, y) | ||||||
|  | 			cr, cg, cb, ca := c.RGBA() | ||||||
|  | 			dr := uint32(r.im.Pix[i+0]) | ||||||
|  | 			dg := uint32(r.im.Pix[i+1]) | ||||||
|  | 			db := uint32(r.im.Pix[i+2]) | ||||||
|  | 			da := uint32(r.im.Pix[i+3]) | ||||||
|  | 			a := (m - (ca * ma / m)) * 0x101 | ||||||
|  | 			r.im.Pix[i+0] = uint8((dr*a + cr*ma) / m >> 8) | ||||||
|  | 			r.im.Pix[i+1] = uint8((dg*a + cg*ma) / m >> 8) | ||||||
|  | 			r.im.Pix[i+2] = uint8((db*a + cb*ma) / m >> 8) | ||||||
|  | 			r.im.Pix[i+3] = uint8((da*a + ca*ma) / m >> 8) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newPatternPainter(im *image.RGBA, mask *image.Alpha, p Pattern) *patternPainter { | ||||||
|  | 	return &patternPainter{im, mask, p} | ||||||
|  | } | ||||||
							
								
								
									
										25
									
								
								vendor/github.com/fogleman/gg/point.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/fogleman/gg/point.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | package gg | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"math" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/image/math/fixed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Point struct { | ||||||
|  | 	X, Y float64 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a Point) Fixed() fixed.Point26_6 { | ||||||
|  | 	return fixp(a.X, a.Y) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a Point) Distance(b Point) float64 { | ||||||
|  | 	return math.Hypot(a.X-b.X, a.Y-b.Y) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a Point) Interpolate(b Point, t float64) Point { | ||||||
|  | 	x := a.X + (b.X-a.X)*t | ||||||
|  | 	y := a.Y + (b.Y-a.Y)*t | ||||||
|  | 	return Point{x, y} | ||||||
|  | } | ||||||
							
								
								
									
										117
									
								
								vendor/github.com/fogleman/gg/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								vendor/github.com/fogleman/gg/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | |||||||
|  | package gg | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"image" | ||||||
|  | 	"image/draw" | ||||||
|  | 	_ "image/jpeg" | ||||||
|  | 	"image/png" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"math" | ||||||
|  | 	"os" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"github.com/golang/freetype/truetype" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/image/font" | ||||||
|  | 	"golang.org/x/image/math/fixed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func Radians(degrees float64) float64 { | ||||||
|  | 	return degrees * math.Pi / 180 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Degrees(radians float64) float64 { | ||||||
|  | 	return radians * 180 / math.Pi | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func LoadImage(path string) (image.Image, error) { | ||||||
|  | 	file, err := os.Open(path) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	defer file.Close() | ||||||
|  | 	im, _, err := image.Decode(file) | ||||||
|  | 	return im, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func LoadPNG(path string) (image.Image, error) { | ||||||
|  | 	file, err := os.Open(path) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	defer file.Close() | ||||||
|  | 	return png.Decode(file) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func SavePNG(path string, im image.Image) error { | ||||||
|  | 	file, err := os.Create(path) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer file.Close() | ||||||
|  | 	return png.Encode(file, im) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func imageToRGBA(src image.Image) *image.RGBA { | ||||||
|  | 	dst := image.NewRGBA(src.Bounds()) | ||||||
|  | 	draw.Draw(dst, dst.Rect, src, image.ZP, draw.Src) | ||||||
|  | 	return dst | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func parseHexColor(x string) (r, g, b, a int) { | ||||||
|  | 	x = strings.TrimPrefix(x, "#") | ||||||
|  | 	a = 255 | ||||||
|  | 	if len(x) == 3 { | ||||||
|  | 		format := "%1x%1x%1x" | ||||||
|  | 		fmt.Sscanf(x, format, &r, &g, &b) | ||||||
|  | 		r |= r << 4 | ||||||
|  | 		g |= g << 4 | ||||||
|  | 		b |= b << 4 | ||||||
|  | 	} | ||||||
|  | 	if len(x) == 6 { | ||||||
|  | 		format := "%02x%02x%02x" | ||||||
|  | 		fmt.Sscanf(x, format, &r, &g, &b) | ||||||
|  | 	} | ||||||
|  | 	if len(x) == 8 { | ||||||
|  | 		format := "%02x%02x%02x%02x" | ||||||
|  | 		fmt.Sscanf(x, format, &r, &g, &b, &a) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func fixp(x, y float64) fixed.Point26_6 { | ||||||
|  | 	return fixed.Point26_6{fix(x), fix(y)} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func fix(x float64) fixed.Int26_6 { | ||||||
|  | 	return fixed.Int26_6(x * 64) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func unfix(x fixed.Int26_6) float64 { | ||||||
|  | 	const shift, mask = 6, 1<<6 - 1 | ||||||
|  | 	if x >= 0 { | ||||||
|  | 		return float64(x>>shift) + float64(x&mask)/64 | ||||||
|  | 	} | ||||||
|  | 	x = -x | ||||||
|  | 	if x >= 0 { | ||||||
|  | 		return -(float64(x>>shift) + float64(x&mask)/64) | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func LoadFontFace(path string, points float64) (font.Face, error) { | ||||||
|  | 	fontBytes, err := ioutil.ReadFile(path) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	f, err := truetype.Parse(fontBytes) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	face := truetype.NewFace(f, &truetype.Options{ | ||||||
|  | 		Size: points, | ||||||
|  | 		// Hinting: font.HintingFull, | ||||||
|  | 	}) | ||||||
|  | 	return face, nil | ||||||
|  | } | ||||||
							
								
								
									
										58
									
								
								vendor/github.com/fogleman/gg/wrap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								vendor/github.com/fogleman/gg/wrap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | package gg | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"strings" | ||||||
|  | 	"unicode" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type measureStringer interface { | ||||||
|  | 	MeasureString(s string) (w, h float64) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func splitOnSpace(x string) []string { | ||||||
|  | 	var result []string | ||||||
|  | 	pi := 0 | ||||||
|  | 	ps := false | ||||||
|  | 	for i, c := range x { | ||||||
|  | 		s := unicode.IsSpace(c) | ||||||
|  | 		if s != ps && i > 0 { | ||||||
|  | 			result = append(result, x[pi:i]) | ||||||
|  | 			pi = i | ||||||
|  | 		} | ||||||
|  | 		ps = s | ||||||
|  | 	} | ||||||
|  | 	result = append(result, x[pi:]) | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func wordWrap(m measureStringer, s string, width float64) []string { | ||||||
|  | 	var result []string | ||||||
|  | 	for _, line := range strings.Split(s, "\n") { | ||||||
|  | 		fields := splitOnSpace(line) | ||||||
|  | 		if len(fields)%2 == 1 { | ||||||
|  | 			fields = append(fields, "") | ||||||
|  | 		} | ||||||
|  | 		x := "" | ||||||
|  | 		for i := 0; i < len(fields); i += 2 { | ||||||
|  | 			w, _ := m.MeasureString(x + fields[i]) | ||||||
|  | 			if w > width { | ||||||
|  | 				if x == "" { | ||||||
|  | 					result = append(result, fields[i]) | ||||||
|  | 					x = "" | ||||||
|  | 					continue | ||||||
|  | 				} else { | ||||||
|  | 					result = append(result, x) | ||||||
|  | 					x = "" | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			x += fields[i] + fields[i+1] | ||||||
|  | 		} | ||||||
|  | 		if x != "" { | ||||||
|  | 			result = append(result, x) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	for i, line := range result { | ||||||
|  | 		result[i] = strings.TrimSpace(line) | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								vendor/github.com/golang/freetype/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/golang/freetype/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | # This is the official list of Freetype-Go authors for copyright purposes. | ||||||
|  | # This file is distinct from the CONTRIBUTORS files. | ||||||
|  | # See the latter for an explanation. | ||||||
|  | # | ||||||
|  | # Freetype-Go is derived from Freetype, which is written in C. The latter | ||||||
|  | # is copyright 1996-2010 David Turner, Robert Wilhelm, and Werner Lemberg. | ||||||
|  |  | ||||||
|  | # Names should be added to this file as | ||||||
|  | #	Name or Organization <email address> | ||||||
|  | # The email address is not required for organizations. | ||||||
|  |  | ||||||
|  | # Please keep the list sorted. | ||||||
|  |  | ||||||
|  | Google Inc. | ||||||
|  | Jeff R. Allen <jra@nella.org> | ||||||
|  | Maksim Kochkin <maxxarts@gmail.com> | ||||||
|  | Michael Fogleman <fogleman@gmail.com> | ||||||
|  | Rémy Oudompheng <oudomphe@phare.normalesup.org> | ||||||
|  | Roger Peppe <rogpeppe@gmail.com> | ||||||
|  | Steven Edwards <steven@stephenwithav.com> | ||||||
							
								
								
									
										38
									
								
								vendor/github.com/golang/freetype/CONTRIBUTORS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/github.com/golang/freetype/CONTRIBUTORS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | # This is the official list of people who can contribute | ||||||
|  | # (and typically have contributed) code to the Freetype-Go repository. | ||||||
|  | # The AUTHORS file lists the copyright holders; this file | ||||||
|  | # lists people.  For example, Google employees are listed here | ||||||
|  | # but not in AUTHORS, because Google holds the copyright. | ||||||
|  | # | ||||||
|  | # The submission process automatically checks to make sure | ||||||
|  | # that people submitting code are listed in this file (by email address). | ||||||
|  | # | ||||||
|  | # Names should be added to this file only after verifying that | ||||||
|  | # the individual or the individual's organization has agreed to | ||||||
|  | # the appropriate Contributor License Agreement, found here: | ||||||
|  | # | ||||||
|  | #     http://code.google.com/legal/individual-cla-v1.0.html | ||||||
|  | #     http://code.google.com/legal/corporate-cla-v1.0.html | ||||||
|  | # | ||||||
|  | # The agreement for individuals can be filled out on the web. | ||||||
|  | # | ||||||
|  | # When adding J Random Contributor's name to this file, | ||||||
|  | # either J's name or J's organization's name should be | ||||||
|  | # added to the AUTHORS file, depending on whether the | ||||||
|  | # individual or corporate CLA was used. | ||||||
|  |  | ||||||
|  | # Names should be added to this file like so: | ||||||
|  | #     Name <email address> | ||||||
|  |  | ||||||
|  | # Please keep the list sorted. | ||||||
|  |  | ||||||
|  | Andrew Gerrand <adg@golang.org> | ||||||
|  | Jeff R. Allen <jra@nella.org> <jeff.allen@gmail.com> | ||||||
|  | Maksim Kochkin <maxxarts@gmail.com> | ||||||
|  | Michael Fogleman <fogleman@gmail.com> | ||||||
|  | Nigel Tao <nigeltao@golang.org> | ||||||
|  | Rémy Oudompheng <oudomphe@phare.normalesup.org> <remyoudompheng@gmail.com> | ||||||
|  | Rob Pike <r@golang.org> | ||||||
|  | Roger Peppe <rogpeppe@gmail.com> | ||||||
|  | Russ Cox <rsc@golang.org> | ||||||
|  | Steven Edwards <steven@stephenwithav.com> | ||||||
							
								
								
									
										12
									
								
								vendor/github.com/golang/freetype/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/golang/freetype/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | Use of the Freetype-Go software is subject to your choice of exactly one of | ||||||
|  | the following two licenses: | ||||||
|  |   * The FreeType License, which is similar to the original BSD license with | ||||||
|  |     an advertising clause, or | ||||||
|  |   * The GNU General Public License (GPL), version 2 or later. | ||||||
|  |  | ||||||
|  | The text of these licenses are available in the licenses/ftl.txt and the | ||||||
|  | licenses/gpl.txt files respectively. They are also available at | ||||||
|  | http://freetype.sourceforge.net/license.html | ||||||
|  |  | ||||||
|  | The Luxi fonts in the testdata directory are licensed separately. See the | ||||||
|  | testdata/COPYING file for details. | ||||||
							
								
								
									
										245
									
								
								vendor/github.com/golang/freetype/raster/geom.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								vendor/github.com/golang/freetype/raster/geom.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,245 @@ | |||||||
|  | // Copyright 2010 The Freetype-Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by your choice of either the | ||||||
|  | // FreeType License or the GNU General Public License version 2 (or | ||||||
|  | // any later version), both of which can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package raster | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"math" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/image/math/fixed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // maxAbs returns the maximum of abs(a) and abs(b). | ||||||
|  | func maxAbs(a, b fixed.Int26_6) fixed.Int26_6 { | ||||||
|  | 	if a < 0 { | ||||||
|  | 		a = -a | ||||||
|  | 	} | ||||||
|  | 	if b < 0 { | ||||||
|  | 		b = -b | ||||||
|  | 	} | ||||||
|  | 	if a < b { | ||||||
|  | 		return b | ||||||
|  | 	} | ||||||
|  | 	return a | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // pNeg returns the vector -p, or equivalently p rotated by 180 degrees. | ||||||
|  | func pNeg(p fixed.Point26_6) fixed.Point26_6 { | ||||||
|  | 	return fixed.Point26_6{-p.X, -p.Y} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // pDot returns the dot product p·q. | ||||||
|  | func pDot(p fixed.Point26_6, q fixed.Point26_6) fixed.Int52_12 { | ||||||
|  | 	px, py := int64(p.X), int64(p.Y) | ||||||
|  | 	qx, qy := int64(q.X), int64(q.Y) | ||||||
|  | 	return fixed.Int52_12(px*qx + py*qy) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // pLen returns the length of the vector p. | ||||||
|  | func pLen(p fixed.Point26_6) fixed.Int26_6 { | ||||||
|  | 	// TODO(nigeltao): use fixed point math. | ||||||
|  | 	x := float64(p.X) | ||||||
|  | 	y := float64(p.Y) | ||||||
|  | 	return fixed.Int26_6(math.Sqrt(x*x + y*y)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // pNorm returns the vector p normalized to the given length, or zero if p is | ||||||
|  | // degenerate. | ||||||
|  | func pNorm(p fixed.Point26_6, length fixed.Int26_6) fixed.Point26_6 { | ||||||
|  | 	d := pLen(p) | ||||||
|  | 	if d == 0 { | ||||||
|  | 		return fixed.Point26_6{} | ||||||
|  | 	} | ||||||
|  | 	s, t := int64(length), int64(d) | ||||||
|  | 	x := int64(p.X) * s / t | ||||||
|  | 	y := int64(p.Y) * s / t | ||||||
|  | 	return fixed.Point26_6{fixed.Int26_6(x), fixed.Int26_6(y)} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // pRot45CW returns the vector p rotated clockwise by 45 degrees. | ||||||
|  | // | ||||||
|  | // Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}. | ||||||
|  | func pRot45CW(p fixed.Point26_6) fixed.Point26_6 { | ||||||
|  | 	// 181/256 is approximately 1/√2, or sin(π/4). | ||||||
|  | 	px, py := int64(p.X), int64(p.Y) | ||||||
|  | 	qx := (+px - py) * 181 / 256 | ||||||
|  | 	qy := (+px + py) * 181 / 256 | ||||||
|  | 	return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // pRot90CW returns the vector p rotated clockwise by 90 degrees. | ||||||
|  | // | ||||||
|  | // Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}. | ||||||
|  | func pRot90CW(p fixed.Point26_6) fixed.Point26_6 { | ||||||
|  | 	return fixed.Point26_6{-p.Y, p.X} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // pRot135CW returns the vector p rotated clockwise by 135 degrees. | ||||||
|  | // | ||||||
|  | // Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}. | ||||||
|  | func pRot135CW(p fixed.Point26_6) fixed.Point26_6 { | ||||||
|  | 	// 181/256 is approximately 1/√2, or sin(π/4). | ||||||
|  | 	px, py := int64(p.X), int64(p.Y) | ||||||
|  | 	qx := (-px - py) * 181 / 256 | ||||||
|  | 	qy := (+px - py) * 181 / 256 | ||||||
|  | 	return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // pRot45CCW returns the vector p rotated counter-clockwise by 45 degrees. | ||||||
|  | // | ||||||
|  | // Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}. | ||||||
|  | func pRot45CCW(p fixed.Point26_6) fixed.Point26_6 { | ||||||
|  | 	// 181/256 is approximately 1/√2, or sin(π/4). | ||||||
|  | 	px, py := int64(p.X), int64(p.Y) | ||||||
|  | 	qx := (+px + py) * 181 / 256 | ||||||
|  | 	qy := (-px + py) * 181 / 256 | ||||||
|  | 	return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // pRot90CCW returns the vector p rotated counter-clockwise by 90 degrees. | ||||||
|  | // | ||||||
|  | // Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}. | ||||||
|  | func pRot90CCW(p fixed.Point26_6) fixed.Point26_6 { | ||||||
|  | 	return fixed.Point26_6{p.Y, -p.X} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // pRot135CCW returns the vector p rotated counter-clockwise by 135 degrees. | ||||||
|  | // | ||||||
|  | // Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}. | ||||||
|  | func pRot135CCW(p fixed.Point26_6) fixed.Point26_6 { | ||||||
|  | 	// 181/256 is approximately 1/√2, or sin(π/4). | ||||||
|  | 	px, py := int64(p.X), int64(p.Y) | ||||||
|  | 	qx := (-px + py) * 181 / 256 | ||||||
|  | 	qy := (-px - py) * 181 / 256 | ||||||
|  | 	return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // An Adder accumulates points on a curve. | ||||||
|  | type Adder interface { | ||||||
|  | 	// Start starts a new curve at the given point. | ||||||
|  | 	Start(a fixed.Point26_6) | ||||||
|  | 	// Add1 adds a linear segment to the current curve. | ||||||
|  | 	Add1(b fixed.Point26_6) | ||||||
|  | 	// Add2 adds a quadratic segment to the current curve. | ||||||
|  | 	Add2(b, c fixed.Point26_6) | ||||||
|  | 	// Add3 adds a cubic segment to the current curve. | ||||||
|  | 	Add3(b, c, d fixed.Point26_6) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // A Path is a sequence of curves, and a curve is a start point followed by a | ||||||
|  | // sequence of linear, quadratic or cubic segments. | ||||||
|  | type Path []fixed.Int26_6 | ||||||
|  |  | ||||||
|  | // String returns a human-readable representation of a Path. | ||||||
|  | func (p Path) String() string { | ||||||
|  | 	s := "" | ||||||
|  | 	for i := 0; i < len(p); { | ||||||
|  | 		if i != 0 { | ||||||
|  | 			s += " " | ||||||
|  | 		} | ||||||
|  | 		switch p[i] { | ||||||
|  | 		case 0: | ||||||
|  | 			s += "S0" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3])) | ||||||
|  | 			i += 4 | ||||||
|  | 		case 1: | ||||||
|  | 			s += "A1" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3])) | ||||||
|  | 			i += 4 | ||||||
|  | 		case 2: | ||||||
|  | 			s += "A2" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+5])) | ||||||
|  | 			i += 6 | ||||||
|  | 		case 3: | ||||||
|  | 			s += "A3" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+7])) | ||||||
|  | 			i += 8 | ||||||
|  | 		default: | ||||||
|  | 			panic("freetype/raster: bad path") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return s | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Clear cancels any previous calls to p.Start or p.AddXxx. | ||||||
|  | func (p *Path) Clear() { | ||||||
|  | 	*p = (*p)[:0] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Start starts a new curve at the given point. | ||||||
|  | func (p *Path) Start(a fixed.Point26_6) { | ||||||
|  | 	*p = append(*p, 0, a.X, a.Y, 0) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add1 adds a linear segment to the current curve. | ||||||
|  | func (p *Path) Add1(b fixed.Point26_6) { | ||||||
|  | 	*p = append(*p, 1, b.X, b.Y, 1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add2 adds a quadratic segment to the current curve. | ||||||
|  | func (p *Path) Add2(b, c fixed.Point26_6) { | ||||||
|  | 	*p = append(*p, 2, b.X, b.Y, c.X, c.Y, 2) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add3 adds a cubic segment to the current curve. | ||||||
|  | func (p *Path) Add3(b, c, d fixed.Point26_6) { | ||||||
|  | 	*p = append(*p, 3, b.X, b.Y, c.X, c.Y, d.X, d.Y, 3) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AddPath adds the Path q to p. | ||||||
|  | func (p *Path) AddPath(q Path) { | ||||||
|  | 	*p = append(*p, q...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AddStroke adds a stroked Path. | ||||||
|  | func (p *Path) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) { | ||||||
|  | 	Stroke(p, q, width, cr, jr) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // firstPoint returns the first point in a non-empty Path. | ||||||
|  | func (p Path) firstPoint() fixed.Point26_6 { | ||||||
|  | 	return fixed.Point26_6{p[1], p[2]} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // lastPoint returns the last point in a non-empty Path. | ||||||
|  | func (p Path) lastPoint() fixed.Point26_6 { | ||||||
|  | 	return fixed.Point26_6{p[len(p)-3], p[len(p)-2]} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // addPathReversed adds q reversed to p. | ||||||
|  | // For example, if q consists of a linear segment from A to B followed by a | ||||||
|  | // quadratic segment from B to C to D, then the values of q looks like: | ||||||
|  | // index: 01234567890123 | ||||||
|  | // value: 0AA01BB12CCDD2 | ||||||
|  | // So, when adding q backwards to p, we want to Add2(C, B) followed by Add1(A). | ||||||
|  | func addPathReversed(p Adder, q Path) { | ||||||
|  | 	if len(q) == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	i := len(q) - 1 | ||||||
|  | 	for { | ||||||
|  | 		switch q[i] { | ||||||
|  | 		case 0: | ||||||
|  | 			return | ||||||
|  | 		case 1: | ||||||
|  | 			i -= 4 | ||||||
|  | 			p.Add1( | ||||||
|  | 				fixed.Point26_6{q[i-2], q[i-1]}, | ||||||
|  | 			) | ||||||
|  | 		case 2: | ||||||
|  | 			i -= 6 | ||||||
|  | 			p.Add2( | ||||||
|  | 				fixed.Point26_6{q[i+2], q[i+3]}, | ||||||
|  | 				fixed.Point26_6{q[i-2], q[i-1]}, | ||||||
|  | 			) | ||||||
|  | 		case 3: | ||||||
|  | 			i -= 8 | ||||||
|  | 			p.Add3( | ||||||
|  | 				fixed.Point26_6{q[i+4], q[i+5]}, | ||||||
|  | 				fixed.Point26_6{q[i+2], q[i+3]}, | ||||||
|  | 				fixed.Point26_6{q[i-2], q[i-1]}, | ||||||
|  | 			) | ||||||
|  | 		default: | ||||||
|  | 			panic("freetype/raster: bad path") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										287
									
								
								vendor/github.com/golang/freetype/raster/paint.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										287
									
								
								vendor/github.com/golang/freetype/raster/paint.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,287 @@ | |||||||
|  | // Copyright 2010 The Freetype-Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by your choice of either the | ||||||
|  | // FreeType License or the GNU General Public License version 2 (or | ||||||
|  | // any later version), both of which can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package raster | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"image" | ||||||
|  | 	"image/color" | ||||||
|  | 	"image/draw" | ||||||
|  | 	"math" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // A Span is a horizontal segment of pixels with constant alpha. X0 is an | ||||||
|  | // inclusive bound and X1 is exclusive, the same as for slices. A fully opaque | ||||||
|  | // Span has Alpha == 0xffff. | ||||||
|  | type Span struct { | ||||||
|  | 	Y, X0, X1 int | ||||||
|  | 	Alpha     uint32 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // A Painter knows how to paint a batch of Spans. Rasterization may involve | ||||||
|  | // Painting multiple batches, and done will be true for the final batch. The | ||||||
|  | // Spans' Y values are monotonically increasing during a rasterization. Paint | ||||||
|  | // may use all of ss as scratch space during the call. | ||||||
|  | type Painter interface { | ||||||
|  | 	Paint(ss []Span, done bool) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // The PainterFunc type adapts an ordinary function to the Painter interface. | ||||||
|  | type PainterFunc func(ss []Span, done bool) | ||||||
|  |  | ||||||
|  | // Paint just delegates the call to f. | ||||||
|  | func (f PainterFunc) Paint(ss []Span, done bool) { f(ss, done) } | ||||||
|  |  | ||||||
|  | // An AlphaOverPainter is a Painter that paints Spans onto a *image.Alpha using | ||||||
|  | // the Over Porter-Duff composition operator. | ||||||
|  | type AlphaOverPainter struct { | ||||||
|  | 	Image *image.Alpha | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Paint satisfies the Painter interface. | ||||||
|  | func (r AlphaOverPainter) Paint(ss []Span, done bool) { | ||||||
|  | 	b := r.Image.Bounds() | ||||||
|  | 	for _, s := range ss { | ||||||
|  | 		if s.Y < b.Min.Y { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if s.Y >= b.Max.Y { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if s.X0 < b.Min.X { | ||||||
|  | 			s.X0 = b.Min.X | ||||||
|  | 		} | ||||||
|  | 		if s.X1 > b.Max.X { | ||||||
|  | 			s.X1 = b.Max.X | ||||||
|  | 		} | ||||||
|  | 		if s.X0 >= s.X1 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X | ||||||
|  | 		p := r.Image.Pix[base+s.X0 : base+s.X1] | ||||||
|  | 		a := int(s.Alpha >> 8) | ||||||
|  | 		for i, c := range p { | ||||||
|  | 			v := int(c) | ||||||
|  | 			p[i] = uint8((v*255 + (255-v)*a) / 255) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewAlphaOverPainter creates a new AlphaOverPainter for the given image. | ||||||
|  | func NewAlphaOverPainter(m *image.Alpha) AlphaOverPainter { | ||||||
|  | 	return AlphaOverPainter{m} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // An AlphaSrcPainter is a Painter that paints Spans onto a *image.Alpha using | ||||||
|  | // the Src Porter-Duff composition operator. | ||||||
|  | type AlphaSrcPainter struct { | ||||||
|  | 	Image *image.Alpha | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Paint satisfies the Painter interface. | ||||||
|  | func (r AlphaSrcPainter) Paint(ss []Span, done bool) { | ||||||
|  | 	b := r.Image.Bounds() | ||||||
|  | 	for _, s := range ss { | ||||||
|  | 		if s.Y < b.Min.Y { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if s.Y >= b.Max.Y { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if s.X0 < b.Min.X { | ||||||
|  | 			s.X0 = b.Min.X | ||||||
|  | 		} | ||||||
|  | 		if s.X1 > b.Max.X { | ||||||
|  | 			s.X1 = b.Max.X | ||||||
|  | 		} | ||||||
|  | 		if s.X0 >= s.X1 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X | ||||||
|  | 		p := r.Image.Pix[base+s.X0 : base+s.X1] | ||||||
|  | 		color := uint8(s.Alpha >> 8) | ||||||
|  | 		for i := range p { | ||||||
|  | 			p[i] = color | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewAlphaSrcPainter creates a new AlphaSrcPainter for the given image. | ||||||
|  | func NewAlphaSrcPainter(m *image.Alpha) AlphaSrcPainter { | ||||||
|  | 	return AlphaSrcPainter{m} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // An RGBAPainter is a Painter that paints Spans onto a *image.RGBA. | ||||||
|  | type RGBAPainter struct { | ||||||
|  | 	// Image is the image to compose onto. | ||||||
|  | 	Image *image.RGBA | ||||||
|  | 	// Op is the Porter-Duff composition operator. | ||||||
|  | 	Op draw.Op | ||||||
|  | 	// cr, cg, cb and ca are the 16-bit color to paint the spans. | ||||||
|  | 	cr, cg, cb, ca uint32 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Paint satisfies the Painter interface. | ||||||
|  | func (r *RGBAPainter) Paint(ss []Span, done bool) { | ||||||
|  | 	b := r.Image.Bounds() | ||||||
|  | 	for _, s := range ss { | ||||||
|  | 		if s.Y < b.Min.Y { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if s.Y >= b.Max.Y { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if s.X0 < b.Min.X { | ||||||
|  | 			s.X0 = b.Min.X | ||||||
|  | 		} | ||||||
|  | 		if s.X1 > b.Max.X { | ||||||
|  | 			s.X1 = b.Max.X | ||||||
|  | 		} | ||||||
|  | 		if s.X0 >= s.X1 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		// This code mimics drawGlyphOver in $GOROOT/src/image/draw/draw.go. | ||||||
|  | 		ma := s.Alpha | ||||||
|  | 		const m = 1<<16 - 1 | ||||||
|  | 		i0 := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride + (s.X0-r.Image.Rect.Min.X)*4 | ||||||
|  | 		i1 := i0 + (s.X1-s.X0)*4 | ||||||
|  | 		if r.Op == draw.Over { | ||||||
|  | 			for i := i0; i < i1; i += 4 { | ||||||
|  | 				dr := uint32(r.Image.Pix[i+0]) | ||||||
|  | 				dg := uint32(r.Image.Pix[i+1]) | ||||||
|  | 				db := uint32(r.Image.Pix[i+2]) | ||||||
|  | 				da := uint32(r.Image.Pix[i+3]) | ||||||
|  | 				a := (m - (r.ca * ma / m)) * 0x101 | ||||||
|  | 				r.Image.Pix[i+0] = uint8((dr*a + r.cr*ma) / m >> 8) | ||||||
|  | 				r.Image.Pix[i+1] = uint8((dg*a + r.cg*ma) / m >> 8) | ||||||
|  | 				r.Image.Pix[i+2] = uint8((db*a + r.cb*ma) / m >> 8) | ||||||
|  | 				r.Image.Pix[i+3] = uint8((da*a + r.ca*ma) / m >> 8) | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			for i := i0; i < i1; i += 4 { | ||||||
|  | 				r.Image.Pix[i+0] = uint8(r.cr * ma / m >> 8) | ||||||
|  | 				r.Image.Pix[i+1] = uint8(r.cg * ma / m >> 8) | ||||||
|  | 				r.Image.Pix[i+2] = uint8(r.cb * ma / m >> 8) | ||||||
|  | 				r.Image.Pix[i+3] = uint8(r.ca * ma / m >> 8) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetColor sets the color to paint the spans. | ||||||
|  | func (r *RGBAPainter) SetColor(c color.Color) { | ||||||
|  | 	r.cr, r.cg, r.cb, r.ca = c.RGBA() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewRGBAPainter creates a new RGBAPainter for the given image. | ||||||
|  | func NewRGBAPainter(m *image.RGBA) *RGBAPainter { | ||||||
|  | 	return &RGBAPainter{Image: m} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // A MonochromePainter wraps another Painter, quantizing each Span's alpha to | ||||||
|  | // be either fully opaque or fully transparent. | ||||||
|  | type MonochromePainter struct { | ||||||
|  | 	Painter   Painter | ||||||
|  | 	y, x0, x1 int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Paint delegates to the wrapped Painter after quantizing each Span's alpha | ||||||
|  | // value and merging adjacent fully opaque Spans. | ||||||
|  | func (m *MonochromePainter) Paint(ss []Span, done bool) { | ||||||
|  | 	// We compact the ss slice, discarding any Spans whose alpha quantizes to zero. | ||||||
|  | 	j := 0 | ||||||
|  | 	for _, s := range ss { | ||||||
|  | 		if s.Alpha >= 0x8000 { | ||||||
|  | 			if m.y == s.Y && m.x1 == s.X0 { | ||||||
|  | 				m.x1 = s.X1 | ||||||
|  | 			} else { | ||||||
|  | 				ss[j] = Span{m.y, m.x0, m.x1, 1<<16 - 1} | ||||||
|  | 				j++ | ||||||
|  | 				m.y, m.x0, m.x1 = s.Y, s.X0, s.X1 | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if done { | ||||||
|  | 		// Flush the accumulated Span. | ||||||
|  | 		finalSpan := Span{m.y, m.x0, m.x1, 1<<16 - 1} | ||||||
|  | 		if j < len(ss) { | ||||||
|  | 			ss[j] = finalSpan | ||||||
|  | 			j++ | ||||||
|  | 			m.Painter.Paint(ss[:j], true) | ||||||
|  | 		} else if j == len(ss) { | ||||||
|  | 			m.Painter.Paint(ss, false) | ||||||
|  | 			if cap(ss) > 0 { | ||||||
|  | 				ss = ss[:1] | ||||||
|  | 			} else { | ||||||
|  | 				ss = make([]Span, 1) | ||||||
|  | 			} | ||||||
|  | 			ss[0] = finalSpan | ||||||
|  | 			m.Painter.Paint(ss, true) | ||||||
|  | 		} else { | ||||||
|  | 			panic("unreachable") | ||||||
|  | 		} | ||||||
|  | 		// Reset the accumulator, so that this Painter can be re-used. | ||||||
|  | 		m.y, m.x0, m.x1 = 0, 0, 0 | ||||||
|  | 	} else { | ||||||
|  | 		m.Painter.Paint(ss[:j], false) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewMonochromePainter creates a new MonochromePainter that wraps the given | ||||||
|  | // Painter. | ||||||
|  | func NewMonochromePainter(p Painter) *MonochromePainter { | ||||||
|  | 	return &MonochromePainter{Painter: p} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // A GammaCorrectionPainter wraps another Painter, performing gamma-correction | ||||||
|  | // on each Span's alpha value. | ||||||
|  | type GammaCorrectionPainter struct { | ||||||
|  | 	// Painter is the wrapped Painter. | ||||||
|  | 	Painter Painter | ||||||
|  | 	// a is the precomputed alpha values for linear interpolation, with fully | ||||||
|  | 	// opaque == 0xffff. | ||||||
|  | 	a [256]uint16 | ||||||
|  | 	// gammaIsOne is whether gamma correction is a no-op. | ||||||
|  | 	gammaIsOne bool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Paint delegates to the wrapped Painter after performing gamma-correction on | ||||||
|  | // each Span. | ||||||
|  | func (g *GammaCorrectionPainter) Paint(ss []Span, done bool) { | ||||||
|  | 	if !g.gammaIsOne { | ||||||
|  | 		const n = 0x101 | ||||||
|  | 		for i, s := range ss { | ||||||
|  | 			if s.Alpha == 0 || s.Alpha == 0xffff { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			p, q := s.Alpha/n, s.Alpha%n | ||||||
|  | 			// The resultant alpha is a linear interpolation of g.a[p] and g.a[p+1]. | ||||||
|  | 			a := uint32(g.a[p])*(n-q) + uint32(g.a[p+1])*q | ||||||
|  | 			ss[i].Alpha = (a + n/2) / n | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	g.Painter.Paint(ss, done) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetGamma sets the gamma value. | ||||||
|  | func (g *GammaCorrectionPainter) SetGamma(gamma float64) { | ||||||
|  | 	g.gammaIsOne = gamma == 1 | ||||||
|  | 	if g.gammaIsOne { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	for i := 0; i < 256; i++ { | ||||||
|  | 		a := float64(i) / 0xff | ||||||
|  | 		a = math.Pow(a, gamma) | ||||||
|  | 		g.a[i] = uint16(0xffff * a) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps | ||||||
|  | // the given Painter. | ||||||
|  | func NewGammaCorrectionPainter(p Painter, gamma float64) *GammaCorrectionPainter { | ||||||
|  | 	g := &GammaCorrectionPainter{Painter: p} | ||||||
|  | 	g.SetGamma(gamma) | ||||||
|  | 	return g | ||||||
|  | } | ||||||
							
								
								
									
										601
									
								
								vendor/github.com/golang/freetype/raster/raster.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										601
									
								
								vendor/github.com/golang/freetype/raster/raster.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,601 @@ | |||||||
|  | // Copyright 2010 The Freetype-Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by your choice of either the | ||||||
|  | // FreeType License or the GNU General Public License version 2 (or | ||||||
|  | // any later version), both of which can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | // Package raster provides an anti-aliasing 2-D rasterizer. | ||||||
|  | // | ||||||
|  | // It is part of the larger Freetype suite of font-related packages, but the | ||||||
|  | // raster package is not specific to font rasterization, and can be used | ||||||
|  | // standalone without any other Freetype package. | ||||||
|  | // | ||||||
|  | // Rasterization is done by the same area/coverage accumulation algorithm as | ||||||
|  | // the Freetype "smooth" module, and the Anti-Grain Geometry library. A | ||||||
|  | // description of the area/coverage algorithm is at | ||||||
|  | // http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm | ||||||
|  | package raster // import "github.com/golang/freetype/raster" | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"strconv" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/image/math/fixed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // A cell is part of a linked list (for a given yi co-ordinate) of accumulated | ||||||
|  | // area/coverage for the pixel at (xi, yi). | ||||||
|  | type cell struct { | ||||||
|  | 	xi          int | ||||||
|  | 	area, cover int | ||||||
|  | 	next        int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Rasterizer struct { | ||||||
|  | 	// If false, the default behavior is to use the even-odd winding fill | ||||||
|  | 	// rule during Rasterize. | ||||||
|  | 	UseNonZeroWinding bool | ||||||
|  | 	// An offset (in pixels) to the painted spans. | ||||||
|  | 	Dx, Dy int | ||||||
|  |  | ||||||
|  | 	// The width of the Rasterizer. The height is implicit in len(cellIndex). | ||||||
|  | 	width int | ||||||
|  | 	// splitScaleN is the scaling factor used to determine how many times | ||||||
|  | 	// to decompose a quadratic or cubic segment into a linear approximation. | ||||||
|  | 	splitScale2, splitScale3 int | ||||||
|  |  | ||||||
|  | 	// The current pen position. | ||||||
|  | 	a fixed.Point26_6 | ||||||
|  | 	// The current cell and its area/coverage being accumulated. | ||||||
|  | 	xi, yi      int | ||||||
|  | 	area, cover int | ||||||
|  |  | ||||||
|  | 	// Saved cells. | ||||||
|  | 	cell []cell | ||||||
|  | 	// Linked list of cells, one per row. | ||||||
|  | 	cellIndex []int | ||||||
|  | 	// Buffers. | ||||||
|  | 	cellBuf      [256]cell | ||||||
|  | 	cellIndexBuf [64]int | ||||||
|  | 	spanBuf      [64]Span | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // findCell returns the index in r.cell for the cell corresponding to | ||||||
|  | // (r.xi, r.yi). The cell is created if necessary. | ||||||
|  | func (r *Rasterizer) findCell() int { | ||||||
|  | 	if r.yi < 0 || r.yi >= len(r.cellIndex) { | ||||||
|  | 		return -1 | ||||||
|  | 	} | ||||||
|  | 	xi := r.xi | ||||||
|  | 	if xi < 0 { | ||||||
|  | 		xi = -1 | ||||||
|  | 	} else if xi > r.width { | ||||||
|  | 		xi = r.width | ||||||
|  | 	} | ||||||
|  | 	i, prev := r.cellIndex[r.yi], -1 | ||||||
|  | 	for i != -1 && r.cell[i].xi <= xi { | ||||||
|  | 		if r.cell[i].xi == xi { | ||||||
|  | 			return i | ||||||
|  | 		} | ||||||
|  | 		i, prev = r.cell[i].next, i | ||||||
|  | 	} | ||||||
|  | 	c := len(r.cell) | ||||||
|  | 	if c == cap(r.cell) { | ||||||
|  | 		buf := make([]cell, c, 4*c) | ||||||
|  | 		copy(buf, r.cell) | ||||||
|  | 		r.cell = buf[0 : c+1] | ||||||
|  | 	} else { | ||||||
|  | 		r.cell = r.cell[0 : c+1] | ||||||
|  | 	} | ||||||
|  | 	r.cell[c] = cell{xi, 0, 0, i} | ||||||
|  | 	if prev == -1 { | ||||||
|  | 		r.cellIndex[r.yi] = c | ||||||
|  | 	} else { | ||||||
|  | 		r.cell[prev].next = c | ||||||
|  | 	} | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // saveCell saves any accumulated r.area/r.cover for (r.xi, r.yi). | ||||||
|  | func (r *Rasterizer) saveCell() { | ||||||
|  | 	if r.area != 0 || r.cover != 0 { | ||||||
|  | 		i := r.findCell() | ||||||
|  | 		if i != -1 { | ||||||
|  | 			r.cell[i].area += r.area | ||||||
|  | 			r.cell[i].cover += r.cover | ||||||
|  | 		} | ||||||
|  | 		r.area = 0 | ||||||
|  | 		r.cover = 0 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // setCell sets the (xi, yi) cell that r is accumulating area/coverage for. | ||||||
|  | func (r *Rasterizer) setCell(xi, yi int) { | ||||||
|  | 	if r.xi != xi || r.yi != yi { | ||||||
|  | 		r.saveCell() | ||||||
|  | 		r.xi, r.yi = xi, yi | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // scan accumulates area/coverage for the yi'th scanline, going from | ||||||
|  | // x0 to x1 in the horizontal direction (in 26.6 fixed point co-ordinates) | ||||||
|  | // and from y0f to y1f fractional vertical units within that scanline. | ||||||
|  | func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f fixed.Int26_6) { | ||||||
|  | 	// Break the 26.6 fixed point X co-ordinates into integral and fractional parts. | ||||||
|  | 	x0i := int(x0) / 64 | ||||||
|  | 	x0f := x0 - fixed.Int26_6(64*x0i) | ||||||
|  | 	x1i := int(x1) / 64 | ||||||
|  | 	x1f := x1 - fixed.Int26_6(64*x1i) | ||||||
|  |  | ||||||
|  | 	// A perfectly horizontal scan. | ||||||
|  | 	if y0f == y1f { | ||||||
|  | 		r.setCell(x1i, yi) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	dx, dy := x1-x0, y1f-y0f | ||||||
|  | 	// A single cell scan. | ||||||
|  | 	if x0i == x1i { | ||||||
|  | 		r.area += int((x0f + x1f) * dy) | ||||||
|  | 		r.cover += int(dy) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	// There are at least two cells. Apart from the first and last cells, | ||||||
|  | 	// all intermediate cells go through the full width of the cell, | ||||||
|  | 	// or 64 units in 26.6 fixed point format. | ||||||
|  | 	var ( | ||||||
|  | 		p, q, edge0, edge1 fixed.Int26_6 | ||||||
|  | 		xiDelta            int | ||||||
|  | 	) | ||||||
|  | 	if dx > 0 { | ||||||
|  | 		p, q = (64-x0f)*dy, dx | ||||||
|  | 		edge0, edge1, xiDelta = 0, 64, 1 | ||||||
|  | 	} else { | ||||||
|  | 		p, q = x0f*dy, -dx | ||||||
|  | 		edge0, edge1, xiDelta = 64, 0, -1 | ||||||
|  | 	} | ||||||
|  | 	yDelta, yRem := p/q, p%q | ||||||
|  | 	if yRem < 0 { | ||||||
|  | 		yDelta -= 1 | ||||||
|  | 		yRem += q | ||||||
|  | 	} | ||||||
|  | 	// Do the first cell. | ||||||
|  | 	xi, y := x0i, y0f | ||||||
|  | 	r.area += int((x0f + edge1) * yDelta) | ||||||
|  | 	r.cover += int(yDelta) | ||||||
|  | 	xi, y = xi+xiDelta, y+yDelta | ||||||
|  | 	r.setCell(xi, yi) | ||||||
|  | 	if xi != x1i { | ||||||
|  | 		// Do all the intermediate cells. | ||||||
|  | 		p = 64 * (y1f - y + yDelta) | ||||||
|  | 		fullDelta, fullRem := p/q, p%q | ||||||
|  | 		if fullRem < 0 { | ||||||
|  | 			fullDelta -= 1 | ||||||
|  | 			fullRem += q | ||||||
|  | 		} | ||||||
|  | 		yRem -= q | ||||||
|  | 		for xi != x1i { | ||||||
|  | 			yDelta = fullDelta | ||||||
|  | 			yRem += fullRem | ||||||
|  | 			if yRem >= 0 { | ||||||
|  | 				yDelta += 1 | ||||||
|  | 				yRem -= q | ||||||
|  | 			} | ||||||
|  | 			r.area += int(64 * yDelta) | ||||||
|  | 			r.cover += int(yDelta) | ||||||
|  | 			xi, y = xi+xiDelta, y+yDelta | ||||||
|  | 			r.setCell(xi, yi) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// Do the last cell. | ||||||
|  | 	yDelta = y1f - y | ||||||
|  | 	r.area += int((edge0 + x1f) * yDelta) | ||||||
|  | 	r.cover += int(yDelta) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Start starts a new curve at the given point. | ||||||
|  | func (r *Rasterizer) Start(a fixed.Point26_6) { | ||||||
|  | 	r.setCell(int(a.X/64), int(a.Y/64)) | ||||||
|  | 	r.a = a | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add1 adds a linear segment to the current curve. | ||||||
|  | func (r *Rasterizer) Add1(b fixed.Point26_6) { | ||||||
|  | 	x0, y0 := r.a.X, r.a.Y | ||||||
|  | 	x1, y1 := b.X, b.Y | ||||||
|  | 	dx, dy := x1-x0, y1-y0 | ||||||
|  | 	// Break the 26.6 fixed point Y co-ordinates into integral and fractional | ||||||
|  | 	// parts. | ||||||
|  | 	y0i := int(y0) / 64 | ||||||
|  | 	y0f := y0 - fixed.Int26_6(64*y0i) | ||||||
|  | 	y1i := int(y1) / 64 | ||||||
|  | 	y1f := y1 - fixed.Int26_6(64*y1i) | ||||||
|  |  | ||||||
|  | 	if y0i == y1i { | ||||||
|  | 		// There is only one scanline. | ||||||
|  | 		r.scan(y0i, x0, y0f, x1, y1f) | ||||||
|  |  | ||||||
|  | 	} else if dx == 0 { | ||||||
|  | 		// This is a vertical line segment. We avoid calling r.scan and instead | ||||||
|  | 		// manipulate r.area and r.cover directly. | ||||||
|  | 		var ( | ||||||
|  | 			edge0, edge1 fixed.Int26_6 | ||||||
|  | 			yiDelta      int | ||||||
|  | 		) | ||||||
|  | 		if dy > 0 { | ||||||
|  | 			edge0, edge1, yiDelta = 0, 64, 1 | ||||||
|  | 		} else { | ||||||
|  | 			edge0, edge1, yiDelta = 64, 0, -1 | ||||||
|  | 		} | ||||||
|  | 		x0i, yi := int(x0)/64, y0i | ||||||
|  | 		x0fTimes2 := (int(x0) - (64 * x0i)) * 2 | ||||||
|  | 		// Do the first pixel. | ||||||
|  | 		dcover := int(edge1 - y0f) | ||||||
|  | 		darea := int(x0fTimes2 * dcover) | ||||||
|  | 		r.area += darea | ||||||
|  | 		r.cover += dcover | ||||||
|  | 		yi += yiDelta | ||||||
|  | 		r.setCell(x0i, yi) | ||||||
|  | 		// Do all the intermediate pixels. | ||||||
|  | 		dcover = int(edge1 - edge0) | ||||||
|  | 		darea = int(x0fTimes2 * dcover) | ||||||
|  | 		for yi != y1i { | ||||||
|  | 			r.area += darea | ||||||
|  | 			r.cover += dcover | ||||||
|  | 			yi += yiDelta | ||||||
|  | 			r.setCell(x0i, yi) | ||||||
|  | 		} | ||||||
|  | 		// Do the last pixel. | ||||||
|  | 		dcover = int(y1f - edge0) | ||||||
|  | 		darea = int(x0fTimes2 * dcover) | ||||||
|  | 		r.area += darea | ||||||
|  | 		r.cover += dcover | ||||||
|  |  | ||||||
|  | 	} else { | ||||||
|  | 		// There are at least two scanlines. Apart from the first and last | ||||||
|  | 		// scanlines, all intermediate scanlines go through the full height of | ||||||
|  | 		// the row, or 64 units in 26.6 fixed point format. | ||||||
|  | 		var ( | ||||||
|  | 			p, q, edge0, edge1 fixed.Int26_6 | ||||||
|  | 			yiDelta            int | ||||||
|  | 		) | ||||||
|  | 		if dy > 0 { | ||||||
|  | 			p, q = (64-y0f)*dx, dy | ||||||
|  | 			edge0, edge1, yiDelta = 0, 64, 1 | ||||||
|  | 		} else { | ||||||
|  | 			p, q = y0f*dx, -dy | ||||||
|  | 			edge0, edge1, yiDelta = 64, 0, -1 | ||||||
|  | 		} | ||||||
|  | 		xDelta, xRem := p/q, p%q | ||||||
|  | 		if xRem < 0 { | ||||||
|  | 			xDelta -= 1 | ||||||
|  | 			xRem += q | ||||||
|  | 		} | ||||||
|  | 		// Do the first scanline. | ||||||
|  | 		x, yi := x0, y0i | ||||||
|  | 		r.scan(yi, x, y0f, x+xDelta, edge1) | ||||||
|  | 		x, yi = x+xDelta, yi+yiDelta | ||||||
|  | 		r.setCell(int(x)/64, yi) | ||||||
|  | 		if yi != y1i { | ||||||
|  | 			// Do all the intermediate scanlines. | ||||||
|  | 			p = 64 * dx | ||||||
|  | 			fullDelta, fullRem := p/q, p%q | ||||||
|  | 			if fullRem < 0 { | ||||||
|  | 				fullDelta -= 1 | ||||||
|  | 				fullRem += q | ||||||
|  | 			} | ||||||
|  | 			xRem -= q | ||||||
|  | 			for yi != y1i { | ||||||
|  | 				xDelta = fullDelta | ||||||
|  | 				xRem += fullRem | ||||||
|  | 				if xRem >= 0 { | ||||||
|  | 					xDelta += 1 | ||||||
|  | 					xRem -= q | ||||||
|  | 				} | ||||||
|  | 				r.scan(yi, x, edge0, x+xDelta, edge1) | ||||||
|  | 				x, yi = x+xDelta, yi+yiDelta | ||||||
|  | 				r.setCell(int(x)/64, yi) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		// Do the last scanline. | ||||||
|  | 		r.scan(yi, x, edge0, x1, y1f) | ||||||
|  | 	} | ||||||
|  | 	// The next lineTo starts from b. | ||||||
|  | 	r.a = b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add2 adds a quadratic segment to the current curve. | ||||||
|  | func (r *Rasterizer) Add2(b, c fixed.Point26_6) { | ||||||
|  | 	// Calculate nSplit (the number of recursive decompositions) based on how | ||||||
|  | 	// 'curvy' it is. Specifically, how much the middle point b deviates from | ||||||
|  | 	// (a+c)/2. | ||||||
|  | 	dev := maxAbs(r.a.X-2*b.X+c.X, r.a.Y-2*b.Y+c.Y) / fixed.Int26_6(r.splitScale2) | ||||||
|  | 	nsplit := 0 | ||||||
|  | 	for dev > 0 { | ||||||
|  | 		dev /= 4 | ||||||
|  | 		nsplit++ | ||||||
|  | 	} | ||||||
|  | 	// dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit | ||||||
|  | 	// is 16. | ||||||
|  | 	const maxNsplit = 16 | ||||||
|  | 	if nsplit > maxNsplit { | ||||||
|  | 		panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa(nsplit)) | ||||||
|  | 	} | ||||||
|  | 	// Recursively decompose the curve nSplit levels deep. | ||||||
|  | 	var ( | ||||||
|  | 		pStack [2*maxNsplit + 3]fixed.Point26_6 | ||||||
|  | 		sStack [maxNsplit + 1]int | ||||||
|  | 		i      int | ||||||
|  | 	) | ||||||
|  | 	sStack[0] = nsplit | ||||||
|  | 	pStack[0] = c | ||||||
|  | 	pStack[1] = b | ||||||
|  | 	pStack[2] = r.a | ||||||
|  | 	for i >= 0 { | ||||||
|  | 		s := sStack[i] | ||||||
|  | 		p := pStack[2*i:] | ||||||
|  | 		if s > 0 { | ||||||
|  | 			// Split the quadratic curve p[:3] into an equivalent set of two | ||||||
|  | 			// shorter curves: p[:3] and p[2:5]. The new p[4] is the old p[2], | ||||||
|  | 			// and p[0] is unchanged. | ||||||
|  | 			mx := p[1].X | ||||||
|  | 			p[4].X = p[2].X | ||||||
|  | 			p[3].X = (p[4].X + mx) / 2 | ||||||
|  | 			p[1].X = (p[0].X + mx) / 2 | ||||||
|  | 			p[2].X = (p[1].X + p[3].X) / 2 | ||||||
|  | 			my := p[1].Y | ||||||
|  | 			p[4].Y = p[2].Y | ||||||
|  | 			p[3].Y = (p[4].Y + my) / 2 | ||||||
|  | 			p[1].Y = (p[0].Y + my) / 2 | ||||||
|  | 			p[2].Y = (p[1].Y + p[3].Y) / 2 | ||||||
|  | 			// The two shorter curves have one less split to do. | ||||||
|  | 			sStack[i] = s - 1 | ||||||
|  | 			sStack[i+1] = s - 1 | ||||||
|  | 			i++ | ||||||
|  | 		} else { | ||||||
|  | 			// Replace the level-0 quadratic with a two-linear-piece | ||||||
|  | 			// approximation. | ||||||
|  | 			midx := (p[0].X + 2*p[1].X + p[2].X) / 4 | ||||||
|  | 			midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 4 | ||||||
|  | 			r.Add1(fixed.Point26_6{midx, midy}) | ||||||
|  | 			r.Add1(p[0]) | ||||||
|  | 			i-- | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add3 adds a cubic segment to the current curve. | ||||||
|  | func (r *Rasterizer) Add3(b, c, d fixed.Point26_6) { | ||||||
|  | 	// Calculate nSplit (the number of recursive decompositions) based on how | ||||||
|  | 	// 'curvy' it is. | ||||||
|  | 	dev2 := maxAbs(r.a.X-3*(b.X+c.X)+d.X, r.a.Y-3*(b.Y+c.Y)+d.Y) / fixed.Int26_6(r.splitScale2) | ||||||
|  | 	dev3 := maxAbs(r.a.X-2*b.X+d.X, r.a.Y-2*b.Y+d.Y) / fixed.Int26_6(r.splitScale3) | ||||||
|  | 	nsplit := 0 | ||||||
|  | 	for dev2 > 0 || dev3 > 0 { | ||||||
|  | 		dev2 /= 8 | ||||||
|  | 		dev3 /= 4 | ||||||
|  | 		nsplit++ | ||||||
|  | 	} | ||||||
|  | 	// devN is 32-bit, and nsplit++ every time we shift off 2 bits, so | ||||||
|  | 	// maxNsplit is 16. | ||||||
|  | 	const maxNsplit = 16 | ||||||
|  | 	if nsplit > maxNsplit { | ||||||
|  | 		panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa(nsplit)) | ||||||
|  | 	} | ||||||
|  | 	// Recursively decompose the curve nSplit levels deep. | ||||||
|  | 	var ( | ||||||
|  | 		pStack [3*maxNsplit + 4]fixed.Point26_6 | ||||||
|  | 		sStack [maxNsplit + 1]int | ||||||
|  | 		i      int | ||||||
|  | 	) | ||||||
|  | 	sStack[0] = nsplit | ||||||
|  | 	pStack[0] = d | ||||||
|  | 	pStack[1] = c | ||||||
|  | 	pStack[2] = b | ||||||
|  | 	pStack[3] = r.a | ||||||
|  | 	for i >= 0 { | ||||||
|  | 		s := sStack[i] | ||||||
|  | 		p := pStack[3*i:] | ||||||
|  | 		if s > 0 { | ||||||
|  | 			// Split the cubic curve p[:4] into an equivalent set of two | ||||||
|  | 			// shorter curves: p[:4] and p[3:7]. The new p[6] is the old p[3], | ||||||
|  | 			// and p[0] is unchanged. | ||||||
|  | 			m01x := (p[0].X + p[1].X) / 2 | ||||||
|  | 			m12x := (p[1].X + p[2].X) / 2 | ||||||
|  | 			m23x := (p[2].X + p[3].X) / 2 | ||||||
|  | 			p[6].X = p[3].X | ||||||
|  | 			p[5].X = m23x | ||||||
|  | 			p[1].X = m01x | ||||||
|  | 			p[2].X = (m01x + m12x) / 2 | ||||||
|  | 			p[4].X = (m12x + m23x) / 2 | ||||||
|  | 			p[3].X = (p[2].X + p[4].X) / 2 | ||||||
|  | 			m01y := (p[0].Y + p[1].Y) / 2 | ||||||
|  | 			m12y := (p[1].Y + p[2].Y) / 2 | ||||||
|  | 			m23y := (p[2].Y + p[3].Y) / 2 | ||||||
|  | 			p[6].Y = p[3].Y | ||||||
|  | 			p[5].Y = m23y | ||||||
|  | 			p[1].Y = m01y | ||||||
|  | 			p[2].Y = (m01y + m12y) / 2 | ||||||
|  | 			p[4].Y = (m12y + m23y) / 2 | ||||||
|  | 			p[3].Y = (p[2].Y + p[4].Y) / 2 | ||||||
|  | 			// The two shorter curves have one less split to do. | ||||||
|  | 			sStack[i] = s - 1 | ||||||
|  | 			sStack[i+1] = s - 1 | ||||||
|  | 			i++ | ||||||
|  | 		} else { | ||||||
|  | 			// Replace the level-0 cubic with a two-linear-piece approximation. | ||||||
|  | 			midx := (p[0].X + 3*(p[1].X+p[2].X) + p[3].X) / 8 | ||||||
|  | 			midy := (p[0].Y + 3*(p[1].Y+p[2].Y) + p[3].Y) / 8 | ||||||
|  | 			r.Add1(fixed.Point26_6{midx, midy}) | ||||||
|  | 			r.Add1(p[0]) | ||||||
|  | 			i-- | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AddPath adds the given Path. | ||||||
|  | func (r *Rasterizer) AddPath(p Path) { | ||||||
|  | 	for i := 0; i < len(p); { | ||||||
|  | 		switch p[i] { | ||||||
|  | 		case 0: | ||||||
|  | 			r.Start( | ||||||
|  | 				fixed.Point26_6{p[i+1], p[i+2]}, | ||||||
|  | 			) | ||||||
|  | 			i += 4 | ||||||
|  | 		case 1: | ||||||
|  | 			r.Add1( | ||||||
|  | 				fixed.Point26_6{p[i+1], p[i+2]}, | ||||||
|  | 			) | ||||||
|  | 			i += 4 | ||||||
|  | 		case 2: | ||||||
|  | 			r.Add2( | ||||||
|  | 				fixed.Point26_6{p[i+1], p[i+2]}, | ||||||
|  | 				fixed.Point26_6{p[i+3], p[i+4]}, | ||||||
|  | 			) | ||||||
|  | 			i += 6 | ||||||
|  | 		case 3: | ||||||
|  | 			r.Add3( | ||||||
|  | 				fixed.Point26_6{p[i+1], p[i+2]}, | ||||||
|  | 				fixed.Point26_6{p[i+3], p[i+4]}, | ||||||
|  | 				fixed.Point26_6{p[i+5], p[i+6]}, | ||||||
|  | 			) | ||||||
|  | 			i += 8 | ||||||
|  | 		default: | ||||||
|  | 			panic("freetype/raster: bad path") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AddStroke adds a stroked Path. | ||||||
|  | func (r *Rasterizer) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) { | ||||||
|  | 	Stroke(r, q, width, cr, jr) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // areaToAlpha converts an area value to a uint32 alpha value. A completely | ||||||
|  | // filled pixel corresponds to an area of 64*64*2, and an alpha of 0xffff. The | ||||||
|  | // conversion of area values greater than this depends on the winding rule: | ||||||
|  | // even-odd or non-zero. | ||||||
|  | func (r *Rasterizer) areaToAlpha(area int) uint32 { | ||||||
|  | 	// The C Freetype implementation (version 2.3.12) does "alpha := area>>1" | ||||||
|  | 	// without the +1. Round-to-nearest gives a more symmetric result than | ||||||
|  | 	// round-down. The C implementation also returns 8-bit alpha, not 16-bit | ||||||
|  | 	// alpha. | ||||||
|  | 	a := (area + 1) >> 1 | ||||||
|  | 	if a < 0 { | ||||||
|  | 		a = -a | ||||||
|  | 	} | ||||||
|  | 	alpha := uint32(a) | ||||||
|  | 	if r.UseNonZeroWinding { | ||||||
|  | 		if alpha > 0x0fff { | ||||||
|  | 			alpha = 0x0fff | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		alpha &= 0x1fff | ||||||
|  | 		if alpha > 0x1000 { | ||||||
|  | 			alpha = 0x2000 - alpha | ||||||
|  | 		} else if alpha == 0x1000 { | ||||||
|  | 			alpha = 0x0fff | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// alpha is now in the range [0x0000, 0x0fff]. Convert that 12-bit alpha to | ||||||
|  | 	// 16-bit alpha. | ||||||
|  | 	return alpha<<4 | alpha>>8 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Rasterize converts r's accumulated curves into Spans for p. The Spans passed | ||||||
|  | // to p are non-overlapping, and sorted by Y and then X. They all have non-zero | ||||||
|  | // width (and 0 <= X0 < X1 <= r.width) and non-zero A, except for the final | ||||||
|  | // Span, which has Y, X0, X1 and A all equal to zero. | ||||||
|  | func (r *Rasterizer) Rasterize(p Painter) { | ||||||
|  | 	r.saveCell() | ||||||
|  | 	s := 0 | ||||||
|  | 	for yi := 0; yi < len(r.cellIndex); yi++ { | ||||||
|  | 		xi, cover := 0, 0 | ||||||
|  | 		for c := r.cellIndex[yi]; c != -1; c = r.cell[c].next { | ||||||
|  | 			if cover != 0 && r.cell[c].xi > xi { | ||||||
|  | 				alpha := r.areaToAlpha(cover * 64 * 2) | ||||||
|  | 				if alpha != 0 { | ||||||
|  | 					xi0, xi1 := xi, r.cell[c].xi | ||||||
|  | 					if xi0 < 0 { | ||||||
|  | 						xi0 = 0 | ||||||
|  | 					} | ||||||
|  | 					if xi1 >= r.width { | ||||||
|  | 						xi1 = r.width | ||||||
|  | 					} | ||||||
|  | 					if xi0 < xi1 { | ||||||
|  | 						r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha} | ||||||
|  | 						s++ | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			cover += r.cell[c].cover | ||||||
|  | 			alpha := r.areaToAlpha(cover*64*2 - r.cell[c].area) | ||||||
|  | 			xi = r.cell[c].xi + 1 | ||||||
|  | 			if alpha != 0 { | ||||||
|  | 				xi0, xi1 := r.cell[c].xi, xi | ||||||
|  | 				if xi0 < 0 { | ||||||
|  | 					xi0 = 0 | ||||||
|  | 				} | ||||||
|  | 				if xi1 >= r.width { | ||||||
|  | 					xi1 = r.width | ||||||
|  | 				} | ||||||
|  | 				if xi0 < xi1 { | ||||||
|  | 					r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha} | ||||||
|  | 					s++ | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if s > len(r.spanBuf)-2 { | ||||||
|  | 				p.Paint(r.spanBuf[:s], false) | ||||||
|  | 				s = 0 | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	p.Paint(r.spanBuf[:s], true) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Clear cancels any previous calls to r.Start or r.AddXxx. | ||||||
|  | func (r *Rasterizer) Clear() { | ||||||
|  | 	r.a = fixed.Point26_6{} | ||||||
|  | 	r.xi = 0 | ||||||
|  | 	r.yi = 0 | ||||||
|  | 	r.area = 0 | ||||||
|  | 	r.cover = 0 | ||||||
|  | 	r.cell = r.cell[:0] | ||||||
|  | 	for i := 0; i < len(r.cellIndex); i++ { | ||||||
|  | 		r.cellIndex[i] = -1 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetBounds sets the maximum width and height of the rasterized image and | ||||||
|  | // calls Clear. The width and height are in pixels, not fixed.Int26_6 units. | ||||||
|  | func (r *Rasterizer) SetBounds(width, height int) { | ||||||
|  | 	if width < 0 { | ||||||
|  | 		width = 0 | ||||||
|  | 	} | ||||||
|  | 	if height < 0 { | ||||||
|  | 		height = 0 | ||||||
|  | 	} | ||||||
|  | 	// Use the same ssN heuristic as the C Freetype (version 2.4.0) | ||||||
|  | 	// implementation. | ||||||
|  | 	ss2, ss3 := 32, 16 | ||||||
|  | 	if width > 24 || height > 24 { | ||||||
|  | 		ss2, ss3 = 2*ss2, 2*ss3 | ||||||
|  | 		if width > 120 || height > 120 { | ||||||
|  | 			ss2, ss3 = 2*ss2, 2*ss3 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	r.width = width | ||||||
|  | 	r.splitScale2 = ss2 | ||||||
|  | 	r.splitScale3 = ss3 | ||||||
|  | 	r.cell = r.cellBuf[:0] | ||||||
|  | 	if height > len(r.cellIndexBuf) { | ||||||
|  | 		r.cellIndex = make([]int, height) | ||||||
|  | 	} else { | ||||||
|  | 		r.cellIndex = r.cellIndexBuf[:height] | ||||||
|  | 	} | ||||||
|  | 	r.Clear() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewRasterizer creates a new Rasterizer with the given bounds. | ||||||
|  | func NewRasterizer(width, height int) *Rasterizer { | ||||||
|  | 	r := new(Rasterizer) | ||||||
|  | 	r.SetBounds(width, height) | ||||||
|  | 	return r | ||||||
|  | } | ||||||
							
								
								
									
										483
									
								
								vendor/github.com/golang/freetype/raster/stroke.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										483
									
								
								vendor/github.com/golang/freetype/raster/stroke.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,483 @@ | |||||||
|  | // Copyright 2010 The Freetype-Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by your choice of either the | ||||||
|  | // FreeType License or the GNU General Public License version 2 (or | ||||||
|  | // any later version), both of which can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package raster | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"golang.org/x/image/math/fixed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Two points are considered practically equal if the square of the distance | ||||||
|  | // between them is less than one quarter (i.e. 1024 / 4096). | ||||||
|  | const epsilon = fixed.Int52_12(1024) | ||||||
|  |  | ||||||
|  | // A Capper signifies how to begin or end a stroked path. | ||||||
|  | type Capper interface { | ||||||
|  | 	// Cap adds a cap to p given a pivot point and the normal vector of a | ||||||
|  | 	// terminal segment. The normal's length is half of the stroke width. | ||||||
|  | 	Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // The CapperFunc type adapts an ordinary function to be a Capper. | ||||||
|  | type CapperFunc func(Adder, fixed.Int26_6, fixed.Point26_6, fixed.Point26_6) | ||||||
|  |  | ||||||
|  | func (f CapperFunc) Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) { | ||||||
|  | 	f(p, halfWidth, pivot, n1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // A Joiner signifies how to join interior nodes of a stroked path. | ||||||
|  | type Joiner interface { | ||||||
|  | 	// Join adds a join to the two sides of a stroked path given a pivot | ||||||
|  | 	// point and the normal vectors of the trailing and leading segments. | ||||||
|  | 	// Both normals have length equal to half of the stroke width. | ||||||
|  | 	Join(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // The JoinerFunc type adapts an ordinary function to be a Joiner. | ||||||
|  | type JoinerFunc func(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) | ||||||
|  |  | ||||||
|  | func (f JoinerFunc) Join(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) { | ||||||
|  | 	f(lhs, rhs, halfWidth, pivot, n0, n1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RoundCapper adds round caps to a stroked path. | ||||||
|  | var RoundCapper Capper = CapperFunc(roundCapper) | ||||||
|  |  | ||||||
|  | func roundCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) { | ||||||
|  | 	// The cubic Bézier approximation to a circle involves the magic number | ||||||
|  | 	// (√2 - 1) * 4/3, which is approximately 35/64. | ||||||
|  | 	const k = 35 | ||||||
|  | 	e := pRot90CCW(n1) | ||||||
|  | 	side := pivot.Add(e) | ||||||
|  | 	start, end := pivot.Sub(n1), pivot.Add(n1) | ||||||
|  | 	d, e := n1.Mul(k), e.Mul(k) | ||||||
|  | 	p.Add3(start.Add(e), side.Sub(d), side) | ||||||
|  | 	p.Add3(side.Add(d), end.Add(e), end) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ButtCapper adds butt caps to a stroked path. | ||||||
|  | var ButtCapper Capper = CapperFunc(buttCapper) | ||||||
|  |  | ||||||
|  | func buttCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) { | ||||||
|  | 	p.Add1(pivot.Add(n1)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SquareCapper adds square caps to a stroked path. | ||||||
|  | var SquareCapper Capper = CapperFunc(squareCapper) | ||||||
|  |  | ||||||
|  | func squareCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) { | ||||||
|  | 	e := pRot90CCW(n1) | ||||||
|  | 	side := pivot.Add(e) | ||||||
|  | 	p.Add1(side.Sub(n1)) | ||||||
|  | 	p.Add1(side.Add(n1)) | ||||||
|  | 	p.Add1(pivot.Add(n1)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RoundJoiner adds round joins to a stroked path. | ||||||
|  | var RoundJoiner Joiner = JoinerFunc(roundJoiner) | ||||||
|  |  | ||||||
|  | func roundJoiner(lhs, rhs Adder, haflWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) { | ||||||
|  | 	dot := pDot(pRot90CW(n0), n1) | ||||||
|  | 	if dot >= 0 { | ||||||
|  | 		addArc(lhs, pivot, n0, n1) | ||||||
|  | 		rhs.Add1(pivot.Sub(n1)) | ||||||
|  | 	} else { | ||||||
|  | 		lhs.Add1(pivot.Add(n1)) | ||||||
|  | 		addArc(rhs, pivot, pNeg(n0), pNeg(n1)) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BevelJoiner adds bevel joins to a stroked path. | ||||||
|  | var BevelJoiner Joiner = JoinerFunc(bevelJoiner) | ||||||
|  |  | ||||||
|  | func bevelJoiner(lhs, rhs Adder, haflWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) { | ||||||
|  | 	lhs.Add1(pivot.Add(n1)) | ||||||
|  | 	rhs.Add1(pivot.Sub(n1)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of | ||||||
|  | // the two possible arcs is taken, i.e. the one spanning <= 180 degrees. The | ||||||
|  | // two vectors n0 and n1 must be of equal length. | ||||||
|  | func addArc(p Adder, pivot, n0, n1 fixed.Point26_6) { | ||||||
|  | 	// r2 is the square of the length of n0. | ||||||
|  | 	r2 := pDot(n0, n0) | ||||||
|  | 	if r2 < epsilon { | ||||||
|  | 		// The arc radius is so small that we collapse to a straight line. | ||||||
|  | 		p.Add1(pivot.Add(n1)) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	// We approximate the arc by 0, 1, 2 or 3 45-degree quadratic segments plus | ||||||
|  | 	// a final quadratic segment from s to n1. Each 45-degree segment has | ||||||
|  | 	// control points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled, | ||||||
|  | 	// rotated and translated. tan(π/8) is approximately 27/64. | ||||||
|  | 	const tpo8 = 27 | ||||||
|  | 	var s fixed.Point26_6 | ||||||
|  | 	// We determine which octant the angle between n0 and n1 is in via three | ||||||
|  | 	// dot products. m0, m1 and m2 are n0 rotated clockwise by 45, 90 and 135 | ||||||
|  | 	// degrees. | ||||||
|  | 	m0 := pRot45CW(n0) | ||||||
|  | 	m1 := pRot90CW(n0) | ||||||
|  | 	m2 := pRot90CW(m0) | ||||||
|  | 	if pDot(m1, n1) >= 0 { | ||||||
|  | 		if pDot(n0, n1) >= 0 { | ||||||
|  | 			if pDot(m2, n1) <= 0 { | ||||||
|  | 				// n1 is between 0 and 45 degrees clockwise of n0. | ||||||
|  | 				s = n0 | ||||||
|  | 			} else { | ||||||
|  | 				// n1 is between 45 and 90 degrees clockwise of n0. | ||||||
|  | 				p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0)) | ||||||
|  | 				s = m0 | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			pm1, n0t := pivot.Add(m1), n0.Mul(tpo8) | ||||||
|  | 			p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0)) | ||||||
|  | 			p.Add2(pm1.Add(n0t), pm1) | ||||||
|  | 			if pDot(m0, n1) >= 0 { | ||||||
|  | 				// n1 is between 90 and 135 degrees clockwise of n0. | ||||||
|  | 				s = m1 | ||||||
|  | 			} else { | ||||||
|  | 				// n1 is between 135 and 180 degrees clockwise of n0. | ||||||
|  | 				p.Add2(pm1.Sub(n0t), pivot.Add(m2)) | ||||||
|  | 				s = m2 | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		if pDot(n0, n1) >= 0 { | ||||||
|  | 			if pDot(m0, n1) >= 0 { | ||||||
|  | 				// n1 is between 0 and 45 degrees counter-clockwise of n0. | ||||||
|  | 				s = n0 | ||||||
|  | 			} else { | ||||||
|  | 				// n1 is between 45 and 90 degrees counter-clockwise of n0. | ||||||
|  | 				p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2)) | ||||||
|  | 				s = pNeg(m2) | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			pm1, n0t := pivot.Sub(m1), n0.Mul(tpo8) | ||||||
|  | 			p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2)) | ||||||
|  | 			p.Add2(pm1.Add(n0t), pm1) | ||||||
|  | 			if pDot(m2, n1) <= 0 { | ||||||
|  | 				// n1 is between 90 and 135 degrees counter-clockwise of n0. | ||||||
|  | 				s = pNeg(m1) | ||||||
|  | 			} else { | ||||||
|  | 				// n1 is between 135 and 180 degrees counter-clockwise of n0. | ||||||
|  | 				p.Add2(pm1.Sub(n0t), pivot.Sub(m0)) | ||||||
|  | 				s = pNeg(m0) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// The final quadratic segment has two endpoints s and n1 and the middle | ||||||
|  | 	// control point is a multiple of s.Add(n1), i.e. it is on the angle | ||||||
|  | 	// bisector of those two points. The multiple ranges between 128/256 and | ||||||
|  | 	// 150/256 as the angle between s and n1 ranges between 0 and 45 degrees. | ||||||
|  | 	// | ||||||
|  | 	// When the angle is 0 degrees (i.e. s and n1 are coincident) then | ||||||
|  | 	// s.Add(n1) is twice s and so the middle control point of the degenerate | ||||||
|  | 	// quadratic segment should be half s.Add(n1), and half = 128/256. | ||||||
|  | 	// | ||||||
|  | 	// When the angle is 45 degrees then 150/256 is the ratio of the lengths of | ||||||
|  | 	// the two vectors {1, tan(π/8)} and {1 + 1/√2, 1/√2}. | ||||||
|  | 	// | ||||||
|  | 	// d is the normalized dot product between s and n1. Since the angle ranges | ||||||
|  | 	// between 0 and 45 degrees then d ranges between 256/256 and 181/256. | ||||||
|  | 	d := 256 * pDot(s, n1) / r2 | ||||||
|  | 	multiple := fixed.Int26_6(150-(150-128)*(d-181)/(256-181)) >> 2 | ||||||
|  | 	p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // midpoint returns the midpoint of two Points. | ||||||
|  | func midpoint(a, b fixed.Point26_6) fixed.Point26_6 { | ||||||
|  | 	return fixed.Point26_6{(a.X + b.X) / 2, (a.Y + b.Y) / 2} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // angleGreaterThan45 returns whether the angle between two vectors is more | ||||||
|  | // than 45 degrees. | ||||||
|  | func angleGreaterThan45(v0, v1 fixed.Point26_6) bool { | ||||||
|  | 	v := pRot45CCW(v0) | ||||||
|  | 	return pDot(v, v1) < 0 || pDot(pRot90CW(v), v1) < 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // interpolate returns the point (1-t)*a + t*b. | ||||||
|  | func interpolate(a, b fixed.Point26_6, t fixed.Int52_12) fixed.Point26_6 { | ||||||
|  | 	s := 1<<12 - t | ||||||
|  | 	x := s*fixed.Int52_12(a.X) + t*fixed.Int52_12(b.X) | ||||||
|  | 	y := s*fixed.Int52_12(a.Y) + t*fixed.Int52_12(b.Y) | ||||||
|  | 	return fixed.Point26_6{fixed.Int26_6(x >> 12), fixed.Int26_6(y >> 12)} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // curviest2 returns the value of t for which the quadratic parametric curve | ||||||
|  | // (1-t)²*a + 2*t*(1-t).b + t²*c has maximum curvature. | ||||||
|  | // | ||||||
|  | // The curvature of the parametric curve f(t) = (x(t), y(t)) is | ||||||
|  | // |x′y″-y′x″| / (x′²+y′²)^(3/2). | ||||||
|  | // | ||||||
|  | // Let d = b-a and e = c-2*b+a, so that f′(t) = 2*d+2*e*t and f″(t) = 2*e. | ||||||
|  | // The curvature's numerator is (2*dx+2*ex*t)*(2*ey)-(2*dy+2*ey*t)*(2*ex), | ||||||
|  | // which simplifies to 4*dx*ey-4*dy*ex, which is constant with respect to t. | ||||||
|  | // | ||||||
|  | // Thus, curvature is extreme where the denominator is extreme, i.e. where | ||||||
|  | // (x′²+y′²) is extreme. The first order condition is that | ||||||
|  | // 2*x′*x″+2*y′*y″ = 0, or (dx+ex*t)*ex + (dy+ey*t)*ey = 0. | ||||||
|  | // Solving for t gives t = -(dx*ex+dy*ey) / (ex*ex+ey*ey). | ||||||
|  | func curviest2(a, b, c fixed.Point26_6) fixed.Int52_12 { | ||||||
|  | 	dx := int64(b.X - a.X) | ||||||
|  | 	dy := int64(b.Y - a.Y) | ||||||
|  | 	ex := int64(c.X - 2*b.X + a.X) | ||||||
|  | 	ey := int64(c.Y - 2*b.Y + a.Y) | ||||||
|  | 	if ex == 0 && ey == 0 { | ||||||
|  | 		return 2048 | ||||||
|  | 	} | ||||||
|  | 	return fixed.Int52_12(-4096 * (dx*ex + dy*ey) / (ex*ex + ey*ey)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // A stroker holds state for stroking a path. | ||||||
|  | type stroker struct { | ||||||
|  | 	// p is the destination that records the stroked path. | ||||||
|  | 	p Adder | ||||||
|  | 	// u is the half-width of the stroke. | ||||||
|  | 	u fixed.Int26_6 | ||||||
|  | 	// cr and jr specify how to end and connect path segments. | ||||||
|  | 	cr Capper | ||||||
|  | 	jr Joiner | ||||||
|  | 	// r is the reverse path. Stroking a path involves constructing two | ||||||
|  | 	// parallel paths 2*u apart. The first path is added immediately to p, | ||||||
|  | 	// the second path is accumulated in r and eventually added in reverse. | ||||||
|  | 	r Path | ||||||
|  | 	// a is the most recent segment point. anorm is the segment normal of | ||||||
|  | 	// length u at that point. | ||||||
|  | 	a, anorm fixed.Point26_6 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // addNonCurvy2 adds a quadratic segment to the stroker, where the segment | ||||||
|  | // defined by (k.a, b, c) achieves maximum curvature at either k.a or c. | ||||||
|  | func (k *stroker) addNonCurvy2(b, c fixed.Point26_6) { | ||||||
|  | 	// We repeatedly divide the segment at its middle until it is straight | ||||||
|  | 	// enough to approximate the stroke by just translating the control points. | ||||||
|  | 	// ds and ps are stacks of depths and points. t is the top of the stack. | ||||||
|  | 	const maxDepth = 5 | ||||||
|  | 	var ( | ||||||
|  | 		ds [maxDepth + 1]int | ||||||
|  | 		ps [2*maxDepth + 3]fixed.Point26_6 | ||||||
|  | 		t  int | ||||||
|  | 	) | ||||||
|  | 	// Initially the ps stack has one quadratic segment of depth zero. | ||||||
|  | 	ds[0] = 0 | ||||||
|  | 	ps[2] = k.a | ||||||
|  | 	ps[1] = b | ||||||
|  | 	ps[0] = c | ||||||
|  | 	anorm := k.anorm | ||||||
|  | 	var cnorm fixed.Point26_6 | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		depth := ds[t] | ||||||
|  | 		a := ps[2*t+2] | ||||||
|  | 		b := ps[2*t+1] | ||||||
|  | 		c := ps[2*t+0] | ||||||
|  | 		ab := b.Sub(a) | ||||||
|  | 		bc := c.Sub(b) | ||||||
|  | 		abIsSmall := pDot(ab, ab) < fixed.Int52_12(1<<12) | ||||||
|  | 		bcIsSmall := pDot(bc, bc) < fixed.Int52_12(1<<12) | ||||||
|  | 		if abIsSmall && bcIsSmall { | ||||||
|  | 			// Approximate the segment by a circular arc. | ||||||
|  | 			cnorm = pRot90CCW(pNorm(bc, k.u)) | ||||||
|  | 			mac := midpoint(a, c) | ||||||
|  | 			addArc(k.p, mac, anorm, cnorm) | ||||||
|  | 			addArc(&k.r, mac, pNeg(anorm), pNeg(cnorm)) | ||||||
|  | 		} else if depth < maxDepth && angleGreaterThan45(ab, bc) { | ||||||
|  | 			// Divide the segment in two and push both halves on the stack. | ||||||
|  | 			mab := midpoint(a, b) | ||||||
|  | 			mbc := midpoint(b, c) | ||||||
|  | 			t++ | ||||||
|  | 			ds[t+0] = depth + 1 | ||||||
|  | 			ds[t-1] = depth + 1 | ||||||
|  | 			ps[2*t+2] = a | ||||||
|  | 			ps[2*t+1] = mab | ||||||
|  | 			ps[2*t+0] = midpoint(mab, mbc) | ||||||
|  | 			ps[2*t-1] = mbc | ||||||
|  | 			continue | ||||||
|  | 		} else { | ||||||
|  | 			// Translate the control points. | ||||||
|  | 			bnorm := pRot90CCW(pNorm(c.Sub(a), k.u)) | ||||||
|  | 			cnorm = pRot90CCW(pNorm(bc, k.u)) | ||||||
|  | 			k.p.Add2(b.Add(bnorm), c.Add(cnorm)) | ||||||
|  | 			k.r.Add2(b.Sub(bnorm), c.Sub(cnorm)) | ||||||
|  | 		} | ||||||
|  | 		if t == 0 { | ||||||
|  | 			k.a, k.anorm = c, cnorm | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		t-- | ||||||
|  | 		anorm = cnorm | ||||||
|  | 	} | ||||||
|  | 	panic("unreachable") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add1 adds a linear segment to the stroker. | ||||||
|  | func (k *stroker) Add1(b fixed.Point26_6) { | ||||||
|  | 	bnorm := pRot90CCW(pNorm(b.Sub(k.a), k.u)) | ||||||
|  | 	if len(k.r) == 0 { | ||||||
|  | 		k.p.Start(k.a.Add(bnorm)) | ||||||
|  | 		k.r.Start(k.a.Sub(bnorm)) | ||||||
|  | 	} else { | ||||||
|  | 		k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, bnorm) | ||||||
|  | 	} | ||||||
|  | 	k.p.Add1(b.Add(bnorm)) | ||||||
|  | 	k.r.Add1(b.Sub(bnorm)) | ||||||
|  | 	k.a, k.anorm = b, bnorm | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add2 adds a quadratic segment to the stroker. | ||||||
|  | func (k *stroker) Add2(b, c fixed.Point26_6) { | ||||||
|  | 	ab := b.Sub(k.a) | ||||||
|  | 	bc := c.Sub(b) | ||||||
|  | 	abnorm := pRot90CCW(pNorm(ab, k.u)) | ||||||
|  | 	if len(k.r) == 0 { | ||||||
|  | 		k.p.Start(k.a.Add(abnorm)) | ||||||
|  | 		k.r.Start(k.a.Sub(abnorm)) | ||||||
|  | 	} else { | ||||||
|  | 		k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, abnorm) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Approximate nearly-degenerate quadratics by linear segments. | ||||||
|  | 	abIsSmall := pDot(ab, ab) < epsilon | ||||||
|  | 	bcIsSmall := pDot(bc, bc) < epsilon | ||||||
|  | 	if abIsSmall || bcIsSmall { | ||||||
|  | 		acnorm := pRot90CCW(pNorm(c.Sub(k.a), k.u)) | ||||||
|  | 		k.p.Add1(c.Add(acnorm)) | ||||||
|  | 		k.r.Add1(c.Sub(acnorm)) | ||||||
|  | 		k.a, k.anorm = c, acnorm | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// The quadratic segment (k.a, b, c) has a point of maximum curvature. | ||||||
|  | 	// If this occurs at an end point, we process the segment as a whole. | ||||||
|  | 	t := curviest2(k.a, b, c) | ||||||
|  | 	if t <= 0 || 4096 <= t { | ||||||
|  | 		k.addNonCurvy2(b, c) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Otherwise, we perform a de Casteljau decomposition at the point of | ||||||
|  | 	// maximum curvature and process the two straighter parts. | ||||||
|  | 	mab := interpolate(k.a, b, t) | ||||||
|  | 	mbc := interpolate(b, c, t) | ||||||
|  | 	mabc := interpolate(mab, mbc, t) | ||||||
|  |  | ||||||
|  | 	// If the vectors ab and bc are close to being in opposite directions, | ||||||
|  | 	// then the decomposition can become unstable, so we approximate the | ||||||
|  | 	// quadratic segment by two linear segments joined by an arc. | ||||||
|  | 	bcnorm := pRot90CCW(pNorm(bc, k.u)) | ||||||
|  | 	if pDot(abnorm, bcnorm) < -fixed.Int52_12(k.u)*fixed.Int52_12(k.u)*2047/2048 { | ||||||
|  | 		pArc := pDot(abnorm, bc) < 0 | ||||||
|  |  | ||||||
|  | 		k.p.Add1(mabc.Add(abnorm)) | ||||||
|  | 		if pArc { | ||||||
|  | 			z := pRot90CW(abnorm) | ||||||
|  | 			addArc(k.p, mabc, abnorm, z) | ||||||
|  | 			addArc(k.p, mabc, z, bcnorm) | ||||||
|  | 		} | ||||||
|  | 		k.p.Add1(mabc.Add(bcnorm)) | ||||||
|  | 		k.p.Add1(c.Add(bcnorm)) | ||||||
|  |  | ||||||
|  | 		k.r.Add1(mabc.Sub(abnorm)) | ||||||
|  | 		if !pArc { | ||||||
|  | 			z := pRot90CW(abnorm) | ||||||
|  | 			addArc(&k.r, mabc, pNeg(abnorm), z) | ||||||
|  | 			addArc(&k.r, mabc, z, pNeg(bcnorm)) | ||||||
|  | 		} | ||||||
|  | 		k.r.Add1(mabc.Sub(bcnorm)) | ||||||
|  | 		k.r.Add1(c.Sub(bcnorm)) | ||||||
|  |  | ||||||
|  | 		k.a, k.anorm = c, bcnorm | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Process the decomposed parts. | ||||||
|  | 	k.addNonCurvy2(mab, mabc) | ||||||
|  | 	k.addNonCurvy2(mbc, c) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add3 adds a cubic segment to the stroker. | ||||||
|  | func (k *stroker) Add3(b, c, d fixed.Point26_6) { | ||||||
|  | 	panic("freetype/raster: stroke unimplemented for cubic segments") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // stroke adds the stroked Path q to p, where q consists of exactly one curve. | ||||||
|  | func (k *stroker) stroke(q Path) { | ||||||
|  | 	// Stroking is implemented by deriving two paths each k.u apart from q. | ||||||
|  | 	// The left-hand-side path is added immediately to k.p; the right-hand-side | ||||||
|  | 	// path is accumulated in k.r. Once we've finished adding the LHS to k.p, | ||||||
|  | 	// we add the RHS in reverse order. | ||||||
|  | 	k.r = make(Path, 0, len(q)) | ||||||
|  | 	k.a = fixed.Point26_6{q[1], q[2]} | ||||||
|  | 	for i := 4; i < len(q); { | ||||||
|  | 		switch q[i] { | ||||||
|  | 		case 1: | ||||||
|  | 			k.Add1( | ||||||
|  | 				fixed.Point26_6{q[i+1], q[i+2]}, | ||||||
|  | 			) | ||||||
|  | 			i += 4 | ||||||
|  | 		case 2: | ||||||
|  | 			k.Add2( | ||||||
|  | 				fixed.Point26_6{q[i+1], q[i+2]}, | ||||||
|  | 				fixed.Point26_6{q[i+3], q[i+4]}, | ||||||
|  | 			) | ||||||
|  | 			i += 6 | ||||||
|  | 		case 3: | ||||||
|  | 			k.Add3( | ||||||
|  | 				fixed.Point26_6{q[i+1], q[i+2]}, | ||||||
|  | 				fixed.Point26_6{q[i+3], q[i+4]}, | ||||||
|  | 				fixed.Point26_6{q[i+5], q[i+6]}, | ||||||
|  | 			) | ||||||
|  | 			i += 8 | ||||||
|  | 		default: | ||||||
|  | 			panic("freetype/raster: bad path") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if len(k.r) == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	// TODO(nigeltao): if q is a closed curve then we should join the first and | ||||||
|  | 	// last segments instead of capping them. | ||||||
|  | 	k.cr.Cap(k.p, k.u, q.lastPoint(), pNeg(k.anorm)) | ||||||
|  | 	addPathReversed(k.p, k.r) | ||||||
|  | 	pivot := q.firstPoint() | ||||||
|  | 	k.cr.Cap(k.p, k.u, pivot, pivot.Sub(fixed.Point26_6{k.r[1], k.r[2]})) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Stroke adds q stroked with the given width to p. The result is typically | ||||||
|  | // self-intersecting and should be rasterized with UseNonZeroWinding. | ||||||
|  | // cr and jr may be nil, which defaults to a RoundCapper or RoundJoiner. | ||||||
|  | func Stroke(p Adder, q Path, width fixed.Int26_6, cr Capper, jr Joiner) { | ||||||
|  | 	if len(q) == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if cr == nil { | ||||||
|  | 		cr = RoundCapper | ||||||
|  | 	} | ||||||
|  | 	if jr == nil { | ||||||
|  | 		jr = RoundJoiner | ||||||
|  | 	} | ||||||
|  | 	if q[0] != 0 { | ||||||
|  | 		panic("freetype/raster: bad path") | ||||||
|  | 	} | ||||||
|  | 	s := stroker{p: p, u: width / 2, cr: cr, jr: jr} | ||||||
|  | 	i := 0 | ||||||
|  | 	for j := 4; j < len(q); { | ||||||
|  | 		switch q[j] { | ||||||
|  | 		case 0: | ||||||
|  | 			s.stroke(q[i:j]) | ||||||
|  | 			i, j = j, j+4 | ||||||
|  | 		case 1: | ||||||
|  | 			j += 4 | ||||||
|  | 		case 2: | ||||||
|  | 			j += 6 | ||||||
|  | 		case 3: | ||||||
|  | 			j += 8 | ||||||
|  | 		default: | ||||||
|  | 			panic("freetype/raster: bad path") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	s.stroke(q[i:]) | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								vendor/github.com/golang/freetype/testdata/COPYING
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/golang/freetype/testdata/COPYING
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | Luxi fonts copyright (c) 2001 by Bigelow & Holmes Inc. Luxi font  | ||||||
|  | instruction code copyright (c) 2001 by URW++ GmbH. All Rights  | ||||||
|  | Reserved. Luxi is a registered trademark of Bigelow & Holmes Inc. | ||||||
|  |  | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining  | ||||||
|  | a copy of these Fonts and associated documentation files (the "Font  | ||||||
|  | Software"), to deal in the Font Software, including without  | ||||||
|  | limitation the rights to use, copy, merge, publish, distribute,  | ||||||
|  | sublicense, and/or sell copies of the Font Software, and to permit  | ||||||
|  | persons to whom the Font Software is furnished to do so, subject to  | ||||||
|  | the following conditions: | ||||||
|  |  | ||||||
|  | The above copyright and trademark notices and this permission notice  | ||||||
|  | shall be included in all copies of one or more of the Font Software. | ||||||
|  |  | ||||||
|  | The Font Software may not be modified, altered, or added to, and in  | ||||||
|  | particular the designs of glyphs or characters in the Fonts may not  | ||||||
|  | be modified nor may additional glyphs or characters be added to the  | ||||||
|  | Fonts. This License becomes null and void when the Fonts or Font  | ||||||
|  | Software have been modified. | ||||||
|  |  | ||||||
|  | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,  | ||||||
|  | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF  | ||||||
|  | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT  | ||||||
|  | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT.  IN NO EVENT SHALL  | ||||||
|  | BIGELOW & HOLMES INC. OR URW++ GMBH. BE LIABLE FOR ANY CLAIM, DAMAGES  | ||||||
|  | OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT,  | ||||||
|  | INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF  | ||||||
|  | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR  | ||||||
|  | INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT  | ||||||
|  | SOFTWARE. | ||||||
|  |  | ||||||
|  | Except as contained in this notice, the names of Bigelow & Holmes  | ||||||
|  | Inc. and URW++ GmbH. shall not be used in advertising or otherwise to  | ||||||
|  | promote the sale, use or other dealings in this Font Software without  | ||||||
|  | prior written authorization from Bigelow & Holmes Inc. and URW++ GmbH. | ||||||
|  |  | ||||||
|  | For further information, contact: | ||||||
|  |  | ||||||
|  | info@urwpp.de | ||||||
|  | or | ||||||
|  | design@bigelowandholmes.com | ||||||
							
								
								
									
										507
									
								
								vendor/github.com/golang/freetype/truetype/face.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										507
									
								
								vendor/github.com/golang/freetype/truetype/face.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,507 @@ | |||||||
|  | // Copyright 2015 The Freetype-Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by your choice of either the | ||||||
|  | // FreeType License or the GNU General Public License version 2 (or | ||||||
|  | // any later version), both of which can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package truetype | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"image" | ||||||
|  | 	"math" | ||||||
|  |  | ||||||
|  | 	"github.com/golang/freetype/raster" | ||||||
|  | 	"golang.org/x/image/font" | ||||||
|  | 	"golang.org/x/image/math/fixed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func powerOf2(i int) bool { | ||||||
|  | 	return i != 0 && (i&(i-1)) == 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Options are optional arguments to NewFace. | ||||||
|  | type Options struct { | ||||||
|  | 	// Size is the font size in points, as in "a 10 point font size". | ||||||
|  | 	// | ||||||
|  | 	// A zero value means to use a 12 point font size. | ||||||
|  | 	Size float64 | ||||||
|  |  | ||||||
|  | 	// DPI is the dots-per-inch resolution. | ||||||
|  | 	// | ||||||
|  | 	// A zero value means to use 72 DPI. | ||||||
|  | 	DPI float64 | ||||||
|  |  | ||||||
|  | 	// Hinting is how to quantize the glyph nodes. | ||||||
|  | 	// | ||||||
|  | 	// A zero value means to use no hinting. | ||||||
|  | 	Hinting font.Hinting | ||||||
|  |  | ||||||
|  | 	// GlyphCacheEntries is the number of entries in the glyph mask image | ||||||
|  | 	// cache. | ||||||
|  | 	// | ||||||
|  | 	// If non-zero, it must be a power of 2. | ||||||
|  | 	// | ||||||
|  | 	// A zero value means to use 512 entries. | ||||||
|  | 	GlyphCacheEntries int | ||||||
|  |  | ||||||
|  | 	// SubPixelsX is the number of sub-pixel locations a glyph's dot is | ||||||
|  | 	// quantized to, in the horizontal direction. For example, a value of 8 | ||||||
|  | 	// means that the dot is quantized to 1/8th of a pixel. This quantization | ||||||
|  | 	// only affects the glyph mask image, not its bounding box or advance | ||||||
|  | 	// width. A higher value gives a more faithful glyph image, but reduces the | ||||||
|  | 	// effectiveness of the glyph cache. | ||||||
|  | 	// | ||||||
|  | 	// If non-zero, it must be a power of 2, and be between 1 and 64 inclusive. | ||||||
|  | 	// | ||||||
|  | 	// A zero value means to use 4 sub-pixel locations. | ||||||
|  | 	SubPixelsX int | ||||||
|  |  | ||||||
|  | 	// SubPixelsY is the number of sub-pixel locations a glyph's dot is | ||||||
|  | 	// quantized to, in the vertical direction. For example, a value of 8 | ||||||
|  | 	// means that the dot is quantized to 1/8th of a pixel. This quantization | ||||||
|  | 	// only affects the glyph mask image, not its bounding box or advance | ||||||
|  | 	// width. A higher value gives a more faithful glyph image, but reduces the | ||||||
|  | 	// effectiveness of the glyph cache. | ||||||
|  | 	// | ||||||
|  | 	// If non-zero, it must be a power of 2, and be between 1 and 64 inclusive. | ||||||
|  | 	// | ||||||
|  | 	// A zero value means to use 1 sub-pixel location. | ||||||
|  | 	SubPixelsY int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (o *Options) size() float64 { | ||||||
|  | 	if o != nil && o.Size > 0 { | ||||||
|  | 		return o.Size | ||||||
|  | 	} | ||||||
|  | 	return 12 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (o *Options) dpi() float64 { | ||||||
|  | 	if o != nil && o.DPI > 0 { | ||||||
|  | 		return o.DPI | ||||||
|  | 	} | ||||||
|  | 	return 72 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (o *Options) hinting() font.Hinting { | ||||||
|  | 	if o != nil { | ||||||
|  | 		switch o.Hinting { | ||||||
|  | 		case font.HintingVertical, font.HintingFull: | ||||||
|  | 			// TODO: support vertical hinting. | ||||||
|  | 			return font.HintingFull | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return font.HintingNone | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (o *Options) glyphCacheEntries() int { | ||||||
|  | 	if o != nil && powerOf2(o.GlyphCacheEntries) { | ||||||
|  | 		return o.GlyphCacheEntries | ||||||
|  | 	} | ||||||
|  | 	// 512 is 128 * 4 * 1, which lets us cache 128 glyphs at 4 * 1 subpixel | ||||||
|  | 	// locations in the X and Y direction. | ||||||
|  | 	return 512 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (o *Options) subPixelsX() (value uint32, halfQuantum, mask fixed.Int26_6) { | ||||||
|  | 	if o != nil { | ||||||
|  | 		switch o.SubPixelsX { | ||||||
|  | 		case 1, 2, 4, 8, 16, 32, 64: | ||||||
|  | 			return subPixels(o.SubPixelsX) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// This default value of 4 isn't based on anything scientific, merely as | ||||||
|  | 	// small a number as possible that looks almost as good as no quantization, | ||||||
|  | 	// or returning subPixels(64). | ||||||
|  | 	return subPixels(4) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (o *Options) subPixelsY() (value uint32, halfQuantum, mask fixed.Int26_6) { | ||||||
|  | 	if o != nil { | ||||||
|  | 		switch o.SubPixelsX { | ||||||
|  | 		case 1, 2, 4, 8, 16, 32, 64: | ||||||
|  | 			return subPixels(o.SubPixelsX) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// This default value of 1 isn't based on anything scientific, merely that | ||||||
|  | 	// vertical sub-pixel glyph rendering is pretty rare. Baseline locations | ||||||
|  | 	// can usually afford to snap to the pixel grid, so the vertical direction | ||||||
|  | 	// doesn't have the deal with the horizontal's fractional advance widths. | ||||||
|  | 	return subPixels(1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // subPixels returns q and the bias and mask that leads to q quantized | ||||||
|  | // sub-pixel locations per full pixel. | ||||||
|  | // | ||||||
|  | // For example, q == 4 leads to a bias of 8 and a mask of 0xfffffff0, or -16, | ||||||
|  | // because we want to round fractions of fixed.Int26_6 as: | ||||||
|  | //	-  0 to  7 rounds to 0. | ||||||
|  | //	-  8 to 23 rounds to 16. | ||||||
|  | //	- 24 to 39 rounds to 32. | ||||||
|  | //	- 40 to 55 rounds to 48. | ||||||
|  | //	- 56 to 63 rounds to 64. | ||||||
|  | // which means to add 8 and then bitwise-and with -16, in two's complement | ||||||
|  | // representation. | ||||||
|  | // | ||||||
|  | // When q ==  1, we want bias == 32 and mask == -64. | ||||||
|  | // When q ==  2, we want bias == 16 and mask == -32. | ||||||
|  | // When q ==  4, we want bias ==  8 and mask == -16. | ||||||
|  | // ... | ||||||
|  | // When q == 64, we want bias ==  0 and mask ==  -1. (The no-op case). | ||||||
|  | // The pattern is clear. | ||||||
|  | func subPixels(q int) (value uint32, bias, mask fixed.Int26_6) { | ||||||
|  | 	return uint32(q), 32 / fixed.Int26_6(q), -64 / fixed.Int26_6(q) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // glyphCacheEntry caches the arguments and return values of rasterize. | ||||||
|  | type glyphCacheEntry struct { | ||||||
|  | 	key glyphCacheKey | ||||||
|  | 	val glyphCacheVal | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type glyphCacheKey struct { | ||||||
|  | 	index  Index | ||||||
|  | 	fx, fy uint8 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type glyphCacheVal struct { | ||||||
|  | 	advanceWidth fixed.Int26_6 | ||||||
|  | 	offset       image.Point | ||||||
|  | 	gw           int | ||||||
|  | 	gh           int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type indexCacheEntry struct { | ||||||
|  | 	rune  rune | ||||||
|  | 	index Index | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewFace returns a new font.Face for the given Font. | ||||||
|  | func NewFace(f *Font, opts *Options) font.Face { | ||||||
|  | 	a := &face{ | ||||||
|  | 		f:          f, | ||||||
|  | 		hinting:    opts.hinting(), | ||||||
|  | 		scale:      fixed.Int26_6(0.5 + (opts.size() * opts.dpi() * 64 / 72)), | ||||||
|  | 		glyphCache: make([]glyphCacheEntry, opts.glyphCacheEntries()), | ||||||
|  | 	} | ||||||
|  | 	a.subPixelX, a.subPixelBiasX, a.subPixelMaskX = opts.subPixelsX() | ||||||
|  | 	a.subPixelY, a.subPixelBiasY, a.subPixelMaskY = opts.subPixelsY() | ||||||
|  |  | ||||||
|  | 	// Fill the cache with invalid entries. Valid glyph cache entries have fx | ||||||
|  | 	// and fy in the range [0, 64). Valid index cache entries have rune >= 0. | ||||||
|  | 	for i := range a.glyphCache { | ||||||
|  | 		a.glyphCache[i].key.fy = 0xff | ||||||
|  | 	} | ||||||
|  | 	for i := range a.indexCache { | ||||||
|  | 		a.indexCache[i].rune = -1 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Set the rasterizer's bounds to be big enough to handle the largest glyph. | ||||||
|  | 	b := f.Bounds(a.scale) | ||||||
|  | 	xmin := +int(b.Min.X) >> 6 | ||||||
|  | 	ymin := -int(b.Max.Y) >> 6 | ||||||
|  | 	xmax := +int(b.Max.X+63) >> 6 | ||||||
|  | 	ymax := -int(b.Min.Y-63) >> 6 | ||||||
|  | 	a.maxw = xmax - xmin | ||||||
|  | 	a.maxh = ymax - ymin | ||||||
|  | 	a.masks = image.NewAlpha(image.Rect(0, 0, a.maxw, a.maxh*len(a.glyphCache))) | ||||||
|  | 	a.r.SetBounds(a.maxw, a.maxh) | ||||||
|  | 	a.p = facePainter{a} | ||||||
|  |  | ||||||
|  | 	return a | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type face struct { | ||||||
|  | 	f             *Font | ||||||
|  | 	hinting       font.Hinting | ||||||
|  | 	scale         fixed.Int26_6 | ||||||
|  | 	subPixelX     uint32 | ||||||
|  | 	subPixelBiasX fixed.Int26_6 | ||||||
|  | 	subPixelMaskX fixed.Int26_6 | ||||||
|  | 	subPixelY     uint32 | ||||||
|  | 	subPixelBiasY fixed.Int26_6 | ||||||
|  | 	subPixelMaskY fixed.Int26_6 | ||||||
|  | 	masks         *image.Alpha | ||||||
|  | 	glyphCache    []glyphCacheEntry | ||||||
|  | 	r             raster.Rasterizer | ||||||
|  | 	p             raster.Painter | ||||||
|  | 	paintOffset   int | ||||||
|  | 	maxw          int | ||||||
|  | 	maxh          int | ||||||
|  | 	glyphBuf      GlyphBuf | ||||||
|  | 	indexCache    [indexCacheLen]indexCacheEntry | ||||||
|  |  | ||||||
|  | 	// TODO: clip rectangle? | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const indexCacheLen = 256 | ||||||
|  |  | ||||||
|  | func (a *face) index(r rune) Index { | ||||||
|  | 	const mask = indexCacheLen - 1 | ||||||
|  | 	c := &a.indexCache[r&mask] | ||||||
|  | 	if c.rune == r { | ||||||
|  | 		return c.index | ||||||
|  | 	} | ||||||
|  | 	i := a.f.Index(r) | ||||||
|  | 	c.rune = r | ||||||
|  | 	c.index = i | ||||||
|  | 	return i | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Close satisfies the font.Face interface. | ||||||
|  | func (a *face) Close() error { return nil } | ||||||
|  |  | ||||||
|  | // Metrics satisfies the font.Face interface. | ||||||
|  | func (a *face) Metrics() font.Metrics { | ||||||
|  | 	scale := float64(a.scale) | ||||||
|  | 	fupe := float64(a.f.FUnitsPerEm()) | ||||||
|  | 	return font.Metrics{ | ||||||
|  | 		Height:  a.scale, | ||||||
|  | 		Ascent:  fixed.Int26_6(math.Ceil(scale * float64(+a.f.ascent) / fupe)), | ||||||
|  | 		Descent: fixed.Int26_6(math.Ceil(scale * float64(-a.f.descent) / fupe)), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Kern satisfies the font.Face interface. | ||||||
|  | func (a *face) Kern(r0, r1 rune) fixed.Int26_6 { | ||||||
|  | 	i0 := a.index(r0) | ||||||
|  | 	i1 := a.index(r1) | ||||||
|  | 	kern := a.f.Kern(a.scale, i0, i1) | ||||||
|  | 	if a.hinting != font.HintingNone { | ||||||
|  | 		kern = (kern + 32) &^ 63 | ||||||
|  | 	} | ||||||
|  | 	return kern | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Glyph satisfies the font.Face interface. | ||||||
|  | func (a *face) Glyph(dot fixed.Point26_6, r rune) ( | ||||||
|  | 	dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) { | ||||||
|  |  | ||||||
|  | 	// Quantize to the sub-pixel granularity. | ||||||
|  | 	dotX := (dot.X + a.subPixelBiasX) & a.subPixelMaskX | ||||||
|  | 	dotY := (dot.Y + a.subPixelBiasY) & a.subPixelMaskY | ||||||
|  |  | ||||||
|  | 	// Split the coordinates into their integer and fractional parts. | ||||||
|  | 	ix, fx := int(dotX>>6), dotX&0x3f | ||||||
|  | 	iy, fy := int(dotY>>6), dotY&0x3f | ||||||
|  |  | ||||||
|  | 	index := a.index(r) | ||||||
|  | 	cIndex := uint32(index) | ||||||
|  | 	cIndex = cIndex*a.subPixelX - uint32(fx/a.subPixelMaskX) | ||||||
|  | 	cIndex = cIndex*a.subPixelY - uint32(fy/a.subPixelMaskY) | ||||||
|  | 	cIndex &= uint32(len(a.glyphCache) - 1) | ||||||
|  | 	a.paintOffset = a.maxh * int(cIndex) | ||||||
|  | 	k := glyphCacheKey{ | ||||||
|  | 		index: index, | ||||||
|  | 		fx:    uint8(fx), | ||||||
|  | 		fy:    uint8(fy), | ||||||
|  | 	} | ||||||
|  | 	var v glyphCacheVal | ||||||
|  | 	if a.glyphCache[cIndex].key != k { | ||||||
|  | 		var ok bool | ||||||
|  | 		v, ok = a.rasterize(index, fx, fy) | ||||||
|  | 		if !ok { | ||||||
|  | 			return image.Rectangle{}, nil, image.Point{}, 0, false | ||||||
|  | 		} | ||||||
|  | 		a.glyphCache[cIndex] = glyphCacheEntry{k, v} | ||||||
|  | 	} else { | ||||||
|  | 		v = a.glyphCache[cIndex].val | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	dr.Min = image.Point{ | ||||||
|  | 		X: ix + v.offset.X, | ||||||
|  | 		Y: iy + v.offset.Y, | ||||||
|  | 	} | ||||||
|  | 	dr.Max = image.Point{ | ||||||
|  | 		X: dr.Min.X + v.gw, | ||||||
|  | 		Y: dr.Min.Y + v.gh, | ||||||
|  | 	} | ||||||
|  | 	return dr, a.masks, image.Point{Y: a.paintOffset}, v.advanceWidth, true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { | ||||||
|  | 	if err := a.glyphBuf.Load(a.f, a.scale, a.index(r), a.hinting); err != nil { | ||||||
|  | 		return fixed.Rectangle26_6{}, 0, false | ||||||
|  | 	} | ||||||
|  | 	xmin := +a.glyphBuf.Bounds.Min.X | ||||||
|  | 	ymin := -a.glyphBuf.Bounds.Max.Y | ||||||
|  | 	xmax := +a.glyphBuf.Bounds.Max.X | ||||||
|  | 	ymax := -a.glyphBuf.Bounds.Min.Y | ||||||
|  | 	if xmin > xmax || ymin > ymax { | ||||||
|  | 		return fixed.Rectangle26_6{}, 0, false | ||||||
|  | 	} | ||||||
|  | 	return fixed.Rectangle26_6{ | ||||||
|  | 		Min: fixed.Point26_6{ | ||||||
|  | 			X: xmin, | ||||||
|  | 			Y: ymin, | ||||||
|  | 		}, | ||||||
|  | 		Max: fixed.Point26_6{ | ||||||
|  | 			X: xmax, | ||||||
|  | 			Y: ymax, | ||||||
|  | 		}, | ||||||
|  | 	}, a.glyphBuf.AdvanceWidth, true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { | ||||||
|  | 	if err := a.glyphBuf.Load(a.f, a.scale, a.index(r), a.hinting); err != nil { | ||||||
|  | 		return 0, false | ||||||
|  | 	} | ||||||
|  | 	return a.glyphBuf.AdvanceWidth, true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // rasterize returns the advance width, integer-pixel offset to render at, and | ||||||
|  | // the width and height of the given glyph at the given sub-pixel offsets. | ||||||
|  | // | ||||||
|  | // The 26.6 fixed point arguments fx and fy must be in the range [0, 1). | ||||||
|  | func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (v glyphCacheVal, ok bool) { | ||||||
|  | 	if err := a.glyphBuf.Load(a.f, a.scale, index, a.hinting); err != nil { | ||||||
|  | 		return glyphCacheVal{}, false | ||||||
|  | 	} | ||||||
|  | 	// Calculate the integer-pixel bounds for the glyph. | ||||||
|  | 	xmin := int(fx+a.glyphBuf.Bounds.Min.X) >> 6 | ||||||
|  | 	ymin := int(fy-a.glyphBuf.Bounds.Max.Y) >> 6 | ||||||
|  | 	xmax := int(fx+a.glyphBuf.Bounds.Max.X+0x3f) >> 6 | ||||||
|  | 	ymax := int(fy-a.glyphBuf.Bounds.Min.Y+0x3f) >> 6 | ||||||
|  | 	if xmin > xmax || ymin > ymax { | ||||||
|  | 		return glyphCacheVal{}, false | ||||||
|  | 	} | ||||||
|  | 	// A TrueType's glyph's nodes can have negative co-ordinates, but the | ||||||
|  | 	// rasterizer clips anything left of x=0 or above y=0. xmin and ymin are | ||||||
|  | 	// the pixel offsets, based on the font's FUnit metrics, that let a | ||||||
|  | 	// negative co-ordinate in TrueType space be non-negative in rasterizer | ||||||
|  | 	// space. xmin and ymin are typically <= 0. | ||||||
|  | 	fx -= fixed.Int26_6(xmin << 6) | ||||||
|  | 	fy -= fixed.Int26_6(ymin << 6) | ||||||
|  | 	// Rasterize the glyph's vectors. | ||||||
|  | 	a.r.Clear() | ||||||
|  | 	pixOffset := a.paintOffset * a.maxw | ||||||
|  | 	clear(a.masks.Pix[pixOffset : pixOffset+a.maxw*a.maxh]) | ||||||
|  | 	e0 := 0 | ||||||
|  | 	for _, e1 := range a.glyphBuf.Ends { | ||||||
|  | 		a.drawContour(a.glyphBuf.Points[e0:e1], fx, fy) | ||||||
|  | 		e0 = e1 | ||||||
|  | 	} | ||||||
|  | 	a.r.Rasterize(a.p) | ||||||
|  | 	return glyphCacheVal{ | ||||||
|  | 		a.glyphBuf.AdvanceWidth, | ||||||
|  | 		image.Point{xmin, ymin}, | ||||||
|  | 		xmax - xmin, | ||||||
|  | 		ymax - ymin, | ||||||
|  | 	}, true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func clear(pix []byte) { | ||||||
|  | 	for i := range pix { | ||||||
|  | 		pix[i] = 0 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // drawContour draws the given closed contour with the given offset. | ||||||
|  | func (a *face) drawContour(ps []Point, dx, dy fixed.Int26_6) { | ||||||
|  | 	if len(ps) == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// The low bit of each point's Flags value is whether the point is on the | ||||||
|  | 	// curve. Truetype fonts only have quadratic Bézier curves, not cubics. | ||||||
|  | 	// Thus, two consecutive off-curve points imply an on-curve point in the | ||||||
|  | 	// middle of those two. | ||||||
|  | 	// | ||||||
|  | 	// See http://chanae.walon.org/pub/ttf/ttf_glyphs.htm for more details. | ||||||
|  |  | ||||||
|  | 	// ps[0] is a truetype.Point measured in FUnits and positive Y going | ||||||
|  | 	// upwards. start is the same thing measured in fixed point units and | ||||||
|  | 	// positive Y going downwards, and offset by (dx, dy). | ||||||
|  | 	start := fixed.Point26_6{ | ||||||
|  | 		X: dx + ps[0].X, | ||||||
|  | 		Y: dy - ps[0].Y, | ||||||
|  | 	} | ||||||
|  | 	var others []Point | ||||||
|  | 	if ps[0].Flags&0x01 != 0 { | ||||||
|  | 		others = ps[1:] | ||||||
|  | 	} else { | ||||||
|  | 		last := fixed.Point26_6{ | ||||||
|  | 			X: dx + ps[len(ps)-1].X, | ||||||
|  | 			Y: dy - ps[len(ps)-1].Y, | ||||||
|  | 		} | ||||||
|  | 		if ps[len(ps)-1].Flags&0x01 != 0 { | ||||||
|  | 			start = last | ||||||
|  | 			others = ps[:len(ps)-1] | ||||||
|  | 		} else { | ||||||
|  | 			start = fixed.Point26_6{ | ||||||
|  | 				X: (start.X + last.X) / 2, | ||||||
|  | 				Y: (start.Y + last.Y) / 2, | ||||||
|  | 			} | ||||||
|  | 			others = ps | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	a.r.Start(start) | ||||||
|  | 	q0, on0 := start, true | ||||||
|  | 	for _, p := range others { | ||||||
|  | 		q := fixed.Point26_6{ | ||||||
|  | 			X: dx + p.X, | ||||||
|  | 			Y: dy - p.Y, | ||||||
|  | 		} | ||||||
|  | 		on := p.Flags&0x01 != 0 | ||||||
|  | 		if on { | ||||||
|  | 			if on0 { | ||||||
|  | 				a.r.Add1(q) | ||||||
|  | 			} else { | ||||||
|  | 				a.r.Add2(q0, q) | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			if on0 { | ||||||
|  | 				// No-op. | ||||||
|  | 			} else { | ||||||
|  | 				mid := fixed.Point26_6{ | ||||||
|  | 					X: (q0.X + q.X) / 2, | ||||||
|  | 					Y: (q0.Y + q.Y) / 2, | ||||||
|  | 				} | ||||||
|  | 				a.r.Add2(q0, mid) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		q0, on0 = q, on | ||||||
|  | 	} | ||||||
|  | 	// Close the curve. | ||||||
|  | 	if on0 { | ||||||
|  | 		a.r.Add1(start) | ||||||
|  | 	} else { | ||||||
|  | 		a.r.Add2(q0, start) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // facePainter is like a raster.AlphaSrcPainter, with an additional Y offset | ||||||
|  | // (face.paintOffset) to the painted spans. | ||||||
|  | type facePainter struct { | ||||||
|  | 	a *face | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p facePainter) Paint(ss []raster.Span, done bool) { | ||||||
|  | 	m := p.a.masks | ||||||
|  | 	b := m.Bounds() | ||||||
|  | 	b.Min.Y = p.a.paintOffset | ||||||
|  | 	b.Max.Y = p.a.paintOffset + p.a.maxh | ||||||
|  | 	for _, s := range ss { | ||||||
|  | 		s.Y += p.a.paintOffset | ||||||
|  | 		if s.Y < b.Min.Y { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if s.Y >= b.Max.Y { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if s.X0 < b.Min.X { | ||||||
|  | 			s.X0 = b.Min.X | ||||||
|  | 		} | ||||||
|  | 		if s.X1 > b.Max.X { | ||||||
|  | 			s.X1 = b.Max.X | ||||||
|  | 		} | ||||||
|  | 		if s.X0 >= s.X1 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		base := (s.Y-m.Rect.Min.Y)*m.Stride - m.Rect.Min.X | ||||||
|  | 		p := m.Pix[base+s.X0 : base+s.X1] | ||||||
|  | 		color := uint8(s.Alpha >> 8) | ||||||
|  | 		for i := range p { | ||||||
|  | 			p[i] = color | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										522
									
								
								vendor/github.com/golang/freetype/truetype/glyph.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										522
									
								
								vendor/github.com/golang/freetype/truetype/glyph.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,522 @@ | |||||||
|  | // Copyright 2010 The Freetype-Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by your choice of either the | ||||||
|  | // FreeType License or the GNU General Public License version 2 (or | ||||||
|  | // any later version), both of which can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package truetype | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"golang.org/x/image/font" | ||||||
|  | 	"golang.org/x/image/math/fixed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // TODO: implement VerticalHinting. | ||||||
|  |  | ||||||
|  | // A Point is a co-ordinate pair plus whether it is 'on' a contour or an 'off' | ||||||
|  | // control point. | ||||||
|  | type Point struct { | ||||||
|  | 	X, Y fixed.Int26_6 | ||||||
|  | 	// The Flags' LSB means whether or not this Point is 'on' the contour. | ||||||
|  | 	// Other bits are reserved for internal use. | ||||||
|  | 	Flags uint32 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // A GlyphBuf holds a glyph's contours. A GlyphBuf can be re-used to load a | ||||||
|  | // series of glyphs from a Font. | ||||||
|  | type GlyphBuf struct { | ||||||
|  | 	// AdvanceWidth is the glyph's advance width. | ||||||
|  | 	AdvanceWidth fixed.Int26_6 | ||||||
|  | 	// Bounds is the glyph's bounding box. | ||||||
|  | 	Bounds fixed.Rectangle26_6 | ||||||
|  | 	// Points contains all Points from all contours of the glyph. If hinting | ||||||
|  | 	// was used to load a glyph then Unhinted contains those Points before they | ||||||
|  | 	// were hinted, and InFontUnits contains those Points before they were | ||||||
|  | 	// hinted and scaled. | ||||||
|  | 	Points, Unhinted, InFontUnits []Point | ||||||
|  | 	// Ends is the point indexes of the end point of each contour. The length | ||||||
|  | 	// of Ends is the number of contours in the glyph. The i'th contour | ||||||
|  | 	// consists of points Points[Ends[i-1]:Ends[i]], where Ends[-1] is | ||||||
|  | 	// interpreted to mean zero. | ||||||
|  | 	Ends []int | ||||||
|  |  | ||||||
|  | 	font    *Font | ||||||
|  | 	scale   fixed.Int26_6 | ||||||
|  | 	hinting font.Hinting | ||||||
|  | 	hinter  hinter | ||||||
|  | 	// phantomPoints are the co-ordinates of the synthetic phantom points | ||||||
|  | 	// used for hinting and bounding box calculations. | ||||||
|  | 	phantomPoints [4]Point | ||||||
|  | 	// pp1x is the X co-ordinate of the first phantom point. The '1' is | ||||||
|  | 	// using 1-based indexing; pp1x is almost always phantomPoints[0].X. | ||||||
|  | 	// TODO: eliminate this and consistently use phantomPoints[0].X. | ||||||
|  | 	pp1x fixed.Int26_6 | ||||||
|  | 	// metricsSet is whether the glyph's metrics have been set yet. For a | ||||||
|  | 	// compound glyph, a sub-glyph may override the outer glyph's metrics. | ||||||
|  | 	metricsSet bool | ||||||
|  | 	// tmp is a scratch buffer. | ||||||
|  | 	tmp []Point | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Flags for decoding a glyph's contours. These flags are documented at | ||||||
|  | // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html. | ||||||
|  | const ( | ||||||
|  | 	flagOnCurve = 1 << iota | ||||||
|  | 	flagXShortVector | ||||||
|  | 	flagYShortVector | ||||||
|  | 	flagRepeat | ||||||
|  | 	flagPositiveXShortVector | ||||||
|  | 	flagPositiveYShortVector | ||||||
|  |  | ||||||
|  | 	// The remaining flags are for internal use. | ||||||
|  | 	flagTouchedX | ||||||
|  | 	flagTouchedY | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // The same flag bits (0x10 and 0x20) are overloaded to have two meanings, | ||||||
|  | // dependent on the value of the flag{X,Y}ShortVector bits. | ||||||
|  | const ( | ||||||
|  | 	flagThisXIsSame = flagPositiveXShortVector | ||||||
|  | 	flagThisYIsSame = flagPositiveYShortVector | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Load loads a glyph's contours from a Font, overwriting any previously loaded | ||||||
|  | // contours for this GlyphBuf. scale is the number of 26.6 fixed point units in | ||||||
|  | // 1 em, i is the glyph index, and h is the hinting policy. | ||||||
|  | func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h font.Hinting) error { | ||||||
|  | 	g.Points = g.Points[:0] | ||||||
|  | 	g.Unhinted = g.Unhinted[:0] | ||||||
|  | 	g.InFontUnits = g.InFontUnits[:0] | ||||||
|  | 	g.Ends = g.Ends[:0] | ||||||
|  | 	g.font = f | ||||||
|  | 	g.hinting = h | ||||||
|  | 	g.scale = scale | ||||||
|  | 	g.pp1x = 0 | ||||||
|  | 	g.phantomPoints = [4]Point{} | ||||||
|  | 	g.metricsSet = false | ||||||
|  |  | ||||||
|  | 	if h != font.HintingNone { | ||||||
|  | 		if err := g.hinter.init(f, scale); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if err := g.load(0, i, true); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	// TODO: this selection of either g.pp1x or g.phantomPoints[0].X isn't ideal, | ||||||
|  | 	// and should be cleaned up once we have all the testScaling tests passing, | ||||||
|  | 	// plus additional tests for Freetype-Go's bounding boxes matching C Freetype's. | ||||||
|  | 	pp1x := g.pp1x | ||||||
|  | 	if h != font.HintingNone { | ||||||
|  | 		pp1x = g.phantomPoints[0].X | ||||||
|  | 	} | ||||||
|  | 	if pp1x != 0 { | ||||||
|  | 		for i := range g.Points { | ||||||
|  | 			g.Points[i].X -= pp1x | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X | ||||||
|  | 	if h != font.HintingNone { | ||||||
|  | 		if len(f.hdmx) >= 8 { | ||||||
|  | 			if n := u32(f.hdmx, 4); n > 3+uint32(i) { | ||||||
|  | 				for hdmx := f.hdmx[8:]; uint32(len(hdmx)) >= n; hdmx = hdmx[n:] { | ||||||
|  | 					if fixed.Int26_6(hdmx[0]) == scale>>6 { | ||||||
|  | 						advanceWidth = fixed.Int26_6(hdmx[2+i]) << 6 | ||||||
|  | 						break | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		advanceWidth = (advanceWidth + 32) &^ 63 | ||||||
|  | 	} | ||||||
|  | 	g.AdvanceWidth = advanceWidth | ||||||
|  |  | ||||||
|  | 	// Set g.Bounds to the 'control box', which is the bounding box of the | ||||||
|  | 	// Bézier curves' control points. This is easier to calculate, no smaller | ||||||
|  | 	// than and often equal to the tightest possible bounding box of the curves | ||||||
|  | 	// themselves. This approach is what C Freetype does. We can't just scale | ||||||
|  | 	// the nominal bounding box in the glyf data as the hinting process and | ||||||
|  | 	// phantom point adjustment may move points outside of that box. | ||||||
|  | 	if len(g.Points) == 0 { | ||||||
|  | 		g.Bounds = fixed.Rectangle26_6{} | ||||||
|  | 	} else { | ||||||
|  | 		p := g.Points[0] | ||||||
|  | 		g.Bounds.Min.X = p.X | ||||||
|  | 		g.Bounds.Max.X = p.X | ||||||
|  | 		g.Bounds.Min.Y = p.Y | ||||||
|  | 		g.Bounds.Max.Y = p.Y | ||||||
|  | 		for _, p := range g.Points[1:] { | ||||||
|  | 			if g.Bounds.Min.X > p.X { | ||||||
|  | 				g.Bounds.Min.X = p.X | ||||||
|  | 			} else if g.Bounds.Max.X < p.X { | ||||||
|  | 				g.Bounds.Max.X = p.X | ||||||
|  | 			} | ||||||
|  | 			if g.Bounds.Min.Y > p.Y { | ||||||
|  | 				g.Bounds.Min.Y = p.Y | ||||||
|  | 			} else if g.Bounds.Max.Y < p.Y { | ||||||
|  | 				g.Bounds.Max.Y = p.Y | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		// Snap the box to the grid, if hinting is on. | ||||||
|  | 		if h != font.HintingNone { | ||||||
|  | 			g.Bounds.Min.X &^= 63 | ||||||
|  | 			g.Bounds.Min.Y &^= 63 | ||||||
|  | 			g.Bounds.Max.X += 63 | ||||||
|  | 			g.Bounds.Max.X &^= 63 | ||||||
|  | 			g.Bounds.Max.Y += 63 | ||||||
|  | 			g.Bounds.Max.Y &^= 63 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *GlyphBuf) load(recursion uint32, i Index, useMyMetrics bool) (err error) { | ||||||
|  | 	// The recursion limit here is arbitrary, but defends against malformed glyphs. | ||||||
|  | 	if recursion >= 32 { | ||||||
|  | 		return UnsupportedError("excessive compound glyph recursion") | ||||||
|  | 	} | ||||||
|  | 	// Find the relevant slice of g.font.glyf. | ||||||
|  | 	var g0, g1 uint32 | ||||||
|  | 	if g.font.locaOffsetFormat == locaOffsetFormatShort { | ||||||
|  | 		g0 = 2 * uint32(u16(g.font.loca, 2*int(i))) | ||||||
|  | 		g1 = 2 * uint32(u16(g.font.loca, 2*int(i)+2)) | ||||||
|  | 	} else { | ||||||
|  | 		g0 = u32(g.font.loca, 4*int(i)) | ||||||
|  | 		g1 = u32(g.font.loca, 4*int(i)+4) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Decode the contour count and nominal bounding box, from the first | ||||||
|  | 	// 10 bytes of the glyf data. boundsYMin and boundsXMax, at offsets 4 | ||||||
|  | 	// and 6, are unused. | ||||||
|  | 	glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, fixed.Int26_6(0), fixed.Int26_6(0) | ||||||
|  | 	if g0+10 <= g1 { | ||||||
|  | 		glyf = g.font.glyf[g0:g1] | ||||||
|  | 		ne = int(int16(u16(glyf, 0))) | ||||||
|  | 		boundsXMin = fixed.Int26_6(int16(u16(glyf, 2))) | ||||||
|  | 		boundsYMax = fixed.Int26_6(int16(u16(glyf, 8))) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Create the phantom points. | ||||||
|  | 	uhm, pp1x := g.font.unscaledHMetric(i), fixed.Int26_6(0) | ||||||
|  | 	uvm := g.font.unscaledVMetric(i, boundsYMax) | ||||||
|  | 	g.phantomPoints = [4]Point{ | ||||||
|  | 		{X: boundsXMin - uhm.LeftSideBearing}, | ||||||
|  | 		{X: boundsXMin - uhm.LeftSideBearing + uhm.AdvanceWidth}, | ||||||
|  | 		{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing}, | ||||||
|  | 		{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing - uvm.AdvanceHeight}, | ||||||
|  | 	} | ||||||
|  | 	if len(glyf) == 0 { | ||||||
|  | 		g.addPhantomsAndScale(len(g.Points), len(g.Points), true, true) | ||||||
|  | 		copy(g.phantomPoints[:], g.Points[len(g.Points)-4:]) | ||||||
|  | 		g.Points = g.Points[:len(g.Points)-4] | ||||||
|  | 		// TODO: also trim g.InFontUnits and g.Unhinted? | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Load and hint the contours. | ||||||
|  | 	if ne < 0 { | ||||||
|  | 		if ne != -1 { | ||||||
|  | 			// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that | ||||||
|  | 			// "the values -2, -3, and so forth, are reserved for future use." | ||||||
|  | 			return UnsupportedError("negative number of contours") | ||||||
|  | 		} | ||||||
|  | 		pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing)) | ||||||
|  | 		if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		np0, ne0 := len(g.Points), len(g.Ends) | ||||||
|  | 		program := g.loadSimple(glyf, ne) | ||||||
|  | 		g.addPhantomsAndScale(np0, np0, true, true) | ||||||
|  | 		pp1x = g.Points[len(g.Points)-4].X | ||||||
|  | 		if g.hinting != font.HintingNone { | ||||||
|  | 			if len(program) != 0 { | ||||||
|  | 				err := g.hinter.run( | ||||||
|  | 					program, | ||||||
|  | 					g.Points[np0:], | ||||||
|  | 					g.Unhinted[np0:], | ||||||
|  | 					g.InFontUnits[np0:], | ||||||
|  | 					g.Ends[ne0:], | ||||||
|  | 				) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return err | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			// Drop the four phantom points. | ||||||
|  | 			g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4] | ||||||
|  | 			g.Unhinted = g.Unhinted[:len(g.Unhinted)-4] | ||||||
|  | 		} | ||||||
|  | 		if useMyMetrics { | ||||||
|  | 			copy(g.phantomPoints[:], g.Points[len(g.Points)-4:]) | ||||||
|  | 		} | ||||||
|  | 		g.Points = g.Points[:len(g.Points)-4] | ||||||
|  | 		if np0 != 0 { | ||||||
|  | 			// The hinting program expects the []Ends values to be indexed | ||||||
|  | 			// relative to the inner glyph, not the outer glyph, so we delay | ||||||
|  | 			// adding np0 until after the hinting program (if any) has run. | ||||||
|  | 			for i := ne0; i < len(g.Ends); i++ { | ||||||
|  | 				g.Ends[i] += np0 | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if useMyMetrics && !g.metricsSet { | ||||||
|  | 		g.metricsSet = true | ||||||
|  | 		g.pp1x = pp1x | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // loadOffset is the initial offset for loadSimple and loadCompound. The first | ||||||
|  | // 10 bytes are the number of contours and the bounding box. | ||||||
|  | const loadOffset = 10 | ||||||
|  |  | ||||||
|  | func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) { | ||||||
|  | 	offset := loadOffset | ||||||
|  | 	for i := 0; i < ne; i++ { | ||||||
|  | 		g.Ends = append(g.Ends, 1+int(u16(glyf, offset))) | ||||||
|  | 		offset += 2 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Note the TrueType hinting instructions. | ||||||
|  | 	instrLen := int(u16(glyf, offset)) | ||||||
|  | 	offset += 2 | ||||||
|  | 	program = glyf[offset : offset+instrLen] | ||||||
|  | 	offset += instrLen | ||||||
|  |  | ||||||
|  | 	if ne == 0 { | ||||||
|  | 		return program | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	np0 := len(g.Points) | ||||||
|  | 	np1 := np0 + int(g.Ends[len(g.Ends)-1]) | ||||||
|  |  | ||||||
|  | 	// Decode the flags. | ||||||
|  | 	for i := np0; i < np1; { | ||||||
|  | 		c := uint32(glyf[offset]) | ||||||
|  | 		offset++ | ||||||
|  | 		g.Points = append(g.Points, Point{Flags: c}) | ||||||
|  | 		i++ | ||||||
|  | 		if c&flagRepeat != 0 { | ||||||
|  | 			count := glyf[offset] | ||||||
|  | 			offset++ | ||||||
|  | 			for ; count > 0; count-- { | ||||||
|  | 				g.Points = append(g.Points, Point{Flags: c}) | ||||||
|  | 				i++ | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Decode the co-ordinates. | ||||||
|  | 	var x int16 | ||||||
|  | 	for i := np0; i < np1; i++ { | ||||||
|  | 		f := g.Points[i].Flags | ||||||
|  | 		if f&flagXShortVector != 0 { | ||||||
|  | 			dx := int16(glyf[offset]) | ||||||
|  | 			offset++ | ||||||
|  | 			if f&flagPositiveXShortVector == 0 { | ||||||
|  | 				x -= dx | ||||||
|  | 			} else { | ||||||
|  | 				x += dx | ||||||
|  | 			} | ||||||
|  | 		} else if f&flagThisXIsSame == 0 { | ||||||
|  | 			x += int16(u16(glyf, offset)) | ||||||
|  | 			offset += 2 | ||||||
|  | 		} | ||||||
|  | 		g.Points[i].X = fixed.Int26_6(x) | ||||||
|  | 	} | ||||||
|  | 	var y int16 | ||||||
|  | 	for i := np0; i < np1; i++ { | ||||||
|  | 		f := g.Points[i].Flags | ||||||
|  | 		if f&flagYShortVector != 0 { | ||||||
|  | 			dy := int16(glyf[offset]) | ||||||
|  | 			offset++ | ||||||
|  | 			if f&flagPositiveYShortVector == 0 { | ||||||
|  | 				y -= dy | ||||||
|  | 			} else { | ||||||
|  | 				y += dy | ||||||
|  | 			} | ||||||
|  | 		} else if f&flagThisYIsSame == 0 { | ||||||
|  | 			y += int16(u16(glyf, offset)) | ||||||
|  | 			offset += 2 | ||||||
|  | 		} | ||||||
|  | 		g.Points[i].Y = fixed.Int26_6(y) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return program | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *GlyphBuf) loadCompound(recursion uint32, uhm HMetric, i Index, | ||||||
|  | 	glyf []byte, useMyMetrics bool) error { | ||||||
|  |  | ||||||
|  | 	// Flags for decoding a compound glyph. These flags are documented at | ||||||
|  | 	// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html. | ||||||
|  | 	const ( | ||||||
|  | 		flagArg1And2AreWords = 1 << iota | ||||||
|  | 		flagArgsAreXYValues | ||||||
|  | 		flagRoundXYToGrid | ||||||
|  | 		flagWeHaveAScale | ||||||
|  | 		flagUnused | ||||||
|  | 		flagMoreComponents | ||||||
|  | 		flagWeHaveAnXAndYScale | ||||||
|  | 		flagWeHaveATwoByTwo | ||||||
|  | 		flagWeHaveInstructions | ||||||
|  | 		flagUseMyMetrics | ||||||
|  | 		flagOverlapCompound | ||||||
|  | 	) | ||||||
|  | 	np0, ne0 := len(g.Points), len(g.Ends) | ||||||
|  | 	offset := loadOffset | ||||||
|  | 	for { | ||||||
|  | 		flags := u16(glyf, offset) | ||||||
|  | 		component := Index(u16(glyf, offset+2)) | ||||||
|  | 		dx, dy, transform, hasTransform := fixed.Int26_6(0), fixed.Int26_6(0), [4]int16{}, false | ||||||
|  | 		if flags&flagArg1And2AreWords != 0 { | ||||||
|  | 			dx = fixed.Int26_6(int16(u16(glyf, offset+4))) | ||||||
|  | 			dy = fixed.Int26_6(int16(u16(glyf, offset+6))) | ||||||
|  | 			offset += 8 | ||||||
|  | 		} else { | ||||||
|  | 			dx = fixed.Int26_6(int16(int8(glyf[offset+4]))) | ||||||
|  | 			dy = fixed.Int26_6(int16(int8(glyf[offset+5]))) | ||||||
|  | 			offset += 6 | ||||||
|  | 		} | ||||||
|  | 		if flags&flagArgsAreXYValues == 0 { | ||||||
|  | 			return UnsupportedError("compound glyph transform vector") | ||||||
|  | 		} | ||||||
|  | 		if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 { | ||||||
|  | 			hasTransform = true | ||||||
|  | 			switch { | ||||||
|  | 			case flags&flagWeHaveAScale != 0: | ||||||
|  | 				transform[0] = int16(u16(glyf, offset+0)) | ||||||
|  | 				transform[3] = transform[0] | ||||||
|  | 				offset += 2 | ||||||
|  | 			case flags&flagWeHaveAnXAndYScale != 0: | ||||||
|  | 				transform[0] = int16(u16(glyf, offset+0)) | ||||||
|  | 				transform[3] = int16(u16(glyf, offset+2)) | ||||||
|  | 				offset += 4 | ||||||
|  | 			case flags&flagWeHaveATwoByTwo != 0: | ||||||
|  | 				transform[0] = int16(u16(glyf, offset+0)) | ||||||
|  | 				transform[1] = int16(u16(glyf, offset+2)) | ||||||
|  | 				transform[2] = int16(u16(glyf, offset+4)) | ||||||
|  | 				transform[3] = int16(u16(glyf, offset+6)) | ||||||
|  | 				offset += 8 | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		savedPP := g.phantomPoints | ||||||
|  | 		np0 := len(g.Points) | ||||||
|  | 		componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0) | ||||||
|  | 		if err := g.load(recursion+1, component, componentUMM); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		if flags&flagUseMyMetrics == 0 { | ||||||
|  | 			g.phantomPoints = savedPP | ||||||
|  | 		} | ||||||
|  | 		if hasTransform { | ||||||
|  | 			for j := np0; j < len(g.Points); j++ { | ||||||
|  | 				p := &g.Points[j] | ||||||
|  | 				newX := 0 + | ||||||
|  | 					fixed.Int26_6((int64(p.X)*int64(transform[0])+1<<13)>>14) + | ||||||
|  | 					fixed.Int26_6((int64(p.Y)*int64(transform[2])+1<<13)>>14) | ||||||
|  | 				newY := 0 + | ||||||
|  | 					fixed.Int26_6((int64(p.X)*int64(transform[1])+1<<13)>>14) + | ||||||
|  | 					fixed.Int26_6((int64(p.Y)*int64(transform[3])+1<<13)>>14) | ||||||
|  | 				p.X, p.Y = newX, newY | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		dx = g.font.scale(g.scale * dx) | ||||||
|  | 		dy = g.font.scale(g.scale * dy) | ||||||
|  | 		if flags&flagRoundXYToGrid != 0 { | ||||||
|  | 			dx = (dx + 32) &^ 63 | ||||||
|  | 			dy = (dy + 32) &^ 63 | ||||||
|  | 		} | ||||||
|  | 		for j := np0; j < len(g.Points); j++ { | ||||||
|  | 			p := &g.Points[j] | ||||||
|  | 			p.X += dx | ||||||
|  | 			p.Y += dy | ||||||
|  | 		} | ||||||
|  | 		// TODO: also adjust g.InFontUnits and g.Unhinted? | ||||||
|  | 		if flags&flagMoreComponents == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	instrLen := 0 | ||||||
|  | 	if g.hinting != font.HintingNone && offset+2 <= len(glyf) { | ||||||
|  | 		instrLen = int(u16(glyf, offset)) | ||||||
|  | 		offset += 2 | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	g.addPhantomsAndScale(np0, len(g.Points), false, instrLen > 0) | ||||||
|  | 	points, ends := g.Points[np0:], g.Ends[ne0:] | ||||||
|  | 	g.Points = g.Points[:len(g.Points)-4] | ||||||
|  | 	for j := range points { | ||||||
|  | 		points[j].Flags &^= flagTouchedX | flagTouchedY | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if instrLen == 0 { | ||||||
|  | 		if !g.metricsSet { | ||||||
|  | 			copy(g.phantomPoints[:], points[len(points)-4:]) | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Hint the compound glyph. | ||||||
|  | 	program := glyf[offset : offset+instrLen] | ||||||
|  | 	// Temporarily adjust the ends to be relative to this compound glyph. | ||||||
|  | 	if np0 != 0 { | ||||||
|  | 		for i := range ends { | ||||||
|  | 			ends[i] -= np0 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// Hinting instructions of a composite glyph completely refer to the | ||||||
|  | 	// (already) hinted subglyphs. | ||||||
|  | 	g.tmp = append(g.tmp[:0], points...) | ||||||
|  | 	if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if np0 != 0 { | ||||||
|  | 		for i := range ends { | ||||||
|  | 			ends[i] += np0 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !g.metricsSet { | ||||||
|  | 		copy(g.phantomPoints[:], points[len(points)-4:]) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) { | ||||||
|  | 	// Add the four phantom points. | ||||||
|  | 	g.Points = append(g.Points, g.phantomPoints[:]...) | ||||||
|  | 	// Scale the points. | ||||||
|  | 	if simple && g.hinting != font.HintingNone { | ||||||
|  | 		g.InFontUnits = append(g.InFontUnits, g.Points[np1:]...) | ||||||
|  | 	} | ||||||
|  | 	for i := np1; i < len(g.Points); i++ { | ||||||
|  | 		p := &g.Points[i] | ||||||
|  | 		p.X = g.font.scale(g.scale * p.X) | ||||||
|  | 		p.Y = g.font.scale(g.scale * p.Y) | ||||||
|  | 	} | ||||||
|  | 	if g.hinting == font.HintingNone { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	// Round the 1st phantom point to the grid, shifting all other points equally. | ||||||
|  | 	// Note that "all other points" starts from np0, not np1. | ||||||
|  | 	// TODO: delete this adjustment and the np0/np1 distinction, when | ||||||
|  | 	// we update the compatibility tests to C Freetype 2.5.3. | ||||||
|  | 	// See http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=05c786d990390a7ca18e62962641dac740bacb06 | ||||||
|  | 	if adjust { | ||||||
|  | 		pp1x := g.Points[len(g.Points)-4].X | ||||||
|  | 		if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 { | ||||||
|  | 			for i := np0; i < len(g.Points); i++ { | ||||||
|  | 				g.Points[i].X += dx | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if simple { | ||||||
|  | 		g.Unhinted = append(g.Unhinted, g.Points[np1:]...) | ||||||
|  | 	} | ||||||
|  | 	// Round the 2nd and 4th phantom point to the grid. | ||||||
|  | 	p := &g.Points[len(g.Points)-3] | ||||||
|  | 	p.X = (p.X + 32) &^ 63 | ||||||
|  | 	p = &g.Points[len(g.Points)-1] | ||||||
|  | 	p.Y = (p.Y + 32) &^ 63 | ||||||
|  | } | ||||||
							
								
								
									
										1770
									
								
								vendor/github.com/golang/freetype/truetype/hint.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1770
									
								
								vendor/github.com/golang/freetype/truetype/hint.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										289
									
								
								vendor/github.com/golang/freetype/truetype/opcodes.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								vendor/github.com/golang/freetype/truetype/opcodes.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,289 @@ | |||||||
|  | // Copyright 2012 The Freetype-Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by your choice of either the | ||||||
|  | // FreeType License or the GNU General Public License version 2 (or | ||||||
|  | // any later version), both of which can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package truetype | ||||||
|  |  | ||||||
|  | // The Truetype opcodes are summarized at | ||||||
|  | // https://developer.apple.com/fonts/TTRefMan/RM07/appendixA.html | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	opSVTCA0    = 0x00 // Set freedom and projection Vectors To Coordinate Axis | ||||||
|  | 	opSVTCA1    = 0x01 // . | ||||||
|  | 	opSPVTCA0   = 0x02 // Set Projection Vector To Coordinate Axis | ||||||
|  | 	opSPVTCA1   = 0x03 // . | ||||||
|  | 	opSFVTCA0   = 0x04 // Set Freedom Vector to Coordinate Axis | ||||||
|  | 	opSFVTCA1   = 0x05 // . | ||||||
|  | 	opSPVTL0    = 0x06 // Set Projection Vector To Line | ||||||
|  | 	opSPVTL1    = 0x07 // . | ||||||
|  | 	opSFVTL0    = 0x08 // Set Freedom Vector To Line | ||||||
|  | 	opSFVTL1    = 0x09 // . | ||||||
|  | 	opSPVFS     = 0x0a // Set Projection Vector From Stack | ||||||
|  | 	opSFVFS     = 0x0b // Set Freedom Vector From Stack | ||||||
|  | 	opGPV       = 0x0c // Get Projection Vector | ||||||
|  | 	opGFV       = 0x0d // Get Freedom Vector | ||||||
|  | 	opSFVTPV    = 0x0e // Set Freedom Vector To Projection Vector | ||||||
|  | 	opISECT     = 0x0f // moves point p to the InterSECTion of two lines | ||||||
|  | 	opSRP0      = 0x10 // Set Reference Point 0 | ||||||
|  | 	opSRP1      = 0x11 // Set Reference Point 1 | ||||||
|  | 	opSRP2      = 0x12 // Set Reference Point 2 | ||||||
|  | 	opSZP0      = 0x13 // Set Zone Pointer 0 | ||||||
|  | 	opSZP1      = 0x14 // Set Zone Pointer 1 | ||||||
|  | 	opSZP2      = 0x15 // Set Zone Pointer 2 | ||||||
|  | 	opSZPS      = 0x16 // Set Zone PointerS | ||||||
|  | 	opSLOOP     = 0x17 // Set LOOP variable | ||||||
|  | 	opRTG       = 0x18 // Round To Grid | ||||||
|  | 	opRTHG      = 0x19 // Round To Half Grid | ||||||
|  | 	opSMD       = 0x1a // Set Minimum Distance | ||||||
|  | 	opELSE      = 0x1b // ELSE clause | ||||||
|  | 	opJMPR      = 0x1c // JuMP Relative | ||||||
|  | 	opSCVTCI    = 0x1d // Set Control Value Table Cut-In | ||||||
|  | 	opSSWCI     = 0x1e // Set Single Width Cut-In | ||||||
|  | 	opSSW       = 0x1f // Set Single Width | ||||||
|  | 	opDUP       = 0x20 // DUPlicate top stack element | ||||||
|  | 	opPOP       = 0x21 // POP top stack element | ||||||
|  | 	opCLEAR     = 0x22 // CLEAR the stack | ||||||
|  | 	opSWAP      = 0x23 // SWAP the top two elements on the stack | ||||||
|  | 	opDEPTH     = 0x24 // DEPTH of the stack | ||||||
|  | 	opCINDEX    = 0x25 // Copy the INDEXed element to the top of the stack | ||||||
|  | 	opMINDEX    = 0x26 // Move the INDEXed element to the top of the stack | ||||||
|  | 	opALIGNPTS  = 0x27 // ALIGN PoinTS | ||||||
|  | 	op_0x28     = 0x28 // deprecated | ||||||
|  | 	opUTP       = 0x29 // UnTouch Point | ||||||
|  | 	opLOOPCALL  = 0x2a // LOOP and CALL function | ||||||
|  | 	opCALL      = 0x2b // CALL function | ||||||
|  | 	opFDEF      = 0x2c // Function DEFinition | ||||||
|  | 	opENDF      = 0x2d // END Function definition | ||||||
|  | 	opMDAP0     = 0x2e // Move Direct Absolute Point | ||||||
|  | 	opMDAP1     = 0x2f // . | ||||||
|  | 	opIUP0      = 0x30 // Interpolate Untouched Points through the outline | ||||||
|  | 	opIUP1      = 0x31 // . | ||||||
|  | 	opSHP0      = 0x32 // SHift Point using reference point | ||||||
|  | 	opSHP1      = 0x33 // . | ||||||
|  | 	opSHC0      = 0x34 // SHift Contour using reference point | ||||||
|  | 	opSHC1      = 0x35 // . | ||||||
|  | 	opSHZ0      = 0x36 // SHift Zone using reference point | ||||||
|  | 	opSHZ1      = 0x37 // . | ||||||
|  | 	opSHPIX     = 0x38 // SHift point by a PIXel amount | ||||||
|  | 	opIP        = 0x39 // Interpolate Point | ||||||
|  | 	opMSIRP0    = 0x3a // Move Stack Indirect Relative Point | ||||||
|  | 	opMSIRP1    = 0x3b // . | ||||||
|  | 	opALIGNRP   = 0x3c // ALIGN to Reference Point | ||||||
|  | 	opRTDG      = 0x3d // Round To Double Grid | ||||||
|  | 	opMIAP0     = 0x3e // Move Indirect Absolute Point | ||||||
|  | 	opMIAP1     = 0x3f // . | ||||||
|  | 	opNPUSHB    = 0x40 // PUSH N Bytes | ||||||
|  | 	opNPUSHW    = 0x41 // PUSH N Words | ||||||
|  | 	opWS        = 0x42 // Write Store | ||||||
|  | 	opRS        = 0x43 // Read Store | ||||||
|  | 	opWCVTP     = 0x44 // Write Control Value Table in Pixel units | ||||||
|  | 	opRCVT      = 0x45 // Read Control Value Table entry | ||||||
|  | 	opGC0       = 0x46 // Get Coordinate projected onto the projection vector | ||||||
|  | 	opGC1       = 0x47 // . | ||||||
|  | 	opSCFS      = 0x48 // Sets Coordinate From the Stack using projection vector and freedom vector | ||||||
|  | 	opMD0       = 0x49 // Measure Distance | ||||||
|  | 	opMD1       = 0x4a // . | ||||||
|  | 	opMPPEM     = 0x4b // Measure Pixels Per EM | ||||||
|  | 	opMPS       = 0x4c // Measure Point Size | ||||||
|  | 	opFLIPON    = 0x4d // set the auto FLIP Boolean to ON | ||||||
|  | 	opFLIPOFF   = 0x4e // set the auto FLIP Boolean to OFF | ||||||
|  | 	opDEBUG     = 0x4f // DEBUG call | ||||||
|  | 	opLT        = 0x50 // Less Than | ||||||
|  | 	opLTEQ      = 0x51 // Less Than or EQual | ||||||
|  | 	opGT        = 0x52 // Greater Than | ||||||
|  | 	opGTEQ      = 0x53 // Greater Than or EQual | ||||||
|  | 	opEQ        = 0x54 // EQual | ||||||
|  | 	opNEQ       = 0x55 // Not EQual | ||||||
|  | 	opODD       = 0x56 // ODD | ||||||
|  | 	opEVEN      = 0x57 // EVEN | ||||||
|  | 	opIF        = 0x58 // IF test | ||||||
|  | 	opEIF       = 0x59 // End IF | ||||||
|  | 	opAND       = 0x5a // logical AND | ||||||
|  | 	opOR        = 0x5b // logical OR | ||||||
|  | 	opNOT       = 0x5c // logical NOT | ||||||
|  | 	opDELTAP1   = 0x5d // DELTA exception P1 | ||||||
|  | 	opSDB       = 0x5e // Set Delta Base in the graphics state | ||||||
|  | 	opSDS       = 0x5f // Set Delta Shift in the graphics state | ||||||
|  | 	opADD       = 0x60 // ADD | ||||||
|  | 	opSUB       = 0x61 // SUBtract | ||||||
|  | 	opDIV       = 0x62 // DIVide | ||||||
|  | 	opMUL       = 0x63 // MULtiply | ||||||
|  | 	opABS       = 0x64 // ABSolute value | ||||||
|  | 	opNEG       = 0x65 // NEGate | ||||||
|  | 	opFLOOR     = 0x66 // FLOOR | ||||||
|  | 	opCEILING   = 0x67 // CEILING | ||||||
|  | 	opROUND00   = 0x68 // ROUND value | ||||||
|  | 	opROUND01   = 0x69 // . | ||||||
|  | 	opROUND10   = 0x6a // . | ||||||
|  | 	opROUND11   = 0x6b // . | ||||||
|  | 	opNROUND00  = 0x6c // No ROUNDing of value | ||||||
|  | 	opNROUND01  = 0x6d // . | ||||||
|  | 	opNROUND10  = 0x6e // . | ||||||
|  | 	opNROUND11  = 0x6f // . | ||||||
|  | 	opWCVTF     = 0x70 // Write Control Value Table in Funits | ||||||
|  | 	opDELTAP2   = 0x71 // DELTA exception P2 | ||||||
|  | 	opDELTAP3   = 0x72 // DELTA exception P3 | ||||||
|  | 	opDELTAC1   = 0x73 // DELTA exception C1 | ||||||
|  | 	opDELTAC2   = 0x74 // DELTA exception C2 | ||||||
|  | 	opDELTAC3   = 0x75 // DELTA exception C3 | ||||||
|  | 	opSROUND    = 0x76 // Super ROUND | ||||||
|  | 	opS45ROUND  = 0x77 // Super ROUND 45 degrees | ||||||
|  | 	opJROT      = 0x78 // Jump Relative On True | ||||||
|  | 	opJROF      = 0x79 // Jump Relative On False | ||||||
|  | 	opROFF      = 0x7a // Round OFF | ||||||
|  | 	op_0x7b     = 0x7b // deprecated | ||||||
|  | 	opRUTG      = 0x7c // Round Up To Grid | ||||||
|  | 	opRDTG      = 0x7d // Round Down To Grid | ||||||
|  | 	opSANGW     = 0x7e // Set ANGle Weight | ||||||
|  | 	opAA        = 0x7f // Adjust Angle | ||||||
|  | 	opFLIPPT    = 0x80 // FLIP PoinT | ||||||
|  | 	opFLIPRGON  = 0x81 // FLIP RanGe ON | ||||||
|  | 	opFLIPRGOFF = 0x82 // FLIP RanGe OFF | ||||||
|  | 	op_0x83     = 0x83 // deprecated | ||||||
|  | 	op_0x84     = 0x84 // deprecated | ||||||
|  | 	opSCANCTRL  = 0x85 // SCAN conversion ConTRoL | ||||||
|  | 	opSDPVTL0   = 0x86 // Set Dual Projection Vector To Line | ||||||
|  | 	opSDPVTL1   = 0x87 // . | ||||||
|  | 	opGETINFO   = 0x88 // GET INFOrmation | ||||||
|  | 	opIDEF      = 0x89 // Instruction DEFinition | ||||||
|  | 	opROLL      = 0x8a // ROLL the top three stack elements | ||||||
|  | 	opMAX       = 0x8b // MAXimum of top two stack elements | ||||||
|  | 	opMIN       = 0x8c // MINimum of top two stack elements | ||||||
|  | 	opSCANTYPE  = 0x8d // SCANTYPE | ||||||
|  | 	opINSTCTRL  = 0x8e // INSTRuction execution ConTRoL | ||||||
|  | 	op_0x8f     = 0x8f | ||||||
|  | 	op_0x90     = 0x90 | ||||||
|  | 	op_0x91     = 0x91 | ||||||
|  | 	op_0x92     = 0x92 | ||||||
|  | 	op_0x93     = 0x93 | ||||||
|  | 	op_0x94     = 0x94 | ||||||
|  | 	op_0x95     = 0x95 | ||||||
|  | 	op_0x96     = 0x96 | ||||||
|  | 	op_0x97     = 0x97 | ||||||
|  | 	op_0x98     = 0x98 | ||||||
|  | 	op_0x99     = 0x99 | ||||||
|  | 	op_0x9a     = 0x9a | ||||||
|  | 	op_0x9b     = 0x9b | ||||||
|  | 	op_0x9c     = 0x9c | ||||||
|  | 	op_0x9d     = 0x9d | ||||||
|  | 	op_0x9e     = 0x9e | ||||||
|  | 	op_0x9f     = 0x9f | ||||||
|  | 	op_0xa0     = 0xa0 | ||||||
|  | 	op_0xa1     = 0xa1 | ||||||
|  | 	op_0xa2     = 0xa2 | ||||||
|  | 	op_0xa3     = 0xa3 | ||||||
|  | 	op_0xa4     = 0xa4 | ||||||
|  | 	op_0xa5     = 0xa5 | ||||||
|  | 	op_0xa6     = 0xa6 | ||||||
|  | 	op_0xa7     = 0xa7 | ||||||
|  | 	op_0xa8     = 0xa8 | ||||||
|  | 	op_0xa9     = 0xa9 | ||||||
|  | 	op_0xaa     = 0xaa | ||||||
|  | 	op_0xab     = 0xab | ||||||
|  | 	op_0xac     = 0xac | ||||||
|  | 	op_0xad     = 0xad | ||||||
|  | 	op_0xae     = 0xae | ||||||
|  | 	op_0xaf     = 0xaf | ||||||
|  | 	opPUSHB000  = 0xb0 // PUSH Bytes | ||||||
|  | 	opPUSHB001  = 0xb1 // . | ||||||
|  | 	opPUSHB010  = 0xb2 // . | ||||||
|  | 	opPUSHB011  = 0xb3 // . | ||||||
|  | 	opPUSHB100  = 0xb4 // . | ||||||
|  | 	opPUSHB101  = 0xb5 // . | ||||||
|  | 	opPUSHB110  = 0xb6 // . | ||||||
|  | 	opPUSHB111  = 0xb7 // . | ||||||
|  | 	opPUSHW000  = 0xb8 // PUSH Words | ||||||
|  | 	opPUSHW001  = 0xb9 // . | ||||||
|  | 	opPUSHW010  = 0xba // . | ||||||
|  | 	opPUSHW011  = 0xbb // . | ||||||
|  | 	opPUSHW100  = 0xbc // . | ||||||
|  | 	opPUSHW101  = 0xbd // . | ||||||
|  | 	opPUSHW110  = 0xbe // . | ||||||
|  | 	opPUSHW111  = 0xbf // . | ||||||
|  | 	opMDRP00000 = 0xc0 // Move Direct Relative Point | ||||||
|  | 	opMDRP00001 = 0xc1 // . | ||||||
|  | 	opMDRP00010 = 0xc2 // . | ||||||
|  | 	opMDRP00011 = 0xc3 // . | ||||||
|  | 	opMDRP00100 = 0xc4 // . | ||||||
|  | 	opMDRP00101 = 0xc5 // . | ||||||
|  | 	opMDRP00110 = 0xc6 // . | ||||||
|  | 	opMDRP00111 = 0xc7 // . | ||||||
|  | 	opMDRP01000 = 0xc8 // . | ||||||
|  | 	opMDRP01001 = 0xc9 // . | ||||||
|  | 	opMDRP01010 = 0xca // . | ||||||
|  | 	opMDRP01011 = 0xcb // . | ||||||
|  | 	opMDRP01100 = 0xcc // . | ||||||
|  | 	opMDRP01101 = 0xcd // . | ||||||
|  | 	opMDRP01110 = 0xce // . | ||||||
|  | 	opMDRP01111 = 0xcf // . | ||||||
|  | 	opMDRP10000 = 0xd0 // . | ||||||
|  | 	opMDRP10001 = 0xd1 // . | ||||||
|  | 	opMDRP10010 = 0xd2 // . | ||||||
|  | 	opMDRP10011 = 0xd3 // . | ||||||
|  | 	opMDRP10100 = 0xd4 // . | ||||||
|  | 	opMDRP10101 = 0xd5 // . | ||||||
|  | 	opMDRP10110 = 0xd6 // . | ||||||
|  | 	opMDRP10111 = 0xd7 // . | ||||||
|  | 	opMDRP11000 = 0xd8 // . | ||||||
|  | 	opMDRP11001 = 0xd9 // . | ||||||
|  | 	opMDRP11010 = 0xda // . | ||||||
|  | 	opMDRP11011 = 0xdb // . | ||||||
|  | 	opMDRP11100 = 0xdc // . | ||||||
|  | 	opMDRP11101 = 0xdd // . | ||||||
|  | 	opMDRP11110 = 0xde // . | ||||||
|  | 	opMDRP11111 = 0xdf // . | ||||||
|  | 	opMIRP00000 = 0xe0 // Move Indirect Relative Point | ||||||
|  | 	opMIRP00001 = 0xe1 // . | ||||||
|  | 	opMIRP00010 = 0xe2 // . | ||||||
|  | 	opMIRP00011 = 0xe3 // . | ||||||
|  | 	opMIRP00100 = 0xe4 // . | ||||||
|  | 	opMIRP00101 = 0xe5 // . | ||||||
|  | 	opMIRP00110 = 0xe6 // . | ||||||
|  | 	opMIRP00111 = 0xe7 // . | ||||||
|  | 	opMIRP01000 = 0xe8 // . | ||||||
|  | 	opMIRP01001 = 0xe9 // . | ||||||
|  | 	opMIRP01010 = 0xea // . | ||||||
|  | 	opMIRP01011 = 0xeb // . | ||||||
|  | 	opMIRP01100 = 0xec // . | ||||||
|  | 	opMIRP01101 = 0xed // . | ||||||
|  | 	opMIRP01110 = 0xee // . | ||||||
|  | 	opMIRP01111 = 0xef // . | ||||||
|  | 	opMIRP10000 = 0xf0 // . | ||||||
|  | 	opMIRP10001 = 0xf1 // . | ||||||
|  | 	opMIRP10010 = 0xf2 // . | ||||||
|  | 	opMIRP10011 = 0xf3 // . | ||||||
|  | 	opMIRP10100 = 0xf4 // . | ||||||
|  | 	opMIRP10101 = 0xf5 // . | ||||||
|  | 	opMIRP10110 = 0xf6 // . | ||||||
|  | 	opMIRP10111 = 0xf7 // . | ||||||
|  | 	opMIRP11000 = 0xf8 // . | ||||||
|  | 	opMIRP11001 = 0xf9 // . | ||||||
|  | 	opMIRP11010 = 0xfa // . | ||||||
|  | 	opMIRP11011 = 0xfb // . | ||||||
|  | 	opMIRP11100 = 0xfc // . | ||||||
|  | 	opMIRP11101 = 0xfd // . | ||||||
|  | 	opMIRP11110 = 0xfe // . | ||||||
|  | 	opMIRP11111 = 0xff // . | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // popCount is the number of stack elements that each opcode pops. | ||||||
|  | var popCount = [256]uint8{ | ||||||
|  | 	// 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f | ||||||
|  | 	0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 5, // 0x00 - 0x0f | ||||||
|  | 	1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, // 0x10 - 0x1f | ||||||
|  | 	1, 1, 0, 2, 0, 1, 1, 2, 0, 1, 2, 1, 1, 0, 1, 1, // 0x20 - 0x2f | ||||||
|  | 	0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 2, 2, 0, 0, 2, 2, // 0x30 - 0x3f | ||||||
|  | 	0, 0, 2, 1, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, // 0x40 - 0x4f | ||||||
|  | 	2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 2, 2, 1, 1, 1, 1, // 0x50 - 0x5f | ||||||
|  | 	2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6f | ||||||
|  | 	2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 1, 1, // 0x70 - 0x7f | ||||||
|  | 	0, 2, 2, 0, 0, 1, 2, 2, 1, 1, 3, 2, 2, 1, 2, 0, // 0x80 - 0x8f | ||||||
|  | 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0x9f | ||||||
|  | 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0 - 0xaf | ||||||
|  | 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 - 0xbf | ||||||
|  | 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 - 0xcf | ||||||
|  | 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 - 0xdf | ||||||
|  | 	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 0xef | ||||||
|  | 	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 - 0xff | ||||||
|  | } | ||||||
							
								
								
									
										653
									
								
								vendor/github.com/golang/freetype/truetype/truetype.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										653
									
								
								vendor/github.com/golang/freetype/truetype/truetype.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,653 @@ | |||||||
|  | // Copyright 2010 The Freetype-Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by your choice of either the | ||||||
|  | // FreeType License or the GNU General Public License version 2 (or | ||||||
|  | // any later version), both of which can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | // Package truetype provides a parser for the TTF and TTC file formats. | ||||||
|  | // Those formats are documented at http://developer.apple.com/fonts/TTRefMan/ | ||||||
|  | // and http://www.microsoft.com/typography/otspec/ | ||||||
|  | // | ||||||
|  | // Some of a font's methods provide lengths or co-ordinates, e.g. bounds, font | ||||||
|  | // metrics and control points. All these methods take a scale parameter, which | ||||||
|  | // is the number of pixels in 1 em, expressed as a 26.6 fixed point value. For | ||||||
|  | // example, if 1 em is 10 pixels then scale is fixed.I(10), which is equal to | ||||||
|  | // fixed.Int26_6(10 << 6). | ||||||
|  | // | ||||||
|  | // To measure a TrueType font in ideal FUnit space, use scale equal to | ||||||
|  | // font.FUnitsPerEm(). | ||||||
|  | package truetype // import "github.com/golang/freetype/truetype" | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/image/math/fixed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // An Index is a Font's index of a rune. | ||||||
|  | type Index uint16 | ||||||
|  |  | ||||||
|  | // A NameID identifies a name table entry. | ||||||
|  | // | ||||||
|  | // See https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html | ||||||
|  | type NameID uint16 | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	NameIDCopyright          NameID = 0 | ||||||
|  | 	NameIDFontFamily                = 1 | ||||||
|  | 	NameIDFontSubfamily             = 2 | ||||||
|  | 	NameIDUniqueSubfamilyID         = 3 | ||||||
|  | 	NameIDFontFullName              = 4 | ||||||
|  | 	NameIDNameTableVersion          = 5 | ||||||
|  | 	NameIDPostscriptName            = 6 | ||||||
|  | 	NameIDTrademarkNotice           = 7 | ||||||
|  | 	NameIDManufacturerName          = 8 | ||||||
|  | 	NameIDDesignerName              = 9 | ||||||
|  | 	NameIDFontDescription           = 10 | ||||||
|  | 	NameIDFontVendorURL             = 11 | ||||||
|  | 	NameIDFontDesignerURL           = 12 | ||||||
|  | 	NameIDFontLicense               = 13 | ||||||
|  | 	NameIDFontLicenseURL            = 14 | ||||||
|  | 	NameIDPreferredFamily           = 16 | ||||||
|  | 	NameIDPreferredSubfamily        = 17 | ||||||
|  | 	NameIDCompatibleName            = 18 | ||||||
|  | 	NameIDSampleText                = 19 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// A 32-bit encoding consists of a most-significant 16-bit Platform ID and a | ||||||
|  | 	// least-significant 16-bit Platform Specific ID. The magic numbers are | ||||||
|  | 	// specified at https://www.microsoft.com/typography/otspec/name.htm | ||||||
|  | 	unicodeEncodingBMPOnly  = 0x00000003 // PID = 0 (Unicode), PSID = 3 (Unicode 2.0 BMP Only) | ||||||
|  | 	unicodeEncodingFull     = 0x00000004 // PID = 0 (Unicode), PSID = 4 (Unicode 2.0 Full Repertoire) | ||||||
|  | 	microsoftSymbolEncoding = 0x00030000 // PID = 3 (Microsoft), PSID = 0 (Symbol) | ||||||
|  | 	microsoftUCS2Encoding   = 0x00030001 // PID = 3 (Microsoft), PSID = 1 (UCS-2) | ||||||
|  | 	microsoftUCS4Encoding   = 0x0003000a // PID = 3 (Microsoft), PSID = 10 (UCS-4) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // An HMetric holds the horizontal metrics of a single glyph. | ||||||
|  | type HMetric struct { | ||||||
|  | 	AdvanceWidth, LeftSideBearing fixed.Int26_6 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // A VMetric holds the vertical metrics of a single glyph. | ||||||
|  | type VMetric struct { | ||||||
|  | 	AdvanceHeight, TopSideBearing fixed.Int26_6 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // A FormatError reports that the input is not a valid TrueType font. | ||||||
|  | type FormatError string | ||||||
|  |  | ||||||
|  | func (e FormatError) Error() string { | ||||||
|  | 	return "freetype: invalid TrueType format: " + string(e) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // An UnsupportedError reports that the input uses a valid but unimplemented | ||||||
|  | // TrueType feature. | ||||||
|  | type UnsupportedError string | ||||||
|  |  | ||||||
|  | func (e UnsupportedError) Error() string { | ||||||
|  | 	return "freetype: unsupported TrueType feature: " + string(e) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // u32 returns the big-endian uint32 at b[i:]. | ||||||
|  | func u32(b []byte, i int) uint32 { | ||||||
|  | 	return uint32(b[i])<<24 | uint32(b[i+1])<<16 | uint32(b[i+2])<<8 | uint32(b[i+3]) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // u16 returns the big-endian uint16 at b[i:]. | ||||||
|  | func u16(b []byte, i int) uint16 { | ||||||
|  | 	return uint16(b[i])<<8 | uint16(b[i+1]) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // readTable returns a slice of the TTF data given by a table's directory entry. | ||||||
|  | func readTable(ttf []byte, offsetLength []byte) ([]byte, error) { | ||||||
|  | 	offset := int(u32(offsetLength, 0)) | ||||||
|  | 	if offset < 0 { | ||||||
|  | 		return nil, FormatError(fmt.Sprintf("offset too large: %d", uint32(offset))) | ||||||
|  | 	} | ||||||
|  | 	length := int(u32(offsetLength, 4)) | ||||||
|  | 	if length < 0 { | ||||||
|  | 		return nil, FormatError(fmt.Sprintf("length too large: %d", uint32(length))) | ||||||
|  | 	} | ||||||
|  | 	end := offset + length | ||||||
|  | 	if end < 0 || end > len(ttf) { | ||||||
|  | 		return nil, FormatError(fmt.Sprintf("offset + length too large: %d", uint32(offset)+uint32(length))) | ||||||
|  | 	} | ||||||
|  | 	return ttf[offset:end], nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // parseSubtables returns the offset and platformID of the best subtable in | ||||||
|  | // table, where best favors a Unicode cmap encoding, and failing that, a | ||||||
|  | // Microsoft cmap encoding. offset is the offset of the first subtable in | ||||||
|  | // table, and size is the size of each subtable. | ||||||
|  | // | ||||||
|  | // If pred is non-nil, then only subtables that satisfy that predicate will be | ||||||
|  | // considered. | ||||||
|  | func parseSubtables(table []byte, name string, offset, size int, pred func([]byte) bool) ( | ||||||
|  | 	bestOffset int, bestPID uint32, retErr error) { | ||||||
|  |  | ||||||
|  | 	if len(table) < 4 { | ||||||
|  | 		return 0, 0, FormatError(name + " too short") | ||||||
|  | 	} | ||||||
|  | 	nSubtables := int(u16(table, 2)) | ||||||
|  | 	if len(table) < size*nSubtables+offset { | ||||||
|  | 		return 0, 0, FormatError(name + " too short") | ||||||
|  | 	} | ||||||
|  | 	ok := false | ||||||
|  | 	for i := 0; i < nSubtables; i, offset = i+1, offset+size { | ||||||
|  | 		if pred != nil && !pred(table[offset:]) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		// We read the 16-bit Platform ID and 16-bit Platform Specific ID as a single uint32. | ||||||
|  | 		// All values are big-endian. | ||||||
|  | 		pidPsid := u32(table, offset) | ||||||
|  | 		// We prefer the Unicode cmap encoding. Failing to find that, we fall | ||||||
|  | 		// back onto the Microsoft cmap encoding. | ||||||
|  | 		if pidPsid == unicodeEncodingBMPOnly || pidPsid == unicodeEncodingFull { | ||||||
|  | 			bestOffset, bestPID, ok = offset, pidPsid>>16, true | ||||||
|  | 			break | ||||||
|  |  | ||||||
|  | 		} else if pidPsid == microsoftSymbolEncoding || | ||||||
|  | 			pidPsid == microsoftUCS2Encoding || | ||||||
|  | 			pidPsid == microsoftUCS4Encoding { | ||||||
|  |  | ||||||
|  | 			bestOffset, bestPID, ok = offset, pidPsid>>16, true | ||||||
|  | 			// We don't break out of the for loop, so that Unicode can override Microsoft. | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !ok { | ||||||
|  | 		return 0, 0, UnsupportedError(name + " encoding") | ||||||
|  | 	} | ||||||
|  | 	return bestOffset, bestPID, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	locaOffsetFormatUnknown int = iota | ||||||
|  | 	locaOffsetFormatShort | ||||||
|  | 	locaOffsetFormatLong | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // A cm holds a parsed cmap entry. | ||||||
|  | type cm struct { | ||||||
|  | 	start, end, delta, offset uint32 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // A Font represents a Truetype font. | ||||||
|  | type Font struct { | ||||||
|  | 	// Tables sliced from the TTF data. The different tables are documented | ||||||
|  | 	// at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html | ||||||
|  | 	cmap, cvt, fpgm, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, os2, prep, vmtx []byte | ||||||
|  |  | ||||||
|  | 	cmapIndexes []byte | ||||||
|  |  | ||||||
|  | 	// Cached values derived from the raw ttf data. | ||||||
|  | 	cm                      []cm | ||||||
|  | 	locaOffsetFormat        int | ||||||
|  | 	nGlyph, nHMetric, nKern int | ||||||
|  | 	fUnitsPerEm             int32 | ||||||
|  | 	ascent                  int32               // In FUnits. | ||||||
|  | 	descent                 int32               // In FUnits; typically negative. | ||||||
|  | 	bounds                  fixed.Rectangle26_6 // In FUnits. | ||||||
|  | 	// Values from the maxp section. | ||||||
|  | 	maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Font) parseCmap() error { | ||||||
|  | 	const ( | ||||||
|  | 		cmapFormat4         = 4 | ||||||
|  | 		cmapFormat12        = 12 | ||||||
|  | 		languageIndependent = 0 | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	offset, _, err := parseSubtables(f.cmap, "cmap", 4, 8, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	offset = int(u32(f.cmap, offset+4)) | ||||||
|  | 	if offset <= 0 || offset > len(f.cmap) { | ||||||
|  | 		return FormatError("bad cmap offset") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cmapFormat := u16(f.cmap, offset) | ||||||
|  | 	switch cmapFormat { | ||||||
|  | 	case cmapFormat4: | ||||||
|  | 		language := u16(f.cmap, offset+4) | ||||||
|  | 		if language != languageIndependent { | ||||||
|  | 			return UnsupportedError(fmt.Sprintf("language: %d", language)) | ||||||
|  | 		} | ||||||
|  | 		segCountX2 := int(u16(f.cmap, offset+6)) | ||||||
|  | 		if segCountX2%2 == 1 { | ||||||
|  | 			return FormatError(fmt.Sprintf("bad segCountX2: %d", segCountX2)) | ||||||
|  | 		} | ||||||
|  | 		segCount := segCountX2 / 2 | ||||||
|  | 		offset += 14 | ||||||
|  | 		f.cm = make([]cm, segCount) | ||||||
|  | 		for i := 0; i < segCount; i++ { | ||||||
|  | 			f.cm[i].end = uint32(u16(f.cmap, offset)) | ||||||
|  | 			offset += 2 | ||||||
|  | 		} | ||||||
|  | 		offset += 2 | ||||||
|  | 		for i := 0; i < segCount; i++ { | ||||||
|  | 			f.cm[i].start = uint32(u16(f.cmap, offset)) | ||||||
|  | 			offset += 2 | ||||||
|  | 		} | ||||||
|  | 		for i := 0; i < segCount; i++ { | ||||||
|  | 			f.cm[i].delta = uint32(u16(f.cmap, offset)) | ||||||
|  | 			offset += 2 | ||||||
|  | 		} | ||||||
|  | 		for i := 0; i < segCount; i++ { | ||||||
|  | 			f.cm[i].offset = uint32(u16(f.cmap, offset)) | ||||||
|  | 			offset += 2 | ||||||
|  | 		} | ||||||
|  | 		f.cmapIndexes = f.cmap[offset:] | ||||||
|  | 		return nil | ||||||
|  |  | ||||||
|  | 	case cmapFormat12: | ||||||
|  | 		if u16(f.cmap, offset+2) != 0 { | ||||||
|  | 			return FormatError(fmt.Sprintf("cmap format: % x", f.cmap[offset:offset+4])) | ||||||
|  | 		} | ||||||
|  | 		length := u32(f.cmap, offset+4) | ||||||
|  | 		language := u32(f.cmap, offset+8) | ||||||
|  | 		if language != languageIndependent { | ||||||
|  | 			return UnsupportedError(fmt.Sprintf("language: %d", language)) | ||||||
|  | 		} | ||||||
|  | 		nGroups := u32(f.cmap, offset+12) | ||||||
|  | 		if length != 12*nGroups+16 { | ||||||
|  | 			return FormatError("inconsistent cmap length") | ||||||
|  | 		} | ||||||
|  | 		offset += 16 | ||||||
|  | 		f.cm = make([]cm, nGroups) | ||||||
|  | 		for i := uint32(0); i < nGroups; i++ { | ||||||
|  | 			f.cm[i].start = u32(f.cmap, offset+0) | ||||||
|  | 			f.cm[i].end = u32(f.cmap, offset+4) | ||||||
|  | 			f.cm[i].delta = u32(f.cmap, offset+8) - f.cm[i].start | ||||||
|  | 			offset += 12 | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return UnsupportedError(fmt.Sprintf("cmap format: %d", cmapFormat)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Font) parseHead() error { | ||||||
|  | 	if len(f.head) != 54 { | ||||||
|  | 		return FormatError(fmt.Sprintf("bad head length: %d", len(f.head))) | ||||||
|  | 	} | ||||||
|  | 	f.fUnitsPerEm = int32(u16(f.head, 18)) | ||||||
|  | 	f.bounds.Min.X = fixed.Int26_6(int16(u16(f.head, 36))) | ||||||
|  | 	f.bounds.Min.Y = fixed.Int26_6(int16(u16(f.head, 38))) | ||||||
|  | 	f.bounds.Max.X = fixed.Int26_6(int16(u16(f.head, 40))) | ||||||
|  | 	f.bounds.Max.Y = fixed.Int26_6(int16(u16(f.head, 42))) | ||||||
|  | 	switch i := u16(f.head, 50); i { | ||||||
|  | 	case 0: | ||||||
|  | 		f.locaOffsetFormat = locaOffsetFormatShort | ||||||
|  | 	case 1: | ||||||
|  | 		f.locaOffsetFormat = locaOffsetFormatLong | ||||||
|  | 	default: | ||||||
|  | 		return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i)) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Font) parseHhea() error { | ||||||
|  | 	if len(f.hhea) != 36 { | ||||||
|  | 		return FormatError(fmt.Sprintf("bad hhea length: %d", len(f.hhea))) | ||||||
|  | 	} | ||||||
|  | 	f.ascent = int32(int16(u16(f.hhea, 4))) | ||||||
|  | 	f.descent = int32(int16(u16(f.hhea, 6))) | ||||||
|  | 	f.nHMetric = int(u16(f.hhea, 34)) | ||||||
|  | 	if 4*f.nHMetric+2*(f.nGlyph-f.nHMetric) != len(f.hmtx) { | ||||||
|  | 		return FormatError(fmt.Sprintf("bad hmtx length: %d", len(f.hmtx))) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Font) parseKern() error { | ||||||
|  | 	// Apple's TrueType documentation (http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html) says: | ||||||
|  | 	// "Previous versions of the 'kern' table defined both the version and nTables fields in the header | ||||||
|  | 	// as UInt16 values and not UInt32 values. Use of the older format on the Mac OS is discouraged | ||||||
|  | 	// (although AAT can sense an old kerning table and still make correct use of it). Microsoft | ||||||
|  | 	// Windows still uses the older format for the 'kern' table and will not recognize the newer one. | ||||||
|  | 	// Fonts targeted for the Mac OS only should use the new format; fonts targeted for both the Mac OS | ||||||
|  | 	// and Windows should use the old format." | ||||||
|  | 	// Since we expect that almost all fonts aim to be Windows-compatible, we only parse the "older" format, | ||||||
|  | 	// just like the C Freetype implementation. | ||||||
|  | 	if len(f.kern) == 0 { | ||||||
|  | 		if f.nKern != 0 { | ||||||
|  | 			return FormatError("bad kern table length") | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	if len(f.kern) < 18 { | ||||||
|  | 		return FormatError("kern data too short") | ||||||
|  | 	} | ||||||
|  | 	version, offset := u16(f.kern, 0), 2 | ||||||
|  | 	if version != 0 { | ||||||
|  | 		return UnsupportedError(fmt.Sprintf("kern version: %d", version)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	n, offset := u16(f.kern, offset), offset+2 | ||||||
|  | 	if n == 0 { | ||||||
|  | 		return UnsupportedError("kern nTables: 0") | ||||||
|  | 	} | ||||||
|  | 	// TODO: support multiple subtables. In practice, almost all .ttf files | ||||||
|  | 	// have only one subtable, if they have a kern table at all. But it's not | ||||||
|  | 	// impossible. Xolonium Regular (https://fontlibrary.org/en/font/xolonium) | ||||||
|  | 	// has 3 subtables. Those subtables appear to be disjoint, rather than | ||||||
|  | 	// being the same kerning pairs encoded in three different ways. | ||||||
|  | 	// | ||||||
|  | 	// For now, we'll use only the first subtable. | ||||||
|  |  | ||||||
|  | 	offset += 2 // Skip the version. | ||||||
|  | 	length, offset := int(u16(f.kern, offset)), offset+2 | ||||||
|  | 	coverage, offset := u16(f.kern, offset), offset+2 | ||||||
|  | 	if coverage != 0x0001 { | ||||||
|  | 		// We only support horizontal kerning. | ||||||
|  | 		return UnsupportedError(fmt.Sprintf("kern coverage: 0x%04x", coverage)) | ||||||
|  | 	} | ||||||
|  | 	f.nKern, offset = int(u16(f.kern, offset)), offset+2 | ||||||
|  | 	if 6*f.nKern != length-14 { | ||||||
|  | 		return FormatError("bad kern table length") | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Font) parseMaxp() error { | ||||||
|  | 	if len(f.maxp) != 32 { | ||||||
|  | 		return FormatError(fmt.Sprintf("bad maxp length: %d", len(f.maxp))) | ||||||
|  | 	} | ||||||
|  | 	f.nGlyph = int(u16(f.maxp, 4)) | ||||||
|  | 	f.maxTwilightPoints = u16(f.maxp, 16) | ||||||
|  | 	f.maxStorage = u16(f.maxp, 18) | ||||||
|  | 	f.maxFunctionDefs = u16(f.maxp, 20) | ||||||
|  | 	f.maxStackElements = u16(f.maxp, 24) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer. | ||||||
|  | func (f *Font) scale(x fixed.Int26_6) fixed.Int26_6 { | ||||||
|  | 	if x >= 0 { | ||||||
|  | 		x += fixed.Int26_6(f.fUnitsPerEm) / 2 | ||||||
|  | 	} else { | ||||||
|  | 		x -= fixed.Int26_6(f.fUnitsPerEm) / 2 | ||||||
|  | 	} | ||||||
|  | 	return x / fixed.Int26_6(f.fUnitsPerEm) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Bounds returns the union of a Font's glyphs' bounds. | ||||||
|  | func (f *Font) Bounds(scale fixed.Int26_6) fixed.Rectangle26_6 { | ||||||
|  | 	b := f.bounds | ||||||
|  | 	b.Min.X = f.scale(scale * b.Min.X) | ||||||
|  | 	b.Min.Y = f.scale(scale * b.Min.Y) | ||||||
|  | 	b.Max.X = f.scale(scale * b.Max.X) | ||||||
|  | 	b.Max.Y = f.scale(scale * b.Max.Y) | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // FUnitsPerEm returns the number of FUnits in a Font's em-square's side. | ||||||
|  | func (f *Font) FUnitsPerEm() int32 { | ||||||
|  | 	return f.fUnitsPerEm | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Index returns a Font's index for the given rune. | ||||||
|  | func (f *Font) Index(x rune) Index { | ||||||
|  | 	c := uint32(x) | ||||||
|  | 	for i, j := 0, len(f.cm); i < j; { | ||||||
|  | 		h := i + (j-i)/2 | ||||||
|  | 		cm := &f.cm[h] | ||||||
|  | 		if c < cm.start { | ||||||
|  | 			j = h | ||||||
|  | 		} else if cm.end < c { | ||||||
|  | 			i = h + 1 | ||||||
|  | 		} else if cm.offset == 0 { | ||||||
|  | 			return Index(c + cm.delta) | ||||||
|  | 		} else { | ||||||
|  | 			offset := int(cm.offset) + 2*(h-len(f.cm)+int(c-cm.start)) | ||||||
|  | 			return Index(u16(f.cmapIndexes, offset)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Name returns the Font's name value for the given NameID. It returns "" if | ||||||
|  | // there was an error, or if that name was not found. | ||||||
|  | func (f *Font) Name(id NameID) string { | ||||||
|  | 	x, platformID, err := parseSubtables(f.name, "name", 6, 12, func(b []byte) bool { | ||||||
|  | 		return NameID(u16(b, 6)) == id | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 	offset, length := u16(f.name, 4)+u16(f.name, x+10), u16(f.name, x+8) | ||||||
|  | 	// Return the ASCII value of the encoded string. | ||||||
|  | 	// The string is encoded as UTF-16 on non-Apple platformIDs; Apple is platformID 1. | ||||||
|  | 	src := f.name[offset : offset+length] | ||||||
|  | 	var dst []byte | ||||||
|  | 	if platformID != 1 { // UTF-16. | ||||||
|  | 		if len(src)&1 != 0 { | ||||||
|  | 			return "" | ||||||
|  | 		} | ||||||
|  | 		dst = make([]byte, len(src)/2) | ||||||
|  | 		for i := range dst { | ||||||
|  | 			dst[i] = printable(u16(src, 2*i)) | ||||||
|  | 		} | ||||||
|  | 	} else { // ASCII. | ||||||
|  | 		dst = make([]byte, len(src)) | ||||||
|  | 		for i, c := range src { | ||||||
|  | 			dst[i] = printable(uint16(c)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return string(dst) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func printable(r uint16) byte { | ||||||
|  | 	if 0x20 <= r && r < 0x7f { | ||||||
|  | 		return byte(r) | ||||||
|  | 	} | ||||||
|  | 	return '?' | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // unscaledHMetric returns the unscaled horizontal metrics for the glyph with | ||||||
|  | // the given index. | ||||||
|  | func (f *Font) unscaledHMetric(i Index) (h HMetric) { | ||||||
|  | 	j := int(i) | ||||||
|  | 	if j < 0 || f.nGlyph <= j { | ||||||
|  | 		return HMetric{} | ||||||
|  | 	} | ||||||
|  | 	if j >= f.nHMetric { | ||||||
|  | 		p := 4 * (f.nHMetric - 1) | ||||||
|  | 		return HMetric{ | ||||||
|  | 			AdvanceWidth:    fixed.Int26_6(u16(f.hmtx, p)), | ||||||
|  | 			LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return HMetric{ | ||||||
|  | 		AdvanceWidth:    fixed.Int26_6(u16(f.hmtx, 4*j)), | ||||||
|  | 		LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, 4*j+2))), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // HMetric returns the horizontal metrics for the glyph with the given index. | ||||||
|  | func (f *Font) HMetric(scale fixed.Int26_6, i Index) HMetric { | ||||||
|  | 	h := f.unscaledHMetric(i) | ||||||
|  | 	h.AdvanceWidth = f.scale(scale * h.AdvanceWidth) | ||||||
|  | 	h.LeftSideBearing = f.scale(scale * h.LeftSideBearing) | ||||||
|  | 	return h | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // unscaledVMetric returns the unscaled vertical metrics for the glyph with | ||||||
|  | // the given index. yMax is the top of the glyph's bounding box. | ||||||
|  | func (f *Font) unscaledVMetric(i Index, yMax fixed.Int26_6) (v VMetric) { | ||||||
|  | 	j := int(i) | ||||||
|  | 	if j < 0 || f.nGlyph <= j { | ||||||
|  | 		return VMetric{} | ||||||
|  | 	} | ||||||
|  | 	if 4*j+4 <= len(f.vmtx) { | ||||||
|  | 		return VMetric{ | ||||||
|  | 			AdvanceHeight:  fixed.Int26_6(u16(f.vmtx, 4*j)), | ||||||
|  | 			TopSideBearing: fixed.Int26_6(int16(u16(f.vmtx, 4*j+2))), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// The OS/2 table has grown over time. | ||||||
|  | 	// https://developer.apple.com/fonts/TTRefMan/RM06/Chap6OS2.html | ||||||
|  | 	// says that it was originally 68 bytes. Optional fields, including | ||||||
|  | 	// the ascender and descender, are described at | ||||||
|  | 	// http://www.microsoft.com/typography/otspec/os2.htm | ||||||
|  | 	if len(f.os2) >= 72 { | ||||||
|  | 		sTypoAscender := fixed.Int26_6(int16(u16(f.os2, 68))) | ||||||
|  | 		sTypoDescender := fixed.Int26_6(int16(u16(f.os2, 70))) | ||||||
|  | 		return VMetric{ | ||||||
|  | 			AdvanceHeight:  sTypoAscender - sTypoDescender, | ||||||
|  | 			TopSideBearing: sTypoAscender - yMax, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return VMetric{ | ||||||
|  | 		AdvanceHeight:  fixed.Int26_6(f.fUnitsPerEm), | ||||||
|  | 		TopSideBearing: 0, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // VMetric returns the vertical metrics for the glyph with the given index. | ||||||
|  | func (f *Font) VMetric(scale fixed.Int26_6, i Index) VMetric { | ||||||
|  | 	// TODO: should 0 be bounds.YMax? | ||||||
|  | 	v := f.unscaledVMetric(i, 0) | ||||||
|  | 	v.AdvanceHeight = f.scale(scale * v.AdvanceHeight) | ||||||
|  | 	v.TopSideBearing = f.scale(scale * v.TopSideBearing) | ||||||
|  | 	return v | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Kern returns the horizontal adjustment for the given glyph pair. A positive | ||||||
|  | // kern means to move the glyphs further apart. | ||||||
|  | func (f *Font) Kern(scale fixed.Int26_6, i0, i1 Index) fixed.Int26_6 { | ||||||
|  | 	if f.nKern == 0 { | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  | 	g := uint32(i0)<<16 | uint32(i1) | ||||||
|  | 	lo, hi := 0, f.nKern | ||||||
|  | 	for lo < hi { | ||||||
|  | 		i := (lo + hi) / 2 | ||||||
|  | 		ig := u32(f.kern, 18+6*i) | ||||||
|  | 		if ig < g { | ||||||
|  | 			lo = i + 1 | ||||||
|  | 		} else if ig > g { | ||||||
|  | 			hi = i | ||||||
|  | 		} else { | ||||||
|  | 			return f.scale(scale * fixed.Int26_6(int16(u16(f.kern, 22+6*i)))) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Parse returns a new Font for the given TTF or TTC data. | ||||||
|  | // | ||||||
|  | // For TrueType Collections, the first font in the collection is parsed. | ||||||
|  | func Parse(ttf []byte) (font *Font, err error) { | ||||||
|  | 	return parse(ttf, 0) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func parse(ttf []byte, offset int) (font *Font, err error) { | ||||||
|  | 	if len(ttf)-offset < 12 { | ||||||
|  | 		err = FormatError("TTF data is too short") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	originalOffset := offset | ||||||
|  | 	magic, offset := u32(ttf, offset), offset+4 | ||||||
|  | 	switch magic { | ||||||
|  | 	case 0x00010000: | ||||||
|  | 		// No-op. | ||||||
|  | 	case 0x74746366: // "ttcf" as a big-endian uint32. | ||||||
|  | 		if originalOffset != 0 { | ||||||
|  | 			err = FormatError("recursive TTC") | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		ttcVersion, offset := u32(ttf, offset), offset+4 | ||||||
|  | 		if ttcVersion != 0x00010000 && ttcVersion != 0x00020000 { | ||||||
|  | 			err = FormatError("bad TTC version") | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		numFonts, offset := int(u32(ttf, offset)), offset+4 | ||||||
|  | 		if numFonts <= 0 { | ||||||
|  | 			err = FormatError("bad number of TTC fonts") | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if len(ttf[offset:])/4 < numFonts { | ||||||
|  | 			err = FormatError("TTC offset table is too short") | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		// TODO: provide an API to select which font in a TrueType collection to return, | ||||||
|  | 		// not just the first one. This may require an API to parse a TTC's name tables, | ||||||
|  | 		// so users of this package can select the font in a TTC by name. | ||||||
|  | 		offset = int(u32(ttf, offset)) | ||||||
|  | 		if offset <= 0 || offset > len(ttf) { | ||||||
|  | 			err = FormatError("bad TTC offset") | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		return parse(ttf, offset) | ||||||
|  | 	default: | ||||||
|  | 		err = FormatError("bad TTF version") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	n, offset := int(u16(ttf, offset)), offset+2 | ||||||
|  | 	offset += 6 // Skip the searchRange, entrySelector and rangeShift. | ||||||
|  | 	if len(ttf) < 16*n+offset { | ||||||
|  | 		err = FormatError("TTF data is too short") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	f := new(Font) | ||||||
|  | 	// Assign the table slices. | ||||||
|  | 	for i := 0; i < n; i++ { | ||||||
|  | 		x := 16*i + offset | ||||||
|  | 		switch string(ttf[x : x+4]) { | ||||||
|  | 		case "cmap": | ||||||
|  | 			f.cmap, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		case "cvt ": | ||||||
|  | 			f.cvt, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		case "fpgm": | ||||||
|  | 			f.fpgm, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		case "glyf": | ||||||
|  | 			f.glyf, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		case "hdmx": | ||||||
|  | 			f.hdmx, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		case "head": | ||||||
|  | 			f.head, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		case "hhea": | ||||||
|  | 			f.hhea, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		case "hmtx": | ||||||
|  | 			f.hmtx, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		case "kern": | ||||||
|  | 			f.kern, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		case "loca": | ||||||
|  | 			f.loca, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		case "maxp": | ||||||
|  | 			f.maxp, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		case "name": | ||||||
|  | 			f.name, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		case "OS/2": | ||||||
|  | 			f.os2, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		case "prep": | ||||||
|  | 			f.prep, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		case "vmtx": | ||||||
|  | 			f.vmtx, err = readTable(ttf, ttf[x+8:x+16]) | ||||||
|  | 		} | ||||||
|  | 		if err != nil { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// Parse and sanity-check the TTF data. | ||||||
|  | 	if err = f.parseHead(); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if err = f.parseMaxp(); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if err = f.parseCmap(); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if err = f.parseKern(); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if err = f.parseHhea(); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	font = f | ||||||
|  | 	return | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								vendor/golang.org/x/image/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								vendor/golang.org/x/image/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | # This source code refers to The Go Authors for copyright purposes. | ||||||
|  | # The master list of authors is in the main Go distribution, | ||||||
|  | # visible at http://tip.golang.org/AUTHORS. | ||||||
							
								
								
									
										3
									
								
								vendor/golang.org/x/image/CONTRIBUTORS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								vendor/golang.org/x/image/CONTRIBUTORS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | # This source code was written by the Go contributors. | ||||||
|  | # The master list of contributors is in the main Go distribution, | ||||||
|  | # visible at http://tip.golang.org/CONTRIBUTORS. | ||||||
							
								
								
									
										27
									
								
								vendor/golang.org/x/image/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/golang.org/x/image/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | Copyright (c) 2009 The Go Authors. All rights reserved. | ||||||
|  |  | ||||||
|  | Redistribution and use in source and binary forms, with or without | ||||||
|  | modification, are permitted provided that the following conditions are | ||||||
|  | met: | ||||||
|  |  | ||||||
|  |    * Redistributions of source code must retain the above copyright | ||||||
|  | notice, this list of conditions and the following disclaimer. | ||||||
|  |    * Redistributions in binary form must reproduce the above | ||||||
|  | copyright notice, this list of conditions and the following disclaimer | ||||||
|  | in the documentation and/or other materials provided with the | ||||||
|  | distribution. | ||||||
|  |    * Neither the name of Google Inc. nor the names of its | ||||||
|  | contributors may be used to endorse or promote products derived from | ||||||
|  | this software without specific prior written permission. | ||||||
|  |  | ||||||
|  | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||||
|  | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||||
|  | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||||
|  | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||||
|  | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||||
|  | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||||
|  | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||||
|  | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||||
|  | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
							
								
								
									
										22
									
								
								vendor/golang.org/x/image/PATENTS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/golang.org/x/image/PATENTS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | Additional IP Rights Grant (Patents) | ||||||
|  |  | ||||||
|  | "This implementation" means the copyrightable works distributed by | ||||||
|  | Google as part of the Go project. | ||||||
|  |  | ||||||
|  | Google hereby grants to You a perpetual, worldwide, non-exclusive, | ||||||
|  | no-charge, royalty-free, irrevocable (except as stated in this section) | ||||||
|  | patent license to make, have made, use, offer to sell, sell, import, | ||||||
|  | transfer and otherwise run, modify and propagate the contents of this | ||||||
|  | implementation of Go, where such license applies only to those patent | ||||||
|  | claims, both currently owned or controlled by Google and acquired in | ||||||
|  | the future, licensable by Google that are necessarily infringed by this | ||||||
|  | implementation of Go.  This grant does not include claims that would be | ||||||
|  | infringed only as a consequence of further modification of this | ||||||
|  | implementation.  If you or your agent or exclusive licensee institute or | ||||||
|  | order or agree to the institution of patent litigation against any | ||||||
|  | entity (including a cross-claim or counterclaim in a lawsuit) alleging | ||||||
|  | that this implementation of Go or any code incorporated within this | ||||||
|  | implementation of Go constitutes direct or contributory patent | ||||||
|  | infringement, or inducement of patent infringement, then any patent | ||||||
|  | rights granted to you under this License for this implementation of Go | ||||||
|  | shall terminate as of the date such litigation is filed. | ||||||
							
								
								
									
										126
									
								
								vendor/golang.org/x/image/font/basicfont/basicfont.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								vendor/golang.org/x/image/font/basicfont/basicfont.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | |||||||
|  | // Copyright 2015 The Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | //go:generate go run gen.go | ||||||
|  |  | ||||||
|  | // Package basicfont provides fixed-size font faces. | ||||||
|  | package basicfont // import "golang.org/x/image/font/basicfont" | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"image" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/image/font" | ||||||
|  | 	"golang.org/x/image/math/fixed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Range maps a contiguous range of runes to vertically adjacent sub-images of | ||||||
|  | // a Face's Mask image. The rune range is inclusive on the low end and | ||||||
|  | // exclusive on the high end. | ||||||
|  | // | ||||||
|  | // If Low <= r && r < High, then the rune r is mapped to the sub-image of | ||||||
|  | // Face.Mask whose bounds are image.Rect(0, y*h, Face.Width, (y+1)*h), | ||||||
|  | // where y = (int(r-Low) + Offset) and h = (Face.Ascent + Face.Descent). | ||||||
|  | type Range struct { | ||||||
|  | 	Low, High rune | ||||||
|  | 	Offset    int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Face7x13 is a Face derived from the public domain X11 misc-fixed font files. | ||||||
|  | // | ||||||
|  | // At the moment, it holds the printable characters in ASCII starting with | ||||||
|  | // space, and the Unicode replacement character U+FFFD. | ||||||
|  | // | ||||||
|  | // Its data is entirely self-contained and does not require loading from | ||||||
|  | // separate files. | ||||||
|  | var Face7x13 = &Face{ | ||||||
|  | 	Advance: 7, | ||||||
|  | 	Width:   6, | ||||||
|  | 	Height:  13, | ||||||
|  | 	Ascent:  11, | ||||||
|  | 	Descent: 2, | ||||||
|  | 	Mask:    mask7x13, | ||||||
|  | 	Ranges: []Range{ | ||||||
|  | 		{'\u0020', '\u007f', 0}, | ||||||
|  | 		{'\ufffd', '\ufffe', 95}, | ||||||
|  | 	}, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Face is a basic font face whose glyphs all have the same metrics. | ||||||
|  | // | ||||||
|  | // It is safe to use concurrently. | ||||||
|  | type Face struct { | ||||||
|  | 	// Advance is the glyph advance, in pixels. | ||||||
|  | 	Advance int | ||||||
|  | 	// Width is the glyph width, in pixels. | ||||||
|  | 	Width int | ||||||
|  | 	// Height is the inter-line height, in pixels. | ||||||
|  | 	Height int | ||||||
|  | 	// Ascent is the glyph ascent, in pixels. | ||||||
|  | 	Ascent int | ||||||
|  | 	// Descent is the glyph descent, in pixels. | ||||||
|  | 	Descent int | ||||||
|  | 	// Left is the left side bearing, in pixels. A positive value means that | ||||||
|  | 	// all of a glyph is to the right of the dot. | ||||||
|  | 	Left int | ||||||
|  |  | ||||||
|  | 	// Mask contains all of the glyph masks. Its width is typically the Face's | ||||||
|  | 	// Width, and its height a multiple of the Face's Height. | ||||||
|  | 	Mask image.Image | ||||||
|  | 	// Ranges map runes to sub-images of Mask. The rune ranges must not | ||||||
|  | 	// overlap, and must be in increasing rune order. | ||||||
|  | 	Ranges []Range | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Face) Close() error                   { return nil } | ||||||
|  | func (f *Face) Kern(r0, r1 rune) fixed.Int26_6 { return 0 } | ||||||
|  |  | ||||||
|  | func (f *Face) Metrics() font.Metrics { | ||||||
|  | 	return font.Metrics{ | ||||||
|  | 		Height:  fixed.I(f.Height), | ||||||
|  | 		Ascent:  fixed.I(f.Ascent), | ||||||
|  | 		Descent: fixed.I(f.Descent), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Face) Glyph(dot fixed.Point26_6, r rune) ( | ||||||
|  | 	dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) { | ||||||
|  |  | ||||||
|  | loop: | ||||||
|  | 	for _, rr := range [2]rune{r, '\ufffd'} { | ||||||
|  | 		for _, rng := range f.Ranges { | ||||||
|  | 			if rr < rng.Low || rng.High <= rr { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			maskp.Y = (int(rr-rng.Low) + rng.Offset) * (f.Ascent + f.Descent) | ||||||
|  | 			ok = true | ||||||
|  | 			break loop | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !ok { | ||||||
|  | 		return image.Rectangle{}, nil, image.Point{}, 0, false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	x := int(dot.X+32)>>6 + f.Left | ||||||
|  | 	y := int(dot.Y+32) >> 6 | ||||||
|  | 	dr = image.Rectangle{ | ||||||
|  | 		Min: image.Point{ | ||||||
|  | 			X: x, | ||||||
|  | 			Y: y - f.Ascent, | ||||||
|  | 		}, | ||||||
|  | 		Max: image.Point{ | ||||||
|  | 			X: x + f.Width, | ||||||
|  | 			Y: y + f.Descent, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return dr, f.Mask, maskp, fixed.I(f.Advance), true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { | ||||||
|  | 	return fixed.R(0, -f.Ascent, f.Width, +f.Descent), fixed.I(f.Advance), true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *Face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { | ||||||
|  | 	return fixed.I(f.Advance), true | ||||||
|  | } | ||||||
							
								
								
									
										1456
									
								
								vendor/golang.org/x/image/font/basicfont/data.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1456
									
								
								vendor/golang.org/x/image/font/basicfont/data.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										115
									
								
								vendor/golang.org/x/image/font/basicfont/gen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								vendor/golang.org/x/image/font/basicfont/gen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | |||||||
|  | // Copyright 2015 The Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | // +build ignore | ||||||
|  |  | ||||||
|  | // This program generates data.go. | ||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"fmt" | ||||||
|  | 	"go/format" | ||||||
|  | 	"image" | ||||||
|  | 	"image/draw" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"log" | ||||||
|  | 	"path" | ||||||
|  | 	"path/filepath" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/image/font" | ||||||
|  | 	"golang.org/x/image/font/plan9font" | ||||||
|  | 	"golang.org/x/image/math/fixed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	// nGlyphs is the number of glyphs to generate: 95 characters in the range | ||||||
|  | 	// [0x20, 0x7e], plus the replacement character. | ||||||
|  | 	const nGlyphs = 95 + 1 | ||||||
|  | 	// The particular font (unicode.7x13.font) leaves the right-most column | ||||||
|  | 	// empty in its ASCII glyphs. We don't have to include that column in the | ||||||
|  | 	// generated glyphs, so we subtract one off the effective width. | ||||||
|  | 	const width, height, ascent = 7 - 1, 13, 11 | ||||||
|  |  | ||||||
|  | 	readFile := func(name string) ([]byte, error) { | ||||||
|  | 		return ioutil.ReadFile(filepath.FromSlash(path.Join("../testdata/fixed", name))) | ||||||
|  | 	} | ||||||
|  | 	fontData, err := readFile("unicode.7x13.font") | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("readFile: %v", err) | ||||||
|  | 	} | ||||||
|  | 	face, err := plan9font.ParseFont(fontData, readFile) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("plan9font.ParseFont: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	dst := image.NewRGBA(image.Rect(0, 0, width, nGlyphs*height)) | ||||||
|  | 	draw.Draw(dst, dst.Bounds(), image.Black, image.Point{}, draw.Src) | ||||||
|  | 	d := &font.Drawer{ | ||||||
|  | 		Dst:  dst, | ||||||
|  | 		Src:  image.White, | ||||||
|  | 		Face: face, | ||||||
|  | 	} | ||||||
|  | 	for i := 0; i < nGlyphs; i++ { | ||||||
|  | 		r := '\ufffd' | ||||||
|  | 		if i < nGlyphs-1 { | ||||||
|  | 			r = 0x20 + rune(i) | ||||||
|  | 		} | ||||||
|  | 		d.Dot = fixed.P(0, height*i+ascent) | ||||||
|  | 		d.DrawString(string(r)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	w := bytes.NewBuffer(nil) | ||||||
|  | 	w.WriteString(preamble) | ||||||
|  | 	fmt.Fprintf(w, "// mask7x13 contains %d %d×%d glyphs in %d Pix bytes.\n", nGlyphs, width, height, nGlyphs*width*height) | ||||||
|  | 	fmt.Fprintf(w, "var mask7x13 = &image.Alpha{\n") | ||||||
|  | 	fmt.Fprintf(w, "  Stride: %d,\n", width) | ||||||
|  | 	fmt.Fprintf(w, "  Rect: image.Rectangle{Max: image.Point{%d, %d*%d}},\n", width, nGlyphs, height) | ||||||
|  | 	fmt.Fprintf(w, "  Pix: []byte{\n") | ||||||
|  | 	b := dst.Bounds() | ||||||
|  | 	for y := b.Min.Y; y < b.Max.Y; y++ { | ||||||
|  | 		if y%height == 0 { | ||||||
|  | 			if y != 0 { | ||||||
|  | 				w.WriteByte('\n') | ||||||
|  | 			} | ||||||
|  | 			i := y / height | ||||||
|  | 			if i < nGlyphs-1 { | ||||||
|  | 				i += 0x20 | ||||||
|  | 				fmt.Fprintf(w, "// %#2x %q\n", i, rune(i)) | ||||||
|  | 			} else { | ||||||
|  | 				fmt.Fprintf(w, "// U+FFFD REPLACEMENT CHARACTER\n") | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		for x := b.Min.X; x < b.Max.X; x++ { | ||||||
|  | 			if dst.RGBAAt(x, y).R > 0 { | ||||||
|  | 				w.WriteString("0xff,") | ||||||
|  | 			} else { | ||||||
|  | 				w.WriteString("0x00,") | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		w.WriteByte('\n') | ||||||
|  | 	} | ||||||
|  | 	w.WriteString("},\n}\n") | ||||||
|  |  | ||||||
|  | 	fmted, err := format.Source(w.Bytes()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("format.Source: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if err := ioutil.WriteFile("data.go", fmted, 0644); err != nil { | ||||||
|  | 		log.Fatalf("ioutil.WriteFile: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const preamble = `// generated by go generate; DO NOT EDIT. | ||||||
|  |  | ||||||
|  | package basicfont | ||||||
|  |  | ||||||
|  | // This data is derived from files in the font/fixed directory of the Plan 9 | ||||||
|  | // Port source code (https://github.com/9fans/plan9port) which were originally | ||||||
|  | // based on the public domain X11 misc-fixed font files. | ||||||
|  |  | ||||||
|  | import "image" | ||||||
|  |  | ||||||
|  | ` | ||||||
							
								
								
									
										359
									
								
								vendor/golang.org/x/image/font/font.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								vendor/golang.org/x/image/font/font.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,359 @@ | |||||||
|  | // Copyright 2015 The Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | // Package font defines an interface for font faces, for drawing text on an | ||||||
|  | // image. | ||||||
|  | // | ||||||
|  | // Other packages provide font face implementations. For example, a truetype | ||||||
|  | // package would provide one based on .ttf font files. | ||||||
|  | package font // import "golang.org/x/image/font" | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"image" | ||||||
|  | 	"image/draw" | ||||||
|  | 	"io" | ||||||
|  | 	"unicode/utf8" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/image/math/fixed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // TODO: who is responsible for caches (glyph images, glyph indices, kerns)? | ||||||
|  | // The Drawer or the Face? | ||||||
|  |  | ||||||
|  | // Face is a font face. Its glyphs are often derived from a font file, such as | ||||||
|  | // "Comic_Sans_MS.ttf", but a face has a specific size, style, weight and | ||||||
|  | // hinting. For example, the 12pt and 18pt versions of Comic Sans are two | ||||||
|  | // different faces, even if derived from the same font file. | ||||||
|  | // | ||||||
|  | // A Face is not safe for concurrent use by multiple goroutines, as its methods | ||||||
|  | // may re-use implementation-specific caches and mask image buffers. | ||||||
|  | // | ||||||
|  | // To create a Face, look to other packages that implement specific font file | ||||||
|  | // formats. | ||||||
|  | type Face interface { | ||||||
|  | 	io.Closer | ||||||
|  |  | ||||||
|  | 	// Glyph returns the draw.DrawMask parameters (dr, mask, maskp) to draw r's | ||||||
|  | 	// glyph at the sub-pixel destination location dot, and that glyph's | ||||||
|  | 	// advance width. | ||||||
|  | 	// | ||||||
|  | 	// It returns !ok if the face does not contain a glyph for r. | ||||||
|  | 	// | ||||||
|  | 	// The contents of the mask image returned by one Glyph call may change | ||||||
|  | 	// after the next Glyph call. Callers that want to cache the mask must make | ||||||
|  | 	// a copy. | ||||||
|  | 	Glyph(dot fixed.Point26_6, r rune) ( | ||||||
|  | 		dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) | ||||||
|  |  | ||||||
|  | 	// GlyphBounds returns the bounding box of r's glyph, drawn at a dot equal | ||||||
|  | 	// to the origin, and that glyph's advance width. | ||||||
|  | 	// | ||||||
|  | 	// It returns !ok if the face does not contain a glyph for r. | ||||||
|  | 	// | ||||||
|  | 	// The glyph's ascent and descent equal -bounds.Min.Y and +bounds.Max.Y. A | ||||||
|  | 	// visual depiction of what these metrics are is at | ||||||
|  | 	// https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png | ||||||
|  | 	GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) | ||||||
|  |  | ||||||
|  | 	// GlyphAdvance returns the advance width of r's glyph. | ||||||
|  | 	// | ||||||
|  | 	// It returns !ok if the face does not contain a glyph for r. | ||||||
|  | 	GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) | ||||||
|  |  | ||||||
|  | 	// Kern returns the horizontal adjustment for the kerning pair (r0, r1). A | ||||||
|  | 	// positive kern means to move the glyphs further apart. | ||||||
|  | 	Kern(r0, r1 rune) fixed.Int26_6 | ||||||
|  |  | ||||||
|  | 	// Metrics returns the metrics for this Face. | ||||||
|  | 	Metrics() Metrics | ||||||
|  |  | ||||||
|  | 	// TODO: ColoredGlyph for various emoji? | ||||||
|  | 	// TODO: Ligatures? Shaping? | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Metrics holds the metrics for a Face. A visual depiction is at | ||||||
|  | // https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png | ||||||
|  | type Metrics struct { | ||||||
|  | 	// Height is the recommended amount of vertical space between two lines of | ||||||
|  | 	// text. | ||||||
|  | 	Height fixed.Int26_6 | ||||||
|  |  | ||||||
|  | 	// Ascent is the distance from the top of a line to its baseline. | ||||||
|  | 	Ascent fixed.Int26_6 | ||||||
|  |  | ||||||
|  | 	// Descent is the distance from the bottom of a line to its baseline. The | ||||||
|  | 	// value is typically positive, even though a descender goes below the | ||||||
|  | 	// baseline. | ||||||
|  | 	Descent fixed.Int26_6 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Drawer draws text on a destination image. | ||||||
|  | // | ||||||
|  | // A Drawer is not safe for concurrent use by multiple goroutines, since its | ||||||
|  | // Face is not. | ||||||
|  | type Drawer struct { | ||||||
|  | 	// Dst is the destination image. | ||||||
|  | 	Dst draw.Image | ||||||
|  | 	// Src is the source image. | ||||||
|  | 	Src image.Image | ||||||
|  | 	// Face provides the glyph mask images. | ||||||
|  | 	Face Face | ||||||
|  | 	// Dot is the baseline location to draw the next glyph. The majority of the | ||||||
|  | 	// affected pixels will be above and to the right of the dot, but some may | ||||||
|  | 	// be below or to the left. For example, drawing a 'j' in an italic face | ||||||
|  | 	// may affect pixels below and to the left of the dot. | ||||||
|  | 	Dot fixed.Point26_6 | ||||||
|  |  | ||||||
|  | 	// TODO: Clip image.Image? | ||||||
|  | 	// TODO: SrcP image.Point for Src images other than *image.Uniform? How | ||||||
|  | 	// does it get updated during DrawString? | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TODO: should DrawString return the last rune drawn, so the next DrawString | ||||||
|  | // call can kern beforehand? Or should that be the responsibility of the caller | ||||||
|  | // if they really want to do that, since they have to explicitly shift d.Dot | ||||||
|  | // anyway? What if ligatures span more than two runes? What if grapheme | ||||||
|  | // clusters span multiple runes? | ||||||
|  | // | ||||||
|  | // TODO: do we assume that the input is in any particular Unicode Normalization | ||||||
|  | // Form? | ||||||
|  | // | ||||||
|  | // TODO: have DrawRunes(s []rune)? DrawRuneReader(io.RuneReader)?? If we take | ||||||
|  | // io.RuneReader, we can't assume that we can rewind the stream. | ||||||
|  | // | ||||||
|  | // TODO: how does this work with line breaking: drawing text up until a | ||||||
|  | // vertical line? Should DrawString return the number of runes drawn? | ||||||
|  |  | ||||||
|  | // DrawBytes draws s at the dot and advances the dot's location. | ||||||
|  | // | ||||||
|  | // It is equivalent to DrawString(string(s)) but may be more efficient. | ||||||
|  | func (d *Drawer) DrawBytes(s []byte) { | ||||||
|  | 	prevC := rune(-1) | ||||||
|  | 	for len(s) > 0 { | ||||||
|  | 		c, size := utf8.DecodeRune(s) | ||||||
|  | 		s = s[size:] | ||||||
|  | 		if prevC >= 0 { | ||||||
|  | 			d.Dot.X += d.Face.Kern(prevC, c) | ||||||
|  | 		} | ||||||
|  | 		dr, mask, maskp, advance, ok := d.Face.Glyph(d.Dot, c) | ||||||
|  | 		if !ok { | ||||||
|  | 			// TODO: is falling back on the U+FFFD glyph the responsibility of | ||||||
|  | 			// the Drawer or the Face? | ||||||
|  | 			// TODO: set prevC = '\ufffd'? | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over) | ||||||
|  | 		d.Dot.X += advance | ||||||
|  | 		prevC = c | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DrawString draws s at the dot and advances the dot's location. | ||||||
|  | func (d *Drawer) DrawString(s string) { | ||||||
|  | 	prevC := rune(-1) | ||||||
|  | 	for _, c := range s { | ||||||
|  | 		if prevC >= 0 { | ||||||
|  | 			d.Dot.X += d.Face.Kern(prevC, c) | ||||||
|  | 		} | ||||||
|  | 		dr, mask, maskp, advance, ok := d.Face.Glyph(d.Dot, c) | ||||||
|  | 		if !ok { | ||||||
|  | 			// TODO: is falling back on the U+FFFD glyph the responsibility of | ||||||
|  | 			// the Drawer or the Face? | ||||||
|  | 			// TODO: set prevC = '\ufffd'? | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over) | ||||||
|  | 		d.Dot.X += advance | ||||||
|  | 		prevC = c | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BoundBytes returns the bounding box of s, drawn at the drawer dot, as well as | ||||||
|  | // the advance. | ||||||
|  | // | ||||||
|  | // It is equivalent to BoundBytes(string(s)) but may be more efficient. | ||||||
|  | func (d *Drawer) BoundBytes(s []byte) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) { | ||||||
|  | 	bounds, advance = BoundBytes(d.Face, s) | ||||||
|  | 	bounds.Min = bounds.Min.Add(d.Dot) | ||||||
|  | 	bounds.Max = bounds.Max.Add(d.Dot) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BoundString returns the bounding box of s, drawn at the drawer dot, as well | ||||||
|  | // as the advance. | ||||||
|  | func (d *Drawer) BoundString(s string) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) { | ||||||
|  | 	bounds, advance = BoundString(d.Face, s) | ||||||
|  | 	bounds.Min = bounds.Min.Add(d.Dot) | ||||||
|  | 	bounds.Max = bounds.Max.Add(d.Dot) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MeasureBytes returns how far dot would advance by drawing s. | ||||||
|  | // | ||||||
|  | // It is equivalent to MeasureString(string(s)) but may be more efficient. | ||||||
|  | func (d *Drawer) MeasureBytes(s []byte) (advance fixed.Int26_6) { | ||||||
|  | 	return MeasureBytes(d.Face, s) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MeasureString returns how far dot would advance by drawing s. | ||||||
|  | func (d *Drawer) MeasureString(s string) (advance fixed.Int26_6) { | ||||||
|  | 	return MeasureString(d.Face, s) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BoundBytes returns the bounding box of s with f, drawn at a dot equal to the | ||||||
|  | // origin, as well as the advance. | ||||||
|  | // | ||||||
|  | // It is equivalent to BoundString(string(s)) but may be more efficient. | ||||||
|  | func BoundBytes(f Face, s []byte) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) { | ||||||
|  | 	prevC := rune(-1) | ||||||
|  | 	for len(s) > 0 { | ||||||
|  | 		c, size := utf8.DecodeRune(s) | ||||||
|  | 		s = s[size:] | ||||||
|  | 		if prevC >= 0 { | ||||||
|  | 			advance += f.Kern(prevC, c) | ||||||
|  | 		} | ||||||
|  | 		b, a, ok := f.GlyphBounds(c) | ||||||
|  | 		if !ok { | ||||||
|  | 			// TODO: is falling back on the U+FFFD glyph the responsibility of | ||||||
|  | 			// the Drawer or the Face? | ||||||
|  | 			// TODO: set prevC = '\ufffd'? | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		b.Min.X += advance | ||||||
|  | 		b.Max.X += advance | ||||||
|  | 		bounds = bounds.Union(b) | ||||||
|  | 		advance += a | ||||||
|  | 		prevC = c | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BoundString returns the bounding box of s with f, drawn at a dot equal to the | ||||||
|  | // origin, as well as the advance. | ||||||
|  | func BoundString(f Face, s string) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) { | ||||||
|  | 	prevC := rune(-1) | ||||||
|  | 	for _, c := range s { | ||||||
|  | 		if prevC >= 0 { | ||||||
|  | 			advance += f.Kern(prevC, c) | ||||||
|  | 		} | ||||||
|  | 		b, a, ok := f.GlyphBounds(c) | ||||||
|  | 		if !ok { | ||||||
|  | 			// TODO: is falling back on the U+FFFD glyph the responsibility of | ||||||
|  | 			// the Drawer or the Face? | ||||||
|  | 			// TODO: set prevC = '\ufffd'? | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		b.Min.X += advance | ||||||
|  | 		b.Max.X += advance | ||||||
|  | 		bounds = bounds.Union(b) | ||||||
|  | 		advance += a | ||||||
|  | 		prevC = c | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MeasureBytes returns how far dot would advance by drawing s with f. | ||||||
|  | // | ||||||
|  | // It is equivalent to MeasureString(string(s)) but may be more efficient. | ||||||
|  | func MeasureBytes(f Face, s []byte) (advance fixed.Int26_6) { | ||||||
|  | 	prevC := rune(-1) | ||||||
|  | 	for len(s) > 0 { | ||||||
|  | 		c, size := utf8.DecodeRune(s) | ||||||
|  | 		s = s[size:] | ||||||
|  | 		if prevC >= 0 { | ||||||
|  | 			advance += f.Kern(prevC, c) | ||||||
|  | 		} | ||||||
|  | 		a, ok := f.GlyphAdvance(c) | ||||||
|  | 		if !ok { | ||||||
|  | 			// TODO: is falling back on the U+FFFD glyph the responsibility of | ||||||
|  | 			// the Drawer or the Face? | ||||||
|  | 			// TODO: set prevC = '\ufffd'? | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		advance += a | ||||||
|  | 		prevC = c | ||||||
|  | 	} | ||||||
|  | 	return advance | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MeasureString returns how far dot would advance by drawing s with f. | ||||||
|  | func MeasureString(f Face, s string) (advance fixed.Int26_6) { | ||||||
|  | 	prevC := rune(-1) | ||||||
|  | 	for _, c := range s { | ||||||
|  | 		if prevC >= 0 { | ||||||
|  | 			advance += f.Kern(prevC, c) | ||||||
|  | 		} | ||||||
|  | 		a, ok := f.GlyphAdvance(c) | ||||||
|  | 		if !ok { | ||||||
|  | 			// TODO: is falling back on the U+FFFD glyph the responsibility of | ||||||
|  | 			// the Drawer or the Face? | ||||||
|  | 			// TODO: set prevC = '\ufffd'? | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		advance += a | ||||||
|  | 		prevC = c | ||||||
|  | 	} | ||||||
|  | 	return advance | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Hinting selects how to quantize a vector font's glyph nodes. | ||||||
|  | // | ||||||
|  | // Not all fonts support hinting. | ||||||
|  | type Hinting int | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	HintingNone Hinting = iota | ||||||
|  | 	HintingVertical | ||||||
|  | 	HintingFull | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Stretch selects a normal, condensed, or expanded face. | ||||||
|  | // | ||||||
|  | // Not all fonts support stretches. | ||||||
|  | type Stretch int | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	StretchUltraCondensed Stretch = -4 | ||||||
|  | 	StretchExtraCondensed Stretch = -3 | ||||||
|  | 	StretchCondensed      Stretch = -2 | ||||||
|  | 	StretchSemiCondensed  Stretch = -1 | ||||||
|  | 	StretchNormal         Stretch = +0 | ||||||
|  | 	StretchSemiExpanded   Stretch = +1 | ||||||
|  | 	StretchExpanded       Stretch = +2 | ||||||
|  | 	StretchExtraExpanded  Stretch = +3 | ||||||
|  | 	StretchUltraExpanded  Stretch = +4 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Style selects a normal, italic, or oblique face. | ||||||
|  | // | ||||||
|  | // Not all fonts support styles. | ||||||
|  | type Style int | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	StyleNormal Style = iota | ||||||
|  | 	StyleItalic | ||||||
|  | 	StyleOblique | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Weight selects a normal, light or bold face. | ||||||
|  | // | ||||||
|  | // Not all fonts support weights. | ||||||
|  | // | ||||||
|  | // The named Weight constants (e.g. WeightBold) correspond to CSS' common | ||||||
|  | // weight names (e.g. "Bold"), but the numerical values differ, so that in Go, | ||||||
|  | // the zero value means to use a normal weight. For the CSS names and values, | ||||||
|  | // see https://developer.mozilla.org/en/docs/Web/CSS/font-weight | ||||||
|  | type Weight int | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	WeightThin       Weight = -3 // CSS font-weight value 100. | ||||||
|  | 	WeightExtraLight Weight = -2 // CSS font-weight value 200. | ||||||
|  | 	WeightLight      Weight = -1 // CSS font-weight value 300. | ||||||
|  | 	WeightNormal     Weight = +0 // CSS font-weight value 400. | ||||||
|  | 	WeightMedium     Weight = +1 // CSS font-weight value 500. | ||||||
|  | 	WeightSemiBold   Weight = +2 // CSS font-weight value 600. | ||||||
|  | 	WeightBold       Weight = +3 // CSS font-weight value 700. | ||||||
|  | 	WeightExtraBold  Weight = +4 // CSS font-weight value 800. | ||||||
|  | 	WeightBlack      Weight = +5 // CSS font-weight value 900. | ||||||
|  | ) | ||||||
							
								
								
									
										610
									
								
								vendor/golang.org/x/image/font/plan9font/plan9font.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										610
									
								
								vendor/golang.org/x/image/font/plan9font/plan9font.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,610 @@ | |||||||
|  | // Copyright 2015 The Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | // Package plan9font implements font faces for the Plan 9 font and subfont file | ||||||
|  | // formats. These formats are described at | ||||||
|  | // http://plan9.bell-labs.com/magic/man2html/6/font | ||||||
|  | package plan9font // import "golang.org/x/image/font/plan9font" | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"image" | ||||||
|  | 	"image/color" | ||||||
|  | 	"log" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/image/font" | ||||||
|  | 	"golang.org/x/image/math/fixed" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // fontchar describes one character glyph in a subfont. | ||||||
|  | // | ||||||
|  | // For more detail, look for "struct Fontchar" in | ||||||
|  | // http://plan9.bell-labs.com/magic/man2html/2/cachechars | ||||||
|  | type fontchar struct { | ||||||
|  | 	x      uint32 // X position in the image holding the glyphs. | ||||||
|  | 	top    uint8  // First non-zero scan line. | ||||||
|  | 	bottom uint8  // Last non-zero scan line. | ||||||
|  | 	left   int8   // Offset of baseline. | ||||||
|  | 	width  uint8  // Width of baseline. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func parseFontchars(p []byte) []fontchar { | ||||||
|  | 	fc := make([]fontchar, len(p)/6) | ||||||
|  | 	for i := range fc { | ||||||
|  | 		fc[i] = fontchar{ | ||||||
|  | 			x:      uint32(p[0]) | uint32(p[1])<<8, | ||||||
|  | 			top:    uint8(p[2]), | ||||||
|  | 			bottom: uint8(p[3]), | ||||||
|  | 			left:   int8(p[4]), | ||||||
|  | 			width:  uint8(p[5]), | ||||||
|  | 		} | ||||||
|  | 		p = p[6:] | ||||||
|  | 	} | ||||||
|  | 	return fc | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // subface implements font.Face for a Plan 9 subfont. | ||||||
|  | type subface struct { | ||||||
|  | 	firstRune rune         // First rune in the subfont. | ||||||
|  | 	n         int          // Number of characters in the subfont. | ||||||
|  | 	height    int          // Inter-line spacing. | ||||||
|  | 	ascent    int          // Height above the baseline. | ||||||
|  | 	fontchars []fontchar   // Character descriptions. | ||||||
|  | 	img       *image.Alpha // Image holding the glyphs. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *subface) Close() error                   { return nil } | ||||||
|  | func (f *subface) Kern(r0, r1 rune) fixed.Int26_6 { return 0 } | ||||||
|  |  | ||||||
|  | func (f *subface) Metrics() font.Metrics { | ||||||
|  | 	return font.Metrics{ | ||||||
|  | 		Height:  fixed.I(f.height), | ||||||
|  | 		Ascent:  fixed.I(f.ascent), | ||||||
|  | 		Descent: fixed.I(f.height - f.ascent), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *subface) Glyph(dot fixed.Point26_6, r rune) ( | ||||||
|  | 	dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) { | ||||||
|  |  | ||||||
|  | 	r -= f.firstRune | ||||||
|  | 	if r < 0 || f.n <= int(r) { | ||||||
|  | 		return image.Rectangle{}, nil, image.Point{}, 0, false | ||||||
|  | 	} | ||||||
|  | 	i := &f.fontchars[r+0] | ||||||
|  | 	j := &f.fontchars[r+1] | ||||||
|  |  | ||||||
|  | 	minX := int(dot.X+32)>>6 + int(i.left) | ||||||
|  | 	minY := int(dot.Y+32)>>6 + int(i.top) - f.ascent | ||||||
|  | 	dr = image.Rectangle{ | ||||||
|  | 		Min: image.Point{ | ||||||
|  | 			X: minX, | ||||||
|  | 			Y: minY, | ||||||
|  | 		}, | ||||||
|  | 		Max: image.Point{ | ||||||
|  | 			X: minX + int(j.x-i.x), | ||||||
|  | 			Y: minY + int(i.bottom) - int(i.top), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	return dr, f.img, image.Point{int(i.x), int(i.top)}, fixed.Int26_6(i.width) << 6, true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *subface) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { | ||||||
|  | 	r -= f.firstRune | ||||||
|  | 	if r < 0 || f.n <= int(r) { | ||||||
|  | 		return fixed.Rectangle26_6{}, 0, false | ||||||
|  | 	} | ||||||
|  | 	i := &f.fontchars[r+0] | ||||||
|  | 	j := &f.fontchars[r+1] | ||||||
|  |  | ||||||
|  | 	bounds = fixed.R( | ||||||
|  | 		int(i.left), | ||||||
|  | 		int(i.top)-f.ascent, | ||||||
|  | 		int(i.left)+int(j.x-i.x), | ||||||
|  | 		int(i.bottom)-f.ascent, | ||||||
|  | 	) | ||||||
|  | 	return bounds, fixed.Int26_6(i.width) << 6, true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *subface) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { | ||||||
|  | 	r -= f.firstRune | ||||||
|  | 	if r < 0 || f.n <= int(r) { | ||||||
|  | 		return 0, false | ||||||
|  | 	} | ||||||
|  | 	return fixed.Int26_6(f.fontchars[r].width) << 6, true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // runeRange maps a single rune range [lo, hi] to a lazily loaded subface. Both | ||||||
|  | // ends of the range are inclusive. | ||||||
|  | type runeRange struct { | ||||||
|  | 	lo, hi      rune | ||||||
|  | 	offset      rune // subfont index that the lo rune maps to. | ||||||
|  | 	relFilename string | ||||||
|  | 	subface     *subface | ||||||
|  | 	bad         bool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // face implements font.Face for a Plan 9 font. | ||||||
|  | // | ||||||
|  | // It maps multiple rune ranges to *subface values. Rune ranges may overlap; | ||||||
|  | // the first match wins. | ||||||
|  | type face struct { | ||||||
|  | 	height     int | ||||||
|  | 	ascent     int | ||||||
|  | 	readFile   func(relFilename string) ([]byte, error) | ||||||
|  | 	runeRanges []runeRange | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *face) Close() error                   { return nil } | ||||||
|  | func (f *face) Kern(r0, r1 rune) fixed.Int26_6 { return 0 } | ||||||
|  |  | ||||||
|  | func (f *face) Metrics() font.Metrics { | ||||||
|  | 	return font.Metrics{ | ||||||
|  | 		Height:  fixed.I(f.height), | ||||||
|  | 		Ascent:  fixed.I(f.ascent), | ||||||
|  | 		Descent: fixed.I(f.height - f.ascent), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *face) Glyph(dot fixed.Point26_6, r rune) ( | ||||||
|  | 	dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) { | ||||||
|  |  | ||||||
|  | 	if s, rr := f.subface(r); s != nil { | ||||||
|  | 		return s.Glyph(dot, rr) | ||||||
|  | 	} | ||||||
|  | 	return image.Rectangle{}, nil, image.Point{}, 0, false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { | ||||||
|  | 	if s, rr := f.subface(r); s != nil { | ||||||
|  | 		return s.GlyphBounds(rr) | ||||||
|  | 	} | ||||||
|  | 	return fixed.Rectangle26_6{}, 0, false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { | ||||||
|  | 	if s, rr := f.subface(r); s != nil { | ||||||
|  | 		return s.GlyphAdvance(rr) | ||||||
|  | 	} | ||||||
|  | 	return 0, false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // For subfont files, if reading the given file name fails, we try appending | ||||||
|  | // ".n" where n is the log2 of the grayscale depth in bits (so at most 3) and | ||||||
|  | // then work down to 0. This was done in Plan 9 when antialiased fonts were | ||||||
|  | // introduced so that the 1-bit displays could keep using the 1-bit forms but | ||||||
|  | // higher depth displays could use the antialiased forms. | ||||||
|  | var subfontSuffixes = [...]string{ | ||||||
|  | 	"", | ||||||
|  | 	".3", | ||||||
|  | 	".2", | ||||||
|  | 	".1", | ||||||
|  | 	".0", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *face) readSubfontFile(name string) ([]byte, error) { | ||||||
|  | 	var firstErr error | ||||||
|  | 	for _, suffix := range subfontSuffixes { | ||||||
|  | 		if b, err := f.readFile(name + suffix); err == nil { | ||||||
|  | 			return b, nil | ||||||
|  | 		} else if firstErr == nil { | ||||||
|  | 			firstErr = err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil, firstErr | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *face) subface(r rune) (*subface, rune) { | ||||||
|  | 	// Fall back on U+FFFD if we can't find r. | ||||||
|  | 	for _, rr := range [2]rune{r, '\ufffd'} { | ||||||
|  | 		// We have to do linear, not binary search. plan9port's | ||||||
|  | 		// lucsans/unicode.8.font says: | ||||||
|  | 		//	0x2591  0x2593  ../luc/Altshades.7.0 | ||||||
|  | 		//	0x2500  0x25ee  ../luc/FormBlock.7.0 | ||||||
|  | 		// and the rune ranges overlap. | ||||||
|  | 		for i := range f.runeRanges { | ||||||
|  | 			x := &f.runeRanges[i] | ||||||
|  | 			if rr < x.lo || x.hi < rr || x.bad { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			if x.subface == nil { | ||||||
|  | 				data, err := f.readSubfontFile(x.relFilename) | ||||||
|  | 				if err != nil { | ||||||
|  | 					log.Printf("plan9font: couldn't read subfont %q: %v", x.relFilename, err) | ||||||
|  | 					x.bad = true | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				sub, err := ParseSubfont(data, x.lo-x.offset) | ||||||
|  | 				if err != nil { | ||||||
|  | 					log.Printf("plan9font: couldn't parse subfont %q: %v", x.relFilename, err) | ||||||
|  | 					x.bad = true | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				x.subface = sub.(*subface) | ||||||
|  | 			} | ||||||
|  | 			return x.subface, rr | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil, 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ParseFont parses a Plan 9 font file. data is the contents of that font file, | ||||||
|  | // which gives relative filenames for subfont files. readFile returns the | ||||||
|  | // contents of those subfont files. It is similar to io/ioutil's ReadFile | ||||||
|  | // function, except that it takes a relative filename instead of an absolute | ||||||
|  | // one. | ||||||
|  | func ParseFont(data []byte, readFile func(relFilename string) ([]byte, error)) (font.Face, error) { | ||||||
|  | 	f := &face{ | ||||||
|  | 		readFile: readFile, | ||||||
|  | 	} | ||||||
|  | 	// TODO: don't use strconv, to avoid the conversions from []byte to string? | ||||||
|  | 	for first := true; len(data) > 0; first = false { | ||||||
|  | 		i := bytes.IndexByte(data, '\n') | ||||||
|  | 		if i < 0 { | ||||||
|  | 			return nil, errors.New("plan9font: invalid font: no final newline") | ||||||
|  | 		} | ||||||
|  | 		row := string(data[:i]) | ||||||
|  | 		data = data[i+1:] | ||||||
|  | 		if first { | ||||||
|  | 			height, s, ok := nextInt32(row) | ||||||
|  | 			if !ok { | ||||||
|  | 				return nil, fmt.Errorf("plan9font: invalid font: invalid header %q", row) | ||||||
|  | 			} | ||||||
|  | 			ascent, s, ok := nextInt32(s) | ||||||
|  | 			if !ok { | ||||||
|  | 				return nil, fmt.Errorf("plan9font: invalid font: invalid header %q", row) | ||||||
|  | 			} | ||||||
|  | 			if height < 0 || 0xffff < height || ascent < 0 || 0xffff < ascent { | ||||||
|  | 				return nil, fmt.Errorf("plan9font: invalid font: invalid header %q", row) | ||||||
|  | 			} | ||||||
|  | 			f.height, f.ascent = int(height), int(ascent) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		lo, s, ok := nextInt32(row) | ||||||
|  | 		if !ok { | ||||||
|  | 			return nil, fmt.Errorf("plan9font: invalid font: invalid row %q", row) | ||||||
|  | 		} | ||||||
|  | 		hi, s, ok := nextInt32(s) | ||||||
|  | 		if !ok { | ||||||
|  | 			return nil, fmt.Errorf("plan9font: invalid font: invalid row %q", row) | ||||||
|  | 		} | ||||||
|  | 		offset, s, _ := nextInt32(s) | ||||||
|  |  | ||||||
|  | 		f.runeRanges = append(f.runeRanges, runeRange{ | ||||||
|  | 			lo:          lo, | ||||||
|  | 			hi:          hi, | ||||||
|  | 			offset:      offset, | ||||||
|  | 			relFilename: s, | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 	return f, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func nextInt32(s string) (ret int32, remaining string, ok bool) { | ||||||
|  | 	i := 0 | ||||||
|  | 	for ; i < len(s) && s[i] <= ' '; i++ { | ||||||
|  | 	} | ||||||
|  | 	j := i | ||||||
|  | 	for ; j < len(s) && s[j] > ' '; j++ { | ||||||
|  | 	} | ||||||
|  | 	n, err := strconv.ParseInt(s[i:j], 0, 32) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, s, false | ||||||
|  | 	} | ||||||
|  | 	for ; j < len(s) && s[j] <= ' '; j++ { | ||||||
|  | 	} | ||||||
|  | 	return int32(n), s[j:], true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ParseSubfont parses a Plan 9 subfont file. | ||||||
|  | // | ||||||
|  | // firstRune is the first rune in the subfont file. For example, the | ||||||
|  | // Phonetic.6.0 subfont, containing glyphs in the range U+0250 to U+02E9, would | ||||||
|  | // set firstRune to '\u0250'. | ||||||
|  | func ParseSubfont(data []byte, firstRune rune) (font.Face, error) { | ||||||
|  | 	data, m, err := parseImage(data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if len(data) < 3*12 { | ||||||
|  | 		return nil, errors.New("plan9font: invalid subfont: header too short") | ||||||
|  | 	} | ||||||
|  | 	n := atoi(data[0*12:]) | ||||||
|  | 	height := atoi(data[1*12:]) | ||||||
|  | 	ascent := atoi(data[2*12:]) | ||||||
|  | 	data = data[3*12:] | ||||||
|  | 	if len(data) != 6*(n+1) { | ||||||
|  | 		return nil, errors.New("plan9font: invalid subfont: data length mismatch") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Convert from plan9Image to image.Alpha, as the standard library's | ||||||
|  | 	// image/draw package works best when glyph masks are of that type. | ||||||
|  | 	img := image.NewAlpha(m.Bounds()) | ||||||
|  | 	for y := img.Rect.Min.Y; y < img.Rect.Max.Y; y++ { | ||||||
|  | 		i := img.PixOffset(img.Rect.Min.X, y) | ||||||
|  | 		for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { | ||||||
|  | 			img.Pix[i] = m.at(x, y) | ||||||
|  | 			i++ | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &subface{ | ||||||
|  | 		firstRune: firstRune, | ||||||
|  | 		n:         n, | ||||||
|  | 		height:    height, | ||||||
|  | 		ascent:    ascent, | ||||||
|  | 		fontchars: parseFontchars(data), | ||||||
|  | 		img:       img, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // plan9Image implements that subset of the Plan 9 image feature set that is | ||||||
|  | // used by this font file format. | ||||||
|  | // | ||||||
|  | // Some features, such as the repl bit and a clip rectangle, are omitted for | ||||||
|  | // simplicity. | ||||||
|  | type plan9Image struct { | ||||||
|  | 	depth int             // Depth of the pixels in bits. | ||||||
|  | 	width int             // Width in bytes of a single scan line. | ||||||
|  | 	rect  image.Rectangle // Extent of the image. | ||||||
|  | 	pix   []byte          // Pixel bits. | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *plan9Image) byteoffset(x, y int) int { | ||||||
|  | 	a := y * m.width | ||||||
|  | 	if m.depth < 8 { | ||||||
|  | 		// We need to always round down, but Go rounds toward zero. | ||||||
|  | 		np := 8 / m.depth | ||||||
|  | 		if x < 0 { | ||||||
|  | 			return a + (x-np+1)/np | ||||||
|  | 		} | ||||||
|  | 		return a + x/np | ||||||
|  | 	} | ||||||
|  | 	return a + x*(m.depth/8) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *plan9Image) Bounds() image.Rectangle { return m.rect } | ||||||
|  | func (m *plan9Image) ColorModel() color.Model { return color.AlphaModel } | ||||||
|  |  | ||||||
|  | func (m *plan9Image) At(x, y int) color.Color { | ||||||
|  | 	if (image.Point{x, y}).In(m.rect) { | ||||||
|  | 		return color.Alpha{m.at(x, y)} | ||||||
|  | 	} | ||||||
|  | 	return color.Alpha{0x00} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *plan9Image) at(x, y int) uint8 { | ||||||
|  | 	b := m.pix[m.byteoffset(x, y)] | ||||||
|  | 	switch m.depth { | ||||||
|  | 	case 1: | ||||||
|  | 		// CGrey, 1. | ||||||
|  | 		mask := uint8(1 << uint8(7-x&7)) | ||||||
|  | 		if (b & mask) != 0 { | ||||||
|  | 			return 0xff | ||||||
|  | 		} | ||||||
|  | 		return 0 | ||||||
|  | 	case 2: | ||||||
|  | 		// CGrey, 2. | ||||||
|  | 		shift := uint(x&3) << 1 | ||||||
|  | 		// Place pixel at top of word. | ||||||
|  | 		y := b << shift | ||||||
|  | 		y &= 0xc0 | ||||||
|  | 		// Replicate throughout. | ||||||
|  | 		y |= y >> 2 | ||||||
|  | 		y |= y >> 4 | ||||||
|  | 		return y | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var compressed = []byte("compressed\n") | ||||||
|  |  | ||||||
|  | func parseImage(data []byte) (remainingData []byte, m *plan9Image, retErr error) { | ||||||
|  | 	if !bytes.HasPrefix(data, compressed) { | ||||||
|  | 		return nil, nil, errors.New("plan9font: unsupported uncompressed format") | ||||||
|  | 	} | ||||||
|  | 	data = data[len(compressed):] | ||||||
|  |  | ||||||
|  | 	const hdrSize = 5 * 12 | ||||||
|  | 	if len(data) < hdrSize { | ||||||
|  | 		return nil, nil, errors.New("plan9font: invalid image: header too short") | ||||||
|  | 	} | ||||||
|  | 	hdr, data := data[:hdrSize], data[hdrSize:] | ||||||
|  |  | ||||||
|  | 	// Distinguish new channel descriptor from old ldepth. Channel descriptors | ||||||
|  | 	// have letters as well as numbers, while ldepths are a single digit | ||||||
|  | 	// formatted as %-11d. | ||||||
|  | 	new := false | ||||||
|  | 	for m := 0; m < 10; m++ { | ||||||
|  | 		if hdr[m] != ' ' { | ||||||
|  | 			new = true | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if hdr[11] != ' ' { | ||||||
|  | 		return nil, nil, errors.New("plan9font: invalid image: bad header") | ||||||
|  | 	} | ||||||
|  | 	if !new { | ||||||
|  | 		return nil, nil, errors.New("plan9font: unsupported ldepth format") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	depth := 0 | ||||||
|  | 	switch s := strings.TrimSpace(string(hdr[:1*12])); s { | ||||||
|  | 	default: | ||||||
|  | 		return nil, nil, fmt.Errorf("plan9font: unsupported pixel format %q", s) | ||||||
|  | 	case "k1": | ||||||
|  | 		depth = 1 | ||||||
|  | 	case "k2": | ||||||
|  | 		depth = 2 | ||||||
|  | 	} | ||||||
|  | 	r := ator(hdr[1*12:]) | ||||||
|  | 	if r.Min.X > r.Max.X || r.Min.Y > r.Max.Y { | ||||||
|  | 		return nil, nil, errors.New("plan9font: invalid image: bad rectangle") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	width := bytesPerLine(r, depth) | ||||||
|  | 	m = &plan9Image{ | ||||||
|  | 		depth: depth, | ||||||
|  | 		width: width, | ||||||
|  | 		rect:  r, | ||||||
|  | 		pix:   make([]byte, width*r.Dy()), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	miny := r.Min.Y | ||||||
|  | 	for miny != r.Max.Y { | ||||||
|  | 		if len(data) < 2*12 { | ||||||
|  | 			return nil, nil, errors.New("plan9font: invalid image: data band too short") | ||||||
|  | 		} | ||||||
|  | 		maxy := atoi(data[0*12:]) | ||||||
|  | 		nb := atoi(data[1*12:]) | ||||||
|  | 		data = data[2*12:] | ||||||
|  |  | ||||||
|  | 		if len(data) < nb { | ||||||
|  | 			return nil, nil, errors.New("plan9font: invalid image: data band length mismatch") | ||||||
|  | 		} | ||||||
|  | 		buf := data[:nb] | ||||||
|  | 		data = data[nb:] | ||||||
|  |  | ||||||
|  | 		if maxy <= miny || r.Max.Y < maxy { | ||||||
|  | 			return nil, nil, fmt.Errorf("plan9font: bad maxy %d", maxy) | ||||||
|  | 		} | ||||||
|  | 		// An old-format image would flip the bits here, but we don't support | ||||||
|  | 		// the old format. | ||||||
|  | 		rr := r | ||||||
|  | 		rr.Min.Y = miny | ||||||
|  | 		rr.Max.Y = maxy | ||||||
|  | 		if err := decompress(m, rr, buf); err != nil { | ||||||
|  | 			return nil, nil, err | ||||||
|  | 		} | ||||||
|  | 		miny = maxy | ||||||
|  | 	} | ||||||
|  | 	return data, m, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Compressed data are sequences of byte codes. If the first byte b has the | ||||||
|  | // 0x80 bit set, the next (b^0x80)+1 bytes are data. Otherwise, these two bytes | ||||||
|  | // specify a previous string to repeat. | ||||||
|  | const ( | ||||||
|  | 	compShortestMatch = 3    // shortest match possible. | ||||||
|  | 	compWindowSize    = 1024 // window size. | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	errDecompressBufferTooSmall = errors.New("plan9font: decompress: buffer too small") | ||||||
|  | 	errDecompressPhaseError     = errors.New("plan9font: decompress: phase error") | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func decompress(m *plan9Image, r image.Rectangle, data []byte) error { | ||||||
|  | 	if !r.In(m.rect) { | ||||||
|  | 		return errors.New("plan9font: decompress: bad rectangle") | ||||||
|  | 	} | ||||||
|  | 	bpl := bytesPerLine(r, m.depth) | ||||||
|  | 	mem := make([]byte, compWindowSize) | ||||||
|  | 	memi := 0 | ||||||
|  | 	omemi := -1 | ||||||
|  | 	y := r.Min.Y | ||||||
|  | 	linei := m.byteoffset(r.Min.X, y) | ||||||
|  | 	eline := linei + bpl | ||||||
|  | 	datai := 0 | ||||||
|  | 	for { | ||||||
|  | 		if linei == eline { | ||||||
|  | 			y++ | ||||||
|  | 			if y == r.Max.Y { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 			linei = m.byteoffset(r.Min.X, y) | ||||||
|  | 			eline = linei + bpl | ||||||
|  | 		} | ||||||
|  | 		if datai == len(data) { | ||||||
|  | 			return errDecompressBufferTooSmall | ||||||
|  | 		} | ||||||
|  | 		c := data[datai] | ||||||
|  | 		datai++ | ||||||
|  | 		if c >= 128 { | ||||||
|  | 			for cnt := c - 128 + 1; cnt != 0; cnt-- { | ||||||
|  | 				if datai == len(data) { | ||||||
|  | 					return errDecompressBufferTooSmall | ||||||
|  | 				} | ||||||
|  | 				if linei == eline { | ||||||
|  | 					return errDecompressPhaseError | ||||||
|  | 				} | ||||||
|  | 				m.pix[linei] = data[datai] | ||||||
|  | 				linei++ | ||||||
|  | 				mem[memi] = data[datai] | ||||||
|  | 				memi++ | ||||||
|  | 				datai++ | ||||||
|  | 				if memi == len(mem) { | ||||||
|  | 					memi = 0 | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			if datai == len(data) { | ||||||
|  | 				return errDecompressBufferTooSmall | ||||||
|  | 			} | ||||||
|  | 			offs := int(data[datai]) + ((int(c) & 3) << 8) + 1 | ||||||
|  | 			datai++ | ||||||
|  | 			if memi < offs { | ||||||
|  | 				omemi = memi + (compWindowSize - offs) | ||||||
|  | 			} else { | ||||||
|  | 				omemi = memi - offs | ||||||
|  | 			} | ||||||
|  | 			for cnt := (c >> 2) + compShortestMatch; cnt != 0; cnt-- { | ||||||
|  | 				if linei == eline { | ||||||
|  | 					return errDecompressPhaseError | ||||||
|  | 				} | ||||||
|  | 				m.pix[linei] = mem[omemi] | ||||||
|  | 				linei++ | ||||||
|  | 				mem[memi] = mem[omemi] | ||||||
|  | 				memi++ | ||||||
|  | 				omemi++ | ||||||
|  | 				if omemi == len(mem) { | ||||||
|  | 					omemi = 0 | ||||||
|  | 				} | ||||||
|  | 				if memi == len(mem) { | ||||||
|  | 					memi = 0 | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func ator(b []byte) image.Rectangle { | ||||||
|  | 	return image.Rectangle{atop(b), atop(b[2*12:])} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func atop(b []byte) image.Point { | ||||||
|  | 	return image.Pt(atoi(b), atoi(b[12:])) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func atoi(b []byte) int { | ||||||
|  | 	i := 0 | ||||||
|  | 	for ; i < len(b) && b[i] == ' '; i++ { | ||||||
|  | 	} | ||||||
|  | 	n := 0 | ||||||
|  | 	for ; i < len(b) && '0' <= b[i] && b[i] <= '9'; i++ { | ||||||
|  | 		n = n*10 + int(b[i]) - '0' | ||||||
|  | 	} | ||||||
|  | 	return n | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func bytesPerLine(r image.Rectangle, depth int) int { | ||||||
|  | 	if depth <= 0 || 32 < depth { | ||||||
|  | 		panic("invalid depth") | ||||||
|  | 	} | ||||||
|  | 	var l int | ||||||
|  | 	if r.Min.X >= 0 { | ||||||
|  | 		l = (r.Max.X*depth + 7) / 8 | ||||||
|  | 		l -= (r.Min.X * depth) / 8 | ||||||
|  | 	} else { | ||||||
|  | 		// Make positive before divide. | ||||||
|  | 		t := (-r.Min.X*depth + 7) / 8 | ||||||
|  | 		l = t + (r.Max.X*depth+7)/8 | ||||||
|  | 	} | ||||||
|  | 	return l | ||||||
|  | } | ||||||
							
								
								
									
										410
									
								
								vendor/golang.org/x/image/math/fixed/fixed.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										410
									
								
								vendor/golang.org/x/image/math/fixed/fixed.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,410 @@ | |||||||
|  | // Copyright 2015 The Go Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a BSD-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | // Package fixed implements fixed-point integer types. | ||||||
|  | package fixed // import "golang.org/x/image/math/fixed" | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // TODO: implement fmt.Formatter for %f and %g. | ||||||
|  |  | ||||||
|  | // I returns the integer value i as an Int26_6. | ||||||
|  | // | ||||||
|  | // For example, passing the integer value 2 yields Int26_6(128). | ||||||
|  | func I(i int) Int26_6 { | ||||||
|  | 	return Int26_6(i << 6) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Int26_6 is a signed 26.6 fixed-point number. | ||||||
|  | // | ||||||
|  | // The integer part ranges from -33554432 to 33554431, inclusive. The | ||||||
|  | // fractional part has 6 bits of precision. | ||||||
|  | // | ||||||
|  | // For example, the number one-and-a-quarter is Int26_6(1<<6 + 1<<4). | ||||||
|  | type Int26_6 int32 | ||||||
|  |  | ||||||
|  | // String returns a human-readable representation of a 26.6 fixed-point number. | ||||||
|  | // | ||||||
|  | // For example, the number one-and-a-quarter becomes "1:16". | ||||||
|  | func (x Int26_6) String() string { | ||||||
|  | 	const shift, mask = 6, 1<<6 - 1 | ||||||
|  | 	if x >= 0 { | ||||||
|  | 		return fmt.Sprintf("%d:%02d", int32(x>>shift), int32(x&mask)) | ||||||
|  | 	} | ||||||
|  | 	x = -x | ||||||
|  | 	if x >= 0 { | ||||||
|  | 		return fmt.Sprintf("-%d:%02d", int32(x>>shift), int32(x&mask)) | ||||||
|  | 	} | ||||||
|  | 	return "-33554432:00" // The minimum value is -(1<<25). | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Floor returns the greatest integer value less than or equal to x. | ||||||
|  | // | ||||||
|  | // Its return type is int, not Int26_6. | ||||||
|  | func (x Int26_6) Floor() int { return int((x + 0x00) >> 6) } | ||||||
|  |  | ||||||
|  | // Round returns the nearest integer value to x. Ties are rounded up. | ||||||
|  | // | ||||||
|  | // Its return type is int, not Int26_6. | ||||||
|  | func (x Int26_6) Round() int { return int((x + 0x20) >> 6) } | ||||||
|  |  | ||||||
|  | // Ceil returns the least integer value greater than or equal to x. | ||||||
|  | // | ||||||
|  | // Its return type is int, not Int26_6. | ||||||
|  | func (x Int26_6) Ceil() int { return int((x + 0x3f) >> 6) } | ||||||
|  |  | ||||||
|  | // Mul returns x*y in 26.6 fixed-point arithmetic. | ||||||
|  | func (x Int26_6) Mul(y Int26_6) Int26_6 { | ||||||
|  | 	return Int26_6((int64(x)*int64(y) + 1<<5) >> 6) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Int52_12 is a signed 52.12 fixed-point number. | ||||||
|  | // | ||||||
|  | // The integer part ranges from -2251799813685248 to 2251799813685247, | ||||||
|  | // inclusive. The fractional part has 12 bits of precision. | ||||||
|  | // | ||||||
|  | // For example, the number one-and-a-quarter is Int52_12(1<<12 + 1<<10). | ||||||
|  | type Int52_12 int64 | ||||||
|  |  | ||||||
|  | // String returns a human-readable representation of a 52.12 fixed-point | ||||||
|  | // number. | ||||||
|  | // | ||||||
|  | // For example, the number one-and-a-quarter becomes "1:1024". | ||||||
|  | func (x Int52_12) String() string { | ||||||
|  | 	const shift, mask = 12, 1<<12 - 1 | ||||||
|  | 	if x >= 0 { | ||||||
|  | 		return fmt.Sprintf("%d:%04d", int64(x>>shift), int64(x&mask)) | ||||||
|  | 	} | ||||||
|  | 	x = -x | ||||||
|  | 	if x >= 0 { | ||||||
|  | 		return fmt.Sprintf("-%d:%04d", int64(x>>shift), int64(x&mask)) | ||||||
|  | 	} | ||||||
|  | 	return "-2251799813685248:0000" // The minimum value is -(1<<51). | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Floor returns the greatest integer value less than or equal to x. | ||||||
|  | // | ||||||
|  | // Its return type is int, not Int52_12. | ||||||
|  | func (x Int52_12) Floor() int { return int((x + 0x000) >> 12) } | ||||||
|  |  | ||||||
|  | // Round returns the nearest integer value to x. Ties are rounded up. | ||||||
|  | // | ||||||
|  | // Its return type is int, not Int52_12. | ||||||
|  | func (x Int52_12) Round() int { return int((x + 0x800) >> 12) } | ||||||
|  |  | ||||||
|  | // Ceil returns the least integer value greater than or equal to x. | ||||||
|  | // | ||||||
|  | // Its return type is int, not Int52_12. | ||||||
|  | func (x Int52_12) Ceil() int { return int((x + 0xfff) >> 12) } | ||||||
|  |  | ||||||
|  | // Mul returns x*y in 52.12 fixed-point arithmetic. | ||||||
|  | func (x Int52_12) Mul(y Int52_12) Int52_12 { | ||||||
|  | 	const M, N = 52, 12 | ||||||
|  | 	lo, hi := muli64(int64(x), int64(y)) | ||||||
|  | 	ret := Int52_12(hi<<M | lo>>N) | ||||||
|  | 	ret += Int52_12((lo >> (N - 1)) & 1) // Round to nearest, instead of rounding down. | ||||||
|  | 	return ret | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // muli64 multiplies two int64 values, returning the 128-bit signed integer | ||||||
|  | // result as two uint64 values. | ||||||
|  | // | ||||||
|  | // This implementation is similar to $GOROOT/src/runtime/softfloat64.go's mullu | ||||||
|  | // function, which is in turn adapted from Hacker's Delight. | ||||||
|  | func muli64(u, v int64) (lo, hi uint64) { | ||||||
|  | 	const ( | ||||||
|  | 		s    = 32 | ||||||
|  | 		mask = 1<<s - 1 | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	u1 := uint64(u >> s) | ||||||
|  | 	u0 := uint64(u & mask) | ||||||
|  | 	v1 := uint64(v >> s) | ||||||
|  | 	v0 := uint64(v & mask) | ||||||
|  |  | ||||||
|  | 	w0 := u0 * v0 | ||||||
|  | 	t := u1*v0 + w0>>s | ||||||
|  | 	w1 := t & mask | ||||||
|  | 	w2 := uint64(int64(t) >> s) | ||||||
|  | 	w1 += u0 * v1 | ||||||
|  | 	return uint64(u) * uint64(v), u1*v1 + w2 + uint64(int64(w1)>>s) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // P returns the integer values x and y as a Point26_6. | ||||||
|  | // | ||||||
|  | // For example, passing the integer values (2, -3) yields Point26_6{128, -192}. | ||||||
|  | func P(x, y int) Point26_6 { | ||||||
|  | 	return Point26_6{Int26_6(x << 6), Int26_6(y << 6)} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Point26_6 is a 26.6 fixed-point coordinate pair. | ||||||
|  | // | ||||||
|  | // It is analogous to the image.Point type in the standard library. | ||||||
|  | type Point26_6 struct { | ||||||
|  | 	X, Y Int26_6 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add returns the vector p+q. | ||||||
|  | func (p Point26_6) Add(q Point26_6) Point26_6 { | ||||||
|  | 	return Point26_6{p.X + q.X, p.Y + q.Y} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Sub returns the vector p-q. | ||||||
|  | func (p Point26_6) Sub(q Point26_6) Point26_6 { | ||||||
|  | 	return Point26_6{p.X - q.X, p.Y - q.Y} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Mul returns the vector p*k. | ||||||
|  | func (p Point26_6) Mul(k Int26_6) Point26_6 { | ||||||
|  | 	return Point26_6{p.X * k / 64, p.Y * k / 64} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Div returns the vector p/k. | ||||||
|  | func (p Point26_6) Div(k Int26_6) Point26_6 { | ||||||
|  | 	return Point26_6{p.X * 64 / k, p.Y * 64 / k} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // In returns whether p is in r. | ||||||
|  | func (p Point26_6) In(r Rectangle26_6) bool { | ||||||
|  | 	return r.Min.X <= p.X && p.X < r.Max.X && r.Min.Y <= p.Y && p.Y < r.Max.Y | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Point52_12 is a 52.12 fixed-point coordinate pair. | ||||||
|  | // | ||||||
|  | // It is analogous to the image.Point type in the standard library. | ||||||
|  | type Point52_12 struct { | ||||||
|  | 	X, Y Int52_12 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add returns the vector p+q. | ||||||
|  | func (p Point52_12) Add(q Point52_12) Point52_12 { | ||||||
|  | 	return Point52_12{p.X + q.X, p.Y + q.Y} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Sub returns the vector p-q. | ||||||
|  | func (p Point52_12) Sub(q Point52_12) Point52_12 { | ||||||
|  | 	return Point52_12{p.X - q.X, p.Y - q.Y} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Mul returns the vector p*k. | ||||||
|  | func (p Point52_12) Mul(k Int52_12) Point52_12 { | ||||||
|  | 	return Point52_12{p.X * k / 4096, p.Y * k / 4096} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Div returns the vector p/k. | ||||||
|  | func (p Point52_12) Div(k Int52_12) Point52_12 { | ||||||
|  | 	return Point52_12{p.X * 4096 / k, p.Y * 4096 / k} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // In returns whether p is in r. | ||||||
|  | func (p Point52_12) In(r Rectangle52_12) bool { | ||||||
|  | 	return r.Min.X <= p.X && p.X < r.Max.X && r.Min.Y <= p.Y && p.Y < r.Max.Y | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // R returns the integer values minX, minY, maxX, maxY as a Rectangle26_6. | ||||||
|  | // | ||||||
|  | // For example, passing the integer values (0, 1, 2, 3) yields | ||||||
|  | // Rectangle26_6{Point26_6{0, 64}, Point26_6{128, 192}}. | ||||||
|  | // | ||||||
|  | // Like the image.Rect function in the standard library, the returned rectangle | ||||||
|  | // has minimum and maximum coordinates swapped if necessary so that it is | ||||||
|  | // well-formed. | ||||||
|  | func R(minX, minY, maxX, maxY int) Rectangle26_6 { | ||||||
|  | 	if minX > maxX { | ||||||
|  | 		minX, maxX = maxX, minX | ||||||
|  | 	} | ||||||
|  | 	if minY > maxY { | ||||||
|  | 		minY, maxY = maxY, minY | ||||||
|  | 	} | ||||||
|  | 	return Rectangle26_6{ | ||||||
|  | 		Point26_6{ | ||||||
|  | 			Int26_6(minX << 6), | ||||||
|  | 			Int26_6(minY << 6), | ||||||
|  | 		}, | ||||||
|  | 		Point26_6{ | ||||||
|  | 			Int26_6(maxX << 6), | ||||||
|  | 			Int26_6(maxY << 6), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Rectangle26_6 is a 26.6 fixed-point coordinate rectangle. The Min bound is | ||||||
|  | // inclusive and the Max bound is exclusive. It is well-formed if Min.X <= | ||||||
|  | // Max.X and likewise for Y. | ||||||
|  | // | ||||||
|  | // It is analogous to the image.Rectangle type in the standard library. | ||||||
|  | type Rectangle26_6 struct { | ||||||
|  | 	Min, Max Point26_6 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add returns the rectangle r translated by p. | ||||||
|  | func (r Rectangle26_6) Add(p Point26_6) Rectangle26_6 { | ||||||
|  | 	return Rectangle26_6{ | ||||||
|  | 		Point26_6{r.Min.X + p.X, r.Min.Y + p.Y}, | ||||||
|  | 		Point26_6{r.Max.X + p.X, r.Max.Y + p.Y}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Sub returns the rectangle r translated by -p. | ||||||
|  | func (r Rectangle26_6) Sub(p Point26_6) Rectangle26_6 { | ||||||
|  | 	return Rectangle26_6{ | ||||||
|  | 		Point26_6{r.Min.X - p.X, r.Min.Y - p.Y}, | ||||||
|  | 		Point26_6{r.Max.X - p.X, r.Max.Y - p.Y}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Intersect returns the largest rectangle contained by both r and s. If the | ||||||
|  | // two rectangles do not overlap then the zero rectangle will be returned. | ||||||
|  | func (r Rectangle26_6) Intersect(s Rectangle26_6) Rectangle26_6 { | ||||||
|  | 	if r.Min.X < s.Min.X { | ||||||
|  | 		r.Min.X = s.Min.X | ||||||
|  | 	} | ||||||
|  | 	if r.Min.Y < s.Min.Y { | ||||||
|  | 		r.Min.Y = s.Min.Y | ||||||
|  | 	} | ||||||
|  | 	if r.Max.X > s.Max.X { | ||||||
|  | 		r.Max.X = s.Max.X | ||||||
|  | 	} | ||||||
|  | 	if r.Max.Y > s.Max.Y { | ||||||
|  | 		r.Max.Y = s.Max.Y | ||||||
|  | 	} | ||||||
|  | 	// Letting r0 and s0 be the values of r and s at the time that the method | ||||||
|  | 	// is called, this next line is equivalent to: | ||||||
|  | 	// | ||||||
|  | 	// if max(r0.Min.X, s0.Min.X) >= min(r0.Max.X, s0.Max.X) || likewiseForY { etc } | ||||||
|  | 	if r.Empty() { | ||||||
|  | 		return Rectangle26_6{} | ||||||
|  | 	} | ||||||
|  | 	return r | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Union returns the smallest rectangle that contains both r and s. | ||||||
|  | func (r Rectangle26_6) Union(s Rectangle26_6) Rectangle26_6 { | ||||||
|  | 	if r.Empty() { | ||||||
|  | 		return s | ||||||
|  | 	} | ||||||
|  | 	if s.Empty() { | ||||||
|  | 		return r | ||||||
|  | 	} | ||||||
|  | 	if r.Min.X > s.Min.X { | ||||||
|  | 		r.Min.X = s.Min.X | ||||||
|  | 	} | ||||||
|  | 	if r.Min.Y > s.Min.Y { | ||||||
|  | 		r.Min.Y = s.Min.Y | ||||||
|  | 	} | ||||||
|  | 	if r.Max.X < s.Max.X { | ||||||
|  | 		r.Max.X = s.Max.X | ||||||
|  | 	} | ||||||
|  | 	if r.Max.Y < s.Max.Y { | ||||||
|  | 		r.Max.Y = s.Max.Y | ||||||
|  | 	} | ||||||
|  | 	return r | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Empty returns whether the rectangle contains no points. | ||||||
|  | func (r Rectangle26_6) Empty() bool { | ||||||
|  | 	return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // In returns whether every point in r is in s. | ||||||
|  | func (r Rectangle26_6) In(s Rectangle26_6) bool { | ||||||
|  | 	if r.Empty() { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	// Note that r.Max is an exclusive bound for r, so that r.In(s) | ||||||
|  | 	// does not require that r.Max.In(s). | ||||||
|  | 	return s.Min.X <= r.Min.X && r.Max.X <= s.Max.X && | ||||||
|  | 		s.Min.Y <= r.Min.Y && r.Max.Y <= s.Max.Y | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Rectangle52_12 is a 52.12 fixed-point coordinate rectangle. The Min bound is | ||||||
|  | // inclusive and the Max bound is exclusive. It is well-formed if Min.X <= | ||||||
|  | // Max.X and likewise for Y. | ||||||
|  | // | ||||||
|  | // It is analogous to the image.Rectangle type in the standard library. | ||||||
|  | type Rectangle52_12 struct { | ||||||
|  | 	Min, Max Point52_12 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Add returns the rectangle r translated by p. | ||||||
|  | func (r Rectangle52_12) Add(p Point52_12) Rectangle52_12 { | ||||||
|  | 	return Rectangle52_12{ | ||||||
|  | 		Point52_12{r.Min.X + p.X, r.Min.Y + p.Y}, | ||||||
|  | 		Point52_12{r.Max.X + p.X, r.Max.Y + p.Y}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Sub returns the rectangle r translated by -p. | ||||||
|  | func (r Rectangle52_12) Sub(p Point52_12) Rectangle52_12 { | ||||||
|  | 	return Rectangle52_12{ | ||||||
|  | 		Point52_12{r.Min.X - p.X, r.Min.Y - p.Y}, | ||||||
|  | 		Point52_12{r.Max.X - p.X, r.Max.Y - p.Y}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Intersect returns the largest rectangle contained by both r and s. If the | ||||||
|  | // two rectangles do not overlap then the zero rectangle will be returned. | ||||||
|  | func (r Rectangle52_12) Intersect(s Rectangle52_12) Rectangle52_12 { | ||||||
|  | 	if r.Min.X < s.Min.X { | ||||||
|  | 		r.Min.X = s.Min.X | ||||||
|  | 	} | ||||||
|  | 	if r.Min.Y < s.Min.Y { | ||||||
|  | 		r.Min.Y = s.Min.Y | ||||||
|  | 	} | ||||||
|  | 	if r.Max.X > s.Max.X { | ||||||
|  | 		r.Max.X = s.Max.X | ||||||
|  | 	} | ||||||
|  | 	if r.Max.Y > s.Max.Y { | ||||||
|  | 		r.Max.Y = s.Max.Y | ||||||
|  | 	} | ||||||
|  | 	// Letting r0 and s0 be the values of r and s at the time that the method | ||||||
|  | 	// is called, this next line is equivalent to: | ||||||
|  | 	// | ||||||
|  | 	// if max(r0.Min.X, s0.Min.X) >= min(r0.Max.X, s0.Max.X) || likewiseForY { etc } | ||||||
|  | 	if r.Empty() { | ||||||
|  | 		return Rectangle52_12{} | ||||||
|  | 	} | ||||||
|  | 	return r | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Union returns the smallest rectangle that contains both r and s. | ||||||
|  | func (r Rectangle52_12) Union(s Rectangle52_12) Rectangle52_12 { | ||||||
|  | 	if r.Empty() { | ||||||
|  | 		return s | ||||||
|  | 	} | ||||||
|  | 	if s.Empty() { | ||||||
|  | 		return r | ||||||
|  | 	} | ||||||
|  | 	if r.Min.X > s.Min.X { | ||||||
|  | 		r.Min.X = s.Min.X | ||||||
|  | 	} | ||||||
|  | 	if r.Min.Y > s.Min.Y { | ||||||
|  | 		r.Min.Y = s.Min.Y | ||||||
|  | 	} | ||||||
|  | 	if r.Max.X < s.Max.X { | ||||||
|  | 		r.Max.X = s.Max.X | ||||||
|  | 	} | ||||||
|  | 	if r.Max.Y < s.Max.Y { | ||||||
|  | 		r.Max.Y = s.Max.Y | ||||||
|  | 	} | ||||||
|  | 	return r | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Empty returns whether the rectangle contains no points. | ||||||
|  | func (r Rectangle52_12) Empty() bool { | ||||||
|  | 	return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // In returns whether every point in r is in s. | ||||||
|  | func (r Rectangle52_12) In(s Rectangle52_12) bool { | ||||||
|  | 	if r.Empty() { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	// Note that r.Max is an exclusive bound for r, so that r.In(s) | ||||||
|  | 	// does not require that r.Max.In(s). | ||||||
|  | 	return s.Min.X <= r.Min.X && r.Max.X <= s.Max.X && | ||||||
|  | 		s.Min.Y <= r.Min.Y && r.Max.Y <= s.Max.Y | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 esimov
					esimov