Refactor: ImBuf: Simplify scaling to work better with future sharing

Make the scale functions only process one buffer at a time, and avoid
the need to put the temporary buffer for box scaling into an ImBuf by
passing around the source buffer and its size rather than an ImBuf.

Pull Request: https://projects.blender.org/blender/blender/pulls/157607
This commit is contained in:
Hans Goudey 2026-04-21 13:57:24 +02:00 • committed by Hans Goudey
parent cebd27159e
commit 8317e48108
3 changed files with 243 additions and 222 deletions

View file

@ -401,21 +401,29 @@ enum class IMBScaleFilter {
* Scale/resize image to new dimensions.
* Return true if \a ibuf is modified.
*/
bool IMB_scale(ImBuf *ibuf,
unsigned int newx,
unsigned int newy,
IMBScaleFilter filter,
bool threaded = true);
bool IMB_scale(ImBuf *ibuf, int2 new_size, IMBScaleFilter filter, bool threaded = true);
inline bool IMB_scale(
ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded = true)
{
return IMB_scale(ibuf, int2(newx, newy), filter, threaded);
}
/**
* Scale/resize image to new dimensions, into a newly created result image.
* Metadata of input image (if any) is copied into the result image.
*/
ImBuf *IMB_scale_into_new(const ImBuf *ibuf,
unsigned int newx,
unsigned int newy,
int2 new_size,
IMBScaleFilter filter,
bool threaded = true);
inline ImBuf *IMB_scale_into_new(const ImBuf *ibuf,
unsigned int newx,
unsigned int newy,
IMBScaleFilter filter,
bool threaded = true)
{
return IMB_scale_into_new(ibuf, int2(newx, newy), filter, threaded);
}
/**
* Test if color-space conversions of pixels in buffer need to take into account alpha.

View file

@ -125,6 +125,7 @@ enum eImBufFlags {
/** Perform no color space conversions when reading, leave the image in the file colorspace. */
IB_no_colorspace_convert = 1 << 18,
};
ENUM_OPERATORS(eImBufFlags);
/** \} */

View file

