mirror of
				https://github.com/esimov/pigo.git
				synced 2025-10-31 11:26:47 +08:00 
			
		
		
		
	Include vendor dependencies
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -3,5 +3,4 @@ | ||||
| *.jpg | ||||
| *.jpeg | ||||
| pigo | ||||
| vendor | ||||
| 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