3

对于某些 Mac 用户,我们网站上的图像不会在 Safari 中显示,他们报告说看不到图像或看到黑色图像。这是一个例子:

http://s3-eu-west-2.amazonaws.com/bp18.boxcleverpress.com/Boxclever_logo_chartreuse.png

我发现的是:

  • 图像在 PC 上显示
  • 在某些 Mac 上显示图像(我有一台旧的还可以)
  • 图像显示在 iPhone 和 iPad 上
  • 图片为PNG
  • 我已经用 pngtastic 优化了图像
  • 将图像复制到 Mac 并使用 Adob​​e Photoshop 打开时,出现错误:文件格式模块无法解析文件
  • 当我尝试在 Windows 上的 Photoshop Elements 中打开 pngtastic 优化文件时,我也收到了该错误
  • 当我尝试在 Windows 上的 Photoshop 中打开优化的文件时,我收到错误 IDAT:不正确的数据检查

我将用未优化的图像替换优化的图像,但我不确定这个问题是否与 pngtastic 或 Adob​​e 图像库或其他东西有关。

4

2 回答 2

3

问题在于Zopfli.java,被 pngtastic 包含在内。

它使用这个 Java 代码来计算 Adler-32 校验和:

/**
 * Calculates the adler32 checksum of the data
 */
private static int adler32(byte[] data) {
    int s1 = 1;
    int s2 = 1 >> 16;
    int i = 0;
    while (i < data.length) {
        int tick = Math.min(data.length, i + 1024);
        while (i < tick) {
            s1 += data[i++];
            s2 += s1;
        }
        s1 %= 65521;
        s2 %= 65521;
    }

    return (s2 << 16) | s1;
}

但是,byteJava 中的 s始终是有符号的,因此它可能会为某些数据输入返回错误的校验和值。此外,赤裸裸的int声明s1s2导致进一步的复杂化。

使用(我的 C 版本)相同的代码并data明确声明 assigned char和 boths1s2as signed int,我得到一个错误的校验和FFFF9180——正是你损坏的 PNG 中的校验和。

如果我将声明更改为使用unsigned charand unsigned int,它会再次返回正确的校验和1BCD6EB2


zopfli 中用于 Adler-32 校验和的原始 C 代码unsigned始终使用类型,因此受此影响的只是 Java 实现。

于 2018-01-10T00:34:12.923 回答
1

问题似乎是由于在我使用 pngtastic 优化的 PNG 中使用了 zopfli 压缩。解决方法是使用不同的 pngtastic 压缩选项,然后可以在 Photoshop 中读取 PNG。

使用不同的压缩算法将导致较少的优化。

我不知道为什么 zopfli 压缩是个问题,可能是我的代码有问题(尽管当只更改 zopli 选项时相同的代码工作正常),在 pngtastic 中,或者 MacOS 和 Adob​​e 没有支持zopfli。

@usr2564301 进行了一些调查,我的示例图像中压缩数据的 Adler-32 校验和似乎不正确。usr2564301 还测试了 pngtastic 代码,发现它产生了正确的校验和。问题可能在于我如何处理来自 pngtastic 的字节流。

下面的代码使用 pngtastic (com.googlecode.pngtastic.core) 执行 PNG 优化

public static final String OPT_ZOPFLI = "zopfli";
public static final String OPT_DEFAULT = "default";
public static final String OPT_IMAGEOPTIM = "imageoptim";

private String optimization = OPT_ZOPFLI;

public void optimizePng(File infile, String out) {

     final InputStream in;
     try {
         in = new BufferedInputStream(new FileInputStream(infile));
         final PngImage image = new PngImage(in);

         // optimize
         final PngOptimizer optimizer = new PngOptimizer();

         optimizer.setCompressor(optimization, 1);
         final PngImage optimizedImage = optimizer.optimize(image, false, 9);

         // export the optimized image to a new file
         final ByteArrayOutputStream optimizedBytes = new ByteArrayOutputStream();
         optimizedImage.writeDataOutputStream(optimizedBytes);
         optimizedImage.export(out, optimizedBytes.toByteArray());

       } catch (FileNotFoundException e) {
         e.printStackTrace();
     } catch (IOException e) {
         e.printStackTrace();
     }
 }
于 2018-01-04T19:16:44.533 回答