Blender V4.3
COM_MemoryBuffer.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "COM_MemoryBuffer.h"
6
8#include "IMB_imbuf_types.hh"
9
10#define ASSERT_BUFFER_CONTAINS_AREA(buf, area) \
11 BLI_assert(BLI_rcti_inside_rcti(&(buf)->get_rect(), &(area)))
12
13#define ASSERT_BUFFER_CONTAINS_AREA_AT_COORDS(buf, area, x, y) \
14 BLI_assert((buf)->get_rect().xmin <= (x)); \
15 BLI_assert((buf)->get_rect().ymin <= (y)); \
16 BLI_assert((buf)->get_rect().xmax >= (x) + BLI_rcti_size_x(&(area))); \
17 BLI_assert((buf)->get_rect().ymax >= (y) + BLI_rcti_size_y(&(area)))
18
19#define ASSERT_VALID_ELEM_SIZE(buf, channel_offset, elem_size) \
20 BLI_assert((buf)->get_num_channels() >= (channel_offset) + (elem_size))
21
22namespace blender::compositor {
23
24static rcti create_rect(const int width, const int height)
25{
26 rcti rect;
27 BLI_rcti_init(&rect, 0, width, 0, height);
28 return rect;
29}
30
31MemoryBuffer::MemoryBuffer(DataType data_type, int width, int height)
32{
33 BLI_rcti_init(&rect_, 0, width, 0, height);
34 is_a_single_elem_ = false;
35 num_channels_ = COM_data_type_num_channels(data_type);
36 buffer_ = (float *)MEM_mallocN_aligned(
37 sizeof(float) * buffer_len() * num_channels_, 16, "COM_MemoryBuffer");
38 owns_data_ = true;
39 datatype_ = data_type;
40
41 set_strides();
42}
43
44MemoryBuffer::MemoryBuffer(DataType data_type, const rcti &rect, bool is_a_single_elem)
45{
46 rect_ = rect;
47 is_a_single_elem_ = is_a_single_elem;
48 num_channels_ = COM_data_type_num_channels(data_type);
49 buffer_ = (float *)MEM_mallocN_aligned(
50 sizeof(float) * buffer_len() * num_channels_, 16, "COM_MemoryBuffer");
51 owns_data_ = true;
52 datatype_ = data_type;
53
54 set_strides();
55}
56
58 float *buffer, int num_channels, int width, int height, bool is_a_single_elem)
59 : MemoryBuffer(buffer, num_channels, create_rect(width, height), is_a_single_elem)
60{
61}
62
64 const int num_channels,
65 const rcti &rect,
66 const bool is_a_single_elem)
67{
68 rect_ = rect;
69 is_a_single_elem_ = is_a_single_elem;
70 num_channels_ = num_channels;
71 datatype_ = COM_num_channels_data_type(num_channels);
72 buffer_ = buffer;
73 owns_data_ = false;
74
75 set_strides();
76}
77
78MemoryBuffer::MemoryBuffer(const MemoryBuffer &src) : MemoryBuffer(src.datatype_, src.rect_, false)
79{
80 /* src may be single elem buffer */
81 fill_from(src);
82}
83
84void MemoryBuffer::set_strides()
85{
86 if (is_a_single_elem_) {
87 this->elem_stride = 0;
88 this->row_stride = 0;
89 }
90 else {
91 this->elem_stride = num_channels_;
92 this->row_stride = get_width() * num_channels_;
93 }
94 to_positive_x_stride_ = rect_.xmin < 0 ? -rect_.xmin + 1 : (rect_.xmin == 0 ? 1 : 0);
95 to_positive_y_stride_ = rect_.ymin < 0 ? -rect_.ymin + 1 : (rect_.ymin == 0 ? 1 : 0);
96}
97
99{
100 memset(buffer_, 0, buffer_len() * num_channels_ * sizeof(float));
101}
102
107
109{
110 BuffersIteratorBuilder<float> builder(buffer_, rect_, area, elem_stride);
111 for (MemoryBuffer *input : inputs) {
112 builder.add_input(input->get_buffer(), input->get_rect(), input->elem_stride);
113 }
114 return builder.build();
115}
116
118{
120 MemoryBuffer *inflated = new MemoryBuffer(datatype_, rect_, false);
121 inflated->copy_from(this, rect_);
122 return inflated;
123}
124
126{
127 float result = buffer_[0];
128 const int64_t size = this->buffer_len();
129 int64_t i;
130
131 const float *fp_src = buffer_;
132
133 for (i = 0; i < size; i++, fp_src += num_channels_) {
134 float value = *fp_src;
135 if (value > result) {
136 result = value;
137 }
138 }
139
140 return result;
141}
142
143float MemoryBuffer::get_max_value(const rcti &rect) const
144{
145 rcti rect_clamp;
146
147 /* first clamp the rect by the bounds or we get un-initialized values */
148 BLI_rcti_isect(&rect, &rect_, &rect_clamp);
149
150 if (!BLI_rcti_is_empty(&rect_clamp)) {
151 MemoryBuffer temp_buffer(datatype_, rect_clamp);
152 temp_buffer.fill_from(*this);
153 return temp_buffer.get_max_value();
154 }
155
156 BLI_assert(0);
157 return 0.0f;
158}
159
161{
162 if (buffer_ && owns_data_) {
163 MEM_freeN(buffer_);
164 buffer_ = nullptr;
165 }
166}
167
168void MemoryBuffer::copy_from(const MemoryBuffer *src, const rcti &area)
169{
170 copy_from(src, area, area.xmin, area.ymin);
171}
172
174 const rcti &area,
175 const int to_x,
176 const int to_y)
177{
179 copy_from(src, area, 0, src->get_num_channels(), to_x, to_y, 0);
180}
181
183 const rcti &area,
184 const int channel_offset,
185 const int elem_size,
186 const int to_channel_offset)
187{
188 copy_from(src, area, channel_offset, elem_size, area.xmin, area.ymin, to_channel_offset);
189}
190
192 const rcti &area,
193 const int channel_offset,
194 const int elem_size,
195 const int to_x,
196 const int to_y,
197 const int to_channel_offset)
198{
199 if (this->is_a_single_elem()) {
200 copy_single_elem_from(src, channel_offset, elem_size, to_channel_offset);
201 }
202 else if (!src->is_a_single_elem() && elem_size == src->get_num_channels() &&
203 elem_size == this->get_num_channels())
204 {
205 BLI_assert(to_channel_offset == 0);
206 BLI_assert(channel_offset == 0);
207 copy_rows_from(src, area, to_x, to_y);
208 }
209 else {
210 copy_elems_from(src, area, channel_offset, elem_size, to_x, to_y, to_channel_offset);
211 }
212}
213
214void MemoryBuffer::copy_from(const uchar *src, const rcti &area)
215{
216 const int elem_stride = this->get_num_channels();
217 const int row_stride = elem_stride * get_width();
218 copy_from(src, area, 0, this->get_num_channels(), elem_stride, row_stride, 0);
219}
220
222 const rcti &area,
223 const int channel_offset,
224 const int elem_size,
225 const int elem_stride,
226 const int row_stride,
227 const int to_channel_offset)
228{
229 copy_from(src,
230 area,
231 channel_offset,
232 elem_size,
235 area.xmin,
236 area.ymin,
237 to_channel_offset);
238}
239
241 const rcti &area,
242 const int channel_offset,
243 const int elem_size,
244 const int elem_stride,
245 const int row_stride,
246 const int to_x,
247 const int to_y,
248 const int to_channel_offset)
249{
250 ASSERT_BUFFER_CONTAINS_AREA_AT_COORDS(this, area, to_x, to_y);
251 ASSERT_VALID_ELEM_SIZE(this, to_channel_offset, elem_size);
252
253 const int width = BLI_rcti_size_x(&area);
254 const int height = BLI_rcti_size_y(&area);
255 const uchar *const src_start = src + area.ymin * row_stride + channel_offset;
256 for (int y = 0; y < height; y++) {
257 const uchar *from_elem = src_start + y * row_stride + area.xmin * elem_stride;
258 float *to_elem = &this->get_value(to_x, to_y + y, to_channel_offset);
259 const float *row_end = to_elem + width * this->elem_stride;
260 while (to_elem < row_end) {
261 for (int i = 0; i < elem_size; i++) {
262 to_elem[i] = float(from_elem[i]) * (1.0f / 255.0f);
263 }
264 to_elem += this->elem_stride;
265 from_elem += elem_stride;
266 }
267 }
268}
269
271{
272 const int width = BLI_rcti_size_x(&area);
273 const int height = BLI_rcti_size_y(&area);
274 float *out = get_elem(area.xmin, area.ymin);
275 /* If area allows continuous memory do conversion in one step. Otherwise per row. */
276 if (get_width() == width) {
277 IMB_colormanagement_processor_apply(&processor, out, width, height, get_num_channels(), false);
278 }
279 else {
280 for (int y = 0; y < height; y++) {
281 IMB_colormanagement_processor_apply(&processor, out, width, 1, get_num_channels(), false);
282 out += row_stride;
283 }
284 }
285}
286
287static void colorspace_to_scene_linear(MemoryBuffer *buf, const rcti &area, ColorSpace *colorspace)
288{
289 const int width = BLI_rcti_size_x(&area);
290 const int height = BLI_rcti_size_y(&area);
291 float *out = buf->get_elem(area.xmin, area.ymin);
292 /* If area allows continuous memory do conversion in one step. Otherwise per row. */
293 if (buf->get_width() == width) {
295 out, width, height, buf->get_num_channels(), colorspace, false);
296 }
297 else {
298 for (int y = 0; y < height; y++) {
300 out, width, 1, buf->get_num_channels(), colorspace, false);
301 out += buf->row_stride;
302 }
303 }
304}
305
306static void premultiply_alpha(MemoryBuffer *buf, const rcti &area)
307{
308 for (int y = area.ymin; y < area.ymax; y++) {
309 for (int x = area.xmin; x < area.xmax; x++) {
311 }
312 }
313}
314
315void MemoryBuffer::copy_from(const ImBuf *src,
316 const rcti &area,
317 const bool ensure_premultiplied,
318 const bool ensure_linear_space)
319{
320 copy_from(src, area, 0, this->get_num_channels(), 0, ensure_premultiplied, ensure_linear_space);
321}
322
323void MemoryBuffer::copy_from(const ImBuf *src,
324 const rcti &area,
325 const int channel_offset,
326 const int elem_size,
327 const int to_channel_offset,
328 const bool ensure_premultiplied,
329 const bool ensure_linear_space)
330{
331 copy_from(src,
332 area,
333 channel_offset,
334 elem_size,
335 area.xmin,
336 area.ymin,
337 to_channel_offset,
338 ensure_premultiplied,
339 ensure_linear_space);
340}
341
342void MemoryBuffer::copy_from(const ImBuf *src,
343 const rcti &area,
344 const int channel_offset,
345 const int elem_size,
346 const int to_x,
347 const int to_y,
348 const int to_channel_offset,
349 const bool ensure_premultiplied,
350 const bool ensure_linear_space)
351{
352 if (src->float_buffer.data) {
353 const MemoryBuffer mem_buf(src->float_buffer.data, src->channels, src->x, src->y, false);
354 copy_from(&mem_buf, area, channel_offset, elem_size, to_x, to_y, to_channel_offset);
355 }
356 else if (src->byte_buffer.data) {
357 const uchar *uc_buf = src->byte_buffer.data;
358 const int elem_stride = src->channels;
359 const int row_stride = elem_stride * src->x;
360 copy_from(uc_buf,
361 area,
362 channel_offset,
363 elem_size,
366 to_x,
367 to_y,
368 to_channel_offset);
369 if (ensure_linear_space) {
371 }
372 if (ensure_premultiplied) {
373 premultiply_alpha(this, area);
374 }
375 }
376 else {
377 /* Empty ImBuf source. Fill destination with empty values. */
378 const float *zero_elem = new float[elem_size]{0};
379 fill(area, to_channel_offset, zero_elem, elem_size);
380 delete[] zero_elem;
381 }
382}
383
384void MemoryBuffer::fill(const rcti &area, const float *value)
385{
386 fill(area, 0, value, this->get_num_channels());
387}
388
389void MemoryBuffer::fill(const rcti &area,
390 const int channel_offset,
391 const float *value,
392 const int value_size)
393{
394 const MemoryBuffer single_elem(const_cast<float *>(value), value_size, this->get_rect(), true);
395 copy_from(&single_elem, area, 0, value_size, area.xmin, area.ymin, channel_offset);
396}
397
399{
400 rcti overlap;
401 overlap.xmin = std::max(rect_.xmin, src.rect_.xmin);
402 overlap.xmax = std::min(rect_.xmax, src.rect_.xmax);
403 overlap.ymin = std::max(rect_.ymin, src.rect_.ymin);
404 overlap.ymax = std::min(rect_.ymax, src.rect_.ymax);
405 copy_from(&src, overlap);
406}
407
408void MemoryBuffer::write_pixel(int x, int y, const float color[4])
409{
410 if (x >= rect_.xmin && x < rect_.xmax && y >= rect_.ymin && y < rect_.ymax) {
411 const intptr_t offset = get_coords_offset(x, y);
412 memcpy(&buffer_[offset], color, sizeof(float) * num_channels_);
413 }
414}
415
416void MemoryBuffer::add_pixel(int x, int y, const float color[4])
417{
418 if (x >= rect_.xmin && x < rect_.xmax && y >= rect_.ymin && y < rect_.ymax) {
419 const intptr_t offset = get_coords_offset(x, y);
420 float *dst = &buffer_[offset];
421 const float *src = color;
422 for (int i = 0; i < num_channels_; i++, dst++, src++) {
423 *dst += *src;
424 }
425 }
426}
427
428static void read_ewa_elem_checked(void *userdata, int x, int y, float result[4])
429{
430 const MemoryBuffer *buffer = static_cast<const MemoryBuffer *>(userdata);
431 buffer->read_elem_checked(x, y, result);
432}
433
434static void read_ewa_elem_clamped(void *userdata, int x, int y, float result[4])
435{
436 const MemoryBuffer *buffer = static_cast<const MemoryBuffer *>(userdata);
437 buffer->read_elem_clamped(x, y, result);
438}
439
441 const float x, const float y, float dx[2], float dy[2], bool extend_boundary, float *out) const
442{
443 BLI_assert(datatype_ == DataType::Color);
444
445 const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}};
446
447 float inv_width = 1.0f / float(this->get_width()), inv_height = 1.0f / float(this->get_height());
448 /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
449 * but compositor uses pixel space. For now let's just divide the values and
450 * switch compositor to normalized space for EWA later.
451 */
452 float uv_normal[2] = {get_relative_x(x) * inv_width, get_relative_y(y) * inv_height};
453 float du_normal[2] = {deriv[0][0] * inv_width, deriv[0][1] * inv_height};
454 float dv_normal[2] = {deriv[1][0] * inv_width, deriv[1][1] * inv_height};
455
457 this->get_height(),
458 false,
459 true,
460 uv_normal,
461 du_normal,
462 dv_normal,
464 const_cast<MemoryBuffer *>(this),
465 out);
466}
467
468void MemoryBuffer::copy_single_elem_from(const MemoryBuffer *src,
469 const int channel_offset,
470 const int elem_size,
471 const int to_channel_offset)
472{
473 ASSERT_VALID_ELEM_SIZE(this, to_channel_offset, elem_size);
474 ASSERT_VALID_ELEM_SIZE(src, channel_offset, elem_size);
476
477 float *to_elem = &this->get_value(
478 this->get_rect().xmin, this->get_rect().ymin, to_channel_offset);
479 const float *from_elem = &src->get_value(
480 src->get_rect().xmin, src->get_rect().ymin, channel_offset);
481 const int elem_bytes = elem_size * sizeof(float);
482 memcpy(to_elem, from_elem, elem_bytes);
483}
484
485void MemoryBuffer::copy_rows_from(const MemoryBuffer *src,
486 const rcti &area,
487 const int to_x,
488 const int to_y)
489{
491 ASSERT_BUFFER_CONTAINS_AREA_AT_COORDS(this, area, to_x, to_y);
492 BLI_assert(this->get_num_channels() == src->get_num_channels());
494 BLI_assert(!src->is_a_single_elem());
495
496 const int width = BLI_rcti_size_x(&area);
497 const int height = BLI_rcti_size_y(&area);
498 const int row_bytes = this->get_num_channels() * width * sizeof(float);
499 for (int y = 0; y < height; y++) {
500 float *to_row = this->get_elem(to_x, to_y + y);
501 const float *from_row = src->get_elem(area.xmin, area.ymin + y);
502 memcpy(to_row, from_row, row_bytes);
503 }
504}
505
506void MemoryBuffer::copy_elems_from(const MemoryBuffer *src,
507 const rcti &area,
508 const int channel_offset,
509 const int elem_size,
510 const int to_x,
511 const int to_y,
512 const int to_channel_offset)
513{
515 ASSERT_BUFFER_CONTAINS_AREA_AT_COORDS(this, area, to_x, to_y);
516 ASSERT_VALID_ELEM_SIZE(this, to_channel_offset, elem_size);
517 ASSERT_VALID_ELEM_SIZE(src, channel_offset, elem_size);
518
519 const int width = BLI_rcti_size_x(&area);
520 const int height = BLI_rcti_size_y(&area);
521 const int elem_bytes = elem_size * sizeof(float);
522 for (int y = 0; y < height; y++) {
523 float *to_elem = &this->get_value(to_x, to_y + y, to_channel_offset);
524 const float *from_elem = &src->get_value(area.xmin, area.ymin + y, channel_offset);
525 const float *row_end = to_elem + width * this->elem_stride;
526 while (to_elem < row_end) {
527 memcpy(to_elem, from_elem, elem_bytes);
528 to_elem += this->elem_stride;
529 from_elem += src->elem_stride;
530 }
531 }
532}
533
534} // namespace blender::compositor
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE void straight_to_premul_v4(float color[4])
void BLI_ewa_filter(int width, int height, bool intpol, bool use_alpha, const float uv[2], const float du[2], const float dv[2], ewa_filter_read_pixel_cb read_pixel_cb, void *userdata, float result[4])
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:193
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.c:418
bool BLI_rcti_isect(const struct rcti *src1, const struct rcti *src2, struct rcti *dest)
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:189
bool BLI_rcti_is_empty(const struct rcti *rect)
unsigned char uchar
#define ASSERT_VALID_ELEM_SIZE(buf, channel_offset, elem_size)
#define ASSERT_BUFFER_CONTAINS_AREA_AT_COORDS(buf, area, x, y)
#define ASSERT_BUFFER_CONTAINS_AREA(buf, area)
void IMB_colormanagement_processor_apply(ColormanageProcessor *cm_processor, float *buffer, int width, int height, int channels, bool predivide)
void IMB_colormanagement_colorspace_to_scene_linear(float *buffer, int width, int height, int channels, ColorSpace *colorspace, bool predivide)
Contains defines and structs used throughout the imbuf module.
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
void add_input(const T *input, const rcti &buffer_area, int elem_stride=1)
BuffersIteratorBuilder::Iterator build()
a MemoryBuffer contains access to the data
const rcti & get_rect() const
get the rect of this MemoryBuffer
void read_elem_checked(int x, int y, float *out) const
float & get_value(int x, int y, int channel)
void copy_from(const MemoryBuffer *src, const rcti &area)
const int get_width() const
get the width of this MemoryBuffer
intptr_t get_coords_offset(int x, int y) const
const int get_height() const
get the height of this MemoryBuffer
void write_pixel(int x, int y, const float color[4])
void fill(const rcti &area, const float *value)
void fill_from(const MemoryBuffer &src)
add the content from other_buffer to this MemoryBuffer
MemoryBuffer(DataType data_type, int width, int height)
construct new temporarily MemoryBuffer for a width and height.
void read_elem_clamped(int x, int y, float *out) const
void read_elem_filtered(float x, float y, float dx[2], float dy[2], bool extend_boundary, float *out) const
void apply_processor(ColormanageProcessor &processor, const rcti area)
Apply a color processor on the given area.
void add_pixel(int x, int y, const float color[4])
BuffersIterator< float > iterate_with(Span< MemoryBuffer * > inputs)
void clear()
clear the buffer. Make all pixels black transparent.
draw_view in_light_buf[] float
DataType
possible data types for sockets
Definition COM_defines.h:21
void * MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
Definition mallocn.cc:110
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
static void premultiply_alpha(MemoryBuffer *buf, const rcti &area)
static rcti create_rect(const int width, const int height)
constexpr DataType COM_num_channels_data_type(const int num_channels)
Definition COM_defines.h:70
constexpr int COM_data_type_num_channels(const DataType datatype)
Definition COM_defines.h:35
static void read_ewa_elem_clamped(void *userdata, int x, int y, float result[4])
static void colorspace_to_scene_linear(MemoryBuffer *buf, const rcti &area, ColorSpace *colorspace)
static void read_ewa_elem_checked(void *userdata, int x, int y, float result[4])
typename BuffersIteratorBuilder< T >::Iterator BuffersIterator
static blender::bke::bNodeSocketTemplate inputs[]
__int64 int64_t
Definition stdint.h:89
_W64 int intptr_t
Definition stdint.h:118
ColorSpace * colorspace
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
int ymin
int ymax
int xmin
int xmax