我们网站上的图片不会在Safari中显示给某些Mac用户,他们会报告看不到图像或黑色图像。这是一个例子:
http://s3-eu-west-2.amazonaws.com/bp18.boxcleverpress.com/Boxclever_logo_chartreuse.png
我发现的是:
我将用未经优化的图像替换优化的图像,但我不确定这个问题是否与pngtastic或Adobe图像库或其他内容有关。
答案 0 :(得分:2)
问题在于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;
}
但是,Java中的byte
是always signed,因此它可能会为某些数据输入返回错误的校验和值。此外,int
和s1
的裸s2
声明会导致进一步的复杂化。
使用(我的C版本)相同的代码,data
明确声明为signed char
,s1
和s2
同为signed int
,我得到了错误的校验和FFFF9180
- 正好是损坏的PNG中的那个。
如果我更改声明以使用unsigned char
和unsigned int
,则会再次返回正确的校验和1BCD6EB2
。
zopfli中Adler-32校验和的original C code始终使用unsigned
类型,因此只是Java实现受此影响。
答案 1 :(得分:1)
问题似乎是由于我使用pngtastic优化的PNG中使用了zopfli压缩。解决方法是使用不同的pngtastic压缩选项,然后在Photoshop中可以读取PNG。
使用不同的压缩算法将导致更少的优化。
我不确定为什么zopfli压缩是一个问题,可能是我的代码中存在错误(尽管只有zopli选项被更改时相同的代码才能正常工作),pngtastic,或者MacOS和Adobe不支持zopfli。
@ usr2564301做了一些调查,看来我示例图片中压缩数据的Adler-32校验和不正确。 usr2564301还测试了pngtastic代码并发现它产生了正确的校验和。问题可能在于我如何处理pngtastic中的字节流。
下面的代码使用pngtastic执行PNG优化(com.googlecode.pngtastic.core)
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();
}
}