TextSprite.cc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  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
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell 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
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #ifndef _DGL_H_
  23. #include "graphics/dgl.h"
  24. #endif
  25. #include "console/consoleTypes.h"
  26. #include "io/bitStream.h"
  27. #include "string/stringBuffer.h"
  28. #include "TextSprite.h"
  29. // Script bindings.
  30. #include "TextSprite_ScriptBinding.h"
  31. //-----------------------------------------------------------------------------
  32. // Text Alignment
  33. //-----------------------------------------------------------------------------
  34. bool TextSprite::setTextAlignment(void* obj, const char* data)
  35. {
  36. static_cast<TextSprite*>(obj)->setTextAlignment(getTextAlignmentEnum(data)); return false;
  37. }
  38. //------------------------------------------------------------------------------
  39. static EnumTable::Enums textAlignmentEnums[] =
  40. {
  41. { TextSprite::ALIGN_LEFT, "Left" },
  42. { TextSprite::ALIGN_CENTER, "Center" },
  43. { TextSprite::ALIGN_RIGHT, "Right" },
  44. { TextSprite::ALIGN_JUSTIFY, "Justify" },
  45. };
  46. static EnumTable gTextAlignmentTable(4, &textAlignmentEnums[0]);
  47. //-----------------------------------------------------------------------------
  48. TextSprite::TextAlign TextSprite::getTextAlignmentEnum(const char* label)
  49. {
  50. // Search for Mnemonic.
  51. for (U32 i = 0; i < (sizeof(textAlignmentEnums) / sizeof(EnumTable::Enums)); i++)
  52. {
  53. if (dStricmp(textAlignmentEnums[i].label, label) == 0)
  54. return (TextAlign)textAlignmentEnums[i].index;
  55. }
  56. // Warn.
  57. Con::warnf("TextSprite::getTextAlignmentEnum() - Invalid text alignment of '%s'", label);
  58. return TextSprite::INVALID_ALIGN;
  59. }
  60. //-----------------------------------------------------------------------------
  61. const char* TextSprite::getTextAlignmentDescription(const TextSprite::TextAlign alignment)
  62. {
  63. // Search for Mnemonic.
  64. for (U32 i = 0; i < (sizeof(textAlignmentEnums) / sizeof(EnumTable::Enums)); i++)
  65. {
  66. if (textAlignmentEnums[i].index == alignment)
  67. return textAlignmentEnums[i].label;
  68. }
  69. // Warn.
  70. Con::warnf("TextSprite::getTextAlignmentDescription() - Invalid text alignment.");
  71. return StringTable->EmptyString;
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Verticle Text Alignment
  75. //-----------------------------------------------------------------------------
  76. bool TextSprite::setTextVAlignment(void* obj, const char* data)
  77. {
  78. static_cast<TextSprite*>(obj)->setTextVAlignment(getTextVAlignmentEnum(data)); return false;
  79. }
  80. //------------------------------------------------------------------------------
  81. static EnumTable::Enums textVAlignmentEnums[] =
  82. {
  83. { TextSprite::VALIGN_TOP, "Top" },
  84. { TextSprite::VALIGN_MIDDLE, "Middle" },
  85. { TextSprite::VALIGN_BOTTOM, "Bottom" },
  86. };
  87. static EnumTable gTextVAlignmentTable(3, &textVAlignmentEnums[0]);
  88. //-----------------------------------------------------------------------------
  89. TextSprite::TextVAlign TextSprite::getTextVAlignmentEnum(const char* label)
  90. {
  91. // Search for Mnemonic.
  92. for (U32 i = 0; i < (sizeof(textVAlignmentEnums) / sizeof(EnumTable::Enums)); i++)
  93. {
  94. if (dStricmp(textVAlignmentEnums[i].label, label) == 0)
  95. return (TextVAlign)textVAlignmentEnums[i].index;
  96. }
  97. // Warn.
  98. Con::warnf("TextSprite::getTextVAlignmentEnum() - Invalid vertical text alignment of '%s'", label);
  99. return TextSprite::INVALID_VALIGN;
  100. }
  101. //-----------------------------------------------------------------------------
  102. const char* TextSprite::getTextVAlignmentDescription(const TextSprite::TextVAlign alignment)
  103. {
  104. // Search for Mnemonic.
  105. for (U32 i = 0; i < (sizeof(textVAlignmentEnums) / sizeof(EnumTable::Enums)); i++)
  106. {
  107. if (textVAlignmentEnums[i].index == alignment)
  108. return textVAlignmentEnums[i].label;
  109. }
  110. // Warn.
  111. Con::warnf("TextSprite::getTextVAlignmentDescription() - Invalid vertical text alignment.");
  112. return StringTable->EmptyString;
  113. }
  114. //-----------------------------------------------------------------------------
  115. // Overflow Mode X
  116. //-----------------------------------------------------------------------------
  117. bool TextSprite::setOverflowModeX(void* obj, const char* data)
  118. {
  119. static_cast<TextSprite*>(obj)->setOverflowModeX(getOverflowModeXEnum(data)); return false;
  120. }
  121. //------------------------------------------------------------------------------
  122. static EnumTable::Enums overflowModeXEnums[] =
  123. {
  124. { TextSprite::OVERFLOW_X_WRAP, "Wrap" },
  125. { TextSprite::OVERFLOW_X_VISIBLE, "Visible" },
  126. { TextSprite::OVERFLOW_X_HIDDEN, "Hidden" },
  127. { TextSprite::OVERFLOW_X_SHRINK, "Shrink" },
  128. };
  129. static EnumTable gOverflowModeXTable(4, &overflowModeXEnums[0]);
  130. //-----------------------------------------------------------------------------
  131. TextSprite::OverflowModeX TextSprite::getOverflowModeXEnum(const char* label)
  132. {
  133. // Search for Mnemonic.
  134. for (U32 i = 0; i < (sizeof(overflowModeXEnums) / sizeof(EnumTable::Enums)); i++)
  135. {
  136. if (dStricmp(overflowModeXEnums[i].label, label) == 0)
  137. return (OverflowModeX)overflowModeXEnums[i].index;
  138. }
  139. // Warn.
  140. Con::warnf("TextSprite::getOverflowModeXEnum() - Invalid overflow mode X of '%s'", label);
  141. return TextSprite::INVALID_OVERFLOW_X;
  142. }
  143. //-----------------------------------------------------------------------------
  144. const char* TextSprite::getOverflowModeXDescription(const TextSprite::OverflowModeX modeX)
  145. {
  146. // Search for Mnemonic.
  147. for (U32 i = 0; i < (sizeof(overflowModeXEnums) / sizeof(EnumTable::Enums)); i++)
  148. {
  149. if (overflowModeXEnums[i].index == modeX)
  150. return overflowModeXEnums[i].label;
  151. }
  152. // Warn.
  153. Con::warnf("TextSprite::getOverflowModeXDescription() - Invalid overflow mode X.");
  154. return StringTable->EmptyString;
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Overflow Mode Y
  158. //-----------------------------------------------------------------------------
  159. bool TextSprite::setOverflowModeY(void* obj, const char* data)
  160. {
  161. static_cast<TextSprite*>(obj)->setOverflowModeY(getOverflowModeYEnum(data)); return false;
  162. }
  163. //------------------------------------------------------------------------------
  164. static EnumTable::Enums overflowModeYEnums[] =
  165. {
  166. { TextSprite::OVERFLOW_Y_VISIBLE, "Visible" },
  167. { TextSprite::OVERFLOW_Y_HIDDEN, "Hidden" },
  168. { TextSprite::OVERFLOW_Y_SHRINK, "Shrink" },
  169. };
  170. static EnumTable gOverflowModeYTable(3, &overflowModeYEnums[0]);
  171. //-----------------------------------------------------------------------------
  172. TextSprite::OverflowModeY TextSprite::getOverflowModeYEnum(const char* label)
  173. {
  174. // Search for Mnemonic.
  175. for (U32 i = 0; i < (sizeof(overflowModeYEnums) / sizeof(EnumTable::Enums)); i++)
  176. {
  177. if (dStricmp(overflowModeYEnums[i].label, label) == 0)
  178. return (OverflowModeY)overflowModeYEnums[i].index;
  179. }
  180. // Warn.
  181. Con::warnf("TextSprite::getOverflowModeYEnum() - Invalid overflow mode Y of '%s'", label);
  182. return TextSprite::INVALID_OVERFLOW_Y;
  183. }
  184. //-----------------------------------------------------------------------------
  185. const char* TextSprite::getOverflowModeYDescription(const TextSprite::OverflowModeY modeY)
  186. {
  187. // Search for Mnemonic.
  188. for (U32 i = 0; i < (sizeof(overflowModeYEnums) / sizeof(EnumTable::Enums)); i++)
  189. {
  190. if (overflowModeYEnums[i].index == modeY)
  191. return overflowModeYEnums[i].label;
  192. }
  193. // Warn.
  194. Con::warnf("TextSprite::getOverflowModeYDescription() - Invalid overflow mode Y.");
  195. return StringTable->EmptyString;
  196. }
  197. //------------------------------------------------------------------------------
  198. IMPLEMENT_CONOBJECT(TextSprite);
  199. //-----------------------------------------------------------------------------
  200. TextSprite::TextSprite() :
  201. mFontSize(1.0f),
  202. mFontScaleX(1.0f),
  203. mFontScaleY(1.0f),
  204. mTextAlign(TextSprite::ALIGN_LEFT),
  205. mTextVAlign(TextSprite::VALIGN_TOP),
  206. mOverflowX(TextSprite::OVERFLOW_X_WRAP),
  207. mOverflowY(TextSprite::OVERFLOW_Y_HIDDEN),
  208. mAutoLineHeight(true),
  209. mCustomLineHeight(1.0f),
  210. mKerning(0.0f),
  211. mFontSpatialsDirty(true),
  212. mCalculatedSize(1.0f, 1.0f)
  213. {
  214. }
  215. //-----------------------------------------------------------------------------
  216. TextSprite::~TextSprite()
  217. {
  218. }
  219. //-----------------------------------------------------------------------------
  220. void TextSprite::initPersistFields()
  221. {
  222. // Call parent.
  223. Parent::initPersistFields();
  224. addProtectedField("font", TypeFontAssetPtr, Offset(mFontAsset, TextSprite), &setFont, &getFont, &writeFont, "");
  225. addProtectedField("text", TypeString, 0, setText, getText, &writeText, "The text to be displayed.");
  226. addProtectedField("fontSize", TypeVector2, Offset(mFontSize, TextSprite), &setFontSize, &defaultProtectedGetFn, &writeFontSize, "");
  227. addProtectedField("fontScaleX", TypeF32, Offset(mFontScaleX, TextSprite), &setFontScaleX, &defaultProtectedGetFn, &writeFontScaleX, "");
  228. addProtectedField("fontScaleY", TypeF32, Offset(mFontScaleY, TextSprite), &setFontScaleY, &defaultProtectedGetFn, &writeFontScaleY, "");
  229. addProtectedField("textAlignment", TypeEnum, Offset(mTextAlign, TextSprite), &setTextAlignment, &defaultProtectedGetFn, &writeTextAlignment, 1, &gTextAlignmentTable, "");
  230. addProtectedField("textVAlignment", TypeEnum, Offset(mTextVAlign, TextSprite), &setTextVAlignment, &defaultProtectedGetFn, &writeTextVAlignment, 1, &gTextVAlignmentTable, "");
  231. addProtectedField("overflowModeX", TypeEnum, Offset(mOverflowX, TextSprite), &setOverflowModeX, &defaultProtectedGetFn, &writeOverflowModeX, 1, &gOverflowModeXTable, "");
  232. addProtectedField("overflowModeY", TypeEnum, Offset(mOverflowY, TextSprite), &setOverflowModeY, &defaultProtectedGetFn, &writeOverflowModeY, 1, &gOverflowModeYTable, "");
  233. addField("autoLineHeight", TypeBool, Offset(mAutoLineHeight, TextSprite), &writeAutoLineHeight, "");
  234. addProtectedField("customLineHeight", TypeF32, Offset(mCustomLineHeight, TextSprite), &setCustomLineHeight, &defaultProtectedGetFn, &writeCustomLineHeight, "");
  235. addProtectedField("kerning", TypeF32, Offset(mKerning, TextSprite), &setKerning, &defaultProtectedGetFn, &writeKerning, "");
  236. }
  237. //-----------------------------------------------------------------------------
  238. bool TextSprite::onAdd()
  239. {
  240. // Call Parent.
  241. if(!Parent::onAdd())
  242. return false;
  243. // Return Okay.
  244. return true;
  245. }
  246. //-----------------------------------------------------------------------------
  247. void TextSprite::onRemove()
  248. {
  249. // Call Parent.
  250. Parent::onRemove();
  251. }
  252. //------------------------------------------------------------------------------
  253. void TextSprite::copyTo(SimObject* object)
  254. {
  255. // Fetch font object.
  256. TextSprite* pFontObject = dynamic_cast<TextSprite*>(object);
  257. // Sanity.
  258. AssertFatal(pFontObject != NULL, "TextSprite::copyTo() - Object is not the correct type.");
  259. // Call parent.
  260. Parent::copyTo(object);
  261. // Copy.
  262. pFontObject->setFont(getFont());
  263. pFontObject->setText(getText());
  264. pFontObject->setFontSize(getFontSize());
  265. pFontObject->setFontScaleX(getFontScaleX());
  266. pFontObject->setFontScaleY(getFontScaleY());
  267. pFontObject->setTextAlignment(getTextAlignment());
  268. pFontObject->setTextVAlignment(getTextVAlignment());
  269. pFontObject->setOverflowModeX(getOverflowModeX());
  270. pFontObject->setOverflowModeY(getOverflowModeY());
  271. pFontObject->setAutoLineHeight(getAutoLineHeight());
  272. pFontObject->setCustomLineHeight(getCustomLineHeight());
  273. pFontObject->setKerning(getKerning());
  274. pFontObject->mCharInfo = mCharInfo;
  275. }
  276. //------------------------------------------------------------------------------
  277. void TextSprite::scenePrepareRender( const SceneRenderState* pSceneRenderState, SceneRenderQueue* pSceneRenderQueue )
  278. {
  279. // Create a default render request.
  280. Scene::createDefaultRenderRequest( pSceneRenderQueue, this );
  281. }
  282. //------------------------------------------------------------------------------
  283. void TextSprite::sceneRender( const SceneRenderState* pSceneRenderState, const SceneRenderRequest* pSceneRenderRequest, BatchRender* pBatchRenderer )
  284. {
  285. // Finish if no font asset.
  286. if ( mFontAsset.isNull() )
  287. return;
  288. // Fetch number of characters to render.
  289. const U32 renderCharacters = mText.length();
  290. // Ignore if no text to render.
  291. if( renderCharacters == 0 )
  292. return;
  293. //Make sure none of our settings are invalid
  294. if (mTextAlign == INVALID_ALIGN)
  295. {
  296. mTextAlign = ALIGN_LEFT;
  297. }
  298. if (mTextVAlign == INVALID_VALIGN)
  299. {
  300. mTextVAlign = VALIGN_TOP;
  301. }
  302. if (mOverflowX == INVALID_OVERFLOW_X)
  303. {
  304. mOverflowX = OVERFLOW_X_WRAP;
  305. }
  306. if (mOverflowY == INVALID_OVERFLOW_Y)
  307. {
  308. mOverflowY = OVERFLOW_Y_HIDDEN;
  309. }
  310. // Get a size ratio
  311. const F32 ratio = mFontAsset->mBitmapFont.getSizeRatio(mFontSize);
  312. //get the line height
  313. const F32 lineHeight = GetLineHeight();
  314. //prep for justify
  315. if (mTextAlign == ALIGN_JUSTIFY)
  316. {
  317. mKerning = 0;
  318. }
  319. //determine the rows
  320. if (mFontSpatialsDirty || mSize != mCalculatedSize)
  321. {
  322. CalculateSpatials(ratio);
  323. }
  324. // If we're shrinking the set the scale
  325. bool shrinkX = false;
  326. bool shrinkY = false;
  327. F32 origScaleX, origScaleY;
  328. F32 origLengthX;
  329. if (mOverflowX == OVERFLOW_X_SHRINK && mLine.front().mLength > mSize.x)
  330. {
  331. shrinkX = true;
  332. origScaleX = mFontScaleX;
  333. mFontScaleX = mSize.x / mLine.front().mLength;
  334. origLengthX = mLine.front().mLength;
  335. mLine.front().mLength = mSize.x;
  336. }
  337. if (mOverflowY == OVERFLOW_Y_SHRINK && (mLine.size() * lineHeight * mFontScaleY) > mSize.y)
  338. {
  339. shrinkY = true;
  340. origScaleY = mFontScaleY;
  341. mFontScaleY = mSize.y / (mLine.size() * lineHeight);
  342. }
  343. // Create a cursor
  344. Vector2 cursor(0.0f, 0.0f);
  345. ApplyAlignment(cursor, mLine.size(), 0, mLine.front().mLength, mLine.front().mEnd - mLine.front().mStart + 1, ratio);
  346. // Render all the characters.
  347. U32 row = 0;
  348. S32 prevCharID = -1;
  349. for (U32 characterIndex = mLine.front().mStart; characterIndex < renderCharacters; ++characterIndex)
  350. {
  351. // Fetch character.
  352. U32 charID = mText.getChar( characterIndex );
  353. if (characterIndex >= mLine[row].mStart)
  354. {
  355. RenderLetter(pBatchRenderer, cursor, charID, ratio, characterIndex);
  356. }
  357. if ((row + 1) < mLine.size() && mLine[row + 1].mStart == (characterIndex + 1))
  358. {
  359. row++;
  360. cursor.x = 0;
  361. prevCharID = -1;
  362. ApplyAlignment(cursor, mLine.size(), row, mLine[row].mLength, mLine[row].mEnd - mLine[row].mStart + 1, ratio);
  363. }
  364. else
  365. {
  366. cursor.x += getCursorAdvance(charID, prevCharID, ratio);
  367. prevCharID = charID;
  368. }
  369. }
  370. //clean up for justify
  371. if (mTextAlign == ALIGN_JUSTIFY)
  372. {
  373. mKerning = 0;
  374. }
  375. //clean up scale
  376. if (mOverflowX == OVERFLOW_X_SHRINK && shrinkX)
  377. {
  378. mFontScaleX = origScaleX;
  379. mLine.front().mLength = origLengthX;
  380. }
  381. if (mOverflowY == OVERFLOW_Y_SHRINK && shrinkY)
  382. {
  383. mFontScaleY = origScaleY;
  384. }
  385. }
  386. //-----------------------------------------------------------------------------
  387. void TextSprite::RenderLetter(BatchRender* pBatchRenderer, Vector2& cursor, U32 charID, F32 ratio, U32 charNum)
  388. {
  389. const BitmapFontCharacter& bmChar = mFontAsset->mBitmapFont.getCharacter(charID);
  390. Vector2 charScale = getCharacterScale(charNum);
  391. Vector2 charOffset = getCharacterOffset(charNum);
  392. ColorF charColor = getCharacterBlendColor(charNum);
  393. F32 fontScaleX = mFontScaleX * charScale.x;
  394. F32 fontScaleY = mFontScaleY * charScale.y;
  395. F32 cursorX = cursor.x + charOffset.x - (bmChar.mWidth * ratio * mFontScaleX * ((charScale.x - 1) / 2));
  396. F32 cursorY = cursor.y - charOffset.y;
  397. //inset, for hiding part of a letter
  398. F32 insetLeft, insetRight, insetTop, insetBottom;
  399. insetLeft = 0;
  400. insetRight = 0;
  401. insetTop = 0;
  402. insetBottom = 0;
  403. // Cropping time!
  404. if (mOverflowX == OVERFLOW_X_HIDDEN)
  405. {
  406. if ((cursorX + (bmChar.mWidth * ratio * fontScaleX) <= 0) || (cursorX >= mSize.x))
  407. return;
  408. if (cursorX < 0 && (cursorX + (bmChar.mWidth * ratio * fontScaleX) > 0))
  409. {
  410. insetLeft = -(cursorX / (bmChar.mWidth * ratio * fontScaleX));
  411. }
  412. if (cursorX < mSize.x && (cursorX + (bmChar.mWidth * ratio * fontScaleX) > mSize.x))
  413. {
  414. insetRight = (((cursorX + (bmChar.mWidth * ratio * fontScaleX)) - mSize.x) / (bmChar.mWidth * ratio * fontScaleX));
  415. }
  416. if (insetLeft + insetRight > 1)
  417. return;
  418. }
  419. if (mOverflowY == OVERFLOW_Y_HIDDEN)
  420. {
  421. F32 tempTop, tempBottom;
  422. tempTop = cursorY - (mFontAsset->mBitmapFont.mBaseline * ratio * fontScaleY) + (bmChar.mYOffset * ratio * fontScaleY);
  423. tempBottom = tempTop + (bmChar.mHeight * ratio * fontScaleY);
  424. if ((tempBottom <= 0) || (tempTop >= mSize.y))
  425. return;
  426. if (tempTop < 0 && tempBottom > 0)
  427. {
  428. insetTop = -(tempTop / (bmChar.mHeight * ratio * fontScaleY));
  429. }
  430. if (tempTop < mSize.y && tempBottom > mSize.y)
  431. {
  432. insetBottom = (tempBottom - mSize.y) / (bmChar.mHeight * ratio * fontScaleY);
  433. }
  434. if (insetTop + insetBottom > 1)
  435. return;
  436. }
  437. //create the source rect
  438. Vector2 sourceOOBB[4];
  439. sourceOOBB[0].Set(bmChar.mOOBB[0].x + ((insetLeft * (F32)bmChar.mWidth) / (F32)bmChar.mPageWidth), bmChar.mOOBB[0].y - ((insetBottom * (F32)bmChar.mHeight) / (F32)bmChar.mPageHeight));
  440. sourceOOBB[1].Set(bmChar.mOOBB[1].x - ((insetRight * (F32)bmChar.mWidth) / (F32)bmChar.mPageWidth), bmChar.mOOBB[1].y - ((insetBottom * (F32)bmChar.mHeight) / (F32)bmChar.mPageHeight));
  441. sourceOOBB[2].Set(bmChar.mOOBB[2].x - ((insetRight * (F32)bmChar.mWidth) / (F32)bmChar.mPageWidth), bmChar.mOOBB[2].y + ((insetTop * (F32)bmChar.mHeight) / (F32)bmChar.mPageHeight));
  442. sourceOOBB[3].Set(bmChar.mOOBB[3].x + ((insetLeft * (F32)bmChar.mWidth) / (F32)bmChar.mPageWidth), bmChar.mOOBB[3].y + ((insetTop * (F32)bmChar.mHeight) / (F32)bmChar.mPageHeight));
  443. //create the destination rect
  444. Vector2 destLeft = (mRenderOOBB[1] - mRenderOOBB[0]);
  445. destLeft.Normalize((F32)cursorX + (insetLeft * bmChar.mWidth * ratio * fontScaleX));
  446. Vector2 destWidth = (mRenderOOBB[1] - mRenderOOBB[0]);
  447. destWidth.Normalize(((bmChar.mWidth * ratio) - (insetLeft * (bmChar.mWidth * ratio)) - (insetRight * (bmChar.mWidth * ratio))) * fontScaleX);
  448. Vector2 destTop = -(mRenderOOBB[3] - mRenderOOBB[0]);
  449. destTop.Normalize((F32)cursorY - (((mFontAsset->mBitmapFont.mBaseline * ratio) - (bmChar.mYOffset * ratio) - (insetTop * bmChar.mHeight * ratio)) * fontScaleY));
  450. Vector2 destHeight = -(mRenderOOBB[3] - mRenderOOBB[0]);
  451. destHeight.Normalize(((bmChar.mHeight * ratio) - (insetBottom * bmChar.mHeight * ratio) - (insetTop * bmChar.mHeight * ratio)) * fontScaleY);
  452. Vector2 destOOBB[4];
  453. destOOBB[0] = (mRenderOOBB[3] + destLeft + destTop + destHeight);
  454. destOOBB[1] = (mRenderOOBB[3] + destLeft + destWidth + destTop + destHeight);
  455. destOOBB[2] = (mRenderOOBB[3] + destLeft + destWidth + destTop);
  456. destOOBB[3] = (mRenderOOBB[3] + destLeft + destTop);
  457. if (charColor != mBlendColor)
  458. {
  459. // Submit batched quad.
  460. pBatchRenderer->SubmitQuad(
  461. destOOBB[0],
  462. destOOBB[1],
  463. destOOBB[2],
  464. destOOBB[3],
  465. sourceOOBB[0],
  466. sourceOOBB[1],
  467. sourceOOBB[2],
  468. sourceOOBB[3],
  469. mFontAsset->getImageTexture(bmChar.mPage),
  470. charColor);
  471. }
  472. else
  473. {
  474. // Submit batched quad.
  475. pBatchRenderer->SubmitQuad(
  476. destOOBB[0],
  477. destOOBB[1],
  478. destOOBB[2],
  479. destOOBB[3],
  480. sourceOOBB[0],
  481. sourceOOBB[1],
  482. sourceOOBB[2],
  483. sourceOOBB[3],
  484. mFontAsset->getImageTexture(bmChar.mPage));
  485. }
  486. }
  487. //-----------------------------------------------------------------------------
  488. void TextSprite::CalculateSpatials(F32 ratio)
  489. {
  490. F32 length = 0;
  491. S32 start = -1;
  492. S32 wordEnd = 0;
  493. F32 wordEndLength = 0;
  494. S32 prevCharID = -1;
  495. mLine.clear();
  496. const U32 renderCharacters = mText.length();
  497. for (U32 i = 0; i < renderCharacters; i++)
  498. {
  499. U32 charID = mText.getChar(i);
  500. const BitmapFontCharacter& bmChar = mFontAsset->mBitmapFont.getCharacter(charID);
  501. if (start == -1 && charID != 32)
  502. {
  503. start = i;
  504. length = 0;
  505. mLine.push_back(BitmapFontLineInfo(start));
  506. }
  507. if (i == start)
  508. {
  509. length -= (bmChar.mXOffset * ratio * mFontScaleX);
  510. }
  511. length += getCursorAdvance(bmChar, prevCharID, ratio);
  512. if (mOverflowX == OVERFLOW_X_WRAP)
  513. {
  514. if (prevCharID != 32 && prevCharID != -1 && charID == 32)
  515. {
  516. wordEnd = i - 1;
  517. wordEndLength = length - getCursorAdvance(bmChar, prevCharID, ratio);
  518. }
  519. if (length > mSize.x && wordEnd > start && start != -1)
  520. {
  521. mLine.back().mEnd = wordEnd;
  522. U32 endCharID = mText.getChar(wordEnd);
  523. const BitmapFontCharacter& bmCharEnd = mFontAsset->mBitmapFont.getCharacter(endCharID);
  524. i = wordEnd;
  525. start = -1;
  526. length = 0;
  527. S32 prevEndCharID = -1;
  528. if (wordEnd != 0)
  529. {
  530. prevEndCharID = mText.getChar(wordEnd - 1);
  531. }
  532. wordEndLength += -getCursorAdvance(bmCharEnd, prevEndCharID, ratio) + (bmCharEnd.mWidth * ratio * mFontScaleX) + (bmCharEnd.mXOffset * ratio * mFontScaleX);
  533. mLine.back().mLength = wordEndLength;
  534. }
  535. }
  536. prevCharID = charID;
  537. if ((i + 1) == renderCharacters)
  538. {
  539. if (charID != 32)//this is the last character and if it's not a space then it's the end of a word.
  540. {
  541. wordEndLength = length;
  542. wordEnd = i;
  543. }
  544. mLine.back().mEnd = wordEnd;
  545. U32 endCharID = mText.getChar(wordEnd);
  546. const BitmapFontCharacter& bmCharEnd = mFontAsset->mBitmapFont.getCharacter(endCharID);
  547. S32 prevEndCharID = -1;
  548. if (wordEnd != 0)
  549. {
  550. prevEndCharID = mText.getChar(wordEnd - 1);
  551. }
  552. wordEndLength += -getCursorAdvance(bmCharEnd, prevEndCharID, ratio) + (bmCharEnd.mWidth * ratio * mFontScaleX) + (bmCharEnd.mXOffset * ratio * mFontScaleX);
  553. mLine.back().mLength = wordEndLength;
  554. }
  555. }
  556. mFontSpatialsDirty = false;
  557. mCalculatedSize = mSize;
  558. }
  559. //-----------------------------------------------------------------------------
  560. void TextSprite::ApplyAlignment(Vector2& cursor, U32 totalRows, U32 row, F32 length, U32 charCount, F32 ratio)
  561. {
  562. //Horizontal Align
  563. if (mTextAlign == ALIGN_CENTER)
  564. {
  565. cursor.x += ((mSize.x - length) / 2.0f);
  566. }
  567. else if (mTextAlign == ALIGN_RIGHT)
  568. {
  569. cursor.x += (mSize.x - length);
  570. }
  571. else if (mTextAlign == ALIGN_JUSTIFY)
  572. {
  573. U32 gapCount = charCount - 1;
  574. mKerning = (-(mSize.x - length) / gapCount) / mFontScaleX;
  575. }
  576. //Vertical Align
  577. const F32 lineHeight = GetLineHeight();
  578. if (mTextVAlign == VALIGN_TOP)
  579. {
  580. cursor.y = ((mFontAsset->mBitmapFont.mBaseline * ratio) + (lineHeight * row)) * mFontScaleY;
  581. }
  582. else if (mTextVAlign == VALIGN_MIDDLE)
  583. {
  584. cursor.y = (mSize.y / 2.0f) - ((((lineHeight / 2.0f) * totalRows) - (lineHeight * row) - (mFontAsset->mBitmapFont.mBaseline * ratio)) * mFontScaleY);
  585. }
  586. else if (mTextVAlign == VALIGN_BOTTOM)
  587. {
  588. cursor.y = mSize.y - (((lineHeight * totalRows) - (lineHeight * row) - (mFontAsset->mBitmapFont.mBaseline * ratio)) * mFontScaleY);
  589. }
  590. }
  591. //-----------------------------------------------------------------------------
  592. F32 TextSprite::getCursorAdvance(U32 charID, S32 prevCharID, F32 ratio)
  593. {
  594. const BitmapFontCharacter& bmChar = mFontAsset->mBitmapFont.getCharacter(charID);
  595. return getCursorAdvance(bmChar, prevCharID, ratio);
  596. }
  597. F32 TextSprite::getCursorAdvance(const BitmapFontCharacter& bmChar, S32 prevCharID, F32 ratio)
  598. {
  599. if (prevCharID != -1)
  600. {
  601. S16 kerning = mFontAsset->mBitmapFont.getKerning(prevCharID, bmChar.mCharID);
  602. return (((bmChar.mXAdvance - kerning) * ratio) - mKerning) * mFontScaleX;
  603. }
  604. else
  605. {
  606. return ((bmChar.mXAdvance * ratio) - mKerning) * mFontScaleX;
  607. }
  608. }
  609. //-----------------------------------------------------------------------------
  610. bool TextSprite::setFont( const char* pFontAssetId )
  611. {
  612. // Set asset.
  613. mFontAsset = pFontAssetId;
  614. // Finish if no font asset.
  615. if ( mFontAsset.isNull() )
  616. return false;
  617. mFontSpatialsDirty = true;
  618. // Return Okay.
  619. return true;
  620. }
  621. //-----------------------------------------------------------------------------
  622. void TextSprite::setCharacterBlendColor(const U32 charNum, const ColorF color)
  623. {
  624. if (mCharInfo.find(charNum) != mCharInfo.end())
  625. {
  626. mCharInfo[charNum].mColor = color;
  627. mCharInfo[charNum].mUseColor = true;
  628. }
  629. else
  630. {
  631. mCharInfo[charNum] = BitmapFontCharacterInfo(color);
  632. }
  633. }
  634. //-----------------------------------------------------------------------------
  635. ColorF TextSprite::getCharacterBlendColor(const U32 charNum)
  636. {
  637. if (mCharInfo.find(charNum) != mCharInfo.end())
  638. {
  639. return mCharInfo[charNum].mColor;
  640. }
  641. else
  642. {
  643. return mBlendColor;
  644. }
  645. }
  646. //-----------------------------------------------------------------------------
  647. bool TextSprite::getCharacterHasBlendColor(const U32 charNum)
  648. {
  649. return (mCharInfo.find(charNum) != mCharInfo.end() && mCharInfo[charNum].mUseColor);
  650. }
  651. //-----------------------------------------------------------------------------
  652. void TextSprite::resetCharacterBlendColor(const U32 charNum)
  653. {
  654. if (mCharInfo.find(charNum) != mCharInfo.end())
  655. {
  656. mCharInfo[charNum].mUseColor = false;
  657. if (mCharInfo[charNum].isDefault())
  658. {
  659. mCharInfo.erase(charNum);
  660. }
  661. }
  662. }
  663. //-----------------------------------------------------------------------------
  664. void TextSprite::setCharacterScale(const U32 charNum, const F32 scaleX, const F32 scaleY)
  665. {
  666. if (mCharInfo.find(charNum) == mCharInfo.end())
  667. {
  668. mCharInfo[charNum] = BitmapFontCharacterInfo();
  669. }
  670. mCharInfo[charNum].mScaleX = scaleX;
  671. mCharInfo[charNum].mScaleY = scaleY;
  672. if (mCharInfo[charNum].isDefault())
  673. {
  674. mCharInfo.erase(charNum);
  675. }
  676. }
  677. //-----------------------------------------------------------------------------
  678. Vector2 TextSprite::getCharacterScale(const U32 charNum)
  679. {
  680. if (mCharInfo.find(charNum) != mCharInfo.end())
  681. {
  682. return Vector2(mCharInfo[charNum].mScaleX, mCharInfo[charNum].mScaleY);
  683. }
  684. else
  685. {
  686. return Vector2(1.0f, 1.0f);
  687. }
  688. }
  689. //-----------------------------------------------------------------------------
  690. void TextSprite::resetCharacterScale(const U32 charNum)
  691. {
  692. if (mCharInfo.find(charNum) != mCharInfo.end())
  693. {
  694. mCharInfo[charNum].mScaleX = 1.0f;
  695. mCharInfo[charNum].mScaleY = 1.0f;
  696. if (mCharInfo[charNum].isDefault())
  697. {
  698. mCharInfo.erase(charNum);
  699. }
  700. }
  701. }
  702. //-----------------------------------------------------------------------------
  703. void TextSprite::setCharacterOffset(const U32 charNum, const F32 offsetX, const F32 offsetY)
  704. {
  705. if (mCharInfo.find(charNum) == mCharInfo.end())
  706. {
  707. mCharInfo[charNum] = BitmapFontCharacterInfo();
  708. }
  709. mCharInfo[charNum].mOffsetX = offsetX;
  710. mCharInfo[charNum].mOffsetY = offsetY;
  711. if (mCharInfo[charNum].isDefault())
  712. {
  713. mCharInfo.erase(charNum);
  714. }
  715. }
  716. //-----------------------------------------------------------------------------
  717. Vector2 TextSprite::getCharacterOffset(const U32 charNum)
  718. {
  719. if (mCharInfo.find(charNum) != mCharInfo.end())
  720. {
  721. return Vector2(mCharInfo[charNum].mOffsetX, mCharInfo[charNum].mOffsetY);
  722. }
  723. else
  724. {
  725. return Vector2(0.0f, 0.0f);
  726. }
  727. }
  728. //-----------------------------------------------------------------------------
  729. void TextSprite::resetCharacterOffset(const U32 charNum)
  730. {
  731. if (mCharInfo.find(charNum) != mCharInfo.end())
  732. {
  733. mCharInfo[charNum].mOffsetX = 0.0f;
  734. mCharInfo[charNum].mOffsetY = 0.0f;
  735. if (mCharInfo[charNum].isDefault())
  736. {
  737. mCharInfo.erase(charNum);
  738. }
  739. }
  740. }