ちょっと画像で遊びたいときに問題となるのが, 画像データの読み書きです. Windows だと一番簡単なのは bmp でしょう. 一般的な bmp だと, 先頭から 0x12 byte 目に幅, 0x22 byte 目に高さが 4byte の整数型で, 0x36 byte 目から BGR の順で画像データが入っているからです. ちなみに一般的な bmp (要は 24 ビット ビットマップ) かどうかは 0x1c に 2byte 整数で 24 が入っているか, 0x1e から 4byte 整数で 0 が入っているかを見れば分かります.
jpeg など bmp 以外の画像フォーマットをいじるときですが, ペイントブラシ等で一旦取り込んで bmp にするのが簡単です. でもファイルが多いとこの作業がおっくうになります. さらに bmp はでかいのでディスクを圧迫します.
というわけで jpeg を直接読み書きしたくなりますが, bmp ほど簡単ではありません. GDI+ といわれるライブラリを使うのですが書き方が面倒です. 毎回, 昔作ったプログラムからコピーしてくるのも何なのでここに記載しておこうと 思います.
#include <shlobj.h> #include <gdiplus.h> #include <Gdiplusinit.h> #pragma comment (lib, "gdiplus.lib") using namespace Gdiplus; |
GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); |
GdiplusShutdown(gdiplusToken); |
Bitmap *in = new Bitmap(IN_PATH); int width=in->GetWidth(); int height= in->GetHeight(); Rect inRect(0, 0, width, height); BitmapData inBD; in->LockBits(&inRect, ImageLockModeRead|ImageLockModeWrite, PixelFormat32bppARGB, &inBD); byte *pInImg = (byte *)inBD.Scan0; int nInStride = inBD.Stride; // 読み込み処理 in->UnlockBits(&inBD); |
Bitmap *out = new Bitmap(width, height, PixelFormat32bppARGB); BitmapData outBD; Rect outRect(0, 0, width, height); out->LockBits(&outRect, ImageLockModeRead|ImageLockModeWrite, PixelFormat32bppARGB, &outBD); byte *pOutImg = (byte *)outBD.Scan0; int nOutStride = outBD.Stride; // 書き込み処理 out->UnlockBits(&outBD); CLSID encoderClsid; GetEncoderClsid(L"image/jpeg", &encoderClsid); out->Save(OUT_PATH, &encoderClsid, NULL); |
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) { UINT num = 0; UINT size = 0; ImageCodecInfo* pImageCodecInfo = NULL; GetImageEncodersSize(&num, &size); if(size == 0) return -1; pImageCodecInfo = (ImageCodecInfo*)(malloc(size)); if(!pImageCodecInfo) return -1; GetImageEncoders(num, size, pImageCodecInfo); for(UINT j = 0; j < num; j++){ if(!wcscmp(pImageCodecInfo[j].MimeType, format)){ *pClsid = pImageCodecInfo[j].Clsid; free(pImageCodecInfo); return j; } } free(pImageCodecInfo); return -1; } |
例えば, セピア調の画像を作るのであれば
// 共通部分 GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&:gdiplusToken, &gdiplusStartupInput, NULL); // 入力部分 Bitmap *in = new Bitmap(IN_PATH); int width=in->GetWidth(); int height= in->GetHeight(); Rect inRect(0, 0, width, height); BitmapData inBD; in->LockBits(&inRect, ImageLockModeRead|ImageLockModeWrite, PixelFormat32bppARGB, &inBD); byte *pInImg = (byte *)inBD.Scan0; int nInStride = inBD.Stride; // 出力部分 Bitmap *out = new Bitmap(width, height, PixelFormat32bppARGB); BitmapData outBD; Rect outRect(0, 0, width, height); out->LockBits(&outRect, ImageLockModeRead|ImageLockModeWrite, PixelFormat32bppARGB, &outBD); byte *pOutImg = (byte *)outBD.Scan0; int nOutStride = outBD.Stride; // 画面操作 for (int x=0;x<width;x++){ for (int y=0;y double g= 0.298912*pInImg[y*nInStride+x*4+2] // 赤 +0.586611*pInImg[y*nInStride+x*4+1] // 緑 +0.114478*pInImg[y*nInStride+x*4 ]; // 青 // セピア調に変えて出力画像へ g/=255; pOutImg[y*nOutStride+x*4+2]=(byte)(g*240); // 赤 pOutImg[y*nOutStride+x*4+1]=(byte)(g*200); // 緑 pOutImg[y*nOutStride+x*4 ]=(byte)(g*145); // 青 } } // 入力 in->UnlockBits(&inBD); // 出力 out->UnlockBits(&outBD); CLSID encoderClsid; GetEncoderClsid(L"image/jpeg", &encoderClsid); out->Save(OUT_PATH, &encoderClsid, NULL); // 共通 GdiplusShutdown(gdiplusToken); |