1 /* === S Y N F I G ========================================================= */
2 /*! \file time.cpp
3 ** \brief Template File
4 **
5 ** $Id$
6 **
7 ** \legal
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2007, 2008 Chris Moore
10 ** Copyright (c) 2008 Gerco Ballintijn
11 ** Copyright (c) 2008 Carlos López
12 **
13 ** This package is free software; you can redistribute it and/or
14 ** modify it under the terms of the GNU General Public License as
15 ** published by the Free Software Foundation; either version 2 of
16 ** the License, or (at your option) any later version.
17 **
18 ** This package is distributed in the hope that it will be useful,
19 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 ** General Public License for more details.
22 ** \endlegal
23 */
24 /* ========================================================================= */
25
26 /* === H E A D E R S ======================================================= */
27
28 #ifdef USING_PCH
29 # include "pch.h"
30 #else
31 #ifdef HAVE_CONFIG_H
32 # include <config.h>
33 #endif
34
35 #include "time.h"
36 #include <ETL/stringf>
37 #include <ETL/misc>
38 #include "general.h"
39 #include <synfig/localization.h>
40 #include <cmath>
41 #include <cassert>
42 #include <algorithm>
43 #include <cstdio>
44 #include <ctype.h>
45
46 #endif
47
48 /* === U S I N G =========================================================== */
49
50 using namespace std;
51 using namespace etl;
52 using namespace synfig;
53
54 #define tolower ::tolower
55
56 /* === M A C R O S ========================================================= */
57
58 /* === G L O B A L S ======================================================= */
59
60 /* === M E T H O D S ======================================================= */
61
Time(const String & str_,float fps)62 Time::Time(const String &str_, float fps):
63 value_(0)
64 {
65 String str(str_);
66 std::transform(str.begin(),str.end(),str.begin(),&tolower);
67
68 // Start/Begin Of Time
69 if(str=="sot" || str=="bot")
70 {
71 operator=(begin());
72 return;
73 }
74 // End Of Time
75 if(str=="eot")
76 {
77 operator=(end());
78 return;
79 }
80
81
82 unsigned int pos=0;
83 int read;
84 float amount;
85
86 // Now try to read it in the letter-abbreviated format
87 while(pos<str.size() && sscanf(String(str,pos).c_str(),"%f%n",&amount,&read))
88 {
89 pos+=read;
90 if(pos>=str.size() || read==0)
91 {
92 // Throw up a warning if there are no units
93 // and the amount isn't zero. There is no need
94 // to warn about units if the value is zero
95 // it is the only case where units are irrelevant.
96 if(amount!=0 && fps)
97 {
98 synfig::warning(_("Time(): No unit provided in time code, assuming FRAMES (\"%s\")"),str.c_str());
99 value_+=amount/fps;
100 }
101 else
102 {
103 synfig::warning(_("Time(): No unit provided in time code and frame rate is unknown! Assuming SECONDS"));
104 value_+=amount;
105 }
106 return;
107 }
108 switch(str[pos])
109 {
110 case 'h':
111 case 'H':
112 value_+=amount*3600;
113 break;
114 case 'm':
115 case 'M':
116 value_+=amount*60;
117 break;
118 case 's':
119 case 'S':
120 value_+=amount;
121 break;
122 case 'f':
123 case 'F':
124 if(fps)
125 value_+=amount/fps;
126 else
127 synfig::warning("Time(): Individual frames referenced, but frame rate is unknown");
128 break;
129 case ':':
130 // try to read it in as a traditional time format
131 {
132 int hour,minute,second;
133 float frame;
134 if(fps && sscanf(str.c_str(),"%d:%d:%d.%f",&hour,&minute,&second,&frame)==4)
135 {
136 value_=frame/fps+(hour*3600+minute*60+second);
137 return;
138 }
139
140 if(sscanf(str.c_str(),"%d:%d:%d",&hour,&minute,&second)==3)
141 {
142 value_=hour*3600+minute*60+second;
143 return;
144 }
145 }
146 synfig::warning("Time(): Bad time format");
147 break;
148
149 default:
150 value_+=amount;
151 synfig::warning("Time(): Unexpected character '%c' when parsing time string \"%s\"",str[pos],str.c_str());
152 break;
153 }
154 pos++;
155 amount=0;
156 }
157 }
158
159 String
get_string(float fps,Time::Format format) const160 Time::get_string(float fps, Time::Format format)const
161 {
162 Time time(*this);
163
164 if(time<=begin())
165 return "SOT"; // Start Of Time
166 if(time>=end())
167 return "EOT"; // End Of Time
168
169 if(fps<0)fps=0;
170
171 if(ceil(time.value_)-time.value_<epsilon_())
172 time.value_=ceil(time.value_);
173
174 int hour = 0, minute = 0;
175 if(!(format<=FORMAT_FRAMES))
176 {
177 hour=time/3600;time-=hour*3600;
178 minute=time/60;time-=minute*60;
179 }
180 // <= is redefined, so this means "is the FORMAT_VIDEO bit set in the format?"
181 if(format<=FORMAT_VIDEO)
182 {
183 int second;
184 second=time;time-=second;
185
186 if(fps && fps>1)
187 {
188 int frame;
189 frame=round_to_int(time*fps);
190
191 return strprintf("%02d:%02d:%02d.%02d",hour,minute,second,frame);
192 }
193 else
194 return strprintf("%02d:%02d:%02d",hour,minute,second);
195 }
196
197 if (format <= FORMAT_FRAMES)
198 {
199 if (fps && fps>0)
200 return strprintf("%df", round_to_int(time * fps));
201 else
202 return strprintf("%ds", round_to_int(time * 1));
203 }
204
205 String ret;
206 bool started = false;
207
208 if(format<=FORMAT_FULL || hour)
209 {
210 ret+=strprintf("%dh",hour);
211 started = true;
212 }
213
214 if(format<=FORMAT_FULL || minute)
215 {
216 if (!(format<=FORMAT_NOSPACES) && started)
217 ret += " ";
218
219 ret += strprintf("%dm", minute);
220 started = true;
221 }
222
223 if(fps && fps>1)
224 {
225 int second;
226 float frame;
227 second=time;time-=second;
228 frame=time*fps;
229
230 if(format<=FORMAT_FULL || second)
231 {
232 if (!(format<=FORMAT_NOSPACES) && started)
233 ret += " ";
234
235 ret += strprintf("%ds", (int)second);
236 started = true;
237 }
238
239 if(format<=FORMAT_FULL || abs(frame) > epsilon_() || !started)
240 {
241 if (!(format<=FORMAT_NOSPACES) && started)
242 ret += " ";
243
244 if(abs(frame-floor(frame) >= epsilon_()))
245 ret += strprintf("%0.3ff", frame);
246 else
247 ret += strprintf("%0.0ff", frame);
248 }
249 }
250 else
251 {
252 float second;
253 second=time;
254 if(format<=FORMAT_FULL || second || !started)
255 {
256 if (!(format<=FORMAT_NOSPACES) && started)
257 ret += " ";
258
259 if(abs(second-floor(second))>=epsilon_())
260 {
261 String seconds(strprintf("%0.8f",second));
262
263 // skip trailing zeros
264 int count = 0;
265 String::reverse_iterator i = seconds.rbegin();
266 for ( ; (*i) == '0'; i++)
267 count++;
268
269 // if we removed too many, go back one place, leaving one zero
270 if (*i < '0' || *i > '9') count--;
271
272 ret += seconds.substr(0, seconds.size()-count) + "s";
273 }
274 else
275 ret+=strprintf("%0.0fs",second);
276 }
277 }
278
279 return ret;
280 }
281
282 Time
round(float fps) const283 Time::round(float fps)const
284 {
285 assert(fps>0);
286
287 value_type time(*this);
288
289 time*=fps;
290
291 if(abs(time-floor(time))<0.5)
292 return floor(time)/fps;
293 else
294 return ceil(time)/fps;
295 }
296
297 #ifdef _DEBUG
298 const char *
c_str() const299 Time::c_str()const
300 {
301 return get_string().c_str();
302 }
303 #endif
304
305 //! \writeme
306 bool
is_valid() const307 Time::is_valid()const
308 {
309 return !std::isnan(value_);
310 }
311