Blender V5.0
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#include "util/unique_ptr.h"
11
13
15
17
19 ImageMetaData &metadata)
20{
21 /* Perform preliminary checks, with meaningful logging. */
22 if (filepath.empty()) {
23 return false;
24 }
25
26 if (!path_exists(filepath.string())) {
27 LOG_ERROR << "Image file '" << filepath.string() << "' does not exist.";
28 return false;
29 }
30 if (path_is_directory(filepath.string())) {
31 LOG_ERROR << "Image file '" << filepath.string() << "' is a directory, cannot use as image.";
32 return false;
33 }
34
35 unique_ptr<ImageInput> in(ImageInput::create(filepath.string()));
36 if (!in) {
37 LOG_ERROR << "Image file '" << filepath.string() << "' failed to load.";
38 return false;
39 }
40
41 ImageSpec spec;
42 if (!in->open(filepath.string(), spec)) {
43 LOG_ERROR << "Image file '" << filepath.string() << "' failed to open.";
44 return false;
45 }
46
47 metadata.width = spec.width;
48 metadata.height = spec.height;
49 metadata.compress_as_srgb = false;
50
51 /* Check the main format, and channel formats. */
52 size_t channel_size = spec.format.basesize();
53
54 bool is_float = false;
55 bool is_half = false;
56
57 if (spec.format.is_floating_point()) {
58 is_float = true;
59 }
60
61 for (size_t channel = 0; channel < spec.channelformats.size(); channel++) {
62 channel_size = max(channel_size, spec.channelformats[channel].basesize());
63 if (spec.channelformats[channel].is_floating_point()) {
64 is_float = true;
65 }
66 }
67
68 /* check if it's half float */
69 if (spec.format == TypeDesc::HALF) {
70 is_half = true;
71 }
72
73 /* set type and channels */
74 metadata.channels = spec.nchannels;
75
76 if (is_half) {
77 metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_HALF4 : IMAGE_DATA_TYPE_HALF;
78 }
79 else if (is_float) {
80 metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_FLOAT4 : IMAGE_DATA_TYPE_FLOAT;
81 }
82 else if (spec.format == TypeDesc::USHORT) {
83 metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_USHORT4 : IMAGE_DATA_TYPE_USHORT;
84 }
85 else {
86 metadata.type = (metadata.channels > 1) ? IMAGE_DATA_TYPE_BYTE4 : IMAGE_DATA_TYPE_BYTE;
87 }
88
89 metadata.colorspace_file_format = in->format_name();
90 metadata.colorspace_file_hint = spec.get_string_attribute("oiio:ColorSpace");
91
92 in->close();
93
94 return true;
95}
96
97template<TypeDesc::BASETYPE FileFormat, typename StorageType>
98static void oiio_load_pixels(const ImageMetaData &metadata,
100 const bool associate_alpha,
101 StorageType *pixels)
102{
103 const size_t width = metadata.width;
104 const size_t height = metadata.height;
105 const int components = metadata.channels;
106
107 /* Read pixels through OpenImageIO. */
108 StorageType *readpixels = pixels;
109 vector<StorageType> tmppixels;
110 if (components > 4) {
111 tmppixels.resize(width * height * components);
112 readpixels = &tmppixels[0];
113 }
114
115 const size_t scanlinesize = width * components * sizeof(StorageType);
116 in->read_image(0,
117 0,
118 0,
119 components,
120 FileFormat,
121 (uchar *)readpixels + (height - 1) * scanlinesize,
122 AutoStride,
123 -scanlinesize,
124 AutoStride);
125
126 if (components > 4) {
127 const size_t dimensions = width * height;
128 for (size_t i = dimensions - 1, pixel = 0; pixel < dimensions; pixel++, i--) {
129 pixels[i * 4 + 3] = tmppixels[i * components + 3];
130 pixels[i * 4 + 2] = tmppixels[i * components + 2];
131 pixels[i * 4 + 1] = tmppixels[i * components + 1];
132 pixels[i * 4 + 0] = tmppixels[i * components + 0];
133 }
134 tmppixels.clear();
135 }
136
137 /* CMYK to RGBA. */
138 const bool cmyk = strcmp(in->format_name(), "jpeg") == 0 && components == 4;
139 if (cmyk) {
140 const StorageType one = util_image_cast_from_float<StorageType>(1.0f);
141
142 const size_t num_pixels = width * height;
143 for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
144 const float c = util_image_cast_to_float(pixels[i * 4 + 0]);
145 const float m = util_image_cast_to_float(pixels[i * 4 + 1]);
146 const float y = util_image_cast_to_float(pixels[i * 4 + 2]);
147 const float k = util_image_cast_to_float(pixels[i * 4 + 3]);
148 pixels[i * 4 + 0] = util_image_cast_from_float<StorageType>((1.0f - c) * (1.0f - k));
149 pixels[i * 4 + 1] = util_image_cast_from_float<StorageType>((1.0f - m) * (1.0f - k));
150 pixels[i * 4 + 2] = util_image_cast_from_float<StorageType>((1.0f - y) * (1.0f - k));
151 pixels[i * 4 + 3] = one;
152 }
153 }
154
155 if (components == 4 && associate_alpha) {
156 const size_t dimensions = width * height;
157 for (size_t i = dimensions - 1, pixel = 0; pixel < dimensions; pixel++, i--) {
158 const StorageType alpha = pixels[i * 4 + 3];
159 pixels[i * 4 + 0] = util_image_multiply_native(pixels[i * 4 + 0], alpha);
160 pixels[i * 4 + 1] = util_image_multiply_native(pixels[i * 4 + 1], alpha);
161 pixels[i * 4 + 2] = util_image_multiply_native(pixels[i * 4 + 2], alpha);
162 }
163 }
164}
165
167 void *pixels,
168 const size_t /*pixels_size*/,
169 const bool associate_alpha)
170{
171 unique_ptr<ImageInput> in = nullptr;
172
173 /* NOTE: Error logging is done in meta data acquisition. */
174 if (!path_exists(filepath.string()) || path_is_directory(filepath.string())) {
175 return false;
176 }
177
178 /* load image from file through OIIO */
179 in = unique_ptr<ImageInput>(ImageInput::create(filepath.string()));
180 if (!in) {
181 return false;
182 }
183
184 ImageSpec spec = ImageSpec();
185 ImageSpec config = ImageSpec();
186
187 /* Load without automatic OIIO alpha conversion, we do it ourselves. OIIO
188 * will associate alpha in the 8bit buffer for PNGs, which leads to too
189 * much precision loss when we load it as half float to do a color-space 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;
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;
243 break;
244 }
245
246 in->close();
247 return true;
248}
249
251{
252 return path_filename(filepath.string());
253}
254
256{
257 return filepath;
258}
259
260bool OIIOImageLoader::equals(const ImageLoader &other) const
261{
262 const OIIOImageLoader &other_loader = (const OIIOImageLoader &)other;
263 return filepath == other_loader.filepath;
264}
265
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:30
~OIIOImageLoader() override
OIIOImageLoader(const string &filepath)
bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override
string name() const override
Definition half.h:41
T util_image_cast_from_float(const float value)
T util_image_multiply_native(T a, T b)
float util_image_cast_to_float(T value)
#define CCL_NAMESPACE_END
#define in
static void oiio_load_pixels(const ImageMetaData &metadata, const unique_ptr< ImageInput > &in, const bool associate_alpha, StorageType *pixels)
#define LOG_ERROR
Definition log.h:101
bool path_is_directory(const string &path)
Definition path.cpp:582
bool path_exists(const string &path)
Definition path.cpp:563
string path_filename(const string &path)
Definition path.cpp:378
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
@ IMAGE_DATA_NUM_TYPES
Definition texture.h:48
@ IMAGE_DATA_TYPE_BYTE
Definition texture.h:37
@ IMAGE_DATA_TYPE_FLOAT
Definition texture.h:36
@ IMAGE_DATA_TYPE_NANOVDB_FP16
Definition texture.h:45
@ IMAGE_DATA_TYPE_FLOAT4
Definition texture.h:33
@ IMAGE_DATA_TYPE_USHORT4
Definition texture.h:39
@ IMAGE_DATA_TYPE_USHORT
Definition texture.h:40
@ IMAGE_DATA_TYPE_NANOVDB_EMPTY
Definition texture.h:46
@ IMAGE_DATA_TYPE_NANOVDB_FLOAT
Definition texture.h:41
@ IMAGE_DATA_TYPE_NANOVDB_FLOAT3
Definition texture.h:42
@ IMAGE_DATA_TYPE_HALF
Definition texture.h:38
@ IMAGE_DATA_TYPE_BYTE4
Definition texture.h:34
@ IMAGE_DATA_TYPE_HALF4
Definition texture.h:35
@ IMAGE_DATA_TYPE_NANOVDB_FLOAT4
Definition texture.h:43
@ IMAGE_DATA_TYPE_NANOVDB_FPN
Definition texture.h:44