博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
修复png工具
阅读量:5759 次
发布时间:2019-06-18

本文共 7033 字,大约阅读时间需要 23 分钟。

  hot3.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);		}	}}

附件

 (需要安装jre或jdk)

(完整可运行程序)

转载于:https://my.oschina.net/penngo/blog/168420

你可能感兴趣的文章
我的友情链接
查看>>
Centos6.6安装选包及基础场景说明
查看>>
java基础面试题-1
查看>>
深克隆与序列化效率的比较
查看>>
lamp+nginx代理+discuz+wordpress+phpmyadmin搭建一
查看>>
nagios监控使用139邮箱报警
查看>>
Windows Phone 7 中各种Task解说(启动器与选择器)
查看>>
罗森伯格助力2011年中国智能建筑技术发展应用论坛哈尔滨站
查看>>
网络割接
查看>>
windows server 2016 活动目录(二)
查看>>
openstack G版 修改vm的flavor级别
查看>>
python_控制台输出带颜色的文字方法
查看>>
java泛型中特殊符号的含义
查看>>
一秒 解决 ERROR 1044 (42000): Access denied for user ''@'localhost' to database 'mysql 问题
查看>>
Android组件化最佳实践 ARetrofit原理
查看>>
舍弃浮躁, 50条重要的C++学习建议
查看>>
同步手绘板——将View的内容映射成Bitmap转图片导出
查看>>
【Android游戏开发之十】(优化处理)详细剖析Android Traceview 效率检视工具!分析程序运行速度!并讲解两种创建SDcard方式!...
查看>>
微信小程序之wx.navigateback往回携带参数
查看>>
陌陌和请吃饭之类的应用,你要是能玩转,那就厉害了
查看>>