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