www.pudn.com > ReadingPeopleTracker-1.28.rar > Image.cc


/*
 * Image.cc
 * general Image class
 *
 * author : A M Baumberg
 */

#ifdef _SVID_SOURCE
#include    // for System V style 48-bit random number generator
//  functions void srand48(long int seed_val), double drand48(void)
// (_SVID_SOURCE must be defined for this to work --- available under Linux)
#endif

#include 
#include 
#include 
#include 
#include 

#include "Image.h"

#include "Point2.h"
#include "Kernel.h"
#include "text_output.h"
#include "tracker_defines_types_and_helpers.h"

namespace ReadingPeopleTracker
{

// definition and initialisation of static member variables
const int Image::CIRCLE_PRECISION = 8;
Point2 *Image::pt = NULL;
#ifndef NO_DISPLAY
EnvIntParameter *Image::display_zoom = new EnvIntParameter("IMAGE_ZOOM", 1);
#endif   // ifndef NO_DISPLAY



// Global and constant variables that determine Image Addressing: 

// image_addressing_mode: determines the orientation of the v axis in the image.
//   This should always be BOTTOM_TO_TOP, like axes in mathematics and physics.
//   TOP_TO_BOTTOM: line 0 is the top line,  BOTTOM_TO_TOP: line 0 is the bottom line
const ImageAddressing Image::image_addressing_mode = IA_BOTTOM_TO_TOP;

// image_storage_mode: determines the orientation of the image in memory.  This has an
//   influence only on functions which operate in the image memory, e.g. JPEGSource.
//   This should be set to the way your graphics library or display (e.g. X11) handles it.
//   TOP_TO_BOTTOM: a lower memory address is closer to the top line of the image,
//   BOTTOM_TO_TOP: a higher memory address is closer to the top line of the image.

#ifdef LINUX
#ifdef USE_GL
// Warning: this is unlike X11, which makes displaying images slower...
const ImageAddressing Image::image_storage_mode = IA_BOTTOM_TO_TOP;
#else
const ImageAddressing Image::image_storage_mode = IA_TOP_TO_BOTTOM;
#endif  // USE_GL

#else   // ie not LINUX

#error " Set both image addressing variables to correct values. "

ImageAddressing Image::image_storage_mode = IA_TOP_TO_BOTTOM;

#endif   // ifdef LINUX
// please check usage of this varable below when changing this!

#ifndef NO_DISPLAY
#ifndef USE_GL
#ifdef USE_VOGL
extern "C"
{
    // these are vogl
    void ginit();
    void vo_xt_window(Display*, Window, int, int);
    void vo_xt_drawable(Display*, Window, Drawable, GC, int, int);
    void vo_array(unsigned char*, int, int, int);
    typedef unsigned short  Colorindex;
    extern void color(Colorindex);
}
    int Image::vogl_init_flag = 0;

#endif   // ifdef USE_VOGL
#endif   // ifndef USE_GL
#endif   // ifndef NO_DISPLAY


void Image::set_colour(unsigned int col)
{
#ifndef NO_DISPLAY
#ifndef USE_GL  // ie use VOGL
#ifdef USE_VOGL
    ::color(col);
#endif   // ifdef USE_VOGL
#else
// FIXME: we are assuming we have an RGB (true colour?) display.
    
    // get r,g,b colour from table defined in Image.h ...
    ::RGBcolor( draw_colour_table[col % NUM_DRAW_COLOURS][0],
		draw_colour_table[col % NUM_DRAW_COLOURS][1],
		draw_colour_table[col % NUM_DRAW_COLOURS][2] );

#endif
#endif   // #ifndef NO_DISPLAY
} 

Image::Image(unsigned int the_width, unsigned int the_height,
	     frame_id_t the_frame_id, frame_time_t the_frame_time_in_ms,
	     unsigned int the_bpp, unsigned char *the_data)
{
    assert (the_width > 0);
    assert (the_height > 0);
    assert (the_bpp > 0);
    
    width = the_width;
    height = the_height;
    
    frame_id = the_frame_id;
    frame_time_in_ms = the_frame_time_in_ms;
    
    image_type = BASE;  // change in derived classes!
    
    switch (bytes_per_pixel = the_bpp)
    {
    case 1: bpp_shift = 0; break;
    case 2: bpp_shift = 1; break;
    case 4: bpp_shift = 2; break;
	
    default:
	cerror << " Image::Image: Bad call to Image constructor (unknown #bpp)" << endl;
	exit(1);
    }
    
#ifndef NO_DISPLAY
    window_title[0] = 0;
#endif   // #ifndef NO_DISPLAY

    // length (size) of an image line in memory
    line_length = width * bytes_per_pixel;

    if (the_data == NULL)
    {
	data = new unsigned char[height * line_length];
	own_data = true;
    }
    else
    {
	data = the_data;
	own_data = false;
    }
    
    end_data = (data + height * line_length);
    ypoints = new unsigned int[height];

    // make sure that ypoints can be used as the user wants (image_addressing_mode)
    // while respecting the image's layout in memory (image_storage_mode):
    if (image_storage_mode == image_addressing_mode)
    {
	for (unsigned int y = 0; y < height; y++)
	    ypoints[y] = y * line_length;
    }
    else
    {
	// flip ypoints to hide the discrepancy from other methods
	for (unsigned int y = 0; y < height; y++)
	    ypoints[y] = (height - (y+1)) * line_length;
    }

#ifndef NO_DISPLAY
#ifdef USE_GL
    glwin = NULLWIN;
#else
    mydisplay = NULL;
    mywindow = NULLWIN;
#endif
#endif   // #ifndef NO_DISPLAY
    
}

Image::~Image()
{
    // de-allocate image memory
    if ((own_data) && (data != NULL))
	delete [] data;
    
    delete [] ypoints;
    
    // close window if Image is currently displayed
#ifndef NO_DISPLAY
#ifdef USE_GL
// GL interface
    if (glwin != NULLWIN)
	winclose(glwin);
#else
// X interface
    if (mydisplay != NULL)
    {
	XFreeGC(mydisplay, mygc);
	XDestroyWindow(mydisplay, mywindow);
	if (myimage->data != (char *) data)
	    delete [] (myimage->data);
	myimage->data = None;
	XFree((char *) myimage);
    }
#endif
#endif   // #ifndef NO_DISPLAY
    
}

inline void Image::clear(unsigned char fill_char)
{
    memset(data, (int) fill_char, (end_data - data));
}

// given a pointer to a pixel within the Image, return x and y ccordinates
void Image::get_pixel_coords(unsigned char *pnt, unsigned int &x, unsigned int &y) const
{
    assert(pnt > data);
    assert(pnt <= end_data);
    
    register unsigned int offset = pnt - data;
    
    x = (offset % line_length) >> bpp_shift;
    
    // this is dependent on image addressing and storage modes:
    if (image_addressing_mode == image_storage_mode)
	y = offset / line_length;
    else
	y = height - ((offset / line_length) + 1);
}

void Image::draw_in_image()
{
    
#ifndef NO_DISPLAY
#ifdef USE_GL
    if (glwin != NULLWIN)
    {
	winset(glwin);
	
	if (image_type == GREY8)
	    cmode();
	else
	    RGBmode();

	gconfig();   // NB we have to do this after cmode / RGBmode
    }
#else
    if (vogl_init_flag == 0)
    {
	if (mydisplay == NULL) //  draw directly into image data
	{
	    // use amb's extension to vogl
	    //    vo_array(data, width, height, bytes_per_pixel);
	    ginit();
	    vogl_init_flag = 1;
	}
	else // draw into backing pixmap or into window
	{
	    //   vo_xt_drawable(mydisplay, mywindow, mywindow, mygc, width,
	    //   height);
	    vogl_init_flag = 3;
	    ginit();
	}
    }
#endif
#endif   // #ifndef NO_DISPLAY
}

#ifdef USE_GL
inline
#endif
void Image::read_image()
{
    
#ifndef NO_DISPLAY
#ifdef USE_GL
    
    winset(glwin);
    lrectread(0,0,width-1,height-1, (Int32 *) data);
    
#else
    
    // do nothing if drawing directly into image data
    if (vogl_init_flag == 1)
	return;
    
    XImage *xi;
    
    if (vogl_init_flag == 2)
	xi = XGetImage(mydisplay, mypixmap, 0,0, width, height, AllPlanes,
		       ZPixmap);
    else
	if ((vogl_init_flag == 3) || (vogl_init_flag == 0))
	    xi = XGetImage(mydisplay, mywindow, 0,0, width, height, AllPlanes,
			   ZPixmap);
	else 
	    memcpy(data, xi->data, (end_data - data));
    
    XDestroyImage(xi);
    flip_vertically(this);
    
#endif
#endif   // #ifndef NO_DISPLAY
}

Image *Image::flip_vertically(Image *r) 
{
    if (r == NULL)
	r = copy_type();
    
    unsigned char *tmp_store = new unsigned char[line_length];
    int endloop;
    if ((height % 2) == 0)
	endloop = height / 2;
    else
    {
	endloop = (height - 1) / 2;
	memcpy(r->get_pixel(0,endloop), get_pixel(0,endloop),
	       line_length);
    }
    int y1;
    int y2;  // could use register here?  but then, get_pixel is inline
    
    for (y1 = 0; y1 < endloop; y1++)
    {
	y2 = height - y1 - 1;
	memcpy(tmp_store, get_pixel(0,y2), line_length);
	memcpy(r->get_pixel(0,y2), get_pixel(0,y1), line_length);
	memcpy(r->get_pixel(0,y1), tmp_store, line_length);
    }
    delete [] tmp_store;
    return r;
}

Image *Image::extract_subimage(int xmin, int xmax, int ymin, int ymax, Image *r)
{ 
    
    // new implementation for base class
    // should be reasonably efficient
    
    if (r == NULL)
	r = copy_type(xmax-xmin+1, ymax-ymin+1);
    
    for (int y = ymin; y <= ymax; y++)
	memcpy(r->get_pixel(0,y-ymin), get_pixel(xmin,y), r->line_length);
    
    return r;
}

void Image::paste_subimage(unsigned int xmin, unsigned int xmax,
			   unsigned int ymin, unsigned int ymax, Image *subimg)
{
    // sanity checks
    assert (xmin < xmax);
    assert (ymin < ymax);
    assert (subimg != NULL);
    assert (check_coords(xmin,ymin) == true);
    assert (check_coords(xmax,ymax) == true);
    assert (get_image_type() == subimg->get_image_type());
    
//     if ((check_pixel(xmin,ymin) == NULL ) || 
// 	(check_pixel(xmax,ymax) == NULL) || 
// 	(xmin > xmax) || (ymin > ymax) || (get_image_type() != subimg->get_image_type()))
//     {
// 	cerror << " bad parameters to paste_subimage because "
// 	       << (void*) check_pixel(xmin,ymin) << " == NULL or "
// 	       << (void*) check_pixel(xmax,ymax) << " == NULL or "
// 	       << xmin << " > " << xmax << " or " << ymin << " > " << ymax
// 	       << " or " << get_image_type() << " != " << subimg->get_image_type()
// 	       << endl;
// 	exit(1);
//     }
    int nbytes = (xmax - xmin + 1) * bytes_per_pixel; 
    for (unsigned int y = ymin; y <= ymax; y++)
	memcpy(get_pixel(xmin,y),subimg->get_pixel(0,y-ymin), nbytes);
}

Image *Image::get_subimage(unsigned int xmin, unsigned int xmax,
			   unsigned int ymin, unsigned int ymax, Image *subimg)
{
    if (subimg == NULL)
	subimg = copy_type(xmax - xmin + 1, ymax - ymin + 1);
    else
	assert (get_image_type() == subimg->get_image_type());
	
    assert (xmin < xmax);
    assert (ymin < ymax);
    assert (check_coords(xmin,ymin) == true);
    assert (check_coords(xmax,ymax) == true);

//     if ((check_pixel(xmin,ymin) == NULL ) || 
// 	(check_pixel(xmax,ymax) == NULL) || 
// 	(xmin > xmax) || (ymin > ymax) || (image_type != subimg->get_image_type()))
//     {
// 	cerror << " Image::get_subimage: bad parameters. " << endl;
// 	exit(1);
//     }
    
    int nbytes = (xmax - xmin + 1) * bytes_per_pixel; 
    for (unsigned int y = ymin; y <= ymax; y++)
	memcpy(subimg->get_pixel(0,y-ymin),get_pixel(xmin,y), nbytes);
    return subimg;
}

unsigned int Image::measure_contrast(void *p1, void *p2)
{
    unsigned char *pix1 = (unsigned char*) p1;
    unsigned char *pix2 = (unsigned char*) p2;
    
    if ((pix1 == NULL) || (pix2 == NULL))
	return 0;
    
    unsigned int val1 = (unsigned int) *pix1;  
    unsigned int val2 = (unsigned int) *pix2;
    return abs(val1 - val2);
}

// the following VIRTUAL function implementations 
// do nothing but return error messages if not overridden/redefined

RGB32Image *Image::rgb_read_image(RGB32Image *res)
{
    cerror << " illegal call to Image::rgb_read_image " << endl
	   << " res = " << res << endl;
    abort();
}

Image *Image::fcombine(Image *image2, int (*func) (void*, void*), Image *r) 
{
    cerror << " illegal call to Image::fcombine. " << endl
	   << " image2 = " << image2 << ", func = " << func
	   << ", r = " << r << endl;
    abort();
}

Image *Image::diff_stats(Image *img2, realno &mean, realno &variance, Image *r) 
{ 
    cerror << "illegal call to Image::diff_stats " << endl;
    mean = variance = 0;
    abort();
}

Image *Image::image_blend(Image *image2, int a, int b, Image *r)
{
    cerror << "illegal call to Image::image_blend " << endl;
    abort();
}

Image *Image::map_intensities(PntGreyMap gmap, Image *r)
{
    cerror << "illegal call to Image::gamma_correct " << endl;
    abort();
}

// Image *Image::neighbour_order(unsigned int n, Image *r) 
// { 
//     cerror << "illegal call to Image::neighbour_order " << endl;
//     abort();
// }

Image *Image::fmed(Image *r) 
{ 
    cerror << "illegal call to Image::fmed " << endl;
    abort(); 
}

Image *Image::half_blur( Image *r) 
{ 
    cerror << "illegal call to Image::half_blur " << endl;
    abort();
}

Image *Image::ColourFilter(Image *res,
			   ColourFilteringMethod method)
{
    cerror << "illegal call to Image::ColourFilter " << endl;
    abort();
}

Image *Image::ColourDistance(Image *res,
			     ColourDistanceMethod)
{
    cerror << "illegal call to Image::ColourDistance " << endl;
    abort();
}


#ifdef HSV
Image *Image::to_HSV32(Image *res)
{
    cerror << "illegal call to Image::to_HSV32 " << endl;
    abort();
}
#endif


Image *Image::to_rgb(Image *r)
{
    cerror << "illegal call to Image::to_rgb " << endl;
    abort();
}


void Image::draw_rectangle(int xmin, int xmax, int ymin, int ymax, int val)
{
    cerror << "illegal call to Image::draw_rectangle " << endl;
    abort();
}


Image *Image::transform(int centre_x, int centre_y, realno angle,
                        realno scale, Image *r)
{
    cerror << "illegal call to Image::transform " << endl;
    abort();
}

// base class implementation assumes noise is to be added
// to each byte (i.e. each field is stored in a byte)

#ifdef _SVID_SOURCE
// the following uses SVID 48-bit random numbers...
static int set_seed = 0;
static int iset = 0;
static realno gset;

Image *Image::add_gaussian_noise(realno sd, realno &noise, Image *res)
{
    if (res == NULL)
	res = copy_type();
    if (set_seed == 0)
    {
	long rseed = time(NULL);
	srand48(rseed);
	set_seed = 1;
    }
    noise = 0;

    unsigned char *in_pnt = data;
    unsigned char *out_pnt = res->data;
    while (in_pnt < end_data)
    {
	realno fac, rsq, v1, v2, corrupt;
	if (iset == 0)
	{
	    do
	    {
		v1 = 2.0 * drand48() - 1.0;
		v2 = 2.0 * drand48() - 1.0;
		rsq = (v1 * v1) + (v2 * v2);
	    } while ((rsq >= 1.0 )|| (rsq == 0.0));
	    
	    fac = sqrt(-2.0 * log(rsq)/rsq);
	    gset = v1 * fac;
	    iset = 1;
	    corrupt = v2 * fac;
	}
	else
	{
	    iset = 0;
	    corrupt = gset;
	}
	
	int icorrupt = (int) (sd * corrupt + 0.5);
	int ipix = (int) *in_pnt++;
	int new_ipix = ipix + icorrupt;
	if (new_ipix > 255)
	    new_ipix = 255;
	if (new_ipix < 0)
	    new_ipix = 0;
	*out_pnt++ = new_ipix;
	noise += (new_ipix - ipix) * (new_ipix - ipix);
    }
    return res;
}
#endif // ifdef _SVID_SOURCE


// sum pixel values 
// assume each field is stored in a byte
// all fields are set

inline realno Image::sum_pixels()
{
    realno total = 0;
    unsigned char *pix_field = data;
    while (pix_field < end_data)
	total += (realno) (*pix_field++);
    return total;
}

// sum [I(x,y) - zero]^2

realno Image::sum_square_pixels(int zero)
{
    realno total = 0;
    unsigned char *pix_field = data;
    while (pix_field < end_data)
    {
	int ival = *pix_field++ - zero;
	total += (realno) (ival * ival);
    }
    
    return total;
}


void Image::median_img_array(Image **arr, int i)
{
    cerror << "illegal call to Image::median_img_array " << endl;
}


Image *Image::plane_project(const realno m00, const realno m01, const realno m02,
			    const realno m10, const realno m11, const realno m12,
			    const realno m20, const realno m21, const realno m22,
			    Image *res)
{
    if (res == NULL)
	res = copy_type();
    
    realno depth, back_x, back_y;
    
    int rw = res->get_width();
    int rh = res->get_height();
    
    for (int x = 0; x < rw; x++)
	for (int y = 0; y < rh; y++)
	{
	    depth = m20 * x +  m21 * y + m22;
	    back_x = (m00 * x + m01 * y + m02) / depth;
	    back_y = (m10 * x + m11 * y + m12) / depth;
	    
	    unsigned char *p1 = nearest_pixel(back_x, back_y);
	    if (p1 != NULL)
		memcpy(res->get_pixel(x,y), p1, bytes_per_pixel);
	    else
		memset(res->get_pixel(x,y), 0, bytes_per_pixel);
	}
    return res;
}

void Image::poly_delete(int i, int &nact, Edge *active)    
{
    int j;

    for (j=0; j=nact)
	return;

    nact--;

    memcpy(&active[j], &active[j+1], (nact-j)*sizeof(active[0]));
}

void Image::poly_insert(int i, int y, int nvert, int &nact, Edge *active)         
{
    int j;
    double dx;
    Point2 *p, *q;
    
    j = ix-p->x)/(q->y-p->y);
    active[nact].x = dx*(y+.5-p->y)+p->x;
    active[nact].i = i;
    nact++;
}

