Changeset View
Changeset View
Standalone View
Standalone View
source/blender/imbuf/intern/transform.cc
| Show All 16 Lines | |||||
| namespace blender::imbuf::transform { | namespace blender::imbuf::transform { | ||||
| struct TransformUserData { | struct TransformUserData { | ||||
| /** \brief Source image buffer to read from. */ | /** \brief Source image buffer to read from. */ | ||||
| const ImBuf *src; | const ImBuf *src; | ||||
| /** \brief Destination image buffer to write to. */ | /** \brief Destination image buffer to write to. */ | ||||
| ImBuf *dst; | ImBuf *dst; | ||||
| /** \brief UV coordinates at the origin (0,0) in source image space. */ | /** \brief UV coordinates at the origin (0,0) in source image space. */ | ||||
| float start_uv[2]; | double start_uv[2]; | ||||
| /** | /** | ||||
| * \brief delta UV coordinates along the source image buffer, when moving a single pixel in the X | * \brief delta UV coordinates along the source image buffer, when moving a single pixel in the X | ||||
| * axis of the dst image buffer. | * axis of the dst image buffer. | ||||
| */ | */ | ||||
| float add_x[2]; | double add_x[2]; | ||||
| /** | /** | ||||
| * \brief delta UV coordinate along the source image buffer, when moving a single pixel in the Y | * \brief delta UV coordinate along the source image buffer, when moving a single pixel in the Y | ||||
| * axes of the dst image buffer. | * axes of the dst image buffer. | ||||
| */ | */ | ||||
| float add_y[2]; | double add_y[2]; | ||||
| /** | /** | ||||
| * \brief Cropping region in source image pixel space. | * \brief Cropping region in source image pixel space. | ||||
| */ | */ | ||||
| rctf src_crop; | rctf src_crop; | ||||
| /** | /** | ||||
| * \brief Initialize the start_uv, add_x and add_y fields based on the given transform matrix. | * \brief Initialize the start_uv, add_x and add_y fields based on the given transform matrix. | ||||
| */ | */ | ||||
| void init(const float transform_matrix[4][4]) | void init(const float transform_matrix[4][4]) | ||||
| { | { | ||||
| init_start_uv(transform_matrix); | init_start_uv(transform_matrix); | ||||
| init_add_x(transform_matrix); | init_add_x(transform_matrix); | ||||
| init_add_y(transform_matrix); | init_add_y(transform_matrix); | ||||
| } | } | ||||
| private: | private: | ||||
| void init_start_uv(const float transform_matrix[4][4]) | void init_start_uv(const float transform_matrix[4][4]) | ||||
| { | { | ||||
| float start_uv_v3[3]; | double start_uv_v3[3]; | ||||
| float orig[3]; | double orig[3]; | ||||
| zero_v3(orig); | double transform_matrix_double[4][4]; | ||||
| mul_v3_m4v3(start_uv_v3, transform_matrix, orig); | copy_m4d_m4(transform_matrix_double, transform_matrix); | ||||
| copy_v2_v2(start_uv, start_uv_v3); | zero_v3_db(orig); | ||||
| mul_v3_m4v3_db(start_uv_v3, transform_matrix_double, orig); | |||||
| copy_v2_v2_db(start_uv, start_uv_v3); | |||||
| } | } | ||||
| void init_add_x(const float transform_matrix[4][4]) | void init_add_x(const float transform_matrix[4][4]) | ||||
| { | { | ||||
| double transform_matrix_double[4][4]; | |||||
| copy_m4d_m4(transform_matrix_double, transform_matrix); | |||||
| const int width = src->x; | const int width = src->x; | ||||
| float add_x_v3[3]; | double add_x_v3[3]; | ||||
| float uv_max_x[3]; | double uv_max_x[3]; | ||||
| zero_v3(uv_max_x); | zero_v3_db(uv_max_x); | ||||
| uv_max_x[0] = width; | uv_max_x[0] = width; | ||||
| uv_max_x[1] = 0.0f; | uv_max_x[1] = 0.0f; | ||||
| mul_v3_m4v3(add_x_v3, transform_matrix, uv_max_x); | mul_v3_m4v3_db(add_x_v3, transform_matrix_double, uv_max_x); | ||||
| sub_v2_v2(add_x_v3, start_uv); | sub_v2_v2_db(add_x_v3, start_uv); | ||||
| mul_v2_fl(add_x_v3, 1.0f / width); | mul_v3db_db(add_x_v3, 1.0f / width); | ||||
| copy_v2_v2(add_x, add_x_v3); | copy_v2_v2_db(add_x, add_x_v3); | ||||
| } | } | ||||
| void init_add_y(const float transform_matrix[4][4]) | void init_add_y(const float transform_matrix[4][4]) | ||||
| { | { | ||||
| double transform_matrix_double[4][4]; | |||||
| copy_m4d_m4(transform_matrix_double, transform_matrix); | |||||
| const int height = src->y; | const int height = src->y; | ||||
| float add_y_v3[3]; | double add_y_v3[3]; | ||||
| float uv_max_y[3]; | double uv_max_y[3]; | ||||
| zero_v3(uv_max_y); | zero_v3_db(uv_max_y); | ||||
| uv_max_y[0] = 0.0f; | uv_max_y[0] = 0.0f; | ||||
| uv_max_y[1] = height; | uv_max_y[1] = height; | ||||
| mul_v3_m4v3(add_y_v3, transform_matrix, uv_max_y); | mul_v3_m4v3_db(add_y_v3, transform_matrix_double, uv_max_y); | ||||
| sub_v2_v2(add_y_v3, start_uv); | sub_v2_v2_db(add_y_v3, start_uv); | ||||
| mul_v2_fl(add_y_v3, 1.0f / height); | mul_v3db_db(add_y_v3, 1.0f / height); | ||||
| copy_v2_v2(add_y, add_y_v3); | copy_v2_v2_db(add_y, add_y_v3); | ||||
| } | } | ||||
| }; | }; | ||||
| /** | /** | ||||
| * \brief Base class for source discarding. | * \brief Base class for source discarding. | ||||
| * | * | ||||
| * The class decides if a specific uv coordinate from the source buffer should be ignored. | * The class decides if a specific uv coordinate from the source buffer should be ignored. | ||||
| * This is used to mix multiple images over a single output buffer. Discarded pixels will | * This is used to mix multiple images over a single output buffer. Discarded pixels will | ||||
| * not change the output buffer. | * not change the output buffer. | ||||
| */ | */ | ||||
| class BaseDiscard { | class BaseDiscard { | ||||
| public: | public: | ||||
| virtual ~BaseDiscard() = default; | virtual ~BaseDiscard() = default; | ||||
| /** | /** | ||||
| * \brief Should the source pixel at the given uv coordinate be discarded. | * \brief Should the source pixel at the given uv coordinate be discarded. | ||||
| */ | */ | ||||
| virtual bool should_discard(const TransformUserData &user_data, const float uv[2]) = 0; | virtual bool should_discard(const TransformUserData &user_data, const double uv[2]) = 0; | ||||
| }; | }; | ||||
| /** | /** | ||||
| * \brief Crop uv-coordinates that are outside the user data src_crop rect. | * \brief Crop uv-coordinates that are outside the user data src_crop rect. | ||||
| */ | */ | ||||
| class CropSource : public BaseDiscard { | class CropSource : public BaseDiscard { | ||||
| public: | public: | ||||
| /** | /** | ||||
| * \brief Should the source pixel at the given uv coordinate be discarded. | * \brief Should the source pixel at the given uv coordinate be discarded. | ||||
| * | * | ||||
| * Uses user_data.src_crop to determine if the uv coordinate should be skipped. | * Uses user_data.src_crop to determine if the uv coordinate should be skipped. | ||||
| */ | */ | ||||
| bool should_discard(const TransformUserData &user_data, const float uv[2]) override | bool should_discard(const TransformUserData &user_data, const double uv[2]) override | ||||
| { | { | ||||
| return uv[0] < user_data.src_crop.xmin || uv[0] >= user_data.src_crop.xmax || | return uv[0] < user_data.src_crop.xmin || uv[0] >= user_data.src_crop.xmax || | ||||
| uv[1] < user_data.src_crop.ymin || uv[1] >= user_data.src_crop.ymax; | uv[1] < user_data.src_crop.ymin || uv[1] >= user_data.src_crop.ymax; | ||||
| } | } | ||||
| }; | }; | ||||
| /** | /** | ||||
| * \brief Discard that does not discard anything. | * \brief Discard that does not discard anything. | ||||
| */ | */ | ||||
| class NoDiscard : public BaseDiscard { | class NoDiscard : public BaseDiscard { | ||||
| public: | public: | ||||
| /** | /** | ||||
| * \brief Should the source pixel at the given uv coordinate be discarded. | * \brief Should the source pixel at the given uv coordinate be discarded. | ||||
| * | * | ||||
| * Will never discard any pixels. | * Will never discard any pixels. | ||||
| */ | */ | ||||
| bool should_discard(const TransformUserData & /*user_data*/, const float /*uv*/[2]) override | bool should_discard(const TransformUserData & /*user_data*/, const double /*uv*/[2]) override | ||||
| { | { | ||||
| return false; | return false; | ||||
| } | } | ||||
| }; | }; | ||||
| /** | /** | ||||
| * \brief Pointer to a pixel to write to in serial. | * \brief Pointer to a pixel to write to in serial. | ||||
| */ | */ | ||||
| ▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | |||||
| * | * | ||||
| * Subclasses have the ability to change the UV coordinates when sampling the source buffer. | * Subclasses have the ability to change the UV coordinates when sampling the source buffer. | ||||
| */ | */ | ||||
| class BaseUVWrapping { | class BaseUVWrapping { | ||||
| public: | public: | ||||
| /** | /** | ||||
| * \brief modify the given u coordinate. | * \brief modify the given u coordinate. | ||||
| */ | */ | ||||
| virtual float modify_u(const ImBuf *source_buffer, float u) = 0; | virtual float modify_u(const ImBuf *source_buffer, double u) = 0; | ||||
jbakker: would also make the return type double so it matches the change in the arguments.
Same or… | |||||
| /** | /** | ||||
| * \brief modify the given v coordinate. | * \brief modify the given v coordinate. | ||||
| */ | */ | ||||
| virtual float modify_v(const ImBuf *source_buffer, float v) = 0; | virtual float modify_v(const ImBuf *source_buffer, double v) = 0; | ||||
| }; | }; | ||||
| /** | /** | ||||
| * \brief UVWrapping method that does not modify the UV coordinates. | * \brief UVWrapping method that does not modify the UV coordinates. | ||||
| */ | */ | ||||
| class PassThroughUV : public BaseUVWrapping { | class PassThroughUV : public BaseUVWrapping { | ||||
| public: | public: | ||||
| float modify_u(const ImBuf * /*source_buffer*/, float u) override | float modify_u(const ImBuf * /*source_buffer*/, double u) override | ||||
| { | { | ||||
| return u; | return u; | ||||
| } | } | ||||
| float modify_v(const ImBuf * /*source_buffer*/, float v) override | float modify_v(const ImBuf * /*source_buffer*/, double v) override | ||||
| { | { | ||||
| return v; | return v; | ||||
| } | } | ||||
| }; | }; | ||||
| /** | /** | ||||
| * \brief UVWrapping method that wrap repeats the UV coordinates. | * \brief UVWrapping method that wrap repeats the UV coordinates. | ||||
| */ | */ | ||||
| class WrapRepeatUV : public BaseUVWrapping { | class WrapRepeatUV : public BaseUVWrapping { | ||||
| public: | public: | ||||
| float modify_u(const ImBuf *source_buffer, float u) override | float modify_u(const ImBuf *source_buffer, double u) override | ||||
| { | { | ||||
| int x = int(floor(u)); | int x = int(floor(u)); | ||||
| x = x % source_buffer->x; | x = x % source_buffer->x; | ||||
| if (x < 0) { | if (x < 0) { | ||||
| x += source_buffer->x; | x += source_buffer->x; | ||||
| } | } | ||||
| return x; | return x; | ||||
| } | } | ||||
| float modify_v(const ImBuf *source_buffer, float v) override | float modify_v(const ImBuf *source_buffer, double v) override | ||||
| { | { | ||||
| int y = int(floor(v)); | int y = int(floor(v)); | ||||
| y = y % source_buffer->y; | y = y % source_buffer->y; | ||||
| if (y < 0) { | if (y < 0) { | ||||
| y += source_buffer->y; | y += source_buffer->y; | ||||
| } | } | ||||
| return y; | return y; | ||||
| } | } | ||||
| Show All 25 Lines | |||||
| class Sampler { | class Sampler { | ||||
| UVWrapping uv_wrapper; | UVWrapping uv_wrapper; | ||||
| public: | public: | ||||
| using ChannelType = StorageType; | using ChannelType = StorageType; | ||||
| static const int ChannelLen = NumChannels; | static const int ChannelLen = NumChannels; | ||||
| using SampleType = std::array<StorageType, NumChannels>; | using SampleType = std::array<StorageType, NumChannels>; | ||||
| void sample(const ImBuf *source, const float u, const float v, SampleType &r_sample) | void sample(const ImBuf *source, const double u, const double v, SampleType &r_sample) | ||||
| { | { | ||||
| if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, float> && | if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, float> && | ||||
| NumChannels == 4) { | NumChannels == 4) { | ||||
| const float wrapped_u = uv_wrapper.modify_u(source, u); | const float wrapped_u = uv_wrapper.modify_u(source, u); | ||||
| const float wrapped_v = uv_wrapper.modify_v(source, v); | const float wrapped_v = uv_wrapper.modify_v(source, v); | ||||
| bilinear_interpolation_color_fl(source, nullptr, r_sample.data(), wrapped_u, wrapped_v); | bilinear_interpolation_color_fl(source, nullptr, r_sample.data(), wrapped_u, wrapped_v); | ||||
| } | } | ||||
| else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v<StorageType, uchar> && | else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v<StorageType, uchar> && | ||||
| Show All 40 Lines | void sample(const ImBuf *source, const double u, const double v, SampleType &r_sample) | ||||
| else { | else { | ||||
| /* Unsupported sampler. */ | /* Unsupported sampler. */ | ||||
| BLI_assert_unreachable(); | BLI_assert_unreachable(); | ||||
| } | } | ||||
| } | } | ||||
| private: | private: | ||||
| void sample_nearest_float(const ImBuf *source, | void sample_nearest_float(const ImBuf *source, | ||||
| const float u, | const double u, | ||||
| const float v, | const double v, | ||||
| SampleType &r_sample) | SampleType &r_sample) | ||||
| { | { | ||||
| BLI_STATIC_ASSERT(std::is_same_v<StorageType, float>); | BLI_STATIC_ASSERT(std::is_same_v<StorageType, float>); | ||||
| /* ImBuf in must have a valid rect or rect_float, assume this is already checked */ | /* ImBuf in must have a valid rect or rect_float, assume this is already checked */ | ||||
| int x1 = int(u); | int x1 = int(u); | ||||
| int y1 = int(v); | int y1 = int(v); | ||||
| ▲ Show 20 Lines • Show All 102 Lines • ▼ Show 20 Lines | |||||
| public: | public: | ||||
| /** | /** | ||||
| * \brief Inner loop of the transformations, processing a full scanline. | * \brief Inner loop of the transformations, processing a full scanline. | ||||
| */ | */ | ||||
| void process(const TransformUserData *user_data, int scanline) | void process(const TransformUserData *user_data, int scanline) | ||||
| { | { | ||||
| const int width = user_data->dst->x; | const int width = user_data->dst->x; | ||||
| float uv[2]; | double uv[2]; | ||||
| madd_v2_v2v2fl(uv, user_data->start_uv, user_data->add_y, scanline); | |||||
| madd_v2_v2db_db(uv, user_data->start_uv, user_data->add_y, scanline); | |||||
| uv[0] = user_data->start_uv[0] + user_data->add_y[0] * scanline; | |||||
| uv[1] = user_data->start_uv[1] + user_data->add_y[1] * scanline; | |||||
deadpinUnsubmitted Done Inline ActionsSeems these lines were left in by mistake? deadpin: Seems these lines were left in by mistake? | |||||
| output.init_pixel_pointer(user_data->dst, 0, scanline); | output.init_pixel_pointer(user_data->dst, 0, scanline); | ||||
| for (int xi = 0; xi < width; xi++) { | for (int xi = 0; xi < width; xi++) { | ||||
| if (!discarder.should_discard(*user_data, uv)) { | if (!discarder.should_discard(*user_data, uv)) { | ||||
| typename Sampler::SampleType sample; | typename Sampler::SampleType sample; | ||||
| sampler.sample(user_data->src, uv[0], uv[1], sample); | sampler.sample(user_data->src, uv[0], uv[1], sample); | ||||
| channel_converter.convert_and_store(sample, output); | channel_converter.convert_and_store(sample, output); | ||||
| } | } | ||||
| add_v2_v2(uv, user_data->add_x); | add_v2_v2_db(uv, user_data->add_x); | ||||
| output.increase_pixel_pointer(); | output.increase_pixel_pointer(); | ||||
| } | } | ||||
| } | } | ||||
| }; | }; | ||||
| /** | /** | ||||
| * \brief callback function for threaded transformation. | * \brief callback function for threaded transformation. | ||||
| */ | */ | ||||
| ▲ Show 20 Lines • Show All 109 Lines • Show Last 20 Lines | |||||
would also make the return type double so it matches the change in the arguments.
Same or modify_v