| Single-Bit Bitmaps | Lab Report | |||
|
Chinese Translation by Hector Xiang |
||||
See Bottom of Page for
Modified Version by Jean-Pierre Jouandet
Purpose
The purpose of this program, pf1Bit.EXE, is to
show how to use Scanline and a pByteArray to create and manipulate pf1Bit
bitmaps.
Materials and Equipment
Software Requirements
Windows 95/98
Delphi 3/4/5 (to recompile)
SingleBitBitmaps.EXEHardware Requirements
VGA display
Procedure
Discussion
There are 8 pixels stored in each byte in a pf1bit bitmap
since each pixel is only a single bit.
A "0" pixel is normally black in a pf1bit bitmap, while a "1" pixel is normally white. However, a palette can be attached to a pf1bit bitmap to define any two colors.
The following method is called when the Checker button is clicked (and during the FormCreate):
| procedure
TFormPf1bit.ButtonCheckerClick(Sender: TObject); VAR Bitmap: TBitmap; i : INTEGER; j : INTEGER; Row : pByteArray; begin // Checkerboard pattern Bitmap := TBitmap.Create; TRY WITH Bitmap DO BEGIN Width := 32; Height := 32; // Unclear why this must follow width/height to work correctly. // If PixelFormat precedes width/height, bitmap will always be black. PixelFormat := pf1bit; IF CheckBoxPalette.Checked THEN Bitmap.Palette := GetTwoColorPalette END; 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 IF j MOD 2 = 0 THEN Row[i] := $AA // 1010 1010 ELSE Row[i] := $55 // 0101 0101 END END; ImageBits.Picture.Graphic := Bitmap FINALLY Bitmap.Free END end; |
Note that after the TBitmap is created, its Width and Height must be specified before the PixelFormat. If not, the pf1bit bitmap will display only in black (This pf1bit Scanline Enigma was described in a UseNet Post.)
When the "Define Palette" checkbox is checked, a two-color palette is attached to the bitmap before the scanlines are defined. The GetTwoColorPalette function is defined by the brush colors of the two TShapes. The colors of these TShapes can be changed using the TColorDialog by clicking on either of them:
| // Based on 6 Feb 1999 post to
borland.public.delphi.graphics by // David Ullrich in UseNet Post FUNCTION TFormPf1bit.GetTwoColorPalette: hPalette; VAR Palette: TMaxLogPalette; BEGIN Palette.palVersion := $300; Palette.palNumEntries := 2; WITH Palette.palPalEntry[0] DO BEGIN peRed := GetRValue(ShapeZero.Brush.Color); peGreen := GetGValue(ShapeZero.Brush.Color); peBlue := GetBValue(ShapeZero.Brush.Color); peFlags := 0 END; WITH Palette.palPalEntry[1] DO BEGIN peRed := GetRValue(ShapeOne.Brush.Color); peGreen := GetGValue(ShapeOne.Brush.Color); peBlue := GetBValue(ShapeOne.Brush.Color); peFlags := 0 END; RESULT := CreatePalette(pLogPalette(@Palette)^) END {GetTwoColorPalette}; |
The Black (0), White(1) and Stripes buttons all call the same method: ButtonTagFillClick. The tag field of these buttons define the fill value for each byte in the pf1bit Scanline.
The "g", Arrow and Smiley buttons all call separate methods that define the Scanlines with constants.
Both the "g" and the "arrow" bitmaps are defined as a CONST ARRAY[0..31, 0..3] OF BYTE. These arrays are transferred to a pByteArray Scanline to create a pf1Bit bitmap.
Here's the constant definition for the Arrow array:
| 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} |
For the details, look at the source code for ButtonArrowClick, ButtonGClick, and ButtonSmileyClick. The ButtonRandomClick method fills the Scanlines with random values. Try pressing this button multiple times.
The Invert button forms the logical "Not" of all the binary pixel data while maintaining the same palette. The ButtonInvertClick method shows the details:
| procedure TFormPf1bit.ButtonInvertClick(Sender:
TObject); VAR Bitmap: TBitmap; i: INTEGER; j: INTEGER; RowIn : pByteArray; RowOut: pByteArray; begin Bitmap := TBitmap.Create; TRY WITH Bitmap DO BEGIN Width := ImageBits.Picture.Bitmap.Width; Height := ImageBits.Picture.Bitmap.Height; // Unclear why this must follow width/height to work correctly. // If PixelFormat precedes width/height, bitmap will always be black. PixelFormat := pf1bit; IF CheckBoxPalette.Checked THEN Bitmap.Palette := GetTwoColorPalette END; FOR j := 0 TO Bitmap.Height-1 DO BEGIN RowOut := pByteArray(Bitmap.Scanline[j]); RowIn := pByteArray(ImageBits.Picture.Bitmap.Scanline[j]); FOR i := 0 TO (Bitmap.Width DIV BitsPerPixel)-1 DO BEGIN RowOut[i] := NOT RowIn[i] END END; ImageBits.Picture.Graphic := Bitmap FINALLY Bitmap.Free END; end; |
An alternative Invert approach would have been to swap the palette entries.
Conclusions
Single bit bitmaps may be very useful for masks. pf1bit bitmaps
can save a considerable amount of memory.
Modified Version by Jean-Pierre Jouandet
Includes ability to rotate pf1bit bitmap by 90, 180 or 270 degrees
Keywords
Scanline, PixelFormat, pf1Bit, pByteArray, palette, CreatePalette, TMaxLogPalette,
Bitmap Rotation
Download
Delphi 3/4/5 Source and EXE (120 KB): pf1bit.ZIP
Compiles without any changes in D4 or D5. D3 EXE is 219 KB; D4 EXE is 311 KB; D5 EXE is
321 KB.
Modified version includes 1652-by-2348 pf1bit bitmap (D3/4/5): JeanPierreJouandetPf1bit.ZIP
Postscript
efg's UseNet
Post about converting 256-color bitmap to pf1bit
Updated 18 February 2002
since 1 Nov 1998