

KNOW-HOW GDI Shell v. 1.0
(C) Stepan S.Vartanov, 1995 - 96



1. Features. 
*	KNOW-HOW GDI Shell v. 1.0 is the WINAPI GDI functions wrapper. 
Its primary purpose is to hide drawing details, such as scaling, 
scrolling and rotations of the image. 
*	ver. 1.0 supports vector graphics only (no bitmaps).
*	KNOW-HOW GDI Shell is a class library, you can instantiate its 
classes or derive your own classes from them.
*	Ver. 1.0 was not tested with Windows 95 - it will be changed in ver. 
1.2
*	Ver. 1.0 uses Borland BGI fonts for text output (zoom, rotate are 
available). TRUE TYPE fonts support will be added in ver. 1.1 and will 
be available in COMMERCIAL (non-SHAREWARE) version only.



2. Usage.

	As with any class library there are few possible approaches. Some 
suggested approaches are described below. See also KH_DEMO project.
We are going to produce picture of the 'bird'.

 


// Put this code in application main file
// This let you to init drawTool only once

#include "drawtool.h"

...

YourApplication::Init()
	 {
	 ...
// Create drawTool in application's destructor
	 drawTool = new KH_Paint();
	 ...
	 }
YourApplication::~YourApplication()
    {
    ....
    delete drawTool;
    ....
    }

Use drawTool drawing functions:

#include "drawtool.h"

MyWindow::draw()
    {
    drawTool->line(100, 100, 200, 300)
    }

Another approach (used in example) uses 
inheritance:

#include "bgipaint.h"

class MyWindow : public KH_Paint
    {
    ...
    drawPig(x, y);	// Your function
    ...
    };

....

MyWindow::drawPig(int x, int y)
    {
    line(100, 100, 200, 300);
    ....
    }



3. Classes and Functions. 


File BGI_FONT.H
	Class encapsulates BGI fonts (*.chr) description. BGI fonts are not 
documented. I use existing format.

struct _export BGI_Font : public AbstractGraphics
	{
      ...
// Deformation of an image
	uchar multx, multy, divx, divy;   	
// Create object and load() (BGI) font. 
	BGI_Font(char* fileName = 0);  
	~BGI_Font();
// Remove old and load new font. Fomt should 
// exist, or NULL is loaded
	void load(char* fileName);     
// Character output
	int draw_char(int x,int y,char c, int dir=0);
// Line of characters
	int outtextxy(int x, int y, char* str, int 
          dir = 0);
// Overload when API accessible
	void outtext(char* str) {} 
// Width of text, pixels (not rotated)
	int gettextwidth(char*  str);
// Heigth of text, pixels (not rotated)
	int getheight() { return CapitalHeight; }
// Set character deformation
	void setusercharsize(int mx, int dx, int my, 
          int dy)
	    {multx=mx; multy=my; divx=dx; divy=dy; }
// Set text justification
	void settextjustify(int j,int k) 
         {h_just=j;v_just = k; }
    };



FILE DRAWTOOL.H

	Defines global drawing object - in the case if you do not want to use 
inheritance. This is not part of library - just an example.:

	#ifndef __KH_PAINT_H_
	#define __KH_PAINT_H_

	#include "bgipaint.h"
	extern KH_Paint* drawTool;



FILE BGIPAINT.H
	User will probably work only with this file. Real API functions of 
Windows are available to this class(es).

// Convenient names - you can use any set - Windows, if your are more 
used to it, or Borland BGI-like names:
	#define SOLID_FILL              1
	#define SOLID_LINE              PS_SOLID
	#define DASHED_LINE             PS_DASH
	#define DOTTED_LINE             PS_DOT
	#define CENTER_LINE             PS_DASHDOT

