2016-05-21 3 views
5

[Изменить: Я сделал минимальный проект, чтобы попытаться сузить то, что происходит. Код внизу все равно генерирует те же артефакты при сохранении]При сохранении растрового изображения на диск, сплошные пути показывают артефакты

У меня есть приложение, которое рисует простую 2D-геометрию с использованием путей. Формы - сплошные цвета, иногда с альфой < 255, и могут быть украшены линиями. В представлении, которое рисует геометрию, никогда не возникало проблемы с тем, как вещи нарисованы. Однако, когда я использую один и тот же код для рисования в Bitmap, а затем сохраняю его как JPEG (со 100 качеством) или PNG, всегда есть те же артефакты в сплошных областях выходных файлов. Это своего рода пятнистость, которая обычно связана с сжатием JPEG.

Скриншот Вид: Screenshot of Activity

Сохраненные изображения: Saved image file

Увеличивать артефактов: Zoom in on artifacts

Я попытался следующие

  • Saving либо PNG и JPEG
  • Включение сглаживания и антиалиасинга и выключаться
  • Увеличение DPI растрового, а также позволило Bitmap использовать его по умолчанию API
  • Применяя матрицу я использую в качестве камеры для геометрического представления, вместо того, чтобы применить его к Холст для точечного рисунка
  • поворачивая HW ускорение и выключение приложения-широкий
  • Использование 3-й библиотеки партии, чтобы сохранить Bitmap в .bmp файл

Все дают те же артефакты, и не делает его хуже ни лучше.

public class MainActivity extends AppCompatActivity { 
Context context; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    this.context = getApplicationContext(); 
} 

// button OnClick listener 
public void saveImage(View view) { 
    new saveBitmapToDisk().execute(false); 
} 

public Bitmap getBitmap() { 
    final int bitmapHeight = 600, bitmapWidth = 600; 
    Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888); 
    Canvas bitmapCanvas = new Canvas(bitmap); 

    float[] triangle = new float[6]; 
    triangle[0] = bitmapWidth/2; 
    triangle[1] = 0; 
    triangle[2] = 0; 
    triangle[3] = bitmapHeight/2; 
    triangle[4] = bitmapWidth/2; 
    triangle[5] = bitmapHeight/2; 

    Path solidPath = new Path(); 
    Paint solidPaint = new Paint(); 
    solidPaint.setStyle(Paint.Style.FILL); 

    solidPath.moveTo(triangle[0], triangle[1]); 

    for(int i = 2; i < triangle.length; i += 2) 
     solidPath.lineTo(triangle[i], triangle[i+1]); 

    solidPath.close(); 

    solidPaint.setColor(Color.GREEN); 
    bitmapCanvas.drawPath(solidPath, solidPaint); 
    return bitmap; 
} 

private class saveBitmapToDisk extends AsyncTask<Boolean, Integer, Uri> { 
    Boolean toShare; 

    @Override 
    protected Uri doInBackground(Boolean... shareFile) { 
     this.toShare = shareFile[0]; 
     final String appName = context.getResources().getString(R.string.app_name); 
     final String IMAGE_SAVE_DIRECTORY = String.format("/%s/", appName); 
     final String fullPath = Environment.getExternalStorageDirectory().getAbsolutePath() + IMAGE_SAVE_DIRECTORY; 
     File dir, file; 

     try { 
      dir = new File(fullPath); 
      if (!dir.exists()) 
       dir.mkdirs(); 

      OutputStream fOut; 

      file = new File(fullPath, String.format("%s.png", appName)); 

      for (int suffix = 0; file.exists(); suffix++) 
       file = new File(fullPath, String.format("%s%03d.png", appName, suffix)); 

      file.createNewFile(); 
      fOut = new FileOutputStream(file); 

      Bitmap saveBitmap = getBitmap(); 
      saveBitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut); 
      fOut.flush(); 
      fOut.close(); 
      MediaStore.Images.Media.insertImage(context.getContentResolver(), file.getAbsolutePath(), file.getName(), file.getName()); 

     } catch (OutOfMemoryError e) { 
      Log.e("MainActivity", "Out of Memory saving bitmap; bitmap is too large"); 
      return null; 
     } catch (Exception e) { 
      Log.e("MainActivity", e.getMessage()); 
      return null; 
     } 

     return Uri.fromFile(file); 
    } 

    @Override 
    protected void onPostExecute(Uri uri) { 
     super.onPostExecute(uri); 
     Toast.makeText(context, "Image saved", Toast.LENGTH_SHORT).show(); 
    } 
} 
} 

ответ

3
  1. Я тестировал программу с PNG и файл не имеет артефактов
  2. Эти артефакты являются результатом сжатия JPEG

Edit: Линия

MediaStore.Images.Media.insertImage(context.getContentResolver(), file.getAbsolutePath(), file.getName(), file.getName()); 

причиняло преобразование в jpeg.

Правильный способ сохранить изображение является

ContentValues values = new ContentValues(); 
values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis()); 
values.put(Images.Media.MIME_TYPE, "image/png"); 
values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath()); 
context.getContentResolver().insert(Images.Media.EXTERNAL_CONTENT_URI, values); 

