Blender V4.3
node_geo_image_texture.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "BKE_image.hh"
8
10#include "BLI_threads.h"
11
13#include "IMB_imbuf.hh"
14#include "IMB_imbuf_types.hh"
15
16#include "UI_interface.hh"
17#include "UI_resources.hh"
18
20
22
24{
25 b.add_input<decl::Image>("Image").hide_label();
26 b.add_input<decl::Vector>("Vector")
27 .implicit_field(implicit_field_inputs::position)
28 .description("Texture coordinates from 0 to 1");
29 b.add_input<decl::Int>("Frame").min(0).max(MAXFRAMEF);
30 b.add_output<decl::Color>("Color").no_muted_links().dependent_field().reference_pass_all();
31 b.add_output<decl::Float>("Alpha").no_muted_links().dependent_field().reference_pass_all();
32}
33
34static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
35{
36 uiItemR(layout, ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
37 uiItemR(layout, ptr, "extension", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
38}
39
40static void node_init(bNodeTree * /*tree*/, bNode *node)
41{
42 NodeGeometryImageTexture *tex = MEM_cnew<NodeGeometryImageTexture>(__func__);
43 tex->interpolation = SHD_INTERP_LINEAR;
45 node->storage = tex;
46}
47
49 private:
50 const int8_t interpolation_;
51 const int8_t extension_;
52 Image &image_;
53 ImageUser image_user_;
54 void *image_lock_;
55 ImBuf *image_buffer_;
56
57 public:
58 ImageFieldsFunction(const int8_t interpolation,
59 const int8_t extension,
60 Image &image,
61 ImageUser image_user)
62 : interpolation_(interpolation),
63 extension_(extension),
64 image_(image),
65 image_user_(image_user)
66 {
67 static const mf::Signature signature = []() {
69 mf::SignatureBuilder builder{"ImageFunction", signature};
70 builder.single_input<float3>("Vector");
71 builder.single_output<ColorGeometry4f>("Color");
72 builder.single_output<float>("Alpha", mf::ParamFlag::SupportsUnusedOutput);
73 return signature;
74 }();
75 this->set_signature(&signature);
76
77 image_buffer_ = BKE_image_acquire_ibuf(&image_, &image_user_, &image_lock_);
78 if (image_buffer_ == nullptr) {
79 throw std::runtime_error("cannot acquire image buffer");
80 }
81
82 if (image_buffer_->float_buffer.data == nullptr) {
84 if (!image_buffer_->float_buffer.data) {
85 IMB_float_from_rect(image_buffer_);
86 }
88 }
89
90 if (image_buffer_->float_buffer.data == nullptr) {
91 BKE_image_release_ibuf(&image_, image_buffer_, image_lock_);
92 throw std::runtime_error("cannot get float buffer");
93 }
94 }
95
97 {
98 BKE_image_release_ibuf(&image_, image_buffer_, image_lock_);
99 }
100
101 static int wrap_periodic(int x, const int width)
102 {
103 x %= width;
104 if (x < 0) {
105 x += width;
106 }
107 return x;
108 }
109
110 static int wrap_clamp(const int x, const int width)
111 {
112 return std::clamp(x, 0, width - 1);
113 }
114
115 static int wrap_mirror(const int x, const int width)
116 {
117 const int m = std::abs(x + (x < 0)) % (2 * width);
118 if (m >= width) {
119 return 2 * width - m - 1;
120 }
121 return m;
122 }
123
124 static float4 image_pixel_lookup(const ImBuf &ibuf, const int px, const int py)
125 {
126 if (px < 0 || py < 0 || px >= ibuf.x || py >= ibuf.y) {
127 return float4(0.0f, 0.0f, 0.0f, 0.0f);
128 }
129 return ((const float4 *)ibuf.float_buffer.data)[px + py * ibuf.x];
130 }
131
132 static float frac(const float x, int *ix)
133 {
134 const int i = int(x) - ((x < 0.0f) ? 1 : 0);
135 *ix = i;
136 return x - float(i);
137 }
138
140 const float px,
141 const float py,
142 const int extension)
143 {
144 const int width = ibuf.x;
145 const int height = ibuf.y;
146 int pix, piy, nix, niy;
147 const float tx = frac(px * float(width) - 0.5f, &pix);
148 const float ty = frac(py * float(height) - 0.5f, &piy);
149 int ppix, ppiy, nnix, nniy;
150
151 switch (extension) {
153 pix = wrap_periodic(pix, width);
154 piy = wrap_periodic(piy, height);
155 ppix = wrap_periodic(pix - 1, width);
156 ppiy = wrap_periodic(piy - 1, height);
157 nix = wrap_periodic(pix + 1, width);
158 niy = wrap_periodic(piy + 1, height);
159 nnix = wrap_periodic(pix + 2, width);
160 nniy = wrap_periodic(piy + 2, height);
161 break;
162 }
164 ppix = pix - 1;
165 ppiy = piy - 1;
166 nix = pix + 1;
167 niy = piy + 1;
168 nnix = pix + 2;
169 nniy = piy + 2;
170 break;
171 }
173 ppix = wrap_clamp(pix - 1, width);
174 ppiy = wrap_clamp(piy - 1, height);
175 nix = wrap_clamp(pix + 1, width);
176 niy = wrap_clamp(piy + 1, height);
177 nnix = wrap_clamp(pix + 2, width);
178 nniy = wrap_clamp(piy + 2, height);
179 pix = wrap_clamp(pix, width);
180 piy = wrap_clamp(piy, height);
181 break;
182 }
184 ppix = wrap_mirror(pix - 1, width);
185 ppiy = wrap_mirror(piy - 1, height);
186 nix = wrap_mirror(pix + 1, width);
187 niy = wrap_mirror(piy + 1, height);
188 nnix = wrap_mirror(pix + 2, width);
189 nniy = wrap_mirror(piy + 2, height);
190 pix = wrap_mirror(pix, width);
191 piy = wrap_mirror(piy, height);
192 break;
193 }
194 default:
195 return float4(0.0f, 0.0f, 0.0f, 0.0f);
196 }
197
198 const int xc[4] = {ppix, pix, nix, nnix};
199 const int yc[4] = {ppiy, piy, niy, nniy};
200 float u[4], v[4];
201
202 u[0] = (((-1.0f / 6.0f) * tx + 0.5f) * tx - 0.5f) * tx + (1.0f / 6.0f);
203 u[1] = ((0.5f * tx - 1.0f) * tx) * tx + (2.0f / 3.0f);
204 u[2] = ((-0.5f * tx + 0.5f) * tx + 0.5f) * tx + (1.0f / 6.0f);
205 u[3] = (1.0f / 6.0f) * tx * tx * tx;
206
207 v[0] = (((-1.0f / 6.0f) * ty + 0.5f) * ty - 0.5f) * ty + (1.0f / 6.0f);
208 v[1] = ((0.5f * ty - 1.0f) * ty) * ty + (2.0f / 3.0f);
209 v[2] = ((-0.5f * ty + 0.5f) * ty + 0.5f) * ty + (1.0f / 6.0f);
210 v[3] = (1.0f / 6.0f) * ty * ty * ty;
211
212 return (v[0] * (u[0] * image_pixel_lookup(ibuf, xc[0], yc[0]) +
213 u[1] * image_pixel_lookup(ibuf, xc[1], yc[0]) +
214 u[2] * image_pixel_lookup(ibuf, xc[2], yc[0]) +
215 u[3] * image_pixel_lookup(ibuf, xc[3], yc[0]))) +
216 (v[1] * (u[0] * image_pixel_lookup(ibuf, xc[0], yc[1]) +
217 u[1] * image_pixel_lookup(ibuf, xc[1], yc[1]) +
218 u[2] * image_pixel_lookup(ibuf, xc[2], yc[1]) +
219 u[3] * image_pixel_lookup(ibuf, xc[3], yc[1]))) +
220 (v[2] * (u[0] * image_pixel_lookup(ibuf, xc[0], yc[2]) +
221 u[1] * image_pixel_lookup(ibuf, xc[1], yc[2]) +
222 u[2] * image_pixel_lookup(ibuf, xc[2], yc[2]) +
223 u[3] * image_pixel_lookup(ibuf, xc[3], yc[2]))) +
224 (v[3] * (u[0] * image_pixel_lookup(ibuf, xc[0], yc[3]) +
225 u[1] * image_pixel_lookup(ibuf, xc[1], yc[3]) +
226 u[2] * image_pixel_lookup(ibuf, xc[2], yc[3]) +
227 u[3] * image_pixel_lookup(ibuf, xc[3], yc[3])));
228 }
229
231 const float px,
232 const float py,
233 const int8_t extension)
234 {
235 const int width = ibuf.x;
236 const int height = ibuf.y;
237 int pix, piy, nix, niy;
238 const float nfx = frac(px * float(width) - 0.5f, &pix);
239 const float nfy = frac(py * float(height) - 0.5f, &piy);
240
241 switch (extension) {
243 nix = pix + 1;
244 niy = piy + 1;
245 break;
246 }
248 nix = wrap_clamp(pix + 1, width);
249 niy = wrap_clamp(piy + 1, height);
250 pix = wrap_clamp(pix, width);
251 piy = wrap_clamp(piy, height);
252 break;
253 }
255 nix = wrap_mirror(pix + 1, width);
256 niy = wrap_mirror(piy + 1, height);
257 pix = wrap_mirror(pix, width);
258 piy = wrap_mirror(piy, height);
259 break;
260 default:
262 pix = wrap_periodic(pix, width);
263 piy = wrap_periodic(piy, height);
264 nix = wrap_periodic(pix + 1, width);
265 niy = wrap_periodic(piy + 1, height);
266 break;
267 }
268
269 const float ptx = 1.0f - nfx;
270 const float pty = 1.0f - nfy;
271
272 return image_pixel_lookup(ibuf, pix, piy) * ptx * pty +
273 image_pixel_lookup(ibuf, nix, piy) * nfx * pty +
274 image_pixel_lookup(ibuf, pix, niy) * ptx * nfy +
275 image_pixel_lookup(ibuf, nix, niy) * nfx * nfy;
276 }
277
279 const float px,
280 const float py,
281 const int extension)
282 {
283 const int width = ibuf.x;
284 const int height = ibuf.y;
285 int ix, iy;
286 const float tx = frac(px * float(width), &ix);
287 const float ty = frac(py * float(height), &iy);
288
289 switch (extension) {
291 ix = wrap_periodic(ix, width);
292 iy = wrap_periodic(iy, height);
293 return image_pixel_lookup(ibuf, ix, iy);
294 }
296 if (tx < 0.0f || ty < 0.0f || tx > 1.0f || ty > 1.0f) {
297 return float4(0.0f, 0.0f, 0.0f, 0.0f);
298 }
299 if (ix < 0 || iy < 0 || ix > width || iy > height) {
300 return float4(0.0f, 0.0f, 0.0f, 0.0f);
301 }
303 }
305 ix = wrap_clamp(ix, width);
306 iy = wrap_clamp(iy, height);
307 return image_pixel_lookup(ibuf, ix, iy);
308 }
310 ix = wrap_mirror(ix, width);
311 iy = wrap_mirror(iy, height);
312 return image_pixel_lookup(ibuf, ix, iy);
313 }
314 default:
315 return float4(0.0f, 0.0f, 0.0f, 0.0f);
316 }
317 }
318
319 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
320 {
321 const VArray<float3> &vectors = params.readonly_single_input<float3>(0, "Vector");
322 MutableSpan<ColorGeometry4f> r_color = params.uninitialized_single_output<ColorGeometry4f>(
323 1, "Color");
324 MutableSpan<float> r_alpha = params.uninitialized_single_output_if_required<float>(2, "Alpha");
325
326 MutableSpan<float4> color_data{reinterpret_cast<float4 *>(r_color.data()), r_color.size()};
327
328 /* Sample image texture. */
329 switch (interpolation_) {
331 mask.foreach_index([&](const int64_t i) {
332 const float3 p = vectors[i];
333 color_data[i] = image_linear_texture_lookup(*image_buffer_, p.x, p.y, extension_);
334 });
335 break;
337 mask.foreach_index([&](const int64_t i) {
338 const float3 p = vectors[i];
339 color_data[i] = image_closest_texture_lookup(*image_buffer_, p.x, p.y, extension_);
340 });
341 break;
342 case SHD_INTERP_CUBIC:
343 case SHD_INTERP_SMART:
344 mask.foreach_index([&](const int64_t i) {
345 const float3 p = vectors[i];
346 color_data[i] = image_cubic_texture_lookup(*image_buffer_, p.x, p.y, extension_);
347 });
348 break;
349 }
350
351 int alpha_mode = image_.alpha_mode;
353 alpha_mode = IMA_ALPHA_CHANNEL_PACKED;
354 }
355
356 switch (alpha_mode) {
357 case IMA_ALPHA_STRAIGHT: {
358 /* #ColorGeometry expects premultiplied alpha, so convert from straight to that. */
359 mask.foreach_index([&](const int64_t i) { straight_to_premul_v4(color_data[i]); });
360 break;
361 }
362 case IMA_ALPHA_PREMUL: {
363 /* Alpha is premultiplied already, nothing to do. */
364 break;
365 }
367 /* Color and alpha channels shouldn't interact with each other, nothing to do. */
368 break;
369 }
370 case IMA_ALPHA_IGNORE: {
371 /* The image should be treated as being opaque. */
372 mask.foreach_index([&](const int64_t i) { color_data[i].w = 1.0f; });
373 break;
374 }
375 }
376
377 if (!r_alpha.is_empty()) {
378 mask.foreach_index([&](const int64_t i) { r_alpha[i] = r_color[i].a; });
379 }
380 }
381};
382
384{
385 Image *image = params.get_input<Image *>("Image");
386 if (image == nullptr) {
387 params.set_default_remaining_outputs();
388 return;
389 }
390
391 const NodeGeometryImageTexture &storage = node_storage(params.node());
392
393 ImageUser image_user;
394 BKE_imageuser_default(&image_user);
395 image_user.cycl = false;
396 image_user.frames = INT_MAX;
397 image_user.sfra = 1;
398 image_user.framenr = BKE_image_is_animated(image) ? params.get_input<int>("Frame") : 0;
399
400 std::unique_ptr<ImageFieldsFunction> image_fn;
401 try {
402 image_fn = std::make_unique<ImageFieldsFunction>(
403 storage.interpolation, storage.extension, *image, image_user);
404 }
405 catch (const std::runtime_error &) {
406 params.set_default_remaining_outputs();
407 return;
408 }
409
410 Field<float3> vector_field = params.extract_input<Field<float3>>("Vector");
411
412 auto image_op = FieldOperation::Create(std::move(image_fn), {std::move(vector_field)});
413
414 params.set_output("Color", Field<ColorGeometry4f>(image_op, 0));
415 params.set_output("Alpha", Field<float>(image_op, 1));
416}
417
418static void node_register()
419{
420 static blender::bke::bNodeType ntype;
421
422 geo_node_type_base(&ntype, GEO_NODE_IMAGE_TEXTURE, "Image Texture", NODE_CLASS_TEXTURE);
423 ntype.declare = node_declare;
425 ntype.initfunc = node_init;
427 &ntype, "NodeGeometryImageTexture", node_free_standard_storage, node_copy_standard_storage);
430
432}
434
435} // namespace blender::nodes::node_geo_image_texture_cc
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_imageuser_default(ImageUser *iuser)
bool BKE_image_is_animated(Image *image)
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define NODE_CLASS_TEXTURE
Definition BKE_node.hh:414
#define ATTR_FALLTHROUGH
MINLINE void straight_to_premul_v4(float color[4])
void BLI_thread_unlock(int type)
Definition threads.cc:333
void BLI_thread_lock(int type)
Definition threads.cc:328
@ LOCK_IMAGE
Definition BLI_threads.h:67
@ IMA_ALPHA_IGNORE
@ IMA_ALPHA_STRAIGHT
@ IMA_ALPHA_PREMUL
@ IMA_ALPHA_CHANNEL_PACKED
@ SHD_INTERP_LINEAR
@ SHD_INTERP_SMART
@ SHD_INTERP_CUBIC
@ SHD_INTERP_CLOSEST
@ SHD_IMAGE_EXTENSION_MIRROR
@ SHD_IMAGE_EXTENSION_CLIP
@ SHD_IMAGE_EXTENSION_REPEAT
@ SHD_IMAGE_EXTENSION_EXTEND
#define MAXFRAMEF
bool IMB_colormanagement_space_name_is_data(const char *name)
void IMB_float_from_rect(ImBuf *ibuf)
Definition divers.cc:802
Contains defines and structs used throughout the imbuf module.
#define NOD_REGISTER_NODE(REGISTER_FUNC)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_SPLIT_EMPTY_NAME
ATTR_WARN_UNUSED_RESULT const BMVert * v
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:244
void set_signature(const Signature *signature)
static float4 image_cubic_texture_lookup(const ImBuf &ibuf, const float px, const float py, const int extension)
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
static float4 image_closest_texture_lookup(const ImBuf &ibuf, const float px, const float py, const int extension)
static float4 image_pixel_lookup(const ImBuf &ibuf, const int px, const int py)
ImageFieldsFunction(const int8_t interpolation, const int8_t extension, Image &image, ImageUser image_user)
static float4 image_linear_texture_lookup(const ImBuf &ibuf, const float px, const float py, const int8_t extension)
local_group_size(16, 16) .push_constant(Type b
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void node_type_size_preset(bNodeType *ntype, eNodeSizePreset size)
Definition node.cc:4614
void node_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:4632
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
void position(const bNode &, void *r_value)
static void node_geo_exec(GeoNodeExecParams params)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_declare(NodeDeclarationBuilder &b)
static void node_init(bNodeTree *, bNode *node)
VecBase< float, 4 > float4
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:46
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:58
#define min(a, b)
Definition sort.c:32
__int64 int64_t
Definition stdint.h:89
signed char int8_t
Definition stdint.h:75
ImBufFloatBuffer float_buffer
ColorManagedColorspaceSettings colorspace_settings
char alpha_mode
Defines a node type.
Definition BKE_node.hh:218
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeDeclareFunction declare
Definition BKE_node.hh:347
PointerRNA * ptr
Definition wm_files.cc:4126