Remove unused ProjectConfiguration
[xy_vsfilter.git] / src / subtitles / libssf / Subtitle.cpp
blob7bb9a7d6f5fa9e847b9b264a5e1603b36e840457
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.
9 *
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 "Subtitle.h"
24 #include "Split.h"
25 #include <math.h>
27 namespace ssf
29 struct Subtitle::n2n_t Subtitle::m_n2n;
31 Subtitle::Subtitle(File* pFile)
32 : m_pFile(pFile)
33 , m_animated(false)
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;
66 Subtitle::~Subtitle()
70 bool Subtitle::Parse(Definition* pDef, float start, float stop, float at)
72 ASSERT(m_pFile && pDef);
74 m_name = pDef->m_name;
76 m_text.RemoveAll();
78 m_time.start = start;
79 m_time.stop = stop;
81 at -= start;
83 Fill::gen_id = 0;
85 m_pFile->Commit();
87 try
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"];
104 Style style;
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))
114 m_text.RemoveHead();
116 while(!m_text.IsEmpty() && (m_text.GetTail().str == Text::SP || m_text.GetTail().str == Text::LSEP))
117 m_text.RemoveTail();
119 for(POSITION pos = m_text.GetHeadPosition(); pos; m_text.GetNext(pos))
121 if(m_text.GetAt(pos).str == Text::LSEP)
123 POSITION prev = pos;
124 m_text.GetPrev(prev);
126 while(prev && m_text.GetAt(prev).str == Text::SP)
128 POSITION tmp = prev;
129 m_text.GetPrev(prev);
130 m_text.RemoveAt(tmp);
133 POSITION next = pos;
134 m_text.GetNext(next);
136 while(next && m_text.GetAt(next).str == Text::SP)
138 POSITION tmp = next;
139 m_text.GetNext(next);
140 m_text.RemoveAt(tmp);
145 catch(Exception& e)
147 TRACE(_T("%s"), e.ToString());
148 return false;
151 m_pFile->Rollback();
153 return true;
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))
185 CStringW str = clip;
187 if(str == L"frame") style.placement.clip = frame;
188 // else ?
190 else
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)
280 float t = 1;
284 StringMapW<float> n2n;
286 n2n[L"start"] = 0;
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);
294 float u = t;
296 if(t < 0) t = 0;
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;
304 float n;
305 t = modf(t, &n);
307 if(direction == L"bw"
308 || direction == L"fwbw" && ((int)n & 1)
309 || direction == L"bwfw" && !((int)n & 1))
310 t = 1 - t;
312 float accel = 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;
323 if(u >= 0 && u < 1)
325 t = accel == 0 ? 1 :
326 accel == 1 ? t :
327 accel >= 1e10 ? 0 :
328 pow(t, accel);
332 catch(Exception&)
336 return t;
339 template<class T>
340 bool Subtitle::MixValue(Definition& def, T& value, float t)
342 StringMapW<T> n2n;
343 return MixValue(def, value, t, &n2n);
346 template<>
347 bool Subtitle::MixValue(Definition& def, float& value, float t)
349 StringMapW<float> n2n;
350 return MixValue(def, value, t, &n2n);
353 template<class T>
354 bool Subtitle::MixValue(Definition& def, T& value, float t, StringMapW<T>* n2n)
356 if(!def.IsValue()) return false;
358 if(t >= 0.5)
360 if(n2n && def.IsValue(Definition::string))
362 if(StringMapW<T>::CPair* p = n2n->Lookup(def))
364 value = p->m_value;
365 return true;
369 value = (T)def;
372 return true;
375 template<>
376 bool Subtitle::MixValue(Definition& def, float& value, float t, StringMapW<float>* n2n)
378 if(!def.IsValue()) return false;
380 if(t > 0)
382 if(n2n && def.IsValue(Definition::string))
384 if(StringMap<float, CStringW>::CPair* p = n2n->Lookup(def))
386 value += (p->m_value - value) * t;
387 return true;
391 value += ((float)def - value) * t;
394 return true;
397 template<>
398 bool Subtitle::MixValue(Definition& def, Path& src, float t)
400 if(!def.IsValue(Definition::string)) return false;
402 if(t >= 1)
404 src = (LPCWSTR)def;
406 else if(t > 0)
408 Path dst = (LPCWSTR)def;
410 if(src.GetCount() == dst.GetCount())
412 for(size_t i = 0, j = src.GetCount(); i < j; i++)
414 Point& s = src[i];
415 const Point& d = dst[i];
416 s.x += (d.x - s.x) * t;
417 s.y += (d.y - s.y) * t;
422 return true;
425 void Subtitle::MixStyle(Definition* pDef, Style& dst, float t)
427 const Style src = dst;
429 if(t <= 0) return;
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)
507 Text text;
508 text.style = style;
510 for(int c = s.PeekChar(); c != Stream::EOS; c = s.PeekChar())
512 s.GetChar();
514 if(c == '[')
516 AddText(text);
518 style = text.style;
520 StringMapW<float> inneroffset = offset;
522 int default_id = 0;
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())
534 m_animated = true;
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);
545 s.SkipWhiteSpace();
546 c = s.GetChar();
548 while(c == ',' || c == ';');
550 if(c != ']') s.ThrowError(_T("unterminated override"));
552 bool fWhiteSpaceNext = s.IsWhiteSpace(s.PeekChar());
553 c = s.SkipWhiteSpace();
555 if(c == '{')
557 s.GetChar();
558 Parse(s, style, at, inneroffset, pParentRef);
560 else
562 if(fWhiteSpaceNext) text.str += (WCHAR)Text::SP;
563 text.style = style;
566 else if(c == ']')
568 s.ThrowError(_T("unexpected ] found"));
570 else if(c == '{')
572 AddText(text);
573 Parse(s, text.style, at, offset, pParentRef);
575 else if(c == '}')
577 break;
579 else
581 if(c == '\\')
583 c = s.GetChar();
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);
593 AddText(text);
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);
600 if(f2) c = Text::SP;
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];
614 if(!str.IsEmpty())
616 t.str = str;
617 m_text.AddTail(t);
620 if(i < n-1 && (m_text.IsEmpty() || m_text.GetTail().str != Text::SP))
622 t.str = (WCHAR)Text::SP;
623 m_text.AddTail(t);
627 t.str.Empty();
632 unsigned int Fill::gen_id = 0;
634 Color::operator DWORD()
636 DWORD c =
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);
642 return c;
645 Path& Path::operator = (LPCWSTR str)
647 Split s(' ', str);
649 SetCount(s/2);
651 for(size_t i = 0, j = GetCount(); i < j; i++)
653 Point p;
654 p.x = s.GetAtFloat(i*2+0);
655 p.y = s.GetAtFloat(i*2+1);
656 SetAt(i, p);
659 return *this;
662 CStringW Path::ToString()
664 CStringW ret;
666 for(size_t i = 0, j = GetCount(); i < j; i++)
668 const Point& p = GetAt(i);
670 CStringW str;
671 str.Format(L"%f %f ", p.x, p.y);
672 ret += str;
675 return ret;
678 bool Style::IsSimilar(const Style& s)
680 return
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;