Ejemplo n.º 1
0
int main()
{
	gdImagePtr im;
	FILE *fp;
	int cor_rad = 60;
	im = gdImageCreateTrueColor(400, 400);
	gdImageFilledRectangle(im, 0, 0, 399, 399, 0x00FFFFFF);

	gdImageFilledArc (im, cor_rad, 399 - cor_rad, cor_rad *2, cor_rad *2, 90, 180, 0x0, gdPie);

	fp = fopen("b.png", "wb");
	if (!fp) {
		fprintf(stderr, "Can't save png image.\n");
		gdImageDestroy(im);
		return 1;
	}
#ifdef HAVE_LIBPNG
	gdImagePng(im, fp);
#else
	printf("No PNG support. Cannot save image.\n");
#endif
	fclose(fp);

	gdImageDestroy(im);
	return 0;
}
Ejemplo n.º 2
0
int 
main (int argc, char *argv[])
{
  gdImagePtr im = gdImageCreate (WIDTH, HEIGHT);
  int white = gdImageColorResolve (im, 0xFF, 0xFF, 0xFF), black = gdImageColorResolve (im, 0, 0, 0),
    red = gdImageColorResolve (im, 0xFF, 0xA0, 0xA0);
  FILE *out;

  /* filled arc - circle */
  gdImageFilledArc (im, WIDTH / 5, HEIGHT / 4, 200, 200, 45, 90, red, gdPie);
  gdImageArc (im, WIDTH / 5, HEIGHT / 4, 200, 200, 45, 90, black);

  /* filled arc - ellipse */
  gdImageFilledArc (im, WIDTH / 2, HEIGHT / 4, 200, 150, 45, 90, red, gdPie);
  gdImageArc (im, WIDTH / 2, HEIGHT / 4, 200, 150, 45, 90, black);


  /* reference lines */
  gdImageLine (im, 0, HEIGHT / 4, WIDTH, HEIGHT / 4, black);
  gdImageLine (im, WIDTH / 5, 0, WIDTH / 5, HEIGHT, black);
  gdImageLine (im, WIDTH / 2, 0, WIDTH / 2, HEIGHT, black);
  gdImageLine (im, WIDTH / 2, HEIGHT / 4, WIDTH / 2 + 300, HEIGHT / 4 + 300, black);
  gdImageLine (im, WIDTH / 5, HEIGHT / 4, WIDTH / 5 + 300, HEIGHT / 4 + 300, black);

  /* TBB: Write img to test/arctest.png */
  out = fopen ("test/arctest.png", "wb");
  if (!out)
    {
      fprintf (stderr, "Can't create test/arctest.png\n");
      exit (1);
    }
  gdImagePng (im, out);
  fclose (out);
  fprintf (stderr, "Test image written to test/arctest.png\n");
  /* Destroy it */
  gdImageDestroy (im);

  return 0;
}
Ejemplo n.º 3
0
result_t Image::filledArc(int32_t x, int32_t y, int32_t width, int32_t height,
                          int32_t start, int32_t end, int32_t color, int32_t style)
{
    if (width <= 0 || height <= 0)
        return CHECK_ERROR(CALL_E_INVALIDARG);

    if (!m_image)
        return CHECK_ERROR(CALL_E_INVALID_CALL);

    if (start < 0 || start > 360 || end < 0 || end > 360)
        return CHECK_ERROR(CALL_E_INVALIDARG);

    gdImageFilledArc(m_image, x, y, width, height, start, end, color, style);
    return 0;
}
Ejemplo n.º 4
0
void doFilledArc(FILE *stream) {
  float cx, cy, w, h, s;
  int e, c, style;

  cx = getFloat(stream);  // center x
  cy = getFloat(stream);  // center y
  w = getFloat(stream);   // width
  h = getFloat(stream);   // height
  s = getNumber(stream);   // start angle
  e = getNumber(stream);   // end angle
  c = getColor(getNumber(stream));   // color
  style = getNumber(stream);

  gdImageFilledArc(image, viewx(cx), viewy(cy), 
		   scalex(w), scaley(h), s, e, c, style);
}
Ejemplo n.º 5
0
int main()
{
	gdImagePtr im;
	int transparent, color;

	im = gdImageCreateTrueColor(100, 100);
	transparent = gdImageColorAllocateAlpha(im, 255, 255, 255, 80);
	gdImageFilledRectangle(im, 0,0, 99,99, transparent);
	color = gdImageColorAllocateAlpha(im, 0, 255, 0, 100);
	gdImageFilledArc(im, 49,49, 99,99, 0,360, color, gdPie);

	gdAssertImageEqualsToFile("gdimagefilledarc/php_bug43828_exp.png", im);

	gdImageDestroy(im);
	return gdNumFailures();
}
Ejemplo n.º 6
0
void graficotorta(int n, long cantidad[], int estado[]) {

    gdImagePtr imagen;
    FILE *archivo;
    char titulo[513];
    char etiqueta[512];
    int blanco, negro, color;
    gdFontPtr fuente = gdFontGetSmall();
    
    imagen = gdImageCreateTrueColor(IMG_WIDTH, IMG_HEIGHT);

    int i;
    int angEtiqueta, xEtiqueta, yEtiqueta;
    float eqgrados;
    int iniciotrozo, fintrozo;
    long suma = 0;
    int aprox;
    for (i=0; i<n; i++) {    
        suma = suma + cantidad[i];
    }
    eqgrados = 360.0/suma;
    
    if (imagen) {
        blanco = gdImageColorAllocate(imagen, 255, 255, 255);
        negro = gdImageColorAllocate(imagen, 0, 0, 0);
        // Pintamos el fondo Blanco
        gdImageFill(imagen, 0, 0, blanco);
        iniciotrozo = 0;
        for (i=0; i<n; i++) {
            fintrozo = (int) iniciotrozo+cantidad[i]*eqgrados;
            angEtiqueta = (iniciotrozo + fintrozo) / 2;
            xEtiqueta = cos(angEtiqueta*PI/180) * 220 + 400;
            yEtiqueta = sin(angEtiqueta*PI/180) * 220 + 290;
            // Color
            color = gdImageColorAllocate(imagen, color_aleatoreo(), color_aleatoreo(), color_aleatoreo());
            //Pintamos fondo el trozo
            gdImageFilledArc(imagen, 400, 300, 400, 400, iniciotrozo, fintrozo, color, gdArc);       
            //Etiqueta de peticion
            memset(etiqueta, 0, 513);
            snprintf(etiqueta, 512, "peticion %s",intStr(estado[i]));
            gdImageString(imagen, fuente, xEtiqueta-25, yEtiqueta, (unsigned char *) etiqueta, negro);
            //Correccion de aproximacion para el porcentaje
            aprox = cantidad[i] * 1000 / suma;
            aprox = aprox%10;
            if (aprox>=5)
                aprox = 1;
            else
                aprox = 0;
            //Etiqueta de porcentaje
            memset(etiqueta, 0, 513);
            snprintf(etiqueta, 512, "%s%%",longStr(cantidad[i]*100/suma+aprox));
            if (cantidad[i]*100/suma<3){      //Para que la etiqueta sea legible
                xEtiqueta = xEtiqueta + 52;
                yEtiqueta = yEtiqueta - 15;
            }
            gdImageString(imagen, fuente, xEtiqueta, yEtiqueta+15, (unsigned char *) etiqueta, negro);
            iniciotrozo = (int) iniciotrozo+cantidad[i]*eqgrados;
        }

        //Pintamos borde del circulo
        gdImageArc(imagen, 400, 300, 400, 400, 0, 360, negro);
        // Coloco el título
        memset(titulo, 0, 513);
        snprintf(titulo, 512, "Peticiones Por Estado");
        gdImageString(imagen, fuente, (int) IMG_WIDTH * 0.4, 25, (unsigned char *) titulo, negro);
        // Pintamos Borde
        gdImageLine(imagen, BORDE_ANCHO, BORDE_ALTO, (IMG_WIDTH - BORDE_ANCHO), BORDE_ALTO, negro); //linea horiz superior
        gdImageLine(imagen, BORDE_ANCHO, (IMG_HEIGHT - BORDE_ALTO), (IMG_WIDTH - BORDE_ANCHO), (IMG_HEIGHT - BORDE_ALTO), negro); //linea horiz inferior
        gdImageLine(imagen, BORDE_ANCHO, BORDE_ALTO, BORDE_ANCHO, (IMG_HEIGHT - BORDE_ALTO), negro); //linea vertical izquierda
        gdImageLine(imagen, (IMG_WIDTH - BORDE_ANCHO), BORDE_ALTO, (IMG_WIDTH - BORDE_ANCHO), (IMG_HEIGHT - BORDE_ALTO), negro); //linea horiz derecha
        // Guardar imagen
        archivo = fopen("graficot.jpg", "wb");
        if (archivo != NULL) {
            gdImageJpeg(imagen, archivo, 100);
            fclose(archivo);
        }
        gdImageDestroy(imagen);
    }
 
}
Ejemplo n.º 7
0
void
gdImageFilledArc (gdImagePtr im, int cx, int cy, int width, int height, int s, int e, int color, int style)
{
  gdPoint pt[7];
  gdPoint axis_pt[4];

  int angle;

  int have_s = 0;
  int have_e = 0;

  int flip_x = 0;
  int flip_y = 0;

  int conquer = 0;

  int i;

  int a;
  int b;

  int x;
  int y;

  long s_sin = 0;
  long s_cos = 0;
  long e_sin = 0;
  long e_cos = 0;

  long w;			/* a * 2 */
  long h;			/* b * 2 */

  long x2;			/* x * 2 */
  long y2;			/* y * 2 */
  long lx2;			/* x * 2 (line) */
  long ly2;			/* y * 2 (line) */

  long ws;			/* (a * 2)^2 */
  long hs;			/* (b * 2)^2 */

  long whs;			/* (a * 2)^2 * (b * 2)^2 */

  long g;			/* decision variable */
  long lg;			/* decision variable (line) */

  width = (width & 1) ? (width + 1) : (width);
  height = (height & 1) ? (height + 1) : (height);

  a = width / 2;
  b = height / 2;

  axis_pt[0].x = a;
  axis_pt[0].y = 0;
  axis_pt[1].x = 0;
  axis_pt[1].y = b;
  axis_pt[2].x = -a;
  axis_pt[2].y = 0;
  axis_pt[3].x = 0;
  axis_pt[3].y = -b;

  if (s == e)
    return;

  if ((e - s) >= 360)
    {
      s = 0;
      e = 0;
    }

  while (s < 0)
    s += 360;
  while (s >= 360)
    s -= 360;
  while (e < 0)
    e += 360;
  while (e >= 360)
    e -= 360;

  if (e <= s)
    e += 360;

  /* I'm assuming a chord-rule at the moment. Need to add origin to get a
   * pie-rule, but will need to set chord-rule before recursion...
   */

  for (i = 0; i < 4; i++)
    {
      if ((s < (i + 1) * 90) && (e > (i + 1) * 90))
	{
	  gdImageFilledArc (im, cx, cy, width, height, s, (i + 1) * 90, color, gdChord);
	  pt[0] = gdArcClosest (width, height, s);
	  pt[0].x += cx;
	  pt[0].y += cy;
	  pt[1].x = cx + axis_pt[(i + 1) & 3].x;
	  pt[1].y = cy + axis_pt[(i + 1) & 3].y;
	  if (e <= (i + 2) * 90)
	    {
	      gdImageFilledArc (im, cx, cy, width, height, (i + 1) * 90, e, color, gdChord);
	      pt[2] = gdArcClosest (width, height, e);
	      pt[2].x += cx;
	      pt[2].y += cy;
	      if (style == gdChord)
		{
		  gdImageFilledPolygon (im, pt, 3, color);
		  gdImagePolygon (im, pt, 3, color);
		}
	      else if (style == gdPie)
		{
		  pt[3].x = cx;
		  pt[3].y = cy;
		  gdImageFilledPolygon (im, pt, 4, color);
		  gdImagePolygon (im, pt, 4, color);
		}
	    }
	  else
	    {
	      gdImageFilledArc (im, cx, cy, width, height, (i + 1) * 90, (i + 2) * 90, color, gdChord);
	      pt[2].x = cx + axis_pt[(i + 2) & 3].x;
	      pt[2].y = cy + axis_pt[(i + 2) & 3].y;
	      if (e <= (i + 3) * 90)
		{
		  gdImageFilledArc (im, cx, cy, width, height, (i + 2) * 90, e, color, gdChord);
		  pt[3] = gdArcClosest (width, height, e);
		  pt[3].x += cx;
		  pt[3].y += cy;
		  if (style == gdChord)
		    {
		      gdImageFilledPolygon (im, pt, 4, color);
		      gdImagePolygon (im, pt, 4, color);
		    }
		  else if (style == gdPie)
		    {
		      pt[4].x = cx;
		      pt[4].y = cy;
		      gdImageFilledPolygon (im, pt, 5, color);
		      gdImagePolygon (im, pt, 5, color);
		    }
		}
	      else
		{
		  gdImageFilledArc (im, cx, cy, width, height, (i + 2) * 90, (i + 3) * 90, color, gdChord);
		  pt[3].x = cx + axis_pt[(i + 3) & 3].x;
		  pt[3].y = cy + axis_pt[(i + 3) & 3].y;
		  if (e <= (i + 4) * 90)
		    {
		      gdImageFilledArc (im, cx, cy, width, height, (i + 3) * 90, e, color, gdChord);
		      pt[4] = gdArcClosest (width, height, e);
		      pt[4].x += cx;
		      pt[4].y += cy;
		      if (style == gdChord)
			{
			  gdImageFilledPolygon (im, pt, 5, color);
			  gdImagePolygon (im, pt, 5, color);
			}
		      else if (style == gdPie)
			{
			  pt[5].x = cx;
			  pt[5].y = cy;
			  gdImageFilledPolygon (im, pt, 6, color);
			  gdImagePolygon (im, pt, 6, color);
			}
		    }
		  else
		    {
		      gdImageFilledArc (im, cx, cy, width, height, (i + 3) * 90, (i + 4) * 90, color, gdChord);
		      pt[4].x = cx + axis_pt[(i + 4) & 3].x;
		      pt[4].y = cy + axis_pt[(i + 4) & 3].y;

		      gdImageFilledArc (im, cx, cy, width, height, (i + 4) * 90, e, color, gdChord);
		      pt[5] = gdArcClosest (width, height, e);
		      pt[5].x += cx;
		      pt[5].y += cy;
		      if (style == gdChord)
			{
			  gdImageFilledPolygon (im, pt, 6, color);
			  gdImagePolygon (im, pt, 6, color);
			}
		      else if (style == gdPie)
			{
			  pt[6].x = cx;
			  pt[6].y = cy;
			  gdImageFilledPolygon (im, pt, 7, color);
			  gdImagePolygon (im, pt, 7, color);
			}
		    }
		}
	    }
	  return;
	}
    }

  /* At this point we have only arcs that lies within a quadrant -
   * map this to first quadrant...
   */

  if ((s >= 90) && (e <= 180))
    {
      angle = s;
      s = 180 - e;
      e = 180 - angle;
      flip_x = 1;
    }
  if ((s >= 180) && (e <= 270))
    {
      s = s - 180;
      e = e - 180;
      flip_x = 1;
      flip_y = 1;
    }
  if ((s >= 270) && (e <= 360))
    {
      angle = s;
      s = 360 - e;
      e = 360 - angle;
      flip_y = 1;
    }

  if (s == 0)
    {
      s_sin = 0;
      s_cos = (long) ((double) 32768);
    }
  else
    {
      s_sin = (long) ((double) 32768 * sin ((double) s * M_PI / (double) 180));
      s_cos = (long) ((double) 32768 * cos ((double) s * M_PI / (double) 180));
    }
  if (e == 0)
    {
      e_sin = (long) ((double) 32768);
      e_cos = 0;
    }
  else
    {
      e_sin = (long) ((double) 32768 * sin ((double) e * M_PI / (double) 180));
      e_cos = (long) ((double) 32768 * cos ((double) e * M_PI / (double) 180));
    }

  w = (long) width;
  h = (long) height;

  ws = w * w;
  hs = h * h;

  whs = 1;
  while ((ws > 32768) || (hs > 32768))
    {
      ws = (ws + 1) / 2;	/* Unfortunate limitations on integers makes */
      hs = (hs + 1) / 2;	/* drawing large  ellipses problematic...    */
      whs *= 2;
    }
  while ((ws * hs) > (0x04000000L / whs))
    {
      ws = (ws + 1) / 2;
      hs = (hs + 1) / 2;
      whs *= 2;
    }
  whs *= ws * hs;

  pt[0].x = w / 2;
  pt[0].y = 0;

  pt[2].x = 0;
  pt[2].y = h / 2;

  have_s = 0;
  have_e = 0;

  if (s == 0)
    have_s = 1;
  if (e == 90)
    have_e = 1;

  x2 = w;
  y2 = 0;			/* Starting point is exactly on ellipse */

  g = x2 - 1;
  g = g * g * hs + 4 * ws - whs;

  while ((x2 * hs) > (y2 * ws))	/* Keep |tangent| > 1 */
    {
      y2 += 2;
      g += ws * 4 * (y2 + 1);

      if (g > 0)		/* Need to drop */
	{
	  x2 -= 2;
	  g -= hs * 4 * x2;
	}

      if ((have_s == 0) && ((s_sin * x2) <= (y2 * s_cos)))
	{
	  pt[0].x = (int) (x2 / 2);
	  pt[0].y = (int) (y2 / 2);
	  have_s = 1;
	}

      if ((have_e == 0) && ((e_sin * x2) <= (y2 * e_cos)))
	{
	  pt[2].x = (int) (x2 / 2);
	  pt[2].y = (int) (y2 / 2);
	  have_e = 1;
	}
    }
  pt[1].x = (int) (x2 / 2);
  pt[1].y = (int) (y2 / 2);

  x2 = 0;
  y2 = h;			/* Starting point is exactly on ellipse */

  g = y2 - 1;
  g = g * g * ws + 4 * hs - whs;

  while ((x2 * hs) < (y2 * ws))
    {
      x2 += 2;
      g += hs * 4 * (x2 + 1);

      if (g > 0)		/* Need to drop */
	{
	  y2 -= 2;
	  g -= ws * 4 * y2;
	}

      if ((have_s == 0) && ((s_sin * x2) >= (y2 * s_cos)))
	{
	  pt[0].x = (int) (x2 / 2);
	  pt[0].y = (int) (y2 / 2);
	  have_s = 1;
	}

      if ((have_e == 0) && ((e_sin * x2) >= (y2 * e_cos)))
	{
	  pt[2].x = (int) (x2 / 2);
	  pt[2].y = (int) (y2 / 2);
	  have_e = 1;
	}
    }

  if ((have_s == 0) || (have_e == 0))
    return;			/* Bizarre case */

  if (style == gdPie)
    {
      pt[3] = pt[0];
      pt[4] = pt[1];
      pt[5] = pt[2];

      pt[0].x = cx + (flip_x ? (-pt[0].x) : pt[0].x);
      pt[0].y = cy + (flip_y ? (-pt[0].y) : pt[0].y);
      pt[1].x = cx;
      pt[1].y = cy;
      pt[2].x = cx + (flip_x ? (-pt[2].x) : pt[2].x);
      pt[2].y = cy + (flip_y ? (-pt[2].y) : pt[2].y);
      gdImageFilledPolygon (im, pt, 3, color);
      gdImagePolygon (im, pt, 3, color);

      pt[0] = pt[3];
      pt[1] = pt[4];
      pt[2] = pt[5];
    }

  if (((s_cos * hs) > (s_sin * ws)) && ((e_cos * hs) < (e_sin * ws)))
    {				/* the points are on different parts of the curve...
				 * this is too tricky to try to handle, so divide and conquer:
				 */
      pt[3] = pt[0];
      pt[4] = pt[1];
      pt[5] = pt[2];

      pt[0].x = cx + (flip_x ? (-pt[0].x) : pt[0].x);
      pt[0].y = cy + (flip_y ? (-pt[0].y) : pt[0].y);
      pt[1].x = cx + (flip_x ? (-pt[1].x) : pt[1].x);
      pt[1].y = cy + (flip_y ? (-pt[1].y) : pt[1].y);
      pt[2].x = cx + (flip_x ? (-pt[2].x) : pt[2].x);
      pt[2].y = cy + (flip_y ? (-pt[2].y) : pt[2].y);
      gdImageFilledPolygon (im, pt, 3, color);
      gdImagePolygon (im, pt, 3, color);

      pt[0] = pt[3];
      pt[2] = pt[4];

      conquer = 1;
    }

  if (conquer || (((s_cos * hs) > (s_sin * ws)) && ((e_cos * hs) > (e_sin * ws))))
    {				/* This is the best bit... */
      /* steep line + ellipse */
      /* go up & left from pt[0] to pt[2] */

      x2 = w;
      y2 = 0;			/* Starting point is exactly on ellipse */

      g = x2 - 1;
      g = g * g * hs + 4 * ws - whs;

      while ((x2 * hs) > (y2 * ws))	/* Keep |tangent| > 1 */
	{
	  if ((s_sin * x2) <= (y2 * s_cos))
	    break;

	  y2 += 2;
	  g += ws * 4 * (y2 + 1);

	  if (g > 0)		/* Need to drop */
	    {
	      x2 -= 2;
	      g -= hs * 4 * x2;
	    }
	}

      lx2 = x2;
      ly2 = y2;

      lg = lx2 * (pt[0].y - pt[2].y) - ly2 * (pt[0].x - pt[2].x);
      lg = (lx2 - 1) * (pt[0].y - pt[2].y) - (ly2 + 2) * (pt[0].x - pt[2].x) - lg;

      while (y2 < (2 * pt[2].y))
	{
	  y2 += 2;
	  g += ws * 4 * (y2 + 1);

	  if (g > 0)		/* Need to drop */
	    {
	      x2 -= 2;
	      g -= hs * 4 * x2;
	    }

	  ly2 += 2;
	  lg -= 2 * (pt[0].x - pt[2].x);

	  if (lg < 0)		/* Need to drop */
	    {
	      lx2 -= 2;
	      lg -= 2 * (pt[0].y - pt[2].y);
	    }

	  y = (int) (y2 / 2);
	  for (x = (int) (lx2 / 2); x <= (int) (x2 / 2); x++)
	    {
	      gdImageSetPixel (im, ((flip_x) ? (cx - x) : (cx + x)),
			       ((flip_y) ? (cy - y) : (cy + y)), color);
	    }
	}
    }
  if (conquer)
    {
      pt[0] = pt[4];
      pt[2] = pt[5];
    }
  if (conquer || (((s_cos * hs) < (s_sin * ws)) && ((e_cos * hs) < (e_sin * ws))))
    {				/* This is the best bit... */
      /* gradual line + ellipse */
      /* go down & right from pt[2] to pt[0] */

      x2 = 0;
      y2 = h;			/* Starting point is exactly on ellipse */

      g = y2 - 1;
      g = g * g * ws + 4 * hs - whs;

      while ((x2 * hs) < (y2 * ws))
	{
	  x2 += 2;
	  g += hs * 4 * (x2 + 1);

	  if (g > 0)		/* Need to drop */
	    {
	      y2 -= 2;
	      g -= ws * 4 * y2;
	    }

	  if ((e_sin * x2) >= (y2 * e_cos))
	    break;
	}

      lx2 = x2;
      ly2 = y2;

      lg = lx2 * (pt[0].y - pt[2].y) - ly2 * (pt[0].x - pt[2].x);
      lg = (lx2 + 2) * (pt[0].y - pt[2].y) - (ly2 - 1) * (pt[0].x - pt[2].x) - lg;

      while (x2 < (2 * pt[0].x))
	{
	  x2 += 2;
	  g += hs * 4 * (x2 + 1);

	  if (g > 0)		/* Need to drop */
	    {
	      y2 -= 2;
	      g -= ws * 4 * y2;
	    }

	  lx2 += 2;
	  lg += 2 * (pt[0].y - pt[2].y);

	  if (lg < 0)		/* Need to drop */
	    {
	      ly2 -= 2;
	      lg += 2 * (pt[0].x - pt[2].x);
	    }

	  x = (int) (x2 / 2);
	  for (y = (int) (ly2 / 2); y <= (int) (y2 / 2); y++)
	    {
	      gdImageSetPixel (im, ((flip_x) ? (cx - x) : (cx + x)),
			       ((flip_y) ? (cy - y) : (cy + y)), color);
	    }
	}
    }
}
Ejemplo n.º 8
0
void
gdImageFilledEllipse (gdImagePtr im, int cx, int cy, int width, int height, int color)
{
  gdImageFilledArc (im, cx, cy, width, height, 0, 360, color, gdChord);
}
Ejemplo n.º 9
0
void CarGPS::draw_readout(struct gpf_data *data)
{
   logger->log(DEBUG, "CarGPS::draw_readout", "start");

   char str[128];
   char *f = conf->get_value("font");
   int white = gdTrueColor(250,250,250);
   int gray = gdTrueColor(200, 250, 200);
   int black = gdTrueColor(0,0,0);
   int cx, cy;

   // time
   int time = (int)data->time;
   int hour = time/10000;
   int minute  = time/100 - (hour*100);
   int second = time - (hour*10000) - (minute*100);
   sprintf(str, "Time: %.2d:%.2d:%.2d UTC", hour, minute, second); 
   gdImageFilledRectangle(img, 0, height - 80, width, height, black);
   draw_string(0, height-40, width, 40, white, f, 18, str);

   // position
   sprintf(str, "Position: %.2f %c, %.2f %c", data->latitude/100.0, 
      data->latitude_ns, data->longitude/100.0, data->longitude_ew);
   gdImageFilledRectangle(img, 0, 0, width, 40, black);
   draw_string(0, 0, width, 40, white, f, 18, str);

   // altitude
   sprintf(str, "Altitude: %d m", data->altitude); 
   //draw_string(0, 150, width, 30, white, f, 18, str);

   // altitude
   sprintf(str, "Satellites in view: %d", data->num_satellites); 
   //draw_string(0, 200, width, 30, white, f, 18, str);

   // draw the new satellite map only if new data is available
   if (data->new_info) 
   {
      gdImageFilledRectangle(img, 0, 40, width, height-80, black);

      //initialize useful variables
      cx = width / 2;
      cy = height / 2;

      // draw the concentric circles
      gdImageArc(img, cx, cy, 100, 100, 0, 360, gray);
      gdImageArc(img, cx, cy, 200, 200, 0, 360, gray);
      gdImageArc(img, cx, cy, 300, 300, 0, 360, gray);

      // crosshair
      gdImageLine(img, cx-150, cy, cx+150, cy, gray);
      gdImageLine(img, cx, cy-150, cx, cy+150, gray);

      // label the directions
      draw_string(cx-20, cy-185, 40, 40, gray, f, 14, "N");
      draw_string(cx+150, cy-20, 40, 40, gray, f, 14, "E");
      draw_string(cx-20, cy+150, 40, 40, gray, f, 14, "S");
      draw_string(cx-185, cy-20, 40, 40, gray, f, 14, "W");

      // draw the satellites
      int x, y;
      float r, theta;

      for (int i = 0; i < data->num_satellites; i++)
      {
         r = 150.0 - (float)data->satellite[i].elevation * 150.0 / 90.0;
         theta = (float)(data->satellite[i].azimuth-90) * 2.0 * 3.14159 / 360.0;

         x = (int)(r * cos(theta)) + cx;
         y = (int)(r * sin(theta)) + cy;

         sprintf(str, "%d", data->satellite[i].id);
         gdImageFilledArc(img, x, y, 5, 5, 0, 360, white, gdArc);
         draw_string(x, y, 18, 18, gray, f, 10, str);
      }
   }

   update();
   logger->log(DEBUG, "CarGPS::draw_readout", "end");
}