~ppsspp/ppsspp/ppsspp_1.3.0

« back to all changes in this revision

Viewing changes to ext/native/gfx_es2/draw_buffer.cpp

  • Committer: Sérgio Benjamim
  • Date: 2017-01-02 00:12:05 UTC
  • Revision ID: sergio_br2@yahoo.com.br-20170102001205-cxbta9za203nmjwm
1.3.0 source (from ppsspp_1.3.0-r160.p5.l1762.a165.t83~56~ubuntu16.04.1.tar.xz).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <algorithm>
 
2
#include <cmath>
 
3
#include <vector>
 
4
#include <stddef.h>
 
5
 
 
6
#include "base/display.h"
 
7
#include "base/logging.h"
 
8
#include "base/stringutil.h"
 
9
#include "math/math_util.h"
 
10
#include "gfx/texture_atlas.h"
 
11
#include "gfx/gl_debug_log.h"
 
12
#include "gfx/gl_common.h"
 
13
#include "gfx_es2/draw_buffer.h"
 
14
#include "gfx_es2/draw_text.h"
 
15
#include "gfx_es2/glsl_program.h"
 
16
#include "util/text/utf8.h"
 
17
#include "util/text/wrap_text.h"
 
18
 
 
19
enum {
 
20
        // Enough?
 
21
        MAX_VERTS = 65536,
 
22
};
 
23
 
 
24
DrawBuffer::DrawBuffer() : count_(0), atlas(0) {
 
25
        verts_ = new Vertex[MAX_VERTS];
 
26
        fontscalex = 1.0f;
 
27
        fontscaley = 1.0f;
 
28
        inited_ = false;
 
29
}
 
30
 
 
31
DrawBuffer::~DrawBuffer() {
 
32
        delete [] verts_;
 
33
}
 
34
 
 
35
void DrawBuffer::Init(Thin3DContext *t3d) {
 
36
        if (inited_)
 
37
                return;
 
38
 
 
39
        t3d_ = t3d;
 
40
        inited_ = true;
 
41
 
 
42
        std::vector<Thin3DVertexComponent> components;
 
43
        components.push_back(Thin3DVertexComponent("Position", SEM_POSITION, FLOATx3, 0));
 
44
        components.push_back(Thin3DVertexComponent("TexCoord0", SEM_TEXCOORD0, FLOATx2, 12));
 
45
        components.push_back(Thin3DVertexComponent("Color0", SEM_COLOR0, UNORM8x4, 20));
 
46
 
 
47
        Thin3DShader *vshader = t3d_->GetVshaderPreset(VS_TEXTURE_COLOR_2D);
 
48
 
 
49
        vformat_ = t3d_->CreateVertexFormat(components, 24, vshader);
 
50
        if (vformat_->RequiresBuffer()) {
 
51
                vbuf_ = t3d_->CreateBuffer(MAX_VERTS * sizeof(Vertex), T3DBufferUsage::DYNAMIC | T3DBufferUsage::VERTEXDATA);
 
52
        } else {
 
53
                vbuf_ = nullptr;
 
54
        }
 
55
}
 
56
 
 
57
void DrawBuffer::Shutdown() {
 
58
        if (vbuf_) {
 
59
                vbuf_->Release();
 
60
        }
 
61
        vformat_->Release();
 
62
 
 
63
        inited_ = false;
 
64
}
 
65
 
 
66
void DrawBuffer::Begin(Thin3DShaderSet *program, DrawBufferPrimitiveMode dbmode) {
 
67
        shaderSet_ = program;
 
68
        count_ = 0;
 
69
        mode_ = dbmode;
 
70
}
 
71
 
 
72
void DrawBuffer::End() {
 
73
        // Currently does nothing, but call it!
 
74
}
 
