Builderには画像読込について、2つの方法があります。
以前に出てきたScanLineと、今回実験するPixelsです。
Pixelsのほうはピクセルの位置を2次元配列で指定でき
分かり易い形になっていますが、妙に遅いとの評判。
果たして本当に遅いのか...
←サイズ800x600のbmp画像を元に
ScanLineとPixesを比べてみます。
実験1では、単純に1ピクセルずつ反転していきます。
実験2では、ランダムに作成したX,Y位置のピクセルを反転していきます。
←こんなフォームを作った。
TMemoは計測結果表示
ScanLine, Pixelsボタンはそれぞれ実行させる用。
<Unit1.h>
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE 管理のコンポーネント
TMemo *Memo1;
TButton *ScanLineBtn;
TButton *PixelsBtn;
void __fastcall ScanLineBtnClick(TObject *Sender);
void __fastcall PixelsBtnClick(TObject *Sender);
private: // ユーザー宣言
public: // ユーザー宣言
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
<Unit1.cpp>
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Randomize(); // 乱数の初期化
}
//---------------------------------------------------------------------------
// ScanLineで実行
void __fastcall TForm1::ScanLineBtnClick(TObject *Sender)
{
Graphics::TBitmap *BM = new Graphics::TBitmap;
BM->LoadFromFile("vader.bmp");
LARGE_INTEGER fr, scnt, ecnt;
DWORD tim;
//-- 実験1 -- 順番に読み込んで反転
QueryPerformanceFrequency(&fr);
QueryPerformanceCounter(&scnt); // 計測開始
for( int y=0; y<BM->Height; y++ ){
Byte *Line = (Byte*)BM->ScanLine[y]; // ScanLineで実行
int i=0;
for( int x=0; x<BM->Width; x++ ){
Line[i] = ~Line[i]; // 反転
Line[i+1] = ~Line[i+1];
Line[i+2] = ~Line[i+2];
i+=3;
}
}
QueryPerformanceCounter(&ecnt); // 計測終了
tim = (DWORD)((ecnt.QuadPart - scnt.QuadPart) * 1000 / fr.QuadPart);
Memo1->Lines->Add("01:ScanLine--> " + IntToStr(tim) + " ms");
BM->SaveToFile("ScanLine_01.bmp");
//-- 実験2 -- ランダムに読み込んで反転
QueryPerformanceFrequency(&fr);
QueryPerformanceCounter(&scnt); // 計測開始
for( int n=0; n<BM->Width*BM->Height; n++ ){
int x = random(BM->Width); // 乱数でX, Yを決定
int y = random(BM->Height);
Byte *Line = (Byte*)BM->ScanLine[y]; // ScanLineで実行
Line[y*BM->Width*3 + x] = ~Line[y*BM->Width*3 + x]; // 反転
Line[y*BM->Width*3 + x+1] = ~Line[y*BM->Width*3 + x+1];
Line[y*BM->Width*3 + x+2] = ~Line[y*BM->Width*3 + x+2];
}
QueryPerformanceCounter(&ecnt); // 計測終了
tim = (DWORD)((ecnt.QuadPart - scnt.QuadPart) * 1000 / fr.QuadPart);
Memo1->Lines->Add("02:ScanLine--> " + IntToStr(tim) + " ms");
BM->SaveToFile("ScanLine_02.bmp");
delete BM;
}
//---------------------------------------------------------------------------
// Pixelsで実行
void __fastcall TForm1::PixelsBtnClick(TObject *Sender)
{
Graphics::TBitmap *BM = new Graphics::TBitmap;
BM->LoadFromFile("vader.bmp");
LARGE_INTEGER fr, scnt, ecnt;
DWORD tim;
//-- 実験1 -- 順番に読み込んで反転
QueryPerformanceFrequency(&fr);
QueryPerformanceCounter(&scnt); // 計測開始
for( int y=0; y<BM->Height; y++ ){
for( int x=0; x<BM->Width; x++ ){
// Pixelsで反転させる
BM->Canvas->Pixels[x][y] = ~BM->Canvas->Pixels[x][y];
}
}
QueryPerformanceCounter(&ecnt); // 計測終了
tim = (DWORD)((ecnt.QuadPart - scnt.QuadPart) * 1000 / fr.QuadPart);
Memo1->Lines->Add("01:Pixels--> " + IntToStr(tim) + " ms");
BM->SaveToFile("Pixels_01.bmp");
//-- 実験2 -- ランダムに読み込んで反転
QueryPerformanceFrequency(&fr);
QueryPerformanceCounter(&scnt); // 計測開始
for( int n=0; n<BM->Width*BM->Height; n++ ){
int x = random(BM->Width); // 乱数でX, Yを決定
int y = random(BM->Height);
// Pixelsで反転させる
BM->Canvas->Pixels[x][y] = ~BM->Canvas->Pixels[x][y];
}
QueryPerformanceCounter(&ecnt); // 計測終了
tim = (DWORD)((ecnt.QuadPart - scnt.QuadPart) * 1000 / fr.QuadPart);
Memo1->Lines->Add("02:Pixels--> " + IntToStr(tim) + " ms");
BM->SaveToFile("Pixels_02.bmp");
delete BM;
}
//---------------------------------------------------------------------------
結果:
01:ScanLine--> 5 ms
02:ScanLine--> 255 ms
01:Pixels--> 800 ms
02:Pixels--> 991 ms
というわけで、やっぱりPixelsは遅かった...
画像としては↓こんな感じになります。(左:ScanLine 右:Pixels)
補足:
今回は計測用にQueryPerformanceFrequency、QueryPerformanceCounterを使っています。
このAPIを使うと最大精度でナノ秒まで見る事が出来る高精度なものです。
計測終了時に * 1000 をしている箇所を変えてやればマイクロ秒などに変更できます。
timeGetTimeも計測用に使えるけど、ミリ秒まで。
それと、処理結果を保存する為にSaveToFileも利用しています。
結果画像を確認する際には便利なもので、ちょいちょい使います。
今回はファイルパスを指定していないのでexeファイルがある場所に作成されます。