Add some timing logs to image_proxy.
[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
28 #include "i18n.h"
29
30 using std::cout;
31 using std::string;
32 using std::stringstream;
33 using boost::shared_ptr;
34
35 RawImageProxy::RawImageProxy (shared_ptr<Image> image)
36         : _image (image)
37 {
38
39 }
40
41 RawImageProxy::RawImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket)
42 {
43         libdcp::Size size (
44                 xml->number_child<int> ("Width"), xml->number_child<int> ("Height")
45                 );
46
47         _image.reset (new Image (PIX_FMT_RGB24, size, true));
48         _image->read_from_socket (socket);
49 }
50
51 shared_ptr<Image>
52 RawImageProxy::image () const
53 {
54         return _image;
55 }
56
57 void
58 RawImageProxy::add_metadata (xmlpp::Node* node) const
59 {
60         node->add_child("Type")->add_child_text (N_("Raw"));
61         node->add_child("Width")->add_child_text (libdcp::raw_convert<string> (_image->size().width));
62         node->add_child("Height")->add_child_text (libdcp::raw_convert<string> (_image->size().height));
63 }
64
65 void
66 RawImageProxy::send_binary (shared_ptr<Socket> socket) const
67 {
68         _image->write_to_socket (socket);
69 }
70
71 MagickImageProxy::MagickImageProxy (boost::filesystem::path path)
72 {
73         /* Read the file into a Blob */
74         
75         boost::uintmax_t const size = boost::filesystem::file_size (path);
76         FILE* f = fopen_boost (path, "rb");
77         if (!f) {
78                 throw OpenFileError (path);
79         }
80                 
81         uint8_t* data = new uint8_t[size];
82         if (fread (data, 1, size, f) != size) {
83                 delete[] data;
84                 throw ReadFileError (path);
85         }
86         
87         fclose (f);
88         _blob.update (data, size);
89         delete[] data;
90 }
91
92 MagickImageProxy::MagickImageProxy (shared_ptr<cxml::Node>, shared_ptr<Socket> socket)
93 {
94         uint32_t const size = socket->read_uint32 ();
95         uint8_t* data = new uint8_t[size];
96         socket->read (data, size);
97         _blob.update (data, size);
98         delete[] data;
99 }
100
101 shared_ptr<Image>
102 MagickImageProxy::image () const
103 {
104         if (_image) {
105                 return _image;
106         }
107
108         TIMING("MagickImageProxy begins read and decode of %1 bytes", _blob.length());
109
110         Magick::Image* magick_image = 0;
111         try {
112                 magick_image = new Magick::Image (_blob);
113         } catch (...) {
114                 throw DecodeError (_("Could not decode image file"));
115         }
116
117         libdcp::Size size (magick_image->columns(), magick_image->rows());
118
119         _image.reset (new Image (PIX_FMT_RGB24, size, true));
120
121         using namespace MagickCore;
122         
123         uint8_t* p = _image->data()[0];
124         for (int y = 0; y < size.height; ++y) {
125                 uint8_t* q = p;
126                 for (int x = 0; x < size.width; ++x) {
127                         Magick::Color c = magick_image->pixelColor (x, y);
128                         *q++ = c.redQuantum() * 255 / QuantumRange;
129                         *q++ = c.greenQuantum() * 255 / QuantumRange;
130                         *q++ = c.blueQuantum() * 255 / QuantumRange;
131                 }
132                 p += _image->stride()[0];
133         }
134
135         delete magick_image;
136
137         TIMING("MagickImageProxy completes read and decode of %1 bytes", _blob.length());
138
139         return _image;
140 }
141
142 void
143 MagickImageProxy::add_metadata (xmlpp::Node* node) const
144 {
145         node->add_child("Type")->add_child_text (N_("Magick"));
146 }
147
148 void
149 MagickImageProxy::send_binary (shared_ptr<Socket> socket) const
150 {
151         socket->write (_blob.length ());
152         socket->write ((uint8_t *) _blob.data (), _blob.length ());
153 }
154
155 shared_ptr<ImageProxy>
156 image_proxy_factory (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket)
157 {
158         if (xml->string_child("Type") == N_("Raw")) {
159                 return shared_ptr<ImageProxy> (new RawImageProxy (xml, socket));
160         } else if (xml->string_child("Type") == N_("Magick")) {
161                 return shared_ptr<MagickImageProxy> (new MagickImageProxy (xml, socket));
162         }
163
164         throw NetworkError (_("Unexpected image type received by server"));
165 }