27

カラーバンディングは、さまざまなソリューションが提供されて以前に何度も議論されてきた問題の古い栗であることを認識しています(基本的に、全体で32ビットを使用するか、ディザリングを使用します)。実際、それほど前のことではありませんが、私はこれに関して自分自身のSOの質問をし、その後答えました。当時、私はその質問への回答に入れた解決策(これはに適用setFormat(PixelFormat.RGBA_8888)され、の場合Windowもに適用される)が私のアプリケーションの問題を完全に解決したと思いました。少なくともこのソリューションは、当時私が開発していたデバイス(おそらくAndroid 2.2)でグラデーションを非常に見栄えよくしました。HolderSurfaceView

現在、HTC One X(Android 4.0)とAsus Nexus 7(Android 4.1)を使用して開発しています。私がやろうとしたのは、の領域全体にグレーのグラデーションを適用することでしたSurfaceView。包含WindowHolderが32ビットカラー用に構成されていることを確認したと思われますが、ひどいバンディングアーティファクトが発生します。実際、Nexus 7では、アーティファクトが動き回るのが見えます。これSurfaceViewは、もちろん継続的に描画しているものだけでなくView、テスト目的でまったく同じグラデーションを描画するために一緒に追加した通常の場合にも発生します。もちろん、これらのアーティファクトが存在し、動き回っているように見える方法は、絶対にひどいように見えます。実際には、信号の弱いアナログTVを見るようなものです。両方Viewまったく同じアーティファクトをSurfaceView示し、一緒に動き回ります。

私の意図は、全体で32ビットを使用することであり、ディザリングは使用しないことです。私は、WindowAndroid4.0よりずっと前にデフォルトで32ビットだったという印象を受けています。に適用することRGBA_8888で、SurfaceViewすべてが32ビットであると予想していたため、アーティファクトを回避できました。

RGBA_8888SOについては、4.0/4.1プラットフォームではもはや効果がないように思われるという質問が他にもいくつかあることに注意してください。

これは私のNexus7のスクリーンショットViewで、上部とSurfaceView下部に法線があり、どちらも同じグラデーションをに適用していCanvasます。もちろん、ディスプレイを見たときのようにアーティファクトは表示されないため、この画面グラブを表示することはおそらくかなり無意味です。ただし、ネクサスの画面ではバンディングが実際にひどく見えることを強調したいと思います。編集:実際、スクリーンショットにはアーティファクトがまったく表示されていませ。Nexus 7で見ているアーティファクトは、均一なバンディングではありません。それは本質的にランダムに見えます。

ここに画像の説明を入力してください

上記を作成するために使用されるテストActivity

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Shader;
import android.os.Bundle;
import android.os.Handler;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.view.SurfaceHolder.Callback;
import android.widget.LinearLayout;

public class GradientTest extends Activity {

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        getWindow().setFormat(PixelFormat.RGBA_8888);
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);      
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
        lp.copyFrom(getWindow().getAttributes());
        lp.format = PixelFormat.RGBA_8888;
        getWindow().setAttributes(lp);     
        LinearLayout ll = new LinearLayout(this);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(500,500);
        params.setMargins(20, 0, 0, 0);
        ll.addView(new GradientView(this), params);    
        ll.addView(new GradientSurfaceView(this), params);
        ll.setOrientation(LinearLayout.VERTICAL);
        setContentView(ll);
    }


    public class GradientView extends View {

        public GradientView(Context context) {
            super(context);     
        }

        @Override
        protected void onDraw(Canvas canvas) {

            Paint paint = new Paint();
            paint.setStyle(Paint.Style.FILL);
            paint.setAntiAlias(false);
            paint.setFilterBitmap(false);
            paint.setDither(false); 
            Shader shader = new LinearGradient(
                    0,
                    0,
                    0,
                    500,
                    //new int[]{0xffafafaf, 0xff414141},
                    new int[]{0xff333333, 0xff555555},
                    null,

                    Shader.TileMode.CLAMP
                    );

            paint.setShader(shader);    
            canvas.drawRect(0,0,500,500, paint);
        }       
    }




    public class GradientSurfaceView extends SurfaceView implements Callback {

        public GradientSurfaceView(Context context) {
            super(context);

            getHolder().setFormat(PixelFormat.RGBA_8888); // Ensure no banding on gradients 
            SurfaceHolder holder = getHolder();
            holder.addCallback(this);    
        }


        Paint paint;
        private GraphThread thread;

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            holder.setFormat(PixelFormat.RGBA_8888); // Ensure no banding on gradients    

            paint = new Paint();
            paint.setStyle(Paint.Style.FILL);
            paint.setAntiAlias(false);
            paint.setFilterBitmap(false);
            paint.setDither(false); 

            Shader shader = new LinearGradient(
                    0,
                    0,
                    0,
                    500,
                    //new int[]{0xffafafaf, 0xff414141},
                    new int[]{0xff333333, 0xff555555},
                    null,
                    Shader.TileMode.CLAMP
                    );


            paint.setShader(shader);  



            thread = new GraphThread(holder, new Handler() );
            thread.setName("GradientSurfaceView_thread");
            thread.start();
        }

        class GraphThread extends Thread {

            /** Handle to the surface manager object we interact with */
            private SurfaceHolder mSurfaceHolder;   

            public GraphThread(SurfaceHolder holder, Handler handler) {
                mSurfaceHolder = holder;
                holder.setFormat(PixelFormat.RGBA_8888); // Ensure no banding on gradients  
            }

            @Override
            public void run() {

                Canvas c = null;

                while (true) {

                    try {
                        c = mSurfaceHolder.lockCanvas();

                        synchronized (mSurfaceHolder) {

                            if (c != null){


                                c.drawRect(0,0,500,500, paint);

                            }
                        }                                 
                    } 

                    finally {
                        if (c != null) {
                            mSurfaceHolder.unlockCanvasAndPost(c);
                        }
                    }
                }
            }    

        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {

        }
    }


}

