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