75
 
 
76
void DrawBuffer::Flush(bool set_blend_state) {
 
77
        if (!shaderSet_) {
 
78
                ELOG("No program set!");
 
79
                return;
 
80
        }
 
81
 
 
82
        if (count_ == 0)
 
83
                return;
 
84
 
 
85
        shaderSet_->SetMatrix4x4("WorldViewProj", drawMatrix_.getReadPtr());
 
86
 
 
87
        if (vbuf_) {
 
88
                vbuf_->SubData((const uint8_t *)verts_, 0, sizeof(Vertex) * count_);
 
89
                int offset = 0;
 
90
                t3d_->Draw(mode_ == DBMODE_NORMAL ? PRIM_TRIANGLES : PRIM_LINES, shaderSet_, vformat_, vbuf_, count_, offset);
 
91
        } else {
 
92
                t3d_->DrawUP(mode_ == DBMODE_NORMAL ? PRIM_TRIANGLES : PRIM_LINES, shaderSet_, vformat_, (const void *)verts_, count_);
 
93
        }
 
94
        count_ = 0;
 
95
}
 
96
 
 
97
void DrawBuffer::V(float x, float y, float z, uint32_t color, float u, float v) {
 
98
        if (count_ >= MAX_VERTS) {
 
99
                FLOG("Overflowed the DrawBuffer");
 
100
                return;
 
101
        }
 
102
 
 
103
        Vertex *vert = &verts_[count_++];
 
104
        vert->x = x;
 
105
        vert->y = y;
 
106
        vert->z = z;
 
107
        vert->rgba = color;
 
108
        vert->u = u;
 
109
        vert->v = v;
 
110
}
 
111
 
 
112
void DrawBuffer::Rect(float x, float y, float w, float h, uint32_t color, int align) {
 
113
        DoAlign(align, &x, &y, &w, &h);
 
114
        RectVGradient(x, y, w, h, color, color);
 
115
}
 
116
 
 
117
void DrawBuffer::hLine(float x1, float y, float x2, uint32_t color) {
 
118
        Rect(x1, y, x2 - x1, pixel_in_dps, color);
 
119
}
 
120
 
 
121
void DrawBuffer::vLine(float x, float y1, float y2, uint32_t color) {
 
122
        Rect(x, y1, pixel_in_dps, y2 - y1, color);
 
123
}
 
124
 
 
125
void DrawBuffer::vLineAlpha50(float x, float y1, float y2, uint32_t color) {
 
126
        Rect(x, y1, pixel_in_dps, y2 - y1, (color | 0xFF000000) & 0x7F000000);
 
127
}
 
128
 
 
129
void DrawBuffer::RectVGradient(float x, float y, float w, float h, uint32_t colorTop, uint32_t colorBottom) {
 
130
        V(x,             y,     0, colorTop,    0, 0);
 
131
        V(x + w, y,              0, colorTop,    1, 0);
 
132
        V(x + w, y + h, 0, colorBottom, 1, 1);
 
133
        V(x,             y,     0, colorTop,    0, 0);
 
134
        V(x + w, y + h, 0, colorBottom, 1, 1);
 
135
        V(x,             y + h, 0, colorBottom, 0, 1);
 
136
}
 
137
 
 
138
void DrawBuffer::RectOutline(float x, float y, float w, float h, uint32_t color, int align) {
 
139
        hLine(x, y, x + w + pixel_in_dps, color);
 
140
        hLine(x, y + h, x + w + pixel_in_dps, color);
 
141
 
 
142
        vLine(x, y, y + h + pixel_in_dps, color);
 
143
        vLine(x + w, y, y + h + pixel_in_dps, color);
 
144
}
 
145
 
 
146
void DrawBuffer::MultiVGradient(float x, float y, float w, float h, GradientStop *stops, int numStops) {
 
147
        for (int i = 0; i < numStops - 1; i++) {
 
148
                float t0 = stops[i].t, t1 = stops[i+1].t;
 
149
                uint32_t c0 = stops[i].t, c1 = stops[i+1].t;
 
150
                RectVGradient(x, y + h * t0, w, h * (t1 - t0), c0, c1);
 
151
        }
 
152
}
 
