Setting contrast in X.org

Use-case: Enhance contrast when reading scanned document and document viewer does not support contrast adjustment.

<note warning>XF86VidModeSetGammaRamp is broken in recent X.org. I have created an ugly patch for redshift (apply, compile, run “./src/redshift -O 3000 white_threshold black_treshold”) to work around it.</note>

I was unable to find a tool that can fix this (tried xcalib, xgamma, xrandr and redshift). So I have created one myself. Unfortunately I don't know how to set contrast per window, so your whole X session is affected.

Compile:

gcc -std=gnu99 -O2 -g -ggdb3 -I/usr/X11R6/include -L/usr/X11R6/lib -lX11 -lXxf86vm -lXext -o xlevels xlevels.c

Usage:

./xlevels white_threshold black_treshold

(it's the same syntax like in GIMP)

Examples:

./xlevels 100 255
./xlevels 50 230
xcalib -c
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <string.h>

#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/xf86vmode.h>
#include <math.h>

int main(int argc, char **argv) {

  /* XF86 substitution table */
  uint16_t rtable[256];


  /* read input */

  uint32_t lowcut = atoi(argv[1]);
  uint32_t highcut = (255-atoi(argv[2]));

  float slope = 257;

  float range = 65535 - (lowcut*257 + highcut*257);

  printf("range = %f\n", range);

  if(range != 0) {
    slope *= 65535/range;
  }

  printf("slope = %f\n", slope);


  /* compute profile */

  for(int i=0; i<256; i++) {

    rtable[i] = (uint16_t) (slope*(i-lowcut));

    if(i<=lowcut) {
      rtable[i] = 0;
    }

    if(i>=255-highcut) {
      rtable[i] = 0xffff;
    }

    printf("%05i ", rtable[i]);
    if(i%16 == 15 && i>1) {
      printf("\n");
    }

  }


  /* X11 magic */

  Display *dpy = NULL;
  dpy = XOpenDisplay (NULL);
  int screen = DefaultScreen (dpy);

  XF86VidModeSetGammaRamp (dpy, screen, 256, rtable, rtable, rtable);

}