Here small simple C++/VCL ScanLine
Alpha Blend example I just put together:
//---------------------------------------------------------------------------
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
}
}
//---------------------------------------------------------------------------
I just process the image on per pixel/channel basis and for each channel (R,G,B) compute:
dst_pixel = ( src_pixel*alpha + dst_pixel*(255-alpha) )/255
where channels and alpha are 8 bit unsigned integers... For speed I used 24 bit pixel format (usually I use 32bit instead).
To avoid *,/
in teh Blending I precomputed 2 LUTs with all posible combinations of number*alpha
and number*(255-alpha)
. The division is done by bit shift >>8
.
To improve speed you can remember all ScanLine[]
of the dst
image into your array once and then use that as the target image will be used many times ...
When I tested this on blending 2 1024x768
images together it took <=9ms
on mine setup. The slowest operation is the ScanLine[]
access, and images where formated to the pixel format prior to blending...
Here GIF preview (scaled down 1/4 and dithered by my capturer so it fits to imgur 2MByte limit):
This is the code I used for this (single timer VCL App):
//$$---- 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();
}
//---------------------------------------------------------------------------
And here the images (first nice 1024x768 images I found on Google images):
Here preview of Blending result:
For more info about the ScanLine see:
If you need even more speed then you should go for GPU Blending (OpenGL or DirectX).
[Edit2] array + rectangle example
After your edited your question its now obvious:
your array of bitmaps is not an array at all
its rather some kind of list template like vector<Graphics::TBitmap*>
or similar... So you do not have access to linear array of the bmps like I do. To make your life easier I used mine template with similar properties so you can see how to handle those (sorry I can not share the template code but you just need to change List<T>
into Vector<T>
or whatever you are using ...
This is the reason why the array pointer did not work for you as you do not have one. Its possibly your template expose it with some member. Mine does it like map.dat
so yours might have something similar or not at all if not stored linearly.
You are blending just 2 images not the whole array
so you can use the first example and add the ScanLine preloading as your images are static... Do the same for backbuffer image as that changes only after resize.
When I put all together here the result:
//$$---- 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++)
{