Xy Flyweight. [Part 3]
[xy_vsfilter.git] / src / subtitles / RTS.cpp
blob4753e6a33c0712217be50a5cee0dd80a764d046a
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 "draw_item.h"
27 #include "cache_manager.h"
28 #include "subpixel_position_controler.h"
29 #include "xy_overlay_paint_machine.h"
30 #include "xy_clipper_paint_machine.h"
32 // WARNING: this isn't very thread safe, use only one RTS a time.
33 static HDC g_hDC;
34 static int g_hDC_refcnt = 0;
36 static long revcolor(long c)
38 return ((c&0xff0000)>>16) + (c&0xff00) + ((c&0xff)<<16);
41 // Skip all leading whitespace
42 inline CStringW::PCXSTR SkipWhiteSpaceLeft(const CStringW& str)
44 CStringW::PCXSTR psz = str.GetString();
46 while( iswspace( *psz ) )
48 psz++;
50 return psz;
53 // Skip all trailing whitespace
54 inline CStringW::PCXSTR SkipWhiteSpaceRight(const CStringW& str)
56 CStringW::PCXSTR psz = str.GetString();
57 CStringW::PCXSTR pszLast = psz + str.GetLength() - 1;
58 bool first_white = false;
59 while( iswspace( *pszLast ) )
61 pszLast--;
62 if(pszLast<psz)
63 break;
65 return pszLast;
68 // Skip all leading whitespace
69 inline CStringW::PCXSTR SkipWhiteSpaceLeft(CStringW::PCXSTR start, CStringW::PCXSTR end)
71 while( start!=end && iswspace( *start ) )
73 start++;
75 return start;
78 // Skip all trailing whitespace, first char must NOT be white space
79 inline CStringW::PCXSTR FastSkipWhiteSpaceRight(CStringW::PCXSTR start, CStringW::PCXSTR end)
81 while( iswspace( *--end ) );
82 return end+1;
85 inline CStringW::PCXSTR FindChar(CStringW::PCXSTR start, CStringW::PCXSTR end, WCHAR c)
87 while( start!=end && *start!=c )
89 start++;
91 return start;
94 //////////////////////////////////////////////////////////////////////////////////////////////
96 // CMyFont
98 CMyFont::CMyFont(const STSStyleBase& style)
100 LOGFONT lf;
101 memset(&lf, 0, sizeof(lf));
102 lf <<= style;
103 lf.lfHeight = (LONG)(style.fontSize+0.5);
104 lf.lfOutPrecision = OUT_TT_PRECIS;
105 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
106 lf.lfQuality = ANTIALIASED_QUALITY;
107 lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
108 if(!CreateFontIndirect(&lf))
110 _tcscpy(lf.lfFaceName, _T("Arial"));
111 CreateFontIndirect(&lf);
113 HFONT hOldFont = SelectFont(g_hDC, *this);
114 TEXTMETRIC tm;
115 GetTextMetrics(g_hDC, &tm);
116 m_ascent = ((tm.tmAscent + 4) >> 3);
117 m_descent = ((tm.tmDescent + 4) >> 3);
118 SelectFont(g_hDC, hOldFont);
121 // CWord
123 CWord::CWord(const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend)
124 : m_style(style), m_str(new CStringW(str))
125 , m_width(0), m_ascent(0), m_descent(0)
126 , m_ktype(ktype), m_kstart(kstart), m_kend(kend)
127 , m_fLineBreak(false), m_fWhiteSpaceChar(false)
128 //, m_pOpaqueBox(NULL)
130 if(m_str.Get().IsEmpty())
132 m_fWhiteSpaceChar = m_fLineBreak = true;
134 m_width = 0;
137 CWord::CWord( const CWord& src):m_str(src.m_str)
139 m_fWhiteSpaceChar = src.m_fWhiteSpaceChar;
140 m_fLineBreak = src.m_fLineBreak;
141 m_style = src.m_style;
142 m_pOpaqueBox = src.m_pOpaqueBox;//allow since it is shared_ptr
143 m_ktype = src.m_ktype;
144 m_kstart = src.m_kstart;
145 m_kend = src.m_kend;
146 m_width = src.m_width;
147 m_ascent = src.m_ascent;
148 m_descent = src.m_descent;
151 CWord::~CWord()
153 //if(m_pOpaqueBox) delete m_pOpaqueBox;
156 bool CWord::Append(const SharedPtrCWord& w)
158 if(!(m_style == w->m_style)
159 || m_fLineBreak || w->m_fLineBreak
160 || w->m_kstart != w->m_kend || m_ktype != w->m_ktype) return(false);
161 m_fWhiteSpaceChar = m_fWhiteSpaceChar && w->m_fWhiteSpaceChar;
162 CStringW *str = new CStringW();//Fix me: anyway to avoid this flyweight update?
163 ASSERT(str);
164 *str = m_str.Get();
165 *str += w->m_str.Get();
166 m_str = XyFwStringW(str);
167 m_width += w->m_width;
168 return(true);
171 void CWord::PaintFromOverlay(const CPoint& p, const CPoint& trans_org2, OverlayKey &subpixel_variance_key, SharedPtrOverlay& overlay)
173 if( SubpixelPositionControler::GetGlobalControler().UseBilinearShift() )
175 CPoint psub = SubpixelPositionControler::GetGlobalControler().GetSubpixel(p);
176 if( (psub.x!=(p.x&SubpixelPositionControler::EIGHT_X_EIGHT_MASK)
177 || psub.y!=(p.y&SubpixelPositionControler::EIGHT_X_EIGHT_MASK)) )
179 overlay.reset(overlay->GetSubpixelVariance((p.x&SubpixelPositionControler::EIGHT_X_EIGHT_MASK) - psub.x,
180 (p.y&SubpixelPositionControler::EIGHT_X_EIGHT_MASK) - psub.y));
181 OverlayMruCache* overlay_cache = CacheManager::GetSubpixelVarianceCache();
182 overlay_cache->UpdateCache(subpixel_variance_key, overlay);
187 void CWord::PaintFromNoneBluredOverlay(SharedPtrOverlay raterize_result, const OverlayKey& overlay_key, SharedPtrOverlay* overlay)
189 if( Rasterizer::IsItReallyBlur(m_style.get().fBlur, m_style.get().fGaussianBlur) )
191 overlay->reset(new Overlay());
192 if(!Rasterizer::Blur(*raterize_result, m_style.get().fBlur, m_style.get().fGaussianBlur, *overlay))
194 *overlay = raterize_result;
197 else
199 *overlay = raterize_result;
201 OverlayMruCache* overlay_cache = CacheManager::GetOverlayMruCache();
202 overlay_cache->UpdateCache(overlay_key, *overlay);
205 bool CWord::PaintFromScanLineData2(const CPoint& psub, const ScanLineData2& scan_line_data2, const OverlayKey& key, SharedPtrOverlay* overlay)
207 SharedPtrOverlay raterize_result(new Overlay());
208 if(!Rasterizer::Rasterize(scan_line_data2, psub.x, psub.y, raterize_result))
210 return false;
212 OverlayNoBlurMruCache* overlay_no_blur_cache = CacheManager::GetOverlayNoBlurMruCache();
213 overlay_no_blur_cache->UpdateCache(key, raterize_result);
214 PaintFromNoneBluredOverlay(raterize_result, key, overlay);
215 return true;
218 bool CWord::PaintFromPathData(const CPoint& psub, const CPoint& trans_org, const PathData& path_data, const OverlayKey& key, SharedPtrOverlay* overlay )
220 bool result = false;
222 PathData *path_data2 = new PathData(path_data);//fix me: this copy operation can be saved if no transform is needed
223 SharedPtrConstPathData shared_ptr_path_data2(path_data2);
224 bool need_transform = NeedTransform();
225 if(need_transform)
226 Transform(path_data2, CPoint(trans_org.x*8, trans_org.y*8));
228 CPoint left_top;
229 CSize size;
230 path_data2->AlignLeftTop(&left_top, &size);
232 int border_x = static_cast<int>(m_style.get().outlineWidthX+0.5);
233 int border_y = static_cast<int>(m_style.get().outlineWidthY+0.5);
234 int wide_border = border_x>border_y ? border_x:border_y;
235 if (m_style.get().borderStyle==1)
237 border_x = border_y = 0;
240 OverlayNoOffsetMruCache* overlay_key_cache = CacheManager::GetOverlayNoOffsetMruCache();
241 OverlayNoOffsetKey overlay_no_offset_key(shared_ptr_path_data2, psub.x, psub.y, border_x, border_y);
242 overlay_no_offset_key.UpdateHashValue();
243 POSITION pos_key = overlay_key_cache->Lookup(overlay_no_offset_key);
244 POSITION pos = NULL;
246 OverlayNoBlurMruCache* overlay_cache = CacheManager::GetOverlayNoBlurMruCache();
247 if (pos_key!=NULL)
249 OverlayNoBlurKey overlay_key = overlay_key_cache->GetAt(pos_key);
250 pos = overlay_cache->Lookup(overlay_key);
252 if (pos)
254 SharedPtrOverlay raterize_result( new Overlay() );
255 *raterize_result = *overlay_cache->GetAt(pos);
256 raterize_result->mOffsetX = left_top.x - psub.x - ((wide_border+7)&~7);
257 raterize_result->mOffsetY = left_top.y - psub.y - ((wide_border+7)&~7);
258 PaintFromNoneBluredOverlay(raterize_result, key, overlay);
259 result = true;
260 overlay_cache->UpdateCache(key, raterize_result);
262 else
264 ScanLineDataMruCache* scan_line_data_cache = CacheManager::GetScanLineDataMruCache();
265 pos = scan_line_data_cache->Lookup(overlay_no_offset_key);
266 SharedPtrConstScanLineData scan_line_data;
267 if( pos != NULL )
269 scan_line_data = scan_line_data_cache->GetAt(pos);
270 scan_line_data_cache->UpdateCache(pos);
272 else
274 ScanLineData *tmp = new ScanLineData();
275 scan_line_data.reset(tmp);
276 if(!tmp->ScanConvert(*path_data2, size))
278 return false;
280 scan_line_data_cache->UpdateCache(overlay_no_offset_key, scan_line_data);
282 ScanLineData2 *tmp = new ScanLineData2(left_top, scan_line_data);
283 SharedPtrScanLineData2 scan_line_data2( tmp );
284 if(m_style.get().borderStyle == 0 && (m_style.get().outlineWidthX+m_style.get().outlineWidthY > 0))
286 if(!tmp->CreateWidenedRegion(border_x, border_y))
288 return false;
291 ScanLineData2MruCache* scan_line_data2_cache = CacheManager::GetScanLineData2MruCache();
292 scan_line_data2_cache->UpdateCache(key, scan_line_data2);
293 result = PaintFromScanLineData2(psub, *tmp, key, overlay);
295 if (result)
297 if (pos_key!=NULL)
299 overlay_key_cache->UpdateCache(pos_key, key);
301 else
303 overlay_key_cache->UpdateCache(overlay_no_offset_key, key);
306 return result;
309 bool CWord::PaintFromRawData( const CPoint& psub, const CPoint& trans_org, const OverlayKey& key, SharedPtrOverlay* overlay )
311 PathDataMruCache* path_data_cache = CacheManager::GetPathDataMruCache();
313 PathData *tmp=new PathData();
314 SharedPtrPathData path_data(tmp);
315 if(!CreatePath(tmp))
317 return false;
319 path_data_cache->UpdateCache(key, path_data);
320 return PaintFromPathData(psub, trans_org, *tmp, key, overlay);
323 bool CWord::DoPaint(const CPoint& psub, const CPoint& trans_org, SharedPtrOverlay* overlay, const OverlayKey& key)
325 bool result = true;
326 OverlayNoBlurMruCache* overlay_no_blur_cache = CacheManager::GetOverlayNoBlurMruCache();
327 POSITION pos = overlay_no_blur_cache->Lookup(key);
329 if(pos!=NULL)
331 SharedPtrOverlay raterize_result = overlay_no_blur_cache->GetAt(pos);
332 overlay_no_blur_cache->UpdateCache( pos );
333 PaintFromNoneBluredOverlay(raterize_result, key, overlay);
335 else
337 ScanLineData2MruCache* scan_line_data_cache = CacheManager::GetScanLineData2MruCache();
338 pos = scan_line_data_cache->Lookup(key);
339 if(pos!=NULL)
341 SharedPtrConstScanLineData2 scan_line_data = scan_line_data_cache->GetAt(pos);
342 scan_line_data_cache->UpdateCache( pos );
343 result = PaintFromScanLineData2(psub, *scan_line_data, key, overlay);
345 else
347 PathDataMruCache* path_data_cache = CacheManager::GetPathDataMruCache();
348 POSITION pos_path = path_data_cache->Lookup(key);
349 if(pos_path!=NULL)
351 SharedPtrConstPathData path_data = path_data_cache->GetAt(pos_path); //important! copy not ref
352 path_data_cache->UpdateCache( pos_path );
353 result = PaintFromPathData(psub, trans_org, *path_data, key, overlay);
355 else
357 result = PaintFromRawData(psub, trans_org, key, overlay);
361 return result;
364 bool CWord::NeedTransform()
366 return (fabs(m_style.get().fontScaleX - 100) > 0.000001) ||
367 (fabs(m_style.get().fontScaleY - 100) > 0.000001) ||
368 (fabs(m_style.get().fontAngleX) > 0.000001) ||
369 (fabs(m_style.get().fontAngleY) > 0.000001) ||
370 (fabs(m_style.get().fontAngleZ) > 0.000001) ||
371 (fabs(m_style.get().fontShiftX) > 0.000001) ||
372 (fabs(m_style.get().fontShiftY) > 0.000001);
375 void CWord::Transform(PathData* path_data, const CPoint& org)
377 //// CPUID from VDub
378 //bool fSSE2 = !!(g_cpuid.m_flags & CCpuID::sse2);
380 //if(fSSE2) { // SSE code
381 // Transform_SSE2(path_data, org);
382 //} else // C-code
383 Transform_C(path_data, org);
386 void CWord::Transform_C(PathData* path_data, const CPoint &org )
388 double scalex = m_style.get().fontScaleX/100;
389 double scaley = m_style.get().fontScaleY/100;
391 double caz = cos((3.1415/180)*m_style.get().fontAngleZ);
392 double saz = sin((3.1415/180)*m_style.get().fontAngleZ);
393 double cax = cos((3.1415/180)*m_style.get().fontAngleX);
394 double sax = sin((3.1415/180)*m_style.get().fontAngleX);
395 double cay = cos((3.1415/180)*m_style.get().fontAngleY);
396 double say = sin((3.1415/180)*m_style.get().fontAngleY);
398 #ifdef _VSMOD
399 // patch m003. random text points
400 double xrnd = m_style.get().mod_rand.X*100;
401 double yrnd = m_style.get().mod_rand.Y*100;
402 double zrnd = m_style.get().mod_rand.Z*100;
404 srand(m_style.get().mod_rand.Seed);
406 // patch m008. distort
407 int xsz,ysz;
408 double dst1x,dst1y,dst2x,dst2y,dst3x,dst3y;
409 int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
411 bool is_dist = m_style.get().mod_distort.enabled;
412 if (is_dist) {
413 for(int i = 0; i < path_data->mPathPoints; i++) {
414 if(minx > path_data->mpPathPoints[i].x) {
415 minx = path_data->mpPathPoints[i].x;
417 if(miny > path_data->mpPathPoints[i].y) {
418 miny = path_data->mpPathPoints[i].y;
420 if(maxx < path_data->mpPathPoints[i].x) {
421 maxx = path_data->mpPathPoints[i].x;
423 if(maxy < path_data->mpPathPoints[i].y) {
424 maxy = path_data->mpPathPoints[i].y;
428 xsz = max(maxx - minx, 0);
429 ysz = max(maxy - miny, 0);
431 dst1x = m_style.get().mod_distort.pointsx[0];
432 dst1y = m_style.get().mod_distort.pointsy[0];
433 dst2x = m_style.get().mod_distort.pointsx[1];
434 dst2y = m_style.get().mod_distort.pointsy[1];
435 dst3x = m_style.get().mod_distort.pointsx[2];
436 dst3y = m_style.get().mod_distort.pointsy[2];
438 #endif
440 for (int i = 0; i < path_data->mPathPoints; i++) {
441 double x, y, z, xx, yy, zz;
443 x = path_data->mpPathPoints[i].x;
444 y = path_data->mpPathPoints[i].y;
445 #ifdef _VSMOD
446 // patch m002. Z-coord
447 z = m_style.get().mod_z;
449 double u, v;
450 if (is_dist) {
451 u = (x-minx) / xsz;
452 v = (y-miny) / ysz;
454 x = minx+(0 + (dst1x - 0)*u + (dst3x-0)*v+(0+dst2x-dst1x-dst3x)*u*v)*xsz;
455 y = miny+(0 + (dst1y - 0)*u + (dst3y-0)*v+(0+dst2y-dst1y-dst3y)*u*v)*ysz;
456 //P = P0 + (P1 - P0)u + (P3 - P0)v + (P0 + P2 - P1 - P3)uv
459 // patch m003. random text points
460 x = xrnd > 0 ? (xrnd - rand() % (int)(xrnd * 2 + 1)) / 100.0 + x : x;
461 y = yrnd > 0 ? (yrnd - rand() % (int)(yrnd * 2 + 1)) / 100.0 + y : y;
462 z = zrnd > 0 ? (zrnd - rand() % (int)(zrnd * 2 + 1)) / 100.0 + z : z;
463 #else
464 z = 0;
465 #endif
466 double _x = x;
467 x = scalex * (x + m_style.get().fontShiftX * y) - org.x;
468 y = scaley * (y + m_style.get().fontShiftY * _x) - org.y;
470 xx = x*caz + y*saz;
471 yy = -(x*saz - y*caz);
472 zz = z;
474 x = xx;
475 y = yy*cax + zz*sax;
476 z = yy*sax - zz*cax;
478 xx = x*cay + z*say;
479 yy = y;
480 zz = x*say - z*cay;
482 zz = max(zz, -19000);
484 x = (xx * 20000) / (zz + 20000);
485 y = (yy * 20000) / (zz + 20000);
487 path_data->mpPathPoints[i].x = (LONG)(x + org.x + 0.5);
488 path_data->mpPathPoints[i].y = (LONG)(y + org.y + 0.5);
492 void CWord::Transform_SSE2(PathData* path_data, const CPoint &org )
494 // __m128 union data type currently not supported with Intel C++ Compiler, so just call C version
495 #ifdef __ICL
496 Transform_C(org);
497 #else
498 // SSE code
499 // speed up ~1.5-1.7x
500 double scalex = m_style.get().fontScaleX/100;
501 double scaley = m_style.get().fontScaleY/100;
503 double caz = cos((3.1415/180)*m_style.get().fontAngleZ);
504 double saz = sin((3.1415/180)*m_style.get().fontAngleZ);
505 double cax = cos((3.1415/180)*m_style.get().fontAngleX);
506 double sax = sin((3.1415/180)*m_style.get().fontAngleX);
507 double cay = cos((3.1415/180)*m_style.get().fontAngleY);
508 double say = sin((3.1415/180)*m_style.get().fontAngleY);
510 __m128 __xshift = _mm_set_ps1(m_style.get().fontShiftX);
511 __m128 __yshift = _mm_set_ps1(m_style.get().fontShiftY);
513 __m128 __xorg = _mm_set_ps1(org.x);
514 __m128 __yorg = _mm_set_ps1(org.y);
516 __m128 __xscale = _mm_set_ps1(scalex);
517 __m128 __yscale = _mm_set_ps1(scaley);
519 #ifdef _VSMOD
520 // patch m003. random text points
521 double xrnd = m_style.get().mod_rand.X*100;
522 double yrnd = m_style.get().mod_rand.Y*100;
523 double zrnd = m_style.get().mod_rand.Z*100;
525 srand(m_style.get().mod_rand.Seed);
527 __m128 __xsz = _mm_setzero_ps();
528 __m128 __ysz = _mm_setzero_ps();
530 __m128 __dst1x, __dst1y, __dst213x, __dst213y, __dst3x, __dst3y;
532 __m128 __miny;
533 __m128 __minx = _mm_set_ps(INT_MAX, INT_MAX, 0, 0);
534 __m128 __max = _mm_set_ps(-INT_MAX, -INT_MAX, 1, 1);
536 bool is_dist = m_style.get().mod_distort.enabled;
537 if(is_dist) {
538 for(int i = 0; i < path_data->mPathPoints; i++) {
539 __m128 __point = _mm_set_ps(path_data->mpPathPoints[i].x, path_data->mpPathPoints[i].y, 0, 0);
540 __minx = _mm_min_ps(__minx, __point);
541 __max = _mm_max_ps(__max, __point);
544 __m128 __zero = _mm_setzero_ps();
545 __max = _mm_sub_ps(__max, __minx); // xsz, ysz, 1, 1
546 __max = _mm_max_ps(__max, __zero);
548 __xsz = _mm_shuffle_ps(__max, __max, _MM_SHUFFLE(3,3,3,3));
549 __ysz = _mm_shuffle_ps(__max, __max, _MM_SHUFFLE(2,2,2,2));
551 __miny = _mm_shuffle_ps(__minx, __minx, _MM_SHUFFLE(2,2,2,2));
552 __minx = _mm_shuffle_ps(__minx, __minx, _MM_SHUFFLE(3,3,3,3));
554 __dst1x = _mm_set_ps1(m_style.get().mod_distort.pointsx[0]);
555 __dst1y = _mm_set_ps1(m_style.get().mod_distort.pointsy[0]);
556 __dst3x = _mm_set_ps1(m_style.get().mod_distort.pointsx[2]);
557 __dst3y = _mm_set_ps1(m_style.get().mod_distort.pointsy[2]);
558 __dst213x = _mm_set_ps1(m_style.get().mod_distort.pointsx[1]); // 2 - 1 - 3
559 __dst213x = _mm_sub_ps(__dst213x, __dst1x);
560 __dst213x = _mm_sub_ps(__dst213x, __dst3x);
562 __dst213y = _mm_set_ps1(m_style.get().mod_distort.pointsy[1]);
563 __dst213x = _mm_sub_ps(__dst213y, __dst1y);
564 __dst213x = _mm_sub_ps(__dst213y, __dst3y);
566 #endif
568 __m128 __caz = _mm_set_ps1(caz);
569 __m128 __saz = _mm_set_ps1(saz);
570 __m128 __cax = _mm_set_ps1(cax);
571 __m128 __sax = _mm_set_ps1(sax);
572 __m128 __cay = _mm_set_ps1(cay);
573 __m128 __say = _mm_set_ps1(say);
575 // this can be paralleled for openmp
576 int mPathPointsD4 = path_data->mPathPoints / 4;
577 int mPathPointsM4 = path_data->mPathPoints % 4;
579 for(ptrdiff_t i = 0; i < mPathPointsD4 + 1; i++) {
580 POINT* const temp_points = path_data->mpPathPoints + 4 * i;
582 __m128 __pointx, __pointy;
583 // we can't use load .-.
584 if(i != mPathPointsD4) {
585 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, temp_points[2].x, temp_points[3].x);
586 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, temp_points[2].y, temp_points[3].y);
587 } else { // last cycle
588 switch(mPathPointsM4) {
589 default:
590 case 0:
591 continue;
592 case 1:
593 __pointx = _mm_set_ps(temp_points[0].x, 0, 0, 0);
594 __pointy = _mm_set_ps(temp_points[0].y, 0, 0, 0);
595 break;
596 case 2:
597 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, 0, 0);
598 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, 0, 0);
599 break;
600 case 3:
601 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, temp_points[2].x, 0);
602 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, temp_points[2].y, 0);
603 break;
607 #ifdef _VSMOD
608 __m128 __pointz = _mm_set_ps1(m_style.get().mod_z);
610 // distort
611 if(is_dist) {
612 //P = P0 + (P1 - P0)u + (P3 - P0)v + (P0 + P2 - P1 - P3)uv
613 __m128 __u = _mm_sub_ps(__pointx, __minx);
614 __m128 __v = _mm_sub_ps(__pointy, __miny);
615 __m128 __1_xsz = _mm_rcp_ps(__xsz);
616 __m128 __1_ysz = _mm_rcp_ps(__ysz);
617 __u = _mm_mul_ps(__u, __1_xsz);
618 __v = _mm_mul_ps(__v, __1_ysz);
620 // x
621 __pointx = _mm_mul_ps(__dst213x, __u);
622 __pointx = _mm_mul_ps(__pointx, __v);
624 __m128 __tmpx = _mm_mul_ps(__dst3x, __v);
625 __pointx = _mm_add_ps(__pointx, __tmpx);
626 __tmpx = _mm_mul_ps(__dst1x, __u);
627 __pointx = _mm_add_ps(__pointx, __tmpx);
629 __pointx = _mm_mul_ps(__pointx, __xsz);
630 __pointx = _mm_add_ps(__pointx, __minx);
632 // y
633 __pointy = _mm_mul_ps(__dst213y, __u);
634 __pointy = _mm_mul_ps(__pointy, __v);
636 __m128 __tmpy = _mm_mul_ps(__dst3y, __v);
637 __pointy = _mm_add_ps(__pointy, __tmpy);
638 __tmpy = _mm_mul_ps(__dst1y, __u);
639 __pointy = _mm_add_ps(__pointy, __tmpy);
641 __pointy = _mm_mul_ps(__pointy, __ysz);
642 __pointy = _mm_add_ps(__pointy, __miny);
645 // randomize
646 if(xrnd!=0 || yrnd!=0 || zrnd!=0) {
647 __declspec(align(16)) float rx[4], ry[4], rz[4];
648 for(int k=0; k<4; k++) {
649 rx[k] = xrnd > 0 ? (xrnd - rand() % (int)(xrnd * 2 + 1)) : 0;
650 ry[k] = yrnd > 0 ? (yrnd - rand() % (int)(yrnd * 2 + 1)) : 0;
651 rz[k] = zrnd > 0 ? (zrnd - rand() % (int)(zrnd * 2 + 1)) : 0;
653 __m128 __001 = _mm_set_ps1(0.01f);
655 if(xrnd!=0) {
656 __m128 __rx = _mm_load_ps(rx);
657 __rx = _mm_mul_ps(__rx, __001);
658 __pointx = _mm_add_ps(__pointx, __rx);
661 if(yrnd!=0) {
662 __m128 __ry = _mm_load_ps(ry);
663 __ry = _mm_mul_ps(__ry, __001);
664 __pointy = _mm_add_ps(__pointy, __ry);
667 if(zrnd!=0) {
668 __m128 __rz = _mm_load_ps(rz);
669 __rz = _mm_mul_ps(__rz, __001);
670 __pointz = _mm_add_ps(__pointz, __rz);
673 #else
674 __m128 __pointz = _mm_set_ps1(0);
675 #endif
677 // scale and shift
678 __m128 __tmpx;
679 if(m_style.get().fontShiftX!=0) {
680 __tmpx = _mm_mul_ps(__xshift, __pointy);
681 __tmpx = _mm_add_ps(__tmpx, __pointx);
682 } else {
683 __tmpx = __pointx;
685 __tmpx = _mm_mul_ps(__tmpx, __xscale);
686 __tmpx = _mm_sub_ps(__tmpx, __xorg);
688 __m128 __tmpy;
689 if(m_style.get().fontShiftY!=0) {
690 __tmpy = _mm_mul_ps(__yshift, __pointx);
691 __tmpy = _mm_add_ps(__tmpy, __pointy);
692 } else {
693 __tmpy = __pointy;
695 __tmpy = _mm_mul_ps(__tmpy, __yscale);
696 __tmpy = _mm_sub_ps(__tmpy, __yorg);
698 // rotate
699 __m128 __xx = _mm_mul_ps(__tmpx, __caz);
700 __m128 __yy = _mm_mul_ps(__tmpy, __saz);
701 __pointx = _mm_add_ps(__xx, __yy);
702 __xx = _mm_mul_ps(__tmpx, __saz);
703 __yy = _mm_mul_ps(__tmpy, __caz);
704 __pointy = _mm_sub_ps(__yy, __xx);
706 __m128 __zz = _mm_mul_ps(__pointz, __sax);
707 __yy = _mm_mul_ps(__pointy, __cax);
708 __pointy = _mm_add_ps(__yy, __zz);
709 __zz = _mm_mul_ps(__pointz, __cax);
710 __yy = _mm_mul_ps(__pointy, __sax);
711 __pointz = _mm_sub_ps(__zz, __yy);
713 __xx = _mm_mul_ps(__pointx, __cay);
714 __zz = _mm_mul_ps(__pointz, __say);
715 __pointx = _mm_add_ps(__xx, __zz);
716 __xx = _mm_mul_ps(__pointx, __say);
717 __zz = _mm_mul_ps(__pointz, __cay);
718 __pointz = _mm_sub_ps(__xx, __zz);
720 __zz = _mm_set_ps1(-19000);
721 __pointz = _mm_max_ps(__pointz, __zz);
723 __m128 __20000 = _mm_set_ps1(20000);
724 __zz = _mm_add_ps(__pointz, __20000);
725 __zz = _mm_rcp_ps(__zz);
727 __pointx = _mm_mul_ps(__pointx, __20000);
728 __pointx = _mm_mul_ps(__pointx, __zz);
730 __pointy = _mm_mul_ps(__pointy, __20000);
731 __pointy = _mm_mul_ps(__pointy, __zz);
733 __pointx = _mm_add_ps(__pointx, __xorg);
734 __pointy = _mm_add_ps(__pointy, __yorg);
736 __m128 __05 = _mm_set_ps1(0.5);
738 __pointx = _mm_add_ps(__pointx, __05);
739 __pointy = _mm_add_ps(__pointy, __05);
741 if(i == mPathPointsD4) { // last cycle
742 for(int k=0; k<mPathPointsM4; k++) {
743 temp_points[k].x = static_cast<LONG>(__pointx.m128_f32[3-k]);
744 temp_points[k].y = static_cast<LONG>(__pointy.m128_f32[3-k]);
746 } else {
747 for(int k=0; k<4; k++) {
748 temp_points[k].x = static_cast<LONG>(__pointx.m128_f32[3-k]);
749 temp_points[k].y = static_cast<LONG>(__pointy.m128_f32[3-k]);
753 #endif // __ICL
756 bool CWord::CreateOpaqueBox()
758 if(m_pOpaqueBox) return(true);
759 STSStyle style = m_style.get();
760 style.borderStyle = 0;
761 style.outlineWidthX = style.outlineWidthY = 0;
762 style.colors[0] = m_style.get().colors[2];
763 style.alpha[0] = m_style.get().alpha[2];
764 int w = (int)(m_style.get().outlineWidthX + 0.5);
765 int h = (int)(m_style.get().outlineWidthY + 0.5);
766 CStringW str;
767 str.Format(L"m %d %d l %d %d %d %d %d %d",
768 -w, -h,
769 m_width+w, -h,
770 m_width+w, m_ascent+m_descent+h,
771 -w, m_ascent+m_descent+h);
772 m_pOpaqueBox.reset( new CPolygon(FwSTSStyle(style), str, 0, 0, 0, 1.0/8, 1.0/8, 0) );
773 return(!!m_pOpaqueBox);
776 bool CWord::operator==( const CWord& rhs ) const
778 return (this==&rhs) || (
779 m_str.GetId() == rhs.m_str.GetId() &&
780 m_fWhiteSpaceChar == rhs.m_fWhiteSpaceChar &&
781 m_fLineBreak == rhs.m_fLineBreak &&
782 m_style == rhs.m_style && //fix me:?
783 m_ktype == rhs.m_ktype &&
784 m_kstart == rhs.m_kstart &&
785 m_kend == rhs.m_kend &&
786 m_width == rhs.m_width &&
787 m_ascent == rhs.m_ascent &&
788 m_descent == rhs.m_descent);
789 //m_pOpaqueBox
793 // CText
795 CText::CText(const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend)
796 : CWord(style, str, ktype, kstart, kend)
798 if(m_str.Get() == L" ")
800 m_fWhiteSpaceChar = true;
802 SharedPtrTextInfo text_info;
803 TextInfoCacheKey text_info_key;
804 text_info_key.m_str_id = m_str.GetId();
805 text_info_key.m_style = m_style;
806 text_info_key.UpdateHashValue();
807 TextInfoMruCache* text_info_cache = CacheManager::GetTextInfoCache();
808 POSITION pos = text_info_cache->Lookup(text_info_key);
809 if(pos==NULL)
811 TextInfo* tmp=new TextInfo();
812 GetTextInfo(tmp, m_style, m_str.Get());
813 text_info.reset(tmp);
814 text_info_cache->UpdateCache(text_info_key, text_info);
816 else
818 text_info = text_info_cache->GetAt(pos);
819 text_info_cache->UpdateCache( pos );
821 this->m_ascent = text_info->m_ascent;
822 this->m_descent = text_info->m_descent;
823 this->m_width = text_info->m_width;
826 CText::CText( const CText& src ):CWord(src)
828 m_width = src.m_width;
831 SharedPtrCWord CText::Copy()
833 SharedPtrCWord result(new CText(*this));
834 return result;
837 bool CText::Append(const SharedPtrCWord& w)
839 boost::shared_ptr<CText> p = boost::dynamic_pointer_cast<CText>(w);
840 return (p && CWord::Append(w));
843 bool CText::CreatePath(PathData* path_data)
845 FwCMyFont font(m_style);
846 HFONT hOldFont = SelectFont(g_hDC, font.get());
847 int width = 0;
848 const CStringW& str = m_str.Get();
849 if(m_style.get().fontSpacing || (long)GetVersion() < 0)
851 bool bFirstPath = true;
852 for(LPCWSTR s = str; *s; s++)
854 CSize extent;
855 if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
856 path_data->PartialBeginPath(g_hDC, bFirstPath);
857 bFirstPath = false;
858 TextOutW(g_hDC, 0, 0, s, 1);
859 path_data->PartialEndPath(g_hDC, width, 0);
860 width += extent.cx + (int)m_style.get().fontSpacing;
863 else
865 CSize extent;
866 if(!GetTextExtentPoint32W(g_hDC, str, str.GetLength(), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
867 path_data->BeginPath(g_hDC);
868 TextOutW(g_hDC, 0, 0, str, str.GetLength());
869 path_data->EndPath(g_hDC);
871 SelectFont(g_hDC, hOldFont);
872 return(true);
875 void CText::GetTextInfo(TextInfo *output, const FwSTSStyle& style, const CStringW& str )
877 FwCMyFont font(style);
878 output->m_ascent = (int)(style.get().fontScaleY/100*font.get().m_ascent);
879 output->m_descent = (int)(style.get().fontScaleY/100*font.get().m_descent);
881 HFONT hOldFont = SelectFont(g_hDC, font.get());
882 if(style.get().fontSpacing || (long)GetVersion() < 0)
884 bool bFirstPath = true;
885 for(LPCWSTR s = str; *s; s++)
887 CSize extent;
888 if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
889 output->m_width += extent.cx + (int)style.get().fontSpacing;
891 // m_width -= (int)m_style.get().fontSpacing; // TODO: subtract only at the end of the line
893 else
895 CSize extent;
896 if(!GetTextExtentPoint32W(g_hDC, str, wcslen(str), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
897 output->m_width += extent.cx;
899 output->m_width = (int)(style.get().fontScaleX/100*output->m_width + 4) >> 3;
900 SelectFont(g_hDC, hOldFont);
903 // CPolygon
905 CPolygon::CPolygon(const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend, double scalex, double scaley, int baseline)
906 : CWord(style, str, ktype, kstart, kend)
907 , m_scalex(scalex), m_scaley(scaley), m_baseline(baseline)
909 ParseStr();
912 CPolygon::CPolygon(CPolygon& src) : CWord(src)
914 m_scalex = src.m_scalex;
915 m_scaley = src.m_scaley;
916 m_baseline = src.m_baseline;
917 m_width = src.m_width;
918 m_ascent = src.m_ascent;
919 m_descent = src.m_descent;
920 m_pathTypesOrg.Copy(src.m_pathTypesOrg);
921 m_pathPointsOrg.Copy(src.m_pathPointsOrg);
923 CPolygon::~CPolygon()
927 SharedPtrCWord CPolygon::Copy()
929 SharedPtrCWord result(DNew CPolygon(*this));
930 return result;
933 bool CPolygon::Append(const SharedPtrCWord& w)
935 // TODO
936 return(false);
939 bool CPolygon::Get6BitFixedPoint(CStringW& str, LONG& ret)
941 LPWSTR s = (LPWSTR)(LPCWSTR)str, e = s;
942 ret = wcstod(str, &e) * 64;
943 str.Delete(0,e-s);
944 XY_LOG_INFO(ret);
945 return(e > s);
948 bool CPolygon::GetPOINT(CStringW& str, POINT& ret)
950 return(Get6BitFixedPoint(str, ret.x) && Get6BitFixedPoint(str, ret.y));
953 bool CPolygon::ParseStr()
955 if(m_pathTypesOrg.GetCount() > 0) return(true);
956 CPoint p;
957 int j, lastsplinestart = -1, firstmoveto = -1, lastmoveto = -1;
958 CStringW str = m_str.Get();
959 str.SpanIncluding(L"mnlbspc 0123456789");
960 str.Replace(L"m", L"*m");
961 str.Replace(L"n", L"*n");
962 str.Replace(L"l", L"*l");
963 str.Replace(L"b", L"*b");
964 str.Replace(L"s", L"*s");
965 str.Replace(L"p", L"*p");
966 str.Replace(L"c", L"*c");
967 int k = 0;
968 for(CStringW s = str.Tokenize(L"*", k); !s.IsEmpty(); s = str.Tokenize(L"*", k))
970 WCHAR c = s[0];
971 s.TrimLeft(L"mnlbspc ");
972 switch(c)
974 case 'm':
975 lastmoveto = m_pathTypesOrg.GetCount();
976 if(firstmoveto == -1)
977 firstmoveto = lastmoveto;
978 while(GetPOINT(s, p)) {
979 m_pathTypesOrg.Add(PT_MOVETO);
980 m_pathPointsOrg.Add(p);
982 break;
983 case 'n':
984 while(GetPOINT(s, p)) {
985 m_pathTypesOrg.Add(PT_MOVETONC);
986 m_pathPointsOrg.Add(p);
988 break;
989 case 'l':
990 if (m_pathPointsOrg.GetCount() < 1) {
991 break;
993 while(GetPOINT(s, p)) {
994 m_pathTypesOrg.Add(PT_LINETO);
995 m_pathPointsOrg.Add(p);
997 break;
998 case 'b':
999 j = m_pathTypesOrg.GetCount();
1000 if (j < 1) {
1001 break;
1003 while(GetPOINT(s, p)) {
1004 m_pathTypesOrg.Add(PT_BEZIERTO);
1005 m_pathPointsOrg.Add(p);
1006 j++;
1008 j = m_pathTypesOrg.GetCount() - ((m_pathTypesOrg.GetCount()-j)%3);
1009 m_pathTypesOrg.SetCount(j);
1010 m_pathPointsOrg.SetCount(j);
1011 break;
1012 case 's':
1013 if (m_pathPointsOrg.GetCount() < 1) {
1014 break;
1017 j = lastsplinestart = m_pathTypesOrg.GetCount();
1018 int i = 3;
1019 while(i-- && GetPOINT(s, p)) {
1020 m_pathTypesOrg.Add(PT_BSPLINETO);
1021 m_pathPointsOrg.Add(p);
1022 j++;
1024 if(m_pathTypesOrg.GetCount()-lastsplinestart < 3) {
1025 m_pathTypesOrg.SetCount(lastsplinestart);
1026 m_pathPointsOrg.SetCount(lastsplinestart);
1027 lastsplinestart = -1;
1030 // no break here
1031 case 'p':
1032 if (m_pathPointsOrg.GetCount() < 3) {
1033 break;
1035 while(GetPOINT(s, p)) {
1036 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1037 m_pathPointsOrg.Add(p);
1039 break;
1040 case 'c':
1041 if(lastsplinestart > 0)
1043 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1044 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1045 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1046 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)
1047 m_pathPointsOrg.Add(p);
1048 p = m_pathPointsOrg[lastsplinestart];
1049 m_pathPointsOrg.Add(p);
1050 p = m_pathPointsOrg[lastsplinestart+1];
1051 m_pathPointsOrg.Add(p);
1052 lastsplinestart = -1;
1054 break;
1055 default:
1056 break;
1059 if(lastmoveto == -1 || firstmoveto > 0)
1061 m_pathTypesOrg.RemoveAll();
1062 m_pathPointsOrg.RemoveAll();
1063 return(false);
1065 int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
1066 for(size_t i = 0; i < m_pathTypesOrg.GetCount(); i++)
1068 m_pathPointsOrg[i].x = (int)(m_scalex * m_pathPointsOrg[i].x);
1069 m_pathPointsOrg[i].y = (int)(m_scaley * m_pathPointsOrg[i].y);
1070 if(minx > m_pathPointsOrg[i].x) minx = m_pathPointsOrg[i].x;
1071 if(miny > m_pathPointsOrg[i].y) miny = m_pathPointsOrg[i].y;
1072 if(maxx < m_pathPointsOrg[i].x) maxx = m_pathPointsOrg[i].x;
1073 if(maxy < m_pathPointsOrg[i].y) maxy = m_pathPointsOrg[i].y;
1075 m_width = max(maxx - minx, 0);
1076 m_ascent = max(maxy - miny, 0);
1077 int baseline = (int)(64 * m_scaley * m_baseline);
1078 m_descent = baseline;
1079 m_ascent -= baseline;
1080 m_width = ((int)(m_style.get().fontScaleX/100 * m_width) + 4) >> 3;
1081 m_ascent = ((int)(m_style.get().fontScaleY/100 * m_ascent) + 4) >> 3;
1082 m_descent = ((int)(m_style.get().fontScaleY/100 * m_descent) + 4) >> 3;
1083 return(true);
1086 bool CPolygon::CreatePath(PathData* path_data)
1088 int len = m_pathTypesOrg.GetCount();
1089 if(len == 0) return(false);
1090 if(path_data->mPathPoints != len)
1092 path_data->mpPathTypes = (BYTE*)realloc(path_data->mpPathTypes, len*sizeof(BYTE));
1093 path_data->mpPathPoints = (POINT*)realloc(path_data->mpPathPoints, len*sizeof(POINT));
1094 if(!path_data->mpPathTypes || !path_data->mpPathPoints) return(false);
1095 path_data->mPathPoints = len;
1097 memcpy(path_data->mpPathTypes, m_pathTypesOrg.GetData(), len*sizeof(BYTE));
1098 memcpy(path_data->mpPathPoints, m_pathPointsOrg.GetData(), len*sizeof(POINT));
1099 return(true);
1102 // CClipper
1104 CClipper::CClipper(CStringW str, CSize size, double scalex, double scaley, bool inverse)
1105 : m_polygon( new CPolygon(FwSTSStyle(), str, 0, 0, 0, scalex, scaley, 0) )
1106 , m_size(size), m_inverse(inverse)
1107 , m_effectType(-1), m_painted(false)
1112 CClipper::~CClipper()
1116 GrayImage2* CClipper::PaintSimpleClipper()
1118 GrayImage2* result = NULL;
1119 if(m_size.cx < 0 || m_size.cy < 0)
1120 return result;
1122 SharedPtrOverlay overlay;
1123 CWordPaintMachine::PaintBody( m_polygon, CPoint(0, 0), CPoint(0, 0), &overlay );
1124 int w = overlay->mOverlayWidth, h = overlay->mOverlayHeight;
1125 int x = (overlay->mOffsetX+4)>>3, y = (overlay->mOffsetY+4)>>3;
1126 result = new GrayImage2();
1127 if( !result )
1128 return result;
1129 result->data = overlay->mBody;
1130 result->pitch = overlay->mOverlayPitch;
1131 result->size.SetSize(w, h);
1132 result->left_top.SetPoint(x, y);
1133 return result;
1136 GrayImage2* CClipper::PaintBaseClipper()
1138 GrayImage2* result = NULL;
1139 //m_pAlphaMask = NULL;
1140 if(m_size.cx < 0 || m_size.cy < 0)
1141 return result;
1143 SharedPtrOverlay overlay;
1144 CWordPaintMachine::PaintBody( m_polygon, CPoint(0, 0), CPoint(0, 0), &overlay );
1145 int w = overlay->mOverlayWidth, h = overlay->mOverlayHeight;
1146 int x = (overlay->mOffsetX+4)>>3, y = (overlay->mOffsetY+4)>>3;
1147 int xo = 0, yo = 0;
1148 if(x < 0) {xo = -x; w -= -x; x = 0;}
1149 if(y < 0) {yo = -y; h -= -y; y = 0;}
1150 if(x+w > m_size.cx) w = m_size.cx-x;
1151 if(y+h > m_size.cy) h = m_size.cy-y;
1152 if(w <= 0 || h <= 0) return result;
1154 result = new GrayImage2();
1155 if( !result )
1156 return result;
1157 result->data.reset( reinterpret_cast<BYTE*>(xy_malloc(m_size.cx*m_size.cy)), xy_free );
1158 result->pitch = m_size.cx;
1159 result->size = m_size;
1160 result->left_top.SetPoint(0, 0);
1162 BYTE * result_data = result->data.get();
1163 if(!result_data)
1165 delete result;
1166 return NULL;
1169 memset( result_data, 0, m_size.cx*m_size.cy );
1171 const BYTE* src = overlay->mBody.get() + (overlay->mOverlayPitch * yo + xo);
1172 BYTE* dst = result_data + m_size.cx * y + x;
1173 while(h--)
1175 //for(int wt=0; wt<w; ++wt)
1176 // dst[wt] = src[wt];
1177 memcpy(dst, src, w);
1178 src += overlay->mOverlayPitch;
1179 dst += m_size.cx;
1181 if(m_inverse)
1183 BYTE* dst = result_data;
1184 for(int i = m_size.cx*m_size.cy; i>0; --i, ++dst)
1185 *dst = 0x40 - *dst; // mask is 6 bit
1187 return result;
1190 GrayImage2* CClipper::PaintBannerClipper()
1192 int width = m_effect.param[2];
1193 int w = m_size.cx, h = m_size.cy;
1195 GrayImage2* result = PaintBaseClipper();
1196 if(!result)
1197 return result;
1199 int da = (64<<8)/width;
1200 BYTE* am = result->data.get();
1201 for(int j = 0; j < h; j++, am += w)
1203 int a = 0;
1204 int k = min(width, w);
1205 for(int i = 0; i < k; i++, a += da)
1206 am[i] = (am[i]*a)>>14;
1207 a = 0x40<<8;
1208 k = w-width;
1209 if(k < 0) {a -= -k*da; k = 0;}
1210 for(int i = k; i < w; i++, a -= da)
1211 am[i] = (am[i]*a)>>14;
1213 return result;
1216 GrayImage2* CClipper::PaintScrollClipper()
1218 int height = m_effect.param[4];
1219 int w = m_size.cx, h = m_size.cy;
1221 GrayImage2* result = PaintBaseClipper();
1222 if(!result)
1223 return result;
1225 BYTE* data = result->data.get();
1227 int da = (64<<8)/height;
1228 int a = 0;
1229 int k = m_effect.param[0]>>3;
1230 int l = k+height;
1231 if(k < 0) {a += -k*da; k = 0;}
1232 if(l > h) {l = h;}
1233 if(k < h)
1235 BYTE* am = &data[k*w];
1236 memset(data, 0, am - data);
1237 for(int j = k; j < l; j++, a += da)
1239 for(int i = 0; i < w; i++, am++)
1240 *am = ((*am)*a)>>14;
1243 da = -(64<<8)/height;
1244 a = 0x40<<8;
1245 l = m_effect.param[1]>>3;
1246 k = l-height;
1247 if(k < 0) {a += -k*da; k = 0;}
1248 if(l > h) {l = h;}
1249 if(k < h)
1251 BYTE* am = &data[k*w];
1252 int j = k;
1253 for(; j < l; j++, a += da)
1255 for(int i = 0; i < w; i++, am++)
1256 *am = ((*am)*a)>>14;
1258 memset(am, 0, (h-j)*w);
1260 return result;
1263 GrayImage2* CClipper::Paint()
1265 GrayImage2* result = NULL;
1266 switch(m_effectType)
1268 case -1:
1269 if (!m_inverse)
1271 result = PaintSimpleClipper();
1273 else
1275 result = PaintBaseClipper();
1277 break;
1278 case EF_BANNER:
1279 result = PaintBannerClipper();
1280 break;
1281 case EF_SCROLL:
1282 result = PaintScrollClipper();
1283 break;
1285 return result;
1288 void CClipper::SetEffect( const Effect& effect, int effectType )
1290 m_effectType = effectType;
1291 m_effect = effect;
1294 SharedPtrGrayImage2 CClipper::GetAlphaMask( const SharedPtrCClipper& clipper )
1296 SharedPtrGrayImage2 result;
1297 CClipperPaintMachine paint_machine(clipper);
1298 paint_machine.Paint(&result);
1299 return result;
1302 // CLine
1304 CLine::~CLine()
1306 //POSITION pos = GetHeadPosition();
1307 //while(pos) delete GetNext(pos);
1310 void CLine::Compact()
1312 POSITION pos = GetHeadPosition();
1313 while(pos)
1315 SharedPtrCWord w = GetNext(pos);
1316 if(!w->m_fWhiteSpaceChar) break;
1317 m_width -= w->m_width;
1318 // delete w;
1319 RemoveHead();
1321 pos = GetTailPosition();
1322 while(pos)
1324 SharedPtrCWord w = GetPrev(pos);
1325 if(!w->m_fWhiteSpaceChar) break;
1326 m_width -= w->m_width;
1327 // delete w;
1328 RemoveTail();
1330 if(IsEmpty()) return;
1331 CLine l;
1332 l.AddTailList(this);
1333 RemoveAll();
1334 SharedPtrCWord last;
1335 pos = l.GetHeadPosition();
1336 while(pos)
1338 SharedPtrCWord w = l.GetNext(pos);
1339 if(!last || !last->Append(w))
1340 AddTail(last = w->Copy());
1342 m_ascent = m_descent = m_borderX = m_borderY = 0;
1343 pos = GetHeadPosition();
1344 while(pos)
1346 SharedPtrCWord w = GetNext(pos);
1347 if(m_ascent < w->m_ascent) m_ascent = w->m_ascent;
1348 if(m_descent < w->m_descent) m_descent = w->m_descent;
1349 if(m_borderX < w->m_style.get().outlineWidthX) m_borderX = (int)(w->m_style.get().outlineWidthX+0.5);
1350 if(m_borderY < w->m_style.get().outlineWidthY) m_borderY = (int)(w->m_style.get().outlineWidthY+0.5);
1354 CRect CLine::PaintAll( CompositeDrawItemList* output, const CRect& clipRect,
1355 const SharedPtrCClipperPaintMachine &clipper, CPoint p, const CPoint& org, const int time, const int alpha )
1357 CRect bbox(0, 0, 0, 0);
1358 POSITION pos = GetHeadPosition();
1359 POSITION outputPos = output->GetHeadPosition();
1360 while(pos)
1362 SharedPtrCWord w = GetNext(pos);
1363 CompositeDrawItem& outputItem = output->GetNext(outputPos);
1364 if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
1365 CPoint shadowPos, outlinePos, bodyPos, transOrg;
1366 shadowPos.x = p.x + static_cast<int>(w->m_style.get().shadowDepthX+0.5);
1367 shadowPos.y = p.y + m_ascent - w->m_ascent + static_cast<int>(w->m_style.get().shadowDepthY+0.5);
1368 outlinePos = CPoint(p.x, p.y + m_ascent - w->m_ascent);
1369 bodyPos = CPoint(p.x, p.y + m_ascent - w->m_ascent);
1370 bool hasShadow = w->m_style.get().shadowDepthX != 0 || w->m_style.get().shadowDepthY != 0;
1371 bool hasOutline = w->m_style.get().outlineWidthX+w->m_style.get().outlineWidthY > 0 && !(w->m_ktype == 2 && time < w->m_kstart);
1372 bool hasBody = true;
1374 SharedPtrOverlayPaintMachine shadow_pm, outline_pm, body_pm;
1375 CWordPaintMachine::CreatePaintMachines(w, shadowPos, outlinePos, bodyPos, org,
1376 hasShadow ? &shadow_pm : NULL,
1377 hasOutline ? &outline_pm : NULL,
1378 hasBody ? &body_pm : NULL);
1380 //shadow
1381 if(hasShadow)
1383 DWORD a = 0xff - w->m_style.get().alpha[3];
1384 if(alpha > 0) a = MulDiv(a, 0xff - alpha, 0xff);
1385 COLORREF shadow = revcolor(w->m_style.get().colors[3]) | (a<<24);
1386 DWORD sw[6] = {shadow, -1};
1387 sw[0] = XySubRenderFrameCreater::GetDefaultCreater()->TransColor(sw[0]);
1388 if(w->m_style.get().borderStyle == 0)
1390 outputItem.shadow.reset(
1391 DrawItem::CreateDrawItem(shadow_pm, clipRect, clipper, shadowPos.x, shadowPos.y, sw,
1392 w->m_ktype > 0 || w->m_style.get().alpha[0] < 0xff,
1393 (w->m_style.get().outlineWidthX+w->m_style.get().outlineWidthY > 0) && !(w->m_ktype == 2 && time < w->m_kstart))
1396 else if(w->m_style.get().borderStyle == 1)
1398 outputItem.shadow.reset(
1399 DrawItem::CreateDrawItem( shadow_pm, clipRect, clipper, shadowPos.x, shadowPos.y, sw, true, false)
1403 //outline
1404 if(hasOutline)
1406 DWORD aoutline = w->m_style.get().alpha[2];
1407 if(alpha > 0) aoutline += MulDiv(alpha, 0xff - w->m_style.get().alpha[2], 0xff);
1408 COLORREF outline = revcolor(w->m_style.get().colors[2]) | ((0xff-aoutline)<<24);
1409 DWORD sw[6] = {outline, -1};
1410 sw[0] = XySubRenderFrameCreater::GetDefaultCreater()->TransColor(sw[0]);
1411 if(w->m_style.get().borderStyle == 0)
1413 outputItem.outline.reset(
1414 DrawItem::CreateDrawItem(outline_pm, clipRect, clipper, outlinePos.x, outlinePos.y, sw, !w->m_style.get().alpha[0] && !w->m_style.get().alpha[1] && !alpha, true)
1417 else if(w->m_style.get().borderStyle == 1)
1419 outputItem.outline.reset(
1420 DrawItem::CreateDrawItem(outline_pm, clipRect, clipper, outlinePos.x, outlinePos.y, sw, true, false)
1424 //body
1425 if(hasBody)
1427 // colors
1428 DWORD aprimary = w->m_style.get().alpha[0];
1429 if(alpha > 0) aprimary += MulDiv(alpha, 0xff - w->m_style.get().alpha[0], 0xff);
1430 COLORREF primary = revcolor(w->m_style.get().colors[0]) | ((0xff-aprimary)<<24);
1431 DWORD asecondary = w->m_style.get().alpha[1];
1432 if(alpha > 0) asecondary += MulDiv(alpha, 0xff - w->m_style.get().alpha[1], 0xff);
1433 COLORREF secondary = revcolor(w->m_style.get().colors[1]) | ((0xff-asecondary)<<24);
1434 DWORD sw[6] = {primary, 0, secondary};
1435 // karaoke
1436 double t;
1437 if(w->m_ktype == 0 || w->m_ktype == 2)
1439 t = time < w->m_kstart ? 0 : 1;
1441 else if(w->m_ktype == 1)
1443 if(time < w->m_kstart) t = 0;
1444 else if(time < w->m_kend)
1446 t = 1.0 * (time - w->m_kstart) / (w->m_kend - w->m_kstart);
1447 double angle = fmod(w->m_style.get().fontAngleZ, 360.0);
1448 if(angle > 90 && angle < 270)
1450 t = 1-t;
1451 COLORREF tmp = sw[0];
1452 sw[0] = sw[2];
1453 sw[2] = tmp;
1456 else t = 1.0;
1458 if(t >= 1)
1460 sw[1] = 0xffffffff;
1462 sw[3] = (int)(w->m_style.get().outlineWidthX + t*w->m_width) >> 3;
1463 sw[4] = sw[2];
1464 sw[5] = 0x00ffffff;
1465 sw[0] = XySubRenderFrameCreater::GetDefaultCreater()->TransColor(sw[0]);
1466 sw[2] = XySubRenderFrameCreater::GetDefaultCreater()->TransColor(sw[2]);
1467 sw[4] = XySubRenderFrameCreater::GetDefaultCreater()->TransColor(sw[4]);
1468 outputItem.body.reset(
1469 DrawItem::CreateDrawItem(body_pm, clipRect, clipper, bodyPos.x, bodyPos.y, sw, true, false)
1472 bbox |= CompositeDrawItem::GetDirtyRect(outputItem);
1473 p.x += w->m_width;
1475 return(bbox);
1478 void CLine::AddWord2Tail( SharedPtrCWord words )
1480 __super::AddTail(words);
1483 bool CLine::IsEmpty()
1485 return __super::IsEmpty();
1488 int CLine::GetWordCount()
1490 return GetCount();
1493 // CSubtitle
1495 CSubtitle::CSubtitle()
1497 memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
1498 m_clipInverse = false;
1499 m_scalex = m_scaley = 1;
1500 m_fAnimated2 = false;
1503 CSubtitle::~CSubtitle()
1505 Empty();
1508 void CSubtitle::Empty()
1510 POSITION pos = GetHeadPosition();
1511 while(pos) delete GetNext(pos);
1512 // pos = m_words.GetHeadPosition();
1513 // while(pos) delete m_words.GetNext(pos);
1514 for(int i = 0; i < EF_NUMBEROFEFFECTS; i++) {if(m_effects[i]) delete m_effects[i];}
1515 memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
1518 int CSubtitle::GetFullWidth()
1520 int width = 0;
1521 POSITION pos = m_words.GetHeadPosition();
1522 while(pos) width += m_words.GetNext(pos)->m_width;
1523 return(width);
1526 int CSubtitle::GetFullLineWidth(POSITION pos)
1528 int width = 0;
1529 while(pos)
1531 SharedPtrCWord w = m_words.GetNext(pos);
1532 if(w->m_fLineBreak) break;
1533 width += w->m_width;
1535 return(width);
1538 int CSubtitle::GetWrapWidth(POSITION pos, int maxwidth)
1540 if(m_wrapStyle == 0 || m_wrapStyle == 3)
1542 if(maxwidth > 0)
1544 // int fullwidth = GetFullWidth();
1545 int fullwidth = GetFullLineWidth(pos);
1546 int minwidth = fullwidth / ((abs(fullwidth) / maxwidth) + 1);
1547 int width = 0, wordwidth = 0;
1548 while(pos && width < minwidth)
1550 SharedPtrCWord w = m_words.GetNext(pos);
1551 wordwidth = w->m_width;
1552 if(abs(width + wordwidth) < abs(maxwidth)) width += wordwidth;
1554 maxwidth = width;
1555 if(m_wrapStyle == 3 && pos) maxwidth -= wordwidth;
1558 else if(m_wrapStyle == 1)
1560 // maxwidth = maxwidth;
1562 else if(m_wrapStyle == 2)
1564 maxwidth = INT_MAX;
1566 return(maxwidth);
1569 CLine* CSubtitle::GetNextLine(POSITION& pos, int maxwidth)
1571 if(pos == NULL) return(NULL);
1572 CLine* ret = new CLine();
1573 if(!ret) return(NULL);
1574 ret->m_width = ret->m_ascent = ret->m_descent = ret->m_borderX = ret->m_borderY = 0;
1575 maxwidth = GetWrapWidth(pos, maxwidth);
1576 bool fEmptyLine = true;
1577 while(pos)
1579 SharedPtrCWord w = m_words.GetNext(pos);
1580 if(ret->m_ascent < w->m_ascent) ret->m_ascent = w->m_ascent;
1581 if(ret->m_descent < w->m_descent) ret->m_descent = w->m_descent;
1582 if(ret->m_borderX < w->m_style.get().outlineWidthX) ret->m_borderX = (int)(w->m_style.get().outlineWidthX+0.5);
1583 if(ret->m_borderY < w->m_style.get().outlineWidthY) ret->m_borderY = (int)(w->m_style.get().outlineWidthY+0.5);
1584 if(w->m_fLineBreak)
1586 if(fEmptyLine) {ret->m_ascent /= 2; ret->m_descent /= 2; ret->m_borderX = ret->m_borderY = 0;}
1587 ret->Compact();
1588 return(ret);
1590 fEmptyLine = false;
1591 bool fWSC = w->m_fWhiteSpaceChar;
1592 int width = w->m_width;
1593 POSITION pos2 = pos;
1594 while(pos2)
1596 if(m_words.GetAt(pos2)->m_fWhiteSpaceChar != fWSC
1597 || m_words.GetAt(pos2)->m_fLineBreak) break;
1598 SharedPtrCWord w2 = m_words.GetNext(pos2);
1599 width += w2->m_width;
1601 if((ret->m_width += width) <= maxwidth || ret->IsEmpty())
1603 ret->AddWord2Tail(w);
1604 while(pos != pos2)
1606 ret->AddWord2Tail(m_words.GetNext(pos));
1608 pos = pos2;
1610 else
1612 if(pos) m_words.GetPrev(pos);
1613 else pos = m_words.GetTailPosition();
1614 ret->m_width -= width;
1615 break;
1618 ret->Compact();
1619 return(ret);
1622 void CSubtitle::CreateClippers(CSize size)
1624 size.cx >>= 3;
1625 size.cy >>= 3;
1626 if(m_effects[EF_BANNER] && m_effects[EF_BANNER]->param[2])
1628 int w = size.cx, h = size.cy;
1629 if(!m_pClipper)
1631 CStringW str;
1632 str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
1633 m_pClipper.reset( new CClipper(str, size, 1, 1, false) );
1634 if(!m_pClipper) return;
1636 m_pClipper->SetEffect( *m_effects[EF_BANNER], EF_BANNER );
1638 else if(m_effects[EF_SCROLL] && m_effects[EF_SCROLL]->param[4])
1640 int height = m_effects[EF_SCROLL]->param[4];
1641 int w = size.cx, h = size.cy;
1642 if(!m_pClipper)
1644 CStringW str;
1645 str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
1646 m_pClipper.reset( new CClipper(str, size, 1, 1, false) );
1647 if(!m_pClipper) return;
1649 m_pClipper->SetEffect(*m_effects[EF_SCROLL], EF_SCROLL);
1653 void CSubtitle::MakeLines(CSize size, CRect marginRect)
1655 CSize spaceNeeded(0, 0);
1656 bool fFirstLine = true;
1657 m_topborder = m_bottomborder = 0;
1658 CLine* l = NULL;
1659 POSITION pos = m_words.GetHeadPosition();
1660 while(pos)
1662 l = GetNextLine(pos, size.cx - marginRect.left - marginRect.right);
1663 if(!l) break;
1664 if(fFirstLine) {m_topborder = l->m_borderY; fFirstLine = false;}
1665 spaceNeeded.cx = max(l->m_width+l->m_borderX, spaceNeeded.cx);
1666 spaceNeeded.cy += l->m_ascent + l->m_descent;
1667 AddTail(l);
1669 if(l) m_bottomborder = l->m_borderY;
1670 m_rect = CRect(
1671 CPoint((m_scrAlignment%3) == 1 ? marginRect.left
1672 : (m_scrAlignment%3) == 2 ? (marginRect.left + (size.cx - marginRect.right) - spaceNeeded.cx + 1) / 2
1673 : (size.cx - marginRect.right - spaceNeeded.cx),
1674 m_scrAlignment <= 3 ? (size.cy - marginRect.bottom - spaceNeeded.cy)
1675 : m_scrAlignment <= 6 ? (marginRect.top + (size.cy - marginRect.bottom) - spaceNeeded.cy + 1) / 2
1676 : marginRect.top),
1677 spaceNeeded);
1680 POSITION CSubtitle::GetHeadLinePosition()
1682 return __super::GetHeadPosition();
1685 CLine* CSubtitle::GetNextLine( POSITION& pos )
1687 return __super::GetNext(pos);
1690 // CScreenLayoutAllocator
1692 void CScreenLayoutAllocator::Empty()
1694 m_subrects.RemoveAll();
1697 void CScreenLayoutAllocator::AdvanceToSegment(int segment, const CAtlArray<int>& sa)
1699 POSITION pos = m_subrects.GetHeadPosition();
1700 while(pos)
1702 POSITION prev = pos;
1703 SubRect& sr = m_subrects.GetNext(pos);
1704 bool fFound = false;
1705 if(abs(sr.segment - segment) <= 1) // using abs() makes it possible to play the subs backwards, too :)
1707 for(size_t i = 0; i < sa.GetCount() && !fFound; i++)
1709 if(sa[i] == sr.entry)
1711 sr.segment = segment;
1712 fFound = true;
1716 if(!fFound) m_subrects.RemoveAt(prev);
1720 CRect CScreenLayoutAllocator::AllocRect(CSubtitle* s, int segment, int entry, int layer, int collisions)
1722 // TODO: handle collisions == 1 (reversed collisions)
1723 POSITION pos = m_subrects.GetHeadPosition();
1724 while(pos)
1726 SubRect& sr = m_subrects.GetNext(pos);
1727 if(sr.segment == segment && sr.entry == entry)
1729 return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
1732 CRect r = s->m_rect + CRect(0, s->m_topborder, 0, s->m_bottomborder);
1733 bool fSearchDown = s->m_scrAlignment > 3;
1734 bool fOK;
1737 fOK = true;
1738 pos = m_subrects.GetHeadPosition();
1739 while(pos)
1741 SubRect& sr = m_subrects.GetNext(pos);
1742 if(layer == sr.layer && !(r & sr.r).IsRectEmpty())
1744 if(fSearchDown)
1746 r.bottom = sr.r.bottom + r.Height();
1747 r.top = sr.r.bottom;
1749 else
1751 r.top = sr.r.top - r.Height();
1752 r.bottom = sr.r.top;
1754 fOK = false;
1758 while(!fOK);
1759 SubRect sr;
1760 sr.r = r;
1761 sr.segment = segment;
1762 sr.entry = entry;
1763 sr.layer = layer;
1764 m_subrects.AddTail(sr);
1765 return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
1768 // CRenderedTextSubtitle
1770 CAtlMap<CStringW, CRenderedTextSubtitle::AssCmdType, CStringElementTraits<CStringW>> CRenderedTextSubtitle::m_cmdMap;
1772 CRenderedTextSubtitle::CRenderedTextSubtitle(CCritSec* pLock)
1773 : CSubPicProviderImpl(pLock)
1775 if( m_cmdMap.IsEmpty() )
1777 InitCmdMap();
1779 m_size = CSize(0, 0);
1780 if(g_hDC_refcnt == 0)
1782 g_hDC = CreateCompatibleDC(NULL);
1783 SetBkMode(g_hDC, TRANSPARENT);
1784 SetTextColor(g_hDC, 0xffffff);
1785 SetMapMode(g_hDC, MM_TEXT);
1787 g_hDC_refcnt++;
1790 CRenderedTextSubtitle::~CRenderedTextSubtitle()
1792 Deinit();
1793 g_hDC_refcnt--;
1794 if(g_hDC_refcnt == 0) DeleteDC(g_hDC);
1797 void CRenderedTextSubtitle::InitCmdMap()
1799 if( m_cmdMap.IsEmpty() )
1801 m_cmdMap.SetAt(L"1c", CMD_1c);
1802 m_cmdMap.SetAt(L"2c", CMD_2c);
1803 m_cmdMap.SetAt(L"3c", CMD_3c);
1804 m_cmdMap.SetAt(L"4c", CMD_4c);
1805 m_cmdMap.SetAt(L"1a", CMD_1a);
1806 m_cmdMap.SetAt(L"2a", CMD_2a);
1807 m_cmdMap.SetAt(L"3a", CMD_3a);
1808 m_cmdMap.SetAt(L"4a", CMD_4a);
1809 m_cmdMap.SetAt(L"alpha", CMD_alpha);
1810 m_cmdMap.SetAt(L"an", CMD_an);
1811 m_cmdMap.SetAt(L"a", CMD_a);
1812 m_cmdMap.SetAt(L"blur", CMD_blur);
1813 m_cmdMap.SetAt(L"bord", CMD_bord);
1814 m_cmdMap.SetAt(L"be", CMD_be);
1815 m_cmdMap.SetAt(L"b", CMD_b);
1816 m_cmdMap.SetAt(L"clip", CMD_clip);
1817 m_cmdMap.SetAt(L"iclip", CMD_iclip);
1818 m_cmdMap.SetAt(L"c", CMD_c);
1819 m_cmdMap.SetAt(L"fade", CMD_fade);
1820 m_cmdMap.SetAt(L"fad", CMD_fad);
1821 m_cmdMap.SetAt(L"fax", CMD_fax);
1822 m_cmdMap.SetAt(L"fay", CMD_fay);
1823 m_cmdMap.SetAt(L"fe", CMD_fe);
1824 m_cmdMap.SetAt(L"fn", CMD_fn);
1825 m_cmdMap.SetAt(L"frx", CMD_frx);
1826 m_cmdMap.SetAt(L"fry", CMD_fry);
1827 m_cmdMap.SetAt(L"frz", CMD_frz);
1828 m_cmdMap.SetAt(L"fr", CMD_fr);
1829 m_cmdMap.SetAt(L"fscx", CMD_fscx);
1830 m_cmdMap.SetAt(L"fscy", CMD_fscy);
1831 m_cmdMap.SetAt(L"fsc", CMD_fsc);
1832 m_cmdMap.SetAt(L"fsp", CMD_fsp);
1833 m_cmdMap.SetAt(L"fs", CMD_fs);
1834 m_cmdMap.SetAt(L"i", CMD_i);
1835 m_cmdMap.SetAt(L"kt", CMD_kt);
1836 m_cmdMap.SetAt(L"kf", CMD_kf);
1837 m_cmdMap.SetAt(L"K", CMD_K);
1838 m_cmdMap.SetAt(L"ko", CMD_ko);
1839 m_cmdMap.SetAt(L"k", CMD_k);
1840 m_cmdMap.SetAt(L"move", CMD_move);
1841 m_cmdMap.SetAt(L"org", CMD_org);
1842 m_cmdMap.SetAt(L"pbo", CMD_pbo);
1843 m_cmdMap.SetAt(L"pos", CMD_pos);
1844 m_cmdMap.SetAt(L"p", CMD_p);
1845 m_cmdMap.SetAt(L"q", CMD_q);
1846 m_cmdMap.SetAt(L"r", CMD_r);
1847 m_cmdMap.SetAt(L"shad", CMD_shad);
1848 m_cmdMap.SetAt(L"s", CMD_s);
1849 m_cmdMap.SetAt(L"t", CMD_t);
1850 m_cmdMap.SetAt(L"u", CMD_u);
1851 m_cmdMap.SetAt(L"xbord", CMD_xbord);
1852 m_cmdMap.SetAt(L"xshad", CMD_xshad);
1853 m_cmdMap.SetAt(L"ybord", CMD_ybord);
1854 m_cmdMap.SetAt(L"yshad", CMD_yshad);
1858 void CRenderedTextSubtitle::Copy(CSimpleTextSubtitle& sts)
1860 __super::Copy(sts);
1861 m_size = CSize(0, 0);
1862 if(CRenderedTextSubtitle* pRTS = dynamic_cast<CRenderedTextSubtitle*>(&sts))
1864 m_size = pRTS->m_size;
1868 void CRenderedTextSubtitle::Empty()
1870 Deinit();
1871 __super::Empty();
1874 void CRenderedTextSubtitle::OnChanged()
1876 __super::OnChanged();
1877 POSITION pos = m_subtitleCache.GetStartPosition();
1878 while(pos)
1880 int i;
1881 CSubtitle* s;
1882 m_subtitleCache.GetNextAssoc(pos, i, s);
1883 delete s;
1885 m_subtitleCache.RemoveAll();
1886 m_sla.Empty();
1889 bool CRenderedTextSubtitle::Init(CSize size, CRect vidrect)
1891 Deinit();
1892 m_size = CSize(size.cx*8, size.cy*8);
1893 m_vidrect = CRect(vidrect.left*8, vidrect.top*8, vidrect.right*8, vidrect.bottom*8);
1894 m_sla.Empty();
1895 return(true);
1898 void CRenderedTextSubtitle::Deinit()
1900 POSITION pos = m_subtitleCache.GetStartPosition();
1901 while(pos)
1903 int i;
1904 CSubtitle* s;
1905 m_subtitleCache.GetNextAssoc(pos, i, s);
1906 delete s;
1908 m_subtitleCache.RemoveAll();
1909 m_sla.Empty();
1910 m_size = CSize(0, 0);
1911 m_vidrect.SetRectEmpty();
1913 CacheManager::GetPathDataMruCache()->RemoveAll();
1914 CacheManager::GetScanLineData2MruCache()->RemoveAll();
1915 CacheManager::GetOverlayNoBlurMruCache()->RemoveAll();
1916 CacheManager::GetOverlayMruCache()->RemoveAll();
1917 CacheManager::GetAssTagListMruCache()->RemoveAll();
1918 CacheManager::GetSubpixelVarianceCache()->RemoveAll();
1919 CacheManager::GetTextInfoCache()->RemoveAll();
1922 void CRenderedTextSubtitle::ParseEffect(CSubtitle* sub, const CStringW& str)
1924 CStringW::PCXSTR str_start = str.GetString();
1925 CStringW::PCXSTR str_end = str_start + str.GetLength();
1926 str_start = SkipWhiteSpaceLeft(str_start, str_end);
1928 if(!sub || *str_start==0)
1929 return;
1931 str_end = FastSkipWhiteSpaceRight(str_start, str_end);
1933 const WCHAR* s = FindChar(str_start, str_end, L';');
1934 if(*s==L';') {
1935 s++;
1938 const CStringW effect(str_start, s-str_start);
1939 if(!effect.CompareNoCase( L"Banner;" ) )
1941 int delay, lefttoright = 0, fadeawaywidth = 0;
1942 if(swscanf(s, L"%d;%d;%d", &delay, &lefttoright, &fadeawaywidth) < 1) return;
1943 Effect* e = new Effect;
1944 if(!e) return;
1945 sub->m_effects[e->type = EF_BANNER] = e;
1946 e->param[0] = (int)(max(1.0*delay/sub->m_scalex, 1));
1947 e->param[1] = lefttoright;
1948 e->param[2] = (int)(sub->m_scalex*fadeawaywidth);
1949 sub->m_wrapStyle = 2;
1951 else if(!effect.CompareNoCase(L"Scroll up;") || !effect.CompareNoCase(L"Scroll down;"))
1953 int top, bottom, delay, fadeawayheight = 0;
1954 if(swscanf(s, L"%d;%d;%d;%d", &top, &bottom, &delay, &fadeawayheight) < 3) return;
1955 if(top > bottom) {int tmp = top; top = bottom; bottom = tmp;}
1956 Effect* e = new Effect;
1957 if(!e) return;
1958 sub->m_effects[e->type = EF_SCROLL] = e;
1959 e->param[0] = (int)(sub->m_scaley*top*8);
1960 e->param[1] = (int)(sub->m_scaley*bottom*8);
1961 e->param[2] = (int)(max(1.0*delay/sub->m_scaley, 1));
1962 e->param[3] = (effect.GetLength() == 12);
1963 e->param[4] = (int)(sub->m_scaley*fadeawayheight);
1967 void CRenderedTextSubtitle::ParseString(CSubtitle* sub, CStringW str, const FwSTSStyle& style)
1969 if(!sub) return;
1970 str.Replace(L"\\N", L"\n");
1971 str.Replace(L"\\n", (sub->m_wrapStyle < 2 || sub->m_wrapStyle == 3) ? L" " : L"\n");
1972 str.Replace(L"\\h", L"\x00A0");
1973 for(int ite = 0, j = 0, len = str.GetLength(); j <= len; j++)
1975 WCHAR c = str[j];
1976 if(c != L'\n' && c != L' ' && c != 0)
1977 continue;
1978 if(ite < j)
1980 if(PCWord tmp_ptr = new CText(style, str.Mid(ite, j-ite), m_ktype, m_kstart, m_kend))
1982 SharedPtrCWord w(tmp_ptr);
1983 sub->m_words.AddTail(w);
1985 else
1987 ///TODO: overflow handling
1989 m_kstart = m_kend;
1991 if(c == L'\n')
1993 if(PCWord tmp_ptr = new CText(style, CStringW(), m_ktype, m_kstart, m_kend))
1995 SharedPtrCWord w(tmp_ptr);
1996 sub->m_words.AddTail(w);
1998 else
2000 ///TODO: overflow handling
2002 m_kstart = m_kend;
2004 else if(c == L' ')
2006 if(PCWord tmp_ptr = new CText(style, CStringW(c), m_ktype, m_kstart, m_kend))
2008 SharedPtrCWord w(tmp_ptr);
2009 sub->m_words.AddTail(w);
2011 else
2013 ///TODO: overflow handling
2015 m_kstart = m_kend;
2017 ite = j+1;
2019 return;
2022 void CRenderedTextSubtitle::ParsePolygon(CSubtitle* sub, const CStringW& str, const FwSTSStyle& style)
2024 if(!sub || !str.GetLength() || !m_nPolygon) return;
2026 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))
2028 SharedPtrCWord w(tmp_ptr);
2029 ///Todo: fix me
2030 //if( PCWord w_cache = m_wordCache.lookup(*w) )
2032 // sub->m_words.AddTail(w_cache);
2033 // delete w;
2035 //else
2037 sub->m_words.AddTail(w);
2039 m_kstart = m_kend;
2043 bool CRenderedTextSubtitle::ParseSSATag( AssTagList *assTags, const CStringW& str )
2045 if(!assTags) return(false);
2046 int nTags = 0, nUnrecognizedTags = 0;
2047 for(int i = 0, j; (j = str.Find(L'\\', i)) >= 0; i = j)
2049 POSITION pos = assTags->AddTail();
2050 AssTag& assTag = assTags->GetAt(pos);
2051 assTag.cmdType = CMD_COUNT;
2053 j++;
2054 CStringW::PCXSTR str_start = str.GetString() + j;
2055 CStringW::PCXSTR pc = str_start;
2056 while( iswspace(*pc) )
2058 pc++;
2060 j += pc-str_start;
2061 str_start = pc;
2062 while( *pc && *pc != L'(' && *pc != L'\\' )
2064 pc++;
2066 j += pc-str_start;
2067 if( pc-str_start>0 )
2069 while( iswspace(*--pc) );
2070 pc++;
2073 const CStringW cmd(str_start, pc-str_start);
2074 if(cmd.IsEmpty()) continue;
2076 CAtlArray<CStringW>& params = assTag.strParams;
2077 if(str[j] == L'(')
2079 j++;
2080 CStringW::PCXSTR str_start = str.GetString() + j;
2081 CStringW::PCXSTR pc = str_start;
2082 while( iswspace(*pc) )
2084 pc++;
2086 j += pc-str_start;
2087 str_start = pc;
2088 while( *pc && *pc != L')' )
2090 pc++;
2092 j += pc-str_start;
2093 if( pc-str_start>0 )
2095 while( iswspace(*--pc) );
2096 pc++;
2099 CStringW::PCXSTR param_start = str_start;
2100 CStringW::PCXSTR param_end = pc;
2101 while( param_start<param_end )
2103 param_start = SkipWhiteSpaceLeft(param_start, param_end);
2105 CStringW::PCXSTR newstart = FindChar(param_start, param_end, L',');
2106 CStringW::PCXSTR newend = FindChar(param_start, param_end, L'\\');
2107 if(newstart > param_start && newstart < newend)
2109 newstart = FastSkipWhiteSpaceRight(param_start, newstart);
2110 CStringW s(param_start, newstart - param_start);
2112 if(!s.IsEmpty()) params.Add(s);
2113 param_start = newstart + 1;
2115 else if(param_start<param_end)
2117 CStringW s(param_start, param_end - param_start);
2119 params.Add(s);
2120 param_start = param_end;
2125 AssCmdType cmd_type = CMD_COUNT;
2126 int cmd_length = min(MAX_CMD_LENGTH, cmd.GetLength());
2127 for( ;cmd_length>=MIN_CMD_LENGTH;cmd_length-- )
2129 if( m_cmdMap.Lookup(cmd.Left(cmd_length), cmd_type) )
2130 break;
2132 if(cmd_length<MIN_CMD_LENGTH)
2133 cmd_type = CMD_COUNT;
2134 switch( cmd_type )
2136 case CMD_fax:
2137 case CMD_fay:
2138 case CMD_fe:
2139 case CMD_fn:
2140 case CMD_frx:
2141 case CMD_fry:
2142 case CMD_frz:
2143 case CMD_fr:
2144 case CMD_fscx:
2145 case CMD_fscy:
2146 case CMD_fsc:
2147 case CMD_fsp:
2148 case CMD_fs:
2149 case CMD_i:
2150 case CMD_kt:
2151 case CMD_kf:
2152 case CMD_K:
2153 case CMD_ko:
2154 case CMD_k:
2155 case CMD_pbo:
2156 case CMD_p:
2157 case CMD_q:
2158 case CMD_r:
2159 case CMD_shad:
2160 case CMD_s:
2161 case CMD_an:
2162 case CMD_a:
2163 case CMD_blur:
2164 case CMD_bord:
2165 case CMD_be:
2166 case CMD_b:
2167 case CMD_u:
2168 case CMD_xbord:
2169 case CMD_xshad:
2170 case CMD_ybord:
2171 case CMD_yshad:
2172 // default:
2173 params.Add(cmd.Mid(cmd_length));
2174 break;
2175 case CMD_c:
2176 case CMD_1c :
2177 case CMD_2c :
2178 case CMD_3c :
2179 case CMD_4c :
2180 case CMD_1a :
2181 case CMD_2a :
2182 case CMD_3a :
2183 case CMD_4a :
2184 case CMD_alpha:
2185 params.Add(cmd.Mid(cmd_length).Trim(L"&H"));
2186 break;
2187 case CMD_clip:
2188 case CMD_iclip:
2189 case CMD_fade:
2190 case CMD_fad:
2191 case CMD_move:
2192 case CMD_org:
2193 case CMD_pos:
2194 break;
2195 case CMD_t:
2196 if (!params.IsEmpty())
2197 ParseSSATag(&assTag.embeded, params[params.GetCount()-1]);
2198 break;
2199 case CMD_COUNT:
2200 nUnrecognizedTags++;
2201 break;
2204 assTag.cmdType = cmd_type;
2206 nTags++;
2208 return(true);
2211 bool CRenderedTextSubtitle::ParseSSATag( CSubtitle* sub, const AssTagList& assTags, STSStyle& style, const STSStyle& org, bool fAnimate /*= false*/ )
2213 if(!sub) return(false);
2215 POSITION pos = assTags.GetHeadPosition();
2216 while(pos)
2218 const AssTag& assTag = assTags.GetNext(pos);
2219 AssCmdType cmd_type = assTag.cmdType;
2220 const CAtlArray<CStringW>& params = assTag.strParams;
2222 // TODO: call ParseStyleModifier(cmd, params, ..) and move the rest there
2223 const CStringW& p = params.GetCount() > 0 ? params[0] : CStringW("");
2224 switch ( cmd_type )
2226 case CMD_1c :
2228 const int i = 0;
2229 DWORD c = wcstol(p, NULL, 16);
2230 style.colors[i] = !p.IsEmpty()
2231 ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
2232 |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
2233 |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
2234 : org.colors[i];
2235 break;
2237 case CMD_2c :
2239 const int i = 1;
2240 DWORD c = wcstol(p, NULL, 16);
2241 style.colors[i] = !p.IsEmpty()
2242 ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
2243 |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
2244 |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
2245 : org.colors[i];
2246 break;
2248 case CMD_3c :
2250 const int i = 2;
2251 DWORD c = wcstol(p, NULL, 16);
2252 style.colors[i] = !p.IsEmpty()
2253 ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
2254 |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
2255 |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
2256 : org.colors[i];
2257 break;
2259 case CMD_4c :
2261 const int i = 3;
2262 DWORD c = wcstol(p, NULL, 16);
2263 style.colors[i] = !p.IsEmpty()
2264 ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
2265 |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
2266 |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
2267 : org.colors[i];
2268 break;
2270 case CMD_1a :
2272 int i = 0;
2273 style.alpha[i] = !p.IsEmpty()
2274 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2275 : org.alpha[i];
2276 break;
2278 case CMD_2a :
2280 int i = 1;
2281 style.alpha[i] = !p.IsEmpty()
2282 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2283 : org.alpha[i];
2284 break;
2286 case CMD_3a :
2288 int i = 2;
2289 style.alpha[i] = !p.IsEmpty()
2290 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2291 : org.alpha[i];
2292 break;
2294 case CMD_4a :
2296 int i = 3;
2297 style.alpha[i] = !p.IsEmpty()
2298 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2299 : org.alpha[i];
2300 break;
2302 case CMD_alpha:
2304 for(int i = 0; i < 4; i++)
2306 style.alpha[i] = !p.IsEmpty()
2307 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2308 : org.alpha[i];
2310 break;
2312 case CMD_an:
2314 int n = wcstol(p, NULL, 10);
2315 if(sub->m_scrAlignment < 0)
2316 sub->m_scrAlignment = (n > 0 && n < 10) ? n : org.scrAlignment;
2317 break;
2319 case CMD_a:
2321 int n = wcstol(p, NULL, 10);
2322 if(sub->m_scrAlignment < 0)
2323 sub->m_scrAlignment = (n > 0 && n < 12) ? ((((n-1)&3)+1)+((n&4)?6:0)+((n&8)?3:0)) : org.scrAlignment;
2324 break;
2326 case CMD_blur:
2328 double n = CalcAnimation(wcstod(p, NULL), style.fGaussianBlur, fAnimate);
2329 style.fGaussianBlur = !p.IsEmpty()
2330 ? (n < 0 ? 0 : n)
2331 : org.fGaussianBlur;
2332 break;
2334 case CMD_bord:
2336 double dst = wcstod(p, NULL);
2337 double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate);
2338 style.outlineWidthX = !p.IsEmpty()
2339 ? (nx < 0 ? 0 : nx)
2340 : org.outlineWidthX;
2341 double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate);
2342 style.outlineWidthY = !p.IsEmpty()
2343 ? (ny < 0 ? 0 : ny)
2344 : org.outlineWidthY;
2345 break;
2347 case CMD_be:
2349 int n = (int)(CalcAnimation(wcstod(p, NULL), style.fBlur, fAnimate)+0.5);
2350 style.fBlur = !p.IsEmpty()
2352 : org.fBlur;
2353 break;
2355 case CMD_b:
2357 int n = wcstol(p, NULL, 10);
2358 style.fontWeight = !p.IsEmpty()
2359 ? (n == 0 ? FW_NORMAL : n == 1 ? FW_BOLD : n >= 100 ? n : org.fontWeight)
2360 : org.fontWeight;
2361 break;
2363 case CMD_clip:
2364 case CMD_iclip:
2366 bool invert = (cmd_type == CMD_iclip);
2367 if(params.GetCount() == 1 && !sub->m_pClipper)
2369 sub->m_pClipper.reset( new CClipper(params[0], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex, sub->m_scaley, invert) );
2371 else if(params.GetCount() == 2 && !sub->m_pClipper)
2373 int scale = max(wcstol(p, NULL, 10), 1);
2374 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) );
2376 else if(params.GetCount() == 4)
2378 CRect r;
2379 sub->m_clipInverse = invert;
2380 r.SetRect(
2381 wcstod(params[0], NULL)+0.5,
2382 wcstod(params[1], NULL)+0.5,
2383 wcstod(params[2], NULL)+0.5,
2384 wcstod(params[3], NULL)+0.5);
2385 CPoint o(0, 0);
2386 if(sub->m_relativeTo == 1) // TODO: this should also apply to the other two clippings above
2388 o.x = m_vidrect.left>>3;
2389 o.y = m_vidrect.top>>3;
2391 sub->m_clip.SetRect(
2392 (int)CalcAnimation(sub->m_scalex*r.left + o.x, sub->m_clip.left, fAnimate),
2393 (int)CalcAnimation(sub->m_scaley*r.top + o.y, sub->m_clip.top, fAnimate),
2394 (int)CalcAnimation(sub->m_scalex*r.right + o.x, sub->m_clip.right, fAnimate),
2395 (int)CalcAnimation(sub->m_scaley*r.bottom + o.y, sub->m_clip.bottom, fAnimate));
2397 break;
2399 case CMD_c:
2401 DWORD c = wcstol(p, NULL, 16);
2402 style.colors[0] = !p.IsEmpty()
2403 ? (((int)CalcAnimation(c&0xff, style.colors[0]&0xff, fAnimate))&0xff
2404 |((int)CalcAnimation(c&0xff00, style.colors[0]&0xff00, fAnimate))&0xff00
2405 |((int)CalcAnimation(c&0xff0000, style.colors[0]&0xff0000, fAnimate))&0xff0000)
2406 : org.colors[0];
2407 break;
2409 case CMD_fade:
2410 case CMD_fad:
2412 sub->m_fAnimated2 = true;
2413 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])
2415 if(Effect* e = new Effect)
2417 for(int i = 0; i < 3; i++)
2418 e->param[i] = wcstol(params[i], NULL, 10);
2419 for(int i = 0; i < 4; i++)
2420 e->t[i] = wcstol(params[3+i], NULL, 10);
2421 sub->m_effects[EF_FADE] = e;
2424 else if(params.GetCount() == 2 && !sub->m_effects[EF_FADE]) // {\fad(t1=t[1], t2=t[2])
2426 if(Effect* e = new Effect)
2428 e->param[0] = e->param[2] = 0xff;
2429 e->param[1] = 0x00;
2430 for(int i = 1; i < 3; i++)
2431 e->t[i] = wcstol(params[i-1], NULL, 10);
2432 e->t[0] = e->t[3] = -1; // will be substituted with "start" and "end"
2433 sub->m_effects[EF_FADE] = e;
2436 break;
2438 case CMD_fax:
2440 style.fontShiftX = !p.IsEmpty()
2441 ? CalcAnimation(wcstod(p, NULL), style.fontShiftX, fAnimate)
2442 : org.fontShiftX;
2443 break;
2445 case CMD_fay:
2447 style.fontShiftY = !p.IsEmpty()
2448 ? CalcAnimation(wcstod(p, NULL), style.fontShiftY, fAnimate)
2449 : org.fontShiftY;
2450 break;
2452 case CMD_fe:
2454 int n = wcstol(p, NULL, 10);
2455 style.charSet = !p.IsEmpty()
2457 : org.charSet;
2458 break;
2460 case CMD_fn:
2462 if(!p.IsEmpty() && p != L'0')
2463 style.fontName = CString(p).Trim();
2464 else
2465 style.fontName = org.fontName;
2466 break;
2468 case CMD_frx:
2470 style.fontAngleX = !p.IsEmpty()
2471 ? CalcAnimation(wcstod(p, NULL), style.fontAngleX, fAnimate)
2472 : org.fontAngleX;
2473 break;
2475 case CMD_fry:
2477 style.fontAngleY = !p.IsEmpty()
2478 ? CalcAnimation(wcstod(p, NULL), style.fontAngleY, fAnimate)
2479 : org.fontAngleY;
2480 break;
2482 case CMD_frz:
2483 case CMD_fr:
2485 style.fontAngleZ = !p.IsEmpty()
2486 ? CalcAnimation(wcstod(p, NULL), style.fontAngleZ, fAnimate)
2487 : org.fontAngleZ;
2488 break;
2490 case CMD_fscx:
2492 double n = CalcAnimation(wcstod(p, NULL), style.fontScaleX, fAnimate);
2493 style.fontScaleX = !p.IsEmpty()
2494 ? ((n < 0) ? 0 : n)
2495 : org.fontScaleX;
2496 break;
2498 case CMD_fscy:
2500 double n = CalcAnimation(wcstod(p, NULL), style.fontScaleY, fAnimate);
2501 style.fontScaleY = !p.IsEmpty()
2502 ? ((n < 0) ? 0 : n)
2503 : org.fontScaleY;
2504 break;
2506 case CMD_fsc:
2508 style.fontScaleX = org.fontScaleX;
2509 style.fontScaleY = org.fontScaleY;
2510 break;
2512 case CMD_fsp:
2514 style.fontSpacing = !p.IsEmpty()
2515 ? CalcAnimation(wcstod(p, NULL), style.fontSpacing, fAnimate)
2516 : org.fontSpacing;
2517 break;
2519 case CMD_fs:
2521 if(!p.IsEmpty())
2523 if(p[0] == L'-' || p[0] == L'+')
2525 double n = CalcAnimation(style.fontSize + style.fontSize*wcstod(p, NULL)/10, style.fontSize, fAnimate);
2526 style.fontSize = (n > 0) ? n : org.fontSize;
2528 else
2530 double n = CalcAnimation(wcstod(p, NULL), style.fontSize, fAnimate);
2531 style.fontSize = (n > 0) ? n : org.fontSize;
2534 else
2536 style.fontSize = org.fontSize;
2538 break;
2540 case CMD_i:
2542 int n = wcstol(p, NULL, 10);
2543 style.fItalic = !p.IsEmpty()
2544 ? (n == 0 ? false : n == 1 ? true : org.fItalic)
2545 : org.fItalic;
2546 break;
2548 case CMD_kt:
2550 m_kstart = !p.IsEmpty()
2551 ? wcstod(p, NULL)*10
2552 : 0;
2553 m_kend = m_kstart;
2554 break;
2555 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2557 case CMD_kf:
2558 case CMD_K:
2560 m_ktype = 1;
2561 m_kstart = m_kend;
2562 m_kend += !p.IsEmpty()
2563 ? wcstod(p, NULL)*10
2564 : 1000;
2565 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2566 break;
2568 case CMD_ko:
2570 m_ktype = 2;
2571 m_kstart = m_kend;
2572 m_kend += !p.IsEmpty()
2573 ? wcstod(p, NULL)*10
2574 : 1000;
2575 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2576 break;
2578 case CMD_k:
2580 m_ktype = 0;
2581 m_kstart = m_kend;
2582 m_kend += !p.IsEmpty()
2583 ? wcstod(p, NULL)*10
2584 : 1000;
2585 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2586 break;
2588 case CMD_move: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3][, t1=t[0], t2=t[1]])}
2590 if((params.GetCount() == 4 || params.GetCount() == 6) && !sub->m_effects[EF_MOVE])
2592 if(Effect* e = new Effect)
2594 e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2595 e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2596 e->param[2] = (int)(sub->m_scalex*wcstod(params[2], NULL)*8);
2597 e->param[3] = (int)(sub->m_scaley*wcstod(params[3], NULL)*8);
2598 e->t[0] = e->t[1] = -1;
2599 if(params.GetCount() == 6)
2601 for(int i = 0; i < 2; i++)
2602 e->t[i] = wcstol(params[4+i], NULL, 10);
2604 sub->m_effects[EF_MOVE] = e;
2605 sub->m_fAnimated2 = true;
2608 break;
2610 case CMD_org: // {\org(x=param[0], y=param[1])}
2612 if(params.GetCount() == 2 && !sub->m_effects[EF_ORG])
2614 if(Effect* e = new Effect)
2616 e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2617 e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2618 sub->m_effects[EF_ORG] = e;
2621 break;
2623 case CMD_pbo:
2625 m_polygonBaselineOffset = wcstol(p, NULL, 10);
2626 break;
2628 case CMD_pos:
2630 if(params.GetCount() == 2 && !sub->m_effects[EF_MOVE])
2632 if(Effect* e = new Effect)
2634 e->param[0] = e->param[2] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2635 e->param[1] = e->param[3] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2636 e->t[0] = e->t[1] = 0;
2637 sub->m_effects[EF_MOVE] = e;
2640 break;
2642 case CMD_p:
2644 int n = wcstol(p, NULL, 10);
2645 m_nPolygon = (n <= 0 ? 0 : n);
2646 break;
2648 case CMD_q:
2650 int n = wcstol(p, NULL, 10);
2651 sub->m_wrapStyle = !p.IsEmpty() && (0 <= n && n <= 3)
2653 : m_defaultWrapStyle;
2654 break;
2656 case CMD_r:
2658 STSStyle* val;
2659 style = (!p.IsEmpty() && m_styles.Lookup(WToT(p), val) && val) ? *val : org;
2660 break;
2662 case CMD_shad:
2664 double dst = wcstod(p, NULL);
2665 double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate);
2666 style.shadowDepthX = !p.IsEmpty()
2667 ? (nx < 0 ? 0 : nx)
2668 : org.shadowDepthX;
2669 double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate);
2670 style.shadowDepthY = !p.IsEmpty()
2671 ? (ny < 0 ? 0 : ny)
2672 : org.shadowDepthY;
2673 break;
2675 case CMD_s:
2677 int n = wcstol(p, NULL, 10);
2678 style.fStrikeOut = !p.IsEmpty()
2679 ? (n == 0 ? false : n == 1 ? true : org.fStrikeOut)
2680 : org.fStrikeOut;
2681 break;
2683 case CMD_t: // \t([<t1>,<t2>,][<accel>,]<style modifiers>)
2685 CStringW param;
2686 m_animStart = m_animEnd = 0;
2687 m_animAccel = 1;
2688 if(params.GetCount() == 1)
2690 param = params[0];
2692 else if(params.GetCount() == 2)
2694 m_animAccel = wcstod(params[0], NULL);
2695 param = params[1];
2697 else if(params.GetCount() == 3)
2699 m_animStart = (int)wcstod(params[0], NULL);
2700 m_animEnd = (int)wcstod(params[1], NULL);
2701 param = params[2];
2703 else if(params.GetCount() == 4)
2705 m_animStart = wcstol(params[0], NULL, 10);
2706 m_animEnd = wcstol(params[1], NULL, 10);
2707 m_animAccel = wcstod(params[2], NULL);
2708 param = params[3];
2710 ParseSSATag(sub, assTag.embeded, style, org, true);
2711 sub->m_fAnimated = true;
2712 sub->m_fAnimated2 = true;
2713 break;
2715 case CMD_u:
2717 int n = wcstol(p, NULL, 10);
2718 style.fUnderline = !p.IsEmpty()
2719 ? (n == 0 ? false : n == 1 ? true : org.fUnderline)
2720 : org.fUnderline;
2721 break;
2723 case CMD_xbord:
2725 double dst = wcstod(p, NULL);
2726 double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate);
2727 style.outlineWidthX = !p.IsEmpty()
2728 ? (nx < 0 ? 0 : nx)
2729 : org.outlineWidthX;
2730 break;
2732 case CMD_xshad:
2734 double dst = wcstod(p, NULL);
2735 double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate);
2736 style.shadowDepthX = !p.IsEmpty()
2737 ? nx
2738 : org.shadowDepthX;
2739 break;
2741 case CMD_ybord:
2743 double dst = wcstod(p, NULL);
2744 double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate);
2745 style.outlineWidthY = !p.IsEmpty()
2746 ? (ny < 0 ? 0 : ny)
2747 : org.outlineWidthY;
2748 break;
2750 case CMD_yshad:
2752 double dst = wcstod(p, NULL);
2753 double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate);
2754 style.shadowDepthY = !p.IsEmpty()
2755 ? ny
2756 : org.shadowDepthY;
2757 break;
2759 default:
2760 break;
2763 return(true);
2766 bool CRenderedTextSubtitle::ParseSSATag(CSubtitle* sub, const CStringW& str, STSStyle& style, const STSStyle& org, bool fAnimate)
2768 if(!sub) return(false);
2770 SharedPtrConstAssTagList assTags;
2771 AssTagListMruCache *ass_tag_cache = CacheManager::GetAssTagListMruCache();
2772 POSITION pos = ass_tag_cache->Lookup(str);
2773 if (pos==NULL)
2775 AssTagList *tmp = new AssTagList();
2776 ParseSSATag(tmp, str);
2777 assTags.reset(tmp);
2778 ass_tag_cache->UpdateCache(str, assTags);
2780 else
2782 assTags = ass_tag_cache->GetAt(pos);
2783 ass_tag_cache->UpdateCache( pos );
2785 return ParseSSATag(sub, *assTags, style, org, fAnimate);
2788 bool CRenderedTextSubtitle::ParseHtmlTag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org)
2790 if(str.Find(L"!--") == 0)
2791 return(true);
2792 bool fClosing = str[0] == L'/';
2793 str.Trim(L" /");
2794 int i = str.Find(L' ');
2795 if(i < 0) i = str.GetLength();
2796 CStringW tag = str.Left(i).MakeLower();
2797 str = str.Mid(i).Trim();
2798 CAtlArray<CStringW> attribs, params;
2799 while((i = str.Find(L'=')) > 0)
2801 attribs.Add(str.Left(i).Trim().MakeLower());
2802 str = str.Mid(i+1);
2803 for(i = 0; _istspace(str[i]); i++);
2804 str = str.Mid(i);
2805 if(str[0] == L'\"') {str = str.Mid(1); i = str.Find(L'\"');}
2806 else i = str.Find(L' ');
2807 if(i < 0) i = str.GetLength();
2808 params.Add(str.Left(i).Trim().MakeLower());
2809 str = str.Mid(i+1);
2811 if(tag == L"text")
2813 else if(tag == L"b" || tag == L"strong")
2814 style.fontWeight = !fClosing ? FW_BOLD : org.fontWeight;
2815 else if(tag == L"i" || tag == L"em")
2816 style.fItalic = !fClosing ? true : org.fItalic;
2817 else if(tag == L"u")
2818 style.fUnderline = !fClosing ? true : org.fUnderline;
2819 else if(tag == L"s" || tag == L"strike" || tag == L"del")
2820 style.fStrikeOut = !fClosing ? true : org.fStrikeOut;
2821 else if(tag == L"font")
2823 if(!fClosing)
2825 for(size_t i = 0; i < attribs.GetCount(); i++)
2827 if(params[i].IsEmpty()) continue;
2828 int nColor = -1;
2829 if(attribs[i] == L"face")
2831 style.fontName = params[i];
2833 else if(attribs[i] == L"size")
2835 if(params[i][0] == L'+')
2836 style.fontSize += wcstol(params[i], NULL, 10);
2837 else if(params[i][0] == L'-')
2838 style.fontSize -= wcstol(params[i], NULL, 10);
2839 else
2840 style.fontSize = wcstol(params[i], NULL, 10);
2842 else if(attribs[i] == L"color")
2844 nColor = 0;
2846 else if(attribs[i] == L"outline-color")
2848 nColor = 2;
2850 else if(attribs[i] == L"outline-level")
2852 style.outlineWidthX = style.outlineWidthY = wcstol(params[i], NULL, 10);
2854 else if(attribs[i] == L"shadow-color")
2856 nColor = 3;
2858 else if(attribs[i] == L"shadow-level")
2860 style.shadowDepthX = style.shadowDepthY = wcstol(params[i], NULL, 10);
2862 if(nColor >= 0 && nColor < 4)
2864 CString key = WToT(params[i]).TrimLeft(L'#');
2865 DWORD val;
2866 if(g_colors.Lookup(key, val))
2867 style.colors[nColor] = val;
2868 else if((style.colors[nColor] = _tcstol(key, NULL, 16)) == 0)
2869 style.colors[nColor] = 0x00ffffff; // default is white
2870 style.colors[nColor] = ((style.colors[nColor]>>16)&0xff)|((style.colors[nColor]&0xff)<<16)|(style.colors[nColor]&0x00ff00);
2874 else
2876 style.fontName = org.fontName;
2877 style.fontSize = org.fontSize;
2878 memcpy(style.colors, org.colors, sizeof(style.colors));
2881 else if(tag == L"k" && attribs.GetCount() == 1 && attribs[0] == L"t")
2883 m_ktype = 1;
2884 m_kstart = m_kend;
2885 m_kend += wcstol(params[0], NULL, 10);
2887 else
2888 return(false);
2889 return(true);
2892 double CRenderedTextSubtitle::CalcAnimation(double dst, double src, bool fAnimate)
2894 int s = m_animStart ? m_animStart : 0;
2895 int e = m_animEnd ? m_animEnd : m_delay;
2896 if(fabs(dst-src) >= 0.0001 && fAnimate)
2898 if(m_time < s) dst = src;
2899 else if(s <= m_time && m_time < e)
2901 double t = pow(1.0 * (m_time - s) / (e - s), m_animAccel);
2902 dst = (1 - t) * src + t * dst;
2904 // else dst = dst;
2906 return(dst);
2909 CSubtitle* CRenderedTextSubtitle::GetSubtitle(int entry)
2911 CSubtitle* sub;
2912 if(m_subtitleCache.Lookup(entry, sub))
2914 if(sub->m_fAnimated) {delete sub; sub = NULL;}
2915 else return(sub);
2917 sub = new CSubtitle();
2918 if(!sub) return(NULL);
2919 CStringW str = GetStrW(entry, true);
2920 STSStyle stss, orgstss;
2921 GetStyle(entry, &stss);
2922 if (stss.fontScaleX == stss.fontScaleY && m_dPARCompensation != 1.0)
2924 switch(m_ePARCompensationType)
2926 case EPCTUpscale:
2927 if (m_dPARCompensation < 1.0)
2928 stss.fontScaleY /= m_dPARCompensation;
2929 else
2930 stss.fontScaleX *= m_dPARCompensation;
2931 break;
2932 case EPCTDownscale:
2933 if (m_dPARCompensation < 1.0)
2934 stss.fontScaleX *= m_dPARCompensation;
2935 else
2936 stss.fontScaleY /= m_dPARCompensation;
2937 break;
2938 case EPCTAccurateSize:
2939 stss.fontScaleX *= m_dPARCompensation;
2940 break;
2943 orgstss = stss;
2944 sub->m_clip.SetRect(0, 0, m_size.cx>>3, m_size.cy>>3);
2945 sub->m_scrAlignment = -stss.scrAlignment;
2946 sub->m_wrapStyle = m_defaultWrapStyle;
2947 sub->m_fAnimated = false;
2948 sub->m_relativeTo = stss.relativeTo;
2949 sub->m_scalex = m_dstScreenSize.cx > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Width() : m_size.cx) / (m_dstScreenSize.cx*8) : 1.0;
2950 sub->m_scaley = m_dstScreenSize.cy > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Height() : m_size.cy) / (m_dstScreenSize.cy*8) : 1.0;
2951 m_animStart = m_animEnd = 0;
2952 m_animAccel = 1;
2953 m_ktype = m_kstart = m_kend = 0;
2954 m_nPolygon = 0;
2955 m_polygonBaselineOffset = 0;
2956 ParseEffect(sub, m_entries.GetAt(entry).effect);
2957 while(!str.IsEmpty())
2959 bool fParsed = false;
2960 int i;
2961 if(str[0] == L'{' && (i = str.Find(L'}')) > 0)
2963 if(fParsed = ParseSSATag(sub, str.Mid(1, i-1), stss, orgstss))
2964 str = str.Mid(i+1);
2966 else if(str[0] == L'<' && (i = str.Find(L'>')) > 0)
2968 if(fParsed = ParseHtmlTag(sub, str.Mid(1, i-1), stss, orgstss))
2969 str = str.Mid(i+1);
2971 if(fParsed)
2973 i = str.FindOneOf(L"{<");
2974 if(i < 0) i = str.GetLength();
2975 if(i == 0) continue;
2977 else
2979 i = str.Mid(1).FindOneOf(L"{<");
2980 if(i < 0) i = str.GetLength()-1;
2981 i++;
2983 STSStyle tmp = stss;
2984 tmp.fontSize = sub->m_scaley*tmp.fontSize*64;
2985 tmp.fontSpacing = sub->m_scalex*tmp.fontSpacing*64;
2986 tmp.outlineWidthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8;
2987 tmp.outlineWidthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8;
2988 tmp.shadowDepthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8;
2989 tmp.shadowDepthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8;
2990 FwSTSStyle fw_tmp(tmp);
2991 if(m_nPolygon)
2993 ParsePolygon(sub, str.Left(i), fw_tmp);
2995 else
2997 ParseString(sub, str.Left(i), fw_tmp);
2999 str = str.Mid(i);
3001 if( sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL] )
3002 sub->m_fAnimated2 = true;
3003 // just a "work-around" solution... in most cases nobody will want to use \org together with moving but without rotating the subs
3004 if(sub->m_effects[EF_ORG] && (sub->m_effects[EF_MOVE] || sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL]))
3005 sub->m_fAnimated = true;
3006 sub->m_scrAlignment = abs(sub->m_scrAlignment);
3007 STSEntry stse = m_entries.GetAt(entry);
3008 CRect marginRect = stse.marginRect;
3009 if(marginRect.left == 0) marginRect.left = orgstss.marginRect.get().left;
3010 if(marginRect.top == 0) marginRect.top = orgstss.marginRect.get().top;
3011 if(marginRect.right == 0) marginRect.right = orgstss.marginRect.get().right;
3012 if(marginRect.bottom == 0) marginRect.bottom = orgstss.marginRect.get().bottom;
3013 marginRect.left = (int)(sub->m_scalex*marginRect.left*8);
3014 marginRect.top = (int)(sub->m_scaley*marginRect.top*8);
3015 marginRect.right = (int)(sub->m_scalex*marginRect.right*8);
3016 marginRect.bottom = (int)(sub->m_scaley*marginRect.bottom*8);
3017 if(stss.relativeTo == 1)
3019 marginRect.left += m_vidrect.left;
3020 marginRect.top += m_vidrect.top;
3021 marginRect.right += m_size.cx - m_vidrect.right;
3022 marginRect.bottom += m_size.cy - m_vidrect.bottom;
3024 sub->CreateClippers(m_size);
3025 sub->MakeLines(m_size, marginRect);
3026 m_subtitleCache[entry] = sub;
3027 return(sub);
3032 STDMETHODIMP CRenderedTextSubtitle::NonDelegatingQueryInterface(REFIID riid, void** ppv)
3034 CheckPointer(ppv, E_POINTER);
3035 *ppv = NULL;
3036 return
3037 QI(IPersist)
3038 QI(ISubStream)
3039 QI(ISubPicProviderEx2)
3040 QI(ISubPicProvider)
3041 QI(ISubPicProviderEx)
3042 __super::NonDelegatingQueryInterface(riid, ppv);
3045 // ISubPicProvider
3047 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetStartPosition(REFERENCE_TIME rt, double fps)
3049 m_fps = fps;
3050 if (m_fps>0)
3052 m_period = 1000/m_fps;
3053 if(m_period<=0)
3055 m_period = 1;
3058 else
3060 //Todo: fix me. max has been defined as a macro. Use #define NOMINMAX to fix it.
3061 //std::numeric_limits<int>::max();
3062 m_period = INT_MAX;
3065 int iSegment;
3066 int subIndex = 1;//If a segment has animate effect then it corresponds to several subpics.
3067 //subIndex, 1 based, indicates which subpic the result corresponds to.
3068 rt /= 10000i64;
3069 const STSSegment *stss = SearchSubs((int)rt, fps, &iSegment, NULL);
3070 if(stss==NULL)
3071 return NULL;
3072 else if(stss->animated)
3074 int start = TranslateSegmentStart(iSegment, fps);
3075 if(rt > start)
3076 subIndex = (rt-start)/m_period + 1;
3078 //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));
3079 return (POSITION)(subIndex | (iSegment<<RTS_POS_SEGMENT_INDEX_BITS));
3080 //if(iSegment < 0) iSegment = 0;
3081 //return(GetNext((POSITION)iSegment));
3084 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetNext(POSITION pos)
3086 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3087 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3088 const STSSegment *stss = GetSegment(iSegment);
3089 ASSERT(stss!=NULL && stss->subs.GetCount()>0);
3090 //DbgLog((LOG_TRACE, 3, "stss:%x count:%d", stss, stss->subs.GetCount()));
3091 if(!stss->animated)
3093 iSegment++;
3094 subIndex = 1;
3096 else
3098 int start, end;
3099 TranslateSegmentStartEnd(iSegment, m_fps, start, end);
3100 if(start+m_period*subIndex < end)
3101 subIndex++;
3102 else
3104 iSegment++;
3105 subIndex = 1;
3108 if(GetSegment(iSegment) != NULL)
3110 ASSERT(GetSegment(iSegment)->subs.GetCount()>0);
3111 return (POSITION)(subIndex | (iSegment<<RTS_POS_SEGMENT_INDEX_BITS));
3113 else
3114 return NULL;
3117 //@return: <0 if segment not found
3118 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStart(POSITION pos, double fps)
3120 //return(10000i64 * TranslateSegmentStart((int)pos-1, fps));
3121 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3122 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3123 int start = TranslateSegmentStart(iSegment, fps);
3124 const STSSegment *stss = GetSegment(iSegment);
3125 if(stss!=NULL)
3127 return (start + (subIndex-1)*m_period)*10000i64;
3129 else
3131 return -1;
3135 //@return: <0 if segment not found
3136 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStop(POSITION pos, double fps)
3138 // return(10000i64 * TranslateSegmentEnd((int)pos-1, fps));
3139 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3140 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3141 int start, end, ret;
3142 TranslateSegmentStartEnd(iSegment, fps, start, end);
3143 const STSSegment *stss = GetSegment(iSegment);
3144 if(stss!=NULL)
3146 if(!stss->animated)
3147 ret = end;
3148 else
3150 ret = start+subIndex*m_period;
3151 if(ret > end)
3152 ret = end;
3154 return ret*10000i64;
3156 else
3157 return -1;
3160 //@start, @stop: -1 if segment not found; @stop may < @start if subIndex exceed uppper bound
3161 STDMETHODIMP_(VOID) CRenderedTextSubtitle::GetStartStop(POSITION pos, double fps, /*out*/REFERENCE_TIME &start, /*out*/REFERENCE_TIME &stop)
3163 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3164 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3165 int tempStart, tempEnd;
3166 TranslateSegmentStartEnd(iSegment, fps, tempStart, tempEnd);
3167 start = tempStart;
3168 stop = tempEnd;
3169 const STSSegment *stss = GetSegment(iSegment);
3170 if(stss!=NULL)
3172 if(stss->animated)
3174 start += (subIndex-1)*m_period;
3175 if(start+m_period < stop)
3176 stop = start+m_period;
3178 //DbgLog((LOG_TRACE, 3, "animated:%d seg:%d idx:%d start:%d stop:%lu", stss->animated, iSegment, subIndex, (ULONG)start, (ULONG)stop));
3179 start *= 10000i64;
3180 stop *= 10000i64;
3182 else
3184 start = -1;
3185 stop = -1;
3189 STDMETHODIMP_(bool) CRenderedTextSubtitle::IsAnimated(POSITION pos)
3191 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3192 if(iSegment>=0 && iSegment<m_segments.GetCount())
3193 return m_segments[iSegment].animated;
3194 else
3195 return false;
3196 //return(true);
3199 struct LSub {int idx, layer, readorder;};
3201 static int lscomp(const void* ls1, const void* ls2)
3203 int ret = ((LSub*)ls1)->layer - ((LSub*)ls2)->layer;
3204 if(!ret) ret = ((LSub*)ls1)->readorder - ((LSub*)ls2)->readorder;
3205 return(ret);
3208 STDMETHODIMP CRenderedTextSubtitle::ParseScript(const SIZE& output_size, REFERENCE_TIME rt, double fps, CSubtitle2List *outputSub2List )
3210 //fix me: check input and log error
3211 int t = (int)(rt / 10000);
3212 int segment;
3213 //const
3214 STSSegment* stss = SearchSubs2(t, fps, &segment);
3215 if(!stss) return S_FALSE;
3216 // clear any cached subs not in the range of +/-30secs measured from the segment's bounds
3218 POSITION pos = m_subtitleCache.GetStartPosition();
3219 while(pos)
3221 int key;
3222 CSubtitle* value;
3223 m_subtitleCache.GetNextAssoc(pos, key, value);
3224 STSEntry& stse = m_entries.GetAt(key);
3225 if(stse.end <= (t-30000) || stse.start > (t+30000))
3227 delete value;
3228 m_subtitleCache.RemoveKey(key);
3229 pos = m_subtitleCache.GetStartPosition();
3233 m_sla.AdvanceToSegment(segment, stss->subs);
3234 CAtlArray<LSub> subs;
3235 for(int i = 0, j = stss->subs.GetCount(); i < j; i++)
3237 LSub ls;
3238 ls.idx = stss->subs[i];
3239 ls.layer = m_entries.GetAt(stss->subs[i]).layer;
3240 ls.readorder = m_entries.GetAt(stss->subs[i]).readorder;
3241 subs.Add(ls);
3243 qsort(subs.GetData(), subs.GetCount(), sizeof(LSub), lscomp);
3245 for(int i = 0, j = subs.GetCount(); i < j; i++)
3247 int entry = subs[i].idx;
3248 STSEntry stse = m_entries.GetAt(entry);
3250 int start = TranslateStart(entry, fps);
3251 m_time = t - start;
3252 m_delay = TranslateEnd(entry, fps) - start;
3254 CSubtitle* s = GetSubtitle(entry);
3255 if(!s) continue;
3256 stss->animated |= s->m_fAnimated2;
3257 CRect clipRect = s->m_clip & CRect(0,0, output_size.cx, output_size.cy);
3258 CRect r = s->m_rect;
3259 CSize spaceNeeded = r.Size();
3260 // apply the effects
3261 bool fPosOverride = false, fOrgOverride = false;
3262 int alpha = 0x00;
3263 CPoint org2;
3264 for(int k = 0; k < EF_NUMBEROFEFFECTS; k++)
3266 if(!s->m_effects[k]) continue;
3267 switch(k)
3269 case EF_MOVE: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3], t1=t[0], t2=t[1])}
3271 CPoint p;
3272 CPoint p1(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
3273 CPoint p2(s->m_effects[k]->param[2], s->m_effects[k]->param[3]);
3274 int t1 = s->m_effects[k]->t[0];
3275 int t2 = s->m_effects[k]->t[1];
3276 if(t2 < t1) {int t = t1; t1 = t2; t2 = t;}
3277 if(t1 <= 0 && t2 <= 0) {t1 = 0; t2 = m_delay;}
3278 if(m_time <= t1) p = p1;
3279 else if (p1 == p2) p = p1;
3280 else if(t1 < m_time && m_time < t2)
3282 double t = 1.0*(m_time-t1)/(t2-t1);
3283 p.x = (int)((1-t)*p1.x + t*p2.x);
3284 p.y = (int)((1-t)*p1.y + t*p2.y);
3286 else p = p2;
3287 r = CRect(
3288 CPoint((s->m_scrAlignment%3) == 1 ? p.x : (s->m_scrAlignment%3) == 0 ? p.x - spaceNeeded.cx : p.x - (spaceNeeded.cx+1)/2,
3289 s->m_scrAlignment <= 3 ? p.y - spaceNeeded.cy : s->m_scrAlignment <= 6 ? p.y - (spaceNeeded.cy+1)/2 : p.y),
3290 spaceNeeded);
3291 if(s->m_relativeTo == 1)
3292 r.OffsetRect(m_vidrect.TopLeft());
3293 fPosOverride = true;
3295 break;
3296 case EF_ORG: // {\org(x=param[0], y=param[1])}
3298 org2 = CPoint(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
3299 fOrgOverride = true;
3301 break;
3302 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])
3304 int t1 = s->m_effects[k]->t[0];
3305 int t2 = s->m_effects[k]->t[1];
3306 int t3 = s->m_effects[k]->t[2];
3307 int t4 = s->m_effects[k]->t[3];
3308 if(t1 == -1 && t4 == -1) {t1 = 0; t3 = m_delay-t3; t4 = m_delay;}
3309 if(m_time < t1) alpha = s->m_effects[k]->param[0];
3310 else if(m_time >= t1 && m_time < t2)
3312 double t = 1.0 * (m_time - t1) / (t2 - t1);
3313 alpha = (int)(s->m_effects[k]->param[0]*(1-t) + s->m_effects[k]->param[1]*t);
3315 else if(m_time >= t2 && m_time < t3) alpha = s->m_effects[k]->param[1];
3316 else if(m_time >= t3 && m_time < t4)
3318 double t = 1.0 * (m_time - t3) / (t4 - t3);
3319 alpha = (int)(s->m_effects[k]->param[1]*(1-t) + s->m_effects[k]->param[2]*t);
3321 else if(m_time >= t4) alpha = s->m_effects[k]->param[2];
3323 break;
3324 case EF_BANNER: // Banner;delay=param[0][;leftoright=param[1];fadeawaywidth=param[2]]
3326 int left = s->m_relativeTo == 1 ? m_vidrect.left : 0,
3327 right = s->m_relativeTo == 1 ? m_vidrect.right : m_size.cx;
3328 r.left = !!s->m_effects[k]->param[1]
3329 ? (left/*marginRect.left*/ - spaceNeeded.cx) + (int)(m_time*8.0/s->m_effects[k]->param[0])
3330 : (right /*- marginRect.right*/) - (int)(m_time*8.0/s->m_effects[k]->param[0]);
3331 r.right = r.left + spaceNeeded.cx;
3332 clipRect &= CRect(left>>3, clipRect.top, right>>3, clipRect.bottom);
3333 fPosOverride = true;
3335 break;
3336 case EF_SCROLL: // Scroll up/down(toptobottom=param[3]);top=param[0];bottom=param[1];delay=param[2][;fadeawayheight=param[4]]
3338 r.top = !!s->m_effects[k]->param[3]
3339 ? s->m_effects[k]->param[0] + (int)(m_time*8.0/s->m_effects[k]->param[2]) - spaceNeeded.cy
3340 : s->m_effects[k]->param[1] - (int)(m_time*8.0/s->m_effects[k]->param[2]);
3341 r.bottom = r.top + spaceNeeded.cy;
3342 CRect cr(0, (s->m_effects[k]->param[0] + 4) >> 3, output_size.cx, (s->m_effects[k]->param[1] + 4) >> 3);
3343 if(s->m_relativeTo == 1)
3344 r.top += m_vidrect.top,
3345 r.bottom += m_vidrect.top,
3346 cr.top += m_vidrect.top>>3,
3347 cr.bottom += m_vidrect.top>>3;
3348 clipRect &= cr;
3349 fPosOverride = true;
3351 break;
3352 default:
3353 break;
3356 if(!fPosOverride && !fOrgOverride && !s->m_fAnimated)
3357 r = m_sla.AllocRect(s, segment, entry, stse.layer, m_collisions);
3358 CPoint org;
3359 org.x = (s->m_scrAlignment%3) == 1 ? r.left : (s->m_scrAlignment%3) == 2 ? r.CenterPoint().x : r.right;
3360 org.y = s->m_scrAlignment <= 3 ? r.bottom : s->m_scrAlignment <= 6 ? r.CenterPoint().y : r.top;
3361 if(!fOrgOverride) org2 = org;
3362 CPoint p2(0, r.top);
3363 // Rectangles for inverse clip
3365 CSubtitle2& sub2 = outputSub2List->GetAt(outputSub2List->AddTail( CSubtitle2(s, clipRect, org, org2, p2, alpha, m_time) ));
3368 return (subs.GetCount()) ? S_OK : S_FALSE;
3371 STDMETHODIMP CRenderedTextSubtitle::RenderEx(SubPicDesc& spd, REFERENCE_TIME rt, double fps, CAtlList<CRect>& rectList)
3373 CSize output_size = CSize(spd.w,spd.h);
3374 rectList.RemoveAll();
3376 CComPtr<IXySubRenderFrame> sub_render_frame;
3377 HRESULT hr = RenderEx(&sub_render_frame, spd.type, output_size, spd.vidrect, rt, fps);
3378 if (SUCCEEDED(hr) && sub_render_frame)
3380 int count = 0;
3381 hr = sub_render_frame->GetBitmapCount(&count);
3382 if(FAILED(hr))
3384 return hr;
3386 int color_space;
3387 hr = sub_render_frame->GetXyColorSpace(&color_space);
3388 if(FAILED(hr))
3390 return hr;
3392 for (int i=0;i<count;i++)
3394 POINT pos;
3395 SIZE size;
3396 LPCVOID pixels;
3397 int pitch;
3398 hr = sub_render_frame->GetBitmap(i, NULL, &pos, &size, &pixels, &pitch );
3399 if(FAILED(hr))
3401 return hr;
3403 rectList.AddTail(CRect(pos, size));
3404 if (color_space==XY_CS_AYUV_PLANAR)
3406 XyPlannerFormatExtra plans;
3407 hr = sub_render_frame->GetBitmapExtra(i, &plans);
3408 if(FAILED(hr))
3410 return hr;
3412 XyBitmap::AlphaBltPlannar(spd, pos, size, plans, pitch);
3414 else
3416 XyBitmap::AlphaBltPack(spd, pos, size, pixels, pitch);
3420 return (!rectList.IsEmpty()) ? S_OK : S_FALSE;
3423 STDMETHODIMP CRenderedTextSubtitle::RenderEx(IXySubRenderFrame**subRenderFrame, int spd_type, const SIZE& output_size, const CRect& video_rect, REFERENCE_TIME rt, double fps)
3425 if (!subRenderFrame)
3427 return S_FALSE;
3430 XyColorSpace color_space = XY_CS_ARGB;
3431 switch(spd_type)
3433 case MSP_AYUV_PLANAR:
3434 color_space = XY_CS_AYUV_PLANAR;
3435 break;
3436 case MSP_XY_AUYV:
3437 color_space = XY_CS_AUYV;
3438 break;
3439 case MSP_AYUV:
3440 color_space = XY_CS_AYUV;
3441 break;
3442 default:
3443 color_space = XY_CS_ARGB;
3444 break;
3447 XySubRenderFrameCreater *render_frame_creater = XySubRenderFrameCreater::GetDefaultCreater();
3448 render_frame_creater->SetColorSpace(color_space);
3450 if(m_size != CSize(output_size.cx*8, output_size.cy*8)
3451 || m_vidrect != CRect(video_rect.left*8, video_rect.top*8, video_rect.right*8, video_rect.bottom*8))
3453 Init(output_size, video_rect);
3454 render_frame_creater->SetOutputRect(CRect(0,0,output_size.cx,output_size.cy));
3455 render_frame_creater->SetClipRect(CRect(0,0,output_size.cx,output_size.cy));
3458 CSubtitle2List sub2List;
3459 HRESULT hr = ParseScript(output_size, rt, fps, &sub2List);
3460 if(hr!=S_OK)
3462 return hr;
3465 CompositeDrawItemListList compDrawItemListList;
3466 DoRender(output_size, sub2List, &compDrawItemListList);
3468 XySubRenderFrame *sub_render_frame;
3469 CompositeDrawItem::Draw(&sub_render_frame, compDrawItemListList);
3470 (*subRenderFrame = sub_render_frame)->AddRef();
3472 return hr;
3475 void CRenderedTextSubtitle::DoRender( const SIZE& output_size, const CSubtitle2List& sub2List,
3476 CompositeDrawItemListList *compDrawItemListList /*output*/)
3478 //check input and log error
3479 POSITION pos=sub2List.GetHeadPosition();
3480 while ( pos!=NULL )
3482 const CSubtitle2& sub2 = sub2List.GetNext(pos);
3483 CompositeDrawItemList& compDrawItemList = compDrawItemListList->GetAt(compDrawItemListList->AddTail());
3484 RenderOneSubtitle(output_size, sub2, &compDrawItemList);
3488 void CRenderedTextSubtitle::RenderOneSubtitle( const SIZE& output_size, const CSubtitle2& sub2,
3489 CompositeDrawItemList* compDrawItemList /*output*/)
3491 CSubtitle* s = sub2.s;
3492 const CRect& clipRect = sub2.clipRect;
3493 const CPoint& org = sub2.org;
3494 const CPoint& org2 = sub2.org2;
3495 const CPoint& p2 = sub2.p;
3496 int alpha = sub2.alpha;
3497 int time = sub2.time;
3498 if(!s) return;
3500 SharedPtrCClipperPaintMachine clipper( new CClipperPaintMachine(s->m_pClipper) );
3502 CRect iclipRect[4];
3503 iclipRect[0] = CRect(0, 0, output_size.cx, clipRect.top);
3504 iclipRect[1] = CRect(0, clipRect.top, clipRect.left, clipRect.bottom);
3505 iclipRect[2] = CRect(clipRect.right, clipRect.top, output_size.cx, clipRect.bottom);
3506 iclipRect[3] = CRect(0, clipRect.bottom, output_size.cx, output_size.cy);
3507 CRect bbox2(0,0,0,0);
3508 POSITION pos = s->GetHeadLinePosition();
3509 CPoint p = p2;
3510 while(pos)
3512 CLine* l = s->GetNextLine(pos);
3513 p.x = (s->m_scrAlignment%3) == 1 ? org.x
3514 : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
3515 : org.x - (l->m_width/2);
3517 CompositeDrawItemList tmpCompDrawItemList;
3518 if (s->m_clipInverse)
3520 CompositeDrawItemList tmp1,tmp2,tmp3,tmp4;
3521 for (int i=0;i<l->GetWordCount();i++)
3523 tmp1.AddTail();
3524 tmp2.AddTail();
3525 tmp3.AddTail();
3526 tmp4.AddTail();
3528 bbox2 |= l->PaintAll(&tmp1, iclipRect[0], clipper, p, org2, time, alpha);
3529 bbox2 |= l->PaintAll(&tmp2, iclipRect[1], clipper, p, org2, time, alpha);
3530 bbox2 |= l->PaintAll(&tmp3, iclipRect[2], clipper, p, org2, time, alpha);
3531 bbox2 |= l->PaintAll(&tmp4, iclipRect[3], clipper, p, org2, time, alpha);
3532 tmpCompDrawItemList.AddTailList(&tmp1);
3533 tmpCompDrawItemList.AddTailList(&tmp2);
3534 tmpCompDrawItemList.AddTailList(&tmp3);
3535 tmpCompDrawItemList.AddTailList(&tmp4);
3537 else
3539 for (int i=0;i<l->GetWordCount();i++)
3541 tmpCompDrawItemList.AddTail();
3543 bbox2 |= l->PaintAll(&tmpCompDrawItemList, clipRect, clipper, p, org2, time, alpha);
3545 compDrawItemList->AddTailList(&tmpCompDrawItemList);
3546 p.y += l->m_ascent + l->m_descent;
3550 STDMETHODIMP CRenderedTextSubtitle::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
3552 CAtlList<CRect> rectList;
3553 HRESULT result = RenderEx(spd, rt, fps, rectList);
3554 POSITION pos = rectList.GetHeadPosition();
3555 CRect bbox2(0,0,0,0);
3556 while(pos!=NULL)
3558 bbox2 |= rectList.GetNext(pos);
3560 bbox = bbox2;
3561 return result;
3564 // IPersist
3566 STDMETHODIMP CRenderedTextSubtitle::GetClassID(CLSID* pClassID)
3568 return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
3571 // ISubStream
3573 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStreamCount()
3575 return(1);
3578 STDMETHODIMP CRenderedTextSubtitle::GetStreamInfo(int iStream, WCHAR** ppName, LCID* pLCID)
3580 if(iStream != 0) return E_INVALIDARG;
3581 if(ppName)
3583 if(!(*ppName = (WCHAR*)CoTaskMemAlloc((m_name.GetLength()+1)*sizeof(WCHAR))))
3584 return E_OUTOFMEMORY;
3585 wcscpy(*ppName, CStringW(m_name));
3587 if(pLCID)
3589 *pLCID = 0; // TODO
3591 return S_OK;
3594 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStream()
3596 return(0);
3599 STDMETHODIMP CRenderedTextSubtitle::SetStream(int iStream)
3601 return iStream == 0 ? S_OK : E_FAIL;
3604 STDMETHODIMP CRenderedTextSubtitle::Reload()
3606 CFileStatus s;
3607 if(!CFile::GetStatus(m_path, s)) return E_FAIL;
3608 return !m_path.IsEmpty() && Open(m_path, DEFAULT_CHARSET) ? S_OK : E_FAIL;
3611 STDMETHODIMP_(bool) CRenderedTextSubtitle::IsColorTypeSupported( int type )
3613 return type==MSP_AYUV_PLANAR ||
3614 type==MSP_AYUV ||
3615 type==MSP_XY_AUYV ||
3616 type==MSP_RGBA;
3619 STDMETHODIMP CRenderedTextSubtitle::Lock()
3621 return CSubPicProviderImpl::Lock();
3624 STDMETHODIMP CRenderedTextSubtitle::Unlock()
3626 return CSubPicProviderImpl::Unlock();