Blender  V3.3
image_oiio.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright 2011-2022 Blender Foundation */
3 
4 #include "scene/image_oiio.h"
5 
6 #include "util/image.h"
7 #include "util/log.h"
8 #include "util/path.h"
9 
11 
12 OIIOImageLoader::OIIOImageLoader(const string &filepath) : filepath(filepath)
13 {
14 }
15 
17 {
18 }
19 
21  ImageMetaData &metadata)
22 {
23  /* Perform preliminary checks, with meaningful logging. */
24  if (!path_exists(filepath.string())) {
25  VLOG_WARNING << "File '" << filepath.string() << "' does not exist.";
26  return false;
27  }
28  if (path_is_directory(filepath.string())) {
29  VLOG_WARNING << "File '" << filepath.string() << "' is a directory, can't use as image.";
30  return false;
31  }
32 
33  unique_ptr<ImageInput> in(ImageInput::create(filepath.string()));
34 
35  if (!in) {
36  return false;
37  }
38 
39  ImageSpec spec;
40  if (!in->open(filepath.string(), spec)) {
41  return false;
42  }
43 
44  metadata.width = spec.width;
45  metadata.height = spec.height;
46  metadata.depth = spec.depth;
47  metadata.compress_as_srgb = false;
48 
49  /* Check the main format, and channel formats. */
50  size_t channel_size = spec.format.basesize();
51 
52  bool is_float = false;
53  bool is_half = false;
54 
55  if (spec.format.is_floating_point()) {
56  is_float = true;
57  }
58 
59  for (size_t channel = 0; channel < spec.channelformats.size(); channel++) {
60  channel_size = max(channel_size, spec.channelformats[channel].basesize());
61  if (spec.channelformats[channel].is_floating_point()) {
62  is_float = true;
63  }
64  }
65 
66  /* check if it's half float */
67  if (spec.format == TypeDesc::HALF) {
68  is_half = true;
69  }
70 
71  /* set type and channels */
72  metadata.channels = spec.nchannels;
73 
74  if (is_half) {
75  metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_HALF4 : IMAGE_DATA_TYPE_HALF;
76  }
77  else if (is_float) {
78  metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_FLOAT4 : IMAGE_DATA_TYPE_FLOAT;
79  }
80  else if (spec.format == TypeDesc::USHORT) {
81  metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_USHORT4 : IMAGE_DATA_TYPE_USHORT;
82  }
83  else {
84  metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_BYTE4 : IMAGE_DATA_TYPE_BYTE;
85  }
86 
87  metadata.colorspace_file_format = in->format_name();
88 
89  in->close();
90 
91  return true;
92 }
93 
94 template<TypeDesc::BASETYPE FileFormat, typename StorageType>
95 static void oiio_load_pixels(const ImageMetaData &metadata,
96  const unique_ptr<ImageInput> &in,
97  const bool associate_alpha,
98  StorageType *pixels)
99 {
100  const size_t width = metadata.width;
101  const size_t height = metadata.height;
102  const int depth = metadata.depth;
103  const int components = metadata.channels;
104 
105  /* Read pixels through OpenImageIO. */
106  StorageType *readpixels = pixels;
107  vector<StorageType> tmppixels;
108  if (components > 4) {
109  tmppixels.resize(width * height * components);
110  readpixels = &tmppixels[0];
111  }
112 
113  if (depth <= 1) {
114  size_t scanlinesize = width * components * sizeof(StorageType);
115  in->read_image(FileFormat,
116  (uchar *)readpixels + (height - 1) * scanlinesize,
117  AutoStride,
118  -scanlinesize,
119  AutoStride);
120  }
121  else {
122  in->read_image(FileFormat, (uchar *)readpixels);
123  }
124 
125  if (components > 4) {
126  size_t dimensions = width * height;
127  for (size_t i = dimensions - 1, pixel = 0; pixel < dimensions; pixel++, i--) {
128  pixels[i * 4 + 3] = tmppixels[i * components + 3];
129  pixels[i * 4 + 2] = tmppixels[i * components + 2];
130  pixels[i * 4 + 1] = tmppixels[i * components + 1];
131  pixels[i * 4 + 0] = tmppixels[i * components + 0];
132  }
133  tmppixels.clear();
134  }
135 
136  /* CMYK to RGBA. */
137  const bool cmyk = strcmp(in->format_name(), "jpeg") == 0 && components == 4;
138  if (cmyk) {
139  const StorageType one = util_image_cast_from_float<StorageType>(1.0f);
140 
141  const size_t num_pixels = width * height * depth;
142  for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
143  float c = util_image_cast_to_float(pixels[i * 4 + 0]);
144  float m = util_image_cast_to_float(pixels[i * 4 + 1]);
145  float y = util_image_cast_to_float(pixels[i * 4 + 2]);
146  float k = util_image_cast_to_float(pixels[i * 4 + 3]);
147  pixels[i * 4 + 0] = util_image_cast_from_float<StorageType>((1.0f - c) * (1.0f - k));
148  pixels[i * 4 + 1] = util_image_cast_from_float<StorageType>((1.0f - m) * (1.0f - k));
149  pixels[i * 4 + 2] = util_image_cast_from_float<StorageType>((1.0f - y) * (1.0f - k));
150  pixels[i * 4 + 3] = one;
151  }
152  }
153 
154  if (components == 4 && associate_alpha) {
155  size_t dimensions = width * height;
156  for (size_t i = dimensions - 1, pixel = 0; pixel < dimensions; pixel++, i--) {
157  const StorageType alpha = pixels[i * 4 + 3];
158  pixels[i * 4 + 0] = util_image_multiply_native(pixels[i * 4 + 0], alpha);
159  pixels[i * 4 + 1] = util_image_multiply_native(pixels[i * 4 + 1], alpha);
160  pixels[i * 4 + 2] = util_image_multiply_native(pixels[i * 4 + 2], alpha);
161  }
162  }
163 }
164 
166  void *pixels,
167  const size_t,
168  const bool associate_alpha)
169 {
170  unique_ptr<ImageInput> in = NULL;
171 
172  /* NOTE: Error logging is done in meta data acquisition. */
173  if (!path_exists(filepath.string()) || path_is_directory(filepath.string())) {
174  return false;
175  }
176 
177  /* load image from file through OIIO */
178  in = unique_ptr<ImageInput>(ImageInput::create(filepath.string()));
179  if (!in) {
180  return false;
181  }
182 
183  ImageSpec spec = ImageSpec();
184  ImageSpec config = ImageSpec();
185 
186  /* Load without automatic OIIO alpha conversion, we do it ourselves. OIIO
187  * will associate alpha in the 8bit buffer for PNGs, which leads to too
188  * much precision loss when we load it as half float to do a color-space
189  * transform. */
190  config.attribute("oiio:UnassociatedAlpha", 1);
191 
192  if (!in->open(filepath.string(), spec, config)) {
193  return false;
194  }
195 
196  bool do_associate_alpha = false;
197  if (associate_alpha) {
198  do_associate_alpha = spec.get_int_attribute("oiio:UnassociatedAlpha", 0);
199 
200  if (!do_associate_alpha && spec.alpha_channel != -1) {
201  /* Workaround OIIO not detecting TGA file alpha the same as Blender (since #3019).
202  * We want anything not marked as premultiplied alpha to get associated. */
203  if (strcmp(in->format_name(), "targa") == 0) {
204  do_associate_alpha = spec.get_int_attribute("targa:alpha_type", -1) != 4;
205  }
206  /* OIIO DDS reader never sets UnassociatedAlpha attribute. */
207  if (strcmp(in->format_name(), "dds") == 0) {
208  do_associate_alpha = true;
209  }
210  /* Workaround OIIO bug that sets oiio:UnassociatedAlpha on the last layer
211  * but not composite image that we read. */
212  if (strcmp(in->format_name(), "psd") == 0) {
213  do_associate_alpha = true;
214  }
215  }
216  }
217 
218  switch (metadata.type) {
221  oiio_load_pixels<TypeDesc::UINT8, uchar>(metadata, in, do_associate_alpha, (uchar *)pixels);
222  break;
225  oiio_load_pixels<TypeDesc::USHORT, uint16_t>(
226  metadata, in, do_associate_alpha, (uint16_t *)pixels);
227  break;
230  oiio_load_pixels<TypeDesc::HALF, half>(metadata, in, do_associate_alpha, (half *)pixels);
231  break;
234  oiio_load_pixels<TypeDesc::FLOAT, float>(metadata, in, do_associate_alpha, (float *)pixels);
235  break;
241  break;
242  }
243 
244  in->close();
245  return true;
246 }
247 
248 string OIIOImageLoader::name() const
249 {
250  return path_filename(filepath.string());
251 }
252 
254 {
255  return filepath;
256 }
257 
258 bool OIIOImageLoader::equals(const ImageLoader &other) const
259 {
260  const OIIOImageLoader &other_loader = (const OIIOImageLoader &)other;
261  return filepath == other_loader.filepath;
262 }
263 
unsigned char uchar
Definition: BLI_sys_types.h:70
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
#define USHORT
Definition: GeoCommon.h:7
ImageDataType type
const char * colorspace_file_format
ustring osl_filepath() const override
Definition: image_oiio.cpp:253
bool equals(const ImageLoader &other) const override
Definition: image_oiio.cpp:258
bool load_pixels(const ImageMetaData &metadata, void *pixels, const size_t pixels_size, const bool associate_alpha) override
Definition: image_oiio.cpp:165
ustring filepath
Definition: image_oiio.h:30
OIIOImageLoader(const string &filepath)
Definition: image_oiio.cpp:12
bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override
Definition: image_oiio.cpp:20
string name() const override
Definition: image_oiio.cpp:248
Definition: half.h:41
#define CCL_NAMESPACE_END
Definition: cuda/compat.h:9
@ HALF
Definition: curve_bevel.c:29
T util_image_multiply_native(T a, T b)
float util_image_cast_to_float(T value)
static void oiio_load_pixels(const ImageMetaData &metadata, const unique_ptr< ImageInput > &in, const bool associate_alpha, StorageType *pixels)
Definition: image_oiio.cpp:95
ccl_gpu_kernel_postfix ccl_global float int num_pixels
#define VLOG_WARNING
Definition: log.h:75
static unsigned c
Definition: RandGen.cpp:83
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRefNull prop_name, int32_t value)
Allocate a new IDProperty of type IDP_INT, set its name and value.
bool path_is_directory(const string &path)
Definition: path.cpp:578
bool path_exists(const string &path)
Definition: path.cpp:559
string path_filename(const string &path)
Definition: path.cpp:376
unsigned short uint16_t
Definition: stdint.h:79
float max
@ IMAGE_DATA_NUM_TYPES
Definition: util/texture.h:43
@ IMAGE_DATA_TYPE_BYTE
Definition: util/texture.h:34
@ IMAGE_DATA_TYPE_FLOAT
Definition: util/texture.h:33
@ IMAGE_DATA_TYPE_NANOVDB_FP16
Definition: util/texture.h:41
@ IMAGE_DATA_TYPE_FLOAT4
Definition: util/texture.h:30
@ IMAGE_DATA_TYPE_USHORT4
Definition: util/texture.h:36
@ IMAGE_DATA_TYPE_USHORT
Definition: util/texture.h:37
@ IMAGE_DATA_TYPE_NANOVDB_FLOAT
Definition: util/texture.h:38
@ IMAGE_DATA_TYPE_NANOVDB_FLOAT3
Definition: util/texture.h:39
@ IMAGE_DATA_TYPE_HALF
Definition: util/texture.h:35
@ IMAGE_DATA_TYPE_BYTE4
Definition: util/texture.h:31
@ IMAGE_DATA_TYPE_HALF4
Definition: util/texture.h:32
@ IMAGE_DATA_TYPE_NANOVDB_FPN
Definition: util/texture.h:40