Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A simple graphics library in C with BMP and WMF export

0.00/5 (No votes)
15 Jul 2011 1  
A graphics library to export graphical output to BMP or WMF.

Introduction

This article is intended to provide a simple C graphics library for the replacement of old BASIC graphic commands such as HPLOT and HCOLOR. This library does not provide on-screen graphics functions but has the ability to export graphical output to BMP or WMF. I hope that this library will be a help for programmers who want to make their own graphics library for simple physics simulations.

Background

I was studying physics simulations and found a wonderful book published in 1984. The author implemented all the simulation algorithms in Apple II BASIC and he used HPLOT and HCOLOR for graphical result. So there were some issues for further study. The old BASIC language is no longer used. C/C++ compilers such as Turbo-C can not be run without a DOSBox. I did not want to install Python or the GD library. What I wanted was a tiny and portable graphics library that could produce raster or vector graphical output for physics simulations.

Python and Python extension packages was a great solution but it did not meet my requirements. Neither did the GD library which supports just raster graphical outputs. Therefore I decided to make my own simple graphics library which would be able to export graphics results to BMP and WMF.

Using the Code

The following BASIC code implements a simple motion. This program plots the X (position) vs. T (time) in the case of a = dv/dt = -x.

'Simple motion with a = dv/dt = -x
 
 10  REM DY2
 15  REM X-T GRAPH OF DY1
 20  INPUT "SCALE OF T-AXIS ="; K
 30  INPUT "SCALE OF X-AXIS ="; L
 40  HGR : HCOLOR=3
 50  HPLOT 0, 40 TO 0, 140
 60  HPLOT 0, 90 TO 279, 90
 70  X0 = 1:V0 = 0:DT = 0.1
 80  X = X0:V = V0
 90  A = -X
 100 V = V + A * (DT/2)
 110 X = X + V * DT
 120 T = T + DT
 130 T1 = T * K:X1 = X * L
 140 IF T1 > 270 THEN END
 150 HPLOT T1, -X1 + 90
 160 A = -X
 170 V = V + A * DT
 180 GOTO 110
 
 ]RUN
 SCALE OF T-AXIS =5
 SCALE OF X-AXIS = 40

The following C code gives the same graphics result:

#include <stdio.h>
#include "libtg.h" 

int main() 
{ 
   /* declare variables for the equation of simple motion */ 
   float x,v,a, x0=1, v0=0, t= 0,dt=0.1,k=5,l=40,x1=0,t1=0;

   /* fwid: width of a frame in inch unit
    fhgt: height of a frame in inch unit
    xwid: pixel width
    xhgt: pixel  height*/
   float fwid = 0, fhgt = 3;
   int xwid=279, xhgt = 180;

   /* declare variable for plotting */
   TG_Color pixel_color = TG_GetColor(TG_BLACK);
   TG_PlotZone_Ptr zone=NULL;
   TG_Workspace_Ptr workspace = TG_InitTGLibrary("Letter");

   if(!workspace)  return 1;
   zone = &workspace->zone;

   /* synchronize the frame size with the pixel dimension.
   Set the max and min of the world coord system
   as the min and max of the pixel coordinate system. */

   fwid = fhgt*((float)xwid/xhgt);
   TG_SetFrame(&workspace->zone.frame, 0, 0, fwid, fhgt, 0, xwid,  0, xhgt);

   TG_BMP_OpenExport(zone, "sm.bmp", xwid, xhgt, 1);
   TG_BMP_SetReverseWorldCoord(1);

   x = x0;   
   v = v0;
   a = -x;
   v + = a*dt/2; 

   while(1) {
      x += v*dt;
      t += dt;
      t1 = t*k;
      x1 = x*l;
      if(t1 > 270) break;
      a = -x;
      v += a*dt;
      zone->plot.putpixel1(zone, t1, -x1+90, pixel_color);
   }
 
   TG_BMP_CloseExport();
   TG_CloseTGLibrary(workspace);
 
   return 0;
}

Coordinate System

