概要

複数のJpegファイルのサイズを変更するプログラムです。
Jpegファイルは本プログラムにファイルをドラックドロップして指定します。
保存先フォルダー名はエディットボックスに直接入力するか参照ボタンを押して選択します。
変更後のサイズは幅と高さで指定します。入力画像の縦横比が指定されたサイズと異なる場合は、指定サイズを限度とする縦横比を保存したサイズに変更します。
ドラッグドロップ後にOKボタンをクリックすると変換されます。
複数ファイルの同時変更に対応します。変更後のファイルは指定されたフォルダーに保存されます。

画像サイズ変更の概要


変更履歴

2014.04.26 初版
2014.05.07 EXEファイル実行前にドラッグ&ドロップした場合のファイル名にダブルコーテーションが付加される場合に対応。

テスト環境

コンパイラ

Visual C++ 2008/2013 Express 32/64bit マルチバイト/UNICODE

実行環境

Windows XP Professional Service Pack 3 32bit(Virtual Box上の仮想マシーン)
Windows 7 Enterprise Service Pack 1 64bit

動作例

本プログラムを起動した状態

プログラムソースの概要

gresize.cpp

_tWinMain

GdiplusStartupによりGDI+の初期化をします。
_tWinMain関数よりダイアログボックスを表示します。
ダイアログが終了したらGdiplusShutdownによりGDI+を終了させます。

DlgProc1

ダイアログの初期化時にDlgProc1関数にWM_INITDIALOGメッセージが発生し、DragAcceptFiles APIを読み出すことによりファイルのドラッグ&ドラッグの受付を許可します。
ドラッグ&ドロップ
本プログラム起動時に引数があった場合は、リストボックスにその内容を設定します。
ファイルがドラッグ&ドラッグされるとDlgProc1関数にWM_DROPFILESメッセージが発生するので、DragQueryFile関数で最初にファイルの個数を取得し、次にファイル名を取得します。
取得されたファイル名は、リストボックスに登録します。
フォルダー名の参照
ボタンがクリックされると、WM_COMMANDメッセージが発生するのでフォルダー名の参照ボタンに対するものである場合GetDir関数を呼び出します。GetDir関数からSHBrowseForFolder APIを呼び出し、フォルダーを選択するダイアログが初期化されます。初期化時にBrowseCallbackProc関数が呼び出されます。BFFM_INITIALIZEDメッセージの場合フォルダー選択の初期時のフォルダーを設定します。
OKボタン
ボタンがクリックされると、DlgProc1関数にWM_COMMANDメッセージが発生するので、OKボタンのメッセージの場合、リストボックスの内容を取得し変換元のファイル名とします。変更後のサイズ及び変更後の保存先フォルダー名を取得します。
変換元のパス名からPathFindFileName APIによりファイル名のみ取得します。このファイル名に保存先フォルダー名を付加して変更後のパス名とします。
resize関数でJpegファイルのサイズを変更します。

resize

Bitmap::FormFileで変更元ファイルを開きBitmapに保存します。
変更元の画像サイズを取得します。
変更後のサイズのBitmapを作成します。
先のBitmapをもとにGraphicsを作成します。 DrawImageにより変換元のBitmapをGraphicsに描画します。
SaveによりBitmapをファイルに保存します。
作成したBitmap及びGraphicsを解放します。

ソースコード

gresize.cpp

// Jpegファイルのサイズを変換する(縦横比保存)
// GDI+ランタイムが必要
// ファイルの指定は本ソフトにファイルをドラッグ&ドロップ

#include <windows.h>
#include <commctrl.h> 
#include <direct.h> 
#include <shlobj.h>
#include <shlwapi.h>
#include <gdiplus.h>
#include <stdio.h>
#include <tchar.h>
#include "resource.h"

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

using namespace Gdiplus;
GdiplusStartupInput gdiSI;
ULONG_PTR	gdiToken;
CLSID     encoderClsid;

LRESULT CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
int GetDir(HWND hWnd,TCHAR* path,TCHAR* root);	//	フォルダー名を取得するコモンコントロール
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid); //	エンコーダーの取得
int resize(TCHAR* dtc,TCHAR* src,int dx,int dy);

TCHAR* file=0;
TCHAR def_dir[MAX_PATH];

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst,TCHAR* lpszCmdLine, int nCmdShow){
	GdiplusStartup(&gdiToken, &gdiSI, NULL);
	if(GetEncoderClsid(L"image/jpeg", &encoderClsid)<0){
		MessageBox(0,TEXT("jpegエンコーダーが取得できませんでした。"),TEXT("エラー"),MB_OK);
		return (int)0;
	}
	TCHAR f[MAX_PATH];
	if(lpszCmdLine[0]){
		if(lpszCmdLine[0]==_T('\"')){
			_tcscpy_s(f,sizeof(f)/sizeof(TCHAR),lpszCmdLine+1);
			f[ _tcslen(f) -1]=_T('\0');
			file=f;
		}else
			file=lpszCmdLine;
	}
	if(DialogBox(hInstance, TEXT("DLG1"), 0, (DLGPROC)DlgProc1)){

	}
	GdiplusShutdown(gdiToken);
	return 0;
}


LRESULT CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
	TCHAR buf[MAX_PATH];
	TCHAR dtc_dir[MAX_PATH];
	TCHAR src_file[MAX_PATH*2];
	TCHAR dtc_file[MAX_PATH*2];
	TCHAR* p;
	HDROP hDrop;
	UINT fmax;
	int	i;
	int w,h;
	switch (msg) {
		case WM_INITDIALOG:
			DragAcceptFiles(hDlg, TRUE);
			if(file){
				SendMessage(GetDlgItem(hDlg,IDC_LISTBOX1) , LB_ADDSTRING , 0 , (LPARAM)file);
				SetWindowText(GetDlgItem(hDlg,IDC_LABEL1) , _TEXT("ファイル数 1"));
			}
			SetWindowText(GetDlgItem(hDlg,IDC_EDIT_DIR) , _TEXT("C:\\"));
			return TRUE;
		case WM_DROPFILES:
			hDrop = (HDROP)wParam;
			fmax = DragQueryFile((HDROP)wParam, 0xFFFFFFFF, NULL, 0);
			for(i = 0; i < (int)fmax; i++) {
				DragQueryFile(hDrop, i, buf, sizeof(buf)/sizeof(TCHAR));
				SendMessage(GetDlgItem(hDlg,IDC_LISTBOX1) , LB_ADDSTRING , 0 , (LPARAM)buf);
			}
			DragFinish(hDrop);
			fmax=(UINT)SendMessage(GetDlgItem(hDlg,IDC_LISTBOX1) ,LB_GETCOUNT,0,0);
			_stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("ファイル数 %i"),fmax);
			SetWindowText(GetDlgItem(hDlg,IDC_LABEL1) , buf);
			break;
		case WM_COMMAND:
			switch (LOWORD(wParam)) {
				case IDC_BUTTON_DIR:
					_tcscpy_s(def_dir,sizeof(def_dir)/sizeof(TCHAR),_TEXT("C:\\"));
					GetDir(hDlg,buf,0);
					SetWindowText(GetDlgItem(hDlg,IDC_EDIT_DIR) , buf);
					return TRUE;
				case IDOK:
					GetWindowText(GetDlgItem(hDlg,IDC_EDIT_WIDTH),buf,sizeof(buf)/sizeof(TCHAR));
					w=_ttoi(buf);
					GetWindowText(GetDlgItem(hDlg,IDC_EDIT_HIGHT),buf,sizeof(buf)/sizeof(TCHAR));
					h=_ttoi(buf);
					GetWindowText(GetDlgItem(hDlg,IDC_EDIT_DIR),dtc_dir,sizeof(dtc_dir)/sizeof(TCHAR));
					fmax=(UINT)SendMessage(GetDlgItem(hDlg,IDC_LISTBOX1) ,LB_GETCOUNT,0,0);
					SendMessage(GetDlgItem(hDlg,IDC_PROGBAR1),PBM_SETRANGE,0,MAKELPARAM(0,fmax));
					SendMessage(GetDlgItem(hDlg,IDC_PROGBAR1),PBM_SETSTEP,1,0);
					for(i = 0; i < (int)fmax; i++){
						SendMessage(GetDlgItem(hDlg,IDC_LISTBOX1) ,LB_GETTEXT,i,(LPARAM)src_file);
						p=PathFindFileName(src_file);
						_stprintf_s(dtc_file,sizeof(dtc_file)/sizeof(TCHAR),_TEXT("%s\\%s"),dtc_dir,p);
						resize(dtc_file,src_file,w,h);
						SendMessage(GetDlgItem(hDlg,IDC_PROGBAR1),PBM_STEPIT,0,0);
						_stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("ファイル数 %i/%i"),i,fmax);
						SetWindowText(GetDlgItem(hDlg,IDC_LABEL1) , buf);

					}
					EndDialog(hDlg, TRUE);
					return TRUE;
				case IDCANCEL:
					EndDialog(hDlg,FALSE);
					return FALSE;
				default:
					return FALSE;
			}
			default:
				return FALSE;
	}
	return TRUE;
}


//	ディレクトリ名を取得するコモンダイアログを表示する

int __stdcall BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData);


int GetDir(HWND hWnd,TCHAR* path,TCHAR* root){
	BROWSEINFO bInfo; 
	LPITEMIDLIST pIDList; 
	bInfo.hwndOwner = hWnd; // ダイアログの親ウインドウのハンドル 
	bInfo.pidlRoot =NULL; // ルートフォルダをデスクトップフォルダとする 
	bInfo.pszDisplayName =path; //szDisplayName; // フォルダ名を受け取るバッファへのポインタ 
	bInfo.lpszTitle =TEXT("保存するフォルダの選択"); // ツリービューの上部に表示される文字列 
	bInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE ; // 表示されるフォルダの種類を示すフラグ 
	bInfo.lpfn = BrowseCallbackProc; // BrowseCallbackProc関数のポインタ 
	bInfo.lParam = NULL; 
	pIDList = SHBrowseForFolder(&bInfo); 
	if(pIDList == NULL){
		path[0]=_TEXT('\0');
		return FALSE; //何も選択されなかった場合 
	}else{
		if(!SHGetPathFromIDList(pIDList, path))
			return FALSE;//変換に失敗 
		CoTaskMemFree( pIDList );// pIDListのメモリを開放 
		return TRUE; 
	}
}
//	上記ダイアログのコールバック関数

int __stdcall BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData){
	if(uMsg==BFFM_INITIALIZED){
		SendMessage(hwnd,BFFM_SETSELECTION,(WPARAM)TRUE,(LPARAM)def_dir );	//	コモンダイアログの初期ディレクトリ
	}
	return 0; 
} 

