]> git.localhorst.tv Git - blank.git/blob - src/graphics/render.cpp
9468b68b794b8640558c986870f885c5e171c26d
[blank.git] / src / graphics / render.cpp
1 #include "ArrayTexture.hpp"
2 #include "CubeMap.hpp"
3 #include "Font.hpp"
4 #include "Format.hpp"
5 #include "Texture.hpp"
6 #include "TextureBase.hpp"
7 #include "Viewport.hpp"
8
9 #include "../app/init.hpp"
10
11 #include <algorithm>
12 #include <cstring>
13 #include <memory>
14 #include <stdexcept>
15
16
17 namespace blank {
18
19 Font::Font(const char *src, int size, long index)
20 : handle(TTF_OpenFontIndex(src, size, index)) {
21         if (!handle) {
22                 throw std::runtime_error(TTF_GetError());
23         }
24 }
25
26 Font::~Font() {
27         if (handle) {
28                 TTF_CloseFont(handle);
29         }
30 }
31
32 Font::Font(Font &&other) noexcept
33 : handle(other.handle) {
34         other.handle = nullptr;
35 }
36
37 Font &Font::operator =(Font &&other) noexcept {
38         std::swap(handle, other.handle);
39         return *this;
40 }
41
42
43 int Font::Style() const noexcept {
44         return TTF_GetFontStyle(handle);
45 }
46
47 void Font::Style(int s) const noexcept {
48         TTF_SetFontStyle(handle, s);
49 }
50
51 int Font::Outline() const noexcept {
52         return TTF_GetFontOutline(handle);
53 }
54
55 void Font::Outline(int px) noexcept {
56         TTF_SetFontOutline(handle, px);
57 }
58
59
60 int Font::Hinting() const noexcept {
61         return TTF_GetFontHinting(handle);
62 }
63
64 void Font::Hinting(int h) const noexcept {
65         TTF_SetFontHinting(handle, h);
66 }
67
68 bool Font::Kerning() const noexcept {
69         return TTF_GetFontKerning(handle);
70 }
71
72 void Font::Kerning(bool b) noexcept {
73         TTF_SetFontKerning(handle, b);
74 }
75
76
77 int Font::Height() const noexcept {
78         return TTF_FontHeight(handle);
79 }
80
81 int Font::Ascent() const noexcept {
82         return TTF_FontAscent(handle);
83 }
84
85 int Font::Descent() const noexcept {
86         return TTF_FontDescent(handle);
87 }
88
89 int Font::LineSkip() const noexcept {
90         return TTF_FontLineSkip(handle);
91 }
92
93
94 const char *Font::FamilyName() const noexcept {
95         return TTF_FontFaceFamilyName(handle);
96 }
97
98 const char *Font::StyleName() const noexcept {
99         return TTF_FontFaceStyleName(handle);
100 }
101
102
103 bool Font::HasGlyph(Uint16 c) const noexcept {
104         return TTF_GlyphIsProvided(handle, c);
105 }
106
107
108 glm::ivec2 Font::TextSize(const char *text) const {
109         glm::ivec2 size;
110         if (TTF_SizeUTF8(handle, text, &size.x, &size.y) != 0) {
111                 throw std::runtime_error(TTF_GetError());
112         }
113         return size;
114 }
115
116 Texture Font::Render(const char *text) const {
117         Texture tex;
118         Render(text, tex);
119         return tex;
120 }
121
122 void Font::Render(const char *text, Texture &tex) const {
123         SDL_Surface *srf = TTF_RenderUTF8_Blended(handle, text, { 0xFF, 0xFF, 0xFF, 0xFF });
124         if (!srf) {
125                 throw std::runtime_error(TTF_GetError());
126         }
127         tex.Bind();
128         tex.Data(*srf, false);
129         tex.FilterLinear();
130         SDL_FreeSurface(srf);
131 }
132
133 Format::Format() noexcept
134 : format(GL_BGRA)
135 , type(GL_UNSIGNED_INT_8_8_8_8_REV)
136 , internal(GL_RGBA8) {
137         sdl_format.format = SDL_PIXELFORMAT_ARGB8888;
138         sdl_format.palette = nullptr;
139         sdl_format.BitsPerPixel = 32;
140         sdl_format.BytesPerPixel = 4;
141         sdl_format.Rmask = 0x00FF0000;
142         sdl_format.Gmask = 0x0000FF00;
143         sdl_format.Bmask = 0x000000FF;
144         sdl_format.Amask = 0xFF000000;
145         sdl_format.Rloss = 0;
146         sdl_format.Gloss = 0;
147         sdl_format.Bloss = 0;
148         sdl_format.Aloss = 0;
149         sdl_format.Rshift = 16;
150         sdl_format.Gshift = 8;
151         sdl_format.Bshift = 0;
152         sdl_format.Ashift = 24;
153         sdl_format.refcount = 1;
154         sdl_format.next = nullptr;
155 }
156
157 Format::Format(const SDL_PixelFormat &fmt) noexcept
158 : sdl_format(fmt) {
159         if (fmt.BytesPerPixel == 4) {
160                 if (fmt.Amask == 0xFF) {
161                         if (fmt.Rmask == 0xFF00) {
162                                 format = GL_BGRA;
163                         } else {
164                                 format = GL_RGBA;
165                         }
166                         type = GL_UNSIGNED_INT_8_8_8_8;
167                 } else {
168                         if (fmt.Rmask == 0xFF) {
169                                 format = GL_RGBA;
170                         } else {
171                                 format = GL_BGRA;
172                         }
173                         type = GL_UNSIGNED_INT_8_8_8_8_REV;
174                 }
175                 internal = GL_RGBA8;
176         } else {
177                 if (fmt.Rmask == 0xFF) {
178                         format = GL_RGB;
179                 } else {
180                         format = GL_BGR;
181                 }
182                 type = GL_UNSIGNED_BYTE;
183                 internal = GL_RGB8;
184         }
185 }
186
187 bool Format::Compatible(const Format &other) const noexcept {
188         return format == other.format && type == other.type && internal == other.internal;
189 }
190
191
192 template<GLenum TARGET, GLsizei COUNT>
193 TextureBase<TARGET, COUNT>::TextureBase() {
194         glGenTextures(COUNT, handle);
195 }
196
197 template<GLenum TARGET, GLsizei COUNT>
198 TextureBase<TARGET, COUNT>::~TextureBase() {
199         glDeleteTextures(COUNT, handle);
200 }
201
202 template<GLenum TARGET, GLsizei COUNT>
203 TextureBase<TARGET, COUNT>::TextureBase(TextureBase &&other) noexcept {
204         std::memcpy(handle, other.handle, sizeof(handle));
205         std::memset(other.handle, 0, sizeof(handle));
206 }
207
208 template<GLenum TARGET, GLsizei COUNT>
209 TextureBase<TARGET, COUNT> &TextureBase<TARGET, COUNT>::operator =(TextureBase &&other) noexcept {
210         std::swap(handle, other.handle);
211         return *this;
212 }
213
214
215 Texture::Texture()
216 : TextureBase()
217 , width(0)
218 , height(0) {
219
220 }
221
222 Texture::~Texture() {
223
224 }
225
226 Texture::Texture(Texture &&other) noexcept
227 : TextureBase(std::move(other)) {
228         width = other.width;
229         height = other.height;
230 }
231
232 Texture &Texture::operator =(Texture &&other) noexcept {
233         TextureBase::operator =(std::move(other));
234         width = other.width;
235         height = other.height;
236         return *this;
237 }
238
239
240 namespace {
241         bool ispow2(unsigned int i) {
242                 // don't care about i == 0 here
243                 return !(i & (i - 1));
244         }
245 }
246
247 void Texture::Data(const SDL_Surface &srf, bool pad2) noexcept {
248         Format format(*srf.format);
249
250         if (!pad2 || (ispow2(srf.w) && ispow2(srf.h))) {
251                 int align = UnpackAlignmentFromPitch(srf.pitch);
252
253                 int pitch = (srf.w * srf.format->BytesPerPixel + align - 1) / align * align;
254                 if (srf.pitch - pitch >= align) {
255                         UnpackRowLength(srf.pitch / srf.format->BytesPerPixel);
256                 } else {
257                         UnpackRowLength(0);
258                 }
259
260                 Data(srf.w, srf.h, format, srf.pixels);
261
262                 UnpackRowLength(0);
263         } else if (srf.w > (1 << 30) || srf.h > (1 << 30)) {
264 #ifndef NDEBUG
265                 throw std::runtime_error("texture too large");
266 #endif
267         } else {
268                 GLsizei width = 1, height = 1;
269                 while (width < srf.w) {
270                         width <<= 1;
271                 }
272                 while (height < srf.h) {
273                         height <<= 1;
274                 }
275                 size_t pitch = width * srf.format->BytesPerPixel;
276                 size_t size = pitch * height;
277                 size_t row_pad = pitch - srf.pitch;
278                 std::unique_ptr<unsigned char[]> data(new unsigned char[size]);
279                 unsigned char *src = reinterpret_cast<unsigned char *>(srf.pixels);
280                 unsigned char *dst = data.get();
281                 for (int row = 0; row < srf.h; ++row) {
282                         std::memcpy(dst, src, srf.pitch);
283                         src += srf.pitch;
284                         dst += srf.pitch;
285                         std::memset(dst, 0, row_pad);
286                         dst += row_pad;
287                 }
288                 std::memset(dst, 0, (height - srf.h) * pitch);
289                 UnpackAlignmentFromPitch(pitch);
290                 Data(width, height, format, data.get());
291         }
292
293         UnpackAlignment(4);
294 }
295
296 void Texture::Data(GLsizei w, GLsizei h, const Format &format, GLvoid *data) noexcept {
297         glTexImage2D(
298                 GL_TEXTURE_2D,
299                 0, format.internal,
300                 w, h,
301                 0,
302                 format.format, format.type,
303                 data
304         );
305         width = w;
306         height = h;
307 }
308
309
310 void Texture::UnpackAlignment(GLint i) noexcept {
311         glPixelStorei(GL_UNPACK_ALIGNMENT, i);
312 }
313
314 int Texture::UnpackAlignmentFromPitch(int pitch) noexcept {
315         int align = 8;
316         while (pitch % align) {
317                 align >>= 1;
318         }
319         UnpackAlignment(align);
320         return align;
321 }
322
323 void Texture::UnpackRowLength(GLint i) noexcept {
324         glPixelStorei(GL_UNPACK_ROW_LENGTH, i);
325 }
326
327
328 ArrayTexture::ArrayTexture()
329 : TextureBase()
330 , width(0)
331 , height(0)
332 , depth(0) {
333
334 }
335
336 ArrayTexture::~ArrayTexture() {
337
338 }
339
340 ArrayTexture::ArrayTexture(ArrayTexture &&other) noexcept
341 : TextureBase(std::move(other)) {
342         width = other.width;
343         height = other.height;
344         depth = other.depth;
345 }
346
347 ArrayTexture &ArrayTexture::operator =(ArrayTexture &&other) noexcept {
348         TextureBase::operator =(std::move(other));
349         width = other.width;
350         height = other.height;
351         depth = other.depth;
352         return *this;
353 }
354
355
356 void ArrayTexture::Reserve(GLsizei w, GLsizei h, GLsizei d, const Format &f) noexcept {
357         glTexStorage3D(
358                 GL_TEXTURE_2D_ARRAY, // which
359                 1,                   // mipmap count
360                 f.internal,          // format
361                 w, h,                // dimensions
362                 d                    // layer count
363         );
364         width = w;
365         height = h;
366         depth = d;
367         format = f;
368 }
369
370 void ArrayTexture::Data(GLsizei l, const SDL_Surface &srf) {
371         Format fmt(*srf.format);
372         if (format.Compatible(fmt)) {
373                 Data(l, fmt, srf.pixels);
374         } else {
375                 SDL_Surface *converted = SDL_ConvertSurface(
376                         const_cast<SDL_Surface *>(&srf),
377                         &format.sdl_format,
378                         0
379                 );
380                 if (!converted) {
381                         throw SDLError("SDL_ConvertSurface");
382                 }
383                 Format new_fmt(*converted->format);
384                 if (!format.Compatible(new_fmt)) {
385                         SDL_FreeSurface(converted);
386                         throw std::runtime_error("unable to convert texture input");
387                 }
388                 Data(l, new_fmt, converted->pixels);
389                 SDL_FreeSurface(converted);
390         }
391 }
392
393 void ArrayTexture::Data(GLsizei l, const Format &f, GLvoid *data) noexcept {
394         glTexSubImage3D(
395                 GL_TEXTURE_2D_ARRAY, // which
396                 0,                   // mipmap lavel
397                 0, 0,                // dest X and Y offset
398                 l,                   // layer offset
399                 width, height,
400                 1,                   // layer count
401                 f.format, f.type,
402                 data
403         );
404 }
405
406
407 CubeMap::CubeMap()
408 : TextureBase() {
409
410 }
411
412 CubeMap::~CubeMap() {
413
414 }
415
416 CubeMap::CubeMap(CubeMap &&other) noexcept
417 : TextureBase(std::move(other)) {
418
419 }
420
421 CubeMap &CubeMap::operator =(CubeMap &&other) noexcept {
422         TextureBase::operator =(std::move(other));
423         return *this;
424 }
425
426
427 void CubeMap::Data(Face f, const SDL_Surface &srf) {
428         Format format;
429         Format fmt(*srf.format);
430         if (format.Compatible(fmt)) {
431                 Data(f, srf.w, srf.h, fmt, srf.pixels);
432         } else {
433                 SDL_Surface *converted = SDL_ConvertSurface(
434                         const_cast<SDL_Surface *>(&srf),
435                         &format.sdl_format,
436                         0
437                 );
438                 if (!converted) {
439                         throw SDLError("SDL_ConvertSurface");
440                 }
441                 Format new_fmt(*converted->format);
442                 if (!format.Compatible(new_fmt)) {
443                         SDL_FreeSurface(converted);
444                         throw std::runtime_error("unable to convert texture input");
445                 }
446                 Data(f, converted->w, converted->h, new_fmt, converted->pixels);
447                 SDL_FreeSurface(converted);
448         }
449 }
450
451 void CubeMap::Data(Face face, GLsizei w, GLsizei h, const Format &f, GLvoid *data) noexcept {
452         glTexImage2D(
453                 face,             // which
454                 0,                // mipmap level
455                 f.internal,       // internal format
456                 w, h,             // size
457                 0,                // border
458                 f.format, f.type, // pixel format
459                 data              // pixel data
460         );
461 }
462
463 }