Цветовые полосы и артефакты с градиентами, несмотря на использование RGBA _8888 везде

Я знаю, что цветовая полоса - это старый каштан проблемы, которая обсуждалась много раз ранее с различными решениями (, которые по существу сводятся к использованию 32 -бит повсюду или использованию дизеринга ). На самом деле, не так давно я задал и впоследствии ответил на свой собственный SO-вопрос по этому поводу. Тогда,Я думал, что решение, которое я изложил в ответе на вопрос (, заключается в применении setFormat(PixelFormat.RGBA_8888)к Window, а также к Holderв случаеSurfaceView)решил проблему в моем приложении навсегда. По крайней мере, благодаря этому решению градиент выглядел очень красиво на устройствах, которые я тогда разрабатывал (, скорее всего, на Android 2.2 ).

Сейчас я разрабатываю HTC One X (Android 4.0 )и Asus Nexus 7 (Android 4.1 ). Что я пытался сделать, так это применить градиент серого ко всей области SurfaceView. Несмотря на то, что я якобы убедился, что содержащие Windowи Holderнастроены на 32 -битный цвет, я получаю ужасные артефакты с полосами. На самом деле, на Nexus 7 я даже вижу, как перемещаются артефакты. Это происходит не только на SurfaceView, который, конечно же, постоянно рисуется, но также и на обычном View, который я добавил рядом, чтобы нарисовать точно такой же градиент для целей тестирования, который рисовался бы один раз. То, что эти артефакты существуют и кажутся движущимися, конечно, выглядит совершенно ужасно, и это на самом деле похоже на просмотр аналогового телевизора с плохим сигналом . И View, и SurfaceViewимеют одинаковые артефакты, которые перемещаются вместе.

Я намерен использовать 32 -бит повсюду, а не использовать сглаживание. У меня сложилось впечатление, что Windowбыло 32 -бит по умолчанию задолго до Android 4.0. Применяя RGBA_8888в SurfaceView, я ожидал, что все будет 32 -бит, что позволит избежать каких-либо артефактов.

Я отмечаю, что есть некоторые другие вопросы по SO, где люди заметили, что RGBA_8888больше не работает на платформах 4.0/4.1.

Это снимок экрана с моего Nexus 7, с обычным Viewвверху и SurfaceViewвнизу, оба применяют один и тот же градиент к Canvas. Конечно, он не так хорошо показывает артефакты, как при взгляде на дисплей,и поэтому, вероятно, довольно бессмысленно показывать этот снимок экрана. Однако я хочу подчеркнуть, что полосы действительно выглядят ужасно на экране Nexus. Редактировать:На самом деле на снимке экрана вообще не видны артефакты . Артефакты, которые я вижу на Nexus 7, не являются однородными полосами; это выглядит случайным в природе.

enter image description here

Тест 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) {

        }
    }


}

Я установил приложение Display Tester из Google Play. Это приложение можно использовать для создания тестовых градиентов на экране. Хотя его градиенты не выглядят идеальными, они кажутся немного лучше, чем то, что мне удалось достичь, что заставляет меня задуматься, есть ли еще какие-то меры, которые я могу предпринять, чтобы предотвратить появление полос.

Еще я заметил, что приложение Display Tester сообщает, что экран моего Nexus имеет разрядность 32 -бит.

Для информации, я явно включаю аппаратное ускорение. Мои уровни SDK:

 

Еще одна вещь, которую я заметил, это то, что градиентный фон по умолчанию для Activity, который, как я понимаю, является функцией Holo, также очень полосатый. На скриншоте этого тоже не видно. И я также только что заметил, что полосы фона на моем Nexus 7 ненадолго перемещаются, что соответствует движению полос на моих двух Views. Если я создам совершенно новый проект Android с «пустым» значением по умолчанию Activity, Activityпокажет неприятный полосатый градиентный фон как на моем Nexus, так и на HTC One X. Это нормально? Я понимаю, что этот черно-фиолетовый градиентный фон по умолчанию — это то, что должен иметь Activity, если включено аппаратное ускорение. Ну, независимо от того, включено ли аппаратное ускорение или нет, я вижу один и тот же неприятный полосчатый Activityфоновый градиент. Это происходит даже в моем пустом тестовом проекте, целевой SDK которого равен 15. Чтобы уточнить, способ, которым я включаю или отключаю аппаратное ускорение, явно использую android:hardwareAccelerated="true"и android:hardwareAccelerated="false".

Я не уверен, что мое наблюдение о черно-фиолетовом Activityградиентном фоне Holo имеет какое-либо отношение к моему основному вопросу, но оно кажется странно связанным. Также странно, что он выглядит таким некачественным (, то есть полосатым ), и выглядит одинаково независимо от того, включено ли аппаратное ускорение. Таким образом, вторичный вопрос будет :Когда у вас есть Activityс градиентным фоном Holo по умолчанию, и для каждого случая аппаратного ускорения, которое явно включено, а затем отключено, должен ли этот градиентный фон (a )быть присутствует и (б )выглядят идеально гладкими? Я бы задал это в отдельном вопросе, но опять же, это кажется связанным.

Итак, резюмируя:Основная проблема, с которой я столкнулся, заключается в том, что применение градиентного фона к SurfaceViewпросто невозможно сделать на моем Nexus 7. Дело не только в полосах, это проблема (, с которой я мог бы с радостью мириться, если бы это было просто что ); на самом деле это тот факт, что полоса носит случайный характер при каждом розыгрыше. Это означает, что SurfaceView, который постоянно перерисовывается, имеет движущийся нечеткий фон.

26
задан Community 23 May 2017 в 12:32
поделиться