[Back to GRAPHICS SWAG index]  [Back to Main SWAG index]  [Original]

{
From: WILLIAM PLANKE
Subj: Write PCX example 1/4

As I follow this forum, many requests are made for PCX graphics
file routines. Those that are looking for Read_PCX info can
find it on the ZSoft BBS in a wonderful Pascal example: ShowPCX.

On the other hand, there is next to zilch out there on how to
Write_PCX files. I know.... I searched and searched and couldn't
find a thing! So with a little brute force  and a few ZSoft
C language snippets <groan>, I got this together:
}


{ =================== TPv6.0  P C X _ W ======================== }

{$R-}    {Range checking, turn off when debugged}

unit PCX_W;

{ --------------------- Interface ----------------- }

interface

type
    Str80 = string [80];

procedure Write_PCX  (Name:Str80);


{ ===================== Implementation ============ }

implementation

uses
    Graph;


{-------------- Write_PCX --------------}

procedure Write_PCX (Name:Str80);

const
     RED1   = 0;
     GREEN1 = 1;
     BLUE1  = 2;

type
    ArrayPal   = array [0..15, RED1..BLUE1] of byte;

const
     MAX_WIDTH  = 4000;    { arbitrary - maximum width (in bytes) of
                             a PCX image }
     INTENSTART =   $5;
     BLUESTART  =  $55;
     GREENSTART =  $A5;
     REDSTART   =  $F5;

type
    Pcx_Header = record
    {comments from ZSoft ShowPCX pascal example}

        Manufacturer: byte;     { Always 10 for PCX file }

        Version: byte;          { 2 - old PCX - no palette (not used
                                      anymore),
                                  3 - no palette,
                                  4 - Microsoft Windows - no palette
                                      (only in old files, new Windows
                                      version uses 3),
                                  5 - with palette }

        Encoding: byte;         { 1 is PCX, it is possible that we may
                                  add additional encoding methods in the
                                  future }

        Bits_per_pixel: byte;   { Number of bits to represent a pixel
                                  (per plane) - 1, 2, 4, or 8 }

        Xmin: integer;          { Image window dimensions (inclusive) }
        Ymin: integer;          { Xmin, Ymin are usually zero (not always)}
        Xmax: integer;
        Ymax: integer;

        Hdpi: integer;          { Resolution of image (dots per inch) }
        Vdpi: integer;          { Set to scanner resolution - 300 is
                                  default }

        ColorMap: ArrayPal;
                                { RGB palette data (16 colors or less)
                                  256 color palette is appended to end
                                  of file }

        Reserved: byte;         { (used to contain video mode)
                                  now it is ignored - just set to zero }

        Nplanes: byte;          { Number of planes }

        Bytes_per_line_per_plane: integer;   { Number of bytes to
                                               allocate for a scanline
                                               plane. MUST be an an EVEN
                                               number! Do NOT calculate
                                               from Xmax-Xmin! }

        PaletteInfo: integer;   { 1 = black & white or color image,
                                  2 = grayscale image - ignored in PB4,
                                      PB4+ palette must also be set to
                                      shades of gray! }

        HscreenSize: integer;   { added for PC Paintbrush IV Plus
                                  ver 1.0,  }
        VscreenSize: integer;   { PC Paintbrush IV ver 1.02 (and later)}
                                { I know it is tempting to use these
                                  fields to determine what video mode
                                  should be used to display the image
                                  - but it is NOT recommended since the
                                  fields will probably just contain
                                  garbage. It is better to have the
                                  user install for the graphics mode he
                                  wants to use... }

        Filler: array [74..127] of byte;     { Just set to zeros }
    end;

    Array80    = array [1..80]        of byte;
    ArrayLnImg = array [1..326]       of byte; { 6 extra bytes at
     beginng of line that BGI uses for size info}
    Line_Array = array [0..MAX_WIDTH] of byte;
    ArrayLnPCX = array [1..4]         of Array80;

var
   PCXName   : File;
   Header    : Pcx_Header;                 { PCX file header }
   ImgLn     : ArrayLnImg;
   PCXLn     : ArrayLnPCX;
   RedLn,
   BlueLn,
   GreenLn,
   IntenLn   : Array80;
   Img       : pointer;


{-------------- BuildHeader- -----------}

procedure BuildHeader;

const
     PALETTEMAP: ArrayPal=
                 {  R    G    B                    }
                (($00, $00, $00),  {  black        }
                 ($00, $00, $AA),  {  blue         }
                 ($00, $AA, $00),  {  green        }
                 ($00, $AA, $AA),  {  cyan         }
                 ($AA, $00, $00),  {  red          }
                 ($AA, $00, $AA),  {  magenta      }
                 ($AA, $55, $00),  {  brown        }
                 ($AA, $AA, $AA),  {  lightgray    }
                 ($55, $55, $55),  {  darkgray     }
                 ($55, $55, $FF),  {  lightblue    }
                 ($55, $FF, $55),  {  lightgreen   }
                 ($55, $FF, $FF),  {  lightcyan    }
                 ($FF, $55, $55),  {  lightred     }
                 ($FF, $55, $FF),  {  lightmagenta }
                 ($FF, $FF, $55),  {  yellow       }
                 ($FF, $FF, $FF) );{  white        }

var
   i : word;

begin
     with Header do
          begin
               Manufacturer  := 10;
               Version  := 5;
               Encoding := 1;
               Bits_per_pixel := 1;
               Xmin := 0;
               Ymin := 0;
               Xmax := 639;
               Ymax := 479;
               Hdpi := 640;
               Vdpi := 480;
               ColorMap := PALETTEMAP;
               Reserved := 0;
               Nplanes  := 4; { Red, Green, Blue, Intensity }
               Bytes_per_line_per_plane := 80;
               PaletteInfo := 1;
               HscreenSize := 0;
               VscreenSize := 0;
               for i := 74 to 127 do
                   Filler [i] := 0;
          end;
