]> git.localhorst.tv Git - blank.git/blob - src/graphics/render.cpp
3d0bbd47c6cf2e00feb6efb10798c22469a694a8
[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 bool Font::Kerning() const noexcept {
38         return TTF_GetFontKerning(handle);
39 }
40
41 void Font::Kerning(bool b) noexcept {
42         TTF_SetFontKerning(handle, b);
43 }
44
45
46 int Font::Height() const noexcept {
47         return TTF_FontHeight(handle);
48 }
49
50 int Font::Ascent() const noexcept {
51         return TTF_FontAscent(handle);
52 }
53
54 int Font::Descent() const noexcept {
55         return TTF_FontDescent(handle);
56 }
57
58 int Font::LineSkip() const noexcept {
59         return TTF_FontLineSkip(handle);
60 }
61
62
63 bool Font::HasGlyph(Uint16 c) const noexcept {
64         return TTF_GlyphIsProvided(handle, c);
65 }
66
67
68 glm::tvec2<int> Font::TextSize(const char *text) const {
69         glm::tvec2<int> size;
70         if (TTF_SizeUTF8(handle, text, &size.x, &size.y) != 0) {
71                 throw std::runtime_error(TTF_GetError());
72         }
73         return size;
74 }
75
76 Texture Font::Render(const char *text, SDL_Color color) const {
77         SDL_Surface *srf = TTF_RenderUTF8_Blended(handle, text, color);
78         if (!srf) {
79                 throw std::runtime_error(TTF_GetError());
80         }
81         Texture tex;
82         tex.Bind();
83         tex.Data(*srf, false);
84         tex.FilterLinear();
85         SDL_FreeSurface(srf);
86         return tex;
87 }
88
89
90 void Format::ReadPixelFormat(const SDL_PixelFormat &fmt) {
91         if (fmt.BytesPerPixel == 4) {
92                 if (fmt.Amask == 0xFF) {
93                         if (fmt.Rmask == 0xFF00) {
94                                 format = GL_BGRA;
95                         } else {
96                                 format = GL_RGBA;
97                         }
98                         type = GL_UNSIGNED_INT_8_8_8_8;
99                 } else {
100                         if (fmt.Rmask == 0xFF) {
101                                 format = GL_RGBA;
102                         } else {
103                                 format = GL_BGRA;
104                         }
105                         type = GL_UNSIGNED_INT_8_8_8_8_REV;
106                 }
107                 internal = GL_RGBA8;
108         } else {
109                 if (fmt.Rmask == 0xFF) {
110                         format = GL_RGB;
111                 } else {
112                         format = GL_BGR;
113                 }
114                 type = GL_UNSIGNED_BYTE;
115                 internal = GL_RGB8;
116         }
117 }
118
119
120 Texture::Texture()
121 : handle(0)
122 , width(0)
123 , height(0) {
124         glGenTextures(1, &handle);
125 }
126
127 Texture::~Texture() {
128         if (handle != 0) {
129                 glDeleteTextures(1, &handle);
130         }
131 }
132
133 Texture::Texture(Texture &&other) noexcept
134 : handle(other.handle) {
135         other.handle = 0;
136         width = other.width;
137         height = other.height;
138 }
139
140 Texture &Texture::operator =(Texture &&other) noexcept {
141         std::swap(handle, other.handle);
142         width = other.width;
143         height = other.height;
144         return *this;
145 }
146
147
148 void Texture::Bind() noexcept {
149         glBindTexture(GL_TEXTURE_2D, handle);
150 }
151
152 namespace {
153         bool ispow2(unsigned int i) {
154                 // don't care about i == 0 here
155                 return !(i & (i - 1));
156         }
157 }
158
159 void Texture::Data(const SDL_Surface &srf, bool pad2) noexcept {
160         Format format;
161         format.ReadPixelFormat(*srf.format);
162
163         if (!pad2 || (ispow2(srf.w) && ispow2(srf.h))) {
164                 int align = UnpackAlignmentFromPitch(srf.pitch);
165
166                 int pitch = (srf.w * srf.format->BytesPerPixel + align - 1) / align * align;
167                 if (srf.pitch - pitch >= align) {
168                         UnpackRowLength(srf.pitch / srf.format->BytesPerPixel);
169                 } else {
170                         UnpackRowLength(0);
171                 }
172
173                 Data(srf.w, srf.h, format, srf.pixels);
174
175                 UnpackRowLength(0);
176         } else if (srf.w > (1 << 30) || srf.h > (1 << 30)) {
177 #ifndef NDEBUG
178                 throw std::runtime_error("texture too large");
179 #endif
180         } else {
181                 GLsizei width = 1, height = 1;
182                 while (width < srf.w) {
183                         width <<= 1;
184                 }
185                 while (height < srf.h) {
186                         height <<= 1;
187                 }
188                 size_t pitch = width * srf.format->BytesPerPixel;
189                 size_t size = pitch * height;
190                 size_t row_pad = pitch - srf.pitch;
191                 std::unique_ptr<unsigned char[]> data(new unsigned char[size]);
192                 unsigned char *src = reinterpret_cast<unsigned char *>(srf.pixels);
193                 unsigned char *dst = data.get();
194                 for (int row = 0; row < srf.h; ++row) {
195                         std::memcpy(dst, src, srf.pitch);
196                         src += srf.pitch;
197                         dst += srf.pitch;
198                         std::memset(dst, 0, row_pad);
199                         dst += row_pad;
200                 }
201                 std::memset(dst, 0, (height - srf.h) * pitch);
202                 UnpackAlignmentFromPitch(pitch);
203                 Data(width, height, format, data.get());
204         }
205
206         UnpackAlignment(4);
207 }
208
209 void Texture::Data(GLsizei w, GLsizei h, const Format &format, GLvoid *data) noexcept {
210         glTexImage2D(
211                 GL_TEXTURE_2D,
212                 0, format.internal,
213                 w, h,
214                 0,
215                 format.format, format.type,
216                 data
217         );
218         width = w;
219         height = h;
220 }
221
222
223 void Texture::FilterNearest() noexcept {
224         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
225         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
226 }
227
228 void Texture::FilterLinear() noexcept {
229         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
230         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
231 }
232
233 void Texture::FilterTrilinear() noexcept {
234         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
235         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
236         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
237         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
238         glGenerateMipmap(GL_TEXTURE_2D);
239 }
240
241
242 void Texture::UnpackAlignment(GLint i) noexcept {
243         glPixelStorei(GL_UNPACK_ALIGNMENT, i);
244 }
245
246 int Texture::UnpackAlignmentFromPitch(int pitch) noexcept {
247         int align = 8;
248         while (pitch % align) {
249                 align >>= 1;
250         }
251         UnpackAlignment(align);
252         return align;
253 }
254
255 void Texture::UnpackRowLength(GLint i) noexcept {
256         glPixelStorei(GL_UNPACK_ROW_LENGTH, i);
257 }
258
259 }