一般PNG圖片壓縮的Java實現

      由於對資源或網速的要求,在手機遊戲或一般的網頁遊戲中,希望能對圖片進最大可能的壓縮,以節省資源。最近公司做的項目也有對這方面的需求,於是我在網上逛了半天,希望能發現現成版的Java方法可以使用(用程序來壓縮而不藉助於工具,要不然2萬多張的圖片你想累死人?雖然PS有批量功能,它卻無法按原來的路徑存放);失望的是,好像沒發現什麼能直接使用代碼,哪怕是提個解決方案也很少。既然網上找不到合適的,那就自己動手,豐衣足食。

      關於PNG圖片的格式我在此就不多說,圖片壓縮方面的理論知識我也不在這多此一舉,網上資料一大堆。開門見山,我們的目標是怎樣用Java把PNG圖片盡最大可能的壓縮;當然,不能看出明顯的失真。

      一:BufferedImage類

      在Java中,關於圖片處理我們自然而然的想到了BufferedImage類,深入瞭解它,你會發現其實Java已經幫我們做好了圖片壓縮了,只是壓縮完的圖片和我們的需求有一點點偏差.......先看看BufferedImage最常用的構造方法:

 public BufferedImage(int width,int height,int imageType);

      構造一個類型爲預定義圖像類型之一的 BufferedImage,其中imageType有以下幾種:

BufferedImage.TYPE_INT_RGB:8 位 RGB 顏色分量,不帶alpha通道。

BufferedImage.TYPE_INT_ARGB:8 位 RGBA 顏色分量,帶alpha通道。

BufferedImage.TYPE_INT_ARGB_PRE:8 位 RGBA 顏色分量,已預乘以 alpha。

BufferedImage.TYPE_INT_BGR:8 位 RGB 顏色分量Windows 或 Solaris 風格的圖像,不帶alpha通道。

BufferedImage.TYPE_3BYTE_BGR:8位GBA顏色分量,用3字節存儲Blue、Green和Red三種顏色,不存在alpha。

BufferedImage.TYPE_4BYTE_ABGR:8位RGBA顏色分量,用3字節存儲Blue、Green和Red三種顏色以及1字節alpha。

BufferedImage.TYPE_4BYTE_ABGR_PRE:具有用3字節存儲的Blue、Green和Red三種顏色以及1字節alpha。

BufferedImage.TYPE_USHORT_565_RGB:具有5-6-5RGB顏色分量(5位Red、6位Green、5位Blue)的圖像,不帶alpha。

BufferedImage.TYPE_USHORT_555_RGB:具有5-5-5RGB顏色分量(5位Red、5位Green、5位Blue)的圖像,不帶alpha。

BufferedImage.TYPE_BYTE_GRAY:表示無符號byte灰度級圖像(無索引)。

BufferedImage.TYPE_USHORT_GRAY:表示一個無符號short 灰度級圖像(無索引)。

BufferedImage.TYPE_BYTE_BINARY:表示一個不透明的以字節打包的 1、2 或 4 位圖像。

BufferedImage.TYPE_BYTE_INDEXED:表示帶索引的字節圖像。  

      其實imageType就是對應着Java內不同格式的壓縮方法,編號分別爲1-13;下面我們將一張原圖用下面的幾句代碼分別調用不同的參數生成圖片看看:

      原圖如下,PNG格式,大小24.0KB:

原圖(24.0KB)

     壓縮後的圖片:

TYPE_INT_RGB(9.21KB)  TYPE_INT_ARGB(11.4KB)  TYPE_INT_ARGB_PRE(11.4KB)  TYPE_INT_BGR(9.21KB)  TYPE_3BYTE_BGR(9.21KB)  TYPE_4BYTE_ABGR(11.4KB)  TYPE_4BYTE_ABGR_PRE(11.4KB)   TYPE_USHORT_565_RGB(5.72KB)   TYPE_USHORT_555_RGB(4.74KB)  TYPE_BYTE_GRAY(3.24KB)  TYPE_USHORT_GRAY(6.62KB)  TYPE_BYTE_BINARY(0.38KB)  TYPE_BYTE_INDEXED(3.50KB)

      從圖片看到,黑白照片最小,不過這不是我們想要,排除;最後一張TYPE_BYTE_INDEXED類型的(其實就是PNG8)是彩色,也不大,但是失真太厲害了,排除;剩下的透明的那幾個大小都一樣,排除;對比剩下背景不透明的那幾張,TYPE_USHORT_555_RGB就是我們要的壓縮類型了。

       二:555格式的位圖

    555格式其實是16位位圖中的一種。16位位圖最多有65536種顏色。每個色素用16位(2個字節)表示。這種格式叫作高彩色,或叫增強型16位色,或64K色。16位中,最低的5位表示藍色分量,中間的5位表示綠色分量,高的5位表示紅色分量,一共佔用了15位,最高的一位保留,設爲0。在555格式下,紅、綠、藍的掩碼分別是:0x7C00、0x03E0、0x001F(在BufferedImage源碼中也有定義)。

      三:進一步處理

      從圖片效果可以看出,555格式非常接近真彩色了,而圖像數據又比真彩圖像小的多,非常滿足我們的要求。但是我們需要背景是透明的,而用TYPE_USHORT_555_RGB生成的圖片背景卻是不透明的,自然而然的我們想到了把不透明的背景替換成透明的不就行了。 

   其中的isBackPixel(rgb)用於判斷當前像素是否爲背景像素:

   經轉化後的圖片如下:

   背景像素替換後(5.28KB)

   轉化後稍微大了一點,這個可以接受;要命的是帶了一個黑色邊框。爲什麼呢?原因很簡單,原圖中邊框部分的像素是介於透明和不透明之間的,而經過555格式壓縮後所有像素都變成了布爾透明,也就是說所有的像素要麼是透明的要麼就是不透明的。

   最容易想到的方法就是把邊框的像素換成原圖邊框的像素,關鍵在於怎麼判斷當前像素是否爲圖片的邊框像素,這個算法可能得花費你一定的時間,下面只是我想到的一種實現:

   其中的cutedImage=cutImageAuto(sourceImage);是對原圖進行裁剪,代碼如下:

   改善後得到的圖片:

   改善轉化方法後的圖片(6.34KB)

   實際上,這種方法只適用於圖片顏色分明(邊框顏色分明,背景顏色唯一),黑色像素不多的圖片。一些比較特殊的圖片就得特殊處理了,如以下圖片:

   蜂(壓縮前) 召喚門(壓縮前)         壓縮後      蜂(壓縮後)   召喚門(壓縮後)    

   原因是黑色不透明像素也是圖片實體的一部分,這樣就把它替換成白色透明的了。可以把代碼改一下,但是圖片的大小會增加不少,就是把程序認爲是背景顏色的像素替換成原圖片的像素;將compressImage()方法中的第33、43、61行改成 pixel[i]=sourcePixel[i]; 即可。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章