トップAndroid Java > ARGB画像データの加算(Overlay)

ARGB画像データの加算(Overlay)

1.はじめに

OSM地図では、基地など危険ゾーンは元の画像にピンク系のハッチングを薄く重ね合わす。 Windows の C# ではその機能がある。Android Java にもそれに近いものがあるかも知れないが、 情報が少なく、また、自分自身の経験が乏しいために、見つからない。

少なくとも当面は自前プログラムで対応する。

2.ARGBイメージデータの重ね合わせ

ARGBイメージデータでは一つの画素の色が 4バイトで表され、 上位バイトから順に A(alpha)、R(red)、G(green)、B(blue) となる。

合成色は、R、G、B 別に次の式で求められる[1]。ここで、アルファ値は重ね合わせる色のアルファ値である。

加算合成の場合、結果が 255 を超えるときは 255 とする。

アルファブレンド: 合成色 = 背景色 + (重ねる色 - 背景色) * (アルファ値 / 255)
加算合成:     合成色 = 背景色 + 重ねる色 * (アルファ値 / 255)

アルファ値は取りあえず、もとのまま(背景色のアルファ値としておく)

地図システムでは、アルファ値=100%で重ねる場合、背景色は無くなり、 重ねる色に置き換わるのが望ましいので、上記では、アルファブレンドとなる。 加算合成では、アルファ値=100%では元の色と重ねる色が半々で混ざりあったものとなる。

3.プログラム

最終的にはパターンを重ね書きするが、その範囲を得るために最初に bmpWork を単色で 塗りつぶしている。

しかし、このプログラムにはいくつか問題がある。 まず、要らない場所にも葉っぱアイコンが描画される。

危険ゾーンの Overlay の色が少々おかしい。GDI+ ではどのような方法をとっているか知りたい。 記事[3]を見る限りでは、アルファブレンドのようである。

    final void fillPolygon(Canvas canvas, TileToRender tile, Paint paint, int[] patterns) {
        tile.cvWork.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        fillPolygon(tile.cvWork, tile, paint);//bmpWork上にpolygonをcolorで塗りつぶし

        tile.bmpWork.getPixels(tile.pxWork, 0, PX, px0, py0, px1-px0, py1-py0);
        tile.bmpMain.getPixels(tile.pxMain, 0, PX, px0, py0, px1-px0, py1-py0);
        int[] mainPixels = tile.pxMain;
        int[] workPixels = tile.pxWork;

        int color = paint.getColor();
        for (int i = 0; i < py1-py0; i++) {
            int ipy = i * PX;
            int off = (py0+i)*PX + px0;
            for (int j = 0; j < px1-px0; j++) {
                int pattern = patterns[off + j];
                if (workPixels[ipy + j] == color) {
                    int a = (int)((pattern>>24)&0xff);  // alpha
                    int cm = mainPixels[ipy + j];
                    int rm = (cm & 0x00ff0000) >> 16;
                    int gm = (cm & 0x0000ff00) >> 8;
                    int bm = cm & 0x000000ff;
                    int rp = (pattern & 0x00ff0000) >> 16;
                    int gp = (pattern & 0x0000ff00) >> 8;
                    int bp = pattern & 0x000000ff;
                    mainPixels[ipy+j] = (cm&0xff000000) + ((rm + (rp-rm)*a/255)<<16)
                                        + ((gm + (gp-gm)*a/255)<<8)
                                        + (bm * (bp-bm)*a/255);
                }
            }
        }
        tile.bmpMain.setPixels(tile.pxMain, 0, PX, px0, py0,px1-px0, py1-py0);
    }

修正後のプログラム

ブレンドでは元の色のアルファ値と重ねる色のアルファ値を対等に扱う案もあるが、 重ねる色のアルファ値が 255(100%)のときは、完全な上書きであり、重ねる色に変えるのが パソコン・タブレット版および標準OSM地図に近くなる。

        int color = paint.getColor();
        float ac = (((color >> 24) & 0xff) / 255.0f);  // alpha
        int rc = (color & 0x00ff0000) >> 16;
        int gc = (color & 0x0000ff00) >> 8;
        int bc = color & 0x000000ff;
        for (int i = 0; i < py1-py0; i++) {
            int ipy = i * PX;
            int off = (py0+i)*PX + px0;
            for (int j = 0; j < px1-px0; j++) {
                int pattern = patterns[off + j];
                if (workPixels[ipy + j] == color) {
                    int rp, gp, bp;
                    float ap = (((pattern >> 24) & 0xff) / 255.0f);  // alpha
                    int cm = mainPixels[ipy + j];
                    float am = (((cm >> 24) & 0xff) / 255.0f);  // alpha
                    int rm = (cm & 0x00ff0000) >> 16;
                    int gm = (cm & 0x0000ff00) >> 8;
                    int bm = cm & 0x000000ff;
                    if (ap == 0) {
                        ap = ac;
                        rp = rc;
                        gp = gc;
                        bp = bc;
                    } else {
                        rp = (pattern & 0x00ff0000) >> 16;
                        gp = (pattern & 0x0000ff00) >> 8;
                        bp = pattern & 0x000000ff;
                    }
                    int r = (int) (rm * (1-ap) + rp * ap);
                    int g = (int) (gm * (1-ap) + gp * ap);
                    int b = (int) (bm * (1-ap) + bp * ap);
                    int a = (int)((am + (1 - am) * ap) * 255);
                    mainPixels[ipy + j] = (a << 24) | (r << 16) | (g << 8) | b;
                }
            }
        }
        tile.bmpMain.setPixels(tile.pxMain, 0, PX, px0, py0,px1-px0, py1-py0);

これで、スマホアプリでもパソコン・タブレット版および標準OSM地図と似た結果が得られるようになった。

アルファ値は単純に重ねる色のアルファ値の方がいいかも知れない。今後、不具合が見つかれば見直したい。

A.リファレンス

[1] アルファブレンドと加算合成
[2] アルファ値を含むブレンドモードの画像合成の計算式
[3] アルファ ブレンドの直線と塗りつぶし