Logo Search packages:      
Sourcecode: opencascade version File versions  Download package

OpenGl_gl2ps.c

/* $Id: gl2ps.c,v 1.177 2004/07/13 15:59:49 geuzaine Exp $ */
/*
 * GL2PS, an OpenGL to PostScript Printing Library
 * Copyright (C) 1999-2004 Christophe Geuzaine <geuz@geuz.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of either:
 *
 * a) the GNU Library General Public License as published by the Free
 * Software Foundation, either version 2 of the License, or (at your
 * option) any later version; or
 *
 * b) the GL2PS License as published by Christophe Geuzaine, either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See either
 * the GNU Library General Public License or the GL2PS License for
 * more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library in the file named "COPYING.LGPL";
 * if not, write to the Free Software Foundation, Inc., 675 Mass Ave,
 * Cambridge, MA 02139, USA.
 *
 * You should have received a copy of the GL2PS License with this
 * library in the file named "COPYING.GL2PS"; if not, I will be glad
 * to provide one.
 *
 * Contributors:
 *   Michael Sweet <mike@easysw.com>
 *   Marc Ume <marc.ume@digitalgraphics.be>
 *   Jean-Francois Remacle <remacle@gce.ucl.ac.be>
 *   Bart Kaptein <B.L.Kaptein@lumc.nl>
 *   Quy Nguyen-Dai <quy@nguyendai.org>
 *   Sam Buss <sbuss@ucsd.edu>
 *   Shane Hill <Shane.Hill@dsto.defence.gov.au>
 *   Romain Boman <r_boman@yahoo.fr>
 *   Rouben Rostamian <rostamian@umbc.edu>
 *   Diego Santa Cruz <Diego.SantaCruz@epfl.ch>
 *   Shahzad Muzaffar <Shahzad.Muzaffar@cern.ch>
 *   Lassi Tuura <lassi.tuura@cern.ch>
 *   Guy Barrand <barrand@lal.in2p3.fr>
 *   Prabhu Ramachandran <prabhu@aero.iitm.ernet.in>
 *   Micha Bieber <bieber@traits.de>
 *
 * For the latest info about gl2ps, see http://www.geuz.org/gl2ps/.
 * Please report all bugs and problems to <gl2ps@geuz.org>.
 */

#include <string.h>
#include <sys/types.h>
#include <stdarg.h>
#include <time.h>
#include <float.h>
/*#include <Standard_Macro.hxx>*/

#include <OpenGl_gl2ps.h>

/********************************************************************* 
 *
 * Private definitions, data structures and prototypes
 *
 *********************************************************************/

/* Magic numbers */

/*#define GL2PS_EPSILON             5.0e-3F
#define GL2PS_EPSILON             5.0e-2F*/
#define GL2PS_DEPTH_FACT          1000.0F
#define GL2PS_SIMPLE_OFFSET       0.05F
#define GL2PS_SIMPLE_OFFSET_LARGE 1.0F
#define GL2PS_ZERO(arg)           (fabs(arg)<1.e-20)
#define GL2PS_FIXED_XREF_ENTRIES  7 

/* Primitive types */

#define GL2PS_NOTYPE          -1
#define GL2PS_TEXT             1
#define GL2PS_POINT            2
#define GL2PS_LINE             3
#define GL2PS_QUADRANGLE       4
#define GL2PS_TRIANGLE         5
#define GL2PS_PIXMAP           6
#define GL2PS_IMAGEMAP         7
#define GL2PS_IMAGEMAP_WRITTEN 8
#define GL2PS_IMAGEMAP_VISIBLE 9


/* BSP tree primitive comparison */

#define GL2PS_COINCIDENT  1
#define GL2PS_IN_FRONT_OF 2
#define GL2PS_IN_BACK_OF  3
#define GL2PS_SPANNING    4

/* 2D BSP tree primitive comparison */

#define GL2PS_POINT_COINCIDENT 0
#define GL2PS_POINT_INFRONT    1
#define GL2PS_POINT_BACK       2

typedef enum {
  T_UNDEFINED    = -1,
  T_CONST_COLOR  = 1,
  T_VAR_COLOR    = 1<<1,
  T_ALPHA_1      = 1<<2,
  T_ALPHA_LESS_1 = 1<<3,
  T_VAR_ALPHA    = 1<<4
} GL2PS_TRIANGLE_PROPERTY;

typedef GLfloat GL2PSxyz[3];
typedef GLfloat GL2PSplane[4];

typedef struct _GL2PSbsptree2d GL2PSbsptree2d;

struct _GL2PSbsptree2d {
  GL2PSplane plane;
  GL2PSbsptree2d *front, *back;
};

typedef struct {
  GLint nmax, size, incr, n;
  char *array;
} GL2PSlist;

typedef struct _GL2PSbsptree GL2PSbsptree;

struct _GL2PSbsptree {
  GL2PSplane plane;
  GL2PSlist *primitives;
  GL2PSbsptree *front, *back;
};

typedef struct {
  GL2PSxyz xyz;
  GL2PSrgba rgba;
} GL2PSvertex;

typedef struct {
  GL2PSvertex vertex[3];
  int prop;
} GL2PStriangle;

typedef struct {
  GLshort fontsize;
  char *str, *fontname;
  GLint alignment;
  GLfloat angle;
} GL2PSstring;

/* FIXME: in the case of an image map, "type" indicates if this image
   map has already been written to the PostScript file or not, and
   "format" indicates if this image map is visible or not */
typedef struct {
  GLsizei width, height;
  GLenum format, type;
  GLfloat *pixels;
} GL2PSimage;

typedef struct _GL2PSimagemap GL2PSimagemap;

struct _GL2PSimagemap {
  GL2PSimage *image;
  GL2PSimagemap *next;
};

typedef struct {
  GL2PSplane plane;
  GLshort type, numverts;
  char boundary, culled;
  unsigned short dash;
  GLfloat width, depth;
  GL2PSvertex *verts;
  union {
    GL2PSstring *text;
    GL2PSimage *image;
  } data;
} GL2PSprimitive;

typedef struct {
#ifdef GL2PS_HAVE_ZLIB
  Bytef *dest, *src, *start;
  uLongf destLen, srcLen;
#else
  int dummy;
#endif
} GL2PScompress;

typedef struct{
  GL2PSlist* ptrlist;
  int gsno, fontno, imno, shno, maskshno, trgroupno;
  int gsobjno, fontobjno, imobjno, shobjno, maskshobjno, trgroupobjno;
} GL2PSpdfgroup;

typedef struct {
  /* General */
  GLint format, sort, options, colorsize, colormode, buffersize;
  char *title, *producer, *filename;
  GLboolean boundary, blending;
  GLfloat *feedback, offset[2], lastlinewidth;
  GLint viewport[4], blendfunc[2];
  GL2PSrgba *colormap, lastrgba, threshold;
  GL2PSlist *primitives;
  FILE *stream;
  GL2PScompress *compress;

  /* BSP-specific */
  GLint maxbestroot;

  /* Occlusion culling-specific */
  GLboolean zerosurfacearea;
  GL2PSbsptree2d *imagetree;
  GL2PSprimitive *primitivetoadd;
  
  /* PDF-specific */
  int streamlength;
  GL2PSlist *pdfprimlist, *pdfgrouplist;
  int *xreflist;
  int objects_stack; /* available objects */
  int extgs_stack; /* graphics state object number */
  int font_stack; /* font object number */
  int im_stack; /* image object number */
  int trgroupobjects_stack; /* xobject numbers */
  int shader_stack; /* shader object numbers */
  int mshader_stack; /* mask shader object numbers */

  unsigned short line_pattern;

  /* for image map list */
  GL2PSimagemap *imagemap_head;
  GL2PSimagemap *imagemap_tail;

  /* EMF Specific */

  /* tolerance */
  double EPSILON;

  /*progress bar*/
  void*    PBObject;
  PB_FUNC  PBFunction;

#ifdef WNT
  HDC metaDC;
#endif

} GL2PScontext;

#ifdef WNT
/* declaration taken from WinGDI.h */
typedef WINGDIAPI BOOL (*GradientFillFunc)( IN HDC, IN PTRIVERTEX, IN ULONG, IN PVOID, IN ULONG, IN ULONG);
#endif

/* The gl2ps context. gl2ps is not thread safe (we should create a
   local GL2PScontext during gl2psBeginPage) */

static GL2PScontext *gl2ps = NULL;

/* Need to forward declare this one */

static GLint gl2psPrintPrimitives(void);

/********************************************************************* 
 *
 * Utility routines
 *
 *********************************************************************/

static void gl2psMsg(GLint level, char *fmt, ...)
{
  va_list args;

  if(!(gl2ps->options & GL2PS_SILENT)){
    switch(level){
    case GL2PS_INFO : fprintf(stderr, "GL2PS info: "); break;
    case GL2PS_WARNING : fprintf(stderr, "GL2PS warning: "); break;
    case GL2PS_ERROR : fprintf(stderr, "GL2PS error: "); break;
    }
#if (defined __STDC__ && __STDC__) || defined __cplusplus || defined WNT
    va_start(args, fmt);
#else
    va_start(args);
#endif
    vfprintf(stderr, fmt, args); 
    va_end(args);
    fprintf(stderr, "\n");
  }
  /* if(level == GL2PS_ERROR) exit(1); */
}

static void *gl2psMalloc(size_t size)
{
  void *ptr;

  if(!size) return(NULL);
  ptr = malloc(size);
  if(!ptr){
    gl2psMsg(GL2PS_ERROR, "Couldn't allocate requested memory");
    exit(1);
  }
  return(ptr);
}

static void *gl2psRealloc(void *ptr, size_t size)
{
  if(!size) return(NULL);
  ptr = realloc(ptr, size);
  if(!ptr){
    gl2psMsg(GL2PS_ERROR, "Couldn't reallocate requested memory");
    exit(1);
  }
  return(ptr);
}

static void gl2psFree(void *ptr)
{
  if(!ptr) return;
  free(ptr);
}

/* zlib compression helper routines */

#ifdef GL2PS_HAVE_ZLIB

static void gl2psSetupCompress(void)
{
  gl2ps->compress = (GL2PScompress*)gl2psMalloc(sizeof(GL2PScompress));
  gl2ps->compress->src = NULL;
  gl2ps->compress->start = NULL;
  gl2ps->compress->dest = NULL;
  gl2ps->compress->srcLen = 0;
  gl2ps->compress->destLen = 0;
}

static void gl2psFreeCompress(void)
{
  if(!gl2ps->compress)
    return;
  gl2psFree(gl2ps->compress->start);
  gl2psFree(gl2ps->compress->dest);
  gl2ps->compress->src = NULL;
  gl2ps->compress->start = NULL;
  gl2ps->compress->dest = NULL;
  gl2ps->compress->srcLen = 0;
  gl2ps->compress->destLen = 0;
}

static int gl2psAllocCompress(unsigned int srcsize)
{
  gl2psFreeCompress();
  
  if(!gl2ps->compress || !srcsize)
    return GL2PS_ERROR;
  
  gl2ps->compress->srcLen = srcsize;
  gl2ps->compress->destLen = (int)ceil(1.001 * gl2ps->compress->srcLen + 12);
  gl2ps->compress->src = (Bytef*)gl2psMalloc(gl2ps->compress->srcLen);
  gl2ps->compress->start = gl2ps->compress->src;
  gl2ps->compress->dest = (Bytef*)gl2psMalloc(gl2ps->compress->destLen);
  
  return GL2PS_SUCCESS;
}

static void *gl2psReallocCompress(unsigned int srcsize)
{
  if(!gl2ps->compress || !srcsize)
    return NULL;
  
  if(srcsize < gl2ps->compress->srcLen)
    return gl2ps->compress->start;
  
  gl2ps->compress->srcLen = srcsize;
  gl2ps->compress->destLen = (int)ceil(1.001 * gl2ps->compress->srcLen + 12);
  gl2ps->compress->src = (Bytef*)gl2psRealloc(gl2ps->compress->src, 
                                              gl2ps->compress->srcLen);
  gl2ps->compress->start = gl2ps->compress->src;
  gl2ps->compress->dest = (Bytef*)gl2psRealloc(gl2ps->compress->dest, 
                                               gl2ps->compress->destLen);
  
  return gl2ps->compress->start;
}

static size_t gl2psWriteBigEndianCompress(unsigned long data, size_t bytes)
{
  size_t i;
  size_t size = sizeof(unsigned long);
  for(i = 1; i <= bytes; ++i){
    *gl2ps->compress->src = (Bytef)(0xff & (data >> (size-i) * 8));
    ++gl2ps->compress->src;
  }
  return bytes;
}

static int gl2psDeflate(void)
{
  /* For compatibility with older zlib versions, we use compress(...)
     instead of compress2(..., Z_BEST_COMPRESSION) */
  return compress(gl2ps->compress->dest, &gl2ps->compress->destLen, 
                  gl2ps->compress->start, gl2ps->compress->srcLen);  
}

#endif

static int gl2psPrintf(const char* fmt, ...)
{
  int ret;
  va_list args;

#ifdef GL2PS_HAVE_ZLIB
  unsigned int oldsize = 0;
  static char buf[1000];
  if(gl2ps->options & GL2PS_COMPRESS){
    va_start(args, fmt);
    ret = vsprintf(buf, fmt, args);
    va_end(args);
    oldsize = gl2ps->compress->srcLen;
    gl2ps->compress->start = (Bytef*)gl2psReallocCompress(oldsize + ret);
    memcpy(gl2ps->compress->start+oldsize, buf, ret);
    ret = 0;
  }
  else{
#endif

#if (defined __STDC__ && __STDC__) || defined __cplusplus || defined WNT
    va_start(args, fmt);
#else
    va_start(args);
#endif

    ret = vfprintf(gl2ps->stream, fmt, args);
    va_end(args);
#ifdef GL2PS_HAVE_ZLIB
  }
#endif
  return ret;
}

static size_t gl2psWriteBigEndian(unsigned long data, size_t bytes)
{
  size_t i;
  size_t size = sizeof(unsigned long);
  for(i = 1; i <= bytes; ++i){
    fputc(0xff & (data >> (size-i) * 8), gl2ps->stream);
  }
  return bytes;
}

static void gl2psAssignTriangleProperties(GL2PStriangle *t)
{
  /* int i; */

  t->prop = T_VAR_COLOR;

  /* Uncommenting the following lines activates an even more fine
     grained distinction between triangle types - please don't delete,
     a remarkable amount of PDF handling code inside this file depends
     on it if activated */
  /*
  t->prop = T_CONST_COLOR;    
  for(i = 0; i < 3; ++i){
    if(!GL2PS_ZERO(t->vertex[0].rgba[i] - t->vertex[1].rgba[i]) || 
       !GL2PS_ZERO(t->vertex[1].rgba[i] - t->vertex[2].rgba[i])){
      t->prop = T_VAR_COLOR;
      break;
    }
  }
  */

  if(!GL2PS_ZERO(t->vertex[0].rgba[3] - t->vertex[1].rgba[3]) || 
     !GL2PS_ZERO(t->vertex[1].rgba[3] - t->vertex[2].rgba[3])){
    t->prop |= T_VAR_ALPHA;
  }
  else{
    if(t->vertex[0].rgba[3] < 1)
      t->prop |= T_ALPHA_LESS_1;
    else
      t->prop |= T_ALPHA_1;
  }
}

static void gl2psFillTriangleFromPrimitive(GL2PStriangle* t, GL2PSprimitive* p,
                                           GLboolean assignprops)
{
  t->vertex[0] = p->verts[0];
  t->vertex[1] = p->verts[1];
  t->vertex[2] = p->verts[2];
  if(GL_TRUE == assignprops)
    gl2psAssignTriangleProperties(t);
}

/* The list handling routines */

static void gl2psListRealloc(GL2PSlist *list, GLint n)
{
  if(!list){
    gl2psMsg(GL2PS_ERROR, "Cannot reallocate NULL list");
    return;
  }
  if(n <= 0) return;
  if(!list->array){
    list->nmax = ((n - 1) / list->incr + 1) * list->incr;
    list->array = (char*)gl2psMalloc(list->nmax * list->size);
  }
  else{
    if(n > list->nmax){
      list->nmax = ((n - 1) / list->incr + 1) * list->incr;
      list->array = (char*)gl2psRealloc(list->array,
                                        list->nmax * list->size);
    }
  }
}

static GL2PSlist *gl2psListCreate(GLint n, GLint incr, GLint size)
{
  GL2PSlist *list;

  if(n < 0) n = 0;
  if(incr <= 0) incr = 1;
  list = (GL2PSlist*)gl2psMalloc(sizeof(GL2PSlist));
  list->nmax = 0;
  list->incr = incr;
  list->size = size;
  list->n = 0;
  list->array = NULL;
  gl2psListRealloc(list, n);
  return(list);
}

static void gl2psListReset(GL2PSlist *list)
{
  if(!list) return;
  list->n = 0;
}

static void gl2psListDelete(GL2PSlist *list)
{
  if(!list) return;  
  gl2psFree(list->array);
  gl2psFree(list);
}

static void gl2psListAdd(GL2PSlist *list, void *data)
{
  if(!list){
    gl2psMsg(GL2PS_ERROR, "Cannot add into unallocated list");
    return;
  }
  list->n++;
  gl2psListRealloc(list, list->n);
  memcpy(&list->array[(list->n - 1) * list->size], data, list->size);
}

static int gl2psListNbr(GL2PSlist *list)
{
  if(!list)
    return 0;
  return(list->n);
}

static void *gl2psListPointer(GL2PSlist *list, GLint index)
{
  if(!list){
    gl2psMsg(GL2PS_ERROR, "Cannot point into unallocated list");
    return NULL;
  }
  if((index < 0) || (index >= list->n)){
    gl2psMsg(GL2PS_ERROR, "Wrong list index in gl2psListPointer");
    return NULL;
  }
  return(&list->array[index * list->size]);
}

static void gl2psListSort( GL2PSlist *list,
                           int (*fcmp)( const void *a, const void *b ) )
{
  if(!list)
    return;

  qsort( list->array, list->n, list->size, fcmp );
}

static void gl2psListAction(GL2PSlist *list, void (*action)(void *data))
{
  GLint i, n=gl2psListNbr(list);

  for( i=0; i<n; i++)
    (*action)( gl2psListPointer( list, i ) );
}

static void gl2psListActionInverse(GL2PSlist *list, void (*action)(void *data))
{
  GLint i;

  for( i=gl2psListNbr( list ); i>0; i-- )
    (*action)(gl2psListPointer( list, i-1 ) );
}

/* Helper for pixmaps and strings */

static GL2PSimage *gl2psCopyPixmap(GL2PSimage* im)
{
  int size;
  GL2PSimage *image = (GL2PSimage*)gl2psMalloc(sizeof(GL2PSimage));
  
  image->width = im->width;
  image->height = im->height;
  image->format = im->format;
  image->type = im->type;

  switch(image->format){
  case GL_RGBA:
    size = image->height * image->width * 4 * sizeof(GLfloat);
    break;
  case GL_RGB:
  default:
    size = image->height * image->width * 3 * sizeof(GLfloat);
    break;
  }

  image->pixels = (GLfloat*)gl2psMalloc(size);
  memcpy(image->pixels, im->pixels, size);
  
  return image;
}

static void gl2psFreePixmap(GL2PSimage* im)
{
  if(!im)
    return;
  gl2psFree(im->pixels);
  gl2psFree(im);
}

static GL2PSstring *gl2psCopyText(GL2PSstring* t)
{
  GL2PSstring *text = (GL2PSstring*)gl2psMalloc(sizeof(GL2PSstring));
  text->str = (char*)gl2psMalloc((strlen(t->str)+1)*sizeof(char));
  strcpy(text->str, t->str); 
  text->fontname = (char*)gl2psMalloc((strlen(t->fontname)+1)*sizeof(char));
  strcpy(text->fontname, t->fontname);
  text->fontsize = t->fontsize;
  text->alignment = t->alignment;
  text->angle = t->angle;
  
  return text;
}

static void gl2psFreeText(GL2PSstring* text)
{
  if(!text)
    return;
  gl2psFree(text->str);
  gl2psFree(text->fontname);
  gl2psFree(text);
}

static void gl2psGetPlane( GL2PSprimitive*, GL2PSplane );

static GL2PSprimitive *gl2psCreatePrimitive()
{
    GL2PSprimitive *prim = (GL2PSprimitive*)gl2psMalloc( sizeof( GL2PSprimitive ) );
    int i;

    for( i=0; i<4; i++ )
        prim->plane[i] = 0.0f;

    return prim;
}

static GL2PSprimitive *gl2psCopyPrimitive( GL2PSprimitive* p )
{
  GL2PSprimitive *prim;
  int i;

  if(!p){
    gl2psMsg(GL2PS_ERROR, "Trying to copy an empty primitive");
    return NULL;
  }

  prim = gl2psCreatePrimitive();
  
  for( i=0; i<4; i++ )
    prim->plane[i] = p->plane[i];

  prim->type = p->type;
  prim->numverts = p->numverts;
  prim->boundary = p->boundary;
  prim->dash = p->dash;
  prim->culled = p->culled;
  prim->width = p->width;
  prim->depth = p->depth;
  prim->verts = (GL2PSvertex*)gl2psMalloc(p->numverts*sizeof(GL2PSvertex));
  memcpy(prim->verts, p->verts, p->numverts * sizeof(GL2PSvertex));

  switch(prim->type){
  case GL2PS_PIXMAP :
    prim->data.image = gl2psCopyPixmap(p->data.image);
    break;
  case GL2PS_TEXT :
    prim->data.text = gl2psCopyText(p->data.text);
    break;
  default:
    break;
  }

  return prim;
}

/* Helpers for rgba colors and PDF blending */

static GLboolean gl2psSameColor(GL2PSrgba rgba1, GL2PSrgba rgba2)
{
  if(!GL2PS_ZERO(rgba1[0] - rgba2[0]) ||
     !GL2PS_ZERO(rgba1[1] - rgba2[1]) ||
     !GL2PS_ZERO(rgba1[2] - rgba2[2]))
    return GL_FALSE;
  return GL_TRUE;
}
  
static GLboolean gl2psVertsSameColor(const GL2PSprimitive *prim)
{
  int i;

  for(i = 1; i < prim->numverts; i++){
    if(!gl2psSameColor(prim->verts[0].rgba, prim->verts[i].rgba)){
      return GL_FALSE;
    }
  }
  return GL_TRUE;
}

static void gl2psSetLastColor(GL2PSrgba rgba)
{
  int i;        
  for(i = 0; i < 3; ++i){
    gl2ps->lastrgba[i] = rgba[i];
  }
}

/* returns TRUE if gl2ps supports the argument combination: only two
   blending modes have been implemented so far */

static GLboolean gl2psSupportedBlendMode(GLenum sfactor, GLenum dfactor)
{
  if( (sfactor == GL_SRC_ALPHA && dfactor == GL_ONE_MINUS_SRC_ALPHA) || 
      (sfactor == GL_ONE && dfactor == GL_ZERO) )
    return GL_TRUE;
  return GL_FALSE;
}

/* Transforms vertex depending on the actual blending function -
   currently the vertex v is considered as source vertex and his alpha
   value is changed to 1.0 if source blending GL_ONE is active. This
   might be extended in the future */

static void gl2psAdaptVertexForBlending(GL2PSvertex *v)
{
  if(!v || !gl2ps)
    return;
  
  if(gl2ps->options & GL2PS_NO_BLENDING || !gl2ps->blending){
    v->rgba[3] = 1.0F;
    return;
  }
  
  switch(gl2ps->blendfunc[0]){
  case GL_ONE:
    v->rgba[3] = 1.0F;
    break;
  default:
    break;
  }
}

/********************************************************************* 
 *
 * 3D sorting routines 
 *
 *********************************************************************/

static GLfloat gl2psComparePointPlane(GL2PSxyz point, GL2PSplane plane)
{
  return(plane[0] * point[0] + 
         plane[1] * point[1] + 
         plane[2] * point[2] + 
         plane[3]);
}

static GLfloat gl2psPsca(GLfloat *a, GLfloat *b)
{
  return(a[0]*b[0] + a[1]*b[1] + a[2]*b[2]);
}

static void gl2psPvec(GLfloat *a, GLfloat *b, GLfloat *c)
{
  c[0] = a[1]*b[2] - a[2]*b[1];
  c[1] = a[2]*b[0] - a[0]*b[2];
  c[2] = a[0]*b[1] - a[1]*b[0];
}

static GLfloat gl2psNorm(GLfloat *a)
{
  return (GLfloat)sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
}

