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;
}
}
这显然是由Android添加到位图中的ICC颜色配置文件引起的。Windows照片查看器无法显示具有该ICC颜色配置文件的图像。
我还没有找出生成此ICC配置文件的方法,我认为这是通过Android Bitmap.compress函数完成的。
使用ImageMagick等工具删除该配置文件将“修复”文件,它们也可以在照片查看器中打开。