Rafactoring CClipper.
[xy_vsfilter.git] / src / subtitles / RTS.cpp
blobc924547e740f7c023da834f12c574304fab9b3cc
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::PaintFromScanLineData2(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 = new PathData(path_data);//fix me: this copy operation can be saved if no transform is needed
291 SharedPtrConstPathData shared_ptr_path_data2(path_data2);
292 bool need_transform = NeedTransform();
293 if(need_transform)
294 Transform(path_data2, CPoint(trans_org.x*8, trans_org.y*8));
296 CPoint left_top;
297 CSize size;
298 path_data2->AlignLeftTop(&left_top, &size);
300 ScanLineDataMruCache* scan_line_data_cache = CacheManager::GetScanLineDataMruCache();
301 ScanLineDataCacheKey scan_line_data_key(shared_ptr_path_data2);
302 POSITION pos = scan_line_data_cache->Lookup(scan_line_data_key);
303 SharedPtrConstScanLineData scan_line_data;
304 if( pos != NULL )
306 scan_line_data = scan_line_data_cache->GetAt(pos);
307 scan_line_data_cache->UpdateCache(pos);
309 else
311 ScanLineData *tmp = new ScanLineData();
312 scan_line_data.reset(tmp);
313 if(!tmp->ScanConvert(*path_data2, size))
315 return false;
317 scan_line_data_cache->UpdateCache(scan_line_data_key, scan_line_data);
319 ScanLineData2 *tmp = new ScanLineData2(left_top, scan_line_data);
320 SharedPtrScanLineData2 scan_line_data2( tmp );
321 if(m_style.get().borderStyle == 0 && (m_style.get().outlineWidthX+m_style.get().outlineWidthY > 0))
323 if(!tmp->CreateWidenedRegion(static_cast<int>(m_style.get().outlineWidthX+0.5),
324 static_cast<int>(m_style.get().outlineWidthY+0.5)))
326 return false;
329 else if(m_style.get().borderStyle == 1)
331 if(!CreateOpaqueBox())
333 return false;
336 ScanLineData2MruCache* scan_line_data2_cache = CacheManager::GetScanLineData2MruCache();
337 scan_line_data2_cache->UpdateCache(key, scan_line_data2);
338 return PaintFromScanLineData2(psub, *tmp, key, overlay);
341 bool CWord::PaintFromRawData( const CPoint& psub, const CPoint& trans_org, const OverlayKey& key, SharedPtrOverlay* overlay )
343 PathDataMruCache* path_data_cache = CacheManager::GetPathDataMruCache();
345 PathData *tmp=new PathData();
346 SharedPtrPathData path_data(tmp);
347 if(!CreatePath(tmp))
349 return false;
351 path_data_cache->UpdateCache(key, path_data);
352 return PaintFromPathData(psub, trans_org, *tmp, key, overlay);
355 bool CWord::DoPaint(const CPoint& psub, const CPoint& trans_org, SharedPtrOverlay* overlay, const OverlayKey& key)
357 bool result = true;
358 OverlayNoBlurMruCache* overlay_no_blur_cache = CacheManager::GetOverlayNoBlurMruCache();
359 POSITION pos = overlay_no_blur_cache->Lookup(key);
361 if(pos!=NULL)
363 SharedPtrOverlay raterize_result = overlay_no_blur_cache->GetAt(pos);
364 overlay_no_blur_cache->UpdateCache( pos );
365 PaintFromNoneBluredOverlay(raterize_result, key, overlay);
367 else
369 ScanLineData2MruCache* scan_line_data_cache = CacheManager::GetScanLineData2MruCache();
370 pos = scan_line_data_cache->Lookup(key);
371 if(pos!=NULL)
373 SharedPtrConstScanLineData2 scan_line_data = scan_line_data_cache->GetAt(pos);
374 scan_line_data_cache->UpdateCache( pos );
375 result = PaintFromScanLineData2(psub, *scan_line_data, key, overlay);
377 else
379 PathDataMruCache* path_data_cache = CacheManager::GetPathDataMruCache();
380 POSITION pos_path = path_data_cache->Lookup(key);
381 if(pos_path!=NULL)
383 SharedPtrConstPathData path_data = path_data_cache->GetAt(pos_path); //important! copy not ref
384 path_data_cache->UpdateCache( pos_path );
385 result = PaintFromPathData(psub, trans_org, *path_data, key, overlay);
387 else
389 result = PaintFromRawData(psub, trans_org, key, overlay);
393 return result;
396 bool CWord::NeedTransform()
398 return (fabs(m_style.get().fontScaleX - 100) > 0.000001) ||
399 (fabs(m_style.get().fontScaleY - 100) > 0.000001) ||
400 (fabs(m_style.get().fontAngleX) > 0.000001) ||
401 (fabs(m_style.get().fontAngleY) > 0.000001) ||
402 (fabs(m_style.get().fontAngleZ) > 0.000001) ||
403 (fabs(m_style.get().fontShiftX) > 0.000001) ||
404 (fabs(m_style.get().fontShiftY) > 0.000001);
407 void CWord::Transform(PathData* path_data, const CPoint& org)
409 //// CPUID from VDub
410 //bool fSSE2 = !!(g_cpuid.m_flags & CCpuID::sse2);
412 //if(fSSE2) { // SSE code
413 // Transform_SSE2(path_data, org);
414 //} else // C-code
415 Transform_C(path_data, org);
418 void CWord::Transform_C(PathData* path_data, const CPoint &org )
420 double scalex = m_style.get().fontScaleX/100;
421 double scaley = m_style.get().fontScaleY/100;
423 double caz = cos((3.1415/180)*m_style.get().fontAngleZ);
424 double saz = sin((3.1415/180)*m_style.get().fontAngleZ);
425 double cax = cos((3.1415/180)*m_style.get().fontAngleX);
426 double sax = sin((3.1415/180)*m_style.get().fontAngleX);
427 double cay = cos((3.1415/180)*m_style.get().fontAngleY);
428 double say = sin((3.1415/180)*m_style.get().fontAngleY);
430 #ifdef _VSMOD
431 // patch m003. random text points
432 double xrnd = m_style.get().mod_rand.X*100;
433 double yrnd = m_style.get().mod_rand.Y*100;
434 double zrnd = m_style.get().mod_rand.Z*100;
436 srand(m_style.get().mod_rand.Seed);
438 // patch m008. distort
439 int xsz,ysz;
440 double dst1x,dst1y,dst2x,dst2y,dst3x,dst3y;
441 int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
443 bool is_dist = m_style.get().mod_distort.enabled;
444 if (is_dist) {
445 for(int i = 0; i < path_data->mPathPoints; i++) {
446 if(minx > path_data->mpPathPoints[i].x) {
447 minx = path_data->mpPathPoints[i].x;
449 if(miny > path_data->mpPathPoints[i].y) {
450 miny = path_data->mpPathPoints[i].y;
452 if(maxx < path_data->mpPathPoints[i].x) {
453 maxx = path_data->mpPathPoints[i].x;
455 if(maxy < path_data->mpPathPoints[i].y) {
456 maxy = path_data->mpPathPoints[i].y;
460 xsz = max(maxx - minx, 0);
461 ysz = max(maxy - miny, 0);
463 dst1x = m_style.get().mod_distort.pointsx[0];
464 dst1y = m_style.get().mod_distort.pointsy[0];
465 dst2x = m_style.get().mod_distort.pointsx[1];
466 dst2y = m_style.get().mod_distort.pointsy[1];
467 dst3x = m_style.get().mod_distort.pointsx[2];
468 dst3y = m_style.get().mod_distort.pointsy[2];
470 #endif
472 for (int i = 0; i < path_data->mPathPoints; i++) {
473 double x, y, z, xx, yy, zz;
475 x = path_data->mpPathPoints[i].x;
476 y = path_data->mpPathPoints[i].y;
477 #ifdef _VSMOD
478 // patch m002. Z-coord
479 z = m_style.get().mod_z;
481 double u, v;
482 if (is_dist) {
483 u = (x-minx) / xsz;
484 v = (y-miny) / ysz;
486 x = minx+(0 + (dst1x - 0)*u + (dst3x-0)*v+(0+dst2x-dst1x-dst3x)*u*v)*xsz;
487 y = miny+(0 + (dst1y - 0)*u + (dst3y-0)*v+(0+dst2y-dst1y-dst3y)*u*v)*ysz;
488 //P = P0 + (P1 - P0)u + (P3 - P0)v + (P0 + P2 - P1 - P3)uv
491 // patch m003. random text points
492 x = xrnd > 0 ? (xrnd - rand() % (int)(xrnd * 2 + 1)) / 100.0 + x : x;
493 y = yrnd > 0 ? (yrnd - rand() % (int)(yrnd * 2 + 1)) / 100.0 + y : y;
494 z = zrnd > 0 ? (zrnd - rand() % (int)(zrnd * 2 + 1)) / 100.0 + z : z;
495 #else
496 z = 0;
497 #endif
498 double _x = x;
499 x = scalex * (x + m_style.get().fontShiftX * y) - org.x;
500 y = scaley * (y + m_style.get().fontShiftY * _x) - org.y;
502 xx = x*caz + y*saz;
503 yy = -(x*saz - y*caz);
504 zz = z;
506 x = xx;
507 y = yy*cax + zz*sax;
508 z = yy*sax - zz*cax;
510 xx = x*cay + z*say;
511 yy = y;
512 zz = x*say - z*cay;
514 zz = max(zz, -19000);
516 x = (xx * 20000) / (zz + 20000);
517 y = (yy * 20000) / (zz + 20000);
519 path_data->mpPathPoints[i].x = (LONG)(x + org.x + 0.5);
520 path_data->mpPathPoints[i].y = (LONG)(y + org.y + 0.5);
524 void CWord::Transform_SSE2(PathData* path_data, const CPoint &org )
526 // __m128 union data type currently not supported with Intel C++ Compiler, so just call C version
527 #ifdef __ICL
528 Transform_C(org);
529 #else
530 // SSE code
531 // speed up ~1.5-1.7x
532 double scalex = m_style.get().fontScaleX/100;
533 double scaley = m_style.get().fontScaleY/100;
535 double caz = cos((3.1415/180)*m_style.get().fontAngleZ);
536 double saz = sin((3.1415/180)*m_style.get().fontAngleZ);
537 double cax = cos((3.1415/180)*m_style.get().fontAngleX);
538 double sax = sin((3.1415/180)*m_style.get().fontAngleX);
539 double cay = cos((3.1415/180)*m_style.get().fontAngleY);
540 double say = sin((3.1415/180)*m_style.get().fontAngleY);
542 __m128 __xshift = _mm_set_ps1(m_style.get().fontShiftX);
543 __m128 __yshift = _mm_set_ps1(m_style.get().fontShiftY);
545 __m128 __xorg = _mm_set_ps1(org.x);
546 __m128 __yorg = _mm_set_ps1(org.y);
548 __m128 __xscale = _mm_set_ps1(scalex);
549 __m128 __yscale = _mm_set_ps1(scaley);
551 #ifdef _VSMOD
552 // patch m003. random text points
553 double xrnd = m_style.get().mod_rand.X*100;
554 double yrnd = m_style.get().mod_rand.Y*100;
555 double zrnd = m_style.get().mod_rand.Z*100;
557 srand(m_style.get().mod_rand.Seed);
559 __m128 __xsz = _mm_setzero_ps();
560 __m128 __ysz = _mm_setzero_ps();
562 __m128 __dst1x, __dst1y, __dst213x, __dst213y, __dst3x, __dst3y;
564 __m128 __miny;
565 __m128 __minx = _mm_set_ps(INT_MAX, INT_MAX, 0, 0);
566 __m128 __max = _mm_set_ps(-INT_MAX, -INT_MAX, 1, 1);
568 bool is_dist = m_style.get().mod_distort.enabled;
569 if(is_dist) {
570 for(int i = 0; i < path_data->mPathPoints; i++) {
571 __m128 __point = _mm_set_ps(path_data->mpPathPoints[i].x, path_data->mpPathPoints[i].y, 0, 0);
572 __minx = _mm_min_ps(__minx, __point);
573 __max = _mm_max_ps(__max, __point);
576 __m128 __zero = _mm_setzero_ps();
577 __max = _mm_sub_ps(__max, __minx); // xsz, ysz, 1, 1
578 __max = _mm_max_ps(__max, __zero);
580 __xsz = _mm_shuffle_ps(__max, __max, _MM_SHUFFLE(3,3,3,3));
581 __ysz = _mm_shuffle_ps(__max, __max, _MM_SHUFFLE(2,2,2,2));
583 __miny = _mm_shuffle_ps(__minx, __minx, _MM_SHUFFLE(2,2,2,2));
584 __minx = _mm_shuffle_ps(__minx, __minx, _MM_SHUFFLE(3,3,3,3));
586 __dst1x = _mm_set_ps1(m_style.get().mod_distort.pointsx[0]);
587 __dst1y = _mm_set_ps1(m_style.get().mod_distort.pointsy[0]);
588 __dst3x = _mm_set_ps1(m_style.get().mod_distort.pointsx[2]);
589 __dst3y = _mm_set_ps1(m_style.get().mod_distort.pointsy[2]);
590 __dst213x = _mm_set_ps1(m_style.get().mod_distort.pointsx[1]); // 2 - 1 - 3
591 __dst213x = _mm_sub_ps(__dst213x, __dst1x);
592 __dst213x = _mm_sub_ps(__dst213x, __dst3x);
594 __dst213y = _mm_set_ps1(m_style.get().mod_distort.pointsy[1]);
595 __dst213x = _mm_sub_ps(__dst213y, __dst1y);
596 __dst213x = _mm_sub_ps(__dst213y, __dst3y);
598 #endif
600 __m128 __caz = _mm_set_ps1(caz);
601 __m128 __saz = _mm_set_ps1(saz);
602 __m128 __cax = _mm_set_ps1(cax);
603 __m128 __sax = _mm_set_ps1(sax);
604 __m128 __cay = _mm_set_ps1(cay);
605 __m128 __say = _mm_set_ps1(say);
607 // this can be paralleled for openmp
608 int mPathPointsD4 = path_data->mPathPoints / 4;
609 int mPathPointsM4 = path_data->mPathPoints % 4;
611 for(ptrdiff_t i = 0; i < mPathPointsD4 + 1; i++) {
612 POINT* const temp_points = path_data->mpPathPoints + 4 * i;
614 __m128 __pointx, __pointy;
615 // we can't use load .-.
616 if(i != mPathPointsD4) {
617 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, temp_points[2].x, temp_points[3].x);
618 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, temp_points[2].y, temp_points[3].y);
619 } else { // last cycle
620 switch(mPathPointsM4) {
621 default:
622 case 0:
623 continue;
624 case 1:
625 __pointx = _mm_set_ps(temp_points[0].x, 0, 0, 0);
626 __pointy = _mm_set_ps(temp_points[0].y, 0, 0, 0);
627 break;
628 case 2:
629 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, 0, 0);
630 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, 0, 0);
631 break;
632 case 3:
633 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, temp_points[2].x, 0);
634 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, temp_points[2].y, 0);
635 break;
639 #ifdef _VSMOD
640 __m128 __pointz = _mm_set_ps1(m_style.get().mod_z);
642 // distort
643 if(is_dist) {
644 //P = P0 + (P1 - P0)u + (P3 - P0)v + (P0 + P2 - P1 - P3)uv
645 __m128 __u = _mm_sub_ps(__pointx, __minx);
646 __m128 __v = _mm_sub_ps(__pointy, __miny);
647 __m128 __1_xsz = _mm_rcp_ps(__xsz);
648 __m128 __1_ysz = _mm_rcp_ps(__ysz);
649 __u = _mm_mul_ps(__u, __1_xsz);
650 __v = _mm_mul_ps(__v, __1_ysz);
652 // x
653 __pointx = _mm_mul_ps(__dst213x, __u);
654 __pointx = _mm_mul_ps(__pointx, __v);
656 __m128 __tmpx = _mm_mul_ps(__dst3x, __v);
657 __pointx = _mm_add_ps(__pointx, __tmpx);
658 __tmpx = _mm_mul_ps(__dst1x, __u);
659 __pointx = _mm_add_ps(__pointx, __tmpx);
661 __pointx = _mm_mul_ps(__pointx, __xsz);
662 __pointx = _mm_add_ps(__pointx, __minx);
664 // y
665 __pointy = _mm_mul_ps(__dst213y, __u);
666 __pointy = _mm_mul_ps(__pointy, __v);
668 __m128 __tmpy = _mm_mul_ps(__dst3y, __v);
669 __pointy = _mm_add_ps(__pointy, __tmpy);
670 __tmpy = _mm_mul_ps(__dst1y, __u);
671 __pointy = _mm_add_ps(__pointy, __tmpy);
673 __pointy = _mm_mul_ps(__pointy, __ysz);
674 __pointy = _mm_add_ps(__pointy, __miny);
677 // randomize
678 if(xrnd!=0 || yrnd!=0 || zrnd!=0) {
679 __declspec(align(16)) float rx[4], ry[4], rz[4];
680 for(int k=0; k<4; k++) {
681 rx[k] = xrnd > 0 ? (xrnd - rand() % (int)(xrnd * 2 + 1)) : 0;
682 ry[k] = yrnd > 0 ? (yrnd - rand() % (int)(yrnd * 2 + 1)) : 0;
683 rz[k] = zrnd > 0 ? (zrnd - rand() % (int)(zrnd * 2 + 1)) : 0;
685 __m128 __001 = _mm_set_ps1(0.01f);
687 if(xrnd!=0) {
688 __m128 __rx = _mm_load_ps(rx);
689 __rx = _mm_mul_ps(__rx, __001);
690 __pointx = _mm_add_ps(__pointx, __rx);
693 if(yrnd!=0) {
694 __m128 __ry = _mm_load_ps(ry);
695 __ry = _mm_mul_ps(__ry, __001);
696 __pointy = _mm_add_ps(__pointy, __ry);
699 if(zrnd!=0) {
700 __m128 __rz = _mm_load_ps(rz);
701 __rz = _mm_mul_ps(__rz, __001);
702 __pointz = _mm_add_ps(__pointz, __rz);
705 #else
706 __m128 __pointz = _mm_set_ps1(0);
707 #endif
709 // scale and shift
710 __m128 __tmpx;
711 if(m_style.get().fontShiftX!=0) {
712 __tmpx = _mm_mul_ps(__xshift, __pointy);
713 __tmpx = _mm_add_ps(__tmpx, __pointx);
714 } else {
715 __tmpx = __pointx;
717 __tmpx = _mm_mul_ps(__tmpx, __xscale);
718 __tmpx = _mm_sub_ps(__tmpx, __xorg);
720 __m128 __tmpy;
721 if(m_style.get().fontShiftY!=0) {
722 __tmpy = _mm_mul_ps(__yshift, __pointx);
723 __tmpy = _mm_add_ps(__tmpy, __pointy);
724 } else {
725 __tmpy = __pointy;
727 __tmpy = _mm_mul_ps(__tmpy, __yscale);
728 __tmpy = _mm_sub_ps(__tmpy, __yorg);
730 // rotate
731 __m128 __xx = _mm_mul_ps(__tmpx, __caz);
732 __m128 __yy = _mm_mul_ps(__tmpy, __saz);
733 __pointx = _mm_add_ps(__xx, __yy);
734 __xx = _mm_mul_ps(__tmpx, __saz);
735 __yy = _mm_mul_ps(__tmpy, __caz);
736 __pointy = _mm_sub_ps(__yy, __xx);
738 __m128 __zz = _mm_mul_ps(__pointz, __sax);
739 __yy = _mm_mul_ps(__pointy, __cax);
740 __pointy = _mm_add_ps(__yy, __zz);
741 __zz = _mm_mul_ps(__pointz, __cax);
742 __yy = _mm_mul_ps(__pointy, __sax);
743 __pointz = _mm_sub_ps(__zz, __yy);
745 __xx = _mm_mul_ps(__pointx, __cay);
746 __zz = _mm_mul_ps(__pointz, __say);
747 __pointx = _mm_add_ps(__xx, __zz);
748 __xx = _mm_mul_ps(__pointx, __say);
749 __zz = _mm_mul_ps(__pointz, __cay);
750 __pointz = _mm_sub_ps(__xx, __zz);
752 __zz = _mm_set_ps1(-19000);
753 __pointz = _mm_max_ps(__pointz, __zz);
755 __m128 __20000 = _mm_set_ps1(20000);
756 __zz = _mm_add_ps(__pointz, __20000);
757 __zz = _mm_rcp_ps(__zz);
759 __pointx = _mm_mul_ps(__pointx, __20000);
760 __pointx = _mm_mul_ps(__pointx, __zz);
762 __pointy = _mm_mul_ps(__pointy, __20000);
763 __pointy = _mm_mul_ps(__pointy, __zz);
765 __pointx = _mm_add_ps(__pointx, __xorg);
766 __pointy = _mm_add_ps(__pointy, __yorg);
768 __m128 __05 = _mm_set_ps1(0.5);
770 __pointx = _mm_add_ps(__pointx, __05);
771 __pointy = _mm_add_ps(__pointy, __05);
773 if(i == mPathPointsD4) { // last cycle
774 for(int k=0; k<mPathPointsM4; k++) {
775 temp_points[k].x = static_cast<LONG>(__pointx.m128_f32[3-k]);
776 temp_points[k].y = static_cast<LONG>(__pointy.m128_f32[3-k]);
778 } else {
779 for(int k=0; k<4; k++) {
780 temp_points[k].x = static_cast<LONG>(__pointx.m128_f32[3-k]);
781 temp_points[k].y = static_cast<LONG>(__pointy.m128_f32[3-k]);
785 #endif // __ICL
788 bool CWord::CreateOpaqueBox()
790 if(m_pOpaqueBox) return(true);
791 STSStyle style = m_style.get();
792 style.borderStyle = 0;
793 style.outlineWidthX = style.outlineWidthY = 0;
794 style.colors[0] = m_style.get().colors[2];
795 style.alpha[0] = m_style.get().alpha[2];
796 int w = (int)(m_style.get().outlineWidthX + 0.5);
797 int h = (int)(m_style.get().outlineWidthY + 0.5);
798 CStringW str;
799 str.Format(L"m %d %d l %d %d %d %d %d %d",
800 -w, -h,
801 m_width+w, -h,
802 m_width+w, m_ascent+m_descent+h,
803 -w, m_ascent+m_descent+h);
804 m_pOpaqueBox.reset( new CPolygon(FwSTSStyle(style), str, 0, 0, 0, 1.0/8, 1.0/8, 0) );
805 return(!!m_pOpaqueBox);
808 void CWord::PaintAll( SharedPtrCWord word,
809 const CPoint& shadowPos, const CPoint& outlinePos, const CPoint& bodyPos, const CPoint& org,
810 OverlayList* shadow, OverlayList* outline, OverlayList* body )
812 CPoint transOrg;
813 if(shadow!=NULL)
815 //has shadow
816 transOrg = org - shadowPos;
818 else if(outline!=NULL)
820 //has outline
821 transOrg = org - outlinePos;
823 else if(body!=NULL)
825 transOrg = org - bodyPos;
827 if(shadow!=NULL)
829 Paint(word, shadowPos, transOrg, shadow);
831 if(outline!=NULL)
833 Paint(word, outlinePos, transOrg, outline);
835 if(body!=NULL)
837 Paint(word, bodyPos, transOrg, body);
842 // CText
844 CText::CText(const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend)
845 : CWord(style, str, ktype, kstart, kend)
847 if(m_str == L" ")
849 m_fWhiteSpaceChar = true;
851 SharedPtrTextInfo text_info;
852 TextInfoCacheKey text_info_key;
853 text_info_key.m_str = m_str;
854 text_info_key.m_style = m_style;
855 TextInfoMruCache* text_info_cache = CacheManager::GetTextInfoCache();
856 POSITION pos = text_info_cache->Lookup(text_info_key);
857 if(pos==NULL)
859 TextInfo* tmp=new TextInfo();
860 GetTextInfo(tmp, m_style, m_str);
861 text_info.reset(tmp);
862 text_info_cache->UpdateCache(text_info_key, text_info);
864 else
866 text_info = text_info_cache->GetAt(pos);
867 text_info_cache->UpdateCache( pos );
869 this->m_ascent = text_info->m_ascent;
870 this->m_descent = text_info->m_descent;
871 this->m_width = text_info->m_width;
874 CText::CText( const CText& src ):CWord(src)
876 m_width = src.m_width;
879 SharedPtrCWord CText::Copy()
881 SharedPtrCWord result(new CText(*this));
882 return result;
885 bool CText::Append(const SharedPtrCWord& w)
887 boost::shared_ptr<CText> p = boost::dynamic_pointer_cast<CText>(w);
888 return (p && CWord::Append(w));
891 bool CText::CreatePath(PathData* path_data)
893 FwCMyFont font(m_style);
894 HFONT hOldFont = SelectFont(g_hDC, font.get());
895 int width = 0;
896 if(m_style.get().fontSpacing || (long)GetVersion() < 0)
898 bool bFirstPath = true;
899 for(LPCWSTR s = m_str; *s; s++)
901 CSize extent;
902 if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
903 path_data->PartialBeginPath(g_hDC, bFirstPath);
904 bFirstPath = false;
905 TextOutW(g_hDC, 0, 0, s, 1);
906 path_data->PartialEndPath(g_hDC, width, 0);
907 width += extent.cx + (int)m_style.get().fontSpacing;
910 else
912 CSize extent;
913 if(!GetTextExtentPoint32W(g_hDC, m_str, m_str.GetLength(), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
914 path_data->BeginPath(g_hDC);
915 TextOutW(g_hDC, 0, 0, m_str, m_str.GetLength());
916 path_data->EndPath(g_hDC);
918 SelectFont(g_hDC, hOldFont);
919 return(true);
922 void CText::GetTextInfo(TextInfo *output, const FwSTSStyle& style, const CStringW& str )
924 FwCMyFont font(style);
925 output->m_ascent = (int)(style.get().fontScaleY/100*font.get().m_ascent);
926 output->m_descent = (int)(style.get().fontScaleY/100*font.get().m_descent);
928 HFONT hOldFont = SelectFont(g_hDC, font.get());
929 if(style.get().fontSpacing || (long)GetVersion() < 0)
931 bool bFirstPath = true;
932 for(LPCWSTR s = str; *s; s++)
934 CSize extent;
935 if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
936 output->m_width += extent.cx + (int)style.get().fontSpacing;
938 // m_width -= (int)m_style.get().fontSpacing; // TODO: subtract only at the end of the line
940 else
942 CSize extent;
943 if(!GetTextExtentPoint32W(g_hDC, str, wcslen(str), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
944 output->m_width += extent.cx;
946 output->m_width = (int)(style.get().fontScaleX/100*output->m_width + 4) >> 3;
947 SelectFont(g_hDC, hOldFont);
950 // CPolygon
952 CPolygon::CPolygon(const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend, double scalex, double scaley, int baseline)
953 : CWord(style, str, ktype, kstart, kend)
954 , m_scalex(scalex), m_scaley(scaley), m_baseline(baseline)
956 ParseStr();
959 CPolygon::CPolygon(CPolygon& src) : CWord(src)
961 m_scalex = src.m_scalex;
962 m_scaley = src.m_scaley;
963 m_baseline = src.m_baseline;
964 m_width = src.m_width;
965 m_ascent = src.m_ascent;
966 m_descent = src.m_descent;
967 m_pathTypesOrg.Copy(src.m_pathTypesOrg);
968 m_pathPointsOrg.Copy(src.m_pathPointsOrg);
970 CPolygon::~CPolygon()
974 SharedPtrCWord CPolygon::Copy()
976 SharedPtrCWord result(DNew CPolygon(*this));
977 return result;
980 bool CPolygon::Append(const SharedPtrCWord& w)
982 // TODO
983 return(false);
986 bool CPolygon::Get6BitFixedPoint(CStringW& str, LONG& ret)
988 LPWSTR s = (LPWSTR)(LPCWSTR)str, e = s;
989 ret = wcstod(str, &e) * 64;
990 str.Delete(0,e-s);
991 XY_LOG_INFO(ret);
992 return(e > s);
995 bool CPolygon::GetPOINT(CStringW& str, POINT& ret)
997 return(Get6BitFixedPoint(str, ret.x) && Get6BitFixedPoint(str, ret.y));
1000 bool CPolygon::ParseStr()
1002 if(m_pathTypesOrg.GetCount() > 0) return(true);
1003 CPoint p;
1004 int j, lastsplinestart = -1, firstmoveto = -1, lastmoveto = -1;
1005 CStringW str = m_str;
1006 str.SpanIncluding(L"mnlbspc 0123456789");
1007 str.Replace(L"m", L"*m");
1008 str.Replace(L"n", L"*n");
1009 str.Replace(L"l", L"*l");
1010 str.Replace(L"b", L"*b");
1011 str.Replace(L"s", L"*s");
1012 str.Replace(L"p", L"*p");
1013 str.Replace(L"c", L"*c");
1014 int k = 0;
1015 for(CStringW s = str.Tokenize(L"*", k); !s.IsEmpty(); s = str.Tokenize(L"*", k))
1017 WCHAR c = s[0];
1018 s.TrimLeft(L"mnlbspc ");
1019 switch(c)
1021 case 'm':
1022 lastmoveto = m_pathTypesOrg.GetCount();
1023 if(firstmoveto == -1)
1024 firstmoveto = lastmoveto;
1025 while(GetPOINT(s, p)) {
1026 m_pathTypesOrg.Add(PT_MOVETO);
1027 m_pathPointsOrg.Add(p);
1029 break;
1030 case 'n':
1031 while(GetPOINT(s, p)) {
1032 m_pathTypesOrg.Add(PT_MOVETONC);
1033 m_pathPointsOrg.Add(p);
1035 break;
1036 case 'l':
1037 if (m_pathPointsOrg.GetCount() < 1) {
1038 break;
1040 while(GetPOINT(s, p)) {
1041 m_pathTypesOrg.Add(PT_LINETO);
1042 m_pathPointsOrg.Add(p);
1044 break;
1045 case 'b':
1046 j = m_pathTypesOrg.GetCount();
1047 if (j < 1) {
1048 break;
1050 while(GetPOINT(s, p)) {
1051 m_pathTypesOrg.Add(PT_BEZIERTO);
1052 m_pathPointsOrg.Add(p);
1053 j++;
1055 j = m_pathTypesOrg.GetCount() - ((m_pathTypesOrg.GetCount()-j)%3);
1056 m_pathTypesOrg.SetCount(j);
1057 m_pathPointsOrg.SetCount(j);
1058 break;
1059 case 's':
1060 if (m_pathPointsOrg.GetCount() < 1) {
1061 break;
1064 j = lastsplinestart = m_pathTypesOrg.GetCount();
1065 int i = 3;
1066 while(i-- && GetPOINT(s, p)) {
1067 m_pathTypesOrg.Add(PT_BSPLINETO);
1068 m_pathPointsOrg.Add(p);
1069 j++;
1071 if(m_pathTypesOrg.GetCount()-lastsplinestart < 3) {
1072 m_pathTypesOrg.SetCount(lastsplinestart);
1073 m_pathPointsOrg.SetCount(lastsplinestart);
1074 lastsplinestart = -1;
1077 // no break here
1078 case 'p':
1079 if (m_pathPointsOrg.GetCount() < 3) {
1080 break;
1082 while(GetPOINT(s, p)) {
1083 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1084 m_pathPointsOrg.Add(p);
1086 break;
1087 case 'c':
1088 if(lastsplinestart > 0)
1090 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1091 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1092 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1093 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)
1094 m_pathPointsOrg.Add(p);
1095 p = m_pathPointsOrg[lastsplinestart];
1096 m_pathPointsOrg.Add(p);
1097 p = m_pathPointsOrg[lastsplinestart+1];
1098 m_pathPointsOrg.Add(p);
1099 lastsplinestart = -1;
1101 break;
1102 default:
1103 break;
1106 if(lastmoveto == -1 || firstmoveto > 0)
1108 m_pathTypesOrg.RemoveAll();
1109 m_pathPointsOrg.RemoveAll();
1110 return(false);
1112 int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
1113 for(size_t i = 0; i < m_pathTypesOrg.GetCount(); i++)
1115 m_pathPointsOrg[i].x = (int)(m_scalex * m_pathPointsOrg[i].x);
1116 m_pathPointsOrg[i].y = (int)(m_scaley * m_pathPointsOrg[i].y);
1117 if(minx > m_pathPointsOrg[i].x) minx = m_pathPointsOrg[i].x;
1118 if(miny > m_pathPointsOrg[i].y) miny = m_pathPointsOrg[i].y;
1119 if(maxx < m_pathPointsOrg[i].x) maxx = m_pathPointsOrg[i].x;
1120 if(maxy < m_pathPointsOrg[i].y) maxy = m_pathPointsOrg[i].y;
1122 m_width = max(maxx - minx, 0);
1123 m_ascent = max(maxy - miny, 0);
1124 int baseline = (int)(64 * m_scaley * m_baseline);
1125 m_descent = baseline;
1126 m_ascent -= baseline;
1127 m_width = ((int)(m_style.get().fontScaleX/100 * m_width) + 4) >> 3;
1128 m_ascent = ((int)(m_style.get().fontScaleY/100 * m_ascent) + 4) >> 3;
1129 m_descent = ((int)(m_style.get().fontScaleY/100 * m_descent) + 4) >> 3;
1130 return(true);
1133 bool CPolygon::CreatePath(PathData* path_data)
1135 int len = m_pathTypesOrg.GetCount();
1136 if(len == 0) return(false);
1137 if(path_data->mPathPoints != len)
1139 path_data->mpPathTypes = (BYTE*)realloc(path_data->mpPathTypes, len*sizeof(BYTE));
1140 path_data->mpPathPoints = (POINT*)realloc(path_data->mpPathPoints, len*sizeof(POINT));
1141 if(!path_data->mpPathTypes || !path_data->mpPathPoints) return(false);
1142 path_data->mPathPoints = len;
1144 memcpy(path_data->mpPathTypes, m_pathTypesOrg.GetData(), len*sizeof(BYTE));
1145 memcpy(path_data->mpPathPoints, m_pathPointsOrg.GetData(), len*sizeof(POINT));
1146 return(true);
1149 // CClipper
1151 CClipper::CClipper(CStringW str, CSize size, double scalex, double scaley, bool inverse)
1152 : m_polygon( new CPolygon(FwSTSStyle(), str, 0, 0, 0, scalex, scaley, 0) )
1153 , m_size(size), m_inverse(inverse)
1154 , m_effectType(-1), m_painted(false)
1159 CClipper::~CClipper()
1163 GrayImage2* CClipper::PaintSimpleClipper()
1165 GrayImage2* result = NULL;
1166 if(m_size.cx < 0 || m_size.cy < 0)
1167 return result;
1169 OverlayList overlay_list;
1170 CWord::Paint( m_polygon, CPoint(0, 0), CPoint(0, 0), &overlay_list );
1171 int w = overlay_list.overlay->mOverlayWidth, h = overlay_list.overlay->mOverlayHeight;
1172 int x = (overlay_list.overlay->mOffsetX+4)>>3, y = (overlay_list.overlay->mOffsetY+4)>>3;
1173 int xo = 0, yo = 0;
1174 if(x < 0) {xo = -x; w -= -x; x = 0;}
1175 if(y < 0) {yo = -y; h -= -y; y = 0;}
1176 if(x+w > m_size.cx) w = m_size.cx-x;
1177 if(y+h > m_size.cy) h = m_size.cy-y;
1178 if(w <= 0 || h <= 0) return result;
1180 result = new GrayImage2();
1181 if( !result )
1182 return result;
1183 result->data.reset(new BYTE[w*h]);
1184 result->pitch = w;
1185 result->size.SetSize(w, h);
1186 result->left_top.SetPoint(x, y);
1188 //fix me: unnecessary copy
1189 BYTE * result_data = result->data.get();
1190 if(!result_data)
1192 delete result;
1193 return NULL;
1195 const BYTE* src = overlay_list.overlay->mpOverlayBuffer.body + (overlay_list.overlay->mOverlayPitch * yo + xo);
1196 while(h--)
1198 //for(int wt=0; wt<w; ++wt)
1199 // dst[wt] = src[wt];
1200 memcpy(result_data, src, w);
1201 src += overlay_list.overlay->mOverlayPitch;
1202 result_data += w;
1204 return result;
1207 GrayImage2* CClipper::PaintBaseClipper()
1209 GrayImage2* result = NULL;
1210 //m_pAlphaMask = NULL;
1211 if(m_size.cx < 0 || m_size.cy < 0)
1212 return result;
1214 OverlayList overlay_list;
1215 CWord::Paint( m_polygon, CPoint(0, 0), CPoint(0, 0), &overlay_list );
1216 int w = overlay_list.overlay->mOverlayWidth, h = overlay_list.overlay->mOverlayHeight;
1217 int x = (overlay_list.overlay->mOffsetX+4)>>3, y = (overlay_list.overlay->mOffsetY+4)>>3;
1218 int xo = 0, yo = 0;
1219 if(x < 0) {xo = -x; w -= -x; x = 0;}
1220 if(y < 0) {yo = -y; h -= -y; y = 0;}
1221 if(x+w > m_size.cx) w = m_size.cx-x;
1222 if(y+h > m_size.cy) h = m_size.cy-y;
1223 if(w <= 0 || h <= 0) return result;
1225 result = new GrayImage2();
1226 if( !result )
1227 return result;
1228 result->data.reset(new BYTE[m_size.cx*m_size.cy]);
1229 result->pitch = m_size.cx;
1230 result->size = m_size;
1231 result->left_top.SetPoint(0, 0);
1233 BYTE * result_data = result->data.get();
1234 if(!result_data)
1236 delete result;
1237 return NULL;
1240 memset( result_data, 0, m_size.cx*m_size.cy );
1242 const BYTE* src = overlay_list.overlay->mpOverlayBuffer.body + (overlay_list.overlay->mOverlayPitch * yo + xo);
1243 BYTE* dst = result_data + m_size.cx * y + x;
1244 while(h--)
1246 //for(int wt=0; wt<w; ++wt)
1247 // dst[wt] = src[wt];
1248 memcpy(dst, src, w);
1249 src += overlay_list.overlay->mOverlayPitch;
1250 dst += m_size.cx;
1252 if(m_inverse)
1254 BYTE* dst = result_data;
1255 for(int i = m_size.cx*m_size.cy; i>0; --i, ++dst)
1256 *dst = 0x40 - *dst; // mask is 6 bit
1258 return result;
1261 GrayImage2* CClipper::PaintBannerClipper()
1263 int width = m_effect.param[2];
1264 int w = m_size.cx, h = m_size.cy;
1266 GrayImage2* result = PaintBaseClipper();
1267 if(!result)
1268 return result;
1270 int da = (64<<8)/width;
1271 BYTE* am = result->data.get();
1272 for(int j = 0; j < h; j++, am += w)
1274 int a = 0;
1275 int k = min(width, w);
1276 for(int i = 0; i < k; i++, a += da)
1277 am[i] = (am[i]*a)>>14;
1278 a = 0x40<<8;
1279 k = w-width;
1280 if(k < 0) {a -= -k*da; k = 0;}
1281 for(int i = k; i < w; i++, a -= da)
1282 am[i] = (am[i]*a)>>14;
1284 return result;
1287 GrayImage2* CClipper::PaintScrollClipper()
1289 int height = m_effect.param[4];
1290 int w = m_size.cx, h = m_size.cy;
1292 GrayImage2* result = PaintBaseClipper();
1293 if(!result)
1294 return result;
1296 BYTE* data = result->data.get();
1298 int da = (64<<8)/height;
1299 int a = 0;
1300 int k = m_effect.param[0]>>3;
1301 int l = k+height;
1302 if(k < 0) {a += -k*da; k = 0;}
1303 if(l > h) {l = h;}
1304 if(k < h)
1306 BYTE* am = &data[k*w];
1307 memset(result, 0, am - data);
1308 for(int j = k; j < l; j++, a += da)
1310 for(int i = 0; i < w; i++, am++)
1311 *am = ((*am)*a)>>14;
1314 da = -(64<<8)/height;
1315 a = 0x40<<8;
1316 l = m_effect.param[1]>>3;
1317 k = l-height;
1318 if(k < 0) {a += -k*da; k = 0;}
1319 if(l > h) {l = h;}
1320 if(k < h)
1322 BYTE* am = &data[k*w];
1323 int j = k;
1324 for(; j < l; j++, a += da)
1326 for(int i = 0; i < w; i++, am++)
1327 *am = ((*am)*a)>>14;
1329 memset(am, 0, (h-j)*w);
1331 return result;
1334 GrayImage2* CClipper::Paint()
1336 GrayImage2* result = NULL;
1337 switch(m_effectType)
1339 case -1:
1340 if (!m_inverse)
1342 result = PaintSimpleClipper();
1344 else
1346 result = PaintBaseClipper();
1348 break;
1349 case EF_BANNER:
1350 result = PaintBannerClipper();
1351 break;
1352 case EF_SCROLL:
1353 result = PaintScrollClipper();
1354 break;
1356 return result;
1359 void CClipper::SetEffect( const Effect& effect, int effectType )
1361 m_effectType = effectType;
1362 m_effect = effect;
1365 SharedPtrGrayImage2 CClipper::GetAlphaMask( const SharedPtrCClipper& clipper )
1367 if (clipper!=NULL)
1369 ClipperAlphaMaskCacheKey key(clipper);
1370 ClipperAlphaMaskMruCache * cache = CacheManager::GetClipperAlphaMaskMruCache();
1371 POSITION pos = cache->Lookup(key);
1372 if( pos!=NULL )
1374 const SharedPtrGrayImage2& result = cache->GetAt(pos);
1375 cache->UpdateCache(pos);
1376 return result;
1378 else
1380 SharedPtrGrayImage2 result( clipper->Paint() );
1381 cache->UpdateCache(key, result);
1382 return result;
1385 else
1387 SharedPtrGrayImage2 result;
1388 return result;
1392 // CLine
1394 CLine::~CLine()
1396 //POSITION pos = GetHeadPosition();
1397 //while(pos) delete GetNext(pos);
1400 void CLine::Compact()
1402 POSITION pos = GetHeadPosition();
1403 while(pos)
1405 SharedPtrCWord w = GetNext(pos);
1406 if(!w->m_fWhiteSpaceChar) break;
1407 m_width -= w->m_width;
1408 // delete w;
1409 RemoveHead();
1411 pos = GetTailPosition();
1412 while(pos)
1414 SharedPtrCWord w = GetPrev(pos);
1415 if(!w->m_fWhiteSpaceChar) break;
1416 m_width -= w->m_width;
1417 // delete w;
1418 RemoveTail();
1420 if(IsEmpty()) return;
1421 CLine l;
1422 l.AddTailList(this);
1423 RemoveAll();
1424 SharedPtrCWord last;
1425 pos = l.GetHeadPosition();
1426 while(pos)
1428 SharedPtrCWord w = l.GetNext(pos);
1429 if(!last || !last->Append(w))
1430 AddTail(last = w->Copy());
1432 m_ascent = m_descent = m_borderX = m_borderY = 0;
1433 pos = GetHeadPosition();
1434 while(pos)
1436 SharedPtrCWord w = GetNext(pos);
1437 if(m_ascent < w->m_ascent) m_ascent = w->m_ascent;
1438 if(m_descent < w->m_descent) m_descent = w->m_descent;
1439 if(m_borderX < w->m_style.get().outlineWidthX) m_borderX = (int)(w->m_style.get().outlineWidthX+0.5);
1440 if(m_borderY < w->m_style.get().outlineWidthY) m_borderY = (int)(w->m_style.get().outlineWidthY+0.5);
1444 CRect CLine::PaintAll( CompositeDrawItemList* output, SubPicDesc& spd, const CRect& clipRect,
1445 const SharedPtrCClipper &clipper, CPoint p, const CPoint& org, const int time, const int alpha )
1447 CRect bbox(0, 0, 0, 0);
1448 POSITION pos = GetHeadPosition();
1449 POSITION outputPos = output->GetHeadPosition();
1450 while(pos)
1452 SharedPtrCWord w = GetNext(pos);
1453 CompositeDrawItem& outputItem = output->GetNext(outputPos);
1454 if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
1455 CPoint shadowPos, outlinePos, bodyPos, transOrg;
1456 shadowPos.x = p.x + static_cast<int>(w->m_style.get().shadowDepthX+0.5);
1457 shadowPos.y = p.y + m_ascent - w->m_ascent + static_cast<int>(w->m_style.get().shadowDepthY+0.5);
1458 outlinePos = CPoint(p.x, p.y + m_ascent - w->m_ascent);
1459 bodyPos = CPoint(p.x, p.y + m_ascent - w->m_ascent);
1460 bool hasShadow = w->m_style.get().shadowDepthX != 0 || w->m_style.get().shadowDepthY != 0;
1461 bool hasOutline = w->m_style.get().outlineWidthX+w->m_style.get().outlineWidthY > 0 && !(w->m_ktype == 2 && time < w->m_kstart);
1462 bool hasBody = true;
1464 OverlayList shadowOverlay, outlineOverlay, bodyOverlay;
1465 CWord::PaintAll(w, shadowPos, outlinePos, bodyPos, org,
1466 hasShadow ? &shadowOverlay : NULL,
1467 hasOutline ? &outlineOverlay : NULL,
1468 hasBody ? &bodyOverlay : NULL);
1469 //shadow
1470 if(hasShadow)
1472 DWORD a = 0xff - w->m_style.get().alpha[3];
1473 if(alpha > 0) a = MulDiv(a, 0xff - alpha, 0xff);
1474 COLORREF shadow = revcolor(w->m_style.get().colors[3]) | (a<<24);
1475 DWORD sw[6] = {shadow, -1};
1476 //xy
1477 if(spd.type == MSP_XY_AUYV)
1479 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1481 else if(spd.type == MSP_AYUV || spd.type == MSP_AYUV_PLANAR)
1483 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1485 if(w->m_style.get().borderStyle == 0)
1487 outputItem.shadow.reset(
1488 CRenderedTextSubtitle::CreateDrawItem(spd, shadowOverlay.overlay, clipRect, clipper, shadowPos.x, shadowPos.y, sw,
1489 w->m_ktype > 0 || w->m_style.get().alpha[0] < 0xff,
1490 (w->m_style.get().outlineWidthX+w->m_style.get().outlineWidthY > 0) && !(w->m_ktype == 2 && time < w->m_kstart))
1492 bbox |= CRenderedTextSubtitle::DryDraw(spd, *outputItem.shadow);
1494 else if(w->m_style.get().borderStyle == 1 && w->m_pOpaqueBox)
1496 outputItem.shadow.reset(
1497 CRenderedTextSubtitle::CreateDrawItem(spd, shadowOverlay.next->overlay, clipRect, clipper, shadowPos.x, shadowPos.y, sw, true, false)
1499 bbox |= CRenderedTextSubtitle::DryDraw(spd, *outputItem.shadow);
1502 //outline
1503 if(hasOutline)
1505 DWORD aoutline = w->m_style.get().alpha[2];
1506 if(alpha > 0) aoutline += MulDiv(alpha, 0xff - w->m_style.get().alpha[2], 0xff);
1507 COLORREF outline = revcolor(w->m_style.get().colors[2]) | ((0xff-aoutline)<<24);
1508 DWORD sw[6] = {outline, -1};
1509 //xy
1510 if(spd.type == MSP_XY_AUYV)
1512 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1514 else if(spd.type == MSP_AYUV || spd.type == MSP_AYUV_PLANAR)
1516 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1518 if(w->m_style.get().borderStyle == 0)
1520 outputItem.outline.reset(
1521 CRenderedTextSubtitle::CreateDrawItem(spd, outlineOverlay.overlay, clipRect, clipper, outlinePos.x, outlinePos.y, sw, !w->m_style.get().alpha[0] && !w->m_style.get().alpha[1] && !alpha, true)
1523 bbox |= CRenderedTextSubtitle::DryDraw(spd, *outputItem.outline);
1525 else if(w->m_style.get().borderStyle == 1 && w->m_pOpaqueBox)
1527 outputItem.outline.reset(
1528 CRenderedTextSubtitle::CreateDrawItem(spd, outlineOverlay.next->overlay, clipRect, clipper, outlinePos.x, outlinePos.y, sw, true, false)
1530 bbox |= CRenderedTextSubtitle::DryDraw(spd, *outputItem.outline);
1533 //body
1534 if(hasBody)
1536 // colors
1537 DWORD aprimary = w->m_style.get().alpha[0];
1538 if(alpha > 0) aprimary += MulDiv(alpha, 0xff - w->m_style.get().alpha[0], 0xff);
1539 COLORREF primary = revcolor(w->m_style.get().colors[0]) | ((0xff-aprimary)<<24);
1540 DWORD asecondary = w->m_style.get().alpha[1];
1541 if(alpha > 0) asecondary += MulDiv(alpha, 0xff - w->m_style.get().alpha[1], 0xff);
1542 COLORREF secondary = revcolor(w->m_style.get().colors[1]) | ((0xff-asecondary)<<24);
1543 DWORD sw[6] = {primary, 0, secondary};
1544 // karaoke
1545 double t;
1546 if(w->m_ktype == 0 || w->m_ktype == 2)
1548 t = time < w->m_kstart ? 0 : 1;
1550 else if(w->m_ktype == 1)
1552 if(time < w->m_kstart) t = 0;
1553 else if(time < w->m_kend)
1555 t = 1.0 * (time - w->m_kstart) / (w->m_kend - w->m_kstart);
1556 double angle = fmod(w->m_style.get().fontAngleZ, 360.0);
1557 if(angle > 90 && angle < 270)
1559 t = 1-t;
1560 COLORREF tmp = sw[0];
1561 sw[0] = sw[2];
1562 sw[2] = tmp;
1565 else t = 1.0;
1567 if(t >= 1)
1569 sw[1] = 0xffffffff;
1571 sw[3] = (int)(w->m_style.get().outlineWidthX + t*w->m_width) >> 3;
1572 sw[4] = sw[2];
1573 sw[5] = 0x00ffffff;
1574 //xy
1575 if(spd.type == MSP_XY_AUYV)
1577 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1578 sw[2] =rgb2yuv(sw[2], XY_AUYV);
1579 sw[4] =rgb2yuv(sw[4], XY_AUYV);
1581 else if(spd.type == MSP_AYUV || spd.type == MSP_AYUV_PLANAR)
1583 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1584 sw[2] =rgb2yuv(sw[2], XY_AYUV);
1585 sw[4] =rgb2yuv(sw[4], XY_AYUV);
1587 outputItem.body.reset(
1588 CRenderedTextSubtitle::CreateDrawItem(spd, bodyOverlay.overlay, clipRect, clipper, bodyPos.x, bodyPos.y, sw, true, false)
1590 bbox |= CRenderedTextSubtitle::DryDraw(spd, *outputItem.body);
1592 p.x += w->m_width;
1594 return(bbox);
1597 void CLine::AddWord2Tail( SharedPtrCWord words )
1599 __super::AddTail(words);
1602 bool CLine::IsEmpty()
1604 return __super::IsEmpty();
1607 int CLine::GetWordCount()
1609 return GetCount();
1612 // CSubtitle
1614 CSubtitle::CSubtitle()
1616 memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
1617 m_clipInverse = false;
1618 m_scalex = m_scaley = 1;
1619 m_fAnimated2 = false;
1622 CSubtitle::~CSubtitle()
1624 Empty();
1627 void CSubtitle::Empty()
1629 POSITION pos = GetHeadPosition();
1630 while(pos) delete GetNext(pos);
1631 // pos = m_words.GetHeadPosition();
1632 // while(pos) delete m_words.GetNext(pos);
1633 for(int i = 0; i < EF_NUMBEROFEFFECTS; i++) {if(m_effects[i]) delete m_effects[i];}
1634 memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
1637 int CSubtitle::GetFullWidth()
1639 int width = 0;
1640 POSITION pos = m_words.GetHeadPosition();
1641 while(pos) width += m_words.GetNext(pos)->m_width;
1642 return(width);
1645 int CSubtitle::GetFullLineWidth(POSITION pos)
1647 int width = 0;
1648 while(pos)
1650 SharedPtrCWord w = m_words.GetNext(pos);
1651 if(w->m_fLineBreak) break;
1652 width += w->m_width;
1654 return(width);
1657 int CSubtitle::GetWrapWidth(POSITION pos, int maxwidth)
1659 if(m_wrapStyle == 0 || m_wrapStyle == 3)
1661 if(maxwidth > 0)
1663 // int fullwidth = GetFullWidth();
1664 int fullwidth = GetFullLineWidth(pos);
1665 int minwidth = fullwidth / ((abs(fullwidth) / maxwidth) + 1);
1666 int width = 0, wordwidth = 0;
1667 while(pos && width < minwidth)
1669 SharedPtrCWord w = m_words.GetNext(pos);
1670 wordwidth = w->m_width;
1671 if(abs(width + wordwidth) < abs(maxwidth)) width += wordwidth;
1673 maxwidth = width;
1674 if(m_wrapStyle == 3 && pos) maxwidth -= wordwidth;
1677 else if(m_wrapStyle == 1)
1679 // maxwidth = maxwidth;
1681 else if(m_wrapStyle == 2)
1683 maxwidth = INT_MAX;
1685 return(maxwidth);
1688 CLine* CSubtitle::GetNextLine(POSITION& pos, int maxwidth)
1690 if(pos == NULL) return(NULL);
1691 CLine* ret = new CLine();
1692 if(!ret) return(NULL);
1693 ret->m_width = ret->m_ascent = ret->m_descent = ret->m_borderX = ret->m_borderY = 0;
1694 maxwidth = GetWrapWidth(pos, maxwidth);
1695 bool fEmptyLine = true;
1696 while(pos)
1698 SharedPtrCWord w = m_words.GetNext(pos);
1699 if(ret->m_ascent < w->m_ascent) ret->m_ascent = w->m_ascent;
1700 if(ret->m_descent < w->m_descent) ret->m_descent = w->m_descent;
1701 if(ret->m_borderX < w->m_style.get().outlineWidthX) ret->m_borderX = (int)(w->m_style.get().outlineWidthX+0.5);
1702 if(ret->m_borderY < w->m_style.get().outlineWidthY) ret->m_borderY = (int)(w->m_style.get().outlineWidthY+0.5);
1703 if(w->m_fLineBreak)
1705 if(fEmptyLine) {ret->m_ascent /= 2; ret->m_descent /= 2; ret->m_borderX = ret->m_borderY = 0;}
1706 ret->Compact();
1707 return(ret);
1709 fEmptyLine = false;
1710 bool fWSC = w->m_fWhiteSpaceChar;
1711 int width = w->m_width;
1712 POSITION pos2 = pos;
1713 while(pos2)
1715 if(m_words.GetAt(pos2)->m_fWhiteSpaceChar != fWSC
1716 || m_words.GetAt(pos2)->m_fLineBreak) break;
1717 SharedPtrCWord w2 = m_words.GetNext(pos2);
1718 width += w2->m_width;
1720 if((ret->m_width += width) <= maxwidth || ret->IsEmpty())
1722 ret->AddWord2Tail(w);
1723 while(pos != pos2)
1725 ret->AddWord2Tail(m_words.GetNext(pos));
1727 pos = pos2;
1729 else
1731 if(pos) m_words.GetPrev(pos);
1732 else pos = m_words.GetTailPosition();
1733 ret->m_width -= width;
1734 break;
1737 ret->Compact();
1738 return(ret);
1741 void CSubtitle::CreateClippers(CSize size)
1743 size.cx >>= 3;
1744 size.cy >>= 3;
1745 if(m_effects[EF_BANNER] && m_effects[EF_BANNER]->param[2])
1747 int w = size.cx, h = size.cy;
1748 if(!m_pClipper)
1750 CStringW str;
1751 str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
1752 m_pClipper.reset( new CClipper(str, size, 1, 1, false) );
1753 if(!m_pClipper) return;
1755 m_pClipper->SetEffect( *m_effects[EF_BANNER], EF_BANNER );
1757 else if(m_effects[EF_SCROLL] && m_effects[EF_SCROLL]->param[4])
1759 int height = m_effects[EF_SCROLL]->param[4];
1760 int w = size.cx, h = size.cy;
1761 if(!m_pClipper)
1763 CStringW str;
1764 str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
1765 m_pClipper.reset( new CClipper(str, size, 1, 1, false) );
1766 if(!m_pClipper) return;
1768 m_pClipper->SetEffect(*m_effects[EF_SCROLL], EF_SCROLL);
1772 void CSubtitle::MakeLines(CSize size, CRect marginRect)
1774 CSize spaceNeeded(0, 0);
1775 bool fFirstLine = true;
1776 m_topborder = m_bottomborder = 0;
1777 CLine* l = NULL;
1778 POSITION pos = m_words.GetHeadPosition();
1779 while(pos)
1781 l = GetNextLine(pos, size.cx - marginRect.left - marginRect.right);
1782 if(!l) break;
1783 if(fFirstLine) {m_topborder = l->m_borderY; fFirstLine = false;}
1784 spaceNeeded.cx = max(l->m_width+l->m_borderX, spaceNeeded.cx);
1785 spaceNeeded.cy += l->m_ascent + l->m_descent;
1786 AddTail(l);
1788 if(l) m_bottomborder = l->m_borderY;
1789 m_rect = CRect(
1790 CPoint((m_scrAlignment%3) == 1 ? marginRect.left
1791 : (m_scrAlignment%3) == 2 ? (marginRect.left + (size.cx - marginRect.right) - spaceNeeded.cx + 1) / 2
1792 : (size.cx - marginRect.right - spaceNeeded.cx),
1793 m_scrAlignment <= 3 ? (size.cy - marginRect.bottom - spaceNeeded.cy)
1794 : m_scrAlignment <= 6 ? (marginRect.top + (size.cy - marginRect.bottom) - spaceNeeded.cy + 1) / 2
1795 : marginRect.top),
1796 spaceNeeded);
1799 POSITION CSubtitle::GetHeadLinePosition()
1801 return __super::GetHeadPosition();
1804 CLine* CSubtitle::GetNextLine( POSITION& pos )
1806 return __super::GetNext(pos);
1809 // CScreenLayoutAllocator
1811 void CScreenLayoutAllocator::Empty()
1813 m_subrects.RemoveAll();
1816 void CScreenLayoutAllocator::AdvanceToSegment(int segment, const CAtlArray<int>& sa)
1818 POSITION pos = m_subrects.GetHeadPosition();
1819 while(pos)
1821 POSITION prev = pos;
1822 SubRect& sr = m_subrects.GetNext(pos);
1823 bool fFound = false;
1824 if(abs(sr.segment - segment) <= 1) // using abs() makes it possible to play the subs backwards, too :)
1826 for(size_t i = 0; i < sa.GetCount() && !fFound; i++)
1828 if(sa[i] == sr.entry)
1830 sr.segment = segment;
1831 fFound = true;
1835 if(!fFound) m_subrects.RemoveAt(prev);
1839 CRect CScreenLayoutAllocator::AllocRect(CSubtitle* s, int segment, int entry, int layer, int collisions)
1841 // TODO: handle collisions == 1 (reversed collisions)
1842 POSITION pos = m_subrects.GetHeadPosition();
1843 while(pos)
1845 SubRect& sr = m_subrects.GetNext(pos);
1846 if(sr.segment == segment && sr.entry == entry)
1848 return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
1851 CRect r = s->m_rect + CRect(0, s->m_topborder, 0, s->m_bottomborder);
1852 bool fSearchDown = s->m_scrAlignment > 3;
1853 bool fOK;
1856 fOK = true;
1857 pos = m_subrects.GetHeadPosition();
1858 while(pos)
1860 SubRect& sr = m_subrects.GetNext(pos);
1861 if(layer == sr.layer && !(r & sr.r).IsRectEmpty())
1863 if(fSearchDown)
1865 r.bottom = sr.r.bottom + r.Height();
1866 r.top = sr.r.bottom;
1868 else
1870 r.top = sr.r.top - r.Height();
1871 r.bottom = sr.r.top;
1873 fOK = false;
1877 while(!fOK);
1878 SubRect sr;
1879 sr.r = r;
1880 sr.segment = segment;
1881 sr.entry = entry;
1882 sr.layer = layer;
1883 m_subrects.AddTail(sr);
1884 return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
1887 // CRenderedTextSubtitle
1889 CAtlMap<CStringW, CRenderedTextSubtitle::AssCmdType, CStringElementTraits<CStringW>> CRenderedTextSubtitle::m_cmdMap;
1891 CRenderedTextSubtitle::CRenderedTextSubtitle(CCritSec* pLock)
1892 : CSubPicProviderImpl(pLock)
1894 if( m_cmdMap.IsEmpty() )
1896 InitCmdMap();
1898 m_size = CSize(0, 0);
1899 if(g_hDC_refcnt == 0)
1901 g_hDC = CreateCompatibleDC(NULL);
1902 SetBkMode(g_hDC, TRANSPARENT);
1903 SetTextColor(g_hDC, 0xffffff);
1904 SetMapMode(g_hDC, MM_TEXT);
1906 g_hDC_refcnt++;
1909 CRenderedTextSubtitle::~CRenderedTextSubtitle()
1911 Deinit();
1912 g_hDC_refcnt--;
1913 if(g_hDC_refcnt == 0) DeleteDC(g_hDC);
1916 void CRenderedTextSubtitle::InitCmdMap()
1918 if( m_cmdMap.IsEmpty() )
1920 m_cmdMap.SetAt(L"1c", CMD_1c);
1921 m_cmdMap.SetAt(L"2c", CMD_2c);
1922 m_cmdMap.SetAt(L"3c", CMD_3c);
1923 m_cmdMap.SetAt(L"4c", CMD_4c);
1924 m_cmdMap.SetAt(L"1a", CMD_1a);
1925 m_cmdMap.SetAt(L"2a", CMD_2a);
1926 m_cmdMap.SetAt(L"3a", CMD_3a);
1927 m_cmdMap.SetAt(L"4a", CMD_4a);
1928 m_cmdMap.SetAt(L"alpha", CMD_alpha);
1929 m_cmdMap.SetAt(L"an", CMD_an);
1930 m_cmdMap.SetAt(L"a", CMD_a);
1931 m_cmdMap.SetAt(L"blur", CMD_blur);
1932 m_cmdMap.SetAt(L"bord", CMD_bord);
1933 m_cmdMap.SetAt(L"be", CMD_be);
1934 m_cmdMap.SetAt(L"b", CMD_b);
1935 m_cmdMap.SetAt(L"clip", CMD_clip);
1936 m_cmdMap.SetAt(L"iclip", CMD_iclip);
1937 m_cmdMap.SetAt(L"c", CMD_c);
1938 m_cmdMap.SetAt(L"fade", CMD_fade);
1939 m_cmdMap.SetAt(L"fad", CMD_fad);
1940 m_cmdMap.SetAt(L"fax", CMD_fax);
1941 m_cmdMap.SetAt(L"fay", CMD_fay);
1942 m_cmdMap.SetAt(L"fe", CMD_fe);
1943 m_cmdMap.SetAt(L"fn", CMD_fn);
1944 m_cmdMap.SetAt(L"frx", CMD_frx);
1945 m_cmdMap.SetAt(L"fry", CMD_fry);
1946 m_cmdMap.SetAt(L"frz", CMD_frz);
1947 m_cmdMap.SetAt(L"fr", CMD_fr);
1948 m_cmdMap.SetAt(L"fscx", CMD_fscx);
1949 m_cmdMap.SetAt(L"fscy", CMD_fscy);
1950 m_cmdMap.SetAt(L"fsc", CMD_fsc);
1951 m_cmdMap.SetAt(L"fsp", CMD_fsp);
1952 m_cmdMap.SetAt(L"fs", CMD_fs);
1953 m_cmdMap.SetAt(L"i", CMD_i);
1954 m_cmdMap.SetAt(L"kt", CMD_kt);
1955 m_cmdMap.SetAt(L"kf", CMD_kf);
1956 m_cmdMap.SetAt(L"K", CMD_K);
1957 m_cmdMap.SetAt(L"ko", CMD_ko);
1958 m_cmdMap.SetAt(L"k", CMD_k);
1959 m_cmdMap.SetAt(L"move", CMD_move);
1960 m_cmdMap.SetAt(L"org", CMD_org);
1961 m_cmdMap.SetAt(L"pbo", CMD_pbo);
1962 m_cmdMap.SetAt(L"pos", CMD_pos);
1963 m_cmdMap.SetAt(L"p", CMD_p);
1964 m_cmdMap.SetAt(L"q", CMD_q);
1965 m_cmdMap.SetAt(L"r", CMD_r);
1966 m_cmdMap.SetAt(L"shad", CMD_shad);
1967 m_cmdMap.SetAt(L"s", CMD_s);
1968 m_cmdMap.SetAt(L"t", CMD_t);
1969 m_cmdMap.SetAt(L"u", CMD_u);
1970 m_cmdMap.SetAt(L"xbord", CMD_xbord);
1971 m_cmdMap.SetAt(L"xshad", CMD_xshad);
1972 m_cmdMap.SetAt(L"ybord", CMD_ybord);
1973 m_cmdMap.SetAt(L"yshad", CMD_yshad);
1977 void CRenderedTextSubtitle::Copy(CSimpleTextSubtitle& sts)
1979 __super::Copy(sts);
1980 m_size = CSize(0, 0);
1981 if(CRenderedTextSubtitle* pRTS = dynamic_cast<CRenderedTextSubtitle*>(&sts))
1983 m_size = pRTS->m_size;
1987 void CRenderedTextSubtitle::Empty()
1989 Deinit();
1990 __super::Empty();
1993 void CRenderedTextSubtitle::OnChanged()
1995 __super::OnChanged();
1996 POSITION pos = m_subtitleCache.GetStartPosition();
1997 while(pos)
1999 int i;
2000 CSubtitle* s;
2001 m_subtitleCache.GetNextAssoc(pos, i, s);
2002 delete s;
2004 m_subtitleCache.RemoveAll();
2005 m_sla.Empty();
2008 bool CRenderedTextSubtitle::Init(CSize size, CRect vidrect)
2010 Deinit();
2011 m_size = CSize(size.cx*8, size.cy*8);
2012 m_vidrect = CRect(vidrect.left*8, vidrect.top*8, vidrect.right*8, vidrect.bottom*8);
2013 m_sla.Empty();
2014 return(true);
2017 void CRenderedTextSubtitle::Deinit()
2019 POSITION pos = m_subtitleCache.GetStartPosition();
2020 while(pos)
2022 int i;
2023 CSubtitle* s;
2024 m_subtitleCache.GetNextAssoc(pos, i, s);
2025 delete s;
2027 m_subtitleCache.RemoveAll();
2028 m_sla.Empty();
2029 m_size = CSize(0, 0);
2030 m_vidrect.SetRectEmpty();
2032 CacheManager::GetCWordMruCache()->RemoveAll();
2033 CacheManager::GetPathDataMruCache()->RemoveAll();
2034 CacheManager::GetScanLineData2MruCache()->RemoveAll();
2035 CacheManager::GetOverlayNoBlurMruCache()->RemoveAll();
2036 CacheManager::GetOverlayMruCache()->RemoveAll();
2037 CacheManager::GetAssTagListMruCache()->RemoveAll();
2038 CacheManager::GetSubpixelVarianceCache()->RemoveAll();
2039 CacheManager::GetTextInfoCache()->RemoveAll();
2042 void CRenderedTextSubtitle::ParseEffect(CSubtitle* sub, const CStringW& str)
2044 CStringW::PCXSTR str_start = str.GetString();
2045 CStringW::PCXSTR str_end = str_start + str.GetLength();
2046 str_start = SkipWhiteSpaceLeft(str_start, str_end);
2048 if(!sub || *str_start==0)
2049 return;
2051 str_end = FastSkipWhiteSpaceRight(str_start, str_end);
2053 const WCHAR* s = FindChar(str_start, str_end, L';');
2054 if(*s==L';') {
2055 s++;
2058 const CStringW effect(str_start, s-str_start);
2059 if(!effect.CompareNoCase( L"Banner;" ) )
2061 int delay, lefttoright = 0, fadeawaywidth = 0;
2062 if(swscanf(s, L"%d;%d;%d", &delay, &lefttoright, &fadeawaywidth) < 1) return;
2063 Effect* e = new Effect;
2064 if(!e) return;
2065 sub->m_effects[e->type = EF_BANNER] = e;
2066 e->param[0] = (int)(max(1.0*delay/sub->m_scalex, 1));
2067 e->param[1] = lefttoright;
2068 e->param[2] = (int)(sub->m_scalex*fadeawaywidth);
2069 sub->m_wrapStyle = 2;
2071 else if(!effect.CompareNoCase(L"Scroll up;") || !effect.CompareNoCase(L"Scroll down;"))
2073 int top, bottom, delay, fadeawayheight = 0;
2074 if(swscanf(s, L"%d;%d;%d;%d", &top, &bottom, &delay, &fadeawayheight) < 3) return;
2075 if(top > bottom) {int tmp = top; top = bottom; bottom = tmp;}
2076 Effect* e = new Effect;
2077 if(!e) return;
2078 sub->m_effects[e->type = EF_SCROLL] = e;
2079 e->param[0] = (int)(sub->m_scaley*top*8);
2080 e->param[1] = (int)(sub->m_scaley*bottom*8);
2081 e->param[2] = (int)(max(1.0*delay/sub->m_scaley, 1));
2082 e->param[3] = (effect.GetLength() == 12);
2083 e->param[4] = (int)(sub->m_scaley*fadeawayheight);
2087 void CRenderedTextSubtitle::ParseString(CSubtitle* sub, CStringW str, const FwSTSStyle& style)
2089 if(!sub) return;
2090 str.Replace(L"\\N", L"\n");
2091 str.Replace(L"\\n", (sub->m_wrapStyle < 2 || sub->m_wrapStyle == 3) ? L" " : L"\n");
2092 str.Replace(L"\\h", L"\x00A0");
2093 for(int ite = 0, j = 0, len = str.GetLength(); j <= len; j++)
2095 WCHAR c = str[j];
2096 if(c != L'\n' && c != L' ' && c != 0)
2097 continue;
2098 if(ite < j)
2100 if(PCWord tmp_ptr = new CText(style, str.Mid(ite, j-ite), m_ktype, m_kstart, m_kend))
2102 SharedPtrCWord w(tmp_ptr);
2103 sub->m_words.AddTail(w);
2105 else
2107 ///TODO: overflow handling
2109 m_kstart = m_kend;
2111 if(c == L'\n')
2113 if(PCWord tmp_ptr = new CText(style, CStringW(), m_ktype, m_kstart, m_kend))
2115 SharedPtrCWord w(tmp_ptr);
2116 sub->m_words.AddTail(w);
2118 else
2120 ///TODO: overflow handling
2122 m_kstart = m_kend;
2124 else if(c == L' ')
2126 if(PCWord tmp_ptr = new CText(style, CStringW(c), m_ktype, m_kstart, m_kend))
2128 SharedPtrCWord w(tmp_ptr);
2129 sub->m_words.AddTail(w);
2131 else
2133 ///TODO: overflow handling
2135 m_kstart = m_kend;
2137 ite = j+1;
2139 return;
2142 void CRenderedTextSubtitle::ParsePolygon(CSubtitle* sub, const CStringW& str, const FwSTSStyle& style)
2144 if(!sub || !str.GetLength() || !m_nPolygon) return;
2146 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))
2148 SharedPtrCWord w(tmp_ptr);
2149 ///Todo: fix me
2150 //if( PCWord w_cache = m_wordCache.lookup(*w) )
2152 // sub->m_words.AddTail(w_cache);
2153 // delete w;
2155 //else
2157 sub->m_words.AddTail(w);
2159 m_kstart = m_kend;
2163 bool CRenderedTextSubtitle::ParseSSATag( AssTagList *assTags, const CStringW& str )
2165 if(!assTags) return(false);
2166 int nTags = 0, nUnrecognizedTags = 0;
2167 for(int i = 0, j; (j = str.Find(L'\\', i)) >= 0; i = j)
2169 POSITION pos = assTags->AddTail();
2170 AssTag& assTag = assTags->GetAt(pos);
2171 assTag.cmdType = CMD_COUNT;
2173 j++;
2174 CStringW::PCXSTR str_start = str.GetString() + j;
2175 CStringW::PCXSTR pc = str_start;
2176 while( iswspace(*pc) )
2178 pc++;
2180 j += pc-str_start;
2181 str_start = pc;
2182 while( *pc && *pc != L'(' && *pc != L'\\' )
2184 pc++;
2186 j += pc-str_start;
2187 if( pc-str_start>0 )
2189 while( iswspace(*--pc) );
2190 pc++;
2193 const CStringW cmd(str_start, pc-str_start);
2194 if(cmd.IsEmpty()) continue;
2196 CAtlArray<CStringW>& params = assTag.strParams;
2197 if(str[j] == L'(')
2199 j++;
2200 CStringW::PCXSTR str_start = str.GetString() + j;
2201 CStringW::PCXSTR pc = str_start;
2202 while( iswspace(*pc) )
2204 pc++;
2206 j += pc-str_start;
2207 str_start = pc;
2208 while( *pc && *pc != L')' )
2210 pc++;
2212 j += pc-str_start;
2213 if( pc-str_start>0 )
2215 while( iswspace(*--pc) );
2216 pc++;
2219 CStringW::PCXSTR param_start = str_start;
2220 CStringW::PCXSTR param_end = pc;
2221 while( param_start<param_end )
2223 param_start = SkipWhiteSpaceLeft(param_start, param_end);
2225 CStringW::PCXSTR newstart = FindChar(param_start, param_end, L',');
2226 CStringW::PCXSTR newend = FindChar(param_start, param_end, L'\\');
2227 if(newstart > param_start && newstart < newend)
2229 newstart = FastSkipWhiteSpaceRight(param_start, newstart);
2230 CStringW s(param_start, newstart - param_start);
2232 if(!s.IsEmpty()) params.Add(s);
2233 param_start = newstart + 1;
2235 else if(param_start<param_end)
2237 CStringW s(param_start, param_end - param_start);
2239 params.Add(s);
2240 param_start = param_end;
2245 AssCmdType cmd_type = CMD_COUNT;
2246 int cmd_length = min(MAX_CMD_LENGTH, cmd.GetLength());
2247 for( ;cmd_length>=MIN_CMD_LENGTH;cmd_length-- )
2249 if( m_cmdMap.Lookup(cmd.Left(cmd_length), cmd_type) )
2250 break;
2252 if(cmd_length<MIN_CMD_LENGTH)
2253 cmd_type = CMD_COUNT;
2254 switch( cmd_type )
2256 case CMD_fax:
2257 case CMD_fay:
2258 case CMD_fe:
2259 case CMD_fn:
2260 case CMD_frx:
2261 case CMD_fry:
2262 case CMD_frz:
2263 case CMD_fr:
2264 case CMD_fscx:
2265 case CMD_fscy:
2266 case CMD_fsc:
2267 case CMD_fsp:
2268 case CMD_fs:
2269 case CMD_i:
2270 case CMD_kt:
2271 case CMD_kf:
2272 case CMD_K:
2273 case CMD_ko:
2274 case CMD_k:
2275 case CMD_pbo:
2276 case CMD_p:
2277 case CMD_q:
2278 case CMD_r:
2279 case CMD_shad:
2280 case CMD_s:
2281 case CMD_an:
2282 case CMD_a:
2283 case CMD_blur:
2284 case CMD_bord:
2285 case CMD_be:
2286 case CMD_b:
2287 case CMD_u:
2288 case CMD_xbord:
2289 case CMD_xshad:
2290 case CMD_ybord:
2291 case CMD_yshad:
2292 // default:
2293 params.Add(cmd.Mid(cmd_length));
2294 break;
2295 case CMD_c:
2296 case CMD_1c :
2297 case CMD_2c :
2298 case CMD_3c :
2299 case CMD_4c :
2300 case CMD_1a :
2301 case CMD_2a :
2302 case CMD_3a :
2303 case CMD_4a :
2304 case CMD_alpha:
2305 params.Add(cmd.Mid(cmd_length).Trim(L"&H"));
2306 break;
2307 case CMD_clip:
2308 case CMD_iclip:
2309 case CMD_fade:
2310 case CMD_fad:
2311 case CMD_move:
2312 case CMD_org:
2313 case CMD_pos:
2314 break;
2315 case CMD_t:
2316 if (!params.IsEmpty())
2317 ParseSSATag(&assTag.embeded, params[params.GetCount()-1]);
2318 break;
2319 case CMD_COUNT:
2320 nUnrecognizedTags++;
2321 break;
2324 assTag.cmd = cmd;
2325 assTag.cmdType = cmd_type;
2327 nTags++;
2329 return(true);
2332 bool CRenderedTextSubtitle::ParseSSATag( CSubtitle* sub, const AssTagList& assTags, STSStyle& style, const STSStyle& org, bool fAnimate /*= false*/ )
2334 if(!sub) return(false);
2336 POSITION pos = assTags.GetHeadPosition();
2337 while(pos)
2339 const AssTag& assTag = assTags.GetNext(pos);
2340 const CStringW& cmd = assTag.cmd;
2341 AssCmdType cmd_type = assTag.cmdType;
2342 const CAtlArray<CStringW>& params = assTag.strParams;
2344 // TODO: call ParseStyleModifier(cmd, params, ..) and move the rest there
2345 const CStringW& p = params.GetCount() > 0 ? params[0] : CStringW("");
2346 switch ( cmd_type )
2348 case CMD_1c :
2349 case CMD_2c :
2350 case CMD_3c :
2351 case CMD_4c :
2353 int i = cmd[0] - L'1';
2354 DWORD c = wcstol(p, NULL, 16);
2355 style.colors[i] = !p.IsEmpty()
2356 ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
2357 |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
2358 |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
2359 : org.colors[i];
2360 break;
2362 case CMD_1a :
2363 case CMD_2a :
2364 case CMD_3a :
2365 case CMD_4a :
2367 int i = cmd[0] - L'1';
2368 style.alpha[i] = !p.IsEmpty()
2369 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2370 : org.alpha[i];
2371 break;
2373 case CMD_alpha:
2375 for(int i = 0; i < 4; i++)
2377 style.alpha[i] = !p.IsEmpty()
2378 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2379 : org.alpha[i];
2381 break;
2383 case CMD_an:
2385 int n = wcstol(p, NULL, 10);
2386 if(sub->m_scrAlignment < 0)
2387 sub->m_scrAlignment = (n > 0 && n < 10) ? n : org.scrAlignment;
2388 break;
2390 case CMD_a:
2392 int n = wcstol(p, NULL, 10);
2393 if(sub->m_scrAlignment < 0)
2394 sub->m_scrAlignment = (n > 0 && n < 12) ? ((((n-1)&3)+1)+((n&4)?6:0)+((n&8)?3:0)) : org.scrAlignment;
2395 break;
2397 case CMD_blur:
2399 double n = CalcAnimation(wcstod(p, NULL), style.fGaussianBlur, fAnimate);
2400 style.fGaussianBlur = !p.IsEmpty()
2401 ? (n < 0 ? 0 : n)
2402 : org.fGaussianBlur;
2403 break;
2405 case CMD_bord:
2407 double dst = wcstod(p, NULL);
2408 double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate);
2409 style.outlineWidthX = !p.IsEmpty()
2410 ? (nx < 0 ? 0 : nx)
2411 : org.outlineWidthX;
2412 double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate);
2413 style.outlineWidthY = !p.IsEmpty()
2414 ? (ny < 0 ? 0 : ny)
2415 : org.outlineWidthY;
2416 break;
2418 case CMD_be:
2420 int n = (int)(CalcAnimation(wcstol(p, NULL, 10), style.fBlur, fAnimate)+0.5);
2421 style.fBlur = !p.IsEmpty()
2423 : org.fBlur;
2424 break;
2426 case CMD_b:
2428 int n = wcstol(p, NULL, 10);
2429 style.fontWeight = !p.IsEmpty()
2430 ? (n == 0 ? FW_NORMAL : n == 1 ? FW_BOLD : n >= 100 ? n : org.fontWeight)
2431 : org.fontWeight;
2432 break;
2434 case CMD_clip:
2435 case CMD_iclip:
2437 bool invert = (cmd_type == CMD_iclip);
2438 if(params.GetCount() == 1 && !sub->m_pClipper)
2440 sub->m_pClipper.reset( new CClipper(params[0], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex, sub->m_scaley, invert) );
2442 else if(params.GetCount() == 2 && !sub->m_pClipper)
2444 int scale = max(wcstol(p, NULL, 10), 1);
2445 sub->m_pClipper.reset( 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) );
2447 else if(params.GetCount() == 4)
2449 CRect r;
2450 sub->m_clipInverse = invert;
2451 r.SetRect(
2452 wcstol(params[0], NULL, 10),
2453 wcstol(params[1], NULL, 10),
2454 wcstol(params[2], NULL, 10),
2455 wcstol(params[3], NULL, 10));
2456 CPoint o(0, 0);
2457 if(sub->m_relativeTo == 1) // TODO: this should also apply to the other two clippings above
2459 o.x = m_vidrect.left>>3;
2460 o.y = m_vidrect.top>>3;
2462 sub->m_clip.SetRect(
2463 (int)CalcAnimation(sub->m_scalex*r.left + o.x, sub->m_clip.left, fAnimate),
2464 (int)CalcAnimation(sub->m_scaley*r.top + o.y, sub->m_clip.top, fAnimate),
2465 (int)CalcAnimation(sub->m_scalex*r.right + o.x, sub->m_clip.right, fAnimate),
2466 (int)CalcAnimation(sub->m_scaley*r.bottom + o.y, sub->m_clip.bottom, fAnimate));
2468 break;
2470 case CMD_c:
2472 DWORD c = wcstol(p, NULL, 16);
2473 style.colors[0] = !p.IsEmpty()
2474 ? (((int)CalcAnimation(c&0xff, style.colors[0]&0xff, fAnimate))&0xff
2475 |((int)CalcAnimation(c&0xff00, style.colors[0]&0xff00, fAnimate))&0xff00
2476 |((int)CalcAnimation(c&0xff0000, style.colors[0]&0xff0000, fAnimate))&0xff0000)
2477 : org.colors[0];
2478 break;
2480 case CMD_fade:
2481 case CMD_fad:
2483 sub->m_fAnimated2 = true;
2484 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])
2486 if(Effect* e = new Effect)
2488 for(int i = 0; i < 3; i++)
2489 e->param[i] = wcstol(params[i], NULL, 10);
2490 for(int i = 0; i < 4; i++)
2491 e->t[i] = wcstol(params[3+i], NULL, 10);
2492 sub->m_effects[EF_FADE] = e;
2495 else if(params.GetCount() == 2 && !sub->m_effects[EF_FADE]) // {\fad(t1=t[1], t2=t[2])
2497 if(Effect* e = new Effect)
2499 e->param[0] = e->param[2] = 0xff;
2500 e->param[1] = 0x00;
2501 for(int i = 1; i < 3; i++)
2502 e->t[i] = wcstol(params[i-1], NULL, 10);
2503 e->t[0] = e->t[3] = -1; // will be substituted with "start" and "end"
2504 sub->m_effects[EF_FADE] = e;
2507 break;
2509 case CMD_fax:
2511 style.fontShiftX = !p.IsEmpty()
2512 ? CalcAnimation(wcstod(p, NULL), style.fontShiftX, fAnimate)
2513 : org.fontShiftX;
2514 break;
2516 case CMD_fay:
2518 style.fontShiftY = !p.IsEmpty()
2519 ? CalcAnimation(wcstod(p, NULL), style.fontShiftY, fAnimate)
2520 : org.fontShiftY;
2521 break;
2523 case CMD_fe:
2525 int n = wcstol(p, NULL, 10);
2526 style.charSet = !p.IsEmpty()
2528 : org.charSet;
2529 break;
2531 case CMD_fn:
2533 if(!p.IsEmpty() && p != L'0')
2534 style.fontName = CString(p).Trim();
2535 else
2536 style.fontName = org.fontName;
2537 break;
2539 case CMD_frx:
2541 style.fontAngleX = !p.IsEmpty()
2542 ? CalcAnimation(wcstod(p, NULL), style.fontAngleX, fAnimate)
2543 : org.fontAngleX;
2544 break;
2546 case CMD_fry:
2548 style.fontAngleY = !p.IsEmpty()
2549 ? CalcAnimation(wcstod(p, NULL), style.fontAngleY, fAnimate)
2550 : org.fontAngleY;
2551 break;
2553 case CMD_frz:
2554 case CMD_fr:
2556 style.fontAngleZ = !p.IsEmpty()
2557 ? CalcAnimation(wcstod(p, NULL), style.fontAngleZ, fAnimate)
2558 : org.fontAngleZ;
2559 break;
2561 case CMD_fscx:
2563 double n = CalcAnimation(wcstol(p, NULL, 10), style.fontScaleX, fAnimate);
2564 style.fontScaleX = !p.IsEmpty()
2565 ? ((n < 0) ? 0 : n)
2566 : org.fontScaleX;
2567 break;
2569 case CMD_fscy:
2571 double n = CalcAnimation(wcstol(p, NULL, 10), style.fontScaleY, fAnimate);
2572 style.fontScaleY = !p.IsEmpty()
2573 ? ((n < 0) ? 0 : n)
2574 : org.fontScaleY;
2575 break;
2577 case CMD_fsc:
2579 style.fontScaleX = org.fontScaleX;
2580 style.fontScaleY = org.fontScaleY;
2581 break;
2583 case CMD_fsp:
2585 style.fontSpacing = !p.IsEmpty()
2586 ? CalcAnimation(wcstod(p, NULL), style.fontSpacing, fAnimate)
2587 : org.fontSpacing;
2588 break;
2590 case CMD_fs:
2592 if(!p.IsEmpty())
2594 if(p[0] == L'-' || p[0] == L'+')
2596 double n = CalcAnimation(style.fontSize + style.fontSize*wcstol(p, NULL, 10)/10, style.fontSize, fAnimate);
2597 style.fontSize = (n > 0) ? n : org.fontSize;
2599 else
2601 double n = CalcAnimation(wcstol(p, NULL, 10), style.fontSize, fAnimate);
2602 style.fontSize = (n > 0) ? n : org.fontSize;
2605 else
2607 style.fontSize = org.fontSize;
2609 break;
2611 case CMD_i:
2613 int n = wcstol(p, NULL, 10);
2614 style.fItalic = !p.IsEmpty()
2615 ? (n == 0 ? false : n == 1 ? true : org.fItalic)
2616 : org.fItalic;
2617 break;
2619 case CMD_kt:
2621 m_kstart = !p.IsEmpty()
2622 ? wcstol(p, NULL, 10)*10
2623 : 0;
2624 m_kend = m_kstart;
2625 break;
2626 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2628 case CMD_kf:
2629 case CMD_K:
2631 m_ktype = 1;
2632 m_kstart = m_kend;
2633 m_kend += !p.IsEmpty()
2634 ? wcstol(p, NULL, 10)*10
2635 : 1000;
2636 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2637 break;
2639 case CMD_ko:
2641 m_ktype = 2;
2642 m_kstart = m_kend;
2643 m_kend += !p.IsEmpty()
2644 ? wcstol(p, NULL, 10)*10
2645 : 1000;
2646 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2647 break;
2649 case CMD_k:
2651 m_ktype = 0;
2652 m_kstart = m_kend;
2653 m_kend += !p.IsEmpty()
2654 ? wcstol(p, NULL, 10)*10
2655 : 1000;
2656 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2657 break;
2659 case CMD_move: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3][, t1=t[0], t2=t[1]])}
2661 if((params.GetCount() == 4 || params.GetCount() == 6) && !sub->m_effects[EF_MOVE])
2663 if(Effect* e = new Effect)
2665 e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2666 e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2667 e->param[2] = (int)(sub->m_scalex*wcstod(params[2], NULL)*8);
2668 e->param[3] = (int)(sub->m_scaley*wcstod(params[3], NULL)*8);
2669 e->t[0] = e->t[1] = -1;
2670 if(params.GetCount() == 6)
2672 for(int i = 0; i < 2; i++)
2673 e->t[i] = wcstol(params[4+i], NULL, 10);
2675 sub->m_effects[EF_MOVE] = e;
2676 sub->m_fAnimated2 = true;
2679 break;
2681 case CMD_org: // {\org(x=param[0], y=param[1])}
2683 if(params.GetCount() == 2 && !sub->m_effects[EF_ORG])
2685 if(Effect* e = new Effect)
2687 e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2688 e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2689 sub->m_effects[EF_ORG] = e;
2692 break;
2694 case CMD_pbo:
2696 m_polygonBaselineOffset = wcstol(p, NULL, 10);
2697 break;
2699 case CMD_pos:
2701 if(params.GetCount() == 2 && !sub->m_effects[EF_MOVE])
2703 if(Effect* e = new Effect)
2705 e->param[0] = e->param[2] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2706 e->param[1] = e->param[3] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2707 e->t[0] = e->t[1] = 0;
2708 sub->m_effects[EF_MOVE] = e;
2711 break;
2713 case CMD_p:
2715 int n = wcstol(p, NULL, 10);
2716 m_nPolygon = (n <= 0 ? 0 : n);
2717 break;
2719 case CMD_q:
2721 int n = wcstol(p, NULL, 10);
2722 sub->m_wrapStyle = !p.IsEmpty() && (0 <= n && n <= 3)
2724 : m_defaultWrapStyle;
2725 break;
2727 case CMD_r:
2729 STSStyle* val;
2730 style = (!p.IsEmpty() && m_styles.Lookup(WToT(p), val) && val) ? *val : org;
2731 break;
2733 case CMD_shad:
2735 double dst = wcstod(p, NULL);
2736 double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate);
2737 style.shadowDepthX = !p.IsEmpty()
2738 ? (nx < 0 ? 0 : nx)
2739 : org.shadowDepthX;
2740 double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate);
2741 style.shadowDepthY = !p.IsEmpty()
2742 ? (ny < 0 ? 0 : ny)
2743 : org.shadowDepthY;
2744 break;
2746 case CMD_s:
2748 int n = wcstol(p, NULL, 10);
2749 style.fStrikeOut = !p.IsEmpty()
2750 ? (n == 0 ? false : n == 1 ? true : org.fStrikeOut)
2751 : org.fStrikeOut;
2752 break;
2754 case CMD_t: // \t([<t1>,<t2>,][<accel>,]<style modifiers>)
2756 CStringW param;
2757 m_animStart = m_animEnd = 0;
2758 m_animAccel = 1;
2759 if(params.GetCount() == 1)
2761 param = params[0];
2763 else if(params.GetCount() == 2)
2765 m_animAccel = wcstod(params[0], NULL);
2766 param = params[1];
2768 else if(params.GetCount() == 3)
2770 m_animStart = (int)wcstod(params[0], NULL);
2771 m_animEnd = (int)wcstod(params[1], NULL);
2772 param = params[2];
2774 else if(params.GetCount() == 4)
2776 m_animStart = wcstol(params[0], NULL, 10);
2777 m_animEnd = wcstol(params[1], NULL, 10);
2778 m_animAccel = wcstod(params[2], NULL);
2779 param = params[3];
2781 ParseSSATag(sub, assTag.embeded, style, org, true);
2782 sub->m_fAnimated = true;
2783 sub->m_fAnimated2 = true;
2784 break;
2786 case CMD_u:
2788 int n = wcstol(p, NULL, 10);
2789 style.fUnderline = !p.IsEmpty()
2790 ? (n == 0 ? false : n == 1 ? true : org.fUnderline)
2791 : org.fUnderline;
2792 break;
2794 case CMD_xbord:
2796 double dst = wcstod(p, NULL);
2797 double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate);
2798 style.outlineWidthX = !p.IsEmpty()
2799 ? (nx < 0 ? 0 : nx)
2800 : org.outlineWidthX;
2801 break;
2803 case CMD_xshad:
2805 double dst = wcstod(p, NULL);
2806 double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate);
2807 style.shadowDepthX = !p.IsEmpty()
2808 ? nx
2809 : org.shadowDepthX;
2810 break;
2812 case CMD_ybord:
2814 double dst = wcstod(p, NULL);
2815 double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate);
2816 style.outlineWidthY = !p.IsEmpty()
2817 ? (ny < 0 ? 0 : ny)
2818 : org.outlineWidthY;
2819 break;
2821 case CMD_yshad:
2823 double dst = wcstod(p, NULL);
2824 double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate);
2825 style.shadowDepthY = !p.IsEmpty()
2826 ? ny
2827 : org.shadowDepthY;
2828 break;
2830 default:
2831 break;
2834 return(true);
2837 bool CRenderedTextSubtitle::ParseSSATag(CSubtitle* sub, const CStringW& str, STSStyle& style, const STSStyle& org, bool fAnimate)
2839 if(!sub) return(false);
2841 SharedPtrConstAssTagList assTags;
2842 AssTagListMruCache *ass_tag_cache = CacheManager::GetAssTagListMruCache();
2843 POSITION pos = ass_tag_cache->Lookup(str);
2844 if (pos==NULL)
2846 AssTagList *tmp = new AssTagList();
2847 ParseSSATag(tmp, str);
2848 assTags.reset(tmp);
2849 ass_tag_cache->UpdateCache(str, assTags);
2851 else
2853 assTags = ass_tag_cache->GetAt(pos);
2854 ass_tag_cache->UpdateCache( pos );
2856 return ParseSSATag(sub, *assTags, style, org, fAnimate);
2859 bool CRenderedTextSubtitle::ParseHtmlTag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org)
2861 if(str.Find(L"!--") == 0)
2862 return(true);
2863 bool fClosing = str[0] == L'/';
2864 str.Trim(L" /");
2865 int i = str.Find(L' ');
2866 if(i < 0) i = str.GetLength();
2867 CStringW tag = str.Left(i).MakeLower();
2868 str = str.Mid(i).Trim();
2869 CAtlArray<CStringW> attribs, params;
2870 while((i = str.Find(L'=')) > 0)
2872 attribs.Add(str.Left(i).Trim().MakeLower());
2873 str = str.Mid(i+1);
2874 for(i = 0; _istspace(str[i]); i++);
2875 str = str.Mid(i);
2876 if(str[0] == L'\"') {str = str.Mid(1); i = str.Find(L'\"');}
2877 else i = str.Find(L' ');
2878 if(i < 0) i = str.GetLength();
2879 params.Add(str.Left(i).Trim().MakeLower());
2880 str = str.Mid(i+1);
2882 if(tag == L"text")
2884 else if(tag == L"b" || tag == L"strong")
2885 style.fontWeight = !fClosing ? FW_BOLD : org.fontWeight;
2886 else if(tag == L"i" || tag == L"em")
2887 style.fItalic = !fClosing ? true : org.fItalic;
2888 else if(tag == L"u")
2889 style.fUnderline = !fClosing ? true : org.fUnderline;
2890 else if(tag == L"s" || tag == L"strike" || tag == L"del")
2891 style.fStrikeOut = !fClosing ? true : org.fStrikeOut;
2892 else if(tag == L"font")
2894 if(!fClosing)
2896 for(size_t i = 0; i < attribs.GetCount(); i++)
2898 if(params[i].IsEmpty()) continue;
2899 int nColor = -1;
2900 if(attribs[i] == L"face")
2902 style.fontName = params[i];
2904 else if(attribs[i] == L"size")
2906 if(params[i][0] == L'+')
2907 style.fontSize += wcstol(params[i], NULL, 10);
2908 else if(params[i][0] == L'-')
2909 style.fontSize -= wcstol(params[i], NULL, 10);
2910 else
2911 style.fontSize = wcstol(params[i], NULL, 10);
2913 else if(attribs[i] == L"color")
2915 nColor = 0;
2917 else if(attribs[i] == L"outline-color")
2919 nColor = 2;
2921 else if(attribs[i] == L"outline-level")
2923 style.outlineWidthX = style.outlineWidthY = wcstol(params[i], NULL, 10);
2925 else if(attribs[i] == L"shadow-color")
2927 nColor = 3;
2929 else if(attribs[i] == L"shadow-level")
2931 style.shadowDepthX = style.shadowDepthY = wcstol(params[i], NULL, 10);
2933 if(nColor >= 0 && nColor < 4)
2935 CString key = WToT(params[i]).TrimLeft(L'#');
2936 DWORD val;
2937 if(g_colors.Lookup(key, val))
2938 style.colors[nColor] = val;
2939 else if((style.colors[nColor] = _tcstol(key, NULL, 16)) == 0)
2940 style.colors[nColor] = 0x00ffffff; // default is white
2941 style.colors[nColor] = ((style.colors[nColor]>>16)&0xff)|((style.colors[nColor]&0xff)<<16)|(style.colors[nColor]&0x00ff00);
2945 else
2947 style.fontName = org.fontName;
2948 style.fontSize = org.fontSize;
2949 memcpy(style.colors, org.colors, sizeof(style.colors));
2952 else if(tag == L"k" && attribs.GetCount() == 1 && attribs[0] == L"t")
2954 m_ktype = 1;
2955 m_kstart = m_kend;
2956 m_kend += wcstol(params[0], NULL, 10);
2958 else
2959 return(false);
2960 return(true);
2963 double CRenderedTextSubtitle::CalcAnimation(double dst, double src, bool fAnimate)
2965 int s = m_animStart ? m_animStart : 0;
2966 int e = m_animEnd ? m_animEnd : m_delay;
2967 if(fabs(dst-src) >= 0.0001 && fAnimate)
2969 if(m_time < s) dst = src;
2970 else if(s <= m_time && m_time < e)
2972 double t = pow(1.0 * (m_time - s) / (e - s), m_animAccel);
2973 dst = (1 - t) * src + t * dst;
2975 // else dst = dst;
2977 return(dst);
2980 CSubtitle* CRenderedTextSubtitle::GetSubtitle(int entry)
2982 CSubtitle* sub;
2983 if(m_subtitleCache.Lookup(entry, sub))
2985 if(sub->m_fAnimated) {delete sub; sub = NULL;}
2986 else return(sub);
2988 sub = new CSubtitle();
2989 if(!sub) return(NULL);
2990 CStringW str = GetStrW(entry, true);
2991 STSStyle stss, orgstss;
2992 GetStyle(entry, &stss);
2993 if (stss.fontScaleX == stss.fontScaleY && m_dPARCompensation != 1.0)
2995 switch(m_ePARCompensationType)
2997 case EPCTUpscale:
2998 if (m_dPARCompensation < 1.0)
2999 stss.fontScaleY /= m_dPARCompensation;
3000 else
3001 stss.fontScaleX *= m_dPARCompensation;
3002 break;
3003 case EPCTDownscale:
3004 if (m_dPARCompensation < 1.0)
3005 stss.fontScaleX *= m_dPARCompensation;
3006 else
3007 stss.fontScaleY /= m_dPARCompensation;
3008 break;
3009 case EPCTAccurateSize:
3010 stss.fontScaleX *= m_dPARCompensation;
3011 break;
3014 orgstss = stss;
3015 sub->m_clip.SetRect(0, 0, m_size.cx>>3, m_size.cy>>3);
3016 sub->m_scrAlignment = -stss.scrAlignment;
3017 sub->m_wrapStyle = m_defaultWrapStyle;
3018 sub->m_fAnimated = false;
3019 sub->m_relativeTo = stss.relativeTo;
3020 sub->m_scalex = m_dstScreenSize.cx > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Width() : m_size.cx) / (m_dstScreenSize.cx*8) : 1.0;
3021 sub->m_scaley = m_dstScreenSize.cy > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Height() : m_size.cy) / (m_dstScreenSize.cy*8) : 1.0;
3022 m_animStart = m_animEnd = 0;
3023 m_animAccel = 1;
3024 m_ktype = m_kstart = m_kend = 0;
3025 m_nPolygon = 0;
3026 m_polygonBaselineOffset = 0;
3027 ParseEffect(sub, m_entries.GetAt(entry).effect);
3028 while(!str.IsEmpty())
3030 bool fParsed = false;
3031 int i;
3032 if(str[0] == L'{' && (i = str.Find(L'}')) > 0)
3034 if(fParsed = ParseSSATag(sub, str.Mid(1, i-1), stss, orgstss))
3035 str = str.Mid(i+1);
3037 else if(str[0] == L'<' && (i = str.Find(L'>')) > 0)
3039 if(fParsed = ParseHtmlTag(sub, str.Mid(1, i-1), stss, orgstss))
3040 str = str.Mid(i+1);
3042 if(fParsed)
3044 i = str.FindOneOf(L"{<");
3045 if(i < 0) i = str.GetLength();
3046 if(i == 0) continue;
3048 else
3050 i = str.Mid(1).FindOneOf(L"{<");
3051 if(i < 0) i = str.GetLength()-1;
3052 i++;
3054 STSStyle tmp = stss;
3055 tmp.fontSize = sub->m_scaley*tmp.fontSize*64;
3056 tmp.fontSpacing = sub->m_scalex*tmp.fontSpacing*64;
3057 tmp.outlineWidthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8;
3058 tmp.outlineWidthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8;
3059 tmp.shadowDepthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8;
3060 tmp.shadowDepthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8;
3061 FwSTSStyle fw_tmp(tmp);
3062 if(m_nPolygon)
3064 ParsePolygon(sub, str.Left(i), fw_tmp);
3066 else
3068 ParseString(sub, str.Left(i), fw_tmp);
3070 str = str.Mid(i);
3072 if( sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL] )
3073 sub->m_fAnimated2 = true;
3074 // just a "work-around" solution... in most cases nobody will want to use \org together with moving but without rotating the subs
3075 if(sub->m_effects[EF_ORG] && (sub->m_effects[EF_MOVE] || sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL]))
3076 sub->m_fAnimated = true;
3077 sub->m_scrAlignment = abs(sub->m_scrAlignment);
3078 STSEntry stse = m_entries.GetAt(entry);
3079 CRect marginRect = stse.marginRect;
3080 if(marginRect.left == 0) marginRect.left = orgstss.marginRect.get().left;
3081 if(marginRect.top == 0) marginRect.top = orgstss.marginRect.get().top;
3082 if(marginRect.right == 0) marginRect.right = orgstss.marginRect.get().right;
3083 if(marginRect.bottom == 0) marginRect.bottom = orgstss.marginRect.get().bottom;
3084 marginRect.left = (int)(sub->m_scalex*marginRect.left*8);
3085 marginRect.top = (int)(sub->m_scaley*marginRect.top*8);
3086 marginRect.right = (int)(sub->m_scalex*marginRect.right*8);
3087 marginRect.bottom = (int)(sub->m_scaley*marginRect.bottom*8);
3088 if(stss.relativeTo == 1)
3090 marginRect.left += m_vidrect.left;
3091 marginRect.top += m_vidrect.top;
3092 marginRect.right += m_size.cx - m_vidrect.right;
3093 marginRect.bottom += m_size.cy - m_vidrect.bottom;
3095 sub->CreateClippers(m_size);
3096 sub->MakeLines(m_size, marginRect);
3097 m_subtitleCache[entry] = sub;
3098 return(sub);
3103 STDMETHODIMP CRenderedTextSubtitle::NonDelegatingQueryInterface(REFIID riid, void** ppv)
3105 CheckPointer(ppv, E_POINTER);
3106 *ppv = NULL;
3107 return
3108 QI(IPersist)
3109 QI(ISubStream)
3110 QI(ISubPicProvider)
3111 QI(ISubPicProviderEx)
3112 __super::NonDelegatingQueryInterface(riid, ppv);
3115 // ISubPicProvider
3117 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetStartPosition(REFERENCE_TIME rt, double fps)
3119 m_fps = fps;
3120 if (m_fps>0)
3122 m_period = 1000/m_fps;
3123 if(m_period<=0)
3125 m_period = 1;
3128 else
3130 //Todo: fix me. max has been defined as a macro. Use #define NOMINMAX to fix it.
3131 //std::numeric_limits<int>::max();
3132 m_period = INT_MAX;
3135 int iSegment;
3136 int subIndex = 1;//If a segment has animate effect then it corresponds to several subpics.
3137 //subIndex, 1 based, indicates which subpic the result corresponds to.
3138 rt /= 10000i64;
3139 const STSSegment *stss = SearchSubs((int)rt, fps, &iSegment, NULL);
3140 if(stss==NULL)
3141 return NULL;
3142 else if(stss->animated)
3144 int start = TranslateSegmentStart(iSegment, fps);
3145 if(rt > start)
3146 subIndex = (rt-start)/m_period + 1;
3148 //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));
3149 return (POSITION)(subIndex | (iSegment<<RTS_POS_SEGMENT_INDEX_BITS));
3150 //if(iSegment < 0) iSegment = 0;
3151 //return(GetNext((POSITION)iSegment));
3154 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetNext(POSITION pos)
3156 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3157 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3158 const STSSegment *stss = GetSegment(iSegment);
3159 ASSERT(stss!=NULL && stss->subs.GetCount()>0);
3160 //DbgLog((LOG_TRACE, 3, "stss:%x count:%d", stss, stss->subs.GetCount()));
3161 if(!stss->animated)
3163 iSegment++;
3164 subIndex = 1;
3166 else
3168 int start, end;
3169 TranslateSegmentStartEnd(iSegment, m_fps, start, end);
3170 if(start+m_period*subIndex < end)
3171 subIndex++;
3172 else
3174 iSegment++;
3175 subIndex = 1;
3178 if(GetSegment(iSegment) != NULL)
3180 ASSERT(GetSegment(iSegment)->subs.GetCount()>0);
3181 return (POSITION)(subIndex | (iSegment<<RTS_POS_SEGMENT_INDEX_BITS));
3183 else
3184 return NULL;
3187 //@return: <0 if segment not found
3188 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStart(POSITION pos, double fps)
3190 //return(10000i64 * TranslateSegmentStart((int)pos-1, fps));
3191 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3192 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3193 int start = TranslateSegmentStart(iSegment, fps);
3194 const STSSegment *stss = GetSegment(iSegment);
3195 if(stss!=NULL)
3197 return (start + (subIndex-1)*m_period)*10000i64;
3199 else
3201 return -1;
3205 //@return: <0 if segment not found
3206 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStop(POSITION pos, double fps)
3208 // return(10000i64 * TranslateSegmentEnd((int)pos-1, fps));
3209 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3210 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3211 int start, end, ret;
3212 TranslateSegmentStartEnd(iSegment, fps, start, end);
3213 const STSSegment *stss = GetSegment(iSegment);
3214 if(stss!=NULL)
3216 if(!stss->animated)
3217 ret = end;
3218 else
3220 ret = start+subIndex*m_period;
3221 if(ret > end)
3222 ret = end;
3224 return ret*10000i64;
3226 else
3227 return -1;
3230 //@start, @stop: -1 if segment not found; @stop may < @start if subIndex exceed uppper bound
3231 STDMETHODIMP_(VOID) CRenderedTextSubtitle::GetStartStop(POSITION pos, double fps, /*out*/REFERENCE_TIME &start, /*out*/REFERENCE_TIME &stop)
3233 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3234 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3235 int tempStart, tempEnd;
3236 TranslateSegmentStartEnd(iSegment, fps, tempStart, tempEnd);
3237 start = tempStart;
3238 stop = tempEnd;
3239 const STSSegment *stss = GetSegment(iSegment);
3240 if(stss!=NULL)
3242 if(stss->animated)
3244 start += (subIndex-1)*m_period;
3245 if(start+m_period < stop)
3246 stop = start+m_period;
3248 //DbgLog((LOG_TRACE, 3, "animated:%d seg:%d idx:%d start:%d stop:%lu", stss->animated, iSegment, subIndex, (ULONG)start, (ULONG)stop));
3249 start *= 10000i64;
3250 stop *= 10000i64;
3252 else
3254 start = -1;
3255 stop = -1;
3259 STDMETHODIMP_(bool) CRenderedTextSubtitle::IsAnimated(POSITION pos)
3261 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3262 if(iSegment>=0 && iSegment<m_segments.GetCount())
3263 return m_segments[iSegment].animated;
3264 else
3265 return false;
3266 //return(true);
3269 struct LSub {int idx, layer, readorder;};
3271 static int lscomp(const void* ls1, const void* ls2)
3273 int ret = ((LSub*)ls1)->layer - ((LSub*)ls2)->layer;
3274 if(!ret) ret = ((LSub*)ls1)->readorder - ((LSub*)ls2)->readorder;
3275 return(ret);
3278 STDMETHODIMP CRenderedTextSubtitle::ParseScript(SubPicDesc& spd, REFERENCE_TIME rt, double fps, CSubtitle2List *outputSub2List )
3280 //fix me: check input and log error
3281 int t = (int)(rt / 10000);
3282 int segment;
3283 //const
3284 STSSegment* stss = SearchSubs2(t, fps, &segment);
3285 if(!stss) return S_FALSE;
3286 // clear any cached subs not in the range of +/-30secs measured from the segment's bounds
3288 POSITION pos = m_subtitleCache.GetStartPosition();
3289 while(pos)
3291 int key;
3292 CSubtitle* value;
3293 m_subtitleCache.GetNextAssoc(pos, key, value);
3294 STSEntry& stse = m_entries.GetAt(key);
3295 if(stse.end <= (t-30000) || stse.start > (t+30000))
3297 delete value;
3298 m_subtitleCache.RemoveKey(key);
3299 pos = m_subtitleCache.GetStartPosition();
3303 m_sla.AdvanceToSegment(segment, stss->subs);
3304 CAtlArray<LSub> subs;
3305 for(int i = 0, j = stss->subs.GetCount(); i < j; i++)
3307 LSub ls;
3308 ls.idx = stss->subs[i];
3309 ls.layer = m_entries.GetAt(stss->subs[i]).layer;
3310 ls.readorder = m_entries.GetAt(stss->subs[i]).readorder;
3311 subs.Add(ls);
3313 qsort(subs.GetData(), subs.GetCount(), sizeof(LSub), lscomp);
3315 for(int i = 0, j = subs.GetCount(); i < j; i++)
3317 int entry = subs[i].idx;
3318 STSEntry stse = m_entries.GetAt(entry);
3320 int start = TranslateStart(entry, fps);
3321 m_time = t - start;
3322 m_delay = TranslateEnd(entry, fps) - start;
3324 CSubtitle* s = GetSubtitle(entry);
3325 if(!s) continue;
3326 stss->animated |= s->m_fAnimated2;
3327 CRect clipRect = s->m_clip;
3328 CRect r = s->m_rect;
3329 CSize spaceNeeded = r.Size();
3330 // apply the effects
3331 bool fPosOverride = false, fOrgOverride = false;
3332 int alpha = 0x00;
3333 CPoint org2;
3334 for(int k = 0; k < EF_NUMBEROFEFFECTS; k++)
3336 if(!s->m_effects[k]) continue;
3337 switch(k)
3339 case EF_MOVE: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3], t1=t[0], t2=t[1])}
3341 CPoint p;
3342 CPoint p1(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
3343 CPoint p2(s->m_effects[k]->param[2], s->m_effects[k]->param[3]);
3344 int t1 = s->m_effects[k]->t[0];
3345 int t2 = s->m_effects[k]->t[1];
3346 if(t2 < t1) {int t = t1; t1 = t2; t2 = t;}
3347 if(t1 <= 0 && t2 <= 0) {t1 = 0; t2 = m_delay;}
3348 if(m_time <= t1) p = p1;
3349 else if (p1 == p2) p = p1;
3350 else if(t1 < m_time && m_time < t2)
3352 double t = 1.0*(m_time-t1)/(t2-t1);
3353 p.x = (int)((1-t)*p1.x + t*p2.x);
3354 p.y = (int)((1-t)*p1.y + t*p2.y);
3356 else p = p2;
3357 r = CRect(
3358 CPoint((s->m_scrAlignment%3) == 1 ? p.x : (s->m_scrAlignment%3) == 0 ? p.x - spaceNeeded.cx : p.x - (spaceNeeded.cx+1)/2,
3359 s->m_scrAlignment <= 3 ? p.y - spaceNeeded.cy : s->m_scrAlignment <= 6 ? p.y - (spaceNeeded.cy+1)/2 : p.y),
3360 spaceNeeded);
3361 if(s->m_relativeTo == 1)
3362 r.OffsetRect(m_vidrect.TopLeft());
3363 fPosOverride = true;
3365 break;
3366 case EF_ORG: // {\org(x=param[0], y=param[1])}
3368 org2 = CPoint(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
3369 fOrgOverride = true;
3371 break;
3372 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])
3374 int t1 = s->m_effects[k]->t[0];
3375 int t2 = s->m_effects[k]->t[1];
3376 int t3 = s->m_effects[k]->t[2];
3377 int t4 = s->m_effects[k]->t[3];
3378 if(t1 == -1 && t4 == -1) {t1 = 0; t3 = m_delay-t3; t4 = m_delay;}
3379 if(m_time < t1) alpha = s->m_effects[k]->param[0];
3380 else if(m_time >= t1 && m_time < t2)
3382 double t = 1.0 * (m_time - t1) / (t2 - t1);
3383 alpha = (int)(s->m_effects[k]->param[0]*(1-t) + s->m_effects[k]->param[1]*t);
3385 else if(m_time >= t2 && m_time < t3) alpha = s->m_effects[k]->param[1];
3386 else if(m_time >= t3 && m_time < t4)
3388 double t = 1.0 * (m_time - t3) / (t4 - t3);
3389 alpha = (int)(s->m_effects[k]->param[1]*(1-t) + s->m_effects[k]->param[2]*t);
3391 else if(m_time >= t4) alpha = s->m_effects[k]->param[2];
3393 break;
3394 case EF_BANNER: // Banner;delay=param[0][;leftoright=param[1];fadeawaywidth=param[2]]
3396 int left = s->m_relativeTo == 1 ? m_vidrect.left : 0,
3397 right = s->m_relativeTo == 1 ? m_vidrect.right : m_size.cx;
3398 r.left = !!s->m_effects[k]->param[1]
3399 ? (left/*marginRect.left*/ - spaceNeeded.cx) + (int)(m_time*8.0/s->m_effects[k]->param[0])
3400 : (right /*- marginRect.right*/) - (int)(m_time*8.0/s->m_effects[k]->param[0]);
3401 r.right = r.left + spaceNeeded.cx;
3402 clipRect &= CRect(left>>3, clipRect.top, right>>3, clipRect.bottom);
3403 fPosOverride = true;
3405 break;
3406 case EF_SCROLL: // Scroll up/down(toptobottom=param[3]);top=param[0];bottom=param[1];delay=param[2][;fadeawayheight=param[4]]
3408 r.top = !!s->m_effects[k]->param[3]
3409 ? s->m_effects[k]->param[0] + (int)(m_time*8.0/s->m_effects[k]->param[2]) - spaceNeeded.cy
3410 : s->m_effects[k]->param[1] - (int)(m_time*8.0/s->m_effects[k]->param[2]);
3411 r.bottom = r.top + spaceNeeded.cy;
3412 CRect cr(0, (s->m_effects[k]->param[0] + 4) >> 3, spd.w, (s->m_effects[k]->param[1] + 4) >> 3);
3413 if(s->m_relativeTo == 1)
3414 r.top += m_vidrect.top,
3415 r.bottom += m_vidrect.top,
3416 cr.top += m_vidrect.top>>3,
3417 cr.bottom += m_vidrect.top>>3;
3418 clipRect &= cr;
3419 fPosOverride = true;
3421 break;
3422 default:
3423 break;
3426 if(!fPosOverride && !fOrgOverride && !s->m_fAnimated)
3427 r = m_sla.AllocRect(s, segment, entry, stse.layer, m_collisions);
3428 CPoint org;
3429 org.x = (s->m_scrAlignment%3) == 1 ? r.left : (s->m_scrAlignment%3) == 2 ? r.CenterPoint().x : r.right;
3430 org.y = s->m_scrAlignment <= 3 ? r.bottom : s->m_scrAlignment <= 6 ? r.CenterPoint().y : r.top;
3431 if(!fOrgOverride) org2 = org;
3432 CPoint p2(0, r.top);
3433 // Rectangles for inverse clip
3435 CSubtitle2& sub2 = outputSub2List->GetAt(outputSub2List->AddTail( CSubtitle2(s, clipRect, org, org2, p2, alpha, m_time) ));
3438 return (subs.GetCount()) ? S_OK : S_FALSE;
3441 STDMETHODIMP CRenderedTextSubtitle::RenderEx(SubPicDesc& spd, REFERENCE_TIME rt, double fps, CAtlList<CRect>& rectList)
3443 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))
3444 Init(CSize(spd.w, spd.h), spd.vidrect);
3446 CSubtitle2List sub2List;
3447 HRESULT hr = ParseScript(spd, rt, fps, &sub2List);
3448 if(hr!=S_OK)
3450 return hr;
3453 CompositeDrawItemListList drawItemListList;
3454 DoRender(spd, sub2List, &rectList, &drawItemListList);
3455 Draw(spd, drawItemListList);
3456 return (!rectList.IsEmpty()) ? S_OK : S_FALSE;
3459 void CRenderedTextSubtitle::DoRender( SubPicDesc& spd, const CSubtitle2List& sub2List,
3460 CAtlList<CRect> *rectList, CompositeDrawItemListList *drawItemListList /*output*/)
3462 //check input and log error
3463 POSITION pos=sub2List.GetHeadPosition();
3464 while ( pos!=NULL )
3466 const CSubtitle2& sub2 = sub2List.GetNext(pos);
3467 CompositeDrawItemList& drawItemList = drawItemListList->GetAt(drawItemListList->AddTail());
3468 RenderOneSubtitle(spd, sub2, rectList, &drawItemList);
3472 void CRenderedTextSubtitle::RenderOneSubtitle( SubPicDesc& spd, const CSubtitle2& sub2,
3473 CAtlList<CRect>* rectList, CompositeDrawItemList* drawItemList /*output*/)
3475 CSubtitle* s = sub2.s;
3476 const CRect& clipRect = sub2.clipRect;
3477 const CPoint& org = sub2.org;
3478 const CPoint& org2 = sub2.org2;
3479 const CPoint& p2 = sub2.p;
3480 int alpha = sub2.alpha;
3481 int time = sub2.time;
3482 if(!s) return;
3484 const SharedPtrCClipper& clipper = s->m_pClipper;
3485 CRect iclipRect[4];
3486 iclipRect[0] = CRect(0, 0, spd.w, clipRect.top);
3487 iclipRect[1] = CRect(0, clipRect.top, clipRect.left, clipRect.bottom);
3488 iclipRect[2] = CRect(clipRect.right, clipRect.top, spd.w, clipRect.bottom);
3489 iclipRect[3] = CRect(0, clipRect.bottom, spd.w, spd.h);
3490 CRect bbox2(0,0,0,0);
3491 POSITION pos = s->GetHeadLinePosition();
3492 CPoint p = p2;
3493 while(pos)
3495 CLine* l = s->GetNextLine(pos);
3496 p.x = (s->m_scrAlignment%3) == 1 ? org.x
3497 : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
3498 : org.x - (l->m_width/2);
3500 CompositeDrawItemList tmpDrawItemList;
3501 if (s->m_clipInverse)
3503 CompositeDrawItemList tmp1,tmp2,tmp3,tmp4;
3504 for (int i=0;i<l->GetWordCount();i++)
3506 tmp1.AddTail();
3507 tmp2.AddTail();
3508 tmp3.AddTail();
3509 tmp4.AddTail();
3511 bbox2 |= l->PaintAll(&tmp1, spd, iclipRect[0], clipper, p, org2, time, alpha);
3512 bbox2 |= l->PaintAll(&tmp2, spd, iclipRect[1], clipper, p, org2, time, alpha);
3513 bbox2 |= l->PaintAll(&tmp3, spd, iclipRect[2], clipper, p, org2, time, alpha);
3514 bbox2 |= l->PaintAll(&tmp4, spd, iclipRect[3], clipper, p, org2, time, alpha);
3515 tmpDrawItemList.AddTailList(&tmp1);
3516 tmpDrawItemList.AddTailList(&tmp2);
3517 tmpDrawItemList.AddTailList(&tmp3);
3518 tmpDrawItemList.AddTailList(&tmp4);
3520 else
3522 for (int i=0;i<l->GetWordCount();i++)
3524 tmpDrawItemList.AddTail();
3526 bbox2 |= l->PaintAll(&tmpDrawItemList, spd, clipRect, clipper, p, org2, time, alpha);
3528 drawItemList->AddTailList(&tmpDrawItemList);
3529 p.y += l->m_ascent + l->m_descent;
3531 rectList->AddTail(bbox2);
3534 STDMETHODIMP CRenderedTextSubtitle::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
3536 CAtlList<CRect> rectList;
3537 HRESULT result = RenderEx(spd, rt, fps, rectList);
3538 POSITION pos = rectList.GetHeadPosition();
3539 CRect bbox2(0,0,0,0);
3540 while(pos!=NULL)
3542 bbox2 |= rectList.GetNext(pos);
3544 bbox = bbox2;
3545 return result;
3548 // IPersist
3550 STDMETHODIMP CRenderedTextSubtitle::GetClassID(CLSID* pClassID)
3552 return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
3555 // ISubStream
3557 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStreamCount()
3559 return(1);
3562 STDMETHODIMP CRenderedTextSubtitle::GetStreamInfo(int iStream, WCHAR** ppName, LCID* pLCID)
3564 if(iStream != 0) return E_INVALIDARG;
3565 if(ppName)
3567 if(!(*ppName = (WCHAR*)CoTaskMemAlloc((m_name.GetLength()+1)*sizeof(WCHAR))))
3568 return E_OUTOFMEMORY;
3569 wcscpy(*ppName, CStringW(m_name));
3571 if(pLCID)
3573 *pLCID = 0; // TODO
3575 return S_OK;
3578 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStream()
3580 return(0);
3583 STDMETHODIMP CRenderedTextSubtitle::SetStream(int iStream)
3585 return iStream == 0 ? S_OK : E_FAIL;
3588 STDMETHODIMP CRenderedTextSubtitle::Reload()
3590 CFileStatus s;
3591 if(!CFile::GetStatus(m_path, s)) return E_FAIL;
3592 return !m_path.IsEmpty() && Open(m_path, DEFAULT_CHARSET) ? S_OK : E_FAIL;
3595 STDMETHODIMP_(bool) CRenderedTextSubtitle::IsColorTypeSupported( int type )
3597 return type==MSP_AYUV_PLANAR ||
3598 type==MSP_AYUV ||
3599 type==MSP_XY_AUYV ||
3600 type==MSP_RGBA;
3603 void CRenderedTextSubtitle::Draw( SubPicDesc& spd, CompositeDrawItemListList& drawItemListList )
3605 POSITION list_pos = drawItemListList.GetHeadPosition();
3606 while(list_pos)
3608 CompositeDrawItemList& drawItemList = drawItemListList.GetNext(list_pos);
3609 POSITION item_pos = drawItemList.GetHeadPosition();
3610 while(item_pos)
3612 CompositeDrawItem& draw_item = drawItemList.GetNext(item_pos);
3613 if(draw_item.shadow)
3614 Draw( spd, *draw_item.shadow );
3616 item_pos = drawItemList.GetHeadPosition();
3617 while(item_pos)
3619 CompositeDrawItem& draw_item = drawItemList.GetNext(item_pos);
3620 if(draw_item.outline)
3621 Draw( spd, *draw_item.outline );
3623 item_pos = drawItemList.GetHeadPosition();
3624 while(item_pos)
3626 CompositeDrawItem& draw_item = drawItemList.GetNext(item_pos);
3627 if(draw_item.body)
3628 Draw( spd, *draw_item.body );
3633 CRect CRenderedTextSubtitle::DryDraw( SubPicDesc& spd, DrawItem& draw_item )
3635 return Rasterizer::DryDraw(spd, draw_item.overlay, draw_item.clip_rect, NULL,
3636 draw_item.xsub, draw_item.ysub, draw_item.switchpts, draw_item.fBody, draw_item.fBorder);
3639 CRect CRenderedTextSubtitle::Draw( SubPicDesc& spd, DrawItem& draw_item )
3641 CRect result;
3642 const SharedPtrGrayImage2& alpha_mask = CClipper::GetAlphaMask(draw_item.clipper);
3643 const SharedPtrByte& alpha = Rasterizer::CompositeAlphaMask(spd, draw_item.overlay, draw_item.clip_rect, alpha_mask.get(),
3644 draw_item.xsub, draw_item.ysub, draw_item.switchpts, draw_item.fBody, draw_item.fBorder,
3645 &result);
3646 Rasterizer::Draw(spd, draw_item.overlay, result, alpha.get(),
3647 draw_item.xsub, draw_item.ysub, draw_item.switchpts, draw_item.fBody, draw_item.fBorder);
3648 return result;
3651 DrawItem* CRenderedTextSubtitle::CreateDrawItem( SubPicDesc& spd, const SharedPtrOverlay& overlay, const CRect& clipRect,
3652 const SharedPtrCClipper &clipper, int xsub, int ysub, const DWORD* switchpts, bool fBody, bool fBorder )
3654 DrawItem* result = new DrawItem();
3655 result->overlay = overlay;
3656 result->clip_rect = clipRect;
3657 result->clipper = clipper;
3658 result->xsub = xsub;
3659 result->ysub = ysub;
3661 memcpy(result->switchpts, switchpts, sizeof(result->switchpts));
3662 result->fBody = fBody;
3663 result->fBorder = fBorder;
3664 return result;