概要

GDI+はGDI(Graphics Design Interface)の機能を強化したものです。
jpegファイル等を比較的簡単に扱うことができます。
本プログラムは、src.jpgファイル読み込み右90度回転させてdtc.jpgへ保存するプログラムです。

テスト環境

コンパイラ

Visual C++ 2008 Standard 32/64bit
Visual C++ 2013 Express 32/64bit

プロジェクトの作成

Win32プロジェクト Windowsアプリケーション

実行環境

Windows 8.1 Enterprise 64bit
Windows 7 EnterPrise Service Pack 1 64bit
Windows Vista Ultimate Service Pack 2 32bit
Windows XP Professional Service Pack 3 32bit

プログラムソースの概要

GDI+のオブジェクトは、Gdiplusという名前空間に属しています。
これらを呼び出すには、オブジェクトの前にGdiplus::を付加するか、using namespace Gdiplus;でディフォルトの名前空間をGdiplusに変更する方法があります。
オブジェクトごとにGdiplus::を付加するのは煩雑なので、using namespaceを使用しています。

_tWinMain関数

Windowsから最初に_tWinMain関数が呼び出されます。
GdiplusStartup関数でGDI+を初期化します。
GetEncoderClsid関数でBitmapオブジェクトからjpegファイルに保存するとき必要なjpeg用のエンコーダを取得します。
Bitmap::FromFileによりファイル名からBitmapオブジェクトを作成しsrcImage変数に保存します。
ファイル名は、UNICODEでなければならないので、マルチバイトでコンパイルされた場合は、MultiByteToWideChar APIによりUNICODEで変換するコードを有効にします。
ファイルが開けない場合は、FromFileはNULLを返すのでプログラムを終了させます。
GetWidthとGetHeight関数でsrcImageのイメージの横ピクセル数と高さ方向のピクセル数を取得します。
GetHorizontalResolutionとGetVerticalResolution関数によりsrcImageの解像度を取得します。
new演算子によりsrcImageを90度回転させたときに必要なピクセルサイズのBitmapオブジェクトを作成しdtcBmpに保存します。
SetResolution関数によってdtcBmpオブジェクトの解像度を設定します。
FromImageによりdtcBmpを基にGraphicsオブジェクトdtcGraphicsを作成します。
DrawImageによりdtcGraphicsにsrcImageを右方向へ90度回転させて描画します。
90度の回転は、元のイメージの左上・右上・左下の座標について変換後座標を指定して、DrawImageを呼び出すことにより実現しています。
回転・傾斜・拡大縮小や反転等が同時にできます。
最後に、GdiplusShutdown関数でGDI+の終了処理をします。

右方向へ90度回転させる場合

下図は左側の長方形を右90度回転させた結果が右側の長方形となります。
図形の幅がx、高さがyの時
Point型の配列を以下の様に作成してdtcGraphics.DrawImage(&srcImage, pt, 3)を呼び出すと右方向へ90度回転して描画されます。
Point pt[] = { Point(y, 0), Point(y, x), Point(0, 0) };
A(0,0) B(x,0) C(x,y) D(0,y) D(0,0) A(y,0) B(y,x) C(0,x) SVGの代替画像

右方向へ任意角度回転させる場合

下図において、A点を中心として線分AB'を左回りに回転させた場合のX軸とはさむ角 B A B'を任意角θと定義します。
任意の角度を回転される場合、上記の例の様に四角形の頂点を回転させると描画領域をはみ出しファイルに正常に保存できませんので回転後の座標を計算後、座標値に補正をかけます。
まず、A(0,0)を中心に回転させるものとします。
回転後の各点(B',C',D')の座標は、三角関数を使って以下の様に表せます。回転結果は、下図の中央の図になります。


















上記の座標のうちx座標y座標それぞれ負の座標になる点が発生しますので最小の点を抽出します。
抽出結果を,mx,myとします。
回転後の図形の幅と横を計算するために、x座標y座標それぞれ最大値なる座標を抽出します。
抽出結果を,nx,nyとします。
回転図形を含む最小の長方形は、下図の右図の緑の四角形で表せます。
この緑の四角形の左上を座標(0,0)に補正した後の各点の座標(A",B",C",D")は以下の通りです。





A"とB"とD"の座標をPoint型配列に代入してDrawImageを呼び出すと任意角度の回転が実現します。
Graphicsオブジェクトは、左上(0,0)と右下E点を満たす大きさである必要があります。
以下のフォームに角度の入力またはプッシュボタンをクリックすると回転のイメージがつかめるかと思います。
角度θ=

A(0,0) B(x,0) C(x,y) D(0,y) A(0,0) B' C' D' (0,0) A" B" C" D" E SVGの代替画像

反転・鏡像・ミラー

下図は左側の長方形を左右方向に反転させた結果が右側の長方形となります。
図形の幅がx、高さがyの時
Point型の配列を以下の様に作成してdtcGraphics.DrawImage(&srcImage, pt, 3)を呼び出すと左右反転して描画されます。
Point pt[] = { Point(x, 0), Point(0, 0), Point(x, y) };
A(0,0) B(x,0) C(x,y) D(0,y) B'(0,0) A'(x,0) D'(x,y) C'(0,y) SVGの代替画像

傾斜

下図は左側の長方形を左右方向に傾斜させた結果が右側の長方形となります。
図形の幅がx、高さがyの時、左右方向の傾斜ピクセル数をtとすると
Point型の配列を以下の様に作成してdtcGraphics.DrawImage(&srcImage, pt, 3)を呼び出すと左右反転して描画されます。
Point pt[] = { Point(t, 0), Point(x+t, 0), Point(0, y) };
A'とB'とD'の座標をPoint型配列に代入してDrawImageを呼び出すと任意角度の回転が実現します。
Graphicsオブジェクトは、左上(0,0)と右下E点を満たす大きさである必要があります。
以下のフォームにオフセットtの入力またはプッシュボタンをクリックすると傾斜のイメージがつかめるかと思います。
横オフセットt=

A(0,0) B(x,0) C(x,y) D(0,y) A'(t,0) B'(x+t,0) C'(x,y) D'(0,y) SVGの代替画像

プログラムソース

//	jpegファイルを右方向へ90度回転させて保存するr(GDI+)
//	Visual C++ 2008/2013	32/64bit

#include <windows.h>
#include <gdiplus.h>
#include <tchar.h>

#pragma comment(lib,"gdiplus.lib")

using namespace Gdiplus;
GdiplusStartupInput gdiSI;
ULONG_PTR           gdiToken;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL InitApp(HINSTANCE, WNDPROC, TCHAR*);
BOOL InitInstance(HINSTANCE, TCHAR*, int);
HWND hWnd;

//	jpegエンコーダーを取得する
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);

