RTS.cpp
上传用户:xjjlds
上传日期:2015-12-05
资源大小:22823k
文件大小:62k
源码类别:

多媒体编程

开发平台:

Visual C++

  1. /* 
  2.  * Copyright (C) 2003-2005 Gabest
  3.  * http://www.gabest.org
  4.  *
  5.  *  This Program is free software; you can redistribute it and/or modify
  6.  *  it under the terms of the GNU General Public License as published by
  7.  *  the Free Software Foundation; either version 2, or (at your option)
  8.  *  any later version.
  9.  *   
  10.  *  This Program is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13.  *  GNU General Public License for more details.
  14.  *   
  15.  *  You should have received a copy of the GNU General Public License
  16.  *  along with GNU Make; see the file COPYING.  If not, write to
  17.  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
  18.  *  http://www.gnu.org/copyleft/gpl.html
  19.  *
  20.  */
  21. #include "stdafx.h"
  22. #include <math.h>
  23. #include <time.h>
  24. #include "RTS.h"
  25. // WARNING: this isn't very thread safe, use only one RTS a time.
  26. static HDC g_hDC;
  27. static int g_hDC_refcnt = 0;
  28. static long revcolor(long c)
  29. {
  30. return ((c&0xff0000)>>16) + (c&0xff00) + ((c&0xff)<<16);
  31. }
  32. //////////////////////////////////////////////////////////////////////////////////////////////
  33. // CMyFont
  34. CMyFont::CMyFont(STSStyle& style)
  35. {
  36. LOGFONT lf;
  37. memset(&lf, 0, sizeof(lf));
  38. lf <<= style;
  39. lf.lfHeight = (LONG)(style.fontSize+0.5);
  40. lf.lfOutPrecision = OUT_TT_PRECIS;
  41. lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  42. lf.lfQuality = ANTIALIASED_QUALITY;
  43. lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
  44. lf.lfEscapement = lf.lfOrientation = 0;
  45. if(!CreateFontIndirect(&lf))
  46. {
  47. _tcscpy(lf.lfFaceName, _T("Arial"));
  48. CreateFontIndirect(&lf);
  49. }
  50. HFONT hOldFont = (HFONT)SelectObject(g_hDC, *this);
  51. TEXTMETRIC tm;
  52. BOOL b = GetTextMetrics(g_hDC, &tm);
  53. DWORD ret = GetLastError();
  54. m_ascent = ((tm.tmAscent + 4) >> 3);
  55. m_descent = ((tm.tmDescent + 4) >> 3);
  56. SelectObject(g_hDC, hOldFont);
  57. }
  58. // CWord
  59. CWord::CWord(STSStyle& style, CStringW str, int ktype, int kstart, int kend) 
  60. : m_style(style), m_str(str)
  61. , m_width(0), m_ascent(0), m_descent(0)
  62. , m_ktype(ktype), m_kstart(kstart), m_kend(kend)
  63. , m_fDrawn(false), m_p(INT_MAX, INT_MAX)
  64. , m_fLineBreak(false), m_fWhiteSpaceChar(false)
  65. , m_pOpaqueBox(NULL)
  66. {
  67. if(str.IsEmpty()) 
  68. {
  69. m_fWhiteSpaceChar = m_fLineBreak = true; 
  70. }
  71. CMyFont font(m_style);
  72. m_ascent = (int)(m_style.fontScaleY/100*font.m_ascent);
  73. m_descent = (int)(m_style.fontScaleY/100*font.m_descent);
  74. m_width = 0;
  75. }
  76. CWord::~CWord()
  77. {
  78. if(m_pOpaqueBox) delete m_pOpaqueBox;
  79. }
  80. bool CWord::Append(CWord* w)
  81. {
  82. if(!(m_style == w->m_style)
  83. || m_fLineBreak || w->m_fLineBreak
  84. || w->m_kstart != w->m_kend || m_ktype != w->m_ktype) return(false);
  85. m_fWhiteSpaceChar = m_fWhiteSpaceChar && w->m_fWhiteSpaceChar;
  86. m_str += w->m_str;
  87. m_width += w->m_width;
  88. m_fDrawn = false;
  89. m_p = CPoint(INT_MAX, INT_MAX);
  90. return(true);
  91. }
  92. void CWord::Paint(CPoint p, CPoint org)
  93. {
  94. if(!m_str) return;
  95. if(!m_fDrawn)
  96. {
  97. if(!CreatePath()) return;
  98. Transform(CPoint((org.x-p.x)*8, (org.y-p.y)*8));
  99. if(!ScanConvert()) return;
  100. if(m_style.borderStyle == 0 && m_style.outlineWidth > 0)
  101. {
  102. if(!CreateWidenedRegion((int)(m_style.outlineWidth+0.5))) return;
  103. }
  104. else if(m_style.borderStyle == 1)
  105. {
  106. if(!CreateOpaqueBox()) return;
  107. }
  108. m_fDrawn = true;
  109. if(!Rasterize(p.x&7, p.y&7, m_style.fBlur)) return;
  110. }
  111. else if((m_p.x&7) != (p.x&7) || (m_p.y&7) != (p.y&7))
  112. {
  113. Rasterize(p.x&7, p.y&7, m_style.fBlur);
  114. }
  115. m_p = p;
  116. if(m_pOpaqueBox)
  117. m_pOpaqueBox->Paint(p, org);
  118. }
  119. void CWord::Transform(CPoint org)
  120. {
  121. double scalex = m_style.fontScaleX/100;
  122. double scaley = m_style.fontScaleY/100;
  123. double caz = cos((3.1415/180)*m_style.fontAngleZ);
  124. double saz = sin((3.1415/180)*m_style.fontAngleZ);
  125. double cax = cos((3.1415/180)*m_style.fontAngleX);
  126. double sax = sin((3.1415/180)*m_style.fontAngleX);
  127. double cay = cos((3.1415/180)*m_style.fontAngleY);
  128. double say = sin((3.1415/180)*m_style.fontAngleY);
  129. for(int i = 0; i < mPathPoints; i++)
  130. {
  131. double x, y, z, xx, yy, zz;
  132. x = scalex * mpPathPoints[i].x - org.x;
  133. y = scaley * mpPathPoints[i].y - org.y;
  134. z = 0;
  135. xx = x*caz + y*saz;
  136. yy = -(x*saz - y*caz);
  137. zz = z;
  138. x = xx;
  139. y = yy*cax + zz*sax;
  140. z = yy*sax - zz*cax;
  141. xx = x*cay + z*say;
  142. yy = y;
  143. zz = x*say - z*cay;
  144. zz = max(zz, -19000);
  145. x = (xx * 20000) / (zz + 20000);
  146. y = (yy * 20000) / (zz + 20000);
  147. mpPathPoints[i].x = (LONG)(x + org.x + 0.5);
  148. mpPathPoints[i].y = (LONG)(y + org.y + 0.5);
  149. }
  150. }
  151. bool CWord::CreateOpaqueBox()
  152. {
  153. if(m_pOpaqueBox) return(true);
  154. STSStyle style = m_style;
  155. style.borderStyle = 0;
  156. style.outlineWidth = 0;
  157. style.colors[0] = m_style.colors[2];
  158. style.alpha[0] = m_style.alpha[2];
  159. int w = (int)(m_style.outlineWidth + 0.5);
  160. CStringW str;
  161. str.Format(L"m %d %d l %d %d %d %d %d %d", 
  162. -w, -w, 
  163. m_width+w, -w, 
  164. m_width+w, m_ascent+m_descent+w, 
  165. -w, m_ascent+m_descent+w);
  166. m_pOpaqueBox = new CPolygon(style, str, 0, 0, 0, 1.0/8, 1.0/8, 0);
  167. return(!!m_pOpaqueBox);
  168. }
  169. // CText
  170. CText::CText(STSStyle& style, CStringW str, int ktype, int kstart, int kend)
  171. : CWord(style, str, ktype, kstart, kend)
  172. {
  173. if(m_str.GetLength() == 1 && m_str[0] == ' ')
  174. {
  175. m_fWhiteSpaceChar = true;
  176. }
  177. CMyFont font(m_style);
  178. HFONT hOldFont = (HFONT)SelectObject(g_hDC, font);
  179. if(m_style.fontSpacing || (long)GetVersion() < 0)
  180. {
  181. bool bFirstPath = true;
  182. for(LPCWSTR s = m_str; *s; s++)
  183. {
  184. CSize extent;
  185. if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectObject(g_hDC, hOldFont); ASSERT(0); return;}
  186. m_width += extent.cx + (int)m_style.fontSpacing;
  187. }
  188. // m_width -= (int)m_style.fontSpacing; // TODO: subtract only at the end of the line
  189. }
  190. else
  191. {
  192. CSize extent;
  193. if(!GetTextExtentPoint32W(g_hDC, m_str, wcslen(str), &extent)) {SelectObject(g_hDC, hOldFont); ASSERT(0); return;}
  194. m_width += extent.cx;
  195. }
  196. m_width = (int)(m_style.fontScaleX/100*m_width + 4) >> 3;
  197. SelectObject(g_hDC, hOldFont);
  198. }
  199. CWord* CText::Copy()
  200. {
  201. return(new CText(m_style, m_str, m_ktype, m_kstart, m_kend));
  202. }
  203. bool CText::Append(CWord* w)
  204. {
  205. return(dynamic_cast<CText*>(w) && CWord::Append(w));
  206. }
  207. bool CText::CreatePath()
  208. {
  209. CMyFont font(m_style);
  210. HFONT hOldFont = (HFONT)SelectObject(g_hDC, font);
  211. int width = 0;
  212. if(m_style.fontSpacing || (long)GetVersion() < 0)
  213. {
  214. bool bFirstPath = true;
  215. for(LPCWSTR s = m_str; *s; s++)
  216. {
  217. CSize extent;
  218. if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectObject(g_hDC, hOldFont); ASSERT(0); return(false);}
  219. PartialBeginPath(g_hDC, bFirstPath); bFirstPath = false;
  220. TextOutW(g_hDC, 0, 0, s, 1);
  221. PartialEndPath(g_hDC, width, 0);
  222. width += extent.cx + (int)m_style.fontSpacing;
  223. }
  224. }
  225. else
  226. {
  227. CSize extent;
  228. if(!GetTextExtentPoint32W(g_hDC, m_str, m_str.GetLength(), &extent)) {SelectObject(g_hDC, hOldFont); ASSERT(0); return(false);}
  229. BeginPath(g_hDC);
  230. TextOutW(g_hDC, 0, 0, m_str, m_str.GetLength());
  231. EndPath(g_hDC);
  232. }
  233. SelectObject(g_hDC, hOldFont);
  234. return(true);
  235. }
  236. // CPolygon
  237. CPolygon::CPolygon(STSStyle& style, CStringW str, int ktype, int kstart, int kend, double scalex, double scaley, int baseline) 
  238. : CWord(style, str, ktype, kstart, kend)
  239. , m_scalex(scalex), m_scaley(scaley), m_baseline(baseline)
  240. {
  241. ParseStr();
  242. }
  243. CPolygon::~CPolygon()
  244. {
  245. }
  246. CWord* CPolygon::Copy()
  247. {
  248. return(new CPolygon(m_style, m_str, m_ktype, m_kstart, m_kend, m_scalex, m_scaley, m_baseline));
  249. }
  250. bool CPolygon::Append(CWord* w)
  251. {
  252. int width = m_width;
  253. CPolygon* p = dynamic_cast<CPolygon*>(w);
  254. if(!p) return(false);
  255. // TODO
  256. return(false);
  257. return(true);
  258. }
  259. bool CPolygon::GetLONG(CStringW& str, LONG& ret)
  260. {
  261. LPWSTR s = (LPWSTR)(LPCWSTR)str, e = s;
  262. ret = wcstol(str, &e, 10);
  263. str = str.Mid(e - s);
  264. return(e > s);
  265. }
  266. bool CPolygon::GetPOINT(CStringW& str, POINT& ret)
  267. {
  268. return(GetLONG(str, ret.x) && GetLONG(str, ret.y));
  269. }
  270. bool CPolygon::ParseStr()
  271. {
  272. if(m_pathTypesOrg.GetSize() > 0) return(true);
  273. CPoint p;
  274. int i, j, lastsplinestart = -1, firstmoveto = -1, lastmoveto = -1;
  275. CStringW str = m_str;
  276. str.SpanIncluding(L"mnlbspc 0123456789");
  277. str.Replace(L"m", L"*m");
  278. str.Replace(L"n", L"*n");
  279. str.Replace(L"l", L"*l");
  280. str.Replace(L"b", L"*b");
  281. str.Replace(L"s", L"*s");
  282. str.Replace(L"p", L"*p");
  283. str.Replace(L"c", L"*c");
  284. int k = 0;
  285. for(CStringW s = str.Tokenize(L"*", k); !s.IsEmpty(); s = str.Tokenize(L"*", k))
  286. {
  287. WCHAR c = s[0];
  288. s.TrimLeft(L"mnlbspc ");
  289. switch(c)
  290. {
  291. case 'm': 
  292. lastmoveto = m_pathTypesOrg.GetSize();
  293. if(firstmoveto == -1) firstmoveto = lastmoveto;
  294. while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_MOVETO); m_pathPointsOrg.Add(p);}
  295. break;
  296. case 'n':
  297. while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_MOVETONC); m_pathPointsOrg.Add(p);}
  298. break;
  299. case 'l':
  300. while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_LINETO); m_pathPointsOrg.Add(p);}
  301. break;
  302. case 'b':
  303. j = m_pathTypesOrg.GetSize();
  304. while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BEZIERTO); m_pathPointsOrg.Add(p); j++;}
  305. j = m_pathTypesOrg.GetSize() - ((m_pathTypesOrg.GetSize()-j)%3);
  306. m_pathTypesOrg.SetSize(j); m_pathPointsOrg.SetSize(j);
  307. break;
  308. case 's':
  309. j = lastsplinestart = m_pathTypesOrg.GetSize();
  310. i = 3;
  311. while(i-- && GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BSPLINETO); m_pathPointsOrg.Add(p); j++;}
  312. if(m_pathTypesOrg.GetSize()-lastsplinestart < 3) {m_pathTypesOrg.SetSize(lastsplinestart); m_pathPointsOrg.SetSize(lastsplinestart); lastsplinestart = -1;}
  313. // no break here
  314. case 'p':
  315. while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BSPLINEPATCHTO); m_pathPointsOrg.Add(p); j++;}
  316. break;
  317. case 'c':
  318. if(lastsplinestart > 0)
  319. {
  320. m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
  321. m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
  322. m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
  323. 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)
  324. m_pathPointsOrg.Add(p);
  325. p = m_pathPointsOrg[lastsplinestart];
  326. m_pathPointsOrg.Add(p);
  327. p = m_pathPointsOrg[lastsplinestart+1];
  328. m_pathPointsOrg.Add(p);
  329. lastsplinestart = -1;
  330. }
  331. break;
  332. default:
  333. break;
  334. }
  335. }
  336. /*
  337. LPCWSTR str = m_str;
  338. while(*str)
  339. {
  340. while(*str && *str != 'm' && *str != 'n' && *str != 'l' && *str != 'b' && *str != 's' && *str != 'p' && *str != 'c') str++;
  341. if(!*str) break;
  342. switch(*str++)
  343. {
  344. case 'm': 
  345. lastmoveto = m_pathTypesOrg.GetSize();
  346. if(firstmoveto == -1) firstmoveto = lastmoveto;
  347. while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_MOVETO); m_pathPointsOrg.Add(p);}
  348. break;
  349. case 'n':
  350. while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_MOVETONC); m_pathPointsOrg.Add(p);}
  351. break;
  352. case 'l':
  353. while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_LINETO); m_pathPointsOrg.Add(p);}
  354. break;
  355. case 'b':
  356. j = m_pathTypesOrg.GetSize();
  357. while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BEZIERTO); m_pathPointsOrg.Add(p); j++;}
  358. j = m_pathTypesOrg.GetSize() - ((m_pathTypesOrg.GetSize()-j)%3);
  359. m_pathTypesOrg.SetSize(j); m_pathPointsOrg.SetSize(j);
  360. break;
  361. case 's':
  362. j = lastsplinestart = m_pathTypesOrg.GetSize();
  363. i = 3;
  364. while(i-- && GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BSPLINETO); m_pathPointsOrg.Add(p); j++;}
  365. if(m_pathTypesOrg.GetSize()-lastsplinestart < 3) {m_pathTypesOrg.SetSize(lastsplinestart); m_pathPointsOrg.SetSize(lastsplinestart); lastsplinestart = -1;}
  366. // no break here
  367. case 'p':
  368. while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BSPLINEPATCHTO); m_pathPointsOrg.Add(p); j++;}
  369. break;
  370. case 'c':
  371. if(lastsplinestart > 0)
  372. {
  373. m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
  374. m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
  375. m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
  376. 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)
  377. m_pathPointsOrg.Add(p);
  378. p = m_pathPointsOrg[lastsplinestart];
  379. m_pathPointsOrg.Add(p);
  380. p = m_pathPointsOrg[lastsplinestart+1];
  381. m_pathPointsOrg.Add(p);
  382. lastsplinestart = -1;
  383. }
  384. break;
  385. default:
  386. break;
  387. }
  388. if(firstmoveto > 0) break;
  389. }
  390. */
  391. if(lastmoveto == -1 || firstmoveto > 0) 
  392. {
  393. m_pathTypesOrg.RemoveAll();
  394. m_pathPointsOrg.RemoveAll();
  395. return(false);
  396. }
  397. int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
  398. for(i = 0; i < m_pathTypesOrg.GetSize(); i++)
  399. {
  400. m_pathPointsOrg[i].x = (int)(64 * m_scalex * m_pathPointsOrg[i].x);
  401. m_pathPointsOrg[i].y = (int)(64 * m_scaley * m_pathPointsOrg[i].y);
  402. if(minx > m_pathPointsOrg[i].x) minx = m_pathPointsOrg[i].x;
  403. if(miny > m_pathPointsOrg[i].y) miny = m_pathPointsOrg[i].y;
  404. if(maxx < m_pathPointsOrg[i].x) maxx = m_pathPointsOrg[i].x;
  405. if(maxy < m_pathPointsOrg[i].y) maxy = m_pathPointsOrg[i].y;
  406. }
  407. m_width = max(maxx - minx, 0);
  408. m_ascent = max(maxy - miny, 0);
  409. int baseline = (int)(64 * m_scaley * m_baseline);
  410. m_descent = baseline;
  411. m_ascent -= baseline;
  412. m_width = ((int)(m_style.fontScaleX/100 * m_width) + 4) >> 3;
  413. m_ascent = ((int)(m_style.fontScaleY/100 * m_ascent) + 4) >> 3;
  414. m_descent = ((int)(m_style.fontScaleY/100 * m_descent) + 4) >> 3;
  415. return(true);
  416. }
  417. bool CPolygon::CreatePath()
  418. {
  419. int len = m_pathTypesOrg.GetSize();
  420. if(len == 0) return(false);
  421. if(mPathPoints != len)
  422. {
  423. mpPathTypes = (BYTE*)realloc(mpPathTypes, len*sizeof(BYTE));
  424. mpPathPoints = (POINT*)realloc(mpPathPoints, len*sizeof(POINT));
  425. if(!mpPathTypes || !mpPathPoints) return(false);
  426. mPathPoints = len;
  427. }
  428. memcpy(mpPathTypes, m_pathTypesOrg.GetData(), len*sizeof(BYTE));
  429. memcpy(mpPathPoints, m_pathPointsOrg.GetData(), len*sizeof(POINT));
  430. return(true);
  431. }
  432. // CClipper
  433. CClipper::CClipper(CStringW str, CSize size, double scalex, double scaley) 
  434. : CPolygon(STSStyle(), str, 0, 0, 0, scalex, scaley, 0)
  435. {
  436. m_size.cx = m_size.cy = 0;
  437. m_pAlphaMask = NULL;
  438. if(size.cx < 0 || size.cy < 0 || !(m_pAlphaMask = new BYTE[size.cx*size.cy])) return;
  439. m_size = size;
  440. memset(m_pAlphaMask, 0, size.cx*size.cy);
  441. Paint(CPoint(0, 0), CPoint(0, 0));
  442. int w = mOverlayWidth, h = mOverlayHeight;
  443. int x = (mOffsetX+4)>>3, y = (mOffsetY+4)>>3;
  444. int xo = 0, yo = 0;
  445. if(x < 0) {xo = -x; w -= -x; x = 0;}
  446. if(y < 0) {yo = -y; h -= -y; y = 0;}
  447. if(x+w > m_size.cx) w = m_size.cx-x;
  448. if(y+h > m_size.cy) h = m_size.cy-y;
  449. if(w <= 0 || h <= 0) return;
  450. const BYTE* src = mpOverlayBuffer + 2*(mOverlayWidth * yo + xo);
  451. BYTE* dst = m_pAlphaMask + m_size.cx * y + x;
  452. while(h--)
  453. {
  454. for(int wt=0; wt<w; ++wt)
  455. dst[wt] = src[wt*2];
  456. src += 2*mOverlayWidth;
  457. dst += m_size.cx;
  458. }
  459. }
  460. CClipper::~CClipper()
  461. {
  462. if(m_pAlphaMask) delete [] m_pAlphaMask;
  463. m_pAlphaMask = NULL;
  464. }
  465. CWord* CClipper::Copy()
  466. {
  467. return(new CClipper(m_str, m_size, m_scalex, m_scaley));
  468. }
  469. bool CClipper::Append(CWord* w)
  470. {
  471. return(false);
  472. }
  473. // CLine
  474. CLine::~CLine()
  475. {
  476. POSITION pos = GetHeadPosition();
  477. while(pos) delete GetNext(pos);
  478. }
  479. void CLine::Compact()
  480. {
  481. POSITION pos = GetHeadPosition();
  482. while(pos)
  483. {
  484. CWord* w = GetNext(pos);
  485. if(!w->m_fWhiteSpaceChar) break;
  486. m_width -= w->m_width;
  487. delete w;
  488. RemoveHead();
  489. }
  490. pos = GetTailPosition();
  491. while(pos)
  492. {
  493. CWord* w = GetPrev(pos);
  494. if(!w->m_fWhiteSpaceChar) break;
  495. m_width -= w->m_width;
  496. delete w;
  497. RemoveTail();
  498. }
  499. if(IsEmpty()) return;
  500. CLine l;
  501. l.AddTail(this);
  502. RemoveAll();
  503. CWord* last = NULL;
  504. pos = l.GetHeadPosition();
  505. while(pos)
  506. {
  507. CWord* w = l.GetNext(pos);
  508. if(!last || !last->Append(w))
  509. AddTail(last = w->Copy());
  510. }
  511. m_ascent = m_descent = m_border = 0;
  512. pos = GetHeadPosition();
  513. while(pos)
  514. {
  515. CWord* w = GetNext(pos);
  516. if(m_ascent < w->m_ascent) m_ascent = w->m_ascent;
  517. if(m_descent < w->m_descent) m_descent = w->m_descent;
  518. if(m_border < w->m_style.outlineWidth) m_border = (int)(w->m_style.outlineWidth+0.5);
  519. }
  520. }
  521. CRect CLine::PaintShadow(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha)
  522. {
  523. CRect bbox(0, 0, 0, 0);
  524. POSITION pos = GetHeadPosition();
  525. while(pos)
  526. {
  527. CWord* w = GetNext(pos);
  528. if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
  529. if(w->m_style.shadowDepth > 0)
  530. {
  531. int x = p.x + (int)(w->m_style.shadowDepth+0.5);
  532. int y = p.y + m_ascent - w->m_ascent + (int)(w->m_style.shadowDepth+0.5);
  533. DWORD a = 0xff - w->m_style.alpha[3];
  534. if(alpha > 0) a = MulDiv(a, 0xff - alpha, 0xff);
  535. COLORREF shadow = revcolor(w->m_style.colors[3]) | (a<<24);
  536. long sw[6] = {shadow, -1};
  537. w->Paint(CPoint(x, y), org);
  538. if(w->m_style.borderStyle == 0)
  539. {
  540. bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, 
  541. w->m_ktype > 0 || w->m_style.alpha[0] < 0xff, 
  542. w->m_style.outlineWidth > 0 && !(w->m_ktype == 2 && time < w->m_kstart));
  543. }
  544. else if(w->m_style.borderStyle == 1 && w->m_pOpaqueBox)
  545. {
  546. bbox |= w->m_pOpaqueBox->Draw(spd, clipRect, pAlphaMask, x, y, sw, true, false);
  547. }
  548. }
  549. p.x += w->m_width;
  550. }
  551. return(bbox);
  552. }
  553. CRect CLine::PaintOutline(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha)
  554. {
  555. CRect bbox(0, 0, 0, 0);
  556. POSITION pos = GetHeadPosition();
  557. while(pos)
  558. {
  559. CWord* w = GetNext(pos);
  560. if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
  561. // if((w->m_style.outlineWidth > 0 || w->m_style.borderStyle == 1 && w->m_style.outlineWidth == 0) && !(w->m_ktype == 2 && time < w->m_kstart))
  562. if(w->m_style.outlineWidth > 0 && !(w->m_ktype == 2 && time < w->m_kstart))
  563. {
  564. int x = p.x;
  565. int y = p.y + m_ascent - w->m_ascent;
  566. DWORD aoutline = w->m_style.alpha[2];
  567. if(alpha > 0) aoutline += MulDiv(alpha, 0xff - w->m_style.alpha[2], 0xff);
  568. COLORREF outline = revcolor(w->m_style.colors[2]) | ((0xff-aoutline)<<24);
  569. long sw[6] = {outline, -1};
  570. w->Paint(CPoint(x, y), org);
  571. if(w->m_style.borderStyle == 0)
  572. {
  573. bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, !w->m_style.alpha[0] && !w->m_style.alpha[1], true);
  574. }
  575. else if(w->m_style.borderStyle == 1 && w->m_pOpaqueBox)
  576. {
  577. bbox |= w->m_pOpaqueBox->Draw(spd, clipRect, pAlphaMask, x, y, sw, true/*!w->m_style.alpha[0] && !w->m_style.alpha[1]*/, false);
  578. }
  579. }
  580. p.x += w->m_width;
  581. }
  582. return(bbox);
  583. }
  584. CRect CLine::PaintBody(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha)
  585. {
  586. CRect bbox(0, 0, 0, 0);
  587. POSITION pos = GetHeadPosition();
  588. while(pos)
  589. {
  590. CWord* w = GetNext(pos);
  591. if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
  592. int x = p.x;
  593. int y = p.y + m_ascent - w->m_ascent;
  594. // colors
  595. DWORD aprimary = w->m_style.alpha[0];
  596. if(alpha > 0) aprimary += MulDiv(alpha, 0xff - w->m_style.alpha[0], 0xff);
  597. COLORREF primary = revcolor(w->m_style.colors[0]) | ((0xff-aprimary)<<24);
  598. DWORD asecondary = w->m_style.alpha[1];
  599. if(alpha > 0) asecondary += MulDiv(alpha, 0xff - w->m_style.alpha[1], 0xff);
  600. COLORREF secondary = revcolor(w->m_style.colors[1]) | ((0xff-asecondary)<<24);
  601. long sw[6] = {primary, 0, secondary};
  602. // karaoke
  603. double t;
  604. if(w->m_ktype == 0 || w->m_ktype == 2)
  605. {
  606. t = time < w->m_kstart ? 0 : 1;
  607. }
  608. else if(w->m_ktype == 1)
  609. {
  610. if(time < w->m_kstart) t = 0;
  611. else if(time < w->m_kend) 
  612. {
  613. t = 1.0 * (time - w->m_kstart) / (w->m_kend - w->m_kstart);
  614. double angle = fmod(w->m_style.fontAngleZ, 360.0);
  615. if(angle > 90 && angle < 270) 
  616. {
  617. t = 1-t;
  618. COLORREF tmp = sw[0]; sw[0] = sw[2]; sw[2] = tmp;
  619. }
  620. }
  621. else t = 1.0;
  622. }
  623. if(t >= 1)
  624. {
  625. sw[1] = 0xffffffff;
  626. }
  627. sw[3] = (int)(w->m_style.outlineWidth + t*w->m_width) >> 3;
  628. sw[4] = sw[2];
  629. sw[5] = 0x00ffffff;
  630. w->Paint(CPoint(x, y), org);
  631. bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, true, false);
  632. p.x += w->m_width;
  633. }
  634. return(bbox);
  635. }
  636. // CSubtitle
  637. CSubtitle::CSubtitle()
  638. {
  639. memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
  640. m_pClipper = NULL;
  641. m_scalex = m_scaley = 1;
  642. }
  643. CSubtitle::~CSubtitle()
  644. {
  645. Empty();
  646. }
  647. void CSubtitle::Empty()
  648. {
  649. POSITION pos = GetHeadPosition();
  650. while(pos) delete GetNext(pos);
  651. pos = m_words.GetHeadPosition();
  652. while(pos) delete m_words.GetNext(pos);
  653. for(int i = 0; i < EF_NUMBEROFEFFECTS; i++) {if(m_effects[i]) delete m_effects[i];}
  654. memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
  655. if(m_pClipper) delete m_pClipper;
  656. m_pClipper = NULL;
  657. }
  658. int CSubtitle::GetFullWidth()
  659. {
  660. int width = 0;
  661. POSITION pos = m_words.GetHeadPosition();
  662. while(pos) width += m_words.GetNext(pos)->m_width;
  663. return(width);
  664. }
  665. int CSubtitle::GetFullLineWidth(POSITION pos)
  666. {
  667. int width = 0;
  668. while(pos) 
  669. {
  670. CWord* w = m_words.GetNext(pos);
  671. if(w->m_fLineBreak) break;
  672. width += w->m_width;
  673. }
  674. return(width);
  675. }
  676. int CSubtitle::GetWrapWidth(POSITION pos, int maxwidth)
  677. {
  678. if(m_wrapStyle == 0 || m_wrapStyle == 3)
  679. {
  680. if(maxwidth > 0) 
  681. {
  682. // int fullwidth = GetFullWidth();
  683. int fullwidth = GetFullLineWidth(pos);
  684. int minwidth = fullwidth / ((abs(fullwidth) / maxwidth) + 1);
  685. int width = 0, wordwidth = 0;
  686. while(pos && width < minwidth)
  687. {
  688. CWord* w = m_words.GetNext(pos);
  689. wordwidth = w->m_width;
  690. if(abs(width + wordwidth) < abs(maxwidth)) width += wordwidth;
  691. }
  692. maxwidth = width;
  693. if(m_wrapStyle == 3 && pos) maxwidth -= wordwidth;
  694. }
  695. }
  696. else if(m_wrapStyle == 1)
  697. {
  698. // maxwidth = maxwidth;
  699. }
  700. else if(m_wrapStyle == 2)
  701. {
  702. maxwidth = INT_MAX;
  703. }
  704. return(maxwidth);
  705. }
  706. CLine* CSubtitle::GetNextLine(POSITION& pos, int maxwidth)
  707. {
  708. if(pos == NULL) return(NULL);
  709. CLine* ret = new CLine();
  710. if(!ret) return(NULL);
  711. ret->m_width = ret->m_ascent = ret->m_descent = ret->m_border = 0;
  712. maxwidth = GetWrapWidth(pos, maxwidth);
  713. bool fEmptyLine = true;
  714. while(pos)
  715. {
  716. CWord* w = m_words.GetNext(pos);
  717. if(ret->m_ascent < w->m_ascent) ret->m_ascent = w->m_ascent;
  718. if(ret->m_descent < w->m_descent) ret->m_descent = w->m_descent;
  719. if(ret->m_border < w->m_style.outlineWidth) ret->m_border = (int)(w->m_style.outlineWidth+0.5);
  720. if(w->m_fLineBreak) 
  721. {
  722. if(fEmptyLine) {ret->m_ascent /= 2; ret->m_descent /= 2; ret->m_border = 0;}
  723. ret->Compact();
  724. return(ret);
  725. }
  726. fEmptyLine = false;
  727. bool fWSC = w->m_fWhiteSpaceChar;
  728. int width = w->m_width;
  729. POSITION pos2 = pos;
  730. while(pos2)
  731. {
  732. if(m_words.GetAt(pos2)->m_fWhiteSpaceChar != fWSC
  733. || m_words.GetAt(pos2)->m_fLineBreak) break;
  734. CWord* w2 = m_words.GetNext(pos2);
  735. width += w2->m_width;
  736. }
  737. if((ret->m_width += width) <= maxwidth || ret->IsEmpty()) 
  738. {
  739. ret->AddTail(w->Copy());
  740. while(pos != pos2)
  741. {
  742. ret->AddTail(m_words.GetNext(pos)->Copy());
  743. }
  744. pos = pos2;
  745. }
  746. else
  747. {
  748. if(pos) m_words.GetPrev(pos);
  749. else pos = m_words.GetTailPosition();
  750. ret->m_width -= width;
  751. break;
  752. }
  753. }
  754. ret->Compact();
  755. return(ret);
  756. }
  757. void CSubtitle::CreateClippers(CSize size)
  758. {
  759. size.cx >>= 3;
  760. size.cy >>= 3;
  761. if(m_effects[EF_BANNER] && m_effects[EF_BANNER]->param[2])
  762. {
  763. int width = m_effects[EF_BANNER]->param[2];
  764. int w = size.cx, h = size.cy;
  765. if(!m_pClipper) 
  766. {
  767. CStringW str;
  768. str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
  769. m_pClipper = new CClipper(str, size, 1, 1);
  770. if(!m_pClipper) return;
  771. }
  772. int da = (64<<8)/width;
  773. BYTE* am = m_pClipper->m_pAlphaMask;
  774. for(int j = 0; j < h; j++, am += w)
  775. {
  776. int a = 0;
  777. int k = min(width, w);
  778. for(int i = 0; i < k; i++, a += da)
  779. am[i] = (am[i]*a)>>14;
  780. a = 0x40<<8;
  781. k = w-width;
  782. if(k < 0) {a -= -k*da; k = 0;}
  783.             
  784. for(int i = k; i < w; i++, a -= da)
  785. am[i] = (am[i]*a)>>14;
  786. }
  787. }
  788. else if(m_effects[EF_SCROLL] && m_effects[EF_SCROLL]->param[4])
  789. {
  790. int height = m_effects[EF_SCROLL]->param[4];
  791. int w = size.cx, h = size.cy;
  792. if(!m_pClipper) 
  793. {
  794. CStringW str;
  795. str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
  796. m_pClipper = new CClipper(str, size, 1, 1);
  797. if(!m_pClipper) return;
  798. }
  799. int da = (64<<8)/height;
  800. int a = 0;
  801. int k = m_effects[EF_SCROLL]->param[0]>>3;
  802. int l = k+height;
  803. if(k < 0) {a += -k*da; k = 0;}
  804. if(l > h) {l = h;}
  805. if(k < h)
  806. {
  807. BYTE* am = &m_pClipper->m_pAlphaMask[k*w];
  808. memset(m_pClipper->m_pAlphaMask, 0, am - m_pClipper->m_pAlphaMask);
  809. for(int j = k; j < l; j++, a += da)
  810. {
  811. for(int i = 0; i < w; i++, am++)
  812. *am = ((*am)*a)>>14;
  813. }
  814. }
  815. da = -(64<<8)/height;
  816. a = 0x40<<8;
  817. l = m_effects[EF_SCROLL]->param[1]>>3;
  818. k = l-height;
  819. if(k < 0) {a += -k*da; k = 0;}
  820. if(l > h) {l = h;}
  821. if(k < h)
  822. {
  823. BYTE* am = &m_pClipper->m_pAlphaMask[k*w];
  824. int j = k;
  825. for(; j < l; j++, a += da)
  826. {
  827. for(int i = 0; i < w; i++, am++)
  828. *am = ((*am)*a)>>14;
  829. }
  830. memset(am, 0, (h-j)*w);
  831. }
  832. }
  833. }
  834. void CSubtitle::MakeLines(CSize size, CRect marginRect)
  835. {
  836. CSize spaceNeeded(0, 0);
  837. bool fFirstLine = true;
  838. m_topborder = m_bottomborder = 0;
  839. CLine* l = NULL;
  840. POSITION pos = m_words.GetHeadPosition();
  841. while(pos)
  842. {
  843. l = GetNextLine(pos, size.cx - marginRect.left - marginRect.right);
  844. if(!l) break;
  845. if(fFirstLine) {m_topborder = l->m_border; fFirstLine = false;}
  846. spaceNeeded.cx = max(l->m_width, spaceNeeded.cx);
  847. spaceNeeded.cy += l->m_ascent + l->m_descent;
  848. AddTail(l);
  849. }
  850. if(l) m_bottomborder = l->m_border;
  851. m_rect = CRect(
  852. CPoint((m_scrAlignment%3) == 1 ? marginRect.left
  853. : (m_scrAlignment%3) == 2 ? (marginRect.left + (size.cx - marginRect.right) - spaceNeeded.cx + 1) / 2
  854. : (size.cx - marginRect.right - spaceNeeded.cx),
  855. m_scrAlignment <= 3 ? (size.cy - marginRect.bottom - spaceNeeded.cy)
  856. : m_scrAlignment <= 6 ? (marginRect.top + (size.cy - marginRect.bottom) - spaceNeeded.cy + 1) / 2
  857. : marginRect.top),
  858. spaceNeeded);
  859. }
  860. // CScreenLayoutAllocator
  861. void CScreenLayoutAllocator::Empty()
  862. {
  863. m_subrects.RemoveAll();
  864. }
  865. void CScreenLayoutAllocator::AdvanceToSegment(int segment, const CSubArray& sa)
  866. {
  867. POSITION pos = m_subrects.GetHeadPosition();
  868. while(pos)
  869. {
  870. POSITION prev = pos;
  871. SubRect& sr = m_subrects.GetNext(pos);
  872. bool fFound = false;
  873. if(abs(sr.segment - segment) <= 1) // using abs() makes it possible to play the subs backwards, too :)
  874. {
  875. for(int i = 0; i < sa.GetSize() && !fFound; i++)
  876. {
  877. if(sa[i] == sr.entry) 
  878. {
  879. sr.segment = segment;
  880. fFound = true;
  881. }
  882. }
  883. }
  884. if(!fFound) m_subrects.RemoveAt(prev);
  885. }
  886. }
  887. CRect CScreenLayoutAllocator::AllocRect(CSubtitle* s, int segment, int entry, int layer, int collisions)
  888. {
  889. // TODO: handle collisions == 1 (reversed collisions)
  890. POSITION pos = m_subrects.GetHeadPosition();
  891. while(pos)
  892. {
  893. SubRect& sr = m_subrects.GetNext(pos);
  894. if(sr.segment == segment && sr.entry == entry) 
  895. {
  896. return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
  897. }
  898. }
  899. CRect r = s->m_rect + CRect(0, s->m_topborder, 0, s->m_bottomborder);
  900. bool fSearchDown = s->m_scrAlignment > 3;
  901. bool fOK;
  902. do
  903. {
  904. fOK = true;
  905. pos = m_subrects.GetHeadPosition();
  906. while(pos)
  907. {
  908. SubRect& sr = m_subrects.GetNext(pos);
  909. if(layer == sr.layer && !(r & sr.r).IsRectEmpty())
  910. {
  911. if(fSearchDown)
  912. {
  913. r.bottom = sr.r.bottom + r.Height();
  914. r.top = sr.r.bottom;
  915. }
  916. else
  917. {
  918. r.top = sr.r.top - r.Height();
  919. r.bottom = sr.r.top;
  920. }
  921. fOK = false;
  922. }
  923. }
  924. }
  925. while(!fOK);
  926. SubRect sr;
  927. sr.r = r;
  928. sr.segment = segment;
  929. sr.entry = entry;
  930. sr.layer = layer;
  931. m_subrects.AddTail(sr);
  932. return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
  933. }
  934. // CRenderedTextSubtitle
  935. CRenderedTextSubtitle::CRenderedTextSubtitle(CCritSec* pLock)
  936. : ISubPicProviderImpl(pLock)
  937. {
  938. m_size = CSize(0, 0);
  939. if(g_hDC_refcnt == 0) 
  940. {
  941. g_hDC = CreateCompatibleDC(NULL);
  942. SetBkMode(g_hDC, TRANSPARENT); 
  943. SetTextColor(g_hDC, 0xffffff); 
  944. SetMapMode(g_hDC, MM_TEXT);
  945. }
  946. g_hDC_refcnt++;
  947. }
  948. CRenderedTextSubtitle::~CRenderedTextSubtitle()
  949. {
  950. Deinit();
  951. g_hDC_refcnt--;
  952. if(g_hDC_refcnt == 0) DeleteDC(g_hDC);
  953. }
  954. void CRenderedTextSubtitle::Copy(CSimpleTextSubtitle& sts)
  955. {
  956. CSimpleTextSubtitle::Copy(sts);
  957. m_size = CSize(0, 0);
  958. if(CRenderedTextSubtitle* pRTS = dynamic_cast<CRenderedTextSubtitle*>(&sts))
  959. {
  960. m_size = pRTS->m_nSize;
  961. }
  962. }
  963. void CRenderedTextSubtitle::Empty()
  964. {
  965. Deinit();
  966. CSimpleTextSubtitle::Empty();
  967. }
  968. void CRenderedTextSubtitle::OnChanged()
  969. {
  970. CSimpleTextSubtitle::OnChanged();
  971. POSITION pos = m_subtitleCache.GetStartPosition();
  972. while(pos)
  973. {
  974. int i;
  975. CSubtitle* s;
  976.         m_subtitleCache.GetNextAssoc(pos, i, s);
  977. delete s;
  978. }
  979. m_subtitleCache.RemoveAll();
  980. m_sla.Empty();
  981. }
  982. bool CRenderedTextSubtitle::Init(CSize size, CRect vidrect)
  983. {
  984. Deinit();
  985. m_size = CSize(size.cx*8, size.cy*8);
  986. m_vidrect = CRect(vidrect.left*8, vidrect.top*8, vidrect.right*8, vidrect.bottom*8);
  987. m_sla.Empty();
  988. return(true);
  989. }
  990. void CRenderedTextSubtitle::Deinit()
  991. {
  992. POSITION pos = m_subtitleCache.GetStartPosition();
  993. while(pos)
  994. {
  995. int i;
  996. CSubtitle* s;
  997.         m_subtitleCache.GetNextAssoc(pos, i, s);
  998. delete s;
  999. }
  1000. m_subtitleCache.RemoveAll();
  1001. m_sla.Empty();
  1002. m_size = CSize(0, 0);
  1003. m_vidrect.SetRectEmpty();
  1004. }
  1005. void CRenderedTextSubtitle::ParseEffect(CSubtitle* sub, CString str)
  1006. {
  1007. str.Trim();
  1008. if(!sub || str.IsEmpty()) return;
  1009. const TCHAR* s = _tcschr(str, ';');
  1010. if(!s) {s = (LPTSTR)(LPCTSTR)str; s += str.GetLength()-1;}
  1011. s++;
  1012. CString effect = str.Left(s - str);
  1013. if(!effect.CompareNoCase(_T("Banner;")))
  1014. {
  1015. int delay, lefttoright = 0, fadeawaywidth = 0;
  1016. if(_stscanf(s, _T("%d;%d;%d"), &delay, &lefttoright, &fadeawaywidth) < 1) return;
  1017. Effect* e = new Effect;
  1018. if(!e) return;
  1019. sub->m_effects[e->type = EF_BANNER] = e;
  1020. e->param[0] = (int)(max(1.0*delay/sub->m_scalex, 1));
  1021. e->param[1] = lefttoright;
  1022. e->param[2] = (int)(sub->m_scalex*fadeawaywidth);
  1023. sub->m_wrapStyle = 2;
  1024. }
  1025. else if(!effect.CompareNoCase(_T("Scroll up;")) || !effect.CompareNoCase(_T("Scroll down;")))
  1026. {
  1027. int top, bottom, delay, fadeawayheight = 0;
  1028. if(_stscanf(s, _T("%d;%d;%d;%d"), &top, &bottom, &delay, &fadeawayheight) < 3) return;
  1029. if(top > bottom) {int tmp = top; top = bottom; bottom = tmp;}
  1030. Effect* e = new Effect;
  1031. if(!e) return;
  1032. sub->m_effects[e->type = EF_SCROLL] = e;
  1033. e->param[0] = (int)(sub->m_scaley*top*8);
  1034. e->param[1] = (int)(sub->m_scaley*bottom*8);
  1035. e->param[2] = (int)(max(1.0*delay/sub->m_scaley, 1));
  1036. e->param[3] = (effect.GetLength() == 12);
  1037. e->param[4] = (int)(sub->m_scaley*fadeawayheight);
  1038. }
  1039. }
  1040. void CRenderedTextSubtitle::ParseString(CSubtitle* sub, CStringW str, STSStyle& style)
  1041. {
  1042. if(!sub) return;
  1043. str.Replace(L"\N", L"n");
  1044. str.Replace(L"\n", (sub->m_wrapStyle < 2 || sub->m_wrapStyle == 3) ? L" " : L"n");
  1045. str.Replace(L"\h", L"x00A0");
  1046. for(int i = 0, j = 0, len = str.GetLength(); j <= len; j++)
  1047. {
  1048. WCHAR c = str[j];
  1049. if(c != 'n' && c != ' ' && c != 'x00A0' && c != 0)
  1050. continue;
  1051. if(i < j)
  1052. {
  1053. if(CWord* w = new CText(style, str.Mid(i, j-i), m_ktype, m_kstart, m_kend))
  1054. {
  1055. sub->m_words.AddTail(w); 
  1056. m_kstart = m_kend;
  1057. }
  1058. }
  1059. if(c == 'n')
  1060. {
  1061. if(CWord* w = new CText(style, CStringW(), m_ktype, m_kstart, m_kend))
  1062. {
  1063. sub->m_words.AddTail(w); 
  1064. m_kstart = m_kend;
  1065. }
  1066. }
  1067. else if(c == ' ' || c == 'x00A0')
  1068. {
  1069. if(CWord* w = new CText(style, CStringW(c), m_ktype, m_kstart, m_kend))
  1070. {
  1071. sub->m_words.AddTail(w); 
  1072. m_kstart = m_kend;
  1073. }
  1074. }
  1075. i = j+1;
  1076. }
  1077. return;
  1078. }
  1079. void CRenderedTextSubtitle::ParsePolygon(CSubtitle* sub, CStringW str, STSStyle& style)
  1080. {
  1081. if(!sub || !str.GetLength() || !m_nPolygon) return;
  1082. 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))
  1083. {
  1084. sub->m_words.AddTail(w); 
  1085. m_kstart = m_kend;
  1086. }
  1087. }
  1088. bool CRenderedTextSubtitle::ParseSSATag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org, bool fAnimate)
  1089. {
  1090. if(!sub) return(false);
  1091. int nTags = 0, nUnrecognizedTags = 0;
  1092. for(int i = 0, j; (j = str.Find('\', i)) >= 0; i = j)
  1093. {
  1094. CStringW cmd;
  1095. for(WCHAR c = str[++j]; c && c != '(' && c != '\'; cmd += c, c = str[++j]);
  1096. cmd.Trim();
  1097. if(cmd.IsEmpty()) continue;
  1098. CArray<CStringW> params;
  1099. if(str[j] == '(')
  1100. {
  1101. CStringW param;
  1102. for(WCHAR c = str[++j]; c && c != ')'; param += c, c = str[++j]);
  1103. param.Trim();
  1104. while(!param.IsEmpty())
  1105. {
  1106. int i = param.Find(','), j = param.Find('\');
  1107. if(i >= 0 && (j < 0 || i < j))
  1108. {
  1109. CStringW s = param.Left(i).Trim();
  1110. if(!s.IsEmpty()) params.Add(s);
  1111. param = i+1 < param.GetLength() ? param.Mid(i+1) : L"";
  1112. }
  1113. else
  1114. {
  1115. param.Trim();
  1116. if(!param.IsEmpty()) params.Add(param);
  1117. param.Empty();
  1118. }
  1119. }
  1120. }
  1121. if(!cmd.Find(L"1c") || !cmd.Find(L"2c") || !cmd.Find(L"3c") || !cmd.Find(L"4c"))
  1122. params.Add(cmd.Mid(2).Trim(L"&H")), cmd = cmd.Left(2);
  1123. else if(!cmd.Find(L"1a") || !cmd.Find(L"2a") || !cmd.Find(L"3a") || !cmd.Find(L"4a"))
  1124. params.Add(cmd.Mid(2).Trim(L"&H")), cmd = cmd.Left(2);
  1125. else if(!cmd.Find(L"alpha"))
  1126. params.Add(cmd.Mid(5).Trim(L"&H")), cmd = cmd.Left(5);
  1127. else if(!cmd.Find(L"an"))
  1128. params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
  1129. else if(!cmd.Find(L"a"))
  1130. params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
  1131. else if(!cmd.Find(L"bord"))
  1132. params.Add(cmd.Mid(4)), cmd = cmd.Left(4);
  1133. else if(!cmd.Find(L"be"))
  1134. params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
  1135. else if(!cmd.Find(L"b"))
  1136. params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
  1137. else if(!cmd.Find(L"clip"))
  1138. ;
  1139. else if(!cmd.Find(L"c"))
  1140. params.Add(cmd.Mid(1).Trim(L"&H")), cmd = cmd.Left(1);
  1141.         else if(!cmd.Find(L"fade"))
  1142. ;
  1143. else if(!cmd.Find(L"fe"))
  1144. params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
  1145. else if(!cmd.Find(L"fn"))
  1146. params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
  1147. else if(!cmd.Find(L"frx") || !cmd.Find(L"fry") || !cmd.Find(L"frz"))
  1148. params.Add(cmd.Mid(3)), cmd = cmd.Left(3);
  1149. else if(!cmd.Find(L"fr"))
  1150. params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
  1151. else if(!cmd.Find(L"fscx") || !cmd.Find(L"fscy"))
  1152. params.Add(cmd.Mid(4)), cmd = cmd.Left(4);
  1153. else if(!cmd.Find(L"fsc"))
  1154. params.Add(cmd.Mid(3)), cmd = cmd.Left(3);
  1155. else if(!cmd.Find(L"fsp"))
  1156. params.Add(cmd.Mid(3)), cmd = cmd.Left(3);
  1157. else if(!cmd.Find(L"fs"))
  1158. params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
  1159. else if(!cmd.Find(L"i"))
  1160. params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
  1161. else if(!cmd.Find(L"kf") || !cmd.Find(L"ko"))
  1162. params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
  1163. else if(!cmd.Find(L"k") || !cmd.Find(L"K"))
  1164. params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
  1165. else if(!cmd.Find(L"move"))
  1166. ;
  1167. else if(!cmd.Find(L"org"))
  1168. ;
  1169. else if(!cmd.Find(L"pbo"))
  1170. params.Add(cmd.Mid(3)), cmd = cmd.Left(3);
  1171. else if(!cmd.Find(L"pos"))
  1172. ;
  1173. else if(!cmd.Find(L"p"))
  1174. params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
  1175. else if(!cmd.Find(L"q"))
  1176. params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
  1177. else if(!cmd.Find(L"r"))
  1178. params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
  1179. else if(!cmd.Find(L"shad"))
  1180. params.Add(cmd.Mid(4)), cmd = cmd.Left(4);
  1181. else if(!cmd.Find(L"s"))
  1182. params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
  1183. else if(!cmd.Find(L"t"))
  1184. ;
  1185. else if(!cmd.Find(L"u"))
  1186. params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
  1187. else
  1188. nUnrecognizedTags++;
  1189. nTags++;
  1190. // TODO: call ParseStyleModifier(cmd, params, ..) and move the rest there
  1191. CStringW p = params.GetCount() > 0 ? params[0] : L"";
  1192. if(cmd == "1c" || cmd == L"2c" || cmd == L"3c" || cmd == L"4c")
  1193. {
  1194. int i = cmd[0] - '1';
  1195. DWORD c = wcstol(p, NULL, 16);
  1196. style.colors[i] = !p.IsEmpty()
  1197. ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
  1198.   |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
  1199.   |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
  1200. : org.colors[i];
  1201. }
  1202. else if(cmd == L"1a" || cmd == L"2a" || cmd == L"3a" || cmd == L"4a")
  1203. {
  1204. int i = cmd[0] - '1';
  1205. style.alpha[i] = !p.IsEmpty()
  1206. ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
  1207. : org.alpha[i];
  1208. }
  1209. else if(cmd == L"alpha")
  1210. {
  1211. for(int i = 0; i < 4; i++)
  1212. {
  1213. style.alpha[i] = !p.IsEmpty()
  1214. ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
  1215. : org.alpha[i];
  1216. }
  1217. }
  1218. else if(cmd == L"an")
  1219. {
  1220. int n = wcstol(p, NULL, 10);
  1221. if(sub->m_scrAlignment < 0)
  1222.                 sub->m_scrAlignment = (n > 0 && n < 10) ? n : org.scrAlignment;
  1223. }
  1224. else if(cmd == L"a")
  1225. {
  1226. int n = wcstol(p, NULL, 10);
  1227. if(sub->m_scrAlignment < 0)
  1228.                 sub->m_scrAlignment = (n > 0 && n < 12) ? ((((n-1)&3)+1)+((n&4)?6:0)+((n&8)?3:0)) : org.scrAlignment;
  1229. }
  1230. else if(cmd == L"bord")
  1231. {
  1232. double n = CalcAnimation(wcstod(p, NULL), style.outlineWidth, fAnimate);
  1233. style.outlineWidth = !p.IsEmpty()
  1234. ? (n < 0 ? 0 : n)
  1235. : org.outlineWidth;
  1236. }
  1237. else if(cmd == L"be")
  1238. {
  1239. int n = wcstol(p, NULL, 10);
  1240. style.fBlur = !p.IsEmpty()
  1241. ? (n == 0 ? false : n == 1 ? true : org.fBlur)
  1242. : org.fBlur;
  1243. }
  1244. else if(cmd == L"b")
  1245. {
  1246. int n = wcstol(p, NULL, 10);
  1247. style.fontWeight = !p.IsEmpty()
  1248. ? (n == 0 ? FW_NORMAL : n == 1 ? FW_BOLD : n >= 100 ? n : org.fontWeight)
  1249. : org.fontWeight;
  1250. }
  1251. else if(cmd == L"clip")
  1252. {
  1253. if(params.GetCount() == 1 && !sub->m_pClipper)
  1254. {
  1255. sub->m_pClipper = new CClipper(params[0], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex, sub->m_scaley);
  1256. }
  1257. else if(params.GetCount() == 2 && !sub->m_pClipper)
  1258. {
  1259. int scale = max(wcstol(p, NULL, 10), 1);
  1260. 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)));
  1261. }
  1262. else if(params.GetCount() == 4)
  1263. {
  1264. sub->m_clip.SetRect(
  1265. (int)CalcAnimation(sub->m_scalex*wcstol(params[0], NULL, 10), sub->m_clip.left, fAnimate),
  1266. (int)CalcAnimation(sub->m_scaley*wcstol(params[1], NULL, 10), sub->m_clip.top, fAnimate),
  1267. (int)CalcAnimation(sub->m_scalex*wcstol(params[2], NULL, 10), sub->m_clip.right, fAnimate),
  1268. (int)CalcAnimation(sub->m_scaley*wcstol(params[3], NULL, 10), sub->m_clip.bottom, fAnimate));
  1269. }
  1270. }
  1271. else if(cmd == L"c")
  1272. {
  1273. DWORD c = wcstol(p, NULL, 16);
  1274. style.colors[0] = !p.IsEmpty()
  1275. ? (((int)CalcAnimation(c&0xff, style.colors[0]&0xff, fAnimate))&0xff
  1276.   |((int)CalcAnimation(c&0xff00, style.colors[0]&0xff00, fAnimate))&0xff00
  1277.   |((int)CalcAnimation(c&0xff0000, style.colors[0]&0xff0000, fAnimate))&0xff0000)
  1278. : org.colors[0];
  1279. }
  1280.         else if(cmd == L"fade" || cmd == L"fad")
  1281. {
  1282. 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])
  1283. {
  1284. if(Effect* e = new Effect)
  1285. {
  1286. for(int i = 0; i < 3; i++)
  1287. e->param[i] = wcstol(params[i], NULL, 10);
  1288. for(int i = 0; i < 4; i++)
  1289. e->t[i] = wcstol(params[3+i], NULL, 10);
  1290.                 
  1291. sub->m_effects[EF_FADE] = e;
  1292. }
  1293. }
  1294. else if(params.GetCount() == 2 && !sub->m_effects[EF_FADE]) // {fad(t1=t[1], t2=t[2])
  1295. {
  1296. if(Effect* e = new Effect)
  1297. {
  1298. e->param[0] = e->param[2] = 0xff;
  1299. e->param[1] = 0x00;
  1300. for(int i = 1; i < 3; i++) 
  1301. e->t[i] = wcstol(params[i-1], NULL, 10);
  1302. e->t[0] = e->t[3] = -1; // will be substituted with "start" and "end"
  1303. sub->m_effects[EF_FADE] = e;
  1304. }
  1305. }
  1306. }
  1307. else if(cmd == L"fe")
  1308. {
  1309. int n = wcstol(p, NULL, 10);
  1310. style.charSet = !p.IsEmpty()
  1311. ? n
  1312. : org.charSet;
  1313. }
  1314. else if(cmd == L"fn")
  1315. {
  1316. style.fontName = (!p.IsEmpty() && p != '0')
  1317. ? CString(p).Trim()
  1318. : org.fontName;
  1319. }
  1320. else if(cmd == L"frx")
  1321. {
  1322. style.fontAngleX = !p.IsEmpty()
  1323. ? CalcAnimation(wcstod(p, NULL), style.fontAngleX, fAnimate)
  1324. : org.fontAngleX;
  1325. }
  1326. else if(cmd == L"fry")
  1327. {
  1328. style.fontAngleY = !p.IsEmpty()
  1329. ? CalcAnimation(wcstod(p, NULL), style.fontAngleY, fAnimate)
  1330. : org.fontAngleY;
  1331. }
  1332. else if(cmd == L"frz" || cmd == L"fr")
  1333. {
  1334. style.fontAngleZ = !p.IsEmpty()
  1335. ? CalcAnimation(wcstod(p, NULL), style.fontAngleZ, fAnimate)
  1336. : org.fontAngleZ;
  1337. }
  1338. else if(cmd == L"fscx")
  1339. {
  1340. double n = CalcAnimation(wcstol(p, NULL, 10), style.fontScaleX, fAnimate);
  1341. style.fontScaleX = !p.IsEmpty()
  1342. ? ((n < 0) ? 0 : n)
  1343. : org.fontScaleX;
  1344. }
  1345. else if(cmd == L"fscy")
  1346. {
  1347. double n = CalcAnimation(wcstol(p, NULL, 10), style.fontScaleY, fAnimate);
  1348. style.fontScaleY = !p.IsEmpty()
  1349. ? ((n < 0) ? 0 : n)
  1350. : org.fontScaleY;
  1351. }
  1352. else if(cmd == L"fsc")
  1353. {
  1354. style.fontScaleX = org.fontScaleX;
  1355. style.fontScaleY = org.fontScaleY;
  1356. }
  1357. else if(cmd == L"fsp")
  1358. {
  1359. style.fontSpacing = !p.IsEmpty()
  1360. ? CalcAnimation(wcstod(p, NULL), style.fontSpacing, fAnimate)
  1361. : org.fontSpacing;
  1362. }
  1363. else if(cmd == L"fs")
  1364. {
  1365. if(!p.IsEmpty())
  1366. {
  1367. if(p[0] == '-' || p[0] == '+')
  1368. {
  1369. double n = CalcAnimation(style.fontSize + style.fontSize*wcstol(p, NULL, 10)/10, style.fontSize, fAnimate);
  1370. style.fontSize = (n > 0) ? n : org.fontSize;
  1371. }
  1372. else
  1373. {
  1374. double n = CalcAnimation(wcstol(p, NULL, 10), style.fontSize, fAnimate);
  1375. style.fontSize = (n > 0) ? n : org.fontSize;
  1376. }
  1377. }
  1378. else
  1379. {
  1380. style.fontSize = org.fontSize;
  1381. }
  1382. }
  1383. else if(cmd == L"i")
  1384. {
  1385. int n = wcstol(p, NULL, 10);
  1386. style.fItalic = !p.IsEmpty()
  1387. ? (n == 0 ? false : n == 1 ? true : org.fItalic)
  1388. : org.fItalic;
  1389. }
  1390. else if(cmd == L"kf" || cmd == L"K")
  1391. {
  1392. m_ktype = 1;
  1393. m_kstart = m_kend;
  1394. m_kend += !p.IsEmpty() 
  1395. ? wcstol(p, NULL, 10)*10
  1396. : 1000;
  1397. }
  1398. else if(cmd == L"ko")
  1399. {
  1400. m_ktype = 2;
  1401. m_kstart = m_kend;
  1402. m_kend += !p.IsEmpty() 
  1403. ? wcstol(p, NULL, 10)*10
  1404. : 1000;
  1405. }
  1406. else if(cmd == L"k")
  1407. {
  1408. m_ktype = 0;
  1409. m_kstart = m_kend;
  1410. m_kend += !p.IsEmpty() 
  1411. ? wcstol(p, NULL, 10)*10
  1412. : 1000;
  1413. }
  1414. else if(cmd == L"move") // {move(x1=param[0], y1=param[1], x2=param[2], y2=param[3][, t1=t[0], t2=t[1]])}
  1415. {
  1416. if((params.GetCount() == 4 || params.GetCount() == 6) && !sub->m_effects[EF_MOVE])
  1417. {
  1418. if(Effect* e = new Effect)
  1419. {
  1420. e->param[0] = (int)(sub->m_scalex*wcstol(params[0], NULL, 10)*8);
  1421. e->param[1] = (int)(sub->m_scaley*wcstol(params[1], NULL, 10)*8);
  1422. e->param[2] = (int)(sub->m_scalex*wcstol(params[2], NULL, 10)*8);
  1423. e->param[3] = (int)(sub->m_scaley*wcstol(params[3], NULL, 10)*8);
  1424. e->t[0] = e->t[1] = -1;
  1425. if(params.GetCount() == 6)
  1426. {
  1427. for(int i = 0; i < 2; i++)
  1428. e->t[i] = wcstol(params[4+i], NULL, 10);
  1429. }
  1430. sub->m_effects[EF_MOVE] = e;
  1431. }
  1432. }
  1433. }
  1434. else if(cmd == L"org") // {org(x=param[0], y=param[1])}
  1435. {
  1436. if(params.GetCount() == 2 && !sub->m_effects[EF_ORG])
  1437. {
  1438. if(Effect* e = new Effect)
  1439. {
  1440. e->param[0] = (int)(sub->m_scalex*wcstol(params[0], NULL, 10)*8);
  1441. e->param[1] = (int)(sub->m_scaley*wcstol(params[1], NULL, 10)*8);
  1442. sub->m_effects[EF_ORG] = e;
  1443. }
  1444. }
  1445. }
  1446. else if(cmd == L"pbo")
  1447. {
  1448. m_polygonBaselineOffset = wcstol(p, NULL, 10);
  1449. }
  1450. else if(cmd == L"pos")
  1451. {
  1452. if(params.GetCount() == 2 && !sub->m_effects[EF_MOVE])
  1453. {
  1454. if(Effect* e = new Effect)
  1455. {
  1456. e->param[0] = e->param[2] = (int)(sub->m_scalex*wcstol(params[0], NULL, 10)*8);
  1457. e->param[1] = e->param[3] = (int)(sub->m_scaley*wcstol(params[1], NULL, 10)*8);
  1458. e->t[0] = e->t[1] = 0;
  1459. sub->m_effects[EF_MOVE] = e;
  1460. }
  1461. }
  1462. }
  1463. else if(cmd == L"p")
  1464. {
  1465. int n = wcstol(p, NULL, 10);
  1466. m_nPolygon = (n <= 0 ? 0 : n);
  1467. }
  1468. else if(cmd == L"q")
  1469. {
  1470. int n = wcstol(p, NULL, 10);
  1471. sub->m_wrapStyle = !p.IsEmpty() && (0 <= n && n <= 3)
  1472. ? n
  1473. : m_defaultWrapStyle;
  1474. }
  1475. else if(cmd == L"r")
  1476. {
  1477. void* val;
  1478. style = (!p.IsEmpty() && m_styles.Lookup(CString(p), val) && val) ? *((STSStyle*)val) : org;
  1479. }
  1480. else if(cmd == L"shad")
  1481. {
  1482. double n = CalcAnimation(wcstod(p, NULL), style.shadowDepth, fAnimate);
  1483. style.shadowDepth = !p.IsEmpty()
  1484. ? (n < 0 ? 0 : n)
  1485. : org.shadowDepth;
  1486. }
  1487. else if(cmd == L"s")
  1488. {
  1489. int n = wcstol(p, NULL, 10);
  1490. style.fStrikeOut = !p.IsEmpty()
  1491. ? (n == 0 ? false : n == 1 ? true : org.fStrikeOut)
  1492. : org.fStrikeOut;
  1493. }
  1494. else if(cmd == L"t") // t([<t1>,<t2>,][<accel>,]<style modifiers>)
  1495. {
  1496. p.Empty();
  1497. m_animStart = m_animEnd = 0;
  1498. m_animAccel = 1;
  1499. if(params.GetCount() == 1)
  1500. {
  1501. p = params[0];
  1502. }
  1503. else if(params.GetCount() == 2)
  1504. {
  1505. m_animAccel = wcstod(params[0], NULL);
  1506. p = params[1];
  1507. }
  1508. else if(params.GetCount() == 3)
  1509. {
  1510. m_animStart = (int)wcstod(params[0], NULL); 
  1511. m_animEnd = (int)wcstod(params[1], NULL);
  1512. p = params[2];
  1513. }
  1514. else if(params.GetCount() == 4)
  1515. {
  1516. m_animStart = wcstol(params[0], NULL, 10); 
  1517. m_animEnd = wcstol(params[1], NULL, 10);
  1518. m_animAccel = wcstod(params[2], NULL);
  1519. p = params[3];
  1520. }
  1521. ParseSSATag(sub, p, style, org, true);
  1522. sub->m_fAnimated = true;
  1523. }
  1524. else if(cmd == L"u")
  1525. {
  1526. int n = wcstol(p, NULL, 10);
  1527. style.fUnderline = !p.IsEmpty()
  1528. ? (n == 0 ? false : n == 1 ? true : org.fUnderline)
  1529. : org.fUnderline;
  1530. }
  1531. }
  1532. // return(nUnrecognizedTags < nTags);
  1533. return(true); // there are ppl keeping coments inside {}, lets make them happy now
  1534. }
  1535. bool CRenderedTextSubtitle::ParseHtmlTag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org)
  1536. {
  1537. if(str.Find(L"!--") == 0)
  1538. return(true);
  1539. bool fClosing = str[0] == '/';
  1540. str.Trim(L" /");
  1541. int i = str.Find(' ');
  1542. if(i < 0) i = str.GetLength();
  1543. CStringW tag = str.Left(i).MakeLower();
  1544. str = str.Mid(i).Trim();
  1545. CArray<CStringW> attribs, params;
  1546. while((i = str.Find('=')) > 0)
  1547. {
  1548. attribs.Add(str.Left(i).Trim().MakeLower());
  1549. str = str.Mid(i+1);
  1550. for(i = 0; _istspace(str[i]); i++);
  1551. str = str.Mid(i);
  1552. if(str[0] == '"') {str = str.Mid(1); i = str.Find('"');}
  1553. else i = str.Find(' ');
  1554. if(i < 0) i = str.GetLength();
  1555. params.Add(str.Left(i).Trim().MakeLower());
  1556. str = str.Mid(i+1);
  1557. }
  1558. if(tag == L"text")
  1559. ;
  1560. else if(tag == L"b" || tag == L"strong")
  1561. style.fontWeight = !fClosing ? FW_BOLD : org.fontWeight;
  1562. else if(tag == L"i" || tag == L"em")
  1563. style.fItalic = !fClosing ? true : org.fItalic;
  1564. else if(tag == L"u")
  1565. style.fUnderline = !fClosing ? true : org.fUnderline;
  1566. else if(tag == L"s" || tag == L"strike" || tag == L"del")
  1567. style.fStrikeOut = !fClosing ? true : org.fStrikeOut;
  1568. else if(tag == L"font")
  1569. {
  1570. if(!fClosing)
  1571. {
  1572. for(i = 0; i < attribs.GetCount(); i++)
  1573. {
  1574. if(params[i].IsEmpty()) continue;
  1575. int nColor = -1;
  1576. if(attribs[i] == L"face")
  1577. {
  1578. style.fontName = params[i];
  1579. }
  1580. else if(attribs[i] == L"size")
  1581. {
  1582. if(params[i][0] == '+')
  1583. style.fontSize += wcstol(params[i], NULL, 10);
  1584. else if(params[i][0] == '-')
  1585. style.fontSize -= wcstol(params[i], NULL, 10);
  1586. else
  1587. style.fontSize = wcstol(params[i], NULL, 10);
  1588. }
  1589. else if(attribs[i] == L"color")
  1590. {
  1591. nColor = 0;
  1592. }
  1593. else if(attribs[i] == L"outline-color")
  1594. {
  1595. nColor = 2;
  1596. }
  1597. else if(attribs[i] == L"outline-level")
  1598. {
  1599. style.outlineWidth = wcstol(params[i], NULL, 10);
  1600. }
  1601. else if(attribs[i] == L"shadow-color")
  1602. {
  1603. nColor = 3;
  1604. }
  1605. else if(attribs[i] == L"shadow-level")
  1606. {
  1607. style.shadowDepth = wcstol(params[i], NULL, 10);
  1608. }
  1609. if(nColor >= 0 && nColor < 4)
  1610. {
  1611. CString key = WToT(params[i]).TrimLeft('#');
  1612. void* val;
  1613. if(g_colors.Lookup(key, val))
  1614. style.colors[nColor] = (DWORD)val;
  1615. else if ((style.colors[nColor] = _tcstol(key, NULL, 16)) == 0)
  1616. style.colors[nColor] = 0x00ffffff;  // default is white
  1617. style.colors[nColor] = ((style.colors[nColor]>>16)&0xff)|((style.colors[nColor]&0xff)<<16)|(style.colors[nColor]&0x00ff00);
  1618. }
  1619. }
  1620. }
  1621. else
  1622. {
  1623. style.fontName = org.fontName;
  1624. style.fontSize = org.fontSize;
  1625. memcpy(style.colors, org.colors, sizeof(style.colors));
  1626. }
  1627. }
  1628. else if(tag == L"k" && attribs.GetCount() == 1 && attribs[0] == L"t")
  1629. {
  1630. m_ktype = 1;
  1631. m_kstart = m_kend;
  1632. m_kend += wcstol(params[0], NULL, 10);
  1633. }
  1634. else 
  1635. return(false);
  1636. return(true);
  1637. }
  1638. double CRenderedTextSubtitle::CalcAnimation(double dst, double src, bool fAnimate)
  1639. {
  1640. int s = m_animStart ? m_animStart : 0;
  1641. int e = m_animEnd ? m_animEnd : m_delay;
  1642. if(fabs(dst-src) >= 0.0001 && fAnimate)
  1643. {
  1644. if(m_time < s) dst = src;
  1645. else if(s <= m_time && m_time < e)
  1646. {
  1647. double t = pow(1.0 * (m_time - s) / (e - s), m_animAccel);
  1648. dst = (1 - t) * src + t * dst;
  1649. }
  1650. // else dst = dst;
  1651. }
  1652. return(dst);
  1653. }
  1654. CSubtitle* CRenderedTextSubtitle::GetSubtitle(int entry)
  1655. {
  1656. CSubtitle* sub;
  1657. if(m_subtitleCache.Lookup(entry, sub)) 
  1658. {
  1659. if(sub->m_fAnimated) {delete sub; sub = NULL;}
  1660. else return(sub);
  1661. }
  1662. sub = new CSubtitle();
  1663. if(!sub) return(NULL);
  1664. CStringW str = GetStrW(entry, true);
  1665. STSStyle stss, orgstss;
  1666. GetStyle(entry, stss);
  1667. orgstss = stss;
  1668. sub->m_clip.SetRect(0, 0, m_size.cx>>3, m_size.cy>>3);
  1669. sub->m_scrAlignment = -stss.scrAlignment;
  1670. sub->m_wrapStyle = m_defaultWrapStyle;
  1671. sub->m_fAnimated = false;
  1672. sub->m_relativeTo = stss.relativeTo;
  1673. sub->m_scalex = m_dstScreenSize.cx > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Width() : m_size.cx) / (m_dstScreenSize.cx*8) : 1.0;
  1674. sub->m_scaley = m_dstScreenSize.cy > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Height() : m_size.cy) / (m_dstScreenSize.cy*8) : 1.0;
  1675. m_animStart = m_animEnd = 0;
  1676. m_animAccel = 1;
  1677. m_ktype = m_kstart = m_kend = 0;
  1678. m_nPolygon = 0;
  1679. m_polygonBaselineOffset = 0;
  1680. ParseEffect(sub, GetAt(entry).effect);
  1681. while(!str.IsEmpty())
  1682. {
  1683. bool fParsed = false;
  1684. int i;
  1685. if(str[0] == '{' && (i = str.Find(L'}')) > 0)
  1686. {
  1687. if(fParsed = ParseSSATag(sub, str.Mid(1, i-1), stss, orgstss))
  1688. str = str.Mid(i+1);
  1689. }
  1690. else if(str[0] == '<' && (i = str.Find(L'>')) > 0)
  1691. {
  1692. if(fParsed = ParseHtmlTag(sub, str.Mid(1, i-1), stss, orgstss))
  1693. str = str.Mid(i+1);
  1694. }
  1695. if(fParsed)
  1696. {
  1697. i = str.FindOneOf(L"{<");
  1698. if(i < 0) i = str.GetLength();
  1699. if(i == 0) continue;
  1700. }
  1701. else
  1702. {
  1703. i = str.Mid(1).FindOneOf(L"{<");
  1704. if(i < 0) i = str.GetLength()-1;
  1705. i++;
  1706. }
  1707. STSStyle tmp = stss;
  1708. tmp.fontSize = sub->m_scaley*tmp.fontSize*64;
  1709. tmp.fontSpacing = sub->m_scalex*tmp.fontSpacing*64;
  1710. tmp.outlineWidth *= (m_fScaledBAS ? ((sub->m_scalex+sub->m_scaley)/2) : 1) * 8;
  1711. tmp.shadowDepth *= (m_fScaledBAS ? ((sub->m_scalex+sub->m_scaley)/2) : 1) * 8;
  1712. if(m_nPolygon)
  1713. {
  1714. ParsePolygon(sub, str.Left(i), tmp);
  1715. }
  1716. else
  1717. {
  1718. ParseString(sub, str.Left(i), tmp);
  1719. }
  1720. str = str.Mid(i);
  1721. }
  1722. // just a "work-around" solution... in most cases nobody will want to use org together with moving but without rotating the subs
  1723. if(sub->m_effects[EF_ORG] && (sub->m_effects[EF_MOVE] || sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL]))
  1724. sub->m_fAnimated = true;
  1725. sub->m_scrAlignment = abs(sub->m_scrAlignment);
  1726. STSEntry stse = GetAt(entry);
  1727. CRect marginRect = stse.marginRect;
  1728. if(marginRect.left == 0) marginRect.left = orgstss.marginRect.left;
  1729. if(marginRect.top == 0) marginRect.top = orgstss.marginRect.top;
  1730. if(marginRect.right == 0) marginRect.right = orgstss.marginRect.right;
  1731. if(marginRect.bottom == 0) marginRect.bottom = orgstss.marginRect.bottom;
  1732. marginRect.left = (int)(sub->m_scalex*marginRect.left*8);
  1733. marginRect.top = (int)(sub->m_scaley*marginRect.top*8);
  1734. marginRect.right = (int)(sub->m_scalex*marginRect.right*8);
  1735. marginRect.bottom = (int)(sub->m_scaley*marginRect.bottom*8);
  1736. if(stss.relativeTo == 1)
  1737. {
  1738. marginRect.left += m_vidrect.left;
  1739. marginRect.top += m_vidrect.top;
  1740. marginRect.right += m_size.cx - m_vidrect.right;
  1741. marginRect.bottom += m_size.cy - m_vidrect.bottom;
  1742. }
  1743. sub->CreateClippers(m_size);
  1744. sub->MakeLines(m_size, marginRect);
  1745. m_subtitleCache[entry] = sub;
  1746. return(sub);
  1747. }
  1748. //
  1749. STDMETHODIMP CRenderedTextSubtitle::NonDelegatingQueryInterface(REFIID riid, void** ppv)
  1750. {
  1751.     CheckPointer(ppv, E_POINTER);
  1752.     *ppv = NULL;
  1753.     return 
  1754. QI(IPersist)
  1755. QI(ISubStream)
  1756. QI(ISubPicProvider)
  1757. __super::NonDelegatingQueryInterface(riid, ppv);
  1758. }
  1759. // ISubPicProvider
  1760. STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetStartPosition(REFERENCE_TIME rt, double fps)
  1761. {
  1762. int iSegment = -1;
  1763. SearchSubs((int)(rt/10000), fps, &iSegment, NULL);
  1764. if(iSegment < 0) iSegment = 0;
  1765. return(GetNext((POSITION)iSegment));
  1766. }
  1767. STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetNext(POSITION pos)
  1768. {
  1769. int iSegment = (int)pos;
  1770. const STSSegment* stss;
  1771. while((stss = GetSegment(iSegment)) && stss->subs.GetSize() == 0)
  1772. iSegment++;
  1773. return(stss ? (POSITION)(iSegment+1) : NULL);
  1774. }
  1775. STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStart(POSITION pos, double fps)
  1776. {
  1777. return(10000i64 * TranslateSegmentStart((int)pos-1, fps));
  1778. }
  1779. STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStop(POSITION pos, double fps)
  1780. {
  1781. return(10000i64 * TranslateSegmentEnd((int)pos-1, fps));
  1782. }
  1783. STDMETHODIMP_(bool) CRenderedTextSubtitle::IsAnimated(POSITION pos)
  1784. {
  1785. // TODO
  1786. return(true);
  1787. }
  1788. struct LSub {int idx, layer, readorder;};
  1789. static int lscomp(const void* ls1, const void* ls2)
  1790. {
  1791. int ret = ((LSub*)ls1)->layer - ((LSub*)ls2)->layer;
  1792. if(!ret) ret = ((LSub*)ls1)->readorder - ((LSub*)ls2)->readorder;
  1793. return(ret);
  1794. }
  1795. STDMETHODIMP CRenderedTextSubtitle::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
  1796. {
  1797. CRect bbox2(0,0,0,0);
  1798. 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))
  1799. Init(CSize(spd.w, spd.h), spd.vidrect);
  1800. int t = (int)(rt / 10000);
  1801. int segment;
  1802. const STSSegment* stss = SearchSubs(t, fps, &segment);
  1803. if(!stss) return S_FALSE;
  1804. // clear any cached subs not in the range of +/-30secs measured from the segment's bounds
  1805. {
  1806. POSITION pos = m_subtitleCache.GetStartPosition();
  1807. while(pos)
  1808. {
  1809. int key;
  1810. CSubtitle* value;
  1811. m_subtitleCache.GetNextAssoc(pos, key, value);
  1812. STSEntry& stse = GetAt(key);
  1813. if(stse.end <= (t-30000) || stse.start > (t+30000)) 
  1814. {
  1815. delete value;
  1816. m_subtitleCache.RemoveKey(key);
  1817. pos = m_subtitleCache.GetStartPosition();
  1818. }
  1819. }
  1820. }
  1821. m_sla.AdvanceToSegment(segment, stss->subs);
  1822. CArray<LSub> subs;
  1823. for(int i = 0, j = stss->subs.GetSize(); i < j; i++)
  1824. {
  1825. LSub ls;
  1826. ls.idx = stss->subs[i];
  1827. ls.layer = GetAt(stss->subs[i]).layer;
  1828. ls.readorder = GetAt(stss->subs[i]).readorder;
  1829. subs.Add(ls);
  1830. }
  1831. qsort(subs.GetData(), subs.GetSize(), sizeof(LSub), lscomp);
  1832. for(int i = 0, j = subs.GetSize(); i < j; i++)
  1833. {
  1834. int entry = subs[i].idx;
  1835. STSEntry stse = GetAt(entry);
  1836. {
  1837. int start = TranslateStart(entry, fps);
  1838. m_time = t - start;
  1839. m_delay = TranslateEnd(entry, fps) - start;
  1840. }
  1841. CSubtitle* s = GetSubtitle(entry);
  1842. if(!s) continue;
  1843. CRect clipRect = s->m_clip;
  1844. CRect r = s->m_rect;
  1845. CSize spaceNeeded = r.Size();
  1846. // apply the effects
  1847. bool fPosOverride = false, fOrgOverride = false;
  1848. int alpha = 0x00;
  1849. CPoint org2;
  1850. for(int k = 0; k < EF_NUMBEROFEFFECTS; k++)
  1851. {
  1852. if(!s->m_effects[k]) continue;
  1853. switch(k)
  1854. {
  1855. case EF_MOVE: // {move(x1=param[0], y1=param[1], x2=param[2], y2=param[3], t1=t[0], t2=t[1])}
  1856. {
  1857. CPoint p;
  1858. CPoint p1(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
  1859. CPoint p2(s->m_effects[k]->param[2], s->m_effects[k]->param[3]);
  1860. int t1 = s->m_effects[k]->t[0];
  1861. int t2 = s->m_effects[k]->t[1];
  1862. if(t2 < t1) {int t = t1; t1 = t2; t2 = t;}
  1863. if(t1 <= 0 && t2 <= 0) {t1 = 0; t2 = m_delay;}
  1864. if(m_time <= t1) p = p1;
  1865. else if(t1 < m_time && m_time < t2)
  1866. {
  1867. double t = 1.0*(m_time-t1)/(t2-t1);
  1868. p.x = (int)((1-t)*p1.x + t*p2.x);
  1869. p.y = (int)((1-t)*p1.y + t*p2.y);
  1870. }
  1871. else p = p2;
  1872. r = CRect(
  1873. CPoint((s->m_scrAlignment%3) == 1 ? p.x : (s->m_scrAlignment%3) == 0 ? p.x - spaceNeeded.cx : p.x - (spaceNeeded.cx+1)/2,
  1874. s->m_scrAlignment <= 3 ? p.y - spaceNeeded.cy : s->m_scrAlignment <= 6 ? p.y - (spaceNeeded.cy+1)/2 : p.y),
  1875. spaceNeeded);
  1876. if(s->m_relativeTo == 1)
  1877. r.OffsetRect(m_vidrect.TopLeft());
  1878. fPosOverride = true;
  1879. }
  1880. break;
  1881. case EF_ORG: // {org(x=param[0], y=param[1])}
  1882. {
  1883. org2 = CPoint(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
  1884. fOrgOverride = true;
  1885. }
  1886. break;
  1887. 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])
  1888. {
  1889. int t1 = s->m_effects[k]->t[0];
  1890. int t2 = s->m_effects[k]->t[1];
  1891. int t3 = s->m_effects[k]->t[2];
  1892. int t4 = s->m_effects[k]->t[3];
  1893. if(t1 == -1 && t4 == -1) {t1 = 0; t3 = m_delay-t3; t4 = m_delay;}
  1894. if(m_time < t1) alpha = s->m_effects[k]->param[0];
  1895. else if(m_time >= t1 && m_time < t2)
  1896. {
  1897. double t = 1.0 * (m_time - t1) / (t2 - t1);
  1898. alpha = (int)(s->m_effects[k]->param[0]*(1-t) + s->m_effects[k]->param[1]*t);
  1899. }
  1900. else if(m_time >= t2 && m_time < t3) alpha = s->m_effects[k]->param[1];
  1901. else if(m_time >= t3 && m_time < t4)
  1902. {
  1903. double t = 1.0 * (m_time - t3) / (t4 - t3);
  1904. alpha = (int)(s->m_effects[k]->param[1]*(1-t) + s->m_effects[k]->param[2]*t);
  1905. }
  1906. else if(m_time >= t4) alpha = s->m_effects[k]->param[2];
  1907. }
  1908. break;
  1909. case EF_BANNER: // Banner;delay=param[0][;leftoright=param[1];fadeawaywidth=param[2]]
  1910. {
  1911. int left = s->m_relativeTo == 1 ? m_vidrect.left : 0, 
  1912. right = s->m_relativeTo == 1 ? m_vidrect.right : m_size.cx;
  1913. r.left = !!s->m_effects[k]->param[1] 
  1914. ? (left/*marginRect.left*/ - spaceNeeded.cx) + (int)(m_time*8.0/s->m_effects[k]->param[0])
  1915. : (right /*- marginRect.right*/) - (int)(m_time*8.0/s->m_effects[k]->param[0]);
  1916. r.right = r.left + spaceNeeded.cx;
  1917. clipRect &= CRect(left>>3, clipRect.top, right>>3, clipRect.bottom);
  1918. fPosOverride = true;
  1919. }
  1920. break;
  1921. case EF_SCROLL: // Scroll up/down(toptobottom=param[3]);top=param[0];bottom=param[1];delay=param[2][;fadeawayheight=param[4]]
  1922. {
  1923. r.top = !!s->m_effects[k]->param[3]
  1924. ? s->m_effects[k]->param[0] + (int)(m_time*8.0/s->m_effects[k]->param[2]) - spaceNeeded.cy
  1925. : s->m_effects[k]->param[1] - (int)(m_time*8.0/s->m_effects[k]->param[2]);
  1926. r.bottom = r.top + spaceNeeded.cy;
  1927. CRect cr(0, (s->m_effects[k]->param[0] + 4) >> 3, spd.w, (s->m_effects[k]->param[1] + 4) >> 3);
  1928. if(s->m_relativeTo == 1)
  1929. r.top += m_vidrect.top, 
  1930. r.bottom += m_vidrect.top, 
  1931. cr.top += m_vidrect.top>>3, 
  1932. cr.bottom += m_vidrect.top>>3;
  1933. clipRect &= cr;
  1934. fPosOverride = true;
  1935. }
  1936. break;
  1937. default:
  1938. break;
  1939. }
  1940. }
  1941. if(!fPosOverride && !fOrgOverride && !s->m_fAnimated) 
  1942. r = m_sla.AllocRect(s, segment, entry, stse.layer, m_collisions);
  1943. CPoint org;
  1944. org.x = (s->m_scrAlignment%3) == 1 ? r.left : (s->m_scrAlignment%3) == 2 ? r.CenterPoint().x : r.right;
  1945. org.y = s->m_scrAlignment <= 3 ? r.bottom : s->m_scrAlignment <= 6 ? r.CenterPoint().y : r.top;
  1946. if(!fOrgOverride) org2 = org;
  1947. BYTE* pAlphaMask = s->m_pClipper?s->m_pClipper->m_pAlphaMask:NULL;
  1948. CPoint p, p2(0, r.top);
  1949. POSITION pos;
  1950. p = p2;
  1951. pos = s->GetHeadPosition();
  1952. while(pos) 
  1953. {
  1954. CLine* l = s->GetNext(pos);
  1955. p.x = (s->m_scrAlignment%3) == 1 ? org.x
  1956. : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
  1957. :    org.x - (l->m_width/2);
  1958. bbox2 |= l->PaintShadow(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
  1959. p.y += l->m_ascent + l->m_descent;
  1960. }
  1961. p = p2;
  1962. pos = s->GetHeadPosition();
  1963. while(pos) 
  1964. {
  1965. CLine* l = s->GetNext(pos);
  1966. p.x = (s->m_scrAlignment%3) == 1 ? org.x
  1967. : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
  1968. :    org.x - (l->m_width/2);
  1969. bbox2 |= l->PaintOutline(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
  1970. p.y += l->m_ascent + l->m_descent;
  1971. }
  1972. p = p2;
  1973. pos = s->GetHeadPosition();
  1974. while(pos) 
  1975. {
  1976. CLine* l = s->GetNext(pos);
  1977. p.x = (s->m_scrAlignment%3) == 1 ? org.x
  1978. : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
  1979. :    org.x - (l->m_width/2);
  1980. bbox2 |= l->PaintBody(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
  1981. p.y += l->m_ascent + l->m_descent;
  1982. }
  1983. }
  1984. bbox = bbox2;
  1985. return (subs.GetSize() && !bbox2.IsRectEmpty()) ? S_OK : S_FALSE;
  1986. }
  1987. // IPersist
  1988. STDMETHODIMP CRenderedTextSubtitle::GetClassID(CLSID* pClassID)
  1989. {
  1990. return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
  1991. }
  1992. // ISubStream
  1993. STDMETHODIMP_(int) CRenderedTextSubtitle::GetStreamCount()
  1994. {
  1995. return(1);
  1996. }
  1997. STDMETHODIMP CRenderedTextSubtitle::GetStreamInfo(int iStream, WCHAR** ppName, LCID* pLCID)
  1998. {
  1999. if(iStream != 0) return E_INVALIDARG;
  2000. if(ppName)
  2001. {
  2002. if(!(*ppName = (WCHAR*)CoTaskMemAlloc((m_name.GetLength()+1)*sizeof(WCHAR))))
  2003. return E_OUTOFMEMORY;
  2004. wcscpy(*ppName, CStringW(m_name));
  2005. }
  2006. if(pLCID)
  2007. {
  2008. *pLCID = 0; // TODO
  2009. }
  2010. return S_OK;
  2011. }
  2012. STDMETHODIMP_(int) CRenderedTextSubtitle::GetStream()
  2013. {
  2014. return(0);
  2015. }
  2016. STDMETHODIMP CRenderedTextSubtitle::SetStream(int iStream)
  2017. {
  2018. return iStream == 0 ? S_OK : E_FAIL;
  2019. }
  2020. STDMETHODIMP CRenderedTextSubtitle::Reload()
  2021. {
  2022. CFileStatus s;
  2023. if(!CFile::GetStatus(m_path, s)) return E_FAIL;
  2024. return !m_path.IsEmpty() && Open(m_path, DEFAULT_CHARSET) ? S_OK : E_FAIL;
  2025. }