PRINTING IN COLOR The HP deskjet line of printer are great. They are relativly inexpensive and all the new models will print in color. When I wanted to write a program that would dump the 16 color VGA screen to my printer, I ran into a problem. I could not find any source code that showed how to print in color. So I had to figure it out for myself. I wasted alot of ink and paper, but I figured it out. Now to save you the frustration here's how to do it. First of all let's explore how to print a simple black and white image to the deskjet. Well first you send some codes that setup the printer, to let it know that we are going to send it some graphic information. Here are the codes to send: Write(Lst, #27'E'); { Reset } Write(Lst, #27'*t300R');{ Set Raster Resolution can be 300,150,75 } Write(Lst, #27'&l0E'); { Set Top Margin 0 Lines } Write(Lst, #27'*b0M'); { Raster Graphics Mode 0 (uncompressed) } Now the printer is ready to accept graphics information. For each line of pixels you want to print you must send these codes: NumBytes:=300; { Number of graphic bytes that I'm going to send } Write(Lst, #27'*b',NumBytes,'W'; { Inform printer to expect graphics line } For I:=1 to 300 Do Write(Lst, #255); { Send a black line } Note that this example will print a solid black line the width of the paper. Of course you would change the #255 to whatever graphic data you want to print. Now these last three steps must be used for every single line. Here is a complete program that will print 5 black lines then eject the page: ---------------------- SNIP HERE ------------------------ Program BlackLin; Uses Printer; Var I, J: Integer; Begin Write(Lst, #27'E'); { Reset } Write(Lst, #27'*t300R');{ Set Raster Resolution can be 300,150,75 } Write(Lst, #27'&l0E'); { Set Top Margin 0 Lines } Write(Lst, #27'*b0M'); { Raster Graphics Mode 0 (uncompressed) } For I:=1 to 5 Do Begin Write(Lst, #27'*b300W'); { Sending 300 bytes of graphic info } For J:=1 to 300 Do Write(Lst, #255); End; Write(Lst, #12); { #12 is a formfeed, makes printer eject page } End. ---------------------- SNIP HERE ------------------------ SOLID BLACK LINES SUCK Now remember that a byte contains 8 bits, and bits are what the printer prints. So for every byte of graphic information that we send the printer is going to print 8 dots. If a bit is on the a black dot will be printed, if a bit is off then the dot is not printed (white). So in the examples we are sending #255 as the graphic information. The value 255 happens to have all the bits set on. This causes the black line to be printed. Now I don't know why anyone would want to print anything besides black lines but if you must send other patterns here is the binary representation: 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 || 128 | 64 | 32 | 16 | 8| 4 | 2 | 1 The first 8 numbers are the values in the first byte and the second 8 are the values in the second byte (of couse this extends for each additional byte sent to the printer). So lets say we want to print a little box that is 16 pixels high and 16 pixels wide. Here is what we would send: 1st line: #255,#255 2nd..15th line: #128,#1 16th line: #255,#255 This would print a solid line for the top and bottom and two vertical lines for the sides. ON TO COLOR ! Ok enough with this black and white stuff, I paid good money for a color printer and by God I want to print in color. Well the printer is capable of printing 7 colors plus white. The colors are: White - No color at all Cyan - Pure color (cartridge has cyan ink) Magenta - Pure color (cartridge has magenta ink) Yellow - Pure color (cartridge has yellow ink) Blue - Combination of Cyan and Magenta Green - Combination of Cyan and Yellow Red - Combination of Magenta and Yellow Black - Combination of Cyan, Magenta, and Yellow (dark brown really) Cyan-Bit Magenta-Bit Yellow-Bit Printed Color -------- ----------- ---------- -------------- 0 0 0 White (none) 0 0 1 Yellow 0 1 0 Magenta 0 1 1 Red 1 0 0 Cyan 1 0 1 Green 1 1 0 Blue 1 1 1 Composite Black Note that if your printer holds two cartridges then you can also print in true black (black ink). In order to print in color you must first inform the printer that you will be printing in color and how you will send the color information. There are three ways to send color information: CMY - Send cyan information, then magenta, then yellow CMYK - Send cyan information, then magenta, then yellow, then true black RGB - Send red information, then green, then blue Note for RGB mode. The printer uses cyan, magenta and yellow ink. In RGB mode the printer translates the RGB values into CMY values. Also note that in RGB mode if R=0 and G=0 and B=0 the color printed is black. Note that the printer does not print in RGB it just processes the data for you to break it into cyan, magenta and yellow. * see note You send the color information exactly like the black information that we send earlier. Only you have to send 3 different lines to get one line on the printer. Here is an example that prints a cyan box: ---------------------- SNIP HERE ------------------------ Program CyanBox; Uses Printer; Var I: Integer; Begin Write(Lst, #27'E'); { Reset } Write(Lst, #27'*t75R'); { Set resolution to 75 DPI } Write(Lst, #27'&l0E'); { Set top margin to 0 lines } Write(Lst, #27'*b0M'); { Send graphics in mode 0 no compression } Write(Lst, #27'*r-3U');{ Sending color graphics in CMY mode } { Use #27'*r-4U for CMYK } { Use #27'*r3U for RGB } Write(Lst, #27'*b2V'); { Sending 2 bytes (note V not W at end) } Write(Lst, #255,#255); { Send first line cyan graphic info } Write(Lst, #27'*b2V'); { Sending 2 bytes (note V not W at end) } Write(Lst, #0#0); { Send first line magenta info } Write(Lst, #27'*b2W'); { Sending 2 bytes (note W for last line) } { If we were using CMYK mode then the setup for yellow would also have V and the K (black) line would have the W. In other words the last color has the W. All others should have V } Write(Lst, #0#0); { Send first line yellow info } For I:=2 to 15 Do Begin Write(Lst, #27'*b2V'); { Sending 2 bytes (note V not W at end) } Write(Lst, #128,#1); { Send first line cyan graphic info } Write(Lst, #27'*b2V'); { Sending 2 bytes (note V not W at end) } Write(Lst, #0#0); { Send first line magenta info } Write(Lst, #27'*b2W'); { Sending 2 bytes (note W for last line) } Write(Lst, #0#0); { Send first line yellow info } End; Write(Lst, #27'*b2V'); { Sending 2 bytes (note V not W at end) } Write(Lst, #255,#255); { Send first line cyan graphic info } Write(Lst, #27'*b2V'); { Sending 2 bytes (note V not W at end) } Write(Lst, #0#0); { Send first line magenta info } Write(Lst, #27'*b2W'); { Sending 2 bytes (note W for last line) } Write(Lst, #0#0); { Send first line yellow info } Write(Lst, #12); { Send formfeed to eject page } End. ---------------------- SNIP HERE ------------------------ Now for something more complex, printing the VGA screen. First off we have a problem. The VGA screen has 16 colors and we only have 8. The problem is that the VGA screen has the same 8 colors only with 2 different intensities. For example Red and LightRed, Blue and LightBlue. So for now we are just going to ignore the intensity. The only real problem is brown, which is low intensity yellow (Oh, well). First order of business, how do we map the screen colors to the printer colors. Well lets look at the screen colors and printer colors in binary. Screen Color - Value - Binary Printer Color - CMY - Value ----------------------------- --------------------------- Black - 0 - 000 Black - 111 - 7 Blue - 1 - 001 Blue - 110 - 6 Green - 2 - 010 Green - 101 - 5 Cyan - 3 - 011 Cyan - 100 - 4 Red - 4 - 100 Red - 011 - 3 Magenta - 5 - 101 Magenta - 010 - 2 Yellow - 6 - 110 Yellow - 001 - 1 White - 7 - 111 White - 000 - 0 Do you see how the screen color values can be converted to the printer color values. Right just reverse the 1's and 0's. The reason you must do this is because screen colors are additive and ink colors are subtractive. In other words when the screen has no color it is black, but if the paper has no color it is white. Now all we have to do is write some code to scan the VGA screen pixel by pixel and convert the colors to printer colors, then print them out. This is kind of more difficult than it seems because of the way the printer values are sent to the printer. Follow this code carefully because there are alot of binary operations to move the color bits around. ---------------------- SNIP HERE ------------------------ Program PrtVGA; Uses Printer, Graph; Procedure Print_VGA_Screen; Var X, Y, I: Integer; { Screen Coords } Screen_Value: Byte; { Holds value of current screen pixel } Printer_Value: Byte; { Holds value of current printer pixel } { These hold the actual bytes that get sent to the printer } Cyan_Line, Magenta_Line, Yellow_Line: Array[0..79] of Byte; Begin { Init printer } Write(Lst, #27'E'); { Reset } Write(Lst, #27'*t150R'); { Set resolution to 150 DPI } Write(Lst, #27'&l0E'); { Set top margin to 0 lines } Write(Lst, #27'*b0M'); { Send graphics in mode 0 no compression } Write(Lst, #27'*r-3U');{ Sending color graphics in CMY mode } For Y:=0 to 479 Do Begin { Zero printer color arrays for each horizontal line } FillChar(Cyan_Line, SizeOf(Cyan_Line), 0); FillChar(Magenta_Line, SizeOf(Magenta_Line), 0); FillChar(Yellow_Line, SizeOf(Yellow_Line), 0); For X:=0 to 639 Do Begin Screen_Value:=GetPixel(X, Y); Screen_Value:=Screen_Value AND 7; { Ignore intensity } Printer_Value:=7 - Screen_Value; { Printer color is bit reversed } { Now comes the complicated part: If a bit is on in Printer_Value then we want to turn on the bit in the appropriate printer color array. } If (Printer_Value AND 1) = 1 Then Yellow_Line[X Div 8]:=Yellow_Line[X Div 8] OR (128 SHR (X Mod 8)); If (Printer_Value AND 2) = 2 Then Magenta_Line[X Div 8]:=Magenta_Line[X Div 8] OR (128 SHR (X Mod 8)); If (Printer_Value AND 4) = 4 Then Cyan_Line[X Div 8]:=Cyan_Line[X Div 8] OR (128 SHR (X Mod 8)); End; { Ok now print the line } Write(Lst, #27'*b80V'); { Sending 80 bytes } For I:=0 to 79 Do Write(Lst, Char(Cyan_Line[I])); Write(Lst, #27'*b80V'); { Sending 80 bytes } For I:=0 to 79 Do Write(Lst, Char(Magenta_Line[I])); Write(Lst, #27'*b80W'); { Sending 80 bytes } For I:=0 to 79 Do Write(Lst, Char(Yellow_Line[I])); End; Write(Lst, #12); { FormFeed, ejects page from printer } End; { Start of main-line code } Var GrDriver, GrMode: Integer; I: Integer; Begin GrDriver:=VGA; GrMode:=VGAHi; InitGraph(GrDriver, GrMode, 'C:\TP\BGI'); SetFillStyle(SolidFill, White); Bar(0, 0, 639, 479); For I:=0 to 150 Do Begin SetColor(I Mod 8); Circle(319, 239, I * 2); End; Print_VGA_Screen; CloseGraph; End. ---------------------- SNIP HERE ------------------------ I bet you thought it was going to be alot harder that this didn't you. Well what can I say about the awsome power of Turbo Pasal. I'm working on developing this topic further by showing you how to read and display BMP files. Then eventually how to print them in color. Bean, thitt@igateway.com *)