static void gl2psGetNormal(GLfloat *a, GLfloat *b, GLfloat *c)
{
  GLfloat norm;

  gl2psPvec(a, b, c);
  if(!GL2PS_ZERO(norm = gl2psNorm(c))){
    c[0] = c[0] / norm;
    c[1] = c[1] / norm;
    c[2] = c[2] / norm;
  }
  else{
    /* FIXME: the plane is still wrong, despite our tests in
       gl2psGetPlane... Let's return a dummy value for now (this is a
       hack: we should do more tests in GetPlane) */
    c[0] = c[1] = 0.0F;
    c[2] = 1.0F;
  }
  if( c[2]<0 )
  {
      c[0] = -c[0];
      c[1] = -c[1];
      c[2] = -c[2];
  }
}

static void gl2psGetPlane(GL2PSprimitive *prim, GL2PSplane plane)
{
  GL2PSxyz v = {0.0F, 0.0F, 0.0F}, w = {0.0F, 0.0F, 0.0F};
  int i;

  if( !GL2PS_ZERO( prim->plane[0] ) || 
      !GL2PS_ZERO( prim->plane[1] ) ||
      !GL2PS_ZERO( prim->plane[2] ) ||
      !GL2PS_ZERO( prim->plane[3] ) )
  {
      for( i=0; i<4; i++ )
        plane[i] = prim->plane[i];
  }


  switch(prim->type){
  case GL2PS_TRIANGLE :
  case GL2PS_QUADRANGLE :
    v[0] = prim->verts[1].xyz[0] - prim->verts[0].xyz[0]; 
    v[1] = prim->verts[1].xyz[1] - prim->verts[0].xyz[1]; 
    v[2] = prim->verts[1].xyz[2] - prim->verts[0].xyz[2]; 
    w[0] = prim->verts[2].xyz[0] - prim->verts[0].xyz[0]; 
    w[1] = prim->verts[2].xyz[1] - prim->verts[0].xyz[1]; 
    w[2] = prim->verts[2].xyz[2] - prim->verts[0].xyz[2]; 
    if((GL2PS_ZERO(v[0]) && GL2PS_ZERO(v[1]) && GL2PS_ZERO(v[2])) || 
       (GL2PS_ZERO(w[0]) && GL2PS_ZERO(w[1]) && GL2PS_ZERO(w[2]))){
      plane[0] = plane[1] = 0.0F;
      plane[2] = 1.0F;
      plane[3] = -prim->verts[0].xyz[2];
    }
    else{
      gl2psGetNormal(v, w, plane);
      plane[3] = 
        - plane[0] * prim->verts[0].xyz[0] 
        - plane[1] * prim->verts[0].xyz[1] 
        - plane[2] * prim->verts[0].xyz[2];
    }
    break;
  case GL2PS_LINE :
    v[0] = prim->verts[1].xyz[0] - prim->verts[0].xyz[0]; 
    v[1] = prim->verts[1].xyz[1] - prim->verts[0].xyz[1]; 
    v[2] = prim->verts[1].xyz[2] - prim->verts[0].xyz[2]; 
    if(GL2PS_ZERO(v[0]) && GL2PS_ZERO(v[1]) && GL2PS_ZERO(v[2])){
      plane[0] = plane[1] = 0.0F;
      plane[2] = 1.0F;
      plane[3] = -prim->verts[0].xyz[2];
    }
    else{
      if(GL2PS_ZERO(v[0]))      w[0] = 1.0F;
      else if(GL2PS_ZERO(v[1])) w[1] = 1.0F;
      else                      w[2] = 1.0F;
      gl2psGetNormal(v, w, plane);
      plane[3] = 
        - plane[0] * prim->verts[0].xyz[0] 
        - plane[1] * prim->verts[0].xyz[1] 
        - plane[2] * prim->verts[0].xyz[2];
    }
    break;
  case GL2PS_POINT :
  case GL2PS_PIXMAP :
  case GL2PS_TEXT :
  case GL2PS_IMAGEMAP:
    plane[0] = plane[1] = 0.0F;
    plane[2] = 1.0F;
    plane[3] = -prim->verts[0].xyz[2];
    break;
  default :
    gl2psMsg(GL2PS_ERROR, "Unknown primitive type in BSP tree");
    plane[0] = plane[1] = plane[3] = 0.0F;
    plane[2] = 1.0F;
    break;
  }

  for( i=0; i<4; i++ )
    prim->plane[i] = plane[i];
}

static void gl2psCutEdge(GL2PSvertex *a, GL2PSvertex *b, GL2PSplane plane,
                         GL2PSvertex *c)
{
  GL2PSxyz v;
  GLfloat sect;

  v[0] = b->xyz[0] - a->xyz[0];
  v[1] = b->xyz[1] - a->xyz[1];
  v[2] = b->xyz[2] - a->xyz[2];
  sect = - gl2psComparePointPlane(a->xyz, plane) / gl2psPsca(plane, v);

  c->xyz[0] = a->xyz[0] + v[0] * sect;
  c->xyz[1] = a->xyz[1] + v[1] * sect;
  c->xyz[2] = a->xyz[2] + v[2] * sect;
  
  c->rgba[0] = (1 - sect) * a->rgba[0] + sect * b->rgba[0];
  c->rgba[1] = (1 - sect) * a->rgba[1] + sect * b->rgba[1];
  c->rgba[2] = (1 - sect) * a->rgba[2] + sect * b->rgba[2];
  c->rgba[3] = (1 - sect) * a->rgba[3] + sect * b->rgba[3];
}

static void gl2psCreateSplitPrimitive(GL2PSprimitive *parent, GL2PSplane plane,
                                      GL2PSprimitive *child, GLshort numverts,
                                      GLshort *index0, GLshort *index1)
{
  GLshort i;

  if(parent->type == GL2PS_IMAGEMAP){
    child->type = GL2PS_IMAGEMAP;
    child->data.image = parent->data.image;
  }
  else{
    if(numverts > 4){
      gl2psMsg(GL2PS_WARNING, "%d vertices in polygon", numverts);
      numverts = 4;
    }
    switch(numverts){
    case 1 : child->type = GL2PS_POINT; break; 
    case 2 : child->type = GL2PS_LINE; break; 
    case 3 : child->type = GL2PS_TRIANGLE; break; 
    case 4 : child->type = GL2PS_QUADRANGLE; break;    
    }
  }

  child->boundary = 0; /* not done! */
  child->depth = parent->depth; /* should not be used in this case */
  child->culled = parent->culled;
  child->dash = parent->dash;
  child->width = parent->width;
  child->numverts = numverts;
  child->verts = (GL2PSvertex*)gl2psMalloc(numverts * sizeof(GL2PSvertex));

  for(i = 0; i < numverts; i++){
    if(index1[i] < 0){
      child->verts[i] = parent->verts[index0[i]];
    }
    else{
      gl2psCutEdge(&parent->verts[index0[i]], &parent->verts[index1[i]], 
                   plane, &child->verts[i]);
    }
  }
}

static void gl2psAddIndex(GLshort *index0, GLshort *index1, GLshort *nb, 
                          GLshort i, GLshort j)
{
  GLint k;

  for(k = 0; k < *nb; k++){
    if((index0[k] == i && index1[k] == j) ||
       (index1[k] == i && index0[k] == j)) return;
  }
  index0[*nb] = i;
  index1[*nb] = j;
  (*nb)++;
}

static GLshort gl2psGetIndex(GLshort i, GLshort num)
{
  return(i < num-1) ? i+1 : 0;
}

static GLint gl2psTestSplitPrimitive(GL2PSprimitive *prim, GL2PSplane plane)
{
  GLint type = GL2PS_COINCIDENT;
  GLshort i, j;
  GLfloat d[5]; 

  for(i = 0; i < prim->numverts; i++){  
    d[i] = gl2psComparePointPlane(prim->verts[i].xyz, plane);
  }

  if(prim->numverts < 2){
    return 0;
  }
  else{
    for(i = 0; i < prim->numverts; i++){
      j = gl2psGetIndex(i, prim->numverts);
      if(d[j] > gl2ps->EPSILON /*GL2PS_EPSILON*/){
        if(type == GL2PS_COINCIDENT)      type = GL2PS_IN_BACK_OF;
        else if(type != GL2PS_IN_BACK_OF) return 1; 
        if(d[i] < -gl2ps->EPSILON)         return 1;
      }
      else if(d[j] < -gl2ps->EPSILON){
        if(type == GL2PS_COINCIDENT)       type = GL2PS_IN_FRONT_OF;   
        else if(type != GL2PS_IN_FRONT_OF) return 1;
        if(d[i] > gl2ps->EPSILON)           return 1;
      }
    }
  }
  return 0;
}

static GLint gl2psSplitPrimitive(GL2PSprimitive *prim, GL2PSplane plane, 
                                 GL2PSprimitive **front, GL2PSprimitive **back)
{
  GLshort i, j, in = 0, out = 0, in0[5], in1[5], out0[5], out1[5];
  GLint type;
  GLfloat d[5]; 

  type = GL2PS_COINCIDENT;

  for(i = 0; i < prim->numverts; i++){  
    d[i] = gl2psComparePointPlane(prim->verts[i].xyz, plane);
  }

  switch(prim->type){
  case GL2PS_POINT :
    if(d[0] > gl2ps->EPSILON)       type = GL2PS_IN_BACK_OF;
    else if(d[0] < -gl2ps->EPSILON) type = GL2PS_IN_FRONT_OF;
    else                           type = GL2PS_COINCIDENT;
    break;
  default :
    for(i = 0; i < prim->numverts; i++){
      j = gl2psGetIndex(i, prim->numverts);
      if(d[j] > gl2ps->EPSILON){
        if(type == GL2PS_COINCIDENT)      type = GL2PS_IN_BACK_OF;
        else if(type != GL2PS_IN_BACK_OF) type = GL2PS_SPANNING; 
        if(d[i] < -gl2ps->EPSILON){
          gl2psAddIndex(in0, in1, &in, i, j);
          gl2psAddIndex(out0, out1, &out, i, j);
          type = GL2PS_SPANNING;
        }
        gl2psAddIndex(out0, out1, &out, j, -1);
      }
      else if(d[j] < -gl2ps->EPSILON){
        if(type == GL2PS_COINCIDENT)       type = GL2PS_IN_FRONT_OF;   
        else if(type != GL2PS_IN_FRONT_OF) type = GL2PS_SPANNING;
        if(d[i] > gl2ps->EPSILON){
          gl2psAddIndex(in0, in1, &in, i, j);
          gl2psAddIndex(out0, out1, &out, i, j);
          type = GL2PS_SPANNING;
        }
        gl2psAddIndex(in0, in1, &in, j, -1);
      }
      else{
        gl2psAddIndex(in0, in1, &in, j, -1);
        gl2psAddIndex(out0, out1, &out, j, -1);
      }
    }
    break;
  }

  if(type == GL2PS_SPANNING){
    *back = gl2psCreatePrimitive();
    *front = gl2psCreatePrimitive();
    gl2psCreateSplitPrimitive(prim, plane, *back, out, out0, out1);
    gl2psCreateSplitPrimitive(prim, plane, *front, in, in0, in1);
  }

  return type;
}

static void gl2psDivideQuad(GL2PSprimitive *quad, 
                            GL2PSprimitive **t1, GL2PSprimitive **t2)
{
  int i;

  *t1 = gl2psCreatePrimitive();
  *t2 = gl2psCreatePrimitive();

  for( i=0; i<4; i++ )
  {
    (*t1)->plane[i] = quad->plane[i];
    (*t2)->plane[i] = quad->plane[i];
  }

  (*t1)->type = (*t2)->type = GL2PS_TRIANGLE;
  (*t1)->numverts = (*t2)->numverts = 3;
  (*t1)->depth = (*t2)->depth = quad->depth;
  (*t1)->culled = (*t2)->culled = quad->culled;
  (*t1)->dash = (*t2)->dash = quad->dash;
  (*t1)->width = (*t2)->width = quad->width;
  (*t1)->verts = (GL2PSvertex*)gl2psMalloc(3 * sizeof(GL2PSvertex));
  (*t2)->verts = (GL2PSvertex*)gl2psMalloc(3 * sizeof(GL2PSvertex));
  (*t1)->verts[0] = quad->verts[0];
  (*t1)->verts[1] = quad->verts[1];
  (*t1)->verts[2] = quad->verts[2];
  (*t1)->boundary = ((quad->boundary & 1) ? 1 : 0) | ((quad->boundary & 2) ? 2 : 0);
  (*t2)->verts[0] = quad->verts[0];
  (*t2)->verts[1] = quad->verts[2];
  (*t2)->verts[2] = quad->verts[3];
  (*t1)->boundary = ((quad->boundary & 4) ? 2 : 0) | ((quad->boundary & 4) ? 2 : 0);
}

static int gl2psCompareDepth(const void *a, const void *b)
{
  GL2PSprimitive *q, *w;
  GLfloat diff;

  q = *(GL2PSprimitive**)a;
  w = *(GL2PSprimitive**)b;
  diff = q->depth - w->depth;
  if(diff > 0.){
    return 1;
  }
  else if(diff < 0.){
    return -1;
  }
  else{
    return 0;
  }
}

static int gl2psTrianglesFirst(const void *a, const void *b)
{
  GL2PSprimitive *q, *w;

  q = *(GL2PSprimitive**)a;
  w = *(GL2PSprimitive**)b;
  return(q->type < w->type ? 1 : -1);
}

static GLint gl2psFindRoot(GL2PSlist *primitives, GL2PSprimitive **root)
{
  GLint i, j, count, best = 1000000, index = 0;
  GL2PSprimitive *prim1, *prim2;
  GL2PSplane plane;
  GLint maxp;
  int n = gl2psListNbr(primitives);

  if( n==0 ){
    gl2psMsg(GL2PS_ERROR, "Cannot find root in empty primitive list");
    return 0;
  }

  *root = *(GL2PSprimitive**)gl2psListPointer(primitives, 0);

  if(gl2ps->options & GL2PS_BEST_ROOT){
    maxp = n;
    if(maxp > gl2ps->maxbestroot){
      maxp = gl2ps->maxbestroot;
    }
    for( i=0; i<maxp; i++ ){
      prim1 = *(GL2PSprimitive**)gl2psListPointer(primitives, i);
      if( prim1->type!=5 ) 
          continue;
      gl2psGetPlane(prim1, plane);
      count = 0;
      for( j=0; j<n; j++ ){
        if(j != i){
          prim2 = *(GL2PSprimitive**)gl2psListPointer(primitives, j);
          count += gl2psTestSplitPrimitive(prim2, plane); 
        }
        if(count > best) break;
      }
      if(count < best){
        best = count;
        index = i;
        *root = prim1;
        if(!count) return index;
      }
    }
    /* if(index) gl2psMsg(GL2PS_INFO, "GL2PS_BEST_ROOT was worth it: %d", index); */
    return index;
  }
  else{
    return 0;
  }
}

static void gl2psFreeImagemap(GL2PSimagemap *list){
  GL2PSimagemap *next;
  while(list != NULL){
    next = list->next;
    gl2psFree(list->image->pixels);
    gl2psFree(list->image);
    gl2psFree(list);
    list = next;
  }
}

static void gl2psFreePrimitive(void *data)
{
  GL2PSprimitive *q;
  
  q = *(GL2PSprimitive**)data;
  gl2psFree(q->verts);
  if(q->type == GL2PS_TEXT){
    gl2psFreeText(q->data.text);
  }
  else if(q->type == GL2PS_PIXMAP){
    gl2psFreePixmap(q->data.image);
  }
  gl2psFree(q);
}

static void gl2psAddPrimitiveInList(GL2PSprimitive *prim, GL2PSlist *list)
{
  GL2PSprimitive *t1, *t2;

  if(prim->type != GL2PS_QUADRANGLE){
    gl2psListAdd(list, &prim);
  }
  else{
    gl2psDivideQuad(prim, &t1, &t2);
    gl2psListAdd(list, &t1);
    gl2psListAdd(list, &t2);
    gl2psFreePrimitive(&prim);
  }
  
}

static void gl2psFreeBspTree(GL2PSbsptree **tree)
{
  if(*tree){
    if((*tree)->back) gl2psFreeBspTree(&(*tree)->back);
    if((*tree)->primitives){
      gl2psListAction((*tree)->primitives, gl2psFreePrimitive);
      gl2psListDelete((*tree)->primitives);
    }
    if((*tree)->front) gl2psFreeBspTree(&(*tree)->front);
    gl2psFree(*tree);
    *tree = NULL;
  }
}

static GLboolean gl2psGreater(GLfloat f1, GLfloat f2)
{
  if(f1 > f2) return GL_TRUE;
  else return GL_FALSE;
}

static GLboolean gl2psLess(GLfloat f1, GLfloat f2)
{
  if(f1 < f2) return GL_TRUE;
  else return GL_FALSE;
}

static int buildBSPcount;
static int numberOfPrimitives;
static int numberOfProcessedPrimitives;

const int lc = 10;
GL2PSlist *frontLists[10] = { NULL, NULL, NULL, NULL, NULL,
                              NULL, NULL, NULL, NULL, NULL },
          *backLists[10] = { NULL, NULL, NULL, NULL, NULL,
                             NULL, NULL, NULL, NULL, NULL },
          *sortLists[10] = { NULL, NULL, NULL, NULL, NULL,
                              NULL, NULL, NULL, NULL, NULL };


static void gl2psBuildBspTree( GL2PSbsptree *tree, 
                               GL2PSlist *primitives, 
                               GLboolean start )
{  
  GL2PSprimitive *prim, *frontprim, *backprim;
  GL2PSlist *frontlist, *backlist;  
  GLint i, j, index, n, root_type;

  buildBSPcount = buildBSPcount + 1;

  tree->front = NULL;
  tree->back = NULL;

  tree->primitives = gl2psListCreate( 1, 2, sizeof( GL2PSprimitive* ) );

  index = gl2psFindRoot(primitives, &prim);

  root_type = prim->type;

  gl2psGetPlane(prim, tree->plane);
  gl2psAddPrimitiveInList(prim, tree->primitives);

  frontlist = gl2psListCreate( 1, 2, sizeof( GL2PSprimitive* ) );
  backlist  = gl2psListCreate( 1, 2, sizeof( GL2PSprimitive* ) );

  for( i=0; i<lc; i++ )
  {
      if( frontLists[i]==NULL ) 
          frontLists[i] = gl2psListCreate( 1, 2, sizeof(GL2PSprimitive*) );
      if( backLists[i]==NULL )
          backLists[i] = gl2psListCreate( 1, 2, sizeof(GL2PSprimitive*) );
  }

  for( i=0, n=gl2psListNbr(primitives); i<n; i++ ){
    if(i != index){
      prim = *(GL2PSprimitive**)gl2psListPointer(primitives,i);
      switch( gl2psSplitPrimitive( prim, tree->plane, &frontprim, &backprim ) )
      {
      case GL2PS_COINCIDENT:
          {
            if( prim->type==3 && root_type==5 )
            {
                gl2psAddPrimitiveInList( gl2psCopyPrimitive( prim ), frontLists[ prim->type ] );
                numberOfPrimitives++;
            }

            gl2psAddPrimitiveInList( prim, tree->primitives);
            break;
          }
      case GL2PS_IN_BACK_OF:
          {
            gl2psAddPrimitiveInList( prim, backLists[ prim->type ] );
            break;
          }
      case GL2PS_IN_FRONT_OF:
          {
            gl2psAddPrimitiveInList( prim, frontLists[ prim->type ] );
            break;
          }
      case GL2PS_SPANNING:
          {
            numberOfPrimitives++;
            gl2psAddPrimitiveInList( backprim, backLists[ backprim->type ] );
            gl2psAddPrimitiveInList( frontprim, frontLists[ frontprim->type ] );
            gl2psFreePrimitive( &prim );
            break;
          }
      }
    }
  }  

    if(gl2psListNbr(tree->primitives))
    {
        for( i=0; i<lc; i++ )
        {
            if( sortLists[i]==NULL ) 
                sortLists[i] = gl2psListCreate( 1, 2, sizeof(GL2PSprimitive*) );
        }
        
        for( i = 0, n = gl2psListNbr( tree->primitives ); i < n; i++ )
        {
            prim = *(GL2PSprimitive**)gl2psListPointer(tree->primitives,i);
            gl2psAddPrimitiveInList( prim, sortLists[ prim->type ] );
        }

        gl2psListReset( tree->primitives );

        for( i = lc-1; i >= 0; i-- )
            for( j = 0, n = gl2psListNbr( sortLists[i] ); j < n; j++ )
                gl2psAddPrimitiveInList( *(GL2PSprimitive**)gl2psListPointer(sortLists[i],j), tree->primitives );

        /*gl2psListSort(tree->primitives, gl2psTrianglesFirst);*/
        numberOfProcessedPrimitives += n;

        /*for( i=0; i<lc; i++ )
        {
            if( start )
            {
                gl2psListDelete( sortLists[i] );
                sortLists[i] = NULL;
            }
            else
                gl2psListReset( sortLists[i] );
        }*/

    }

  if( gl2ps->PBFunction )
      (*gl2ps->PBFunction)( numberOfProcessedPrimitives, numberOfPrimitives, gl2ps->PBObject );

    for( i=lc-1; i>=0; i-- )
    {
        for( j=0, n=gl2psListNbr( frontLists[i] ); j<n; j++ )
            gl2psAddPrimitiveInList( *(GL2PSprimitive**)gl2psListPointer( frontLists[i],j ), frontlist );

        for( j=0, n=gl2psListNbr( backLists[i] ); j<n; j++ )
            gl2psAddPrimitiveInList( *(GL2PSprimitive**)gl2psListPointer( backLists[i],j ), backlist );
    }

  for( i=0; i<lc; i++ )
  {
      if( start )
      {
          gl2psListDelete( frontLists[i] );
          gl2psListDelete( backLists[i] );
          frontLists[i] = NULL;
          backLists[i] = NULL;

          gl2psListDelete( sortLists[i] );
          sortLists[i] = NULL;
      }
      else
      {
          gl2psListReset( frontLists[i] );
          gl2psListReset( backLists[i] );
          
          gl2psListReset( sortLists[i] );
      }
  }

  if( gl2psListNbr( frontlist ) )
  {
      tree->front = (GL2PSbsptree*)gl2psMalloc(sizeof(GL2PSbsptree));
      gl2psBuildBspTree( tree->front, frontlist, GL_FALSE );
  }
  else
      gl2psListDelete(frontlist);

  if( gl2psListNbr( backlist ) )
  {
      tree->back = (GL2PSbsptree*)gl2psMalloc(sizeof(GL2PSbsptree));
      gl2psBuildBspTree( tree->back, backlist, GL_FALSE );
  }
  else
      gl2psListDelete(backlist);

  gl2psListDelete(primitives);
}

static void gl2psTraverseBspTree(GL2PSbsptree *tree, GL2PSxyz eye, GLfloat epsilon,
                                 GLboolean (*compare)(GLfloat f1, GLfloat f2),
                                 void (*action)(void *data), int inverse)
{
  GLfloat result;

  if(!tree) return;

  result = gl2psComparePointPlane(eye, tree->plane);

  if(GL_TRUE == compare(result, epsilon)){
    gl2psTraverseBspTree(tree->back, eye, epsilon, compare, action, inverse);
    if(inverse){
      gl2psListActionInverse(tree->primitives, action);
    }
    else{
      gl2psListAction(tree->primitives, action);
    }
    gl2psTraverseBspTree(tree->front, eye, epsilon, compare, action, inverse);
  }
  else if(GL_TRUE == compare(-epsilon, result)){ 
    gl2psTraverseBspTree(tree->front, eye, epsilon, compare, action, inverse);
    if(inverse){
      gl2psListActionInverse(tree->primitives, action);
    }
    else{
      gl2psListAction(tree->primitives, action);
    }
    gl2psTraverseBspTree(tree->back, eye, epsilon, compare, action, inverse);
  }
  else{
    gl2psTraverseBspTree(tree->front, eye, epsilon, compare, action, inverse);
    gl2psTraverseBspTree(tree->back, eye, epsilon, compare, action, inverse);
  }
}

/********************************************************************* 
 *
 * 2D sorting routines (for occlusion culling) 
 *
 *********************************************************************/

static GLint gl2psGetPlaneFromPoints(GL2PSxyz a, GL2PSxyz b, GL2PSplane plane)
{
  GLfloat n; 

  plane[0] = b[1] - a[1];
  plane[1] = a[0] - b[0];
  n = (GLfloat)sqrt(plane[0]*plane[0] + plane[1]*plane[1]);
  plane[2] = 0.0F;
  if(n != 0.0){
    plane[0] /= n;
    plane[1] /= n;
    plane[3] = -plane[0]*a[0]-plane[1]*a[1]; 
    return 1;
  }
  else{
    plane[0] = -1.0F;
    plane[1] = 0.0F;
    plane[3] = a[0];
    return 0;
  }
}

