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