CCLabelLayout.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  1. /****************************************************************************
  2. Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
  3. http://www.cocos.com
  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. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. THE SOFTWARE.
  19. ****************************************************************************/
  20. #include "2d/CCLabelLayout.h"
  21. #include "2d/CCTTFLabelAtlasCache.h"
  22. #include "base/ccUTF8.h"
  23. #include "MiddlewareMacro.h"
  24. #include "renderer/renderer/Pass.h"
  25. #include "renderer/renderer/Effect.h"
  26. #include "renderer/renderer/Technique.h"
  27. #include "renderer/scene/assembler/CustomAssembler.hpp"
  28. #include "cocos/editor-support/MeshBuffer.h"
  29. #include "cocos/editor-support/IOBuffer.h"
  30. #include "cocos/editor-support/middleware-adapter.h"
  31. #include "cocos/editor-support/MiddlewareManager.h"
  32. #include "renderer/gfx/DeviceGraphics.h"
  33. #include "renderer/gfx/Texture2D.h"
  34. #include <cassert>
  35. #include <algorithm>
  36. #include <fstream>
  37. #include <unordered_map>
  38. #include <chrono>
  39. #include "base/ccConfig.h"
  40. #if CC_ENABLE_TTF_LABEL_RENDERER
  41. namespace {
  42. const std::string textureKey = "texture";
  43. const std::string shadowColor = "shadowColor";
  44. const std::string outlineSizeKey = "outlineSize";
  45. const std::string outlineColorKey= "outlineColor";
  46. }
  47. using namespace cocos2d::renderer;
  48. using namespace cocos2d::middleware;
  49. namespace cocos2d {
  50. namespace {
  51. void recalculateUV(const Rect &oldRect, const Rect &oldUV, const Rect &newRect, Rect &newUV)
  52. {
  53. auto offsetOrigin = newRect.origin - oldRect.origin;
  54. float uvWidth = newRect.size.width / oldRect.size.width * oldUV.size.width;
  55. float uvHeight = newRect.size.height / oldRect.size.height * oldUV.size.height;
  56. float uvOriginX = offsetOrigin.x / oldRect.size.width * oldUV.size.width + oldUV.origin.x;
  57. float uvOriginY = (oldUV.size.height - uvHeight - offsetOrigin.y / oldRect.size.height * oldUV.size.height) + oldUV.origin.y;
  58. newUV.setRect(uvOriginX, uvOriginY, uvWidth, uvHeight);
  59. }
  60. inline void find_2nd_3rd(float min1, float max1, float min2, float max2, float &second, float &third)
  61. {
  62. assert(max1 >= min1 && max2 >= min2);
  63. if (max1 < max2)
  64. {
  65. second = min1 < min2 ? min2 : min1;
  66. third = max1;
  67. }
  68. else
  69. {
  70. second = min2 < min1 ? min1 : min2;
  71. third = max2;
  72. }
  73. }
  74. void rectUnion(const Rect &a, const Rect &b, Rect &out)
  75. {
  76. float xMax, yMax;
  77. find_2nd_3rd(a.getMinX(), a.getMaxX(), b.getMinX(), b.getMaxX(), out.origin.x, xMax);
  78. find_2nd_3rd(a.getMinY(), a.getMaxY(), b.getMinY(), b.getMaxY(), out.origin.y, yMax);
  79. out.size.setSize(xMax - out.origin.x, yMax - out.origin.y);
  80. }
  81. void subRect(const Rect &rect, const Rect &uv, float offsetX, float width, Rect &out1, Rect &out2)
  82. {
  83. out1.origin.set(offsetX + rect.origin.x, rect.origin.y);
  84. out1.size.setSize(width, rect.size.height);
  85. out2.origin.set(uv.origin.x + uv.size.width * offsetX / rect.size.width, uv.origin.y);
  86. out2.size.setSize(uv.size.width * width / rect.size.width, uv.size.height);
  87. }
  88. void splitRectIntoThreeParts(int texId, const Rect &defRect, const Rect &targetRect, const Rect &uv, cocos2d::TextRowSpace &space)
  89. {
  90. constexpr float LEFT = 0.3f;
  91. constexpr float MIDDLE = 0.4f;
  92. constexpr float RIGHT = 0.3f;
  93. Rect out1, out2;
  94. float cursorX = 0.0f;
  95. //part left
  96. subRect(targetRect, uv, 0.0f, targetRect.size.width * LEFT, out1, out2);
  97. cursorX = defRect.size.width *LEFT;
  98. out1.size.width = cursorX;
  99. space.fillRect(texId, out1, out2);
  100. //part middle
  101. subRect(targetRect, uv, targetRect.size.width * LEFT, targetRect.size.width * MIDDLE, out1, out2);
  102. out1.origin.x = targetRect.origin.x + cursorX;
  103. out1.size.width = targetRect.size.width - defRect.size.width * (LEFT + RIGHT);
  104. cursorX = targetRect.size.width - defRect.size.width * RIGHT;
  105. space.fillRect(texId, out1, out2);
  106. //part right
  107. subRect(targetRect, uv, targetRect.size.width * (LEFT + MIDDLE) , targetRect.size.width *RIGHT, out1, out2);
  108. out1.origin.x = targetRect.origin.x + cursorX;
  109. out1.size.width = defRect.size.width * RIGHT;
  110. space.fillRect(texId, out1, out2);
  111. }
  112. }
  113. class TextRenderGroupItem {
  114. public:
  115. enum DirtyFlag {
  116. VERTEX_DIRTY = 1,
  117. INDEXES_DIRTY = 2
  118. };
  119. TextRenderGroupItem(renderer::Texture *);
  120. virtual ~TextRenderGroupItem();
  121. void reset();
  122. void addRect(const Rect &vert, const Rect &uv, const Color4B &color, bool italics);
  123. void upload();
  124. inline int getRectSize() const { return _rectSize; }
  125. middleware::MeshBuffer* getBuffer() { return _buffer; }
  126. private:
  127. void addIndexes();
  128. middleware::MeshBuffer * _buffer = nullptr;
  129. renderer::Texture *_texture = nullptr;
  130. int _rectSize = 0;
  131. int _indexSize = 0;
  132. int _dirtyFlags = -1;
  133. };
  134. class TextRenderGroup {
  135. public:
  136. void reset();
  137. void addRect(renderer::Texture*, const Rect &vert, const Rect &uv, const Color4B &color, bool italics);
  138. int fill(CustomAssembler *assembler, int, LabelLayout *layout, EffectVariant *);
  139. private:
  140. std::unordered_map<renderer::Texture*, std::shared_ptr<TextRenderGroupItem>> _buffers;
  141. };
  142. TextRenderGroupItem::TextRenderGroupItem(renderer::Texture* tex)
  143. {
  144. _texture = tex;
  145. _buffer = new middleware::MeshBuffer(VF_XYUVC);
  146. }
  147. TextRenderGroupItem::~TextRenderGroupItem()
  148. {
  149. delete _buffer;
  150. }
  151. void TextRenderGroupItem::reset() {
  152. _buffer->reset();
  153. _rectSize = 0;
  154. _dirtyFlags = -1;
  155. }
  156. void TextRenderGroupItem::addRect(const Rect &rect, const Rect &uv, const Color4B &color, bool italics)
  157. {
  158. middleware::IOBuffer &vertexBuffer = _buffer->getVB();
  159. const int minSize = sizeof(V2F_T2F_C4B) * std::max(16, 4 * _rectSize);
  160. if (vertexBuffer.getCapacity() < minSize)
  161. {
  162. vertexBuffer.checkSpace(minSize << 2, true);
  163. }
  164. V2F_T2F_C4B *verts = ((V2F_T2F_C4B*)vertexBuffer.getBuffer()) + 4 * _rectSize;
  165. float height = rect.size.height;
  166. float offsetX = 0.0f;
  167. const float factor = 0.21255f;
  168. if (italics) {
  169. offsetX = height * factor;
  170. }
  171. verts[0].vertex = { rect.getMinX() + offsetX, rect.getMaxY() };
  172. verts[1].vertex = { rect.getMaxX() + offsetX, rect.getMaxY() };
  173. verts[2].vertex = { rect.getMinX() - offsetX, rect.getMinY() };
  174. verts[3].vertex = { rect.getMaxX() - offsetX, rect.getMinY() };
  175. verts[0].texCoord = { uv.getMinX(), uv.getMinY() };
  176. verts[1].texCoord = { uv.getMaxX(), uv.getMinY() };
  177. verts[2].texCoord = { uv.getMinX(), uv.getMaxY() };
  178. verts[3].texCoord = { uv.getMaxX(), uv.getMaxY() };
  179. verts[0].color = color;
  180. verts[1].color = color;
  181. verts[2].color = color;
  182. verts[3].color = color;
  183. _rectSize += 1;
  184. _dirtyFlags |= DirtyFlag::VERTEX_DIRTY;
  185. }
  186. void TextRenderGroupItem::addIndexes()
  187. {
  188. middleware::IOBuffer &indexBuffer = _buffer->getIB();
  189. int indexSize = sizeof(uint16_t) * _rectSize * 6;
  190. indexBuffer.checkSpace(indexSize, true);
  191. uint16_t *indices = (uint16_t*)indexBuffer.getBuffer();
  192. for (int i = _indexSize; i < _rectSize; i += 1)
  193. {
  194. indices[i * 6 + 0] = 0 + 4 * i;
  195. indices[i * 6 + 1] = 1 + 4 * i;
  196. indices[i * 6 + 2] = 2 + 4 * i;
  197. indices[i * 6 + 3] = 1 + 4 * i;
  198. indices[i * 6 + 4] = 3 + 4 * i;
  199. indices[i * 6 + 5] = 2 + 4 * i;
  200. }
  201. if (_indexSize < _rectSize)
  202. {
  203. _indexSize = _rectSize;
  204. _dirtyFlags |= DirtyFlag::INDEXES_DIRTY;
  205. }
  206. }
  207. void TextRenderGroupItem::upload()
  208. {
  209. addIndexes();
  210. middleware::IOBuffer &vertexBuffer = _buffer->getVB();
  211. middleware::IOBuffer &indexBuffer = _buffer->getIB();
  212. vertexBuffer.move(_rectSize * 4 * sizeof(V2F_T2F_C4B));
  213. indexBuffer.move(_rectSize * 6 * sizeof(uint16_t));
  214. if (_dirtyFlags | DirtyFlag::INDEXES_DIRTY)
  215. {
  216. _buffer->uploadIB();
  217. }
  218. if (_dirtyFlags | DirtyFlag::VERTEX_DIRTY)
  219. {
  220. _buffer->uploadVB();
  221. }
  222. _dirtyFlags = 0;
  223. }
  224. void TextRenderGroup::addRect(renderer::Texture* tex, const Rect &vert, const Rect &uv, const Color4B &color, bool italics)
  225. {
  226. auto &itemPtr = _buffers[tex];
  227. if (!itemPtr) {
  228. itemPtr = std::make_shared<TextRenderGroupItem>(tex);
  229. }
  230. itemPtr->addRect(vert, uv, color, italics);
  231. }
  232. void TextRenderGroup::reset()
  233. {
  234. for (auto &it : _buffers)
  235. {
  236. it.second->reset();
  237. }
  238. _buffers.clear();
  239. }
  240. int TextRenderGroup::fill(CustomAssembler *assembler, int startIndex, LabelLayout *layout, EffectVariant *templateEffect)
  241. {
  242. int groupIndex = startIndex;
  243. for (auto &it : _buffers)
  244. {
  245. auto &item = it.second;
  246. if (item->getRectSize() > 0)
  247. {
  248. item->upload();
  249. assembler->updateIABuffer(groupIndex, item->getBuffer()->getGLVB(), item->getBuffer()->getGLIB());
  250. assembler->updateIARange(groupIndex, 0, item->getRectSize() * 6);
  251. auto *effect = assembler->getEffect(groupIndex);
  252. if (!effect && templateEffect) {
  253. effect = new cocos2d::renderer::EffectVariant();
  254. effect->autorelease();
  255. effect->copy(templateEffect);
  256. assembler->updateEffect(groupIndex, effect);
  257. }
  258. if(effect->getPasses().at(0)->getDefinesHash() != templateEffect->getPasses().at(0)->getDefinesHash()){
  259. effect->copy(templateEffect);
  260. }
  261. if (effect) {
  262. effect->setProperty(textureKey, it.first);
  263. }
  264. groupIndex += 1;
  265. }
  266. }
  267. return groupIndex;
  268. }
  269. struct TextSpaceArray {
  270. void addSpace(TextRowSpace& space)
  271. {
  272. if (space.size() > 0)
  273. {
  274. _maxWidth = std::max(_maxWidth, space.getWidth());
  275. }
  276. _data.emplace_back(std::move(space));
  277. }
  278. float _maxWidth = FLT_MIN;
  279. std::vector<TextRowSpace> _data;
  280. };
  281. TextRowSpace::GlyphBlock & TextRowSpace::operator[](size_t i)
  282. {
  283. return _data[i];
  284. }
  285. TextRowSpace::GlyphBlock & TextRowSpace::appendBlock()
  286. {
  287. _data.resize(_data.size() + 1);
  288. return _data[_data.size() - 1];
  289. }
  290. TextRowSpace::TextRowSpace(TextRowSpace &&other)
  291. {
  292. _left = other._left;
  293. _bottom = other._bottom;
  294. _right = other._right;
  295. _top = other._top;
  296. _x = other._x;
  297. _y = other._y;
  298. _data = std::move(other._data);
  299. _ignored = other._ignored;
  300. other.reset();
  301. }
  302. void TextRowSpace::reset()
  303. {
  304. _left = FLT_MAX;
  305. _bottom = FLT_MAX;
  306. _right = FLT_MIN;
  307. _top = FLT_MIN;
  308. _x = 0.0f;
  309. _y = 0.0f;
  310. _data.clear();
  311. _ignored = false;
  312. }
  313. void TextRowSpace::fillRect(int texId, Rect &rect, Rect &uv)
  314. {
  315. GlyphBlock &block = appendBlock();
  316. _left = std::min(_left, rect.getMinX());
  317. _right = std::max(_right, rect.getMaxX());
  318. _bottom = std::min(_bottom, rect.getMinY());
  319. _top = std::max(_top, rect.getMaxY());
  320. block.area = rect;
  321. block.uv = uv;
  322. block.ignored = false;
  323. block.texId = texId;
  324. }
  325. void TextRowSpace::translate(float x, float y)
  326. {
  327. _x += x;
  328. _y += y;
  329. }
  330. Vec2 TextRowSpace::center() const
  331. {
  332. Vec2 ret((_left + _right) / 2.0f, (_bottom + _top) / 2.0f);
  333. return ret;
  334. }
  335. float TextRowSpace::getExtentedWidth(float left, float right) const
  336. {
  337. if (_data.size() == 0)
  338. {
  339. return right - left;
  340. }
  341. return std::max(_right, right) - std::min(_left, left);
  342. }
  343. void TextRowSpace::clip(const Rect &rect)
  344. {
  345. Rect contentRect(_x + _left, _y + _bottom, _right - _left, _top - _bottom);
  346. if (!rect.intersectsRect(contentRect)) {
  347. _ignored = true;
  348. return;
  349. }
  350. const auto size = _data.size();
  351. const auto offset = getOffset();
  352. Rect letter;
  353. Rect unionRect;
  354. for (int i = 0; i < size; i++)
  355. {
  356. letter.size = _data[i].area.size;
  357. letter.origin = offset + _data[i].area.origin;
  358. if (!rect.intersectsRect(letter))
  359. {
  360. _data[i].ignored = true;
  361. }
  362. else if (rect.containsPoint(letter.origin) && rect.containsPoint(Vec2(letter.getMaxX(), letter.getMaxY())))
  363. {
  364. _data[i].area.origin = letter.origin;
  365. }
  366. else
  367. {
  368. //Rect unionRect = letter.unionWithRect(rect); // incorrect result
  369. rectUnion(letter, rect, unionRect);
  370. _data[i].area = unionRect;
  371. recalculateUV(letter, _data[i].uv, unionRect, _data[i].uv);
  372. }
  373. }
  374. //ignore translate
  375. _x = 0.0f;
  376. _y = 0.0f;
  377. }
  378. Rect TextRowSpace::asRect() const {
  379. return Rect(_left + _x, _bottom + _y, _right - _left, _top - _bottom);
  380. }
  381. bool LabelLayout::init(const std::string& font, const std::string& text, float fontSize, float retinaFontSize, LabelLayoutInfo *info)
  382. {
  383. _inited = true;
  384. _layoutInfo = info;
  385. _retinaFontSize = std::max(fontSize, retinaFontSize);
  386. _fontAtlas = TTFLabelAtlasCache::getInstance()->load(font, _retinaFontSize, info);
  387. if(!_fontAtlas) {
  388. return false;
  389. }
  390. _fontScale = fontSize / _fontAtlas->getFontSize();
  391. _groups = std::make_shared<TextRenderGroup>();
  392. if (info->shadowBlur >= 0)
  393. {
  394. _shadowGroups = std::make_shared<TextRenderGroup>();
  395. }
  396. _string = text;
  397. _font = font;
  398. _fontSize = fontSize;
  399. cocos2d::StringUtils::UTF8ToUTF32(text.c_str(), _u32string);
  400. _textSpace.clear();
  401. updateContent();
  402. return true;
  403. }
  404. LabelLayout::~LabelLayout()
  405. {
  406. }
  407. void LabelLayout::setString(const std::string &txt, bool forceUpdate)
  408. {
  409. if (_string != txt) {
  410. _string = txt;
  411. cocos2d::StringUtils::UTF8ToUTF32(txt.c_str(), _u32string);
  412. updateContent();
  413. }
  414. else if (forceUpdate)
  415. {
  416. updateContent();
  417. }
  418. }
  419. bool LabelLayout::updateContent()
  420. {
  421. if(!_fontAtlas)
  422. {
  423. return false;
  424. }
  425. auto *atlas = _fontAtlas->getFontAtlas();
  426. auto *ttf = _fontAtlas->getTTF();
  427. FontLetterDefinition* letterDef = nullptr;
  428. Rect row;
  429. const float LineHeight = _layoutInfo->lineHeight;
  430. const float SpaceX = _layoutInfo->spaceX;
  431. const LabelOverflow OverFlow = _layoutInfo->overflow;
  432. const bool ClampAndWrap = _layoutInfo->wrap && OverFlow == LabelOverflow::CLAMP;
  433. const bool ResizeHeight = OverFlow == LabelOverflow::RESIZE_HEIGHT;
  434. const int ContentWidth = _layoutInfo->width;
  435. const int ContentHeight = _layoutInfo->height;
  436. const LabelAlignmentH HAlign = _layoutInfo->halign;
  437. const LabelAlignmentV VAlign = _layoutInfo->valign;
  438. const float AnchorX = _layoutInfo->anchorX;
  439. const float AnchorY = _layoutInfo->anchorY;
  440. const bool Underline = _layoutInfo->underline;
  441. //LabelCursor cursor;
  442. float cursorX = 0;
  443. TextSpaceArray textSpaces;
  444. TextRowSpace rowSpace;
  445. Rect letterRect;
  446. std::unique_ptr<std::vector<int> > kerning = nullptr;
  447. if (_enableKerning) {
  448. kerning = ttf->getHorizontalKerningForUTF32Text(_u32string);
  449. }
  450. atlas->prepareLetters(_u32string, ttf);
  451. for (int i = 0; i < _u32string.size(); i++)
  452. {
  453. auto ch = _u32string[i];
  454. if (ch == u'\r')
  455. {
  456. cursorX = 0;
  457. continue;
  458. }
  459. if (ch == u'\n')
  460. {
  461. textSpaces.addSpace(rowSpace);
  462. cursorX = 0;
  463. continue;
  464. }
  465. letterDef = atlas->getOrLoad(ch, ttf);
  466. if (!letterDef) continue;
  467. letterRect = letterDef->rect;
  468. letterRect.origin *= _fontScale;
  469. letterRect.size = Size(letterRect.size.width * _fontScale, letterRect.size.height * _fontScale);
  470. if (_enableKerning && kerning) {
  471. auto const kerningX = kerning->at(i) * _fontScale;
  472. cursorX += kerningX;
  473. }
  474. float left = cursorX - letterDef->outline * _fontScale + letterRect.getMinX();
  475. float bottom = letterDef->outline * _fontScale - letterRect.getMaxY();
  476. if ((ResizeHeight || ClampAndWrap) && rowSpace.getExtentedWidth(left, left + letterRect.size.width) > ContentWidth)
  477. {
  478. // RESIZE_HEIGHT or (CLAMP & Enable Wrap)
  479. textSpaces.addSpace(rowSpace);
  480. cursorX = 0;
  481. left = cursorX - letterDef->outline * _fontScale + letterRect.getMinX();
  482. }
  483. float width = letterRect.size.width;
  484. float height = letterRect.size.height;
  485. Rect letterRectInline(left, bottom, width, height);
  486. Rect letterTexture(letterDef->texX, letterDef->texY, letterDef->texWidth, letterDef->texHeight);
  487. rowSpace.fillRect(letterDef->textureID, letterRectInline, letterTexture);
  488. cursorX += SpaceX + letterDef->xAdvance * _fontScale;
  489. }
  490. textSpaces.addSpace(rowSpace);
  491. auto& list = textSpaces._data;
  492. //add underline
  493. if(Underline){
  494. auto underline = atlas->getOrLoad('_', ttf);
  495. for (auto &space : list) {
  496. Rect letterRect = underline->rect;
  497. letterRect.origin *= _fontScale;
  498. letterRect.size = Size(letterRect.size.width * _fontScale, letterRect.size.height * _fontScale);
  499. float bottom = underline->outline * _fontScale - letterRect.getMaxY();
  500. float left = -letterDef->outline * _fontScale + space.getLeft();
  501. Rect letterRectInline(left, bottom, space.getWidth(), letterRect.size.height);
  502. Rect letterTexture(underline->texX, underline->texY, underline->texWidth, underline->texHeight);
  503. splitRectIntoThreeParts(underline->textureID, letterRect, letterRectInline, letterTexture, space);
  504. }
  505. }
  506. Vec2 newCenter;
  507. Vec2 delta;
  508. // default value in LabelOverflow::None mode
  509. float maxLineWidth = textSpaces._maxWidth;
  510. const float TotalTextHeight = textSpaces._data.size() * LineHeight;
  511. float topMost = (TotalTextHeight - LineHeight) * (1.0f - AnchorY);
  512. float leftMost = - AnchorX * maxLineWidth;
  513. float rightMost = (1.0f - AnchorX) * maxLineWidth;
  514. float centerX = (0.5f - AnchorX) * ContentWidth;
  515. Rect textArea(0, 0, 0, 0);
  516. for (int i = 0; i < list.size(); i++)
  517. {
  518. auto& s = list[i];
  519. if (!s.validate())
  520. {
  521. continue;
  522. }
  523. if (HAlign == LabelAlignmentH::CENTER)
  524. {
  525. newCenter.set(centerX, topMost - i * LineHeight);
  526. }
  527. else if (HAlign == LabelAlignmentH::LEFT)
  528. {
  529. newCenter.set(leftMost + s.getWidth() * 0.5, topMost - i * LineHeight);
  530. }
  531. else // right
  532. {
  533. newCenter.set(rightMost - s.getWidth() * 0.5f, topMost - i * LineHeight);
  534. }
  535. delta = newCenter - s.center();
  536. s.translate(delta.x, delta.y);
  537. textArea.merge(s.asRect());
  538. }
  539. //no resize
  540. if (OverFlow == LabelOverflow::NONE || OverFlow == LabelOverflow::CLAMP || OverFlow == LabelOverflow::RESIZE_HEIGHT)
  541. {
  542. Vec2 centerArea(textArea.origin.x + textArea.size.width / 2.0, textArea.origin.y + textArea.size.height / 2.0);
  543. float tPosX, tPosY;
  544. float halfAreaWidth = textArea.size.width * 0.5f;
  545. float halfAreaHeight = textArea.size.height * 0.5f;
  546. float contentWidth = ContentWidth;
  547. float contentHeight = ContentHeight;
  548. if (OverFlow == LabelOverflow::NONE)
  549. {
  550. contentWidth = textArea.size.width;
  551. contentHeight = textArea.size.height;
  552. }
  553. switch (HAlign)
  554. {
  555. case LabelAlignmentH::LEFT:
  556. tPosX = halfAreaWidth - AnchorX * contentWidth;
  557. break;
  558. case LabelAlignmentH::CENTER:
  559. tPosX = (0.5f - AnchorX) * contentWidth;
  560. break;
  561. case LabelAlignmentH::RIGHT:
  562. tPosX = (1.0f - AnchorX) * contentWidth - halfAreaWidth;
  563. break;
  564. default:
  565. assert(false);
  566. }
  567. switch (VAlign)
  568. {
  569. case LabelAlignmentV::TOP:
  570. tPosY = (1.0f - AnchorY) * contentHeight - halfAreaHeight;
  571. break;
  572. case LabelAlignmentV::CENTER:
  573. tPosY = (0.5f - AnchorY) * ContentHeight;
  574. break;
  575. case LabelAlignmentV::BOTTOM:
  576. tPosY = halfAreaHeight - AnchorY * contentHeight;
  577. break;
  578. default:
  579. assert(false);
  580. }
  581. delta = Vec2(tPosX, tPosY) - centerArea;
  582. for (auto &textSpace : textSpaces._data) {
  583. textSpace.translate(delta.x, delta.y);
  584. }
  585. if (OverFlow == LabelOverflow::CLAMP)
  586. {
  587. // clipping
  588. Rect displayRegion(-ContentWidth * AnchorX, -ContentHeight * AnchorY, ContentWidth, ContentHeight);
  589. for (auto &textSpace : textSpaces._data) {
  590. textSpace.clip(displayRegion);
  591. }
  592. }
  593. }
  594. else if (OverFlow == LabelOverflow::SHRINK)
  595. {
  596. float scale = std::min(ContentWidth / textArea.size.width, ContentHeight / textArea.size.height);
  597. scale = std::min(1.0f, scale);
  598. _scale = scale;
  599. Vec2 centerArea(textArea.origin.x + textArea.size.width * 0.5f, textArea.origin.y + textArea.size.height * 0.5f);
  600. float tPosXScale, tPosYScale;
  601. float halfTextWidth = textArea.size.width * 0.5f * _scale;
  602. float halfTextHeight = textArea.size.height * 0.5f * _scale;
  603. switch (HAlign)
  604. {
  605. case LabelAlignmentH::LEFT:
  606. tPosXScale = halfTextWidth - AnchorX * ContentWidth;
  607. break;
  608. case LabelAlignmentH::CENTER:
  609. tPosXScale = (0.5f - AnchorX) * ContentWidth;
  610. break;
  611. case LabelAlignmentH::RIGHT:
  612. tPosXScale = (1.0f - AnchorX) * ContentWidth - halfTextWidth;
  613. break;
  614. default:
  615. assert(false);
  616. }
  617. switch (VAlign)
  618. {
  619. case LabelAlignmentV::TOP:
  620. tPosYScale = (1.0f - AnchorY) * ContentHeight - halfTextHeight;
  621. break;
  622. case LabelAlignmentV::CENTER:
  623. tPosYScale = (0.5f - AnchorY) * ContentHeight;
  624. break;
  625. case LabelAlignmentV::BOTTOM:
  626. tPosYScale = halfTextHeight -AnchorY * ContentHeight;
  627. break;
  628. default:
  629. assert(false);
  630. }
  631. delta = Vec2(tPosXScale/ _scale, tPosYScale/_scale) - centerArea;
  632. for (auto &textSpace : textSpaces._data) {
  633. textSpace.translate(delta.x, delta.y);
  634. }
  635. }
  636. _textSpace = std::move(textSpaces._data);
  637. return true;
  638. }
  639. void LabelLayout::fillAssembler(renderer::CustomAssembler *assembler, EffectVariant *templateEffect)
  640. {
  641. assembler->reset();
  642. if(!_groups) {
  643. return;
  644. }
  645. _groups->reset();
  646. int groupIndex = 0;
  647. if (_textSpace.empty())
  648. return;
  649. if (_shadowGroups)
  650. {
  651. _shadowGroups->reset();
  652. }
  653. assembler->setUseModel(true);
  654. auto *fontAtals = _fontAtlas->getFontAtlas();
  655. Color4B textColor = _layoutInfo->color;
  656. // apply transform to all rectangles
  657. Rect rect;
  658. float scale = _scale;
  659. const bool Italics = _layoutInfo->italic;
  660. if (_shadowGroups && _layoutInfo->shadowBlur >= 0)
  661. {
  662. Vec2 offset(_layoutInfo->shadowX, _layoutInfo->shadowY);
  663. Color4B shadowColor = _layoutInfo->shadowColor;
  664. for (auto &textSpace : _textSpace) {
  665. if (textSpace.isIgnored()) continue;
  666. Vec2 shadowOffset = textSpace.getOffset();
  667. auto size = textSpace.size();
  668. for (int i = 0; i < size; i++)
  669. {
  670. TextRowSpace::GlyphBlock item = textSpace[i];
  671. if (textSpace.isIgnored(i)) continue;
  672. item.area.size = item.area.size * scale;
  673. item.area.origin = (offset + item.area.origin + shadowOffset) * scale;
  674. _shadowGroups->addRect(fontAtals->frameAt(item.texId).getTexture(), item.area, item.uv, shadowColor, Italics);
  675. }
  676. }
  677. groupIndex = _shadowGroups->fill(assembler, groupIndex, this, templateEffect);
  678. Technique::Parameter outlineSizeP(outlineSizeKey, Technique::Parameter::Type::FLOAT, (float*)&(_layoutInfo->outlineSize));
  679. if (_layoutInfo->outlineSize > 0.0f)
  680. {
  681. // use shadow color to replace outline color
  682. Color4F outlineColor(_layoutInfo->shadowColor);
  683. Technique::Parameter outlineColorP(outlineColorKey, Technique::Parameter::Type::COLOR4, (float*)&outlineColor);
  684. for (auto i = 0; i < groupIndex; i++)
  685. {
  686. auto *e = assembler->getEffect(i);
  687. e->setProperty(outlineColorKey, outlineColorP);
  688. e->setProperty(outlineSizeKey, outlineSizeP);
  689. }
  690. }
  691. else {
  692. for (auto i = 0; i < groupIndex; i++)
  693. {
  694. auto *e = assembler->getEffect(i);
  695. e->setProperty(outlineSizeKey, outlineSizeP);
  696. }
  697. }
  698. }
  699. for (auto &textSpace : _textSpace) {
  700. if (textSpace.isIgnored()) continue;
  701. Vec2 offset = textSpace.getOffset();
  702. auto size = textSpace.size();
  703. for (int i = 0; i < size; i++)
  704. {
  705. auto &item = textSpace[i];
  706. if (textSpace.isIgnored(i)) continue;
  707. item.area.size = item.area.size * scale;
  708. item.area.origin = (offset + item.area.origin) * scale;
  709. _groups->addRect(fontAtals->frameAt(item.texId).getTexture(), item.area, item.uv, textColor, Italics);
  710. }
  711. }
  712. int textStartIndex = groupIndex;
  713. groupIndex = _groups->fill(assembler, groupIndex, this, templateEffect);
  714. if (_layoutInfo->outlineSize > 0.0f)
  715. {
  716. Color4F outlineColor(_layoutInfo->outlineColor);
  717. Technique::Parameter outlineColorP(outlineColorKey, Technique::Parameter::Type::COLOR4, (float*)&outlineColor);
  718. Technique::Parameter outlineSizeP(outlineSizeKey, Technique::Parameter::Type::FLOAT, (float*)&(_layoutInfo->outlineSize));
  719. for (auto i = textStartIndex; i < groupIndex; i++)
  720. {
  721. auto *e = assembler->getEffect(i);
  722. e->setProperty(outlineColorKey, outlineColorP);
  723. e->setProperty(outlineSizeKey, outlineSizeP);
  724. }
  725. }
  726. else {
  727. Color4F outlineColor(_layoutInfo->outlineColor);
  728. Technique::Parameter outlineSizeP(outlineSizeKey, Technique::Parameter::Type::FLOAT, (float*)&(_layoutInfo->outlineSize));
  729. for (auto i = textStartIndex; i < groupIndex; i++)
  730. {
  731. auto *e = assembler->getEffect(i);
  732. e->setProperty(outlineSizeKey, outlineSizeP);
  733. }
  734. }
  735. }
  736. }
  737. #endif