4

I am using android custom camera to captured JPG images, But not able to preview them on windows photo viewer. Can anyone please advise. Images are visible using other applications like Ms Paint, Office, Windows 10 Photo application.

enter image description here

3 Answers3

3

As of Android 10, Bitmap.compress() seems to have added an ICC profile.

This issue was caused by an incorrect version value in the added ICC profile. This issue was reported and fixed on the skia issue tracker. I don't know when it will be fixed on Android.
https://bugs.chromium.org/p/skia/issues/detail?id=12491

So, deleting the ICC profile from the jpeg image generated with Bitmap.compress() seems to be the best way at the moment. To solve it programmatically, you need to parse the JPEG data generated with Bitmap.compress() to delete the ICC profile segment.

Update 2022-09-07: This issue is fixed in Android 13. ( SKIA SkICC.cpp )

This is an example code to delete ICC Profile segment. Please refer to Exiv2's "The Metadata in JPEG files" for how the ICC Profile is embedded in the jpeg file.

Update 2022-10-28: Fixed incorrect APP2 segment copy

public class JpegFixer {

    private static final int MARKER = 0xff;
    private static final int MARKER_SOI = 0xd8;
    private static final int MARKER_SOS = 0xda;
    private static final int MARKER_APP2 = 0xe2;
    private static final int MARKER_EOI = 0xd9;

    public void removeIccProfile(InputStream inputStream, String outputPath) throws IOException {
        FileOutputStream outputStream = new FileOutputStream( outputPath );

        if ( inputStream.read() != MARKER || inputStream.read() != MARKER_SOI ) {
            throw new IOException( "Not a JPEG image" );
        }
        outputStream.write( MARKER );
        outputStream.write( MARKER_SOI );

        while ( true ) {
            int marker0 = inputStream.read();
            int marker1 = inputStream.read();
            if ( marker0 != MARKER ) {
                throw new IOException( "Invalid marker:" + Integer.toHexString( marker0 & 0xff ) );
            }
            int length0 = inputStream.read();
            int length1 = inputStream.read();
            final int segmentLength = (length0 << 8 & 0xFF00) | (length1 & 0xFF);
            final int length = segmentLength - 2;
            if ( length < 0 ) {
                throw new IOException( "Invalid length" );
            }

            if ( marker1 == MARKER_EOI || marker1 == MARKER_SOS ) {
                outputStream.write( marker0 );
                outputStream.write( marker1 );
                outputStream.write( length0 );
                outputStream.write( length1 );
                copy( inputStream, outputStream );
                break;
            }

            // ICC_PROFILE\0"
            if ( marker1 == MARKER_APP2 && length >= 12 + 2 ) {
                byte[] buffer = new byte[12];
                read( inputStream, buffer );
                if ( buffer[0] == 'I' && buffer[1] == 'C' &&
                        buffer[2] == 'C' && buffer[3] == '_' &&
                        buffer[4] == 'P' && buffer[5] == 'R' &&
                        buffer[6] == 'O' && buffer[7] == 'F' &&
                        buffer[8] == 'I' && buffer[9] == 'L' &&
                        buffer[10] == 'E' && buffer[11] == 0 ) {
                    // skip segment
                    inputStream.skip( length - buffer.length );
                }
                else {
                    // copy segment
                    outputStream.write( marker0 );
                    outputStream.write( marker1 );
                    outputStream.write( length0 );
                    outputStream.write( length1 );
                    outputStream.write( buffer );
                    copy( inputStream, outputStream, length - buffer.length );
                }
            }
            else {
                // copy segment
                outputStream.write( marker0 );
                outputStream.write( marker1 );
                outputStream.write( length0 );
                outputStream.write( length1 );
                copy( inputStream, outputStream, length );
            }
        }
    }

    public void read(InputStream is, byte[] buffer) throws IOException {
        int totalBytesRead = 0;
        while ( totalBytesRead != buffer.length ) {
            final int bytesRead = is.read( buffer, totalBytesRead, buffer.length - totalBytesRead );
            if ( bytesRead == -1 ) {
                throw new EOFException( "End of data reached." );
            }
            totalBytesRead += bytesRead;
        }
    }

    private int copy(InputStream is, OutputStream out, int numBytes) throws IOException {
        int remainder = numBytes;
        byte[] buffer = new byte[8192];
        while ( remainder > 0 ) {
            int n = is.read( buffer, 0, Math.min( remainder, buffer.length ) );
            if ( n == -1 ) {
                break;
            }
            remainder -= n;
            out.write( buffer, 0, n );
        }
        return numBytes;
    }

    private int copy(InputStream is, OutputStream os) throws IOException {
        int total = 0;
        byte[] buffer = new byte[8192];
        int c;
        while ( (c = is.read(buffer)) != -1 ) {
            total += c;
            os.write( buffer, 0, c );
        }
        return total;
    }

}

Blue Ocean
  • 208
  • 2
  • 8
2

This is obviously caused by an ICC color profile that will be added to the bitmaps by Android. The Windows photo viewer isn't capable to show the images with that ICC color profile.

I did not find out yet how this ICC profile is being generated, I assume that this is done by the Android Bitmap.compress function.

Removing the profile with e.g. ImageMagick will "repair" the files and they will open in photo viewer too.

hschottm
  • 121
  • 4
0

Other Answers to this Question already explained the Issue which may be induced by some Bug related to the Image Compression techniques adopted by Android, preventing the image to be Opened in Windows Photo Viewer.

I have reported this issue to Google and Android Community multiple times but even after following many Android Upgrades, this issue still persist.

I understand this problem is irritating and in the wake of no direct solution, I wish to share an easy and reliable turnaround to this problem with Open Source Softwares which are reliable.

  1. For a Single Image, use GIMP to open the Image and then Export (CTRL+SHIFT+E) the image as it is while replacing the existing file. Export image pop up in GIMP

  2. For Multiple Images, use CAESIUM Image Compressor with Quality set to 100%. Caesium Image Compressor Windows

Anubhav Yadav
  • 143
  • 2
  • 7