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"
24
DrawBuffer::DrawBuffer() : count_(0), atlas(0) {
25
verts_ = new Vertex[MAX_VERTS];
31
DrawBuffer::~DrawBuffer() {
35
void DrawBuffer::Init(Thin3DContext *t3d) {
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));
47
Thin3DShader *vshader = t3d_->GetVshaderPreset(VS_TEXTURE_COLOR_2D);
49
vformat_ = t3d_->CreateVertexFormat(components, 24, vshader);
50
if (vformat_->RequiresBuffer()) {
51
vbuf_ = t3d_->CreateBuffer(MAX_VERTS * sizeof(Vertex), T3DBufferUsage::DYNAMIC | T3DBufferUsage::VERTEXDATA);
57
void DrawBuffer::Shutdown() {
66
void DrawBuffer::Begin(Thin3DShaderSet *program, DrawBufferPrimitiveMode dbmode) {
72
void DrawBuffer::End() {
73
// Currently does nothing, but call it!
76
void DrawBuffer::Flush(bool set_blend_state) {
78
ELOG("No program set!");
85
shaderSet_->SetMatrix4x4("WorldViewProj", drawMatrix_.getReadPtr());
88
vbuf_->SubData((const uint8_t *)verts_, 0, sizeof(Vertex) * count_);
90
t3d_->Draw(mode_ == DBMODE_NORMAL ? PRIM_TRIANGLES : PRIM_LINES, shaderSet_, vformat_, vbuf_, count_, offset);
92
t3d_->DrawUP(mode_ == DBMODE_NORMAL ? PRIM_TRIANGLES : PRIM_LINES, shaderSet_, vformat_, (const void *)verts_, count_);
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");
103
Vertex *vert = &verts_[count_++];
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);
117
void DrawBuffer::hLine(float x1, float y, float x2, uint32_t color) {
118
Rect(x1, y, x2 - x1, pixel_in_dps, color);
121
void DrawBuffer::vLine(float x, float y1, float y2, uint32_t color) {
122
Rect(x, y1, pixel_in_dps, y2 - y1, color);
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);
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);
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);
142
vLine(x, y, y + h + pixel_in_dps, color);
143
vLine(x + w, y, y + h + pixel_in_dps, color);
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);
154
void DrawBuffer::Rect(float x, float y, float w, float h,
155
float u, float v, float uw, float uh,
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);
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];
169
// Pre-rotated - we are making a thick line here
170
float dx = -(y2 - y1);
172
float len = sqrtf(dx * dx + dy * dy) / thickness;
179
float x[4] = { x1 - dx, x2 - dx, x1 + dx, x2 + dx };
180
float y[4] = { y1 - dy, y2 - dy, y1 + dy, y2 + dy };
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);
190
void DrawBuffer::MeasureImage(ImageID atlas_image, float *w, float *h) {
191
const AtlasImage &image = atlas->images[atlas_image];
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);
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);
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;
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;
249
const float uv[6][2] = {
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]);
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);
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);
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];
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;
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);
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);
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);
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;
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);
336
class AtlasWordWrapper : public WordWrapper {
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) {
343
float MeasureWidth(const char *str, size_t bytes) override;
345
const AtlasFont &atlasfont_;
349
float AtlasWordWrapper::MeasureWidth(const char *str, size_t bytes) {
351
for (UTF8 utf(str); utf.byteIndex() < (int)bytes; ) {
352
uint32_t c = utf.next();
354
// Skip ampersand prefixes ("&&" is an ampersand.)
357
const AtlasChar *ch = atlasfont_.getChar(c);
359
ch = atlasfont_.getChar('?');
361
w += ch->wx * scale_;
366
void DrawBuffer::MeasureTextCount(int font, const char *text, int count, float *w, float *h) {
367
const AtlasFont &atlasfont = *atlas->fonts[font];
377
if (utf.byteIndex() >= count)
380
// Translate non-breaking space to space.
385
maxX = std::max(maxX, wacc);
389
} else if (cval == '&' && utf.peek() != '&') {
390
// Ignore lone ampersands
393
const AtlasChar *c = atlasfont.getChar(cval);
395
wacc += c->wx * fontscalex;
398
if (w) *w = std::max(wacc, maxX);
399
if (h) *h = atlasfont.height * fontscaley * lines;
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();
409
MeasureTextCount(font, toMeasure.c_str(), (int)toMeasure.length(), w, h);
412
void DrawBuffer::MeasureText(int font, const char *text, float *w, float *h) {
413
return MeasureTextCount(font, text, (int)strlen(text), w, h);
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);
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)) {
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) {
438
} else if (align & ALIGN_RIGHT) {
441
if (align & ALIGN_VCENTER) {
443
} else if (align & ALIGN_BOTTOM) {
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();
453
float totalWidth, totalHeight;
454
MeasureTextRect(font, toDraw.c_str(), (int)toDraw.size(), Bounds(x, y, w, h), &totalWidth, &totalHeight, align);
456
std::vector<std::string> lines;
457
SplitString(toDraw, '\n', lines);
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;
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);
473
MeasureText(font, line.c_str(), &tw, &th);
478
// ROTATE_* doesn't yet work right.
479
void DrawBuffer::DrawText(int font, const char *text, float x, float y, Color color, int align) {
481
if (count_ + strlen(text) * 6 > MAX_VERTS) {
485
const AtlasFont &atlasfont = *atlas->fonts[font];
488
MeasureText(font, text, &w, &h);
490
DoAlign(align, &x, &y, &w, &h);
493
if (align & ROTATE_90DEG_LEFT) {
494
x -= atlasfont.ascend*fontscaley;
498
y += atlasfont.ascend*fontscaley;
505
// Translate non-breaking space to space.
510
y += atlasfont.height * fontscaley;
513
} else if (cval == '&' && utf.peek() != '&') {
514
// Ignore lone ampersands
517
const AtlasChar *ch = atlasfont.getChar(cval);
519
ch = atlasfont.getChar('?');
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;
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;
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;
543
x += c.wx * fontscalex;