static void gl2psFreeBspImageTree(GL2PSbsptree2d **tree)
{
  if(*tree){
    if((*tree)->back)  gl2psFreeBspImageTree(&(*tree)->back);
    if((*tree)->front) gl2psFreeBspImageTree(&(*tree)->front);
    gl2psFree(*tree);
    *tree = NULL;
  }
}

static GLint gl2psCheckPoint(GL2PSxyz point, GL2PSplane plane)
{
  GLfloat pt_dis;

  pt_dis = gl2psComparePointPlane(point, plane);
  if(pt_dis > gl2ps->EPSILON)        return GL2PS_POINT_INFRONT;
  else if(pt_dis < -gl2ps->EPSILON)  return GL2PS_POINT_BACK;
  else                              return GL2PS_POINT_COINCIDENT;
}

static void gl2psAddPlanesInBspTreeImage(GL2PSprimitive *prim,
                                         GL2PSbsptree2d **tree)
{
  GLint ret = 0;
  GLint i;
  GLint offset = 0;
  GL2PSbsptree2d *head = NULL, *cur = NULL;

  if((*tree == NULL) && (prim->numverts > 2)){
    head = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
    for(i = 0; i < prim->numverts-1; i++){
      if(!gl2psGetPlaneFromPoints(prim->verts[i].xyz,
                                  prim->verts[i+1].xyz,
                                  head->plane)){
        if(prim->numverts-i > 3){
          offset++;
        }
        else{
          gl2psFree(head);
          return;
        }
      }
      else{
        break;
      }
    }
    head->back = NULL;
    head->front = NULL;
    for(i = 2+offset; i < prim->numverts; i++){
      ret = gl2psCheckPoint(prim->verts[i].xyz, head->plane);
      if(ret != GL2PS_POINT_COINCIDENT) break;
    }
    switch(ret){
    case GL2PS_POINT_INFRONT :
      cur = head;
      for(i = 1+offset; i < prim->numverts-1; i++){
        if(cur->front == NULL){
          cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
        }
        if(gl2psGetPlaneFromPoints(prim->verts[i].xyz,
                                   prim->verts[i+1].xyz,
                                   cur->front->plane)){
          cur = cur->front;
          cur->front = NULL;
          cur->back = NULL;
        }
      }
      if(cur->front == NULL){
        cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
      }
      if(gl2psGetPlaneFromPoints(prim->verts[i].xyz,
                                 prim->verts[offset].xyz,
                                 cur->front->plane)){
        cur->front->front = NULL;
        cur->front->back = NULL;
      }
      else{
        gl2psFree(cur->front);
        cur->front = NULL;
      }
      break;
    case GL2PS_POINT_BACK :
      for(i = 0; i < 4; i++){
        head->plane[i] = -head->plane[i];
      }
      cur = head;
      for(i = 1+offset; i < prim->numverts-1; i++){
        if(cur->front == NULL){
          cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
        }
        if(gl2psGetPlaneFromPoints(prim->verts[i+1].xyz,
                                   prim->verts[i].xyz,
                                   cur->front->plane)){
          cur = cur->front;
          cur->front = NULL;
          cur->back = NULL;
        }
      }
      if(cur->front == NULL){
        cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
      }
      if(gl2psGetPlaneFromPoints(prim->verts[offset].xyz,
                                 prim->verts[i].xyz,
                                 cur->front->plane)){
        cur->front->front = NULL;
        cur->front->back = NULL;
      }
      else{
        gl2psFree(cur->front);
        cur->front = NULL;
      }
      break;
    default:
      gl2psFree(head);
      return;
    }
    (*tree) = head;
  }
}

static GLint gl2psCheckPrimitive(GL2PSprimitive *prim, GL2PSplane plane)
{
  GLint i;
  GLint pos;

  pos = gl2psCheckPoint(prim->verts[0].xyz, plane);
  for(i = 1; i < prim->numverts; i++){
    pos |= gl2psCheckPoint(prim->verts[i].xyz, plane);
    if(pos == (GL2PS_POINT_INFRONT | GL2PS_POINT_BACK)) return GL2PS_SPANNING;
  }
  if(pos & GL2PS_POINT_INFRONT)   return GL2PS_IN_FRONT_OF;
  else if(pos & GL2PS_POINT_BACK) return GL2PS_IN_BACK_OF;
  else                            return GL2PS_COINCIDENT;
}

static GL2PSprimitive *gl2psCreateSplitPrimitive2D(GL2PSprimitive *parent,
                                                   GLshort numverts,
                                                   GL2PSvertex *vertx)
{
  GLint i;
  GL2PSprimitive *child = gl2psCreatePrimitive();

  for( i=0; i<4; i++ )
      child->plane[i] = parent->plane[i];

  if(parent->type == GL2PS_IMAGEMAP){
    child->type = GL2PS_IMAGEMAP;
    child->data.image = parent->data.image;
  }
  else {
    switch(numverts){
    case 1 : child->type = GL2PS_POINT; break;
    case 2 : child->type = GL2PS_LINE; break;
    case 3 : child->type = GL2PS_TRIANGLE; break;
    case 4 : child->type = GL2PS_QUADRANGLE; break;
    }
  }
  child->boundary = 0; /* not done! */
  child->depth = parent->depth;
  child->culled = parent->culled;
  child->dash = parent->dash;
  child->width = parent->width;
  child->numverts = numverts;
  child->verts = (GL2PSvertex*)gl2psMalloc(numverts * sizeof(GL2PSvertex));
  for(i = 0; i < numverts; i++){
    child->verts[i] = vertx[i];
  }
  return child;
}

static void gl2psSplitPrimitive2D(GL2PSprimitive *prim,
                                  GL2PSplane plane, 
                                  GL2PSprimitive **front, 
                                  GL2PSprimitive **back)
{
  /* cur will hold the position of the current vertex
     prev will hold the position of the previous vertex
     prev0 will hold the position of the vertex number 0
     v1 and v2 represent the current and previous vertices, respectively
     flag is set if the current vertex should be checked against the plane */
  GLint cur = -1, prev = -1, i, v1 = 0, v2 = 0, flag = 1, prev0 = -1;
  
  /* list of vertices that will go in front and back primitive */
  GL2PSvertex *front_list = NULL, *back_list = NULL;
  
  /* number of vertices in front and back list */
  GLshort front_count = 0, back_count = 0;

  for(i = 0; i <= prim->numverts; i++){
    v1 = i;
    if(v1 == prim->numverts){
      if(prim->numverts < 3) break;
      v1 = 0;
      v2 = prim->numverts-1;
      cur = prev0;
    }
    else if(flag){
      cur = gl2psCheckPoint(prim->verts[v1].xyz, plane);
      if(i == 0){
        prev0 = cur;
      }
    } 
    if(((prev == -1) || (prev == cur) || (prev == 0) || (cur == 0)) &&
       (i < prim->numverts)){
      if(cur == GL2PS_POINT_INFRONT){
        front_count++;
        front_list = (GL2PSvertex*)gl2psRealloc(front_list,
                                                sizeof(GL2PSvertex)*front_count);
        front_list[front_count-1] = prim->verts[v1];
      }
      else if(cur == GL2PS_POINT_BACK){
        back_count++;
        back_list = (GL2PSvertex*)gl2psRealloc(back_list,
                                               sizeof(GL2PSvertex)*back_count);
        back_list[back_count-1] = prim->verts[v1];
      }
      else{
        front_count++;
        front_list = (GL2PSvertex*)gl2psRealloc(front_list,
                                                sizeof(GL2PSvertex)*front_count);
        front_list[front_count-1] = prim->verts[v1];
        back_count++;
        back_list = (GL2PSvertex*)gl2psRealloc(back_list,
                                               sizeof(GL2PSvertex)*back_count);
        back_list[back_count-1] = prim->verts[v1];
      }
      flag = 1;
    }
    else if((prev != cur) && (cur != 0) && (prev != 0)){
      if(v1 != 0){
        v2 = v1-1;
        i--;
      }
      front_count++;
      front_list = (GL2PSvertex*)gl2psRealloc(front_list,
                                              sizeof(GL2PSvertex)*front_count);
      gl2psCutEdge(&prim->verts[v2],
                   &prim->verts[v1],
                   plane,
                   &front_list[front_count-1]);
      back_count++;
      back_list = (GL2PSvertex*)gl2psRealloc(back_list,
                                             sizeof(GL2PSvertex)*back_count);
      back_list[back_count-1] = front_list[front_count-1];
      flag = 0;
    }
    prev = cur;
  }
  *front = gl2psCreateSplitPrimitive2D(prim, front_count, front_list);
  *back = gl2psCreateSplitPrimitive2D(prim, back_count, back_list);
  gl2psFree(front_list);
  gl2psFree(back_list);
}

static GLint gl2psAddInBspImageTree(GL2PSprimitive *prim, GL2PSbsptree2d **tree)
{
  GLint ret = 0;
  GL2PSprimitive *frontprim = NULL, *backprim = NULL;
  
  /* FIXME: until we consider the actual extent of text strings and
     pixmaps, never cull them. Otherwise the whole string/pixmap gets
     culled as soon as the reference point is hidden */
  if(prim->type == GL2PS_PIXMAP || prim->type == GL2PS_TEXT){
    return 1;
  }

  if(*tree == NULL){
    if((prim->type != GL2PS_IMAGEMAP) && (GL_FALSE == gl2ps->zerosurfacearea)){
      gl2psAddPlanesInBspTreeImage(gl2ps->primitivetoadd, tree);
    }
    return 1;
  }
  else{
    switch(gl2psCheckPrimitive(prim, (*tree)->plane)){
    case GL2PS_IN_BACK_OF: return gl2psAddInBspImageTree(prim, &(*tree)->back);
    case GL2PS_IN_FRONT_OF: 
      if((*tree)->front != NULL) return gl2psAddInBspImageTree(prim, &(*tree)->front);
      else                       return 0;
    case GL2PS_SPANNING:
      gl2psSplitPrimitive2D(prim, (*tree)->plane, &frontprim, &backprim);
      ret = gl2psAddInBspImageTree(backprim, &(*tree)->back);
      if((*tree)->front != NULL){
        if(gl2psAddInBspImageTree(frontprim, &(*tree)->front)){
          ret = 1;
        }
      }
      gl2psFree(frontprim->verts);
      gl2psFree(frontprim);
      gl2psFree(backprim->verts);
      gl2psFree(backprim);
      return ret;
    case GL2PS_COINCIDENT:
      if((*tree)->back != NULL){
        gl2ps->zerosurfacearea = GL_TRUE;
        ret = gl2psAddInBspImageTree(prim, &(*tree)->back);
        gl2ps->zerosurfacearea = GL_FALSE;
        if(ret) return ret;
      }
      if((*tree)->front != NULL){
        gl2ps->zerosurfacearea = GL_TRUE;
        ret = gl2psAddInBspImageTree(prim, &(*tree)->front);
        gl2ps->zerosurfacearea = GL_FALSE;
        if(ret) return ret;
      }
      if(prim->type == GL2PS_LINE) return 1;
      else                         return 0;
    }
  }
  return 0;
}

static void gl2psAddInImageTree(void *data)
{
  GL2PSprimitive *prim = *(GL2PSprimitive **)data;
  gl2ps->primitivetoadd = prim;
  if(prim->type == GL2PS_IMAGEMAP && prim->data.image->format == GL2PS_IMAGEMAP_VISIBLE){
    prim->culled = 1;
  }
  else if(!gl2psAddInBspImageTree(prim, &gl2ps->imagetree)){
    prim->culled = 1;
  }
  else if(prim->type == GL2PS_IMAGEMAP){
    prim->data.image->format = GL2PS_IMAGEMAP_VISIBLE;
  }
}

/* Boundary construction */

static void gl2psAddBoundaryInList(GL2PSprimitive *prim, GL2PSlist *list)
{
  GL2PSprimitive *b;
  GLshort i;
  GL2PSxyz c;
  GL2PSplane plane;

  c[0] = c[1] = c[2] = 0.0F;
  for(i = 0; i < prim->numverts; i++){
    c[0] += prim->verts[i].xyz[0];
    c[1] += prim->verts[i].xyz[1];
  }
  c[0] /= prim->numverts;
  c[1] /= prim->numverts;

  for(i = 0; i < prim->numverts; i++){
    if(prim->boundary & (GLint)pow(2., i)){
      b = gl2psCreatePrimitive();
      b->type = GL2PS_LINE;
      b->dash = prim->dash;
      b->depth = prim->depth; /* FIXME: this is wrong */
      b->culled = prim->culled;
      b->width = prim->width;
      b->boundary = 0;
      b->numverts = 2;
      b->verts = (GL2PSvertex*)gl2psMalloc(2 * sizeof(GL2PSvertex));
      gl2psGetPlane( b, plane );

#if 0 /* FIXME: need to work on boundary offset... */
      v[0] = c[0] - prim->verts[i].xyz[0];
      v[1] = c[1] - prim->verts[i].xyz[1];
      v[2] = 0.0F;
      norm = gl2psNorm(v);
      v[0] /= norm;
      v[1] /= norm;
      b->verts[0].xyz[0] = prim->verts[i].xyz[0] +0.1*v[0];
      b->verts[0].xyz[1] = prim->verts[i].xyz[1] +0.1*v[1];
      b->verts[0].xyz[2] = prim->verts[i].xyz[2];
      v[0] = c[0] - prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[0];
      v[1] = c[1] - prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[1];
      norm = gl2psNorm(v);
      v[0] /= norm;
      v[1] /= norm;
      b->verts[1].xyz[0] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[0] +0.1*v[0];
      b->verts[1].xyz[1] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[1] +0.1*v[1];
      b->verts[1].xyz[2] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[2];
#else
      b->verts[0].xyz[0] = prim->verts[i].xyz[0];
      b->verts[0].xyz[1] = prim->verts[i].xyz[1];
      b->verts[0].xyz[2] = prim->verts[i].xyz[2];
      b->verts[1].xyz[0] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[0];
      b->verts[1].xyz[1] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[1];
      b->verts[1].xyz[2] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[2];
#endif

      b->verts[0].rgba[0] = 0.0F;
      b->verts[0].rgba[1] = 0.0F;
      b->verts[0].rgba[2] = 0.0F;
      b->verts[0].rgba[3] = 0.0F;
      b->verts[1].rgba[0] = 0.0F;
      b->verts[1].rgba[1] = 0.0F;
      b->verts[1].rgba[2] = 0.0F;
      b->verts[1].rgba[3] = 0.0F;
      gl2psListAdd(list, &b);
    }
  }

}

static void gl2psBuildPolygonBoundary(GL2PSbsptree *tree)
{
  GLint i;
  GL2PSprimitive *prim;

  if(!tree) return;
  gl2psBuildPolygonBoundary(tree->back);
  for(i = 0; i < gl2psListNbr(tree->primitives); i++){
    prim = *(GL2PSprimitive**)gl2psListPointer(tree->primitives, i);
    if(prim->boundary) gl2psAddBoundaryInList(prim, tree->primitives);
  }
  gl2psBuildPolygonBoundary(tree->front);
}

/********************************************************************* 
 *
 * Feedback buffer parser
 *
 *********************************************************************/

static void gl2psAddPolyPrimitive(GLshort type, GLshort numverts, 
                                  GL2PSvertex *verts, GLint offset, 
                                  unsigned short dash, GLfloat width,
                                  char boundary)
{
  GLshort i;
  GLfloat factor, units, area, dZ, dZdX, dZdY, maxdZ;
  GL2PSprimitive *prim;
  GL2PSplane plane;

  prim = gl2psCreatePrimitive();
  prim->type = type;
  prim->numverts = numverts;
  prim->verts = (GL2PSvertex*)gl2psMalloc(numverts * sizeof(GL2PSvertex));
  memcpy(prim->verts, verts, numverts * sizeof(GL2PSvertex));
  prim->boundary = boundary;
  prim->dash = dash;
  prim->width = width;
  prim->culled = 0;
  gl2psGetPlane( prim, plane );

  if(gl2ps->options & GL2PS_SIMPLE_LINE_OFFSET){

    if(type == GL2PS_LINE){
      if(gl2ps->sort == GL2PS_SIMPLE_SORT){
        prim->verts[0].xyz[2] -= GL2PS_SIMPLE_OFFSET_LARGE;
        prim->verts[1].xyz[2] -= GL2PS_SIMPLE_OFFSET_LARGE;
      }
      else{
        prim->verts[0].xyz[2] -= GL2PS_SIMPLE_OFFSET;
        prim->verts[1].xyz[2] -= GL2PS_SIMPLE_OFFSET;
      }
    }

  }
  else if(offset && type == GL2PS_TRIANGLE){

    /* FIXME: this needs some more work... */

    if(gl2ps->sort == GL2PS_SIMPLE_SORT){    
      factor = gl2ps->offset[0];
      units = gl2ps->offset[1];
    }
    else{
      factor = gl2ps->offset[0] / 800.0F;
      units = gl2ps->offset[1] / 800.0F;
    }

    area = 
      (prim->verts[1].xyz[0] - prim->verts[0].xyz[0]) * 
      (prim->verts[2].xyz[1] - prim->verts[1].xyz[1]) - 
      (prim->verts[2].xyz[0] - prim->verts[1].xyz[0]) * 
      (prim->verts[1].xyz[1] - prim->verts[0].xyz[1]);
    dZdX = 
      (prim->verts[2].xyz[1] - prim->verts[1].xyz[1]) *
      (prim->verts[1].xyz[2] - prim->verts[0].xyz[2]) -
      (prim->verts[1].xyz[1] - prim->verts[0].xyz[1]) *
      (prim->verts[2].xyz[2] - prim->verts[1].xyz[2]) / area;
    dZdY = 
      (prim->verts[1].xyz[0] - prim->verts[0].xyz[0]) *
      (prim->verts[2].xyz[2] - prim->verts[1].xyz[2]) -
      (prim->verts[2].xyz[0] - prim->verts[1].xyz[0]) *
      (prim->verts[1].xyz[2] - prim->verts[0].xyz[2]) / area;
    
    maxdZ = (GLfloat)sqrt(dZdX*dZdX + dZdY*dZdY);

    dZ = factor * maxdZ + units;

    prim->verts[0].xyz[2] += dZ;
    prim->verts[1].xyz[2] += dZ;
    prim->verts[2].xyz[2] += dZ;
  }

  prim->depth = 0.;
  if(gl2ps->sort == GL2PS_SIMPLE_SORT){
    for(i = 0; i < numverts; i++){
      prim->depth += prim->verts[i].xyz[2]; 
    }
    prim->depth /= (GLfloat)numverts;
  }
  
  gl2psListAdd(gl2ps->primitives, &prim);
}

static GLint gl2psGetVertex(GL2PSvertex *v, GLfloat *p)
{
  GLint i;

  v->xyz[0] = p[0];
  v->xyz[1] = p[1];
  v->xyz[2] = GL2PS_DEPTH_FACT * p[2];

  if(gl2ps->colormode == GL_COLOR_INDEX && gl2ps->colorsize > 0){
    i = (GLint)(p[3] + 0.5);
    v->rgba[0] = gl2ps->colormap[i][0];
    v->rgba[1] = gl2ps->colormap[i][1];
    v->rgba[2] = gl2ps->colormap[i][2];
    v->rgba[3] = gl2ps->colormap[i][3];
    return 4;
  }
  else{
    v->rgba[0] = p[3];
    v->rgba[1] = p[4];
    v->rgba[2] = p[5];
    v->rgba[3] = p[6];
    return 7;
  }
}

char* gl2psImportString( GLfloat** cur )
{
  if( cur )
  {
    int len, i;
    char* res = NULL;
    len = ( int )( *(*cur+1) ); (*cur)+=2;

    res = ( char* )gl2psMalloc( ( len+1 )*sizeof( char ) );

    for( i=0; i<len; i++, (*cur)+=2 )
        res[i] = ( char )( *(*cur+1) );
    res[len] = '\0';
    return res;
  }
  else
    return NULL;  
}

