Nexus Sの緑がかったディスプレイを何とかする(OpenGL編)

Nexus S のディスプレイは、輝度を低くしたときに何となく緑がかっています。これは、Nexus One との比較写真を見れば一目瞭然です。

最初は有機ELの個体差なのかと思ったのですが、周りの人が持っている Nexus S 3〜4台を見比べてみても、どれも同様の色合いをしています。ディスプレイの輝度が高い時は特に問題ないのですが、輝度を低くした時や明るさを自動調整に設定した時に顕著に表れます。

というわけで、ディスプレイのホワイトバランスを調整してみることにしました。まず、SurfaceFlinger に手を入れる方法を試してみました。方法としては、Android SurfaceFlinger tricks for fun and profit にて紹介されているバッテリー残量が少なくなった時に特定の色のみを発光させることで消費電力を抑えられるのではないかという実験で使われていたものを参考にさせてもらいました。

画面描画の直前に OpenGL のフィルタをかけることで、色調整を行います。実際のコード差分が下記です。

diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 64eed4b..18a43ff 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -373,7 +373,12 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
     uint32_t height = texture.height;
 
     GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
-    if (UNLIKELY(s.alpha < 0xFF)) {
+
+    bool noEffect = false;
+    int renderColorR = 1000;
+    int renderColorG = 750;
+    int renderColorB = 970;
+
+    if (UNLIKELY(s.alpha < 0xFF) && noEffect) {
         const GLfloat alpha = s.alpha * (1.0f/255.0f);
         if (mPremultipliedAlpha) {
             glColor4f(alpha, alpha, alpha, alpha);
@@ -383,7 +388,7 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
         glEnable(GL_BLEND);
         glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);
         glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-    } else {
+    } else if (noEffect) {
         glColor4f(1, 1, 1, 1);
         glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
         if (needsBlending()) {
@@ -392,6 +397,16 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
         } else {
             glDisable(GL_BLEND);
         }
+    } else {
+        // Apply a render effect, which is simple color masks for now.
+        GLenum env, src;
+        env = GL_MODULATE;
+        src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
+        const GGLfixed alpha = (s.alpha << 16)/255;
+        glColor4x(alpha*renderColorR/1000, alpha*renderColorG/1000, alpha*renderColorB/1000, alpha);
+        glEnable(GL_BLEND);
+        glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);
+        glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env);
     }
 
     /*

これをコンパイルして元の libsurfaceflinger.so と入れ替えることで、だいぶまともな色で表示されるようになりました。ただし、この方法ではディスプレイ輝度にかかわらず同じフィルタがかかってしまうため、明るさを最大にした時に今度は赤っぽい色合いになってしまいました。また、スクリーンショットを撮った時にもフィルタによる調整後の画像が出力されてしまいます。

有機ELは、バックライトで後ろから光を当てるのではなく画素自体が発光するため、黒が黒く見えるコントラストの高い画質で評判がよいのに非常に勿体ないです。ところが不思議なのは、ネット上を見回しても Nexus S のこの問題について文句を言っている人がいないことです。みんな気にならないのでしょうか…

というわけで、別の方法を考えてあるので次回はそちらを試して見ようと思います。



このエントリーのはてなブックマーク (-)