ISDCF name fixes with > 6 channels and HI/VI (#1633).
[dcpomatic.git] / src / lib / util.cc
index 0b6f4be72b63c7ce1edbbc30fc06f5f33b19b5c0..765fc3bf5135b45a7f26e1e72c30ff22452dde9f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -41,6 +41,9 @@
 #include "string_text.h"
 #include "font.h"
 #include "render_text.h"
+#include "ffmpeg_image_proxy.h"
+#include "image.h"
+#include "text_decoder.h"
 #include <dcp/locale_convert.h>
 #include <dcp/util.h>
 #include <dcp/raw_convert.h>
@@ -59,6 +62,7 @@ extern "C" {
 #include <boost/range/algorithm/replace_if.hpp>
 #include <boost/thread.hpp>
 #include <boost/filesystem.hpp>
+#include <boost/locale.hpp>
 #ifdef DCPOMATIC_WINDOWS
 #include <boost/locale.hpp>
 #include <dbghelp.h>
@@ -77,6 +81,7 @@ extern "C" {
 #include "i18n.h"
 
 using std::string;
+using std::wstring;
 using std::setfill;
 using std::ostream;
 using std::endl;
@@ -374,7 +379,7 @@ dcpomatic_setup ()
        Pango::init ();
        dcp::init ();
 
-#ifdef DCPOMATIC_WINDOWS
+#if defined(DCPOMATIC_WINDOWS) || defined(DCPOMATIC_OSX)
        /* Render something to fontconfig to create its cache */
        list<StringText> subs;
        dcp::SubtitleString ss(
@@ -466,7 +471,7 @@ digest_head_tail (vector<boost::filesystem::path> files, boost::uintmax_t size)
        while (i < int64_t (files.size()) && to_do > 0) {
                FILE* f = fopen_boost (files[i], "rb");
                if (!f) {
-                       throw OpenFileError (files[i].string(), errno, true);
+                       throw OpenFileError (files[i].string(), errno, OpenFileError::READ);
                }
 
                boost::uintmax_t this_time = min (to_do, boost::filesystem::file_size (files[i]));
@@ -486,7 +491,7 @@ digest_head_tail (vector<boost::filesystem::path> files, boost::uintmax_t size)
        while (i >= 0 && to_do > 0) {
                FILE* f = fopen_boost (files[i], "rb");
                if (!f) {
-                       throw OpenFileError (files[i].string(), errno, true);
+                       throw OpenFileError (files[i].string(), errno, OpenFileError::READ);
                }
 
                boost::uintmax_t this_time = min (to_do, boost::filesystem::file_size (files[i]));
@@ -740,20 +745,39 @@ careful_string_filter (string s)
           Safety first and all that.
        */
 
+       wstring ws = boost::locale::conv::utf_to_utf<wchar_t>(s);
+
        string out;
        string const allowed = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_%.+";
-       for (size_t i = 0; i < s.size(); ++i) {
-               if (allowed.find (s[i]) != string::npos) {
-                       out += s[i];
+       for (size_t i = 0; i < ws.size(); ++i) {
+
+               wchar_t c = ws[i];
+
+               /* Remove some accents */
+               if (wstring(L"áàâ").find(c) != string::npos) {
+                       c = 'a';
+               }
+               if (wstring(L"éèêë").find(c) != string::npos) {
+                       c = 'e';
+               }
+               if (wstring(L"ö").find(c) != string::npos) {
+                       c = 'o';
+               }
+               if (wstring(L"ü").find(c) != string::npos) {
+                       c = 'u';
+               }
+
+               if (allowed.find(c) != string::npos) {
+                       out += c;
                }
        }
 
-       return out;
+       return boost::locale::conv::utf_to_utf<char>(out);
 }
 
 /** @param mapped List of mapped audio channels from a Film.
  *  @param channels Total number of channels in the Film.
- *  @return First: number of non-LFE channels, second: number of LFE channels.
+ *  @return First: number of non-LFE soundtrack channels (L/R/C/Ls/Rs/Lc/Rc/Bsl/Bsr), second: number of LFE channels.
  */
 pair<int, int>
 audio_channel_types (list<int> mapped, int channels)
@@ -767,10 +791,24 @@ audio_channel_types (list<int> mapped, int channels)
                        continue;
                }
 
-               if (static_cast<dcp::Channel> (i) == dcp::LFE) {
+               switch (static_cast<dcp::Channel>(i)) {
+               case dcp::LFE:
                        ++lfe;
-               } else {
+                       break;
+               case dcp::LEFT:
+               case dcp::RIGHT:
+               case dcp::CENTRE:
+               case dcp::LS:
+               case dcp::RS:
+               case dcp::LC:
+               case dcp::RC:
+               case dcp::BSL:
+               case dcp::BSR:
                        ++non_lfe;
+                       break;
+               case dcp::HI:
+               case dcp::VI:
+                       break;
                }
        }
 
@@ -881,6 +919,45 @@ day_of_week_to_string (boost::gregorian::greg_weekday d)
        return d.as_long_string ();
 }
 
+/** @param size Size of picture that the subtitle will be overlaid onto */
+void
+emit_subtitle_image (ContentTimePeriod period, dcp::SubtitleImage sub, dcp::Size size, shared_ptr<TextDecoder> decoder)
+{
+       /* XXX: this is rather inefficient; decoding the image just to get its size */
+       FFmpegImageProxy proxy (sub.png_image());
+       shared_ptr<Image> image = proxy.image().first;
+       /* set up rect with height and width */
+       dcpomatic::Rect<double> rect(0, 0, image->size().width / double(size.width), image->size().height / double(size.height));
+
+       /* add in position */
+
+       switch (sub.h_align()) {
+       case dcp::HALIGN_LEFT:
+               rect.x += sub.h_position();
+               break;
+       case dcp::HALIGN_CENTER:
+               rect.x += 0.5 + sub.h_position() - rect.width / 2;
+               break;
+       case dcp::HALIGN_RIGHT:
+               rect.x += 1 - sub.h_position() - rect.width;
+               break;
+       }
+
+       switch (sub.v_align()) {
+       case dcp::VALIGN_TOP:
+               rect.y += sub.v_position();
+               break;
+       case dcp::VALIGN_CENTER:
+               rect.y += 0.5 + sub.v_position() - rect.height / 2;
+               break;
+       case dcp::VALIGN_BOTTOM:
+               rect.y += 1 - sub.v_position() - rect.height;
+               break;
+       }
+
+       decoder->emit_bitmap (period, image, rect);
+}
+
 #ifdef DCPOMATIC_VARIANT_SWAROOP
 
 /* Make up a key from the machine UUID */