WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 358 commits from brush-assets-project into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
37 changed files with 1012 additions and 846 deletions
Showing only changes of commit 1db2f2380a - Show all commits

View File

@ -1718,10 +1718,12 @@ class CyclesPreferences(bpy.types.AddonPreferences):
col.prop(self, "metalrt")
if compute_device_type == 'HIP':
has_cuda, has_optix, has_hip, has_metal, has_oneapi, has_hiprt = _cycles.get_device_types()
row = layout.row()
row.enabled = has_hiprt
row.prop(self, "use_hiprt")
import platform
if platform.system() == "Windows": # HIP-RT is currently only supported on Windows
has_cuda, has_optix, has_hip, has_metal, has_oneapi, has_hiprt = _cycles.get_device_types()
row = layout.row()
row.enabled = has_hiprt
row.prop(self, "use_hiprt")
elif compute_device_type == 'ONEAPI' and _cycles.with_embree_gpu:
row = layout.row()

View File

@ -650,6 +650,9 @@ static bool bake_setup_pass(Scene *scene, const string &bake_type, const int bak
integrator->set_use_direct_light(use_direct_light);
integrator->set_use_indirect_light(use_indirect_light);
const PassInfo pass_info = Pass::get_info(type);
integrator->set_use_denoise(pass_info.support_denoise);
return true;
}

View File

