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