2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

画像変換ツールを自作する

Last updated at Posted at 2021-03-13

はじめに

画像変換ツールを自作したい方に向けて、実装例を紹介します。
FreeImageというライブラリを使うことで、PNGJPEGWebPGIFBMPTIFFなど、多様なファイル形式に相互変換可能です。

開発環境の情報

画像変換ツールの実装例

画像ファイルを読み込んでからの画像変換処理を実装しました。

convFreeImage.d
/+ dub.sdl:
	name "convFreeImage"
	dependency "bindbc-freeimage" version=">=0.3.0"
	versions "FI_318"
+/
// dub build --build=release --single convFreeImage.d
import std.conv;
import std.stdio;
import std.string;
import std.windows.charset;

import bindbc.freeimage;

void main(string[] args)
{
	// Argument check
	if ( args.length < 3 ){
	    writeln("Argument required : input output");
		return;
	}
	string ifile = args[1].toMBSz.to!string;
	string ofile = args[2].toMBSz.to!string;
	
	// Load FreeImage
	FISupport fis = loadFreeImage();
	if ( fis != fiSupport ){
		if ( fis == FISupport.noLibrary ){
			writeln("FISupport.noLibrary");
		} else if ( FISupport.badLibrary ){
			writeln("FISupport.badLibrary");
		}
		return;
	}
	
	// Load Image
	FREE_IMAGE_FORMAT fif = FreeImage_GetFileType(ifile.toStringz);
	FIBITMAP *dib = FreeImage_Load(fif, ifile.toStringz);
	scope(exit) FreeImage_Unload(dib);
	if ( dib == null ){
		writeln("FreeImage_Load error");
		return;
	}
	FREE_IMAGE_FORMAT format = FreeImage_GetFIFFromFilename(ofile.toStringz);
	switch ( format ){
		case FIF_GIF:
			if ( FreeImage_GetBPP(dib) > 8 ){
				FIBITMAP *dibold = dib;
				dib = FreeImage_ColorQuantize(dibold, FIQ_WUQUANT);
				FreeImage_Unload(dibold);
			}
			break;
		case FIF_JPEG:
			if ( FreeImage_GetBPP(dib) > 24 ){
				FIBITMAP *dibold = dib;
				dib = FreeImage_ConvertTo24Bits(dibold);
				FreeImage_Unload(dibold);
			}
			break;
		default:
	}
	FreeImage_Save(format, dib, ofile.toStringz);
}

FreeImageを使う際の注意点

GIF形式への変換

GIF形式へ変換を行う場合は、入力データを256色(8ビット)以下に減色する必要があります。
FreeImage_ColorQuantizeを使って減色処理を行っています。

JPEG形式への変換

JPEG形式へ変換を行う場合は、入力データの24ビットカラー(RGB)以下に変換する必要があります。
入力データのピクセル情報が24ビットを超える場合は、FreeImage_ConvertTo24Bitsを使って24ビットカラー(RGB)に変換してます。

コンパイル実行例

コマンドプロンプト
D:\Dev> dub build --build=release --single convFreeImage.d
Performing "release" build using D:\Dev\dmd2\windows\bin\dmd.exe for x86_64.
bindbc-loader 0.3.2: target for configuration "noBC" is up to date.
bindbc-freeimage 0.5.0: target for configuration "dynamic" is up to date.
convFreeImage ~master: building configuration "application"...
Linking...
To force a rebuild of up-to-date targets, run again with --force.

モジュール実行例

コンパイルしたモジュールconvFreeImage.exeを実行するには、FreeImage.dllが必要です。
ダウンロードページより、入手してください。

コマンドプロンプト
D:\Dev> convFreeImage a.jpg b.webp

おまけ:メモリ上の画像データ変換

メモリ上の画像データを読み込むことや、メモリに画像データを書き込むことができます。

  • loadMemoryubyte配列からFreeImageが管理するメモリFIMEMORYへの読み込み
  • saveMemoryがメモリFIMEMORYからubyte配列への画像データを書き込み
  • convertが画像形式変換処理
    の実装例となります。
convFreeImage2.d
/+ dub.sdl:
	name "convFreeImage2"
	dependency "bindbc-freeimage" version=">=0.3.0"
	versions "FI_318"
+/
// dub build --build=release --single convFreeImage2.d
import std.conv;
import std.stdio;
import std.string;
import std.windows.charset;

import bindbc.freeimage;

ubyte[] loadFile(string ifile)
{
	auto file = File(ifile, "rb");
	scope(exit) file.close();
	auto size = file.size();
	ubyte[] buf = file.rawRead(new ubyte[size]);
	return ( buf );
}

void saveFile(ubyte[] buf, string ofile)
{
	auto file = File(ofile, "wb");
	scope(exit) file.close();
	file.rawWrite(buf);
}

FIMEMORY* loadMemory(ubyte[] buf)
{
	FIMEMORY* hmem = FreeImage_OpenMemory(buf.ptr, cast(uint)buf.length);
	return ( hmem );
}

ubyte[] saveMemory(FIMEMORY* hmem)
{
	ubyte* mem;
	uint bufsize;
	FreeImage_AcquireMemory(hmem, &mem, &bufsize);
	FreeImage_SeekMemory(hmem, 0, 0/* = SEEK_SET */);
	ubyte[] buf = new ubyte[bufsize];
	FreeImage_ReadMemory(buf.ptr, 1, cast(uint)buf.length, hmem);
	return ( buf );
}

FIMEMORY* convert(FIMEMORY* himem, FREE_IMAGE_FORMAT format)
{
	FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(himem);
	FIBITMAP* dib = FreeImage_LoadFromMemory(fif, himem);
	switch ( format ){
		case FIF_GIF:
			if ( FreeImage_GetBPP(dib) > 8 ){
				FIBITMAP *dibold = dib;
				dib = FreeImage_ColorQuantize(dibold, FIQ_WUQUANT);
				FreeImage_Unload(dibold);
			}
			break;
		case FIF_JPEG:
			if ( FreeImage_GetBPP(dib) > 24 ){
				FIBITMAP *dibold = dib;
				dib = FreeImage_ConvertTo24Bits(dibold);
				FreeImage_Unload(dibold);
			}
			break;
		default:
	}
	FIMEMORY* homem = FreeImage_OpenMemory();
	FreeImage_SaveToMemory(format, dib, homem);
	return ( homem );
}

void main(string[] args)
{
	// Argument check
	if ( args.length < 3 ){
	    writeln("Argument required : input output");
		return;
	}
	// Load FreeImage
	FISupport fis = loadFreeImage();
	if ( fis != fiSupport ){
		if ( fis == FISupport.noLibrary ){
			writeln("FISupport.noLibrary");
		} else if ( FISupport.badLibrary ){
			writeln("FISupport.badLibrary");
		}
		return;
	}
	ubyte[] buf = loadFile(args[1]);
	FIMEMORY* himem = loadMemory(buf);
	scope(exit) FreeImage_CloseMemory(himem);
	FREE_IMAGE_FORMAT format = FreeImage_GetFIFFromFilename(args[2].toMBSz);
	FIMEMORY* homem = convert(himem, format);
	scope(exit) FreeImage_CloseMemory(homem);
	buf = saveMemory(homem);
	saveFile(buf, args[2]);
}

その他、画像ファイル作成のための参考情報

画像ファイル変換処理を自作する(AVIFHEIC形式)

アニメーションファイルを自作する

画像ビューアを自作する

サムネイル画像を自作する

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?