Click here to Skip to main content
15,886,362 members
Articles / Programming Languages / C

A simple graphics library in C with BMP and WMF export

Rate me:
Please Sign up or sign in to vote.
4.46/5 (7 votes)
15 Jul 2011CPOL5 min read 32.8K   1.6K   14  
A graphics library to export graphical output to BMP or WMF.
/*------------------------------------------------------------------
	Copyright (c) Uisang Hwang 2011
	
	LibTG : Library for Technical Graph v0.01

	Tested on 
	Visual C++ 2005
	Visual C++ 2010
	Open Watcom C/C++ v1.9

	Permission to use, copy, modify, and distribute this software
	for any use is hereby granted provided
	this notice is kept intact within the source file

	Existing Bugs: 
		1. the disk memory option of Export BMP doesn't work
 ------------------------------------------------------------------*/
 
#include <stdlib.h>
#include <string.h> /*... memset()  ...*/
#include <stdio.h>  /*... fprintf() ...*/
#include <math.h>   /*... fabs()    ...*/
#include <ctype.h>  /*... toupper() ...*/

#include "libtg.h"

#ifdef __cplusplus
extern "C" {
#endif

#define InchToTwip		(1270.0f)
#define POINTS			72.0f
#define _pi				3.141592653589793238

#ifndef NULL
#define NULL 0
#endif

#define MAX_DEFAULT_COLOR 17
#define MAX_DEFAULT_SYMBOL 7
#define _to_twip(x)		((x)*InchToTwip)

/*-------------------------------------------------------------------*/

typedef struct TG_PlotAttribute_ /* plot attribute for a line */
{
	TG_Real thk; /* Line thickness */
	ulong cur_pen_color, prv_pen_color;
}
	TG_PlotAttr,
	*TG_PlotAttr_Ptr;

/*-------------------------------------------------------------------*/

static const int sym_circle_point 		= 12;
static const int sym_delta_point 		= 3;
static const int sym_gradient_point 	= 3;
static const int sym_rtriangle_point 	= 3;
static const int sym_ltriangle_point 	= 3;
static const int sym_diamond_point 		= 4;
static const int sym_square_point 		= 4;

static TG_Real sym_circlex[13];
static TG_Real sym_circley[13];
static TG_Real sym_deltax[4];
static TG_Real sym_deltay[4];
static TG_Real sym_gradientx[4];
static TG_Real sym_gradienty[4];
static TG_Real sym_rtrianglex[4];
static TG_Real sym_rtriangley[4];
static TG_Real sym_ltrianglex[4];
static TG_Real sym_ltriangley[4];
static TG_Real sym_diamondx[5];
static TG_Real sym_diamondy[5];
static TG_Real sym_squarex[5];
static TG_Real sym_squarey[5];

static void TG_ComputeCircleSymbol(TG_Real size, TG_Real width, TG_Real lx, TG_Real ly);
static void TG_ComputeDeltaSymbol(TG_Real size, TG_Real width, TG_Real lx, TG_Real ly); 
static void TG_ComputeGradientSymbol(TG_Real size, TG_Real width, TG_Real lx, TG_Real ly); 
static void TG_ComputeRTriangleSymbol(TG_Real size, TG_Real width, TG_Real lx, TG_Real ly); 
static void TG_ComputeLTriangleSymbol(TG_Real size, TG_Real width, TG_Real lx, TG_Real ly); 
static void TG_ComputeDiamondSymbol(TG_Real size, TG_Real width, TG_Real lx, TG_Real ly); 
static void TG_ComputeSquareSymbol(TG_Real size, TG_Real width, TG_Real lx, TG_Real ly);

typedef void(*TG_ComputeSymbol)(TG_Real,TG_Real,TG_Real,TG_Real);

typedef struct TG_SymbolPool_
{
	int npoint;
	TG_Real line_thk;
	TG_Real* pointx;
	TG_Real* pointy;
	TG_ComputeSymbol TG_ComputeSymbolCoordinate;
}
	*TG_SymbolPool_Ptr, 
	TG_SymbolPool;
	
TG_SymbolPool symbol_pool[] = 
{
	{ 12, 0.01f, sym_circlex   , sym_circley   , TG_ComputeCircleSymbol    },
	{  3, 0.01f, sym_deltax    , sym_deltay    , TG_ComputeDeltaSymbol     },
	{  3, 0.01f, sym_gradientx , sym_gradienty , TG_ComputeGradientSymbol  },
	{  3, 0.01f, sym_rtrianglex, sym_rtriangley, TG_ComputeRTriangleSymbol },
	{  3, 0.01f, sym_ltrianglex, sym_ltriangley, TG_ComputeLTriangleSymbol },
	{  4, 0.01f, sym_diamondx  , sym_diamondy  , TG_ComputeDiamondSymbol   },
	{  4, 0.01f, sym_squarex   , sym_squarey   , TG_ComputeSquareSymbol    },
	/*... User defined symbol ...*/
	{  0, 0.01f, NULL, NULL, NULL },
	{  0, 0.01f, NULL, NULL, NULL } 
};

TG_PlotAttr plot_attr;

/*-------------------------------------------------------------------
 * W2L : pointer to function for different coordinate system
 *       the default coordinate system is World whose origin is 
 *       lower left corner. However in pixel coord system the origin
 *       is upper left corner. 
 *       If you call XXXAPI_ReverseWorld2Length(1), the corner will
 *       be upper left corner. XXX is BMP or WMF.
 *-------------------------------------------------------------------*/
 
typedef void(*W2L)(TG_PlotZone_Ptr,TG_Point_Ptr,TG_Point_Ptr); 
typedef void(*TG_PrintError)(TG_Error);

static TG_PrintError TG_ErrorWriter = NULL;
static char TG_DefaultPaperName[40]="LETTER";

TG_Status TG_GetPaperFormat(const char *paper_name, TG_Paper_Ptr paper);
void TG_DrawLine(TG_PlotZone_Ptr p, int x1, int y1, int x2, int y2, ulong color);

/*-------------------------------------------------------------------*/

void TG_GetSymbolCoordinate(TG_Symbol sym_index, TG_Real x, TG_Real y, TG_Real lx, TG_Real ly)
{
	if(sym_index < 0 || sym_index > (MAX_DEFAULT_SYMBOL-1)) return;
	
	symbol_pool[sym_index].TG_ComputeSymbolCoordinate(x,y,lx,ly);
}

void TG_InitPlotAttr(TG_PlotAttr_Ptr p)
{
	plot_attr.cur_pen_color = TG_GetColor(TG_BLACK);
	plot_attr.prv_pen_color = plot_attr.cur_pen_color;
}

void TG_GetRGB(ulong c, int* r, int* g, int* b)
{
    *r = c>>16;
    *g = (c>>8)&0x00ff;
    *b = c&0x0000ff;
}

ulong TG_GetCustomColor(int r, int g, int b)
{
	return (((((ulong)r)<<16)|(((ulong)g)<<8))|(ulong)b);
}

ushort TG_ReverseUShort(ushort s) 
{	
	return ((s>>8)|(s<<8));
}

ulong TG_GetColor(int c)
{
	static ulong color_table[] = {
		/* dark colors */
		0x000000, /* BLACK */
		0xff0000, /* RED */
		0x00ff00, /* GREEN */
		0x0000ff, /* BLUE */
		0xffff00, /* YELLOW */
		0x00ffff, /* CYAN */
		0xff00ff, /* MAGENTA */
		0xff8811, /* BROWN */
		0x800080, /* PURPLE */
		/* light colors */
		0xc0c0c0, /* LIGHTGRAY*/
		0x808080, /* DARKGRAY */
		0x00ccff, /* LIGHTBLUE */
		0x00dd33, /* LIGHTGREEN */
		0x00ffee, /* LIGHTCYAN */
		0xff2200, /* LIGHTRED */
		0xff0022, /* LIGHTMAGENTA */
		0xffffff, /* WHITE */
	};

	if( c < 0 || c >= MAX_DEFAULT_COLOR)
	{
		fprintf(stderr, "Error: Invalid color index at TG_GetColor(%d)\n"
		"       BLACK color will be returned\n", c);
		c = TG_BLACK;
	}
	
	return color_table[c];
}

static void TG_DefaultErrorWriter(TG_Error error_number)
{
	/*... under development ...*/
}

TG_Workspace_Ptr TG_InitTGLibrary(const char* paper_name)
{
	TG_Status status;
	TG_Workspace_Ptr workspace = (TG_Workspace_Ptr)calloc(sizeof(TG_Workspace), sizeof(char));
	
	if(!workspace) return NULL;	
	memset(workspace, 0x00, sizeof(TG_Workspace));	

	TG_ErrorWriter = TG_DefaultErrorWriter;
	status = TG_GetPaperFormat(paper_name, &workspace->paper);
	
	if(status == TG_Fail)
	{
		fprintf(stderr, "%s will be used for invalid paper", TG_DefaultPaperName);
		TG_GetPaperFormat(TG_DefaultPaperName, &workspace->paper);
	}
	
	return workspace;
}

void TG_CloseTGLibrary(TG_Workspace_Ptr workspace)
{
	if(workspace) { free(workspace); workspace = NULL; }
}

void TG_SetFrame(TG_Frame_Ptr f, TG_Real sx, TG_Real sy, TG_Real width, TG_Real height, 
	TG_Real minx, TG_Real maxx, TG_Real miny, TG_Real maxy)
{
	f->frame_size.sx = sx;
	f->frame_size.sy = sy;
	f->frame_size.width = width;
	f->frame_size.height = height;	
	
	f->minx = minx;
	f->maxx = maxx;
	f->miny = miny;
	f->maxy = maxy;
	
	f->fxl = width / (TG_Real)fabs(maxx-minx);
	f->fyl = height / (TG_Real)fabs(maxy-miny);
	f->fx = (f->fxl>f->fyl?f->fyl:f->fxl);
}

void TG_ToUpper(char *s)
{
	while(*s) *s++ = toupper(*s);
}

void TG_FindPaperSeries(const char *paper_name, int *start)
{
	int i, len = strlen(paper_name);
	
	*start = -1;
	
	for(i = 0; i < len; i++)
	{
		if(paper_name[i] == 'A') { *start = 0; break; }
		else if(paper_name[i] == 'B') { *start = 13; break; }
		else if(paper_name[i] == 'C') { *start = 24; break; }
		else if(paper_name[i] == 'L') { *start = 35; break; }
	}
}

TG_Error TG_GetPaperFormat(const char *paper_name, TG_Paper_Ptr paper)
{
	/* Total number of paper types: 13+11*2+1 = 36 */
	static const int total_paper_format = 36;
	
	static TG_Paper paper_library[] = 
	{
		/* A Series Format: 13 */
		{ "2A0", 46.75f , 66.25f , 1189.0f, 682.0f  },  /* 2A0 46 3/4 �� 66 1/4  1189 ��1682 */
		{ "A0" , 33.0f  , 46.75f , 841.0f , 1189.0f },  /* A0  33     �� 46 3/4  841 �� 1189 */   
		{ "A1" , 23.375f, 33.0f  , 594.0f , 841.0f  },  /* A1  23 3/8 �� 33      594 �� 841  */  
		{ "4A0", 66.25f , 93.625f, 1682.0f, 378.0f  },  /* 4A0 66 1/4 �� 93 5/8  1682 ��2378 */
		{ "A2" , 16.5f  , 23.375f, 420.0f , 594.0f  },  /* A2  16 1/2 �� 23 3/8  420 �� 594  */
		{ "A3" , 11.75f , 16.5f  , 297.0f , 420.0f  },  /* A3  11 3/4 �� 16 1/2  297 �� 420  */
		{ "A4" , 8.25f  , 11.75f , 210.0f , 297.0f  },  /* A4  8 1/4  �� 11 3/4  210 �� 297  */
		{ "A5" , 5.875f , 8.25f  , 148.0f , 210.0f  },  /* A5  5 7/8  �� 8 1/4   148 �� 210  */
		{ "A6" , 4.125f , 5.875f , 105.0f , 148.0f  },  /* A6  4 1/8  �� 5 7/8   105 �� 148  */
		{ "A7" , 2.875f , 4.125f , 74.0f  , 105.0f  },  /* A7  2 7/8  �� 4 1/8   74  �� 105  */
		{ "A8" , 2.0f   , 2.875f , 52.0f  , 74.0f   },  /* A8  2      �� 2 7/8   52  �� 74   */
		{ "A9" , 1.5f   , 2.0f   , 37.0f  , 52.0f   },  /* A9  1 1/2  �� 2       37  �� 52   */
		{ "A10", 1.0f   , 1.5f   , 26.0f  , 37.0f   },  /* A10 1      �� 1 1/2   26  �� 37   */
		
		/* B Series Format: 11 */
		
		{ "B0" , 39.375f , 55.75f  , 1000.0f, 1414.0f},  /* B0  39 3/8 �� 55 3/4  1000 �� 1414*/
		{ "B1" , 27.75f  , 39.375f , 707.0f , 1000.0f},  /* B1  27 3/4 �� 39 3/8  707  �� 1000*/
		{ "B2" , 19.75f  , 27.75f  , 500.0f , 707.0f },  /* B2  19 3/4 �� 27 3/4  500  �� 707 */
		{ "B3" , 13.875f , 19.75f  , 353.0f , 500.0f },  /* B3  13 7/8 �� 19 3/4  353  �� 500 */
		{ "B4" , 9.875f  , 13.875f , 250.0f , 353.0f },  /* B4  9 7/8  �� 13 7/8  250  �� 353 */
		{ "B5" , 7.0f    , 9.875f  , 176.0f , 250.0f },  /* B5  7      �� 9 7/8   176  �� 250 */
		{ "B6" , 4.875f  , 7.0f    , 125.0f , 176.0f },  /* B6  4 7/8  �� 7       125  �� 176 */
		{ "B7" , 3.5f    , 4.875f  , 88.0f  , 125.0f },  /* B7  3 1/2  �� 4 7/8   88   �� 125 */
		{ "B8" , 2.5f    , 3.5f    , 62.0f  , 88.0f  },  /* B8  2 1/2  �� 3 1/2   62   �� 88  */
		{ "B9" , 1.75f   , 2.5f    , 44.0f  , 62.0f  },  /* B9  1 3/4  �� 2 1/2   44   �� 62  */
		{ "B10", 1.25f   , 1.75f   , 31.0f  , 44.0f  },  /* B10 1 1/4  �� 1 3/4   31   �� 44  */
		
		/* C Series Formats: 11 */

		{ "C0" , 36.0f  , 51.0f  , 917.0f, 1297.0f},  /* C0 36     �� 51      917 �� 1297  */
		{ "C1" , 25.5f  , 36.0f  , 648.0f, 917.0f },  /* C1 25 1/2 �� 36      648 �� 917   */
		{ "C2" , 18     , 25.5f  , 458.0f, 648.0f },  /* C2 18     �� 25 1/2  458 �� 648   */
		{ "C3" , 12.75f , 18.0f  , 324.0f, 458.0f },  /* C3 12 3/4 �� 18      324 �� 458   */
		{ "C4" , 9      , 12.75f , 229.0f, 324.0f },  /* C4 9      �� 12 3/4  229 �� 324   */
		{ "C5" , 6.375f , 9.0f   , 162.0f, 229.0f },  /* C5 6 3/8  �� 9       162 �� 229   */
		{ "C6" , 4.5f   , 6.375f , 114.0f, 162.0f },  /* C6 4 1/2  �� 6 3/8   114 �� 162   */
		{ "C7" , 3.1875f, 4.5f   , 81.0f , 114.0f },  /* C7 3 3/16 �� 4 1/2   81  �� 114   */
		{ "C8" , 2.25f  , 3.1875f, 57.0f , 81.0f  },  /* C8 2 1/4  �� 3 3/16  57  �� 81    */
		{ "C9" , 1.625f , 2.25f  , 40.0f , 57.0f  },  /* C9 1 5/8  �� 2 1/4   40  �� 57    */
		{ "C10", 1.125  , 1.625f , 28.0f , 40.0f  },  /* C10 1 1/8 �� 1 5/8   28 �� 40     */
    
		/* US: Letter */
	    { "LETTER" , 8.5f, 11.0f, 215.8915f, 279.389f}/* Letter 11 ��8.5 */
	};
	
	int i, start=-1;
	char name_buffer[80];
	
	strcpy(name_buffer, paper_name);
	TG_ToUpper(name_buffer);
	TG_FindPaperSeries(name_buffer, &start);
	
	if(start < 0) start = total_paper_format-1; /* set Letter as default paper */

	for(i = start; i < total_paper_format; i++)
	{
		if(strcmp(paper_library[i].series_name, paper_name) ==0)
		{
			strcpy(paper->series_name, paper_library[i].series_name);
			paper->width_inch = paper_library[i].width_inch; 
			paper->height_inch = paper_library[i].height_inch;
			paper->width_mm = paper_library[i].width_mm; 
			paper->height_mm = paper_library[i].height_mm;
			return TG_Success;
		}
	}
	
	return TG_ERROR_INVALID_PAPERNAME; // no appropriate paper exist
}

#ifdef __EXPORT_BMP__

/*
	Bitmap file format
	http://en.wikipedia.org/wiki/BMP_file_format
*/

/*... BITMAP file header ...*/

#ifdef __TURBOC__
	typedef ulong uint32_t;
	typedef ushort uint16_t;
#else
	typedef unsigned int uint32_t;
	typedef unsigned short uint16_t;
#endif

/*... Bytes per pixel ...*/
static const int BYTEPP_V3 = 3;
static const int STD_V3BMP_HEADER_SIZE = 54;
	
typedef struct bmpfile_magic_ {
  unsigned char magic[2];
} bmpfile_magic;

typedef struct bmpfile_header_
{
  uint32_t filesz;
  uint16_t creator1;
  uint16_t creator2;
  uint32_t bmp_offset;
} bmpfile_header;

typedef struct BitmapFileHeader_
{
	bmpfile_magic magicNumber;
	bmpfile_header fileHeader;
}
	BitmapFileHeader, *BitmapFileHeader_Ptr;

/*... BITMAP info header ...*/
typedef struct BitmapInfoHeader_
{
  uint32_t header_sz;
  uint32_t width;
  uint32_t height;
  uint16_t nplanes;
  uint16_t bitspp;
  uint32_t compress_type;
  uint32_t bmp_bytesz;
  uint32_t hres;
  uint32_t vres;
  uint32_t ncolors;
  uint32_t nimpcolors;
} 
	bmp_dib_v3_header_t, BitmapInfoHeader, *BitmapInfoHeader_Ptr;

typedef struct BitmapDevice_
{
	BitmapFileHeader file;
	BitmapInfoHeader info;
	FILE* writer;
	int scanline_length;
	int prv_x, prv_y;
	int mem_disk, mem_disk_fail;
	int prv_xpos, prv_ypos;
	TG_Real scale_ppi;
	ubyte *raw_data;
	W2L world2length;	
}
	BitmapDevice, *BitmapDevice_Ptr;

BitmapDevice bmp_device;

void BMPAPI_World2Length(TG_PlotZone_Ptr p, TG_Point_Ptr w, TG_Point_Ptr l) 
{
	l->x = (w->x-p->frame.minx) * p->frame.fx;
	l->y = p->frame.frame_size.height-(w->y-p->frame.miny) * p->frame.fx;
}

void BMPAPI_ReverseWorld2Length(TG_PlotZone_Ptr p, TG_Point_Ptr w, TG_Point_Ptr l) 
{
	l->x = (w->x-p->frame.minx) * p->frame.fx;
	l->y = (w->y-p->frame.miny) * p->frame.fx;
}

void TG_BMP_SetReverseWorldCoord(int coord)
{
	if(coord==0) bmp_device.world2length = BMPAPI_World2Length;
	else bmp_device.world2length = BMPAPI_ReverseWorld2Length;
}

void BMPAPI_Length2Pixel(TG_PlotZone_Ptr p, TG_Point_Ptr l, TG_Point_Ptr x) 
{
	x->x = (l->x) * bmp_device.scale_ppi;
	x->y = (l->y) * bmp_device.scale_ppi;
}

void BMPAPI_SetPixel(int x, int y, TG_Color c)
{
	int r, g, b; 
	ulong mem_size=0;
	ubyte* mem_ptr=NULL;
	TG_GetRGB(c, &r, &g, &b);
    
    if(x < 0 || x >= bmp_device.info.width || 
    	y < 0 || y >= bmp_device.info.height) 
    {
		/*... the error message make a program slow ...*/
		/*fprintf(stderr, "Error: x, y exceeds image size at BMPAPI_SetPixel(%d,%d)\n", x,y);*/
    	return;
    }
    	
	if(bmp_device.mem_disk && !bmp_device.mem_disk_fail) 
	{
		mem_size = bmp_device.scanline_length*
		(bmp_device.info.height-(y+1))+x*3;
		mem_ptr = &bmp_device.raw_data[mem_size];
		*mem_ptr++ = b;
		*mem_ptr++ = g;
		*mem_ptr   = r;
	}
	else {
		mem_size = STD_V3BMP_HEADER_SIZE+bmp_device.scanline_length*
		(bmp_device.info.height-(y+1))+x*3;
		fseek(bmp_device.writer, mem_size, SEEK_SET);
		fputc(b, bmp_device.writer);
		fputc(g, bmp_device.writer);
		fputc(r, bmp_device.writer);
	}
	
	return;
}

void BMPAPI_SetPixel1(TG_PlotZone_Ptr p, TG_Real x, TG_Real y, TG_Color c)
{
	/* 	wc: World Coordinate
		lc: logical Coordinate
		pc: physical Coordinate */
	TG_Point wc, lc, pc;

	wc.x = x, wc.y = y;
	bmp_device.world2length(p, &wc, &lc);
	BMPAPI_Length2Pixel(p, &lc, &pc);
	BMPAPI_SetPixel(pc.x, pc.y, c);	
}

void BMPAPI_SetPixel2(TG_PlotZone_Ptr p, int x, int y, TG_Color c)
{
	BMPAPI_SetPixel(x, y, c);
}

void BMPAPI_MakePen(TG_PlotZone_Ptr p, TG_Color c, TG_Real thk)
{
	plot_attr.prv_pen_color = plot_attr.cur_pen_color;
	plot_attr.cur_pen_color = c;
}

void BMPAPI_DeletePen(void)
{
	plot_attr.cur_pen_color = plot_attr.prv_pen_color;
	plot_attr.thk = 0;
}

void BMPAPI_MoveTo(TG_PlotZone_Ptr p, TG_Real x, TG_Real y)
{
	/* 	wc: World Coordinate
		lc: logical Coordinate
		pc: physical Coordinate */
	TG_Point wc, lc, pc;

	wc.x = x, wc.y = y;
	bmp_device.world2length(p, &wc, &lc);
	BMPAPI_Length2Pixel(p, &lc, &pc);
	bmp_device.prv_xpos = (int)pc.x;
	bmp_device.prv_ypos = (int)pc.y;
}

void BMPAPI_LineTo(TG_PlotZone_Ptr p, TG_Real x, TG_Real y)
{
	/* 	wc: World Coordinate
		lc: logical Coordinate
		pc: physical Coordinate */
	TG_Point wc, lc, pc;

	wc.x = x, wc.y = y;
	bmp_device.world2length(p, &wc, &lc);
	BMPAPI_Length2Pixel(p, &lc, &pc);
	TG_DrawLine(p, bmp_device.prv_xpos, bmp_device.prv_ypos, 
	pc.x, pc.y, plot_attr.cur_pen_color);
	bmp_device.prv_xpos = (int)pc.x;
	bmp_device.prv_ypos = (int)pc.y;
}

void BMPAPI_Line(TG_PlotZone_Ptr p, TG_Real x1, TG_Real y1, TG_Real x2, TG_Real y2, TG_Color color, TG_Real thk)
{
	BMPAPI_MakePen(p, color, thk);
	BMPAPI_MoveTo(p, x1, y1);
	BMPAPI_LineTo(p, x2, y2);
	BMPAPI_DeletePen();
}

TG_Color BMPAPI_GetPixel(x,y)
{
	ulong mem_size=0;
	ubyte* mem_ptr=NULL;
	int r,g,b;
	int width = bmp_device.info.width;
	int height = bmp_device.info.height;
	
    if(x < 0 || x >= width || y < 0 || y >= height) 
    {
		/*... the error message make a program slow ...*/
		/*fprintf(stderr, "Error: x, y exceeds image size at BMPAPI_GetPixel(%d,%d)\n", x,y);*/
    }

	if(x < 0) x = 0;
	if(x >= width) x = width-2;
	if(y < 0) y = 0;
	if(y >= height) y = height-2;
    	
	if(bmp_device.mem_disk && !bmp_device.mem_disk_fail) 
	{
		mem_size = bmp_device.scanline_length*
		(bmp_device.info.height-(y+1))+x*3;
		mem_ptr = &bmp_device.raw_data[mem_size];
		b = *mem_ptr++;
		g = *mem_ptr++;
		r = *mem_ptr;
	}
	else {
		mem_size = STD_V3BMP_HEADER_SIZE+bmp_device.scanline_length*
		(bmp_device.info.height-(y+1))+x*3;
		fseek(bmp_device.writer, mem_size, SEEK_SET);
		b = fgetc(bmp_device.writer);
		g = fgetc(bmp_device.writer);
		r = fgetc(bmp_device.writer);
	}
	
	return TG_GetCustomColor(r,g,b);
}

void BMPAPI_DrawHorizLine(TG_PlotZone_Ptr p, int x1, int x2, int y, TG_Color c)
{
	while(x1 <= x2) BMPAPI_SetPixel2(p, x1++, y, c);
}

/* Learning Algorithms in C, Jaekyu Lee 1996 published in South Korea */

int BMPAPI_ScanLeft(int x, int y, TG_Color fc, TG_Color bc)
{
	TG_Color v;

	v = BMPAPI_GetPixel(x,y);
	while( v != fc && v != bc && x >= 0)
		v = BMPAPI_GetPixel(--x, y);
	x++;

	return x;
}

int BMPAPI_ScanRight(int x, int y,TG_Color fc, TG_Color bc)
{
	TG_Color v;

	v = BMPAPI_GetPixel(x,y);
	while( v != fc && v != bc && x < bmp_device.info.width)
		v = BMPAPI_GetPixel(++x, y);
	x--;

	return x;
}

#define NOT_DRAWN	0
#define BELOW_DRAWN 1
#define UPPER_DRAWN	2

int BMPAPI_LineFill(TG_PlotZone_Ptr p, int sx, int sy, TG_Color c, TG_Color bc, int pl, int pr, int flag)
{
	int x, y;
	TG_Color v;
	int xleft, xright;

	xleft = xright = sx;
	y = sy;

	if(sx < 0) return 0;
	if(sx >= bmp_device.info.width) return bmp_device.info.width-1;

	xleft = BMPAPI_ScanLeft(sx, sy, c, bc);
	xright = BMPAPI_ScanRight(sx, sy, c, bc);

	BMPAPI_DrawHorizLine(p, xleft, xright, y, c);

	if(flag == UPPER_DRAWN)
	{
		for(x = xleft; x < pl; x++)
		{
			v = BMPAPI_GetPixel(x, y-1);
			if(v != c && v != bc)
				x = BMPAPI_LineFill(p, x, y-1, c, bc, xleft, xright, BELOW_DRAWN);
		}

		for(x = pr+1; x <= xright; x++)
		{
			v = BMPAPI_GetPixel(x, y-1);
			if(v != c && v != bc)
				x = BMPAPI_LineFill(p, x, y-1, c, bc, xleft, xright, BELOW_DRAWN);
		}
	}
	else
	{
		for(x = xleft; x <= xright; x++)
		{
			v = BMPAPI_GetPixel(x, y-1);
			if(v != c && v != bc)
				x = BMPAPI_LineFill(p, x, y-1, c, bc, xleft, xright, BELOW_DRAWN);
		}
	}

	if(flag == BELOW_DRAWN)
	{
		for(x = xleft; x < pl; x++)
		{
			v = BMPAPI_GetPixel(x, y+1);
			if(v != c && v != bc)
				x = BMPAPI_LineFill(p, x, y+1, c, bc, xleft, xright, UPPER_DRAWN);
		}

		for(x = pr+1; x <= xright; x++)
		{
			v = BMPAPI_GetPixel(x, y+1);
			if(v != c && v != bc)
				x = BMPAPI_LineFill(p, x, y+1, c, bc, xleft, xright, UPPER_DRAWN);
		}
	}
	else
	{
		for(x = xleft; x <= xright; x++)
		{
			v = BMPAPI_GetPixel(x, y+1);
			if(v != c && v != bc)
				x = BMPAPI_LineFill(p, x, y+1, c, bc, xleft, xright, UPPER_DRAWN);
		}
	}

	return xright;
}

/*... End of Learning Algorithms in C ...*/

/* lc : outline color */

void BMPAPI_Polyline(TG_PlotZone_Ptr p, TG_Real* x, TG_Real* y, int npoint, TG_Color lc, TG_Real lthk)
{
	/* 	wc: World Coordinate
		gc: logical Coordinate
		pc: physical Coordinate */
	TG_Point wc, gc, pc;

	int i=0;
	int* xx = (int*)malloc(sizeof(int)*npoint);
	int* yy = (int*)malloc(sizeof(int)*npoint);
	
	if(!xx || !yy) {
		if(xx) free(xx);
		if(yy) free(yy);
		
		fprintf(stderr, "Error: not enough memory at BMPAPI_Polygon\n");
		return;
	}
	
	for(i = 0; i < npoint; i++)
	{
		wc.x = x[i], wc.y = y[i];
		bmp_device.world2length(p, &wc, &gc);
		BMPAPI_Length2Pixel(p, &gc, &pc);	
		xx[i] = pc.x;
		yy[i] = pc.y;
	}
	
	xx[i] = xx[0];
	yy[i] = yy[0];
	
	for(i = 0; i < npoint; i++)
	{
		TG_DrawLine(p, xx[i], yy[i], xx[i+1], yy[i+1], lc);
	}
}

void BMPAPI_Polygon(TG_PlotZone_Ptr p, TG_Real* x, TG_Real* y, int npoint, TG_Color fc)
{
	/* 	wc: World Coordinate
		lc: logical Coordinate
		pc: physical Coordinate */
	TG_Point wc, lc, pc;
	TG_Real px,py;
	int i = 0;
	int* xx = (int*)malloc(sizeof(int)*(npoint+1));
	int* yy = (int*)malloc(sizeof(int)*(npoint+1));
	
	if(!xx || !yy) {
		if(xx) free(xx);
		if(yy) free(yy);
		
		fprintf(stderr, "Error: not enough memory at BMPAPI_Polygon\n");
		return;
	}
	
	px = py = 0;

	for(i = 0; i < npoint; i++)
	{
		wc.x = x[i], wc.y = y[i];
		bmp_device.world2length(p, &wc, &lc);
		BMPAPI_Length2Pixel(p, &lc, &pc);	
		xx[i] = pc.x;
		yy[i] = pc.y;

		px += pc.x;
		py += pc.y;
	}
	
	xx[i] = xx[0];
	yy[i] = yy[0];
    
	i=0;
	while(i < npoint) { TG_DrawLine(p, xx[i], yy[i], xx[i+1], yy[i+1], fc); i++;}
    
	/*... find the cetroid of the polygon ...*/
	px /= npoint;
	py /= npoint;

	BMPAPI_LineFill(p, px, py, fc, fc, 0,0,0);
}

void BMPAPI_Symbol(TG_PlotZone_Ptr p, TG_Real x, TG_Real y, TG_Symbol sym, TG_Real size, TG_Color fc, int fill, TG_Color lc, int line, TG_Real lthk)
{
	/* 	wc: World Coordinate
		gc: logical Coordinate
		pc: physical Coordinate */
	TG_Point wc, gc, pc;
	int x1, x2, y1, y2, px, py, i;
	int sym_size = (int)(size*p->frame.frame_size.height/p->frame.fx);

	if(sym < 0 || sym > (MAX_DEFAULT_SYMBOL-1)) return;

	/*... No symbol drawing ...*/
	if(fill == 0 && line == 0) return;
	
	TG_GetSymbolCoordinate(sym, sym_size, 1, x, y);
	BMPAPI_Polygon (p, symbol_pool[sym].pointx, symbol_pool[sym].pointy, symbol_pool[sym].npoint, TG_GetCustomColor(0x99, 0xcc, 0xcc));

	if(fill == 1) 
	{
		for(i = 0; i < symbol_pool[sym].npoint; i++)
		{
			wc.x = symbol_pool[sym].pointx[i];
			wc.y = symbol_pool[sym].pointy[i];
			bmp_device.world2length(p, &wc, &gc);
			BMPAPI_Length2Pixel(p, &gc, &pc);	
			x1 = pc.x;
			y1 = pc.y;

			wc.x = symbol_pool[sym].pointx[i+1];
			wc.y = symbol_pool[sym].pointy[i+1];
			bmp_device.world2length(p, &wc, &gc);
			BMPAPI_Length2Pixel(p, &gc, &pc);	
			x2 = pc.x;
			y2 = pc.y;

			TG_DrawLine(p, x1, y1, x2, y2, lc);
		}

		wc.x = x, wc.y = y;
		bmp_device.world2length(p, &wc, &gc);
		BMPAPI_Length2Pixel(p, &gc, &pc);
		px = pc.x;
		py = pc.y;

		BMPAPI_LineFill(p, px, py, fc, lc, 0,0,0);
	}
}

void BMPAPI_gSave(void)
{
}

void BMPAPI_gRestore(void)
{
}

int TG_BMP_OpenExport(TG_PlotZone* p, const char* file, int wid, int hgt, int memory)
{
	int BPP = 24;
	int raw_data_size = (int)(((BPP*wid)/32.0)*4);
	int remainder = raw_data_size%4;
	int padding_size = remainder==0?0:4-remainder;
	TG_Real ppix=0, ppiy=0;
	int scan_line_size = raw_data_size+padding_size;
	int raw_data_length = scan_line_size*hgt;
    BitmapFileHeader_Ptr bfh = &bmp_device.file;
    BitmapInfoHeader_Ptr bih = &bmp_device.info;
    FILE* writer = NULL;

    bmp_device.scanline_length = scan_line_size;
    bmp_device.writer = fopen(file, "wb");
    
    if(!bmp_device.writer) {
        fprintf(stderr, "Error: can't open %s at TG_BMP_OpenExport", file);
        return 0;
    }
    writer = bmp_device.writer;
        
	bfh->magicNumber.magic[0] = 'B';
	bfh->magicNumber.magic[1] = 'M';
	bfh->fileHeader.filesz = STD_V3BMP_HEADER_SIZE+raw_data_length;
	bfh->fileHeader.creator1 = 0;
	bfh->fileHeader.creator2 = 0;
	bfh->fileHeader.bmp_offset = STD_V3BMP_HEADER_SIZE;
	
	bih->header_sz = sizeof(BitmapInfoHeader);
	bih->width = wid;
	bih->height = hgt;
	bih->nplanes = 1;
	bih->bitspp = 24;
	bih->compress_type = 0; /* BI_RGB none */
	bih->bmp_bytesz = raw_data_length;

	/*
		72 Dots/Inch
		------------* 100 Cm/Meter = 2834.6
		2.54 Cm/Inch
	*/
	bih->hres = 2835;
	bih->vres = 2835;
	bih->ncolors = 0;
	bih->nimpcolors = 0;
	
	/*... WRITE BMP FILE HEADER:14 ...*/
	
	fwrite(bfh->magicNumber.magic     , 2, sizeof(ubyte), writer);
	fwrite(&bfh->fileHeader.filesz    , 4, sizeof(ubyte), writer);
	fwrite(&bfh->fileHeader.creator1  , 2, sizeof(ubyte), writer);	
	fwrite(&bfh->fileHeader.creator2  , 2, sizeof(ubyte), writer);	
	fwrite(&bfh->fileHeader.bmp_offset, 4, sizeof(ubyte), writer);	
	
	/*... WRITE BMP INFO HEADER:40 ...*/
	fwrite(&bih->header_sz    , 4, sizeof(ubyte), writer);	
	fwrite(&bih->width        , 4, sizeof(ubyte), writer);	
	fwrite(&bih->height       , 4, sizeof(ubyte), writer);	
	fwrite(&bih->nplanes      , 2, sizeof(ubyte), writer);	
	fwrite(&bih->bitspp       , 2, sizeof(ubyte), writer);	
	fwrite(&bih->compress_type, 4, sizeof(ubyte), writer);	
	fwrite(&bih->bmp_bytesz   , 4, sizeof(ubyte), writer);	
	fwrite(&bih->hres         , 4, sizeof(ubyte), writer);	
	fwrite(&bih->vres         , 4, sizeof(ubyte), writer);	
	fwrite(&bih->ncolors      , 4, sizeof(ubyte), writer);	
	fwrite(&bih->nimpcolors   , 4, sizeof(ubyte), writer);	
	
	bmp_device.raw_data = NULL;
	bmp_device.mem_disk = memory;
	bmp_device.mem_disk_fail = 0;
	
	if(memory) {
		bmp_device.raw_data = (ubyte*)calloc(bih->bmp_bytesz, sizeof(ubyte));
	    if(!bmp_device.raw_data) {
	        printf("Warning: allocating memory disk, not enough memory\n");
	        bmp_device.mem_disk_fail = 1;
	    }
	    else memset(bmp_device.raw_data, 0xff, bih->bmp_bytesz*sizeof(ubyte));
	}
	
	if(!memory || (memory && bmp_device.mem_disk_fail)) {
		ubyte dummy=0xff;
		fseek(writer, STD_V3BMP_HEADER_SIZE, SEEK_SET);
	    fwrite(&dummy, bih->bmp_bytesz, sizeof(ubyte), writer);
	}

	p->plot.makepen		= BMPAPI_MakePen;
	p->plot.moveto		= BMPAPI_MoveTo;
	p->plot.line		= BMPAPI_Line;
	p->plot.lineto		= BMPAPI_LineTo;
	p->plot.putpixel1	= BMPAPI_SetPixel1;
	p->plot.putpixel2	= BMPAPI_SetPixel2;
	p->plot.deletepen	= BMPAPI_DeletePen;
	p->plot.gsave		= BMPAPI_gSave;
	p->plot.grestore	= BMPAPI_gRestore;
	p->plot.polyline	= BMPAPI_Polyline;
	p->plot.polygon		= BMPAPI_Polygon;
	p->plot.symbol		= BMPAPI_Symbol;
	
	bmp_device.prv_xpos = 0;
	bmp_device.prv_ypos = 0;
	
	if(p)
	{
		ppix = wid/p->frame.frame_size.width;
		ppiy = hgt/p->frame.frame_size.height;
		bmp_device.scale_ppi = ppix > ppiy? ppiy:ppix;
	}
	
	TG_InitPlotAttr(&plot_attr);
	TG_BMP_SetReverseWorldCoord(0);
	
	return 1;
}

void TG_BMP_CloseExport(void)
{
	if(bmp_device.mem_disk && !bmp_device.mem_disk_fail) {
		fwrite(bmp_device.raw_data, bmp_device.info.bmp_bytesz, 
		sizeof(ubyte), bmp_device.writer);
		free(bmp_device.raw_data);
		bmp_device.mem_disk = 0;
		bmp_device.raw_data = NULL;
	}
	
    fclose(bmp_device.writer);
    bmp_device.mem_disk_fail = 0;
    bmp_device.writer=NULL;
}

#endif

/*
	Ref:
	http://www.gamedev.net/reference/articles/article1275.asp
*/

void TG_DrawLine(TG_PlotZone_Ptr p, int x1, int y1, int x2, int y2, TG_Color color)
{
	int x, y, deltax, deltay, xinc1, xinc2, yinc1, yinc2;
	int den, num, numadd, curpixel, numpixels;
	
	deltax = abs(x2 - x1);        /* The difference between the x's */
	deltay = abs(y2 - y1);        /* The difference between the y's */
	x = x1;                       /* Start x off at the first pixel */
	y = y1;                       /* Start y off at the first pixel */
	
	if (x2 >= x1)                 /* The x-values are increasing */
	{
		xinc1 = 1;
		xinc2 = 1;
	}
	else                          /* The x-values are decreasing */
	{
		xinc1 = -1;
		xinc2 = -1;
	}
	
	if (y2 >= y1)                 /* The y-values are increasing */
	{
		yinc1 = 1;
		yinc2 = 1;
	}
	else                          /* The y-values are decreasing */
	{
		yinc1 = -1;
		yinc2 = -1;
	}
	
	if (deltax >= deltay)         /* There is at least one x-value for every y-value */
	{
		xinc1 = 0;                  /* Don't change the x when numerator >= denominator */
		yinc2 = 0;                  /* Don't change the y for every iteration */
		den = deltax;
		num = deltax / 2;
		numadd = deltay;
		numpixels = deltax;         /* There are more x-values than y-values */
	}
	else                          /* There is at least one y-value for every x-value */
	{
		xinc2 = 0;                  /* Don't change the x for every iteration */
		yinc1 = 0;                  /* Don't change the y when numerator >= denominator */
		den = deltay;
		num = deltay / 2;
		numadd = deltax;
		numpixels = deltay;         /* There are more y-values than x-values */
	}
	
	for (curpixel = 0; curpixel <= numpixels; curpixel++)
	{
		p->plot.putpixel2(p, x, y, color);             /* Draw the current pixel */
		num += numadd;              /* Increase the numerator by the top of the fraction */
		if (num >= den)             /* Check if numerator >= denominator */
		{
			num -= den;               /* Calculate the new numerator value */
			x += xinc1;               /* Change the x as appropriate */
			y += yinc1;               /* Change the y as appropriate */
		}
		x += xinc2;                 /* Change the x as appropriate */
		y += yinc2;                 /* Change the y as appropriate */
	}
}

/*----------- WINDOWSMETA FILE DEVICE DRIVER ------------*/

#ifdef __EXPORT_WMF__

typedef struct StandardMetaHeader_
{
	short FileType;           /*  2     */
	short HeaderSize;         /* +2 = 4 */
	short Version;            /* +2 = 6 */
	ulong FileSize;           /* +4 = 10*/
	short NumOfObjects;       /* +2 = 12*/
	ulong MaxRecordSize;      /* +4 = 16*/
	short NumOfParams;        /* +2 = 18*/
}
	StandardMetaHeader, *StandardMetaHeader_ptr;

typedef struct PlaceableMetaHeader_ 
{
	ulong MagicNumber;  /*...  4 Key ( always 0x9AC6CDD7 ) */
	short Handle;       /*... +2 Metafile HANDLE number (always 0)*/
	short Left;         /*... +2 Left coordinate in metafile units*/
	short Top;          /*... +2 Top    "             "           */
	short Right;        /*... +2 Right      "            "        */
	short Bottom;       /*... +2 Bottom        "            "     */
	short Inch;         /*... +2 Number of metafile units per inch*/
	ulong Reserved;     /*... +4 Reserved (always 0)*/
	short CheckSum;     /*...  2 Checksum value for previous 10 words*/
}                       /*  = 22 bytes : placeable header size*/
	PlaceableMetaHeader, *PlaceableMetaHeader_ptr;

typedef struct RectWin_
{
	TG_Real left, top, right, bottom;
}
	RectWin, *RectWin_ptr;

typedef struct WmfHeader_
{
	StandardMetaHeader smh;
	PlaceableMetaHeader pmh;
}
	WmfHeader, *WmfHeader_ptr;

typedef struct MetaRecord_ 
{
	ulong  Size;
	ushort  Function;
	ushort *Parameters;  /* !Reverse order */
}
	MetaRecord, *MetaRecord_ptr;

typedef struct MetaDevice
{
	WmfHeader header;
	FILE* writer;

	/* SetPixel no effect on WMF: draw pixel manually */
	/* set the size as 0.001 * height of a frame */	
	/* I forgot the actual meaning of size and width */
	TG_Real pixel_size, pixel_width;
	W2L world2length;
	int cur_pen, prv_pen;
	int cur_brush, prv_brush;
}
	MetaDevice, *MetaDevice_ptr;

/*---------- WINDOWS METAFILE HEADER GLOBAL VAR ------------*/

MetaDevice wmf_device;

int  nTh_GDI_Object=-1;
static const TG_Real TG_DEFAULT_PIXEL_SIZE = .001;
static const TG_Real TG_DEFAULT_PIXEL_WIDTH = 1;

static const TG_Short TG_META_SETBKCOLOR             = 0x0201;
static const TG_Short TG_META_SETBKMODE              = 0x0102;
static const TG_Short TG_META_SETMAPMODE             = 0x0103;
static const TG_Short TG_META_SETROP2                = 0x0104;
static const TG_Short TG_META_SETRELABS              = 0x0105;
static const TG_Short TG_META_SETPOLYFILLMODE        = 0x0106;
static const TG_Short TG_META_SETSTRETCHBLTMODE      = 0x0107;
static const TG_Short TG_META_SETTEXTCHAREXTRA       = 0x0108;
static const TG_Short TG_META_SETTEXTCOLOR           = 0x0209;
static const TG_Short TG_META_SETTEXTJUSTIFICATION   = 0x020A;
static const TG_Short TG_META_SETWINDOWORG           = 0x020B;
static const TG_Short TG_META_SETWINDOWEXT           = 0x020C;
static const TG_Short TG_META_SETVIEWPORTORG         = 0x020D;
static const TG_Short TG_META_SETVIEWPORTEXT         = 0x020E;
static const TG_Short TG_META_OFFSETWINDOWORG        = 0x020F;
static const TG_Short TG_META_SCALEWINDOWEXT         = 0x0410;
static const TG_Short TG_META_OFFSETVIEWPORTORG      = 0x0211;
static const TG_Short TG_META_SCALEVIEWPORTEXT       = 0x0412;
static const TG_Short TG_META_LINETO                 = 0x0213;
static const TG_Short TG_META_MOVETO                 = 0x0214;
static const TG_Short TG_META_EXCLUDECLIPRECT        = 0x0415;
static const TG_Short TG_META_INTERSECTCLIPRECT      = 0x0416;
static const TG_Short TG_META_ARC                    = 0x0817;
static const TG_Short TG_META_ELLIPSE                = 0x0418;
static const TG_Short TG_META_FLOODFILL              = 0x0419;
static const TG_Short TG_META_PIE                    = 0x081A;
static const TG_Short TG_META_RECTANGLE              = 0x041B;
static const TG_Short TG_META_ROUNDRECT              = 0x061C;
static const TG_Short TG_META_PATBLT                 = 0x061D;
static const TG_Short TG_META_SAVEDC                 = 0x001E;
static const TG_Short TG_META_SETPIXEL               = 0x041F;
static const TG_Short TG_META_OFFSETCLIPRGN          = 0x0220;
static const TG_Short TG_META_TEXTOUT                = 0x0521;
static const TG_Short TG_META_BITBLT                 = 0x0922;
static const TG_Short TG_META_STRETCHBLT             = 0x0B23;
static const TG_Short TG_META_POLYGON                = 0x0324;
static const TG_Short TG_META_POLYLINE               = 0x0325;
static const TG_Short TG_META_ESCAPE                 = 0x0626;
static const TG_Short TG_META_RESTOREDC              = 0x0127;
static const TG_Short TG_META_FILLREGION             = 0x0228;
static const TG_Short TG_META_FRAMEREGION            = 0x0429;
static const TG_Short TG_META_INVERTREGION           = 0x012A;
static const TG_Short TG_META_PAINTREGION            = 0x012B;
static const TG_Short TG_META_SELECTCLIPREGION       = 0x012C;
static const TG_Short TG_META_SELECTOBJECT           = 0x012D;
static const TG_Short TG_META_SETTEXTALIGN           = 0x012E;
static const TG_Short TG_META_DRAWTEXT               = 0x062F;
static const TG_Short TG_META_CHORD                  = 0x0830;
static const TG_Short TG_META_SETMAPPERFLAGS         = 0x0231;
static const TG_Short TG_META_EXTTEXTOUT             = 0x0a32;
static const TG_Short TG_META_SETDIBTODEV            = 0x0d33;
static const TG_Short TG_META_SELECTPALETTE          = 0x0234;
static const TG_Short TG_META_REALIZEPALETTE         = 0x0035;
static const TG_Short TG_META_ANIMATEPALETTE         = 0x0436;
static const TG_Short TG_META_SETPALENTRIES          = 0x0037;
static const TG_Short TG_META_POLYPOLYGON            = 0x0538;
static const TG_Short TG_META_RESIZEPALETTE          = 0x0139;
static const TG_Short TG_META_DIBBITBLT              = 0x0940;
static const TG_Short TG_META_DIBSTRETCHBLT          = 0x0b41;
static const TG_Short TG_META_DIBCREATEPATTERNBRUSH  = 0x0142;
static const TG_Short TG_META_STRETCHDIB             = 0x0f43;
static const TG_Short TG_META_EXTFLOODFILL           = 0x0548;
static const TG_Short TG_META_RESETDC                = 0x014C;
static const TG_Short TG_META_STARTDOC               = 0x014D;
static const TG_Short TG_META_STARTPAGE              = 0x004F;
static const TG_Short TG_META_ENDPAGE                = 0x0050;
static const TG_Short TG_META_ABORTDOC               = 0x0052;
static const TG_Short TG_META_ENDDOC                 = 0x005E;
static const TG_Short TG_META_DELETEOBJECT           = 0x01f0;
static const TG_Short TG_META_CREATEPALETTE          = 0x00f7;
static const TG_Short TG_META_CREATEBRUSH            = 0x00F8;
static const TG_Short TG_META_CREATEPATTERNBRUSH     = 0x01F9;
static const TG_Short TG_META_CREATEPENINDIRECT      = 0x02FA;
static const TG_Short TG_META_CREATEFONTINDIRECT     = 0x02FB;
static const TG_Short TG_META_CREATEBRUSHINDIRECT    = 0x02FC;
static const TG_Short TG_META_CREATEBITMAPINDIRECT   = 0x02FD;
static const TG_Short TG_META_CREATEBITMAP           = 0x06FE;
static const TG_Short TG_META_CREATEREGION           = 0x06FF;

static const TG_Short TG_ALTERNATE       = 1;
static const TG_Short TG_WINDING         = 2;

static const TG_Short TG_MM_TEXT         = 1;
static const TG_Short TG_MM_LOMETRIC     = 2;
static const TG_Short TG_MM_HIMETRIC     = 3;
static const TG_Short TG_MM_LOENGLISH    = 4;
static const TG_Short TG_MM_HIENGLISH    = 5;
static const TG_Short TG_MM_TWIPS        = 6;
static const TG_Short TG_MM_ISOTROPIC    = 7;
static const TG_Short TG_MM_ANISOTROPIC  = 8;

static const TG_Short TG_PS_SOLID        = 0;
static const TG_Short TG_PS_DASH         = 1;
static const TG_Short TG_PS_DOT          = 2;
static const TG_Short TG_PS_DASHDOT      = 3;
static const TG_Short TG_PS_DASHDOTDOT   = 4;
static const TG_Short TG_PS_NULL         = 5;
static const TG_Short TG_PS_INSIDEFRAME  = 6;

static const TG_Short TG_BS_SOLID        = 0;
static const TG_Short TG_BS_NULL         = 1;
static const TG_Short TG_BS_HOLLOW       = 1; //BS_NULL
static const TG_Short TG_BS_HATCHED      = 2;
static const TG_Short TG_BS_PATTERN      = 3;
static const TG_Short TG_BS_INDEXED      = 4;
static const TG_Short TG_BS_DIBPATTERN   = 5;

static const TG_Short TG_META_END        = 0x0000;

#ifdef __TURBOC__
  static ulong TG_META_MagicNumber		= 0x9AC6CDD7;
  static ulong TG_META_KeyLow			= 0x0000FFFF;
  static ulong TG_META_KeyHigh			= 0xFFFF0000;
  static ulong TG_META_END_SIZE			= 0x00000003;
#else
  static const int   TG_META_MagicNumber	= 0x9AC6CDD7;
  static const int   TG_META_KeyLow			= 0x0000FFFF;
  static const int   TG_META_KeyHigh		= 0xFFFF0000;
  static const int   META_TG_META_END_SIZE	= 0x00000003;
#endif

static const int   TG_META_KeyShift               = 16;
static const int   TG_META_DEFAULT_RECORD_SIZE    = 3;
static const int   TG_META_STANDARD_HEADER_SIZE   = 9;
static const int   TG_META_PLACEABLE_HEADER_SIZE  = 11;
static const short TG_META_RESTORE_DC_PARAM       = -1;


void WMFAPI_Polyline(TG_PlotZone_Ptr p, TG_Real* pointx, TG_Real* pointy, int npoint, TG_Color lc, TG_Real lthk);
void WMFAPI_Polygon(TG_PlotZone_Ptr p, TG_Real* pointx, TG_Real* pointy, int npoint, TG_Color fc);
void WMFAPI_SetPolyFillMode(short fillmode);

void WMFAPI_World2Length(TG_PlotZone_Ptr p, TG_Point_Ptr w, TG_Point_Ptr l) 
{
	l->x = (w->x-p->frame.minx) * p->frame.fx;
	l->y = p->frame.frame_size.height-(w->y-p->frame.miny) * p->frame.fx;
}

void WMFAPI_ReverseWorld2Length(TG_PlotZone_Ptr p, TG_Point_Ptr w, TG_Point_Ptr l) 
{
	l->x = (w->x-p->frame.minx) * p->frame.fx;
	l->y = (w->y-p->frame.miny) * p->frame.fx;
}

void TG_WMF_SetReverseWorldCoord(int coord)
{
	if(coord==0) wmf_device.world2length = WMFAPI_World2Length;
	else wmf_device.world2length = WMFAPI_ReverseWorld2Length;
}

void WMFAPI_InitStandardMetaHeader(StandardMetaHeader_ptr p) 
{
	memset(p, 0x00, sizeof(StandardMetaHeader));

    p->FileType      = 0x0001;
    p->HeaderSize    = 0x0009;
    p->Version       = 0x0300;
    p->FileSize      = 18;
    p->NumOfObjects  = 0;
    p->MaxRecordSize = 0;
    p->NumOfParams   = 0;
}

void WMFAPI_AddFileSize(StandardMetaHeader_ptr p, int size) 
{
    p->FileSize += size;
}
  
void WMFAPI_SetMaxRecordSize(StandardMetaHeader_ptr p, int size) 
{
    p->MaxRecordSize = size;
}

void WMFAPI_SetNumberOfObjects(StandardMetaHeader_ptr p, int num) 
{
    p->NumOfObjects  = (short)num;    
}

void WMFAPI_InitPlaceableMetaHeader(PlaceableMetaHeader_ptr p, RectWin* w) 
{
	memset(p, 0x00, sizeof(PlaceableMetaHeader));

    p->MagicNumber = TG_META_MagicNumber;
    p->Handle      = 0;
    p->Left        = (short)_to_twip(w->left);
    p->Top         = (short)_to_twip(w->top);
    p->Right       = (short)_to_twip(w->right); 
    p->Bottom      = (short)_to_twip(w->bottom);
    p->Inch        = (short)(1270);

    p->Reserved    = 0; 
    p->CheckSum    = 0;
    p->CheckSum   ^= (TG_META_MagicNumber & TG_META_KeyLow);
    p->CheckSum   ^= (((ulong)(TG_META_MagicNumber & TG_META_KeyHigh)) >> TG_META_KeyShift);
    p->CheckSum   ^= p->Handle;
    p->CheckSum   ^= p->Left;
    p->CheckSum   ^= p->Top;
    p->CheckSum   ^= p->Right;
    p->CheckSum   ^= p->Bottom;
    p->CheckSum   ^= p->Inch;
    p->CheckSum   ^= (p->Reserved & TG_META_KeyLow);
    p->CheckSum   ^= (((ulong)(p->Reserved & TG_META_KeyHigh)) >> TG_META_KeyShift);
}

void WMFAPI_WriteHeader(FILE* writer, StandardMetaHeader_ptr smh, PlaceableMetaHeader_ptr pmh) 
{
	fwrite(&pmh->MagicNumber, 1, 4, writer);
	fwrite(&pmh->Handle, 1, 2, writer);
	fwrite(&pmh->Left, 1, 2, writer);
	fwrite(&pmh->Top, 1, 2, writer);
	fwrite(&pmh->Right, 1, 2, writer);
	fwrite(&pmh->Bottom, 1, 2, writer);
	fwrite(&pmh->Inch, 1, 2, writer);
	fwrite(&pmh->Reserved, 1, 4, writer);
	fwrite(&pmh->CheckSum, 1, 2, writer);

	fwrite(&smh->FileType, 1, 2, writer);
	fwrite(&smh->HeaderSize, 1, 2, writer);
	fwrite(&smh->Version, 1, 2, writer);
	fwrite(&smh->FileSize, 1, 4, writer);
	fwrite(&smh->NumOfObjects, 1, 2, writer);
	fwrite(&smh->MaxRecordSize, 1, 4, writer);
	fwrite(&smh->NumOfParams, 1, 2, writer);
}

void WMFAPI_InitMetaRecord(MetaRecord_ptr p)
{
    p->Size = TG_META_DEFAULT_RECORD_SIZE;
    p->Function = TG_META_END;
    p->Parameters = NULL;
}
  
void WMFAPI_SetMemory(MetaRecord_ptr p, int nParam) 
{
    p->Parameters = (ushort*)malloc(sizeof(ushort)*nParam);
}

void WMFAPI_SetMetaRecord1(MetaRecord_ptr p, short function)
{
    p->Size = TG_META_DEFAULT_RECORD_SIZE;
    p->Function = function;
    p->Parameters = NULL;
}

void WMFAPI_SetMetaRecord2(MetaRecord_ptr p, short function, int nParam)
{
    p->Size = TG_META_DEFAULT_RECORD_SIZE+nParam;
    p->Function = function;
    WMFAPI_SetMemory(p, nParam);
}

void WMFAPI_ReleaseMetaRecord(MetaRecord_ptr p)
{
    free(p->Parameters);
}
  
void WMFAPI_SetParam(MetaRecord_ptr p, int nthParam, int param) 
{
    p->Parameters[nthParam] = (ushort)param;
}
  
void WMFAPI_SetFunction(MetaRecord_ptr p, ushort Function) 
{
    p->Function = Function;
}

ushort WMFAPI_GetNumberOfParams(MetaRecord_ptr p)
{
    return (ushort)(p->Size - TG_META_DEFAULT_RECORD_SIZE);
}

void WMFAPI_WriteMetaRecord(MetaDevice_ptr dev, MetaRecord_ptr rec, int wparam)
{
	int i=0, nParam=0;
	fwrite(&rec->Size, 1, sizeof(ulong), dev->writer);
	fwrite(&rec->Function, 1, sizeof(ushort), dev->writer);

	nParam = WMFAPI_GetNumberOfParams(rec);
	if(wparam) 
	{
		for(i = 0; i < nParam; i++) 
		{ 
			fwrite(&rec->Parameters[i], 1, sizeof(ushort), dev->writer);
		}
	}
}

void WMFAPI_SetWindowOrg(TG_PlotZone_Ptr p, TG_Real x, TG_Real y) 
{
	MetaRecord rec;
	ulong r_size=0;

	WMFAPI_SetMetaRecord2(&rec, TG_META_SETWINDOWORG, 2);
	WMFAPI_SetParam(&rec, 0, (ushort)_to_twip(y));
	WMFAPI_SetParam(&rec, 1, (ushort)_to_twip(x));

	r_size = rec.Size;
	if(r_size > wmf_device.header.smh.MaxRecordSize) 
		wmf_device.header.smh.MaxRecordSize = r_size;
	wmf_device.header.smh.FileSize += r_size;
	WMFAPI_WriteMetaRecord(&wmf_device, &rec, 1);
	WMFAPI_ReleaseMetaRecord(&rec);
}

void WMFAPI_SetWindowExt(TG_PlotZone_Ptr p, TG_Real x, TG_Real y) 
{
	MetaRecord rec;
	ulong r_size=0;

	WMFAPI_SetMetaRecord2(&rec, TG_META_SETWINDOWEXT, 2);
	WMFAPI_SetParam(&rec, 0, (ushort)_to_twip(y));
	WMFAPI_SetParam(&rec, 1, (ushort)_to_twip(x));

	r_size = rec.Size;
	if(r_size > wmf_device.header.smh.MaxRecordSize) 
		wmf_device.header.smh.MaxRecordSize = r_size;
	wmf_device.header.smh.FileSize += r_size;
	WMFAPI_WriteMetaRecord(&wmf_device, &rec, 1);
	WMFAPI_ReleaseMetaRecord(&rec);
}

void WMFAPI_SetMapMode(short mode)
{
	MetaRecord rec;
	ulong r_size=0;

	WMFAPI_SetMetaRecord2(&rec, TG_META_SETMAPMODE, 1);
	WMFAPI_SetParam(&rec, 0, mode);
	r_size = rec.Size;

	if(r_size > wmf_device.header.smh.MaxRecordSize) 
		wmf_device.header.smh.MaxRecordSize = r_size;
	wmf_device.header.smh.FileSize += r_size;
	WMFAPI_WriteMetaRecord(&wmf_device, &rec, 1);
	WMFAPI_ReleaseMetaRecord(&rec);
}

int WMFAPI_CreatePenIndirect(short style, short x_wid, short y_hgt, TG_Color color)
{
	MetaRecord rec;
	ulong r_size=0;

	WMFAPI_SetMetaRecord2(&rec, TG_META_CREATEPENINDIRECT, 5);
	WMFAPI_SetParam(&rec, 0, style);
	WMFAPI_SetParam(&rec, 1, x_wid);
	WMFAPI_SetParam(&rec, 2, y_hgt);
	WMFAPI_SetParam(&rec, 3, TG_ReverseUShort(color>>8));
	WMFAPI_SetParam(&rec, 4, (color&0x000000ff));

	r_size = rec.Size;
	if(r_size > wmf_device.header.smh.MaxRecordSize) 
		wmf_device.header.smh.MaxRecordSize = r_size;
	wmf_device.header.smh.FileSize += r_size;
	++nTh_GDI_Object;
	WMFAPI_WriteMetaRecord(&wmf_device, &rec, 1);
	WMFAPI_ReleaseMetaRecord(&rec);
	wmf_device.header.smh.NumOfObjects++;

	return nTh_GDI_Object;
}

void WMFAPI_SelectObject(int _nTh_GDI_Object) 
{
	MetaRecord rec;
	ulong r_size=0;

	WMFAPI_SetMetaRecord2(&rec, TG_META_SELECTOBJECT, 1);
	WMFAPI_SetParam(&rec, 0, _nTh_GDI_Object);

	r_size = rec.Size;
	if(r_size > wmf_device.header.smh.MaxRecordSize) 
	wmf_device.header.smh.MaxRecordSize = r_size;
	wmf_device.header.smh.FileSize += r_size;
	WMFAPI_WriteMetaRecord(&wmf_device, &rec, 1);
	WMFAPI_ReleaseMetaRecord(&rec);
}

void WMFAPI_DeleteObject(int _nTh_GDI_Object) 
{
	MetaRecord rec;
	ulong r_size=0;

	WMFAPI_SetMetaRecord2(&rec, TG_META_DELETEOBJECT, 1);
	WMFAPI_SetParam(&rec, 0, _nTh_GDI_Object);

	r_size = rec.Size;
	if(r_size > wmf_device.header.smh.MaxRecordSize) 
		wmf_device.header.smh.MaxRecordSize = r_size;
	wmf_device.header.smh.FileSize += r_size;
	WMFAPI_WriteMetaRecord(&wmf_device, &rec, 1);
	WMFAPI_ReleaseMetaRecord(&rec);
	--nTh_GDI_Object;
}

void WMFAPI_SaveDC(void)
{
	MetaRecord rec;
	ulong r_size=0;

	WMFAPI_SetMetaRecord1(&rec, TG_META_SAVEDC);
	r_size = rec.Size;
	if(r_size > wmf_device.header.smh.MaxRecordSize) 
		wmf_device.header.smh.MaxRecordSize = r_size;
	wmf_device.header.smh.FileSize += r_size;
	WMFAPI_WriteMetaRecord(&wmf_device, &rec, 0);
	WMFAPI_ReleaseMetaRecord(&rec);
}

void WMFAPI_RestoreDC(void)
{
  MetaRecord rec;
  ulong r_size=0;
  
  WMFAPI_SetMetaRecord2(&rec, TG_META_RESTOREDC, 1);

  WMFAPI_SetParam(&rec, 0, 0xffff);
  r_size = rec.Size;
  if(r_size > wmf_device.header.smh.MaxRecordSize) 
	  wmf_device.header.smh.MaxRecordSize = r_size;
  wmf_device.header.smh.FileSize += r_size;

  WMFAPI_WriteMetaRecord(&wmf_device, &rec, 1);
  WMFAPI_ReleaseMetaRecord(&rec);
}

void WMFAPI_EndRecord(void) 
{
	MetaRecord rec;
	ulong r_size=0;

	WMFAPI_SetMetaRecord1(&rec, TG_META_END);
	r_size = rec.Size;
	if(r_size > wmf_device.header.smh.MaxRecordSize) 
	wmf_device.header.smh.MaxRecordSize = r_size;
	wmf_device.header.smh.FileSize += r_size;
	WMFAPI_WriteMetaRecord(&wmf_device, &rec, 0);
	WMFAPI_ReleaseMetaRecord(&rec);
}

void WMFAPI_CloseMetafile(void)
{
	WMFAPI_EndRecord();
	
	fseek(wmf_device.writer, 0L, SEEK_SET);
	WMFAPI_WriteHeader(wmf_device.writer, &wmf_device.header.smh, &wmf_device.header.pmh);
	fclose(wmf_device.writer);
	
	wmf_device.writer = NULL;
	nTh_GDI_Object = -1;
}

void WMFAPI_GenericSetPixel(TG_PlotZone_Ptr p, TG_Real x, TG_Real y, TG_Color c) 
{
	TG_Real pixel_size = wmf_device.pixel_size*wmf_device.pixel_width*
		p->frame.frame_size.height/p->frame.fx;
	TG_ComputeSquareSymbol(pixel_size, 1, x, y);
	WMFAPI_Polygon(p, sym_squarex, sym_squarey, sym_square_point, c);    
}

void WMFAPI_SetPixel1(TG_PlotZone_Ptr p, TG_Real x, TG_Real y, TG_Color c) 
{
	WMFAPI_GenericSetPixel(p, x, y, c);
}

void WMFAPI_SetPixel2(TG_PlotZone_Ptr p, int x, int y, TG_Color c) 
{
	WMFAPI_GenericSetPixel(p, x, y, c);
}

void WMFAPI_MoveTo(TG_PlotZone_Ptr p, TG_Real x, TG_Real y) 
{
	/* 	wc: World Coordinate
		lc: logical Coordinate */
	TG_Point wc, lc;

	MetaRecord rec;
	ulong r_size = 0;
	wc.x = x, wc.y = y;

	wmf_device.world2length(p, &wc, &lc);
	WMFAPI_SetMetaRecord2(&rec, TG_META_MOVETO, 2);
	WMFAPI_SetParam(&rec, 0, (ushort)_to_twip(lc.y));
	WMFAPI_SetParam(&rec, 1, (ushort)_to_twip(lc.x));
 
	r_size = rec.Size;
	if(r_size > wmf_device.header.smh.MaxRecordSize) 
		wmf_device.header.smh.MaxRecordSize = r_size;
	wmf_device.header.smh.FileSize += r_size;
	WMFAPI_WriteMetaRecord(&wmf_device, &rec, 1);
	WMFAPI_ReleaseMetaRecord(&rec);
}

void WMFAPI_LineTo(TG_PlotZone_Ptr p, TG_Real x, TG_Real y) 
{
	TG_Point wc, lc;
	MetaRecord rec;
	ulong r_size = 0;
	wc.x = x, wc.y = y;

	wmf_device.world2length(p, &wc, &lc);
	WMFAPI_SetMetaRecord2(&rec, TG_META_LINETO, 2);
	WMFAPI_SetParam(&rec, 0, (ushort)_to_twip(lc.y));
	WMFAPI_SetParam(&rec, 1, (ushort)_to_twip(lc.x));
 
	r_size = rec.Size;
	if(r_size > wmf_device.header.smh.MaxRecordSize) 
		wmf_device.header.smh.MaxRecordSize = r_size;
	wmf_device.header.smh.FileSize += r_size;
	WMFAPI_WriteMetaRecord(&wmf_device, &rec, 1);
	WMFAPI_ReleaseMetaRecord(&rec);
}

void WMFAPI_MakePen(TG_PlotZone_Ptr p, TG_Color c, TG_Real thk)
{
	
	plot_attr.prv_pen_color = plot_attr.cur_pen_color;
	plot_attr.cur_pen_color = c;
	plot_attr.thk = _to_twip(thk*p->frame.frame_size.height);
	
	wmf_device.cur_pen = WMFAPI_CreatePenIndirect(TG_PS_SOLID,plot_attr.thk,0,c);
	WMFAPI_SelectObject(wmf_device.cur_pen);
}

void WMFAPI_DeletePen(void)
{
	WMFAPI_DeleteObject(wmf_device.cur_pen);
	plot_attr.cur_pen_color = plot_attr.prv_pen_color;
	plot_attr.thk = 0;
}

void WMFAPI_Line(TG_PlotZone_Ptr p, TG_Real x1, TG_Real y1, TG_Real x2, TG_Real y2, TG_Color color, TG_Real thk)
{
	WMFAPI_MakePen(p,color,thk);
	WMFAPI_MoveTo(p, x1, y1);
	WMFAPI_LineTo(p, x2, y2);
	WMFAPI_DeletePen();
}

int WMFAPI_CreateBrushIndirect(short style, TG_Color color, short hatch)
{
	ulong r_size;
	MetaRecord rec;
		
	WMFAPI_SetMetaRecord2(&rec, TG_META_CREATEBRUSHINDIRECT, 4);
	WMFAPI_SetParam(&rec, 0, style);
	WMFAPI_SetParam(&rec, 1, TG_ReverseUShort(color>>8));
	WMFAPI_SetParam(&rec, 2, (color&0x000000ff));
	WMFAPI_SetParam(&rec, 3, hatch);
	
	r_size = rec.Size;
	if(r_size > wmf_device.header.smh.MaxRecordSize) 
		wmf_device.header.smh.MaxRecordSize = r_size;
	wmf_device.header.smh.FileSize += r_size;
	WMFAPI_WriteMetaRecord(&wmf_device, &rec, 1);
	WMFAPI_ReleaseMetaRecord(&rec);
	wmf_device.header.smh.NumOfObjects++;
	++nTh_GDI_Object;
	
	return nTh_GDI_Object;
}

void WMFAPI_Polyline(TG_PlotZone_Ptr p, TG_Real* pointx, TG_Real* pointy, int npoint, TG_Color lc, TG_Real lthk)
{
	TG_Point wc, gc;
	int i, j;
	ulong r_size;
	MetaRecord rec;

	WMFAPI_SaveDC();
	WMFAPI_MakePen(p, lc, lthk);
	WMFAPI_SetMetaRecord2(&rec, TG_META_POLYLINE, 1+npoint*2); 
	WMFAPI_SetParam(&rec, 0, npoint);
	
	for(i = 0, j = 1; i < npoint; i++)
	{
		wc.x = pointx[i];
		wc.y = pointy[i];
		wmf_device.world2length(p, &wc, &gc);
		WMFAPI_SetParam(&rec, j++, (ushort)_to_twip(gc.x));
		WMFAPI_SetParam(&rec, j++, (ushort)_to_twip(gc.y));
	}	
	
	r_size = rec.Size;
	if(r_size > wmf_device.header.smh.MaxRecordSize) 
		wmf_device.header.smh.MaxRecordSize = r_size;
	wmf_device.header.smh.FileSize += r_size;
	WMFAPI_WriteMetaRecord(&wmf_device, &rec, 1);
	WMFAPI_ReleaseMetaRecord(&rec);
	WMFAPI_DeletePen();
	WMFAPI_RestoreDC();
}

void WMFAPI_SetPolyFillMode(short fillmode)
{
	ulong r_size;
	MetaRecord rec;
	
	WMFAPI_SetMetaRecord2(&rec, TG_META_SETPOLYFILLMODE, 1);
	WMFAPI_SetParam(&rec, 0, fillmode);
	
	r_size = rec.Size;
	if(r_size > wmf_device.header.smh.MaxRecordSize) 
		wmf_device.header.smh.MaxRecordSize = r_size;
	wmf_device.header.smh.FileSize += r_size;
	WMFAPI_WriteMetaRecord(&wmf_device, &rec, 1);
	WMFAPI_ReleaseMetaRecord(&rec);	
}

void WMFAPI_Polygon(TG_PlotZone_Ptr p, TG_Real* pointx, TG_Real* pointy, int npoint, TG_Color fc)
{
	TG_Point wc, lc;
	int i,j;
	ulong r_size;
	MetaRecord rec;
	
	if(npoint < 3) return;
	
	wmf_device.cur_pen = WMFAPI_CreatePenIndirect(TG_PS_SOLID, 1, 0, fc);
	WMFAPI_SelectObject(wmf_device.cur_pen);
	
	wmf_device.cur_brush = WMFAPI_CreateBrushIndirect(TG_BS_SOLID, fc, TG_BS_SOLID);
	WMFAPI_SelectObject(wmf_device.cur_brush);
	
	WMFAPI_SetMetaRecord2(&rec, TG_META_POLYGON, 1+npoint*2); 
	WMFAPI_SetParam(&rec, 0, npoint);
	
	for(i = 0, j = 1; i < npoint; i++)
	{
		wc.x = pointx[i];
		wc.y = pointy[i];
		wmf_device.world2length(p, &wc, &lc);
		WMFAPI_SetParam(&rec, j++, (ushort)_to_twip(lc.x));
		WMFAPI_SetParam(&rec, j++, (ushort)_to_twip(lc.y));
	}

	r_size = rec.Size;
	if(r_size > wmf_device.header.smh.MaxRecordSize) 
		wmf_device.header.smh.MaxRecordSize = r_size;
	wmf_device.header.smh.FileSize += r_size;
	WMFAPI_WriteMetaRecord(&wmf_device, &rec, 1);
	WMFAPI_ReleaseMetaRecord(&rec);
	WMFAPI_DeleteObject(wmf_device.cur_brush);
	WMFAPI_DeleteObject(wmf_device.cur_pen);
}

void WMFAPI_Symbol(TG_PlotZone_Ptr p, TG_Real x, TG_Real y, TG_Symbol sym, TG_Real size, TG_Color fc, int fill, TG_Color lc, int line, TG_Real lthk)
{
	TG_Real sym_size = size*p->frame.frame_size.height/p->frame.fx;

	if(sym < 0 || sym > (MAX_DEFAULT_SYMBOL-1)) return;

	/*... No symbol drawing ...*/
	if(fill == 0 && line == 0) return;
	
	TG_GetSymbolCoordinate(sym, sym_size, 1, x, y);
	
	if(fill == 1)
	WMFAPI_Polygon(p, symbol_pool[sym].pointx, symbol_pool[sym].pointy, symbol_pool[sym].npoint, fc);	
	
	if(line==1 && (fc != lc))
	WMFAPI_Polyline(p, symbol_pool[sym].pointx, symbol_pool[sym].pointy, symbol_pool[sym].npoint+1, lc, lthk);
}

int TG_WMF_OpenExport(TG_PlotZone* p, const char* file)
{
	RectWin w;

	w.left = 0;
	w.top = 0;
	w.right = p->frame.frame_size.width;
	w.bottom = p->frame.frame_size.height;
	wmf_device.writer = fopen(file, "wb");

	if(!wmf_device.writer) { 
		fprintf(stderr, "Error: can't open %s at TG_WMF_OpenExport\n", file);
		return 0;
	}

	WMFAPI_InitStandardMetaHeader(&wmf_device.header.smh);
	WMFAPI_InitPlaceableMetaHeader(&wmf_device.header.pmh, &w);
	WMFAPI_WriteHeader(wmf_device.writer, &wmf_device.header.smh, &wmf_device.header.pmh);
	WMFAPI_SetWindowOrg(p, w.left, w.top);
	WMFAPI_SetWindowExt(p, w.right, w.bottom);

	p->plot.makepen		= WMFAPI_MakePen;
	p->plot.moveto		= WMFAPI_MoveTo;
	p->plot.line		= WMFAPI_Line;
	p->plot.lineto		= WMFAPI_LineTo;
	p->plot.putpixel1	= WMFAPI_SetPixel1;
	p->plot.putpixel2	= WMFAPI_SetPixel2;
	p->plot.deletepen	= WMFAPI_DeletePen;
	p->plot.gsave		= WMFAPI_SaveDC;
	p->plot.grestore	= WMFAPI_RestoreDC;
	p->plot.polyline	= WMFAPI_Polyline;
	p->plot.polygon		= WMFAPI_Polygon;
	p->plot.symbol		= WMFAPI_Symbol;

	wmf_device.pixel_size = TG_DEFAULT_PIXEL_SIZE;
	wmf_device.pixel_width = TG_DEFAULT_PIXEL_WIDTH;
	wmf_device.cur_pen = 0;
	wmf_device.prv_pen = 0;
	
	TG_WMF_SetReverseWorldCoord(0);
	TG_InitPlotAttr(&plot_attr);

	return 1;
}

void TG_WMF_CloseExport(void)
{
	WMFAPI_CloseMetafile();
}

#endif

/*-----------------------------------------------------
	SYMBOL 
-----------------------------------------------------*/

static void TG_ComputeCircleSymbol(TG_Real size, TG_Real width, TG_Real lx, TG_Real ly) 
{
	int i=0;
	//TG_Real r= width * size * 0.005; // length
	TG_Real r= width * size * 0.5; // length
	static const TG_Real pi6 = _pi / 6.0;
	static const TG_Real pi3 = _pi / 3.0;
	TG_Real zero = 0;
	
	sym_circlex[0]  = r   		; 
	sym_circley[0]  = zero		;
	sym_circlex[3]  = zero		; 
	sym_circley[3]  = r			;
	sym_circlex[6]  = -r  		; 
	sym_circley[6]  = zero		;
	sym_circlex[9]  = zero		; 
	sym_circley[9]  = -r		;
	sym_circlex[1]  = r*cos(pi6); 
	sym_circlex[2]  = r*cos(pi3);
	sym_circley[1]  = r*sin(pi6); 
	sym_circley[2]  = r*sin(pi3);
	sym_circlex[4]  = -sym_circlex[2]; 
	sym_circley[4]  =  sym_circley[2];
	sym_circlex[5]  = -sym_circlex[1]; 
	sym_circley[5]  =  sym_circley[1];
	sym_circlex[7]  =  sym_circlex[5]; 
	sym_circley[7]  = -sym_circley[5];
	sym_circlex[8]  =  sym_circlex[4]; 
	sym_circley[8]  = -sym_circley[4];
	sym_circlex[10] = -sym_circlex[8]; 
	sym_circley[10] =  sym_circley[8];
	sym_circlex[11] = -sym_circlex[7]; 
	sym_circley[11] =  sym_circley[7];
	sym_circlex[12] =  sym_circlex[0];
	sym_circley[12] =  sym_circley[0];
	
	while(i <= 12) { sym_circlex[i] += lx; sym_circley[i] += ly; i++;}
}

void TG_ComputeDeltaSymbol(TG_Real size, TG_Real width, TG_Real lx, TG_Real ly) 
{
	TG_Real span= width*size;
	TG_Real height = span * 0.866025;
	TG_Real hs=span*0.5, hh=height*0.5;
	
	sym_deltax[0] = -hs + lx;
	sym_deltay[0] = -hh + ly;	
	sym_deltax[1] =  hs + lx;
	sym_deltay[1] = -hh + ly;
	sym_deltax[2] =   0 + lx;
	sym_deltay[2] =  hh + ly;
	sym_deltax[3] = sym_deltax[0];
	sym_deltay[3] = sym_deltay[0];
}
void TG_ComputeGradientSymbol(TG_Real size, TG_Real width, TG_Real lx, TG_Real ly) 
{
	TG_Real span= width*size;
	TG_Real height = span * 0.866025;
	TG_Real hs=span*0.5, hh=height*0.51;
	
	sym_gradientx[0] = -hs + lx;
	sym_gradienty[0] =  hh + ly;
	sym_gradientx[1] =  hs + lx;
	sym_gradienty[1] =  hh + ly;
	sym_gradientx[2] =   0 + lx;
	sym_gradienty[2] = -hh + ly;
	sym_gradientx[3] = sym_gradientx[0];
	sym_gradienty[3] = sym_gradienty[0];
}

void TG_ComputeRTriangleSymbol(TG_Real size, TG_Real width, TG_Real lx, TG_Real ly) 
{
	TG_Real span= width*size;
	TG_Real height = span * 0.866025;
	TG_Real hs=span*0.5, hh=height*0.5;
	
	sym_rtrianglex[0] =  hh + lx;
	sym_rtriangley[0] =  hs + ly;
	sym_rtrianglex[1] =  hh + lx;
	sym_rtriangley[1] = -hs + ly;
	sym_rtrianglex[2] = -hh + lx;
	sym_rtriangley[2] =   0 + ly;
	sym_rtrianglex[3] = sym_rtrianglex[0];
	sym_rtriangley[3] = sym_rtriangley[0];
}

void TG_ComputeLTriangleSymbol(TG_Real size, TG_Real width, TG_Real lx, TG_Real ly) 
{
	TG_Real span= width*size;
	TG_Real height = span * 0.866025;
	TG_Real hs=span*0.5, hh=height*0.5;
	
	sym_ltrianglex[0] = -hh + lx;
	sym_ltriangley[0] =  hs + ly;
	sym_ltrianglex[1] = -hh + lx;
	sym_ltriangley[1] = -hs + ly;
	sym_ltrianglex[2] =  hh + lx;
	sym_ltriangley[2] =   0 + ly;
	sym_ltrianglex[3] = sym_ltrianglex[0];
	sym_ltriangley[3] = sym_ltriangley[0];
}

void TG_ComputeDiamondSymbol(TG_Real size, TG_Real width, TG_Real lx, TG_Real ly) 
{
	TG_Real span=width*size;
	TG_Real hy=0.546*span, hx=0.546*span;

	sym_diamondx[0] =   0 + lx;
	sym_diamondy[0] =  hy + ly;
	sym_diamondx[1] = -hx + lx;
	sym_diamondy[1] =   0 + ly;
	sym_diamondx[2] =   0 + lx;
	sym_diamondy[2] = -hy + ly;
	sym_diamondx[3] =  hx + lx;
	sym_diamondy[3] =   0 + ly;
	sym_diamondx[4] = sym_diamondx[0];
	sym_diamondy[4] = sym_diamondy[0];
}

void TG_ComputeSquareSymbol(TG_Real size, TG_Real width, TG_Real lx, TG_Real ly) 
{
	TG_Real hs=width*size*0.5;
	
	sym_squarex[0] = -hs + lx;
	sym_squarey[0] =  hs + ly;
	sym_squarex[1] = -hs + lx;
	sym_squarey[1] = -hs + ly;
	sym_squarex[2] =  hs + lx;
	sym_squarey[2] = -hs + ly;
	sym_squarex[3] =  hs + lx;
	sym_squarey[3] =  hs + ly;
	sym_squarex[4] = sym_squarex[0];
	sym_squarey[4] = sym_squarey[0];
}

#ifdef __cplusplus
}
#endif

/*... End of libgt.c ...*/

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
hus
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions