iOS应用,xcode会对PNG文件进行优化,图片预览无法显示。之前网上找了些工具,发现大的png图无法完全修复,网上找到原因:在大的png图有多个idat chunk,解决这问题的python代码https://gist.github.com/urielka/3609051。下边代码主要是根据python版本用java实现,文章末尾会提供对应的可运行程序下载。swing界面部分代码省略,只提供修复图片的代码实现。
package com.penngo.fixpng; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.zip.CRC32; import java.util.zip.DeflaterOutputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; /** * * @author penngo * */ public class PngUtil { public static final int BUFFERSIZE = 4096; public static void fixPng(String file, String fixFile) throws Exception { byte[] png_header = { (byte) 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; // PNG byte[] data_chunk = { 0x49, 0x44, 0x41, 0x54 }; // IDAT byte[] end_chunk = { 0x49, 0x45, 0x4e, 0x44 }; // IEND byte[] cgbi_chunk = { 0x43, 0x67, 0x42, 0x49 }; // CgBI byte[] ihdr_chunk = { 0x49, 0x48, 0x44, 0x52 }; // IHDR File f = new File(file); FileInputStream in = new FileInputStream(f); long size = f.length(); byte[] oldPNG = readBytes(in); in.close(); byte[] header = substr(oldPNG, 0, 8); if (toHexString(header).equals(toHexString(png_header)) == false) { throw new Exception("Not a PNG file!"); } ByteArrayOutputStream idatAcc = new ByteArrayOutputStream(); ByteArrayOutputStream newPNG = new ByteArrayOutputStream(); newPNG.write(header); boolean breakLoop = false; int chunkPos = header.length; int width = 0; int height = 0; while (chunkPos < size) { boolean skip = false; byte[] chunkLength = substr(oldPNG, chunkPos, 4); int sl = byteToInt(chunkLength); byte[] chunkType = substr(oldPNG, chunkPos + 4, 4); byte[] chunkData = substr(oldPNG, chunkPos + 8, sl); chunkPos += sl + 12; if (toHexString(chunkType).equals(toHexString(ihdr_chunk))) { width = byteToInt(substr(chunkData, 0, 4)); height = byteToInt(substr(chunkData, 4, 8)); } if (toHexString(chunkType).equals(toHexString(data_chunk))) { idatAcc.write(chunkData, 0, chunkData.length); skip = true; } if (toHexString(chunkType).equals(toHexString(cgbi_chunk))) { skip = true; } long crc = 0; if (toHexString(chunkType).equals(toHexString(end_chunk))) { try { chunkData = gzinflate(idatAcc.toByteArray()); } catch (Exception e) { throw new Exception("normal PNG file, not need to fix"); } chunkType = data_chunk.clone(); ByteArrayOutputStream newdata = new ByteArrayOutputStream(); for (int y = 0; y < height; y++) { int i = newdata.size(); newdata.write(chunkData[i]); for (int x = 0; x < width; x++) { i = newdata.size(); newdata.write(chunkData[i + 2]); newdata.write(chunkData[i + 1]); newdata.write(chunkData[i + 0]); newdata.write(chunkData[i + 3]); } } chunkData = newdata.toByteArray(); chunkData = gzcompress(chunkData); sl = chunkData.length; ByteArrayOutputStream dd2 = new ByteArrayOutputStream(); dd2.write(chunkType); dd2.write(chunkData); byte[] dd = dd2.toByteArray(); crc = crc32(dd); breakLoop = true; } if (!skip) { newPNG.write(intToByte(sl)); newPNG.write(chunkType); if (sl > 0) { newPNG.write(chunkData); } newPNG.write(intToByte((int) crc)); } if (breakLoop) { break; } } idatAcc.close(); FileOutputStream out = new FileOutputStream(fixFile); out.write(newPNG.toByteArray()); newPNG.close(); out.close(); } public static void main(String[] args) throws Exception { System.out.println("==="); String name = "C:\\Users\\penngo\\Documents\\Default-Portrait@2x~ipad.png"; String name2 = "C:\\Users\\penngo\\Documents\\fixed\\Icon@2x_3.png"; fixPng(name, name2); } public static byte[] longToByte(long number) { byte[] v = new byte[8]; v[0] = (byte) (number >> 56); v[1] = (byte) (number >> 48); v[2] = (byte) (number >> 40); v[3] = (byte) (number >> 32); v[4] = (byte) (number >> 24); v[5] = (byte) (number >> 16); v[6] = (byte) (number >> 8); v[7] = (byte) (number >> 0); return v; } public static int byteToInt(byte[] b) { int mask = 0xff; int temp = 0; int n = 0; for (int i = 0; i < 4; i++) { n <<= 8; temp = b[i] & mask; n |= temp; } return n; } public static byte[] intToByte(int number) { byte[] v = new byte[4]; v[0] = (byte) (number >> 24); v[1] = (byte) (number >> 16); v[2] = (byte) (number >> 8); v[3] = (byte) (number >> 0); return v; } public static String toHexString(byte[] b) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < b.length; i++) { sb.append(Integer.toHexString(b[i] & 0xFF)); } return sb.toString(); } public static byte[] gzinflate(byte[] b) throws Exception{ try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); InputStream inflInstream = new InflaterInputStream( new ByteArrayInputStream(b), new Inflater(true)); byte bytes[] = new byte[1024]; while (true) { int length = inflInstream.read(bytes, 0, 1024); if (length == -1) { break; } outputStream.write(bytes, 0, length); } byte[] array = outputStream.toByteArray(); outputStream.close(); return array; } catch (Exception e) { throw e; // e.printStackTrace(); } //return null; } public static byte[] substr(byte[] b, int start, int length) { byte[] by = new byte[length]; for (int i = 0; i < length; i++) { by[i] = b[start + i]; } return by; } public static byte[] gzcompress(byte[] utfEncodedBytes) throws IllegalArgumentException, IllegalStateException { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DeflaterOutputStream gzipOutputStream = new DeflaterOutputStream( baos); gzipOutputStream.write(utfEncodedBytes); gzipOutputStream.finish(); return baos.toByteArray(); } catch (Exception e) { throw new IllegalStateException("GZIP compression failed: " + e, e); } } public static long crc32(byte[] b) { CRC32 crc = new CRC32(); crc.update(b); return crc.getValue(); } public static byte[] readBytes(InputStream inputStream) { byte[] bytes = null; if (inputStream == null) { throw new RuntimeException("inputStream is null"); } try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); transfer(inputStream, outputStream); bytes = outputStream.toByteArray(); outputStream.close(); return bytes; } catch (IOException ex) { throw new RuntimeException("couldn't read bytes from inputStream", ex); } } public static int transfer(InputStream in, OutputStream out) { int total = 0; byte[] buffer = new byte[BUFFERSIZE]; try { int bytesRead = in.read(buffer); while (bytesRead != -1) { out.write(buffer, 0, bytesRead); total += bytesRead; bytesRead = in.read(buffer); } return total; } catch (IOException ex) { throw new RuntimeException("couldn't write bytes to output stream", ex); } } }
附件
fixpng.jar (http://download.csdn.net/detail/penngo/6390727需要安装jre或jdk)
fixpng.zip (http://download.csdn.net/detail/penngo/6390795完整可运行程序)