Fix build on Debian unstable which now has GraphicsMagick rather than ImageMagick.
[dcpomatic.git] / src / lib / image_proxy.cc
1 /*
2     Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (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
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <Magick++.h>
21 #include <libdcp/util.h>
22 #include <libdcp/raw_convert.h>
23 #include "image_proxy.h"
24 #include "image.h"
25 #include "exceptions.h"
26 #include "cross.h"
27 #include "log.h"
28
29 #include "i18n.h"
30
31 #define LOG_TIMING(...) _log->microsecond_log (String::compose (__VA_ARGS__), Log::TYPE_TIMING);
32
33 using std::cout;
34 using std::string;
35 using boost::shared_ptr;
36
37 ImageProxy::ImageProxy (shared_ptr<Log> log)
38         : _log (log)
39 {
40
41 }
42
43 RawImageProxy::RawImageProxy (shared_ptr<Image> image, shared_ptr<Log> log)
44         : ImageProxy (log)
45         , _image (image)
46 {
47
48 }
49
50 RawImageProxy::RawImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket, shared_ptr<Log> log)
51         : ImageProxy (log)
52 {
53         libdcp::Size size (
54                 xml->number_child<int> ("Width"), xml->number_child<int> ("Height")
55                 );
56
57         _image.reset (new Image (static_cast<AVPixelFormat> (xml->number_child<int> ("PixelFormat")), size, true));
58         _image->read_from_socket (socket);
59 }
60
61 shared_ptr<Image>
62 RawImageProxy::image () const
63 {
64         return _image;
65 }
66
67 void
68 RawImageProxy::add_metadata (xmlpp::Node* node) const
69 {
70         node->add_child("Type")->add_child_text (N_("Raw"));
71         node->add_child("Width")->add_child_text (libdcp::raw_convert<string> (_image->size().width));
72         node->add_child("Height")->add_child_text (libdcp::raw_convert<string> (_image->size().height));
73         node->add_child("PixelFormat")->add_child_text (libdcp::raw_convert<string> (_image->pixel_format ()));
74 }
75
76 void
77 RawImageProxy::send_binary (shared_ptr<Socket> socket) const
78 {
79         _image->write_to_socket (socket);
80 }
81
82 MagickImageProxy::MagickImageProxy (boost::filesystem::path path, shared_ptr<Log> log)
83         : ImageProxy (log)
84 {
85         /* Read the file into a Blob */
86         
87         boost::uintmax_t const size = boost::filesystem::file_size (path);
88         FILE* f = fopen_boost (path, "rb");
89         if (!f) {
90                 throw OpenFileError (path);
91         }
92                 
93         uint8_t* data = new uint8_t[size];
94         if (fread (data, 1, size, f) != size) {
95                 delete[] data;
96                 throw ReadFileError (path);
97         }
98         
99         fclose (f);
100         _blob.update (data, size);
101         delete[] data;
102 }
103
104 MagickImageProxy::MagickImageProxy (shared_ptr<cxml::Node>, shared_ptr<Socket> socket, shared_ptr<Log> log)
105         : ImageProxy (log)
106 {
107         uint32_t const size = socket->read_uint32 ();
108         uint8_t* data = new uint8_t[size];
109         socket->read (data, size);
110         _blob.update (data, size);
111         delete[] data;
112 }
113
114 shared_ptr<Image>
115 MagickImageProxy::image () const
116 {
117         if (_image) {
118                 return _image;
119         }
120
121         LOG_TIMING ("[%1] MagickImageProxy begins decode and convert of %2 bytes", boost::this_thread::get_id(), _blob.length());
122
123         Magick::Image* magick_image = 0;
124         string error;
125         try {
126                 magick_image = new Magick::Image (_blob);
127         } catch (Magick::Exception& e) {
128                 error = e.what ();
129         }
130
131         if (!magick_image) {
132                 /* ImageMagick cannot auto-detect Targa files, it seems, so try here with an
133                    explicit format.  I can't find it documented that passing a (0, 0) geometry
134                    is allowed, but it seems to work.
135                 */
136                 try {
137                         magick_image = new Magick::Image (_blob, Magick::Geometry (0, 0), "TGA");
138                 } catch (...) {
139
140                 }
141         }
142
143         if (!magick_image) {
144                 /* If we failed both an auto-detect and a forced-Targa we give the error from
145                    the auto-detect.
146                 */
147                 throw DecodeError (String::compose (_("Could not decode image file (%1)"), error));
148         }
149
150         LOG_TIMING ("[%1] MagickImageProxy decode finished", boost::this_thread::get_id ());
151
152         libdcp::Size size (magick_image->columns(), magick_image->rows());
153
154         _image.reset (new Image (PIX_FMT_RGB24, size, true));
155
156         /* Write line-by-line here as _image must be aligned, and write() cannot be told about strides */
157         uint8_t* p = _image->data()[0];
158         for (int i = 0; i < size.height; ++i) {
159 #ifdef DCPOMATIC_IMAGE_MAGICK           
160                 using namespace MagickCore;
161 #else
162                 using namespace MagickLib;
163 #endif          
164                 magick_image->write (0, i, size.width, 1, "RGB", CharPixel, p);
165                 p += _image->stride()[0];
166         }
167
168         delete magick_image;
169
170         LOG_TIMING ("[%1] MagickImageProxy completes decode and convert of %2 bytes", boost::this_thread::get_id(), _blob.length());
171
172         return _image;
173 }
174
175 void
176 MagickImageProxy::add_metadata (xmlpp::Node* node) const
177 {
178         node->add_child("Type")->add_child_text (N_("Magick"));
179 }
180
181 void
182 MagickImageProxy::send_binary (shared_ptr<Socket> socket) const
183 {
184         socket->write (_blob.length ());
185         socket->write ((uint8_t *) _blob.data (), _blob.length ());
186 }
187
188 shared_ptr<ImageProxy>
189 image_proxy_factory (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket, shared_ptr<Log> log)
190 {
191         if (xml->string_child("Type") == N_("Raw")) {
192                 return shared_ptr<ImageProxy> (new RawImageProxy (xml, socket, log));
193         } else if (xml->string_child("Type") == N_("Magick")) {
194                 return shared_ptr<MagickImageProxy> (new MagickImageProxy (xml, socket, log));
195         }
196
197         throw NetworkError (_("Unexpected image type received by server"));
198 }