修复png工具

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完整可运行程序)




你可能感兴趣的:(java,ios,png)