void Image::draw_filled_polygon(int n, Point2 *vertices)
{
    //
    //  Concave Polygon Scan Conversion
    //  by Paul Heckbert
    //  from "Graphics Gems", Academic Press, 1990
    //
    
    // modified by amb
    pt = vertices;
    
    Edge *active = new Edge[n];
    
    int win_x0 = 0, win_x1 = width-1;
    int win_y0 = 0, win_y1 = height-1;
    
    int y0, y1, i, j, xl, xr;
    int *ind = new int[n];
    int k;
    
    if (n<=0)
	return;

    for (k=0; k0) ? i-1 : n-1;	// vertex previous to i
	    if (pt[j].y <= y-.5)	// old edge, remove from active list
		poly_delete(j, nact, active);
	    else
		if (pt[j].y > y+.5)	// new edge, add to active list
		    poly_insert(j, y, n, nact, active);
	    j = (i y+.5)	// new edge, add to active list
		    poly_insert(i, y, n, nact, active);
	}
	
	// NB: nact at this point is usually 2, sometimes 0

	// if no edges, simply continue
	if (nact == 0)
	    continue;
	
	// sort active edge list by active[j].x
	qsort(active, nact, sizeof(active[0]), &compare_active);
	
	// draw horizontal segments for scanline y
	for (j=0; jwin_x1)
		xr = win_x1;
	    if (xl<=xr)
		draw_horizontal(y, xl, xr);
	    active[j].x += active[j].dx;	// increment edge coords
	    active[j+1].x += active[j+1].dx;
	}
    }
    delete [] ind;
    delete [] active;
    
}


