fork download
  1. // Copyright (C) 2002-2012 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. // Sep 11, 2014; Jonathan Frisch
  5.  
  6. #include "CGUIFontBitmapScalable.h"
  7. #ifdef _IRR_COMPILE_WITH_GUI_
  8.  
  9. namespace irr
  10. {
  11. namespace gui
  12. {
  13. //! constructor
  14. CGUIFontBitmapScalable::CGUIFontBitmapScalable(IGUIEnvironment *env, const io::path& filename, ILogger* logger)
  15. : Driver(0), SpriteBank(0), Environment(env), Logger(logger), fontScale(1.f), baseFontScale(.5f),
  16. WrongCharacter(0), MaxHeight(0), GlobalKerningWidth(0), GlobalKerningHeight(0)
  17. {
  18. #ifdef _DEBUG
  19. setDebugName("CGUIFontBitmapScalable");
  20. #endif
  21.  
  22. if (Environment)
  23. {
  24. // don't grab environment, to avoid circular references
  25. Driver = Environment->getVideoDriver();
  26.  
  27. SpriteBank = Environment->getSpriteBank(filename);
  28. if (!SpriteBank) // could be default-font which has no file
  29. SpriteBank = Environment->addEmptySpriteBank(filename);
  30. if (SpriteBank)
  31. SpriteBank->grab();
  32. }
  33.  
  34. if (Driver)
  35. Driver->grab();
  36.  
  37. if(Logger)
  38. Logger->grab();
  39.  
  40. setInvisibleCharacters ( L" " );
  41. }
  42.  
  43.  
  44. //! destructor
  45. CGUIFontBitmapScalable::~CGUIFontBitmapScalable()
  46. {
  47. if(Logger)
  48. Logger->drop();
  49.  
  50. if (Driver)
  51. Driver->drop();
  52.  
  53. if (SpriteBank)
  54. {
  55. SpriteBank->drop();
  56. // TODO: spritebank still exists in gui-environment and should be removed here when it's
  57. // reference-count is 1. Just can't do that from here at the moment.
  58. // But spritebank would not be able to drop textures anyway because those are in texture-cache
  59. // where they can't be removed unless materials start reference-couting 'em.
  60. }
  61. }
  62.  
  63.  
  64. //! loads a font file from xml
  65. bool CGUIFontBitmapScalable::load(io::IXMLReader* xml, const io::path& directory)
  66. {
  67. if (!SpriteBank)
  68. return false;
  69.  
  70. SpriteBank->clear();
  71.  
  72. while (xml->read())
  73. {
  74. if (io::EXN_ELEMENT == xml->getNodeType())
  75. {
  76. if (core::stringw(L"Texture") == xml->getNodeName())
  77. {
  78. // add a texture
  79. core::stringc fn = xml->getAttributeValue(L"filename");
  80. u32 i = (u32)xml->getAttributeValueAsInt(L"index");
  81. core::stringw alpha = xml->getAttributeValue(L"hasAlpha");
  82.  
  83. while (i+1 > SpriteBank->getTextureCount())
  84. SpriteBank->addTexture(0);
  85.  
  86. // disable mipmaps+filtering
  87. bool mipmap = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
  88. Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
  89.  
  90. // load texture
  91. io::path textureFullName = core::mergeFilename(directory, fn);
  92. SpriteBank->setTexture(i, Driver->getTexture(textureFullName));
  93.  
  94. // set previous mip-map+filter state
  95. Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, mipmap);
  96.  
  97. // couldn't load texture, abort.
  98. if (!SpriteBank->getTexture(i))
  99. {
  100. if(Logger)
  101. Logger->log("Unable to load all textures in the font, aborting", ELL_ERROR);
  102. _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
  103. return false;
  104. }
  105. else
  106. {
  107. // colorkey texture rather than alpha channel?
  108. if (alpha == core::stringw("false"))
  109. Driver->makeColorKeyTexture(SpriteBank->getTexture(i), core::position2di(0,0));
  110. }
  111. }
  112. else if (core::stringw(L"c") == xml->getNodeName())
  113. {
  114. // adding a character to this font
  115. SFontArea a;
  116. SGUISpriteFrame f;
  117. SGUISprite s;
  118. core::rect<s32> rectangle;
  119.  
  120. a.underhang = xml->getAttributeValueAsInt(L"u");
  121. a.overhang = xml->getAttributeValueAsInt(L"o");
  122. a.spriteno = SpriteBank->getSprites().size();
  123. s32 texno = xml->getAttributeValueAsInt(L"i");
  124.  
  125. // parse rectangle
  126. core::stringc rectstr = xml->getAttributeValue(L"r");
  127. wchar_t ch = xml->getAttributeValue(L"c")[0];
  128.  
  129. const c8 *c = rectstr.c_str();
  130. s32 val;
  131. val = 0;
  132. while (*c >= '0' && *c <= '9')
  133. {
  134. val *= 10;
  135. val += *c - '0';
  136. c++;
  137. }
  138. rectangle.UpperLeftCorner.X = val;
  139. while (*c == L' ' || *c == L',') c++;
  140.  
  141. val = 0;
  142. while (*c >= '0' && *c <= '9')
  143. {
  144. val *= 10;
  145. val += *c - '0';
  146. c++;
  147. }
  148. rectangle.UpperLeftCorner.Y = val;
  149. while (*c == L' ' || *c == L',') c++;
  150.  
  151. val = 0;
  152. while (*c >= '0' && *c <= '9')
  153. {
  154. val *= 10;
  155. val += *c - '0';
  156. c++;
  157. }
  158. rectangle.LowerRightCorner.X = val;
  159. while (*c == L' ' || *c == L',') c++;
  160.  
  161. val = 0;
  162. while (*c >= '0' && *c <= '9')
  163. {
  164. val *= 10;
  165. val += *c - '0';
  166. c++;
  167. }
  168. rectangle.LowerRightCorner.Y = val;
  169.  
  170. CharacterMap.insert(ch,Areas.size());
  171.  
  172. // make frame
  173. f.rectNumber = SpriteBank->getPositions().size();
  174. f.textureNumber = texno;
  175.  
  176. // add frame to sprite
  177. s.Frames.push_back(f);
  178. s.frameTime = 0;
  179.  
  180. // add rectangle to sprite bank
  181. SpriteBank->getPositions().push_back(rectangle);
  182. a.width = rectangle.getWidth();
  183.  
  184. // add sprite to sprite bank
  185. SpriteBank->getSprites().push_back(s);
  186.  
  187. // add character to font
  188. Areas.push_back(a);
  189. }
  190. }
  191. }
  192.  
  193. // set bad character
  194. WrongCharacter = getAreaFromCharacter(L' ');
  195.  
  196. setMaxHeight();
  197.  
  198. return true;
  199. }
  200.  
  201.  
  202. void CGUIFontBitmapScalable::setMaxHeight()
  203. {
  204. if ( !SpriteBank )
  205. return;
  206.  
  207. MaxHeight = 0;
  208. s32 t;
  209.  
  210. core::array< core::rect<s32> >& p = SpriteBank->getPositions();
  211.  
  212. for (u32 i=0; i<p.size(); ++i)
  213. {
  214. t = p[i].getHeight();
  215. if (t>MaxHeight)
  216. MaxHeight = t;
  217. }
  218.  
  219. }
  220.  
  221.  
  222. //! loads a font file, native file needed, for texture parsing
  223. bool CGUIFontBitmapScalable::load(io::IReadFile* file)
  224. {
  225. if (!Driver)
  226. return false;
  227.  
  228. return loadTexture(Driver->createImageFromFile(file),
  229. file->getFileName());
  230. }
  231.  
  232.  
  233. //! loads a font file, native file needed, for texture parsing
  234. bool CGUIFontBitmapScalable::load(const io::path& filename)
  235. {
  236. if (!Driver)
  237. return false;
  238.  
  239. return loadTexture(Driver->createImageFromFile( filename ),
  240. filename);
  241. }
  242.  
  243.  
  244. //! load & prepare font from ITexture
  245. bool CGUIFontBitmapScalable::loadTexture(video::IImage* image, const io::path& name)
  246. {
  247. if (!image || !SpriteBank)
  248. return false;
  249.  
  250. s32 lowerRightPositions = 0;
  251.  
  252. video::IImage* tmpImage=image;
  253. bool deleteTmpImage=false;
  254. switch(image->getColorFormat())
  255. {
  256. case video::ECF_R5G6B5:
  257. tmpImage = Driver->createImage(video::ECF_A1R5G5B5,image->getDimension());
  258. image->copyTo(tmpImage);
  259. deleteTmpImage=true;
  260. break;
  261. case video::ECF_A1R5G5B5:
  262. case video::ECF_A8R8G8B8:
  263. break;
  264. case video::ECF_R8G8B8:
  265. tmpImage = Driver->createImage(video::ECF_A8R8G8B8,image->getDimension());
  266. image->copyTo(tmpImage);
  267. deleteTmpImage=true;
  268. break;
  269. default:
  270. if(Logger)
  271. Logger->log("Unknown texture format provided for CGUIFontBitmapScalable::loadTexture", ELL_ERROR);
  272. return false;
  273. }
  274. readPositions(tmpImage, lowerRightPositions);
  275.  
  276. WrongCharacter = getAreaFromCharacter(L' ');
  277.  
  278. // output warnings
  279. if (!lowerRightPositions || !SpriteBank->getSprites().size())
  280. {
  281. if(Logger)
  282. Logger->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);
  283. }
  284. else if (lowerRightPositions != (s32)SpriteBank->getPositions().size())
  285. {
  286. if(Logger)
  287. Logger->log("The amount of upper corner pixels and the lower corner pixels is not equal, font file may be corrupted.", ELL_ERROR);
  288. }
  289.  
  290. bool ret = ( !SpriteBank->getSprites().empty() && lowerRightPositions );
  291.  
  292. if ( ret )
  293. {
  294. bool flag[2];
  295. flag[0] = Driver->getTextureCreationFlag ( video::ETCF_ALLOW_NON_POWER_2 );
  296. flag[1] = Driver->getTextureCreationFlag ( video::ETCF_CREATE_MIP_MAPS );
  297.  
  298. Driver->setTextureCreationFlag(video::ETCF_ALLOW_NON_POWER_2, true);
  299. Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false );
  300.  
  301. SpriteBank->addTexture(Driver->addTexture(name, tmpImage));
  302.  
  303. Driver->setTextureCreationFlag(video::ETCF_ALLOW_NON_POWER_2, flag[0] );
  304. Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, flag[1] );
  305. }
  306. if (deleteTmpImage)
  307. tmpImage->drop();
  308. image->drop();
  309.  
  310. setMaxHeight();
  311.  
  312. return ret;
  313. }
  314.  
  315.  
  316. void CGUIFontBitmapScalable::readPositions(video::IImage* image, s32& lowerRightPositions)
  317. {
  318. if (!SpriteBank )
  319. return;
  320.  
  321. const core::dimension2d<u32> size = image->getDimension();
  322.  
  323. video::SColor colorTopLeft = image->getPixel(0,0);
  324. colorTopLeft.setAlpha(255);
  325. image->setPixel(0,0,colorTopLeft);
  326. video::SColor colorLowerRight = image->getPixel(1,0);
  327. video::SColor colorBackGround = image->getPixel(2,0);
  328. video::SColor colorBackGroundTransparent = 0;
  329.  
  330. image->setPixel(1,0,colorBackGround);
  331.  
  332. // start parsing
  333.  
  334. core::position2d<s32> pos(0,0);
  335. for (pos.Y=0; pos.Y<(s32)size.Height; ++pos.Y)
  336. {
  337. for (pos.X=0; pos.X<(s32)size.Width; ++pos.X)
  338. {
  339. const video::SColor c = image->getPixel(pos.X, pos.Y);
  340. if (c == colorTopLeft)
  341. {
  342. image->setPixel(pos.X, pos.Y, colorBackGroundTransparent);
  343. SpriteBank->getPositions().push_back(core::rect<s32>(pos, pos));
  344. }
  345. else
  346. if (c == colorLowerRight)
  347. {
  348. // too many lower right points
  349. if (SpriteBank->getPositions().size()<=(u32)lowerRightPositions)
  350. {
  351. lowerRightPositions = 0;
  352. return;
  353. }
  354.  
  355. image->setPixel(pos.X, pos.Y, colorBackGroundTransparent);
  356. SpriteBank->getPositions()[lowerRightPositions].LowerRightCorner = pos;
  357. // add frame to sprite bank
  358. SGUISpriteFrame f;
  359. f.rectNumber = lowerRightPositions;
  360. f.textureNumber = 0;
  361. SGUISprite s;
  362. s.Frames.push_back(f);
  363. s.frameTime = 0;
  364. SpriteBank->getSprites().push_back(s);
  365. // add character to font
  366. SFontArea a;
  367. a.overhang = 0;
  368. a.underhang = 0;
  369. a.spriteno = lowerRightPositions;
  370. a.width = SpriteBank->getPositions()[lowerRightPositions].getWidth();
  371. a.height = SpriteBank->getPositions()[lowerRightPositions].getHeight();
  372. Areas.push_back(a);
  373. // map letter to character
  374. wchar_t ch = (wchar_t)(lowerRightPositions + 32);
  375. CharacterMap.set(ch, lowerRightPositions);
  376.  
  377. ++lowerRightPositions;
  378. }
  379. else
  380. if (c == colorBackGround)
  381. image->setPixel(pos.X, pos.Y, colorBackGroundTransparent);
  382. }
  383. }
  384. }
  385.  
  386.  
  387. //! set an Pixel Offset on Drawing ( scale position on width )
  388. void CGUIFontBitmapScalable::setKerningWidth(s32 kerning)
  389. {
  390. GlobalKerningWidth = kerning;
  391. }
  392.  
  393.  
  394. //! set an Pixel Offset on Drawing ( scale position on width )
  395. s32 CGUIFontBitmapScalable::getKerningWidth(const wchar_t* thisLetter, const wchar_t* previousLetter) const
  396. {
  397. s32 ret = GlobalKerningWidth;
  398.  
  399. if (thisLetter)
  400. {
  401. ret += Areas[getAreaFromCharacter(*thisLetter)].overhang * fontScale * baseFontScale;
  402.  
  403. if (previousLetter)
  404. {
  405. ret += Areas[getAreaFromCharacter(*previousLetter)].underhang * fontScale * baseFontScale;
  406. }
  407. }
  408.  
  409. return ret;
  410. }
  411.  
  412.  
  413. //! set an Pixel Offset on Drawing ( scale position on height )
  414. void CGUIFontBitmapScalable::setKerningHeight(s32 kerning)
  415. {
  416. GlobalKerningHeight = kerning;
  417. }
  418.  
  419.  
  420. //! set an Pixel Offset on Drawing ( scale position on height )
  421. s32 CGUIFontBitmapScalable::getKerningHeight () const
  422. {
  423. return GlobalKerningHeight;
  424. }
  425.  
  426.  
  427. //! returns the sprite number from a given character
  428. u32 CGUIFontBitmapScalable::getSpriteNoFromChar(const wchar_t *c) const
  429. {
  430. return Areas[getAreaFromCharacter(*c)].spriteno;
  431. }
  432.  
  433.  
  434. s32 CGUIFontBitmapScalable::getAreaFromCharacter(const wchar_t c) const
  435. {
  436. core::map<wchar_t, s32>::Node* n = CharacterMap.find(c);
  437. if (n)
  438. return n->getValue();
  439. else
  440. return WrongCharacter;
  441. }
  442.  
  443. void CGUIFontBitmapScalable::setInvisibleCharacters( const wchar_t *s )
  444. {
  445. Invisible = s;
  446. }
  447.  
  448.  
  449. //! returns the dimension of text
  450. core::dimension2d<u32> CGUIFontBitmapScalable::getDimension(const wchar_t* text) const
  451. {
  452. core::dimension2d<u32> dim(0, 0);
  453. core::dimension2d<u32> thisLine(0, MaxHeight);
  454.  
  455. for (const wchar_t* p = text; *p; ++p)
  456. {
  457. bool lineBreak=false;
  458. if (*p == L'\r') // Mac or Windows breaks
  459. {
  460. lineBreak = true;
  461. if (p[1] == L'\n') // Windows breaks
  462. ++p;
  463. }
  464. else if (*p == L'\n') // Unix breaks
  465. {
  466. lineBreak = true;
  467. }
  468. if (lineBreak)
  469. {
  470. dim.Height += thisLine.Height;
  471. if (dim.Width < thisLine.Width)
  472. dim.Width = thisLine.Width;
  473. thisLine.Width = 0;
  474. continue;
  475. }
  476.  
  477. const SFontArea &area = Areas[getAreaFromCharacter(*p)];
  478.  
  479. thisLine.Width += area.underhang * fontScale * baseFontScale;
  480. thisLine.Width += area.width * fontScale * baseFontScale + area.overhang * fontScale * baseFontScale + GlobalKerningWidth;
  481. }
  482.  
  483. dim.Height += thisLine.Height;
  484. if (dim.Width < thisLine.Width)
  485. dim.Width = thisLine.Width;
  486.  
  487. return dim;
  488. }
  489.  
  490. //! draws some text and clips it to the specified rectangle if wanted
  491. void CGUIFontBitmapScalable::draw(const core::stringw& text, const core::rect<s32>& position,
  492. video::SColor color,
  493. bool hcenter, bool vcenter, const core::rect<s32>* clip
  494. )
  495. {
  496. if (!Driver || !SpriteBank)
  497. return;
  498.  
  499. core::dimension2d<s32> textDimension; // NOTE: don't make this u32 or the >> later on can fail when the dimension width is < position width
  500. core::recti offset(position);
  501.  
  502. if (hcenter || vcenter || clip)
  503. textDimension = getDimension(text.c_str());
  504.  
  505. if (hcenter)
  506. {
  507. offset.UpperLeftCorner.X += (position.getWidth() - textDimension.Width) >> 1;
  508. }
  509.  
  510. if (vcenter)
  511. {
  512. offset.UpperLeftCorner.Y += (position.getHeight() - textDimension.Height) >> 1;
  513. }
  514.  
  515. if (clip)
  516. {
  517. core::rect<s32> clippedRect(offset);//, textDimension);
  518. clippedRect.clipAgainst(*clip);
  519. if (!clippedRect.isValid())
  520. return;
  521. }
  522.  
  523. core::array<u32> indices(text.size());
  524. core::array<core::recti> offsets(text.size());
  525.  
  526. for(u32 i = 0;i < text.size();i++)
  527. {
  528. wchar_t c = text[i];
  529.  
  530. bool lineBreak=false;
  531. if ( c == L'\r') // Mac or Windows breaks
  532. {
  533. lineBreak = true;
  534. if ( text[i + 1] == L'\n') // Windows breaks
  535. c = text[++i];
  536. }
  537. else if ( c == L'\n') // Unix breaks
  538. {
  539. lineBreak = true;
  540. }
  541.  
  542. if (lineBreak)
  543. {
  544. offset.UpperLeftCorner.Y += MaxHeight;
  545. offset.UpperLeftCorner.X = position.UpperLeftCorner.X;
  546.  
  547. if ( hcenter )
  548. {
  549. offset.UpperLeftCorner.X += (position.getWidth() - textDimension.Width) >> 1;
  550. }
  551. continue;
  552. }
  553.  
  554. SFontArea& area = Areas[getAreaFromCharacter(c)];
  555.  
  556. offset.UpperLeftCorner.X += area.underhang * fontScale * baseFontScale;
  557. offset.LowerRightCorner.X = area.width * fontScale * baseFontScale + offset.UpperLeftCorner.X + area.underhang * fontScale * baseFontScale;
  558. offset.LowerRightCorner.Y = area.height * fontScale * baseFontScale + offset.UpperLeftCorner.Y;
  559. if ( Invisible.findFirst ( c ) < 0 )
  560. {
  561. indices.push_back(area.spriteno);
  562. offsets.push_back(offset);
  563. }
  564.  
  565. offset.UpperLeftCorner.X += area.width * fontScale * baseFontScale + area.overhang * fontScale * baseFontScale + GlobalKerningWidth;
  566. }
  567.  
  568. video::SColor colors[4] = {color, color, color, color};
  569. for(u32 i = 0; i < offsets.size(); i++)
  570. {
  571. SpriteBank->draw2DSprite(indices[i], offsets[i], clip, colors);
  572. }
  573. }
  574.  
  575.  
  576. //! Calculates the index of the character in the text which is on a specific position.
  577. s32 CGUIFontBitmapScalable::getCharacterFromPos(const wchar_t* text, s32 pixel_x) const
  578. {
  579. s32 x = 0;
  580. s32 idx = 0;
  581.  
  582. while (text[idx])
  583. {
  584. const SFontArea& a = Areas[getAreaFromCharacter(text[idx])];
  585.  
  586. x += a.width * fontScale * baseFontScale + a.overhang * fontScale * baseFontScale + a.underhang * fontScale * baseFontScale + GlobalKerningWidth;
  587.  
  588. if (x >= pixel_x)
  589. return idx;
  590.  
  591. ++idx;
  592. }
  593.  
  594. return -1;
  595. }
  596.  
  597. IGUISpriteBank* CGUIFontBitmapScalable::getSpriteBank() const
  598. {
  599. return SpriteBank;
  600. }
  601.  
  602. } // end namespace gui
  603. } // end namespace irr
  604.  
  605. #endif // _IRR_COMPILE_WITH_GUI_
  606.  
Compilation error #stdin compilation error #stdout 0s 0KB
stdin
Standard input is empty
compilation info
prog.cpp:6:36: fatal error: CGUIFontBitmapScalable.h: No such file or directory
 #include "CGUIFontBitmapScalable.h"
                                    ^
compilation terminated.
stdout
Standard output is empty