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)
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
29 struct Subtitle::n2n_t
Subtitle::m_n2n
;
31 Subtitle::Subtitle(File
* pFile
)
35 if(m_n2n
.align
[0].IsEmpty())
37 m_n2n
.align
[0][L
"left"] = 0;
38 m_n2n
.align
[0][L
"center"] = 0.5;
39 m_n2n
.align
[0][L
"middle"] = 0.5;
40 m_n2n
.align
[0][L
"right"] = 1;
43 if(m_n2n
.align
[1].IsEmpty())
45 m_n2n
.align
[1][L
"top"] = 0;
46 m_n2n
.align
[1][L
"middle"] = 0.5;
47 m_n2n
.align
[1][L
"center"] = 0.5;
48 m_n2n
.align
[1][L
"bottom"] = 1;
51 if(m_n2n
.weight
.IsEmpty())
53 m_n2n
.weight
[L
"thin"] = FW_THIN
;
54 m_n2n
.weight
[L
"normal"] = FW_NORMAL
;
55 m_n2n
.weight
[L
"bold"] = FW_BOLD
;
58 if(m_n2n
.transition
.IsEmpty())
60 m_n2n
.transition
[L
"start"] = 0;
61 m_n2n
.transition
[L
"stop"] = 1e10
;
62 m_n2n
.transition
[L
"linear"] = 1;
70 bool Subtitle::Parse(Definition
* pDef
, float start
, float stop
, float at
)
72 ASSERT(m_pFile
&& pDef
);
74 m_name
= pDef
->m_name
;
89 Definition
& frame
= (*pDef
)[L
"frame"];
91 m_frame
.reference
= frame
[L
"reference"];
92 m_frame
.resolution
.cx
= frame
[L
"resolution"][L
"cx"];
93 m_frame
.resolution
.cy
= frame
[L
"resolution"][L
"cy"];
95 Definition
& direction
= (*pDef
)[L
"direction"];
97 m_direction
.primary
= direction
[L
"primary"];
98 m_direction
.secondary
= direction
[L
"secondary"];
100 m_wrap
= (*pDef
)[L
"wrap"];
102 m_layer
= (*pDef
)[L
"layer"];
105 GetStyle(&(*pDef
)[L
"style"], style
);
107 StringMapW
<float> offset
;
108 Definition
& block
= (*pDef
)[L
"@"];
109 Parse(WCharInputStream((LPCWSTR
)block
), style
, at
, offset
, dynamic_cast<Reference
*>(block
.m_parent
));
111 // TODO: trimming should be done by the renderer later, after breaking the words into lines
113 while(!m_text
.IsEmpty() && (m_text
.GetHead().str
== Text::SP
|| m_text
.GetHead().str
== Text::LSEP
))
116 while(!m_text
.IsEmpty() && (m_text
.GetTail().str
== Text::SP
|| m_text
.GetTail().str
== Text::LSEP
))
119 for(POSITION pos
= m_text
.GetHeadPosition(); pos
; m_text
.GetNext(pos
))
121 if(m_text
.GetAt(pos
).str
== Text::LSEP
)
124 m_text
.GetPrev(prev
);
126 while(prev
&& m_text
.GetAt(prev
).str
== Text::SP
)
129 m_text
.GetPrev(prev
);
130 m_text
.RemoveAt(tmp
);
134 m_text
.GetNext(next
);
136 while(next
&& m_text
.GetAt(next
).str
== Text::SP
)
139 m_text
.GetNext(next
);
140 m_text
.RemoveAt(tmp
);
147 TRACE(_T("%s"), e
.ToString());
156 void Subtitle::GetStyle(Definition
* pDef
, Style
& style
)
158 style
.placement
.pos
.x
= 0;
159 style
.placement
.pos
.y
= 0;
160 style
.placement
.pos
.auto_x
= true;
161 style
.placement
.pos
.auto_y
= true;
163 style
.placement
.org
.x
= 0;
164 style
.placement
.org
.y
= 0;
165 style
.placement
.org
.auto_x
= true;
166 style
.placement
.org
.auto_y
= true;
168 Rect frame
= {0, m_frame
.resolution
.cx
, m_frame
.resolution
.cy
, 0};
170 style
.placement
.clip
.t
= -1;
171 style
.placement
.clip
.r
= -1;
172 style
.placement
.clip
.b
= -1;
173 style
.placement
.clip
.l
= -1;
177 style
.linebreak
= (*pDef
)[L
"linebreak"];
179 Definition
& placement
= (*pDef
)[L
"placement"];
181 Definition
& clip
= placement
[L
"clip"];
183 if(clip
.IsValue(Definition::string
))
187 if(str
== L
"frame") style
.placement
.clip
= frame
;
192 if(clip
[L
"t"].IsValue()) style
.placement
.clip
.t
= clip
[L
"t"];
193 if(clip
[L
"r"].IsValue()) style
.placement
.clip
.r
= clip
[L
"r"];
194 if(clip
[L
"b"].IsValue()) style
.placement
.clip
.b
= clip
[L
"b"];
195 if(clip
[L
"l"].IsValue()) style
.placement
.clip
.l
= clip
[L
"l"];
198 StringMapW
<float> n2n_margin
[2];
200 n2n_margin
[0][L
"top"] = 0;
201 n2n_margin
[0][L
"right"] = 0;
202 n2n_margin
[0][L
"bottom"] = frame
.b
- frame
.t
;
203 n2n_margin
[0][L
"left"] = frame
.r
- frame
.l
;
204 n2n_margin
[1][L
"top"] = frame
.b
- frame
.t
;
205 n2n_margin
[1][L
"right"] = frame
.r
- frame
.l
;
206 n2n_margin
[1][L
"bottom"] = 0;
207 n2n_margin
[1][L
"left"] = 0;
209 placement
[L
"margin"][L
"t"].GetAsNumber(style
.placement
.margin
.t
, &n2n_margin
[0]);
210 placement
[L
"margin"][L
"r"].GetAsNumber(style
.placement
.margin
.r
, &n2n_margin
[0]);
211 placement
[L
"margin"][L
"b"].GetAsNumber(style
.placement
.margin
.b
, &n2n_margin
[1]);
212 placement
[L
"margin"][L
"l"].GetAsNumber(style
.placement
.margin
.l
, &n2n_margin
[1]);
214 placement
[L
"align"][L
"h"].GetAsNumber(style
.placement
.align
.h
, &m_n2n
.align
[0]);
215 placement
[L
"align"][L
"v"].GetAsNumber(style
.placement
.align
.v
, &m_n2n
.align
[1]);
217 if(placement
[L
"pos"][L
"x"].IsValue()) {style
.placement
.pos
.x
= placement
[L
"pos"][L
"x"]; style
.placement
.pos
.auto_x
= false;}
218 if(placement
[L
"pos"][L
"y"].IsValue()) {style
.placement
.pos
.y
= placement
[L
"pos"][L
"y"]; style
.placement
.pos
.auto_y
= false;}
220 placement
[L
"offset"][L
"x"].GetAsNumber(style
.placement
.offset
.x
);
221 placement
[L
"offset"][L
"y"].GetAsNumber(style
.placement
.offset
.y
);
223 style
.placement
.angle
.x
= placement
[L
"angle"][L
"x"];
224 style
.placement
.angle
.y
= placement
[L
"angle"][L
"y"];
225 style
.placement
.angle
.z
= placement
[L
"angle"][L
"z"];
227 if(placement
[L
"org"][L
"x"].IsValue()) {style
.placement
.org
.x
= placement
[L
"org"][L
"x"]; style
.placement
.org
.auto_x
= false;}
228 if(placement
[L
"org"][L
"y"].IsValue()) {style
.placement
.org
.y
= placement
[L
"org"][L
"y"]; style
.placement
.org
.auto_y
= false;}
230 style
.placement
.path
= placement
[L
"path"];
232 Definition
& font
= (*pDef
)[L
"font"];
234 style
.font
.face
= font
[L
"face"];
235 style
.font
.size
= font
[L
"size"];
236 font
[L
"weight"].GetAsNumber(style
.font
.weight
, &m_n2n
.weight
);
237 style
.font
.color
.a
= font
[L
"color"][L
"a"];
238 style
.font
.color
.r
= font
[L
"color"][L
"r"];
239 style
.font
.color
.g
= font
[L
"color"][L
"g"];
240 style
.font
.color
.b
= font
[L
"color"][L
"b"];
241 style
.font
.underline
= font
[L
"underline"];
242 style
.font
.strikethrough
= font
[L
"strikethrough"];
243 style
.font
.italic
= font
[L
"italic"];
244 style
.font
.spacing
= font
[L
"spacing"];
245 style
.font
.scale
.cx
= font
[L
"scale"][L
"cx"];
246 style
.font
.scale
.cy
= font
[L
"scale"][L
"cy"];
247 style
.font
.kerning
= font
[L
"kerning"];
249 Definition
& background
= (*pDef
)[L
"background"];
251 style
.background
.color
.a
= background
[L
"color"][L
"a"];
252 style
.background
.color
.r
= background
[L
"color"][L
"r"];
253 style
.background
.color
.g
= background
[L
"color"][L
"g"];
254 style
.background
.color
.b
= background
[L
"color"][L
"b"];
255 style
.background
.size
= background
[L
"size"];
256 style
.background
.type
= background
[L
"type"];
257 style
.background
.blur
= background
[L
"blur"];
259 Definition
& shadow
= (*pDef
)[L
"shadow"];
261 style
.shadow
.color
.a
= shadow
[L
"color"][L
"a"];
262 style
.shadow
.color
.r
= shadow
[L
"color"][L
"r"];
263 style
.shadow
.color
.g
= shadow
[L
"color"][L
"g"];
264 style
.shadow
.color
.b
= shadow
[L
"color"][L
"b"];
265 style
.shadow
.depth
= shadow
[L
"depth"];
266 style
.shadow
.angle
= shadow
[L
"angle"];
267 style
.shadow
.blur
= shadow
[L
"blur"];
269 Definition
& fill
= (*pDef
)[L
"fill"];
271 style
.fill
.color
.a
= fill
[L
"color"][L
"a"];
272 style
.fill
.color
.r
= fill
[L
"color"][L
"r"];
273 style
.fill
.color
.g
= fill
[L
"color"][L
"g"];
274 style
.fill
.color
.b
= fill
[L
"color"][L
"b"];
275 style
.fill
.width
= fill
[L
"width"];
278 float Subtitle::GetMixWeight(Definition
* pDef
, float at
, StringMapW
<float>& offset
, int default_id
)
284 StringMapW
<float> n2n
;
287 n2n
[L
"stop"] = m_time
.stop
- m_time
.start
;
289 Definition::Time time
;
290 if(pDef
->GetAsTime(time
, offset
, &n2n
, default_id
) && time
.start
.value
< time
.stop
.value
)
292 t
= (at
- time
.start
.value
) / (time
.stop
.value
- time
.start
.value
);
297 else if(t
>= 1) t
= 0.99999f
; // doh
299 if((*pDef
)[L
"loop"].IsValue()) t
*= (float)(*pDef
)[L
"loop"];
301 CStringW direction
= (*pDef
)[L
"direction"].IsValue() ? (*pDef
)[L
"direction"] : L
"fw";
302 if(direction
== L
"fwbw" || direction
== L
"bwfw") t
*= 2;
307 if(direction
== L
"bw"
308 || direction
== L
"fwbw" && ((int)n
& 1)
309 || direction
== L
"bwfw" && !((int)n
& 1))
314 if((*pDef
)[L
"transition"].IsValue())
316 Definition::Number
<float> n
;
317 (*pDef
)[L
"transition"].GetAsNumber(n
, &m_n2n
.transition
);
318 if(n
.value
>= 0) accel
= n
.value
;
321 if(t
== 0.99999f
) t
= 1;
340 bool Subtitle::MixValue(Definition
& def
, T
& value
, float t
)
343 return MixValue(def
, value
, t
, &n2n
);
347 bool Subtitle::MixValue(Definition
& def
, float& value
, float t
)
349 StringMapW
<float> n2n
;
350 return MixValue(def
, value
, t
, &n2n
);
354 bool Subtitle::MixValue(Definition
& def
, T
& value
, float t
, StringMapW
<T
>* n2n
)
356 if(!def
.IsValue()) return false;
360 if(n2n
&& def
.IsValue(Definition::string
))
362 if(StringMapW
<T
>::CPair
* p
= n2n
->Lookup(def
))
376 bool Subtitle::MixValue(Definition
& def
, float& value
, float t
, StringMapW
<float>* n2n
)
378 if(!def
.IsValue()) return false;
382 if(n2n
&& def
.IsValue(Definition::string
))
384 if(StringMap
<float, CStringW
>::CPair
* p
= n2n
->Lookup(def
))
386 value
+= (p
->m_value
- value
) * t
;
391 value
+= ((float)def
- value
) * t
;
398 bool Subtitle::MixValue(Definition
& def
, Path
& src
, float t
)
400 if(!def
.IsValue(Definition::string
)) return false;
408 Path dst
= (LPCWSTR
)def
;
410 if(src
.GetCount() == dst
.GetCount())
412 for(size_t i
= 0, j
= src
.GetCount(); i
< j
; i
++)
415 const Point
& d
= dst
[i
];
416 s
.x
+= (d
.x
- s
.x
) * t
;
417 s
.y
+= (d
.y
- s
.y
) * t
;
425 void Subtitle::MixStyle(Definition
* pDef
, Style
& dst
, float t
)
427 const Style src
= dst
;
430 else if(t
> 1) t
= 1;
432 MixValue((*pDef
)[L
"linebreak"], dst
.linebreak
, t
);
434 Definition
& placement
= (*pDef
)[L
"placement"];
436 MixValue(placement
[L
"clip"][L
"t"], dst
.placement
.clip
.t
, t
);
437 MixValue(placement
[L
"clip"][L
"r"], dst
.placement
.clip
.r
, t
);
438 MixValue(placement
[L
"clip"][L
"b"], dst
.placement
.clip
.b
, t
);
439 MixValue(placement
[L
"clip"][L
"l"], dst
.placement
.clip
.l
, t
);
440 MixValue(placement
[L
"align"][L
"h"], dst
.placement
.align
.h
, t
, &m_n2n
.align
[0]);
441 MixValue(placement
[L
"align"][L
"v"], dst
.placement
.align
.v
, t
, &m_n2n
.align
[1]);
442 dst
.placement
.pos
.auto_x
= !MixValue(placement
[L
"pos"][L
"x"], dst
.placement
.pos
.x
, dst
.placement
.pos
.auto_x
? 1 : t
);
443 dst
.placement
.pos
.auto_y
= !MixValue(placement
[L
"pos"][L
"y"], dst
.placement
.pos
.y
, dst
.placement
.pos
.auto_y
? 1 : t
);
444 MixValue(placement
[L
"offset"][L
"x"], dst
.placement
.offset
.x
, t
);
445 MixValue(placement
[L
"offset"][L
"y"], dst
.placement
.offset
.y
, t
);
446 MixValue(placement
[L
"angle"][L
"x"], dst
.placement
.angle
.x
, t
);
447 MixValue(placement
[L
"angle"][L
"y"], dst
.placement
.angle
.y
, t
);
448 MixValue(placement
[L
"angle"][L
"z"], dst
.placement
.angle
.z
, t
);
449 dst
.placement
.org
.auto_x
= !MixValue(placement
[L
"org"][L
"x"], dst
.placement
.org
.x
, dst
.placement
.org
.auto_x
? 1 : t
);
450 dst
.placement
.org
.auto_y
= !MixValue(placement
[L
"org"][L
"y"], dst
.placement
.org
.y
, dst
.placement
.org
.auto_y
? 1 : t
);
451 MixValue(placement
[L
"path"], dst
.placement
.path
, t
);
453 Definition
& font
= (*pDef
)[L
"font"];
455 MixValue(font
[L
"face"], dst
.font
.face
, t
);
456 MixValue(font
[L
"size"], dst
.font
.size
, t
);
457 MixValue(font
[L
"weight"], dst
.font
.weight
, t
, &m_n2n
.weight
);
458 MixValue(font
[L
"color"][L
"a"], dst
.font
.color
.a
, t
);
459 MixValue(font
[L
"color"][L
"r"], dst
.font
.color
.r
, t
);
460 MixValue(font
[L
"color"][L
"g"], dst
.font
.color
.g
, t
);
461 MixValue(font
[L
"color"][L
"b"], dst
.font
.color
.b
, t
);
462 MixValue(font
[L
"underline"], dst
.font
.underline
, t
);
463 MixValue(font
[L
"strikethrough"], dst
.font
.strikethrough
, t
);
464 MixValue(font
[L
"italic"], dst
.font
.italic
, t
);
465 MixValue(font
[L
"spacing"], dst
.font
.spacing
, t
);
466 MixValue(font
[L
"scale"][L
"cx"], dst
.font
.scale
.cx
, t
);
467 MixValue(font
[L
"scale"][L
"cy"], dst
.font
.scale
.cy
, t
);
468 MixValue(font
[L
"kerning"], dst
.font
.kerning
, t
);
470 Definition
& background
= (*pDef
)[L
"background"];
472 MixValue(background
[L
"color"][L
"a"], dst
.background
.color
.a
, t
);
473 MixValue(background
[L
"color"][L
"r"], dst
.background
.color
.r
, t
);
474 MixValue(background
[L
"color"][L
"g"], dst
.background
.color
.g
, t
);
475 MixValue(background
[L
"color"][L
"b"], dst
.background
.color
.b
, t
);
476 MixValue(background
[L
"size"], dst
.background
.size
, t
);
477 MixValue(background
[L
"type"], dst
.background
.type
, t
);
478 MixValue(background
[L
"blur"], dst
.background
.blur
, t
);
480 Definition
& shadow
= (*pDef
)[L
"shadow"];
482 MixValue(shadow
[L
"color"][L
"a"], dst
.shadow
.color
.a
, t
);
483 MixValue(shadow
[L
"color"][L
"r"], dst
.shadow
.color
.r
, t
);
484 MixValue(shadow
[L
"color"][L
"g"], dst
.shadow
.color
.g
, t
);
485 MixValue(shadow
[L
"color"][L
"b"], dst
.shadow
.color
.b
, t
);
486 MixValue(shadow
[L
"depth"], dst
.shadow
.depth
, t
);
487 MixValue(shadow
[L
"angle"], dst
.shadow
.angle
, t
);
488 MixValue(shadow
[L
"blur"], dst
.shadow
.blur
, t
);
490 Definition
& fill
= (*pDef
)[L
"fill"];
492 MixValue(fill
[L
"color"][L
"a"], dst
.fill
.color
.a
, t
);
493 MixValue(fill
[L
"color"][L
"r"], dst
.fill
.color
.r
, t
);
494 MixValue(fill
[L
"color"][L
"g"], dst
.fill
.color
.g
, t
);
495 MixValue(fill
[L
"color"][L
"b"], dst
.fill
.color
.b
, t
);
496 MixValue(fill
[L
"width"], dst
.fill
.width
, t
);
498 if(fill
.m_priority
>= PNormal
) // this assumes there is no way to set low priority inline overrides
500 if(dst
.fill
.id
> 0) throw Exception(_T("cannot apply fill more than once on the same text"));
501 dst
.fill
.id
= ++Fill::gen_id
;
505 void Subtitle::Parse(InputStream
& s
, Style style
, float at
, StringMapW
<float> offset
, Reference
* pParentRef
)
510 for(int c
= s
.PeekChar(); c
!= Stream::EOS
; c
= s
.PeekChar())
520 StringMapW
<float> inneroffset
= offset
;
526 Definition
* pDef
= m_pFile
->CreateDef(pParentRef
);
528 m_pFile
->ParseRefs(s
, pDef
, L
",;]");
530 ASSERT(pDef
->IsType(L
"style") || pDef
->IsTypeUnknown());
532 if((*pDef
)[L
"time"][L
"start"].IsValue() && (*pDef
)[L
"time"][L
"stop"].IsValue())
537 float t
= GetMixWeight(pDef
, at
, offset
, ++default_id
);
538 MixStyle(pDef
, style
, t
);
540 if((*pDef
)[L
"@"].IsValue())
542 Parse(WCharInputStream((LPCWSTR
)(*pDef
)[L
"@"]), style
, at
, offset
, pParentRef
);
548 while(c
== ',' || c
== ';');
550 if(c
!= ']') s
.ThrowError(_T("unterminated override"));
552 bool fWhiteSpaceNext
= s
.IsWhiteSpace(s
.PeekChar());
553 c
= s
.SkipWhiteSpace();
558 Parse(s
, style
, at
, inneroffset
, pParentRef
);
562 if(fWhiteSpaceNext
) text
.str
+= (WCHAR
)Text::SP
;
568 s
.ThrowError(_T("unexpected ] found"));
573 Parse(s
, text
.style
, at
, offset
, pParentRef
);
584 if(c
== Stream::EOS
) break;
585 else if(c
== 'n') {AddText(text
); text
.str
= (WCHAR
)Text::LSEP
; AddText(text
); continue;}
586 else if(c
== 'h') c
= Text::NBSP
;
589 AddChar(text
, (WCHAR
)c
);
596 void Subtitle::AddChar(Text
& t
, WCHAR c
)
598 bool f1
= !t
.str
.IsEmpty() && Stream::IsWhiteSpace(t
.str
[t
.str
.GetLength()-1]);
599 bool f2
= Stream::IsWhiteSpace(c
);
601 if(!f1
|| !f2
) t
.str
+= (WCHAR
)c
;
604 void Subtitle::AddText(Text
& t
)
606 if(t
.str
.IsEmpty()) return;
608 Split
sa(' ', t
.str
, 0, Split::Max
);
610 for(size_t i
= 0, n
= sa
; i
< n
; i
++)
612 CStringW str
= sa
[i
];
620 if(i
< n
-1 && (m_text
.IsEmpty() || m_text
.GetTail().str
!= Text::SP
))
622 t
.str
= (WCHAR
)Text::SP
;
632 unsigned int Fill::gen_id
= 0;
634 Color::operator DWORD()
637 (min(max((DWORD
)b
, 0), 255) << 0) |
638 (min(max((DWORD
)g
, 0), 255) << 8) |
639 (min(max((DWORD
)r
, 0), 255) << 16) |
640 (min(max((DWORD
)a
, 0), 255) << 24);
645 Path
& Path::operator = (LPCWSTR str
)
651 for(size_t i
= 0, j
= GetCount(); i
< j
; i
++)
654 p
.x
= s
.GetAtFloat(i
*2+0);
655 p
.y
= s
.GetAtFloat(i
*2+1);
662 CStringW
Path::ToString()
666 for(size_t i
= 0, j
= GetCount(); i
< j
; i
++)
668 const Point
& p
= GetAt(i
);
671 str
.Format(L
"%f %f ", p
.x
, p
.y
);
678 bool Style::IsSimilar(const Style
& s
)
681 font
.color
.r
== s
.font
.color
.r
682 && font
.color
.g
== s
.font
.color
.g
683 && font
.color
.b
== s
.font
.color
.b
684 && font
.color
.a
== s
.font
.color
.a
685 && background
.color
.r
== s
.background
.color
.r
686 && background
.color
.g
== s
.background
.color
.g
687 && background
.color
.b
== s
.background
.color
.b
688 && background
.color
.a
== s
.background
.color
.a
689 && background
.size
== s
.background
.size
690 && background
.type
== s
.background
.type
691 && background
.blur
== s
.background
.blur
692 && shadow
.color
.r
== s
.shadow
.color
.r
693 && shadow
.color
.g
== s
.shadow
.color
.g
694 && shadow
.color
.b
== s
.shadow
.color
.b
695 && shadow
.color
.a
== s
.shadow
.color
.a
696 && shadow
.depth
== s
.shadow
.depth
697 && shadow
.angle
== s
.shadow
.angle
698 && shadow
.blur
== s
.shadow
.blur
699 && fill
.id
== s
.fill
.id
;