UI: Refactor timecode functions into BLI_timecode
[blender.git] / source / blender / blenlib / intern / timecode.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * 
22  * Contributor(s): Joshua Leung
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/blendlib/intern/timecode.c
28  *  \ingroup blendlib
29  *
30  * Time-Code string formatting
31  */
32
33 #include <stdio.h>
34
35
36 #include "BLI_string.h"
37 #include "BLI_math.h"
38
39 #include "BLI_timecode.h"  /* own include */
40
41 #include "BLI_strict_flags.h"
42
43 #include "DNA_userdef_types.h"  /* for eTimecodeStyles only */
44
45
46 /**
47  * Generate timecode/frame number string and store in \a str
48  *
49  * \param str  destination string
50  * \param maxncpy  maximum number of characters to copy ``sizeof(str)``
51  * \param power  special setting for #View2D grid drawing,
52  *        used to specify how detailed we need to be
53  * \param time_seconds  time total time in seconds
54  * \param fps  frames per second, typically from the #FPS macro
55  * \param timecode_style  enum from eTimecodeStyles
56  * \return length of \a str
57  */
58
59 size_t BLI_timecode_string_from_time(
60         char *str, const size_t maxncpy, const int power, const float time_seconds,
61         const double fps, const short timecode_style)
62 {
63         int hours = 0, minutes = 0, seconds = 0, frames = 0;
64         float time = time_seconds;
65         char neg[2] = {'\0'};
66         size_t rlen;
67
68         /* get cframes */
69         if (time < 0) {
70                 /* correction for negative cfraues */
71                 neg[0] = '-';
72                 time = -time;
73         }
74
75         if (time >= 3600) {
76                 /* hours */
77                 /* XXX should we only display a single digit for hours since clips are
78                  *     VERY UNLIKELY to be more than 1-2 hours max? However, that would
79                  *     go against conventions...
80                  */
81                 hours = (int)time / 3600;
82                 time = (float)fmod(time, 3600);
83         }
84
85         if (time >= 60) {
86                 /* minutes */
87                 minutes = (int)time / 60;
88                 time = (float)fmod(time, 60);
89         }
90
91         if (power <= 0) {
92                 /* seconds + frames
93                  * Frames are derived from 'fraction' of second. We need to perform some additional rounding
94                  * to cope with 'half' frames, etc., which should be fine in most cases
95                  */
96                 seconds = (int)time;
97                 frames = iroundf((float)(((double)time - (double)seconds) * fps));
98         }
99         else {
100                 /* seconds (with pixel offset rounding) */
101                 seconds = iroundf(time);
102         }
103
104         switch (timecode_style) {
105                 case USER_TIMECODE_MINIMAL:
106                 {
107                         /* - In general, minutes and seconds should be shown, as most clips will be
108                          *   within this length. Hours will only be included if relevant.
109                          * - Only show frames when zoomed in enough for them to be relevant
110                          *   (using separator of '+' for frames).
111                          *   When showing frames, use slightly different display to avoid confusion with mm:ss format
112                          */
113                         if (power <= 0) {
114                                 /* include "frames" in display */
115                                 if (hours) {
116                                         rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d:%02d+%02d", neg, hours, minutes, seconds, frames);
117                                 }
118                                 else if (minutes) {
119                                         rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d+%02d", neg, minutes, seconds, frames);
120                                 }
121                                 else {
122                                         rlen = BLI_snprintf(str, maxncpy, "%s%d+%02d", neg, seconds, frames);
123                                 }
124                         }
125                         else {
126                                 /* don't include 'frames' in display */
127                                 if (hours) {
128                                         rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d:%02d", neg, hours, minutes, seconds);
129                                 }
130                                 else {
131                                         rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d", neg, minutes, seconds);
132                                 }
133                         }
134                         break;
135                 }
136                 case USER_TIMECODE_SMPTE_MSF:
137                 {
138                         /* reduced SMPTE format that always shows minutes, seconds, frames. Hours only shown as needed. */
139                         if (hours) {
140                                 rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d:%02d:%02d", neg, hours, minutes, seconds, frames);
141                         }
142                         else {
143                                 rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d:%02d", neg, minutes, seconds, frames);
144                         }
145                         break;
146                 }
147                 case USER_TIMECODE_MILLISECONDS:
148                 {
149                         /* reduced SMPTE. Instead of frames, milliseconds are shown */
150
151                         /* precision of decimal part */
152                         const int ms_dp = (power <= 0) ? (1 - power) : 1;
153
154                         /* to get 2 digit whole-number part for seconds display
155                          * (i.e. 3 is for 2 digits + radix, on top of full length) */
156                         const int s_pad = ms_dp + 3;
157
158                         if (hours) {
159                                 rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d:%0*.*f", neg, hours, minutes, s_pad, ms_dp, time);
160                         }
161                         else {
162                                 rlen = BLI_snprintf(str, maxncpy, "%s%02d:%0*.*f", neg, minutes, s_pad,  ms_dp, time);
163                         }
164                         break;
165                 }
166                 case USER_TIMECODE_SECONDS_ONLY:
167                 {
168                         /* only show the original seconds display */
169                         /* round to whole numbers if power is >= 1 (i.e. scale is coarse) */
170                         if (power <= 0) {
171                                 rlen = BLI_snprintf(str, maxncpy, "%.*f", 1 - power, time_seconds);
172                         }
173                         else {
174                                 rlen = BLI_snprintf(str, maxncpy, "%d", iroundf(time_seconds));
175                         }
176                         break;
177                 }
178                 case USER_TIMECODE_SMPTE_FULL:
179                 default:
180                 {
181                         /* full SMPTE format */
182                         rlen = BLI_snprintf(str, maxncpy, "%s%02d:%02d:%02d:%02d", neg, hours, minutes, seconds, frames);
183                         break;
184                 }
185         }
186
187         return rlen;
188 }
189
190
191 /**
192  * Generate time string and store in \a str
193  *
194  * \param str  destination string
195  * \param maxncpy  maximum number of characters to copy ``sizeof(str)``
196  * \param power  special setting for #View2D grid drawing,
197  *        used to specify how detailed we need to be
198  * \param time_seconds  time total time in seconds
199  * \param seconds  time in seconds.
200  * \return length of \a str
201  *
202  * \note in some cases this is used to print non-seconds values.
203  */
204 size_t BLI_timecode_string_from_time_simple(
205         char *str, const size_t maxncpy, const int power, const float time_seconds)
206 {
207         size_t rlen;
208
209         /* round to whole numbers if power is >= 1 (i.e. scale is coarse) */
210         if (power <= 0) {
211                 rlen = BLI_snprintf(str, maxncpy, "%.*f", 1 - power, time_seconds);
212         }
213         else {
214                 rlen = BLI_snprintf(str, maxncpy, "%d", iroundf(time_seconds));
215         }
216
217         return rlen;
218 }