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