2016年11月28日 星期一

如何在Java上使用 pngquant png壓縮工具 (JNI)

pngquant是一個不錯的png圖檔縮工具,但它是用C語言寫成的,如果要用Java來使用它的話,就必需使用JNI (Iava Native Interface)的方式來調用C的程式,剛好工作上需要在Java的環境上使用它,特別在這紀錄下要使用pngquant的JNI流程。

pngquant的官網上有提供圖形化GUI的工具及命令列的Command-Line工具,如果要用程式碼的使用方式的話,可以點 "a library" 連結到它的 lib 網站 (libimagequant),在lib網站中,可以看到許多不同語言的使用方式。

我的環境是Windows 7,jdk1.5.0_15,必須製作相應libimagequant專案的dll檔。
制作dll檔有許多方式,libimagequant官網給Windows的建議為使用Viusal Studio或MinGW工具來產生dll,我選擇最簡單的方式,去Microsoft官網下載安裝Visual Studio 2015來用。

點擊 "MSVC-compatible branch of the library" 可連到MSVC版lib的Git網站,把整個專案下載下來後,就可以開始進行JNI的步驟。

以下是JNI的詳細步驟:

  1. 使用命令列視窗(cmd)到下載並已被解壓縮的libimagequant-msvc資料夾,打上以下指令來產生相應PngQuant.class、Image.class、 Result.class (Class檔請自行compile產生)的 標頭檔 (副檔名為h)
    javah org.pngquant.PngQuant
    javah org.pngquant.Image
    javah org.pngquant.Result
    產生出來的檔名會被多加一些前綴,把它們都刪掉,再把得到的pngQuant.h、Image.h、Result.h丟到org/pngquant的目錄下。
  2. 開啟Visual Studio 2015,New 一個 Win32 Project,Application type選 DLL ,Additional options選Empty Project就好。
  3. 先把org/pngquant/PngQuant.c丟到org外面那層(因為要配合裡面寫的include的path路徑),在Solution Explorer的Source File中,將全部檔(包括org資料夾之下)都Add進去(有些其實用不到,不過沒關係都先丟進去)
  4. 這時看PngQuant.h可能會發現有被畫紅線的Error,對專案安右鍵選擇"Properties" --> Configuration --> C/C++  設定 Additional Include Directories ,請設定jdk中include和include/win32的位置,例如以下路徑:
    C:\Program Files (x86)\Java\jdk1.5.0_15\include
    C:\Program Files (x86)\Java\jdk1.5.0_15\include\win32
    然後將專案的mode從debug改成release
  5. 開啟Eclipse,建立一個測試專案,將org/pngquant下的java檔全部丟到專案中,並且再將Visual Studio release出來的dll檔丟進去。
    撰寫一個測試的程式,例如叫做libimagequantTest.java,內容如下:
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    
    import javax.imageio.ImageIO;
    
    import org.pngquant.PngQuant;
    
    public class libimagequantTest {
    
     public static void main(String[] args) {
       BufferedImage newImg;
       
       PngQuant pngQuant = new PngQuant();
       
       try {
        newImg = pngQuant.getRemapped(ImageIO.read(new File("D:\\old_picture.png")));
        ImageIO.write(newImg, "png", new File("D:\\new_picture.png"));
       } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
       }
       
       pngQuant.close();
     }
    
    }
    
    最後的檔案結構會像這樣
  6. 在專案上按右鍵選擇Properties --> Java Build Path 設定 Nativ Library Location,例如此例我設定的是
    libimagequant_Test/src/org/pngquant
  7. 最後放上一張檔案大小較大的圖 D:\\old_picture.png,執行libimagequantTest.java,如果有出現另一張圖D:\\new_picture.png,就代表大功告成了。
源始碼下載:
pngquant JNI_Test.7z

2016年11月27日 星期日

Ardulink - Arduino的Java控制方案

Ardulink是一個開源Arduino的Java解決方案,提供了使用Java來控制Arduino的方法,讓Java操作Arduino版子變的更簡單,其源裡是先再Arduino上寫入程式,用來監聽從序列阜(Serial Port)上傳來的指令並做相應的操作、也用序列阜傳回相應的回應,序列阜的連接方式用到了之前文章"用Java與Arduino的序列阜溝通(使用RXTX)"講到的RXTX,而Java就用包好的API來對Arduino送指令。