end;


{-------------- GetBGIPlane ------------}

procedure GetBGIPlane (Start:word; var Plane:Array80);

var
   i : word;

begin
     for i:= 1 to Header.Bytes_per_line_per_plane do
         Plane [i] := ImgLn [Start +i -1]
end;

{-------------- BuildPCXPlane ----------}

procedure BuildPCXPlane (Start:word; Plane:Array80);

var
   i : word;

begin
     for i := 1 to Header.Bytes_per_line_per_plane do
         PCXLn [Start] [i] := Plane [i];
end;


{-------------- EncPCXLine -------------}

procedure EncPCXLine (PlaneLine : word); { Encode a PCX line }

var
   This,
   Last,
   RunCount : byte;
   i,
   j        : word;


  {-------------- EncPut -----------------}

  procedure EncPut (Byt, Cnt :byte);

  const
       COMPRESS_NUM = $C0;  { this is the upper two bits that
                              indicate a count }

  var
     Holder : byte;

  begin
  {$I-}
       if (Cnt = 1) and (COMPRESS_NUM <> (COMPRESS_NUM and Byt)) then
          blockwrite (PCXName, Byt,1)          { single occurance }
          {good place for file error handler!}
       else
           begin
                Holder := (COMPRESS_NUM or Cnt);
                blockwrite (PCXName, Holder, 1); { number of times the
                                                   following color
                                                   occurs }
                blockwrite (PCXName, Byt, 1);
           end;
  {$I+}
  end;


begin
     i := 1;         { used in PCXLn }
     RunCount := 1;
     Last := PCXLn [PlaneLine][i];
     for j := 1 to Header.Bytes_per_line_per_plane -1 do
         begin
              inc (i);
              This := PCXLn [PlaneLine][i];
              if This = Last then
                 begin
                      inc (RunCount);
                      if RunCount = 63 then   { reached PCX run length
                                                limited max yet? }
                         begin
                              EncPut (Last, RunCount);
                              RunCount := 0;
                         end;
                 end
              else
                  begin
                       if RunCount >= 1 then
                          Encput (Last, RunCount);
                       Last := This;
                       RunCount := 1;
                  end;
         end;
     if RunCount >= 1 then  { any left over ? }
        Encput (Last, RunCount);
end;

            { - - -W-R-I-T-E-_-P-C-X- - - - - - - - }

const
     XMAX = 639;
     YMAX = 479;

var
   i, j, Size : word;

begin
     BuildHeader;
     assign     (PCXName,Name);
{$I-}
     rewrite    (PCXName,1);
     blockwrite (PCXName,Header,sizeof (Header));
     {good place for file error handler!}
{$I+}
     setviewport (0,0,XMAX,YMAX, ClipOn);
     Size := imagesize (0,0,XMAX,0); { size of a single row }
     getmem (Img,Size);

     for i := 0 to YMAX do
         begin
              getimage (0,i,XMAX,i,Img^);  { Grab 1 line from the
                                             screen store in Img
                                             buffer  }
              move (Img^,ImgLn,Size {326});


              GetBGIPlane (INTENSTART, IntenLn);
              GetBGIPlane (BLUESTART,  BlueLn );
              GetBGIPlane (GREENSTART, GreenLn);
              GetBGIPlane (REDSTART,   RedLn  );
              BuildPCXPlane (1, RedLn  );
              BuildPCXPlane (2, GreenLn);
              BuildPCXPlane (3, BlueLn );
              BuildPCXPlane (4, IntenLn); { 320 bytes/line
                                            uncompressed }
              for j := 1 to Header.NPlanes do

                  EncPCXLine (j);
         end;
     freemem (Img,Size);           (* Release the memory        *)
{$I-}
     close (PCXName);              (* Save the Image            *)
{$I+}
end;

end {PCX.TPU} .


{ -----------------------Test Program -------------------------- }

program WritePCX;

uses
    Graph, PCX_W;

{-------------- DrawHorizBars ----------}

procedure DrawHorizBars;

var
   i, Color : word;

begin
     cleardevice;
     Color := 15;
     for i := 0 to 15 do
         begin
              setfillstyle (solidfill,Color);
              bar (0,i*30,639,i*30+30);       { 16*30 = 480 }
              dec (Color);
         end;
end;

{-------------- Main -------------------}

var
   NameW : Str80;
   Gd,
   Gm    : integer;

begin
     writeln;
     if (ParamCount = 0) then           { no DOS command line
                                          parameters }
        begin
             write ('Enter name of PCX picture file to write: ');
             readln (NameW);
             writeln;
        end
     else
         begin
              NameW := paramstr (1);  { get filename from DOS
                                        command line }
         end;

     if (Pos ('.', NameW) = 0) then   { make sure the filename
                                        has PCX extension }
        NameW := Concat (NameW, '.pcx');

     Gd:=VGA;
     Gm:=VGAhi; {640x480, 16 colors}
     initgraph (Gd,Gm,'..\bgi');  { path to your EGAVGA.BGI }

     DrawHorizBars;

     readln;
     Write_PCX (NameW); { PCX_W.TPU }
     closegraph;                    { Close graphics    }
     textmode (co80);               { back to text mode }
end.  { Write_PCX }

[Back to GRAPHICS SWAG index]  [Back to Main SWAG index]  [Original]