// NewGraphicContext creates a new Graphic context from an image. func NewGraphicContext(width, height int) *GraphicContext { gc := &GraphicContext{ draw2dbase.NewStackGraphicContext(), NewPainter(), raster.NewRasterizer(width, height), raster.NewRasterizer(width, height), } return gc }
// NewGraphicContextWithPainter creates a new Graphic context from an image and a Painter (see Freetype-go) func NewGraphicContextWithPainter(img draw.Image, painter Painter) *GraphicContext { width, height := img.Bounds().Dx(), img.Bounds().Dy() dpi := 92 gc := &GraphicContext{ draw2dbase.NewStackGraphicContext(), img, painter, raster.NewRasterizer(width, height), raster.NewRasterizer(width, height), truetype.NewGlyphBuf(), dpi, } return gc }
func BenchmarkFreetypeNonZeroWinding(b *testing.B) { var p Path p.LineTo(10, 190) draw2dbase.TraceCubic(&p, []float64{10, 190, 10, 10, 190, 10, 190, 190}, 0.5) poly := Polygon(p.points) color := color.RGBA{0, 0, 0, 0xff} for i := 0; i < b.N; i++ { img := image.NewRGBA(image.Rect(0, 0, 200, 200)) rasterizer := raster.NewRasterizer(200, 200) rasterizer.UseNonZeroWinding = true rasterizer.Start(raster.Point{ X: raster.Fix32(10 * 256), Y: raster.Fix32(190 * 256)}) for j := 0; j < len(poly); j = j + 2 { rasterizer.Add1(raster.Point{ X: raster.Fix32(poly[j] * 256), Y: raster.Fix32(poly[j+1] * 256)}) } painter := raster.NewRGBAPainter(img) painter.SetColor(color) rasterizer.Rasterize(painter) } }
// NewContext creates a new Context. func NewContext() *Context { return &Context{ r: raster.NewRasterizer(0, 0), fontSize: 12, dpi: 72, scale: 12 << 6, } }
func main() { const ( w = 400 h = 400 ) r := raster.NewRasterizer(w, h) r.UseNonZeroWinding = true cjs := []struct { c raster.Capper j raster.Joiner }{ {raster.RoundCapper, raster.RoundJoiner}, {raster.ButtCapper, raster.BevelJoiner}, {raster.SquareCapper, raster.BevelJoiner}, } for i, cj := range cjs { var path raster.Path path.Start(fixed.P(30+100*i, 30+120*i)) path.Add1(fixed.P(180+100*i, 80+120*i)) path.Add1(fixed.P(50+100*i, 130+120*i)) raster.Stroke(r, path, fixed.I(20), cj.c, cj.j) } rgba := image.NewRGBA(image.Rect(0, 0, w, h)) draw.Draw(rgba, rgba.Bounds(), image.Black, image.Point{}, draw.Src) p := raster.NewRGBAPainter(rgba) p.SetColor(color.RGBA{0x7f, 0x7f, 0x7f, 0xff}) r.Rasterize(p) white := color.RGBA{0xff, 0xff, 0xff, 0xff} for i := range cjs { rgba.SetRGBA(30+100*i, 30+120*i, white) rgba.SetRGBA(180+100*i, 80+120*i, white) rgba.SetRGBA(50+100*i, 130+120*i, white) } // Save that RGBA image to disk. outFile, err := os.Create("out.png") if err != nil { log.Println(err) os.Exit(1) } defer outFile.Close() b := bufio.NewWriter(outFile) 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.") }
// NewContext creates a new Context. func NewContext() *Context { return &Context{ r: raster.NewRasterizer(0, 0), glyphBuf: truetype.NewGlyphBuf(), fontSize: 12, dpi: 108, scale: 12 << 6, } }
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 createGlyphPage(resolution resolution, glyphMaxSizePixels math.Size) *glyphPage { // Handle exceptionally large glyphs. size := math.Size{W: glyphPageWidth, H: glyphPageHeight}.Max(glyphMaxSizePixels) size.W = align(size.W, glyphSizeAlignment) size.H = align(size.H, glyphSizeAlignment) return &glyphPage{ resolution: resolution, glyphMaxSizePixels: glyphMaxSizePixels, size: size, image: image.NewAlpha(image.Rect(0, 0, size.W, size.H)), offsets: make(map[rune]math.Point), rowHeight: 0, rast: raster.NewRasterizer(glyphMaxSizePixels.W, glyphMaxSizePixels.H), } }
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. outFile, err := os.Create("out.png") if err != nil { log.Println(err) os.Exit(1) } defer outFile.Close() b := bufio.NewWriter(outFile) 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 TestFreetypeNonZeroWinding(t *testing.T) { var p Path p.LineTo(10, 190) draw2dbase.TraceCubic(&p, []float64{10, 190, 10, 10, 190, 10, 190, 190}, 0.5) poly := Polygon(p.points) color := color.RGBA{0, 0, 0, 0xff} img := image.NewRGBA(image.Rect(0, 0, 200, 200)) rasterizer := raster.NewRasterizer(200, 200) rasterizer.UseNonZeroWinding = true rasterizer.Start(raster.Point{ X: raster.Fix32(10 * 256), Y: raster.Fix32(190 * 256)}) for j := 0; j < len(poly); j = j + 2 { rasterizer.Add1(raster.Point{ X: raster.Fix32(poly[j] * 256), Y: raster.Fix32(poly[j+1] * 256)}) } painter := raster.NewRGBAPainter(img) painter.SetColor(color) rasterizer.Rasterize(painter) draw2dimg.SaveToPngFile("../output/raster/TestFreetypeNonZeroWinding.png", img) }
func main() { const ( n = 17 r = 64 * 80 ) s := fixed.Int26_6(r * math.Sqrt(2) / 2) t := fixed.Int26_6(r * math.Tan(math.Pi/8)) m := image.NewRGBA(image.Rect(0, 0, 800, 600)) draw.Draw(m, m.Bounds(), image.NewUniform(color.RGBA{63, 63, 63, 255}), image.ZP, draw.Src) mp := raster.NewRGBAPainter(m) mp.SetColor(image.Black) z := raster.NewRasterizer(800, 600) for i := 0; i < n; i++ { cx := fixed.Int26_6(6400 + 12800*(i%4)) cy := fixed.Int26_6(640 + 8000*(i/4)) c := fixed.Point26_6{X: cx, Y: cy} theta := math.Pi * (0.5 + 0.5*float64(i)/(n-1)) dx := fixed.Int26_6(r * math.Cos(theta)) dy := fixed.Int26_6(r * math.Sin(theta)) d := fixed.Point26_6{X: dx, Y: dy} // Draw a quarter-circle approximated by two quadratic segments, // with each segment spanning 45 degrees. z.Start(c) z.Add1(c.Add(fixed.Point26_6{X: r, Y: 0})) z.Add2(c.Add(fixed.Point26_6{X: r, Y: t}), c.Add(fixed.Point26_6{X: s, Y: s})) z.Add2(c.Add(fixed.Point26_6{X: t, Y: r}), c.Add(fixed.Point26_6{X: 0, Y: r})) // Add another quadratic segment whose angle ranges between 0 and 90 // degrees. For an explanation of the magic constants 128, 150, 181 and // 256, read the comments in the freetype/raster package. dot := 256 * pDot(d, fixed.Point26_6{X: 0, Y: r}) / (r * r) multiple := fixed.Int26_6(150-(150-128)*(dot-181)/(256-181)) >> 2 z.Add2(c.Add(fixed.Point26_6{X: dx, Y: r + dy}.Mul(multiple)), c.Add(d)) // Close the curve. z.Add1(c) } z.Rasterize(mp) for i := 0; i < n; i++ { cx := fixed.Int26_6(6400 + 12800*(i%4)) cy := fixed.Int26_6(640 + 8000*(i/4)) for j := 0; j < n; j++ { theta := math.Pi * float64(j) / (n - 1) dx := fixed.Int26_6(r * math.Cos(theta)) dy := fixed.Int26_6(r * math.Sin(theta)) m.Set(int((cx+dx)/64), int((cy+dy)/64), color.RGBA{255, 255, 0, 255}) } } // Save that RGBA image to disk. outFile, err := os.Create("out.png") if err != nil { log.Println(err) os.Exit(1) } defer outFile.Close() b := bufio.NewWriter(outFile) err = png.Encode(b, m) 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 (s *Sparkline) Draw(dst draw.Image) { if len(s.data) == 0 { return } min := s.data[0] max := s.data[0] for _, n := range s.data[1:] { if n < min { min = n } if n > max { max = n } } // try to be helpful when there's limited data if max < 1.0 { max = 1.0 } if min > max-1.0 { min = max - 1.0 if min < 0.0 { min = 0.0 } } bounds := dst.Bounds() dx, dy := bounds.Dx(), bounds.Dy() tmp := image.NewRGBA(image.Rectangle{Max: image.Point{X: dx, Y: dy}}) p := raster.NewRGBAPainter(tmp) r := raster.NewRasterizer(dx, dy) r.UseNonZeroWinding = true var q raster.Path q.Start(s.scale(0, s.data[0], min, max, dx, dy)) for i, n := range s.data[1:] { pt := s.scale(i+1, n, min, max, dx, dy) q.Add1(pt) } const strokeWidth = fixed.Int26_6(5 << 6) r.AddStroke(q, strokeWidth, raster.RoundCapper, raster.RoundJoiner) p.SetColor(s.fg) r.Rasterize(p) r.Clear() q.Clear() headPt := s.scale(len(s.data)-1, s.data[len(s.data)-1], min, max, dx, dy) q.Start(headPt) // miniscule nudge so something actually is output q.Add1(headPt.Add(fixed.Point26_6{X: 1, Y: 1})) const headWidth = fixed.Int26_6(8 << 6) r.AddStroke(q, headWidth, raster.RoundCapper, raster.RoundJoiner) // TODO really decide between uint64 vs float32 vs uint32 etc // value := uint64((s.data[len(s.data)-1] - min) / max * float32(^uint64(0))) value := uint64(s.data[len(s.data)-1]) headColor := PickColor(s.thresholds, value) p.SetColor(headColor) r.Rasterize(p) draw.Draw(dst, bounds, tmp, image.ZP, draw.Over) }
func Handler(w http.ResponseWriter, req *http.Request) { const size = 500 c := image.NewRGBA(image.Rect(0, 0, size, size)) white := &image.Uniform{C: color.White} draw.Draw(c, c.Bounds(), white, image.ZP, draw.Src) p := raster.NewRGBAPainter(c) p.SetColor(color.Black) r := raster.NewRasterizer(500, 500) r.UseNonZeroWinding = true var path raster.Path path.Start(nudge(fixed.P(50, 50))) path.Add1(nudge(fixed.P(50, 450))) path.Add1(nudge(fixed.P(450, 450))) r.AddStroke(path, fixed.I(2), raster.ButtCapper, raster.BevelJoiner) r.Rasterize(p) r.Clear() p.SetColor(color.Gray16{0x7FFF}) path.Clear() r.Clear() path.Start(nudge(fixed.P(450, 450))) path.Add1(nudge(fixed.P(450, 50))) path.Add1(nudge(fixed.P(50, 50))) r.AddStroke(path, fixed.I(1), raster.ButtCapper, raster.BevelJoiner) r.Rasterize(p) p.SetColor(color.Gray16{0x7FFF}) for x := 50; x <= 450; x += 50 { path.Clear() path.Start(nudge(fixed.P(x, 450))) path.Add1(nudge(fixed.P(x, 460))) r.AddStroke(path, fixed.I(1), raster.ButtCapper, raster.BevelJoiner) } for x := 50; x <= 450; x += 50 { path.Clear() path.Start(nudge(fixed.P(50, x))) path.Add1(nudge(fixed.P(40, x))) r.AddStroke(path, fixed.I(1), raster.ButtCapper, raster.BevelJoiner) } r.Rasterize(p) p.SetColor(color.RGBA{0x00, 0x00, 0xFF, 0xFF}) r.Clear() path.Clear() path.Start(nudge(fixed.P(50, 450))) path.Add1(nudge(fixed.P(450, 50))) r.AddStroke(path, fixed.I(1), raster.ButtCapper, raster.BevelJoiner) r.Rasterize(p) p.SetColor(color.RGBA{0xCC, 0x00, 0x00, 0xFF}) r.Clear() window := fixed.Rectangle26_6{fixed.P(50, 450), fixed.P(450, 50)} // path = plotPath(ratSin, big.NewRat(0, 1), big.NewRat(-1, 1), big.NewRat(10, 1), big.NewRat(1, 1), window) // path = plotPath(ratProb, big.NewRat(0, 1), big.NewRat(0, 1), big.NewRat(1, 1<<57), big.NewRat(1, 1<<57), window) var lo, hi *big.Rat var locap, hicap, scalecap string id, _ := strconv.Atoi(req.FormValue("x")) switch id { case 0: lo = big.NewRat(1<<53-1<<3, 1<<54) hi = big.NewRat(1<<53+1<<3, 1<<54) locap = "1/2 - 1/2^51" hicap = "1/2 + 1/2^51" scalecap = "1/2^53" case 1: lo = big.NewRat(1<<54-1<<4, 1<<54) hi = big.NewRat(1<<54, 1<<54) locap = "1 - 1/2^50" hicap = "1" scalecap = "1/2^53" case 2: lo = big.NewRat(0, 1<<54) hi = big.NewRat(1<<4, 1<<54) locap = "0" hicap = "1/2^50" scalecap = "1/2^53" case 3: lo = big.NewRat(0, 1<<54) hi = big.NewRat(1<<2, 1<<62) locap = "0" hicap = "1/2^59" scalecap = "1/2^63" } mode, _ := strconv.Atoi(req.FormValue("mode")) path = plotPath(ratProb(mode), lo, lo, hi, hi, window) r.AddStroke(path, fixed.I(1), raster.ButtCapper, raster.BevelJoiner) r.Rasterize(p) var modestr string switch mode { case 0: modestr = "original behavior (can return 1)" case 1: modestr = "new behavior (too much 0)" case 2: modestr = "retry loop" } data, err := ioutil.ReadFile("/tmp/luxisr.ttf") if err != nil { panic(err) } tfont, err := freetype.ParseFont(data) if err != nil { panic(err) } const pt = 10 ft := freetype.NewContext() ft.SetDst(c) ft.SetDPI(100) ft.SetFont(tfont) ft.SetFontSize(float64(pt)) ft.SetSrc(image.NewUniform(color.Black)) ft.SetClip(image.Rect(0, 0, 0, 0)) wid, err := ft.DrawString(locap, freetype.Pt(0, 0)) if err != nil { panic(err) } pp := freetype.Pt(50, 480) pp.X -= wid.X / 2 ft.SetClip(c.Bounds()) ft.DrawString(locap, pp) ft.SetClip(image.Rect(0, 0, 0, 0)) wid, err = ft.DrawString(hicap, freetype.Pt(0, 0)) if err != nil { panic(err) } pp = freetype.Pt(450, 480) pp.X -= wid.X / 2 ft.SetClip(c.Bounds()) ft.DrawString(hicap, pp) r.Clear() p.SetColor(color.Black) path.Clear() const dy = 5 path.Start(nudge(fixed.P(400, 400))) path.Add1(nudge(fixed.P(400, 400-dy))) path.Add1(nudge(fixed.P(400, 400+dy))) path.Add1(nudge(fixed.P(400, 400))) path.Add1(nudge(fixed.P(450, 400))) path.Add1(nudge(fixed.P(450, 400-dy))) path.Add1(nudge(fixed.P(450, 400+dy))) path.Add1(nudge(fixed.P(450, 400))) r.AddStroke(path, fixed.I(2), raster.ButtCapper, raster.BevelJoiner) r.Rasterize(p) ft.SetClip(image.Rect(0, 0, 0, 0)) wid, err = ft.DrawString(scalecap, freetype.Pt(0, 0)) if err != nil { panic(err) } pp = freetype.Pt(425, 420) pp.X -= wid.X / 2 ft.SetClip(c.Bounds()) ft.DrawString(scalecap, pp) ft.SetClip(image.Rect(0, 0, 0, 0)) wid, err = ft.DrawString(modestr, freetype.Pt(0, 0)) if err != nil { panic(err) } pp = freetype.Pt(250, 490) pp.X -= wid.X / 2 ft.SetClip(c.Bounds()) ft.DrawString(modestr, pp) w.Write(pngEncode(c)) }