CrystalSpace

Public API Reference

Main Page | Modules | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members | Related Pages

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 "csutil/csuctransform.h"
00028 #include "draw_common.h"
00029 #include "csplugincommon/canvas/softfontcache.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 utf8_char *text, 
00047     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 = strlen ((char*)text);
00064     int charW, charH, advance = 0;
00065     bool firstchar = true;
00066     while (textLen > 0)
00067     {
00068       utf32_char glyph;
00069       int skip = csUnicodeTransform::UTF8Decode (text, textLen, glyph, 0);
00070       if (skip == 0) break;
00071   
00072       text += skip;
00073       textLen -= skip;
00074   
00075       csSoftFontCache::SoftGlyphCacheData* cacheData = 
00076         (csSoftFontCache::SoftGlyphCacheData*)cache->CacheGlyph (knownFont, 
00077         glyph, flags);
00078       if (!cacheData->hasGlyph) 
00079       {
00080         // fall back to the default glyph (CS_FONT_DEFAULT_GLYPH)
00081         cacheData = (csSoftFontCache::SoftGlyphCacheData*)cache->CacheGlyph (
00082           knownFont, CS_FONT_DEFAULT_GLYPH, flags);
00083         if (!cacheData->hasGlyph) continue;
00084       }
00085   
00086       register uint8 *CharImageAlpha = cacheData->glyphAlphaData;
00087       register uint8 *CharImage = cacheData->glyphData;
00088       if ((!CharImage) && (!CharImageAlpha))
00089         continue;
00090   
00091       csBitmapMetrics* bmetrics;
00092       if (CharImageAlpha)
00093       {
00094         bmetrics = &cacheData->alphaMetrics;
00095       }
00096       else
00097       {
00098         bmetrics = &cacheData->bitmapMetrics;
00099       }
00100       charW = bmetrics->width;
00101       charH = bmetrics->height;
00102       
00103       int y = pen_y - bmetrics->top;
00104       
00105       // If we are advancing more than the last char was wide, we have to
00106       // fill the 'gap' with bg.
00107       
00108       int x = pen_x - (advance > 0 ? advance : 0) + (bmetrics->left < 0 ? bmetrics->left : 0);
00109       advance += bmetrics->left;
00110       
00111       // Hack: in case the first char has a negative left bitmap offset,
00112       // some of the background isn't drawn. Fix that.
00113       if (firstchar)
00114       {
00115         if (advance < 0)
00116         {
00117           advance = 0;
00118         }
00119         firstchar = false;
00120       }
00121      
00122       if (alphaBG != 0)
00123       {
00124         while (advance > 0)
00125         {
00126           if (x >= ClipX2)
00127             return;
00128     
00129           int cury = y;
00130           for (int i = 0; i < charH; i++, cury++)
00131           {
00132             if ((cury < ClipY1) || (cury >= ClipY2)) continue;
00133             register Tpixel *VRAM = (Tpixel*)G2D->GetPixelAt (x, cury);
00134             if (x >= ClipX1) mixerBG.Mix (*VRAM);
00135           }
00136           x++; advance--;
00137         }
00138       }
00139       else
00140       {
00141         if (advance > 0)
00142         {
00143           x += advance;
00144           advance = 0;
00145         }
00146       }
00147   
00148       if (x >= ClipX2)
00149         return;
00150   
00151       // If character is completely outside the clipping rectangle, continue
00152       if (!((x + charW <= ClipX1) || (x >= ClipX2)
00153        || (y + charH <= ClipY1) || (y >= ClipY2)))
00154       {
00155         int cury = y;
00156     
00157         int oldAdvance = advance;
00158         // If character should not be clipped, go the fast path
00159         if ((x < ClipX1) || (x + charW >= ClipX2)
00160          || (y < ClipY1) || (y + charH >= ClipY2))
00161         {
00162           // Perform full clipping
00163           int lX = x < ClipX1 ? ClipX1 - x : 0;
00164           int rX = x + charW >= ClipX2 ? ClipX2 - x : charW;
00165           int lBytes = CharImageAlpha ? lX : lX >> 3;
00166           int shiftX = CharImageAlpha ? 0 : lX & 7;
00167           int bbl = CharImageAlpha ? charW : (charW + 7) / 8; // bytes per line
00168           int lAbsX = x + lX;
00169           uint8 *p = CharImageAlpha ? CharImageAlpha - bbl : CharImage - bbl;
00170           
00171           if (CharImageAlpha)
00172           {
00173             for (int i = 0; i < charH; i++, cury++)
00174             {
00175               advance = oldAdvance;
00176               p += bbl;
00177               if ((cury < ClipY1) || (cury >= ClipY2)) 
00178               {
00179                 if (advance < 0) advance = MIN(0, advance + (rX - lX));
00180                 continue;
00181               }
00182               CharImageAlpha = p + lBytes;
00183               register uint8 CharLine = (*CharImageAlpha++) << shiftX;
00184               register Tpixel* VRAM = (Tpixel*)G2D->GetPixelAt (lAbsX, cury);
00185               // If we are advancing less than the last char was wide, the current
00186               // and last chars overlap. So we can't draw opaque, but have to draw
00187               // transparent instead.
00188               if (advance >= 0)
00189               {
00190                 for (int j = lX; j < rX; j++)
00191                 {
00192                   if (CharLine == 0xff)
00193                   {
00194                     mixerFG.Mix (*VRAM);
00195                   }
00196                   else if (CharLine == 0x00)
00197                   {
00198                     mixerBG.Mix (*VRAM);
00199                   }
00200                   else
00201                   {
00202                     // @@@ Could be more optimal, probably.
00203                     Tpixel mixedFG = *VRAM;
00204                     mixerFG.Mix (mixedFG);
00205                     mixerBG.Mix (*VRAM);
00206                     
00207                     Tpixmixer3 mixer (G2D, mixedFG, CharLine);
00208                     mixer.Mix (*VRAM);
00209                   }
00210                   VRAM++;
00211                   if (j < rX-1) CharLine = (*CharImageAlpha++);
00212                 } /* endfor */
00213               }
00214               else
00215               {
00216                 for (int j = lX; j < rX; j++)
00217                 {
00218                   if (CharLine == 0xff)
00219                   {
00220                     mixerFG.Mix (*VRAM);
00221                   }
00222                   else if (CharLine != 0x00)
00223                   {
00224                     // @@@ Could be more optimal, probably.
00225                     Tpixel mixedFG = *VRAM;
00226                     mixerFG.Mix (mixedFG);
00227                     
00228                     Tpixmixer3 mixer (G2D, mixedFG, CharLine);
00229                     mixer.Mix (*VRAM);
00230                   }
00231                   VRAM++;
00232                   if (j < rX-1) CharLine = (*CharImageAlpha++);
00233                 } /* endfor */
00234                 if (advance < 0) advance++;
00235               }
00236             }
00237           }
00238           else
00239           {
00240             for (int i = 0; i < charH; i++, cury++)
00241             {
00242               advance = oldAdvance;
00243               p += bbl;
00244               if ((cury < ClipY1) || (cury >= ClipY2))
00245               {
00246                 if (advance < 0) advance = MIN(0, advance + (rX - lX));
00247                 continue;
00248               }
00249               CharImage = p + lBytes;
00250               register uint8 CharLine = (*CharImage++) << shiftX;
00251               register Tpixel* VRAM = (Tpixel*)G2D->GetPixelAt (lAbsX, cury);
00252               for (int j = lX; j < rX; j++)
00253               {
00254                 if (advance >= 0)
00255                 {
00256                   if (CharLine & 0x80)
00257                     mixerFG.Mix (*VRAM++);
00258                   else
00259                     mixerBG.Mix (*VRAM++);
00260                 }
00261                 else
00262                 {
00263                   if (CharLine & 0x80)
00264                     mixerFG.Mix (*VRAM++);
00265                   else
00266                     VRAM++;
00267                   advance++;
00268                 }
00269                 if ((j & 7) == 7)
00270                   CharLine = (*CharImage++);
00271                 else
00272                   CharLine += CharLine;
00273               } /* endfor */
00274             }
00275           }
00276         }
00277         else
00278         {
00279           // no clipping
00280           if (CharImageAlpha)
00281           {
00282             for (int i = 0; i < charH; i++, cury++)
00283             {
00284               advance = oldAdvance;
00285               register Tpixel* VRAM = (Tpixel*)G2D->GetPixelAt (x, cury);
00286               register unsigned pixW = charW;
00287               register int pix;
00288               for (pix = pixW; pix > 0; pix--)
00289               {
00290                 register uint8 CharLine = (*CharImageAlpha++);
00291                 if (advance < 0)
00292                 {
00293                   if (CharLine == 0xff)
00294                   {
00295                     mixerFG.Mix (*VRAM);
00296                   }
00297                   else if (CharLine != 0x00)
00298                   {
00299                     // @@@ Could be more optimal, probably.
00300                     Tpixel mixedFG = *VRAM;
00301                     mixerFG.Mix (mixedFG);
00302                     
00303                     Tpixmixer3 mixer (G2D, mixedFG, CharLine);
00304                     mixer.Mix (*VRAM);
00305                   }
00306                 }
00307                 else
00308                 {
00309                   if (CharLine == 0xff)
00310                   {
00311                     mixerFG.Mix (*VRAM);
00312                   }
00313                   else if (CharLine == 0x00)
00314                   {
00315                     mixerBG.Mix (*VRAM);
00316                   }
00317                   else
00318                   {
00319                     // @@@ Could be more optimal, probably.
00320                     Tpixel mixedFG = *VRAM;
00321                     mixerFG.Mix (mixedFG);
00322                     mixerBG.Mix (*VRAM);
00323                     
00324                     Tpixmixer3 mixer (G2D, mixedFG, CharLine);
00325                     mixer.Mix (*VRAM);
00326                   }
00327                 }
00328                 if (advance < 0) advance++;
00329                 VRAM++;
00330               }
00331             }
00332           }
00333           else
00334           {
00335             for (int i = 0; i < charH; i++, cury++)
00336             {
00337               register Tpixel* VRAM = (Tpixel*)G2D->GetPixelAt (x, cury);
00338               register unsigned pixW = charW;
00339               while (pixW)
00340               {
00341                 register unsigned char CharLine = *CharImage++;
00342                 register int pix;
00343                 for (pix = pixW < 8 ? pixW : 8, pixW -= pix; CharLine && pix; pix--)
00344                 {
00345                   if (advance < 0)
00346                   {
00347                     if (CharLine & 0x80)
00348                       mixerFG.Mix (*VRAM++);
00349                     else
00350                       VRAM++;
00351                     // Addition is faster than shift, at least on i586+
00352                     CharLine += CharLine;
00353                   }
00354                   else
00355                   {
00356                     if (CharLine & 0x80)
00357                       mixerFG.Mix (*VRAM++);
00358                     else
00359                       mixerBG.Mix (*VRAM++);
00360                     // Addition is faster than shift, at least on i586+
00361                     CharLine += CharLine;
00362                   }
00363                   if (advance < 0) advance++;
00364                 } /* endfor */
00365                 if (advance < 0)
00366                 {
00367                   VRAM -= advance;
00368                   pix += advance;
00369                 }
00370                 while (pix--)
00371                   mixerBG.Mix (*VRAM++);
00372               } 
00373             } 
00374           }
00375         } /* endif */
00376       }
00377   
00378       pen_x += cacheData->glyphMetrics.advance;
00379       advance += cacheData->glyphMetrics.advance - (charW + bmetrics->left);
00380     }
00381     cache->PurgeEmptyPlanes ();
00382     
00383   }
00384 };
00385 
00388 #endif // __CS_CSPLUGINCOMMON_CANVAS_DRAW_TEXT_H___

Generated for Crystal Space by doxygen 1.3.9.1