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.
This commit is contained in:
@@ -83,22 +83,136 @@ struct TransformUserData {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<eIMBTransformMode Mode, InterpolationColorFunction ColorInterpolation, int ChannelLen = 4>
|
/**
|
||||||
class ScanlineProcessor {
|
* \brief Base class for source discarding.
|
||||||
private:
|
*
|
||||||
void pixel_from_buffer(const struct ImBuf *ibuf, unsigned char **outI, float **outF, int y) const
|
* 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])
|
||||||
{
|
{
|
||||||
const size_t offset = ((size_t)ibuf->x) * y * ChannelLen;
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (ibuf->rect) {
|
/**
|
||||||
*outI = (unsigned char *)ibuf->rect + offset;
|
* \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 = (y * (size_t)image_buffer->x + x) * NumChannels;
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<ImBufStorageType, float>) {
|
||||||
|
pointer = image_buffer->rect_float + offset;
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_same_v<ImBufStorageType, unsigned char>) {
|
||||||
|
pointer = image_buffer->rect + offset;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pointer = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ibuf->rect_float) {
|
float *get_float_pointer()
|
||||||
*outF = ibuf->rect_float + offset;
|
{
|
||||||
|
if constexpr (std::is_same_v<ImBufStorageType, float>) {
|
||||||
|
return pointer;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
unsigned char *get_uchar_pointer()
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<ImBufStorageType, unsigned char>) {
|
||||||
|
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:
|
public:
|
||||||
void process(const TransformUserData *user_data, int scanline)
|
void process(const TransformUserData *user_data, int scanline)
|
||||||
@@ -108,27 +222,15 @@ class ScanlineProcessor {
|
|||||||
float uv[2];
|
float uv[2];
|
||||||
madd_v2_v2v2fl(uv, user_data->start_uv, user_data->add_y, scanline);
|
madd_v2_v2v2fl(uv, user_data->start_uv, user_data->add_y, scanline);
|
||||||
|
|
||||||
unsigned char *outI = nullptr;
|
output.init_pixel_pointer(user_data->dst, 0, scanline);
|
||||||
float *outF = nullptr;
|
|
||||||
pixel_from_buffer(user_data->dst, &outI, &outF, scanline);
|
|
||||||
|
|
||||||
for (int xi = 0; xi < width; xi++) {
|
for (int xi = 0; xi < width; xi++) {
|
||||||
if constexpr (Mode == IMB_TRANSFORM_MODE_CROP_SRC) {
|
if (!discarder.should_discard(*user_data, uv)) {
|
||||||
if (uv[0] >= user_data->src_crop.xmin && uv[0] < user_data->src_crop.xmax &&
|
ColorInterpolation(
|
||||||
uv[1] >= user_data->src_crop.ymin && uv[1] < user_data->src_crop.ymax) {
|
user_data->src, output.get_uchar_pointer(), output.get_float_pointer(), uv[0], uv[1]);
|
||||||
ColorInterpolation(user_data->src, outI, outF, uv[0], uv[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ColorInterpolation(user_data->src, outI, outF, uv[0], uv[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
add_v2_v2(uv, user_data->add_x);
|
add_v2_v2(uv, user_data->add_x);
|
||||||
if (outI) {
|
output.increase_pixel_pointer();
|
||||||
outI += ChannelLen;
|
|
||||||
}
|
|
||||||
if (outF) {
|
|
||||||
outF += ChannelLen;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -147,13 +249,13 @@ ScanlineThreadFunc get_scanline_function(const eIMBTransformMode mode)
|
|||||||
switch (mode) {
|
switch (mode) {
|
||||||
case IMB_TRANSFORM_MODE_REGULAR:
|
case IMB_TRANSFORM_MODE_REGULAR:
|
||||||
return transform_scanline_function<
|
return transform_scanline_function<
|
||||||
ScanlineProcessor<IMB_TRANSFORM_MODE_REGULAR, DefaultFunction>>;
|
ScanlineProcessor<NoDiscard, DefaultFunction, TexelPointer<float, 4>>>;
|
||||||
case IMB_TRANSFORM_MODE_CROP_SRC:
|
case IMB_TRANSFORM_MODE_CROP_SRC:
|
||||||
return transform_scanline_function<
|
return transform_scanline_function<
|
||||||
ScanlineProcessor<IMB_TRANSFORM_MODE_CROP_SRC, DefaultFunction>>;
|
ScanlineProcessor<CropSource, DefaultFunction, TexelPointer<float, 4>>>;
|
||||||
case IMB_TRANSFORM_MODE_WRAP_REPEAT:
|
case IMB_TRANSFORM_MODE_WRAP_REPEAT:
|
||||||
return transform_scanline_function<
|
return transform_scanline_function<
|
||||||
ScanlineProcessor<IMB_TRANSFORM_MODE_WRAP_REPEAT, WrapRepeatFunction>>;
|
ScanlineProcessor<NoDiscard, WrapRepeatFunction, TexelPointer<float, 4>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
BLI_assert_unreachable();
|
BLI_assert_unreachable();
|
||||||
|
|||||||
Reference in New Issue
Block a user