Blender V4.5
blender/image.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 <algorithm>
6
7#include "DNA_image_types.h"
8
9#include "IMB_imbuf_types.hh"
10
11#include "BKE_image.hh"
12
13#include "blender/image.h"
14#include "blender/session.h"
15
16#include "util/half.h"
17#include "util/types_float4.h"
18
20
21/* Packed Images */
22
25 const int frame,
26 const int tile_number,
27 const bool is_preview_render)
30 /* Don't free cache for preview render to avoid race condition from #93560, to be fixed
31 * properly later as we are close to release. */
32 free_cache(!is_preview_render && !BKE_image_has_loaded_ibuf(b_image))
33{
34 this->b_iuser.framenr = frame;
35 if (b_image->source != IMA_SRC_TILED) {
36 /* Image sequences currently not supported by this image loader. */
37 assert(b_image->source != IMA_SRC_SEQUENCE);
38 }
39 else {
40 /* Set UDIM tile, each can have different resolution. */
41 this->b_iuser.tile = tile_number;
42 }
43}
44
46 ImageMetaData &metadata)
47{
48 bool is_float = false;
49
50 {
51 void *lock;
53 if (ibuf) {
54 is_float = ibuf->float_buffer.data != nullptr;
55 metadata.width = ibuf->x;
56 metadata.height = ibuf->y;
57 metadata.channels = (is_float) ? ibuf->channels : 4;
58 }
59 else {
60 metadata.width = 0;
61 metadata.height = 0;
62 metadata.channels = 0;
63 }
65 }
66
67 metadata.depth = 1;
68
69 if (is_float) {
70 if (metadata.channels == 1) {
71 metadata.type = IMAGE_DATA_TYPE_FLOAT;
72 }
73 else {
74 metadata.channels = 4;
76 }
77
78 /* Float images are already converted on the Blender side,
79 * no need to do anything in Cycles. */
81 }
82 else {
83 /* In some cases (e.g. #94135), the colorspace setting in Blender gets updated as part of the
84 * metadata queries in this function, so update the colorspace setting here. */
85 metadata.colorspace = b_image->colorspace_settings.name;
86 metadata.type = IMAGE_DATA_TYPE_BYTE4;
87 }
88
89 return true;
90}
91
92static void load_float_pixels(const ImBuf *ibuf, const ImageMetaData &metadata, float *out_pixels)
93{
94 const size_t num_pixels = ((size_t)metadata.width) * metadata.height;
95 const int out_channels = metadata.channels;
96 const int in_channels = ibuf->channels;
97 const float *in_pixels = ibuf->float_buffer.data;
98
99 if (in_pixels && out_channels == in_channels) {
100 /* Straight copy pixel data. */
101 memcpy(out_pixels, in_pixels, num_pixels * out_channels * sizeof(float));
102 }
103 else if (in_pixels && out_channels == 4) {
104 /* Fill channels to 4. */
105 float *out_pixel = out_pixels;
106 const float *in_pixel = in_pixels;
107 for (size_t i = 0; i < num_pixels; i++) {
108 out_pixel[0] = in_pixel[0];
109 out_pixel[1] = (in_channels >= 2) ? in_pixel[1] : 0.0f;
110 out_pixel[2] = (in_channels >= 3) ? in_pixel[2] : 0.0f;
111 out_pixel[3] = (in_channels >= 4) ? in_pixel[3] : 1.0f;
112 out_pixel += out_channels;
113 in_pixel += in_channels;
114 }
115 }
116 else {
117 /* Missing or invalid pixel data. */
118 if (out_channels == 1) {
119 std::fill(out_pixels, out_pixels + num_pixels, 0.0f);
120 }
121 else {
122 std::fill((float4 *)out_pixels,
123 (float4 *)out_pixels + num_pixels,
124 make_float4(1.0f, 0.0f, 1.0f, 1.0f));
125 }
126 }
127}
128
129static void load_half_pixels(const ImBuf *ibuf,
130 const ImageMetaData &metadata,
131 half *out_pixels,
132 const bool associate_alpha)
133{
134 /* Half float. Blender does not have a half type, but in some cases
135 * we up-sample byte to half to avoid precision loss for colorspace
136 * conversion. */
137 const size_t num_pixels = ((size_t)metadata.width) * metadata.height;
138 const int out_channels = metadata.channels;
139 const int in_channels = 4;
140 const uchar *in_pixels = ibuf->byte_buffer.data;
141
142 if (in_pixels) {
143 /* Convert uchar to half. */
144 const uchar *in_pixel = in_pixels;
145 half *out_pixel = out_pixels;
146 if (associate_alpha && out_channels == in_channels) {
147 for (size_t i = 0; i < num_pixels; i++, in_pixel += in_channels, out_pixel += out_channels) {
148 const float alpha = util_image_cast_to_float(in_pixel[3]);
149 out_pixel[0] = float_to_half_image(util_image_cast_to_float(in_pixel[0]) * alpha);
150 out_pixel[1] = float_to_half_image(util_image_cast_to_float(in_pixel[1]) * alpha);
151 out_pixel[2] = float_to_half_image(util_image_cast_to_float(in_pixel[2]) * alpha);
152 out_pixel[3] = float_to_half_image(alpha);
153 }
154 }
155 else {
156 for (size_t i = 0; i < num_pixels; i++) {
157 for (int c = 0; c < out_channels; c++, in_pixel++, out_pixel++) {
158 *out_pixel = float_to_half_image(util_image_cast_to_float(*in_pixel));
159 }
160 }
161 }
162 }
163 else {
164 /* Missing or invalid pixel data. */
165 if (out_channels == 1) {
166 std::fill(out_pixels, out_pixels + num_pixels, float_to_half_image(0.0f));
167 }
168 else {
169 std::fill((half4 *)out_pixels,
170 (half4 *)out_pixels + num_pixels,
171 float4_to_half4_display(make_float4(1.0f, 0.0f, 1.0f, 1.0f)));
172 }
173 }
174}
175
176static void load_byte_pixels(const ImBuf *ibuf,
177 const ImageMetaData &metadata,
178 uchar *out_pixels,
179 const bool associate_alpha)
180{
181 const size_t num_pixels = ((size_t)metadata.width) * metadata.height;
182 const int out_channels = metadata.channels;
183 const int in_channels = 4;
184 const uchar *in_pixels = ibuf->byte_buffer.data;
185
186 if (in_pixels) {
187 /* Straight copy pixel data. */
188 memcpy(out_pixels, in_pixels, num_pixels * in_channels * sizeof(unsigned char));
189
190 if (associate_alpha && out_channels == in_channels) {
191 /* Premultiply, byte images are always straight for Blender. */
192 unsigned char *out_pixel = (unsigned char *)out_pixels;
193 for (size_t i = 0; i < num_pixels; i++, out_pixel += 4) {
194 out_pixel[0] = (out_pixel[0] * out_pixel[3]) / 255;
195 out_pixel[1] = (out_pixel[1] * out_pixel[3]) / 255;
196 out_pixel[2] = (out_pixel[2] * out_pixel[3]) / 255;
197 }
198 }
199 }
200 else {
201 /* Missing or invalid pixel data. */
202 if (out_channels == 1) {
203 std::fill(out_pixels, out_pixels + num_pixels, 0.0f);
204 }
205 else {
206 std::fill(
207 (uchar4 *)out_pixels, (uchar4 *)out_pixels + num_pixels, make_uchar4(255, 0, 255, 255));
208 }
209 }
210}
211
213 void *out_pixels,
214 const size_t /*out_pixels_size*/,
215 const bool associate_alpha)
216{
217 void *lock;
219
220 /* Image changed since we requested metadata, assume we'll get a signal to reload it later. */
221 const bool mismatch = (ibuf == nullptr || ibuf->x != metadata.width ||
222 ibuf->y != metadata.height);
223
224 if (!mismatch) {
225 if (metadata.type == IMAGE_DATA_TYPE_FLOAT || metadata.type == IMAGE_DATA_TYPE_FLOAT4) {
226 load_float_pixels(ibuf, metadata, (float *)out_pixels);
227 }
228 else if (metadata.type == IMAGE_DATA_TYPE_HALF || metadata.type == IMAGE_DATA_TYPE_HALF4) {
229 load_half_pixels(ibuf, metadata, (half *)out_pixels, associate_alpha);
230 }
231 else {
232 load_byte_pixels(ibuf, metadata, (uchar *)out_pixels, associate_alpha);
233 }
234 }
235
237
238 /* Free image buffers to save memory during render. */
239 if (free_cache) {
241 }
242
243 return !mismatch;
244}
245
247{
248 return b_image->id.name + 2;
249}
250
252{
253 const BlenderImageLoader &other_loader = (const BlenderImageLoader &)other;
254 return b_image == other_loader.b_image && b_iuser.framenr == other_loader.b_iuser.framenr &&
255 b_iuser.tile == other_loader.b_iuser.tile;
256}
257
259{
260 return b_iuser.tile;
261}
262
263/* Point Density */
264
266 BL::ShaderNodeTexPointDensity b_node)
268{
269}
270
272 ImageMetaData &metadata)
273{
274 metadata.channels = 4;
275 metadata.width = b_node.resolution();
276 metadata.height = metadata.width;
277 metadata.depth = metadata.width;
278 metadata.type = IMAGE_DATA_TYPE_FLOAT4;
279 return true;
280}
281
283 void *pixels,
284 const size_t /*pixels_size*/,
285 const bool /*associate_alpha*/)
286{
287 int length;
288 b_node.calc_point_density(b_depsgraph, &length, (float **)&pixels);
289 return true;
290}
291
293{
294 /* Force builtin images to be loaded along with Blender data sync. This
295 * is needed because we may be reading from depsgraph evaluated data which
296 * can be freed by Blender before Cycles reads it.
297 *
298 * TODO: the assumption that no further access to builtin image data will
299 * happen is really weak, and likely to break in the future. We should find
300 * a better solution to hand over the data directly to the image manager
301 * instead of through callbacks whose timing is difficult to control. */
302 ImageManager *manager = session->scene->image_manager.get();
303 Device *device = session->device.get();
304 manager->device_load_builtin(device, session->scene.get(), session->progress);
305}
306
308{
309 return BL::ShaderNodeTexPointDensity(b_node).name();
310}
311
313{
314 const BlenderPointDensityLoader &other_loader = (const BlenderPointDensityLoader &)other;
315 return b_node == other_loader.b_node && b_depsgraph == other_loader.b_depsgraph;
316}
317
ImBuf * BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock)
void BKE_image_free_buffers_ex(Image *image, bool do_lock)
bool BKE_image_has_loaded_ibuf(Image *image)
unsigned char uchar
@ IMA_SRC_TILED
volatile int lock
static void load_half_pixels(const ImBuf *ibuf, const ImageMetaData &metadata, half *out_pixels, const bool associate_alpha)
static void load_byte_pixels(const ImBuf *ibuf, const ImageMetaData &metadata, uchar *out_pixels, const bool associate_alpha)
static void load_float_pixels(const ImBuf *ibuf, const ImageMetaData &metadata, float *out_pixels)
bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override
bool load_pixels(const ImageMetaData &metadata, void *pixels, const size_t pixels_size, const bool associate_alpha) override
bool equals(const ImageLoader &other) const override
BlenderImageLoader(::Image *b_image, ::ImageUser *b_iuser, const int frame, const int tile_number, const bool is_preview_render)
int get_tile_number() const override
string name() const override
bool equals(const ImageLoader &other) const override
BL::ShaderNodeTexPointDensity b_node
BlenderPointDensityLoader(BL::Depsgraph depsgraph, BL::ShaderNodeTexPointDensity b_node)
string name() const override
bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override
bool load_pixels(const ImageMetaData &metadata, void *pixels, const size_t pixels_size, const bool associate_alpha) override
unique_ptr< Session > session
void device_load_builtin(Device *device, Scene *scene, Progress &progress)
ImageDataType type
Definition half.h:41
float util_image_cast_to_float(T value)
#define CCL_NAMESPACE_END
ccl_device_forceinline float4 make_float4(const float x, const float y, const float z, const float w)
ccl_device_forceinline uchar4 make_uchar4(const uchar x, const uchar y, const uchar z, const uchar w)
VecBase< float, 4 > float4
float length(VecOp< float, D >) RET
ccl_device_inline half float_to_half_image(const float f)
Definition half.h:70
ccl_device_inline half4 float4_to_half4_display(const float4 f)
Definition half.h:151
ustring u_colorspace_raw
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
Definition half.h:60
i
Definition text_draw.cc:230
@ IMAGE_DATA_TYPE_FLOAT
@ IMAGE_DATA_TYPE_FLOAT4
@ IMAGE_DATA_TYPE_HALF
@ IMAGE_DATA_TYPE_BYTE4
@ IMAGE_DATA_TYPE_HALF4