Load image from binary base64
<p>EDIT: This is a bug in Android version &lt;4.3 Kitkat. It relates to the libjpeg library in Android, which can't handle JPEGs with missing EOF/EOI bits, or apparently with metadata/EXIF data that it doesn't like.
<a href="https://code.google.com/p/android/issues/detail?id=9064" rel="nofollow">https://code.google.com/p/android/issues/detail?id=9064</a></p>

<p>ORIGINAL QUESTION:</p>

<p>I have an issue when loading an image in my app.</p>

<p>My endpoint sends <code>JSON</code> which contains a <code>BASE64</code> encoded image. Depending on the REST call, these images can be <code>PNG</code> or <code>JPG</code>. Some of the <code>JPG</code> files suffer from an issue where they are missing an <code>EOF</code> bit at the end. The <code>PNG</code> files work, and some <code>JPG</code> files work, but unfortunately a lot of these <code>JPG</code> files with the issue are present in the Oracle DB (stored as <code>BLOB</code>). I don't have control of the DB.</p>

<p>I have been looking through Google bugs here:
<a href="https://code.google.com/p/android/issues/detail?id=9064" rel="nofollow">https://code.google.com/p/android/issues/detail?id=9064</a>
and here:
<a href="https://code.google.com/p/android/issues/detail?id=57502" rel="nofollow">https://code.google.com/p/android/issues/detail?id=57502</a></p>

<p>The issue is also seen where the encoding is <code>CYMK</code> using a custom ICC profile.</p>

<p>Decoding the image the standard way returns <code>false</code>:</p>

<pre><code>byte[] imageAsBytes = Base64.decode(base64ImageString, Base64.DEFAULT);
return BitmapFactory.decodeByteArray(imageAsBytes, 0, imageAsBytes.length);
</code></pre>

<p>According to the bug reports above, the built in JPG parser in Android is to blame.</p>

<p>I'm trying to figure out a workaround for my device, which is stuck on <code>4.2.2</code>. I have no other option on this OS version.</p>

<p>I thought it might be a good idea to try and use an image loader library like <code>Universal Image Loader</code>, but it requires I either have the image stored locally, or stored on a URL. As I get the data in <code>BASE64</code> from the REST server, I can't use this. An option is to support <code>decodeByteArray</code> in a custom class that extends <code>BaseImageDecoder</code>, as stated by the dev at the bottom here: <a href="https://github.com/nostra13/Android-Universal-Image-Loader/issues/209" rel="nofollow">https://github.com/nostra13/Android-Universal-Image-Loader/issues/209</a></p>

<p>Here's where I get stuck. I already have a custom image decoder to try handle the issue of the missing <code>EOF</code> marker in the JPG file, but I don't know how to edit it to add support for <code>decodeByteArray</code>.</p>

<p>Here is my <code>CustomImageDecoder</code>:</p>

<pre><code>public class CustomImageDecoder extends BaseImageDecoder {

    public CustomImageDecoder(boolean loggingEnabled) {
        super(loggingEnabled);
    }

    @Override
    protected InputStream getImageStream(ImageDecodingInfo decodingInfo) throws IOException {
        InputStream stream = decodingInfo.getDownloader()
                .getStream(decodingInfo.getImageUri(), decodingInfo.getExtraForDownloader());
        return stream == null ? null : new JpegClosedInputStream(stream);
    }

    private class JpegClosedInputStream extends InputStream {

        private static final int JPEG_EOI_1 = 0xFF;
        private static final int JPEG_EOI_2 = 0xD9;
        private final InputStream inputStream;
        private int bytesPastEnd;

        private JpegClosedInputStream(final InputStream iInputStream) {
            inputStream = iInputStream;
            bytesPastEnd = 0;
        }

        @Override
        public int read() throws IOException {
            int buffer = inputStream.read();
            if (buffer == -1) {
                if (bytesPastEnd &gt; 0) {
                    buffer = JPEG_EOI_2;
                } else {
                    ++bytesPastEnd;
                    buffer = JPEG_EOI_1;
                }
            }

            return buffer;
        }
    }
}
</code></pre>

<p>By the way, using the above custom class, I am trying to load my byte array like this:</p>

<pre><code>byte[] bytes = Base64.decode(formattedB64String, Base64.NO_WRAP);
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
String imageId = "stream://" + is.hashCode();
...
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.displayImage(imageId, userImage, options);
</code></pre>

<p>and I get this error:
<code>ImageLoader: Image can't be decoded [stream://1097215584_656x383]</code></p>

<p>Universal Image loader does not allow the <code>stream://</code> schema, so I created a custom <code>BaseImageDownloader</code> class that allows it:</p>

<pre><code>public class StreamImageDownloader extends BaseImageDownloader {

    private static final String SCHEME_STREAM = "stream";
    private static final String STREAM_URI_PREFIX = SCHEME_STREAM + "://";

    public StreamImageDownloader(Context context) {
        super(context);
    }

    @Override
    protected InputStream getStreamFromOtherSource(String imageUri, Object extra) throws IOException {
        if (imageUri.startsWith(STREAM_URI_PREFIX)) {
            return (InputStream) extra;
        } else {
            return super.getStreamFromOtherSource(imageUri, extra);
        }
    }
}
</code></pre>

<p>So if anyone can help me create a better <code>CustomImageDecoder</code> that handles  a <code>BASE64</code> encoded string, or a <code>byte[]</code> containing an image so I can use <code>decodeByteArray</code>, I would be grateful!</p>

<p>Thank you.</p>