738 lines
17 KiB
C++
738 lines
17 KiB
C++
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup imbuf
|
|
*/
|
|
|
|
/* It's become a bit messy... Basically, only the IMB_ prefixed files
|
|
* should remain. */
|
|
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
|
|
#include "IMB_imbuf.hh"
|
|
#include "IMB_imbuf_types.hh"
|
|
|
|
#include "IMB_allocimbuf.hh"
|
|
#include "IMB_colormanagement_intern.hh"
|
|
#include "IMB_filetype.hh"
|
|
#include "IMB_metadata.hh"
|
|
|
|
#include "imbuf.hh"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_threads.h"
|
|
|
|
#include "GPU_texture.hh"
|
|
|
|
static SpinLock refcounter_spin;
|
|
|
|
void imb_refcounter_lock_init()
|
|
{
|
|
BLI_spin_init(&refcounter_spin);
|
|
}
|
|
|
|
void imb_refcounter_lock_exit()
|
|
{
|
|
BLI_spin_end(&refcounter_spin);
|
|
}
|
|
|
|
#ifndef WIN32
|
|
static SpinLock mmap_spin;
|
|
|
|
void imb_mmap_lock_init()
|
|
{
|
|
BLI_spin_init(&mmap_spin);
|
|
}
|
|
|
|
void imb_mmap_lock_exit()
|
|
{
|
|
BLI_spin_end(&mmap_spin);
|
|
}
|
|
|
|
void imb_mmap_lock()
|
|
{
|
|
BLI_spin_lock(&mmap_spin);
|
|
}
|
|
|
|
void imb_mmap_unlock()
|
|
{
|
|
BLI_spin_unlock(&mmap_spin);
|
|
}
|
|
#endif
|
|
|
|
/* Free the specified buffer storage, freeing memory when needed and restoring the state of the
|
|
* buffer to its defaults. */
|
|
template<class BufferType> static void imb_free_buffer(BufferType &buffer)
|
|
{
|
|
if (buffer.data) {
|
|
switch (buffer.ownership) {
|
|
case IB_DO_NOT_TAKE_OWNERSHIP:
|
|
break;
|
|
|
|
case IB_TAKE_OWNERSHIP:
|
|
MEM_freeN(buffer.data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Reset buffer to defaults. */
|
|
buffer.data = nullptr;
|
|
buffer.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
|
|
}
|
|
|
|
/* Free the specified DDS buffer storage, freeing memory when needed and restoring the state of the
|
|
* buffer to its defaults. */
|
|
static void imb_free_dds_buffer(DDSData &dds_data)
|
|
{
|
|
if (dds_data.data) {
|
|
switch (dds_data.ownership) {
|
|
case IB_DO_NOT_TAKE_OWNERSHIP:
|
|
break;
|
|
|
|
case IB_TAKE_OWNERSHIP:
|
|
/* dds_data.data is allocated by DirectDrawSurface::readData(), so don't use MEM_freeN! */
|
|
free(dds_data.data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Reset buffer to defaults. */
|
|
dds_data.data = nullptr;
|
|
dds_data.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
|
|
}
|
|
|
|
/* Allocate pixel storage of the given buffer. The buffer owns the allocated memory.
|
|
* Returns true of allocation succeeded, false otherwise. */
|
|
template<class BufferType>
|
|
bool imb_alloc_buffer(BufferType &buffer,
|
|
const uint x,
|
|
const uint y,
|
|
const uint channels,
|
|
const size_t type_size,
|
|
bool initialize_pixels)
|
|
{
|
|
buffer.data = static_cast<decltype(BufferType::data)>(
|
|
imb_alloc_pixels(x, y, channels, type_size, initialize_pixels, __func__));
|
|
if (!buffer.data) {
|
|
return false;
|
|
}
|
|
|
|
buffer.ownership = IB_TAKE_OWNERSHIP;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Make the buffer available for modification.
|
|
* Is achieved by ensuring that the buffer is the only owner of its data. */
|
|
template<class BufferType> void imb_make_writeable_buffer(BufferType &buffer)
|
|
{
|
|
if (!buffer.data) {
|
|
return;
|
|
}
|
|
|
|
switch (buffer.ownership) {
|
|
case IB_DO_NOT_TAKE_OWNERSHIP:
|
|
buffer.data = static_cast<decltype(BufferType::data)>(MEM_dupallocN(buffer.data));
|
|
buffer.ownership = IB_TAKE_OWNERSHIP;
|
|
|
|
case IB_TAKE_OWNERSHIP:
|
|
break;
|
|
}
|
|
}
|
|
|
|
template<class BufferType>
|
|
auto imb_steal_buffer_data(BufferType &buffer) -> decltype(BufferType::data)
|
|
{
|
|
if (!buffer.data) {
|
|
return nullptr;
|
|
}
|
|
|
|
switch (buffer.ownership) {
|
|
case IB_DO_NOT_TAKE_OWNERSHIP:
|
|
BLI_assert_msg(false, "Unexpected behavior: stealing non-owned data pointer");
|
|
return nullptr;
|
|
|
|
case IB_TAKE_OWNERSHIP: {
|
|
decltype(BufferType::data) data = buffer.data;
|
|
|
|
buffer.data = nullptr;
|
|
buffer.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
|
|
|
|
return data;
|
|
}
|
|
}
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void imb_freemipmapImBuf(ImBuf *ibuf)
|
|
{
|
|
int a;
|
|
|
|
/* Do not trust ibuf->miptot, in some cases IMB_remakemipmap can leave unfreed unused levels,
|
|
* leading to memory leaks... */
|
|
for (a = 0; a < IMB_MIPMAP_LEVELS; a++) {
|
|
if (ibuf->mipmap[a] != nullptr) {
|
|
IMB_freeImBuf(ibuf->mipmap[a]);
|
|
ibuf->mipmap[a] = nullptr;
|
|
}
|
|
}
|
|
|
|
ibuf->miptot = 0;
|
|
}
|
|
|
|
void imb_freerectfloatImBuf(ImBuf *ibuf)
|
|
{
|
|
if (ibuf == nullptr) {
|
|
return;
|
|
}
|
|
|
|
imb_free_buffer(ibuf->float_buffer);
|
|
|
|
imb_freemipmapImBuf(ibuf);
|
|
|
|
ibuf->flags &= ~IB_rectfloat;
|
|
}
|
|
|
|
void imb_freerectImBuf(ImBuf *ibuf)
|
|
{
|
|
if (ibuf == nullptr) {
|
|
return;
|
|
}
|
|
|
|
imb_free_buffer(ibuf->byte_buffer);
|
|
|
|
imb_freemipmapImBuf(ibuf);
|
|
|
|
ibuf->flags &= ~IB_rect;
|
|
}
|
|
|
|
static void freeencodedbufferImBuf(ImBuf *ibuf)
|
|
{
|
|
if (ibuf == nullptr) {
|
|
return;
|
|
}
|
|
|
|
imb_free_buffer(ibuf->encoded_buffer);
|
|
|
|
ibuf->encoded_buffer_size = 0;
|
|
ibuf->encoded_size = 0;
|
|
|
|
ibuf->flags &= ~IB_mem;
|
|
}
|
|
|
|
void imb_freerectImbuf_all(ImBuf *ibuf)
|
|
{
|
|
imb_freerectImBuf(ibuf);
|
|
imb_freerectfloatImBuf(ibuf);
|
|
freeencodedbufferImBuf(ibuf);
|
|
}
|
|
|
|
void IMB_free_gpu_textures(ImBuf *ibuf)
|
|
{
|
|
if (!ibuf || !ibuf->gpu.texture) {
|
|
return;
|
|
}
|
|
|
|
GPU_texture_free(ibuf->gpu.texture);
|
|
ibuf->gpu.texture = nullptr;
|
|
}
|
|
|
|
void IMB_freeImBuf(ImBuf *ibuf)
|
|
{
|
|
if (ibuf == nullptr) {
|
|
return;
|
|
}
|
|
|
|
bool needs_free = false;
|
|
|
|
BLI_spin_lock(&refcounter_spin);
|
|
if (ibuf->refcounter > 0) {
|
|
ibuf->refcounter--;
|
|
}
|
|
else {
|
|
needs_free = true;
|
|
}
|
|
BLI_spin_unlock(&refcounter_spin);
|
|
|
|
if (needs_free) {
|
|
/* Include this check here as the path may be manipulated after creation. */
|
|
BLI_assert_msg(!(ibuf->filepath[0] == '/' && ibuf->filepath[1] == '/'),
|
|
"'.blend' relative \"//\" must not be used in ImBuf!");
|
|
|
|
imb_freerectImbuf_all(ibuf);
|
|
IMB_free_gpu_textures(ibuf);
|
|
IMB_metadata_free(ibuf->metadata);
|
|
colormanage_cache_free(ibuf);
|
|
imb_free_dds_buffer(ibuf->dds_data);
|
|
MEM_freeN(ibuf);
|
|
}
|
|
}
|
|
|
|
void IMB_refImBuf(ImBuf *ibuf)
|
|
{
|
|
BLI_spin_lock(&refcounter_spin);
|
|
ibuf->refcounter++;
|
|
BLI_spin_unlock(&refcounter_spin);
|
|
}
|
|
|
|
ImBuf *IMB_makeSingleUser(ImBuf *ibuf)
|
|
{
|
|
if (ibuf == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
BLI_spin_lock(&refcounter_spin);
|
|
const bool is_single = (ibuf->refcounter == 0);
|
|
BLI_spin_unlock(&refcounter_spin);
|
|
if (is_single) {
|
|
return ibuf;
|
|
}
|
|
|
|
ImBuf *rval = IMB_dupImBuf(ibuf);
|
|
|
|
IMB_metadata_copy(rval, ibuf);
|
|
|
|
IMB_freeImBuf(ibuf);
|
|
|
|
return rval;
|
|
}
|
|
|
|
bool imb_addencodedbufferImBuf(ImBuf *ibuf)
|
|
{
|
|
if (ibuf == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
freeencodedbufferImBuf(ibuf);
|
|
|
|
if (ibuf->encoded_buffer_size == 0) {
|
|
ibuf->encoded_buffer_size = 10000;
|
|
}
|
|
|
|
ibuf->encoded_size = 0;
|
|
|
|
if (!imb_alloc_buffer(
|
|
ibuf->encoded_buffer, ibuf->encoded_buffer_size, 1, 1, sizeof(uint8_t), true))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ibuf->flags |= IB_mem;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool imb_enlargeencodedbufferImBuf(ImBuf *ibuf)
|
|
{
|
|
if (ibuf == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
if (ibuf->encoded_buffer_size < ibuf->encoded_size) {
|
|
printf("%s: error in parameters\n", __func__);
|
|
return false;
|
|
}
|
|
|
|
uint newsize = 2 * ibuf->encoded_buffer_size;
|
|
newsize = std::max<uint>(newsize, 10000);
|
|
|
|
ImBufByteBuffer new_buffer;
|
|
if (!imb_alloc_buffer(new_buffer, newsize, 1, 1, sizeof(uint8_t), true)) {
|
|
return false;
|
|
}
|
|
|
|
if (ibuf->encoded_buffer.data) {
|
|
memcpy(new_buffer.data, ibuf->encoded_buffer.data, ibuf->encoded_size);
|
|
}
|
|
else {
|
|
ibuf->encoded_size = 0;
|
|
}
|
|
|
|
imb_free_buffer(ibuf->encoded_buffer);
|
|
|
|
ibuf->encoded_buffer = new_buffer;
|
|
ibuf->encoded_buffer_size = newsize;
|
|
ibuf->flags |= IB_mem;
|
|
|
|
return true;
|
|
}
|
|
|
|
void *imb_alloc_pixels(
|
|
uint x, uint y, uint channels, size_t typesize, bool initialize_pixels, const char *alloc_name)
|
|
{
|
|
/* Protect against buffer overflow vulnerabilities from files specifying
|
|
* a width and height that overflow and alloc too little memory. */
|
|
if (!(uint64_t(x) * uint64_t(y) < (SIZE_MAX / (channels * typesize)))) {
|
|
return nullptr;
|
|
}
|
|
|
|
size_t size = size_t(x) * size_t(y) * size_t(channels) * typesize;
|
|
return initialize_pixels ? MEM_callocN(size, alloc_name) : MEM_mallocN(size, alloc_name);
|
|
}
|
|
|
|
bool imb_addrectfloatImBuf(ImBuf *ibuf, const uint channels, bool initialize_pixels)
|
|
{
|
|
if (ibuf == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
/* NOTE: Follows the historical code.
|
|
* Is unclear if it is desired or not to free mipmaps. If mipmaps are to be preserved a simple
|
|
* `imb_free_buffer(ibuf->float_buffer)` can be used instead. */
|
|
if (ibuf->float_buffer.data) {
|
|
imb_freerectfloatImBuf(ibuf); /* frees mipmap too, hrm */
|
|
}
|
|
|
|
if (!imb_alloc_buffer(
|
|
ibuf->float_buffer, ibuf->x, ibuf->y, channels, sizeof(float), initialize_pixels))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ibuf->channels = channels;
|
|
ibuf->flags |= IB_rectfloat;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool imb_addrectImBuf(ImBuf *ibuf, bool initialize_pixels)
|
|
{
|
|
/* Question; why also add ZBUF (when `planes > 32`)? */
|
|
|
|
if (ibuf == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
/* Don't call imb_freerectImBuf, it frees mipmaps,
|
|
* this call is used only too give float buffers display. */
|
|
imb_free_buffer(ibuf->byte_buffer);
|
|
|
|
if (!imb_alloc_buffer(
|
|
ibuf->byte_buffer, ibuf->x, ibuf->y, 4, sizeof(uint8_t), initialize_pixels))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ibuf->flags |= IB_rect;
|
|
|
|
return true;
|
|
}
|
|
|
|
uint8_t *IMB_steal_byte_buffer(ImBuf *ibuf)
|
|
{
|
|
uint8_t *data = imb_steal_buffer_data(ibuf->byte_buffer);
|
|
ibuf->flags &= ~IB_rect;
|
|
return data;
|
|
}
|
|
|
|
float *IMB_steal_float_buffer(ImBuf *ibuf)
|
|
{
|
|
float *data = imb_steal_buffer_data(ibuf->float_buffer);
|
|
ibuf->flags &= ~IB_rectfloat;
|
|
return data;
|
|
}
|
|
|
|
uint8_t *IMB_steal_encoded_buffer(ImBuf *ibuf)
|
|
{
|
|
uint8_t *data = imb_steal_buffer_data(ibuf->encoded_buffer);
|
|
|
|
ibuf->encoded_size = 0;
|
|
ibuf->encoded_buffer_size = 0;
|
|
|
|
ibuf->flags &= ~IB_mem;
|
|
|
|
return data;
|
|
}
|
|
|
|
void IMB_make_writable_byte_buffer(ImBuf *ibuf)
|
|
{
|
|
imb_make_writeable_buffer(ibuf->byte_buffer);
|
|
}
|
|
|
|
void IMB_make_writable_float_buffer(ImBuf *ibuf)
|
|
{
|
|
imb_make_writeable_buffer(ibuf->float_buffer);
|
|
}
|
|
|
|
void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, const ImBufOwnership ownership)
|
|
{
|
|
imb_free_buffer(ibuf->byte_buffer);
|
|
ibuf->flags &= ~IB_rect;
|
|
|
|
if (buffer_data) {
|
|
ibuf->byte_buffer.data = buffer_data;
|
|
ibuf->byte_buffer.ownership = ownership;
|
|
|
|
ibuf->flags |= IB_rect;
|
|
}
|
|
}
|
|
|
|
void IMB_assign_float_buffer(ImBuf *ibuf, float *buffer_data, const ImBufOwnership ownership)
|
|
{
|
|
imb_free_buffer(ibuf->float_buffer);
|
|
ibuf->flags &= ~IB_rectfloat;
|
|
|
|
if (buffer_data) {
|
|
ibuf->float_buffer.data = buffer_data;
|
|
ibuf->float_buffer.ownership = ownership;
|
|
|
|
ibuf->flags |= IB_rectfloat;
|
|
}
|
|
}
|
|
|
|
void IMB_assign_byte_buffer(ImBuf *ibuf,
|
|
const ImBufByteBuffer &buffer,
|
|
const ImBufOwnership ownership)
|
|
{
|
|
IMB_assign_byte_buffer(ibuf, buffer.data, ownership);
|
|
ibuf->byte_buffer.colorspace = buffer.colorspace;
|
|
}
|
|
|
|
void IMB_assign_float_buffer(ImBuf *ibuf,
|
|
const ImBufFloatBuffer &buffer,
|
|
const ImBufOwnership ownership)
|
|
{
|
|
IMB_assign_float_buffer(ibuf, buffer.data, ownership);
|
|
ibuf->float_buffer.colorspace = buffer.colorspace;
|
|
}
|
|
|
|
void IMB_assign_dds_data(ImBuf *ibuf, const DDSData &data, const ImBufOwnership ownership)
|
|
{
|
|
BLI_assert(ibuf->ftype == IMB_FTYPE_DDS);
|
|
|
|
imb_free_dds_buffer(ibuf->dds_data);
|
|
|
|
ibuf->dds_data = data;
|
|
ibuf->dds_data.ownership = ownership;
|
|
}
|
|
|
|
ImBuf *IMB_allocFromBufferOwn(
|
|
uint8_t *byte_buffer, float *float_buffer, uint w, uint h, uint channels)
|
|
{
|
|
if (!(byte_buffer || float_buffer)) {
|
|
return nullptr;
|
|
}
|
|
|
|
ImBuf *ibuf = IMB_allocImBuf(w, h, 32, 0);
|
|
|
|
ibuf->channels = channels;
|
|
|
|
if (float_buffer) {
|
|
/* TODO(sergey): The 4 channels is the historical code. Should probably be `channels`, but
|
|
* needs a dedicated investigation. */
|
|
BLI_assert(MEM_allocN_len(float_buffer) == sizeof(float[4]) * w * h);
|
|
IMB_assign_float_buffer(ibuf, float_buffer, IB_TAKE_OWNERSHIP);
|
|
}
|
|
|
|
if (byte_buffer) {
|
|
BLI_assert(MEM_allocN_len(byte_buffer) == sizeof(uint8_t[4]) * w * h);
|
|
IMB_assign_byte_buffer(ibuf, byte_buffer, IB_TAKE_OWNERSHIP);
|
|
}
|
|
|
|
return ibuf;
|
|
}
|
|
|
|
ImBuf *IMB_allocFromBuffer(
|
|
const uint8_t *byte_buffer, const float *float_buffer, uint w, uint h, uint channels)
|
|
{
|
|
ImBuf *ibuf = nullptr;
|
|
|
|
if (!(byte_buffer || float_buffer)) {
|
|
return nullptr;
|
|
}
|
|
|
|
ibuf = IMB_allocImBuf(w, h, 32, 0);
|
|
|
|
ibuf->channels = channels;
|
|
|
|
/* NOTE: Avoid #MEM_dupallocN since the buffers might not be allocated using guarded-allocation.
|
|
*/
|
|
if (float_buffer) {
|
|
/* TODO(sergey): The 4 channels is the historical code. Should probably be `channels`, but
|
|
* needs a dedicated investigation. */
|
|
imb_alloc_buffer(ibuf->float_buffer, w, h, 4, sizeof(float), false);
|
|
|
|
memcpy(ibuf->float_buffer.data, float_buffer, sizeof(float[4]) * w * h);
|
|
}
|
|
|
|
if (byte_buffer) {
|
|
imb_alloc_buffer(ibuf->byte_buffer, w, h, 4, sizeof(uint8_t), false);
|
|
|
|
memcpy(ibuf->byte_buffer.data, byte_buffer, sizeof(uint8_t[4]) * w * h);
|
|
}
|
|
|
|
return ibuf;
|
|
}
|
|
|
|
ImBuf *IMB_allocImBuf(uint x, uint y, uchar planes, uint flags)
|
|
{
|
|
ImBuf *ibuf = MEM_cnew<ImBuf>("ImBuf_struct");
|
|
|
|
if (ibuf) {
|
|
if (!IMB_initImBuf(ibuf, x, y, planes, flags)) {
|
|
IMB_freeImBuf(ibuf);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return ibuf;
|
|
}
|
|
|
|
bool IMB_initImBuf(ImBuf *ibuf, uint x, uint y, uchar planes, uint flags)
|
|
{
|
|
memset(ibuf, 0, sizeof(ImBuf));
|
|
|
|
ibuf->x = x;
|
|
ibuf->y = y;
|
|
ibuf->planes = planes;
|
|
ibuf->ftype = IMB_FTYPE_PNG;
|
|
/* The '15' means, set compression to low ratio but not time consuming. */
|
|
ibuf->foptions.quality = 15;
|
|
/* float option, is set to other values when buffers get assigned. */
|
|
ibuf->channels = 4;
|
|
/* IMB_DPI_DEFAULT -> pixels-per-meter. */
|
|
ibuf->ppm[0] = ibuf->ppm[1] = IMB_DPI_DEFAULT / 0.0254;
|
|
|
|
const bool init_pixels = (flags & IB_uninitialized_pixels) == 0;
|
|
|
|
if (flags & IB_rect) {
|
|
if (imb_addrectImBuf(ibuf, init_pixels) == false) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (flags & IB_rectfloat) {
|
|
if (imb_addrectfloatImBuf(ibuf, ibuf->channels, init_pixels) == false) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* assign default spaces */
|
|
colormanage_imbuf_set_default_spaces(ibuf);
|
|
|
|
return true;
|
|
}
|
|
|
|
ImBuf *IMB_dupImBuf(const ImBuf *ibuf1)
|
|
{
|
|
ImBuf *ibuf2, tbuf;
|
|
int flags = IB_uninitialized_pixels;
|
|
int a, x, y;
|
|
|
|
if (ibuf1 == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (ibuf1->byte_buffer.data) {
|
|
flags |= IB_rect;
|
|
}
|
|
|
|
x = ibuf1->x;
|
|
y = ibuf1->y;
|
|
|
|
ibuf2 = IMB_allocImBuf(x, y, ibuf1->planes, flags);
|
|
if (ibuf2 == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (flags & IB_rect) {
|
|
memcpy(ibuf2->byte_buffer.data, ibuf1->byte_buffer.data, size_t(x) * y * 4 * sizeof(uint8_t));
|
|
}
|
|
|
|
if (ibuf1->float_buffer.data) {
|
|
/* Ensure the correct number of channels are being allocated for the new #ImBuf. Some
|
|
* compositing scenarios might end up with >4 channels and we want to duplicate them properly.
|
|
*/
|
|
if (imb_addrectfloatImBuf(ibuf2, ibuf1->channels, false) == false) {
|
|
IMB_freeImBuf(ibuf2);
|
|
return nullptr;
|
|
}
|
|
|
|
memcpy(ibuf2->float_buffer.data,
|
|
ibuf1->float_buffer.data,
|
|
size_t(ibuf2->channels) * x * y * sizeof(float));
|
|
}
|
|
|
|
if (ibuf1->encoded_buffer.data) {
|
|
ibuf2->encoded_buffer_size = ibuf1->encoded_buffer_size;
|
|
if (imb_addencodedbufferImBuf(ibuf2) == false) {
|
|
IMB_freeImBuf(ibuf2);
|
|
return nullptr;
|
|
}
|
|
|
|
memcpy(ibuf2->encoded_buffer.data, ibuf1->encoded_buffer.data, ibuf1->encoded_size);
|
|
}
|
|
|
|
ibuf2->byte_buffer.colorspace = ibuf1->byte_buffer.colorspace;
|
|
ibuf2->float_buffer.colorspace = ibuf1->float_buffer.colorspace;
|
|
|
|
/* silly trick to copy the entire contents of ibuf1 struct over to ibuf */
|
|
tbuf = *ibuf1;
|
|
|
|
/* fix pointers */
|
|
tbuf.byte_buffer = ibuf2->byte_buffer;
|
|
tbuf.float_buffer = ibuf2->float_buffer;
|
|
tbuf.encoded_buffer = ibuf2->encoded_buffer;
|
|
for (a = 0; a < IMB_MIPMAP_LEVELS; a++) {
|
|
tbuf.mipmap[a] = nullptr;
|
|
}
|
|
tbuf.dds_data.data = nullptr;
|
|
|
|
/* set malloc flag */
|
|
tbuf.refcounter = 0;
|
|
|
|
/* for now don't duplicate metadata */
|
|
tbuf.metadata = nullptr;
|
|
|
|
tbuf.display_buffer_flags = nullptr;
|
|
tbuf.colormanage_cache = nullptr;
|
|
|
|
/* GPU textures can not be easily copied, as it is not guaranteed that this function is called
|
|
* from within an active GPU context. */
|
|
tbuf.gpu.texture = nullptr;
|
|
|
|
*ibuf2 = tbuf;
|
|
|
|
return ibuf2;
|
|
}
|
|
|
|
size_t IMB_get_rect_len(const ImBuf *ibuf)
|
|
{
|
|
return size_t(ibuf->x) * size_t(ibuf->y);
|
|
}
|
|
|
|
size_t IMB_get_size_in_memory(ImBuf *ibuf)
|
|
{
|
|
int a;
|
|
size_t size = 0, channel_size = 0;
|
|
|
|
size += sizeof(ImBuf);
|
|
|
|
if (ibuf->byte_buffer.data) {
|
|
channel_size += sizeof(char);
|
|
}
|
|
|
|
if (ibuf->float_buffer.data) {
|
|
channel_size += sizeof(float);
|
|
}
|
|
|
|
size += channel_size * ibuf->x * ibuf->y * ibuf->channels;
|
|
|
|
if (ibuf->miptot) {
|
|
for (a = 0; a < ibuf->miptot; a++) {
|
|
if (ibuf->mipmap[a]) {
|
|
size += IMB_get_size_in_memory(ibuf->mipmap[a]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return size;
|
|
}
|