// 最初に呼び出される関数

int WINAPI _tWinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, TCHAR* lpsCmdLine, int nCmdShow){
	GdiplusStartup(&gdiToken, &gdiSI, NULL);
	CLSID     encoderClsid;
	if(GetEncoderClsid(L"image/jpeg", &encoderClsid)<0){
		MessageBox(0,TEXT("jpegエンコーダーが取得できませんでした。"),TEXT("エラー"),MB_OK);
		return (int)1;
	}

	TCHAR* srcFile = TEXT("src.jpg");	//	読み込むファイル名指定
	TCHAR* dtcFile = TEXT("dtc.jpg");	//	保存するファイル名指定

#ifndef UNICODE
	WCHAR srcFileW[MAX_PATH];
	MultiByteToWideChar(932, 0, srcFile, -1, srcFileW, sizeof(srcFileW) / sizeof(TCHAR));
	Image& srcImage = *Bitmap::FromFile(srcFileW);
#else
	Image& srcImage = *Bitmap::FromFile(srcFile);
#endif
	if (&srcImage == NULL){
		MessageBox(0, _TEXT("ソースファイルが開けませんでした"), _TEXT("エラー"), MB_OK);
		return 2;
	}

	int px = srcImage.GetWidth();
	int py = srcImage.GetHeight();
	float xDpi = srcImage.GetHorizontalResolution();
	float yDpi = srcImage.GetVerticalResolution();

	Bitmap& dtcBmp = *(new Bitmap(py, px));
	dtcBmp.SetResolution(xDpi , yDpi);
	Graphics& dtcGraphics = *Graphics::FromImage(&dtcBmp);
//	左上・右上・右下の座標を右へ90度回転させた座標を指定する。
	Point pt[] = { Point(py, 0), Point(py, px), Point(0, 0) };

	dtcGraphics.DrawImage(&srcImage, pt, 3);
	srcImage.~Image();
	Status s;
#ifndef UNICODE
	WCHAR dtcFileW[MAX_PATH];
	MultiByteToWideChar(932, 0, dtcFile, -1, dtcFileW, sizeof(dtcFileW) / sizeof(TCHAR));
	s = dtcBmp.Save(dtcFileW, &encoderClsid);
#else
	s = dtcBmp.Save(dtcFile, &encoderClsid);
#endif

	if (s != Ok){
		MessageBox(0, _TEXT("ディスティネーションファイルが開けませんでした"), _TEXT("エラー"), MB_OK);
		return 2;
	}

	delete &dtcBmp;
	dtcGraphics.~Graphics();

	GdiplusShutdown(gdiToken);

	return 0;
}

//	jpegエンコーダーを取得する

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) {
	UINT  num = 0;
	UINT  size = 0;
	ImageCodecInfo* pImageCodecInfo;
	GetImageEncodersSize(&num, &size);
	if (size == 0)
		return -1;
	pImageCodecInfo = (ImageCodecInfo*)new char[size];
	if (pImageCodecInfo == NULL)
		return -1;
	GetImageEncoders(num, size, pImageCodecInfo);
	for (UINT n = 0; n<num; ++n) {
		if (wcscmp(pImageCodecInfo[n].MimeType, format) == 0) {
			*pClsid = pImageCodecInfo[n].Clsid;
			delete pImageCodecInfo;
			return n;
		}
	}
	delete pImageCodecInfo;
	return -1;
}

ソースファイルと実行ファイルのダウンロード

ダウンロードjpegrote1.zip(74.8kByte)

ZIPファイルに含まれるファイル
jpegview1cpp
jpegview1.exe
image.jpg