]> git.localhorst.tv Git - l2e.git/blob - src/graphics/Font.cpp
dynamic width right aligned rendering with Font
[l2e.git] / src / graphics / Font.cpp
1 #include "Font.h"
2
3 #include "../loader/Interpreter.h"
4 #include "../loader/TypeDescription.h"
5
6 #include <cmath>
7 #include <cstring>
8 #include <iostream>
9
10 using geometry::Vector;
11 using loader::FieldDescription;
12 using loader::Interpreter;
13 using loader::TypeDescription;
14 using std::pow;
15
16 namespace graphics {
17
18 int Font::StringWidth(const char *s) const {
19         int width(0), col(0);
20         for (int i(0); s[i]; ++i) {
21                 if (s[i] == '\n') {
22                         if (width < col) {
23                                 width = col;
24                         }
25                         col = 0;
26                 } else {
27                         ++col;
28                 }
29         }
30         return (width < col ? col : width) * CharWidth();
31 }
32
33 int Font::StringHeight(const char *s) const {
34         if (*s == '\0') {
35                 return 0;
36         }
37         int height(1);
38         for (; *s; ++s) {
39                 if (*s == '\n') {
40                         ++height;
41                 }
42         }
43         return height * CharHeight();
44 }
45
46
47 void Font::DrawChar(char c, SDL_Surface *dest, const Vector<int> &position) const {
48         if (!sprite) return;
49
50         int col(colOffset + (c % 0x10));
51         int row(rowOffset + (c / 0x10));
52         sprite->Draw(dest, position, col, row);
53 }
54
55 void Font::DrawString(const char *s, SDL_Surface *dest, const Vector<int> &positionIn, int maxWidth) const {
56         if (!sprite) return;
57
58         Vector<int> position(positionIn);
59         Vector<int> lineHead(positionIn);
60         Vector<int> step(CharWidth(), 0);
61         Vector<int> lineBreak(0, CharHeight());
62         for (int i(0), col(0); s[i] && (maxWidth <= 0 || col < maxWidth); ++i) {
63                 if (s[i] == '\n') {
64                         lineHead += lineBreak;
65                         position = lineHead;
66                         col = 0;
67                 } else {
68                         DrawChar(s[i], dest, position);
69                         position += step;
70                         ++col;
71                 }
72         }
73 }
74
75 void Font::DrawStringRight(const char *s, SDL_Surface *dest, const Vector<int> &positionIn, int maxWidth) const {
76         // NOTE: this does not handle line breaks
77         if (!sprite) return;
78
79         int length(0);
80         if (maxWidth > 0) {
81                 while (length < maxWidth && s[length] != '\0') {
82                         ++length;
83                 }
84         } else {
85                 length = std::strlen(s);
86         }
87         Vector<int> position(positionIn.X() - length * CharWidth(), positionIn.Y());
88
89         DrawString(s, dest, position, length);
90 }
91
92 void Font::DrawDigit(int digit, SDL_Surface *dest, const Vector<int> &position) const {
93         if (!sprite) return;
94
95         DrawChar(digit + 0x30, dest, position);
96 }
97
98 void Font::DrawNumber(int numberIn, SDL_Surface *dest, const Vector<int> &positionIn, int digits) const {
99         if (!sprite) return;
100
101         int number(numberIn);
102         if (digits > 0 && numberIn >= pow(10.0, digits)) {
103                 numberIn = pow(10.0, digits) - 1;
104         }
105
106         Vector<int> position(positionIn);
107         Vector<int> step(sprite->Width(), 0);
108
109         if (digits > 0) {
110                 int i(digits - 1);
111                 while (number < pow(10.0, i) && i > 0) {
112                         position += step;
113                         --i;
114                 }
115         }
116
117         int m(10);
118         while (m <= number) {
119                 m *= 10;
120         }
121
122         while (m > 9) {
123                 m /= 10;
124                 DrawDigit((number / m) % 10, dest, position);
125                 position += step;
126         }
127 }
128
129 void Font::DrawNumberRight(int number, SDL_Surface *dest, const Vector<int> &positionIn, int digits) const {
130         if (!sprite) return;
131
132         Vector<int> position(positionIn);
133         if (digits > 0) {
134                 position.X() -= digits * CharWidth();
135         } else if (number == 0) {
136                 position.X() -= CharWidth();
137         } else {
138                 for (int i = number; i > 0; i /= 10) {
139                         position.X() -= CharWidth();
140                 }
141         }
142
143         DrawNumber(number, dest, position, digits);
144 }
145
146
147 void Font::CreateTypeDescription() {
148         Font f;
149
150         TypeDescription &td(TypeDescription::Create(TYPE_ID, "Font"));
151         td.SetDescription(
152                         "Simple font with fixed-width characters using a sprite for rendering.\n"
153                         "Characters from strings are mapped as follows:\n"
154                         "<pre>sprite column = column offset + (character % 16)\n"
155                         "sprite row    = row    offset + (character / 16)</pre>");
156         td.SetConstructor(&Construct);
157         td.SetSize(sizeof(Font));
158
159         td.AddField("sprite", FieldDescription(((char *)&f.sprite) - ((char *)&f), Sprite::TYPE_ID).SetReferenced().SetDescription("a sprite where each tile corresponds to a character"));
160         td.AddField("columnoffset", FieldDescription(((char *)&f.colOffset) - ((char *)&f), Interpreter::NUMBER_ID).SetDescription("offset of the column of the first character"));
161         td.AddField("rowoffset", FieldDescription(((char *)&f.rowOffset) - ((char *)&f), Interpreter::NUMBER_ID).SetDescription("offset of the row of the first character"));
162 }
163
164 void Font::Construct(void *data) {
165         new (data) Font;
166 }
167
168 }