GooglePlayからDisplayTesterというアプリケーションをインストールしました。このアプリケーションを使用して、画面上にテストグラデーションを作成できます。グラデーションは完璧には見えませんが、私が達成できたものよりも少し良く見えます。それは、バンディングを防ぐために私ができるさらなる対策があるかどうか疑問に思います。

もう1つ注意すべき点は、DisplayTesterアプリケーションがNexusの画面が32ビットであることを報告していることです。

参考までに、ハードウェアアクセラレーションを明示的に有効にしています。私のSDKレベルは次のとおりです。

 <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15"></uses-sdk>

もう1つ気付いたのはActivity、Holoの機能であると私が理解している、のデフォルトのグラデーション背景も非常に縞模様になっていることです。これもスクリーンショットにはまったく表示されません。また、Nexus 7では、2つのバンディングの動きに共感して、背景のバンディングが一時的に動き回っていることにも気づきましたViewsActivityデフォルトの「空」で完全に新しいAndroidプロジェクトを作成するとActivity、NexusとHTC One Xの両方で厄介な縞模様のグラデーションの背景が表示されます。これは正常ですか?Activityこの黒/紫のグラデーションのデフォルトの背景は、ハードウェアアクセラレーションが有効になっている場合に必要なものであることを理解しています。ええと、ハードウェアアクセラレーションが有効になっているかどうかに関係なく、同じ厄介な縞模様が表示されますActivity背景のグラデーション。これは、ターゲットSDKが15である私の空のテストプロジェクトでも発生します。明確にするために、ハードウェアアクセラレーションを有効または無効にする方法は、明示的にとを使用android:hardwareAccelerated="true"してandroid:hardwareAccelerated="false"います。

ホロの黒/紫のグラデーションの背景についての私の観察Activityが私の主な質問と関係があるかどうかはわかりませんが、それは奇妙なことに関連しているようです。また、ハードウェアアクセラレーションがオンになっているかどうかに関係なく、品質が低く(つまり、縞模様になっている)、同じように見えるのも奇妙です。したがって、2番目の質問は次のようになります。ActivityデフォルトのHoloグラデーションの背景があり、ハードウェアアクセラレーションが明示的に有効にされてから無効になっている場合、このグラデーションの背景は(a)存在し、(b)完全に滑らかに見える必要がありますか?これは別の質問で質問しますが、繰り返しになりますが、関連しているようです。

要約すると、私が抱えている基本的な問題は、グラデーションの背景をSurfaceView単純に適用することはできないということです。私のNexus 7では、それが問題であるのはバンディングだけではありません。それ); 実際には、各ドローでバンディングがランダムであるという事実があります。これは、SurfaceView絶えず再描画されるaが、移動するぼやけた背景を持つことになることを意味します。

4

4 に答える 4

4

これを答えで締めくくるために、私が到達した結論は、Nexus 7にはハードウェア/ファームウェアの問題があるということです。つまり、グラデーションのレンダリングが完全に苦手です.

于 2013-05-10T14:53:46.730 に答える
2

表面ビュー自体のピクセルフォーマットを設定しようとしましたか?

    final SurfaceHolder holder = getHolder();
    holder.setFormat(PixelFormat.RGBA_8888);
于 2015-05-12T09:47:29.440 に答える
1

ICS 以上でピクセル フォーマットを設定しても効果がない場合は、常にネイティブ ピクセル フォーマットにレンダリングするハードウェア アクセラレーションが原因である可能性が最も高いです。ただし、ほとんどの場合、それは ARGB_8888 である必要があります。SurfaceView のピクセル形式だけでなく、アクティビティのウィンドウ ピクセル形式も設定してください。

加速をオフにすることで、そうであるかどうかを簡単に確認できます。あなたはそれをテストしたと述べていますが、それをどのように行ったかについては言及していません。ターゲット sdk レベルを設定することは、それを行うための最も明示的な方法ではありません。

ソフトウェア レンダリングは、HoneyComb (3.0) ではデフォルトで ARGB_8888 に切り替えられましたが、これがデフォルトではない古いデバイスを正しくサポートするには、明示的に設定する必要があります。

于 2012-08-17T07:07:45.083 に答える
1

ペイントのディザリングを false に設定したのはなぜですか。ディザリングを有効にすることをお勧めします

paint.setDither(true);

Android doc には、レンダリングがダウングレードされることが明確に記載されています。

DITHER_FLAG ビットの設定またはクリア ディザリングは、デバイスよりも精度の高い色をダウンサンプリングする方法に影響します。ディザリングなしは一般的に高速ですが、高精度の色は切り捨てられます (例: 8888 -> 565)。ディザリングは、視覚的なアーティファクトを減らすために、このプロセスに固有のエラーを分散しようとします。

FLAG_DITHER をウィンドウに追加することもできます。

window.setFormat(PixelFormat.RGBA_8888);
window.setFlags(WindowManager.LayoutParams.FLAG_DITHER, WindowManager.LayoutParams.FLAG_DITHER);
于 2012-10-22T17:49:14.830 に答える