/Irrlicht/CGUIFont.cpp

http://myjeh.googlecode.com/ · C++ · 571 lines · 417 code · 116 blank · 38 comment · 95 complexity · 68c342b1e269ecd14b01518387d284bc MD5 · raw file

  1. // Copyright (C) 2002-2010 Nikolaus Gebhardt
  2. // This file is part of the "Irrlicht Engine".
  3. // For conditions of distribution and use, see copyright notice in irrlicht.h
  4. #include "CGUIFont.h"
  5. #ifdef _IRR_COMPILE_WITH_GUI_
  6. #include "os.h"
  7. #include "IGUIEnvironment.h"
  8. #include "IXMLReader.h"
  9. #include "IReadFile.h"
  10. #include "IVideoDriver.h"
  11. #include "IGUISpriteBank.h"
  12. #include "CImage.h"
  13. namespace irr
  14. {
  15. namespace gui
  16. {
  17. //! constructor
  18. CGUIFont::CGUIFont(IGUIEnvironment *env, const io::path& filename)
  19. : Driver(0), SpriteBank(0), Environment(env), WrongCharacter(0),
  20. MaxHeight(0), GlobalKerningWidth(0), GlobalKerningHeight(0)
  21. {
  22. #ifdef _DEBUG
  23. setDebugName("CGUIFont");
  24. #endif
  25. if (Environment)
  26. {
  27. // don't grab environment, to avoid circular references
  28. Driver = Environment->getVideoDriver();
  29. SpriteBank = Environment->addEmptySpriteBank(filename);
  30. if (SpriteBank)
  31. SpriteBank->grab();
  32. }
  33. if (Driver)
  34. Driver->grab();
  35. setInvisibleCharacters ( L" " );
  36. }
  37. //! destructor
  38. CGUIFont::~CGUIFont()
  39. {
  40. if (Driver)
  41. Driver->drop();
  42. if (SpriteBank)
  43. SpriteBank->drop();
  44. }
  45. //! loads a font file from xml
  46. bool CGUIFont::load(io::IXMLReader* xml)
  47. {
  48. if (!SpriteBank)
  49. return false;
  50. while (xml->read())
  51. {
  52. if (io::EXN_ELEMENT == xml->getNodeType())
  53. {
  54. if (core::stringw(L"Texture") == xml->getNodeName())
  55. {
  56. // add a texture
  57. core::stringc fn = xml->getAttributeValue(L"filename");
  58. u32 i = (u32)xml->getAttributeValueAsInt(L"index");
  59. core::stringw alpha = xml->getAttributeValue(L"hasAlpha");
  60. while (i+1 > SpriteBank->getTextureCount())
  61. SpriteBank->addTexture(0);
  62. // disable mipmaps+filtering
  63. bool mipmap = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
  64. Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
  65. // load texture
  66. SpriteBank->setTexture(i, Driver->getTexture(fn));
  67. // set previous mip-map+filter state
  68. Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, mipmap);
  69. // couldn't load texture, abort.
  70. if (!SpriteBank->getTexture(i))
  71. {
  72. os::Printer::log("Unable to load all textures in the font, aborting", ELL_ERROR);
  73. _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
  74. return false;
  75. }
  76. else
  77. {
  78. // colorkey texture rather than alpha channel?
  79. if (alpha == core::stringw("false"))
  80. Driver->makeColorKeyTexture(SpriteBank->getTexture(i), core::position2di(0,0));
  81. }
  82. }
  83. else if (core::stringw(L"c") == xml->getNodeName())
  84. {
  85. // adding a character to this font
  86. SFontArea a;
  87. SGUISpriteFrame f;
  88. SGUISprite s;
  89. core::rect<s32> rectangle;
  90. a.underhang = xml->getAttributeValueAsInt(L"u");
  91. a.overhang = xml->getAttributeValueAsInt(L"o");
  92. a.spriteno = SpriteBank->getSprites().size();
  93. s32 texno = xml->getAttributeValueAsInt(L"i");
  94. // parse rectangle
  95. core::stringc rectstr = xml->getAttributeValue(L"r");
  96. wchar_t ch = xml->getAttributeValue(L"c")[0];
  97. const c8 *c = rectstr.c_str();
  98. s32 val;
  99. val = 0;
  100. while (*c >= '0' && *c <= '9')
  101. {
  102. val *= 10;
  103. val += *c - '0';
  104. c++;
  105. }
  106. rectangle.UpperLeftCorner.X = val;
  107. while (*c == L' ' || *c == L',') c++;
  108. val = 0;
  109. while (*c >= '0' && *c <= '9')
  110. {
  111. val *= 10;
  112. val += *c - '0';
  113. c++;
  114. }
  115. rectangle.UpperLeftCorner.Y = val;
  116. while (*c == L' ' || *c == L',') c++;
  117. val = 0;
  118. while (*c >= '0' && *c <= '9')
  119. {
  120. val *= 10;
  121. val += *c - '0';
  122. c++;
  123. }
  124. rectangle.LowerRightCorner.X = val;
  125. while (*c == L' ' || *c == L',') c++;
  126. val = 0;
  127. while (*c >= '0' && *c <= '9')
  128. {
  129. val *= 10;
  130. val += *c - '0';
  131. c++;
  132. }
  133. rectangle.LowerRightCorner.Y = val;
  134. CharacterMap.insert(ch,Areas.size());
  135. // make frame
  136. f.rectNumber = SpriteBank->getPositions().size();
  137. f.textureNumber = texno;
  138. // add frame to sprite
  139. s.Frames.push_back(f);
  140. s.frameTime = 0;
  141. // add rectangle to sprite bank
  142. SpriteBank->getPositions().push_back(rectangle);
  143. a.width = rectangle.getWidth();
  144. // add sprite to sprite bank
  145. SpriteBank->getSprites().push_back(s);
  146. // add character to font
  147. Areas.push_back(a);
  148. }
  149. }
  150. }
  151. // set bad character
  152. WrongCharacter = getAreaFromCharacter(L' ');
  153. setMaxHeight();
  154. return true;
  155. }
  156. void CGUIFont::setMaxHeight()
  157. {
  158. MaxHeight = 0;
  159. s32 t;
  160. core::array< core::rect<s32> >& p = SpriteBank->getPositions();
  161. for (u32 i=0; i<p.size(); ++i)
  162. {
  163. t = p[i].getHeight();
  164. if (t>MaxHeight)
  165. MaxHeight = t;
  166. }
  167. }
  168. //! loads a font file, native file needed, for texture parsing
  169. bool CGUIFont::load(io::IReadFile* file)
  170. {
  171. if (!Driver)
  172. return false;
  173. return loadTexture(Driver->createImageFromFile(file),
  174. file->getFileName());
  175. }
  176. //! loads a font file, native file needed, for texture parsing
  177. bool CGUIFont::load(const io::path& filename)
  178. {
  179. if (!Driver)
  180. return false;
  181. return loadTexture(Driver->createImageFromFile( filename ),
  182. filename);
  183. }
  184. //! load & prepare font from ITexture
  185. bool CGUIFont::loadTexture(video::IImage* image, const io::path& name)
  186. {
  187. if (!image)
  188. return false;
  189. s32 lowerRightPositions = 0;
  190. video::IImage* tmpImage=image;
  191. bool deleteTmpImage=false;
  192. switch(image->getColorFormat())
  193. {
  194. case video::ECF_R5G6B5:
  195. tmpImage = new video::CImage(video::ECF_A1R5G5B5,image->getDimension());
  196. image->copyTo(tmpImage);
  197. deleteTmpImage=true;
  198. break;
  199. case video::ECF_A1R5G5B5:
  200. case video::ECF_A8R8G8B8:
  201. break;
  202. case video::ECF_R8G8B8:
  203. tmpImage = new video::CImage(video::ECF_A8R8G8B8,image->getDimension());
  204. image->copyTo(tmpImage);
  205. deleteTmpImage=true;
  206. break;
  207. }
  208. readPositions(tmpImage, lowerRightPositions);
  209. WrongCharacter = getAreaFromCharacter(L' ');
  210. // output warnings
  211. if (!lowerRightPositions || !SpriteBank->getSprites().size())
  212. os::Printer::log("Either no upper or lower corner pixels in the font file. If this font was made using the new font tool, please load the XML file instead. If not, the font may be corrupted.", ELL_ERROR);
  213. else
  214. if (lowerRightPositions != (s32)SpriteBank->getPositions().size())
  215. os::Printer::log("The amount of upper corner pixels and the lower corner pixels is not equal, font file may be corrupted.", ELL_ERROR);
  216. bool ret = ( !SpriteBank->getSprites().empty() && lowerRightPositions );
  217. if ( ret )
  218. {
  219. bool flag[2];
  220. flag[0] = Driver->getTextureCreationFlag ( video::ETCF_ALLOW_NON_POWER_2 );
  221. flag[1] = Driver->getTextureCreationFlag ( video::ETCF_CREATE_MIP_MAPS );
  222. Driver->setTextureCreationFlag(video::ETCF_ALLOW_NON_POWER_2, true);
  223. Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false );
  224. SpriteBank->addTexture(Driver->addTexture(name, tmpImage));
  225. Driver->setTextureCreationFlag(video::ETCF_ALLOW_NON_POWER_2, flag[0] );
  226. Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, flag[1] );
  227. }
  228. if (deleteTmpImage)
  229. tmpImage->drop();
  230. image->drop();
  231. setMaxHeight();
  232. return ret;
  233. }
  234. void CGUIFont::readPositions(video::IImage* image, s32& lowerRightPositions)
  235. {
  236. const core::dimension2d<u32> size = image->getDimension();
  237. video::SColor colorTopLeft = image->getPixel(0,0);
  238. colorTopLeft.setAlpha(255);
  239. image->setPixel(0,0,colorTopLeft);
  240. video::SColor colorLowerRight = image->getPixel(1,0);
  241. video::SColor colorBackGround = image->getPixel(2,0);
  242. video::SColor colorBackGroundTransparent = 0;
  243. image->setPixel(1,0,colorBackGround);
  244. // start parsing
  245. core::position2d<s32> pos(0,0);
  246. for (pos.Y=0; pos.Y<(s32)size.Height; ++pos.Y)
  247. {
  248. for (pos.X=0; pos.X<(s32)size.Width; ++pos.X)
  249. {
  250. const video::SColor c = image->getPixel(pos.X, pos.Y);
  251. if (c == colorTopLeft)
  252. {
  253. image->setPixel(pos.X, pos.Y, colorBackGroundTransparent);
  254. SpriteBank->getPositions().push_back(core::rect<s32>(pos, pos));
  255. }
  256. else
  257. if (c == colorLowerRight)
  258. {
  259. // too many lower right points
  260. if (SpriteBank->getPositions().size()<=(u32)lowerRightPositions)
  261. {
  262. lowerRightPositions = 0;
  263. return;
  264. }
  265. image->setPixel(pos.X, pos.Y, colorBackGroundTransparent);
  266. SpriteBank->getPositions()[lowerRightPositions].LowerRightCorner = pos;
  267. // add frame to sprite bank
  268. SGUISpriteFrame f;
  269. f.rectNumber = lowerRightPositions;
  270. f.textureNumber = 0;
  271. SGUISprite s;
  272. s.Frames.push_back(f);
  273. s.frameTime = 0;
  274. SpriteBank->getSprites().push_back(s);
  275. // add character to font
  276. SFontArea a;
  277. a.overhang = 0;
  278. a.underhang = 0;
  279. a.spriteno = lowerRightPositions;
  280. a.width = SpriteBank->getPositions()[lowerRightPositions].getWidth();
  281. Areas.push_back(a);
  282. // map letter to character
  283. wchar_t ch = (wchar_t)(lowerRightPositions + 32);
  284. CharacterMap.set(ch, lowerRightPositions);
  285. ++lowerRightPositions;
  286. }
  287. else
  288. if (c == colorBackGround)
  289. image->setPixel(pos.X, pos.Y, colorBackGroundTransparent);
  290. }
  291. }
  292. }
  293. //! set an Pixel Offset on Drawing ( scale position on width )
  294. void CGUIFont::setKerningWidth(s32 kerning)
  295. {
  296. GlobalKerningWidth = kerning;
  297. }
  298. //! set an Pixel Offset on Drawing ( scale position on width )
  299. s32 CGUIFont::getKerningWidth(const wchar_t* thisLetter, const wchar_t* previousLetter) const
  300. {
  301. s32 ret = GlobalKerningWidth;
  302. if (thisLetter)
  303. {
  304. ret += Areas[getAreaFromCharacter(*thisLetter)].overhang;
  305. if (previousLetter)
  306. {
  307. ret += Areas[getAreaFromCharacter(*previousLetter)].underhang;
  308. }
  309. }
  310. return ret;
  311. }
  312. //! set an Pixel Offset on Drawing ( scale position on height )
  313. void CGUIFont::setKerningHeight(s32 kerning)
  314. {
  315. GlobalKerningHeight = kerning;
  316. }
  317. //! set an Pixel Offset on Drawing ( scale position on height )
  318. s32 CGUIFont::getKerningHeight () const
  319. {
  320. return GlobalKerningHeight;
  321. }
  322. //! returns the sprite number from a given character
  323. u32 CGUIFont::getSpriteNoFromChar(const wchar_t *c) const
  324. {
  325. return Areas[getAreaFromCharacter(*c)].spriteno;
  326. }
  327. s32 CGUIFont::getAreaFromCharacter(const wchar_t c) const
  328. {
  329. core::map<wchar_t, s32>::Node* n = CharacterMap.find(c);
  330. if (n)
  331. return n->getValue();
  332. else
  333. return WrongCharacter;
  334. }
  335. void CGUIFont::setInvisibleCharacters( const wchar_t *s )
  336. {
  337. Invisible = s;
  338. }
  339. //! returns the dimension of text
  340. core::dimension2d<u32> CGUIFont::getDimension(const wchar_t* text) const
  341. {
  342. core::dimension2d<u32> dim(0, 0);
  343. core::dimension2d<u32> thisLine(0, MaxHeight);
  344. for (const wchar_t* p = text; *p; ++p)
  345. {
  346. bool lineBreak=false;
  347. if (*p == L'\r') // Mac or Windows breaks
  348. {
  349. lineBreak = true;
  350. if (p[1] == L'\n') // Windows breaks
  351. ++p;
  352. }
  353. else if (*p == L'\n') // Unix breaks
  354. {
  355. lineBreak = true;
  356. }
  357. if (lineBreak)
  358. {
  359. dim.Height += thisLine.Height;
  360. if (dim.Width < thisLine.Width)
  361. dim.Width = thisLine.Width;
  362. thisLine.Width = 0;
  363. continue;
  364. }
  365. const SFontArea &area = Areas[getAreaFromCharacter(*p)];
  366. thisLine.Width += area.underhang;
  367. thisLine.Width += area.width + area.overhang + GlobalKerningWidth;
  368. }
  369. dim.Height += thisLine.Height;
  370. if (dim.Width < thisLine.Width)
  371. dim.Width = thisLine.Width;
  372. return dim;
  373. }
  374. //! draws some text and clips it to the specified rectangle if wanted
  375. void CGUIFont::draw(const core::stringw& text, const core::rect<s32>& position,
  376. video::SColor color,
  377. bool hcenter, bool vcenter, const core::rect<s32>* clip
  378. )
  379. {
  380. if (!Driver)
  381. return;
  382. core::dimension2d<s32> textDimension; // NOTE: don't make this u32 or the >> later on can fail when the dimension widht is < position width
  383. core::position2d<s32> offset = position.UpperLeftCorner;
  384. if (hcenter || vcenter || clip)
  385. textDimension = getDimension(text.c_str());
  386. if (hcenter)
  387. offset.X += (position.getWidth() - textDimension.Width) >> 1;
  388. if (vcenter)
  389. offset.Y += (position.getHeight() - textDimension.Height) >> 1;
  390. if (clip)
  391. {
  392. core::rect<s32> clippedRect(offset, textDimension);
  393. clippedRect.clipAgainst(*clip);
  394. if (!clippedRect.isValid())
  395. return;
  396. }
  397. core::array<u32> indices(text.size());
  398. core::array<core::position2di> offsets(text.size());
  399. for(u32 i = 0;i < text.size();i++)
  400. {
  401. wchar_t c = text[i];
  402. bool lineBreak=false;
  403. if ( c == L'\r') // Mac or Windows breaks
  404. {
  405. lineBreak = true;
  406. if ( text[i + 1] == L'\n') // Windows breaks
  407. c = text[++i];
  408. }
  409. else if ( c == L'\n') // Unix breaks
  410. {
  411. lineBreak = true;
  412. }
  413. if (lineBreak)
  414. {
  415. offset.Y += MaxHeight;
  416. offset.X = position.UpperLeftCorner.X;
  417. if ( hcenter )
  418. {
  419. offset.X += (position.getWidth() - textDimension.Width) >> 1;
  420. }
  421. continue;
  422. }
  423. SFontArea& area = Areas[getAreaFromCharacter(c)];
  424. offset.X += area.underhang;
  425. if ( Invisible.findFirst ( c ) < 0 )
  426. {
  427. indices.push_back(area.spriteno);
  428. offsets.push_back(offset);
  429. }
  430. offset.X += area.width + area.overhang + GlobalKerningWidth;
  431. }
  432. SpriteBank->draw2DSpriteBatch(indices, offsets, clip, color);
  433. }
  434. //! Calculates the index of the character in the text which is on a specific position.
  435. s32 CGUIFont::getCharacterFromPos(const wchar_t* text, s32 pixel_x) const
  436. {
  437. s32 x = 0;
  438. s32 idx = 0;
  439. while (text[idx])
  440. {
  441. const SFontArea& a = Areas[getAreaFromCharacter(text[idx])];
  442. x += a.width + a.overhang + a.underhang + GlobalKerningWidth;
  443. if (x >= pixel_x)
  444. return idx;
  445. ++idx;
  446. }
  447. return -1;
  448. }
  449. IGUISpriteBank* CGUIFont::getSpriteBank() const
  450. {
  451. return SpriteBank;
  452. }
  453. } // end namespace gui
  454. } // end namespace irr
  455. #endif // _IRR_COMPILE_WITH_GUI_