   a Bitmap Resize Algorithm  Announcement:
A 5 to 10 times faster version of this program has been completed.

Introduction
Information can be stored in an analog or digital form.
The big advantage of digital storage is the possibility to manipulate the data using mathematical functions.
This article handles a simple algorithm to resize (enlarge or reduce) computer pictures.
Data formats and mathematics are discussed in detail.
The Delphi-implementation and full source-code listing are also included.

Data formats
A computer picture (or screen) is made up of dots which are called pixels.
A colored pixel is a mix of three colors: red, green and blue.
Using realistic colors, the intensity of each color is a number ranging 0..255.
0 means zero intensity, 255 (or hexadecimal ff) indicates the highest intensity.

This is what a (very enlarged) pixel looks like: maximum intensity of all three colors produces white.

Internally, Windows stores the data of each pixel in a 32 bit word: The red information is stored in bits 0..7, green in 8..15, blue in 16..23.
Bits 24..31 are not used.

A picture is a 2-dimensional table of pixels.
In Delphi, this table may be part of a Bitmap component.
This component offers the choice of several data formats and has procedures to manipulate
the pixels (drawing lines, filling areas, copying ...).
Bitmaps may be copied to a paintbox to be displayed on the screen.
Pixeldata in the bitmap is addressed by using the [x,y] coordinates.

Image below shows a bitmap with some coordinates.
A pixel is pictured as a square. Commonly used pixelformats are: 16 bits, 24 bits or 32 bits.

24 bit is the Windows internal format.
In this application, the 32 bit format is used.
In this format, the colors red and blue have traded positions in the
32 bit word as compared to the windows format, so red is positioned
in bits 16..23 and blue in bits 0..7.

If a variable "color" has to receive the data of pixel[x,y] of a bitmap named bm1 then
color := bm1.canvas.pixels[x,y]
does the job.

Notice, that "color" has the Windows format, regardless of the format in which
the colors were stored in the bitmap.

To further extract the individual color intensities into bytes R,G,B , use the statements:
R := color and \$ff;
G := (color shr 8) and \$ff;
B := (color shr 16) and \$ff;
To show the bitmap on the screen, using a paintbox component
on form1 while positioning the left-top at paintbox position [x,y] , use the statement:
form1.paintbox1.canvas.draw(x,y,bm1)
Reducing pictures
Image below shows a source- and destination bitmap.
The source bitmap has 5 rows of 5 columns, the destination is a 4 * 4 bitmap. We regard the bitmaps as pieces of paper.
A pixel is given the dimension 1 * 1.
The destination bitmap dimensions are the dimensions of the source bitmap
divided by a factor f.
So, for each destination pixel, a corresponding area (f*f) on the source map may be drawn.
In most cases, this area will partially overlap the pixels in the source.

The color of a destination pixel is the (weighted) addition of the colors of
the pixels covered on the source map.
In the picture above, 4 pixels in the source contribute to the (marked) pixel
in the destination.
This contribution is proportional to the area covered.

We zoom into above picture to show the details: We see a part of the source bitmap with 4 pixels:
ABCD : [i,j]
DCHI : [i+1,j]
BEFC : [i,j+1]
CFGH : [i+1,j+1]
The destination-pixel [x,y] dimensions are multiplied by f and projected on the
source bitmap. This projection is the (red) rectangle PQRS.
We must clearly distinguish pixel positions in the bitmap and distances on the map.
Pixel positions are [i,j] , so point A has distance i to the left and j to the top.
sx1,sx2,sy1,sy2 are variables of type single, which hold therefore floating point
values indicating the distance of the (red) square edges to the edges of the map.
Note , that PS = PQ = sx2-sx1 = sy2-sy1 = f;

If the destination-pixel is [x,y] then
sy1 := f * y;
sy2 := sy1 + f;
sx1 := f * x;
sx2 := sx1 + f;
j := floor(sy1);
i := floor(sx1);
fi2 := 1/(f * f);
PC is the fraction of the red rectangle which overlaps pixel[i,j].
Now, the colors of the pixel[i,j] must be extracted and the proper fraction
If dx,dy are the edges of the (red) rectangle overlapping pixel[i,j] then
dx := 1-(sx1-i);
dy := 1-(sy1-j);
PC := dx*dy*fi2;
color := bm1.canvas.pixels[i,j];
sR := color and \$ff; //source red
sG := (color shr 8) and \$ff; //source green
sB := (color shr 16) and \$ff; //source blue
destR := destR + sR*PC; //dest red
destG := destG + sG*PC; //dest green
destB := destB + sB*PC; //dest blue
bm2.Canvas.pixels[x,y] := RGB(trunc(destR),trunc(destG),trunc(destB));//write destination
Note, that destR,destG,destB have been set to 0 after advancing to a new destination[x,y]
To calculate the color of destination-pixel [x,y], the above statements must be repeated
4 times, for all source pixels ABCD, DCHI, BEFC, CFGH.

This concludes the description of the algorithm.
For the reduction of a picture, a similar description holds.

Below is a picture of the form, showing the results Opendialog and Savedialog components are added to the form.
Two bitmaps are created: bm1 for the source, bm2 for the destination.
Paintbox1,2 components are used to display the bitmaps.

The overal procedure is:
- scan destination bitmap bm2 , row by row
- for each pixel in bm2, calculate overlap with source bitmap bm1
- scan all pixels in source which are overlapped
- add colors of these pixels according to fraction of overlap
- copy color to destination pixel

For larger images, the program is rather slow.
After the source listing, a modification is discussed to reduce processing time.

The complete source listing

```unit Unit1;
interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
OpenDialog1: TOpenDialog;
PaintBox1: TPaintBox;
StaticText1: TStaticText;
StaticText2: TStaticText;
Label1: TLabel;
Label2: TLabel;
Edit1: TEdit;
Label3: TLabel;
Button2: TButton;
PaintBox2: TPaintBox;
SaveDialog1: TSaveDialog;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormKeyPress(Sender: TObject; var Key: Char);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{\$R *.DFM}

var bm1,bm2 : TBitmap;
sw,sh : word;

procedure TForm1.Button1Click(Sender: TObject);
begin
opendialog1.filter := 'bitmaps | *bmp' ;
if opendialog1.execute then
begin
bm1.pixelformat := pf32bit; //in case other format loaded
end;
with paintbox1 do
with canvas do
begin
brush.color := 0;
fillrect(rect(0,0,width,height));
draw(0,0,bm1);
end;
sw := bm1.width;
sh := bm1.height;
statictext1.caption := inttostr(sw);
statictext2.caption := inttostr(sh);
end;

function floor(a : single) : word;
//return a rounded down to integer
begin
result := trunc(a);
end;

function ceiling(a : single) : word;
//return a rounded up to integer
begin
result := trunc(a);
if frac(a) > 0.0001 then inc(result);//fix acces violation if small fraction
end;

procedure TForm1.Button2Click(Sender: TObject);
//reduce or enlarge
var sx1,sy1,sx2,sy2 : single;    //source field positions
x,y : word;                  //dest field pixels
destR,destG,destB : single;  //destination colors
sR,sG,sB : byte;             //source colors
destWidth, destheight : word;
f,fi2 : single;
i,j : word;
dx,dy,PC : single;
color : longInt;
begin
with paintbox2 do
with canvas do fillrect(rect(0,0,width,height));
//
destwidth := strtoint(edit1.text);
f := sw / destwidth;
fi2 := 1/f;
fi2 := fi2*fi2;
destheight := trunc(bm1.height/f);
with bm2 do
begin
width := destwidth;
height := destheight;
end;
//---
for y := 0 to destheight-1 do         //vertical destination pixels
begin
sy1 := f * y;
sy2 := sy1 + f;
for x := 0 to destwidth-1 do        //horizontal destination pixels
begin
sx1 := f * x;
sx2 := sx1 + f;
destR := 0; destG := 0; destB := 0;       //clear colors
for j := floor(sy1) to ceiling(sy2)-1 do  //vertical source pixels
begin
dy := 1;
if sy1 > j then begin
dy := dy-(sy1-j);
end;
if sy2 < j+1 then begin
dy := dy-(j+1-sy2);
end;
for i := floor(sx1) to ceiling(sx2)-1 do //horizontal source pixels
begin
dx := 1;
if sx1 > i then begin
dx := dx-(sx1-i);
end;
if sx2 < i+1 then begin
dx := dx-(i+1-sx2);
end;
color := bm1.canvas.pixels[i,j];
sR := color and \$ff;
sG := (color shr 8) and \$ff;
sB := (color shr 16) and \$ff;
PC := dx*dy*fi2;
destR := destR + sR*PC;
destG := destG + sG*PC;
destB := destB + sB*PC;
end;//for i
end;//for j
bm2.Canvas.pixels[x,y] := RGB(trunc(destR),trunc(destG),trunc(destB));
end;//for x
end;//for y
paintbox2.canvas.draw(0,0,bm2);
end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
//make picture for website
var formImage : Tbitmap;
begin
if upcase(key) = 'P' then
begin
formimage := Tbitmap.create;
formimage := getformimage;
formImage.canvas.draw(paintbox1.left,paintbox1.top,bm1);
formImage.canvas.draw(paintbox2.left,paintbox2.top,bm2);
if savedialog1.execute then
formImage.savetofile(savedialog1.filename);
formImage.free;
end;
end;

initialization

bm1 := Tbitmap.create;
with bm1 do
begin
pixelformat := pf32bit;
width := 100; height := 100;
end;
bm2 := TBitmap.create;
with bm2 do
begin
pixelformat := pf32bit;
width := 100; height := 100;
end;
sw := 100; sh := 100;

finalization

bm1.free;
bm2.free;

end.
```
Speeding up
The following procedure is slow:
color := bm1.canvas.pixels[i,j]
Reason is that Windows is called.
To bypass windows and directly address the pixels in the array, a pointer is used.
This pointer is the scanline property of the bitmap component.
If p is a pointer to a large a9one dimensional) array of LongInt and
p := bm1.scanline[y]
then pixel column x on row y may now be addressed as
color := p^[x]
which increases speed considerably.

Below is the modified procedure.
```
procedure TForm1.Button2Click(Sender: TObject);
//reduce or enlarge
type TWa = array[0..1000] of longInt;
PWa = ^TWa;
var sx1,sy1,sx2,sy2 : single;    //source field positions
py,pj : PWa;
x,y,i,j : word;              //source,dest field pixels
destR,destG,destB : single;  //destination colors
sR,sG,sB : byte;             //source colors
destWidth, destheight : word;
f,fi2 : single;              //factors
dx,dy,AP : single;           //distance, area percentage
color : DWord;//longInt;
begin
with paintbox2 do
with canvas do fillrect(rect(0,0,width,height));
//
destwidth := strtoint(edit1.text);
f := sw / destwidth;
fi2 := 1/f;
fi2 := fi2*fi2;
destheight := trunc(bm1.height/f);
with bm2 do
begin
width := destwidth;
height := destheight;
end;
//---
for y := 0 to destheight-1 do         //vertical destination pixels
begin
sy1 := f * y;
sy2 := sy1 + f;
py := bm2.ScanLine[y];
for x := 0 to destwidth-1 do        //horizontal destination pixels
begin
sx1 := f * x;
sx2 := sx1 + f;
destR := 0; destG := 0; destB := 0;       //clear colors
for j := floor(sy1) to ceiling(sy2)-1 do  //vertical source pixels
begin
pj := bm1.scanline[j];
dy := 1;
if sy1 > j then begin
dy := dy-(sy1-j);
end;
if sy2 < j+1 then begin
dy := dy-(j+1-sy2);
end;
for i := floor(sx1) to ceiling(sx2)-1 do //horizontal source pixels
begin
dx := 1;
if sx1 > i then begin
dx := dx-(sx1-i);
end;
if sx2 < i+1 then begin
dx := dx-(i+1-sx2);
end;
color := pj^[i];
sB := color and \$ff;
sG := (color shr 8) and \$ff;
sR := (color shr 16) and \$ff;
AP := dx*dy*fi2;
destR := destR + sR*AP;
destG := destG + sG*AP;
destB := destB + sB*AP;
end;//for i
end;//for j
color := trunc(destB);
color := color or (trunc(destG) shl 8);
color := color or (trunc(destR) shl 16);
py^[x] := color;
end;//for x
end;//for y
paintbox2.canvas.draw(0,0,bm2);
end;
```   