// Do not use HS_BDIAGONAL, HS_FDIAGONAL, 
// VS_VERTICAL, HS_HORIZONTAL HS_CROSS and 
// HS_DIAGCROSS !!!

	#define LINE_FILL               0
	#define HATCH_FILL              2
	#define SLASH_FILL              3
	#define XHATCH_FILL             4
	#define BKSLASH_FILL            5
	#define LTSLASH_FILL            6

	#define LEFT_TEXT     0
	#define CENTER_TEXT   1
	#define RIGHT_TEXT    2
	#define BOTTOM_TEXT   0
	#define TOP_TEXT      2

	#define HS_SOLID                SOLID_FILL

	enum COLORS { BLACK, BLUE, GREEN, CYAN, RED, 
MAGENTA, BROWN, LIGHTGRAY, DARKGRAY, LIGHTBLUE, 
LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA, 
YELLOW, WHITE };

// Use this structure to reference collors in 16 
colors scale
	struct color_to_16
	 {
	 int color;
	 COLORREF colorref;
	 };
	// Descriptions of colors
	static color_to_16 tricolors[16] =
	 {   { BLACK, 0 },
		  { BLUE, RGB(0, 0, 128) },
		  { GREEN, RGB(0, 128, 0) },
		  {  CYAN, RGB(0, 128, 128) },
		  {  RED, RGB(128, 0, 0) },
		  { MAGENTA, RGB(74, 23, 74) },
		  { BROWN, RGB(128, 64, 64) },
		  { LIGHTGRAY, RGB(128, 128, 128) },
		  { DARKGRAY, RGB(64, 64, 64) },
		  { LIGHTBLUE, RGB(0, 0, 255) },
		  { LIGHTGREEN, RGB(0, 255, 0) },
		  { LIGHTCYAN, RGB(0, 255, 255) },
		  { LIGHTRED, RGB(255, 0, 0) },
		  { LIGHTMAGENTA, RGB(255, 0, 255) },
		  { YELLOW, RGB(255,255,30) },
		  { WHITE, RGB(255, 255, 255) }
	 };

// There are some reasons why this class is not 
part of 
// KH_Paint. However under normal circumstances 
user will never use it 
// directly
	struct _export To_Paint
	 {
	 int PenSize;
	 int PenStyle;
	 COLORREF color;

	 int pattern_num;
	 int fill_color;
	 COLORREF BrushColor;

	 HDC DC;
/////////////////////////////////
	 To_Paint() { pattern_num = SOLID_FILL; 
           PenSize = 1; PenStyle = PS_SOLID; 
           color = RGB(0,0,0); 
	     BrushColor = RGB(0,0,0); }
/////////////////////////////////
	 int getx() 
		{ POINT p; ::GetCurrentPositionEx(DC, 
             &p); return p.x; }
	 int gety() 
		{ POINT p; ::GetCurrentPositionEx(DC, 
             &p); return p.y; }
	 COLORREF getcolorref() { return color; }
	 int getcolor(COLORREF col);
	 int getcolor() { return getcolor(color); }

	 void setcolor(int r, int g, int b)	
		{ color = RGB(r,g,b); }
	 void setcolor(int c)
	{ color = tricolors[c].colorref; }

	 void putpixel(int x, int y) 
		{ ::SetPixel(DC, x, y, getcolorref()); }
	 void setlinestyle(int width, int style)
		{ PenSize = width; PenStyle = style; }
	void setlinestyle(int style, unsigned int, 
            int width)
		{ PenSize = width; PenStyle = style; }

	void setfillstyle(int style, int col)
		{
		pattern_num = style;
		fill_color = col;
		BrushColor = tricolors[col].colorref;
		}
	void setfillstyle(int style, COLORREF col)
		{
		pattern_num = style;
		BrushColor = col;
		}

	void moveto(int x, int y) { ::MoveTo(DC, x, 
            y); }
    	void lineto(int x, int y);
    	void fillpoly(int numpoints, int* 
            polypoints);
    	void drawpoly(int numpoints, int far* 
            points);
    	};



File BGIPAINT.H