Вот мой упрощена тестовая программа, которая отправляет полученный файл непосредственно

public class Test2Activity extends Activity { 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    new saveBitmapToDisk().execute(); 
    } 

    public Bitmap getBitmap() { 
    final int bitmapHeight = 600, bitmapWidth = 600; 
    Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888); 
    Canvas bitmapCanvas = new Canvas(bitmap); 

    Paint solidPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
    solidPaint.setStyle(Paint.Style.FILL); 
    solidPaint.setColor(Color.RED); 
    bitmapCanvas.drawCircle(300, 300, 200, solidPaint); 

    return bitmap; 
    } 

    private class saveBitmapToDisk extends AsyncTask<Void, Void, Uri> { 
    Boolean toShare; 

    @Override 
    protected Uri doInBackground(Void... shareFile) { 
     Context context = Test2Activity.this; 
     try { 
     File file = new File(context.getExternalFilesDir(null), "test.png"); 
     FileOutputStream fOut = new FileOutputStream(file); 

     Bitmap saveBitmap = getBitmap(); 
     saveBitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut); 
     fOut.flush(); 
     fOut.close(); 
     return Uri.fromFile(file); 
     } catch (OutOfMemoryError e) { 
     Log.e("MainActivity", "Out of Memory saving bitmap; bitmap is too large"); 
     return null; 
     } catch (Exception e) { 
     Log.e("MainActivity", e.getMessage()); 
     return null; 
     } 

    } 

    @Override 
    protected void onPostExecute(Uri uri) { 
     Context context = Test2Activity.this; 
     Toast.makeText(context, "Image saved", Toast.LENGTH_SHORT).show(); 

     final Intent intent = new Intent(android.content.Intent.ACTION_SEND); 
     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
     intent.putExtra(Intent.EXTRA_STREAM, uri); 
     intent.setType("image/png"); 
     Test2Activity.this.startActivity(intent); 
    } 
    } 
} 
+0

Я просто дважды проверил на новом устройстве, и я определенно все еще испытываю артефакты, и это определенно не старый файл. И это не миниатюра; переход в раздел «информация» указывает правильные размеры. Возможно ли, что это что-то в среде сборки или в некоторых таких? – Project

+0

@Project Я добавил в свой ответ упрощенную деятельность, которую вы должны проверить. Он создает изображение круга на прозрачном фоне и открывает намерение поделиться отправкой этого файла, запустите его на своем устройстве и отправьте сгенерированное изображение по электронной почте самому себе. Если изображение имеет прозрачный фон, оно было закодировано как PNG, если оно непрозрачно, оно было закодировано как jpeg. – yoah

+0

Протестировал ваш пример, он имеет прозрачный фон и никаких артефактов. Любопытно, может ли это быть каталог, который я сберегаю, или что-то ... будет трястись сразу, как только раздался кофе. – Project

1

Артефакты, подобные этому, являются естественным и неизбежным следствием сжатия JPEG.

Они не должны возникать при сжатии PNG. Если вы получаете такие артефакты при создании PNG-файла, я бы сказал, что вы вообще не создаете поток PNG, а скорее поток JPEG в файле с расширением PNG. Никакой достойный декодер не использует расширение файла.

+0

Это происходит как с saveBitmap.compress (Bitmap.CompressFormat.JPEG , 100, fOut); и saveBitmap.compress (Bitmap.CompressFormat.PNG, 100, fOut); – Project

+0

Чтобы быть более четким; У меня было это как CompressFormat.PNG в течение длительного времени, в котором я получал изображения, подобные этому. Единственная причина, по которой это CompressFormat.JPEG, заключается в том, что я экспериментировал с изменением кода, чтобы увидеть, были ли у меня разные результаты. – Project

0

я заметил две вещи в вашем коде:

1) Имя файла вы сохранить это String.format("%s.jpg", appName) или String.format("%s%03d.png", appName, suffix) независимо от фактического кодирования.

2) Растровое изображение, которое вы сохраняете, имеет плотность, определяемую prefs.saveImageDensity().get(), поэтому может быть не такой, как фактическая плотность растрового изображения, которое вы видите на экране.

Возможно, вы смутили себя 1) или, возможно, 2), вы видите артефакты сжатия, которые вы видите?

+0

1) Является функцией моего редактирования сообщения неправильно 2) Я должен был быть яснее; Я пробовал это во всех видах плотностей, в том числе и в собственной плотности. Я только что вернулся и удостоверился, что расширение файла было PNG независимо, и закомментировал настройку DPI. Проблема все еще возникает. – Project

+0

В документации даже упоминается, что сохранение в PNG - операция без потерь, но, возможно, там есть ошибка. Вы должны попробовать написать отдельную тестовую программу, которая загружает и сохраняет растровое изображение и видит, существует ли проблема даже там. Это покажет, действительно ли сохранение PNG действительно без потерь. – hkBst

+0

Я заменил Android API для использования [этой библиотеки] (https://github.com/ultrakain/AndroidBitmapUtil), чтобы сохранить как BITMAP, и я получаю те же артефакты. Таким образом, похоже, что это не имеет никакого отношения к сжатию, но с фактическим рисованием в растровое изображение. Который кажется мне довольно странным. – Project

Смежные вопросы