@ -7,6 +7,7 @@
* \ingroup imbuf
*/
#include "BLI_math_interp.hh"
#include "BLI_math_vector.hh"
#include "BLI_task.hh"
#include "BLI_utildefines.h"
@ -16,37 +17,12 @@
#include "IMB_filter.hh"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
#include "IMB_metadata.hh"
#include "BLI_sys_types.h" /* for intptr_t support */
namespace blender {
static void alloc_scale_dst_buffers(
const ImBuf *ibuf, uint newx, uint newy, uchar4 **r_dst_byte, float **r_dst_float)
{
*r_dst_byte = nullptr;
if (ibuf->byte_data() != nullptr) {
*r_dst_byte = MEM_new_array_uninitialized<uchar4>(size_t(newx) * size_t(newy),
"scale_buf_byte");
if (*r_dst_byte == nullptr) {
return;
}
}
*r_dst_float = nullptr;
if (ibuf->float_data() != nullptr) {
*r_dst_float = MEM_new_array_uninitialized<float>(size_t(ibuf->channels) * newx * newy,
"scale_buf_float");
if (*r_dst_float == nullptr) {
if (*r_dst_byte) {
MEM_delete(*r_dst_byte);
}
return;
}
}
}
static inline float4 load_pixel(const uchar4 *ptr)
{
return float4(ptr[0]);
@ -88,41 +64,46 @@ static inline void store_pixel(float4 pix, float4 *ptr)
*ptr = pix;
}
template<typename Fn>
static void to_static_pixel_type(const ImBuf *ibuf,
uchar4 *dst_byte,
float *dst_float,
template<typename BufferT, typename Fn>
static void to_static_pixel_type(const BufferT *src_buffer,
const int channels,
BufferT *dst_buffer,
const Fn &fn)
{
if (dst_byte != nullptr) {
const uchar4 *src = reinterpret_cast<const uchar4 *>(ibuf->byte_data());
fn(src, dst_byte);
if constexpr (std::is_same_v<BufferT, uchar>) {
fn(reinterpret_cast<const uchar4 *>(src_buffer), reinterpret_cast<uchar4 *>(dst_buffer));
}
if (dst_float != nullptr) {
if (ibuf->channels == 1) {
fn(ibuf->float_data(), dst_float);
else {
if (channels == 1) {
fn(src_buffer, dst_buffer);
}
else if (ibuf->channels == 2) {
const float2 *src = reinterpret_cast<const float2 *>(ibuf->float_data());
fn(src, reinterpret_cast<float2 *>(dst_float));
else if (channels == 2) {
const float2 *src = reinterpret_cast<const float2 *>(src_buffer);
fn(src, reinterpret_cast<float2 *>(dst_buffer));
}
else if (ibuf->channels == 3) {
const float3 *src = reinterpret_cast<const float3 *>(ibuf->float_data());
fn(src, reinterpret_cast<float3 *>(dst_float));
else if (channels == 3) {
const float3 *src = reinterpret_cast<const float3 *>(src_buffer);
fn(src, reinterpret_cast<float3 *>(dst_buffer));
}
else if (ibuf->channels == 4) {
const float4 *src = reinterpret_cast<const float4 *>(ibuf->float_data());
fn(src, reinterpret_cast<float4 *>(dst_float));
else if (channels == 4) {
const float4 *src = reinterpret_cast<const float4 *>(src_buffer);
fn(src, reinterpret_cast<float4 *>(dst_buffer));
}
}
}
static void scale_down_x_func(
const ImBuf *ibuf, int newx, int /*newy*/, uchar4 *dst_byte, float *dst_float, bool threaded)
template<typename BufferT>
static void scale_down_x_func(const BufferT *src_buffer,
const int2 src_size,
const int channels,
BufferT *dst_buffer,
const int2 dst_size,
bool threaded)
{
const int ibufx = ibuf->x;
const int ibufy = ibuf->y;
to_static_pixel_type(ibuf, dst_byte, dst_float, [&]<typename T>(const T *src, T *dst) {
const int newx = dst_size.x;
const int ibufx = src_size.x;
const int ibufy = src_size.y;
to_static_pixel_type(src_buffer, channels, dst_buffer, [&]<typename T>(const T *src, T *dst) {
const float add = (ibufx - 0.01f) / newx;
const float inv_add = 1.0f / add;
@ -157,12 +138,18 @@ static void scale_down_x_func(
});
}
static void scale_down_y_func(
const ImBuf *ibuf, int /*newx*/, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
template<typename BufferT>
static void scale_down_y_func(const BufferT *src_buffer,
const int2 src_size,
const int channels,
BufferT *dst_buffer,
const int2 dst_size,
bool threaded)
{
const int ibufx = ibuf->x;
const int ibufy = ibuf->y;
to_static_pixel_type(ibuf, dst_byte, dst_float, [&]<typename T>(const T *src, T *dst) {
const int newy = dst_size.y;
const int ibufx = src_size.x;
const int ibufy = src_size.y;
to_static_pixel_type(src_buffer, channels, dst_buffer, [&]<typename T>(const T *src, T *dst) {
const float add = (ibufy - 0.01f) / newy;
const float inv_add = 1.0f / add;
@ -197,12 +184,18 @@ static void scale_down_y_func(
});
}
static void scale_up_x_func(
const ImBuf *ibuf, int newx, int /*newy*/, uchar4 *dst_byte, float *dst_float, bool threaded)
template<typename BufferT>
static void scale_up_x_func(const BufferT *src_buffer,
const int2 src_size,
const int channels,
BufferT *dst_buffer,
const int2 dst_size,
bool threaded)
{
const int ibufx = ibuf->x;
const int ibufy = ibuf->y;
to_static_pixel_type(ibuf, dst_byte, dst_float, [&]<typename T>(const T *src, T *dst) {
const int newx = dst_size.x;
const int ibufx = src_size.x;
const int ibufy = src_size.y;
to_static_pixel_type(src_buffer, channels, dst_buffer, [&]<typename T>(const T *src, T *dst) {
const float add = (ibufx - 0.001f) / newx;
/* Special case: source is 1px wide (see #70356). */
if (UNLIKELY(ibufx == 1)) {
@ -251,12 +244,18 @@ static void scale_up_x_func(
});
}
static void scale_up_y_func(
const ImBuf *ibuf, int /*newx*/, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
template<typename BufferT>
static void scale_up_y_func(const BufferT *src_buffer,
const int2 src_size,
const int channels,
BufferT *dst_buffer,
const int2 dst_size,
bool threaded)
{
const int ibufx = ibuf->x;
const int ibufy = ibuf->y;
to_static_pixel_type(ibuf, dst_byte, dst_float, [&]<typename T>(const T *src, T *dst) {
const int newy = dst_size.y;
const int ibufx = src_size.x;
const int ibufy = src_size.y;
to_static_pixel_type(src_buffer, channels, dst_buffer, [&]<typename T>(const T *src, T *dst) {
const float add = (ibufy - 0.001f) / newy;
/* Special case: source is 1px high (see #70356). */
if (UNLIKELY(ibufy == 1)) {
@ -304,55 +303,45 @@ static void scale_up_y_func(
});
}
using ScaleFunction = void (*)(
const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded);
static void scale_with_function(ImBuf *ibuf, int newx, int newy, ScaleFunction func, bool threaded)
template<typename BufferT>
static void imb_scale_box(const BufferT *src_buffer,
const int2 src_size,
const int channels,
BufferT *dst_buffer,
const int2 dst_size,
const bool threaded)
{
/* Allocate destination buffers. */
uchar4 *dst_byte = nullptr;
float *dst_float = nullptr;
alloc_scale_dst_buffers(ibuf, newx, newy, &dst_byte, &dst_float);
if (dst_byte == nullptr && dst_float == nullptr) {
return;
BufferT *tmp_buffer = MEM_new_array_uninitialized<BufferT>(
int64_t(channels) * src_size.x * dst_size.y, __func__);
if (dst_size.x < src_size.x) {
scale_down_x_func(
src_buffer, src_size, channels, tmp_buffer, int2(dst_size.x, src_size.y), threaded);
}
else {
scale_up_x_func(
src_buffer, src_size, channels, tmp_buffer, int2(dst_size.x, src_size.y), threaded);
}
/* Do actual processing. */
func(ibuf, newx, newy, dst_byte, dst_float, threaded);
if (dst_size.y < src_size.y) {
scale_down_y_func(
tmp_buffer, int2(dst_size.x, src_size.y), channels, dst_buffer, dst_size, threaded);
}
else {
scale_up_y_func(
tmp_buffer, int2(dst_size.x, src_size.y), channels, dst_buffer, dst_size, threaded);
}
/* Modify image to point to new destination. */
if (dst_byte != nullptr) {
IMB_free_byte_pixels(ibuf);
IMB_assign_byte_buffer(ibuf, reinterpret_cast<uint8_t *>(dst_byte), IB_TAKE_OWNERSHIP);
}
if (dst_float != nullptr) {
IMB_free_float_pixels(ibuf);
IMB_assign_float_buffer(ibuf, dst_float, IB_TAKE_OWNERSHIP);
}
ibuf->x = newx;
ibuf->y = newy;
}
static void imb_scale_box(ImBuf *ibuf, uint newx, uint newy, bool threaded)
{
if (newx != 0 && (newx < ibuf->x)) {
scale_with_function(ibuf, newx, ibuf->y, scale_down_x_func, threaded);
}
if (newy != 0 && (newy < ibuf->y)) {
scale_with_function(ibuf, ibuf->x, newy, scale_down_y_func, threaded);
}
if (newx != 0 && (newx > ibuf->x)) {
scale_with_function(ibuf, newx, ibuf->y, scale_up_x_func, threaded);
}
if (newy != 0 && (newy > ibuf->y)) {
scale_with_function(ibuf, ibuf->x, newy, scale_up_y_func, threaded);
}
MEM_delete(tmp_buffer);
}
template<typename T>
static void scale_nearest(
const T *src, T *dst, int ibufx, int ibufy, int newx, int newy, IndexRange y_range)
const T *src, T *dst, const int2 src_size, const int2 dst_size, IndexRange y_range)
{
const int ibufx = src_size.x;
const int ibufy = src_size.y;
const int newx = dst_size.x;
const int newy = dst_size.y;
/* Nearest sample scaling. Step through pixels in fixed point coordinates. */
constexpr int FRAC_BITS = 16;
int64_t stepx = ((int64_t(ibufx) << FRAC_BITS) + newx / 2) / newx;
@ -371,180 +360,203 @@ static void scale_nearest(
}
}
static void scale_nearest_func(
const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
template<typename BufferT>
static void scale_nearest_func(const BufferT *src_buffer,
const int2 src_size,
const int channels,
BufferT *dst_buffer,
const int2 dst_size,
bool threaded)
{
const int grain_size = threaded ? 64 : newy;
threading::parallel_for(IndexRange(newy), grain_size, [&](IndexRange y_range) {
/* Byte pixels. */
if (dst_byte != nullptr) {
const uchar4 *src = reinterpret_cast<const uchar4 *>(ibuf->byte_data());
scale_nearest(src, dst_byte, ibuf->x, ibuf->y, newx, newy, y_range);
const int grain_size = threaded ? 64 : dst_size.y;
threading::parallel_for(IndexRange(dst_size.y), grain_size, [&](IndexRange y_range) {
if constexpr (std::is_same_v<BufferT, uchar>) {
const uchar4 *src = reinterpret_cast<const uchar4 *>(src_buffer);
scale_nearest(src, reinterpret_cast<uchar4 *>(dst_buffer), src_size, dst_size, y_range);
}
/* Float pixels. */
if (dst_float != nullptr) {
if (ibuf->channels == 1) {
scale_nearest(ibuf->float_data(), dst_float, ibuf->x, ibuf->y, newx, newy, y_range);
else {
if (channels == 1) {
scale_nearest(src_buffer, dst_buffer, src_size, dst_size, y_range);
}
else if (ibuf->channels == 2) {
const float2 *src = reinterpret_cast<const float2 *>(ibuf->float_data());
scale_nearest(
src, reinterpret_cast<float2 *>(dst_float), ibuf->x, ibuf->y, newx, newy, y_range);
else if (channels == 2) {
const float2 *src = reinterpret_cast<const float2 *>(src_buffer);
scale_nearest(src, reinterpret_cast<float2 *>(dst_buffer), src_size, dst_size, y_range);
}
else if (ibuf->channels == 3) {
const float3 *src = reinterpret_cast<const float3 *>(ibuf->float_data());
scale_nearest(
src, reinterpret_cast<float3 *>(dst_float), ibuf->x, ibuf->y, newx, newy, y_range);
else if (channels == 3) {
const float3 *src = reinterpret_cast<const float3 *>(src_buffer);
scale_nearest(src, reinterpret_cast<float3 *>(dst_buffer), src_size, dst_size, y_range);
}
else if (ibuf->channels == 4) {
const float4 *src = reinterpret_cast<const float4 *>(ibuf->float_data());
scale_nearest(
src, reinterpret_cast<float4 *>(dst_float), ibuf->x, ibuf->y, newx, newy, y_range);
else if (channels == 4) {
const float4 *src = reinterpret_cast<const float4 *>(src_buffer);
scale_nearest(src, reinterpret_cast<float4 *>(dst_buffer), src_size, dst_size, y_range);
}
}
});
}
static void scale_bilinear_func(
const ImBuf *ibuf, int newx, int newy, uchar4 *dst_byte, float *dst_float, bool threaded)
template<typename BufferT>
static void scale_bilinear(const BufferT *src_buffer,
const int2 src_size,
const int channels,
BufferT *dst_buffer,
const int2 dst_size,
bool threaded)
{
using namespace blender::imbuf;
const int newx = dst_size.x;
const int newy = dst_size.y;
const int grain_size = threaded ? 32 : newy;
threading::parallel_for(IndexRange(newy), grain_size, [&](IndexRange y_range) {
float factor_x = float(ibuf->x) / newx;
float factor_y = float(ibuf->y) / newy;
float factor_x = float(src_size.x) / newx;
float factor_y = float(src_size.y) / newy;
for (const int y : y_range) {
float v = (float(y) + 0.5f) * factor_y - 0.5f;
for (int x = 0; x < newx; x++) {
float u = (float(x) + 0.5f) * factor_x - 0.5f;
int64_t offset = int64_t(y) * newx + x;
if (dst_byte) {
interpolate_bilinear_byte(ibuf, reinterpret_cast<uchar *>(dst_byte + offset), u, v);
if constexpr (std::is_same_v<BufferT, uchar>) {
*reinterpret_cast<uchar4 *>(dst_buffer + offset * 4) = math::interpolate_bilinear_byte(
src_buffer, src_size.x, src_size.y, u, v);
}
if (dst_float) {
float *pixel = dst_float + ibuf->channels * offset;
math::interpolate_bilinear_fl(
ibuf->float_data(), pixel, ibuf->x, ibuf->y, ibuf->channels, u, v);
else {
float *pixel = dst_buffer + channels * offset;
math::interpolate_bilinear_fl(src_buffer, pixel, src_size.x, src_size.y, channels, u, v);
}
}
}
});
}
bool IMB_scale(ImBuf *ibuf, uint newx, uint newy, IMBScaleFilter filter, bool threaded)
bool IMB_scale(ImBuf *ibuf, const int2 new_size, IMBScaleFilter filter, bool threaded)
{
BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!");
BLI_assert_msg(new_size.x > 0 && new_size.y > 0,
"Images must be at least 1 on both dimensions!");
if (ibuf == nullptr) {
return false;
}
if (newx == ibuf->x && newy == ibuf->y) {
const int2 src_size = int2(ibuf->x, ibuf->y);
if (src_size == new_size) {
return false;
}
switch (filter) {
case IMBScaleFilter::Nearest:
scale_with_function(ibuf, newx, newy, scale_nearest_func, threaded);
case IMBScaleFilter::Nearest: {
if (const float *src = ibuf->float_data()) {
float *dst = MEM_new_array_uninitialized<float>(
size_t(ibuf->channels) * new_size.x * new_size.y, __func__);
scale_nearest_func(src, src_size, ibuf->channels, dst, new_size, threaded);
IMB_assign_float_buffer(ibuf, dst, IB_TAKE_OWNERSHIP);
}
if (const uchar *src = ibuf->byte_data()) {
uchar *dst = MEM_new_array_uninitialized<uchar>(size_t(new_size.x) * new_size.y * 4,
__func__);
scale_nearest_func(src, src_size, 4, dst, new_size, threaded);
IMB_assign_byte_buffer(ibuf, dst, IB_TAKE_OWNERSHIP);
}
break;
case IMBScaleFilter::Bilinear:
scale_with_function(ibuf, newx, newy, scale_bilinear_func, threaded);
}
case IMBScaleFilter::Bilinear: {
if (const float *src = ibuf->float_data()) {
float *dst = MEM_new_array_uninitialized<float>(
size_t(ibuf->channels) * new_size.x * new_size.y, __func__);
scale_bilinear(src, src_size, ibuf->channels, dst, new_size, threaded);
IMB_assign_float_buffer(ibuf, dst, IB_TAKE_OWNERSHIP);
}
if (const uchar *src = ibuf->byte_data()) {
uchar *dst = MEM_new_array_uninitialized<uchar>(size_t(new_size.x) * new_size.y * 4,
__func__);
scale_bilinear(src, src_size, 4, dst, new_size, threaded);
IMB_assign_byte_buffer(ibuf, dst, IB_TAKE_OWNERSHIP);
}
break;
case IMBScaleFilter::Box:
imb_scale_box(ibuf, newx, newy, threaded);
}
case IMBScaleFilter::Box: {
if (const float *src = ibuf->float_data()) {
float *dst = MEM_new_array_uninitialized<float>(
size_t(ibuf->channels) * new_size.x * new_size.y, __func__);
imb_scale_box(src, src_size, ibuf->channels, dst, new_size, threaded);
IMB_assign_float_buffer(ibuf, dst, IB_TAKE_OWNERSHIP);
}
if (const uchar *src = ibuf->byte_data()) {
uchar *dst = MEM_new_array_uninitialized<uchar>(size_t(new_size.x) * new_size.y * 4,
__func__);
imb_scale_box(src, src_size, 4, dst, new_size, threaded);
IMB_assign_byte_buffer(ibuf, dst, IB_TAKE_OWNERSHIP);
}
break;
}
}
ibuf->x = new_size.x;
ibuf->y = new_size.y;
return true;
}
ImBuf *IMB_scale_into_new(
const ImBuf *ibuf, uint newx, uint newy, IMBScaleFilter filter, bool threaded)
ImBuf *IMB_scale_into_new(const ImBuf *ibuf,
const int2 new_size,
IMBScaleFilter filter,
bool threaded)
{
BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!");
BLI_assert_msg(new_size.x > 0 && new_size.y > 0,
"Images must be at least 1 on both dimensions!");
if (ibuf == nullptr) {
return nullptr;
}
/* Size same as source: just copy source image. */
if (newx == ibuf->x && newy == ibuf->y) {
const int2 src_size = int2(ibuf->x, ibuf->y);
if (src_size == new_size) {
ImBuf *dst = IMB_dupImBuf(ibuf);
IMB_metadata_copy(dst, ibuf);
return dst;
}
/* Allocate destination buffers. */
uchar4 *dst_byte = nullptr;
float *dst_float = nullptr;
alloc_scale_dst_buffers(ibuf, newx, newy, &dst_byte, &dst_float);
eImBufFlags flags = IB_uninitialized_pixels;
if (ibuf->byte_data()) {
flags |= IB_byte_data;
}
if (ibuf->float_data()) {
flags |= IB_float_data;
}
ImBuf *dst = IMB_allocImBuf(new_size.x, new_size.y, ibuf->planes, flags);
dst->channels = ibuf->channels;
IMB_metadata_copy(dst, ibuf);
dst->colormanage_flag = ibuf->colormanage_flag;
uchar *dst_byte = dst->byte_data_for_write();
float *dst_float = dst->float_data_for_write();
if (dst_byte == nullptr && dst_float == nullptr) {
IMB_freeImBuf(dst);
return nullptr;
}
switch (filter) {
case IMBScaleFilter::Nearest:
scale_nearest_func(ibuf, newx, newy, dst_byte, dst_float, threaded);
case IMBScaleFilter::Nearest: {
if (const float *src = ibuf->float_data()) {
scale_nearest_func(src, src_size, ibuf->channels, dst_float, new_size, threaded);
}
if (const uchar *src = ibuf->byte_data()) {
scale_nearest_func(src, src_size, 4, dst_byte, new_size, threaded);
}
break;
case IMBScaleFilter::Bilinear:
scale_bilinear_func(ibuf, newx, newy, dst_byte, dst_float, threaded);
}
case IMBScaleFilter::Bilinear: {
if (const float *src = ibuf->float_data()) {
scale_bilinear(src, src_size, ibuf->channels, dst_float, new_size, threaded);
}
if (const uchar *src = ibuf->byte_data()) {
scale_bilinear(src, src_size, 4, dst_byte, new_size, threaded);
}
break;
}
case IMBScaleFilter::Box: {
/* Horizontal scale. */
uchar4 *tmp_byte = nullptr;
float *tmp_float = nullptr;
alloc_scale_dst_buffers(ibuf, newx, ibuf->y, &tmp_byte, &tmp_float);
if (tmp_byte == nullptr && tmp_float == nullptr) {
if (dst_byte != nullptr) {
MEM_delete(dst_byte);
}
if (dst_byte != nullptr) {
MEM_delete(dst_float);
}
return nullptr;
if (const float *src = ibuf->float_data()) {
imb_scale_box(src, src_size, ibuf->channels, dst_float, new_size, threaded);
}
if (newx < ibuf->x) {
scale_down_x_func(ibuf, newx, ibuf->y, tmp_byte, tmp_float, threaded);
if (const uchar *src = ibuf->byte_data()) {
imb_scale_box(src, src_size, 4, dst_byte, new_size, threaded);
}
else {
scale_up_x_func(ibuf, newx, ibuf->y, tmp_byte, tmp_float, threaded);
}
/* Vertical scale. */
ImBuf tmpbuf;
IMB_initImBuf(&tmpbuf, newx, ibuf->y, ibuf->planes, 0);
if (tmp_byte != nullptr) {
IMB_assign_byte_buffer(
&tmpbuf, reinterpret_cast<uint8_t *>(tmp_byte), IB_DO_NOT_TAKE_OWNERSHIP);
}
if (tmp_float != nullptr) {
IMB_assign_float_buffer(&tmpbuf, tmp_float, IB_DO_NOT_TAKE_OWNERSHIP);
}
if (newy < ibuf->y) {
scale_down_y_func(&tmpbuf, newx, newy, dst_byte, dst_float, threaded);
}
else {
scale_up_y_func(&tmpbuf, newx, newy, dst_byte, dst_float, threaded);
}
if (tmp_byte != nullptr) {
MEM_delete(tmp_byte);
}
if (tmp_float != nullptr) {
MEM_delete(tmp_float);
}
} break;
}
/* Create result image. */
ImBuf *dst = IMB_allocImBuf(newx, newy, ibuf->planes, IB_uninitialized_pixels);
dst->channels = ibuf->channels;
IMB_metadata_copy(dst, ibuf);
dst->colormanage_flag = ibuf->colormanage_flag;
if (dst_byte != nullptr) {
IMB_assign_byte_buffer(dst, reinterpret_cast<uint8_t *>(dst_byte), IB_TAKE_OWNERSHIP);
dst->byte_buffer.colorspace = ibuf->byte_buffer.colorspace;
}
if (dst_float != nullptr) {
IMB_assign_float_buffer(dst, dst_float, IB_TAKE_OWNERSHIP);
dst->float_buffer.colorspace = ibuf->float_buffer.colorspace;
break;
}
}
return dst;
}