Windows照片查看器无法打开Android Studio中的位图压缩图像

luaexgnf  于 2022-11-16  发布在  Android
关注(0)|答案(3)|浏览(260)

我正在使用Android自定义相机捕捉JPG图像,但无法在Windows照片查看器上预览它们。任何人都可以请建议。图像是可见的使用其他应用程序,如Ms Paint,Office,Windows 10照片应用程序。

uplii1fm

uplii1fm1#

这显然是由Android将添加到位图的ICC颜色配置文件导致的。Windows照片查看器无法显示具有该ICC颜色配置文件的图像。
我还没有发现这个ICC配置文件是如何生成的,我假设这是由Android Bitmap.compress函数完成的。
删除配置文件,例如ImageMagick将“修复”文件,他们将在照片查看器中打开太。

63lcw9qa

63lcw9qa2#

从Android 10开始,Bitmap.compress()似乎添加了一个ICC配置文件。
这个问题是由添加的ICC配置文件中的错误版本值引起的。这个问题在skia问题跟踪器上报告并修复。我不知道什么时候会在Android上修复。
https://bugs.chromium.org/p/skia/issues/detail?id=12491
因此,从Bitmap.compress()生成的jpeg图像中删除ICC配置文件似乎是目前最好的方法。要通过编程解决此问题,您需要解析Bitmap.compress()生成的JPEG数据,以删除ICC配置文件段。

2022年9月7日更新:此问题已在Android 13中修复。(SKIA SkICC.cpp)

这是一个删除ICC配置文件段的示例代码。请参考Exiv2的“JPEG文件中的元数据”,了解ICC配置文件如何嵌入到jpeg文件中。

更新日期:2022年10月28日:修复了不正确的APP2段复制

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;
    }

}
yvgpqqbh

yvgpqqbh3#

此问题的其他答案已经解释了可能由Android采用的图像压缩技术相关的一些错误导致的问题,阻止图像在Windows照片查看器中打开。
我已经多次向谷歌和Android社区报告了这个问题,但即使在进行了多次Android升级后,这个问题仍然存在。
我知道这个问题是令人恼火的,在没有直接的解决方案之后,我希望分享一个简单可靠的周转这个问题与开源软件是可靠的。
1.对于单个图像,使用GIMP打开图像,然后在替换现有文件时按原样导出(CTRL+SHIFT+E)图像。

1.对于多个图像,请使用CAESIUM图像压缩程序,并将“质量”设置为100%。

相关问题