Пример #1
0
func (f *Face) Metrics() font.Metrics {
	return font.Metrics{
		Height:  fixed.I(f.Height),
		Ascent:  fixed.I(f.Ascent),
		Descent: fixed.I(f.Descent),
	}
}
Пример #2
0
func (f *subface) Metrics() font.Metrics {
	return font.Metrics{
		Height:  fixed.I(f.height),
		Ascent:  fixed.I(f.ascent),
		Descent: fixed.I(f.height - f.ascent),
	}
}
Пример #3
0
func generateImage(params Params) image.Image {
	fgColor := white

	var detectedFont *truetype.Font
	if []rune(params.text)[0] > '\u2E7F' {
		detectedFont = cjkFont
	} else {
		detectedFont = defaultFont
	}

	fontSize := float64(params.size) * 0.5

	img := image.NewRGBA(image.Rect(0, 0, params.size, params.size))

	if params.seed != 0 {
		rand.Seed(params.seed)
	}

	var bgColor color.RGBA
	if params.color == (color.RGBA{}) {
		bgColor = colors[rand.Intn(len(colors))]
	} else {
		bgColor = params.color
	}

	if params.border {
		bgColor, fgColor = fgColor, bgColor
	}

	draw.Draw(img, img.Bounds(), &image.Uniform{bgColor}, image.ZP, draw.Src)

	if params.border {
		strokeWidth := fontSize * 0.08
		circleSize := fontSize * 0.92
		arcAngle := math.Pi * 2

		gc := draw2dimg.NewGraphicContext(img)
		gc.SetStrokeColor(fgColor)
		gc.SetLineWidth(strokeWidth)
		gc.ArcTo(fontSize, fontSize, circleSize, circleSize, arcAngle, arcAngle)
		gc.Stroke()
	}

	d := &font.Drawer{
		Dst: img,
		Src: image.NewUniform(fgColor),
		Face: truetype.NewFace(detectedFont, &truetype.Options{
			Size: fontSize,
			DPI:  72,
		}),
	}
	d.Dot = fixed.Point26_6{
		X: (fixed.I(params.size) - d.MeasureString(params.text)) / 2,
		Y: fixed.I(int(math.Ceil(fontSize * 1.35))),
	}
	d.DrawString(params.text)

	return img
}
Пример #4
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)
	}
}
Пример #5
0
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
}
Пример #6
0
func NewFontFace(path string, pixels float32, fg, bg color.Color) (fontface *FontFace, err error) {
	var (
		font      *truetype.Font
		fontbytes []byte
		bounds    fixed.Rectangle26_6
		context   *freetype.Context
		points    float32
		dpi       float32 = 96
	)
	if fontbytes, err = ioutil.ReadFile(path); err != nil {
		return
	}
	if font, err = freetype.ParseFont(fontbytes); err != nil {
		return
	}
	points = pixels * 72 / dpi
	bounds = font.Bounds(fixed.I(int(pixels)))
	context = freetype.NewContext()
	context.SetFont(font)
	context.SetFontSize(float64(points))
	context.SetDPI(float64(dpi))
	fontface = &FontFace{
		font:    font,
		charw:   float32(bounds.Max.X-bounds.Min.X) / 64,
		charh:   float32(bounds.Max.Y-bounds.Min.Y) / 64,
		offy:    float32(bounds.Min.Y) / 64,
		fg:      fg,
		bg:      bg,
		context: context,
	}
	return
}
Пример #7
0
func (f *Face) Glyph(dot fixed.Point26_6, r rune) (
	dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {

loop:
	for _, rr := range [2]rune{r, '\ufffd'} {
		for _, rng := range f.Ranges {
			if rr < rng.Low || rng.High <= rr {
				continue
			}
			maskp.Y = (int(rr-rng.Low) + rng.Offset) * f.Height
			ok = true
			break loop
		}
	}
	if !ok {
		return image.Rectangle{}, nil, image.Point{}, 0, false
	}

	minX := int(dot.X+32) >> 6
	minY := int(dot.Y+32)>>6 - f.Ascent
	dr = image.Rectangle{
		Min: image.Point{
			X: minX,
			Y: minY,
		},
		Max: image.Point{
			X: minX + f.Width,
			Y: minY + f.Height,
		},
	}

	return dr, f.Mask, maskp, fixed.I(f.Advance), true
}
Пример #8
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)
	}
}
Пример #9
0
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.")
}
Пример #10
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)
}
Пример #11
0
// our avatar image is square
func (g *drawer) Draw(s string, size int, bg *color.RGBA) image.Image {
	// draw the background
	dst := image.NewRGBA(image.Rect(0, 0, size, size))
	draw.Draw(dst, dst.Bounds(), &image.Uniform{bg}, image.ZP, draw.Src)

	// draw the text
	drawer := &font.Drawer{
		Dst:  dst,
		Src:  image.White,
		Face: g.face,
	}

	// font index
	fi := g.font.Index([]rune(s)[0])

	// glyph example: http://www.freetype.org/freetype2/docs/tutorial/metrics.png
	var gbuf truetype.GlyphBuf
	var err error
	var _fsize fixed.Int26_6 = fixed.Int26_6(g.fontSize * g.dpi * (64.0 / 72.0))
	err = gbuf.Load(g.font, _fsize, fi, font.HintingFull)
	if err != nil {
		// fixme
		drawer.DrawString("")
		return dst
	}

	// center
	dY := int((size - int(gbuf.Bounds.Max.Y-gbuf.Bounds.Min.Y)>>6) / 2)
	dX := int((size - int(gbuf.Bounds.Max.X-gbuf.Bounds.Min.X)>>6) / 2)
	y := int(gbuf.Bounds.Max.Y>>6) + dY
	x := 0 - int(gbuf.Bounds.Min.X>>6) + dX

	//y := 10 + int(math.Ceil(g.fontSize*g.dpi/72)) //FIXME: what does it mean?
	drawer.Dot = fixed.Point26_6{
		X: fixed.I(x), //(fixed.I(size) - drawer.MeasureString(s)) / 2,
		Y: fixed.I(y),
	}
	drawer.DrawString(s)

	return dst
}
Пример #12
0
// our avatar image is square
func (g *drawer) Draw(s string, size int, bg *color.RGBA) image.Image {
	// draw the background
	dst := image.NewRGBA(image.Rect(0, 0, size, size))
	draw.Draw(dst, dst.Bounds(), &image.Uniform{bg}, image.ZP, draw.Src)

	// draw the text
	drawer := &font.Drawer{
		Dst:  dst,
		Src:  image.White,
		Face: g.face,
	}

	y := 10 + int(math.Ceil(g.fontSize*g.dpi/72)) //FIXME: what does it mean?
	drawer.Dot = fixed.Point26_6{
		X: (fixed.I(size) - drawer.MeasureString(s)) / 2,
		Y: fixed.I(y),
	}
	drawer.DrawString(s)

	return dst
}
Пример #13
0
// renderLoadingScreen will render the Loading screen.
func (m *Module) renderInformation(rgba *image.RGBA, dimensions image.Point, background image.Image, text string, textSizing string) {
	// Prepare a dark grey background to draw on.
	draw.Draw(rgba, rgba.Bounds(), background, image.ZP, draw.Src)

	d := &font.Drawer{
		Dst: rgba,
		Src: foreground,
		Face: truetype.NewFace(m.font, &truetype.Options{
			Size:    informationPopupFontSize,
			DPI:     dpi,
			Hinting: font.HintingNone,
		}),
	}
	dy := int(math.Ceil(informationPopupFontSize * dpi / 72))
	textWidth := d.MeasureString(textSizing)
	d.Dot = fixed.Point26_6{
		X: fixed.I(dimensions.X/2) - (textWidth / 2),
		Y: fixed.I(dimensions.Y/2 + dy/2),
	}
	d.DrawString(text)
}
Пример #14
0
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")
	}
}
Пример #15
0
// buildSprite renders an image from a text and font face as well as computing its
// bounding box.
//
// TODO: This is a really naive implementation that really should be refactored
// into something more elegant.
func buildSprite(text string, face font.Face) Word {
	var (
		canvasWidth  = 1024
		canvasHeight = 1024
	)

	// Create temporary scratch image to determine real font size.
	scratch := image.NewRGBA(image.Rect(0, 0, canvasWidth, canvasHeight))
	draw.Draw(scratch, scratch.Bounds(), image.Transparent, image.ZP, draw.Src)

	d := &font.Drawer{
		Dst:  scratch,
		Src:  image.White,
		Face: face,
	}

	d.Dot = fixed.Point26_6{
		X: fixed.I(canvasWidth / 2),
		Y: fixed.I(canvasHeight / 2),
	}

	d.DrawString(text)

	// Generate trimmed image and quadtree from temporary canvas.
	tt := buildTree(scratch, color.RGBA{0, 0, 0, 0}).Trimmed()
	r := tt.Extents

	tr := image.Rect(0, 0, r.Dx(), r.Dy())
	im := image.NewRGBA(tr)
	draw.Draw(im, tr, scratch, r.Min, draw.Src)

	t := buildTree(im, color.RGBA{0, 0, 0, 0})

	return Word{
		Text:   text,
		Image:  im,
		Bounds: t,
	}
}
Пример #16
0
func Render(letter string, bgColor color.Color, width int, out io.Writer) error {
	fg := pickForegroundColor(bgColor)

	rgba := image.NewRGBA(image.Rect(0, 0, width, width))
	draw.Draw(rgba, rgba.Bounds(), &image.Uniform{bgColor}, image.ZP, draw.Src)

	fontSize := fontSizeFactor * float64(width)
	d := &font.Drawer{
		Dst: rgba,
		Src: &image.Uniform{fg},
		Face: truetype.NewFace(fnt, &truetype.Options{
			Size:    fontSize,
			DPI:     dpi,
			Hinting: font.HintingNone,
		}),
	}

	y := int(yOffsetFactor*float64(width)) + int(math.Ceil(fontSize*dpi/72))
	d.Dot = fixed.Point26_6{
		X: (fixed.I(width) - d.MeasureString(letter)) / 2,
		Y: fixed.I(y),
	}
	d.DrawString(letter)

	b := bufio.NewWriter(out)
	encoder := png.Encoder{CompressionLevel: png.DefaultCompression}
	err := encoder.Encode(b, rgba)
	if err != nil {
		return err
	}
	err = b.Flush()
	if err != nil {
		return err
	}
	return nil
}
Пример #17
0
func (t *Text) GetStringSize(s string) (fixed.Int26_6, fixed.Int26_6) {
	// Assumes 72 DPI
	fupe := fixed.Int26_6(t.fontSize * 64.0)
	width := fixed.I(0)

	prev, hasPrev := t.font.Index(0), false
	for _, r := range s {
		idx := t.font.Index(r)
		if hasPrev {
			width += t.font.Kerning(fupe, prev, idx)
		}

		width += t.font.HMetric(fupe, idx).AdvanceWidth
		prev, hasPrev = idx, true
	}

	fontBounds := t.font.Bounds(fupe)
	return width, (fontBounds.YMax - fontBounds.YMin)
}
Пример #18
0
func NewFontAtlas(filename string, dpi, fontSize float64) (*FontAtlas, error) {
	atlas := &FontAtlas{}
	atlas.Rendered = make(map[rune]Glyph, 256)

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

	atlas.drawPadding = float32(fontSize * 0.5)
	atlas.lineHeight = float32(fontSize * 1.2)

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

	atlas.Image = image.NewRGBA(image.Rect(0, 0, 1024, 1024))

	atlas.Context = freetype.NewContext()
	atlas.Context.SetDPI(dpi)

	atlas.Context.SetFont(atlas.TTF)
	atlas.Context.SetFontSize(fontSize)

	atlas.Context.SetClip(atlas.Image.Bounds())
	atlas.Context.SetSrc(image.White)
	atlas.Context.SetDst(atlas.Image)

	atlas.maxBounds = atlas.TTF.Bounds(fixed.I(int(fontSize)))

	opts := &truetype.Options{}
	opts.Size = fontSize
	opts.Hinting = font.HintingFull

	atlas.Face = truetype.NewFace(atlas.TTF, opts)
	return atlas, nil
}
Пример #19
0
func main() {
	flag.Parse()

	chimg := make(chan image.Image)

	go func() {
		resp, err := http.Get(fmt.Sprintf("%s/%dx%d", unsplashUrl, *width, *height))
		if err != nil {
			log.Fatal(err)
		}
		img, err := jpeg.Decode(resp.Body)
		if err != nil {
			log.Fatal(err)
		}
		chimg <- img
	}()

	//load font
	chmask := make(chan image.Image)
	go func() {
		fontBytes, err := ubuntu.Asset("Ubuntu-B.ttf")
		if err != nil {
			log.Fatal(err)
		}
		f, err := truetype.Parse(fontBytes)
		if err != nil {
			log.Fatal(err)
		}

		//generate text mast
		mask := image.NewRGBA(image.Rect(0, 0, *width, *height))
		//draw.Draw(mask, mask.Bounds(), image.White, image.ZP, draw.Src)
		d := &font.Drawer{
			Dst: mask,
			Src: image.White,
			Face: truetype.NewFace(f, &truetype.Options{
				Size:    float64(*fontSize),
				DPI:     float64(*dpi),
				Hinting: font.HintingNone,
			}),
		}
		d.Dot = fixed.Point26_6{
			X: (fixed.I(*width) - d.MeasureString(*text)) / 2,
			Y: fixed.I(*height) / 2,
		}
		d.DrawString(*text)
		chmask <- mask
	}()

	mask := <-chmask
	img := <-chimg

	finalDst := image.NewRGBA(img.Bounds())
	changedDst := InvertColors(Flip(img))

	//Convert dst
	dstB := img.Bounds()
	draw.Draw(finalDst, finalDst.Bounds(), img, dstB.Min, draw.Src)
	draw.DrawMask(finalDst, finalDst.Bounds(), changedDst, image.ZP, mask, image.ZP, draw.Over)
	file, err := os.Create(*output)
	if err != nil {
		log.Fatal(err)
	}
	png.Encode(file, finalDst)
}
Пример #20
0
func (f *Face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
	return fixed.I(f.Advance), true
}
Пример #21
0
func (f *Face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
	return fixed.R(0, -f.Ascent, f.Width, -f.Ascent+f.Height), fixed.I(f.Advance), true
}
Пример #22
0
func (m *Module) render(rgba *image.RGBA, display Display, dimensions image.Point) {
	// Prepare a blue background to draw on.
	draw.Draw(rgba, rgba.Bounds(), background, image.ZP, draw.Src)

	// First, render the very next train's minutes on the left half of the screen.
	d := &font.Drawer{
		Dst: rgba,
		Src: foreground,
		Face: truetype.NewFace(m.font, &truetype.Options{
			Size:    nextTrainFontSize,
			DPI:     dpi,
			Hinting: font.HintingNone,
		}),
	}
	textWidth := d.MeasureString(strconv.Itoa(display.NextTrainMinutes))
	d.Dot = fixed.Point26_6{
		X: fixed.I(dimensions.X/4) - (textWidth / 2),
		Y: fixed.I(int(4 * dimensions.Y / 5)),
	}
	d.DrawString(strconv.Itoa(display.NextTrainMinutes))

	// Render the little "min" label next to the next train time.
	m.renderMin(rgba, fixed.Point26_6{
		X: d.Dot.X,
		Y: d.Dot.Y,
	})

	// Now, render the next next train's minutes on the right half of the screen.
	if display.NextNextOK {
		d = &font.Drawer{
			Dst: rgba,
			Src: foreground,
			Face: truetype.NewFace(m.font, &truetype.Options{
				Size:    nextNextTrainFontSize,
				DPI:     dpi,
				Hinting: font.HintingNone,
			}),
		}
		textWidth = d.MeasureString(strconv.Itoa(display.NextNextTrainMinutes))
		d.Dot = fixed.Point26_6{
			X: fixed.I(3*dimensions.X/4) - (textWidth / 2),
			Y: fixed.I(int(2 * dimensions.Y / 3)),
		}
		d.DrawString(strconv.Itoa(display.NextNextTrainMinutes))

		// Render the little "min" label next to the next, next train time.
		m.renderMin(rgba, fixed.Point26_6{
			X: d.Dot.X,
			Y: d.Dot.Y,
		})
	}

	// Render the text indicating the freshness of the presented data.
	d = &font.Drawer{
		Dst: rgba,
		Src: secondaryForeground,
		Face: truetype.NewFace(m.font, &truetype.Options{
			Size:    lastUpdatedAtFontSize,
			DPI:     dpi,
			Hinting: font.HintingNone,
		}),
	}
	updatedAt := fmt.Sprintf("Predictions accurate as of %v seconds ago, from %s.", display.UpdatedSecondsAgo, display.PredictionSource)
	textWidth = d.MeasureString(updatedAt)
	d.Dot = fixed.Point26_6{
		X: fixed.I(dimensions.X-10) - textWidth,
		Y: fixed.I(dimensions.Y - lastUpdatedAtFontSize),
	}
	d.DrawString(updatedAt)
}
Пример #23
0
func (b BoxGoalGenerator) CreateSignature(req util.ParsedSignatureRequest) (util.Signature, error) {
	username := req.GetProperty("username").(string)
	skill := req.GetProperty("skill").(util.Skill)
	goal := req.GetProperty("goal").(int)
	goaltype := req.GetProperty("goaltype").(util.GoalType)

	stats, err := util.GetStats(username)
	if err != nil {
		var s util.Signature
		return s, errors.New(fmt.Sprintf("Failed to fetch stats for %s", username))
	}
	stat := util.GetStatBySkill(stats, skill)

	currentLevel := util.LevelFromXP(stat.Skill, stat.Xp)
	currentXP := stat.Xp
	var goalXP int
	var remainder int
	if goaltype == util.GoalXP {
		goalXP = goal
		remainder = goalXP - currentXP
	} else {
		goalXP = util.XPForLevel(stat.Skill, goal)
		remainder = util.XPToLevel(stat.Skill, currentXP, goal)
	}
	goalLevel := util.LevelFromXP(stat.Skill, goalXP)
	if remainder < 0 {
		remainder = 0
	}
	percent := int(float64(currentXP) / float64(goalXP) * 100.0)
	if percent > 100 {
		percent = 100
	}

	c := freetype.NewContext()

	createDrawer := func(img draw.Image, color *image.Uniform, f *truetype.Font,
		size float64, dpi float64, hinting font.Hinting) *font.Drawer {
		return &font.Drawer{
			Dst: img,
			Src: color,
			Face: truetype.NewFace(f, &truetype.Options{
				Size:    size,
				DPI:     dpi,
				Hinting: hinting,
			})}
	}

	baseImage := cloneImage(baseImage)

	drawer := createDrawer(baseImage, fontColor, baseFont, size, dpi,
		font.HintingFull)

	drawString := func(str string, x fixed.Int26_6, y int) {
		drawer.Dot = fixed.Point26_6{
			X: x,
			//Y: fixed.I(y + int((c.PointToFixed(size) >> 6))),
			Y: fixed.I(y + int(c.PointToFixed(size)>>6)),
		}
		drawer.DrawString(str)
	}

	drawRightAlignedString := func(str string, x, y int) {
		width := drawer.MeasureString(str)
		drawString(str, fixed.I(x)-width, y)
	}

	// Skill name and current level
	drawString(
		fmt.Sprintf("%s: %d/%d", skill.Name, currentLevel, goalLevel),
		fixed.I(7), 1)

	for _, label := range staticLabels {
		if label.str == "Target lvl:" && goaltype == util.GoalXP {
			label.str = "Target XP:"
		}

		drawString(label.str, fixed.I(label.x), label.y)
	}

	x, y := 150, 15

	// current xp
	drawRightAlignedString(util.Format(currentXP), x, y)
	y += 15

	// goal
	drawRightAlignedString(util.Format(goal), x, y)
	y += 15

	// remainder
	drawRightAlignedString(util.Format(remainder), x, y)
	y += 15

	// bar
	drawBar(baseImage, percent)

	// bar percentage
	x = 71
	y = 61
	color := image.White
	if percent >= 50 {
		color = image.Black
	}

	drawer = createDrawer(baseImage, color, baseFont, 11, dpi,
		font.HintingFull)
	drawString(fmt.Sprintf("%d%%", percent), fixed.I(x), y)

	return util.Signature{username, baseImage}, nil
}
Пример #24
0
func (s *Service) Draw(siteName string, w http.ResponseWriter) {
	// Read the font data.
	fontBytes := MustAsset("assets/luxisr.ttf")
	f, err := truetype.Parse(fontBytes)
	if err != nil {
		log.Println(err)
		return
	}

	// Draw the background and the guidelines.
	fg, bg := image.Black, image.White
	const imgW, imgH = 1200, 700
	rgba := image.NewRGBA(image.Rect(0, 0, imgW, imgH))
	draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src)

	d := &font.Drawer{
		Dst: rgba,
		Src: fg,
		Face: truetype.NewFace(f, &truetype.Options{
			Size:    size,
			DPI:     dpi,
			Hinting: hinting,
		}),
	}
	y := (imgH / 2) + (int(math.Ceil(size*dpi/72)) / 2)
	//dy := int(math.Ceil(size * spacing * dpi / 72))
	d.Dot = fixed.Point26_6{
		X: (fixed.I(imgW) - d.MeasureString(siteName)) / 2,
		Y: fixed.I(y),
	}
	d.DrawString(siteName)

	d = &font.Drawer{
		Dst: rgba,
		Src: image.NewUniform(color.Gray16{0xaaaa}),
		Face: truetype.NewFace(f, &truetype.Options{
			Size:    smallSize,
			DPI:     dpi,
			Hinting: hinting,
		}),
	}

	dy := int(math.Ceil(smallSize * spacing * dpi / 72))
	d.Dot = fixed.Point26_6{
		X: (fixed.I(imgW) - d.MeasureString(sas)) / 2,
		Y: fixed.I(y + dy),
	}
	d.DrawString(sas)

	b := bufio.NewWriter(w)
	err = png.Encode(b, rgba)
	if err != nil {
		log.Println(err)
		return
	}
	err = b.Flush()
	if err != nil {
		log.Println(err)
		return
	}
}
Пример #25
0
func tryLoadFont(fontFilePath string) (Font, error) {
	defer tlog.FuncLog(tlog.Func("LoadFont"))
	var data []byte
	{
		fd, err := os.Open(fontFilePath)
		if err != nil {
			return Font{}, err
		}
		defer fd.Close()

		data, err = ioutil.ReadAll(fd)
		if err != nil {
			return Font{}, err
		}
	}

	texsdfpath := filepath.Join(os.TempDir(), filepath.Base(fontFilePath)+".sdf")
	texsdf, err := LoadPng(texsdfpath)
	sdfLoaded := err == nil
	if err != nil {
		texsdf = image.NewGray(image.Rect(0, 0, texSize, texSize))
	}
	name := filepath.Base(fontFilePath)
	name = name[:len(name)-len(filepath.Ext(name))]

	var f = Font{
		SdfTex:    texsdf,
		Glyphs:    make([]Glyph, int(highRune-lowRune+1)),
		FirstRune: lowRune,
		Name:      name,
		Height:    0,
		Leading:   0,
	}

	{
		// Read the truetype font.
		ttf, err := truetype.Parse(data)
		if err != nil {
			return Font{}, err
		}

		var baseline = 3 * sdfSize / 4
		var targetGlyphExtentPxs = float64(sdfSize) * 3.0 / 4.0
		var fontHeight float64    // maximum height, in renormalized EMs
		var renormalizeEM float64 // (real max glyph size / em)
		{
			// the real max glyph extent in EMs
			var maxGlyphExtentEMs float64
			{
				var max fixed.Int26_6    // = 0; max extent(height or width) of a glyph in FontUnits
				var height fixed.Int26_6 // = 0; max height of a glyph in FontUnits
				{
					scale := fixed.I(int(ttf.FUnitsPerEm()))
					b := ttf.Bounds(scale)
					max = b.Max.X - b.Min.X
					height = b.Max.Y - b.Min.Y
					if max < height {
						max = height
					}
					for ch := lowRune; ch <= highRune; ch++ {
						hmetric := ttf.HMetric(scale, ttf.Index(ch))
						vmetric := ttf.VMetric(scale, ttf.Index(ch))
						if max < hmetric.AdvanceWidth {
							max = hmetric.AdvanceWidth
						}
						if height < vmetric.AdvanceHeight {
							height = vmetric.AdvanceHeight
						}
						if max < height {
							max = height
						}
					}
				}
				maxGlyphExtentEMs = float64(max) / float64(ttf.FUnitsPerEm())
				fontHeight = float64(height) / float64(ttf.FUnitsPerEm())
			}
			renormalizeEM = 1 / maxGlyphExtentEMs
			fontHeight *= renormalizeEM
		}
		f.Height = em(fontHeight)
		f.Leading = em(leadingFactor * fontHeight)
		scale := fixed.I(int(ttf.FUnitsPerEm()))

		graphicIdx := 0
		for ch := lowRune; ch <= highRune; ch++ {
			// fmt.Printf("%c", ch)

			idx := int(ch - lowRune)

			var aw float64
			{
				hmetric := ttf.HMetric(scale, ttf.Index(ch))
				aw = float64(hmetric.AdvanceWidth) / float64(ttf.FUnitsPerEm()) * renormalizeEM
				f.Glyphs[idx].AdvanceWidth = em(aw)
			}

			if !unicode.IsGraphic(ch) || unicode.IsSpace(ch) {
				f.Glyphs[idx].Cell = image.ZR
				continue
			}

			x0 := cellSize * (graphicIdx % texNCells)
			y0 := cellSize * (graphicIdx / texNCells)
			graphicIdx++
			f.Glyphs[idx].Cell = image.Rect(x0, y0, x0+cellSize, y0+cellSize)

			sdfTargetWidth := targetGlyphExtentPxs * aw
			sdfTargetHeight := targetGlyphExtentPxs * fontHeight

			if !sdfLoaded {
				img := image.NewGray(image.Rect(0, 0, sdfSize, sdfSize))
				c := freetype.NewContext()
				c.SetFont(ttf)
				c.SetDPI(72)                                        // so that one Pt == one Pixel
				c.SetFontSize(targetGlyphExtentPxs * renormalizeEM) // how many Pt the max glyph has
				c.SetClip(img.Bounds())
				c.SetDst(img)
				c.SetSrc(image.White)

				sdfX := (sdfSize - int(sdfTargetWidth)) / 2
				c.DrawString(string(ch), freetype.Pt(sdfX, baseline))
				sdfize(img)
				scaleDownTo(f.SdfTex, f.Glyphs[idx].Cell, img)
			}

			dx := (sdfSize - int(sdfTargetWidth)) / 2 * cellSize / sdfSize
			cellHeight := sdfTargetHeight * float64(cellSize) / float64(sdfSize)
			dy := (cellSize - int(cellHeight)) / 4

			f.Glyphs[idx].Cell = image.Rect(x0+dx, y0+3*dy, x0+cellSize-dx, y0+cellSize-dy)
			// drawRect(f.SdfTex, f.Glyphs[idx].Cell, color.Gray{255})
		}
	}

	if !sdfLoaded {
		SavePng(texsdfpath, f.SdfTex)
		tlog.Println("sdf ", texsdfpath, " saved")
	}
	// ShowImage(f.SdfTex)
	tlog.Println(fontFilePath, " loaded")
	return f, nil
}
Пример #26
0
func TestLineReaderSingle(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},
		Style{Offset: 13, Face: f, Foreground: image.Black},
	)

	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)},
	})

	var line Line
	var err error

	if line, err = r.ReadLine(); err != nil {
		t.Error(line, err)
	}

	if !reflect.DeepEqual(line, 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)},
		},
	}) {
		t.Error("invalid first line:", line)
	}

	if line, err = r.ReadLine(); err != nil {
		t.Error(err)
	}

	if !reflect.DeepEqual(line, Line{
		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)},
		},
	}) {
		t.Error("invalid second line:", line)
	}

	if _, err := r.ReadLine(); err != io.EOF {
		t.Error(err)
	}
}
Пример #27
0
func TestLineReaderSimple(t *testing.T) {
	f := truetype.NewFace(clearSans, nil)
	defer f.Close()

	c := NewReader(strings.NewReader("Hello World!"),
		Style{Offset: 0, Face: f, Foreground: image.Black, Background: image.White},
		Style{Offset: 5, Face: f, Foreground: 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)},
	})

	var line Line
	var err error

	if line, err = r.ReadLine(); err != nil {
		t.Error(err)
	}

	if !reflect.DeepEqual(line, 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)},
		},
	}) {
		t.Error("invalid line:", line)
	}

	// The first EOF will come from the underlying run reader.
	if _, err := r.ReadLine(); err != io.EOF {
		t.Error(err)
	}

	// The second EOF will come from the line reader which has cached that it
	// has nothing to return anymore.
	if _, err := r.ReadLine(); err != io.EOF {
		t.Error(err)
	}
}
Пример #28
0
func i26_6(f C.CGFloat) fixed.Int26_6 {
	a, b := math.Modf(float64(f))
	return fixed.I(int(a)) + fixed.Int26_6(b*64)
}
Пример #29
0
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.")
}
Пример #30
0
func testScaling(t *testing.T, h font.Hinting) {
	for _, tc := range scalingTestCases {
		f, testdataIsOptional, err := parseTestdataFont(tc.name)
		if err != nil {
			if testdataIsOptional {
				t.Log(err)
			} else {
				t.Error(err)
			}
			continue
		}
		hintingStr := "sans"
		if h != font.HintingNone {
			hintingStr = "with"
		}
		testFile, err := os.Open(fmt.Sprintf(
			"../testdata/%s-%dpt-%s-hinting.txt", tc.name, tc.size, hintingStr))
		if err != nil {
			t.Errorf("%s: Open: %v", tc.name, err)
			continue
		}
		defer testFile.Close()

		wants := []scalingTestData{}
		scanner := bufio.NewScanner(testFile)
		if scanner.Scan() {
			major, minor, patch := 0, 0, 0
			_, err := fmt.Sscanf(scanner.Text(), "freetype version %d.%d.%d", &major, &minor, &patch)
			if err != nil {
				t.Errorf("%s: version information: %v", tc.name, err)
			}
			if (major < 2) || (major == 2 && minor < 5) || (major == 2 && minor == 5 && patch < 1) {
				t.Errorf("%s: need freetype version >= 2.5.1.\n"+
					"Try setting LD_LIBRARY_PATH=/path/to/freetype_built_from_src/objs/.libs/\n"+
					"and re-running testdata/make-other-hinting-txts.sh",
					tc.name)
				continue
			}
		} else {
			t.Errorf("%s: no version information", tc.name)
			continue
		}
		for scanner.Scan() {
			wants = append(wants, scalingTestParse(scanner.Text()))
		}
		if err := scanner.Err(); err != nil && err != io.EOF {
			t.Errorf("%s: Scanner: %v", tc.name, err)
			continue
		}

		glyphBuf := &GlyphBuf{}
		for i, want := range wants {
			if err = glyphBuf.Load(f, fixed.I(tc.size), Index(i), h); err != nil {
				t.Errorf("%s: glyph #%d: Load: %v", tc.name, i, err)
				continue
			}
			got := scalingTestData{
				advanceWidth: glyphBuf.AdvanceWidth,
				bounds:       glyphBuf.Bounds,
				points:       glyphBuf.Points,
			}

			if got.advanceWidth != want.advanceWidth {
				t.Errorf("%s: glyph #%d advance width:\ngot  %v\nwant %v",
					tc.name, i, got.advanceWidth, want.advanceWidth)
				continue
			}

			if got.bounds != want.bounds {
				t.Errorf("%s: glyph #%d bounds:\ngot  %v\nwant %v",
					tc.name, i, got.bounds, want.bounds)
				continue
			}

			for i := range got.points {
				got.points[i].Flags &= 0x01
			}
			if len(got.points) != len(want.points) {
				t.Errorf("%s: glyph #%d:\ngot  %v\nwant %v\ndifferent slice lengths: %d versus %d",
					tc.name, i, got.points, want.points, len(got.points), len(want.points))
				continue
			}
			if j, equals := scalingTestEquals(got.points, want.points); !equals {
				t.Errorf("%s: glyph #%d:\ngot  %v\nwant %v\nat index %d: %v versus %v",
					tc.name, i, got.points, want.points, j, got.points[j], want.points[j])
				continue
			}
		}
	}
}