The default coordinate system of LibTG is the world coordinate system whose origin is the lower left corner. However, export functions convert the world coordinate system into logical (paper) coord system for WMF or pixel coord system for BMP. The origin of BMP and WMF is the same, the upper left corner. The difference in the coordinate system between WMF and BMP is the unit. WMF uses inch unit, internally twip, and BMP uses pixel unit. You may change the origin of the WMF coordinate system by using SetWindowsOrg but I will not be going into that topic here.

Usually, old BASIC simulation code uses pixel coordinate system (e.g.: HPLOT T1, -X1 + 90). Therefore, XXXAPI_SetReverseWorldCoord(1) must be called before any plot function; XXX can be BMP or WMF. The first image represents the world coordinate system and the next one shows the paper or pixel coordinate system. There are two mapping processes in LibTG: world to logical and logical to pixel. BMP export requires both, and WMF just needs world to logical. The logical coordinate system is the alias of the paper coordinate system in inch unit. It will take a lot of time to explain the WMF format in detail. You can find the format spec on the Internet or check the WMF export part of LibTG.

//Example1: BMP export

TG_Point world, logical, pixel;
world.x = x, world.y = y;
World2Length(&&world, &&logical);
Length2Pixel(&&logical, &&pixel);
... putpixel(..., pixel.x, pixel.y, ...);

Example2: WMF export

TG_Point world, logical, pixel;
world.x = x, world.y = y;
World2Length(&&world, &&logical);
... putpixel(..., _to_twip(pixel.x), _to_twip(pixel.y), ...);

Now, let's export the graph to WMF. It is very simple. You just need to replace BMP with WMF and delete the last three arguments of OpenExport(..., xwid, xhgt, 1).

... 

TG_WMF_OpenExport(zone, "sm.wmf");
TG_WMF_SetReverseWorldCoord(1); 

...
TG_WMF_CloseExport(); 
TG_CloseTGLibrary(workspace);
 
return 0;

You can add a symbol by inserting just three lines. The functions gsave and grestore protect the properties of lines or pixels from the changed graphic status after drawing the symbols. gsave and grestore are valid only for WMF export. If you want just BMP, you don't need to use gsave and grestore.

...
zone->plot.putpixel1(zone, t1, -x1+90, pixel_color);

zone->plot.gsave();
zone->plot.symbol(zone, t1, -x1+90, TG_SYM_CIRCLE,.03, 
                  TG_GetColor(TG_RED),1 ,TG_GetColor(TG_BLACK),1,0.001);
zone->plot.grestore();

int skip=5, iskip=0;
TG_BMP_OpenExport(zone, "sms-skip.bmp", xwid, xhgt, 1); 
            ... 
zone->plot.putpixel1(zone, t1, -x1+90, pixel_color);

if((iskip%skip)) iskip++;
else {
    zone->plot.gsave();
    zone->plot.symbol(zone, t1, -x1+90, TG_SYM_CIRCLE,.03, 
            TG_GetColor(TG_RED),1 ,TG_GetColor(TG_BLACK),1,0.001);
    zone->plot.grestore();
    iskip = 1;
}

Here are some funny examples and I will post just the images. Check the source code to learn more about them. These images are plots of some potential functions in uniform flow. The topics are usually dealt at the beginning of fluid mechanics. The first is a dipole under uniform flow (BMP). The second is a vortex under uniform flow (BMP). The last shows the flow behaviors around a plate with an angle of attack (BMP).

I inserted BMP and WMF files into Microsoft PowerPoint:

I made a simple WMF analyzer which saves the meta commands in a WMF file as a text format. It is very useful and intuitive when I develop a WMF library from scratch. The program has three buttons: WMF, OK, and Cancel. If you click WMF, a file dialog box appears. Choose a WMF file and a text file with the same name and ".txt" extension will be created automatically, or click the OK button. Unfortunately, I lost the source code but have the executable program. I can't upload any executable program to CodeProject. If you need it, email me.

SMW.TXT
***************************************
*  Windows Meta File(16bit) Analyzer  *
*             2004. 2. 2.             *
*    written by Euisang Hwang         *
***************************************

