Android 设置PNG图片的打印分辨率dpi(pHYs)
1.了解png的原文件数据,头文件IHDR,控制物理密度的pHYs,
关于png的头文件IHDR:
/satanzw/article/details/38757121
png图片都是以固定标识89 50 4E 47 0D 0A 1A 0A开始,然后接着IHDR
例如一张png从头开始为:
89 50 4E 47 0D 0A 1A 0A00 00 00 0D49 48 44 52
00 00 00 06 00 00 00 06 08 02 00 00 00 F9 7D AA
93
对于上面,89 50 4E 47 0D 0A 1A 0A 是PNG头部署名域,表示这是一个PNG图片
00 00 00 0D 描述IHDR头部的大小 ,13个字节
49 48 44 52 是Chunk Type Code, 这里对应为‘IHDR ’,49对应为十进制的73,即为大写字母I的ASCII编码,同理后三字节分别对应H,D,R
00 00 00 06 00 00 00 06 08 02 00 00 00 是Chunk Data,前四字节是图片宽度,随后四字节是图片高度,00 00 00 06表示此图片宽高都是6, 6x6像素
F9 7D AA 93 是IHDR的CRC校验,至此头部结束
关于PNG数据块(Chunk)
数据块有统一的格式,四字节数据块长度,四字节数据块名字,对应长度的数据值,四字节CRC校验
/lidabo/p/3701197.html
/TR/PNG-Chunks.html
关于pHYs数据块
对于pHYs,同理,四个长度,四个名字,9个数据,四个校验,共21字节
其中,9字节数据部分为关键,前4字节是横向密度,随后4字节是纵向密度,最后一个是密度单位,0未知,1是米。
通常dpi是指dots per inch,png中单位是dots per meter, 一英寸=0.0254米
也就是说,想要设置150dpi的话,这里要设置成150/0.0254=5906,但是注意5906是十进制!
The pHYs chunk specifies the intended pixel size or aspect ratio for display of the image. It contains:
Pixels per unit, X axis: 4 bytes (unsigned integer)
Pixels per unit, Y axis: 4 bytes (unsigned integer)
Unit specifier: 1 byte
The following values are legal for the unit specifier:
0: unit is unknown
1: unit is the meter
When the unit specifier is 0, the pHYs chunk defines pixel aspect ratio only; the actual size of the pixels remains unspecified. Conversion note: one inch is equal to exactly 0.0254 meters.
例如对于pHYs数据块如下:
00 00 00 09 70 48 59 73 00 00 17 12 00 00 17
12 01 67 9f d2 52
表示数据部分有9字节(00 00 00 09),名字是pHYs(70 48 59 73),打印分辨率是5906dpm,即150dpi(00 00 17 12),单位是米(01),CRC校验结尾(67 9f d2 52)
四字节crc校验具体算法我没研究,但是发现随便写上去也不影响图片的一般使用,所以这四字节我就随便取了四个值
以下是一张2x2像素的150dpi的png图片文件数据截图
2.设置图片打印分辨率dpi(物理分辨率), 即为png文件数据流中的pHYs数据块
在这里是文件结构中没有pHYs数据块的方法,在头文件IHDR之后添加pHYs数据块
**
步骤:获取bitmap的数据流,向其字节数组中添加pHys数据,保存,bingo!
**
关键代码:
public static void save(Bitmap bitmap, File file, int dpi) {try {ByteArrayOutputStream imageByteArray = new ByteArrayOutputStream();press(pressFormat.PNG, 100, imageByteArray);byte[] imageData = imageByteArray.toByteArray();imageByteArray.close();FileOutputStream fileOutputStream = new FileOutputStream(file);fileOutputStream.write(setDpi(imageData, dpi));fileOutputStream.close();imageData = null;Log.e("aaa", "saved");} catch (Exception e) {Log.e("aaa", "Wrong in Class 'BitmapToPng'");Log.e("aaa", e.getMessage());}}private static byte[] setDpi(byte[] imageData, int dpi) {byte[] imageDataCopy = new byte[imageData.length + 21];System.arraycopy(imageData, 0, imageDataCopy, 0, 33);System.arraycopy(imageData, 33, imageDataCopy, 33 + 21, imageData.length - 33);int[] pHYs = new int[]{0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 23, 18, 0, 0, 23, 18, 1, 103, 159, 210, 82};for (int i = 0; i < 21; i++) {imageDataCopy[i + 33] = (byte) (pHYs[i] & 0xff);}dpi = (int) (dpi / 0.0254);imageDataCopy[41] = (byte) (dpi >> 24);imageDataCopy[42] = (byte) (dpi >> 16);imageDataCopy[43] = (byte) (dpi >> 8);imageDataCopy[44] = (byte) (dpi & 0xff);imageDataCopy[45] = (byte) (dpi >> 24);imageDataCopy[46] = (byte) (dpi >> 16);imageDataCopy[47] = (byte) (dpi >> 8);imageDataCopy[48] = (byte) (dpi & 0xff);// for (int i = 0; i < 30; i++) {// String line = "";// for (int j = 0; j < 16; j++) {//int temp = imageDataCopy[16 * i + j] & 0xFF;//line += Integer.toHexString(temp) + " ";// }// Log.e(i + "", line);// }return imageDataCopy;}