forked from blender/blender
main sync #3
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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]);
|
||||
|
||||
/** \} */
|
||||
|
@ -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,
|
||||
|
29
source/blender/imbuf/intern/format_psd.cc
Normal file
29
source/blender/imbuf/intern/format_psd.cc
Normal 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);
|
||||
}
|
||||
}
|
@ -18,8 +18,10 @@ set(INC_SYS
|
||||
|
||||
set(SRC
|
||||
openimageio_api.h
|
||||
openimageio_support.hh
|
||||
|
||||
openimageio_api.cpp
|
||||
openimageio_support.cc
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
@ -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" */
|
||||
|
@ -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
|
||||
|
398
source/blender/imbuf/intern/oiio/openimageio_support.cc
Normal file
398
source/blender/imbuf/intern/oiio/openimageio_support.cc
Normal 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
|
104
source/blender/imbuf/intern/oiio/openimageio_support.hh
Normal file
104
source/blender/imbuf/intern/oiio/openimageio_support.hh
Normal 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
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user