153
 
 
154
void DrawBuffer::Rect(float x, float y, float w, float h,
 
155
        float u, float v, float uw, float uh,
 
156
        uint32_t color) {
 
157
                V(x,       y,     0, color, u, v);
 
158
                V(x + w, y,        0, color, u + uw, v);
 
159
                V(x + w, y + h, 0, color, u + uw, v + uh);
 
160
                V(x,       y,     0, color, u, v);
 
161
                V(x + w, y + h, 0, color, u + uw, v + uh);
 
162
                V(x,       y + h, 0, color, u, v + uh);
 
163
}
 
164
 
 
165
void DrawBuffer::Line(int atlas_image, float x1, float y1, float x2, float y2, float thickness, uint32_t color) {
 
166
        const AtlasImage &image = atlas->images[atlas_image];
 
167
 
 
168
        // No caps yet!
 
169
        // Pre-rotated - we are making a thick line here
 
170
        float dx = -(y2 - y1);
 
171
        float dy = x2 - x1;
 
172
        float len = sqrtf(dx * dx + dy * dy) / thickness;
 
173
        if (len <= 0.0f)
 
174
                len = 1.0f;
 
175
 
 
176
        dx /= len;
 
177
        dy /= len;
 
178
 
 
179
        float x[4] = { x1 - dx, x2 - dx, x1 + dx, x2 + dx };
 
180
        float y[4] = { y1 - dy, y2 - dy, y1 + dy, y2 + dy };
 
181
 
 
182
        V(x[0], y[0], color, image.u1, image.v1);
 
183
        V(x[1], y[1], color, image.u2, image.v1);
 
184
        V(x[2], y[2], color, image.u1, image.v2);
 
185
        V(x[2], y[2], color, image.u1, image.v2);
 
186
        V(x[1], y[1], color, image.u2, image.v1);
 
187
        V(x[3], y[3], color, image.u2, image.v2);
 
188
}
 
189
 
 
190
void DrawBuffer::MeasureImage(ImageID atlas_image, float *w, float *h) {
 
191
        const AtlasImage &image = atlas->images[atlas_image];
 
192
        *w = (float)image.w;
 
193
        *h = (float)image.h;
 
194
}
 
195
 
 
196
void DrawBuffer::DrawImage(ImageID atlas_image, float x, float y, float scale, Color color, int align) {
 
197
        const AtlasImage &image = atlas->images[atlas_image];
 
198
        float w = (float)image.w * scale;
 
199
        float h = (float)image.h * scale;
 
200
        if (align & ALIGN_HCENTER) x -= w / 2;
 
201
        if (align & ALIGN_RIGHT) x -= w;
 
202
        if (align & ALIGN_VCENTER) y -= h / 2;
 
203
        if (align & ALIGN_BOTTOM) y -= h;
 
204
        DrawImageStretch(atlas_image, x, y, x + w, y + h, color);
 
205
}
 
206
 
 
207
void DrawBuffer::DrawImageStretch(ImageID atlas_image, float x1, float y1, float x2, float y2, Color color) {
 
208
        const AtlasImage &image = atlas->images[atlas_image];
 
209
        V(x1,   y1, color, image.u1, image.v1);
 
210
        V(x2,   y1, color, image.u2, image.v1);
 
211
        V(x2,   y2, color, image.u2, image.v2);
 
212
        V(x1,   y1, color, image.u1, image.v1);
 
213
        V(x2,   y2, color, image.u2, image.v2);
 
214
        V(x1,   y2, color, image.u1, image.v2);
 
215
}
 
216
 
 
217
inline void rot(float *v, float angle, float xc, float yc) {
 
218
        const float x = v[0] - xc;
 
219
        const float y = v[1] - yc;
 
220
        const float sa = sinf(angle);
 
221
        const float ca = cosf(angle);
 
222
        v[0] = x * ca + y * -sa + xc;
 
223
        v[1] = x * sa + y *  ca + yc;
 
224
}
 