KH_Paint class could be considered as the main class in the library. 

	struct _export KH_Paint : public To_Paint, 
                     public Paint
	{
	KH_Paint() : To_Paint(), Paint() {}

      int getx();
      int gety();
	loc get_CP();
	void putpixel(int x, int y);
	void line(int xstart, int ystart, int xend, 
        int yend)
        { moveto(xstart, ystart); lineto(xend, 
        yend); }

	void lineto(int x, int y);
	void moveto(int x, int y);
	void circle(int x, int y, int radius)
        { ellipse(x, y, 0, 360, radius, radius);}
	void ellipse(int x, int y, int stangle, int 
         endangle, int xr, int yr);
	void rectangle(int left, int top, int right, 
         int bottom);
	void drawpoly(int numpoints, int far* 
         points);
	void fillpoly(int numpoints, int far* points)
	    { int f = fill; fill = ON; 
          drawpoly(numpoints,points); fill = f; }
	void bar3d(int l, int t, int r, int b, int d, 
          int top);
	virtual void outtext(char* str, int dir = 0);
	void set_fill(int state) { fill = state; }
    };



FILE PAINT.H

	Encapsulates axes transformation routines. Rotation could be 
simply processed if it is single operation. To use nested rotations we use 
stack of rotations info structures. 

	Example of complex rotation:
     	void f1(int x, int y, int alpha) 
		{ ... perform rotation ... }
     	void f2(int x, int y, int alpha) 
		{ rotate(10,10,90); f1(x, y, alpha); }
	Function f2() calls rotation procedure and then calls f1(), which calls 
another rotation. We could a) cancel first rotation and b) add first and 
second rotations to complex transformation.
	Library will handle the stack of rotations for you.

      Some additional facilities were added. Functions could set error code 
in kh_error_code variable.
	The zoom and addzoom are deformation coefficients. You could
	use zoom in any part of program. Addzoom is used only once to set 
the additional deformation of the whole picture (for example if context is 
changed from screen to printer), for preview and so on.

class _export Paint : public BGI_Font, public 
Trigonometry
    {
    public:
        loc zoom;       // Image deformation
        loc add_zoom;   // Additional deformation
        loc lt;      // Scroll of part of picture
        loc add_scroll;  // Left - top clip of 
                    // the whole picture
	int fill;         // Fill flag
	int mirror;	// Mirror reflection
    public:
	loc center;       // Rotation center
	int alpha;        // Rotation angle
	bool R_STACK;     // Use or not stack of 
				// rotations
	Stack* r_stack;   // Stack of rotations

	Paint();
	~Paint() { delete r_stack; }

	loc get_add_zoom() { return add_zoom; }
	loc get_zoom() { return zoom; }

// X coord. if no rotation
	void set_mirror(int m) { mirror = m; } 	
	void set_stack(bool r) { R_STACK = r; 
		r_stack->flash(); rotate(loc(0, 0), 0);}
	void set_stack_soft(bool r) { R_STACK = r; }
	void set_zoom(double x, double y) { zoom.X = 
              x * add_zoom.X; zoom.Y = y * 
              add_zoom.Y; }    // No out-of-range 
                               // control !!!
	void set_add_zoom(double x, double y) 
		{ add_zoom.X = 100 * x; add_zoom.Y = 100 
            * y; }
	void set_scroll(int x, int y) { lt.X = x; 
            lt.Y = y; }
	void set_add_scroll(int x, int y) 
          { add_scroll.X = x;
	    add_scroll.Y = y; }
	void rotate(loc c, int a);
	void rotate(int x, int y, int alpha) 
           { rotate(loc(x, y), alpha); }
	void set_fill(bool f) { fill = f; }

// Using alpha and center returns rotated 
// coordinates
	loc rot(int x, int y);
// Return completely transformed point: rotated, 
// zoomed and scrolled
	loc transform(int x, int y);
    };


4. Discussion

HOW TO DRAW (C++ drawing using extended set of graphical 
primitives).

