From 0e2136df7bf39b4b612f158130e5258b8cd87fbd Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Fri, 21 Jan 2022 12:53:17 -0600 Subject: [PATCH] libweston: Add a function to test if transformed images need filters If a transformation matrix causes a scale, a rotation not a multiple of 90 degrees or a non-integral translation then textures rendered with it would benefit from bilinear filtering. This test is done in a lazy fashion by examining elements of the matrix to check for a simple pattern that indicates these conditions are met. Signed-off-by: Derek Foreman --- include/libweston/matrix.h | 5 ++ shared/matrix.c | 125 +++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/include/libweston/matrix.h b/include/libweston/matrix.h index 2d3418a7f..a05e31a8e 100644 --- a/include/libweston/matrix.h +++ b/include/libweston/matrix.h @@ -27,6 +27,8 @@ #ifndef WESTON_MATRIX_H #define WESTON_MATRIX_H +#include + #ifdef __cplusplus extern "C" { #endif @@ -66,6 +68,9 @@ int weston_matrix_invert(struct weston_matrix *inverse, const struct weston_matrix *matrix); +bool +weston_matrix_needs_filtering(const struct weston_matrix *matrix); + #ifdef __cplusplus } #endif diff --git a/shared/matrix.c b/shared/matrix.c index 4c35ca3ff..369765b21 100644 --- a/shared/matrix.c +++ b/shared/matrix.c @@ -26,6 +26,7 @@ #include "config.h" +#include #include #include #include @@ -270,3 +271,127 @@ weston_matrix_invert(struct weston_matrix *inverse, return 0; } + +static bool +near_zero(float a) +{ + if (fabs(a) > 0.00001) + return false; + + return true; +} + +static float +get_el(const struct weston_matrix *matrix, int row, int col) +{ + assert(row >= 0 && row <= 3); + assert(col >= 0 && col <= 3); + + return matrix->d[col * 4 + row]; +} + +static bool +near_zero_at(const struct weston_matrix *matrix, int row, int col) +{ + return near_zero(get_el(matrix, row, col)); +} + +static bool +near_pm_one_at(const struct weston_matrix *matrix, int row, int col) +{ + return near_zero(fabs(get_el(matrix, row, col)) - 1.0); +} + +static bool +near_int_at(const struct weston_matrix *matrix, int row, int col) +{ + float el = get_el(matrix, row, col); + + return near_zero(roundf(el) - el); +} + +/* Lazy decompose the matrix to figure out whether its operations will + * cause an image to look ugly without some kind of filtering. + * + * while this is a 3D transformation matrix, we only concern ourselves + * with 2D for this test. We do use some small rounding to try to catch + * sequences of operations that lead back to a matrix that doesn't + * require filters. + * + * We assume the matrix won't be used to transform a vector with w != 1.0 + * + * Filtering will be necessary when: + * a non-integral translation is applied + * non-affine (perspective) translation is in use + * any scaling (other than -1) is in use + * a rotation that isn't a multiple of 90 degrees about Z is present + */ +WL_EXPORT bool +weston_matrix_needs_filtering(const struct weston_matrix *matrix) +{ + /* check for non-integral X/Y translation - ignore Z */ + if (!near_int_at(matrix, 0, 3) || + !near_int_at(matrix, 1, 3)) + return true; + + /* Any transform matrix that matches this will be non-affine. */ + if (!near_zero_at(matrix, 3, 0) || + !near_zero_at(matrix, 3, 1) || + !near_zero_at(matrix, 3, 2) || + !near_pm_one_at(matrix, 3, 3)) + return true; + + /* Check for anything that could come from a rotation that isn't + * around the Z axis: + * [ ? ? 0 ? ] + * [ ? ? 0 ? ] + * [ 0 0 ±1 ? ] + * [ ? ? ? 1 ] + * It's not clear that we'd realistically see a -1 in [2][2], but + * it wouldn't require filtering if we did, so allow it. + */ + if (!near_zero_at(matrix, 0, 2) || + !near_zero_at(matrix, 1, 2) || + !near_zero_at(matrix, 2, 0) || + !near_zero_at(matrix, 2, 1) || + !near_pm_one_at(matrix, 2, 2)) + return true; + + /* We've culled the low hanging fruit, now let's match the only + * matrices left we don't have to filter, before defaulting to + * filtering. + * + * These are a combination of testing rotation and scaling at once: */ + if (near_pm_one_at(matrix, 0, 0)) { + /* This could be a multiple of 90 degree rotation about Z, + * possibly with a flip, if the matrix is of the form: + * [ ±1 0 0 ? ] + * [ 0 ±1 0 ? ] + * [ 0 0 1 ? ] + * [ 0 0 0 1 ] + * Forcing ±1 excludes non-unity scale. + */ + if (near_zero_at(matrix, 1, 0) && + near_zero_at(matrix, 0, 1) && + near_pm_one_at(matrix, 1, 1)) + return false; + } + if (near_zero_at(matrix, 0, 0)) { + /* This could be a multiple of 90 degree rotation about Z, + * possibly with a flip, if the matrix is of the form: + * [ 0 ±1 0 ? ] + * [ ±1 0 0 ? ] + * [ 0 0 1 ? ] + * [ 0 0 0 1 ] + * Forcing ±1 excludes non-unity scale. + */ + if (near_zero_at(matrix, 1, 1) && + near_pm_one_at(matrix, 1, 0) && + near_pm_one_at(matrix, 0, 1)) + return false; + } + + /* The matrix wasn't "simple" enough to classify with dumb + * heuristics, so recommend filtering */ + return true; +}