SSブログ

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++開発にチェックを入れる。
01.png

②cmakeのインストール
下記ページより自分の環境に合ったzipファイルをダウンロードして適当なところに解凍
https://cmake.org/download/
02.png

binフォルダへのパスを通す
03.png

③webp-imageioの取得
以下のオープンソースを用いる。
gitクライアントを使ってもいいが、面倒なので直接zipでダウンロードして適当なところに解凍
https://bitbucket.org/luciad/webp-imageio/downloads/
04.png
2020/10/06追記
リポジトリが消えていたようなので、forkされたリポジトリを用いる(詳細追記予定)
https://github.com/lonnyj/webp-imageio

④libwebpの取得
同様にzipでダウンロードして解凍
https://github.com/webmproject/libwebp
05.png

解凍してできたlibwebpフォルダを、③の解凍先に移動
また、③の解凍先に「build」という空のフォルダを作成する
06.png

④libwebpのビルド
コマンドプロンプトで、③で作成したbuildフォルダに移動する
07.png

以下のコマンドを実行
cmake .. -G "Visual Studio 16 2019"
08.png

処理が終わったら以下のコマンドを実行
cmake --build .
09.png

以下のように表示されたらビルド成功
10.png

build\src\main\c\Debugにwebp-imageio.dllが生成されている。これがJNIの本体。
11.png

⑤ライブラリjarファイルのビルド
以下のコマンドを実行
cd ..
.\gradlew build -x test
12.png

以下のように表示されていればビルド成功
13.png
javaのバージョンによってはCould not determine java versionと出たりする場合があるが、
その場合はこういった記事を参考にすること。

build/libsにjarファイルが作成されていることを確認する。
14.png
ちなみにここでビルドしたバージョンより低いJavaのバージョンで作成したプロジェクトに入れても
動作しないので注意。

⑥プロジェクトでの読み込み(java.library.pathにJNIを追加)
名前が長いので適当にリネームしてプロジェクトにコピーし、
ビルドパスでjarファイルを指定する。
15.png

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ファイルを置いて読み込ませている。
16.png

ちなみに、アニメーション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も読み込めるやり方が分かればいいのだが。

以上。

タグ:WebP Java Swing SWT
nice!(0)  コメント(0) 
共通テーマ:パソコン・インターネット

nice! 0

コメント 0

コメントを書く

お名前:[必須]
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。