Introduction.
    There are many problems requiring the use of vector graphics. It is 
usually compact, fast and easy to scale. It is good... But its advantages for 
the user very often become the headache (should I use 'pain in the head' 
instead?) for the programmer, who have to draw it using direct calls to 
graphical primitives. This headache could become a nightmare if the 
program requires rotations, reflections and scaling of the image. Of the 
complex one...
    The usual solution is object-oriented approach. Objects remember its 
coordinates, rotation angles and so on. But with this approach you need a 
lot of code and of data as well: containers, coordinates you will never use 
and more. For example, we can create class "Line" with "left", "top", "right" 
and "bottom" data members. If we need to draw 1000 parallel lines, most 
of this coordinates will be absolutely unnecessary.
    As the alternative you can add new facilities to the set of graphical 
primitives you use. Coordinate transformations become system - not user - 
supported.
    In this article I describe the shell for any vector graphics library "KNOW-
HOW.GRAPHICS. This shell is platform-independent (DOS / Windows), 
and written on C++. Using it, programmer still should call the low-level 
drawing functions like lineto(), moveto() and so on, but the system hides 
such details as rotation, scaling, reflection and scrolling.
*) Demo version of library is available on
http://home.istar.ca/~stepanv/Know_how.htm (Capital K in Know_how)
Tasks.

    As mentioned above, we need code to hide from user four coordinate 
transforming operations: reflection, scrolling, scaling and rotation. It should 
also be platform-independent.

Coordinates transformation.

Rotation.

    From the point of speed, the less effective is rotations, and especially 
nested rotations.
*) Saying "nested rotations" I mean group of rotations with different centers 
and different angles. This tool could be very useful. For example, to draw 
some image, requiring rotation, we use the f1() function (LISTING 1). Now 
we need to draw few figures, rotated to some (different) angles.
If we do not have support for nested rotations, we have to calc all 
coordinates manually. With this mechanism, the system can do it for us.

LISTING 1
*	f1(...)
*	   {
*	   for(int i = 0; i < 6; i++)
*	       {
*	       rotate(x, y, i );
*	       putchar(str[i];
*	       endrotate();    // See later
*	       }
*	   }

*	f2(...)
*	   {
*	   rotateON(); 
*	   for(int i = 0; i < 5; i++)
*	       {
*	       rotate(x, y, 360 / 5);
*	       f1(...);
*	       }
*	   rotateOFF();
*	   }


	The stack of rotations is activates with the rotateON() call. After 
this moment, all the rotate info will be kept in the stack. Drawing primitives 
will use it automatically.
    The code above could be optimized. If rotation center (x, y) is always 
the same, we need only 2 rotations (one in f1 and one in f2). To remove 
rotation info from stack we can use the following code (LISTING 2).

LISTING 2.
*	...
*	rotate(x, y, i * 360 / 5);
*	f1(...);
*	endrotate();

    The call to endrotate() is equal to stack pop() command. It removes the 
last stack info.

Trigonometry.

    To speed up drawing we can overload standard C trigonometric 
functions, like sin() and cos() which work with the argument of type double. 
Instead we shell use array of pre-calculated values of sinus with the step 1 
degree.
Additional acceleration could be achieved if instead of array of double 
values in the range -1 to 1, we'll use array of integer values of sin(x) * 
10000. This approach could dramatically, in some cases hundred times, 
speed up the calculations. Precision is enough for most tasks.

Reflections.
    Having the rotation support we do not need to support reflections with 
"any" mirror. Reflection with vertically oriented mirror in combination with 
the rotation could do the same job.


KNOW-HOW.GRAPHICS uses mirror(x) function to set the mirror.

Scrolling and scaling.

    This transformations are trivial. But it is desirable to give the user the 
opportunity to use it in any part of the drawing procedure, and in the same 
time to have possibility for scaling of all the image. This problem could be 
solved by keeping the information in two parameters for every of this 
procedures. For example:

*	loc zoom(100, 100)   // Keep local zoom info
*	loc add_zoom(1, 1)   // Keep additional zoom factor

We use the agreement that zoom() could be called at any time to set the 
local scaling coefficient, and add_zoom() - only once, at the beginning of 
drawing. The resulted scaling factor is the multiplication of zoom and 
add_zoom factors. You can think about the stack of scaling information 2 
elements depth, where first element is fixed.

*) The loc structure I refer to is one of the few, defined in the GEOM.H file 
of the library as

