## Public API Reference

- Main Page
- Modules
- Namespaces
- Classes
- Files
- Related Pages

- File List
- File Members

# 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___