From 28617bb167981ac1e02655c0fc262fe1571d8e1e Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 11 Jun 2021 09:34:31 +0200 Subject: Sequencer: Transform ImBuf Processor. Inside the sequencer the cropping and transform of images/buffers were implemented locally. This reduced the optimizations that a compiler could do and added confusing code styles. This patch adds `IMB_transform` to reduce the confusion and increases compiler optimizations as more code can be inlined and we can keep track of indices inside the inner loop. This increases end-user performance by 30% when playing back aa video in VSE. Reviewed By: ISS, zeddb Differential Revision: https://developer.blender.org/D11549 --- source/blender/imbuf/intern/imageprocess.c | 131 +++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) (limited to 'source/blender/imbuf/intern/imageprocess.c') diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index 1fe3a7717fb..c88a9b81ff2 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -349,6 +349,137 @@ void nearest_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, in nearest_interpolation_color(in, outI, outF, u, v); } +/* -------------------------------------------------------------------- */ +/** \name Image transform + * \{ */ +typedef struct TransformUserData { + ImBuf *src; + ImBuf *dst; + float start_uv[2]; + float add_x[2]; + float add_y[2]; + rctf src_crop; +} TransformUserData; + +static void imb_transform_calc_start_uv(const float transform_matrix[3][3], float r_start_uv[2]) +{ + float orig[2]; + orig[0] = 0.0f; + orig[1] = 0.0f; + mul_v2_m3v2(r_start_uv, transform_matrix, orig); +} + +static void imb_transform_calc_add_x(const float transform_matrix[3][3], + const float start_uv[2], + const int width, + float r_add_x[2]) +{ + float uv_max_x[2]; + uv_max_x[0] = width; + uv_max_x[1] = 0.0f; + mul_v2_m3v2(r_add_x, transform_matrix, uv_max_x); + sub_v2_v2(r_add_x, start_uv); + mul_v2_fl(r_add_x, 1.0f / width); +} + +static void imb_transform_calc_add_y(const float transform_matrix[3][3], + const float start_uv[2], + const int height, + float r_add_y[2]) +{ + float uv_max_y[2]; + uv_max_y[0] = 0.0f; + uv_max_y[1] = height; + mul_v2_m3v2(r_add_y, transform_matrix, uv_max_y); + sub_v2_v2(r_add_y, start_uv); + mul_v2_fl(r_add_y, 1.0f / height); +} + +typedef void (*InterpolationColorFunction)( + struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v); +BLI_INLINE void imb_transform_scanlines(const TransformUserData *user_data, + int start_scanline, + int num_scanlines, + InterpolationColorFunction interpolation) +{ + const int width = user_data->dst->x; + + float next_line_start_uv[2]; + madd_v2_v2v2fl(next_line_start_uv, user_data->start_uv, user_data->add_y, start_scanline); + + unsigned char *outI = NULL; + float *outF = NULL; + pixel_from_buffer(user_data->dst, &outI, &outF, 0, start_scanline); + + for (int yi = start_scanline; yi < start_scanline + num_scanlines; yi++) { + float uv[2]; + copy_v2_v2(uv, next_line_start_uv); + add_v2_v2(next_line_start_uv, user_data->add_y); + for (int xi = 0; xi < width; xi++) { + 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) { + interpolation(user_data->src, outI, outF, uv[0], uv[1]); + } + add_v2_v2(uv, user_data->add_x); + if (outI) { + outI += 4; + } + if (outF) { + outF += 4; + } + } + } +} + +static void imb_transform_nearest_scanlines(void *custom_data, + int start_scanline, + int num_scanlines) +{ + const TransformUserData *user_data = custom_data; + imb_transform_scanlines(user_data, start_scanline, num_scanlines, nearest_interpolation_color); +} + +static void imb_transform_bilinear_scanlines(void *custom_data, + int start_scanline, + int num_scanlines) +{ + const TransformUserData *user_data = custom_data; + imb_transform_scanlines(user_data, start_scanline, num_scanlines, bilinear_interpolation_color); +} + +static ScanlineThreadFunc imb_transform_scanline_func(const eIMBInterpolationFilterMode filter) +{ + ScanlineThreadFunc scanline_func = NULL; + switch (filter) { + case IMB_FILTER_NEAREST: + scanline_func = imb_transform_nearest_scanlines; + break; + case IMB_FILTER_BILINEAR: + scanline_func = imb_transform_bilinear_scanlines; + break; + } + return scanline_func; +} + +void IMB_transform(struct ImBuf *src, + struct ImBuf *dst, + float transform_matrix[3][3], + struct rctf *src_crop, + const eIMBInterpolationFilterMode filter) +{ + TransformUserData user_data; + user_data.src = src; + user_data.dst = dst; + user_data.src_crop = *src_crop; + imb_transform_calc_start_uv(transform_matrix, user_data.start_uv); + imb_transform_calc_add_x(transform_matrix, user_data.start_uv, src->x, user_data.add_x); + imb_transform_calc_add_y(transform_matrix, user_data.start_uv, src->y, user_data.add_y); + ScanlineThreadFunc scanline_func = imb_transform_scanline_func(filter); + IMB_processor_apply_threaded_scanlines(dst->y, scanline_func, &user_data); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Threaded Image Processing * \{ */ -- cgit v1.2.3