00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "stdafx.h"
00023 #include <math.h>
00024 #include <time.h>
00025 #include "RTS.h"
00026
00027
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
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
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
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
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
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
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
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];
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
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
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
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
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);
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);
00689
00690
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);
00729
00730 int x = p.x;
00731 int y = p.y + m_ascent - w->m_ascent;
00732
00733
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
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
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
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
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
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)
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
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
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
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])
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])
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;
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")
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")
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")
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
01841 return(true);
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;
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
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
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
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
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
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
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:
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:
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:
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:
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 - spaceNeeded.cx) + (int)(m_time*8.0/s->m_effects[k]->param[0])
02306 : (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:
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
02411
02412 STDMETHODIMP CRenderedTextSubtitle::GetClassID(CLSID* pClassID)
02413 {
02414 return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
02415 }
02416
02417
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;
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 }