JavaでWebP画像ファイルを扱う(Windows向け) [プログラミング]
JavaでWebPファイルを扱うのはとんでもなくめんどくさかったのでメモ。
jpgやpngはImageIO.read()で読み込むことができるが、webpはそのままだと非対応で例外となる。
webpを読み込ませるにはネイティブライブラリを自前でビルドしてプロジェクトに組み込む必要がある。
が、あまりちゃんと手順を解説していなかったり非常にわかりにくいのでインストール~導入までをまとめる。
※アニメーションのあるwebpファイルは直接読み込むことができないので注意。
■必要なもの
・Visual Studio 2019
・cmake
・webp-imageio
・libwebp
①Visual Studio 2019のインストール
下記ページよりインストーラーをダウンロード
https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=16
直リンクは以下。
https://download.visualstudio.microsoft.com/download/pr/b79d5d47-f792-4eec-adcf-2da2dfa3e3db/4961e5fa0e5d318394f1e3cb8314a8ebe24f3f414a966f0fe0e4b8719e1cf669/vs_Community.exe
インストール時にはC++開発にチェックを入れる。
②cmakeのインストール
下記ページより自分の環境に合ったzipファイルをダウンロードして適当なところに解凍
https://cmake.org/download/
binフォルダへのパスを通す
③webp-imageioの取得
以下のオープンソースを用いる。
gitクライアントを使ってもいいが、面倒なので直接zipでダウンロードして適当なところに解凍
https://bitbucket.org/luciad/webp-imageio/downloads/
2020/10/06追記
リポジトリが消えていたようなので、forkされたリポジトリを用いる(詳細追記予定)
https://github.com/lonnyj/webp-imageio
④libwebpの取得
同様にzipでダウンロードして解凍
https://github.com/webmproject/libwebp
解凍してできたlibwebpフォルダを、③の解凍先に移動
また、③の解凍先に「build」という空のフォルダを作成する
④libwebpのビルド
コマンドプロンプトで、③で作成したbuildフォルダに移動する
以下のコマンドを実行
cmake .. -G "Visual Studio 16 2019"
処理が終わったら以下のコマンドを実行
cmake --build .
以下のように表示されたらビルド成功
build\src\main\c\Debugにwebp-imageio.dllが生成されている。これがJNIの本体。
⑤ライブラリjarファイルのビルド
以下のコマンドを実行
cd ..
.\gradlew build -x test
以下のように表示されていればビルド成功
javaのバージョンによってはCould not determine java versionと出たりする場合があるが、
その場合はこういった記事を参考にすること。
build/libsにjarファイルが作成されていることを確認する。
ちなみにここでビルドしたバージョンより低いJavaのバージョンで作成したプロジェクトに入れても
動作しないので注意。
⑥プロジェクトでの読み込み(java.library.pathにJNIを追加)
名前が長いので適当にリネームしてプロジェクトにコピーし、
ビルドパスでjarファイルを指定する。
mainの冒頭あたりでSystem.load()でwebp-imageio.dllの絶対パスを指定する。
ImageIO.read()を呼べばwebpがBufferedImageで取得できるはずなので、
あとはSwingなりSWTなりで表示できる。
以下Swingでのサンプルソース
package endpoint;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
public class MainWindow extends JFrame {
private JPanel contentPane;
BufferedImage bufferedImage = null;
/**
* Launch the application.
*/
public static void main(String[] args) {
System.load("C:\\Temp\\webp-imageio.dll");
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MainWindow frame = new MainWindow();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public MainWindow() {
try {
bufferedImage = ImageIO.read(new File("C:\\Temp\\face.webp"));
} catch (UnsatisfiedLinkError | NoClassDefFoundError | IOException e) {}
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
}
public void paint(Graphics graphics) {
graphics.drawImage(bufferedImage, 100, 100, null);
}
}
サンプルではC:\Temp以下にdllファイルとwebpファイルを置いて読み込ませている。
ちなみに、アニメーションwebpからwebpmuxを用いて切り出したフレームに対して
ImageIO.readしても例外で読み込めない場合がある(ライブラリが対応しきれてない?)。
どうしてもやりたい場合はwebpmuxとdwebpをシステムコールで呼び出し、
生成された画像を読み込むのが手っ取り早い。
以下からlibwebp-**-windows-x**.zipをダウンロードして解凍。
https://storage.googleapis.com/downloads.webmproject.org/releases/webp/index.html
binフォルダのwebpmux.exeとdwebp.exeを適当なところに置く。
以下サンプルソース
ちなみに、アニメーションwebpからwebpmuxを用いて切り出したフレームに対して
ImageIO.readしても例外で読み込めない場合がある(ライブラリが対応しきれてない?)。
どうしてもやりたい場合はwebpmuxとdwebpをシステムコールで呼び出し、
生成された画像を読み込むのが手っ取り早い。
以下からlibwebp-**-windows-x**.zipをダウンロードして解凍。
https://storage.googleapis.com/downloads.webmproject.org/releases/webp/index.html
binフォルダのwebpmux.exeとdwebp.exeを適当なところに置く。
以下サンプルソース
package endpoint;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
public class MainWindow extends JFrame {
private JPanel contentPane;
BufferedImage bufferedImage = null;
/**
* Launch the application.
*/
public static void main(String[] args) {
System.load("C:\\Temp\\webp-imageio.dll");
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MainWindow frame = new MainWindow("C:\\Temp\\face");
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public MainWindow(String name) {
final String src = name + ".webp";
final String frame = name + "_0.webp";
final String png = name + ".png";
try {
bufferedImage = ImageIO.read(new File(src));
} catch (UnsatisfiedLinkError | NoClassDefFoundError | IOException e) {
String result = processStart(new String[] {
"C:\\Temp\\webpmux.exe",
"-get",
"frame",
"0",
src,
"-o",
frame});
System.out.println(result);
result = processStart(new String[] {
"C:\\Temp\\dwebp.exe",
frame,
"-o",
png});
File pngFile = new File(png);
if (pngFile.isFile()) {
try {
bufferedImage = ImageIO.read(pngFile);
} catch (IOException e1) {
e1.printStackTrace();
}
};
}
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
}
public void paint(Graphics graphics) {
graphics.drawImage(bufferedImage, 100, 100, null);
}
private String processStart(String[] args) {
StringBuilder sb = new StringBuilder();
ProcessBuilder pb = new ProcessBuilder(args);
pb.redirectErrorStream(true);
try {
Process process = pb.start();
InputStream is = process.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line = "";
while((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
br.close();
is.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return sb.toString();
}
}
サンプルでは最初のwebpが読み込めなかったときにまず0フレーム目を切り出し、
切り出したフレームに対してdwebpでpngなどに変換すれば読み込める。
が、わざわざシステムコールするくらいならライブラリ作成とかせずに
全部こっちで統一したほうがいい気がしている。
というかそもそもアニメーションwebpも読み込めるやり方が分かればいいのだが。
以上。
切り出したフレームに対してdwebpでpngなどに変換すれば読み込める。
が、わざわざシステムコールするくらいならライブラリ作成とかせずに
全部こっちで統一したほうがいい気がしている。
というかそもそもアニメーションwebpも読み込めるやり方が分かればいいのだが。
以上。
コメント 0