Blender V5.0
allocimbuf.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9/* It's become a bit messy... Basically, only the IMB_ prefixed files
10 * should remain. */
11
12#include <algorithm>
13#include <cstddef>
14
15#include "IMB_imbuf.hh"
16#include "IMB_imbuf_types.hh"
17
18#include "IMB_allocimbuf.hh"
20#include "IMB_filetype.hh"
21#include "IMB_metadata.hh"
22
23#include "imbuf.hh"
24
25#include "MEM_guardedalloc.h"
26
27#include "BLI_threads.h"
28
29#include "GPU_texture.hh"
30
31#include "CLG_log.h"
32
33#include "atomic_ops.h"
34
35static CLG_LogRef LOG = {"image.buffer"};
36
37/* Free the specified buffer storage, freeing memory when needed and restoring the state of the
38 * buffer to its defaults. */
39template<class BufferType> static void imb_free_buffer(BufferType &buffer)
40{
41 if (buffer.data) {
42 switch (buffer.ownership) {
44 break;
45
47 MEM_freeN(buffer.data);
48 break;
49 }
50 }
51
52 /* Reset buffer to defaults. */
53 buffer.data = nullptr;
54 buffer.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
55}
56
57/* Free the specified DDS buffer storage, freeing memory when needed and restoring the state of the
58 * buffer to its defaults. */
59static void imb_free_dds_buffer(DDSData &dds_data)
60{
61 if (dds_data.data) {
62 switch (dds_data.ownership) {
64 break;
65
67 /* dds_data.data is allocated by DirectDrawSurface::readData(), so don't use MEM_freeN! */
68 free(dds_data.data);
69 break;
70 }
71 }
72
73 /* Reset buffer to defaults. */
74 dds_data.data = nullptr;
76}
77
78/* Allocate pixel storage of the given buffer. The buffer owns the allocated memory.
79 * Returns true of allocation succeeded, false otherwise. */
80template<class BufferType>
81bool imb_alloc_buffer(BufferType &buffer,
82 const uint x,
83 const uint y,
84 const uint channels,
85 const size_t type_size,
86 bool initialize_pixels)
87{
88 buffer.data = static_cast<decltype(BufferType::data)>(
89 imb_alloc_pixels(x, y, channels, type_size, initialize_pixels, __func__));
90 if (!buffer.data) {
91 return false;
92 }
93
94 buffer.ownership = IB_TAKE_OWNERSHIP;
95
96 return true;
97}
98
99/* Make the buffer available for modification.
100 * Is achieved by ensuring that the buffer is the only owner of its data. */
101template<class BufferType> void imb_make_writeable_buffer(BufferType &buffer)
102{
103 if (!buffer.data) {
104 return;
105 }
106
107 switch (buffer.ownership) {
109 buffer.data = static_cast<decltype(BufferType::data)>(MEM_dupallocN(buffer.data));
110 buffer.ownership = IB_TAKE_OWNERSHIP;
111
113 break;
114 }
115}
116
117template<class BufferType>
118auto imb_steal_buffer_data(BufferType &buffer) -> decltype(BufferType::data)
119{
120 if (!buffer.data) {
121 return nullptr;
122 }
123
124 switch (buffer.ownership) {
126 BLI_assert_msg(false, "Unexpected behavior: stealing non-owned data pointer");
127 return nullptr;
128
129 case IB_TAKE_OWNERSHIP: {
130 decltype(BufferType::data) data = buffer.data;
131
132 buffer.data = nullptr;
133 buffer.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
134
135 return data;
136 }
137 }
138
140
141 return nullptr;
142}
143
145{
146 if (ibuf == nullptr) {
147 return;
148 }
150 ibuf->flags &= ~IB_float_data;
151}
152
154{
155 if (ibuf == nullptr) {
156 return;
157 }
159 ibuf->flags &= ~IB_byte_data;
160}
161
162static void free_encoded_data(ImBuf *ibuf)
163{
164 if (ibuf == nullptr) {
165 return;
166 }
167
169
170 ibuf->encoded_buffer_size = 0;
171 ibuf->encoded_size = 0;
172
173 ibuf->flags &= ~IB_mem;
174}
175
177{
180 free_encoded_data(ibuf);
181}
182
184{
185 if (!ibuf || !ibuf->gpu.texture) {
186 return;
187 }
188
190 ibuf->gpu.texture = nullptr;
191}
192
194{
195 if (ibuf == nullptr) {
196 return;
197 }
198
199 bool needs_free = atomic_sub_and_fetch_int32(&ibuf->refcounter, 1) < 0;
200 if (needs_free) {
201 /* Include this check here as the path may be manipulated after creation. */
202 BLI_assert_msg(!(ibuf->filepath[0] == '/' && ibuf->filepath[1] == '/'),
203 "'.blend' relative \"//\" must not be used in ImBuf!");
204
205 IMB_free_all_data(ibuf);
210 MEM_freeN(ibuf);
211 }
212}
213
215{
217}
218
220{
221 if (ibuf == nullptr) {
222 return nullptr;
223 }
224
225 const bool is_single = (atomic_load_int32(&ibuf->refcounter) == 0);
226 if (is_single) {
227 return ibuf;
228 }
229
230 ImBuf *rval = IMB_dupImBuf(ibuf);
231
232 IMB_metadata_copy(rval, ibuf);
233
234 IMB_freeImBuf(ibuf);
235
236 return rval;
237}
238
240{
241 if (ibuf == nullptr) {
242 return false;
243 }
244
245 free_encoded_data(ibuf);
246
247 if (ibuf->encoded_buffer_size == 0) {
248 ibuf->encoded_buffer_size = 10000;
249 }
250
251 ibuf->encoded_size = 0;
252
253 if (!imb_alloc_buffer(
254 ibuf->encoded_buffer, ibuf->encoded_buffer_size, 1, 1, sizeof(uint8_t), true))
255 {
256 return false;
257 }
258
259 ibuf->flags |= IB_mem;
260
261 return true;
262}
263
265{
266 if (ibuf == nullptr) {
267 return false;
268 }
269
270 if (ibuf->encoded_buffer_size < ibuf->encoded_size) {
271 CLOG_ERROR(&LOG, "%s: error in parameters\n", __func__);
272 return false;
273 }
274
275 uint newsize = 2 * ibuf->encoded_buffer_size;
276 newsize = std::max<uint>(newsize, 10000);
277
278 ImBufByteBuffer new_buffer;
279 if (!imb_alloc_buffer(new_buffer, newsize, 1, 1, sizeof(uint8_t), true)) {
280 return false;
281 }
282
283 if (ibuf->encoded_buffer.data) {
284 memcpy(new_buffer.data, ibuf->encoded_buffer.data, ibuf->encoded_size);
285 }
286 else {
287 ibuf->encoded_size = 0;
288 }
289
291
292 ibuf->encoded_buffer = new_buffer;
293 ibuf->encoded_buffer_size = newsize;
294 ibuf->flags |= IB_mem;
295
296 return true;
297}
298
300 uint x, uint y, uint channels, size_t typesize, bool initialize_pixels, const char *alloc_name)
301{
302 /* Protect against buffer overflow vulnerabilities from files specifying
303 * a width and height that overflow and alloc too little memory. */
304 if (!(uint64_t(x) * uint64_t(y) < (SIZE_MAX / (channels * typesize)))) {
305 return nullptr;
306 }
307
308 size_t size = size_t(x) * size_t(y) * size_t(channels) * typesize;
309 return initialize_pixels ? MEM_callocN(size, alloc_name) : MEM_mallocN(size, alloc_name);
310}
311
312bool IMB_alloc_float_pixels(ImBuf *ibuf, const uint channels, bool initialize_pixels)
313{
314 if (ibuf == nullptr) {
315 return false;
316 }
317
318 if (ibuf->float_buffer.data) {
320 }
321
322 if (!imb_alloc_buffer(
323 ibuf->float_buffer, ibuf->x, ibuf->y, channels, sizeof(float), initialize_pixels))
324 {
325 return false;
326 }
327
328 ibuf->channels = channels;
329 ibuf->flags |= IB_float_data;
330
331 return true;
332}
333
334bool IMB_alloc_byte_pixels(ImBuf *ibuf, bool initialize_pixels)
335{
336 /* Question; why also add ZBUF (when `planes > 32`)? */
337
338 if (ibuf == nullptr) {
339 return false;
340 }
341
343
344 if (!imb_alloc_buffer(
345 ibuf->byte_buffer, ibuf->x, ibuf->y, 4, sizeof(uint8_t), initialize_pixels))
346 {
347 return false;
348 }
349
350 ibuf->flags |= IB_byte_data;
351
352 return true;
353}
354
356{
357 uint8_t *data = imb_steal_buffer_data(ibuf->byte_buffer);
358 ibuf->flags &= ~IB_byte_data;
359 return data;
360}
361
363{
365 ibuf->flags &= ~IB_float_data;
366 return data;
367}
368
370{
371 uint8_t *data = imb_steal_buffer_data(ibuf->encoded_buffer);
372
373 ibuf->encoded_size = 0;
374 ibuf->encoded_buffer_size = 0;
375
376 ibuf->flags &= ~IB_mem;
377
378 return data;
379}
380
385
390
391void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, const ImBufOwnership ownership)
392{
394 ibuf->flags &= ~IB_byte_data;
395
396 if (buffer_data) {
397 ibuf->byte_buffer.data = buffer_data;
398 ibuf->byte_buffer.ownership = ownership;
399
400 ibuf->flags |= IB_byte_data;
401 }
402}
403
404void IMB_assign_float_buffer(ImBuf *ibuf, float *buffer_data, const ImBufOwnership ownership)
405{
407 ibuf->flags &= ~IB_float_data;
408
409 if (buffer_data) {
410 ibuf->float_buffer.data = buffer_data;
411 ibuf->float_buffer.ownership = ownership;
412
413 ibuf->flags |= IB_float_data;
414 }
415}
416
418 const ImBufByteBuffer &buffer,
419 const ImBufOwnership ownership)
420{
421 IMB_assign_byte_buffer(ibuf, buffer.data, ownership);
422 ibuf->byte_buffer.colorspace = buffer.colorspace;
423}
424
426 const ImBufFloatBuffer &buffer,
427 const ImBufOwnership ownership)
428{
429 IMB_assign_float_buffer(ibuf, buffer.data, ownership);
430 ibuf->float_buffer.colorspace = buffer.colorspace;
431}
432
433void IMB_assign_dds_data(ImBuf *ibuf, const DDSData &data, const ImBufOwnership ownership)
434{
436
438
439 ibuf->dds_data = data;
440 ibuf->dds_data.ownership = ownership;
441}
442
444 uint8_t *byte_buffer, float *float_buffer, uint w, uint h, uint channels)
445{
446 if (!(byte_buffer || float_buffer)) {
447 return nullptr;
448 }
449
450 ImBuf *ibuf = IMB_allocImBuf(w, h, 32, 0);
451
452 ibuf->channels = channels;
453
454 if (float_buffer) {
455 /* TODO(sergey): The 4 channels is the historical code. Should probably be `channels`, but
456 * needs a dedicated investigation. */
457 BLI_assert(MEM_allocN_len(float_buffer) == sizeof(float[4]) * w * h);
458 IMB_assign_float_buffer(ibuf, float_buffer, IB_TAKE_OWNERSHIP);
459 }
460
461 if (byte_buffer) {
462 BLI_assert(MEM_allocN_len(byte_buffer) == sizeof(uint8_t[4]) * w * h);
463 IMB_assign_byte_buffer(ibuf, byte_buffer, IB_TAKE_OWNERSHIP);
464 }
465
466 return ibuf;
467}
468
470 const uint8_t *byte_buffer, const float *float_buffer, uint w, uint h, uint channels)
471{
472 ImBuf *ibuf = nullptr;
473
474 if (!(byte_buffer || float_buffer)) {
475 return nullptr;
476 }
477
478 ibuf = IMB_allocImBuf(w, h, 32, 0);
479
480 ibuf->channels = channels;
481
482 /* NOTE: Avoid #MEM_dupallocN since the buffers might not be allocated using guarded-allocation.
483 */
484 if (float_buffer) {
485 /* TODO(sergey): The 4 channels is the historical code. Should probably be `channels`, but
486 * needs a dedicated investigation. */
487 imb_alloc_buffer(ibuf->float_buffer, w, h, 4, sizeof(float), false);
488
489 memcpy(ibuf->float_buffer.data, float_buffer, sizeof(float[4]) * w * h);
490 }
491
492 if (byte_buffer) {
493 imb_alloc_buffer(ibuf->byte_buffer, w, h, 4, sizeof(uint8_t), false);
494
495 memcpy(ibuf->byte_buffer.data, byte_buffer, sizeof(uint8_t[4]) * w * h);
496 }
497
498 return ibuf;
499}
500
502{
503 ImBuf *ibuf = MEM_callocN<ImBuf>("ImBuf_struct");
504
505 if (ibuf) {
506 if (!IMB_initImBuf(ibuf, x, y, planes, flags)) {
507 IMB_freeImBuf(ibuf);
508 return nullptr;
509 }
510 }
511
512 return ibuf;
513}
514
515bool IMB_initImBuf(ImBuf *ibuf, uint x, uint y, uchar planes, uint flags)
516{
517 *ibuf = ImBuf{};
518
519 ibuf->x = x;
520 ibuf->y = y;
521 ibuf->planes = planes;
522 ibuf->ftype = IMB_FTYPE_PNG;
523 /* The '15' means, set compression to low ratio but not time consuming. */
524 ibuf->foptions.quality = 15;
525 /* float option, is set to other values when buffers get assigned. */
526 ibuf->channels = 4;
527 /* IMB_DPI_DEFAULT -> pixels-per-meter. */
528 ibuf->ppm[0] = ibuf->ppm[1] = IMB_DPI_DEFAULT / 0.0254;
529
530 const bool init_pixels = (flags & IB_uninitialized_pixels) == 0;
531
532 if (flags & IB_byte_data) {
533 if (IMB_alloc_byte_pixels(ibuf, init_pixels) == false) {
534 return false;
535 }
536 }
537
538 if (flags & IB_float_data) {
539 if (IMB_alloc_float_pixels(ibuf, ibuf->channels, init_pixels) == false) {
540 return false;
541 }
542 }
543
544 /* assign default spaces */
546
547 return true;
548}
549
551{
552 ImBuf *ibuf2, tbuf;
553 int flags = IB_uninitialized_pixels;
554 int x, y;
555
556 if (ibuf1 == nullptr) {
557 return nullptr;
558 }
559
560 if (ibuf1->byte_buffer.data) {
561 flags |= IB_byte_data;
562 }
563
564 x = ibuf1->x;
565 y = ibuf1->y;
566
567 ibuf2 = IMB_allocImBuf(x, y, ibuf1->planes, flags);
568 if (ibuf2 == nullptr) {
569 return nullptr;
570 }
571
572 if (flags & IB_byte_data) {
573 memcpy(ibuf2->byte_buffer.data, ibuf1->byte_buffer.data, size_t(x) * y * 4 * sizeof(uint8_t));
574 }
575
576 if (ibuf1->float_buffer.data) {
577 /* Ensure the correct number of channels are being allocated for the new #ImBuf. Some
578 * compositing scenarios might end up with >4 channels and we want to duplicate them properly.
579 */
580 if (IMB_alloc_float_pixels(ibuf2, ibuf1->channels, false) == false) {
581 IMB_freeImBuf(ibuf2);
582 return nullptr;
583 }
584
585 memcpy(ibuf2->float_buffer.data,
586 ibuf1->float_buffer.data,
587 size_t(ibuf2->channels) * x * y * sizeof(float));
588 }
589
590 if (ibuf1->encoded_buffer.data) {
592 if (imb_addencodedbufferImBuf(ibuf2) == false) {
593 IMB_freeImBuf(ibuf2);
594 return nullptr;
595 }
596
597 memcpy(ibuf2->encoded_buffer.data, ibuf1->encoded_buffer.data, ibuf1->encoded_size);
598 }
599
602
603 /* silly trick to copy the entire contents of ibuf1 struct over to ibuf */
604 tbuf = *ibuf1;
605
606 /* fix pointers */
607 tbuf.byte_buffer = ibuf2->byte_buffer;
608 tbuf.float_buffer = ibuf2->float_buffer;
609 tbuf.encoded_buffer = ibuf2->encoded_buffer;
610 tbuf.dds_data.data = nullptr;
611
612 /* Set `malloc` flag. */
613 tbuf.refcounter = 0;
614
615 /* for now don't duplicate metadata */
616 tbuf.metadata = nullptr;
617
618 tbuf.display_buffer_flags = nullptr;
619 tbuf.colormanage_cache = nullptr;
620
621 /* GPU textures can not be easily copied, as it is not guaranteed that this function is called
622 * from within an active GPU context. */
623 tbuf.gpu.texture = nullptr;
624
625 *ibuf2 = tbuf;
626
627 return ibuf2;
628}
629
630size_t IMB_get_pixel_count(const ImBuf *ibuf)
631{
632 return size_t(ibuf->x) * size_t(ibuf->y);
633}
634
635size_t IMB_get_size_in_memory(const ImBuf *ibuf)
636{
637 size_t size = 0, channel_size = 0;
638
639 size += sizeof(ImBuf);
640
641 if (ibuf->byte_buffer.data) {
642 channel_size += sizeof(char);
643 }
644
645 if (ibuf->float_buffer.data) {
646 channel_size += sizeof(float);
647 }
648
649 size += channel_size * IMB_get_pixel_count(ibuf) * size_t(ibuf->channels);
650
651 return size;
652}
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
void BLI_kdtree_nd_ free(KDTree *tree)
unsigned char uchar
unsigned int uint
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
void GPU_texture_free(blender::gpu::Texture *texture)
@ IMB_FTYPE_DDS
@ IMB_FTYPE_PNG
ImBufOwnership
@ IB_DO_NOT_TAKE_OWNERSHIP
@ IB_TAKE_OWNERSHIP
@ IB_float_data
@ IB_byte_data
@ IB_uninitialized_pixels
@ IB_mem
void IMB_metadata_free(IDProperty *metadata)
Definition metadata.cc:32
void IMB_metadata_copy(ImBuf *ibuf_dst, const ImBuf *ibuf_src)
Definition metadata.cc:59
Read Guarded memory(de)allocation.
void IMB_free_gpu_textures(ImBuf *ibuf)
bool IMB_alloc_byte_pixels(ImBuf *ibuf, bool initialize_pixels)
float * IMB_steal_float_buffer(ImBuf *ibuf)
ImBuf * IMB_dupImBuf(const ImBuf *ibuf1)
void IMB_make_writable_byte_buffer(ImBuf *ibuf)
uint8_t * IMB_steal_byte_buffer(ImBuf *ibuf)
bool IMB_alloc_float_pixels(ImBuf *ibuf, const uint channels, bool initialize_pixels)
bool IMB_initImBuf(ImBuf *ibuf, uint x, uint y, uchar planes, uint flags)
ImBuf * IMB_makeSingleUser(ImBuf *ibuf)
void * imb_alloc_pixels(uint x, uint y, uint channels, size_t typesize, bool initialize_pixels, const char *alloc_name)
static void imb_free_dds_buffer(DDSData &dds_data)
Definition allocimbuf.cc:59
void IMB_free_all_data(ImBuf *ibuf)
void IMB_make_writable_float_buffer(ImBuf *ibuf)
bool imb_alloc_buffer(BufferType &buffer, const uint x, const uint y, const uint channels, const size_t type_size, bool initialize_pixels)
Definition allocimbuf.cc:81
ImBuf * IMB_allocImBuf(uint x, uint y, uchar planes, uint flags)
ImBuf * IMB_allocFromBufferOwn(uint8_t *byte_buffer, float *float_buffer, uint w, uint h, uint channels)
bool imb_enlargeencodedbufferImBuf(ImBuf *ibuf)
static void free_encoded_data(ImBuf *ibuf)
void IMB_freeImBuf(ImBuf *ibuf)
size_t IMB_get_pixel_count(const ImBuf *ibuf)
Get the length of the data of the given image buffer in pixels.
void imb_make_writeable_buffer(BufferType &buffer)
void IMB_free_float_pixels(ImBuf *ibuf)
void IMB_refImBuf(ImBuf *ibuf)
void IMB_free_byte_pixels(ImBuf *ibuf)
uint8_t * IMB_steal_encoded_buffer(ImBuf *ibuf)
void IMB_assign_dds_data(ImBuf *ibuf, const DDSData &data, const ImBufOwnership ownership)
auto imb_steal_buffer_data(BufferType &buffer) -> decltype(BufferType::data)
void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, const ImBufOwnership ownership)
ImBuf * IMB_allocFromBuffer(const uint8_t *byte_buffer, const float *float_buffer, uint w, uint h, uint channels)
static void imb_free_buffer(BufferType &buffer)
Definition allocimbuf.cc:39
bool imb_addencodedbufferImBuf(ImBuf *ibuf)
void IMB_assign_float_buffer(ImBuf *ibuf, float *buffer_data, const ImBufOwnership ownership)
size_t IMB_get_size_in_memory(const ImBuf *ibuf)
Provides wrapper around system-specific atomic primitives, and some extensions (faked-atomic operatio...
ATOMIC_INLINE int32_t atomic_add_and_fetch_int32(int32_t *p, int32_t x)
ATOMIC_INLINE int32_t atomic_load_int32(const int32_t *v)
ATOMIC_INLINE int32_t atomic_sub_and_fetch_int32(int32_t *p, int32_t x)
BMesh const char void * data
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
nullptr float
void colormanage_cache_free(ImBuf *ibuf)
void colormanage_imbuf_set_default_spaces(ImBuf *ibuf)
#define SIZE_MAX
#define IMB_DPI_DEFAULT
Definition imbuf.hh:26
#define LOG(level)
Definition log.h:97
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
size_t(* MEM_allocN_len)(const void *vmemh)
Definition mallocn.cc:36
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ImBufOwnership ownership
unsigned char * data
const ColorSpace * colorspace
ImBufOwnership ownership
const ColorSpace * colorspace
ImBufOwnership ownership
blender::gpu::Texture * texture
ImBufGPU gpu
char filepath[IMB_FILEPATH_SIZE]
DDSData dds_data
ImBufFloatBuffer float_buffer
ImbFormatOptions foptions
ImBufByteBuffer byte_buffer
unsigned char planes
enum eImbFileType ftype
unsigned int encoded_buffer_size
unsigned int * display_buffer_flags
IDProperty * metadata
ColormanageCache * colormanage_cache
ImBufByteBuffer encoded_buffer
int32_t refcounter
double ppm[2]
unsigned int encoded_size