*	struct loc
*	   {
*	   int X, Y;
*	   ...

The ... here means constructors, assignment and comparison operators 
and more.

Platform-independent code.

    At the first - abstract - level of library we use virtual place holders for 
graphical primitives. They do nothing and should be overloaded.

LISTING 3.
*	class Abstract
*	   {
*	   virtual void lineto(...) {}


    The next level contains more sophisticated primitives, which does not 
require direct calls of platform-dependent drawing functions and could be 
implemented as combinations of calls for level1 functions. For example, 
rectangle(...) could be implemented as calls to moveto() / lineto() functions 
(LISTING 4).

LISTING 4.
*	class Abstract1 : publis Abstract
*	   {
*	   void rectangle(...);
*	...


    At the same level of abstraction we could add text output support.
KNOW-HOW.GRAPHICS supports BGI fonts. Borland have not published 
BGI fonts format, but it is well-known. To draw we need moveto() and 
lineto() calls only. We already have abstract prototypes for this functions, 
so it is not difficult to write cross-platform version with rotated and so on 
fonts (LISTING 5). In ver. 1.1 TRUE TYPE fonts should be available.

LISTING 5.
*	class KH_BGI_Fnt : public Abstract
*	   {
*	   putchar(...);
*	   outtextxy(...);
*	   gettextwidth(...);
*	...


*) Actually KNOW-HOW.GRAPHICS use a bit more complex approach. 
The BGI font header contains the isFilled byte, so the character drawing 
function check this flag and uses lineto / moveto if font is not filled and 
drawpoly / fillpoly otherwise.

    Next level in this hierarchy supports the coordinates transformation.
For example, for the line drawing function (LISTING 6).

LISTING 6.
*	Paint :: LineTo(loc p)
*	   {
*	   transform(&p);
*	   lineto(p);
*	   }

The transform() function perform the coordinate transformation described 
above.


Drawing of polygons.

    Both DOS BGI and Windows GDI includes polygon drawing functions. 
To be able to transform this figures we should use transform() call for 
every element of the array of coordinates (LISTING 8). (DOS is no longer 
supported)

LISTING 8.
*	loc p;
*	for(int i = 0; i < 2*n; i++)
*	   {
*	   transform(&p);
*	   array[i]  = p.X;
*	   array[i + 1] = p.Y;
*	   drawpoly(array, n);
*	....


    This code fragment belongs to one of abstract levels and call virtual 
drawpoly() function - place holder. In the derivated class it will be 
overloaded with ::drawpoly (BGI) or ::Polygon (GDI) depending on 
compiler directives.

Drawing of ellipses.

    If ellipse axes have the same direction as coordinate axes, the task is 
trivial. In the other case we can use call to drawpoly() or fillpoly() functions 
passing the array of 360 points (1 degree step). Using the "fast" 
trigonometry of our library make this procedure effective enough. The 
resolution is also sufficient for most tasks and could be increased if 
necessary.

Using KNOW-HOW.GDI in the applications.

    Like for every object-oriented system there are two possibilities. We can 
create object of the "drawing" class and use calls to its drawing functions.

LISTING 10.

*	KH_Paint* painter = new KH_Paint();
*	...
*	painter->lineto(loc_p);

	The second approach uses derivation. We derive our class from 
the "drawing" one and than may use its functions directly (LISTING 11).

LISTING 11.

*	class MyClass : public KH_Paint
*	...
*	MyClass::draw() { line(...); ... }


