]> git.localhorst.tv Git - blank.git/blob - src/graphics/render.cpp
move font color from texture to uniform
[blank.git] / src / graphics / render.cpp
1 #include "Font.hpp"
2 #include "Format.hpp"
3 #include "Texture.hpp"
4
5 #include <algorithm>
6 #include <cstring>
7 #include <memory>
8 #include <stdexcept>
9
10
11 namespace blank {
12
13 Font::Font(const char *src, int size, long index)
14 : handle(TTF_OpenFontIndex(src, size, index)) {
15         if (!handle) {
16                 throw std::runtime_error(TTF_GetError());
17         }
18 }
19
20 Font::~Font() {
21         if (handle) {
22                 TTF_CloseFont(handle);
23         }
24 }
25
26 Font::Font(Font &&other) noexcept
27 : handle(other.handle) {
28         other.handle = nullptr;
29 }
30
31 Font &Font::operator =(Font &&other) noexcept {
32         std::swap(handle, other.handle);
33         return *this;
34 }
35
36
37 int Font::Style() const noexcept {
38         return TTF_GetFontStyle(handle);
39 }
40
41 void Font::Style(int s) const noexcept {
42         TTF_SetFontStyle(handle, s);
43 }
44
45 int Font::Outline() const noexcept {
46         return TTF_GetFontOutline(handle);
47 }
48
49 void Font::Outline(int px) noexcept {
50         TTF_SetFontOutline(handle, px);
51 }
52
53
54 int Font::Hinting() const noexcept {
55         return TTF_GetFontHinting(handle);
56 }
57
58 void Font::Hinting(int h) const noexcept {
59         TTF_SetFontHinting(handle, h);
60 }
61
62 bool Font::Kerning() const noexcept {
63         return TTF_GetFontKerning(handle);
64 }
65
66 void Font::Kerning(bool b) noexcept {
67         TTF_SetFontKerning(handle, b);
68 }
69
70
71 int Font::Height() const noexcept {
72         return TTF_FontHeight(handle);
73 }
74
75 int Font::Ascent() const noexcept {
76         return TTF_FontAscent(handle);
77 }
78
79 int Font::Descent() const noexcept {
80         return TTF_FontDescent(handle);
81 }
82
83 int Font::LineSkip() const noexcept {
84         return TTF_FontLineSkip(handle);
85 }
86
87
88 const char *Font::FamilyName() const noexcept {
89         return TTF_FontFaceFamilyName(handle);
90 }
91
92 const char *Font::StyleName() const noexcept {
93         return TTF_FontFaceStyleName(handle);
94 }
95
96
97 bool Font::HasGlyph(Uint16 c) const noexcept {
98         return TTF_GlyphIsProvided(handle, c);
99 }
100
101
102 glm::tvec2<int> Font::TextSize(const char *text) const {
103         glm::tvec2<int> size;
104         if (TTF_SizeUTF8(handle, text, &size.x, &size.y) != 0) {
105                 throw std::runtime_error(TTF_GetError());
106         }
107         return size;
108 }
109
110 Texture Font::Render(const char *text) const {
111         Texture tex;
112         Render(text, tex);
113         return tex;
114 }
115
116 void Font::Render(const char *text, Texture &tex) const {
117         SDL_Surface *srf = TTF_RenderUTF8_Blended(handle, text, { 0xFF, 0xFF, 0xFF, 0xFF });
118         if (!srf) {
119                 throw std::runtime_error(TTF_GetError());
120         }
121         tex.Bind();
122         tex.Data(*srf, false);
123         tex.FilterLinear();
124         SDL_FreeSurface(srf);
125 }
126
127
128 void Format::ReadPixelFormat(const SDL_PixelFormat &fmt) {
129         if (fmt.BytesPerPixel == 4) {
130                 if (fmt.Amask == 0xFF) {
131                         if (fmt.Rmask == 0xFF00) {
132                                 format = GL_BGRA;
133                         } else {
134                                 format = GL_RGBA;
135                         }
136                         type = GL_UNSIGNED_INT_8_8_8_8;
137                 } else {
138                         if (fmt.Rmask == 0xFF) {
139                                 format = GL_RGBA;
140                         } else {
141                                 format = GL_BGRA;
142                         }
143                         type = GL_UNSIGNED_INT_8_8_8_8_REV;
144                 }
145                 internal = GL_RGBA8;
146         } else {
147                 if (fmt.Rmask == 0xFF) {
148                         format = GL_RGB;
149                 } else {
150                         format = GL_BGR;
151                 }
152                 type = GL_UNSIGNED_BYTE;
153                 internal = GL_RGB8;
154         }
155 }
156
157
158 Texture::Texture()
159 : handle(0)
160 , width(0)
161 , height(0) {
162         glGenTextures(1, &handle);
163 }
164
165 Texture::~Texture() {
166         if (handle != 0) {
167                 glDeleteTextures(1, &handle);
168         }
169 }
170
171 Texture::Texture(Texture &&other) noexcept
172 : handle(other.handle) {
173         other.handle = 0;
174         width = other.width;
175         height = other.height;
176 }
177
178 Texture &Texture::operator =(Texture &&other) noexcept {
179         std::swap(handle, other.handle);
180         width = other.width;
181         height = other.height;
182         return *this;
183 }
184
185
186 void Texture::Bind() noexcept {
187         glBindTexture(GL_TEXTURE_2D, handle);
188 }
189
190 namespace {
191         bool ispow2(unsigned int i) {
192                 // don't care about i == 0 here
193                 return !(i & (i - 1));
194         }
195 }
196
197 void Texture::Data(const SDL_Surface &srf, bool pad2) noexcept {
198         Format format;
199         format.ReadPixelFormat(*srf.format);
200
201         if (!pad2 || (ispow2(srf.w) && ispow2(srf.h))) {
202                 int align = UnpackAlignmentFromPitch(srf.pitch);
203
204                 int pitch = (srf.w * srf.format->BytesPerPixel + align - 1) / align * align;
205                 if (srf.pitch - pitch >= align) {
206                         UnpackRowLength(srf.pitch / srf.format->BytesPerPixel);
207                 } else {
208                         UnpackRowLength(0);
209                 }
210
211                 Data(srf.w, srf.h, format, srf.pixels);
212
213                 UnpackRowLength(0);
214         } else if (srf.w > (1 << 30) || srf.h > (1 << 30)) {
215 #ifndef NDEBUG
216                 throw std::runtime_error("texture too large");
217 #endif
218         } else {
219                 GLsizei width = 1, height = 1;
220                 while (width < srf.w) {
221                         width <<= 1;
222                 }
223                 while (height < srf.h) {
224                         height <<= 1;
225                 }
226                 size_t pitch = width * srf.format->BytesPerPixel;
227                 size_t size = pitch * height;
228                 size_t row_pad = pitch - srf.pitch;
229                 std::unique_ptr<unsigned char[]> data(new unsigned char[size]);
230                 unsigned char *src = reinterpret_cast<unsigned char *>(srf.pixels);
231                 unsigned char *dst = data.get();
232                 for (int row = 0; row < srf.h; ++row) {
233                         std::memcpy(dst, src, srf.pitch);
234                         src += srf.pitch;
235                         dst += srf.pitch;
236                         std::memset(dst, 0, row_pad);
237                         dst += row_pad;
238                 }
239                 std::memset(dst, 0, (height - srf.h) * pitch);
240                 UnpackAlignmentFromPitch(pitch);
241                 Data(width, height, format, data.get());
242         }
243
244         UnpackAlignment(4);
245 }
246
247 void Texture::Data(GLsizei w, GLsizei h, const Format &format, GLvoid *data) noexcept {
248         glTexImage2D(
249                 GL_TEXTURE_2D,
250                 0, format.internal,
251                 w, h,
252                 0,
253                 format.format, format.type,
254                 data
255         );
256         width = w;
257         height = h;
258 }
259
260
261 void Texture::FilterNearest() noexcept {
262         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
263         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
264 }
265
266 void Texture::FilterLinear() noexcept {
267         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
268         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
269 }
270
271 void Texture::FilterTrilinear() noexcept {
272         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
273         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
274         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
275         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
276         glGenerateMipmap(GL_TEXTURE_2D);
277 }
278
279
280 void Texture::UnpackAlignment(GLint i) noexcept {
281         glPixelStorei(GL_UNPACK_ALIGNMENT, i);
282 }
283
284 int Texture::UnpackAlignmentFromPitch(int pitch) noexcept {
285         int align = 8;
286         while (pitch % align) {
287                 align >>= 1;
288         }
289         UnpackAlignment(align);
290         return align;
291 }
292
293 void Texture::UnpackRowLength(GLint i) noexcept {
294         glPixelStorei(GL_UNPACK_ROW_LENGTH, i);
295 }
296
297 }