Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-core / tags / stable / src / synfig / time.cpp
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 <cmath>
40 #include <cassert>
41 #include <algorithm>
42 #include <ctype.h>
43 #include <math.h>
44
45
46 #ifdef WIN32
47 #include <float.h>
48 #ifndef isnan
49 extern "C" { int _isnan(double x); }
50 #define isnan _isnan
51 #endif
52 #endif
53
54 // For some reason isnan() isn't working on macosx any more.
55 // This is a quick fix.
56 #if defined(__APPLE__) && !defined(SYNFIG_ISNAN_FIX)
57 #ifdef isnan
58 #undef isnan
59 #endif
60 inline bool isnan(double x) { return x != x; }
61 inline bool isnan(float x) { return x != x; }
62 #define SYNFIG_ISNAN_FIX 1
63 #endif
64
65
66 #endif
67
68 /* === U S I N G =========================================================== */
69
70 using namespace std;
71 using namespace etl;
72 using namespace synfig;
73
74 #define tolower ::tolower
75
76 /* === M A C R O S ========================================================= */
77
78 /* === G L O B A L S ======================================================= */
79
80 /* === M E T H O D S ======================================================= */
81
82 Time::Time(const String &str_, float fps):
83         value_(0)
84 {
85         String str(str_);
86         std::transform(str.begin(),str.end(),str.begin(),&tolower);
87
88         // Start/Begin Of Time
89         if(str=="sot" || str=="bot")
90         {
91                 operator=(begin());
92                 return;
93         }
94         // End Of Time
95         if(str=="eot")
96         {
97                 operator=(end());
98                 return;
99         }
100
101
102         unsigned int pos=0;
103         int read;
104         float amount;
105
106         // Now try to read it in the letter-abbreviated format
107         while(pos<str.size() && sscanf(String(str,pos).c_str(),"%f%n",&amount,&read))
108         {
109                 pos+=read;
110                 if(pos>=str.size() || read==0)
111                 {
112                         // Throw up a warning if there are no units
113                         // and the amount isn't zero. There is no need
114                         // to warn about units if the value is zero
115                         // it is the only case where units are irrelevant.
116                         if(amount!=0)
117                                 synfig::warning("Time(): No unit provided in time code, assuming SECONDS (\"%s\")",str.c_str());
118                         value_+=amount;
119                         return;
120                 }
121                 switch(str[pos])
122                 {
123                         case 'h':
124                         case 'H':
125                                 value_+=amount*3600;
126                                 break;
127                         case 'm':
128                         case 'M':
129                                 value_+=amount*60;
130                                 break;
131                         case 's':
132                         case 'S':
133                                 value_+=amount;
134                                 break;
135                         case 'f':
136                         case 'F':
137                                 if(fps)
138                                         value_+=amount/fps;
139                                 else
140                                         synfig::warning("Time(): Individual frames referenced, but frame rate is unknown");
141                                 break;
142                         case ':':
143                                 // try to read it in as a traditional time format
144                                 {
145                                         int hour,minute,second;
146                                         float frame;
147                                         if(fps && sscanf(str.c_str(),"%d:%d:%d.%f",&hour,&minute,&second,&frame)==4)
148                                         {
149                                                         value_=frame/fps+(hour*3600+minute*60+second);
150                                                         return;
151                                         }
152
153                                         if(sscanf(str.c_str(),"%d:%d:%d",&hour,&minute,&second)==3)
154                                         {
155                                                 value_=hour*3600+minute*60+second;
156                                                 return;
157                                         }
158                                 }
159                                 synfig::warning("Time(): Bad time format");
160                                 break;
161
162                         default:
163                                 value_+=amount;
164                                 synfig::warning("Time(): Unexpected character '%c' when parsing time string \"%s\"",str[pos],str.c_str());
165                                 break;
166                 }
167                 pos++;
168                 amount=0;
169         }
170 }
171
172 String
173 Time::get_string(float fps, Time::Format format)const
174 {
175         Time time(*this);
176
177         if(time<=begin())
178                 return "SOT";   // Start Of Time
179         if(time>=end())
180                 return "EOT";   // End Of Time
181
182         if(fps<0)fps=0;
183
184         if(ceil(time.value_)-time.value_<epsilon_())
185                 time.value_=ceil(time.value_);
186
187         int hour,minute;
188         if(!(format<=FORMAT_FRAMES))
189         {
190                 hour=time/3600;time-=hour*3600;
191                 minute=time/60;time-=minute*60;
192         }
193         // <= is redefined, so this means "is the FORMAT_VIDEO bit set in the format?"
194         if(format<=FORMAT_VIDEO)
195         {
196                 int second;
197                 second=time;time-=second;
198
199                 if(fps && fps>1)
200                 {
201                         int frame;
202                         frame=round_to_int(time*fps);
203
204                         return strprintf("%02d:%02d:%02d.%02d",hour,minute,second,frame);
205                 }
206                 else
207                         return strprintf("%02d:%02d:%02d",hour,minute,second);
208         }
209
210         if (format <= FORMAT_FRAMES)
211         {
212                 if (fps && fps>0)
213                         return strprintf("%df", round_to_int(time * fps));
214                 else
215                         return strprintf("%ds", round_to_int(time * 1));
216         }
217
218         String ret;
219         bool started = false;
220
221         if(format<=FORMAT_FULL || hour)
222         {
223                 ret+=strprintf("%dh",hour);
224                 started = true;
225         }
226
227         if(format<=FORMAT_FULL || minute)
228         {
229                 if (!(format<=FORMAT_NOSPACES) && started)
230                         ret += " ";
231
232                 ret += strprintf("%dm", minute);
233                 started = true;
234         }
235
236         if(fps && fps>1)
237         {
238                 int second;
239                 float frame;
240                 second=time;time-=second;
241                 frame=time*fps;
242
243                 if(format<=FORMAT_FULL || second)
244                 {
245                         if (!(format<=FORMAT_NOSPACES) && started)
246                                 ret += " ";
247
248                         ret += strprintf("%ds", (int)second);
249                         started = true;
250                 }
251
252                 if(format<=FORMAT_FULL || abs(frame) > epsilon_() || !started)
253                 {
254                         if (!(format<=FORMAT_NOSPACES) && started)
255                                 ret += " ";
256
257                         if(abs(frame-floor(frame) >= epsilon_()))
258                                 ret += strprintf("%0.3ff", frame);
259                         else
260                                 ret += strprintf("%0.0ff", frame);
261                 }
262         }
263         else
264         {
265                 float second;
266                 second=time;
267                 if(format<=FORMAT_FULL || second || !started)
268                 {
269                         if (!(format<=FORMAT_NOSPACES) && started)
270                                 ret += " ";
271
272                         if(abs(second-floor(second))>=epsilon_())
273                         {
274                                 String seconds(strprintf("%0.8f",second));
275
276                                 // skip trailing zeros
277                                 int count = 0;
278                                 String::reverse_iterator i = seconds.rbegin();
279                                 for ( ; (*i) == '0'; i++)
280                                         count++;
281
282                                 // if we removed too many, go back one place, leaving one zero
283                                 if (*i < '0' || *i > '9') count--;
284
285                                 ret += seconds.substr(0, seconds.size()-count) + "s";
286                         }
287                         else
288                                 ret+=strprintf("%0.0fs",second);
289                 }
290         }
291
292         return ret;
293 }
294
295 Time
296 Time::round(float fps)const
297 {
298         assert(fps>0);
299
300         value_type time(*this);
301
302         time*=fps;
303
304         if(abs(time-floor(time))<0.5)
305                 return floor(time)/fps;
306         else
307                 return ceil(time)/fps;
308 }
309
310 #ifdef _DEBUG
311 const char *
312 Time::c_str()const
313 {
314         return get_string().c_str();
315 }
316 #endif
317
318 //! \writeme
319 bool
320 Time::is_valid()const
321 {
322         return !isnan(value_);
323 }