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
23 #include "SubtitleFile.h"
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();
51 Definition
* pDef
= defs
.GetNext(pos
);
55 Definition::Time time
;
57 if(pDef
->GetAsTime(time
, offset
) && (*pDef
)[L
"@"].IsValue())
59 m_segments
.Insert(time
.start
.value
, time
.stop
.value
, pDef
);
68 void SubtitleFile::Append(InputStream
& s
, float start
, float stop
, bool fSetTime
)
70 Reference
* pRootRef
= GetRootRef();
72 ParseDefs(s
, pRootRef
);
74 CAtlList
<Definition
*> defs
;
77 POSITION pos
= defs
.GetHeadPosition();
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
);
90 Definition::Time time
;
91 StringMapW
<float> offset
;
92 pDef
->GetAsTime(time
, offset
);
93 if(time
.start
.value
== start
&& time
.stop
.value
== stop
)
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");
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();
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
);
144 return !subs
.IsEmpty();
149 SubtitleFile::Segment::Segment(float start
, float stop
, const SegmentItem
* si
)
156 SubtitleFile::Segment::Segment(const Segment
& s
)
161 void SubtitleFile::Segment::operator = (const Segment
& s
)
171 void SubtitleFile::SegmentList::RemoveAll()
173 __super::RemoveAll();
177 void SubtitleFile::SegmentList::Insert(float start
, float stop
, Definition
* pDef
)
179 if(start
>= stop
) {ASSERT(0); return;}
183 SegmentItem si
= {pDef
, start
, stop
};
187 AddTail(Segment(start
, stop
, &si
));
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
));
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
));
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
)
229 InsertAfter(pos
, s2
);
232 else if(s
.m_start
== start
)
238 else if(stop
< s
.m_stop
)
242 InsertAfter(pos
, s2
);
252 size_t SubtitleFile::SegmentList::Index(bool fForce
)
254 if(m_index
.IsEmpty() || fForce
)
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
)
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
)
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
;}
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"
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"
322 L
"time#time {scale: 1;}; \n"
323 L
"time#startstop {start: \"start\"; stop: \"stop\";}; \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"
330 L
"#nobr {linebreak: \"none\"}; \n"
332 L
"subtitle#subtitle \n"
336 L
" reference: \"video\"; \n"
337 L
" resolution: {cx: 640; cy: 480;}; \n"
342 L
" primary: \"right\"; \n"
343 L
" secondary: \"down\"; \n"
346 L
" wrap: \"normal\"; \n"
352 L
" linebreak: \"word\"; \n"
356 L
" clip: \"none\"; \n"
357 L
" margin: {t: 0; r: 0; b: 0; l: 0;}; \n"
358 L
" align: bottomcenter; \n"
360 L
" offset: {x: 0; y: 0;}; \n"
361 L
" angle: {x: 0; y: 0; z: 0;}; \n"
368 L
" face: \"Arial\"; \n"
370 L
" weight: \"bold\"; \n"
372 L
" underline: \"false\"; \n"
373 L
" strikethrough: \"false\"; \n"
374 L
" italic: \"false\"; \n"
376 L
" scale: {cx: 1; cy: 1;}; \n"
377 L
" kerning: \"true\"; \n"
384 L
" type: \"outline\"; \n"
390 L
" color: black {a: 128;}; \n"
398 L
" color: yellow; \n"