00001 #include "SdlArs.h"
00002
00003 namespace Ars
00004 {
00005 SDL_Surface *HPaint::NoSurface = 0;
00006
00007 HPaint::HPaint( void )
00008 : m_pSurface( NoSurface ), m_PaintMode( PAINT_NORMAL )
00009 , m_pFontEngine( 0 )
00010 , m_bCachedMetricsValid(false), m_MaskChar(' '), m_MaxFontHeight(-1)
00011 {
00012 m_pFontEngine = ArsApplication::GetInstance()->GetDefaultFontEngine();
00013 }
00014
00015 HPaint::HPaint( SDL_Surface *&surface )
00016 : m_pSurface( surface ), m_PaintMode( PAINT_NORMAL )
00017 , m_pFontEngine( 0 )
00018 , m_bCachedMetricsValid(false), m_MaskChar(' '), m_MaxFontHeight(-1)
00019 {
00020 m_pFontEngine = ArsApplication::GetInstance()->GetDefaultFontEngine();
00021 }
00022
00023 void HPaint::_TextOut( XRect &drawRect , XPoint &OriginPoint , const std::string &str , const RGBColor &FontColor )
00024 {
00025 m_bCachedMetricsValid = false;
00026 XPoint OriginOffset , linebox;
00027 std::vector<XRect> CharacterRects;
00028
00029 GetMetrics( str , &linebox , &OriginOffset , &CharacterRects );
00030
00031 textSize.x = max( textSize.x , linebox.x );
00032 switch( (GetStates() & VALIGN_MASK) )
00033 {
00034 case S_ALIGN_TOP : OriginPoint = XPoint( OriginPoint.x , drawRect.y ); break;
00035 case S_ALIGN_BOTTOM : OriginPoint = XPoint( OriginPoint.x , drawRect.Bottom() ); break;
00036 }
00037
00038 switch( (GetStates() & HALIGN_MASK) )
00039 {
00040 case S_ALIGN_LEFT : OriginPoint = XPoint( drawRect.x , OriginPoint.y ); break;
00041 case S_ALIGN_RIGHT : OriginPoint = XPoint( drawRect.Right() , OriginPoint.y ); break;
00042 default : OriginPoint = XPoint( drawRect.x + drawRect.Width() / 2 , OriginPoint.y ); break;
00043 }
00044
00045 for( unsigned int i = 0 ; i < str.size() ; ++i )
00046 {
00047 FT_BitmapGlyphRec *pGlyph;
00048 if( m_MaskChar == ' ' )
00049 pGlyph = m_pFontEngine->RenderGlyph( str[i] );
00050 else
00051 pGlyph = m_pFontEngine->RenderGlyph( m_MaskChar );
00052
00053 for (int y = 0; y < pGlyph->bitmap.rows; ++y )
00054 {
00055 for (int x = 0; x < pGlyph->bitmap.width; ++x )
00056 {
00057 unsigned char *PixelOffset = pGlyph->bitmap.buffer + y * pGlyph->bitmap.width + x;
00058 if( *PixelOffset != 0x00 )
00059 {
00060 RGBColor PixelColor(FontColor.red, FontColor.green, FontColor.blue, *PixelOffset);
00061
00062 XPoint PixelPoint( XPoint( x + pGlyph->left, y ) + OriginPoint + OriginOffset + CharacterRects[i].TopLeft() );
00063
00064 if( drawRect.HitTest( PixelPoint ) == XRect::RELPOS_INSIDE)
00065 DrawPoint(PixelPoint, PixelColor);
00066 }
00067 }
00068 }
00069 }
00070 }
00071
00072 void HPaint::TextOutStr( const XRect &BoundingRect, const std::string &aStr , const RGBColor &FontColor )
00073 {
00074 string str;
00075 for( int i = 0 ; aStr[i] ; i++ )
00076 if( aStr[i] == '\t' )
00077 str += " ";
00078 else
00079 str += aStr[i];
00080
00081 m_bCachedMetricsValid = false;
00082 textSize = XPoint( 0 , 0 );
00083
00084 XPoint OriginPoint = BoundingRect.TopLeft();
00085 XRect drawRect = BoundingRect;
00086
00087 int nrlines = 1;
00088 string s = str;
00089 string::size_type n0 = 0 , n1 = s.find( '\n' , n0 );
00090 while( n1 != string::npos )
00091 {
00092 n0 = n1 + 1;
00093 n1 = s.find( '\n' , n0 );
00094 nrlines += 1;
00095 }
00096 if( (GetStates() & VALIGN_MASK) == 0 )
00097 OriginPoint = XPoint( OriginPoint.x , OriginPoint.y + 2 + max( 0 , (int)(drawRect.Height() - ((GetMaxFontHeight() + 2) * (nrlines -1))) / 2 ) );
00098
00099 textSize.y = (GetMaxFontHeight() + 2) * nrlines;
00100 n0 = 0 , n1 = s.find( '\n' , n0 );
00101 while( n1 != string::npos )
00102 {
00103 _TextOut( drawRect , OriginPoint , s.substr( n0 , n1 - n0 ) , FontColor );
00104
00105 n0 = n1 + 1;
00106 n1 = s.find( '\n' , n0 );
00107 if( (GetStates() & VALIGN_MASK) == 0 )
00108 OriginPoint = XPoint( OriginPoint.x , OriginPoint.y + (GetMaxFontHeight() + 2) );
00109 else
00110 drawRect.y = drawRect.y + (GetMaxFontHeight() + 2);
00111 }
00112
00113 _TextOut( drawRect , OriginPoint , s.substr( n0 ) , FontColor );
00114 }
00115
00116 XPoint HPaint::GetTextSize( const string &aStr )
00117 {
00118 string str;
00119 for( int i = 0 ; aStr[i] ; i++ )
00120 if( aStr[i] == '\t' )
00121 str += " ";
00122 else
00123 str += aStr[i];
00124 XPoint ans( 0 , 0 );
00125
00126 int nrlines = 1;
00127 string s = str;
00128 string::size_type n0 = 0 , n1 = s.find( '\n' , n0 );
00129 while( n1 != string::npos )
00130 {
00131 XPoint lineBox( 0 , 0 );
00132
00133 m_bCachedMetricsValid = false;
00134 GetMetrics( s.substr( n0 , n1 - n0 ) , &lineBox , 0 , 0 );
00135 ans.x = max( ans.x , lineBox.x );
00136 n0 = n1 + 1;
00137 n1 = s.find( '\n' , n0 );
00138 nrlines += 1;
00139 }
00140 XPoint lineBox( 0 , 0 );
00141 m_bCachedMetricsValid = false;
00142 GetMetrics( s.substr( n0 ) , &lineBox , 0 , 0 );
00143 ans.x = max( ans.x , lineBox.x );
00144
00145 ans.y = (m_pFontEngine->GetFontSize() + 2) * nrlines;
00146
00147 return ans;
00148 }
00149
00150 unsigned int HPaint::GetMaxFontHeight()
00151 {
00152 if (m_MaxFontHeight < 0)
00153 {
00154 int maxHeight=0;
00155 FT_Glyph_Metrics* pMetrics;
00156 for(int i=0; i < 256; i++)
00157 {
00158 pMetrics = m_pFontEngine->GetMetrics((char)i);
00159 if ((pMetrics->height >> 6) > maxHeight)
00160 {
00161 maxHeight = (pMetrics->height >> 6);
00162 }
00163
00164 }
00165 m_MaxFontHeight = maxHeight + 2;
00166 }
00167 return m_MaxFontHeight;
00168 }
00169
00170 void HPaint::GetMetrics( const std::string &str , XPoint* pBoundedDimensions, XPoint* pOriginOffset, std::vector<XRect>* pCharacterRects) const
00171 {
00172 if (! m_bCachedMetricsValid )
00173 {
00174 m_CachedCharacterRects.clear();
00175
00176 int iMinY = 0;
00177 int iMaxY = 0;
00178 int iLength = 0;
00179 for (unsigned int i = 0; i < str.size(); ++i)
00180 {
00181 FT_Glyph_Metrics* pMetrics;
00182 if (m_MaskChar == ' ')
00183 {
00184 pMetrics = m_pFontEngine->GetMetrics(str[i]);
00185 }
00186 else
00187 {
00188 pMetrics = m_pFontEngine->GetMetrics(m_MaskChar);
00189 }
00190
00191
00192 if ((pMetrics->horiBearingY - pMetrics->height) < iMinY)
00193 {
00194 iMinY = pMetrics->horiBearingY - pMetrics->height;
00195 }
00196 if (pMetrics->horiBearingY > iMaxY)
00197 {
00198 iMaxY = pMetrics->horiBearingY;
00199 }
00200 iLength += (pMetrics->horiAdvance);
00201
00202 m_CachedCharacterRects.push_back( XRect((iLength - pMetrics->horiAdvance) >> 6, pMetrics->horiBearingY >> 6, (iLength >> 6) - ((iLength - pMetrics->horiAdvance) >> 6) , (pMetrics->height >> 6) - (pMetrics->horiBearingY >> 6 )));
00203 }
00204
00205 iMinY = iMinY >> 6;
00206 iMaxY = iMaxY >> 6;
00207 iLength = iLength >> 6;
00208
00209
00210 for(std::vector<XRect>::iterator iter = m_CachedCharacterRects.begin(); iter != m_CachedCharacterRects.end(); ++iter)
00211 iter->y = iMaxY - iter->y;
00212
00213
00214 m_CachedCharacterRects.push_back(XRect(iLength, iMaxY, iLength, iMinY));
00215
00216 m_CachedBoundedDimensions = XPoint(iLength, iMaxY - iMinY);
00217
00218 switch ( (GetStates() & HALIGN_MASK) )
00219 {
00220 default:
00221 m_OriginOffset.x = -iLength / 2;
00222 break;
00223 case S_ALIGN_RIGHT:
00224 m_OriginOffset.x = -iLength;
00225 break;
00226 case S_ALIGN_LEFT:
00227 m_OriginOffset.x = 0;
00228 break;
00229 }
00230
00231 switch( (GetStates() & VALIGN_MASK) )
00232 {
00233 case S_ALIGN_TOP:
00234 m_OriginOffset.y = 0;
00235 break;
00236 case S_ALIGN_BOTTOM:
00237 m_OriginOffset.y = iMinY - iMaxY;
00238 break;
00239 default:
00240 m_OriginOffset.y = (iMinY - iMaxY) / 2;
00241 break;
00242 }
00243
00244 m_bCachedMetricsValid = true;
00245 }
00246
00247 if (pBoundedDimensions)
00248 {
00249 *pBoundedDimensions = m_CachedBoundedDimensions;
00250 }
00251
00252 if (pOriginOffset)
00253 {
00254 *pOriginOffset = m_OriginOffset;
00255 }
00256
00257 if (pCharacterRects)
00258 {
00259 *pCharacterRects = m_CachedCharacterRects;
00260 }
00261 }
00262
00263 void HPaint::DrawHLine(const int xStart, const int xEnd, const int y, const RGBColor &LineColor)
00264 {
00265 if (!m_pSurface)
00266 {
00267 tron<< "HPaint::HPaint : Invalid pointer to surface.\n";
00268 return ;
00269 }
00270
00271 #ifdef WIN32
00272 #ifndef MSVC6 // There are problems with VC6 and these std lib template classes
00273
00274
00275
00276
00277
00278
00279
00280
00281 #endif // MSVC6
00282 #endif // WIN32
00283
00284 SDL_Rect Rect;
00285 Rect.x = static_cast<short int>( min(xStart, xEnd) );
00286 Rect.y = static_cast<short int>(y);
00287 Rect.w = static_cast<short int>( max(xEnd - xStart + 1, xStart - xEnd + 1) );
00288 Rect.h = 1;
00289 SDL_FillRect(m_pSurface, &Rect, LineColor.SDLColor(m_pSurface->format));
00290 }
00291
00292
00293 void HPaint::DrawVLine(const int yStart, const int yEnd, const int x, const RGBColor &LineColor)
00294 {
00295 if (!m_pSurface)
00296 {
00297 tron<< "HPaint::HPaint : Invalid pointer to surface.\n";
00298 return ;
00299 }
00300 #ifdef WIN32
00301 #ifndef MSVC6 // There are problems with VC6 and these std lib template classes
00302
00303
00304
00305
00306
00307
00308
00309
00310 #endif // MSVC6
00311 #endif // WIN32
00312
00313 SDL_Rect Rect;
00314 Rect.x = static_cast<short int>(x);
00315 Rect.y = static_cast<short int>( min(yStart, yEnd) );
00316 Rect.w = 1;
00317 Rect.h = static_cast<short int>( max(yEnd - yStart + 1, yStart - yEnd + 1) );
00318 SDL_FillRect(m_pSurface, &Rect, LineColor.SDLColor(m_pSurface->format));
00319 }
00320
00321
00322 void HPaint::DrawRect(const XRect &Rect, const bool bFilled, const RGBColor &BorderColor, const RGBColor &FillColor)
00323 {
00324 if (!m_pSurface)
00325 {
00326 tron<< "HPaint::HPaint : Invalid pointer to surface.\n";
00327 return ;
00328 }
00329 XRect RealRect( Rect );
00330
00331 if (bFilled)
00332 {
00333 RealRect.w += 1;
00334 RealRect.h += 1;
00335 SDL_FillRect(m_pSurface, &RealRect, FillColor.SDLColor(m_pSurface->format));
00336 }
00337
00338 if (!bFilled || (BorderColor != FillColor))
00339 {
00340 RealRect = Rect;
00341
00342 DrawHLine(RealRect.x, RealRect.Right(), RealRect.y, BorderColor);
00343 DrawHLine(RealRect.x, RealRect.Right(), RealRect.Bottom(), BorderColor);
00344 DrawVLine(RealRect.y, RealRect.Bottom(), RealRect.x, BorderColor);
00345 DrawVLine(RealRect.y, RealRect.Bottom(), RealRect.Right(), BorderColor);
00346 RealRect.Grow(-1);
00347 }
00348 }
00349
00350
00351 void HPaint::DrawLine(const XPoint &Point1, const XPoint &Point2, const RGBColor &LineColor)
00352 {
00353 if (!m_pSurface)
00354 {
00355 tron<< "HPaint::HPaint : Invalid pointer to surface.\n";
00356 return ;
00357 }
00358 if (Point1.x == Point2.x)
00359 {
00360 DrawVLine(Point1.y, Point2.y, Point1.x, LineColor);
00361 }
00362 else
00363 {
00364 const double iSlope = double(Point2.y - Point1.y) / (Point2.x - Point1.x);
00365 if( iSlope <= 1.0 && iSlope >= -1.0 )
00366 {
00367 XPoint StartPoint = (Point1.x < Point2.x) ? Point1 : Point2;
00368 XPoint EndPoint = (Point1.x < Point2.x) ? Point2 : Point1;
00369 for (int x = StartPoint.x; x <= EndPoint.x; ++x)
00370 {
00371 DrawPoint(XPoint(x, static_cast<int>(StartPoint.y + (x - StartPoint.x) * iSlope)), LineColor);
00372 }
00373 }
00374 else
00375 {
00376 XPoint StartPoint = (Point1.y < Point2.y) ? Point1 : Point2;
00377 XPoint EndPoint = (Point1.y < Point2.y) ? Point2 : Point1;
00378 for (int y = StartPoint.y; y <= EndPoint.y; ++y)
00379 {
00380 DrawPoint(XPoint(static_cast<int>(StartPoint.x + (y - StartPoint.y) / iSlope), y), LineColor);
00381 }
00382 }
00383 }
00384 }
00385
00386 void HPaint::DrawPoint(const XPoint &Point, const RGBColor &PointColor)
00387 {
00388 if (!m_pSurface)
00389 {
00390 tron<< "HPaint::HPaint : Invalid pointer to surface.\n";
00391 return ;
00392 }
00393 DrawPoint( m_pSurface , Point , PointColor );
00394 }
00395
00396
00397 void HPaint::DrawPoint( SDL_Surface *s , const XPoint &Point, const RGBColor &PointColor)
00398 {
00399 if (!s)
00400 {
00401 tron<< "HPaint::HPaint : Invalid pointer to surface.\n";
00402 return ;
00403 }
00404 XPoint RealPoint = Point;
00405 if (XRect(0, 0, s->w, s->h).HitTest(RealPoint) == XRect::RELPOS_INSIDE)
00406 {
00407 LockSurface();
00408 Uint8* PixelOffset = static_cast<Uint8*>(s->pixels) +
00409 s->format->BytesPerPixel * RealPoint.x + s->pitch * RealPoint.y;
00410 switch (s->format->BytesPerPixel)
00411 {
00412 case 1:
00413 *reinterpret_cast<Uint8*>(PixelOffset) = static_cast<Uint8>(MixColor(ReadPoint(Point), PointColor).SDLColor(s->format));
00414 break;
00415 case 2:
00416 *reinterpret_cast<Uint16*>(PixelOffset) = static_cast<Uint16>(MixColor(ReadPoint(Point), PointColor).SDLColor(s->format));
00417 break;
00418 case 3:
00419 {
00420 Uint32 PixelColor = MixColor(ReadPoint(Point), PointColor).SDLColor(s->format);
00421 Uint8* pPixelSource = reinterpret_cast<Uint8*>(&PixelColor);
00422 Uint8* pPixelDest = reinterpret_cast<Uint8*>(PixelOffset);
00423 *pPixelDest = *pPixelSource;
00424 *(++pPixelDest) = *(++pPixelSource);
00425 *(++pPixelDest) = *(++pPixelSource);
00426 break;
00427 }
00428 case 4:
00429 *reinterpret_cast<Uint32*>(PixelOffset) = static_cast<Uint32>(MixColor(ReadPoint(Point), PointColor).SDLColor(s->format));
00430 break;
00431 default:
00432 tron << "HPaint::DrawPoint : Unrecognized BytesPerPixel.\n";
00433 break;
00434 }
00435 UnlockSurface();
00436 }
00437 }
00438
00439
00440 RGBColor HPaint::ReadPoint(const XPoint &Point)
00441 {
00442 if (!m_pSurface)
00443 tron<< "HPaint::HPaint : Invalid pointer to surface.\n";
00444 else
00445 return ReadPoint( m_pSurface , Point );
00446 return RGBColor( 0 , 0 ,0 );
00447 }
00448
00449 RGBColor HPaint::ReadPoint( SDL_Surface *s , const XPoint &Point)
00450 {
00451 if (!s)
00452 {
00453 tron<< "HPaint::HPaint : Invalid pointer to surface.\n";
00454 return RGBColor( 0 , 0 ,0 );
00455 }
00456 XPoint RealPoint = Point;
00457 Uint32 PixelColor = 0;
00458 if (XRect(0, 0, s->w, s->h).HitTest(RealPoint) == XRect::RELPOS_INSIDE)
00459 {
00460 Uint8* PixelOffset = static_cast<Uint8*>(s->pixels) +
00461 s->format->BytesPerPixel * RealPoint.x + s->pitch * RealPoint.y;
00462 switch (s->format->BytesPerPixel)
00463 {
00464 case 1:
00465 PixelColor = *reinterpret_cast<Uint8*>(PixelOffset);
00466 break;
00467 case 2:
00468 PixelColor = *reinterpret_cast<Uint16*>(PixelOffset);
00469 break;
00470 case 3:
00471 {
00472 Uint8* pPixelDest = reinterpret_cast<Uint8*>(&PixelColor);
00473 Uint8* pPixelSource = reinterpret_cast<Uint8*>(PixelOffset);
00474 *pPixelDest = *pPixelSource;
00475 *(++pPixelDest) = *(++pPixelSource);
00476 *(++pPixelDest) = *(++pPixelSource);
00477 break;
00478 }
00479 case 4:
00480 PixelColor = *reinterpret_cast<Uint32*>(PixelOffset);
00481 break;
00482 default:
00483 tron << "HPaint::DrawPoint : Unrecognized BytesPerPixel.\n";
00484 break;
00485 }
00486 }
00487 return RGBColor(&PixelColor, s->format);
00488 }
00489
00490
00491 void HPaint::LockSurface(void)
00492 {
00493 if (!m_pSurface)
00494 {
00495 tron<< "HPaint::HPaint : Invalid pointer to surface.\n";
00496 return ;
00497 }
00498 if (SDL_MUSTLOCK(m_pSurface))
00499 {
00500 if (SDL_LockSurface(m_pSurface) < 0)
00501 {
00502 SDL_Delay(10);
00503 if (SDL_LockSurface(m_pSurface) < 0)
00504 {
00505 tron << "Unable to lock surface.\n";
00506 }
00507 }
00508 }
00509 }
00510
00511
00512 void HPaint::UnlockSurface(void)
00513 {
00514 if (!m_pSurface)
00515 {
00516 tron<< "HPaint::HPaint : Invalid pointer to surface.\n";
00517 return ;
00518 }
00519 if (SDL_MUSTLOCK(m_pSurface))
00520 SDL_UnlockSurface(m_pSurface);
00521 }
00522
00523
00524 RGBColor HPaint::MixColor( const RGBColor &ColorBase, const RGBColor &ColorAdd )
00525 {
00526 RGBColor MixedColor( 0x00, 0x00, 0x00, 0x00 );
00527 switch (m_PaintMode)
00528 {
00529 case PAINT_IGNORE:
00530 MixedColor = ColorBase;
00531 break;
00532 case PAINT_REPLACE:
00533 MixedColor = ColorAdd;
00534 break;
00535 case PAINT_NORMAL:
00536 MixedColor = ColorBase + ColorAdd;
00537 break;
00538 case PAINT_AND:
00539 MixedColor = ColorBase &ColorAdd;
00540 break;
00541 case PAINT_OR:
00542 MixedColor = ColorBase | ColorAdd;
00543 break;
00544 case PAINT_XOR:
00545 MixedColor = ColorBase ^ ColorAdd;
00546 break;
00547 }
00548
00549 return MixedColor;
00550 }
00551
00552
00553 void HPaint::ReplaceColor(const RGBColor &NewColor, const RGBColor &OldColor)
00554 {
00555 for (int y = 0; y < m_pSurface->h; ++y)
00556 {
00557 for (int x = 0; x < m_pSurface->w; ++x)
00558 {
00559 XPoint point(x, y);
00560 if(ReadPoint(point) == OldColor)
00561 {
00562 DrawPoint(point, NewColor);
00563 }
00564 }
00565 }
00566 }
00567
00568
00569 void HPaint::TransparentColor(const RGBColor &TransparentColor)
00570 {
00571 SDL_SetColorKey(m_pSurface, SDL_SRCCOLORKEY, TransparentColor.SDLColor(m_pSurface->format));
00572 }
00573
00575 Painter::Painter( void )
00576 {
00577 }
00578
00579 void Painter::Init( icstring &aStr )
00580 {
00581 Cfg::Handler &h = Cfg::GetCfg();
00582
00583
00584 icstring tmp = Rsrc::FindCrdOf( aStr , "fontid" );
00585 int fid = Rsrc::GetInt( tmp );
00586 tmp = Rsrc::FindCrdOf( aStr , "fonth" );
00587 int fh = Rsrc::GetInt( tmp );
00588
00589 if( fid >= 0 || fh >= 0 )
00590 {
00591 string fntName = m_pFontEngine->GetFontFilename();
00592
00593 if( fid >= 0 )
00594 {
00595 char tmp[20];
00596 sprintf( tmp , "Font%d" , fid );
00597 Cfg::Handler &fnt = Cfg::GetCfg( h.GetCfgString( "Paths" , "Fonts" ) );
00598 fntName = fnt.GetCfgFile( "" , tmp );
00599 }
00600
00601 if( fh < 0 )
00602 m_pFontEngine = ArsApplication::GetInstance()->GetFontEngine( fntName );
00603 else
00604 m_pFontEngine = ArsApplication::GetInstance()->GetFontEngine( fntName , fh );
00605 }
00606
00607 Thing::Init( aStr );
00608 }
00609
00610 }
00611