void Image::draw_filled_circle(int x0, int y0, int r)
{
    int limit = 0;  
    int sigma;
    int x = 0;
    int y = r;
    int delta = (1 - r) << 1;
    while (y >= limit)
    {
	if (delta < 0)
	{
	    sigma = ((delta + y) << 1) - 1;
	    if (sigma > 0)
	    {
		draw_horizontal(y0+y, x0-x, x0+x);
		draw_horizontal(y0-y, x0-x, x0+x);
		x++;y--;
		delta += (x - y + 1) << 1;
	    }
	    else
	    {
		x++;
		delta += (x << 1) + 1;
	    }
	}
	else
	    if (delta > 0)
	    {
		sigma = ((delta - x) << 1) - 1;
		if (sigma > 0)
		{
		    draw_horizontal(y+y0, x0-x, x0+x);
		    draw_horizontal(y0-y, x0-x, x0+x);
		    y--;
		    delta += 1 - (y << 1);
		}
		else
		{
		    draw_horizontal(y+y0, x0-x, x0+x);
		    draw_horizontal(y0-y, x0-x, x0+x);
		    x++; y--;
		    delta += (x - y + 1) << 1;
		}
	    }
	    else
	    {
		draw_horizontal(y+y0, x0-x, x0+x);
		draw_horizontal(y0-y, x0-x, x0+x);
		x++;y--;
		delta += (x - y + 1) << 1;
	    }
	
    }
}