Placeable Metafile Header
-------------------------------------
                    Hex    Dec       
-------------------------------------
DWORD MagicNumber : 0x9ac6cdd7 2596720087
WORD  Handle      : 000000     0
short Left        : 000000     0
short Top         : 000000     0
short Right       : 0x1711     5905
short Bottom      : 0x0ee2     3810
short Inch        : 0x04f6     1270
DWORD Reserved    : 0000000000 0
WORD  CheckSum    : 0x4a14     18964
-------------------------------------

Standard Metafile Header
-------------------------------------
                      Hex   Dex      
-------------------------------------
 WORD FileType      : 0x0001     1
 WORD HeaderSize    : 0x0009     9
 WORD Version       : 0x0300     768
DWORD FileSize      : 0x00008d07 36103
 WORD NumOfObjects  : 0x057c     1404
DWORD MaxRecordSize : 0x0000001e 30
 WORD NumOfParams   : 000000     0
--------------------------------------

Record size  : 5
API Function : 0x020b
API Prototype: DWORD SetWindowOrg (HDC,int,int)
Parameter 0  : 000000 0
Parameter 1  : 000000 0

Record size  : 5
API Function : 0x020c
API Prototype: DWORD SetWindowExt (HDC,int,int)
Parameter 0  : 0x0ee2 3810
Parameter 1  : 0x1711 5905

Record size  : 8
API Function : 0x02fa
API Prototype: HPEN CreatePenIndirect (LOGPEN FAR *)
Parameter 0  : PS_SOLID(0)
Parameter 1  : 1
Parameter 2  : 0
Parameter 3  :   0 (RED)
Parameter 4  :   0 (GREEN)
                 0 (BLUE)

..............................

Record size  : 3
API Function : 000000
API Prototype: (End) (End) (End)
---------------------
Total record size : 36085

Points of Interest

I could not find any library for creating WMF files. It was very fun for me to write pure C code for making a WMF export from scratch.

How to Compile

Open a development command line by clicking the \Start\Programs\Microsoft Visual Studio\Visual Studio Tools\2005 Visual Studio 2005 Command Prompt shortcut. Type a few words: cl xxx.c libtg.c. That's it!

Setting environment for using Microsoft Visual Studio 2005 x86 tools.

C:\Program Files (x86)\Microsoft Visual Studio 8\VC>d:

D:\>cd programming\libtg

D:\programming\libtg>cl s1.c libtg.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86

Copyright (C) Microsoft Corporation.  All rights reserved.

s1.c
libtg.c
Generating Code...
Microsoft (R) Incremental Linker Version 8.00.50727.762
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:s1.exe
s1.obj
libtg.obj

D:\programming\libtg>s1

D:\programming\libtg>dir sms-skip.bmp      ----> or sms-skip.wmf
 Volume in drive D is DATA
 Volume Serial Number is 7CF4-A7A1

 Directory of D:\programming\libtg

07/13/2011  07:48 PM           151,254 sms-skip.bmp
               1 File(s)        151,254 bytes
               0 Dir(s)  142,672,297,984 bytes free

D:\programming\libtg>

Or you can make a project in Visual Studio 2005/2010 or open Watcom C/C++. Ignore the many type conversion warnings due to the double to float conversions.

Bugs and Further Development

  1. I have not implemented a thick line algorithm for BMP export.
  2. Exception handling needs to be added for safe usage.
  3. Disk memory option for BMP export does not work.
  4. Text functions need to be added.
  5. Lots of bugs still exist.

Conclusion

Old physics simulation books used the BASIC language. For graphical output, programmers must select other ways. Installing Open Source software or Open Source libraries may be a good solution. However, fantastic graphic functions are not needed for simple physics simulations. Just a C/C++ compiler and simple functions are enough. LibTG will be a good alternative for satisfactory quality of raster graphics or vector graphics output.

History

First of all, forgive my English. English is not my mother tongue. However, I think that C/C++ or other programming languages are universal. This article is my first try at CodeProject. I hope that all the readers will enjoy my library.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here