Blender V4.5
image_save.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cerrno>
10#include <cstring>
11
12#include "BLI_fileops.h"
13#include "BLI_index_range.hh"
14#include "BLI_listbase.h"
15#include "BLI_path_utils.hh"
16#include "BLI_string.h"
17#include "BLI_task.hh"
18#include "BLI_vector.hh"
19
20#include "BLT_translation.hh"
21
22#include "DNA_image_types.h"
23
24#include "MEM_guardedalloc.h"
25
27#include "IMB_imbuf.hh"
28#include "IMB_imbuf_types.hh"
29#include "IMB_openexr.hh"
30
31#include "BKE_colortools.hh"
32#include "BKE_global.hh"
33#include "BKE_image.hh"
34#include "BKE_image_format.hh"
35#include "BKE_image_save.hh"
36#include "BKE_library.hh"
37#include "BKE_main.hh"
38#include "BKE_report.hh"
39#include "BKE_scene.hh"
40
41#include "RE_pipeline.h"
42
43using blender::Vector;
44
46 Main *bmain,
47 Scene *scene,
48 Image *ima,
49 ImageUser *iuser,
50 const bool guess_path,
51 const bool save_as_render)
52{
53 /* For saving a tiled image we need an iuser, so use a local one if there isn't already one. */
54 ImageUser save_iuser;
55 if (iuser == nullptr) {
56 BKE_imageuser_default(&save_iuser);
57 iuser = &save_iuser;
58 iuser->scene = scene;
59 }
60
61 *opts = ImageSaveOptions{};
62
63 opts->bmain = bmain;
64 opts->scene = scene;
65 opts->save_as_render = ima->source == IMA_SRC_VIEWER || save_as_render;
66
67 BKE_image_format_init(&opts->im_format, false);
68
69 void *lock;
70 ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock);
71
72 if (ibuf) {
73 const char *ima_colorspace = ima->colorspace_settings.name;
74
75 if (opts->save_as_render) {
76 /* Render/compositor output or user chose to save with render settings. */
77 BKE_image_format_init_for_write(&opts->im_format, scene, nullptr);
78 if (!BKE_image_is_multiview(ima)) {
79 /* In case multiview is disabled,
80 * render settings would be invalid for render result in this area. */
83 }
84 }
85 else {
87 if (ima->source == IMA_SRC_GENERATED &&
89 {
91 }
92
93 /* use the multiview image settings as the default */
96
97 /* Render output: colorspace from render settings. */
99 }
100
101 /* Default to saving in the same colorspace as the image setting. */
102 if (!opts->save_as_render) {
103 STRNCPY(opts->im_format.linear_colorspace_settings.name, ima_colorspace);
104 }
105
107
108 /* Compute filepath, but don't resolve multiview and UDIM which are handled
109 * by the image saving code itself. */
110 BKE_image_user_file_path_ex(bmain, iuser, ima, opts->filepath, false, false);
111
112 /* For movies, replace extension and add the frame number to avoid writing over the movie file
113 * itself and provide a good default file path. */
114 if (ima->source == IMA_SRC_MOVIE) {
115 char filepath_no_ext[FILE_MAX];
116 STRNCPY(filepath_no_ext, opts->filepath);
117 BLI_path_extension_strip(filepath_no_ext);
118 SNPRINTF(opts->filepath, "%s_%.*d", filepath_no_ext, 4, ibuf->fileframe);
120 opts->filepath, sizeof(opts->filepath), &opts->im_format);
121 }
122
123 /* sanitize all settings */
124
125 /* unlikely but just in case */
128 }
129
130 /* some formats don't use quality so fallback to scenes quality */
131 if (opts->im_format.quality == 0) {
132 opts->im_format.quality = scene->r.im_format.quality;
133 }
134
135 /* check for empty path */
136 if (guess_path && opts->filepath[0] == 0) {
137 const bool is_prev_save = !STREQ(G.filepath_last_image, "//");
138 if (opts->save_as_render) {
139 if (is_prev_save) {
140 STRNCPY(opts->filepath, G.filepath_last_image);
141 }
142 else {
143 BLI_path_join(opts->filepath, sizeof(opts->filepath), "//", DATA_("Untitled"));
145 }
146 }
147 else {
148 BLI_path_join(opts->filepath, sizeof(opts->filepath), "//", ima->id.name + 2);
151 is_prev_save ? G.filepath_last_image : BKE_main_blendfile_path(bmain));
152 }
153
154 /* append UDIM marker if not present */
155 if (ima->source == IMA_SRC_TILED && strstr(opts->filepath, "<UDIM>") == nullptr) {
156 int len = strlen(opts->filepath);
157 STR_CONCAT(opts->filepath, len, ".<UDIM>");
158 }
159 }
160 }
161
162 /* Copy for detecting UI changes. */
164 opts->prev_imtype = opts->im_format.imtype;
165
166 BKE_image_release_ibuf(ima, ibuf, lock);
167
168 return (ibuf != nullptr);
169}
170
172{
173 /* Auto update color space when changing save as render and file type. */
174 if (opts->save_as_render) {
175 if (!opts->prev_save_as_render) {
177 BKE_image_format_init_for_write(&opts->im_format, opts->scene, nullptr);
178 }
179 else {
181 }
182 }
183 }
184 else {
185 if (opts->prev_save_as_render) {
186 /* Copy colorspace from image settings. */
188 &image->colorspace_settings);
189 }
190 else if (opts->im_format.imtype != opts->prev_imtype) {
192 }
193 }
194
196 opts->prev_imtype = opts->im_format.imtype;
197}
198
203
205 const char *filepath,
206 const ImageSaveOptions *opts)
207{
208 if (opts->do_newpath) {
209 STRNCPY(ima->filepath, filepath);
210
211 /* only image path, never ibuf */
212 if (opts->relative) {
213 const char *relbase = ID_BLEND_PATH(opts->bmain, &ima->id);
214 BLI_path_rel(ima->filepath, relbase); /* only after saving */
215 }
216 }
217}
218
220 Image *ima,
221 ImBuf *ibuf,
222 int ok,
223 const ImageSaveOptions *opts,
224 const bool save_copy,
225 const char *filepath,
226 bool *r_colorspace_changed)
227{
228 if (!ok) {
230 RPT_ERROR,
231 "Could not write image: %s",
232 errno ? strerror(errno) : "internal error, see console");
233 return;
234 }
235
236 if (save_copy) {
237 return;
238 }
239
240 if (opts->do_newpath) {
241 STRNCPY(ibuf->filepath, filepath);
242 }
243
244 /* The tiled image code-path must call this on its own. */
245 if (ima->source != IMA_SRC_TILED) {
246 image_save_update_filepath(ima, filepath, opts);
247 }
248
249 ibuf->userflags &= ~IB_BITMAPDIRTY;
250
251 /* change type? */
252 if (ima->type == IMA_TYPE_R_RESULT) {
253 ima->type = IMA_TYPE_IMAGE;
254
255 /* workaround to ensure the render result buffer is no longer used
256 * by this image, otherwise can crash when a new render result is
257 * created. */
260 }
262 ima->source = IMA_SRC_FILE;
263 ima->type = IMA_TYPE_IMAGE;
264 ImageTile *base_tile = BKE_image_get_tile(ima, 0);
265 base_tile->gen_flag &= ~IMA_GEN_TILE;
266 }
267
268 /* Update image file color space when saving to another color space. */
269 const bool linear_float_output = BKE_imtype_requires_linear_float(opts->im_format.imtype);
270
271 if (!opts->save_as_render || linear_float_output) {
275 {
278 *r_colorspace_changed = true;
279 }
280 }
281}
282
283static void imbuf_save_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf)
284{
285 if (colormanaged_ibuf != ibuf) {
286 /* This guys might be modified by image buffer write functions,
287 * need to copy them back from color managed image buffer to an
288 * original one, so file type of image is being properly updated.
289 */
290 ibuf->ftype = colormanaged_ibuf->ftype;
291 ibuf->foptions = colormanaged_ibuf->foptions;
292 ibuf->planes = colormanaged_ibuf->planes;
293
294 IMB_freeImBuf(colormanaged_ibuf);
295 }
296}
297
305 Image *ima,
306 ImageUser *iuser,
307 const ImageSaveOptions *opts,
308 bool *r_colorspace_changed)
309{
310 void *lock;
311 ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock);
312 RenderResult *rr = nullptr;
313 bool ok = false;
314
315 if (ibuf == nullptr || (ibuf->byte_buffer.data == nullptr && ibuf->float_buffer.data == nullptr))
316 {
317 BKE_image_release_ibuf(ima, ibuf, lock);
318 return ok;
319 }
320
321 ImBuf *colormanaged_ibuf = nullptr;
322 const bool save_copy = opts->save_copy;
323 const bool save_as_render = opts->save_as_render;
324 const ImageFormatData *imf = &opts->im_format;
325
326 if (ima->type == IMA_TYPE_R_RESULT) {
327 /* enforce user setting for RGB or RGBA, but skip BW */
328 if (opts->im_format.planes == R_IMF_PLANES_RGBA) {
330 }
331 else if (opts->im_format.planes == R_IMF_PLANES_RGB) {
332 ibuf->planes = R_IMF_PLANES_RGB;
333 }
334 }
335 else {
336 /* TODO: better solution, if a 24bit image is painted onto it may contain alpha. */
337 if ((opts->im_format.planes == R_IMF_PLANES_RGBA) &&
338 /* it has been painted onto */
339 (ibuf->userflags & IB_BITMAPDIRTY))
340 {
341 /* checks each pixel, not ideal */
343 }
344 }
345
346 /* we need renderresult for exr and rendered multiview */
347 rr = BKE_image_acquire_renderresult(opts->scene, ima);
348 const bool is_mono = !(rr ? RE_ResultIsMultiView(rr) : BKE_image_is_multiview(ima));
349 const bool is_exr_rr = rr && ELEM(imf->imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) &&
351 const bool is_multilayer = is_exr_rr && (imf->imtype == R_IMF_IMTYPE_MULTILAYER);
352 const int layer = (iuser && !is_multilayer) ? iuser->layer : -1;
353
354 /* error handling */
355 if (rr == nullptr) {
356 if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) {
357 BKE_report(reports, RPT_ERROR, "Did not write, no Multilayer Image");
359 BKE_image_release_ibuf(ima, ibuf, lock);
360 return ok;
361 }
362 }
363 else {
365 if (!BKE_image_is_stereo(ima)) {
367 RPT_ERROR,
368 R"(Did not write, the image doesn't have a "%s" and "%s" views)",
372 BKE_image_release_ibuf(ima, ibuf, lock);
373 return ok;
374 }
375
376 /* It shouldn't ever happen. */
377 if ((BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name)) == nullptr) ||
378 (BLI_findstring(&rr->views, STEREO_RIGHT_NAME, offsetof(RenderView, name)) == nullptr))
379 {
381 RPT_ERROR,
382 R"(Did not write, the image doesn't have a "%s" and "%s" views)",
386 BKE_image_release_ibuf(ima, ibuf, lock);
387 return ok;
388 }
389 }
390 BKE_imbuf_stamp_info(rr, ibuf);
391 }
392
393 /* Don't write permanently into the render-result. */
394 double rr_ppm_prev[2] = {0, 0};
395
396 if (save_as_render && rr) {
397 /* These could be used in the case of a null `rr`, currently they're not though.
398 * Note that setting zero when there is no `rr` is intentional,
399 * this signifies no valid PPM is set. */
400 double ppm[2] = {0, 0};
401 if (opts->scene) {
402 BKE_scene_ppm_get(&opts->scene->r, ppm);
403 }
404 copy_v2_v2_db(rr_ppm_prev, rr->ppm);
405 copy_v2_v2_db(rr->ppm, ppm);
406 }
407
408 /* From now on, calls to #BKE_image_release_renderresult must restore the PPM beforehand. */
409 auto render_result_restore_ppm = [rr, save_as_render, rr_ppm_prev]() {
410 if (save_as_render && rr) {
411 copy_v2_v2_db(rr->ppm, rr_ppm_prev);
412 }
413 };
414
415 /* fancy multiview OpenEXR */
416 if (imf->views_format == R_IMF_VIEWS_MULTIVIEW && is_exr_rr) {
417 /* save render result */
419 reports, rr, opts->filepath, imf, save_as_render, nullptr, layer);
420
421 render_result_restore_ppm();
423 image_save_post(reports, ima, ibuf, ok, opts, true, opts->filepath, r_colorspace_changed);
424 BKE_image_release_ibuf(ima, ibuf, lock);
425 }
426 /* regular mono pipeline */
427 else if (is_mono) {
428 if (is_exr_rr) {
430 reports, rr, opts->filepath, imf, save_as_render, nullptr, layer);
431 }
432 else {
433 colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, imf);
434 ok = BKE_imbuf_write_as(colormanaged_ibuf, opts->filepath, imf, save_copy);
435 imbuf_save_post(ibuf, colormanaged_ibuf);
436 }
437
438 render_result_restore_ppm();
441 ima,
442 ibuf,
443 ok,
444 opts,
445 (is_exr_rr ? true : save_copy),
446 opts->filepath,
447 r_colorspace_changed);
448 BKE_image_release_ibuf(ima, ibuf, lock);
449 }
450 /* individual multiview images */
451 else if (imf->views_format == R_IMF_VIEWS_INDIVIDUAL) {
452 uchar planes = ibuf->planes;
453 const int totviews = (rr ? BLI_listbase_count(&rr->views) : BLI_listbase_count(&ima->views));
454
455 if (!is_exr_rr) {
456 BKE_image_release_ibuf(ima, ibuf, lock);
457 }
458
459 for (int i = 0; i < totviews; i++) {
460 char filepath[FILE_MAX];
461 bool ok_view = false;
462 const char *view = rr ? ((RenderView *)BLI_findlink(&rr->views, i))->name :
463 ((ImageView *)BLI_findlink(&ima->views, i))->name;
464
465 if (is_exr_rr) {
466 BKE_scene_multiview_view_filepath_get(&opts->scene->r, opts->filepath, view, filepath);
468 reports, rr, filepath, imf, save_as_render, view, layer);
469 image_save_post(reports, ima, ibuf, ok_view, opts, true, filepath, r_colorspace_changed);
470 }
471 else {
472 /* copy iuser to get the correct ibuf for this view */
473 ImageUser view_iuser;
474
475 if (iuser) {
476 /* copy iuser to get the correct ibuf for this view */
477 view_iuser = *iuser;
478 }
479 else {
480 BKE_imageuser_default(&view_iuser);
481 }
482
483 view_iuser.view = i;
484 view_iuser.flag &= ~IMA_SHOW_STEREO;
485
486 if (rr) {
487 BKE_image_multilayer_index(rr, &view_iuser);
488 }
489 else {
490 BKE_image_multiview_index(ima, &view_iuser);
491 }
492
493 ibuf = BKE_image_acquire_ibuf(ima, &view_iuser, &lock);
494 ibuf->planes = planes;
495
496 BKE_scene_multiview_view_filepath_get(&opts->scene->r, opts->filepath, view, filepath);
497
498 colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, imf);
499 ok_view = BKE_imbuf_write_as(colormanaged_ibuf, filepath, &opts->im_format, save_copy);
500 imbuf_save_post(ibuf, colormanaged_ibuf);
501 image_save_post(reports, ima, ibuf, ok_view, opts, true, filepath, r_colorspace_changed);
502 BKE_image_release_ibuf(ima, ibuf, lock);
503 }
504 ok &= ok_view;
505 }
506
507 render_result_restore_ppm();
509
510 if (is_exr_rr) {
511 BKE_image_release_ibuf(ima, ibuf, lock);
512 }
513 }
514 /* stereo (multiview) images */
515 else if (opts->im_format.views_format == R_IMF_VIEWS_STEREO_3D) {
516 if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) {
518 reports, rr, opts->filepath, imf, save_as_render, nullptr, layer);
519
520 render_result_restore_ppm();
522 image_save_post(reports, ima, ibuf, ok, opts, true, opts->filepath, r_colorspace_changed);
523 BKE_image_release_ibuf(ima, ibuf, lock);
524 }
525 else {
526 ImBuf *ibuf_stereo[2] = {nullptr};
527
528 uchar planes = ibuf->planes;
529 const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
530
531 /* we need to get the specific per-view buffers */
532 BKE_image_release_ibuf(ima, ibuf, lock);
533 bool stereo_ok = true;
534
535 for (int i = 0; i < 2; i++) {
536 ImageUser view_iuser;
537
538 if (iuser) {
539 view_iuser = *iuser;
540 }
541 else {
542 BKE_imageuser_default(&view_iuser);
543 }
544
545 view_iuser.flag &= ~IMA_SHOW_STEREO;
546
547 if (rr) {
548 int id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name));
549 view_iuser.view = id;
550 BKE_image_multilayer_index(rr, &view_iuser);
551 }
552 else {
553 view_iuser.view = i;
554 BKE_image_multiview_index(ima, &view_iuser);
555 }
556
557 ibuf = BKE_image_acquire_ibuf(ima, &view_iuser, &lock);
558
559 if (ibuf == nullptr) {
561 reports, RPT_ERROR, "Did not write, unexpected error when saving stereo image");
562 BKE_image_release_ibuf(ima, ibuf, lock);
563 stereo_ok = false;
564 break;
565 }
566
567 ibuf->planes = planes;
568
569 /* color manage the ImBuf leaving it ready for saving */
570 colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, imf);
571
572 BKE_image_format_to_imbuf(colormanaged_ibuf, imf);
573
574 /* duplicate buffer to prevent locker issue when using render result */
575 ibuf_stereo[i] = IMB_dupImBuf(colormanaged_ibuf);
576
577 imbuf_save_post(ibuf, colormanaged_ibuf);
578
579 BKE_image_release_ibuf(ima, ibuf, lock);
580 }
581
582 if (stereo_ok) {
583 ibuf = IMB_stereo3d_ImBuf(imf, ibuf_stereo[0], ibuf_stereo[1]);
584
585 /* save via traditional path */
586 if (ibuf) {
587 ok = BKE_imbuf_write_as(ibuf, opts->filepath, imf, save_copy);
588
589 IMB_freeImBuf(ibuf);
590 }
591 }
592
593 for (int i = 0; i < 2; i++) {
594 IMB_freeImBuf(ibuf_stereo[i]);
595 }
596
597 render_result_restore_ppm();
599 }
600 }
601 else {
602 render_result_restore_ppm();
604 BKE_image_release_ibuf(ima, ibuf, lock);
605 }
606
607 return ok;
608}
609
611 ReportList *reports, Main *bmain, Image *ima, ImageUser *iuser, const ImageSaveOptions *opts)
612{
613 /* For saving a tiled image we need an iuser, so use a local one if there isn't already one. */
614 ImageUser save_iuser;
615 if (iuser == nullptr) {
616 BKE_imageuser_default(&save_iuser);
617 iuser = &save_iuser;
618 iuser->scene = opts->scene;
619 }
620
621 bool colorspace_changed = false;
622
623 eUDIM_TILE_FORMAT tile_format;
624 char *udim_pattern = nullptr;
625
626 if (ima->source == IMA_SRC_TILED) {
627 /* Verify filepath for tiled images contains a valid UDIM marker. */
628 udim_pattern = BKE_image_get_tile_strformat(opts->filepath, &tile_format);
629 if (tile_format == UDIM_TILE_FORMAT_NONE) {
631 RPT_ERROR,
632 "When saving a tiled image, the path '%s' must contain a valid UDIM marker",
633 opts->filepath);
634 return false;
635 }
636 }
637
638 /* Save images */
639 bool ok = false;
640 if (ima->source != IMA_SRC_TILED) {
641 ok = image_save_single(reports, ima, iuser, opts, &colorspace_changed);
642 }
643 else {
644 /* Save all the tiles. */
646 ImageSaveOptions tile_opts = *opts;
648 tile_opts.filepath, udim_pattern, tile_format, tile->tile_number);
649
650 iuser->tile = tile->tile_number;
651 ok = image_save_single(reports, ima, iuser, &tile_opts, &colorspace_changed);
652 if (!ok) {
653 break;
654 }
655 }
656
657 /* Set the image path and clear the per-tile generated flag only if all tiles were ok. */
658 if (ok) {
660 tile->gen_flag &= ~IMA_GEN_TILE;
661 }
662 image_save_update_filepath(ima, opts->filepath, opts);
663 }
664 MEM_freeN(udim_pattern);
665 }
666
667 if (colorspace_changed) {
668 BKE_image_signal(bmain, ima, nullptr, IMA_SIGNAL_COLORMANAGE);
669 }
670
671 return ok;
672}
673
674/* OpenEXR saving, single and multilayer. */
675
677 const int width,
678 const int height,
679 const int channels,
680 const ImageFormatData *imf,
681 Vector<float *> &tmp_output_rects)
682{
683 if (imf == nullptr) {
684 return rect;
685 }
686
687 const char *to_colorspace = imf->linear_colorspace_settings.name;
688 if (to_colorspace[0] == '\0' || IMB_colormanagement_space_name_is_scene_linear(to_colorspace)) {
689 return rect;
690 }
691
692 float *output_rect = (float *)MEM_dupallocN(rect);
693 tmp_output_rects.append(output_rect);
694
695 const char *from_colorspace = IMB_colormanagement_role_colorspace_name_get(
698 output_rect, width, height, channels, from_colorspace, to_colorspace, false);
699
700 return output_rect;
701}
702
704 float *input_buffer, int width, int height, int channels, Vector<float *> &temporary_buffers)
705{
706 float *gray_scale_output = MEM_malloc_arrayN<float>(size_t(width) * size_t(height),
707 "Gray Scale Buffer For EXR");
708 temporary_buffers.append(gray_scale_output);
709
711 blender::IndexRange(height), 1, [&](const blender::IndexRange sub_y_range) {
712 for (const int64_t y : sub_y_range) {
713 for (const int64_t x : blender::IndexRange(width)) {
714 const int64_t index = y * int64_t(width) + x;
715 gray_scale_output[index] = IMB_colormanagement_get_luminance(input_buffer +
716 index * channels);
717 }
718 }
719 });
720
721 return gray_scale_output;
722}
723
724static float *image_exr_opaque_alpha_buffer(int width,
725 int height,
726 Vector<float *> &temporary_buffers)
727{
728 float *alpha_output = MEM_malloc_arrayN<float>(size_t(width) * size_t(height),
729 "Opaque Alpha Buffer For EXR");
730 temporary_buffers.append(alpha_output);
731
733 blender::IndexRange(height), 1, [&](const blender::IndexRange sub_y_range) {
734 for (const int64_t y : sub_y_range) {
735 for (const int64_t x : blender::IndexRange(width)) {
736 alpha_output[y * int64_t(width) + x] = 1.0;
737 }
738 }
739 });
740
741 return alpha_output;
742}
743
744static void add_exr_compositing_result(void *exr_handle,
745 const RenderResult *render_result,
746 const ImageFormatData *imf,
747 bool save_as_render,
748 const char *view_name,
749 int layer,
750 Vector<float *> &temporary_buffers)
751{
752 /* Render result has no compositing result. */
753 if (!render_result->have_combined) {
754 return;
755 }
756
757 /* Skip compositing result if we are saving a single layer EXR that is not the compositing
758 * layer, which always has the layer index of 0. */
759 const bool is_multi_layer = !(imf && imf->imtype == R_IMF_IMTYPE_OPENEXR);
760 if (!is_multi_layer && layer != 0) {
761 return;
762 }
763
764 /* Write the compositing result for the view with the given view name, or for all views if no
765 * view name is given. */
766 LISTBASE_FOREACH (RenderView *, render_view, &render_result->views) {
767 if (!render_view->ibuf || !render_view->ibuf->float_buffer.data) {
768 continue;
769 }
770
771 /* If a view name is given, then we skip views that do not match the given view name.
772 * Otherwise, we always add the views. */
773 if (view_name && !STREQ(view_name, render_view->name)) {
774 continue;
775 }
776
777 /* If a view name is given, that means we are writing a single view, so no need to identify the
778 * channel by the view name, and we supply an empty view to the rest of the code. */
779 const char *render_view_name = view_name ? "" : render_view->name;
780
781 /* Compositing results is always a 4-channel RGBA. */
782 const int channels_count_in_buffer = 4;
783 float *output_buffer = (save_as_render) ? image_exr_from_scene_linear_to_output(
784 render_view->ibuf->float_buffer.data,
785 render_result->rectx,
786 render_result->recty,
787 channels_count_in_buffer,
788 imf,
789 temporary_buffers) :
790 render_view->ibuf->float_buffer.data;
791
792 /* For multi-layer EXRs, we write the buffer as is with all its 4 channels. */
793 const bool half_float = (imf && imf->depth == R_IMF_CHAN_DEPTH_16);
794 if (is_multi_layer) {
795 for (int i = 0; i < channels_count_in_buffer; i++) {
796 char passname[EXR_PASS_MAXNAME];
797 RE_render_result_full_channel_name(passname, nullptr, "Combined", nullptr, "RGBA", i);
798 IMB_exr_add_channel(exr_handle,
799 "Composite",
800 passname,
801 render_view_name,
802 channels_count_in_buffer,
803 channels_count_in_buffer * render_result->rectx,
804 output_buffer + i,
805 half_float);
806 }
807 continue;
808 }
809
810 /* For single layer EXR, we only add the channels specified in the image format and do any
811 * needed color format conversion.
812 *
813 * In case of a single required channel, we need to do RGBA to BW conversion. */
814 const int required_channels = imf ? imf->planes / 8 : 4;
815 if (required_channels == 1) {
816 float *gray_scale_output = image_exr_from_rgb_to_bw(output_buffer,
817 render_result->rectx,
818 render_result->recty,
819 channels_count_in_buffer,
820 temporary_buffers);
821 IMB_exr_add_channel(exr_handle,
822 "",
823 "V",
824 render_view_name,
825 1,
826 render_result->rectx,
827 gray_scale_output,
828 half_float);
829 continue;
830 }
831
832 /* Add RGB[A] channels. This will essentially skip the alpha channel if only three channels
833 * were required. */
834 for (int i = 0; i < required_channels; i++) {
835 IMB_exr_add_channel(exr_handle,
836 "",
837 std::string(1, "RGBA"[i]).c_str(),
838 render_view_name,
839 channels_count_in_buffer,
840 channels_count_in_buffer * render_result->rectx,
841 output_buffer + i,
842 half_float);
843 }
844 }
845}
846
848 const RenderResult *rr,
849 const char *filepath,
850 const ImageFormatData *imf,
851 const bool save_as_render,
852 const char *view,
853 int layer)
854{
855 void *exrhandle = IMB_exr_get_handle();
856 const bool multi_layer = !(imf && imf->imtype == R_IMF_IMTYPE_OPENEXR);
857
858 /* Write first layer if not multilayer and no layer was specified. */
859 if (!multi_layer && layer == -1) {
860 layer = 0;
861 }
862
863 /* First add views since IMB_exr_add_channel checks number of views. */
864 const RenderView *first_rview = (const RenderView *)rr->views.first;
865 if (first_rview && (first_rview->next || first_rview->name[0])) {
866 LISTBASE_FOREACH (RenderView *, rview, &rr->views) {
867 if (!view || STREQ(view, rview->name)) {
868 IMB_exr_add_view(exrhandle, rview->name);
869 }
870 }
871 }
872
873 Vector<float *> tmp_output_rects;
874 add_exr_compositing_result(exrhandle, rr, imf, save_as_render, view, layer, tmp_output_rects);
875
876 /* Other render layers. */
877 int nr = (rr->have_combined) ? 1 : 0;
878 const bool has_multiple_layers = BLI_listbase_count_at_most(&rr->layers, 2) > 1;
879 LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) {
880 /* Skip other render layers if requested. */
881 if (!multi_layer && nr != layer) {
882 nr++;
883 continue;
884 }
885 nr++;
886
887 LISTBASE_FOREACH (RenderPass *, render_pass, &rl->passes) {
888 /* Skip non-RGBA and Z passes if not using multi layer. */
889 if (!multi_layer && !STR_ELEM(render_pass->name, RE_PASSNAME_COMBINED, "")) {
890 continue;
891 }
892
893 /* Skip pass if it does not match the requested view(s). */
894 const char *viewname = render_pass->view;
895 if (view) {
896 if (!STREQ(view, viewname)) {
897 continue;
898 }
899
900 viewname = "";
901 }
902
903 /* We only store RGBA passes as half float, for
904 * others precision loss can be problematic. */
905 const bool pass_RGBA = RE_RenderPassIsColor(render_pass);
906 const bool half_float = (imf && imf->depth == R_IMF_CHAN_DEPTH_16);
907 const bool pass_half_float = half_float && pass_RGBA;
908
909 /* Color-space conversion only happens on RGBA passes. */
910 float *output_rect = (save_as_render && pass_RGBA) ?
912 render_pass->ibuf->float_buffer.data,
913 rr->rectx,
914 rr->recty,
915 render_pass->channels,
916 imf,
917 tmp_output_rects) :
918 render_pass->ibuf->float_buffer.data;
919
920 /* For multi-layer EXRs, we write the pass as is with all of its channels. */
921 if (multi_layer) {
922 for (int i = 0; i < render_pass->channels; i++) {
923 char passname[EXR_PASS_MAXNAME];
924 char layname[EXR_PASS_MAXNAME];
925
926 /* A single unnamed layer indicates that the pass name should be used as the layer name,
927 * while the pass name should be the channel ID. */
928 if (!has_multiple_layers && rl->name[0] == '\0') {
929 passname[0] = render_pass->chan_id[i];
930 passname[1] = '\0';
931 STRNCPY(layname, render_pass->name);
932 }
933 else {
935 passname, nullptr, render_pass->name, nullptr, render_pass->chan_id, i);
936 STRNCPY(layname, rl->name);
937 }
938
939 IMB_exr_add_channel(exrhandle,
940 layname,
941 passname,
942 viewname,
943 render_pass->channels,
944 render_pass->channels * rr->rectx,
945 output_rect + i,
946 pass_half_float);
947 }
948 continue;
949 }
950
951 /* For single layer EXR, we only add the channels specified in the image format and do any
952 * needed color format conversion.
953 *
954 * First, if the required channels equal the pass channels, we add the channels as is. Or,
955 * we add the RGB[A] channels if the pass is RGB[A] and we require RGB[A]. If the alpha
956 * channel is required but does not exist in the pass, it will be added below. */
957 const int required_channels = imf ? imf->planes / 8 : 4;
958 if (required_channels == render_pass->channels ||
959 (required_channels != 1 && render_pass->channels != 1))
960 {
961 for (int i = 0; i < std::min(required_channels, render_pass->channels); i++) {
962 IMB_exr_add_channel(exrhandle,
963 "",
964 std::string(1, render_pass->chan_id[i]).c_str(),
965 viewname,
966 render_pass->channels,
967 render_pass->channels * rr->rectx,
968 output_rect + i,
969 pass_half_float);
970 }
971 }
972 else if (required_channels == 1) {
973 /* In case of a single required channel, we need to do RGB[A] to BW conversion. We know the
974 * input is RGB[A] and not single channel because it filed the condition above. */
975 float *gray_scale_output = image_exr_from_rgb_to_bw(
976 output_rect, rr->rectx, rr->recty, render_pass->channels, tmp_output_rects);
978 exrhandle, "", "V", viewname, 1, rr->rectx, gray_scale_output, pass_half_float);
979 }
980 else if (render_pass->channels == 1) {
981 /* In case of a single channel pass, we need to broadcast the same channel for each of the
982 * RGB channels that are required. We know the RGB is required because single channel
983 * requirement was handled above. The alpha channel will be added later. */
984 for (int i = 0; i < 3; i++) {
985 IMB_exr_add_channel(exrhandle,
986 "",
987 std::string(1, "RGB"[i]).c_str(),
988 viewname,
989 1,
990 rr->rectx,
991 output_rect,
992 pass_half_float);
993 }
994 }
995
996 /* Add an opaque alpha channel if the pass contains no alpha channel but an alpha channel is
997 * required. */
998 if (required_channels == 4 && render_pass->channels < 4) {
999 float *alpha_output = image_exr_opaque_alpha_buffer(
1000 rr->rectx, rr->recty, tmp_output_rects);
1002 exrhandle, "", "A", viewname, 1, rr->rectx, alpha_output, pass_half_float);
1003 }
1004 }
1005 }
1006
1007 errno = 0;
1008
1010
1011 const int compress = (imf ? imf->exr_codec : 0);
1012 const int quality = (imf ? imf->quality : 90);
1013 bool success = IMB_exr_begin_write(
1014 exrhandle, filepath, rr->rectx, rr->recty, rr->ppm, compress, quality, rr->stamp_data);
1015 if (success) {
1016 IMB_exr_write_channels(exrhandle);
1017 }
1018 else {
1019 /* TODO: get the error from openexr's exception. */
1021 reports, RPT_ERROR, "Error writing render result, %s (see console)", strerror(errno));
1022 }
1023
1024 for (float *rect : tmp_output_rects) {
1025 MEM_freeN(rect);
1026 }
1027
1028 IMB_exr_close(exrhandle);
1029 return success;
1030}
1031
1032/* Render output. */
1033
1035 const char *filepath,
1036 int ok,
1037 int err)
1038{
1039 if (ok) {
1040 /* no need to report, just some helpful console info */
1041 if (!G.quiet) {
1042 printf("Saved: '%s'\n", filepath);
1043 }
1044 }
1045 else {
1046 /* report on error since users will want to know what failed */
1048 reports, RPT_ERROR, "Render error (%s) cannot save: '%s'", strerror(err), filepath);
1049 }
1050}
1051
1053 const Scene *scene,
1054 const RenderResult *rr,
1055 ImBuf *ibuf,
1056 const char *filepath,
1057 const ImageFormatData *imf,
1058 const bool stamp)
1059{
1060 bool ok;
1061
1062 if (stamp) {
1063 /* writes the name of the individual cameras */
1064 ok = BKE_imbuf_write_stamp(scene, rr, ibuf, filepath, imf);
1065 }
1066 else {
1067 ok = BKE_imbuf_write(ibuf, filepath, imf);
1068 }
1069
1070 image_render_print_save_message(reports, filepath, ok, errno);
1071
1072 return ok;
1073}
1074
1076 RenderResult *rr,
1077 const Scene *scene,
1078 const bool stamp,
1079 const char *filepath_basis,
1080 const ImageFormatData *format,
1081 bool save_as_render)
1082{
1083 bool ok = true;
1084
1085 if (!rr) {
1086 return false;
1087 }
1088
1089 ImageFormatData image_format;
1090 BKE_image_format_init_for_write(&image_format, scene, format);
1091
1092 if (!save_as_render) {
1094 &format->linear_colorspace_settings);
1095 }
1096
1097 const bool is_mono = !RE_ResultIsMultiView(rr);
1098 const bool is_exr_rr = ELEM(
1101 const float dither = scene->r.dither_intensity;
1102
1103 if (image_format.views_format == R_IMF_VIEWS_MULTIVIEW && is_exr_rr) {
1105 reports, rr, filepath_basis, &image_format, save_as_render, nullptr, -1);
1106 image_render_print_save_message(reports, filepath_basis, ok, errno);
1107 }
1108
1109 /* mono, legacy code */
1110 else if (is_mono || (image_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) {
1111 int view_id = 0;
1112 for (const RenderView *rv = (const RenderView *)rr->views.first; rv; rv = rv->next, view_id++)
1113 {
1114 char filepath[FILE_MAX];
1115 if (is_mono) {
1116 STRNCPY(filepath, filepath_basis);
1117 }
1118 else {
1119 BKE_scene_multiview_view_filepath_get(&scene->r, filepath_basis, rv->name, filepath);
1120 }
1121
1122 if (is_exr_rr) {
1124 reports, rr, filepath, &image_format, save_as_render, rv->name, -1);
1125 image_render_print_save_message(reports, filepath, ok, errno);
1126
1127 /* optional preview images for exr */
1128 if (ok && (image_format.flag & R_IMF_FLAG_PREVIEW_JPG)) {
1129 image_format.imtype = R_IMF_IMTYPE_JPEG90;
1130 image_format.depth = R_IMF_CHAN_DEPTH_8;
1131
1132 if (BLI_path_extension_check(filepath, ".exr")) {
1133 filepath[strlen(filepath) - 4] = 0;
1134 }
1135 BKE_image_path_ext_from_imformat_ensure(filepath, sizeof(filepath), &image_format);
1136
1137 ImBuf *ibuf = RE_render_result_rect_to_ibuf(rr, &image_format, dither, view_id);
1138 ibuf->planes = 24;
1139 IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, false, &image_format);
1140
1142 reports, scene, rr, ibuf, filepath, &image_format, stamp);
1143
1144 IMB_freeImBuf(ibuf);
1145 }
1146 }
1147 else {
1148 ImBuf *ibuf = RE_render_result_rect_to_ibuf(rr, &image_format, dither, view_id);
1149
1150 IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, false, &image_format);
1151
1153 reports, scene, rr, ibuf, filepath, &image_format, stamp);
1154
1155 /* imbuf knows which rects are not part of ibuf */
1156 IMB_freeImBuf(ibuf);
1157 }
1158 }
1159 }
1160 else { /* R_IMF_VIEWS_STEREO_3D */
1162
1163 char filepath[FILE_MAX];
1164 STRNCPY(filepath, filepath_basis);
1165
1166 if (image_format.imtype == R_IMF_IMTYPE_MULTILAYER) {
1167 printf("Stereo 3D not supported for MultiLayer image: %s\n", filepath);
1168 }
1169 else {
1170 ImBuf *ibuf_arr[3] = {nullptr};
1171 const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
1172 int i;
1173
1174 for (i = 0; i < 2; i++) {
1175 int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name));
1176 ibuf_arr[i] = RE_render_result_rect_to_ibuf(rr, &image_format, dither, view_id);
1177 IMB_colormanagement_imbuf_for_write(ibuf_arr[i], save_as_render, false, &image_format);
1178 }
1179
1180 ibuf_arr[2] = IMB_stereo3d_ImBuf(&image_format, ibuf_arr[0], ibuf_arr[1]);
1181
1182 if (ibuf_arr[2]) {
1184 reports, scene, rr, ibuf_arr[2], filepath, &image_format, stamp);
1185
1186 /* optional preview images for exr */
1187 if (ok && is_exr_rr && (image_format.flag & R_IMF_FLAG_PREVIEW_JPG)) {
1188 image_format.imtype = R_IMF_IMTYPE_JPEG90;
1189 image_format.depth = R_IMF_CHAN_DEPTH_8;
1190
1191 if (BLI_path_extension_check(filepath, ".exr")) {
1192 filepath[strlen(filepath) - 4] = 0;
1193 }
1194
1195 BKE_image_path_ext_from_imformat_ensure(filepath, sizeof(filepath), &image_format);
1196 ibuf_arr[2]->planes = 24;
1197
1199 reports, scene, rr, ibuf_arr[2], filepath, &image_format, stamp);
1200 }
1201 }
1202 else {
1203 BKE_reportf(reports, RPT_ERROR, "Failed to create stereo image buffer");
1204 ok = false;
1205 }
1206
1207 /* imbuf knows which rects are not part of ibuf */
1208 for (i = 0; i < 3; i++) {
1209 if (ibuf_arr[i]) {
1210 IMB_freeImBuf(ibuf_arr[i]);
1211 }
1212 }
1213 }
1214 }
1215
1216 BKE_image_format_free(&image_format);
1217
1218 return ok;
1219}
void BKE_color_managed_colorspace_settings_copy(ColorManagedColorspaceSettings *colorspace_settings, const ColorManagedColorspaceSettings *settings)
bool BKE_color_managed_colorspace_settings_equals(const ColorManagedColorspaceSettings *settings1, const ColorManagedColorspaceSettings *settings2)
eUDIM_TILE_FORMAT
Definition BKE_image.hh:446
@ UDIM_TILE_FORMAT_NONE
Definition BKE_image.hh:447
void BKE_imbuf_stamp_info(const RenderResult *rr, ImBuf *ibuf)
ImBuf * BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
char * BKE_image_get_tile_strformat(const char *filepath, eUDIM_TILE_FORMAT *r_tile_format)
void BKE_image_set_filepath_from_tile_number(char *filepath, const char *pattern, eUDIM_TILE_FORMAT tile_format, int tile_number)
RenderPass * BKE_image_multilayer_index(RenderResult *rr, ImageUser *iuser)
void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock)
void BKE_image_release_renderresult(Scene *scene, Image *ima, RenderResult *render_result)
void BKE_image_user_file_path_ex(const Main *bmain, const ImageUser *iuser, const Image *ima, char *filepath, const bool resolve_udim, const bool resolve_multiview)
#define IMA_SIGNAL_COLORMANAGE
Definition BKE_image.hh:167
bool BKE_imbuf_alpha_test(ImBuf *ibuf)
void BKE_imageuser_default(ImageUser *iuser)
bool BKE_image_is_stereo(const Image *ima)
bool BKE_image_is_multiview(const Image *ima)
bool BKE_imbuf_write(ImBuf *ibuf, const char *filepath, const ImageFormatData *imf)
void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
bool BKE_imbuf_write_stamp(const Scene *scene, const RenderResult *rr, ImBuf *ibuf, const char *filepath, const ImageFormatData *imf)
bool BKE_imbuf_write_as(ImBuf *ibuf, const char *filepath, const ImageFormatData *imf, bool save_copy)
RenderResult * BKE_image_acquire_renderresult(Scene *scene, Image *ima)
void BKE_image_multiview_index(const Image *ima, ImageUser *iuser)
void BKE_image_format_free(ImageFormatData *imf)
void BKE_image_format_init(ImageFormatData *imf, const bool render)
void BKE_image_format_init_for_write(ImageFormatData *imf, const Scene *scene_src, const ImageFormatData *imf_src)
void BKE_image_format_from_imbuf(ImageFormatData *im_format, const ImBuf *imbuf)
int BKE_image_path_ext_from_imformat_ensure(char *filepath, size_t filepath_maxncpy, const ImageFormatData *im_format)
void BKE_image_format_update_color_space_for_type(ImageFormatData *format)
void BKE_image_format_to_imbuf(ImBuf *ibuf, const ImageFormatData *imf)
void BKE_image_format_color_management_copy_from_scene(ImageFormatData *imf, const Scene *scene)
bool BKE_imtype_requires_linear_float(char imtype)
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:872
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
void BKE_scene_multiview_view_filepath_get(const RenderData *rd, const char *filepath, const char *view, char *r_filepath)
Definition scene.cc:3128
void BKE_scene_ppm_get(const RenderData *rd, double r_ppm[2])
Definition scene.cc:3245
#define BLI_assert(a)
Definition BLI_assert.h:46
File and directory operations.
bool BLI_file_ensure_parent_dir_exists(const char *filepath) ATTR_NONNULL(1)
Definition fileops_c.cc:429
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
void * BLI_findstring(const ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:608
int BLI_listbase_count_at_most(const ListBase *listbase, int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:511
int BLI_findstringindex(const ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:780
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
MINLINE void copy_v2_v2_db(double r[2], const double a[2])
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
bool bool BLI_path_extension_strip(char *path) ATTR_NONNULL(1)
#define FILE_MAX
#define BLI_path_join(...)
bool BLI_path_extension_check(const char *path, const char *ext) ATTR_NONNULL(1
bool void BLI_path_rel(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1)
bool BLI_path_make_safe_filename(char *filename) ATTR_NONNULL(1)
#define STR_ELEM(...)
Definition BLI_string.h:656
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
#define STR_CONCAT(dst, len, suffix)
Definition BLI_string.h:604
unsigned char uchar
#define ELEM(...)
#define STREQ(a, b)
#define DATA_(msgid)
@ IMA_SRC_FILE
@ IMA_SRC_MOVIE
@ IMA_SRC_GENERATED
@ IMA_SRC_VIEWER
@ IMA_SRC_TILED
@ IMA_GEN_TILE
@ IMA_SHOW_STEREO
@ IMA_TYPE_R_RESULT
@ IMA_TYPE_COMPOSITE
@ IMA_TYPE_IMAGE
#define RE_PASSNAME_COMBINED
#define STEREO_LEFT_NAME
@ R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE
@ R_IMF_IMTYPE_JPEG90
@ R_IMF_IMTYPE_OPENEXR
@ R_IMF_IMTYPE_MULTILAYER
@ R_IMF_PLANES_RGB
@ R_IMF_PLANES_RGBA
@ R_IMF_PLANES_BW
@ R_IMF_CHAN_DEPTH_8
@ R_IMF_CHAN_DEPTH_16
@ R_IMF_VIEWS_MULTIVIEW
@ R_IMF_VIEWS_STEREO_3D
@ R_IMF_VIEWS_INDIVIDUAL
#define STEREO_RIGHT_NAME
@ R_IMF_FLAG_PREVIEW_JPG
static AppView * view
bool IMB_colormanagement_space_name_is_scene_linear(const char *name)
void IMB_colormanagement_transform_float(float *buffer, int width, int height, int channels, const char *from_colorspace, const char *to_colorspace, bool predivide)
ImBuf * IMB_colormanagement_imbuf_for_write(ImBuf *ibuf, bool save_as_render, bool allocate_result, const ImageFormatData *image_format)
bool IMB_colormanagement_space_name_is_data(const char *name)
@ COLOR_ROLE_DEFAULT_BYTE
@ COLOR_ROLE_SCENE_LINEAR
BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3])
const char * IMB_colormanagement_role_colorspace_name_get(int role)
ImBuf * IMB_dupImBuf(const ImBuf *ibuf1)
void IMB_freeImBuf(ImBuf *ibuf)
ImBuf * IMB_stereo3d_ImBuf(const ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *ibuf_right)
void IMB_free_float_pixels(ImBuf *ibuf)
void IMB_free_byte_pixels(ImBuf *ibuf)
@ IB_BITMAPDIRTY
void IMB_exr_add_view(void *handle, const char *name)
bool IMB_exr_begin_write(void *handle, const char *filepath, int width, int height, const double ppm[2], int compress, int quality, const StampData *stamp)
void IMB_exr_close(void *handle)
#define EXR_PASS_MAXNAME
void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, const char *viewname, int xstride, int ystride, float *rect, bool use_half_float)
void IMB_exr_write_channels(void *handle)
void * IMB_exr_get_handle()
Read Guarded memory(de)allocation.
ReportList * reports
Definition WM_types.hh:1025
volatile int lock
long long int int64_t
void append(const T &value)
#define offsetof(t, d)
#define printf(...)
#define ID_BLEND_PATH(_bmain, _id)
bool BKE_image_save(ReportList *reports, Main *bmain, Image *ima, ImageUser *iuser, const ImageSaveOptions *opts)
static float * image_exr_from_scene_linear_to_output(float *rect, const int width, const int height, const int channels, const ImageFormatData *imf, Vector< float * > &tmp_output_rects)
static void image_save_post(ReportList *reports, Image *ima, ImBuf *ibuf, int ok, const ImageSaveOptions *opts, const bool save_copy, const char *filepath, bool *r_colorspace_changed)
static void imbuf_save_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf)
bool BKE_image_render_write_exr(ReportList *reports, const RenderResult *rr, const char *filepath, const ImageFormatData *imf, const bool save_as_render, const char *view, int layer)
static bool image_render_write_stamp_test(ReportList *reports, const Scene *scene, const RenderResult *rr, ImBuf *ibuf, const char *filepath, const ImageFormatData *imf, const bool stamp)
bool BKE_image_save_options_init(ImageSaveOptions *opts, Main *bmain, Scene *scene, Image *ima, ImageUser *iuser, const bool guess_path, const bool save_as_render)
Definition image_save.cc:45
static float * image_exr_from_rgb_to_bw(float *input_buffer, int width, int height, int channels, Vector< float * > &temporary_buffers)
static bool image_save_single(ReportList *reports, Image *ima, ImageUser *iuser, const ImageSaveOptions *opts, bool *r_colorspace_changed)
static float * image_exr_opaque_alpha_buffer(int width, int height, Vector< float * > &temporary_buffers)
static void image_render_print_save_message(ReportList *reports, const char *filepath, int ok, int err)
static void image_save_update_filepath(Image *ima, const char *filepath, const ImageSaveOptions *opts)
bool BKE_image_render_write(ReportList *reports, RenderResult *rr, const Scene *scene, const bool stamp, const char *filepath_basis, const ImageFormatData *format, bool save_as_render)
void BKE_image_save_options_update(ImageSaveOptions *opts, const Image *image)
static void add_exr_compositing_result(void *exr_handle, const RenderResult *render_result, const ImageFormatData *imf, bool save_as_render, const char *view_name, int layer, Vector< float * > &temporary_buffers)
void BKE_image_save_options_free(ImageSaveOptions *opts)
const ccl_global KernelWorkTile * tile
format
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define G(x, y, z)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
bool RE_RenderPassIsColor(const RenderPass *render_pass)
bool RE_HasFloatPixels(const RenderResult *result)
ImBuf * RE_render_result_rect_to_ibuf(RenderResult *rr, const ImageFormatData *imf, const float dither, const int view_id)
void RE_render_result_full_channel_name(char *fullname, const char *layname, const char *passname, const char *viewname, const char *chan_id, const int channel)
bool RE_ResultIsMultiView(RenderResult *rr)
char name[66]
Definition DNA_ID.h:415
char filepath[IMB_FILEPATH_SIZE]
ImBufFloatBuffer float_buffer
ImbFormatOptions foptions
ImBufByteBuffer byte_buffer
unsigned char planes
enum eImbFileType ftype
ColorManagedColorspaceSettings linear_colorspace_settings
Stereo3dFormat stereo3d_format
ImageFormatData im_format
struct Scene * scene
ColorManagedColorspaceSettings colorspace_settings
char views_format
char filepath[1024]
ListBase tiles
short source
ListBase views
struct Stereo3dFormat * stereo3d_format
void * first
struct ImageFormatData im_format
float dither_intensity
ListBase views
ListBase layers
struct StampData * stamp_data
double ppm[2]
struct RenderView * next
Definition RE_pipeline.h:38
char name[64]
Definition RE_pipeline.h:39
struct RenderData r
void * BKE_image_get_tile
Definition stubs.c:36
i
Definition text_draw.cc:230
uint len