Move some methods to where they make more sense.
[libdcp.git] / src / types.cc
1 /*
2     Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
6     libdcp is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     libdcp is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with libdcp.  If not, see <http://www.gnu.org/licenses/>.
18
19     In addition, as a special exception, the copyright holders give
20     permission to link the code of portions of this program with the
21     OpenSSL library under certain conditions as described in each
22     individual source file, and distribute linked combinations
23     including the two.
24
25     You must obey the GNU General Public License in all respects
26     for all of the code used other than OpenSSL.  If you modify
27     file(s) with this exception, you may extend this exception to your
28     version of the file(s), but you are not obligated to do so.  If you
29     do not wish to do so, delete this exception statement from your
30     version.  If you delete this exception statement from all source
31     files in the program, then also delete it here.
32 */
33
34 #include "raw_convert.h"
35 #include "types.h"
36 #include "exceptions.h"
37 #include "compose.hpp"
38 #include "dcp_assert.h"
39 #include <boost/algorithm/string.hpp>
40 #include <vector>
41 #include <cstdio>
42 #include <iomanip>
43
44 using namespace std;
45 using namespace dcp;
46 using namespace boost;
47
48 bool dcp::operator== (dcp::Size const & a, dcp::Size const & b)
49 {
50         return (a.width == b.width && a.height == b.height);
51 }
52
53 bool dcp::operator!= (dcp::Size const & a, dcp::Size const & b)
54 {
55         return !(a == b);
56 }
57
58 ostream& dcp::operator<< (ostream& s, dcp::Size const & a)
59 {
60         s << a.width << "x" << a.height;
61         return s;
62 }
63
64 /** Construct a Fraction from a string of the form <numerator> <denominator>
65  *  e.g. "1 3".
66  */
67 Fraction::Fraction (string s)
68 {
69         vector<string> b;
70         split (b, s, is_any_of (" "));
71         if (b.size() != 2) {
72                 boost::throw_exception (XMLError ("malformed fraction " + s + " in XML node"));
73         }
74         numerator = raw_convert<int> (b[0]);
75         denominator = raw_convert<int> (b[1]);
76 }
77
78 string
79 Fraction::as_string () const
80 {
81         return String::compose ("%1 %2", numerator, denominator);
82 }
83
84 bool
85 dcp::operator== (Fraction const & a, Fraction const & b)
86 {
87         return (a.numerator == b.numerator && a.denominator == b.denominator);
88 }
89
90 bool
91 dcp::operator!= (Fraction const & a, Fraction const & b)
92 {
93         return (a.numerator != b.numerator || a.denominator != b.denominator);
94 }
95
96 ostream&
97 dcp::operator<< (ostream& s, Fraction const & f)
98 {
99         s << f.numerator << "/" << f.denominator;
100         return s;
101 }
102
103 /** Construct a Colour, initialising it to black. */
104 Colour::Colour ()
105         : r (0)
106         , g (0)
107         , b (0)
108 {
109
110 }
111
112 /** Construct a Colour from R, G and B.  The values run between
113  *  0 and 255.
114  */
115 Colour::Colour (int r_, int g_, int b_)
116         : r (r_)
117         , g (g_)
118         , b (b_)
119 {
120
121 }
122
123 /** Construct a Colour from an ARGB hex string; the alpha value is ignored.
124  *  @param argb_hex A string of the form AARRGGBB, where e.g. RR is a two-character
125  *  hex value.
126  */
127 Colour::Colour (string argb_hex)
128 {
129         int alpha;
130         if (sscanf (argb_hex.c_str(), "%2x%2x%2x%2x", &alpha, &r, &g, &b) != 4) {
131                 boost::throw_exception (XMLError ("could not parse colour string"));
132         }
133 }
134
135 /** @return An ARGB string of the form AARRGGBB, where e.g. RR is a two-character
136  *  hex value.  The alpha value will always be FF (ie 255; maximum alpha).
137  */
138 string
139 Colour::to_argb_string () const
140 {
141         char buffer[9];
142         snprintf (buffer, sizeof(buffer), "FF%02X%02X%02X", r, g, b);
143         return buffer;
144 }
145
146 /** @return An RGB string of the form RRGGBB, where e.g. RR is a two-character
147  *  hex value.
148  */
149 string
150 Colour::to_rgb_string () const
151 {
152         char buffer[7];
153         snprintf (buffer, sizeof(buffer), "%02X%02X%02X", r, g, b);
154         return buffer;
155 }
156
157 /** operator== for Colours.
158  *  @param a First colour to compare.
159  *  @param b Second colour to compare.
160  */
161 bool
162 dcp::operator== (Colour const & a, Colour const & b)
163 {
164         return (a.r == b.r && a.g == b.g && a.b == b.b);
165 }
166
167 /** operator!= for Colours.
168  *  @param a First colour to compare.
169  *  @param b Second colour to compare.
170  */
171 bool
172 dcp::operator!= (Colour const & a, Colour const & b)
173 {
174         return !(a == b);
175 }
176
177 ostream &
178 dcp::operator<< (ostream& s, Colour const & c)
179 {
180         s << "(" << c.r << ", " << c.g << ", " << c.b << ")";
181         return s;
182 }
183
184 string
185 dcp::effect_to_string (Effect e)
186 {
187         switch (e) {
188         case NONE:
189                 return "none";
190         case BORDER:
191                 return "border";
192         case SHADOW:
193                 return "shadow";
194         }
195
196         boost::throw_exception (MiscError ("unknown effect type"));
197 }
198
199 Effect
200 dcp::string_to_effect (string s)
201 {
202         if (s == "none") {
203                 return NONE;
204         } else if (s == "border") {
205                 return BORDER;
206         } else if (s == "shadow") {
207                 return SHADOW;
208         }
209
210         boost::throw_exception (DCPReadError ("unknown subtitle effect type"));
211 }
212
213 string
214 dcp::halign_to_string (HAlign h)
215 {
216         switch (h) {
217         case HALIGN_LEFT:
218                 return "left";
219         case HALIGN_CENTER:
220                 return "center";
221         case HALIGN_RIGHT:
222                 return "right";
223         }
224
225         boost::throw_exception (MiscError ("unknown subtitle halign type"));
226 }
227
228 HAlign
229 dcp::string_to_halign (string s)
230 {
231         if (s == "left") {
232                 return HALIGN_LEFT;
233         } else if (s == "center") {
234                 return HALIGN_CENTER;
235         } else if (s == "right") {
236                 return HALIGN_RIGHT;
237         }
238
239         boost::throw_exception (DCPReadError ("unknown subtitle halign type"));
240 }
241
242 string
243 dcp::valign_to_string (VAlign v)
244 {
245         switch (v) {
246         case VALIGN_TOP:
247                 return "top";
248         case VALIGN_CENTER:
249                 return "center";
250         case VALIGN_BOTTOM:
251                 return "bottom";
252         }
253
254         boost::throw_exception (MiscError ("unknown subtitle valign type"));
255 }
256
257 VAlign
258 dcp::string_to_valign (string s)
259 {
260         if (s == "top") {
261                 return VALIGN_TOP;
262         } else if (s == "center") {
263                 return VALIGN_CENTER;
264         } else if (s == "bottom") {
265                 return VALIGN_BOTTOM;
266         }
267
268         boost::throw_exception (DCPReadError ("unknown subtitle valign type"));
269 }
270
271 string
272 dcp::direction_to_string (Direction v)
273 {
274         switch (v) {
275         case DIRECTION_LTR:
276                 return "ltr";
277         case DIRECTION_RTL:
278                 return "rtl";
279         case DIRECTION_TTB:
280                 return "ttb";
281         case DIRECTION_BTT:
282                 return "btt";
283         }
284
285         boost::throw_exception (MiscError ("unknown subtitle direction type"));
286 }
287
288 Direction
289 dcp::string_to_direction (string s)
290 {
291         if (s == "ltr" || s == "horizontal") {
292                 return DIRECTION_LTR;
293         } else if (s == "rtl") {
294                 return DIRECTION_RTL;
295         } else if (s == "ttb" || s == "vertical") {
296                 return DIRECTION_TTB;
297         } else if (s == "btt") {
298                 return DIRECTION_BTT;
299         }
300
301         boost::throw_exception (DCPReadError ("unknown subtitle direction type"));
302 }
303
304 /** Convert a content kind to a string which can be used in a
305  *  &lt;ContentKind&gt; node.
306  *  @param kind ContentKind.
307  *  @return string.
308  */
309 string
310 dcp::content_kind_to_string (ContentKind kind)
311 {
312         switch (kind) {
313         case FEATURE:
314                 return "feature";
315         case SHORT:
316                 return "short";
317         case TRAILER:
318                 return "trailer";
319         case TEST:
320                 return "test";
321         case TRANSITIONAL:
322                 return "transitional";
323         case RATING:
324                 return "rating";
325         case TEASER:
326                 return "teaser";
327         case POLICY:
328                 return "policy";
329         case PUBLIC_SERVICE_ANNOUNCEMENT:
330                 return "psa";
331         case ADVERTISEMENT:
332                 return "advertisement";
333         }
334
335         DCP_ASSERT (false);
336 }
337
338 /** Convert a string from a &lt;ContentKind&gt; node to a libdcp ContentKind.
339  *  Reasonably tolerant about varying case.
340  *  @param kind Content kind string.
341  *  @return libdcp ContentKind.
342  */
343 dcp::ContentKind
344 dcp::content_kind_from_string (string kind)
345 {
346         transform (kind.begin(), kind.end(), kind.begin(), ::tolower);
347
348         if (kind == "feature") {
349                 return FEATURE;
350         } else if (kind == "short") {
351                 return SHORT;
352         } else if (kind == "trailer") {
353                 return TRAILER;
354         } else if (kind == "test") {
355                 return TEST;
356         } else if (kind == "transitional") {
357                 return TRANSITIONAL;
358         } else if (kind == "rating") {
359                 return RATING;
360         } else if (kind == "teaser") {
361                 return TEASER;
362         } else if (kind == "policy") {
363                 return POLICY;
364         } else if (kind == "psa") {
365                 return PUBLIC_SERVICE_ANNOUNCEMENT;
366         } else if (kind == "advertisement") {
367                 return ADVERTISEMENT;
368         }
369
370         throw BadContentKindError (kind);
371 }