Example #1
0
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")
	}
}
Example #2
0
func newDrawer(fontFile string) (*drawer, error) {
	if fontFile == "" {
		return nil, errFontRequired
	}
	g := new(drawer)
	g.fontSize = 75.0
	g.dpi = 72.0
	g.fontHinting = font.HintingNone

	ttf, err := getTTF(fontFile)
	if err != nil {
		return nil, errInvalidTTF
	}
	g.face = truetype.NewFace(ttf, &truetype.Options{
		Size:    g.fontSize,
		DPI:     g.dpi,
		Hinting: g.fontHinting,
	})

	fontBytes, err := ioutil.ReadFile(fontFile)
	if err != nil {
		return nil, errInvalidTTF
	}
	font, err := freetype.ParseFont(fontBytes)
	if err != nil {
		return nil, errInvalidTTF
	}
	g.font = font
	return g, nil
}
Example #3
0
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()}
}
Example #4
0
// buildWords generates word sprites.
func buildWords(words map[string]int, f *truetype.Font) []Word {
	ch := make(chan Word)
	wg := &sync.WaitGroup{}
	wg.Add(len(words))

	for k, v := range words {
		go func(w string, occ int) {
			face := truetype.NewFace(f, &truetype.Options{
				Size:    float64(occ * 12),
				DPI:     72,
				Hinting: font.HintingFull,
			})

			wd := buildSprite(w, face)
			wd.Weight = occ

			ch <- wd
			wg.Done()
		}(k, v)
	}

	go func() {
		wg.Wait()
		close(ch)
	}()

	var data []Word
	for w := range ch {
		data = append(data, w)
	}

	return data
}
Example #5
0
// ReadFaceFile parses the contents of path as a truetype font.
func ReadFaceFile(path string, opt *truetype.Options) (font.Face, error) {
	ttf, err := ReadFontFile(path)
	if err != nil {
		return nil, err
	}
	face := truetype.NewFace(ttf, opt)
	return face, nil
}
Example #6
0
// ReadFace parses the data read from r as a truetype font.
func ReadFace(r io.Reader, opt *truetype.Options) (font.Face, error) {
	ttf, err := ReadFont(r)
	if err != nil {
		return nil, err
	}
	face := truetype.NewFace(ttf, opt)
	return face, nil
}
Example #7
0
// initLayout constructs two masks for drawing the battery and the remaining
// energy as well as sets the pixel bounds for drawing energy capacity.  the
// masks allow for simplified space-fills and reduced chance of pixel gaps.
func (app *App) initLayout() {
	var zeropt image.Point

	rectOutTop := image.Rectangle{Min: app.Layout.battRect.Min, Max: app.Layout.battRect.Min.Add(image.Point{2, 2})}
	rectOutBottom := rectOutTop.Add(image.Point{Y: app.Layout.battRect.Size().Y - rectOutTop.Size().Y})
	capRect := image.Rectangle{
		Min: image.Point{X: rectOutTop.Min.X, Y: rectOutTop.Max.Y},
		Max: image.Point{X: rectOutBottom.Max.X, Y: rectOutBottom.Min.Y},
	}
	bodyRect := app.Layout.battRect
	bodyRect.Min.X = capRect.Max.X

	// energy will be drawn under the battery shell.  The only place where it
	// is not safe to draw energy is outside the battery on the positive end.
	energyMask := image.NewAlpha(app.Layout.battRect)
	draw.Draw(energyMask, app.Layout.battRect, opaque, zeropt, draw.Over)
	draw.Draw(energyMask, rectOutTop, transparent, zeropt, draw.Src)
	draw.Draw(energyMask, rectOutBottom, transparent, zeropt, draw.Src)
	app.maskEnergy = energyMask

	// the body uses the same mask as the energy with additional transparency
	// inside the battery's shell.  the mask construction is complex because
	// area inside the cap may be exposed.
	bodyMask := image.NewAlpha(app.Layout.battRect)
	draw.Draw(bodyMask, app.Layout.battRect, energyMask, app.Layout.battRect.Min, draw.Over)
	bodyMaskRect := shrinkRect(bodyRect, app.Layout.thickness)
	draw.Draw(bodyMask, bodyMaskRect, transparent, zeropt, draw.Src)
	capMaskRect := shrinkRect(capRect, app.Layout.thickness)
	capMaskRect.Max.X += 2 * app.Layout.thickness
	draw.Draw(bodyMask, capMaskRect, transparent, zeropt, draw.Src)
	app.maskBattery = bodyMask

	// create a freetype.Context to render text.  each time the context is used
	// it must have its SetDst method called.
	app.tt = freetype.NewContext()
	app.tt.SetSrc(black)
	app.tt.SetClip(app.Layout.textRect)
	app.tt.SetDPI(app.Layout.DPI)
	app.tt.SetFont(app.Layout.font)
	app.tt.SetFontSize(app.Layout.fontSize)
	ttopt := &truetype.Options{
		Size: app.Layout.fontSize,
		DPI:  app.Layout.DPI,
	}
	ttface := truetype.NewFace(app.Layout.font, ttopt)
	app.font = &font.Drawer{
		Src:  black,
		Face: ttface,
	}

	// the rectangle in which energy is drawn needs to account for thickness to
	// make the visible percentage more accurate.  after adjustment reduce the
	// energy rect to account for the account of energy drained.  the energy
	// mask makes computing Y bounds largely irrelevant.
	app.minEnergy = capMaskRect.Min.X
	app.maxEnergy = bodyMaskRect.Max.X
}
Example #8
0
func (f *Font) setup() {
	f.drawer = &font.Drawer{
		Face: truetype.NewFace(f.fnt, &truetype.Options{
			Size:    f.Size,
			DPI:     f.DPI,
			Hinting: font.HintingNone,
		}),
	}
}
Example #9
0
func TestRunReaderEndOfLine(t *testing.T) {
	f := truetype.NewFace(clearSans, nil)
	defer f.Close()

	c := NewReader(strings.NewReader("Hello World!\nHow are you doing?"),
		Style{Offset: 0, Face: f, Foreground: image.Black, Background: image.White},
		Style{Offset: 5, Face: f, Foreground: image.White},
	)

	r := NewRunReader(c, nil, fixed.Rectangle26_6{
		Min: fixed.Point26_6{X: fixed.I(1), Y: fixed.I(1)},
		Max: fixed.Point26_6{X: fixed.I(600), Y: fixed.I(600)},
	})

	var run Run
	var err error

	if run, err = r.ReadRun(); err != nil {
		t.Error(run, err)
	}

	if run != (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(13, 0)},
		},
	}) {
		t.Error("invalid first run:", run)
	}

	if run, err = r.ReadRun(); err != nil {
		t.Error(run, err)
	}

	if run != (Run{
		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(13, 0)},
		},
	}) {
		t.Error("invalid second run:", run)
	}

	if _, err = r.ReadRun(); err != io.EOF {
		t.Error(err)
	}
}
Example #10
0
func TTF(ttf *truetype.Font, data FaceData) Face {
	return ttfFace{
		Face: truetype.NewFace(ttf, &truetype.Options{
			Size:    data.Size,
			DPI:     data.DPI,
			Hinting: data.Hinting,
		}),
		font: ttf,
		data: data,
	}
}
Example #11
0
func TestMeasureEmpty(t *testing.T) {
	f := truetype.NewFace(clearSans, nil)
	defer f.Close()

	s := MeasureBytes(nil, f)

	if s != (fixed.Point26_6{
		X: int26_6(0, 0),
		Y: int26_6(14, 60),
	}) {
		t.Error("invalid result of measuring an empty string:", s)
	}
}
Example #12
0
func TestMeasureMultiLine(t *testing.T) {
	f := truetype.NewFace(clearSans, nil)
	defer f.Close()

	s := MeasureString("Hello World!\nHello World!", f)

	if s != (fixed.Point26_6{
		X: int26_6(64, 55),
		Y: int26_6(26, 60),
	}) {
		t.Error("invalid result of measuring a multi-line string:", s)
	}
}
Example #13
0
func (m *Module) renderMin(rgba *image.RGBA, position fixed.Point26_6) {
	d := &font.Drawer{
		Dst: rgba,
		Src: secondaryForeground,
		Face: truetype.NewFace(m.font, &truetype.Options{
			Size:    minsFontSize,
			DPI:     dpi,
			Hinting: font.HintingNone,
		}),
	}
	d.Dot = position
	d.DrawString("min")
}
Example #14
0
func TestReaderPanicNotSorted(t *testing.T) {
	f := truetype.NewFace(clearSans, nil)
	defer f.Close()
	defer func() { recover() }()

	NewReader(nil,
		Style{Offset: 0, Face: f, Foreground: image.Black, Background: image.White},
		Style{Offset: 2, Face: f, Foreground: image.White},
		Style{Offset: 1, Face: f, Foreground: image.Black},
	)

	t.Error("expected panic: styles must be in the right order")
}
Example #15
0
func TestReaderPanicBadBackground(t *testing.T) {
	f := truetype.NewFace(clearSans, nil)
	defer f.Close()
	defer func() { recover() }()

	NewReader(nil, Style{
		Offset:     0,
		Face:       f,
		Foreground: image.Black,
		Background: nil,
	})

	t.Error("expected panic: no color was given")
}
Example #16
0
func TestReaderPanicBadOffset(t *testing.T) {
	f := truetype.NewFace(clearSans, nil)
	defer f.Close()
	defer func() { recover() }()

	NewReader(nil, Style{
		Offset:     1,
		Face:       f,
		Foreground: image.Black,
		Background: image.White,
	})

	t.Error("expected panic: invalid first style offset")
}
Example #17
0
// from fogleman/gg
func loadFontFace(path string, points float64) (font.Face, error) {
	fontBytes, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	f, err := truetype.Parse(fontBytes)
	if err != nil {
		return nil, err
	}
	face := truetype.NewFace(f, &truetype.Options{
		Size:    points,
		Hinting: font.HintingFull,
	})
	return face, nil
}
Example #18
0
func BenchmarkBuildSprite(b *testing.B) {
	f, err := readFont(defaultFont)
	if err != nil {
		b.FailNow()
	}

	face := truetype.NewFace(f, &truetype.Options{
		Size:    12.0,
		DPI:     72,
		Hinting: font.HintingFull,
	})

	for i := 0; i < b.N; i++ {
		buildSprite("test", face)
	}
}
Example #19
0
// MeasureFont returns the pixel width of the string s at font size sz.
// It tries to use system Arial font if possible, but falls back to a
// conservative ballpark estimate otherwise.
func MeasureFont(s string, sz int) int {
	// use actual TTF font metrics if available
	if theFont != nil {
		myFace := truetype.NewFace(theFont, &truetype.Options{
			Size: float64(sz),
			DPI:  float64(DefaultSettings.dpi),
		})
		d := &font.Drawer{Face: myFace}
		w := d.MeasureString(s)

		// convert from 26.6 fixed point to pixels
		return int(w >> 6)
	}

	return len(s) * (sz - 2)
}
Example #20
0
func (f *font) glyphTable(resolution resolution) *glyphTable {
	t, found := f.resolutions[resolution]
	if !found {
		opt := truetype.Options{
			Size:              float64(f.size),
			DPI:               float64(resolution.intDipsToPixels(72)),
			Hinting:           fnt.HintingFull,
			GlyphCacheEntries: 1,
			SubPixelsX:        1,
			SubPixelsY:        1,
		}
		t = newGlyphTable(truetype.NewFace(f.ttf, &opt))
		f.resolutions[resolution] = t
	}
	return t
}
Example #21
0
func TestComputeWordsBounds(t *testing.T) {
	f := truetype.NewFace(clearSans, nil)
	defer f.Close()

	words := []Run{
		{Offset: 0, Face: f, Text: "Hello"},
		{Offset: 5, Face: f, Text: " "},
		{Offset: 6, Face: f, Text: "World!"},
	}

	computeWordsBounds(words, 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)},
	})

	if !reflect.DeepEqual(words, []Run{
		{
			Offset: 0,
			Text:   "Hello",
			Face:   f,
			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,
			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)},
			},
		},
		{
			Offset: 6,
			Text:   "World!",
			Face:   f,
			Bounds: fixed.Rectangle26_6{
				Min: fixed.Point26_6{X: int26_6(31, 35), Y: int26_6(1, 0)},
				Max: fixed.Point26_6{X: int26_6(65, 55), Y: int26_6(15, 5)},
			},
		},
	}) {
		t.Error(words)
	}
}
Example #22
0
// LoadAsset loads the asset at path and interprets it as a font for rendering
// with golang.org/x/image/font using opt to create the font.Face object.
func LoadAsset(path string, opt *truetype.Options) (*truetype.Font, font.Face, error) {
	f, err := asset.Open(path)
	if err != nil {
		return nil, nil, err
	}
	defer f.Close()
	raw, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, nil, err
	}
	ttf, err := freetype.ParseFont(raw)
	if err != nil {
		return nil, nil, err
	}
	face := truetype.NewFace(ttf, opt)
	return ttf, face, nil
}
Example #23
0
func TestLineReaderEmpty(t *testing.T) {
	f := truetype.NewFace(clearSans, nil)
	defer f.Close()

	c := NewReader(strings.NewReader(""),
		Style{Offset: 0, Face: f, Foreground: image.Black, Background: image.White},
	)

	r := NewLineReader(c, nil, fixed.Rectangle26_6{
		Min: fixed.Point26_6{X: fixed.I(1), Y: fixed.I(1)},
		Max: fixed.Point26_6{X: fixed.I(600), Y: fixed.I(600)},
	})

	if _, err := r.ReadLine(); err != io.EOF {
		t.Error(err)
	}
}
Example #24
0
func TestRenderEmptyText(t *testing.T) {
	f := truetype.NewFace(clearSans, nil)
	defer f.Close()

	c := NewReader(strings.NewReader(""),
		Style{Offset: 0, Face: f, Foreground: image.Black, Background: image.White},
	)

	view, err := Render(c, NewFixedLayout(fixed.R(1, 1, 600, 600)))

	if err != nil {
		t.Error(err)
	}

	if !reflect.DeepEqual(view, View{}) {
		t.Error("invalid view rendered from empty string:", view)
	}
}
Example #25
0
// renderTransitRouteName will render the name of the route in the top left corner.
func (m *Module) renderTransitRouteName(rgba *image.RGBA, dimensions image.Point, text string) {
	d := &font.Drawer{
		Dst: rgba,
		Src: foreground,
		Face: truetype.NewFace(m.font, &truetype.Options{
			Size:    transitRouteNameFontSize,
			DPI:     dpi,
			Hinting: font.HintingNone,
		}),
	}
	dy := int(math.Ceil(transitRouteNameFontSize * dpi / 72))
	// I can't figure out how to get rid of the annoying notification bar. For now, the Y offset here
	// needs to include the height of the notification bar which is 48dp ~= 0.3in.
	d.Dot = fixed.Point26_6{
		X: fixed.I(5),
		Y: fixed.I(int(math.Ceil(0.3*float64(dpi))) + dy),
	}
	d.DrawString(text)
}
Example #26
0
func NewFontAtlas(filename string, dpi, fontSize float64) (*FontAtlas, error) {
	atlas := &FontAtlas{}

	content, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	atlas.TTF, err = truetype.Parse(content)
	if err != nil {
		return nil, err
	}

	opts := &truetype.Options{}
	opts.Size = fontSize

	atlas.Face = truetype.NewFace(atlas.TTF, opts)
	return atlas, nil
}
Example #27
0
func testDraw(t *testing.T, dir Direction) {
	const str = "Hello World!\nHow are you doing?"

	var face = truetype.NewFace(clearSans, &truetype.Options{DPI: 144})
	var size fixed.Point26_6
	var err error

	if size, err = Measure(strings.NewReader(str), face); err != nil {
		t.Error(err)
		return
	}

	dst := image.NewRGBA(image.Rect(0, 0, int(size.X>>6)+1, int(size.Y>>6)+1))
	src := image.NewUniform(color.Black)

	for i, n := 0, len(dst.Pix); i != n; i++ {
		dst.Pix[i] = 0xFF
	}

	DrawString(
		str,
		face,
		dst,
		src,
		fixed.Rectangle26_6{Max: size},
		dir,
	)

	ok := false

	for i, n := 0, len(dst.Pix); i != n; i++ {
		if dst.Pix[i] != 0xFF {
			ok = true
			break
		}
	}

	if !ok {
		t.Error("it looks like nothing was written to the image where we should have rendered the text")
	}

	saveTest(t, dst, "text.Draw_"+dir.(fmt.Stringer).String()+".png")
}
Example #28
0
func FontLoad(fontName string, fontSize int) (font.Face, error) {

	// TODO: select the correct font path
	fontBytes, err := fonts.LoadFont(fontName)
	if err != nil {
		return nil, err
	}
	fontFace, err := truetype.Parse(fontBytes)
	if err != nil {
		return nil, err
	}
	face := truetype.NewFace(fontFace, &truetype.Options{
		Size: float64(fontSize) * 72.0 / 96.0,
		DPI:  72,
		// Hinting: font.HintingNone,
		Hinting: font.HintingFull,
	})
	return face, nil
}
Example #29
0
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)
	}
}
Example #30
0
func main() {
	flag.Parse()
	fmt.Printf("Loading fontfile %q\n", *fontfile)
	b, err := ioutil.ReadFile(*fontfile)
	if err != nil {
		log.Println(err)
		return
	}
	f, err := truetype.Parse(b)
	if err != nil {
		log.Println(err)
		return
	}
	fupe := fixed.Int26_6(f.FUnitsPerEm())
	printBounds(f.Bounds(fupe))
	fmt.Printf("FUnitsPerEm:%d\n\n", fupe)

	c0, c1 := 'A', 'V'

	i0 := f.Index(c0)
	hm := f.HMetric(fupe, i0)
	g := &truetype.GlyphBuf{}
	err = g.Load(f, fupe, i0, font.HintingNone)
	if err != nil {
		log.Println(err)
		return
	}
	fmt.Printf("'%c' glyph\n", c0)
	fmt.Printf("AdvanceWidth:%d LeftSideBearing:%d\n", hm.AdvanceWidth, hm.LeftSideBearing)
	printGlyph(g)
	i1 := f.Index(c1)
	fmt.Printf("\n'%c', '%c' Kern:%d\n", c0, c1, f.Kern(fupe, i0, i1))

	fmt.Printf("\nThe numbers above are in FUnits.\n" +
		"The numbers below are in 26.6 fixed point pixels, at 12pt and 72dpi.\n\n")
	a := truetype.NewFace(f, &truetype.Options{
		Size: 12,
		DPI:  72,
	})
	fmt.Printf("%#v\n", a.Metrics())
}