| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- //
- // Copyright (c) 2008-2014 the Urho3D project.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- #include "Precompiled.h"
- #include "Context.h"
- #include "Font.h"
- #include "FontFace.h"
- #include "Log.h"
- #include "Material.h"
- #include "Node.h"
- #include "Profiler.h"
- #include "ResourceCache.h"
- #include "Technique.h"
- #include "Texture2D.h"
- #include "UIRect.h"
- #include "UIText.h"
- #include "UIXEvents.h"
- #include "DebugNew.h"
- namespace Urho3D
- {
- static const float MIN_ROW_SPACING = 0.5f;
- extern const char* textEffects[];
- extern const char* horizontalAlignments[];
- extern const char* UIX_CATEGORY;
- void UITextLineInfo::Clear()
- {
- lineWidth_ = 0.0f;
- fontGlyphs_.Clear();
- glyphAdvances_.Clear();
- }
- void UITextLineInfo::Add(const FontGlyph* glyph, short kerning)
- {
- fontGlyphs_.Push(glyph);
-
- float advance = (glyph->advanceX_ + kerning) * PIXEL_SIZE;
- lineWidth_ += advance;
- glyphAdvances_.Push(advance);
- }
- UIText::UIText(Context* context) :
- Drawable2D(context),
- usedInText3D_(false),
- fontSize_(DEFAULT_FONT_SIZE),
- textAlignment_(HA_LEFT),
- rowSpacing_(1.0f),
- color_(Color::WHITE),
- selectionStart_(0),
- selectionLength_(0),
- selectionColor_(Color::TRANSPARENT),
- textEffect_(TE_NONE),
- effectColor_(Color::BLACK),
- rowHeight_(0.0f),
- numChars_(0)
- {
- }
- UIText::~UIText()
- {
- }
- void UIText::RegisterObject(Context* context)
- {
- context->RegisterFactory<UIText>(UIX_CATEGORY);
- MIXED_ACCESSOR_ATTRIBUTE("Font", GetFontAttr, SetFontAttr, ResourceRef, ResourceRef(Font::GetTypeStatic()), AM_FILE);
- ACCESSOR_ATTRIBUTE("Font Size", GetFontSize, SetFontSize, int, DEFAULT_FONT_SIZE, AM_FILE);
- ACCESSOR_ATTRIBUTE("Text", GetText, SetText, String, String::EMPTY, AM_FILE);
- ENUM_ACCESSOR_ATTRIBUTE("Text Alignment", GetTextAlignment, SetTextAlignment, HorizontalAlignment, horizontalAlignments, HA_LEFT, AM_FILE);
- ACCESSOR_ATTRIBUTE("Row Spacing", GetRowSpacing, SetRowSpacing, float, 1.0f, AM_FILE);
- ACCESSOR_ATTRIBUTE("Color", GetColor, SetColor, Color, Color::WHITE, AM_FILE);
- ACCESSOR_ATTRIBUTE("Selection Color", GetSelectionColor, SetSelectionColor, Color, Color::TRANSPARENT, AM_FILE);
- ENUM_ACCESSOR_ATTRIBUTE("Text Effect", GetTextEffect, SetTextEffect, TextEffect, textEffects, TE_NONE, AM_FILE);
- ACCESSOR_ATTRIBUTE("Effect Color", GetEffectColor, SetEffectColor, Color, Color::BLACK, AM_FILE);
- COPY_BASE_ATTRIBUTES(Drawable2D);
- }
- bool UIText::SetFont(const String& fontName, int size)
- {
- ResourceCache* cache = GetSubsystem<ResourceCache>();
- return SetFont(cache->GetResource<Font>(fontName), size);
- }
- bool UIText::SetFont(Font* font, int size)
- {
- if (!font)
- {
- LOGERROR("Null font for Text");
- return false;
- }
- if (font != font_ || size != fontSize_)
- {
- font_ = font;
- fontSize_ = Max(size, 1);
- if (font_)
- {
- fontFace_ = font->GetFace(fontSize_);
- SetTexture(fontFace_->GetTextures()[0]);
- UpdateMaterial(true);
- }
- else
- {
- SetTexture(0);
- SetCustomMaterial(0);
- }
-
- UpdateText();
- verticesDirty_ = true;
- }
- return true;
- }
- void UIText::SetFontSize(int fontSize)
- {
- SetFont(font_, fontSize);
- }
- void UIText::SetText(const String& text)
- {
- if (text == text_)
- return;
- text_ = text;
-
- UpdateText();
- ValidateSelection();
- verticesDirty_ = true;
- }
- void UIText::SetTextAlignment(HorizontalAlignment align)
- {
- if (align == textAlignment_)
- return;
-
- textAlignment_ = align;
- verticesDirty_ = true;
- }
- void UIText::SetRowSpacing(float spacing)
- {
- if (spacing == rowSpacing_)
- return;
- rowSpacing_ = Max(spacing, MIN_ROW_SPACING);
- verticesDirty_ = true;
- }
- void UIText::SetColor(const Color& color)
- {
- if (color == color_)
- return;
- color_ = color;
- verticesDirty_ = true;
- }
- void UIText::SetSelection(unsigned start, unsigned length)
- {
- if (start == selectionStart_ && length == selectionLength_)
- return;
- selectionStart_ = start;
- selectionLength_ = length;
- ValidateSelection();
- verticesDirty_ = true;
- }
- void UIText::ClearSelection()
- {
- selectionStart_ = 0;
-
- if (selectionLength_ != 0)
- {
- selectionLength_ = 0;
- verticesDirty_ = true;
- }
- }
- void UIText::SetSelectionColor(const Color& color)
- {
- if (color == selectionColor_)
- return;
- selectionColor_ = color;
- if (selectionLength_ != 0)
- verticesDirty_ = true;
- }
- void UIText::SetTextEffect(TextEffect textEffect)
- {
- if (textEffect == textEffect_)
- return;
- textEffect_ = textEffect;
- UpdateMaterial();
- verticesDirty_ = true;
- }
- void UIText::SetEffectColor(const Color& effectColor)
- {
- if (effectColor == effectColor_)
- return;
- effectColor_ = effectColor;
- if (textEffect_ != TE_NONE)
- verticesDirty_ = true;
- }
- void UIText::SetFontAttr(const ResourceRef& value)
- {
- ResourceCache* cache = GetSubsystem<ResourceCache>();
- SetFont(cache->GetResource<Font>(value.name_), fontSize_);
- }
- ResourceRef UIText::GetFontAttr() const
- {
- return GetResourceRef(font_, Font::GetTypeStatic());
- }
- void UIText::OnNodeSet(Node* node)
- {
- Drawable2D::OnNodeSet(node);
- if (node)
- {
- uiRect_ = node->GetComponent<UIRect>();
- if (uiRect_)
- SubscribeToEvent(uiRect_, E_UIRECTDIRTIED, HANDLER(UIText, HandleRectDirtied));
- else
- LOGERROR("UIRect must by added first");
- }
- }
- void UIText::OnWorldBoundingBoxUpdate()
- {
- boundingBox_.Clear();
- if (uiRect_)
- {
- const Rect& rect = uiRect_->GetRect();
- boundingBox_.Merge(rect.min_);
- boundingBox_.Merge(rect.max_);
- }
- worldBoundingBox_ = boundingBox_;
- }
- void UIText::UpdateVertices()
- {
- if (!verticesDirty_)
- return;
- vertices_.Clear();
- if (textLineInfo_.Empty())
- return;
- TextEffect textEffect = font_->IsSDFFont() ? TE_NONE : textEffect_;
- const Vector<SharedPtr<Texture2D> >& textures = fontFace_->GetTextures();
- const Rect& rect = uiRect_->GetRect();
- float totalHeight = textLineInfo_.Size() * rowHeight_;
- unsigned color = color_.ToUInt();
- unsigned effectColor = effectColor_.ToUInt();
- for (unsigned i = 0; i < textLineInfo_.Size(); ++i)
- {
- const UITextLineInfo& lineInfo = textLineInfo_[i];
-
- float x;
- switch (textAlignment_)
- {
- case HA_LEFT:
- x = rect.min_.x_;
- break;
- case HA_CENTER:
- x = rect.Center().x_ - lineInfo.lineWidth_ * 0.5f;
- break;
- case HA_RIGHT:
- x = rect.max_.x_ - lineInfo.lineWidth_;
- break;
- }
- float y = rect.Center().y_ + totalHeight * 0.5f - rowHeight_ * i;
- switch (textEffect)
- {
- case TE_NONE:
- UpdateTextLineVertices(lineInfo, x, y, color);
- break;
- case TE_SHADOW:
- UpdateTextLineVertices(lineInfo, x + PIXEL_SIZE, y - PIXEL_SIZE, effectColor);
- UpdateTextLineVertices(lineInfo, x, y, color);
- break;
- case TE_STROKE:
- UpdateTextLineVertices(lineInfo, x - PIXEL_SIZE, y - PIXEL_SIZE, effectColor);
- UpdateTextLineVertices(lineInfo, x , y - PIXEL_SIZE, effectColor);
- UpdateTextLineVertices(lineInfo, x + PIXEL_SIZE, y - PIXEL_SIZE, effectColor);
- UpdateTextLineVertices(lineInfo, x - PIXEL_SIZE, y, effectColor);
- UpdateTextLineVertices(lineInfo, x + PIXEL_SIZE, y, effectColor);
-
- UpdateTextLineVertices(lineInfo, x - PIXEL_SIZE, y + PIXEL_SIZE, effectColor);
- UpdateTextLineVertices(lineInfo, x , y + PIXEL_SIZE, effectColor);
- UpdateTextLineVertices(lineInfo, x + PIXEL_SIZE, y + PIXEL_SIZE, effectColor);
- UpdateTextLineVertices(lineInfo, x, y, color);
- break;
- }
- }
- verticesDirty_ = false;
- }
- void UIText::HandleRectDirtied(StringHash eventType, VariantMap& eventData)
- {
- worldBoundingBoxDirty_ = true;
- verticesDirty_ = true;
- }
- void UIText::UpdateMaterial(bool newFont)
- {
- Material* material = GetCustomMaterial();
- if (!material)
- {
- material = new Material(context_);
- Technique* tech = new Technique(context_);
- Pass* pass = tech->CreatePass(PASS_ALPHA);
- pass->SetVertexShader("Text");
- pass->SetPixelShader("Text");
- pass->SetDepthWrite(false);
- // Text alway use alpha blend mode
- pass->SetBlendMode(BLEND_ALPHA);
- material->SetTechnique(0, tech);
- material->SetCullMode(CULL_NONE);
- SetCustomMaterial(material);
- }
- material->SetTexture(TU_DIFFUSE, texture_);
- if (newFont)
- {
- Technique* tech = material->GetTechnique(0);
- Pass* pass = tech->GetPass(PASS_ALPHA);
- if (font_ && font_->IsSDFFont())
- {
- switch (textEffect_)
- {
- case TE_NONE:
- pass->SetPixelShaderDefines("SIGNED_DISTANCE_FIELD");
- break;
- case TE_SHADOW:
- pass->SetPixelShaderDefines("SIGNED_DISTANCE_FIELD TEXT_EFFECT_SHADOW");
- break;
- case TE_STROKE:
- pass->SetPixelShaderDefines("SIGNED_DISTANCE_FIELD TEXT_EFFECT_STROKE");
- break;
- }
- }
- else
- pass->SetPixelShaderDefines("");
- }
- }
- void UIText::UpdateText()
- {
- textLineInfo_.Clear();
- // Convert to Unicode text
- PODVector<unsigned> unicodeText;
- for (unsigned i = 0; i < text_.Length();)
- unicodeText.Push(text_.NextUTF8Char(i));
- numChars_ = unicodeText.Size();
- if (!fontFace_)
- return;
- rowHeight_ = fontFace_->GetRowHeight() * PIXEL_SIZE;
- UITextLineInfo lineInfo;
- for (unsigned i = 0; i < unicodeText.Size(); ++i)
- {
- unsigned c = unicodeText[i];
- if (c != '\n')
- {
- const FontGlyph* glyph = fontFace_->GetGlyph(c);
- if (glyph)
- lineInfo.Add(glyph, i < unicodeText.Size() - 1 ? fontFace_->GetKerning(c, unicodeText[i + 1]) : 0);
- }
- else
- {
- textLineInfo_.Push(lineInfo);
- lineInfo.Clear();
- }
- }
- if (!lineInfo.fontGlyphs_.Empty())
- textLineInfo_.Push(lineInfo);
- }
- void UIText::ValidateSelection()
- {
- if (numChars_)
- {
- if (selectionStart_ >= numChars_)
- selectionStart_ = numChars_ - 1;
- if (selectionStart_ + selectionLength_ > numChars_)
- selectionLength_ = numChars_ - selectionStart_;
- }
- else
- {
- selectionStart_ = 0;
- selectionLength_ = 0;
- }
- }
- void UIText::UpdateTextLineVertices(const UITextLineInfo& lineInfo, float x, float y, unsigned color)
- {
- const PODVector<float>& advances = lineInfo.glyphAdvances_;
- const PODVector<const FontGlyph*>& fontGlyphs = lineInfo.fontGlyphs_;
- float invTexWidth = 1.0f / (float)texture_->GetWidth();
- float invTexHeight = 1.0f / (float)texture_->GetHeight();
- for (unsigned i = 0; i < fontGlyphs.Size(); ++i)
- {
- const FontGlyph& glyph = *fontGlyphs[i];
- /*
- V1---------V2
- | / |
- | / |
- | / |
- | / |
- | / |
- V0---------V3
- */
- Vertex2D vertex0;
- Vertex2D vertex1;
- Vertex2D vertex2;
- Vertex2D vertex3;
- float left = x + glyph.offsetX_ * PIXEL_SIZE;
- float right = left + glyph.width_ * PIXEL_SIZE;
- float top = y - glyph.offsetY_ * PIXEL_SIZE;
- float bottom = top - glyph.height_ * PIXEL_SIZE;
- float uLeft = glyph.x_ * invTexWidth;
- float uRight = (glyph.x_ + glyph.width_) * invTexWidth;
- float vTop = glyph.y_ * invTexHeight;
- float vBottom = (glyph.y_ + glyph.height_) * invTexHeight;
- vertex0.position_ = Vector3(left, bottom);
- vertex1.position_ = Vector3(left, top);
- vertex2.position_ = Vector3(right, top);
- vertex3.position_ = Vector3(right, bottom);
- vertex0.uv_ = Vector2(uLeft, vBottom);
- vertex1.uv_ = Vector2(uLeft, vTop);
- vertex2.uv_ = Vector2(uRight, vTop);
- vertex3.uv_ = Vector2(uRight, vBottom);
- vertex0.color_ = color;
- vertex1.color_ = color;
- vertex2.color_ = color;
- vertex3.color_ = color;
- vertices_.Push(vertex0);
- vertices_.Push(vertex1);
- vertices_.Push(vertex2);
- vertices_.Push(vertex3);
- x += advances[i];
- }
- }
- }
|