// rasterize returns the glyph mask and integer-pixel offset to render the // given glyph at the given sub-pixel offsets. // The 24.8 fixed point arguments fx and fy must be in the range [0, 1). func (c *Context) rasterize(glyph truetype.Index, fx, fy raster.Fix32) (*image.Alpha, image.Point, error) { if err := c.glyphBuf.Load(c.font, c.scale, glyph, nil); err != nil { return nil, image.ZP, err } // Calculate the integer-pixel bounds for the glyph. xmin := int(fx+raster.Fix32(c.glyphBuf.B.XMin<<2)) >> 8 ymin := int(fy-raster.Fix32(c.glyphBuf.B.YMax<<2)) >> 8 xmax := int(fx+raster.Fix32(c.glyphBuf.B.XMax<<2)+0xff) >> 8 ymax := int(fy-raster.Fix32(c.glyphBuf.B.YMin<<2)+0xff) >> 8 if xmin > xmax || ymin > ymax { return nil, image.ZP, errors.New("freetype: negative sized glyph") } // 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 += raster.Fix32(-xmin << 8) fy += raster.Fix32(-ymin << 8) // Rasterize the glyph's vectors. c.r.Clear() e0 := 0 for _, e1 := range c.glyphBuf.End { c.drawContour(c.glyphBuf.Point[e0:e1], fx, fy) e0 = e1 } a := image.NewAlpha(image.Rect(0, 0, xmax-xmin, ymax-ymin)) c.r.Rasterize(raster.NewAlphaSrcPainter(a)) return a, image.Point{xmin, ymin}, nil }
func main() { // Draw a rounded corner that is one pixel wide. r := raster.NewRasterizer(50, 50) r.Start(p(5, 5)) r.Add1(p(5, 25)) r.Add2(p(5, 45), p(25, 45)) r.Add1(p(45, 45)) r.Add1(p(45, 44)) r.Add1(p(26, 44)) r.Add2(p(6, 44), p(6, 24)) r.Add1(p(6, 5)) r.Add1(p(5, 5)) // Rasterize that curve multiple times at different gammas. const ( w = 600 h = 200 ) rgba := image.NewRGBA(image.Rect(0, 0, w, h)) draw.Draw(rgba, image.Rect(0, 0, w, h/2), image.Black, image.ZP, draw.Src) draw.Draw(rgba, image.Rect(0, h/2, w, h), image.White, image.ZP, draw.Src) mask := image.NewAlpha(image.Rect(0, 0, 50, 50)) painter := raster.NewAlphaSrcPainter(mask) gammas := []float64{1.0 / 10.0, 1.0 / 3.0, 1.0 / 2.0, 2.0 / 3.0, 4.0 / 5.0, 1.0, 5.0 / 4.0, 3.0 / 2.0, 2.0, 3.0, 10.0} for i, g := range gammas { draw.Draw(mask, mask.Bounds(), image.Transparent, image.ZP, draw.Src) r.Rasterize(raster.NewGammaCorrectionPainter(painter, g)) x, y := 50*i+25, 25 draw.DrawMask(rgba, image.Rect(x, y, x+50, y+50), image.White, image.ZP, mask, image.ZP, draw.Over) y += 100 draw.DrawMask(rgba, image.Rect(x, y, x+50, y+50), image.Black, image.ZP, mask, image.ZP, draw.Over) } // Save that RGBA image to disk. f, err := os.Create("out.png") if err != nil { log.Println(err) os.Exit(1) } defer f.Close() b := bufio.NewWriter(f) err = png.Encode(b, rgba) if err != nil { log.Println(err) os.Exit(1) } err = b.Flush() if err != nil { log.Println(err) os.Exit(1) } fmt.Println("Wrote out.png OK.") }
func (p *glyphPage) add(rune rune, g *glyph) bool { if _, found := p.offsets[rune]; found { panic("Glyph already added to glyph page") } w, h := g.size(p.resolution).WH() x, y := p.nextPoint.X, p.nextPoint.Y if x+w > p.size.W { // Row full, start new line x = 0 y += p.rowHeight + glyphPadding p.rowHeight = 0 } if y+h > p.size.H { return false // Page full } // Build the raster contours p.rast.Clear() fx := -raster.Fix32((int64(g.B.XMin) * int64(p.resolution)) >> 14) fy := +raster.Fix32((int64(g.B.YMax) * int64(p.resolution)) >> 14) e0 := 0 for _, e1 := range g.End { p.drawContour(g.Point[e0:e1], fx, fy) e0 = e1 } // Perform the rasterization a := &image.Alpha{ Pix: p.image.Pix[x+y*p.image.Stride:], Stride: p.image.Stride, Rect: image.Rect(0, 0, w, h), } p.rast.Rasterize(raster.NewAlphaSrcPainter(a)) p.offsets[rune] = math.Point{X: x, Y: y} p.nextPoint = math.Point{X: x + w + glyphPadding, Y: y} if h > p.rowHeight { p.rowHeight = h } if p.tex != nil { p.tex.Release() p.tex = nil } return true }
func main() { // Rasterize the contours to a mask image. const ( w = 400 h = 400 ) r := raster.NewRasterizer(w, h) contour(r, outside) contour(r, inside) mask := image.NewAlpha(image.Rect(0, 0, w, h)) p := raster.NewAlphaSrcPainter(mask) r.Rasterize(p) // Draw the mask image (in gray) onto an RGBA image. rgba := image.NewRGBA(image.Rect(0, 0, w, h)) gray := image.NewUniform(color.Alpha{0x1f}) draw.Draw(rgba, rgba.Bounds(), image.Black, image.ZP, draw.Src) draw.DrawMask(rgba, rgba.Bounds(), gray, image.ZP, mask, image.ZP, draw.Over) showNodes(rgba, outside) showNodes(rgba, inside) // Save that RGBA image to disk. f, err := os.Create("out.png") if err != nil { log.Println(err) os.Exit(1) } defer f.Close() b := bufio.NewWriter(f) err = png.Encode(b, rgba) if err != nil { log.Println(err) os.Exit(1) } err = b.Flush() if err != nil { log.Println(err) os.Exit(1) } fmt.Println("Wrote out.png OK.") }
func TestFreetype(t *testing.T) { var p Path p.LineTo(10, 190) c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} c.Segment(&p, flattening_threshold) poly := Polygon(p.points) rgba := color.RGBA{0, 0, 0, 0xff} bounds := image.Rect(0, 0, 200, 200) mask := image.NewAlpha(bounds) img := image.NewRGBA(bounds) rasterizer := raster.NewRasterizer(200, 200) rasterizer.UseNonZeroWinding = false rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)}) for j := 0; j < len(poly); j = j + 2 { rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)}) } painter := raster.NewAlphaSrcPainter(mask) rasterizer.Rasterize(painter) DrawSolidRGBA(img, mask, rgba) savepng("_testFreetype.png", img) }
func genGlyphs(font *truetype.Font, size int, text string) (glyphs []*glyph) { scale := int32(float64(size) * dpi * (64.0 / 72.0)) clip := image.Rect(0, 0, width, height) // Calculate the rasterizer's bounds to handle the largest glyph. b := font.Bounds(scale) xmin := int(b.XMin) >> 6 ymin := -int(b.YMax) >> 6 xmax := int(b.XMax+63) >> 6 ymax := -int(b.YMin-63) >> 6 r := raster.NewRasterizer(xmax-xmin, ymax-ymin) buf := truetype.NewGlyphBuf() for _, variant := range []string{strings.ToUpper(text), strings.ToLower(text)} { pt := Pt(30, 10+int(pointToFix32(float64(size))>>8)) for _, char := range variant { idx := font.Index(char) buf.Load(font, scale, idx, truetype.FullHinting) // Calculate the integer-pixel bounds for the glyph. xmin := int(raster.Fix32(buf.B.XMin<<2)) >> 8 ymin := int(-raster.Fix32(buf.B.YMax<<2)) >> 8 xmax := int(raster.Fix32(buf.B.XMax<<2)+0xff) >> 8 ymax := int(-raster.Fix32(buf.B.YMin<<2)+0xff) >> 8 fx := raster.Fix32(-xmin << 8) fy := raster.Fix32(-ymin << 8) ix := int(pt.X >> 8) iy := int(pt.Y >> 8) // Rasterize the glyph's vectors. r.Clear() e0 := 0 for _, e1 := range buf.End { drawContour(r, buf.Point[e0:e1], fx, fy) e0 = e1 } mask := image.NewAlpha(image.Rect(0, 0, xmax-xmin, ymax-ymin)) r.Rasterize(raster.NewAlphaSrcPainter(mask)) pt.X += raster.Fix32(buf.AdvanceWidth << 2) offset := image.Point{xmin + ix, ymin + iy} glyphRect := mask.Bounds().Add(offset) dr := clip.Intersect(glyphRect) mp := image.Point{0, dr.Min.Y - glyphRect.Min.Y} glyphs = append(glyphs, &glyph{ mask: mask, mp: mp, dr: dr, }) } } return }