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) } }
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.") }
// NewGraphicContext creates a new Graphic context from an image. func NewGraphicContext(img draw.Image) *GraphicContext { var painter Painter switch selectImage := img.(type) { case *image.RGBA: painter = raster.NewRGBAPainter(selectImage) default: panic("Image type not supported") } return NewGraphicContextWithPainter(img, painter) }
// NewPainter returns a Painter that will draw from src onto // dst using the Porter-Duff composition operator op. func NewPainter(dst draw.Image, src image.Image, op draw.Op) (p raster.Painter) { defer func() { fmt.Printf("newpainter %T\n", p) }() if src, ok := src.(*image.Uniform); ok { switch dst := dst.(type) { case *image.Alpha: if _, _, _, a := src.RGBA(); a == 0xffff { switch op { case draw.Src: return &raster.AlphaSrcPainter{dst} case draw.Over: return &raster.AlphaOverPainter{dst} } } case *image.RGBA: p := raster.NewRGBAPainter(dst) p.SetColor(src) p.Op = op return p } } return &genericImagePainter{dst, src, op} }
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 genTwitterGif(tweets []anaconda.Tweet, username string, tid int64) (string, error) { wid := 440 height := 220 colList := color.Palette{ color.RGBA{0x00, 0x00, 0x00, 0xFF}, color.RGBA{0xFF, 0x33, 0x33, 0xFF}, color.RGBA{0x33, 0xFF, 0x33, 0xFF}, color.RGBA{0x33, 0x33, 0xFF, 0xFF}, color.RGBA{0xFF, 0xFF, 0x33, 0xFF}, color.RGBA{0xFF, 0x33, 0xFF, 0xFF}, color.RGBA{0x33, 0xFF, 0xFF, 0xFF}, } newList := []*image.Paletted{} delayList := []int{} fireworkList := []FireWork{} disposalList := []byte{} draw2d.SetFontFolder("static") for i := range tweets { f := genFireworkFromTweet(tweets, i, float64(wid), float64(height)) fireworkList = append(fireworkList, f) } boundRect := image.Rect(0, 0, wid, height) for len(fireworkList) > 0 { rawImg := image.NewRGBA(boundRect) // TODO :: Create Custom Painter // which does blend up painter := raster.NewRGBAPainter(rawImg) gc := draw2dimg.NewGraphicContextWithPainter(rawImg, painter) gc.SetFontData(draw2d.FontData{ Name: "Roboto", }) gc.SetFontSize(8) gc.Clear() gc.SetFillColor(colList[0]) gc.MoveTo(0, 0) gc.LineTo(0, float64(height)) gc.LineTo(float64(wid), float64(height)) gc.LineTo(float64(wid), 0) gc.Close() gc.Fill() newFList := []FireWork{} for _, f := range fireworkList { if f.d > 0 { f.d -= 1.0 } else { gc.SetFillColor(colList[f.colID]) gc.SetStrokeColor(colList[f.colID]) gc.MoveTo(f.x, f.y) gc.FillStringAt(f.text, f.x-4, f.y+4) gc.MoveTo(f.x, f.y) gc.SetLineWidth(f.sz) gc.LineTo(f.x-f.dx, f.y-f.dy) for ns := 1.0; ns < f.sz; ns += 1.0 { gc.SetLineWidth(f.sz - ns) gc.LineTo(f.x-f.dx*ns*0.2, f.y-f.dy*ns*0.2) } gc.Stroke() f.x += f.dx f.y += f.dy f.t -= 1.0 f.dy += 0.3 } if f.t > 0 { newFList = append(newFList, f) } else if len(f.bundle) > 0 { for _, subF := range f.bundle { subF.x += f.x subF.y += f.y newFList = append(newFList, subF) } } } fireworkList = newFList // Make Pallette Image newImg := image.NewPaletted(boundRect, colList) for x := 0; x < wid; x++ { for y := 0; y < height; y++ { newImg.SetColorIndex(x, y, uint8(colList.Index(rawImg.At(x, y)))) } } // Add Lists if len(newList) == 0 { disposalList = append(disposalList, gif.DisposalNone) } else { disposalList = append(disposalList, gif.DisposalPrevious) } newList = append(newList, newImg) delayList = append(delayList, 10) } log.Println("Saving gif with ", len(newList), " frames") gifData := gif.GIF{ Image: newList, Delay: delayList, Disposal: disposalList, LoopCount: -1, BackgroundIndex: 0, Config: image.Config{ ColorModel: colList, Width: wid, Height: height, }, } fn := MakeGifFilename(username, tid) f, e := os.Create(fn) if e != nil { log.Println(e) return "", e } e = gif.EncodeAll(f, &gifData) if e != nil { log.Println(e) return "", e } return fn, nil }
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)) }