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

Reply via email to