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

Nexus S のディスプレイ輝度を低くしたときに何となく緑がかっているのを直す話の続きです。

前回、SurfaceFlinger に手を入れる方法を試しましたが、輝度による特性の変化を考慮していなかったのと、ディスプレイ表示の直前にフィルタをかけるのでスクリーンショット画像の色合いまで変わってしまう問題があって別の方法を模索していました。

Video Driverまわりはプロプライエタリだと勝手に思っていたのですが、ソースが非公開なのはアクセラレータ周りで、ディスプレイパネルドライバは Samsung によって公開されていました。ソースは以下にあったので、すぐにgit clone して読み進めました。

http://android.git.kernel.org/?p=kernel/samsung.git

通常、パネルによって発色の特性は様々なので、ガンマを補正するための値が設定されています。Nexus Sでもこれは同様で、s3cfb_tl2796.c に gamma_lookup 関数が用意されていて、引数として brightness, RGB種別とその値を渡すことで、補正された値が返ってきます。で、どうやら この commit から gamma_table が予め計算された輝度ごとの値から、計算して求める方式に変更になっていました。

drivers/video/samsung/s3cfb_tl2796.c
static u32 gamma_lookup(struct s5p_lcd *lcd, u8 brightness, u32 val, int c)
{
	int i;
	u32 bl = 0;
	u32 bh = 0;
	u32 vl = 0;
	u32 vh;
	u32 b;
	u32 ret;
	u64 tmp;
	struct s5p_panel_data *pdata = lcd->data;
	const struct tl2796_gamma_adj_points *bv = lcd->gamma_adj_points;

	if (!val) {
		b = 0;
	} else {
		tmp = bv->v255 - bv->v0;
		tmp *= brightness;
		do_div(tmp, 255);

		tmp *= (val - bv->v0);
		do_div(tmp, bv->v255 - bv->v0);
		b = tmp + bv->v0;
	}

	for (i = 0; i < pdata->gamma_table_size; i++) {
		bl = bh;
		bh = pdata->gamma_table[i].brightness;
		if (bh >= b)
			break;
	}
	vh = pdata->gamma_table[i].v[c];
	if (i == 0 || (b - bl) == 0) {
		ret = vl = vh;
	} else {
		vl = pdata->gamma_table[i - 1].v[c];
		tmp = (u64)vh * (b - bl) + (u64)vl * (bh - b);
		do_div(tmp, bh - bl);
		ret = tmp;
	}

	pr_debug("%s: looking for %3d %08x c %d, %08x, "
		"found %08x:%08x, v %7d:%7d, ret %7d\n",
		__func__, brightness, val, c, b, bl, bh, vl, vh, ret);

	return ret;
}

この式を変更すればいいことは分かるのですが、調整するにはそれなりの時間と根気が必要です。そこで、herring-panel.c に設定されている gamma_table をいじって、お茶を濁すことにしました。以下が差分です。