static void gl2psParseFeedbackBuffer(GLint used)
{
  char flag;
  unsigned short dash = 0;
  GLboolean boundary;
  GLint i, sizeoffloat, count, v, vtot, offset = 0;
  GLfloat lwidth = 1.0F, psize = 1.0F;
  GLfloat *current;
  GL2PSvertex vertices[3];
  GL2PSprimitive *prim;
  GL2PSimagemap *node;
  GL2PSplane plane;

  current = gl2ps->feedback;
  boundary = gl2ps->boundary = GL_FALSE;

  /*for( i=0; i<used; i++ )
      printf( "%.0f , ", current[i] );*/

  while(used > 0){

    if(GL_TRUE == boundary) gl2ps->boundary = GL_TRUE;
    
    switch((GLint)*current){
    case GL_POINT_TOKEN :
      current ++;
      used --;
      i = gl2psGetVertex(&vertices[0], current);
      current += i;
      used    -= i;
      gl2psAddPolyPrimitive(GL2PS_POINT, 1, vertices, 0, dash, psize, 0);
      break;
    case GL_LINE_TOKEN :
    case GL_LINE_RESET_TOKEN :
      current ++;
      used --;
      i = gl2psGetVertex(&vertices[0], current);
      current += i;
      used    -= i;
      i = gl2psGetVertex(&vertices[1], current);
      current += i;
      used    -= i;
      gl2psAddPolyPrimitive(GL2PS_LINE, 2, vertices, 0, dash, lwidth, 0);
      break;
    case GL_POLYGON_TOKEN :
      count = (GLint)current[1];
      current += 2;
      used -= 2;
      v = vtot = 0;
      while(count > 0 && used > 0){
        i = gl2psGetVertex(&vertices[v], current);
        gl2psAdaptVertexForBlending(&vertices[v]);
        current += i;
        used    -= i;
        count --;
        vtot++;
        if(v == 2){
          if(GL_TRUE == boundary){
            if(!count && vtot == 2) flag = 1|2|4;
            else if(!count) flag = 2|4;
            else if(vtot == 2) flag = 1|2;
            else flag = 2;
          }
          else
            flag = 0;
          gl2psAddPolyPrimitive(GL2PS_TRIANGLE, 3, vertices, 
                                offset, dash, 1, flag);
          vertices[1] = vertices[2];
        }
        else
          v ++;
      }
      break;      
    case GL_BITMAP_TOKEN :
    case GL_DRAW_PIXEL_TOKEN :
    case GL_COPY_PIXEL_TOKEN :
      current ++;
      used --;
      i = gl2psGetVertex(&vertices[0], current);
      current += i;
      used    -= i;
      break;      
    case GL_PASS_THROUGH_TOKEN :
      switch((GLint)current[1]){
      case GL2PS_LINE_STIPPLE_PATTERN : 
          current += 2;
          used -= 2;
          gl2ps->line_pattern = ( unsigned short ) current[1];
          break;
      case GL2PS_BEGIN_POLYGON_OFFSET_FILL : offset = 1; break;
      case GL2PS_END_POLYGON_OFFSET_FILL : offset = 0; break;
      case GL2PS_BEGIN_POLYGON_BOUNDARY : boundary = GL_TRUE; break;
      case GL2PS_END_POLYGON_BOUNDARY : boundary = GL_FALSE; break;
      /*case GL2PS_BEGIN_LINE_STIPPLE : dash = 4; break;*/
      case GL2PS_BEGIN_LINE_STIPPLE : dash = gl2ps->line_pattern; break;
      case GL2PS_END_LINE_STIPPLE : dash = 0xFFFF; break;
      case GL2PS_BEGIN_BLEND : gl2ps->blending = GL_TRUE; break;
      case GL2PS_END_BLEND : gl2ps->blending = GL_FALSE; break;
      case GL2PS_SRC_BLEND : 
        current += 2; 
        used -= 2; 
        gl2ps->blendfunc[0] = (GLint)current[1];
        break;
      case GL2PS_DST_BLEND : 
        current += 2; 
        used -= 2; 
        gl2ps->blendfunc[1] = (GLint)current[1];
        break;
      case GL2PS_SET_POINT_SIZE : 
        current += 2; 
        used -= 2; 
        psize = current[1];
        break;
      case GL2PS_SET_LINE_WIDTH : 
        current += 2; 
        used -= 2; 
        lwidth = current[1];
        break;
      case GL2PS_DRAW_IMAGEMAP_TOKEN :
        prim = gl2psCreatePrimitive();
        prim->type = GL2PS_IMAGEMAP;
        prim->boundary = 0;
        prim->numverts = 4;
        prim->verts = (GL2PSvertex *)gl2psMalloc(4 * sizeof(GL2PSvertex));
        prim->culled = 0;
        prim->dash = 0;
        prim->width = 1;
        gl2psGetPlane( prim, plane );
        
        node = (GL2PSimagemap*)gl2psMalloc(sizeof(GL2PSimagemap));
        node->image = (GL2PSimage*)gl2psMalloc(sizeof(GL2PSimage));
        node->image->type = 0;
        node->image->format = 0;
        node->next = NULL;
        
        if(gl2ps->imagemap_head == NULL)
          gl2ps->imagemap_head = node;
        else
          gl2ps->imagemap_tail->next = node;
        gl2ps->imagemap_tail = node;
        prim->data.image = node->image;
        
        current += 2; used -= 2;
        i = gl2psGetVertex(&prim->verts[0], &current[1]);
        current += i; used -= i;
        
        node->image->width = (GLint)current[2];
        current += 2; used -= 2;
        node->image->height = (GLint)current[2];
        prim->verts[0].xyz[0] = ( GLfloat )( prim->verts[0].xyz[0] - (int)(node->image->width / 2) + 0.5 );
        prim->verts[0].xyz[1] = ( GLfloat )( prim->verts[0].xyz[1] - (int)(node->image->height / 2) + 0.5 );
        prim->depth = prim->verts[0].xyz[2];
        for(i = 1; i < 4; i++){
          for(v = 0; v < 3; v++){
            prim->verts[i].xyz[v] = prim->verts[0].xyz[v];
            prim->verts[i].rgba[v] = prim->verts[0].rgba[v];
          }
          prim->verts[i].rgba[v] = prim->verts[0].rgba[v];
        }
        prim->verts[1].xyz[0] = prim->verts[1].xyz[0] + node->image->width;
        prim->verts[2].xyz[0] = prim->verts[1].xyz[0];
        prim->verts[2].xyz[1] = prim->verts[2].xyz[1] + node->image->height;
        prim->verts[3].xyz[1] = prim->verts[2].xyz[1];

        sizeoffloat = sizeof(GLfloat);
        v = 2 * sizeoffloat;
        vtot = node->image->height + node->image->height * 
          ((node->image->width-1)/8);
        node->image->pixels = (GLfloat*)gl2psMalloc(v + vtot);
        node->image->pixels[0] = prim->verts[0].xyz[0];
        node->image->pixels[1] = prim->verts[0].xyz[1];
        
        for(i = 0; i < vtot; i += sizeoffloat){
          current += 2; used -= 2;
          if((vtot - i) >= 4)
            memcpy(&(((char*)(node->image->pixels))[i + v]), &(current[2]), sizeoffloat);
          else
            memcpy(&(((char*)(node->image->pixels))[i + v]), &(current[2]), vtot - i);
        }
        current++; used--;
        gl2psListAdd(gl2ps->primitives, &prim);
        break;

        case GL2PS_MARKER:
        {
            GLint i, v;
            GLfloat xorg, yorg;
            GLsizei w, h, size, size2;
            GL2PSxyz vv;
            GLfloat  rgba[4];
            GLfloat* ptr;

            prim = gl2psCreatePrimitive();
            prim->type = GL2PS_IMAGEMAP;
            prim->boundary = 0;
            prim->numverts = 4;
            prim->verts = (GL2PSvertex *)gl2psMalloc(4 * sizeof(GL2PSvertex));
            prim->culled = 0;
            prim->dash = 0;
            prim->width = 1;
            gl2psGetPlane( prim, plane );

            node = (GL2PSimagemap*)gl2psMalloc(sizeof(GL2PSimagemap));
            node->image = (GL2PSimage*)gl2psMalloc(sizeof(GL2PSimage));
            node->image->type = 0;
            node->image->format = 0;
            node->next = NULL;
        
            if(gl2ps->imagemap_head == NULL)
                gl2ps->imagemap_head = node;
            else
                gl2ps->imagemap_tail->next = node;
            gl2ps->imagemap_tail = node;
            prim->data.image = node->image;



            current += 2;
            used -= 2;

            w = ( GLsizei )current[1];
            h = ( GLsizei )current[3];

            xorg = current[5];
            yorg = current[7];

            current += 8;
            used -= 8;

            size = h + h * ( ( w-1 ) / 8 );
            size2 = size*2;

            prim->data.image->pixels = ( GLfloat* ) gl2psMalloc( ( 2+size )*sizeof( GLfloat ) );
            prim->data.image->width = w;
            prim->data.image->height = h;
            prim->data.image->format = GL_RGBA;

            if( current[1+size2]>-1E+14 )
            {
                vv[0] = ( GLfloat )( current[1+size2] - ( int ) w/2 + 0.5 );
                vv[1] = ( GLfloat )( current[2+size2] + ( int ) h/2 - 0.5 );
                vv[2] = current[3+size2];
                rgba[0] = current[4+size2];
                rgba[1] = current[5+size2];
                rgba[2] = current[6+size2];
                rgba[3] = current[7+size2];


                for( i=0; i<4; i++ )
                    prim->verts[0].rgba[i] = rgba[i];            

                prim->verts[0].xyz[0] = ( GLfloat )( vv[0] - ( int ) w/2 + 0.5 );
                prim->verts[0].xyz[1] = ( GLfloat )( vv[1] + ( int ) h/2 - 0.5 );
                prim->depth = vv[2];
                for( i=1; i<4; i++ )
                {
                    for( v=0; v<3; v++)
                    {
                        prim->verts[i].xyz[v] = prim->verts[0].xyz[v];
                        prim->verts[i].rgba[v] = prim->verts[0].rgba[v];
                    }
                    prim->verts[i].rgba[v] = prim->verts[0].rgba[v];
                }
                prim->verts[1].xyz[0] = prim->verts[1].xyz[0] + w;
                prim->verts[2].xyz[0] = prim->verts[1].xyz[0];
                prim->verts[2].xyz[1] = prim->verts[2].xyz[1] + h;
                prim->verts[3].xyz[1] = prim->verts[2].xyz[1];



                ptr = prim->data.image->pixels;
                ptr[0] = vv[0];
                ptr[1] = vv[1];
                for( i=0; i<size; i++ )
                    ptr[ i+2 ] = current[ 1 + 2*i ];

                current += 8+size2;
                used -= 8+size2;

                gl2psListAdd( gl2ps->primitives, &prim );
            }
            else
            {
                current+=size2;
                used-=size2;
            }
        }
        break;

        case GL2PS_BUFTEXT:
        {
            GLfloat *oldcur;
            char* text, *font;
            GLint i, ind;
            GLfloat height;
            GLfloat xyz[3], rgba[4];

            current += 2;
            used -= 2;

            for( i=0, ind=1; i<3; i++, ind++ )
                xyz[i] = current[ind];
            for( i=0; i<4; i++, ind++ )
                rgba[i] = current[ind];

            ind++; height = current[ ind ]; ind++;

            current+=ind; used-=ind;
            oldcur = current;
            text = gl2psImportString( &current );
            font = gl2psImportString( &current );
            used -= ( current-oldcur );

            if( gl2ps->options & GL2PS_NO_TEXT )
            {
            }
            else
            {
                prim = gl2psCreatePrimitive();
                prim->type = GL2PS_TEXT;
                prim->boundary = 0;
                prim->numverts = 1;
                prim->verts = (GL2PSvertex*)gl2psMalloc(sizeof(GL2PSvertex));
                prim->verts[0].xyz[0] = xyz[0];
                prim->verts[0].xyz[1] = xyz[1];
                prim->verts[0].xyz[2] = GL2PS_DEPTH_FACT * xyz[2];
                prim->depth = xyz[2];
                prim->culled = 0;
                prim->dash = 0;
                prim->width = 1;

                gl2psGetPlane( prim, plane );

                for( i=0; i<4; i++ )
                    prim->verts[0].rgba[i] = rgba[i];

                prim->data.text = (GL2PSstring*)gl2psMalloc(sizeof(GL2PSstring));
                prim->data.text->str = (char*)gl2psMalloc((strlen(text)+1)*sizeof(char));
                strcpy(prim->data.text->str, text); 
                prim->data.text->fontname = (char*)gl2psMalloc((strlen(font)+1)*sizeof(char));
                strcpy(prim->data.text->fontname, font);
                prim->data.text->fontsize = ( short )height;
                prim->data.text->alignment = GL2PS_TEXT_BL;
                prim->data.text->angle = 0.0f;

                gl2psListAdd(gl2ps->primitives, &prim);
            }

            if( text )
              free( text );

            if( font )
              free( font );

            current -= 2;
            used += 2;
        }
        break;
      }
      current += 2; 
      used -= 2; 
      break;
    default :
      gl2psMsg(GL2PS_WARNING, "Unknown token in buffer");
      current ++;
      used --;
      break;
    }
  }
}

/********************************************************************* 
 *
 * PostScript routines
 *
 *********************************************************************/

static GLfloat gl2psGetRGB(GL2PSimage* im, GLuint x, GLuint y,
                           GLfloat *red, GLfloat *green, GLfloat *blue)
{
  
  GLsizei width = im->width;
  GLsizei height = im->height;
  GLfloat *pixels = im->pixels;
  GLfloat *pimag;

  /* OpenGL image is from down to up, PS image is up to down */  
  switch(im->format){
  case GL_RGBA:
    pimag = pixels + 4 * (width * (height - 1 - y) + x);
    break;
  case GL_RGB:
  default:
    pimag = pixels + 3 * (width * (height - 1 - y) + x);
    break;
  }
  *red = *pimag; pimag++;
  *green = *pimag; pimag++;
  *blue = *pimag; pimag++;

  return (im->format == GL_RGBA) ? *pimag : 1.0F;
}

static void gl2psWriteByte(unsigned char byte)
{
  unsigned char h = byte / 16;
  unsigned char l = byte % 16;
  gl2psPrintf("%x%x", h, l);
}

static void gl2psPrintPostScriptPixmap(GLfloat x, GLfloat y, GL2PSimage *im)
{
  int nbhex, nbyte2, nbyte4, nbyte8;
  GLsizei row, col, col_max;
  GLfloat dr, dg, db;
  unsigned char red, green, blue, b, grey;
  GLsizei width = im->width;
  GLsizei height = im->height;

  /* FIXME: should we define an option for these? */
  int greyscale = 0; /* set to 1 to output greyscale image */
  int nbits = 8; /* number of bits per color compoment (2, 4 or 8) */

  if((width <= 0) || (height <= 0)) return;

  gl2psPrintf("gsave\n");
  gl2psPrintf("%.2f %.2f translate\n", x, y); 
  gl2psPrintf("%d %d scale\n", (int)width, (int)height); 

  if(greyscale){ /* greyscale, 8 bits per pixel */
    gl2psPrintf("/picstr %d string def\n", (int)width); 
    gl2psPrintf("%d %d %d\n", (int)width, (int)height, 8); 
    gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", (int)width, (int)height, (int)height); 
    gl2psPrintf("{ currentfile picstr readhexstring pop }\n");
    gl2psPrintf("image\n");
    for(row = 0; row < height; row++){
      for(col = 0; col < width; col++){ 
        gl2psGetRGB(im, col, row, &dr, &dg, &db);
        grey = (unsigned char)(255.0 * (0.30 * dr + 0.59 * dg + 0.11 * db));
        gl2psWriteByte(grey);
      }
      gl2psPrintf("\n");
    }
    nbhex = width * height * 2; 
    gl2psPrintf("%%%% nbhex digit          :%d\n", nbhex); 
  }
  else if(nbits == 2){ /* color, 2 bits for r and g and b; rgbs following each other */
    nbyte2 = (width * 3)/4;
    nbyte2 /=3;
    nbyte2 *=3;
    col_max = (nbyte2 * 4)/3;
    gl2psPrintf("/rgbstr %d string def\n", nbyte2); 
    gl2psPrintf("%d %d %d\n", (int)col_max, (int)height, 2); 
    gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", (int)col_max, (int)height, (int)height); 
    gl2psPrintf("{ currentfile rgbstr readhexstring pop }\n" );
    gl2psPrintf("false 3\n" );
    gl2psPrintf("colorimage\n" );
    for(row = 0; row < height; row++){
      for(col = 0; col < col_max; col+=4){
        gl2psGetRGB(im, col, row, &dr, &dg, &db);
        red = (unsigned char)(3.0 * dr);
        green = (unsigned char)(3.0 * dg);
        blue = (unsigned char)(3.0 * db);
        b = red;
        b = (b<<2)+green;
        b = (b<<2)+blue;
        gl2psGetRGB(im, col+1, row, &dr, &dg, &db);
        red = (unsigned char)(3.0 * dr);
        green = (unsigned char)(3.0 * dg);
        blue = (unsigned char)(3.0 * db);
        b = (b<<2)+red;
        gl2psWriteByte(b);
        b = green;
        b = (b<<2)+blue;
        gl2psGetRGB(im, col+2, row, &dr, &dg, &db);
        red = (unsigned char)(3.0 * dr);
        green = (unsigned char)(3.0 * dg);
        blue = (unsigned char)(3.0 * db);
        b = (b<<2)+red;
        b = (b<<2)+green;
        gl2psWriteByte(b);
        b = blue;
        gl2psGetRGB(im, col+3, row, &dr, &dg, &db);
        red = (unsigned char)(3.0 * dr);
        green = (unsigned char)(3.0 * dg);
        blue = (unsigned char)(3.0 * db);
        b = (b<<2)+red;
        b = (b<<2)+green;
        b = (b<<2)+blue;
        gl2psWriteByte(b);
      }
      gl2psPrintf("\n");
    }
  }
  else if(nbits == 4){ /* color, 4 bits for r and g and b; rgbs following each other */
    nbyte4 = (width  * 3)/2;
    nbyte4 /=3;
    nbyte4 *=3;
    col_max = (nbyte4 * 2)/3;
    gl2psPrintf("/rgbstr %d string def\n", nbyte4);
    gl2psPrintf("%d %d %d\n", (int)col_max, (int)height, 4);
    gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", (int)col_max, (int)height, (int)height);
    gl2psPrintf("{ currentfile rgbstr readhexstring pop }\n");
    gl2psPrintf("false 3\n");
    gl2psPrintf("colorimage\n");
    for(row = 0; row < height; row++){
      for(col = 0; col < col_max; col+=2){
        gl2psGetRGB(im, col, row, &dr, &dg, &db);
        red = (unsigned char)(15. * dr);
        green = (unsigned char)(15. * dg);
        gl2psPrintf("%x%x", red, green);
        blue = (unsigned char)(15. * db);
        gl2psGetRGB(im, col+1, row, &dr, &dg, &db);
        red = (unsigned char)(15. * dr);
        gl2psPrintf("%x%x",blue,red);
        green = (unsigned char)(15. * dg);
        blue = (unsigned char)(15. * db);
        gl2psPrintf("%x%x", green, blue);
      }
      gl2psPrintf("\n");
    }
  }
  else{ /* color, 8 bits for r and g and b; rgbs following each other */
    nbyte8 = width * 3;
    gl2psPrintf("/rgbstr %d string def\n", nbyte8);
    gl2psPrintf("%d %d %d\n", (int)width, (int)height, 8);
    gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", (int)width, (int)height, (int)height); 
    gl2psPrintf("{ currentfile rgbstr readhexstring pop }\n");
    gl2psPrintf("false 3\n");
    gl2psPrintf("colorimage\n");
    for(row = 0; row < height; row++){
      for(col = 0; col < width; col++){
        gl2psGetRGB(im, col, row, &dr, &dg, &db);
        red = (unsigned char)(255.0 * dr);
        gl2psWriteByte(red);
        green = (unsigned char)(255.0 * dg);
        gl2psWriteByte(green);
        blue = (unsigned char)(255.0 * db);
        gl2psWriteByte(blue);
      }
      gl2psPrintf("\n");
    }
  }

  gl2psPrintf("grestore\n");
}

static void gl2psPrintPostScriptImagemap(GLfloat x, GLfloat y,
                                         GLsizei width, GLsizei height,
                                         GLfloat *imagemap){
  int i, size;
  
  if((width <= 0) || (height <= 0)) return;
  
  size = height + height * ((width-1)/8);
  
  gl2psPrintf("gsave\n");
  gl2psPrintf("%.2f %.2f translate\n", x, y);
  gl2psPrintf("%d %d scale\n%d %d\ntrue\n", width, height, width, height); 
  gl2psPrintf("[ %d 0 0 -%d 0 %d ] {<", width, height, 0 );
  for(i = 0; i < size; i++){
    gl2psWriteByte( ( unsigned char )( *imagemap ) );
    imagemap++;
  }
  gl2psPrintf(">} imagemask\ngrestore\n");
}

