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