{ 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 , 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 }