static void gl2psPrintPostScriptHeader(void)
{
  GLint index;
  GLfloat rgba[4];
  time_t now;

#ifdef GL2PS_HAVE_ZLIB
  /* since compression is not part of the PostScript standard, we
     simply generate a gzipped PostScript file ("ps.gz" or
     "eps.gz") */
  
  char tmp[10] = {'\x1f', '\x8b', /* magic numbers: 0x1f, 0x8b */
                  8, /* compression method: Z_DEFLATED */
                  0, /* flags */
                  0, 0, 0, 0, /* time */
                  2, /* extra flags: max compression */
                  '\x03'}; /* OS code: 0x03 (Unix) */

  if(gl2ps->options & GL2PS_COMPRESS){
    gl2psSetupCompress();
    /* add the gzip file header */
    fwrite(tmp, 10, 1, gl2ps->stream);
  }
#endif  

  time(&now);

  if(gl2ps->format == GL2PS_PS){
    gl2psPrintf("%%!PS-Adobe-3.0\n");
  }
  else{
    gl2psPrintf("%%!PS-Adobe-3.0 EPSF-3.0\n");
  }

  gl2psPrintf("%%%%Title: %s\n"
              "%%%%Creator: GL2PS %d.%d.%d, (C) 1999-2004 Christophe Geuzaine <geuz@geuz.org>\n"
              "%%%%For: %s\n"
              "%%%%CreationDate: %s"
              "%%%%LanguageLevel: 3\n"
              "%%%%DocumentData: Clean7Bit\n"
              "%%%%Pages: 1\n",
              gl2ps->title, GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION, GL2PS_PATCH_VERSION,
              gl2ps->producer, ctime(&now));

  if(gl2ps->format == GL2PS_PS){
    gl2psPrintf("%%%%Orientation: %s\n"
                "%%%%DocumentMedia: Default %d %d 0 () ()\n",
                (gl2ps->options & GL2PS_LANDSCAPE) ? "Landscape" : "Portrait",
                (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[3] :
                (int)gl2ps->viewport[2], 
                (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[2] : 
                (int)gl2ps->viewport[3]);
  }

  gl2psPrintf("%%%%BoundingBox: %d %d %d %d\n"
              "%%%%EndComments\n",
              (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[1] : 
              (int)gl2ps->viewport[0],
              (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[0] :
              (int)gl2ps->viewport[1],
              (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[3] : 
              (int)gl2ps->viewport[2],
              (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[2] :
              (int)gl2ps->viewport[3]);

  /* RGB color: r g b C (replace C by G in output to change from rgb to gray)
     Grayscale: r g b G
     Font choose: size fontname FC
     Text string: (string) x y size fontname SXX
     Point primitive: x y size P
     Line width: width W
     Flat-shaded line: x2 y2 x1 y1 L
     Smooth-shaded line: x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 SL
     Flat-shaded triangle: x3 y3 x2 y2 x1 y1 T
     Smooth-shaded triangle: x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 ST */

  gl2psPrintf("%%%%BeginProlog\n"
              "/gl2psdict 64 dict def gl2psdict begin\n"
              "0 setlinecap 0 setlinejoin\n"
              "/tryPS3shading %s def %% set to false to force subdivision\n"
              "/rThreshold %g def %% red component subdivision threshold\n"
              "/gThreshold %g def %% green component subdivision threshold\n"
              "/bThreshold %g def %% blue component subdivision threshold\n",
              (gl2ps->options & GL2PS_NO_PS3_SHADING) ? "false" : "true",
              gl2ps->threshold[0], gl2ps->threshold[1], gl2ps->threshold[2]);

  gl2psPrintf("/BD { bind def } bind def\n"
              "/C  { setrgbcolor } BD\n"
              "/G  { 0.082 mul exch 0.6094 mul add exch 0.3086 mul add neg 1.0 add setgray } BD\n"
              "/W  { setlinewidth } BD\n");

  gl2psPrintf("/FC { findfont exch /SH exch def SH scalefont setfont } BD\n"
              "/SW { dup stringwidth pop } BD\n"
              "/S  { FC moveto show } BD\n"
              "/SBC{ FC moveto SW -2 div 0 rmoveto show } BD\n"
              "/SBR{ FC moveto SW neg 0 rmoveto show } BD\n"
              "/SCL{ FC moveto 0 SH -2 div rmoveto show } BD\n"
              "/SCC{ FC moveto SW -2 div SH -2 div rmoveto show } BD\n"
              "/SCR{ FC moveto SW neg SH -2 div rmoveto show } BD\n"
              "/STL{ FC moveto 0 SH neg rmoveto show } BD\n"
              "/STC{ FC moveto SW -2 div SH neg rmoveto show } BD\n"
              "/STR{ FC moveto SW neg SH neg rmoveto show } BD\n");

  gl2psPrintf("/P  { newpath 0.0 360.0 arc closepath fill } BD\n"
              "/L  { newpath moveto lineto stroke } BD\n"
              "/SL { C moveto C lineto stroke } BD\n"
              "/T  { newpath moveto lineto lineto closepath gsave fill grestore stroke } BD\n");
  
  /* Smooth-shaded triangle with PostScript level 3 shfill operator:
        x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 STshfill */

  gl2psPrintf("/STshfill {\n"
              "      /b1 exch def /g1 exch def /r1 exch def /y1 exch def /x1 exch def\n"
              "      /b2 exch def /g2 exch def /r2 exch def /y2 exch def /x2 exch def\n"
              "      /b3 exch def /g3 exch def /r3 exch def /y3 exch def /x3 exch def\n"
              "      gsave << /ShadingType 4 /ColorSpace [/DeviceRGB]\n"
              "      /DataSource [ 0 x1 y1 r1 g1 b1 0 x2 y2 r2 g2 b2 0 x3 y3 r3 g3 b3 ] >>\n"
              "      shfill grestore } BD\n");

  /* Flat-shaded triangle with middle color:
        x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 Tm */

  gl2psPrintf(/* stack : x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 */
              "/Tm { 3 -1 roll 8 -1 roll 13 -1 roll add add 3 div\n" /* r = (r1+r2+r3)/3 */
              /* stack : x3 y3 g3 b3 x2 y2 g2 b2 x1 y1 g1 b1 r */
              "      3 -1 roll 7 -1 roll 11 -1 roll add add 3 div\n" /* g = (g1+g2+g3)/3 */
              /* stack : x3 y3 b3 x2 y2 b2 x1 y1 b1 r g b */
              "      3 -1 roll 6 -1 roll 9 -1 roll add add 3 div" /* b = (b1+b2+b3)/3 */
              /* stack : x3 y3 x2 y2 x1 y1 r g b */
              " C T } BD\n");

  /* Split triangle in four sub-triangles (at sides middle points) and call the
     STnoshfill procedure on each, interpolating the colors in RGB space:
        x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 STsplit
     (in procedure comments key: (Vi) = xi yi ri gi bi) */

  gl2psPrintf("/STsplit {\n"
              "      4 index 15 index add 0.5 mul\n" /* x13 = (x1+x3)/2 */
              "      4 index 15 index add 0.5 mul\n" /* y13 = (y1+y3)/2 */
              "      4 index 15 index add 0.5 mul\n" /* r13 = (r1+r3)/2 */
              "      4 index 15 index add 0.5 mul\n" /* g13 = (g1+g3)/2 */
              "      4 index 15 index add 0.5 mul\n" /* b13 = (b1+b3)/2 */
              "      5 copy 5 copy 25 15 roll\n");

  /* at his point, stack = (V3) (V13) (V13) (V13) (V2) (V1) */

  gl2psPrintf("      9 index 30 index add 0.5 mul\n" /* x23 = (x2+x3)/2 */
              "      9 index 30 index add 0.5 mul\n" /* y23 = (y2+y3)/2 */
              "      9 index 30 index add 0.5 mul\n" /* r23 = (r2+r3)/2 */
              "      9 index 30 index add 0.5 mul\n" /* g23 = (g2+g3)/2 */
              "      9 index 30 index add 0.5 mul\n" /* b23 = (b2+b3)/2 */
              "      5 copy 5 copy 35 5 roll 25 5 roll 15 5 roll\n");

  /* stack = (V3) (V13) (V23) (V13) (V23) (V13) (V23) (V2) (V1) */

  gl2psPrintf("      4 index 10 index add 0.5 mul\n" /* x12 = (x1+x2)/2 */
              "      4 index 10 index add 0.5 mul\n" /* y12 = (y1+y2)/2 */
              "      4 index 10 index add 0.5 mul\n" /* r12 = (r1+r2)/2 */
              "      4 index 10 index add 0.5 mul\n" /* g12 = (g1+g2)/2 */
              "      4 index 10 index add 0.5 mul\n" /* b12 = (b1+b2)/2 */
              "      5 copy 5 copy 40 5 roll 25 5 roll 15 5 roll 25 5 roll\n");
  
  /* stack = (V3) (V13) (V23) (V13) (V12) (V23) (V13) (V1) (V12) (V23) (V12) (V2) */

  gl2psPrintf("      STnoshfill STnoshfill STnoshfill STnoshfill } BD\n");
  
  /* Gouraud shaded triangle using recursive subdivision until the difference
     between corner colors does not exceed the thresholds:
        x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 STnoshfill  */

  gl2psPrintf("/STnoshfill {\n"
              "      2 index 8 index sub abs rThreshold gt\n" /* |r1-r2|>rth */
              "      { STsplit }\n"
              "      { 1 index 7 index sub abs gThreshold gt\n" /* |g1-g2|>gth */
              "        { STsplit }\n"
              "        { dup 6 index sub abs bThreshold gt\n" /* |b1-b2|>bth */
              "          { STsplit }\n"
              "          { 2 index 13 index sub abs rThreshold gt\n" /* |r1-r3|>rht */
              "            { STsplit }\n"
              "            { 1 index 12 index sub abs gThreshold gt\n" /* |g1-g3|>gth */
              "              { STsplit }\n"
              "              { dup 11 index sub abs bThreshold gt\n" /* |b1-b3|>bth */
              "                { STsplit }\n"
              "                { 7 index 13 index sub abs rThreshold gt\n"); /* |r2-r3|>rht */
  gl2psPrintf("                  { STsplit }\n"
              "                  { 6 index 12 index sub abs gThreshold gt\n" /* |g2-g3|>gth */
              "                    { STsplit }\n"
              "                    { 5 index 11 index sub abs bThreshold gt\n" /* |b2-b3|>bth */
              "                      { STsplit }\n"
              "                      { Tm }\n" /* all colors sufficiently similar */
              "                      ifelse }\n"
              "                    ifelse }\n"
              "                  ifelse }\n"
              "                ifelse }\n"
              "              ifelse }\n"
              "            ifelse }\n"
              "          ifelse }\n"
              "        ifelse }\n"
              "      ifelse } BD\n");
  
  gl2psPrintf("tryPS3shading\n"
              "{ /shfill where\n"
              "  { /ST { STshfill } BD }\n"
              "  { /ST { STnoshfill } BD }\n"
              "  ifelse }\n"
              "{ /ST { STnoshfill } BD }\n"
              "ifelse\n");

  gl2psPrintf("end\n"
              "%%%%EndProlog\n"
              "%%%%BeginSetup\n"
              "/DeviceRGB setcolorspace\n"
              "gl2psdict begin\n"
              "%%%%EndSetup\n"
              "%%%%Page: 1 1\n"
              "%%%%BeginPageSetup\n");
  
  if(gl2ps->options & GL2PS_LANDSCAPE){
    gl2psPrintf("%d 0 translate 90 rotate\n",
                (int)gl2ps->viewport[3]);
  }

  gl2psPrintf("%%%%EndPageSetup\n"
              "mark\n"
              "gsave\n"
              "1.0 1.0 scale\n");
          
  if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
    if(gl2ps->colormode == GL_RGBA || gl2ps->colorsize == 0){
      glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba);
    }
    else{
      glGetIntegerv(GL_INDEX_CLEAR_VALUE, &index);
      rgba[0] = gl2ps->colormap[index][0];
      rgba[1] = gl2ps->colormap[index][1];
      rgba[2] = gl2ps->colormap[index][2];
      rgba[3] = 0.0F;
    }
    gl2psPrintf("%g %g %g C\n"
                "newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto\n"
                "closepath fill\n",
                rgba[0], rgba[1], rgba[2], 
                (int)gl2ps->viewport[0], (int)gl2ps->viewport[1], (int)gl2ps->viewport[2], 
                (int)gl2ps->viewport[1], (int)gl2ps->viewport[2], (int)gl2ps->viewport[3], 
                (int)gl2ps->viewport[0], (int)gl2ps->viewport[3]);
  }
}

static void gl2psPrintPostScriptColor(GL2PSrgba rgba)
{
  if(!gl2psSameColor(gl2ps->lastrgba, rgba)){
    gl2psSetLastColor(rgba);
    gl2psPrintf("%g %g %g C\n", rgba[0], rgba[1], rgba[2]);
  }
}

static void gl2psResetPostScriptColor(void)
{
  gl2ps->lastrgba[0] = gl2ps->lastrgba[1] = gl2ps->lastrgba[2] = -1.;
}

static void gl2psDashToArray( unsigned short dash, char* buffer )
{
    if( dash==0xFFFF )
        strcpy( buffer, "[] 0" );
    else
    {
        char num[30];
        unsigned short mask = 0x8000;
        int i, pat = ( dash & mask ), count=0;
        if( pat!=0 )
            pat = 1;

        strcpy( buffer, "[ " );
        for( i=0; i<8*sizeof( dash ); i++, mask = mask >> 1 )
            if( ( mask & dash )==( pat*mask ) )
                count++;
            else
            {
                sprintf( num, "%i ", count );
                strcat( buffer, num );
                count = 1;
                pat = 1-pat;
            }
        if( count>0 )
        {
            sprintf( num, "%i ", count );
            strcat( buffer, num );
        }
        strcat( buffer, "] 0" );
    }
}

static void gl2psPrintPostScriptPrimitive(void *data)
{
  GL2PSprimitive *prim;

  prim = *(GL2PSprimitive**)data;

  if((gl2ps->options & GL2PS_OCCLUSION_CULL) && prim->culled) return;

  switch(prim->type){
  case GL2PS_PIXMAP :
    gl2psPrintPostScriptPixmap(prim->verts[0].xyz[0], prim->verts[0].xyz[1],
                               prim->data.image);
    break;
  case GL2PS_IMAGEMAP :
    if(prim->data.image->type != GL2PS_IMAGEMAP_WRITTEN){
      gl2psPrintPostScriptColor(prim->verts[0].rgba);
      gl2psPrintPostScriptImagemap(prim->data.image->pixels[0],
                                   prim->data.image->pixels[1],
                                   prim->data.image->width, prim->data.image->height,
                                   prim->data.image->pixels + 2 );
      prim->data.image->type = GL2PS_IMAGEMAP_WRITTEN;
    }
    break;
  case GL2PS_TEXT :
    gl2psPrintPostScriptColor(prim->verts[0].rgba);
    gl2psPrintf("(%s) %g %g %d /%s ",
                prim->data.text->str, prim->verts[0].xyz[0], prim->verts[0].xyz[1],
                prim->data.text->fontsize, prim->data.text->fontname);

    switch(prim->data.text->alignment){
    case GL2PS_TEXT_C:
      gl2psPrintf("SCC\n");
      break;
    case GL2PS_TEXT_CL:
      gl2psPrintf("SCL\n");
      break;
    case GL2PS_TEXT_CR:
      gl2psPrintf("SCR\n");
      break;
    case GL2PS_TEXT_B:
      gl2psPrintf("SBC\n");
      break;
    case GL2PS_TEXT_BR:
      gl2psPrintf("SBR\n");
      break;
    case GL2PS_TEXT_T:
      gl2psPrintf("STC\n");
      break;
    case GL2PS_TEXT_TL:
      gl2psPrintf("STL\n");
      break;
    case GL2PS_TEXT_TR:
      gl2psPrintf("STR\n");
      break;
    case GL2PS_TEXT_BL:
    default:
      gl2psPrintf("S\n");
      break;
    }
    break;
  case GL2PS_POINT :
    gl2psPrintPostScriptColor(prim->verts[0].rgba);
    gl2psPrintf("%g %g %g P\n", 
                prim->verts[0].xyz[0], prim->verts[0].xyz[1], 0.5*prim->width);
    break;
  case GL2PS_LINE :
    if(gl2ps->lastlinewidth != prim->width){
      gl2ps->lastlinewidth = prim->width;
      gl2psPrintf("%g W\n", gl2ps->lastlinewidth);
    }
    if(prim->dash){
      char buffer[1024];
      gl2psDashToArray( prim->dash, buffer );
      strcat( buffer, " setdash\n" );
      gl2psPrintf( buffer );
    }
    if(!gl2psVertsSameColor(prim)){
      gl2psResetPostScriptColor();
      gl2psPrintf("%g %g %g %g %g %g %g %g %g %g SL\n",
                  prim->verts[1].xyz[0], prim->verts[1].xyz[1],
                  prim->verts[1].rgba[0], prim->verts[1].rgba[1],
                  prim->verts[1].rgba[2], prim->verts[0].xyz[0],
                  prim->verts[0].xyz[1], prim->verts[0].rgba[0],
                  prim->verts[0].rgba[1], prim->verts[0].rgba[2]);
    }
    else{
      gl2psPrintPostScriptColor(prim->verts[0].rgba);
      gl2psPrintf("%g %g %g %g L\n",
                  prim->verts[1].xyz[0], prim->verts[1].xyz[1],
                  prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
    }
    if(prim->dash){
      gl2psPrintf("[] 0 setdash\n");
    }
    break;
  case GL2PS_TRIANGLE :
    if(!gl2psVertsSameColor(prim)){
      gl2psResetPostScriptColor();
      gl2psPrintf("%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g ST\n",
                  prim->verts[2].xyz[0], prim->verts[2].xyz[1],
                  prim->verts[2].rgba[0], prim->verts[2].rgba[1],
                  prim->verts[2].rgba[2], prim->verts[1].xyz[0],
                  prim->verts[1].xyz[1], prim->verts[1].rgba[0],
                  prim->verts[1].rgba[1], prim->verts[1].rgba[2],
                  prim->verts[0].xyz[0], prim->verts[0].xyz[1],
                  prim->verts[0].rgba[0], prim->verts[0].rgba[1],
                  prim->verts[0].rgba[2]);
    }
    else{
      gl2psPrintPostScriptColor(prim->verts[0].rgba);
      gl2psPrintf("%g %g %g %g %g %g T\n",
                  prim->verts[2].xyz[0], prim->verts[2].xyz[1],
                  prim->verts[1].xyz[0], prim->verts[1].xyz[1],
                  prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
    }
    break;
  case GL2PS_QUADRANGLE :
    gl2psMsg(GL2PS_WARNING, "There should not be any quad left to print");
    break;
  default :
    gl2psMsg(GL2PS_ERROR, "Unknown type of primitive to print");
    break;
  }
}

static void gl2psPrintPostScriptFooter(void)
{
#ifdef GL2PS_HAVE_ZLIB
  int n;
  uLong crc, len;
  char tmp[8];
#endif
  
  gl2psPrintf("grestore\n"
              "showpage\n"
              "cleartomark\n"
              "%%%%PageTrailer\n"
              "%%%%Trailer\n"
              "end\n"
              "%%%%EOF\n");
  
#ifdef GL2PS_HAVE_ZLIB
  if(gl2ps->options & GL2PS_COMPRESS){
    if(Z_OK != gl2psDeflate()){
      gl2psMsg(GL2PS_ERROR, "Zlib deflate error");
    }
    else{
      /* determine the length of the header in the zlib stream */
      n = 2; /* CMF+FLG */
      if(gl2ps->compress->dest[1] & (1<<5)){
        n += 4; /* DICTID */
      }
      /* write the data, without the zlib header and footer */
      fwrite(gl2ps->compress->dest+n, gl2ps->compress->destLen-(n+4), 
             1, gl2ps->stream);
      /* add the gzip file footer */
      crc = crc32(0L, gl2ps->compress->start, gl2ps->compress->srcLen);
      for(n = 0; n < 4; ++n){
        tmp[n] = (char)(crc & 0xff);
        crc >>= 8;
      }
      len = gl2ps->compress->srcLen;
      for(n = 4; n < 8; ++n){
        tmp[n] = (char)(len & 0xff);
        len >>= 8;
      }
      fwrite(tmp, 8, 1, gl2ps->stream);
    }
    gl2psFreeCompress();
    gl2psFree(gl2ps->compress);
    gl2ps->compress = NULL;
  }
#endif 
}

static void gl2psPrintPostScriptBeginViewport(GLint viewport[4])
{
  GLint index;
  GLfloat rgba[4];
  int x = viewport[0], y = viewport[1], w = viewport[2], h = viewport[3];

  glRenderMode(GL_FEEDBACK);

  gl2psPrintf("gsave\n"
              "1.0 1.0 scale\n");
          
  if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
    if(gl2ps->colormode == GL_RGBA || gl2ps->colorsize == 0){
      glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba);
    }
    else{
      glGetIntegerv(GL_INDEX_CLEAR_VALUE, &index);
      rgba[0] = gl2ps->colormap[index][0];
      rgba[1] = gl2ps->colormap[index][1];
      rgba[2] = gl2ps->colormap[index][2];
      rgba[3] = 0.0F;
    }
    gl2psPrintf("%g %g %g C\n"
                "newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto\n"
                "closepath fill\n",
                rgba[0], rgba[1], rgba[2], 
                x, y, x+w, y, x+w, y+h, x, y+h);
    gl2psPrintf("newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto\n"
                "closepath clip\n",
                x, y, x+w, y, x+w, y+h, x, y+h);
  }

}

static GLint gl2psPrintPostScriptEndViewport(void)
{
  GLint res;

  res = gl2psPrintPrimitives();
  gl2psPrintf("grestore\n");
  return res;
}

/********************************************************************* 
 *
 * LaTeX routines
 *
 *********************************************************************/

static void gl2psPrintTeXHeader(void)
{
  char name[256];
  int i;

  if(gl2ps->filename && strlen(gl2ps->filename) < 256){
    for(i = strlen(gl2ps->filename)-1; i >= 0; i--){
      if(gl2ps->filename[i] == '.'){
        strncpy(name, gl2ps->filename, i);
        name[i] = '\0';
        break;
      }
    }
    if(i <= 0) strcpy(name, gl2ps->filename);
  }
  else{
    strcpy(name, "untitled");
  }

  fprintf(gl2ps->stream, 
          "\\setlength{\\unitlength}{1pt}\n"
          "\\begin{picture}(0,0)\n"
          "\\includegraphics{%s}\n"
          "\\end{picture}%%\n"
          "%s\\begin{picture}(%d,%d)(0,0)\n",
          name, (gl2ps->options & GL2PS_LANDSCAPE) ? "\\rotatebox{90}{" : "",
          (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
}

static void gl2psPrintTeXPrimitive(void *data)
{
  GL2PSprimitive *prim;

  prim = *(GL2PSprimitive**)data;

  switch(prim->type){
  case GL2PS_TEXT :
    fprintf(gl2ps->stream, "\\fontsize{%d}{0}\n\\selectfont", 
            prim->data.text->fontsize);
    fprintf(gl2ps->stream, "\\put(%g,%g){\\makebox(0,0)",
            prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
    switch(prim->data.text->alignment){
    case GL2PS_TEXT_CL:
      fprintf(gl2ps->stream, "[l]{");
      break;
    case GL2PS_TEXT_CR:
      fprintf(gl2ps->stream, "[r]{");
      break;
    case GL2PS_TEXT_B:
      fprintf(gl2ps->stream, "[b]{");
      break;
    case GL2PS_TEXT_BL:
      fprintf(gl2ps->stream, "[bl]{");
      break;
    case GL2PS_TEXT_BR:
      fprintf(gl2ps->stream, "[br]{");
      break;
    case GL2PS_TEXT_T:
      fprintf(gl2ps->stream, "[t]{");
      break;
    case GL2PS_TEXT_TL:
      fprintf(gl2ps->stream, "[tl]{");
      break;
    case GL2PS_TEXT_TR:
      fprintf(gl2ps->stream, "[tr]{");
      break;
    default:
      break;
    }
    if(prim->data.text->angle)
      fprintf(gl2ps->stream, "\\rotatebox{%g}{", prim->data.text->angle);
    fprintf(gl2ps->stream, "\\textcolor[rgb]{%g,%g,%g}{{%s}}",
            prim->verts[0].rgba[0], prim->verts[0].rgba[1], prim->verts[0].rgba[2],
            prim->data.text->str);
    if(prim->data.text->angle)
      fprintf(gl2ps->stream, "}");
    fprintf(gl2ps->stream, "}}\n");
    break;
  default :
    break;
  }
}

static void gl2psPrintTeXFooter(void)
{
  fprintf(gl2ps->stream, "\\end{picture}%s\n",
          (gl2ps->options & GL2PS_LANDSCAPE) ? "}" : "");
}

/********************************************************************* 
 *
 * PDF routines
 *
 *********************************************************************/

static int gl2psPrintPDFCompressorType(void)
{
#ifdef GL2PS_HAVE_ZLIB
  if(gl2ps->options & GL2PS_COMPRESS){
    return fprintf(gl2ps->stream, "/Filter [/FlateDecode]\n");
  }
#endif
  return 0;
}

static int gl2psPrintPDFStrokeColor(GL2PSrgba rgba)
{
  int i, offs = 0;

  gl2psSetLastColor(rgba);
  for(i = 0; i < 3; ++i){
    if(GL2PS_ZERO(rgba[i]))
      offs += gl2psPrintf("%.0f ", 0.);
    else if(rgba[i] < 1e-4 || rgba[i] > 1e6) /* avoid %e formatting */
      offs += gl2psPrintf("%f ", rgba[i]);
    else
      offs += gl2psPrintf("%g ", rgba[i]);
  }
  offs += gl2psPrintf("RG\n");
  return offs;
}

static int gl2psPrintPDFFillColor(GL2PSrgba rgba)
{
  int i, offs = 0;
  
  for(i = 0; i < 3; ++i){
    if(GL2PS_ZERO(rgba[i]))
      offs += gl2psPrintf("%.0f ", 0.);
    else if(rgba[i] < 1e-4 || rgba[i] > 1e6) /* avoid %e formatting */
      offs += gl2psPrintf("%f ", rgba[i]);
    else
      offs += gl2psPrintf("%g ", rgba[i]);
  }
  offs += gl2psPrintf("rg\n");
  return offs;
}

static int gl2psPrintPDFLineWidth(GLfloat lw)
{
  if(GL2PS_ZERO(lw))
    return gl2psPrintf("%.0f w\n", 0.);
  else if(lw < 1e-4 || lw > 1e6) /* avoid %e formatting */
    return gl2psPrintf("%f w\n", lw);
  else
    return gl2psPrintf("%g w\n", lw);
}

static void gl2psPutPDFText(GL2PSstring *text, int cnt, GLfloat x, GLfloat y)
{
  gl2ps->streamlength += 
    gl2psPrintf("BT\n"
                "/F%d %d Tf\n"
                "%f %f Td\n"
                "(%s) Tj\n"
                "ET\n", 
                cnt, text->fontsize, x, y, text->str);  
}

static void gl2psPutPDFImage(GL2PSimage *image, int cnt, GLfloat x, GLfloat y)
{
  gl2ps->streamlength += 
    gl2psPrintf("q\n"
                "%d 0 0 %d %f %f cm\n"
                "/Im%d Do\n"
                "Q\n",
                (int)image->width, (int)image->height, x, y, cnt);
}

static void gl2psPDFstacksInit(void)
{
  gl2ps->objects_stack = GL2PS_FIXED_XREF_ENTRIES + 1; 
  gl2ps->extgs_stack = 0;   
  gl2ps->font_stack = 0;    
  gl2ps->im_stack = 0;      
  gl2ps->trgroupobjects_stack = 0;    
  gl2ps->shader_stack = 0;  
  gl2ps->mshader_stack = 0; 
}

static void gl2psPDFgroupObjectInit(GL2PSpdfgroup *gro)
{
  if(!gro)
    return;
  
  gro->ptrlist = NULL;
  gro->fontno = gro->gsno = gro->imno = gro->maskshno = gro->shno 
    = gro->trgroupno = gro->fontobjno = gro->imobjno = gro->shobjno 
    = gro->maskshobjno = gro->gsobjno = gro->trgroupobjno = -1;
}

/* Build up group objects and assign name and object numbers */

static void gl2psPDFgroupListInit(void)
{
  int i;
  GL2PSprimitive *p = NULL;
  GL2PSpdfgroup gro;
  int lasttype = GL2PS_NOTYPE;
  GL2PSrgba lastrgba;
  unsigned short lastdash = 0;
  GLfloat lastwidth = 1;
  GL2PStriangle tmpt, lastt;
  int lastTriangleWasNotSimpleWithSameColor = 0;

  if(!gl2ps->pdfprimlist)
    return;

  gl2ps->pdfgrouplist = gl2psListCreate(500, 500, sizeof(GL2PSpdfgroup));

  for(i = 0; i < gl2psListNbr(gl2ps->pdfprimlist); ++i){  
    p = *(GL2PSprimitive**)gl2psListPointer(gl2ps->pdfprimlist, i);
    switch(p->type){
    case GL2PS_PIXMAP:
      gl2psPDFgroupObjectInit(&gro);
      gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
      gro.imno = gl2ps->im_stack++;
      gl2psListAdd(gro.ptrlist, &p);
      gl2psListAdd(gl2ps->pdfgrouplist, &gro);
      break;
    case GL2PS_TEXT:
      gl2psPDFgroupObjectInit(&gro);
      gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
      gro.fontno = gl2ps->font_stack++;
      gl2psListAdd(gro.ptrlist, &p);
      gl2psListAdd(gl2ps->pdfgrouplist, &gro);
      break;
    case GL2PS_LINE:
      if(lasttype != p->type || lastwidth != p->width || lastdash != p->dash ||
         !gl2psSameColor(p->verts[0].rgba, lastrgba)){
        gl2psPDFgroupObjectInit(&gro);
        gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
        gl2psListAdd(gro.ptrlist, &p);
        gl2psListAdd(gl2ps->pdfgrouplist, &gro);
      }
      else{
        gl2psListAdd(gro.ptrlist, &p);
      }
      lastdash = p->dash;
      lastwidth = p->width;
      lastrgba[0] = p->verts[0].rgba[0];
      lastrgba[1] = p->verts[0].rgba[1];
      lastrgba[2] = p->verts[0].rgba[2];
      break;
    case GL2PS_POINT:
      if(lasttype != p->type || lastwidth != p->width || 
         !gl2psSameColor(p->verts[0].rgba, lastrgba)){
        gl2psPDFgroupObjectInit(&gro);
        gro.ptrlist = gl2psListCreate(1,2,sizeof(GL2PSprimitive*));
        gl2psListAdd(gro.ptrlist, &p);
        gl2psListAdd(gl2ps->pdfgrouplist, &gro);
      }
      else{
        gl2psListAdd(gro.ptrlist, &p);
      }
      lastwidth = p->width;
      lastrgba[0] = p->verts[0].rgba[0];
      lastrgba[1] = p->verts[0].rgba[1];
      lastrgba[2] = p->verts[0].rgba[2];
      break;
    case GL2PS_TRIANGLE:
      gl2psFillTriangleFromPrimitive(&tmpt, p, GL_TRUE);
      lastTriangleWasNotSimpleWithSameColor = 
        !(tmpt.prop & T_CONST_COLOR && tmpt.prop & T_ALPHA_1) ||
        !gl2psSameColor(tmpt.vertex[0].rgba, lastt.vertex[0].rgba);
      if(lasttype == p->type && tmpt.prop == lastt.prop && 
         lastTriangleWasNotSimpleWithSameColor){
        /* TODO Check here for last alpha */
        gl2psListAdd(gro.ptrlist, &p);
      }
      else{
        gl2psPDFgroupObjectInit(&gro);
        gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
        gl2psListAdd(gro.ptrlist, &p);
        gl2psListAdd(gl2ps->pdfgrouplist, &gro);
      }
      lastt.prop = tmpt.prop;
      break;
    default:
      break;
    } 
    lasttype = p->type;
  }
}

static void gl2psSortOutTrianglePDFgroup(GL2PSpdfgroup *gro)
{
  GL2PStriangle t;
  GL2PSprimitive *prim = NULL;
  
  if(!gro)
    return;

  if(!gl2psListNbr(gro->ptrlist))
    return;

  prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);

  if(prim->type != GL2PS_TRIANGLE)
    return;

  gl2psFillTriangleFromPrimitive(&t, prim, GL_TRUE);
  
  if(t.prop & T_CONST_COLOR && t.prop & T_ALPHA_LESS_1){        
    gro->gsno = gl2ps->extgs_stack++; 
    gro->gsobjno = gl2ps->objects_stack ++;
  }
  else if(t.prop & T_CONST_COLOR && t.prop & T_VAR_ALPHA){              
    gro->gsno = gl2ps->extgs_stack++;
    gro->gsobjno = gl2ps->objects_stack++;
    gro->trgroupno = gl2ps->trgroupobjects_stack++; 
    gro->trgroupobjno = gl2ps->objects_stack++;
    gro->maskshno = gl2ps->mshader_stack++;
    gro->maskshobjno = gl2ps->objects_stack++;
  }
  else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_1){          
    gro->shno = gl2ps->shader_stack++;
    gro->shobjno = gl2ps->objects_stack++;
  }
  else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_LESS_1){             
    gro->gsno = gl2ps->extgs_stack++;
    gro->gsobjno = gl2ps->objects_stack++;
    gro->shno = gl2ps->shader_stack++; 
    gro->shobjno = gl2ps->objects_stack++;
  }
  else if(t.prop & T_VAR_COLOR && t.prop & T_VAR_ALPHA){                
    gro->gsno = gl2ps->extgs_stack++;
    gro->gsobjno = gl2ps->objects_stack++;
    gro->shno = gl2ps->shader_stack++; 
    gro->shobjno = gl2ps->objects_stack++;
    gro->trgroupno = gl2ps->trgroupobjects_stack++; 
    gro->trgroupobjno = gl2ps->objects_stack++;
    gro->maskshno = gl2ps->mshader_stack++;
    gro->maskshobjno = gl2ps->objects_stack++;
  }
}

/* Main stream data */

static void gl2psPDFgroupListWriteMainStream(void)
{
  int i, j, lastel;
  GL2PSprimitive *prim = NULL;
  GL2PSpdfgroup *gro;
  GL2PStriangle t;

  if(!gl2ps->pdfgrouplist)
    return;

  for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
    gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);

    lastel = gl2psListNbr(gro->ptrlist) - 1;
    if(lastel < 0)
      continue;

    prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);

    switch(prim->type){
    case GL2PS_PIXMAP:
      for(j = 0; j <= lastel; ++j){
        prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
        gl2psPutPDFImage(prim->data.image, gro->imno, prim->verts[0].xyz[0], 
                         prim->verts[0].xyz[1]);
      }
      break;
    case GL2PS_TEXT:
      for(j = 0; j <= lastel; ++j){  
        prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
        gl2ps->streamlength += gl2psPrintPDFFillColor(prim->verts[0].rgba);
        gl2psPutPDFText(prim->data.text, gro->fontno, prim->verts[0].xyz[0],
                        prim->verts[0].xyz[1]);
      }
      break;
    case GL2PS_LINE:
      gl2ps->streamlength += gl2psPrintPDFLineWidth(prim->width);
      gl2ps->streamlength += gl2psPrintPDFStrokeColor(prim->verts[0].rgba);
      if(prim->dash){
        char buffer[1024];
        gl2psDashToArray( prim->dash, buffer );
        strcat( buffer, " d\n" );
        gl2ps->streamlength += gl2psPrintf( buffer );
      }
      else{
        gl2ps->streamlength += gl2psPrintf("[] 0 d\n"); 
      }
      for(j = 0; j <= lastel; ++j){  
        prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
        gl2ps->streamlength += 
          gl2psPrintf("%f %f m %f %f l\n",
                      prim->verts[0].xyz[0], prim->verts[0].xyz[1],
                      prim->verts[1].xyz[0], prim->verts[1].xyz[1]);
      }
      gl2ps->streamlength += gl2psPrintf("S\n");
      break;
    case GL2PS_POINT:
      gl2ps->streamlength += gl2psPrintf("1 J\n");
      gl2ps->streamlength += gl2psPrintPDFLineWidth(prim->width);
      gl2ps->streamlength += gl2psPrintPDFStrokeColor(prim->verts[0].rgba);
      for(j = 0; j <= lastel; ++j){  
        prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
        gl2ps->streamlength +=
          gl2psPrintf("%f %f m %f %f l\n",
                      prim->verts[0].xyz[0], prim->verts[0].xyz[1],
                      prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
      }
      gl2ps->streamlength += gl2psPrintf("S\n"); 
      gl2ps->streamlength += gl2psPrintf("0 J\n");
      break;
    case GL2PS_TRIANGLE:
      gl2psFillTriangleFromPrimitive(&t, prim, GL_TRUE);
      gl2psSortOutTrianglePDFgroup(gro);
      
      /* No alpha and const color: Simple PDF draw orders  */
      if(t.prop & T_CONST_COLOR && t.prop & T_ALPHA_1){         
        gl2ps->streamlength += gl2psPrintPDFFillColor(t.vertex[0].rgba);        
        for(j = 0; j <= lastel; ++j){  
          prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
          gl2psFillTriangleFromPrimitive(&t, prim, GL_FALSE);
          gl2ps->streamlength 
            += gl2psPrintf("%f %f m\n"
                           "%f %f l\n"
                           "%f %f l\n"
                           "h f\n",
                           t.vertex[0].xyz[0], t.vertex[0].xyz[1],
                           t.vertex[1].xyz[0], t.vertex[1].xyz[1],
                           t.vertex[2].xyz[0], t.vertex[2].xyz[1]);
        }
      }
      /* Const alpha < 1 and const color: Simple PDF draw orders 
         and an extra extended Graphics State for the alpha const */
      else if(t.prop & T_CONST_COLOR && t.prop & T_ALPHA_LESS_1){               
        gl2ps->streamlength += gl2psPrintf("q\n"
                                           "/GS%d gs\n",
                                           gro->gsno);
        gl2ps->streamlength += gl2psPrintPDFFillColor(prim->verts[0].rgba);
        for(j = 0; j <= lastel; ++j){  
          prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
          gl2psFillTriangleFromPrimitive(&t, prim, GL_FALSE);
          gl2ps->streamlength 
            += gl2psPrintf("%f %f m\n"
                           "%f %f l\n"
                           "%f %f l\n"
                           "h f\n",
                           t.vertex[0].xyz[0], t.vertex[0].xyz[1],
                           t.vertex[1].xyz[0], t.vertex[1].xyz[1],
                           t.vertex[2].xyz[0], t.vertex[2].xyz[1]);
        }
        gl2ps->streamlength += gl2psPrintf("Q\n");
      }
      /* Variable alpha and const color: Simple PDF draw orders 
         and an extra extended Graphics State + Xobject + Shader 
         object for the alpha mask */
      else if(t.prop & T_CONST_COLOR && t.prop & T_VAR_ALPHA){          
        gl2ps->streamlength += gl2psPrintf("q\n"
                                           "/GS%d gs\n"
                                           "/TrG%d Do\n",
                                           gro->gsno, gro->trgroupno);
        gl2ps->streamlength += gl2psPrintPDFFillColor(prim->verts[0].rgba);
        for(j = 0; j <= lastel; ++j){  
          prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
          gl2psFillTriangleFromPrimitive(&t, prim, GL_FALSE);
          gl2ps->streamlength 
            += gl2psPrintf("%f %f m\n"
                           "%f %f l\n"
                           "%f %f l\n"
                           "h f\n",
                           t.vertex[0].xyz[0], t.vertex[0].xyz[1],
                           t.vertex[1].xyz[0], t.vertex[1].xyz[1],
                           t.vertex[2].xyz[0], t.vertex[2].xyz[1]);
        }
        gl2ps->streamlength += gl2psPrintf("Q\n");
      }
      /* Variable color and no alpha: Shader Object for the colored
         triangle(s) */
      else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_1){              
        gl2ps->streamlength += gl2psPrintf("/Sh%d sh\n", gro->shno);
      }
      /* Variable color and const alpha < 1: Shader Object for the 
         colored triangle(s) and an extra extended Graphics State 
         for the alpha const */
      else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_LESS_1){         
        gl2ps->streamlength += gl2psPrintf("q\n"
                                           "/GS%d gs\n"
                                           "/Sh%d sh\n"
                                           "Q\n",
                                           gro->gsno, gro->shno);
      }
      /* Variable alpha and color: Shader Object for the colored 
         triangle(s) and an extra extended Graphics State 
         + Xobject + Shader object for the alpha mask */
      else if(t.prop & T_VAR_COLOR && t.prop & T_VAR_ALPHA){            
        gl2ps->streamlength += gl2psPrintf("q\n"
                                           "/GS%d gs\n"
                                           "/TrG%d Do\n"
                                           "/Sh%d sh\n"
                                           "Q\n",
                                           gro->gsno, gro->trgroupno, gro->shno);
      }
      break;
    default:
      break;
    } 
  }
}

