Цветовая окантовка и артефакты с градиентами, несмотря на использование RGBA 8888 везде
я знаю, что цветовая окантовка-это старый каштан проблемы, которая обсуждалась много раз раньше с различными предоставленными решениями (которые по существу сводятся к использованию 32-битного повсюду или использованию смешивания). На самом деле не так давно я спросил и впоследствии ответил на мой собственный так вопрос относительно этого. Тогда я думал, что решение, которое я положил в ответ на этот вопрос (который должен применяться 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-бит во всем, а не использовать dithering. У меня сложилось впечатление, что Window
был 32-бит по умолчанию задолго до Android 4.0. Путем применения RGBA_8888
на SurfaceView
Я ожидал, что все будет 32-битным, таким образом, избегая каких-либо артефактов.
я отмечаю, что есть некоторые другие вопросы о том, где люди заметили, что RGBA_8888
больше не кажется эффективным на платформах 4.0 / 4.1.
это скриншот из моего Nexus 7, с нормальным View
и SurfaceView
ниже, оба применяя один и тот же градиент к Canvas
. Конечно, он не показывает артефакты так хорошо, как они делают, когда смотрят на дисплей, и поэтому, вероятно, довольно бессмысленно показывать этот захват экрана. Я хочу подчеркнуть, что кольцевание действительно выглядит ужасно на экран Нексуса. Edit: на самом деле, скриншот действительно не показывает артефакты на всех. Артефакты, которые я вижу на 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) {
}
}
}
я установил приложение под названием Display Tester из Google Play. Это приложение может быть использовано для создания тестовых градиентов на экране. Хотя его градиенты не выглядят идеально, они кажутся немного лучше, чем то, что я смог достичь, что заставляет меня задуматься, есть ли еще одна мера, которую я могу сделать, чтобы предотвратить кольцевание.
другая вещь, которую я отмечаю, заключается в том, что приложение Display Tester сообщает, что экран моего Nexus 32-бит.
для информации, я прямо позволяющие аппаратное ускорение. Мои уровни SDK:
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15"></uses-sdk>
еще одна вещь, которую я замечаю, - это фон градиента по умолчанию для the Activity
, который, как я понимаю, является особенностью Holo, также очень полосатый. Это также не отображается на скриншоте. И я также только что заметил, что полоса фона ненадолго перемещается на моем Nexus 7, в соответствии с движением полосы в моих двух Views
. Если я создам совершенно новый проект Android с "пустым" по умолчанию Activity
на Activity
показывает неприятный полосатый градиентный фон на обоих моих Nexus и HTC One X. Это нормально? Я понимаю, что этот черный / фиолетовый градиент по умолчанию фон является то, что Activity
буду есть, если аппаратное ускорение включено. Ну, независимо от того, включено ли аппаратное ускорение или нет, я вижу ту же неприятную полосу Activity
фоновый градиент. Это даже происходит в моем пустом тестовом проекте, целевой SDK которого равен 15. Чтобы уточнить, способ включения или отключения аппаратного ускорения явно использует android:hardwareAccelerated="true"
и android:hardwareAccelerated="false"
.
я не уверен, что мое наблюдение о Холо черный / фиолетовый Activity
градиентный фон имеет какое-то отношение к моему основному вопросу, но он кажется странно связанным. Также странно, что он выглядит таким низким качеством (т. е. полосатым) и выглядит одинаково независимо от того, включено ли аппаратное ускорение. Таким образом, вторичным вопросом будет: когда у вас есть Activity
с фоном градиента Holo по умолчанию и для каждого случая аппаратного ускорения, когда эксплицитность включена, а затем отключена, если этот фон градиента (a) присутствует и (b) выглядит идеально гладко? Я бы задал это в отдельном вопросе, но опять же, это кажется связанным.
Итак, вкратце: основная проблема заключается в том, что применение градиентного фона к SurfaceView
просто не может быть сделано, кажется, на моем Nexus 7. Проблема не только в кольцевании (с которым я бы с радостью смирился, если бы это было так); на самом деле это тот факт, что кольцевание является случайным по своей природе при каждом розыгрыше. Это означает, что SurfaceView
что постоянно перерисовывает заканчивается до переезда, нечеткий фон.
4 ответов
чтобы завершить это ответом, я пришел к выводу, что Nexus 7 просто имеет некоторые проблемы с оборудованием / прошивкой, что означает, что он полностью штаны при рендеринге градиентов.
вы пробовали установить pixelformat для самого вида поверхности ?
final SurfaceHolder holder = getHolder();
holder.setFormat(PixelFormat.RGBA_8888);
Если вы не видите эффекта установки формата пикселей в ICS и выше, это, скорее всего, связано с аппаратным ускорением, которое всегда будет отображаться в собственном формате пикселей. Большую часть времени это должно быть просто ARGB_8888. Убедитесь, что вы также установили формат пикселя окна вашей деятельности, а не только формат пикселя на SurfaceView.
вы можете легко проверить, если это так, отключив ускорение. Вы упоминаете, что проверили это, но не упоминаете, как вы это сделали что. Установка целевого уровня sdk-не самый явный способ сделать это.
с верхней части моей головы рендеринг программного обеспечения переключился на ARGB_8888 по умолчанию в HoneyComb (3.0), но снова вам нужно будет установить его явно для правильной поддержки старых устройств, где это не по умолчанию.
почему вы установили false сглаживание на вашей краске. Я бы предложил активировать dithering
paint.setDither(true);
Android doc ясно говорит, что он понизит ваш рендеринг:
установка или очистка dither_flag бит Dithering влияет на то, как цвета которые имеют более высокую точность, чем устройства уменьшается. Нет dithering обычно быстрее, но цвета с более высокой точностью-это просто усе вниз (например, 8888 -> 565). Dithering пытается распределить ошибка, присущая этому процессу, для уменьшения визуальных артефактов.
вы также можете попытаться добавить FLAG_DITHER в окно:
window.setFormat(PixelFormat.RGBA_8888);
window.setFlags(WindowManager.LayoutParams.FLAG_DITHER, WindowManager.LayoutParams.FLAG_DITHER);