Blender  V2.93
merge.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2019 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "render/merge.h"
18 
19 #include "util/util_array.h"
20 #include "util/util_map.h"
21 #include "util/util_system.h"
22 #include "util/util_time.h"
23 #include "util/util_unique_ptr.h"
24 
25 #include <OpenImageIO/filesystem.h>
26 #include <OpenImageIO/imageio.h>
27 
28 OIIO_NAMESPACE_USING
29 
31 
32 /* Merge Image Layer */
33 
39 };
40 
42  /* Full channel name. */
43  string channel_name;
44  /* Channel format in the file. */
45  TypeDesc format;
46  /* Type of operation to perform when merging. */
48  /* Offset of layer channels in input image. */
49  int offset;
50  /* Offset of layer channels in merged image. */
52 };
53 
55  /* Layer name. */
56  string name;
57  /* Passes. */
59  /* Sample amount that was used for rendering this layer. */
60  int samples;
61 };
62 
63 /* Merge Image */
64 
65 struct MergeImage {
66  /* OIIO file handle. */
67  unique_ptr<ImageInput> in;
68  /* Image file path. */
69  string filepath;
70  /* Render layers. */
72 };
73 
74 /* Channel Parsing */
75 
76 static MergeChannelOp parse_channel_operation(const string &pass_name)
77 {
78  if (pass_name == "Depth" || pass_name == "IndexMA" || pass_name == "IndexOB" ||
79  string_startswith(pass_name, "Crypto")) {
80  return MERGE_CHANNEL_COPY;
81  }
82  else if (string_startswith(pass_name, "Debug BVH") ||
83  string_startswith(pass_name, "Debug Ray") ||
84  string_startswith(pass_name, "Debug Render Time")) {
85  return MERGE_CHANNEL_SUM;
86  }
87  else {
88  return MERGE_CHANNEL_AVERAGE;
89  }
90 }
91 
92 /* Splits in at its last dot, setting suffix to the part after the dot and
93  * into the part before it. Returns whether a dot was found. */
94 static bool split_last_dot(string &in, string &suffix)
95 {
96  size_t pos = in.rfind(".");
97  if (pos == string::npos) {
98  return false;
99  }
100  suffix = in.substr(pos + 1);
101  in = in.substr(0, pos);
102  return true;
103 }
104 
105 /* Separate channel names as generated by Blender.
106  * Multiview format: RenderLayer.Pass.View.Channel
107  * Otherwise: RenderLayer.Pass.Channel */
108 static bool parse_channel_name(
109  string name, string &renderlayer, string &pass, string &channel, bool multiview_channels)
110 {
111  if (!split_last_dot(name, channel)) {
112  return false;
113  }
114  string view;
115  if (multiview_channels && !split_last_dot(name, view)) {
116  return false;
117  }
118  if (!split_last_dot(name, pass)) {
119  return false;
120  }
121  renderlayer = name;
122 
123  if (multiview_channels) {
124  renderlayer += "." + view;
125  }
126 
127  return true;
128 }
129 
130 static bool parse_channels(const ImageSpec &in_spec,
131  vector<MergeImageLayer> &layers,
132  string &error)
133 {
134  const ParamValue *multiview = in_spec.find_attribute("multiView");
135  const bool multiview_channels = (multiview && multiview->type().basetype == TypeDesc::STRING &&
136  multiview->type().arraylen >= 2);
137 
138  layers.clear();
139 
140  /* Loop over all the channels in the file, parse their name and sort them
141  * by RenderLayer.
142  * Channels that can't be parsed are directly passed through to the output. */
143  map<string, MergeImageLayer> file_layers;
144  for (int i = 0; i < in_spec.nchannels; i++) {
145  MergeImagePass pass;
146  pass.channel_name = in_spec.channelnames[i];
147  pass.format = (in_spec.channelformats.size() > 0) ? in_spec.channelformats[i] : in_spec.format;
148  pass.offset = i;
149  pass.merge_offset = i;
150 
151  string layername, passname, channelname;
153  pass.channel_name, layername, passname, channelname, multiview_channels)) {
154  /* Channel part of a render layer. */
155  pass.op = parse_channel_operation(passname);
156  }
157  else {
158  /* Other channels are added in unnamed layer. */
159  layername = "";
161  }
162 
163  file_layers[layername].passes.push_back(pass);
164  }
165 
166  /* Loop over all detected render-layers, check whether they contain a full set of input channels.
167  * Any channels that won't be processed internally are also passed through. */
168  for (auto &i : file_layers) {
169  const string &name = i.first;
170  MergeImageLayer &layer = i.second;
171 
172  layer.name = name;
173  layer.samples = 0;
174 
175  /* Determine number of samples from metadata. */
176  if (layer.name == "") {
177  layer.samples = 1;
178  }
179  else if (layer.samples < 1) {
180  string sample_string = in_spec.get_string_attribute("cycles." + name + ".samples", "");
181  if (sample_string != "") {
182  if (!sscanf(sample_string.c_str(), "%d", &layer.samples)) {
183  error = "Failed to parse samples metadata: " + sample_string;
184  return false;
185  }
186  }
187  }
188 
189  if (layer.samples < 1) {
191  "No sample number specified in the file for layer %s or on the command line",
192  name.c_str());
193  return false;
194  }
195 
196  layers.push_back(layer);
197  }
198 
199  return true;
200 }
201 
202 static bool open_images(const vector<string> &filepaths, vector<MergeImage> &images, string &error)
203 {
204  for (const string &filepath : filepaths) {
205  unique_ptr<ImageInput> in(ImageInput::open(filepath));
206  if (!in) {
207  error = "Couldn't open file: " + filepath;
208  return false;
209  }
210 
211  MergeImage image;
212  image.in = std::move(in);
213  image.filepath = filepath;
214  if (!parse_channels(image.in->spec(), image.layers, error)) {
215  return false;
216  }
217 
218  if (image.layers.size() == 0) {
219  error = "Could not find a render layer for merging";
220  return false;
221  }
222 
223  if (image.in->spec().deep) {
224  error = "Merging deep images not supported.";
225  return false;
226  }
227 
228  if (images.size() > 0) {
229  const ImageSpec &base_spec = images[0].in->spec();
230  const ImageSpec &spec = image.in->spec();
231 
232  if (base_spec.width != spec.width || base_spec.height != spec.height ||
233  base_spec.depth != spec.depth || base_spec.format != spec.format ||
234  base_spec.deep != spec.deep) {
235  error = "Images do not have matching size and data layout.";
236  return false;
237  }
238  }
239 
240  images.push_back(std::move(image));
241  }
242 
243  return true;
244 }
245 
246 static void merge_render_time(ImageSpec &spec,
247  const vector<MergeImage> &images,
248  const string &name,
249  const bool average)
250 {
251  double time = 0.0;
252 
253  for (const MergeImage &image : images) {
254  string time_str = image.in->spec().get_string_attribute(name, "");
256  }
257 
258  if (average) {
259  time /= images.size();
260  }
261 
262  spec.attribute(name, TypeDesc::STRING, time_human_readable_from_seconds(time));
263 }
264 
265 static void merge_layer_render_time(ImageSpec &spec,
266  const vector<MergeImage> &images,
267  const string &layer_name,
268  const string &time_name,
269  const bool average)
270 {
271  string name = "cycles." + layer_name + "." + time_name;
272  double time = 0.0;
273 
274  for (const MergeImage &image : images) {
275  string time_str = image.in->spec().get_string_attribute(name, "");
277  }
278 
279  if (average) {
280  time /= images.size();
281  }
282 
283  spec.attribute(name, TypeDesc::STRING, time_human_readable_from_seconds(time));
284 }
285 
287  ImageSpec &out_spec,
288  vector<int> &channel_total_samples)
289 {
290  /* Based on first image. */
291  out_spec = images[0].in->spec();
292 
293  /* Merge channels and compute offsets. */
294  out_spec.nchannels = 0;
295  out_spec.channelformats.clear();
296  out_spec.channelnames.clear();
297 
298  for (MergeImage &image : images) {
299  for (MergeImageLayer &layer : image.layers) {
300  for (MergeImagePass &pass : layer.passes) {
301  /* Test if matching channel already exists in merged image. */
302  bool found = false;
303 
304  for (size_t i = 0; i < out_spec.nchannels; i++) {
305  if (pass.channel_name == out_spec.channelnames[i]) {
306  pass.merge_offset = i;
307  channel_total_samples[i] += layer.samples;
308  /* First image wins for channels that can't be averaged or summed. */
309  if (pass.op == MERGE_CHANNEL_COPY) {
310  pass.op = MERGE_CHANNEL_NOP;
311  }
312  found = true;
313  break;
314  }
315  }
316 
317  if (!found) {
318  /* Add new channel. */
319  pass.merge_offset = out_spec.nchannels;
320  channel_total_samples.push_back(layer.samples);
321 
322  out_spec.channelnames.push_back(pass.channel_name);
323  out_spec.channelformats.push_back(pass.format);
324  out_spec.nchannels++;
325  }
326  }
327  }
328  }
329 
330  /* Merge metadata. */
331  merge_render_time(out_spec, images, "RenderTime", false);
332 
333  map<string, int> layer_num_samples;
334  for (MergeImage &image : images) {
335  for (MergeImageLayer &layer : image.layers) {
336  if (layer.name != "") {
337  layer_num_samples[layer.name] += layer.samples;
338  }
339  }
340  }
341 
342  for (const auto &i : layer_num_samples) {
343  string name = "cycles." + i.first + ".samples";
344  out_spec.attribute(name, TypeDesc::STRING, string_printf("%d", i.second));
345 
346  merge_layer_render_time(out_spec, images, i.first, "total_time", false);
347  merge_layer_render_time(out_spec, images, i.first, "render_time", false);
348  merge_layer_render_time(out_spec, images, i.first, "synchronization_time", true);
349  }
350 }
351 
352 static void alloc_pixels(const ImageSpec &spec, array<float> &pixels)
353 {
354  const size_t width = spec.width;
355  const size_t height = spec.height;
356  const size_t num_channels = spec.nchannels;
357 
358  const size_t num_pixels = (size_t)width * (size_t)height;
359  pixels.resize(num_pixels * num_channels);
360 }
361 
362 static bool merge_pixels(const vector<MergeImage> &images,
363  const ImageSpec &out_spec,
364  const vector<int> &channel_total_samples,
365  array<float> &out_pixels,
366  string &error)
367 {
368  alloc_pixels(out_spec, out_pixels);
369  memset(out_pixels.data(), 0, out_pixels.size() * sizeof(float));
370 
371  for (const MergeImage &image : images) {
372  /* Read all channels into buffer. Reading all channels at once is
373  * faster than individually due to interleaved EXR channel storage. */
374  array<float> pixels;
375  alloc_pixels(image.in->spec(), pixels);
376 
377  if (!image.in->read_image(TypeDesc::FLOAT, pixels.data())) {
378  error = "Failed to read image: " + image.filepath;
379  return false;
380  }
381 
382  for (size_t li = 0; li < image.layers.size(); li++) {
383  const MergeImageLayer &layer = image.layers[li];
384 
385  const size_t stride = image.in->spec().nchannels;
386  const size_t out_stride = out_spec.nchannels;
387  const size_t num_pixels = pixels.size();
388 
389  for (const MergeImagePass &pass : layer.passes) {
390  size_t offset = pass.offset;
391  size_t out_offset = pass.merge_offset;
392 
393  switch (pass.op) {
394  case MERGE_CHANNEL_NOP:
395  break;
396  case MERGE_CHANNEL_COPY:
397  for (; offset < num_pixels; offset += stride, out_offset += out_stride) {
398  out_pixels[out_offset] = pixels[offset];
399  }
400  break;
401  case MERGE_CHANNEL_SUM:
402  for (; offset < num_pixels; offset += stride, out_offset += out_stride) {
403  out_pixels[out_offset] += pixels[offset];
404  }
405  break;
407  /* Weights based on sample metadata. Per channel since not
408  * all files are guaranteed to have the same channels. */
409  const int total_samples = channel_total_samples[out_offset];
410  const float t = (float)layer.samples / (float)total_samples;
411 
412  for (; offset < num_pixels; offset += stride, out_offset += out_stride) {
413  out_pixels[out_offset] += t * pixels[offset];
414  }
415  break;
416  }
417  }
418  }
419  }
420 
421  return true;
422 }
423 
424 static bool save_output(const string &filepath,
425  const ImageSpec &spec,
426  const array<float> &pixels,
427  string &error)
428 {
429  /* Write to temporary file path, so we merge images in place and don't
430  * risk destroying files when something goes wrong in file saving. */
431  string extension = OIIO::Filesystem::extension(filepath);
432  string unique_name = ".merge-tmp-" + OIIO::Filesystem::unique_path();
433  string tmp_filepath = filepath + unique_name + extension;
434  unique_ptr<ImageOutput> out(ImageOutput::create(tmp_filepath));
435 
436  if (!out) {
437  error = "Failed to open temporary file " + tmp_filepath + " for writing";
438  return false;
439  }
440 
441  /* Open temporary file and write image buffers. */
442  if (!out->open(tmp_filepath, spec)) {
443  error = "Failed to open file " + tmp_filepath + " for writing: " + out->geterror();
444  return false;
445  }
446 
447  bool ok = true;
448  if (!out->write_image(TypeDesc::FLOAT, pixels.data())) {
449  error = "Failed to write to file " + tmp_filepath + ": " + out->geterror();
450  ok = false;
451  }
452 
453  if (!out->close()) {
454  error = "Failed to save to file " + tmp_filepath + ": " + out->geterror();
455  ok = false;
456  }
457 
458  out.reset();
459 
460  /* Copy temporary file to output filepath. */
461  string rename_error;
462  if (ok && !OIIO::Filesystem::rename(tmp_filepath, filepath, rename_error)) {
463  error = "Failed to move merged image to " + filepath + ": " + rename_error;
464  ok = false;
465  }
466 
467  if (!ok) {
468  OIIO::Filesystem::remove(tmp_filepath);
469  }
470 
471  return ok;
472 }
473 
474 /* Image Merger */
475 
477 {
478 }
479 
481 {
482  if (input.empty()) {
483  error = "No input file paths specified.";
484  return false;
485  }
486  if (output.empty()) {
487  error = "No output file path specified.";
488  return false;
489  }
490 
491  /* Open images and verify they have matching layout. */
492  vector<MergeImage> images;
493  if (!open_images(input, images, error)) {
494  return false;
495  }
496 
497  /* Merge metadata and setup channels and offsets. */
498  ImageSpec out_spec;
499  vector<int> channel_total_samples;
500  merge_channels_metadata(images, out_spec, channel_total_samples);
501 
502  /* Merge pixels. */
503  array<float> out_pixels;
504  if (!merge_pixels(images, out_spec, channel_total_samples, out_pixels, error)) {
505  return false;
506  }
507 
508  /* We don't need input anymore at this point, and will possibly
509  * overwrite the same file. */
510  images.clear();
511 
512  /* Save output file. */
513  return save_output(output, out_spec, out_pixels, error);
514 }
515 
typedef float(TangentPoint)[2]
static AppView * view
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei stride
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble t
bool run()
Definition: merge.cpp:480
string output
Definition: merge.h:38
vector< string > input
Definition: merge.h:36
ImageMerger()
Definition: merge.cpp:476
string error
Definition: merge.h:33
T * data()
Definition: util_array.h:208
size_t size() const
Definition: util_array.h:203
T * resize(size_t newsize)
Definition: util_array.h:150
double time
uint pos
#define CCL_NAMESPACE_END
static bool parse_channels(const ImageSpec &in_spec, vector< MergeImageLayer > &layers, string &error)
Definition: merge.cpp:130
static bool parse_channel_name(string name, string &renderlayer, string &pass, string &channel, bool multiview_channels)
Definition: merge.cpp:108
static void merge_channels_metadata(vector< MergeImage > &images, ImageSpec &out_spec, vector< int > &channel_total_samples)
Definition: merge.cpp:286
static bool split_last_dot(string &in, string &suffix)
Definition: merge.cpp:94
MergeChannelOp
Definition: merge.cpp:34
@ MERGE_CHANNEL_AVERAGE
Definition: merge.cpp:38
@ MERGE_CHANNEL_SUM
Definition: merge.cpp:37
@ MERGE_CHANNEL_NOP
Definition: merge.cpp:35
@ MERGE_CHANNEL_COPY
Definition: merge.cpp:36
static bool open_images(const vector< string > &filepaths, vector< MergeImage > &images, string &error)
Definition: merge.cpp:202
static void merge_layer_render_time(ImageSpec &spec, const vector< MergeImage > &images, const string &layer_name, const string &time_name, const bool average)
Definition: merge.cpp:265
static void alloc_pixels(const ImageSpec &spec, array< float > &pixels)
Definition: merge.cpp:352
static bool merge_pixels(const vector< MergeImage > &images, const ImageSpec &out_spec, const vector< int > &channel_total_samples, array< float > &out_pixels, string &error)
Definition: merge.cpp:362
static void merge_render_time(ImageSpec &spec, const vector< MergeImage > &images, const string &name, const bool average)
Definition: merge.cpp:246
static MergeChannelOp parse_channel_operation(const string &pass_name)
Definition: merge.cpp:76
static bool save_output(const string &filepath, const ImageSpec &spec, const array< float > &pixels, string &error)
Definition: merge.cpp:424
static void error(const char *str)
Definition: meshlaplacian.c:65
static void unique_name(bNode *node)
vector< MergeImagePass > passes
Definition: merge.cpp:58
string name
Definition: merge.cpp:56
string channel_name
Definition: merge.cpp:43
int merge_offset
Definition: merge.cpp:51
TypeDesc format
Definition: merge.cpp:45
MergeChannelOp op
Definition: merge.cpp:47
vector< MergeImageLayer > layers
Definition: merge.cpp:71
string filepath
Definition: merge.cpp:69
unique_ptr< ImageInput > in
Definition: merge.cpp:67
ccl_device_inline float average(const float2 &a)
bool string_startswith(const string &s, const char *start)
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
Definition: util_string.cpp:32
double time_human_readable_to_seconds(const string &time_string)
Definition: util_time.cpp:93
string time_human_readable_from_seconds(const double seconds)
Definition: util_time.cpp:78