Image: Add OIIO support APIs

This adds a new set of APIs supporting the loading and saving of image
formats through OIIO. It makes use of the recent IOProxy work in OIIO
to align with the existing Blender image loading/saving machinery.

The support code here has been prototyped to work with ~7 of our image
formats so far. It includes centralized handling of `IB_test`,
`IB_mem`, and `IB_metadata` flags, which the existing code did not
handle consistently or at all depending on the format.

The PSD format (`format_psd.cc`) is included since the prior location
of the code has been restructured away. It serves as an example of how
the loading code typically flows for all the other formats.

Pull Request: https://projects.blender.org/blender/blender/pulls/105519
This commit is contained in:
Jesse Yurkovich 2023-03-14 04:42:17 +01:00 committed by Jesse Yurkovich
parent 19565469c8
commit 8929ed75d4
15 changed files with 558 additions and 333 deletions

View File

@ -18,6 +18,7 @@ set(INC_SYS
${JPEG_INCLUDE_DIR}
${PNG_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS}
${OPENIMAGEIO_INCLUDE_DIRS}
)
set(SRC
@ -29,6 +30,7 @@ set(SRC
intern/divers.c
intern/filetype.c
intern/filter.c
intern/format_psd.cc
intern/imageprocess.c
intern/indexer.c
intern/iris.c

View File

@ -678,7 +678,7 @@ void IMB_sampleImageAtLocation(
* \attention defined in readimage.c
*/
struct ImBuf *IMB_loadifffile(
int file, const char *filepath, int flags, char colorspace[IM_MAX_SPACE], const char *descr);
int file, int flags, char colorspace[IM_MAX_SPACE], const char *descr);
/**
* \attention defined in scaling.c

View File

@ -318,9 +318,6 @@ extern const char *imb_ext_image[];
extern const char *imb_ext_movie[];
extern const char *imb_ext_audio[];
/** Image formats that can only be loaded via filepath. */
extern const char *imb_ext_image_filepath_only[];
/* -------------------------------------------------------------------- */
/** \name Imbuf Color Management Flag
*

View File

@ -256,3 +256,16 @@ struct ImBuf *imb_load_filepath_thumbnail_webp(const char *filepath,
bool imb_savewebp(struct ImBuf *ibuf, const char *name, int flags);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Format: PSD (#IMB_FTYPE_PSD)
* \{ */
bool imb_is_a_psd(const unsigned char *buf, size_t size);
struct ImBuf *imb_load_psd(const uchar *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);
/** \} */

View File

@ -184,9 +184,9 @@ const ImFileType IMB_FILE_TYPES[] = {
{
.init = NULL,
.exit = NULL,
.is_a = imb_is_a_photoshop,
.load = NULL,
.load_filepath = imb_load_photoshop,
.is_a = imb_is_a_psd,
.load = imb_load_psd,
.load_filepath = NULL,
.load_filepath_thumbnail = NULL,
.save = NULL,
.flag = IM_FTYPE_FLOAT,

View File

@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "oiio/openimageio_support.hh"
#include "IMB_imbuf_types.h"
OIIO_NAMESPACE_USING
using namespace blender::imbuf;
extern "C" {
bool imb_is_a_psd(const uchar *mem, size_t size)
{
return imb_oiio_check(mem, size, "psd");
}
ImBuf *imb_load_psd(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
ImageSpec config, spec;
config.attribute("oiio:UnassociatedAlpha", 1);
ReadContext ctx{mem, size, "psd", IMB_FTYPE_PSD, flags};
/* PSD should obey color space information embedded in the file. */
ctx.use_embedded_colorspace = true;
return imb_oiio_read(ctx, config, colorspace, spec);
}
}

View File

@ -18,8 +18,10 @@ set(INC_SYS
set(SRC
openimageio_api.h
openimageio_support.hh
openimageio_api.cpp
openimageio_support.cc
)
set(LIB

View File

@ -5,278 +5,16 @@
* \ingroup openimageio
*/
#include <set>
#if defined(WIN32)
# include "utfconv.h"
# define _USE_MATH_DEFINES
#endif
/* NOTE: Keep first, #BLI_path_util conflicts with OIIO's format. */
#include "openimageio_api.h"
#include <OpenImageIO/imageio.h>
#include <memory>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "IMB_allocimbuf.h"
#include "IMB_colormanagement.h"
#include "IMB_colormanagement_intern.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
OIIO_NAMESPACE_USING
using std::string;
using std::unique_ptr;
using uchar = uchar;
template<class T, class Q>
static void fill_all_channels(T *pixels, int width, int height, int components, Q alpha)
{
if (components == 2) {
for (int i = width * height - 1; i >= 0; i--) {
pixels[i * 4 + 3] = pixels[i * 2 + 1];
pixels[i * 4 + 2] = pixels[i * 2 + 0];
pixels[i * 4 + 1] = pixels[i * 2 + 0];
pixels[i * 4 + 0] = pixels[i * 2 + 0];
}
}
else if (components == 3) {
for (int i = width * height - 1; i >= 0; i--) {
pixels[i * 4 + 3] = alpha;
pixels[i * 4 + 2] = pixels[i * 3 + 2];
pixels[i * 4 + 1] = pixels[i * 3 + 1];
pixels[i * 4 + 0] = pixels[i * 3 + 0];
}
}
else if (components == 1) {
for (int i = width * height - 1; i >= 0; i--) {
pixels[i * 4 + 3] = alpha;
pixels[i * 4 + 2] = pixels[i];
pixels[i * 4 + 1] = pixels[i];
pixels[i * 4 + 0] = pixels[i];
}
}
}
static ImBuf *imb_oiio_load_image(
ImageInput *in, int width, int height, int components, int flags, bool is_alpha)
{
ImBuf *ibuf;
int scanlinesize = width * components * sizeof(uchar);
/* allocate the memory for the image */
ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, flags | IB_rect);
try {
if (!in->read_image(0,
0,
0,
components,
TypeDesc::UINT8,
(uchar *)ibuf->rect + (height - 1) * scanlinesize,
AutoStride,
-scanlinesize,
AutoStride)) {
std::cerr << __func__ << ": ImageInput::read_image() failed:" << std::endl
<< in->geterror() << std::endl;
if (ibuf) {
IMB_freeImBuf(ibuf);
}
return nullptr;
}
}
catch (const std::exception &exc) {
std::cerr << exc.what() << std::endl;
if (ibuf) {
IMB_freeImBuf(ibuf);
}
return nullptr;
}
/* ImBuf always needs 4 channels */
fill_all_channels((uchar *)ibuf->rect, width, height, components, 0xFF);
return ibuf;
}
static ImBuf *imb_oiio_load_image_float(
ImageInput *in, int width, int height, int components, int flags, bool is_alpha)
{
ImBuf *ibuf;
int scanlinesize = width * components * sizeof(float);
/* allocate the memory for the image */
ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, flags | IB_rectfloat);
try {
if (!in->read_image(0,
0,
0,
components,
TypeDesc::FLOAT,
(uchar *)ibuf->rect_float + (height - 1) * scanlinesize,
AutoStride,
-scanlinesize,
AutoStride)) {
std::cerr << __func__ << ": ImageInput::read_image() failed:" << std::endl
<< in->geterror() << std::endl;
if (ibuf) {
IMB_freeImBuf(ibuf);
}
return nullptr;
}
}
catch (const std::exception &exc) {
std::cerr << exc.what() << std::endl;
if (ibuf) {
IMB_freeImBuf(ibuf);
}
return nullptr;
}
/* ImBuf always needs 4 channels */
fill_all_channels((float *)ibuf->rect_float, width, height, components, 1.0f);
/* NOTE: Photoshop 16 bit files never has alpha with it,
* so no need to handle associated/unassociated alpha. */
return ibuf;
}
extern "C" {
bool imb_is_a_photoshop(const uchar *mem, size_t size)
{
const uchar magic[4] = {'8', 'B', 'P', 'S'};
if (size < sizeof(magic)) {
return false;
}
return memcmp(magic, mem, sizeof(magic)) == 0;
}
int imb_save_photoshop(struct ImBuf *ibuf, const char * /*name*/, int flags)
{
if (flags & IB_mem) {
std::cerr << __func__ << ": Photoshop PSD-save: Create PSD in memory"
<< " currently not supported" << std::endl;
imb_addencodedbufferImBuf(ibuf);
ibuf->encodedsize = 0;
return 0;
}
return 0;
}
struct ImBuf *imb_load_photoshop(const char *filename, int flags, char colorspace[IM_MAX_SPACE])
{
struct ImBuf *ibuf = nullptr;
int width, height, components;
bool is_float, is_alpha, is_half;
int basesize;
char file_colorspace[IM_MAX_SPACE];
const bool is_colorspace_manually_set = (colorspace[0] != '\0');
/* load image from file through OIIO */
if (IMB_ispic_type_matches(filename, IMB_FTYPE_PSD) == 0) {
return nullptr;
}
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
unique_ptr<ImageInput> in(ImageInput::create(filename));
if (!in) {
std::cerr << __func__ << ": ImageInput::create() failed:" << std::endl
<< OIIO_NAMESPACE::geterror() << std::endl;
return nullptr;
}
ImageSpec spec, config;
config.attribute("oiio:UnassociatedAlpha", int(1));
if (!in->open(filename, spec, config)) {
std::cerr << __func__ << ": ImageInput::open() failed:" << std::endl
<< in->geterror() << std::endl;
return nullptr;
}
if (!is_colorspace_manually_set) {
string ics = spec.get_string_attribute("oiio:ColorSpace");
BLI_strncpy(file_colorspace, ics.c_str(), IM_MAX_SPACE);
/* Only use color-spaces exist. */
if (colormanage_colorspace_get_named(file_colorspace)) {
strcpy(colorspace, file_colorspace);
}
else {
std::cerr << __func__ << ": The embed colorspace (\"" << file_colorspace
<< "\") not supported in existent OCIO configuration file. Fallback "
<< "to system default colorspace (\"" << colorspace << "\")." << std::endl;
}
}
width = spec.width;
height = spec.height;
components = spec.nchannels;
is_alpha = spec.alpha_channel != -1;
basesize = spec.format.basesize();
is_float = basesize > 1;
is_half = spec.format == TypeDesc::HALF;
/* we only handle certain number of components */
if (!(components >= 1 && components <= 4)) {
if (in) {
in->close();
}
return nullptr;
}
if (is_float) {
ibuf = imb_oiio_load_image_float(in.get(), width, height, components, flags, is_alpha);
}
else {
ibuf = imb_oiio_load_image(in.get(), width, height, components, flags, is_alpha);
}
if (in) {
in->close();
}
if (!ibuf) {
return nullptr;
}
/* ImBuf always needs 4 channels */
ibuf->ftype = IMB_FTYPE_PSD;
ibuf->channels = 4;
ibuf->planes = (3 + (is_alpha ? 1 : 0)) * 4 << basesize;
ibuf->flags |= (is_float && is_half) ? IB_halffloat : 0;
try {
return ibuf;
}
catch (const std::exception &exc) {
std::cerr << exc.what() << std::endl;
if (ibuf) {
IMB_freeImBuf(ibuf);
}
return nullptr;
}
}
int OIIO_getVersionHex(void)
{
return openimageio_version();
}
} /* export "C" */
} /* extern "C" */

View File

@ -7,20 +7,10 @@
#pragma once
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
struct ImBuf;
bool imb_is_a_photoshop(const unsigned char *mem, size_t size);
int imb_save_photoshop(struct ImBuf *ibuf, const char *name, int flags);
struct ImBuf *imb_load_photoshop(const char *name, int flags, char *colorspace);
int OIIO_getVersionHex(void);
#ifdef __cplusplus

View File

@ -0,0 +1,398 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "openimageio_support.hh"
#include "BLI_blenlib.h"
#include "BKE_idprop.h"
#include "DNA_ID.h" /* ID property definitions. */
#include "IMB_allocimbuf.h"
#include "IMB_colormanagement.h"
#include "IMB_metadata.h"
OIIO_NAMESPACE_USING
using std::string;
using std::unique_ptr;
namespace blender::imbuf {
/* An OIIO IOProxy used during file packing to write into an in-memory #ImBuf buffer. */
class ImBufMemWriter : public Filesystem::IOProxy {
public:
ImBufMemWriter(ImBuf *ibuf) : IOProxy("", Write), ibuf_(ibuf)
{
}
const char *proxytype() const override
{
return "ImBufMemWriter";
}
size_t write(const void *buf, size_t size) override
{
size = pwrite(buf, size, m_pos);
m_pos += size;
return size;
}
size_t pwrite(const void *buf, size_t size, int64_t offset) override
{
/* If buffer is too small increase it. */
size_t end = offset + size;
while (end > ibuf_->encodedbuffersize) {
if (!imb_enlargeencodedbufferImBuf(ibuf_)) {
/* Out of memory. */
return 0;
}
}
memcpy(ibuf_->encodedbuffer + offset, buf, size);
if (end > ibuf_->encodedsize) {
ibuf_->encodedsize = end;
}
return size;
}
size_t size() const override
{
return ibuf_->encodedsize;
}
private:
ImBuf *ibuf_;
};
/* Utility to in-place expand an n-component pixel buffer into a 4-component buffer. */
template<typename T>
static void fill_all_channels(T *pixels, int width, int height, int components, T alpha)
{
const int64_t pixel_count = int64_t(width) * height;
if (components == 3) {
for (int64_t i = 0; i < pixel_count; i++) {
pixels[i * 4 + 3] = alpha;
}
}
else if (components == 1) {
for (int64_t i = 0; i < pixel_count; i++) {
pixels[i * 4 + 3] = alpha;
pixels[i * 4 + 2] = pixels[i * 4 + 0];
pixels[i * 4 + 1] = pixels[i * 4 + 0];
}
}
else if (components == 2) {
for (int64_t i = 0; i < pixel_count; i++) {
pixels[i * 4 + 3] = pixels[i * 4 + 1];
pixels[i * 4 + 2] = pixels[i * 4 + 0];
pixels[i * 4 + 1] = pixels[i * 4 + 0];
}
}
}
template<typename T>
static ImBuf *load_pixels(
ImageInput *in, int width, int height, int channels, int flags, bool use_all_planes)
{
/* Allocate the ImBuf for the image. */
constexpr bool is_float = sizeof(T) > 1;
const uint format_flag = is_float ? IB_rectfloat : IB_rect;
const uint ibuf_flags = (flags & IB_test) ? 0 : format_flag;
const int planes = use_all_planes ? 32 : 8 * channels;
ImBuf *ibuf = IMB_allocImBuf(width, height, planes, ibuf_flags);
if (!ibuf) {
return nullptr;
}
/* No need to load actual pixel data during the test phase. */
if (flags & IB_test) {
return ibuf;
}
/* Calculate an appropriate stride to read n-channels directly into
* the ImBuf 4-channel layout. */
const stride_t ibuf_xstride = sizeof(T) * 4;
const stride_t ibuf_ystride = ibuf_xstride * width;
const TypeDesc format = is_float ? TypeDesc::FLOAT : TypeDesc::UINT8;
uchar *rect = is_float ? reinterpret_cast<uchar *>(ibuf->rect_float) :
reinterpret_cast<uchar *>(ibuf->rect);
void *ibuf_data = rect + ((stride_t(height) - 1) * ibuf_ystride);
bool ok = in->read_image(
0, 0, 0, channels, format, ibuf_data, ibuf_xstride, -ibuf_ystride, AutoStride);
if (!ok) {
fprintf(stderr, "ImageInput::read_image() failed: %s\n", in->geterror().c_str());
IMB_freeImBuf(ibuf);
return nullptr;
}
/* ImBuf always needs 4 channels */
const T alpha_fill = is_float ? 1.0f : 0xFF;
fill_all_channels<T>(reinterpret_cast<T *>(rect), width, height, channels, alpha_fill);
return ibuf;
}
static void set_colorspace_name(char colorspace[IM_MAX_SPACE],
const ReadContext &ctx,
const ImageSpec &spec,
bool is_float)
{
const bool is_colorspace_set = (colorspace[0] != '\0');
if (is_colorspace_set) {
return;
}
/* Use a default role unless otherwise specified. */
if (ctx.use_colorspace_role >= 0) {
colorspace_set_default_role(colorspace, IM_MAX_SPACE, ctx.use_colorspace_role);
}
else if (is_float) {
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT);
}
else {
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
}
/* Override if necessary. */
if (ctx.use_embedded_colorspace) {
string ics = spec.get_string_attribute("oiio:ColorSpace");
char file_colorspace[IM_MAX_SPACE];
BLI_strncpy(file_colorspace, ics.c_str(), IM_MAX_SPACE);
/* Only use color-spaces that exist. */
if (colormanage_colorspace_get_named(file_colorspace)) {
BLI_strncpy(colorspace, file_colorspace, IM_MAX_SPACE);
}
}
}
/**
* Get an #ImBuf filled in with pixel data and associated metadata using the provided ImageInput.
*/
static ImBuf *get_oiio_ibuf(ImageInput *in, const ReadContext &ctx, char colorspace[IM_MAX_SPACE])
{
const ImageSpec &spec = in->spec();
const int width = spec.width;
const int height = spec.height;
const int channels = spec.nchannels;
const bool has_alpha = spec.alpha_channel != -1;
const bool is_float = spec.format.basesize() > 1;
if (channels < 1 || channels > 4) {
return nullptr;
}
const bool use_all_planes = has_alpha || ctx.use_all_planes;
ImBuf *ibuf = nullptr;
if (is_float) {
ibuf = load_pixels<float>(in, width, height, channels, ctx.flags, use_all_planes);
ibuf->channels = 4;
}
else {
ibuf = load_pixels<uchar>(in, width, height, channels, ctx.flags, use_all_planes);
}
/* Fill in common ibuf properties. */
if (ibuf) {
ibuf->ftype = ctx.file_type;
ibuf->flags |= (spec.format == TypeDesc::HALF) ? IB_halffloat : 0;
set_colorspace_name(colorspace, ctx, spec, is_float);
float x_res = spec.get_float_attribute("XResolution", 0.0f);
float y_res = spec.get_float_attribute("YResolution", 0.0f);
if (x_res > 0.0f && y_res > 0.0f) {
double scale = 1.0;
auto unit = spec.get_string_attribute("ResolutionUnit", "");
if (unit == "in" || unit == "inch") {
scale = 100.0 / 2.54;
}
else if (unit == "cm") {
scale = 100.0;
}
ibuf->ppm[0] = scale * x_res;
ibuf->ppm[1] = scale * y_res;
}
/* Transfer metadata to the ibuf if necessary. */
if (ctx.flags & IB_metadata) {
IMB_metadata_ensure(&ibuf->metadata);
ibuf->flags |= (spec.extra_attribs.empty()) ? 0 : IB_metadata;
for (const auto &attrib : spec.extra_attribs) {
IMB_metadata_set_field(ibuf->metadata, attrib.name().c_str(), attrib.get_string().c_str());
}
}
}
return ibuf;
}
/**
* Returns an ImageInput for the precise `format` requested using the provided IOMemReader.
* If successful, the ImageInput will be opened and ready for operations. Null will be returned if
* the format was not found or if the open call fails.
*/
static unique_ptr<ImageInput> get_oiio_reader(const char *format,
const ImageSpec &config,
Filesystem::IOMemReader &mem_reader,
ImageSpec &r_newspec)
{
/* Attempt to create a reader based on the passed in format. */
unique_ptr<ImageInput> in = ImageInput::create(format);
if (!in) {
return nullptr;
}
/* Open the reader using the ioproxy. */
in->set_ioproxy(&mem_reader);
bool ok = in->open("", r_newspec, config);
if (!ok) {
in.reset();
}
return in;
}
bool imb_oiio_check(const uchar *mem, size_t mem_size, const char *file_format)
{
ImageSpec config, spec;
/* This memory proxy must remain alive for the full duration of the read. */
Filesystem::IOMemReader mem_reader(cspan<uchar>(mem, mem_size));
unique_ptr<ImageInput> in = get_oiio_reader(file_format, config, mem_reader, spec);
return in ? true : false;
}
ImBuf *imb_oiio_read(const ReadContext &ctx,
const ImageSpec &config,
char colorspace[IM_MAX_SPACE],
ImageSpec &r_newspec)
{
/* This memory proxy must remain alive for the full duration of the read. */
Filesystem::IOMemReader mem_reader(cspan<uchar>(ctx.mem_start, ctx.mem_size));
unique_ptr<ImageInput> in = get_oiio_reader(ctx.file_format, config, mem_reader, r_newspec);
if (!in) {
return nullptr;
}
return get_oiio_ibuf(in.get(), ctx, colorspace);
}
bool imb_oiio_write(const WriteContext &ctx, const char *filepath, const ImageSpec &file_spec)
{
unique_ptr<ImageOutput> out = ImageOutput::create(ctx.file_format);
if (!out) {
return false;
}
auto write_op = [&out, &ctx]() {
return out->write_image(
ctx.mem_format, ctx.mem_start, ctx.mem_xstride, -ctx.mem_ystride, AutoStride);
};
bool ok = false;
if (ctx.flags & IB_mem) {
/* This memory proxy must remain alive for the full duration of the write. */
ImBufMemWriter writer(ctx.ibuf);
imb_addencodedbufferImBuf(ctx.ibuf);
out->set_ioproxy(&writer);
out->open("", file_spec);
ok = write_op();
}
else {
out->open(filepath, file_spec);
ok = write_op();
}
out->close();
return ok;
}
WriteContext imb_create_write_context(const char *file_format,
ImBuf *ibuf,
int flags,
bool prefer_float)
{
WriteContext ctx{};
ctx.file_format = file_format;
ctx.ibuf = ibuf;
ctx.flags = flags;
const int width = ibuf->x;
const int height = ibuf->y;
const bool use_float = prefer_float && (ibuf->rect_float != nullptr);
if (use_float) {
const int mem_channels = ibuf->channels ? ibuf->channels : 4;
ctx.mem_xstride = sizeof(float) * mem_channels;
ctx.mem_ystride = width * ctx.mem_xstride;
ctx.mem_format = TypeDesc::FLOAT;
ctx.mem_start = reinterpret_cast<uchar *>(ibuf->rect_float);
}
else {
const int mem_channels = 4;
ctx.mem_xstride = sizeof(uchar) * mem_channels;
ctx.mem_ystride = width * ctx.mem_xstride;
ctx.mem_format = TypeDesc::UINT8;
ctx.mem_start = reinterpret_cast<uchar *>(ibuf->rect);
}
/* We always write using a negative y-stride so ensure we start at the end. */
ctx.mem_start = ctx.mem_start + ((stride_t(height) - 1) * ctx.mem_ystride);
return ctx;
}
ImageSpec imb_create_write_spec(const WriteContext &ctx, int file_channels, TypeDesc data_format)
{
const int width = ctx.ibuf->x;
const int height = ctx.ibuf->y;
ImageSpec file_spec(width, height, file_channels, data_format);
/* Populate the spec with all common attributes.
*
* Care must be taken with the metadata:
* - It should be processed first, before the "Resolution" metadata below, to
* ensure the proper values end up in the ImageSpec
* - It needs to filter format-specific metadata that may no longer apply to
* the current format being written (e.g. metadata for tiff being written to a png)
*/
if (ctx.ibuf->metadata) {
for (IDProperty *prop = static_cast<IDProperty *>(ctx.ibuf->metadata->data.group.first); prop;
prop = prop->next) {
if (prop->type == IDP_STRING) {
/* If this property has a prefixed name (oiio:, tiff:, etc.) and it belongs to
* oiio or a different format, then skip. */
if (char *colon = strchr(prop->name, ':')) {
std::string prefix(prop->name, colon);
Strutil::to_lower(prefix);
if (prefix == "oiio" ||
(!STREQ(prefix.c_str(), ctx.file_format) && OIIO::is_imageio_format_name(prefix))) {
/* Skip this attribute. */
continue;
}
}
file_spec.attribute(prop->name, IDP_String(prop));
}
}
}
if (ctx.ibuf->ppm[0] > 0.0 && ctx.ibuf->ppm[1] > 0.0) {
/* More OIIO formats support inch than meter. */
file_spec.attribute("ResolutionUnit", "in");
file_spec.attribute("XResolution", float(ctx.ibuf->ppm[0] * 0.0254));
file_spec.attribute("YResolution", float(ctx.ibuf->ppm[1] * 0.0254));
}
return file_spec;
}
} // namespace blender::imbuf

View File

@ -0,0 +1,104 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <memory>
#include <OpenImageIO/filesystem.h>
#include <OpenImageIO/imageio.h>
#include "BLI_sys_types.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
namespace blender::imbuf {
/**
* Parameters and settings used while reading image formats.
*/
struct ReadContext {
const uchar *mem_start;
const size_t mem_size;
const char *file_format;
const eImbFileType file_type;
const int flags;
/* Override the automatic color-role choice with the value specified here. */
int use_colorspace_role = -1;
/* Allocate and use all ImBuf image planes even if the image has fewer. */
bool use_all_planes = false;
/* Use the colorspace provided in the image metadata when available. */
bool use_embedded_colorspace = false;
};
/**
* Parameters and settings used while writing image formats.
*/
struct WriteContext {
const char *file_format;
ImBuf *ibuf;
OIIO::stride_t mem_xstride;
OIIO::stride_t mem_ystride;
OIIO::TypeDesc mem_format;
uchar *mem_start;
int flags;
};
/**
* Check to see if we can load and open the given file format.
*/
bool imb_oiio_check(const uchar *mem, size_t mem_size, const char *file_format);
/**
* The primary method for reading data into an #ImBuf.
*
* During the `IB_test` phase of loading, the `colorspace` parameter will be populated
* with the appropriate colorspace name.
*
* Upon return, the `r_newspec` parameter will contain image format information
* which can be inspected afterwards if necessary.
*/
ImBuf *imb_oiio_read(const ReadContext &ctx,
const OIIO::ImageSpec &config,
char colorspace[IM_MAX_SPACE],
OIIO::ImageSpec &r_newspec);
/**
* The primary method for writing data from an #ImBuf to either a physical or in-memory
* destination.
*
* The `file_spec` parameter will typically come from #imb_create_write_spec.
*/
bool imb_oiio_write(const WriteContext &ctx,
const char *filepath,
const OIIO::ImageSpec &file_spec);
/**
* Create a #WriteContext based on the provided #ImBuf and format information.
*
* If the provided #ImBuf contains both byte and float buffers, the `prefer_float`
* flag controls which buffer to use. By default, if a float buffer exists it will
* be used.
*/
WriteContext imb_create_write_context(const char *file_format,
ImBuf *ibuf,
int flags,
bool prefer_float = true);
/**
* Returns an ImageSpec filled in with all common attributes associated with the #ImBuf
* provided as part of the #WriteContext.
*
* This includes optional metadata that has been attached to the #ImBuf and which should be
* written to the new file as necessary.
*/
OIIO::ImageSpec imb_create_write_spec(const WriteContext &ctx,
int file_channels,
OIIO::TypeDesc data_format);
} // namespace blender::imbuf

View File

@ -441,7 +441,7 @@ static void openexr_header_metadata(Header *header, struct ImBuf *ibuf)
IDProperty *prop;
for (prop = (IDProperty *)ibuf->metadata->data.group.first; prop; prop = prop->next) {
if (prop->type == IDP_STRING) {
if (prop->type == IDP_STRING && !STREQ(prop->name, "compression")) {
header->insert(prop->name, StringAttribute(IDP_String(prop)));
}
}

View File

@ -114,44 +114,7 @@ ImBuf *IMB_ibImageFromMemory(
return NULL;
}
static ImBuf *IMB_ibImageFromFile(const char *filepath,
int flags,
char colorspace[IM_MAX_SPACE],
const char *descr)
{
ImBuf *ibuf;
const ImFileType *type;
char effective_colorspace[IM_MAX_SPACE] = "";
if (colorspace) {
BLI_strncpy(effective_colorspace, colorspace, sizeof(effective_colorspace));
}
for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) {
if (type->load_filepath) {
ibuf = type->load_filepath(filepath, flags, effective_colorspace);
if (ibuf) {
imb_handle_alpha(ibuf, flags, colorspace, effective_colorspace);
return ibuf;
}
}
}
if ((flags & IB_test) == 0) {
fprintf(stderr, "%s: unknown fileformat (%s)\n", __func__, descr);
}
return NULL;
}
static bool imb_is_filepath_format(const char *filepath)
{
/* return true if this is one of the formats that can't be loaded from memory */
return BLI_path_extension_check_array(filepath, imb_ext_image_filepath_only);
}
ImBuf *IMB_loadifffile(
int file, const char *filepath, int flags, char colorspace[IM_MAX_SPACE], const char *descr)
ImBuf *IMB_loadifffile(int file, int flags, char colorspace[IM_MAX_SPACE], const char *descr)
{
ImBuf *ibuf;
uchar *mem;
@ -161,10 +124,6 @@ ImBuf *IMB_loadifffile(
return NULL;
}
if (imb_is_filepath_format(filepath)) {
return IMB_ibImageFromFile(filepath, flags, colorspace, descr);
}
size = BLI_file_descriptor_size(file);
imb_mmap_lock();
@ -198,7 +157,7 @@ ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_S
return NULL;
}
ibuf = IMB_loadifffile(file, filepath, flags, colorspace, filepath);
ibuf = IMB_loadifffile(file, flags, colorspace, filepath);
if (ibuf) {
BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name));
@ -277,7 +236,7 @@ ImBuf *IMB_testiffname(const char *filepath, int flags)
return NULL;
}
ibuf = IMB_loadifffile(file, filepath, flags | IB_test | IB_multilayer, colorspace, filepath);
ibuf = IMB_loadifffile(file, flags | IB_test | IB_multilayer, colorspace, filepath);
if (ibuf) {
BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name));

View File

@ -67,13 +67,6 @@ const char *imb_ext_image[] = {
NULL,
};
const char *imb_ext_image_filepath_only[] = {
".psd",
".pdd",
".psb",
NULL,
};
const char *imb_ext_movie[] = {
".avi", ".flc", ".mov", ".movie", ".mp4", ".m4v", ".m2v", ".m2t", ".m2ts", ".mts",
".ts", ".mv", ".avs", ".wmv", ".ogv", ".ogg", ".r3d", ".dv", ".mpeg", ".mpg",

View File

@ -480,7 +480,7 @@ static PyObject *M_imbuf_load(PyObject *UNUSED(self), PyObject *args, PyObject *
return NULL;
}
ImBuf *ibuf = IMB_loadifffile(file, filepath, IB_rect, NULL, filepath);
ImBuf *ibuf = IMB_loadifffile(file, IB_rect, NULL, filepath);
close(file);