mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
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:
parent
cebd27159e
commit
8317e48108
3 changed files with 243 additions and 222 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue