Since someone from Italy asked me for an example of using pf1bit
Bitmaps, I thought I would post part of my response and add
other details for pf8bit and pf24bit here in case others were wondering.
Background
-----------------
The new Delphi 3 scanline property allows quick access to
individual pixels, but you must know what Bitmap.PixelFormat
you're working with before you can access the pixels.
Possible PixelFormats include:
pfDevice
pf1bit
pf4bit
pf8bit
pf15bit
pf16bit
pf24bit
pf32bit
pf24bit Bitmaps
----------------------
For pf24bit bitmaps, I define (I wish Borland would)
CONST
PixelCountMax = 32768;
TYPE
pRGBArray = ^TRGBArray;
TRGBArray = ARRAY[0..PixelCountMax-1] OF TRGBTriple;
Note: TRGBTriple is defined in the Windows.PAS unit.
To step through a 24-bit bitmap and while creating a new one and access
the 3-bytes-per-pixel data, use a construct like the following:
...
VAR
i : INTEGER;
j : INTEGER;
RowOriginal : pRGBArray;
RowProcessed: pRGBArray;
BEGIN
IF OriginalBitmap.PixelFormat <> pf24bit
THEN RAISE EImageProcessingError.Create('GetImageSpace: ' +
'Bitmap must be 24-bit color.');
{Step through each row of image.}
FOR j := OriginalBitmap.Height-1 DOWNTO 0 DO
BEGIN
RowOriginal := pRGBArray(OriginalBitmap.Scanline[j]);
RowProcessed := pRGBArray(ProcessedBitmap.Scanline[j]);
FOR i := OriginalBitmap.Width-1 DOWNTO 0 DO
BEGIN
// Access individual color RGB color planes with references like:
// RowProcessed[i].rgbtRed := RowOriginal[i].rgbtRed;
// RowProcessed[i].rgbtGreen := RowOriginal[i].rgbtGreen;
// RowProcessed[i].rgbtBlue := RowOriginal[i].rgbtBlue;
END
END
...
pf8bit Bitmaps
---------------------
Access to these byte-per-pixel bitmaps is easy using the
TByteArray (defined in SysUtils.PAS):
PByteArray = ^TByteArray;
TByteArray = array[0..32767] of Byte;
(I suppose, but I've never tried it, you could access pf16bit Bitmaps using
the following defined in SysUtils.PAS:
PWordArray = ^TWordArray;
TWordArray = array[0..16383] of Word; )
To process an 8-bit (pf8bit) bitmap, use a construct like the following
that
constructs a histogram of such a bitmap:
TYPE
THistogram = ARRAY[0..255] OF INTEGER;
...
VAR
Histogram: THistogram;
i : INTEGER;
j : INTEGER;
Row : pByteArray;
...
FOR i := Low(THistogram) TO High(THistogram) DO
Histogram[i] := 0;
IF Bitmap.PixelFormat = pf8bit
THEN BEGIN
FOR j := Bitmap.Height-1 DOWNTO 0 DO
BEGIN
Row := pByteArray(Bitmap.Scanline[j]);
FOR i := Bitmap.Width-1 DOWNTO 0 DO
BEGIN
INC (Histogram[Row[i]])
END
END
END
...
pf1bit Bitmaps
--------------------
Accessing pf8bit bitmaps is easy since they are one byte per pixel.
But you can save a lot of memory if you only need a single bit per pixel
(such as with various masks), if you use pf1bit Bitmaps.
As with pf8bit bitmaps, use a TByteArray to access pf1bit Scanlines.
But you will need to perform bit operations on the bytes to access the
various pixels. Also, the width of the Scanline is Bitmap.Width DIV 8
bytes.
The following code shows how to create the following kinds of 1-bit
bitmaps:
black, white, stripes, "g", "arrow" and random -- an "invert" option is
also
available. (Send me an E-mail if you'd like the complete working source
code including the form.)
Create a form with an Image1: TImage on it -- I used 1 256x256 Image1
with Stretch := TRUE to see the individual pixels more easily. The buttons
Black, White and stripes have tags of 0, 255, and 85 ($55 = 01010101
binary)
that call ButtonstripesClick when selected.
Buttons "g" and "arrow" call separate event handlers to draw these bitmaps
taken form HP Laserjet examples.
"Random" just randomly sets bits on in the 1-bit bitmaps.
"Invert" changes all the 0s to 1's and vice versa.
// Example of how to use Bitmap.Scanline for PixelFormat=pf1Bit.
// Requested by Mino Ballone from Italy.
//
// Copyright (C) 1997, Earl F. Glynn, Overland Park, KS. All rights
reserved.
// May be freely used for non-commerical purposes.
unit ScreenSingleBit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
ButtonBlack: TButton;
ButtonWhite: TButton;
Buttonstripes: TButton;
ButtonG: TButton;
ButtonArrow: TButton;
ButtonRandom: TButton;
ButtonInvert: TButton;
procedure ButtonstripesClick(Sender: TObject);
procedure ButtonGClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ButtonRandomClick(Sender: TObject);
procedure ButtonInvertClick(Sender: TObject);
procedure ButtonArrowClick(Sender: TObject);
private
Bitmap: TBitmap;
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
CONST
BitsPerPixel = 8;
procedure TForm1.ButtonstripesClick(Sender: TObject);
VAR
i : INTEGER;
j : INTEGER;
Row : pByteArray;
Value : BYTE;
begin
Value := (Sender AS TButton).Tag;
// Value = $00 = 00000000 binary for black
// Value = $FF = 11111111 binary for white
// Value = $55 = 01010101 binary for black & white stripes
FOR j := 0 TO Bitmap.Height-1 DO
BEGIN
Row := pByteArray(Bitmap.Scanline[j]);
FOR i := 0 TO (Bitmap.Width DIV BitsPerPixel)-1 DO
BEGIN
Row[i] := Value
END
END;
Image1.Picture.Graphic := Bitmap
end;
procedure TForm1.ButtonGClick(Sender: TObject);
CONST
{The "g" bitmap was adapted from the LaserJet IIP Printer Tech Ref
Manual}
G: ARRAY[0..31, 0..3] OF BYTE =
{ 0} ( ($00, $FC, $0F, $C0), {00000000 11111100 00001111 11000000}
{ 1} ($07, $FF, $1F, $E0), {00000111 11111111 00011111 11100000}
{ 2} ($0F, $FF, $9F, $C0), {00001111 11111111 10011111 11000000}
{ 3} ($3F, $D7, $DE, $00), {00111111 11010111 11011110 00000000}
{ 4} ($3E, $01, $FE, $00), {00111110 00000001 11111110 00000000}
{ 5} ($7C, $00, $7E, $00), {01111100 00000000 01111110 00000000}
{ 6} ($78, $00, $7E, $00), {01111000 00000000 01111110 00000000}
{ 7} ($F0, $00, $3E, $00), {11110000 00000000 00111110 00000000}
{ 8} ($F0, $00, $3E, $00), {11110000 00000000 00111110 00000000}
{ 9} ($F0, $00, $1E, $00), {11110000 00000000 00011110 00000000}
{10} ($F0, $00, $1E, $00), {11110000 00000000 00011110 00000000}
{11} ($F0, $00, $1E, $00), {11110000 00000000 00011110 00000000}
{12} ($F0, $00, $1E, $00), {11110000 00000000 00011110 00000000}
{13} ($F0, $00, $3E, $00), {11110000 00000000 00111110 00000000}
{14} ($78, $00, $3E, $00), {01111000 00000000 00111110 00000000}
{15} ($78, $00, $3E, $00), {01111000 00000000 00111110 00000000}
{16} ($78, $00, $7E, $00), {01111000 00000000 01111110 00000000}
{17} ($3C, $00, $FE, $00), {00111100 00000000 11111110 00000000}
{18} ($1F, $D7, $DE, $00), {00011111 11010111 11011110 00000000}
{19} ($0F, $FF, $5E, $00), {00001111 11111111 10011110 00000000}
{20} ($07, $FF, $1E, $00), {00000111 11111111 00011110 00000000}
{21} ($00, $A8, $1E, $00), {00000000 10101000 00011110 00000000}
{22} ($00, $00, $1E, $00), {00000000 00000000 00011110 00000000}
{23} ($00, $00, $1E, $00), {00000000 00000000 00011110 00000000}
{24} ($00, $00, $1E, $00), {00000000 00000000 00011110 00000000}
{25} ($00, $00, $3E, $00), {00000000 00000000 00111110 00000000}
{26} ($00, $00, $3C, $00), {00000000 00000000 00111100 00000000}
{27} ($00, $00, $7C, $00), {00000000 00000000 01111100 00000000}
{28} ($00, $01, $F8, $00), {00000000 00000001 11111000 00000000}
{29} ($01, $FF, $F0, $00), {00000001 11111111 11110000 00000000}
{30} ($03, $FF, $E0, $00), {00000011 11111111 11100000 00000000}
{31} ($01, $FF, $80, $00)); {00000001 11111111 10000000 00000000}
VAR
i : INTEGER;
j : INTEGER;
Row: pByteArray;
begin
FOR j := 0 TO Bitmap.Height-1 DO
BEGIN
Row := pByteArray(Bitmap.Scanline[j]);
FOR i := 0 TO (Bitmap.Width DIV BitsPerPixel)-1 DO
BEGIN
Row[i] := G[j,i]
END
END;
Image1.Picture.Graphic := Bitmap
end;
procedure TForm1.ButtonArrowClick(Sender: TObject);
CONST
{The "arrow" bitmap was adapted from the LaserJet IIP Printer Tech Ref
Manual}
Arrow: ARRAY[0..31, 0..3] OF BYTE =
{ 0} ( ($00, $00, $80, $00), {00000000 00000000 10000000 00000000}
{ 1} ($00, $00, $C0, $00), {00000000 00000000 11000000 00000000}
{ 2} ($00, $00, $E0, $00), {00000000 00000000 11100000 00000000}
{ 3} ($00, $00, $F0, $00), {00000000 00000000 11110000 00000000}
{ 4} ($00, $00, $F8, $00), {00000000 00000000 11111000 00000000}
{ 5} ($00, $00, $FC, $00), {00000000 00000000 11111100 00000000}
{ 6} ($00, $00, $FE, $00), {00000000 00000000 11111110 00000000}
{ 7} ($00, $00, $FF, $00), {00000000 00000000 11111111 00000000}
{ 8} ($00, $00, $FF, $80), {00000000 00000000 11111111 10000000}
{ 9} ($FF, $FF, $FF, $C0), {11111111 11111111 11111111 11000000}
{10} ($FF, $FF, $FF, $E0), {11111111 11111111 11111111 11100000}
{11} ($FF, $FF, $FF, $F0), {11111111 11111111 11111111 11110000}
{12} ($FF, $FF, $FF, $F8), {11111111 11111111 11111111 11111000}
{13} ($FF, $FF, $FF, $FC), {11111111 11111111 11111111 11111100}
{14} ($FF, $FF, $FF, $FE), {11111111 11111111 11111111 11111110}
{15} ($FF, $FF, $FF, $FF), {11111111 11111111 11111111 11111111}
{16} ($FF, $FF, $FF, $FF), {11111111 11111111 11111111 11111111}
{17} ($FF, $FF, $FF, $FE), {11111111 11111111 11111111 11111110}
{18} ($FF, $FF, $FF, $FC), {11111111 11111111 11111111 11111100}
{19} ($FF, $FF, $FF, $F8), {11111111 11111111 11111111 11111000}
{20} ($FF, $FF, $FF, $F0), {11111111 11111111 11111111 11110000}
{21} ($FF, $FF, $FF, $E0), {11111111 11111111 11111111 11100000}
{22} ($FF, $FF, $FF, $C0), {11111111 11111111 11111111 11000000}
{23} ($00, $00, $FF, $80), {00000000 00000000 11111111 10000000}
{24} ($00, $00, $FF, $00), {00000000 00000000 11111111 00000000}
{25} ($00, $00, $FE, $00), {00000000 00000000 11111110 00000000}
{26} ($00, $00, $FC, $00), {00000000 00000000 11111100 00000000}
{27} ($00, $00, $F8, $00), {00000000 00000000 11111000 00000000}
{28} ($00, $00, $F0, $00), {00000000 00000000 11110000 00000000}
{29} ($00, $00, $E0, $00), {00000000 00000000 11100000 00000000}
{30} ($00, $00, $C0, $00), {00000000 00000000 11000000 00000000}
{31} ($00, $00, $80, $00)); {00000000 00000000 10000000 00000000}
VAR
i : INTEGER;
j : INTEGER;
Row: pByteArray;
begin
FOR j := 0 TO Bitmap.Height-1 DO
BEGIN
Row := pByteArray(Bitmap.Scanline[j]);
FOR i := 0 TO (Bitmap.Width DIV BitsPerPixel)-1 DO
BEGIN
Row[i] := arrow[j,i]
END
END;
Image1.Picture.Graphic := Bitmap
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Bitmap := TBitmap.Create;
WITH Bitmap DO
BEGIN
Width := 32;
Height := 32;
PixelFormat := pf1bit
END;
Image1.Picture.Graphic := Bitmap
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Bitmap.Free
end;
procedure TForm1.ButtonRandomClick(Sender: TObject);
VAR
i : INTEGER;
j : INTEGER;
Row: pByteArray;
begin
FOR j := 0 TO Bitmap.Height-1 DO
BEGIN
Row := pByteArray(Bitmap.Scanline[j]);
FOR i := 0 TO (Bitmap.Width DIV BitsPerPixel)-1 DO
BEGIN
Row[i] := Random(256)
END
END;
Image1.Picture.Graphic := Bitmap
end;
procedure TForm1.ButtonInvertClick(Sender: TObject);
VAR
i : INTEGER;
j : INTEGER;
Row: pByteArray;
begin
FOR j := 0 TO Bitmap.Height-1 DO
BEGIN
Row := pByteArray(Bitmap.Scanline[j]);
FOR i := 0 TO (Bitmap.Width DIV BitsPerPixel)-1 DO
BEGIN
Row[i] := NOT Row[i]
END
END;
Image1.Picture.Graphic := Bitmap
end;
end.
Maybe someday Borland will give us a place to send Technical Notes
like this so they're easier to find in the future. Using the DejaNews
search engine is effective but not always very efficient.
efg
_________________________________________________
Earl F. Glynn EarlGl...@WorldNet.att.net
MedTech Research Corporation
Lenexa, KS 66219 USA