Dear developers, On the borders of my screen, my stylus have some distortion. So I decide to edit some sources to fix the problem. I contacted Mr. Hutterer who helped me to make this mail. To fix it I added an opposite distortion with a polynomial function. Each polynomial depend on 4 parameters. There is 4 polynomials, one per border. - I didn't manage to make floating point parameters - I added some functions to solve a linear system but I'm sure that including a mathematical library should be better. - I'm afraid that my calculus with variables of type double slows the execution. But I didn't manage to measure it.
For example on my laptop I use the following parameters : > xinput set-prop "Wacom ISDv4 EC Pen stylus" --type=int "Wacom Tablet Border Distortion Top Y" 100 0 55 68 > xinput set-prop "Wacom ISDv4 EC Pen stylus" --type=int "Wacom Tablet Border Distortion Top X" 80 0 20 26 Yours faithfully, Mario Geiger >From efa7dbb3a0bd54e3b58fba9541d54669c1224d5e Mon Sep 17 00:00:00 2001 From: Mario Geiger <geiger.ma...@gmail.com> Date: Mon, 24 Aug 2015 23:29:35 +0200 Subject: [PATCH] distortion --- include/wacom-properties.h | 6 ++ src/wcmCommon.c | 52 ++++++++-- src/wcmXCommand.c | 232 +++++++++++++++++++++++++++++++++++++++++++++ src/xf86WacomDefs.h | 10 ++ 4 files changed, 293 insertions(+), 7 deletions(-) diff --git a/include/wacom-properties.h b/include/wacom-properties.h index b845083..e25a944 100644 --- a/include/wacom-properties.h +++ b/include/wacom-properties.h @@ -27,6 +27,12 @@ /* 32 bit, 4 values, top x, top y, bottom x, bottom y */ #define WACOM_PROP_TABLET_AREA "Wacom Tablet Area" +/* 32 bit, 4 values, border width, border offset, point physical, point logical */ +#define WACOM_PROP_TABLET_DISTORTION_TOP_X "Wacom Tablet Border Distortion Top X" +#define WACOM_PROP_TABLET_DISTORTION_TOP_Y "Wacom Tablet Border Distortion Top Y" +#define WACOM_PROP_TABLET_DISTORTION_BOTTOM_X "Wacom Tablet Border Distortion Bottom X" +#define WACOM_PROP_TABLET_DISTORTION_BOTTOM_Y "Wacom Tablet Border Distortion Bottom Y" + /* 8 bit, 1 value, [0 - 3] (NONE, CW, CCW, HALF) */ #define WACOM_PROP_ROTATION "Wacom Rotation" diff --git a/src/wcmCommon.c b/src/wcmCommon.c index 9408f42..00c5dc5 100644 --- a/src/wcmCommon.c +++ b/src/wcmCommon.c @@ -438,6 +438,21 @@ static void sendCommonEvents(InputInfoPtr pInfo, const WacomDeviceState* ds, sendWheelStripEvents(pInfo, ds, first_val, num_vals, valuators); } +static double distortionCorrectionInBorders(double coord, double border, double* polynomial) +{ + if (coord < border) { + double x = coord; + int i; + + coord = 0.0; + for (i = 0; i < 5; ++i) { + coord *= x; + coord += polynomial[i]; + } + } + return coord; +} + /* rotate x and y before post X inout events */ void wcmRotateAndScaleCoordinates(InputInfoPtr pInfo, int* x, int* y) { @@ -446,19 +461,42 @@ void wcmRotateAndScaleCoordinates(InputInfoPtr pInfo, int* x, int* y) DeviceIntPtr dev = pInfo->dev; AxisInfoPtr axis_x, axis_y; int tmp_coord; + double dcoord; /* scale into on topX/topY area */ axis_x = &dev->valuator->axes[0]; axis_y = &dev->valuator->axes[1]; /* Don't try to scale relative axes */ - if (axis_x->max_value > axis_x->min_value) - *x = xf86ScaleAxis(*x, axis_x->max_value, axis_x->min_value, - priv->bottomX, priv->topX); - - if (axis_y->max_value > axis_y->min_value) - *y = xf86ScaleAxis(*y, axis_y->max_value, axis_y->min_value, - priv->bottomY, priv->topY); + if (axis_x->max_value > axis_x->min_value) { + dcoord = (*x - priv->topX) / (double)(priv->bottomX - priv->topX); + dcoord = distortionCorrectionInBorders(dcoord, priv->distortion_topX_border, priv->distortion_topX_poly); + dcoord = 1.0 - dcoord; + dcoord = distortionCorrectionInBorders(dcoord, priv->distortion_bottomX_border, priv->distortion_bottomX_poly); + dcoord = 1.0 - dcoord; + + //*x = dcoord * (priv->bottomX - priv->topX) + priv->topX; + //*x = xf86ScaleAxis(*x, axis_x->max_value, axis_x->min_value, + // priv->bottomX, priv->topX); + + *x = round(dcoord * (axis_x->max_value - axis_x->min_value) + axis_x->min_value); + if (*x < axis_x->min_value) *x = axis_x->min_value; + if (*x > axis_x->max_value) *x = axis_x->max_value; + } + + if (axis_y->max_value > axis_y->min_value) { + dcoord = (*y - priv->topY) / (double)(priv->bottomY - priv->topY); + dcoord = distortionCorrectionInBorders(dcoord, priv->distortion_topY_border, priv->distortion_topY_poly); + dcoord = 1.0 - dcoord; + dcoord = distortionCorrectionInBorders(dcoord, priv->distortion_bottomY_border, priv->distortion_bottomY_poly); + dcoord = 1.0 - dcoord; + //*y = dcoord * (priv->bottomY - priv->topY) + priv->topY; + //*y = xf86ScaleAxis(*y, axis_y->max_value, axis_y->min_value, + // priv->bottomY, priv->topY); + *y = round(dcoord * (axis_y->max_value - axis_y->min_value) + axis_y->min_value); + if (*y < axis_y->min_value) *y = axis_y->min_value; + if (*y > axis_y->max_value) *y = axis_y->max_value; + } /* coordinates are now in the axis rage we advertise for the device */ diff --git a/src/wcmXCommand.c b/src/wcmXCommand.c index 346ff61..2a620f0 100644 --- a/src/wcmXCommand.c +++ b/src/wcmXCommand.c @@ -34,6 +34,8 @@ #endif static void wcmBindToSerial(InputInfoPtr pInfo, unsigned int serial); +static int distortionCorrectionComputePolynomial(double p, double a, double h, double d, double* poly); + /***************************************************************************** * wcmDevSwitchModeCall -- @@ -82,6 +84,10 @@ int wcmDevSwitchMode(ClientPtr client, DeviceIntPtr dev, int mode) static Atom prop_devnode; static Atom prop_rotation; static Atom prop_tablet_area; +static Atom prop_tablet_distortion_topX; +static Atom prop_tablet_distortion_topY; +static Atom prop_tablet_distortion_bottomX; +static Atom prop_tablet_distortion_bottomY; static Atom prop_pressurecurve; static Atom prop_serials; static Atom prop_serial_binding; @@ -227,6 +233,18 @@ void InitWcmDeviceProperties(InputInfoPtr pInfo) prop_tablet_area = InitWcmAtom(pInfo->dev, WACOM_PROP_TABLET_AREA, XA_INTEGER, 32, 4, values); } + if (!IsPad(priv)) { + /* border_width border_offset point_physical point_logical */ + values[0] = 0; + values[1] = 0; + values[2] = 0; + values[3] = 0; + prop_tablet_distortion_topX = InitWcmAtom(pInfo->dev, WACOM_PROP_TABLET_DISTORTION_TOP_X, XA_INTEGER, 32, 4, values); + prop_tablet_distortion_topY = InitWcmAtom(pInfo->dev, WACOM_PROP_TABLET_DISTORTION_TOP_Y, XA_INTEGER, 32, 4, values); + prop_tablet_distortion_bottomX = InitWcmAtom(pInfo->dev, WACOM_PROP_TABLET_DISTORTION_BOTTOM_X, XA_INTEGER, 32, 4, values); + prop_tablet_distortion_bottomY = InitWcmAtom(pInfo->dev, WACOM_PROP_TABLET_DISTORTION_BOTTOM_Y, XA_INTEGER, 32, 4, values); + } + values[0] = common->wcmRotate; if (!IsPad(priv)) { prop_rotation = InitWcmAtom(pInfo->dev, WACOM_PROP_ROTATION, XA_INTEGER, 8, 1, values); @@ -717,6 +735,66 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, priv->bottomX = values[2]; priv->bottomY = values[3]; } + } else if (property == prop_tablet_distortion_topX) + { + INT32 *values = (INT32*)prop->data; + + if (prop->size != 4 || prop->format != 32) + return BadValue; + + if (!checkonly) + { + /* border_width border_offset point_physical point_logical */ + priv->distortion_topX_border = values[0]/1000.0; + distortionCorrectionComputePolynomial( + values[1]/1000.0, values[2]/1000.0, values[3]/1000.0, values[0]/1000.0, + priv->distortion_topX_poly); + } + } else if (property == prop_tablet_distortion_topY) + { + INT32 *values = (INT32*)prop->data; + + if (prop->size != 4 || prop->format != 32) + return BadValue; + + if (!checkonly) + { + /* border_width border_offset point_physical point_logical */ + priv->distortion_topY_border = values[0]/1000.0; + distortionCorrectionComputePolynomial( + values[1]/1000.0, values[2]/1000.0, values[3]/1000.0, values[0]/1000.0, + priv->distortion_topY_poly); + } + } else if (property == prop_tablet_distortion_bottomX) + { + INT32 *values = (INT32*)prop->data; + + if (prop->size != 4 || prop->format != 32) + return BadValue; + + if (!checkonly) + { + /* border_width border_offset point_physical point_logical */ + priv->distortion_bottomX_border = values[0]/1000.0; + distortionCorrectionComputePolynomial( + values[1]/1000.0, values[2]/1000.0, values[3]/1000.0, values[0]/1000.0, + priv->distortion_bottomX_poly); + } + } else if (property == prop_tablet_distortion_bottomY) + { + INT32 *values = (INT32*)prop->data; + + if (prop->size != 4 || prop->format != 32) + return BadValue; + + if (!checkonly) + { + /* border_width border_offset point_physical point_logical */ + priv->distortion_bottomY_border = values[0]/1000.0; + distortionCorrectionComputePolynomial( + values[1]/1000.0, values[2]/1000.0, values[3]/1000.0, values[0]/1000.0, + priv->distortion_bottomY_poly); + } } else if (property == prop_pressurecurve) { INT32 *pcurve; @@ -1073,4 +1151,158 @@ wcmBindToSerial(InputInfoPtr pInfo, unsigned int serial) } +/* P A = L U + * P : permutation matrix + * L : lower matrix + * U : upper matrix +*/ +static void +lu_decomposition(int n, const double* A, double* P, double* L, double* U) +{ + int k,i,r; + double m; + + // P,L = identity + for (i = 0; i < n*n; ++i) P[i] = L[i] = 0.0; + for (i = 0; i < n*n; i += n+1) P[i] = L[i] = 1.0; + + // U = A + for (i = 0; i < n*n; ++i) U[i] = A[i]; + + for (k = 0; k < n; ++k) { + // find maximum in column k + r = k; + m = U[r*n+k]; + for (i = k; i < n; ++i) if (U[i*n+k] > m) { + r = i; + m = U[i*n+k]; + } + + if (m > 0.0) { + // swap lines r,k + if (r != k) { + for (i = 0; i < n; ++i) { + m = U[r*n+i]; + U[r*n+i] = U[k*n+i]; + U[k*n+i] = m; + m = P[r*n+i]; + P[r*n+i] = P[k*n+i]; + P[k*n+i] = m; + } + for (i = 0; i < k; ++i) { + m = L[r*n+i]; + L[r*n+i] = L[k*n+i]; + L[k*n+i] = m; + } + } + + // make 0's + for (i = k+1; i < n; ++i) { + L[i*n+k] = U[i*n+k] / U[k*n+k]; + for (r = 0; r < n; ++r) U[i*n+r] -= L[i*n+k] * U[k*n+r]; + } + } + } +} + +/* LUx = b */ +static int +solve_lu(int n, const double* L, const double* U, const double* b, double* x) +{ + int k, i; + double s; + double* y = (double*)malloc(sizeof(double)*n); + + // Ly = b + for (k = 0; k < n; ++k) { + s = 0.0; + for (i = 0; i < k; ++i) s += L[k*n+i] * y[i]; + y[k] = b[k] - s; + } + + // Ux = y + for (k = n-1; k >= 0; --k) { + s = 0.0; + for (i = n-1; i > k; --i) s += U[k*n+i] * x[i]; + if (U[k*n+k] == 0.0) { + free(y); + return 1; + } + x[k] = (y[k] - s) / U[k*n+k]; + } + free(y); + return 0; +} + + +/* Ax = b */ +static int +solve_ls(int n, const double* A, const double* b, double* x) +{ + int i,k; + double *P,*L,*U,*Pb; + P = (double*)malloc(sizeof(double)*n*n); + L = (double*)malloc(sizeof(double)*n*n); + U = (double*)malloc(sizeof(double)*n*n); + Pb = (double*)malloc(sizeof(double)*n); + lu_decomposition(n, A, P, L, U); + for (i = 0; i < n; ++i) { + Pb[i] = 0; + for (k = 0; k < n; ++k) { + Pb[i] += P[i*n+k] * b[k]; + } + } + i = solve_lu(n, L, U, Pb, x); + free(P); + free(L); + free(U); + free(Pb); + return i; +} + +static int +distortionCorrectionComputePolynomial(double p, double a, double h, double d, double* poly) +{ + /* p = offset at the border + * (a,h) = dirtortion point + * d = width of the border zone + * + * poly = {c4, c3... c0} + */ + + /* F(x) = c4 x^4 + c3 x^3 + c2 x^2 + c1 x + c0 + * + * Constraints => Matrix + * F(0) = p => 0 0 0 0 1 : p + * F(d) = d => d^4 d^3 d^2 d 1 : d + * F(a) = h => a^4 a^3 a^2 a 1 : h + * F'(d) = 1 => 4d^3 3d^2 2d 1 0 : 1 + * F'(a) = 1 => 4a^3 3a^2 2a 1 0 : 1 + */ + int r; + + const double Matrix[25] = { + 0, 0, 0, 0, 1, + d*d*d*d, d*d*d, d*d, d, 1, + a*a*a*a, a*a*a, a*a, a, 1, + 4*d*d*d, 3*d*d, 2*d, 1, 0, + 4*a*a*a, 3*a*a, 2*a, 1, 0 + }; + const double rhs[5] = { + p, + d, + h, + 1, + 1 + }; + + r = solve_ls(5, Matrix, rhs, poly); + if (r != 0) { + // set to F(x) = x + poly[0] = poly[1] = poly[2] = poly[4] = 0.0; + poly[3] = 1.0; + } + return r; +} + /* vim: set noexpandtab tabstop=8 shiftwidth=8: */ diff --git a/src/xf86WacomDefs.h b/src/xf86WacomDefs.h index 1575960..45679ec 100644 --- a/src/xf86WacomDefs.h +++ b/src/xf86WacomDefs.h @@ -262,6 +262,16 @@ struct _WacomDeviceRec unsigned int cur_serial; /* current serial in prox */ int cur_device_id; /* current device ID in prox */ + /* distortion */ + double distortion_topX_border; + double distortion_topY_border; + double distortion_bottomX_border; + double distortion_bottomY_border; + double distortion_topX_poly[5]; + double distortion_topY_poly[5]; + double distortion_bottomX_poly[5]; + double distortion_bottomY_poly[5]; + /* button mapping information * * 'button' variables are indexed by physical button number (0..nbuttons) -- 2.1.4
------------------------------------------------------------------------------
_______________________________________________ Linuxwacom-devel mailing list Linuxwacom-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxwacom-devel