Split scan convert operation for a new cache. [Part 2]
[xy_vsfilter.git] / src / subtitles / RTS.cpp
blob184a1c0173c05a93e03dfac57acdb4b52e832cdc
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 if(type==XY_AYUV)
38 return ColorConvTable::Argb2Ayuv(argb);
39 else
40 return ColorConvTable::Argb2Auyv(argb);
43 static long revcolor(long c)
45 return ((c&0xff0000)>>16) + (c&0xff00) + ((c&0xff)<<16);
48 // Skip all leading whitespace
49 inline CStringW::PCXSTR SkipWhiteSpaceLeft(const CStringW& str)
51 CStringW::PCXSTR psz = str.GetString();
53 while( iswspace( *psz ) )
55 psz++;
57 return psz;
60 // Skip all trailing whitespace
61 inline CStringW::PCXSTR SkipWhiteSpaceRight(const CStringW& str)
63 CStringW::PCXSTR psz = str.GetString();
64 CStringW::PCXSTR pszLast = psz + str.GetLength() - 1;
65 bool first_white = false;
66 while( iswspace( *pszLast ) )
68 pszLast--;
69 if(pszLast<psz)
70 break;
72 return pszLast;
75 // Skip all leading whitespace
76 inline CStringW::PCXSTR SkipWhiteSpaceLeft(CStringW::PCXSTR start, CStringW::PCXSTR end)
78 while( start!=end && iswspace( *start ) )
80 start++;
82 return start;
85 // Skip all trailing whitespace, first char must NOT be white space
86 inline CStringW::PCXSTR FastSkipWhiteSpaceRight(CStringW::PCXSTR start, CStringW::PCXSTR end)
88 while( iswspace( *--end ) );
89 return end+1;
92 inline CStringW::PCXSTR FindChar(CStringW::PCXSTR start, CStringW::PCXSTR end, WCHAR c)
94 while( start!=end && *start!=c )
96 start++;
98 return start;
101 //////////////////////////////////////////////////////////////////////////////////////////////
103 // CMyFont
105 CMyFont::CMyFont(const STSStyleBase& style)
107 LOGFONT lf;
108 memset(&lf, 0, sizeof(lf));
109 lf <<= style;
110 lf.lfHeight = (LONG)(style.fontSize+0.5);
111 lf.lfOutPrecision = OUT_TT_PRECIS;
112 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
113 lf.lfQuality = ANTIALIASED_QUALITY;
114 lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
115 if(!CreateFontIndirect(&lf))
117 _tcscpy(lf.lfFaceName, _T("Arial"));
118 CreateFontIndirect(&lf);
120 HFONT hOldFont = SelectFont(g_hDC, *this);
121 TEXTMETRIC tm;
122 GetTextMetrics(g_hDC, &tm);
123 m_ascent = ((tm.tmAscent + 4) >> 3);
124 m_descent = ((tm.tmDescent + 4) >> 3);
125 SelectFont(g_hDC, hOldFont);
128 // CWord
130 CWord::CWord(const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend)
131 : m_style(style), m_str(str)
132 , m_width(0), m_ascent(0), m_descent(0)
133 , m_ktype(ktype), m_kstart(kstart), m_kend(kend)
134 , m_fLineBreak(false), m_fWhiteSpaceChar(false)
135 //, m_pOpaqueBox(NULL)
137 if(m_str.IsEmpty())
139 m_fWhiteSpaceChar = m_fLineBreak = true;
141 m_width = 0;
144 CWord::CWord( const CWord& src)
146 m_str = src.m_str;
147 m_fWhiteSpaceChar = src.m_fWhiteSpaceChar;
148 m_fLineBreak = src.m_fLineBreak;
149 m_style = src.m_style;
150 m_pOpaqueBox = src.m_pOpaqueBox;//allow since it is shared_ptr
151 m_ktype = src.m_ktype;
152 m_kstart = src.m_kstart;
153 m_kend = src.m_kend;
154 m_width = src.m_width;
155 m_ascent = src.m_ascent;
156 m_descent = src.m_descent;
159 CWord::~CWord()
161 //if(m_pOpaqueBox) delete m_pOpaqueBox;
164 bool CWord::Append(const SharedPtrCWord& w)
166 if(!(m_style == w->m_style)
167 || m_fLineBreak || w->m_fLineBreak
168 || w->m_kstart != w->m_kend || m_ktype != w->m_ktype) return(false);
169 m_fWhiteSpaceChar = m_fWhiteSpaceChar && w->m_fWhiteSpaceChar;
170 m_str += w->m_str;
171 m_width += w->m_width;
172 return(true);
175 void CWord::Paint( SharedPtrCWord word, const CPoint& p, const CPoint& trans_org, OverlayList* overlay_list )
177 if(!word->m_str || overlay_list==NULL) return;
178 bool error = false;
181 CPoint trans_org2 = trans_org;
182 bool need_transform = word->NeedTransform();
183 if(!need_transform)
185 trans_org2.x=0;
186 trans_org2.y=0;
189 CPoint psub_true( (p.x&SubpixelPositionControler::EIGHT_X_EIGHT_MASK), (p.y&SubpixelPositionControler::EIGHT_X_EIGHT_MASK) );
190 OverlayKey sub_key(*word, psub_true, trans_org2);
191 if( SubpixelPositionControler::GetGlobalControler().UseBilinearShift() )
193 OverlayMruCache* overlay_cache = CacheManager::GetSubpixelVarianceCache();
195 POSITION pos = overlay_cache->Lookup(sub_key);
196 if(pos!=NULL)
198 overlay_list->overlay = overlay_cache->GetAt(pos);
199 overlay_cache->UpdateCache( pos );
202 if( !overlay_list->overlay )
204 CPoint psub = SubpixelPositionControler::GetGlobalControler().GetSubpixel(p);
205 OverlayKey overlay_key(*word, psub, trans_org2);
206 OverlayMruCache* overlay_cache = CacheManager::GetOverlayMruCache();
207 POSITION pos = overlay_cache->Lookup(overlay_key);
208 if(pos==NULL)
210 if( !word->DoPaint(psub, trans_org2, &(overlay_list->overlay), overlay_key) )
212 error = true;
213 break;
216 else
218 overlay_list->overlay = overlay_cache->GetAt(pos);
219 overlay_cache->UpdateCache( pos );
221 PaintFromOverlay(p, trans_org2, sub_key, overlay_list->overlay);
224 if(word->m_style.get().borderStyle == 1)
226 if(!word->CreateOpaqueBox())
228 error = true;
229 break;
231 overlay_list->next = new OverlayList();
232 Paint(word->m_pOpaqueBox, p, trans_org, overlay_list->next);
234 } while(false);
235 if(error)
237 overlay_list->overlay.reset( new Overlay() );
241 void CWord::PaintFromOverlay(const CPoint& p, const CPoint& trans_org2, OverlayKey &subpixel_variance_key, SharedPtrOverlay& overlay)
243 if( SubpixelPositionControler::GetGlobalControler().UseBilinearShift() )
245 CPoint psub = SubpixelPositionControler::GetGlobalControler().GetSubpixel(p);
246 if( (psub.x!=(p.x&SubpixelPositionControler::EIGHT_X_EIGHT_MASK)
247 || psub.y!=(p.y&SubpixelPositionControler::EIGHT_X_EIGHT_MASK)) )
249 overlay.reset(overlay->GetSubpixelVariance((p.x&SubpixelPositionControler::EIGHT_X_EIGHT_MASK) - psub.x,
250 (p.y&SubpixelPositionControler::EIGHT_X_EIGHT_MASK) - psub.y));
251 OverlayMruCache* overlay_cache = CacheManager::GetSubpixelVarianceCache();
252 overlay_cache->UpdateCache(subpixel_variance_key, overlay);
257 void CWord::PaintFromNoneBluredOverlay(SharedPtrOverlay raterize_result, const OverlayKey& overlay_key, SharedPtrOverlay* overlay)
259 if( m_style.get().fBlur>0 || m_style.get().fGaussianBlur>0.000001 )
261 overlay->reset(new Overlay());
262 if(!Rasterizer::Blur(*raterize_result, m_style.get().fBlur, m_style.get().fGaussianBlur, *overlay))
264 *overlay = raterize_result;
267 else
269 *overlay = raterize_result;
271 OverlayMruCache* overlay_cache = CacheManager::GetOverlayMruCache();
272 overlay_cache->UpdateCache(overlay_key, *overlay);
275 bool CWord::PaintFromScanLineData(const CPoint& psub, const ScanLineData2& scan_line_data2, const OverlayKey& key, SharedPtrOverlay* overlay)
277 SharedPtrOverlay raterize_result(new Overlay());
278 if(!Rasterizer::Rasterize(scan_line_data2, psub.x, psub.y, raterize_result))
280 return false;
282 OverlayNoBlurMruCache* overlay_no_blur_cache = CacheManager::GetOverlayNoBlurMruCache();
283 overlay_no_blur_cache->UpdateCache(key, raterize_result);
284 PaintFromNoneBluredOverlay(raterize_result, key, overlay);
285 return true;
288 bool CWord::PaintFromPathData(const CPoint& psub, const CPoint& trans_org, const PathData& path_data, const OverlayKey& key, SharedPtrOverlay* overlay )
290 PathData path_data2 = path_data;
291 bool need_transform = NeedTransform();
292 if(need_transform)
293 Transform(&path_data2, CPoint(trans_org.x*8, trans_org.y*8));
295 CPoint left_top;
296 CSize size;
297 path_data2.AlignLeftTop(&left_top, &size);
299 SharedPtrScanLineData2 tmp(new ScanLineData2());
300 if(!tmp->ScanConvert(path_data2, size))
302 return false;
304 tmp->SetOffset(left_top);
306 if(m_style.get().borderStyle == 0 && (m_style.get().outlineWidthX+m_style.get().outlineWidthY > 0))
308 if(!tmp->CreateWidenedRegion(static_cast<int>(m_style.get().outlineWidthX+0.5),
309 static_cast<int>(m_style.get().outlineWidthY+0.5)))
311 return false;
314 else if(m_style.get().borderStyle == 1)
316 if(!CreateOpaqueBox())
318 return false;
321 ScanLineDataMruCache* scan_line_data_cache = CacheManager::GetScanLineDataMruCache();
322 scan_line_data_cache->UpdateCache(key, tmp);
323 return PaintFromScanLineData(psub, *tmp, key, overlay);
326 bool CWord::PaintFromRawData( const CPoint& psub, const CPoint& trans_org, const OverlayKey& key, SharedPtrOverlay* overlay )
328 PathDataMruCache* path_data_cache = CacheManager::GetPathDataMruCache();
330 PathData *tmp=new PathData();
331 SharedPtrPathData path_data(tmp);
332 if(!CreatePath(tmp))
334 return false;
336 path_data_cache->UpdateCache(key, path_data);
337 return PaintFromPathData(psub, trans_org, *tmp, key, overlay);
340 bool CWord::DoPaint(const CPoint& psub, const CPoint& trans_org, SharedPtrOverlay* overlay, const OverlayKey& key)
342 bool result = true;
343 OverlayNoBlurMruCache* overlay_no_blur_cache = CacheManager::GetOverlayNoBlurMruCache();
344 POSITION pos = overlay_no_blur_cache->Lookup(key);
346 if(pos!=NULL)
348 SharedPtrOverlay raterize_result = overlay_no_blur_cache->GetAt(pos);
349 overlay_no_blur_cache->UpdateCache( pos );
350 PaintFromNoneBluredOverlay(raterize_result, key, overlay);
352 else
354 ScanLineDataMruCache* scan_line_data_cache = CacheManager::GetScanLineDataMruCache();
355 pos = scan_line_data_cache->Lookup(key);
356 if(pos!=NULL)
358 SharedPtrConstScanLineData2 scan_line_data = scan_line_data_cache->GetAt(pos);
359 scan_line_data_cache->UpdateCache( pos );
360 result = PaintFromScanLineData(psub, *scan_line_data, key, overlay);
362 else
364 PathDataMruCache* path_data_cache = CacheManager::GetPathDataMruCache();
365 POSITION pos_path = path_data_cache->Lookup(key);
366 if(pos_path!=NULL)
368 SharedPtrConstPathData path_data = path_data_cache->GetAt(pos_path); //important! copy not ref
369 path_data_cache->UpdateCache( pos_path );
370 result = PaintFromPathData(psub, trans_org, *path_data, key, overlay);
372 else
374 result = PaintFromRawData(psub, trans_org, key, overlay);
378 return result;
381 bool CWord::NeedTransform()
383 return (fabs(m_style.get().fontScaleX - 100) > 0.000001) ||
384 (fabs(m_style.get().fontScaleY - 100) > 0.000001) ||
385 (fabs(m_style.get().fontAngleX) > 0.000001) ||
386 (fabs(m_style.get().fontAngleY) > 0.000001) ||
387 (fabs(m_style.get().fontAngleZ) > 0.000001) ||
388 (fabs(m_style.get().fontShiftX) > 0.000001) ||
389 (fabs(m_style.get().fontShiftY) > 0.000001);
392 void CWord::Transform(PathData* path_data, const CPoint& org)
394 //// CPUID from VDub
395 //bool fSSE2 = !!(g_cpuid.m_flags & CCpuID::sse2);
397 //if(fSSE2) { // SSE code
398 // Transform_SSE2(path_data, org);
399 //} else // C-code
400 Transform_C(path_data, org);
403 void CWord::Transform_C(PathData* path_data, const CPoint &org )
405 double scalex = m_style.get().fontScaleX/100;
406 double scaley = m_style.get().fontScaleY/100;
408 double caz = cos((3.1415/180)*m_style.get().fontAngleZ);
409 double saz = sin((3.1415/180)*m_style.get().fontAngleZ);
410 double cax = cos((3.1415/180)*m_style.get().fontAngleX);
411 double sax = sin((3.1415/180)*m_style.get().fontAngleX);
412 double cay = cos((3.1415/180)*m_style.get().fontAngleY);
413 double say = sin((3.1415/180)*m_style.get().fontAngleY);
415 #ifdef _VSMOD
416 // patch m003. random text points
417 double xrnd = m_style.get().mod_rand.X*100;
418 double yrnd = m_style.get().mod_rand.Y*100;
419 double zrnd = m_style.get().mod_rand.Z*100;
421 srand(m_style.get().mod_rand.Seed);
423 // patch m008. distort
424 int xsz,ysz;
425 double dst1x,dst1y,dst2x,dst2y,dst3x,dst3y;
426 int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
428 bool is_dist = m_style.get().mod_distort.enabled;
429 if (is_dist) {
430 for(int i = 0; i < path_data->mPathPoints; i++) {
431 if(minx > path_data->mpPathPoints[i].x) {
432 minx = path_data->mpPathPoints[i].x;
434 if(miny > path_data->mpPathPoints[i].y) {
435 miny = path_data->mpPathPoints[i].y;
437 if(maxx < path_data->mpPathPoints[i].x) {
438 maxx = path_data->mpPathPoints[i].x;
440 if(maxy < path_data->mpPathPoints[i].y) {
441 maxy = path_data->mpPathPoints[i].y;
445 xsz = max(maxx - minx, 0);
446 ysz = max(maxy - miny, 0);
448 dst1x = m_style.get().mod_distort.pointsx[0];
449 dst1y = m_style.get().mod_distort.pointsy[0];
450 dst2x = m_style.get().mod_distort.pointsx[1];
451 dst2y = m_style.get().mod_distort.pointsy[1];
452 dst3x = m_style.get().mod_distort.pointsx[2];
453 dst3y = m_style.get().mod_distort.pointsy[2];
455 #endif
457 for (int i = 0; i < path_data->mPathPoints; i++) {
458 double x, y, z, xx, yy, zz;
460 x = path_data->mpPathPoints[i].x;
461 y = path_data->mpPathPoints[i].y;
462 #ifdef _VSMOD
463 // patch m002. Z-coord
464 z = m_style.get().mod_z;
466 double u, v;
467 if (is_dist) {
468 u = (x-minx) / xsz;
469 v = (y-miny) / ysz;
471 x = minx+(0 + (dst1x - 0)*u + (dst3x-0)*v+(0+dst2x-dst1x-dst3x)*u*v)*xsz;
472 y = miny+(0 + (dst1y - 0)*u + (dst3y-0)*v+(0+dst2y-dst1y-dst3y)*u*v)*ysz;
473 //P = P0 + (P1 - P0)u + (P3 - P0)v + (P0 + P2 - P1 - P3)uv
476 // patch m003. random text points
477 x = xrnd > 0 ? (xrnd - rand() % (int)(xrnd * 2 + 1)) / 100.0 + x : x;
478 y = yrnd > 0 ? (yrnd - rand() % (int)(yrnd * 2 + 1)) / 100.0 + y : y;
479 z = zrnd > 0 ? (zrnd - rand() % (int)(zrnd * 2 + 1)) / 100.0 + z : z;
480 #else
481 z = 0;
482 #endif
483 double _x = x;
484 x = scalex * (x + m_style.get().fontShiftX * y) - org.x;
485 y = scaley * (y + m_style.get().fontShiftY * _x) - org.y;
487 xx = x*caz + y*saz;
488 yy = -(x*saz - y*caz);
489 zz = z;
491 x = xx;
492 y = yy*cax + zz*sax;
493 z = yy*sax - zz*cax;
495 xx = x*cay + z*say;
496 yy = y;
497 zz = x*say - z*cay;
499 zz = max(zz, -19000);
501 x = (xx * 20000) / (zz + 20000);
502 y = (yy * 20000) / (zz + 20000);
504 path_data->mpPathPoints[i].x = (LONG)(x + org.x + 0.5);
505 path_data->mpPathPoints[i].y = (LONG)(y + org.y + 0.5);
509 void CWord::Transform_SSE2(PathData* path_data, const CPoint &org )
511 // __m128 union data type currently not supported with Intel C++ Compiler, so just call C version
512 #ifdef __ICL
513 Transform_C(org);
514 #else
515 // SSE code
516 // speed up ~1.5-1.7x
517 double scalex = m_style.get().fontScaleX/100;
518 double scaley = m_style.get().fontScaleY/100;
520 double caz = cos((3.1415/180)*m_style.get().fontAngleZ);
521 double saz = sin((3.1415/180)*m_style.get().fontAngleZ);
522 double cax = cos((3.1415/180)*m_style.get().fontAngleX);
523 double sax = sin((3.1415/180)*m_style.get().fontAngleX);
524 double cay = cos((3.1415/180)*m_style.get().fontAngleY);
525 double say = sin((3.1415/180)*m_style.get().fontAngleY);
527 __m128 __xshift = _mm_set_ps1(m_style.get().fontShiftX);
528 __m128 __yshift = _mm_set_ps1(m_style.get().fontShiftY);
530 __m128 __xorg = _mm_set_ps1(org.x);
531 __m128 __yorg = _mm_set_ps1(org.y);
533 __m128 __xscale = _mm_set_ps1(scalex);
534 __m128 __yscale = _mm_set_ps1(scaley);
536 #ifdef _VSMOD
537 // patch m003. random text points
538 double xrnd = m_style.get().mod_rand.X*100;
539 double yrnd = m_style.get().mod_rand.Y*100;
540 double zrnd = m_style.get().mod_rand.Z*100;
542 srand(m_style.get().mod_rand.Seed);
544 __m128 __xsz = _mm_setzero_ps();
545 __m128 __ysz = _mm_setzero_ps();
547 __m128 __dst1x, __dst1y, __dst213x, __dst213y, __dst3x, __dst3y;
549 __m128 __miny;
550 __m128 __minx = _mm_set_ps(INT_MAX, INT_MAX, 0, 0);
551 __m128 __max = _mm_set_ps(-INT_MAX, -INT_MAX, 1, 1);
553 bool is_dist = m_style.get().mod_distort.enabled;
554 if(is_dist) {
555 for(int i = 0; i < path_data->mPathPoints; i++) {
556 __m128 __point = _mm_set_ps(path_data->mpPathPoints[i].x, path_data->mpPathPoints[i].y, 0, 0);
557 __minx = _mm_min_ps(__minx, __point);
558 __max = _mm_max_ps(__max, __point);
561 __m128 __zero = _mm_setzero_ps();
562 __max = _mm_sub_ps(__max, __minx); // xsz, ysz, 1, 1
563 __max = _mm_max_ps(__max, __zero);
565 __xsz = _mm_shuffle_ps(__max, __max, _MM_SHUFFLE(3,3,3,3));
566 __ysz = _mm_shuffle_ps(__max, __max, _MM_SHUFFLE(2,2,2,2));
568 __miny = _mm_shuffle_ps(__minx, __minx, _MM_SHUFFLE(2,2,2,2));
569 __minx = _mm_shuffle_ps(__minx, __minx, _MM_SHUFFLE(3,3,3,3));
571 __dst1x = _mm_set_ps1(m_style.get().mod_distort.pointsx[0]);
572 __dst1y = _mm_set_ps1(m_style.get().mod_distort.pointsy[0]);
573 __dst3x = _mm_set_ps1(m_style.get().mod_distort.pointsx[2]);
574 __dst3y = _mm_set_ps1(m_style.get().mod_distort.pointsy[2]);
575 __dst213x = _mm_set_ps1(m_style.get().mod_distort.pointsx[1]); // 2 - 1 - 3
576 __dst213x = _mm_sub_ps(__dst213x, __dst1x);
577 __dst213x = _mm_sub_ps(__dst213x, __dst3x);
579 __dst213y = _mm_set_ps1(m_style.get().mod_distort.pointsy[1]);
580 __dst213x = _mm_sub_ps(__dst213y, __dst1y);
581 __dst213x = _mm_sub_ps(__dst213y, __dst3y);
583 #endif
585 __m128 __caz = _mm_set_ps1(caz);
586 __m128 __saz = _mm_set_ps1(saz);
587 __m128 __cax = _mm_set_ps1(cax);
588 __m128 __sax = _mm_set_ps1(sax);
589 __m128 __cay = _mm_set_ps1(cay);
590 __m128 __say = _mm_set_ps1(say);
592 // this can be paralleled for openmp
593 int mPathPointsD4 = path_data->mPathPoints / 4;
594 int mPathPointsM4 = path_data->mPathPoints % 4;
596 for(ptrdiff_t i = 0; i < mPathPointsD4 + 1; i++) {
597 POINT* const temp_points = path_data->mpPathPoints + 4 * i;
599 __m128 __pointx, __pointy;
600 // we can't use load .-.
601 if(i != mPathPointsD4) {
602 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, temp_points[2].x, temp_points[3].x);
603 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, temp_points[2].y, temp_points[3].y);
604 } else { // last cycle
605 switch(mPathPointsM4) {
606 default:
607 case 0:
608 continue;
609 case 1:
610 __pointx = _mm_set_ps(temp_points[0].x, 0, 0, 0);
611 __pointy = _mm_set_ps(temp_points[0].y, 0, 0, 0);
612 break;
613 case 2:
614 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, 0, 0);
615 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, 0, 0);
616 break;
617 case 3:
618 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, temp_points[2].x, 0);
619 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, temp_points[2].y, 0);
620 break;
624 #ifdef _VSMOD
625 __m128 __pointz = _mm_set_ps1(m_style.get().mod_z);
627 // distort
628 if(is_dist) {
629 //P = P0 + (P1 - P0)u + (P3 - P0)v + (P0 + P2 - P1 - P3)uv
630 __m128 __u = _mm_sub_ps(__pointx, __minx);
631 __m128 __v = _mm_sub_ps(__pointy, __miny);
632 __m128 __1_xsz = _mm_rcp_ps(__xsz);
633 __m128 __1_ysz = _mm_rcp_ps(__ysz);
634 __u = _mm_mul_ps(__u, __1_xsz);
635 __v = _mm_mul_ps(__v, __1_ysz);
637 // x
638 __pointx = _mm_mul_ps(__dst213x, __u);
639 __pointx = _mm_mul_ps(__pointx, __v);
641 __m128 __tmpx = _mm_mul_ps(__dst3x, __v);
642 __pointx = _mm_add_ps(__pointx, __tmpx);
643 __tmpx = _mm_mul_ps(__dst1x, __u);
644 __pointx = _mm_add_ps(__pointx, __tmpx);
646 __pointx = _mm_mul_ps(__pointx, __xsz);
647 __pointx = _mm_add_ps(__pointx, __minx);
649 // y
650 __pointy = _mm_mul_ps(__dst213y, __u);
651 __pointy = _mm_mul_ps(__pointy, __v);
653 __m128 __tmpy = _mm_mul_ps(__dst3y, __v);
654 __pointy = _mm_add_ps(__pointy, __tmpy);
655 __tmpy = _mm_mul_ps(__dst1y, __u);
656 __pointy = _mm_add_ps(__pointy, __tmpy);
658 __pointy = _mm_mul_ps(__pointy, __ysz);
659 __pointy = _mm_add_ps(__pointy, __miny);
662 // randomize
663 if(xrnd!=0 || yrnd!=0 || zrnd!=0) {
664 __declspec(align(16)) float rx[4], ry[4], rz[4];
665 for(int k=0; k<4; k++) {
666 rx[k] = xrnd > 0 ? (xrnd - rand() % (int)(xrnd * 2 + 1)) : 0;
667 ry[k] = yrnd > 0 ? (yrnd - rand() % (int)(yrnd * 2 + 1)) : 0;
668 rz[k] = zrnd > 0 ? (zrnd - rand() % (int)(zrnd * 2 + 1)) : 0;
670 __m128 __001 = _mm_set_ps1(0.01f);
672 if(xrnd!=0) {
673 __m128 __rx = _mm_load_ps(rx);
674 __rx = _mm_mul_ps(__rx, __001);
675 __pointx = _mm_add_ps(__pointx, __rx);
678 if(yrnd!=0) {
679 __m128 __ry = _mm_load_ps(ry);
680 __ry = _mm_mul_ps(__ry, __001);
681 __pointy = _mm_add_ps(__pointy, __ry);
684 if(zrnd!=0) {
685 __m128 __rz = _mm_load_ps(rz);
686 __rz = _mm_mul_ps(__rz, __001);
687 __pointz = _mm_add_ps(__pointz, __rz);
690 #else
691 __m128 __pointz = _mm_set_ps1(0);
692 #endif
694 // scale and shift
695 __m128 __tmpx;
696 if(m_style.get().fontShiftX!=0) {
697 __tmpx = _mm_mul_ps(__xshift, __pointy);
698 __tmpx = _mm_add_ps(__tmpx, __pointx);
699 } else {
700 __tmpx = __pointx;
702 __tmpx = _mm_mul_ps(__tmpx, __xscale);
703 __tmpx = _mm_sub_ps(__tmpx, __xorg);
705 __m128 __tmpy;
706 if(m_style.get().fontShiftY!=0) {
707 __tmpy = _mm_mul_ps(__yshift, __pointx);
708 __tmpy = _mm_add_ps(__tmpy, __pointy);
709 } else {
710 __tmpy = __pointy;
712 __tmpy = _mm_mul_ps(__tmpy, __yscale);
713 __tmpy = _mm_sub_ps(__tmpy, __yorg);
715 // rotate
716 __m128 __xx = _mm_mul_ps(__tmpx, __caz);
717 __m128 __yy = _mm_mul_ps(__tmpy, __saz);
718 __pointx = _mm_add_ps(__xx, __yy);
719 __xx = _mm_mul_ps(__tmpx, __saz);
720 __yy = _mm_mul_ps(__tmpy, __caz);
721 __pointy = _mm_sub_ps(__yy, __xx);
723 __m128 __zz = _mm_mul_ps(__pointz, __sax);
724 __yy = _mm_mul_ps(__pointy, __cax);
725 __pointy = _mm_add_ps(__yy, __zz);
726 __zz = _mm_mul_ps(__pointz, __cax);
727 __yy = _mm_mul_ps(__pointy, __sax);
728 __pointz = _mm_sub_ps(__zz, __yy);
730 __xx = _mm_mul_ps(__pointx, __cay);
731 __zz = _mm_mul_ps(__pointz, __say);
732 __pointx = _mm_add_ps(__xx, __zz);
733 __xx = _mm_mul_ps(__pointx, __say);
734 __zz = _mm_mul_ps(__pointz, __cay);
735 __pointz = _mm_sub_ps(__xx, __zz);
737 __zz = _mm_set_ps1(-19000);
738 __pointz = _mm_max_ps(__pointz, __zz);
740 __m128 __20000 = _mm_set_ps1(20000);
741 __zz = _mm_add_ps(__pointz, __20000);
742 __zz = _mm_rcp_ps(__zz);
744 __pointx = _mm_mul_ps(__pointx, __20000);
745 __pointx = _mm_mul_ps(__pointx, __zz);
747 __pointy = _mm_mul_ps(__pointy, __20000);
748 __pointy = _mm_mul_ps(__pointy, __zz);
750 __pointx = _mm_add_ps(__pointx, __xorg);
751 __pointy = _mm_add_ps(__pointy, __yorg);
753 __m128 __05 = _mm_set_ps1(0.5);
755 __pointx = _mm_add_ps(__pointx, __05);
756 __pointy = _mm_add_ps(__pointy, __05);
758 if(i == mPathPointsD4) { // last cycle
759 for(int k=0; k<mPathPointsM4; k++) {
760 temp_points[k].x = static_cast<LONG>(__pointx.m128_f32[3-k]);
761 temp_points[k].y = static_cast<LONG>(__pointy.m128_f32[3-k]);
763 } else {
764 for(int k=0; k<4; k++) {
765 temp_points[k].x = static_cast<LONG>(__pointx.m128_f32[3-k]);
766 temp_points[k].y = static_cast<LONG>(__pointy.m128_f32[3-k]);
770 #endif // __ICL
773 bool CWord::CreateOpaqueBox()
775 if(m_pOpaqueBox) return(true);
776 STSStyle style = m_style.get();
777 style.borderStyle = 0;
778 style.outlineWidthX = style.outlineWidthY = 0;
779 style.colors[0] = m_style.get().colors[2];
780 style.alpha[0] = m_style.get().alpha[2];
781 int w = (int)(m_style.get().outlineWidthX + 0.5);
782 int h = (int)(m_style.get().outlineWidthY + 0.5);
783 CStringW str;
784 str.Format(L"m %d %d l %d %d %d %d %d %d",
785 -w, -h,
786 m_width+w, -h,
787 m_width+w, m_ascent+m_descent+h,
788 -w, m_ascent+m_descent+h);
789 m_pOpaqueBox.reset( new CPolygon(FwSTSStyle(style), str, 0, 0, 0, 1.0/8, 1.0/8, 0) );
790 return(!!m_pOpaqueBox);
793 void CWord::PaintAll( SharedPtrCWord word,
794 const CPoint& shadowPos, const CPoint& outlinePos, const CPoint& bodyPos, const CPoint& org,
795 OverlayList* shadow, OverlayList* outline, OverlayList* body )
797 CPoint transOrg;
798 if(shadow!=NULL)
800 //has shadow
801 transOrg = org - shadowPos;
803 else if(outline!=NULL)
805 //has outline
806 transOrg = org - outlinePos;
808 else if(body!=NULL)
810 transOrg = org - bodyPos;
812 if(shadow!=NULL)
814 Paint(word, shadowPos, transOrg, shadow);
816 if(outline!=NULL)
818 Paint(word, outlinePos, transOrg, outline);
820 if(body!=NULL)
822 Paint(word, bodyPos, transOrg, body);
827 // CText
829 CText::CText(const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend)
830 : CWord(style, str, ktype, kstart, kend)
832 if(m_str == L" ")
834 m_fWhiteSpaceChar = true;
836 SharedPtrTextInfo text_info;
837 TextInfoCacheKey text_info_key;
838 text_info_key.m_str = m_str;
839 text_info_key.m_style = m_style;
840 TextInfoMruCache* text_info_cache = CacheManager::GetTextInfoCache();
841 POSITION pos = text_info_cache->Lookup(text_info_key);
842 if(pos==NULL)
844 TextInfo* tmp=new TextInfo();
845 GetTextInfo(tmp, m_style, m_str);
846 text_info.reset(tmp);
847 text_info_cache->UpdateCache(text_info_key, text_info);
849 else
851 text_info = text_info_cache->GetAt(pos);
852 text_info_cache->UpdateCache( pos );
854 this->m_ascent = text_info->m_ascent;
855 this->m_descent = text_info->m_descent;
856 this->m_width = text_info->m_width;
859 CText::CText( const CText& src ):CWord(src)
861 m_width = src.m_width;
864 SharedPtrCWord CText::Copy()
866 SharedPtrCWord result(new CText(*this));
867 return result;
870 bool CText::Append(const SharedPtrCWord& w)
872 boost::shared_ptr<CText> p = boost::dynamic_pointer_cast<CText>(w);
873 return (p && CWord::Append(w));
876 bool CText::CreatePath(PathData* path_data)
878 FwCMyFont font(m_style);
879 HFONT hOldFont = SelectFont(g_hDC, font.get());
880 int width = 0;
881 if(m_style.get().fontSpacing || (long)GetVersion() < 0)
883 bool bFirstPath = true;
884 for(LPCWSTR s = m_str; *s; s++)
886 CSize extent;
887 if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
888 path_data->PartialBeginPath(g_hDC, bFirstPath);
889 bFirstPath = false;
890 TextOutW(g_hDC, 0, 0, s, 1);
891 path_data->PartialEndPath(g_hDC, width, 0);
892 width += extent.cx + (int)m_style.get().fontSpacing;
895 else
897 CSize extent;
898 if(!GetTextExtentPoint32W(g_hDC, m_str, m_str.GetLength(), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
899 path_data->BeginPath(g_hDC);
900 TextOutW(g_hDC, 0, 0, m_str, m_str.GetLength());
901 path_data->EndPath(g_hDC);
903 SelectFont(g_hDC, hOldFont);
904 return(true);
907 void CText::GetTextInfo(TextInfo *output, const FwSTSStyle& style, const CStringW& str )
909 FwCMyFont font(style);
910 output->m_ascent = (int)(style.get().fontScaleY/100*font.get().m_ascent);
911 output->m_descent = (int)(style.get().fontScaleY/100*font.get().m_descent);
913 HFONT hOldFont = SelectFont(g_hDC, font.get());
914 if(style.get().fontSpacing || (long)GetVersion() < 0)
916 bool bFirstPath = true;
917 for(LPCWSTR s = str; *s; s++)
919 CSize extent;
920 if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
921 output->m_width += extent.cx + (int)style.get().fontSpacing;
923 // m_width -= (int)m_style.get().fontSpacing; // TODO: subtract only at the end of the line
925 else
927 CSize extent;
928 if(!GetTextExtentPoint32W(g_hDC, str, wcslen(str), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
929 output->m_width += extent.cx;
931 output->m_width = (int)(style.get().fontScaleX/100*output->m_width + 4) >> 3;
932 SelectFont(g_hDC, hOldFont);
935 // CPolygon
937 CPolygon::CPolygon(const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend, double scalex, double scaley, int baseline)
938 : CWord(style, str, ktype, kstart, kend)
939 , m_scalex(scalex), m_scaley(scaley), m_baseline(baseline)
941 ParseStr();
944 CPolygon::CPolygon(CPolygon& src) : CWord(src)
946 m_scalex = src.m_scalex;
947 m_scaley = src.m_scaley;
948 m_baseline = src.m_baseline;
949 m_width = src.m_width;
950 m_ascent = src.m_ascent;
951 m_descent = src.m_descent;
952 m_pathTypesOrg.Copy(src.m_pathTypesOrg);
953 m_pathPointsOrg.Copy(src.m_pathPointsOrg);
955 CPolygon::~CPolygon()
959 SharedPtrCWord CPolygon::Copy()
961 SharedPtrCWord result(DNew CPolygon(*this));
962 return result;
965 bool CPolygon::Append(const SharedPtrCWord& w)
967 // TODO
968 return(false);
971 bool CPolygon::Get6BitFixedPoint(CStringW& str, LONG& ret)
973 LPWSTR s = (LPWSTR)(LPCWSTR)str, e = s;
974 ret = wcstod(str, &e) * 64;
975 str.Delete(0,e-s);
976 XY_LOG_INFO(ret);
977 return(e > s);
980 bool CPolygon::GetPOINT(CStringW& str, POINT& ret)
982 return(Get6BitFixedPoint(str, ret.x) && Get6BitFixedPoint(str, ret.y));
985 bool CPolygon::ParseStr()
987 if(m_pathTypesOrg.GetCount() > 0) return(true);
988 CPoint p;
989 int j, lastsplinestart = -1, firstmoveto = -1, lastmoveto = -1;
990 CStringW str = m_str;
991 str.SpanIncluding(L"mnlbspc 0123456789");
992 str.Replace(L"m", L"*m");
993 str.Replace(L"n", L"*n");
994 str.Replace(L"l", L"*l");
995 str.Replace(L"b", L"*b");
996 str.Replace(L"s", L"*s");
997 str.Replace(L"p", L"*p");
998 str.Replace(L"c", L"*c");
999 int k = 0;
1000 for(CStringW s = str.Tokenize(L"*", k); !s.IsEmpty(); s = str.Tokenize(L"*", k))
1002 WCHAR c = s[0];
1003 s.TrimLeft(L"mnlbspc ");
1004 switch(c)
1006 case 'm':
1007 lastmoveto = m_pathTypesOrg.GetCount();
1008 if(firstmoveto == -1)
1009 firstmoveto = lastmoveto;
1010 while(GetPOINT(s, p)) {
1011 m_pathTypesOrg.Add(PT_MOVETO);
1012 m_pathPointsOrg.Add(p);
1014 break;
1015 case 'n':
1016 while(GetPOINT(s, p)) {
1017 m_pathTypesOrg.Add(PT_MOVETONC);
1018 m_pathPointsOrg.Add(p);
1020 break;
1021 case 'l':
1022 if (m_pathPointsOrg.GetCount() < 1) {
1023 break;
1025 while(GetPOINT(s, p)) {
1026 m_pathTypesOrg.Add(PT_LINETO);
1027 m_pathPointsOrg.Add(p);
1029 break;
1030 case 'b':
1031 j = m_pathTypesOrg.GetCount();
1032 if (j < 1) {
1033 break;
1035 while(GetPOINT(s, p)) {
1036 m_pathTypesOrg.Add(PT_BEZIERTO);
1037 m_pathPointsOrg.Add(p);
1038 j++;
1040 j = m_pathTypesOrg.GetCount() - ((m_pathTypesOrg.GetCount()-j)%3);
1041 m_pathTypesOrg.SetCount(j);
1042 m_pathPointsOrg.SetCount(j);
1043 break;
1044 case 's':
1045 if (m_pathPointsOrg.GetCount() < 1) {
1046 break;
1049 j = lastsplinestart = m_pathTypesOrg.GetCount();
1050 int i = 3;
1051 while(i-- && GetPOINT(s, p)) {
1052 m_pathTypesOrg.Add(PT_BSPLINETO);
1053 m_pathPointsOrg.Add(p);
1054 j++;
1056 if(m_pathTypesOrg.GetCount()-lastsplinestart < 3) {
1057 m_pathTypesOrg.SetCount(lastsplinestart);
1058 m_pathPointsOrg.SetCount(lastsplinestart);
1059 lastsplinestart = -1;
1062 // no break here
1063 case 'p':
1064 if (m_pathPointsOrg.GetCount() < 3) {
1065 break;
1067 while(GetPOINT(s, p)) {
1068 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1069 m_pathPointsOrg.Add(p);
1071 break;
1072 case 'c':
1073 if(lastsplinestart > 0)
1075 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1076 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1077 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1078 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)
1079 m_pathPointsOrg.Add(p);
1080 p = m_pathPointsOrg[lastsplinestart];
1081 m_pathPointsOrg.Add(p);
1082 p = m_pathPointsOrg[lastsplinestart+1];
1083 m_pathPointsOrg.Add(p);
1084 lastsplinestart = -1;
1086 break;
1087 default:
1088 break;
1091 if(lastmoveto == -1 || firstmoveto > 0)
1093 m_pathTypesOrg.RemoveAll();
1094 m_pathPointsOrg.RemoveAll();
1095 return(false);
1097 int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
1098 for(size_t i = 0; i < m_pathTypesOrg.GetCount(); i++)
1100 m_pathPointsOrg[i].x = (int)(m_scalex * m_pathPointsOrg[i].x);
1101 m_pathPointsOrg[i].y = (int)(m_scaley * m_pathPointsOrg[i].y);
1102 if(minx > m_pathPointsOrg[i].x) minx = m_pathPointsOrg[i].x;
1103 if(miny > m_pathPointsOrg[i].y) miny = m_pathPointsOrg[i].y;
1104 if(maxx < m_pathPointsOrg[i].x) maxx = m_pathPointsOrg[i].x;
1105 if(maxy < m_pathPointsOrg[i].y) maxy = m_pathPointsOrg[i].y;
1107 m_width = max(maxx - minx, 0);
1108 m_ascent = max(maxy - miny, 0);
1109 int baseline = (int)(64 * m_scaley * m_baseline);
1110 m_descent = baseline;
1111 m_ascent -= baseline;
1112 m_width = ((int)(m_style.get().fontScaleX/100 * m_width) + 4) >> 3;
1113 m_ascent = ((int)(m_style.get().fontScaleY/100 * m_ascent) + 4) >> 3;
1114 m_descent = ((int)(m_style.get().fontScaleY/100 * m_descent) + 4) >> 3;
1115 return(true);
1118 bool CPolygon::CreatePath(PathData* path_data)
1120 int len = m_pathTypesOrg.GetCount();
1121 if(len == 0) return(false);
1122 if(path_data->mPathPoints != len)
1124 path_data->mpPathTypes = (BYTE*)realloc(path_data->mpPathTypes, len*sizeof(BYTE));
1125 path_data->mpPathPoints = (POINT*)realloc(path_data->mpPathPoints, len*sizeof(POINT));
1126 if(!path_data->mpPathTypes || !path_data->mpPathPoints) return(false);
1127 path_data->mPathPoints = len;
1129 memcpy(path_data->mpPathTypes, m_pathTypesOrg.GetData(), len*sizeof(BYTE));
1130 memcpy(path_data->mpPathPoints, m_pathPointsOrg.GetData(), len*sizeof(POINT));
1131 return(true);
1134 // CClipper
1136 CClipper::CClipper(CStringW str, CSize size, double scalex, double scaley, bool inverse)
1137 : m_polygon( new CPolygon(FwSTSStyle(), str, 0, 0, 0, scalex, scaley, 0) )
1138 , m_size(size), m_inverse(inverse)
1139 , m_effectType(-1), m_painted(false)
1144 CClipper::~CClipper()
1146 m_pAlphaMask.reset(NULL);
1149 void CClipper::PaintBaseClipper()
1151 //m_pAlphaMask = NULL;
1152 if(m_size.cx < 0 || m_size.cy < 0)
1153 return;
1154 m_pAlphaMask.reset(new BYTE[m_size.cx*m_size.cy]);
1155 if( !m_pAlphaMask )
1156 return;
1157 memset( m_pAlphaMask.get(), 0, m_size.cx*m_size.cy);
1158 OverlayList overlay_list;
1159 CWord::Paint( m_polygon, CPoint(0, 0), CPoint(0, 0), &overlay_list );
1160 int w = overlay_list.overlay->mOverlayWidth, h = overlay_list.overlay->mOverlayHeight;
1161 int x = (overlay_list.overlay->mOffsetX+4)>>3, y = (overlay_list.overlay->mOffsetY+4)>>3;
1162 int xo = 0, yo = 0;
1163 if(x < 0) {xo = -x; w -= -x; x = 0;}
1164 if(y < 0) {yo = -y; h -= -y; y = 0;}
1165 if(x+w > m_size.cx) w = m_size.cx-x;
1166 if(y+h > m_size.cy) h = m_size.cy-y;
1167 if(w <= 0 || h <= 0) return;
1168 const BYTE* src = overlay_list.overlay->mpOverlayBuffer.body + (overlay_list.overlay->mOverlayPitch * yo + xo);
1169 BYTE* dst = m_pAlphaMask.get() + m_size.cx * y + x;
1170 while(h--)
1172 //for(int wt=0; wt<w; ++wt)
1173 // dst[wt] = src[wt];
1174 memcpy(dst, src, w);
1175 src += overlay_list.overlay->mOverlayPitch;
1176 dst += m_size.cx;
1178 if(m_inverse)
1180 BYTE* dst = m_pAlphaMask.get();
1181 for(int i = m_size.cx*m_size.cy; i>0; --i, ++dst)
1182 *dst = 0x40 - *dst; // mask is 6 bit
1186 void CClipper::PaintBannerClipper()
1188 int width = m_effect.param[2];
1189 int w = m_size.cx, h = m_size.cy;
1191 PaintBaseClipper();
1192 int da = (64<<8)/width;
1193 BYTE* am = m_pAlphaMask.get();
1194 for(int j = 0; j < h; j++, am += w)
1196 int a = 0;
1197 int k = min(width, w);
1198 for(int i = 0; i < k; i++, a += da)
1199 am[i] = (am[i]*a)>>14;
1200 a = 0x40<<8;
1201 k = w-width;
1202 if(k < 0) {a -= -k*da; k = 0;}
1203 for(int i = k; i < w; i++, a -= da)
1204 am[i] = (am[i]*a)>>14;
1208 void CClipper::PaintScrollClipper()
1210 int height = m_effect.param[4];
1211 int w = m_size.cx, h = m_size.cy;
1213 PaintBaseClipper();
1215 int da = (64<<8)/height;
1216 int a = 0;
1217 int k = m_effect.param[0]>>3;
1218 int l = k+height;
1219 if(k < 0) {a += -k*da; k = 0;}
1220 if(l > h) {l = h;}
1221 if(k < h)
1223 BYTE* am = &m_pAlphaMask[k*w];
1224 memset(m_pAlphaMask.get(), 0, am - m_pAlphaMask.get());
1225 for(int j = k; j < l; j++, a += da)
1227 for(int i = 0; i < w; i++, am++)
1228 *am = ((*am)*a)>>14;
1231 da = -(64<<8)/height;
1232 a = 0x40<<8;
1233 l = m_effect.param[1]>>3;
1234 k = l-height;
1235 if(k < 0) {a += -k*da; k = 0;}
1236 if(l > h) {l = h;}
1237 if(k < h)
1239 BYTE* am = &m_pAlphaMask[k*w];
1240 int j = k;
1241 for(; j < l; j++, a += da)
1243 for(int i = 0; i < w; i++, am++)
1244 *am = ((*am)*a)>>14;
1246 memset(am, 0, (h-j)*w);
1250 void CClipper::Paint()
1252 if(!m_painted)
1254 m_painted=true;
1255 switch(m_effectType)
1257 case -1:
1258 PaintBaseClipper();
1259 break;
1260 case EF_BANNER:
1261 PaintBannerClipper();
1262 break;
1263 case EF_SCROLL:
1264 PaintScrollClipper();
1265 break;
1270 void CClipper::SetEffect( const Effect& effect, int effectType )
1272 m_effectType = effectType;
1273 m_effect = effect;
1276 const SharedArrayByte& CClipper::GetAlphaMask()
1278 Paint();
1279 return m_pAlphaMask;
1282 // CLine
1284 CLine::~CLine()
1286 //POSITION pos = GetHeadPosition();
1287 //while(pos) delete GetNext(pos);
1290 void CLine::Compact()
1292 POSITION pos = GetHeadPosition();
1293 while(pos)
1295 SharedPtrCWord w = GetNext(pos);
1296 if(!w->m_fWhiteSpaceChar) break;
1297 m_width -= w->m_width;
1298 // delete w;
1299 RemoveHead();
1301 pos = GetTailPosition();
1302 while(pos)
1304 SharedPtrCWord w = GetPrev(pos);
1305 if(!w->m_fWhiteSpaceChar) break;
1306 m_width -= w->m_width;
1307 // delete w;
1308 RemoveTail();
1310 if(IsEmpty()) return;
1311 CLine l;
1312 l.AddTailList(this);
1313 RemoveAll();
1314 SharedPtrCWord last;
1315 pos = l.GetHeadPosition();
1316 while(pos)
1318 SharedPtrCWord w = l.GetNext(pos);
1319 if(!last || !last->Append(w))
1320 AddTail(last = w->Copy());
1322 m_ascent = m_descent = m_borderX = m_borderY = 0;
1323 pos = GetHeadPosition();
1324 while(pos)
1326 SharedPtrCWord w = GetNext(pos);
1327 if(m_ascent < w->m_ascent) m_ascent = w->m_ascent;
1328 if(m_descent < w->m_descent) m_descent = w->m_descent;
1329 if(m_borderX < w->m_style.get().outlineWidthX) m_borderX = (int)(w->m_style.get().outlineWidthX+0.5);
1330 if(m_borderY < w->m_style.get().outlineWidthY) m_borderY = (int)(w->m_style.get().outlineWidthY+0.5);
1334 CRect CLine::PaintAll( CompositeDrawItemList* output, SubPicDesc& spd, const CRect& clipRect,
1335 SharedArrayByte pAlphaMask, CPoint p, const CPoint& org, const int time, const int alpha )
1337 CRect bbox(0, 0, 0, 0);
1338 POSITION pos = GetHeadPosition();
1339 POSITION outputPos = output->GetHeadPosition();
1340 while(pos)
1342 SharedPtrCWord w = GetNext(pos);
1343 CompositeDrawItem& outputItem = output->GetNext(outputPos);
1344 if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
1345 CPoint shadowPos, outlinePos, bodyPos, transOrg;
1346 shadowPos.x = p.x + static_cast<int>(w->m_style.get().shadowDepthX+0.5);
1347 shadowPos.y = p.y + m_ascent - w->m_ascent + static_cast<int>(w->m_style.get().shadowDepthY+0.5);
1348 outlinePos = CPoint(p.x, p.y + m_ascent - w->m_ascent);
1349 bodyPos = CPoint(p.x, p.y + m_ascent - w->m_ascent);
1350 bool hasShadow = w->m_style.get().shadowDepthX != 0 || w->m_style.get().shadowDepthY != 0;
1351 bool hasOutline = w->m_style.get().outlineWidthX+w->m_style.get().outlineWidthY > 0 && !(w->m_ktype == 2 && time < w->m_kstart);
1352 bool hasBody = true;
1354 OverlayList shadowOverlay, outlineOverlay, bodyOverlay;
1355 CWord::PaintAll(w, shadowPos, outlinePos, bodyPos, org,
1356 hasShadow ? &shadowOverlay : NULL,
1357 hasOutline ? &outlineOverlay : NULL,
1358 hasBody ? &bodyOverlay : NULL);
1359 //shadow
1360 if(hasShadow)
1362 DWORD a = 0xff - w->m_style.get().alpha[3];
1363 if(alpha > 0) a = MulDiv(a, 0xff - alpha, 0xff);
1364 COLORREF shadow = revcolor(w->m_style.get().colors[3]) | (a<<24);
1365 DWORD sw[6] = {shadow, -1};
1366 //xy
1367 if(spd.type == MSP_XY_AUYV)
1369 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1371 else if(spd.type == MSP_AYUV || spd.type == MSP_AYUV_PLANAR)
1373 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1375 if(w->m_style.get().borderStyle == 0)
1377 outputItem.shadow.reset(
1378 Rasterizer::CreateDrawItem(spd, shadowOverlay.overlay, clipRect, pAlphaMask, shadowPos.x, shadowPos.y, sw,
1379 w->m_ktype > 0 || w->m_style.get().alpha[0] < 0xff,
1380 (w->m_style.get().outlineWidthX+w->m_style.get().outlineWidthY > 0) && !(w->m_ktype == 2 && time < w->m_kstart))
1382 bbox |= Rasterizer::DryDraw(spd, *outputItem.shadow);
1384 else if(w->m_style.get().borderStyle == 1 && w->m_pOpaqueBox)
1386 outputItem.shadow.reset(
1387 Rasterizer::CreateDrawItem(spd, shadowOverlay.next->overlay, clipRect, pAlphaMask, shadowPos.x, shadowPos.y, sw, true, false)
1389 bbox |= Rasterizer::DryDraw(spd, *outputItem.shadow);
1392 //outline
1393 if(hasOutline)
1395 DWORD aoutline = w->m_style.get().alpha[2];
1396 if(alpha > 0) aoutline += MulDiv(alpha, 0xff - w->m_style.get().alpha[2], 0xff);
1397 COLORREF outline = revcolor(w->m_style.get().colors[2]) | ((0xff-aoutline)<<24);
1398 DWORD sw[6] = {outline, -1};
1399 //xy
1400 if(spd.type == MSP_XY_AUYV)
1402 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1404 else if(spd.type == MSP_AYUV || spd.type == MSP_AYUV_PLANAR)
1406 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1408 if(w->m_style.get().borderStyle == 0)
1410 outputItem.outline.reset(
1411 Rasterizer::CreateDrawItem(spd, outlineOverlay.overlay, clipRect, pAlphaMask, outlinePos.x, outlinePos.y, sw, !w->m_style.get().alpha[0] && !w->m_style.get().alpha[1] && !alpha, true)
1413 bbox |= Rasterizer::DryDraw(spd, *outputItem.outline);
1415 else if(w->m_style.get().borderStyle == 1 && w->m_pOpaqueBox)
1417 outputItem.outline.reset(
1418 Rasterizer::CreateDrawItem(spd, outlineOverlay.next->overlay, clipRect, pAlphaMask, outlinePos.x, outlinePos.y, sw, true, false)
1420 bbox |= Rasterizer::DryDraw(spd, *outputItem.outline);
1423 //body
1424 if(hasBody)
1426 // colors
1427 DWORD aprimary = w->m_style.get().alpha[0];
1428 if(alpha > 0) aprimary += MulDiv(alpha, 0xff - w->m_style.get().alpha[0], 0xff);
1429 COLORREF primary = revcolor(w->m_style.get().colors[0]) | ((0xff-aprimary)<<24);
1430 DWORD asecondary = w->m_style.get().alpha[1];
1431 if(alpha > 0) asecondary += MulDiv(alpha, 0xff - w->m_style.get().alpha[1], 0xff);
1432 COLORREF secondary = revcolor(w->m_style.get().colors[1]) | ((0xff-asecondary)<<24);
1433 DWORD sw[6] = {primary, 0, secondary};
1434 // karaoke
1435 double t;
1436 if(w->m_ktype == 0 || w->m_ktype == 2)
1438 t = time < w->m_kstart ? 0 : 1;
1440 else if(w->m_ktype == 1)
1442 if(time < w->m_kstart) t = 0;
1443 else if(time < w->m_kend)
1445 t = 1.0 * (time - w->m_kstart) / (w->m_kend - w->m_kstart);
1446 double angle = fmod(w->m_style.get().fontAngleZ, 360.0);
1447 if(angle > 90 && angle < 270)
1449 t = 1-t;
1450 COLORREF tmp = sw[0];
1451 sw[0] = sw[2];
1452 sw[2] = tmp;
1455 else t = 1.0;
1457 if(t >= 1)
1459 sw[1] = 0xffffffff;
1461 sw[3] = (int)(w->m_style.get().outlineWidthX + t*w->m_width) >> 3;
1462 sw[4] = sw[2];
1463 sw[5] = 0x00ffffff;
1464 //xy
1465 if(spd.type == MSP_XY_AUYV)
1467 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1468 sw[2] =rgb2yuv(sw[2], XY_AUYV);
1469 sw[4] =rgb2yuv(sw[4], XY_AUYV);
1471 else if(spd.type == MSP_AYUV || spd.type == MSP_AYUV_PLANAR)
1473 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1474 sw[2] =rgb2yuv(sw[2], XY_AYUV);
1475 sw[4] =rgb2yuv(sw[4], XY_AYUV);
1477 outputItem.body.reset(
1478 Rasterizer::CreateDrawItem(spd, bodyOverlay.overlay, clipRect, pAlphaMask, bodyPos.x, bodyPos.y, sw, true, false)
1480 bbox |= Rasterizer::DryDraw(spd, *outputItem.body);
1482 p.x += w->m_width;
1484 return(bbox);
1487 void CLine::AddWord2Tail( SharedPtrCWord words )
1489 __super::AddTail(words);
1492 bool CLine::IsEmpty()
1494 return __super::IsEmpty();
1497 int CLine::GetWordCount()
1499 return GetCount();
1502 // CSubtitle
1504 CSubtitle::CSubtitle()
1506 memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
1507 m_pClipper = NULL;
1508 m_clipInverse = false;
1509 m_scalex = m_scaley = 1;
1510 m_fAnimated2 = false;
1513 CSubtitle::~CSubtitle()
1515 Empty();
1518 void CSubtitle::Empty()
1520 POSITION pos = GetHeadPosition();
1521 while(pos) delete GetNext(pos);
1522 // pos = m_words.GetHeadPosition();
1523 // while(pos) delete m_words.GetNext(pos);
1524 for(int i = 0; i < EF_NUMBEROFEFFECTS; i++) {if(m_effects[i]) delete m_effects[i];}
1525 memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
1526 if(m_pClipper) delete m_pClipper;
1527 m_pClipper = NULL;
1530 int CSubtitle::GetFullWidth()
1532 int width = 0;
1533 POSITION pos = m_words.GetHeadPosition();
1534 while(pos) width += m_words.GetNext(pos)->m_width;
1535 return(width);
1538 int CSubtitle::GetFullLineWidth(POSITION pos)
1540 int width = 0;
1541 while(pos)
1543 SharedPtrCWord w = m_words.GetNext(pos);
1544 if(w->m_fLineBreak) break;
1545 width += w->m_width;
1547 return(width);
1550 int CSubtitle::GetWrapWidth(POSITION pos, int maxwidth)
1552 if(m_wrapStyle == 0 || m_wrapStyle == 3)
1554 if(maxwidth > 0)
1556 // int fullwidth = GetFullWidth();
1557 int fullwidth = GetFullLineWidth(pos);
1558 int minwidth = fullwidth / ((abs(fullwidth) / maxwidth) + 1);
1559 int width = 0, wordwidth = 0;
1560 while(pos && width < minwidth)
1562 SharedPtrCWord w = m_words.GetNext(pos);
1563 wordwidth = w->m_width;
1564 if(abs(width + wordwidth) < abs(maxwidth)) width += wordwidth;
1566 maxwidth = width;
1567 if(m_wrapStyle == 3 && pos) maxwidth -= wordwidth;
1570 else if(m_wrapStyle == 1)
1572 // maxwidth = maxwidth;
1574 else if(m_wrapStyle == 2)
1576 maxwidth = INT_MAX;
1578 return(maxwidth);
1581 CLine* CSubtitle::GetNextLine(POSITION& pos, int maxwidth)
1583 if(pos == NULL) return(NULL);
1584 CLine* ret = new CLine();
1585 if(!ret) return(NULL);
1586 ret->m_width = ret->m_ascent = ret->m_descent = ret->m_borderX = ret->m_borderY = 0;
1587 maxwidth = GetWrapWidth(pos, maxwidth);
1588 bool fEmptyLine = true;
1589 while(pos)
1591 SharedPtrCWord w = m_words.GetNext(pos);
1592 if(ret->m_ascent < w->m_ascent) ret->m_ascent = w->m_ascent;
1593 if(ret->m_descent < w->m_descent) ret->m_descent = w->m_descent;
1594 if(ret->m_borderX < w->m_style.get().outlineWidthX) ret->m_borderX = (int)(w->m_style.get().outlineWidthX+0.5);
1595 if(ret->m_borderY < w->m_style.get().outlineWidthY) ret->m_borderY = (int)(w->m_style.get().outlineWidthY+0.5);
1596 if(w->m_fLineBreak)
1598 if(fEmptyLine) {ret->m_ascent /= 2; ret->m_descent /= 2; ret->m_borderX = ret->m_borderY = 0;}
1599 ret->Compact();
1600 return(ret);
1602 fEmptyLine = false;
1603 bool fWSC = w->m_fWhiteSpaceChar;
1604 int width = w->m_width;
1605 POSITION pos2 = pos;
1606 while(pos2)
1608 if(m_words.GetAt(pos2)->m_fWhiteSpaceChar != fWSC
1609 || m_words.GetAt(pos2)->m_fLineBreak) break;
1610 SharedPtrCWord w2 = m_words.GetNext(pos2);
1611 width += w2->m_width;
1613 if((ret->m_width += width) <= maxwidth || ret->IsEmpty())
1615 ret->AddWord2Tail(w);
1616 while(pos != pos2)
1618 ret->AddWord2Tail(m_words.GetNext(pos));
1620 pos = pos2;
1622 else
1624 if(pos) m_words.GetPrev(pos);
1625 else pos = m_words.GetTailPosition();
1626 ret->m_width -= width;
1627 break;
1630 ret->Compact();
1631 return(ret);
1634 void CSubtitle::CreateClippers(CSize size)
1636 size.cx >>= 3;
1637 size.cy >>= 3;
1638 if(m_effects[EF_BANNER] && m_effects[EF_BANNER]->param[2])
1640 int w = size.cx, h = size.cy;
1641 if(!m_pClipper)
1643 CStringW str;
1644 str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
1645 m_pClipper = new CClipper(str, size, 1, 1, false);
1646 if(!m_pClipper) return;
1648 m_pClipper->SetEffect( *m_effects[EF_BANNER], EF_BANNER );
1650 else if(m_effects[EF_SCROLL] && m_effects[EF_SCROLL]->param[4])
1652 int height = m_effects[EF_SCROLL]->param[4];
1653 int w = size.cx, h = size.cy;
1654 if(!m_pClipper)
1656 CStringW str;
1657 str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
1658 m_pClipper = new CClipper(str, size, 1, 1, false);
1659 if(!m_pClipper) return;
1661 m_pClipper->SetEffect(*m_effects[EF_SCROLL], EF_SCROLL);
1665 void CSubtitle::MakeLines(CSize size, CRect marginRect)
1667 CSize spaceNeeded(0, 0);
1668 bool fFirstLine = true;
1669 m_topborder = m_bottomborder = 0;
1670 CLine* l = NULL;
1671 POSITION pos = m_words.GetHeadPosition();
1672 while(pos)
1674 l = GetNextLine(pos, size.cx - marginRect.left - marginRect.right);
1675 if(!l) break;
1676 if(fFirstLine) {m_topborder = l->m_borderY; fFirstLine = false;}
1677 spaceNeeded.cx = max(l->m_width+l->m_borderX, spaceNeeded.cx);
1678 spaceNeeded.cy += l->m_ascent + l->m_descent;
1679 AddTail(l);
1681 if(l) m_bottomborder = l->m_borderY;
1682 m_rect = CRect(
1683 CPoint((m_scrAlignment%3) == 1 ? marginRect.left
1684 : (m_scrAlignment%3) == 2 ? (marginRect.left + (size.cx - marginRect.right) - spaceNeeded.cx + 1) / 2
1685 : (size.cx - marginRect.right - spaceNeeded.cx),
1686 m_scrAlignment <= 3 ? (size.cy - marginRect.bottom - spaceNeeded.cy)
1687 : m_scrAlignment <= 6 ? (marginRect.top + (size.cy - marginRect.bottom) - spaceNeeded.cy + 1) / 2
1688 : marginRect.top),
1689 spaceNeeded);
1692 POSITION CSubtitle::GetHeadLinePosition()
1694 return __super::GetHeadPosition();
1697 CLine* CSubtitle::GetNextLine( POSITION& pos )
1699 return __super::GetNext(pos);
1702 // CScreenLayoutAllocator
1704 void CScreenLayoutAllocator::Empty()
1706 m_subrects.RemoveAll();
1709 void CScreenLayoutAllocator::AdvanceToSegment(int segment, const CAtlArray<int>& sa)
1711 POSITION pos = m_subrects.GetHeadPosition();
1712 while(pos)
1714 POSITION prev = pos;
1715 SubRect& sr = m_subrects.GetNext(pos);
1716 bool fFound = false;
1717 if(abs(sr.segment - segment) <= 1) // using abs() makes it possible to play the subs backwards, too :)
1719 for(size_t i = 0; i < sa.GetCount() && !fFound; i++)
1721 if(sa[i] == sr.entry)
1723 sr.segment = segment;
1724 fFound = true;
1728 if(!fFound) m_subrects.RemoveAt(prev);
1732 CRect CScreenLayoutAllocator::AllocRect(CSubtitle* s, int segment, int entry, int layer, int collisions)
1734 // TODO: handle collisions == 1 (reversed collisions)
1735 POSITION pos = m_subrects.GetHeadPosition();
1736 while(pos)
1738 SubRect& sr = m_subrects.GetNext(pos);
1739 if(sr.segment == segment && sr.entry == entry)
1741 return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
1744 CRect r = s->m_rect + CRect(0, s->m_topborder, 0, s->m_bottomborder);
1745 bool fSearchDown = s->m_scrAlignment > 3;
1746 bool fOK;
1749 fOK = true;
1750 pos = m_subrects.GetHeadPosition();
1751 while(pos)
1753 SubRect& sr = m_subrects.GetNext(pos);
1754 if(layer == sr.layer && !(r & sr.r).IsRectEmpty())
1756 if(fSearchDown)
1758 r.bottom = sr.r.bottom + r.Height();
1759 r.top = sr.r.bottom;
1761 else
1763 r.top = sr.r.top - r.Height();
1764 r.bottom = sr.r.top;
1766 fOK = false;
1770 while(!fOK);
1771 SubRect sr;
1772 sr.r = r;
1773 sr.segment = segment;
1774 sr.entry = entry;
1775 sr.layer = layer;
1776 m_subrects.AddTail(sr);
1777 return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
1780 // CRenderedTextSubtitle
1782 CAtlMap<CStringW, CRenderedTextSubtitle::AssCmdType, CStringElementTraits<CStringW>> CRenderedTextSubtitle::m_cmdMap;
1784 CRenderedTextSubtitle::CRenderedTextSubtitle(CCritSec* pLock)
1785 : CSubPicProviderImpl(pLock)
1787 if( m_cmdMap.IsEmpty() )
1789 InitCmdMap();
1791 m_size = CSize(0, 0);
1792 if(g_hDC_refcnt == 0)
1794 g_hDC = CreateCompatibleDC(NULL);
1795 SetBkMode(g_hDC, TRANSPARENT);
1796 SetTextColor(g_hDC, 0xffffff);
1797 SetMapMode(g_hDC, MM_TEXT);
1799 g_hDC_refcnt++;
1802 CRenderedTextSubtitle::~CRenderedTextSubtitle()
1804 Deinit();
1805 g_hDC_refcnt--;
1806 if(g_hDC_refcnt == 0) DeleteDC(g_hDC);
1809 void CRenderedTextSubtitle::InitCmdMap()
1811 if( m_cmdMap.IsEmpty() )
1813 m_cmdMap.SetAt(L"1c", CMD_1c);
1814 m_cmdMap.SetAt(L"2c", CMD_2c);
1815 m_cmdMap.SetAt(L"3c", CMD_3c);
1816 m_cmdMap.SetAt(L"4c", CMD_4c);
1817 m_cmdMap.SetAt(L"1a", CMD_1a);
1818 m_cmdMap.SetAt(L"2a", CMD_2a);
1819 m_cmdMap.SetAt(L"3a", CMD_3a);
1820 m_cmdMap.SetAt(L"4a", CMD_4a);
1821 m_cmdMap.SetAt(L"alpha", CMD_alpha);
1822 m_cmdMap.SetAt(L"an", CMD_an);
1823 m_cmdMap.SetAt(L"a", CMD_a);
1824 m_cmdMap.SetAt(L"blur", CMD_blur);
1825 m_cmdMap.SetAt(L"bord", CMD_bord);
1826 m_cmdMap.SetAt(L"be", CMD_be);
1827 m_cmdMap.SetAt(L"b", CMD_b);
1828 m_cmdMap.SetAt(L"clip", CMD_clip);
1829 m_cmdMap.SetAt(L"iclip", CMD_iclip);
1830 m_cmdMap.SetAt(L"c", CMD_c);
1831 m_cmdMap.SetAt(L"fade", CMD_fade);
1832 m_cmdMap.SetAt(L"fad", CMD_fad);
1833 m_cmdMap.SetAt(L"fax", CMD_fax);
1834 m_cmdMap.SetAt(L"fay", CMD_fay);
1835 m_cmdMap.SetAt(L"fe", CMD_fe);
1836 m_cmdMap.SetAt(L"fn", CMD_fn);
1837 m_cmdMap.SetAt(L"frx", CMD_frx);
1838 m_cmdMap.SetAt(L"fry", CMD_fry);
1839 m_cmdMap.SetAt(L"frz", CMD_frz);
1840 m_cmdMap.SetAt(L"fr", CMD_fr);
1841 m_cmdMap.SetAt(L"fscx", CMD_fscx);
1842 m_cmdMap.SetAt(L"fscy", CMD_fscy);
1843 m_cmdMap.SetAt(L"fsc", CMD_fsc);
1844 m_cmdMap.SetAt(L"fsp", CMD_fsp);
1845 m_cmdMap.SetAt(L"fs", CMD_fs);
1846 m_cmdMap.SetAt(L"i", CMD_i);
1847 m_cmdMap.SetAt(L"kt", CMD_kt);
1848 m_cmdMap.SetAt(L"kf", CMD_kf);
1849 m_cmdMap.SetAt(L"K", CMD_K);
1850 m_cmdMap.SetAt(L"ko", CMD_ko);
1851 m_cmdMap.SetAt(L"k", CMD_k);
1852 m_cmdMap.SetAt(L"move", CMD_move);
1853 m_cmdMap.SetAt(L"org", CMD_org);
1854 m_cmdMap.SetAt(L"pbo", CMD_pbo);
1855 m_cmdMap.SetAt(L"pos", CMD_pos);
1856 m_cmdMap.SetAt(L"p", CMD_p);
1857 m_cmdMap.SetAt(L"q", CMD_q);
1858 m_cmdMap.SetAt(L"r", CMD_r);
1859 m_cmdMap.SetAt(L"shad", CMD_shad);
1860 m_cmdMap.SetAt(L"s", CMD_s);
1861 m_cmdMap.SetAt(L"t", CMD_t);
1862 m_cmdMap.SetAt(L"u", CMD_u);
1863 m_cmdMap.SetAt(L"xbord", CMD_xbord);
1864 m_cmdMap.SetAt(L"xshad", CMD_xshad);
1865 m_cmdMap.SetAt(L"ybord", CMD_ybord);
1866 m_cmdMap.SetAt(L"yshad", CMD_yshad);
1870 void CRenderedTextSubtitle::Copy(CSimpleTextSubtitle& sts)
1872 __super::Copy(sts);
1873 m_size = CSize(0, 0);
1874 if(CRenderedTextSubtitle* pRTS = dynamic_cast<CRenderedTextSubtitle*>(&sts))
1876 m_size = pRTS->m_size;
1880 void CRenderedTextSubtitle::Empty()
1882 Deinit();
1883 __super::Empty();
1886 void CRenderedTextSubtitle::OnChanged()
1888 __super::OnChanged();
1889 POSITION pos = m_subtitleCache.GetStartPosition();
1890 while(pos)
1892 int i;
1893 CSubtitle* s;
1894 m_subtitleCache.GetNextAssoc(pos, i, s);
1895 delete s;
1897 m_subtitleCache.RemoveAll();
1898 m_sla.Empty();
1901 bool CRenderedTextSubtitle::Init(CSize size, CRect vidrect)
1903 Deinit();
1904 m_size = CSize(size.cx*8, size.cy*8);
1905 m_vidrect = CRect(vidrect.left*8, vidrect.top*8, vidrect.right*8, vidrect.bottom*8);
1906 m_sla.Empty();
1907 return(true);
1910 void CRenderedTextSubtitle::Deinit()
1912 POSITION pos = m_subtitleCache.GetStartPosition();
1913 while(pos)
1915 int i;
1916 CSubtitle* s;
1917 m_subtitleCache.GetNextAssoc(pos, i, s);
1918 delete s;
1920 m_subtitleCache.RemoveAll();
1921 m_sla.Empty();
1922 m_size = CSize(0, 0);
1923 m_vidrect.SetRectEmpty();
1925 CacheManager::GetCWordMruCache()->RemoveAll();
1926 CacheManager::GetPathDataMruCache()->RemoveAll();
1927 CacheManager::GetScanLineDataMruCache()->RemoveAll();
1928 CacheManager::GetOverlayNoBlurMruCache()->RemoveAll();
1929 CacheManager::GetOverlayMruCache()->RemoveAll();
1930 CacheManager::GetAssTagListMruCache()->RemoveAll();
1931 CacheManager::GetSubpixelVarianceCache()->RemoveAll();
1932 CacheManager::GetTextInfoCache()->RemoveAll();
1935 void CRenderedTextSubtitle::ParseEffect(CSubtitle* sub, const CStringW& str)
1937 CStringW::PCXSTR str_start = str.GetString();
1938 CStringW::PCXSTR str_end = str_start + str.GetLength();
1939 str_start = SkipWhiteSpaceLeft(str_start, str_end);
1941 if(!sub || *str_start==0)
1942 return;
1944 str_end = FastSkipWhiteSpaceRight(str_start, str_end);
1946 const WCHAR* s = FindChar(str_start, str_end, L';');
1947 if(*s==L';') {
1948 s++;
1951 const CStringW effect(str_start, s-str_start);
1952 if(!effect.CompareNoCase( L"Banner;" ) )
1954 int delay, lefttoright = 0, fadeawaywidth = 0;
1955 if(swscanf(s, L"%d;%d;%d", &delay, &lefttoright, &fadeawaywidth) < 1) return;
1956 Effect* e = new Effect;
1957 if(!e) return;
1958 sub->m_effects[e->type = EF_BANNER] = e;
1959 e->param[0] = (int)(max(1.0*delay/sub->m_scalex, 1));
1960 e->param[1] = lefttoright;
1961 e->param[2] = (int)(sub->m_scalex*fadeawaywidth);
1962 sub->m_wrapStyle = 2;
1964 else if(!effect.CompareNoCase(L"Scroll up;") || !effect.CompareNoCase(L"Scroll down;"))
1966 int top, bottom, delay, fadeawayheight = 0;
1967 if(swscanf(s, L"%d;%d;%d;%d", &top, &bottom, &delay, &fadeawayheight) < 3) return;
1968 if(top > bottom) {int tmp = top; top = bottom; bottom = tmp;}
1969 Effect* e = new Effect;
1970 if(!e) return;
1971 sub->m_effects[e->type = EF_SCROLL] = e;
1972 e->param[0] = (int)(sub->m_scaley*top*8);
1973 e->param[1] = (int)(sub->m_scaley*bottom*8);
1974 e->param[2] = (int)(max(1.0*delay/sub->m_scaley, 1));
1975 e->param[3] = (effect.GetLength() == 12);
1976 e->param[4] = (int)(sub->m_scaley*fadeawayheight);
1980 void CRenderedTextSubtitle::ParseString(CSubtitle* sub, CStringW str, const FwSTSStyle& style)
1982 if(!sub) return;
1983 str.Replace(L"\\N", L"\n");
1984 str.Replace(L"\\n", (sub->m_wrapStyle < 2 || sub->m_wrapStyle == 3) ? L" " : L"\n");
1985 str.Replace(L"\\h", L"\x00A0");
1986 for(int ite = 0, j = 0, len = str.GetLength(); j <= len; j++)
1988 WCHAR c = str[j];
1989 if(c != L'\n' && c != L' ' && c != 0)
1990 continue;
1991 if(ite < j)
1993 if(PCWord tmp_ptr = new CText(style, str.Mid(ite, j-ite), m_ktype, m_kstart, m_kend))
1995 SharedPtrCWord w(tmp_ptr);
1996 sub->m_words.AddTail(w);
1998 else
2000 ///TODO: overflow handling
2002 m_kstart = m_kend;
2004 if(c == L'\n')
2006 if(PCWord tmp_ptr = new CText(style, CStringW(), m_ktype, m_kstart, m_kend))
2008 SharedPtrCWord w(tmp_ptr);
2009 sub->m_words.AddTail(w);
2011 else
2013 ///TODO: overflow handling
2015 m_kstart = m_kend;
2017 else if(c == L' ')
2019 if(PCWord tmp_ptr = new CText(style, CStringW(c), m_ktype, m_kstart, m_kend))
2021 SharedPtrCWord w(tmp_ptr);
2022 sub->m_words.AddTail(w);
2024 else
2026 ///TODO: overflow handling
2028 m_kstart = m_kend;
2030 ite = j+1;
2032 return;
2035 void CRenderedTextSubtitle::ParsePolygon(CSubtitle* sub, const CStringW& str, const FwSTSStyle& style)
2037 if(!sub || !str.GetLength() || !m_nPolygon) return;
2039 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))
2041 SharedPtrCWord w(tmp_ptr);
2042 ///Todo: fix me
2043 //if( PCWord w_cache = m_wordCache.lookup(*w) )
2045 // sub->m_words.AddTail(w_cache);
2046 // delete w;
2048 //else
2050 sub->m_words.AddTail(w);
2052 m_kstart = m_kend;
2056 bool CRenderedTextSubtitle::ParseSSATag( AssTagList *assTags, const CStringW& str )
2058 if(!assTags) return(false);
2059 int nTags = 0, nUnrecognizedTags = 0;
2060 for(int i = 0, j; (j = str.Find(L'\\', i)) >= 0; i = j)
2062 POSITION pos = assTags->AddTail();
2063 AssTag& assTag = assTags->GetAt(pos);
2064 assTag.cmdType = CMD_COUNT;
2066 j++;
2067 CStringW::PCXSTR str_start = str.GetString() + j;
2068 CStringW::PCXSTR pc = str_start;
2069 while( iswspace(*pc) )
2071 pc++;
2073 j += pc-str_start;
2074 str_start = pc;
2075 while( *pc && *pc != L'(' && *pc != L'\\' )
2077 pc++;
2079 j += pc-str_start;
2080 if( pc-str_start>0 )
2082 while( iswspace(*--pc) );
2083 pc++;
2086 const CStringW cmd(str_start, pc-str_start);
2087 if(cmd.IsEmpty()) continue;
2089 CAtlArray<CStringW>& params = assTag.strParams;
2090 if(str[j] == L'(')
2092 j++;
2093 CStringW::PCXSTR str_start = str.GetString() + j;
2094 CStringW::PCXSTR pc = str_start;
2095 while( iswspace(*pc) )
2097 pc++;
2099 j += pc-str_start;
2100 str_start = pc;
2101 while( *pc && *pc != L')' )
2103 pc++;
2105 j += pc-str_start;
2106 if( pc-str_start>0 )
2108 while( iswspace(*--pc) );
2109 pc++;
2112 CStringW::PCXSTR param_start = str_start;
2113 CStringW::PCXSTR param_end = pc;
2114 while( param_start<param_end )
2116 param_start = SkipWhiteSpaceLeft(param_start, param_end);
2118 CStringW::PCXSTR newstart = FindChar(param_start, param_end, L',');
2119 CStringW::PCXSTR newend = FindChar(param_start, param_end, L'\\');
2120 if(newstart > param_start && newstart < newend)
2122 newstart = FastSkipWhiteSpaceRight(param_start, newstart);
2123 CStringW s(param_start, newstart - param_start);
2125 if(!s.IsEmpty()) params.Add(s);
2126 param_start = newstart + 1;
2128 else if(param_start<param_end)
2130 CStringW s(param_start, param_end - param_start);
2132 params.Add(s);
2133 param_start = param_end;
2138 AssCmdType cmd_type = CMD_COUNT;
2139 int cmd_length = min(MAX_CMD_LENGTH, cmd.GetLength());
2140 for( ;cmd_length>=MIN_CMD_LENGTH;cmd_length-- )
2142 if( m_cmdMap.Lookup(cmd.Left(cmd_length), cmd_type) )
2143 break;
2145 if(cmd_length<MIN_CMD_LENGTH)
2146 cmd_type = CMD_COUNT;
2147 switch( cmd_type )
2149 case CMD_fax:
2150 case CMD_fay:
2151 case CMD_fe:
2152 case CMD_fn:
2153 case CMD_frx:
2154 case CMD_fry:
2155 case CMD_frz:
2156 case CMD_fr:
2157 case CMD_fscx:
2158 case CMD_fscy:
2159 case CMD_fsc:
2160 case CMD_fsp:
2161 case CMD_fs:
2162 case CMD_i:
2163 case CMD_kt:
2164 case CMD_kf:
2165 case CMD_K:
2166 case CMD_ko:
2167 case CMD_k:
2168 case CMD_pbo:
2169 case CMD_p:
2170 case CMD_q:
2171 case CMD_r:
2172 case CMD_shad:
2173 case CMD_s:
2174 case CMD_an:
2175 case CMD_a:
2176 case CMD_blur:
2177 case CMD_bord:
2178 case CMD_be:
2179 case CMD_b:
2180 case CMD_u:
2181 case CMD_xbord:
2182 case CMD_xshad:
2183 case CMD_ybord:
2184 case CMD_yshad:
2185 // default:
2186 params.Add(cmd.Mid(cmd_length));
2187 break;
2188 case CMD_c:
2189 case CMD_1c :
2190 case CMD_2c :
2191 case CMD_3c :
2192 case CMD_4c :
2193 case CMD_1a :
2194 case CMD_2a :
2195 case CMD_3a :
2196 case CMD_4a :
2197 case CMD_alpha:
2198 params.Add(cmd.Mid(cmd_length).Trim(L"&H"));
2199 break;
2200 case CMD_clip:
2201 case CMD_iclip:
2202 case CMD_fade:
2203 case CMD_fad:
2204 case CMD_move:
2205 case CMD_org:
2206 case CMD_pos:
2207 break;
2208 case CMD_t:
2209 if (!params.IsEmpty())
2210 ParseSSATag(&assTag.embeded, params[params.GetCount()-1]);
2211 break;
2212 case CMD_COUNT:
2213 nUnrecognizedTags++;
2214 break;
2217 assTag.cmd = cmd;
2218 assTag.cmdType = cmd_type;
2220 nTags++;
2222 return(true);
2225 bool CRenderedTextSubtitle::ParseSSATag( CSubtitle* sub, const AssTagList& assTags, STSStyle& style, const STSStyle& org, bool fAnimate /*= false*/ )
2227 if(!sub) return(false);
2229 POSITION pos = assTags.GetHeadPosition();
2230 while(pos)
2232 const AssTag& assTag = assTags.GetNext(pos);
2233 const CStringW& cmd = assTag.cmd;
2234 AssCmdType cmd_type = assTag.cmdType;
2235 const CAtlArray<CStringW>& params = assTag.strParams;
2237 // TODO: call ParseStyleModifier(cmd, params, ..) and move the rest there
2238 const CStringW& p = params.GetCount() > 0 ? params[0] : CStringW("");
2239 switch ( cmd_type )
2241 case CMD_1c :
2242 case CMD_2c :
2243 case CMD_3c :
2244 case CMD_4c :
2246 int i = cmd[0] - L'1';
2247 DWORD c = wcstol(p, NULL, 16);
2248 style.colors[i] = !p.IsEmpty()
2249 ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
2250 |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
2251 |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
2252 : org.colors[i];
2253 break;
2255 case CMD_1a :
2256 case CMD_2a :
2257 case CMD_3a :
2258 case CMD_4a :
2260 int i = cmd[0] - L'1';
2261 style.alpha[i] = !p.IsEmpty()
2262 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2263 : org.alpha[i];
2264 break;
2266 case CMD_alpha:
2268 for(int i = 0; i < 4; i++)
2270 style.alpha[i] = !p.IsEmpty()
2271 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2272 : org.alpha[i];
2274 break;
2276 case CMD_an:
2278 int n = wcstol(p, NULL, 10);
2279 if(sub->m_scrAlignment < 0)
2280 sub->m_scrAlignment = (n > 0 && n < 10) ? n : org.scrAlignment;
2281 break;
2283 case CMD_a:
2285 int n = wcstol(p, NULL, 10);
2286 if(sub->m_scrAlignment < 0)
2287 sub->m_scrAlignment = (n > 0 && n < 12) ? ((((n-1)&3)+1)+((n&4)?6:0)+((n&8)?3:0)) : org.scrAlignment;
2288 break;
2290 case CMD_blur:
2292 double n = CalcAnimation(wcstod(p, NULL), style.fGaussianBlur, fAnimate);
2293 style.fGaussianBlur = !p.IsEmpty()
2294 ? (n < 0 ? 0 : n)
2295 : org.fGaussianBlur;
2296 break;
2298 case CMD_bord:
2300 double dst = wcstod(p, NULL);
2301 double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate);
2302 style.outlineWidthX = !p.IsEmpty()
2303 ? (nx < 0 ? 0 : nx)
2304 : org.outlineWidthX;
2305 double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate);
2306 style.outlineWidthY = !p.IsEmpty()
2307 ? (ny < 0 ? 0 : ny)
2308 : org.outlineWidthY;
2309 break;
2311 case CMD_be:
2313 int n = (int)(CalcAnimation(wcstol(p, NULL, 10), style.fBlur, fAnimate)+0.5);
2314 style.fBlur = !p.IsEmpty()
2316 : org.fBlur;
2317 break;
2319 case CMD_b:
2321 int n = wcstol(p, NULL, 10);
2322 style.fontWeight = !p.IsEmpty()
2323 ? (n == 0 ? FW_NORMAL : n == 1 ? FW_BOLD : n >= 100 ? n : org.fontWeight)
2324 : org.fontWeight;
2325 break;
2327 case CMD_clip:
2328 case CMD_iclip:
2330 bool invert = (cmd_type == CMD_iclip);
2331 if(params.GetCount() == 1 && !sub->m_pClipper)
2333 sub->m_pClipper = new CClipper(params[0], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex, sub->m_scaley, invert);
2335 else if(params.GetCount() == 2 && !sub->m_pClipper)
2337 int scale = max(wcstol(p, NULL, 10), 1);
2338 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);
2340 else if(params.GetCount() == 4)
2342 CRect r;
2343 sub->m_clipInverse = invert;
2344 r.SetRect(
2345 wcstol(params[0], NULL, 10),
2346 wcstol(params[1], NULL, 10),
2347 wcstol(params[2], NULL, 10),
2348 wcstol(params[3], NULL, 10));
2349 CPoint o(0, 0);
2350 if(sub->m_relativeTo == 1) // TODO: this should also apply to the other two clippings above
2352 o.x = m_vidrect.left>>3;
2353 o.y = m_vidrect.top>>3;
2355 sub->m_clip.SetRect(
2356 (int)CalcAnimation(sub->m_scalex*r.left + o.x, sub->m_clip.left, fAnimate),
2357 (int)CalcAnimation(sub->m_scaley*r.top + o.y, sub->m_clip.top, fAnimate),
2358 (int)CalcAnimation(sub->m_scalex*r.right + o.x, sub->m_clip.right, fAnimate),
2359 (int)CalcAnimation(sub->m_scaley*r.bottom + o.y, sub->m_clip.bottom, fAnimate));
2361 break;
2363 case CMD_c:
2365 DWORD c = wcstol(p, NULL, 16);
2366 style.colors[0] = !p.IsEmpty()
2367 ? (((int)CalcAnimation(c&0xff, style.colors[0]&0xff, fAnimate))&0xff
2368 |((int)CalcAnimation(c&0xff00, style.colors[0]&0xff00, fAnimate))&0xff00
2369 |((int)CalcAnimation(c&0xff0000, style.colors[0]&0xff0000, fAnimate))&0xff0000)
2370 : org.colors[0];
2371 break;
2373 case CMD_fade:
2374 case CMD_fad:
2376 sub->m_fAnimated2 = true;
2377 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])
2379 if(Effect* e = new Effect)
2381 for(int i = 0; i < 3; i++)
2382 e->param[i] = wcstol(params[i], NULL, 10);
2383 for(int i = 0; i < 4; i++)
2384 e->t[i] = wcstol(params[3+i], NULL, 10);
2385 sub->m_effects[EF_FADE] = e;
2388 else if(params.GetCount() == 2 && !sub->m_effects[EF_FADE]) // {\fad(t1=t[1], t2=t[2])
2390 if(Effect* e = new Effect)
2392 e->param[0] = e->param[2] = 0xff;
2393 e->param[1] = 0x00;
2394 for(int i = 1; i < 3; i++)
2395 e->t[i] = wcstol(params[i-1], NULL, 10);
2396 e->t[0] = e->t[3] = -1; // will be substituted with "start" and "end"
2397 sub->m_effects[EF_FADE] = e;
2400 break;
2402 case CMD_fax:
2404 style.fontShiftX = !p.IsEmpty()
2405 ? CalcAnimation(wcstod(p, NULL), style.fontShiftX, fAnimate)
2406 : org.fontShiftX;
2407 break;
2409 case CMD_fay:
2411 style.fontShiftY = !p.IsEmpty()
2412 ? CalcAnimation(wcstod(p, NULL), style.fontShiftY, fAnimate)
2413 : org.fontShiftY;
2414 break;
2416 case CMD_fe:
2418 int n = wcstol(p, NULL, 10);
2419 style.charSet = !p.IsEmpty()
2421 : org.charSet;
2422 break;
2424 case CMD_fn:
2426 if(!p.IsEmpty() && p != L'0')
2427 style.fontName = CString(p).Trim();
2428 else
2429 style.fontName = org.fontName;
2430 break;
2432 case CMD_frx:
2434 style.fontAngleX = !p.IsEmpty()
2435 ? CalcAnimation(wcstod(p, NULL), style.fontAngleX, fAnimate)
2436 : org.fontAngleX;
2437 break;
2439 case CMD_fry:
2441 style.fontAngleY = !p.IsEmpty()
2442 ? CalcAnimation(wcstod(p, NULL), style.fontAngleY, fAnimate)
2443 : org.fontAngleY;
2444 break;
2446 case CMD_frz:
2447 case CMD_fr:
2449 style.fontAngleZ = !p.IsEmpty()
2450 ? CalcAnimation(wcstod(p, NULL), style.fontAngleZ, fAnimate)
2451 : org.fontAngleZ;
2452 break;
2454 case CMD_fscx:
2456 double n = CalcAnimation(wcstol(p, NULL, 10), style.fontScaleX, fAnimate);
2457 style.fontScaleX = !p.IsEmpty()
2458 ? ((n < 0) ? 0 : n)
2459 : org.fontScaleX;
2460 break;
2462 case CMD_fscy:
2464 double n = CalcAnimation(wcstol(p, NULL, 10), style.fontScaleY, fAnimate);
2465 style.fontScaleY = !p.IsEmpty()
2466 ? ((n < 0) ? 0 : n)
2467 : org.fontScaleY;
2468 break;
2470 case CMD_fsc:
2472 style.fontScaleX = org.fontScaleX;
2473 style.fontScaleY = org.fontScaleY;
2474 break;
2476 case CMD_fsp:
2478 style.fontSpacing = !p.IsEmpty()
2479 ? CalcAnimation(wcstod(p, NULL), style.fontSpacing, fAnimate)
2480 : org.fontSpacing;
2481 break;
2483 case CMD_fs:
2485 if(!p.IsEmpty())
2487 if(p[0] == L'-' || p[0] == L'+')
2489 double n = CalcAnimation(style.fontSize + style.fontSize*wcstol(p, NULL, 10)/10, style.fontSize, fAnimate);
2490 style.fontSize = (n > 0) ? n : org.fontSize;
2492 else
2494 double n = CalcAnimation(wcstol(p, NULL, 10), style.fontSize, fAnimate);
2495 style.fontSize = (n > 0) ? n : org.fontSize;
2498 else
2500 style.fontSize = org.fontSize;
2502 break;
2504 case CMD_i:
2506 int n = wcstol(p, NULL, 10);
2507 style.fItalic = !p.IsEmpty()
2508 ? (n == 0 ? false : n == 1 ? true : org.fItalic)
2509 : org.fItalic;
2510 break;
2512 case CMD_kt:
2514 m_kstart = !p.IsEmpty()
2515 ? wcstol(p, NULL, 10)*10
2516 : 0;
2517 m_kend = m_kstart;
2518 break;
2519 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2521 case CMD_kf:
2522 case CMD_K:
2524 m_ktype = 1;
2525 m_kstart = m_kend;
2526 m_kend += !p.IsEmpty()
2527 ? wcstol(p, NULL, 10)*10
2528 : 1000;
2529 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2530 break;
2532 case CMD_ko:
2534 m_ktype = 2;
2535 m_kstart = m_kend;
2536 m_kend += !p.IsEmpty()
2537 ? wcstol(p, NULL, 10)*10
2538 : 1000;
2539 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2540 break;
2542 case CMD_k:
2544 m_ktype = 0;
2545 m_kstart = m_kend;
2546 m_kend += !p.IsEmpty()
2547 ? wcstol(p, NULL, 10)*10
2548 : 1000;
2549 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2550 break;
2552 case CMD_move: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3][, t1=t[0], t2=t[1]])}
2554 if((params.GetCount() == 4 || params.GetCount() == 6) && !sub->m_effects[EF_MOVE])
2556 if(Effect* e = new Effect)
2558 e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2559 e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2560 e->param[2] = (int)(sub->m_scalex*wcstod(params[2], NULL)*8);
2561 e->param[3] = (int)(sub->m_scaley*wcstod(params[3], NULL)*8);
2562 e->t[0] = e->t[1] = -1;
2563 if(params.GetCount() == 6)
2565 for(int i = 0; i < 2; i++)
2566 e->t[i] = wcstol(params[4+i], NULL, 10);
2568 sub->m_effects[EF_MOVE] = e;
2569 sub->m_fAnimated2 = true;
2572 break;
2574 case CMD_org: // {\org(x=param[0], y=param[1])}
2576 if(params.GetCount() == 2 && !sub->m_effects[EF_ORG])
2578 if(Effect* e = new Effect)
2580 e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2581 e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2582 sub->m_effects[EF_ORG] = e;
2585 break;
2587 case CMD_pbo:
2589 m_polygonBaselineOffset = wcstol(p, NULL, 10);
2590 break;
2592 case CMD_pos:
2594 if(params.GetCount() == 2 && !sub->m_effects[EF_MOVE])
2596 if(Effect* e = new Effect)
2598 e->param[0] = e->param[2] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2599 e->param[1] = e->param[3] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2600 e->t[0] = e->t[1] = 0;
2601 sub->m_effects[EF_MOVE] = e;
2604 break;
2606 case CMD_p:
2608 int n = wcstol(p, NULL, 10);
2609 m_nPolygon = (n <= 0 ? 0 : n);
2610 break;
2612 case CMD_q:
2614 int n = wcstol(p, NULL, 10);
2615 sub->m_wrapStyle = !p.IsEmpty() && (0 <= n && n <= 3)
2617 : m_defaultWrapStyle;
2618 break;
2620 case CMD_r:
2622 STSStyle* val;
2623 style = (!p.IsEmpty() && m_styles.Lookup(WToT(p), val) && val) ? *val : org;
2624 break;
2626 case CMD_shad:
2628 double dst = wcstod(p, NULL);
2629 double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate);
2630 style.shadowDepthX = !p.IsEmpty()
2631 ? (nx < 0 ? 0 : nx)
2632 : org.shadowDepthX;
2633 double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate);
2634 style.shadowDepthY = !p.IsEmpty()
2635 ? (ny < 0 ? 0 : ny)
2636 : org.shadowDepthY;
2637 break;
2639 case CMD_s:
2641 int n = wcstol(p, NULL, 10);
2642 style.fStrikeOut = !p.IsEmpty()
2643 ? (n == 0 ? false : n == 1 ? true : org.fStrikeOut)
2644 : org.fStrikeOut;
2645 break;
2647 case CMD_t: // \t([<t1>,<t2>,][<accel>,]<style modifiers>)
2649 CStringW param;
2650 m_animStart = m_animEnd = 0;
2651 m_animAccel = 1;
2652 if(params.GetCount() == 1)
2654 param = params[0];
2656 else if(params.GetCount() == 2)
2658 m_animAccel = wcstod(params[0], NULL);
2659 param = params[1];
2661 else if(params.GetCount() == 3)
2663 m_animStart = (int)wcstod(params[0], NULL);
2664 m_animEnd = (int)wcstod(params[1], NULL);
2665 param = params[2];
2667 else if(params.GetCount() == 4)
2669 m_animStart = wcstol(params[0], NULL, 10);
2670 m_animEnd = wcstol(params[1], NULL, 10);
2671 m_animAccel = wcstod(params[2], NULL);
2672 param = params[3];
2674 ParseSSATag(sub, assTag.embeded, style, org, true);
2675 sub->m_fAnimated = true;
2676 sub->m_fAnimated2 = true;
2677 break;
2679 case CMD_u:
2681 int n = wcstol(p, NULL, 10);
2682 style.fUnderline = !p.IsEmpty()
2683 ? (n == 0 ? false : n == 1 ? true : org.fUnderline)
2684 : org.fUnderline;
2685 break;
2687 case CMD_xbord:
2689 double dst = wcstod(p, NULL);
2690 double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate);
2691 style.outlineWidthX = !p.IsEmpty()
2692 ? (nx < 0 ? 0 : nx)
2693 : org.outlineWidthX;
2694 break;
2696 case CMD_xshad:
2698 double dst = wcstod(p, NULL);
2699 double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate);
2700 style.shadowDepthX = !p.IsEmpty()
2701 ? nx
2702 : org.shadowDepthX;
2703 break;
2705 case CMD_ybord:
2707 double dst = wcstod(p, NULL);
2708 double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate);
2709 style.outlineWidthY = !p.IsEmpty()
2710 ? (ny < 0 ? 0 : ny)
2711 : org.outlineWidthY;
2712 break;
2714 case CMD_yshad:
2716 double dst = wcstod(p, NULL);
2717 double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate);
2718 style.shadowDepthY = !p.IsEmpty()
2719 ? ny
2720 : org.shadowDepthY;
2721 break;
2723 default:
2724 break;
2727 return(true);
2730 bool CRenderedTextSubtitle::ParseSSATag(CSubtitle* sub, const CStringW& str, STSStyle& style, const STSStyle& org, bool fAnimate)
2732 if(!sub) return(false);
2734 SharedPtrConstAssTagList assTags;
2735 AssTagListMruCache *ass_tag_cache = CacheManager::GetAssTagListMruCache();
2736 POSITION pos = ass_tag_cache->Lookup(str);
2737 if (pos==NULL)
2739 AssTagList *tmp = new AssTagList();
2740 ParseSSATag(tmp, str);
2741 assTags.reset(tmp);
2742 ass_tag_cache->UpdateCache(str, assTags);
2744 else
2746 assTags = ass_tag_cache->GetAt(pos);
2747 ass_tag_cache->UpdateCache( pos );
2749 return ParseSSATag(sub, *assTags, style, org, fAnimate);
2752 bool CRenderedTextSubtitle::ParseHtmlTag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org)
2754 if(str.Find(L"!--") == 0)
2755 return(true);
2756 bool fClosing = str[0] == L'/';
2757 str.Trim(L" /");
2758 int i = str.Find(L' ');
2759 if(i < 0) i = str.GetLength();
2760 CStringW tag = str.Left(i).MakeLower();
2761 str = str.Mid(i).Trim();
2762 CAtlArray<CStringW> attribs, params;
2763 while((i = str.Find(L'=')) > 0)
2765 attribs.Add(str.Left(i).Trim().MakeLower());
2766 str = str.Mid(i+1);
2767 for(i = 0; _istspace(str[i]); i++);
2768 str = str.Mid(i);
2769 if(str[0] == L'\"') {str = str.Mid(1); i = str.Find(L'\"');}
2770 else i = str.Find(L' ');
2771 if(i < 0) i = str.GetLength();
2772 params.Add(str.Left(i).Trim().MakeLower());
2773 str = str.Mid(i+1);
2775 if(tag == L"text")
2777 else if(tag == L"b" || tag == L"strong")
2778 style.fontWeight = !fClosing ? FW_BOLD : org.fontWeight;
2779 else if(tag == L"i" || tag == L"em")
2780 style.fItalic = !fClosing ? true : org.fItalic;
2781 else if(tag == L"u")
2782 style.fUnderline = !fClosing ? true : org.fUnderline;
2783 else if(tag == L"s" || tag == L"strike" || tag == L"del")
2784 style.fStrikeOut = !fClosing ? true : org.fStrikeOut;
2785 else if(tag == L"font")
2787 if(!fClosing)
2789 for(size_t i = 0; i < attribs.GetCount(); i++)
2791 if(params[i].IsEmpty()) continue;
2792 int nColor = -1;
2793 if(attribs[i] == L"face")
2795 style.fontName = params[i];
2797 else if(attribs[i] == L"size")
2799 if(params[i][0] == L'+')
2800 style.fontSize += wcstol(params[i], NULL, 10);
2801 else if(params[i][0] == L'-')
2802 style.fontSize -= wcstol(params[i], NULL, 10);
2803 else
2804 style.fontSize = wcstol(params[i], NULL, 10);
2806 else if(attribs[i] == L"color")
2808 nColor = 0;
2810 else if(attribs[i] == L"outline-color")
2812 nColor = 2;
2814 else if(attribs[i] == L"outline-level")
2816 style.outlineWidthX = style.outlineWidthY = wcstol(params[i], NULL, 10);
2818 else if(attribs[i] == L"shadow-color")
2820 nColor = 3;
2822 else if(attribs[i] == L"shadow-level")
2824 style.shadowDepthX = style.shadowDepthY = wcstol(params[i], NULL, 10);
2826 if(nColor >= 0 && nColor < 4)
2828 CString key = WToT(params[i]).TrimLeft(L'#');
2829 DWORD val;
2830 if(g_colors.Lookup(key, val))
2831 style.colors[nColor] = val;
2832 else if((style.colors[nColor] = _tcstol(key, NULL, 16)) == 0)
2833 style.colors[nColor] = 0x00ffffff; // default is white
2834 style.colors[nColor] = ((style.colors[nColor]>>16)&0xff)|((style.colors[nColor]&0xff)<<16)|(style.colors[nColor]&0x00ff00);
2838 else
2840 style.fontName = org.fontName;
2841 style.fontSize = org.fontSize;
2842 memcpy(style.colors, org.colors, sizeof(style.colors));
2845 else if(tag == L"k" && attribs.GetCount() == 1 && attribs[0] == L"t")
2847 m_ktype = 1;
2848 m_kstart = m_kend;
2849 m_kend += wcstol(params[0], NULL, 10);
2851 else
2852 return(false);
2853 return(true);
2856 double CRenderedTextSubtitle::CalcAnimation(double dst, double src, bool fAnimate)
2858 int s = m_animStart ? m_animStart : 0;
2859 int e = m_animEnd ? m_animEnd : m_delay;
2860 if(fabs(dst-src) >= 0.0001 && fAnimate)
2862 if(m_time < s) dst = src;
2863 else if(s <= m_time && m_time < e)
2865 double t = pow(1.0 * (m_time - s) / (e - s), m_animAccel);
2866 dst = (1 - t) * src + t * dst;
2868 // else dst = dst;
2870 return(dst);
2873 CSubtitle* CRenderedTextSubtitle::GetSubtitle(int entry)
2875 CSubtitle* sub;
2876 if(m_subtitleCache.Lookup(entry, sub))
2878 if(sub->m_fAnimated) {delete sub; sub = NULL;}
2879 else return(sub);
2881 sub = new CSubtitle();
2882 if(!sub) return(NULL);
2883 CStringW str = GetStrW(entry, true);
2884 STSStyle stss, orgstss;
2885 GetStyle(entry, &stss);
2886 if (stss.fontScaleX == stss.fontScaleY && m_dPARCompensation != 1.0)
2888 switch(m_ePARCompensationType)
2890 case EPCTUpscale:
2891 if (m_dPARCompensation < 1.0)
2892 stss.fontScaleY /= m_dPARCompensation;
2893 else
2894 stss.fontScaleX *= m_dPARCompensation;
2895 break;
2896 case EPCTDownscale:
2897 if (m_dPARCompensation < 1.0)
2898 stss.fontScaleX *= m_dPARCompensation;
2899 else
2900 stss.fontScaleY /= m_dPARCompensation;
2901 break;
2902 case EPCTAccurateSize:
2903 stss.fontScaleX *= m_dPARCompensation;
2904 break;
2907 orgstss = stss;
2908 sub->m_clip.SetRect(0, 0, m_size.cx>>3, m_size.cy>>3);
2909 sub->m_scrAlignment = -stss.scrAlignment;
2910 sub->m_wrapStyle = m_defaultWrapStyle;
2911 sub->m_fAnimated = false;
2912 sub->m_relativeTo = stss.relativeTo;
2913 sub->m_scalex = m_dstScreenSize.cx > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Width() : m_size.cx) / (m_dstScreenSize.cx*8) : 1.0;
2914 sub->m_scaley = m_dstScreenSize.cy > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Height() : m_size.cy) / (m_dstScreenSize.cy*8) : 1.0;
2915 m_animStart = m_animEnd = 0;
2916 m_animAccel = 1;
2917 m_ktype = m_kstart = m_kend = 0;
2918 m_nPolygon = 0;
2919 m_polygonBaselineOffset = 0;
2920 ParseEffect(sub, m_entries.GetAt(entry).effect);
2921 while(!str.IsEmpty())
2923 bool fParsed = false;
2924 int i;
2925 if(str[0] == L'{' && (i = str.Find(L'}')) > 0)
2927 if(fParsed = ParseSSATag(sub, str.Mid(1, i-1), stss, orgstss))
2928 str = str.Mid(i+1);
2930 else if(str[0] == L'<' && (i = str.Find(L'>')) > 0)
2932 if(fParsed = ParseHtmlTag(sub, str.Mid(1, i-1), stss, orgstss))
2933 str = str.Mid(i+1);
2935 if(fParsed)
2937 i = str.FindOneOf(L"{<");
2938 if(i < 0) i = str.GetLength();
2939 if(i == 0) continue;
2941 else
2943 i = str.Mid(1).FindOneOf(L"{<");
2944 if(i < 0) i = str.GetLength()-1;
2945 i++;
2947 STSStyle tmp = stss;
2948 tmp.fontSize = sub->m_scaley*tmp.fontSize*64;
2949 tmp.fontSpacing = sub->m_scalex*tmp.fontSpacing*64;
2950 tmp.outlineWidthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8;
2951 tmp.outlineWidthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8;
2952 tmp.shadowDepthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8;
2953 tmp.shadowDepthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8;
2954 FwSTSStyle fw_tmp(tmp);
2955 if(m_nPolygon)
2957 ParsePolygon(sub, str.Left(i), fw_tmp);
2959 else
2961 ParseString(sub, str.Left(i), fw_tmp);
2963 str = str.Mid(i);
2965 if( sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL] )
2966 sub->m_fAnimated2 = true;
2967 // just a "work-around" solution... in most cases nobody will want to use \org together with moving but without rotating the subs
2968 if(sub->m_effects[EF_ORG] && (sub->m_effects[EF_MOVE] || sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL]))
2969 sub->m_fAnimated = true;
2970 sub->m_scrAlignment = abs(sub->m_scrAlignment);
2971 STSEntry stse = m_entries.GetAt(entry);
2972 CRect marginRect = stse.marginRect;
2973 if(marginRect.left == 0) marginRect.left = orgstss.marginRect.get().left;
2974 if(marginRect.top == 0) marginRect.top = orgstss.marginRect.get().top;
2975 if(marginRect.right == 0) marginRect.right = orgstss.marginRect.get().right;
2976 if(marginRect.bottom == 0) marginRect.bottom = orgstss.marginRect.get().bottom;
2977 marginRect.left = (int)(sub->m_scalex*marginRect.left*8);
2978 marginRect.top = (int)(sub->m_scaley*marginRect.top*8);
2979 marginRect.right = (int)(sub->m_scalex*marginRect.right*8);
2980 marginRect.bottom = (int)(sub->m_scaley*marginRect.bottom*8);
2981 if(stss.relativeTo == 1)
2983 marginRect.left += m_vidrect.left;
2984 marginRect.top += m_vidrect.top;
2985 marginRect.right += m_size.cx - m_vidrect.right;
2986 marginRect.bottom += m_size.cy - m_vidrect.bottom;
2988 sub->CreateClippers(m_size);
2989 sub->MakeLines(m_size, marginRect);
2990 m_subtitleCache[entry] = sub;
2991 return(sub);
2996 STDMETHODIMP CRenderedTextSubtitle::NonDelegatingQueryInterface(REFIID riid, void** ppv)
2998 CheckPointer(ppv, E_POINTER);
2999 *ppv = NULL;
3000 return
3001 QI(IPersist)
3002 QI(ISubStream)
3003 QI(ISubPicProvider)
3004 QI(ISubPicProviderEx)
3005 __super::NonDelegatingQueryInterface(riid, ppv);
3008 // ISubPicProvider
3010 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetStartPosition(REFERENCE_TIME rt, double fps)
3012 m_fps = fps;
3013 if (m_fps>0)
3015 m_period = 1000/m_fps;
3016 if(m_period<=0)
3018 m_period = 1;
3021 else
3023 //Todo: fix me. max has been defined as a macro. Use #define NOMINMAX to fix it.
3024 //std::numeric_limits<int>::max();
3025 m_period = INT_MAX;
3028 int iSegment;
3029 int subIndex = 1;//If a segment has animate effect then it corresponds to several subpics.
3030 //subIndex, 1 based, indicates which subpic the result corresponds to.
3031 rt /= 10000i64;
3032 const STSSegment *stss = SearchSubs((int)rt, fps, &iSegment, NULL);
3033 if(stss==NULL)
3034 return NULL;
3035 else if(stss->animated)
3037 int start = TranslateSegmentStart(iSegment, fps);
3038 if(rt > start)
3039 subIndex = (rt-start)/m_period + 1;
3041 //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));
3042 return (POSITION)(subIndex | (iSegment<<RTS_POS_SEGMENT_INDEX_BITS));
3043 //if(iSegment < 0) iSegment = 0;
3044 //return(GetNext((POSITION)iSegment));
3047 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetNext(POSITION pos)
3049 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3050 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3051 const STSSegment *stss = GetSegment(iSegment);
3052 ASSERT(stss!=NULL && stss->subs.GetCount()>0);
3053 //DbgLog((LOG_TRACE, 3, "stss:%x count:%d", stss, stss->subs.GetCount()));
3054 if(!stss->animated)
3056 iSegment++;
3057 subIndex = 1;
3059 else
3061 int start, end;
3062 TranslateSegmentStartEnd(iSegment, m_fps, start, end);
3063 if(start+m_period*subIndex < end)
3064 subIndex++;
3065 else
3067 iSegment++;
3068 subIndex = 1;
3071 if(GetSegment(iSegment) != NULL)
3073 ASSERT(GetSegment(iSegment)->subs.GetCount()>0);
3074 return (POSITION)(subIndex | (iSegment<<RTS_POS_SEGMENT_INDEX_BITS));
3076 else
3077 return NULL;
3080 //@return: <0 if segment not found
3081 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStart(POSITION pos, double fps)
3083 //return(10000i64 * TranslateSegmentStart((int)pos-1, fps));
3084 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3085 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3086 int start = TranslateSegmentStart(iSegment, fps);
3087 const STSSegment *stss = GetSegment(iSegment);
3088 if(stss!=NULL)
3090 return (start + (subIndex-1)*m_period)*10000i64;
3092 else
3094 return -1;
3098 //@return: <0 if segment not found
3099 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStop(POSITION pos, double fps)
3101 // return(10000i64 * TranslateSegmentEnd((int)pos-1, fps));
3102 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3103 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3104 int start, end, ret;
3105 TranslateSegmentStartEnd(iSegment, fps, start, end);
3106 const STSSegment *stss = GetSegment(iSegment);
3107 if(stss!=NULL)
3109 if(!stss->animated)
3110 ret = end;
3111 else
3113 ret = start+subIndex*m_period;
3114 if(ret > end)
3115 ret = end;
3117 return ret*10000i64;
3119 else
3120 return -1;
3123 //@start, @stop: -1 if segment not found; @stop may < @start if subIndex exceed uppper bound
3124 STDMETHODIMP_(VOID) CRenderedTextSubtitle::GetStartStop(POSITION pos, double fps, /*out*/REFERENCE_TIME &start, /*out*/REFERENCE_TIME &stop)
3126 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3127 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3128 int tempStart, tempEnd;
3129 TranslateSegmentStartEnd(iSegment, fps, tempStart, tempEnd);
3130 start = tempStart;
3131 stop = tempEnd;
3132 const STSSegment *stss = GetSegment(iSegment);
3133 if(stss!=NULL)
3135 if(stss->animated)
3137 start += (subIndex-1)*m_period;
3138 if(start+m_period < stop)
3139 stop = start+m_period;
3141 //DbgLog((LOG_TRACE, 3, "animated:%d seg:%d idx:%d start:%d stop:%lu", stss->animated, iSegment, subIndex, (ULONG)start, (ULONG)stop));
3142 start *= 10000i64;
3143 stop *= 10000i64;
3145 else
3147 start = -1;
3148 stop = -1;
3152 STDMETHODIMP_(bool) CRenderedTextSubtitle::IsAnimated(POSITION pos)
3154 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3155 if(iSegment>=0 && iSegment<m_segments.GetCount())
3156 return m_segments[iSegment].animated;
3157 else
3158 return false;
3159 //return(true);
3162 struct LSub {int idx, layer, readorder;};
3164 static int lscomp(const void* ls1, const void* ls2)
3166 int ret = ((LSub*)ls1)->layer - ((LSub*)ls2)->layer;
3167 if(!ret) ret = ((LSub*)ls1)->readorder - ((LSub*)ls2)->readorder;
3168 return(ret);
3171 STDMETHODIMP CRenderedTextSubtitle::ParseScript(SubPicDesc& spd, REFERENCE_TIME rt, double fps, CSubtitle2List *outputSub2List )
3173 //fix me: check input and log error
3174 int t = (int)(rt / 10000);
3175 int segment;
3176 //const
3177 STSSegment* stss = SearchSubs2(t, fps, &segment);
3178 if(!stss) return S_FALSE;
3179 // clear any cached subs not in the range of +/-30secs measured from the segment's bounds
3181 POSITION pos = m_subtitleCache.GetStartPosition();
3182 while(pos)
3184 int key;
3185 CSubtitle* value;
3186 m_subtitleCache.GetNextAssoc(pos, key, value);
3187 STSEntry& stse = m_entries.GetAt(key);
3188 if(stse.end <= (t-30000) || stse.start > (t+30000))
3190 delete value;
3191 m_subtitleCache.RemoveKey(key);
3192 pos = m_subtitleCache.GetStartPosition();
3196 m_sla.AdvanceToSegment(segment, stss->subs);
3197 CAtlArray<LSub> subs;
3198 for(int i = 0, j = stss->subs.GetCount(); i < j; i++)
3200 LSub ls;
3201 ls.idx = stss->subs[i];
3202 ls.layer = m_entries.GetAt(stss->subs[i]).layer;
3203 ls.readorder = m_entries.GetAt(stss->subs[i]).readorder;
3204 subs.Add(ls);
3206 qsort(subs.GetData(), subs.GetCount(), sizeof(LSub), lscomp);
3208 for(int i = 0, j = subs.GetCount(); i < j; i++)
3210 int entry = subs[i].idx;
3211 STSEntry stse = m_entries.GetAt(entry);
3213 int start = TranslateStart(entry, fps);
3214 m_time = t - start;
3215 m_delay = TranslateEnd(entry, fps) - start;
3217 CSubtitle* s = GetSubtitle(entry);
3218 if(!s) continue;
3219 stss->animated |= s->m_fAnimated2;
3220 CRect clipRect = s->m_clip;
3221 CRect r = s->m_rect;
3222 CSize spaceNeeded = r.Size();
3223 // apply the effects
3224 bool fPosOverride = false, fOrgOverride = false;
3225 int alpha = 0x00;
3226 CPoint org2;
3227 for(int k = 0; k < EF_NUMBEROFEFFECTS; k++)
3229 if(!s->m_effects[k]) continue;
3230 switch(k)
3232 case EF_MOVE: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3], t1=t[0], t2=t[1])}
3234 CPoint p;
3235 CPoint p1(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
3236 CPoint p2(s->m_effects[k]->param[2], s->m_effects[k]->param[3]);
3237 int t1 = s->m_effects[k]->t[0];
3238 int t2 = s->m_effects[k]->t[1];
3239 if(t2 < t1) {int t = t1; t1 = t2; t2 = t;}
3240 if(t1 <= 0 && t2 <= 0) {t1 = 0; t2 = m_delay;}
3241 if(m_time <= t1) p = p1;
3242 else if (p1 == p2) p = p1;
3243 else if(t1 < m_time && m_time < t2)
3245 double t = 1.0*(m_time-t1)/(t2-t1);
3246 p.x = (int)((1-t)*p1.x + t*p2.x);
3247 p.y = (int)((1-t)*p1.y + t*p2.y);
3249 else p = p2;
3250 r = CRect(
3251 CPoint((s->m_scrAlignment%3) == 1 ? p.x : (s->m_scrAlignment%3) == 0 ? p.x - spaceNeeded.cx : p.x - (spaceNeeded.cx+1)/2,
3252 s->m_scrAlignment <= 3 ? p.y - spaceNeeded.cy : s->m_scrAlignment <= 6 ? p.y - (spaceNeeded.cy+1)/2 : p.y),
3253 spaceNeeded);
3254 if(s->m_relativeTo == 1)
3255 r.OffsetRect(m_vidrect.TopLeft());
3256 fPosOverride = true;
3258 break;
3259 case EF_ORG: // {\org(x=param[0], y=param[1])}
3261 org2 = CPoint(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
3262 fOrgOverride = true;
3264 break;
3265 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])
3267 int t1 = s->m_effects[k]->t[0];
3268 int t2 = s->m_effects[k]->t[1];
3269 int t3 = s->m_effects[k]->t[2];
3270 int t4 = s->m_effects[k]->t[3];
3271 if(t1 == -1 && t4 == -1) {t1 = 0; t3 = m_delay-t3; t4 = m_delay;}
3272 if(m_time < t1) alpha = s->m_effects[k]->param[0];
3273 else if(m_time >= t1 && m_time < t2)
3275 double t = 1.0 * (m_time - t1) / (t2 - t1);
3276 alpha = (int)(s->m_effects[k]->param[0]*(1-t) + s->m_effects[k]->param[1]*t);
3278 else if(m_time >= t2 && m_time < t3) alpha = s->m_effects[k]->param[1];
3279 else if(m_time >= t3 && m_time < t4)
3281 double t = 1.0 * (m_time - t3) / (t4 - t3);
3282 alpha = (int)(s->m_effects[k]->param[1]*(1-t) + s->m_effects[k]->param[2]*t);
3284 else if(m_time >= t4) alpha = s->m_effects[k]->param[2];
3286 break;
3287 case EF_BANNER: // Banner;delay=param[0][;leftoright=param[1];fadeawaywidth=param[2]]
3289 int left = s->m_relativeTo == 1 ? m_vidrect.left : 0,
3290 right = s->m_relativeTo == 1 ? m_vidrect.right : m_size.cx;
3291 r.left = !!s->m_effects[k]->param[1]
3292 ? (left/*marginRect.left*/ - spaceNeeded.cx) + (int)(m_time*8.0/s->m_effects[k]->param[0])
3293 : (right /*- marginRect.right*/) - (int)(m_time*8.0/s->m_effects[k]->param[0]);
3294 r.right = r.left + spaceNeeded.cx;
3295 clipRect &= CRect(left>>3, clipRect.top, right>>3, clipRect.bottom);
3296 fPosOverride = true;
3298 break;
3299 case EF_SCROLL: // Scroll up/down(toptobottom=param[3]);top=param[0];bottom=param[1];delay=param[2][;fadeawayheight=param[4]]
3301 r.top = !!s->m_effects[k]->param[3]
3302 ? s->m_effects[k]->param[0] + (int)(m_time*8.0/s->m_effects[k]->param[2]) - spaceNeeded.cy
3303 : s->m_effects[k]->param[1] - (int)(m_time*8.0/s->m_effects[k]->param[2]);
3304 r.bottom = r.top + spaceNeeded.cy;
3305 CRect cr(0, (s->m_effects[k]->param[0] + 4) >> 3, spd.w, (s->m_effects[k]->param[1] + 4) >> 3);
3306 if(s->m_relativeTo == 1)
3307 r.top += m_vidrect.top,
3308 r.bottom += m_vidrect.top,
3309 cr.top += m_vidrect.top>>3,
3310 cr.bottom += m_vidrect.top>>3;
3311 clipRect &= cr;
3312 fPosOverride = true;
3314 break;
3315 default:
3316 break;
3319 if(!fPosOverride && !fOrgOverride && !s->m_fAnimated)
3320 r = m_sla.AllocRect(s, segment, entry, stse.layer, m_collisions);
3321 CPoint org;
3322 org.x = (s->m_scrAlignment%3) == 1 ? r.left : (s->m_scrAlignment%3) == 2 ? r.CenterPoint().x : r.right;
3323 org.y = s->m_scrAlignment <= 3 ? r.bottom : s->m_scrAlignment <= 6 ? r.CenterPoint().y : r.top;
3324 if(!fOrgOverride) org2 = org;
3325 CPoint p2(0, r.top);
3326 // Rectangles for inverse clip
3328 CSubtitle2& sub2 = outputSub2List->GetAt(outputSub2List->AddTail( CSubtitle2(s, clipRect, org, org2, p2, alpha, m_time) ));
3331 return (subs.GetCount()) ? S_OK : S_FALSE;
3334 STDMETHODIMP CRenderedTextSubtitle::RenderEx(SubPicDesc& spd, REFERENCE_TIME rt, double fps, CAtlList<CRect>& rectList)
3336 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))
3337 Init(CSize(spd.w, spd.h), spd.vidrect);
3339 CSubtitle2List sub2List;
3340 HRESULT hr = ParseScript(spd, rt, fps, &sub2List);
3341 if(hr!=S_OK)
3343 return hr;
3346 CompositeDrawItemListList drawItemListList;
3347 DoRender(spd, sub2List, &rectList, &drawItemListList);
3348 Draw(spd, drawItemListList);
3349 return (!rectList.IsEmpty()) ? S_OK : S_FALSE;
3352 void CRenderedTextSubtitle::DoRender( SubPicDesc& spd, const CSubtitle2List& sub2List,
3353 CAtlList<CRect> *rectList, CompositeDrawItemListList *drawItemListList /*output*/)
3355 //check input and log error
3356 POSITION pos=sub2List.GetHeadPosition();
3357 while ( pos!=NULL )
3359 const CSubtitle2& sub2 = sub2List.GetNext(pos);
3360 CompositeDrawItemList& drawItemList = drawItemListList->GetAt(drawItemListList->AddTail());
3361 RenderOneSubtitle(spd, sub2, rectList, &drawItemList);
3365 void CRenderedTextSubtitle::RenderOneSubtitle( SubPicDesc& spd, const CSubtitle2& sub2,
3366 CAtlList<CRect>* rectList, CompositeDrawItemList* drawItemList /*output*/)
3368 CSubtitle* s = sub2.s;
3369 const CRect& clipRect = sub2.clipRect;
3370 const CPoint& org = sub2.org;
3371 const CPoint& org2 = sub2.org2;
3372 const CPoint& p2 = sub2.p;
3373 int alpha = sub2.alpha;
3374 int time = sub2.time;
3375 if(!s) return;
3377 SharedArrayByte pAlphaMask;
3378 if( s->m_pClipper )
3379 pAlphaMask = s->m_pClipper->GetAlphaMask();
3380 CRect iclipRect[4];
3381 iclipRect[0] = CRect(0, 0, spd.w, clipRect.top);
3382 iclipRect[1] = CRect(0, clipRect.top, clipRect.left, clipRect.bottom);
3383 iclipRect[2] = CRect(clipRect.right, clipRect.top, spd.w, clipRect.bottom);
3384 iclipRect[3] = CRect(0, clipRect.bottom, spd.w, spd.h);
3385 CRect bbox2(0,0,0,0);
3386 POSITION pos = s->GetHeadLinePosition();
3387 CPoint p = p2;
3388 while(pos)
3390 CLine* l = s->GetNextLine(pos);
3391 p.x = (s->m_scrAlignment%3) == 1 ? org.x
3392 : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
3393 : org.x - (l->m_width/2);
3395 CompositeDrawItemList tmpDrawItemList;
3396 if (s->m_clipInverse)
3398 CompositeDrawItemList tmp1,tmp2,tmp3,tmp4;
3399 for (int i=0;i<l->GetWordCount();i++)
3401 tmp1.AddTail();
3402 tmp2.AddTail();
3403 tmp3.AddTail();
3404 tmp4.AddTail();
3406 bbox2 |= l->PaintAll(&tmp1, spd, iclipRect[0], pAlphaMask, p, org2, time, alpha);
3407 bbox2 |= l->PaintAll(&tmp2, spd, iclipRect[1], pAlphaMask, p, org2, time, alpha);
3408 bbox2 |= l->PaintAll(&tmp3, spd, iclipRect[2], pAlphaMask, p, org2, time, alpha);
3409 bbox2 |= l->PaintAll(&tmp4, spd, iclipRect[3], pAlphaMask, p, org2, time, alpha);
3410 tmpDrawItemList.AddTailList(&tmp1);
3411 tmpDrawItemList.AddTailList(&tmp2);
3412 tmpDrawItemList.AddTailList(&tmp3);
3413 tmpDrawItemList.AddTailList(&tmp4);
3415 else
3417 for (int i=0;i<l->GetWordCount();i++)
3419 tmpDrawItemList.AddTail();
3421 bbox2 |= l->PaintAll(&tmpDrawItemList, spd, clipRect, pAlphaMask, p, org2, time, alpha);
3423 drawItemList->AddTailList(&tmpDrawItemList);
3424 p.y += l->m_ascent + l->m_descent;
3426 rectList->AddTail(bbox2);
3429 STDMETHODIMP CRenderedTextSubtitle::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
3431 CAtlList<CRect> rectList;
3432 HRESULT result = RenderEx(spd, rt, fps, rectList);
3433 POSITION pos = rectList.GetHeadPosition();
3434 CRect bbox2(0,0,0,0);
3435 while(pos!=NULL)
3437 bbox2 |= rectList.GetNext(pos);
3439 bbox = bbox2;
3440 return result;
3443 // IPersist
3445 STDMETHODIMP CRenderedTextSubtitle::GetClassID(CLSID* pClassID)
3447 return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
3450 // ISubStream
3452 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStreamCount()
3454 return(1);
3457 STDMETHODIMP CRenderedTextSubtitle::GetStreamInfo(int iStream, WCHAR** ppName, LCID* pLCID)
3459 if(iStream != 0) return E_INVALIDARG;
3460 if(ppName)
3462 if(!(*ppName = (WCHAR*)CoTaskMemAlloc((m_name.GetLength()+1)*sizeof(WCHAR))))
3463 return E_OUTOFMEMORY;
3464 wcscpy(*ppName, CStringW(m_name));
3466 if(pLCID)
3468 *pLCID = 0; // TODO
3470 return S_OK;
3473 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStream()
3475 return(0);
3478 STDMETHODIMP CRenderedTextSubtitle::SetStream(int iStream)
3480 return iStream == 0 ? S_OK : E_FAIL;
3483 STDMETHODIMP CRenderedTextSubtitle::Reload()
3485 CFileStatus s;
3486 if(!CFile::GetStatus(m_path, s)) return E_FAIL;
3487 return !m_path.IsEmpty() && Open(m_path, DEFAULT_CHARSET) ? S_OK : E_FAIL;
3490 STDMETHODIMP_(bool) CRenderedTextSubtitle::IsColorTypeSupported( int type )
3492 return type==MSP_AYUV_PLANAR ||
3493 type==MSP_AYUV ||
3494 type==MSP_XY_AUYV ||
3495 type==MSP_RGBA;
3498 void CRenderedTextSubtitle::Draw( SubPicDesc& spd, CompositeDrawItemListList& drawItemListList )
3500 POSITION list_pos = drawItemListList.GetHeadPosition();
3501 while(list_pos)
3503 CompositeDrawItemList& drawItemList = drawItemListList.GetNext(list_pos);
3504 POSITION item_pos = drawItemList.GetHeadPosition();
3505 while(item_pos)
3507 CompositeDrawItem& draw_item = drawItemList.GetNext(item_pos);
3508 if(draw_item.shadow)
3509 Rasterizer::Draw( spd, *draw_item.shadow );
3511 item_pos = drawItemList.GetHeadPosition();
3512 while(item_pos)
3514 CompositeDrawItem& draw_item = drawItemList.GetNext(item_pos);
3515 if(draw_item.outline)
3516 Rasterizer::Draw( spd, *draw_item.outline );
3518 item_pos = drawItemList.GetHeadPosition();
3519 while(item_pos)
3521 CompositeDrawItem& draw_item = drawItemList.GetNext(item_pos);
3522 if(draw_item.body)
3523 Rasterizer::Draw( spd, *draw_item.body );