OpenEXR: Preserve compression and depth settings when re-saving file

Set the flags on the image buffer when loading an EXR file, so they can be
used when saving.

This also removes IB_halffloat and replaces it by the file options flag.

Pull Request: https://projects.blender.org/blender/blender/pulls/135656
This commit is contained in:
David Murmann
2025-03-26 21:35:20 +01:00
committed by Brecht Van Lommel
parent 202db40afb
commit afee81753e
8 changed files with 91 additions and 53 deletions

View File

@@ -818,12 +818,51 @@ void BKE_image_format_to_imbuf(ImBuf *ibuf, const ImageFormatData *imf)
}
}
static char imtype_best_depth(const ImBuf *ibuf, const char imtype)
{
const char depth_ok = BKE_imtype_valid_depths(imtype);
if (ibuf->float_buffer.data) {
if (depth_ok & R_IMF_CHAN_DEPTH_32) {
return R_IMF_CHAN_DEPTH_32;
}
if (depth_ok & R_IMF_CHAN_DEPTH_24) {
return R_IMF_CHAN_DEPTH_24;
}
if (depth_ok & R_IMF_CHAN_DEPTH_16) {
return R_IMF_CHAN_DEPTH_16;
}
if (depth_ok & R_IMF_CHAN_DEPTH_12) {
return R_IMF_CHAN_DEPTH_12;
}
return R_IMF_CHAN_DEPTH_8;
}
if (depth_ok & R_IMF_CHAN_DEPTH_8) {
return R_IMF_CHAN_DEPTH_8;
}
if (depth_ok & R_IMF_CHAN_DEPTH_12) {
return R_IMF_CHAN_DEPTH_12;
}
if (depth_ok & R_IMF_CHAN_DEPTH_16) {
return R_IMF_CHAN_DEPTH_16;
}
if (depth_ok & R_IMF_CHAN_DEPTH_24) {
return R_IMF_CHAN_DEPTH_24;
}
if (depth_ok & R_IMF_CHAN_DEPTH_32) {
return R_IMF_CHAN_DEPTH_32;
}
return R_IMF_CHAN_DEPTH_8; /* fallback, should not get here */
}
void BKE_image_format_from_imbuf(ImageFormatData *im_format, const ImBuf *imbuf)
{
/* Read from ImBuf after file read. */
int ftype = imbuf->ftype;
int custom_flags = imbuf->foptions.flag;
char quality = imbuf->foptions.quality;
bool is_depth_set = false;
BKE_image_format_init(im_format, false);
@@ -839,6 +878,7 @@ void BKE_image_format_from_imbuf(ImageFormatData *im_format, const ImBuf *imbuf)
if (custom_flags & PNG_16BIT) {
im_format->depth = R_IMF_CHAN_DEPTH_16;
is_depth_set = true;
}
im_format->compress = quality;
@@ -853,6 +893,7 @@ void BKE_image_format_from_imbuf(ImageFormatData *im_format, const ImBuf *imbuf)
im_format->imtype = R_IMF_IMTYPE_TIFF;
if (custom_flags & TIF_16BIT) {
im_format->depth = R_IMF_CHAN_DEPTH_16;
is_depth_set = true;
}
if (custom_flags & TIF_COMPRESS_NONE) {
im_format->tiff_codec = R_IMF_TIFF_CODEC_NONE;
@@ -871,11 +912,17 @@ void BKE_image_format_from_imbuf(ImageFormatData *im_format, const ImBuf *imbuf)
#ifdef WITH_OPENEXR
else if (ftype == IMB_FTYPE_OPENEXR) {
im_format->imtype = R_IMF_IMTYPE_OPENEXR;
char exr_codec = custom_flags & OPENEXR_CODEC_MASK;
if (custom_flags & OPENEXR_HALF) {
im_format->depth = R_IMF_CHAN_DEPTH_16;
is_depth_set = true;
}
if (custom_flags & OPENEXR_CODEC_MASK) {
im_format->exr_codec = R_IMF_EXR_CODEC_ZIP; /* Can't determine compression */
else if (exr_codec == R_IMF_EXR_CODEC_B44 || exr_codec == R_IMF_EXR_CODEC_B44A) {
/* B44 and B44A are only selectable for half precision images, default to ZIP compression */
exr_codec = R_IMF_EXR_CODEC_ZIP;
}
if (exr_codec < R_IMF_EXR_CODEC_MAX) {
im_format->exr_codec = exr_codec;
}
}
#endif
@@ -903,9 +950,11 @@ void BKE_image_format_from_imbuf(ImageFormatData *im_format, const ImBuf *imbuf)
if (custom_flags & JP2_16BIT) {
im_format->depth = R_IMF_CHAN_DEPTH_16;
is_depth_set = true;
}
else if (custom_flags & JP2_12BIT) {
im_format->depth = R_IMF_CHAN_DEPTH_12;
is_depth_set = true;
}
if (custom_flags & JP2_YCC) {
@@ -942,6 +991,11 @@ void BKE_image_format_from_imbuf(ImageFormatData *im_format, const ImBuf *imbuf)
im_format->quality = quality;
}
/* Default depth, accounting for float buffer and format support */
if (!is_depth_set) {
im_format->depth = imtype_best_depth(imbuf, im_format->imtype);
}
/* planes */
im_format->planes = imbuf->planes;
}

View File

@@ -42,44 +42,6 @@
using blender::Vector;
static char imtype_best_depth(ImBuf *ibuf, const char imtype)
{
const char depth_ok = BKE_imtype_valid_depths(imtype);
if (ibuf->float_buffer.data) {
if (depth_ok & R_IMF_CHAN_DEPTH_32) {
return R_IMF_CHAN_DEPTH_32;
}
if (depth_ok & R_IMF_CHAN_DEPTH_24) {
return R_IMF_CHAN_DEPTH_24;
}
if (depth_ok & R_IMF_CHAN_DEPTH_16) {
return R_IMF_CHAN_DEPTH_16;
}
if (depth_ok & R_IMF_CHAN_DEPTH_12) {
return R_IMF_CHAN_DEPTH_12;
}
return R_IMF_CHAN_DEPTH_8;
}
if (depth_ok & R_IMF_CHAN_DEPTH_8) {
return R_IMF_CHAN_DEPTH_8;
}
if (depth_ok & R_IMF_CHAN_DEPTH_12) {
return R_IMF_CHAN_DEPTH_12;
}
if (depth_ok & R_IMF_CHAN_DEPTH_16) {
return R_IMF_CHAN_DEPTH_16;
}
if (depth_ok & R_IMF_CHAN_DEPTH_24) {
return R_IMF_CHAN_DEPTH_24;
}
if (depth_ok & R_IMF_CHAN_DEPTH_32) {
return R_IMF_CHAN_DEPTH_32;
}
return R_IMF_CHAN_DEPTH_8; /* fallback, should not get here */
}
bool BKE_image_save_options_init(ImageSaveOptions *opts,
Main *bmain,
Scene *scene,
@@ -108,13 +70,11 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts,
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock);
if (ibuf) {
bool is_depth_set = false;
const char *ima_colorspace = ima->colorspace_settings.name;
if (opts->save_as_render) {
/* Render/compositor output or user chose to save with render settings. */
BKE_image_format_init_for_write(&opts->im_format, scene, nullptr);
is_depth_set = true;
if (!BKE_image_is_multiview(ima)) {
/* In case multiview is disabled,
* render settings would be invalid for render result in this area. */
@@ -156,11 +116,6 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts,
opts->im_format.planes = R_IMF_PLANES_RGBA;
}
/* depth, account for float buffer and format support */
if (is_depth_set == false) {
opts->im_format.depth = imtype_best_depth(ibuf, opts->im_format.imtype);
}
/* some formats don't use quality so fallback to scenes quality */
if (opts->im_format.quality == 0) {
opts->im_format.quality = scene->r.im_format.quality;

View File

@@ -283,7 +283,7 @@ CachedImage::CachedImage(Context &context,
ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &image_user_for_pass, nullptr);
ImBuf *linear_image_buffer = compute_linear_buffer(image_buffer);
const bool use_half_float = linear_image_buffer->flags & IB_halffloat;
const bool use_half_float = linear_image_buffer->foptions.flag & OPENEXR_HALF;
this->result.set_precision(use_half_float ? ResultPrecision::Half : ResultPrecision::Full);
this->result.set_type(get_result_type(render_result, image_user_for_pass, linear_image_buffer));

View File

@@ -943,7 +943,7 @@ void uiTemplateImage(uiLayout *layout,
void *lock;
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock);
if (ibuf && ibuf->float_buffer.data && (ibuf->flags & IB_halffloat) == 0) {
if (ibuf && ibuf->float_buffer.data && (ibuf->foptions.flag & OPENEXR_HALF) == 0) {
uiItemR(col, &imaptr, "use_half_precision", UI_ITEM_NONE, std::nullopt, ICON_NONE);
}
BKE_image_release_ibuf(ima, ibuf, lock);

View File

@@ -102,7 +102,6 @@ enum eImBufFlags {
/** ignore alpha on load and substitute it with 1.0f */
IB_alphamode_ignore = 1 << 15,
IB_thumbnail = 1 << 16,
IB_halffloat = 1 << 18,
};
/** \} */

View File

@@ -203,7 +203,7 @@ static ImBuf *get_oiio_ibuf(ImageInput *in, const ReadContext &ctx, char colorsp
/* Fill in common ibuf properties. */
if (ibuf) {
ibuf->ftype = ctx.file_type;
ibuf->flags |= (spec.format == TypeDesc::HALF) ? IB_halffloat : 0;
ibuf->foptions.flag |= (spec.format == TypeDesc::HALF) ? OPENEXR_HALF : 0;
set_colorspace_name(colorspace, ctx, spec, is_float);

View File

@@ -466,6 +466,35 @@ static void openexr_header_compression(Header *header, int compression, int qual
}
}
static int openexr_header_get_compression(const Header &header)
{
switch (header.compression()) {
case NO_COMPRESSION:
return R_IMF_EXR_CODEC_NONE;
case RLE_COMPRESSION:
return R_IMF_EXR_CODEC_RLE;
case ZIPS_COMPRESSION:
return R_IMF_EXR_CODEC_ZIPS;
case ZIP_COMPRESSION:
return R_IMF_EXR_CODEC_ZIP;
case PIZ_COMPRESSION:
return R_IMF_EXR_CODEC_PIZ;
case PXR24_COMPRESSION:
return R_IMF_EXR_CODEC_PXR24;
case B44_COMPRESSION:
return R_IMF_EXR_CODEC_B44;
case B44A_COMPRESSION:
return R_IMF_EXR_CODEC_B44A;
case DWAA_COMPRESSION:
return R_IMF_EXR_CODEC_DWAA;
case DWAB_COMPRESSION:
return R_IMF_EXR_CODEC_DWAB;
case NUM_COMPRESSION_METHODS:
return R_IMF_EXR_CODEC_NONE;
}
return R_IMF_EXR_CODEC_NONE;
}
static void openexr_header_metadata(Header *header, ImBuf *ibuf)
{
if (ibuf->metadata) {
@@ -2220,7 +2249,8 @@ ImBuf *imb_load_openexr(const uchar *mem, size_t size, int flags, char colorspac
const bool is_alpha = exr_has_alpha(*file);
ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, 0);
ibuf->flags |= exr_is_half_float(*file) ? IB_halffloat : 0;
ibuf->foptions.flag |= exr_is_half_float(*file) ? OPENEXR_HALF : 0;
ibuf->foptions.flag |= openexr_header_get_compression(file_header);
if (hasXDensity(file_header)) {
/* Convert inches to meters. */

View File

@@ -59,7 +59,7 @@ static void imb_gpu_get_format(const ImBuf *ibuf,
if (float_rect) {
/* Float. */
const bool use_high_bitdepth = (!(ibuf->flags & IB_halffloat) && high_bitdepth);
const bool use_high_bitdepth = (!(ibuf->foptions.flag & OPENEXR_HALF) && high_bitdepth);
*r_texture_format = is_grayscale ? (use_high_bitdepth ? GPU_R32F : GPU_R16F) :
(use_high_bitdepth ? GPU_RGBA32F : GPU_RGBA16F);
}