@ -4711,9 +4711,9 @@ def km_object_mode(params):
("object.join", {"type": 'J', "value": 'PRESS', "ctrl": True}, None),
("wm.context_toggle", {"type": 'PERIOD', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'tool_settings.use_transform_data_origin')]}),
("anim.keyframe_insert", {"type": 'I', "value": 'PRESS'}, None),
("anim.keyframe_insert_menu", {"type": 'K', "value": 'PRESS'}, {"properties": [("always_prompt", True)]}),
("anim.keyframe_delete_v3d", {"type": 'I', "value": 'PRESS', "alt": True}, None),
("anim.keying_set_active_set", {"type": 'I', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
("anim.keying_set_active_set", {"type": 'K', "value": 'PRESS', "shift": True}, None),
("collection.create", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
("collection.objects_remove", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("collection.objects_remove_all",
@ -4730,6 +4730,16 @@ def km_object_mode(params):
*_template_items_context_menu("VIEW3D_MT_object_context_menu", params.context_menu_event),
])
if params.use_pie_click_drag:
items.extend([
("anim.keyframe_insert", {"type": 'I', "value": 'CLICK'}, None),
op_menu_pie("ANIM_MT_keyframe_insert_pie", {"type": 'I', "value": 'CLICK_DRAG'}),
])
else:
items.extend([
("anim.keyframe_insert", {"type": 'I', "value": 'PRESS'}, None),
])
if params.legacy:
items.extend([
("object.select_mirror", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None),
@ -4850,9 +4860,9 @@ def km_pose(params):
("armature.assign_to_collection", {"type": 'M', "value": 'PRESS', "shift": True}, None),
("armature.move_to_collection", {"type": 'M', "value": 'PRESS'}, None),
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
("anim.keyframe_insert", {"type": 'I', "value": 'PRESS'}, None),
("anim.keyframe_insert_menu", {"type": 'K', "value": 'PRESS'}, {"properties": [("always_prompt", True)]}),
("anim.keyframe_delete_v3d", {"type": 'I', "value": 'PRESS', "alt": True}, None),
("anim.keying_set_active_set", {"type": 'I', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
("anim.keying_set_active_set", {"type": 'K', "value": 'PRESS', "shift": True}, None),
("pose.push", {"type": 'E', "value": 'PRESS', "ctrl": True}, None),
("pose.relax", {"type": 'E', "value": 'PRESS', "alt": True}, None),
("pose.breakdown", {"type": 'E', "value": 'PRESS', "shift": True}, None),
@ -4861,6 +4871,16 @@ def km_pose(params):
*_template_items_context_menu("VIEW3D_MT_pose_context_menu", params.context_menu_event),
])
if params.use_pie_click_drag:
items.extend([
("anim.keyframe_insert", {"type": 'I', "value": 'CLICK'}, None),
op_menu_pie("ANIM_MT_keyframe_insert_pie", {"type": 'I', "value": 'CLICK_DRAG'}),
])
else:
items.extend([
("anim.keyframe_insert", {"type": 'I', "value": 'PRESS'}, None),
])
return keymap

View File

@ -11,6 +11,7 @@ if "bpy" in locals():
del reload
_modules = [
"anim",
"asset_shelf",
"node_add_menu",
"node_add_menu_compositor",

View File

@ -0,0 +1,31 @@
from bpy.types import Menu
class ANIM_MT_keyframe_insert_pie(Menu):
bl_label = "Keyframe Insert Pie"
def draw(self, context):
layout = self.layout
pie = layout.menu_pie()
prop = pie.operator("anim.keyframe_insert_by_name", text="Location")
prop.type = "Location"
prop = pie.operator("anim.keyframe_insert_by_name", text="Scale")
prop.type = "Scaling"
prop = pie.operator("anim.keyframe_insert_by_name", text="Available")
prop.type = "Available"
prop = pie.operator("anim.keyframe_insert_by_name", text="Rotation")
prop.type = "Rotation"
classes = (
ANIM_MT_keyframe_insert_pie,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)

View File

@ -29,7 +29,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 14
#define BLENDER_FILE_SUBVERSION 15
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@ -23,8 +23,7 @@
#include "BLT_translation.h"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
/* Allow using deprecated functionality for .blend file I/O. */
#define DNA_DEPRECATED_ALLOW

View File

@ -26,7 +26,7 @@
#include "DNA_listBase.h"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
#include "IMB_openexr.hh"
#include "GPU_texture.h"
@ -35,6 +35,8 @@
#include <cstring>
using blender::float4;
/* Statics */
static ListBase studiolights;
static int last_studiolight_id = 0;
@ -487,11 +489,11 @@ static void studiolight_create_matcap_specular_gputexture(StudioLight *sl)
sl->flag |= STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE;
}
static void studiolight_calculate_radiance(ImBuf *ibuf, float color[4], const float direction[3])
static float4 studiolight_calculate_radiance(ImBuf *ibuf, const float direction[3])
{
float uv[2];
direction_to_equirect(uv, direction);
nearest_interpolation_color_wrap(ibuf, nullptr, color, uv[0] * ibuf->x, uv[1] * ibuf->y);
return blender::imbuf::interpolate_nearest_fl(ibuf, uv[0] * ibuf->x, uv[1] * ibuf->y);
}
/*
@ -692,7 +694,7 @@ static void studiolight_radiance_preview(uint *icon_buffer, StudioLight *sl)
uint alphamask = alpha_circle_mask(dx, dy, 0.5f - texel_size[0], 0.5f);
if (alphamask != 0) {
float normal[3], direction[3], color[4];
float normal[3], direction[3];
const float incoming[3] = {0.0f, 0.0f, -1.0f};
sphere_normal_from_uv(normal, dx, dy);
reflect_v3_v3v3(direction, incoming, normal);
@ -700,7 +702,7 @@ static void studiolight_radiance_preview(uint *icon_buffer, StudioLight *sl)
SWAP(float, direction[1], direction[2]);
direction[1] = -direction[1];
studiolight_calculate_radiance(sl->equirect_radiance_buffer, color, direction);
float4 color = studiolight_calculate_radiance(sl->equirect_radiance_buffer, direction);
*pixel = rgb_to_cpack(linearrgb_to_srgb(color[0]),
linearrgb_to_srgb(color[1]),
@ -716,6 +718,8 @@ static void studiolight_radiance_preview(uint *icon_buffer, StudioLight *sl)
static void studiolight_matcap_preview(uint *icon_buffer, StudioLight *sl, bool flipped)
{
using namespace blender;
BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EXTERNAL_IMAGE_LOADED);
ImBuf *diffuse_buffer = sl->matcap_diffuse.ibuf;
@ -728,14 +732,12 @@ static void studiolight_matcap_preview(uint *icon_buffer, StudioLight *sl, bool
dx = 1.0f - dx;
}
float color[4];
float u = dx * diffuse_buffer->x - 1.0f;
float v = dy * diffuse_buffer->y - 1.0f;
nearest_interpolation_color(diffuse_buffer, nullptr, color, u, v);
float4 color = imbuf::interpolate_nearest_fl(diffuse_buffer, u, v);
if (specular_buffer) {
float specular[4];
nearest_interpolation_color(specular_buffer, nullptr, specular, u, v);
float4 specular = imbuf::interpolate_nearest_fl(specular_buffer, u, v);
add_v3_v3(color, specular);
}

View File

@ -32,6 +32,7 @@
#include "IMB_colormanagement.hh"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
#include "MEM_guardedalloc.h"
/* == Parameterization constants == */
@ -1286,34 +1287,79 @@ void BKE_tracking_stabilization_data_get(MovieClip *clip,
discard_stabilization_working_context(ctx);
}
using interpolation_func = void (*)(const ImBuf *, ImBuf *, float, float, int, int);
struct TrackingStabilizeFrameInterpolationData {
ImBuf *ibuf;
ImBuf *tmpibuf;
float (*mat)[4];
interpolation_func interpolation;
int tracking_filter;
};
static void tracking_stabilize_frame_interpolation_cb(void *__restrict userdata,
const int j,
const int y,
const TaskParallelTLS *__restrict /*tls*/)
{
using namespace blender;
TrackingStabilizeFrameInterpolationData *data =
static_cast<TrackingStabilizeFrameInterpolationData *>(userdata);
ImBuf *ibuf = data->ibuf;
ImBuf *tmpibuf = data->tmpibuf;
float(*mat)[4] = data->mat;
interpolation_func interpolation = data->interpolation;
float vec[3] = {0.0f, float(y), 0.0f};
float rvec[3];
for (int i = 0; i < tmpibuf->x; i++) {
float vec[3] = {float(i), float(j), 0.0f};
mul_v3_m4v3(vec, mat, vec);
interpolation(ibuf, tmpibuf, vec[0], vec[1], i, j);
if (ibuf->float_buffer.data) {
/* Float image. */
float4 *dst = reinterpret_cast<float4 *>(tmpibuf->float_buffer.data) + y * tmpibuf->x;
if (data->tracking_filter == TRACKING_FILTER_BILINEAR) {
for (int x = 0; x < tmpibuf->x; x++, dst++) {
vec[0] = float(x);
mul_v3_m4v3(rvec, mat, vec);
*dst = imbuf::interpolate_bilinear_fl(ibuf, rvec[0], rvec[1]);
}
}
else if (data->tracking_filter == TRACKING_FILTER_BICUBIC) {
for (int x = 0; x < tmpibuf->x; x++, dst++) {
vec[0] = float(x);
mul_v3_m4v3(rvec, mat, vec);
*dst = imbuf::interpolate_cubic_bspline_fl(ibuf, rvec[0], rvec[1]);
}
}
else {
/* Nearest or fallback to nearest. */
for (int x = 0; x < tmpibuf->x; x++, dst++) {
vec[0] = float(x);
mul_v3_m4v3(rvec, mat, vec);
*dst = imbuf::interpolate_nearest_fl(ibuf, rvec[0], rvec[1]);
}
}
}
else if (ibuf->byte_buffer.data) {
/* Byte image. */
uchar4 *dst = reinterpret_cast<uchar4 *>(tmpibuf->byte_buffer.data) + y * tmpibuf->x;
if (data->tracking_filter == TRACKING_FILTER_BILINEAR) {
for (int x = 0; x < tmpibuf->x; x++, dst++) {
vec[0] = float(x);
mul_v3_m4v3(rvec, mat, vec);
*dst = imbuf::interpolate_bilinear_byte(ibuf, rvec[0], rvec[1]);
}
}
else if (data->tracking_filter == TRACKING_FILTER_BICUBIC) {
for (int x = 0; x < tmpibuf->x; x++, dst++) {
vec[0] = float(x);
mul_v3_m4v3(rvec, mat, vec);
*dst = imbuf::interpolate_cubic_bspline_byte(ibuf, rvec[0], rvec[1]);
}
}
else {
/* Nearest or fallback to nearest. */
for (int x = 0; x < tmpibuf->x; x++, dst++) {
vec[0] = float(x);
mul_v3_m4v3(rvec, mat, vec);
*dst = imbuf::interpolate_nearest_byte(ibuf, rvec[0], rvec[1]);
}
}
}
}
@ -1327,8 +1373,6 @@ ImBuf *BKE_tracking_stabilize_frame(
int width = ibuf->x, height = ibuf->y;
float pixel_aspect = tracking->camera.pixel_aspect;
float mat[4][4];
int filter = tracking->stabilization.filter;
interpolation_func interpolation = nullptr;
int ibuf_flags;
if (translation) {
@ -1378,25 +1422,11 @@ ImBuf *BKE_tracking_stabilize_frame(
* thus we need the inverse of the transformation to apply. */
invert_m4(mat);
if (filter == TRACKING_FILTER_NEAREST) {
interpolation = nearest_interpolation;
}
else if (filter == TRACKING_FILTER_BILINEAR) {
interpolation = bilinear_interpolation;
}
else if (filter == TRACKING_FILTER_BICUBIC) {
interpolation = bicubic_interpolation;
}
else {
/* fallback to default interpolation method */
interpolation = nearest_interpolation;
}
TrackingStabilizeFrameInterpolationData data = {};
data.ibuf = ibuf;
data.tmpibuf = tmpibuf;
data.mat = mat;
data.interpolation = interpolation;
data.tracking_filter = tracking->stabilization.filter;
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);

View File

@ -289,18 +289,24 @@ MINLINE uint ceil_to_multiple_u(uint a, uint b);
MINLINE uint64_t ceil_to_multiple_ul(uint64_t a, uint64_t b);
/**
* modulo that handles negative numbers, works the same as Python's.
* Floored modulo that is useful for wrapping numbers over \a n,
* including when \a i is negative.
*
* This is the same as Python % or GLSL mod(): `mod_i(-5, 3) = 1`.
*
* \return an integer in the interval [0, n), same sign as n.
*/
MINLINE int mod_i(int i, int n);
/**
* Modulo that returns a positive result, regardless of the sign of \a f.
* Floored modulo that is useful for wrapping numbers over \a n,
* including when \a f is negative.
*
* For example, mod_f_positive(-0.1, 1.0) => 0.9.
* This is the same as Python % or GLSL mod(): `floored_fmod(-0.2, 1.0) = 0.8`.
*
* \returns a float in the interval [0, n).
* \return a float in the interval [0, n), same sign as n.
*/
MINLINE float mod_f_positive(float f, float n);
MINLINE float floored_fmod(float f, float n);
/**
* Round to closest even number, halfway cases are rounded away from zero.

View File

@ -6,33 +6,204 @@
/** \file
* \ingroup bli
*
* 2D image sampling with filtering functions.
*
* All functions take (u, v) texture coordinate, non-normalized (i.e. ranging
* from (0,0) to (width,height) over the image).
*
* Any filtering done on texel values just blends them without color space or
* gamma conversions.
*
* Sampling completely outside the image returns transparent black.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "BLI_math_base.h"
#include "BLI_math_vector_types.hh"
void BLI_bicubic_interpolation_fl(
namespace blender::math {
/**
* Nearest (point) sampling.
*
* Returns texel at floor(u,v) integer index -- note that it is not "nearest
* to u,v coordinate", but rather with fractional part truncated (it would be
* "nearest" if subtracting 0.5 from input u,v).
*/
inline void interpolate_nearest_byte(
const uchar *buffer, uchar *output, int width, int height, float u, float v)
{
BLI_assert(buffer);
int x = int(u);
int y = int(v);
/* Outside image? */
if (x < 0 || x >= width || y < 0 || y >= height) {
output[0] = output[1] = output[2] = output[3] = 0;
return;
}
const uchar *data = buffer + (int64_t(width) * y + x) * 4;
output[0] = data[0];
output[1] = data[1];
output[2] = data[2];
output[3] = data[3];
}
[[nodiscard]] inline uchar4 interpolate_nearest_byte(
const uchar *buffer, int width, int height, float u, float v)
{
uchar4 res;
interpolate_nearest_byte(buffer, res, width, height, u, v);
return res;
}
inline void interpolate_nearest_fl(
const float *buffer, float *output, int width, int height, int components, float u, float v)
{
BLI_assert(buffer);
int x = int(u);
int y = int(v);
/* Outside image? */
if (x < 0 || x >= width || y < 0 || y >= height) {
for (int i = 0; i < components; i++) {
output[i] = 0.0f;
}
return;
}
const float *data = buffer + (int64_t(width) * y + x) * components;
for (int i = 0; i < components; i++) {
output[i] = data[i];
}
}
[[nodiscard]] inline float4 interpolate_nearest_fl(
const float *buffer, int width, int height, float u, float v)
{
float4 res;
interpolate_nearest_fl(buffer, res, width, height, 4, u, v);
return res;
}
/**
* Wrapped nearest sampling. (u,v) is repeated to be inside the image size.
*/
inline void interpolate_nearest_wrap_byte(
const uchar *buffer, uchar *output, int width, int height, float u, float v)
{
BLI_assert(buffer);
u = floored_fmod(u, float(width));
v = floored_fmod(v, float(height));
int x = int(u);
int y = int(v);
BLI_assert(x >= 0 && y >= 0 && x < width && y < height);
const uchar *data = buffer + (int64_t(width) * y + x) * 4;
output[0] = data[0];
output[1] = data[1];
output[2] = data[2];
output[3] = data[3];
}
[[nodiscard]] inline uchar4 interpolate_nearest_wrap_byte(
const uchar *buffer, int width, int height, float u, float v)
{
uchar4 res;
interpolate_nearest_wrap_byte(buffer, res, width, height, u, v);
return res;
}
inline void interpolate_nearest_wrap_fl(
const float *buffer, float *output, int width, int height, int components, float u, float v)
{
BLI_assert(buffer);
u = floored_fmod(u, float(width));
v = floored_fmod(v, float(height));
int x = int(u);
int y = int(v);
BLI_assert(x >= 0 && y >= 0 && x < width && y < height);
const float *data = buffer + (int64_t(width) * y + x) * components;
for (int i = 0; i < components; i++) {
output[i] = data[i];
}
}
[[nodiscard]] inline float4 interpolate_nearest_wrap_fl(
const float *buffer, int width, int height, float u, float v)
{
float4 res;
interpolate_nearest_wrap_fl(buffer, res, width, height, 4, u, v);
return res;
}
/**
* Bilinear sampling.
*
* Takes four image samples at floor(u,v) and floor(u,v)+1, and blends them
* based on fractional parts of u,v. Samples outside the image are turned
* into transparent black.
*
* Note that you probably want to subtract 0.5 from u,v before this function,
* to get proper filtering.
*/
[[nodiscard]] uchar4 interpolate_bilinear_byte(
const uchar *buffer, int width, int height, float u, float v);
[[nodiscard]] float4 interpolate_bilinear_fl(
const float *buffer, int width, int height, float u, float v);
void interpolate_bilinear_fl(
const float *buffer, float *output, int width, int height, int components, float u, float v);
void BLI_bicubic_interpolation_char(
const unsigned char *buffer, unsigned char *output, int width, int height, float u, float v);
/**
* Wrapped bilinear sampling. (u,v) is repeated to be inside the image size,
* including properly wrapping samples that are right on the edges.
*/
void BLI_bilinear_interpolation_fl(
[[nodiscard]] uchar4 interpolate_bilinear_wrap_byte(
const uchar *buffer, int width, int height, float u, float v);
[[nodiscard]] float4 interpolate_bilinear_wrap_fl(
const float *buffer, int width, int height, float u, float v);
void interpolate_bilinear_wrap_fl(const float *buffer,
float *output,
int width,
int height,
int components,
float u,
float v,
bool wrap_x,
bool wrap_y);
/**
* Cubic B-Spline sampling.
*
* Takes 4x4 image samples at floor(u,v)-1 .. floor(u,v)+2, and blends them
* based on fractional parts of u,v. Uses B-Spline variant Mitchell-Netravali
* filter (B=1, C=0), which has no ringing but introduces quite a lot of blur.
* Samples outside the image are clamped to texels at image edge.
*
* Note that you probably want to subtract 0.5 from u,v before this function,
* to get proper filtering.
*/
[[nodiscard]] uchar4 interpolate_cubic_bspline_byte(
const uchar *buffer, int width, int height, float u, float v);
[[nodiscard]] float4 interpolate_cubic_bspline_fl(
const float *buffer, int width, int height, float u, float v);
void interpolate_cubic_bspline_fl(
const float *buffer, float *output, int width, int height, int components, float u, float v);
void BLI_bilinear_interpolation_char(
const unsigned char *buffer, unsigned char *output, int width, int height, float u, float v);
void BLI_bilinear_interpolation_wrap_fl(const float *buffer,
float *output,
int width,
int height,
int components,
float u,
float v,
bool wrap_x,
bool wrap_y);
} // namespace blender::math
#define EWA_MAXIDX 255
extern const float EWA_WTS[EWA_MAXIDX + 1];
@ -56,7 +227,3 @@ void BLI_ewa_filter(int width,
ewa_filter_read_pixel_cb read_pixel_cb,
void *userdata,
float result[4]);
#ifdef __cplusplus
}
#endif

View File

@ -310,14 +310,9 @@ MINLINE int mod_i(int i, int n)
return (i % n + n) % n;
}
MINLINE float mod_f_positive(const float f, const float n)
MINLINE float floored_fmod(const float f, const float n)
{
const float modulo = fmodf(f, n);
if (modulo < 0) {
/* fmodf returns a value in the interval (-n, n). */
return modulo + n;
}
return modulo;
return f - n * floorf(f / n);
}
MINLINE float fractf(float a)

View File

@ -117,6 +117,8 @@ static void bicubic_interpolation(
{
using namespace blender;
BLI_assert(src_buffer && output);
#if BLI_HAVE_SSE2
if constexpr (std::is_same_v<T, uchar>) {
if (components == 4) {
@ -203,33 +205,28 @@ static void bicubic_interpolation(
}
}
void BLI_bicubic_interpolation_fl(
const float *buffer, float *output, int width, int height, int components, float u, float v)
{
bicubic_interpolation<float>(buffer, output, width, height, components, u, v);
}
void BLI_bicubic_interpolation_char(
const uchar *buffer, uchar *output, int width, int height, float u, float v)
{
bicubic_interpolation<uchar>(buffer, output, width, height, 4, u, v);
}
/* BILINEAR INTERPOLATION */
BLI_INLINE void bilinear_interpolation_fl(const float *float_buffer,
float *float_output,
int width,
int height,
int components,
float u,
float v,
bool wrap_x,
bool wrap_y)
BLI_INLINE void bilinear_fl_impl(const float *buffer,
float *output,
int width,
int height,
int components,
float u,
float v,
bool wrap_x = false,
bool wrap_y = false)
{
BLI_assert(buffer && output);
float a, b;
float a_b, ma_b, a_mb, ma_mb;
int y1, y2, x1, x2;
if (wrap_x) {
u = floored_fmod(u, float(width));
}
if (wrap_y) {
v = floored_fmod(v, float(height));
}
float uf = floorf(u);
float vf = floorf(v);
@ -241,60 +238,54 @@ BLI_INLINE void bilinear_interpolation_fl(const float *float_buffer,
const float *row1, *row2, *row3, *row4;
const float empty[4] = {0.0f, 0.0f, 0.0f, 0.0f};
/* pixel value must be already wrapped, however values at boundaries may flip */
/* Check if +1 samples need wrapping, or we we don't do wrapping then if
* we are sampling completely outside the image. */
if (wrap_x) {
if (x1 < 0) {
x1 = width - 1;
}
if (x2 >= width) {
x2 = 0;
}
}
else if (x2 < 0 || x1 >= width) {
copy_vn_fl(float_output, components, 0.0f);
copy_vn_fl(output, components, 0.0f);
return;
}
if (wrap_y) {
if (y1 < 0) {
y1 = height - 1;
}
if (y2 >= height) {
y2 = 0;
}
}
else if (y2 < 0 || y1 >= height) {
copy_vn_fl(float_output, components, 0.0f);
copy_vn_fl(output, components, 0.0f);
return;
}
/* sample including outside of edges of image */
/* Sample including outside of edges of image. */
if (x1 < 0 || y1 < 0) {
row1 = empty;
}
else {
row1 = float_buffer + width * y1 * components + components * x1;
row1 = buffer + width * y1 * components + components * x1;
}
if (x1 < 0 || y2 > height - 1) {
row2 = empty;
}
else {
row2 = float_buffer + width * y2 * components + components * x1;
row2 = buffer + width * y2 * components + components * x1;
}
if (x2 > width - 1 || y1 < 0) {
row3 = empty;
}
else {
row3 = float_buffer + width * y1 * components + components * x2;
row3 = buffer + width * y1 * components + components * x2;
}
if (x2 > width - 1 || y2 > height - 1) {
row4 = empty;
}
else {
row4 = float_buffer + width * y2 * components + components * x2;
row4 = buffer + width * y2 * components + components * x2;
}
a = u - uf;
@ -305,12 +296,12 @@ BLI_INLINE void bilinear_interpolation_fl(const float *float_buffer,
ma_mb = (1.0f - a) * (1.0f - b);
if (components == 1) {
float_output[0] = ma_mb * row1[0] + a_mb * row3[0] + ma_b * row2[0] + a_b * row4[0];
output[0] = ma_mb * row1[0] + a_mb * row3[0] + ma_b * row2[0] + a_b * row4[0];
}
else if (components == 3) {
float_output[0] = ma_mb * row1[0] + a_mb * row3[0] + ma_b * row2[0] + a_b * row4[0];
float_output[1] = ma_mb * row1[1] + a_mb * row3[1] + ma_b * row2[1] + a_b * row4[1];
float_output[2] = ma_mb * row1[2] + a_mb * row3[2] + ma_b * row2[2] + a_b * row4[2];
output[0] = ma_mb * row1[0] + a_mb * row3[0] + ma_b * row2[0] + a_b * row4[0];
output[1] = ma_mb * row1[1] + a_mb * row3[1] + ma_b * row2[1] + a_b * row4[1];
output[2] = ma_mb * row1[2] + a_mb * row3[2] + ma_b * row2[2] + a_b * row4[2];
}
else {
#if BLI_HAVE_SSE2
@ -325,19 +316,23 @@ BLI_INLINE void bilinear_interpolation_fl(const float *float_buffer,
__m128 rgba13 = _mm_add_ps(rgba1, rgba3);
__m128 rgba24 = _mm_add_ps(rgba2, rgba4);
__m128 rgba = _mm_add_ps(rgba13, rgba24);
_mm_storeu_ps(float_output, rgba);
_mm_storeu_ps(output, rgba);
#else
float_output[0] = ma_mb * row1[0] + a_mb * row3[0] + ma_b * row2[0] + a_b * row4[0];
float_output[1] = ma_mb * row1[1] + a_mb * row3[1] + ma_b * row2[1] + a_b * row4[1];
float_output[2] = ma_mb * row1[2] + a_mb * row3[2] + ma_b * row2[2] + a_b * row4[2];
float_output[3] = ma_mb * row1[3] + a_mb * row3[3] + ma_b * row2[3] + a_b * row4[3];
output[0] = ma_mb * row1[0] + a_mb * row3[0] + ma_b * row2[0] + a_b * row4[0];
output[1] = ma_mb * row1[1] + a_mb * row3[1] + ma_b * row2[1] + a_b * row4[1];
output[2] = ma_mb * row1[2] + a_mb * row3[2] + ma_b * row2[2] + a_b * row4[2];
output[3] = ma_mb * row1[3] + a_mb * row3[3] + ma_b * row2[3] + a_b * row4[3];
#endif
}
}
void BLI_bilinear_interpolation_char(
const uchar *buffer, uchar *output, int width, int height, float u, float v)
namespace blender::math {
uchar4 interpolate_bilinear_byte(const uchar *buffer, int width, int height, float u, float v)
{
BLI_assert(buffer);
uchar4 res;
#if BLI_HAVE_SSE2
/* Bilinear interpolation needs to read and blend four image pixels, while
* also handling conditions of sample coordinate being outside of the
@ -375,7 +370,7 @@ void BLI_bilinear_interpolation_char(
int ycoord[4];
_mm_storeu_ps((float *)xcoord, _mm_castsi128_ps(x1234));
_mm_storeu_ps((float *)ycoord, _mm_castsi128_ps(y1234));
int sample1 = ((const int *)buffer)[ycoord[0] * (int64_t)width + xcoord[0]];
int sample1 = ((const int *)buffer)[ycoord[0] * int64_t(width) + xcoord[0]];
int sample2 = ((const int *)buffer)[ycoord[1] * int64_t(width) + xcoord[1]];
int sample3 = ((const int *)buffer)[ycoord[2] * int64_t(width) + xcoord[2]];
int sample4 = ((const int *)buffer)[ycoord[3] * int64_t(width) + xcoord[3]];
@ -414,37 +409,26 @@ void BLI_bilinear_interpolation_char(
__m128i rgba32 = _mm_cvttps_epi32(rgba);
__m128i rgba16 = _mm_packs_epi32(rgba32, _mm_setzero_si128());
__m128i rgba8 = _mm_packus_epi16(rgba16, _mm_setzero_si128());
_mm_store_ss((float *)output, _mm_castsi128_ps(rgba8));
_mm_store_ss((float *)&res, _mm_castsi128_ps(rgba8));
#else
float a, b;
float a_b, ma_b, a_mb, ma_mb;
int y1, y2, x1, x2;
float uf = floorf(u);
float vf = floorf(v);
x1 = (int)uf;
x2 = x1 + 1;
y1 = (int)vf;
y2 = y1 + 1;
int x1 = (int)uf;
int x2 = x1 + 1;
int y1 = (int)vf;
int y2 = y1 + 1;
/* Completely outside of the image? */
if (x2 < 0 || x1 >= width || y2 < 0 || y1 >= height) {
return uchar4(0);
}
/* Sample including outside of edges of image. */
const uchar *row1, *row2, *row3, *row4;
uchar empty[4] = {0, 0, 0, 0};
/* completely outside of the image? */
if (x2 < 0 || x1 >= width) {
copy_vn_uchar(output, 4, 0);
return;
}
if (y2 < 0 || y1 >= height) {
copy_vn_uchar(output, 4, 0);
return;
}
/* sample including outside of edges of image */
if (x1 < 0 || y1 < 0) {
row1 = empty;
}
@ -473,39 +457,119 @@ void BLI_bilinear_interpolation_char(
row4 = buffer + width * y2 * 4 + 4 * x2;
}
a = u - uf;
b = v - vf;
a_b = a * b;
ma_b = (1.0f - a) * b;
a_mb = a * (1.0f - b);
ma_mb = (1.0f - a) * (1.0f - b);
float a = u - uf;
float b = v - vf;
float a_b = a * b;
float ma_b = (1.0f - a) * b;
float a_mb = a * (1.0f - b);
float ma_mb = (1.0f - a) * (1.0f - b);
output[0] = (uchar)(ma_mb * row1[0] + a_mb * row3[0] + ma_b * row2[0] + a_b * row4[0] + 0.5f);
output[1] = (uchar)(ma_mb * row1[1] + a_mb * row3[1] + ma_b * row2[1] + a_b * row4[1] + 0.5f);
output[2] = (uchar)(ma_mb * row1[2] + a_mb * row3[2] + ma_b * row2[2] + a_b * row4[2] + 0.5f);
output[3] = (uchar)(ma_mb * row1[3] + a_mb * row3[3] + ma_b * row2[3] + a_b * row4[3] + 0.5f);
res.x = (uchar)(ma_mb * row1[0] + a_mb * row3[0] + ma_b * row2[0] + a_b * row4[0] + 0.5f);
res.y = (uchar)(ma_mb * row1[1] + a_mb * row3[1] + ma_b * row2[1] + a_b * row4[1] + 0.5f);
res.z = (uchar)(ma_mb * row1[2] + a_mb * row3[2] + ma_b * row2[2] + a_b * row4[2] + 0.5f);
res.w = (uchar)(ma_mb * row1[3] + a_mb * row3[3] + ma_b * row2[3] + a_b * row4[3] + 0.5f);
#endif
return res;
}
void BLI_bilinear_interpolation_fl(
float4 interpolate_bilinear_fl(const float *buffer, int width, int height, float u, float v)
{
float4 res;
bilinear_fl_impl(buffer, res, width, height, 4, u, v);
return res;
}
void interpolate_bilinear_fl(
const float *buffer, float *output, int width, int height, int components, float u, float v)
{
bilinear_interpolation_fl(buffer, output, width, height, components, u, v, false, false);
bilinear_fl_impl(buffer, output, width, height, components, u, v);
}
void BLI_bilinear_interpolation_wrap_fl(const float *buffer,
float *output,
int width,
int height,
int components,
float u,
float v,
bool wrap_x,
bool wrap_y)
void interpolate_bilinear_wrap_fl(const float *buffer,
float *output,
int width,
int height,
int components,
float u,
float v,
bool wrap_x,
bool wrap_y)
{
bilinear_interpolation_fl(buffer, output, width, height, components, u, v, wrap_x, wrap_y);
bilinear_fl_impl(buffer, output, width, height, components, u, v, wrap_x, wrap_y);
}
uchar4 interpolate_bilinear_wrap_byte(const uchar *buffer, int width, int height, float u, float v)
{
u = floored_fmod(u, float(width));
v = floored_fmod(v, float(height));
float uf = floorf(u);
float vf = floorf(v);
int x1 = (int)uf;
int x2 = x1 + 1;
int y1 = (int)vf;
int y2 = y1 + 1;
/* Wrap interpolation pixels if needed. */
BLI_assert(x1 >= 0 && x1 < width && y1 >= 0 && y1 < height);
if (x2 >= width) {
x2 = 0;
}
if (y2 >= height) {
y2 = 0;
}
float a = u - uf;
float b = v - vf;
float a_b = a * b;
float ma_b = (1.0f - a) * b;
float a_mb = a * (1.0f - b);
float ma_mb = (1.0f - a) * (1.0f - b);
/* Blend samples. */
const uchar *row1 = buffer + (int64_t(width) * y1 + x1) * 4;
const uchar *row2 = buffer + (int64_t(width) * y2 + x1) * 4;
const uchar *row3 = buffer + (int64_t(width) * y1 + x2) * 4;
const uchar *row4 = buffer + (int64_t(width) * y2 + x2) * 4;
uchar4 res;
res.x = uchar(ma_mb * row1[0] + a_mb * row3[0] + ma_b * row2[0] + a_b * row4[0] + 0.5f);
res.y = uchar(ma_mb * row1[1] + a_mb * row3[1] + ma_b * row2[1] + a_b * row4[1] + 0.5f);
res.z = uchar(ma_mb * row1[2] + a_mb * row3[2] + ma_b * row2[2] + a_b * row4[2] + 0.5f);
res.w = uchar(ma_mb * row1[3] + a_mb * row3[3] + ma_b * row2[3] + a_b * row4[3] + 0.5f);
return res;
}
float4 interpolate_bilinear_wrap_fl(const float *buffer, int width, int height, float u, float v)
{
float4 res;
bilinear_fl_impl(buffer, res, width, height, 4, u, v, true, true);
return res;
}
uchar4 interpolate_cubic_bspline_byte(const uchar *buffer, int width, int height, float u, float v)
{
uchar4 res;
bicubic_interpolation<uchar>(buffer, res, width, height, 4, u, v);
return res;
}
float4 interpolate_cubic_bspline_fl(const float *buffer, int width, int height, float u, float v)
{
float4 res;
bicubic_interpolation<float>(buffer, res, width, height, 4, u, v);
return res;
}
void interpolate_cubic_bspline_fl(
const float *buffer, float *output, int width, int height, int components, float u, float v)
{
bicubic_interpolation<float>(buffer, output, width, height, components, u, v);
}
} // namespace blender::math
/**************************************************************************
* Filtering method based on
* "Creating raster omnimax images from multiple perspective views

View File

@ -183,11 +183,17 @@ TEST(math_base, InterpolateInt)
EXPECT_EQ(math::interpolate(100, 200, 0.4f), 140);
}
TEST(math_base, ModFPositive)
TEST(math_base, FlooredFMod)
{
EXPECT_FLOAT_EQ(mod_f_positive(3.27f, 1.57f), 0.12999988f);
EXPECT_FLOAT_EQ(mod_f_positive(327.f, 47.f), 45.f);
EXPECT_FLOAT_EQ(mod_f_positive(-0.1f, 1.0f), 0.9f);
EXPECT_FLOAT_EQ(floored_fmod(3.27f, 1.57f), 0.12999988f);
EXPECT_FLOAT_EQ(floored_fmod(327.f, 47.f), 45.f);
EXPECT_FLOAT_EQ(floored_fmod(-0.1f, 1.0f), 0.9f);
EXPECT_FLOAT_EQ(floored_fmod(-0.9f, 1.0f), 0.1f);
EXPECT_FLOAT_EQ(floored_fmod(-100.1f, 1.0f), 0.90000153f);
EXPECT_FLOAT_EQ(floored_fmod(-0.1f, 12345.0f), 12344.9f);
EXPECT_FLOAT_EQ(floored_fmod(12345.1f, 12345.0f), 0.099609375f);
EXPECT_FLOAT_EQ(floored_fmod(12344.999f, 12345.0f), 12344.999f);
EXPECT_FLOAT_EQ(floored_fmod(12345.0f, 12345.0f), 0.0f);
}
} // namespace blender::tests

View File

@ -8,7 +8,9 @@
#include "BLI_math_interp.hh"
using namespace blender;
using namespace blender::math;
static constexpr float float_tolerance = 0.00005f;
static constexpr int image_width = 3;
static constexpr int image_height = 3;
static constexpr unsigned char image_char[image_height][image_width][4] = {
@ -16,147 +18,241 @@ static constexpr unsigned char image_char[image_height][image_width][4] = {
{{0, 1, 2, 3}, {62, 72, 82, 92}, {126, 127, 128, 129}},
{{1, 2, 3, 4}, {73, 108, 153, 251}, {128, 129, 130, 131}},
};
static constexpr float image_fl[image_height][image_width][4] = {
{{255, 254, 217, 216}, {230, 230, 230, 230}, {240, 160, 90, 20}},
{{0, 1, 2, 3}, {62, 72, 82, 92}, {126, 127, 128, 129}},
{{1, 2, 3, 4}, {73, 108, 153, 251}, {128, 129, 130, 131}},
};
TEST(math_interp, BilinearCharExactSamples)
{
unsigned char res[4];
unsigned char exp1[4] = {73, 108, 153, 251};
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 1.0f, 2.0f);
EXPECT_EQ_ARRAY(exp1, res, 4);
unsigned char exp2[4] = {240, 160, 90, 20};
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 2.0f, 0.0f);
EXPECT_EQ_ARRAY(exp2, res, 4);
uchar4 res;
uchar4 exp1 = {73, 108, 153, 251};
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 1.0f, 2.0f);
EXPECT_EQ(exp1, res);
uchar4 exp2 = {240, 160, 90, 20};
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 2.0f, 0.0f);
EXPECT_EQ(exp2, res);
}
TEST(math_interp, BilinearCharHalfwayUSamples)
{
unsigned char res[4];
unsigned char exp1[4] = {31, 37, 42, 48};
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 0.5f, 1.0f);
EXPECT_EQ_ARRAY(exp1, res, 4);
unsigned char exp2[4] = {243, 242, 224, 223};
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 0.5f, 0.0f);
EXPECT_EQ_ARRAY(exp2, res, 4);
uchar4 res;
uchar4 exp1 = {31, 37, 42, 48};
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 0.5f, 1.0f);
EXPECT_EQ(exp1, res);
uchar4 exp2 = {243, 242, 224, 223};
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 0.5f, 0.0f);
EXPECT_EQ(exp2, res);
}
TEST(math_interp, BilinearCharHalfwayVSamples)
{
unsigned char res[4];
unsigned char exp1[4] = {1, 2, 3, 4};
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 0.0f, 1.5f);
EXPECT_EQ_ARRAY(exp1, res, 4);
unsigned char exp2[4] = {127, 128, 129, 130};
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 2.0f, 1.5f);
EXPECT_EQ_ARRAY(exp2, res, 4);
uchar4 res;
uchar4 exp1 = {1, 2, 3, 4};
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 0.0f, 1.5f);
EXPECT_EQ(exp1, res);
uchar4 exp2 = {127, 128, 129, 130};
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 2.0f, 1.5f);
EXPECT_EQ(exp2, res);
}
TEST(math_interp, BilinearCharSamples)
{
unsigned char res[4];
unsigned char exp1[4] = {136, 133, 132, 130};
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 1.25f, 0.625f);
EXPECT_EQ_ARRAY(exp1, res, 4);
unsigned char exp2[4] = {219, 191, 167, 142};
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 1.4f, 0.1f);
EXPECT_EQ_ARRAY(exp2, res, 4);
uchar4 res;
uchar4 exp1 = {136, 133, 132, 130};
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 1.25f, 0.625f);
EXPECT_EQ(exp1, res);
uchar4 exp2 = {219, 191, 167, 142};
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 1.4f, 0.1f);
EXPECT_EQ(exp2, res);
}
TEST(math_interp, BilinearFloatSamples)
{
float4 res;
float4 exp1 = {135.9375f, 133.28125f, 131.5625f, 129.84375f};
res = interpolate_bilinear_fl(image_fl[0][0], image_width, image_height, 1.25f, 0.625f);
EXPECT_V4_NEAR(exp1, res, float_tolerance);
float4 exp2 = {219.36f, 191.2f, 166.64f, 142.08f};
res = interpolate_bilinear_fl(image_fl[0][0], image_width, image_height, 1.4f, 0.1f);
EXPECT_V4_NEAR(exp2, res, float_tolerance);
}
TEST(math_interp, BilinearCharPartiallyOutsideImage)
{
unsigned char res[4];
unsigned char exp1[4] = {1, 1, 2, 2};
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, -0.5f, 2.0f);
EXPECT_EQ_ARRAY(exp1, res, 4);
unsigned char exp2[4] = {9, 11, 15, 22};
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 1.25f, 2.9f);
EXPECT_EQ_ARRAY(exp2, res, 4);
unsigned char exp3[4] = {173, 115, 65, 14};
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 2.2f, -0.1f);
EXPECT_EQ_ARRAY(exp3, res, 4);
uchar4 res;
uchar4 exp1 = {1, 1, 2, 2};
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, -0.5f, 2.0f);
EXPECT_EQ(exp1, res);
uchar4 exp2 = {9, 11, 15, 22};
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 1.25f, 2.9f);
EXPECT_EQ(exp2, res);
uchar4 exp3 = {173, 115, 65, 14};
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 2.2f, -0.1f);
EXPECT_EQ(exp3, res);
}
TEST(math_interp, BilinearCharPartiallyOutsideImageWrap)
{
uchar4 res;
uchar4 exp1 = {65, 66, 67, 68};
res = interpolate_bilinear_wrap_byte(image_char[0][0], image_width, image_height, -0.5f, 2.0f);
EXPECT_EQ(exp1, res);
uchar4 exp2 = {218, 203, 190, 182};
res = interpolate_bilinear_wrap_byte(image_char[0][0], image_width, image_height, 1.25f, 2.9f);
EXPECT_EQ(exp2, res);
uchar4 exp3 = {229, 171, 114, 64};
res = interpolate_bilinear_wrap_byte(image_char[0][0], image_width, image_height, 2.2f, -0.1f);
EXPECT_EQ(exp3, res);
}
TEST(math_interp, BilinearFloatPartiallyOutsideImage)
{
float4 res;
float4 exp1 = {0.5f, 1, 1.5f, 2};
res = interpolate_bilinear_fl(image_fl[0][0], image_width, image_height, -0.5f, 2.0f);
EXPECT_V4_NEAR(exp1, res, float_tolerance);
float4 exp2 = {8.675f, 11.325f, 14.725f, 22.1f};
res = interpolate_bilinear_fl(image_fl[0][0], image_width, image_height, 1.25f, 2.9f);
EXPECT_V4_NEAR(exp2, res, float_tolerance);
float4 exp3 = {172.8f, 115.2f, 64.8f, 14.4f};
res = interpolate_bilinear_fl(image_fl[0][0], image_width, image_height, 2.2f, -0.1f);
EXPECT_V4_NEAR(exp3, res, float_tolerance);
}
TEST(math_interp, BilinearFloatPartiallyOutsideImageWrap)
{
float4 res;
float4 exp1 = {64.5f, 65.5f, 66.5f, 67.5f};
interpolate_bilinear_wrap_fl(
image_fl[0][0], res, image_width, image_height, 4, -0.5f, 2.0f, true, true);
EXPECT_V4_NEAR(exp1, res, float_tolerance);
res = interpolate_bilinear_wrap_fl(image_fl[0][0], image_width, image_height, -0.5f, 2.0f);
EXPECT_V4_NEAR(exp1, res, float_tolerance);
float4 exp2 = {217.92502f, 202.57501f, 190.22501f, 181.85f};
interpolate_bilinear_wrap_fl(
image_fl[0][0], res, image_width, image_height, 4, 1.25f, 2.9f, true, true);
EXPECT_V4_NEAR(exp2, res, float_tolerance);
res = interpolate_bilinear_wrap_fl(image_fl[0][0], image_width, image_height, 1.25f, 2.9f);
EXPECT_V4_NEAR(exp2, res, float_tolerance);
float4 exp3 = {228.96f, 171.27998f, 114.32f, 63.84f};
interpolate_bilinear_wrap_fl(
image_fl[0][0], res, image_width, image_height, 4, 2.2f, -0.1f, true, true);
EXPECT_V4_NEAR(exp3, res, float_tolerance);
res = interpolate_bilinear_wrap_fl(image_fl[0][0], image_width, image_height, 2.2f, -0.1f);
EXPECT_V4_NEAR(exp3, res, float_tolerance);
}
TEST(math_interp, BilinearCharFullyOutsideImage)
{
unsigned char res[4];
unsigned char exp[4] = {0, 0, 0, 0};
uchar4 res;
uchar4 exp = {0, 0, 0, 0};
/* Out of range on U */
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, -1.5f, 0);
EXPECT_EQ_ARRAY(exp, res, 4);
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, -1.1f, 0);
EXPECT_EQ_ARRAY(exp, res, 4);
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 3, 0);
EXPECT_EQ_ARRAY(exp, res, 4);
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 5, 0);
EXPECT_EQ_ARRAY(exp, res, 4);
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, -1.5f, 0);
EXPECT_EQ(exp, res);
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, -1.1f, 0);
EXPECT_EQ(exp, res);
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 3, 0);
EXPECT_EQ(exp, res);
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 5, 0);
EXPECT_EQ(exp, res);
/* Out of range on V */
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 0, -3.2f);
EXPECT_EQ_ARRAY(exp, res, 4);
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 0, -1.5f);
EXPECT_EQ_ARRAY(exp, res, 4);
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 0, 3.1f);
EXPECT_EQ_ARRAY(exp, res, 4);
BLI_bilinear_interpolation_char(image_char[0][0], res, image_width, image_height, 0, 500.0f);
EXPECT_EQ_ARRAY(exp, res, 4);
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 0, -3.2f);
EXPECT_EQ(exp, res);
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 0, -1.5f);
EXPECT_EQ(exp, res);
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 0, 3.1f);
EXPECT_EQ(exp, res);
res = interpolate_bilinear_byte(image_char[0][0], image_width, image_height, 0, 500.0f);
EXPECT_EQ(exp, res);
}
TEST(math_interp, BicubicCharExactSamples)
TEST(math_interp, CubicBSplineCharExactSamples)
{
ColorTheme4b res;
ColorTheme4b exp1 = {69, 90, 116, 172};
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, 1.0f, 2.0f);
uchar4 res;
uchar4 exp1 = {69, 90, 116, 172};
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, 1.0f, 2.0f);
EXPECT_EQ(exp1, res);
ColorTheme4b exp2 = {218, 163, 115, 66};
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, 2.0f, 0.0f);
uchar4 exp2 = {218, 163, 115, 66};
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, 2.0f, 0.0f);
EXPECT_EQ(exp2, res);
}
TEST(math_interp, BicubicCharSamples)
TEST(math_interp, CubicBSplineCharSamples)
{
ColorTheme4b res;
ColorTheme4b exp1 = {142, 136, 131, 128};
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, 1.25f, 0.625f);
uchar4 res;
uchar4 exp1 = {142, 136, 131, 128};
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, 1.25f, 0.625f);
EXPECT_EQ(exp1, res);
ColorTheme4b exp2 = {202, 177, 154, 132};
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, 1.4f, 0.1f);
uchar4 exp2 = {202, 177, 154, 132};
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, 1.4f, 0.1f);
EXPECT_EQ(exp2, res);
}
TEST(math_interp, BicubicCharPartiallyOutsideImage)
TEST(math_interp, CubicBSplineFloatSamples)
{
ColorTheme4b res;
ColorTheme4b exp1 = {2, 4, 6, 8};
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, -0.5f, 2.0f);
float4 res;
float4 exp1 = {142.14418f, 136.255798f, 130.87924f, 127.85243f};
res = interpolate_cubic_bspline_fl(image_fl[0][0], image_width, image_height, 1.25f, 0.625f);
EXPECT_V4_NEAR(exp1, res, float_tolerance);
float4 exp2 = {202.36082f, 177.13397f, 154.21078f, 132.30153f};
res = interpolate_cubic_bspline_fl(image_fl[0][0], image_width, image_height, 1.4f, 0.1f);
EXPECT_V4_NEAR(exp2, res, float_tolerance);
}
TEST(math_interp, CubicBSplineCharPartiallyOutsideImage)
{
uchar4 res;
uchar4 exp1 = {2, 4, 6, 8};
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, -0.5f, 2.0f);
EXPECT_EQ(exp1, res);
ColorTheme4b exp2 = {85, 107, 135, 195};
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, 1.25f, 2.9f);
uchar4 exp2 = {85, 107, 135, 195};
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, 1.25f, 2.9f);
EXPECT_EQ(exp2, res);
ColorTheme4b exp3 = {225, 161, 105, 49};
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, 2.2f, -0.1f);
uchar4 exp3 = {225, 161, 105, 49};
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, 2.2f, -0.1f);
EXPECT_EQ(exp3, res);
}
TEST(math_interp, BicubicCharFullyOutsideImage)
TEST(math_interp, CubicBSplineFloatPartiallyOutsideImage)
{
ColorTheme4b res;
ColorTheme4b exp = {0, 0, 0, 0};
float4 res;
float4 exp1 = {2.29861f, 3.92014f, 5.71528f, 8.430554f};
res = interpolate_cubic_bspline_fl(image_fl[0][0], image_width, image_height, -0.5f, 2.0f);
EXPECT_V4_NEAR(exp1, res, float_tolerance);
float4 exp2 = {85.41022f, 107.21497f, 135.13849f, 195.49146f};
res = interpolate_cubic_bspline_fl(image_fl[0][0], image_width, image_height, 1.25f, 2.9f);
EXPECT_V4_NEAR(exp2, res, float_tolerance);
float4 exp3 = {224.73579f, 160.66783f, 104.63521f, 48.60260f};
res = interpolate_cubic_bspline_fl(image_fl[0][0], image_width, image_height, 2.2f, -0.1f);
EXPECT_V4_NEAR(exp3, res, float_tolerance);
}
TEST(math_interp, CubicBSplineCharFullyOutsideImage)
{
uchar4 res;
uchar4 exp = {0, 0, 0, 0};
/* Out of range on U */
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, -1.5f, 0);
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, -1.5f, 0);
EXPECT_EQ(exp, res);
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, -1.1f, 0);
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, -1.1f, 0);
EXPECT_EQ(exp, res);
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, 3, 0);
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, 3, 0);
EXPECT_EQ(exp, res);
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, 5, 0);
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, 5, 0);
EXPECT_EQ(exp, res);
/* Out of range on V */
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, 0, -3.2f);
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, 0, -3.2f);
EXPECT_EQ(exp, res);
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, 0, -1.5f);
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, 0, -1.5f);
EXPECT_EQ(exp, res);
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, 0, 3.1f);
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, 0, 3.1f);
EXPECT_EQ(exp, res);
BLI_bicubic_interpolation_char(image_char[0][0], res, image_width, image_height, 0, 500.0f);
res = interpolate_cubic_bspline_byte(image_char[0][0], image_width, image_height, 0, 500.0f);
EXPECT_EQ(exp, res);
}

