Merge with MPC-HC 6d1472b2f18266d92e5bc068667de348c0cd3b3b.
[xy_vsfilter.git] / src / subtitles / libssf / SubtitleFile.cpp
blob788fb1b69fbbdd1c94ab1a926974772e2367f3b7
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 "SubtitleFile.h"
25 namespace ssf
27 SubtitleFile::SubtitleFile()
31 SubtitleFile::~SubtitleFile()
35 void SubtitleFile::Parse(InputStream& s)
37 m_segments.RemoveAll();
39 __super::Parse(s, s_predef);
41 // TODO: check file.format == "ssf" and file.version == 1
43 CAtlList<Definition*> defs;
44 GetRootRef()->GetChildDefs(defs, L"subtitle");
46 StringMapW<float> offset;
48 POSITION pos = defs.GetHeadPosition();
49 while(pos)
51 Definition* pDef = defs.GetNext(pos);
53 try
55 Definition::Time time;
57 if(pDef->GetAsTime(time, offset) && (*pDef)[L"@"].IsValue())
59 m_segments.Insert(time.start.value, time.stop.value, pDef);
62 catch(Exception&)
68 void SubtitleFile::Append(InputStream& s, float start, float stop, bool fSetTime)
70 Reference* pRootRef = GetRootRef();
72 ParseDefs(s, pRootRef);
74 CAtlList<Definition*> defs;
75 GetNewDefs(defs);
77 POSITION pos = defs.GetHeadPosition();
78 while(pos)
80 Definition* pDef = defs.GetNext(pos);
82 if(pDef->m_parent == pRootRef && pDef->m_type == L"subtitle" && (*pDef)[L"@"].IsValue())
84 m_segments.Insert(start, stop, pDef);
86 if(fSetTime)
88 try
90 Definition::Time time;
91 StringMapW<float> offset;
92 pDef->GetAsTime(time, offset);
93 if(time.start.value == start && time.stop.value == stop)
94 continue;
96 catch(Exception&)
100 CStringW str;
101 str.Format(L"%.3f", start);
102 pDef->SetChildAsNumber(L"time.start", str, L"s");
103 str.Format(L"%.3f", stop);
104 pDef->SetChildAsNumber(L"time.stop", str, L"s");
109 Commit();
112 bool SubtitleFile::Lookup(float at, CAutoPtrList<Subtitle>& subs)
114 if(!subs.IsEmpty()) {ASSERT(0); return false;}
116 CAtlList<SegmentItem> sis;
117 m_segments.Lookup(at, sis);
119 POSITION pos = sis.GetHeadPosition();
120 while(pos)
122 SegmentItem& si = sis.GetNext(pos);
124 CAutoPtr<Subtitle> s(new Subtitle(this));
126 if(s->Parse(si.pDef, si.start, si.stop, at))
128 for(POSITION pos = subs.GetHeadPosition(); pos; subs.GetNext(pos))
130 if(s->m_layer < subs.GetAt(pos)->m_layer)
132 subs.InsertBefore(pos, s);
133 break;
137 if(s)
139 subs.AddTail(s);
144 return !subs.IsEmpty();
149 SubtitleFile::Segment::Segment(float start, float stop, const SegmentItem* si)
151 m_start = start;
152 m_stop = stop;
153 if(si) AddTail(*si);
156 SubtitleFile::Segment::Segment(const Segment& s)
158 *this = s;
161 void SubtitleFile::Segment::operator = (const Segment& s)
163 m_start = s.m_start;
164 m_stop = s.m_stop;
165 RemoveAll();
166 AddTailList(&s);
171 void SubtitleFile::SegmentList::RemoveAll()
173 __super::RemoveAll();
174 m_index.RemoveAll();
177 void SubtitleFile::SegmentList::Insert(float start, float stop, Definition* pDef)
179 if(start >= stop) {ASSERT(0); return;}
181 m_index.RemoveAll();
183 SegmentItem si = {pDef, start, stop};
185 if(IsEmpty())
187 AddTail(Segment(start, stop, &si));
188 return;
191 Segment& head = GetHead();
192 Segment& tail = GetTail();
194 if(start >= tail.m_stop && stop > tail.m_stop)
196 if(start > tail.m_stop) AddTail(Segment(tail.m_stop, start));
197 AddTail(Segment(start, stop, &si));
199 else if(start < head.m_start && stop <= head.m_start)
201 if(stop < head.m_start) AddHead(Segment(stop, head.m_start));
202 AddHead(Segment(start, stop, &si));
204 else
206 if(start < head.m_start)
208 AddHead(Segment(start, head.m_start, &si));
209 start = head.m_start;
212 if(stop > tail.m_stop)
214 AddTail(Segment(tail.m_stop, stop, &si));
215 stop = tail.m_stop;
218 for(POSITION pos = GetHeadPosition(); pos; GetNext(pos))
220 Segment& s = GetAt(pos);
222 if(start >= s.m_stop) continue;
223 if(stop <= s.m_start) break;
225 if(s.m_start < start && start < s.m_stop)
227 Segment s2 = s;
228 s2.m_start = start;
229 InsertAfter(pos, s2);
230 s.m_stop = start;
232 else if(s.m_start == start)
234 if(stop > s.m_stop)
236 start = s.m_stop;
238 else if(stop < s.m_stop)
240 Segment s2 = s;
241 s2.m_start = stop;
242 InsertAfter(pos, s2);
243 s.m_stop = stop;
246 s.AddTail(si);
252 size_t SubtitleFile::SegmentList::Index(bool fForce)
254 if(m_index.IsEmpty() || fForce)
256 m_index.RemoveAll();
257 POSITION pos = GetHeadPosition();
258 while(pos) m_index.Add(&GetNext(pos));
261 return m_index.GetCount();
264 void SubtitleFile::SegmentList::Lookup(float at, CAtlList<SegmentItem>& sis)
266 sis.RemoveAll();
268 size_t k;
269 if(Lookup(at, k))
271 sis.AddTailList(GetSegment(k));
275 bool SubtitleFile::SegmentList::Lookup(float at, size_t& k)
277 if(!Index()) return false;
279 size_t i = 0, j = m_index.GetCount()-1;
281 if(m_index[i]->m_start <= at && at < m_index[j]->m_stop)
284 k = (i+j)/2;
285 if(m_index[k]->m_start <= at && at < m_index[k]->m_stop) {return true;}
286 else if(at < m_index[k]->m_start) {if(j == k) k--; j = k;}
287 else if(at >= m_index[k]->m_stop) {if(i == k) k++; i = k;}
289 while(i <= j);
291 return false;
294 const SubtitleFile::Segment* SubtitleFile::SegmentList::GetSegment(size_t k)
296 return 0 <= k && k < m_index.GetCount() ? m_index[k] : NULL;
299 // TODO: this should be overridable from outside
301 LPCWSTR SubtitleFile::s_predef =
302 L"color#white {a: 255; r: 255; g: 255; b: 255;}; \n"
303 L"color#black {a: 255; r: 0; g: 0; b: 0;}; \n"
304 L"color#gray {a: 255; r: 128; g: 128; b: 128;}; \n"
305 L"color#red {a: 255; r: 255; g: 0; b: 0;}; \n"
306 L"color#green {a: 255; r: 0; g: 255; b: 0;}; \n"
307 L"color#blue {a: 255; r: 0; g: 0; b: 255;}; \n"
308 L"color#cyan {a: 255; r: 0; g: 255; b: 255;}; \n"
309 L"color#yellow {a: 255; r: 255; g: 255; b: 0;}; \n"
310 L"color#magenta {a: 255; r: 255; g: 0; b: 255;}; \n"
311 L" \n"
312 L"align#topleft {v: \"top\"; h: \"left\";}; \n"
313 L"align#topcenter {v: \"top\"; h: \"center\";}; \n"
314 L"align#topright {v: \"top\"; h: \"right\";}; \n"
315 L"align#middleleft {v: \"middle\"; h: \"left\";}; \n"
316 L"align#middlecenter {v: \"middle\"; h: \"center\";}; \n"
317 L"align#middleright {v: \"middle\"; h: \"right\";}; \n"
318 L"align#bottomleft {v: \"bottom\"; h: \"left\";}; \n"
319 L"align#bottomcenter {v: \"bottom\"; h: \"center\";}; \n"
320 L"align#bottomright {v: \"bottom\"; h: \"right\";}; \n"
321 L" \n"
322 L"time#time {scale: 1;}; \n"
323 L"time#startstop {start: \"start\"; stop: \"stop\";}; \n"
324 L" \n"
325 L"#b {font.weight: \"bold\"}; \n"
326 L"#i {font.italic: \"true\"}; \n"
327 L"#u {font.underline: \"true\"}; \n"
328 L"#s {font.strikethrough: \"true\"}; \n"
329 L" \n"
330 L"#nobr {linebreak: \"none\"}; \n"
331 L" \n"
332 L"subtitle#subtitle \n"
333 L"{ \n"
334 L" frame \n"
335 L" { \n"
336 L" reference: \"video\"; \n"
337 L" resolution: {cx: 640; cy: 480;}; \n"
338 L" }; \n"
339 L" \n"
340 L" direction \n"
341 L" { \n"
342 L" primary: \"right\"; \n"
343 L" secondary: \"down\"; \n"
344 L" }; \n"
345 L" \n"
346 L" wrap: \"normal\"; \n"
347 L" \n"
348 L" layer: 0; \n"
349 L" \n"
350 L" style \n"
351 L" { \n"
352 L" linebreak: \"word\"; \n"
353 L" \n"
354 L" placement \n"
355 L" { \n"
356 L" clip: \"none\"; \n"
357 L" margin: {t: 0; r: 0; b: 0; l: 0;}; \n"
358 L" align: bottomcenter; \n"
359 L" pos: \"auto\" \n"
360 L" offset: {x: 0; y: 0;}; \n"
361 L" angle: {x: 0; y: 0; z: 0;}; \n"
362 L" org: \"auto\" \n"
363 L" path: \"\"; \n"
364 L" }; \n"
365 L" \n"
366 L" font \n"
367 L" { \n"
368 L" face: \"Arial\"; \n"
369 L" size: 20; \n"
370 L" weight: \"bold\"; \n"
371 L" color: white; \n"
372 L" underline: \"false\"; \n"
373 L" strikethrough: \"false\"; \n"
374 L" italic: \"false\"; \n"
375 L" spacing: 0; \n"
376 L" scale: {cx: 1; cy: 1;}; \n"
377 L" kerning: \"true\"; \n"
378 L" }; \n"
379 L" \n"
380 L" background \n"
381 L" { \n"
382 L" color: black; \n"
383 L" size: 2; \n"
384 L" type: \"outline\"; \n"
385 L" blur: 0; \n"
386 L" }; \n"
387 L" \n"
388 L" shadow \n"
389 L" { \n"
390 L" color: black {a: 128;}; \n"
391 L" depth: 2; \n"
392 L" angle: -45; \n"
393 L" blur: 0; \n"
394 L" }; \n"
395 L" \n"
396 L" fill \n"
397 L" { \n"
398 L" color: yellow; \n"
399 L" width: 0; \n"
400 L" }; \n"
401 L" }; \n"
402 L"}; \n";