My blog has been moved to ariya.ofilabs.com.

Wednesday, October 06, 2010

color inversion for web pages

Something I worked on during my last few weeks with Qualcomm was color inversion for the web browser in Android. The patch was then integrated by Enrico (see the diff) because I switched job.

Later on this feature was also pulled into CyanogenMod. Thus, if you are running CyanogenMod 6 on your shiny Android device, you can try this feature already! In the web browser, just pick the Settings menu and then scroll a bit until you see a checkbox for Invert Color. You should get something like the captured screens below:

The idea behind this is to reduce the power consumption of Organic LED display, because on mostly-white web page, it hungrily grabs to 3x more power compared to LCD. This is similar to Jeff's trick of applying color filter in SurfaceFlinger.

Doing it at the user-space level gives one advantage: we can keep the embedded images non-inverted. Blindly inverting the entire screen would result in web pages look rather funky, especially those new sites with photos to accompany the articles. Of course, this hackish approach will not work 100%, (hint: double XOR?) since the web designer likely never intend the page to be color inverted. However it seems to work most of the time, at least with pages which do not have ueberartistic look-and-feel.

Faithful followers of my blog know that I already played with the color inversion ages ago, in the form of giving night mode appearance for QWebView. Thus, it happened that in one afternoon break I played with QtWebKit to do something similar to this selective color inversion.

Now, in the case of Android WebKit, the effect was rather easy to achieve. This is because Skia's SkCanvas is basically an interface which can be subclassed easily, while none of the functions in QPainter is virtual. The trick using Skia was to use a proxy canvas (a slight variation of Skia's built-in SkProxyCanvas) which I invented for this purpose but somehow also useful for another feature.

For QtWebKit however, we need to tweak it with some QPaintEngine voodoo. Check out the code at the usual X2 repository, find it under webkit/nightcapture (it still has some rooms for improvement, left as exercises for the adventurous readers). For simplicity, I made it as capturing tool so you need to pass a URL and the output filename. Unless you do something wrong, expect to get something like this:

The trick is the same as my approach for the Android patch: invert the colors of each image before drawing it, at the end invert the entire viewport. This seems like slow, but it's the best compromise I found out working with most sites. Note how we skip inverting any brush pixmap as usually brush is for the tiled background (and thus we want to keep it that way, i.e. not inverted). You can do some fancy magic with clipping and whatnot, but I doubt the end result is much better. Again, this whole stuff is a hack anyway and there will be always corner cases which will not work, no matter what approach you pick, thus there is no need to make it more complicated that it should be.

Just like they say, we code for life, i.e. the battery life.

6 comments:

Kåre said...

Wouldn't a custom style-sheet achieve the same?

Dotan Cohen said...

The fact that it doesn't invert image colours is terrific. I requested this very feature for the KDE "Invert" effect:

Invert effect: don't invert images
https://bugs.kde.org/show_bug.cgi?id=224211

Ariya Hidayat said...

@Kåre: Custom style is very complicated. In short: it's hard to achieve the same without sacrificing the performance. The best is to set a fixed bright text color and dark background color, but that tend to break many sites even more.

Ariya Hidayat said...

@Dotan: It will be very hard to do it at the window manager level, as discussed in the bug.

Alessandro Portale said...

I have no clue about Webkit, so forgive my naive question. Isn't there a central css processor that could invert all incoming colors on-the-fly?

Ariya Hidayat said...

@Alessandro: Unfortunately, not (yet).