- nvgTextBounds() API changed, assumes x,y as input too - fixed nvgTextBounds() return values - added nvgTextBoxBounds() - text line width is handle more consistently - added note about return values of text measure funds - changed demo to show text(box)bounds usageshared-context
@@ -223,7 +223,7 @@ void drawEditBoxNum(struct NVGcontext* vg, | |||
drawEditBoxBase(vg, x,y, w,h); | |||
uw = nvgTextBounds(vg, units, NULL, NULL); | |||
uw = nvgTextBounds(vg, 0,0, units, NULL, NULL); | |||
nvgFontSize(vg, 18.0f); | |||
nvgFontFace(vg, "sans"); | |||
@@ -288,11 +288,11 @@ void drawButton(struct NVGcontext* vg, int preicon, const char* text, float x, f | |||
nvgFontSize(vg, 20.0f); | |||
nvgFontFace(vg, "sans-bold"); | |||
tw = nvgTextBounds(vg, text, NULL, NULL); | |||
tw = nvgTextBounds(vg, 0,0, text, NULL, NULL); | |||
if (preicon != 0) { | |||
nvgFontSize(vg, h*1.3f); | |||
nvgFontFace(vg, "icons"); | |||
iw = nvgTextBounds(vg, cpToUTF8(preicon,icon), NULL, NULL); | |||
iw = nvgTextBounds(vg, 0,0, cpToUTF8(preicon,icon), NULL, NULL); | |||
iw += h*0.15f; | |||
} | |||
@@ -815,9 +815,12 @@ void drawParagraph(struct NVGcontext* vg, float x, float y, float width, float h | |||
const char* text = "This is longer chunk of text.\n \n Would have used lorem ipsum but she was busy jumping over the lazy dog with the fox and all the men who came to the aid of the party."; | |||
const char* start; | |||
const char* end; | |||
int nrows, i, nglyphs, j; | |||
int nrows, i, nglyphs, j, lnum = 0; | |||
float lineh; | |||
float caretx, px; | |||
float bounds[4]; | |||
float gx,gy; | |||
int gutter = 0; | |||
NVG_NOTUSED(height); | |||
nvgSave(vg); | |||
@@ -848,7 +851,7 @@ void drawParagraph(struct NVGcontext* vg, float x, float y, float width, float h | |||
if (hit) { | |||
caretx = (mx < x+row->width/2) ? x : x+row->width; | |||
px = x; | |||
nglyphs = nvgTextGlyphPositions(vg, row->start, row->end, x, y, glyphs, 100); | |||
nglyphs = nvgTextGlyphPositions(vg, x, y, row->start, row->end, glyphs, 100); | |||
for (j = 0; j < nglyphs; j++) { | |||
float x0 = glyphs[j].x; | |||
float x1 = (j+1 < nglyphs) ? glyphs[j+1].x : x+row->width; | |||
@@ -861,20 +864,52 @@ void drawParagraph(struct NVGcontext* vg, float x, float y, float width, float h | |||
nvgFillColor(vg, nvgRGBA(255,192,0,255)); | |||
nvgRect(vg, caretx, y, 1, lineh); | |||
nvgFill(vg); | |||
gutter = lnum+1; | |||
gx = x - 10; | |||
gy = y + lineh/2; | |||
} | |||
lnum++; | |||
y += lineh; | |||
} | |||
// Keep going... | |||
start = rows[nrows-1].next; | |||
} | |||
y += 10.0f; | |||
if (gutter) { | |||
char txt[16]; | |||
snprintf(txt, sizeof(txt), "%d", gutter); | |||
nvgFontSize(vg, 13.0f); | |||
nvgTextAlign(vg, NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE); | |||
nvgFillColor(vg, nvgRGBA(0,0,0,220)); | |||
nvgFontSize(vg, 12.0f); | |||
nvgTextBounds(vg, gx,gy, txt, NULL, bounds); | |||
nvgBeginPath(vg); | |||
nvgFillColor(vg, nvgRGBA(255,192,0,255)); | |||
nvgRoundedRect(vg, (int)bounds[0]-4,(int)bounds[1]-2, (int)(bounds[2]-bounds[0])+8, (int)(bounds[3]-bounds[1])+4, ((int)(bounds[3]-bounds[1])+4)/2-1); | |||
nvgFill(vg); | |||
nvgFillColor(vg, nvgRGBA(32,32,32,255)); | |||
nvgText(vg, gx,gy, txt, NULL); | |||
} | |||
y += 20.0f; | |||
nvgFontSize(vg, 13.0f); | |||
nvgTextAlign(vg, NVG_ALIGN_LEFT|NVG_ALIGN_TOP); | |||
nvgTextLineHeight(vg, 1.2f); | |||
nvgTextBoxBounds(vg, x,y, 150, "Hover your mouse over the text to see calculated caret position.", NULL, bounds); | |||
nvgBeginPath(vg); | |||
nvgFillColor(vg, nvgRGBA(220,220,220,255)); | |||
nvgRoundedRect(vg, bounds[0]-2,bounds[1]-2, (int)(bounds[2]-bounds[0])+4, (int)(bounds[3]-bounds[1])+4, 3); | |||
px = (int)((bounds[2]+bounds[0])/2); | |||
nvgMoveTo(vg, px,bounds[1] - 10); | |||
nvgLineTo(vg, px+7,bounds[1]+1); | |||
nvgLineTo(vg, px-7,bounds[1]+1); | |||
nvgFill(vg); | |||
nvgFillColor(vg, nvgRGBA(0,0,0,220)); | |||
nvgTextBox(vg, x,y, 150, "Hover your mouse over the text to see calculated caret position.", NULL); | |||
nvgRestore(vg); | |||
@@ -67,7 +67,7 @@ struct FONSquad | |||
}; | |||
struct FONStextIter { | |||
float x, y, scale, spacing; | |||
float x, y, nextx, nexty, scale, spacing; | |||
unsigned int codepoint; | |||
short isize, iblur; | |||
struct FONSfont* font; | |||
@@ -112,7 +112,8 @@ void fonsSetFont(struct FONScontext* s, int font); | |||
float fonsDrawText(struct FONScontext* s, float x, float y, const char* string, const char* end); | |||
// Measure text | |||
float fonsTextBounds(struct FONScontext* s, const char* string, const char* end, float* bounds); | |||
float fonsTextBounds(struct FONScontext* s, float x, float y, const char* string, const char* end, float* bounds); | |||
void fonsLineBounds(struct FONScontext* s, float y, float* miny, float* maxy); | |||
void fonsVertMetrics(struct FONScontext* s, float* ascender, float* descender, float* lineh); | |||
// Text iterator | |||
@@ -1223,10 +1224,10 @@ float fonsDrawText(struct FONScontext* stash, | |||
if (state->align & FONS_ALIGN_LEFT) { | |||
// empty | |||
} else if (state->align & FONS_ALIGN_RIGHT) { | |||
width = fonsTextBounds(stash, str, end, NULL); | |||
width = fonsTextBounds(stash, x,y, str, end, NULL); | |||
x -= width; | |||
} else if (state->align & FONS_ALIGN_CENTER) { | |||
width = fonsTextBounds(stash, str, end, NULL); | |||
width = fonsTextBounds(stash, x,y, str, end, NULL); | |||
x -= width * 0.5f; | |||
} | |||
// Align vertically. | |||
@@ -1278,10 +1279,10 @@ int fonsTextIterInit(struct FONScontext* stash, struct FONStextIter* iter, | |||
if (state->align & FONS_ALIGN_LEFT) { | |||
// empty | |||
} else if (state->align & FONS_ALIGN_RIGHT) { | |||
width = fonsTextBounds(stash, str, end, NULL); | |||
width = fonsTextBounds(stash, x,y, str, end, NULL); | |||
x -= width; | |||
} else if (state->align & FONS_ALIGN_CENTER) { | |||
width = fonsTextBounds(stash, str, end, NULL); | |||
width = fonsTextBounds(stash, x,y, str, end, NULL); | |||
x -= width * 0.5f; | |||
} | |||
// Align vertically. | |||
@@ -1290,8 +1291,8 @@ int fonsTextIterInit(struct FONScontext* stash, struct FONStextIter* iter, | |||
if (end == NULL) | |||
end = str + strlen(str); | |||
iter->x = x; | |||
iter->y = y; | |||
iter->x = iter->nextx = x; | |||
iter->y = iter->nexty = y; | |||
iter->spacing = state->spacing; | |||
iter->str = str; | |||
iter->next = str; | |||
@@ -1315,9 +1316,11 @@ int fonsTextIterNext(struct FONScontext* stash, struct FONStextIter* iter, struc | |||
continue; | |||
str++; | |||
// Get glyph and quad | |||
iter->x = iter->nextx; | |||
iter->y = iter->nexty; | |||
glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur); | |||
if (glyph != NULL) | |||
fons__getQuad(stash, iter->font, iter->prevGlyph, glyph, iter->scale, iter->spacing, &iter->x, &iter->y, quad); | |||
fons__getQuad(stash, iter->font, iter->prevGlyph, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); | |||
iter->prevGlyph = glyph; | |||
break; | |||
} | |||
@@ -1375,6 +1378,7 @@ void fonsDrawDebug(struct FONScontext* stash, float x, float y) | |||
} | |||
float fonsTextBounds(struct FONScontext* stash, | |||
float x, float y, | |||
const char* str, const char* end, | |||
float* bounds) | |||
{ | |||
@@ -1388,7 +1392,8 @@ float fonsTextBounds(struct FONScontext* stash, | |||
short iblur = (short)state->blur; | |||
float scale; | |||
struct FONSfont* font; | |||
float x = 0, y = 0, minx, miny, maxx, maxy; | |||
float startx, advance; | |||
float minx, miny, maxx, maxy; | |||
if (stash == NULL) return 0; | |||
if (state->font < 0 || state->font >= stash->nfonts) return 0; | |||
@@ -1402,6 +1407,7 @@ float fonsTextBounds(struct FONScontext* stash, | |||
minx = maxx = x; | |||
miny = maxy = y; | |||
startx = x; | |||
if (end == NULL) | |||
end = str + strlen(str); | |||
@@ -1414,21 +1420,28 @@ float fonsTextBounds(struct FONScontext* stash, | |||
fons__getQuad(stash, font, prevGlyph, glyph, state->spacing, scale, &x, &y, &q); | |||
if (q.x0 < minx) minx = q.x0; | |||
if (q.x1 > maxx) maxx = q.x1; | |||
if (q.y1 < miny) miny = q.y1; | |||
if (q.y0 > maxy) maxy = q.y0; | |||
if (stash->params.flags & FONS_ZERO_TOPLEFT) { | |||
if (q.y0 < miny) miny = q.y0; | |||
if (q.y1 > maxy) maxy = q.y1; | |||
} else { | |||
if (q.y1 < miny) miny = q.y1; | |||
if (q.y0 > maxy) maxy = q.y0; | |||
} | |||
} | |||
prevGlyph = glyph; | |||
} | |||
advance = x - startx; | |||
// Align horizontally | |||
if (state->align & FONS_ALIGN_LEFT) { | |||
// empty | |||
} else if (state->align & FONS_ALIGN_RIGHT) { | |||
minx -= x; | |||
maxx -= x; | |||
minx -= advance; | |||
maxx -= advance; | |||
} else if (state->align & FONS_ALIGN_CENTER) { | |||
minx -= x * 0.5f; | |||
maxx -= x * 0.5f; | |||
minx -= advance * 0.5f; | |||
maxx -= advance * 0.5f; | |||
} | |||
if (bounds) { | |||
@@ -1438,7 +1451,7 @@ float fonsTextBounds(struct FONScontext* stash, | |||
bounds[3] = maxy; | |||
} | |||
return x; | |||
return advance; | |||
} | |||
void fonsVertMetrics(struct FONScontext* stash, | |||
@@ -1462,6 +1475,29 @@ void fonsVertMetrics(struct FONScontext* stash, | |||
*lineh = font->lineh*isize/10.0f; | |||
} | |||
void fonsLineBounds(struct FONScontext* stash, float y, float* miny, float* maxy) | |||
{ | |||
struct FONSfont* font; | |||
struct FONSstate* state = fons__getState(stash); | |||
short isize; | |||
if (stash == NULL) return; | |||
if (state->font < 0 || state->font >= stash->nfonts) return; | |||
font = stash->fonts[state->font]; | |||
isize = (short)(state->size*10.0f); | |||
if (!font->data) return; | |||
y += fons__getVertAlign(stash, font, state->align, isize); | |||
if (stash->params.flags & FONS_ZERO_TOPLEFT) { | |||
*miny = y - font->ascender * (float)isize/10.0f; | |||
*maxy = *miny + font->lineh*isize/10.0f; | |||
} else { | |||
*maxy = y + font->descender * (float)isize/10.0f; | |||
*miny = *maxy - font->lineh*isize/10.0f; | |||
} | |||
} | |||
const unsigned char* fonsGetTextureData(struct FONScontext* stash, int* width, int* height) | |||
{ | |||
if (width != NULL) | |||
@@ -2002,7 +2002,7 @@ float nvgText(struct NVGcontext* ctx, float x, float y, const char* string, cons | |||
return iter.x; | |||
} | |||
void nvgTextBox(struct NVGcontext* ctx, float x, float y, float width, const char* string, const char* end) | |||
void nvgTextBox(struct NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end) | |||
{ | |||
struct NVGstate* state = nvg__getState(ctx); | |||
struct NVGtextRow rows[2]; | |||
@@ -2018,15 +2018,15 @@ void nvgTextBox(struct NVGcontext* ctx, float x, float y, float width, const cha | |||
state->textAlign = NVG_ALIGN_LEFT | valign; | |||
while ((nrows = nvgTextBreakLines(ctx, string, end, width, rows, 2))) { | |||
while ((nrows = nvgTextBreakLines(ctx, string, end, breakRowWidth, rows, 2))) { | |||
for (i = 0; i < nrows; i++) { | |||
struct NVGtextRow* row = &rows[i]; | |||
if (haling & NVG_ALIGN_LEFT) | |||
nvgText(ctx, x, y, row->start, row->end); | |||
else if (haling & NVG_ALIGN_CENTER) | |||
nvgText(ctx, x + width*0.5f - row->width*0.5f, y, row->start, row->end); | |||
nvgText(ctx, x + breakRowWidth*0.5f - row->width*0.5f, y, row->start, row->end); | |||
else if (haling & NVG_ALIGN_RIGHT) | |||
nvgText(ctx, x + width - row->width, y, row->start, row->end); | |||
nvgText(ctx, x + breakRowWidth - row->width, y, row->start, row->end); | |||
y += lineh * state->lineHeight; | |||
} | |||
string = rows[nrows-1].next; | |||
@@ -2035,7 +2035,7 @@ void nvgTextBox(struct NVGcontext* ctx, float x, float y, float width, const cha | |||
state->textAlign = oldAlign; | |||
} | |||
int nvgTextGlyphPositions(struct NVGcontext* ctx, const char* string, const char* end, float x, float y, struct NVGglyphPosition* positions, int maxPositions) | |||
int nvgTextGlyphPositions(struct NVGcontext* ctx, float x, float y, const char* string, const char* end, struct NVGglyphPosition* positions, int maxPositions) | |||
{ | |||
struct NVGstate* state = nvg__getState(ctx); | |||
float scale = nvg__getFontScale(state) * ctx->devicePxRatio; | |||
@@ -2079,7 +2079,7 @@ enum NVGcodepointType { | |||
NVG_CHAR, | |||
}; | |||
int nvgTextBreakLines(struct NVGcontext* ctx, const char* string, const char* end, float maxRowWidth, struct NVGtextRow* rows, int maxRows) | |||
int nvgTextBreakLines(struct NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, struct NVGtextRow* rows, int maxRows) | |||
{ | |||
struct NVGstate* state = nvg__getState(ctx); | |||
float scale = nvg__getFontScale(state) * ctx->devicePxRatio; | |||
@@ -2089,12 +2089,16 @@ int nvgTextBreakLines(struct NVGcontext* ctx, const char* string, const char* en | |||
int nrows = 0; | |||
float rowStartX = 0; | |||
float rowWidth = 0; | |||
float rowMinX = 0; | |||
float rowMaxX = 0; | |||
const char* rowStart = NULL; | |||
const char* rowEnd = NULL; | |||
const char* wordStart = NULL; | |||
float wordStartX = 0; | |||
float wordMinX = 0; | |||
const char* breakEnd = NULL; | |||
float breakWidth = 0; | |||
float breakMaxX = 0; | |||
int type = NVG_SPACE, ptype = NVG_SPACE; | |||
unsigned int pcodepoint = 0; | |||
@@ -2112,7 +2116,7 @@ int nvgTextBreakLines(struct NVGcontext* ctx, const char* string, const char* en | |||
fonsSetAlign(ctx->fs, state->textAlign); | |||
fonsSetFont(ctx->fs, state->fontId); | |||
maxRowWidth *= scale; | |||
breakRowWidth *= scale; | |||
fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end); | |||
while (fonsTextIterNext(ctx->fs, &iter, &q)) { | |||
@@ -2143,54 +2147,72 @@ int nvgTextBreakLines(struct NVGcontext* ctx, const char* string, const char* en | |||
rows[nrows].start = rowStart != NULL ? rowStart : iter.str; | |||
rows[nrows].end = rowEnd != NULL ? rowEnd : iter.str; | |||
rows[nrows].width = rowWidth * invscale; | |||
rows[nrows].minx = rowMinX * invscale; | |||
rows[nrows].maxx = rowMaxX * invscale; | |||
rows[nrows].next = iter.next; | |||
nrows++; | |||
if (nrows >= maxRows) | |||
return nrows; | |||
// Set null break point | |||
breakEnd = rowStart; | |||
breakWidth = 0.0; | |||
breakMaxX = 0.0; | |||
// Indicate to skip the white space at the beginning of the row. | |||
rowStart = NULL; | |||
rowEnd = NULL; | |||
rowWidth = 0; | |||
rowMinX = rowMaxX = 0; | |||
} else { | |||
if (rowStart == NULL) { | |||
// Skip white space until the beginning of the line | |||
if (type == NVG_CHAR) { | |||
// The current char is the row so far | |||
rowStartX = q.x0; | |||
rowStartX = iter.x; | |||
rowStart = iter.str; | |||
rowEnd = iter.next; | |||
rowWidth = q.x1 - rowStartX; | |||
rowWidth = iter.nextx - rowStartX; // q.x1 - rowStartX; | |||
rowMinX = q.x0 - rowStartX; | |||
rowMaxX = q.x1 - rowStartX; | |||
wordStart = iter.str; | |||
wordStartX = q.x0; | |||
wordStartX = iter.x; | |||
wordMinX = q.x0 - rowStartX; | |||
// Set null break point | |||
breakEnd = rowStart; | |||
breakWidth = 0.0; | |||
breakMaxX = 0.0; | |||
} | |||
} else { | |||
float nextWidth = q.x1 - rowStartX; | |||
float nextWidth = iter.nextx - rowStartX; //q.x1 - rowStartX; | |||
if (nextWidth > maxRowWidth) { | |||
if (nextWidth > breakRowWidth) { | |||
// The run length is too long, need to break to new line. | |||
if (breakEnd == rowStart) { | |||
// The current word is longer than the row length, just break it from here. | |||
rows[nrows].start = rowStart; | |||
rows[nrows].end = iter.str; | |||
rows[nrows].width = rowWidth * invscale; | |||
rows[nrows].minx = rowMinX * invscale; | |||
rows[nrows].maxx = rowMaxX * invscale; | |||
rows[nrows].next = iter.str; | |||
nrows++; | |||
if (nrows >= maxRows) | |||
return nrows; | |||
rowStartX = q.x0; | |||
rowStartX = iter.x; | |||
rowStart = iter.str; | |||
rowEnd = iter.next; | |||
rowWidth = q.x1 - rowStartX; | |||
rowWidth = iter.nextx - rowStartX; | |||
rowMinX = q.x0 - rowStartX; | |||
rowMaxX = q.x1 - rowStartX; | |||
wordStart = iter.str; | |||
wordStartX = q.x0; | |||
wordStartX = iter.x; | |||
wordMinX = q.x0 - rowStartX; | |||
} else { | |||
// Break the line from the end of the last word, and start new line from the begining of the new. | |||
rows[nrows].start = rowStart; | |||
rows[nrows].end = breakEnd; | |||
rows[nrows].width = breakWidth * invscale; | |||
rows[nrows].minx = rowMinX * invscale; | |||
rows[nrows].maxx = breakMaxX * invscale; | |||
rows[nrows].next = wordStart; | |||
nrows++; | |||
if (nrows >= maxRows) | |||
@@ -2198,28 +2220,34 @@ int nvgTextBreakLines(struct NVGcontext* ctx, const char* string, const char* en | |||
rowStartX = wordStartX; | |||
rowStart = wordStart; | |||
rowEnd = iter.next; | |||
rowWidth = q.x1 - rowStartX; | |||
rowWidth = iter.nextx - rowStartX; // q.x1 - rowStartX; | |||
rowMinX = wordMinX; | |||
rowMaxX = q.x1 - rowStartX; | |||
// No change to the word start | |||
} | |||
// Set null break point | |||
breakEnd = rowStart; | |||
breakWidth = 0.0; | |||
breakMaxX = 0.0; | |||
} | |||
// track last non-white space character | |||
if (type == NVG_CHAR) { | |||
rowEnd = iter.next; | |||
rowWidth = q.x1 - rowStartX; | |||
rowWidth = iter.nextx - rowStartX; // q.x1 - rowStartX; | |||
rowMaxX = q.x1 - rowStartX; | |||
} | |||
// track last end of a word | |||
if (ptype == NVG_CHAR && (type == NVG_SPACE || type == NVG_SPACE)) { | |||
breakEnd = iter.str; | |||
breakWidth = rowWidth; | |||
breakMaxX = rowMaxX; | |||
} | |||
// track last beginning of a word | |||
if ((ptype == NVG_SPACE || ptype == NVG_SPACE) && type == NVG_CHAR) { | |||
wordStart = iter.str; | |||
wordStartX = q.x0; | |||
wordStartX = iter.x; | |||
wordMinX = q.x0 - rowStartX; | |||
} | |||
} | |||
} | |||
@@ -2233,6 +2261,8 @@ int nvgTextBreakLines(struct NVGcontext* ctx, const char* string, const char* en | |||
rows[nrows].start = rowStart; | |||
rows[nrows].end = rowEnd; | |||
rows[nrows].width = rowWidth * invscale; | |||
rows[nrows].minx = rowMinX * invscale; | |||
rows[nrows].maxx = rowMaxX * invscale; | |||
rows[nrows].next = end; | |||
nrows++; | |||
} | |||
@@ -2240,7 +2270,7 @@ int nvgTextBreakLines(struct NVGcontext* ctx, const char* string, const char* en | |||
return nrows; | |||
} | |||
float nvgTextBounds(struct NVGcontext* ctx, const char* string, const char* end, float* bounds) | |||
float nvgTextBounds(struct NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds) | |||
{ | |||
struct NVGstate* state = nvg__getState(ctx); | |||
float scale = nvg__getFontScale(state) * ctx->devicePxRatio; | |||
@@ -2255,7 +2285,7 @@ float nvgTextBounds(struct NVGcontext* ctx, const char* string, const char* end, | |||
fonsSetAlign(ctx->fs, state->textAlign); | |||
fonsSetFont(ctx->fs, state->fontId); | |||
width = fonsTextBounds(ctx->fs, string, end, bounds); | |||
width = fonsTextBounds(ctx->fs, x, y, string, end, bounds); | |||
if (bounds != NULL) { | |||
bounds[0] *= invscale; | |||
bounds[1] *= invscale; | |||
@@ -2265,6 +2295,77 @@ float nvgTextBounds(struct NVGcontext* ctx, const char* string, const char* end, | |||
return width * invscale; | |||
} | |||
void nvgTextBoxBounds(struct NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds) | |||
{ | |||
struct NVGstate* state = nvg__getState(ctx); | |||
struct NVGtextRow rows[2]; | |||
float scale = nvg__getFontScale(state) * ctx->devicePxRatio; | |||
float invscale = 1.0f / scale; | |||
int nrows = 0, i; | |||
int oldAlign = state->textAlign; | |||
int haling = state->textAlign & (NVG_ALIGN_LEFT | NVG_ALIGN_CENTER | NVG_ALIGN_RIGHT); | |||
int valign = state->textAlign & (NVG_ALIGN_TOP | NVG_ALIGN_MIDDLE | NVG_ALIGN_BOTTOM | NVG_ALIGN_BASELINE); | |||
float lineh = 0, rminy = 0, rmaxy = 0; | |||
float minx, miny, maxx, maxy; | |||
if (state->fontId == FONS_INVALID) { | |||
if (bounds != NULL) | |||
bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0f; | |||
return; | |||
} | |||
nvgTextMetrics(ctx, NULL, NULL, &lineh); | |||
nvgTextMetrics(ctx, NULL, NULL, &lineh); | |||
state->textAlign = NVG_ALIGN_LEFT | valign; | |||
minx = maxx = x; | |||
miny = maxy = y; | |||
fonsSetSize(ctx->fs, state->fontSize*scale); | |||
fonsSetSpacing(ctx->fs, state->letterSpacing*scale); | |||
fonsSetBlur(ctx->fs, state->fontBlur*scale); | |||
fonsSetAlign(ctx->fs, state->textAlign); | |||
fonsSetFont(ctx->fs, state->fontId); | |||
fonsLineBounds(ctx->fs, 0, &rminy, &rmaxy); | |||
rminy *= invscale; | |||
rmaxy *= invscale; | |||
while ((nrows = nvgTextBreakLines(ctx, string, end, breakRowWidth, rows, 2))) { | |||
for (i = 0; i < nrows; i++) { | |||
struct NVGtextRow* row = &rows[i]; | |||
float rminx, rmaxx, dx = 0; | |||
// Horizontal bounds | |||
if (haling & NVG_ALIGN_LEFT) | |||
dx = 0; | |||
else if (haling & NVG_ALIGN_CENTER) | |||
dx = breakRowWidth*0.5f - row->width*0.5f; | |||
else if (haling & NVG_ALIGN_RIGHT) | |||
dx = breakRowWidth - row->width; | |||
rminx = x + row->minx + dx; | |||
rmaxx = x + row->maxx + dx; | |||
minx = nvg__minf(minx, rminx); | |||
maxx = nvg__maxf(maxx, rmaxx); | |||
// Vertical bounds. | |||
miny = nvg__minf(miny, y + rminy); | |||
maxy = nvg__maxf(maxy, y + rmaxy); | |||
y += lineh * state->lineHeight; | |||
} | |||
string = rows[nrows-1].next; | |||
} | |||
state->textAlign = oldAlign; | |||
if (bounds != NULL) { | |||
bounds[0] = minx * invscale; | |||
bounds[1] = miny * invscale; | |||
bounds[2] = maxx * invscale; | |||
bounds[3] = maxy * invscale; | |||
} | |||
} | |||
void nvgTextMetrics(struct NVGcontext* ctx, float* ascender, float* descender, float* lineh) | |||
{ | |||
struct NVGstate* state = nvg__getState(ctx); | |||
@@ -96,7 +96,8 @@ struct NVGtextRow { | |||
const char* start; // Pointer to the input text where the row starts. | |||
const char* end; // Pointer to the input text where the row ends (one past the last character). | |||
const char* next; // Pointer to the beginning of the next row. | |||
float width; // Width of the row. | |||
float width; // Logical width of the row. | |||
float minx, maxx; // Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending. | |||
}; | |||
@@ -385,7 +386,26 @@ void nvgStroke(struct NVGcontext* ctx); | |||
// font size, letter spacing and text align are supported. Font blur allows you | |||
// to create simple text effects such as drop shadows. | |||
// | |||
// At render time the tont face can be set based on the font handles or name. | |||
// At render time the font face can be set based on the font handles or name. | |||
// | |||
// Font measure functions return values in local space, the calculations are | |||
// carried in the same resolution as the final rendering. This is done because | |||
// the text glyph positions are snapped to the nearest pixels sharp rendering. | |||
// | |||
// The local space means that values are not rotated or scale as per the current | |||
// transformation. For example if you set font size to 12, which would mean that | |||
// line height is 16, then regardless of the current scaling and rotation, the | |||
// returned line height is always 16. Some measures may vary because of the scaling | |||
// since aforementioned pixel snapping. | |||
// | |||
// While this may sound a little odd, the setup allows you to always render the | |||
// same way regardless of scaling. I.e. following works regardless of scaling: | |||
// | |||
// const char* txt = "Text me up."; | |||
// nvgTextBounds(vg, x,y, txt, NULL, bounds); | |||
// nvgBeginPath(vg); | |||
// nvgRoundedRect(vg, bounds[0],bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]); | |||
// nvgFill(vg); | |||
// | |||
// Note: currently only solid color fill is supported for text. | |||
@@ -427,24 +447,31 @@ float nvgText(struct NVGcontext* ctx, float x, float y, const char* string, cons | |||
// Draws multi-line text string at specified location wrapped at the specified width. If end is specified only the sub-string up to the end is drawn. | |||
// White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. | |||
// Words longer than the max width are slit at nearest character (i.e. no hyphenation). | |||
void nvgTextBox(struct NVGcontext* ctx, float x, float y, float width, const char* string, const char* end); | |||
void nvgTextBox(struct NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end); | |||
// Measures the specified text string. Parameter bounds should be a pointer to float[4] if | |||
// the bounding box of the text should be returned. Returns the width of the measured text. | |||
// Current transform does not affect the measured values. | |||
float nvgTextBounds(struct NVGcontext* ctx, const char* string, const char* end, float* bounds); | |||
// Measures the specified text string. Parameter bounds should be a pointer to float[4], | |||
// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] | |||
// Returns the horizontal advance of the measured text (i.e. where the next character should drawn). | |||
// Measured values are returned in local coordinate space. | |||
float nvgTextBounds(struct NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds); | |||
// Returns the vertical metrics based on the current text style. | |||
// Current transform does not affect the measured values. | |||
void nvgTextMetrics(struct NVGcontext* ctx, float* ascender, float* descender, float* lineh); | |||
// Measures the specified multi-text string. Parameter bounds should be a pointer to float[4], | |||
// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] | |||
// Measured values are returned in local coordinate space. | |||
void nvgTextBoxBounds(struct NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds); | |||
// Calculates the glyph x positions of the specified text. If end is specified only the sub-string will be used. | |||
int nvgTextGlyphPositions(struct NVGcontext* ctx, const char* string, const char* end, float x, float y, struct NVGglyphPosition* positions, int maxPositions); | |||
// Measured values are returned in local coordinate space. | |||
int nvgTextGlyphPositions(struct NVGcontext* ctx, float x, float y, const char* string, const char* end, struct NVGglyphPosition* positions, int maxPositions); | |||
// Returns the vertical metrics based on the current text style. | |||
// Measured values are returned in local coordinate space. | |||
void nvgTextMetrics(struct NVGcontext* ctx, float* ascender, float* descender, float* lineh); | |||
// Breaks the specified text into lines. If end is specified only the sub-string will be used. | |||
// White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. | |||
// Words longer than the max width are slit at nearest character (i.e. no hyphenation). | |||
int nvgTextBreakLines(struct NVGcontext* ctx, const char* string, const char* end, float maxRowWidth, struct NVGtextRow* rows, int maxRows); | |||
int nvgTextBreakLines(struct NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, struct NVGtextRow* rows, int maxRows); | |||
// | |||
// Internal Render API | |||