Changing from boost multi_index MRU to ATL style MRU.
[xy_vsfilter.git] / src / subtitles / RTS.cpp
blob09c3d76884eb23e19ed938b96ec1424c859c64b1
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 "cache_manager.h"
27 #include "../subpic/color_conv_table.h"
28 #include "subpixel_position_controler.h"
30 // WARNING: this isn't very thread safe, use only one RTS a time.
31 static HDC g_hDC;
32 static int g_hDC_refcnt = 0;
34 enum XY_MSP_SUBTYPE {XY_AYUV, XY_AUYV};
35 static inline DWORD rgb2yuv(DWORD argb, XY_MSP_SUBTYPE type)
37 const ColorConvTable* color_conv_table = ColorConvTable::GetDefaultColorConvTable();
38 DWORD axxv;
39 int r = (argb & 0x00ff0000) >> 16;
40 int g = (argb & 0x0000ff00) >> 8;
41 int b = (argb & 0x000000ff);
42 int y = (color_conv_table->c2y_cyb * b + color_conv_table->c2y_cyg * g + color_conv_table->c2y_cyr * r + 0x108000) >> 16;
43 int scaled_y = (y-16) * color_conv_table->cy_cy;
44 int u = ((((b<<16) - scaled_y) >> 10) * color_conv_table->c2y_cu + 0x800000 + 0x8000) >> 16;
45 int v = ((((r<<16) - scaled_y) >> 10) * color_conv_table->c2y_cv + 0x800000 + 0x8000) >> 16;
46 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));
47 u *= (u>0);
48 u = 255 - (255-u)*(u<256);
49 v *= (v>0);
50 v = 255 - (255-v)*(v<256);
51 DbgLog((LOG_TRACE, 5, TEXT("u=%x v=%x"), u, v));
52 if(type==XY_AYUV)
53 axxv = (argb & 0xff000000) | (y<<16) | (u<<8) | v;
54 else
55 axxv = (argb & 0xff000000) | (y<<8) | (u<<16) | v;
56 DbgLog((LOG_TRACE, 5, TEXT("axxv=%x"), axxv));
57 return axxv;
58 //return argb;
60 static long revcolor(long c)
62 return ((c&0xff0000)>>16) + (c&0xff00) + ((c&0xff)<<16);
65 // Skip all leading whitespace
66 inline CStringW::PCXSTR SkipWhiteSpaceLeft(const CStringW& str)
68 CStringW::PCXSTR psz = str.GetString();
70 while( iswspace( *psz ) )
72 psz++;
74 return psz;
77 // Skip all trailing whitespace
78 inline CStringW::PCXSTR SkipWhiteSpaceRight(const CStringW& str)
80 CStringW::PCXSTR psz = str.GetString();
81 CStringW::PCXSTR pszLast = psz + str.GetLength() - 1;
82 bool first_white = false;
83 while( iswspace( *pszLast ) )
85 pszLast--;
86 if(pszLast<psz)
87 break;
89 return pszLast;
92 // Skip all leading whitespace
93 inline CStringW::PCXSTR SkipWhiteSpaceLeft(CStringW::PCXSTR start, CStringW::PCXSTR end)
95 while( start!=end && iswspace( *start ) )
97 start++;
99 return start;
102 // Skip all trailing whitespace, first char must NOT be white space
103 inline CStringW::PCXSTR FastSkipWhiteSpaceRight(CStringW::PCXSTR start, CStringW::PCXSTR end)
105 while( iswspace( *--end ) );
106 return end+1;
109 inline CStringW::PCXSTR FindChar(CStringW::PCXSTR start, CStringW::PCXSTR end, WCHAR c)
111 while( start!=end && *start!=c )
113 start++;
115 return start;
118 //////////////////////////////////////////////////////////////////////////////////////////////
120 // CMyFont
122 CMyFont::CMyFont(const STSStyleBase& style)
124 LOGFONT lf;
125 memset(&lf, 0, sizeof(lf));
126 lf <<= style;
127 lf.lfHeight = (LONG)(style.fontSize+0.5);
128 lf.lfOutPrecision = OUT_TT_PRECIS;
129 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
130 lf.lfQuality = ANTIALIASED_QUALITY;
131 lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
132 if(!CreateFontIndirect(&lf))
134 _tcscpy(lf.lfFaceName, _T("Arial"));
135 CreateFontIndirect(&lf);
137 HFONT hOldFont = SelectFont(g_hDC, *this);
138 TEXTMETRIC tm;
139 GetTextMetrics(g_hDC, &tm);
140 m_ascent = ((tm.tmAscent + 4) >> 3);
141 m_descent = ((tm.tmDescent + 4) >> 3);
142 SelectFont(g_hDC, hOldFont);
145 // CWord
147 CWord::CWord(const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend)
148 : m_style(style), m_str(str)
149 , m_width(0), m_ascent(0), m_descent(0)
150 , m_ktype(ktype), m_kstart(kstart), m_kend(kend)
151 , m_fLineBreak(false), m_fWhiteSpaceChar(false)
152 //, m_pOpaqueBox(NULL)
154 if(m_str.IsEmpty())
156 m_fWhiteSpaceChar = m_fLineBreak = true;
158 m_width = 0;
161 CWord::CWord( const CWord& src)
163 m_str = src.m_str;
164 m_fWhiteSpaceChar = src.m_fWhiteSpaceChar;
165 m_fLineBreak = src.m_fLineBreak;
166 m_style = src.m_style;
167 m_pOpaqueBox = src.m_pOpaqueBox;//allow since it is shared_ptr
168 m_ktype = src.m_ktype;
169 m_kstart = src.m_kstart;
170 m_kend = src.m_kend;
171 m_width = src.m_width;
172 m_ascent = src.m_ascent;
173 m_descent = src.m_descent;
176 CWord::~CWord()
178 //if(m_pOpaqueBox) delete m_pOpaqueBox;
181 bool CWord::Append(const SharedPtrCWord& w)
183 if(!(m_style == w->m_style)
184 || m_fLineBreak || w->m_fLineBreak
185 || w->m_kstart != w->m_kend || m_ktype != w->m_ktype) return(false);
186 m_fWhiteSpaceChar = m_fWhiteSpaceChar && w->m_fWhiteSpaceChar;
187 m_str += w->m_str;
188 m_width += w->m_width;
189 return(true);
192 void CWord::Paint( SharedPtrCWord word, const CPoint& p, const CPoint& org, OverlayList* overlay_list )
194 if(!word->m_str || overlay_list==NULL) return;
195 bool error = false;
198 CPoint trans_org = org - p;
199 bool need_transform = word->NeedTransform();
200 if(!need_transform)
202 trans_org.x=0;
203 trans_org.y=0;
206 if( SubpixelPositionControler::GetGlobalControler().UseBilinearShift() )
208 CPoint psub_true( (p.x&SubpixelPositionControler::EIGHT_X_EIGHT_MASK), (p.y&SubpixelPositionControler::EIGHT_X_EIGHT_MASK) );
209 OverlayKey sub_key(*word, psub_true, trans_org);
211 OverlayMruCache* overlay_cache = CacheManager::GetSubpixelVarianceCache();
213 OverlayMruCache::hashed_cache_const_iterator iter = overlay_cache->hash_find(sub_key);
214 if(iter!=overlay_cache->hash_end())
216 overlay_list->overlay = iter->overlay;
217 overlay_cache->update_cache( *iter );
220 if( !overlay_list->overlay )
222 CPoint psub = SubpixelPositionControler::GetGlobalControler().GetSubpixel(p);
223 OverlayKey overlay_key(*word, psub, trans_org);
224 OverlayMruCache* overlay_cache = CacheManager::GetOverlayMruCache();
225 OverlayMruCache::hashed_cache_const_iterator iter = overlay_cache->hash_find(overlay_key);
226 if(iter==overlay_cache->hash_end())
228 if( !word->DoPaint(psub, trans_org, &(overlay_list->overlay), overlay_key) )
230 error = true;
231 break;
233 OverlayMruItem item(overlay_key, overlay_list->overlay);
234 overlay_cache->update_cache(item);
236 else
238 overlay_list->overlay = iter->overlay;
239 overlay_cache->update_cache( *iter );
241 if( SubpixelPositionControler::GetGlobalControler().UseBilinearShift()
242 && (psub.x!=(p.x&SubpixelPositionControler::EIGHT_X_EIGHT_MASK)
243 || psub.y!=(p.y&SubpixelPositionControler::EIGHT_X_EIGHT_MASK)) )
245 overlay_list->overlay.reset(overlay_list->overlay->GetSubpixelVariance((p.x&SubpixelPositionControler::EIGHT_X_EIGHT_MASK) - psub.x,
246 (p.y&SubpixelPositionControler::EIGHT_X_EIGHT_MASK) - psub.y));
247 CPoint psub_true( (p.x&SubpixelPositionControler::EIGHT_X_EIGHT_MASK), (p.y&SubpixelPositionControler::EIGHT_X_EIGHT_MASK) );
248 OverlayKey sub_key(*word, psub_true, trans_org);
249 OverlayMruCache* overlay_cache = CacheManager::GetSubpixelVarianceCache();
250 OverlayMruItem item(sub_key, overlay_list->overlay);
251 overlay_cache->update_cache(item);
255 if(word->m_style.get().borderStyle == 1)
257 if(!word->CreateOpaqueBox())
259 error = true;
260 break;
262 overlay_list->next = new OverlayList();
263 Paint(word->m_pOpaqueBox, p, org, overlay_list->next);
265 } while(false);
266 if(error)
268 overlay_list->overlay.reset( new Overlay() );
272 bool CWord::DoPaint(const CPoint& psub, const CPoint& trans_org, SharedPtrOverlay* overlay, const OverlayKey& key)
274 //overlay->reset(new Overlay());
275 OverlayNoBlurMruCache* overlay_no_blur_cache = CacheManager::GetOverlayNoBlurMruCache();
276 POSITION pos = overlay_no_blur_cache->Lookup(key);
278 SharedPtrOverlay raterize_result;
279 if(pos==NULL)
281 raterize_result.reset(new Overlay());
283 SharedPtrConstScanLineData scan_line_data;
284 ScanLineDataMruCache* scan_line_data_cache = CacheManager::GetScanLineDataMruCache();
285 POSITION pos_scan_line_data = scan_line_data_cache->Lookup(key);
286 if(pos_scan_line_data==NULL)
288 //get outline path, if not cached, create it and cache a copy, else copy from cache
289 SharedPtrPathData path_data(new PathData());
290 PathDataMruCache* path_data_cache = CacheManager::GetPathDataMruCache();
291 POSITION pos_path = path_data_cache->Lookup(key);
292 if(pos_path==NULL)
294 if(!CreatePath(path_data))
296 return false;
299 SharedPtrPathData data(new PathData());
300 *data = *path_data;//important! copy not ref
301 path_data_cache->UpdateCache(key, data);
303 else
305 *path_data = *(path_data_cache->GetAt(pos_path)); //important! copy not ref
306 path_data_cache->UpdateCache( pos_path );
309 bool need_transform = NeedTransform();
310 if(need_transform)
311 Transform(path_data, CPoint(trans_org.x*8, trans_org.y*8));
313 SharedPtrScanLineData tmp(new ScanLineData());
314 if(!tmp->ScanConvert(path_data))
316 return false;
318 if(m_style.get().borderStyle == 0 && (m_style.get().outlineWidthX+m_style.get().outlineWidthY > 0))
320 if(!tmp->CreateWidenedRegion(static_cast<int>(m_style.get().outlineWidthX+0.5),
321 static_cast<int>(m_style.get().outlineWidthY+0.5)))
323 return false;
326 else if(m_style.get().borderStyle == 1)
328 if(!CreateOpaqueBox())
330 return false;
334 scan_line_data_cache->UpdateCache(key, tmp);
335 scan_line_data = tmp;
337 else
339 scan_line_data = scan_line_data_cache->GetAt(pos_scan_line_data);
340 scan_line_data_cache->UpdateCache( pos_scan_line_data );
342 if(!Rasterizer::Rasterize(*scan_line_data, psub.x, psub.y, raterize_result))
344 return false;
347 overlay_no_blur_cache->UpdateCache(key, raterize_result);
349 else
351 raterize_result = overlay_no_blur_cache->GetAt(pos);
352 overlay_no_blur_cache->UpdateCache( pos );
354 if( m_style.get().fBlur>0 || m_style.get().fGaussianBlur>0.000001 )
356 overlay->reset(new Overlay());
357 if(!Rasterizer::Blur(*raterize_result, m_style.get().fBlur, m_style.get().fGaussianBlur, *overlay))
359 *overlay = raterize_result;
362 else
364 *overlay = raterize_result;
366 return true;
369 bool CWord::NeedTransform()
371 return (fabs(m_style.get().fontScaleX - 100) > 0.000001) ||
372 (fabs(m_style.get().fontScaleY - 100) > 0.000001) ||
373 (fabs(m_style.get().fontAngleX) > 0.000001) ||
374 (fabs(m_style.get().fontAngleY) > 0.000001) ||
375 (fabs(m_style.get().fontAngleZ) > 0.000001) ||
376 (fabs(m_style.get().fontShiftX) > 0.000001) ||
377 (fabs(m_style.get().fontShiftY) > 0.000001);
380 void CWord::Transform(SharedPtrPathData path_data, const CPoint& org)
382 //// CPUID from VDub
383 //bool fSSE2 = !!(g_cpuid.m_flags & CCpuID::sse2);
385 //if(fSSE2) { // SSE code
386 // Transform_SSE2(path_data, org);
387 //} else // C-code
388 Transform_C(path_data, org);
391 void CWord::Transform_C(const SharedPtrPathData& path_data, const CPoint &org )
393 double scalex = m_style.get().fontScaleX/100;
394 double scaley = m_style.get().fontScaleY/100;
396 double caz = cos((3.1415/180)*m_style.get().fontAngleZ);
397 double saz = sin((3.1415/180)*m_style.get().fontAngleZ);
398 double cax = cos((3.1415/180)*m_style.get().fontAngleX);
399 double sax = sin((3.1415/180)*m_style.get().fontAngleX);
400 double cay = cos((3.1415/180)*m_style.get().fontAngleY);
401 double say = sin((3.1415/180)*m_style.get().fontAngleY);
403 #ifdef _VSMOD
404 // patch m003. random text points
405 double xrnd = m_style.get().mod_rand.X*100;
406 double yrnd = m_style.get().mod_rand.Y*100;
407 double zrnd = m_style.get().mod_rand.Z*100;
409 srand(m_style.get().mod_rand.Seed);
411 // patch m008. distort
412 int xsz,ysz;
413 double dst1x,dst1y,dst2x,dst2y,dst3x,dst3y;
414 int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
416 bool is_dist = m_style.get().mod_distort.enabled;
417 if (is_dist) {
418 for(int i = 0; i < path_data->mPathPoints; i++) {
419 if(minx > path_data->mpPathPoints[i].x) {
420 minx = path_data->mpPathPoints[i].x;
422 if(miny > path_data->mpPathPoints[i].y) {
423 miny = path_data->mpPathPoints[i].y;
425 if(maxx < path_data->mpPathPoints[i].x) {
426 maxx = path_data->mpPathPoints[i].x;
428 if(maxy < path_data->mpPathPoints[i].y) {
429 maxy = path_data->mpPathPoints[i].y;
433 xsz = max(maxx - minx, 0);
434 ysz = max(maxy - miny, 0);
436 dst1x = m_style.get().mod_distort.pointsx[0];
437 dst1y = m_style.get().mod_distort.pointsy[0];
438 dst2x = m_style.get().mod_distort.pointsx[1];
439 dst2y = m_style.get().mod_distort.pointsy[1];
440 dst3x = m_style.get().mod_distort.pointsx[2];
441 dst3y = m_style.get().mod_distort.pointsy[2];
443 #endif
445 for (int i = 0; i < path_data->mPathPoints; i++) {
446 double x, y, z, xx, yy, zz;
448 x = path_data->mpPathPoints[i].x;
449 y = path_data->mpPathPoints[i].y;
450 #ifdef _VSMOD
451 // patch m002. Z-coord
452 z = m_style.get().mod_z;
454 double u, v;
455 if (is_dist) {
456 u = (x-minx) / xsz;
457 v = (y-miny) / ysz;
459 x = minx+(0 + (dst1x - 0)*u + (dst3x-0)*v+(0+dst2x-dst1x-dst3x)*u*v)*xsz;
460 y = miny+(0 + (dst1y - 0)*u + (dst3y-0)*v+(0+dst2y-dst1y-dst3y)*u*v)*ysz;
461 //P = P0 + (P1 - P0)u + (P3 - P0)v + (P0 + P2 - P1 - P3)uv
464 // patch m003. random text points
465 x = xrnd > 0 ? (xrnd - rand() % (int)(xrnd * 2 + 1)) / 100.0 + x : x;
466 y = yrnd > 0 ? (yrnd - rand() % (int)(yrnd * 2 + 1)) / 100.0 + y : y;
467 z = zrnd > 0 ? (zrnd - rand() % (int)(zrnd * 2 + 1)) / 100.0 + z : z;
468 #else
469 z = 0;
470 #endif
471 double _x = x;
472 x = scalex * (x + m_style.get().fontShiftX * y) - org.x;
473 y = scaley * (y + m_style.get().fontShiftY * _x) - org.y;
475 xx = x*caz + y*saz;
476 yy = -(x*saz - y*caz);
477 zz = z;
479 x = xx;
480 y = yy*cax + zz*sax;
481 z = yy*sax - zz*cax;
483 xx = x*cay + z*say;
484 yy = y;
485 zz = x*say - z*cay;
487 zz = max(zz, -19000);
489 x = (xx * 20000) / (zz + 20000);
490 y = (yy * 20000) / (zz + 20000);
492 path_data->mpPathPoints[i].x = (LONG)(x + org.x + 0.5);
493 path_data->mpPathPoints[i].y = (LONG)(y + org.y + 0.5);
497 void CWord::Transform_SSE2(const SharedPtrPathData& path_data, const CPoint &org )
499 // __m128 union data type currently not supported with Intel C++ Compiler, so just call C version
500 #ifdef __ICL
501 Transform_C(org);
502 #else
503 // SSE code
504 // speed up ~1.5-1.7x
505 double scalex = m_style.get().fontScaleX/100;
506 double scaley = m_style.get().fontScaleY/100;
508 double caz = cos((3.1415/180)*m_style.get().fontAngleZ);
509 double saz = sin((3.1415/180)*m_style.get().fontAngleZ);
510 double cax = cos((3.1415/180)*m_style.get().fontAngleX);
511 double sax = sin((3.1415/180)*m_style.get().fontAngleX);
512 double cay = cos((3.1415/180)*m_style.get().fontAngleY);
513 double say = sin((3.1415/180)*m_style.get().fontAngleY);
515 __m128 __xshift = _mm_set_ps1(m_style.get().fontShiftX);
516 __m128 __yshift = _mm_set_ps1(m_style.get().fontShiftY);
518 __m128 __xorg = _mm_set_ps1(org.x);
519 __m128 __yorg = _mm_set_ps1(org.y);
521 __m128 __xscale = _mm_set_ps1(scalex);
522 __m128 __yscale = _mm_set_ps1(scaley);
524 #ifdef _VSMOD
525 // patch m003. random text points
526 double xrnd = m_style.get().mod_rand.X*100;
527 double yrnd = m_style.get().mod_rand.Y*100;
528 double zrnd = m_style.get().mod_rand.Z*100;
530 srand(m_style.get().mod_rand.Seed);
532 __m128 __xsz = _mm_setzero_ps();
533 __m128 __ysz = _mm_setzero_ps();
535 __m128 __dst1x, __dst1y, __dst213x, __dst213y, __dst3x, __dst3y;
537 __m128 __miny;
538 __m128 __minx = _mm_set_ps(INT_MAX, INT_MAX, 0, 0);
539 __m128 __max = _mm_set_ps(-INT_MAX, -INT_MAX, 1, 1);
541 bool is_dist = m_style.get().mod_distort.enabled;
542 if(is_dist) {
543 for(int i = 0; i < path_data->mPathPoints; i++) {
544 __m128 __point = _mm_set_ps(path_data->mpPathPoints[i].x, path_data->mpPathPoints[i].y, 0, 0);
545 __minx = _mm_min_ps(__minx, __point);
546 __max = _mm_max_ps(__max, __point);
549 __m128 __zero = _mm_setzero_ps();
550 __max = _mm_sub_ps(__max, __minx); // xsz, ysz, 1, 1
551 __max = _mm_max_ps(__max, __zero);
553 __xsz = _mm_shuffle_ps(__max, __max, _MM_SHUFFLE(3,3,3,3));
554 __ysz = _mm_shuffle_ps(__max, __max, _MM_SHUFFLE(2,2,2,2));
556 __miny = _mm_shuffle_ps(__minx, __minx, _MM_SHUFFLE(2,2,2,2));
557 __minx = _mm_shuffle_ps(__minx, __minx, _MM_SHUFFLE(3,3,3,3));
559 __dst1x = _mm_set_ps1(m_style.get().mod_distort.pointsx[0]);
560 __dst1y = _mm_set_ps1(m_style.get().mod_distort.pointsy[0]);
561 __dst3x = _mm_set_ps1(m_style.get().mod_distort.pointsx[2]);
562 __dst3y = _mm_set_ps1(m_style.get().mod_distort.pointsy[2]);
563 __dst213x = _mm_set_ps1(m_style.get().mod_distort.pointsx[1]); // 2 - 1 - 3
564 __dst213x = _mm_sub_ps(__dst213x, __dst1x);
565 __dst213x = _mm_sub_ps(__dst213x, __dst3x);
567 __dst213y = _mm_set_ps1(m_style.get().mod_distort.pointsy[1]);
568 __dst213x = _mm_sub_ps(__dst213y, __dst1y);
569 __dst213x = _mm_sub_ps(__dst213y, __dst3y);
571 #endif
573 __m128 __caz = _mm_set_ps1(caz);
574 __m128 __saz = _mm_set_ps1(saz);
575 __m128 __cax = _mm_set_ps1(cax);
576 __m128 __sax = _mm_set_ps1(sax);
577 __m128 __cay = _mm_set_ps1(cay);
578 __m128 __say = _mm_set_ps1(say);
580 // this can be paralleled for openmp
581 int mPathPointsD4 = path_data->mPathPoints / 4;
582 int mPathPointsM4 = path_data->mPathPoints % 4;
584 for(ptrdiff_t i = 0; i < mPathPointsD4 + 1; i++) {
585 POINT* const temp_points = path_data->mpPathPoints + 4 * i;
587 __m128 __pointx, __pointy;
588 // we can't use load .-.
589 if(i != mPathPointsD4) {
590 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, temp_points[2].x, temp_points[3].x);
591 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, temp_points[2].y, temp_points[3].y);
592 } else { // last cycle
593 switch(mPathPointsM4) {
594 default:
595 case 0:
596 continue;
597 case 1:
598 __pointx = _mm_set_ps(temp_points[0].x, 0, 0, 0);
599 __pointy = _mm_set_ps(temp_points[0].y, 0, 0, 0);
600 break;
601 case 2:
602 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, 0, 0);
603 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, 0, 0);
604 break;
605 case 3:
606 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, temp_points[2].x, 0);
607 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, temp_points[2].y, 0);
608 break;
612 #ifdef _VSMOD
613 __m128 __pointz = _mm_set_ps1(m_style.get().mod_z);
615 // distort
616 if(is_dist) {
617 //P = P0 + (P1 - P0)u + (P3 - P0)v + (P0 + P2 - P1 - P3)uv
618 __m128 __u = _mm_sub_ps(__pointx, __minx);
619 __m128 __v = _mm_sub_ps(__pointy, __miny);
620 __m128 __1_xsz = _mm_rcp_ps(__xsz);
621 __m128 __1_ysz = _mm_rcp_ps(__ysz);
622 __u = _mm_mul_ps(__u, __1_xsz);
623 __v = _mm_mul_ps(__v, __1_ysz);
625 // x
626 __pointx = _mm_mul_ps(__dst213x, __u);
627 __pointx = _mm_mul_ps(__pointx, __v);
629 __m128 __tmpx = _mm_mul_ps(__dst3x, __v);
630 __pointx = _mm_add_ps(__pointx, __tmpx);
631 __tmpx = _mm_mul_ps(__dst1x, __u);
632 __pointx = _mm_add_ps(__pointx, __tmpx);
634 __pointx = _mm_mul_ps(__pointx, __xsz);
635 __pointx = _mm_add_ps(__pointx, __minx);
637 // y
638 __pointy = _mm_mul_ps(__dst213y, __u);
639 __pointy = _mm_mul_ps(__pointy, __v);
641 __m128 __tmpy = _mm_mul_ps(__dst3y, __v);
642 __pointy = _mm_add_ps(__pointy, __tmpy);
643 __tmpy = _mm_mul_ps(__dst1y, __u);
644 __pointy = _mm_add_ps(__pointy, __tmpy);
646 __pointy = _mm_mul_ps(__pointy, __ysz);
647 __pointy = _mm_add_ps(__pointy, __miny);
650 // randomize
651 if(xrnd!=0 || yrnd!=0 || zrnd!=0) {
652 __declspec(align(16)) float rx[4], ry[4], rz[4];
653 for(int k=0; k<4; k++) {
654 rx[k] = xrnd > 0 ? (xrnd - rand() % (int)(xrnd * 2 + 1)) : 0;
655 ry[k] = yrnd > 0 ? (yrnd - rand() % (int)(yrnd * 2 + 1)) : 0;
656 rz[k] = zrnd > 0 ? (zrnd - rand() % (int)(zrnd * 2 + 1)) : 0;
658 __m128 __001 = _mm_set_ps1(0.01f);
660 if(xrnd!=0) {
661 __m128 __rx = _mm_load_ps(rx);
662 __rx = _mm_mul_ps(__rx, __001);
663 __pointx = _mm_add_ps(__pointx, __rx);
666 if(yrnd!=0) {
667 __m128 __ry = _mm_load_ps(ry);
668 __ry = _mm_mul_ps(__ry, __001);
669 __pointy = _mm_add_ps(__pointy, __ry);
672 if(zrnd!=0) {
673 __m128 __rz = _mm_load_ps(rz);
674 __rz = _mm_mul_ps(__rz, __001);
675 __pointz = _mm_add_ps(__pointz, __rz);
678 #else
679 __m128 __pointz = _mm_set_ps1(0);
680 #endif
682 // scale and shift
683 __m128 __tmpx;
684 if(m_style.get().fontShiftX!=0) {
685 __tmpx = _mm_mul_ps(__xshift, __pointy);
686 __tmpx = _mm_add_ps(__tmpx, __pointx);
687 } else {
688 __tmpx = __pointx;
690 __tmpx = _mm_mul_ps(__tmpx, __xscale);
691 __tmpx = _mm_sub_ps(__tmpx, __xorg);
693 __m128 __tmpy;
694 if(m_style.get().fontShiftY!=0) {
695 __tmpy = _mm_mul_ps(__yshift, __pointx);
696 __tmpy = _mm_add_ps(__tmpy, __pointy);
697 } else {
698 __tmpy = __pointy;
700 __tmpy = _mm_mul_ps(__tmpy, __yscale);
701 __tmpy = _mm_sub_ps(__tmpy, __yorg);
703 // rotate
704 __m128 __xx = _mm_mul_ps(__tmpx, __caz);
705 __m128 __yy = _mm_mul_ps(__tmpy, __saz);
706 __pointx = _mm_add_ps(__xx, __yy);
707 __xx = _mm_mul_ps(__tmpx, __saz);
708 __yy = _mm_mul_ps(__tmpy, __caz);
709 __pointy = _mm_sub_ps(__yy, __xx);
711 __m128 __zz = _mm_mul_ps(__pointz, __sax);
712 __yy = _mm_mul_ps(__pointy, __cax);
713 __pointy = _mm_add_ps(__yy, __zz);
714 __zz = _mm_mul_ps(__pointz, __cax);
715 __yy = _mm_mul_ps(__pointy, __sax);
716 __pointz = _mm_sub_ps(__zz, __yy);
718 __xx = _mm_mul_ps(__pointx, __cay);
719 __zz = _mm_mul_ps(__pointz, __say);
720 __pointx = _mm_add_ps(__xx, __zz);
721 __xx = _mm_mul_ps(__pointx, __say);
722 __zz = _mm_mul_ps(__pointz, __cay);
723 __pointz = _mm_sub_ps(__xx, __zz);
725 __zz = _mm_set_ps1(-19000);
726 __pointz = _mm_max_ps(__pointz, __zz);
728 __m128 __20000 = _mm_set_ps1(20000);
729 __zz = _mm_add_ps(__pointz, __20000);
730 __zz = _mm_rcp_ps(__zz);
732 __pointx = _mm_mul_ps(__pointx, __20000);
733 __pointx = _mm_mul_ps(__pointx, __zz);
735 __pointy = _mm_mul_ps(__pointy, __20000);
736 __pointy = _mm_mul_ps(__pointy, __zz);
738 __pointx = _mm_add_ps(__pointx, __xorg);
739 __pointy = _mm_add_ps(__pointy, __yorg);
741 __m128 __05 = _mm_set_ps1(0.5);
743 __pointx = _mm_add_ps(__pointx, __05);
744 __pointy = _mm_add_ps(__pointy, __05);
746 if(i == mPathPointsD4) { // last cycle
747 for(int k=0; k<mPathPointsM4; k++) {
748 temp_points[k].x = static_cast<LONG>(__pointx.m128_f32[3-k]);
749 temp_points[k].y = static_cast<LONG>(__pointy.m128_f32[3-k]);
751 } else {
752 for(int k=0; k<4; k++) {
753 temp_points[k].x = static_cast<LONG>(__pointx.m128_f32[3-k]);
754 temp_points[k].y = static_cast<LONG>(__pointy.m128_f32[3-k]);
758 #endif // __ICL
761 bool CWord::CreateOpaqueBox()
763 if(m_pOpaqueBox) return(true);
764 STSStyle style = m_style.get();
765 style.borderStyle = 0;
766 style.outlineWidthX = style.outlineWidthY = 0;
767 style.colors[0] = m_style.get().colors[2];
768 style.alpha[0] = m_style.get().alpha[2];
769 int w = (int)(m_style.get().outlineWidthX + 0.5);
770 int h = (int)(m_style.get().outlineWidthY + 0.5);
771 CStringW str;
772 str.Format(L"m %d %d l %d %d %d %d %d %d",
773 -w, -h,
774 m_width+w, -h,
775 m_width+w, m_ascent+m_descent+h,
776 -w, m_ascent+m_descent+h);
777 m_pOpaqueBox.reset( new CPolygon(FwSTSStyle(style), str, 0, 0, 0, 1.0/8, 1.0/8, 0) );
778 return(!!m_pOpaqueBox);
781 // CText
783 CText::CText(const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend)
784 : CWord(style, str, ktype, kstart, kend)
786 if(m_str == L" ")
788 m_fWhiteSpaceChar = true;
790 SharedPtrTextInfo text_info;
791 TextInfoCacheKey text_info_key;
792 text_info_key.m_str = m_str;
793 text_info_key.m_style = m_style;
794 TextInfoMruCache* text_info_cache = CacheManager::GetTextInfoCache();
795 TextInfoMruCache::hashed_cache_const_iterator iter = text_info_cache->hash_find(text_info_key);
796 if(iter==text_info_cache->hash_end())
798 TextInfo* tmp=new TextInfo();
799 GetTextInfo(tmp, m_style, m_str);
800 text_info.reset(tmp);
801 TextInfoMruItem item(text_info_key, text_info);
802 text_info_cache->update_cache(item);
804 else
806 text_info = iter->text_info;
807 text_info_cache->update_cache( *iter );
809 this->m_ascent = text_info->m_ascent;
810 this->m_descent = text_info->m_descent;
811 this->m_width = text_info->m_width;
814 CText::CText( const CText& src ):CWord(src)
816 m_width = src.m_width;
819 SharedPtrCWord CText::Copy()
821 SharedPtrCWord result(new CText(*this));
822 return result;
825 bool CText::Append(const SharedPtrCWord& w)
827 return (w && CWord::Append(w));
830 bool CText::CreatePath(const SharedPtrPathData& path_data)
832 FwCMyFont font(m_style);
833 HFONT hOldFont = SelectFont(g_hDC, font.get());
834 int width = 0;
835 if(m_style.get().fontSpacing || (long)GetVersion() < 0)
837 bool bFirstPath = true;
838 for(LPCWSTR s = m_str; *s; s++)
840 CSize extent;
841 if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
842 path_data->PartialBeginPath(g_hDC, bFirstPath);
843 bFirstPath = false;
844 TextOutW(g_hDC, 0, 0, s, 1);
845 path_data->PartialEndPath(g_hDC, width, 0);
846 width += extent.cx + (int)m_style.get().fontSpacing;
849 else
851 CSize extent;
852 if(!GetTextExtentPoint32W(g_hDC, m_str, m_str.GetLength(), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
853 path_data->BeginPath(g_hDC);
854 TextOutW(g_hDC, 0, 0, m_str, m_str.GetLength());
855 path_data->EndPath(g_hDC);
857 SelectFont(g_hDC, hOldFont);
858 return(true);
861 void CText::GetTextInfo(TextInfo *output, const FwSTSStyle& style, const CStringW& str )
863 FwCMyFont font(style);
864 output->m_ascent = (int)(style.get().fontScaleY/100*font.get().m_ascent);
865 output->m_descent = (int)(style.get().fontScaleY/100*font.get().m_descent);
867 HFONT hOldFont = SelectFont(g_hDC, font.get());
868 if(style.get().fontSpacing || (long)GetVersion() < 0)
870 bool bFirstPath = true;
871 for(LPCWSTR s = str; *s; s++)
873 CSize extent;
874 if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
875 output->m_width += extent.cx + (int)style.get().fontSpacing;
877 // m_width -= (int)m_style.get().fontSpacing; // TODO: subtract only at the end of the line
879 else
881 CSize extent;
882 if(!GetTextExtentPoint32W(g_hDC, str, wcslen(str), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
883 output->m_width += extent.cx;
885 output->m_width = (int)(style.get().fontScaleX/100*output->m_width + 4) >> 3;
886 SelectFont(g_hDC, hOldFont);
889 // CPolygon
891 CPolygon::CPolygon(const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend, double scalex, double scaley, int baseline)
892 : CWord(style, str, ktype, kstart, kend)
893 , m_scalex(scalex), m_scaley(scaley), m_baseline(baseline)
895 ParseStr();
898 CPolygon::CPolygon(CPolygon& src) : CWord(src)
900 m_scalex = src.m_scalex;
901 m_scaley = src.m_scaley;
902 m_baseline = src.m_baseline;
903 m_width = src.m_width;
904 m_ascent = src.m_ascent;
905 m_descent = src.m_descent;
906 m_pathTypesOrg.Copy(src.m_pathTypesOrg);
907 m_pathPointsOrg.Copy(src.m_pathPointsOrg);
909 CPolygon::~CPolygon()
913 SharedPtrCWord CPolygon::Copy()
915 SharedPtrCWord result(DNew CPolygon(*this));
916 return result;
919 bool CPolygon::Append(const SharedPtrCWord& w)
921 // TODO
922 return(false);
925 bool CPolygon::GetLONG(CStringW& str, LONG& ret)
927 LPWSTR s = (LPWSTR)(LPCWSTR)str, e = s;
928 ret = wcstol(str, &e, 10);
929 str = str.Mid(e - s);
930 return(e > s);
933 bool CPolygon::GetPOINT(CStringW& str, POINT& ret)
935 return(GetLONG(str, ret.x) && GetLONG(str, ret.y));
938 bool CPolygon::ParseStr()
940 if(m_pathTypesOrg.GetCount() > 0) return(true);
941 CPoint p;
942 int j, lastsplinestart = -1, firstmoveto = -1, lastmoveto = -1;
943 CStringW str = m_str;
944 str.SpanIncluding(L"mnlbspc 0123456789");
945 str.Replace(L"m", L"*m");
946 str.Replace(L"n", L"*n");
947 str.Replace(L"l", L"*l");
948 str.Replace(L"b", L"*b");
949 str.Replace(L"s", L"*s");
950 str.Replace(L"p", L"*p");
951 str.Replace(L"c", L"*c");
952 int k = 0;
953 for(CStringW s = str.Tokenize(L"*", k); !s.IsEmpty(); s = str.Tokenize(L"*", k))
955 WCHAR c = s[0];
956 s.TrimLeft(L"mnlbspc ");
957 switch(c)
959 case 'm':
960 lastmoveto = m_pathTypesOrg.GetCount();
961 if(firstmoveto == -1) firstmoveto = lastmoveto;
962 while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_MOVETO); m_pathPointsOrg.Add(p);}
963 break;
964 case 'n':
965 while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_MOVETONC); m_pathPointsOrg.Add(p);}
966 break;
967 case 'l':
968 while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_LINETO); m_pathPointsOrg.Add(p);}
969 break;
970 case 'b':
971 j = m_pathTypesOrg.GetCount();
972 while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BEZIERTO); m_pathPointsOrg.Add(p); j++;}
973 j = m_pathTypesOrg.GetCount() - ((m_pathTypesOrg.GetCount()-j)%3);
974 m_pathTypesOrg.SetCount(j);
975 m_pathPointsOrg.SetCount(j);
976 break;
977 case 's':
979 j = lastsplinestart = m_pathTypesOrg.GetCount();
980 int i = 3;
981 while(i-- && GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BSPLINETO); m_pathPointsOrg.Add(p); j++;}
982 if(m_pathTypesOrg.GetCount()-lastsplinestart < 3) {m_pathTypesOrg.SetCount(lastsplinestart); m_pathPointsOrg.SetCount(lastsplinestart); lastsplinestart = -1;}
984 // no break here
985 case 'p':
986 while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BSPLINEPATCHTO); m_pathPointsOrg.Add(p); j++;}
987 break;
988 case 'c':
989 if(lastsplinestart > 0)
991 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
992 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
993 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
994 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)
995 m_pathPointsOrg.Add(p);
996 p = m_pathPointsOrg[lastsplinestart];
997 m_pathPointsOrg.Add(p);
998 p = m_pathPointsOrg[lastsplinestart+1];
999 m_pathPointsOrg.Add(p);
1000 lastsplinestart = -1;
1002 break;
1003 default:
1004 break;
1008 LPCWSTR str = m_str;
1009 while(*str)
1011 while(*str && *str != 'm' && *str != 'n' && *str != 'l' && *str != 'b' && *str != 's' && *str != 'p' && *str != 'c') str++;
1013 if(!*str) break;
1015 switch(*str++)
1017 case 'm':
1018 lastmoveto = m_pathTypesOrg.GetCount();
1019 if(firstmoveto == -1) firstmoveto = lastmoveto;
1020 while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_MOVETO); m_pathPointsOrg.Add(p);}
1021 break;
1022 case 'n':
1023 while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_MOVETONC); m_pathPointsOrg.Add(p);}
1024 break;
1025 case 'l':
1026 while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_LINETO); m_pathPointsOrg.Add(p);}
1027 break;
1028 case 'b':
1029 j = m_pathTypesOrg.GetCount();
1030 while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BEZIERTO); m_pathPointsOrg.Add(p); j++;}
1031 j = m_pathTypesOrg.GetCount() - ((m_pathTypesOrg.GetCount()-j)%3);
1032 m_pathTypesOrg.SetCount(j); m_pathPointsOrg.SetCount(j);
1033 break;
1034 case 's':
1035 j = lastsplinestart = m_pathTypesOrg.GetCount();
1036 i = 3;
1037 while(i-- && GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BSPLINETO); m_pathPointsOrg.Add(p); j++;}
1038 if(m_pathTypesOrg.GetCount()-lastsplinestart < 3) {m_pathTypesOrg.SetCount(lastsplinestart); m_pathPointsOrg.SetCount(lastsplinestart); lastsplinestart = -1;}
1039 // no break here
1040 case 'p':
1041 while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BSPLINEPATCHTO); m_pathPointsOrg.Add(p); j++;}
1042 break;
1043 case 'c':
1044 if(lastsplinestart > 0)
1046 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1047 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1048 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1049 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)
1050 m_pathPointsOrg.Add(p);
1051 p = m_pathPointsOrg[lastsplinestart];
1052 m_pathPointsOrg.Add(p);
1053 p = m_pathPointsOrg[lastsplinestart+1];
1054 m_pathPointsOrg.Add(p);
1055 lastsplinestart = -1;
1057 break;
1058 default:
1059 break;
1062 if(firstmoveto > 0) break;
1065 if(lastmoveto == -1 || firstmoveto > 0)
1067 m_pathTypesOrg.RemoveAll();
1068 m_pathPointsOrg.RemoveAll();
1069 return(false);
1071 int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
1072 for(size_t i = 0; i < m_pathTypesOrg.GetCount(); i++)
1074 m_pathPointsOrg[i].x = (int)(64 * m_scalex * m_pathPointsOrg[i].x);
1075 m_pathPointsOrg[i].y = (int)(64 * m_scaley * m_pathPointsOrg[i].y);
1076 if(minx > m_pathPointsOrg[i].x) minx = m_pathPointsOrg[i].x;
1077 if(miny > m_pathPointsOrg[i].y) miny = m_pathPointsOrg[i].y;
1078 if(maxx < m_pathPointsOrg[i].x) maxx = m_pathPointsOrg[i].x;
1079 if(maxy < m_pathPointsOrg[i].y) maxy = m_pathPointsOrg[i].y;
1081 m_width = max(maxx - minx, 0);
1082 m_ascent = max(maxy - miny, 0);
1083 int baseline = (int)(64 * m_scaley * m_baseline);
1084 m_descent = baseline;
1085 m_ascent -= baseline;
1086 m_width = ((int)(m_style.get().fontScaleX/100 * m_width) + 4) >> 3;
1087 m_ascent = ((int)(m_style.get().fontScaleY/100 * m_ascent) + 4) >> 3;
1088 m_descent = ((int)(m_style.get().fontScaleY/100 * m_descent) + 4) >> 3;
1089 return(true);
1092 bool CPolygon::CreatePath(const SharedPtrPathData& path_data)
1094 int len = m_pathTypesOrg.GetCount();
1095 if(len == 0) return(false);
1096 if(path_data->mPathPoints != len)
1098 path_data->mpPathTypes = (BYTE*)realloc(path_data->mpPathTypes, len*sizeof(BYTE));
1099 path_data->mpPathPoints = (POINT*)realloc(path_data->mpPathPoints, len*sizeof(POINT));
1100 if(!path_data->mpPathTypes || !path_data->mpPathPoints) return(false);
1101 path_data->mPathPoints = len;
1103 memcpy(path_data->mpPathTypes, m_pathTypesOrg.GetData(), len*sizeof(BYTE));
1104 memcpy(path_data->mpPathPoints, m_pathPointsOrg.GetData(), len*sizeof(POINT));
1105 return(true);
1108 // CClipper
1110 CClipper::CClipper(CStringW str, CSize size, double scalex, double scaley, bool inverse)
1111 : m_polygon( new CPolygon(FwSTSStyle(), str, 0, 0, 0, scalex, scaley, 0) )
1113 m_size.cx = m_size.cy = 0;
1114 //m_pAlphaMask = NULL;
1115 if(size.cx < 0 || size.cy < 0)
1116 return;
1117 m_pAlphaMask.reset(new BYTE[size.cx*size.cy]);
1118 if( !m_pAlphaMask )
1119 return;
1120 m_size = size;
1121 m_inverse = inverse;
1122 memset( m_pAlphaMask.get(), 0, size.cx*size.cy);
1123 OverlayList overlay_list;
1124 CWord::Paint( m_polygon, CPoint(0, 0), CPoint(0, 0), &overlay_list );
1125 int w = overlay_list.overlay->mOverlayWidth, h = overlay_list.overlay->mOverlayHeight;
1126 int x = (overlay_list.overlay->mOffsetX+4)>>3, y = (overlay_list.overlay->mOffsetY+4)>>3;
1127 int xo = 0, yo = 0;
1128 if(x < 0) {xo = -x; w -= -x; x = 0;}
1129 if(y < 0) {yo = -y; h -= -y; y = 0;}
1130 if(x+w > m_size.cx) w = m_size.cx-x;
1131 if(y+h > m_size.cy) h = m_size.cy-y;
1132 if(w <= 0 || h <= 0) return;
1133 const BYTE* src = overlay_list.overlay->mpOverlayBuffer.body + (overlay_list.overlay->mOverlayPitch * yo + xo);
1134 BYTE* dst = m_pAlphaMask.get() + m_size.cx * y + x;
1135 while(h--)
1137 //for(int wt=0; wt<w; ++wt)
1138 // dst[wt] = src[wt];
1139 memcpy(dst, src, w);
1140 src += overlay_list.overlay->mOverlayPitch;
1141 dst += m_size.cx;
1143 if(inverse)
1145 BYTE* dst = m_pAlphaMask.get();
1146 for(int i = size.cx*size.cy; i>0; --i, ++dst)
1147 *dst = 0x40 - *dst; // mask is 6 bit
1151 CClipper::~CClipper()
1153 m_pAlphaMask.reset(NULL);
1156 // CLine
1158 CLine::~CLine()
1160 //POSITION pos = GetHeadPosition();
1161 //while(pos) delete GetNext(pos);
1164 void CLine::Compact()
1166 POSITION pos = GetHeadPosition();
1167 while(pos)
1169 SharedPtrCWord w = GetNext(pos);
1170 if(!w->m_fWhiteSpaceChar) break;
1171 m_width -= w->m_width;
1172 // delete w;
1173 RemoveHead();
1175 pos = GetTailPosition();
1176 while(pos)
1178 SharedPtrCWord w = GetPrev(pos);
1179 if(!w->m_fWhiteSpaceChar) break;
1180 m_width -= w->m_width;
1181 // delete w;
1182 RemoveTail();
1184 if(IsEmpty()) return;
1185 CLine l;
1186 l.AddTailList(this);
1187 RemoveAll();
1188 SharedPtrCWord last;
1189 pos = l.GetHeadPosition();
1190 while(pos)
1192 SharedPtrCWord w = l.GetNext(pos);
1193 if(!last || !last->Append(w))
1194 AddTail(last = w->Copy());
1196 m_ascent = m_descent = m_borderX = m_borderY = 0;
1197 pos = GetHeadPosition();
1198 while(pos)
1200 SharedPtrCWord w = GetNext(pos);
1201 if(m_ascent < w->m_ascent) m_ascent = w->m_ascent;
1202 if(m_descent < w->m_descent) m_descent = w->m_descent;
1203 if(m_borderX < w->m_style.get().outlineWidthX) m_borderX = (int)(w->m_style.get().outlineWidthX+0.5);
1204 if(m_borderY < w->m_style.get().outlineWidthY) m_borderY = (int)(w->m_style.get().outlineWidthY+0.5);
1208 CRect CLine::PaintShadow(CompositeDrawItemList* output, SubPicDesc& spd, CRect& clipRect, SharedArrayByte pAlphaMask, CPoint p, CPoint org, int time, int alpha)
1210 CRect bbox(0, 0, 0, 0);
1211 POSITION pos = GetHeadPosition();
1212 POSITION outputPos = output->GetHeadPosition();
1213 while(pos)
1215 SharedPtrCWord w = GetNext(pos);
1216 CompositeDrawItem& outputItem = output->GetNext(outputPos);
1218 if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
1219 if(w->m_style.get().shadowDepthX != 0 || w->m_style.get().shadowDepthY != 0)
1221 int x = p.x + (int)(w->m_style.get().shadowDepthX+0.5);
1222 int y = p.y + m_ascent - w->m_ascent + (int)(w->m_style.get().shadowDepthY+0.5);
1223 DWORD a = 0xff - w->m_style.get().alpha[3];
1224 if(alpha > 0) a = MulDiv(a, 0xff - alpha, 0xff);
1225 COLORREF shadow = revcolor(w->m_style.get().colors[3]) | (a<<24);
1226 DWORD sw[6] = {shadow, -1};
1227 //xy
1228 if(spd.type == MSP_AUYV)
1230 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1232 else if(spd.type == MSP_AYUV || spd.type == MSP_AY11)
1234 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1236 OverlayList overlay_list;
1237 CWord::Paint(w, CPoint(x, y), org, &overlay_list);
1238 if(w->m_style.get().borderStyle == 0)
1240 outputItem.shadow.reset(
1241 Rasterizer::CreateDrawItem(spd, overlay_list.overlay, clipRect, pAlphaMask, x, y, sw,
1242 w->m_ktype > 0 || w->m_style.get().alpha[0] < 0xff,
1243 (w->m_style.get().outlineWidthX+w->m_style.get().outlineWidthY > 0) && !(w->m_ktype == 2 && time < w->m_kstart))
1245 bbox |= Rasterizer::DryDraw(spd, *outputItem.shadow);
1247 else if(w->m_style.get().borderStyle == 1 && w->m_pOpaqueBox)
1249 outputItem.shadow.reset(
1250 Rasterizer::CreateDrawItem(spd, overlay_list.next->overlay, clipRect, pAlphaMask, x, y, sw, true, false)
1252 bbox |= Rasterizer::DryDraw(spd, *outputItem.shadow);
1255 p.x += w->m_width;
1257 return(bbox);
1260 CRect CLine::PaintOutline(CompositeDrawItemList* output, SubPicDesc& spd, CRect& clipRect, SharedArrayByte pAlphaMask, CPoint p, CPoint org, int time, int alpha)
1262 CRect bbox(0, 0, 0, 0);
1263 POSITION pos = GetHeadPosition();
1264 POSITION outputPos = output->GetHeadPosition();
1265 while(pos)
1267 SharedPtrCWord w = GetNext(pos);
1268 CompositeDrawItem& outputItem = output->GetNext(outputPos);
1269 if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
1270 if(w->m_style.get().outlineWidthX+w->m_style.get().outlineWidthY > 0 && !(w->m_ktype == 2 && time < w->m_kstart))
1272 int x = p.x;
1273 int y = p.y + m_ascent - w->m_ascent;
1274 DWORD aoutline = w->m_style.get().alpha[2];
1275 if(alpha > 0) aoutline += MulDiv(alpha, 0xff - w->m_style.get().alpha[2], 0xff);
1276 COLORREF outline = revcolor(w->m_style.get().colors[2]) | ((0xff-aoutline)<<24);
1277 DWORD sw[6] = {outline, -1};
1278 //xy
1279 if(spd.type == MSP_AUYV)
1281 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1283 else if(spd.type == MSP_AYUV || spd.type == MSP_AY11)
1285 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1287 OverlayList overlay_list;
1288 CWord::Paint(w, CPoint(x, y), org, &overlay_list);
1289 if(w->m_style.get().borderStyle == 0)
1291 outputItem.outline.reset(
1292 Rasterizer::CreateDrawItem(spd, overlay_list.overlay, clipRect, pAlphaMask, x, y, sw, !w->m_style.get().alpha[0] && !w->m_style.get().alpha[1] && !alpha, true)
1294 bbox |= Rasterizer::DryDraw(spd, *outputItem.outline);
1296 else if(w->m_style.get().borderStyle == 1 && w->m_pOpaqueBox)
1298 outputItem.outline.reset(
1299 Rasterizer::CreateDrawItem(spd, overlay_list.next->overlay, clipRect, pAlphaMask, x, y, sw, true, false)
1301 bbox |= Rasterizer::DryDraw(spd, *outputItem.outline);
1304 p.x += w->m_width;
1306 return(bbox);
1309 CRect CLine::PaintBody(CompositeDrawItemList* output, SubPicDesc& spd, CRect& clipRect, SharedArrayByte pAlphaMask, CPoint p, CPoint org, int time, int alpha)
1311 CRect bbox(0, 0, 0, 0);
1312 POSITION pos = GetHeadPosition();
1313 POSITION outputPos = output->GetHeadPosition();
1314 while(pos)
1316 SharedPtrCWord w = GetNext(pos);
1317 CompositeDrawItem& outputItem = output->GetNext(outputPos);
1318 if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
1319 int x = p.x;
1320 int y = p.y + m_ascent - w->m_ascent;
1321 // colors
1322 DWORD aprimary = w->m_style.get().alpha[0];
1323 if(alpha > 0) aprimary += MulDiv(alpha, 0xff - w->m_style.get().alpha[0], 0xff);
1324 COLORREF primary = revcolor(w->m_style.get().colors[0]) | ((0xff-aprimary)<<24);
1325 DWORD asecondary = w->m_style.get().alpha[1];
1326 if(alpha > 0) asecondary += MulDiv(alpha, 0xff - w->m_style.get().alpha[1], 0xff);
1327 COLORREF secondary = revcolor(w->m_style.get().colors[1]) | ((0xff-asecondary)<<24);
1328 DWORD sw[6] = {primary, 0, secondary};
1329 // karaoke
1330 double t;
1331 if(w->m_ktype == 0 || w->m_ktype == 2)
1333 t = time < w->m_kstart ? 0 : 1;
1335 else if(w->m_ktype == 1)
1337 if(time < w->m_kstart) t = 0;
1338 else if(time < w->m_kend)
1340 t = 1.0 * (time - w->m_kstart) / (w->m_kend - w->m_kstart);
1341 double angle = fmod(w->m_style.get().fontAngleZ, 360.0);
1342 if(angle > 90 && angle < 270)
1344 t = 1-t;
1345 COLORREF tmp = sw[0];
1346 sw[0] = sw[2];
1347 sw[2] = tmp;
1350 else t = 1.0;
1352 if(t >= 1)
1354 sw[1] = 0xffffffff;
1356 sw[3] = (int)(w->m_style.get().outlineWidthX + t*w->m_width) >> 3;
1357 sw[4] = sw[2];
1358 sw[5] = 0x00ffffff;
1359 //xy
1360 if(spd.type == MSP_AUYV)
1362 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1363 sw[2] =rgb2yuv(sw[2], XY_AUYV);
1364 sw[4] =rgb2yuv(sw[4], XY_AUYV);
1366 else if(spd.type == MSP_AYUV || spd.type == MSP_AY11)
1368 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1369 sw[2] =rgb2yuv(sw[2], XY_AYUV);
1370 sw[4] =rgb2yuv(sw[4], XY_AYUV);
1372 OverlayList overlay_list;
1373 CWord::Paint(w, CPoint(x, y), org, &overlay_list);
1374 outputItem.body.reset(
1375 Rasterizer::CreateDrawItem(spd, overlay_list.overlay, clipRect, pAlphaMask, x, y, sw, true, false)
1377 bbox |= Rasterizer::DryDraw(spd, *outputItem.body);
1378 p.x += w->m_width;
1380 return(bbox);
1383 void CLine::AddWord2Tail( SharedPtrCWord words )
1385 __super::AddTail(words);
1388 bool CLine::IsEmpty()
1390 return __super::IsEmpty();
1393 int CLine::GetWordCount()
1395 return GetCount();
1399 // CSubtitle
1401 CSubtitle::CSubtitle()
1403 memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
1404 m_pClipper = NULL;
1405 m_clipInverse = false;
1406 m_scalex = m_scaley = 1;
1407 m_fAnimated2 = false;
1410 CSubtitle::~CSubtitle()
1412 Empty();
1415 void CSubtitle::Empty()
1417 POSITION pos = GetHeadPosition();
1418 while(pos) delete GetNext(pos);
1419 // pos = m_words.GetHeadPosition();
1420 // while(pos) delete m_words.GetNext(pos);
1421 for(int i = 0; i < EF_NUMBEROFEFFECTS; i++) {if(m_effects[i]) delete m_effects[i];}
1422 memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
1423 if(m_pClipper) delete m_pClipper;
1424 m_pClipper = NULL;
1427 int CSubtitle::GetFullWidth()
1429 int width = 0;
1430 POSITION pos = m_words.GetHeadPosition();
1431 while(pos) width += m_words.GetNext(pos)->m_width;
1432 return(width);
1435 int CSubtitle::GetFullLineWidth(POSITION pos)
1437 int width = 0;
1438 while(pos)
1440 SharedPtrCWord w = m_words.GetNext(pos);
1441 if(w->m_fLineBreak) break;
1442 width += w->m_width;
1444 return(width);
1447 int CSubtitle::GetWrapWidth(POSITION pos, int maxwidth)
1449 if(m_wrapStyle == 0 || m_wrapStyle == 3)
1451 if(maxwidth > 0)
1453 // int fullwidth = GetFullWidth();
1454 int fullwidth = GetFullLineWidth(pos);
1455 int minwidth = fullwidth / ((abs(fullwidth) / maxwidth) + 1);
1456 int width = 0, wordwidth = 0;
1457 while(pos && width < minwidth)
1459 SharedPtrCWord w = m_words.GetNext(pos);
1460 wordwidth = w->m_width;
1461 if(abs(width + wordwidth) < abs(maxwidth)) width += wordwidth;
1463 maxwidth = width;
1464 if(m_wrapStyle == 3 && pos) maxwidth -= wordwidth;
1467 else if(m_wrapStyle == 1)
1469 // maxwidth = maxwidth;
1471 else if(m_wrapStyle == 2)
1473 maxwidth = INT_MAX;
1475 return(maxwidth);
1478 CLine* CSubtitle::GetNextLine(POSITION& pos, int maxwidth)
1480 if(pos == NULL) return(NULL);
1481 CLine* ret = new CLine();
1482 if(!ret) return(NULL);
1483 ret->m_width = ret->m_ascent = ret->m_descent = ret->m_borderX = ret->m_borderY = 0;
1484 maxwidth = GetWrapWidth(pos, maxwidth);
1485 bool fEmptyLine = true;
1486 while(pos)
1488 SharedPtrCWord w = m_words.GetNext(pos);
1489 if(ret->m_ascent < w->m_ascent) ret->m_ascent = w->m_ascent;
1490 if(ret->m_descent < w->m_descent) ret->m_descent = w->m_descent;
1491 if(ret->m_borderX < w->m_style.get().outlineWidthX) ret->m_borderX = (int)(w->m_style.get().outlineWidthX+0.5);
1492 if(ret->m_borderY < w->m_style.get().outlineWidthY) ret->m_borderY = (int)(w->m_style.get().outlineWidthY+0.5);
1493 if(w->m_fLineBreak)
1495 if(fEmptyLine) {ret->m_ascent /= 2; ret->m_descent /= 2; ret->m_borderX = ret->m_borderY = 0;}
1496 ret->Compact();
1497 return(ret);
1499 fEmptyLine = false;
1500 bool fWSC = w->m_fWhiteSpaceChar;
1501 int width = w->m_width;
1502 POSITION pos2 = pos;
1503 while(pos2)
1505 if(m_words.GetAt(pos2)->m_fWhiteSpaceChar != fWSC
1506 || m_words.GetAt(pos2)->m_fLineBreak) break;
1507 SharedPtrCWord w2 = m_words.GetNext(pos2);
1508 width += w2->m_width;
1510 if((ret->m_width += width) <= maxwidth || ret->IsEmpty())
1512 ret->AddWord2Tail(w);
1513 while(pos != pos2)
1515 ret->AddWord2Tail(m_words.GetNext(pos));
1517 pos = pos2;
1519 else
1521 if(pos) m_words.GetPrev(pos);
1522 else pos = m_words.GetTailPosition();
1523 ret->m_width -= width;
1524 break;
1527 ret->Compact();
1528 return(ret);
1531 void CSubtitle::CreateClippers(CSize size)
1533 size.cx >>= 3;
1534 size.cy >>= 3;
1535 if(m_effects[EF_BANNER] && m_effects[EF_BANNER]->param[2])
1537 int width = m_effects[EF_BANNER]->param[2];
1538 int w = size.cx, h = size.cy;
1539 if(!m_pClipper)
1541 CStringW str;
1542 str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
1543 m_pClipper = new CClipper(str, size, 1, 1, false);
1544 if(!m_pClipper) return;
1546 int da = (64<<8)/width;
1547 BYTE* am = m_pClipper->m_pAlphaMask.get();
1548 for(int j = 0; j < h; j++, am += w)
1550 int a = 0;
1551 int k = min(width, w);
1552 for(int i = 0; i < k; i++, a += da)
1553 am[i] = (am[i]*a)>>14;
1554 a = 0x40<<8;
1555 k = w-width;
1556 if(k < 0) {a -= -k*da; k = 0;}
1557 for(int i = k; i < w; i++, a -= da)
1558 am[i] = (am[i]*a)>>14;
1561 else if(m_effects[EF_SCROLL] && m_effects[EF_SCROLL]->param[4])
1563 int height = m_effects[EF_SCROLL]->param[4];
1564 int w = size.cx, h = size.cy;
1565 if(!m_pClipper)
1567 CStringW str;
1568 str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
1569 m_pClipper = new CClipper(str, size, 1, 1, false);
1570 if(!m_pClipper) return;
1572 int da = (64<<8)/height;
1573 int a = 0;
1574 int k = m_effects[EF_SCROLL]->param[0]>>3;
1575 int l = k+height;
1576 if(k < 0) {a += -k*da; k = 0;}
1577 if(l > h) {l = h;}
1578 if(k < h)
1580 BYTE* am = &m_pClipper->m_pAlphaMask[k*w];
1581 memset(m_pClipper->m_pAlphaMask.get(), 0, am - m_pClipper->m_pAlphaMask.get());
1582 for(int j = k; j < l; j++, a += da)
1584 for(int i = 0; i < w; i++, am++)
1585 *am = ((*am)*a)>>14;
1588 da = -(64<<8)/height;
1589 a = 0x40<<8;
1590 l = m_effects[EF_SCROLL]->param[1]>>3;
1591 k = l-height;
1592 if(k < 0) {a += -k*da; k = 0;}
1593 if(l > h) {l = h;}
1594 if(k < h)
1596 BYTE* am = &m_pClipper->m_pAlphaMask[k*w];
1597 int j = k;
1598 for(; j < l; j++, a += da)
1600 for(int i = 0; i < w; i++, am++)
1601 *am = ((*am)*a)>>14;
1603 memset(am, 0, (h-j)*w);
1608 void CSubtitle::MakeLines(CSize size, CRect marginRect)
1610 CSize spaceNeeded(0, 0);
1611 bool fFirstLine = true;
1612 m_topborder = m_bottomborder = 0;
1613 CLine* l = NULL;
1614 POSITION pos = m_words.GetHeadPosition();
1615 while(pos)
1617 l = GetNextLine(pos, size.cx - marginRect.left - marginRect.right);
1618 if(!l) break;
1619 if(fFirstLine) {m_topborder = l->m_borderY; fFirstLine = false;}
1620 spaceNeeded.cx = max(l->m_width+l->m_borderX, spaceNeeded.cx);
1621 spaceNeeded.cy += l->m_ascent + l->m_descent;
1622 AddTail(l);
1624 if(l) m_bottomborder = l->m_borderY;
1625 m_rect = CRect(
1626 CPoint((m_scrAlignment%3) == 1 ? marginRect.left
1627 : (m_scrAlignment%3) == 2 ? (marginRect.left + (size.cx - marginRect.right) - spaceNeeded.cx + 1) / 2
1628 : (size.cx - marginRect.right - spaceNeeded.cx),
1629 m_scrAlignment <= 3 ? (size.cy - marginRect.bottom - spaceNeeded.cy)
1630 : m_scrAlignment <= 6 ? (marginRect.top + (size.cy - marginRect.bottom) - spaceNeeded.cy + 1) / 2
1631 : marginRect.top),
1632 spaceNeeded);
1635 POSITION CSubtitle::GetHeadLinePosition()
1637 return __super::GetHeadPosition();
1640 CLine* CSubtitle::GetNextLine( POSITION& pos )
1642 return __super::GetNext(pos);
1645 // CScreenLayoutAllocator
1647 void CScreenLayoutAllocator::Empty()
1649 m_subrects.RemoveAll();
1652 void CScreenLayoutAllocator::AdvanceToSegment(int segment, const CAtlArray<int>& sa)
1654 POSITION pos = m_subrects.GetHeadPosition();
1655 while(pos)
1657 POSITION prev = pos;
1658 SubRect& sr = m_subrects.GetNext(pos);
1659 bool fFound = false;
1660 if(abs(sr.segment - segment) <= 1) // using abs() makes it possible to play the subs backwards, too :)
1662 for(size_t i = 0; i < sa.GetCount() && !fFound; i++)
1664 if(sa[i] == sr.entry)
1666 sr.segment = segment;
1667 fFound = true;
1671 if(!fFound) m_subrects.RemoveAt(prev);
1675 CRect CScreenLayoutAllocator::AllocRect(CSubtitle* s, int segment, int entry, int layer, int collisions)
1677 // TODO: handle collisions == 1 (reversed collisions)
1678 POSITION pos = m_subrects.GetHeadPosition();
1679 while(pos)
1681 SubRect& sr = m_subrects.GetNext(pos);
1682 if(sr.segment == segment && sr.entry == entry)
1684 return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
1687 CRect r = s->m_rect + CRect(0, s->m_topborder, 0, s->m_bottomborder);
1688 bool fSearchDown = s->m_scrAlignment > 3;
1689 bool fOK;
1692 fOK = true;
1693 pos = m_subrects.GetHeadPosition();
1694 while(pos)
1696 SubRect& sr = m_subrects.GetNext(pos);
1697 if(layer == sr.layer && !(r & sr.r).IsRectEmpty())
1699 if(fSearchDown)
1701 r.bottom = sr.r.bottom + r.Height();
1702 r.top = sr.r.bottom;
1704 else
1706 r.top = sr.r.top - r.Height();
1707 r.bottom = sr.r.top;
1709 fOK = false;
1713 while(!fOK);
1714 SubRect sr;
1715 sr.r = r;
1716 sr.segment = segment;
1717 sr.entry = entry;
1718 sr.layer = layer;
1719 m_subrects.AddTail(sr);
1720 return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
1723 // CRenderedTextSubtitle
1725 CAtlMap<CStringW, CRenderedTextSubtitle::AssCmdType, CStringElementTraits<CStringW>> CRenderedTextSubtitle::m_cmdMap;
1727 CRenderedTextSubtitle::CRenderedTextSubtitle(CCritSec* pLock)
1728 : CSubPicProviderImpl(pLock)
1730 if( m_cmdMap.IsEmpty() )
1732 InitCmdMap();
1734 m_size = CSize(0, 0);
1735 if(g_hDC_refcnt == 0)
1737 g_hDC = CreateCompatibleDC(NULL);
1738 SetBkMode(g_hDC, TRANSPARENT);
1739 SetTextColor(g_hDC, 0xffffff);
1740 SetMapMode(g_hDC, MM_TEXT);
1742 g_hDC_refcnt++;
1745 CRenderedTextSubtitle::~CRenderedTextSubtitle()
1747 Deinit();
1748 g_hDC_refcnt--;
1749 if(g_hDC_refcnt == 0) DeleteDC(g_hDC);
1752 void CRenderedTextSubtitle::InitCmdMap()
1754 if( m_cmdMap.IsEmpty() )
1756 m_cmdMap.SetAt(L"1c", CMD_1c);
1757 m_cmdMap.SetAt(L"2c", CMD_2c);
1758 m_cmdMap.SetAt(L"3c", CMD_3c);
1759 m_cmdMap.SetAt(L"4c", CMD_4c);
1760 m_cmdMap.SetAt(L"1a", CMD_1a);
1761 m_cmdMap.SetAt(L"2a", CMD_2a);
1762 m_cmdMap.SetAt(L"3a", CMD_3a);
1763 m_cmdMap.SetAt(L"4a", CMD_4a);
1764 m_cmdMap.SetAt(L"alpha", CMD_alpha);
1765 m_cmdMap.SetAt(L"an", CMD_an);
1766 m_cmdMap.SetAt(L"a", CMD_a);
1767 m_cmdMap.SetAt(L"blur", CMD_blur);
1768 m_cmdMap.SetAt(L"bord", CMD_bord);
1769 m_cmdMap.SetAt(L"be", CMD_be);
1770 m_cmdMap.SetAt(L"b", CMD_b);
1771 m_cmdMap.SetAt(L"clip", CMD_clip);
1772 m_cmdMap.SetAt(L"iclip", CMD_iclip);
1773 m_cmdMap.SetAt(L"c", CMD_c);
1774 m_cmdMap.SetAt(L"fade", CMD_fade);
1775 m_cmdMap.SetAt(L"fad", CMD_fad);
1776 m_cmdMap.SetAt(L"fax", CMD_fax);
1777 m_cmdMap.SetAt(L"fay", CMD_fay);
1778 m_cmdMap.SetAt(L"fe", CMD_fe);
1779 m_cmdMap.SetAt(L"fn", CMD_fn);
1780 m_cmdMap.SetAt(L"frx", CMD_frx);
1781 m_cmdMap.SetAt(L"fry", CMD_fry);
1782 m_cmdMap.SetAt(L"frz", CMD_frz);
1783 m_cmdMap.SetAt(L"fr", CMD_fr);
1784 m_cmdMap.SetAt(L"fscx", CMD_fscx);
1785 m_cmdMap.SetAt(L"fscy", CMD_fscy);
1786 m_cmdMap.SetAt(L"fsc", CMD_fsc);
1787 m_cmdMap.SetAt(L"fsp", CMD_fsp);
1788 m_cmdMap.SetAt(L"fs", CMD_fs);
1789 m_cmdMap.SetAt(L"i", CMD_i);
1790 m_cmdMap.SetAt(L"kt", CMD_kt);
1791 m_cmdMap.SetAt(L"kf", CMD_kf);
1792 m_cmdMap.SetAt(L"K", CMD_K);
1793 m_cmdMap.SetAt(L"ko", CMD_ko);
1794 m_cmdMap.SetAt(L"k", CMD_k);
1795 m_cmdMap.SetAt(L"move", CMD_move);
1796 m_cmdMap.SetAt(L"org", CMD_org);
1797 m_cmdMap.SetAt(L"pbo", CMD_pbo);
1798 m_cmdMap.SetAt(L"pos", CMD_pos);
1799 m_cmdMap.SetAt(L"p", CMD_p);
1800 m_cmdMap.SetAt(L"q", CMD_q);
1801 m_cmdMap.SetAt(L"r", CMD_r);
1802 m_cmdMap.SetAt(L"shad", CMD_shad);
1803 m_cmdMap.SetAt(L"s", CMD_s);
1804 m_cmdMap.SetAt(L"t", CMD_t);
1805 m_cmdMap.SetAt(L"u", CMD_u);
1806 m_cmdMap.SetAt(L"xbord", CMD_xbord);
1807 m_cmdMap.SetAt(L"xshad", CMD_xshad);
1808 m_cmdMap.SetAt(L"ybord", CMD_ybord);
1809 m_cmdMap.SetAt(L"yshad", CMD_yshad);
1813 void CRenderedTextSubtitle::Copy(CRenderedTextSubtitle& rts)
1815 __super::Copy(rts);
1816 m_size = rts.m_size;
1819 void CRenderedTextSubtitle::Copy(CSimpleTextSubtitle& sts)
1821 __super::Copy(sts);
1824 void CRenderedTextSubtitle::Empty()
1826 Deinit();
1827 __super::Empty();
1830 void CRenderedTextSubtitle::OnChanged()
1832 __super::OnChanged();
1833 POSITION pos = m_subtitleCache.GetStartPosition();
1834 while(pos)
1836 int i;
1837 CSubtitle* s;
1838 m_subtitleCache.GetNextAssoc(pos, i, s);
1839 delete s;
1841 m_subtitleCache.RemoveAll();
1842 m_sla.Empty();
1845 bool CRenderedTextSubtitle::Init(CSize size, CRect vidrect)
1847 Deinit();
1848 m_size = CSize(size.cx*8, size.cy*8);
1849 m_vidrect = CRect(vidrect.left*8, vidrect.top*8, vidrect.right*8, vidrect.bottom*8);
1850 m_sla.Empty();
1851 return(true);
1854 void CRenderedTextSubtitle::Deinit()
1856 POSITION pos = m_subtitleCache.GetStartPosition();
1857 while(pos)
1859 int i;
1860 CSubtitle* s;
1861 m_subtitleCache.GetNextAssoc(pos, i, s);
1862 delete s;
1864 m_subtitleCache.RemoveAll();
1865 m_sla.Empty();
1866 m_size = CSize(0, 0);
1867 m_vidrect.SetRectEmpty();
1869 CacheManager::GetCWordMruCache()->clear();
1870 CacheManager::GetPathDataMruCache()->RemoveAll();
1871 CacheManager::GetScanLineDataMruCache()->RemoveAll();
1872 CacheManager::GetOverlayNoBlurMruCache()->RemoveAll();
1873 CacheManager::GetOverlayMruCache()->clear();
1876 void CRenderedTextSubtitle::ParseEffect(CSubtitle* sub, const CStringW& str)
1878 CStringW::PCXSTR str_start = str.GetString();
1879 CStringW::PCXSTR str_end = str_start + str.GetLength();
1880 str_start = SkipWhiteSpaceLeft(str_start, str_end);
1882 if(!sub || *str_start==0)
1883 return;
1885 str_end = FastSkipWhiteSpaceRight(str_start, str_end);
1887 const WCHAR* s = FindChar(str_start, str_end, L';');
1888 if(*s==L';') {
1889 s++;
1892 const CStringW effect(str_start, s-str_start);
1893 if(!effect.CompareNoCase( L"Banner;" ) )
1895 int delay, lefttoright = 0, fadeawaywidth = 0;
1896 if(swscanf(s, L"%d;%d;%d", &delay, &lefttoright, &fadeawaywidth) < 1) return;
1897 Effect* e = new Effect;
1898 if(!e) return;
1899 sub->m_effects[e->type = EF_BANNER] = e;
1900 e->param[0] = (int)(max(1.0*delay/sub->m_scalex, 1));
1901 e->param[1] = lefttoright;
1902 e->param[2] = (int)(sub->m_scalex*fadeawaywidth);
1903 sub->m_wrapStyle = 2;
1905 else if(!effect.CompareNoCase(L"Scroll up;") || !effect.CompareNoCase(L"Scroll down;"))
1907 int top, bottom, delay, fadeawayheight = 0;
1908 if(swscanf(s, L"%d;%d;%d;%d", &top, &bottom, &delay, &fadeawayheight) < 3) return;
1909 if(top > bottom) {int tmp = top; top = bottom; bottom = tmp;}
1910 Effect* e = new Effect;
1911 if(!e) return;
1912 sub->m_effects[e->type = EF_SCROLL] = e;
1913 e->param[0] = (int)(sub->m_scaley*top*8);
1914 e->param[1] = (int)(sub->m_scaley*bottom*8);
1915 e->param[2] = (int)(max(1.0*delay/sub->m_scaley, 1));
1916 e->param[3] = (effect.GetLength() == 12);
1917 e->param[4] = (int)(sub->m_scaley*fadeawayheight);
1921 void CRenderedTextSubtitle::ParseString(CSubtitle* sub, CStringW str, const FwSTSStyle& style)
1923 if(!sub) return;
1924 str.Replace(L"\\N", L"\n");
1925 str.Replace(L"\\n", (sub->m_wrapStyle < 2 || sub->m_wrapStyle == 3) ? L" " : L"\n");
1926 str.Replace(L"\\h", L"\x00A0");
1927 CWordMruCache* word_mru_cache=CacheManager::GetCWordMruCache();
1928 for(int ite = 0, j = 0, len = str.GetLength(); j <= len; j++)
1930 WCHAR c = str[j];
1931 if(c != L'\n' && c != L' ' && c != L'\x00A0' && c != 0)
1932 continue;
1933 if(ite < j)
1935 CWordCacheKey word_cache_key(style, str.Mid(ite, j-ite), m_ktype, m_kstart, m_kend);
1936 CWordMruCache::hashed_cache_const_iterator iter = word_mru_cache->hash_find(word_cache_key);
1937 if( iter != word_mru_cache->hash_end() )
1939 sub->m_words.AddTail(iter->word);
1941 else if(PCWord tmp_ptr = new CText(style, str.Mid(ite, j-ite), m_ktype, m_kstart, m_kend))
1943 SharedPtrCWord w(tmp_ptr);
1944 sub->m_words.AddTail(w);
1945 CWordMruItem item(word_cache_key, w);
1946 word_mru_cache->update_cache(item);
1948 else
1950 ///TODO: overflow handling
1952 m_kstart = m_kend;
1954 if(c == L'\n')
1956 CWordCacheKey word_cache_key(style, CStringW(), m_ktype, m_kstart, m_kend);
1957 CWordMruCache::hashed_cache_const_iterator iter = word_mru_cache->hash_find(word_cache_key);
1958 if( iter != word_mru_cache->hash_end() )
1960 sub->m_words.AddTail(iter->word);
1962 else if(PCWord tmp_ptr = new CText(style, CStringW(), m_ktype, m_kstart, m_kend))
1964 SharedPtrCWord w(tmp_ptr);
1965 sub->m_words.AddTail(w);
1966 CWordMruItem item(word_cache_key, w);
1967 word_mru_cache->update_cache(item);
1969 else
1971 ///TODO: overflow handling
1973 m_kstart = m_kend;
1975 else if(c == L' ' || c == L'\x00A0')
1977 CWordCacheKey word_cache_key(style, CStringW(c), m_ktype, m_kstart, m_kend);
1978 CWordMruCache::hashed_cache_const_iterator iter = word_mru_cache->hash_find(word_cache_key);
1979 if( iter != word_mru_cache->hash_end() )
1981 sub->m_words.AddTail(iter->word);
1983 else if(PCWord tmp_ptr = new CText(style, CStringW(c), m_ktype, m_kstart, m_kend))
1985 SharedPtrCWord w(tmp_ptr);
1986 sub->m_words.AddTail(w);
1987 CWordMruItem item(word_cache_key, w);
1988 word_mru_cache->update_cache(item);
1990 else
1992 ///TODO: overflow handling
1994 m_kstart = m_kend;
1996 ite = j+1;
1998 return;
2001 void CRenderedTextSubtitle::ParsePolygon(CSubtitle* sub, const CStringW& str, const FwSTSStyle& style)
2003 if(!sub || !str.GetLength() || !m_nPolygon) return;
2005 if(PCWord tmp_ptr = 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))
2007 SharedPtrCWord w(tmp_ptr);
2008 ///Todo: fix me
2009 //if( PCWord w_cache = m_wordCache.lookup(*w) )
2011 // sub->m_words.AddTail(w_cache);
2012 // delete w;
2014 //else
2016 sub->m_words.AddTail(w);
2018 m_kstart = m_kend;
2022 bool CRenderedTextSubtitle::ParseSSATag( AssTagList *assTags, const CStringW& str )
2024 if(!assTags) return(false);
2025 int nTags = 0, nUnrecognizedTags = 0;
2026 for(int i = 0, j; (j = str.Find(L'\\', i)) >= 0; i = j)
2028 POSITION pos = assTags->AddTail();
2029 AssTag& assTag = assTags->GetAt(pos);
2030 assTag.cmdType = CMD_COUNT;
2032 j++;
2033 CStringW::PCXSTR str_start = str.GetString() + j;
2034 CStringW::PCXSTR pc = str_start;
2035 while( iswspace(*pc) )
2037 pc++;
2039 j += pc-str_start;
2040 str_start = pc;
2041 while( *pc && *pc != L'(' && *pc != L'\\' )
2043 pc++;
2045 j += pc-str_start;
2046 if( pc-str_start>0 )
2048 while( iswspace(*--pc) );
2049 pc++;
2052 const CStringW cmd(str_start, pc-str_start);
2053 if(cmd.IsEmpty()) continue;
2055 CAtlArray<CStringW>& params = assTag.strParams;
2056 if(str[j] == L'(')
2058 j++;
2059 CStringW::PCXSTR str_start = str.GetString() + j;
2060 CStringW::PCXSTR pc = str_start;
2061 while( iswspace(*pc) )
2063 pc++;
2065 j += pc-str_start;
2066 str_start = pc;
2067 while( *pc && *pc != L')' )
2069 pc++;
2071 j += pc-str_start;
2072 if( pc-str_start>0 )
2074 while( iswspace(*--pc) );
2075 pc++;
2078 CStringW::PCXSTR param_start = str_start;
2079 CStringW::PCXSTR param_end = pc;
2080 while( param_start<param_end )
2082 param_start = SkipWhiteSpaceLeft(param_start, param_end);
2084 CStringW::PCXSTR newstart = FindChar(param_start, param_end, L',');
2085 CStringW::PCXSTR newend = FindChar(param_start, param_end, L'\\');
2086 if(newstart > param_start && newstart < newend)
2088 newstart = FastSkipWhiteSpaceRight(param_start, newstart);
2089 CStringW s(param_start, newstart - param_start);
2091 if(!s.IsEmpty()) params.Add(s);
2092 param_start = newstart + 1;
2094 else if(param_start<param_end)
2096 CStringW s(param_start, param_end - param_start);
2098 params.Add(s);
2099 param_start = param_end;
2104 AssCmdType cmd_type = CMD_COUNT;
2105 int cmd_length = min(MAX_CMD_LENGTH, cmd.GetLength());
2106 for( ;cmd_length>=MIN_CMD_LENGTH;cmd_length-- )
2108 if( m_cmdMap.Lookup(cmd.Left(cmd_length), cmd_type) )
2109 break;
2111 if(cmd_length<MIN_CMD_LENGTH)
2112 cmd_type = CMD_COUNT;
2113 switch( cmd_type )
2115 case CMD_fax:
2116 case CMD_fay:
2117 case CMD_fe:
2118 case CMD_fn:
2119 case CMD_frx:
2120 case CMD_fry:
2121 case CMD_frz:
2122 case CMD_fr:
2123 case CMD_fscx:
2124 case CMD_fscy:
2125 case CMD_fsc:
2126 case CMD_fsp:
2127 case CMD_fs:
2128 case CMD_i:
2129 case CMD_kt:
2130 case CMD_kf:
2131 case CMD_K:
2132 case CMD_ko:
2133 case CMD_k:
2134 case CMD_pbo:
2135 case CMD_p:
2136 case CMD_q:
2137 case CMD_r:
2138 case CMD_shad:
2139 case CMD_s:
2140 case CMD_an:
2141 case CMD_a:
2142 case CMD_blur:
2143 case CMD_bord:
2144 case CMD_be:
2145 case CMD_b:
2146 case CMD_u:
2147 case CMD_xbord:
2148 case CMD_xshad:
2149 case CMD_ybord:
2150 case CMD_yshad:
2151 // default:
2152 params.Add(cmd.Mid(cmd_length));
2153 break;
2154 case CMD_c:
2155 case CMD_1c :
2156 case CMD_2c :
2157 case CMD_3c :
2158 case CMD_4c :
2159 case CMD_1a :
2160 case CMD_2a :
2161 case CMD_3a :
2162 case CMD_4a :
2163 case CMD_alpha:
2164 params.Add(cmd.Mid(cmd_length).Trim(L"&H"));
2165 break;
2166 case CMD_clip:
2167 case CMD_iclip:
2168 case CMD_fade:
2169 case CMD_fad:
2170 case CMD_move:
2171 case CMD_org:
2172 case CMD_pos:
2173 break;
2174 case CMD_t:
2175 ParseSSATag(&assTag.embeded, params[params.GetCount()-1]);
2176 break;
2177 case CMD_COUNT:
2178 nUnrecognizedTags++;
2179 break;
2182 assTag.cmd = cmd;
2183 assTag.cmdType = cmd_type;
2185 nTags++;
2187 return(true);
2190 bool CRenderedTextSubtitle::ParseSSATag( CSubtitle* sub, const AssTagList& assTags, STSStyle& style, const STSStyle& org, bool fAnimate /*= false*/ )
2192 if(!sub) return(false);
2194 POSITION pos = assTags.GetHeadPosition();
2195 while(pos)
2197 const AssTag& assTag = assTags.GetNext(pos);
2198 const CStringW& cmd = assTag.cmd;
2199 AssCmdType cmd_type = assTag.cmdType;
2200 const CAtlArray<CStringW>& params = assTag.strParams;
2202 // TODO: call ParseStyleModifier(cmd, params, ..) and move the rest there
2203 const CStringW& p = params.GetCount() > 0 ? params[0] : CStringW("");
2204 switch ( cmd_type )
2206 case CMD_1c :
2207 case CMD_2c :
2208 case CMD_3c :
2209 case CMD_4c :
2211 int i = cmd[0] - L'1';
2212 DWORD c = wcstol(p, NULL, 16);
2213 style.colors[i] = !p.IsEmpty()
2214 ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
2215 |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
2216 |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
2217 : org.colors[i];
2218 break;
2220 case CMD_1a :
2221 case CMD_2a :
2222 case CMD_3a :
2223 case CMD_4a :
2225 int i = cmd[0] - L'1';
2226 style.alpha[i] = !p.IsEmpty()
2227 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2228 : org.alpha[i];
2229 break;
2231 case CMD_alpha:
2233 for(int i = 0; i < 4; i++)
2235 style.alpha[i] = !p.IsEmpty()
2236 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2237 : org.alpha[i];
2239 break;
2241 case CMD_an:
2243 int n = wcstol(p, NULL, 10);
2244 if(sub->m_scrAlignment < 0)
2245 sub->m_scrAlignment = (n > 0 && n < 10) ? n : org.scrAlignment;
2246 break;
2248 case CMD_a:
2250 int n = wcstol(p, NULL, 10);
2251 if(sub->m_scrAlignment < 0)
2252 sub->m_scrAlignment = (n > 0 && n < 12) ? ((((n-1)&3)+1)+((n&4)?6:0)+((n&8)?3:0)) : org.scrAlignment;
2253 break;
2255 case CMD_blur:
2257 double n = CalcAnimation(wcstod(p, NULL), style.fGaussianBlur, fAnimate);
2258 style.fGaussianBlur = !p.IsEmpty()
2259 ? (n < 0 ? 0 : n)
2260 : org.fGaussianBlur;
2261 break;
2263 case CMD_bord:
2265 double dst = wcstod(p, NULL);
2266 double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate);
2267 style.outlineWidthX = !p.IsEmpty()
2268 ? (nx < 0 ? 0 : nx)
2269 : org.outlineWidthX;
2270 double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate);
2271 style.outlineWidthY = !p.IsEmpty()
2272 ? (ny < 0 ? 0 : ny)
2273 : org.outlineWidthY;
2274 break;
2276 case CMD_be:
2278 int n = (int)(CalcAnimation(wcstol(p, NULL, 10), style.fBlur, fAnimate)+0.5);
2279 style.fBlur = !p.IsEmpty()
2281 : org.fBlur;
2282 break;
2284 case CMD_b:
2286 int n = wcstol(p, NULL, 10);
2287 style.fontWeight = !p.IsEmpty()
2288 ? (n == 0 ? FW_NORMAL : n == 1 ? FW_BOLD : n >= 100 ? n : org.fontWeight)
2289 : org.fontWeight;
2290 break;
2292 case CMD_clip:
2293 case CMD_iclip:
2295 bool invert = (cmd_type == CMD_iclip);
2296 if(params.GetCount() == 1 && !sub->m_pClipper)
2298 sub->m_pClipper = new CClipper(params[0], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex, sub->m_scaley, invert);
2300 else if(params.GetCount() == 2 && !sub->m_pClipper)
2302 int scale = max(wcstol(p, NULL, 10), 1);
2303 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);
2305 else if(params.GetCount() == 4)
2307 CRect r;
2308 sub->m_clipInverse = invert;
2309 r.SetRect(
2310 wcstol(params[0], NULL, 10),
2311 wcstol(params[1], NULL, 10),
2312 wcstol(params[2], NULL, 10),
2313 wcstol(params[3], NULL, 10));
2314 CPoint o(0, 0);
2315 if(sub->m_relativeTo == 1) // TODO: this should also apply to the other two clippings above
2317 o.x = m_vidrect.left>>3;
2318 o.y = m_vidrect.top>>3;
2320 sub->m_clip.SetRect(
2321 (int)CalcAnimation(sub->m_scalex*r.left + o.x, sub->m_clip.left, fAnimate),
2322 (int)CalcAnimation(sub->m_scaley*r.top + o.y, sub->m_clip.top, fAnimate),
2323 (int)CalcAnimation(sub->m_scalex*r.right + o.x, sub->m_clip.right, fAnimate),
2324 (int)CalcAnimation(sub->m_scaley*r.bottom + o.y, sub->m_clip.bottom, fAnimate));
2326 break;
2328 case CMD_c:
2330 DWORD c = wcstol(p, NULL, 16);
2331 style.colors[0] = !p.IsEmpty()
2332 ? (((int)CalcAnimation(c&0xff, style.colors[0]&0xff, fAnimate))&0xff
2333 |((int)CalcAnimation(c&0xff00, style.colors[0]&0xff00, fAnimate))&0xff00
2334 |((int)CalcAnimation(c&0xff0000, style.colors[0]&0xff0000, fAnimate))&0xff0000)
2335 : org.colors[0];
2336 break;
2338 case CMD_fade:
2339 case CMD_fad:
2341 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])
2343 if(Effect* e = new Effect)
2345 for(int i = 0; i < 3; i++)
2346 e->param[i] = wcstol(params[i], NULL, 10);
2347 for(int i = 0; i < 4; i++)
2348 e->t[i] = wcstol(params[3+i], NULL, 10);
2349 sub->m_effects[EF_FADE] = e;
2352 else if(params.GetCount() == 2 && !sub->m_effects[EF_FADE]) // {\fad(t1=t[1], t2=t[2])
2354 if(Effect* e = new Effect)
2356 e->param[0] = e->param[2] = 0xff;
2357 e->param[1] = 0x00;
2358 for(int i = 1; i < 3; i++)
2359 e->t[i] = wcstol(params[i-1], NULL, 10);
2360 e->t[0] = e->t[3] = -1; // will be substituted with "start" and "end"
2361 sub->m_effects[EF_FADE] = e;
2364 break;
2366 case CMD_fax:
2368 style.fontShiftX = !p.IsEmpty()
2369 ? CalcAnimation(wcstod(p, NULL), style.fontShiftX, fAnimate)
2370 : org.fontShiftX;
2371 break;
2373 case CMD_fay:
2375 style.fontShiftY = !p.IsEmpty()
2376 ? CalcAnimation(wcstod(p, NULL), style.fontShiftY, fAnimate)
2377 : org.fontShiftY;
2378 break;
2380 case CMD_fe:
2382 int n = wcstol(p, NULL, 10);
2383 style.charSet = !p.IsEmpty()
2385 : org.charSet;
2386 break;
2388 case CMD_fn:
2390 if(!p.IsEmpty() && p != L'0')
2391 style.fontName = CString(p).Trim();
2392 else
2393 style.fontName = org.fontName;
2394 break;
2396 case CMD_frx:
2398 style.fontAngleX = !p.IsEmpty()
2399 ? CalcAnimation(wcstod(p, NULL), style.fontAngleX, fAnimate)
2400 : org.fontAngleX;
2401 break;
2403 case CMD_fry:
2405 style.fontAngleY = !p.IsEmpty()
2406 ? CalcAnimation(wcstod(p, NULL), style.fontAngleY, fAnimate)
2407 : org.fontAngleY;
2408 break;
2410 case CMD_frz:
2411 case CMD_fr:
2413 style.fontAngleZ = !p.IsEmpty()
2414 ? CalcAnimation(wcstod(p, NULL), style.fontAngleZ, fAnimate)
2415 : org.fontAngleZ;
2416 break;
2418 case CMD_fscx:
2420 double n = CalcAnimation(wcstol(p, NULL, 10), style.fontScaleX, fAnimate);
2421 style.fontScaleX = !p.IsEmpty()
2422 ? ((n < 0) ? 0 : n)
2423 : org.fontScaleX;
2424 break;
2426 case CMD_fscy:
2428 double n = CalcAnimation(wcstol(p, NULL, 10), style.fontScaleY, fAnimate);
2429 style.fontScaleY = !p.IsEmpty()
2430 ? ((n < 0) ? 0 : n)
2431 : org.fontScaleY;
2432 break;
2434 case CMD_fsc:
2436 style.fontScaleX = org.fontScaleX;
2437 style.fontScaleY = org.fontScaleY;
2438 break;
2440 case CMD_fsp:
2442 style.fontSpacing = !p.IsEmpty()
2443 ? CalcAnimation(wcstod(p, NULL), style.fontSpacing, fAnimate)
2444 : org.fontSpacing;
2445 break;
2447 case CMD_fs:
2449 if(!p.IsEmpty())
2451 if(p[0] == L'-' || p[0] == L'+')
2453 double n = CalcAnimation(style.fontSize + style.fontSize*wcstol(p, NULL, 10)/10, style.fontSize, fAnimate);
2454 style.fontSize = (n > 0) ? n : org.fontSize;
2456 else
2458 double n = CalcAnimation(wcstol(p, NULL, 10), style.fontSize, fAnimate);
2459 style.fontSize = (n > 0) ? n : org.fontSize;
2462 else
2464 style.fontSize = org.fontSize;
2466 break;
2468 case CMD_i:
2470 int n = wcstol(p, NULL, 10);
2471 style.fItalic = !p.IsEmpty()
2472 ? (n == 0 ? false : n == 1 ? true : org.fItalic)
2473 : org.fItalic;
2474 break;
2476 case CMD_kt:
2478 m_kstart = !p.IsEmpty()
2479 ? wcstol(p, NULL, 10)*10
2480 : 0;
2481 m_kend = m_kstart;
2482 break;
2483 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2485 case CMD_kf:
2486 case CMD_K:
2488 m_ktype = 1;
2489 m_kstart = m_kend;
2490 m_kend += !p.IsEmpty()
2491 ? wcstol(p, NULL, 10)*10
2492 : 1000;
2493 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2494 break;
2496 case CMD_ko:
2498 m_ktype = 2;
2499 m_kstart = m_kend;
2500 m_kend += !p.IsEmpty()
2501 ? wcstol(p, NULL, 10)*10
2502 : 1000;
2503 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2504 break;
2506 case CMD_k:
2508 m_ktype = 0;
2509 m_kstart = m_kend;
2510 m_kend += !p.IsEmpty()
2511 ? wcstol(p, NULL, 10)*10
2512 : 1000;
2513 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2514 break;
2516 case CMD_move: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3][, t1=t[0], t2=t[1]])}
2518 if((params.GetCount() == 4 || params.GetCount() == 6) && !sub->m_effects[EF_MOVE])
2520 if(Effect* e = new Effect)
2522 e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2523 e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2524 e->param[2] = (int)(sub->m_scalex*wcstod(params[2], NULL)*8);
2525 e->param[3] = (int)(sub->m_scaley*wcstod(params[3], NULL)*8);
2526 e->t[0] = e->t[1] = -1;
2527 if(params.GetCount() == 6)
2529 for(int i = 0; i < 2; i++)
2530 e->t[i] = wcstol(params[4+i], NULL, 10);
2532 sub->m_effects[EF_MOVE] = e;
2535 break;
2537 case CMD_org: // {\org(x=param[0], y=param[1])}
2539 if(params.GetCount() == 2 && !sub->m_effects[EF_ORG])
2541 if(Effect* e = new Effect)
2543 e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2544 e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2545 sub->m_effects[EF_ORG] = e;
2548 break;
2550 case CMD_pbo:
2552 m_polygonBaselineOffset = wcstol(p, NULL, 10);
2553 break;
2555 case CMD_pos:
2557 if(params.GetCount() == 2 && !sub->m_effects[EF_MOVE])
2559 if(Effect* e = new Effect)
2561 e->param[0] = e->param[2] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2562 e->param[1] = e->param[3] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2563 e->t[0] = e->t[1] = 0;
2564 sub->m_effects[EF_MOVE] = e;
2567 break;
2569 case CMD_p:
2571 int n = wcstol(p, NULL, 10);
2572 m_nPolygon = (n <= 0 ? 0 : n);
2573 break;
2575 case CMD_q:
2577 int n = wcstol(p, NULL, 10);
2578 sub->m_wrapStyle = !p.IsEmpty() && (0 <= n && n <= 3)
2580 : m_defaultWrapStyle;
2581 break;
2583 case CMD_r:
2585 STSStyle* val;
2586 style = (!p.IsEmpty() && m_styles.Lookup(WToT(p), val) && val) ? *val : org;
2587 break;
2589 case CMD_shad:
2591 double dst = wcstod(p, NULL);
2592 double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate);
2593 style.shadowDepthX = !p.IsEmpty()
2594 ? (nx < 0 ? 0 : nx)
2595 : org.shadowDepthX;
2596 double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate);
2597 style.shadowDepthY = !p.IsEmpty()
2598 ? (ny < 0 ? 0 : ny)
2599 : org.shadowDepthY;
2600 break;
2602 case CMD_s:
2604 int n = wcstol(p, NULL, 10);
2605 style.fStrikeOut = !p.IsEmpty()
2606 ? (n == 0 ? false : n == 1 ? true : org.fStrikeOut)
2607 : org.fStrikeOut;
2608 break;
2610 case CMD_t: // \t([<t1>,<t2>,][<accel>,]<style modifiers>)
2612 CStringW param;
2613 m_animStart = m_animEnd = 0;
2614 m_animAccel = 1;
2615 if(params.GetCount() == 1)
2617 param = params[0];
2619 else if(params.GetCount() == 2)
2621 m_animAccel = wcstod(params[0], NULL);
2622 param = params[1];
2624 else if(params.GetCount() == 3)
2626 m_animStart = (int)wcstod(params[0], NULL);
2627 m_animEnd = (int)wcstod(params[1], NULL);
2628 param = params[2];
2630 else if(params.GetCount() == 4)
2632 m_animStart = wcstol(params[0], NULL, 10);
2633 m_animEnd = wcstol(params[1], NULL, 10);
2634 m_animAccel = wcstod(params[2], NULL);
2635 param = params[3];
2637 ParseSSATag(sub, assTag.embeded, style, org, true);
2638 sub->m_fAnimated = true;
2639 break;
2641 case CMD_u:
2643 int n = wcstol(p, NULL, 10);
2644 style.fUnderline = !p.IsEmpty()
2645 ? (n == 0 ? false : n == 1 ? true : org.fUnderline)
2646 : org.fUnderline;
2647 break;
2649 case CMD_xbord:
2651 double dst = wcstod(p, NULL);
2652 double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate);
2653 style.outlineWidthX = !p.IsEmpty()
2654 ? (nx < 0 ? 0 : nx)
2655 : org.outlineWidthX;
2656 break;
2658 case CMD_xshad:
2660 double dst = wcstod(p, NULL);
2661 double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate);
2662 style.shadowDepthX = !p.IsEmpty()
2663 ? nx
2664 : org.shadowDepthX;
2665 break;
2667 case CMD_ybord:
2669 double dst = wcstod(p, NULL);
2670 double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate);
2671 style.outlineWidthY = !p.IsEmpty()
2672 ? (ny < 0 ? 0 : ny)
2673 : org.outlineWidthY;
2674 break;
2676 case CMD_yshad:
2678 double dst = wcstod(p, NULL);
2679 double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate);
2680 style.shadowDepthY = !p.IsEmpty()
2681 ? ny
2682 : org.shadowDepthY;
2683 break;
2685 default:
2686 break;
2689 return(true);
2692 bool CRenderedTextSubtitle::ParseSSATag(CSubtitle* sub, const CStringW& str, STSStyle& style, const STSStyle& org, bool fAnimate)
2694 if(!sub) return(false);
2696 SharedPtrConstAssTagList assTags;
2697 AssTagListMruCache *ass_tag_cache = CacheManager::GetAssTagListMruCache();
2698 AssTagListMruCache::hashed_cache_const_iterator iter = ass_tag_cache->hash_find(str);
2699 if (iter==ass_tag_cache->hash_end())
2701 AssTagList *tmp = new AssTagList();
2702 ParseSSATag(tmp, str);
2703 assTags.reset(tmp);
2704 AssTagListMruItem item;
2705 item.script = str; item.tag_list = assTags;
2706 ass_tag_cache->update_cache( item );
2708 else
2710 assTags = iter->tag_list;
2711 ass_tag_cache->update_cache( *iter );
2713 return ParseSSATag(sub, *assTags, style, org, fAnimate);
2716 bool CRenderedTextSubtitle::ParseHtmlTag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org)
2718 if(str.Find(L"!--") == 0)
2719 return(true);
2720 bool fClosing = str[0] == L'/';
2721 str.Trim(L" /");
2722 int i = str.Find(L' ');
2723 if(i < 0) i = str.GetLength();
2724 CStringW tag = str.Left(i).MakeLower();
2725 str = str.Mid(i).Trim();
2726 CAtlArray<CStringW> attribs, params;
2727 while((i = str.Find(L'=')) > 0)
2729 attribs.Add(str.Left(i).Trim().MakeLower());
2730 str = str.Mid(i+1);
2731 for(i = 0; _istspace(str[i]); i++);
2732 str = str.Mid(i);
2733 if(str[0] == L'\"') {str = str.Mid(1); i = str.Find(L'\"');}
2734 else i = str.Find(L' ');
2735 if(i < 0) i = str.GetLength();
2736 params.Add(str.Left(i).Trim().MakeLower());
2737 str = str.Mid(i+1);
2739 if(tag == L"text")
2741 else if(tag == L"b" || tag == L"strong")
2742 style.fontWeight = !fClosing ? FW_BOLD : org.fontWeight;
2743 else if(tag == L"i" || tag == L"em")
2744 style.fItalic = !fClosing ? true : org.fItalic;
2745 else if(tag == L"u")
2746 style.fUnderline = !fClosing ? true : org.fUnderline;
2747 else if(tag == L"s" || tag == L"strike" || tag == L"del")
2748 style.fStrikeOut = !fClosing ? true : org.fStrikeOut;
2749 else if(tag == L"font")
2751 if(!fClosing)
2753 for(size_t i = 0; i < attribs.GetCount(); i++)
2755 if(params[i].IsEmpty()) continue;
2756 int nColor = -1;
2757 if(attribs[i] == L"face")
2759 style.fontName = params[i];
2761 else if(attribs[i] == L"size")
2763 if(params[i][0] == L'+')
2764 style.fontSize += wcstol(params[i], NULL, 10);
2765 else if(params[i][0] == L'-')
2766 style.fontSize -= wcstol(params[i], NULL, 10);
2767 else
2768 style.fontSize = wcstol(params[i], NULL, 10);
2770 else if(attribs[i] == L"color")
2772 nColor = 0;
2774 else if(attribs[i] == L"outline-color")
2776 nColor = 2;
2778 else if(attribs[i] == L"outline-level")
2780 style.outlineWidthX = style.outlineWidthY = wcstol(params[i], NULL, 10);
2782 else if(attribs[i] == L"shadow-color")
2784 nColor = 3;
2786 else if(attribs[i] == L"shadow-level")
2788 style.shadowDepthX = style.shadowDepthY = wcstol(params[i], NULL, 10);
2790 if(nColor >= 0 && nColor < 4)
2792 CString key = WToT(params[i]).TrimLeft(L'#');
2793 DWORD val;
2794 if(g_colors.Lookup(key, val))
2795 style.colors[nColor] = val;
2796 else if((style.colors[nColor] = _tcstol(key, NULL, 16)) == 0)
2797 style.colors[nColor] = 0x00ffffff; // default is white
2798 style.colors[nColor] = ((style.colors[nColor]>>16)&0xff)|((style.colors[nColor]&0xff)<<16)|(style.colors[nColor]&0x00ff00);
2802 else
2804 style.fontName = org.fontName;
2805 style.fontSize = org.fontSize;
2806 memcpy(style.colors, org.colors, sizeof(style.colors));
2809 else if(tag == L"k" && attribs.GetCount() == 1 && attribs[0] == L"t")
2811 m_ktype = 1;
2812 m_kstart = m_kend;
2813 m_kend += wcstol(params[0], NULL, 10);
2815 else
2816 return(false);
2817 return(true);
2820 double CRenderedTextSubtitle::CalcAnimation(double dst, double src, bool fAnimate)
2822 int s = m_animStart ? m_animStart : 0;
2823 int e = m_animEnd ? m_animEnd : m_delay;
2824 if(fabs(dst-src) >= 0.0001 && fAnimate)
2826 if(m_time < s) dst = src;
2827 else if(s <= m_time && m_time < e)
2829 double t = pow(1.0 * (m_time - s) / (e - s), m_animAccel);
2830 dst = (1 - t) * src + t * dst;
2832 // else dst = dst;
2834 return(dst);
2837 CSubtitle* CRenderedTextSubtitle::GetSubtitle(int entry)
2839 CSubtitle* sub;
2840 if(m_subtitleCache.Lookup(entry, sub))
2842 if(sub->m_fAnimated) {delete sub; sub = NULL;}
2843 else return(sub);
2845 sub = new CSubtitle();
2846 if(!sub) return(NULL);
2847 CStringW str = GetStrW(entry, true);
2848 STSStyle stss, orgstss;
2849 GetStyle(entry, &stss);
2850 if (stss.fontScaleX == stss.fontScaleY && m_dPARCompensation != 1.0)
2852 switch(m_ePARCompensationType)
2854 case EPCTUpscale:
2855 if (m_dPARCompensation < 1.0)
2856 stss.fontScaleY /= m_dPARCompensation;
2857 else
2858 stss.fontScaleX *= m_dPARCompensation;
2859 break;
2860 case EPCTDownscale:
2861 if (m_dPARCompensation < 1.0)
2862 stss.fontScaleX *= m_dPARCompensation;
2863 else
2864 stss.fontScaleY /= m_dPARCompensation;
2865 break;
2866 case EPCTAccurateSize:
2867 stss.fontScaleX *= m_dPARCompensation;
2868 break;
2871 orgstss = stss;
2872 sub->m_clip.SetRect(0, 0, m_size.cx>>3, m_size.cy>>3);
2873 sub->m_scrAlignment = -stss.scrAlignment;
2874 sub->m_wrapStyle = m_defaultWrapStyle;
2875 sub->m_fAnimated = false;
2876 sub->m_relativeTo = stss.relativeTo;
2877 sub->m_scalex = m_dstScreenSize.cx > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Width() : m_size.cx) / (m_dstScreenSize.cx*8) : 1.0;
2878 sub->m_scaley = m_dstScreenSize.cy > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Height() : m_size.cy) / (m_dstScreenSize.cy*8) : 1.0;
2879 m_animStart = m_animEnd = 0;
2880 m_animAccel = 1;
2881 m_ktype = m_kstart = m_kend = 0;
2882 m_nPolygon = 0;
2883 m_polygonBaselineOffset = 0;
2884 ParseEffect(sub, m_entries.GetAt(entry).effect);
2885 while(!str.IsEmpty())
2887 bool fParsed = false;
2888 int i;
2889 if(str[0] == L'{' && (i = str.Find(L'}')) > 0)
2891 if(fParsed = ParseSSATag(sub, str.Mid(1, i-1), stss, orgstss))
2892 str = str.Mid(i+1);
2894 else if(str[0] == L'<' && (i = str.Find(L'>')) > 0)
2896 if(fParsed = ParseHtmlTag(sub, str.Mid(1, i-1), stss, orgstss))
2897 str = str.Mid(i+1);
2899 if(fParsed)
2901 i = str.FindOneOf(L"{<");
2902 if(i < 0) i = str.GetLength();
2903 if(i == 0) continue;
2905 else
2907 i = str.Mid(1).FindOneOf(L"{<");
2908 if(i < 0) i = str.GetLength()-1;
2909 i++;
2911 STSStyle tmp = stss;
2912 tmp.fontSize = sub->m_scaley*tmp.fontSize*64;
2913 tmp.fontSpacing = sub->m_scalex*tmp.fontSpacing*64;
2914 tmp.outlineWidthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8;
2915 tmp.outlineWidthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8;
2916 tmp.shadowDepthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8;
2917 tmp.shadowDepthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8;
2918 FwSTSStyle fw_tmp(tmp);
2919 if(m_nPolygon)
2921 ParsePolygon(sub, str.Left(i), fw_tmp);
2923 else
2925 ParseString(sub, str.Left(i), fw_tmp);
2927 str = str.Mid(i);
2929 sub->m_fAnimated2 |= sub->m_fAnimated;
2930 if( sub->m_effects[EF_FADE] || sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL]
2931 || sub->m_effects[EF_MOVE] )
2932 sub->m_fAnimated2 = true;
2933 // just a "work-around" solution... in most cases nobody will want to use \org together with moving but without rotating the subs
2934 if(sub->m_effects[EF_ORG] && (sub->m_effects[EF_MOVE] || sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL]))
2935 sub->m_fAnimated = true;
2936 sub->m_scrAlignment = abs(sub->m_scrAlignment);
2937 STSEntry stse = m_entries.GetAt(entry);
2938 CRect marginRect = stse.marginRect;
2939 if(marginRect.left == 0) marginRect.left = orgstss.marginRect.get().left;
2940 if(marginRect.top == 0) marginRect.top = orgstss.marginRect.get().top;
2941 if(marginRect.right == 0) marginRect.right = orgstss.marginRect.get().right;
2942 if(marginRect.bottom == 0) marginRect.bottom = orgstss.marginRect.get().bottom;
2943 marginRect.left = (int)(sub->m_scalex*marginRect.left*8);
2944 marginRect.top = (int)(sub->m_scaley*marginRect.top*8);
2945 marginRect.right = (int)(sub->m_scalex*marginRect.right*8);
2946 marginRect.bottom = (int)(sub->m_scaley*marginRect.bottom*8);
2947 if(stss.relativeTo == 1)
2949 marginRect.left += m_vidrect.left;
2950 marginRect.top += m_vidrect.top;
2951 marginRect.right += m_size.cx - m_vidrect.right;
2952 marginRect.bottom += m_size.cy - m_vidrect.bottom;
2954 sub->CreateClippers(m_size);
2955 sub->MakeLines(m_size, marginRect);
2956 m_subtitleCache[entry] = sub;
2957 return(sub);
2962 STDMETHODIMP CRenderedTextSubtitle::NonDelegatingQueryInterface(REFIID riid, void** ppv)
2964 CheckPointer(ppv, E_POINTER);
2965 *ppv = NULL;
2966 return
2967 QI(IPersist)
2968 QI(ISubStream)
2969 QI(ISubPicProvider)
2970 QI(ISubPicProviderEx)
2971 __super::NonDelegatingQueryInterface(riid, ppv);
2974 // ISubPicProvider
2976 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetStartPosition(REFERENCE_TIME rt, double fps)
2978 //DbgLog((LOG_TRACE, 3, "rt:%lu", (ULONG)rt/10000));
2979 m_fps = fps;//fix me: check is fps changed and do some re-init thing
2980 int iSegment;
2981 int subIndex = 1;//If a segment has animate effect then it corresponds to several subpics.
2982 //subIndex, 1 based, indicates which subpic the result corresponds to.
2983 rt /= 10000i64;
2984 const STSSegment *stss = SearchSubs((int)rt, fps, &iSegment, NULL);
2985 if(stss==NULL)
2986 return NULL;
2987 else if(stss->animated)
2989 int start = TranslateSegmentStart(iSegment, fps);
2990 if(rt > start)
2991 subIndex = (rt-start)/RTS_ANIMATE_SUBPIC_DUR + 1;
2993 //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));
2994 return (POSITION)(subIndex | (iSegment<<RTS_POS_SEGMENT_INDEX_BITS));
2995 //if(iSegment < 0) iSegment = 0;
2996 //return(GetNext((POSITION)iSegment));
2999 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetNext(POSITION pos)
3001 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3002 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3003 const STSSegment *stss = GetSegment(iSegment);
3004 ASSERT(stss!=NULL && stss->subs.GetCount()>0);
3005 //DbgLog((LOG_TRACE, 3, "stss:%x count:%d", stss, stss->subs.GetCount()));
3006 if(!stss->animated)
3008 iSegment++;
3009 subIndex = 1;
3011 else
3013 int start, end;
3014 TranslateSegmentStartEnd(iSegment, m_fps, start, end);
3015 if(start+RTS_ANIMATE_SUBPIC_DUR*subIndex < end)
3016 subIndex++;
3017 else
3019 iSegment++;
3020 subIndex = 1;
3023 if(GetSegment(iSegment) != NULL)
3025 ASSERT(GetSegment(iSegment)->subs.GetCount()>0);
3026 return (POSITION)(subIndex | (iSegment<<RTS_POS_SEGMENT_INDEX_BITS));
3028 else
3029 return NULL;
3032 //@return: <0 if segment not found
3033 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStart(POSITION pos, double fps)
3035 //return(10000i64 * TranslateSegmentStart((int)pos-1, fps));
3036 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3037 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3038 int start = TranslateSegmentStart(iSegment, fps);
3039 const STSSegment *stss = GetSegment(iSegment);
3040 if(stss!=NULL)
3042 return (start + (subIndex-1)*RTS_ANIMATE_SUBPIC_DUR)*10000i64;
3044 else
3046 return -1;
3050 //@return: <0 if segment not found
3051 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStop(POSITION pos, double fps)
3053 // return(10000i64 * TranslateSegmentEnd((int)pos-1, fps));
3054 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3055 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3056 int start, end, ret;
3057 TranslateSegmentStartEnd(iSegment, fps, start, end);
3058 const STSSegment *stss = GetSegment(iSegment);
3059 if(stss!=NULL)
3061 if(!stss->animated)
3062 ret = end;
3063 else
3065 ret = start+subIndex*RTS_ANIMATE_SUBPIC_DUR;
3066 if(ret > end)
3067 ret = end;
3069 return ret*10000i64;
3071 else
3072 return -1;
3075 //@start, @stop: -1 if segment not found; @stop may < @start if subIndex exceed uppper bound
3076 STDMETHODIMP_(VOID) CRenderedTextSubtitle::GetStartStop(POSITION pos, double fps, /*out*/REFERENCE_TIME &start, /*out*/REFERENCE_TIME &stop)
3078 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3079 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3080 int tempStart, tempEnd;
3081 TranslateSegmentStartEnd(iSegment, fps, tempStart, tempEnd);
3082 start = tempStart;
3083 stop = tempEnd;
3084 const STSSegment *stss = GetSegment(iSegment);
3085 if(stss!=NULL)
3087 if(stss->animated)
3089 start += (subIndex-1)*RTS_ANIMATE_SUBPIC_DUR;
3090 if(start+RTS_ANIMATE_SUBPIC_DUR < stop)
3091 stop = start+RTS_ANIMATE_SUBPIC_DUR;
3093 //DbgLog((LOG_TRACE, 3, "animated:%d seg:%d idx:%d start:%d stop:%lu", stss->animated, iSegment, subIndex, (ULONG)start, (ULONG)stop));
3094 start *= 10000i64;
3095 stop *= 10000i64;
3097 else
3099 start = -1;
3100 stop = -1;
3104 STDMETHODIMP_(bool) CRenderedTextSubtitle::IsAnimated(POSITION pos)
3106 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3107 if(iSegment>=0 && iSegment<m_segments.GetCount())
3108 return m_segments[iSegment].animated;
3109 else
3110 return false;
3111 //return(true);
3114 struct LSub {int idx, layer, readorder;};
3116 static int lscomp(const void* ls1, const void* ls2)
3118 int ret = ((LSub*)ls1)->layer - ((LSub*)ls2)->layer;
3119 if(!ret) ret = ((LSub*)ls1)->readorder - ((LSub*)ls2)->readorder;
3120 return(ret);
3123 STDMETHODIMP CRenderedTextSubtitle::RenderEx(SubPicDesc& spd, REFERENCE_TIME rt, double fps, CAtlList<CRect>& rectList)
3125 CRect bbox2(0,0,0,0);
3126 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))
3127 Init(CSize(spd.w, spd.h), spd.vidrect);
3128 int t = (int)(rt / 10000);
3129 int segment;
3130 //const
3131 STSSegment* stss = SearchSubs2(t, fps, &segment);
3132 if(!stss) return S_FALSE;
3133 // clear any cached subs not in the range of +/-30secs measured from the segment's bounds
3135 POSITION pos = m_subtitleCache.GetStartPosition();
3136 while(pos)
3138 int key;
3139 CSubtitle* value;
3140 m_subtitleCache.GetNextAssoc(pos, key, value);
3141 STSEntry& stse = m_entries.GetAt(key);
3142 if(stse.end <= (t-30000) || stse.start > (t+30000))
3144 delete value;
3145 m_subtitleCache.RemoveKey(key);
3146 pos = m_subtitleCache.GetStartPosition();
3150 m_sla.AdvanceToSegment(segment, stss->subs);
3151 CAtlArray<LSub> subs;
3152 for(int i = 0, j = stss->subs.GetCount(); i < j; i++)
3154 LSub ls;
3155 ls.idx = stss->subs[i];
3156 ls.layer = m_entries.GetAt(stss->subs[i]).layer;
3157 ls.readorder = m_entries.GetAt(stss->subs[i]).readorder;
3158 subs.Add(ls);
3160 qsort(subs.GetData(), subs.GetCount(), sizeof(LSub), lscomp);
3162 CompositeDrawItemList drawItemList;
3163 for(int i = 0, j = subs.GetCount(); i < j; i++)
3165 int entry = subs[i].idx;
3166 STSEntry stse = m_entries.GetAt(entry);
3168 int start = TranslateStart(entry, fps);
3169 m_time = t - start;
3170 m_delay = TranslateEnd(entry, fps) - start;
3172 CSubtitle* s = GetSubtitle(entry);
3173 if(!s) continue;
3174 stss->animated |= s->m_fAnimated2;
3175 CRect clipRect = s->m_clip;
3176 CRect r = s->m_rect;
3177 CSize spaceNeeded = r.Size();
3178 // apply the effects
3179 bool fPosOverride = false, fOrgOverride = false;
3180 int alpha = 0x00;
3181 CPoint org2;
3182 for(int k = 0; k < EF_NUMBEROFEFFECTS; k++)
3184 if(!s->m_effects[k]) continue;
3185 switch(k)
3187 case EF_MOVE: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3], t1=t[0], t2=t[1])}
3189 CPoint p;
3190 CPoint p1(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
3191 CPoint p2(s->m_effects[k]->param[2], s->m_effects[k]->param[3]);
3192 int t1 = s->m_effects[k]->t[0];
3193 int t2 = s->m_effects[k]->t[1];
3194 if(t2 < t1) {int t = t1; t1 = t2; t2 = t;}
3195 if(t1 <= 0 && t2 <= 0) {t1 = 0; t2 = m_delay;}
3196 if(m_time <= t1) p = p1;
3197 else if (p1 == p2) p = p1;
3198 else if(t1 < m_time && m_time < t2)
3200 double t = 1.0*(m_time-t1)/(t2-t1);
3201 p.x = (int)((1-t)*p1.x + t*p2.x);
3202 p.y = (int)((1-t)*p1.y + t*p2.y);
3204 else p = p2;
3205 r = CRect(
3206 CPoint((s->m_scrAlignment%3) == 1 ? p.x : (s->m_scrAlignment%3) == 0 ? p.x - spaceNeeded.cx : p.x - (spaceNeeded.cx+1)/2,
3207 s->m_scrAlignment <= 3 ? p.y - spaceNeeded.cy : s->m_scrAlignment <= 6 ? p.y - (spaceNeeded.cy+1)/2 : p.y),
3208 spaceNeeded);
3209 if(s->m_relativeTo == 1)
3210 r.OffsetRect(m_vidrect.TopLeft());
3211 fPosOverride = true;
3213 break;
3214 case EF_ORG: // {\org(x=param[0], y=param[1])}
3216 org2 = CPoint(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
3217 fOrgOverride = true;
3219 break;
3220 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])
3222 int t1 = s->m_effects[k]->t[0];
3223 int t2 = s->m_effects[k]->t[1];
3224 int t3 = s->m_effects[k]->t[2];
3225 int t4 = s->m_effects[k]->t[3];
3226 if(t1 == -1 && t4 == -1) {t1 = 0; t3 = m_delay-t3; t4 = m_delay;}
3227 if(m_time < t1) alpha = s->m_effects[k]->param[0];
3228 else if(m_time >= t1 && m_time < t2)
3230 double t = 1.0 * (m_time - t1) / (t2 - t1);
3231 alpha = (int)(s->m_effects[k]->param[0]*(1-t) + s->m_effects[k]->param[1]*t);
3233 else if(m_time >= t2 && m_time < t3) alpha = s->m_effects[k]->param[1];
3234 else if(m_time >= t3 && m_time < t4)
3236 double t = 1.0 * (m_time - t3) / (t4 - t3);
3237 alpha = (int)(s->m_effects[k]->param[1]*(1-t) + s->m_effects[k]->param[2]*t);
3239 else if(m_time >= t4) alpha = s->m_effects[k]->param[2];
3241 break;
3242 case EF_BANNER: // Banner;delay=param[0][;leftoright=param[1];fadeawaywidth=param[2]]
3244 int left = s->m_relativeTo == 1 ? m_vidrect.left : 0,
3245 right = s->m_relativeTo == 1 ? m_vidrect.right : m_size.cx;
3246 r.left = !!s->m_effects[k]->param[1]
3247 ? (left/*marginRect.left*/ - spaceNeeded.cx) + (int)(m_time*8.0/s->m_effects[k]->param[0])
3248 : (right /*- marginRect.right*/) - (int)(m_time*8.0/s->m_effects[k]->param[0]);
3249 r.right = r.left + spaceNeeded.cx;
3250 clipRect &= CRect(left>>3, clipRect.top, right>>3, clipRect.bottom);
3251 fPosOverride = true;
3253 break;
3254 case EF_SCROLL: // Scroll up/down(toptobottom=param[3]);top=param[0];bottom=param[1];delay=param[2][;fadeawayheight=param[4]]
3256 r.top = !!s->m_effects[k]->param[3]
3257 ? s->m_effects[k]->param[0] + (int)(m_time*8.0/s->m_effects[k]->param[2]) - spaceNeeded.cy
3258 : s->m_effects[k]->param[1] - (int)(m_time*8.0/s->m_effects[k]->param[2]);
3259 r.bottom = r.top + spaceNeeded.cy;
3260 CRect cr(0, (s->m_effects[k]->param[0] + 4) >> 3, spd.w, (s->m_effects[k]->param[1] + 4) >> 3);
3261 if(s->m_relativeTo == 1)
3262 r.top += m_vidrect.top,
3263 r.bottom += m_vidrect.top,
3264 cr.top += m_vidrect.top>>3,
3265 cr.bottom += m_vidrect.top>>3;
3266 clipRect &= cr;
3267 fPosOverride = true;
3269 break;
3270 default:
3271 break;
3274 if(!fPosOverride && !fOrgOverride && !s->m_fAnimated)
3275 r = m_sla.AllocRect(s, segment, entry, stse.layer, m_collisions);
3276 CPoint org;
3277 org.x = (s->m_scrAlignment%3) == 1 ? r.left : (s->m_scrAlignment%3) == 2 ? r.CenterPoint().x : r.right;
3278 org.y = s->m_scrAlignment <= 3 ? r.bottom : s->m_scrAlignment <= 6 ? r.CenterPoint().y : r.top;
3279 if(!fOrgOverride) org2 = org;
3280 SharedArrayByte pAlphaMask;
3281 if( s->m_pClipper )
3282 pAlphaMask = s->m_pClipper->m_pAlphaMask;
3283 CPoint p, p2(0, r.top);
3284 POSITION pos;
3285 p = p2;
3286 // Rectangles for inverse clip
3287 CRect iclipRect[4];
3288 iclipRect[0] = CRect(0, 0, spd.w, clipRect.top);
3289 iclipRect[1] = CRect(0, clipRect.top, clipRect.left, clipRect.bottom);
3290 iclipRect[2] = CRect(clipRect.right, clipRect.top, spd.w, clipRect.bottom);
3291 iclipRect[3] = CRect(0, clipRect.bottom, spd.w, spd.h);
3292 int dbgTest = 0;
3293 bbox2 = CRect(0,0,0,0);
3294 pos = s->GetHeadLinePosition();
3295 while(pos)
3297 CLine* l = s->GetNextLine(pos);
3298 p.x = (s->m_scrAlignment%3) == 1 ? org.x
3299 : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
3300 : org.x - (l->m_width/2);
3302 CompositeDrawItemList tmpDrawItemList;
3303 if (s->m_clipInverse)
3305 for (int i=0;i<l->GetWordCount();i++)
3307 tmpDrawItemList.AddTail();
3308 tmpDrawItemList.AddTail();
3309 tmpDrawItemList.AddTail();
3310 tmpDrawItemList.AddTail();
3312 bbox2 |= l->PaintShadow(&tmpDrawItemList, spd, iclipRect[0], pAlphaMask, p, org2, m_time, alpha);
3313 bbox2 |= l->PaintShadow(&tmpDrawItemList, spd, iclipRect[1], pAlphaMask, p, org2, m_time, alpha);
3314 bbox2 |= l->PaintShadow(&tmpDrawItemList, spd, iclipRect[2], pAlphaMask, p, org2, m_time, alpha);
3315 bbox2 |= l->PaintShadow(&tmpDrawItemList, spd, iclipRect[3], pAlphaMask, p, org2, m_time, alpha);
3317 bbox2 |= l->PaintOutline(&tmpDrawItemList, spd, iclipRect[0], pAlphaMask, p, org2, m_time, alpha);
3318 bbox2 |= l->PaintOutline(&tmpDrawItemList, spd, iclipRect[1], pAlphaMask, p, org2, m_time, alpha);
3319 bbox2 |= l->PaintOutline(&tmpDrawItemList, spd, iclipRect[2], pAlphaMask, p, org2, m_time, alpha);
3320 bbox2 |= l->PaintOutline(&tmpDrawItemList, spd, iclipRect[3], pAlphaMask, p, org2, m_time, alpha);
3322 bbox2 |= l->PaintBody(&tmpDrawItemList, spd, iclipRect[0], pAlphaMask, p, org2, m_time, alpha);
3323 bbox2 |= l->PaintBody(&tmpDrawItemList, spd, iclipRect[1], pAlphaMask, p, org2, m_time, alpha);
3324 bbox2 |= l->PaintBody(&tmpDrawItemList, spd, iclipRect[2], pAlphaMask, p, org2, m_time, alpha);
3325 bbox2 |= l->PaintBody(&tmpDrawItemList, spd, iclipRect[3], pAlphaMask, p, org2, m_time, alpha);
3327 else
3329 for (int i=0;i<l->GetWordCount();i++)
3331 tmpDrawItemList.AddTail();
3333 bbox2 |= l->PaintShadow(&tmpDrawItemList, spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
3335 bbox2 |= l->PaintOutline(&tmpDrawItemList, spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
3337 bbox2 |= l->PaintBody(&tmpDrawItemList, spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
3340 drawItemList.AddTailList(&tmpDrawItemList);
3341 p.y += l->m_ascent + l->m_descent;
3343 rectList.AddTail(bbox2);
3346 Draw(spd, drawItemList);
3347 return (subs.GetCount() && !rectList.IsEmpty()) ? S_OK : S_FALSE;
3351 STDMETHODIMP CRenderedTextSubtitle::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
3353 CAtlList<CRect> rectList;
3354 HRESULT result = RenderEx(spd, rt, fps, rectList);
3355 POSITION pos = rectList.GetHeadPosition();
3356 CRect bbox2(0,0,0,0);
3357 while(pos!=NULL)
3359 bbox2 |= rectList.GetNext(pos);
3361 bbox = bbox2;
3362 return result;
3365 // IPersist
3367 STDMETHODIMP CRenderedTextSubtitle::GetClassID(CLSID* pClassID)
3369 return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
3372 // ISubStream
3374 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStreamCount()
3376 return(1);
3379 STDMETHODIMP CRenderedTextSubtitle::GetStreamInfo(int iStream, WCHAR** ppName, LCID* pLCID)
3381 if(iStream != 0) return E_INVALIDARG;
3382 if(ppName)
3384 if(!(*ppName = (WCHAR*)CoTaskMemAlloc((m_name.GetLength()+1)*sizeof(WCHAR))))
3385 return E_OUTOFMEMORY;
3386 wcscpy(*ppName, CStringW(m_name));
3388 if(pLCID)
3390 *pLCID = 0; // TODO
3392 return S_OK;
3395 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStream()
3397 return(0);
3400 STDMETHODIMP CRenderedTextSubtitle::SetStream(int iStream)
3402 return iStream == 0 ? S_OK : E_FAIL;
3405 STDMETHODIMP CRenderedTextSubtitle::Reload()
3407 CFileStatus s;
3408 if(!CFile::GetStatus(m_path, s)) return E_FAIL;
3409 return !m_path.IsEmpty() && Open(m_path, DEFAULT_CHARSET) ? S_OK : E_FAIL;
3412 STDMETHODIMP_(bool) CRenderedTextSubtitle::IsColorTypeSupported( int type )
3414 return type==MSP_AY11 ||
3415 type==MSP_AYUV ||
3416 type==MSP_AUYV ||
3417 type==MSP_RGBA;
3420 void CRenderedTextSubtitle::Draw( SubPicDesc& spd, CompositeDrawItemList& drawItemList )
3422 POSITION pos = drawItemList.GetHeadPosition();
3423 while(pos)
3425 CompositeDrawItem& draw_item = drawItemList.GetNext(pos);
3426 if(draw_item.shadow)
3427 Rasterizer::Draw( spd, *draw_item.shadow );
3429 pos = drawItemList.GetHeadPosition();
3430 while(pos)
3432 CompositeDrawItem& draw_item = drawItemList.GetNext(pos);
3433 if(draw_item.outline)
3434 Rasterizer::Draw( spd, *draw_item.outline );
3436 pos = drawItemList.GetHeadPosition();
3437 while(pos)
3439 CompositeDrawItem& draw_item = drawItemList.GetNext(pos);
3440 if(draw_item.body)
3441 Rasterizer::Draw( spd, *draw_item.body );