]> git.localhorst.tv Git - blank.git/blob - src/graphics/render.cpp
set and display block type labels
[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, SDL_Color color) const {
111         Texture tex;
112         return tex;
113 }
114
115 void Font::Render(const char *text, SDL_Color color, Texture &tex) const {
116         SDL_Surface *srf = TTF_RenderUTF8_Blended(handle, text, color);
117         if (!srf) {
118                 throw std::runtime_error(TTF_GetError());
119         }
120         tex.Bind();
121         tex.Data(*srf, false);
122         tex.FilterLinear();
123         SDL_FreeSurface(srf);
124 }
125
126
127 void Format::ReadPixelFormat(const SDL_PixelFormat &fmt) {
128         if (fmt.BytesPerPixel == 4) {
129                 if (fmt.Amask == 0xFF) {
130                         if (fmt.Rmask == 0xFF00) {
131                                 format = GL_BGRA;
132                         } else {
133                                 format = GL_RGBA;
134                         }
135                         type = GL_UNSIGNED_INT_8_8_8_8;
136                 } else {
137                         if (fmt.Rmask == 0xFF) {
138                                 format = GL_RGBA;
139                         } else {
140                                 format = GL_BGRA;
141                         }
142                         type = GL_UNSIGNED_INT_8_8_8_8_REV;
143                 }
144                 internal = GL_RGBA8;
145         } else {
146                 if (fmt.Rmask == 0xFF) {
147                         format = GL_RGB;
148                 } else {
149                         format = GL_BGR;
150                 }
151                 type = GL_UNSIGNED_BYTE;
152                 internal = GL_RGB8;
153         }
154 }
155
156
157 Texture::Texture()
158 : handle(0)
159 , width(0)
160 , height(0) {
161         glGenTextures(1, &handle);
162 }
163
164 Texture::~Texture() {
165         if (handle != 0) {
166                 glDeleteTextures(1, &handle);
167         }
168 }
169
170 Texture::Texture(Texture &&other) noexcept
171 : handle(other.handle) {
172         other.handle = 0;
173         width = other.width;
174         height = other.height;
175 }
176
177 Texture &Texture::operator =(Texture &&other) noexcept {
178         std::swap(handle, other.handle);
179         width = other.width;
180         height = other.height;
181         return *this;
182 }
183
184
185 void Texture::Bind() noexcept {
186         glBindTexture(GL_TEXTURE_2D, handle);
187 }
188
189 namespace {
190         bool ispow2(unsigned int i) {
191                 // don't care about i == 0 here
192                 return !(i & (i - 1));
193         }
194 }
195
196 void Texture::Data(const SDL_Surface &srf, bool pad2) noexcept {
197         Format format;
198         format.ReadPixelFormat(*srf.format);
199
200         if (!pad2 || (ispow2(srf.w) && ispow2(srf.h))) {
201                 int align = UnpackAlignmentFromPitch(srf.pitch);
202
203                 int pitch = (srf.w * srf.format->BytesPerPixel + align - 1) / align * align;
204                 if (srf.pitch - pitch >= align) {
205                         UnpackRowLength(srf.pitch / srf.format->BytesPerPixel);
206                 } else {
207                         UnpackRowLength(0);
208                 }
209
210                 Data(srf.w, srf.h, format, srf.pixels);
211
212                 UnpackRowLength(0);
213         } else if (srf.w > (1 << 30) || srf.h > (1 << 30)) {
214 #ifndef NDEBUG
215                 throw std::runtime_error("texture too large");
216 #endif
217         } else {
218                 GLsizei width = 1, height = 1;
219                 while (width < srf.w) {
220                         width <<= 1;
221                 }
222                 while (height < srf.h) {
223                         height <<= 1;
224                 }
225                 size_t pitch = width * srf.format->BytesPerPixel;
226                 size_t size = pitch * height;
227                 size_t row_pad = pitch - srf.pitch;
228                 std::unique_ptr<unsigned char[]> data(new unsigned char[size]);
229                 unsigned char *src = reinterpret_cast<unsigned char *>(srf.pixels);
230                 unsigned char *dst = data.get();
231                 for (int row = 0; row < srf.h; ++row) {
232                         std::memcpy(dst, src, srf.pitch);
233                         src += srf.pitch;
234                         dst += srf.pitch;
235                         std::memset(dst, 0, row_pad);
236                         dst += row_pad;
237                 }
238                 std::memset(dst, 0, (height - srf.h) * pitch);
239                 UnpackAlignmentFromPitch(pitch);
240                 Data(width, height, format, data.get());
241         }
242
243         UnpackAlignment(4);
244 }
245
246 void Texture::Data(GLsizei w, GLsizei h, const Format &format, GLvoid *data) noexcept {
247         glTexImage2D(
248                 GL_TEXTURE_2D,
249                 0, format.internal,
250                 w, h,
251                 0,
252                 format.format, format.type,
253                 data
254         );
255         width = w;
256         height = h;
257 }
258
259
260 void Texture::FilterNearest() noexcept {
261         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
262         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
263 }
264
265 void Texture::FilterLinear() noexcept {
266         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
267         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
268 }
269
270 void Texture::FilterTrilinear() noexcept {
271         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
272         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
273         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
274         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
275         glGenerateMipmap(GL_TEXTURE_2D);
276 }
277
278
279 void Texture::UnpackAlignment(GLint i) noexcept {
280         glPixelStorei(GL_UNPACK_ALIGNMENT, i);
281 }
282
283 int Texture::UnpackAlignmentFromPitch(int pitch) noexcept {
284         int align = 8;
285         while (pitch % align) {
286                 align >>= 1;
287         }
288         UnpackAlignment(align);
289         return align;
290 }
291
292 void Texture::UnpackRowLength(GLint i) noexcept {
293         glPixelStorei(GL_UNPACK_ROW_LENGTH, i);
294 }
295
296 }