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