/* Graphics State names */

static int gl2psPDFgroupListWriteGStateResources(void)
{
  GL2PSpdfgroup *gro;
  int offs = 0;
  int i;

  offs += fprintf(gl2ps->stream,
                  "/ExtGState\n" 
                  "<<\n"
                  "/GSa 7 0 R\n");
  for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){  
    gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i); 
    if(gro->gsno >= 0)
      offs += fprintf(gl2ps->stream, "/GS%d %d 0 R\n", gro->gsno, gro->gsobjno);
  }
  offs += fprintf(gl2ps->stream, ">>\n"); 
  return offs;
}

/* Main Shader names */

static int gl2psPDFgroupListWriteShaderResources(void)
{
  GL2PSpdfgroup *gro;
  int offs = 0;
  int i;

  offs += fprintf(gl2ps->stream,
                  "/Shading\n"
                  "<<\n");
  for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){  
    gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i); 
    if(gro->shno >= 0)
      offs += fprintf(gl2ps->stream, "/Sh%d %d 0 R\n", gro->shno, gro->shobjno);
    if(gro->maskshno >= 0)
      offs += fprintf(gl2ps->stream, "/TrSh%d %d 0 R\n", gro->maskshno, gro->maskshobjno);
  }
  offs += fprintf(gl2ps->stream,">>\n");  
  return offs;
}

/* Images & Mask Shader XObject names */

static int gl2psPDFgroupListWriteXObjectResources(void)
{
  int i;
  GL2PSprimitive *p = NULL;
  GL2PSpdfgroup *gro;
  int offs = 0;

  offs += fprintf(gl2ps->stream,
                  "/XObject\n"
                  "<<\n");

  for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){  
    gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i); 
    if(!gl2psListNbr(gro->ptrlist))
      continue;
    p = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);
    switch(p->type){
    case GL2PS_PIXMAP:
      gro->imobjno = gl2ps->objects_stack++;
      if(GL_RGBA == p->data.image->format)  /* reserve one object for image mask */
        gl2ps->objects_stack++;
      offs += fprintf(gl2ps->stream, "/Im%d %d 0 R\n", gro->imno, gro->imobjno);
    case GL2PS_TRIANGLE:
      if(gro->trgroupno >=0)
        offs += fprintf(gl2ps->stream, "/TrG%d %d 0 R\n", gro->trgroupno, gro->trgroupobjno);
      break;
    default:
      break;
    }
  }
  offs += fprintf(gl2ps->stream,">>\n");
  return offs;
}

/* Font names */

static int gl2psPDFgroupListWriteFontResources(void)
{
  int i;
  GL2PSpdfgroup *gro;
  int offs = 0;

  offs += fprintf(gl2ps->stream, "/Font\n<<\n");

  for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){  
    gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i); 
    if(gro->fontno < 0)
      continue;
    gro->fontobjno = gl2ps->objects_stack++;
    offs += fprintf(gl2ps->stream, "/F%d %d 0 R\n", gro->fontno, gro->fontobjno);
  }
  offs += fprintf(gl2ps->stream, ">>\n");

  return offs;
}

static void gl2psPDFgroupListDelete(void)
{
  int i;
  GL2PSpdfgroup *gro = NULL;
  
  if(!gl2ps->pdfgrouplist)
    return;

  for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){ 
    gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist,i);
    gl2psListDelete(gro->ptrlist);
  }

  gl2psListDelete(gl2ps->pdfgrouplist);
  gl2ps->pdfgrouplist = NULL;
}

/* Print 1st PDF object - file info */

static int gl2psPrintPDFInfo(void)
{
  int offs;
  time_t now;
  struct tm *newtime;
  
  time(&now);
  newtime = gmtime(&now);
  
  offs = fprintf(gl2ps->stream,
                 "1 0 obj\n"
                 "<<\n"
                 "/Title (%s)\n"
                 "/Creator (%s)\n"
                 "/Producer (GL2PS %d.%d.%d, "
                 "(C) 1999-2004 Christophe Geuzaine <geuz@geuz.org>)\n",
                 gl2ps->title, gl2ps->producer,
                 GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION, GL2PS_PATCH_VERSION);
  
  if(!newtime){
    offs += fprintf(gl2ps->stream, 
                    ">>\n"
                    "endobj\n");
    return offs;
  }
  
  offs += fprintf(gl2ps->stream, 
                  "/CreationDate (D:%d%02d%02d%02d%02d%02d)\n"
                  ">>\n"
                  "endobj\n",
                  newtime->tm_year+1900, 
                  newtime->tm_mon+1, 
                  newtime->tm_mday,
                  newtime->tm_hour,
                  newtime->tm_min,
                  newtime->tm_sec);
  return offs;
}

/* Create catalog and page structure - 2nd and 3th PDF object */

static int gl2psPrintPDFCatalog(void)
{
  return fprintf(gl2ps->stream, 
                 "2 0 obj\n"
                 "<<\n"
                 "/Type /Catalog\n"
                 "/Pages 3 0 R\n"
                 ">>\n"
                 "endobj\n");
}

static int gl2psPrintPDFPages(void)
{
  return fprintf(gl2ps->stream, 
                 "3 0 obj\n"
                 "<<\n" 
                 "/Type /Pages\n"
                 "/Kids [6 0 R]\n"
                 "/Count 1\n"
                 ">>\n"
                 "endobj\n");
}

/* Open stream for data - graphical objects, fonts etc. PDF object 4 */

static int gl2psOpenPDFDataStream(void)
{
  int offs = 0;
  
  offs += fprintf(gl2ps->stream, 
                  "4 0 obj\n"
                  "<<\n" 
                  "/Length 5 0 R\n" );
  offs += gl2psPrintPDFCompressorType();
  offs += fprintf(gl2ps->stream, 
                  ">>\n"
                  "stream\n");
  return offs;
}

/* Stream setup - Graphics state, fill background if allowed */

