int reflow(struct textLine *ln, int tillend) { int len, i; struct textLine *nextln; while (ln && ln->next && !ln->br) { nextln = ln->next; if ((ln->selected && !nextln->selected) || (!ln->selected && nextln->selected)) { ln = nextln; continue; } if (!ln->len) { nextln->prev = ln->prev; if (nextln->next) nextln->next->prev = ln; keepSelect(ln); *ln = *nextln; //curline 可能正指向 ln, 所以释放 nextln 而不是 ln free(nextln); ln->changed = 1; continue; } len = min(MAXLINELEN - ln->len, nextln->len); if (len) { memcpy(ln->text + ln->len, nextln->text, len); //从下一行抓取的字符数 len = measureLine(ln->text, ln->len + len, MAXLINELEN, 0, &i) - ln->len; } if (nextln->len && len <= 0) break; ln->len += len; nextln->len -= len; memmove(nextln->text, nextln->text + len, nextln->len); ln->changed = 1; nextln->changed = 1; if (!nextln->len && measureWidth(ln->text, ln->len) < MAXLINELEN) { ln->next = nextln->next; if (nextln->next) nextln->next->prev = ln; if (nextln->br) ln->br = 1; keepSelect(nextln); free(nextln); if (ln->br) break; nextln = ln->next; } ln = nextln; } return 0; }
static void insertChar(struct edit *edit, const char ch) { struct textLine *ln = edit->curline, *newln; int len, width; if (!edit->overwrite) memmove(ln->text + edit->col + 1, ln->text + edit->col, ln->len - edit->col); ln->text[edit->col] = ch; edit->col++; edit->colcur = measureWidth(ln->text, edit->col); if (!edit->overwrite || edit->col > ln->len) ln->len++; ln->changed = 1; len = measureLine(ln->text, ln->len, MAXLINELEN, 0, &width); if (len == ln->len && width < MAXLINELEN) { edit->position = edit->colcur; return; } newln = allocNextLine(ln); newln->br = ln->br; ln->br = 0; memcpy(newln->text, ln->text + len, ln->len - len); newln->len = ln->len - len; ln->len = len; edit->redrawall = 1; reflow(newln, 0); if (edit->col > len || edit->colcur >= MAXLINELEN) { edit->curline = ln->next; edit->col = edit->col - len; edit->colcur = measureWidth(edit->curline->text, edit->col); edit->line++; edit->linecur++; } edit->position = edit->colcur; }
void Font::renderString( const std::wstring & str, glm::vec2 & cursor, float fontSize, float maxWidth) { float scale = Text::Font::DTP_TO_METERS * fontSize / mFontSize; bool wrap = (maxWidth == maxWidth); if (wrap) { maxWidth /= scale; } gl::MatrixStack & mv = gl::Stacks::modelview(); size_t mvDepth = mv.size(); glm::vec4 aspectTest = gl::Stacks::projection().top() * glm::vec4(1, 1, 0, 1); float aspect = std::abs(aspectTest.x / aspectTest.y); mv.push().translate(cursor).translate(glm::vec2(0, scale * -mAscent)); // scale the modelview from into font units mv.scale(scale); gl::ProgramPtr program = GlUtils::getProgram( Resource::SHADERS_TEXT_VS, Resource::SHADERS_TEXT_FS); program->use(); program->setUniform("Color", glm::vec4(1)); program->setUniform("Font", 0); program->setUniform4x4f("Projection", gl::Stacks::projection().top()); mTexture->bind(); mGeometry->bindVertexArray(); std::vector<std::wstring> tokens = Tokenize(str); // Stores how far we've moved from the start of the string, in DTP units glm::vec2 advance; static std::wstring SPACE = toUtf16(" "); for_each(tokens.begin(), tokens.end(), [&](const std::wstring & token) { float tokenWidth = measureWidth(token, fontSize) ; if (wrap && 0 != advance.x && (advance.x + tokenWidth) > maxWidth) { advance.x = 0; advance.y -= (mAscent + mDescent); } for_each(token.begin(), token.end(), [&](::uint16_t id) { if ('\n' == id) { advance.x = 0; advance.y -= (mAscent + mDescent); return; } if (!contains(id)) { id = '?'; } // get metrics for this character to speed up measurements const Font::Metrics & m = getMetrics(id); if (wrap && ((advance.x + m.d) > maxWidth)) { advance.x = 0; advance.y -= (mAscent + mDescent); } // We create an offset vec2 to hold the local offset of this character // This includes compensating for the inverted Y axis of the font // coordinates glm::vec2 offset(advance); offset.y -= m.size.y; // Bind the new position mv.push().translate(offset).apply(program).pop(); // Render the item glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*)(m.indexOffset * sizeof(GLuint))); advance.x += m.d;//+ m.offset.x;// font->getAdvance(m, mFontSize); }); advance.x += getMetrics(' ').d; }); gl::VertexArray::unbind(); gl::Texture2d::unbind(); gl::Program::clear(); mv.pop(); //cursor.x += advance * scale; }
float Font::measureWidth(const std::wstring &text, float fontSize, bool precise) const { return measureWidth(text, 0, text.length(), fontSize, precise); }
void wyBitmapFontLabel::updateContentSize() { // remove all quads in all atlas wyArrayEach(m_atlasList, clearAtlas, NULL); // if null, do nothing if(!m_text) return; // break line into a vector vector<const char*>* lines = wyUtils::breakLines(m_text, m_font, m_lineWidth, m_spaceWidth, m_tabSize); // get width first because we need support text alignment vector<float>* widthList = measureWidth(lines); // get max width float maxWidth = 0; for(vector<float>::iterator iter = widthList->begin(); iter != widthList->end(); iter++) { maxWidth = MAX(maxWidth, *iter); } // line number int line = 0; // real width of final render float width = 0; // final render y position float y = 0; // prev line height float prevLineHeight = 0; // render line one by one for(vector<const char*>::iterator iter = lines->begin(); iter != lines->end(); iter++, line++) { char* p = (char*)(*iter); float x = 0; float lineOffset = 0; float charWidth, charHeight; bool firstLineChar = true; float currentLineHeight = 0; // line offset switch(m_alignment) { case LEFT: lineOffset = 0; break; case CENTER: lineOffset = (maxWidth - widthList->at(line)) / 2; break; case RIGHT: lineOffset = maxWidth - widthList->at(line); break; } // if first char of this line, add line spacing if(line > 0) { y += m_lineSpacing; if(prevLineHeight == 0) y += m_font->getEmptyLineHeight(); else y += prevLineHeight; } while(*p != 0) { // get char integer int c = 0; int b = wyUtils::getUTF8Bytes(*p); while(b-- > 0) { c <<= 8; c |= *p & 0xff; p++; } wyCharInfo* pCi = m_font->getCharInfo(c); if(pCi) { // get char size charWidth = pCi->texRect.width; charHeight = pCi->texRect.height; // if not first char of line, add left padding if(!firstLineChar) { x += pCi->left; } // choose max height as line height currentLineHeight = m_lineHeight > 0 ? m_lineHeight : MAX(currentLineHeight, charHeight + pCi->top); // get atlas wyTextureAtlas* atlas = (wyTextureAtlas*)wyArrayGet(m_atlasList, pCi->page); // get vertex corner float left = x + lineOffset; float right = left + charWidth; float top = -y - pCi->top; float bottom = top - charHeight; // build vertex wyQuad3D v; v.bl_x = left; v.bl_y = bottom; v.bl_z = 0.0f; v.br_x = right; v.br_y = bottom; v.br_z = 0.0f; v.tl_x = left; v.tl_y = top; v.tl_z = 0.0f; v.tr_x = right; v.tr_y = top; v.tr_z = 0.0f; // build texture coordinates wyQuad2D t; wyTexture2D* tex = m_font->getTexture(pCi->page); left = pCi->texRect.x / tex->getPixelWidth(); right = (pCi->texRect.x + charWidth) / tex->getPixelWidth(); top = pCi->texRect.y / tex->getPixelHeight(); bottom = (pCi->texRect.y + charHeight) / tex->getPixelHeight(); t.bl_x = left; t.bl_y = bottom; t.br_x = right; t.br_y = bottom; t.tl_x = left; t.tl_y = top; t.tr_x = right; t.tr_y = top; // add quad atlas->appendQuad(t, v); // adjust x and y x += charWidth + pCi->right; } else { // special check for space or tab if(c == ' ') { x += m_spaceWidth; } else if(c == '\t') { x += m_spaceWidth * m_tabSize; } } // we can clear line first char flag here firstLineChar = false; } // save line height prevLineHeight = currentLineHeight; // save max width width = MAX(width, x); } // add last line if(prevLineHeight != 0) { y += prevLineHeight; } // set label content size setContentSize(width, y); // adjust quad for(int i = 0; i < m_atlasList->num; i++) { wyTextureAtlas* atlas = (wyTextureAtlas*)wyArrayGet(m_atlasList, i); atlas->iterateQuad3D(adjustAtlasPosition, this); } // release for(vector<const char*>::iterator iter = lines->begin(); iter != lines->end(); iter++) { releaseLine(*iter); } WYDELETE(lines); WYDELETE(widthList); }