BsTextData.h 13 KB

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