225
 
 
226
void DrawBuffer::DrawImageRotated(ImageID atlas_image, float x, float y, float scale, float angle, Color color, bool mirror_h) {
 
227
        const AtlasImage &image = atlas->images[atlas_image];
 
228
        float w = (float)image.w * scale;
 
229
        float h = (float)image.h * scale;
 
230
        float x1 = x - w / 2;
 
231
        float x2 = x + w / 2;
 
232
        float y1 = y - h / 2;
 
233
        float y2 = y + h / 2;
 
234
        float v[6][2] = {
 
235
                {x1, y1},
 
236
                {x2, y1},
 
237
                {x2, y2},
 
238
                {x1, y1},
 
239
                {x2, y2},
 
240
                {x1, y2},
 
241
        };
 
242
        float u1 = image.u1;
 
243
        float u2 = image.u2;
 
244
        if (mirror_h) {
 
245
                float temp = u1;
 
246
                u1 = u2;
 
247
                u2 = temp;
 
248
        }
 
249
        const float uv[6][2] = {
 
250
                {u1, image.v1},
 
251
                {u2, image.v1},
 
252
                {u2, image.v2},
 
253
                {u1, image.v1},
 
254
                {u2, image.v2},
 
255
                {u1, image.v2},
 
256
        };
 
257
        for (int i = 0; i < 6; i++) {
 
258
                rot(v[i], angle, x, y);
 
259
                V(v[i][0], v[i][1], 0, color, uv[i][0], uv[i][1]);
 
260
        }
 
261
}
 
262
 
 
263
// TODO: add arc support
 
264
void DrawBuffer::Circle(float xc, float yc, float radius, float thickness, int segments, float startAngle, uint32_t color, float u_mul) {
 
265
        float angleDelta = PI * 2 / segments;
 
266
        float uDelta = 1.0f / segments;
 
267
        float t2 = thickness / 2.0f;
 
268
        float r1 = radius + t2;
 
269
        float r2 = radius - t2;
 
270
        for (int i = 0; i < segments + 1; i++) {
 
271
                float angle1 = i * angleDelta;
 
272
                float angle2 = (i + 1) * angleDelta;
 
273
                float u1 = u_mul * i * uDelta;
 
274
                float u2 = u_mul * (i + 1) * uDelta;
 
275
                // TODO: get rid of one pair of cos/sin per loop, can reuse from last iteration
 
276
                float c1 = cosf(angle1), s1 = sinf(angle1), c2 = cosf(angle2), s2 = sinf(angle2);
 
277
                const float x[4] = {c1 * r1 + xc, c2 * r1 + xc, c1 * r2 + xc, c2 * r2 + xc};
 
278
                const float y[4] = {s1 * r1 + yc, s2 * r1 + yc, s1 * r2 + yc, s2 * r2 + yc};
 
279
                V(x[0], y[0], color, u1, 0);
 
280
                V(x[1], y[1], color, u2, 0);
 
281
                V(x[2], y[2], color, u1, 1);
 
282
                V(x[1], y[1], color, u2, 0);
 
283
                V(x[3], y[3], color, u2, 1);
 
284
                V(x[2], y[2], color, u1, 1);
 
285
        }
 
286
}
 
287
 
 
288
void DrawBuffer::DrawTexRect(float x1, float y1, float x2, float y2, float u1, float v1, float u2, float v2, Color color) {
 
289
        V(x1,   y1, color, u1, v1);
 
290
        V(x2,   y1, color, u2, v1);
 
291
        V(x2,   y2, color, u2, v2);
 
292
        V(x1,   y1, color, u1, v1);
 
293
        V(x2,   y2, color, u2, v2);
 
294
        V(x1,   y2, color, u1, v2);
 
295
}
 
