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___
Generated for Crystal Space by doxygen 1.4.6