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