- Main Page
- Namespaces
- Classes
- Files
- Directories

- File List
- File Members

# RTS.cpp

    00001 /*
    00002  *      Copyright (C) 2003-2005 Gabest
    00003  *      http://www.gabest.org
    00004  *
    00005  *  This Program is free software; you can redistribute it and/or modify
    00006  *  it under the terms of the GNU General Public License as published by
    00007  *  the Free Software Foundation; either version 2, or (at your option)
    00008  *  any later version.
    00009  *
    00010  *  This Program 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
    00013  *  GNU General Public License for more details.
    00014  *
    00015  *  You should have received a copy of the GNU General Public License
    00016  *  along with GNU Make; see the file COPYING.  If not, write to
    00017  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
    00018  *  http://www.gnu.org/copyleft/gpl.html
    00019  *
    00020  */
    00021
    00022 #include "stdafx.h"
    00023 #include <math.h>
    00024 #include <time.h>
    00025 #include "RTS.h"
    00026
    00027 // WARNING: this isn't very thread safe, use only one RTS a time.
    00028 static HDC g_hDC;
    00029 static int g_hDC_refcnt = 0;
    00030
    00031 static long revcolor(long c)
    00032 {
    00033         return ((c&0xff0000)>>16) + (c&0xff00) + ((c&0xff)<<16);
    00034 }
    00035
    00037
    00038 // CMyFont
    00039
    00040 CMyFont::CMyFont(STSStyle& style)
    00041 {
    00042         LOGFONT lf;
    00043         memset(&lf, 0, sizeof(lf));
    00044         lf <<= style;
    00045         lf.lfHeight = (LONG)(style.fontSize+0.5);
    00046         lf.lfOutPrecision = OUT_TT_PRECIS;
    00047         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
    00048         lf.lfQuality = ANTIALIASED_QUALITY;
    00049         lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
    00050 lf.lfEscapement = lf.lfOrientation = 0;
    00051         if(!CreateFontIndirect(&lf))
    00052         {
    00053                 _tcscpy(lf.lfFaceName, _T("Arial"));
    00054                 CreateFontIndirect(&lf);
    00055         }
    00056
    00057         HFONT hOldFont = (HFONT)SelectObject(g_hDC, *this);
    00058         TEXTMETRIC tm;
    00059         BOOL b = GetTextMetrics(g_hDC, &tm);
    00060         DWORD ret = GetLastError();
    00061         m_ascent = ((tm.tmAscent + 4) >> 3);
    00062         m_descent = ((tm.tmDescent + 4) >> 3);
    00063         SelectObject(g_hDC, hOldFont);
    00064 }
    00065
    00066 // CWord
    00067
    00068 CWord::CWord(STSStyle& style, CStringW str, int ktype, int kstart, int kend)
    00069         : m_style(style), m_str(str)
    00070         , m_width(0), m_ascent(0), m_descent(0)
    00071         , m_ktype(ktype), m_kstart(kstart), m_kend(kend)
    00072         , m_fDrawn(false), m_p(INT_MAX, INT_MAX)
    00073         , m_fLineBreak(false), m_fWhiteSpaceChar(false)
    00074         , m_pOpaqueBox(NULL)
    00075 {
    00076         if(str.IsEmpty())
    00077         {
    00078                 m_fWhiteSpaceChar = m_fLineBreak = true;
    00079         }
    00080
    00081         CMyFont font(m_style);
    00082         m_ascent = (int)(m_style.fontScaleY/100*font.m_ascent);
    00083         m_descent = (int)(m_style.fontScaleY/100*font.m_descent);
    00084         m_width = 0;
    00085 }
    00086
    00087 CWord::~CWord()
    00088 {
    00089         if(m_pOpaqueBox) delete m_pOpaqueBox;
    00090 }
    00091
    00092 bool CWord::Append(CWord* w)
    00093 {
    00094         if(!(m_style == w->m_style)
    00095         || m_fLineBreak || w->m_fLineBreak
    00096         || w->m_kstart != w->m_kend || m_ktype != w->m_ktype) return(false);
    00097
    00098         m_fWhiteSpaceChar = m_fWhiteSpaceChar && w->m_fWhiteSpaceChar;
    00099         m_str += w->m_str;
    00100         m_width += w->m_width;
    00101
    00102         m_fDrawn = false;
    00103         m_p = CPoint(INT_MAX, INT_MAX);
    00104
    00105         return(true);
    00106 }
    00107
    00108 void CWord::Paint(CPoint p, CPoint org)
    00109 {
    00110         if(!m_str) return;
    00111
    00112         if(!m_fDrawn)
    00113         {
    00114                 if(!CreatePath()) return;
    00115
    00116                 Transform(CPoint((org.x-p.x)*8, (org.y-p.y)*8));
    00117
    00118                 if(!ScanConvert()) return;
    00119
    00120                 if(m_style.borderStyle == 0 && m_style.outlineWidth > 0)
    00121                 {
    00122                         if(!CreateWidenedRegion((int)(m_style.outlineWidth+0.5))) return;
    00123                 }
    00124                 else if(m_style.borderStyle == 1)
    00125                 {
    00126                         if(!CreateOpaqueBox()) return;
    00127                 }
    00128
    00129                 m_fDrawn = true;
    00130
    00131                 if(!Rasterize(p.x&7, p.y&7, m_style.fBlur)) return;
    00132         }
    00133         else if((m_p.x&7) != (p.x&7) || (m_p.y&7) != (p.y&7))
    00134         {
    00135                 Rasterize(p.x&7, p.y&7, m_style.fBlur);
    00136         }
    00137
    00138         m_p = p;
    00139
    00140         if(m_pOpaqueBox)
    00141                 m_pOpaqueBox->Paint(p, org);
    00142 }
    00143
    00144 void CWord::Transform(CPoint org)
    00145 {
    00146         double scalex = m_style.fontScaleX/100;
    00147         double scaley = m_style.fontScaleY/100;
    00148
    00149         double caz = cos((3.1415/180)*m_style.fontAngleZ);
    00150         double saz = sin((3.1415/180)*m_style.fontAngleZ);
    00151         double cax = cos((3.1415/180)*m_style.fontAngleX);
    00152         double sax = sin((3.1415/180)*m_style.fontAngleX);
    00153         double cay = cos((3.1415/180)*m_style.fontAngleY);
    00154         double say = sin((3.1415/180)*m_style.fontAngleY);
    00155
    00156         for(int i = 0; i < mPathPoints; i++)
    00157         {
    00158                 double x, y, z, xx, yy, zz;
    00159
    00160                 x = scalex * mpPathPoints[i].x - org.x;
    00161                 y = scaley * mpPathPoints[i].y - org.y;
    00162                 z = 0;
    00163
    00164                 xx = x*caz + y*saz;
    00165                 yy = -(x*saz - y*caz);
    00166                 zz = z;
    00167
    00168                 x = xx;
    00169                 y = yy*cax + zz*sax;
    00170                 z = yy*sax - zz*cax;
    00171
    00172                 xx = x*cay + z*say;
    00173                 yy = y;
    00174                 zz = x*say - z*cay;
    00175
    00176                 zz = max(zz, -19000);
    00177
    00178                 x = (xx * 20000) / (zz + 20000);
    00179                 y = (yy * 20000) / (zz + 20000);
    00180
    00181                 mpPathPoints[i].x = (LONG)(x + org.x + 0.5);
    00182                 mpPathPoints[i].y = (LONG)(y + org.y + 0.5);
    00183         }
    00184 }
    00185
    00186 bool CWord::CreateOpaqueBox()
    00187 {
    00188         if(m_pOpaqueBox) return(true);
    00189
    00190         STSStyle style = m_style;
    00191         style.borderStyle = 0;
    00192         style.outlineWidth = 0;
    00193         style.colors[0] = m_style.colors[2];
    00194         style.alpha[0] = m_style.alpha[2];
    00195
    00196         int w = (int)(m_style.outlineWidth + 0.5);
    00197
    00198         CStringW str;
    00199         str.Format(L"m %d %d l %d %d %d %d %d %d",
    00200                 -w, -w,
    00201                 m_width+w, -w,
    00202                 m_width+w, m_ascent+m_descent+w,
    00203                 -w, m_ascent+m_descent+w);
    00204
    00205         m_pOpaqueBox = new CPolygon(style, str, 0, 0, 0, 1.0/8, 1.0/8, 0);
    00206
    00207         return(!!m_pOpaqueBox);
    00208 }
    00209
    00210 // CText
    00211
    00212 CText::CText(STSStyle& style, CStringW str, int ktype, int kstart, int kend)
    00213         : CWord(style, str, ktype, kstart, kend)
    00214 {
    00215         if(m_str.GetLength() == 1 && m_str[0] == ' ')
    00216         {
    00217                 m_fWhiteSpaceChar = true;
    00218         }
    00219
    00220         CMyFont font(m_style);
    00221
    00222         HFONT hOldFont = (HFONT)SelectObject(g_hDC, font);
    00223
    00224         if(m_style.fontSpacing || (long)GetVersion() < 0)
    00225         {
    00226                 bool bFirstPath = true;
    00227
    00228                 for(LPCWSTR s = m_str; *s; s++)
    00229                 {
    00230                         CSize extent;
    00231                         if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectObject(g_hDC, hOldFont); ASSERT(0); return;}
    00232                         m_width += extent.cx + (int)m_style.fontSpacing;
    00233                 }
    00234 //                      m_width -= (int)m_style.fontSpacing; // TODO: subtract only at the end of the line
    00235         }
    00236         else
    00237         {
    00238                 CSize extent;
    00239                 if(!GetTextExtentPoint32W(g_hDC, m_str, wcslen(str), &extent)) {SelectObject(g_hDC, hOldFont); ASSERT(0); return;}
    00240                 m_width += extent.cx;
    00241         }
    00242
    00243         m_width = (int)(m_style.fontScaleX/100*m_width + 4) >> 3;
    00244
    00245         SelectObject(g_hDC, hOldFont);
    00246 }
    00247
    00248 CWord* CText::Copy()
    00249 {
    00250         return(new CText(m_style, m_str, m_ktype, m_kstart, m_kend));
    00251 }
    00252
    00253 bool CText::Append(CWord* w)
    00254 {
    00255         return(dynamic_cast<CText*>(w) && CWord::Append(w));
    00256 }
    00257
    00258 bool CText::CreatePath()
    00259 {
    00260         CMyFont font(m_style);
    00261         HFONT hOldFont = (HFONT)SelectObject(g_hDC, font);
    00262
    00263         int width = 0;
    00264
    00265         if(m_style.fontSpacing || (long)GetVersion() < 0)
    00266         {
    00267                 bool bFirstPath = true;
    00268
    00269                 for(LPCWSTR s = m_str; *s; s++)
    00270                 {
    00271                         CSize extent;
    00272                         if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectObject(g_hDC, hOldFont); ASSERT(0); return(false);}
    00273
    00274                         PartialBeginPath(g_hDC, bFirstPath); bFirstPath = false;
    00275                         TextOutW(g_hDC, 0, 0, s, 1);
    00276                         PartialEndPath(g_hDC, width, 0);
    00277
    00278                         width += extent.cx + (int)m_style.fontSpacing;
    00279                 }
    00280         }
    00281         else
    00282         {
    00283                 CSize extent;
    00284                 if(!GetTextExtentPoint32W(g_hDC, m_str, m_str.GetLength(), &extent)) {SelectObject(g_hDC, hOldFont); ASSERT(0); return(false);}
    00285
    00286                 BeginPath(g_hDC);
    00287                 TextOutW(g_hDC, 0, 0, m_str, m_str.GetLength());
    00288                 EndPath(g_hDC);
    00289         }
    00290
    00291         SelectObject(g_hDC, hOldFont);
    00292
    00293         return(true);
    00294 }
    00295
    00296 // CPolygon
    00297
    00298 CPolygon::CPolygon(STSStyle& style, CStringW str, int ktype, int kstart, int kend, double scalex, double scaley, int baseline)
    00299         : CWord(style, str, ktype, kstart, kend)
    00300         , m_scalex(scalex), m_scaley(scaley), m_baseline(baseline)
    00301 {
    00302         ParseStr();
    00303 }
    00304
    00305 CPolygon::~CPolygon()
    00306 {
    00307 }
    00308
    00309 CWord* CPolygon::Copy()
    00310 {
    00311         return(new CPolygon(m_style, m_str, m_ktype, m_kstart, m_kend, m_scalex, m_scaley, m_baseline));
    00312 }
    00313
    00314 bool CPolygon::Append(CWord* w)
    00315 {
    00316         int width = m_width;
    00317
    00318         CPolygon* p = dynamic_cast<CPolygon*>(w);
    00319         if(!p) return(false);
    00320
    00321         // TODO
    00322         return(false);
    00323
    00324         return(true);
    00325 }
    00326
    00327 bool CPolygon::GetLONG(CStringW& str, LONG& ret)
    00328 {
    00329         LPWSTR s = (LPWSTR)(LPCWSTR)str, e = s;
    00330         ret = wcstol(str, &e, 10);
    00331         str = str.Mid(e - s);
    00332         return(e > s);
    00333 }
    00334
    00335 bool CPolygon::GetPOINT(CStringW& str, POINT& ret)
    00336 {
    00337         return(GetLONG(str, ret.x) && GetLONG(str, ret.y));
    00338 }
    00339
    00340 bool CPolygon::ParseStr()
    00341 {
    00342         if(m_pathTypesOrg.GetSize() > 0) return(true);
    00343
    00344         CPoint p;
    00345         int i, j, lastsplinestart = -1, firstmoveto = -1, lastmoveto = -1;
    00346
    00347         CStringW str = m_str;
    00348         str.SpanIncluding(L"mnlbspc 0123456789");
    00349         str.Replace(L"m", L"*m");
    00350         str.Replace(L"n", L"*n");
    00351         str.Replace(L"l", L"*l");
    00352         str.Replace(L"b", L"*b");
    00353         str.Replace(L"s", L"*s");
    00354         str.Replace(L"p", L"*p");
    00355         str.Replace(L"c", L"*c");
    00356
    00357         int k = 0;
    00358         for(CStringW s = str.Tokenize(L"*", k); !s.IsEmpty(); s = str.Tokenize(L"*", k))
    00359         {
    00360                 WCHAR c = s[0];
    00361                 s.TrimLeft(L"mnlbspc ");
    00362                 switch(c)
    00363                 {
    00364                 case 'm':
    00365                         lastmoveto = m_pathTypesOrg.GetSize();
    00366                         if(firstmoveto == -1) firstmoveto = lastmoveto;
    00367                         while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_MOVETO); m_pathPointsOrg.Add(p);}
    00368                         break;
    00369                 case 'n':
    00370                         while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_MOVETONC); m_pathPointsOrg.Add(p);}
    00371                         break;
    00372                 case 'l':
    00373                         while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_LINETO); m_pathPointsOrg.Add(p);}
    00374                         break;
    00375                 case 'b':
    00376                         j = m_pathTypesOrg.GetSize();
    00377                         while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BEZIERTO); m_pathPointsOrg.Add(p); j++;}
    00378                         j = m_pathTypesOrg.GetSize() - ((m_pathTypesOrg.GetSize()-j)%3);
    00379                         m_pathTypesOrg.SetSize(j); m_pathPointsOrg.SetSize(j);
    00380                         break;
    00381                 case 's':
    00382                         j = lastsplinestart = m_pathTypesOrg.GetSize();
    00383                         i = 3;
    00384                         while(i-- && GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BSPLINETO); m_pathPointsOrg.Add(p); j++;}
    00385                         if(m_pathTypesOrg.GetSize()-lastsplinestart < 3) {m_pathTypesOrg.SetSize(lastsplinestart); m_pathPointsOrg.SetSize(lastsplinestart); lastsplinestart = -1;}
    00386                         // no break here
    00387                 case 'p':
    00388                         while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BSPLINEPATCHTO); m_pathPointsOrg.Add(p); j++;}
    00389                         break;
    00390                 case 'c':
    00391                         if(lastsplinestart > 0)
    00392                         {
    00393                                 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
    00394                                 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
    00395                                 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
    00396                                 p = m_pathPointsOrg[lastsplinestart-1]; // we need p for temp storage, because operator [] will return a reference to CPoint and Add() may reallocate its internal buffer (this is true for MFC 7.0 but not for 6.0, hehe)
    00397                                 m_pathPointsOrg.Add(p);
    00398                                 p = m_pathPointsOrg[lastsplinestart];
    00399                                 m_pathPointsOrg.Add(p);
    00400                                 p = m_pathPointsOrg[lastsplinestart+1];
    00401                                 m_pathPointsOrg.Add(p);
    00402                                 lastsplinestart = -1;
    00403                         }
    00404                         break;
    00405                 default:
    00406                         break;
    00407                 }
    00408         }
    00409 /*
    00410         LPCWSTR str = m_str;
    00411         while(*str)
    00412         {
    00413                 while(*str && *str != 'm' && *str != 'n' && *str != 'l' && *str != 'b' && *str != 's' && *str != 'p' && *str != 'c') str++;
    00414
    00415                 if(!*str) break;
    00416
    00417                 switch(*str++)
    00418                 {
    00419                 case 'm':
    00420                         lastmoveto = m_pathTypesOrg.GetSize();
    00421                         if(firstmoveto == -1) firstmoveto = lastmoveto;
    00422                         while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_MOVETO); m_pathPointsOrg.Add(p);}
    00423                         break;
    00424                 case 'n':
    00425                         while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_MOVETONC); m_pathPointsOrg.Add(p);}
    00426                         break;
    00427                 case 'l':
    00428                         while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_LINETO); m_pathPointsOrg.Add(p);}
    00429                         break;
    00430                 case 'b':
    00431                         j = m_pathTypesOrg.GetSize();
    00432                         while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BEZIERTO); m_pathPointsOrg.Add(p); j++;}
    00433                         j = m_pathTypesOrg.GetSize() - ((m_pathTypesOrg.GetSize()-j)%3);
    00434                         m_pathTypesOrg.SetSize(j); m_pathPointsOrg.SetSize(j);
    00435                         break;
    00436                 case 's':
    00437                         j = lastsplinestart = m_pathTypesOrg.GetSize();
    00438                         i = 3;
    00439                         while(i-- && GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BSPLINETO); m_pathPointsOrg.Add(p); j++;}
    00440                         if(m_pathTypesOrg.GetSize()-lastsplinestart < 3) {m_pathTypesOrg.SetSize(lastsplinestart); m_pathPointsOrg.SetSize(lastsplinestart); lastsplinestart = -1;}
    00441                         // no break here
    00442                 case 'p':
    00443                         while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BSPLINEPATCHTO); m_pathPointsOrg.Add(p); j++;}
    00444                         break;
    00445                 case 'c':
    00446                         if(lastsplinestart > 0)
    00447                         {
    00448                                 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
    00449                                 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
    00450                                 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
    00451                                 p = m_pathPointsOrg[lastsplinestart-1]; // we need p for temp storage, because operator [] will return a reference to CPoint and Add() may reallocate its internal buffer (this is true for MFC 7.0 but not for 6.0, hehe)
    00452                                 m_pathPointsOrg.Add(p);
    00453                                 p = m_pathPointsOrg[lastsplinestart];
    00454                                 m_pathPointsOrg.Add(p);
    00455                                 p = m_pathPointsOrg[lastsplinestart+1];
    00456                                 m_pathPointsOrg.Add(p);
    00457                                 lastsplinestart = -1;
    00458                         }
    00459                         break;
    00460                 default:
    00461                         break;
    00462                 }
    00463
    00464                 if(firstmoveto > 0) break;
    00465         }
    00466 */
    00467         if(lastmoveto == -1 || firstmoveto > 0)
    00468         {
    00469                 m_pathTypesOrg.RemoveAll();
    00470                 m_pathPointsOrg.RemoveAll();
    00471                 return(false);
    00472         }
    00473
    00474         int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
    00475
    00476         for(i = 0; i < m_pathTypesOrg.GetSize(); i++)
    00477         {
    00478                 m_pathPointsOrg[i].x = (int)(64 * m_scalex * m_pathPointsOrg[i].x);
    00479                 m_pathPointsOrg[i].y = (int)(64 * m_scaley * m_pathPointsOrg[i].y);
    00480                 if(minx > m_pathPointsOrg[i].x) minx = m_pathPointsOrg[i].x;
    00481                 if(miny > m_pathPointsOrg[i].y) miny = m_pathPointsOrg[i].y;
    00482                 if(maxx < m_pathPointsOrg[i].x) maxx = m_pathPointsOrg[i].x;
    00483                 if(maxy < m_pathPointsOrg[i].y) maxy = m_pathPointsOrg[i].y;
    00484         }
    00485
    00486         m_width = max(maxx - minx, 0);
    00487         m_ascent = max(maxy - miny, 0);
    00488
    00489         int baseline = (int)(64 * m_scaley * m_baseline);
    00490         m_descent = baseline;
    00491         m_ascent -= baseline;
    00492
    00493         m_width = ((int)(m_style.fontScaleX/100 * m_width) + 4) >> 3;
    00494         m_ascent = ((int)(m_style.fontScaleY/100 * m_ascent) + 4) >> 3;
    00495         m_descent = ((int)(m_style.fontScaleY/100 * m_descent) + 4) >> 3;
    00496
    00497         return(true);
    00498 }
    00499
    00500 bool CPolygon::CreatePath()
    00501 {
    00502         int len = m_pathTypesOrg.GetSize();
    00503         if(len == 0) return(false);
    00504
    00505         if(mPathPoints != len)
    00506         {
    00507                 mpPathTypes = (BYTE*)realloc(mpPathTypes, len*sizeof(BYTE));
    00508                 mpPathPoints = (POINT*)realloc(mpPathPoints, len*sizeof(POINT));
    00509                 if(!mpPathTypes || !mpPathPoints) return(false);
    00510                 mPathPoints = len;
    00511         }
    00512
    00513         memcpy(mpPathTypes, m_pathTypesOrg.GetData(), len*sizeof(BYTE));
    00514         memcpy(mpPathPoints, m_pathPointsOrg.GetData(), len*sizeof(POINT));
    00515
    00516         return(true);
    00517 }
    00518
    00519 // CClipper
    00520
    00521 CClipper::CClipper(CStringW str, CSize size, double scalex, double scaley)
    00522         : CPolygon(STSStyle(), str, 0, 0, 0, scalex, scaley, 0)
    00523 {
    00524         m_size.cx = m_size.cy = 0;
    00525         m_pAlphaMask = NULL;
    00526
    00527         if(size.cx < 0 || size.cy < 0 || !(m_pAlphaMask = new BYTE[size.cx*size.cy])) return;
    00528
    00529         m_size = size;
    00530
    00531         memset(m_pAlphaMask, 0, size.cx*size.cy);
    00532
    00533         Paint(CPoint(0, 0), CPoint(0, 0));
    00534
    00535         int w = mOverlayWidth, h = mOverlayHeight;
    00536
    00537         int x = (mOffsetX+4)>>3, y = (mOffsetY+4)>>3;
    00538         int xo = 0, yo = 0;
    00539
    00540         if(x < 0) {xo = -x; w -= -x; x = 0;}
    00541         if(y < 0) {yo = -y; h -= -y; y = 0;}
    00542         if(x+w > m_size.cx) w = m_size.cx-x;
    00543         if(y+h > m_size.cy) h = m_size.cy-y;
    00544
    00545         if(w <= 0 || h <= 0) return;
    00546
    00547         const BYTE* src = mpOverlayBuffer + 2*(mOverlayWidth * yo + xo);
    00548         BYTE* dst = m_pAlphaMask + m_size.cx * y + x;
    00549
    00550         while(h--)
    00551         {
    00552                 for(int wt=0; wt<w; ++wt)
    00553                         dst[wt] = src[wt*2];
    00554
    00555                 src += 2*mOverlayWidth;
    00556                 dst += m_size.cx;
    00557         }
    00558 }
    00559
    00560 CClipper::~CClipper()
    00561 {
    00562         if(m_pAlphaMask) delete [] m_pAlphaMask;
    00563         m_pAlphaMask = NULL;
    00564 }
    00565
    00566 CWord* CClipper::Copy()
    00567 {
    00568         return(new CClipper(m_str, m_size, m_scalex, m_scaley));
    00569 }
    00570
    00571 bool CClipper::Append(CWord* w)
    00572 {
    00573         return(false);
    00574 }
    00575
    00576 // CLine
    00577
    00578 CLine::~CLine()
    00579 {
    00580         POSITION pos = GetHeadPosition();
    00581         while(pos) delete GetNext(pos);
    00582 }
    00583
    00584 void CLine::Compact()
    00585 {
    00586         POSITION pos = GetHeadPosition();
    00587         while(pos)
    00588         {
    00589                 CWord* w = GetNext(pos);
    00590                 if(!w->m_fWhiteSpaceChar) break;
    00591
    00592                 m_width -= w->m_width;
    00593                 delete w;
    00594                 RemoveHead();
    00595         }
    00596
    00597         pos = GetTailPosition();
    00598         while(pos)
    00599         {
    00600                 CWord* w = GetPrev(pos);
    00601                 if(!w->m_fWhiteSpaceChar) break;
    00602
    00603                 m_width -= w->m_width;
    00604                 delete w;
    00605                 RemoveTail();
    00606         }
    00607
    00608         if(IsEmpty()) return;
    00609
    00610         CLine l;
    00611         l.AddTail(this);
    00612         RemoveAll();
    00613
    00614         CWord* last = NULL;
    00615
    00616         pos = l.GetHeadPosition();
    00617         while(pos)
    00618         {
    00619                 CWord* w = l.GetNext(pos);
    00620
    00621                 if(!last || !last->Append(w))
    00622                         AddTail(last = w->Copy());
    00623         }
    00624
    00625         m_ascent = m_descent = m_border = 0;
    00626
    00627         pos = GetHeadPosition();
    00628         while(pos)
    00629         {
    00630                 CWord* w = GetNext(pos);
    00631
    00632                 if(m_ascent < w->m_ascent) m_ascent = w->m_ascent;
    00633                 if(m_descent < w->m_descent) m_descent = w->m_descent;
    00634                 if(m_border < w->m_style.outlineWidth) m_border = (int)(w->m_style.outlineWidth+0.5);
    00635         }
    00636 }
    00637
    00638 CRect CLine::PaintShadow(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha)
    00639 {
    00640         CRect bbox(0, 0, 0, 0);
    00641
    00642         POSITION pos = GetHeadPosition();
    00643         while(pos)
    00644         {
    00645                 CWord* w = GetNext(pos);
    00646
    00647                 if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
    00648
    00649                 if(w->m_style.shadowDepth > 0)
    00650                 {
    00651                         int x = p.x + (int)(w->m_style.shadowDepth+0.5);
    00652                         int y = p.y + m_ascent - w->m_ascent + (int)(w->m_style.shadowDepth+0.5);
    00653
    00654                         DWORD a = 0xff - w->m_style.alpha[3];
    00655                         if(alpha > 0) a = MulDiv(a, 0xff - alpha, 0xff);
    00656                         COLORREF shadow = revcolor(w->m_style.colors[3]) | (a<<24);
    00657                         long sw[6] = {shadow, -1};
    00658
    00659                         w->Paint(CPoint(x, y), org);
    00660
    00661                         if(w->m_style.borderStyle == 0)
    00662                         {
    00663                                 bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw,
    00664                                         w->m_ktype > 0 || w->m_style.alpha[0] < 0xff,
    00665                                         w->m_style.outlineWidth > 0 && !(w->m_ktype == 2 && time < w->m_kstart));
    00666                         }
    00667                         else if(w->m_style.borderStyle == 1 && w->m_pOpaqueBox)
    00668                         {
    00669                                 bbox |= w->m_pOpaqueBox->Draw(spd, clipRect, pAlphaMask, x, y, sw, true, false);
    00670                         }
    00671                 }
    00672
    00673                 p.x += w->m_width;
    00674         }
    00675
    00676         return(bbox);
    00677 }
    00678
    00679 CRect CLine::PaintOutline(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha)
    00680 {
    00681         CRect bbox(0, 0, 0, 0);
    00682
    00683         POSITION pos = GetHeadPosition();
    00684         while(pos)
    00685         {
    00686                 CWord* w = GetNext(pos);
    00687
    00688                 if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
    00689
    00690 //              if((w->m_style.outlineWidth > 0 || w->m_style.borderStyle == 1 && w->m_style.outlineWidth == 0) && !(w->m_ktype == 2 && time < w->m_kstart))
    00691                 if(w->m_style.outlineWidth > 0 && !(w->m_ktype == 2 && time < w->m_kstart))
    00692                 {
    00693                         int x = p.x;
    00694                         int y = p.y + m_ascent - w->m_ascent;
    00695
    00696                         DWORD aoutline = w->m_style.alpha[2];
    00697                         if(alpha > 0) aoutline += MulDiv(alpha, 0xff - w->m_style.alpha[2], 0xff);
    00698                         COLORREF outline = revcolor(w->m_style.colors[2]) | ((0xff-aoutline)<<24);
    00699                         long sw[6] = {outline, -1};
    00700
    00701                         w->Paint(CPoint(x, y), org);
    00702
    00703                         if(w->m_style.borderStyle == 0)
    00704                         {
    00705                                 bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, !w->m_style.alpha[0] && !w->m_style.alpha[1], true);
    00706                         }
    00707                         else if(w->m_style.borderStyle == 1 && w->m_pOpaqueBox)
    00708                         {
    00709                                 bbox |= w->m_pOpaqueBox->Draw(spd, clipRect, pAlphaMask, x, y, sw, true, false);
    00710                         }
    00711                 }
    00712
    00713                 p.x += w->m_width;
    00714         }
    00715
    00716         return(bbox);
    00717 }
    00718
    00719 CRect CLine::PaintBody(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha)
    00720 {
    00721         CRect bbox(0, 0, 0, 0);
    00722
    00723         POSITION pos = GetHeadPosition();
    00724         while(pos)
    00725         {
    00726                 CWord* w = GetNext(pos);
    00727
    00728                 if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
    00729
    00730                 int x = p.x;
    00731                 int y = p.y + m_ascent - w->m_ascent;
    00732
    00733                 // colors
    00734
    00735                 DWORD aprimary = w->m_style.alpha[0];
    00736                 if(alpha > 0) aprimary += MulDiv(alpha, 0xff - w->m_style.alpha[0], 0xff);
    00737                 COLORREF primary = revcolor(w->m_style.colors[0]) | ((0xff-aprimary)<<24);
    00738
    00739                 DWORD asecondary = w->m_style.alpha[1];
    00740                 if(alpha > 0) asecondary += MulDiv(alpha, 0xff - w->m_style.alpha[1], 0xff);
    00741                 COLORREF secondary = revcolor(w->m_style.colors[1]) | ((0xff-asecondary)<<24);
    00742
    00743                 long sw[6] = {primary, 0, secondary};
    00744
    00745                 // karaoke
    00746
    00747                 double t;
    00748
    00749                 if(w->m_ktype == 0 || w->m_ktype == 2)
    00750                 {
    00751                         t = time < w->m_kstart ? 0 : 1;
    00752                 }
    00753                 else if(w->m_ktype == 1)
    00754                 {
    00755                         if(time < w->m_kstart) t = 0;
    00756                         else if(time < w->m_kend)
    00757                         {
    00758                                 t = 1.0 * (time - w->m_kstart) / (w->m_kend - w->m_kstart);
    00759
    00760                                 double angle = fmod(w->m_style.fontAngleZ, 360.0);
    00761                                 if(angle > 90 && angle < 270)
    00762                                 {
    00763                                         t = 1-t;
    00764                                         COLORREF tmp = sw[0]; sw[0] = sw[2]; sw[2] = tmp;
    00765                                 }
    00766                         }
    00767                         else t = 1.0;
    00768                 }
    00769
    00770                 if(t >= 1)
    00771                 {
    00772                         sw[1] = 0xffffffff;
    00773                 }
    00774
    00775                 sw[3] = (int)(w->m_style.outlineWidth + t*w->m_width) >> 3;
    00776                 sw[4] = sw[2];
    00777                 sw[5] = 0x00ffffff;
    00778
    00779                 w->Paint(CPoint(x, y), org);
    00780
    00781                 bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, true, false);
    00782
    00783                 p.x += w->m_width;
    00784         }
    00785
    00786         return(bbox);
    00787 }
    00788
    00789
    00790 // CSubtitle
    00791
    00792 CSubtitle::CSubtitle()
    00793 {
    00794         memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
    00795         m_pClipper = NULL;
    00796         m_scalex = m_scaley = 1;
    00797 }
    00798
    00799 CSubtitle::~CSubtitle()
    00800 {
    00801         Empty();
    00802 }
    00803
    00804 void CSubtitle::Empty()
    00805 {
    00806         POSITION pos = GetHeadPosition();
    00807         while(pos) delete GetNext(pos);
    00808
    00809         pos = m_words.GetHeadPosition();
    00810         while(pos) delete m_words.GetNext(pos);
    00811
    00812         for(int i = 0; i < EF_NUMBEROFEFFECTS; i++) {if(m_effects[i]) delete m_effects[i];}
    00813         memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
    00814
    00815         if(m_pClipper) delete m_pClipper;
    00816         m_pClipper = NULL;
    00817 }
    00818
    00819 int CSubtitle::GetFullWidth()
    00820 {
    00821         int width = 0;
    00822
    00823         POSITION pos = m_words.GetHeadPosition();
    00824         while(pos) width += m_words.GetNext(pos)->m_width;
    00825
    00826         return(width);
    00827 }
    00828
    00829 int CSubtitle::GetFullLineWidth(POSITION pos)
    00830 {
    00831         int width = 0;
    00832
    00833         while(pos)
    00834         {
    00835                 CWord* w = m_words.GetNext(pos);
    00836                 if(w->m_fLineBreak) break;
    00837                 width += w->m_width;
    00838         }
    00839
    00840         return(width);
    00841 }
    00842
    00843 int CSubtitle::GetWrapWidth(POSITION pos, int maxwidth)
    00844 {
    00845         if(m_wrapStyle == 0 || m_wrapStyle == 3)
    00846         {
    00847                 if(maxwidth > 0)
    00848                 {
    00849 //                      int fullwidth = GetFullWidth();
    00850                         int fullwidth = GetFullLineWidth(pos);
    00851
    00852                         int minwidth = fullwidth / ((abs(fullwidth) / maxwidth) + 1);
    00853
    00854                         int width = 0, wordwidth = 0;
    00855
    00856                         while(pos && width < minwidth)
    00857                         {
    00858                                 CWord* w = m_words.GetNext(pos);
    00859                                 wordwidth = w->m_width;
    00860                                 if(abs(width + wordwidth) < abs(maxwidth)) width += wordwidth;
    00861                         }
    00862
    00863                         maxwidth = width;
    00864
    00865                         if(m_wrapStyle == 3 && pos) maxwidth -= wordwidth;
    00866                 }
    00867         }
    00868         else if(m_wrapStyle == 1)
    00869         {
    00870 //              maxwidth = maxwidth;
    00871         }
    00872         else if(m_wrapStyle == 2)
    00873         {
    00874                 maxwidth = INT_MAX;
    00875         }
    00876
    00877         return(maxwidth);
    00878 }
    00879
    00880 CLine* CSubtitle::GetNextLine(POSITION& pos, int maxwidth)
    00881 {
    00882         if(pos == NULL) return(NULL);
    00883
    00884         CLine* ret = new CLine();
    00885         if(!ret) return(NULL);
    00886
    00887         ret->m_width = ret->m_ascent = ret->m_descent = ret->m_border = 0;
    00888
    00889         maxwidth = GetWrapWidth(pos, maxwidth);
    00890
    00891         bool fEmptyLine = true;
    00892
    00893         while(pos)
    00894         {
    00895                 CWord* w = m_words.GetNext(pos);
    00896
    00897                 if(ret->m_ascent < w->m_ascent) ret->m_ascent = w->m_ascent;
    00898                 if(ret->m_descent < w->m_descent) ret->m_descent = w->m_descent;
    00899                 if(ret->m_border < w->m_style.outlineWidth) ret->m_border = (int)(w->m_style.outlineWidth+0.5);
    00900
    00901                 if(w->m_fLineBreak)
    00902                 {
    00903                         if(fEmptyLine) {ret->m_ascent /= 2; ret->m_descent /= 2; ret->m_border = 0;}
    00904
    00905                         ret->Compact();
    00906
    00907                         return(ret);
    00908                 }
    00909
    00910                 fEmptyLine = false;
    00911
    00912                 bool fWSC = w->m_fWhiteSpaceChar;
    00913
    00914                 int width = w->m_width;
    00915                 POSITION pos2 = pos;
    00916                 while(pos2)
    00917                 {
    00918                         if(m_words.GetAt(pos2)->m_fWhiteSpaceChar != fWSC
    00919                         || m_words.GetAt(pos2)->m_fLineBreak) break;
    00920
    00921                         CWord* w2 = m_words.GetNext(pos2);
    00922                         width += w2->m_width;
    00923                 }
    00924
    00925                 if((ret->m_width += width) <= maxwidth || ret->IsEmpty())
    00926                 {
    00927                         ret->AddTail(w->Copy());
    00928
    00929                         while(pos != pos2)
    00930                         {
    00931                                 ret->AddTail(m_words.GetNext(pos)->Copy());
    00932                         }
    00933
    00934                         pos = pos2;
    00935                 }
    00936                 else
    00937                 {
    00938                         if(pos) m_words.GetPrev(pos);
    00939                         else pos = m_words.GetTailPosition();
    00940
    00941                         ret->m_width -= width;
    00942
    00943                         break;
    00944                 }
    00945         }
    00946
    00947         ret->Compact();
    00948
    00949         return(ret);
    00950 }
    00951
    00952 void CSubtitle::CreateClippers(CSize size)
    00953 {
    00954         size.cx >>= 3;
    00955         size.cy >>= 3;
    00956
    00957         if(m_effects[EF_BANNER] && m_effects[EF_BANNER]->param[2])
    00958         {
    00959                 int width = m_effects[EF_BANNER]->param[2];
    00960
    00961                 int w = size.cx, h = size.cy;
    00962
    00963                 if(!m_pClipper)
    00964                 {
    00965                         CStringW str;
    00966                         str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
    00967                         m_pClipper = new CClipper(str, size, 1, 1);
    00968                         if(!m_pClipper) return;
    00969                 }
    00970
    00971                 int da = (64<<8)/width;
    00972                 BYTE* am = m_pClipper->m_pAlphaMask;
    00973
    00974                 for(int j = 0; j < h; j++, am += w)
    00975                 {
    00976                         int a = 0;
    00977                         int k = min(width, w);
    00978
    00979                         for(int i = 0; i < k; i++, a += da)
    00980                                 am[i] = (am[i]*a)>>14;
    00981
    00982                         a = 0x40<<8;
    00983                         k = w-width;
    00984
    00985                         if(k < 0) {a -= -k*da; k = 0;}
    00986
    00987                         for(int i = k; i < w; i++, a -= da)
    00988                                 am[i] = (am[i]*a)>>14;
    00989                 }
    00990         }
    00991         else if(m_effects[EF_SCROLL] && m_effects[EF_SCROLL]->param[4])
    00992         {
    00993                 int height = m_effects[EF_SCROLL]->param[4];
    00994
    00995                 int w = size.cx, h = size.cy;
    00996
    00997                 if(!m_pClipper)
    00998                 {
    00999                         CStringW str;
    01000                         str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
    01001                         m_pClipper = new CClipper(str, size, 1, 1);
    01002                         if(!m_pClipper) return;
    01003                 }
    01004
    01005                 int da = (64<<8)/height;
    01006                 int a = 0;
    01007                 int k = m_effects[EF_SCROLL]->param[0]>>3;
    01008                 int l = k+height;
    01009                 if(k < 0) {a += -k*da; k = 0;}
    01010                 if(l > h) {l = h;}
    01011
    01012                 if(k < h)
    01013                 {
    01014                         BYTE* am = &m_pClipper->m_pAlphaMask[k*w];
    01015
    01016                         memset(m_pClipper->m_pAlphaMask, 0, am - m_pClipper->m_pAlphaMask);
    01017
    01018                         for(int j = k; j < l; j++, a += da)
    01019                         {
    01020                                 for(int i = 0; i < w; i++, am++)
    01021                                         *am = ((*am)*a)>>14;
    01022                         }
    01023                 }
    01024
    01025                 da = -(64<<8)/height;
    01026                 a = 0x40<<8;
    01027                 l = m_effects[EF_SCROLL]->param[1]>>3;
    01028                 k = l-height;
    01029                 if(k < 0) {a += -k*da; k = 0;}
    01030                 if(l > h) {l = h;}
    01031
    01032                 if(k < h)
    01033                 {
    01034                         BYTE* am = &m_pClipper->m_pAlphaMask[k*w];
    01035
    01036                         int j = k;
    01037                         for(; j < l; j++, a += da)
    01038                         {
    01039                                 for(int i = 0; i < w; i++, am++)
    01040                                         *am = ((*am)*a)>>14;
    01041                         }
    01042
    01043                         memset(am, 0, (h-j)*w);
    01044                 }
    01045         }
    01046 }
    01047
    01048 void CSubtitle::MakeLines(CSize size, CRect marginRect)
    01049 {
    01050         CSize spaceNeeded(0, 0);
    01051
    01052         bool fFirstLine = true;
    01053
    01054         m_topborder = m_bottomborder = 0;
    01055
    01056         CLine* l = NULL;
    01057
    01058         POSITION pos = m_words.GetHeadPosition();
    01059         while(pos)
    01060         {
    01061                 l = GetNextLine(pos, size.cx - marginRect.left - marginRect.right);
    01062                 if(!l) break;
    01063
    01064                 if(fFirstLine) {m_topborder = l->m_border; fFirstLine = false;}
    01065
    01066                 spaceNeeded.cx = max(l->m_width, spaceNeeded.cx);
    01067                 spaceNeeded.cy += l->m_ascent + l->m_descent;
    01068
    01069                 AddTail(l);
    01070         }
    01071
    01072         if(l) m_bottomborder = l->m_border;
    01073
    01074         m_rect = CRect(
    01075                 CPoint((m_scrAlignment%3) == 1 ? marginRect.left
    01076                                 : (m_scrAlignment%3) == 2 ? (marginRect.left + (size.cx - marginRect.right) - spaceNeeded.cx + 1) / 2
    01077                                 : (size.cx - marginRect.right - spaceNeeded.cx),
    01078                                 m_scrAlignment <= 3 ? (size.cy - marginRect.bottom - spaceNeeded.cy)
    01079                                 : m_scrAlignment <= 6 ? (marginRect.top + (size.cy - marginRect.bottom) - spaceNeeded.cy + 1) / 2
    01080                                 : marginRect.top),
    01081                 spaceNeeded);
    01082 }
    01083
    01084 // CScreenLayoutAllocator
    01085
    01086 void CScreenLayoutAllocator::Empty()
    01087 {
    01088         m_subrects.RemoveAll();
    01089 }
    01090
    01091 void CScreenLayoutAllocator::AdvanceToSegment(int segment, const CSubArray& sa)
    01092 {
    01093         POSITION pos = m_subrects.GetHeadPosition();
    01094         while(pos)
    01095         {
    01096                 POSITION prev = pos;
    01097
    01098                 SubRect& sr = m_subrects.GetNext(pos);
    01099
    01100                 bool fFound = false;
    01101
    01102                 if(abs(sr.segment - segment) <= 1) // using abs() makes it possible to play the subs backwards, too :)
    01103                 {
    01104                         for(int i = 0; i < sa.GetSize() && !fFound; i++)
    01105                         {
    01106                                 if(sa[i] == sr.entry)
    01107                                 {
    01108                                         sr.segment = segment;
    01109                                         fFound = true;
    01110                                 }
    01111                         }
    01112                 }
    01113
    01114                 if(!fFound) m_subrects.RemoveAt(prev);
    01115         }
    01116 }
    01117
    01118 CRect CScreenLayoutAllocator::AllocRect(CSubtitle* s, int segment, int entry, int layer, int collisions)
    01119 {
    01120         // TODO: handle collisions == 1 (reversed collisions)
    01121
    01122         POSITION pos = m_subrects.GetHeadPosition();
    01123         while(pos)
    01124         {
    01125                 SubRect& sr = m_subrects.GetNext(pos);
    01126                 if(sr.segment == segment && sr.entry == entry)
    01127                 {
    01128                         return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
    01129                 }
    01130         }
    01131
    01132         CRect r = s->m_rect + CRect(0, s->m_topborder, 0, s->m_bottomborder);
    01133
    01134         bool fSearchDown = s->m_scrAlignment > 3;
    01135
    01136         bool fOK;
    01137
    01138         do
    01139         {
    01140                 fOK = true;
    01141
    01142                 pos = m_subrects.GetHeadPosition();
    01143                 while(pos)
    01144                 {
    01145                         SubRect& sr = m_subrects.GetNext(pos);
    01146
    01147                         if(layer == sr.layer && !(r & sr.r).IsRectEmpty())
    01148                         {
    01149                                 if(fSearchDown)
    01150                                 {
    01151                                         r.bottom = sr.r.bottom + r.Height();
    01152                                         r.top = sr.r.bottom;
    01153                                 }
    01154                                 else
    01155                                 {
    01156                                         r.top = sr.r.top - r.Height();
    01157                                         r.bottom = sr.r.top;
    01158                                 }
    01159
    01160                                 fOK = false;
    01161                         }
    01162                 }
    01163         }
    01164         while(!fOK);
    01165
    01166         SubRect sr;
    01167         sr.r = r;
    01168         sr.segment = segment;
    01169         sr.entry = entry;
    01170         sr.layer = layer;
    01171         m_subrects.AddTail(sr);
    01172
    01173         return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
    01174 }
    01175
    01176 // CRenderedTextSubtitle
    01177
    01178 CRenderedTextSubtitle::CRenderedTextSubtitle(CCritSec* pLock)
    01179         : ISubPicProviderImpl(pLock)
    01180 {
    01181         m_size = CSize(0, 0);
    01182
    01183         if(g_hDC_refcnt == 0)
    01184         {
    01185                 g_hDC = CreateCompatibleDC(NULL);
    01186                 SetBkMode(g_hDC, TRANSPARENT);
    01187                 SetTextColor(g_hDC, 0xffffff);
    01188                 SetMapMode(g_hDC, MM_TEXT);
    01189         }
    01190
    01191         g_hDC_refcnt++;
    01192 }
    01193
    01194 CRenderedTextSubtitle::~CRenderedTextSubtitle()
    01195 {
    01196         Deinit();
    01197
    01198         g_hDC_refcnt--;
    01199         if(g_hDC_refcnt == 0) DeleteDC(g_hDC);
    01200 }
    01201
    01202 void CRenderedTextSubtitle::Copy(CSimpleTextSubtitle& sts)
    01203 {
    01204         CSimpleTextSubtitle::Copy(sts);
    01205
    01206         m_size = CSize(0, 0);
    01207
    01208         if(CRenderedTextSubtitle* pRTS = dynamic_cast<CRenderedTextSubtitle*>(&sts))
    01209         {
    01210                 m_size = pRTS->m_nSize;
    01211         }
    01212 }
    01213
    01214 void CRenderedTextSubtitle::Empty()
    01215 {
    01216         Deinit();
    01217
    01218         CSimpleTextSubtitle::Empty();
    01219 }
    01220
    01221 void CRenderedTextSubtitle::OnChanged()
    01222 {
    01223         CSimpleTextSubtitle::OnChanged();
    01224
    01225         POSITION pos = m_subtitleCache.GetStartPosition();
    01226         while(pos)
    01227         {
    01228                 int i;
    01229                 CSubtitle* s;
    01230         m_subtitleCache.GetNextAssoc(pos, i, s);
    01231                 delete s;
    01232         }
    01233
    01234         m_subtitleCache.RemoveAll();
    01235
    01236         m_sla.Empty();
    01237 }
    01238
    01239 bool CRenderedTextSubtitle::Init(CSize size, CRect vidrect)
    01240 {
    01241         Deinit();
    01242
    01243         m_size = CSize(size.cx*8, size.cy*8);
    01244         m_vidrect = CRect(vidrect.left*8, vidrect.top*8, vidrect.right*8, vidrect.bottom*8);
    01245
    01246         m_sla.Empty();
    01247
    01248         return(true);
    01249 }
    01250
    01251 void CRenderedTextSubtitle::Deinit()
    01252 {
    01253         POSITION pos = m_subtitleCache.GetStartPosition();
    01254         while(pos)
    01255         {
    01256                 int i;
    01257                 CSubtitle* s;
    01258         m_subtitleCache.GetNextAssoc(pos, i, s);
    01259                 delete s;
    01260         }
    01261
    01262         m_subtitleCache.RemoveAll();
    01263
    01264         m_sla.Empty();
    01265
    01266         m_size = CSize(0, 0);
    01267         m_vidrect.SetRectEmpty();
    01268 }
    01269
    01270 void CRenderedTextSubtitle::ParseEffect(CSubtitle* sub, CString str)
    01271 {
    01272         str.Trim();
    01273         if(!sub || str.IsEmpty()) return;
    01274
    01275         const TCHAR* s = _tcschr(str, ';');
    01276         if(!s) {s = (LPTSTR)(LPCTSTR)str; s += str.GetLength()-1;}
    01277         s++;
    01278         CString effect = str.Left(s - str);
    01279
    01280         if(!effect.CompareNoCase(_T("Banner;")))
    01281         {
    01282                 int delay, lefttoright = 0, fadeawaywidth = 0;
    01283                 if(_stscanf(s, _T("%d;%d;%d"), &delay, &lefttoright, &fadeawaywidth) < 1) return;
    01284
    01285                 Effect* e = new Effect;
    01286                 if(!e) return;
    01287
    01288                 sub->m_effects[e->type = EF_BANNER] = e;
    01289                 e->param[0] = (int)(max(1.0*delay/sub->m_scalex, 1));
    01290                 e->param[1] = lefttoright;
    01291                 e->param[2] = (int)(sub->m_scalex*fadeawaywidth);
    01292
    01293                 sub->m_wrapStyle = 2;
    01294         }
    01295         else if(!effect.CompareNoCase(_T("Scroll up;")) || !effect.CompareNoCase(_T("Scroll down;")))
    01296         {
    01297                 int top, bottom, delay, fadeawayheight = 0;
    01298                 if(_stscanf(s, _T("%d;%d;%d;%d"), &top, &bottom, &delay, &fadeawayheight) < 3) return;
    01299
    01300                 if(top > bottom) {int tmp = top; top = bottom; bottom = tmp;}
    01301
    01302                 Effect* e = new Effect;
    01303                 if(!e) return;
    01304
    01305                 sub->m_effects[e->type = EF_SCROLL] = e;
    01306                 e->param[0] = (int)(sub->m_scaley*top*8);
    01307                 e->param[1] = (int)(sub->m_scaley*bottom*8);
    01308                 e->param[2] = (int)(max(1.0*delay/sub->m_scaley, 1));
    01309                 e->param[3] = (effect.GetLength() == 12);
    01310                 e->param[4] = (int)(sub->m_scaley*fadeawayheight);
    01311         }
    01312 }
    01313
    01314 void CRenderedTextSubtitle::ParseString(CSubtitle* sub, CStringW str, STSStyle& style)
    01315 {
    01316         if(!sub) return;
    01317
    01318         str.Replace(L"\\N", L"\n");
    01319         str.Replace(L"\\n", (sub->m_wrapStyle < 2 || sub->m_wrapStyle == 3) ? L" " : L"\n");
    01320         str.Replace(L"\\h", L"\x00A0");
    01321
    01322         for(int i = 0, j = 0, len = str.GetLength(); j <= len; j++)
    01323         {
    01324                 WCHAR c = str[j];
    01325
    01326                 if(c != '\n' && c != ' ' && c != '\x00A0' && c != 0)
    01327                         continue;
    01328
    01329                 if(i < j)
    01330                 {
    01331                         if(CWord* w = new CText(style, str.Mid(i, j-i), m_ktype, m_kstart, m_kend))
    01332                         {
    01333                                 sub->m_words.AddTail(w);
    01334                                 m_kstart = m_kend;
    01335                         }
    01336                 }
    01337
    01338                 if(c == '\n')
    01339                 {
    01340                         if(CWord* w = new CText(style, CStringW(), m_ktype, m_kstart, m_kend))
    01341                         {
    01342                                 sub->m_words.AddTail(w);
    01343                                 m_kstart = m_kend;
    01344                         }
    01345                 }
    01346                 else if(c == ' ' || c == '\x00A0')
    01347                 {
    01348                         if(CWord* w = new CText(style, CStringW(c), m_ktype, m_kstart, m_kend))
    01349                         {
    01350                                 sub->m_words.AddTail(w);
    01351                                 m_kstart = m_kend;
    01352                         }
    01353                 }
    01354
    01355                 i = j+1;
    01356         }
    01357
    01358         return;
    01359 }
    01360
    01361 void CRenderedTextSubtitle::ParsePolygon(CSubtitle* sub, CStringW str, STSStyle& style)
    01362 {
    01363         if(!sub || !str.GetLength() || !m_nPolygon) return;
    01364
    01365         if(CWord* w = new CPolygon(style, str, m_ktype, m_kstart, m_kend, sub->m_scalex/(1<<(m_nPolygon-1)), sub->m_scaley/(1<<(m_nPolygon-1)), m_polygonBaselineOffset))
    01366         {
    01367                 sub->m_words.AddTail(w);
    01368                 m_kstart = m_kend;
    01369         }
    01370 }
    01371
    01372 bool CRenderedTextSubtitle::ParseSSATag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org, bool fAnimate)
    01373 {
    01374         if(!sub) return(false);
    01375
    01376         int nTags = 0, nUnrecognizedTags = 0;
    01377
    01378         for(int i = 0, j; (j = str.Find('\\', i)) >= 0; i = j)
    01379         {
    01380                 CStringW cmd;
    01381                 for(WCHAR c = str[++j]; c && c != '(' && c != '\\'; cmd += c, c = str[++j]);
    01382                 cmd.Trim();
    01383                 if(cmd.IsEmpty()) continue;
    01384
    01385                 CArray<CStringW> params;
    01386
    01387                 if(str[j] == '(')
    01388                 {
    01389                         CStringW param;
    01390                         for(WCHAR c = str[++j]; c && c != ')'; param += c, c = str[++j]);
    01391                         param.Trim();
    01392
    01393                         while(!param.IsEmpty())
    01394                         {
    01395                                 int i = param.Find(','), j = param.Find('\\');
    01396
    01397                                 if(i >= 0 && (j < 0 || i < j))
    01398                                 {
    01399                                         CStringW s = param.Left(i).Trim();
    01400                                         if(!s.IsEmpty()) params.Add(s);
    01401                                         param = i+1 < param.GetLength() ? param.Mid(i+1) : L"";
    01402                                 }
    01403                                 else
    01404                                 {
    01405                                         param.Trim();
    01406                                         if(!param.IsEmpty()) params.Add(param);
    01407                                         param.Empty();
    01408                                 }
    01409                         }
    01410                 }
    01411
    01412                 if(!cmd.Find(L"1c") || !cmd.Find(L"2c") || !cmd.Find(L"3c") || !cmd.Find(L"4c"))
    01413                         params.Add(cmd.Mid(2).Trim(L"&H")), cmd = cmd.Left(2);
    01414                 else if(!cmd.Find(L"1a") || !cmd.Find(L"2a") || !cmd.Find(L"3a") || !cmd.Find(L"4a"))
    01415                         params.Add(cmd.Mid(2).Trim(L"&H")), cmd = cmd.Left(2);
    01416                 else if(!cmd.Find(L"alpha"))
    01417                         params.Add(cmd.Mid(5).Trim(L"&H")), cmd = cmd.Left(5);
    01418                 else if(!cmd.Find(L"an"))
    01419                         params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
    01420                 else if(!cmd.Find(L"a"))
    01421                         params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
    01422                 else if(!cmd.Find(L"bord"))
    01423                         params.Add(cmd.Mid(4)), cmd = cmd.Left(4);
    01424                 else if(!cmd.Find(L"be"))
    01425                         params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
    01426                 else if(!cmd.Find(L"b"))
    01427                         params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
    01428                 else if(!cmd.Find(L"clip"))
    01429                         ;
    01430                 else if(!cmd.Find(L"c"))
    01431                         params.Add(cmd.Mid(1).Trim(L"&H")), cmd = cmd.Left(1);
    01432         else if(!cmd.Find(L"fade"))
    01433                         ;
    01434                 else if(!cmd.Find(L"fe"))
    01435                         params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
    01436                 else if(!cmd.Find(L"fn"))
    01437                         params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
    01438                 else if(!cmd.Find(L"frx") || !cmd.Find(L"fry") || !cmd.Find(L"frz"))
    01439                         params.Add(cmd.Mid(3)), cmd = cmd.Left(3);
    01440                 else if(!cmd.Find(L"fr"))
    01441                         params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
    01442                 else if(!cmd.Find(L"fscx") || !cmd.Find(L"fscy"))
    01443                         params.Add(cmd.Mid(4)), cmd = cmd.Left(4);
    01444                 else if(!cmd.Find(L"fsc"))
    01445                         params.Add(cmd.Mid(3)), cmd = cmd.Left(3);
    01446                 else if(!cmd.Find(L"fsp"))
    01447                         params.Add(cmd.Mid(3)), cmd = cmd.Left(3);
    01448                 else if(!cmd.Find(L"fs"))
    01449                         params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
    01450                 else if(!cmd.Find(L"i"))
    01451                         params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
    01452                 else if(!cmd.Find(L"kf") || !cmd.Find(L"ko"))
    01453                         params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
    01454                 else if(!cmd.Find(L"k") || !cmd.Find(L"K"))
    01455                         params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
    01456                 else if(!cmd.Find(L"move"))
    01457                         ;
    01458                 else if(!cmd.Find(L"org"))
    01459                         ;
    01460                 else if(!cmd.Find(L"pbo"))
    01461                         params.Add(cmd.Mid(3)), cmd = cmd.Left(3);
    01462                 else if(!cmd.Find(L"pos"))
    01463                         ;
    01464                 else if(!cmd.Find(L"p"))
    01465                         params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
    01466                 else if(!cmd.Find(L"q"))
    01467                         params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
    01468                 else if(!cmd.Find(L"r"))
    01469                         params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
    01470                 else if(!cmd.Find(L"shad"))
    01471                         params.Add(cmd.Mid(4)), cmd = cmd.Left(4);
    01472                 else if(!cmd.Find(L"s"))
    01473                         params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
    01474                 else if(!cmd.Find(L"t"))
    01475                         ;
    01476                 else if(!cmd.Find(L"u"))
    01477                         params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
    01478                 else
    01479                         nUnrecognizedTags++;
    01480
    01481                 nTags++;
    01482
    01483                 // TODO: call ParseStyleModifier(cmd, params, ..) and move the rest there
    01484
    01485                 CStringW p = params.GetCount() > 0 ? params[0] : L"";
    01486
    01487                 if(cmd == "1c" || cmd == L"2c" || cmd == L"3c" || cmd == L"4c")
    01488                 {
    01489                         int i = cmd[0] - '1';
    01490
    01491                         DWORD c = wcstol(p, NULL, 16);
    01492                         style.colors[i] = !p.IsEmpty()
    01493                                 ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
    01494                                   |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
    01495                                   |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
    01496                                 : org.colors[i];
    01497                 }
    01498                 else if(cmd == L"1a" || cmd == L"2a" || cmd == L"3a" || cmd == L"4a")
    01499                 {
    01500                         int i = cmd[0] - '1';
    01501
    01502                         style.alpha[i] = !p.IsEmpty()
    01503                                 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
    01504                                 : org.alpha[i];
    01505                 }
    01506                 else if(cmd == L"alpha")
    01507                 {
    01508                         for(int i = 0; i < 4; i++)
    01509                         {
    01510                                 style.alpha[i] = !p.IsEmpty()
    01511                                         ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
    01512                                         : org.alpha[i];
    01513                         }
    01514                 }
    01515                 else if(cmd == L"an")
    01516                 {
    01517                         int n = wcstol(p, NULL, 10);
    01518                         if(sub->m_scrAlignment < 0)
    01519                 sub->m_scrAlignment = (n > 0 && n < 10) ? n : org.scrAlignment;
    01520                 }
    01521                 else if(cmd == L"a")
    01522                 {
    01523                         int n = wcstol(p, NULL, 10);
    01524                         if(sub->m_scrAlignment < 0)
    01525                 sub->m_scrAlignment = (n > 0 && n < 12) ? ((((n-1)&3)+1)+((n&4)?6:0)+((n&8)?3:0)) : org.scrAlignment;
    01526                 }
    01527                 else if(cmd == L"bord")
    01528                 {
    01529                         double n = CalcAnimation(wcstod(p, NULL), style.outlineWidth, fAnimate);
    01530                         style.outlineWidth = !p.IsEmpty()
    01531                                 ? (n < 0 ? 0 : n)
    01532                                 : org.outlineWidth;
    01533                 }
    01534                 else if(cmd == L"be")
    01535                 {
    01536                         int n = wcstol(p, NULL, 10);
    01537                         style.fBlur = !p.IsEmpty()
    01538                                 ? (n == 0 ? false : n == 1 ? true : org.fBlur)
    01539                                 : org.fBlur;
    01540                 }
    01541                 else if(cmd == L"b")
    01542                 {
    01543                         int n = wcstol(p, NULL, 10);
    01544                         style.fontWeight = !p.IsEmpty()
    01545                                 ? (n == 0 ? FW_NORMAL : n == 1 ? FW_BOLD : n >= 100 ? n : org.fontWeight)
    01546                                 : org.fontWeight;
    01547                 }
    01548                 else if(cmd == L"clip")
    01549                 {
    01550                         if(params.GetCount() == 1 && !sub->m_pClipper)
    01551                         {
    01552                                 sub->m_pClipper = new CClipper(params[0], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex, sub->m_scaley);
    01553                         }
    01554                         else if(params.GetCount() == 2 && !sub->m_pClipper)
    01555                         {
    01556                                 int scale = max(wcstol(p, NULL, 10), 1);
    01557                                 sub->m_pClipper = new CClipper(params[1], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex/(1<<(scale-1)), sub->m_scaley/(1<<(scale-1)));
    01558                         }
    01559                         else if(params.GetCount() == 4)
    01560                         {
    01561                                 sub->m_clip.SetRect(
    01562                                         (int)CalcAnimation(sub->m_scalex*wcstol(params[0], NULL, 10), sub->m_clip.left, fAnimate),
    01563                                         (int)CalcAnimation(sub->m_scaley*wcstol(params[1], NULL, 10), sub->m_clip.top, fAnimate),
    01564                                         (int)CalcAnimation(sub->m_scalex*wcstol(params[2], NULL, 10), sub->m_clip.right, fAnimate),
    01565                                         (int)CalcAnimation(sub->m_scaley*wcstol(params[3], NULL, 10), sub->m_clip.bottom, fAnimate));
    01566                         }
    01567                 }
    01568                 else if(cmd == L"c")
    01569                 {
    01570                         DWORD c = wcstol(p, NULL, 16);
    01571                         style.colors[0] = !p.IsEmpty()
    01572                                 ? (((int)CalcAnimation(c&0xff, style.colors[0]&0xff, fAnimate))&0xff
    01573                                   |((int)CalcAnimation(c&0xff00, style.colors[0]&0xff00, fAnimate))&0xff00
    01574                                   |((int)CalcAnimation(c&0xff0000, style.colors[0]&0xff0000, fAnimate))&0xff0000)
    01575                                 : org.colors[0];
    01576                 }
    01577         else if(cmd == L"fade" || cmd == L"fad")
    01578                 {
    01579                         if(params.GetCount() == 7 && !sub->m_effects[EF_FADE])// {\fade(a1=param[0], a2=param[1], a3=param[2], t1=t[0], t2=t[1], t3=t[2], t4=t[3])
    01580                         {
    01581                                 if(Effect* e = new Effect)
    01582                                 {
    01583                                         for(int i = 0; i < 3; i++)
    01584                                                 e->param[i] = wcstol(params[i], NULL, 10);
    01585                                         for(int i = 0; i < 4; i++)
    01586                                                 e->t[i] = wcstol(params[3+i], NULL, 10);
    01587
    01588                                         sub->m_effects[EF_FADE] = e;
    01589                                 }
    01590                         }
    01591                         else if(params.GetCount() == 2 && !sub->m_effects[EF_FADE]) // {\fad(t1=t[1], t2=t[2])
    01592                         {
    01593                                 if(Effect* e = new Effect)
    01594                                 {
    01595                                         e->param[0] = e->param[2] = 0xff;
    01596                                         e->param[1] = 0x00;
    01597                                         for(int i = 1; i < 3; i++)
    01598                                                 e->t[i] = wcstol(params[i-1], NULL, 10);
    01599                                         e->t[0] = e->t[3] = -1; // will be substituted with "start" and "end"
    01600
    01601                                         sub->m_effects[EF_FADE] = e;
    01602                                 }
    01603                         }
    01604                 }
    01605                 else if(cmd == L"fe")
    01606                 {
    01607                         int n = wcstol(p, NULL, 10);
    01608                         style.charSet = !p.IsEmpty()
    01609                                 ? n
    01610                                 : org.charSet;
    01611                 }
    01612                 else if(cmd == L"fn")
    01613                 {
    01614                         style.fontName = (!p.IsEmpty() && p != '0')
    01615                                 ? CString(p).Trim()
    01616                                 : org.fontName;
    01617                 }
    01618                 else if(cmd == L"frx")
    01619                 {
    01620                         style.fontAngleX = !p.IsEmpty()
    01621                                 ? CalcAnimation(wcstod(p, NULL), style.fontAngleX, fAnimate)
    01622                                 : org.fontAngleX;
    01623                 }
    01624                 else if(cmd == L"fry")
    01625                 {
    01626                         style.fontAngleY = !p.IsEmpty()
    01627                                 ? CalcAnimation(wcstod(p, NULL), style.fontAngleY, fAnimate)
    01628                                 : org.fontAngleY;
    01629                 }
    01630                 else if(cmd == L"frz" || cmd == L"fr")
    01631                 {
    01632                         style.fontAngleZ = !p.IsEmpty()
    01633                                 ? CalcAnimation(wcstod(p, NULL), style.fontAngleZ, fAnimate)
    01634                                 : org.fontAngleZ;
    01635                 }
    01636                 else if(cmd == L"fscx")
    01637                 {
    01638                         double n = CalcAnimation(wcstol(p, NULL, 10), style.fontScaleX, fAnimate);
    01639                         style.fontScaleX = !p.IsEmpty()
    01640                                 ? ((n < 0) ? 0 : n)
    01641                                 : org.fontScaleX;
    01642                 }
    01643                 else if(cmd == L"fscy")
    01644                 {
    01645                         double n = CalcAnimation(wcstol(p, NULL, 10), style.fontScaleY, fAnimate);
    01646                         style.fontScaleY = !p.IsEmpty()
    01647                                 ? ((n < 0) ? 0 : n)
    01648                                 : org.fontScaleY;
    01649                 }
    01650                 else if(cmd == L"fsc")
    01651                 {
    01652                         style.fontScaleX = org.fontScaleX;
    01653                         style.fontScaleY = org.fontScaleY;
    01654                 }
    01655                 else if(cmd == L"fsp")
    01656                 {
    01657                         style.fontSpacing = !p.IsEmpty()
    01658                                 ? CalcAnimation(wcstod(p, NULL), style.fontSpacing, fAnimate)
    01659                                 : org.fontSpacing;
    01660                 }
    01661                 else if(cmd == L"fs")
    01662                 {
    01663                         if(!p.IsEmpty())
    01664                         {
    01665                                 if(p[0] == '-' || p[0] == '+')
    01666                                 {
    01667                                         double n = CalcAnimation(style.fontSize + style.fontSize*wcstol(p, NULL, 10)/10, style.fontSize, fAnimate);
    01668                                         style.fontSize = (n > 0) ? n : org.fontSize;
    01669                                 }
    01670                                 else
    01671                                 {
    01672                                         double n = CalcAnimation(wcstol(p, NULL, 10), style.fontSize, fAnimate);
    01673                                         style.fontSize = (n > 0) ? n : org.fontSize;
    01674                                 }
    01675                         }
    01676                         else
    01677                         {
    01678                                 style.fontSize = org.fontSize;
    01679                         }
    01680                 }
    01681                 else if(cmd == L"i")
    01682                 {
    01683                         int n = wcstol(p, NULL, 10);
    01684                         style.fItalic = !p.IsEmpty()
    01685                                 ? (n == 0 ? false : n == 1 ? true : org.fItalic)
    01686                                 : org.fItalic;
    01687                 }
    01688                 else if(cmd == L"kf" || cmd == L"K")
    01689                 {
    01690                         m_ktype = 1;
    01691                         m_kstart = m_kend;
    01692                         m_kend += !p.IsEmpty()
    01693                                 ? wcstol(p, NULL, 10)*10
    01694                                 : 1000;
    01695                 }
    01696                 else if(cmd == L"ko")
    01697                 {
    01698                         m_ktype = 2;
    01699                         m_kstart = m_kend;
    01700                         m_kend += !p.IsEmpty()
    01701                                 ? wcstol(p, NULL, 10)*10
    01702                                 : 1000;
    01703                 }
    01704                 else if(cmd == L"k")
    01705                 {
    01706                         m_ktype = 0;
    01707                         m_kstart = m_kend;
    01708                         m_kend += !p.IsEmpty()
    01709                                 ? wcstol(p, NULL, 10)*10
    01710                                 : 1000;
    01711                 }
    01712                 else if(cmd == L"move") // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3][, t1=t[0], t2=t[1]])}
    01713                 {
    01714                         if((params.GetCount() == 4 || params.GetCount() == 6) && !sub->m_effects[EF_MOVE])
    01715                         {
    01716                                 if(Effect* e = new Effect)
    01717                                 {
    01718                                         e->param[0] = (int)(sub->m_scalex*wcstol(params[0], NULL, 10)*8);
    01719                                         e->param[1] = (int)(sub->m_scaley*wcstol(params[1], NULL, 10)*8);
    01720                                         e->param[2] = (int)(sub->m_scalex*wcstol(params[2], NULL, 10)*8);
    01721                                         e->param[3] = (int)(sub->m_scaley*wcstol(params[3], NULL, 10)*8);
    01722
    01723                                         e->t[0] = e->t[1] = -1;
    01724
    01725                                         if(params.GetCount() == 6)
    01726                                         {
    01727                                                 for(int i = 0; i < 2; i++)
    01728                                                         e->t[i] = wcstol(params[4+i], NULL, 10);
    01729                                         }
    01730
    01731                                         sub->m_effects[EF_MOVE] = e;
    01732                                 }
    01733                         }
    01734                 }
    01735                 else if(cmd == L"org") // {\org(x=param[0], y=param[1])}
    01736                 {
    01737                         if(params.GetCount() == 2 && !sub->m_effects[EF_ORG])
    01738                         {
    01739                                 if(Effect* e = new Effect)
    01740                                 {
    01741                                         e->param[0] = (int)(sub->m_scalex*wcstol(params[0], NULL, 10)*8);
    01742                                         e->param[1] = (int)(sub->m_scaley*wcstol(params[1], NULL, 10)*8);
    01743
    01744                                         sub->m_effects[EF_ORG] = e;
    01745                                 }
    01746                         }
    01747                 }
    01748                 else if(cmd == L"pbo")
    01749                 {
    01750                         m_polygonBaselineOffset = wcstol(p, NULL, 10);
    01751                 }
    01752                 else if(cmd == L"pos")
    01753                 {
    01754                         if(params.GetCount() == 2 && !sub->m_effects[EF_MOVE])
    01755                         {
    01756                                 if(Effect* e = new Effect)
    01757                                 {
    01758                                         e->param[0] = e->param[2] = (int)(sub->m_scalex*wcstol(params[0], NULL, 10)*8);
    01759                                         e->param[1] = e->param[3] = (int)(sub->m_scaley*wcstol(params[1], NULL, 10)*8);
    01760                                         e->t[0] = e->t[1] = 0;
    01761
    01762                                         sub->m_effects[EF_MOVE] = e;
    01763                                 }
    01764                         }
    01765                 }
    01766                 else if(cmd == L"p")
    01767                 {
    01768                         int n = wcstol(p, NULL, 10);
    01769                         m_nPolygon = (n <= 0 ? 0 : n);
    01770                 }
    01771                 else if(cmd == L"q")
    01772                 {
    01773                         int n = wcstol(p, NULL, 10);
    01774                         sub->m_wrapStyle = !p.IsEmpty() && (0 <= n && n <= 3)
    01775                                 ? n
    01776                                 : m_defaultWrapStyle;
    01777                 }
    01778                 else if(cmd == L"r")
    01779                 {
    01780                         void* val;
    01781                         style = (!p.IsEmpty() && m_styles.Lookup(CString(p), val) && val) ? *((STSStyle*)val) : org;
    01782                 }
    01783                 else if(cmd == L"shad")
    01784                 {
    01785                         double n = CalcAnimation(wcstod(p, NULL), style.shadowDepth, fAnimate);
    01786                         style.shadowDepth = !p.IsEmpty()
    01787                                 ? (n < 0 ? 0 : n)
    01788                                 : org.shadowDepth;
    01789                 }
    01790                 else if(cmd == L"s")
    01791                 {
    01792                         int n = wcstol(p, NULL, 10);
    01793                         style.fStrikeOut = !p.IsEmpty()
    01794                                 ? (n == 0 ? false : n == 1 ? true : org.fStrikeOut)
    01795                                 : org.fStrikeOut;
    01796                 }
    01797                 else if(cmd == L"t") // \t([<t1>,<t2>,][<accel>,]<style modifiers>)
    01798                 {
    01799                         p.Empty();
    01800
    01801                         m_animStart = m_animEnd = 0;
    01802                         m_animAccel = 1;
    01803
    01804                         if(params.GetCount() == 1)
    01805                         {
    01806                                 p = params[0];
    01807                         }
    01808                         else if(params.GetCount() == 2)
    01809                         {
    01810                                 m_animAccel = wcstod(params[0], NULL);
    01811                                 p = params[1];
    01812                         }
    01813                         else if(params.GetCount() == 3)
    01814                         {
    01815                                 m_animStart = (int)wcstod(params[0], NULL);
    01816                                 m_animEnd = (int)wcstod(params[1], NULL);
    01817                                 p = params[2];
    01818                         }
    01819                         else if(params.GetCount() == 4)
    01820                         {
    01821                                 m_animStart = wcstol(params[0], NULL, 10);
    01822                                 m_animEnd = wcstol(params[1], NULL, 10);
    01823                                 m_animAccel = wcstod(params[2], NULL);
    01824                                 p = params[3];
    01825                         }
    01826
    01827                         ParseSSATag(sub, p, style, org, true);
    01828
    01829                         sub->m_fAnimated = true;
    01830                 }
    01831                 else if(cmd == L"u")
    01832                 {
    01833                         int n = wcstol(p, NULL, 10);
    01834                         style.fUnderline = !p.IsEmpty()
    01835                                 ? (n == 0 ? false : n == 1 ? true : org.fUnderline)
    01836                                 : org.fUnderline;
    01837                 }
    01838         }
    01839
    01840 //      return(nUnrecognizedTags < nTags);
    01841         return(true); // there are ppl keeping coments inside {}, lets make them happy now
    01842 }
    01843
    01844 bool CRenderedTextSubtitle::ParseHtmlTag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org)
    01845 {
    01846         if(str.Find(L"!--") == 0)
    01847                 return(true);
    01848
    01849         bool fClosing = str[0] == '/';
    01850         str.Trim(L" /");
    01851
    01852         int i = str.Find(' ');
    01853         if(i < 0) i = str.GetLength();
    01854
    01855         CStringW tag = str.Left(i).MakeLower();
    01856         str = str.Mid(i).Trim();
    01857
    01858         CArray<CStringW> attribs, params;
    01859         while((i = str.Find('=')) > 0)
    01860         {
    01861                 attribs.Add(str.Left(i).Trim().MakeLower());
    01862                 str = str.Mid(i+1);
    01863                 for(i = 0; _istspace(str[i]); i++);
    01864                 str = str.Mid(i);
    01865                 if(str[0] == '\"') {str = str.Mid(1); i = str.Find('\"');}
    01866                 else i = str.Find(' ');
    01867                 if(i < 0) i = str.GetLength();
    01868                 params.Add(str.Left(i).Trim().MakeLower());
    01869                 str = str.Mid(i+1);
    01870         }
    01871
    01872         if(tag == L"text")
    01873                 ;
    01874         else if(tag == L"b" || tag == L"strong")
    01875                 style.fontWeight = !fClosing ? FW_BOLD : org.fontWeight;
    01876         else if(tag == L"i" || tag == L"em")
    01877                 style.fItalic = !fClosing ? true : org.fItalic;
    01878         else if(tag == L"u")
    01879                 style.fUnderline = !fClosing ? true : org.fUnderline;
    01880         else if(tag == L"s" || tag == L"strike" || tag == L"del")
    01881                 style.fStrikeOut = !fClosing ? true : org.fStrikeOut;
    01882         else if(tag == L"font")
    01883         {
    01884                 if(!fClosing)
    01885                 {
    01886                         for(i = 0; i < attribs.GetCount(); i++)
    01887                         {
    01888                                 if(params[i].IsEmpty()) continue;
    01889
    01890                                 int nColor = -1;
    01891
    01892                                 if(attribs[i] == L"face")
    01893                                 {
    01894                                         style.fontName = params[i];
    01895                                 }
    01896                                 else if(attribs[i] == L"size")
    01897                                 {
    01898                                         if(params[i][0] == '+')
    01899                                                 style.fontSize += wcstol(params[i], NULL, 10);
    01900                                         else if(params[i][0] == '-')
    01901                                                 style.fontSize -= wcstol(params[i], NULL, 10);
    01902                                         else
    01903                                                 style.fontSize = wcstol(params[i], NULL, 10);
    01904                                 }
    01905                                 else if(attribs[i] == L"color")
    01906                                 {
    01907                                         nColor = 0;
    01908                                 }
    01909                                 else if(attribs[i] == L"outline-color")
    01910                                 {
    01911                                         nColor = 2;
    01912                                 }
    01913                                 else if(attribs[i] == L"outline-level")
    01914                                 {
    01915                                         style.outlineWidth = wcstol(params[i], NULL, 10);
    01916                                 }
    01917                                 else if(attribs[i] == L"shadow-color")
    01918                                 {
    01919                                         nColor = 3;
    01920                                 }
    01921                                 else if(attribs[i] == L"shadow-level")
    01922                                 {
    01923                                         style.shadowDepth = wcstol(params[i], NULL, 10);
    01924                                 }
    01925
    01926                                 if(nColor >= 0 && nColor < 4)
    01927                                 {
    01928                                         CString key = WToT(params[i]).TrimLeft('#');
    01929                                         void* val;
    01930                                         if(g_colors.Lookup(key, val))
    01931                                                 style.colors[nColor] = (DWORD)val;
    01932                                         else if ((style.colors[nColor] = _tcstol(key, NULL, 16)) == 0)
    01933                                                 style.colors[nColor] = 0x00ffffff;  // default is white
    01934                                         style.colors[nColor] = ((style.colors[nColor]>>16)&0xff)|((style.colors[nColor]&0xff)<<16)|(style.colors[nColor]&0x00ff00);
    01935                                 }
    01936                         }
    01937                 }
    01938                 else
    01939                 {
    01940                         style.fontName = org.fontName;
    01941                         style.fontSize = org.fontSize;
    01942                         memcpy(style.colors, org.colors, sizeof(style.colors));
    01943                 }
    01944         }
    01945         else if(tag == L"k" && attribs.GetCount() == 1 && attribs[0] == L"t")
    01946         {
    01947                 m_ktype = 1;
    01948                 m_kstart = m_kend;
    01949                 m_kend += wcstol(params[0], NULL, 10);
    01950         }
    01951         else
    01952                 return(false);
    01953
    01954         return(true);
    01955 }
    01956
    01957 double CRenderedTextSubtitle::CalcAnimation(double dst, double src, bool fAnimate)
    01958 {
    01959         int s = m_animStart ? m_animStart : 0;
    01960         int e = m_animEnd ? m_animEnd : m_delay;
    01961
    01962         if(fabs(dst-src) >= 0.0001 && fAnimate)
    01963         {
    01964                 if(m_time < s) dst = src;
    01965                 else if(s <= m_time && m_time < e)
    01966                 {
    01967                         double t = pow(1.0 * (m_time - s) / (e - s), m_animAccel);
    01968                         dst = (1 - t) * src + t * dst;
    01969                 }
    01970 //              else dst = dst;
    01971         }
    01972
    01973         return(dst);
    01974 }
    01975
    01976 CSubtitle* CRenderedTextSubtitle::GetSubtitle(int entry)
    01977 {
    01978         CSubtitle* sub;
    01979         if(m_subtitleCache.Lookup(entry, sub))
    01980         {
    01981                 if(sub->m_fAnimated) {delete sub; sub = NULL;}
    01982                 else return(sub);
    01983         }
    01984
    01985         sub = new CSubtitle();
    01986         if(!sub) return(NULL);
    01987
    01988         CStringW str = GetStrW(entry, true);
    01989
    01990         STSStyle stss, orgstss;
    01991         GetStyle(entry, stss);
    01992         orgstss = stss;
    01993
    01994         sub->m_clip.SetRect(0, 0, m_size.cx>>3, m_size.cy>>3);
    01995         sub->m_scrAlignment = -stss.scrAlignment;
    01996         sub->m_wrapStyle = m_defaultWrapStyle;
    01997         sub->m_fAnimated = false;
    01998         sub->m_relativeTo = stss.relativeTo;
    01999
    02000         sub->m_scalex = m_dstScreenSize.cx > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Width() : m_size.cx) / (m_dstScreenSize.cx*8) : 1.0;
    02001         sub->m_scaley = m_dstScreenSize.cy > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Height() : m_size.cy) / (m_dstScreenSize.cy*8) : 1.0;
    02002
    02003         m_animStart = m_animEnd = 0;
    02004         m_animAccel = 1;
    02005         m_ktype = m_kstart = m_kend = 0;
    02006         m_nPolygon = 0;
    02007         m_polygonBaselineOffset = 0;
    02008
    02009         ParseEffect(sub, GetAt(entry).effect);
    02010
    02011         while(!str.IsEmpty())
    02012         {
    02013                 bool fParsed = false;
    02014
    02015                 int i;
    02016
    02017                 if(str[0] == '{' && (i = str.Find(L'}')) > 0)
    02018                 {
    02019                         if(fParsed = ParseSSATag(sub, str.Mid(1, i-1), stss, orgstss))
    02020                                 str = str.Mid(i+1);
    02021                 }
    02022                 else if(str[0] == '<' && (i = str.Find(L'>')) > 0)
    02023                 {
    02024                         if(fParsed = ParseHtmlTag(sub, str.Mid(1, i-1), stss, orgstss))
    02025                                 str = str.Mid(i+1);
    02026                 }
    02027
    02028                 if(fParsed)
    02029                 {
    02030                         i = str.FindOneOf(L"{<");
    02031                         if(i < 0) i = str.GetLength();
    02032                         if(i == 0) continue;
    02033                 }
    02034                 else
    02035                 {
    02036                         i = str.Mid(1).FindOneOf(L"{<");
    02037                         if(i < 0) i = str.GetLength()-1;
    02038                         i++;
    02039                 }
    02040
    02041                 STSStyle tmp = stss;
    02042
    02043                 tmp.fontSize = sub->m_scaley*tmp.fontSize*64;
    02044                 tmp.fontSpacing = sub->m_scalex*tmp.fontSpacing*64;
    02045                 tmp.outlineWidth *= (m_fScaledBAS ? ((sub->m_scalex+sub->m_scaley)/2) : 1) * 8;
    02046                 tmp.shadowDepth *= (m_fScaledBAS ? ((sub->m_scalex+sub->m_scaley)/2) : 1) * 8;
    02047
    02048                 if(m_nPolygon)
    02049                 {
    02050                         ParsePolygon(sub, str.Left(i), tmp);
    02051                 }
    02052                 else
    02053                 {
    02054                         ParseString(sub, str.Left(i), tmp);
    02055                 }
    02056
    02057                 str = str.Mid(i);
    02058         }
    02059
    02060         // just a "work-around" solution... in most cases nobody will want to use \org together with moving but without rotating the subs
    02061         if(sub->m_effects[EF_ORG] && (sub->m_effects[EF_MOVE] || sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL]))
    02062                 sub->m_fAnimated = true;
    02063
    02064         sub->m_scrAlignment = abs(sub->m_scrAlignment);
    02065
    02066         STSEntry stse = GetAt(entry);
    02067         CRect marginRect = stse.marginRect;
    02068         if(marginRect.left == 0) marginRect.left = orgstss.marginRect.left;
    02069         if(marginRect.top == 0) marginRect.top = orgstss.marginRect.top;
    02070         if(marginRect.right == 0) marginRect.right = orgstss.marginRect.right;
    02071         if(marginRect.bottom == 0) marginRect.bottom = orgstss.marginRect.bottom;
    02072         marginRect.left = (int)(sub->m_scalex*marginRect.left*8);
    02073         marginRect.top = (int)(sub->m_scaley*marginRect.top*8);
    02074         marginRect.right = (int)(sub->m_scalex*marginRect.right*8);
    02075         marginRect.bottom = (int)(sub->m_scaley*marginRect.bottom*8);
    02076
    02077         if(stss.relativeTo == 1)
    02078         {
    02079                 marginRect.left += m_vidrect.left;
    02080                 marginRect.top += m_vidrect.top;
    02081                 marginRect.right += m_size.cx - m_vidrect.right;
    02082                 marginRect.bottom += m_size.cy - m_vidrect.bottom;
    02083         }
    02084
    02085         sub->CreateClippers(m_size);
    02086
    02087         sub->MakeLines(m_size, marginRect);
    02088
    02089         m_subtitleCache[entry] = sub;
    02090
    02091         return(sub);
    02092 }
    02093
    02094 //
    02095
    02096 STDMETHODIMP CRenderedTextSubtitle::NonDelegatingQueryInterface(REFIID riid, void** ppv)
    02097 {
    02098     CheckPointer(ppv, E_POINTER);
    02099     *ppv = NULL;
    02100
    02101     return
    02102                 QI(IPersist)
    02103                 QI(ISubStream)
    02104                 QI(ISubPicProvider)
    02105                 __super::NonDelegatingQueryInterface(riid, ppv);
    02106 }
    02107
    02108 // ISubPicProvider
    02109
    02110 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetStartPosition(REFERENCE_TIME rt, double fps)
    02111 {
    02112         int iSegment = -1;
    02113         SearchSubs((int)(rt/10000), fps, &iSegment, NULL);
    02114
    02115         if(iSegment < 0) iSegment = 0;
    02116
    02117         return(GetNext((POSITION)iSegment));
    02118 }
    02119
    02120 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetNext(POSITION pos)
    02121 {
    02122         int iSegment = (int)pos;
    02123
    02124         const STSSegment* stss;
    02125         while((stss = GetSegment(iSegment)) && stss->subs.GetSize() == 0)
    02126                 iSegment++;
    02127
    02128         return(stss ? (POSITION)(iSegment+1) : NULL);
    02129 }
    02130
    02131 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStart(POSITION pos, double fps)
    02132 {
    02133         return(10000i64 * TranslateSegmentStart((int)pos-1, fps));
    02134 }
    02135
    02136 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStop(POSITION pos, double fps)
    02137 {
    02138         return(10000i64 * TranslateSegmentEnd((int)pos-1, fps));
    02139 }
    02140
    02141 STDMETHODIMP_(bool) CRenderedTextSubtitle::IsAnimated(POSITION pos)
    02142 {
    02143         // TODO
    02144         return(true);
    02145 }
    02146
    02147 struct LSub {int idx, layer, readorder;};
    02148
    02149 static int lscomp(const void* ls1, const void* ls2)
    02150 {
    02151         int ret = ((LSub*)ls1)->layer - ((LSub*)ls2)->layer;
    02152         if(!ret) ret = ((LSub*)ls1)->readorder - ((LSub*)ls2)->readorder;
    02153         return(ret);
    02154 }
    02155
    02156 STDMETHODIMP CRenderedTextSubtitle::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
    02157 {
    02158         CRect bbox2(0,0,0,0);
    02159
    02160         if(m_size != CSize(spd.w*8, spd.h*8) || m_vidrect != CRect(spd.vidrect.left*8, spd.vidrect.top*8, spd.vidrect.right*8, spd.vidrect.bottom*8))
    02161                 Init(CSize(spd.w, spd.h), spd.vidrect);
    02162
    02163         int t = (int)(rt / 10000);
    02164
    02165         int segment;
    02166         const STSSegment* stss = SearchSubs(t, fps, &segment);
    02167         if(!stss) return S_FALSE;
    02168
    02169         // clear any cached subs not in the range of +/-30secs measured from the segment's bounds
    02170         {
    02171                 POSITION pos = m_subtitleCache.GetStartPosition();
    02172                 while(pos)
    02173                 {
    02174                         int key;
    02175                         CSubtitle* value;
    02176                         m_subtitleCache.GetNextAssoc(pos, key, value);
    02177
    02178                         STSEntry& stse = GetAt(key);
    02179                         if(stse.end <= (t-30000) || stse.start > (t+30000))
    02180                         {
    02181                                 delete value;
    02182                                 m_subtitleCache.RemoveKey(key);
    02183                                 pos = m_subtitleCache.GetStartPosition();
    02184                         }
    02185                 }
    02186         }
    02187
    02188         m_sla.AdvanceToSegment(segment, stss->subs);
    02189
    02190         CArray<LSub> subs;
    02191
    02192         for(int i = 0, j = stss->subs.GetSize(); i < j; i++)
    02193         {
    02194                 LSub ls;
    02195                 ls.idx = stss->subs[i];
    02196                 ls.layer = GetAt(stss->subs[i]).layer;
    02197                 ls.readorder = GetAt(stss->subs[i]).readorder;
    02198                 subs.Add(ls);
    02199         }
    02200
    02201         qsort(subs.GetData(), subs.GetSize(), sizeof(LSub), lscomp);
    02202
    02203         for(int i = 0, j = subs.GetSize(); i < j; i++)
    02204         {
    02205                 int entry = subs[i].idx;
    02206
    02207                 STSEntry stse = GetAt(entry);
    02208
    02209                 {
    02210                         int start = TranslateStart(entry, fps);
    02211                         m_time = t - start;
    02212                         m_delay = TranslateEnd(entry, fps) - start;
    02213                 }
    02214
    02215                 CSubtitle* s = GetSubtitle(entry);
    02216                 if(!s) continue;
    02217
    02218                 CRect clipRect = s->m_clip;
    02219                 CRect r = s->m_rect;
    02220                 CSize spaceNeeded = r.Size();
    02221
    02222                 // apply the effects
    02223
    02224                 bool fPosOverride = false, fOrgOverride = false;
    02225
    02226                 int alpha = 0x00;
    02227
    02228                 CPoint org2;
    02229
    02230                 for(int k = 0; k < EF_NUMBEROFEFFECTS; k++)
    02231                 {
    02232                         if(!s->m_effects[k]) continue;
    02233
    02234                         switch(k)
    02235                         {
    02236                         case EF_MOVE: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3], t1=t[0], t2=t[1])}
    02237                                 {
    02238                                         CPoint p;
    02239                                         CPoint p1(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
    02240                                         CPoint p2(s->m_effects[k]->param[2], s->m_effects[k]->param[3]);
    02241                                         int t1 = s->m_effects[k]->t[0];
    02242                                         int t2 = s->m_effects[k]->t[1];
    02243
    02244                                         if(t2 < t1) {int t = t1; t1 = t2; t2 = t;}
    02245
    02246                                         if(t1 <= 0 && t2 <= 0) {t1 = 0; t2 = m_delay;}
    02247
    02248                                         if(m_time <= t1) p = p1;
    02249                                         else if(t1 < m_time && m_time < t2)
    02250                                         {
    02251                                                 double t = 1.0*(m_time-t1)/(t2-t1);
    02252                                                 p.x = (int)((1-t)*p1.x + t*p2.x);
    02253                                                 p.y = (int)((1-t)*p1.y + t*p2.y);
    02254                                         }
    02255                                         else p = p2;
    02256
    02257                                         r = CRect(
    02258                                                         CPoint((s->m_scrAlignment%3) == 1 ? p.x : (s->m_scrAlignment%3) == 0 ? p.x - spaceNeeded.cx : p.x - (spaceNeeded.cx+1)/2,
    02259                                                                         s->m_scrAlignment <= 3 ? p.y - spaceNeeded.cy : s->m_scrAlignment <= 6 ? p.y - (spaceNeeded.cy+1)/2 : p.y),
    02260                                                         spaceNeeded);
    02261
    02262                                         if(s->m_relativeTo == 1)
    02263                                                 r.OffsetRect(m_vidrect.TopLeft());
    02264
    02265                                         fPosOverride = true;
    02266                                 }
    02267                                 break;
    02268                         case EF_ORG: // {\org(x=param[0], y=param[1])}
    02269                                 {
    02270                                         org2 = CPoint(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
    02271
    02272                                         fOrgOverride = true;
    02273                                 }
    02274                                 break;
    02275                         case EF_FADE: // {\fade(a1=param[0], a2=param[1], a3=param[2], t1=t[0], t2=t[1], t3=t[2], t4=t[3]) or {\fad(t1=t[1], t2=t[2])
    02276                                 {
    02277                                         int t1 = s->m_effects[k]->t[0];
    02278                                         int t2 = s->m_effects[k]->t[1];
    02279                                         int t3 = s->m_effects[k]->t[2];
    02280                                         int t4 = s->m_effects[k]->t[3];
    02281
    02282                                         if(t1 == -1 && t4 == -1) {t1 = 0; t3 = m_delay-t3; t4 = m_delay;}
    02283
    02284                                         if(m_time < t1) alpha = s->m_effects[k]->param[0];
    02285                                         else if(m_time >= t1 && m_time < t2)
    02286                                         {
    02287                                                 double t = 1.0 * (m_time - t1) / (t2 - t1);
    02288                                                 alpha = (int)(s->m_effects[k]->param[0]*(1-t) + s->m_effects[k]->param[1]*t);
    02289                                         }
    02290                                         else if(m_time >= t2 && m_time < t3) alpha = s->m_effects[k]->param[1];
    02291                                         else if(m_time >= t3 && m_time < t4)
    02292                                         {
    02293                                                 double t = 1.0 * (m_time - t3) / (t4 - t3);
    02294                                                 alpha = (int)(s->m_effects[k]->param[1]*(1-t) + s->m_effects[k]->param[2]*t);
    02295                                         }
    02296                                         else if(m_time >= t4) alpha = s->m_effects[k]->param[2];
    02297                                 }
    02298                                 break;
    02299                         case EF_BANNER: // Banner;delay=param[0][;leftoright=param[1];fadeawaywidth=param[2]]
    02300                                 {
    02301                                         int left = s->m_relativeTo == 1 ? m_vidrect.left : 0,
    02302                                                 right = s->m_relativeTo == 1 ? m_vidrect.right : m_size.cx;
    02303
    02304                                         r.left = !!s->m_effects[k]->param[1]
    02305                                                 ? (left/*marginRect.left*/ - spaceNeeded.cx) + (int)(m_time*8.0/s->m_effects[k]->param[0])
    02306                                                 : (right /*- marginRect.right*/) - (int)(m_time*8.0/s->m_effects[k]->param[0]);
    02307
    02308                                         r.right = r.left + spaceNeeded.cx;
    02309
    02310                                         clipRect &= CRect(left>>3, clipRect.top, right>>3, clipRect.bottom);
    02311
    02312                                         fPosOverride = true;
    02313                                 }
    02314                                 break;
    02315                         case EF_SCROLL: // Scroll up/down(toptobottom=param[3]);top=param[0];bottom=param[1];delay=param[2][;fadeawayheight=param[4]]
    02316                                 {
    02317                                         r.top = !!s->m_effects[k]->param[3]
    02318                                                 ? s->m_effects[k]->param[0] + (int)(m_time*8.0/s->m_effects[k]->param[2]) - spaceNeeded.cy
    02319                                                 : s->m_effects[k]->param[1] - (int)(m_time*8.0/s->m_effects[k]->param[2]);
    02320
    02321                                         r.bottom = r.top + spaceNeeded.cy;
    02322
    02323                                         CRect cr(0, (s->m_effects[k]->param[0] + 4) >> 3, spd.w, (s->m_effects[k]->param[1] + 4) >> 3);
    02324
    02325                                         if(s->m_relativeTo == 1)
    02326                                                 r.top += m_vidrect.top,
    02327                                                 r.bottom += m_vidrect.top,
    02328                                                 cr.top += m_vidrect.top>>3,
    02329                                                 cr.bottom += m_vidrect.top>>3;
    02330
    02331                                         clipRect &= cr;
    02332
    02333                                         fPosOverride = true;
    02334                                 }
    02335                                 break;
    02336                         default:
    02337                                 break;
    02338                         }
    02339                 }
    02340
    02341                 if(!fPosOverride && !fOrgOverride && !s->m_fAnimated)
    02342                         r = m_sla.AllocRect(s, segment, entry, stse.layer, m_collisions);
    02343
    02344                 CPoint org;
    02345                 org.x = (s->m_scrAlignment%3) == 1 ? r.left : (s->m_scrAlignment%3) == 2 ? r.CenterPoint().x : r.right;
    02346                 org.y = s->m_scrAlignment <= 3 ? r.bottom : s->m_scrAlignment <= 6 ? r.CenterPoint().y : r.top;
    02347
    02348                 if(!fOrgOverride) org2 = org;
    02349
    02350                 BYTE* pAlphaMask = s->m_pClipper?s->m_pClipper->m_pAlphaMask:NULL;
    02351
    02352                 CPoint p, p2(0, r.top);
    02353
    02354                 POSITION pos;
    02355
    02356                 p = p2;
    02357
    02358                 pos = s->GetHeadPosition();
    02359                 while(pos)
    02360                 {
    02361                         CLine* l = s->GetNext(pos);
    02362
    02363                         p.x = (s->m_scrAlignment%3) == 1 ? org.x
    02364                                 : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
    02365                                 :                                                          org.x - (l->m_width/2);
    02366
    02367                         bbox2 |= l->PaintShadow(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
    02368
    02369                         p.y += l->m_ascent + l->m_descent;
    02370                 }
    02371
    02372                 p = p2;
    02373
    02374                 pos = s->GetHeadPosition();
    02375                 while(pos)
    02376                 {
    02377                         CLine* l = s->GetNext(pos);
    02378
    02379                         p.x = (s->m_scrAlignment%3) == 1 ? org.x
    02380                                 : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
    02381                                 :                                                          org.x - (l->m_width/2);
    02382
    02383                         bbox2 |= l->PaintOutline(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
    02384
    02385                         p.y += l->m_ascent + l->m_descent;
    02386                 }
    02387
    02388                 p = p2;
    02389
    02390                 pos = s->GetHeadPosition();
    02391                 while(pos)
    02392                 {
    02393                         CLine* l = s->GetNext(pos);
    02394
    02395                         p.x = (s->m_scrAlignment%3) == 1 ? org.x
    02396                                 : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
    02397                                 :                                                          org.x - (l->m_width/2);
    02398
    02399                         bbox2 |= l->PaintBody(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
    02400
    02401                         p.y += l->m_ascent + l->m_descent;
    02402                 }
    02403         }
    02404
    02405         bbox = bbox2;
    02406
    02407         return (subs.GetSize() && !bbox2.IsRectEmpty()) ? S_OK : S_FALSE;
    02408 }
    02409
    02410 // IPersist
    02411
    02412 STDMETHODIMP CRenderedTextSubtitle::GetClassID(CLSID* pClassID)
    02413 {
    02414         return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
    02415 }
    02416
    02417 // ISubStream
    02418
    02419 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStreamCount()
    02420 {
    02421         return(1);
    02422 }
    02423
    02424 STDMETHODIMP CRenderedTextSubtitle::GetStreamInfo(int iStream, WCHAR** ppName, LCID* pLCID)
    02425 {
    02426         if(iStream != 0) return E_INVALIDARG;
    02427
    02428         if(ppName)
    02429         {
    02430                 if(!(*ppName = (WCHAR*)CoTaskMemAlloc((m_name.GetLength()+1)*sizeof(WCHAR))))
    02431                         return E_OUTOFMEMORY;
    02432
    02433                 wcscpy(*ppName, CStringW(m_name));
    02434         }
    02435
    02436         if(pLCID)
    02437         {
    02438                 *pLCID = 0; // TODO
    02439         }
    02440
    02441         return S_OK;
    02442 }
    02443
    02444 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStream()
    02445 {
    02446         return(0);
    02447 }
    02448
    02449 STDMETHODIMP CRenderedTextSubtitle::SetStream(int iStream)
    02450 {
    02451         return iStream == 0 ? S_OK : E_FAIL;
    02452 }
    02453
    02454 STDMETHODIMP CRenderedTextSubtitle::Reload()
    02455 {
    02456         CFileStatus s;
    02457         if(!CFile::GetStatus(m_path, s)) return E_FAIL;
    02458         return !m_path.IsEmpty() && Open(m_path, DEFAULT_CHARSET) ? S_OK : E_FAIL;
    02459 }