2016-05-21 13 views
5

[Bearbeiten: Ich habe ein minimales Projekt gemacht, um zu versuchen, einzugrenzen, was los ist. Der Code am unteren Rand erzeugt immer noch die gleichen Artefakte beim Speichern.Beim Speichern von Bitmap auf Festplatte, feste Pfade zeigen Artefakte

Ich habe eine App, die einfache 2D-Geometrie mit Pfaden zeichnet. Die Formen sind alle einfarbig, manchmal mit Alpha < 255, und können mit Linien verziert sein. In der Ansicht, die die Geometrie zeichnet, gab es nie ein Problem mit der Art, wie Dinge gezeichnet werden. Wenn ich jedoch den gleichen Code verwende, um zu einer Bitmap zu zeichnen und sie dann entweder als JPEG (mit 100er Qualität) oder PNG zu speichern, gibt es in den durchgehenden Bereichen der Ausgabedateien immer das gleiche Artefakt. Es ist eine Art Sprenkelung, die normalerweise mit JPEG-Komprimierung verbunden ist.

Screenshot der Ansicht: Screenshot of Activity

Gespeichert Bild: Saved image file

Zoom auf Artefakte: Zoom in on artifacts

ich folgendes

  • Speichern entweder PNG versucht haben und JPEG
  • Drehen Dithering und Anti-Aliasing und Ausschalten
  • die DPI des Bitmaps erhöhen und erlaubt auch die Bitmap seine Standard API
  • Anwenden der Matrix I als Kamera zur geometrischen Darstellung verwenden zu verwenden, anstatt es zu bewerben die Leinwand für die Bitmap
  • Drehen HW-Beschleunigung und ausschalten App weit
  • eine 3rd-Party-Bibliothek Verwenden der Bitmap in eine .bmp Datei

alle die gleichen Artefakte liefern zu retten, weder macht es noch schlimmer noch besser.

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(); 
    } 
} 
} 

Antwort

3
  1. testete ich Ihr Programm mit PNG und die Datei hat keine Artefakte
  2. Diese Artefakte sind ein Ergebnis der JPEG-Komprimierung

Edit: Die Linie

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

verursacht wurde Umwandlung in JPEG.

Der richtige Weg ist es, das Bild zu speichern

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); 

Hier ist meine vereinfachte Testprogramm, das die erzeugte Datei direkt

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

Ich habe nur ein neues Gerät überprüft, und ich habe definitiv immer noch Artefakte, und es ist definitiv keine alte Datei. Noch ist es ein thumbnail; Wenn Sie zum Abschnitt "Info" gehen, werden die korrekten Abmessungen angezeigt. Ist es möglich, dass dies etwas in der Build-Umgebung oder etwas ähnliches ist? – Project

+0

@Projekt Ich fügte meiner Antwort eine vereinfachte Aktivität hinzu, die Sie testen sollten. Es erstellt ein Kreisbild auf transparentem Hintergrund und öffnet die Freigabeabsicht, um diese Datei zu senden, auf Ihrem Gerät auszuführen und das generierte Bild per E-Mail an Sie zu senden. Wenn das Bild einen transparenten Hintergrund hat, wurde es als PNG codiert, wenn es undurchsichtig war, wurde es als JPEG codiert. – yoah

+0

Getestet Ihr Beispiel, es hat einen transparenten Hintergrund und keine Artefakte. Neugierig, ob es sich um das Verzeichnis handelt, in dem ich mich gerade aufhalte, oder um sowas ... stochert es herum, sobald Kaffee passiert ist. – Project

1

Artefakte wie diese sind natürliche und unvermeidliche Folge der JPEG-Kompression.

Sie sollten nicht in PNG-Komprimierung auftauchen. Wenn Sie solche Artefakte beim Erstellen einer PNG-Datei erhalten, wette ich, dass Sie überhaupt keinen PNG-Stream erstellen, sondern einen JPEG-Stream in einer Datei mit einer PNG-Erweiterung. Kein vernünftiger Decoder benötigt die Dateiendung.

+0

Dies sowohl mit saveBitmap.compress auftritt sendet (Bitmap.CompressFormat.JPEG , 100, fOut); und saveBitmap.compress (Bitmap.CompressFormat.PNG, 100, fOut); – Project

+0

Um klarer zu sein; Ich hatte dies lange als CompressFormat.PNG, wobei ich solche Bilder bekam. Der einzige Grund, warum es CompressFormat.JPEG hier ist, ist, dass ich experimentierte, um Dinge über den Code zu ändern, um zu sehen, ob ich andere Ergebnisse habe. – Project

0

Ich bemerkte zwei Dinge in Ihrem Code:

1) Der Dateiname Sie String.format("%s.jpg", appName) oder String.format("%s%03d.png", appName, suffix) unabhängig von der tatsächlichen Codierung sind zu speichern.

2) Das Bitmap, das Sie speichern, hat seine Dichte, die durch prefs.saveImageDensity().get() bestimmt wird, so dass es möglicherweise nicht mit der tatsächlichen Dichte der Bitmap übereinstimmt, die Sie auf dem Bildschirm sehen.

Vielleicht verwirrten Sie sich mit 1) oder vielleicht 2) verursacht die Kompressionsartefakte, die Sie sehen?

+0

1) Ist eine Funktion von meiner Bearbeitung der Post unsachgemäß 2) Ich hätte klarer sein sollen; Ich habe das in allen Arten von Dichten versucht, einschließlich der nativen Dichte. Ich ging einfach zurück und stellte sicher, dass die Dateierweiterung PNG unabhängig war, und auskommentierte Einstellung der DPI. Problem tritt immer noch auf. – Project

+0

In der Dokumentation wird sogar erwähnt, dass das Speichern in PNG ein verlustfreier Vorgang ist, aber möglicherweise gibt es dort einen Fehler. Sie sollten versuchen, ein separates Testprogramm zu schreiben, das eine Bitmap lädt und speichert und sieht, ob das Problem selbst dort auftritt. Das zeigt, ob das Speichern in PNG wirklich verlustfrei ist. – hkBst

+0

Ich ersetzte mit Android API für die Verwendung von [diese Bibliothek] (https://github.com/ultrakain/AndroidBitmapUtil), um als BITMAP zu speichern, und ich bekomme die gleichen Artefakte. Es scheint also nichts mit der Komprimierung zu tun zu haben, sondern mit dem eigentlichen Zeichnen zu einer Bitmap. Was mir ziemlich komisch vorkommt. – Project