lugre_ogrefonthelper.h

Go to the documentation of this file.
00001 /*
00002 http://www.opensource.org/licenses/mit-license.php  (MIT-License)
00003 
00004 Copyright (c) 2007 Lugre-Team
00005 
00006 Permission is hereby granted, free of charge, to any person obtaining a copy
00007 of this software and associated documentation files (the "Software"), to deal
00008 in the Software without restriction, including without limitation the rights
00009 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00010 copies of the Software, and to permit persons to whom the Software is
00011 furnished to do so, subject to the following conditions:
00012 
00013 The above copyright notice and this permission notice shall be included in
00014 all copies or substantial portions of the Software.
00015 
00016 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00017 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00018 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00019 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00020 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00021 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00022 THE SOFTWARE.
00023 */
00024 #ifndef LUGRE_OGREFONTHELPER_H
00025 #define LUGRE_OGREFONTHELPER_H
00026 
00028 #include <Ogre.h>
00029 #include <OgreFont.h>
00030 #include <OgreTextAreaOverlayElement.h>
00031 
00032 
00033 namespace Lugre {
00034 
00037 class cOgreFontHelper { public:
00038     typedef Ogre::UTFString::unicode_char   unicode_char;
00039     typedef Ogre::UTFString::const_iterator itor;
00040     
00042     enum eAlignment {
00043         Align_Left,
00044         Align_Center,
00045         Align_Right,
00046     };
00047     
00048     Ogre::FontPtr   mpFont;
00049     eAlignment      mAlign;
00050     float           mfCharHeight;
00051     float           mfLineHeight;
00052     float           mfSpaceWidth;
00053     float           mfTabWidth;
00054     float           mfGlyphWidthFactor;
00055     float           mfWrapMaxW;
00056     
00059     cOgreFontHelper (Ogre::FontPtr mpFont,const float mfGlyphWidthFactor,const float mfCharHeight,
00060                     const float mfSpaceWidth,const float mfWrapMaxW=0,eAlignment mAlign=Align_Left)
00061         : mpFont(mpFont), mfGlyphWidthFactor(mfGlyphWidthFactor), mfCharHeight(mfCharHeight), 
00062           mfSpaceWidth(mfSpaceWidth), mfLineHeight(mfCharHeight), mfWrapMaxW(mfWrapMaxW), mAlign(mAlign) {
00063         mfTabWidth = 4*mfSpaceWidth; // only simple tabs for now
00064     }
00065     
00067     static eAlignment   Alignment   (Ogre::TextAreaOverlayElement::Alignment align) {
00068         switch (align) {
00069             case Ogre::TextAreaOverlayElement::Center: return Align_Center;
00070             case Ogre::TextAreaOverlayElement::Right: return Align_Right;
00071             default: return Align_Left;
00072         }
00073     }
00074     
00076     static eAlignment   Alignment   (Ogre::GuiHorizontalAlignment align) {
00077         switch (align) {
00078             case Ogre::GHA_CENTER: return Align_Center;
00079             case Ogre::GHA_RIGHT: return Align_Right;
00080             default: return Align_Left;
00081         }
00082     }
00083     
00085     enum {
00086         UNICODE_NEL     = 0x0085,
00087         UNICODE_CR      = 0x000D,
00088         UNICODE_LF      = 0x000A,
00089         UNICODE_TAB     = 0x0009,
00090         UNICODE_SPACE   = 0x0020,
00091         UNICODE_ZERO    = 0x0030,
00092     };
00093     
00094     static inline bool  IsTab           (unicode_char c) { return c == UNICODE_TAB; }
00095     static inline bool  IsSpace         (unicode_char c) { return c == UNICODE_SPACE; }
00096     static inline bool  IsNewLine       (unicode_char c) { return c == UNICODE_CR || c == UNICODE_LF || c == UNICODE_NEL; }
00097     static inline bool  IsWhiteSpace    (unicode_char c) { return IsTab(c) || IsSpace(c) || IsNewLine(c); }
00098     static inline bool  IsCRLF          (unicode_char a,unicode_char b) { return a == UNICODE_CR && b == UNICODE_LF; }
00099     
00100     static inline bool  IsTab           (itor i) { return IsTab(        i.getCharacter()); }
00101     static inline bool  IsSpace         (itor i) { return IsSpace(      i.getCharacter()); }
00102     static inline bool  IsNewLine       (itor i) { return IsNewLine(    i.getCharacter()); }
00103     static inline bool  IsWhiteSpace    (itor i) { return IsWhiteSpace( i.getCharacter()); }
00104     
00105     // measurement 
00106     
00107     inline float GetCharWidth(unicode_char c) {
00108         if (IsNewLine(c)) return 0;
00109         if (IsTab(c)) return mfTabWidth;
00110         return IsSpace(c) ? mfSpaceWidth : (mpFont->getGlyphAspectRatio(c) * mfGlyphWidthFactor);
00111     }
00112     
00113     inline float    CalcLineLen (itor i,const itor iEnd) {
00114         float len = 0.0;
00115         unicode_char c = 0;
00116         bool bFirstChar = true;
00117         for (;i != iEnd;) {
00118             unicode_char lastc = c;
00119             c = i.getCharacter();
00120             if (IsNewLine(c)) break;
00121             if (!bFirstChar && mfWrapMaxW > 0 && TestAutoWrap(i,iEnd,len,IsWhiteSpace(lastc))) break;
00122             ++i;
00123             len += GetCharWidth(c);
00124             bFirstChar = false;
00125         }
00126         return len;
00127     }
00128     
00130     inline float    CalcWordLen (itor i,const itor iEnd) {
00131         float len = 0.0;
00132         for (;i != iEnd;++i) {
00133             unicode_char c = i.getCharacter();
00134             if (IsWhiteSpace(c)) break;
00135             len += GetCharWidth(c);
00136         }
00137         return len;
00138     }
00139     
00140     bool    TestAutoWrap    (itor i,const itor iEnd,const float x,const bool bKeepWords) {
00141         if (i == iEnd) return false;
00142         bool bDebug = false;
00143         unicode_char c = i.getCharacter();
00144         if (mfWrapMaxW > 0 && !IsWhiteSpace(c)) {
00145             if (bDebug) printf(" aw? charlen=%f",GetCharWidth(c));
00146             if (x + GetCharWidth(c) > mfWrapMaxW) { // forced autowrap without considering word boundaries
00147                 if (bDebug) printf(" forcedWrap\n");
00148                 return true;
00149             } else if (bKeepWords) {
00150                 float wordlen = CalcWordLen(i,iEnd); // = 0 if mCur points to whitespace
00151                 if (bDebug) printf(" wordlen=%f x=%f x+wl=%f max=%f",wordlen,x,(x+wordlen),mfWrapMaxW);
00152                 // don't respect word boundaries if the word to be wrapped doesn't fit on a line
00153                 if (wordlen > 0.0 && x + wordlen > mfWrapMaxW && wordlen < mfWrapMaxW) {
00154                     if (bDebug) printf(" wordWrap\n");
00155                     return true;
00156                 }
00157             }
00158         }
00159         if (bDebug) printf("\n");
00160         return false;
00161     }
00162     
00163     // drawing
00164     
00165     static inline void  WriteVertex (float* &pVert,const float x,const float y,const float z,const float u,const float v) {
00166         *pVert++ = x;
00167         *pVert++ = y;
00168         *pVert++ = z;
00169         *pVert++ = u;
00170         *pVert++ = v;
00171     }
00172         
00176     float   WriteChar_NoIndex   (float* &pVert,unicode_char c,const float left,const float top,const float z) {
00177         const Ogre::Font::UVRect& uvRect = mpFont->getGlyphTexCoords(c);
00178         float h = mfCharHeight;
00179         float w = mfGlyphWidthFactor * mpFont->getGlyphAspectRatio(c);
00180         
00181         // First tri
00182         WriteVertex(pVert,left  ,top  ,z,uvRect.left, uvRect.top);      // Upper left
00183         WriteVertex(pVert,left  ,top+h,z,uvRect.left, uvRect.bottom);   // Bottom left
00184         WriteVertex(pVert,left+w,top  ,z,uvRect.right,uvRect.top);      // Top right
00185 
00186         // Second tri
00187         WriteVertex(pVert,left+w,top  ,z,uvRect.right,uvRect.top);      // Top right (again)
00188         WriteVertex(pVert,left  ,top+h,z,uvRect.left, uvRect.bottom);   // Bottom left
00189         WriteVertex(pVert,left+w,top+h,z,uvRect.right,uvRect.bottom);   // Bottom left (again)
00190         return w;
00191     }
00192     
00194     class cTextIterator { public:
00195         float x,y; 
00196         
00197         cTextIterator   (cOgreFontHelper& mFontHelper,const Ogre::UTFString& sText)
00198             : mFontHelper(mFontHelper), mCur(sText.begin()), mEnd(sText.end()), 
00199                 x(0),y(0),c(0),mfLineStartX(0), mbFirstChar(true), mbLineFeed(false) { 
00200             StartLine();
00201         }
00202         
00203         cTextIterator   (cOgreFontHelper& mFontHelper,itor mCur,itor mEnd)
00204             : mFontHelper(mFontHelper), mCur(mCur), mEnd(mEnd), 
00205                 x(0),y(0),c(0),mfLineStartX(0), mbFirstChar(true), mbLineFeed(false) { 
00206             StartLine();
00207         }
00208                 
00209         inline  bool    HasNext () { return mCur != mEnd; }
00210         
00212         inline  unicode_char    Next    () { 
00213             if (mCur == mEnd) return 0;
00214             unicode_char lastc = c;
00215             c = mCur.getCharacter();
00216             
00217             if (!mbFirstChar) x += mFontHelper.GetCharWidth(lastc);
00218             
00219             // only execute linefeed AFTER the user has had the chance to draw something at the end of the last line
00220             if (mbLineFeed) {
00221                 mbLineFeed = false;
00222                 LineFeed(); // in case of CRLF, mCur points to the first char AFTER both CR and LF here
00223             }
00224             
00225             // execute autowrap
00226             if (mFontHelper.mfWrapMaxW > 0 && !mbFirstChar && mFontHelper.TestAutoWrap(mCur,mEnd,x-mfLineStartX,IsWhiteSpace(lastc))) 
00227                 LineFeed(); 
00228                 
00229             ++mCur;
00230             
00231             // if c is the beginning of a CRLF, skip first part without doing anything
00232             mbLineFeed = IsNewLine(c) && (mCur == mEnd || !IsCRLF(c,mCur.getCharacter()));
00233             
00234             mbFirstChar = false;
00235             return c;
00236         }
00237         
00241         inline void StartLine   () {
00242             switch (mFontHelper.mAlign) {
00243                 case Align_Left:        x = 0;break;
00244                 case Align_Center:      x = - 0.5 * mFontHelper.CalcLineLen(mCur,mEnd);break;
00245                 case Align_Right:       x = -       mFontHelper.CalcLineLen(mCur,mEnd);break;
00246                 default:                x = 0;break;
00247             }
00248             mfLineStartX = x;
00249         }
00250         
00251         inline void LineFeed    () {
00252             StartLine();
00253             y += mFontHelper.mfLineHeight;
00254         }
00255         
00256         
00257         private:
00258         cOgreFontHelper&    mFontHelper;
00259         itor                mCur;
00260         itor                mEnd;
00261         bool                mbFirstChar;
00262         bool                mbLineFeed;
00263         float               mfLineStartX;
00264         unicode_char        c;
00265     };
00266     
00267     // TODO : operator +  == != ......, copy constructor ? 
00268     // derive from Ogre::UTFString::_const_fwd_iterator ?
00269     // comparison with normal ogre iterators :    != == 
00270     
00271     // TODO : real tab support : align across multiple line
00272     // TODO : default : mCharHeight = 0.02; mPixelCharHeight = 12;
00273     // TODO : calc linelen for centering
00274     // TODO : convenience variants with viewport and fontsize params.
00275     // TODO : current char (widht,height, texcoords,isnewline,iswhitespace,...)
00276     // TODO : clone font material, if it is used in overlay its depthcheck and lighting will be disabled
00277         //mpMaterial->setDepthCheckEnabled(false);
00278         //mpMaterial->setLightingEnabled(false);
00279     // TODO : clone font for 3d material ?
00280     
00281     // ***** ***** ***** ***** ***** utils
00282     
00285     void    GetTextBounds   (const Ogre::UTFString& text,Ogre::Real& w,Ogre::Real &h) {
00286         w = h = 0;
00287         // iterate over all chars in caption
00288         cOgreFontHelper::cTextIterator itor(*this,text);
00289         while (itor.HasNext()) {
00290             unicode_char c = itor.Next();
00291             w = mymax(w,itor.x + GetCharWidth(c));
00292         }
00293         h = mfLineHeight + itor.y;
00294     }
00295 
00298     void    GetGlyphBounds  (const Ogre::UTFString& text,const int iCharIndex,Ogre::Real& l,Ogre::Real& t,Ogre::Real& r,Ogre::Real& b) {
00299         l=t=r=b=0;
00300         // iterate over all chars in caption
00301         int iCurIndex = 0;
00302         cOgreFontHelper::cTextIterator itor(*this,text);
00303         while (itor.HasNext()) {
00304             unicode_char c = itor.Next();
00305             ++iCurIndex;
00306             if (iCurIndex == iCharIndex) {
00307                 l = itor.x; t = itor.y; r = l + GetCharWidth(c); b = t + mfCharHeight;
00308                 return;
00309             }
00310         }
00311     }
00312 
00317     int     GetGlyphAtPos   (const Ogre::UTFString& text,const float x,const float y) {
00318         // iterate over all chars in caption
00319         int iCurIndex = 0;
00320         cOgreFontHelper::cTextIterator itor(*this,text);
00321         float curx,cury;
00322         while (itor.HasNext()) {
00323             unicode_char c = itor.Next();
00324             ++iCurIndex;
00325             curx = x - itor.x;
00326             cury = y - itor.y;
00327             if (curx >= 0 && cury >= 0 && cury < mfCharHeight && curx < GetCharWidth(c))
00328                 return iCurIndex;
00329         }
00330         return -1;
00331     }
00332 };
00333 
00334 
00335 
00336 #if 0
00337     Ogre::FontManager::load (String &name, String &group,...)
00338 
00339     void TextAreaOverlayElement::setFontName( String& font )
00340     {
00341         mpFont = FontManager::getSingleton().getByName( font );
00342         if (mpFont.isNull())
00343             OGRE_EXCEPT( Exception::ERR_ITEM_NOT_FOUND, "Could not find font " + font,
00344                 "TextAreaOverlayElement::setFontName" );
00345         mpFont->load();
00346         mpMaterial = mpFont->getMaterial();
00347         mpMaterial->setDepthCheckEnabled(false);
00348         mpMaterial->setLightingEnabled(false);
00349         
00350         mGeomPositionsOutOfDate = true;
00351         mGeomUVsOutOfDate = true;
00352     }
00353     
00354     #if OGRE_UNICODE_SUPPORT
00355         typedef UTFString DisplayString;
00356     #   define OGRE_DEREF_DISPLAYSTRING_ITERATOR(it) it.getCharacter()
00357     #else
00358         typedef String DisplayString;
00359     #   define OGRE_DEREF_DISPLAYSTRING_ITERATOR(it) *it
00360     #endif
00361     
00362     /*
00363     suggestion for Ogre::UTFString::_base_iterator : stuff like 
00364         IsTab IsSpace IsNewLine IsWhiteSpace IsCRLF
00365     
00366     suggestion for TextAreaOverlayElement::updatePositionGeometry() :
00367     at the beginning of a line is the linelength is calculated even if is not needed (mAlignment == left)
00368     
00369     // WARNING ! MISSING second mRenderOp.vertexData->vertexCount -= 6; IN OGRE CODE for CR/LF ? (only one for CR)
00370     */
00371 #endif
00372 
00373 };
00374     
00375 #endif
00376 

Generated on Wed May 23 06:00:13 2012 for cpp by  doxygen 1.5.6