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