func drawPerimeterNumber(dst draw.Image, pt image.Point, number string) { dot := fixed.P(pt.X, pt.Y) d := font.Drawer{ Dst: dst, Src: image.NewUniform(color.RGBA{B: 0xFF, A: 0xFF}), Face: basicfont.Face7x13, Dot: dot, } d.DrawString(number) }
func (c *Context) Render(txt string, size float64, col color.Color) (*image.RGBA, error) { bnd := c.fnt.Bounds(fixed.I(int(size + 0.5))) lh := int26_6ToFloat64(bnd.Max.Y) - int26_6ToFloat64(bnd.Min.Y) - 0.5 c.ft.SetSrc(image.NewUniform(col)) c.ft.SetFontSize(size) /* Render image to temporary buffer to determine final size */ tmp := nullImage{} c.ft.SetDst(tmp) c.ft.SetClip(tmp.Bounds()) p, err := c.ft.DrawString(txt, fixed.P(0, int(lh))) if err != nil { return nil, err } dst := image.NewRGBA(image.Rect(0, 0, int(int26_6ToFloat64(p.X)+0.5), int(lh))) draw.Draw(dst, dst.Bounds(), image.NewUniform(color.RGBA{}), image.ZP, draw.Src) c.ft.SetDst(dst) c.ft.SetClip(dst.Bounds()) p, err = c.ft.DrawString(txt, fixed.P(0, int(size))) if err != nil { return nil, err } return dst, nil }
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.") }
func TestHorizontalLayout(t *testing.T) { layout := NewHorizontalLayout(fixed.P(10, 10), fixed.P(100, 100)) if r, ok := layout.NextBounds(); !ok { t.Error("no bounds returned by the horizontal layout") } else if r != fixed.R(10, 10, 110, 110) { t.Error("invalid bounds returned by the horizontal layout:", r) } if r, ok := layout.NextBounds(); !ok { t.Error("no bounds returned by the horizontal layout") } else if r != fixed.R(110, 10, 210, 110) { t.Error("invalid bounds returned by the horizontal layout:", r) } }
func BenchmarkDrawString(b *testing.B) { data, err := ioutil.ReadFile("../licenses/gpl.txt") if err != nil { b.Fatal(err) } lines := strings.Split(string(data), "\n") data, err = ioutil.ReadFile("../testdata/luxisr.ttf") if err != nil { b.Fatal(err) } f, err := Parse(data) if err != nil { b.Fatal(err) } dst := image.NewRGBA(image.Rect(0, 0, 800, 600)) draw.Draw(dst, dst.Bounds(), image.White, image.ZP, draw.Src) d := &font.Drawer{ Dst: dst, Src: image.Black, Face: NewFace(f, nil), } b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { for j, line := range lines { d.Dot = fixed.P(0, (j*16)%600) d.DrawString(line) } } }
func TestDrawView(t *testing.T) { const str = "Hello World!\nHow are you doing?" f1 := truetype.NewFace(clearSans, &truetype.Options{DPI: 144}) f2 := truetype.NewFace(clearSansBoldItalic, &truetype.Options{DPI: 144}) red := image.NewUniform(color.RGBA{255, 0, 0, 255}) yellow := image.NewUniform(color.RGBA{255, 255, 0, 255}) view, _ := Render( NewReader( strings.NewReader("Hello World!\nHow are you doing?"), Style{Offset: 0, Face: f1, Foreground: image.Black, Background: yellow}, Style{Offset: 10, Face: f2, Foreground: red, Background: yellow}, Style{Offset: 20, Face: f1, Foreground: image.Black, Background: image.White}, ), NewNaturalLayout(fixed.P(0, 0)), ) size := view.Bounds.Max.Sub(view.Bounds.Min) for _, a := range []Alignment{Left, Right, Center, Justify} { dst := image.NewRGBA(image.Rect(0, 0, int(size.X>>6)+1, int(size.Y>>6)+1)) view.Align(a) view.Draw(dst, LeftToRight) saveTest(t, dst, "text.View.Draw_"+a.(fmt.Stringer).String()+".png") } }
func (l *Label) newTextTexture(eng sprite.Engine) sprite.SubTex { fg, bg := image.Black, image.White draw.Draw(l.rgba, l.rgba.Bounds(), bg, image.ZP, draw.Src) d := &sfont.Drawer{ Dst: l.rgba, Src: fg, Face: truetype.NewFace(l.font, truetype.Options{ Size: l.fontSize, DPI: 72, Hinting: sfont.HintingFull, }), } spacing := 1.5 dy := int(math.Ceil(l.fontSize * spacing)) for i, s := range strings.Split(l.Text, "\n") { d.Dot = fixed.P(0, int(l.fontSize*0.8)+dy*i) d.DrawString(s) } t, err := eng.LoadTexture(l.rgba) if err != nil { log.Fatal(err) } return sprite.SubTex{t, l.rgba.Bounds()} }
func drawLayerNumber(dst draw.Image, n int) { d := font.Drawer{ Dst: dst, Src: image.Black, Face: basicfont.Face7x13, Dot: fixed.P(2, 13), } d.DrawString(fmt.Sprintf("Layer %03d", n)) }
func TestVerticalLayout(t *testing.T) { layout := NewVerticalLayout(fixed.P(10, 10), fixed.I(100)) if r, ok := layout.NextBounds(); !ok { t.Error("no bounds returned by the vertical layout") } else if r != fixed.R(10, 10, 110, 33554431) { t.Error("invalid bounds returned by the vertical layout:", r) } if _, ok := layout.NextBounds(); ok { t.Error("incomplete vertical layout") } }
func (f *Font) RenderNRGBA(text string) *image.NRGBA { width, height, yBearing := f.TextDimensions(text) font := f.TTF size := f.Size if size <= 0 { panic("Font size cannot be <= 0") } // Default colors if f.FG == nil { f.FG = color.NRGBA{0, 0, 0, 0} } if f.BG == nil { f.BG = color.NRGBA{0, 0, 0, 0} } // Colors fg := image.NewUniform(f.FG) bg := image.NewUniform(f.BG) // Create the font context c := freetype.NewContext() nrgba := image.NewNRGBA(image.Rect(0, 0, width, height)) draw.Draw(nrgba, nrgba.Bounds(), bg, image.ZP, draw.Src) c.SetDPI(dpi) c.SetFont(font) c.SetFontSize(size) c.SetClip(nrgba.Bounds()) c.SetDst(nrgba) c.SetSrc(fg) // Draw the text. pt := fixed.P(0, int(yBearing)) _, err := c.DrawString(text, pt) if err != nil { log.Println(err) return nil } return nrgba }
func TestCharAtSuccess(t *testing.T) { f := truetype.NewFace(clearSans, nil) defer f.Close() c := NewReader(strings.NewReader("Hello World!\n"), Style{Offset: 0, Face: f, Foreground: image.Black, Background: image.White}, ) view, err := Render(c, NewNaturalLayout(fixed.P(1, 1))) if err != nil { t.Error(err) return } char, bounds, ok := view.CharAt(fixed.Point26_6{ X: int26_6(31, 0), Y: int26_6(5, 0), }, LeftToRight) if !ok { t.Error("no char found") return } if char != (Char{ Offset: 5, Rune: ' ', Face: f, Foreground: image.Black, Background: image.White, }) { t.Error("invalid char found:", char) } if bounds != (fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(28, 37), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(31, 35), Y: int26_6(15, 5)}, }) { t.Error("invalid char bounds:", bounds) } }
func (t *TextTexture) Create(eng sprite.Engine, text string) (sprite.SubTex, error) { draw.Draw(t.rgba, t.rgba.Bounds(), t.bg, image.ZP, draw.Src) d := &sfont.Drawer{ Dst: t.rgba, Src: t.fg, Face: t.Face, } dy := int(math.Ceil(t.Face.Size * t.Spacing)) for i, s := range strings.Split(text, "\n") { d.Dot = fixed.P(0, int(t.Face.Size*0.8)+dy*i) d.DrawString(s) } tex, err := eng.LoadTexture(t.rgba) if err != nil { return sprite.SubTex{}, err } return sprite.SubTex{tex, t.rgba.Bounds()}, nil }
func (f *Font) Render(text string, width, height float32, color Color) *Texture { /* Set color */ f.drawer.Src = image.NewUniform(color.RGBA()) line := math.Ceil(f.Size * f.DPI / 72) //height := int(f.Spacing * line) //width := int(float64(f.drawer.MeasureString(text)) / f.Size) /* Create and attach destination image */ rgba := image.NewRGBA(image.Rect(0, 0, int(width), int(height))) f.drawer.Dst = rgba /* Draw text */ f.drawer.Dot = fixed.P(0, int(line)) f.drawer.DrawString(text) fmt.Println("Font texture size W:", width, "H:", height) tx := CreateTexture(int32(width), int32(height)) tx.Buffer(rgba) return tx }
func TestCharAtFailure(t *testing.T) { f := truetype.NewFace(clearSans, nil) defer f.Close() c := NewReader(strings.NewReader("Hello World!\n"), Style{Offset: 0, Face: f, Foreground: image.Black, Background: image.White}, ) view, err := Render(c, NewNaturalLayout(fixed.P(1, 1))) if err != nil { t.Error(err) return } char, bounds, ok := view.CharAt(fixed.Point26_6{}, LeftToRight) if char != (Char{}) || bounds != (fixed.Rectangle26_6{}) || ok { t.Error("not char should have been found at (0, 0) but we got", char, bounds, ok) } }
func (app *App) drawText(img draw.Image, metrics *battery.Metrics, f battery.MetricFormatter) error { // measure the text so that it can be centered within the text area. if f // is a MaxMetricFormatter use it's MaxFormattedWidth method to determine // the appropriate centering position so that a change in metric values // (but not formatter) will have a smooth transition in the ui. app.font.Dst = img text := f.Format(metrics) measuretext := text if fmax, ok := f.(battery.MaxMetricFormatter); ok { measuretext = fmax.MaxFormattedWidth() } xoffset := app.font.MeasureString(measuretext) ttwidth := int(xoffset >> 6) ttheight := int(app.tt.PointToFixed(app.Layout.fontSize) >> 6) padleft := (app.Layout.textRect.Size().X - ttwidth) / 2 padtop := (app.Layout.textRect.Size().Y - ttheight) / 2 x := app.Layout.textRect.Min.X + padleft y := app.Layout.textRect.Max.Y - padtop app.font.Dot = fixed.P(x, y) app.font.DrawString(text) return nil }
func parseFont() error { f, err := ebitenutil.OpenFile("_resources/fonts/mplus-1p-regular.ttf") if err != nil { return err } defer func() { _ = f.Close() }() b, err := ioutil.ReadAll(f) if err != nil { return err } tt, err := truetype.Parse(b) if err != nil { return err } w, h := textImage.Size() dst := image.NewRGBA(image.Rect(0, 0, w, h)) const size = 24 const dpi = 72 d := &font.Drawer{ Dst: dst, Src: image.White, Face: truetype.NewFace(tt, &truetype.Options{ Size: size, DPI: dpi, Hinting: font.HintingFull, }), } y := size for _, s := range text { d.Dot = fixed.P(0, y) d.DrawString(s) y += size } return textImage.ReplacePixels(dst.Pix) }
func (atlas *FontAtlas) loadGlyph(r rune) { if _, ok := atlas.Rendered[r]; ok { return } atlas.Dirty = true glyph := Glyph{} glyph.Rune = r bounds, advance, _ := atlas.Face.GlyphBounds(r) glyph.Bounds = bounds glyph.Advance = advance width := ceilPx(bounds.Max.X-bounds.Min.X) + glyphPadding*2 height := ceilPx(bounds.Max.Y-bounds.Min.Y) + glyphPadding*2 if atlas.CursorX+glyphMargin+width+glyphMargin > atlas.Image.Bounds().Dx() { atlas.CursorX = 0 atlas.CursorY += glyphMargin + atlas.maxGlyphInRow } x := atlas.CursorX + glyphMargin y := atlas.CursorY + glyphMargin glyph.Loc = image.Rect(x, y, x+width, y+height) glyph.RelLoc = RelBounds(glyph.Loc, atlas.Image.Bounds()) pt := fixed.P(x+glyphPadding, y+glyphPadding).Sub(bounds.Min) atlas.Context.DrawString(string(r), pt) if height > atlas.maxGlyphInRow { atlas.maxGlyphInRow = height } atlas.CursorX += glyphMargin + width + glyphMargin atlas.Rendered[r] = glyph }
func (atlas *FontAtlas) Draw(text string, b Bounds) { m := atlas.Face.Metrics() w := pow2(font.MeasureString(atlas.Face, text).Ceil()) h := pow2(m.Ascent.Ceil() + m.Descent.Ceil()) if w > 2048 { w = 2048 } if h > 2048 { h = 2048 } b.Max.X = b.Min.X + float32(w) b.Max.Y = b.Min.Y + float32(h) rendered := image.NewRGBA(image.Rect(0, 0, w, h)) drawer := font.Drawer{ Dst: rendered, Src: image.Black, Face: atlas.Face, } drawer.Dot = fixed.P(0, m.Ascent.Ceil()) drawer.DrawString(text) atlas.draw(rendered, b) }
func main() { flag.Parse() if *flagFontfile == "" || *flagOutdir == "" || *flagPkgname == "" { flag.Usage() return } if *flagScale < 1 { log.Println("scale must be >= 1") return } bin, err := ioutil.ReadFile(*flagFontfile) if err != nil { log.Println(err) return } f, err := truetype.Parse(bin) if err != nil { log.Println(err) return } sdf := NewSDF(*flagTSize, *flagFSize, *flagPad, *flagScale) d := &font.Drawer{ Dst: sdf.src, Src: image.Black, Face: truetype.NewFace(f, &truetype.Options{ Size: sdf.fsize, Hinting: font.HintingNone, }), } var glyphs []*glyph if *flagAscii { glyphs = fromString(ascii, d.Face) } else { glyphs = enumerate(f, d.Face) } if len(glyphs) == 0 { log.Fatalf("sdf: failed to enumerate glyphs from %s\n", *flagFontfile) } if a := area(glyphs, sdf.pad); a > sdf.tsize*sdf.tsize { asq := math.Sqrt(float64(a)) log.Fatalf("sdf: glyphs area %[1]v ~= %.2[2]fx%.2[2]f greater than texture area %[3]vx%[3]v\n", a, asq, sdf.tsize) } sort.Sort(byHeight(glyphs)) x, y, dy := 0, 0, glyphs[0].height()+sdf.pad*2 var wg sync.WaitGroup for _, g := range glyphs { adx, ady := g.width()+sdf.pad*2, g.height()+sdf.pad*2 if x+adx > sdf.tsize { x = 0 y += dy dy = ady } g.tc = [4]float32{ float32(x+sdf.pad) / float32(sdf.tsize), float32(y+sdf.pad) / float32(sdf.tsize), float32(g.width()) / float32(sdf.tsize), float32(g.height()) / float32(sdf.tsize), } d.Dot = fixed.P(x+sdf.pad-int(g.b.Min.X>>6), y+sdf.pad-g.b.Min.Y.Ceil()) d.DrawString(string(g.r)) wg.Add(1) go func(m image.Image) { sdf.calc(m) wg.Done() }(sdf.src.SubImage(image.Rect(x, y, x+adx, y+ady))) x += adx } wg.Wait() sdf.writeSrc() sdf.writeDst() sdf.writeOut() // generate source file to accompany out.png buf := new(bytes.Buffer) buf.WriteString("// generated by gen.go; DO NOT EDIT\n") fmt.Fprintf(buf, "package %s\n\n", *flagPkgname) ascent := float32(d.Face.Metrics().Ascent.Ceil()) descent := float32(d.Face.Metrics().Descent.Floor()) scale := float32(sdf.fsize) / (ascent + descent) fmt.Fprintf(buf, "const AscentUnit = %v\n", (ascent*scale)/float32(sdf.fsize)) fmt.Fprintf(buf, "const DescentUnit = %v\n", (descent*scale)/float32(sdf.fsize)) fmt.Fprintf(buf, "const TextureSize = %v\n", *flagTSize) fmt.Fprintf(buf, "const FontSize = %v\n", *flagFSize) fmt.Fprintf(buf, "const Pad = %v\n\n", *flagPad) buf.WriteString("var Texcoords = map[rune][4]float32{\n") for _, g := range glyphs { s := string(g.r) if s == "'" { s = `\'` } else if s == "\\" { s = `\\` } fmt.Fprintf(buf, "\t'%s': {%v, %v, %v, %v},\n", s, g.tc[0], g.tc[1], g.tc[2], g.tc[3]) } buf.WriteString("}\n\n") buf.WriteString("var Bounds = map[rune][5]float32{\n") for _, g := range glyphs { s := string(g.r) if s == "'" { s = `\'` } else if s == "\\" { s = `\\` } nx := float32(g.b.Min.X>>6) / float32(sdf.fsize) ny := float32(g.b.Max.Y>>6) / float32(sdf.fsize) rect := g.b.Max.Sub(g.b.Min) w, h := float32(rect.X>>6), float32(rect.Y>>6) nw := float32(w) / float32(sdf.fsize) nh := float32(h) / float32(sdf.fsize) na := float32(g.a>>6) / float32(sdf.fsize) fmt.Fprintf(buf, "\t'%s': {%v, %v, %v, %v, %v},\n", s, nx, ny, nw, nh, na) } buf.WriteString("}\n\n") if err := ioutil.WriteFile(filepath.Join(*flagOutdir, "texcoords.go"), buf.Bytes(), 0644); err != nil { log.Fatal(err) } }
func ExampleParseFont() { 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.Fatal(err) } face, err := plan9font.ParseFont(fontData, readFile) if err != nil { log.Fatal(err) } // TODO: derive the ascent from the face's metrics. const ascent = 11 dst := image.NewRGBA(image.Rect(0, 0, 4*7, 13)) draw.Draw(dst, dst.Bounds(), image.Black, image.Point{}, draw.Src) d := &font.Drawer{ Dst: dst, Src: image.White, Face: face, Dot: fixed.P(0, ascent), } // Draw: // - U+0053 LATIN CAPITAL LETTER S // - U+03A3 GREEK CAPITAL LETTER SIGMA // - U+222B INTEGRAL // - U+3055 HIRAGANA LETTER SA // The testdata does not contain the CJK subfont files, so U+3055 HIRAGANA // LETTER SA (さ) should be rendered as U+FFFD REPLACEMENT CHARACTER (�). // // The missing subfont file will trigger an "open // ../testdata/shinonome/k12.3000: no such file or directory" log message. // This is expected and can be ignored. d.DrawString("SΣ∫さ") // Convert the dst image to ASCII art. var out []byte b := dst.Bounds() for y := b.Min.Y; y < b.Max.Y; y++ { out = append(out, '0'+byte(y%10), ' ') for x := b.Min.X; x < b.Max.X; x++ { if dst.RGBAAt(x, y).R > 0 { out = append(out, 'X') } else { out = append(out, '.') } } // Highlight the last row before the baseline. Glyphs like 'S' without // descenders should not affect any pixels whose Y coordinate is >= the // baseline. if y == ascent-1 { out = append(out, '_') } out = append(out, '\n') } os.Stdout.Write(out) // Output: // 0 ..................X......... // 1 .................X.X........ // 2 .XXXX..XXXXXX....X.....XXX.. // 3 X....X.X.........X....XX.XX. // 4 X.......X........X....X.X.X. // 5 X........X.......X....XXX.X. // 6 .XXXX.....X......X....XX.XX. // 7 .....X...X.......X....XX.XX. // 8 .....X..X........X....XXXXX. // 9 X....X.X.........X....XX.XX. // 0 .XXXX..XXXXXX....X.....XXX.._ // 1 ...............X.X.......... // 2 ................X........... }
func TestAlign(t *testing.T) { f := truetype.NewFace(clearSans, nil) defer f.Close() view, _ := Render( NewReader( strings.NewReader("Hello World!\nHow are you doing?"), Style{Offset: 0, Face: f, Foreground: image.Black, Background: image.White}, ), NewNaturalLayout(fixed.P(1, 1)), ) // -- test right alignment -- view.Align(Right) if !reflect.DeepEqual(view, View{ Frames: []Frame{{ Lines: []Line{ { Runs: []Run{{ Offset: 0, Text: "Hello World!", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(41, 6), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(15, 5)}, }, }}, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(41, 6), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(15, 5)}, }, }, { LF: Char{ Offset: 12, Rune: '\n', Face: f, Foreground: image.Black, Background: image.White, }, Runs: []Run{{ Offset: 13, Text: "How are you doing?", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }}, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }, }, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }}, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }) { t.Error("invalid right alignment:", view) } // -- test center alignment -- view.Align(Center) if !reflect.DeepEqual(view, View{ Frames: []Frame{{ Lines: []Line{ { Runs: []Run{{ Offset: 0, Text: "Hello World!", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(21, 3), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(85, 58), Y: int26_6(15, 5)}, }, }}, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(21, 3), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(85, 58), Y: int26_6(15, 5)}, }, }, { LF: Char{ Offset: 12, Rune: '\n', Face: f, Foreground: image.Black, Background: image.White, }, Runs: []Run{{ Offset: 13, Text: "How are you doing?", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }}, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }, }, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }}, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }) { t.Error("invalid center alignment:", view) } // -- test left alignment -- view.Align(Left) if !reflect.DeepEqual(view, View{ Frames: []Frame{{ Lines: []Line{ { Runs: []Run{{ Offset: 0, Text: "Hello World!", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(65, 55), Y: int26_6(15, 5)}, }, }}, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(65, 55), Y: int26_6(15, 5)}, }, }, { LF: Char{ Offset: 12, Rune: '\n', Face: f, Foreground: image.Black, Background: image.White, }, Runs: []Run{{ Offset: 13, Text: "How are you doing?", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }}, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }, }, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }}, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }) { t.Error("invalid left alignment:", view) } // -- test justify alignment -- view.Align(Justify) if !reflect.DeepEqual(view, View{ Frames: []Frame{{ Lines: []Line{ { Runs: []Run{ { Offset: 0, Text: "Hello", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(28, 37), Y: int26_6(15, 5)}, }, }, { Offset: 5, Text: " ", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(28, 37), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(71, 41), Y: int26_6(15, 5)}, }, }, { Offset: 6, Text: "World!", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(71, 41), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(15, 5)}, }, }, }, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(15, 5)}, }, }, { LF: Char{ Offset: 12, Rune: '\n', Face: f, Foreground: image.Black, Background: image.White, }, Runs: []Run{ { Offset: 13, Text: "How", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(25, 27), Y: int26_6(27, 5)}, }, }, { Offset: 16, Text: " ", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(25, 27), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(28, 25), Y: int26_6(27, 5)}, }, }, { Offset: 17, Text: "are", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(28, 25), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(45, 22), Y: int26_6(27, 5)}, }, }, { Offset: 20, Text: " ", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(45, 22), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(48, 20), Y: int26_6(27, 5)}, }, }, { Offset: 21, Text: "you", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(48, 20), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(67, 28), Y: int26_6(27, 5)}, }, }, { Offset: 24, Text: " ", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(67, 28), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(70, 26), Y: int26_6(27, 5)}, }, }, { Offset: 25, Text: "doing?", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(70, 26), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }, }, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }, }, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }}, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(105, 61), Y: int26_6(27, 5)}, }, }) { t.Error("invalid text justification:", view) } }
func TestRenderSimpleText(t *testing.T) { f := truetype.NewFace(clearSans, nil) defer f.Close() c := NewReader(strings.NewReader("Hello World!\n"), Style{Offset: 0, Face: f, Foreground: image.Black, Background: image.White}, Style{Offset: 5, Face: f, Foreground: image.White}, ) view, err := Render(c, NewNaturalLayout(fixed.P(1, 1))) if err != nil { t.Error(err) } if !reflect.DeepEqual(view, View{ Frames: []Frame{ { Lines: []Line{ { Runs: []Run{ { Offset: 0, Text: "Hello", Face: f, Foreground: image.Black, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(28, 37), Y: int26_6(15, 5)}, }, }, { Offset: 5, Text: " World!", Face: f, Foreground: image.White, Background: image.White, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(28, 37), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(65, 55), Y: int26_6(15, 5)}, }, }, }, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(65, 55), Y: int26_6(15, 5)}, }, }, { LF: Char{ Offset: 12, Rune: '\n', Face: f, Foreground: image.White, Background: image.White, }, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(13, 0)}, Max: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(25, 0)}, }, }, }, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(65, 55), Y: int26_6(25, 0)}, }, }, }, Bounds: fixed.Rectangle26_6{ Min: fixed.Point26_6{X: int26_6(1, 0), Y: int26_6(1, 0)}, Max: fixed.Point26_6{X: int26_6(65, 55), Y: int26_6(25, 0)}, }, }) { t.Error("invalid view rendered from non-empty string:", view) } }
func main() { flag.Parse() // TODO: mmap the files. if *fontFlag == "" { flag.Usage() log.Fatal("no font specified") } var face font.Face if strings.HasSuffix(*fontFlag, ".font") { fontData, err := ioutil.ReadFile(*fontFlag) if err != nil { log.Fatal(err) } dir := filepath.Dir(*fontFlag) face, err = plan9font.ParseFont(fontData, func(name string) ([]byte, error) { return ioutil.ReadFile(filepath.Join(dir, filepath.FromSlash(name))) }) if err != nil { log.Fatal(err) } } else { fontData, err := ioutil.ReadFile(*fontFlag) if err != nil { log.Fatal(err) } face, err = plan9font.ParseSubfont(fontData, rune(*firstRuneFlag)) if err != nil { log.Fatal(err) } } dst := image.NewRGBA(image.Rect(0, 0, 800, 300)) draw.Draw(dst, dst.Bounds(), image.Black, image.Point{}, draw.Src) d := &font.Drawer{ Dst: dst, Src: image.White, Face: face, } ss := []string{ "The quick brown fox jumps over the lazy dog.", "Hello, 世界.", "U+FFFD is \ufffd.", } for i, s := range ss { d.Dot = fixed.P(20, 100*i+80) dot0 := pt(d.Dot) d.DrawString(s) dot1 := pt(d.Dot) dst.SetRGBA(dot0.X, dot0.Y, color.RGBA{0xff, 0x00, 0x00, 0xff}) dst.SetRGBA(dot1.X, dot1.Y, color.RGBA{0x00, 0x00, 0xff, 0xff}) } out, err := os.Create("out.png") if err != nil { log.Fatal(err) } defer out.Close() if err := png.Encode(out, dst); err != nil { log.Fatal(err) } }
// returns image of rendered font func (this FontInfo) GetImage() image.Image { f := truetype.Font(this) maxWidth := 0 curW := 0 maxHeight := int(f.Bounds(sizeFixed).Max.Sub(f.Bounds(sizeFixed).Min).Y) + insetPadding for idx, val := range text { if idx > 0 && int(math.Mod(float64(idx), float64(itemsPerRow))) == 0 { maxHeight += int(f.Bounds(sizeFixed).Max.Sub(f.Bounds(sizeFixed).Min).Y) + insetPadding curW = 0 } i := f.Index(val) hmet := f.HMetric(sizeFixed, i) curW += int(hmet.AdvanceWidth) + insetPadding if curW > maxWidth { maxWidth = curW } } Logging.Debug("Font ", this.Name()) Logging.Debug("Max width: ", maxWidth) Logging.Debug("Max height: ", maxHeight) // Draw the background and the guidelines. fg, bg := image.Black, image.Transparent // diffY := int(f.Bounds(sizeFixed).Max.Sub(f.Bounds(sizeFixed).Min).Y) // add a constant to the y term because of subtle bleeding between vertical space, probably because of advance height rgba := image.NewRGBA(image.Rect(0, 0, maxWidth, maxHeight)) //diffY+5)) draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src) // Draw the text. h := font.HintingNone switch hinting { case "full": h = font.HintingFull } d := &font.Drawer{ Dst: rgba, Src: fg, Face: truetype.NewFace(&f, &truetype.Options{ Size: size, DPI: dpi, Hinting: h, }), } // y := int(math.Abs(float64(f.Bounds(sizeFixed).Max.Y))) //10 + int(math.Ceil(*size**dpi/72)) // dy := int(math.Ceil(size * spacing * dpi / 72)) tWidth := 0 tHeight := int(math.Abs(float64(f.Bounds(sizeFixed).Max.Y))) for idx, r := range text { if idx > 0 && int(math.Mod(float64(idx), float64(itemsPerRow))) == 0 { tHeight += int(f.Bounds(sizeFixed).Max.Sub(f.Bounds(sizeFixed).Min).Y) + insetPadding tWidth = 0 } i := f.Index(r) hmet := f.HMetric(sizeFixed, i) d.Dot = fixed.P(tWidth, tHeight) tWidth += int(hmet.AdvanceWidth) + insetPadding d.DrawString(string(r)) } return rgba }
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) } }
func createImages(dir, mode string) { os.Mkdir(dir, 0777) //Load font file b, err := ioutil.ReadFile("Arial.ttf") if err != nil { log.Println(err) return } //Parse font file ttf, err := truetype.Parse(b) if err != nil { log.Println(err) return } //Create Font.Face from font face := truetype.NewFace(ttf, &truetype.Options{ Size: size, DPI: dpi, Hinting: font.HintingNone, }) //Create initial matrix m := make([]Bit, 0) for y := 17; y <= height; y = y + int(size) { for x := 4; x <= width; x = x + int(size) { val := "1" rand.Seed(time.Now().UnixNano()) if rand.Intn(2) == 1 { val = "0" } m = append(m, Bit{X: x, Y: y, Val: val}) } } //Create next matrix mm := make([][]Bit, 0) mm = append(mm, m) //Get a random list rand.Seed(time.Now().UnixNano()) randBit := rand.Perm(len(m)) //Create each next matrix for i := 0; i < len(randBit); i++ { tmpM := make([]Bit, len(m)) //Copy last matrix copy(tmpM, mm[len(mm)-1]) //Update one bit tmpBit := tmpM[randBit[i]] if tmpBit.Val == "0" { tmpBit.Val = "1" } else { tmpBit.Val = "0" } //Save bit in matrix tmpM[randBit[i]] = tmpBit //Append new matrix mm = append(mm, tmpM) } //Get a random list rand.Seed(time.Now().UnixNano()) randBit = rand.Perm(len(m)) //Create each next matrix for i := 0; i < len(randBit); i++ { tmpM := make([]Bit, len(m)) //Copy last matrix copy(tmpM, mm[len(mm)-1]) //Update one bit tmpBit := tmpM[randBit[i]] if tmpBit.Val == "0" { tmpBit.Val = "1" } else { tmpBit.Val = "0" } //Save bit in matrix tmpM[randBit[i]] = tmpBit //Append new matrix mm = append(mm, tmpM) } //Create all matrix images for i := 0; i < len(mm); i++ { if i%100 == 0 { log.Printf("i:%d/%d", i, len(mm)) } //Create template images src := image.NewNRGBA(image.Rect(0, 0, width, height)) dst := image.NewNRGBA(image.Rect(0, 0, width, height)) for y := 0; y < height; y++ { for x := 0; x < width; x++ { if mode == "black" { src.Set(x, y, color.NRGBA{uint8(255), uint8(255), uint8(255), 255}) dst.Set(x, y, color.NRGBA{uint8(0), uint8(0), uint8(0), 255}) } else { src.Set(x, y, color.NRGBA{uint8(0), uint8(0), uint8(0), 255}) dst.Set(x, y, color.NRGBA{uint8(255), uint8(255), uint8(255), 255}) } } } //Create file f, err := os.OpenFile(fmt.Sprintf("%s/%d.png", dir, i), os.O_CREATE|os.O_WRONLY, 0666) if err != nil { log.Fatalf("OpenFile - Err:%s", err) } //Draw the bit for _, val := range mm[i] { p := fixed.P(val.X, val.Y) d := font.Drawer{Dst: dst, Src: src, Face: face, Dot: p} d.DrawString(val.Val) } //Save the file if err = png.Encode(f, dst); err != nil { log.Fatalf("Encode0 - Err:%s", err) } } }
func main() { flag.Parse() // Read the font data. fontBytes, err := ioutil.ReadFile(*fontfile) if err != nil { log.Println(err) return } f, err := truetype.Parse(fontBytes) if err != nil { log.Println(err) return } // Draw the background and the guidelines. fg, bg := image.Black, image.White ruler := color.RGBA{0xdd, 0xdd, 0xdd, 0xff} if *wonb { fg, bg = image.White, image.Black ruler = color.RGBA{0x22, 0x22, 0x22, 0xff} } const imgW, imgH = 640, 480 rgba := image.NewRGBA(image.Rect(0, 0, imgW, imgH)) draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src) for i := 0; i < 200; i++ { rgba.Set(10, 10+i, ruler) rgba.Set(10+i, 10, ruler) } // Draw the text. h := font.HintingNone switch *hinting { case "full": h = font.HintingFull } d := &font.Drawer{ Dst: rgba, Src: fg, Face: truetype.NewFace(f, &truetype.Options{ Size: *size, DPI: *dpi, Hinting: h, }), } y := 10 + int(math.Ceil(*size**dpi/72)) dy := int(math.Ceil(*size * *spacing * *dpi / 72)) d.Dot = fixed.Point26_6{ X: (fixed.I(imgW) - d.MeasureString(title)) / 2, Y: fixed.I(y), } d.DrawString(title) y += dy for _, s := range text { d.Dot = fixed.P(10, y) d.DrawString(s) y += dy } // 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.") }
// Draw renders the demo state to the screen func (p *StatusPainter) Draw(sz size.Event, pad int, opt *truetype.Options) { if sz.WidthPx == 0 && sz.HeightPx == 0 { return } fsize := opt.Size if fsize == 0 { fsize = 12 } pixY := int(float64(fsize)*float64(sz.PixelsPerPt)) + pad if p.sz != sz { p.sz = sz if p.image != nil { p.image.Release() } p.image = p.images.NewImage(sz.WidthPx, pixY) // BUG: face will not update if opt changes p.opt = nil } if p.opt == nil || *opt != *p.opt { if p.opt != nil { dpi := p.opt.DPI p.opt.DPI = opt.DPI if *opt == *p.opt { // we have already correctly computed the DPI so this opt is // nothing new. p.opt.DPI = dpi } else { p.opt = nil } } if p.opt == nil { p.opt = &truetype.Options{} *p.opt = *opt p.opt.DPI = float64(sz.PixelsPerPt) * 72 p.face = truetype.NewFace(p.ttf, p.opt) } } state := p.demo.State() state.Mut = nil if p.frozen == nil || *state != *p.frozen { // generate a current image draw.Draw(p.image.RGBA, p.image.RGBA.Bounds(), p.bg, image.Pt(0, 0), draw.Over) originX := 2 originY := pixY - 2 drawer := &font.Drawer{ Dst: p.image.RGBA, Src: image.NewUniform(color.Black), Face: p.face, Dot: fixed.P(originX, originY), } text := fmt.Sprintf("%v Count=%d", state.Last.Format(time.RFC3339), state.Counter) drawer.DrawString(text) p.image.Upload() } p.image.Draw( sz, geom.Point{X: 0, Y: 0}, geom.Point{X: sz.WidthPt, Y: 0}, geom.Point{X: 0, Y: geom.Pt(float64(pixY) / float64(sz.PixelsPerPt))}, p.image.RGBA.Bounds(), ) }
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)) }
func drawRGBA(m *image.RGBA, tp image.Point) { draw.Draw(m, m.Bounds(), image.White, image.Point{}, draw.Src) for _, p := range crossPoints { m.SetRGBA(p.X, p.Y, crossColor) } d := font.Drawer{ Dst: m, Src: image.Black, Face: basicfont.Face7x13, Dot: fixed.P(0, 12), } d.DrawString(fmt.Sprint(tp)) }