296
 
 
297
void DrawBuffer::DrawImage4Grid(ImageID atlas_image, float x1, float y1, float x2, float y2, Color color, float corner_scale) {
 
298
        const AtlasImage &image = atlas->images[atlas_image];
 
299
 
 
300
        float u1 = image.u1, v1 = image.v1, u2 = image.u2, v2 = image.v2;
 
301
        float um = (u2 + u1) * 0.5f;
 
302
        float vm = (v2 + v1) * 0.5f;
 
303
        float iw2 = (image.w * 0.5f) * corner_scale;
 
304
        float ih2 = (image.h * 0.5f) * corner_scale;
 
305
        float xa = x1 + iw2;
 
306
        float xb = x2 - iw2;
 
307
        float ya = y1 + ih2;
 
308
        float yb = y2 - ih2;
 
309
        // Top row
 
310
        DrawTexRect(x1, y1, xa, ya, u1, v1, um, vm, color);
 
311
        DrawTexRect(xa, y1, xb, ya, um, v1, um, vm, color);
 
312
        DrawTexRect(xb, y1, x2, ya, um, v1, u2, vm, color);
 
313
        // Middle row
 
314
        DrawTexRect(x1, ya, xa, yb, u1, vm, um, vm, color);
 
315
        DrawTexRect(xa, ya, xb, yb, um, vm, um, vm, color);
 
316
        DrawTexRect(xb, ya, x2, yb, um, vm, u2, vm, color);
 
317
        // Bottom row
 
318
        DrawTexRect(x1, yb, xa, y2, u1, vm, um, v2, color);
 
319
        DrawTexRect(xa, yb, xb, y2, um, vm, um, v2, color);
 
320
        DrawTexRect(xb, yb, x2, y2, um, vm, u2, v2, color);
 
321
}
 
322
 
 
323
void DrawBuffer::DrawImage2GridH(ImageID atlas_image, float x1, float y1, float x2, Color color, float corner_scale) {
 
324
        const AtlasImage &image = atlas->images[atlas_image];
 
325
        float um = (image.u1 + image.u2) * 0.5f;
 
326
        float iw2 = (image.w * 0.5f) * corner_scale;
 
327
        float xa = x1 + iw2;
 
328
        float xb = x2 - iw2;
 
329
        float u1 = image.u1, v1 = image.v1, u2 = image.u2, v2 = image.v2;
 
330
        float y2 = y1 + image.h;
 
331
        DrawTexRect(x1, y1, xa, y2, u1, v1, um, v2, color);
 
332
        DrawTexRect(xa, y1, xb, y2, um, v1, um, v2, color);
 
333
        DrawTexRect(xb, y1, x2, y2, um, v1, u2, v2, color);
 
334
}
 
335
 
 
336
class AtlasWordWrapper : public WordWrapper {
 
337
public:
 
338
        // Note: maxW may be height if rotated.
 
339
        AtlasWordWrapper(const AtlasFont &atlasfont, float scale, const char *str, float maxW) : WordWrapper(str, maxW), atlasfont_(atlasfont), scale_(scale) {
 
340
        }
 
341
 
 
342
protected:
 
343
        float MeasureWidth(const char *str, size_t bytes) override;
 
344
 
 
345
        const AtlasFont &atlasfont_;
 
346
        const float scale_;
 
347
};
 
348
 
 
349
float AtlasWordWrapper::MeasureWidth(const char *str, size_t bytes) {
 
350
        float w = 0.0f;
 
351
        for (UTF8 utf(str); utf.byteIndex() < (int)bytes; ) {
 
352
                uint32_t c = utf.next();
 
353
                if (c == '&') {
 
354
                        // Skip ampersand prefixes ("&&" is an ampersand.)
 
355
                        c = utf.next();
 
356
                }
 
357
                const AtlasChar *ch = atlasfont_.getChar(c);
 
358
                if (!ch)
 
359
                        ch = atlasfont_.getChar('?');
 
360
 
 
361
                w += ch->wx * scale_;
 
362
        }
 
363
        return w;
 
364
}
 
