DelphiのTBitmap用 画像縮小コード
前に作ってたソフトで使っていたソースを公開。
DHGL(Delphi High-Level Graphic Library)のソースを元に加工しています。
Delphiだけでは最適化が難しいので、一部C++でSSE命令で記述しDLL化したものをcall。
SSE使用法の参考にもなるかと。
unit BitmapUtils; interface uses Windows, Classes, Graphics, SysUtils; function Shrink_SSE(Bitmap: TBitmap; Width, Height: Integer): TBitmap; type TTriple = packed record B, G, R: Byte; end; TTripleArray = array[0..40000000] of TTriple; PTripleArray = ^TTripleArray; procedure Shrink_2(S, N:pointer; SW, SH, DW, DH:integer); stdcall; external 'exf.dll'; implementation function Shrink_SSE(Bitmap: TBitmap; Width, Height: Integer): TBitmap; type TDoubleTriple = record B, G, R: double; end; var NewBitmap, SourceBitmap: TBitmap; // 変換元のビットマップの大きさ SourceWidth, SourceHeight: Integer; // スキャンラインポインタのキャッシュ SourceScans, NewScans: array of PTripleArray; i: Integer; ScanLinePtr0 : Pointer; ScanLineOffset: Longint; begin SourceWidth := Bitmap.Width; SourceHeight := Bitmap.Height; NewBitmap := TBitmap.Create; try // 変換先 ビットマップを作る NewBitmap.PixelFormat := pf24bit; NewBitmap.Width := Width; NewBitmap.Height := Height; // 変換元をフルカラーにする SourceBitmap := TBitmap.Create; try SourceBitmap.Assign(Bitmap); SourceBitmap.PixelFormat := pf24Bit; // スキャンラインポインタのキャッシュを作る SetLength(SourceScans, SourceHeight); SetLength(NewScans, Height); ScanLinePtr0:=SourceBitmap.ScanLine[0]; if(SourceBitmap.Height > 0) then ScanLineOffset := Integer(SourceBitmap.ScanLine[1])-Integer(SourceBitmap.ScanLine[0]); for i := 0 to SourceHeight-1 do SourceScans[i] := Pointer(Integer(ScanLinePtr0)+ScanLineOffset*i); ScanLinePtr0:=NewBitmap.ScanLine[0]; if(NewBitmap.Height > 0) then ScanLineOffset := Integer(NewBitmap.ScanLine[1])-Integer(NewBitmap.ScanLine[0]); for i := 0 to Height-1 do NewScans[i] := Pointer(Integer(ScanLinePtr0)+ScanLineOffset*i); Shrink_2(SourceScans, NewScans, SourceWidth, SourceHeight, Width, Height); finally SourceBitmap.Free; end; except NewBitmap.Free; Raise; end; Result := NewBitmap; end; end.
C++側
#include <xmmintrin.h> #pragma pack(1) typedef struct { unsigned char B; unsigned char G; unsigned char R; } TTriple; #pragma pack() typedef __declspec(align(16)) struct { float R; float G; float B; float Dummy; } TFloatTriple; inline int trunc_double_sse2(double x) { __asm { cvttsd2si eax, x } } inline int trunc_float_sse(float x) { __asm { cvttss2si eax, x } } extern "C" void __stdcall Shrink_2(TTriple **sourceScans, TTriple **newScans, int sourceWidth, int sourceHeight, int width, int height); void __stdcall Shrink_2(TTriple **sourceScans, TTriple **newScans, int sourceWidth, int sourceHeight, int width, int height) { int x, y, i, j; TTriple *pSourceScan; TTriple *pNewScan; double rectTop, rectLeft, rectRight, rectBottom; int trunced_rectTop, trunced_rectBottom; int rectLeft_i, rectRight_i; float ratio; double xRatio, yRatio; TFloatTriple pixel; TTriple sourcePixel; float w, h; __m128 xmmv_ratio_rcp; __m128 xmmv_w; __m128 xmmv_spx; __m128 xmmv_dpx; ratio = (double)(sourceWidth * sourceHeight) / (double)width / (double)height; xRatio = (double)sourceWidth / (double)width; yRatio = (double)sourceHeight / (double)height; xmmv_ratio_rcp = _mm_set_ps1(1 / ratio); for(y=0; y<height; y++) { pNewScan = newScans[y]; rectTop = y * yRatio; rectBottom = (y+1) * yRatio - 0.000001; trunced_rectTop = trunc_double_sse2(rectTop); trunced_rectBottom = trunc_double_sse2(rectBottom); for(x=0; x<width; x++) { // 変換先ピクセルを変換元に投影する。 rectLeft = x * xRatio; rectRight = (x+1) * xRatio - 0.000001; rectLeft_i = trunc_double_sse2(rectLeft); rectRight_i = trunc_double_sse2(rectRight); // 変換元に投影された変換先ピクセルと交わっている // 変換元ピクセルを選び出し積分する //pixel.R = 0; pixel.G = 0; pixel.B = 0; xmmv_dpx = _mm_setzero_ps(); for(j=trunced_rectTop; j<=trunced_rectBottom; j++) { pSourceScan = sourceScans[j]; _mm_prefetch((char *)pSourceScan, _MM_HINT_NTA); for(i=rectLeft_i; i<=rectRight_i; i++) { sourcePixel = pSourceScan[i]; //_mm_prefetch((char *)&sourcePixel, _MM_HINT_NTA); // 投影されたピクセルと変換元ピクセルの交わっている // 部分の大きさを求める if( (rectLeft < i) && ((i+1) < rectRight) ) w = 1; else if( (i <= rectLeft) && ((i+1) < rectRight) ) w = 1 - (rectLeft - i); else if( (rectLeft < i) && (rectRight <= (i+1)) ) w = rectRight - i; else w = rectRight - rectLeft; if( (rectTop < j) && ((j+1) < rectBottom) ) h = 1; else if( (j <= rectTop) && ((j+1) < rectBottom) ) h = 1 - (rectTop - j); else if( (rectTop < j) && (rectBottom < (j+1)) ) h = rectBottom - j; else h = rectBottom - rectTop; xmmv_w = _mm_set_ps1(w * h); xmmv_spx = _mm_setr_ps(sourcePixel.R, sourcePixel.G, sourcePixel.B, 0); xmmv_w = _mm_mul_ps(xmmv_w, xmmv_spx); xmmv_dpx = _mm_add_ps(xmmv_dpx, xmmv_w); // 変換元 1 ピクセル分 積分 /* pixel.R = pixel.R + w * h * sourcePixel.R; pixel.G = pixel.G + w * h * sourcePixel.G; pixel.B = pixel.B + w * h * sourcePixel.B; */ } } xmmv_dpx = _mm_mul_ps(xmmv_dpx, xmmv_ratio_rcp); _mm_store_ps((float *)&pixel, xmmv_dpx); // 積分値から平均値を求め変換先に代入する /* pNewScan[x].R = round(pixel.R / ratio); pNewScan[x].G = round(pixel.G / ratio); pNewScan[x].B = round(pixel.B / ratio); */ pNewScan[x].R = trunc_float_sse(pixel.R); pNewScan[x].G = trunc_float_sse(pixel.G); pNewScan[x].B = trunc_float_sse(pixel.B); } } }
だいぶ放置・・・
だいぶ放置してました。
RaspberryPi用のSDカードが壊れて依頼、本体は引き出しに眠ったままです。
面白い活用方法はないか考え中なんですがイマイチ思いつかない・・・
皆さんどんな風に使われてるんでしょう?
まあでも、これは好奇心で色々弄れる健全な大人のおもちゃですね。
思いついたときにまた電源入れることにしよう!
Skype for WindowsはDelphi製
Skype for WindowsってDelphiで開発されてるんですね。
約2年前のプレスリリースですが、今知りました。
興味ない方にはどうでもいい事ですが・・
【Raspberry Pi】 VNCで接続
sshで接続して操作で十分なんですが、せっかくなのでVNCで接続できるよう設定します。
軽いと言われているTightVNCをインストール。
$ sudo apt-get install tightvncserver
SDカードが遅いせいか多少時間がかかりましたがインストール完了。
ディスプレイ番号0、解像度1024x768、24bitカラーにて起動。
$ vncserver :0 -geometry 1024x768 -depth 24
初回はパスワードを設定するよう聞かれるので入力。
なぜか8文字までしか認識しなく、長い場合は切り捨てられるようなので注意。
次にViewモードのパスワードを設定するか聞かれますがNを選択。
(表示のみって何に使うんでしょうか・・?)
珍しくログを取ったので貼っておきます。
You will require a password to access your desktops. Password: Warning: password truncated to the length of 8. Verify: Would you like to enter a view-only password (y/n)? n xauth: file /home/pi/.Xauthority does not exist New 'X' desktop is raspberrypi:0 Creating default startup script /home/pi/.vnc/xstartup Starting applications specified in /home/pi/.vnc/xstartup Log file is /home/pi/.vnc/raspberrypi:0.log
WindowsからVNCクライアントを使って接続。今回は有名なRealVNCを使ってみます。
サーバに「RaspberryPiのIP:5900(+ディスプレイ番号)」で接続。
RaspberryPiのIPが192.168.1.20でディスプレイ番号が0なら「192.168.1.20:5900」を指定。
VNCサーバーを終了するには -killオプションにディスプレイ番号を指定する。
$ vncserver -kill :0
【Raspberry Pi】 Nginx・PHPインストール
webサーバーといえばApacheが有名ですが、
軽い・速い・メモリ消費量が少ない・使いやすい「Nginx」をインストールします。
下記に簡潔にに手順を記載。
$ sudo apt-get install nginx
ついでにPHPもインストールしておきます。(MySQLモジュールも一緒に)
$ sudo apt-get install php5 php5-fpm php5-cgi php5-cli php5-common php5-mysql
piユーザーディレクトリに www/logs と www/html を作成。
$ cd ※piユーザーホームへ移動 $ mkdir -p www/{logs,html}
[Nginxの設定]
/home/pi/www/ 配下に対する設定
$ sudo vi /etc/nginx/sites-available/pi
server { listen 80; server_name localhost; access_log /home/pi/www/logs/access.log; error_log /home/pi/www/logs/error.log; location / { root /home/pi/www/html; index index.html index.php; } location ~ \.php$ { #fastcgi_pass 127.0.0.1:9000; fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME /home/pi/www/html$fastcgi_script_name; } location ~ /\.ht { deny all; } }
サイトを有効にするため、上記ファイルを /etc/nginx/sites-enabled/ へシンボリックリンクを張る。
$ sudo ln -s /etc/nginx/sites-available/pi /etc/nginx/sites-enabled/
Nginxの設定ファイルに問題が無いかチェック。
$ sudo nginx -t
問題がなければ設定再読み込み。
$ sudo nginx -s reload
PHPが動作するかphpinfo()で検証。
/home/pi/www/html/配下に下記のPHPコードでindex.phpを作成。
<?php phpinfo(); ?>