PageRenderTime 46ms CodeModel.GetById 18ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/Irrlicht/CGUIFont.cpp

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