int resize(TCHAR* dtc,TCHAR* src,int dx,int dy){

	Image* srcImage=0;

#ifdef UNICODE
	srcImage=Bitmap::FromFile(src);	
#else
	WCHAR srcFile[MAX_PATH];
	MultiByteToWideChar(932,0,src,-1,srcFile,sizeof(srcFile)/sizeof(WCHAR));
	srcImage=Bitmap::FromFile(srcFile);	
#endif

	if(srcImage==0 || srcImage->GetLastStatus()!=Gdiplus::Ok){
		MessageBox(0,src,_TEXT("Error"),MB_OK);
		srcImage->~Image();
		return -1;
	}
	int	px=srcImage->GetWidth();	//	jpgファイルの画像の大きさ(横)
	int py=srcImage->GetHeight();	//	jpgファイルの画像の大きさ(縦)
	float px_res=srcImage->GetHorizontalResolution();	//	jpgファイルの解像度DPI(横)
	float py_res=srcImage->GetVerticalResolution();	//	jpgファイルの解像度DPI(縦)
	
	double pxy=double(px)/double(py);	// オリジナル横/縦

	double dxy=double(dx)/double(dy);	// 変換後横/縦

	int dx2,dy2;

	if(pxy>dxy){	//	オリジナルが横が長いので横基準に縦を縮小する
		dx2=dx;
		dy2=(int)double(dx/pxy);
	}else{
		dy2=dy;
		dx2=(int)double(dy*pxy);
	}


	Bitmap* bmp=new Bitmap(dx2,dy2);		// 保存用
	bmp->SetResolution(px_res,py_res);

	Graphics*  MyGraphics = Graphics::FromImage(bmp);

// 平行四辺形の左上隅、右上隅、および左下隅
	Point pt[]={ Point(0,0),Point(dx2,0),Point(0,dy2) };

	MyGraphics->DrawImage(srcImage, pt,3);
	srcImage->~Image();

#ifdef UNICODE
	bmp->Save(dtc,&encoderClsid);
#else
	WCHAR dtcFile[MAX_PATH];
	MultiByteToWideChar(932,0,dtc,-1,dtcFile,sizeof(dtcFile)/sizeof(WCHAR));
	bmp->Save(dtcFile,&encoderClsid);
#endif
	delete bmp;		
	delete MyGraphics;
	return 0;
}

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;
}

resource.h

#define IDC_LISTBOX1 2001
#define IDC_EDIT_DIR 2002
#define IDC_BUTTON_DIR 2003

#define IDC_EDIT_WIDTH 2004
#define IDC_EDIT_HIGHT 2005

#define IDC_PROGBAR1 2006
#define IDC_LABEL1 2007

resource.rc

#include <windows.h>
#include "resource.h"


DLG1 DIALOG DISCARDABLE  0, 0, 380, 309
STYLE DS_MODALFRAME | WS_POPUP
CAPTION "画像サイズ変換"
FONT 9, "MS Pゴシック"
BEGIN
	CONTROL "ListBox", IDC_LISTBOX1, "LISTBOX", WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_DISABLENOSCROLL , 7, 26, 366, 128
	LTEXT			"ここへファイルをドラッグドロップしてください",-1,7,7,222,10
	
	CONTROL "保存フォルダー名", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 7, 175, 64, 18
	CONTROL "参照(&B)", IDC_BUTTON_DIR, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 70,170, 50, 14
	CONTROL "",IDC_EDIT_DIR, "EDIT", WS_CHILD | WS_DLGFRAME | WS_VISIBLE | ES_AUTOHSCROLL, 14, 193, 353, 15

	CONTROL "幅", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 7, 222, 8, 15
	CONTROL "350", IDC_EDIT_WIDTH, "EDIT", WS_CHILD | WS_DLGFRAME | WS_VISIBLE | ES_AUTOHSCROLL, 20, 218, 24, 15
	CONTROL "高", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 61, 222, 8, 15
	CONTROL "262", IDC_EDIT_HIGHT, "EDIT", WS_CHILD | WS_DLGFRAME | WS_VISIBLE | ES_AUTOHSCROLL, 74, 218, 24, 15

	CONTROL "", IDC_LABEL1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 7, 244, 366, 15
	CONTROL "", IDC_PROGBAR1, "MSCTLS_PROGRESS32", WS_CHILD | WS_VISIBLE | WS_BORDER, 7, 259, 366, 15

	DEFPUSHBUTTON	"OK(&O)",IDOK,115,288,50,14
	PUSHBUTTON	"キャンセル(&C)",IDCANCEL,225,288,50,14
END

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