1. 程式人生 > ># Android 裁剪儲存為透明png圖片,並設定其列印解析度dpi(pHYs)

# Android 裁剪儲存為透明png圖片,並設定其列印解析度dpi(pHYs)

Android 裁剪儲存為透明png圖片,並設定其列印解析度dpi(pHYs)

1.裁剪透明區

做一張同樣大小的圖(路徑圖),想裁去的部分設定為白色,遍歷識別路徑圖的白色畫素點位置,並設定目標圖該位置為透明顏色,關鍵程式碼:

public static Bitmap cut(Bitmap bitmap, Bitmap bitmapPath) {
        for (int x = 0; x < bitmapPath.getWidth(); x++) {
            for (int y = 0; y < bitmapPath.getHeight(); y++) {
                if (bitmapPath.getPixel(x, y) == -1) {
                    bitmap.setPixel(x, y, Color.TRANSPARENT);
                }
            }
        }
        return bitmap;
    }

2.瞭解png的原檔案資料,標頭檔案IHDR,控制物理密度的pHYs,

  • 關於png的標頭檔案IHDR:

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)

  • 關於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圖片檔案資料截圖 在這裡插入圖片描述

3.設定圖片列印解析度dpi(物理解析度), 即為png檔案資料流中的pHYs資料塊

在這裡是檔案結構中沒有pHYs資料塊的方法,在標頭檔案IHDR之後新增pHYs資料塊

**

  • 步驟:獲取bitmap的資料流,向其位元組陣列中新增pHys資料,儲存,bingo!

** 關鍵程式碼:

public static void save(Bitmap bitmap, File file, int dpi) {
        try {
            ByteArrayOutputStream imageByteArray = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.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[] sPHs = 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) (sPHs[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;
    }