static int gl2psOpenPDFDataStreamWritePreface(void)
{
  int offs;
  GLint index;
  GLfloat rgba[4];

  offs = gl2psPrintf("/GSa gs\n");
  
  if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
    if(gl2ps->colormode == GL_RGBA || gl2ps->colorsize == 0){
      glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba);
    }
    else{
      glGetIntegerv(GL_INDEX_CLEAR_VALUE, &index);
      rgba[0] = gl2ps->colormap[index][0];
      rgba[1] = gl2ps->colormap[index][1];
      rgba[2] = gl2ps->colormap[index][2];
      rgba[3] = 0.0F;
    }
    offs += gl2psPrintPDFFillColor(rgba);
    offs += gl2psPrintf("%d %d %d %d re\n",
                    (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
                    (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
    offs += gl2psPrintf("f\n");  
  }
  return offs;
}

/* Use the functions above to create the first part of the PDF*/

static void gl2psPrintPDFHeader(void)
{
  int offs = 0;
  gl2ps->pdfprimlist = gl2psListCreate(500, 500, sizeof(GL2PSprimitive*));
  gl2psPDFstacksInit();

  gl2ps->xreflist = (int*)gl2psMalloc(sizeof(int) * gl2ps->objects_stack); 

#ifdef GL2PS_HAVE_ZLIB
  if(gl2ps->options & GL2PS_COMPRESS){
    gl2psSetupCompress();
  }
#endif    
  gl2ps->xreflist[0] = 0;
  offs += fprintf(gl2ps->stream, "%%PDF-1.4\n");
  gl2ps->xreflist[1] = offs;
  
  offs += gl2psPrintPDFInfo();
  gl2ps->xreflist[2] = offs;
  
  offs += gl2psPrintPDFCatalog();
  gl2ps->xreflist[3] = offs;
  
  offs += gl2psPrintPDFPages();
  gl2ps->xreflist[4] = offs;
  
  offs += gl2psOpenPDFDataStream();
  gl2ps->xreflist[5] = offs; /* finished in gl2psPrintPDFFooter */
  gl2ps->streamlength = gl2psOpenPDFDataStreamWritePreface();
}

/* The central primitive drawing */

static void gl2psPrintPDFPrimitive(void *data)
{
  GL2PSprimitive *prim = *(GL2PSprimitive**)data;

  if((gl2ps->options & GL2PS_OCCLUSION_CULL) && prim->culled) 
    return;

  prim = gl2psCopyPrimitive(prim); /* deep copy */
  gl2psListAdd(gl2ps->pdfprimlist, &prim);
}

/* close stream and ... */

static int gl2psClosePDFDataStream(void)
{
  int offs = 0;
 
#ifdef GL2PS_HAVE_ZLIB
  if(gl2ps->options & GL2PS_COMPRESS){
    if(Z_OK != gl2psDeflate())
      gl2psMsg(GL2PS_ERROR, "Zlib deflate error");
    else
      fwrite(gl2ps->compress->dest, gl2ps->compress->destLen, 1, gl2ps->stream);
    gl2ps->streamlength += gl2ps->compress->destLen;
    
    offs += gl2ps->streamlength;
    gl2psFreeCompress();
  }
#endif 
  
  offs += fprintf(gl2ps->stream, 
                  "endstream\n"
                  "endobj\n");
  return offs;
}

/* ... write the now known length object */

static int gl2psPrintPDFDataStreamLength(int val)
{
  return fprintf(gl2ps->stream,
                 "5 0 obj\n"
                 "%d\n"
                 "endobj\n", val);
}

/* Put the info created before in PDF objects */

static int gl2psPrintPDFOpenPage(void)
{
  int offs;
  
  /* Write fixed part */
  
  offs = fprintf(gl2ps->stream, 
                 "6 0 obj\n"
                 "<<\n" 
                 "/Type /Page\n"
                 "/Parent 3 0 R\n"
                 "/MediaBox [%d %d %d %d]\n",
                 (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
                 (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
  
  if(gl2ps->options & GL2PS_LANDSCAPE)
    offs += fprintf(gl2ps->stream, "/Rotate -90\n");
  
  offs += fprintf(gl2ps->stream,
                  "/Contents 4 0 R\n"
                  "/Resources\n" 
                  "<<\n" 
                  "/ProcSet [/PDF /Text /ImageB /ImageC]  %%/ImageI\n");
  
  return offs;

  /* End fixed part, proceeds in gl2psPDFgroupListWriteVariableResources() */
}

static int gl2psPDFgroupListWriteVariableResources(void)
{
  int offs = 0;
  
  /* a) Graphics States for shader alpha masks*/
  offs += gl2psPDFgroupListWriteGStateResources();  
  
  /* b) Shader and shader masks */ 
  offs += gl2psPDFgroupListWriteShaderResources();  
 
  /* c) XObjects (Images & Shader Masks) */
  offs += gl2psPDFgroupListWriteXObjectResources();
  
  /* d) Fonts */
  offs += gl2psPDFgroupListWriteFontResources();
  
  /* End resources and page */
  offs += fprintf(gl2ps->stream,
                  ">>\n"
                  ">>\n"
                  "endobj\n");
  return offs;
}

/* Standard Graphics State */

static int gl2psPrintPDFGSObject(void)
{
  return fprintf(gl2ps->stream,
                 "7 0 obj\n"
                 "<<\n"
                 "/Type /ExtGState\n"
                 "/SA false\n"
                 "/SM 0.02\n"
                 "/OP false\n"
                 "/op false\n"
                 "/OPM 0\n"
                 "/BG2 /Default\n"
                 "/UCR2 /Default\n"
                 "/TR2 /Default\n"
                 ">>\n"
                 "endobj\n");
}

/* Put vertex' edge flag (8bit) and coordinates (32bit) in shader stream */

static int gl2psPrintPDFShaderStreamDataCoord(GL2PSvertex* vertex, 
                                              size_t (*action)(unsigned long data, 
                                                               size_t size), 
                                              GLfloat dx, GLfloat dy, 
                                              GLfloat xmin, GLfloat ymin)
{
  int offs = 0;
  unsigned long imap;
  GLfloat diff;
  double dmax = ~1UL;
  char edgeflag = 0;

  offs += (*action)(edgeflag, 1);

  /* The Shader stream in PDF requires to be in a 'big-endian'
     order */
    
  if(GL2PS_ZERO(dx*dy)){
    offs += (*action)(0, 4);
    offs += (*action)(0, 4);
  }
  else{
    diff = (vertex->xyz[0] - xmin) / dx;
    if(diff > 1)
      diff = 1.0F;
    else if(diff < 0)
      diff = 0.0F;
    imap = (unsigned long)(diff * dmax);
    offs += (*action)(imap, 4);
      
    diff = (vertex->xyz[1] - ymin) / dy;
    if(diff > 1)
      diff = 1.0F;
    else if(diff < 0)
      diff = 0.0F;
    imap = (unsigned long)(diff * dmax);
    offs += (*action)(imap, 4);
  }
  
  return offs;
}

/* Put vertex' rgb value (8bit for every component) in shader stream */

static int gl2psPrintPDFShaderStreamDataRGB(GL2PSvertex *vertex,
                                            size_t (*action)(unsigned long data, 
                                                             size_t size))
{
  int offs = 0;
  unsigned long imap;
  double dmax = ~1UL;

  imap = (unsigned long)((vertex->rgba[0]) * dmax);
  offs += (*action)(imap, 1);
    
  imap = (unsigned long)((vertex->rgba[1]) * dmax);
  offs += (*action)(imap, 1);
    
  imap = (unsigned long)((vertex->rgba[2]) * dmax);
  offs += (*action)(imap, 1);
  
  return offs;
}

/* Put vertex' alpha (8/16bit) in shader stream */

static int gl2psPrintPDFShaderStreamDataAlpha(GL2PSvertex* vertex, 
                                              size_t (*action)(unsigned long data, 
                                                               size_t size),
                                              int sigbyte)
{
  int offs = 0;
  unsigned long imap;
  double dmax = ~1UL;
        
  if(sigbyte!=8 && sigbyte!=16)
    sigbyte = 8;
        
  sigbyte /= 8;
  
  imap = (unsigned long)((vertex->rgba[3]) * dmax);
  
  offs += (*action)(imap, sigbyte);
  
  return offs;
}

/* Put a triangles raw data in shader stream */

static int gl2psPrintPDFShaderStreamData(GL2PStriangle* triangle, 
                                         GLfloat dx, GLfloat dy, 
                                         GLfloat xmin, GLfloat ymin,
                                         size_t (*action)(unsigned long data, 
                                                          size_t size),
                                         int gray)
{
  int i, offs = 0;
  GL2PSvertex v;
  
  if(gray && gray != 8 && gray != 16)
    gray = 8;
  
  for(i = 0; i < 3; ++i){
    offs += gl2psPrintPDFShaderStreamDataCoord(&triangle->vertex[i], action,
                                               dx, dy, xmin, ymin);
    if(gray){ 
      v = triangle->vertex[i];
      offs += gl2psPrintPDFShaderStreamDataAlpha(&v, action, gray); 
    }
    else{
      offs += gl2psPrintPDFShaderStreamDataRGB(&triangle->vertex[i], action);
    }
  }
  
  return offs;
}

static void gl2psPDFRectHull(GLfloat *xmin, GLfloat *xmax, 
                             GLfloat *ymin, GLfloat *ymax, 
                             GL2PStriangle *triangles, int cnt)
{
  int i, j;

  *xmin = triangles[0].vertex[0].xyz[0];
  *xmax = triangles[0].vertex[0].xyz[0];
  *ymin = triangles[0].vertex[0].xyz[1];
  *ymax = triangles[0].vertex[0].xyz[1];
  
  for(i = 0; i < cnt; ++i){
    for(j = 0; j < 3; ++j){
      if(*xmin > triangles[i].vertex[j].xyz[0])
        *xmin = triangles[i].vertex[j].xyz[0];
      if(*xmax < triangles[i].vertex[j].xyz[0])
        *xmax = triangles[i].vertex[j].xyz[0];
      if(*ymin > triangles[i].vertex[j].xyz[1])
        *ymin = triangles[i].vertex[j].xyz[1];
      if(*ymax < triangles[i].vertex[j].xyz[1])
        *ymax = triangles[i].vertex[j].xyz[1];
    }
  }
}

/* Writes shaded triangle 
   gray == 0 means write RGB triangles
   gray == 8             8bit-grayscale (for alpha masks)
   gray == 16            16bit-grayscale (for alpha masks) */

static int gl2psPrintPDFShader(int obj, GL2PStriangle *triangles, 
                               int size, int gray)
{
  int i, offs = 0, vertexbytes, done = 0;
  GLfloat xmin, xmax, ymin, ymax;
        
  switch(gray){
  case 0:
    vertexbytes = 1+4+4+1+1+1;
    break;
  case 8:
    vertexbytes = 1+4+4+1;
    break;
  case 16:
    vertexbytes = 1+4+4+2;
    break;
  default:
    gray = 8;
    vertexbytes = 1+4+4+1;
    break;
  }
  
  gl2psPDFRectHull(&xmin, &xmax, &ymin, &ymax, triangles, size);
  
  offs += fprintf(gl2ps->stream,
                  "%d 0 obj\n"
                  "<< "
                  "/ShadingType 4 "
                  "/ColorSpace %s "
                  "/BitsPerCoordinate 32 "
                  "/BitsPerComponent %d "
                  "/BitsPerFlag 8 "
                  "/Decode [%f %f %f %f 0 1 %s] ",
                  obj,
                  (gray) ? "/DeviceGray" : "/DeviceRGB", 
                  (gray) ? gray : 8,
                  xmin, xmax, ymin, ymax,
                  (gray) ? "" : "0 1 0 1");
  
#ifdef GL2PS_HAVE_ZLIB
  if(gl2ps->options & GL2PS_COMPRESS){
    gl2psAllocCompress(vertexbytes * size * 3);

    for(i = 0; i < size; ++i)
      gl2psPrintPDFShaderStreamData(&triangles[i],
                                    xmax-xmin, ymax-ymin, xmin, ymin, 
                                    gl2psWriteBigEndianCompress,gray);

    if(Z_OK == gl2psDeflate() && 23 + gl2ps->compress->destLen < gl2ps->compress->srcLen){
      offs += gl2psPrintPDFCompressorType();
      offs += fprintf(gl2ps->stream,
                      "/Length %d "
                      ">>\n"
                      "stream\n",
                      (int)gl2ps->compress->destLen);
      offs += gl2ps->compress->destLen * fwrite(gl2ps->compress->dest, 
                                                gl2ps->compress->destLen, 
                                                1, gl2ps->stream);
      done = 1;
    }
    gl2psFreeCompress();
  }
#endif

  if(!done){
    /* no compression, or too long after compression, or compress error
       -> write non-compressed entry */
    offs += fprintf(gl2ps->stream,
                    "/Length %d "
                    ">>\n"
                    "stream\n",
                    vertexbytes * 3 * size);
    for(i = 0; i < size; ++i)
      offs += gl2psPrintPDFShaderStreamData(&triangles[i],
                                            xmax-xmin, ymax-ymin, xmin, ymin,
                                            gl2psWriteBigEndian,gray);
  }
  
  offs += fprintf(gl2ps->stream,
                  "\nendstream\n"
                  "endobj\n");
  
  return offs;
}

/* Writes a XObject for a shaded triangle mask */

static int gl2psPrintPDFShaderMask(int obj, int childobj)
{
  int offs = 0, len;
  
  offs += fprintf(gl2ps->stream,
                  "%d 0 obj\n"
                  "<<\n"
                  "/Type /XObject\n"
                  "/Subtype /Form\n"
                  "/BBox [ %d %d %d %d ]\n"
                  "/Group \n<<\n/S /Transparency /CS /DeviceRGB\n"
                  ">>\n",
                  obj,
                  (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
                  (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
  
  len = (childobj>0) 
    ? strlen("/TrSh sh\n") + (int)log10(childobj)+1
    : strlen("/TrSh0 sh\n"); 
  
  offs += fprintf(gl2ps->stream,
                  "/Length %d\n"
                  ">>\n"
                  "stream\n",
                  len);
  offs += fprintf(gl2ps->stream,
                  "/TrSh%d sh\n",
                  childobj);
  offs += fprintf(gl2ps->stream,
                  "endstream\n"
                  "endobj\n");
  
  return offs;
}

/* Writes a Extended graphics state for a shaded triangle mask if
   simplealpha ist true the childobj argument is ignored and a /ca
   statement will be written instead */

static int gl2psPrintPDFShaderExtGS(int obj, int childobj)
{
  int offs = 0;
  
  offs += fprintf(gl2ps->stream,
                  "%d 0 obj\n"
                  "<<\n",
                  obj);
  
  offs += fprintf(gl2ps->stream,
                  "/SMask << /S /Alpha /G %d 0 R >> ",
                  childobj);
  
  offs += fprintf(gl2ps->stream,
                  ">>\n"
                  "endobj\n");
  return offs;
}

/* a simple graphics state */

static int gl2psPrintPDFShaderSimpleExtGS(int obj, GLfloat alpha)
{
  int offs = 0;
  
  offs += fprintf(gl2ps->stream,
                  "%d 0 obj\n"
                  "<<\n"
                  "/ca %g"
                  ">>\n"
                  "endobj\n",
                  obj, alpha);
  return offs;
}

/* Similar groups of  functions for pixmaps and text */

static int gl2psPrintPDFPixmapStreamData(GL2PSimage* im,
                                         size_t (*action)(unsigned long data, 
                                                          size_t size), 
                                         int gray)
{
  int x, y;
  GLfloat r, g, b, a;

  if(im->format != GL_RGBA && gray)
    return 0;

  if(gray && gray !=8 && gray != 16)
    gray = 8;

  gray /= 8;
  
  for(y = 0; y < im->height; ++y){
    for(x = 0; x < im->width; ++x){
      a = gl2psGetRGB(im, x, y, &r, &g, &b);
      if(im->format == GL_RGBA && gray){
        (*action)((unsigned long)(a*255) << 24, gray);
      }
      else{
        (*action)((unsigned long)(r*255) << 24, 1);
        (*action)((unsigned long)(g*255) << 24, 1);
        (*action)((unsigned long)(b*255) << 24, 1);
      }
    }
  }

  switch(gray){
  case 0: return 3 * im->width * im->height;
  case 1: return im->width * im->height;
  case 2: return 2 * im->width * im->height;
  default: return 3 * im->width * im->height;
  }
}

static int gl2psPrintPDFPixmap(int obj, int childobj, GL2PSimage *im, int gray)
{
  int offs = 0, done = 0, sigbytes = 3;

  if(gray && gray !=8 && gray != 16)
    gray = 8;
  
  if(gray)
    sigbytes = gray / 8; 
  
  offs += fprintf(gl2ps->stream,
                  "%d 0 obj\n"
                  "<<\n"
                  "/Type /XObject\n"
                  "/Subtype /Image\n"
                  "/Width %d\n"
                  "/Height %d\n"
                  "/ColorSpace %s \n"
                  "/BitsPerComponent 8\n",
                  obj,
                  (int)im->width, (int)im->height,
                  (gray) ? "/DeviceGray" : "/DeviceRGB" );
  if(GL_RGBA == im->format && gray == 0){
    offs += fprintf(gl2ps->stream,
                    "/SMask %d 0 R\n",
                    childobj);
  }
  
#ifdef GL2PS_HAVE_ZLIB
  if(gl2ps->options & GL2PS_COMPRESS){
    gl2psAllocCompress((int)(im->width * im->height * sigbytes));
    
    gl2psPrintPDFPixmapStreamData(im, gl2psWriteBigEndianCompress, gray);
    
    if(Z_OK == gl2psDeflate() && 23 + gl2ps->compress->destLen < gl2ps->compress->srcLen){
      offs += gl2psPrintPDFCompressorType();
      offs += fprintf(gl2ps->stream,
                      "/Length %d "
                      ">>\n"
                      "stream\n",
                      (int)gl2ps->compress->destLen);
      offs += gl2ps->compress->destLen * fwrite(gl2ps->compress->dest, gl2ps->compress->destLen,
                                                1, gl2ps->stream);
      done = 1;
    }
    gl2psFreeCompress();
  }
#endif
  
  if(!done){
    /* no compression, or too long after compression, or compress error
       -> write non-compressed entry */
    offs += fprintf(gl2ps->stream,
                    "/Length %d "
                    ">>\n"
                    "stream\n",
                    (int)(im->width * im->height * sigbytes));
    offs += gl2psPrintPDFPixmapStreamData(im, gl2psWriteBigEndian, gray);
  }
  
  offs += fprintf(gl2ps->stream,
                  "\nendstream\n"
                  "endobj\n");
  
  return offs;
}

static int gl2psPrintPDFText(int obj, GL2PSstring* s, int fontnumber)
{
  int offs = 0;
  
  offs += fprintf(gl2ps->stream,
                  "%d 0 obj\n"
                  "<<\n"
                  "/Type /Font\n"
                  "/Subtype /Type1\n"
                  "/Name /F%d\n"
                  "/BaseFont /%s\n"
                  "/Encoding /MacRomanEncoding\n"
                  ">>\n"
                  "endobj\n",
                  obj, fontnumber, s->fontname);
  return offs;
}

/* Write the physical objects */

static int gl2psPDFgroupListWriteObjects(int entryoffs)
{
  int i,j;
  GL2PSprimitive *p = NULL;
  GL2PSpdfgroup *gro;
  int offs = entryoffs;
  GL2PStriangle *triangles;
  int size = 0;

  if(!gl2ps->pdfgrouplist)
    return offs;
  
  for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){  
    gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i); 
    if(!gl2psListNbr(gro->ptrlist))
      continue;
    p = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);
    switch(p->type){
    case GL2PS_PIXMAP:
      gl2ps->xreflist[gro->imobjno] = offs;
      offs += gl2psPrintPDFPixmap(gro->imobjno, gro->imobjno+1, p->data.image, 0);
      if(p->data.image->format == GL_RGBA){
        gl2ps->xreflist[gro->imobjno+1] = offs;
        offs += gl2psPrintPDFPixmap(gro->imobjno+1, -1, p->data.image, 8);
      }
      break;
    case GL2PS_TEXT:
      gl2ps->xreflist[gro->fontobjno] = offs;
      offs += gl2psPrintPDFText(gro->fontobjno,p->data.text,gro->fontno);
      break;
    case GL2PS_TRIANGLE:
      size = gl2psListNbr(gro->ptrlist);
      triangles = (GL2PStriangle*)gl2psMalloc(sizeof(GL2PStriangle) * size);
      for(j = 0; j < size; ++j){  
        p = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
        gl2psFillTriangleFromPrimitive(&triangles[j], p, GL_TRUE);
      }
      if(triangles[0].prop & T_VAR_COLOR){
        gl2ps->xreflist[gro->shobjno] = offs;
        offs += gl2psPrintPDFShader(gro->shobjno, triangles, size, 0);
      }
      if(triangles[0].prop & T_ALPHA_LESS_1){
        gl2ps->xreflist[gro->gsobjno] = offs;
        offs += gl2psPrintPDFShaderSimpleExtGS(gro->gsobjno, triangles[0].vertex[0].rgba[3]);
      }
      if(triangles[0].prop & T_VAR_ALPHA){
        gl2ps->xreflist[gro->gsobjno] = offs;
        offs += gl2psPrintPDFShaderExtGS(gro->gsobjno, gro->trgroupobjno);
        gl2ps->xreflist[gro->trgroupobjno] = offs;
        offs += gl2psPrintPDFShaderMask(gro->trgroupobjno, gro->maskshno);
        gl2ps->xreflist[gro->maskshobjno] = offs;
        offs += gl2psPrintPDFShader(gro->maskshobjno, triangles, size, 8);
      }
      gl2psFree(triangles);
      break;
    case GL2PS_LINE:
      break;
    case GL2PS_POINT:
      break;
    default:
      break;
    } 
  }
  return offs;
}

/* All variable data has been written at this point and all required
   functioninality has been gathered, so we can write now file footer
   with cross reference table and trailer */

static void gl2psPrintPDFFooter(void)
{
  int i, offs;  

  gl2psPDFgroupListInit();
  gl2psPDFgroupListWriteMainStream();
 
  offs = gl2ps->xreflist[5] + gl2ps->streamlength; 
  offs += gl2psClosePDFDataStream();
  gl2ps->xreflist[5] = offs;
  
  offs += gl2psPrintPDFDataStreamLength(gl2ps->streamlength);
  gl2ps->xreflist[6] = offs;
  gl2ps->streamlength = 0;
  
  offs += gl2psPrintPDFOpenPage();
  offs += gl2psPDFgroupListWriteVariableResources();
  gl2ps->xreflist = (int*)gl2psRealloc(gl2ps->xreflist,
                                       sizeof(int) * (gl2ps->objects_stack + 1));
  gl2ps->xreflist[7] = offs;
  
  offs += gl2psPrintPDFGSObject();
  gl2ps->xreflist[8] = offs;
  
  gl2ps->xreflist[gl2ps->objects_stack] = 
    gl2psPDFgroupListWriteObjects(gl2ps->xreflist[8]);

  /* Start cross reference table. The file has to been opened in
     binary mode to preserve the 20 digit string length! */
  fprintf(gl2ps->stream,
          "xref\n"
          "0 %d\n"
          "%010d 65535 f \n", gl2ps->objects_stack, 0);
  
  for(i = 1; i < gl2ps->objects_stack; ++i)
    fprintf(gl2ps->stream, "%010d 00000 n \n", gl2ps->xreflist[i]);
  
  fprintf(gl2ps->stream,
          "trailer\n"
          "<<\n" 
          "/Size %d\n"
          "/Info 1 0 R\n"
          "/Root 2 0 R\n"
          ">>\n"
          "startxref\n%d\n"
          "%%%%EOF\n",
          gl2ps->objects_stack, gl2ps->xreflist[gl2ps->objects_stack]);
  
  /* Free auxiliary lists and arrays */    
  gl2psFree(gl2ps->xreflist);
  gl2psListDelete(gl2ps->pdfprimlist);
  gl2psPDFgroupListDelete();
  
#ifdef GL2PS_HAVE_ZLIB
  if(gl2ps->options & GL2PS_COMPRESS){
    gl2psFreeCompress();
    gl2psFree(gl2ps->compress);
    gl2ps->compress = NULL;
  }
#endif
}

/* PDF begin viewport */

static void gl2psPrintPDFBeginViewport(GLint viewport[4])
{
  int offs;
  GLint index;
  GLfloat rgba[4];
  int x = viewport[0], y = viewport[1], w = viewport[2], h = viewport[3];
  
  offs = 0;
  
  glRenderMode(GL_FEEDBACK);
  
  offs += gl2psPrintf("q\n");
  
  if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
    if(gl2ps->colormode == GL_RGBA || gl2ps->colorsize == 0){
      glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba);
    }
    else{
      glGetIntegerv(GL_INDEX_CLEAR_VALUE, &index);
      rgba[0] = gl2ps->colormap[index][0];
      rgba[1] = gl2ps->colormap[index][1];
      rgba[2] = gl2ps->colormap[index][2];
      rgba[3] = 0.;
    }
    offs += gl2psPrintPDFFillColor(rgba);
    offs += gl2psPrintf("%d %d %d %d re\n"
                        "W\n"
                        "f\n",
                        x, y, w, h);
  }
  else{
    offs += gl2psPrintf("%d %d %d %d re\n"
                        "W\n"   
                        "n\n",
                        x, y, w, h);            
  }
  
  gl2ps->streamlength += offs;
}

static GLint gl2psPrintPDFEndViewport(void)
{
  GLint res;
  
  res = gl2psPrintPrimitives();

  gl2ps->streamlength += gl2psPrintf("Q\n");
  
  return res;
}

/*********************************************************************/
const GLfloat EMF_UNIT = 40;

#ifdef WNT

COLORREF gl2psWinColor( GLfloat v[4] )
{
    return RGB( v[0]*255, v[1]*255, v[2]*255 );
}

#endif

static void gl2psPrintEMFImagemap( GLfloat x, GLfloat y,
                                   GLsizei width, GLsizei height,
                                   GL2PSrgba color,
                                   GLfloat *imagemap )
{
#ifdef WNT
    BITMAPINFO info;
    BITMAPINFOHEADER head;
    unsigned char* bits = gl2psMalloc( width * height * 4 );
    GLint i, j, ind, bit, bwid = ( width-1 )/8 + 1;

    head.biSize = sizeof( BITMAPINFOHEADER ); 
    head.biWidth = width; 
    head.biHeight = height; 
    head.biPlanes = 1; 
    head.biBitCount = 32; 
    head.biCompression = BI_RGB; 
    head.biSizeImage = 0; 
    head.biXPelsPerMeter = 0;
    head.biYPelsPerMeter = 0;
    head.biClrUsed = 0; 
    head.biClrImportant = 0; 

    info.bmiHeader = head;

    ind = 0;
    for( i=0; i<height; i++ )
        for( j=0; j<width; j++, ind+=4 )
        {
            bit = bwid * 8 * ( height-1-i ) + j;
            if( ( unsigned char )( imagemap[ bit/8 ] ) & ( 1 << ( 7-(bit%8) ) ) )
            {
                bits[ ind   ] = ( GLint )( color[2]*255 );  /*b*/
                bits[ ind+1 ] = ( GLint )( color[1]*255 );  /*g*/
                bits[ ind+2 ] = ( GLint )( color[0]*255 );  /*r*/
                bits[ ind+3 ] = ( GLint )( color[3]*255 );  /*alpha*/
            }
            else
            {
                bits[ ind   ] = 0;  /*b*/
                bits[ ind+1 ] = 0;  /*g*/
                bits[ ind+2 ] = 0;  /*r*/
                bits[ ind+3 ] = 0;  /*alpha*/
            }
        }

    SetDIBitsToDevice( gl2ps->metaDC, ( GLint )( x * EMF_UNIT ), ( GLint )( y * EMF_UNIT ), 
                       width, height, 0, 0, 0, height, bits, &info, DIB_RGB_COLORS );
#endif
}

static void gl2psPrintEMFPrimitive( void *data )
{
#ifdef WNT
    GL2PSprimitive *prim;

    /* msimg32.dll is not available on Windows NT, hence static linking cannot be used */
    /* WARNING: currently library is loaded once but never unloaded */
    static int first_time = 1;
    static HMODULE msimgLib = NULL;
    static GradientFillFunc gradFill = NULL;
    if ( first_time ) {
      first_time = 0;
      // the functionality commented out until problem of having it working in Release mode 
      // is fixed; see OCC10093 and OCC10099
//      msimgLib = LoadLibrary( "msimg32.dll" );
//      if ( msimgLib ) gradFill = (GradientFillFunc)GetProcAddress( msimgLib, "GradientFill" );
    }

    prim = *(GL2PSprimitive**)data;

    if( ( gl2ps->options & GL2PS_OCCLUSION_CULL ) && prim->culled )
        return;

    if( gl2ps->lastlinewidth != prim->width )
        gl2ps->lastlinewidth = prim->width;

    switch( prim->type )
    {
        case GL2PS_PIXMAP :
            break;
        case GL2PS_IMAGEMAP :
            if( prim->data.image->type != GL2PS_IMAGEMAP_WRITTEN )
                gl2psPrintEMFImagemap( prim->data.image->pixels[0],
                                       prim->data.image->pixels[1],
                                       prim->data.image->width,
                                       prim->data.image->height,
                                       prim->verts[0].rgba,
                                       prim->data.image->pixels + 2 );
            prim->data.image->type = GL2PS_IMAGEMAP_WRITTEN;
            break;
        case GL2PS_TEXT :
            {
                COLORREF color = gl2psWinColor( prim->verts[0].rgba );
                GLfloat x, y, size;
                HFONT font;
                HGDIOBJ old1, old2;
                LOGBRUSH data;
                HBRUSH brush;

                size = prim->data.text->fontsize;
                size = size * EMF_UNIT;
                
                font = CreateFont( ( GLint )size, 0, 0, 0, FW_NORMAL, 0, 0, 0, 
                            ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
                            PROOF_QUALITY, FF_DONTCARE | DEFAULT_PITCH, 
                            prim->data.text->fontname );

                data.lbStyle = BS_SOLID;
                data.lbColor = color;
                brush = CreateBrushIndirect( &data );

                x = prim->verts[0].xyz[0] * EMF_UNIT;
                y = prim->verts[0].xyz[1] * EMF_UNIT;

                switch( prim->data.text->alignment )
                {
                    case GL2PS_TEXT_C:
                        break;
                    case GL2PS_TEXT_CL:
                        break;
                    case GL2PS_TEXT_CR:
                        break;
                    case GL2PS_TEXT_B:
                        break;
                    case GL2PS_TEXT_BR:
                        break;
                    case GL2PS_TEXT_T:
                        break;
                    case GL2PS_TEXT_TL:
                        break;
                    case GL2PS_TEXT_TR:
                        break;
                    case GL2PS_TEXT_BL:
                        break;
                    default:
                        break;
                }

                old1 = SelectObject( gl2ps->metaDC, font );
                old2 = SelectObject( gl2ps->metaDC, brush );

                SetBkMode( gl2ps->metaDC, TRANSPARENT );
                SetTextAlign( gl2ps->metaDC, TA_BASELINE );

                BeginPath( gl2ps->metaDC );

                TextOut( gl2ps->metaDC, ( long )x, ( long )y,
                         prim->data.text->str,
                         strlen( prim->data.text->str ) );

                EndPath( gl2ps->metaDC );

                FillPath( gl2ps->metaDC );

                SelectObject( gl2ps->metaDC, old2 );
                SelectObject( gl2ps->metaDC, old1 );

                DeleteObject( font );
                DeleteObject( brush );
            }
            break;
        case GL2PS_POINT :
        case GL2PS_LINE :
            {
                COLORREF color = gl2psWinColor( prim->verts[0].rgba );
                GLfloat  wpen  = prim->width > 1 ? prim->width : 1.0f;
                HPEN     pen   = CreatePen( PS_SOLID, ( GLint )( wpen*EMF_UNIT ), color );
                GLint ind = 1;
                HGDIOBJ old;
                GLfloat x, y;

                old = SelectObject( gl2ps->metaDC, pen );

                x = prim->verts[0].xyz[0] * EMF_UNIT;
                y = prim->verts[0].xyz[1] * EMF_UNIT;

                MoveToEx( gl2ps->metaDC, ( long )x, ( long )y, NULL );

                if( prim->type == GL2PS_POINT )
                    ind = 0;

                x = prim->verts[ind].xyz[0] * EMF_UNIT;
                y = prim->verts[ind].xyz[1] * EMF_UNIT;
                LineTo( gl2ps->metaDC, ( long )x, ( long )y );

                SelectObject( gl2ps->metaDC, old );

                DeleteObject( pen );
            }
            break;
        case GL2PS_TRIANGLE :
            if( gl2psVertsSameColor( prim ) || gradFill == 0 )
            {
                POINT pp[3];
                GLint i;

                COLORREF color = gl2psWinColor( prim->verts[0].rgba );
                HBRUSH brush = CreateSolidBrush( color );
                HPEN   pen   = CreatePen( PS_SOLID, 
                                          gl2ps->lastlinewidth>1 ? 
                                            ( GLint )gl2ps->lastlinewidth : 1, 
                                          color );

                HGDIOBJ old1 = SelectObject( gl2ps->metaDC, brush ),
                        old2 = SelectObject( gl2ps->metaDC, pen );

                for( i=0; i<3; i++ )
                {
                    GLfloat xx, yy;
                    
                    xx = prim->verts[i].xyz[0] * EMF_UNIT;
                    yy = prim->verts[i].xyz[1] * EMF_UNIT;

                    pp[i].x = ( long )xx;
                    pp[i].y = ( long )yy;
                }

                Polygon( gl2ps->metaDC, pp, 3 );

                SelectObject( gl2ps->metaDC, old2 );
                SelectObject( gl2ps->metaDC, old1 );

                DeleteObject( brush );
            }
            else
            {
                 TRIVERTEX vv[3];
                 GRADIENT_TRIANGLE gr;
                 GLint i;

                 for( i=0; i<3; i++ )
                 {
                     GLfloat xx, yy;
                     GLfloat rgba[4];
                     GLint j;

                     xx = prim->verts[i].xyz[0] * EMF_UNIT;
                     yy = prim->verts[i].xyz[1] * EMF_UNIT;

                     for( j=0; j<4; j++ )
                        rgba[j] = prim->verts[i].rgba[j] * ( 0xff00 );

                     vv[i].x = ( long )xx;
                     vv[i].y = ( long )yy;
                     vv[i].Red   = ( unsigned short )rgba[0];
                     vv[i].Green = ( unsigned short )rgba[1];
                     vv[i].Blue  = ( unsigned short )rgba[2];
                     vv[i].Alpha = ( unsigned short )rgba[3];
                 }

                 gr.Vertex1 = 0;
                 gr.Vertex2 = 1;
                 gr.Vertex3 = 2;

                 gradFill( gl2ps->metaDC, vv, 3, &gr, 1, GRADIENT_FILL_TRIANGLE );
            }
            break;
        case GL2PS_QUADRANGLE :
            gl2psMsg( GL2PS_WARNING, "There should not be any quad left to print" );
        break;
        default :
            gl2psMsg( GL2PS_ERROR, "Unknown type of primitive to print" );
        break;
    }
#endif
}

static void gl2psPrintEMFHeader( void )
{
#ifdef WNT

    HDC screen_dc, bit_dc;
    RECT r;
    GLfloat x = ( GLfloat )gl2ps->viewport[ 0 ],
            y = ( GLfloat )gl2ps->viewport[ 1 ],
            w = ( GLfloat )gl2ps->viewport[ 2 ],
            h = ( GLfloat )gl2ps->viewport[ 3 ];
    GLfloat rgba[4];
    GLint index;
    GLfloat ll, rr, tt, bb;

    ll =   x     * EMF_UNIT;
    rr = ( x+w ) * EMF_UNIT;
    tt =   y     * EMF_UNIT;
    bb = ( y+h ) * EMF_UNIT;

    r.left   = ( long )ll;
    r.right  = ( long )rr;
    r.top    = ( long )tt;
    r.bottom = ( long )bb;

    screen_dc = GetDC( 0 );
    bit_dc = CreateCompatibleDC( screen_dc );

    gl2ps->metaDC = CreateEnhMetaFile( bit_dc, gl2ps->filename, &r, "" );

      SetMapMode( gl2ps->metaDC, MM_HIMETRIC );
      SetWindowOrgEx( gl2ps->metaDC, 0, r.bottom, NULL );

    if( gl2ps->options & GL2PS_DRAW_BACKGROUND )
    {
       if( gl2ps->colormode == GL_RGBA || gl2ps->colorsize == 0 )
          glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba);
       else
       {
          glGetIntegerv(GL_INDEX_CLEAR_VALUE, &index);
          rgba[0] = gl2ps->colormap[index][0];
          rgba[1] = gl2ps->colormap[index][1];
          rgba[2] = gl2ps->colormap[index][2];
          rgba[3] = 0.0F;
       }

         FillRect( gl2ps->metaDC, &r, CreateSolidBrush( RGB( rgba[0]*255,
                                                           rgba[1]*255,
                                                           rgba[2]*255 ) ) );
    }

    ReleaseDC( 0, screen_dc );
    DeleteDC( bit_dc );

#endif
}

static void gl2psPrintEMFFooter( void )
{
#ifdef WNT
    DeleteEnhMetaFile( CloseEnhMetaFile( gl2ps->metaDC ) );
#endif
}

static void gl2psPrintEMFBeginViewport( GLint viewport[4] )
{
}

static GLint gl2psPrintEMFEndViewport( void )
{
    return 0;
}

/********************************************************************* 
 *
 * General primitive printing routine
 *
 *********************************************************************/

static GLint gl2psPrintPrimitives(void)
{
  GL2PSbsptree *root;
  GL2PSxyz eye = {0.0F, 0.0F, 100000.0F};
  GLint used;
  void (*pprim)(void *data) = NULL;

  used = glRenderMode(GL_RENDER);

  if(used < 0){
    gl2psMsg(GL2PS_INFO, "OpenGL feedback buffer overflow");
    return GL2PS_OVERFLOW;
  }

  if(used > 0){
    if(gl2ps->format == GL2PS_PS || 
       gl2ps->format == GL2PS_EPS ||
       gl2ps->format == GL2PS_PDF ||
       gl2ps->format == GL2PS_EMF ){
      gl2psParseFeedbackBuffer(used);
    }
  }

  if(!gl2psListNbr(gl2ps->primitives)){
    /* Empty feedback buffer and/or nothing else to print */
    return GL2PS_NO_FEEDBACK;
  }

  switch(gl2ps->format){
  case GL2PS_TEX :
    pprim = gl2psPrintTeXPrimitive;
    break;
  case GL2PS_PS :
  case GL2PS_EPS :
    pprim = gl2psPrintPostScriptPrimitive;
    break;
  case GL2PS_EMF:
    pprim = gl2psPrintEMFPrimitive;
    break;
  case GL2PS_PDF :
    pprim = gl2psPrintPDFPrimitive;
    break;
  }
  
  switch(gl2ps->sort){
  case GL2PS_NO_SORT :
    gl2psListAction(gl2ps->primitives, pprim);
    gl2psListAction(gl2ps->primitives, gl2psFreePrimitive);
    /* reset the primitive list, waiting for the next viewport */
    gl2psListReset(gl2ps->primitives);
    break;
  case GL2PS_SIMPLE_SORT :
    gl2psListSort(gl2ps->primitives, gl2psCompareDepth);
    if(gl2ps->options & GL2PS_OCCLUSION_CULL){
      gl2psListAction(gl2ps->primitives, gl2psAddInImageTree);
      gl2psFreeBspImageTree(&gl2ps->imagetree);
    }
    gl2psListActionInverse(gl2ps->primitives, pprim);
    gl2psListAction(gl2ps->primitives, gl2psFreePrimitive);
    /* reset the primitive list, waiting for the next viewport */
    gl2psListReset(gl2ps->primitives);
    break;
  case GL2PS_BSP_SORT :
    root = (GL2PSbsptree*)gl2psMalloc(sizeof(GL2PSbsptree));

    buildBSPcount = 0;
    numberOfPrimitives = gl2psListNbr( gl2ps->primitives );
    numberOfProcessedPrimitives = 0;
    gl2psBuildBspTree( root, gl2ps->primitives, GL_TRUE );
#ifdef _DEBUG
    gl2psMsg(GL2PS_INFO, "Build BSP tree is called %i times ", buildBSPcount );
#endif

    if(GL_TRUE == gl2ps->boundary) gl2psBuildPolygonBoundary(root);
    if(gl2ps->options & GL2PS_OCCLUSION_CULL){
      gl2psTraverseBspTree(root, eye, -(float)gl2ps->EPSILON, gl2psLess,
                           gl2psAddInImageTree, 1);
      gl2psFreeBspImageTree(&gl2ps->imagetree);
    }
    gl2psTraverseBspTree(root, eye, (float)gl2ps->EPSILON, gl2psGreater, 
                         pprim, 0);
    gl2psFreeBspTree(&root);
    /* reallocate the primitive list (it's been deleted by
       gl2psBuildBspTree) in case there is another viewport */
    gl2ps->primitives = gl2psListCreate(500, 500, sizeof(GL2PSprimitive*));
    break;
  default :
    gl2psMsg(GL2PS_ERROR, "Unknown sorting algorithm: %d", gl2ps->sort);
    return GL2PS_ERROR;
  }

  if( gl2ps->stream )
    fflush( gl2ps->stream );

  return GL2PS_SUCCESS;
}

/********************************************************************* 
 *
 * Public routines
 *
 *********************************************************************/

GL2PSDLL_API GLint gl2psBeginPage(const char *title, const char *producer, 
                                  GLint viewport[4], GLint format, GLint sort,
                                  GLint options, GLint colormode,
                                  GLint colorsize, GL2PSrgba *colormap,
                                  GLint nr, GLint ng, GLint nb, GLint buffersize,
                                  const char *filename,
                                  double precision, PB_FUNC PB_Function, void* PB_Object )
{
  int i;
  char fname[1024];

  if(gl2ps){
    gl2psMsg(GL2PS_ERROR, "gl2psBeginPage called in wrong program state");
    return GL2PS_ERROR;
  }

  gl2ps = (GL2PScontext*)gl2psMalloc(sizeof(GL2PScontext));
  gl2ps->maxbestroot = 10;
  gl2ps->format = format;
  gl2ps->sort = sort;
  gl2ps->options = options;
  gl2ps->compress = NULL;
  gl2ps->imagemap_head = NULL;
  gl2ps->imagemap_tail = NULL;

  /*precision*/
  gl2ps->EPSILON = precision;

  /*progress bar*/
  gl2ps->PBFunction = PB_Function;
  gl2ps->PBObject = PB_Object;

  if(gl2ps->options & GL2PS_USE_CURRENT_VIEWPORT){
    glGetIntegerv(GL_VIEWPORT, gl2ps->viewport);
  }
  else{
    for(i = 0; i < 4; i++){
      gl2ps->viewport[i] = viewport[i];
    }
  }
  gl2ps->threshold[0] = nr ? 1.0F/(GLfloat)nr : 0.032F;
  gl2ps->threshold[1] = ng ? 1.0F/(GLfloat)ng : 0.017F;
  gl2ps->threshold[2] = nb ? 1.0F/(GLfloat)nb : 0.050F;
  gl2ps->colormode = colormode;
  gl2ps->buffersize = buffersize > 0 ? buffersize : 2048 * 2048;
  for(i = 0; i < 4; i++){
    gl2ps->lastrgba[i] = -1.0F;
  }
  gl2ps->lastlinewidth = -1.0F;
  gl2ps->imagetree = NULL;
  gl2ps->primitivetoadd = NULL;
  gl2ps->zerosurfacearea = GL_FALSE;  
  gl2ps->pdfprimlist = NULL;
  gl2ps->pdfgrouplist = NULL;
  gl2ps->xreflist = NULL;
  gl2ps->line_pattern = 0xFFFF;
  
  gl2ps->blending = glIsEnabled(GL_BLEND);
  glGetIntegerv(GL_BLEND_SRC, &gl2ps->blendfunc[0]);
  glGetIntegerv(GL_BLEND_DST, &gl2ps->blendfunc[1]);

  if(gl2ps->colormode == GL_RGBA){
    gl2ps->colorsize = 0;
    gl2ps->colormap = NULL;
  }
  else if(gl2ps->colormode == GL_COLOR_INDEX){
    if(!colorsize || !colormap){
      gl2psMsg(GL2PS_ERROR, "Missing colormap for GL_COLOR_INDEX rendering");
      gl2psFree(gl2ps);
      gl2ps = NULL;
      return GL2PS_ERROR;
    }
    gl2ps->colorsize = colorsize;
    gl2ps->colormap = (GL2PSrgba*)gl2psMalloc(gl2ps->colorsize * sizeof(GL2PSrgba));
    memcpy(gl2ps->colormap, colormap, gl2ps->colorsize * sizeof(GL2PSrgba));
  }
  else{
    gl2psMsg(GL2PS_ERROR, "Unknown color mode in gl2psBeginPage");
    gl2psFree(gl2ps);
    gl2ps = NULL;
    return GL2PS_ERROR;
  }
  
  strcpy( fname, filename );
  /*switch( format )
  {
     case GL2PS_PS:
        strcat( fname, ".ps" );
        break;
     case GL2PS_EPS:
        strcat( fname, ".eps" );
        break;
     case GL2PS_TEX:
        strcat( fname, ".tex" );
        break;
     case GL2PS_PDF:
        strcat( fname, ".pdf" );
        break;
     case GL2PS_EMF:
        strcat( fname, ".emf" );
        break;
  }*/

  if( format!=GL2PS_EMF )
  {
     gl2ps->stream = fopen( fname, "wb" );
     if( gl2ps->stream )
       rewind( gl2ps->stream );
  }
   else
       gl2ps->stream = NULL;

  if(!title){
    gl2ps->title = (char*)gl2psMalloc(sizeof(char));
    gl2ps->title[0] = '\0';
  }
  else{
    gl2ps->title = (char*)gl2psMalloc((strlen(title)+1)*sizeof(char));
    strcpy(gl2ps->title, title);
  }
    
  if(!producer){
    gl2ps->producer = (char*)gl2psMalloc(sizeof(char));
    gl2ps->producer[0] = '\0';
  }
  else{
    gl2ps->producer = (char*)gl2psMalloc((strlen(producer)+1)*sizeof(char));
    strcpy(gl2ps->producer, producer);
  }
  
  if(!filename){
    gl2ps->filename = (char*)gl2psMalloc(sizeof(char));
    gl2ps->filename[0] = '\0';
  }
  else{
    gl2ps->filename = (char*)gl2psMalloc((strlen(fname)+1)*sizeof(char));
    strcpy(gl2ps->filename, fname);
  }

  switch(gl2ps->format){
  case GL2PS_TEX :
    gl2psPrintTeXHeader();
    break;
  case GL2PS_PS :
  case GL2PS_EPS :
    gl2psPrintPostScriptHeader();
    break;
  case GL2PS_EMF :
    gl2psPrintEMFHeader();
    break;
  case GL2PS_PDF :
    gl2psPrintPDFHeader();
    break;
  default :
    gl2psMsg(GL2PS_ERROR, "Unknown output format: %d", gl2ps->format);
    gl2psFree(gl2ps->colormap);
    gl2psFree(gl2ps->title);
    gl2psFree(gl2ps->producer);
    gl2psFree(gl2ps->filename);
    gl2psFree(gl2ps);
    gl2ps = NULL;
    return GL2PS_ERROR;
  }

  gl2ps->primitives = gl2psListCreate(500, 500, sizeof(GL2PSprimitive*));
  gl2ps->feedback = (GLfloat*)gl2psMalloc(gl2ps->buffersize * sizeof(GLfloat));
  glFeedbackBuffer(gl2ps->buffersize, GL_3D_COLOR, gl2ps->feedback);
  glRenderMode(GL_FEEDBACK);  

  return GL2PS_SUCCESS;
}

GL2PSDLL_API GLint gl2psEndPage(void)
{
  GLint res;

  if(!gl2ps) return GL2PS_UNINITIALIZED;

  res = gl2psPrintPrimitives();

  /* print the footer even if gl2psPrintPrimitives didn't succeed, so
     that we end up with a valid file */
  switch(gl2ps->format){
  case GL2PS_TEX :
    gl2psPrintTeXFooter();
    break;
  case GL2PS_PS :
  case GL2PS_EPS :
    gl2psPrintPostScriptFooter();
    break;
  case GL2PS_EMF :
    gl2psPrintEMFFooter();
    break;
  case GL2PS_PDF :
    gl2psPrintPDFFooter();
    break;
  }

  if( gl2ps->stream )
    fflush( gl2ps->stream );

  gl2psListDelete(gl2ps->primitives);
  gl2psFreeImagemap(gl2ps->imagemap_head);
  gl2psFree(gl2ps->colormap);
  gl2psFree(gl2ps->title);
  gl2psFree(gl2ps->producer);
  gl2psFree(gl2ps->filename);
  gl2psFree(gl2ps->feedback);

  if( gl2ps->stream )
      fclose( gl2ps->stream );

  gl2psFree(gl2ps);
  gl2ps = NULL;

  return res;
}

GL2PSDLL_API GLint gl2psBeginViewport(GLint viewport[4])
{
  if(!gl2ps) return GL2PS_UNINITIALIZED;

  switch(gl2ps->format){
  case GL2PS_PS :
  case GL2PS_EPS :
    gl2psPrintPostScriptBeginViewport(viewport);
    break;
  case GL2PS_EMF :
    gl2psPrintEMFBeginViewport(viewport);
    break;
  case GL2PS_PDF :
    gl2psPrintPDFBeginViewport(viewport);
    break;
  default :
    break;
  }
  
  return GL2PS_SUCCESS;
}

GL2PSDLL_API GLint gl2psEndViewport(void)
{
  GLint res;

  if(!gl2ps) return GL2PS_UNINITIALIZED;

  switch(gl2ps->format){
  case GL2PS_PS :
  case GL2PS_EPS :
    res = gl2psPrintPostScriptEndViewport();
    break;
  case GL2PS_EMF :
    res = gl2psPrintEMFEndViewport();
    break;
  case GL2PS_PDF :
    res = gl2psPrintPDFEndViewport();
    break;
  default :
    res = GL2PS_SUCCESS;
    break;
  }

  return res;
}

/*GL2PSDLL_API GLint gl2psTextOpt(const char *str, const char *fontname, 
                                GLshort fontsize, GLint alignment, GLfloat angle )
{
  GLfloat pos[4];
  GL2PSprimitive *prim;
  GLboolean valid;
  GL2PSplane plane;

  if(!gl2ps || !str) return GL2PS_UNINITIALIZED;

  if(gl2ps->options & GL2PS_NO_TEXT) return GL2PS_SUCCESS;

  glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID, &valid);
  if(GL_FALSE == valid) return GL2PS_SUCCESS; /* the primitive is culled * /

  glGetFloatv(GL_CURRENT_RASTER_POSITION, pos);

  prim = gl2psCreatePrimitive();
  prim->type = GL2PS_TEXT;
  prim->boundary = 0;
  prim->numverts = 1;
  prim->verts = (GL2PSvertex*)gl2psMalloc(sizeof(GL2PSvertex));  
  prim->verts[0].xyz[0] = pos[0];
  prim->verts[0].xyz[1] = pos[1];
  prim->verts[0].xyz[2] = GL2PS_DEPTH_FACT * pos[2];
  prim->depth = pos[2];
  prim->culled = 0;
  prim->dash = 0;
  prim->width = 1;
  gl2psGetPlane( prim, plane );

  /* glGetFloatv(GL_CURRENT_RASTER_COLOR, prim->verts[0].rgba); * /
  glGetFloatv( GL_CURRENT_COLOR, prim->verts[0].rgba );

  prim->data.text = (GL2PSstring*)gl2psMalloc(sizeof(GL2PSstring));
  prim->data.text->str = (char*)gl2psMalloc((strlen(str)+1)*sizeof(char));
  strcpy(prim->data.text->str, str); 
  prim->data.text->fontname = (char*)gl2psMalloc((strlen(fontname)+1)*sizeof(char));
  strcpy(prim->data.text->fontname, fontname);
  prim->data.text->fontsize = fontsize;
  prim->data.text->alignment = alignment;
  prim->data.text->angle = angle;

  gl2psListAdd(gl2ps->primitives, &prim);

  return GL2PS_SUCCESS;
}

Standard_EXPORT GLint gl2psText( const char *str, const char *fontname,
                                 GLshort fontsize )
{
  return gl2psTextOpt( str, fontname, fontsize, GL2PS_TEXT_BL, 0.0F );
}*/

GL2PSDLL_API GLint gl2psDrawPixels( GLsizei width, GLsizei height,
                                    GLint xorig, GLint yorig,
                                    GLenum format, GLenum type, 
                                    const void *pixels)
{
  int size, i;
  GLfloat pos[4], *piv;
  GL2PSprimitive *prim;
  GLboolean valid;
  GL2PSplane plane;

  if(!gl2ps || !pixels) return GL2PS_UNINITIALIZED;

  if((width <= 0) || (height <= 0)) return GL2PS_ERROR;

  if(gl2ps->options & GL2PS_NO_PIXMAP) return GL2PS_SUCCESS;

  if((format != GL_RGB && format != GL_RGBA) || type != GL_FLOAT){
    gl2psMsg(GL2PS_ERROR, "gl2psDrawPixels only implemented for "
             "GL_RGB/GL_RGBA, GL_FLOAT pixels");
    return GL2PS_ERROR;
  }

  glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID, &valid);
  if(GL_FALSE == valid) return GL2PS_SUCCESS; /* the primitive is culled */

  glGetFloatv(GL_CURRENT_RASTER_POSITION, pos);

  prim = gl2psCreatePrimitive();
  prim->type = GL2PS_PIXMAP;
  prim->boundary = 0;
  prim->numverts = 1;
  prim->verts = (GL2PSvertex*)gl2psMalloc(sizeof(GL2PSvertex));
  prim->verts[0].xyz[0] = pos[0] + xorig;
  prim->verts[0].xyz[1] = pos[1] + yorig;
  prim->verts[0].xyz[2] = GL2PS_DEPTH_FACT * pos[2];
  prim->depth = pos[2];
  prim->culled = 0;
  prim->dash = 0;
  prim->width = 1;
  glGetFloatv(GL_CURRENT_RASTER_COLOR, prim->verts[0].rgba);
  prim->data.image = (GL2PSimage*)gl2psMalloc(sizeof(GL2PSimage));
  prim->data.image->width = width;
  prim->data.image->height = height;
  prim->data.image->format = format;
  prim->data.image->type = type;
  gl2psGetPlane( prim, plane );

  switch(format){
  case GL_RGBA:
    if(gl2ps->options & GL2PS_NO_BLENDING || !gl2ps->blending){
      /* special case: blending turned off */
      prim->data.image->format = GL_RGB;
      size = height * width * 3;
      prim->data.image->pixels = (GLfloat*)gl2psMalloc(size * sizeof(GLfloat));
      piv = (GLfloat*)pixels;
      for(i = 0; i < size; ++i, ++piv){
        prim->data.image->pixels[i] = *piv;
        if(!((i+1)%3))
          ++piv;
      }   
    }
    else{
      size = height * width * 4;
      prim->data.image->pixels = (GLfloat*)gl2psMalloc(size * sizeof(GLfloat));
      memcpy(prim->data.image->pixels, pixels, size * sizeof(GLfloat));
    }
    break;
  case GL_RGB:
  default:
    size = height * width * 3;
    prim->data.image->pixels = (GLfloat*)gl2psMalloc(size * sizeof(GLfloat));
    memcpy(prim->data.image->pixels, pixels, size * sizeof(GLfloat));
    break;
  }

  gl2psListAdd(gl2ps->primitives, &prim);

  return GL2PS_SUCCESS;
}

