BsTextData.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. #pragma once
  2. #include "BsCorePrerequisites.h"
  3. #include "BsFontDesc.h"
  4. #include "BsVector2I.h"
  5. namespace BansheeEngine
  6. {
  7. /**
  8. * @brief This object takes as input a string, a font and optionally some constraints (like word wrap)
  9. * and outputs a set of character data you may use for rendering or metrics.
  10. */
  11. class TextData
  12. {
  13. private:
  14. /**
  15. * @brief Represents a single word as a set of characters, or optionally just a blank space
  16. * of a certain length.
  17. *
  18. * @note Due to the way allocation is handled, this class is not allowed to have a destructor.
  19. */
  20. class TextWord
  21. {
  22. public:
  23. /**
  24. * @brief Initializes the word and signals if it just a space (or multiple spaces), or
  25. * an actual word with letters.
  26. */
  27. void init(bool spacer);
  28. /**
  29. * @brief Appends a new character to the word.
  30. *
  31. * @param charIdx Sequential index of the character in the original string.
  32. * @param desc Character description from the font.
  33. *
  34. * @returns How many pixels did the added character expand the word by.
  35. */
  36. UINT32 addChar(UINT32 charIdx, const CHAR_DESC& desc);
  37. /**
  38. * @brief Adds a space to the word. Word must have previously have been declared as
  39. * a "spacer".
  40. */
  41. void addSpace(UINT32 spaceWidth);
  42. /**
  43. * @brief Returns the width of the word in pixels.
  44. */
  45. UINT32 getWidth() const { return mWidth; }
  46. /**
  47. * @brief Returns height of the word in pixels.
  48. */
  49. UINT32 getHeight() const { return mHeight; }
  50. /**
  51. * @brief Returns true if word is a spacer. Spacers contain just a space
  52. * of a certain length with no actual characters.
  53. */
  54. bool isSpacer() const { return mSpacer; }
  55. /**
  56. * @brief Returns the number of characters in the word.
  57. */
  58. UINT32 getNumChars() const { return mLastChar == nullptr ? 0 : (mCharsEnd - mCharsStart + 1); }
  59. /**
  60. * @brief Returns the index of the starting character in the word.
  61. */
  62. UINT32 getCharsStart() const { return mCharsStart; }
  63. /**
  64. * @brief Returns the index of the last character in the word.
  65. */
  66. UINT32 getCharsEnd() const { return mCharsEnd; }
  67. private:
  68. UINT32 mCharsStart, mCharsEnd;
  69. UINT32 mWidth;
  70. UINT32 mHeight;
  71. const CHAR_DESC* mLastChar;
  72. bool mSpacer;
  73. UINT32 mSpaceWidth;
  74. };
  75. /**
  76. * @brief Contains information about a single texture page that contains rendered
  77. * character data.
  78. *
  79. * @note Due to the way allocation is handled, this class is not allowed to have a destructor.
  80. */
  81. struct PageInfo
  82. {
  83. UINT32 numQuads;
  84. HTexture texture;
  85. };
  86. public:
  87. /**
  88. * @brief Represents a single line as a set of words.
  89. *
  90. * @note Due to the way allocation is handled, this class is not allowed to have a destructor.
  91. */
  92. class BS_CORE_EXPORT TextLine
  93. {
  94. public:
  95. /**
  96. * @brief Returns width of the line in pixels.
  97. */
  98. UINT32 getWidth() const { return mWidth; }
  99. /**
  100. * @brief Returns height of the line in pixels.
  101. */
  102. UINT32 getHeight() const { return mHeight; }
  103. /**
  104. * @brief Returns an offset used to separate two lines.
  105. */
  106. UINT32 getYOffset() const { return mTextData->getLineHeight(); }
  107. /**
  108. * @brief Fills the vertex/uv/index buffers for the specified page, with all the character data
  109. * needed for rendering.
  110. *
  111. * @param page The page.
  112. * @param [out] vertices Pre-allocated array where character vertices will be written.
  113. * @param [out] uvs Pre-allocated array where character uv coordinates will be written.
  114. * @param [out] indexes Pre-allocated array where character indices will be written.
  115. * @param offset Offsets the location at which the method writes to the buffers.
  116. * Counted as number of quads.
  117. * @param size Total number of quads that can fit into the specified buffers.
  118. *
  119. * @return Number of quads that were written.
  120. */
  121. UINT32 fillBuffer(UINT32 page, Vector2* vertices, Vector2* uvs, UINT32* indexes, UINT32 offset, UINT32 size) const;
  122. /**
  123. * @brief Returns the total number of characters on this line.
  124. */
  125. UINT32 getNumChars() const;
  126. /**
  127. * @brief Query if this line was created explicitly due to a newline character.
  128. * As opposed to a line that was created because a word couldn't fit on the previous line.
  129. */
  130. bool hasNewlineChar() const { return mHasNewline; }
  131. private:
  132. friend class TextData;
  133. /**
  134. * @brief Appends a new character to the line.
  135. *
  136. * @param charIdx Sequential index of the character in the original string.
  137. * @param desc Character description from the font.
  138. */
  139. void add(UINT32 charIdx, const CHAR_DESC& charDesc);
  140. /**
  141. * @brief Appends a space to the line.
  142. */
  143. void addSpace();
  144. /**
  145. * @brief Adds a new word to the line.
  146. *
  147. * @param wordIdx Sequential index of the word in the original string.
  148. * Spaces are counted as words as well.
  149. * @param word Description of the word.
  150. */
  151. void addWord(UINT32 wordIdx, const TextWord& word);
  152. /**
  153. * @brief Initializes the line. Must be called after construction.
  154. */
  155. void init(TextData* textData);
  156. /**
  157. * @brief Finalizes the line. Do not add new characters/words after a line has
  158. * been finalized.
  159. *
  160. * @param hasNewlineChar Set to true if line was create due to an explicit newline char.
  161. * As opposed to a line that was created because a word couldn't fit
  162. * on the previous line.
  163. */
  164. void finalize(bool hasNewlineChar);
  165. /**
  166. * @brief Returns true if the line contains no words.
  167. */
  168. bool isEmpty() const { return mIsEmpty; }
  169. /**
  170. * @brief Removes last word from the line and returns its sequential index.
  171. */
  172. UINT32 removeLastWord();
  173. /**
  174. * @brief Calculates the line width and height in pixels.
  175. */
  176. void calculateBounds();
  177. private:
  178. TextData* mTextData;
  179. UINT32 mWordsStart, mWordsEnd;
  180. UINT32 mWidth;
  181. UINT32 mHeight;
  182. bool mIsEmpty;
  183. bool mHasNewline;
  184. };
  185. public:
  186. /**
  187. * @brief Initializes a new text data using the specified string and font. Text will attempt to fit into
  188. * the provided area. It will wrap words to new line if it doesn't fit and is enabled. If word wrap
  189. * isn't possible the text will be clipped. If the specified area is zero size then the text
  190. * will not be clipped or word wrapped in any way.
  191. *
  192. * After this object is constructed you may call various getter methods to get needed information.
  193. */
  194. BS_CORE_EXPORT TextData(const WString& text, const HFont& font, UINT32 fontSize, UINT32 width = 0, UINT32 height = 0, bool wordWrap = false);
  195. BS_CORE_EXPORT ~TextData();
  196. /**
  197. * @brief Returns the number of lines that were generated.
  198. */
  199. BS_CORE_EXPORT UINT32 getNumLines() const { return mNumLines; }
  200. /**
  201. * @brief Returns the number of font pages references by the used characters.
  202. */
  203. BS_CORE_EXPORT UINT32 getNumPages() const { return mNumPageInfos; }
  204. /**
  205. * @brief Returns the height of a line in pixels.
  206. */
  207. BS_CORE_EXPORT UINT32 getLineHeight() const;
  208. /**
  209. * @brief Gets information describing a single line at the specified index.
  210. */
  211. BS_CORE_EXPORT const TextLine& getLine(UINT32 idx) const { return mLines[idx]; }
  212. /**
  213. * @brief Returns font texture for the provided page index.
  214. */
  215. BS_CORE_EXPORT const HTexture& getTextureForPage(UINT32 page) const;
  216. /**
  217. * @brief Returns the number of quads used by all the characters in the provided page.
  218. */
  219. BS_CORE_EXPORT UINT32 getNumQuadsForPage(UINT32 page) const { return mPageInfos[page].numQuads; }
  220. /**
  221. * @brief Returns the width of the actual text in pixels.
  222. */
  223. BS_CORE_EXPORT UINT32 getWidth() const;
  224. /**
  225. * @brief Returns the height of the actual text in pixels.
  226. */
  227. BS_CORE_EXPORT UINT32 getHeight() const;
  228. private:
  229. friend class TextLine;
  230. /**
  231. * @brief Returns Y offset that determines the line on which the characters are placed.
  232. * In pixels.
  233. */
  234. INT32 getBaselineOffset() const;
  235. /**
  236. * @brief Returns the width of a single space in pixels.
  237. */
  238. UINT32 getSpaceWidth() const;
  239. /**
  240. * @brief Gets a description of a single character referenced by its sequential index
  241. * based on the original string.
  242. */
  243. const CHAR_DESC& getChar(UINT32 idx) const { return *mChars[idx]; }
  244. /**
  245. * @brief Gets a description of a single word referenced by its sequential index
  246. * based on the original string.
  247. */
  248. const TextWord& getWord(UINT32 idx) const { return mWords[idx]; }
  249. private:
  250. const CHAR_DESC** mChars;
  251. UINT32 mNumChars;
  252. TextWord* mWords;
  253. UINT32 mNumWords;
  254. TextLine* mLines;
  255. UINT32 mNumLines;
  256. PageInfo* mPageInfos;
  257. UINT32 mNumPageInfos;
  258. void* mData;
  259. HFont mFont;
  260. const FontData* mFontData;
  261. // Static buffers used to reduce runtime memory allocation
  262. private:
  263. static BS_THREADLOCAL bool BuffersInitialized;
  264. static BS_THREADLOCAL TextWord* WordBuffer;
  265. static BS_THREADLOCAL UINT32 WordBufferSize;
  266. static BS_THREADLOCAL UINT32 NextFreeWord;
  267. static BS_THREADLOCAL TextLine* LineBuffer;
  268. static BS_THREADLOCAL UINT32 LineBufferSize;
  269. static BS_THREADLOCAL UINT32 NextFreeLine;
  270. static BS_THREADLOCAL PageInfo* PageBuffer;
  271. static BS_THREADLOCAL UINT32 PageBufferSize;
  272. static BS_THREADLOCAL UINT32 NextFreePageInfo;
  273. /**
  274. * @brief Allocates an initial set of buffers that will be reused while parsing
  275. * text data.
  276. */
  277. static void initAlloc();
  278. /**
  279. * @brief Allocates a new word and adds it to the buffer. Returns index of the word
  280. * in the word buffer.
  281. *
  282. * @param spacer Specify true if the word is only to contain spaces. (Spaces are considered
  283. * a special type of word).
  284. */
  285. static UINT32 allocWord(bool spacer);
  286. /**
  287. * @brief Allocates a new line and adds it to the buffer. Returns index of the line
  288. * in the line buffer.
  289. */
  290. static UINT32 allocLine(TextData* textData);
  291. /**
  292. * @brief Resets all allocation counters.
  293. */
  294. static void deallocAll();
  295. /**
  296. * @brief Increments the count of characters for the referenced page, and optionally
  297. * creates page info if it doesn't already exist.
  298. */
  299. static void addCharToPage(UINT32 page, const FontData& fontData);
  300. };
  301. }