47b69ec87f91508b66aead83397b2391e4549b78
[blender-staging.git] / source / blender / compositor / operations / COM_OutputFileOperation.cpp
1 /*
2  * Copyright 2011, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (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 Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor: 
19  *              Jeroen Bakker 
20  *              Monique Dewanchand
21  *              Lukas Tönne
22  */
23
24 #include "COM_OutputFileOperation.h"
25 #include "COM_SocketConnection.h"
26 #include <string.h>
27 #include "BLI_listbase.h"
28 #include "BLI_path_util.h"
29 #include "BLI_string.h"
30 #include "BKE_image.h"
31 #include "BKE_global.h"
32 #include "BKE_main.h"
33
34 #include "DNA_color_types.h"
35
36 extern "C" {
37         #include "MEM_guardedalloc.h"
38         #include "IMB_imbuf.h"
39         #include "IMB_colormanagement.h"
40         #include "IMB_imbuf_types.h"
41 }
42
43 static int get_datatype_size(DataType datatype)
44 {
45         switch (datatype) {
46                 case COM_DT_VALUE:  return 1;
47                 case COM_DT_VECTOR: return 3;
48                 case COM_DT_COLOR:  return 4;
49                 default:            return 0;
50         }
51 }
52
53 static float *init_buffer(unsigned int width, unsigned int height, DataType datatype)
54 {
55         // When initializing the tree during initial load the width and height can be zero.
56         if (width != 0 && height != 0) {
57                 int size = get_datatype_size(datatype);
58                 return (float *)MEM_callocN(width * height * size * sizeof(float), "OutputFile buffer");
59         }
60         else
61                 return NULL;
62 }
63
64 static void write_buffer_rect(rcti *rect, const bNodeTree *tree,
65                               SocketReader *reader, float *buffer, unsigned int width, DataType datatype)
66 {
67         float color[4];
68         int i, size = get_datatype_size(datatype);
69
70         if (!buffer) return;
71         int x1 = rect->xmin;
72         int y1 = rect->ymin;
73         int x2 = rect->xmax;
74         int y2 = rect->ymax;
75         int offset = (y1 * width + x1) * size;
76         int x;
77         int y;
78         bool breaked = false;
79
80         for (y = y1; y < y2 && (!breaked); y++) {
81                 for (x = x1; x < x2 && (!breaked); x++) {
82                         reader->read(color, x, y, COM_PS_NEAREST);
83                         
84                         for (i = 0; i < size; ++i)
85                                 buffer[offset + i] = color[i];
86                         offset += size;
87                         
88                         if (tree->test_break && tree->test_break(tree->tbh))
89                                 breaked = true;
90                 }
91                 offset += (width - (x2 - x1)) * size;
92         }
93 }
94
95
96 OutputSingleLayerOperation::OutputSingleLayerOperation(
97         const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path,
98         const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings)
99 {
100         this->m_rd = rd;
101         this->m_tree = tree;
102         
103         this->addInputSocket(datatype);
104         
105         this->m_outputBuffer = NULL;
106         this->m_datatype = datatype;
107         this->m_imageInput = NULL;
108         
109         this->m_format = format;
110         BLI_strncpy(this->m_path, path, sizeof(this->m_path));
111
112         this->m_viewSettings = viewSettings;
113         this->m_displaySettings = displaySettings;
114 }
115
116 void OutputSingleLayerOperation::initExecution()
117 {
118         this->m_imageInput = getInputSocketReader(0);
119         this->m_outputBuffer = init_buffer(this->getWidth(), this->getHeight(), this->m_datatype);
120 }
121
122 void OutputSingleLayerOperation::executeRegion(rcti *rect, unsigned int tileNumber)
123 {
124         write_buffer_rect(rect, this->m_tree, this->m_imageInput, this->m_outputBuffer, this->getWidth(), this->m_datatype);
125 }
126
127 void OutputSingleLayerOperation::deinitExecution()
128 {
129         if (this->getWidth() * this->getHeight() != 0) {
130                 
131                 int size = get_datatype_size(this->m_datatype);
132                 ImBuf *ibuf = IMB_allocImBuf(this->getWidth(), this->getHeight(), this->m_format->planes, 0);
133                 Main *bmain = G.main; /* TODO, have this passed along */
134                 char filename[FILE_MAX];
135                 
136                 ibuf->channels = size;
137                 ibuf->rect_float = this->m_outputBuffer;
138                 ibuf->mall |= IB_rectfloat; 
139                 ibuf->dither = this->m_rd->dither_intensity;
140                 
141                 IMB_colormanagement_imbuf_for_write(ibuf, TRUE, FALSE, m_viewSettings, m_displaySettings,
142                                                     this->m_format);
143
144                 BKE_makepicstring(filename, this->m_path, bmain->name, this->m_rd->cfra, this->m_format,
145                                   (this->m_rd->scemode & R_EXTENSION), true);
146                 
147                 if (0 == BKE_imbuf_write(ibuf, filename, this->m_format))
148                         printf("Cannot save Node File Output to %s\n", filename);
149                 else
150                         printf("Saved: %s\n", filename);
151                 
152                 IMB_freeImBuf(ibuf);
153         }
154         this->m_outputBuffer = NULL;
155         this->m_imageInput = NULL;
156 }
157
158
159 OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_)
160 {
161         BLI_strncpy(this->name, name_, sizeof(this->name));
162         this->datatype = datatype_;
163         /* these are created in initExecution */
164         this->outputBuffer = 0;
165         this->imageInput = 0;
166 }
167
168 OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(
169     const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec)
170 {
171         this->m_rd = rd;
172         this->m_tree = tree;
173         
174         BLI_strncpy(this->m_path, path, sizeof(this->m_path));
175         this->m_exr_codec = exr_codec;
176 }
177
178 void OutputOpenExrMultiLayerOperation::add_layer(const char *name, DataType datatype)
179 {
180         this->addInputSocket(datatype);
181         this->m_layers.push_back(OutputOpenExrLayer(name, datatype));
182 }
183
184 void OutputOpenExrMultiLayerOperation::initExecution()
185 {
186         for (unsigned int i = 0; i < this->m_layers.size(); ++i) {
187                 this->m_layers[i].imageInput = getInputSocketReader(i);
188                 this->m_layers[i].outputBuffer = init_buffer(this->getWidth(), this->getHeight(), this->m_layers[i].datatype);
189         }
190 }
191
192 void OutputOpenExrMultiLayerOperation::executeRegion(rcti *rect, unsigned int tileNumber)
193 {
194         for (unsigned int i = 0; i < this->m_layers.size(); ++i) {
195                 write_buffer_rect(rect, this->m_tree, this->m_layers[i].imageInput, this->m_layers[i].outputBuffer, this->getWidth(), this->m_layers[i].datatype);
196         }
197 }
198
199 void OutputOpenExrMultiLayerOperation::deinitExecution()
200 {
201         unsigned int width = this->getWidth();
202         unsigned int height = this->getHeight();
203         if (width != 0 && height != 0) {
204                 Main *bmain = G.main; /* TODO, have this passed along */
205                 char filename[FILE_MAX];
206                 void *exrhandle = IMB_exr_get_handle();
207                 
208                 BKE_makepicstring_from_type(filename, this->m_path, bmain->name, this->m_rd->cfra, R_IMF_IMTYPE_MULTILAYER,
209                                   (this->m_rd->scemode & R_EXTENSION), true);
210                 BLI_make_existing_file(filename);
211                 
212                 for (unsigned int i = 0; i < this->m_layers.size(); ++i) {
213                         char channelname[EXR_TOT_MAXNAME];
214                         BLI_strncpy(channelname, this->m_layers[i].name, sizeof(channelname) - 2);
215                         char *channelname_ext = channelname + strlen(channelname);
216                         
217                         float *buf = this->m_layers[i].outputBuffer;
218                         
219                         /* create channels */
220                         switch (this->m_layers[i].datatype) {
221                                 case COM_DT_VALUE:
222                                         strcpy(channelname_ext, ".V");
223                                         IMB_exr_add_channel(exrhandle, 0, channelname, 1, width, buf);
224                                         break;
225                                 case COM_DT_VECTOR:
226                                         strcpy(channelname_ext, ".X");
227                                         IMB_exr_add_channel(exrhandle, 0, channelname, 3, 3 * width, buf);
228                                         strcpy(channelname_ext, ".Y");
229                                         IMB_exr_add_channel(exrhandle, 0, channelname, 3, 3 * width, buf + 1);
230                                         strcpy(channelname_ext, ".Z");
231                                         IMB_exr_add_channel(exrhandle, 0, channelname, 3, 3 * width, buf + 2);
232                                         break;
233                                 case COM_DT_COLOR:
234                                         strcpy(channelname_ext, ".R");
235                                         IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf);
236                                         strcpy(channelname_ext, ".G");
237                                         IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf + 1);
238                                         strcpy(channelname_ext, ".B");
239                                         IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf + 2);
240                                         strcpy(channelname_ext, ".A");
241                                         IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf + 3);
242                                         break;
243                                 default:
244                                         break;
245                         }
246                         
247                 }
248                 
249                 /* when the filename has no permissions, this can fail */
250                 if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec)) {
251                         IMB_exr_write_channels(exrhandle);
252                 }
253                 else {
254                         /* TODO, get the error from openexr's exception */
255                         /* XXX nice way to do report? */
256                         printf("Error Writing Render Result, see console\n");
257                 }
258                 
259                 IMB_exr_close(exrhandle);
260                 for (unsigned int i = 0; i < this->m_layers.size(); ++i) {
261                         if (this->m_layers[i].outputBuffer) {
262                                 MEM_freeN(this->m_layers[i].outputBuffer);
263                                 this->m_layers[i].outputBuffer = NULL;
264                         }
265                         
266                         this->m_layers[i].imageInput = NULL;
267                 }
268         }
269 }