365
 
 
366
void DrawBuffer::MeasureTextCount(int font, const char *text, int count, float *w, float *h) {
 
367
        const AtlasFont &atlasfont = *atlas->fonts[font];
 
368
 
 
369
        unsigned int cval;
 
370
        float wacc = 0;
 
371
        float maxX = 0.0f;
 
372
        int lines = 1;
 
373
        UTF8 utf(text);
 
374
        while (true) {
 
375
                if (utf.end())
 
376
                        break;
 
377
                if (utf.byteIndex() >= count)
 
378
                        break;
 
379
                cval = utf.next();
 
380
                // Translate non-breaking space to space.
 
381
                if (cval == 0xA0) {
 
382
                        cval = ' ';
 
383
                }
 
384
                if (cval == '\n') {
 
385
                        maxX = std::max(maxX, wacc);
 
386
                        wacc = 0;
 
387
                        lines++;
 
388
                        continue;
 
389
                } else if (cval == '&' && utf.peek() != '&') {
 
390
                        // Ignore lone ampersands
 
391
                        continue;
 
392
                }
 
393
                const AtlasChar *c = atlasfont.getChar(cval);
 
394
                if (c) {
 
395
                        wacc += c->wx * fontscalex;
 
396
                }
 
397
        }
 
398
        if (w) *w = std::max(wacc, maxX);
 
399
        if (h) *h = atlasfont.height * fontscaley * lines;
 
400
}
 
401
 
 
402
void DrawBuffer::MeasureTextRect(int font, const char *text, int count, const Bounds &bounds, float *w, float *h, int align) {
 
403
        std::string toMeasure = std::string(text, count);
 
404
        if (align & FLAG_WRAP_TEXT) {
 
405
                AtlasWordWrapper wrapper(*atlas->fonts[font], fontscalex, toMeasure.c_str(), bounds.w);
 
406
                toMeasure = wrapper.Wrapped();
 
407
        }
 
408
 
 
409
        MeasureTextCount(font, toMeasure.c_str(), (int)toMeasure.length(), w, h);
 
410
}
 
411
 
 
412
void DrawBuffer::MeasureText(int font, const char *text, float *w, float *h) {
 
413
        return MeasureTextCount(font, text, (int)strlen(text), w, h);
 
414
}
 
415
 
 
416
void DrawBuffer::DrawTextShadow(int font, const char *text, float x, float y, Color color, int flags) {
 
417
        uint32_t alpha = (color >> 1) & 0xFF000000;
 
418
        DrawText(font, text, x + 2, y + 2, alpha, flags);
 
419
        DrawText(font, text, x, y, color, flags);
 
420
}
 
421
 
 
422
void DrawBuffer::DoAlign(int flags, float *x, float *y, float *w, float *h) {
 
423
        if (flags & ALIGN_HCENTER) *x -= *w / 2;
 
424
        if (flags & ALIGN_RIGHT) *x -= *w;
 
425
        if (flags & ALIGN_VCENTER) *y -= *h / 2;
 
426
        if (flags & ALIGN_BOTTOM) *y -= *h;
 
427
        if (flags & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) {
 
428
                std::swap(*w, *h);
 
429
                std::swap(*x, *y);
 
430
        }
 
431
}
 
432
 
 
433
 
 
434
// TODO: Actually use the rect properly, take bounds.
 
435
void DrawBuffer::DrawTextRect(int font, const char *text, float x, float y, float w, float h, Color color, int align) {
 
436
        if (align & ALIGN_HCENTER) {
 
437
                x += w / 2;
 
438
        } else if (align & ALIGN_RIGHT) {
 
439
                x += w;
 
440
        }
 
441
        if (align & ALIGN_VCENTER) {
 
442
                y += h / 2;
 
443
        } else if (align & ALIGN_BOTTOM) {
 
444
                y += h;
 
445
        }
 
446
 
 
447
        std::string toDraw = text;
 
448
        if (align & FLAG_WRAP_TEXT) {
 
449
                AtlasWordWrapper wrapper(*atlas->fonts[font], fontscalex, toDraw.c_str(), w);
 
450
                toDraw = wrapper.Wrapped();
 
451
        }
 
452
 
 
453
        float totalWidth, totalHeight;
 
454
        MeasureTextRect(font, toDraw.c_str(), (int)toDraw.size(), Bounds(x, y, w, h), &totalWidth, &totalHeight, align);
 
455
 
 
456
        std::vector<std::string> lines;
 
457
        SplitString(toDraw, '\n', lines);
 
458
 
 
459
        float baseY = y;
 
460
        if (align & ALIGN_VCENTER) {
 
461
                baseY -= totalHeight / 2;
 
462
                align = align & ~ALIGN_VCENTER;
 
463
        } else if (align & ALIGN_BOTTOM) {
 
464
                baseY -= totalHeight;
 
465
                align = align & ~ALIGN_BOTTOM;
 
466
        }
 
467
 
 
468
        // This allows each line to be horizontally centered by itself.
 
469
        for (const std::string &line : lines) {
 
470
                DrawText(font, line.c_str(), x, baseY, color, align);
 
471
 
 
472
                float tw, th;
 
473
                MeasureText(font, line.c_str(), &tw, &th);
 
474
                baseY += th;
 
475
        }
 
476
}
 
