From f76e04bf4d7cdce8fe563b85bb272e9d5ede2b78 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 10 Dec 2021 15:19:41 +0100 Subject: ImBuf: Use templating for IMB_transform. Reduce the inner loop of IMB_transform by extracting writing to an output buffer in a template. This reduces a branch in the inner loop and would allow different number of channels in the future. --- source/blender/imbuf/intern/transform.cc | 162 +++++++++++++++++++++++++------ 1 file changed, 132 insertions(+), 30 deletions(-) (limited to 'source/blender/imbuf') diff --git a/source/blender/imbuf/intern/transform.cc b/source/blender/imbuf/intern/transform.cc index 6cd5ef23783..7c130c658c4 100644 --- a/source/blender/imbuf/intern/transform.cc +++ b/source/blender/imbuf/intern/transform.cc @@ -83,22 +83,136 @@ struct TransformUserData { } }; -template -class ScanlineProcessor { - private: - void pixel_from_buffer(const struct ImBuf *ibuf, unsigned char **outI, float **outF, int y) const +/** + * \brief Base class for source discarding. + * + * 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 + * not change the output buffer. + */ +class BaseDiscard { + public: + virtual ~BaseDiscard() = default; + + /** + * \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; +}; +/** + * \brief Crop uv-coordinates that are outside the user data src_crop rect. + */ +class CropSource : public BaseDiscard { + public: + /** + * \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. + */ + virtual bool should_discard(const TransformUserData &user_data, const float uv[2]) + { + 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; + } +}; + +/** + * \brief Discard that does not discard anything. + */ +class NoDiscard : public BaseDiscard { + public: + /** + * \brief Should the source pixel at the given uv coordinate be discarded. + * + * Will never discard any pixels. + */ + virtual bool should_discard(const TransformUserData &UNUSED(user_data), + const float UNUSED(uv[2])) + { + return false; + } +}; + +/** + * \brief pointer to a texel to write or read serial. + */ +template< + /** + * \brief Kind of buffer. + * Possible options: float, unsigned char. + */ + typename ImBufStorageType = float, + + /** + * \brief Number of channels of a single pixel. + */ + int NumChannels = 4> +class TexelPointer { + ImBufStorageType *pointer; + + public: + void init_pixel_pointer(const ImBuf *image_buffer, int x, int y) { - const size_t offset = ((size_t)ibuf->x) * y * ChannelLen; + const size_t offset = (y * (size_t)image_buffer->x + x) * NumChannels; - if (ibuf->rect) { - *outI = (unsigned char *)ibuf->rect + offset; + if constexpr (std::is_same_v) { + pointer = image_buffer->rect_float + offset; } + else if constexpr (std::is_same_v) { + pointer = image_buffer->rect + offset; + } + else { + pointer = nullptr; + } + } - if (ibuf->rect_float) { - *outF = ibuf->rect_float + offset; + float *get_float_pointer() + { + if constexpr (std::is_same_v) { + return pointer; + } + else { + return nullptr; } } + unsigned char *get_uchar_pointer() + { + if constexpr (std::is_same_v) { + return pointer; + } + else { + return nullptr; + } + } + + void increase_pixel_pointer() + { + pointer += NumChannels; + } +}; + +template< + /** + * \brief Discard function to use. + * + * \attention Should be a subclass of BaseDiscard. + */ + typename Discard, + + /** + * \brief Color interpolation function to read from the source buffer. + */ + InterpolationColorFunction ColorInterpolation, + + /** + * \brief Kernel to store to the destination buffer. + * Should be an TexelPointer + */ + typename OutputTexelPointer> +class ScanlineProcessor { + Discard discarder; + OutputTexelPointer output; public: void process(const TransformUserData *user_data, int scanline) @@ -108,27 +222,15 @@ class ScanlineProcessor { float uv[2]; madd_v2_v2v2fl(uv, user_data->start_uv, user_data->add_y, scanline); - unsigned char *outI = nullptr; - float *outF = nullptr; - pixel_from_buffer(user_data->dst, &outI, &outF, scanline); - + output.init_pixel_pointer(user_data->dst, 0, scanline); for (int xi = 0; xi < width; xi++) { - if constexpr (Mode == IMB_TRANSFORM_MODE_CROP_SRC) { - if (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) { - ColorInterpolation(user_data->src, outI, outF, uv[0], uv[1]); - } - } - else { - ColorInterpolation(user_data->src, outI, outF, uv[0], uv[1]); + if (!discarder.should_discard(*user_data, uv)) { + ColorInterpolation( + user_data->src, output.get_uchar_pointer(), output.get_float_pointer(), uv[0], uv[1]); } + add_v2_v2(uv, user_data->add_x); - if (outI) { - outI += ChannelLen; - } - if (outF) { - outF += ChannelLen; - } + output.increase_pixel_pointer(); } } }; @@ -147,13 +249,13 @@ ScanlineThreadFunc get_scanline_function(const eIMBTransformMode mode) switch (mode) { case IMB_TRANSFORM_MODE_REGULAR: return transform_scanline_function< - ScanlineProcessor>; + ScanlineProcessor>>; case IMB_TRANSFORM_MODE_CROP_SRC: return transform_scanline_function< - ScanlineProcessor>; + ScanlineProcessor>>; case IMB_TRANSFORM_MODE_WRAP_REPEAT: return transform_scanline_function< - ScanlineProcessor>; + ScanlineProcessor>>; } BLI_assert_unreachable(); -- cgit v1.2.3