要使用Ardulink,可以先到Ardulink的下載頁下載,會得到一個壓縮檔,找個位置解壓縮後就可以開始進行Ardulink的Java範例,LED閃光(BlinkLED),下面講解詳細步驟:
  1. 下載Ardulink並解壓縮以後,可以看到如下的檔案結構。
    我們會需要用到的有"winDLLs"及"lib"資料夾。
    打開winDLLs,可以看到32bit和64bit兩個資料夾,依自已需求選擇打開資料夾後可以再看到三個檔案:LibusbJava.dll、RXTXcomm.jar、rxtxSerial.dll
    LibusbJava.dll及rxtxSerial.dll是Java使用JNI要用的檔案。
    RXTXcomm.jar是Java要引入lib的其中一個檔案。
    lib資料夾裡的檔案是要給Java要引入的lib。
    而在bin資料夾裡面,放了兩個bat檔,其內容就只是幫忙把LibusbJava.dll、RXTXcomm.jar、rxtxSerial.dll放到lib資料夾而已。
    為了之後方便且起,在這裡我也把LibusbJava.dll、RXTXcomm.jar、rxtxSerial.dll通通放到lib資料夾。
  2. 接著是Arduino這端,我們必需要先再Arduino版子上寫入Serial Port溝通相關的程式,打開sketches資料夾,可以看到許多角本,這邊選擇ArdulinkProtocol裡的ArdulinkProtocol.ino執行就好,當然如果有相應的版子也可以選擇相應的角本,例如Digispark可以選擇ArdulinkProtocol4Digispark。
    需要注意的是,也為不同型號的版子有些許不同,例如Arduino Due目前沒支援tone()和noTone()這兩個函式,所以要稍微修改角本,因為我使用的是Arduino Due,為了簡單測試方便,就先把用到tone()和noTone()的指令先注解掉。
  3. 再來是Java這端,這裡以Eclipse Neon IDE及jdk1.8.0_05為例,檔案結構如下,
    BlinkLED是我們的主程式,用來控制Arduino的LED燈開闗(第13號PIN),先把剛剛lib資料夾裡的jar檔及RXTXcomm.jar引入library中。
    接著設定專案的Native library location,路徑為LibusbJava.dll、rxtxSerial.dll所在的路徑。
    BlinkLED.java的內容如下(參考https://github.com/Ardulink/Ardulink-2),作用為一秒亮、一秒暗Arduino板子的LED燈(Pin 13)。
    import java.io.IOException;
    import java.util.concurrent.TimeUnit;
    
    import org.ardulink.core.Link;
    import org.ardulink.core.Pin;
    import org.ardulink.core.Pin.DigitalPin;
    import org.ardulink.core.convenience.Links;
    
    public class BlinkLED {
    
     public static void main(String[] args) {
          Link link = Links.getDefault();
             DigitalPin pin = Pin.digitalPin(13);  //這裡13 pin是板子的LED燈
             boolean power = true;
             while (true) {
                 System.out.println("Send power:" + power);
                 try {
                  //swicthDigitalPin()為數位輸出,對應了Arduino的digitalWrite()
         link.switchDigitalPin(pin, power);  
         power = !power;
                  TimeUnit.SECONDS.sleep(1);  //1秒亮、1秒暗
        } catch (IOException | InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
        }             
             }
     }
    }
    
  4. 執行BlinkLED.java後,應該就可以看到Arduino板子上的LED燈一亮一暗了。
    執行時可能會有下警告訊息,主要是RXTX版本及SLF4J Class衝突等警告,不過並不會影響程式運行,所以可暫且忽略。
    Stable Library
    =========================================
    Native lib Version = RXTX-2.2-20081207 Cloudhopper Build rxtx.cloudhopper.net
    Java lib Version   = RXTX-2.1-7
    WARNING:  RXTX Version mismatch
     Jar version = RXTX-2.1-7
     native lib Version = RXTX-2.2-20081207 Cloudhopper Build rxtx.cloudhopper.net
    SLF4J: Class path contains multiple SLF4J bindings.
    SLF4J: Found binding in [jar:file:/C:/Users/Administrator/Downloads/ardulink/lib/slf4j-jdk14-1.7.12.jar!/org/slf4j/impl/StaticLoggerBinder.class]
    SLF4J: Found binding in [jar:file:/C:/Users/Administrator/Downloads/ardulink/lib/slf4j-log4j12-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
    SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
    SLF4J: Actual binding is of type [org.slf4j.impl.JDK14LoggerFactory]



附注:
Ardulink有做一個UI介面可以跟Arduino板子作連接並控制(ardulink-console-2.0.1.jar),放在下載下來的 lib/ardulink-console-2.0.1.jar,在執行set32bitWindowsRXTX.bat或set64bitWindowsRXTX.bat(或自己手動把LibusbJava.dll、RXTXcomm.jar、rxtxSerial.dll丟到lib資料下)後,可以直接開啟ardulink-console-2.0.1.jar,看到如下視窗介面,按下Connect與Arduino連接後,在許多功能可以使用,對於想要測一下Arduino,還不想花太多時間寫程式時是個不錯的工具。