Paint Machine. [Part 5]
[xy_vsfilter.git] / src / subtitles / RTS.cpp
blob7adbb6442d209071f1e7d2148386d57cc2751fbf
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 "../subpic/color_conv_table.h"
29 #include "subpixel_position_controler.h"
30 #include "xy_overlay_paint_machine.h"
31 #include "xy_clipper_paint_machine.h"
33 // WARNING: this isn't very thread safe, use only one RTS a time.
34 static HDC g_hDC;
35 static int g_hDC_refcnt = 0;
37 enum XY_MSP_SUBTYPE {XY_AYUV, XY_AUYV};
38 static inline DWORD rgb2yuv(DWORD argb, XY_MSP_SUBTYPE type)
40 if(type==XY_AYUV)
41 return ColorConvTable::Argb2Ayuv(argb);
42 else
43 return ColorConvTable::Argb2Auyv(argb);
46 static long revcolor(long c)
48 return ((c&0xff0000)>>16) + (c&0xff00) + ((c&0xff)<<16);
51 // Skip all leading whitespace
52 inline CStringW::PCXSTR SkipWhiteSpaceLeft(const CStringW& str)
54 CStringW::PCXSTR psz = str.GetString();
56 while( iswspace( *psz ) )
58 psz++;
60 return psz;
63 // Skip all trailing whitespace
64 inline CStringW::PCXSTR SkipWhiteSpaceRight(const CStringW& str)
66 CStringW::PCXSTR psz = str.GetString();
67 CStringW::PCXSTR pszLast = psz + str.GetLength() - 1;
68 bool first_white = false;
69 while( iswspace( *pszLast ) )
71 pszLast--;
72 if(pszLast<psz)
73 break;
75 return pszLast;
78 // Skip all leading whitespace
79 inline CStringW::PCXSTR SkipWhiteSpaceLeft(CStringW::PCXSTR start, CStringW::PCXSTR end)
81 while( start!=end && iswspace( *start ) )
83 start++;
85 return start;
88 // Skip all trailing whitespace, first char must NOT be white space
89 inline CStringW::PCXSTR FastSkipWhiteSpaceRight(CStringW::PCXSTR start, CStringW::PCXSTR end)
91 while( iswspace( *--end ) );
92 return end+1;
95 inline CStringW::PCXSTR FindChar(CStringW::PCXSTR start, CStringW::PCXSTR end, WCHAR c)
97 while( start!=end && *start!=c )
99 start++;
101 return start;
104 //////////////////////////////////////////////////////////////////////////////////////////////
106 // CMyFont
108 CMyFont::CMyFont(const STSStyleBase& style)
110 LOGFONT lf;
111 memset(&lf, 0, sizeof(lf));
112 lf <<= style;
113 lf.lfHeight = (LONG)(style.fontSize+0.5);
114 lf.lfOutPrecision = OUT_TT_PRECIS;
115 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
116 lf.lfQuality = ANTIALIASED_QUALITY;
117 lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
118 if(!CreateFontIndirect(&lf))
120 _tcscpy(lf.lfFaceName, _T("Arial"));
121 CreateFontIndirect(&lf);
123 HFONT hOldFont = SelectFont(g_hDC, *this);
124 TEXTMETRIC tm;
125 GetTextMetrics(g_hDC, &tm);
126 m_ascent = ((tm.tmAscent + 4) >> 3);
127 m_descent = ((tm.tmDescent + 4) >> 3);
128 SelectFont(g_hDC, hOldFont);
131 // CWord
133 CWord::CWord(const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend)
134 : m_style(style), m_str(str)
135 , m_width(0), m_ascent(0), m_descent(0)
136 , m_ktype(ktype), m_kstart(kstart), m_kend(kend)
137 , m_fLineBreak(false), m_fWhiteSpaceChar(false)
138 //, m_pOpaqueBox(NULL)
140 if(m_str.IsEmpty())
142 m_fWhiteSpaceChar = m_fLineBreak = true;
144 m_width = 0;
147 CWord::CWord( const CWord& src)
149 m_str = src.m_str;
150 m_fWhiteSpaceChar = src.m_fWhiteSpaceChar;
151 m_fLineBreak = src.m_fLineBreak;
152 m_style = src.m_style;
153 m_pOpaqueBox = src.m_pOpaqueBox;//allow since it is shared_ptr
154 m_ktype = src.m_ktype;
155 m_kstart = src.m_kstart;
156 m_kend = src.m_kend;
157 m_width = src.m_width;
158 m_ascent = src.m_ascent;
159 m_descent = src.m_descent;
162 CWord::~CWord()
164 //if(m_pOpaqueBox) delete m_pOpaqueBox;
167 bool CWord::Append(const SharedPtrCWord& w)
169 if(!(m_style == w->m_style)
170 || m_fLineBreak || w->m_fLineBreak
171 || w->m_kstart != w->m_kend || m_ktype != w->m_ktype) return(false);
172 m_fWhiteSpaceChar = m_fWhiteSpaceChar && w->m_fWhiteSpaceChar;
173 m_str += w->m_str;
174 m_width += w->m_width;
175 return(true);
178 void CWord::PaintFromOverlay(const CPoint& p, const CPoint& trans_org2, OverlayKey &subpixel_variance_key, SharedPtrOverlay& overlay)
180 if( SubpixelPositionControler::GetGlobalControler().UseBilinearShift() )
182 CPoint psub = SubpixelPositionControler::GetGlobalControler().GetSubpixel(p);
183 if( (psub.x!=(p.x&SubpixelPositionControler::EIGHT_X_EIGHT_MASK)
184 || psub.y!=(p.y&SubpixelPositionControler::EIGHT_X_EIGHT_MASK)) )
186 overlay.reset(overlay->GetSubpixelVariance((p.x&SubpixelPositionControler::EIGHT_X_EIGHT_MASK) - psub.x,
187 (p.y&SubpixelPositionControler::EIGHT_X_EIGHT_MASK) - psub.y));
188 OverlayMruCache* overlay_cache = CacheManager::GetSubpixelVarianceCache();
189 overlay_cache->UpdateCache(subpixel_variance_key, overlay);
194 void CWord::PaintFromNoneBluredOverlay(SharedPtrOverlay raterize_result, const OverlayKey& overlay_key, SharedPtrOverlay* overlay)
196 if( m_style.get().fBlur>0 || m_style.get().fGaussianBlur>0.000001 )
198 overlay->reset(new Overlay());
199 if(!Rasterizer::Blur(*raterize_result, m_style.get().fBlur, m_style.get().fGaussianBlur, *overlay))
201 *overlay = raterize_result;
204 else
206 *overlay = raterize_result;
208 OverlayMruCache* overlay_cache = CacheManager::GetOverlayMruCache();
209 overlay_cache->UpdateCache(overlay_key, *overlay);
212 bool CWord::PaintFromScanLineData2(const CPoint& psub, const ScanLineData2& scan_line_data2, const OverlayKey& key, SharedPtrOverlay* overlay)
214 SharedPtrOverlay raterize_result(new Overlay());
215 if(!Rasterizer::Rasterize(scan_line_data2, psub.x, psub.y, raterize_result))
217 return false;
219 OverlayNoBlurMruCache* overlay_no_blur_cache = CacheManager::GetOverlayNoBlurMruCache();
220 overlay_no_blur_cache->UpdateCache(key, raterize_result);
221 PaintFromNoneBluredOverlay(raterize_result, key, overlay);
222 return true;
225 bool CWord::PaintFromPathData(const CPoint& psub, const CPoint& trans_org, const PathData& path_data, const OverlayKey& key, SharedPtrOverlay* overlay )
227 bool result = false;
229 PathData *path_data2 = new PathData(path_data);//fix me: this copy operation can be saved if no transform is needed
230 SharedPtrConstPathData shared_ptr_path_data2(path_data2);
231 bool need_transform = NeedTransform();
232 if(need_transform)
233 Transform(path_data2, CPoint(trans_org.x*8, trans_org.y*8));
235 CPoint left_top;
236 CSize size;
237 path_data2->AlignLeftTop(&left_top, &size);
239 int border_x = static_cast<int>(m_style.get().outlineWidthX+0.5);
240 int border_y = static_cast<int>(m_style.get().outlineWidthY+0.5);
241 int wide_border = border_x>border_y ? border_x:border_y;
242 if (m_style.get().borderStyle==1)
244 border_x = border_y = 0;
247 OverlayNoOffsetMruCache* overlay_key_cache = CacheManager::GetOverlayNoOffsetMruCache();
248 OverlayNoOffsetKey overlay_no_offset_key(shared_ptr_path_data2, psub.x, psub.y, border_x, border_y);
249 overlay_no_offset_key.UpdateHashValue();
250 POSITION pos = overlay_key_cache->Lookup(overlay_no_offset_key);
252 OverlayNoBlurMruCache* overlay_cache = CacheManager::GetOverlayNoBlurMruCache();
253 if (pos!=NULL)
255 OverlayNoBlurKey overlay_key = overlay_key_cache->GetAt(pos);
256 pos = overlay_cache->Lookup(overlay_key);
258 if (pos)
260 SharedPtrOverlay raterize_result( new Overlay() );
261 *raterize_result = *overlay_cache->GetAt(pos);
262 raterize_result->mOffsetX = left_top.x - psub.x - ((wide_border+7)&~7);
263 raterize_result->mOffsetY = left_top.y - psub.y - ((wide_border+7)&~7);
264 PaintFromNoneBluredOverlay(raterize_result, key, overlay);
265 result = true;
266 overlay_cache->UpdateCache(key, raterize_result);
268 else
270 ScanLineDataMruCache* scan_line_data_cache = CacheManager::GetScanLineDataMruCache();
271 pos = scan_line_data_cache->Lookup(overlay_no_offset_key);
272 SharedPtrConstScanLineData scan_line_data;
273 if( pos != NULL )
275 scan_line_data = scan_line_data_cache->GetAt(pos);
276 scan_line_data_cache->UpdateCache(pos);
278 else
280 ScanLineData *tmp = new ScanLineData();
281 scan_line_data.reset(tmp);
282 if(!tmp->ScanConvert(*path_data2, size))
284 return false;
286 scan_line_data_cache->UpdateCache(overlay_no_offset_key, scan_line_data);
288 ScanLineData2 *tmp = new ScanLineData2(left_top, scan_line_data);
289 SharedPtrScanLineData2 scan_line_data2( tmp );
290 if(m_style.get().borderStyle == 0 && (m_style.get().outlineWidthX+m_style.get().outlineWidthY > 0))
292 if(!tmp->CreateWidenedRegion(border_x, border_y))
294 return false;
297 ScanLineData2MruCache* scan_line_data2_cache = CacheManager::GetScanLineData2MruCache();
298 scan_line_data2_cache->UpdateCache(key, scan_line_data2);
299 result = PaintFromScanLineData2(psub, *tmp, key, overlay);
301 if (result)
303 overlay_key_cache->UpdateCache(overlay_no_offset_key, key);
305 return result;
308 bool CWord::PaintFromRawData( const CPoint& psub, const CPoint& trans_org, const OverlayKey& key, SharedPtrOverlay* overlay )
310 PathDataMruCache* path_data_cache = CacheManager::GetPathDataMruCache();
312 PathData *tmp=new PathData();
313 SharedPtrPathData path_data(tmp);
314 if(!CreatePath(tmp))
316 return false;
318 path_data_cache->UpdateCache(key, path_data);
319 return PaintFromPathData(psub, trans_org, *tmp, key, overlay);
322 bool CWord::DoPaint(const CPoint& psub, const CPoint& trans_org, SharedPtrOverlay* overlay, const OverlayKey& key)
324 bool result = true;
325 OverlayNoBlurMruCache* overlay_no_blur_cache = CacheManager::GetOverlayNoBlurMruCache();
326 POSITION pos = overlay_no_blur_cache->Lookup(key);
328 if(pos!=NULL)
330 SharedPtrOverlay raterize_result = overlay_no_blur_cache->GetAt(pos);
331 overlay_no_blur_cache->UpdateCache( pos );
332 PaintFromNoneBluredOverlay(raterize_result, key, overlay);
334 else
336 ScanLineData2MruCache* scan_line_data_cache = CacheManager::GetScanLineData2MruCache();
337 pos = scan_line_data_cache->Lookup(key);
338 if(pos!=NULL)
340 SharedPtrConstScanLineData2 scan_line_data = scan_line_data_cache->GetAt(pos);
341 scan_line_data_cache->UpdateCache( pos );
342 result = PaintFromScanLineData2(psub, *scan_line_data, key, overlay);
344 else
346 PathDataMruCache* path_data_cache = CacheManager::GetPathDataMruCache();
347 POSITION pos_path = path_data_cache->Lookup(key);
348 if(pos_path!=NULL)
350 SharedPtrConstPathData path_data = path_data_cache->GetAt(pos_path); //important! copy not ref
351 path_data_cache->UpdateCache( pos_path );
352 result = PaintFromPathData(psub, trans_org, *path_data, key, overlay);
354 else
356 result = PaintFromRawData(psub, trans_org, key, overlay);
360 return result;
363 bool CWord::NeedTransform()
365 return (fabs(m_style.get().fontScaleX - 100) > 0.000001) ||
366 (fabs(m_style.get().fontScaleY - 100) > 0.000001) ||
367 (fabs(m_style.get().fontAngleX) > 0.000001) ||
368 (fabs(m_style.get().fontAngleY) > 0.000001) ||
369 (fabs(m_style.get().fontAngleZ) > 0.000001) ||
370 (fabs(m_style.get().fontShiftX) > 0.000001) ||
371 (fabs(m_style.get().fontShiftY) > 0.000001);
374 void CWord::Transform(PathData* path_data, const CPoint& org)
376 //// CPUID from VDub
377 //bool fSSE2 = !!(g_cpuid.m_flags & CCpuID::sse2);
379 //if(fSSE2) { // SSE code
380 // Transform_SSE2(path_data, org);
381 //} else // C-code
382 Transform_C(path_data, org);
385 void CWord::Transform_C(PathData* path_data, const CPoint &org )
387 double scalex = m_style.get().fontScaleX/100;
388 double scaley = m_style.get().fontScaleY/100;
390 double caz = cos((3.1415/180)*m_style.get().fontAngleZ);
391 double saz = sin((3.1415/180)*m_style.get().fontAngleZ);
392 double cax = cos((3.1415/180)*m_style.get().fontAngleX);
393 double sax = sin((3.1415/180)*m_style.get().fontAngleX);
394 double cay = cos((3.1415/180)*m_style.get().fontAngleY);
395 double say = sin((3.1415/180)*m_style.get().fontAngleY);
397 #ifdef _VSMOD
398 // patch m003. random text points
399 double xrnd = m_style.get().mod_rand.X*100;
400 double yrnd = m_style.get().mod_rand.Y*100;
401 double zrnd = m_style.get().mod_rand.Z*100;
403 srand(m_style.get().mod_rand.Seed);
405 // patch m008. distort
406 int xsz,ysz;
407 double dst1x,dst1y,dst2x,dst2y,dst3x,dst3y;
408 int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
410 bool is_dist = m_style.get().mod_distort.enabled;
411 if (is_dist) {
412 for(int i = 0; i < path_data->mPathPoints; i++) {
413 if(minx > path_data->mpPathPoints[i].x) {
414 minx = path_data->mpPathPoints[i].x;
416 if(miny > path_data->mpPathPoints[i].y) {
417 miny = path_data->mpPathPoints[i].y;
419 if(maxx < path_data->mpPathPoints[i].x) {
420 maxx = path_data->mpPathPoints[i].x;
422 if(maxy < path_data->mpPathPoints[i].y) {
423 maxy = path_data->mpPathPoints[i].y;
427 xsz = max(maxx - minx, 0);
428 ysz = max(maxy - miny, 0);
430 dst1x = m_style.get().mod_distort.pointsx[0];
431 dst1y = m_style.get().mod_distort.pointsy[0];
432 dst2x = m_style.get().mod_distort.pointsx[1];
433 dst2y = m_style.get().mod_distort.pointsy[1];
434 dst3x = m_style.get().mod_distort.pointsx[2];
435 dst3y = m_style.get().mod_distort.pointsy[2];
437 #endif
439 for (int i = 0; i < path_data->mPathPoints; i++) {
440 double x, y, z, xx, yy, zz;
442 x = path_data->mpPathPoints[i].x;
443 y = path_data->mpPathPoints[i].y;
444 #ifdef _VSMOD
445 // patch m002. Z-coord
446 z = m_style.get().mod_z;
448 double u, v;
449 if (is_dist) {
450 u = (x-minx) / xsz;
451 v = (y-miny) / ysz;
453 x = minx+(0 + (dst1x - 0)*u + (dst3x-0)*v+(0+dst2x-dst1x-dst3x)*u*v)*xsz;
454 y = miny+(0 + (dst1y - 0)*u + (dst3y-0)*v+(0+dst2y-dst1y-dst3y)*u*v)*ysz;
455 //P = P0 + (P1 - P0)u + (P3 - P0)v + (P0 + P2 - P1 - P3)uv
458 // patch m003. random text points
459 x = xrnd > 0 ? (xrnd - rand() % (int)(xrnd * 2 + 1)) / 100.0 + x : x;
460 y = yrnd > 0 ? (yrnd - rand() % (int)(yrnd * 2 + 1)) / 100.0 + y : y;
461 z = zrnd > 0 ? (zrnd - rand() % (int)(zrnd * 2 + 1)) / 100.0 + z : z;
462 #else
463 z = 0;
464 #endif
465 double _x = x;
466 x = scalex * (x + m_style.get().fontShiftX * y) - org.x;
467 y = scaley * (y + m_style.get().fontShiftY * _x) - org.y;
469 xx = x*caz + y*saz;
470 yy = -(x*saz - y*caz);
471 zz = z;
473 x = xx;
474 y = yy*cax + zz*sax;
475 z = yy*sax - zz*cax;
477 xx = x*cay + z*say;
478 yy = y;
479 zz = x*say - z*cay;
481 zz = max(zz, -19000);
483 x = (xx * 20000) / (zz + 20000);
484 y = (yy * 20000) / (zz + 20000);
486 path_data->mpPathPoints[i].x = (LONG)(x + org.x + 0.5);
487 path_data->mpPathPoints[i].y = (LONG)(y + org.y + 0.5);
491 void CWord::Transform_SSE2(PathData* path_data, const CPoint &org )
493 // __m128 union data type currently not supported with Intel C++ Compiler, so just call C version
494 #ifdef __ICL
495 Transform_C(org);
496 #else
497 // SSE code
498 // speed up ~1.5-1.7x
499 double scalex = m_style.get().fontScaleX/100;
500 double scaley = m_style.get().fontScaleY/100;
502 double caz = cos((3.1415/180)*m_style.get().fontAngleZ);
503 double saz = sin((3.1415/180)*m_style.get().fontAngleZ);
504 double cax = cos((3.1415/180)*m_style.get().fontAngleX);
505 double sax = sin((3.1415/180)*m_style.get().fontAngleX);
506 double cay = cos((3.1415/180)*m_style.get().fontAngleY);
507 double say = sin((3.1415/180)*m_style.get().fontAngleY);
509 __m128 __xshift = _mm_set_ps1(m_style.get().fontShiftX);
510 __m128 __yshift = _mm_set_ps1(m_style.get().fontShiftY);
512 __m128 __xorg = _mm_set_ps1(org.x);
513 __m128 __yorg = _mm_set_ps1(org.y);
515 __m128 __xscale = _mm_set_ps1(scalex);
516 __m128 __yscale = _mm_set_ps1(scaley);
518 #ifdef _VSMOD
519 // patch m003. random text points
520 double xrnd = m_style.get().mod_rand.X*100;
521 double yrnd = m_style.get().mod_rand.Y*100;
522 double zrnd = m_style.get().mod_rand.Z*100;
524 srand(m_style.get().mod_rand.Seed);
526 __m128 __xsz = _mm_setzero_ps();
527 __m128 __ysz = _mm_setzero_ps();
529 __m128 __dst1x, __dst1y, __dst213x, __dst213y, __dst3x, __dst3y;
531 __m128 __miny;
532 __m128 __minx = _mm_set_ps(INT_MAX, INT_MAX, 0, 0);
533 __m128 __max = _mm_set_ps(-INT_MAX, -INT_MAX, 1, 1);
535 bool is_dist = m_style.get().mod_distort.enabled;
536 if(is_dist) {
537 for(int i = 0; i < path_data->mPathPoints; i++) {
538 __m128 __point = _mm_set_ps(path_data->mpPathPoints[i].x, path_data->mpPathPoints[i].y, 0, 0);
539 __minx = _mm_min_ps(__minx, __point);
540 __max = _mm_max_ps(__max, __point);
543 __m128 __zero = _mm_setzero_ps();
544 __max = _mm_sub_ps(__max, __minx); // xsz, ysz, 1, 1
545 __max = _mm_max_ps(__max, __zero);
547 __xsz = _mm_shuffle_ps(__max, __max, _MM_SHUFFLE(3,3,3,3));
548 __ysz = _mm_shuffle_ps(__max, __max, _MM_SHUFFLE(2,2,2,2));
550 __miny = _mm_shuffle_ps(__minx, __minx, _MM_SHUFFLE(2,2,2,2));
551 __minx = _mm_shuffle_ps(__minx, __minx, _MM_SHUFFLE(3,3,3,3));
553 __dst1x = _mm_set_ps1(m_style.get().mod_distort.pointsx[0]);
554 __dst1y = _mm_set_ps1(m_style.get().mod_distort.pointsy[0]);
555 __dst3x = _mm_set_ps1(m_style.get().mod_distort.pointsx[2]);
556 __dst3y = _mm_set_ps1(m_style.get().mod_distort.pointsy[2]);
557 __dst213x = _mm_set_ps1(m_style.get().mod_distort.pointsx[1]); // 2 - 1 - 3
558 __dst213x = _mm_sub_ps(__dst213x, __dst1x);
559 __dst213x = _mm_sub_ps(__dst213x, __dst3x);
561 __dst213y = _mm_set_ps1(m_style.get().mod_distort.pointsy[1]);
562 __dst213x = _mm_sub_ps(__dst213y, __dst1y);
563 __dst213x = _mm_sub_ps(__dst213y, __dst3y);
565 #endif
567 __m128 __caz = _mm_set_ps1(caz);
568 __m128 __saz = _mm_set_ps1(saz);
569 __m128 __cax = _mm_set_ps1(cax);
570 __m128 __sax = _mm_set_ps1(sax);
571 __m128 __cay = _mm_set_ps1(cay);
572 __m128 __say = _mm_set_ps1(say);
574 // this can be paralleled for openmp
575 int mPathPointsD4 = path_data->mPathPoints / 4;
576 int mPathPointsM4 = path_data->mPathPoints % 4;
578 for(ptrdiff_t i = 0; i < mPathPointsD4 + 1; i++) {
579 POINT* const temp_points = path_data->mpPathPoints + 4 * i;
581 __m128 __pointx, __pointy;
582 // we can't use load .-.
583 if(i != mPathPointsD4) {
584 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, temp_points[2].x, temp_points[3].x);
585 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, temp_points[2].y, temp_points[3].y);
586 } else { // last cycle
587 switch(mPathPointsM4) {
588 default:
589 case 0:
590 continue;
591 case 1:
592 __pointx = _mm_set_ps(temp_points[0].x, 0, 0, 0);
593 __pointy = _mm_set_ps(temp_points[0].y, 0, 0, 0);
594 break;
595 case 2:
596 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, 0, 0);
597 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, 0, 0);
598 break;
599 case 3:
600 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, temp_points[2].x, 0);
601 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, temp_points[2].y, 0);
602 break;
606 #ifdef _VSMOD
607 __m128 __pointz = _mm_set_ps1(m_style.get().mod_z);
609 // distort
610 if(is_dist) {
611 //P = P0 + (P1 - P0)u + (P3 - P0)v + (P0 + P2 - P1 - P3)uv
612 __m128 __u = _mm_sub_ps(__pointx, __minx);
613 __m128 __v = _mm_sub_ps(__pointy, __miny);
614 __m128 __1_xsz = _mm_rcp_ps(__xsz);
615 __m128 __1_ysz = _mm_rcp_ps(__ysz);
616 __u = _mm_mul_ps(__u, __1_xsz);
617 __v = _mm_mul_ps(__v, __1_ysz);
619 // x
620 __pointx = _mm_mul_ps(__dst213x, __u);
621 __pointx = _mm_mul_ps(__pointx, __v);
623 __m128 __tmpx = _mm_mul_ps(__dst3x, __v);
624 __pointx = _mm_add_ps(__pointx, __tmpx);
625 __tmpx = _mm_mul_ps(__dst1x, __u);
626 __pointx = _mm_add_ps(__pointx, __tmpx);
628 __pointx = _mm_mul_ps(__pointx, __xsz);
629 __pointx = _mm_add_ps(__pointx, __minx);
631 // y
632 __pointy = _mm_mul_ps(__dst213y, __u);
633 __pointy = _mm_mul_ps(__pointy, __v);
635 __m128 __tmpy = _mm_mul_ps(__dst3y, __v);
636 __pointy = _mm_add_ps(__pointy, __tmpy);
637 __tmpy = _mm_mul_ps(__dst1y, __u);
638 __pointy = _mm_add_ps(__pointy, __tmpy);
640 __pointy = _mm_mul_ps(__pointy, __ysz);
641 __pointy = _mm_add_ps(__pointy, __miny);
644 // randomize
645 if(xrnd!=0 || yrnd!=0 || zrnd!=0) {
646 __declspec(align(16)) float rx[4], ry[4], rz[4];
647 for(int k=0; k<4; k++) {
648 rx[k] = xrnd > 0 ? (xrnd - rand() % (int)(xrnd * 2 + 1)) : 0;
649 ry[k] = yrnd > 0 ? (yrnd - rand() % (int)(yrnd * 2 + 1)) : 0;
650 rz[k] = zrnd > 0 ? (zrnd - rand() % (int)(zrnd * 2 + 1)) : 0;
652 __m128 __001 = _mm_set_ps1(0.01f);
654 if(xrnd!=0) {
655 __m128 __rx = _mm_load_ps(rx);
656 __rx = _mm_mul_ps(__rx, __001);
657 __pointx = _mm_add_ps(__pointx, __rx);
660 if(yrnd!=0) {
661 __m128 __ry = _mm_load_ps(ry);
662 __ry = _mm_mul_ps(__ry, __001);
663 __pointy = _mm_add_ps(__pointy, __ry);
666 if(zrnd!=0) {
667 __m128 __rz = _mm_load_ps(rz);
668 __rz = _mm_mul_ps(__rz, __001);
669 __pointz = _mm_add_ps(__pointz, __rz);
672 #else
673 __m128 __pointz = _mm_set_ps1(0);
674 #endif
676 // scale and shift
677 __m128 __tmpx;
678 if(m_style.get().fontShiftX!=0) {
679 __tmpx = _mm_mul_ps(__xshift, __pointy);
680 __tmpx = _mm_add_ps(__tmpx, __pointx);
681 } else {
682 __tmpx = __pointx;
684 __tmpx = _mm_mul_ps(__tmpx, __xscale);
685 __tmpx = _mm_sub_ps(__tmpx, __xorg);
687 __m128 __tmpy;
688 if(m_style.get().fontShiftY!=0) {
689 __tmpy = _mm_mul_ps(__yshift, __pointx);
690 __tmpy = _mm_add_ps(__tmpy, __pointy);
691 } else {
692 __tmpy = __pointy;
694 __tmpy = _mm_mul_ps(__tmpy, __yscale);
695 __tmpy = _mm_sub_ps(__tmpy, __yorg);
697 // rotate
698 __m128 __xx = _mm_mul_ps(__tmpx, __caz);
699 __m128 __yy = _mm_mul_ps(__tmpy, __saz);
700 __pointx = _mm_add_ps(__xx, __yy);
701 __xx = _mm_mul_ps(__tmpx, __saz);
702 __yy = _mm_mul_ps(__tmpy, __caz);
703 __pointy = _mm_sub_ps(__yy, __xx);
705 __m128 __zz = _mm_mul_ps(__pointz, __sax);
706 __yy = _mm_mul_ps(__pointy, __cax);
707 __pointy = _mm_add_ps(__yy, __zz);
708 __zz = _mm_mul_ps(__pointz, __cax);
709 __yy = _mm_mul_ps(__pointy, __sax);
710 __pointz = _mm_sub_ps(__zz, __yy);
712 __xx = _mm_mul_ps(__pointx, __cay);
713 __zz = _mm_mul_ps(__pointz, __say);
714 __pointx = _mm_add_ps(__xx, __zz);
715 __xx = _mm_mul_ps(__pointx, __say);
716 __zz = _mm_mul_ps(__pointz, __cay);
717 __pointz = _mm_sub_ps(__xx, __zz);
719 __zz = _mm_set_ps1(-19000);
720 __pointz = _mm_max_ps(__pointz, __zz);
722 __m128 __20000 = _mm_set_ps1(20000);
723 __zz = _mm_add_ps(__pointz, __20000);
724 __zz = _mm_rcp_ps(__zz);
726 __pointx = _mm_mul_ps(__pointx, __20000);
727 __pointx = _mm_mul_ps(__pointx, __zz);
729 __pointy = _mm_mul_ps(__pointy, __20000);
730 __pointy = _mm_mul_ps(__pointy, __zz);
732 __pointx = _mm_add_ps(__pointx, __xorg);
733 __pointy = _mm_add_ps(__pointy, __yorg);
735 __m128 __05 = _mm_set_ps1(0.5);
737 __pointx = _mm_add_ps(__pointx, __05);
738 __pointy = _mm_add_ps(__pointy, __05);
740 if(i == mPathPointsD4) { // last cycle
741 for(int k=0; k<mPathPointsM4; k++) {
742 temp_points[k].x = static_cast<LONG>(__pointx.m128_f32[3-k]);
743 temp_points[k].y = static_cast<LONG>(__pointy.m128_f32[3-k]);
745 } else {
746 for(int k=0; k<4; k++) {
747 temp_points[k].x = static_cast<LONG>(__pointx.m128_f32[3-k]);
748 temp_points[k].y = static_cast<LONG>(__pointy.m128_f32[3-k]);
752 #endif // __ICL
755 bool CWord::CreateOpaqueBox()
757 if(m_pOpaqueBox) return(true);
758 STSStyle style = m_style.get();
759 style.borderStyle = 0;
760 style.outlineWidthX = style.outlineWidthY = 0;
761 style.colors[0] = m_style.get().colors[2];
762 style.alpha[0] = m_style.get().alpha[2];
763 int w = (int)(m_style.get().outlineWidthX + 0.5);
764 int h = (int)(m_style.get().outlineWidthY + 0.5);
765 CStringW str;
766 str.Format(L"m %d %d l %d %d %d %d %d %d",
767 -w, -h,
768 m_width+w, -h,
769 m_width+w, m_ascent+m_descent+h,
770 -w, m_ascent+m_descent+h);
771 m_pOpaqueBox.reset( new CPolygon(FwSTSStyle(style), str, 0, 0, 0, 1.0/8, 1.0/8, 0) );
772 return(!!m_pOpaqueBox);
775 // CText
777 CText::CText(const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend)
778 : CWord(style, str, ktype, kstart, kend)
780 if(m_str == L" ")
782 m_fWhiteSpaceChar = true;
784 SharedPtrTextInfo text_info;
785 TextInfoCacheKey text_info_key;
786 text_info_key.m_str = m_str;
787 text_info_key.m_style = m_style;
788 text_info_key.UpdateHashValue();
789 TextInfoMruCache* text_info_cache = CacheManager::GetTextInfoCache();
790 POSITION pos = text_info_cache->Lookup(text_info_key);
791 if(pos==NULL)
793 TextInfo* tmp=new TextInfo();
794 GetTextInfo(tmp, m_style, m_str);
795 text_info.reset(tmp);
796 text_info_cache->UpdateCache(text_info_key, text_info);
798 else
800 text_info = text_info_cache->GetAt(pos);
801 text_info_cache->UpdateCache( pos );
803 this->m_ascent = text_info->m_ascent;
804 this->m_descent = text_info->m_descent;
805 this->m_width = text_info->m_width;
808 CText::CText( const CText& src ):CWord(src)
810 m_width = src.m_width;
813 SharedPtrCWord CText::Copy()
815 SharedPtrCWord result(new CText(*this));
816 return result;
819 bool CText::Append(const SharedPtrCWord& w)
821 boost::shared_ptr<CText> p = boost::dynamic_pointer_cast<CText>(w);
822 return (p && CWord::Append(w));
825 bool CText::CreatePath(PathData* path_data)
827 FwCMyFont font(m_style);
828 HFONT hOldFont = SelectFont(g_hDC, font.get());
829 int width = 0;
830 if(m_style.get().fontSpacing || (long)GetVersion() < 0)
832 bool bFirstPath = true;
833 for(LPCWSTR s = m_str; *s; s++)
835 CSize extent;
836 if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
837 path_data->PartialBeginPath(g_hDC, bFirstPath);
838 bFirstPath = false;
839 TextOutW(g_hDC, 0, 0, s, 1);
840 path_data->PartialEndPath(g_hDC, width, 0);
841 width += extent.cx + (int)m_style.get().fontSpacing;
844 else
846 CSize extent;
847 if(!GetTextExtentPoint32W(g_hDC, m_str, m_str.GetLength(), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
848 path_data->BeginPath(g_hDC);
849 TextOutW(g_hDC, 0, 0, m_str, m_str.GetLength());
850 path_data->EndPath(g_hDC);
852 SelectFont(g_hDC, hOldFont);
853 return(true);
856 void CText::GetTextInfo(TextInfo *output, const FwSTSStyle& style, const CStringW& str )
858 FwCMyFont font(style);
859 output->m_ascent = (int)(style.get().fontScaleY/100*font.get().m_ascent);
860 output->m_descent = (int)(style.get().fontScaleY/100*font.get().m_descent);
862 HFONT hOldFont = SelectFont(g_hDC, font.get());
863 if(style.get().fontSpacing || (long)GetVersion() < 0)
865 bool bFirstPath = true;
866 for(LPCWSTR s = str; *s; s++)
868 CSize extent;
869 if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
870 output->m_width += extent.cx + (int)style.get().fontSpacing;
872 // m_width -= (int)m_style.get().fontSpacing; // TODO: subtract only at the end of the line
874 else
876 CSize extent;
877 if(!GetTextExtentPoint32W(g_hDC, str, wcslen(str), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
878 output->m_width += extent.cx;
880 output->m_width = (int)(style.get().fontScaleX/100*output->m_width + 4) >> 3;
881 SelectFont(g_hDC, hOldFont);
884 // CPolygon
886 CPolygon::CPolygon(const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend, double scalex, double scaley, int baseline)
887 : CWord(style, str, ktype, kstart, kend)
888 , m_scalex(scalex), m_scaley(scaley), m_baseline(baseline)
890 ParseStr();
893 CPolygon::CPolygon(CPolygon& src) : CWord(src)
895 m_scalex = src.m_scalex;
896 m_scaley = src.m_scaley;
897 m_baseline = src.m_baseline;
898 m_width = src.m_width;
899 m_ascent = src.m_ascent;
900 m_descent = src.m_descent;
901 m_pathTypesOrg.Copy(src.m_pathTypesOrg);
902 m_pathPointsOrg.Copy(src.m_pathPointsOrg);
904 CPolygon::~CPolygon()
908 SharedPtrCWord CPolygon::Copy()
910 SharedPtrCWord result(DNew CPolygon(*this));
911 return result;
914 bool CPolygon::Append(const SharedPtrCWord& w)
916 // TODO
917 return(false);
920 bool CPolygon::Get6BitFixedPoint(CStringW& str, LONG& ret)
922 LPWSTR s = (LPWSTR)(LPCWSTR)str, e = s;
923 ret = wcstod(str, &e) * 64;
924 str.Delete(0,e-s);
925 XY_LOG_INFO(ret);
926 return(e > s);
929 bool CPolygon::GetPOINT(CStringW& str, POINT& ret)
931 return(Get6BitFixedPoint(str, ret.x) && Get6BitFixedPoint(str, ret.y));
934 bool CPolygon::ParseStr()
936 if(m_pathTypesOrg.GetCount() > 0) return(true);
937 CPoint p;
938 int j, lastsplinestart = -1, firstmoveto = -1, lastmoveto = -1;
939 CStringW str = m_str;
940 str.SpanIncluding(L"mnlbspc 0123456789");
941 str.Replace(L"m", L"*m");
942 str.Replace(L"n", L"*n");
943 str.Replace(L"l", L"*l");
944 str.Replace(L"b", L"*b");
945 str.Replace(L"s", L"*s");
946 str.Replace(L"p", L"*p");
947 str.Replace(L"c", L"*c");
948 int k = 0;
949 for(CStringW s = str.Tokenize(L"*", k); !s.IsEmpty(); s = str.Tokenize(L"*", k))
951 WCHAR c = s[0];
952 s.TrimLeft(L"mnlbspc ");
953 switch(c)
955 case 'm':
956 lastmoveto = m_pathTypesOrg.GetCount();
957 if(firstmoveto == -1)
958 firstmoveto = lastmoveto;
959 while(GetPOINT(s, p)) {
960 m_pathTypesOrg.Add(PT_MOVETO);
961 m_pathPointsOrg.Add(p);
963 break;
964 case 'n':
965 while(GetPOINT(s, p)) {
966 m_pathTypesOrg.Add(PT_MOVETONC);
967 m_pathPointsOrg.Add(p);
969 break;
970 case 'l':
971 if (m_pathPointsOrg.GetCount() < 1) {
972 break;
974 while(GetPOINT(s, p)) {
975 m_pathTypesOrg.Add(PT_LINETO);
976 m_pathPointsOrg.Add(p);
978 break;
979 case 'b':
980 j = m_pathTypesOrg.GetCount();
981 if (j < 1) {
982 break;
984 while(GetPOINT(s, p)) {
985 m_pathTypesOrg.Add(PT_BEZIERTO);
986 m_pathPointsOrg.Add(p);
987 j++;
989 j = m_pathTypesOrg.GetCount() - ((m_pathTypesOrg.GetCount()-j)%3);
990 m_pathTypesOrg.SetCount(j);
991 m_pathPointsOrg.SetCount(j);
992 break;
993 case 's':
994 if (m_pathPointsOrg.GetCount() < 1) {
995 break;
998 j = lastsplinestart = m_pathTypesOrg.GetCount();
999 int i = 3;
1000 while(i-- && GetPOINT(s, p)) {
1001 m_pathTypesOrg.Add(PT_BSPLINETO);
1002 m_pathPointsOrg.Add(p);
1003 j++;
1005 if(m_pathTypesOrg.GetCount()-lastsplinestart < 3) {
1006 m_pathTypesOrg.SetCount(lastsplinestart);
1007 m_pathPointsOrg.SetCount(lastsplinestart);
1008 lastsplinestart = -1;
1011 // no break here
1012 case 'p':
1013 if (m_pathPointsOrg.GetCount() < 3) {
1014 break;
1016 while(GetPOINT(s, p)) {
1017 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1018 m_pathPointsOrg.Add(p);
1020 break;
1021 case 'c':
1022 if(lastsplinestart > 0)
1024 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1025 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1026 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1027 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)
1028 m_pathPointsOrg.Add(p);
1029 p = m_pathPointsOrg[lastsplinestart];
1030 m_pathPointsOrg.Add(p);
1031 p = m_pathPointsOrg[lastsplinestart+1];
1032 m_pathPointsOrg.Add(p);
1033 lastsplinestart = -1;
1035 break;
1036 default:
1037 break;
1040 if(lastmoveto == -1 || firstmoveto > 0)
1042 m_pathTypesOrg.RemoveAll();
1043 m_pathPointsOrg.RemoveAll();
1044 return(false);
1046 int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
1047 for(size_t i = 0; i < m_pathTypesOrg.GetCount(); i++)
1049 m_pathPointsOrg[i].x = (int)(m_scalex * m_pathPointsOrg[i].x);
1050 m_pathPointsOrg[i].y = (int)(m_scaley * m_pathPointsOrg[i].y);
1051 if(minx > m_pathPointsOrg[i].x) minx = m_pathPointsOrg[i].x;
1052 if(miny > m_pathPointsOrg[i].y) miny = m_pathPointsOrg[i].y;
1053 if(maxx < m_pathPointsOrg[i].x) maxx = m_pathPointsOrg[i].x;
1054 if(maxy < m_pathPointsOrg[i].y) maxy = m_pathPointsOrg[i].y;
1056 m_width = max(maxx - minx, 0);
1057 m_ascent = max(maxy - miny, 0);
1058 int baseline = (int)(64 * m_scaley * m_baseline);
1059 m_descent = baseline;
1060 m_ascent -= baseline;
1061 m_width = ((int)(m_style.get().fontScaleX/100 * m_width) + 4) >> 3;
1062 m_ascent = ((int)(m_style.get().fontScaleY/100 * m_ascent) + 4) >> 3;
1063 m_descent = ((int)(m_style.get().fontScaleY/100 * m_descent) + 4) >> 3;
1064 return(true);
1067 bool CPolygon::CreatePath(PathData* path_data)
1069 int len = m_pathTypesOrg.GetCount();
1070 if(len == 0) return(false);
1071 if(path_data->mPathPoints != len)
1073 path_data->mpPathTypes = (BYTE*)realloc(path_data->mpPathTypes, len*sizeof(BYTE));
1074 path_data->mpPathPoints = (POINT*)realloc(path_data->mpPathPoints, len*sizeof(POINT));
1075 if(!path_data->mpPathTypes || !path_data->mpPathPoints) return(false);
1076 path_data->mPathPoints = len;
1078 memcpy(path_data->mpPathTypes, m_pathTypesOrg.GetData(), len*sizeof(BYTE));
1079 memcpy(path_data->mpPathPoints, m_pathPointsOrg.GetData(), len*sizeof(POINT));
1080 return(true);
1083 // CClipper
1085 CClipper::CClipper(CStringW str, CSize size, double scalex, double scaley, bool inverse)
1086 : m_polygon( new CPolygon(FwSTSStyle(), str, 0, 0, 0, scalex, scaley, 0) )
1087 , m_size(size), m_inverse(inverse)
1088 , m_effectType(-1), m_painted(false)
1093 CClipper::~CClipper()
1097 GrayImage2* CClipper::PaintSimpleClipper()
1099 GrayImage2* result = NULL;
1100 if(m_size.cx < 0 || m_size.cy < 0)
1101 return result;
1103 SharedPtrOverlay overlay;
1104 CWordPaintMachine::PaintBody( m_polygon, CPoint(0, 0), CPoint(0, 0), &overlay );
1105 int w = overlay->mOverlayWidth, h = overlay->mOverlayHeight;
1106 int x = (overlay->mOffsetX+4)>>3, y = (overlay->mOffsetY+4)>>3;
1107 result = new GrayImage2();
1108 if( !result )
1109 return result;
1110 result->data = overlay->mBody;
1111 result->pitch = overlay->mOverlayPitch;
1112 result->size.SetSize(w, h);
1113 result->left_top.SetPoint(x, y);
1114 return result;
1117 GrayImage2* CClipper::PaintBaseClipper()
1119 GrayImage2* result = NULL;
1120 //m_pAlphaMask = NULL;
1121 if(m_size.cx < 0 || m_size.cy < 0)
1122 return result;
1124 SharedPtrOverlay overlay;
1125 CWordPaintMachine::PaintBody( m_polygon, CPoint(0, 0), CPoint(0, 0), &overlay );
1126 int w = overlay->mOverlayWidth, h = overlay->mOverlayHeight;
1127 int x = (overlay->mOffsetX+4)>>3, y = (overlay->mOffsetY+4)>>3;
1128 int xo = 0, yo = 0;
1129 if(x < 0) {xo = -x; w -= -x; x = 0;}
1130 if(y < 0) {yo = -y; h -= -y; y = 0;}
1131 if(x+w > m_size.cx) w = m_size.cx-x;
1132 if(y+h > m_size.cy) h = m_size.cy-y;
1133 if(w <= 0 || h <= 0) return result;
1135 result = new GrayImage2();
1136 if( !result )
1137 return result;
1138 result->data.reset( reinterpret_cast<BYTE*>(xy_malloc(m_size.cx*m_size.cy)), xy_free );
1139 result->pitch = m_size.cx;
1140 result->size = m_size;
1141 result->left_top.SetPoint(0, 0);
1143 BYTE * result_data = result->data.get();
1144 if(!result_data)
1146 delete result;
1147 return NULL;
1150 memset( result_data, 0, m_size.cx*m_size.cy );
1152 const BYTE* src = overlay->mBody.get() + (overlay->mOverlayPitch * yo + xo);
1153 BYTE* dst = result_data + m_size.cx * y + x;
1154 while(h--)
1156 //for(int wt=0; wt<w; ++wt)
1157 // dst[wt] = src[wt];
1158 memcpy(dst, src, w);
1159 src += overlay->mOverlayPitch;
1160 dst += m_size.cx;
1162 if(m_inverse)
1164 BYTE* dst = result_data;
1165 for(int i = m_size.cx*m_size.cy; i>0; --i, ++dst)
1166 *dst = 0x40 - *dst; // mask is 6 bit
1168 return result;
1171 GrayImage2* CClipper::PaintBannerClipper()
1173 int width = m_effect.param[2];
1174 int w = m_size.cx, h = m_size.cy;
1176 GrayImage2* result = PaintBaseClipper();
1177 if(!result)
1178 return result;
1180 int da = (64<<8)/width;
1181 BYTE* am = result->data.get();
1182 for(int j = 0; j < h; j++, am += w)
1184 int a = 0;
1185 int k = min(width, w);
1186 for(int i = 0; i < k; i++, a += da)
1187 am[i] = (am[i]*a)>>14;
1188 a = 0x40<<8;
1189 k = w-width;
1190 if(k < 0) {a -= -k*da; k = 0;}
1191 for(int i = k; i < w; i++, a -= da)
1192 am[i] = (am[i]*a)>>14;
1194 return result;
1197 GrayImage2* CClipper::PaintScrollClipper()
1199 int height = m_effect.param[4];
1200 int w = m_size.cx, h = m_size.cy;
1202 GrayImage2* result = PaintBaseClipper();
1203 if(!result)
1204 return result;
1206 BYTE* data = result->data.get();
1208 int da = (64<<8)/height;
1209 int a = 0;
1210 int k = m_effect.param[0]>>3;
1211 int l = k+height;
1212 if(k < 0) {a += -k*da; k = 0;}
1213 if(l > h) {l = h;}
1214 if(k < h)
1216 BYTE* am = &data[k*w];
1217 memset(data, 0, am - data);
1218 for(int j = k; j < l; j++, a += da)
1220 for(int i = 0; i < w; i++, am++)
1221 *am = ((*am)*a)>>14;
1224 da = -(64<<8)/height;
1225 a = 0x40<<8;
1226 l = m_effect.param[1]>>3;
1227 k = l-height;
1228 if(k < 0) {a += -k*da; k = 0;}
1229 if(l > h) {l = h;}
1230 if(k < h)
1232 BYTE* am = &data[k*w];
1233 int j = k;
1234 for(; j < l; j++, a += da)
1236 for(int i = 0; i < w; i++, am++)
1237 *am = ((*am)*a)>>14;
1239 memset(am, 0, (h-j)*w);
1241 return result;
1244 GrayImage2* CClipper::Paint()
1246 GrayImage2* result = NULL;
1247 switch(m_effectType)
1249 case -1:
1250 if (!m_inverse)
1252 result = PaintSimpleClipper();
1254 else
1256 result = PaintBaseClipper();
1258 break;
1259 case EF_BANNER:
1260 result = PaintBannerClipper();
1261 break;
1262 case EF_SCROLL:
1263 result = PaintScrollClipper();
1264 break;
1266 return result;
1269 void CClipper::SetEffect( const Effect& effect, int effectType )
1271 m_effectType = effectType;
1272 m_effect = effect;
1275 SharedPtrGrayImage2 CClipper::GetAlphaMask( const SharedPtrCClipper& clipper )
1277 SharedPtrGrayImage2 result;
1278 CClipperPaintMachine paint_machine(clipper);
1279 paint_machine.Paint(&result);
1280 return result;
1283 // CLine
1285 CLine::~CLine()
1287 //POSITION pos = GetHeadPosition();
1288 //while(pos) delete GetNext(pos);
1291 void CLine::Compact()
1293 POSITION pos = GetHeadPosition();
1294 while(pos)
1296 SharedPtrCWord w = GetNext(pos);
1297 if(!w->m_fWhiteSpaceChar) break;
1298 m_width -= w->m_width;
1299 // delete w;
1300 RemoveHead();
1302 pos = GetTailPosition();
1303 while(pos)
1305 SharedPtrCWord w = GetPrev(pos);
1306 if(!w->m_fWhiteSpaceChar) break;
1307 m_width -= w->m_width;
1308 // delete w;
1309 RemoveTail();
1311 if(IsEmpty()) return;
1312 CLine l;
1313 l.AddTailList(this);
1314 RemoveAll();
1315 SharedPtrCWord last;
1316 pos = l.GetHeadPosition();
1317 while(pos)
1319 SharedPtrCWord w = l.GetNext(pos);
1320 if(!last || !last->Append(w))
1321 AddTail(last = w->Copy());
1323 m_ascent = m_descent = m_borderX = m_borderY = 0;
1324 pos = GetHeadPosition();
1325 while(pos)
1327 SharedPtrCWord w = GetNext(pos);
1328 if(m_ascent < w->m_ascent) m_ascent = w->m_ascent;
1329 if(m_descent < w->m_descent) m_descent = w->m_descent;
1330 if(m_borderX < w->m_style.get().outlineWidthX) m_borderX = (int)(w->m_style.get().outlineWidthX+0.5);
1331 if(m_borderY < w->m_style.get().outlineWidthY) m_borderY = (int)(w->m_style.get().outlineWidthY+0.5);
1335 CRect CLine::PaintAll( CompositeDrawItemList* output, SubPicDesc& spd, const CRect& clipRect,
1336 const SharedPtrCClipperPaintMachine &clipper, CPoint p, const CPoint& org, const int time, const int alpha )
1338 CRect bbox(0, 0, 0, 0);
1339 POSITION pos = GetHeadPosition();
1340 POSITION outputPos = output->GetHeadPosition();
1341 while(pos)
1343 SharedPtrCWord w = GetNext(pos);
1344 CompositeDrawItem& outputItem = output->GetNext(outputPos);
1345 if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
1346 CPoint shadowPos, outlinePos, bodyPos, transOrg;
1347 shadowPos.x = p.x + static_cast<int>(w->m_style.get().shadowDepthX+0.5);
1348 shadowPos.y = p.y + m_ascent - w->m_ascent + static_cast<int>(w->m_style.get().shadowDepthY+0.5);
1349 outlinePos = CPoint(p.x, p.y + m_ascent - w->m_ascent);
1350 bodyPos = CPoint(p.x, p.y + m_ascent - w->m_ascent);
1351 bool hasShadow = w->m_style.get().shadowDepthX != 0 || w->m_style.get().shadowDepthY != 0;
1352 bool hasOutline = w->m_style.get().outlineWidthX+w->m_style.get().outlineWidthY > 0 && !(w->m_ktype == 2 && time < w->m_kstart);
1353 bool hasBody = true;
1355 SharedPtrOverlayPaintMachine shadow_pm, outline_pm, body_pm;
1356 CWordPaintMachine::CreatePaintMachines(w, shadowPos, outlinePos, bodyPos, org,
1357 hasShadow ? &shadow_pm : NULL,
1358 hasOutline ? &outline_pm : NULL,
1359 hasBody ? &body_pm : NULL);
1361 //shadow
1362 if(hasShadow)
1364 DWORD a = 0xff - w->m_style.get().alpha[3];
1365 if(alpha > 0) a = MulDiv(a, 0xff - alpha, 0xff);
1366 COLORREF shadow = revcolor(w->m_style.get().colors[3]) | (a<<24);
1367 DWORD sw[6] = {shadow, -1};
1368 //xy
1369 if(spd.type == MSP_XY_AUYV)
1371 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1373 else if(spd.type == MSP_AYUV || spd.type == MSP_AYUV_PLANAR)
1375 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1377 if(w->m_style.get().borderStyle == 0)
1379 outputItem.shadow.reset(
1380 DrawItem::CreateDrawItem(shadow_pm, clipRect, clipper, shadowPos.x, shadowPos.y, sw,
1381 w->m_ktype > 0 || w->m_style.get().alpha[0] < 0xff,
1382 (w->m_style.get().outlineWidthX+w->m_style.get().outlineWidthY > 0) && !(w->m_ktype == 2 && time < w->m_kstart))
1385 else if(w->m_style.get().borderStyle == 1)
1387 outputItem.shadow.reset(
1388 DrawItem::CreateDrawItem( shadow_pm, clipRect, clipper, shadowPos.x, shadowPos.y, sw, true, false)
1392 //outline
1393 if(hasOutline)
1395 DWORD aoutline = w->m_style.get().alpha[2];
1396 if(alpha > 0) aoutline += MulDiv(alpha, 0xff - w->m_style.get().alpha[2], 0xff);
1397 COLORREF outline = revcolor(w->m_style.get().colors[2]) | ((0xff-aoutline)<<24);
1398 DWORD sw[6] = {outline, -1};
1399 //xy
1400 if(spd.type == MSP_XY_AUYV)
1402 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1404 else if(spd.type == MSP_AYUV || spd.type == MSP_AYUV_PLANAR)
1406 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1408 if(w->m_style.get().borderStyle == 0)
1410 outputItem.outline.reset(
1411 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)
1414 else if(w->m_style.get().borderStyle == 1)
1416 outputItem.outline.reset(
1417 DrawItem::CreateDrawItem(outline_pm, clipRect, clipper, outlinePos.x, outlinePos.y, sw, true, false)
1421 //body
1422 if(hasBody)
1424 // colors
1425 DWORD aprimary = w->m_style.get().alpha[0];
1426 if(alpha > 0) aprimary += MulDiv(alpha, 0xff - w->m_style.get().alpha[0], 0xff);
1427 COLORREF primary = revcolor(w->m_style.get().colors[0]) | ((0xff-aprimary)<<24);
1428 DWORD asecondary = w->m_style.get().alpha[1];
1429 if(alpha > 0) asecondary += MulDiv(alpha, 0xff - w->m_style.get().alpha[1], 0xff);
1430 COLORREF secondary = revcolor(w->m_style.get().colors[1]) | ((0xff-asecondary)<<24);
1431 DWORD sw[6] = {primary, 0, secondary};
1432 // karaoke
1433 double t;
1434 if(w->m_ktype == 0 || w->m_ktype == 2)
1436 t = time < w->m_kstart ? 0 : 1;
1438 else if(w->m_ktype == 1)
1440 if(time < w->m_kstart) t = 0;
1441 else if(time < w->m_kend)
1443 t = 1.0 * (time - w->m_kstart) / (w->m_kend - w->m_kstart);
1444 double angle = fmod(w->m_style.get().fontAngleZ, 360.0);
1445 if(angle > 90 && angle < 270)
1447 t = 1-t;
1448 COLORREF tmp = sw[0];
1449 sw[0] = sw[2];
1450 sw[2] = tmp;
1453 else t = 1.0;
1455 if(t >= 1)
1457 sw[1] = 0xffffffff;
1459 sw[3] = (int)(w->m_style.get().outlineWidthX + t*w->m_width) >> 3;
1460 sw[4] = sw[2];
1461 sw[5] = 0x00ffffff;
1462 //xy
1463 if(spd.type == MSP_XY_AUYV)
1465 sw[0] =rgb2yuv(sw[0], XY_AUYV);
1466 sw[2] =rgb2yuv(sw[2], XY_AUYV);
1467 sw[4] =rgb2yuv(sw[4], XY_AUYV);
1469 else if(spd.type == MSP_AYUV || spd.type == MSP_AYUV_PLANAR)
1471 sw[0] =rgb2yuv(sw[0], XY_AYUV);
1472 sw[2] =rgb2yuv(sw[2], XY_AYUV);
1473 sw[4] =rgb2yuv(sw[4], XY_AYUV);
1475 outputItem.body.reset(
1476 DrawItem::CreateDrawItem(body_pm, clipRect, clipper, bodyPos.x, bodyPos.y, sw, true, false)
1479 bbox |= CompositeDrawItem::GetDirtyRect(outputItem);
1480 p.x += w->m_width;
1482 return(bbox);
1485 void CLine::AddWord2Tail( SharedPtrCWord words )
1487 __super::AddTail(words);
1490 bool CLine::IsEmpty()
1492 return __super::IsEmpty();
1495 int CLine::GetWordCount()
1497 return GetCount();
1500 // CSubtitle
1502 CSubtitle::CSubtitle()
1504 memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
1505 m_clipInverse = false;
1506 m_scalex = m_scaley = 1;
1507 m_fAnimated2 = false;
1510 CSubtitle::~CSubtitle()
1512 Empty();
1515 void CSubtitle::Empty()
1517 POSITION pos = GetHeadPosition();
1518 while(pos) delete GetNext(pos);
1519 // pos = m_words.GetHeadPosition();
1520 // while(pos) delete m_words.GetNext(pos);
1521 for(int i = 0; i < EF_NUMBEROFEFFECTS; i++) {if(m_effects[i]) delete m_effects[i];}
1522 memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
1525 int CSubtitle::GetFullWidth()
1527 int width = 0;
1528 POSITION pos = m_words.GetHeadPosition();
1529 while(pos) width += m_words.GetNext(pos)->m_width;
1530 return(width);
1533 int CSubtitle::GetFullLineWidth(POSITION pos)
1535 int width = 0;
1536 while(pos)
1538 SharedPtrCWord w = m_words.GetNext(pos);
1539 if(w->m_fLineBreak) break;
1540 width += w->m_width;
1542 return(width);
1545 int CSubtitle::GetWrapWidth(POSITION pos, int maxwidth)
1547 if(m_wrapStyle == 0 || m_wrapStyle == 3)
1549 if(maxwidth > 0)
1551 // int fullwidth = GetFullWidth();
1552 int fullwidth = GetFullLineWidth(pos);
1553 int minwidth = fullwidth / ((abs(fullwidth) / maxwidth) + 1);
1554 int width = 0, wordwidth = 0;
1555 while(pos && width < minwidth)
1557 SharedPtrCWord w = m_words.GetNext(pos);
1558 wordwidth = w->m_width;
1559 if(abs(width + wordwidth) < abs(maxwidth)) width += wordwidth;
1561 maxwidth = width;
1562 if(m_wrapStyle == 3 && pos) maxwidth -= wordwidth;
1565 else if(m_wrapStyle == 1)
1567 // maxwidth = maxwidth;
1569 else if(m_wrapStyle == 2)
1571 maxwidth = INT_MAX;
1573 return(maxwidth);
1576 CLine* CSubtitle::GetNextLine(POSITION& pos, int maxwidth)
1578 if(pos == NULL) return(NULL);
1579 CLine* ret = new CLine();
1580 if(!ret) return(NULL);
1581 ret->m_width = ret->m_ascent = ret->m_descent = ret->m_borderX = ret->m_borderY = 0;
1582 maxwidth = GetWrapWidth(pos, maxwidth);
1583 bool fEmptyLine = true;
1584 while(pos)
1586 SharedPtrCWord w = m_words.GetNext(pos);
1587 if(ret->m_ascent < w->m_ascent) ret->m_ascent = w->m_ascent;
1588 if(ret->m_descent < w->m_descent) ret->m_descent = w->m_descent;
1589 if(ret->m_borderX < w->m_style.get().outlineWidthX) ret->m_borderX = (int)(w->m_style.get().outlineWidthX+0.5);
1590 if(ret->m_borderY < w->m_style.get().outlineWidthY) ret->m_borderY = (int)(w->m_style.get().outlineWidthY+0.5);
1591 if(w->m_fLineBreak)
1593 if(fEmptyLine) {ret->m_ascent /= 2; ret->m_descent /= 2; ret->m_borderX = ret->m_borderY = 0;}
1594 ret->Compact();
1595 return(ret);
1597 fEmptyLine = false;
1598 bool fWSC = w->m_fWhiteSpaceChar;
1599 int width = w->m_width;
1600 POSITION pos2 = pos;
1601 while(pos2)
1603 if(m_words.GetAt(pos2)->m_fWhiteSpaceChar != fWSC
1604 || m_words.GetAt(pos2)->m_fLineBreak) break;
1605 SharedPtrCWord w2 = m_words.GetNext(pos2);
1606 width += w2->m_width;
1608 if((ret->m_width += width) <= maxwidth || ret->IsEmpty())
1610 ret->AddWord2Tail(w);
1611 while(pos != pos2)
1613 ret->AddWord2Tail(m_words.GetNext(pos));
1615 pos = pos2;
1617 else
1619 if(pos) m_words.GetPrev(pos);
1620 else pos = m_words.GetTailPosition();
1621 ret->m_width -= width;
1622 break;
1625 ret->Compact();
1626 return(ret);
1629 void CSubtitle::CreateClippers(CSize size)
1631 size.cx >>= 3;
1632 size.cy >>= 3;
1633 if(m_effects[EF_BANNER] && m_effects[EF_BANNER]->param[2])
1635 int w = size.cx, h = size.cy;
1636 if(!m_pClipper)
1638 CStringW str;
1639 str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
1640 m_pClipper.reset( new CClipper(str, size, 1, 1, false) );
1641 if(!m_pClipper) return;
1643 m_pClipper->SetEffect( *m_effects[EF_BANNER], EF_BANNER );
1645 else if(m_effects[EF_SCROLL] && m_effects[EF_SCROLL]->param[4])
1647 int height = m_effects[EF_SCROLL]->param[4];
1648 int w = size.cx, h = size.cy;
1649 if(!m_pClipper)
1651 CStringW str;
1652 str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
1653 m_pClipper.reset( new CClipper(str, size, 1, 1, false) );
1654 if(!m_pClipper) return;
1656 m_pClipper->SetEffect(*m_effects[EF_SCROLL], EF_SCROLL);
1660 void CSubtitle::MakeLines(CSize size, CRect marginRect)
1662 CSize spaceNeeded(0, 0);
1663 bool fFirstLine = true;
1664 m_topborder = m_bottomborder = 0;
1665 CLine* l = NULL;
1666 POSITION pos = m_words.GetHeadPosition();
1667 while(pos)
1669 l = GetNextLine(pos, size.cx - marginRect.left - marginRect.right);
1670 if(!l) break;
1671 if(fFirstLine) {m_topborder = l->m_borderY; fFirstLine = false;}
1672 spaceNeeded.cx = max(l->m_width+l->m_borderX, spaceNeeded.cx);
1673 spaceNeeded.cy += l->m_ascent + l->m_descent;
1674 AddTail(l);
1676 if(l) m_bottomborder = l->m_borderY;
1677 m_rect = CRect(
1678 CPoint((m_scrAlignment%3) == 1 ? marginRect.left
1679 : (m_scrAlignment%3) == 2 ? (marginRect.left + (size.cx - marginRect.right) - spaceNeeded.cx + 1) / 2
1680 : (size.cx - marginRect.right - spaceNeeded.cx),
1681 m_scrAlignment <= 3 ? (size.cy - marginRect.bottom - spaceNeeded.cy)
1682 : m_scrAlignment <= 6 ? (marginRect.top + (size.cy - marginRect.bottom) - spaceNeeded.cy + 1) / 2
1683 : marginRect.top),
1684 spaceNeeded);
1687 POSITION CSubtitle::GetHeadLinePosition()
1689 return __super::GetHeadPosition();
1692 CLine* CSubtitle::GetNextLine( POSITION& pos )
1694 return __super::GetNext(pos);
1697 // CScreenLayoutAllocator
1699 void CScreenLayoutAllocator::Empty()
1701 m_subrects.RemoveAll();
1704 void CScreenLayoutAllocator::AdvanceToSegment(int segment, const CAtlArray<int>& sa)
1706 POSITION pos = m_subrects.GetHeadPosition();
1707 while(pos)
1709 POSITION prev = pos;
1710 SubRect& sr = m_subrects.GetNext(pos);
1711 bool fFound = false;
1712 if(abs(sr.segment - segment) <= 1) // using abs() makes it possible to play the subs backwards, too :)
1714 for(size_t i = 0; i < sa.GetCount() && !fFound; i++)
1716 if(sa[i] == sr.entry)
1718 sr.segment = segment;
1719 fFound = true;
1723 if(!fFound) m_subrects.RemoveAt(prev);
1727 CRect CScreenLayoutAllocator::AllocRect(CSubtitle* s, int segment, int entry, int layer, int collisions)
1729 // TODO: handle collisions == 1 (reversed collisions)
1730 POSITION pos = m_subrects.GetHeadPosition();
1731 while(pos)
1733 SubRect& sr = m_subrects.GetNext(pos);
1734 if(sr.segment == segment && sr.entry == entry)
1736 return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
1739 CRect r = s->m_rect + CRect(0, s->m_topborder, 0, s->m_bottomborder);
1740 bool fSearchDown = s->m_scrAlignment > 3;
1741 bool fOK;
1744 fOK = true;
1745 pos = m_subrects.GetHeadPosition();
1746 while(pos)
1748 SubRect& sr = m_subrects.GetNext(pos);
1749 if(layer == sr.layer && !(r & sr.r).IsRectEmpty())
1751 if(fSearchDown)
1753 r.bottom = sr.r.bottom + r.Height();
1754 r.top = sr.r.bottom;
1756 else
1758 r.top = sr.r.top - r.Height();
1759 r.bottom = sr.r.top;
1761 fOK = false;
1765 while(!fOK);
1766 SubRect sr;
1767 sr.r = r;
1768 sr.segment = segment;
1769 sr.entry = entry;
1770 sr.layer = layer;
1771 m_subrects.AddTail(sr);
1772 return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
1775 // CRenderedTextSubtitle
1777 CAtlMap<CStringW, CRenderedTextSubtitle::AssCmdType, CStringElementTraits<CStringW>> CRenderedTextSubtitle::m_cmdMap;
1779 CRenderedTextSubtitle::CRenderedTextSubtitle(CCritSec* pLock)
1780 : CSubPicProviderImpl(pLock)
1782 if( m_cmdMap.IsEmpty() )
1784 InitCmdMap();
1786 m_size = CSize(0, 0);
1787 if(g_hDC_refcnt == 0)
1789 g_hDC = CreateCompatibleDC(NULL);
1790 SetBkMode(g_hDC, TRANSPARENT);
1791 SetTextColor(g_hDC, 0xffffff);
1792 SetMapMode(g_hDC, MM_TEXT);
1794 g_hDC_refcnt++;
1797 CRenderedTextSubtitle::~CRenderedTextSubtitle()
1799 Deinit();
1800 g_hDC_refcnt--;
1801 if(g_hDC_refcnt == 0) DeleteDC(g_hDC);
1804 void CRenderedTextSubtitle::InitCmdMap()
1806 if( m_cmdMap.IsEmpty() )
1808 m_cmdMap.SetAt(L"1c", CMD_1c);
1809 m_cmdMap.SetAt(L"2c", CMD_2c);
1810 m_cmdMap.SetAt(L"3c", CMD_3c);
1811 m_cmdMap.SetAt(L"4c", CMD_4c);
1812 m_cmdMap.SetAt(L"1a", CMD_1a);
1813 m_cmdMap.SetAt(L"2a", CMD_2a);
1814 m_cmdMap.SetAt(L"3a", CMD_3a);
1815 m_cmdMap.SetAt(L"4a", CMD_4a);
1816 m_cmdMap.SetAt(L"alpha", CMD_alpha);
1817 m_cmdMap.SetAt(L"an", CMD_an);
1818 m_cmdMap.SetAt(L"a", CMD_a);
1819 m_cmdMap.SetAt(L"blur", CMD_blur);
1820 m_cmdMap.SetAt(L"bord", CMD_bord);
1821 m_cmdMap.SetAt(L"be", CMD_be);
1822 m_cmdMap.SetAt(L"b", CMD_b);
1823 m_cmdMap.SetAt(L"clip", CMD_clip);
1824 m_cmdMap.SetAt(L"iclip", CMD_iclip);
1825 m_cmdMap.SetAt(L"c", CMD_c);
1826 m_cmdMap.SetAt(L"fade", CMD_fade);
1827 m_cmdMap.SetAt(L"fad", CMD_fad);
1828 m_cmdMap.SetAt(L"fax", CMD_fax);
1829 m_cmdMap.SetAt(L"fay", CMD_fay);
1830 m_cmdMap.SetAt(L"fe", CMD_fe);
1831 m_cmdMap.SetAt(L"fn", CMD_fn);
1832 m_cmdMap.SetAt(L"frx", CMD_frx);
1833 m_cmdMap.SetAt(L"fry", CMD_fry);
1834 m_cmdMap.SetAt(L"frz", CMD_frz);
1835 m_cmdMap.SetAt(L"fr", CMD_fr);
1836 m_cmdMap.SetAt(L"fscx", CMD_fscx);
1837 m_cmdMap.SetAt(L"fscy", CMD_fscy);
1838 m_cmdMap.SetAt(L"fsc", CMD_fsc);
1839 m_cmdMap.SetAt(L"fsp", CMD_fsp);
1840 m_cmdMap.SetAt(L"fs", CMD_fs);
1841 m_cmdMap.SetAt(L"i", CMD_i);
1842 m_cmdMap.SetAt(L"kt", CMD_kt);
1843 m_cmdMap.SetAt(L"kf", CMD_kf);
1844 m_cmdMap.SetAt(L"K", CMD_K);
1845 m_cmdMap.SetAt(L"ko", CMD_ko);
1846 m_cmdMap.SetAt(L"k", CMD_k);
1847 m_cmdMap.SetAt(L"move", CMD_move);
1848 m_cmdMap.SetAt(L"org", CMD_org);
1849 m_cmdMap.SetAt(L"pbo", CMD_pbo);
1850 m_cmdMap.SetAt(L"pos", CMD_pos);
1851 m_cmdMap.SetAt(L"p", CMD_p);
1852 m_cmdMap.SetAt(L"q", CMD_q);
1853 m_cmdMap.SetAt(L"r", CMD_r);
1854 m_cmdMap.SetAt(L"shad", CMD_shad);
1855 m_cmdMap.SetAt(L"s", CMD_s);
1856 m_cmdMap.SetAt(L"t", CMD_t);
1857 m_cmdMap.SetAt(L"u", CMD_u);
1858 m_cmdMap.SetAt(L"xbord", CMD_xbord);
1859 m_cmdMap.SetAt(L"xshad", CMD_xshad);
1860 m_cmdMap.SetAt(L"ybord", CMD_ybord);
1861 m_cmdMap.SetAt(L"yshad", CMD_yshad);
1865 void CRenderedTextSubtitle::Copy(CSimpleTextSubtitle& sts)
1867 __super::Copy(sts);
1868 m_size = CSize(0, 0);
1869 if(CRenderedTextSubtitle* pRTS = dynamic_cast<CRenderedTextSubtitle*>(&sts))
1871 m_size = pRTS->m_size;
1875 void CRenderedTextSubtitle::Empty()
1877 Deinit();
1878 __super::Empty();
1881 void CRenderedTextSubtitle::OnChanged()
1883 __super::OnChanged();
1884 POSITION pos = m_subtitleCache.GetStartPosition();
1885 while(pos)
1887 int i;
1888 CSubtitle* s;
1889 m_subtitleCache.GetNextAssoc(pos, i, s);
1890 delete s;
1892 m_subtitleCache.RemoveAll();
1893 m_sla.Empty();
1896 bool CRenderedTextSubtitle::Init(CSize size, CRect vidrect)
1898 Deinit();
1899 m_size = CSize(size.cx*8, size.cy*8);
1900 m_vidrect = CRect(vidrect.left*8, vidrect.top*8, vidrect.right*8, vidrect.bottom*8);
1901 m_sla.Empty();
1902 return(true);
1905 void CRenderedTextSubtitle::Deinit()
1907 POSITION pos = m_subtitleCache.GetStartPosition();
1908 while(pos)
1910 int i;
1911 CSubtitle* s;
1912 m_subtitleCache.GetNextAssoc(pos, i, s);
1913 delete s;
1915 m_subtitleCache.RemoveAll();
1916 m_sla.Empty();
1917 m_size = CSize(0, 0);
1918 m_vidrect.SetRectEmpty();
1920 CacheManager::GetCWordMruCache()->RemoveAll();
1921 CacheManager::GetPathDataMruCache()->RemoveAll();
1922 CacheManager::GetScanLineData2MruCache()->RemoveAll();
1923 CacheManager::GetOverlayNoBlurMruCache()->RemoveAll();
1924 CacheManager::GetOverlayMruCache()->RemoveAll();
1925 CacheManager::GetAssTagListMruCache()->RemoveAll();
1926 CacheManager::GetSubpixelVarianceCache()->RemoveAll();
1927 CacheManager::GetTextInfoCache()->RemoveAll();
1930 void CRenderedTextSubtitle::ParseEffect(CSubtitle* sub, const CStringW& str)
1932 CStringW::PCXSTR str_start = str.GetString();
1933 CStringW::PCXSTR str_end = str_start + str.GetLength();
1934 str_start = SkipWhiteSpaceLeft(str_start, str_end);
1936 if(!sub || *str_start==0)
1937 return;
1939 str_end = FastSkipWhiteSpaceRight(str_start, str_end);
1941 const WCHAR* s = FindChar(str_start, str_end, L';');
1942 if(*s==L';') {
1943 s++;
1946 const CStringW effect(str_start, s-str_start);
1947 if(!effect.CompareNoCase( L"Banner;" ) )
1949 int delay, lefttoright = 0, fadeawaywidth = 0;
1950 if(swscanf(s, L"%d;%d;%d", &delay, &lefttoright, &fadeawaywidth) < 1) return;
1951 Effect* e = new Effect;
1952 if(!e) return;
1953 sub->m_effects[e->type = EF_BANNER] = e;
1954 e->param[0] = (int)(max(1.0*delay/sub->m_scalex, 1));
1955 e->param[1] = lefttoright;
1956 e->param[2] = (int)(sub->m_scalex*fadeawaywidth);
1957 sub->m_wrapStyle = 2;
1959 else if(!effect.CompareNoCase(L"Scroll up;") || !effect.CompareNoCase(L"Scroll down;"))
1961 int top, bottom, delay, fadeawayheight = 0;
1962 if(swscanf(s, L"%d;%d;%d;%d", &top, &bottom, &delay, &fadeawayheight) < 3) return;
1963 if(top > bottom) {int tmp = top; top = bottom; bottom = tmp;}
1964 Effect* e = new Effect;
1965 if(!e) return;
1966 sub->m_effects[e->type = EF_SCROLL] = e;
1967 e->param[0] = (int)(sub->m_scaley*top*8);
1968 e->param[1] = (int)(sub->m_scaley*bottom*8);
1969 e->param[2] = (int)(max(1.0*delay/sub->m_scaley, 1));
1970 e->param[3] = (effect.GetLength() == 12);
1971 e->param[4] = (int)(sub->m_scaley*fadeawayheight);
1975 void CRenderedTextSubtitle::ParseString(CSubtitle* sub, CStringW str, const FwSTSStyle& style)
1977 if(!sub) return;
1978 str.Replace(L"\\N", L"\n");
1979 str.Replace(L"\\n", (sub->m_wrapStyle < 2 || sub->m_wrapStyle == 3) ? L" " : L"\n");
1980 str.Replace(L"\\h", L"\x00A0");
1981 for(int ite = 0, j = 0, len = str.GetLength(); j <= len; j++)
1983 WCHAR c = str[j];
1984 if(c != L'\n' && c != L' ' && c != 0)
1985 continue;
1986 if(ite < j)
1988 if(PCWord tmp_ptr = new CText(style, str.Mid(ite, j-ite), m_ktype, m_kstart, m_kend))
1990 SharedPtrCWord w(tmp_ptr);
1991 sub->m_words.AddTail(w);
1993 else
1995 ///TODO: overflow handling
1997 m_kstart = m_kend;
1999 if(c == L'\n')
2001 if(PCWord tmp_ptr = new CText(style, CStringW(), m_ktype, m_kstart, m_kend))
2003 SharedPtrCWord w(tmp_ptr);
2004 sub->m_words.AddTail(w);
2006 else
2008 ///TODO: overflow handling
2010 m_kstart = m_kend;
2012 else if(c == L' ')
2014 if(PCWord tmp_ptr = new CText(style, CStringW(c), m_ktype, m_kstart, m_kend))
2016 SharedPtrCWord w(tmp_ptr);
2017 sub->m_words.AddTail(w);
2019 else
2021 ///TODO: overflow handling
2023 m_kstart = m_kend;
2025 ite = j+1;
2027 return;
2030 void CRenderedTextSubtitle::ParsePolygon(CSubtitle* sub, const CStringW& str, const FwSTSStyle& style)
2032 if(!sub || !str.GetLength() || !m_nPolygon) return;
2034 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))
2036 SharedPtrCWord w(tmp_ptr);
2037 ///Todo: fix me
2038 //if( PCWord w_cache = m_wordCache.lookup(*w) )
2040 // sub->m_words.AddTail(w_cache);
2041 // delete w;
2043 //else
2045 sub->m_words.AddTail(w);
2047 m_kstart = m_kend;
2051 bool CRenderedTextSubtitle::ParseSSATag( AssTagList *assTags, const CStringW& str )
2053 if(!assTags) return(false);
2054 int nTags = 0, nUnrecognizedTags = 0;
2055 for(int i = 0, j; (j = str.Find(L'\\', i)) >= 0; i = j)
2057 POSITION pos = assTags->AddTail();
2058 AssTag& assTag = assTags->GetAt(pos);
2059 assTag.cmdType = CMD_COUNT;
2061 j++;
2062 CStringW::PCXSTR str_start = str.GetString() + j;
2063 CStringW::PCXSTR pc = str_start;
2064 while( iswspace(*pc) )
2066 pc++;
2068 j += pc-str_start;
2069 str_start = pc;
2070 while( *pc && *pc != L'(' && *pc != L'\\' )
2072 pc++;
2074 j += pc-str_start;
2075 if( pc-str_start>0 )
2077 while( iswspace(*--pc) );
2078 pc++;
2081 const CStringW cmd(str_start, pc-str_start);
2082 if(cmd.IsEmpty()) continue;
2084 CAtlArray<CStringW>& params = assTag.strParams;
2085 if(str[j] == L'(')
2087 j++;
2088 CStringW::PCXSTR str_start = str.GetString() + j;
2089 CStringW::PCXSTR pc = str_start;
2090 while( iswspace(*pc) )
2092 pc++;
2094 j += pc-str_start;
2095 str_start = pc;
2096 while( *pc && *pc != L')' )
2098 pc++;
2100 j += pc-str_start;
2101 if( pc-str_start>0 )
2103 while( iswspace(*--pc) );
2104 pc++;
2107 CStringW::PCXSTR param_start = str_start;
2108 CStringW::PCXSTR param_end = pc;
2109 while( param_start<param_end )
2111 param_start = SkipWhiteSpaceLeft(param_start, param_end);
2113 CStringW::PCXSTR newstart = FindChar(param_start, param_end, L',');
2114 CStringW::PCXSTR newend = FindChar(param_start, param_end, L'\\');
2115 if(newstart > param_start && newstart < newend)
2117 newstart = FastSkipWhiteSpaceRight(param_start, newstart);
2118 CStringW s(param_start, newstart - param_start);
2120 if(!s.IsEmpty()) params.Add(s);
2121 param_start = newstart + 1;
2123 else if(param_start<param_end)
2125 CStringW s(param_start, param_end - param_start);
2127 params.Add(s);
2128 param_start = param_end;
2133 AssCmdType cmd_type = CMD_COUNT;
2134 int cmd_length = min(MAX_CMD_LENGTH, cmd.GetLength());
2135 for( ;cmd_length>=MIN_CMD_LENGTH;cmd_length-- )
2137 if( m_cmdMap.Lookup(cmd.Left(cmd_length), cmd_type) )
2138 break;
2140 if(cmd_length<MIN_CMD_LENGTH)
2141 cmd_type = CMD_COUNT;
2142 switch( cmd_type )
2144 case CMD_fax:
2145 case CMD_fay:
2146 case CMD_fe:
2147 case CMD_fn:
2148 case CMD_frx:
2149 case CMD_fry:
2150 case CMD_frz:
2151 case CMD_fr:
2152 case CMD_fscx:
2153 case CMD_fscy:
2154 case CMD_fsc:
2155 case CMD_fsp:
2156 case CMD_fs:
2157 case CMD_i:
2158 case CMD_kt:
2159 case CMD_kf:
2160 case CMD_K:
2161 case CMD_ko:
2162 case CMD_k:
2163 case CMD_pbo:
2164 case CMD_p:
2165 case CMD_q:
2166 case CMD_r:
2167 case CMD_shad:
2168 case CMD_s:
2169 case CMD_an:
2170 case CMD_a:
2171 case CMD_blur:
2172 case CMD_bord:
2173 case CMD_be:
2174 case CMD_b:
2175 case CMD_u:
2176 case CMD_xbord:
2177 case CMD_xshad:
2178 case CMD_ybord:
2179 case CMD_yshad:
2180 // default:
2181 params.Add(cmd.Mid(cmd_length));
2182 break;
2183 case CMD_c:
2184 case CMD_1c :
2185 case CMD_2c :
2186 case CMD_3c :
2187 case CMD_4c :
2188 case CMD_1a :
2189 case CMD_2a :
2190 case CMD_3a :
2191 case CMD_4a :
2192 case CMD_alpha:
2193 params.Add(cmd.Mid(cmd_length).Trim(L"&H"));
2194 break;
2195 case CMD_clip:
2196 case CMD_iclip:
2197 case CMD_fade:
2198 case CMD_fad:
2199 case CMD_move:
2200 case CMD_org:
2201 case CMD_pos:
2202 break;
2203 case CMD_t:
2204 if (!params.IsEmpty())
2205 ParseSSATag(&assTag.embeded, params[params.GetCount()-1]);
2206 break;
2207 case CMD_COUNT:
2208 nUnrecognizedTags++;
2209 break;
2212 assTag.cmdType = cmd_type;
2214 nTags++;
2216 return(true);
2219 bool CRenderedTextSubtitle::ParseSSATag( CSubtitle* sub, const AssTagList& assTags, STSStyle& style, const STSStyle& org, bool fAnimate /*= false*/ )
2221 if(!sub) return(false);
2223 POSITION pos = assTags.GetHeadPosition();
2224 while(pos)
2226 const AssTag& assTag = assTags.GetNext(pos);
2227 AssCmdType cmd_type = assTag.cmdType;
2228 const CAtlArray<CStringW>& params = assTag.strParams;
2230 // TODO: call ParseStyleModifier(cmd, params, ..) and move the rest there
2231 const CStringW& p = params.GetCount() > 0 ? params[0] : CStringW("");
2232 switch ( cmd_type )
2234 case CMD_1c :
2236 const int i = 0;
2237 DWORD c = wcstol(p, NULL, 16);
2238 style.colors[i] = !p.IsEmpty()
2239 ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
2240 |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
2241 |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
2242 : org.colors[i];
2243 break;
2245 case CMD_2c :
2247 const int i = 1;
2248 DWORD c = wcstol(p, NULL, 16);
2249 style.colors[i] = !p.IsEmpty()
2250 ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
2251 |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
2252 |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
2253 : org.colors[i];
2254 break;
2256 case CMD_3c :
2258 const int i = 2;
2259 DWORD c = wcstol(p, NULL, 16);
2260 style.colors[i] = !p.IsEmpty()
2261 ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
2262 |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
2263 |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
2264 : org.colors[i];
2265 break;
2267 case CMD_4c :
2269 const int i = 3;
2270 DWORD c = wcstol(p, NULL, 16);
2271 style.colors[i] = !p.IsEmpty()
2272 ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
2273 |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
2274 |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
2275 : org.colors[i];
2276 break;
2278 case CMD_1a :
2280 int i = 0;
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_2a :
2288 int i = 1;
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_3a :
2296 int i = 2;
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_4a :
2304 int i = 3;
2305 style.alpha[i] = !p.IsEmpty()
2306 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2307 : org.alpha[i];
2308 break;
2310 case CMD_alpha:
2312 for(int i = 0; i < 4; i++)
2314 style.alpha[i] = !p.IsEmpty()
2315 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2316 : org.alpha[i];
2318 break;
2320 case CMD_an:
2322 int n = wcstol(p, NULL, 10);
2323 if(sub->m_scrAlignment < 0)
2324 sub->m_scrAlignment = (n > 0 && n < 10) ? n : org.scrAlignment;
2325 break;
2327 case CMD_a:
2329 int n = wcstol(p, NULL, 10);
2330 if(sub->m_scrAlignment < 0)
2331 sub->m_scrAlignment = (n > 0 && n < 12) ? ((((n-1)&3)+1)+((n&4)?6:0)+((n&8)?3:0)) : org.scrAlignment;
2332 break;
2334 case CMD_blur:
2336 double n = CalcAnimation(wcstod(p, NULL), style.fGaussianBlur, fAnimate);
2337 style.fGaussianBlur = !p.IsEmpty()
2338 ? (n < 0 ? 0 : n)
2339 : org.fGaussianBlur;
2340 break;
2342 case CMD_bord:
2344 double dst = wcstod(p, NULL);
2345 double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate);
2346 style.outlineWidthX = !p.IsEmpty()
2347 ? (nx < 0 ? 0 : nx)
2348 : org.outlineWidthX;
2349 double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate);
2350 style.outlineWidthY = !p.IsEmpty()
2351 ? (ny < 0 ? 0 : ny)
2352 : org.outlineWidthY;
2353 break;
2355 case CMD_be:
2357 int n = (int)(CalcAnimation(wcstod(p, NULL), style.fBlur, fAnimate)+0.5);
2358 style.fBlur = !p.IsEmpty()
2360 : org.fBlur;
2361 break;
2363 case CMD_b:
2365 int n = wcstol(p, NULL, 10);
2366 style.fontWeight = !p.IsEmpty()
2367 ? (n == 0 ? FW_NORMAL : n == 1 ? FW_BOLD : n >= 100 ? n : org.fontWeight)
2368 : org.fontWeight;
2369 break;
2371 case CMD_clip:
2372 case CMD_iclip:
2374 bool invert = (cmd_type == CMD_iclip);
2375 if(params.GetCount() == 1 && !sub->m_pClipper)
2377 sub->m_pClipper.reset( new CClipper(params[0], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex, sub->m_scaley, invert) );
2379 else if(params.GetCount() == 2 && !sub->m_pClipper)
2381 int scale = max(wcstol(p, NULL, 10), 1);
2382 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) );
2384 else if(params.GetCount() == 4)
2386 CRect r;
2387 sub->m_clipInverse = invert;
2388 r.SetRect(
2389 wcstod(params[0], NULL)+0.5,
2390 wcstod(params[1], NULL)+0.5,
2391 wcstod(params[2], NULL)+0.5,
2392 wcstod(params[3], NULL)+0.5);
2393 CPoint o(0, 0);
2394 if(sub->m_relativeTo == 1) // TODO: this should also apply to the other two clippings above
2396 o.x = m_vidrect.left>>3;
2397 o.y = m_vidrect.top>>3;
2399 sub->m_clip.SetRect(
2400 (int)CalcAnimation(sub->m_scalex*r.left + o.x, sub->m_clip.left, fAnimate),
2401 (int)CalcAnimation(sub->m_scaley*r.top + o.y, sub->m_clip.top, fAnimate),
2402 (int)CalcAnimation(sub->m_scalex*r.right + o.x, sub->m_clip.right, fAnimate),
2403 (int)CalcAnimation(sub->m_scaley*r.bottom + o.y, sub->m_clip.bottom, fAnimate));
2405 break;
2407 case CMD_c:
2409 DWORD c = wcstol(p, NULL, 16);
2410 style.colors[0] = !p.IsEmpty()
2411 ? (((int)CalcAnimation(c&0xff, style.colors[0]&0xff, fAnimate))&0xff
2412 |((int)CalcAnimation(c&0xff00, style.colors[0]&0xff00, fAnimate))&0xff00
2413 |((int)CalcAnimation(c&0xff0000, style.colors[0]&0xff0000, fAnimate))&0xff0000)
2414 : org.colors[0];
2415 break;
2417 case CMD_fade:
2418 case CMD_fad:
2420 sub->m_fAnimated2 = true;
2421 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])
2423 if(Effect* e = new Effect)
2425 for(int i = 0; i < 3; i++)
2426 e->param[i] = wcstol(params[i], NULL, 10);
2427 for(int i = 0; i < 4; i++)
2428 e->t[i] = wcstol(params[3+i], NULL, 10);
2429 sub->m_effects[EF_FADE] = e;
2432 else if(params.GetCount() == 2 && !sub->m_effects[EF_FADE]) // {\fad(t1=t[1], t2=t[2])
2434 if(Effect* e = new Effect)
2436 e->param[0] = e->param[2] = 0xff;
2437 e->param[1] = 0x00;
2438 for(int i = 1; i < 3; i++)
2439 e->t[i] = wcstol(params[i-1], NULL, 10);
2440 e->t[0] = e->t[3] = -1; // will be substituted with "start" and "end"
2441 sub->m_effects[EF_FADE] = e;
2444 break;
2446 case CMD_fax:
2448 style.fontShiftX = !p.IsEmpty()
2449 ? CalcAnimation(wcstod(p, NULL), style.fontShiftX, fAnimate)
2450 : org.fontShiftX;
2451 break;
2453 case CMD_fay:
2455 style.fontShiftY = !p.IsEmpty()
2456 ? CalcAnimation(wcstod(p, NULL), style.fontShiftY, fAnimate)
2457 : org.fontShiftY;
2458 break;
2460 case CMD_fe:
2462 int n = wcstol(p, NULL, 10);
2463 style.charSet = !p.IsEmpty()
2465 : org.charSet;
2466 break;
2468 case CMD_fn:
2470 if(!p.IsEmpty() && p != L'0')
2471 style.fontName = CString(p).Trim();
2472 else
2473 style.fontName = org.fontName;
2474 break;
2476 case CMD_frx:
2478 style.fontAngleX = !p.IsEmpty()
2479 ? CalcAnimation(wcstod(p, NULL), style.fontAngleX, fAnimate)
2480 : org.fontAngleX;
2481 break;
2483 case CMD_fry:
2485 style.fontAngleY = !p.IsEmpty()
2486 ? CalcAnimation(wcstod(p, NULL), style.fontAngleY, fAnimate)
2487 : org.fontAngleY;
2488 break;
2490 case CMD_frz:
2491 case CMD_fr:
2493 style.fontAngleZ = !p.IsEmpty()
2494 ? CalcAnimation(wcstod(p, NULL), style.fontAngleZ, fAnimate)
2495 : org.fontAngleZ;
2496 break;
2498 case CMD_fscx:
2500 double n = CalcAnimation(wcstod(p, NULL), style.fontScaleX, fAnimate);
2501 style.fontScaleX = !p.IsEmpty()
2502 ? ((n < 0) ? 0 : n)
2503 : org.fontScaleX;
2504 break;
2506 case CMD_fscy:
2508 double n = CalcAnimation(wcstod(p, NULL), style.fontScaleY, fAnimate);
2509 style.fontScaleY = !p.IsEmpty()
2510 ? ((n < 0) ? 0 : n)
2511 : org.fontScaleY;
2512 break;
2514 case CMD_fsc:
2516 style.fontScaleX = org.fontScaleX;
2517 style.fontScaleY = org.fontScaleY;
2518 break;
2520 case CMD_fsp:
2522 style.fontSpacing = !p.IsEmpty()
2523 ? CalcAnimation(wcstod(p, NULL), style.fontSpacing, fAnimate)
2524 : org.fontSpacing;
2525 break;
2527 case CMD_fs:
2529 if(!p.IsEmpty())
2531 if(p[0] == L'-' || p[0] == L'+')
2533 double n = CalcAnimation(style.fontSize + style.fontSize*wcstod(p, NULL)/10, style.fontSize, fAnimate);
2534 style.fontSize = (n > 0) ? n : org.fontSize;
2536 else
2538 double n = CalcAnimation(wcstod(p, NULL), style.fontSize, fAnimate);
2539 style.fontSize = (n > 0) ? n : org.fontSize;
2542 else
2544 style.fontSize = org.fontSize;
2546 break;
2548 case CMD_i:
2550 int n = wcstol(p, NULL, 10);
2551 style.fItalic = !p.IsEmpty()
2552 ? (n == 0 ? false : n == 1 ? true : org.fItalic)
2553 : org.fItalic;
2554 break;
2556 case CMD_kt:
2558 m_kstart = !p.IsEmpty()
2559 ? wcstod(p, NULL)*10
2560 : 0;
2561 m_kend = m_kstart;
2562 break;
2563 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2565 case CMD_kf:
2566 case CMD_K:
2568 m_ktype = 1;
2569 m_kstart = m_kend;
2570 m_kend += !p.IsEmpty()
2571 ? wcstod(p, NULL)*10
2572 : 1000;
2573 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2574 break;
2576 case CMD_ko:
2578 m_ktype = 2;
2579 m_kstart = m_kend;
2580 m_kend += !p.IsEmpty()
2581 ? wcstod(p, NULL)*10
2582 : 1000;
2583 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2584 break;
2586 case CMD_k:
2588 m_ktype = 0;
2589 m_kstart = m_kend;
2590 m_kend += !p.IsEmpty()
2591 ? wcstod(p, NULL)*10
2592 : 1000;
2593 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2594 break;
2596 case CMD_move: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3][, t1=t[0], t2=t[1]])}
2598 if((params.GetCount() == 4 || params.GetCount() == 6) && !sub->m_effects[EF_MOVE])
2600 if(Effect* e = new Effect)
2602 e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2603 e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2604 e->param[2] = (int)(sub->m_scalex*wcstod(params[2], NULL)*8);
2605 e->param[3] = (int)(sub->m_scaley*wcstod(params[3], NULL)*8);
2606 e->t[0] = e->t[1] = -1;
2607 if(params.GetCount() == 6)
2609 for(int i = 0; i < 2; i++)
2610 e->t[i] = wcstol(params[4+i], NULL, 10);
2612 sub->m_effects[EF_MOVE] = e;
2613 sub->m_fAnimated2 = true;
2616 break;
2618 case CMD_org: // {\org(x=param[0], y=param[1])}
2620 if(params.GetCount() == 2 && !sub->m_effects[EF_ORG])
2622 if(Effect* e = new Effect)
2624 e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2625 e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2626 sub->m_effects[EF_ORG] = e;
2629 break;
2631 case CMD_pbo:
2633 m_polygonBaselineOffset = wcstol(p, NULL, 10);
2634 break;
2636 case CMD_pos:
2638 if(params.GetCount() == 2 && !sub->m_effects[EF_MOVE])
2640 if(Effect* e = new Effect)
2642 e->param[0] = e->param[2] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2643 e->param[1] = e->param[3] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2644 e->t[0] = e->t[1] = 0;
2645 sub->m_effects[EF_MOVE] = e;
2648 break;
2650 case CMD_p:
2652 int n = wcstol(p, NULL, 10);
2653 m_nPolygon = (n <= 0 ? 0 : n);
2654 break;
2656 case CMD_q:
2658 int n = wcstol(p, NULL, 10);
2659 sub->m_wrapStyle = !p.IsEmpty() && (0 <= n && n <= 3)
2661 : m_defaultWrapStyle;
2662 break;
2664 case CMD_r:
2666 STSStyle* val;
2667 style = (!p.IsEmpty() && m_styles.Lookup(WToT(p), val) && val) ? *val : org;
2668 break;
2670 case CMD_shad:
2672 double dst = wcstod(p, NULL);
2673 double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate);
2674 style.shadowDepthX = !p.IsEmpty()
2675 ? (nx < 0 ? 0 : nx)
2676 : org.shadowDepthX;
2677 double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate);
2678 style.shadowDepthY = !p.IsEmpty()
2679 ? (ny < 0 ? 0 : ny)
2680 : org.shadowDepthY;
2681 break;
2683 case CMD_s:
2685 int n = wcstol(p, NULL, 10);
2686 style.fStrikeOut = !p.IsEmpty()
2687 ? (n == 0 ? false : n == 1 ? true : org.fStrikeOut)
2688 : org.fStrikeOut;
2689 break;
2691 case CMD_t: // \t([<t1>,<t2>,][<accel>,]<style modifiers>)
2693 CStringW param;
2694 m_animStart = m_animEnd = 0;
2695 m_animAccel = 1;
2696 if(params.GetCount() == 1)
2698 param = params[0];
2700 else if(params.GetCount() == 2)
2702 m_animAccel = wcstod(params[0], NULL);
2703 param = params[1];
2705 else if(params.GetCount() == 3)
2707 m_animStart = (int)wcstod(params[0], NULL);
2708 m_animEnd = (int)wcstod(params[1], NULL);
2709 param = params[2];
2711 else if(params.GetCount() == 4)
2713 m_animStart = wcstol(params[0], NULL, 10);
2714 m_animEnd = wcstol(params[1], NULL, 10);
2715 m_animAccel = wcstod(params[2], NULL);
2716 param = params[3];
2718 ParseSSATag(sub, assTag.embeded, style, org, true);
2719 sub->m_fAnimated = true;
2720 sub->m_fAnimated2 = true;
2721 break;
2723 case CMD_u:
2725 int n = wcstol(p, NULL, 10);
2726 style.fUnderline = !p.IsEmpty()
2727 ? (n == 0 ? false : n == 1 ? true : org.fUnderline)
2728 : org.fUnderline;
2729 break;
2731 case CMD_xbord:
2733 double dst = wcstod(p, NULL);
2734 double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate);
2735 style.outlineWidthX = !p.IsEmpty()
2736 ? (nx < 0 ? 0 : nx)
2737 : org.outlineWidthX;
2738 break;
2740 case CMD_xshad:
2742 double dst = wcstod(p, NULL);
2743 double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate);
2744 style.shadowDepthX = !p.IsEmpty()
2745 ? nx
2746 : org.shadowDepthX;
2747 break;
2749 case CMD_ybord:
2751 double dst = wcstod(p, NULL);
2752 double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate);
2753 style.outlineWidthY = !p.IsEmpty()
2754 ? (ny < 0 ? 0 : ny)
2755 : org.outlineWidthY;
2756 break;
2758 case CMD_yshad:
2760 double dst = wcstod(p, NULL);
2761 double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate);
2762 style.shadowDepthY = !p.IsEmpty()
2763 ? ny
2764 : org.shadowDepthY;
2765 break;
2767 default:
2768 break;
2771 return(true);
2774 bool CRenderedTextSubtitle::ParseSSATag(CSubtitle* sub, const CStringW& str, STSStyle& style, const STSStyle& org, bool fAnimate)
2776 if(!sub) return(false);
2778 SharedPtrConstAssTagList assTags;
2779 AssTagListMruCache *ass_tag_cache = CacheManager::GetAssTagListMruCache();
2780 POSITION pos = ass_tag_cache->Lookup(str);
2781 if (pos==NULL)
2783 AssTagList *tmp = new AssTagList();
2784 ParseSSATag(tmp, str);
2785 assTags.reset(tmp);
2786 ass_tag_cache->UpdateCache(str, assTags);
2788 else
2790 assTags = ass_tag_cache->GetAt(pos);
2791 ass_tag_cache->UpdateCache( pos );
2793 return ParseSSATag(sub, *assTags, style, org, fAnimate);
2796 bool CRenderedTextSubtitle::ParseHtmlTag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org)
2798 if(str.Find(L"!--") == 0)
2799 return(true);
2800 bool fClosing = str[0] == L'/';
2801 str.Trim(L" /");
2802 int i = str.Find(L' ');
2803 if(i < 0) i = str.GetLength();
2804 CStringW tag = str.Left(i).MakeLower();
2805 str = str.Mid(i).Trim();
2806 CAtlArray<CStringW> attribs, params;
2807 while((i = str.Find(L'=')) > 0)
2809 attribs.Add(str.Left(i).Trim().MakeLower());
2810 str = str.Mid(i+1);
2811 for(i = 0; _istspace(str[i]); i++);
2812 str = str.Mid(i);
2813 if(str[0] == L'\"') {str = str.Mid(1); i = str.Find(L'\"');}
2814 else i = str.Find(L' ');
2815 if(i < 0) i = str.GetLength();
2816 params.Add(str.Left(i).Trim().MakeLower());
2817 str = str.Mid(i+1);
2819 if(tag == L"text")
2821 else if(tag == L"b" || tag == L"strong")
2822 style.fontWeight = !fClosing ? FW_BOLD : org.fontWeight;
2823 else if(tag == L"i" || tag == L"em")
2824 style.fItalic = !fClosing ? true : org.fItalic;
2825 else if(tag == L"u")
2826 style.fUnderline = !fClosing ? true : org.fUnderline;
2827 else if(tag == L"s" || tag == L"strike" || tag == L"del")
2828 style.fStrikeOut = !fClosing ? true : org.fStrikeOut;
2829 else if(tag == L"font")
2831 if(!fClosing)
2833 for(size_t i = 0; i < attribs.GetCount(); i++)
2835 if(params[i].IsEmpty()) continue;
2836 int nColor = -1;
2837 if(attribs[i] == L"face")
2839 style.fontName = params[i];
2841 else if(attribs[i] == L"size")
2843 if(params[i][0] == L'+')
2844 style.fontSize += wcstol(params[i], NULL, 10);
2845 else if(params[i][0] == L'-')
2846 style.fontSize -= wcstol(params[i], NULL, 10);
2847 else
2848 style.fontSize = wcstol(params[i], NULL, 10);
2850 else if(attribs[i] == L"color")
2852 nColor = 0;
2854 else if(attribs[i] == L"outline-color")
2856 nColor = 2;
2858 else if(attribs[i] == L"outline-level")
2860 style.outlineWidthX = style.outlineWidthY = wcstol(params[i], NULL, 10);
2862 else if(attribs[i] == L"shadow-color")
2864 nColor = 3;
2866 else if(attribs[i] == L"shadow-level")
2868 style.shadowDepthX = style.shadowDepthY = wcstol(params[i], NULL, 10);
2870 if(nColor >= 0 && nColor < 4)
2872 CString key = WToT(params[i]).TrimLeft(L'#');
2873 DWORD val;
2874 if(g_colors.Lookup(key, val))
2875 style.colors[nColor] = val;
2876 else if((style.colors[nColor] = _tcstol(key, NULL, 16)) == 0)
2877 style.colors[nColor] = 0x00ffffff; // default is white
2878 style.colors[nColor] = ((style.colors[nColor]>>16)&0xff)|((style.colors[nColor]&0xff)<<16)|(style.colors[nColor]&0x00ff00);
2882 else
2884 style.fontName = org.fontName;
2885 style.fontSize = org.fontSize;
2886 memcpy(style.colors, org.colors, sizeof(style.colors));
2889 else if(tag == L"k" && attribs.GetCount() == 1 && attribs[0] == L"t")
2891 m_ktype = 1;
2892 m_kstart = m_kend;
2893 m_kend += wcstol(params[0], NULL, 10);
2895 else
2896 return(false);
2897 return(true);
2900 double CRenderedTextSubtitle::CalcAnimation(double dst, double src, bool fAnimate)
2902 int s = m_animStart ? m_animStart : 0;
2903 int e = m_animEnd ? m_animEnd : m_delay;
2904 if(fabs(dst-src) >= 0.0001 && fAnimate)
2906 if(m_time < s) dst = src;
2907 else if(s <= m_time && m_time < e)
2909 double t = pow(1.0 * (m_time - s) / (e - s), m_animAccel);
2910 dst = (1 - t) * src + t * dst;
2912 // else dst = dst;
2914 return(dst);
2917 CSubtitle* CRenderedTextSubtitle::GetSubtitle(int entry)
2919 CSubtitle* sub;
2920 if(m_subtitleCache.Lookup(entry, sub))
2922 if(sub->m_fAnimated) {delete sub; sub = NULL;}
2923 else return(sub);
2925 sub = new CSubtitle();
2926 if(!sub) return(NULL);
2927 CStringW str = GetStrW(entry, true);
2928 STSStyle stss, orgstss;
2929 GetStyle(entry, &stss);
2930 if (stss.fontScaleX == stss.fontScaleY && m_dPARCompensation != 1.0)
2932 switch(m_ePARCompensationType)
2934 case EPCTUpscale:
2935 if (m_dPARCompensation < 1.0)
2936 stss.fontScaleY /= m_dPARCompensation;
2937 else
2938 stss.fontScaleX *= m_dPARCompensation;
2939 break;
2940 case EPCTDownscale:
2941 if (m_dPARCompensation < 1.0)
2942 stss.fontScaleX *= m_dPARCompensation;
2943 else
2944 stss.fontScaleY /= m_dPARCompensation;
2945 break;
2946 case EPCTAccurateSize:
2947 stss.fontScaleX *= m_dPARCompensation;
2948 break;
2951 orgstss = stss;
2952 sub->m_clip.SetRect(0, 0, m_size.cx>>3, m_size.cy>>3);
2953 sub->m_scrAlignment = -stss.scrAlignment;
2954 sub->m_wrapStyle = m_defaultWrapStyle;
2955 sub->m_fAnimated = false;
2956 sub->m_relativeTo = stss.relativeTo;
2957 sub->m_scalex = m_dstScreenSize.cx > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Width() : m_size.cx) / (m_dstScreenSize.cx*8) : 1.0;
2958 sub->m_scaley = m_dstScreenSize.cy > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Height() : m_size.cy) / (m_dstScreenSize.cy*8) : 1.0;
2959 m_animStart = m_animEnd = 0;
2960 m_animAccel = 1;
2961 m_ktype = m_kstart = m_kend = 0;
2962 m_nPolygon = 0;
2963 m_polygonBaselineOffset = 0;
2964 ParseEffect(sub, m_entries.GetAt(entry).effect);
2965 while(!str.IsEmpty())
2967 bool fParsed = false;
2968 int i;
2969 if(str[0] == L'{' && (i = str.Find(L'}')) > 0)
2971 if(fParsed = ParseSSATag(sub, str.Mid(1, i-1), stss, orgstss))
2972 str = str.Mid(i+1);
2974 else if(str[0] == L'<' && (i = str.Find(L'>')) > 0)
2976 if(fParsed = ParseHtmlTag(sub, str.Mid(1, i-1), stss, orgstss))
2977 str = str.Mid(i+1);
2979 if(fParsed)
2981 i = str.FindOneOf(L"{<");
2982 if(i < 0) i = str.GetLength();
2983 if(i == 0) continue;
2985 else
2987 i = str.Mid(1).FindOneOf(L"{<");
2988 if(i < 0) i = str.GetLength()-1;
2989 i++;
2991 STSStyle tmp = stss;
2992 tmp.fontSize = sub->m_scaley*tmp.fontSize*64;
2993 tmp.fontSpacing = sub->m_scalex*tmp.fontSpacing*64;
2994 tmp.outlineWidthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8;
2995 tmp.outlineWidthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8;
2996 tmp.shadowDepthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8;
2997 tmp.shadowDepthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8;
2998 FwSTSStyle fw_tmp(tmp);
2999 if(m_nPolygon)
3001 ParsePolygon(sub, str.Left(i), fw_tmp);
3003 else
3005 ParseString(sub, str.Left(i), fw_tmp);
3007 str = str.Mid(i);
3009 if( sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL] )
3010 sub->m_fAnimated2 = true;
3011 // just a "work-around" solution... in most cases nobody will want to use \org together with moving but without rotating the subs
3012 if(sub->m_effects[EF_ORG] && (sub->m_effects[EF_MOVE] || sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL]))
3013 sub->m_fAnimated = true;
3014 sub->m_scrAlignment = abs(sub->m_scrAlignment);
3015 STSEntry stse = m_entries.GetAt(entry);
3016 CRect marginRect = stse.marginRect;
3017 if(marginRect.left == 0) marginRect.left = orgstss.marginRect.get().left;
3018 if(marginRect.top == 0) marginRect.top = orgstss.marginRect.get().top;
3019 if(marginRect.right == 0) marginRect.right = orgstss.marginRect.get().right;
3020 if(marginRect.bottom == 0) marginRect.bottom = orgstss.marginRect.get().bottom;
3021 marginRect.left = (int)(sub->m_scalex*marginRect.left*8);
3022 marginRect.top = (int)(sub->m_scaley*marginRect.top*8);
3023 marginRect.right = (int)(sub->m_scalex*marginRect.right*8);
3024 marginRect.bottom = (int)(sub->m_scaley*marginRect.bottom*8);
3025 if(stss.relativeTo == 1)
3027 marginRect.left += m_vidrect.left;
3028 marginRect.top += m_vidrect.top;
3029 marginRect.right += m_size.cx - m_vidrect.right;
3030 marginRect.bottom += m_size.cy - m_vidrect.bottom;
3032 sub->CreateClippers(m_size);
3033 sub->MakeLines(m_size, marginRect);
3034 m_subtitleCache[entry] = sub;
3035 return(sub);
3040 STDMETHODIMP CRenderedTextSubtitle::NonDelegatingQueryInterface(REFIID riid, void** ppv)
3042 CheckPointer(ppv, E_POINTER);
3043 *ppv = NULL;
3044 return
3045 QI(IPersist)
3046 QI(ISubStream)
3047 QI(ISubPicProvider)
3048 QI(ISubPicProviderEx)
3049 __super::NonDelegatingQueryInterface(riid, ppv);
3052 // ISubPicProvider
3054 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetStartPosition(REFERENCE_TIME rt, double fps)
3056 m_fps = fps;
3057 if (m_fps>0)
3059 m_period = 1000/m_fps;
3060 if(m_period<=0)
3062 m_period = 1;
3065 else
3067 //Todo: fix me. max has been defined as a macro. Use #define NOMINMAX to fix it.
3068 //std::numeric_limits<int>::max();
3069 m_period = INT_MAX;
3072 int iSegment;
3073 int subIndex = 1;//If a segment has animate effect then it corresponds to several subpics.
3074 //subIndex, 1 based, indicates which subpic the result corresponds to.
3075 rt /= 10000i64;
3076 const STSSegment *stss = SearchSubs((int)rt, fps, &iSegment, NULL);
3077 if(stss==NULL)
3078 return NULL;
3079 else if(stss->animated)
3081 int start = TranslateSegmentStart(iSegment, fps);
3082 if(rt > start)
3083 subIndex = (rt-start)/m_period + 1;
3085 //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));
3086 return (POSITION)(subIndex | (iSegment<<RTS_POS_SEGMENT_INDEX_BITS));
3087 //if(iSegment < 0) iSegment = 0;
3088 //return(GetNext((POSITION)iSegment));
3091 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetNext(POSITION pos)
3093 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3094 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3095 const STSSegment *stss = GetSegment(iSegment);
3096 ASSERT(stss!=NULL && stss->subs.GetCount()>0);
3097 //DbgLog((LOG_TRACE, 3, "stss:%x count:%d", stss, stss->subs.GetCount()));
3098 if(!stss->animated)
3100 iSegment++;
3101 subIndex = 1;
3103 else
3105 int start, end;
3106 TranslateSegmentStartEnd(iSegment, m_fps, start, end);
3107 if(start+m_period*subIndex < end)
3108 subIndex++;
3109 else
3111 iSegment++;
3112 subIndex = 1;
3115 if(GetSegment(iSegment) != NULL)
3117 ASSERT(GetSegment(iSegment)->subs.GetCount()>0);
3118 return (POSITION)(subIndex | (iSegment<<RTS_POS_SEGMENT_INDEX_BITS));
3120 else
3121 return NULL;
3124 //@return: <0 if segment not found
3125 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStart(POSITION pos, double fps)
3127 //return(10000i64 * TranslateSegmentStart((int)pos-1, fps));
3128 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3129 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3130 int start = TranslateSegmentStart(iSegment, fps);
3131 const STSSegment *stss = GetSegment(iSegment);
3132 if(stss!=NULL)
3134 return (start + (subIndex-1)*m_period)*10000i64;
3136 else
3138 return -1;
3142 //@return: <0 if segment not found
3143 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStop(POSITION pos, double fps)
3145 // return(10000i64 * TranslateSegmentEnd((int)pos-1, fps));
3146 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3147 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3148 int start, end, ret;
3149 TranslateSegmentStartEnd(iSegment, fps, start, end);
3150 const STSSegment *stss = GetSegment(iSegment);
3151 if(stss!=NULL)
3153 if(!stss->animated)
3154 ret = end;
3155 else
3157 ret = start+subIndex*m_period;
3158 if(ret > end)
3159 ret = end;
3161 return ret*10000i64;
3163 else
3164 return -1;
3167 //@start, @stop: -1 if segment not found; @stop may < @start if subIndex exceed uppper bound
3168 STDMETHODIMP_(VOID) CRenderedTextSubtitle::GetStartStop(POSITION pos, double fps, /*out*/REFERENCE_TIME &start, /*out*/REFERENCE_TIME &stop)
3170 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3171 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3172 int tempStart, tempEnd;
3173 TranslateSegmentStartEnd(iSegment, fps, tempStart, tempEnd);
3174 start = tempStart;
3175 stop = tempEnd;
3176 const STSSegment *stss = GetSegment(iSegment);
3177 if(stss!=NULL)
3179 if(stss->animated)
3181 start += (subIndex-1)*m_period;
3182 if(start+m_period < stop)
3183 stop = start+m_period;
3185 //DbgLog((LOG_TRACE, 3, "animated:%d seg:%d idx:%d start:%d stop:%lu", stss->animated, iSegment, subIndex, (ULONG)start, (ULONG)stop));
3186 start *= 10000i64;
3187 stop *= 10000i64;
3189 else
3191 start = -1;
3192 stop = -1;
3196 STDMETHODIMP_(bool) CRenderedTextSubtitle::IsAnimated(POSITION pos)
3198 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3199 if(iSegment>=0 && iSegment<m_segments.GetCount())
3200 return m_segments[iSegment].animated;
3201 else
3202 return false;
3203 //return(true);
3206 struct LSub {int idx, layer, readorder;};
3208 static int lscomp(const void* ls1, const void* ls2)
3210 int ret = ((LSub*)ls1)->layer - ((LSub*)ls2)->layer;
3211 if(!ret) ret = ((LSub*)ls1)->readorder - ((LSub*)ls2)->readorder;
3212 return(ret);
3215 STDMETHODIMP CRenderedTextSubtitle::ParseScript(SubPicDesc& spd, REFERENCE_TIME rt, double fps, CSubtitle2List *outputSub2List )
3217 //fix me: check input and log error
3218 int t = (int)(rt / 10000);
3219 int segment;
3220 //const
3221 STSSegment* stss = SearchSubs2(t, fps, &segment);
3222 if(!stss) return S_FALSE;
3223 // clear any cached subs not in the range of +/-30secs measured from the segment's bounds
3225 POSITION pos = m_subtitleCache.GetStartPosition();
3226 while(pos)
3228 int key;
3229 CSubtitle* value;
3230 m_subtitleCache.GetNextAssoc(pos, key, value);
3231 STSEntry& stse = m_entries.GetAt(key);
3232 if(stse.end <= (t-30000) || stse.start > (t+30000))
3234 delete value;
3235 m_subtitleCache.RemoveKey(key);
3236 pos = m_subtitleCache.GetStartPosition();
3240 m_sla.AdvanceToSegment(segment, stss->subs);
3241 CAtlArray<LSub> subs;
3242 for(int i = 0, j = stss->subs.GetCount(); i < j; i++)
3244 LSub ls;
3245 ls.idx = stss->subs[i];
3246 ls.layer = m_entries.GetAt(stss->subs[i]).layer;
3247 ls.readorder = m_entries.GetAt(stss->subs[i]).readorder;
3248 subs.Add(ls);
3250 qsort(subs.GetData(), subs.GetCount(), sizeof(LSub), lscomp);
3252 for(int i = 0, j = subs.GetCount(); i < j; i++)
3254 int entry = subs[i].idx;
3255 STSEntry stse = m_entries.GetAt(entry);
3257 int start = TranslateStart(entry, fps);
3258 m_time = t - start;
3259 m_delay = TranslateEnd(entry, fps) - start;
3261 CSubtitle* s = GetSubtitle(entry);
3262 if(!s) continue;
3263 stss->animated |= s->m_fAnimated2;
3264 CRect clipRect = s->m_clip & CRect(0,0, spd.w, spd.h);
3265 CRect r = s->m_rect;
3266 CSize spaceNeeded = r.Size();
3267 // apply the effects
3268 bool fPosOverride = false, fOrgOverride = false;
3269 int alpha = 0x00;
3270 CPoint org2;
3271 for(int k = 0; k < EF_NUMBEROFEFFECTS; k++)
3273 if(!s->m_effects[k]) continue;
3274 switch(k)
3276 case EF_MOVE: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3], t1=t[0], t2=t[1])}
3278 CPoint p;
3279 CPoint p1(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
3280 CPoint p2(s->m_effects[k]->param[2], s->m_effects[k]->param[3]);
3281 int t1 = s->m_effects[k]->t[0];
3282 int t2 = s->m_effects[k]->t[1];
3283 if(t2 < t1) {int t = t1; t1 = t2; t2 = t;}
3284 if(t1 <= 0 && t2 <= 0) {t1 = 0; t2 = m_delay;}
3285 if(m_time <= t1) p = p1;
3286 else if (p1 == p2) p = p1;
3287 else if(t1 < m_time && m_time < t2)
3289 double t = 1.0*(m_time-t1)/(t2-t1);
3290 p.x = (int)((1-t)*p1.x + t*p2.x);
3291 p.y = (int)((1-t)*p1.y + t*p2.y);
3293 else p = p2;
3294 r = CRect(
3295 CPoint((s->m_scrAlignment%3) == 1 ? p.x : (s->m_scrAlignment%3) == 0 ? p.x - spaceNeeded.cx : p.x - (spaceNeeded.cx+1)/2,
3296 s->m_scrAlignment <= 3 ? p.y - spaceNeeded.cy : s->m_scrAlignment <= 6 ? p.y - (spaceNeeded.cy+1)/2 : p.y),
3297 spaceNeeded);
3298 if(s->m_relativeTo == 1)
3299 r.OffsetRect(m_vidrect.TopLeft());
3300 fPosOverride = true;
3302 break;
3303 case EF_ORG: // {\org(x=param[0], y=param[1])}
3305 org2 = CPoint(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
3306 fOrgOverride = true;
3308 break;
3309 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])
3311 int t1 = s->m_effects[k]->t[0];
3312 int t2 = s->m_effects[k]->t[1];
3313 int t3 = s->m_effects[k]->t[2];
3314 int t4 = s->m_effects[k]->t[3];
3315 if(t1 == -1 && t4 == -1) {t1 = 0; t3 = m_delay-t3; t4 = m_delay;}
3316 if(m_time < t1) alpha = s->m_effects[k]->param[0];
3317 else if(m_time >= t1 && m_time < t2)
3319 double t = 1.0 * (m_time - t1) / (t2 - t1);
3320 alpha = (int)(s->m_effects[k]->param[0]*(1-t) + s->m_effects[k]->param[1]*t);
3322 else if(m_time >= t2 && m_time < t3) alpha = s->m_effects[k]->param[1];
3323 else if(m_time >= t3 && m_time < t4)
3325 double t = 1.0 * (m_time - t3) / (t4 - t3);
3326 alpha = (int)(s->m_effects[k]->param[1]*(1-t) + s->m_effects[k]->param[2]*t);
3328 else if(m_time >= t4) alpha = s->m_effects[k]->param[2];
3330 break;
3331 case EF_BANNER: // Banner;delay=param[0][;leftoright=param[1];fadeawaywidth=param[2]]
3333 int left = s->m_relativeTo == 1 ? m_vidrect.left : 0,
3334 right = s->m_relativeTo == 1 ? m_vidrect.right : m_size.cx;
3335 r.left = !!s->m_effects[k]->param[1]
3336 ? (left/*marginRect.left*/ - spaceNeeded.cx) + (int)(m_time*8.0/s->m_effects[k]->param[0])
3337 : (right /*- marginRect.right*/) - (int)(m_time*8.0/s->m_effects[k]->param[0]);
3338 r.right = r.left + spaceNeeded.cx;
3339 clipRect &= CRect(left>>3, clipRect.top, right>>3, clipRect.bottom);
3340 fPosOverride = true;
3342 break;
3343 case EF_SCROLL: // Scroll up/down(toptobottom=param[3]);top=param[0];bottom=param[1];delay=param[2][;fadeawayheight=param[4]]
3345 r.top = !!s->m_effects[k]->param[3]
3346 ? s->m_effects[k]->param[0] + (int)(m_time*8.0/s->m_effects[k]->param[2]) - spaceNeeded.cy
3347 : s->m_effects[k]->param[1] - (int)(m_time*8.0/s->m_effects[k]->param[2]);
3348 r.bottom = r.top + spaceNeeded.cy;
3349 CRect cr(0, (s->m_effects[k]->param[0] + 4) >> 3, spd.w, (s->m_effects[k]->param[1] + 4) >> 3);
3350 if(s->m_relativeTo == 1)
3351 r.top += m_vidrect.top,
3352 r.bottom += m_vidrect.top,
3353 cr.top += m_vidrect.top>>3,
3354 cr.bottom += m_vidrect.top>>3;
3355 clipRect &= cr;
3356 fPosOverride = true;
3358 break;
3359 default:
3360 break;
3363 if(!fPosOverride && !fOrgOverride && !s->m_fAnimated)
3364 r = m_sla.AllocRect(s, segment, entry, stse.layer, m_collisions);
3365 CPoint org;
3366 org.x = (s->m_scrAlignment%3) == 1 ? r.left : (s->m_scrAlignment%3) == 2 ? r.CenterPoint().x : r.right;
3367 org.y = s->m_scrAlignment <= 3 ? r.bottom : s->m_scrAlignment <= 6 ? r.CenterPoint().y : r.top;
3368 if(!fOrgOverride) org2 = org;
3369 CPoint p2(0, r.top);
3370 // Rectangles for inverse clip
3372 CSubtitle2& sub2 = outputSub2List->GetAt(outputSub2List->AddTail( CSubtitle2(s, clipRect, org, org2, p2, alpha, m_time) ));
3375 return (subs.GetCount()) ? S_OK : S_FALSE;
3378 STDMETHODIMP CRenderedTextSubtitle::RenderEx(SubPicDesc& spd, REFERENCE_TIME rt, double fps, CAtlList<CRect>& rectList)
3380 if(m_size != CSize(spd.w*8, spd.h*8) || m_vidrect != CRect(spd.vidrect.left*8, spd.vidrect.top*8, spd.vidrect.right*8, spd.vidrect.bottom*8))
3381 Init(CSize(spd.w, spd.h), spd.vidrect);
3383 CSubtitle2List sub2List;
3384 HRESULT hr = ParseScript(spd, rt, fps, &sub2List);
3385 if(hr!=S_OK)
3387 return hr;
3390 CompositeDrawItemListList compDrawItemListList;
3391 DoRender(spd, sub2List, &rectList, &compDrawItemListList);
3392 CompositeDrawItem::Draw(spd, compDrawItemListList);
3393 return (!rectList.IsEmpty()) ? S_OK : S_FALSE;
3396 void CRenderedTextSubtitle::DoRender( SubPicDesc& spd, const CSubtitle2List& sub2List,
3397 CAtlList<CRect> *rectList, CompositeDrawItemListList *compDrawItemListList /*output*/)
3399 //check input and log error
3400 POSITION pos=sub2List.GetHeadPosition();
3401 while ( pos!=NULL )
3403 const CSubtitle2& sub2 = sub2List.GetNext(pos);
3404 CompositeDrawItemList& compDrawItemList = compDrawItemListList->GetAt(compDrawItemListList->AddTail());
3405 RenderOneSubtitle(spd, sub2, rectList, &compDrawItemList);
3409 void CRenderedTextSubtitle::RenderOneSubtitle( SubPicDesc& spd, const CSubtitle2& sub2,
3410 CAtlList<CRect>* rectList, CompositeDrawItemList* compDrawItemList /*output*/)
3412 CSubtitle* s = sub2.s;
3413 const CRect& clipRect = sub2.clipRect;
3414 const CPoint& org = sub2.org;
3415 const CPoint& org2 = sub2.org2;
3416 const CPoint& p2 = sub2.p;
3417 int alpha = sub2.alpha;
3418 int time = sub2.time;
3419 if(!s) return;
3421 SharedPtrCClipperPaintMachine clipper( new CClipperPaintMachine(s->m_pClipper) );
3423 CRect iclipRect[4];
3424 iclipRect[0] = CRect(0, 0, spd.w, clipRect.top);
3425 iclipRect[1] = CRect(0, clipRect.top, clipRect.left, clipRect.bottom);
3426 iclipRect[2] = CRect(clipRect.right, clipRect.top, spd.w, clipRect.bottom);
3427 iclipRect[3] = CRect(0, clipRect.bottom, spd.w, spd.h);
3428 CRect bbox2(0,0,0,0);
3429 POSITION pos = s->GetHeadLinePosition();
3430 CPoint p = p2;
3431 while(pos)
3433 CLine* l = s->GetNextLine(pos);
3434 p.x = (s->m_scrAlignment%3) == 1 ? org.x
3435 : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
3436 : org.x - (l->m_width/2);
3438 CompositeDrawItemList tmpCompDrawItemList;
3439 if (s->m_clipInverse)
3441 CompositeDrawItemList tmp1,tmp2,tmp3,tmp4;
3442 for (int i=0;i<l->GetWordCount();i++)
3444 tmp1.AddTail();
3445 tmp2.AddTail();
3446 tmp3.AddTail();
3447 tmp4.AddTail();
3449 bbox2 |= l->PaintAll(&tmp1, spd, iclipRect[0], clipper, p, org2, time, alpha);
3450 bbox2 |= l->PaintAll(&tmp2, spd, iclipRect[1], clipper, p, org2, time, alpha);
3451 bbox2 |= l->PaintAll(&tmp3, spd, iclipRect[2], clipper, p, org2, time, alpha);
3452 bbox2 |= l->PaintAll(&tmp4, spd, iclipRect[3], clipper, p, org2, time, alpha);
3453 tmpCompDrawItemList.AddTailList(&tmp1);
3454 tmpCompDrawItemList.AddTailList(&tmp2);
3455 tmpCompDrawItemList.AddTailList(&tmp3);
3456 tmpCompDrawItemList.AddTailList(&tmp4);
3458 else
3460 for (int i=0;i<l->GetWordCount();i++)
3462 tmpCompDrawItemList.AddTail();
3464 bbox2 |= l->PaintAll(&tmpCompDrawItemList, spd, clipRect, clipper, p, org2, time, alpha);
3466 compDrawItemList->AddTailList(&tmpCompDrawItemList);
3467 p.y += l->m_ascent + l->m_descent;
3469 rectList->AddTail(bbox2);
3472 STDMETHODIMP CRenderedTextSubtitle::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
3474 CAtlList<CRect> rectList;
3475 HRESULT result = RenderEx(spd, rt, fps, rectList);
3476 POSITION pos = rectList.GetHeadPosition();
3477 CRect bbox2(0,0,0,0);
3478 while(pos!=NULL)
3480 bbox2 |= rectList.GetNext(pos);
3482 bbox = bbox2;
3483 return result;
3486 // IPersist
3488 STDMETHODIMP CRenderedTextSubtitle::GetClassID(CLSID* pClassID)
3490 return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
3493 // ISubStream
3495 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStreamCount()
3497 return(1);
3500 STDMETHODIMP CRenderedTextSubtitle::GetStreamInfo(int iStream, WCHAR** ppName, LCID* pLCID)
3502 if(iStream != 0) return E_INVALIDARG;
3503 if(ppName)
3505 if(!(*ppName = (WCHAR*)CoTaskMemAlloc((m_name.GetLength()+1)*sizeof(WCHAR))))
3506 return E_OUTOFMEMORY;
3507 wcscpy(*ppName, CStringW(m_name));
3509 if(pLCID)
3511 *pLCID = 0; // TODO
3513 return S_OK;
3516 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStream()
3518 return(0);
3521 STDMETHODIMP CRenderedTextSubtitle::SetStream(int iStream)
3523 return iStream == 0 ? S_OK : E_FAIL;
3526 STDMETHODIMP CRenderedTextSubtitle::Reload()
3528 CFileStatus s;
3529 if(!CFile::GetStatus(m_path, s)) return E_FAIL;
3530 return !m_path.IsEmpty() && Open(m_path, DEFAULT_CHARSET) ? S_OK : E_FAIL;
3533 STDMETHODIMP_(bool) CRenderedTextSubtitle::IsColorTypeSupported( int type )
3535 return type==MSP_AYUV_PLANAR ||
3536 type==MSP_AYUV ||
3537 type==MSP_XY_AUYV ||
3538 type==MSP_RGBA;