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.
-
Did you find the solution? – Igor M. Jan 18 '21 at 11:32
-
anything new on this? – Ali Izadyar Apr 19 '22 at 03:37
-
1This issue occurs on Android 10 through Android 12L, and has been fixed in Android 13. [Android 13 Skia source code](https://android.googlesource.com/platform/external/skia/+/refs/heads/android13-release/src/core/SkICC.cpp#99) – Blue Ocean Sep 07 '22 at 02:02
3 Answers
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;
}
}

- 208
- 2
- 8
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.

- 121
- 4
-
Skia. https://bugs.chromium.org/p/skia/issues/detail?id=12491 Also, this is a bug since "para" encoding of skia is not supported in v2, yet it was creating v2. https://color.org/security/android_bug.xalter – Валерий Заподовников Oct 06 '21 at 08:26
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.

- 143
- 2
- 7