View File

@ -2714,6 +2714,20 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 401, 15)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_COMPOSIT) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == CMP_NODE_KEYING) {
NodeKeyingData &keying_data = *static_cast<NodeKeyingData *>(node->storage);
keying_data.edge_kernel_radius = max_ii(keying_data.edge_kernel_radius - 1, 0);
}
}
}
}
FOREACH_NODETREE_END;
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View File

@ -249,11 +249,11 @@ class MemoryBuffer {
single_y = rel_y - last_y;
}
BLI_bilinear_interpolation_fl(buffer_, out, 1, 1, num_channels_, single_x, single_y);
math::interpolate_bilinear_fl(buffer_, out, 1, 1, num_channels_, single_x, single_y);
return;
}
BLI_bilinear_interpolation_fl(buffer_,
math::interpolate_bilinear_fl(buffer_,
out,
get_width(),
get_height(),
@ -451,10 +451,7 @@ class MemoryBuffer {
}
break;
case MemoryBufferExtend::Repeat:
x = fmodf(x, w);
if (x < 0.0f) {
x += w;
}
x = floored_fmod(x, w);
break;
}
@ -470,10 +467,7 @@ class MemoryBuffer {
}
break;
case MemoryBufferExtend::Repeat:
y = fmodf(y, h);
if (y < 0.0f) {
y += h;
}
y = floored_fmod(y, h);
break;
}
@ -547,7 +541,7 @@ class MemoryBuffer {
memcpy(result, buffer_, sizeof(float) * num_channels_);
}
else {
BLI_bilinear_interpolation_wrap_fl(buffer_,
math::interpolate_bilinear_wrap_fl(buffer_,
result,
get_width(),
get_height(),

View File

@ -7,8 +7,7 @@
#include "BKE_scene.h"
#include "IMB_colormanagement.hh"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
namespace blender::compositor {
@ -102,27 +101,27 @@ static void sample_image_at_location(ImBuf *ibuf,
if (ibuf->float_buffer.data) {
switch (sampler) {
case PixelSampler::Nearest:
nearest_interpolation_color(ibuf, nullptr, color, x, y);
imbuf::interpolate_nearest_fl(ibuf, color, x, y);
break;
case PixelSampler::Bilinear:
bilinear_interpolation_color(ibuf, nullptr, color, x, y);
imbuf::interpolate_bilinear_fl(ibuf, color, x, y);
break;
case PixelSampler::Bicubic:
bicubic_interpolation_color(ibuf, nullptr, color, x, y);
imbuf::interpolate_cubic_bspline_fl(ibuf, color, x, y);
break;
}
}
else {
uchar byte_color[4];
uchar4 byte_color;
switch (sampler) {
case PixelSampler::Nearest:
nearest_interpolation_color(ibuf, byte_color, nullptr, x, y);
byte_color = imbuf::interpolate_nearest_byte(ibuf, x, y);
break;
case PixelSampler::Bilinear:
bilinear_interpolation_color(ibuf, byte_color, nullptr, x, y);
byte_color = imbuf::interpolate_bilinear_byte(ibuf, x, y);
break;
case PixelSampler::Bicubic:
bicubic_interpolation_color(ibuf, byte_color, nullptr, x, y);
byte_color = imbuf::interpolate_cubic_bspline_byte(ibuf, x, y);
break;
}
rgba_uchar_to_float(color, byte_color);

View File

@ -44,11 +44,13 @@ void KeyingClipOperation::execute_pixel(float output[4], int x, int y, void *dat
float value = buffer[(y * buffer_width + x)];
bool ok = false;
int start_x = max_ff(0, x - delta + 1), start_y = max_ff(0, y - delta + 1),
end_x = min_ff(x + delta - 1, buffer_width - 1),
end_y = min_ff(y + delta - 1, buffer_height - 1);
int count = 0, total_count = (end_x - start_x + 1) * (end_y - start_y + 1) - 1;
const int start_x = max_ff(0, x - delta);
const int start_y = max_ff(0, y - delta);
const int end_x = min_ff(x + delta, buffer_width - 1);
const int end_y = min_ff(y + delta, buffer_height - 1);
int count = 0, total_count = (end_x - start_x + 1) * (end_y - start_y + 1);
int threshold_count = ceil(float(total_count) * 0.9f);
if (delta == 0) {
@ -57,10 +59,6 @@ void KeyingClipOperation::execute_pixel(float output[4], int x, int y, void *dat
for (int cx = start_x; ok == false && cx <= end_x; cx++) {
for (int cy = start_y; ok == false && cy <= end_y; cy++) {
if (UNLIKELY(cx == x && cy == y)) {
continue;
}
int buffer_index = (cy * buffer_width + cx);
float current_value = buffer[buffer_index];
@ -141,14 +139,14 @@ void KeyingClipOperation::update_memory_buffer_partial(MemoryBuffer *output,
const int x = it.x;
const int y = it.y;
const int start_x = std::max(0, x - delta + 1);
const int start_y = std::max(0, y - delta + 1);
const int end_x = std::min(x + delta, width);
const int end_y = std::min(y + delta, height);
const int x_len = end_x - start_x;
const int y_len = end_y - start_y;
const int start_x = std::max(0, x - delta);
const int start_y = std::max(0, y - delta);
const int end_x = std::min(x + delta, width - 1);
const int end_y = std::min(y + delta, height - 1);
const int x_len = end_x - start_x + 1;
const int y_len = end_y - start_y + 1;
const int total_count = x_len * y_len - 1;
const int total_count = x_len * y_len;
const int threshold_count = ceil(float(total_count) * 0.9f);
bool ok = false;
if (delta == 0) {
@ -163,10 +161,6 @@ void KeyingClipOperation::update_memory_buffer_partial(MemoryBuffer *output,
for (; ok == false && row < end_row; row += row_stride) {
const float *end_elem = row + x_len * elem_stride;
for (const float *elem = row; ok == false && elem < end_elem; elem += elem_stride) {
if (UNLIKELY(elem == main_elem)) {
continue;
}
const float current_value = *elem;
if (fabsf(current_value - value) < tolerance) {
count++;

View File

@ -8,6 +8,7 @@
#include "BKE_movieclip.h"
#include "IMB_imbuf.hh"
#include "IMB_interp.hh"
namespace blender::compositor {
@ -81,13 +82,13 @@ void MovieClipBaseOperation::execute_pixel_sampled(float output[4],
else {
switch (sampler) {
case PixelSampler::Nearest:
nearest_interpolation_color(ibuf, nullptr, output, x, y);
imbuf::interpolate_nearest_fl(ibuf, output, x, y);
break;
case PixelSampler::Bilinear:
bilinear_interpolation_color(ibuf, nullptr, output, x, y);
imbuf::interpolate_bilinear_fl(ibuf, output, x, y);
break;
case PixelSampler::Bicubic:
bicubic_interpolation_color(ibuf, nullptr, output, x, y);
imbuf::interpolate_cubic_bspline_fl(ibuf, output, x, y);
break;
}
}

View File

@ -6,7 +6,7 @@
#include "BLI_string.h"
#include "IMB_imbuf.hh"
#include "IMB_interp.hh"
namespace blender::compositor {
@ -88,13 +88,13 @@ void MultilayerColorOperation::execute_pixel_sampled(float output[4],
if (number_of_channels_ == 4) {
switch (sampler) {
case PixelSampler::Nearest:
nearest_interpolation_color(buffer_, nullptr, output, x, y);
imbuf::interpolate_nearest_fl(buffer_, output, x, y);
break;
case PixelSampler::Bilinear:
bilinear_interpolation_color(buffer_, nullptr, output, x, y);
imbuf::interpolate_bilinear_fl(buffer_, output, x, y);
break;
case PixelSampler::Bicubic:
bicubic_interpolation_color(buffer_, nullptr, output, x, y);
imbuf::interpolate_cubic_bspline_fl(buffer_, output, x, y);
break;
}
}

View File

@ -4,6 +4,7 @@
#include "COM_RenderLayersProg.h"
#include "BLI_math_interp.hh"
#include "BLI_string.h"
#include "BKE_image.h"
@ -55,7 +56,6 @@ void RenderLayersProg::init_execution()
void RenderLayersProg::do_interpolation(float output[4], float x, float y, PixelSampler sampler)
{
uint offset;
int width = this->get_width(), height = this->get_height();
int ix = x, iy = y;
@ -73,27 +73,14 @@ void RenderLayersProg::do_interpolation(float output[4], float x, float y, Pixel
}
switch (sampler) {
case PixelSampler::Nearest: {
offset = (iy * width + ix) * elementsize_;
if (elementsize_ == 1) {
output[0] = input_buffer_[offset];
}
else if (elementsize_ == 3) {
copy_v3_v3(output, &input_buffer_[offset]);
}
else {
copy_v4_v4(output, &input_buffer_[offset]);
}
case PixelSampler::Nearest:
math::interpolate_nearest_fl(input_buffer_, output, width, height, elementsize_, x, y);
break;
}
case PixelSampler::Bilinear:
BLI_bilinear_interpolation_fl(input_buffer_, output, width, height, elementsize_, x, y);
math::interpolate_bilinear_fl(input_buffer_, output, width, height, elementsize_, x, y);
break;
case PixelSampler::Bicubic:
BLI_bicubic_interpolation_fl(input_buffer_, output, width, height, elementsize_, x, y);
math::interpolate_cubic_bspline_fl(input_buffer_, output, width, height, elementsize_, x, y);
break;
}
}

View File

@ -11,6 +11,7 @@
#include "BKE_image_partial_update.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
@ -503,11 +504,10 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
float xf = x / (float)texture_width;
float u = info.clipping_uv_bounds.xmax * xf +
info.clipping_uv_bounds.xmin * (1.0 - xf) - tile_offset_x;
nearest_interpolation_color(tile_buffer,
nullptr,
&extracted_buffer.float_buffer.data[offset * 4],
u * tile_buffer->x,
v * tile_buffer->y);
imbuf::interpolate_nearest_fl(tile_buffer,
&extracted_buffer.float_buffer.data[offset * 4],
u * tile_buffer->x,
v * tile_buffer->y);
offset++;
}
}

View File

@ -936,7 +936,7 @@ void time_offset_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float
/* This simulates the fcu curve moving in time. */
const float time = fcu->bezt[segment->start_index + i].vec[1][0] + frame_offset;
/* Need to normalize time to first_key to specify that as the wrapping point. */
const float wrapped_time = mod_f_positive(time - first_key_x, fcu_x_range) + first_key_x;
const float wrapped_time = floored_fmod(time - first_key_x, fcu_x_range) + first_key_x;
const float delta_y = fcu_y_range * floorf((time - first_key_x) / fcu_x_range);
const float key_y_value = evaluate_fcurve(fcu, wrapped_time) + delta_y;

View File

@ -27,6 +27,7 @@
#include "BLI_math_bits.h"
#include "BLI_math_color_blend.h"
#include "BLI_math_geom.h"
#include "BLI_math_vector.hh"
#include "BLI_memarena.h"
#include "BLI_task.h"
#include "BLI_threads.h"
@ -37,7 +38,7 @@
#include "BLT_translation.h"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
#include "DNA_brush_types.h"
#include "DNA_customdata_types.h"
@ -715,35 +716,17 @@ static int project_paint_PickFace(const ProjPaintState *ps, const float pt[2], f
return best_tri_index;
}
/* Converts a uv coord into a pixel location wrapping if the uv is outside 0-1 range */
static void uvco_to_wrapped_pxco(const float uv[2], int ibuf_x, int ibuf_y, float *x, float *y)
{
/* use */
*x = fmodf(uv[0], 1.0f);
*y = fmodf(uv[1], 1.0f);
if (*x < 0.0f) {
*x += 1.0f;
}
if (*y < 0.0f) {
*y += 1.0f;
}
*x = *x * ibuf_x - 0.5f;
*y = *y * ibuf_y - 0.5f;
}
/* Set the top-most face color that the screen space coord 'pt' touches
* (or return 0 if none touch) */
static bool project_paint_PickColor(
const ProjPaintState *ps, const float pt[2], float *rgba_fp, uchar *rgba, const bool interp)
{
using namespace blender;
const float *tri_uv[3];
float w[3], uv[2];
int tri_index;
Image *ima;
ImBuf *ibuf;
int xi, yi;
tri_index = project_paint_PickFace(ps, pt, w);
@ -770,58 +753,32 @@ static bool project_paint_PickColor(
return false;
}
float x = uv[0] * ibuf->x;
float y = uv[1] * ibuf->y;
if (interp) {
float x, y;
uvco_to_wrapped_pxco(uv, ibuf->x, ibuf->y, &x, &y);
x -= 0.5f;
y -= 0.5f;
}
if (ibuf->float_buffer.data) {
if (rgba_fp) {
bilinear_interpolation_color_wrap(ibuf, nullptr, rgba_fp, x, y);
}
else {
float rgba_tmp_f[4];
bilinear_interpolation_color_wrap(ibuf, nullptr, rgba_tmp_f, x, y);
premul_float_to_straight_uchar(rgba, rgba_tmp_f);
}
if (ibuf->float_buffer.data) {
float4 col = interp ? imbuf::interpolate_bilinear_wrap_fl(ibuf, x, y) :
imbuf::interpolate_nearest_wrap_fl(ibuf, x, y);
col = math::clamp(col, 0.0f, 1.0f);
if (rgba_fp) {
memcpy(rgba_fp, &col, sizeof(col));
}
else {
if (rgba) {
bilinear_interpolation_color_wrap(ibuf, rgba, nullptr, x, y);
}
else {
uchar rgba_tmp[4];
bilinear_interpolation_color_wrap(ibuf, rgba_tmp, nullptr, x, y);
straight_uchar_to_premul_float(rgba_fp, rgba_tmp);
}
premul_float_to_straight_uchar(rgba, col);
}
}
else {
// xi = int((uv[0]*ibuf->x) + 0.5f);
// yi = int((uv[1]*ibuf->y) + 0.5f);
// if (xi < 0 || xi >= ibuf->x || yi < 0 || yi >= ibuf->y) return false;
/* wrap */
xi = mod_i(int(uv[0] * ibuf->x), ibuf->x);
yi = mod_i(int(uv[1] * ibuf->y), ibuf->y);
uchar4 col = interp ? imbuf::interpolate_bilinear_wrap_byte(ibuf, x, y) :
imbuf::interpolate_nearest_wrap_byte(ibuf, x, y);
if (rgba) {
if (ibuf->float_buffer.data) {
const float *rgba_tmp_fp = ibuf->float_buffer.data + (xi + yi * ibuf->x * 4);
premul_float_to_straight_uchar(rgba, rgba_tmp_fp);
}
else {
*((uint *)rgba) = *(uint *)(((char *)ibuf->byte_buffer.data) + ((xi + yi * ibuf->x) * 4));
}
memcpy(rgba, &col, sizeof(col));
}
if (rgba_fp) {
if (ibuf->float_buffer.data) {
copy_v4_v4(rgba_fp, (ibuf->float_buffer.data + ((xi + yi * ibuf->x) * 4)));
}
else {
uchar *tmp_ch = ibuf->byte_buffer.data + ((xi + yi * ibuf->x) * 4);
straight_uchar_to_premul_float(rgba_fp, tmp_ch);
}
else {
straight_uchar_to_premul_float(rgba_fp, col);
}
}
BKE_image_release_ibuf(ima, ibuf, nullptr);
@ -1657,18 +1614,22 @@ static float screen_px_line_point_factor_v2_persp(const ProjPaintState *ps,
static void project_face_pixel(
const float *tri_uv[3], ImBuf *ibuf_other, const float w[3], uchar rgba_ub[4], float rgba_f[4])
{
float uv_other[2], x, y;
using namespace blender;
float uv_other[2];
interp_v2_v2v2v2(uv_other, UNPACK3(tri_uv), w);
/* use */
uvco_to_wrapped_pxco(uv_other, ibuf_other->x, ibuf_other->y, &x, &y);
float x = uv_other[0] * ibuf_other->x - 0.5f;
float y = uv_other[1] * ibuf_other->y - 0.5f;
if (ibuf_other->float_buffer.data) { /* from float to float */
bilinear_interpolation_color_wrap(ibuf_other, nullptr, rgba_f, x, y);
if (ibuf_other->float_buffer.data) {
float4 col = imbuf::interpolate_bilinear_wrap_fl(ibuf_other, x, y);
col = math::clamp(col, 0.0f, 1.0f);
memcpy(rgba_f, &col, sizeof(col));
}
else { /* from char to float */
bilinear_interpolation_color_wrap(ibuf_other, rgba_ub, nullptr, x, y);
else {
uchar4 col = imbuf::interpolate_bilinear_wrap_byte(ibuf_other, x, y);
memcpy(rgba_ub, &col, sizeof(col));
}
}
@ -5388,11 +5349,10 @@ static void do_projectpaint_thread(TaskPool *__restrict /*pool*/, void *ph_v)
if (is_floatbuf) {
BLI_assert(ps->reproject_ibuf->float_buffer.data != nullptr);
bicubic_interpolation_color(ps->reproject_ibuf,
nullptr,
projPixel->newColor.f,
projPixel->projCoSS[0],
projPixel->projCoSS[1]);
blender::imbuf::interpolate_cubic_bspline_fl(ps->reproject_ibuf,
projPixel->newColor.f,
projPixel->projCoSS[0],
projPixel->projCoSS[1]);
if (projPixel->newColor.f[3]) {
float mask = float(projPixel->mask) * (1.0f / 65535.0f);
@ -5404,12 +5364,10 @@ static void do_projectpaint_thread(TaskPool *__restrict /*pool*/, void *ph_v)
}
else {
BLI_assert(ps->reproject_ibuf->byte_buffer.data != nullptr);
bicubic_interpolation_color(ps->reproject_ibuf,
projPixel->newColor.ch,
nullptr,
projPixel->projCoSS[0],
projPixel->projCoSS[1]);
blender::imbuf::interpolate_cubic_bspline_byte(ps->reproject_ibuf,
projPixel->newColor.ch,
projPixel->projCoSS[0],
projPixel->projCoSS[1]);
if (projPixel->newColor.ch[3]) {
float mask = float(projPixel->mask) * (1.0f / 65535.0f);
projPixel->newColor.ch[3] *= mask;

View File

@ -19,8 +19,7 @@
#include "BLT_translation.h"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
#include "DNA_brush_types.h"
#include "DNA_customdata_types.h"

View File

@ -19,6 +19,7 @@
#include "BLI_listbase.h"
#include "BLI_math_color.h"
#include "BLI_math_matrix.h"
#include "BLI_math_vector.hh"
#include "BLI_rect.h"
#include "BLI_utildefines.h"
@ -51,6 +52,7 @@
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
#include "RE_texture.h"
@ -372,6 +374,7 @@ static int imapaint_pick_face(ViewContext *vc, const int mval[2], uint *r_index,
void paint_sample_color(
bContext *C, ARegion *region, int x, int y, bool texpaint_proj, bool use_palette)
{
using namespace blender;
Scene *scene = CTX_data_scene(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Paint *paint = BKE_paint_get_active_from_context(C);
@ -445,46 +448,31 @@ void paint_sample_color(
}
if (image) {
float uv[2];
float u, v;
/* XXX get appropriate ImageUser instead */
ImageUser iuser;
BKE_imageuser_default(&iuser);
iuser.framenr = image->lastframe;
float uv[2];
imapaint_pick_uv(me_eval, scene, ob_eval, faceindex, mval, uv);
if (image->source == IMA_SRC_TILED) {
float new_uv[2];
iuser.tile = BKE_image_get_tile_from_pos(image, uv, new_uv, nullptr);
u = new_uv[0];
v = new_uv[1];
}
else {
u = fmodf(uv[0], 1.0f);
v = fmodf(uv[1], 1.0f);
if (u < 0.0f) {
u += 1.0f;
}
if (v < 0.0f) {
v += 1.0f;
}
uv[0] = new_uv[0];
uv[1] = new_uv[1];
}
ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, nullptr);
if (ibuf && (ibuf->byte_buffer.data || ibuf->float_buffer.data)) {
u = u * ibuf->x;
v = v * ibuf->y;
float u = uv[0] * ibuf->x;
float v = uv[1] * ibuf->y;
if (ibuf->float_buffer.data) {
float rgba_f[4];
if (interp == SHD_INTERP_CLOSEST) {
nearest_interpolation_color_wrap(ibuf, nullptr, rgba_f, u, v);
}
else {
bilinear_interpolation_color_wrap(ibuf, nullptr, rgba_f, u, v);
}
float4 rgba_f = interp == SHD_INTERP_CLOSEST ?
imbuf::interpolate_nearest_wrap_fl(ibuf, u, v) :
imbuf::interpolate_bilinear_wrap_fl(ibuf, u, v);
rgba_f = math::clamp(rgba_f, 0.0f, 1.0f);
straight_to_premul_v4(rgba_f);
if (use_palette) {
linearrgb_to_srgb_v3_v3(color->rgb, rgba_f);
@ -495,13 +483,9 @@ void paint_sample_color(
}
}
else {
uchar rgba[4];
if (interp == SHD_INTERP_CLOSEST) {
nearest_interpolation_color_wrap(ibuf, rgba, nullptr, u, v);
}
else {
bilinear_interpolation_color_wrap(ibuf, rgba, nullptr, u, v);
}
uchar4 rgba = interp == SHD_INTERP_CLOSEST ?
imbuf::interpolate_nearest_wrap_byte(ibuf, u, v) :
imbuf::interpolate_bilinear_wrap_byte(ibuf, u, v);
if (use_palette) {
rgb_uchar_to_float(color->rgb, rgba);
}

View File

@ -36,6 +36,7 @@ set(SRC
intern/format_tiff.cc
intern/imageprocess.cc
intern/indexer.cc
intern/interp.cc
intern/iris.cc
intern/jpeg.cc
intern/metadata.cc
@ -58,6 +59,7 @@ set(SRC
IMB_imbuf.hh
IMB_imbuf_enums.h
IMB_imbuf_types.hh
IMB_interp.hh
IMB_metadata.hh
IMB_moviecache.hh
IMB_openexr.hh

View File

@ -512,47 +512,9 @@ void IMB_buffer_byte_from_byte(unsigned char *rect_to,
*/
void IMB_convert_rgba_to_abgr(ImBuf *ibuf);
void bicubic_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout);
void nearest_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout);
void bilinear_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout);
typedef void (*InterpolationColorFunction)(
const ImBuf *in, unsigned char outI[4], float outF[4], float u, float v);
void bicubic_interpolation_color(
const ImBuf *in, unsigned char outI[4], float outF[4], float u, float v);
/* Functions assumes out to be zeroed, only does RGBA. */
void nearest_interpolation_color_char(
const ImBuf *in, unsigned char outI[4], float outF[4], float u, float v);
void nearest_interpolation_color_fl(
const ImBuf *in, unsigned char outI[4], float outF[4], float u, float v);
void nearest_interpolation_color(
const ImBuf *in, unsigned char outI[4], float outF[4], float u, float v);
void nearest_interpolation_color_wrap(
const ImBuf *in, unsigned char outI[4], float outF[4], float u, float v);
void bilinear_interpolation_color(
const ImBuf *in, unsigned char outI[4], float outF[4], float u, float v);
void bilinear_interpolation_color_char(const ImBuf *in, unsigned char outI[4], float u, float v);
void bilinear_interpolation_color_fl(const ImBuf *in, float outF[4], float u, float v);
/**
* Note about wrapping, the u/v still needs to be within the image bounds,
* just the interpolation is wrapped.
* This the same as bilinear_interpolation_color except it wraps
* rather than using empty and emptyI.
*/
void bilinear_interpolation_color_wrap(
const ImBuf *in, unsigned char outI[4], float outF[4], float u, float v);
void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[3]);
void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, const float backcol[3]);
/**
* Sample pixel of image using NEAREST method.
*/
void IMB_sampleImageAtLocation(
ImBuf *ibuf, float x, float y, bool make_linear_rgb, float color[4]);
ImBuf *IMB_loadifffile(int file, int flags, char colorspace[IM_MAX_SPACE], const char *descr);
ImBuf *IMB_half_x(ImBuf *ibuf1);

View File

@ -0,0 +1,100 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup imbuf
*
* Image buffer pixel sampling functions.
* Mostly convenience wrappers around lower level `BLI_math_interp.hh`.
*/
#pragma once
#include "BLI_math_interp.hh"
#include "IMB_imbuf_types.hh"
#include <cstring>
namespace blender::imbuf {
[[nodiscard]] inline uchar4 interpolate_nearest_byte(const ImBuf *in, float u, float v)
{
return math::interpolate_nearest_byte(in->byte_buffer.data, in->x, in->y, u, v);
}
[[nodiscard]] inline float4 interpolate_nearest_fl(const ImBuf *in, float u, float v)
{
return math::interpolate_nearest_fl(in->float_buffer.data, in->x, in->y, u, v);
}
inline void interpolate_nearest_byte(const ImBuf *in, uchar output[4], float u, float v)
{
math::interpolate_nearest_byte(in->byte_buffer.data, output, in->x, in->y, u, v);
}
inline void interpolate_nearest_fl(const ImBuf *in, float output[4], float u, float v)
{
math::interpolate_nearest_fl(in->float_buffer.data, output, in->x, in->y, 4, u, v);
}
[[nodiscard]] inline uchar4 interpolate_nearest_wrap_byte(const ImBuf *in, float u, float v)
{
return math::interpolate_nearest_wrap_byte(in->byte_buffer.data, in->x, in->y, u, v);
}
[[nodiscard]] inline float4 interpolate_nearest_wrap_fl(const ImBuf *in, float u, float v)
{
return math::interpolate_nearest_wrap_fl(in->float_buffer.data, in->x, in->y, u, v);
}
[[nodiscard]] inline uchar4 interpolate_bilinear_byte(const ImBuf *in, float u, float v)
{
return math::interpolate_bilinear_byte(in->byte_buffer.data, in->x, in->y, u, v);
}
[[nodiscard]] inline float4 interpolate_bilinear_fl(const ImBuf *in, float u, float v)
{
return math::interpolate_bilinear_fl(in->float_buffer.data, in->x, in->y, u, v);
}
inline void interpolate_bilinear_byte(const ImBuf *in, uchar output[4], float u, float v)
{
uchar4 col = math::interpolate_bilinear_byte(in->byte_buffer.data, in->x, in->y, u, v);
memcpy(output, &col, sizeof(col));
}
inline void interpolate_bilinear_fl(const ImBuf *in, float output[4], float u, float v)
{
float4 col = math::interpolate_bilinear_fl(in->float_buffer.data, in->x, in->y, u, v);
memcpy(output, &col, sizeof(col));
}
[[nodiscard]] inline uchar4 interpolate_bilinear_wrap_byte(const ImBuf *in, float u, float v)
{
return math::interpolate_bilinear_wrap_byte(in->byte_buffer.data, in->x, in->y, u, v);
}
[[nodiscard]] inline float4 interpolate_bilinear_wrap_fl(const ImBuf *in, float u, float v)
{
return math::interpolate_bilinear_wrap_fl(in->float_buffer.data, in->x, in->y, u, v);
}
[[nodiscard]] inline uchar4 interpolate_cubic_bspline_byte(const ImBuf *in, float u, float v)
{
return math::interpolate_cubic_bspline_byte(in->byte_buffer.data, in->x, in->y, u, v);
}
[[nodiscard]] inline float4 interpolate_cubic_bspline_fl(const ImBuf *in, float u, float v)
{
return math::interpolate_cubic_bspline_fl(in->float_buffer.data, in->x, in->y, u, v);
}
inline void interpolate_cubic_bspline_byte(const ImBuf *in, uchar output[4], float u, float v)
{
uchar4 col = math::interpolate_cubic_bspline_byte(in->byte_buffer.data, in->x, in->y, u, v);
memcpy(output, &col, sizeof(col));
}
inline void interpolate_cubic_bspline_fl(const ImBuf *in, float output[4], float u, float v)
{
float4 col = math::interpolate_cubic_bspline_fl(in->float_buffer.data, in->x, in->y, u, v);
memcpy(output, &col, sizeof(col));
}
} // namespace blender::imbuf
/**
* Sample pixel of image using NEAREST method.
*/
void IMB_sampleImageAtLocation(
ImBuf *ibuf, float x, float y, bool make_linear_rgb, float color[4]);

View File

@ -1,14 +1,10 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup imbuf
*
* This file was moved here from the `src/` directory.
* It is meant to deal with endianness. It resided in a general blending lib.
* The other functions were only used during rendering. This single function remained.
* It should probably move to `imbuf/intern/util.cc`, but we'll keep it here for the time being.
*/
#include <cmath>
@ -16,7 +12,6 @@
#include "MEM_guardedalloc.h"
#include "BLI_math_interp.hh"
#include "BLI_task.h"
#include "BLI_utildefines.h"
@ -59,286 +54,6 @@ void IMB_convert_rgba_to_abgr(ImBuf *ibuf)
}
}
static void pixel_from_buffer(const ImBuf *ibuf, uchar **outI, float **outF, int x, int y)
{
size_t offset = size_t(ibuf->x) * y * 4 + 4 * x;
if (ibuf->byte_buffer.data) {
*outI = ibuf->byte_buffer.data + offset;
}
if (ibuf->float_buffer.data) {
*outF = ibuf->float_buffer.data + offset;
}
}
/* -------------------------------------------------------------------- */
/** \name Bi-Cubic Interpolation
* \{ */
void bicubic_interpolation_color(const ImBuf *in, uchar outI[4], float outF[4], float u, float v)
{
if (outF) {
BLI_bicubic_interpolation_fl(in->float_buffer.data, outF, in->x, in->y, 4, u, v);
}
else {
BLI_bicubic_interpolation_char(in->byte_buffer.data, outI, in->x, in->y, u, v);
}
}
void bicubic_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout)
{
uchar *outI = nullptr;
float *outF = nullptr;
if (in == nullptr || (in->byte_buffer.data == nullptr && in->float_buffer.data == nullptr)) {
return;
}
/* GCC warns these could be uninitialized, but its ok. */
pixel_from_buffer(out, &outI, &outF, xout, yout);
bicubic_interpolation_color(in, outI, outF, u, v);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Bi-Linear Interpolation
* \{ */
void bilinear_interpolation_color_fl(const ImBuf *in, float outF[4], float u, float v)
{
BLI_assert(outF);
BLI_assert(in->float_buffer.data);
BLI_bilinear_interpolation_fl(in->float_buffer.data, outF, in->x, in->y, 4, u, v);
}
void bilinear_interpolation_color_char(const ImBuf *in, uchar outI[4], float u, float v)
{
BLI_assert(outI);
BLI_assert(in->byte_buffer.data);
BLI_bilinear_interpolation_char(in->byte_buffer.data, outI, in->x, in->y, u, v);
}
void bilinear_interpolation_color(const ImBuf *in, uchar outI[4], float outF[4], float u, float v)
{
if (outF) {
BLI_bilinear_interpolation_fl(in->float_buffer.data, outF, in->x, in->y, 4, u, v);
}
else {
BLI_bilinear_interpolation_char(in->byte_buffer.data, outI, in->x, in->y, u, v);
}
}
/* Function assumes out to be zeroed, only does RGBA. */
/* BILINEAR INTERPOLATION */
void bilinear_interpolation_color_wrap(
const ImBuf *in, uchar outI[4], float outF[4], float u, float v)
{
float *row1, *row2, *row3, *row4, a, b;
uchar *row1I, *row2I, *row3I, *row4I;
float a_b, ma_b, a_mb, ma_mb;
int y1, y2, x1, x2;
/* ImBuf in must have a valid rect or rect_float, assume this is already checked */
x1 = int(floor(u));
x2 = int(ceil(u));
y1 = int(floor(v));
y2 = int(ceil(v));
/* sample area entirely outside image? */
if (x2 < 0 || x1 > in->x - 1 || y2 < 0 || y1 > in->y - 1) {
return;
}
/* Wrap interpolation pixels - main difference from #bilinear_interpolation_color. */
if (x1 < 0) {
x1 = in->x + x1;
}
if (y1 < 0) {
y1 = in->y + y1;
}
if (x2 >= in->x) {
x2 = x2 - in->x;
}
if (y2 >= in->y) {
y2 = y2 - in->y;
}
a = u - floorf(u);
b = v - floorf(v);
a_b = a * b;
ma_b = (1.0f - a) * b;
a_mb = a * (1.0f - b);
ma_mb = (1.0f - a) * (1.0f - b);
if (outF) {
float *in_rect_float = in->float_buffer.data;
/* sample including outside of edges of image */
row1 = in_rect_float + size_t(in->x) * y1 * 4 + 4 * x1;
row2 = in_rect_float + size_t(in->x) * y2 * 4 + 4 * x1;
row3 = in_rect_float + size_t(in->x) * y1 * 4 + 4 * x2;
row4 = in_rect_float + size_t(in->x) * y2 * 4 + 4 * x2;
outF[0] = ma_mb * row1[0] + a_mb * row3[0] + ma_b * row2[0] + a_b * row4[0];
outF[1] = ma_mb * row1[1] + a_mb * row3[1] + ma_b * row2[1] + a_b * row4[1];
outF[2] = ma_mb * row1[2] + a_mb * row3[2] + ma_b * row2[2] + a_b * row4[2];
outF[3] = ma_mb * row1[3] + a_mb * row3[3] + ma_b * row2[3] + a_b * row4[3];
/* clamp here or else we can easily get off-range */
clamp_v4(outF, 0.0f, 1.0f);
}
if (outI) {
uchar *in_rect = in->byte_buffer.data;
/* sample including outside of edges of image */
row1I = in_rect + size_t(in->x) * y1 * 4 + 4 * x1;
row2I = in_rect + size_t(in->x) * y2 * 4 + 4 * x1;
row3I = in_rect + size_t(in->x) * y1 * 4 + 4 * x2;
row4I = in_rect + size_t(in->x) * y2 * 4 + 4 * x2;
/* Tested with white images and this should not wrap back to zero. */
outI[0] = roundf(ma_mb * row1I[0] + a_mb * row3I[0] + ma_b * row2I[0] + a_b * row4I[0]);
outI[1] = roundf(ma_mb * row1I[1] + a_mb * row3I[1] + ma_b * row2I[1] + a_b * row4I[1]);
outI[2] = roundf(ma_mb * row1I[2] + a_mb * row3I[2] + ma_b * row2I[2] + a_b * row4I[2]);
outI[3] = roundf(ma_mb * row1I[3] + a_mb * row3I[3] + ma_b * row2I[3] + a_b * row4I[3]);
}
}
void bilinear_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout)
{
uchar *outI = nullptr;
float *outF = nullptr;
if (in == nullptr || (in->byte_buffer.data == nullptr && in->float_buffer.data == nullptr)) {
return;
}
/* GCC warns these could be uninitialized, but its ok. */
pixel_from_buffer(out, &outI, &outF, xout, yout);
bilinear_interpolation_color(in, outI, outF, u, v);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Nearest Interpolation
* \{ */
void nearest_interpolation_color_char(
const ImBuf *in, uchar outI[4], float /*outF*/[4], float u, float v)
{
BLI_assert(outI);
BLI_assert(in->byte_buffer.data);
/* ImBuf in must have a valid rect or rect_float, assume this is already checked */
int x1 = int(u);
int y1 = int(v);
/* sample area entirely outside image? */
if (x1 < 0 || x1 >= in->x || y1 < 0 || y1 >= in->y) {
outI[0] = outI[1] = outI[2] = outI[3] = 0;
return;
}
const size_t offset = (size_t(in->x) * y1 + x1) * 4;
const uchar *dataI = in->byte_buffer.data + offset;
outI[0] = dataI[0];
outI[1] = dataI[1];
outI[2] = dataI[2];
outI[3] = dataI[3];
}
void nearest_interpolation_color_fl(
const ImBuf *in, uchar /*outI*/[4], float outF[4], float u, float v)
{
BLI_assert(outF);
BLI_assert(in->float_buffer.data);
/* ImBuf in must have a valid rect or rect_float, assume this is already checked */
int x1 = int(u);
int y1 = int(v);
/* sample area entirely outside image? */
if (x1 < 0 || x1 >= in->x || y1 < 0 || y1 >= in->y) {
zero_v4(outF);
return;
}
const size_t offset = (size_t(in->x) * y1 + x1) * 4;
const float *dataF = in->float_buffer.data + offset;
copy_v4_v4(outF, dataF);
}
void nearest_interpolation_color(const ImBuf *in, uchar outI[4], float outF[4], float u, float v)
{
if (outF) {
nearest_interpolation_color_fl(in, outI, outF, u, v);
}
else {
nearest_interpolation_color_char(in, outI, outF, u, v);
}
}
void nearest_interpolation_color_wrap(
const ImBuf *in, uchar outI[4], float outF[4], float u, float v)
{
const float *dataF;
uchar *dataI;
int y, x;
/* ImBuf in must have a valid rect or rect_float, assume this is already checked */
x = int(floor(u));
y = int(floor(v));
x = x % in->x;
y = y % in->y;
/* Wrap interpolation pixels - main difference from #nearest_interpolation_color. */
if (x < 0) {
x += in->x;
}
if (y < 0) {
y += in->y;
}
dataI = in->byte_buffer.data + size_t(in->x) * y * 4 + 4 * x;
if (outI) {
outI[0] = dataI[0];
outI[1] = dataI[1];
outI[2] = dataI[2];
outI[3] = dataI[3];
}
dataF = in->float_buffer.data + size_t(in->x) * y * 4 + 4 * x;
if (outF) {
outF[0] = dataF[0];
outF[1] = dataF[1];
outF[2] = dataF[2];
outF[3] = dataF[3];
}
}
void nearest_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout)
{
uchar *outI = nullptr;
float *outF = nullptr;
if (in == nullptr || (in->byte_buffer.data == nullptr && in->float_buffer.data == nullptr)) {
return;
}
/* gcc warns these could be uninitialized, but its ok. */
pixel_from_buffer(out, &outI, &outF, xout, yout);
nearest_interpolation_color(in, outI, outF, u, v);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Threaded Image Processing
* \{ */
@ -472,25 +187,3 @@ void IMB_alpha_under_color_byte(uchar *rect, int x, int y, const float backcol[3
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Sample Pixel
* \{ */
void IMB_sampleImageAtLocation(ImBuf *ibuf, float x, float y, bool make_linear_rgb, float color[4])
{
if (ibuf->float_buffer.data) {
nearest_interpolation_color(ibuf, nullptr, color, x, y);
}
else {
uchar byte_color[4];
nearest_interpolation_color(ibuf, byte_color, nullptr, x, y);
rgba_uchar_to_float(color, byte_color);
if (make_linear_rgb) {
IMB_colormanagement_colorspace_to_scene_linear_v4(
color, false, ibuf->byte_buffer.colorspace);
}
}
}
/** \} */

View File

@ -0,0 +1,27 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup imbuf
*/
#include "BLI_math_vector.h"
#include "IMB_colormanagement.hh"
#include "IMB_interp.hh"
void IMB_sampleImageAtLocation(ImBuf *ibuf, float x, float y, bool make_linear_rgb, float color[4])
{
using namespace blender;
if (ibuf->float_buffer.data) {
imbuf::interpolate_nearest_fl(ibuf, color, x, y);
}
else {
uchar4 byte_color = imbuf::interpolate_nearest_byte(ibuf, x, y);
rgba_uchar_to_float(color, byte_color);
if (make_linear_rgb) {
IMB_colormanagement_colorspace_to_scene_linear_v4(
color, false, ibuf->byte_buffer.colorspace);
}
}
}

View File

@ -9,16 +9,15 @@
#include <cmath>
#include "BLI_math_color.h"
#include "BLI_math_interp.hh"
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
#include "IMB_filter.hh"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
#include "imbuf.hh"
#include "IMB_filter.hh"
#include "BLI_sys_types.h" /* for intptr_t support */
static void imb_half_x_no_alloc(ImBuf *ibuf2, ImBuf *ibuf1)
@ -1745,6 +1744,7 @@ static void scale_thread_init(void *data_v, int start_line, int tot_line, void *
static void *do_scale_thread(void *data_v)
{
using namespace blender::imbuf;
ScaleThreadData *data = (ScaleThreadData *)data_v;
ImBuf *ibuf = data->ibuf;
int i;
@ -1761,13 +1761,12 @@ static void *do_scale_thread(void *data_v)
int offset = y * data->newx + x;
if (data->byte_buffer) {
uchar *pixel = data->byte_buffer + 4 * offset;
BLI_bilinear_interpolation_char(ibuf->byte_buffer.data, pixel, ibuf->x, ibuf->y, u, v);
interpolate_bilinear_byte(ibuf, data->byte_buffer + 4 * offset, u, v);
}
if (data->float_buffer) {
float *pixel = data->float_buffer + ibuf->channels * offset;
BLI_bilinear_interpolation_fl(
blender::math::interpolate_bilinear_fl(
ibuf->float_buffer.data, pixel, ibuf->x, ibuf->y, ibuf->channels, u, v);
}
}

View File

@ -1,4 +1,5 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
@ -17,7 +18,10 @@
#include "BLI_vector.hh"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
using blender::float4;
using blender::uchar4;
namespace blender::imbuf::transform {
@ -134,27 +138,6 @@ static float wrap_uv(float value, int size)
return x;
}
template<int NumChannels>
static void sample_nearest_float(const ImBuf *source, float u, float v, float *r_sample)
{
int x1 = int(u);
int y1 = int(v);
/* Break when sample outside image is requested. */
if (x1 < 0 || x1 >= source->x || y1 < 0 || y1 >= source->y) {
for (int i = 0; i < NumChannels; i++) {
r_sample[i] = 0.0f;
}
return;
}
size_t offset = (size_t(source->x) * y1 + x1) * NumChannels;
const float *dataF = source->float_buffer.data + offset;
for (int i = 0; i < NumChannels; i++) {
r_sample[i] = dataF[i];
}
}
/* Read a pixel from an image buffer, with filtering/wrapping parameters. */
template<eIMBInterpolationFilterMode Filter, typename T, int NumChannels, bool WrapUV>
static void sample_image(const ImBuf *source, float u, float v, T *r_sample)
@ -171,19 +154,19 @@ static void sample_image(const ImBuf *source, float u, float v, T *r_sample)
v -= 0.5f;
}
if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<T, float> && NumChannels == 4) {
bilinear_interpolation_color_fl(source, r_sample, u, v);
interpolate_bilinear_fl(source, r_sample, u, v);
}
else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v<T, uchar> && NumChannels == 4)
{
nearest_interpolation_color_char(source, r_sample, nullptr, u, v);
interpolate_nearest_byte(source, r_sample, u, v);
}
else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<T, uchar> && NumChannels == 4)
{
bilinear_interpolation_color_char(source, r_sample, u, v);
interpolate_bilinear_byte(source, r_sample, u, v);
}
else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<T, float>) {
if constexpr (WrapUV) {
BLI_bilinear_interpolation_wrap_fl(source->float_buffer.data,
math::interpolate_bilinear_wrap_fl(source->float_buffer.data,
r_sample,
source->x,
source->y,
@ -194,20 +177,21 @@ static void sample_image(const ImBuf *source, float u, float v, T *r_sample)
true);
}
else {
BLI_bilinear_interpolation_fl(
math::interpolate_bilinear_fl(
source->float_buffer.data, r_sample, source->x, source->y, NumChannels, u, v);
}
}
else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v<T, float>) {
sample_nearest_float<NumChannels>(source, u, v, r_sample);
math::interpolate_nearest_fl(
source->float_buffer.data, r_sample, source->x, source->y, NumChannels, u, v);
}
else if constexpr (Filter == IMB_FILTER_BICUBIC && std::is_same_v<T, float>) {
BLI_bicubic_interpolation_fl(
math::interpolate_cubic_bspline_fl(
source->float_buffer.data, r_sample, source->x, source->y, NumChannels, u, v);
}
else if constexpr (Filter == IMB_FILTER_BICUBIC && std::is_same_v<T, uchar> && NumChannels == 4)
{
BLI_bicubic_interpolation_char(source->byte_buffer.data, r_sample, source->x, source->y, u, v);
interpolate_cubic_bspline_byte(source, r_sample, u, v);
}
else {
/* Unsupported sampler. */

View File

@ -19,7 +19,7 @@
#include "BKE_mesh_mapping.hh"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
#include "MEM_guardedalloc.h"
@ -221,9 +221,12 @@ class TextureMarginMap {
*/
void lookup_pixels(ImBuf *ibuf, char *mask, int maxPolygonSteps)
{
float4 *ibuf_ptr_fl = reinterpret_cast<float4 *>(ibuf->float_buffer.data);
uchar4 *ibuf_ptr_ch = reinterpret_cast<uchar4 *>(ibuf->byte_buffer.data);
size_t pixel_index = 0;
for (int y = 0; y < h_; y++) {
for (int x = 0; x < w_; x++) {
uint32_t dp = get_pixel(x, y);
uint32_t dp = pixel_data_[pixel_index];
if (IsDijkstraPixel(dp) && !DijkstraPixelIsUnset(dp)) {
int dist = DijkstraPixelGetDistance(dp);
int direction = DijkstraPixelGetDirection(dp);
@ -270,9 +273,14 @@ class TextureMarginMap {
}
if (found_pixel_in_polygon) {
bilinear_interpolation(ibuf, ibuf, destX, destY, x, y);
if (ibuf_ptr_fl) {
ibuf_ptr_fl[pixel_index] = imbuf::interpolate_bilinear_fl(ibuf, destX, destY);
}
if (ibuf_ptr_ch) {
ibuf_ptr_ch[pixel_index] = imbuf::interpolate_bilinear_byte(ibuf, destX, destY);
}
/* Add our new pixels to the assigned pixel map. */
mask[y * w_ + x] = 1;
mask[pixel_index] = 1;
}
}
}
@ -280,8 +288,9 @@ class TextureMarginMap {
/* These are not margin pixels, make sure the extend filter which is run after this step
* leaves them alone.
*/
mask[y * w_ + x] = 1;
mask[pixel_index] = 1;
}
pixel_index++;
}
}
}

View File

@ -1,5 +1,5 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
* SPDX-FileCopyrightText: 2003-2009 Blender Authors
* SPDX-FileCopyrightText: 2003-2024 Blender Authors
* SPDX-FileCopyrightText: 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
*
* SPDX-License-Identifier: GPL-2.0-or-later */
@ -40,6 +40,7 @@
#include "IMB_colormanagement.hh"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_interp.hh"
#include "IMB_metadata.hh"
#include "BLI_math_color_blend.h"
@ -64,7 +65,7 @@
#include "strip_time.hh"
#include "utils.hh"
using blender::float4;
using namespace blender;
static SeqEffectHandle get_sequence_effect_impl(int seq_type);
@ -1536,7 +1537,7 @@ static void transform_image(int x,
int y,
int start_line,
int total_lines,
ImBuf *ibuf1,
ImBuf *ibuf,
ImBuf *out,
float scale_x,
float scale_y,
@ -1549,6 +1550,10 @@ static void transform_image(int x,
float s = sinf(rotate);
float c = cosf(rotate);
float4 *dst_fl = reinterpret_cast<float4 *>(out->float_buffer.data);
uchar4 *dst_ch = reinterpret_cast<uchar4 *>(out->byte_buffer.data);
size_t offset = size_t(x) * start_line;
for (int yi = start_line; yi < start_line + total_lines; yi++) {
for (int xi = 0; xi < x; xi++) {
/* Translate point. */
@ -1570,15 +1575,31 @@ static void transform_image(int x,
/* interpolate */
switch (interpolation) {
case 0:
nearest_interpolation(ibuf1, out, xt, yt, xi, yi);
if (dst_fl) {
dst_fl[offset] = imbuf::interpolate_nearest_fl(ibuf, xt, yt);
}
else {
dst_ch[offset] = imbuf::interpolate_nearest_byte(ibuf, xt, yt);
}
break;
case 1:
bilinear_interpolation(ibuf1, out, xt, yt, xi, yi);
if (dst_fl) {
dst_fl[offset] = imbuf::interpolate_bilinear_fl(ibuf, xt, yt);
}
else {
dst_ch[offset] = imbuf::interpolate_bilinear_byte(ibuf, xt, yt);
}
break;
case 2:
bicubic_interpolation(ibuf1, out, xt, yt, xi, yi);
if (dst_fl) {
dst_fl[offset] = imbuf::interpolate_cubic_bspline_fl(ibuf, xt, yt);
}
else {
dst_ch[offset] = imbuf::interpolate_cubic_bspline_byte(ibuf, xt, yt);
}
break;
}
offset++;
}
}
}

View File

@ -411,7 +411,7 @@ class Report:
shutil.copy(new_img, old_img)
failed = False
# Generate diff image.
# Generate color diff image.
command = (
self.oiiotool,
ref_img,
@ -423,7 +423,24 @@ class Report:
"--mulc", "16",
"-o", diff_color_img,
)
try:
subprocess.check_output(command)
except subprocess.CalledProcessError as e:
if self.verbose:
print_message(e.output.decode("utf-8", 'ignore'))
# Generate alpha diff image.
command = (
self.oiiotool,
ref_img,
"--ch", "A",
tmp_filepath,
"--ch", "A",
"--sub",
"--abs",
"--mulc", "16",
"-o", diff_alpha_img,
)
try:
subprocess.check_output(command)
except subprocess.CalledProcessError as e: