这里小巧简单 的 C ++ / VCL 强> ScanLine 的 Alpha Blend 强> 我把它放在一起的例子:
ScanLine
//--------------------------------------------------------------------------- void blend(Graphics::TBitmap *dst,int x,int y,Graphics::TBitmap *src,BYTE alpha) { const int n=3; // pixel align [Bytes] int dx0,dy0,dx1,dy1, // dst BBOX sx0,sy0,sx1,sy1, // src BBOX dx,dy,sx,sy,i; BYTE *dp,*sp; WORD a,_a,sc,dc,da[256],sa[256]; // compute BBOX (handle clipping) dx=src->Width; dy=src->Height; dx0=x; sx0=0; dx1=x+dx; sx1=dx; dy0=y; sy0=0; dy1=y+dy; sy1=dy; if (dx0<0){ sx0-=dx0; dx0=0; } if (dy0<0){ sy0-=dy0; dy0=0; } dx=dst->Width; dy=dst->Height; if (dx1>dx){ sx1+=dx-dx1; dx1=dx; } if (dy1>dy){ sy1+=dy-dy1; dy1=dy; } // make sure config is compatible with ScanLine[] dst->HandleType=bmDIB; dst->PixelFormat=pf24bit; src->HandleType=bmDIB; src->PixelFormat=pf24bit; // blend a=alpha; _a=255-a; for (i=0;i<256;i++){ da[i]=_a*i; sa[i]=a*i; } // precompite BYTE*a and BYTE*_a LUTs for (dy=dy0,sy=sy0;dy<dy1;dy++,sy++) // ScanLines { dp=(BYTE*)dst->ScanLine[dy]+(n*dx0); sp=(BYTE*)src->ScanLine[sy]+(n*sx0); for (dx=dx0,sx=sx0;dx<dx1;dx++,sx++) // single ScanLine for (i=0;i<n;i++,dp++,sp++) // RGB *dp=WORD((sa[*sp]+da[*dp])>>8); // blend function } } //---------------------------------------------------------------------------
我只是基于每个像素/通道处理图像,并为每个通道(R,G,B)计算:
dst_pixel = src_pixel*alpha + dst_pixel*(255-alpha)
其中通道和alpha是8位无符号整数...对于速度我使用24位像素格式(通常我用32位代替)。
避免 *,/ in teh Blending I precomputed 2 的 LUT 强> s所有可能的组合 number*alpha 和 number*(255-alpha) 。除法是通过位移完成的 >>8 。
*,/
number*alpha
number*(255-alpha)
>>8
为了提高速度,你可以记住所有 ScanLine[] 的 dst 将图像刻录到数组中然后使用它,因为目标图像将被多次使用...
ScanLine[]
dst
当我在混合2上测试时 1024x768 它们合起来了 <=9ms 在我的设置。最慢的操作是 ScanLine[] 访问,以及在混合之前格式化为像素格式的图像...
1024x768
<=9ms
这里有GIF预览(按比例缩小1/4并且由我的捕获器抖动,因此它适合模糊2MByte限制):
这是我用于此的代码(单个计时器VCL应用程序):
//$$---- Form CPP ---- //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "win_main.h" #include <math.h> #include <jpeg.hpp> //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TMain *Main; Graphics::TBitmap *bmp,*bmp0,*bmp1; // back buffer, image0, image1, ... //--------------------------------------------------------------------------- void blend(Graphics::TBitmap *dst,int x,int y,Graphics::TBitmap *src,BYTE alpha) { const int n=3; // pixel align [Bytes] int dx0,dy0,dx1,dy1, // dst BBOX sx0,sy0,sx1,sy1, // src BBOX dx,dy,sx,sy,i; BYTE *dp,*sp; WORD a,_a,sc,dc,da[256],sa[256]; // compute BBOX (handle clipping) dx=src->Width; dy=src->Height; dx0=x; sx0=0; dx1=x+dx; sx1=dx; dy0=y; sy0=0; dy1=y+dy; sy1=dy; if (dx0<0){ sx0-=dx0; dx0=0; } if (dy0<0){ sy0-=dy0; dy0=0; } dx=dst->Width; dy=dst->Height; if (dx1>dx){ sx1+=dx-dx1; dx1=dx; } if (dy1>dy){ sy1+=dy-dy1; dy1=dy; } // make sure config is compatible with ScanLine[] dst->HandleType=bmDIB; dst->PixelFormat=pf24bit; src->HandleType=bmDIB; src->PixelFormat=pf24bit; // blend a=alpha; _a=255-a; for (i=0;i<256;i++){ da[i]=_a*i; sa[i]=a*i; } // precompite BYTE*a and BYTE*_a LUTs for (dy=dy0,sy=sy0;dy<dy1;dy++,sy++) // ScanLines { dp=(BYTE*)dst->ScanLine[dy]+(n*dx0); sp=(BYTE*)src->ScanLine[sy]+(n*sx0); for (dx=dx0,sx=sx0;dx<dx1;dx++,sx++) // single ScanLine for (i=0;i<n;i++,dp++,sp++) // RGB *dp=WORD((sa[*sp]+da[*dp])>>8); // blend function } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void TMain::draw() { bmp->Canvas->Draw(0,0,bmp0); // render background bmp0 static float a=0.0; a+=0.025*M_PI; blend(bmp,0,0,bmp1,fabs(255.0*sin(a))); // alfa blend in bmp1 Main->Canvas->Draw(0,0,bmp); // show result on screen } //--------------------------------------------------------------------------- __fastcall TMain::TMain(TComponent* Owner) : TForm(Owner) { // create bitmaps bmp=new Graphics::TBitmap; bmp0=new Graphics::TBitmap; bmp1=new Graphics::TBitmap; // laod images TJPEGImage *jpg=new TJPEGImage; jpg->LoadFromFile("img0.jpg"); bmp0->Assign(jpg); jpg->LoadFromFile("img1.jpg"); bmp1->Assign(jpg); delete jpg; } //--------------------------------------------------------------------------- void __fastcall TMain::FormDestroy(TObject *Sender) { // delete bitmaps delete bmp0; delete bmp1; delete bmp; } //--------------------------------------------------------------------------- void __fastcall TMain::FormResize(TObject *Sender) { bmp->Width =ClientWidth; bmp->Height=ClientHeight; } //--------------------------------------------------------------------------- void __fastcall TMain::FormPaint(TObject *Sender) { draw(); } //--------------------------------------------------------------------------- void __fastcall TMain::tim_redrawTimer(TObject *Sender) { draw(); } //---------------------------------------------------------------------------
这里是图像(我在Google图像上找到的第一个漂亮的1024x768图像):
这里预览混合结果:
有关的更多信息 的 扫描线 强> 看到:
如果你需要更快的速度,你应该去 的 GPU 强> 混合( 的 OpenGL的 强> 要么 的 的DirectX 强> )。
的 [Edit2]数组+矩形示例 强>
在你编辑了你的问题之后,现在很明显:
的 您的位图数组根本不是数组 强>
它相当像某种列表模板 vector<Graphics::TBitmap*> 或类似的......所以你不能像我一样访问bmps的线性阵列。为了让您的生活更轻松,我使用了具有类似属性的我的模板,以便您可以看到如何处理这些(抱歉,我无法共享模板代码,但您只需要更改 List<T> 成 Vector<T> 或者你在用什么......
vector<Graphics::TBitmap*>
List<T>
Vector<T>
这就是为什么数组指针不能为你工作的原因,因为你没有。它可能是你的模板用一些成员公开它。我喜欢它 map.dat 所以,如果没有线性存储,你的相似或不相似。
map.dat
的 您只混合了2个图像而不是整个阵列 强>
因此,您可以使用第一个示例并添加ScanLine预加载,因为您的图像是静态的...对后备缓冲区图像执行相同操作,因为只有在调整大小后才会更改。
当我把所有这些放在一起的结果:
//$$---- Form CPP ---- //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "win_main.h" #include <math.h> #include <jpeg.hpp> #include "list.h" // mine list<T> template you got probably vector<> or something similar instead #include "performance.h" // this is mine tbeg/tend/tstr time measurement //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TMain *Main; //--------------------------------------------------------------------------- // [back buffer] Graphics::TBitmap *bmp; // bitmap BYTE **bmp_pyx=NULL; // preloaded ScanLines [y][x] void bmp_init() // create preloaded ScanLines { bmp_pyx=new BYTE*[bmp->Height]; for (int y=0;y<bmp->Height;y++) bmp_pyx[y]=(BYTE*)bmp->ScanLine[y]; } void bmp_exit() // release preloaded ScanLines { delete[] bmp_pyx; } //--------------------------------------------------------------------------- // [array of images] const AnsiString filename[]= // filenames { "img0.jpg", "img1.jpg", "img2.jpg", "img3.jpg", "img4.jpg", "img5.jpg", "img6.jpg", "img7.jpg", "img8.jpg", "img9.jpg", "" }; List<Graphics::TBitmap*> map; // your "array" of bitmaps int maps=0; // number of images BYTE ***map_pyx=NULL; // preloaded ScanLines [ix][y][x] //--------------------------------------------------------------------------- void map_init() // alocate and prepare data { int i,y; Graphics::TBitmap *bmp; TJPEGImage *jpg=new TJPEGImage; // create "array" of bmp (you already got this) for (maps=0;filename[maps]!="";maps++) { map.add(new Graphics::TBitmap); // this is like your push_back(new Graphics::TBitmap) jpg->LoadFromFile(filename[maps]); // filename[] -> jpg -> bmp -> map[] map[maps]->Assign(jpg); // here you can also rescale or whatever you want to do... map[maps]->HandleType=bmDIB; map[maps]->PixelFormat=pf24bit; } // create preloaded ScanLines (you need to add this into your app init) map_pyx=new BYTE**[maps]; // **map_pyx[] for (i=0;i<maps;i++) { map_pyx[i]=new BYTE*[map[i]->Height]; // *map_pyx[][] for (y=0;y<map[i]->Height;y++) // map_pyx[][]] map_pyx[i][y]=(BYTE*)map[i]->ScanLine[y]; } delete jpg; } //--------------------------------------------------------------------------- void map_exit() // release data (you need to add this in app exit) { int i; for (i=0;i<maps;i++) { delete map[i]; delete[] map_pyx[i]; } delete[] map_pyx; } //--------------------------------------------------------------------------- void blend_rec(BYTE **dp,int x0,int y0,int x1,int y1,BYTE **sp,BYTE alpha) { const int n=3; // pixel align [Bytes] int x,y,i; BYTE *d,*s; WORD da[256],sa[256]; // pixelformat align x0*=n; x1*=n; // prepare alpha*BYTE and (255-alpha)*BYTE LUTs y= alpha; for (x=0;x<256;x++) sa[x]=x*y; y=255-alpha; for (x=0;x<256;x++) da[x]=x*y; // blend for (y=y0;y<y1;y++) { d=dp[y]+x0; s=sp[y]+x0; for (x=x0;x<x1;x++,d++,s++) *d=WORD((sa[*s]+da[*d])>>8); // blend function } // release data } //--------------------------------------------------------------------------- void TMain::draw() { bmp->Canvas->Draw(0,0,map[0]); // render background bmp[0] static float a=0.0; a+=0.025*M_PI; // animation ... BYTE alpha=128+float(127.0*sin(a)); tbeg(); blend_rec(bmp_pyx,200,500,400,600,map_pyx[1],alpha); // add the blended rectangle (except background which is bmp[0] tend(); Caption=tstr(); Canvas->Draw(0,0,bmp); // show on screen // bmp->SaveToFile("out.bmp"); } //--------------------------------------------------------------------------- __fastcall TMain::TMain(TComponent* Owner) : TForm(Owner) { // create bitmaps bmp=new Graphics::TBitmap; bmp_init(); map_init(); } //--------------------------------------------------------------------------- void __fastcall TMain::FormDestroy(TObject *Sender) { // delete bitmaps delete bmp; bmp_exit(); map_exit(); } //--------------------------------------------------------------------------- void __fastcall TMain::FormResize(TObject *Sender) { bmp->Width =ClientWidth; bmp->Height=ClientHeight; bmp->HandleType=bmDIB; bmp->PixelFormat=pf24bit; bmp_exit(); bmp_init(); } //--------------------------------------------------------------------------- void __fastcall TMain::FormPaint(TObject *Sender) { draw(); } //--------------------------------------------------------------------------- void __fastcall TMain::tim_redrawTimer(TObject *Sender) { draw(); } //---------------------------------------------------------------------------
混合是在不到的时间内完成的 0.5ms 在我选择的矩形设置上。你可以看到它比原来更快 9ms ...因为如果您使用剪裁区域,您仍然会混合整个图像而不复制结果。这种方法只混合并复制所需的内容。
0.5ms
9ms
小心我删除了范围检查,所以确保矩形在图像内...
如果你想以同样的方式测量时间,我正在使用我的代码:
的 Performance.h: 强>
//--------------------------------------------------------------------------- //--- Performance counter time measurement: 2.01 ---------------------------- //--------------------------------------------------------------------------- #ifndef _performance_h #define _performance_h //--------------------------------------------------------------------------- const int performance_max=64; // push urovni double performance_Tms=-1.0, // perioda citaca [ms] performance_tms=0.0, // zmerany cas po tend [ms] performance_t0[performance_max]; // zmerane start casy [ms] int performance_ix=-1; // index aktualneho casu //--------------------------------------------------------------------------- void tbeg(double *t0=NULL) // mesure start time { double t; LARGE_INTEGER i; if (performance_Tms<=0.0) { for (int j=0;j<performance_max;j++) performance_t0[j]=0.0; QueryPerformanceFrequency(&i); performance_Tms=1000.0/double(i.QuadPart); } QueryPerformanceCounter(&i); t=double(i.QuadPart); t*=performance_Tms; if (t0) { t0[0]=t; return; } performance_ix++; if ((performance_ix>=0)&&(performance_ix<performance_max)) performance_t0[performance_ix]=t; } //--------------------------------------------------------------------------- void tpause(double *t0=NULL) // stop counting time between tbeg()..tend() calls { double t; LARGE_INTEGER i; QueryPerformanceCounter(&i); t=double(i.QuadPart); t*=performance_Tms; if (t0) { t0[0]=t-t0[0]; return; } if ((performance_ix>=0)&&(performance_ix<performance_max)) performance_t0[performance_ix]=t-performance_t0[performance_ix]; } //--------------------------------------------------------------------------- void tresume(double *t0=NULL) // resume counting time between tbeg()..tend() calls { double t; LARGE_INTEGER i; QueryPerformanceCounter(&i); t=double(i.QuadPart); t*=performance_Tms; if (t0) { t0[0]=t-t0[0]; return; } if ((performance_ix>=0)&&(performance_ix<performance_max)) performance_t0[performance_ix]=t-performance_t0[performance_ix]; } //--------------------------------------------------------------------------- double tend(double *t0=NULL) // return duration [ms] between matching tbeg()..tend() calls { double t; LARGE_INTEGER i; QueryPerformanceCounter(&i); t=double(i.QuadPart); t*=performance_Tms; if (t0) { t-=t0[0]; performance_tms=t; return t; } if ((performance_ix>=0)&&(performance_ix<performance_max)) t-=performance_t0[performance_ix]; else t=0.0; performance_ix--; performance_tms=t; return t; } //--------------------------------------------------------------------------- double tper(double *t0=NULL) // return duration [ms] between tper() calls { double t,tt; LARGE_INTEGER i; if (performance_Tms<=0.0) { for (int j=0;j<performance_max;j++) performance_t0[j]=0.0; QueryPerformanceFrequency(&i); performance_Tms=1000.0/double(i.QuadPart); } QueryPerformanceCounter(&i); t=double(i.QuadPart); t*=performance_Tms; if (t0) { tt=t-t0[0]; t0[0]=t; performance_tms=tt; return tt; } performance_ix++; if ((performance_ix>=0)&&(performance_ix<performance_max)) { tt=t-performance_t0[performance_ix]; performance_t0[performance_ix]=t; } else { t=0.0; tt=0.0; }; performance_ix--; performance_tms=tt; return tt; } //--------------------------------------------------------------------------- AnsiString tstr() { AnsiString s; s=s.sprintf("%8.3lf",performance_tms); while (s.Length()<8) s=" "+s; s="["+s+" ms]"; return s; } //--------------------------------------------------------------------------- AnsiString tstr(int N) { AnsiString s; s=s.sprintf("%8.3lf",performance_tms/double(N)); while (s.Length()<8) s=" "+s; s="["+s+" ms]"; return s; } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #endif //--------------------------------------------------------------------------- //---------------------------------------------------------------------------