add a test file
[xy_vsfilter.git] / src / subtitles / RTS.cpp
blob56845403b4ca9348591017552b317f73129b6213
1 /*
2 * Copyright (C) 2003-2006 Gabest
3 * http://www.gabest.org
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.
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.
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
22 #include "stdafx.h"
23 #include <math.h>
24 #include <time.h>
25 #include "RTS.h"
26 #include "../SubPic/MemSubPic.h"
27 #include "../subpic/color_conv_table.h"
29 // WARNING: this isn't very thread safe, use only one RTS a time.
30 static HDC g_hDC;
31 static int g_hDC_refcnt = 0;
33 //static OverlayCache g_overlay_cache;
34 const int OVERLAY_CACHE_ITEM_NUM = 256;
35 const int WORD_CACHE_ITEM_NUM = 512;
36 static OverlayMruCache g_overlay_cache(OVERLAY_CACHE_ITEM_NUM);
37 static CWordMruCache g_word_cache(WORD_CACHE_ITEM_NUM);
40 std::size_t hash_value(const CWord& key)
42 return( CStringElementTraits<CString>::Hash(key.m_str.get()) );
45 std::size_t hash_value(const CWordCacheKey& key)
47 return( CStringElementTraits<CString>::Hash(key.m_str.get()) );
50 std::size_t hash_value(const OverlayKey& key)
52 return( CStringElementTraits<CString>::Hash(key.m_str.get()) ^ key.m_p.x ^ key.m_p.y );
55 enum XY_MSP_SUBTYPE {XY_AYUV, XY_AUYV};
56 static inline DWORD rgb2yuv(DWORD argb, XY_MSP_SUBTYPE type)
58 const ColorConvTable* color_conv_table = ColorConvTable::GetDefaultColorConvTable();
59 DWORD axxv;
60 int r = (argb & 0x00ff0000) >> 16;
61 int g = (argb & 0x0000ff00) >> 8;
62 int b = (argb & 0x000000ff);
63 int y = (color_conv_table->c2y_cyb * b + color_conv_table->c2y_cyg * g + color_conv_table->c2y_cyr * r + 0x108000) >> 16;
64 int scaled_y = (y-16) * color_conv_table->cy_cy;
65 int u = ((((b<<16) - scaled_y) >> 10) * color_conv_table->c2y_cu + 0x800000 + 0x8000) >> 16;
66 int v = ((((r<<16) - scaled_y) >> 10) * color_conv_table->c2y_cv + 0x800000 + 0x8000) >> 16;
67 DbgLog((LOG_TRACE, 5, TEXT("argb=%x r=%d %x g=%d %x b=%d %x y=%d %x u=%d %x v=%d %x"), argb, r, r, g, g, b, b, y, y, u, u, v, v));
68 u *= (u>0);
69 u = 255 - (255-u)*(u<256);
70 v *= (v>0);
71 v = 255 - (255-v)*(v<256);
72 DbgLog((LOG_TRACE, 5, TEXT("u=%x v=%x"), u, v));
73 if(type==XY_AYUV)
74 axxv = (argb & 0xff000000) | (y<<16) | (u<<8) | v;
75 else
76 axxv = (argb & 0xff000000) | (y<<8) | (u<<16) | v;
77 DbgLog((LOG_TRACE, 5, TEXT("axxv=%x"), axxv));
78 return axxv;
79 //return argb;
81 static long revcolor(long c)
83 return ((c&0xff0000)>>16) + (c&0xff00) + ((c&0xff)<<16);
86 //////////////////////////////////////////////////////////////////////////////////////////////
88 // CMyFont
90 CMyFont::CMyFont(const FwSTSStyle& style)
92 LOGFONT lf;
93 memset(&lf, 0, sizeof(lf));
94 lf <<= style.get();
95 lf.lfHeight = (LONG)(style.get().fontSize+0.5);
96 lf.lfOutPrecision = OUT_TT_PRECIS;
97 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
98 lf.lfQuality = ANTIALIASED_QUALITY;
99 lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
100 if(!CreateFontIndirect(&lf))
102 _tcscpy(lf.lfFaceName, _T("Arial"));
103 CreateFontIndirect(&lf);
105 HFONT hOldFont = SelectFont(g_hDC, *this);
106 TEXTMETRIC tm;
107 GetTextMetrics(g_hDC, &tm);
108 m_ascent = ((tm.tmAscent + 4) >> 3);
109 m_descent = ((tm.tmDescent + 4) >> 3);
110 SelectFont(g_hDC, hOldFont);
113 // CWord
115 CWord::CWord(const STSStyle& style, const CStringW& str, int ktype, int kstart, int kend)
116 : m_style(style), m_str(str)
117 , m_width(0), m_ascent(0), m_descent(0)
118 , m_ktype(ktype), m_kstart(kstart), m_kend(kend)
119 , m_fDrawn(false), m_p(INT_MAX, INT_MAX)
120 , m_fLineBreak(false), m_fWhiteSpaceChar(false)
121 , m_pOpaqueBox(NULL)
123 if(m_str.get().IsEmpty())
125 m_fWhiteSpaceChar = m_fLineBreak = true;
127 FwCMyFont font(m_style);
128 m_ascent = (int)(m_style.get().fontScaleY/100*font.get().m_ascent);
129 m_descent = (int)(m_style.get().fontScaleY/100*font.get().m_descent);
130 m_width = 0;
133 CWord::~CWord()
135 if(m_pOpaqueBox) delete m_pOpaqueBox;
138 bool CWord::Append(CWord* w)
140 if(!(m_style == w->m_style)
141 || m_fLineBreak || w->m_fLineBreak
142 || w->m_kstart != w->m_kend || m_ktype != w->m_ktype) return(false);
143 m_fWhiteSpaceChar = m_fWhiteSpaceChar && w->m_fWhiteSpaceChar;
144 CStringW temp = m_str.get();
145 temp += w->m_str.get();
146 m_str = temp;
147 m_width += w->m_width;
148 m_fDrawn = false;
149 m_p = CPoint(INT_MAX, INT_MAX);
150 return(true);
153 void CWord::Paint(CPoint p, CPoint org, OverlayList* overlay_list)
155 if(!m_str.get() || overlay_list==NULL) return;
157 OverlayCompatibleKey::CompKey comp_key(this, org-p);
158 bool need_transform = NeedTransform();
159 if(!need_transform)
161 comp_key.p.SetPoint(0,0);
163 const OverlayMruCache::hashed_cache& overlay_cache = g_overlay_cache.get_hashed_cache();
164 const OverlayMruCache::hashed_cache::iterator iter = overlay_cache.find(comp_key,OverlayCompatibleKey(),OverlayCompatibleKey());
165 if(iter==overlay_cache.end())
167 overlay_list->overlay = new Overlay();
168 if(!m_fDrawn)
170 if(!CreatePath()) return;
171 if(need_transform)
172 Transform(CPoint((org.x-p.x)*8, (org.y-p.y)*8));
173 if(!ScanConvert()) return;
174 if(m_style.get().borderStyle == 0 && (m_style.get().outlineWidthX+m_style.get().outlineWidthY > 0))
176 if(!CreateWidenedRegion((int)(m_style.get().outlineWidthX+0.5), (int)(m_style.get().outlineWidthY+0.5))) return;
178 else if(m_style.get().borderStyle == 1)
180 if(!CreateOpaqueBox()) return;
182 m_fDrawn = true;
183 //if(!Rasterize(p.x&7, p.y&7, m_style.get().fBlur, m_style.get().fGaussianBlur, overlay_list->overlay)) return;
184 if(!Rasterize(0, 0, m_style.get().fBlur, m_style.get().fGaussianBlur, overlay_list->overlay)) return;
186 else
188 //Rasterize(p.x&7, p.y&7, m_style.get().fBlur, m_style.get().fGaussianBlur, overlay_list->overlay);
189 Rasterize(0, 0, m_style.get().fBlur, m_style.get().fGaussianBlur, overlay_list->overlay);
191 OverlayMruItem item(comp_key, overlay_list->overlay);
192 g_overlay_cache.update_cache(item);
194 else
196 overlay_list->overlay = iter->overlay;
198 m_p = p;
199 if(m_pOpaqueBox)
201 overlay_list->next = new OverlayList();
202 m_pOpaqueBox->Paint(p, org, overlay_list->next);
205 bool CWord::NeedTransform()
207 return (fabs(m_style.get().fontScaleX - 100) > 0.000001) ||
208 (fabs(m_style.get().fontScaleY - 100) > 0.000001) ||
209 (fabs(m_style.get().fontAngleX) > 0.000001) ||
210 (fabs(m_style.get().fontAngleY) > 0.000001) ||
211 (fabs(m_style.get().fontAngleZ) > 0.000001) ||
212 (fabs(m_style.get().fontShiftX) > 0.000001) ||
213 (fabs(m_style.get().fontShiftY) > 0.000001);
216 void CWord::Transform(CPoint org)
218 #ifdef _VSMOD
219 // CPUID from VDub
220 bool fSSE2 = !!(g_cpuid.m_flags & CCpuID::sse2);
222 if(fSSE2) { // SSE code
223 Transform_SSE2(org);
224 } else // C-code
225 #endif
226 Transform_C(org);
229 void CWord::Transform_C( CPoint &org )
231 double scalex = m_style.get().fontScaleX/100;
232 double scaley = m_style.get().fontScaleY/100;
234 double caz = cos((3.1415/180)*m_style.get().fontAngleZ);
235 double saz = sin((3.1415/180)*m_style.get().fontAngleZ);
236 double cax = cos((3.1415/180)*m_style.get().fontAngleX);
237 double sax = sin((3.1415/180)*m_style.get().fontAngleX);
238 double cay = cos((3.1415/180)*m_style.get().fontAngleY);
239 double say = sin((3.1415/180)*m_style.get().fontAngleY);
241 #ifdef _VSMOD
242 // patch m003. random text points
243 double xrnd = m_style.get().mod_rand.X*100;
244 double yrnd = m_style.get().mod_rand.Y*100;
245 double zrnd = m_style.get().mod_rand.Z*100;
247 srand(m_style.get().mod_rand.Seed);
249 // patch m008. distort
250 int xsz,ysz;
251 double dst1x,dst1y,dst2x,dst2y,dst3x,dst3y;
252 int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
254 bool is_dist = m_style.get().mod_distort.enabled;
255 if (is_dist) {
256 for(int i = 0; i < mPathPoints; i++) {
257 if(minx > mpPathPoints[i].x) {
258 minx = mpPathPoints[i].x;
260 if(miny > mpPathPoints[i].y) {
261 miny = mpPathPoints[i].y;
263 if(maxx < mpPathPoints[i].x) {
264 maxx = mpPathPoints[i].x;
266 if(maxy < mpPathPoints[i].y) {
267 maxy = mpPathPoints[i].y;
271 xsz = max(maxx - minx, 0);
272 ysz = max(maxy - miny, 0);
274 dst1x = m_style.get().mod_distort.pointsx[0];
275 dst1y = m_style.get().mod_distort.pointsy[0];
276 dst2x = m_style.get().mod_distort.pointsx[1];
277 dst2y = m_style.get().mod_distort.pointsy[1];
278 dst3x = m_style.get().mod_distort.pointsx[2];
279 dst3y = m_style.get().mod_distort.pointsy[2];
281 #endif
283 for (size_t i = 0; i < mPathPoints; i++) {
284 double x, y, z, xx, yy, zz;
286 x = mpPathPoints[i].x;
287 y = mpPathPoints[i].y;
288 #ifdef _VSMOD
289 // patch m002. Z-coord
290 z = m_style.get().mod_z;
292 double u, v;
293 if (is_dist) {
294 u = (x-minx) / xsz;
295 v = (y-miny) / ysz;
297 x = minx+(0 + (dst1x - 0)*u + (dst3x-0)*v+(0+dst2x-dst1x-dst3x)*u*v)*xsz;
298 y = miny+(0 + (dst1y - 0)*u + (dst3y-0)*v+(0+dst2y-dst1y-dst3y)*u*v)*ysz;
299 //P = P0 + (P1 - P0)u + (P3 - P0)v + (P0 + P2 - P1 - P3)uv
302 // patch m003. random text points
303 x = xrnd > 0 ? (xrnd - rand() % (int)(xrnd * 2 + 1)) / 100.0 + x : x;
304 y = yrnd > 0 ? (yrnd - rand() % (int)(yrnd * 2 + 1)) / 100.0 + y : y;
305 z = zrnd > 0 ? (zrnd - rand() % (int)(zrnd * 2 + 1)) / 100.0 + z : z;
306 #else
307 z = 0;
308 #endif
309 double _x = x;
310 x = scalex * (x + m_style.get().fontShiftX * y) - org.x;
311 y = scaley * (y + m_style.get().fontShiftY * _x) - org.y;
313 xx = x*caz + y*saz;
314 yy = -(x*saz - y*caz);
315 zz = z;
317 x = xx;
318 y = yy*cax + zz*sax;
319 z = yy*sax - zz*cax;
321 xx = x*cay + z*say;
322 yy = y;
323 zz = x*say - z*cay;
325 zz = max(zz, -19000);
327 x = (xx * 20000) / (zz + 20000);
328 y = (yy * 20000) / (zz + 20000);
330 mpPathPoints[i].x = (LONG)(x + org.x + 0.5);
331 mpPathPoints[i].y = (LONG)(y + org.y + 0.5);
335 void CWord::Transform_SSE2( CPoint &org )
337 // __m128 union data type currently not supported with Intel C++ Compiler, so just call C version
338 #ifdef __ICL
339 Transform_C(org);
340 #else
341 // SSE code
342 // speed up ~1.5-1.7x
343 double scalex = m_style.get().fontScaleX/100;
344 double scaley = m_style.get().fontScaleY/100;
346 double caz = cos((3.1415/180)*m_style.get().fontAngleZ);
347 double saz = sin((3.1415/180)*m_style.get().fontAngleZ);
348 double cax = cos((3.1415/180)*m_style.get().fontAngleX);
349 double sax = sin((3.1415/180)*m_style.get().fontAngleX);
350 double cay = cos((3.1415/180)*m_style.get().fontAngleY);
351 double say = sin((3.1415/180)*m_style.get().fontAngleY);
353 __m128 __xshift = _mm_set_ps1(m_style.get().fontShiftX);
354 __m128 __yshift = _mm_set_ps1(m_style.get().fontShiftY);
356 __m128 __xorg = _mm_set_ps1(org.x);
357 __m128 __yorg = _mm_set_ps1(org.y);
359 __m128 __xscale = _mm_set_ps1(scalex);
360 __m128 __yscale = _mm_set_ps1(scaley);
362 #ifdef _VSMOD
363 // patch m003. random text points
364 double xrnd = m_style.get().mod_rand.X*100;
365 double yrnd = m_style.get().mod_rand.Y*100;
366 double zrnd = m_style.get().mod_rand.Z*100;
368 srand(m_style.get().mod_rand.Seed);
370 __m128 __xsz = _mm_setzero_ps();
371 __m128 __ysz = _mm_setzero_ps();
373 __m128 __dst1x, __dst1y, __dst213x, __dst213y, __dst3x, __dst3y;
375 __m128 __miny;
376 __m128 __minx = _mm_set_ps(INT_MAX, INT_MAX, 0, 0);
377 __m128 __max = _mm_set_ps(-INT_MAX, -INT_MAX, 1, 1);
379 bool is_dist = m_style.get().mod_distort.enabled;
380 if(is_dist) {
381 for(int i = 0; i < mPathPoints; i++) {
382 __m128 __point = _mm_set_ps(mpPathPoints[i].x, mpPathPoints[i].y, 0, 0);
383 __minx = _mm_min_ps(__minx, __point);
384 __max = _mm_max_ps(__max, __point);
387 __m128 __zero = _mm_setzero_ps();
388 __max = _mm_sub_ps(__max, __minx); // xsz, ysz, 1, 1
389 __max = _mm_max_ps(__max, __zero);
391 __xsz = _mm_shuffle_ps(__max, __max, _MM_SHUFFLE(3,3,3,3));
392 __ysz = _mm_shuffle_ps(__max, __max, _MM_SHUFFLE(2,2,2,2));
394 __miny = _mm_shuffle_ps(__minx, __minx, _MM_SHUFFLE(2,2,2,2));
395 __minx = _mm_shuffle_ps(__minx, __minx, _MM_SHUFFLE(3,3,3,3));
397 __dst1x = _mm_set_ps1(m_style.get().mod_distort.pointsx[0]);
398 __dst1y = _mm_set_ps1(m_style.get().mod_distort.pointsy[0]);
399 __dst3x = _mm_set_ps1(m_style.get().mod_distort.pointsx[2]);
400 __dst3y = _mm_set_ps1(m_style.get().mod_distort.pointsy[2]);
401 __dst213x = _mm_set_ps1(m_style.get().mod_distort.pointsx[1]); // 2 - 1 - 3
402 __dst213x = _mm_sub_ps(__dst213x, __dst1x);
403 __dst213x = _mm_sub_ps(__dst213x, __dst3x);
405 __dst213y = _mm_set_ps1(m_style.get().mod_distort.pointsy[1]);
406 __dst213x = _mm_sub_ps(__dst213y, __dst1y);
407 __dst213x = _mm_sub_ps(__dst213y, __dst3y);
409 #endif
411 __m128 __caz = _mm_set_ps1(caz);
412 __m128 __saz = _mm_set_ps1(saz);
413 __m128 __cax = _mm_set_ps1(cax);
414 __m128 __sax = _mm_set_ps1(sax);
415 __m128 __cay = _mm_set_ps1(cay);
416 __m128 __say = _mm_set_ps1(say);
418 // this can be paralleled for openmp
419 int mPathPointsD4 = mPathPoints / 4;
420 int mPathPointsM4 = mPathPoints % 4;
422 for(ptrdiff_t i = 0; i < mPathPointsD4 + 1; i++) {
423 __m128 __pointx, __pointy;
424 // we can't use load .-.
425 if(i != mPathPointsD4) {
426 __pointx = _mm_set_ps(mpPathPoints[4 * i + 0].x, mpPathPoints[4 * i + 1].x, mpPathPoints[4 * i + 2].x, mpPathPoints[4 * i + 3].x);
427 __pointy = _mm_set_ps(mpPathPoints[4 * i + 0].y, mpPathPoints[4 * i + 1].y, mpPathPoints[4 * i + 2].y, mpPathPoints[4 * i + 3].y);
428 } else { // last cycle
429 switch(mPathPointsM4) {
430 default:
431 case 0:
432 continue;
433 case 1:
434 __pointx = _mm_set_ps(mpPathPoints[4 * i + 0].x, 0, 0, 0);
435 __pointy = _mm_set_ps(mpPathPoints[4 * i + 0].y, 0, 0, 0);
436 break;
437 case 2:
438 __pointx = _mm_set_ps(mpPathPoints[4 * i + 0].x, mpPathPoints[4 * i + 1].x, 0, 0);
439 __pointy = _mm_set_ps(mpPathPoints[4 * i + 0].y, mpPathPoints[4 * i + 1].y, 0, 0);
440 break;
441 case 3:
442 __pointx = _mm_set_ps(mpPathPoints[4 * i + 0].x, mpPathPoints[4 * i + 1].x, mpPathPoints[4 * i + 2].x, 0);
443 __pointy = _mm_set_ps(mpPathPoints[4 * i + 0].y, mpPathPoints[4 * i + 1].y, mpPathPoints[4 * i + 2].y, 0);
444 break;
448 #ifdef _VSMOD
449 __m128 __pointz = _mm_set_ps1(m_style.get().mod_z);
451 // distort
452 if(is_dist) {
453 //P = P0 + (P1 - P0)u + (P3 - P0)v + (P0 + P2 - P1 - P3)uv
454 __m128 __u = _mm_sub_ps(__pointx, __minx);
455 __m128 __v = _mm_sub_ps(__pointy, __miny);
456 __m128 __1_xsz = _mm_rcp_ps(__xsz);
457 __m128 __1_ysz = _mm_rcp_ps(__ysz);
458 __u = _mm_mul_ps(__u, __1_xsz);
459 __v = _mm_mul_ps(__v, __1_ysz);
461 // x
462 __pointx = _mm_mul_ps(__dst213x, __u);
463 __pointx = _mm_mul_ps(__pointx, __v);
465 __m128 __tmpx = _mm_mul_ps(__dst3x, __v);
466 __pointx = _mm_add_ps(__pointx, __tmpx);
467 __tmpx = _mm_mul_ps(__dst1x, __u);
468 __pointx = _mm_add_ps(__pointx, __tmpx);
470 __pointx = _mm_mul_ps(__pointx, __xsz);
471 __pointx = _mm_add_ps(__pointx, __minx);
473 // y
474 __pointy = _mm_mul_ps(__dst213y, __u);
475 __pointy = _mm_mul_ps(__pointy, __v);
477 __m128 __tmpy = _mm_mul_ps(__dst3y, __v);
478 __pointy = _mm_add_ps(__pointy, __tmpy);
479 __tmpy = _mm_mul_ps(__dst1y, __u);
480 __pointy = _mm_add_ps(__pointy, __tmpy);
482 __pointy = _mm_mul_ps(__pointy, __ysz);
483 __pointy = _mm_add_ps(__pointy, __miny);
486 // randomize
487 if(xrnd!=0 || yrnd!=0 || zrnd!=0) {
488 __declspec(align(16)) float rx[4], ry[4], rz[4];
489 for(int k=0; k<4; k++) {
490 rx[k] = xrnd > 0 ? (xrnd - rand() % (int)(xrnd * 2 + 1)) : 0;
491 ry[k] = yrnd > 0 ? (yrnd - rand() % (int)(yrnd * 2 + 1)) : 0;
492 rz[k] = zrnd > 0 ? (zrnd - rand() % (int)(zrnd * 2 + 1)) : 0;
494 __m128 __001 = _mm_set_ps1(0.01f);
496 if(xrnd!=0) {
497 __m128 __rx = _mm_load_ps(rx);
498 __rx = _mm_mul_ps(__rx, __001);
499 __pointx = _mm_add_ps(__pointx, __rx);
502 if(yrnd!=0) {
503 __m128 __ry = _mm_load_ps(ry);
504 __ry = _mm_mul_ps(__ry, __001);
505 __pointy = _mm_add_ps(__pointy, __ry);
508 if(zrnd!=0) {
509 __m128 __rz = _mm_load_ps(rz);
510 __rz = _mm_mul_ps(__rz, __001);
511 __pointz = _mm_add_ps(__pointz, __rz);
514 #else
515 __m128 __pointz = _mm_set_ps1(0);
516 #endif
518 // scale and shift
519 __m128 __tmpx;
520 if(m_style.get().fontShiftX!=0) {
521 __tmpx = _mm_mul_ps(__xshift, __pointy);
522 __tmpx = _mm_add_ps(__tmpx, __pointx);
523 } else {
524 __tmpx = __pointx;
526 __tmpx = _mm_mul_ps(__tmpx, __xscale);
527 __tmpx = _mm_sub_ps(__tmpx, __xorg);
529 __m128 __tmpy;
530 if(m_style.get().fontShiftY!=0) {
531 __tmpy = _mm_mul_ps(__yshift, __pointx);
532 __tmpy = _mm_add_ps(__tmpy, __pointy);
533 } else {
534 __tmpy = __pointy;
536 __tmpy = _mm_mul_ps(__tmpy, __yscale);
537 __tmpy = _mm_sub_ps(__tmpy, __yorg);
539 // rotate
540 __m128 __xx = _mm_mul_ps(__tmpx, __caz);
541 __m128 __yy = _mm_mul_ps(__tmpy, __saz);
542 __pointx = _mm_add_ps(__xx, __yy);
543 __xx = _mm_mul_ps(__tmpx, __saz);
544 __yy = _mm_mul_ps(__tmpy, __caz);
545 __pointy = _mm_sub_ps(__yy, __xx);
547 __m128 __zz = _mm_mul_ps(__pointz, __sax);
548 __yy = _mm_mul_ps(__pointy, __cax);
549 __pointy = _mm_add_ps(__yy, __zz);
550 __zz = _mm_mul_ps(__pointz, __cax);
551 __yy = _mm_mul_ps(__pointy, __sax);
552 __pointz = _mm_sub_ps(__zz, __yy);
554 __xx = _mm_mul_ps(__pointx, __cay);
555 __zz = _mm_mul_ps(__pointz, __say);
556 __pointx = _mm_add_ps(__xx, __zz);
557 __xx = _mm_mul_ps(__pointx, __say);
558 __zz = _mm_mul_ps(__pointz, __cay);
559 __pointz = _mm_sub_ps(__xx, __zz);
561 __zz = _mm_set_ps1(-19000);
562 __pointz = _mm_max_ps(__pointz, __zz);
564 __m128 __20000 = _mm_set_ps1(20000);
565 __zz = _mm_add_ps(__pointz, __20000);
566 __zz = _mm_rcp_ps(__zz);
568 __pointx = _mm_mul_ps(__pointx, __20000);
569 __pointx = _mm_mul_ps(__pointx, __zz);
571 __pointy = _mm_mul_ps(__pointy, __20000);
572 __pointy = _mm_mul_ps(__pointy, __zz);
574 __pointx = _mm_add_ps(__pointx, __xorg);
575 __pointy = _mm_add_ps(__pointy, __yorg);
577 __m128 __05 = _mm_set_ps1(0.5);
579 __pointx = _mm_add_ps(__pointx, __05);
580 __pointy = _mm_add_ps(__pointy, __05);
582 if(i == mPathPointsD4) { // last cycle
583 for(int k=0; k<mPathPointsM4; k++) {
584 mpPathPoints[i*4+k].x = static_cast<LONG>(__pointx.m128_f32[3-k]);
585 mpPathPoints[i*4+k].y = static_cast<LONG>(__pointy.m128_f32[3-k]);
587 } else {
588 for(int k=0; k<4; k++) {
589 mpPathPoints[i*4+k].x = static_cast<LONG>(__pointx.m128_f32[3-k]);
590 mpPathPoints[i*4+k].y = static_cast<LONG>(__pointy.m128_f32[3-k]);
594 #endif // __ICL
597 bool CWord::CreateOpaqueBox()
599 if(m_pOpaqueBox) return(true);
600 STSStyle style = m_style.get();
601 style.borderStyle = 0;
602 style.outlineWidthX = style.outlineWidthY = 0;
603 style.colors[0] = m_style.get().colors[2];
604 style.alpha[0] = m_style.get().alpha[2];
605 int w = (int)(m_style.get().outlineWidthX + 0.5);
606 int h = (int)(m_style.get().outlineWidthY + 0.5);
607 CStringW str;
608 str.Format(L"m %d %d l %d %d %d %d %d %d",
609 -w, -h,
610 m_width+w, -h,
611 m_width+w, m_ascent+m_descent+h,
612 -w, m_ascent+m_descent+h);
613 m_pOpaqueBox = new CPolygon(FwSTSStyle(style), str, 0, 0, 0, 1.0/8, 1.0/8, 0);
614 return(!!m_pOpaqueBox);
617 // CText
619 CText::CText(const STSStyle& style, const CStringW& str, int ktype, int kstart, int kend)
620 : CWord(style, str, ktype, kstart, kend)
622 if(m_str == L" ")
624 m_fWhiteSpaceChar = true;
626 FwCMyFont font(m_style);
627 HFONT hOldFont = SelectFont(g_hDC, font.get());
628 if(m_style.get().fontSpacing || (long)GetVersion() < 0)
630 bool bFirstPath = true;
631 for(LPCWSTR s = m_str.get(); *s; s++)
633 CSize extent;
634 if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
635 m_width += extent.cx + (int)m_style.get().fontSpacing;
637 // m_width -= (int)m_style.get().fontSpacing; // TODO: subtract only at the end of the line
639 else
641 CSize extent;
642 if(!GetTextExtentPoint32W(g_hDC, m_str.get(), wcslen(m_str.get()), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
643 m_width += extent.cx;
645 m_width = (int)(m_style.get().fontScaleX/100*m_width + 4) >> 3;
646 SelectFont(g_hDC, hOldFont);
649 CWord* CText::Copy()
651 return new CText(*this);
654 bool CText::Append(CWord* w)
656 return(dynamic_cast<CText*>(w) && CWord::Append(w));
659 bool CText::CreatePath()
661 FwCMyFont font(m_style);
662 HFONT hOldFont = SelectFont(g_hDC, font.get());
663 int width = 0;
664 if(m_style.get().fontSpacing || (long)GetVersion() < 0)
666 bool bFirstPath = true;
667 for(LPCWSTR s = m_str.get(); *s; s++)
669 CSize extent;
670 if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
671 PartialBeginPath(g_hDC, bFirstPath);
672 bFirstPath = false;
673 TextOutW(g_hDC, 0, 0, s, 1);
674 PartialEndPath(g_hDC, width, 0);
675 width += extent.cx + (int)m_style.get().fontSpacing;
678 else
680 CSize extent;
681 if(!GetTextExtentPoint32W(g_hDC, m_str.get(), m_str.get().GetLength(), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
682 BeginPath(g_hDC);
683 TextOutW(g_hDC, 0, 0, m_str.get(), m_str.get().GetLength());
684 EndPath(g_hDC);
686 SelectFont(g_hDC, hOldFont);
687 return(true);
690 // CPolygon
692 CPolygon::CPolygon(const FwSTSStyle& style, CStringW str, int ktype, int kstart, int kend, double scalex, double scaley, int baseline)
693 : CWord(style.get(), str, ktype, kstart, kend)
694 , m_scalex(scalex), m_scaley(scaley), m_baseline(baseline)
696 ParseStr();
699 CPolygon::CPolygon(CPolygon& src) : CWord(src.m_style, src.m_str.get(), src.m_ktype, src.m_kstart, src.m_kend)
701 m_scalex = src.m_scalex;
702 m_scaley = src.m_scaley;
703 m_baseline = src.m_baseline;
704 m_width = src.m_width;
705 m_ascent = src.m_ascent;
706 m_descent = src.m_descent;
707 m_pathTypesOrg.Copy(src.m_pathTypesOrg);
708 m_pathPointsOrg.Copy(src.m_pathPointsOrg);
710 CPolygon::~CPolygon()
714 CWord* CPolygon::Copy()
716 return(DNew CPolygon(*this));
719 bool CPolygon::Append(CWord* w)
721 int width = m_width;
722 CPolygon* p = dynamic_cast<CPolygon*>(w);
723 if(!p) return(false);
724 // TODO
725 return(false);
726 return(true);
729 bool CPolygon::GetLONG(CStringW& str, LONG& ret)
731 LPWSTR s = (LPWSTR)(LPCWSTR)str, e = s;
732 ret = wcstol(str, &e, 10);
733 str = str.Mid(e - s);
734 return(e > s);
737 bool CPolygon::GetPOINT(CStringW& str, POINT& ret)
739 return(GetLONG(str, ret.x) && GetLONG(str, ret.y));
742 bool CPolygon::ParseStr()
744 if(m_pathTypesOrg.GetCount() > 0) return(true);
745 CPoint p;
746 int i, j, lastsplinestart = -1, firstmoveto = -1, lastmoveto = -1;
747 CStringW str = m_str.get();
748 str.SpanIncluding(L"mnlbspc 0123456789");
749 str.Replace(L"m", L"*m");
750 str.Replace(L"n", L"*n");
751 str.Replace(L"l", L"*l");
752 str.Replace(L"b", L"*b");
753 str.Replace(L"s", L"*s");
754 str.Replace(L"p", L"*p");
755 str.Replace(L"c", L"*c");
756 int k = 0;
757 for(CStringW s = str.Tokenize(L"*", k); !s.IsEmpty(); s = str.Tokenize(L"*", k))
759 WCHAR c = s[0];
760 s.TrimLeft(L"mnlbspc ");
761 switch(c)
763 case 'm':
764 lastmoveto = m_pathTypesOrg.GetCount();
765 if(firstmoveto == -1) firstmoveto = lastmoveto;
766 while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_MOVETO); m_pathPointsOrg.Add(p);}
767 break;
768 case 'n':
769 while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_MOVETONC); m_pathPointsOrg.Add(p);}
770 break;
771 case 'l':
772 while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_LINETO); m_pathPointsOrg.Add(p);}
773 break;
774 case 'b':
775 j = m_pathTypesOrg.GetCount();
776 while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BEZIERTO); m_pathPointsOrg.Add(p); j++;}
777 j = m_pathTypesOrg.GetCount() - ((m_pathTypesOrg.GetCount()-j)%3);
778 m_pathTypesOrg.SetCount(j);
779 m_pathPointsOrg.SetCount(j);
780 break;
781 case 's':
782 j = lastsplinestart = m_pathTypesOrg.GetCount();
783 i = 3;
784 while(i-- && GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BSPLINETO); m_pathPointsOrg.Add(p); j++;}
785 if(m_pathTypesOrg.GetCount()-lastsplinestart < 3) {m_pathTypesOrg.SetCount(lastsplinestart); m_pathPointsOrg.SetCount(lastsplinestart); lastsplinestart = -1;}
786 // no break here
787 case 'p':
788 while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BSPLINEPATCHTO); m_pathPointsOrg.Add(p); j++;}
789 break;
790 case 'c':
791 if(lastsplinestart > 0)
793 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
794 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
795 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
796 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)
797 m_pathPointsOrg.Add(p);
798 p = m_pathPointsOrg[lastsplinestart];
799 m_pathPointsOrg.Add(p);
800 p = m_pathPointsOrg[lastsplinestart+1];
801 m_pathPointsOrg.Add(p);
802 lastsplinestart = -1;
804 break;
805 default:
806 break;
810 LPCWSTR str = m_str;
811 while(*str)
813 while(*str && *str != 'm' && *str != 'n' && *str != 'l' && *str != 'b' && *str != 's' && *str != 'p' && *str != 'c') str++;
815 if(!*str) break;
817 switch(*str++)
819 case 'm':
820 lastmoveto = m_pathTypesOrg.GetCount();
821 if(firstmoveto == -1) firstmoveto = lastmoveto;
822 while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_MOVETO); m_pathPointsOrg.Add(p);}
823 break;
824 case 'n':
825 while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_MOVETONC); m_pathPointsOrg.Add(p);}
826 break;
827 case 'l':
828 while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_LINETO); m_pathPointsOrg.Add(p);}
829 break;
830 case 'b':
831 j = m_pathTypesOrg.GetCount();
832 while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BEZIERTO); m_pathPointsOrg.Add(p); j++;}
833 j = m_pathTypesOrg.GetCount() - ((m_pathTypesOrg.GetCount()-j)%3);
834 m_pathTypesOrg.SetCount(j); m_pathPointsOrg.SetCount(j);
835 break;
836 case 's':
837 j = lastsplinestart = m_pathTypesOrg.GetCount();
838 i = 3;
839 while(i-- && GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BSPLINETO); m_pathPointsOrg.Add(p); j++;}
840 if(m_pathTypesOrg.GetCount()-lastsplinestart < 3) {m_pathTypesOrg.SetCount(lastsplinestart); m_pathPointsOrg.SetCount(lastsplinestart); lastsplinestart = -1;}
841 // no break here
842 case 'p':
843 while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BSPLINEPATCHTO); m_pathPointsOrg.Add(p); j++;}
844 break;
845 case 'c':
846 if(lastsplinestart > 0)
848 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
849 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
850 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
851 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)
852 m_pathPointsOrg.Add(p);
853 p = m_pathPointsOrg[lastsplinestart];
854 m_pathPointsOrg.Add(p);
855 p = m_pathPointsOrg[lastsplinestart+1];
856 m_pathPointsOrg.Add(p);
857 lastsplinestart = -1;
859 break;
860 default:
861 break;
864 if(firstmoveto > 0) break;
867 if(lastmoveto == -1 || firstmoveto > 0)
869 m_pathTypesOrg.RemoveAll();
870 m_pathPointsOrg.RemoveAll();
871 return(false);
873 int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
874 for(i = 0; i < m_pathTypesOrg.GetCount(); i++)
876 m_pathPointsOrg[i].x = (int)(64 * m_scalex * m_pathPointsOrg[i].x);
877 m_pathPointsOrg[i].y = (int)(64 * m_scaley * m_pathPointsOrg[i].y);
878 if(minx > m_pathPointsOrg[i].x) minx = m_pathPointsOrg[i].x;
879 if(miny > m_pathPointsOrg[i].y) miny = m_pathPointsOrg[i].y;
880 if(maxx < m_pathPointsOrg[i].x) maxx = m_pathPointsOrg[i].x;
881 if(maxy < m_pathPointsOrg[i].y) maxy = m_pathPointsOrg[i].y;
883 m_width = max(maxx - minx, 0);
884 m_ascent = max(maxy - miny, 0);
885 int baseline = (int)(64 * m_scaley * m_baseline);
886 m_descent = baseline;
887 m_ascent -= baseline;
888 m_width = ((int)(m_style.get().fontScaleX/100 * m_width) + 4) >> 3;
889 m_ascent = ((int)(m_style.get().fontScaleY/100 * m_ascent) + 4) >> 3;
890 m_descent = ((int)(m_style.get().fontScaleY/100 * m_descent) + 4) >> 3;
891 return(true);
894 bool CPolygon::CreatePath()
896 int len = m_pathTypesOrg.GetCount();
897 if(len == 0) return(false);
898 if(mPathPoints != len)
900 mpPathTypes = (BYTE*)realloc(mpPathTypes, len*sizeof(BYTE));
901 mpPathPoints = (POINT*)realloc(mpPathPoints, len*sizeof(POINT));
902 if(!mpPathTypes || !mpPathPoints) return(false);
903 mPathPoints = len;
905 memcpy(mpPathTypes, m_pathTypesOrg.GetData(), len*sizeof(BYTE));
906 memcpy(mpPathPoints, m_pathPointsOrg.GetData(), len*sizeof(POINT));
907 return(true);
910 // CClipper
912 CClipper::CClipper(CStringW str, CSize size, double scalex, double scaley, bool inverse)
913 : CPolygon(FwSTSStyle(), str, 0, 0, 0, scalex, scaley, 0)
915 m_size.cx = m_size.cy = 0;
916 m_pAlphaMask = NULL;
917 if(size.cx < 0 || size.cy < 0 || !(m_pAlphaMask = new BYTE[size.cx*size.cy])) return;
918 m_size = size;
919 m_inverse = inverse;
920 memset(m_pAlphaMask, 0, size.cx*size.cy);
921 OverlayList overlay_list;
922 Paint(CPoint(0, 0), CPoint(0, 0), &overlay_list);
923 int w = overlay_list.overlay->mOverlayWidth, h = overlay_list.overlay->mOverlayHeight;
924 int x = (overlay_list.overlay->mOffsetX+4)>>3, y = (overlay_list.overlay->mOffsetY+4)>>3;
925 int xo = 0, yo = 0;
926 if(x < 0) {xo = -x; w -= -x; x = 0;}
927 if(y < 0) {yo = -y; h -= -y; y = 0;}
928 if(x+w > m_size.cx) w = m_size.cx-x;
929 if(y+h > m_size.cy) h = m_size.cy-y;
930 if(w <= 0 || h <= 0) return;
931 const BYTE* src = overlay_list.overlay->mpOverlayBuffer.body + (overlay_list.overlay->mOverlayPitch * yo + xo);
932 BYTE* dst = m_pAlphaMask + m_size.cx * y + x;
933 while(h--)
935 //for(int wt=0; wt<w; ++wt)
936 // dst[wt] = src[wt];
937 memcpy(dst, src, w);
938 src += overlay_list.overlay->mOverlayPitch;
939 dst += m_size.cx;
941 if(inverse)
943 BYTE* dst = m_pAlphaMask;
944 for(int i = size.cx*size.cy; i>0; --i, ++dst)
945 *dst = 0x40 - *dst; // mask is 6 bit
949 CClipper::~CClipper()
951 if(m_pAlphaMask) delete [] m_pAlphaMask;
952 m_pAlphaMask = NULL;
955 CWord* CClipper::Copy()
957 return(new CClipper(m_str.get(), m_size, m_scalex, m_scaley, m_inverse));
960 bool CClipper::Append(CWord* w)
962 return(false);
965 // CLine
967 CLine::~CLine()
969 //POSITION pos = GetHeadPosition();
970 //while(pos) delete GetNext(pos);
973 void CLine::Compact()
975 POSITION pos = GetHeadPosition();
976 while(pos)
978 CWord* w = GetNext(pos);
979 if(!w->m_fWhiteSpaceChar) break;
980 m_width -= w->m_width;
981 delete w;
982 RemoveHead();
984 pos = GetTailPosition();
985 while(pos)
987 CWord* w = GetPrev(pos);
988 if(!w->m_fWhiteSpaceChar) break;
989 m_width -= w->m_width;
990 delete w;
991 RemoveTail();
993 if(IsEmpty()) return;
994 CLine l;
995 l.AddTailList(this);
996 RemoveAll();
997 CWord* last = NULL;
998 pos = l.GetHeadPosition();
999 while(pos)
1001 CWord* w = l.GetNext(pos);
1002 if(!last || !last->Append(w))
1003 AddTail(last = w->Copy());
1005 m_ascent = m_descent = m_borderX = m_borderY = 0;
1006 pos = GetHeadPosition();
1007 while(pos)
1009 CWord* w = GetNext(pos);
1010 if(m_ascent < w->m_ascent) m_ascent = w->m_ascent;
1011 if(m_descent < w->m_descent) m_descent = w->m_descent;
1012 if(m_borderX < w->m_style.get().outlineWidthX) m_borderX = (int)(w->m_style.get().outlineWidthX+0.5);
1013 if(m_borderY < w->m_style.get().outlineWidthY) m_borderY = (int)(w->m_style.get().outlineWidthY+0.5);
1017 CRect CLine::PaintShadow(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha)
1019 CRect bbox(0, 0, 0, 0);
1020 POSITION pos = GetHeadPosition();
1021 while(pos)
1023 CWord* w = GetNext(pos);
1024 if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
1025 if(w->m_style.get().shadowDepthX != 0 || w->m_style.get().shadowDepthY != 0)
1027 int x = p.x + (int)(w->m_style.get().shadowDepthX+0.5);
1028 int y = p.y + m_ascent - w->m_ascent + (int)(w->m_style.get().shadowDepthY+0.5);
1029 DWORD a = 0xff - w->m_style.get().alpha[3];
1030 if(alpha > 0) a = MulDiv(a, 0xff - alpha, 0xff);
1031 COLORREF shadow = revcolor(w->m_style.get().colors[3]) | (a<<24);
1032 DWORD sw[6] = {shadow, -1};
1033 //xy
1034 if(spd.type == MSP_YUY2)
1036 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1038 else if(spd.type == MSP_AYUV || spd.type == MSP_YV12 || spd.type == MSP_IYUV)
1040 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1042 OverlayList overlay_list;
1043 w->Paint(CPoint(x, y), org, &overlay_list);
1044 if(w->m_style.get().borderStyle == 0)
1046 bbox |= w->Draw(spd, overlay_list.overlay, clipRect, pAlphaMask, x, y, sw,
1047 w->m_ktype > 0 || w->m_style.get().alpha[0] < 0xff,
1048 (w->m_style.get().outlineWidthX+w->m_style.get().outlineWidthY > 0) && !(w->m_ktype == 2 && time < w->m_kstart));
1050 else if(w->m_style.get().borderStyle == 1 && w->m_pOpaqueBox)
1052 bbox |= w->m_pOpaqueBox->Draw(spd, overlay_list.next->overlay, clipRect, pAlphaMask, x, y, sw, true, false);
1055 p.x += w->m_width;
1057 return(bbox);
1060 CRect CLine::PaintOutline(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha)
1062 CRect bbox(0, 0, 0, 0);
1063 POSITION pos = GetHeadPosition();
1064 while(pos)
1066 CWord* w = GetNext(pos);
1067 if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
1068 if(w->m_style.get().outlineWidthX+w->m_style.get().outlineWidthY > 0 && !(w->m_ktype == 2 && time < w->m_kstart))
1070 int x = p.x;
1071 int y = p.y + m_ascent - w->m_ascent;
1072 DWORD aoutline = w->m_style.get().alpha[2];
1073 if(alpha > 0) aoutline += MulDiv(alpha, 0xff - w->m_style.get().alpha[2], 0xff);
1074 COLORREF outline = revcolor(w->m_style.get().colors[2]) | ((0xff-aoutline)<<24);
1075 DWORD sw[6] = {outline, -1};
1076 //xy
1077 if(spd.type == MSP_YUY2)
1079 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1081 else if(spd.type == MSP_AYUV || spd.type == MSP_YV12 || spd.type == MSP_IYUV)
1083 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1085 OverlayList overlay_list;
1086 w->Paint(CPoint(x, y), org, &overlay_list);
1087 if(w->m_style.get().borderStyle == 0)
1089 bbox |= w->Draw(spd, overlay_list.overlay, clipRect, pAlphaMask, x, y, sw, !w->m_style.get().alpha[0] && !w->m_style.get().alpha[1] && !alpha, true);
1091 else if(w->m_style.get().borderStyle == 1 && w->m_pOpaqueBox)
1093 bbox |= w->m_pOpaqueBox->Draw(spd, overlay_list.next->overlay, clipRect, pAlphaMask, x, y, sw, true, false);
1096 p.x += w->m_width;
1098 return(bbox);
1101 CRect CLine::PaintBody(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha)
1103 CRect bbox(0, 0, 0, 0);
1104 POSITION pos = GetHeadPosition();
1105 while(pos)
1107 CWord* w = GetNext(pos);
1108 if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
1109 int x = p.x;
1110 int y = p.y + m_ascent - w->m_ascent;
1111 // colors
1112 DWORD aprimary = w->m_style.get().alpha[0];
1113 if(alpha > 0) aprimary += MulDiv(alpha, 0xff - w->m_style.get().alpha[0], 0xff);
1114 COLORREF primary = revcolor(w->m_style.get().colors[0]) | ((0xff-aprimary)<<24);
1115 DWORD asecondary = w->m_style.get().alpha[1];
1116 if(alpha > 0) asecondary += MulDiv(alpha, 0xff - w->m_style.get().alpha[1], 0xff);
1117 COLORREF secondary = revcolor(w->m_style.get().colors[1]) | ((0xff-asecondary)<<24);
1118 DWORD sw[6] = {primary, 0, secondary};
1119 // karaoke
1120 double t;
1121 if(w->m_ktype == 0 || w->m_ktype == 2)
1123 t = time < w->m_kstart ? 0 : 1;
1125 else if(w->m_ktype == 1)
1127 if(time < w->m_kstart) t = 0;
1128 else if(time < w->m_kend)
1130 t = 1.0 * (time - w->m_kstart) / (w->m_kend - w->m_kstart);
1131 double angle = fmod(w->m_style.get().fontAngleZ, 360.0);
1132 if(angle > 90 && angle < 270)
1134 t = 1-t;
1135 COLORREF tmp = sw[0];
1136 sw[0] = sw[2];
1137 sw[2] = tmp;
1140 else t = 1.0;
1142 if(t >= 1)
1144 sw[1] = 0xffffffff;
1146 sw[3] = (int)(w->m_style.get().outlineWidthX + t*w->m_width) >> 3;
1147 sw[4] = sw[2];
1148 sw[5] = 0x00ffffff;
1149 //xy
1150 if(spd.type == MSP_YUY2)
1152 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1153 sw[2] =rgb2yuv(sw[2], XY_AUYV);
1154 sw[4] =rgb2yuv(sw[4], XY_AUYV);
1156 else if(spd.type == MSP_AYUV || spd.type == MSP_YV12 || spd.type == MSP_IYUV)
1158 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1159 sw[2] =rgb2yuv(sw[2], XY_AYUV);
1160 sw[4] =rgb2yuv(sw[4], XY_AYUV);
1162 OverlayList overlay_list;
1163 w->Paint(CPoint(x, y), org, &overlay_list);
1164 bbox |= w->Draw(spd, overlay_list.overlay, clipRect, pAlphaMask, x, y, sw, true, false);
1165 p.x += w->m_width;
1167 return(bbox);
1171 // CSubtitle
1173 CSubtitle::CSubtitle()
1175 memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
1176 m_pClipper = NULL;
1177 m_clipInverse = false;
1178 m_scalex = m_scaley = 1;
1181 CSubtitle::~CSubtitle()
1183 Empty();
1186 void CSubtitle::Empty()
1188 POSITION pos = GetHeadPosition();
1189 while(pos) delete GetNext(pos);
1190 // pos = m_words.GetHeadPosition();
1191 // while(pos) delete m_words.GetNext(pos);
1192 for(int i = 0; i < EF_NUMBEROFEFFECTS; i++) {if(m_effects[i]) delete m_effects[i];}
1193 memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
1194 if(m_pClipper) delete m_pClipper;
1195 m_pClipper = NULL;
1198 int CSubtitle::GetFullWidth()
1200 int width = 0;
1201 POSITION pos = m_words.GetHeadPosition();
1202 while(pos) width += m_words.GetNext(pos)->m_width;
1203 return(width);
1206 int CSubtitle::GetFullLineWidth(POSITION pos)
1208 int width = 0;
1209 while(pos)
1211 CWord* w = m_words.GetNext(pos);
1212 if(w->m_fLineBreak) break;
1213 width += w->m_width;
1215 return(width);
1218 int CSubtitle::GetWrapWidth(POSITION pos, int maxwidth)
1220 if(m_wrapStyle == 0 || m_wrapStyle == 3)
1222 if(maxwidth > 0)
1224 // int fullwidth = GetFullWidth();
1225 int fullwidth = GetFullLineWidth(pos);
1226 int minwidth = fullwidth / ((abs(fullwidth) / maxwidth) + 1);
1227 int width = 0, wordwidth = 0;
1228 while(pos && width < minwidth)
1230 CWord* w = m_words.GetNext(pos);
1231 wordwidth = w->m_width;
1232 if(abs(width + wordwidth) < abs(maxwidth)) width += wordwidth;
1234 maxwidth = width;
1235 if(m_wrapStyle == 3 && pos) maxwidth -= wordwidth;
1238 else if(m_wrapStyle == 1)
1240 // maxwidth = maxwidth;
1242 else if(m_wrapStyle == 2)
1244 maxwidth = INT_MAX;
1246 return(maxwidth);
1249 CLine* CSubtitle::GetNextLine(POSITION& pos, int maxwidth)
1251 if(pos == NULL) return(NULL);
1252 CLine* ret = new CLine();
1253 if(!ret) return(NULL);
1254 ret->m_width = ret->m_ascent = ret->m_descent = ret->m_borderX = ret->m_borderY = 0;
1255 maxwidth = GetWrapWidth(pos, maxwidth);
1256 bool fEmptyLine = true;
1257 while(pos)
1259 CWord* w = m_words.GetNext(pos);
1260 if(ret->m_ascent < w->m_ascent) ret->m_ascent = w->m_ascent;
1261 if(ret->m_descent < w->m_descent) ret->m_descent = w->m_descent;
1262 if(ret->m_borderX < w->m_style.get().outlineWidthX) ret->m_borderX = (int)(w->m_style.get().outlineWidthX+0.5);
1263 if(ret->m_borderY < w->m_style.get().outlineWidthY) ret->m_borderY = (int)(w->m_style.get().outlineWidthY+0.5);
1264 if(w->m_fLineBreak)
1266 if(fEmptyLine) {ret->m_ascent /= 2; ret->m_descent /= 2; ret->m_borderX = ret->m_borderY = 0;}
1267 ret->Compact();
1268 return(ret);
1270 fEmptyLine = false;
1271 bool fWSC = w->m_fWhiteSpaceChar;
1272 int width = w->m_width;
1273 POSITION pos2 = pos;
1274 while(pos2)
1276 if(m_words.GetAt(pos2)->m_fWhiteSpaceChar != fWSC
1277 || m_words.GetAt(pos2)->m_fLineBreak) break;
1278 CWord* w2 = m_words.GetNext(pos2);
1279 width += w2->m_width;
1281 if((ret->m_width += width) <= maxwidth || ret->IsEmpty())
1283 ret->AddTail(w->Copy());
1284 while(pos != pos2)
1286 ret->AddTail(m_words.GetNext(pos)->Copy());
1288 pos = pos2;
1290 else
1292 if(pos) m_words.GetPrev(pos);
1293 else pos = m_words.GetTailPosition();
1294 ret->m_width -= width;
1295 break;
1298 ret->Compact();
1299 return(ret);
1302 void CSubtitle::CreateClippers(CSize size)
1304 size.cx >>= 3;
1305 size.cy >>= 3;
1306 if(m_effects[EF_BANNER] && m_effects[EF_BANNER]->param[2])
1308 int width = m_effects[EF_BANNER]->param[2];
1309 int w = size.cx, h = size.cy;
1310 if(!m_pClipper)
1312 CStringW str;
1313 str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
1314 m_pClipper = new CClipper(str, size, 1, 1, false);
1315 if(!m_pClipper) return;
1317 int da = (64<<8)/width;
1318 BYTE* am = m_pClipper->m_pAlphaMask;
1319 for(int j = 0; j < h; j++, am += w)
1321 int a = 0;
1322 int k = min(width, w);
1323 for(int i = 0; i < k; i++, a += da)
1324 am[i] = (am[i]*a)>>14;
1325 a = 0x40<<8;
1326 k = w-width;
1327 if(k < 0) {a -= -k*da; k = 0;}
1328 for(int i = k; i < w; i++, a -= da)
1329 am[i] = (am[i]*a)>>14;
1332 else if(m_effects[EF_SCROLL] && m_effects[EF_SCROLL]->param[4])
1334 int height = m_effects[EF_SCROLL]->param[4];
1335 int w = size.cx, h = size.cy;
1336 if(!m_pClipper)
1338 CStringW str;
1339 str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
1340 m_pClipper = new CClipper(str, size, 1, 1, false);
1341 if(!m_pClipper) return;
1343 int da = (64<<8)/height;
1344 int a = 0;
1345 int k = m_effects[EF_SCROLL]->param[0]>>3;
1346 int l = k+height;
1347 if(k < 0) {a += -k*da; k = 0;}
1348 if(l > h) {l = h;}
1349 if(k < h)
1351 BYTE* am = &m_pClipper->m_pAlphaMask[k*w];
1352 memset(m_pClipper->m_pAlphaMask, 0, am - m_pClipper->m_pAlphaMask);
1353 for(int j = k; j < l; j++, a += da)
1355 for(int i = 0; i < w; i++, am++)
1356 *am = ((*am)*a)>>14;
1359 da = -(64<<8)/height;
1360 a = 0x40<<8;
1361 l = m_effects[EF_SCROLL]->param[1]>>3;
1362 k = l-height;
1363 if(k < 0) {a += -k*da; k = 0;}
1364 if(l > h) {l = h;}
1365 if(k < h)
1367 BYTE* am = &m_pClipper->m_pAlphaMask[k*w];
1368 int j = k;
1369 for(; j < l; j++, a += da)
1371 for(int i = 0; i < w; i++, am++)
1372 *am = ((*am)*a)>>14;
1374 memset(am, 0, (h-j)*w);
1379 void CSubtitle::MakeLines(CSize size, CRect marginRect)
1381 CSize spaceNeeded(0, 0);
1382 bool fFirstLine = true;
1383 m_topborder = m_bottomborder = 0;
1384 CLine* l = NULL;
1385 POSITION pos = m_words.GetHeadPosition();
1386 while(pos)
1388 l = GetNextLine(pos, size.cx - marginRect.left - marginRect.right);
1389 if(!l) break;
1390 if(fFirstLine) {m_topborder = l->m_borderY; fFirstLine = false;}
1391 spaceNeeded.cx = max(l->m_width+l->m_borderX, spaceNeeded.cx);
1392 spaceNeeded.cy += l->m_ascent + l->m_descent;
1393 AddTail(l);
1395 if(l) m_bottomborder = l->m_borderY;
1396 m_rect = CRect(
1397 CPoint((m_scrAlignment%3) == 1 ? marginRect.left
1398 : (m_scrAlignment%3) == 2 ? (marginRect.left + (size.cx - marginRect.right) - spaceNeeded.cx + 1) / 2
1399 : (size.cx - marginRect.right - spaceNeeded.cx),
1400 m_scrAlignment <= 3 ? (size.cy - marginRect.bottom - spaceNeeded.cy)
1401 : m_scrAlignment <= 6 ? (marginRect.top + (size.cy - marginRect.bottom) - spaceNeeded.cy + 1) / 2
1402 : marginRect.top),
1403 spaceNeeded);
1406 // CScreenLayoutAllocator
1408 void CScreenLayoutAllocator::Empty()
1410 m_subrects.RemoveAll();
1413 void CScreenLayoutAllocator::AdvanceToSegment(int segment, const CAtlArray<int>& sa)
1415 POSITION pos = m_subrects.GetHeadPosition();
1416 while(pos)
1418 POSITION prev = pos;
1419 SubRect& sr = m_subrects.GetNext(pos);
1420 bool fFound = false;
1421 if(abs(sr.segment - segment) <= 1) // using abs() makes it possible to play the subs backwards, too :)
1423 for(int i = 0; i < sa.GetCount() && !fFound; i++)
1425 if(sa[i] == sr.entry)
1427 sr.segment = segment;
1428 fFound = true;
1432 if(!fFound) m_subrects.RemoveAt(prev);
1436 CRect CScreenLayoutAllocator::AllocRect(CSubtitle* s, int segment, int entry, int layer, int collisions)
1438 // TODO: handle collisions == 1 (reversed collisions)
1439 POSITION pos = m_subrects.GetHeadPosition();
1440 while(pos)
1442 SubRect& sr = m_subrects.GetNext(pos);
1443 if(sr.segment == segment && sr.entry == entry)
1445 return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
1448 CRect r = s->m_rect + CRect(0, s->m_topborder, 0, s->m_bottomborder);
1449 bool fSearchDown = s->m_scrAlignment > 3;
1450 bool fOK;
1453 fOK = true;
1454 pos = m_subrects.GetHeadPosition();
1455 while(pos)
1457 SubRect& sr = m_subrects.GetNext(pos);
1458 if(layer == sr.layer && !(r & sr.r).IsRectEmpty())
1460 if(fSearchDown)
1462 r.bottom = sr.r.bottom + r.Height();
1463 r.top = sr.r.bottom;
1465 else
1467 r.top = sr.r.top - r.Height();
1468 r.bottom = sr.r.top;
1470 fOK = false;
1474 while(!fOK);
1475 SubRect sr;
1476 sr.r = r;
1477 sr.segment = segment;
1478 sr.entry = entry;
1479 sr.layer = layer;
1480 m_subrects.AddTail(sr);
1481 return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
1484 // CRenderedTextSubtitle
1486 CAtlMap<CStringW, CRenderedTextSubtitle::AssCmdType, CStringElementTraits<CStringW>> CRenderedTextSubtitle::m_cmdMap;
1488 CRenderedTextSubtitle::CRenderedTextSubtitle(CCritSec* pLock)
1489 : ISubPicProviderImpl(pLock)
1491 if( m_cmdMap.IsEmpty() )
1493 InitCmdMap();
1495 m_size = CSize(0, 0);
1496 if(g_hDC_refcnt == 0)
1498 g_hDC = CreateCompatibleDC(NULL);
1499 SetBkMode(g_hDC, TRANSPARENT);
1500 SetTextColor(g_hDC, 0xffffff);
1501 SetMapMode(g_hDC, MM_TEXT);
1503 g_hDC_refcnt++;
1506 CRenderedTextSubtitle::~CRenderedTextSubtitle()
1508 Deinit();
1509 g_hDC_refcnt--;
1510 if(g_hDC_refcnt == 0) DeleteDC(g_hDC);
1513 void CRenderedTextSubtitle::InitCmdMap()
1515 if( m_cmdMap.IsEmpty() )
1517 m_cmdMap.SetAt(L"1c", CMD_1c);
1518 m_cmdMap.SetAt(L"2c", CMD_2c);
1519 m_cmdMap.SetAt(L"3c", CMD_3c);
1520 m_cmdMap.SetAt(L"4c", CMD_4c);
1521 m_cmdMap.SetAt(L"1a", CMD_1a);
1522 m_cmdMap.SetAt(L"2a", CMD_2a);
1523 m_cmdMap.SetAt(L"3a", CMD_3a);
1524 m_cmdMap.SetAt(L"4a", CMD_4a);
1525 m_cmdMap.SetAt(L"alpha", CMD_alpha);
1526 m_cmdMap.SetAt(L"an", CMD_an);
1527 m_cmdMap.SetAt(L"a", CMD_a);
1528 m_cmdMap.SetAt(L"blur", CMD_blur);
1529 m_cmdMap.SetAt(L"bord", CMD_bord);
1530 m_cmdMap.SetAt(L"be", CMD_be);
1531 m_cmdMap.SetAt(L"b", CMD_b);
1532 m_cmdMap.SetAt(L"clip", CMD_clip);
1533 m_cmdMap.SetAt(L"iclip", CMD_iclip);
1534 m_cmdMap.SetAt(L"c", CMD_c);
1535 m_cmdMap.SetAt(L"fade", CMD_fade);
1536 m_cmdMap.SetAt(L"fad", CMD_fad);
1537 m_cmdMap.SetAt(L"fax", CMD_fax);
1538 m_cmdMap.SetAt(L"fay", CMD_fay);
1539 m_cmdMap.SetAt(L"fe", CMD_fe);
1540 m_cmdMap.SetAt(L"fn", CMD_fn);
1541 m_cmdMap.SetAt(L"frx", CMD_frx);
1542 m_cmdMap.SetAt(L"fry", CMD_fry);
1543 m_cmdMap.SetAt(L"frz", CMD_frz);
1544 m_cmdMap.SetAt(L"fr", CMD_fr);
1545 m_cmdMap.SetAt(L"fscx", CMD_fscx);
1546 m_cmdMap.SetAt(L"fscy", CMD_fscy);
1547 m_cmdMap.SetAt(L"fsc", CMD_fsc);
1548 m_cmdMap.SetAt(L"fsp", CMD_fsp);
1549 m_cmdMap.SetAt(L"fs", CMD_fs);
1550 m_cmdMap.SetAt(L"i", CMD_i);
1551 m_cmdMap.SetAt(L"kt", CMD_kt);
1552 m_cmdMap.SetAt(L"kf", CMD_kf);
1553 m_cmdMap.SetAt(L"K", CMD_K);
1554 m_cmdMap.SetAt(L"ko", CMD_ko);
1555 m_cmdMap.SetAt(L"k", CMD_k);
1556 m_cmdMap.SetAt(L"move", CMD_move);
1557 m_cmdMap.SetAt(L"org", CMD_org);
1558 m_cmdMap.SetAt(L"pbo", CMD_pbo);
1559 m_cmdMap.SetAt(L"pos", CMD_pos);
1560 m_cmdMap.SetAt(L"p", CMD_p);
1561 m_cmdMap.SetAt(L"q", CMD_q);
1562 m_cmdMap.SetAt(L"r", CMD_r);
1563 m_cmdMap.SetAt(L"shad", CMD_shad);
1564 m_cmdMap.SetAt(L"s", CMD_s);
1565 m_cmdMap.SetAt(L"t", CMD_t);
1566 m_cmdMap.SetAt(L"u", CMD_u);
1567 m_cmdMap.SetAt(L"xbord", CMD_xbord);
1568 m_cmdMap.SetAt(L"xshad", CMD_xshad);
1569 m_cmdMap.SetAt(L"ybord", CMD_ybord);
1570 m_cmdMap.SetAt(L"yshad", CMD_yshad);
1574 void CRenderedTextSubtitle::Copy(CSimpleTextSubtitle& sts)
1576 __super::Copy(sts);
1577 m_size = CSize(0, 0);
1578 if(CRenderedTextSubtitle* pRTS = dynamic_cast<CRenderedTextSubtitle*>(&sts))
1580 m_size = pRTS->m_size;
1584 void CRenderedTextSubtitle::Empty()
1586 Deinit();
1587 __super::Empty();
1590 void CRenderedTextSubtitle::OnChanged()
1592 __super::OnChanged();
1593 POSITION pos = m_subtitleCache.GetStartPosition();
1594 while(pos)
1596 int i;
1597 CSubtitle* s;
1598 m_subtitleCache.GetNextAssoc(pos, i, s);
1599 delete s;
1601 m_subtitleCache.RemoveAll();
1602 m_sla.Empty();
1605 bool CRenderedTextSubtitle::Init(CSize size, CRect vidrect)
1607 Deinit();
1608 m_size = CSize(size.cx*8, size.cy*8);
1609 m_vidrect = CRect(vidrect.left*8, vidrect.top*8, vidrect.right*8, vidrect.bottom*8);
1610 m_sla.Empty();
1611 return(true);
1614 void CRenderedTextSubtitle::Deinit()
1616 POSITION pos = m_subtitleCache.GetStartPosition();
1617 while(pos)
1619 int i;
1620 CSubtitle* s;
1621 m_subtitleCache.GetNextAssoc(pos, i, s);
1622 delete s;
1624 m_subtitleCache.RemoveAll();
1625 m_sla.Empty();
1626 m_size = CSize(0, 0);
1627 m_vidrect.SetRectEmpty();
1629 g_word_cache.clear();
1630 g_overlay_cache.clear();
1633 void CRenderedTextSubtitle::ParseEffect(CSubtitle* sub, CString str)
1635 str.Trim();
1636 if(!sub || str.IsEmpty()) return;
1637 const TCHAR* s = _tcschr(str, ';');
1638 if(!s) {s = (LPTSTR)(LPCTSTR)str; s += str.GetLength()-1;}
1639 s++;
1640 CString effect = str.Left(s - str);
1641 if(!effect.CompareNoCase(_T("Banner;")))
1643 int delay, lefttoright = 0, fadeawaywidth = 0;
1644 if(_stscanf(s, _T("%d;%d;%d"), &delay, &lefttoright, &fadeawaywidth) < 1) return;
1645 Effect* e = new Effect;
1646 if(!e) return;
1647 sub->m_effects[e->type = EF_BANNER] = e;
1648 e->param[0] = (int)(max(1.0*delay/sub->m_scalex, 1));
1649 e->param[1] = lefttoright;
1650 e->param[2] = (int)(sub->m_scalex*fadeawaywidth);
1651 sub->m_wrapStyle = 2;
1653 else if(!effect.CompareNoCase(_T("Scroll up;")) || !effect.CompareNoCase(_T("Scroll down;")))
1655 int top, bottom, delay, fadeawayheight = 0;
1656 if(_stscanf(s, _T("%d;%d;%d;%d"), &top, &bottom, &delay, &fadeawayheight) < 3) return;
1657 if(top > bottom) {int tmp = top; top = bottom; bottom = tmp;}
1658 Effect* e = new Effect;
1659 if(!e) return;
1660 sub->m_effects[e->type = EF_SCROLL] = e;
1661 e->param[0] = (int)(sub->m_scaley*top*8);
1662 e->param[1] = (int)(sub->m_scaley*bottom*8);
1663 e->param[2] = (int)(max(1.0*delay/sub->m_scaley, 1));
1664 e->param[3] = (effect.GetLength() == 12);
1665 e->param[4] = (int)(sub->m_scaley*fadeawayheight);
1669 void CRenderedTextSubtitle::ParseString(CSubtitle* sub, CStringW str, const FwSTSStyle& style)
1671 if(!sub) return;
1672 str.Replace(L"\\N", L"\n");
1673 str.Replace(L"\\n", (sub->m_wrapStyle < 2 || sub->m_wrapStyle == 3) ? L" " : L"\n");
1674 str.Replace(L"\\h", L"\x00A0");
1675 for(int ite = 0, j = 0, len = str.GetLength(); j <= len; j++)
1677 WCHAR c = str[j];
1678 if(c != L'\n' && c != L' ' && c != L'\x00A0' && c != 0)
1679 continue;
1680 if(ite < j)
1682 CWordCacheKey word_cache_key(style, str.Mid(ite, j-ite), m_ktype, m_kstart, m_kend);
1683 const CWordMruCache::hashed_cache& word_cache = g_word_cache.get_hashed_cache();
1684 const CWordMruCache::hashed_cache::iterator iter = word_cache.find(word_cache_key);
1685 if( iter != word_cache.end() )
1687 sub->m_words.AddTail(iter->word);
1689 else if(CWord* w = new CText(style, str.Mid(ite, j-ite), m_ktype, m_kstart, m_kend))
1691 sub->m_words.AddTail(w);
1692 CWordMruItem item(word_cache_key, w);
1693 g_word_cache.update_cache(item);
1695 else
1697 ///TODO: overflow handling
1699 m_kstart = m_kend;
1701 if(c == L'\n')
1703 CWordCacheKey word_cache_key(style, CStringW(), m_ktype, m_kstart, m_kend);
1704 const CWordMruCache::hashed_cache& word_cache = g_word_cache.get_hashed_cache();
1705 const CWordMruCache::hashed_cache::iterator iter = word_cache.find(word_cache_key);
1706 if( iter != word_cache.end() )
1708 sub->m_words.AddTail(iter->word);
1710 else if(CWord* w = new CText(style, CStringW(), m_ktype, m_kstart, m_kend))
1712 sub->m_words.AddTail(w);
1713 CWordMruItem item(word_cache_key, w);
1714 g_word_cache.update_cache(item);
1716 else
1718 ///TODO: overflow handling
1720 m_kstart = m_kend;
1722 else if(c == L' ' || c == L'\x00A0')
1724 CWordCacheKey word_cache_key(style, CStringW(c), m_ktype, m_kstart, m_kend);
1725 const CWordMruCache::hashed_cache& word_cache = g_word_cache.get_hashed_cache();
1726 const CWordMruCache::hashed_cache::iterator iter = word_cache.find(word_cache_key);
1727 if( iter != word_cache.end() )
1729 sub->m_words.AddTail(iter->word);
1731 else if(CWord* w = new CText(style, CStringW(c), m_ktype, m_kstart, m_kend))
1733 sub->m_words.AddTail(w);
1734 CWordMruItem item(word_cache_key, w);
1735 g_word_cache.update_cache(item);
1737 else
1739 ///TODO: overflow handling
1741 m_kstart = m_kend;
1743 ite = j+1;
1745 return;
1748 void CRenderedTextSubtitle::ParsePolygon(CSubtitle* sub, CStringW str, const FwSTSStyle& style)
1750 if(!sub || !str.GetLength() || !m_nPolygon) return;
1752 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))
1754 ///Todo: fix me
1755 //if( CWord* w_cache = m_wordCache.lookup(*w) )
1757 // sub->m_words.AddTail(w_cache);
1758 // delete w;
1760 //else
1762 sub->m_words.AddTail(w);
1764 m_kstart = m_kend;
1768 bool CRenderedTextSubtitle::ParseSSATag(CSubtitle* sub, CStringW str, STSStyle& style, const STSStyle& org, bool fAnimate)
1770 if(!sub) return(false);
1771 int nTags = 0, nUnrecognizedTags = 0;
1772 for(int i = 0, j; (j = str.Find(L'\\', i)) >= 0; i = j)
1774 CStringW cmd;
1775 for(WCHAR c = str[++j]; c && c != L'(' && c != L'\\'; cmd += c, c = str[++j]);
1776 cmd.Trim();
1777 if(cmd.IsEmpty()) continue;
1778 CAtlArray<CStringW> params;
1779 if(str[j] == L'(')
1781 CStringW param;
1782 for(WCHAR c = str[++j]; c && c != L')'; param += c, c = str[++j]);
1783 param.Trim();
1784 while(!param.IsEmpty())
1786 int i = param.Find(L','), j = param.Find(L'\\');
1787 if(i >= 0 && (j < 0 || i < j))
1789 CStringW s = param.Left(i).Trim();
1790 if(!s.IsEmpty()) params.Add(s);
1791 param = i+1 < param.GetLength() ? param.Mid(i+1) : L"";
1793 else
1795 param.Trim();
1796 if(!param.IsEmpty()) params.Add(param);
1797 param.Empty();
1802 AssCmdType cmd_type = CMD_COUNT;
1803 int cmd_length = min(MAX_CMD_LENGTH, cmd.GetLength());
1804 for( ;cmd_length>=MIN_CMD_LENGTH;cmd_length-- )
1806 if( m_cmdMap.Lookup(cmd.Left(cmd_length), cmd_type) )
1807 break;
1809 if(cmd_length<MIN_CMD_LENGTH)
1810 cmd_type = CMD_COUNT;
1811 switch( cmd_type )
1813 case CMD_fax:
1814 case CMD_fay:
1815 case CMD_fe:
1816 case CMD_fn:
1817 case CMD_frx:
1818 case CMD_fry:
1819 case CMD_frz:
1820 case CMD_fr:
1821 case CMD_fscx:
1822 case CMD_fscy:
1823 case CMD_fsc:
1824 case CMD_fsp:
1825 case CMD_fs:
1826 case CMD_i:
1827 case CMD_kt:
1828 case CMD_kf:
1829 case CMD_K:
1830 case CMD_ko:
1831 case CMD_k:
1832 case CMD_pbo:
1833 case CMD_p:
1834 case CMD_q:
1835 case CMD_r:
1836 case CMD_shad:
1837 case CMD_s:
1838 case CMD_an:
1839 case CMD_a:
1840 case CMD_blur:
1841 case CMD_bord:
1842 case CMD_be:
1843 case CMD_b:
1844 case CMD_u:
1845 case CMD_xbord:
1846 case CMD_xshad:
1847 case CMD_ybord:
1848 case CMD_yshad:
1849 // default:
1850 params.Add(cmd.Mid(cmd_length));
1851 break;
1852 case CMD_c:
1853 case CMD_1c :
1854 case CMD_2c :
1855 case CMD_3c :
1856 case CMD_4c :
1857 case CMD_1a :
1858 case CMD_2a :
1859 case CMD_3a :
1860 case CMD_4a :
1861 case CMD_alpha:
1862 params.Add(cmd.Mid(cmd_length).Trim(L"&H"));
1863 break;
1864 case CMD_clip:
1865 case CMD_iclip:
1866 case CMD_fade:
1867 case CMD_fad:
1868 case CMD_move:
1869 case CMD_org:
1870 case CMD_pos:
1871 case CMD_t:
1872 break;
1873 case CMD_COUNT:
1874 nUnrecognizedTags++;
1875 break;
1877 nTags++;
1878 // TODO: call ParseStyleModifier(cmd, params, ..) and move the rest there
1879 CStringW p = params.GetCount() > 0 ? params[0] : L"";
1880 switch ( cmd_type )
1882 case CMD_1c :
1883 case CMD_2c :
1884 case CMD_3c :
1885 case CMD_4c :
1887 int i = cmd[0] - L'1';
1888 DWORD c = wcstol(p, NULL, 16);
1889 style.colors[i] = !p.IsEmpty()
1890 ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
1891 |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
1892 |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
1893 : org.colors[i];
1894 break;
1896 case CMD_1a :
1897 case CMD_2a :
1898 case CMD_3a :
1899 case CMD_4a :
1901 int i = cmd[0] - L'1';
1902 style.alpha[i] = !p.IsEmpty()
1903 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
1904 : org.alpha[i];
1905 break;
1907 case CMD_alpha:
1909 for(int i = 0; i < 4; i++)
1911 style.alpha[i] = !p.IsEmpty()
1912 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
1913 : org.alpha[i];
1915 break;
1917 case CMD_an:
1919 int n = wcstol(p, NULL, 10);
1920 if(sub->m_scrAlignment < 0)
1921 sub->m_scrAlignment = (n > 0 && n < 10) ? n : org.scrAlignment;
1922 break;
1924 case CMD_a:
1926 int n = wcstol(p, NULL, 10);
1927 if(sub->m_scrAlignment < 0)
1928 sub->m_scrAlignment = (n > 0 && n < 12) ? ((((n-1)&3)+1)+((n&4)?6:0)+((n&8)?3:0)) : org.scrAlignment;
1929 break;
1931 case CMD_blur:
1933 double n = CalcAnimation(wcstod(p, NULL), style.fGaussianBlur, fAnimate);
1934 style.fGaussianBlur = !p.IsEmpty()
1935 ? (n < 0 ? 0 : n)
1936 : org.fGaussianBlur;
1937 break;
1939 case CMD_bord:
1941 double dst = wcstod(p, NULL);
1942 double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate);
1943 style.outlineWidthX = !p.IsEmpty()
1944 ? (nx < 0 ? 0 : nx)
1945 : org.outlineWidthX;
1946 double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate);
1947 style.outlineWidthY = !p.IsEmpty()
1948 ? (ny < 0 ? 0 : ny)
1949 : org.outlineWidthY;
1950 break;
1952 case CMD_be:
1954 int n = (int)(CalcAnimation(wcstol(p, NULL, 10), style.fBlur, fAnimate)+0.5);
1955 style.fBlur = !p.IsEmpty()
1957 : org.fBlur;
1958 break;
1960 case CMD_b:
1962 int n = wcstol(p, NULL, 10);
1963 style.fontWeight = !p.IsEmpty()
1964 ? (n == 0 ? FW_NORMAL : n == 1 ? FW_BOLD : n >= 100 ? n : org.fontWeight)
1965 : org.fontWeight;
1966 break;
1968 case CMD_clip:
1969 case CMD_iclip:
1971 bool invert = (cmd_type == CMD_iclip);
1972 if(params.GetCount() == 1 && !sub->m_pClipper)
1974 sub->m_pClipper = new CClipper(params[0], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex, sub->m_scaley, invert);
1976 else if(params.GetCount() == 2 && !sub->m_pClipper)
1978 int scale = max(wcstol(p, NULL, 10), 1);
1979 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)), invert);
1981 else if(params.GetCount() == 4)
1983 CRect r;
1984 sub->m_clipInverse = invert;
1985 r.SetRect(
1986 wcstol(params[0], NULL, 10),
1987 wcstol(params[1], NULL, 10),
1988 wcstol(params[2], NULL, 10),
1989 wcstol(params[3], NULL, 10));
1990 CPoint o(0, 0);
1991 if(sub->m_relativeTo == 1) // TODO: this should also apply to the other two clippings above
1993 o.x = m_vidrect.left>>3;
1994 o.y = m_vidrect.top>>3;
1996 sub->m_clip.SetRect(
1997 (int)CalcAnimation(sub->m_scalex*r.left + o.x, sub->m_clip.left, fAnimate),
1998 (int)CalcAnimation(sub->m_scaley*r.top + o.y, sub->m_clip.top, fAnimate),
1999 (int)CalcAnimation(sub->m_scalex*r.right + o.x, sub->m_clip.right, fAnimate),
2000 (int)CalcAnimation(sub->m_scaley*r.bottom + o.y, sub->m_clip.bottom, fAnimate));
2002 break;
2004 case CMD_c:
2006 DWORD c = wcstol(p, NULL, 16);
2007 style.colors[0] = !p.IsEmpty()
2008 ? (((int)CalcAnimation(c&0xff, style.colors[0]&0xff, fAnimate))&0xff
2009 |((int)CalcAnimation(c&0xff00, style.colors[0]&0xff00, fAnimate))&0xff00
2010 |((int)CalcAnimation(c&0xff0000, style.colors[0]&0xff0000, fAnimate))&0xff0000)
2011 : org.colors[0];
2012 break;
2014 case CMD_fade:
2015 case CMD_fad:
2017 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])
2019 if(Effect* e = new Effect)
2021 for(int i = 0; i < 3; i++)
2022 e->param[i] = wcstol(params[i], NULL, 10);
2023 for(int i = 0; i < 4; i++)
2024 e->t[i] = wcstol(params[3+i], NULL, 10);
2025 sub->m_effects[EF_FADE] = e;
2028 else if(params.GetCount() == 2 && !sub->m_effects[EF_FADE]) // {\fad(t1=t[1], t2=t[2])
2030 if(Effect* e = new Effect)
2032 e->param[0] = e->param[2] = 0xff;
2033 e->param[1] = 0x00;
2034 for(int i = 1; i < 3; i++)
2035 e->t[i] = wcstol(params[i-1], NULL, 10);
2036 e->t[0] = e->t[3] = -1; // will be substituted with "start" and "end"
2037 sub->m_effects[EF_FADE] = e;
2040 break;
2042 case CMD_fax:
2044 style.fontShiftX = !p.IsEmpty()
2045 ? CalcAnimation(wcstod(p, NULL), style.fontShiftX, fAnimate)
2046 : org.fontShiftX;
2047 break;
2049 case CMD_fay:
2051 style.fontShiftY = !p.IsEmpty()
2052 ? CalcAnimation(wcstod(p, NULL), style.fontShiftY, fAnimate)
2053 : org.fontShiftY;
2054 break;
2056 case CMD_fe:
2058 int n = wcstol(p, NULL, 10);
2059 style.charSet = !p.IsEmpty()
2061 : org.charSet;
2062 break;
2064 case CMD_fn:
2066 if(!p.IsEmpty() && p != L'0')
2067 style.fontName = CString(p).Trim();
2068 else
2069 style.fontName = org.fontName;
2070 break;
2072 case CMD_frx:
2074 style.fontAngleX = !p.IsEmpty()
2075 ? CalcAnimation(wcstod(p, NULL), style.fontAngleX, fAnimate)
2076 : org.fontAngleX;
2077 break;
2079 case CMD_fry:
2081 style.fontAngleY = !p.IsEmpty()
2082 ? CalcAnimation(wcstod(p, NULL), style.fontAngleY, fAnimate)
2083 : org.fontAngleY;
2084 break;
2086 case CMD_frz:
2087 case CMD_fr:
2089 style.fontAngleZ = !p.IsEmpty()
2090 ? CalcAnimation(wcstod(p, NULL), style.fontAngleZ, fAnimate)
2091 : org.fontAngleZ;
2092 break;
2094 case CMD_fscx:
2096 double n = CalcAnimation(wcstol(p, NULL, 10), style.fontScaleX, fAnimate);
2097 style.fontScaleX = !p.IsEmpty()
2098 ? ((n < 0) ? 0 : n)
2099 : org.fontScaleX;
2100 break;
2102 case CMD_fscy:
2104 double n = CalcAnimation(wcstol(p, NULL, 10), style.fontScaleY, fAnimate);
2105 style.fontScaleY = !p.IsEmpty()
2106 ? ((n < 0) ? 0 : n)
2107 : org.fontScaleY;
2108 break;
2110 case CMD_fsc:
2112 style.fontScaleX = org.fontScaleX;
2113 style.fontScaleY = org.fontScaleY;
2114 break;
2116 case CMD_fsp:
2118 style.fontSpacing = !p.IsEmpty()
2119 ? CalcAnimation(wcstod(p, NULL), style.fontSpacing, fAnimate)
2120 : org.fontSpacing;
2121 break;
2123 case CMD_fs:
2125 if(!p.IsEmpty())
2127 if(p[0] == L'-' || p[0] == L'+')
2129 double n = CalcAnimation(style.fontSize + style.fontSize*wcstol(p, NULL, 10)/10, style.fontSize, fAnimate);
2130 style.fontSize = (n > 0) ? n : org.fontSize;
2132 else
2134 double n = CalcAnimation(wcstol(p, NULL, 10), style.fontSize, fAnimate);
2135 style.fontSize = (n > 0) ? n : org.fontSize;
2138 else
2140 style.fontSize = org.fontSize;
2142 break;
2144 case CMD_i:
2146 int n = wcstol(p, NULL, 10);
2147 style.fItalic = !p.IsEmpty()
2148 ? (n == 0 ? false : n == 1 ? true : org.fItalic)
2149 : org.fItalic;
2150 break;
2152 case CMD_kt:
2154 m_kstart = !p.IsEmpty()
2155 ? wcstol(p, NULL, 10)*10
2156 : 0;
2157 m_kend = m_kstart;
2158 break;
2159 sub->m_fAnimated = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2161 case CMD_kf:
2162 case CMD_K:
2164 m_ktype = 1;
2165 m_kstart = m_kend;
2166 m_kend += !p.IsEmpty()
2167 ? wcstol(p, NULL, 10)*10
2168 : 1000;
2169 sub->m_fAnimated = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2170 break;
2172 case CMD_ko:
2174 m_ktype = 2;
2175 m_kstart = m_kend;
2176 m_kend += !p.IsEmpty()
2177 ? wcstol(p, NULL, 10)*10
2178 : 1000;
2179 sub->m_fAnimated = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2180 break;
2182 case CMD_k:
2184 m_ktype = 0;
2185 m_kstart = m_kend;
2186 m_kend += !p.IsEmpty()
2187 ? wcstol(p, NULL, 10)*10
2188 : 1000;
2189 sub->m_fAnimated = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2190 break;
2192 case CMD_move: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3][, t1=t[0], t2=t[1]])}
2194 if((params.GetCount() == 4 || params.GetCount() == 6) && !sub->m_effects[EF_MOVE])
2196 if(Effect* e = new Effect)
2198 e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2199 e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2200 e->param[2] = (int)(sub->m_scalex*wcstod(params[2], NULL)*8);
2201 e->param[3] = (int)(sub->m_scaley*wcstod(params[3], NULL)*8);
2202 e->t[0] = e->t[1] = -1;
2203 if(params.GetCount() == 6)
2205 for(int i = 0; i < 2; i++)
2206 e->t[i] = wcstol(params[4+i], NULL, 10);
2208 sub->m_effects[EF_MOVE] = e;
2211 break;
2213 case CMD_org: // {\org(x=param[0], y=param[1])}
2215 if(params.GetCount() == 2 && !sub->m_effects[EF_ORG])
2217 if(Effect* e = new Effect)
2219 e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2220 e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2221 sub->m_effects[EF_ORG] = e;
2224 break;
2226 case CMD_pbo:
2228 m_polygonBaselineOffset = wcstol(p, NULL, 10);
2229 break;
2231 case CMD_pos:
2233 if(params.GetCount() == 2 && !sub->m_effects[EF_MOVE])
2235 if(Effect* e = new Effect)
2237 e->param[0] = e->param[2] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2238 e->param[1] = e->param[3] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2239 e->t[0] = e->t[1] = 0;
2240 sub->m_effects[EF_MOVE] = e;
2243 break;
2245 case CMD_p:
2247 int n = wcstol(p, NULL, 10);
2248 m_nPolygon = (n <= 0 ? 0 : n);
2249 break;
2251 case CMD_q:
2253 int n = wcstol(p, NULL, 10);
2254 sub->m_wrapStyle = !p.IsEmpty() && (0 <= n && n <= 3)
2256 : m_defaultWrapStyle;
2257 break;
2259 case CMD_r:
2261 STSStyle* val;
2262 style = (!p.IsEmpty() && m_styles.Lookup(FwString(WToT(p)), val) && val) ? *val : org;
2263 break;
2265 case CMD_shad:
2267 double dst = wcstod(p, NULL);
2268 double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate);
2269 style.shadowDepthX = !p.IsEmpty()
2270 ? (nx < 0 ? 0 : nx)
2271 : org.shadowDepthX;
2272 double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate);
2273 style.shadowDepthY = !p.IsEmpty()
2274 ? (ny < 0 ? 0 : ny)
2275 : org.shadowDepthY;
2276 break;
2278 case CMD_s:
2280 int n = wcstol(p, NULL, 10);
2281 style.fStrikeOut = !p.IsEmpty()
2282 ? (n == 0 ? false : n == 1 ? true : org.fStrikeOut)
2283 : org.fStrikeOut;
2284 break;
2286 case CMD_t: // \t([<t1>,<t2>,][<accel>,]<style modifiers>)
2288 p.Empty();
2289 m_animStart = m_animEnd = 0;
2290 m_animAccel = 1;
2291 if(params.GetCount() == 1)
2293 p = params[0];
2295 else if(params.GetCount() == 2)
2297 m_animAccel = wcstod(params[0], NULL);
2298 p = params[1];
2300 else if(params.GetCount() == 3)
2302 m_animStart = (int)wcstod(params[0], NULL);
2303 m_animEnd = (int)wcstod(params[1], NULL);
2304 p = params[2];
2306 else if(params.GetCount() == 4)
2308 m_animStart = wcstol(params[0], NULL, 10);
2309 m_animEnd = wcstol(params[1], NULL, 10);
2310 m_animAccel = wcstod(params[2], NULL);
2311 p = params[3];
2313 ParseSSATag(sub, p, style, org, true);
2314 sub->m_fAnimated = true;
2315 break;
2317 case CMD_u:
2319 int n = wcstol(p, NULL, 10);
2320 style.fUnderline = !p.IsEmpty()
2321 ? (n == 0 ? false : n == 1 ? true : org.fUnderline)
2322 : org.fUnderline;
2323 break;
2325 case CMD_xbord:
2327 double dst = wcstod(p, NULL);
2328 double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate);
2329 style.outlineWidthX = !p.IsEmpty()
2330 ? (nx < 0 ? 0 : nx)
2331 : org.outlineWidthX;
2332 break;
2334 case CMD_xshad:
2336 double dst = wcstod(p, NULL);
2337 double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate);
2338 style.shadowDepthX = !p.IsEmpty()
2339 ? nx
2340 : org.shadowDepthX;
2341 break;
2343 case CMD_ybord:
2345 double dst = wcstod(p, NULL);
2346 double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate);
2347 style.outlineWidthY = !p.IsEmpty()
2348 ? (ny < 0 ? 0 : ny)
2349 : org.outlineWidthY;
2350 break;
2352 case CMD_yshad:
2354 double dst = wcstod(p, NULL);
2355 double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate);
2356 style.shadowDepthY = !p.IsEmpty()
2357 ? ny
2358 : org.shadowDepthY;
2359 break;
2361 default:
2362 break;
2365 // return(nUnrecognizedTags < nTags);
2366 return(true); // there are ppl keeping coments inside {}, lets make them happy now
2369 bool CRenderedTextSubtitle::ParseHtmlTag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org)
2371 if(str.Find(L"!--") == 0)
2372 return(true);
2373 bool fClosing = str[0] == L'/';
2374 str.Trim(L" /");
2375 int i = str.Find(L' ');
2376 if(i < 0) i = str.GetLength();
2377 CStringW tag = str.Left(i).MakeLower();
2378 str = str.Mid(i).Trim();
2379 CAtlArray<CStringW> attribs, params;
2380 while((i = str.Find(L'=')) > 0)
2382 attribs.Add(str.Left(i).Trim().MakeLower());
2383 str = str.Mid(i+1);
2384 for(i = 0; _istspace(str[i]); i++);
2385 str = str.Mid(i);
2386 if(str[0] == L'\"') {str = str.Mid(1); i = str.Find(L'\"');}
2387 else i = str.Find(L' ');
2388 if(i < 0) i = str.GetLength();
2389 params.Add(str.Left(i).Trim().MakeLower());
2390 str = str.Mid(i+1);
2392 if(tag == L"text")
2394 else if(tag == L"b" || tag == L"strong")
2395 style.fontWeight = !fClosing ? FW_BOLD : org.fontWeight;
2396 else if(tag == L"i" || tag == L"em")
2397 style.fItalic = !fClosing ? true : org.fItalic;
2398 else if(tag == L"u")
2399 style.fUnderline = !fClosing ? true : org.fUnderline;
2400 else if(tag == L"s" || tag == L"strike" || tag == L"del")
2401 style.fStrikeOut = !fClosing ? true : org.fStrikeOut;
2402 else if(tag == L"font")
2404 if(!fClosing)
2406 for(i = 0; i < attribs.GetCount(); i++)
2408 if(params[i].IsEmpty()) continue;
2409 int nColor = -1;
2410 if(attribs[i] == L"face")
2412 style.fontName = params[i];
2414 else if(attribs[i] == L"size")
2416 if(params[i][0] == L'+')
2417 style.fontSize += wcstol(params[i], NULL, 10);
2418 else if(params[i][0] == L'-')
2419 style.fontSize -= wcstol(params[i], NULL, 10);
2420 else
2421 style.fontSize = wcstol(params[i], NULL, 10);
2423 else if(attribs[i] == L"color")
2425 nColor = 0;
2427 else if(attribs[i] == L"outline-color")
2429 nColor = 2;
2431 else if(attribs[i] == L"outline-level")
2433 style.outlineWidthX = style.outlineWidthY = wcstol(params[i], NULL, 10);
2435 else if(attribs[i] == L"shadow-color")
2437 nColor = 3;
2439 else if(attribs[i] == L"shadow-level")
2441 style.shadowDepthX = style.shadowDepthY = wcstol(params[i], NULL, 10);
2443 if(nColor >= 0 && nColor < 4)
2445 CString key = WToT(params[i]).TrimLeft(L'#');
2446 DWORD val;
2447 if(g_colors.Lookup(key, val))
2448 style.colors[nColor] = val;
2449 else if((style.colors[nColor] = _tcstol(key, NULL, 16)) == 0)
2450 style.colors[nColor] = 0x00ffffff; // default is white
2451 style.colors[nColor] = ((style.colors[nColor]>>16)&0xff)|((style.colors[nColor]&0xff)<<16)|(style.colors[nColor]&0x00ff00);
2455 else
2457 style.fontName = org.fontName;
2458 style.fontSize = org.fontSize;
2459 memcpy(style.colors, org.colors, sizeof(style.colors));
2462 else if(tag == L"k" && attribs.GetCount() == 1 && attribs[0] == L"t")
2464 m_ktype = 1;
2465 m_kstart = m_kend;
2466 m_kend += wcstol(params[0], NULL, 10);
2468 else
2469 return(false);
2470 return(true);
2473 double CRenderedTextSubtitle::CalcAnimation(double dst, double src, bool fAnimate)
2475 int s = m_animStart ? m_animStart : 0;
2476 int e = m_animEnd ? m_animEnd : m_delay;
2477 if(fabs(dst-src) >= 0.0001 && fAnimate)
2479 if(m_time < s) dst = src;
2480 else if(s <= m_time && m_time < e)
2482 double t = pow(1.0 * (m_time - s) / (e - s), m_animAccel);
2483 dst = (1 - t) * src + t * dst;
2485 // else dst = dst;
2487 return(dst);
2490 CSubtitle* CRenderedTextSubtitle::GetSubtitle(int entry)
2492 CSubtitle* sub;
2493 if(m_subtitleCache.Lookup(entry, sub))
2495 if(sub->m_fAnimated) {delete sub; sub = NULL;}
2496 else return(sub);
2498 sub = new CSubtitle();
2499 if(!sub) return(NULL);
2500 CStringW str = GetStrW(entry, true);
2501 STSStyle stss, orgstss;
2502 GetStyle(entry, &stss);
2503 if (stss.fontScaleX == stss.fontScaleY && m_dPARCompensation != 1.0)
2505 switch(m_ePARCompensationType)
2507 case EPCTUpscale:
2508 if (m_dPARCompensation < 1.0)
2509 stss.fontScaleY /= m_dPARCompensation;
2510 else
2511 stss.fontScaleX *= m_dPARCompensation;
2512 break;
2513 case EPCTDownscale:
2514 if (m_dPARCompensation < 1.0)
2515 stss.fontScaleX *= m_dPARCompensation;
2516 else
2517 stss.fontScaleY /= m_dPARCompensation;
2518 break;
2519 case EPCTAccurateSize:
2520 stss.fontScaleX *= m_dPARCompensation;
2521 break;
2524 orgstss = stss;
2525 sub->m_clip.SetRect(0, 0, m_size.cx>>3, m_size.cy>>3);
2526 sub->m_scrAlignment = -stss.scrAlignment;
2527 sub->m_wrapStyle = m_defaultWrapStyle;
2528 sub->m_fAnimated = false;
2529 sub->m_relativeTo = stss.relativeTo;
2530 sub->m_scalex = m_dstScreenSize.cx > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Width() : m_size.cx) / (m_dstScreenSize.cx*8) : 1.0;
2531 sub->m_scaley = m_dstScreenSize.cy > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Height() : m_size.cy) / (m_dstScreenSize.cy*8) : 1.0;
2532 m_animStart = m_animEnd = 0;
2533 m_animAccel = 1;
2534 m_ktype = m_kstart = m_kend = 0;
2535 m_nPolygon = 0;
2536 m_polygonBaselineOffset = 0;
2537 ParseEffect(sub, GetAt(entry).effect);
2538 while(!str.IsEmpty())
2540 bool fParsed = false;
2541 int i;
2542 if(str[0] == L'{' && (i = str.Find(L'}')) > 0)
2544 if(fParsed = ParseSSATag(sub, str.Mid(1, i-1), stss, orgstss))
2545 str = str.Mid(i+1);
2547 else if(str[0] == L'<' && (i = str.Find(L'>')) > 0)
2549 if(fParsed = ParseHtmlTag(sub, str.Mid(1, i-1), stss, orgstss))
2550 str = str.Mid(i+1);
2552 if(fParsed)
2554 i = str.FindOneOf(L"{<");
2555 if(i < 0) i = str.GetLength();
2556 if(i == 0) continue;
2558 else
2560 i = str.Mid(1).FindOneOf(L"{<");
2561 if(i < 0) i = str.GetLength()-1;
2562 i++;
2564 STSStyle tmp = stss;
2565 tmp.fontSize = sub->m_scaley*tmp.fontSize*64;
2566 tmp.fontSpacing = sub->m_scalex*tmp.fontSpacing*64;
2567 tmp.outlineWidthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8;
2568 tmp.outlineWidthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8;
2569 tmp.shadowDepthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8;
2570 tmp.shadowDepthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8;
2571 FwSTSStyle fw_tmp(tmp);
2572 if(m_nPolygon)
2574 ParsePolygon(sub, str.Left(i), fw_tmp);
2576 else
2578 ParseString(sub, str.Left(i), fw_tmp);
2580 str = str.Mid(i);
2582 sub->m_fAnimated2 = sub->m_fAnimated;
2583 if(sub->m_effects[EF_FADE] || sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL]
2584 || (sub->m_effects[EF_MOVE] && (sub->m_effects[EF_MOVE]->t[1]!=sub->m_effects[EF_MOVE]->t[0])))
2585 sub->m_fAnimated2 = true;
2586 // just a "work-around" solution... in most cases nobody will want to use \org together with moving but without rotating the subs
2587 if(sub->m_effects[EF_ORG] && (sub->m_effects[EF_MOVE] || sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL]))
2588 sub->m_fAnimated = true;
2589 sub->m_scrAlignment = abs(sub->m_scrAlignment);
2590 STSEntry stse = GetAt(entry);
2591 CRect marginRect = stse.marginRect;
2592 if(marginRect.left == 0) marginRect.left = orgstss.marginRect.get().left;
2593 if(marginRect.top == 0) marginRect.top = orgstss.marginRect.get().top;
2594 if(marginRect.right == 0) marginRect.right = orgstss.marginRect.get().right;
2595 if(marginRect.bottom == 0) marginRect.bottom = orgstss.marginRect.get().bottom;
2596 marginRect.left = (int)(sub->m_scalex*marginRect.left*8);
2597 marginRect.top = (int)(sub->m_scaley*marginRect.top*8);
2598 marginRect.right = (int)(sub->m_scalex*marginRect.right*8);
2599 marginRect.bottom = (int)(sub->m_scaley*marginRect.bottom*8);
2600 if(stss.relativeTo == 1)
2602 marginRect.left += m_vidrect.left;
2603 marginRect.top += m_vidrect.top;
2604 marginRect.right += m_size.cx - m_vidrect.right;
2605 marginRect.bottom += m_size.cy - m_vidrect.bottom;
2607 sub->CreateClippers(m_size);
2608 sub->MakeLines(m_size, marginRect);
2609 m_subtitleCache[entry] = sub;
2610 return(sub);
2615 STDMETHODIMP CRenderedTextSubtitle::NonDelegatingQueryInterface(REFIID riid, void** ppv)
2617 CheckPointer(ppv, E_POINTER);
2618 *ppv = NULL;
2619 return
2620 QI(IPersist)
2621 QI(ISubStream)
2622 QI(ISubPicProvider)
2623 __super::NonDelegatingQueryInterface(riid, ppv);
2626 // ISubPicProvider
2628 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetStartPosition(REFERENCE_TIME rt, double fps)
2630 //DbgLog((LOG_TRACE, 3, "rt:%lu", (ULONG)rt/10000));
2631 m_fps = fps;//fix me: check is fps changed and do some re-init thing
2632 int iSegment;
2633 int subIndex = 1;//If a segment has animate effect then it corresponds to several subpics.
2634 //subIndex, 1 based, indicates which subpic the result corresponds to.
2635 rt /= 10000i64;
2636 const STSSegment *stss = SearchSubs((int)rt, fps, &iSegment, NULL);
2637 if(stss==NULL)
2638 return NULL;
2639 else if(stss->animated)
2641 int start = TranslateSegmentStart(iSegment, fps);
2642 if(rt > start)
2643 subIndex = (rt-start)/RTS_ANIMATE_SUBPIC_DUR + 1;
2645 //DbgLog((LOG_TRACE, 3, "animated:%d seg:%d idx:%d DUR:%d rt:%lu", stss->animated, iSegment, subIndex, RTS_ANIMATE_SUBPIC_DUR, (ULONG)rt/10000));
2646 return (POSITION)(subIndex | (iSegment<<RTS_POS_SEGMENT_INDEX_BITS));
2647 //if(iSegment < 0) iSegment = 0;
2648 //return(GetNext((POSITION)iSegment));
2651 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetNext(POSITION pos)
2653 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
2654 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
2655 const STSSegment *stss = GetSegment(iSegment);
2656 ASSERT(stss!=NULL && stss->subs.GetCount()>0);
2657 //DbgLog((LOG_TRACE, 3, "stss:%x count:%d", stss, stss->subs.GetCount()));
2658 if(!stss->animated)
2660 iSegment++;
2661 subIndex = 1;
2663 else
2665 int start, end;
2666 TranslateSegmentStartEnd(iSegment, m_fps, start, end);
2667 if(start+RTS_ANIMATE_SUBPIC_DUR*subIndex < end)
2668 subIndex++;
2669 else
2671 iSegment++;
2672 subIndex = 1;
2675 if(GetSegment(iSegment) != NULL)
2677 ASSERT(GetSegment(iSegment)->subs.GetCount()>0);
2678 return (POSITION)(subIndex | (iSegment<<RTS_POS_SEGMENT_INDEX_BITS));
2680 else
2681 return NULL;
2684 //@return: <0 if segment not found
2685 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStart(POSITION pos, double fps)
2687 //return(10000i64 * TranslateSegmentStart((int)pos-1, fps));
2688 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
2689 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
2690 int start = TranslateSegmentStart(iSegment, fps);
2691 const STSSegment *stss = GetSegment(iSegment);
2692 if(stss!=NULL)
2694 return (start + (subIndex-1)*RTS_ANIMATE_SUBPIC_DUR)*10000i64;
2696 else
2698 return -1;
2702 //@return: <0 if segment not found
2703 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStop(POSITION pos, double fps)
2705 // return(10000i64 * TranslateSegmentEnd((int)pos-1, fps));
2706 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
2707 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
2708 int start, end, ret;
2709 TranslateSegmentStartEnd(iSegment, fps, start, end);
2710 const STSSegment *stss = GetSegment(iSegment);
2711 if(stss!=NULL)
2713 if(!stss->animated)
2714 ret = end;
2715 else
2717 ret = start+subIndex*RTS_ANIMATE_SUBPIC_DUR;
2718 if(ret > end)
2719 ret = end;
2721 return ret*10000i64;
2723 else
2724 return -1;
2727 //@start, @stop: -1 if segment not found; @stop may < @start if subIndex exceed uppper bound
2728 STDMETHODIMP_(VOID) CRenderedTextSubtitle::GetStartStop(POSITION pos, double fps, /*out*/REFERENCE_TIME &start, /*out*/REFERENCE_TIME &stop)
2730 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
2731 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
2732 int tempStart, tempEnd;
2733 TranslateSegmentStartEnd(iSegment, fps, tempStart, tempEnd);
2734 start = tempStart;
2735 stop = tempEnd;
2736 const STSSegment *stss = GetSegment(iSegment);
2737 if(stss!=NULL)
2739 if(stss->animated)
2741 start += (subIndex-1)*RTS_ANIMATE_SUBPIC_DUR;
2742 if(start+RTS_ANIMATE_SUBPIC_DUR < stop)
2743 stop = start+RTS_ANIMATE_SUBPIC_DUR;
2745 //DbgLog((LOG_TRACE, 3, "animated:%d seg:%d idx:%d start:%d stop:%lu", stss->animated, iSegment, subIndex, (ULONG)start, (ULONG)stop));
2746 start *= 10000i64;
2747 stop *= 10000i64;
2749 else
2751 start = -1;
2752 stop = -1;
2756 STDMETHODIMP_(bool) CRenderedTextSubtitle::IsAnimated(POSITION pos)
2758 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
2759 if(iSegment>=0 && iSegment<m_segments.GetCount())
2760 return m_segments[iSegment].animated;
2761 else
2762 return false;
2763 //return(true);
2766 struct LSub {int idx, layer, readorder;};
2768 static int lscomp(const void* ls1, const void* ls2)
2770 int ret = ((LSub*)ls1)->layer - ((LSub*)ls2)->layer;
2771 if(!ret) ret = ((LSub*)ls1)->readorder - ((LSub*)ls2)->readorder;
2772 return(ret);
2775 STDMETHODIMP CRenderedTextSubtitle::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, CAtlList<CRect>& rectList)
2777 CRect bbox2(0,0,0,0);
2778 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))
2779 Init(CSize(spd.w, spd.h), spd.vidrect);
2780 int t = (int)(rt / 10000);
2781 int segment;
2782 //const
2783 STSSegment* stss = SearchSubs2(t, fps, &segment);
2784 if(!stss) return S_FALSE;
2785 // clear any cached subs not in the range of +/-30secs measured from the segment's bounds
2787 POSITION pos = m_subtitleCache.GetStartPosition();
2788 while(pos)
2790 int key;
2791 CSubtitle* value;
2792 m_subtitleCache.GetNextAssoc(pos, key, value);
2793 STSEntry& stse = GetAt(key);
2794 if(stse.end <= (t-30000) || stse.start > (t+30000))
2796 delete value;
2797 m_subtitleCache.RemoveKey(key);
2798 pos = m_subtitleCache.GetStartPosition();
2802 m_sla.AdvanceToSegment(segment, stss->subs);
2803 CAtlArray<LSub> subs;
2804 for(int i = 0, j = stss->subs.GetCount(); i < j; i++)
2806 LSub ls;
2807 ls.idx = stss->subs[i];
2808 ls.layer = GetAt(stss->subs[i]).layer;
2809 ls.readorder = GetAt(stss->subs[i]).readorder;
2810 subs.Add(ls);
2812 qsort(subs.GetData(), subs.GetCount(), sizeof(LSub), lscomp);
2813 for(int i = 0, j = subs.GetCount(); i < j; i++)
2815 int entry = subs[i].idx;
2816 STSEntry stse = GetAt(entry);
2818 int start = TranslateStart(entry, fps);
2819 m_time = t - start;
2820 m_delay = TranslateEnd(entry, fps) - start;
2822 CSubtitle* s = GetSubtitle(entry);
2823 if(!s) continue;
2824 stss->animated |= s->m_fAnimated2;
2825 CRect clipRect = s->m_clip;
2826 CRect r = s->m_rect;
2827 CSize spaceNeeded = r.Size();
2828 // apply the effects
2829 bool fPosOverride = false, fOrgOverride = false;
2830 int alpha = 0x00;
2831 CPoint org2;
2832 for(int k = 0; k < EF_NUMBEROFEFFECTS; k++)
2834 if(!s->m_effects[k]) continue;
2835 switch(k)
2837 case EF_MOVE: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3], t1=t[0], t2=t[1])}
2839 CPoint p;
2840 CPoint p1(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
2841 CPoint p2(s->m_effects[k]->param[2], s->m_effects[k]->param[3]);
2842 int t1 = s->m_effects[k]->t[0];
2843 int t2 = s->m_effects[k]->t[1];
2844 if(t2 < t1) {int t = t1; t1 = t2; t2 = t;}
2845 if(t1 <= 0 && t2 <= 0) {t1 = 0; t2 = m_delay;}
2846 if(m_time <= t1) p = p1;
2847 else if (p1 == p2) p = p1;
2848 else if(t1 < m_time && m_time < t2)
2850 double t = 1.0*(m_time-t1)/(t2-t1);
2851 p.x = (int)((1-t)*p1.x + t*p2.x);
2852 p.y = (int)((1-t)*p1.y + t*p2.y);
2854 else p = p2;
2855 r = CRect(
2856 CPoint((s->m_scrAlignment%3) == 1 ? p.x : (s->m_scrAlignment%3) == 0 ? p.x - spaceNeeded.cx : p.x - (spaceNeeded.cx+1)/2,
2857 s->m_scrAlignment <= 3 ? p.y - spaceNeeded.cy : s->m_scrAlignment <= 6 ? p.y - (spaceNeeded.cy+1)/2 : p.y),
2858 spaceNeeded);
2859 if(s->m_relativeTo == 1)
2860 r.OffsetRect(m_vidrect.TopLeft());
2861 fPosOverride = true;
2863 break;
2864 case EF_ORG: // {\org(x=param[0], y=param[1])}
2866 org2 = CPoint(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
2867 fOrgOverride = true;
2869 break;
2870 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])
2872 int t1 = s->m_effects[k]->t[0];
2873 int t2 = s->m_effects[k]->t[1];
2874 int t3 = s->m_effects[k]->t[2];
2875 int t4 = s->m_effects[k]->t[3];
2876 if(t1 == -1 && t4 == -1) {t1 = 0; t3 = m_delay-t3; t4 = m_delay;}
2877 if(m_time < t1) alpha = s->m_effects[k]->param[0];
2878 else if(m_time >= t1 && m_time < t2)
2880 double t = 1.0 * (m_time - t1) / (t2 - t1);
2881 alpha = (int)(s->m_effects[k]->param[0]*(1-t) + s->m_effects[k]->param[1]*t);
2883 else if(m_time >= t2 && m_time < t3) alpha = s->m_effects[k]->param[1];
2884 else if(m_time >= t3 && m_time < t4)
2886 double t = 1.0 * (m_time - t3) / (t4 - t3);
2887 alpha = (int)(s->m_effects[k]->param[1]*(1-t) + s->m_effects[k]->param[2]*t);
2889 else if(m_time >= t4) alpha = s->m_effects[k]->param[2];
2891 break;
2892 case EF_BANNER: // Banner;delay=param[0][;leftoright=param[1];fadeawaywidth=param[2]]
2894 int left = s->m_relativeTo == 1 ? m_vidrect.left : 0,
2895 right = s->m_relativeTo == 1 ? m_vidrect.right : m_size.cx;
2896 r.left = !!s->m_effects[k]->param[1]
2897 ? (left/*marginRect.left*/ - spaceNeeded.cx) + (int)(m_time*8.0/s->m_effects[k]->param[0])
2898 : (right /*- marginRect.right*/) - (int)(m_time*8.0/s->m_effects[k]->param[0]);
2899 r.right = r.left + spaceNeeded.cx;
2900 clipRect &= CRect(left>>3, clipRect.top, right>>3, clipRect.bottom);
2901 fPosOverride = true;
2903 break;
2904 case EF_SCROLL: // Scroll up/down(toptobottom=param[3]);top=param[0];bottom=param[1];delay=param[2][;fadeawayheight=param[4]]
2906 r.top = !!s->m_effects[k]->param[3]
2907 ? s->m_effects[k]->param[0] + (int)(m_time*8.0/s->m_effects[k]->param[2]) - spaceNeeded.cy
2908 : s->m_effects[k]->param[1] - (int)(m_time*8.0/s->m_effects[k]->param[2]);
2909 r.bottom = r.top + spaceNeeded.cy;
2910 CRect cr(0, (s->m_effects[k]->param[0] + 4) >> 3, spd.w, (s->m_effects[k]->param[1] + 4) >> 3);
2911 if(s->m_relativeTo == 1)
2912 r.top += m_vidrect.top,
2913 r.bottom += m_vidrect.top,
2914 cr.top += m_vidrect.top>>3,
2915 cr.bottom += m_vidrect.top>>3;
2916 clipRect &= cr;
2917 fPosOverride = true;
2919 break;
2920 default:
2921 break;
2924 if(!fPosOverride && !fOrgOverride && !s->m_fAnimated)
2925 r = m_sla.AllocRect(s, segment, entry, stse.layer, m_collisions);
2926 CPoint org;
2927 org.x = (s->m_scrAlignment%3) == 1 ? r.left : (s->m_scrAlignment%3) == 2 ? r.CenterPoint().x : r.right;
2928 org.y = s->m_scrAlignment <= 3 ? r.bottom : s->m_scrAlignment <= 6 ? r.CenterPoint().y : r.top;
2929 if(!fOrgOverride) org2 = org;
2930 BYTE* pAlphaMask = s->m_pClipper?s->m_pClipper->m_pAlphaMask:NULL;
2931 CPoint p, p2(0, r.top);
2932 POSITION pos;
2933 p = p2;
2934 // Rectangles for inverse clip
2935 CRect iclipRect[4];
2936 iclipRect[0] = CRect(0, 0, spd.w, clipRect.top);
2937 iclipRect[1] = CRect(0, clipRect.top, clipRect.left, clipRect.bottom);
2938 iclipRect[2] = CRect(clipRect.right, clipRect.top, spd.w, clipRect.bottom);
2939 iclipRect[3] = CRect(0, clipRect.bottom, spd.w, spd.h);
2940 int dbgTest = 0;
2941 bbox2 = CRect(0,0,0,0);
2942 pos = s->GetHeadPosition();
2943 while(pos)
2945 CLine* l = s->GetNext(pos);
2946 p.x = (s->m_scrAlignment%3) == 1 ? org.x
2947 : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
2948 : org.x - (l->m_width/2);
2949 if (s->m_clipInverse)
2951 bbox2 |= l->PaintShadow(spd, iclipRect[0], pAlphaMask, p, org2, m_time, alpha);
2952 bbox2 |= l->PaintShadow(spd, iclipRect[1], pAlphaMask, p, org2, m_time, alpha);
2953 bbox2 |= l->PaintShadow(spd, iclipRect[2], pAlphaMask, p, org2, m_time, alpha);
2954 bbox2 |= l->PaintShadow(spd, iclipRect[3], pAlphaMask, p, org2, m_time, alpha);
2956 else
2958 bbox2 |= l->PaintShadow(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
2960 p.y += l->m_ascent + l->m_descent;
2962 p = p2;
2963 pos = s->GetHeadPosition();
2964 while(pos)
2966 CLine* l = s->GetNext(pos);
2967 p.x = (s->m_scrAlignment%3) == 1 ? org.x
2968 : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
2969 : org.x - (l->m_width/2);
2970 if (s->m_clipInverse)
2972 bbox2 |= l->PaintOutline(spd, iclipRect[0], pAlphaMask, p, org2, m_time, alpha);
2973 bbox2 |= l->PaintOutline(spd, iclipRect[1], pAlphaMask, p, org2, m_time, alpha);
2974 bbox2 |= l->PaintOutline(spd, iclipRect[2], pAlphaMask, p, org2, m_time, alpha);
2975 bbox2 |= l->PaintOutline(spd, iclipRect[3], pAlphaMask, p, org2, m_time, alpha);
2977 else
2979 bbox2 |= l->PaintOutline(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
2981 p.y += l->m_ascent + l->m_descent;
2983 p = p2;
2984 pos = s->GetHeadPosition();
2985 while(pos)
2987 CLine* l = s->GetNext(pos);
2988 p.x = (s->m_scrAlignment%3) == 1 ? org.x
2989 : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
2990 : org.x - (l->m_width/2);
2991 if (s->m_clipInverse)
2993 bbox2 |= l->PaintBody(spd, iclipRect[0], pAlphaMask, p, org2, m_time, alpha);
2994 bbox2 |= l->PaintBody(spd, iclipRect[1], pAlphaMask, p, org2, m_time, alpha);
2995 bbox2 |= l->PaintBody(spd, iclipRect[2], pAlphaMask, p, org2, m_time, alpha);
2996 bbox2 |= l->PaintBody(spd, iclipRect[3], pAlphaMask, p, org2, m_time, alpha);
2998 else
3000 bbox2 |= l->PaintBody(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
3002 //DbgLog((LOG_TRACE,3,"%d line:%x bbox2 l:%d, t:%d, r:%d, b:%d", dbgTest++, l, bbox2->left, bbox2->top, bbox2->right, bbox2->bottom));
3003 p.y += l->m_ascent + l->m_descent;
3005 rectList.AddTail(bbox2);
3007 return (subs.GetCount() && !rectList.IsEmpty()) ? S_OK : S_FALSE;
3011 STDMETHODIMP CRenderedTextSubtitle::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
3013 CAtlList<CRect> rectList;
3014 HRESULT result = Render(spd, rt, fps, rectList);
3015 POSITION pos = rectList.GetHeadPosition();
3016 CRect bbox2(0,0,0,0);
3017 while(pos!=NULL)
3019 bbox2 |= rectList.GetNext(pos);
3021 bbox = bbox2;
3022 return result;
3025 // IPersist
3027 STDMETHODIMP CRenderedTextSubtitle::GetClassID(CLSID* pClassID)
3029 return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
3032 // ISubStream
3034 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStreamCount()
3036 return(1);
3039 STDMETHODIMP CRenderedTextSubtitle::GetStreamInfo(int iStream, WCHAR** ppName, LCID* pLCID)
3041 if(iStream != 0) return E_INVALIDARG;
3042 if(ppName)
3044 if(!(*ppName = (WCHAR*)CoTaskMemAlloc((m_name.GetLength()+1)*sizeof(WCHAR))))
3045 return E_OUTOFMEMORY;
3046 wcscpy(*ppName, CStringW(m_name));
3048 if(pLCID)
3050 *pLCID = 0; // TODO
3052 return S_OK;
3055 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStream()
3057 return(0);
3060 STDMETHODIMP CRenderedTextSubtitle::SetStream(int iStream)
3062 return iStream == 0 ? S_OK : E_FAIL;
3065 STDMETHODIMP CRenderedTextSubtitle::Reload()
3067 CFileStatus s;
3068 if(!CFile::GetStatus(m_path, s)) return E_FAIL;
3069 return !m_path.IsEmpty() && Open(m_path, DEFAULT_CHARSET) ? S_OK : E_FAIL;
3072 CWordCacheKey::CWordCacheKey( const CWord& word )
3074 m_str = word.m_str;
3075 m_style = word.m_style;
3076 m_ktype = word.m_ktype;
3077 m_kstart = word.m_kstart;
3078 m_kend = word.m_kend;
3081 CWordCacheKey::CWordCacheKey( const CWordCacheKey& key )
3083 m_str = key.m_str;
3084 m_style = key.m_style;
3085 m_ktype = key.m_ktype;
3086 m_kstart = key.m_kstart;
3087 m_kend = key.m_kend;
3090 CWordCacheKey::CWordCacheKey( const STSStyle& style, const CStringW& str, int ktype, int kstart, int kend )
3091 :m_style(style),m_str(str),m_ktype(ktype),m_kstart(kstart),m_kend(m_kend)
3096 bool CWordCacheKey::operator==( const CWordCacheKey& key ) const
3098 return (m_str == key.m_str &&
3099 m_style == key.m_style &&
3100 m_ktype == key.m_ktype &&
3101 m_kstart == key.m_kstart &&
3102 m_kend == key.m_kend);
3105 bool CWordCacheKey::operator==(const CWord& key)const
3107 return (m_str == key.m_str &&
3108 m_style == key.m_style &&
3109 m_ktype == key.m_ktype &&
3110 m_kstart == key.m_kstart &&
3111 m_kend == key.m_kend);
3114 void OverlayMruCache::update_cache( const OverlayMruItem& item )
3116 std::pair<iterator,bool> p=il.push_front(item);
3118 if(!p.second){ /* duplicate item */
3119 il.relocate(il.begin(),p.first); /* put in front */
3121 else if(il.size()>max_num_items){ /* keep the length <= max_num_items */
3122 delete il.rbegin()->overlay;
3123 il.pop_back();
3127 void OverlayMruCache::clear()
3129 for(OverlayMruCache::iterator iter=begin();iter!=end();iter++)
3131 delete iter->overlay;
3133 __super::clear();
3136 void CWordMruCache::update_cache( const CWordMruItem& item )
3138 std::pair<iterator,bool> p=il.push_front(item);
3140 if(!p.second){ /* duplicate item */
3141 il.relocate(il.begin(),p.first); /* put in front */
3143 else if(il.size()>max_num_items){ /* keep the length <= max_num_items */
3144 delete il.rbegin()->word;
3145 il.pop_back();
3149 void CWordMruCache::clear()
3151 for(CWordMruCache::iterator iter=begin();iter!=end();iter++)
3153 delete iter->word;
3155 __super::clear();