LCOV - code coverage report
Current view: top level - xenolith/renderer/basic2d - XL2dLabel.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 410 486 84.4 %
Date: 2024-05-12 00:16:13 Functions: 64 73 87.7 %

          Line data    Source code
       1             : /**
       2             :  Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       3             : 
       4             :  Permission is hereby granted, free of charge, to any person obtaining a copy
       5             :  of this software and associated documentation files (the "Software"), to deal
       6             :  in the Software without restriction, including without limitation the rights
       7             :  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8             :  copies of the Software, and to permit persons to whom the Software is
       9             :  furnished to do so, subject to the following conditions:
      10             : 
      11             :  The above copyright notice and this permission notice shall be included in
      12             :  all copies or substantial portions of the Software.
      13             : 
      14             :  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15             :  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16             :  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17             :  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18             :  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19             :  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      20             :  THE SOFTWARE.
      21             :  **/
      22             : 
      23             : #include "XL2dLabel.h"
      24             : #include "XL2dFrameContext.h"
      25             : #include "XLFrameInfo.h"
      26             : #include "XLEventListener.h"
      27             : #include "XLDirector.h"
      28             : #include "XLFontExtension.h"
      29             : 
      30             : namespace STAPPLER_VERSIONIZED stappler::xenolith::basic2d {
      31             : 
      32        5860 : Label::Selection::~Selection() { }
      33             : 
      34        2930 : bool Label::Selection::init() {
      35        2930 :         if (!Sprite::init()) {
      36           0 :                 return false;
      37             :         }
      38             : 
      39        2930 :         return true;
      40             : }
      41             : 
      42       13906 : void Label::Selection::clear() {
      43       13906 :         _vertexes.clear();
      44       13906 : }
      45             : 
      46          10 : void Label::Selection::emplaceRect(const Rect &rect) {
      47          20 :         _vertexes.addQuad().setGeometry(
      48          10 :                         Vec4(rect.origin.x, _contentSize.height - rect.origin.y - rect.size.height, 0.0f, 0.0f), rect.size);
      49          10 : }
      50             : 
      51       70056 : void Label::Selection::updateColor() {
      52       70056 :         Sprite::updateColor();
      53       70056 : }
      54             : 
      55          10 : void Label::Selection::updateVertexes() {
      56             :         //_vertexes.clear();
      57          10 : }
      58             : 
      59             : template <typename Interface>
      60        6430 : static size_t Label_getQuadsCount(const font::TextLayoutData<Interface> *format) {
      61        6430 :         size_t ret = 0;
      62             : 
      63        6430 :         const font::RangeLayoutData *targetRange = nullptr;
      64             : 
      65       28011 :         for (auto it = format->begin(); it != format->end(); ++ it) {
      66       21559 :                 if (&(*it.range) != targetRange) {
      67        6588 :                         targetRange = &(*it.range);
      68             :                 }
      69             : 
      70       21559 :                 const auto start = it.start();
      71       21557 :                 auto end = start + it.count();
      72       21562 :                 if (it.line->start + it.line->count == end) {
      73       21447 :                         const font::CharLayoutData &c = format->chars[end - 1];
      74       21448 :                         if (!chars::isspace(c.charID) && c.charID != char16_t(0x0A)) {
      75        6592 :                                 ++ ret;
      76             :                         }
      77       21449 :                         end -= 1;
      78             :                 }
      79             : 
      80      355023 :                 for (auto charIdx = start; charIdx < end; ++ charIdx) {
      81      333442 :                         const font::CharLayoutData &c = format->chars[charIdx];
      82      333425 :                         if (!chars::isspace(c.charID) && c.charID != char16_t(0x0A) && c.charID != char16_t(0x00AD)) {
      83      290380 :                                 ++ ret;
      84             :                         }
      85             :                 }
      86             :         }
      87             : 
      88        6443 :         return ret;
      89             : }
      90             : 
      91      296199 : static void Label_pushColorMap(const font::RangeLayoutData &range, Vector<ColorMask> &cMap) {
      92      296199 :         ColorMask mask = ColorMask::None;
      93      296199 :         if (!range.colorDirty) {
      94      296244 :                 mask |= ColorMask::Color;
      95             :         }
      96      296192 :         if (!range.opacityDirty) {
      97      296259 :                 mask |= ColorMask::A;
      98             :         }
      99      296151 :         cMap.push_back(mask);
     100      296196 : }
     101             : 
     102      296034 : static void Label_writeTextureQuad(float height, const font::Metrics &m, const font::CharLayoutData &c,
     103             :                 const font::CharShape &l, const font::RangeLayoutData &range, const font::LineLayoutData &line, VertexArray::Quad &quad) {
     104      296034 :         switch (range.align) {
     105           0 :         case font::VerticalAlign::Sub:
     106           0 :                 quad.drawChar(m, l, c.pos, height - line.pos + m.descender / 2, range.color, range.decoration, c.face);
     107           0 :                 break;
     108           0 :         case font::VerticalAlign::Super:
     109           0 :                 quad.drawChar(m, l, c.pos, height - line.pos + m.ascender / 2, range.color, range.decoration, c.face);
     110           0 :                 break;
     111      296034 :         default:
     112      296034 :                 quad.drawChar(m, l, c.pos, height - line.pos, range.color, range.decoration, c.face);
     113      296194 :                 break;
     114             :         }
     115      296194 : }
     116             : 
     117             : template <typename Interface>
     118        6430 : static void Label_writeQuads(VertexArray &vertexes, const font::TextLayoutData<Interface> *format, Vector<ColorMask> &colorMap) {
     119        6430 :         auto quadsCount = Label_getQuadsCount(format);
     120        6443 :         colorMap.clear();
     121        6440 :         colorMap.reserve(quadsCount);
     122             : 
     123        6434 :         const font::RangeLayoutData *targetRange = nullptr;
     124        6434 :         font::Metrics metrics;
     125             : 
     126        6434 :         vertexes.clear();
     127             : 
     128       28003 :         for (auto it = format->begin(); it != format->end(); ++ it) {
     129       21561 :                 if (it.count() == 0) {
     130           0 :                         continue;
     131             :                 }
     132             : 
     133       21561 :                 if (&(*it.range) != targetRange) {
     134        6590 :                         targetRange = &(*it.range);
     135        6590 :                         metrics = targetRange->layout->getMetrics();
     136             :                 }
     137             : 
     138       21561 :                 const auto start = it.start();
     139       21561 :                 auto end = start + it.count();
     140             : 
     141      375595 :                 for (auto charIdx = start; charIdx < end; ++ charIdx) {
     142      354233 :                         const font::CharLayoutData &c = format->chars[charIdx];
     143      354147 :                         if (!chars::isspace(c.charID) && c.charID != char16_t(0x0A) && c.charID != char16_t(0x00AD)) {
     144             : 
     145      296251 :                                 uint16_t face = 0;
     146      296251 :                                 auto ch = targetRange->layout->getChar(c.charID, face);
     147             : 
     148      296379 :                                 if (ch.charID == c.charID) {
     149      296433 :                                         auto quad = vertexes.addQuad();
     150      296154 :                                         Label_pushColorMap(*it.range, colorMap);
     151      296088 :                                         Label_writeTextureQuad(format->height, metrics, c, ch, *it.range, *it.line, quad);
     152             :                                 }
     153             :                         }
     154             :                 }
     155             : 
     156       21362 :                 if (it.line->start + it.line->count == end) {
     157       21453 :                         const font::CharLayoutData &c = format->chars[end - 1];
     158       21452 :                         if (c.charID == char16_t(0x00AD)) {
     159           0 :                                 uint16_t face = 0;
     160           0 :                                 auto ch = targetRange->layout->getChar('-', face);
     161             : 
     162           0 :                                 if (ch.charID == '-') {
     163           0 :                                         auto quad = vertexes.addQuad();
     164           0 :                                         Label_pushColorMap(*it.range, colorMap);
     165           0 :                                         Label_writeTextureQuad(format->height, metrics, c, ch, *it.range, *it.line, quad);
     166             :                                 }
     167             :                         }
     168       21453 :                         end -= 1;
     169             :                 }
     170             : 
     171       21362 :                 if (it.count() > 0 && it.range->decoration != font::TextDecoration::None) {
     172          75 :                         auto chstart = it.start();
     173          75 :                         auto chend = it.end();
     174          75 :                         while (chstart < chend && chars::isspace(format->chars[chstart].charID)) {
     175           0 :                                 ++ chstart;
     176             :                         }
     177             : 
     178          75 :                         if (chstart == chend) {
     179           0 :                                 return;
     180             :                         }
     181             : 
     182          75 :                         const font::CharLayoutData &firstChar = format->chars[chstart];
     183          75 :                         const font::CharLayoutData &lastChar = format->chars[chend - 1];
     184             : 
     185          75 :                         auto color = it.range->color;
     186          75 :                         color.a = uint8_t(0.75f * color.a);
     187          75 :                         auto layoutMetrics = it.range->layout->getMetrics();
     188             : 
     189          75 :                         float offset = 0.0f;
     190          75 :                         switch (it.range->decoration) {
     191           0 :                         case font::TextDecoration::None: break;
     192           0 :                         case font::TextDecoration::Overline:
     193           0 :                                 offset = layoutMetrics.height;
     194           0 :                                 break;
     195          35 :                         case font::TextDecoration::LineThrough:
     196          35 :                                 offset = (layoutMetrics.height * 11.0f) / 24.0f;
     197          35 :                                 break;
     198          40 :                         case font::TextDecoration::Underline:
     199          40 :                                 offset = layoutMetrics.height / 8.0f;
     200          40 :                                 break;
     201             :                         }
     202             : 
     203          75 :                         const float width = layoutMetrics.height / 16.0f;
     204          75 :                         const float base = floorf(width);
     205          75 :                         const float frac = width - base;
     206             : 
     207          75 :                         const auto underlineBase = uint16_t(base);
     208          75 :                         const auto underlineX = firstChar.pos;
     209          75 :                         const auto underlineWidth = lastChar.pos + lastChar.advance - firstChar.pos;
     210          75 :                         const auto underlineY = format->height - it.line->pos + offset - underlineBase / 2;
     211          75 :                         const auto underlineHeight = underlineBase;
     212             : 
     213          75 :                         auto quad = vertexes.addQuad();
     214          75 :                         Label_pushColorMap(*it.range, colorMap);
     215          75 :                         quad.drawUnderlineRect(underlineX, underlineY, underlineWidth, underlineHeight, color);
     216          75 :                         if (frac > 0.1) {
     217           0 :                                 color.a *= frac;
     218             : 
     219           0 :                                 auto uquad = vertexes.addQuad();
     220           0 :                                 Label_pushColorMap(*it.range, colorMap);
     221           0 :                                 uquad.drawUnderlineRect(underlineX, underlineY - 1, underlineWidth, 1, color);
     222             :                         }
     223             :                 }
     224             :         }
     225             : }
     226             : 
     227        6436 : void Label::writeQuads(VertexArray &vertexes, const font::TextLayoutData<memory::StandartInterface> *format, Vector<ColorMask> &colorMap) {
     228        6436 :         Label_writeQuads(vertexes, format, colorMap);
     229        6440 : }
     230             : 
     231           0 : void Label::writeQuads(VertexArray &vertexes, const font::TextLayoutData<memory::PoolInterface> *format, Vector<ColorMask> &colorMap) {
     232           0 :         Label_writeQuads(vertexes, format, colorMap);
     233           0 : }
     234             : 
     235        6434 : Rc<LabelResult> Label::writeResult(TextLayout *format, const Color4F &color) {
     236        6434 :         auto result = Rc<LabelResult>::alloc();
     237        6435 :         VertexArray array;
     238        6433 :         array.init(format->getData()->chars.size() * 4, format->getData()->chars.size() * 6);
     239             : 
     240        6433 :         writeQuads(array, format->getData(), result->colorMap);
     241        6441 :         result->data.transform = Mat4::IDENTITY;
     242        6441 :         result->data.data = array.pop();
     243       12881 :         return result;
     244        6440 : }
     245             : 
     246        2399 : Label::~Label() {
     247        1465 :         _format = nullptr;
     248        2399 : }
     249             : 
     250        1385 : bool Label::init() {
     251        1385 :         return init(nullptr);
     252             : }
     253             : 
     254          70 : bool Label::init(StringView str) {
     255          70 :         return init(nullptr, DescriptionStyle(), str, 0.0f, TextAlign::Left);
     256             : }
     257             : 
     258           0 : bool Label::init(StringView str, float w, TextAlign a) {
     259           0 :         return init(nullptr, DescriptionStyle(), str, w, a);
     260             : }
     261             : 
     262        1465 : bool Label::init(font::FontController *source, const DescriptionStyle &style,
     263             :                 StringView str, float width, TextAlign alignment) {
     264        1465 :         if (!Sprite::init()) {
     265           0 :                 return false;
     266             :         }
     267             : 
     268        1465 :         if (!source) {
     269        1455 :                 source = Application::getInstance()->getExtension<font::FontController>();
     270             :         }
     271             : 
     272        1465 :         _source = source;
     273        1465 :         _style = style;
     274        1465 :         setNormalized(true);
     275             : 
     276        1465 :         setColorMode(core::ColorMode::AlphaChannel);
     277        1465 :         setRenderingLevel(RenderingLevel::Surface);
     278             : 
     279        1465 :         auto el = Rc<EventListener>::create();
     280        1465 :         el->onEventWithObject(font::FontController::onFontSourceUpdated, source, std::bind(&Label::onFontSourceUpdated, this));
     281             : 
     282        1465 :         if (_source->isLoaded()) {
     283        1415 :                 setTexture(Rc<Texture>(_source->getTexture()));
     284             :         } else {
     285          50 :                 el->onEventWithObject(font::FontController::onLoaded, source, std::bind(&Label::onFontSourceLoaded, this), true);
     286             :         }
     287             : 
     288        1465 :         _listener = addComponent(el);
     289             : 
     290        1465 :         _selection = addChild(Rc<Selection>::create());
     291        1465 :         _selection->setAnchorPoint(Vec2(0.0f, 0.0f));
     292        1465 :         _selection->setPosition(Vec2(0.0f, 0.0f));
     293        1465 :         _selection->setColor(Color::BlueGrey_500);
     294        1465 :         _selection->setOpacity(OpacityValue(64));
     295        1465 :         _selection->setVisible(false);
     296             : 
     297        1465 :         _marked = addChild(Rc<Selection>::create());
     298        1465 :         _marked->setAnchorPoint(Vec2(0.0f, 0.0f));
     299        1465 :         _marked->setPosition(Vec2(0.0f, 0.0f));
     300        1465 :         _marked->setColor(Color::Green_500);
     301        1465 :         _marked->setOpacity(OpacityValue(64));
     302        1465 :         _marked->setVisible(false);
     303             : 
     304        1465 :         setColor(Color4F(_style.text.color, _style.text.opacity), true);
     305             : 
     306        1465 :         setString(str);
     307        1465 :         setWidth(width);
     308        1465 :         setAlignment(alignment);
     309             : 
     310        1465 :         return true;
     311        1465 : }
     312             : 
     313           0 : bool Label::init(const DescriptionStyle &style, StringView str, float w, TextAlign a) {
     314           0 :         return init(nullptr, style, str, w, a);
     315             : }
     316             : 
     317         340 : void Label::tryUpdateLabel() {
     318         340 :         if (_parent) {
     319         340 :                 updateLabelScale(_parent->getNodeToWorldTransform());
     320             :         }
     321         340 :         if (_labelDirty) {
     322          20 :                 updateLabel();
     323             :         }
     324         340 : }
     325             : 
     326         230 : void Label::setStyle(const DescriptionStyle &style) {
     327         230 :         _style = style;
     328             : 
     329         230 :         setColor(Color4F(_style.text.color, _style.text.opacity), true);
     330             : 
     331         230 :         setLabelDirty();
     332         230 : }
     333             : 
     334         115 : const Label::DescriptionStyle &Label::getStyle() const {
     335         115 :         return _style;
     336             : }
     337             : 
     338        6449 : Rc<LabelDeferredResult> Label::runDeferred(thread::TaskQueue &queue, TextLayout *format, const Color4F &color) {
     339        6449 :         auto result = new std::promise<Rc<LabelResult>>;
     340        6449 :         Rc<LabelDeferredResult> ret = Rc<LabelDeferredResult>::create(result->get_future());
     341        6449 :         queue.perform([queue = Rc<thread::TaskQueue>(&queue), format = Rc<Label::TextLayout>(format), color, ret, result] () mutable {
     342        6436 :                 auto res = Label::writeResult(format, color);
     343        6440 :                 result->set_value(res);
     344             : 
     345        6438 :                 queue->onMainThread([ret = move(ret), res = move(res), result] () mutable {
     346        6449 :                         ret->handleReady(move(res));
     347        6449 :                         delete result;
     348        6449 :                 }, queue);
     349        6448 :         }, ret);
     350        6449 :         return ret;
     351           0 : }
     352             : 
     353        7768 : void Label::applyLayout(TextLayout *layout) {
     354        7768 :         _format = layout;
     355             : 
     356        7768 :         if (_format) {
     357        6783 :                 if (_format->empty()) {
     358           0 :                         setContentSize(Size2(0.0f, getFontHeight() / _labelDensity));
     359             :                 } else {
     360        6783 :                         setContentSize(Size2(_format->getWidth() / _labelDensity, _format->getHeight() / _labelDensity));
     361             :                 }
     362             : 
     363        6783 :                 setSelectionCursor(getSelectionCursor());
     364        6783 :                 setMarkedCursor(getMarkedCursor());
     365             : 
     366        6783 :                 _labelDirty = false;
     367        6783 :                 _vertexColorDirty = false;
     368        6783 :                 _vertexesDirty = true;
     369             :         } else {
     370         985 :                 _vertexesDirty = true;
     371             :         }
     372        7768 : }
     373             : 
     374        7788 : void Label::updateLabel() {
     375        7788 :         if (!_source) {
     376        1005 :                 return;
     377             :         }
     378             : 
     379        7788 :         if (_string16.empty()) {
     380         985 :                 applyLayout(nullptr);
     381         985 :                 setContentSize(Size2(0.0f, getFontHeight() / _labelDensity));
     382         985 :                 return;
     383             :         }
     384             : 
     385        6803 :         auto spec = Rc<font::TextLayout>::alloc(_source, _string16.size(), _compiledStyles.size() + 1);
     386             : 
     387        6803 :         _compiledStyles = compileStyle();
     388        6803 :         _style.text.color = _displayedColor.getColor();
     389        6803 :         _style.text.opacity = _displayedColor.getOpacity();
     390        6803 :         _style.text.whiteSpace = font::WhiteSpace::PreWrap;
     391             : 
     392        6803 :         if (!updateFormatSpec(spec, _compiledStyles, _labelDensity, _adjustValue)) {
     393          20 :                 return;
     394             :         }
     395             : 
     396        6783 :         applyLayout(spec);
     397        6803 : }
     398             : 
     399        3164 : void Label::onContentSizeDirty() {
     400        3164 :         Sprite::onContentSizeDirty();
     401             : 
     402        3164 :         _selection->setContentSize(_contentSize);
     403        3164 :         _marked->setContentSize(_contentSize);
     404        3164 : }
     405             : 
     406        3271 : void Label::onTransformDirty(const Mat4 &parent) {
     407        3271 :         updateLabelScale(parent);
     408        3271 :         Sprite::onTransformDirty(parent);
     409        3271 : }
     410             : 
     411        4873 : void Label::onGlobalTransformDirty(const Mat4 &parent) {
     412        4873 :         if (!_transformDirty) {
     413        1602 :                 updateLabelScale(parent);
     414             :         }
     415             : 
     416        4873 :         Sprite::onGlobalTransformDirty(parent);
     417        4873 : }
     418             : 
     419      131127 : void Label::updateColor() {
     420      131127 :         if (_format) {
     421      183024 :                 for (auto &it : _format->getData()->ranges) {
     422       92027 :                         if (!it.colorDirty) {
     423       92027 :                                 it.color.r = uint8_t(_displayedColor.r * 255.0f);
     424       92027 :                                 it.color.g = uint8_t(_displayedColor.g * 255.0f);
     425       92027 :                                 it.color.b = uint8_t(_displayedColor.b * 255.0f);
     426             :                         }
     427       92027 :                         if (!it.opacityDirty) {
     428       92027 :                                 it.color.a = uint8_t(_displayedColor.a * 255.0f);
     429             :                         }
     430             :                 }
     431             :         }
     432      131127 :         _vertexColorDirty = true;
     433      131127 : }
     434             : 
     435       23795 : void Label::updateVertexesColor() {
     436       23795 :         if (_deferredResult) {
     437       20699 :                 _deferredResult->updateColor(_displayedColor);
     438             :         } else {
     439        3096 :                 if (!_colorMap.empty()) {
     440           0 :                         _vertexes.updateColorQuads(_displayedColor, _colorMap);
     441             :                 }
     442             :         }
     443       23795 : }
     444             : 
     445           0 : void Label::updateQuadsForeground(font::FontController *controller, TextLayout *format, Vector<ColorMask> &colorMap) {
     446           0 :         writeQuads(_vertexes, format->getData(), colorMap);
     447           0 : }
     448             : 
     449       44873 : bool Label::checkVertexDirty() const {
     450       44873 :         return _vertexesDirty || _labelDirty;
     451             : }
     452             : 
     453       89787 : NodeFlags Label::processParentFlags(FrameInfo &info, NodeFlags parentFlags) {
     454       89787 :         if (_labelDirty) {
     455        6940 :                 updateLabel();
     456             :         }
     457             : 
     458       89787 :         return Sprite::processParentFlags(info, parentFlags);
     459             : }
     460             : 
     461       44873 : void Label::pushCommands(FrameInfo &frame, NodeFlags flags) {
     462       44873 :         if (_deferred) {
     463       44873 :                 if (!_deferredResult || (_deferredResult->isReady() && _deferredResult->getResult()->data.empty())) {
     464        7240 :                         return;
     465             :                 }
     466             : 
     467       37633 :                 FrameContextHandle2d *handle = static_cast<FrameContextHandle2d *>(frame.currentContext);
     468             : 
     469      112899 :                 handle->commands->pushDeferredVertexResult(_deferredResult, frame.viewProjectionStack.back(), frame.modelTransformStack.back(),
     470       75266 :                                 _normalized, frame.zPath, _materialId, handle->getCurrentState(), _realRenderingLevel, frame.depthStack.back(), _commandFlags);
     471             :         } else {
     472           0 :                 Sprite::pushCommands(frame, flags);
     473             :         }
     474             : }
     475             : 
     476        5213 : void Label::updateLabelScale(const Mat4 &parent) {
     477        5213 :         Vec3 scale;
     478        5213 :         parent.decompose(&scale, nullptr, nullptr);
     479             : 
     480        5213 :         if (_scale.x != 1.f) { scale.x *= _scale.x; }
     481        5213 :         if (_scale.y != 1.f) { scale.y *= _scale.y; }
     482        5213 :         if (_scale.z != 1.f) { scale.z *= _scale.z; }
     483             : 
     484        5213 :         auto density = std::min(std::min(scale.x, scale.y), scale.z);
     485        5213 :         if (density != _labelDensity) {
     486           0 :                 _labelDensity = density;
     487           0 :                 setLabelDirty();
     488             :         }
     489             : 
     490        5213 :         if (_labelDirty) {
     491         584 :                 updateLabel();
     492             :         }
     493        5213 : }
     494             : 
     495         166 : void Label::setAdjustValue(uint8_t val) {
     496         166 :         if (_adjustValue != val) {
     497         142 :                 _adjustValue = val;
     498         142 :                 setLabelDirty();
     499             :         }
     500         166 : }
     501          12 : uint8_t Label::getAdjustValue() const {
     502          12 :         return _adjustValue;
     503             : }
     504             : 
     505          12 : bool Label::isOverflow() const {
     506          12 :         if (_format) {
     507          12 :                 return _format->isOverflow();
     508             :         }
     509           0 :         return false;
     510             : }
     511             : 
     512          12 : size_t Label::getCharsCount() const {
     513          12 :         return _format?_format->getData()->chars.size():0;
     514             : }
     515          12 : size_t Label::getLinesCount() const {
     516          12 :         return _format?_format->getData()->lines.size():0;
     517             : }
     518          24 : Label::LineLayout Label::getLine(uint32_t num) const {
     519          24 :         if (_format) {
     520          24 :                 if (num < _format->getData()->lines.size()) {
     521          12 :                         return _format->getData()->lines[num];
     522             :                 }
     523             :         }
     524          12 :         return LineLayout();
     525             : }
     526             : 
     527         995 : uint16_t Label::getFontHeight() const {
     528         995 :         auto l = _source->getLayout(_style.font);
     529         995 :         if (l.get()) {
     530         995 :                 return l->getFontHeight();
     531             :         }
     532           0 :         return 0;
     533         995 : }
     534             : 
     535        6655 : void Label::updateVertexes() {
     536        6655 :         if (!_source) {
     537           0 :                 return;
     538             :         }
     539             : 
     540        6655 :         if (_labelDirty) {
     541         244 :                 updateLabel();
     542             :         }
     543             : 
     544        6655 :         if (!_format || _format->getData()->chars.size() == 0 || _string16.empty()) {
     545         206 :                 _vertexes.clear();
     546         206 :                 _labelDirty = false;
     547         206 :                 _deferredResult = nullptr;
     548         206 :                 return;
     549             :         }
     550             : 
     551       13056 :         for (auto &it : _format->getData()->ranges) {
     552        6607 :                 auto dep = _source->addTextureChars(it.layout, SpanView<font::CharLayoutData>(_format->getData()->chars, it.start, it.count));
     553        6607 :                 if (dep) {
     554         962 :                         emplace_ordered(_pendingDependencies, move(dep));
     555             :                 }
     556        6607 :         }
     557             : 
     558        6449 :         if (_deferred) {
     559        6449 :                 _deferredResult = runDeferred(*_director->getApplication()->getQueue(), _format, _displayedColor);
     560        6449 :                 _vertexes.clear();
     561        6449 :                 _vertexColorDirty = false;
     562             :         } else {
     563           0 :                 _deferredResult = nullptr;
     564           0 :                 updateQuadsForeground(_source, _format, _colorMap);
     565           0 :                 _vertexColorDirty = true;
     566             :         }
     567             : }
     568             : 
     569           0 : void Label::onFontSourceUpdated() {
     570           0 :         setLabelDirty();
     571           0 :         _vertexesDirty = true;
     572           0 : }
     573             : 
     574          50 : void Label::onFontSourceLoaded() {
     575          50 :         if (_source) {
     576          50 :                 setTexture(Rc<Texture>(_source->getTexture()));
     577          50 :                 _vertexesDirty = true;
     578          50 :                 setLabelDirty();
     579             :         }
     580          50 : }
     581             : 
     582           0 : void Label::onLayoutUpdated() {
     583           0 :         _labelDirty = false;
     584           0 : }
     585             : 
     586         250 : Vec2 Label::getCursorPosition(uint32_t charIndex, bool front) const {
     587         250 :         if (_format) {
     588         230 :                 auto d = _format->getData();
     589         230 :                 if (charIndex < d->chars.size()) {
     590          50 :                         auto &c = d->chars[charIndex];
     591          50 :                         auto line = _format->getLine(charIndex);
     592          50 :                         if (line) {
     593          50 :                                 return Vec2( (front ? c.pos : c.pos + c.advance) / _labelDensity, _contentSize.height - line->pos / _labelDensity);
     594             :                         }
     595         180 :                 } else if (charIndex >= d->chars.size() && charIndex != 0) {
     596         180 :                         auto &c = d->chars.back();
     597         180 :                         auto &l = d->lines.back();
     598         180 :                         if (c.charID == char16_t(0x0A)) {
     599           0 :                                 return getCursorOrigin();
     600             :                         } else {
     601         180 :                                 return Vec2( (c.pos + c.advance) / _labelDensity, _contentSize.height - l.pos / _labelDensity);
     602             :                         }
     603             :                 }
     604             :         }
     605             : 
     606          20 :         return Vec2::ZERO;
     607             : }
     608             : 
     609          12 : Vec2 Label::getCursorOrigin() const {
     610          12 :         switch (_alignment) {
     611          12 :         case TextAlign::Left:
     612             :         case TextAlign::Justify:
     613          12 :                 return Vec2( 0.0f / _labelDensity, _contentSize.height - _format->getHeight() / _labelDensity);
     614             :                 break;
     615           0 :         case TextAlign::Center:
     616           0 :                 return Vec2( _contentSize.width * 0.5f / _labelDensity, _contentSize.height - _format->getHeight() / _labelDensity);
     617             :                 break;
     618           0 :         case TextAlign::Right:
     619           0 :                 return Vec2( _contentSize.width / _labelDensity, _contentSize.height - _format->getHeight() / _labelDensity);
     620             :                 break;
     621             :         }
     622           0 :         return Vec2::ZERO;
     623             : }
     624             : 
     625          12 : Pair<uint32_t, bool> Label::getCharIndex(const Vec2 &pos, font::CharSelectMode mode) const {
     626          12 :         if (!_format) {
     627           0 :                 return pair(0, false);
     628             :         }
     629             : 
     630          12 :         auto ret = _format->getChar(pos.x * _labelDensity, _format->getHeight() - pos.y * _labelDensity, mode);
     631          12 :         if (ret.first == maxOf<uint32_t>()) {
     632           0 :                 return pair(maxOf<uint32_t>(), false);
     633          12 :         } else if (ret.second == font::CharSelectMode::Prefix) {
     634           0 :                 return pair(ret.first, false);
     635             :         } else {
     636          12 :                 return pair(ret.first, true);
     637             :         }
     638             : }
     639             : 
     640          12 : core::TextCursor Label::selectWord(uint32_t chIdx) const {
     641          12 :         auto ret = _format->selectWord(chIdx);
     642          12 :         return core::TextCursor(ret.first, ret.second);
     643             : }
     644             : 
     645          12 : float Label::getMaxLineX() const {
     646          12 :         if (_format) {
     647          12 :                 return _format->getMaxAdvance() / _labelDensity;
     648             :         }
     649           0 :         return 0.0f;
     650             : }
     651             : 
     652           0 : void Label::setDeferred(bool val) {
     653           0 :         if (val != _deferred) {
     654           0 :                 _deferred = val;
     655           0 :                 _vertexesDirty = true;
     656             :         }
     657           0 : }
     658             : 
     659        7123 : void Label::setSelectionCursor(core::TextCursor c) {
     660        7123 :         _selection->clear();
     661        7123 :         _selection->setVisible(c != core::TextCursor::InvalidCursor && c.length > 0);
     662        7123 :         if (_format && c != core::TextCursor::InvalidCursor && c.length > 0) {
     663          10 :                 auto rects = _format->getLabelRects(c.start, c.start + c.length - 1, _labelDensity);
     664          20 :                 for (auto &rect: rects) {
     665          10 :                         _selection->emplaceRect(rect);
     666             :                 }
     667          10 :                 _selection->updateColor();
     668          10 :         }
     669        7123 :         _selection->setTextCursor(c);
     670        7123 : }
     671             : 
     672        6783 : core::TextCursor Label::getSelectionCursor() const {
     673        6783 :         return _selection->getTextCursor();
     674             : }
     675             : 
     676        5779 : void Label::setSelectionColor(const Color4F &c) {
     677        5779 :         _selection->setColor(c,  false);
     678        5779 : }
     679             : 
     680          12 : Color4F Label::getSelectionColor() const {
     681          12 :         return _selection->getColor();
     682             : }
     683             : 
     684        6783 : void Label::setMarkedCursor(core::TextCursor c) {
     685        6783 :         _marked->clear();
     686        6783 :         _marked->setVisible(c != core::TextCursor::InvalidCursor && c.length > 0);
     687        6783 :         if (c != core::TextCursor::InvalidCursor && c.length > 0) {
     688           0 :                 auto rects = _format->getLabelRects(c.start, c.start + c.length, _labelDensity);
     689           0 :                 for (auto &rect: rects) {
     690           0 :                         _marked->emplaceRect(rect);
     691             :                 }
     692           0 :                 _marked->updateColor();
     693           0 :         }
     694        6783 :         _marked->setTextCursor(c);
     695        6783 : }
     696             : 
     697        6783 : core::TextCursor Label::getMarkedCursor() const {
     698        6783 :         return _marked->getTextCursor();
     699             : }
     700             : 
     701           6 : void Label::setMarkedColor(const Color4F &c) {
     702           6 :         _marked->setColor(c,  false);
     703           6 : }
     704             : 
     705           6 : Color4F Label::getMarkedColor() const {
     706           6 :         return _marked->getColor();
     707             : }
     708             : 
     709             : 
     710       12898 : LabelDeferredResult::~LabelDeferredResult() {
     711        6449 :         if (_future) {
     712           0 :                 delete _future;
     713           0 :                 _future = nullptr;
     714             :         }
     715       12898 : }
     716             : 
     717        6449 : bool LabelDeferredResult::init(std::future<Rc<LabelResult>> &&future) {
     718        6449 :         _future = new std::future<Rc<LabelResult>>(move(future));
     719        6449 :         return true;
     720             : }
     721             : 
     722       37623 : SpanView<TransformVertexData> LabelDeferredResult::getData() {
     723       37623 :         std::unique_lock<Mutex> lock(_mutex);
     724       37623 :         if (_future) {
     725        1229 :                 _result = _future->get();
     726        1229 :                 delete _future;
     727        1229 :                 _future = nullptr;
     728        1229 :                 DeferredVertexResult::handleReady();
     729             :         }
     730       75246 :         return makeSpanView(&_result->data, 1);
     731       37623 : }
     732             : 
     733        6449 : void LabelDeferredResult::handleReady(Rc<LabelResult> &&res) {
     734        6449 :         std::unique_lock<Mutex> lock(_mutex);
     735        6449 :         if (_future) {
     736        5220 :                 delete _future;
     737        5220 :                 _future = nullptr;
     738             :         }
     739        6449 :         _result = move(res);
     740        6449 :         DeferredVertexResult::handleReady();
     741        6449 : }
     742             : 
     743       20699 : void LabelDeferredResult::updateColor(const Color4F &color) {
     744       20699 :         getResult(); // ensure rendering was complete
     745             : 
     746       20699 :         std::unique_lock<Mutex> lock(_mutex);
     747       20699 :         if (_result) {
     748       20699 :                 VertexArray arr;
     749       20699 :                 arr.init(_result->data.data);
     750       20699 :                 arr.updateColorQuads(color, _result->colorMap);
     751       20699 :                 _result->data.data = arr.pop();
     752       20699 :         }
     753       20699 : }
     754             : 
     755       51883 : Rc<VertexData> LabelDeferredResult::getResult() const {
     756       51883 :         std::unique_lock<Mutex> lock(_mutex);
     757      103766 :         return _result->data.data;
     758       51883 : }
     759             : 
     760             : }

Generated by: LCOV version 1.14