GL2PSDLL_API GLint gl2psDrawImageMap(GLsizei width, GLsizei height,
                                     const GLfloat position[3],
                                     const unsigned char *imagemap){
  int size, i;
  int sizeoffloat = sizeof(GLfloat);
  
  if(!gl2ps || !imagemap) return GL2PS_UNINITIALIZED;

  if((width <= 0) || (height <= 0)) return GL2PS_ERROR;
  
  size = height + height * ((width-1)/8);
  glPassThrough(GL2PS_DRAW_IMAGEMAP_TOKEN);
  glBegin(GL_POINTS);
  glVertex3f(position[0], position[1],position[2]);
  glEnd();
  glPassThrough( ( GLfloat )width );
  glPassThrough( ( GLfloat )height );
  for(i = 0; i < size; i += sizeoffloat){
    float *value = (float*)imagemap;
    glPassThrough(*value);
    imagemap += sizeoffloat;
  }
  return GL2PS_SUCCESS;
}

GL2PSDLL_API GLint gl2psEnable(GLint mode)
{
  if(!gl2ps) return GL2PS_UNINITIALIZED;

  switch(mode){
  case GL2PS_POLYGON_OFFSET_FILL :
    glPassThrough(GL2PS_BEGIN_POLYGON_OFFSET_FILL);
    glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &gl2ps->offset[0]);
    glGetFloatv(GL_POLYGON_OFFSET_UNITS, &gl2ps->offset[1]);
    break;
  case GL2PS_POLYGON_BOUNDARY :
    glPassThrough(GL2PS_BEGIN_POLYGON_BOUNDARY);
    break;
  case GL2PS_LINE_STIPPLE :
    glPassThrough(GL2PS_BEGIN_LINE_STIPPLE);
    break;
  case GL2PS_BLEND :
    glPassThrough(GL2PS_BEGIN_BLEND);
    break;
  default :
    gl2psMsg(GL2PS_WARNING, "Unknown mode in gl2psEnable: %d", mode);
    return GL2PS_WARNING;
  }

  return GL2PS_SUCCESS;
}

GL2PSDLL_API GLint gl2psDisable(GLint mode)
{
  if(!gl2ps) return GL2PS_UNINITIALIZED;

  switch(mode){
  case GL2PS_POLYGON_OFFSET_FILL :
    glPassThrough(GL2PS_END_POLYGON_OFFSET_FILL);
    break;
  case GL2PS_POLYGON_BOUNDARY :
    glPassThrough(GL2PS_END_POLYGON_BOUNDARY);
    break;
  case GL2PS_LINE_STIPPLE :
    glPassThrough(GL2PS_END_LINE_STIPPLE);
    break;
  case GL2PS_BLEND :
    glPassThrough(GL2PS_END_BLEND);
    break;
  default :
    gl2psMsg(GL2PS_WARNING, "Unknown mode in gl2psDisable: %d", mode);
    return GL2PS_WARNING;
  }

  return GL2PS_SUCCESS;
}

GL2PSDLL_API GLint gl2psPointSize(GLfloat value)
{
  if(!gl2ps) return GL2PS_UNINITIALIZED;

  glPassThrough(GL2PS_SET_POINT_SIZE);
  glPassThrough(value);
  
  return GL2PS_SUCCESS;
}

GL2PSDLL_API GLint gl2psLineWidth(GLfloat value)
{
  if(!gl2ps) return GL2PS_UNINITIALIZED;

  glPassThrough(GL2PS_SET_LINE_WIDTH);
  glPassThrough(value);

  return GL2PS_SUCCESS;
}

GL2PSDLL_API GLint gl2psBlendFunc(GLenum sfactor, GLenum dfactor)
{
  if(!gl2ps) return GL2PS_UNINITIALIZED;

  if(GL_FALSE == gl2psSupportedBlendMode(sfactor, dfactor))
    return GL2PS_WARNING;

  glPassThrough(GL2PS_SRC_BLEND);
  glPassThrough((GLfloat)sfactor);
  glPassThrough(GL2PS_DST_BLEND);
  glPassThrough((GLfloat)dfactor);

  return GL2PS_SUCCESS;
}

Generated by  Doxygen 1.6.0   Back to index