forked from g3n/engine
/
atlas.go
137 lines (120 loc) · 3.36 KB
/
atlas.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright 2016 The G3N Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package text
import (
"bufio"
"fmt"
"github.com/hecate-tech/engine/math32"
"image"
"image/png"
"os"
"unicode/utf8"
)
// CharInfo contains the information to locate a character in an Atlas
type CharInfo struct {
X int // Position X in pixels in the sheet image from left to right
Y int // Position Y in pixels in the sheet image from top to bottom
Width int // Char width in pixels
Height int // Char heigh in pixels
// Normalized position of char in the image
OffsetX float32
OffsetY float32
RepeatX float32
RepeatY float32
}
// Atlas represents an image containing characters and the information about their location in the image
type Atlas struct {
Chars []CharInfo
Image *image.RGBA
Height int // Recommended vertical space between two lines of text
Ascent int // Distance from the top of a line to its base line
Descent int // Distance from the bottom of a line to its baseline
}
// NewAtlas returns a pointer to a new Atlas object
func NewAtlas(font *Font, first, last rune) *Atlas {
a := new(Atlas)
a.Chars = make([]CharInfo, last+1)
// Get font metrics
metrics := font.Metrics()
a.Height = int(metrics.Height >> 6)
a.Ascent = int(metrics.Ascent >> 6)
a.Descent = int(metrics.Descent >> 6)
const cols = 16
col := 0
encoded := make([]byte, 4)
line := []byte{}
lines := ""
maxWidth := 0
lastX := 0
lastY := a.Descent
nlines := 0
for code := first; code <= last; code++ {
// Encodes rune into UTF8 and appends to current line
count := utf8.EncodeRune(encoded, code)
line = append(line, encoded[:count]...)
// Measure current line
width, _ := font.MeasureText(string(line))
// Sets current code fields
cinfo := &a.Chars[code]
cinfo.X = lastX
cinfo.Y = lastY
cinfo.Width = width - lastX - 1
cinfo.Height += a.Height
lastX = width
fmt.Printf("%c: cinfo:%+v\n", code, cinfo)
// Checks end of the current line
col++
if col >= cols || code == last {
nlines++
lines += string(line) + "\n"
line = []byte{}
// Checks max width
if width > maxWidth {
maxWidth = width
}
if code == last {
break
}
col = 0
lastX = 0
lastY += a.Height
}
}
height := (nlines * a.Height) + a.Descent
// Draw atlas image
canvas := NewCanvas(maxWidth, height, &math32.Color4{1, 1, 1, 1})
canvas.DrawText(0, 0, lines, font)
a.Image = canvas.RGBA
// Calculate normalized char positions in the image
fWidth := float32(maxWidth)
fHeight := float32(height)
for i := 0; i < len(a.Chars); i++ {
char := &a.Chars[i]
char.OffsetX = float32(char.X) / fWidth
char.OffsetY = float32(char.Y) / fHeight
char.RepeatX = float32(char.Width) / fWidth
char.RepeatY = float32(char.Height) / fHeight
}
a.SavePNG("atlas.png")
return a
}
// SavePNG saves the current atlas image as a PNG image file
func (a *Atlas) SavePNG(filename string) error {
// Save that RGBA image to disk.
outFile, err := os.Create(filename)
if err != nil {
return err
}
defer outFile.Close()
b := bufio.NewWriter(outFile)
err = png.Encode(b, a.Image)
if err != nil {
return err
}
err = b.Flush()
if err != nil {
return err
}
return nil
}