diff --git a/arch/arm/mach-s5pv210/herring-panel.c b/arch/arm/mach-s5pv210/herring-panel.c
index c5f9775..0e44072 100755
--- a/arch/arm/mach-s5pv210/herring-panel.c
+++ b/arch/arm/mach-s5pv210/herring-panel.c
@@ -164,8 +164,8 @@ static const struct tl2796_gamma_adj_points gamma_adj_points = {
 };
 
 static const struct gamma_entry gamma_table[] = {
-       {       BV_0, { 4200000, 4200000, 4200000, }, },
-       {          1, { 3994200, 4107600, 3910200, }, },
+       {       BV_0, { 4200000, 4050000, 4130000, }, },
+       {          1, { 3994200, 4000000, 3910200, }, },
        {          2, { 3467167, 3789169, 3488171, }, },
        {    7000000, { 3310578, 3407998, 3280696, }, },
        {   14000000, { 3234532, 3291183, 3181487, }, },

というわけで修正済みの kernel をここに置いておきます。

まだ完璧ではないものの、とりあえず気にならないレベルにはなりました。グラフィック周りに詳しい方がちゃんと調整してくれるといいんですが…

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 のこの問題について文句を言っている人がいないことです。みんな気にならないのでしょうか…

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

Nexus S のホーム画面を晒してみる

たまにはホーム画面でも晒してみます。

iPhone しか使ったことがないとなかなか分からないかもしれませんが、Android ではホーム画面も一つのアプリケーションで自由に入れ替えることができます。有名なものだと、ADW.Launcher, Launcher Pro といったものがあるのですが、いろいろ試して最近は lifehacker でも紹介されていた「Zeam Launcher」に落ち着いています。機能は必要充分で何より動作が軽いのでお勧めです。

» Zeam Launcher (Android Market)

スクリーンは3面しか使ってません。基本的に透過フェチなので、ウィジェット類はすべて背景透明です。また、壁紙は Nexus 標準の Live Wallpaper でアニメーションするものを使っています。

2011/01/14追記:
ウィジェットの詳細を教えて欲しいと言われたので補足します。

左画面から、

zsh 環境で Android OS をビルドする際に envsetup.sh を使う

Android OS をビルドする際にとても便利な機能を提供してくれる envsetup.sh なのですが、zsh 環境では以下のようなエラーになってしまいます。

% source build/envsetup.sh
build/envsetup.sh:1: no matches found: vendor/*/vendorsetup.sh

これを回避するには次のように "unsetopt NOMATCH" を設定します。

% unsetopt NOMATCH

これで mm や croot などの便利コマンドが使えるようになります。

% source build/envsetup.sh
including device/htc/passion/vendorsetup.sh
including device/samsung/crespo/vendorsetup.sh
% help
Invoke ". build/envsetup.sh" from your shell to add the following functions to 
your environment:
- croot:   Changes directory to the top of the tree.
- m:       Makes from the top of the tree.
- mm:      Builds all of the modules in the current directory.
- mmm:     Builds all of the modules in the supplied directories.
- cgrep:   Greps on all local C/C++ files.
- jgrep:   Greps on all local Java files.
- resgrep: Greps on all local res/*.xml files.
- godir:   Go to the directory containing a file.

Look at the source to view more functions. The complete list is:
 add_lunch_combo cgrep check_product check_variant choosecombo chooseproduct
 choosetype choosevariant cproj croot findmakefile gdbclient get_abs_build_var
 get_build_var getbugreports getprebuilt gettop godir help isviewserverstarted
 jgrep lunch m mm mmm pid print_lunch_menu printconfig resgrep runhat runtest
 set_java_home set_sequence_number set_stuff_for_environment setpaths settitle
 smoketest startviewserver stopviewserver systemstack tapas tracedmdump

Nexus S 用のカーネルをビルドする

先日、話題のおせちどころではないスカスカ状態の Nexus S が届いたわけですが、あちらは知人用で自分用のは昨年末に入手していました。配送途中の盗難ということで保険が効いて、輸入代行業者から再注文してくれることになったので、ほっとしています。

その Nexus S ですが Nexus One が出たときほどのインパクトもなく、実際に触った感じもファインチューニングくらいの印象で特に感動するような点もありません。開発機として使うのでなければ、わざわざ Nexus One から乗り換えるほどではない感じです。ただ、画面が大きくて軽いのとタッチパネルの精度が上がっているので、自分は今は S をメインに使っています。

Nexus S

さて、早速 Nexus S 用のカーネルをビルドする方法について説明します。Nexus S 用カーネルのソースコードは、android.git.kernel.org で公開されています。まずはここからソースコードを取得します。この取得は結構時間がかかって、下手したら1時間以上かかるので気長に待ちましょう。

$ git clone git://android.git.kernel.org/kernel/samsung.git

ソースが無事に取得できたら、.configを初期化します。herring は、Nexus S に搭載されているボード(基盤)の名称でこれが Nexus S 用のおまじないです。

$ cd samsung
$ make ARCH=arm herring_defconfig

次にカーネルのビルドに入ります。クロスコンパイルのための toolchain の場所を指定します。この toolchain は、Android 本体のソースコードに含まれています。

$ make -j2 ARCH=arm CROSS_COMPILE=/path/to/prebuilt/linux-x86/toolchain/arm-eabi-
4.4.3/bin/arm-eabi-

arch/arm/boot/zImage ができていればビルド成功です。次にこれをブート用のイメージにします。今回は、stock の boot.img から kernel のみを入れ替えることにします。

$ split_bootimg.pl boot.img
Page size: 4096 (0x00001000)
Kernel size: 2799472 (0x002ab770)
Ramdisk size: 142845 (0x00022dfd)
Second size: 0 (0x00000000)
Board name:   
Command line: console=ttyFIQ0 no_console_suspend
Writing boot.img-kernel ... complete.
Writing boot.img-ramdisk.gz ... complete.

$ mkbootimg --cmdline 'console=ttyFIQ0 no_console_suspend' --kernel zImage 
--base 0x30000000 --pagesize 4096 --ramdisk boot.img-ramdisk.gz -o my-boot.img

それでは、実際に実機に転送してブートしてみましょう。注意するべき点としては、いきなり my-boot.img を flash してしまわないことです。試しに起動するだけなら、fastboot コマンドで boot パーティションに変更を加えることなくテストすることができます。

Volume Upボタンを押しながら電源を入れて、ブートローダーモードにした後にUSBケーブルを接続して以下のコマンドを実行します。

$ fastboot boot my-boot.img

問題なく起動して動作するようだったら、boot パーティションに書き込みます。

$ fastboot flash boot my-boot.img

まずは素のカーネルで起動するところまで確認できました。ここが最初のスタート地点です。ここから後はクロックアップするなり、スケジューラを変更するなり、好きなだけチューニングすることができます。

海外から購入した「Nexus S」が途中で盗難にあった話 [進行中]

昨年末に「Nexus S」を海外の輸入代行サービスを使って購入しました。
ようやく今日その荷物が届いたので、箱を開けて中身を確認したのですが、Nexus S本体が綺麗に抜き取られていました。不思議なことにそれ以外のケーブルやバッテリーはそのまま残っていました。バッテリーなしでどうやって使うんでしょうか?また、USBケーブルの封も剥がされていて一度使ったように見受けられます。

以前、iPad発売直後にも同様の事件があったと思いますが、まさか自分が同じ目に遭うとはかなりショックです。同じように日本から購入したいと思っている人も多くいると思うので、誰かの役に立てばと思い、ここに書き記しておくことにします。

購入からこれまでの流れは、次のような感じです。

12/17 HopShopGo の BuyForMe サービスで発注
12/19 BestBuy サイトにて代理購入
12/23 商品が ComGateway に到着
12/29 日本に向けて発送(DHL)
01/03 成田税関
01/04 自宅に配達&盗難発覚 ← イマココ

23日〜29日の間がやけに開いているのが非常に気になりますが、現時点ではまだどこで盗まれたのかは分かりません。もしかしたら、可能性は低いと思いますが BestBuy から到着した時点でなかったのかもしれません。

今回使った HopShopGo というサービスですが、海外のショッピングサイトでの購入と日本への発送を代行してくれる業者で、手数料として10%ほど取られるのですが、アメリカ国内の州税と日本の関税、消費税がかからないので比較的安価に利用することができます。決済を PayPal、海外宛ての配送は DHL が行っているので、いずれかの保険が利用できるはずと考えています。(ComGateway 側の従業員の可能性もありますが、まぁ非は認めないと思うので…)

とりあえず、HopShopGo に問い合わせてみることにしました。FAQによると問題が発生した場合は、ComGateway のカスタマーサービスが対応するとのことです。ライブチャットとメールでの問い合わせを受け付けているようなのですが、コールセンターが GMT+8 な場所(おそらくシンガポール)にあるらしく既に営業時間を過ぎていたのでメールで問い合わせることにしました。日本語が通じないといけないので、念のため英語で下記のようなメールに写真を添付して送りました。どんな回答が返ってくるのかドキドキです。

Subject: Possible theft of purchased item

Hi,

I have purchased "Nexus S", a smartphone, through your BuyForMe
service from BestBuy.
The item arrived today, and I found, after opening the box, the phone
itself had been removed.
Where is my phone???
The accessories were all there, except that the package of the USB
cable had been opened.

I would like to have a complete set of Nexus S, or refund this set.
Let me know what you can do about this matter ASAP.

-----------------------------------------------
Order No. xxx-xxx-xxxxxx

Shipment Reference No.:  xxx-xxx-xxxxxxxxxx
Shipping Method: express
Shipment Date: December 25, 2010

Total Number of Packages: 1
Total Chargable Weight(kg): 1.0
Total Declared Value: US$ 530.00
-----------------------------------------------

Thanks,
Hidetaka Yamashita

今のところこの返事待ちですが、また進展があったら追記します。

Android用日本語フォントパッケージ

ここ最近は、「MIUI」というカスタムROMがお気に入りで Nexus One にはこれを入れて使っています。中国のDev teamによって開発されているのですが、アップデートが頻繁で毎週のように新バージョンがリリースされています。アップデートが盛んなのはいい事なのですが、そのたびにフォントの入れ替えを行わなくてはいけなくてちょっと面倒です。MIUI に限らず、カスタムROMを使っている大抵の人はそう思っているんじゃないかと思います。
そこで、自分はフォント入れ替え用のパッケージを作って、カスタムROMの zip を適用後にフォント入れ替え用 zip を再適用しています。手元にいくつかフリーフォントで作ったフォント入れ替え用 zip があるので、画面サンプルと一緒にここに置いておきます。

MigMix 1M - migmix-1m-fonts-signed.zip

モトヤLシーダ3等幅 - mtlc3m-fonts-signed.zip

モトヤLマルベリ3等幅 - mtlmr3m-fonts-signed.zip

ゆかりんフォント - yukarryaa-fonts-signed.zip

どのフォントがいいのかは迷うところですね。ゆかりんも意外と悪くないし…

ロングマン現代英英辞典[5訂版]をEPWINGに変換する

ロングマン現代英英辞典 [5訂版] DVD-ROM付

今まで英和/和英辞典として『英辞郎』を便利に使っていたのですが、そろそろ英辞郎は卒業して英英辞典を使いたいと思うようになりました。そこで、ちょっと奮発して『ロングマン 現代英英辞典【5訂版】』を購入しました。本当は付属のDVDだけが欲しかったのですが、紙の辞書付きのパッケージしかないみたいなので仕方がありません。付属のDVDには専用の辞書検索ソフトが含まれていて、これをインストールして使うのが普通の使い方だと思われます。しかし、他の辞書と串刺し検索したり、Androidでも使いたかったのでEPWING化することにしました。

作業に当たっては、以下のサイトで配布されているスクリプトを利用させてもらいました。

≫ LDOCE5 DVDをEPWING化する

1. 前準備

環境の準備が簡単なUbuntu 10.04上で作業します。まず、必要なパッケージをインストールします。

  • FreePWING
  • Image::Magick
  • XML::DOM
% aptitude install freepwing perlmagick libxml-dom-perl
2. 変換スクリプトのダウンロード
% curl -O http://www.geocities.co.jp/tak492/ldoce5-fpw-20100111.tar.gz
% tar zxvf ldoce5-fpw-20100111.tar.gz
% cd ldoce5-fpw-20100111

※各スクリプトの改行コードがCRLFになっているので、LFに変換しました。

3. Configの編集

ldoce5-fpw.confを自分の環境に合わせて編集します。変更したのは次の2行だけ。それぞれ、辞書データが格納されているディレクトリ(DVDを直接参照してもOK)と外字作成用フォントへのパスを指定します。

$ldoce5dir = "/tmp/ldoce5.data/";
$font = '/tmp/arialuni.ttf';
4. make

変換スクリプトを実行します。

% fpwmake package

ちょっと時間がかかりますが、ldoce5-fpw.zip というファイルができれば完了です。
この辞書データを展開してSDカードにコピーすれば、Android の辞書ソフト DroidWing から使えるようになります。

DroidWing で検索した例:

 

Nexus One の kernel を入れ替えて認識されるメモリを増やす

512MBのメモリを積んでいる Nexus One ですが、OS には200MBほどしか認識されていません。このまま使っているのは勿体ないので、patched kernel に入れ替えて認識メモリを増やすハックが海外の Nexus One ユーザの間で行われています。

私も我慢できずに dwang が kernel 2.6.29 にパッチを当ててビルドしてくれたものを試してみました。しばらく使ってみたのですが安定して動作しているので、以下に手順を書いておきます。

1. zImageとbcm4329.koをダウンロード

http://drop.io/tqyu5ai#

※zImageは「zImage-2.6.29.6」という名前で保存
(後で分からなくなりそうなので何となく)

2. FASTBOOTモードで起動

電源オフの状態から、トラックボールボタンを押しながら電源を入れます。

3. kernel書き換え

PCと接続してfastbootコマンドを実行。

# fastboot flash zimage zImage-2.6.29.6

rebootします。

4. wifiモジュール置き換え

このままだと、無線LANが使えないのでwifiモジュールを置き換えます。

# adb remount
# adb push bcm4329.ko /system/lib/modules/

以下のように約400MBのメモリが認識されていればOK

# free
              total         used         free       shared      buffers
  Mem:       395152       270860       124292            0         5024
 Swap:            0            0            0
Total:       395152       270860       124292

これでNexus Oneに関しては、アプリ切り替えがほぼオンメモリでPalm OS4レベルの体感速度になった気がします 。当然ながら、動かなくなっても責任は取れないのでkenerl入れ替えは自己責任でお願いします。

Nexus Oneで某キャリアのMMSを使えるようにする

先日、私もNexus Oneを入手しました。WVGA有機ELの綺麗な画面に加えて、1GHzのCPUと512MBのRAMは非常に快適です。すべての操作がサクサク動きます。不思議なことにBlogやtwitterを見ていると、日本では売っていないはずのこの端末を持っている人がたくさんいます。知り合いだけで10人くらいは所有者がいるんじゃないでしょうか。これがグローバル化というやつかもしれません。

というわけで、Nexus Oneでも某キャリアのMMSが使えるようにメッセージアプリをEclairのソースからビルドし直しました。下のスクリーンショットのようにいろいろなUser-Agentに変更する機能を付けたので、黒SIMだけでなく銀SIMでも使えるはず。

ダウンロードはこちらからどうぞ。Mms.apkという名前で保存してください。

前回、インストール方法に関する質問が多かったので、手順も書いておきます。rootedであることが前提です。Nexus Oneを持っているような人だったら余裕だと思うので、方法は自分で調べてください。

$ adb remount
$ adb shell find /system /data -name \*android\*mms.apk\* -o -name Mms.apk -delete
$ adb uninstall com.android.mms
$ adb install Mms.apk

2番目の手順は、最初から入っているMms.apkと署名が異なるため、[INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES]エラーを回避するのに必要です。 また、実際に利用する際には「アクセスポイントの設定」でMMS関連の設定を行うこともお忘れなく。

ヒント

2010/02/06 追記:
銀SIMで使えたというV702NKと708SCをUser-Agent一覧に追加しました。
thanks to chimcityさん

2010/02/11 追記:
X01TのUser-Agent文字列を修正しました。
thanks to 通りすがりの電動刑事さん