void Image::draw_line(int x0, int y0, int x1, int y1)
{    
    if (DrawLineWidth > 1)
    {      
	Point2 plist[4];
	realno delta_x, delta_y, dlength;
	delta_x = (realno) (x1 - x0);
	delta_y = (realno) (y1 - y0);
	dlength = sqrt((delta_x * delta_x) + (delta_y * delta_y));
	int r = (int) ((DrawLineWidth - 0.99) / 2);
	if (dlength > 0)
        {
	    realno dx = delta_x / dlength;
	    realno dy = delta_y / dlength;
	    realno nx = dy;
	    realno ny = -dx;
	    plist[0].x = x0 + 0.5 + ((r + 0.5) * nx) - (dx * 0.5);
	    plist[0].y = y0 + 0.5 + ((r + 0.5) * ny) - (dy * 0.5);
	    plist[1].x = x0 + 0.5 - ((r + 0.5) * nx) - (dx * 0.5);
	    plist[1].y = y0 + 0.5 - ((r + 0.5) * ny) - (dy * 0.5);
	    plist[2].x = x1 + 0.5 - ((r + 0.5) * nx) + (dx * 0.5);
	    plist[2].y = y1 + 0.5 - ((r + 0.5) * ny) + (dy * 0.5);
	    plist[3].x = x1 + 0.5 + ((r + 0.5) * nx) + (dx * 0.5);
	    plist[3].y = y1 + 0.5 + ((r + 0.5) * ny) + (dy * 0.5);
	    draw_filled_polygon(4, plist);
	}
	draw_filled_circle(x0,y0,r);	  
	draw_filled_circle(x1,y1,r);        
	return;
    }
    
    int d;
    int x;
    int y;
    int ax;
    int ay;
    int sx;
    int sy;
    int dx;
    int dy;
    
    dx = x1-x0;  ax = ABS(dx)<<1;  sx = SGN(dx);
    dy = y1-y0;  ay = ABS(dy)<<1;  sy = SGN(dy);
    
    if ((dx == 0) && (dy == 0))
	return;
    x = x0;
    y = y0;
    if (ax>ay)
    {                // x dominant
        d = ay-(ax>>1);
        for (;;)
	{
	    plot_pixel(x,y);
            if (x==x1)
		return;
            if (d>=0)
	    {
                y += sy;
                d -= ax;
            }
            x += sx;
            d += ay;
        }
    }
    else
    {                      // y dominant
        d = ax-(ay>>1);
        for (;;)
	{
	    plot_pixel(x,y);
            if (y==y1)
		return;
            if (d>=0)
	    {
                x += sx;
                d -= ay;
            }
            y += sy;
            d += ax;
        }
    }
}