477
 
 
478
// ROTATE_* doesn't yet work right.
 
479
void DrawBuffer::DrawText(int font, const char *text, float x, float y, Color color, int align) {
 
480
        // rough estimate
 
481
        if (count_ + strlen(text) * 6 > MAX_VERTS) {
 
482
                Flush(true);
 
483
        }
 
484
 
 
485
        const AtlasFont &atlasfont = *atlas->fonts[font];
 
486
        unsigned int cval;
 
487
        float w, h;
 
488
        MeasureText(font, text, &w, &h);
 
489
        if (align) {
 
490
                DoAlign(align, &x, &y, &w, &h);
 
491
        }
 
492
 
 
493
        if (align & ROTATE_90DEG_LEFT) {
 
494
                x -= atlasfont.ascend*fontscaley;
 
495
                // y += h;
 
496
        }
 
497
        else
 
498
                y += atlasfont.ascend*fontscaley;
 
499
        float sx = x;
 
500
        UTF8 utf(text);
 
501
        while (true) {
 
502
                if (utf.end())
 
503
                        break;
 
504
                cval = utf.next();
 
505
                // Translate non-breaking space to space.
 
506
                if (cval == 0xA0) {
 
507
                        cval = ' ';
 
508
                }
 
509
                if (cval == '\n') {
 
510
                        y += atlasfont.height * fontscaley;
 
511
                        x = sx;
 
512
                        continue;
 
513
                } else if (cval == '&' && utf.peek() != '&') {
 
514
                        // Ignore lone ampersands
 
515
                        continue;
 
516
                }
 
517
                const AtlasChar *ch = atlasfont.getChar(cval);
 
518
                if (!ch)
 
519
                        ch = atlasfont.getChar('?');
 
520
                if (ch) {
 
521
                        const AtlasChar &c = *ch;
 
522
                        float cx1, cy1, cx2, cy2;
 
523
                        if (align & ROTATE_90DEG_LEFT) {
 
524
                                cy1 = y - c.ox * fontscalex;
 
525
                                cx1 = x + c.oy * fontscaley;
 
526
                                cy2 = y - (c.ox + c.pw) * fontscalex;
 
527
                                cx2 = x + (c.oy + c.ph) * fontscaley;
 
528
                        } else {
 
529
                                cx1 = x + c.ox * fontscalex;
 
530
                                cy1 = y + c.oy * fontscaley;
 
531
                                cx2 = x + (c.ox + c.pw) * fontscalex;
 
532
                                cy2 = y + (c.oy + c.ph) * fontscaley;
 
533
                        }
 
534
                        V(cx1,  cy1, color, c.sx, c.sy);
 
535
                        V(cx2,  cy1, color, c.ex, c.sy);
 
536
                        V(cx2,  cy2, color, c.ex, c.ey);
 
537
                        V(cx1,  cy1, color, c.sx, c.sy);
 
538
                        V(cx2,  cy2, color, c.ex, c.ey);
 
539
                        V(cx1,  cy2, color, c.sx, c.ey);
 
540
                        if (align & ROTATE_90DEG_LEFT)
 
541
                                y -= c.wx * fontscalex;
 
542
                        else
 
543
                                x += c.wx * fontscalex;
 
544
                }
 
545
        }
 
546
}