Blender V5.0
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/transform.h"
18#include "util/types_float4.h"
19
21
22/* Packed Images */
23
26 const int frame,
27 const int tile_number,
28 const bool is_preview_render)
31 /* Don't free cache for preview render to avoid race condition from #93560, to be fixed
32 * properly later as we are close to release. */
33 free_cache(!is_preview_render && !BKE_image_has_loaded_ibuf(b_image))
34{
35 this->b_iuser.framenr = frame;
36 if (b_image->source != IMA_SRC_TILED) {
37 /* Image sequences currently not supported by this image loader. */
38 assert(b_image->source != IMA_SRC_SEQUENCE);
39 }
40 else {
41 /* Set UDIM tile, each can have different resolution. */
42 this->b_iuser.tile = tile_number;
43 }
44}
45
47 ImageMetaData &metadata)
48{
49 bool is_float = false;
50
51 {
52 void *lock;
54 if (ibuf) {
55 is_float = ibuf->float_buffer.data != nullptr;
56 metadata.width = ibuf->x;
57 metadata.height = ibuf->y;
58 metadata.channels = (is_float) ? ibuf->channels : 4;
59 }
60 else {
61 metadata.width = 0;
62 metadata.height = 0;
63 metadata.channels = 0;
64 }
66 }
67
68 if (is_float) {
69 if (metadata.channels == 1) {
70 metadata.type = IMAGE_DATA_TYPE_FLOAT;
71 }
72 else {
73 metadata.channels = 4;
75 }
76
77 /* Float images are already converted on the Blender side,
78 * no need to do anything in Cycles. */
80 }
81 else {
82 /* In some cases (e.g. #94135), the colorspace setting in Blender gets updated as part of the
83 * metadata queries in this function, so update the colorspace setting here. */
84 metadata.colorspace = b_image->colorspace_settings.name;
85 metadata.type = IMAGE_DATA_TYPE_BYTE4;
86 }
87
88 return true;
89}
90
91static void load_float_pixels(const ImBuf *ibuf, const ImageMetaData &metadata, float *out_pixels)
92{
93 const size_t num_pixels = ((size_t)metadata.width) * metadata.height;
94 const int out_channels = metadata.channels;
95 const int in_channels = ibuf->channels;
96 const float *in_pixels = ibuf->float_buffer.data;
97
98 if (in_pixels && out_channels == in_channels) {
99 /* Straight copy pixel data. */
100 memcpy(out_pixels, in_pixels, num_pixels * out_channels * sizeof(float));
101 }
102 else if (in_pixels && out_channels == 4) {
103 /* Fill channels to 4. */
104 float *out_pixel = out_pixels;
105 const float *in_pixel = in_pixels;
106 for (size_t i = 0; i < num_pixels; i++) {
107 out_pixel[0] = in_pixel[0];
108 out_pixel[1] = (in_channels >= 2) ? in_pixel[1] : 0.0f;
109 out_pixel[2] = (in_channels >= 3) ? in_pixel[2] : 0.0f;
110 out_pixel[3] = (in_channels >= 4) ? in_pixel[3] : 1.0f;
111 out_pixel += out_channels;
112 in_pixel += in_channels;
113 }
114 }
115 else {
116 /* Missing or invalid pixel data. */
117 if (out_channels == 1) {
118 std::fill(out_pixels, out_pixels + num_pixels, 0.0f);
119 }
120 else {
121 std::fill((float4 *)out_pixels,
122 (float4 *)out_pixels + num_pixels,
123 make_float4(1.0f, 0.0f, 1.0f, 1.0f));
124 }
125 }
126}
127
128static void load_half_pixels(const ImBuf *ibuf,
129 const ImageMetaData &metadata,
130 half *out_pixels,
131 const bool associate_alpha)
132{
133 /* Half float. Blender does not have a half type, but in some cases
134 * we up-sample byte to half to avoid precision loss for colorspace
135 * conversion. */
136 const size_t num_pixels = ((size_t)metadata.width) * metadata.height;
137 const int out_channels = metadata.channels;
138 const int in_channels = 4;
139 const uchar *in_pixels = ibuf->byte_buffer.data;
140
141 if (in_pixels) {
142 /* Convert uchar to half. */
143 const uchar *in_pixel = in_pixels;
144 half *out_pixel = out_pixels;
145 if (associate_alpha && out_channels == in_channels) {
146 for (size_t i = 0; i < num_pixels; i++, in_pixel += in_channels, out_pixel += out_channels) {
147 const float alpha = util_image_cast_to_float(in_pixel[3]);
148 out_pixel[0] = float_to_half_image(util_image_cast_to_float(in_pixel[0]) * alpha);
149 out_pixel[1] = float_to_half_image(util_image_cast_to_float(in_pixel[1]) * alpha);
150 out_pixel[2] = float_to_half_image(util_image_cast_to_float(in_pixel[2]) * alpha);
151 out_pixel[3] = float_to_half_image(alpha);
152 }
153 }
154 else {
155 for (size_t i = 0; i < num_pixels; i++) {
156 for (int c = 0; c < out_channels; c++, in_pixel++, out_pixel++) {
157 *out_pixel = float_to_half_image(util_image_cast_to_float(*in_pixel));
158 }
159 }
160 }
161 }
162 else {
163 /* Missing or invalid pixel data. */
164 if (out_channels == 1) {
165 std::fill(out_pixels, out_pixels + num_pixels, float_to_half_image(0.0f));
166 }
167 else {
168 std::fill((half4 *)out_pixels,
169 (half4 *)out_pixels + num_pixels,
170 float4_to_half4_display(make_float4(1.0f, 0.0f, 1.0f, 1.0f)));
171 }
172 }
173}
174
175static void load_byte_pixels(const ImBuf *ibuf,
176 const ImageMetaData &metadata,
177 uchar *out_pixels,
178 const bool associate_alpha)
179{
180 const size_t num_pixels = ((size_t)metadata.width) * metadata.height;
181 const int out_channels = metadata.channels;
182 const int in_channels = 4;
183 const uchar *in_pixels = ibuf->byte_buffer.data;
184
185 if (in_pixels) {
186 /* Straight copy pixel data. */
187 memcpy(out_pixels, in_pixels, num_pixels * in_channels * sizeof(unsigned char));
188
189 if (associate_alpha && out_channels == in_channels) {
190 /* Premultiply, byte images are always straight for Blender. */
191 unsigned char *out_pixel = (unsigned char *)out_pixels;
192 for (size_t i = 0; i < num_pixels; i++, out_pixel += 4) {
193 out_pixel[0] = (out_pixel[0] * out_pixel[3]) / 255;
194 out_pixel[1] = (out_pixel[1] * out_pixel[3]) / 255;
195 out_pixel[2] = (out_pixel[2] * out_pixel[3]) / 255;
196 }
197 }
198 }
199 else {
200 /* Missing or invalid pixel data. */
201 if (out_channels == 1) {
202 std::fill(out_pixels, out_pixels + num_pixels, 0.0f);
203 }
204 else {
205 std::fill(
206 (uchar4 *)out_pixels, (uchar4 *)out_pixels + num_pixels, make_uchar4(255, 0, 255, 255));
207 }
208 }
209}
210
212 void *out_pixels,
213 const size_t /*out_pixels_size*/,
214 const bool associate_alpha)
215{
216 void *lock;
218
219 /* Image changed since we requested metadata, assume we'll get a signal to reload it later. */
220 const bool mismatch = (ibuf == nullptr || ibuf->x != metadata.width ||
221 ibuf->y != metadata.height);
222
223 if (!mismatch) {
224 if (metadata.type == IMAGE_DATA_TYPE_FLOAT || metadata.type == IMAGE_DATA_TYPE_FLOAT4) {
225 load_float_pixels(ibuf, metadata, (float *)out_pixels);
226 }
227 else if (metadata.type == IMAGE_DATA_TYPE_HALF || metadata.type == IMAGE_DATA_TYPE_HALF4) {
228 load_half_pixels(ibuf, metadata, (half *)out_pixels, associate_alpha);
229 }
230 else {
231 load_byte_pixels(ibuf, metadata, (uchar *)out_pixels, associate_alpha);
232 }
233 }
234
236
237 /* Free image buffers to save memory during render. */
238 if (free_cache) {
240 }
241
242 return !mismatch;
243}
244
246{
247 return b_image->id.name + 2;
248}
249
251{
252 const BlenderImageLoader &other_loader = (const BlenderImageLoader &)other;
253 return b_image == other_loader.b_image && b_iuser.framenr == other_loader.b_iuser.framenr &&
254 b_iuser.tile == other_loader.b_iuser.tile;
255}
256
258{
259 return b_iuser.tile;
260}
261
263{
264 /* Force builtin images to be loaded along with Blender data sync. This
265 * is needed because we may be reading from depsgraph evaluated data which
266 * can be freed by Blender before Cycles reads it.
267 *
268 * TODO: the assumption that no further access to builtin image data will
269 * happen is really weak, and likely to break in the future. We should find
270 * a better solution to hand over the data directly to the image manager
271 * instead of through callbacks whose timing is difficult to control. */
272 ImageManager *manager = session->scene->image_manager.get();
273 Device *device = session->device.get();
274 manager->device_load_builtin(device, session->scene.get(), session->progress);
275}
276
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
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 uchar4 make_uchar4(const uchar x, const uchar y, const uchar z, const uchar w)
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
#define make_float4
ustring u_colorspace_raw
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
Definition half.h:60
i
Definition text_draw.cc:230
@ IMAGE_DATA_TYPE_FLOAT
Definition texture.h:36
@ IMAGE_DATA_TYPE_FLOAT4
Definition texture.h:33
@ IMAGE_DATA_TYPE_HALF
Definition texture.h:38
@ IMAGE_DATA_TYPE_BYTE4
Definition texture.h:34
@ IMAGE_DATA_TYPE_HALF4
Definition texture.h:35