void OsmAnd::TextRasterizer_P::measureHalo(const Style& style, QVector<LinePaint>& paints) const { for (auto& linePaint : paints) { linePaint.maxBoundsTop = 0; linePaint.minBoundsTop = std::numeric_limits<SkScalar>::max(); for (auto& textPaint : linePaint.textPaints) { const auto haloPaint = getHaloPaint(textPaint.paint, style); SkPaint::FontMetrics metrics; textPaint.height = haloPaint.getFontMetrics(&metrics); linePaint.maxFontHeight = qMax(linePaint.maxFontHeight, textPaint.height); linePaint.minFontHeight = qMin(linePaint.minFontHeight, textPaint.height); linePaint.maxFontLineSpacing = qMax(linePaint.maxFontLineSpacing, metrics.fLeading); linePaint.minFontLineSpacing = qMin(linePaint.minFontLineSpacing, metrics.fLeading); linePaint.maxFontTop = qMax(linePaint.maxFontTop, -metrics.fTop); linePaint.minFontTop = qMin(linePaint.minFontTop, -metrics.fTop); linePaint.maxFontBottom = qMax(linePaint.maxFontBottom, metrics.fBottom); linePaint.minFontBottom = qMin(linePaint.minFontBottom, metrics.fBottom); SkRect haloBounds; haloPaint.measureText( textPaint.text.constData(), textPaint.text.length()*sizeof(QChar), &haloBounds); textPaint.bounds.join(haloBounds); linePaint.maxBoundsTop = qMax(linePaint.maxBoundsTop, -textPaint.bounds.top()); linePaint.minBoundsTop = qMin(linePaint.minBoundsTop, -textPaint.bounds.top()); } } }
void WMeasurePaintDevice::drawText(const WRectF& rect, WFlags<AlignmentFlag> flags, TextFlag textFlag, const WString& text, const WPointF *clipPoint) { if (clipPoint && painter()) { if (!painter()->clipPathTransform().map(painter()->clipPath()) .isPointInPath(painter()->worldTransform().map(*clipPoint))) return; } double w = 0, h = 0; WString line = text; WFontMetrics fm = fontMetrics(); for (;;) { WTextItem t = measureText(line, rect.width(), textFlag == TextWordWrap ? true : false); h += fm.height(); w = std::max(w, t.width()); if (t.text() == line) break; else line = WString ::fromUTF8(line.toUTF8().substr(t.text().toUTF8().length())); } AlignmentFlag horizontalAlign = flags & AlignHorizontalMask; AlignmentFlag verticalAlign = flags & AlignVerticalMask; double x, y; switch (horizontalAlign) { case AlignLeft: x = rect.left(); break; case AlignCenter: x = rect.left() + (rect.width() - w) / 2; break; case AlignRight: default: x = rect.right() - w; break; } switch (verticalAlign) { case AlignTop: y = rect.top(); break; case AlignMiddle: y = rect.top() + (rect.height() - h) / 2; break; case AlignBottom: default: y = rect.bottom() - h; break; } expandBounds(WRectF(x, y, w, h)); }
Font::FONT_RECT Font::measureText(const char* text) throw(invalid_argument, runtime_error) { ASSERT( (text != 0), invalid_argument("text") ); return measureText(string(text)); }
Font::FONT_RECT Font::measureText(const wstring& wText) throw(runtime_error) { try { return measureText(wText.c_str()); } catch(const invalid_argument&){} //return FTBBox(); }
int GUISliderValue::Render(void) { if (!isConditionTrue()) { mRendered = false; return 0; } if(mLabel) { int w, h; mLabel->GetCurrentBounds(w, h); if (w != mLabelW) { mLabelW = w; int textX = mRenderX + (mRenderW/2 - mLabelW/2); mLabel->SetRenderPos(textX, mRenderY); } int res = mLabel->Render(); if(res < 0) return res; } // line gr_color(mLineColor.red, mLineColor.green, mLineColor.blue, mLineColor.alpha); gr_fill(mLineX, mLineY, lineW, mLineH); // slider uint32_t sliderX = (mValuePct*lineW)/100 + mLineX; sliderX -= mSliderW/2; gr_color(mSliderColor.red, mSliderColor.green, mSliderColor.blue, mSliderColor.alpha); gr_fill(sliderX, mSliderY, mSliderW, mSliderH); void *fontResource = NULL; if(mFont) fontResource = mFont->GetResource(); gr_color(mTextColor.red, mTextColor.green, mTextColor.blue, mTextColor.alpha); if(mShowRange) { int rangeY = (mLineY - mLineH/2) - mFontHeight/2; gr_textEx(mRenderX + mPadding/2, rangeY, mMinStr.c_str(), fontResource); gr_textEx(mLineX + lineW + mPadding/2, rangeY, mMaxStr.c_str(), fontResource); } if(mValueStr && mShowCurr) { sprintf(mValueStr, "%d", mValue); int textW = measureText(mValueStr); gr_textEx(mRenderX + (mRenderW/2 - textW/2), mSliderY+mSliderH, mValueStr, fontResource); } mRendered = true; return 0; }
int BaseFontTT::getTextHeight(const byte *text, int width) { WideString textStr; if (_gameRef->_textEncoding == TEXT_UTF8) { textStr = StringUtil::utf8ToWide((const char *)text); } else { textStr = StringUtil::ansiToWide((const char *)text); } int textWidth, textHeight; measureText(textStr, width, -1, textWidth, textHeight); return textHeight; }
void Font::renderText(const wchar_t* wText, float x, float y, float width, float height) throw(invalid_argument, runtime_error) { ASSERT( (wText != 0), invalid_argument("wText") ); std::lock_guard<std::mutex> guard(synchroMutex_); if(font_ == 0) { return; } size_t textLen = wcslen(wText); if(width >= 0.0f) { Font::FONT_RECT rect = measureText(wText); if(rect.width > width) { float pixPerChar = rect.width / textLen; float excess = rect.width - width; size_t charsForErase = static_cast<size_t>(lround(excess / pixPerChar)); textLen -= charsForErase; } } glPushMatrix(); glPushAttrib(GL_COLOR_BUFFER_BIT); glColor3fv(color_.c_array()); font_->Render(wText, textLen, FTPoint(static_cast<FTGL_FLOAT>(x), static_cast<FTGL_FLOAT>(y)), FTPoint(), FTGL::RENDER_ALL); glPopAttrib(); glPopMatrix(); }
int BaseFontTT::getTextWidth(const byte *text, int maxLength) { WideString textStr; if (_gameRef->_textEncoding == TEXT_UTF8) { textStr = StringUtil::utf8ToWide((const char *)text); } else { textStr = StringUtil::ansiToWide((const char *)text); } if (maxLength >= 0 && textStr.size() > (uint32)maxLength) { textStr = WideString(textStr.c_str(), (uint32)maxLength); } //text = text.substr(0, MaxLength); // TODO: Remove int textWidth, textHeight; measureText(textStr, -1, -1, textWidth, textHeight); return textWidth; }
Font::FONT_RECT Font::measureText(const string& text) throw(runtime_error) { return measureText(UTF8_to_UTF16(text)); }
/* ** Create a dialog for the output of a shell command. The dialog lives until ** the user presses the Dismiss button, and is then destroyed */ static void createOutputDialog(Widget parent, char *text) { Arg al[50]; int ac, rows, cols, hasScrollBar, wrapped; Widget form, textW, button; XmString st1; /* measure the width and height of the text to determine size for dialog */ measureText(text, MAX_OUT_DIALOG_COLS, &rows, &cols, &wrapped); if (rows > MAX_OUT_DIALOG_ROWS) { rows = MAX_OUT_DIALOG_ROWS; hasScrollBar = True; } else hasScrollBar = False; if (cols > MAX_OUT_DIALOG_COLS) cols = MAX_OUT_DIALOG_COLS; if (cols == 0) cols = 1; /* Without completely emulating Motif's wrapping algorithm, we can't be sure that we haven't underestimated the number of lines in case a line has wrapped, so let's assume that some lines could be obscured */ if (wrapped) hasScrollBar = True; ac = 0; form = CreateFormDialog(parent, "shellOutForm", al, ac); ac = 0; XtSetArg(al[ac], XmNlabelString, st1=MKSTRING("OK")); ac++; XtSetArg(al[ac], XmNmarginWidth, BUTTON_WIDTH_MARGIN); ac++; XtSetArg(al[ac], XmNhighlightThickness, 0); ac++; XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++; button = XmCreatePushButtonGadget(form, "ok", al, ac); XtManageChild(button); XtVaSetValues(form, XmNdefaultButton, button, NULL); XtVaSetValues(form, XmNcancelButton, button, NULL); XmStringFree(st1); XtAddCallback(button, XmNactivateCallback, destroyOutDialogCB, XtParent(form)); ac = 0; XtSetArg(al[ac], XmNrows, rows); ac++; XtSetArg(al[ac], XmNcolumns, cols); ac++; XtSetArg(al[ac], XmNresizeHeight, False); ac++; XtSetArg(al[ac], XmNtraversalOn, False); ac++; XtSetArg(al[ac], XmNwordWrap, True); ac++; XtSetArg(al[ac], XmNscrollHorizontal, False); ac++; XtSetArg(al[ac], XmNscrollVertical, hasScrollBar); ac++; XtSetArg(al[ac], XmNhighlightThickness, 0); ac++; XtSetArg(al[ac], XmNspacing, 0); ac++; XtSetArg(al[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++; XtSetArg(al[ac], XmNeditable, False); ac++; XtSetArg(al[ac], XmNvalue, text); ac++; XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++; XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++; XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; XtSetArg(al[ac], XmNbottomWidget, button); ac++; textW = XmCreateScrolledText(form, "outText", al, ac); AddMouseWheelSupport(textW); XtManageChild(textW); XtVaSetValues(XtParent(form), XmNtitle, "Output from Command", NULL); ManageDialogCenteredOnPointer(form); }
GUISliderValue::GUISliderValue(xml_node<>* node) : GUIObject(node) { xml_attribute<>* attr; xml_node<>* child; mMin = 0; mMax = 100; mValue = 0; mLineH = 2; mLinePadding = 10; mSliderW = 5; mSliderH = 30; mLineX = 0; mLineY = 0; mValueStr = NULL; mAction = NULL; mShowCurr = true; mShowRange = false; mChangeOnDrag = false; mRendered = false; mLabel = NULL; ConvertStrToColor("white", &mTextColor); ConvertStrToColor("white", &mLineColor); ConvertStrToColor("blue", &mSliderColor); if (!node) { LOGERR("GUISliderValue created without XML node\n"); return; } mLabel = new GUIText(node); if(mLabel->Render() < 0) { delete mLabel; mLabel = NULL; } mAction = new GUIAction(node); child = node->first_node("font"); if (child) { attr = child->first_attribute("resource"); if (attr) mFont = PageManager::FindResource(attr->value()); attr = child->first_attribute("color"); if (attr) { std::string color = attr->value(); ConvertStrToColor(color, &mTextColor); } } // Load the placement LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW); child = node->first_node("colors"); if (child) { attr = child->first_attribute("line"); if (attr) ConvertStrToColor(attr->value(), &mLineColor); attr = child->first_attribute("slider"); if (attr) ConvertStrToColor(attr->value(), &mSliderColor); } child = node->first_node("data"); if (child) { attr = child->first_attribute("variable"); if (attr) mVariable = attr->value(); attr = child->first_attribute("min"); if (attr) { mMinStr = gui_parse_text(attr->value()); mMin = atoi(mMinStr.c_str()); } attr = child->first_attribute("max"); if (attr) { mMaxStr = gui_parse_text(attr->value()); mMax = atoi(mMaxStr.c_str()); } if (mMin > mMax) mMin = mMax; attr = child->first_attribute("default"); if (attr) { string parsevalue = gui_parse_text(attr->value()); int def = atoi(parsevalue.c_str()); if (def < mMin) def = mMin; else if (def > mMax) def = mMax; DataManager::SetValue(mVariable, def); } attr = child->first_attribute("showrange"); if (attr) mShowRange = atoi(attr->value()); attr = child->first_attribute("showcurr"); if (attr) mShowCurr = atoi(attr->value()); attr = child->first_attribute("changeondrag"); if (attr) mChangeOnDrag = atoi(attr->value()); } child = node->first_node("dimensions"); if (child) { attr = child->first_attribute("lineh"); if (attr) { string parsevalue = gui_parse_text(attr->value()); mLineH = atoi(parsevalue.c_str()); } attr = child->first_attribute("linepadding"); if (attr) { string parsevalue = gui_parse_text(attr->value()); mPadding = atoi(parsevalue.c_str()); } attr = child->first_attribute("sliderw"); if (attr) { string parsevalue = gui_parse_text(attr->value()); mSliderW = atoi(parsevalue.c_str()); } attr = child->first_attribute("sliderh"); if (attr) { string parsevalue = gui_parse_text(attr->value()); mSliderH = atoi(parsevalue.c_str()); } } gr_getFontDetails(mFont ? mFont->GetResource() : NULL, (unsigned*) &mFontHeight, NULL); if(mShowCurr) { int maxLen = std::max(strlen(mMinStr.c_str()), strlen(mMaxStr.c_str())); mValueStr = new char[maxLen+1]; } loadValue(true); mLinePadding = mPadding; if (mShowRange) { int textW = std::max(measureText(mMaxStr), measureText(mMinStr)); mLinePadding += textW; } SetRenderPos(mRenderX, mRenderY, mRenderW); }
WTextItem FontSupport::measureText(const WFont& font, const WString& text, double maxWidth, bool wordWrap) { PANGO_LOCK; enabledFontFormats = enabledFontFormats_; /* * Note: accurate measuring on a bitmap requires that the transformation * is applied, because hinting may push chars to boundaries e.g. when * rotated (or scaled too?) */ std::string utf8 = text.toUTF8(); const char *s = utf8.c_str(); if (wordWrap) { int utflen = g_utf8_strlen(s, -1); PangoLogAttr *attrs = new PangoLogAttr[utflen + 1]; PangoLanguage *language = pango_language_from_string("en-US"); pango_get_log_attrs(s, utf8.length(), -1, language, attrs, utflen + 1); double w = 0, nextW = -1; int current = 0; int measured = 0; int end = 0; bool maxWidthReached = false; for (int i = 0; i < utflen + 1; ++i) { if (i == utflen || attrs[i].is_line_break) { int cend = g_utf8_offset_to_pointer(s, end) - s; WTextItem ti = measureText(font, WString::fromUTF8(utf8.substr(measured, cend - measured)), -1, false); if (isEpsilonMore(w + ti.width(), maxWidth)) { nextW = ti.width(); maxWidthReached = true; break; } else { measured = cend; current = g_utf8_offset_to_pointer(s, i) - s; w += ti.width(); if (i == utflen) { w += measureText(font, WString::fromUTF8(utf8.substr(measured)), -1, false).width(); measured = utf8.length(); } } } if (!attrs[i].is_white) end = i + 1; } delete[] attrs; if (maxWidthReached) { return WTextItem(WString::fromUTF8(utf8.substr(0, current)), w, nextW); } else { /* * For some reason, the sum of the individual widths is a bit less * (for longer stretches of text), so we re-measure it ! */ w = measureText(font, WString::fromUTF8(utf8.substr(0, measured)), -1, false).width(); return WTextItem(text, w); } } else { std::vector<PangoGlyphString *> glyphs; int width; GList *items = layoutText(font, utf8, glyphs, width); double w = pangoUnitsToDouble(width); for (unsigned i = 0; i < glyphs.size(); ++i) pango_glyph_string_free(glyphs[i]); g_list_foreach(items, (GFunc) pango_item_free, nullptr); g_list_free(items); return WTextItem(text, w); } }
void Dashboard::measureText(const string& text, int size, bool mono, int& width, int& height) { measureText(text.c_str(), size, mono, width, height); }
bool OsmAnd::TextRasterizer_P::rasterize( SkBitmap& targetBitmap, const QString& text_, const Style& style, QVector<SkScalar>* const outGlyphWidths, float* const outExtraTopSpace, float* const outExtraBottomSpace, float* const outLineSpacing) const { // Prepare text and break by lines const auto text = ICU::convertToVisualOrder(text_); const auto lineRefs = style.wrapWidth > 0 ? ICU::getTextWrappingRefs(text, style.wrapWidth) : (QVector<QStringRef>() << QStringRef(&text)); // Obtain paints from lines and style auto paints = evaluatePaints(lineRefs, style); // Measure text SkScalar maxLineWidthInPixels = 0; measureText(paints, maxLineWidthInPixels); // Measure glyphs (if requested and there's no halo) if (outGlyphWidths && style.haloRadius == 0) measureGlyphs(paints, *outGlyphWidths); // Process halo if exists if (style.haloRadius > 0) { measureHalo(style, paints); if (outGlyphWidths) measureHaloGlyphs(style, paints, *outGlyphWidths); } // Set output line spacing if (outLineSpacing) { float lineSpacing = 0.0f; for (const auto& linePaint : constOf(paints)) lineSpacing = qMax(lineSpacing, linePaint.maxFontLineSpacing); *outLineSpacing = lineSpacing; } // Calculate extra top and bottom space if (outExtraTopSpace) { SkScalar maxTop = 0; for (const auto& linePaint : constOf(paints)) maxTop = qMax(maxTop, linePaint.maxFontTop); *outExtraTopSpace = qMax(0.0f, maxTop - paints.first().maxFontTop); } if (outExtraBottomSpace) { SkScalar maxBottom = 0; for (const auto& linePaint : constOf(paints)) maxBottom = qMax(maxBottom, linePaint.maxFontBottom); *outExtraBottomSpace = qMax(0.0f, maxBottom - paints.last().maxFontBottom); } // Position text horizontally and vertically const auto textArea = positionText(paints, maxLineWidthInPixels, style.textAlignment); // Calculate bitmap size auto bitmapWidth = qCeil(textArea.width()); auto bitmapHeight = qCeil(textArea.height()); if (style.backgroundBitmap) { // Clear extra spacing if (outExtraTopSpace) *outExtraTopSpace = 0.0f; if (outExtraBottomSpace) *outExtraBottomSpace = 0.0f; // Enlarge bitmap if shield is larger than text bitmapWidth = qMax(bitmapWidth, style.backgroundBitmap->width()); bitmapHeight = qMax(bitmapHeight, style.backgroundBitmap->height()); // Shift text area to proper position in a larger const auto offset = SkPoint::Make( (bitmapWidth - qCeil(textArea.width())) / 2.0f, (bitmapHeight - qCeil(textArea.height())) / 2.0f); for (auto& linePaint : paints) { for (auto& textPaint : linePaint.textPaints) textPaint.positionedBounds.offset(offset); } } // Check if bitmap size was successfully calculated if (bitmapWidth <= 0 || bitmapHeight <= 0) { LogPrintf(LogSeverityLevel::Error, "Failed to rasterize text '%s': resulting bitmap size %dx%d is invalid", qPrintable(text), bitmapWidth, bitmapHeight); return false; } // Create a bitmap that will be hold entire symbol (if target is empty) if (targetBitmap.isNull()) { if (!targetBitmap.tryAllocPixels(SkImageInfo::MakeN32Premul(bitmapWidth, bitmapHeight))) { LogPrintf(LogSeverityLevel::Error, "Failed to allocate bitmap of size %dx%d", qPrintable(text), bitmapWidth, bitmapHeight); return false; } targetBitmap.eraseColor(SK_ColorTRANSPARENT); } SkBitmapDevice target(targetBitmap); SkCanvas canvas(&target); // If there is background this text, rasterize it also if (style.backgroundBitmap) { canvas.drawBitmap(*style.backgroundBitmap, (bitmapWidth - style.backgroundBitmap->width()) / 2.0f, (bitmapHeight - style.backgroundBitmap->height()) / 2.0f, nullptr); } // Rasterize text halo first (if enabled) if (style.haloRadius > 0) { for (const auto& linePaint : paints) { for (const auto& textPaint : linePaint.textPaints) { const auto haloPaint = getHaloPaint(textPaint.paint, style); canvas.drawText( textPaint.text.constData(), textPaint.text.length()*sizeof(QChar), textPaint.positionedBounds.left(), textPaint.positionedBounds.top(), haloPaint); } } } // Rasterize text itself for (const auto& linePaint : paints) { for (const auto& textPaint : linePaint.textPaints) { canvas.drawText( textPaint.text.constData(), textPaint.text.length()*sizeof(QChar), textPaint.positionedBounds.left(), textPaint.positionedBounds.top(), textPaint.paint); } } canvas.flush(); return true; }