Bugfix: PGS YVU => YUV
[xy_vsfilter.git] / src / subtitles / RTS.cpp
blobc34dc69776729768b689e0e1d7c8ae97341a8085
1 /*
2 * Copyright (C) 2003-2006 Gabest
3 * http://www.gabest.org
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GNU Make; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #include "stdafx.h"
23 #include <math.h>
24 #include <time.h>
25 #include "RTS.h"
26 #include "draw_item.h"
27 #include "cache_manager.h"
28 #include "subpixel_position_controler.h"
29 #include "xy_overlay_paint_machine.h"
30 #include "xy_clipper_paint_machine.h"
32 // WARNING: this isn't very thread safe, use only one RTS a time.
33 static HDC g_hDC;
34 static int g_hDC_refcnt = 0;
36 static long revcolor(long c)
38 return ((c&0xff0000)>>16) + (c&0xff00) + ((c&0xff)<<16);
41 // Skip all leading whitespace
42 inline CStringW::PCXSTR SkipWhiteSpaceLeft(const CStringW& str)
44 CStringW::PCXSTR psz = str.GetString();
46 while( iswspace( *psz ) )
48 psz++;
50 return psz;
53 // Skip all trailing whitespace
54 inline CStringW::PCXSTR SkipWhiteSpaceRight(const CStringW& str)
56 CStringW::PCXSTR psz = str.GetString();
57 CStringW::PCXSTR pszLast = psz + str.GetLength() - 1;
58 bool first_white = false;
59 while( iswspace( *pszLast ) )
61 pszLast--;
62 if(pszLast<psz)
63 break;
65 return pszLast;
68 // Skip all leading whitespace
69 inline CStringW::PCXSTR SkipWhiteSpaceLeft(CStringW::PCXSTR start, CStringW::PCXSTR end)
71 while( start!=end && iswspace( *start ) )
73 start++;
75 return start;
78 // Skip all trailing whitespace, first char must NOT be white space
79 inline CStringW::PCXSTR FastSkipWhiteSpaceRight(CStringW::PCXSTR start, CStringW::PCXSTR end)
81 while( iswspace( *--end ) );
82 return end+1;
85 inline CStringW::PCXSTR FindChar(CStringW::PCXSTR start, CStringW::PCXSTR end, WCHAR c)
87 while( start!=end && *start!=c )
89 start++;
91 return start;
94 //////////////////////////////////////////////////////////////////////////////////////////////
96 // CMyFont
98 CMyFont::CMyFont(const STSStyleBase& style)
100 LOGFONT lf;
101 memset(&lf, 0, sizeof(lf));
102 lf <<= style;
103 lf.lfHeight = (LONG)(style.fontSize+0.5);
104 lf.lfOutPrecision = OUT_TT_PRECIS;
105 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
106 lf.lfQuality = ANTIALIASED_QUALITY;
107 lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
108 if(!CreateFontIndirect(&lf))
110 _tcscpy(lf.lfFaceName, _T("Arial"));
111 CreateFontIndirect(&lf);
113 HFONT hOldFont = SelectFont(g_hDC, *this);
114 TEXTMETRIC tm;
115 GetTextMetrics(g_hDC, &tm);
116 m_ascent = ((tm.tmAscent + 4) >> 3);
117 m_descent = ((tm.tmDescent + 4) >> 3);
118 SelectFont(g_hDC, hOldFont);
121 // CWord
123 CWord::CWord( const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend
124 , double target_scale_x/*=1.0*/, double target_scale_y/*=1.0*/
125 , bool round_to_whole_pixel_after_scale_to_target/*=false*/)
126 : m_style(style), m_str(new CStringW(str))
127 , m_width(0), m_ascent(0), m_descent(0)
128 , m_ktype(ktype), m_kstart(kstart), m_kend(kend)
129 , m_fLineBreak(false), m_fWhiteSpaceChar(false)
130 , m_target_scale_x(target_scale_x), m_target_scale_y(target_scale_y)
131 , m_round_to_whole_pixel_after_scale_to_target(round_to_whole_pixel_after_scale_to_target)
132 //, m_pOpaqueBox(NULL)
134 if(m_str.Get().IsEmpty())
136 m_fWhiteSpaceChar = m_fLineBreak = true;
138 m_width = 0;
141 CWord::CWord( const CWord& src):m_str(src.m_str)
143 m_fWhiteSpaceChar = src.m_fWhiteSpaceChar;
144 m_fLineBreak = src.m_fLineBreak;
145 m_style = src.m_style;
146 m_pOpaqueBox = src.m_pOpaqueBox;//allow since it is shared_ptr
147 m_ktype = src.m_ktype;
148 m_kstart = src.m_kstart;
149 m_kend = src.m_kend;
150 m_width = src.m_width;
151 m_ascent = src.m_ascent;
152 m_descent = src.m_descent;
153 m_target_scale_x = src.m_target_scale_x;
154 m_target_scale_y = src.m_target_scale_y;
155 m_round_to_whole_pixel_after_scale_to_target = src.m_round_to_whole_pixel_after_scale_to_target;
158 CWord::~CWord()
160 //if(m_pOpaqueBox) delete m_pOpaqueBox;
163 bool CWord::Append(const SharedPtrCWord& w)
165 if(!(m_style == w->m_style)
166 || m_fLineBreak || w->m_fLineBreak
167 || w->m_kstart != w->m_kend || m_ktype != w->m_ktype) return(false);
168 m_fWhiteSpaceChar = m_fWhiteSpaceChar && w->m_fWhiteSpaceChar;
169 CStringW *str = new CStringW();//Fix me: anyway to avoid this flyweight update?
170 ASSERT(str);
171 *str = m_str.Get();
172 *str += w->m_str.Get();
173 m_str = XyFwStringW(str);
174 m_width += w->m_width;
175 return(true);
178 void CWord::PaintFromOverlay(const CPointCoor2& p, const CPointCoor2& 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( Rasterizer::IsItReallyBlur(m_style.get().fBlur, m_style.get().fGaussianBlur) )
198 overlay->reset(new Overlay());
199 if(!Rasterizer::Blur(*raterize_result, m_style.get().fBlur, m_style.get().fGaussianBlur,
200 m_target_scale_x, m_target_scale_y, *overlay))
202 *overlay = raterize_result;
205 else
207 *overlay = raterize_result;
209 OverlayMruCache* overlay_cache = CacheManager::GetOverlayMruCache();
210 overlay_cache->UpdateCache(overlay_key, *overlay);
213 bool CWord::PaintFromScanLineData2(const CPointCoor2& psub, const ScanLineData2& scan_line_data2, const OverlayKey& key, SharedPtrOverlay* overlay)
215 SharedPtrOverlay raterize_result(new Overlay());
216 if(!Rasterizer::Rasterize(scan_line_data2, psub.x, psub.y, raterize_result))
218 return false;
220 OverlayNoBlurMruCache* overlay_no_blur_cache = CacheManager::GetOverlayNoBlurMruCache();
221 overlay_no_blur_cache->UpdateCache(key, raterize_result);
222 PaintFromNoneBluredOverlay(raterize_result, key, overlay);
223 return true;
226 bool CWord::PaintFromPathData(const CPointCoor2& psub, const CPointCoor2& trans_org, const PathData& path_data, const OverlayKey& key, SharedPtrOverlay* overlay )
228 bool result = false;
230 PathData *path_data2 = new PathData(path_data);//fix me: this copy operation can be saved if no transform is needed
231 SharedPtrConstPathData shared_ptr_path_data2(path_data2);
232 bool need_transform = NeedTransform();
233 if(need_transform)
234 Transform(path_data2, CPoint(trans_org.x*8, trans_org.y*8));
236 CPoint left_top;
237 CSize size;
238 path_data2->AlignLeftTop(&left_top, &size);
240 int border_x = static_cast<int>(m_style.get().outlineWidthX*m_target_scale_x+0.5);//fix me: rounding err
241 int border_y = static_cast<int>(m_style.get().outlineWidthY*m_target_scale_y+0.5);//fix me: rounding err
242 int wide_border = border_x>border_y ? border_x:border_y;
243 if (m_style.get().borderStyle==1)
245 border_x = border_y = 0;
248 OverlayNoOffsetMruCache* overlay_key_cache = CacheManager::GetOverlayNoOffsetMruCache();
249 OverlayNoOffsetKey overlay_no_offset_key(shared_ptr_path_data2, psub.x, psub.y, border_x, border_y);
250 overlay_no_offset_key.UpdateHashValue();
251 POSITION pos_key = overlay_key_cache->Lookup(overlay_no_offset_key);
252 POSITION pos = NULL;
254 OverlayNoBlurMruCache* overlay_cache = CacheManager::GetOverlayNoBlurMruCache();
255 if (pos_key!=NULL)
257 OverlayNoBlurKey overlay_key = overlay_key_cache->GetAt(pos_key);
258 pos = overlay_cache->Lookup(overlay_key);
260 if (pos)
262 SharedPtrOverlay raterize_result( new Overlay() );
263 *raterize_result = *overlay_cache->GetAt(pos);
264 raterize_result->mOffsetX = left_top.x - psub.x - ((wide_border+7)&~7);
265 raterize_result->mOffsetY = left_top.y - psub.y - ((wide_border+7)&~7);
266 PaintFromNoneBluredOverlay(raterize_result, key, overlay);
267 result = true;
268 overlay_cache->UpdateCache(key, raterize_result);
270 else
272 ScanLineDataMruCache* scan_line_data_cache = CacheManager::GetScanLineDataMruCache();
273 pos = scan_line_data_cache->Lookup(overlay_no_offset_key);
274 SharedPtrConstScanLineData scan_line_data;
275 if( pos != NULL )
277 scan_line_data = scan_line_data_cache->GetAt(pos);
278 scan_line_data_cache->UpdateCache(pos);
280 else
282 ScanLineData *tmp = new ScanLineData();
283 scan_line_data.reset(tmp);
284 if(!tmp->ScanConvert(*path_data2, size))
286 return false;
288 scan_line_data_cache->UpdateCache(overlay_no_offset_key, scan_line_data);
290 ScanLineData2 *tmp = new ScanLineData2(left_top, scan_line_data);
291 SharedPtrScanLineData2 scan_line_data2( tmp );
292 if(m_style.get().borderStyle == 0 && (m_style.get().outlineWidthX+m_style.get().outlineWidthY > 0))
294 if(!tmp->CreateWidenedRegion(border_x, border_y))
296 return false;
299 ScanLineData2MruCache* scan_line_data2_cache = CacheManager::GetScanLineData2MruCache();
300 scan_line_data2_cache->UpdateCache(key, scan_line_data2);
301 result = PaintFromScanLineData2(psub, *tmp, key, overlay);
303 if (result)
305 if (pos_key!=NULL)
307 overlay_key_cache->UpdateCache(pos_key, key);
309 else
311 overlay_key_cache->UpdateCache(overlay_no_offset_key, key);
314 return result;
317 bool CWord::PaintFromRawData( const CPointCoor2& psub, const CPointCoor2& trans_org, const OverlayKey& key, SharedPtrOverlay* overlay )
319 PathDataMruCache* path_data_cache = CacheManager::GetPathDataMruCache();
321 PathData *tmp=new PathData();
322 SharedPtrPathData path_data(tmp);
323 if(!CreatePath(tmp))
325 return false;
327 path_data_cache->UpdateCache(key, path_data);
328 return PaintFromPathData(psub, trans_org, *tmp, key, overlay);
331 bool CWord::DoPaint(const CPointCoor2& psub, const CPointCoor2& trans_org, SharedPtrOverlay* overlay, const OverlayKey& key)
333 bool result = true;
334 OverlayNoBlurMruCache* overlay_no_blur_cache = CacheManager::GetOverlayNoBlurMruCache();
335 POSITION pos = overlay_no_blur_cache->Lookup(key);
337 if(pos!=NULL)
339 SharedPtrOverlay raterize_result = overlay_no_blur_cache->GetAt(pos);
340 overlay_no_blur_cache->UpdateCache( pos );
341 PaintFromNoneBluredOverlay(raterize_result, key, overlay);
343 else
345 ScanLineData2MruCache* scan_line_data_cache = CacheManager::GetScanLineData2MruCache();
346 pos = scan_line_data_cache->Lookup(key);
347 if(pos!=NULL)
349 SharedPtrConstScanLineData2 scan_line_data = scan_line_data_cache->GetAt(pos);
350 scan_line_data_cache->UpdateCache( pos );
351 result = PaintFromScanLineData2(psub, *scan_line_data, key, overlay);
353 else
355 PathDataMruCache* path_data_cache = CacheManager::GetPathDataMruCache();
356 POSITION pos_path = path_data_cache->Lookup(key);
357 if(pos_path!=NULL)
359 SharedPtrConstPathData path_data = path_data_cache->GetAt(pos_path); //important! copy not ref
360 path_data_cache->UpdateCache( pos_path );
361 result = PaintFromPathData(psub, trans_org, *path_data, key, overlay);
363 else
365 result = PaintFromRawData(psub, trans_org, key, overlay);
369 return result;
372 bool CWord::NeedTransform()
374 return (fabs(m_style.get().fontScaleX - 100) > 0.000001) ||
375 (fabs(m_style.get().fontScaleY - 100) > 0.000001) ||
376 (fabs(m_style.get().fontAngleX) > 0.000001) ||
377 (fabs(m_style.get().fontAngleY) > 0.000001) ||
378 (fabs(m_style.get().fontAngleZ) > 0.000001) ||
379 (fabs(m_style.get().fontShiftX) > 0.000001) ||
380 (fabs(m_style.get().fontShiftY) > 0.000001) ||
381 (fabs(m_target_scale_x-1.0) > 0.000001) ||
382 (fabs(m_target_scale_y-1.0) > 0.000001);
385 void CWord::Transform(PathData* path_data, const CPointCoor2& org)
387 //// CPUID from VDub
388 //bool fSSE2 = !!(g_cpuid.m_flags & CCpuID::sse2);
390 //if(fSSE2) { // SSE code
391 // Transform_SSE2(path_data, org);
392 //} else // C-code
393 Transform_C(path_data, org);
396 void CWord::Transform_C(PathData* path_data, const CPointCoor2 &org )
398 ASSERT(path_data);
399 const STSStyle& style = m_style.get();
401 double scalex = style.fontScaleX/100;
402 double scaley = style.fontScaleY/100;
404 double caz = cos((3.1415/180)*style.fontAngleZ);
405 double saz = sin((3.1415/180)*style.fontAngleZ);
406 double cax = cos((3.1415/180)*style.fontAngleX);
407 double sax = sin((3.1415/180)*style.fontAngleX);
408 double cay = cos((3.1415/180)*style.fontAngleY);
409 double say = sin((3.1415/180)*style.fontAngleY);
411 double xxx[3][3];
412 /******************
413 targetScaleX 0 0
414 S0 = 0 targetScaleY 0
415 0 0 1
416 /******************
417 20000 0 0
418 A0 = 0 20000 0
419 0 0 1
420 /******************
421 cay 0 say
422 A1 = 0 1 0
423 say 0 -cay
424 /******************
425 1 0 0
426 A2 = 0 cax sax
427 0 sax -cax
428 /******************
429 caz saz 0
430 A3 =-saz caz 0
431 0 0 1
432 /******************
433 scalex scalex*fontShiftX -org.x/targetScaleX
434 A4 = scaley*fontShiftY scaley -org.y/targetScaleY
435 0 0 0
436 /******************
437 0 0 0
438 B0 = 0 0 0
439 0 0 20000
440 /******************
441 Formula:
442 (x,y,z)' = (S0*A0*A1*A2*A3*A4 + B0) * (x y 1)'
443 z = max(1000,z)
444 x = x/z + tagetScaleX*org.x
445 y = y/z + tagetScaleY*org.y
446 *******************/
448 //A3*A4
449 ASSERT(m_target_scale_x!=0 && m_target_scale_y!=0);
450 double tmp1 = -org.x/m_target_scale_x;
451 double tmp2 = -org.y/m_target_scale_y;
453 xxx[0][0] = caz*scalex + saz*scaley*style.fontShiftY;
454 xxx[0][1] = caz*scalex*style.fontShiftX + saz*scaley;
455 xxx[0][2] = caz*tmp1 + saz*tmp2;
457 xxx[1][0] = -saz*scalex + caz*scaley*style.fontShiftY;
458 xxx[1][1] = -saz*scalex*style.fontShiftX + caz*scaley;
459 xxx[1][2] = -saz*tmp1 + caz*tmp2;
461 xxx[2][0] =
462 xxx[2][0] =
463 xxx[2][0] = 0;
465 //A2*A3*A4
467 xxx[2][0] = sax*xxx[1][0];
468 xxx[2][1] = sax*xxx[1][1];
469 xxx[2][2] = sax*xxx[1][2];
471 xxx[1][0] = cax*xxx[1][0];
472 xxx[1][1] = cax*xxx[1][1];
473 xxx[1][2] = cax*xxx[1][2];
475 //A1*A2*A3*A4
477 tmp1 = xxx[0][0];
478 tmp2 = xxx[0][1];
479 double tmp3 = xxx[0][2];
480 xxx[0][0] = cay*tmp1 + say*xxx[2][0];
481 xxx[0][1] = cay*tmp2 + say*xxx[2][1];
482 xxx[0][2] = cay*tmp3 + say*xxx[2][2];
484 xxx[2][0] = say*tmp1 - cay*xxx[2][0];
485 xxx[2][1] = say*tmp2 - cay*xxx[2][1];
486 xxx[2][2] = say*tmp3 - cay*xxx[2][2];
488 //S0*A0*A1*A2*A3*A4
490 tmp1 = 20000*m_target_scale_x;
491 xxx[0][0] *= tmp1;
492 xxx[0][1] *= tmp1;
493 xxx[0][2] *= tmp1;
495 tmp1 = 20000*m_target_scale_y;
496 xxx[1][0] *= tmp1;
497 xxx[1][1] *= tmp1;
498 xxx[1][2] *= tmp1;
500 //A0*A1*A2*A3*A4+B0
502 xxx[2][2] += 20000;
504 double scaled_org_x = org.x+0.5;
505 double scaled_org_y = org.y+0.5;
507 for (int i = 0; i < path_data->mPathPoints; i++) {
508 double x, y, z, xx;
510 xx = path_data->mpPathPoints[i].x;
511 y = path_data->mpPathPoints[i].y;
513 z = xxx[2][0] * xx + xxx[2][1] * y + xxx[2][2];
514 x = xxx[0][0] * xx + xxx[0][1] * y + xxx[0][2];
515 y = xxx[1][0] * xx + xxx[1][1] * y + xxx[1][2];
517 z = z > 1000 ? z : 1000;
519 x = x / z;
520 y = y / z;
522 path_data->mpPathPoints[i].x = (long)(x + scaled_org_x);
523 path_data->mpPathPoints[i].y = (long)(y + scaled_org_y);
524 if (m_round_to_whole_pixel_after_scale_to_target && (m_target_scale_x!=1.0 || m_target_scale_y!=1.0))
526 path_data->mpPathPoints[i].x = (path_data->mpPathPoints[i].x + 32)&~63;
527 path_data->mpPathPoints[i].y = (path_data->mpPathPoints[i].y + 32)&~63;//fix me: readability
532 void CWord::Transform_SSE2(PathData* path_data, const CPointCoor2 &org )
534 // __m128 union data type currently not supported with Intel C++ Compiler, so just call C version
535 #ifdef __ICL
536 Transform_C(org);
537 #else
538 // SSE code
539 // speed up ~1.5-1.7x
540 double scalex = m_style.get().fontScaleX/100;
541 double scaley = m_style.get().fontScaleY/100;
543 double caz = cos((3.1415/180)*m_style.get().fontAngleZ);
544 double saz = sin((3.1415/180)*m_style.get().fontAngleZ);
545 double cax = cos((3.1415/180)*m_style.get().fontAngleX);
546 double sax = sin((3.1415/180)*m_style.get().fontAngleX);
547 double cay = cos((3.1415/180)*m_style.get().fontAngleY);
548 double say = sin((3.1415/180)*m_style.get().fontAngleY);
550 __m128 __xshift = _mm_set_ps1(m_style.get().fontShiftX);
551 __m128 __yshift = _mm_set_ps1(m_style.get().fontShiftY);
553 __m128 __xorg = _mm_set_ps1(org.x);
554 __m128 __yorg = _mm_set_ps1(org.y);
556 __m128 __xscale = _mm_set_ps1(scalex);
557 __m128 __yscale = _mm_set_ps1(scaley);
559 #ifdef _VSMOD
560 // patch m003. random text points
561 double xrnd = m_style.get().mod_rand.X*100;
562 double yrnd = m_style.get().mod_rand.Y*100;
563 double zrnd = m_style.get().mod_rand.Z*100;
565 srand(m_style.get().mod_rand.Seed);
567 __m128 __xsz = _mm_setzero_ps();
568 __m128 __ysz = _mm_setzero_ps();
570 __m128 __dst1x, __dst1y, __dst213x, __dst213y, __dst3x, __dst3y;
572 __m128 __miny;
573 __m128 __minx = _mm_set_ps(INT_MAX, INT_MAX, 0, 0);
574 __m128 __max = _mm_set_ps(-INT_MAX, -INT_MAX, 1, 1);
576 bool is_dist = m_style.get().mod_distort.enabled;
577 if(is_dist) {
578 for(int i = 0; i < path_data->mPathPoints; i++) {
579 __m128 __point = _mm_set_ps(path_data->mpPathPoints[i].x, path_data->mpPathPoints[i].y, 0, 0);
580 __minx = _mm_min_ps(__minx, __point);
581 __max = _mm_max_ps(__max, __point);
584 __m128 __zero = _mm_setzero_ps();
585 __max = _mm_sub_ps(__max, __minx); // xsz, ysz, 1, 1
586 __max = _mm_max_ps(__max, __zero);
588 __xsz = _mm_shuffle_ps(__max, __max, _MM_SHUFFLE(3,3,3,3));
589 __ysz = _mm_shuffle_ps(__max, __max, _MM_SHUFFLE(2,2,2,2));
591 __miny = _mm_shuffle_ps(__minx, __minx, _MM_SHUFFLE(2,2,2,2));
592 __minx = _mm_shuffle_ps(__minx, __minx, _MM_SHUFFLE(3,3,3,3));
594 __dst1x = _mm_set_ps1(m_style.get().mod_distort.pointsx[0]);
595 __dst1y = _mm_set_ps1(m_style.get().mod_distort.pointsy[0]);
596 __dst3x = _mm_set_ps1(m_style.get().mod_distort.pointsx[2]);
597 __dst3y = _mm_set_ps1(m_style.get().mod_distort.pointsy[2]);
598 __dst213x = _mm_set_ps1(m_style.get().mod_distort.pointsx[1]); // 2 - 1 - 3
599 __dst213x = _mm_sub_ps(__dst213x, __dst1x);
600 __dst213x = _mm_sub_ps(__dst213x, __dst3x);
602 __dst213y = _mm_set_ps1(m_style.get().mod_distort.pointsy[1]);
603 __dst213x = _mm_sub_ps(__dst213y, __dst1y);
604 __dst213x = _mm_sub_ps(__dst213y, __dst3y);
606 #endif
608 __m128 __caz = _mm_set_ps1(caz);
609 __m128 __saz = _mm_set_ps1(saz);
610 __m128 __cax = _mm_set_ps1(cax);
611 __m128 __sax = _mm_set_ps1(sax);
612 __m128 __cay = _mm_set_ps1(cay);
613 __m128 __say = _mm_set_ps1(say);
615 // this can be paralleled for openmp
616 int mPathPointsD4 = path_data->mPathPoints / 4;
617 int mPathPointsM4 = path_data->mPathPoints % 4;
619 for(ptrdiff_t i = 0; i < mPathPointsD4 + 1; i++) {
620 POINT* const temp_points = path_data->mpPathPoints + 4 * i;
622 __m128 __pointx, __pointy;
623 // we can't use load .-.
624 if(i != mPathPointsD4) {
625 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, temp_points[2].x, temp_points[3].x);
626 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, temp_points[2].y, temp_points[3].y);
627 } else { // last cycle
628 switch(mPathPointsM4) {
629 default:
630 case 0:
631 continue;
632 case 1:
633 __pointx = _mm_set_ps(temp_points[0].x, 0, 0, 0);
634 __pointy = _mm_set_ps(temp_points[0].y, 0, 0, 0);
635 break;
636 case 2:
637 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, 0, 0);
638 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, 0, 0);
639 break;
640 case 3:
641 __pointx = _mm_set_ps(temp_points[0].x, temp_points[1].x, temp_points[2].x, 0);
642 __pointy = _mm_set_ps(temp_points[0].y, temp_points[1].y, temp_points[2].y, 0);
643 break;
647 #ifdef _VSMOD
648 __m128 __pointz = _mm_set_ps1(m_style.get().mod_z);
650 // distort
651 if(is_dist) {
652 //P = P0 + (P1 - P0)u + (P3 - P0)v + (P0 + P2 - P1 - P3)uv
653 __m128 __u = _mm_sub_ps(__pointx, __minx);
654 __m128 __v = _mm_sub_ps(__pointy, __miny);
655 __m128 __1_xsz = _mm_rcp_ps(__xsz);
656 __m128 __1_ysz = _mm_rcp_ps(__ysz);
657 __u = _mm_mul_ps(__u, __1_xsz);
658 __v = _mm_mul_ps(__v, __1_ysz);
660 // x
661 __pointx = _mm_mul_ps(__dst213x, __u);
662 __pointx = _mm_mul_ps(__pointx, __v);
664 __m128 __tmpx = _mm_mul_ps(__dst3x, __v);
665 __pointx = _mm_add_ps(__pointx, __tmpx);
666 __tmpx = _mm_mul_ps(__dst1x, __u);
667 __pointx = _mm_add_ps(__pointx, __tmpx);
669 __pointx = _mm_mul_ps(__pointx, __xsz);
670 __pointx = _mm_add_ps(__pointx, __minx);
672 // y
673 __pointy = _mm_mul_ps(__dst213y, __u);
674 __pointy = _mm_mul_ps(__pointy, __v);
676 __m128 __tmpy = _mm_mul_ps(__dst3y, __v);
677 __pointy = _mm_add_ps(__pointy, __tmpy);
678 __tmpy = _mm_mul_ps(__dst1y, __u);
679 __pointy = _mm_add_ps(__pointy, __tmpy);
681 __pointy = _mm_mul_ps(__pointy, __ysz);
682 __pointy = _mm_add_ps(__pointy, __miny);
685 // randomize
686 if(xrnd!=0 || yrnd!=0 || zrnd!=0) {
687 __declspec(align(16)) float rx[4], ry[4], rz[4];
688 for(int k=0; k<4; k++) {
689 rx[k] = xrnd > 0 ? (xrnd - rand() % (int)(xrnd * 2 + 1)) : 0;
690 ry[k] = yrnd > 0 ? (yrnd - rand() % (int)(yrnd * 2 + 1)) : 0;
691 rz[k] = zrnd > 0 ? (zrnd - rand() % (int)(zrnd * 2 + 1)) : 0;
693 __m128 __001 = _mm_set_ps1(0.01f);
695 if(xrnd!=0) {
696 __m128 __rx = _mm_load_ps(rx);
697 __rx = _mm_mul_ps(__rx, __001);
698 __pointx = _mm_add_ps(__pointx, __rx);
701 if(yrnd!=0) {
702 __m128 __ry = _mm_load_ps(ry);
703 __ry = _mm_mul_ps(__ry, __001);
704 __pointy = _mm_add_ps(__pointy, __ry);
707 if(zrnd!=0) {
708 __m128 __rz = _mm_load_ps(rz);
709 __rz = _mm_mul_ps(__rz, __001);
710 __pointz = _mm_add_ps(__pointz, __rz);
713 #else
714 __m128 __pointz = _mm_set_ps1(0);
715 #endif
717 // scale and shift
718 __m128 __tmpx;
719 if(m_style.get().fontShiftX!=0) {
720 __tmpx = _mm_mul_ps(__xshift, __pointy);
721 __tmpx = _mm_add_ps(__tmpx, __pointx);
722 } else {
723 __tmpx = __pointx;
725 __tmpx = _mm_mul_ps(__tmpx, __xscale);
726 __tmpx = _mm_sub_ps(__tmpx, __xorg);
728 __m128 __tmpy;
729 if(m_style.get().fontShiftY!=0) {
730 __tmpy = _mm_mul_ps(__yshift, __pointx);
731 __tmpy = _mm_add_ps(__tmpy, __pointy);
732 } else {
733 __tmpy = __pointy;
735 __tmpy = _mm_mul_ps(__tmpy, __yscale);
736 __tmpy = _mm_sub_ps(__tmpy, __yorg);
738 // rotate
739 __m128 __xx = _mm_mul_ps(__tmpx, __caz);
740 __m128 __yy = _mm_mul_ps(__tmpy, __saz);
741 __pointx = _mm_add_ps(__xx, __yy);
742 __xx = _mm_mul_ps(__tmpx, __saz);
743 __yy = _mm_mul_ps(__tmpy, __caz);
744 __pointy = _mm_sub_ps(__yy, __xx);
746 __m128 __zz = _mm_mul_ps(__pointz, __sax);
747 __yy = _mm_mul_ps(__pointy, __cax);
748 __pointy = _mm_add_ps(__yy, __zz);
749 __zz = _mm_mul_ps(__pointz, __cax);
750 __yy = _mm_mul_ps(__pointy, __sax);
751 __pointz = _mm_sub_ps(__zz, __yy);
753 __xx = _mm_mul_ps(__pointx, __cay);
754 __zz = _mm_mul_ps(__pointz, __say);
755 __pointx = _mm_add_ps(__xx, __zz);
756 __xx = _mm_mul_ps(__pointx, __say);
757 __zz = _mm_mul_ps(__pointz, __cay);
758 __pointz = _mm_sub_ps(__xx, __zz);
760 __zz = _mm_set_ps1(-19000);
761 __pointz = _mm_max_ps(__pointz, __zz);
763 __m128 __20000 = _mm_set_ps1(20000);
764 __zz = _mm_add_ps(__pointz, __20000);
765 __zz = _mm_rcp_ps(__zz);
767 __pointx = _mm_mul_ps(__pointx, __20000);
768 __pointx = _mm_mul_ps(__pointx, __zz);
770 __pointy = _mm_mul_ps(__pointy, __20000);
771 __pointy = _mm_mul_ps(__pointy, __zz);
773 __pointx = _mm_add_ps(__pointx, __xorg);
774 __pointy = _mm_add_ps(__pointy, __yorg);
776 __m128 __05 = _mm_set_ps1(0.5);
778 __pointx = _mm_add_ps(__pointx, __05);
779 __pointy = _mm_add_ps(__pointy, __05);
781 if(i == mPathPointsD4) { // last cycle
782 for(int k=0; k<mPathPointsM4; k++) {
783 temp_points[k].x = static_cast<LONG>(__pointx.m128_f32[3-k]);
784 temp_points[k].y = static_cast<LONG>(__pointy.m128_f32[3-k]);
786 } else {
787 for(int k=0; k<4; k++) {
788 temp_points[k].x = static_cast<LONG>(__pointx.m128_f32[3-k]);
789 temp_points[k].y = static_cast<LONG>(__pointy.m128_f32[3-k]);
793 #endif // __ICL
796 bool CWord::CreateOpaqueBox()
798 if(m_pOpaqueBox) return(true);
799 STSStyle style = m_style.get();
800 style.borderStyle = 0;
801 style.outlineWidthX = style.outlineWidthY = 0;
802 style.colors[0] = m_style.get().colors[2];
803 style.alpha[0] = m_style.get().alpha[2];
804 int w = (int)(m_style.get().outlineWidthX + 0.5);
805 int h = (int)(m_style.get().outlineWidthY + 0.5);
806 CStringW str;
807 str.Format(L"m %d %d l %d %d %d %d %d %d",
808 -w, -h,
809 m_width+w, -h,
810 m_width+w, m_ascent+m_descent+h,
811 -w, m_ascent+m_descent+h);
812 m_pOpaqueBox.reset( new CPolygon(FwSTSStyle(style), str, 0, 0, 0, 1.0/8, 1.0/8, 0, m_target_scale_x, m_target_scale_y) );
813 return(!!m_pOpaqueBox);
816 bool CWord::operator==( const CWord& rhs ) const
818 return (this==&rhs) || (
819 m_str.GetId() == rhs.m_str.GetId() &&
820 m_fWhiteSpaceChar == rhs.m_fWhiteSpaceChar &&
821 m_fLineBreak == rhs.m_fLineBreak &&
822 m_style == rhs.m_style && //fix me:?
823 m_ktype == rhs.m_ktype &&
824 m_kstart == rhs.m_kstart &&
825 m_kend == rhs.m_kend &&
826 m_width == rhs.m_width &&
827 m_ascent == rhs.m_ascent &&
828 m_descent == rhs.m_descent &&
829 m_target_scale_x == rhs.m_target_scale_x &&
830 m_target_scale_y == rhs.m_target_scale_y);
831 //m_pOpaqueBox
835 // CText
837 CText::CText( const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend
838 , double target_scale_x/*=1.0*/, double target_scale_y/*=1.0*/ )
839 : CWord(style, str, ktype, kstart, kend, target_scale_x, target_scale_y)
841 if(m_str.Get() == L" ")
843 m_fWhiteSpaceChar = true;
845 SharedPtrTextInfo text_info;
846 TextInfoCacheKey text_info_key;
847 text_info_key.m_str_id = m_str.GetId();
848 text_info_key.m_style = m_style;
849 text_info_key.UpdateHashValue();
850 TextInfoMruCache* text_info_cache = CacheManager::GetTextInfoCache();
851 POSITION pos = text_info_cache->Lookup(text_info_key);
852 if(pos==NULL)
854 TextInfo* tmp=new TextInfo();
855 GetTextInfo(tmp, m_style, m_str.Get());
856 text_info.reset(tmp);
857 text_info_cache->UpdateCache(text_info_key, text_info);
859 else
861 text_info = text_info_cache->GetAt(pos);
862 text_info_cache->UpdateCache( pos );
864 this->m_ascent = text_info->m_ascent;
865 this->m_descent = text_info->m_descent;
866 this->m_width = text_info->m_width;
869 CText::CText( const CText& src ):CWord(src)
871 m_width = src.m_width;
874 SharedPtrCWord CText::Copy()
876 SharedPtrCWord result(new CText(*this));
877 return result;
880 bool CText::Append(const SharedPtrCWord& w)
882 boost::shared_ptr<CText> p = boost::dynamic_pointer_cast<CText>(w);
883 return (p && CWord::Append(w));
886 bool CText::CreatePath(PathData* path_data)
888 FwCMyFont font(m_style);
889 HFONT hOldFont = SelectFont(g_hDC, font.get());
890 int width = 0;
891 const CStringW& str = m_str.Get();
892 if(m_style.get().fontSpacing || (long)GetVersion() < 0)
894 bool bFirstPath = true;
895 for(LPCWSTR s = str; *s; s++)
897 CSize extent;
898 if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
899 path_data->PartialBeginPath(g_hDC, bFirstPath);
900 bFirstPath = false;
901 TextOutW(g_hDC, 0, 0, s, 1);
902 path_data->PartialEndPath(g_hDC, width, 0);
903 width += extent.cx + (int)m_style.get().fontSpacing;
906 else
908 CSize extent;
909 if(!GetTextExtentPoint32W(g_hDC, str, str.GetLength(), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
910 path_data->BeginPath(g_hDC);
911 TextOutW(g_hDC, 0, 0, str, str.GetLength());
912 path_data->EndPath(g_hDC);
914 SelectFont(g_hDC, hOldFont);
915 return(true);
918 void CText::GetTextInfo(TextInfo *output, const FwSTSStyle& style, const CStringW& str )
920 FwCMyFont font(style);
921 output->m_ascent = (int)(style.get().fontScaleY/100*font.get().m_ascent);
922 output->m_descent = (int)(style.get().fontScaleY/100*font.get().m_descent);
924 HFONT hOldFont = SelectFont(g_hDC, font.get());
925 if(style.get().fontSpacing || (long)GetVersion() < 0)
927 bool bFirstPath = true;
928 for(LPCWSTR s = str; *s; s++)
930 CSize extent;
931 if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
932 output->m_width += extent.cx + (int)style.get().fontSpacing;
934 // m_width -= (int)m_style.get().fontSpacing; // TODO: subtract only at the end of the line
936 else
938 CSize extent;
939 if(!GetTextExtentPoint32W(g_hDC, str, wcslen(str), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
940 output->m_width += extent.cx;
942 output->m_width = (int)(style.get().fontScaleX/100*output->m_width + 4) >> 3;
943 SelectFont(g_hDC, hOldFont);
946 // CPolygon
948 CPolygon::CPolygon( const FwSTSStyle& style, const CStringW& str, int ktype, int kstart, int kend
949 , double scalex, double scaley, int baseline
950 , double target_scale_x/*=1.0*/, double target_scale_y/*=1.0*/
951 , bool round_to_whole_pixel_after_scale_to_target/*=false*/)
952 : CWord(style, str, ktype, kstart, kend, target_scale_x, target_scale_y, round_to_whole_pixel_after_scale_to_target)
953 , m_scalex(scalex), m_scaley(scaley), m_baseline(baseline)
955 ParseStr();
958 CPolygon::CPolygon(CPolygon& src) : CWord(src)
960 m_scalex = src.m_scalex;
961 m_scaley = src.m_scaley;
962 m_baseline = src.m_baseline;
963 m_width = src.m_width;
964 m_ascent = src.m_ascent;
965 m_descent = src.m_descent;
966 m_pathTypesOrg.Copy(src.m_pathTypesOrg);
967 m_pathPointsOrg.Copy(src.m_pathPointsOrg);
970 CPolygon::~CPolygon()
974 SharedPtrCWord CPolygon::Copy()
976 SharedPtrCWord result(DNew CPolygon(*this));
977 return result;
980 bool CPolygon::Append(const SharedPtrCWord& w)
982 // TODO
983 return(false);
986 bool CPolygon::Get6BitFixedPoint(CStringW& str, LONG& ret)
988 LPWSTR s = (LPWSTR)(LPCWSTR)str, e = s;
989 ret = wcstod(str, &e) * 64;
990 str.Delete(0,e-s);
991 XY_LOG_INFO(ret);
992 return(e > s);
995 bool CPolygon::GetPOINT(CStringW& str, POINT& ret)
997 return(Get6BitFixedPoint(str, ret.x) && Get6BitFixedPoint(str, ret.y));
1000 bool CPolygon::ParseStr()
1002 if(m_pathTypesOrg.GetCount() > 0) return(true);
1003 CPoint p;
1004 int j, lastsplinestart = -1, firstmoveto = -1, lastmoveto = -1;
1005 CStringW str = m_str.Get();
1006 str.SpanIncluding(L"mnlbspc 0123456789");
1007 str.Replace(L"m", L"*m");
1008 str.Replace(L"n", L"*n");
1009 str.Replace(L"l", L"*l");
1010 str.Replace(L"b", L"*b");
1011 str.Replace(L"s", L"*s");
1012 str.Replace(L"p", L"*p");
1013 str.Replace(L"c", L"*c");
1014 int k = 0;
1015 for(CStringW s = str.Tokenize(L"*", k); !s.IsEmpty(); s = str.Tokenize(L"*", k))
1017 WCHAR c = s[0];
1018 s.TrimLeft(L"mnlbspc ");
1019 switch(c)
1021 case 'm':
1022 lastmoveto = m_pathTypesOrg.GetCount();
1023 if(firstmoveto == -1)
1024 firstmoveto = lastmoveto;
1025 while(GetPOINT(s, p)) {
1026 m_pathTypesOrg.Add(PT_MOVETO);
1027 m_pathPointsOrg.Add(p);
1029 break;
1030 case 'n':
1031 while(GetPOINT(s, p)) {
1032 m_pathTypesOrg.Add(PT_MOVETONC);
1033 m_pathPointsOrg.Add(p);
1035 break;
1036 case 'l':
1037 if (m_pathPointsOrg.GetCount() < 1) {
1038 break;
1040 while(GetPOINT(s, p)) {
1041 m_pathTypesOrg.Add(PT_LINETO);
1042 m_pathPointsOrg.Add(p);
1044 break;
1045 case 'b':
1046 j = m_pathTypesOrg.GetCount();
1047 if (j < 1) {
1048 break;
1050 while(GetPOINT(s, p)) {
1051 m_pathTypesOrg.Add(PT_BEZIERTO);
1052 m_pathPointsOrg.Add(p);
1053 j++;
1055 j = m_pathTypesOrg.GetCount() - ((m_pathTypesOrg.GetCount()-j)%3);
1056 m_pathTypesOrg.SetCount(j);
1057 m_pathPointsOrg.SetCount(j);
1058 break;
1059 case 's':
1060 if (m_pathPointsOrg.GetCount() < 1) {
1061 break;
1064 j = lastsplinestart = m_pathTypesOrg.GetCount();
1065 int i = 3;
1066 while(i-- && GetPOINT(s, p)) {
1067 m_pathTypesOrg.Add(PT_BSPLINETO);
1068 m_pathPointsOrg.Add(p);
1069 j++;
1071 if(m_pathTypesOrg.GetCount()-lastsplinestart < 3) {
1072 m_pathTypesOrg.SetCount(lastsplinestart);
1073 m_pathPointsOrg.SetCount(lastsplinestart);
1074 lastsplinestart = -1;
1077 // no break here
1078 case 'p':
1079 if (m_pathPointsOrg.GetCount() < 3) {
1080 break;
1082 while(GetPOINT(s, p)) {
1083 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1084 m_pathPointsOrg.Add(p);
1086 break;
1087 case 'c':
1088 if(lastsplinestart > 0)
1090 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1091 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1092 m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
1093 p = m_pathPointsOrg[lastsplinestart-1]; // we need p for temp storage, because operator [] will return a reference to CPoint and Add() may reallocate its internal buffer (this is true for MFC 7.0 but not for 6.0, hehe)
1094 m_pathPointsOrg.Add(p);
1095 p = m_pathPointsOrg[lastsplinestart];
1096 m_pathPointsOrg.Add(p);
1097 p = m_pathPointsOrg[lastsplinestart+1];
1098 m_pathPointsOrg.Add(p);
1099 lastsplinestart = -1;
1101 break;
1102 default:
1103 break;
1106 if(lastmoveto == -1 || firstmoveto > 0)
1108 m_pathTypesOrg.RemoveAll();
1109 m_pathPointsOrg.RemoveAll();
1110 return(false);
1112 int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
1113 for(size_t i = 0; i < m_pathTypesOrg.GetCount(); i++)
1115 m_pathPointsOrg[i].x = (int)(m_scalex * m_pathPointsOrg[i].x);
1116 m_pathPointsOrg[i].y = (int)(m_scaley * m_pathPointsOrg[i].y);
1117 if(minx > m_pathPointsOrg[i].x) minx = m_pathPointsOrg[i].x;
1118 if(miny > m_pathPointsOrg[i].y) miny = m_pathPointsOrg[i].y;
1119 if(maxx < m_pathPointsOrg[i].x) maxx = m_pathPointsOrg[i].x;
1120 if(maxy < m_pathPointsOrg[i].y) maxy = m_pathPointsOrg[i].y;
1122 m_width = max(maxx - minx, 0);
1123 m_ascent = max(maxy - miny, 0);
1124 int baseline = (int)(64 * m_scaley * m_baseline);
1125 m_descent = baseline;
1126 m_ascent -= baseline;
1127 m_width = ((int)(m_style.get().fontScaleX/100 * m_width) + 4) >> 3;
1128 m_ascent = ((int)(m_style.get().fontScaleY/100 * m_ascent) + 4) >> 3;
1129 m_descent = ((int)(m_style.get().fontScaleY/100 * m_descent) + 4) >> 3;
1130 return(true);
1133 bool CPolygon::CreatePath(PathData* path_data)
1135 int len = m_pathTypesOrg.GetCount();
1136 if(len == 0) return(false);
1137 if(path_data->mPathPoints != len)
1139 path_data->mpPathTypes = (BYTE*)realloc(path_data->mpPathTypes, len*sizeof(BYTE));
1140 path_data->mpPathPoints = (POINT*)realloc(path_data->mpPathPoints, len*sizeof(POINT));
1141 if(!path_data->mpPathTypes || !path_data->mpPathPoints) return(false);
1142 path_data->mPathPoints = len;
1144 memcpy(path_data->mpPathTypes, m_pathTypesOrg.GetData(), len*sizeof(BYTE));
1145 memcpy(path_data->mpPathPoints, m_pathPointsOrg.GetData(), len*sizeof(POINT));
1146 return(true);
1149 // CClipper
1151 CClipper::CClipper(CStringW str, CSizeCoor2 size, double scalex, double scaley, bool inverse
1152 , double target_scale_x/*=1.0*/, double target_scale_y/*=1.0*/)
1153 : m_polygon( new CPolygon(FwSTSStyle(), str, 0, 0, 0, scalex, scaley, 0, target_scale_x, target_scale_y, true) )
1154 , m_size(size), m_inverse(inverse)
1155 , m_effectType(-1), m_painted(false)
1160 CClipper::~CClipper()
1164 GrayImage2* CClipper::PaintSimpleClipper()
1166 GrayImage2* result = NULL;
1167 if(m_size.cx < 0 || m_size.cy < 0)
1168 return result;
1170 SharedPtrOverlay overlay;
1171 CWordPaintMachine::PaintBody( m_polygon, CPoint(0, 0), CPoint(0, 0), &overlay );
1172 int w = overlay->mOverlayWidth, h = overlay->mOverlayHeight;
1173 int x = (overlay->mOffsetX+4)>>3, y = (overlay->mOffsetY+4)>>3;
1174 result = new GrayImage2();
1175 if( !result )
1176 return result;
1177 result->data = overlay->mBody;
1178 result->pitch = overlay->mOverlayPitch;
1179 result->size.SetSize(w, h);
1180 result->left_top.SetPoint(x, y);
1181 return result;
1184 GrayImage2* CClipper::PaintBaseClipper()
1186 GrayImage2* result = NULL;
1187 //m_pAlphaMask = NULL;
1188 if(m_size.cx < 0 || m_size.cy < 0)
1189 return result;
1191 SharedPtrOverlay overlay;
1192 CWordPaintMachine::PaintBody( m_polygon, CPoint(0, 0), CPoint(0, 0), &overlay );
1193 int w = overlay->mOverlayWidth, h = overlay->mOverlayHeight;
1194 int x = (overlay->mOffsetX+4)>>3, y = (overlay->mOffsetY+4)>>3;
1195 int xo = 0, yo = 0;
1196 if(x < 0) {xo = -x; w -= -x; x = 0;}
1197 if(y < 0) {yo = -y; h -= -y; y = 0;}
1198 if(x+w > m_size.cx) w = m_size.cx-x;
1199 if(y+h > m_size.cy) h = m_size.cy-y;
1200 if(w <= 0 || h <= 0) return result;
1202 result = new GrayImage2();
1203 if( !result )
1204 return result;
1205 result->data.reset( reinterpret_cast<BYTE*>(xy_malloc(m_size.cx*m_size.cy)), xy_free );
1206 result->pitch = m_size.cx;
1207 result->size = m_size;
1208 result->left_top.SetPoint(0, 0);
1210 BYTE * result_data = result->data.get();
1211 if(!result_data)
1213 delete result;
1214 return NULL;
1217 memset( result_data, 0, m_size.cx*m_size.cy );
1219 const BYTE* src = overlay->mBody.get() + (overlay->mOverlayPitch * yo + xo);
1220 BYTE* dst = result_data + m_size.cx * y + x;
1221 while(h--)
1223 //for(int wt=0; wt<w; ++wt)
1224 // dst[wt] = src[wt];
1225 memcpy(dst, src, w);
1226 src += overlay->mOverlayPitch;
1227 dst += m_size.cx;
1229 if(m_inverse)
1231 BYTE* dst = result_data;
1232 for(int i = m_size.cx*m_size.cy; i>0; --i, ++dst)
1233 *dst = 0x40 - *dst; // mask is 6 bit
1235 return result;
1238 GrayImage2* CClipper::PaintBannerClipper()
1240 ASSERT(m_polygon);
1242 int width = static_cast<int>(m_effect.param[2] * m_polygon->m_target_scale_x);//fix me: rounding err
1243 int w = m_size.cx, h = m_size.cy;
1245 GrayImage2* result = PaintBaseClipper();
1246 if(!result)
1247 return result;
1249 int da = (64<<8)/width;
1250 BYTE* am = result->data.get();
1251 for(int j = 0; j < h; j++, am += w)
1253 int a = 0;
1254 int k = min(width, w);
1255 for(int i = 0; i < k; i++, a += da)
1256 am[i] = (am[i]*a)>>14;
1257 a = 0x40<<8;
1258 k = w-width;
1259 if(k < 0) {a -= -k*da; k = 0;}
1260 for(int i = k; i < w; i++, a -= da)
1261 am[i] = (am[i]*a)>>14;
1263 return result;
1266 GrayImage2* CClipper::PaintScrollClipper()
1268 ASSERT(m_polygon);
1270 int height = static_cast<int>(m_effect.param[4] * m_polygon->m_target_scale_y);//fix me: rounding err
1271 int w = m_size.cx, h = m_size.cy;
1273 GrayImage2* result = PaintBaseClipper();
1274 if(!result)
1275 return result;
1277 BYTE* data = result->data.get();
1279 int da = (64<<8)/height;
1280 int a = 0;
1281 int k = (static_cast<int>(m_effect.param[0] * m_polygon->m_target_scale_y)>>3);//fix me: rounding err
1282 int l = k+height;
1283 if(k < 0) {a += -k*da; k = 0;}
1284 if(l > h) {l = h;}
1285 if(k < h)
1287 BYTE* am = &data[k*w];
1288 memset(data, 0, am - data);
1289 for(int j = k; j < l; j++, a += da)
1291 for(int i = 0; i < w; i++, am++)
1292 *am = ((*am)*a)>>14;
1295 da = -(64<<8)/height;
1296 a = 0x40<<8;
1297 l = (static_cast<int>(m_effect.param[1] * m_polygon->m_target_scale_y)>>3);//fix me: rounding err
1298 k = l-height;
1299 if(k < 0) {a += -k*da; k = 0;}
1300 if(l > h) {l = h;}
1301 if(k < h)
1303 BYTE* am = &data[k*w];
1304 int j = k;
1305 for(; j < l; j++, a += da)
1307 for(int i = 0; i < w; i++, am++)
1308 *am = ((*am)*a)>>14;
1310 memset(am, 0, (h-j)*w);
1312 return result;
1315 GrayImage2* CClipper::Paint()
1317 GrayImage2* result = NULL;
1318 switch(m_effectType)
1320 case -1:
1321 if (!m_inverse)
1323 result = PaintSimpleClipper();
1325 else
1327 result = PaintBaseClipper();
1329 break;
1330 case EF_BANNER:
1331 result = PaintBannerClipper();
1332 break;
1333 case EF_SCROLL:
1334 result = PaintScrollClipper();
1335 break;
1337 return result;
1340 void CClipper::SetEffect( const Effect& effect, int effectType )
1342 m_effectType = effectType;
1343 m_effect = effect;
1346 SharedPtrGrayImage2 CClipper::GetAlphaMask( const SharedPtrCClipper& clipper )
1348 SharedPtrGrayImage2 result;
1349 CClipperPaintMachine paint_machine(clipper);
1350 paint_machine.Paint(&result);
1351 return result;
1354 // CLine
1356 CLine::~CLine()
1358 //POSITION pos = GetHeadPosition();
1359 //while(pos) delete GetNext(pos);
1362 void CLine::Compact()
1364 POSITION pos = GetHeadPosition();
1365 while(pos)
1367 SharedPtrCWord w = GetNext(pos);
1368 if(!w->m_fWhiteSpaceChar) break;
1369 m_width -= w->m_width;
1370 // delete w;
1371 RemoveHead();
1373 pos = GetTailPosition();
1374 while(pos)
1376 SharedPtrCWord w = GetPrev(pos);
1377 if(!w->m_fWhiteSpaceChar) break;
1378 m_width -= w->m_width;
1379 // delete w;
1380 RemoveTail();
1382 if(IsEmpty()) return;
1383 CLine l;
1384 l.AddTailList(this);
1385 RemoveAll();
1386 SharedPtrCWord last;
1387 pos = l.GetHeadPosition();
1388 while(pos)
1390 SharedPtrCWord w = l.GetNext(pos);
1391 if(!last || !last->Append(w))
1392 AddTail(last = w->Copy());
1394 m_ascent = m_descent = m_borderX = m_borderY = 0;
1395 pos = GetHeadPosition();
1396 while(pos)
1398 SharedPtrCWord w = GetNext(pos);
1399 if(m_ascent < w->m_ascent) m_ascent = w->m_ascent;
1400 if(m_descent < w->m_descent) m_descent = w->m_descent;
1401 if(m_borderX < w->m_style.get().outlineWidthX) m_borderX = (int)(w->m_style.get().outlineWidthX+0.5);
1402 if(m_borderY < w->m_style.get().outlineWidthY) m_borderY = (int)(w->m_style.get().outlineWidthY+0.5);
1406 CRectCoor2 CLine::PaintAll( CompositeDrawItemList* output, const CRectCoor2& clipRect,
1407 const SharedPtrCClipperPaintMachine &clipper, CPoint p, const CPoint& org, const int time, const int alpha )
1409 CRectCoor2 bbox(0, 0, 0, 0);
1410 POSITION pos = GetHeadPosition();
1411 POSITION outputPos = output->GetHeadPosition();
1412 while(pos)
1414 SharedPtrCWord w = GetNext(pos);
1415 CompositeDrawItem& outputItem = output->GetNext(outputPos);
1416 if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
1417 CPointCoor2 shadowPos, outlinePos, bodyPos, org_coor2;
1419 double shadowPos_x = p.x + w->m_style.get().shadowDepthX;
1420 double shadowPos_y = p.y + m_ascent - w->m_ascent + w->m_style.get().shadowDepthY;
1421 outlinePos = CPoint(p.x, p.y + m_ascent - w->m_ascent);
1422 bodyPos = CPoint(p.x, p.y + m_ascent - w->m_ascent);
1424 shadowPos.x = static_cast<int>(shadowPos_x * w->m_target_scale_x + 0.5);
1425 shadowPos.y = static_cast<int>(shadowPos_y * w->m_target_scale_y + 0.5);
1426 outlinePos.x *= w->m_target_scale_x;
1427 outlinePos.y *= w->m_target_scale_y;
1428 bodyPos.x *= w->m_target_scale_x;
1429 bodyPos.y *= w->m_target_scale_y;
1430 org_coor2.x = org.x * w->m_target_scale_x;//fix me: move it out of this loop
1431 org_coor2.y = org.y * w->m_target_scale_y;
1433 bool hasShadow = w->m_style.get().shadowDepthX != 0 || w->m_style.get().shadowDepthY != 0;
1434 bool hasOutline = ((w->m_style.get().outlineWidthX*w->m_target_scale_x+0.5>=1.0) ||
1435 (w->m_style.get().outlineWidthY*w->m_target_scale_y+0.5)>=1.0) && !(w->m_ktype == 2 && time < w->m_kstart);
1436 bool hasBody = true;
1438 SharedPtrOverlayPaintMachine shadow_pm, outline_pm, body_pm;
1439 CWordPaintMachine::CreatePaintMachines(w, shadowPos, outlinePos, bodyPos, org_coor2,
1440 hasShadow ? &shadow_pm : NULL,
1441 hasOutline ? &outline_pm : NULL,
1442 hasBody ? &body_pm : NULL);
1444 //shadow
1445 if(hasShadow)
1447 DWORD a = 0xff - w->m_style.get().alpha[3];
1448 if(alpha > 0) a = MulDiv(a, 0xff - alpha, 0xff);
1449 COLORREF shadow = revcolor(w->m_style.get().colors[3]) | (a<<24);
1450 DWORD sw[6] = {shadow, -1};
1451 sw[0] = XySubRenderFrameCreater::GetDefaultCreater()->TransColor(sw[0]);
1452 if(w->m_style.get().borderStyle == 0)
1454 outputItem.shadow.reset(
1455 DrawItem::CreateDrawItem(shadow_pm, clipRect, clipper, shadowPos.x, shadowPos.y, sw,
1456 w->m_ktype > 0 || w->m_style.get().alpha[0] < 0xff,
1457 hasOutline)
1460 else if(w->m_style.get().borderStyle == 1)
1462 outputItem.shadow.reset(
1463 DrawItem::CreateDrawItem( shadow_pm, clipRect, clipper, shadowPos.x, shadowPos.y, sw, true, false)
1467 //outline
1468 if(hasOutline)
1470 DWORD aoutline = w->m_style.get().alpha[2];
1471 if(alpha > 0) aoutline += MulDiv(alpha, 0xff - w->m_style.get().alpha[2], 0xff);
1472 COLORREF outline = revcolor(w->m_style.get().colors[2]) | ((0xff-aoutline)<<24);
1473 DWORD sw[6] = {outline, -1};
1474 sw[0] = XySubRenderFrameCreater::GetDefaultCreater()->TransColor(sw[0]);
1475 if(w->m_style.get().borderStyle == 0)
1477 outputItem.outline.reset(
1478 DrawItem::CreateDrawItem(outline_pm, clipRect, clipper, outlinePos.x, outlinePos.y, sw,
1479 !w->m_style.get().alpha[0] && !w->m_style.get().alpha[1] && !alpha, true)
1482 else if(w->m_style.get().borderStyle == 1)
1484 outputItem.outline.reset(
1485 DrawItem::CreateDrawItem(outline_pm, clipRect, clipper, outlinePos.x, outlinePos.y, sw, true, false)
1489 //body
1490 if(hasBody)
1492 // colors
1493 DWORD aprimary = w->m_style.get().alpha[0];
1494 if(alpha > 0) aprimary += MulDiv(alpha, 0xff - w->m_style.get().alpha[0], 0xff);
1495 COLORREF primary = revcolor(w->m_style.get().colors[0]) | ((0xff-aprimary)<<24);
1496 DWORD asecondary = w->m_style.get().alpha[1];
1497 if(alpha > 0) asecondary += MulDiv(alpha, 0xff - w->m_style.get().alpha[1], 0xff);
1498 COLORREF secondary = revcolor(w->m_style.get().colors[1]) | ((0xff-asecondary)<<24);
1499 DWORD sw[6] = {primary, 0, secondary};
1500 // karaoke
1501 double t;
1502 if(w->m_ktype == 0 || w->m_ktype == 2)
1504 t = time < w->m_kstart ? 0 : 1;
1506 else if(w->m_ktype == 1)
1508 if(time < w->m_kstart) t = 0;
1509 else if(time < w->m_kend)
1511 t = 1.0 * (time - w->m_kstart) / (w->m_kend - w->m_kstart);
1512 double angle = fmod(w->m_style.get().fontAngleZ, 360.0);
1513 if(angle > 90 && angle < 270)
1515 t = 1-t;
1516 COLORREF tmp = sw[0];
1517 sw[0] = sw[2];
1518 sw[2] = tmp;
1521 else t = 1.0;
1523 if(t >= 1)
1525 sw[1] = 0xffffffff;
1527 sw[3] = (int)( (w->m_style.get().outlineWidthX + t*w->m_width)*w->m_target_scale_x ) >> 3;
1528 sw[4] = sw[2];
1529 sw[5] = 0x00ffffff;
1530 sw[0] = XySubRenderFrameCreater::GetDefaultCreater()->TransColor(sw[0]);
1531 sw[2] = XySubRenderFrameCreater::GetDefaultCreater()->TransColor(sw[2]);
1532 sw[4] = XySubRenderFrameCreater::GetDefaultCreater()->TransColor(sw[4]);
1533 outputItem.body.reset(
1534 DrawItem::CreateDrawItem(body_pm, clipRect, clipper, bodyPos.x, bodyPos.y, sw, true, false)
1537 bbox |= CompositeDrawItem::GetDirtyRect(outputItem);
1538 p.x += w->m_width;
1540 return(bbox);
1543 void CLine::AddWord2Tail( SharedPtrCWord words )
1545 __super::AddTail(words);
1548 bool CLine::IsEmpty()
1550 return __super::IsEmpty();
1553 int CLine::GetWordCount()
1555 return GetCount();
1558 // CSubtitle
1560 CSubtitle::CSubtitle()
1562 memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
1563 m_clipInverse = false;
1564 m_scalex = m_scaley = 1;
1565 m_fAnimated2 = false;
1567 m_target_scale_x = m_target_scale_y = 1.0;
1570 CSubtitle::~CSubtitle()
1572 Empty();
1575 void CSubtitle::Empty()
1577 POSITION pos = GetHeadPosition();
1578 while(pos) delete GetNext(pos);
1579 // pos = m_words.GetHeadPosition();
1580 // while(pos) delete m_words.GetNext(pos);
1581 for(int i = 0; i < EF_NUMBEROFEFFECTS; i++) {if(m_effects[i]) delete m_effects[i];}
1582 memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
1585 int CSubtitle::GetFullWidth()
1587 int width = 0;
1588 POSITION pos = m_words.GetHeadPosition();
1589 while(pos) width += m_words.GetNext(pos)->m_width;
1590 return(width);
1593 int CSubtitle::GetFullLineWidth(POSITION pos)
1595 int width = 0;
1596 while(pos)
1598 SharedPtrCWord w = m_words.GetNext(pos);
1599 if(w->m_fLineBreak) break;
1600 width += w->m_width;
1602 return(width);
1605 int CSubtitle::GetWrapWidth(POSITION pos, int maxwidth)
1607 if(m_wrapStyle == 0 || m_wrapStyle == 3)
1609 if(maxwidth > 0)
1611 // int fullwidth = GetFullWidth();
1612 int fullwidth = GetFullLineWidth(pos);
1613 int minwidth = fullwidth / ((abs(fullwidth) / maxwidth) + 1);
1614 int width = 0, wordwidth = 0;
1615 while(pos && width < minwidth)
1617 SharedPtrCWord w = m_words.GetNext(pos);
1618 wordwidth = w->m_width;
1619 if(abs(width + wordwidth) < abs(maxwidth)) width += wordwidth;
1621 maxwidth = width;
1622 if(m_wrapStyle == 3 && pos) maxwidth -= wordwidth;
1625 else if(m_wrapStyle == 1)
1627 // maxwidth = maxwidth;
1629 else if(m_wrapStyle == 2)
1631 maxwidth = INT_MAX;
1633 return(maxwidth);
1636 CLine* CSubtitle::GetNextLine(POSITION& pos, int maxwidth)
1638 if(pos == NULL) return(NULL);
1639 CLine* ret = new CLine();
1640 if(!ret) return(NULL);
1641 ret->m_width = ret->m_ascent = ret->m_descent = ret->m_borderX = ret->m_borderY = 0;
1642 maxwidth = GetWrapWidth(pos, maxwidth);
1643 bool fEmptyLine = true;
1644 while(pos)
1646 SharedPtrCWord w = m_words.GetNext(pos);
1647 if(ret->m_ascent < w->m_ascent) ret->m_ascent = w->m_ascent;
1648 if(ret->m_descent < w->m_descent) ret->m_descent = w->m_descent;
1649 if(ret->m_borderX < w->m_style.get().outlineWidthX) ret->m_borderX = (int)(w->m_style.get().outlineWidthX+0.5);
1650 if(ret->m_borderY < w->m_style.get().outlineWidthY) ret->m_borderY = (int)(w->m_style.get().outlineWidthY+0.5);
1651 if(w->m_fLineBreak)
1653 if(fEmptyLine) {ret->m_ascent /= 2; ret->m_descent /= 2; ret->m_borderX = ret->m_borderY = 0;}
1654 ret->Compact();
1655 return(ret);
1657 fEmptyLine = false;
1658 bool fWSC = w->m_fWhiteSpaceChar;
1659 int width = w->m_width;
1660 POSITION pos2 = pos;
1661 while(pos2)
1663 if(m_words.GetAt(pos2)->m_fWhiteSpaceChar != fWSC
1664 || m_words.GetAt(pos2)->m_fLineBreak) break;
1665 SharedPtrCWord w2 = m_words.GetNext(pos2);
1666 width += w2->m_width;
1668 if((ret->m_width += width) <= maxwidth || ret->IsEmpty())
1670 ret->AddWord2Tail(w);
1671 while(pos != pos2)
1673 ret->AddWord2Tail(m_words.GetNext(pos));
1675 pos = pos2;
1677 else
1679 if(pos) m_words.GetPrev(pos);
1680 else pos = m_words.GetTailPosition();
1681 ret->m_width -= width;
1682 break;
1685 ret->Compact();
1686 return(ret);
1689 void CSubtitle::CreateClippers( CSize size1, const CSizeCoor2& size_scale_to )
1691 size1.cx >>= 3;
1692 size1.cy >>= 3;
1693 if(m_effects[EF_BANNER] && m_effects[EF_BANNER]->param[2])
1695 int w = size1.cx, h = size1.cy;
1696 if(!m_pClipper)
1698 CStringW str;
1699 str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
1700 m_pClipper.reset( new CClipper(str, size_scale_to, 1, 1, false, m_target_scale_x, m_target_scale_y) );
1701 if(!m_pClipper) return;
1703 m_pClipper->SetEffect( *m_effects[EF_BANNER], EF_BANNER );
1705 else if(m_effects[EF_SCROLL] && m_effects[EF_SCROLL]->param[4])
1707 int height = m_effects[EF_SCROLL]->param[4];
1708 int w = size1.cx, h = size1.cy;
1709 if(!m_pClipper)
1711 CStringW str;
1712 str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
1713 m_pClipper.reset( new CClipper(str, size_scale_to, 1, 1, false, m_target_scale_x, m_target_scale_y) );
1714 if(!m_pClipper) return;
1716 m_pClipper->SetEffect(*m_effects[EF_SCROLL], EF_SCROLL);
1720 void CSubtitle::MakeLines(CSize size, CRect marginRect)
1722 CSize spaceNeeded(0, 0);
1723 bool fFirstLine = true;
1724 m_topborder = m_bottomborder = 0;
1725 CLine* l = NULL;
1726 POSITION pos = m_words.GetHeadPosition();
1727 while(pos)
1729 l = GetNextLine(pos, size.cx - marginRect.left - marginRect.right);
1730 if(!l) break;
1731 if(fFirstLine) {m_topborder = l->m_borderY; fFirstLine = false;}
1732 spaceNeeded.cx = max(l->m_width+l->m_borderX, spaceNeeded.cx);
1733 spaceNeeded.cy += l->m_ascent + l->m_descent;
1734 AddTail(l);
1736 if(l) m_bottomborder = l->m_borderY;
1737 m_rect = CRect(
1738 CPoint((m_scrAlignment%3) == 1 ? marginRect.left
1739 : (m_scrAlignment%3) == 2 ? (marginRect.left + (size.cx - marginRect.right) - spaceNeeded.cx + 1) / 2
1740 : (size.cx - marginRect.right - spaceNeeded.cx),
1741 m_scrAlignment <= 3 ? (size.cy - marginRect.bottom - spaceNeeded.cy)
1742 : m_scrAlignment <= 6 ? (marginRect.top + (size.cy - marginRect.bottom) - spaceNeeded.cy + 1) / 2
1743 : marginRect.top),
1744 spaceNeeded);
1747 POSITION CSubtitle::GetHeadLinePosition()
1749 return __super::GetHeadPosition();
1752 CLine* CSubtitle::GetNextLine( POSITION& pos )
1754 return __super::GetNext(pos);
1757 // CScreenLayoutAllocator
1759 void CScreenLayoutAllocator::Empty()
1761 m_subrects.RemoveAll();
1764 void CScreenLayoutAllocator::AdvanceToSegment(int segment, const CAtlArray<int>& sa)
1766 POSITION pos = m_subrects.GetHeadPosition();
1767 while(pos)
1769 POSITION prev = pos;
1770 SubRect& sr = m_subrects.GetNext(pos);
1771 bool fFound = false;
1772 if(abs(sr.segment - segment) <= 1) // using abs() makes it possible to play the subs backwards, too :)
1774 for(size_t i = 0; i < sa.GetCount() && !fFound; i++)
1776 if(sa[i] == sr.entry)
1778 sr.segment = segment;
1779 fFound = true;
1783 if(!fFound) m_subrects.RemoveAt(prev);
1787 CRect CScreenLayoutAllocator::AllocRect(CSubtitle* s, int segment, int entry, int layer, int collisions)
1789 // TODO: handle collisions == 1 (reversed collisions)
1790 POSITION pos = m_subrects.GetHeadPosition();
1791 while(pos)
1793 SubRect& sr = m_subrects.GetNext(pos);
1794 if(sr.segment == segment && sr.entry == entry)
1796 return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
1799 CRect r = s->m_rect + CRect(0, s->m_topborder, 0, s->m_bottomborder);
1800 bool fSearchDown = s->m_scrAlignment > 3;
1801 bool fOK;
1804 fOK = true;
1805 pos = m_subrects.GetHeadPosition();
1806 while(pos)
1808 SubRect& sr = m_subrects.GetNext(pos);
1809 if(layer == sr.layer && !(r & sr.r).IsRectEmpty())
1811 if(fSearchDown)
1813 r.bottom = sr.r.bottom + r.Height();
1814 r.top = sr.r.bottom;
1816 else
1818 r.top = sr.r.top - r.Height();
1819 r.bottom = sr.r.top;
1821 fOK = false;
1825 while(!fOK);
1826 SubRect sr;
1827 sr.r = r;
1828 sr.segment = segment;
1829 sr.entry = entry;
1830 sr.layer = layer;
1831 m_subrects.AddTail(sr);
1832 return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
1835 // CRenderedTextSubtitle
1837 CAtlMap<CStringW, CRenderedTextSubtitle::AssCmdType, CStringElementTraits<CStringW>> CRenderedTextSubtitle::m_cmdMap;
1839 CRenderedTextSubtitle::CRenderedTextSubtitle(CCritSec* pLock)
1840 : CSubPicProviderImpl(pLock)
1841 , m_target_scale_x(1.0), m_target_scale_y(1.0)
1843 if( m_cmdMap.IsEmpty() )
1845 InitCmdMap();
1847 m_size = CSize(0, 0);
1848 if(g_hDC_refcnt == 0)
1850 g_hDC = CreateCompatibleDC(NULL);
1851 SetBkMode(g_hDC, TRANSPARENT);
1852 SetTextColor(g_hDC, 0xffffff);
1853 SetMapMode(g_hDC, MM_TEXT);
1855 g_hDC_refcnt++;
1858 CRenderedTextSubtitle::~CRenderedTextSubtitle()
1860 Deinit();
1861 g_hDC_refcnt--;
1862 if(g_hDC_refcnt == 0) DeleteDC(g_hDC);
1865 void CRenderedTextSubtitle::InitCmdMap()
1867 if( m_cmdMap.IsEmpty() )
1869 m_cmdMap.SetAt(L"1c", CMD_1c);
1870 m_cmdMap.SetAt(L"2c", CMD_2c);
1871 m_cmdMap.SetAt(L"3c", CMD_3c);
1872 m_cmdMap.SetAt(L"4c", CMD_4c);
1873 m_cmdMap.SetAt(L"1a", CMD_1a);
1874 m_cmdMap.SetAt(L"2a", CMD_2a);
1875 m_cmdMap.SetAt(L"3a", CMD_3a);
1876 m_cmdMap.SetAt(L"4a", CMD_4a);
1877 m_cmdMap.SetAt(L"alpha", CMD_alpha);
1878 m_cmdMap.SetAt(L"an", CMD_an);
1879 m_cmdMap.SetAt(L"a", CMD_a);
1880 m_cmdMap.SetAt(L"blur", CMD_blur);
1881 m_cmdMap.SetAt(L"bord", CMD_bord);
1882 m_cmdMap.SetAt(L"be", CMD_be);
1883 m_cmdMap.SetAt(L"b", CMD_b);
1884 m_cmdMap.SetAt(L"clip", CMD_clip);
1885 m_cmdMap.SetAt(L"iclip", CMD_iclip);
1886 m_cmdMap.SetAt(L"c", CMD_c);
1887 m_cmdMap.SetAt(L"fade", CMD_fade);
1888 m_cmdMap.SetAt(L"fad", CMD_fad);
1889 m_cmdMap.SetAt(L"fax", CMD_fax);
1890 m_cmdMap.SetAt(L"fay", CMD_fay);
1891 m_cmdMap.SetAt(L"fe", CMD_fe);
1892 m_cmdMap.SetAt(L"fn", CMD_fn);
1893 m_cmdMap.SetAt(L"frx", CMD_frx);
1894 m_cmdMap.SetAt(L"fry", CMD_fry);
1895 m_cmdMap.SetAt(L"frz", CMD_frz);
1896 m_cmdMap.SetAt(L"fr", CMD_fr);
1897 m_cmdMap.SetAt(L"fscx", CMD_fscx);
1898 m_cmdMap.SetAt(L"fscy", CMD_fscy);
1899 m_cmdMap.SetAt(L"fsc", CMD_fsc);
1900 m_cmdMap.SetAt(L"fsp", CMD_fsp);
1901 m_cmdMap.SetAt(L"fs", CMD_fs);
1902 m_cmdMap.SetAt(L"i", CMD_i);
1903 m_cmdMap.SetAt(L"kt", CMD_kt);
1904 m_cmdMap.SetAt(L"kf", CMD_kf);
1905 m_cmdMap.SetAt(L"K", CMD_K);
1906 m_cmdMap.SetAt(L"ko", CMD_ko);
1907 m_cmdMap.SetAt(L"k", CMD_k);
1908 m_cmdMap.SetAt(L"move", CMD_move);
1909 m_cmdMap.SetAt(L"org", CMD_org);
1910 m_cmdMap.SetAt(L"pbo", CMD_pbo);
1911 m_cmdMap.SetAt(L"pos", CMD_pos);
1912 m_cmdMap.SetAt(L"p", CMD_p);
1913 m_cmdMap.SetAt(L"q", CMD_q);
1914 m_cmdMap.SetAt(L"r", CMD_r);
1915 m_cmdMap.SetAt(L"shad", CMD_shad);
1916 m_cmdMap.SetAt(L"s", CMD_s);
1917 m_cmdMap.SetAt(L"t", CMD_t);
1918 m_cmdMap.SetAt(L"u", CMD_u);
1919 m_cmdMap.SetAt(L"xbord", CMD_xbord);
1920 m_cmdMap.SetAt(L"xshad", CMD_xshad);
1921 m_cmdMap.SetAt(L"ybord", CMD_ybord);
1922 m_cmdMap.SetAt(L"yshad", CMD_yshad);
1926 void CRenderedTextSubtitle::Copy(CSimpleTextSubtitle& sts)
1928 __super::Copy(sts);
1929 m_size = CSize(0, 0);
1930 if(CRenderedTextSubtitle* pRTS = dynamic_cast<CRenderedTextSubtitle*>(&sts))
1932 m_size = pRTS->m_size;
1936 void CRenderedTextSubtitle::Empty()
1938 Deinit();
1939 __super::Empty();
1942 void CRenderedTextSubtitle::OnChanged()
1944 __super::OnChanged();
1945 POSITION pos = m_subtitleCache.GetStartPosition();
1946 while(pos)
1948 int i;
1949 CSubtitle* s;
1950 m_subtitleCache.GetNextAssoc(pos, i, s);
1951 delete s;
1953 m_subtitleCache.RemoveAll();
1954 m_sla.Empty();
1958 bool CRenderedTextSubtitle::Init( const SIZECoor2& size_scale_to, const SIZE& size1, const CRect& video_rect )
1960 Deinit();
1961 m_size_scale_to = CSize(size_scale_to.cx*8, size_scale_to.cy*8);//fix me?
1962 m_size = CSize(size1.cx*8, size1.cy*8);
1963 m_vidrect = CRect(video_rect.left*8, video_rect.top*8, video_rect.right*8, video_rect.bottom*8);
1965 ASSERT(size1.cx!=0 && size1.cy!=0);
1967 m_target_scale_x = size_scale_to.cx * 1.0 / size1.cx;
1968 m_target_scale_y = size_scale_to.cy * 1.0 / size1.cy;
1970 return(true);
1973 void CRenderedTextSubtitle::Deinit()
1975 POSITION pos = m_subtitleCache.GetStartPosition();
1976 while(pos)
1978 int i;
1979 CSubtitle* s;
1980 m_subtitleCache.GetNextAssoc(pos, i, s);
1981 delete s;
1983 m_subtitleCache.RemoveAll();
1984 m_sla.Empty();
1986 m_size_scale_to = CSize(0, 0);
1987 m_size = CSize(0, 0);
1988 m_vidrect.SetRectEmpty();
1990 m_target_scale_x = m_target_scale_y = 1.0;
1992 CacheManager::GetBitmapMruCache()->RemoveAll();
1994 CacheManager::GetClipperAlphaMaskMruCache()->RemoveAll();
1995 CacheManager::GetTextInfoCache()->RemoveAll();
1996 CacheManager::GetAssTagListMruCache()->RemoveAll();
1998 CacheManager::GetScanLineDataMruCache()->RemoveAll();
1999 CacheManager::GetOverlayNoOffsetMruCache()->RemoveAll();
2001 CacheManager::GetSubpixelVarianceCache()->RemoveAll();
2002 CacheManager::GetOverlayMruCache()->RemoveAll();
2003 CacheManager::GetOverlayNoBlurMruCache()->RemoveAll();
2004 CacheManager::GetScanLineData2MruCache()->RemoveAll();
2005 CacheManager::GetPathDataMruCache()->RemoveAll();
2008 void CRenderedTextSubtitle::ParseEffect(CSubtitle* sub, const CStringW& str)
2010 CStringW::PCXSTR str_start = str.GetString();
2011 CStringW::PCXSTR str_end = str_start + str.GetLength();
2012 str_start = SkipWhiteSpaceLeft(str_start, str_end);
2014 if(!sub || *str_start==0)
2015 return;
2017 str_end = FastSkipWhiteSpaceRight(str_start, str_end);
2019 const WCHAR* s = FindChar(str_start, str_end, L';');
2020 if(*s==L';') {
2021 s++;
2024 const CStringW effect(str_start, s-str_start);
2025 if(!effect.CompareNoCase( L"Banner;" ) )
2027 int delay, lefttoright = 0, fadeawaywidth = 0;
2028 if(swscanf(s, L"%d;%d;%d", &delay, &lefttoright, &fadeawaywidth) < 1) return;
2029 Effect* e = new Effect;
2030 if(!e) return;
2031 sub->m_effects[e->type = EF_BANNER] = e;
2032 e->param[0] = (int)(max(1.0*delay/sub->m_scalex, 1));
2033 e->param[1] = lefttoright;
2034 e->param[2] = (int)(sub->m_scalex*fadeawaywidth);
2035 sub->m_wrapStyle = 2;
2037 else if(!effect.CompareNoCase(L"Scroll up;") || !effect.CompareNoCase(L"Scroll down;"))
2039 int top, bottom, delay, fadeawayheight = 0;
2040 if(swscanf(s, L"%d;%d;%d;%d", &top, &bottom, &delay, &fadeawayheight) < 3) return;
2041 if(top > bottom) {int tmp = top; top = bottom; bottom = tmp;}
2042 Effect* e = new Effect;
2043 if(!e) return;
2044 sub->m_effects[e->type = EF_SCROLL] = e;
2045 e->param[0] = (int)(sub->m_scaley*top*8);
2046 e->param[1] = (int)(sub->m_scaley*bottom*8);
2047 e->param[2] = (int)(max(1.0*delay/sub->m_scaley, 1));
2048 e->param[3] = (effect.GetLength() == 12);
2049 e->param[4] = (int)(sub->m_scaley*fadeawayheight);
2053 void CRenderedTextSubtitle::ParseString(CSubtitle* sub, CStringW str, const FwSTSStyle& style)
2055 if(!sub) return;
2056 str.Replace(L"\\N", L"\n");
2057 str.Replace(L"\\n", (sub->m_wrapStyle < 2 || sub->m_wrapStyle == 3) ? L" " : L"\n");
2058 str.Replace(L"\\h", L"\x00A0");
2059 for(int ite = 0, j = 0, len = str.GetLength(); j <= len; j++)
2061 WCHAR c = str[j];
2062 if(c != L'\n' && c != L' ' && c != 0)
2063 continue;
2064 if(ite < j)
2066 if(PCWord tmp_ptr = new CText(style, str.Mid(ite, j-ite), m_ktype, m_kstart, m_kend
2067 , m_target_scale_x, m_target_scale_y))
2069 SharedPtrCWord w(tmp_ptr);
2070 sub->m_words.AddTail(w);
2072 else
2074 ///TODO: overflow handling
2076 m_kstart = m_kend;
2078 if(c == L'\n')
2080 if(PCWord tmp_ptr = new CText(style, CStringW(), m_ktype, m_kstart, m_kend
2081 , m_target_scale_x, m_target_scale_y))
2083 SharedPtrCWord w(tmp_ptr);
2084 sub->m_words.AddTail(w);
2086 else
2088 ///TODO: overflow handling
2090 m_kstart = m_kend;
2092 else if(c == L' ')
2094 if(PCWord tmp_ptr = new CText(style, CStringW(c), m_ktype, m_kstart, m_kend
2095 , m_target_scale_x, m_target_scale_y))
2097 SharedPtrCWord w(tmp_ptr);
2098 sub->m_words.AddTail(w);
2100 else
2102 ///TODO: overflow handling
2104 m_kstart = m_kend;
2106 ite = j+1;
2108 return;
2111 void CRenderedTextSubtitle::ParsePolygon(CSubtitle* sub, const CStringW& str, const FwSTSStyle& style)
2113 if(!sub || !str.GetLength() || !m_nPolygon) return;
2115 if(PCWord tmp_ptr = new CPolygon(style, str, m_ktype, m_kstart, m_kend, sub->m_scalex/(1<<(m_nPolygon-1))
2116 , sub->m_scaley/(1<<(m_nPolygon-1)), m_polygonBaselineOffset
2117 , m_target_scale_x, m_target_scale_y))
2119 SharedPtrCWord w(tmp_ptr);
2120 ///Todo: fix me
2121 //if( PCWord w_cache = m_wordCache.lookup(*w) )
2123 // sub->m_words.AddTail(w_cache);
2124 // delete w;
2126 //else
2128 sub->m_words.AddTail(w);
2130 m_kstart = m_kend;
2134 bool CRenderedTextSubtitle::ParseSSATag( AssTagList *assTags, const CStringW& str )
2136 if(!assTags) return(false);
2137 int nTags = 0, nUnrecognizedTags = 0;
2138 for(int i = 0, j; (j = str.Find(L'\\', i)) >= 0; i = j)
2140 POSITION pos = assTags->AddTail();
2141 AssTag& assTag = assTags->GetAt(pos);
2142 assTag.cmdType = CMD_COUNT;
2144 j++;
2145 CStringW::PCXSTR str_start = str.GetString() + j;
2146 CStringW::PCXSTR pc = str_start;
2147 while( iswspace(*pc) )
2149 pc++;
2151 j += pc-str_start;
2152 str_start = pc;
2153 while( *pc && *pc != L'(' && *pc != L'\\' )
2155 pc++;
2157 j += pc-str_start;
2158 if( pc-str_start>0 )
2160 while( iswspace(*--pc) );
2161 pc++;
2164 const CStringW cmd(str_start, pc-str_start);
2165 if(cmd.IsEmpty()) continue;
2167 CAtlArray<CStringW>& params = assTag.strParams;
2168 if(str[j] == L'(')
2170 j++;
2171 CStringW::PCXSTR str_start = str.GetString() + j;
2172 CStringW::PCXSTR pc = str_start;
2173 while( iswspace(*pc) )
2175 pc++;
2177 j += pc-str_start;
2178 str_start = pc;
2179 while( *pc && *pc != L')' )
2181 pc++;
2183 j += pc-str_start;
2184 if( pc-str_start>0 )
2186 while( iswspace(*--pc) );
2187 pc++;
2190 CStringW::PCXSTR param_start = str_start;
2191 CStringW::PCXSTR param_end = pc;
2192 while( param_start<param_end )
2194 param_start = SkipWhiteSpaceLeft(param_start, param_end);
2196 CStringW::PCXSTR newstart = FindChar(param_start, param_end, L',');
2197 CStringW::PCXSTR newend = FindChar(param_start, param_end, L'\\');
2198 if(newstart > param_start && newstart < newend)
2200 newstart = FastSkipWhiteSpaceRight(param_start, newstart);
2201 CStringW s(param_start, newstart - param_start);
2203 if(!s.IsEmpty()) params.Add(s);
2204 param_start = newstart + 1;
2206 else if(param_start<param_end)
2208 CStringW s(param_start, param_end - param_start);
2210 params.Add(s);
2211 param_start = param_end;
2216 AssCmdType cmd_type = CMD_COUNT;
2217 int cmd_length = min(MAX_CMD_LENGTH, cmd.GetLength());
2218 for( ;cmd_length>=MIN_CMD_LENGTH;cmd_length-- )
2220 if( m_cmdMap.Lookup(cmd.Left(cmd_length), cmd_type) )
2221 break;
2223 if(cmd_length<MIN_CMD_LENGTH)
2224 cmd_type = CMD_COUNT;
2225 switch( cmd_type )
2227 case CMD_fax:
2228 case CMD_fay:
2229 case CMD_fe:
2230 case CMD_fn:
2231 case CMD_frx:
2232 case CMD_fry:
2233 case CMD_frz:
2234 case CMD_fr:
2235 case CMD_fscx:
2236 case CMD_fscy:
2237 case CMD_fsc:
2238 case CMD_fsp:
2239 case CMD_fs:
2240 case CMD_i:
2241 case CMD_kt:
2242 case CMD_kf:
2243 case CMD_K:
2244 case CMD_ko:
2245 case CMD_k:
2246 case CMD_pbo:
2247 case CMD_p:
2248 case CMD_q:
2249 case CMD_r:
2250 case CMD_shad:
2251 case CMD_s:
2252 case CMD_an:
2253 case CMD_a:
2254 case CMD_blur:
2255 case CMD_bord:
2256 case CMD_be:
2257 case CMD_b:
2258 case CMD_u:
2259 case CMD_xbord:
2260 case CMD_xshad:
2261 case CMD_ybord:
2262 case CMD_yshad:
2263 // default:
2264 params.Add(cmd.Mid(cmd_length));
2265 break;
2266 case CMD_c:
2267 case CMD_1c :
2268 case CMD_2c :
2269 case CMD_3c :
2270 case CMD_4c :
2271 case CMD_1a :
2272 case CMD_2a :
2273 case CMD_3a :
2274 case CMD_4a :
2275 case CMD_alpha:
2276 params.Add(cmd.Mid(cmd_length).Trim(L"&H"));
2277 break;
2278 case CMD_clip:
2279 case CMD_iclip:
2280 case CMD_fade:
2281 case CMD_fad:
2282 case CMD_move:
2283 case CMD_org:
2284 case CMD_pos:
2285 break;
2286 case CMD_t:
2287 if (!params.IsEmpty() && params.GetCount()<=4)
2288 ParseSSATag(&assTag.embeded, params[params.GetCount()-1]);
2289 break;
2290 case CMD_COUNT:
2291 nUnrecognizedTags++;
2292 break;
2295 assTag.cmdType = cmd_type;
2297 nTags++;
2299 return(true);
2302 bool CRenderedTextSubtitle::ParseSSATag( CSubtitle* sub, const AssTagList& assTags, STSStyle& style, const STSStyle& org, bool fAnimate /*= false*/ )
2304 if(!sub) return(false);
2306 POSITION pos = assTags.GetHeadPosition();
2307 while(pos)
2309 const AssTag& assTag = assTags.GetNext(pos);
2310 AssCmdType cmd_type = assTag.cmdType;
2311 const CAtlArray<CStringW>& params = assTag.strParams;
2313 // TODO: call ParseStyleModifier(cmd, params, ..) and move the rest there
2314 const CStringW& p = params.GetCount() > 0 ? params[0] : CStringW("");
2315 switch ( cmd_type )
2317 case CMD_1c :
2318 case CMD_2c :
2319 case CMD_3c :
2320 case CMD_4c :
2322 const int i = cmd_type==CMD_1c ? 0 :
2323 cmd_type==CMD_2c ? 1 :
2324 cmd_type==CMD_3c ? 2 :
2325 /*cmd_type==CMD_4c ?*/ 3;
2326 DWORD c = wcstol(p, NULL, 16);
2327 style.colors[i] = !p.IsEmpty()
2328 ? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
2329 |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
2330 |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
2331 : org.colors[i];
2332 break;
2334 case CMD_1a :
2335 case CMD_2a :
2336 case CMD_3a :
2337 case CMD_4a :
2339 const int i = cmd_type==CMD_1a ? 0 :
2340 cmd_type==CMD_2a ? 1 :
2341 cmd_type==CMD_3a ? 2 :
2342 /*cmd_type==CMD_4a ?*/ 3;
2343 style.alpha[i] = !p.IsEmpty()
2344 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2345 : org.alpha[i];
2346 break;
2348 case CMD_alpha:
2350 for(int i = 0; i < 4; i++)
2352 style.alpha[i] = !p.IsEmpty()
2353 ? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
2354 : org.alpha[i];
2356 break;
2358 case CMD_an:
2360 int n = wcstol(p, NULL, 10);
2361 if(sub->m_scrAlignment < 0)
2362 sub->m_scrAlignment = (n > 0 && n < 10) ? n : org.scrAlignment;
2363 break;
2365 case CMD_a:
2367 int n = wcstol(p, NULL, 10);
2368 if(sub->m_scrAlignment < 0)
2369 sub->m_scrAlignment = (n > 0 && n < 12) ? ((((n-1)&3)+1)+((n&4)?6:0)+((n&8)?3:0)) : org.scrAlignment;
2370 break;
2372 case CMD_blur:
2374 double n = CalcAnimation(wcstod(p, NULL), style.fGaussianBlur, fAnimate);
2375 style.fGaussianBlur = !p.IsEmpty()
2376 ? (n < 0 ? 0 : n)
2377 : org.fGaussianBlur;
2378 break;
2380 case CMD_bord:
2382 double dst = wcstod(p, NULL);
2383 double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate);
2384 style.outlineWidthX = !p.IsEmpty()
2385 ? (nx < 0 ? 0 : nx)
2386 : org.outlineWidthX;
2387 double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate);
2388 style.outlineWidthY = !p.IsEmpty()
2389 ? (ny < 0 ? 0 : ny)
2390 : org.outlineWidthY;
2391 break;
2393 case CMD_be:
2395 double d = CalcAnimation(wcstod(p, NULL), style.fBlur, fAnimate);
2396 style.fBlur = !p.IsEmpty()
2398 : org.fBlur;
2399 break;
2401 case CMD_b:
2403 int n = wcstol(p, NULL, 10);
2404 style.fontWeight = !p.IsEmpty()
2405 ? (n == 0 ? FW_NORMAL : n == 1 ? FW_BOLD : n >= 100 ? n : org.fontWeight)
2406 : org.fontWeight;
2407 break;
2409 case CMD_clip:
2410 case CMD_iclip:
2412 bool invert = (cmd_type == CMD_iclip);
2413 if(params.GetCount() == 1 && !sub->m_pClipper)
2415 sub->m_pClipper.reset( new CClipper(params[0], CSize(m_size_scale_to.cx>>3, m_size_scale_to.cy>>3), sub->m_scalex, sub->m_scaley, invert, m_target_scale_x, m_target_scale_y) );
2417 else if(params.GetCount() == 2 && !sub->m_pClipper)
2419 int scale = max(wcstol(p, NULL, 10), 1);
2420 sub->m_pClipper.reset( new CClipper(params[1], CSize(m_size_scale_to.cx>>3, m_size_scale_to.cy>>3), sub->m_scalex/(1<<(scale-1)), sub->m_scaley/(1<<(scale-1)), invert, m_target_scale_x, m_target_scale_y) );
2422 else if(params.GetCount() == 4)
2424 CRect r;
2425 sub->m_clipInverse = invert;
2426 r.SetRect(
2427 wcstod(params[0], NULL)+0.5,
2428 wcstod(params[1], NULL)+0.5,
2429 wcstod(params[2], NULL)+0.5,
2430 wcstod(params[3], NULL)+0.5);
2431 CPoint o(0, 0);
2432 if(sub->m_relativeTo == 1) // TODO: this should also apply to the other two clippings above
2434 o.x = m_vidrect.left>>3;
2435 o.y = m_vidrect.top>>3;
2437 sub->m_clip.SetRect(
2438 (int)CalcAnimation(sub->m_scalex*r.left + o.x, sub->m_clip.left, fAnimate),
2439 (int)CalcAnimation(sub->m_scaley*r.top + o.y, sub->m_clip.top, fAnimate),
2440 (int)CalcAnimation(sub->m_scalex*r.right + o.x, sub->m_clip.right, fAnimate),
2441 (int)CalcAnimation(sub->m_scaley*r.bottom + o.y, sub->m_clip.bottom, fAnimate));
2443 break;
2445 case CMD_c:
2447 DWORD c = wcstol(p, NULL, 16);
2448 style.colors[0] = !p.IsEmpty()
2449 ? (((int)CalcAnimation(c&0xff, style.colors[0]&0xff, fAnimate))&0xff
2450 |((int)CalcAnimation(c&0xff00, style.colors[0]&0xff00, fAnimate))&0xff00
2451 |((int)CalcAnimation(c&0xff0000, style.colors[0]&0xff0000, fAnimate))&0xff0000)
2452 : org.colors[0];
2453 break;
2455 case CMD_fade:
2456 case CMD_fad:
2458 sub->m_fAnimated2 = true;
2459 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])
2461 if(Effect* e = new Effect)
2463 for(int i = 0; i < 3; i++)
2464 e->param[i] = wcstol(params[i], NULL, 10);
2465 for(int i = 0; i < 4; i++)
2466 e->t[i] = wcstol(params[3+i], NULL, 10);
2467 sub->m_effects[EF_FADE] = e;
2470 else if(params.GetCount() == 2 && !sub->m_effects[EF_FADE]) // {\fad(t1=t[1], t2=t[2])
2472 if(Effect* e = new Effect)
2474 e->param[0] = e->param[2] = 0xff;
2475 e->param[1] = 0x00;
2476 for(int i = 1; i < 3; i++)
2477 e->t[i] = wcstol(params[i-1], NULL, 10);
2478 e->t[0] = e->t[3] = -1; // will be substituted with "start" and "end"
2479 sub->m_effects[EF_FADE] = e;
2482 break;
2484 case CMD_fax:
2486 style.fontShiftX = !p.IsEmpty()
2487 ? CalcAnimation(wcstod(p, NULL), style.fontShiftX, fAnimate)
2488 : org.fontShiftX;
2489 break;
2491 case CMD_fay:
2493 style.fontShiftY = !p.IsEmpty()
2494 ? CalcAnimation(wcstod(p, NULL), style.fontShiftY, fAnimate)
2495 : org.fontShiftY;
2496 break;
2498 case CMD_fe:
2500 int n = wcstol(p, NULL, 10);
2501 style.charSet = !p.IsEmpty()
2503 : org.charSet;
2504 break;
2506 case CMD_fn:
2508 if(!p.IsEmpty() && p != L'0')
2509 style.fontName = CString(p).Trim();
2510 else
2511 style.fontName = org.fontName;
2512 break;
2514 case CMD_frx:
2516 style.fontAngleX = !p.IsEmpty()
2517 ? CalcAnimation(wcstod(p, NULL), style.fontAngleX, fAnimate)
2518 : org.fontAngleX;
2519 break;
2521 case CMD_fry:
2523 style.fontAngleY = !p.IsEmpty()
2524 ? CalcAnimation(wcstod(p, NULL), style.fontAngleY, fAnimate)
2525 : org.fontAngleY;
2526 break;
2528 case CMD_frz:
2529 case CMD_fr:
2531 style.fontAngleZ = !p.IsEmpty()
2532 ? CalcAnimation(wcstod(p, NULL), style.fontAngleZ, fAnimate)
2533 : org.fontAngleZ;
2534 break;
2536 case CMD_fscx:
2538 double n = CalcAnimation(wcstod(p, NULL), style.fontScaleX, fAnimate);
2539 style.fontScaleX = !p.IsEmpty()
2540 ? ((n < 0) ? 0 : n)
2541 : org.fontScaleX;
2542 break;
2544 case CMD_fscy:
2546 double n = CalcAnimation(wcstod(p, NULL), style.fontScaleY, fAnimate);
2547 style.fontScaleY = !p.IsEmpty()
2548 ? ((n < 0) ? 0 : n)
2549 : org.fontScaleY;
2550 break;
2552 case CMD_fsc:
2554 style.fontScaleX = org.fontScaleX;
2555 style.fontScaleY = org.fontScaleY;
2556 break;
2558 case CMD_fsp:
2560 style.fontSpacing = !p.IsEmpty()
2561 ? CalcAnimation(wcstod(p, NULL), style.fontSpacing, fAnimate)
2562 : org.fontSpacing;
2563 break;
2565 case CMD_fs:
2567 if(!p.IsEmpty())
2569 if(p[0] == L'-' || p[0] == L'+')
2571 double n = CalcAnimation(style.fontSize + style.fontSize*wcstod(p, NULL)/10, style.fontSize, fAnimate);
2572 style.fontSize = (n > 0) ? n : org.fontSize;
2574 else
2576 double n = CalcAnimation(wcstod(p, NULL), style.fontSize, fAnimate);
2577 style.fontSize = (n > 0) ? n : org.fontSize;
2580 else
2582 style.fontSize = org.fontSize;
2584 break;
2586 case CMD_i:
2588 int n = wcstol(p, NULL, 10);
2589 style.fItalic = !p.IsEmpty()
2590 ? (n == 0 ? false : n == 1 ? true : org.fItalic)
2591 : org.fItalic;
2592 break;
2594 case CMD_kt:
2596 m_kstart = !p.IsEmpty()
2597 ? wcstod(p, NULL)*10
2598 : 0;
2599 m_kend = m_kstart;
2600 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2601 break;
2603 case CMD_kf:
2604 case CMD_K:
2606 m_ktype = 1;
2607 m_kstart = m_kend;
2608 m_kend += !p.IsEmpty()
2609 ? wcstod(p, NULL)*10
2610 : 1000;
2611 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2612 break;
2614 case CMD_ko:
2616 m_ktype = 2;
2617 m_kstart = m_kend;
2618 m_kend += !p.IsEmpty()
2619 ? wcstod(p, NULL)*10
2620 : 1000;
2621 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2622 break;
2624 case CMD_k:
2626 m_ktype = 0;
2627 m_kstart = m_kend;
2628 m_kend += !p.IsEmpty()
2629 ? wcstod(p, NULL)*10
2630 : 1000;
2631 sub->m_fAnimated2 = true;//fix me: define m_fAnimated m_fAnimated2 strictly
2632 break;
2634 case CMD_move: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3][, t1=t[0], t2=t[1]])}
2636 if((params.GetCount() == 4 || params.GetCount() == 6) && !sub->m_effects[EF_MOVE])
2638 if(Effect* e = new Effect)
2640 e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2641 e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2642 e->param[2] = (int)(sub->m_scalex*wcstod(params[2], NULL)*8);
2643 e->param[3] = (int)(sub->m_scaley*wcstod(params[3], NULL)*8);
2644 e->t[0] = e->t[1] = -1;
2645 if(params.GetCount() == 6)
2647 for(int i = 0; i < 2; i++)
2648 e->t[i] = wcstol(params[4+i], NULL, 10);
2650 sub->m_effects[EF_MOVE] = e;
2651 sub->m_fAnimated2 = true;
2654 break;
2656 case CMD_org: // {\org(x=param[0], y=param[1])}
2658 if(params.GetCount() == 2 && !sub->m_effects[EF_ORG])
2660 if(Effect* e = new Effect)
2662 e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2663 e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2664 sub->m_effects[EF_ORG] = e;
2667 break;
2669 case CMD_pbo:
2671 m_polygonBaselineOffset = wcstol(p, NULL, 10);
2672 break;
2674 case CMD_pos:
2676 if(params.GetCount() == 2 && !sub->m_effects[EF_MOVE])
2678 if(Effect* e = new Effect)
2680 e->param[0] = e->param[2] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
2681 e->param[1] = e->param[3] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
2682 e->t[0] = e->t[1] = 0;
2683 sub->m_effects[EF_MOVE] = e;
2686 break;
2688 case CMD_p:
2690 int n = wcstol(p, NULL, 10);
2691 m_nPolygon = (n <= 0 ? 0 : n);
2692 break;
2694 case CMD_q:
2696 int n = wcstol(p, NULL, 10);
2697 sub->m_wrapStyle = !p.IsEmpty() && (0 <= n && n <= 3)
2699 : m_defaultWrapStyle;
2700 break;
2702 case CMD_r:
2704 STSStyle* val;
2705 style = (!p.IsEmpty() && m_styles.Lookup(WToT(p), val) && val) ? *val : org;
2706 break;
2708 case CMD_shad:
2710 double dst = wcstod(p, NULL);
2711 double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate);
2712 style.shadowDepthX = !p.IsEmpty()
2713 ? (nx < 0 ? 0 : nx)
2714 : org.shadowDepthX;
2715 double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate);
2716 style.shadowDepthY = !p.IsEmpty()
2717 ? (ny < 0 ? 0 : ny)
2718 : org.shadowDepthY;
2719 break;
2721 case CMD_s:
2723 int n = wcstol(p, NULL, 10);
2724 style.fStrikeOut = !p.IsEmpty()
2725 ? (n == 0 ? false : n == 1 ? true : org.fStrikeOut)
2726 : org.fStrikeOut;
2727 break;
2729 case CMD_t: // \t([<t1>,<t2>,][<accel>,]<style modifiers>)
2731 CStringW param;
2732 m_animStart = m_animEnd = 0;
2733 m_animAccel = 1;
2734 if(params.GetCount() == 1)
2736 param = params[0];
2738 else if(params.GetCount() == 2)
2740 m_animAccel = wcstod(params[0], NULL);
2741 param = params[1];
2743 else if(params.GetCount() == 3)
2745 m_animStart = (int)wcstod(params[0], NULL);
2746 m_animEnd = (int)wcstod(params[1], NULL);
2747 param = params[2];
2749 else if(params.GetCount() == 4)
2751 m_animStart = wcstol(params[0], NULL, 10);
2752 m_animEnd = wcstol(params[1], NULL, 10);
2753 m_animAccel = wcstod(params[2], NULL);
2754 param = params[3];
2756 ParseSSATag(sub, assTag.embeded, style, org, true);
2757 sub->m_fAnimated = true;
2758 sub->m_fAnimated2 = true;
2759 break;
2761 case CMD_u:
2763 int n = wcstol(p, NULL, 10);
2764 style.fUnderline = !p.IsEmpty()
2765 ? (n == 0 ? false : n == 1 ? true : org.fUnderline)
2766 : org.fUnderline;
2767 break;
2769 case CMD_xbord:
2771 double dst = wcstod(p, NULL);
2772 double nx = CalcAnimation(dst, style.outlineWidthX, fAnimate);
2773 style.outlineWidthX = !p.IsEmpty()
2774 ? (nx < 0 ? 0 : nx)
2775 : org.outlineWidthX;
2776 break;
2778 case CMD_xshad:
2780 double dst = wcstod(p, NULL);
2781 double nx = CalcAnimation(dst, style.shadowDepthX, fAnimate);
2782 style.shadowDepthX = !p.IsEmpty()
2783 ? nx
2784 : org.shadowDepthX;
2785 break;
2787 case CMD_ybord:
2789 double dst = wcstod(p, NULL);
2790 double ny = CalcAnimation(dst, style.outlineWidthY, fAnimate);
2791 style.outlineWidthY = !p.IsEmpty()
2792 ? (ny < 0 ? 0 : ny)
2793 : org.outlineWidthY;
2794 break;
2796 case CMD_yshad:
2798 double dst = wcstod(p, NULL);
2799 double ny = CalcAnimation(dst, style.shadowDepthY, fAnimate);
2800 style.shadowDepthY = !p.IsEmpty()
2801 ? ny
2802 : org.shadowDepthY;
2803 break;
2805 default:
2806 break;
2809 return(true);
2812 bool CRenderedTextSubtitle::ParseSSATag(CSubtitle* sub, const CStringW& str, STSStyle& style, const STSStyle& org, bool fAnimate)
2814 if(!sub) return(false);
2816 SharedPtrConstAssTagList assTags;
2817 AssTagListMruCache *ass_tag_cache = CacheManager::GetAssTagListMruCache();
2818 POSITION pos = ass_tag_cache->Lookup(str);
2819 if (pos==NULL)
2821 AssTagList *tmp = new AssTagList();
2822 ParseSSATag(tmp, str);
2823 assTags.reset(tmp);
2824 ass_tag_cache->UpdateCache(str, assTags);
2826 else
2828 assTags = ass_tag_cache->GetAt(pos);
2829 ass_tag_cache->UpdateCache( pos );
2831 return ParseSSATag(sub, *assTags, style, org, fAnimate);
2834 bool CRenderedTextSubtitle::ParseHtmlTag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org)
2836 if(str.Find(L"!--") == 0)
2837 return(true);
2838 bool fClosing = str[0] == L'/';
2839 str.Trim(L" /");
2840 int i = str.Find(L' ');
2841 if(i < 0) i = str.GetLength();
2842 CStringW tag = str.Left(i).MakeLower();
2843 str = str.Mid(i).Trim();
2844 CAtlArray<CStringW> attribs, params;
2845 while((i = str.Find(L'=')) > 0)
2847 attribs.Add(str.Left(i).Trim().MakeLower());
2848 str = str.Mid(i+1);
2849 for(i = 0; _istspace(str[i]); i++);
2850 str = str.Mid(i);
2851 if(str[0] == L'\"') {str = str.Mid(1); i = str.Find(L'\"');}
2852 else i = str.Find(L' ');
2853 if(i < 0) i = str.GetLength();
2854 params.Add(str.Left(i).Trim().MakeLower());
2855 str = str.Mid(i+1);
2857 if(tag == L"text")
2859 else if(tag == L"b" || tag == L"strong")
2860 style.fontWeight = !fClosing ? FW_BOLD : org.fontWeight;
2861 else if(tag == L"i" || tag == L"em")
2862 style.fItalic = !fClosing ? true : org.fItalic;
2863 else if(tag == L"u")
2864 style.fUnderline = !fClosing ? true : org.fUnderline;
2865 else if(tag == L"s" || tag == L"strike" || tag == L"del")
2866 style.fStrikeOut = !fClosing ? true : org.fStrikeOut;
2867 else if(tag == L"font")
2869 if(!fClosing)
2871 for(size_t i = 0; i < attribs.GetCount(); i++)
2873 if(params[i].IsEmpty()) continue;
2874 int nColor = -1;
2875 if(attribs[i] == L"face")
2877 style.fontName = params[i];
2879 else if(attribs[i] == L"size")
2881 if(params[i][0] == L'+')
2882 style.fontSize += wcstol(params[i], NULL, 10);
2883 else if(params[i][0] == L'-')
2884 style.fontSize -= wcstol(params[i], NULL, 10);
2885 else
2886 style.fontSize = wcstol(params[i], NULL, 10);
2888 else if(attribs[i] == L"color")
2890 nColor = 0;
2892 else if(attribs[i] == L"outline-color")
2894 nColor = 2;
2896 else if(attribs[i] == L"outline-level")
2898 style.outlineWidthX = style.outlineWidthY = wcstol(params[i], NULL, 10);
2900 else if(attribs[i] == L"shadow-color")
2902 nColor = 3;
2904 else if(attribs[i] == L"shadow-level")
2906 style.shadowDepthX = style.shadowDepthY = wcstol(params[i], NULL, 10);
2908 if(nColor >= 0 && nColor < 4)
2910 CString key = WToT(params[i]).TrimLeft(L'#');
2911 DWORD val;
2912 if(g_colors.Lookup(key, val))
2913 style.colors[nColor] = val;
2914 else if((style.colors[nColor] = _tcstol(key, NULL, 16)) == 0)
2915 style.colors[nColor] = 0x00ffffff; // default is white
2916 style.colors[nColor] = ((style.colors[nColor]>>16)&0xff)|((style.colors[nColor]&0xff)<<16)|(style.colors[nColor]&0x00ff00);
2920 else
2922 style.fontName = org.fontName;
2923 style.fontSize = org.fontSize;
2924 memcpy(style.colors, org.colors, sizeof(style.colors));
2927 else if(tag == L"k" && attribs.GetCount() == 1 && attribs[0] == L"t")
2929 m_ktype = 1;
2930 m_kstart = m_kend;
2931 m_kend += wcstol(params[0], NULL, 10);
2933 else
2934 return(false);
2935 return(true);
2938 double CRenderedTextSubtitle::CalcAnimation(double dst, double src, bool fAnimate)
2940 int s = m_animStart ? m_animStart : 0;
2941 int e = m_animEnd ? m_animEnd : m_delay;
2942 if(fabs(dst-src) >= 0.0001 && fAnimate)
2944 if(m_time < s) dst = src;
2945 else if(s <= m_time && m_time < e)
2947 double t = pow(1.0 * (m_time - s) / (e - s), m_animAccel);
2948 dst = (1 - t) * src + t * dst;
2950 // else dst = dst;
2952 return(dst);
2955 CSubtitle* CRenderedTextSubtitle::GetSubtitle(int entry)
2957 CSubtitle* sub;
2958 if(m_subtitleCache.Lookup(entry, sub))
2960 if(sub->m_fAnimated) {delete sub; sub = NULL;}
2961 else return(sub);
2963 sub = new CSubtitle();
2964 if(!sub) return(NULL);
2965 CStringW str = GetStrW(entry, true);
2966 STSStyle stss, orgstss;
2967 GetStyle(entry, &stss);
2968 if (stss.fontScaleX == stss.fontScaleY && m_dPARCompensation != 1.0)
2970 switch(m_ePARCompensationType)
2972 case EPCTUpscale:
2973 if (m_dPARCompensation < 1.0)
2974 stss.fontScaleY /= m_dPARCompensation;
2975 else
2976 stss.fontScaleX *= m_dPARCompensation;
2977 break;
2978 case EPCTDownscale:
2979 if (m_dPARCompensation < 1.0)
2980 stss.fontScaleX *= m_dPARCompensation;
2981 else
2982 stss.fontScaleY /= m_dPARCompensation;
2983 break;
2984 case EPCTAccurateSize:
2985 stss.fontScaleX *= m_dPARCompensation;
2986 break;
2989 orgstss = stss;
2990 sub->m_clip.SetRect(0, 0, m_size.cx>>3, m_size.cy>>3);
2991 sub->m_scrAlignment = -stss.scrAlignment;
2992 sub->m_wrapStyle = m_defaultWrapStyle;
2993 sub->m_fAnimated = false;
2994 sub->m_relativeTo = stss.relativeTo;
2995 sub->m_scalex = m_dstScreenSize.cx > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Width() : m_size.cx) / (m_dstScreenSize.cx*8) : 1.0;
2996 sub->m_scaley = m_dstScreenSize.cy > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Height() : m_size.cy) / (m_dstScreenSize.cy*8) : 1.0;
2998 sub->m_target_scale_x = m_target_scale_x;
2999 sub->m_target_scale_y = m_target_scale_y;
3001 m_animStart = m_animEnd = 0;
3002 m_animAccel = 1;
3003 m_ktype = m_kstart = m_kend = 0;
3004 m_nPolygon = 0;
3005 m_polygonBaselineOffset = 0;
3006 ParseEffect(sub, m_entries.GetAt(entry).effect);
3007 while(!str.IsEmpty())
3009 bool fParsed = false;
3010 int i;
3011 if(str[0] == L'{' && (i = str.Find(L'}')) > 0)
3013 fParsed = ParseSSATag(sub, str.Mid(1, i-1), stss, orgstss);
3014 if(fParsed)
3015 str = str.Mid(i+1);
3017 else if(str[0] == L'<' && (i = str.Find(L'>')) > 0)
3019 fParsed = ParseHtmlTag(sub, str.Mid(1, i-1), stss, orgstss);
3020 if(fParsed)
3021 str = str.Mid(i+1);
3023 if(fParsed)
3025 i = str.FindOneOf(L"{<");
3026 if(i < 0) i = str.GetLength();
3027 if(i == 0) continue;
3029 else
3031 i = str.Mid(1).FindOneOf(L"{<");
3032 if(i < 0) i = str.GetLength()-1;
3033 i++;
3035 STSStyle tmp = stss;
3036 tmp.fontSize = sub->m_scaley*tmp.fontSize*64;
3037 tmp.fontSpacing = sub->m_scalex*tmp.fontSpacing*64;
3038 tmp.outlineWidthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8;
3039 tmp.outlineWidthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8;
3040 tmp.shadowDepthX *= (m_fScaledBAS ? sub->m_scalex : 1) * 8;
3041 tmp.shadowDepthY *= (m_fScaledBAS ? sub->m_scaley : 1) * 8;
3042 FwSTSStyle fw_tmp(tmp);
3043 if(m_nPolygon)
3045 ParsePolygon(sub, str.Left(i), fw_tmp);
3047 else
3049 ParseString(sub, str.Left(i), fw_tmp);
3051 str = str.Mid(i);
3053 if( sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL] )
3054 sub->m_fAnimated2 = true;
3055 // just a "work-around" solution... in most cases nobody will want to use \org together with moving but without rotating the subs
3056 if(sub->m_effects[EF_ORG] && (sub->m_effects[EF_MOVE] || sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL]))
3057 sub->m_fAnimated = true;
3058 sub->m_scrAlignment = abs(sub->m_scrAlignment);
3059 STSEntry stse = m_entries.GetAt(entry);
3060 CRect marginRect = stse.marginRect;
3061 if(marginRect.left == 0) marginRect.left = orgstss.marginRect.get().left;
3062 if(marginRect.top == 0) marginRect.top = orgstss.marginRect.get().top;
3063 if(marginRect.right == 0) marginRect.right = orgstss.marginRect.get().right;
3064 if(marginRect.bottom == 0) marginRect.bottom = orgstss.marginRect.get().bottom;
3065 marginRect.left = (int)(sub->m_scalex*marginRect.left*8);
3066 marginRect.top = (int)(sub->m_scaley*marginRect.top*8);
3067 marginRect.right = (int)(sub->m_scalex*marginRect.right*8);
3068 marginRect.bottom = (int)(sub->m_scaley*marginRect.bottom*8);
3069 if(stss.relativeTo == 1)
3071 marginRect.left += m_vidrect.left;
3072 marginRect.top += m_vidrect.top;
3073 marginRect.right += m_size.cx - m_vidrect.right;
3074 marginRect.bottom += m_size.cy - m_vidrect.bottom;
3076 sub->CreateClippers(m_size, m_size_scale_to);
3077 sub->MakeLines(m_size, marginRect);
3078 m_subtitleCache[entry] = sub;
3079 return(sub);
3084 STDMETHODIMP CRenderedTextSubtitle::NonDelegatingQueryInterface(REFIID riid, void** ppv)
3086 CheckPointer(ppv, E_POINTER);
3087 *ppv = NULL;
3088 return
3089 QI(IPersist)
3090 QI(ISubStream)
3091 QI(ISubPicProviderEx2)
3092 QI(ISubPicProvider)
3093 QI(ISubPicProviderEx)
3094 __super::NonDelegatingQueryInterface(riid, ppv);
3097 // ISubPicProvider
3099 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetStartPosition(REFERENCE_TIME rt, double fps)
3101 m_fps = fps;
3102 if (m_fps>0)
3104 m_period = 1000/m_fps;
3105 if(m_period<=0)
3107 m_period = 1;
3110 else
3112 //Todo: fix me. max has been defined as a macro. Use #define NOMINMAX to fix it.
3113 //std::numeric_limits<int>::max();
3114 m_period = INT_MAX;
3117 int iSegment;
3118 int subIndex = 1;//If a segment has animate effect then it corresponds to several subpics.
3119 //subIndex, 1 based, indicates which subpic the result corresponds to.
3120 rt /= 10000i64;
3121 const STSSegment *stss = SearchSubs((int)rt, fps, &iSegment, NULL);
3122 if(stss==NULL)
3123 return NULL;
3124 else if(stss->animated)
3126 int start = TranslateSegmentStart(iSegment, fps);
3127 if(rt > start)
3128 subIndex = (rt-start)/m_period + 1;
3130 //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));
3131 return (POSITION)(subIndex | (iSegment<<RTS_POS_SEGMENT_INDEX_BITS));
3132 //if(iSegment < 0) iSegment = 0;
3133 //return(GetNext((POSITION)iSegment));
3136 STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetNext(POSITION pos)
3138 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3139 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3140 const STSSegment *stss = GetSegment(iSegment);
3141 ASSERT(stss!=NULL && stss->subs.GetCount()>0);
3142 //DbgLog((LOG_TRACE, 3, "stss:%x count:%d", stss, stss->subs.GetCount()));
3143 if(!stss->animated)
3145 iSegment++;
3146 subIndex = 1;
3148 else
3150 int start, end;
3151 TranslateSegmentStartEnd(iSegment, m_fps, start, end);
3152 if(start+m_period*subIndex < end)
3153 subIndex++;
3154 else
3156 iSegment++;
3157 subIndex = 1;
3160 if(GetSegment(iSegment) != NULL)
3162 ASSERT(GetSegment(iSegment)->subs.GetCount()>0);
3163 return (POSITION)(subIndex | (iSegment<<RTS_POS_SEGMENT_INDEX_BITS));
3165 else
3166 return NULL;
3169 //@return: <0 if segment not found
3170 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStart(POSITION pos, double fps)
3172 //return(10000i64 * TranslateSegmentStart((int)pos-1, fps));
3173 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3174 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3175 int start = TranslateSegmentStart(iSegment, fps);
3176 const STSSegment *stss = GetSegment(iSegment);
3177 if(stss!=NULL)
3179 return (start + (subIndex-1)*m_period)*10000i64;
3181 else
3183 return -1;
3187 //@return: <0 if segment not found
3188 STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStop(POSITION pos, double fps)
3190 // return(10000i64 * TranslateSegmentEnd((int)pos-1, fps));
3191 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3192 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3193 int start, end, ret;
3194 TranslateSegmentStartEnd(iSegment, fps, start, end);
3195 const STSSegment *stss = GetSegment(iSegment);
3196 if(stss!=NULL)
3198 if(!stss->animated)
3199 ret = end;
3200 else
3202 ret = start+subIndex*m_period;
3203 if(ret > end)
3204 ret = end;
3206 return ret*10000i64;
3208 else
3209 return -1;
3212 //@start, @stop: -1 if segment not found; @stop may < @start if subIndex exceed uppper bound
3213 STDMETHODIMP_(VOID) CRenderedTextSubtitle::GetStartStop(POSITION pos, double fps, /*out*/REFERENCE_TIME &start, /*out*/REFERENCE_TIME &stop)
3215 int iSegment = ((int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3216 int subIndex = ((int)pos & RTS_POS_SUB_INDEX_MASK);
3217 int tempStart, tempEnd;
3218 TranslateSegmentStartEnd(iSegment, fps, tempStart, tempEnd);
3219 start = tempStart;
3220 stop = tempEnd;
3221 const STSSegment *stss = GetSegment(iSegment);
3222 if(stss!=NULL)
3224 if(stss->animated)
3226 start += (subIndex-1)*m_period;
3227 if(start+m_period < stop)
3228 stop = start+m_period;
3230 //DbgLog((LOG_TRACE, 3, "animated:%d seg:%d idx:%d start:%d stop:%lu", stss->animated, iSegment, subIndex, (ULONG)start, (ULONG)stop));
3231 start *= 10000i64;
3232 stop *= 10000i64;
3234 else
3236 start = -1;
3237 stop = -1;
3241 STDMETHODIMP_(bool) CRenderedTextSubtitle::IsAnimated(POSITION pos)
3243 unsigned int iSegment = ((unsigned int)pos>>RTS_POS_SEGMENT_INDEX_BITS);
3244 if(iSegment<m_segments.GetCount())
3245 return m_segments[iSegment].animated;
3246 else
3247 return false;
3248 //return(true);
3251 struct LSub {int idx, layer, readorder;};
3253 static int lscomp(const void* ls1, const void* ls2)
3255 int ret = ((LSub*)ls1)->layer - ((LSub*)ls2)->layer;
3256 if(!ret) ret = ((LSub*)ls1)->readorder - ((LSub*)ls2)->readorder;
3257 return(ret);
3260 HRESULT CRenderedTextSubtitle::ParseScript(REFERENCE_TIME rt, double fps, CSubtitle2List *outputSub2List )
3262 //fix me: check input and log error
3263 int t = (int)(rt / 10000);
3264 int segment;
3265 //const
3266 STSSegment* stss = SearchSubs2(t, fps, &segment);
3267 if(!stss) return S_FALSE;
3268 // clear any cached subs not in the range of +/-30secs measured from the segment's bounds
3270 POSITION pos = m_subtitleCache.GetStartPosition();
3271 while(pos)
3273 int key;
3274 CSubtitle* value;
3275 m_subtitleCache.GetNextAssoc(pos, key, value);
3276 STSEntry& stse = m_entries.GetAt(key);
3277 if(stse.end <= (t-30000) || stse.start > (t+30000))
3279 delete value;
3280 m_subtitleCache.RemoveKey(key);
3281 pos = m_subtitleCache.GetStartPosition();
3285 m_sla.AdvanceToSegment(segment, stss->subs);
3286 CAtlArray<LSub> subs;
3287 for(int i = 0, j = stss->subs.GetCount(); i < j; i++)
3289 LSub ls;
3290 ls.idx = stss->subs[i];
3291 ls.layer = m_entries.GetAt(stss->subs[i]).layer;
3292 ls.readorder = m_entries.GetAt(stss->subs[i]).readorder;
3293 subs.Add(ls);
3295 qsort(subs.GetData(), subs.GetCount(), sizeof(LSub), lscomp);
3297 for(int i = 0, j = subs.GetCount(); i < j; i++)
3299 int entry = subs[i].idx;
3300 STSEntry stse = m_entries.GetAt(entry);
3302 int start = TranslateStart(entry, fps);
3303 m_time = t - start;
3304 m_delay = TranslateEnd(entry, fps) - start;
3306 CSubtitle* s = GetSubtitle(entry);
3307 if(!s) continue;
3308 stss->animated |= s->m_fAnimated2;
3309 CRect clipRect = s->m_clip & CRect(0,0, (m_size.cx>>3), (m_size.cy>>3));
3310 CRect r = s->m_rect;
3311 CSize spaceNeeded = r.Size();
3312 // apply the effects
3313 bool fPosOverride = false, fOrgOverride = false;
3314 int alpha = 0x00;
3315 CPoint org2;
3316 for(int k = 0; k < EF_NUMBEROFEFFECTS; k++)
3318 if(!s->m_effects[k]) continue;
3319 switch(k)
3321 case EF_MOVE: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3], t1=t[0], t2=t[1])}
3323 CPoint p;
3324 CPoint p1(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
3325 CPoint p2(s->m_effects[k]->param[2], s->m_effects[k]->param[3]);
3326 int t1 = s->m_effects[k]->t[0];
3327 int t2 = s->m_effects[k]->t[1];
3328 if(t2 < t1) {int t = t1; t1 = t2; t2 = t;}
3329 if(t1 <= 0 && t2 <= 0) {t1 = 0; t2 = m_delay;}
3330 if(m_time <= t1) p = p1;
3331 else if (p1 == p2) p = p1;
3332 else if(t1 < m_time && m_time < t2)
3334 double t = 1.0*(m_time-t1)/(t2-t1);
3335 p.x = (int)((1-t)*p1.x + t*p2.x);
3336 p.y = (int)((1-t)*p1.y + t*p2.y);
3338 else p = p2;
3339 r = CRect(
3340 CPoint((s->m_scrAlignment%3) == 1 ? p.x : (s->m_scrAlignment%3) == 0 ? p.x - spaceNeeded.cx : p.x - (spaceNeeded.cx+1)/2,
3341 s->m_scrAlignment <= 3 ? p.y - spaceNeeded.cy : s->m_scrAlignment <= 6 ? p.y - (spaceNeeded.cy+1)/2 : p.y),
3342 spaceNeeded);
3343 if(s->m_relativeTo == 1)
3344 r.OffsetRect(m_vidrect.TopLeft());
3345 fPosOverride = true;
3347 break;
3348 case EF_ORG: // {\org(x=param[0], y=param[1])}
3350 org2 = CPoint(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
3351 fOrgOverride = true;
3353 break;
3354 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])
3356 int t1 = s->m_effects[k]->t[0];
3357 int t2 = s->m_effects[k]->t[1];
3358 int t3 = s->m_effects[k]->t[2];
3359 int t4 = s->m_effects[k]->t[3];
3360 if(t1 == -1 && t4 == -1) {t1 = 0; t3 = m_delay-t3; t4 = m_delay;}
3361 if(m_time < t1) alpha = s->m_effects[k]->param[0];
3362 else if(m_time >= t1 && m_time < t2)
3364 double t = 1.0 * (m_time - t1) / (t2 - t1);
3365 alpha = (int)(s->m_effects[k]->param[0]*(1-t) + s->m_effects[k]->param[1]*t);
3367 else if(m_time >= t2 && m_time < t3) alpha = s->m_effects[k]->param[1];
3368 else if(m_time >= t3 && m_time < t4)
3370 double t = 1.0 * (m_time - t3) / (t4 - t3);
3371 alpha = (int)(s->m_effects[k]->param[1]*(1-t) + s->m_effects[k]->param[2]*t);
3373 else if(m_time >= t4) alpha = s->m_effects[k]->param[2];
3375 break;
3376 case EF_BANNER: // Banner;delay=param[0][;leftoright=param[1];fadeawaywidth=param[2]]
3378 int left = s->m_relativeTo == 1 ? m_vidrect.left : 0,
3379 right = s->m_relativeTo == 1 ? m_vidrect.right : m_size.cx;
3380 r.left = !!s->m_effects[k]->param[1]
3381 ? (left/*marginRect.left*/ - spaceNeeded.cx) + (int)(m_time*8.0/s->m_effects[k]->param[0])
3382 : (right /*- marginRect.right*/) - (int)(m_time*8.0/s->m_effects[k]->param[0]);
3383 r.right = r.left + spaceNeeded.cx;
3384 clipRect &= CRect(left>>3, clipRect.top, right>>3, clipRect.bottom);
3385 fPosOverride = true;
3387 break;
3388 case EF_SCROLL: // Scroll up/down(toptobottom=param[3]);top=param[0];bottom=param[1];delay=param[2][;fadeawayheight=param[4]]
3390 r.top = !!s->m_effects[k]->param[3]
3391 ? s->m_effects[k]->param[0] + (int)(m_time*8.0/s->m_effects[k]->param[2]) - spaceNeeded.cy
3392 : s->m_effects[k]->param[1] - (int)(m_time*8.0/s->m_effects[k]->param[2]);
3393 r.bottom = r.top + spaceNeeded.cy;
3394 CRect cr(0, (s->m_effects[k]->param[0] + 4) >> 3, (m_size.cx>>3), (s->m_effects[k]->param[1] + 4) >> 3);
3395 if(s->m_relativeTo == 1)
3396 r.top += m_vidrect.top,
3397 r.bottom += m_vidrect.top,
3398 cr.top += m_vidrect.top>>3,
3399 cr.bottom += m_vidrect.top>>3;
3400 clipRect &= cr;
3401 fPosOverride = true;
3403 break;
3404 default:
3405 break;
3408 if(!fPosOverride && !fOrgOverride && !s->m_fAnimated)
3409 r = m_sla.AllocRect(s, segment, entry, stse.layer, m_collisions);
3410 CPoint org;
3411 org.x = (s->m_scrAlignment%3) == 1 ? r.left : (s->m_scrAlignment%3) == 2 ? r.CenterPoint().x : r.right;
3412 org.y = s->m_scrAlignment <= 3 ? r.bottom : s->m_scrAlignment <= 6 ? r.CenterPoint().y : r.top;
3413 if(!fOrgOverride) org2 = org;
3414 CPoint p2(0, r.top);
3415 // Rectangles for inverse clip
3417 CRectCoor2 clipRect_coor2;
3418 clipRect_coor2.left = clipRect.left * m_target_scale_x;
3419 clipRect_coor2.right = clipRect.right * m_target_scale_x;
3420 clipRect_coor2.top = clipRect.top * m_target_scale_y;
3421 clipRect_coor2.bottom = clipRect.bottom * m_target_scale_y;
3422 CSubtitle2& sub2 = outputSub2List->GetAt(outputSub2List->AddTail( CSubtitle2(s, clipRect_coor2, org, org2, p2, alpha, m_time) ));
3425 return (subs.GetCount()) ? S_OK : S_FALSE;
3428 STDMETHODIMP CRenderedTextSubtitle::RenderEx(SubPicDesc& spd, REFERENCE_TIME rt, double fps, CAtlList<CRectCoor2>& rectList)
3430 CSize output_size = CSize(spd.w,spd.h);
3431 rectList.RemoveAll();
3433 CComPtr<IXySubRenderFrame> sub_render_frame;
3434 HRESULT hr = RenderEx(&sub_render_frame, spd.type, output_size, output_size, spd.vidrect, rt, fps);
3435 if (SUCCEEDED(hr) && sub_render_frame)
3437 int count = 0;
3438 hr = sub_render_frame->GetBitmapCount(&count);
3439 if(FAILED(hr))
3441 return hr;
3443 int color_space;
3444 hr = sub_render_frame->GetXyColorSpace(&color_space);
3445 if(FAILED(hr))
3447 return hr;
3449 for (int i=0;i<count;i++)
3451 POINT pos;
3452 SIZE size;
3453 LPCVOID pixels;
3454 int pitch;
3455 hr = sub_render_frame->GetBitmap(i, NULL, &pos, &size, &pixels, &pitch );
3456 if(FAILED(hr))
3458 return hr;
3460 rectList.AddTail(CRect(pos, size));
3461 if (color_space==XY_CS_AYUV_PLANAR)
3463 XyPlannerFormatExtra plans;
3464 hr = sub_render_frame->GetBitmapExtra(i, &plans);
3465 if(FAILED(hr))
3467 return hr;
3469 XyBitmap::AlphaBltPlannar(spd, pos, size, plans, pitch);
3471 else
3473 XyBitmap::AlphaBltPack(spd, pos, size, pixels, pitch);
3477 return (!rectList.IsEmpty()) ? S_OK : S_FALSE;
3480 STDMETHODIMP CRenderedTextSubtitle::RenderEx( IXySubRenderFrame**subRenderFrame, int spd_type,
3481 const SIZECoor2& size_scale_to, const SIZE& size1, const CRect& video_rect,
3482 REFERENCE_TIME rt, double fps )
3484 if (!subRenderFrame)
3486 return S_FALSE;
3489 XyColorSpace color_space = XY_CS_ARGB;
3490 switch(spd_type)
3492 case MSP_AYUV_PLANAR:
3493 color_space = XY_CS_AYUV_PLANAR;
3494 break;
3495 case MSP_XY_AUYV:
3496 color_space = XY_CS_AUYV;
3497 break;
3498 case MSP_AYUV:
3499 color_space = XY_CS_AYUV;
3500 break;
3501 default:
3502 color_space = XY_CS_ARGB;
3503 break;
3506 XySubRenderFrameCreater *render_frame_creater = XySubRenderFrameCreater::GetDefaultCreater();
3507 render_frame_creater->SetColorSpace(color_space);
3509 if( m_size_scale_to != CSize(size_scale_to.cx*8, size_scale_to.cy*8)
3510 || m_size != CSize(size1.cx*8, size1.cy*8)
3511 || m_vidrect != CRect(video_rect.left*8, video_rect.top*8, video_rect.right*8, video_rect.bottom*8))
3513 Init(size_scale_to, size1, video_rect);
3514 render_frame_creater->SetOutputRect(CRect(0, 0, size_scale_to.cx, size_scale_to.cy));
3515 render_frame_creater->SetClipRect(CRect(0, 0, size_scale_to.cx, size_scale_to.cy));
3518 CSubtitle2List sub2List;
3519 HRESULT hr = ParseScript(rt, fps, &sub2List);
3520 if(hr!=S_OK)
3522 return hr;
3525 CompositeDrawItemListList compDrawItemListList;
3526 DoRender(size_scale_to, sub2List, &compDrawItemListList);
3528 XySubRenderFrame *sub_render_frame;
3529 CompositeDrawItem::Draw(&sub_render_frame, compDrawItemListList);
3530 (*subRenderFrame = sub_render_frame)->AddRef();
3532 return hr;
3535 void CRenderedTextSubtitle::DoRender( const SIZECoor2& output_size, const CSubtitle2List& sub2List,
3536 CompositeDrawItemListList *compDrawItemListList /*output*/)
3538 //check input and log error
3539 POSITION pos=sub2List.GetHeadPosition();
3540 while ( pos!=NULL )
3542 const CSubtitle2& sub2 = sub2List.GetNext(pos);
3543 CompositeDrawItemList& compDrawItemList = compDrawItemListList->GetAt(compDrawItemListList->AddTail());
3544 RenderOneSubtitle(output_size, sub2, &compDrawItemList);
3548 void CRenderedTextSubtitle::RenderOneSubtitle( const SIZECoor2& output_size, const CSubtitle2& sub2,
3549 CompositeDrawItemList* compDrawItemList /*output*/)
3551 CSubtitle* s = sub2.s;
3552 const CRect& clipRect = sub2.clipRect;
3553 const CPoint& org = sub2.org;
3554 const CPoint& org2 = sub2.org2;
3555 const CPoint& p2 = sub2.p;
3556 int alpha = sub2.alpha;
3557 int time = sub2.time;
3558 if(!s) return;
3560 SharedPtrCClipperPaintMachine clipper( new CClipperPaintMachine(s->m_pClipper) );
3562 CRect iclipRect[4];
3563 iclipRect[0] = CRect(0, 0, output_size.cx, clipRect.top);
3564 iclipRect[1] = CRect(0, clipRect.top, clipRect.left, clipRect.bottom);
3565 iclipRect[2] = CRect(clipRect.right, clipRect.top, output_size.cx, clipRect.bottom);
3566 iclipRect[3] = CRect(0, clipRect.bottom, output_size.cx, output_size.cy);
3567 CRect bbox2(0,0,0,0);
3568 POSITION pos = s->GetHeadLinePosition();
3569 CPoint p = p2;
3570 while(pos)
3572 CLine* l = s->GetNextLine(pos);
3573 p.x = (s->m_scrAlignment%3) == 1 ? org.x
3574 : (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
3575 : org.x - (l->m_width/2);
3577 CompositeDrawItemList tmpCompDrawItemList;
3578 if (s->m_clipInverse)
3580 CompositeDrawItemList tmp1,tmp2,tmp3,tmp4;
3581 for (int i=0;i<l->GetWordCount();i++)
3583 tmp1.AddTail();
3584 tmp2.AddTail();
3585 tmp3.AddTail();
3586 tmp4.AddTail();
3588 bbox2 |= l->PaintAll(&tmp1, iclipRect[0], clipper, p, org2, time, alpha);
3589 bbox2 |= l->PaintAll(&tmp2, iclipRect[1], clipper, p, org2, time, alpha);
3590 bbox2 |= l->PaintAll(&tmp3, iclipRect[2], clipper, p, org2, time, alpha);
3591 bbox2 |= l->PaintAll(&tmp4, iclipRect[3], clipper, p, org2, time, alpha);
3592 tmpCompDrawItemList.AddTailList(&tmp1);
3593 tmpCompDrawItemList.AddTailList(&tmp2);
3594 tmpCompDrawItemList.AddTailList(&tmp3);
3595 tmpCompDrawItemList.AddTailList(&tmp4);
3597 else
3599 for (int i=0;i<l->GetWordCount();i++)
3601 tmpCompDrawItemList.AddTail();
3603 bbox2 |= l->PaintAll(&tmpCompDrawItemList, clipRect, clipper, p, org2, time, alpha);
3605 compDrawItemList->AddTailList(&tmpCompDrawItemList);
3606 p.y += l->m_ascent + l->m_descent;
3610 STDMETHODIMP CRenderedTextSubtitle::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECTCoor2& bbox)
3612 CAtlList<CRect> rectList;
3613 HRESULT result = RenderEx(spd, rt, fps, rectList);
3614 POSITION pos = rectList.GetHeadPosition();
3615 CRect bbox2(0,0,0,0);
3616 while(pos!=NULL)
3618 bbox2 |= rectList.GetNext(pos);
3620 bbox = bbox2;
3621 return result;
3624 // IPersist
3626 STDMETHODIMP CRenderedTextSubtitle::GetClassID(CLSID* pClassID)
3628 return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
3631 // ISubStream
3633 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStreamCount()
3635 return(1);
3638 STDMETHODIMP CRenderedTextSubtitle::GetStreamInfo(int iStream, WCHAR** ppName, LCID* pLCID)
3640 if(iStream != 0) return E_INVALIDARG;
3641 if(ppName)
3643 *ppName = (WCHAR*)CoTaskMemAlloc((m_name.GetLength()+1)*sizeof(WCHAR));
3644 if(!(*ppName))
3645 return E_OUTOFMEMORY;
3646 wcscpy(*ppName, CStringW(m_name));
3648 if(pLCID)
3650 *pLCID = 0; // TODO
3652 return S_OK;
3655 STDMETHODIMP_(int) CRenderedTextSubtitle::GetStream()
3657 return(0);
3660 STDMETHODIMP CRenderedTextSubtitle::SetStream(int iStream)
3662 return iStream == 0 ? S_OK : E_FAIL;
3665 STDMETHODIMP CRenderedTextSubtitle::Reload()
3667 CFileStatus s;
3668 if(!CFile::GetStatus(m_path, s)) return E_FAIL;
3669 return !m_path.IsEmpty() && Open(m_path, DEFAULT_CHARSET) ? S_OK : E_FAIL;
3672 STDMETHODIMP_(bool) CRenderedTextSubtitle::IsColorTypeSupported( int type )
3674 return type==MSP_AYUV_PLANAR ||
3675 type==MSP_AYUV ||
3676 type==MSP_XY_AUYV ||
3677 type==MSP_RGBA;
3680 STDMETHODIMP CRenderedTextSubtitle::Lock()
3682 return CSubPicProviderImpl::Lock();
3685 STDMETHODIMP CRenderedTextSubtitle::Unlock()
3687 return CSubPicProviderImpl::Unlock();