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