CrystalSpace

Public API Reference

csplugincommon/canvas/draw_text.h

Go to the documentation of this file.
00001 /*
00002     Copyright (C) 2004 by Jorrit Tyberghein
00003               (C) 2004 by Frank Richter
00004 
00005     This library is free software; you can redistribute it and/or
00006     modify it under the terms of the GNU Library General Public
00007     License as published by the Free Software Foundation; either
00008     version 2 of the License, or (at your option) any later version.
00009 
00010     This library is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013     Library General Public License for more details.
00014 
00015     You should have received a copy of the GNU Library General Public
00016     License along with this library; if not, write to the Free
00017     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018 */
00019 
00020 #ifndef __CS_CSPLUGINCOMMON_CANVAS_DRAW_TEXT_H__
00021 #define __CS_CSPLUGINCOMMON_CANVAS_DRAW_TEXT_H__
00022 
00027 #include "csplugincommon/canvas/draw_common.h"
00028 #include "csplugincommon/canvas/softfontcache.h"
00029 #include "csutil/csuctransform.h"
00030 
00041 template<class Tpixel, class Tpixmixer1, class Tpixmixer2, class Tpixmixer3>
00042 class csG2DDrawText
00043 {
00044 public:
00045   static void DrawText (csSoftFontCache* cache, iFont* font, int pen_x, int pen_y,
00046     Tpixel fg, uint8 alphaFG, Tpixel bg, uint8 alphaBG, const void* text, 
00047     bool isWide, uint flags)
00048   {
00049     csGraphics2D* G2D = cache->G2D;
00050     const int ClipX1 = cache->ClipX1, ClipY1 = cache->ClipY1, 
00051       ClipX2 = cache->ClipX2, ClipY2 = cache->ClipY2;
00052     Tpixmixer1 mixerFG (G2D, fg, alphaFG);
00053     Tpixmixer2 mixerBG (G2D, bg, alphaBG);
00054     
00055     if (!font)
00056       return;
00057     
00058     if (!(flags & CS_WRITE_BASELINE)) pen_y += font->GetAscent ();
00059   
00060     csSoftFontCache::KnownFont* knownFont = cache->GetCachedFont (font);
00061     if (knownFont == 0) knownFont = cache->CacheFont (font);
00062   
00063     size_t textLen = isWide ? wcslen ((wchar_t*)text) : strlen ((char*)text);
00064     int charW, charH, advance = 0;
00065     bool firstchar = true;
00066     while (textLen > 0)
00067     {
00068       utf32_char glyph;
00069       if (isWide)
00070       {
00071         int skip = csUnicodeTransform::Decode ((wchar_t*)text, textLen, glyph, 0);
00072         if (skip == 0) break;
00073   
00074         text = ((wchar_t*)text + skip);
00075         textLen -= skip;
00076       }
00077       else
00078       {
00079         int skip = csUnicodeTransform::UTF8Decode ((utf8_char*)text, textLen, glyph, 0);
00080         if (skip == 0) break;
00081   
00082         text = ((utf8_char*)text + skip);
00083         textLen -= skip;
00084       }
00085   
00086       csSoftFontCache::SoftGlyphCacheData* cacheData = 
00087         (csSoftFontCache::SoftGlyphCacheData*)cache->CacheGlyph (knownFont, 
00088         glyph, flags);
00089       if (!cacheData->hasGlyph) 
00090       {
00091         // fall back to the default glyph (CS_FONT_DEFAULT_GLYPH)
00092         cacheData = (csSoftFontCache::SoftGlyphCacheData*)cache->CacheGlyph (
00093           knownFont, CS_FONT_DEFAULT_GLYPH, flags);
00094         if (!cacheData->hasGlyph) continue;
00095       }
00096   
00097       csBitmapMetrics* bmetrics;
00098       if (cacheData->glyphAlphaDataBuf.IsValid())
00099       {
00100         bmetrics = &cacheData->alphaMetrics;
00101       }
00102       else if (cacheData->glyphDataBuf.IsValid())
00103       {
00104         bmetrics = &cacheData->bitmapMetrics;
00105       }
00106       else
00107         continue;
00108 
00109       register uint8 *CharImageAlpha = cacheData->glyphAlphaData;
00110       register uint8 *CharImage = cacheData->glyphData;
00111   
00112       charW = bmetrics->width;
00113       charH = bmetrics->height;
00114       
00115       int y = pen_y - bmetrics->top;
00116       
00117       // If we are advancing more than the last char was wide, we have to
00118       // fill the 'gap' with bg.
00119       
00120       int x = pen_x - (advance > 0 ? advance : 0) + (bmetrics->left < 0 ? bmetrics->left : 0);
00121       advance += bmetrics->left;
00122       
00123       // Hack: in case the first char has a negative left bitmap offset,
00124       // some of the background isn't drawn. Fix that.
00125       if (firstchar)
00126       {
00127         if (advance < 0)
00128         {
00129           advance = 0;
00130         }
00131         firstchar = false;
00132       }
00133      
00134       if (alphaBG != 0)
00135       {
00136         while (advance > 0)
00137         {
00138           if (x >= ClipX2)
00139             return;
00140     
00141           int cury = y;
00142           for (int i = 0; i < charH; i++, cury++)
00143           {
00144             if ((cury < ClipY1) || (cury >= ClipY2)) continue;
00145             register Tpixel *VRAM = (Tpixel*)G2D->GetPixelAt (x, cury);
00146             if (x >= ClipX1) mixerBG.Mix (*VRAM);
00147           }
00148           x++; advance--;
00149         }
00150       }
00151       else
00152       {
00153         if (advance > 0)
00154         {
00155           x += advance;
00156           advance = 0;
00157         }
00158       }
00159   
00160       if (x >= ClipX2)
00161         return;
00162   
00163       // If character is completely outside the clipping rectangle, continue
00164       if (!((x + charW <= ClipX1) || (x >= ClipX2)
00165        || (y + charH <= ClipY1) || (y >= ClipY2)))
00166       {
00167         int cury = y;
00168     
00169         int oldAdvance = advance;
00170         // If character should not be clipped, go the fast path
00171         if ((x < ClipX1) || (x + charW >= ClipX2)
00172          || (y < ClipY1) || (y + charH >= ClipY2))
00173         {
00174           // Perform full clipping
00175           int lX = x < ClipX1 ? ClipX1 - x : 0;
00176           int rX = x + charW >= ClipX2 ? ClipX2 - x : charW;
00177           int lBytes = CharImageAlpha ? lX : lX >> 3;
00178           int shiftX = CharImageAlpha ? 0 : lX & 7;
00179           int bbl = CharImageAlpha ? charW : (charW + 7) / 8; // bytes per line
00180           int lAbsX = x + lX;
00181           uint8 *p = CharImageAlpha ? CharImageAlpha - bbl : CharImage - bbl;
00182           
00183           if (CharImageAlpha)
00184           {
00185             for (int i = 0; i < charH; i++, cury++)
00186             {
00187               advance = oldAdvance;
00188               p += bbl;
00189               if ((cury < ClipY1) || (cury >= ClipY2)) 
00190               {
00191                 if (advance < 0) advance = MIN(0, advance + (rX - lX));
00192                 continue;
00193               }
00194               CharImageAlpha = p + lBytes;
00195               register uint8 CharLine = (*CharImageAlpha++) << shiftX;
00196               register Tpixel* VRAM = (Tpixel*)G2D->GetPixelAt (lAbsX, cury);
00197               // If we are advancing less than the last char was wide, the current
00198               // and last chars overlap. So we can't draw opaque, but have to draw
00199               // transparent instead.
00200               if (advance >= 0)
00201               {
00202                 for (int j = lX; j < rX; j++)
00203                 {
00204                   if (CharLine == 0xff)
00205                   {
00206                     mixerFG.Mix (*VRAM);
00207                   }
00208                   else if (CharLine == 0x00)
00209                   {
00210                     mixerBG.Mix (*VRAM);
00211                   }
00212                   else
00213                   {
00214                     // @@@ Could be more optimal, probably.
00215                     Tpixel mixedFG = *VRAM;
00216                     mixerFG.Mix (mixedFG);
00217                     mixerBG.Mix (*VRAM);
00218                     
00219                     Tpixmixer3 mixer (G2D, mixedFG, CharLine);
00220                     mixer.Mix (*VRAM);
00221                   }
00222                   VRAM++;
00223                   if (j < rX-1) CharLine = (*CharImageAlpha++);
00224                 } /* endfor */
00225               }
00226               else
00227               {
00228                 for (int j = lX; j < rX; j++)
00229                 {
00230                   if (CharLine == 0xff)
00231                   {
00232                     mixerFG.Mix (*VRAM);
00233                   }
00234                   else if (CharLine != 0x00)
00235                   {
00236                     // @@@ Could be more optimal, probably.
00237                     Tpixel mixedFG = *VRAM;
00238                     mixerFG.Mix (mixedFG);
00239                     
00240                     Tpixmixer3 mixer (G2D, mixedFG, CharLine);
00241                     mixer.Mix (*VRAM);
00242                   }
00243                   VRAM++;
00244                   if (j < rX-1) CharLine = (*CharImageAlpha++);
00245                 } /* endfor */
00246                 if (advance < 0) advance++;
00247               }
00248             }
00249           }
00250           else if (CharImage)
00251           {
00252             for (int i = 0; i < charH; i++, cury++)
00253             {
00254               advance = oldAdvance;
00255               p += bbl;
00256               if ((cury < ClipY1) || (cury >= ClipY2))
00257               {
00258                 if (advance < 0) advance = MIN(0, advance + (rX - lX));
00259                 continue;
00260               }
00261               CharImage = p + lBytes;
00262               register uint8 CharLine = (*CharImage++) << shiftX;
00263               register Tpixel* VRAM = (Tpixel*)G2D->GetPixelAt (lAbsX, cury);
00264               for (int j = lX; j < rX; j++)
00265               {
00266                 if (advance >= 0)
00267                 {
00268                   if (CharLine & 0x80)
00269                     mixerFG.Mix (*VRAM++);
00270                   else
00271                     mixerBG.Mix (*VRAM++);
00272                 }
00273                 else
00274                 {
00275                   if (CharLine & 0x80)
00276                     mixerFG.Mix (*VRAM++);
00277                   else
00278                     VRAM++;
00279                   advance++;
00280                 }
00281                 if ((j & 7) == 7)
00282                   CharLine = (*CharImage++);
00283                 else
00284                   CharLine += CharLine;
00285               } /* endfor */
00286             }
00287           }
00288         }
00289         else
00290         {
00291           // no clipping
00292           if (CharImageAlpha)
00293           {
00294             for (int i = 0; i < charH; i++, cury++)
00295             {
00296               advance = oldAdvance;
00297               register Tpixel* VRAM = (Tpixel*)G2D->GetPixelAt (x, cury);
00298               register unsigned pixW = charW;
00299               register int pix;
00300               for (pix = pixW; pix > 0; pix--)
00301               {
00302                 register uint8 CharLine = (*CharImageAlpha++);
00303                 if (advance < 0)
00304                 {
00305                   if (CharLine == 0xff)
00306                   {
00307                     mixerFG.Mix (*VRAM);
00308                   }
00309                   else if (CharLine != 0x00)
00310                   {
00311                     // @@@ Could be more optimal, probably.
00312                     Tpixel mixedFG = *VRAM;
00313                     mixerFG.Mix (mixedFG);
00314                     
00315                     Tpixmixer3 mixer (G2D, mixedFG, CharLine);
00316                     mixer.Mix (*VRAM);
00317                   }
00318                 }
00319                 else
00320                 {
00321                   if (CharLine == 0xff)
00322                   {
00323                     mixerFG.Mix (*VRAM);
00324                   }
00325                   else if (CharLine == 0x00)
00326                   {
00327                     mixerBG.Mix (*VRAM);
00328                   }
00329                   else
00330                   {
00331                     // @@@ Could be more optimal, probably.
00332                     Tpixel mixedFG = *VRAM;
00333                     mixerFG.Mix (mixedFG);
00334                     mixerBG.Mix (*VRAM);
00335                     
00336                     Tpixmixer3 mixer (G2D, mixedFG, CharLine);
00337                     mixer.Mix (*VRAM);
00338                   }
00339                 }
00340                 if (advance < 0) advance++;
00341                 VRAM++;
00342               }
00343             }
00344           }
00345           else if (CharImage)
00346           {
00347             for (int i = 0; i < charH; i++, cury++)
00348             {
00349               register Tpixel* VRAM = (Tpixel*)G2D->GetPixelAt (x, cury);
00350               register unsigned pixW = charW;
00351               while (pixW)
00352               {
00353                 register unsigned char CharLine = *CharImage++;
00354                 register int pix;
00355                 for (pix = pixW < 8 ? pixW : 8, pixW -= pix; CharLine && pix; pix--)
00356                 {
00357                   if (advance < 0)
00358                   {
00359                     if (CharLine & 0x80)
00360                       mixerFG.Mix (*VRAM++);
00361                     else
00362                       VRAM++;
00363                     // Addition is faster than shift, at least on i586+
00364                     CharLine += CharLine;
00365                   }
00366                   else
00367                   {
00368                     if (CharLine & 0x80)
00369                       mixerFG.Mix (*VRAM++);
00370                     else
00371                       mixerBG.Mix (*VRAM++);
00372                     // Addition is faster than shift, at least on i586+
00373                     CharLine += CharLine;
00374                   }
00375                   if (advance < 0) advance++;
00376                 } /* endfor */
00377                 if (advance < 0)
00378                 {
00379                   VRAM -= advance;
00380                   pix += advance;
00381                 }
00382                 while (pix--)
00383                   mixerBG.Mix (*VRAM++);
00384               } 
00385             } 
00386           }
00387         } /* endif */
00388       }
00389   
00390       pen_x += cacheData->glyphMetrics.advance;
00391       advance += cacheData->glyphMetrics.advance - (charW + bmetrics->left);
00392     }
00393     cache->PurgeEmptyPlanes ();
00394     
00395   }
00396 };
00397 
00400 #endif // __CS_CSPLUGINCOMMON_CANVAS_DRAW_TEXT_H___

Generated for Crystal Space by doxygen 1.4.7