ViewPoint(TM) ------------- Howdy. I hate stupid ads. I think marketing hype and stuff that looks like it belongs on a cerial box belong in this category. So this is not an ad in the usual sence of the word. I'll stay away from hype and slogans and just tell you about the library, programmer to programmer. First some basics: the "C++ ViewPoint Graphics Library -- Essential Drawing Components" is a rendering library for PC's. It is easy to use, powerful, and fast. ------------------------------- I interrupt this prose to bring you an example, to show the overall flavor of the library. Here is a graphical equivilent of a "hello world" program-- one that shows you can compile and link and do something simple. This draws a square with an X in the middle, centered in the screen. It is mostly housekeeping and overhead, since the program itself does not do much. #include "usual.h" #include "vp.h" #include "device.h" #include "getkey.h" screen_device d ("vga.drv"); int main() { if (!d.init()) return 3; //go into graphics mode viewport v (d); //the whole screen v.set_scale (0,0,999,999); v.rectangle (333,333, 666,666); v.line (400,400, 600,600); v.line (400,600, 600,400); key::get(); //pause } First, look at the include files. USUAL.H is included in every source file. VP.H is included for the viewport class, and DEVICE.H is included for the device class and its derived type, the screen_device. The last include file, GETKEY.H, is to supply the key class used in the example to pause and wait for a keypress. The next line defines `d', a screen_device. The screen device is a shell around loadable device drivers, and the constructor will load the named driver. The first line in main() is a call to device::init() which will activate the driver. A parameter can be given to specify which mode to use, as a driver may support a number of different graphics modes. In this case no parameter is used so it will take the default as recorded in the driver file itself. It returns FALSE if not successful. After calling init(), `d' is an active device, and any rendering primitives (member functions of class device) can be used on it. However, these drawing functions really are quite primitive, and they use absolute device pixels. Depending on which driver you used, the number of pixels on the screen can vary. So drawing a rectangle in the center will require some calculation. You can find out what the resolution of the display is with d.xmax() and d.ymax(), and use some computations in every point you plot to adjust it to the actual display size. That is quite a pain. So the library offers "world coordinates" which is basically a way to have the library do all that calculation for you. The viewport is a higher level class which has this ability. A viewport is attached to some device. Line 11 defines `v' to be a viewport attached to `d' and taking up the whole screen. Now you can use the high level commands in v instead of the low level commands in d, but the scaling problem still remains-- the pixels in v are exactly the same as those in d. The next line solves this. Instead of having to adapt my code to deal with whatever display resolution is at run time, I turn it around and tell it what size coordinate system I want to use. The call to v.set_scale() specifies what I want to call the upper left and lower right pixels in the viewport. Now, all uses of v will use the resolution I specified, automatically mapping it to the physical device. The next line draws a rectangle, centered in the 1000x1000 viewport. The following draw the diagonal lines to form the X. The last waits for a keypress. Note that there is no code for cleaning up. When v goes out of scope, the viewport is destroyed. When d goes out of scope (at program termination) its destructor will put the screen back in text mode, as if d.finish() was called. ------------------------------- The 17 line example above should give you some idea of how it is set up. There is a low level device class and a high level viewport class. The device class offers primitive commands. There are command for reading device information (like what the resolution is), color and palette control, display control, and rendering. The color commands can deal with 32 bit color. Many other libraries are locked into 8 bit pixels. But the future of PC graphics offers more advanced abilities than this. Since 32 bit numbers are less efficient to manipulate, the library can be compiled to deal with 8, 16 or 32 bit values (16 is the default). Code compiled with one option can be linked with code compiled with another setting. With ViewPoint, you can use advanced features like the Siearra Hi-Color DAC with 32768 colors. Color may be mapped, and there are functions to set up the palette. The palette control will do the best it can for the device in use. The BGI, for example, uses different commands for EGA and VGA color settings! The VP's device class is intended to abstract the device, so this one function will set palette mapping in the best way for whatever device. The rendering functions include pixels, lines, rectangles, flood filling, and block moves. They are all pretty simple, like read_pixel(x,y) or line (style, x1,y1,x2,y2). You would find using functions on this level to be very much like using other graphics libraries. But you normally don't use the device rendering functions. Rather, you use the viewport rendering functions, which are themselves implemented using the device's. A viewport is a higher level entity than an device. It belongs to some device, so drawing with the viewport will make stuff appear on that device. It also holds a style and a coordinate system. All the device rendering commands take a style as the first parameter. The viewport holds a style and implies this style, so you don't specify it each time. A viewport does coordinate transformations. Rather than plotting in device pixels, you can use any kind of coordinate system you like. The program above could pretend that the screen was 1000x1000 and use constants for all its positions, even though this program will run on any kind of device at all. That is a simple example. Coordinate systems can be rotated and skewed. See the SEGMENT demo program. The viewport also has more drawing functions, and they are friendlier. While a device has void device::line (const gfxstyle&, int x1,int y1, int x2,int y2); A viewport has // absolute viewport& line (int x1,int y1, int x2,int y2); viewport& line (const pair& p1, const pair& p2); viewport& line (const rect& r); viewport& line (int x, int y); viewport& line (const pair& p2); viewport& movecp (int x1, int y1); viewport& movecp (const pair& p); // relative viewport& line_rel (int deltax, int deltay); viewport& line_rel (const pair& delta); viewport& up (int dist); viewport& down (int dist); viewport& left (int dist); viewport& right (int dist); viewport& movecp_rel (int deltax, int deltay); viewport& movecp_rel (const pair& p); // polylines viewport& polyline (int count, const int* data); //absolute viewport& polyline (int count, const pair* data); //absolute viewport& polyline_rel (int count, const int* data, int x, int y); viewport& polyline_rel (int count, const int* data, const pair& start); viewport& polyline_rel (int count, const pair* data, int x, int y); viewport& polyline_rel (int count, const pair* data, const pair& start); Of these 21 functions, 5 of them are called `line'. Thanks to overloading in C++, the library can be easier to use. It is quite practical to have 5 different line functions since you don't have to remember 5 different names. You just give it whatever kind of data you happen to have on hand for its parameters. In this case, you can feed it 4 ints, (x and y source and dest), 2 ints (x and y of the dest, continues from where the last one left off), 2 pairs, one pair (dest only), or a rectangle. It makes the library easy to use because you can use the data you have on hand rather than keeping it in the form the library likes, or having to convert it. When you draw a line, it will appear in some color or colors, using some logical operation and some pattern. All this is in the style structure. Each viewport has its own style. In the first example program, the color of the drawing could have been specified by saying v.style.FPen= 7; // forground pen is white or somesuch. There is an FPen (forground pen) and a BPen (background pen). You can set a 16 bit line pattern. There are two ways the line pattern is applied. In all cases 1's in the pattern are drawn using the FPen. You have your choice with the 0's. They can be transparent or drawn using BPen. In all cases, a logic operation can also be applied to all pixel drawn: REPLACE, AND, OR, or XOR. When drawing a polyline, the line pattern is continuous across the whole thing. It does not start over with each segment, like most other libraries do. This is a general ability: the style can be set to preserve line patterns in progress across multiple calls, so lines connect smoothly. Patterns are applied to curves too. Another fine touch for using patterns is the "open interval". Say you draw from A to B and then from B to C. Point B is drawn twice. If you are using XOR mode, that is bad. If you are trying to make your line pattern continuous across the segments, that is bad. With an open interval, the second line does not draw from B to C but skipps the first pixel. B is not drawn twice. With diagonal lines and scaled coordinate systems, this would be nearly impossible to do manually. But ViewPoint supports it as a low level primitive, where it belongs. There are other fields in the style for dealing with filled shapes. A filled shape can be drawn in a solid color using the FPen. Or it can use a fill pattern. The pattern can be any size, though drivers can impose an allingment restriction (width must be a multiple of 8, for example). The fill pattern can take two forms. A one plane fill pattern is just like the line pattern in two dimentions. That is, it uses FPen for the 1's and your choice of BPen or transparent for the 0's. Or a fill pattern can be full color. In all cases, the logic modes apply. Fill patterns work the same way on all filled shapes-- rectangles, polygons, ellipses, even flood filling. You might think a rectangle is pretty simple. But with a rotated or skewed coordinate system the rectangle can appear diamond shaped. Naturally it handles all this by itself. But that is simple. Filled polygons are more interesting. The viewport class has 6 forms of the filled_polygon() function. Paramters can be arrays of ints or pairs, and a starting point can be specified as 2 ints or a pair or left out. Polygons are very difficult to do "right" in raster graphics. ViewPoint implements correct meshing polygons. That is, two polygons that have the same edge in common will mesh together seemlessly. No pixel is drawn twice. Only points that are strictly on the inside of the polygon are drawn, and a a decision process is used to handle points on the edges and corners. This means that a polygon mesh can be drawn to form complex shapes. Furthermore, the implementation is fast. It was timed reciently as being over 30% faster than BGI's filled polygons, and their's are "sloppy". But sometimes you don't want meshing polygons. For a single polygon sitting out there by itself, it can appear "shaved". You want it to round off rather than rounding in. You don't want the left corner pixel to be zapped. So you can also specify polygons with Bresenham edges. That is, the polygon will exactly fit over a set of ordinary lines drawn between the same points. There are 21 ways to draw lines, 6 to draw filled polygons. And that is just counting the function calls, not the variations in the `mode' parameter for polys or all the differnet settings in the style. How many ways can you draw ellipses and ellipse related things? More than a few dozen... more like hundreds! You can specify center-radii, specify bounding boxes in several different ways, and so on. Then you can draw an arc or the whole thing, where the arc can be specified in several different ways. The number of functions needed was into the double digits at the minimum. So a better way was invented. The different properties of the rendering call are specified by individual calls, and several forms of each are available. This means that the total number of effective calls is equal to the product of the numbers of its parts. Here are some examples: v.ellipse(center,rad_x,rad_y).draw(); //draw whole ellipse v.ellipse(bounding_box).arc(angle1,angle2).draw(); //draw arc Notice that the second line is three functions. The first call, ellipse(), could have used several (6) different forms to specify the same shape. The second call, arc(), could have used 2 different forms or be left out entirely to draw the whole thing. And finally, the third call, draw(), does the rendering of the built-up definition. Instead of draw() you could have used filled() or pieslice() or others. This paragraph touched on 54 (though only 48 are useful) effective rendering functions. Viewports support flood fills. As you've probably guessed by now, there are several ways to do it. First of all, it uses the fill pattern and style stuff the same way as any other filled shape. Even if you are filling with a pattern and a logical operation that turns into the border color, or that does nothing, it will not get confused. Any shape can be filled properly no matter what modes, colors, or styles are in use. You can have flood fill search out a border color and stop at the border. Or you can have it fill over a region of a color and stop when it hits something that is not that color. For simpler fills, you can specify the fast fill mode which does not work with arbitrary shapes but is just fine for convex shapes and is much faster. There is also a change_color() function that will change all pixels of one color inside a specified rectangle into another color. This function is extremely fast, and is great for things like hilighting menu choices. There are move and scroll functions that copy stuff from one place on the screen to another. Naturally, there are lots of choices on parameters and modes. Moving is really a special case of bitmap manipulation-- GET and PUT functions. Bitmaps are a major part of the ViewPoint library, and they even have their own class. Class pixarray has over three dozen functions in it. A pixarray holds a bitmap image. It does its own memory management, and will use the far heap even in a small model program. You don't have to allocate memory for it-- that is automatic. GETing an image is as simple as: pix= v.get(r); where pix is a pixarray, v is a viewport, and r is a rect. You can use different forms for the paramters besides a rect. This shows that bitmaps can be used in an expressive way-- they can be used in expressions as high level objects. The flip side of a get is a put. Just putting a pixarray at some position would be too boreing. Using the logical operations is better. But there is even more variety: you can put just part of a pixarray, and magnify while putting, or both. Besides a place to hold an image between a get and a put, class pixarray can be used to manipulate the image. A simple example is to recolor it. You can map one set of colors onto another, or extract individual colors. You can query the pixarray for its size and other info, and read and write individual pixels within it, and even copy parts from one bitmap to another. A more exotic function is ortho_transform() which is really 8 functions rolled into one. It can mirror, transpose, and rotate (in 90 degree increments) an image. There are also functions to load and save pixarrays to disk files. This should give you a general feeling for the concepts the library is founded on. But it is just scratching the surface. There is support for text that lets you have different kinds of fonts and is very extendable. And the mouse! The "intelligent mouse classes" are deserving of their own demonstration. You've never seen anything like it! So check it out! You can have all this for $220 plus shipping. It includes full source code. Tech support is good. Updates and bug fixes are available online. There is a 60 day satisfaction guarantee. John M. Dlugosz CIS: 70007,4657 Dlugosz Software PO Box 876506 Plano TX 75086 (214)618-2023