void Image::draw_circle(int x0, int y0, int r)
{
    static const realno cth = cos(M_PI_2 / ((realno) CIRCLE_PRECISION));
    static const realno sth = sin(M_PI_2 / ((realno) CIRCLE_PRECISION));
    int i;
    
    if (DrawLineWidth > 1)
    {
	Point2 *pnt_data = new Point2[CIRCLE_PRECISION*8+2];
	Point2 *big_circle = pnt_data;
	Point2 *small_circle = &pnt_data[CIRCLE_PRECISION*4+1];
	
	realno x_big = 0; realno y_big = r + 0.5;
	realno x_small = 0; realno y_small = (r  - DrawLineWidth);
	Point2 origin(x0+0.5, y0+0.5);
	for (i = 0; i < CIRCLE_PRECISION; i++)
	{
	    big_circle[i] = Point2(x_big, y_big);
	    big_circle[i+CIRCLE_PRECISION] = Point2(y_big, -x_big);
	    big_circle[i+2*CIRCLE_PRECISION] = Point2(-x_big, -y_big);
	    big_circle[i+3*CIRCLE_PRECISION] = Point2(-y_big, x_big);
	    small_circle[i] = Point2(x_small, y_small);
	    small_circle[i+CIRCLE_PRECISION] = Point2(y_small, -x_small);
	    small_circle[i+2*CIRCLE_PRECISION] = Point2(-x_small, -y_small);
	    small_circle[i+3*CIRCLE_PRECISION] = Point2(-y_small, x_small);
	    realno new_x_big = cth * x_big + sth * y_big;
	    realno new_y_big = -sth * x_big + cth * y_big;
	    x_big = new_x_big; y_big = new_y_big;
	    realno new_x_small = cth * x_small + sth * y_small;
	    realno new_y_small = -sth * x_small + cth * y_small;
	    x_small = new_x_small; y_small = new_y_small;
	}
	big_circle[4*CIRCLE_PRECISION] = big_circle[0];
	small_circle[4*CIRCLE_PRECISION] = small_circle[0];
	for (i = 0; i < (8 * CIRCLE_PRECISION + 2); i++)
	    pnt_data[i] = pnt_data[i] + origin;
	
	draw_filled_polygon(8*CIRCLE_PRECISION+2, pnt_data);
	delete [] pnt_data;
	return;
    }
    
    register int x = 0;
    register int y = r;
    register int delta = ( 1 - r) << 1;
    int limit = 0;  
    int sigma;
    while (y >= limit)
    {
	plot_pixel(x0-x,y0+y);
	plot_pixel(x0+x,y0+y);
	plot_pixel(x0-x,y0-y);
	plot_pixel(x0+x,y0-y);
	if (delta < 0)
	{
	    sigma = ((delta + y) << 1) - 1;
	    if (sigma > 0)
	    {
		x++;y--;
		delta += (x - y + 1) << 1;
	    }
	    else
	    {
		x++;
		delta += (x << 1) + 1;
	    }
	}
	else if (delta > 0)
	{
	    sigma = ((delta - x) << 1) - 1;
	    if (sigma > 0)
	    {
		y--;
		delta += 1 - (y << 1);
	    }
	    else
	    {
		x++; y--;
		delta += (x - y + 1) << 1;
	    }
	}
	else
	{
	    x++; y--;
	    delta += (x - y + 1) << 1;
	}
	
    }
}

} // namespace ReadingPeopleTracker