Fix non-variant build.
[dcpomatic.git] / src / lib / magick_image_proxy.cc
1 /*
2     Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic 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     DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 #include "magick_image_proxy.h"
22 #include "cross.h"
23 #include "exceptions.h"
24 #include "dcpomatic_socket.h"
25 #include "image.h"
26 #include "compose.hpp"
27 #include <Magick++.h>
28 #include <libxml++/libxml++.h>
29 #include <iostream>
30
31 #include "i18n.h"
32
33 using std::string;
34 using std::cout;
35 using std::pair;
36 using std::make_pair;
37 using boost::shared_ptr;
38 using boost::optional;
39 using boost::dynamic_pointer_cast;
40
41 MagickImageProxy::MagickImageProxy (boost::filesystem::path path)
42         : _path (path)
43 {
44         /* Read the file into a Blob */
45
46         boost::uintmax_t const size = boost::filesystem::file_size (path);
47         FILE* f = fopen_boost (path, "rb");
48         if (!f) {
49                 throw OpenFileError (path, errno, true);
50         }
51
52         uint8_t* data = new uint8_t[size];
53         if (fread (data, 1, size, f) != size) {
54                 delete[] data;
55                 throw ReadFileError (path);
56         }
57
58         fclose (f);
59         _blob.update (data, size);
60         delete[] data;
61 }
62
63 MagickImageProxy::MagickImageProxy (shared_ptr<cxml::Node>, shared_ptr<Socket> socket)
64 {
65         uint32_t const size = socket->read_uint32 ();
66         uint8_t* data = new uint8_t[size];
67         socket->read (data, size);
68         _blob.update (data, size);
69         delete[] data;
70 }
71
72 pair<shared_ptr<Image>, int>
73 MagickImageProxy::image (optional<dcp::NoteHandler>, optional<dcp::Size>) const
74 {
75         boost::mutex::scoped_lock lm (_mutex);
76
77         if (_image) {
78                 return make_pair (_image, 0);
79         }
80
81         Magick::Image* magick_image = 0;
82         string error;
83         try {
84                 magick_image = new Magick::Image (_blob);
85         } catch (Magick::Exception& e) {
86                 error = e.what ();
87         }
88
89         if (!magick_image) {
90                 /* ImageMagick cannot auto-detect Targa files, it seems, so try here with an
91                    explicit format.  I can't find it documented that passing a (0, 0) geometry
92                    is allowed, but it seems to work.
93                 */
94                 try {
95                         magick_image = new Magick::Image (_blob, Magick::Geometry (0, 0), "TGA");
96                 } catch (...) {
97
98                 }
99         }
100
101         if (!magick_image) {
102                 /* If we failed both an auto-detect and a forced-Targa we give the error from
103                    the auto-detect.
104                 */
105                 if (_path) {
106                         throw DecodeError (String::compose (_("Could not decode image file %1 (%2)"), _path->string(), error));
107                 } else {
108                         throw DecodeError (String::compose (_("Could not decode image file (%1)"), error));
109                 }
110         }
111
112         unsigned char const * data = static_cast<unsigned char const *>(_blob.data());
113         if (data[801] == 1 || magick_image->image()->colorspace == Magick::sRGBColorspace) {
114                 /* Either:
115                    1.  The transfer characteristic in this file is "printing density"; in this case ImageMagick sets the colour space
116                        to LogColorspace, or
117                    2.  The file is sRGB.
118
119                    Empirically we find that in these cases if we subsequently call colorSpace(Magick::RGBColorspace) the colours
120                    are very wrong.  To prevent this, set the image colour space to RGB to stop the ::colorSpace call below doing
121                    anything.  See #1123 and others.
122                 */
123                 magick_image->image()->colorspace = Magick::RGBColorspace;
124         }
125
126         magick_image->colorSpace(Magick::RGBColorspace);
127
128         dcp::Size size (magick_image->columns(), magick_image->rows());
129
130         _image.reset (new Image (AV_PIX_FMT_RGB24, size, true));
131
132         /* Write line-by-line here as _image must be aligned, and write() cannot be told about strides */
133         uint8_t* p = _image->data()[0];
134         for (int i = 0; i < size.height; ++i) {
135 #ifdef DCPOMATIC_HAVE_MAGICKCORE_NAMESPACE
136                 using namespace MagickCore;
137 #endif
138 #ifdef DCPOMATIC_HAVE_MAGICKLIB_NAMESPACE
139                 using namespace MagickLib;
140 #endif
141                 magick_image->write (0, i, size.width, 1, "RGB", CharPixel, p);
142                 p += _image->stride()[0];
143         }
144
145         delete magick_image;
146
147         return make_pair (_image, 0);
148 }
149
150 void
151 MagickImageProxy::add_metadata (xmlpp::Node* node) const
152 {
153         node->add_child("Type")->add_child_text (N_("Magick"));
154 }
155
156 void
157 MagickImageProxy::send_binary (shared_ptr<Socket> socket) const
158 {
159         socket->write (_blob.length ());
160         socket->write ((uint8_t *) _blob.data (), _blob.length ());
161 }
162
163 bool
164 MagickImageProxy::same (shared_ptr<const ImageProxy> other) const
165 {
166         shared_ptr<const MagickImageProxy> mp = dynamic_pointer_cast<const MagickImageProxy> (other);
167         if (!mp) {
168                 return false;
169         }
170
171         if (_blob.length() != mp->_blob.length()) {
172                 return false;
173         }
174
175         return memcmp (_blob.data(), mp->_blob.data(), _blob.length()) == 0;
176 }
177
178 AVPixelFormat
179 MagickImageProxy::pixel_format () const
180 {
181         return AV_PIX_FMT_RGB24;
182 }
183
184 size_t
185 MagickImageProxy::memory_used () const
186 {
187         size_t m = _blob.length();
188         if (_image) {
189                 m += _image->memory_used();
190         }
191         return m;
192 }