title: How to compress Bitmap as JPEG with least quality loss on Android?
body: <p><em>This is not a straightforward problem, please read through!</em></p>
  
  <p>I want to manipulate a JPEG file and save it again as JPEG. The problem is that even without manipulation there's significant (visible) quality loss. 
  <strong>Question</strong>: what option or API am I missing to be able to re-compress JPEG without quality loss (I know it's not exactly possible, but I think what I describe below is not an acceptable level of artifacts, especially with quality=100).</p>
  
  <h2>Control</h2>
  
  <p>I load it as a <code>Bitmap</code> from the file:</p>
  
  <pre><code>BitmapFactory.Options options = new BitmapFactory.Options();
  // explicitly state everything so the configuration is clear
  options.inPreferredConfig = Config.ARGB_8888;
  options.inDither = false; // shouldn't be used anyway since 8888 can store HQ pixels
  options.inScaled = false;
  options.inPremultiplied = false; // no alpha, but disable explicitly
  options.inSampleSize = 1; // make sure pixels are 1:1
  options.inPreferQualityOverSpeed = true; // doesn't make a difference
  // I'm loading the highest possible quality without any scaling/sizing/manipulation
  Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/image.jpg", options);
  </code></pre>
  
  <p>Now, to have a control image to compare to, let's save the plain Bitmap bytes as PNG:</p>
  
  <pre><code>bitmap.compress(PNG, 100/*ignored*/, new FileOutputStream("/sdcard/image.png"));
  </code></pre>
  
  <p><strong><em>I compared this to the original JPEG image on my computer and there's no visual difference.</em></strong></p>
  
  <p>I also saved the raw <code>int[]</code> from <code>getPixels</code> and loaded it as a raw ARGB file on my computer: there's no visual difference to the original JPEG, nor the PNG saved from Bitmap.</p>
  
  <p>I checked the Bitmap's dimensions and config, they match the source image and the input options: it's decoded as <code>ARGB_8888</code> as expected.</p>
  
  <p><strong><em>The above to control checks prove that the pixels in the in-memory Bitmap are correct.</em></strong></p>
  
  <h2>Problem</h2>
  
  <p>I want to have JPEG files as a result, so the above PNG and RAW approaches wouldn't work, let's try to save as JPEG 100% first:</p>
  
  <pre><code>// 100% still expected lossy, but not this amount of artifacts
  bitmap.compress(JPEG, 100, new FileOutputStream("/sdcard/image.jpg"));
  </code></pre>
  
  <p><em>I'm not sure its measure is percent, but it's easier to read and discuss, so I'm gonna use it.</em></p>
  
  <p>I'm aware that JPEG with the quality of 100% is still lossy, but it shouldn't be so visually lossy that it's noticeable from afar. Here's a comparison of two 100% compressions of the same source.</p>
  
  <p>Open them in separate tabs and click back and forth between to see what I mean. The difference images were made using Gimp: original as bottom layer, re-compressed middle layer with "Grain extract" mode, top layer full white with "Value" mode to enhance badness.</p>
  
  <p><em>The below images are uploaded to Imgur which also compresses the files, but since all of the images are compressed the same, the original unwanted artifacts remain visible the same way I see it when opening my original files.</em></p>
  
  <p>Original [560k]:
  <a href="https://i.stack.imgur.com/7wv8T.jpg" rel="noreferrer"><img src="https://i.stack.imgur.com/7wv8T.jpg" alt="Original picture"></a>
  Imgur's difference to original (not relevant to problem, just to show that it's not causing any <em>extra</em> artifacts when uploading the images):
  <a href="https://i.stack.imgur.com/Lrjfu.jpg" rel="noreferrer"><img src="https://i.stack.imgur.com/Lrjfu.jpg" alt="imgur&#39;s distortion"></a>
  IrfanView 100% [728k] (visually identical to original):
  <a href="https://i.stack.imgur.com/bp5QC.jpg" rel="noreferrer"><img src="https://i.stack.imgur.com/bp5QC.jpg" alt="100% with IrfanView"></a>
  IrfanView 100%'s difference to original (barely anything)
  <a href="https://i.stack.imgur.com/qxzCM.jpg" rel="noreferrer"><img src="https://i.stack.imgur.com/qxzCM.jpg" alt="100% with IrfanView diff"></a>
  Android 100% [942k]:
  <a href="https://i.stack.imgur.com/JPtCd.jpg" rel="noreferrer"><img src="https://i.stack.imgur.com/JPtCd.jpg" alt="100% with Android"></a>
  Android 100%'s difference to original (tinting, banding, smearing)
  <a href="https://i.stack.imgur.com/vIuH4.jpg" rel="noreferrer"><img src="https://i.stack.imgur.com/vIuH4.jpg" alt="100% with Android diff"></a></p>
  
  <p>In IrfanView I have to go below 50% [50k] to see remotely similar effects. At 70% [100k] in IrfanView there's no noticable difference, but the size is 9th of Android's.</p>
  
  <h2>Background</h2>
  
  <p>I created an app that takes a picture from Camera API, that image comes as a <code>byte[]</code> and is an encoded JPEG blob. I saved this file via <code>OutputStream.write(byte[])</code> method, that was my original source file. <code>decodeByteArray(data, 0, data.length, options)</code> decodes the same pixels as reading from a File, tested with <code>Bitmap.sameAs</code> so it's irrelevant to the issue.</p>
  
  <p>I was using my Samsung Galaxy S4 with Android 4.4.2 to test things out.
  Edit: while investigating further I also tried Android 6.0 and N preview emulators and they reproduce the same issue.</p>