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

4

我正在使用自定义Android相机拍摄JPG图像,但无法在Windows照片查看器上预览它们。请问是否有人可以给予建议?使用其他应用程序(如Ms Paint、Office、Windows 10照片应用程序)可以查看这些图像。

输入图片说明


你找到解决方案了吗? - Igor M.
这方面有什么新进展吗? - Ali Izadyar
1
此问题发生在 Android 10 到 Android 12L上,并已在 Android 13 中得到修复。Android 13 Skia源代码 - Blue Ocean
3个回答

3
截至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-09-07:该问题已在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;
    }

}


你能分享一些相关代码吗? - Ali Izadyar

2

这显然是由Android添加到位图中的ICC颜色配置文件引起的。Windows照片查看器无法显示具有该ICC颜色配置文件的图像。

我还没有找出生成此ICC配置文件的方法,我认为这是通过Android Bitmap.compress函数完成的。

使用ImageMagick等工具删除该配置文件将“修复”文件,它们也可以在照片查看器中打开。


Skia。https://bugs.chromium.org/p/skia/issues/detail?id=12491 此外,“para”编码的skia在v2中不受支持,因此它正在创建v2是一个错误。https://color.org/security/android_bug.xalter - Валерий Заподовников

0

这个问题的其他答案已经解释了可能由Android采用的图像压缩技术中的某些Bug引起的问题,导致无法在Windows照片查看器中打开图像。

我多次向Google和Android社区报告了此问题,但即使在遵循许多Android升级之后,此问题仍然存在。

我理解这个问题很恼人,在没有直接解决方案的情况下,我希望分享一个易于使用且可靠的解决方法,其中包括一些可靠的开源软件。

  1. 对于单个图像,请使用GIMP打开图像,然后将其导出(CTRL+SHIFT+E),同时替换现有文件。 Export image pop up in GIMP

  2. 对于多个图像,请使用质量设置为100%的CAESIUM图像压缩器。 Caesium Image Compressor Windows


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接