github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/golang/freetype/freetype.go (about) 1 // Copyright 2010 The Freetype-Go Authors. All rights reserved. 2 // Use of this source code is governed by your choice of either the 3 // FreeType License or the GNU General Public License version 2 (or 4 // any later version), both of which can be found in the LICENSE file. 5 6 // The freetype package provides a convenient API to draw text onto an image. 7 // Use the freetype/raster and freetype/truetype packages for lower level 8 // control over rasterization and TrueType parsing. 9 package freetype // import "github.com/insionng/yougam/libraries/golang/freetype" 10 11 import ( 12 "errors" 13 "image" 14 "image/draw" 15 16 "github.com/insionng/yougam/libraries/golang/freetype/raster" 17 "github.com/insionng/yougam/libraries/golang/freetype/truetype" 18 "github.com/insionng/yougam/libraries/x/image/font" 19 "github.com/insionng/yougam/libraries/x/image/math/fixed" 20 ) 21 22 // These constants determine the size of the glyph cache. The cache is keyed 23 // primarily by the glyph index modulo nGlyphs, and secondarily by sub-pixel 24 // position for the mask image. Sub-pixel positions are quantized to 25 // nXFractions possible values in both the x and y directions. 26 const ( 27 nGlyphs = 256 28 nXFractions = 4 29 nYFractions = 1 30 ) 31 32 // An entry in the glyph cache is keyed explicitly by the glyph index and 33 // implicitly by the quantized x and y fractional offset. It maps to a mask 34 // image and an offset. 35 type cacheEntry struct { 36 valid bool 37 glyph truetype.Index 38 advanceWidth fixed.Int26_6 39 mask *image.Alpha 40 offset image.Point 41 } 42 43 // ParseFont just calls the Parse function from the freetype/truetype package. 44 // It is provided here so that code that imports this package doesn't need 45 // to also include the freetype/truetype package. 46 func ParseFont(b []byte) (*truetype.Font, error) { 47 return truetype.Parse(b) 48 } 49 50 // Pt converts from a co-ordinate pair measured in pixels to a fixed.Point26_6 51 // co-ordinate pair measured in fixed.Int26_6 units. 52 func Pt(x, y int) fixed.Point26_6 { 53 return fixed.Point26_6{ 54 X: fixed.Int26_6(x << 6), 55 Y: fixed.Int26_6(y << 6), 56 } 57 } 58 59 // A Context holds the state for drawing text in a given font and size. 60 type Context struct { 61 r *raster.Rasterizer 62 f *truetype.Font 63 glyphBuf truetype.GlyphBuf 64 // clip is the clip rectangle for drawing. 65 clip image.Rectangle 66 // dst and src are the destination and source images for drawing. 67 dst draw.Image 68 src image.Image 69 // fontSize and dpi are used to calculate scale. scale is the number of 70 // 26.6 fixed point units in 1 em. hinting is the hinting policy. 71 fontSize, dpi float64 72 scale fixed.Int26_6 73 hinting font.Hinting 74 // cache is the glyph cache. 75 cache [nGlyphs * nXFractions * nYFractions]cacheEntry 76 } 77 78 // PointToFixed converts the given number of points (as in "a 12 point font") 79 // into a 26.6 fixed point number of pixels. 80 func (c *Context) PointToFixed(x float64) fixed.Int26_6 { 81 return fixed.Int26_6(x * float64(c.dpi) * (64.0 / 72.0)) 82 } 83 84 // drawContour draws the given closed contour with the given offset. 85 func (c *Context) drawContour(ps []truetype.Point, dx, dy fixed.Int26_6) { 86 if len(ps) == 0 { 87 return 88 } 89 90 // The low bit of each point's Flags value is whether the point is on the 91 // curve. Truetype fonts only have quadratic Bézier curves, not cubics. 92 // Thus, two consecutive off-curve points imply an on-curve point in the 93 // middle of those two. 94 // 95 // See http://chanae.walon.org/pub/ttf/ttf_glyphs.htm for more details. 96 97 // ps[0] is a truetype.Point measured in FUnits and positive Y going 98 // upwards. start is the same thing measured in fixed point units and 99 // positive Y going downwards, and offset by (dx, dy). 100 start := fixed.Point26_6{ 101 X: dx + ps[0].X, 102 Y: dy - ps[0].Y, 103 } 104 others := []truetype.Point(nil) 105 if ps[0].Flags&0x01 != 0 { 106 others = ps[1:] 107 } else { 108 last := fixed.Point26_6{ 109 X: dx + ps[len(ps)-1].X, 110 Y: dy - ps[len(ps)-1].Y, 111 } 112 if ps[len(ps)-1].Flags&0x01 != 0 { 113 start = last 114 others = ps[:len(ps)-1] 115 } else { 116 start = fixed.Point26_6{ 117 X: (start.X + last.X) / 2, 118 Y: (start.Y + last.Y) / 2, 119 } 120 others = ps 121 } 122 } 123 c.r.Start(start) 124 q0, on0 := start, true 125 for _, p := range others { 126 q := fixed.Point26_6{ 127 X: dx + p.X, 128 Y: dy - p.Y, 129 } 130 on := p.Flags&0x01 != 0 131 if on { 132 if on0 { 133 c.r.Add1(q) 134 } else { 135 c.r.Add2(q0, q) 136 } 137 } else { 138 if on0 { 139 // No-op. 140 } else { 141 mid := fixed.Point26_6{ 142 X: (q0.X + q.X) / 2, 143 Y: (q0.Y + q.Y) / 2, 144 } 145 c.r.Add2(q0, mid) 146 } 147 } 148 q0, on0 = q, on 149 } 150 // Close the curve. 151 if on0 { 152 c.r.Add1(start) 153 } else { 154 c.r.Add2(q0, start) 155 } 156 } 157 158 // rasterize returns the advance width, glyph mask and integer-pixel offset 159 // to render the given glyph at the given sub-pixel offsets. 160 // The 26.6 fixed point arguments fx and fy must be in the range [0, 1). 161 func (c *Context) rasterize(glyph truetype.Index, fx, fy fixed.Int26_6) ( 162 fixed.Int26_6, *image.Alpha, image.Point, error) { 163 164 if err := c.glyphBuf.Load(c.f, c.scale, glyph, c.hinting); err != nil { 165 return 0, nil, image.Point{}, err 166 } 167 // Calculate the integer-pixel bounds for the glyph. 168 xmin := int(fx+c.glyphBuf.Bounds.Min.X) >> 6 169 ymin := int(fy-c.glyphBuf.Bounds.Max.Y) >> 6 170 xmax := int(fx+c.glyphBuf.Bounds.Max.X+0x3f) >> 6 171 ymax := int(fy-c.glyphBuf.Bounds.Min.Y+0x3f) >> 6 172 if xmin > xmax || ymin > ymax { 173 return 0, nil, image.Point{}, errors.New("freetype: negative sized glyph") 174 } 175 // A TrueType's glyph's nodes can have negative co-ordinates, but the 176 // rasterizer clips anything left of x=0 or above y=0. xmin and ymin are 177 // the pixel offsets, based on the font's FUnit metrics, that let a 178 // negative co-ordinate in TrueType space be non-negative in rasterizer 179 // space. xmin and ymin are typically <= 0. 180 fx -= fixed.Int26_6(xmin << 6) 181 fy -= fixed.Int26_6(ymin << 6) 182 // Rasterize the glyph's vectors. 183 c.r.Clear() 184 e0 := 0 185 for _, e1 := range c.glyphBuf.Ends { 186 c.drawContour(c.glyphBuf.Points[e0:e1], fx, fy) 187 e0 = e1 188 } 189 a := image.NewAlpha(image.Rect(0, 0, xmax-xmin, ymax-ymin)) 190 c.r.Rasterize(raster.NewAlphaSrcPainter(a)) 191 return c.glyphBuf.AdvanceWidth, a, image.Point{xmin, ymin}, nil 192 } 193 194 // glyph returns the advance width, glyph mask and integer-pixel offset to 195 // render the given glyph at the given sub-pixel point. It is a cache for the 196 // rasterize method. Unlike rasterize, p's co-ordinates do not have to be in 197 // the range [0, 1). 198 func (c *Context) glyph(glyph truetype.Index, p fixed.Point26_6) ( 199 fixed.Int26_6, *image.Alpha, image.Point, error) { 200 201 // Split p.X and p.Y into their integer and fractional parts. 202 ix, fx := int(p.X>>6), p.X&0x3f 203 iy, fy := int(p.Y>>6), p.Y&0x3f 204 // Calculate the index t into the cache array. 205 tg := int(glyph) % nGlyphs 206 tx := int(fx) / (64 / nXFractions) 207 ty := int(fy) / (64 / nYFractions) 208 t := ((tg*nXFractions)+tx)*nYFractions + ty 209 // Check for a cache hit. 210 if e := c.cache[t]; e.valid && e.glyph == glyph { 211 return e.advanceWidth, e.mask, e.offset.Add(image.Point{ix, iy}), nil 212 } 213 // Rasterize the glyph and put the result into the cache. 214 advanceWidth, mask, offset, err := c.rasterize(glyph, fx, fy) 215 if err != nil { 216 return 0, nil, image.Point{}, err 217 } 218 c.cache[t] = cacheEntry{true, glyph, advanceWidth, mask, offset} 219 return advanceWidth, mask, offset.Add(image.Point{ix, iy}), nil 220 } 221 222 // DrawString draws s at p and returns p advanced by the text extent. The text 223 // is placed so that the left edge of the em square of the first character of s 224 // and the baseline intersect at p. The majority of the affected pixels will be 225 // above and to the right of the point, but some may be below or to the left. 226 // For example, drawing a string that starts with a 'J' in an italic font may 227 // affect pixels below and left of the point. 228 // 229 // p is a fixed.Point26_6 and can therefore represent sub-pixel positions. 230 func (c *Context) DrawString(s string, p fixed.Point26_6) (fixed.Point26_6, error) { 231 if c.f == nil { 232 return fixed.Point26_6{}, errors.New("freetype: DrawText called with a nil font") 233 } 234 prev, hasPrev := truetype.Index(0), false 235 for _, rune := range s { 236 index := c.f.Index(rune) 237 if hasPrev { 238 kern := c.f.Kern(c.scale, prev, index) 239 if c.hinting != font.HintingNone { 240 kern = (kern + 32) &^ 63 241 } 242 p.X += kern 243 } 244 advanceWidth, mask, offset, err := c.glyph(index, p) 245 if err != nil { 246 return fixed.Point26_6{}, err 247 } 248 p.X += advanceWidth 249 glyphRect := mask.Bounds().Add(offset) 250 dr := c.clip.Intersect(glyphRect) 251 if !dr.Empty() { 252 mp := image.Point{0, dr.Min.Y - glyphRect.Min.Y} 253 draw.DrawMask(c.dst, dr, c.src, image.ZP, mask, mp, draw.Over) 254 } 255 prev, hasPrev = index, true 256 } 257 return p, nil 258 } 259 260 // recalc recalculates scale and bounds values from the font size, screen 261 // resolution and font metrics, and invalidates the glyph cache. 262 func (c *Context) recalc() { 263 c.scale = fixed.Int26_6(c.fontSize * c.dpi * (64.0 / 72.0)) 264 if c.f == nil { 265 c.r.SetBounds(0, 0) 266 } else { 267 // Set the rasterizer's bounds to be big enough to handle the largest glyph. 268 b := c.f.Bounds(c.scale) 269 xmin := +int(b.Min.X) >> 6 270 ymin := -int(b.Max.Y) >> 6 271 xmax := +int(b.Max.X+63) >> 6 272 ymax := -int(b.Min.Y-63) >> 6 273 c.r.SetBounds(xmax-xmin, ymax-ymin) 274 } 275 for i := range c.cache { 276 c.cache[i] = cacheEntry{} 277 } 278 } 279 280 // SetDPI sets the screen resolution in dots per inch. 281 func (c *Context) SetDPI(dpi float64) { 282 if c.dpi == dpi { 283 return 284 } 285 c.dpi = dpi 286 c.recalc() 287 } 288 289 // SetFont sets the font used to draw text. 290 func (c *Context) SetFont(f *truetype.Font) { 291 if c.f == f { 292 return 293 } 294 c.f = f 295 c.recalc() 296 } 297 298 // SetFontSize sets the font size in points (as in "a 12 point font"). 299 func (c *Context) SetFontSize(fontSize float64) { 300 if c.fontSize == fontSize { 301 return 302 } 303 c.fontSize = fontSize 304 c.recalc() 305 } 306 307 // SetHinting sets the hinting policy. 308 func (c *Context) SetHinting(hinting font.Hinting) { 309 c.hinting = hinting 310 for i := range c.cache { 311 c.cache[i] = cacheEntry{} 312 } 313 } 314 315 // SetDst sets the destination image for draw operations. 316 func (c *Context) SetDst(dst draw.Image) { 317 c.dst = dst 318 } 319 320 // SetSrc sets the source image for draw operations. This is typically an 321 // image.Uniform. 322 func (c *Context) SetSrc(src image.Image) { 323 c.src = src 324 } 325 326 // SetClip sets the clip rectangle for drawing. 327 func (c *Context) SetClip(clip image.Rectangle) { 328 c.clip = clip 329 } 330 331 // TODO(nigeltao): implement Context.SetGamma. 332 333 // NewContext creates a new Context. 334 func NewContext() *Context { 335 return &Context{ 336 r: raster.NewRasterizer(0, 0), 337 fontSize: 12, 338 dpi: 72, 339 scale: 12 << 6, 340 } 341 }