"Fossies" - the Fresh Open Source Software Archive

Member "gfsview-snapshot-121130/gl2ps/gl2ps.c" (30 Nov 2012, 183658 Bytes) of package /linux/privat/gfsview-snapshot-121130.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gl2ps.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * GL2PS, an OpenGL to PostScript Printing Library
    3  * Copyright (C) 1999-2011 C. Geuzaine
    4  *
    5  * This program is free software; you can redistribute it and/or
    6  * modify it under the terms of either:
    7  *
    8  * a) the GNU Library General Public License as published by the Free
    9  * Software Foundation, either version 2 of the License, or (at your
   10  * option) any later version; or
   11  *
   12  * b) the GL2PS License as published by Christophe Geuzaine, either
   13  * version 2 of the License, or (at your option) any later version.
   14  *
   15  * This program is distributed in the hope that it will be useful, but
   16  * WITHOUT ANY WARRANTY; without even the implied warranty of
   17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See either
   18  * the GNU Library General Public License or the GL2PS License for
   19  * more details.
   20  *
   21  * You should have received a copy of the GNU Library General Public
   22  * License along with this library in the file named "COPYING.LGPL";
   23  * if not, write to the Free Software Foundation, Inc., 675 Mass Ave,
   24  * Cambridge, MA 02139, USA.
   25  *
   26  * You should have received a copy of the GL2PS License with this
   27  * library in the file named "COPYING.GL2PS"; if not, I will be glad
   28  * to provide one.
   29  *
   30  * For the latest info about gl2ps and a full list of contributors,
   31  * see http://www.geuz.org/gl2ps/.
   32  *
   33  * Please report all bugs and problems to <gl2ps@geuz.org>.
   34  */
   35 
   36 #include "gl2ps.h"
   37 
   38 #include <math.h>
   39 #include <string.h>
   40 #include <sys/types.h>
   41 #include <stdarg.h>
   42 #include <time.h>
   43 #include <float.h>
   44 
   45 #if defined(GL2PS_HAVE_ZLIB)
   46 #include <zlib.h>
   47 #endif
   48 
   49 #if defined(GL2PS_HAVE_LIBPNG)
   50 #include <png.h>
   51 #endif
   52 
   53 /*********************************************************************
   54  *
   55  * Private definitions, data structures and prototypes
   56  *
   57  *********************************************************************/
   58 
   59 /* Magic numbers (assuming that the order of magnitude of window
   60    coordinates is 10^3) */
   61 
   62 #define GL2PS_EPSILON       5.0e-3F
   63 #define GL2PS_ZSCALE        1000.0F
   64 #define GL2PS_ZOFFSET       5.0e-2F
   65 #define GL2PS_ZOFFSET_LARGE 20.0F
   66 #define GL2PS_ZERO(arg)     (fabs(arg) < 1.e-20)
   67 
   68 /* Primitive types */
   69 
   70 #define GL2PS_NO_TYPE          -1
   71 #define GL2PS_TEXT             1
   72 #define GL2PS_POINT            2
   73 #define GL2PS_LINE             3
   74 #define GL2PS_QUADRANGLE       4
   75 #define GL2PS_TRIANGLE         5
   76 #define GL2PS_PIXMAP           6
   77 #define GL2PS_IMAGEMAP         7
   78 #define GL2PS_IMAGEMAP_WRITTEN 8
   79 #define GL2PS_IMAGEMAP_VISIBLE 9
   80 #define GL2PS_SPECIAL          10
   81 
   82 /* BSP tree primitive comparison */
   83 
   84 #define GL2PS_COINCIDENT  1
   85 #define GL2PS_IN_FRONT_OF 2
   86 #define GL2PS_IN_BACK_OF  3
   87 #define GL2PS_SPANNING    4
   88 
   89 /* 2D BSP tree primitive comparison */
   90 
   91 #define GL2PS_POINT_COINCIDENT 0
   92 #define GL2PS_POINT_INFRONT    1
   93 #define GL2PS_POINT_BACK       2
   94 
   95 /* Internal feedback buffer pass-through tokens */
   96 
   97 #define GL2PS_BEGIN_OFFSET_TOKEN   1
   98 #define GL2PS_END_OFFSET_TOKEN     2
   99 #define GL2PS_BEGIN_BOUNDARY_TOKEN 3
  100 #define GL2PS_END_BOUNDARY_TOKEN   4
  101 #define GL2PS_BEGIN_STIPPLE_TOKEN  5
  102 #define GL2PS_END_STIPPLE_TOKEN    6
  103 #define GL2PS_POINT_SIZE_TOKEN     7
  104 #define GL2PS_LINE_WIDTH_TOKEN     8
  105 #define GL2PS_BEGIN_BLEND_TOKEN    9
  106 #define GL2PS_END_BLEND_TOKEN      10
  107 #define GL2PS_SRC_BLEND_TOKEN      11
  108 #define GL2PS_DST_BLEND_TOKEN      12
  109 #define GL2PS_IMAGEMAP_TOKEN       13
  110 #define GL2PS_DRAW_PIXELS_TOKEN    14
  111 #define GL2PS_TEXT_TOKEN           15
  112 
  113 typedef enum {
  114   T_UNDEFINED    = -1,
  115   T_CONST_COLOR  = 1,
  116   T_VAR_COLOR    = 1<<1,
  117   T_ALPHA_1      = 1<<2,
  118   T_ALPHA_LESS_1 = 1<<3,
  119   T_VAR_ALPHA    = 1<<4
  120 } GL2PS_TRIANGLE_PROPERTY;
  121 
  122 typedef GLfloat GL2PSxyz[3];
  123 typedef GLfloat GL2PSplane[4];
  124 
  125 typedef struct _GL2PSbsptree2d GL2PSbsptree2d;
  126 
  127 struct _GL2PSbsptree2d {
  128   GL2PSplane plane;
  129   GL2PSbsptree2d *front, *back;
  130 };
  131 
  132 typedef struct {
  133   GLint nmax, size, incr, n;
  134   char *array;
  135 } GL2PSlist;
  136 
  137 typedef struct _GL2PSbsptree GL2PSbsptree;
  138 
  139 struct _GL2PSbsptree {
  140   GL2PSplane plane;
  141   GL2PSlist *primitives;
  142   GL2PSbsptree *front, *back;
  143 };
  144 
  145 typedef struct {
  146   GL2PSxyz xyz;
  147   GL2PSrgba rgba;
  148 } GL2PSvertex;
  149 
  150 typedef struct {
  151   GL2PSvertex vertex[3];
  152   int prop;
  153 } GL2PStriangle;
  154 
  155 typedef struct {
  156   GLshort fontsize;
  157   char *str, *fontname;
  158   /* Note: for a 'special' string, 'alignment' holds the format
  159      (PostScript, PDF, etc.) of the special string */
  160   GLint alignment;
  161   GLfloat angle;
  162 } GL2PSstring;
  163 
  164 typedef struct {
  165   GLsizei width, height;
  166   /* Note: for an imagemap, 'type' indicates if it has already been
  167      written to the file or not, and 'format' indicates if it is
  168      visible or not */
  169   GLenum format, type;
  170   GLfloat zoom_x, zoom_y;
  171   GLfloat *pixels;
  172 } GL2PSimage;
  173 
  174 typedef struct _GL2PSimagemap GL2PSimagemap;
  175 
  176 struct _GL2PSimagemap {
  177   GL2PSimage *image;
  178   GL2PSimagemap *next;
  179 };
  180 
  181 typedef struct {
  182   GLshort type, numverts;
  183   GLushort pattern;
  184   char boundary, offset, culled;
  185   GLint factor;
  186   GLfloat width;
  187   GL2PSvertex *verts;
  188   union {
  189     GL2PSstring *text;
  190     GL2PSimage *image;
  191   } data;
  192 } GL2PSprimitive;
  193 
  194 typedef struct {
  195 #if defined(GL2PS_HAVE_ZLIB)
  196   Bytef *dest, *src, *start;
  197   uLongf destLen, srcLen;
  198 #else
  199   int dummy;
  200 #endif
  201 } GL2PScompress;
  202 
  203 typedef struct{
  204   GL2PSlist* ptrlist;
  205   int gsno, fontno, imno, shno, maskshno, trgroupno;
  206   int gsobjno, fontobjno, imobjno, shobjno, maskshobjno, trgroupobjno;
  207 } GL2PSpdfgroup;
  208 
  209 typedef struct {
  210   /* General */
  211   GLint format, sort, options, colorsize, colormode, buffersize;
  212   char *title, *producer, *filename;
  213   GLboolean boundary, blending;
  214   GLfloat *feedback, offset[2], lastlinewidth;
  215   GLint viewport[4], blendfunc[2], lastfactor;
  216   GL2PSrgba *colormap, lastrgba, threshold, bgcolor;
  217   GLushort lastpattern;
  218   GL2PSvertex lastvertex;
  219   GL2PSlist *primitives, *auxprimitives;
  220   FILE *stream;
  221   GL2PScompress *compress;
  222   GLboolean header;
  223 
  224   /* BSP-specific */
  225   GLint maxbestroot;
  226 
  227   /* Occlusion culling-specific */
  228   GLboolean zerosurfacearea;
  229   GL2PSbsptree2d *imagetree;
  230   GL2PSprimitive *primitivetoadd;
  231 
  232   /* PDF-specific */
  233   int streamlength;
  234   GL2PSlist *pdfprimlist, *pdfgrouplist;
  235   int *xreflist;
  236   int objects_stack; /* available objects */
  237   int extgs_stack; /* graphics state object number */
  238   int font_stack; /* font object number */
  239   int im_stack; /* image object number */
  240   int trgroupobjects_stack; /* xobject numbers */
  241   int shader_stack; /* shader object numbers */
  242   int mshader_stack; /* mask shader object numbers */
  243 
  244   /* for image map list */
  245   GL2PSimagemap *imagemap_head;
  246   GL2PSimagemap *imagemap_tail;
  247 } GL2PScontext;
  248 
  249 typedef struct {
  250   void  (*printHeader)(void);
  251   void  (*printFooter)(void);
  252   void  (*beginViewport)(GLint viewport[4]);
  253   GLint (*endViewport)(void);
  254   void  (*printPrimitive)(void *data);
  255   void  (*printFinalPrimitive)(void);
  256   const char *file_extension;
  257   const char *description;
  258 } GL2PSbackend;
  259 
  260 /* The gl2ps context. gl2ps is not thread safe (we should create a
  261    local GL2PScontext during gl2psBeginPage) */
  262 
  263 static GL2PScontext *gl2ps = NULL;
  264 
  265 /* Need to forward-declare this one */
  266 
  267 static GLint gl2psPrintPrimitives(void);
  268 
  269 /*********************************************************************
  270  *
  271  * Utility routines
  272  *
  273  *********************************************************************/
  274 
  275 static void gl2psMsg(GLint level, const char *fmt, ...)
  276 {
  277   va_list args;
  278 
  279   if(!(gl2ps->options & GL2PS_SILENT)){
  280     switch(level){
  281     case GL2PS_INFO : fprintf(stderr, "GL2PS info: "); break;
  282     case GL2PS_WARNING : fprintf(stderr, "GL2PS warning: "); break;
  283     case GL2PS_ERROR : fprintf(stderr, "GL2PS error: "); break;
  284     }
  285     va_start(args, fmt);
  286     vfprintf(stderr, fmt, args);
  287     va_end(args);
  288     fprintf(stderr, "\n");
  289   }
  290   /* if(level == GL2PS_ERROR) exit(1); */
  291 }
  292 
  293 static void *gl2psMalloc(size_t size)
  294 {
  295   void *ptr;
  296 
  297   if(!size) return NULL;
  298   ptr = malloc(size);
  299   if(!ptr){
  300     gl2psMsg(GL2PS_ERROR, "Couldn't allocate requested memory");
  301     return NULL;
  302   }
  303   return ptr;
  304 }
  305 
  306 static void *gl2psRealloc(void *ptr, size_t size)
  307 {
  308   void *orig = ptr;
  309   if(!size) return NULL;
  310   ptr = realloc(orig, size);
  311   if(!ptr){
  312     gl2psMsg(GL2PS_ERROR, "Couldn't reallocate requested memory");
  313     free(orig);
  314     return NULL;
  315   }
  316   return ptr;
  317 }
  318 
  319 static void gl2psFree(void *ptr)
  320 {
  321   if(!ptr) return;
  322   free(ptr);
  323 }
  324 
  325 static int gl2psWriteBigEndian(unsigned long data, int bytes)
  326 {
  327   int i;
  328   int size = sizeof(unsigned long);
  329   for(i = 1; i <= bytes; ++i){
  330     fputc(0xff & (data >> (size - i) * 8), gl2ps->stream);
  331   }
  332   return bytes;
  333 }
  334 
  335 /* zlib compression helper routines */
  336 
  337 #if defined(GL2PS_HAVE_ZLIB)
  338 
  339 static void gl2psSetupCompress(void)
  340 {
  341   gl2ps->compress = (GL2PScompress*)gl2psMalloc(sizeof(GL2PScompress));
  342   gl2ps->compress->src = NULL;
  343   gl2ps->compress->start = NULL;
  344   gl2ps->compress->dest = NULL;
  345   gl2ps->compress->srcLen = 0;
  346   gl2ps->compress->destLen = 0;
  347 }
  348 
  349 static void gl2psFreeCompress(void)
  350 {
  351   if(!gl2ps->compress)
  352     return;
  353   gl2psFree(gl2ps->compress->start);
  354   gl2psFree(gl2ps->compress->dest);
  355   gl2ps->compress->src = NULL;
  356   gl2ps->compress->start = NULL;
  357   gl2ps->compress->dest = NULL;
  358   gl2ps->compress->srcLen = 0;
  359   gl2ps->compress->destLen = 0;
  360 }
  361 
  362 static int gl2psAllocCompress(unsigned int srcsize)
  363 {
  364   gl2psFreeCompress();
  365 
  366   if(!gl2ps->compress || !srcsize)
  367     return GL2PS_ERROR;
  368 
  369   gl2ps->compress->srcLen = srcsize;
  370   gl2ps->compress->destLen = (int)ceil(1.001 * gl2ps->compress->srcLen + 12);
  371   gl2ps->compress->src = (Bytef*)gl2psMalloc(gl2ps->compress->srcLen);
  372   gl2ps->compress->start = gl2ps->compress->src;
  373   gl2ps->compress->dest = (Bytef*)gl2psMalloc(gl2ps->compress->destLen);
  374 
  375   return GL2PS_SUCCESS;
  376 }
  377 
  378 static void *gl2psReallocCompress(unsigned int srcsize)
  379 {
  380   if(!gl2ps->compress || !srcsize)
  381     return NULL;
  382 
  383   if(srcsize < gl2ps->compress->srcLen)
  384     return gl2ps->compress->start;
  385 
  386   gl2ps->compress->srcLen = srcsize;
  387   gl2ps->compress->destLen = (int)ceil(1.001 * gl2ps->compress->srcLen + 12);
  388   gl2ps->compress->src = (Bytef*)gl2psRealloc(gl2ps->compress->src,
  389                                               gl2ps->compress->srcLen);
  390   gl2ps->compress->start = gl2ps->compress->src;
  391   gl2ps->compress->dest = (Bytef*)gl2psRealloc(gl2ps->compress->dest,
  392                                                gl2ps->compress->destLen);
  393 
  394   return gl2ps->compress->start;
  395 }
  396 
  397 static int gl2psWriteBigEndianCompress(unsigned long data, int bytes)
  398 {
  399   int i;
  400   int size = sizeof(unsigned long);
  401   for(i = 1; i <= bytes; ++i){
  402     *gl2ps->compress->src = (Bytef)(0xff & (data >> (size-i) * 8));
  403     ++gl2ps->compress->src;
  404   }
  405   return bytes;
  406 }
  407 
  408 static int gl2psDeflate(void)
  409 {
  410   /* For compatibility with older zlib versions, we use compress(...)
  411      instead of compress2(..., Z_BEST_COMPRESSION) */
  412   return compress(gl2ps->compress->dest, &gl2ps->compress->destLen,
  413                   gl2ps->compress->start, gl2ps->compress->srcLen);
  414 }
  415 
  416 #endif
  417 
  418 static int gl2psPrintf(const char* fmt, ...)
  419 {
  420   int ret;
  421   va_list args;
  422 
  423 #if defined(GL2PS_HAVE_ZLIB)
  424   unsigned int oldsize = 0;
  425   static char buf[1000];
  426   if(gl2ps->options & GL2PS_COMPRESS){
  427     va_start(args, fmt);
  428     ret = vsprintf(buf, fmt, args);
  429     va_end(args);
  430     oldsize = gl2ps->compress->srcLen;
  431     gl2ps->compress->start = (Bytef*)gl2psReallocCompress(oldsize + ret);
  432     memcpy(gl2ps->compress->start+oldsize, buf, ret);
  433     ret = 0;
  434   }
  435   else{
  436 #endif
  437     va_start(args, fmt);
  438     ret = vfprintf(gl2ps->stream, fmt, args);
  439     va_end(args);
  440 #if defined(GL2PS_HAVE_ZLIB)
  441   }
  442 #endif
  443   return ret;
  444 }
  445 
  446 static void gl2psPrintGzipHeader(void)
  447 {
  448 #if defined(GL2PS_HAVE_ZLIB)
  449   char tmp[10] = {'\x1f', '\x8b', /* magic numbers: 0x1f, 0x8b */
  450                   8, /* compression method: Z_DEFLATED */
  451                   0, /* flags */
  452                   0, 0, 0, 0, /* time */
  453                   2, /* extra flags: max compression */
  454                   '\x03'}; /* OS code: 0x03 (Unix) */
  455 
  456   if(gl2ps->options & GL2PS_COMPRESS){
  457     gl2psSetupCompress();
  458     /* add the gzip file header */
  459     fwrite(tmp, 10, 1, gl2ps->stream);
  460   }
  461 #endif
  462 }
  463 
  464 static void gl2psPrintGzipFooter(void)
  465 {
  466 #if defined(GL2PS_HAVE_ZLIB)
  467   int n;
  468   uLong crc, len;
  469   char tmp[8];
  470 
  471   if(gl2ps->options & GL2PS_COMPRESS){
  472     if(Z_OK != gl2psDeflate()){
  473       gl2psMsg(GL2PS_ERROR, "Zlib deflate error");
  474     }
  475     else{
  476       /* determine the length of the header in the zlib stream */
  477       n = 2; /* CMF+FLG */
  478       if(gl2ps->compress->dest[1] & (1<<5)){
  479         n += 4; /* DICTID */
  480       }
  481       /* write the data, without the zlib header and footer */
  482       fwrite(gl2ps->compress->dest+n, gl2ps->compress->destLen-(n+4),
  483              1, gl2ps->stream);
  484       /* add the gzip file footer */
  485       crc = crc32(0L, gl2ps->compress->start, gl2ps->compress->srcLen);
  486       for(n = 0; n < 4; ++n){
  487         tmp[n] = (char)(crc & 0xff);
  488         crc >>= 8;
  489       }
  490       len = gl2ps->compress->srcLen;
  491       for(n = 4; n < 8; ++n){
  492         tmp[n] = (char)(len & 0xff);
  493         len >>= 8;
  494       }
  495       fwrite(tmp, 8, 1, gl2ps->stream);
  496     }
  497     gl2psFreeCompress();
  498     gl2psFree(gl2ps->compress);
  499     gl2ps->compress = NULL;
  500   }
  501 #endif
  502 }
  503 
  504 /* The list handling routines */
  505 
  506 static void gl2psListRealloc(GL2PSlist *list, GLint n)
  507 {
  508   if(!list){
  509     gl2psMsg(GL2PS_ERROR, "Cannot reallocate NULL list");
  510     return;
  511   }
  512   if(n <= 0) return;
  513   if(!list->array){
  514     list->nmax = n;
  515     list->array = (char*)gl2psMalloc(list->nmax * list->size);
  516   }
  517   else{
  518     if(n > list->nmax){
  519       list->nmax = ((n - 1) / list->incr + 1) * list->incr;
  520       list->array = (char*)gl2psRealloc(list->array,
  521                                         list->nmax * list->size);
  522     }
  523   }
  524 }
  525 
  526 static GL2PSlist *gl2psListCreate(GLint n, GLint incr, GLint size)
  527 {
  528   GL2PSlist *list;
  529 
  530   if(n < 0) n = 0;
  531   if(incr <= 0) incr = 1;
  532   list = (GL2PSlist*)gl2psMalloc(sizeof(GL2PSlist));
  533   list->nmax = 0;
  534   list->incr = incr;
  535   list->size = size;
  536   list->n = 0;
  537   list->array = NULL;
  538   gl2psListRealloc(list, n);
  539   return list;
  540 }
  541 
  542 static void gl2psListReset(GL2PSlist *list)
  543 {
  544   if(!list) return;
  545   list->n = 0;
  546 }
  547 
  548 static void gl2psListDelete(GL2PSlist *list)
  549 {
  550   if(!list) return;
  551   gl2psFree(list->array);
  552   gl2psFree(list);
  553 }
  554 
  555 static void gl2psListAdd(GL2PSlist *list, void *data)
  556 {
  557   if(!list){
  558     gl2psMsg(GL2PS_ERROR, "Cannot add into unallocated list");
  559     return;
  560   }
  561   list->n++;
  562   gl2psListRealloc(list, list->n);
  563   memcpy(&list->array[(list->n - 1) * list->size], data, list->size);
  564 }
  565 
  566 static int gl2psListNbr(GL2PSlist *list)
  567 {
  568   if(!list)
  569     return 0;
  570   return list->n;
  571 }
  572 
  573 static void *gl2psListPointer(GL2PSlist *list, GLint index)
  574 {
  575   if(!list){
  576     gl2psMsg(GL2PS_ERROR, "Cannot point into unallocated list");
  577     return NULL;
  578   }
  579   if((index < 0) || (index >= list->n)){
  580     gl2psMsg(GL2PS_ERROR, "Wrong list index in gl2psListPointer");
  581     return NULL;
  582   }
  583   return &list->array[index * list->size];
  584 }
  585 
  586 static void gl2psListSort(GL2PSlist *list,
  587                           int (*fcmp)(const void *a, const void *b))
  588 {
  589   if(!list)
  590     return;
  591   qsort(list->array, list->n, list->size, fcmp);
  592 }
  593 
  594 static void gl2psListAction(GL2PSlist *list, void (*action)(void *data))
  595 {
  596   GLint i;
  597 
  598   for(i = 0; i < gl2psListNbr(list); i++){
  599     (*action)(gl2psListPointer(list, i));
  600   }
  601 }
  602 
  603 static void gl2psListActionInverse(GL2PSlist *list, void (*action)(void *data))
  604 {
  605   GLint i;
  606 
  607   for(i = gl2psListNbr(list); i > 0; i--){
  608     (*action)(gl2psListPointer(list, i-1));
  609   }
  610 }
  611 
  612 #if defined(GL2PS_HAVE_LIBPNG)
  613 
  614 static void gl2psListRead(GL2PSlist *list, int index, void *data)
  615 {
  616   if((index < 0) || (index >= list->n))
  617     gl2psMsg(GL2PS_ERROR, "Wrong list index in gl2psListRead");
  618   memcpy(data, &list->array[index * list->size], list->size);
  619 }
  620 
  621 static void gl2psEncodeBase64Block(unsigned char in[3], unsigned char out[4], int len)
  622 {
  623   static const char cb64[] =
  624     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  625 
  626   out[0] = cb64[ in[0] >> 2 ];
  627   out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
  628   out[2] = (len > 1) ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=';
  629   out[3] = (len > 2) ? cb64[ in[2] & 0x3f ] : '=';
  630 }
  631 
  632 static void gl2psListEncodeBase64(GL2PSlist *list)
  633 {
  634   unsigned char *buffer, in[3], out[4];
  635   int i, n, index, len;
  636 
  637   n = list->n * list->size;
  638   buffer = (unsigned char*)gl2psMalloc(n * sizeof(unsigned char));
  639   memcpy(buffer, list->array, n * sizeof(unsigned char));
  640   gl2psListReset(list);
  641 
  642   index = 0;
  643   while(index < n) {
  644     len = 0;
  645     for(i = 0; i < 3; i++) {
  646       if(index < n){
  647         in[i] = buffer[index];
  648         len++;
  649       }
  650       else{
  651         in[i] = 0;
  652       }
  653       index++;
  654     }
  655     if(len) {
  656       gl2psEncodeBase64Block(in, out, len);
  657       for(i = 0; i < 4; i++)
  658         gl2psListAdd(list, &out[i]);
  659     }
  660   }
  661   gl2psFree(buffer);
  662 }
  663 
  664 #endif
  665 
  666 /* Helpers for rgba colors */
  667 
  668 static GLboolean gl2psSameColor(GL2PSrgba rgba1, GL2PSrgba rgba2)
  669 {
  670   if(!GL2PS_ZERO(rgba1[0] - rgba2[0]) ||
  671      !GL2PS_ZERO(rgba1[1] - rgba2[1]) ||
  672      !GL2PS_ZERO(rgba1[2] - rgba2[2]))
  673     return GL_FALSE;
  674   return GL_TRUE;
  675 }
  676 
  677 static GLboolean gl2psVertsSameColor(const GL2PSprimitive *prim)
  678 {
  679   int i;
  680 
  681   for(i = 1; i < prim->numverts; i++){
  682     if(!gl2psSameColor(prim->verts[0].rgba, prim->verts[i].rgba)){
  683       return GL_FALSE;
  684     }
  685   }
  686   return GL_TRUE;
  687 }
  688 
  689 static GLboolean gl2psSameColorThreshold(int n, GL2PSrgba rgba[],
  690                                          GL2PSrgba threshold)
  691 {
  692   int i;
  693 
  694   if(n < 2) return GL_TRUE;
  695 
  696   for(i = 1; i < n; i++){
  697     if(fabs(rgba[0][0] - rgba[i][0]) > threshold[0] ||
  698        fabs(rgba[0][1] - rgba[i][1]) > threshold[1] ||
  699        fabs(rgba[0][2] - rgba[i][2]) > threshold[2])
  700       return GL_FALSE;
  701   }
  702 
  703   return GL_TRUE;
  704 }
  705 
  706 static void gl2psSetLastColor(GL2PSrgba rgba)
  707 {
  708   int i;
  709   for(i = 0; i < 3; ++i){
  710     gl2ps->lastrgba[i] = rgba[i];
  711   }
  712 }
  713 
  714 static GLfloat gl2psGetRGB(GL2PSimage *im, GLuint x, GLuint y,
  715                            GLfloat *red, GLfloat *green, GLfloat *blue)
  716 {
  717 
  718   GLsizei width = im->width;
  719   GLsizei height = im->height;
  720   GLfloat *pixels = im->pixels;
  721   GLfloat *pimag;
  722 
  723   /* OpenGL image is from down to up, PS image is up to down */
  724   switch(im->format){
  725   case GL_RGBA:
  726     pimag = pixels + 4 * (width * (height - 1 - y) + x);
  727     break;
  728   case GL_RGB:
  729   default:
  730     pimag = pixels + 3 * (width * (height - 1 - y) + x);
  731     break;
  732   }
  733   *red = *pimag; pimag++;
  734   *green = *pimag; pimag++;
  735   *blue = *pimag; pimag++;
  736 
  737   return (im->format == GL_RGBA) ? *pimag : 1.0F;
  738 }
  739 
  740 /* Helper routines for pixmaps */
  741 
  742 static GL2PSimage *gl2psCopyPixmap(GL2PSimage *im)
  743 {
  744   int size;
  745   GL2PSimage *image = (GL2PSimage*)gl2psMalloc(sizeof(GL2PSimage));
  746 
  747   image->width = im->width;
  748   image->height = im->height;
  749   image->format = im->format;
  750   image->type = im->type;
  751   image->zoom_x = im->zoom_x;
  752   image->zoom_y = im->zoom_y;
  753 
  754   switch(image->format){
  755   case GL_RGBA:
  756     size = image->height * image->width * 4 * sizeof(GLfloat);
  757     break;
  758   case GL_RGB:
  759   default:
  760     size = image->height * image->width * 3 * sizeof(GLfloat);
  761     break;
  762   }
  763 
  764   image->pixels = (GLfloat*)gl2psMalloc(size);
  765   memcpy(image->pixels, im->pixels, size);
  766 
  767   return image;
  768 }
  769 
  770 static void gl2psFreePixmap(GL2PSimage *im)
  771 {
  772   if(!im)
  773     return;
  774   gl2psFree(im->pixels);
  775   gl2psFree(im);
  776 }
  777 
  778 #if defined(GL2PS_HAVE_LIBPNG)
  779 
  780 #if !defined(png_jmpbuf)
  781 #  define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
  782 #endif
  783 
  784 static void gl2psUserWritePNG(png_structp png_ptr, png_bytep data, png_size_t length)
  785 {
  786   unsigned int i;
  787   GL2PSlist *png = (GL2PSlist*)png_get_io_ptr(png_ptr);
  788   for(i = 0; i < length; i++)
  789     gl2psListAdd(png, &data[i]);
  790 }
  791 
  792 static void gl2psUserFlushPNG(png_structp png_ptr)
  793 {
  794   (void) png_ptr;  /* not used */
  795 }
  796 
  797 static void gl2psConvertPixmapToPNG(GL2PSimage *pixmap, GL2PSlist *png)
  798 {
  799   png_structp png_ptr;
  800   png_infop info_ptr;
  801   unsigned char *row_data;
  802   GLfloat dr, dg, db;
  803   int row, col;
  804 
  805   if(!(png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)))
  806     return;
  807 
  808   if(!(info_ptr = png_create_info_struct(png_ptr))){
  809     png_destroy_write_struct(&png_ptr, NULL);
  810     return;
  811   }
  812 
  813   if(setjmp(png_jmpbuf(png_ptr))) {
  814     png_destroy_write_struct(&png_ptr, &info_ptr);
  815     return;
  816   }
  817 
  818   png_set_write_fn(png_ptr, (void *)png, gl2psUserWritePNG, gl2psUserFlushPNG);
  819   png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
  820   png_set_IHDR(png_ptr, info_ptr, pixmap->width, pixmap->height, 8,
  821                PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
  822                PNG_FILTER_TYPE_BASE);
  823   png_write_info(png_ptr, info_ptr);
  824 
  825   row_data = (unsigned char*)gl2psMalloc(3 * pixmap->width * sizeof(unsigned char));
  826   for(row = 0; row < pixmap->height; row++){
  827     for(col = 0; col < pixmap->width; col++){
  828       gl2psGetRGB(pixmap, col, row, &dr, &dg, &db);
  829       row_data[3*col] = (unsigned char)(255. * dr);
  830       row_data[3*col+1] = (unsigned char)(255. * dg);
  831       row_data[3*col+2] = (unsigned char)(255. * db);
  832     }
  833     png_write_row(png_ptr, (png_bytep)row_data);
  834   }
  835   gl2psFree(row_data);
  836 
  837   png_write_end(png_ptr, info_ptr);
  838   png_destroy_write_struct(&png_ptr, &info_ptr);
  839 }
  840 
  841 #endif
  842 
  843 /* Helper routines for text strings */
  844 
  845 static GLint gl2psAddText(GLint type, const char *str, const char *fontname,
  846                           GLshort fontsize, GLint alignment, GLfloat angle)
  847 {
  848   GLfloat pos[4];
  849   GL2PSprimitive *prim;
  850   GLboolean valid;
  851 
  852   if(!gl2ps || !str || !fontname) return GL2PS_UNINITIALIZED;
  853 
  854   if(gl2ps->options & GL2PS_NO_TEXT) return GL2PS_SUCCESS;
  855 
  856   glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID, &valid);
  857   if(GL_FALSE == valid) return GL2PS_SUCCESS; /* the primitive is culled */
  858 
  859   glGetFloatv(GL_CURRENT_RASTER_POSITION, pos);
  860 
  861   prim = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
  862   prim->type = type;
  863   prim->boundary = 0;
  864   prim->numverts = 1;
  865   prim->verts = (GL2PSvertex*)gl2psMalloc(sizeof(GL2PSvertex));
  866   prim->verts[0].xyz[0] = pos[0];
  867   prim->verts[0].xyz[1] = pos[1];
  868   prim->verts[0].xyz[2] = pos[2];
  869   prim->culled = 0;
  870   prim->offset = 0;
  871   prim->pattern = 0;
  872   prim->factor = 0;
  873   prim->width = 1;
  874   glGetFloatv(GL_CURRENT_RASTER_COLOR, prim->verts[0].rgba);
  875   prim->data.text = (GL2PSstring*)gl2psMalloc(sizeof(GL2PSstring));
  876   prim->data.text->str = (char*)gl2psMalloc((strlen(str)+1)*sizeof(char));
  877   strcpy(prim->data.text->str, str);
  878   prim->data.text->fontname = (char*)gl2psMalloc((strlen(fontname)+1)*sizeof(char));
  879   strcpy(prim->data.text->fontname, fontname);
  880   prim->data.text->fontsize = fontsize;
  881   prim->data.text->alignment = alignment;
  882   prim->data.text->angle = angle;
  883 
  884   gl2psListAdd(gl2ps->auxprimitives, &prim);
  885   glPassThrough(GL2PS_TEXT_TOKEN);
  886 
  887   return GL2PS_SUCCESS;
  888 }
  889 
  890 static GL2PSstring *gl2psCopyText(GL2PSstring *t)
  891 {
  892   GL2PSstring *text = (GL2PSstring*)gl2psMalloc(sizeof(GL2PSstring));
  893   text->str = (char*)gl2psMalloc((strlen(t->str)+1)*sizeof(char));
  894   strcpy(text->str, t->str);
  895   text->fontname = (char*)gl2psMalloc((strlen(t->fontname)+1)*sizeof(char));
  896   strcpy(text->fontname, t->fontname);
  897   text->fontsize = t->fontsize;
  898   text->alignment = t->alignment;
  899   text->angle = t->angle;
  900 
  901   return text;
  902 }
  903 
  904 static void gl2psFreeText(GL2PSstring *text)
  905 {
  906   if(!text)
  907     return;
  908   gl2psFree(text->str);
  909   gl2psFree(text->fontname);
  910   gl2psFree(text);
  911 }
  912 
  913 /* Helpers for blending modes */
  914 
  915 static GLboolean gl2psSupportedBlendMode(GLenum sfactor, GLenum dfactor)
  916 {
  917   /* returns TRUE if gl2ps supports the argument combination: only two
  918      blending modes have been implemented so far */
  919 
  920   if( (sfactor == GL_SRC_ALPHA && dfactor == GL_ONE_MINUS_SRC_ALPHA) ||
  921       (sfactor == GL_ONE && dfactor == GL_ZERO) )
  922     return GL_TRUE;
  923   return GL_FALSE;
  924 }
  925 
  926 static void gl2psAdaptVertexForBlending(GL2PSvertex *v)
  927 {
  928   /* Transforms vertex depending on the actual blending function -
  929      currently the vertex v is considered as source vertex and his
  930      alpha value is changed to 1.0 if source blending GL_ONE is
  931      active. This might be extended in the future */
  932 
  933   if(!v || !gl2ps)
  934     return;
  935 
  936   if(gl2ps->options & GL2PS_NO_BLENDING || !gl2ps->blending){
  937     v->rgba[3] = 1.0F;
  938     return;
  939   }
  940 
  941   switch(gl2ps->blendfunc[0]){
  942   case GL_ONE:
  943     v->rgba[3] = 1.0F;
  944     break;
  945   default:
  946     break;
  947   }
  948 }
  949 
  950 static void gl2psAssignTriangleProperties(GL2PStriangle *t)
  951 {
  952   /* int i; */
  953 
  954   t->prop = T_VAR_COLOR;
  955 
  956   /* Uncommenting the following lines activates an even more fine
  957      grained distinction between triangle types - please don't delete,
  958      a remarkable amount of PDF handling code inside this file depends
  959      on it if activated */
  960   /*
  961   t->prop = T_CONST_COLOR;
  962   for(i = 0; i < 3; ++i){
  963     if(!GL2PS_ZERO(t->vertex[0].rgba[i] - t->vertex[1].rgba[i]) ||
  964        !GL2PS_ZERO(t->vertex[1].rgba[i] - t->vertex[2].rgba[i])){
  965       t->prop = T_VAR_COLOR;
  966       break;
  967     }
  968   }
  969   */
  970 
  971   if(!GL2PS_ZERO(t->vertex[0].rgba[3] - t->vertex[1].rgba[3]) ||
  972      !GL2PS_ZERO(t->vertex[1].rgba[3] - t->vertex[2].rgba[3])){
  973     t->prop |= T_VAR_ALPHA;
  974   }
  975   else{
  976     if(t->vertex[0].rgba[3] < 1)
  977       t->prop |= T_ALPHA_LESS_1;
  978     else
  979       t->prop |= T_ALPHA_1;
  980   }
  981 }
  982 
  983 static void gl2psFillTriangleFromPrimitive(GL2PStriangle *t, GL2PSprimitive *p,
  984                                            GLboolean assignprops)
  985 {
  986   t->vertex[0] = p->verts[0];
  987   t->vertex[1] = p->verts[1];
  988   t->vertex[2] = p->verts[2];
  989   if(GL_TRUE == assignprops)
  990     gl2psAssignTriangleProperties(t);
  991 }
  992 
  993 static void gl2psInitTriangle(GL2PStriangle *t)
  994 {
  995   int i;
  996   GL2PSvertex vertex = { {-1.0F, -1.0F, -1.0F}, {-1.0F, -1.0F, -1.0F, -1.0F} };
  997   for(i = 0; i < 3; i++)
  998     t->vertex[i] = vertex;
  999   t->prop = T_UNDEFINED;
 1000 }
 1001 
 1002 /* Miscellaneous helper routines */
 1003 
 1004 static GL2PSprimitive *gl2psCopyPrimitive(GL2PSprimitive *p)
 1005 {
 1006   GL2PSprimitive *prim;
 1007 
 1008   if(!p){
 1009     gl2psMsg(GL2PS_ERROR, "Trying to copy an empty primitive");
 1010     return NULL;
 1011   }
 1012 
 1013   prim = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
 1014 
 1015   prim->type = p->type;
 1016   prim->numverts = p->numverts;
 1017   prim->boundary = p->boundary;
 1018   prim->offset = p->offset;
 1019   prim->pattern = p->pattern;
 1020   prim->factor = p->factor;
 1021   prim->culled = p->culled;
 1022   prim->width = p->width;
 1023   prim->verts = (GL2PSvertex*)gl2psMalloc(p->numverts*sizeof(GL2PSvertex));
 1024   memcpy(prim->verts, p->verts, p->numverts * sizeof(GL2PSvertex));
 1025 
 1026   switch(prim->type){
 1027   case GL2PS_PIXMAP :
 1028     prim->data.image = gl2psCopyPixmap(p->data.image);
 1029     break;
 1030   case GL2PS_TEXT :
 1031   case GL2PS_SPECIAL :
 1032     prim->data.text = gl2psCopyText(p->data.text);
 1033     break;
 1034   default:
 1035     break;
 1036   }
 1037 
 1038   return prim;
 1039 }
 1040 
 1041 static GLboolean gl2psSamePosition(GL2PSxyz p1, GL2PSxyz p2)
 1042 {
 1043   if(!GL2PS_ZERO(p1[0] - p2[0]) ||
 1044      !GL2PS_ZERO(p1[1] - p2[1]) ||
 1045      !GL2PS_ZERO(p1[2] - p2[2]))
 1046     return GL_FALSE;
 1047   return GL_TRUE;
 1048 }
 1049 
 1050 /*********************************************************************
 1051  *
 1052  * 3D sorting routines
 1053  *
 1054  *********************************************************************/
 1055 
 1056 static GLfloat gl2psComparePointPlane(GL2PSxyz point, GL2PSplane plane)
 1057 {
 1058   return (plane[0] * point[0] +
 1059           plane[1] * point[1] +
 1060           plane[2] * point[2] +
 1061           plane[3]);
 1062 }
 1063 
 1064 static GLfloat gl2psPsca(GLfloat *a, GLfloat *b)
 1065 {
 1066   return (a[0]*b[0] + a[1]*b[1] + a[2]*b[2]);
 1067 }
 1068 
 1069 static void gl2psPvec(GLfloat *a, GLfloat *b, GLfloat *c)
 1070 {
 1071   c[0] = a[1]*b[2] - a[2]*b[1];
 1072   c[1] = a[2]*b[0] - a[0]*b[2];
 1073   c[2] = a[0]*b[1] - a[1]*b[0];
 1074 }
 1075 
 1076 static GLfloat gl2psNorm(GLfloat *a)
 1077 {
 1078   return (GLfloat)sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
 1079 }
 1080 
 1081 static void gl2psGetNormal(GLfloat *a, GLfloat *b, GLfloat *c)
 1082 {
 1083   GLfloat norm;
 1084 
 1085   gl2psPvec(a, b, c);
 1086   if(!GL2PS_ZERO(norm = gl2psNorm(c))){
 1087     c[0] = c[0] / norm;
 1088     c[1] = c[1] / norm;
 1089     c[2] = c[2] / norm;
 1090   }
 1091   else{
 1092     /* The plane is still wrong despite our tests in gl2psGetPlane.
 1093        Let's return a dummy value for now (this is a hack: we should
 1094        do more intelligent tests in GetPlane) */
 1095     c[0] = c[1] = 0.0F;
 1096     c[2] = 1.0F;
 1097   }
 1098 }
 1099 
 1100 static void gl2psGetPlane(GL2PSprimitive *prim, GL2PSplane plane)
 1101 {
 1102   GL2PSxyz v = {0.0F, 0.0F, 0.0F}, w = {0.0F, 0.0F, 0.0F};
 1103 
 1104   switch(prim->type){
 1105   case GL2PS_TRIANGLE :
 1106   case GL2PS_QUADRANGLE :
 1107     v[0] = prim->verts[1].xyz[0] - prim->verts[0].xyz[0];
 1108     v[1] = prim->verts[1].xyz[1] - prim->verts[0].xyz[1];
 1109     v[2] = prim->verts[1].xyz[2] - prim->verts[0].xyz[2];
 1110     w[0] = prim->verts[2].xyz[0] - prim->verts[0].xyz[0];
 1111     w[1] = prim->verts[2].xyz[1] - prim->verts[0].xyz[1];
 1112     w[2] = prim->verts[2].xyz[2] - prim->verts[0].xyz[2];
 1113     if((GL2PS_ZERO(v[0]) && GL2PS_ZERO(v[1]) && GL2PS_ZERO(v[2])) ||
 1114        (GL2PS_ZERO(w[0]) && GL2PS_ZERO(w[1]) && GL2PS_ZERO(w[2]))){
 1115       plane[0] = plane[1] = 0.0F;
 1116       plane[2] = 1.0F;
 1117       plane[3] = -prim->verts[0].xyz[2];
 1118     }
 1119     else{
 1120       gl2psGetNormal(v, w, plane);
 1121       plane[3] =
 1122         - plane[0] * prim->verts[0].xyz[0]
 1123         - plane[1] * prim->verts[0].xyz[1]
 1124         - plane[2] * prim->verts[0].xyz[2];
 1125     }
 1126     break;
 1127   case GL2PS_LINE :
 1128     v[0] = prim->verts[1].xyz[0] - prim->verts[0].xyz[0];
 1129     v[1] = prim->verts[1].xyz[1] - prim->verts[0].xyz[1];
 1130     v[2] = prim->verts[1].xyz[2] - prim->verts[0].xyz[2];
 1131     if(GL2PS_ZERO(v[0]) && GL2PS_ZERO(v[1]) && GL2PS_ZERO(v[2])){
 1132       plane[0] = plane[1] = 0.0F;
 1133       plane[2] = 1.0F;
 1134       plane[3] = -prim->verts[0].xyz[2];
 1135     }
 1136     else{
 1137       if(GL2PS_ZERO(v[0]))      w[0] = 1.0F;
 1138       else if(GL2PS_ZERO(v[1])) w[1] = 1.0F;
 1139       else                      w[2] = 1.0F;
 1140       gl2psGetNormal(v, w, plane);
 1141       plane[3] =
 1142         - plane[0] * prim->verts[0].xyz[0]
 1143         - plane[1] * prim->verts[0].xyz[1]
 1144         - plane[2] * prim->verts[0].xyz[2];
 1145     }
 1146     break;
 1147   case GL2PS_POINT :
 1148   case GL2PS_PIXMAP :
 1149   case GL2PS_TEXT :
 1150   case GL2PS_SPECIAL :
 1151   case GL2PS_IMAGEMAP:
 1152     plane[0] = plane[1] = 0.0F;
 1153     plane[2] = 1.0F;
 1154     plane[3] = -prim->verts[0].xyz[2];
 1155     break;
 1156   default :
 1157     gl2psMsg(GL2PS_ERROR, "Unknown primitive type in BSP tree");
 1158     plane[0] = plane[1] = plane[3] = 0.0F;
 1159     plane[2] = 1.0F;
 1160     break;
 1161   }
 1162 }
 1163 
 1164 static void gl2psCutEdge(GL2PSvertex *a, GL2PSvertex *b, GL2PSplane plane,
 1165                          GL2PSvertex *c)
 1166 {
 1167   GL2PSxyz v;
 1168   GLfloat sect, psca;
 1169 
 1170   v[0] = b->xyz[0] - a->xyz[0];
 1171   v[1] = b->xyz[1] - a->xyz[1];
 1172   v[2] = b->xyz[2] - a->xyz[2];
 1173 
 1174   if(!GL2PS_ZERO(psca = gl2psPsca(plane, v)))
 1175     sect = -gl2psComparePointPlane(a->xyz, plane) / psca;
 1176   else
 1177     sect = 0.0F;
 1178 
 1179   c->xyz[0] = a->xyz[0] + v[0] * sect;
 1180   c->xyz[1] = a->xyz[1] + v[1] * sect;
 1181   c->xyz[2] = a->xyz[2] + v[2] * sect;
 1182 
 1183   c->rgba[0] = (1 - sect) * a->rgba[0] + sect * b->rgba[0];
 1184   c->rgba[1] = (1 - sect) * a->rgba[1] + sect * b->rgba[1];
 1185   c->rgba[2] = (1 - sect) * a->rgba[2] + sect * b->rgba[2];
 1186   c->rgba[3] = (1 - sect) * a->rgba[3] + sect * b->rgba[3];
 1187 }
 1188 
 1189 static void gl2psCreateSplitPrimitive(GL2PSprimitive *parent, GL2PSplane plane,
 1190                                       GL2PSprimitive *child, GLshort numverts,
 1191                                       GLshort *index0, GLshort *index1)
 1192 {
 1193   GLshort i;
 1194 
 1195   if(parent->type == GL2PS_IMAGEMAP){
 1196     child->type = GL2PS_IMAGEMAP;
 1197     child->data.image = parent->data.image;
 1198   }
 1199   else{
 1200     if(numverts > 4){
 1201       gl2psMsg(GL2PS_WARNING, "%d vertices in polygon", numverts);
 1202       numverts = 4;
 1203     }
 1204     switch(numverts){
 1205     case 1 : child->type = GL2PS_POINT; break;
 1206     case 2 : child->type = GL2PS_LINE; break;
 1207     case 3 : child->type = GL2PS_TRIANGLE; break;
 1208     case 4 : child->type = GL2PS_QUADRANGLE; break;
 1209     default: child->type = GL2PS_NO_TYPE; break;
 1210     }
 1211   }
 1212 
 1213   child->boundary = 0; /* FIXME: not done! */
 1214   child->culled = parent->culled;
 1215   child->offset = parent->offset;
 1216   child->pattern = parent->pattern;
 1217   child->factor = parent->factor;
 1218   child->width = parent->width;
 1219   child->numverts = numverts;
 1220   child->verts = (GL2PSvertex*)gl2psMalloc(numverts * sizeof(GL2PSvertex));
 1221 
 1222   for(i = 0; i < numverts; i++){
 1223     if(index1[i] < 0){
 1224       child->verts[i] = parent->verts[index0[i]];
 1225     }
 1226     else{
 1227       gl2psCutEdge(&parent->verts[index0[i]], &parent->verts[index1[i]],
 1228                    plane, &child->verts[i]);
 1229     }
 1230   }
 1231 }
 1232 
 1233 static void gl2psAddIndex(GLshort *index0, GLshort *index1, GLshort *nb,
 1234                           GLshort i, GLshort j)
 1235 {
 1236   GLint k;
 1237 
 1238   for(k = 0; k < *nb; k++){
 1239     if((index0[k] == i && index1[k] == j) ||
 1240        (index1[k] == i && index0[k] == j)) return;
 1241   }
 1242   index0[*nb] = i;
 1243   index1[*nb] = j;
 1244   (*nb)++;
 1245 }
 1246 
 1247 static GLshort gl2psGetIndex(GLshort i, GLshort num)
 1248 {
 1249   return (i < num - 1) ? i + 1 : 0;
 1250 }
 1251 
 1252 static GLint gl2psTestSplitPrimitive(GL2PSprimitive *prim, GL2PSplane plane)
 1253 {
 1254   GLint type = GL2PS_COINCIDENT;
 1255   GLshort i, j;
 1256   GLfloat d[5];
 1257 
 1258   for(i = 0; i < prim->numverts; i++){
 1259     d[i] = gl2psComparePointPlane(prim->verts[i].xyz, plane);
 1260   }
 1261 
 1262   if(prim->numverts < 2){
 1263     return 0;
 1264   }
 1265   else{
 1266     for(i = 0; i < prim->numverts; i++){
 1267       j = gl2psGetIndex(i, prim->numverts);
 1268       if(d[j] > GL2PS_EPSILON){
 1269         if(type == GL2PS_COINCIDENT)      type = GL2PS_IN_BACK_OF;
 1270         else if(type != GL2PS_IN_BACK_OF) return 1;
 1271         if(d[i] < -GL2PS_EPSILON)         return 1;
 1272       }
 1273       else if(d[j] < -GL2PS_EPSILON){
 1274         if(type == GL2PS_COINCIDENT)       type = GL2PS_IN_FRONT_OF;
 1275         else if(type != GL2PS_IN_FRONT_OF) return 1;
 1276         if(d[i] > GL2PS_EPSILON)           return 1;
 1277       }
 1278     }
 1279   }
 1280   return 0;
 1281 }
 1282 
 1283 static GLint gl2psSplitPrimitive(GL2PSprimitive *prim, GL2PSplane plane,
 1284                                  GL2PSprimitive **front, GL2PSprimitive **back)
 1285 {
 1286   GLshort i, j, in = 0, out = 0, in0[5], in1[5], out0[5], out1[5];
 1287   GLint type;
 1288   GLfloat d[5];
 1289 
 1290   type = GL2PS_COINCIDENT;
 1291 
 1292   for(i = 0; i < prim->numverts; i++){
 1293     d[i] = gl2psComparePointPlane(prim->verts[i].xyz, plane);
 1294   }
 1295 
 1296   switch(prim->type){
 1297   case GL2PS_POINT :
 1298     if(d[0] > GL2PS_EPSILON)       type = GL2PS_IN_BACK_OF;
 1299     else if(d[0] < -GL2PS_EPSILON) type = GL2PS_IN_FRONT_OF;
 1300     else                           type = GL2PS_COINCIDENT;
 1301     break;
 1302   default :
 1303     for(i = 0; i < prim->numverts; i++){
 1304       j = gl2psGetIndex(i, prim->numverts);
 1305       if(d[j] > GL2PS_EPSILON){
 1306         if(type == GL2PS_COINCIDENT)      type = GL2PS_IN_BACK_OF;
 1307         else if(type != GL2PS_IN_BACK_OF) type = GL2PS_SPANNING;
 1308         if(d[i] < -GL2PS_EPSILON){
 1309           gl2psAddIndex(in0, in1, &in, i, j);
 1310           gl2psAddIndex(out0, out1, &out, i, j);
 1311           type = GL2PS_SPANNING;
 1312         }
 1313         gl2psAddIndex(out0, out1, &out, j, -1);
 1314       }
 1315       else if(d[j] < -GL2PS_EPSILON){
 1316         if(type == GL2PS_COINCIDENT)       type = GL2PS_IN_FRONT_OF;
 1317         else if(type != GL2PS_IN_FRONT_OF) type = GL2PS_SPANNING;
 1318         if(d[i] > GL2PS_EPSILON){
 1319           gl2psAddIndex(in0, in1, &in, i, j);
 1320           gl2psAddIndex(out0, out1, &out, i, j);
 1321           type = GL2PS_SPANNING;
 1322         }
 1323         gl2psAddIndex(in0, in1, &in, j, -1);
 1324       }
 1325       else{
 1326         gl2psAddIndex(in0, in1, &in, j, -1);
 1327         gl2psAddIndex(out0, out1, &out, j, -1);
 1328       }
 1329     }
 1330     break;
 1331   }
 1332 
 1333   if(type == GL2PS_SPANNING){
 1334     *back = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
 1335     *front = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
 1336     gl2psCreateSplitPrimitive(prim, plane, *back, out, out0, out1);
 1337     gl2psCreateSplitPrimitive(prim, plane, *front, in, in0, in1);
 1338   }
 1339 
 1340   return type;
 1341 }
 1342 
 1343 static void gl2psDivideQuad(GL2PSprimitive *quad,
 1344                             GL2PSprimitive **t1, GL2PSprimitive **t2)
 1345 {
 1346   *t1 = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
 1347   *t2 = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
 1348   (*t1)->type = (*t2)->type = GL2PS_TRIANGLE;
 1349   (*t1)->numverts = (*t2)->numverts = 3;
 1350   (*t1)->culled = (*t2)->culled = quad->culled;
 1351   (*t1)->offset = (*t2)->offset = quad->offset;
 1352   (*t1)->pattern = (*t2)->pattern = quad->pattern;
 1353   (*t1)->factor = (*t2)->factor = quad->factor;
 1354   (*t1)->width = (*t2)->width = quad->width;
 1355   (*t1)->verts = (GL2PSvertex*)gl2psMalloc(3 * sizeof(GL2PSvertex));
 1356   (*t2)->verts = (GL2PSvertex*)gl2psMalloc(3 * sizeof(GL2PSvertex));
 1357   (*t1)->verts[0] = quad->verts[0];
 1358   (*t1)->verts[1] = quad->verts[1];
 1359   (*t1)->verts[2] = quad->verts[2];
 1360   (*t1)->boundary = ((quad->boundary & 1) ? 1 : 0) | ((quad->boundary & 2) ? 2 : 0);
 1361   (*t2)->verts[0] = quad->verts[0];
 1362   (*t2)->verts[1] = quad->verts[2];
 1363   (*t2)->verts[2] = quad->verts[3];
 1364   (*t2)->boundary = ((quad->boundary & 4) ? 2 : 0) | ((quad->boundary & 8) ? 4 : 0);
 1365 }
 1366 
 1367 static int gl2psCompareDepth(const void *a, const void *b)
 1368 {
 1369   const GL2PSprimitive *q, *w;
 1370   GLfloat dq = 0.0F, dw = 0.0F, diff;
 1371   int i;
 1372 
 1373   q = *(const GL2PSprimitive* const*)a;
 1374   w = *(const GL2PSprimitive* const*)b;
 1375 
 1376   for(i = 0; i < q->numverts; i++){
 1377     dq += q->verts[i].xyz[2];
 1378   }
 1379   dq /= (GLfloat)q->numverts;
 1380 
 1381   for(i = 0; i < w->numverts; i++){
 1382     dw += w->verts[i].xyz[2];
 1383   }
 1384   dw /= (GLfloat)w->numverts;
 1385 
 1386   diff = dq - dw;
 1387   if(diff > 0.){
 1388     return -1;
 1389   }
 1390   else if(diff < 0.){
 1391     return 1;
 1392   }
 1393   else{
 1394     return 0;
 1395   }
 1396 }
 1397 
 1398 static int gl2psTrianglesFirst(const void *a, const void *b)
 1399 {
 1400   const GL2PSprimitive *q, *w;
 1401 
 1402   q = *(const GL2PSprimitive* const*)a;
 1403   w = *(const GL2PSprimitive* const*)b;
 1404   return (q->type < w->type ? 1 : -1);
 1405 }
 1406 
 1407 static GLint gl2psFindRoot(GL2PSlist *primitives, GL2PSprimitive **root)
 1408 {
 1409   GLint i, j, count, best = 1000000, index = 0;
 1410   GL2PSprimitive *prim1, *prim2;
 1411   GL2PSplane plane;
 1412   GLint maxp;
 1413 
 1414   if(!gl2psListNbr(primitives)){
 1415     gl2psMsg(GL2PS_ERROR, "Cannot fint root in empty primitive list");
 1416     return 0;
 1417   }
 1418 
 1419   *root = *(GL2PSprimitive**)gl2psListPointer(primitives, 0);
 1420 
 1421   if(gl2ps->options & GL2PS_BEST_ROOT){
 1422     maxp = gl2psListNbr(primitives);
 1423     if(maxp > gl2ps->maxbestroot){
 1424       maxp = gl2ps->maxbestroot;
 1425     }
 1426     for(i = 0; i < maxp; i++){
 1427       prim1 = *(GL2PSprimitive**)gl2psListPointer(primitives, i);
 1428       gl2psGetPlane(prim1, plane);
 1429       count = 0;
 1430       for(j = 0; j < gl2psListNbr(primitives); j++){
 1431         if(j != i){
 1432           prim2 = *(GL2PSprimitive**)gl2psListPointer(primitives, j);
 1433           count += gl2psTestSplitPrimitive(prim2, plane);
 1434         }
 1435         if(count > best) break;
 1436       }
 1437       if(count < best){
 1438         best = count;
 1439         index = i;
 1440         *root = prim1;
 1441         if(!count) return index;
 1442       }
 1443     }
 1444     /* if(index) gl2psMsg(GL2PS_INFO, "GL2PS_BEST_ROOT was worth it: %d", index); */
 1445     return index;
 1446   }
 1447   else{
 1448     return 0;
 1449   }
 1450 }
 1451 
 1452 static void gl2psFreeImagemap(GL2PSimagemap *list)
 1453 {
 1454   GL2PSimagemap *next;
 1455   while(list != NULL){
 1456     next = list->next;
 1457     gl2psFree(list->image->pixels);
 1458     gl2psFree(list->image);
 1459     gl2psFree(list);
 1460     list = next;
 1461   }
 1462 }
 1463 
 1464 static void gl2psFreePrimitive(void *data)
 1465 {
 1466   GL2PSprimitive *q;
 1467 
 1468   q = *(GL2PSprimitive**)data;
 1469   gl2psFree(q->verts);
 1470   if(q->type == GL2PS_TEXT || q->type == GL2PS_SPECIAL){
 1471     gl2psFreeText(q->data.text);
 1472   }
 1473   else if(q->type == GL2PS_PIXMAP){
 1474     gl2psFreePixmap(q->data.image);
 1475   }
 1476   gl2psFree(q);
 1477 }
 1478 
 1479 static void gl2psAddPrimitiveInList(GL2PSprimitive *prim, GL2PSlist *list)
 1480 {
 1481   GL2PSprimitive *t1, *t2;
 1482 
 1483   if(prim->type != GL2PS_QUADRANGLE){
 1484     gl2psListAdd(list, &prim);
 1485   }
 1486   else{
 1487     gl2psDivideQuad(prim, &t1, &t2);
 1488     gl2psListAdd(list, &t1);
 1489     gl2psListAdd(list, &t2);
 1490     gl2psFreePrimitive(&prim);
 1491   }
 1492 
 1493 }
 1494 
 1495 static void gl2psFreeBspTree(GL2PSbsptree **tree)
 1496 {
 1497   if(*tree){
 1498     if((*tree)->back) gl2psFreeBspTree(&(*tree)->back);
 1499     if((*tree)->primitives){
 1500       gl2psListAction((*tree)->primitives, gl2psFreePrimitive);
 1501       gl2psListDelete((*tree)->primitives);
 1502     }
 1503     if((*tree)->front) gl2psFreeBspTree(&(*tree)->front);
 1504     gl2psFree(*tree);
 1505     *tree = NULL;
 1506   }
 1507 }
 1508 
 1509 static GLboolean gl2psGreater(GLfloat f1, GLfloat f2)
 1510 {
 1511   if(f1 > f2) return GL_TRUE;
 1512   else return GL_FALSE;
 1513 }
 1514 
 1515 static GLboolean gl2psLess(GLfloat f1, GLfloat f2)
 1516 {
 1517   if(f1 < f2) return GL_TRUE;
 1518   else return GL_FALSE;
 1519 }
 1520 
 1521 static void gl2psBuildBspTree(GL2PSbsptree *tree, GL2PSlist *primitives)
 1522 {
 1523   GL2PSprimitive *prim, *frontprim = NULL, *backprim = NULL;
 1524   GL2PSlist *frontlist, *backlist;
 1525   GLint i, index;
 1526 
 1527   tree->front = NULL;
 1528   tree->back = NULL;
 1529   tree->primitives = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
 1530   index = gl2psFindRoot(primitives, &prim);
 1531   gl2psGetPlane(prim, tree->plane);
 1532   gl2psAddPrimitiveInList(prim, tree->primitives);
 1533 
 1534   frontlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
 1535   backlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
 1536 
 1537   for(i = 0; i < gl2psListNbr(primitives); i++){
 1538     if(i != index){
 1539       prim = *(GL2PSprimitive**)gl2psListPointer(primitives,i);
 1540       switch(gl2psSplitPrimitive(prim, tree->plane, &frontprim, &backprim)){
 1541       case GL2PS_COINCIDENT:
 1542         gl2psAddPrimitiveInList(prim, tree->primitives);
 1543         break;
 1544       case GL2PS_IN_BACK_OF:
 1545         gl2psAddPrimitiveInList(prim, backlist);
 1546         break;
 1547       case GL2PS_IN_FRONT_OF:
 1548         gl2psAddPrimitiveInList(prim, frontlist);
 1549         break;
 1550       case GL2PS_SPANNING:
 1551         gl2psAddPrimitiveInList(backprim, backlist);
 1552         gl2psAddPrimitiveInList(frontprim, frontlist);
 1553         gl2psFreePrimitive(&prim);
 1554         break;
 1555       }
 1556     }
 1557   }
 1558 
 1559   if(gl2psListNbr(tree->primitives)){
 1560     gl2psListSort(tree->primitives, gl2psTrianglesFirst);
 1561   }
 1562 
 1563   if(gl2psListNbr(frontlist)){
 1564     gl2psListSort(frontlist, gl2psTrianglesFirst);
 1565     tree->front = (GL2PSbsptree*)gl2psMalloc(sizeof(GL2PSbsptree));
 1566     gl2psBuildBspTree(tree->front, frontlist);
 1567   }
 1568   else{
 1569     gl2psListDelete(frontlist);
 1570   }
 1571 
 1572   if(gl2psListNbr(backlist)){
 1573     gl2psListSort(backlist, gl2psTrianglesFirst);
 1574     tree->back = (GL2PSbsptree*)gl2psMalloc(sizeof(GL2PSbsptree));
 1575     gl2psBuildBspTree(tree->back, backlist);
 1576   }
 1577   else{
 1578     gl2psListDelete(backlist);
 1579   }
 1580 
 1581   gl2psListDelete(primitives);
 1582 }
 1583 
 1584 static void gl2psTraverseBspTree(GL2PSbsptree *tree, GL2PSxyz eye, GLfloat epsilon,
 1585                                  GLboolean (*compare)(GLfloat f1, GLfloat f2),
 1586                                  void (*action)(void *data), int inverse)
 1587 {
 1588   GLfloat result;
 1589 
 1590   if(!tree) return;
 1591 
 1592   result = gl2psComparePointPlane(eye, tree->plane);
 1593 
 1594   if(GL_TRUE == compare(result, epsilon)){
 1595     gl2psTraverseBspTree(tree->back, eye, epsilon, compare, action, inverse);
 1596     if(inverse){
 1597       gl2psListActionInverse(tree->primitives, action);
 1598     }
 1599     else{
 1600       gl2psListAction(tree->primitives, action);
 1601     }
 1602     gl2psTraverseBspTree(tree->front, eye, epsilon, compare, action, inverse);
 1603   }
 1604   else if(GL_TRUE == compare(-epsilon, result)){
 1605     gl2psTraverseBspTree(tree->front, eye, epsilon, compare, action, inverse);
 1606     if(inverse){
 1607       gl2psListActionInverse(tree->primitives, action);
 1608     }
 1609     else{
 1610       gl2psListAction(tree->primitives, action);
 1611     }
 1612     gl2psTraverseBspTree(tree->back, eye, epsilon, compare, action, inverse);
 1613   }
 1614   else{
 1615     gl2psTraverseBspTree(tree->front, eye, epsilon, compare, action, inverse);
 1616     gl2psTraverseBspTree(tree->back, eye, epsilon, compare, action, inverse);
 1617   }
 1618 }
 1619 
 1620 static void gl2psRescaleAndOffset(void)
 1621 {
 1622   GL2PSprimitive *prim;
 1623   GLfloat minZ, maxZ, rangeZ, scaleZ;
 1624   GLfloat factor, units, area, dZ, dZdX, dZdY, maxdZ;
 1625   int i, j;
 1626 
 1627   if(!gl2psListNbr(gl2ps->primitives))
 1628     return;
 1629 
 1630   /* get z-buffer range */
 1631   prim = *(GL2PSprimitive**)gl2psListPointer(gl2ps->primitives, 0);
 1632   minZ = maxZ = prim->verts[0].xyz[2];
 1633   for(i = 1; i < prim->numverts; i++){
 1634     if(prim->verts[i].xyz[2] < minZ) minZ = prim->verts[i].xyz[2];
 1635     if(prim->verts[i].xyz[2] > maxZ) maxZ = prim->verts[i].xyz[2];
 1636   }
 1637   for(i = 1; i < gl2psListNbr(gl2ps->primitives); i++){
 1638     prim = *(GL2PSprimitive**)gl2psListPointer(gl2ps->primitives, i);
 1639     for(j = 0; j < prim->numverts; j++){
 1640       if(prim->verts[j].xyz[2] < minZ) minZ = prim->verts[j].xyz[2];
 1641       if(prim->verts[j].xyz[2] > maxZ) maxZ = prim->verts[j].xyz[2];
 1642     }
 1643   }
 1644   rangeZ = (maxZ - minZ);
 1645 
 1646   /* rescale z-buffer coordinate in [0,GL2PS_ZSCALE], to make it of
 1647      the same order of magnitude as the x and y coordinates */
 1648   scaleZ = GL2PS_ZERO(rangeZ) ? GL2PS_ZSCALE : (GL2PS_ZSCALE / rangeZ);
 1649   /* avoid precision loss (we use floats!) */
 1650   if(scaleZ > 100000.F) scaleZ = 100000.F;
 1651 
 1652   /* apply offsets */
 1653   for(i = 0; i < gl2psListNbr(gl2ps->primitives); i++){
 1654     prim = *(GL2PSprimitive**)gl2psListPointer(gl2ps->primitives, i);
 1655     for(j = 0; j < prim->numverts; j++){
 1656       prim->verts[j].xyz[2] = (prim->verts[j].xyz[2] - minZ) * scaleZ;
 1657     }
 1658     if((gl2ps->options & GL2PS_SIMPLE_LINE_OFFSET) &&
 1659        (prim->type == GL2PS_LINE)){
 1660       if(gl2ps->sort == GL2PS_SIMPLE_SORT){
 1661         prim->verts[0].xyz[2] -= GL2PS_ZOFFSET_LARGE;
 1662         prim->verts[1].xyz[2] -= GL2PS_ZOFFSET_LARGE;
 1663       }
 1664       else{
 1665         prim->verts[0].xyz[2] -= GL2PS_ZOFFSET;
 1666         prim->verts[1].xyz[2] -= GL2PS_ZOFFSET;
 1667       }
 1668     }
 1669     else if(prim->offset && (prim->type == GL2PS_TRIANGLE)){
 1670       factor = gl2ps->offset[0];
 1671       units = gl2ps->offset[1];
 1672       area =
 1673         (prim->verts[1].xyz[0] - prim->verts[0].xyz[0]) *
 1674         (prim->verts[2].xyz[1] - prim->verts[1].xyz[1]) -
 1675         (prim->verts[2].xyz[0] - prim->verts[1].xyz[0]) *
 1676         (prim->verts[1].xyz[1] - prim->verts[0].xyz[1]);
 1677       if(!GL2PS_ZERO(area)){
 1678         dZdX =
 1679           ((prim->verts[2].xyz[1] - prim->verts[1].xyz[1]) *
 1680            (prim->verts[1].xyz[2] - prim->verts[0].xyz[2]) -
 1681            (prim->verts[1].xyz[1] - prim->verts[0].xyz[1]) *
 1682            (prim->verts[2].xyz[2] - prim->verts[1].xyz[2])) / area;
 1683         dZdY =
 1684           ((prim->verts[1].xyz[0] - prim->verts[0].xyz[0]) *
 1685            (prim->verts[2].xyz[2] - prim->verts[1].xyz[2]) -
 1686            (prim->verts[2].xyz[0] - prim->verts[1].xyz[0]) *
 1687            (prim->verts[1].xyz[2] - prim->verts[0].xyz[2])) / area;
 1688         maxdZ = (GLfloat)sqrt(dZdX * dZdX + dZdY * dZdY);
 1689       }
 1690       else{
 1691         maxdZ = 0.0F;
 1692       }
 1693       dZ = factor * maxdZ + units;
 1694       prim->verts[0].xyz[2] += dZ;
 1695       prim->verts[1].xyz[2] += dZ;
 1696       prim->verts[2].xyz[2] += dZ;
 1697     }
 1698   }
 1699 }
 1700 
 1701 /*********************************************************************
 1702  *
 1703  * 2D sorting routines (for occlusion culling)
 1704  *
 1705  *********************************************************************/
 1706 
 1707 static GLint gl2psGetPlaneFromPoints(GL2PSxyz a, GL2PSxyz b, GL2PSplane plane)
 1708 {
 1709   GLfloat n;
 1710 
 1711   plane[0] = b[1] - a[1];
 1712   plane[1] = a[0] - b[0];
 1713   n = (GLfloat)sqrt(plane[0]*plane[0] + plane[1]*plane[1]);
 1714   plane[2] = 0.0F;
 1715   if(!GL2PS_ZERO(n)){
 1716     plane[0] /= n;
 1717     plane[1] /= n;
 1718     plane[3] = -plane[0]*a[0]-plane[1]*a[1];
 1719     return 1;
 1720   }
 1721   else{
 1722     plane[0] = -1.0F;
 1723     plane[1] = 0.0F;
 1724     plane[3] = a[0];
 1725     return 0;
 1726   }
 1727 }
 1728 
 1729 static void gl2psFreeBspImageTree(GL2PSbsptree2d **tree)
 1730 {
 1731   if(*tree){
 1732     if((*tree)->back)  gl2psFreeBspImageTree(&(*tree)->back);
 1733     if((*tree)->front) gl2psFreeBspImageTree(&(*tree)->front);
 1734     gl2psFree(*tree);
 1735     *tree = NULL;
 1736   }
 1737 }
 1738 
 1739 static GLint gl2psCheckPoint(GL2PSxyz point, GL2PSplane plane)
 1740 {
 1741   GLfloat pt_dis;
 1742 
 1743   pt_dis = gl2psComparePointPlane(point, plane);
 1744   if(pt_dis > GL2PS_EPSILON)        return GL2PS_POINT_INFRONT;
 1745   else if(pt_dis < -GL2PS_EPSILON)  return GL2PS_POINT_BACK;
 1746   else                              return GL2PS_POINT_COINCIDENT;
 1747 }
 1748 
 1749 static void gl2psAddPlanesInBspTreeImage(GL2PSprimitive *prim,
 1750                                          GL2PSbsptree2d **tree)
 1751 {
 1752   GLint ret = 0;
 1753   GLint i;
 1754   GLint offset = 0;
 1755   GL2PSbsptree2d *head = NULL, *cur = NULL;
 1756 
 1757   if((*tree == NULL) && (prim->numverts > 2)){
 1758     /* don't cull if transparent
 1759     for(i = 0; i < prim->numverts - 1; i++)
 1760       if(prim->verts[i].rgba[3] < 1.0F) return;
 1761     */
 1762     head = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
 1763     for(i = 0; i < prim->numverts-1; i++){
 1764       if(!gl2psGetPlaneFromPoints(prim->verts[i].xyz,
 1765                                   prim->verts[i+1].xyz,
 1766                                   head->plane)){
 1767         if(prim->numverts-i > 3){
 1768           offset++;
 1769         }
 1770         else{
 1771           gl2psFree(head);
 1772           return;
 1773         }
 1774       }
 1775       else{
 1776         break;
 1777       }
 1778     }
 1779     head->back = NULL;
 1780     head->front = NULL;
 1781     for(i = 2+offset; i < prim->numverts; i++){
 1782       ret = gl2psCheckPoint(prim->verts[i].xyz, head->plane);
 1783       if(ret != GL2PS_POINT_COINCIDENT) break;
 1784     }
 1785     switch(ret){
 1786     case GL2PS_POINT_INFRONT :
 1787       cur = head;
 1788       for(i = 1+offset; i < prim->numverts-1; i++){
 1789         if(cur->front == NULL){
 1790           cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
 1791         }
 1792         if(gl2psGetPlaneFromPoints(prim->verts[i].xyz,
 1793                                    prim->verts[i+1].xyz,
 1794                                    cur->front->plane)){
 1795           cur = cur->front;
 1796           cur->front = NULL;
 1797           cur->back = NULL;
 1798         }
 1799       }
 1800       if(cur->front == NULL){
 1801         cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
 1802       }
 1803       if(gl2psGetPlaneFromPoints(prim->verts[i].xyz,
 1804                                  prim->verts[offset].xyz,
 1805                                  cur->front->plane)){
 1806         cur->front->front = NULL;
 1807         cur->front->back = NULL;
 1808       }
 1809       else{
 1810         gl2psFree(cur->front);
 1811         cur->front = NULL;
 1812       }
 1813       break;
 1814     case GL2PS_POINT_BACK :
 1815       for(i = 0; i < 4; i++){
 1816         head->plane[i] = -head->plane[i];
 1817       }
 1818       cur = head;
 1819       for(i = 1+offset; i < prim->numverts-1; i++){
 1820         if(cur->front == NULL){
 1821           cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
 1822         }
 1823         if(gl2psGetPlaneFromPoints(prim->verts[i+1].xyz,
 1824                                    prim->verts[i].xyz,
 1825                                    cur->front->plane)){
 1826           cur = cur->front;
 1827           cur->front = NULL;
 1828           cur->back = NULL;
 1829         }
 1830       }
 1831       if(cur->front == NULL){
 1832         cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
 1833       }
 1834       if(gl2psGetPlaneFromPoints(prim->verts[offset].xyz,
 1835                                  prim->verts[i].xyz,
 1836                                  cur->front->plane)){
 1837         cur->front->front = NULL;
 1838         cur->front->back = NULL;
 1839       }
 1840       else{
 1841         gl2psFree(cur->front);
 1842         cur->front = NULL;
 1843       }
 1844       break;
 1845     default:
 1846       gl2psFree(head);
 1847       return;
 1848     }
 1849     (*tree) = head;
 1850   }
 1851 }
 1852 
 1853 static GLint gl2psCheckPrimitive(GL2PSprimitive *prim, GL2PSplane plane)
 1854 {
 1855   GLint i;
 1856   GLint pos;
 1857 
 1858   pos = gl2psCheckPoint(prim->verts[0].xyz, plane);
 1859   for(i = 1; i < prim->numverts; i++){
 1860     pos |= gl2psCheckPoint(prim->verts[i].xyz, plane);
 1861     if(pos == (GL2PS_POINT_INFRONT | GL2PS_POINT_BACK)) return GL2PS_SPANNING;
 1862   }
 1863   if(pos & GL2PS_POINT_INFRONT)   return GL2PS_IN_FRONT_OF;
 1864   else if(pos & GL2PS_POINT_BACK) return GL2PS_IN_BACK_OF;
 1865   else                            return GL2PS_COINCIDENT;
 1866 }
 1867 
 1868 static GL2PSprimitive *gl2psCreateSplitPrimitive2D(GL2PSprimitive *parent,
 1869                                                    GLshort numverts,
 1870                                                    GL2PSvertex *vertx)
 1871 {
 1872   GLint i;
 1873   GL2PSprimitive *child = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
 1874 
 1875   if(parent->type == GL2PS_IMAGEMAP){
 1876     child->type = GL2PS_IMAGEMAP;
 1877     child->data.image = parent->data.image;
 1878   }
 1879   else {
 1880     switch(numverts){
 1881     case 1 : child->type = GL2PS_POINT; break;
 1882     case 2 : child->type = GL2PS_LINE; break;
 1883     case 3 : child->type = GL2PS_TRIANGLE; break;
 1884     case 4 : child->type = GL2PS_QUADRANGLE; break;
 1885     default: child->type = GL2PS_NO_TYPE; break; /* FIXME */
 1886     }
 1887   }
 1888   child->boundary = 0; /* FIXME: not done! */
 1889   child->culled = parent->culled;
 1890   child->offset = parent->offset;
 1891   child->pattern = parent->pattern;
 1892   child->factor = parent->factor;
 1893   child->width = parent->width;
 1894   child->numverts = numverts;
 1895   child->verts = (GL2PSvertex*)gl2psMalloc(numverts * sizeof(GL2PSvertex));
 1896   for(i = 0; i < numverts; i++){
 1897     child->verts[i] = vertx[i];
 1898   }
 1899   return child;
 1900 }
 1901 
 1902 static void gl2psSplitPrimitive2D(GL2PSprimitive *prim,
 1903                                   GL2PSplane plane,
 1904                                   GL2PSprimitive **front,
 1905                                   GL2PSprimitive **back)
 1906 {
 1907   /* cur will hold the position of the current vertex
 1908      prev will hold the position of the previous vertex
 1909      prev0 will hold the position of the vertex number 0
 1910      v1 and v2 represent the current and previous vertices, respectively
 1911      flag is set if the current vertex should be checked against the plane */
 1912   GLint cur = -1, prev = -1, i, v1 = 0, v2 = 0, flag = 1, prev0 = -1;
 1913 
 1914   /* list of vertices that will go in front and back primitive */
 1915   GL2PSvertex *front_list = NULL, *back_list = NULL;
 1916 
 1917   /* number of vertices in front and back list */
 1918   GLshort front_count = 0, back_count = 0;
 1919 
 1920   for(i = 0; i <= prim->numverts; i++){
 1921     v1 = i;
 1922     if(v1 == prim->numverts){
 1923       if(prim->numverts < 3) break;
 1924       v1 = 0;
 1925       v2 = prim->numverts - 1;
 1926       cur = prev0;
 1927     }
 1928     else if(flag){
 1929       cur = gl2psCheckPoint(prim->verts[v1].xyz, plane);
 1930       if(i == 0){
 1931         prev0 = cur;
 1932       }
 1933     }
 1934     if(((prev == -1) || (prev == cur) || (prev == 0) || (cur == 0)) &&
 1935        (i < prim->numverts)){
 1936       if(cur == GL2PS_POINT_INFRONT){
 1937         front_count++;
 1938         front_list = (GL2PSvertex*)gl2psRealloc(front_list,
 1939                                                 sizeof(GL2PSvertex)*front_count);
 1940         front_list[front_count-1] = prim->verts[v1];
 1941       }
 1942       else if(cur == GL2PS_POINT_BACK){
 1943         back_count++;
 1944         back_list = (GL2PSvertex*)gl2psRealloc(back_list,
 1945                                                sizeof(GL2PSvertex)*back_count);
 1946         back_list[back_count-1] = prim->verts[v1];
 1947       }
 1948       else{
 1949         front_count++;
 1950         front_list = (GL2PSvertex*)gl2psRealloc(front_list,
 1951                                                 sizeof(GL2PSvertex)*front_count);
 1952         front_list[front_count-1] = prim->verts[v1];
 1953         back_count++;
 1954         back_list = (GL2PSvertex*)gl2psRealloc(back_list,
 1955                                                sizeof(GL2PSvertex)*back_count);
 1956         back_list[back_count-1] = prim->verts[v1];
 1957       }
 1958       flag = 1;
 1959     }
 1960     else if((prev != cur) && (cur != 0) && (prev != 0)){
 1961       if(v1 != 0){
 1962         v2 = v1-1;
 1963         i--;
 1964       }
 1965       front_count++;
 1966       front_list = (GL2PSvertex*)gl2psRealloc(front_list,
 1967                                               sizeof(GL2PSvertex)*front_count);
 1968       gl2psCutEdge(&prim->verts[v2], &prim->verts[v1],
 1969                    plane, &front_list[front_count-1]);
 1970       back_count++;
 1971       back_list = (GL2PSvertex*)gl2psRealloc(back_list,
 1972                                              sizeof(GL2PSvertex)*back_count);
 1973       back_list[back_count-1] = front_list[front_count-1];
 1974       flag = 0;
 1975     }
 1976     prev = cur;
 1977   }
 1978   *front = gl2psCreateSplitPrimitive2D(prim, front_count, front_list);
 1979   *back = gl2psCreateSplitPrimitive2D(prim, back_count, back_list);
 1980   gl2psFree(front_list);
 1981   gl2psFree(back_list);
 1982 }
 1983 
 1984 static GLint gl2psAddInBspImageTree(GL2PSprimitive *prim, GL2PSbsptree2d **tree)
 1985 {
 1986   GLint ret = 0;
 1987   GL2PSprimitive *frontprim = NULL, *backprim = NULL;
 1988 
 1989   /* FIXME: until we consider the actual extent of text strings and
 1990      pixmaps, never cull them. Otherwise the whole string/pixmap gets
 1991      culled as soon as the reference point is hidden */
 1992   if(prim->type == GL2PS_PIXMAP ||
 1993      prim->type == GL2PS_TEXT ||
 1994      prim->type == GL2PS_SPECIAL){
 1995     return 1;
 1996   }
 1997 
 1998   if(*tree == NULL){
 1999     if((prim->type != GL2PS_IMAGEMAP) && (GL_FALSE == gl2ps->zerosurfacearea)){
 2000       gl2psAddPlanesInBspTreeImage(gl2ps->primitivetoadd, tree);
 2001     }
 2002     return 1;
 2003   }
 2004   else{
 2005     switch(gl2psCheckPrimitive(prim, (*tree)->plane)){
 2006     case GL2PS_IN_BACK_OF: return gl2psAddInBspImageTree(prim, &(*tree)->back);
 2007     case GL2PS_IN_FRONT_OF:
 2008       if((*tree)->front != NULL) return gl2psAddInBspImageTree(prim, &(*tree)->front);
 2009       else                       return 0;
 2010     case GL2PS_SPANNING:
 2011       gl2psSplitPrimitive2D(prim, (*tree)->plane, &frontprim, &backprim);
 2012       ret = gl2psAddInBspImageTree(backprim, &(*tree)->back);
 2013       if((*tree)->front != NULL){
 2014         if(gl2psAddInBspImageTree(frontprim, &(*tree)->front)){
 2015           ret = 1;
 2016         }
 2017       }
 2018       gl2psFree(frontprim->verts);
 2019       gl2psFree(frontprim);
 2020       gl2psFree(backprim->verts);
 2021       gl2psFree(backprim);
 2022       return ret;
 2023     case GL2PS_COINCIDENT:
 2024       if((*tree)->back != NULL){
 2025         gl2ps->zerosurfacearea = GL_TRUE;
 2026         ret = gl2psAddInBspImageTree(prim, &(*tree)->back);
 2027         gl2ps->zerosurfacearea = GL_FALSE;
 2028         if(ret) return ret;
 2029       }
 2030       if((*tree)->front != NULL){
 2031         gl2ps->zerosurfacearea = GL_TRUE;
 2032         ret = gl2psAddInBspImageTree(prim, &(*tree)->front);
 2033         gl2ps->zerosurfacearea = GL_FALSE;
 2034         if(ret) return ret;
 2035       }
 2036       if(prim->type == GL2PS_LINE) return 1;
 2037       else                         return 0;
 2038     }
 2039   }
 2040   return 0;
 2041 }
 2042 
 2043 static void gl2psAddInImageTree(void *data)
 2044 {
 2045   GL2PSprimitive *prim = *(GL2PSprimitive **)data;
 2046   gl2ps->primitivetoadd = prim;
 2047   if(prim->type == GL2PS_IMAGEMAP && prim->data.image->format == GL2PS_IMAGEMAP_VISIBLE){
 2048     prim->culled = 1;
 2049   }
 2050   else if(!gl2psAddInBspImageTree(prim, &gl2ps->imagetree)){
 2051     prim->culled = 1;
 2052   }
 2053   else if(prim->type == GL2PS_IMAGEMAP){
 2054     prim->data.image->format = GL2PS_IMAGEMAP_VISIBLE;
 2055   }
 2056 }
 2057 
 2058 /* Boundary construction */
 2059 
 2060 static void gl2psAddBoundaryInList(GL2PSprimitive *prim, GL2PSlist *list)
 2061 {
 2062   GL2PSprimitive *b;
 2063   GLshort i;
 2064   GL2PSxyz c;
 2065 
 2066   c[0] = c[1] = c[2] = 0.0F;
 2067   for(i = 0; i < prim->numverts; i++){
 2068     c[0] += prim->verts[i].xyz[0];
 2069     c[1] += prim->verts[i].xyz[1];
 2070   }
 2071   c[0] /= prim->numverts;
 2072   c[1] /= prim->numverts;
 2073 
 2074   for(i = 0; i < prim->numverts; i++){
 2075     if(prim->boundary & (GLint)pow(2., i)){
 2076       b = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
 2077       b->type = GL2PS_LINE;
 2078       b->offset = prim->offset;
 2079       b->pattern = prim->pattern;
 2080       b->factor = prim->factor;
 2081       b->culled = prim->culled;
 2082       b->width = prim->width;
 2083       b->boundary = 0;
 2084       b->numverts = 2;
 2085       b->verts = (GL2PSvertex*)gl2psMalloc(2 * sizeof(GL2PSvertex));
 2086 
 2087 #if 0 /* FIXME: need to work on boundary offset... */
 2088       v[0] = c[0] - prim->verts[i].xyz[0];
 2089       v[1] = c[1] - prim->verts[i].xyz[1];
 2090       v[2] = 0.0F;
 2091       norm = gl2psNorm(v);
 2092       v[0] /= norm;
 2093       v[1] /= norm;
 2094       b->verts[0].xyz[0] = prim->verts[i].xyz[0] +0.1*v[0];
 2095       b->verts[0].xyz[1] = prim->verts[i].xyz[1] +0.1*v[1];
 2096       b->verts[0].xyz[2] = prim->verts[i].xyz[2];
 2097       v[0] = c[0] - prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[0];
 2098       v[1] = c[1] - prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[1];
 2099       norm = gl2psNorm(v);
 2100       v[0] /= norm;
 2101       v[1] /= norm;
 2102       b->verts[1].xyz[0] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[0] +0.1*v[0];
 2103       b->verts[1].xyz[1] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[1] +0.1*v[1];
 2104       b->verts[1].xyz[2] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[2];
 2105 #else
 2106       b->verts[0].xyz[0] = prim->verts[i].xyz[0];
 2107       b->verts[0].xyz[1] = prim->verts[i].xyz[1];
 2108       b->verts[0].xyz[2] = prim->verts[i].xyz[2];
 2109       b->verts[1].xyz[0] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[0];
 2110       b->verts[1].xyz[1] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[1];
 2111       b->verts[1].xyz[2] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[2];
 2112 #endif
 2113 
 2114       b->verts[0].rgba[0] = 0.0F;
 2115       b->verts[0].rgba[1] = 0.0F;
 2116       b->verts[0].rgba[2] = 0.0F;
 2117       b->verts[0].rgba[3] = 0.0F;
 2118       b->verts[1].rgba[0] = 0.0F;
 2119       b->verts[1].rgba[1] = 0.0F;
 2120       b->verts[1].rgba[2] = 0.0F;
 2121       b->verts[1].rgba[3] = 0.0F;
 2122       gl2psListAdd(list, &b);
 2123     }
 2124   }
 2125 
 2126 }
 2127 
 2128 static void gl2psBuildPolygonBoundary(GL2PSbsptree *tree)
 2129 {
 2130   GLint i;
 2131   GL2PSprimitive *prim;
 2132 
 2133   if(!tree) return;
 2134   gl2psBuildPolygonBoundary(tree->back);
 2135   for(i = 0; i < gl2psListNbr(tree->primitives); i++){
 2136     prim = *(GL2PSprimitive**)gl2psListPointer(tree->primitives, i);
 2137     if(prim->boundary) gl2psAddBoundaryInList(prim, tree->primitives);
 2138   }
 2139   gl2psBuildPolygonBoundary(tree->front);
 2140 }
 2141 
 2142 /*********************************************************************
 2143  *
 2144  * Feedback buffer parser
 2145  *
 2146  *********************************************************************/
 2147 
 2148 static void gl2psAddPolyPrimitive(GLshort type, GLshort numverts,
 2149                                   GL2PSvertex *verts, GLint offset,
 2150                                   GLushort pattern, GLint factor,
 2151                                   GLfloat width, char boundary)
 2152 {
 2153   GL2PSprimitive *prim;
 2154 
 2155   prim = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
 2156   prim->type = type;
 2157   prim->numverts = numverts;
 2158   prim->verts = (GL2PSvertex*)gl2psMalloc(numverts * sizeof(GL2PSvertex));
 2159   memcpy(prim->verts, verts, numverts * sizeof(GL2PSvertex));
 2160   prim->boundary = boundary;
 2161   prim->offset = offset;
 2162   prim->pattern = pattern;
 2163   prim->factor = factor;
 2164   prim->width = width;
 2165   prim->culled = 0;
 2166 
 2167   /* FIXME: here we should have an option to split stretched
 2168      tris/quads to enhance SIMPLE_SORT */
 2169 
 2170   gl2psListAdd(gl2ps->primitives, &prim);
 2171 }
 2172 
 2173 static GLint gl2psGetVertex(GL2PSvertex *v, GLfloat *p)
 2174 {
 2175   GLint i;
 2176 
 2177   v->xyz[0] = p[0];
 2178   v->xyz[1] = p[1];
 2179   v->xyz[2] = p[2];
 2180 
 2181   if(gl2ps->colormode == GL_COLOR_INDEX && gl2ps->colorsize > 0){
 2182     i = (GLint)(p[3] + 0.5);
 2183     v->rgba[0] = gl2ps->colormap[i][0];
 2184     v->rgba[1] = gl2ps->colormap[i][1];
 2185     v->rgba[2] = gl2ps->colormap[i][2];
 2186     v->rgba[3] = gl2ps->colormap[i][3];
 2187     return 4;
 2188   }
 2189   else{
 2190     v->rgba[0] = p[3];
 2191     v->rgba[1] = p[4];
 2192     v->rgba[2] = p[5];
 2193     v->rgba[3] = p[6];
 2194     return 7;
 2195   }
 2196 }
 2197 
 2198 static void gl2psParseFeedbackBuffer(GLint used)
 2199 {
 2200   char flag;
 2201   GLushort pattern = 0;
 2202   GLboolean boundary;
 2203   GLint i, sizeoffloat, count, v, vtot, offset = 0, factor = 0, auxindex = 0;
 2204   GLfloat lwidth = 1.0F, psize = 1.0F;
 2205   GLfloat *current;
 2206   GL2PSvertex vertices[3];
 2207   GL2PSprimitive *prim;
 2208   GL2PSimagemap *node;
 2209 
 2210   current = gl2ps->feedback;
 2211   boundary = gl2ps->boundary = GL_FALSE;
 2212 
 2213   while(used > 0){
 2214 
 2215     if(GL_TRUE == boundary) gl2ps->boundary = GL_TRUE;
 2216 
 2217     switch((GLint)*current){
 2218     case GL_POINT_TOKEN :
 2219       current ++;
 2220       used --;
 2221       i = gl2psGetVertex(&vertices[0], current);
 2222       current += i;
 2223       used    -= i;
 2224       gl2psAddPolyPrimitive(GL2PS_POINT, 1, vertices, 0,
 2225                             pattern, factor, psize, 0);
 2226       break;
 2227     case GL_LINE_TOKEN :
 2228     case GL_LINE_RESET_TOKEN :
 2229       current ++;
 2230       used --;
 2231       i = gl2psGetVertex(&vertices[0], current);
 2232       current += i;
 2233       used    -= i;
 2234       i = gl2psGetVertex(&vertices[1], current);
 2235       current += i;
 2236       used    -= i;
 2237       gl2psAddPolyPrimitive(GL2PS_LINE, 2, vertices, 0,
 2238                             pattern, factor, lwidth, 0);
 2239       break;
 2240     case GL_POLYGON_TOKEN :
 2241       count = (GLint)current[1];
 2242       current += 2;
 2243       used -= 2;
 2244       v = vtot = 0;
 2245       while(count > 0 && used > 0){
 2246         i = gl2psGetVertex(&vertices[v], current);
 2247         gl2psAdaptVertexForBlending(&vertices[v]);
 2248         current += i;
 2249         used    -= i;
 2250         count --;
 2251         vtot++;
 2252         if(v == 2){
 2253           if(GL_TRUE == boundary){
 2254             if(!count && vtot == 2) flag = 1|2|4;
 2255             else if(!count) flag = 2|4;
 2256             else if(vtot == 2) flag = 1|2;
 2257             else flag = 2;
 2258           }
 2259           else
 2260             flag = 0;
 2261           gl2psAddPolyPrimitive(GL2PS_TRIANGLE, 3, vertices, offset,
 2262                                 pattern, factor, 1, flag);
 2263           vertices[1] = vertices[2];
 2264         }
 2265         else
 2266           v ++;
 2267       }
 2268       break;
 2269     case GL_BITMAP_TOKEN :
 2270     case GL_DRAW_PIXEL_TOKEN :
 2271     case GL_COPY_PIXEL_TOKEN :
 2272       current ++;
 2273       used --;
 2274       i = gl2psGetVertex(&vertices[0], current);
 2275       current += i;
 2276       used    -= i;
 2277       break;
 2278     case GL_PASS_THROUGH_TOKEN :
 2279       switch((GLint)current[1]){
 2280       case GL2PS_BEGIN_OFFSET_TOKEN : offset = 1; break;
 2281       case GL2PS_END_OFFSET_TOKEN : offset = 0; break;
 2282       case GL2PS_BEGIN_BOUNDARY_TOKEN : boundary = GL_TRUE; break;
 2283       case GL2PS_END_BOUNDARY_TOKEN : boundary = GL_FALSE; break;
 2284       case GL2PS_END_STIPPLE_TOKEN : pattern = factor = 0; break;
 2285       case GL2PS_BEGIN_BLEND_TOKEN : gl2ps->blending = GL_TRUE; break;
 2286       case GL2PS_END_BLEND_TOKEN : gl2ps->blending = GL_FALSE; break;
 2287       case GL2PS_BEGIN_STIPPLE_TOKEN :
 2288         current += 2;
 2289         used -= 2;
 2290         pattern = (GLushort)current[1];
 2291         current += 2;
 2292         used -= 2;
 2293         factor = (GLint)current[1];
 2294         break;
 2295       case GL2PS_SRC_BLEND_TOKEN :
 2296         current += 2;
 2297         used -= 2;
 2298         gl2ps->blendfunc[0] = (GLint)current[1];
 2299         break;
 2300       case GL2PS_DST_BLEND_TOKEN :
 2301         current += 2;
 2302         used -= 2;
 2303         gl2ps->blendfunc[1] = (GLint)current[1];
 2304         break;
 2305       case GL2PS_POINT_SIZE_TOKEN :
 2306         current += 2;
 2307         used -= 2;
 2308         psize = current[1];
 2309         break;
 2310       case GL2PS_LINE_WIDTH_TOKEN :
 2311         current += 2;
 2312         used -= 2;
 2313         lwidth = current[1];
 2314         break;
 2315       case GL2PS_IMAGEMAP_TOKEN :
 2316         prim = (GL2PSprimitive *)gl2psMalloc(sizeof(GL2PSprimitive));
 2317         prim->type = GL2PS_IMAGEMAP;
 2318         prim->boundary = 0;
 2319         prim->numverts = 4;
 2320         prim->verts = (GL2PSvertex *)gl2psMalloc(4 * sizeof(GL2PSvertex));
 2321         prim->culled = 0;
 2322         prim->offset = 0;
 2323         prim->pattern = 0;
 2324         prim->factor = 0;
 2325         prim->width = 1;
 2326 
 2327         node = (GL2PSimagemap*)gl2psMalloc(sizeof(GL2PSimagemap));
 2328         node->image = (GL2PSimage*)gl2psMalloc(sizeof(GL2PSimage));
 2329         node->image->type = 0;
 2330         node->image->format = 0;
 2331         node->image->zoom_x = 1.0F;
 2332         node->image->zoom_y = 1.0F;
 2333         node->next = NULL;
 2334 
 2335         if(gl2ps->imagemap_head == NULL)
 2336           gl2ps->imagemap_head = node;
 2337         else
 2338           gl2ps->imagemap_tail->next = node;
 2339         gl2ps->imagemap_tail = node;
 2340         prim->data.image = node->image;
 2341 
 2342         current += 2; used -= 2;
 2343         i = gl2psGetVertex(&prim->verts[0], &current[1]);
 2344         current += i; used -= i;
 2345 
 2346         node->image->width = (GLint)current[2];
 2347         current += 2; used -= 2;
 2348         node->image->height = (GLint)current[2];
 2349         prim->verts[0].xyz[0] = prim->verts[0].xyz[0] - (int)(node->image->width / 2) + 0.5F;
 2350         prim->verts[0].xyz[1] = prim->verts[0].xyz[1] - (int)(node->image->height / 2) + 0.5F;
 2351         for(i = 1; i < 4; i++){
 2352           for(v = 0; v < 3; v++){
 2353             prim->verts[i].xyz[v] = prim->verts[0].xyz[v];
 2354             prim->verts[i].rgba[v] = prim->verts[0].rgba[v];
 2355           }
 2356           prim->verts[i].rgba[v] = prim->verts[0].rgba[v];
 2357         }
 2358         prim->verts[1].xyz[0] = prim->verts[1].xyz[0] + node->image->width;
 2359         prim->verts[2].xyz[0] = prim->verts[1].xyz[0];
 2360         prim->verts[2].xyz[1] = prim->verts[2].xyz[1] + node->image->height;
 2361         prim->verts[3].xyz[1] = prim->verts[2].xyz[1];
 2362 
 2363         sizeoffloat = sizeof(GLfloat);
 2364         v = 2 * sizeoffloat;
 2365         vtot = node->image->height + node->image->height *
 2366           ((node->image->width - 1) / 8);
 2367         node->image->pixels = (GLfloat*)gl2psMalloc(v + vtot);
 2368         node->image->pixels[0] = prim->verts[0].xyz[0];
 2369         node->image->pixels[1] = prim->verts[0].xyz[1];
 2370 
 2371         for(i = 0; i < vtot; i += sizeoffloat){
 2372           current += 2; used -= 2;
 2373           if((vtot - i) >= 4)
 2374             memcpy(&(((char*)(node->image->pixels))[i + v]), &(current[2]), sizeoffloat);
 2375           else
 2376             memcpy(&(((char*)(node->image->pixels))[i + v]), &(current[2]), vtot - i);
 2377         }
 2378         current++; used--;
 2379         gl2psListAdd(gl2ps->primitives, &prim);
 2380         break;
 2381       case GL2PS_DRAW_PIXELS_TOKEN :
 2382       case GL2PS_TEXT_TOKEN :
 2383         if(auxindex < gl2psListNbr(gl2ps->auxprimitives))
 2384           gl2psListAdd(gl2ps->primitives,
 2385                        gl2psListPointer(gl2ps->auxprimitives, auxindex++));
 2386         else
 2387           gl2psMsg(GL2PS_ERROR, "Wrong number of auxiliary tokens in buffer");
 2388         break;
 2389       }
 2390       current += 2;
 2391       used -= 2;
 2392       break;
 2393     default :
 2394       gl2psMsg(GL2PS_WARNING, "Unknown token in buffer");
 2395       current ++;
 2396       used --;
 2397       break;
 2398     }
 2399   }
 2400 
 2401   gl2psListReset(gl2ps->auxprimitives);
 2402 }
 2403 
 2404 /*********************************************************************
 2405  *
 2406  * PostScript routines
 2407  *
 2408  *********************************************************************/
 2409 
 2410 static void gl2psWriteByte(unsigned char byte)
 2411 {
 2412   unsigned char h = byte / 16;
 2413   unsigned char l = byte % 16;
 2414   gl2psPrintf("%x%x", h, l);
 2415 }
 2416 
 2417 static void gl2psPrintPostScriptPixmap(GLfloat x, GLfloat y, GL2PSimage *im)
 2418 {
 2419   GLuint nbhex, nbyte, nrgb, nbits;
 2420   GLuint row, col, ibyte, icase;
 2421   GLfloat dr, dg, db, fgrey;
 2422   unsigned char red = 0, green = 0, blue = 0, b, grey;
 2423   GLuint width = (GLuint)im->width;
 2424   GLuint height = (GLuint)im->height;
 2425 
 2426   /* FIXME: should we define an option for these? Or just keep the
 2427      8-bit per component case? */
 2428   int greyscale = 0; /* set to 1 to output greyscale image */
 2429   int nbit = 8; /* number of bits per color compoment (2, 4 or 8) */
 2430 
 2431   if((width <= 0) || (height <= 0)) return;
 2432 
 2433   gl2psPrintf("gsave\n");
 2434   gl2psPrintf("%.2f %.2f translate\n", x, y);
 2435   gl2psPrintf("%.2f %.2f scale\n", width * im->zoom_x, height * im->zoom_y);
 2436 
 2437   if(greyscale){ /* greyscale */
 2438     gl2psPrintf("/picstr %d string def\n", width);
 2439     gl2psPrintf("%d %d %d\n", width, height, 8);
 2440     gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", width, height, height);
 2441     gl2psPrintf("{ currentfile picstr readhexstring pop }\n");
 2442     gl2psPrintf("image\n");
 2443     for(row = 0; row < height; row++){
 2444       for(col = 0; col < width; col++){
 2445         gl2psGetRGB(im, col, row, &dr, &dg, &db);
 2446         fgrey = (0.30F * dr + 0.59F * dg + 0.11F * db);
 2447         grey = (unsigned char)(255. * fgrey);
 2448         gl2psWriteByte(grey);
 2449       }
 2450       gl2psPrintf("\n");
 2451     }
 2452     nbhex = width * height * 2;
 2453     gl2psPrintf("%%%% nbhex digit          :%d\n", nbhex);
 2454   }
 2455   else if(nbit == 2){ /* color, 2 bits for r and g and b; rgbs following each other */
 2456     nrgb = width  * 3;
 2457     nbits = nrgb * nbit;
 2458     nbyte = nbits / 8;
 2459     if((nbyte * 8) != nbits) nbyte++;
 2460     gl2psPrintf("/rgbstr %d string def\n", nbyte);
 2461     gl2psPrintf("%d %d %d\n", width, height, nbit);
 2462     gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", width, height, height);
 2463     gl2psPrintf("{ currentfile rgbstr readhexstring pop }\n");
 2464     gl2psPrintf("false 3\n");
 2465     gl2psPrintf("colorimage\n");
 2466     for(row = 0; row < height; row++){
 2467       icase = 1;
 2468       col = 0;
 2469       b = 0;
 2470       for(ibyte = 0; ibyte < nbyte; ibyte++){
 2471         if(icase == 1) {
 2472           if(col < width) {
 2473             gl2psGetRGB(im, col, row, &dr, &dg, &db);
 2474           }
 2475           else {
 2476             dr = dg = db = 0;
 2477           }
 2478           col++;
 2479           red = (unsigned char)(3. * dr);
 2480           green = (unsigned char)(3. * dg);
 2481           blue = (unsigned char)(3. * db);
 2482           b = red;
 2483           b = (b<<2) + green;
 2484           b = (b<<2) + blue;
 2485           if(col < width) {
 2486             gl2psGetRGB(im, col, row, &dr, &dg, &db);
 2487           }
 2488           else {
 2489             dr = dg = db = 0;
 2490           }
 2491           col++;
 2492           red = (unsigned char)(3. * dr);
 2493           green = (unsigned char)(3. * dg);
 2494           blue = (unsigned char)(3. * db);
 2495           b = (b<<2) + red;
 2496           gl2psWriteByte(b);
 2497           b = 0;
 2498           icase++;
 2499         }
 2500         else if(icase == 2) {
 2501           b = green;
 2502           b = (b<<2) + blue;
 2503           if(col < width) {
 2504             gl2psGetRGB(im, col, row, &dr, &dg, &db);
 2505           }
 2506           else {
 2507             dr = dg = db = 0;
 2508           }
 2509           col++;
 2510           red = (unsigned char)(3. * dr);
 2511           green = (unsigned char)(3. * dg);
 2512           blue = (unsigned char)(3. * db);
 2513           b = (b<<2) + red;
 2514           b = (b<<2) + green;
 2515           gl2psWriteByte(b);
 2516           b = 0;
 2517           icase++;
 2518         }
 2519         else if(icase == 3) {
 2520           b = blue;
 2521           if(col < width) {
 2522             gl2psGetRGB(im, col, row, &dr, &dg, &db);
 2523           }
 2524           else {
 2525             dr = dg = db = 0;
 2526           }
 2527           col++;
 2528           red = (unsigned char)(3. * dr);
 2529           green = (unsigned char)(3. * dg);
 2530           blue = (unsigned char)(3. * db);
 2531           b = (b<<2) + red;
 2532           b = (b<<2) + green;
 2533           b = (b<<2) + blue;
 2534           gl2psWriteByte(b);
 2535           b = 0;
 2536           icase = 1;
 2537         }
 2538       }
 2539       gl2psPrintf("\n");
 2540     }
 2541   }
 2542   else if(nbit == 4){ /* color, 4 bits for r and g and b; rgbs following each other */
 2543     nrgb = width  * 3;
 2544     nbits = nrgb * nbit;
 2545     nbyte = nbits / 8;
 2546     if((nbyte * 8) != nbits) nbyte++;
 2547     gl2psPrintf("/rgbstr %d string def\n", nbyte);
 2548     gl2psPrintf("%d %d %d\n", width, height, nbit);
 2549     gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", width, height, height);
 2550     gl2psPrintf("{ currentfile rgbstr readhexstring pop }\n");
 2551     gl2psPrintf("false 3\n");
 2552     gl2psPrintf("colorimage\n");
 2553     for(row = 0; row < height; row++){
 2554       col = 0;
 2555       icase = 1;
 2556       for(ibyte = 0; ibyte < nbyte; ibyte++){
 2557         if(icase == 1) {
 2558           if(col < width) {
 2559             gl2psGetRGB(im, col, row, &dr, &dg, &db);
 2560           }
 2561           else {
 2562             dr = dg = db = 0;
 2563           }
 2564           col++;
 2565           red = (unsigned char)(15. * dr);
 2566           green = (unsigned char)(15. * dg);
 2567           gl2psPrintf("%x%x", red, green);
 2568           icase++;
 2569         }
 2570         else if(icase == 2) {
 2571           blue = (unsigned char)(15. * db);
 2572           if(col < width) {
 2573             gl2psGetRGB(im, col, row, &dr, &dg, &db);
 2574           }
 2575           else {
 2576             dr = dg = db = 0;
 2577           }
 2578           col++;
 2579           red = (unsigned char)(15. * dr);
 2580           gl2psPrintf("%x%x", blue, red);
 2581           icase++;
 2582         }
 2583         else if(icase == 3) {
 2584           green = (unsigned char)(15. * dg);
 2585           blue = (unsigned char)(15. * db);
 2586           gl2psPrintf("%x%x", green, blue);
 2587           icase = 1;
 2588         }
 2589       }
 2590       gl2psPrintf("\n");
 2591     }
 2592   }
 2593   else{ /* 8 bit for r and g and b */
 2594     nbyte = width * 3;
 2595     gl2psPrintf("/rgbstr %d string def\n", nbyte);
 2596     gl2psPrintf("%d %d %d\n", width, height, 8);
 2597     gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", width, height, height);
 2598     gl2psPrintf("{ currentfile rgbstr readhexstring pop }\n");
 2599     gl2psPrintf("false 3\n");
 2600     gl2psPrintf("colorimage\n");
 2601     for(row = 0; row < height; row++){
 2602       for(col = 0; col < width; col++){
 2603         gl2psGetRGB(im, col, row, &dr, &dg, &db);
 2604         red = (unsigned char)(255. * dr);
 2605         gl2psWriteByte(red);
 2606         green = (unsigned char)(255. * dg);
 2607         gl2psWriteByte(green);
 2608         blue = (unsigned char)(255. * db);
 2609         gl2psWriteByte(blue);
 2610       }
 2611       gl2psPrintf("\n");
 2612     }
 2613   }
 2614 
 2615   gl2psPrintf("grestore\n");
 2616 }
 2617 
 2618 static void gl2psPrintPostScriptImagemap(GLfloat x, GLfloat y,
 2619                                          GLsizei width, GLsizei height,
 2620                                          const unsigned char *imagemap){
 2621   int i, size;
 2622 
 2623   if((width <= 0) || (height <= 0)) return;
 2624 
 2625   size = height + height * (width - 1) / 8;
 2626 
 2627   gl2psPrintf("gsave\n");
 2628   gl2psPrintf("%.2f %.2f translate\n", x, y);
 2629   gl2psPrintf("%d %d scale\n%d %d\ntrue\n", width, height,width, height);
 2630   gl2psPrintf("[ %d 0 0 -%d 0 %d ] {<", width, height);
 2631   for(i = 0; i < size; i++){
 2632     gl2psWriteByte(*imagemap);
 2633     imagemap++;
 2634   }
 2635   gl2psPrintf(">} imagemask\ngrestore\n");
 2636 }
 2637 
 2638 static void gl2psPrintPostScriptHeader(void)
 2639 {
 2640   time_t now;
 2641 
 2642   /* Since compression is not part of the PostScript standard,
 2643      compressed PostScript files are just gzipped PostScript files
 2644      ("ps.gz" or "eps.gz") */
 2645   gl2psPrintGzipHeader();
 2646 
 2647   time(&now);
 2648 
 2649   if(gl2ps->format == GL2PS_PS){
 2650     gl2psPrintf("%%!PS-Adobe-3.0\n");
 2651   }
 2652   else{
 2653     gl2psPrintf("%%!PS-Adobe-3.0 EPSF-3.0\n");
 2654   }
 2655 
 2656   gl2psPrintf("%%%%Title: %s\n"
 2657               "%%%%Creator: GL2PS %d.%d.%d%s, %s\n"
 2658               "%%%%For: %s\n"
 2659               "%%%%CreationDate: %s"
 2660               "%%%%LanguageLevel: 3\n"
 2661               "%%%%DocumentData: Clean7Bit\n"
 2662               "%%%%Pages: 1\n",
 2663               gl2ps->title, GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION,
 2664               GL2PS_PATCH_VERSION, GL2PS_EXTRA_VERSION, GL2PS_COPYRIGHT,
 2665               gl2ps->producer, ctime(&now));
 2666 
 2667   if(gl2ps->format == GL2PS_PS){
 2668     gl2psPrintf("%%%%Orientation: %s\n"
 2669                 "%%%%DocumentMedia: Default %d %d 0 () ()\n",
 2670                 (gl2ps->options & GL2PS_LANDSCAPE) ? "Landscape" : "Portrait",
 2671                 (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[3] :
 2672                 (int)gl2ps->viewport[2],
 2673                 (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[2] :
 2674                 (int)gl2ps->viewport[3]);
 2675   }
 2676 
 2677   gl2psPrintf("%%%%BoundingBox: %d %d %d %d\n"
 2678               "%%%%EndComments\n",
 2679               (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[1] :
 2680               (int)gl2ps->viewport[0],
 2681               (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[0] :
 2682               (int)gl2ps->viewport[1],
 2683               (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[3] :
 2684               (int)gl2ps->viewport[2],
 2685               (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[2] :
 2686               (int)gl2ps->viewport[3]);
 2687 
 2688   /* RGB color: r g b C (replace C by G in output to change from rgb to gray)
 2689      Grayscale: r g b G
 2690      Font choose: size fontname FC
 2691      Text string: (string) x y size fontname S??
 2692      Rotated text string: (string) angle x y size fontname S??R
 2693      Point primitive: x y size P
 2694      Line width: width W
 2695      Line start: x y LS
 2696      Line joining last point: x y L
 2697      Line end: x y LE
 2698      Flat-shaded triangle: x3 y3 x2 y2 x1 y1 T
 2699      Smooth-shaded triangle: x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 ST */
 2700 
 2701   gl2psPrintf("%%%%BeginProlog\n"
 2702               "/gl2psdict 64 dict def gl2psdict begin\n"
 2703               "0 setlinecap 0 setlinejoin\n"
 2704               "/tryPS3shading %s def %% set to false to force subdivision\n"
 2705               "/rThreshold %g def %% red component subdivision threshold\n"
 2706               "/gThreshold %g def %% green component subdivision threshold\n"
 2707               "/bThreshold %g def %% blue component subdivision threshold\n",
 2708               (gl2ps->options & GL2PS_NO_PS3_SHADING) ? "false" : "true",
 2709               gl2ps->threshold[0], gl2ps->threshold[1], gl2ps->threshold[2]);
 2710 
 2711   gl2psPrintf("/BD { bind def } bind def\n"
 2712               "/C  { setrgbcolor } BD\n"
 2713               "/G  { 0.082 mul exch 0.6094 mul add exch 0.3086 mul add neg 1.0 add setgray } BD\n"
 2714               "/W  { setlinewidth } BD\n");
 2715 
 2716   gl2psPrintf("/FC { findfont exch /SH exch def SH scalefont setfont } BD\n"
 2717               "/SW { dup stringwidth pop } BD\n"
 2718               "/S  { FC moveto show } BD\n"
 2719               "/SBC{ FC moveto SW -2 div 0 rmoveto show } BD\n"
 2720               "/SBR{ FC moveto SW neg 0 rmoveto show } BD\n"
 2721               "/SCL{ FC moveto 0 SH -2 div rmoveto show } BD\n"
 2722               "/SCC{ FC moveto SW -2 div SH -2 div rmoveto show } BD\n"
 2723               "/SCR{ FC moveto SW neg SH -2 div rmoveto show } BD\n"
 2724               "/STL{ FC moveto 0 SH neg rmoveto show } BD\n"
 2725               "/STC{ FC moveto SW -2 div SH neg rmoveto show } BD\n"
 2726               "/STR{ FC moveto SW neg SH neg rmoveto show } BD\n");
 2727 
 2728   /* rotated text routines: same nameanem with R appended */
 2729 
 2730   gl2psPrintf("/FCT { FC translate 0 0 } BD\n"
 2731               "/SR  { gsave FCT moveto rotate show grestore } BD\n"
 2732               "/SBCR{ gsave FCT moveto rotate SW -2 div 0 rmoveto show grestore } BD\n"
 2733               "/SBRR{ gsave FCT moveto rotate SW neg 0 rmoveto show grestore } BD\n"
 2734               "/SCLR{ gsave FCT moveto rotate 0 SH -2 div rmoveto show grestore} BD\n");
 2735   gl2psPrintf("/SCCR{ gsave FCT moveto rotate SW -2 div SH -2 div rmoveto show grestore} BD\n"
 2736               "/SCRR{ gsave FCT moveto rotate SW neg SH -2 div rmoveto show grestore} BD\n"
 2737               "/STLR{ gsave FCT moveto rotate 0 SH neg rmoveto show grestore } BD\n"
 2738               "/STCR{ gsave FCT moveto rotate SW -2 div SH neg rmoveto show grestore } BD\n"
 2739               "/STRR{ gsave FCT moveto rotate SW neg SH neg rmoveto show grestore } BD\n");
 2740 
 2741   gl2psPrintf("/P  { newpath 0.0 360.0 arc closepath fill } BD\n"
 2742               "/LS { newpath moveto } BD\n"
 2743               "/L  { lineto } BD\n"
 2744               "/LE { lineto stroke } BD\n"
 2745               "/T  { newpath moveto lineto lineto closepath fill } BD\n");
 2746 
 2747   /* Smooth-shaded triangle with PostScript level 3 shfill operator:
 2748         x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 STshfill */
 2749 
 2750   gl2psPrintf("/STshfill {\n"
 2751               "      /b1 exch def /g1 exch def /r1 exch def /y1 exch def /x1 exch def\n"
 2752               "      /b2 exch def /g2 exch def /r2 exch def /y2 exch def /x2 exch def\n"
 2753               "      /b3 exch def /g3 exch def /r3 exch def /y3 exch def /x3 exch def\n"
 2754               "      gsave << /ShadingType 4 /ColorSpace [/DeviceRGB]\n"
 2755               "      /DataSource [ 0 x1 y1 r1 g1 b1 0 x2 y2 r2 g2 b2 0 x3 y3 r3 g3 b3 ] >>\n"
 2756               "      shfill grestore } BD\n");
 2757 
 2758   /* Flat-shaded triangle with middle color:
 2759         x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 Tm */
 2760 
 2761   gl2psPrintf(/* stack : x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 */
 2762               "/Tm { 3 -1 roll 8 -1 roll 13 -1 roll add add 3 div\n" /* r = (r1+r2+r3)/3 */
 2763               /* stack : x3 y3 g3 b3 x2 y2 g2 b2 x1 y1 g1 b1 r */
 2764               "      3 -1 roll 7 -1 roll 11 -1 roll add add 3 div\n" /* g = (g1+g2+g3)/3 */
 2765               /* stack : x3 y3 b3 x2 y2 b2 x1 y1 b1 r g b */
 2766               "      3 -1 roll 6 -1 roll 9 -1 roll add add 3 div" /* b = (b1+b2+b3)/3 */
 2767               /* stack : x3 y3 x2 y2 x1 y1 r g b */
 2768               " C T } BD\n");
 2769 
 2770   /* Split triangle in four sub-triangles (at sides middle points) and call the
 2771      STnoshfill procedure on each, interpolating the colors in RGB space:
 2772         x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 STsplit
 2773      (in procedure comments key: (Vi) = xi yi ri gi bi) */
 2774 
 2775   gl2psPrintf("/STsplit {\n"
 2776               "      4 index 15 index add 0.5 mul\n" /* x13 = (x1+x3)/2 */
 2777               "      4 index 15 index add 0.5 mul\n" /* y13 = (y1+y3)/2 */
 2778               "      4 index 15 index add 0.5 mul\n" /* r13 = (r1+r3)/2 */
 2779               "      4 index 15 index add 0.5 mul\n" /* g13 = (g1+g3)/2 */
 2780               "      4 index 15 index add 0.5 mul\n" /* b13 = (b1+b3)/2 */
 2781               "      5 copy 5 copy 25 15 roll\n");
 2782 
 2783   /* at his point, stack = (V3) (V13) (V13) (V13) (V2) (V1) */
 2784 
 2785   gl2psPrintf("      9 index 30 index add 0.5 mul\n" /* x23 = (x2+x3)/2 */
 2786               "      9 index 30 index add 0.5 mul\n" /* y23 = (y2+y3)/2 */
 2787               "      9 index 30 index add 0.5 mul\n" /* r23 = (r2+r3)/2 */
 2788               "      9 index 30 index add 0.5 mul\n" /* g23 = (g2+g3)/2 */
 2789               "      9 index 30 index add 0.5 mul\n" /* b23 = (b2+b3)/2 */
 2790               "      5 copy 5 copy 35 5 roll 25 5 roll 15 5 roll\n");
 2791 
 2792   /* stack = (V3) (V13) (V23) (V13) (V23) (V13) (V23) (V2) (V1) */
 2793 
 2794   gl2psPrintf("      4 index 10 index add 0.5 mul\n" /* x12 = (x1+x2)/2 */
 2795               "      4 index 10 index add 0.5 mul\n" /* y12 = (y1+y2)/2 */
 2796               "      4 index 10 index add 0.5 mul\n" /* r12 = (r1+r2)/2 */
 2797               "      4 index 10 index add 0.5 mul\n" /* g12 = (g1+g2)/2 */
 2798               "      4 index 10 index add 0.5 mul\n" /* b12 = (b1+b2)/2 */
 2799               "      5 copy 5 copy 40 5 roll 25 5 roll 15 5 roll 25 5 roll\n");
 2800 
 2801   /* stack = (V3) (V13) (V23) (V13) (V12) (V23) (V13) (V1) (V12) (V23) (V12) (V2) */
 2802 
 2803   gl2psPrintf("      STnoshfill STnoshfill STnoshfill STnoshfill } BD\n");
 2804 
 2805   /* Gouraud shaded triangle using recursive subdivision until the difference
 2806      between corner colors does not exceed the thresholds:
 2807         x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 STnoshfill  */
 2808 
 2809   gl2psPrintf("/STnoshfill {\n"
 2810               "      2 index 8 index sub abs rThreshold gt\n" /* |r1-r2|>rth */
 2811               "      { STsplit }\n"
 2812               "      { 1 index 7 index sub abs gThreshold gt\n" /* |g1-g2|>gth */
 2813               "        { STsplit }\n"
 2814               "        { dup 6 index sub abs bThreshold gt\n" /* |b1-b2|>bth */
 2815               "          { STsplit }\n"
 2816               "          { 2 index 13 index sub abs rThreshold gt\n" /* |r1-r3|>rht */
 2817               "            { STsplit }\n"
 2818               "            { 1 index 12 index sub abs gThreshold gt\n" /* |g1-g3|>gth */
 2819               "              { STsplit }\n"
 2820               "              { dup 11 index sub abs bThreshold gt\n" /* |b1-b3|>bth */
 2821               "                { STsplit }\n"
 2822               "                { 7 index 13 index sub abs rThreshold gt\n"); /* |r2-r3|>rht */
 2823   gl2psPrintf("                  { STsplit }\n"
 2824               "                  { 6 index 12 index sub abs gThreshold gt\n" /* |g2-g3|>gth */
 2825               "                    { STsplit }\n"
 2826               "                    { 5 index 11 index sub abs bThreshold gt\n" /* |b2-b3|>bth */
 2827               "                      { STsplit }\n"
 2828               "                      { Tm }\n" /* all colors sufficiently similar */
 2829               "                      ifelse }\n"
 2830               "                    ifelse }\n"
 2831               "                  ifelse }\n"
 2832               "                ifelse }\n"
 2833               "              ifelse }\n"
 2834               "            ifelse }\n"
 2835               "          ifelse }\n"
 2836               "        ifelse }\n"
 2837               "      ifelse } BD\n");
 2838 
 2839   gl2psPrintf("tryPS3shading\n"
 2840               "{ /shfill where\n"
 2841               "  { /ST { STshfill } BD }\n"
 2842               "  { /ST { STnoshfill } BD }\n"
 2843               "  ifelse }\n"
 2844               "{ /ST { STnoshfill } BD }\n"
 2845               "ifelse\n");
 2846 
 2847   gl2psPrintf("end\n"
 2848               "%%%%EndProlog\n"
 2849               "%%%%BeginSetup\n"
 2850               "/DeviceRGB setcolorspace\n"
 2851               "gl2psdict begin\n"
 2852               "%%%%EndSetup\n"
 2853               "%%%%Page: 1 1\n"
 2854               "%%%%BeginPageSetup\n");
 2855 
 2856   if(gl2ps->options & GL2PS_LANDSCAPE){
 2857     gl2psPrintf("%d 0 translate 90 rotate\n",
 2858                 (int)gl2ps->viewport[3]);
 2859   }
 2860 
 2861   gl2psPrintf("%%%%EndPageSetup\n"
 2862               "mark\n"
 2863               "gsave\n"
 2864               "1.0 1.0 scale\n");
 2865 
 2866   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
 2867     gl2psPrintf("%g %g %g C\n"
 2868                 "newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto\n"
 2869                 "closepath fill\n",
 2870                 gl2ps->bgcolor[0], gl2ps->bgcolor[1], gl2ps->bgcolor[2],
 2871                 (int)gl2ps->viewport[0], (int)gl2ps->viewport[1], (int)gl2ps->viewport[2],
 2872                 (int)gl2ps->viewport[1], (int)gl2ps->viewport[2], (int)gl2ps->viewport[3],
 2873                 (int)gl2ps->viewport[0], (int)gl2ps->viewport[3]);
 2874   }
 2875 }
 2876 
 2877 static void gl2psPrintPostScriptColor(GL2PSrgba rgba)
 2878 {
 2879   if(!gl2psSameColor(gl2ps->lastrgba, rgba)){
 2880     gl2psSetLastColor(rgba);
 2881     gl2psPrintf("%g %g %g C\n", rgba[0], rgba[1], rgba[2]);
 2882   }
 2883 }
 2884 
 2885 static void gl2psResetPostScriptColor(void)
 2886 {
 2887   gl2ps->lastrgba[0] = gl2ps->lastrgba[1] = gl2ps->lastrgba[2] = -1.;
 2888 }
 2889 
 2890 static void gl2psEndPostScriptLine(void)
 2891 {
 2892   int i;
 2893   if(gl2ps->lastvertex.rgba[0] >= 0.){
 2894     gl2psPrintf("%g %g LE\n", gl2ps->lastvertex.xyz[0], gl2ps->lastvertex.xyz[1]);
 2895     for(i = 0; i < 3; i++)
 2896       gl2ps->lastvertex.xyz[i] = -1.;
 2897     for(i = 0; i < 4; i++)
 2898       gl2ps->lastvertex.rgba[i] = -1.;
 2899   }
 2900 }
 2901 
 2902 static void gl2psParseStipplePattern(GLushort pattern, GLint factor,
 2903                                      int *nb, int array[10])
 2904 {
 2905   int i, n;
 2906   int on[8] = {0, 0, 0, 0, 0, 0, 0, 0};
 2907   int off[8] = {0, 0, 0, 0, 0, 0, 0, 0};
 2908   char tmp[16];
 2909 
 2910   /* extract the 16 bits from the OpenGL stipple pattern */
 2911   for(n = 15; n >= 0; n--){
 2912     tmp[n] = (char)(pattern & 0x01);
 2913     pattern >>= 1;
 2914   }
 2915   /* compute the on/off pixel sequence */
 2916   n = 0;
 2917   for(i = 0; i < 8; i++){
 2918     while(n < 16 && !tmp[n]){ off[i]++; n++; }
 2919     while(n < 16 && tmp[n]){ on[i]++; n++; }
 2920     if(n >= 15){ i++; break; }
 2921   }
 2922 
 2923   /* store the on/off array from right to left, starting with off
 2924      pixels. The PostScript specification allows for at most 11
 2925      elements in the on/off array, so we limit ourselves to 5 on/off
 2926      couples (our longest possible array is thus [on4 off4 on3 off3
 2927      on2 off2 on1 off1 on0 off0]) */
 2928   *nb = 0;
 2929   for(n = i - 1; n >= 0; n--){
 2930     array[(*nb)++] = factor * on[n];
 2931     array[(*nb)++] = factor * off[n];
 2932     if(*nb == 10) break;
 2933   }
 2934 }
 2935 
 2936 static int gl2psPrintPostScriptDash(GLushort pattern, GLint factor, const char *str)
 2937 {
 2938   int len = 0, i, n, array[10];
 2939 
 2940   if(pattern == gl2ps->lastpattern && factor == gl2ps->lastfactor)
 2941     return 0;
 2942 
 2943   gl2ps->lastpattern = pattern;
 2944   gl2ps->lastfactor = factor;
 2945 
 2946   if(!pattern || !factor){
 2947     /* solid line */
 2948     len += gl2psPrintf("[] 0 %s\n", str);
 2949   }
 2950   else{
 2951     gl2psParseStipplePattern(pattern, factor, &n, array);
 2952     len += gl2psPrintf("[");
 2953     for(i = 0; i < n; i++){
 2954       if(i) len += gl2psPrintf(" ");
 2955       len += gl2psPrintf("%d", array[i]);
 2956     }
 2957     len += gl2psPrintf("] 0 %s\n", str);
 2958   }
 2959 
 2960   return len;
 2961 }
 2962 
 2963 static void gl2psPrintPostScriptPrimitive(void *data)
 2964 {
 2965   int newline;
 2966   GL2PSprimitive *prim;
 2967 
 2968   prim = *(GL2PSprimitive**)data;
 2969 
 2970   if((gl2ps->options & GL2PS_OCCLUSION_CULL) && prim->culled) return;
 2971 
 2972   /* Every effort is made to draw lines as connected segments (i.e.,
 2973      using a single PostScript path): this is the only way to get nice
 2974      line joins and to not restart the stippling for every line
 2975      segment. So if the primitive to print is not a line we must first
 2976      finish the current line (if any): */
 2977   if(prim->type != GL2PS_LINE) gl2psEndPostScriptLine();
 2978 
 2979   switch(prim->type){
 2980   case GL2PS_POINT :
 2981     gl2psPrintPostScriptColor(prim->verts[0].rgba);
 2982     gl2psPrintf("%g %g %g P\n",
 2983                 prim->verts[0].xyz[0], prim->verts[0].xyz[1], 0.5 * prim->width);
 2984     break;
 2985   case GL2PS_LINE :
 2986     if(!gl2psSamePosition(gl2ps->lastvertex.xyz, prim->verts[0].xyz) ||
 2987        !gl2psSameColor(gl2ps->lastrgba, prim->verts[0].rgba) ||
 2988        gl2ps->lastlinewidth != prim->width ||
 2989        gl2ps->lastpattern != prim->pattern ||
 2990        gl2ps->lastfactor != prim->factor){
 2991       /* End the current line if the new segment does not start where
 2992          the last one ended, or if the color, the width or the
 2993          stippling have changed (multi-stroking lines with changing
 2994          colors is necessary until we use /shfill for lines;
 2995          unfortunately this means that at the moment we can screw up
 2996          line stippling for smooth-shaded lines) */
 2997       gl2psEndPostScriptLine();
 2998       newline = 1;
 2999     }
 3000     else{
 3001       newline = 0;
 3002     }
 3003     if(gl2ps->lastlinewidth != prim->width){
 3004       gl2ps->lastlinewidth = prim->width;
 3005       gl2psPrintf("%g W\n", gl2ps->lastlinewidth);
 3006     }
 3007     gl2psPrintPostScriptDash(prim->pattern, prim->factor, "setdash");
 3008     gl2psPrintPostScriptColor(prim->verts[0].rgba);
 3009     gl2psPrintf("%g %g %s\n", prim->verts[0].xyz[0], prim->verts[0].xyz[1],
 3010                 newline ? "LS" : "L");
 3011     gl2ps->lastvertex = prim->verts[1];
 3012     break;
 3013   case GL2PS_TRIANGLE :
 3014     if(!gl2psVertsSameColor(prim)){
 3015       gl2psResetPostScriptColor();
 3016       gl2psPrintf("%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g ST\n",
 3017                   prim->verts[2].xyz[0], prim->verts[2].xyz[1],
 3018                   prim->verts[2].rgba[0], prim->verts[2].rgba[1],
 3019                   prim->verts[2].rgba[2], prim->verts[1].xyz[0],
 3020                   prim->verts[1].xyz[1], prim->verts[1].rgba[0],
 3021                   prim->verts[1].rgba[1], prim->verts[1].rgba[2],
 3022                   prim->verts[0].xyz[0], prim->verts[0].xyz[1],
 3023                   prim->verts[0].rgba[0], prim->verts[0].rgba[1],
 3024                   prim->verts[0].rgba[2]);
 3025     }
 3026     else{
 3027       gl2psPrintPostScriptColor(prim->verts[0].rgba);
 3028       gl2psPrintf("%g %g %g %g %g %g T\n",
 3029                   prim->verts[2].xyz[0], prim->verts[2].xyz[1],
 3030                   prim->verts[1].xyz[0], prim->verts[1].xyz[1],
 3031                   prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
 3032     }
 3033     break;
 3034   case GL2PS_QUADRANGLE :
 3035     gl2psMsg(GL2PS_WARNING, "There should not be any quad left to print");
 3036     break;
 3037   case GL2PS_PIXMAP :
 3038     gl2psPrintPostScriptPixmap(prim->verts[0].xyz[0], prim->verts[0].xyz[1],
 3039                                prim->data.image);
 3040     break;
 3041   case GL2PS_IMAGEMAP :
 3042     if(prim->data.image->type != GL2PS_IMAGEMAP_WRITTEN){
 3043       gl2psPrintPostScriptColor(prim->verts[0].rgba);
 3044       gl2psPrintPostScriptImagemap(prim->data.image->pixels[0],
 3045                                    prim->data.image->pixels[1],
 3046                                    prim->data.image->width, prim->data.image->height,
 3047                                    (const unsigned char*)(&(prim->data.image->pixels[2])));
 3048       prim->data.image->type = GL2PS_IMAGEMAP_WRITTEN;
 3049     }
 3050     break;
 3051   case GL2PS_TEXT :
 3052     gl2psPrintPostScriptColor(prim->verts[0].rgba);
 3053     gl2psPrintf("(%s) ", prim->data.text->str);
 3054     if(prim->data.text->angle)
 3055       gl2psPrintf("%g ", prim->data.text->angle);
 3056     gl2psPrintf("%g %g %d /%s ",
 3057                 prim->verts[0].xyz[0], prim->verts[0].xyz[1],
 3058                 prim->data.text->fontsize, prim->data.text->fontname);
 3059     switch(prim->data.text->alignment){
 3060     case GL2PS_TEXT_C:
 3061       gl2psPrintf(prim->data.text->angle ? "SCCR\n" : "SCC\n");
 3062       break;
 3063     case GL2PS_TEXT_CL:
 3064       gl2psPrintf(prim->data.text->angle ? "SCLR\n" : "SCL\n");
 3065       break;
 3066     case GL2PS_TEXT_CR:
 3067       gl2psPrintf(prim->data.text->angle ? "SCRR\n" : "SCR\n");
 3068       break;
 3069     case GL2PS_TEXT_B:
 3070       gl2psPrintf(prim->data.text->angle ? "SBCR\n" : "SBC\n");
 3071       break;
 3072     case GL2PS_TEXT_BR:
 3073       gl2psPrintf(prim->data.text->angle ? "SBRR\n" : "SBR\n");
 3074       break;
 3075     case GL2PS_TEXT_T:
 3076       gl2psPrintf(prim->data.text->angle ? "STCR\n" : "STC\n");
 3077       break;
 3078     case GL2PS_TEXT_TL:
 3079       gl2psPrintf(prim->data.text->angle ? "STLR\n" : "STL\n");
 3080       break;
 3081     case GL2PS_TEXT_TR:
 3082       gl2psPrintf(prim->data.text->angle ? "STRR\n" : "STR\n");
 3083       break;
 3084     case GL2PS_TEXT_BL:
 3085     default:
 3086       gl2psPrintf(prim->data.text->angle ? "SR\n" : "S\n");
 3087       break;
 3088     }
 3089     break;
 3090   case GL2PS_SPECIAL :
 3091     /* alignment contains the format for which the special output text
 3092        is intended */
 3093     if(prim->data.text->alignment == GL2PS_PS ||
 3094        prim->data.text->alignment == GL2PS_EPS)
 3095       gl2psPrintf("%s\n", prim->data.text->str);
 3096     break;
 3097   default :
 3098     break;
 3099   }
 3100 }
 3101 
 3102 static void gl2psPrintPostScriptFooter(void)
 3103 {
 3104   gl2psPrintf("grestore\n"
 3105               "showpage\n"
 3106               "cleartomark\n"
 3107               "%%%%PageTrailer\n"
 3108               "%%%%Trailer\n"
 3109               "end\n"
 3110               "%%%%EOF\n");
 3111 
 3112   gl2psPrintGzipFooter();
 3113 }
 3114 
 3115 static void gl2psPrintPostScriptBeginViewport(GLint viewport[4])
 3116 {
 3117   GLint index;
 3118   GLfloat rgba[4];
 3119   int x = viewport[0], y = viewport[1], w = viewport[2], h = viewport[3];
 3120 
 3121   glRenderMode(GL_FEEDBACK);
 3122 
 3123   if(gl2ps->header){
 3124     gl2psPrintPostScriptHeader();
 3125     gl2ps->header = GL_FALSE;
 3126   }
 3127 
 3128   gl2psPrintf("gsave\n"
 3129               "1.0 1.0 scale\n");
 3130 
 3131   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
 3132     if(gl2ps->colormode == GL_RGBA || gl2ps->colorsize == 0){
 3133       glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba);
 3134     }
 3135     else{
 3136       glGetIntegerv(GL_INDEX_CLEAR_VALUE, &index);
 3137       rgba[0] = gl2ps->colormap[index][0];
 3138       rgba[1] = gl2ps->colormap[index][1];
 3139       rgba[2] = gl2ps->colormap[index][2];
 3140       rgba[3] = 1.0F;
 3141     }
 3142     gl2psPrintf("%g %g %g C\n"
 3143                 "newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto\n"
 3144                 "closepath fill\n",
 3145                 rgba[0], rgba[1], rgba[2],
 3146                 x, y, x+w, y, x+w, y+h, x, y+h);
 3147   }
 3148 
 3149   gl2psPrintf("newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto\n"
 3150               "closepath clip\n",
 3151               x, y, x+w, y, x+w, y+h, x, y+h);
 3152 
 3153 }
 3154 
 3155 static GLint gl2psPrintPostScriptEndViewport(void)
 3156 {
 3157   GLint res;
 3158 
 3159   res = gl2psPrintPrimitives();
 3160   gl2psPrintf("grestore\n");
 3161   return res;
 3162 }
 3163 
 3164 static void gl2psPrintPostScriptFinalPrimitive(void)
 3165 {
 3166   /* End any remaining line, if any */
 3167   gl2psEndPostScriptLine();
 3168 }
 3169 
 3170 /* definition of the PostScript and Encapsulated PostScript backends */
 3171 
 3172 static GL2PSbackend gl2psPS = {
 3173   gl2psPrintPostScriptHeader,
 3174   gl2psPrintPostScriptFooter,
 3175   gl2psPrintPostScriptBeginViewport,
 3176   gl2psPrintPostScriptEndViewport,
 3177   gl2psPrintPostScriptPrimitive,
 3178   gl2psPrintPostScriptFinalPrimitive,
 3179   "ps",
 3180   "Postscript"
 3181 };
 3182 
 3183 static GL2PSbackend gl2psEPS = {
 3184   gl2psPrintPostScriptHeader,
 3185   gl2psPrintPostScriptFooter,
 3186   gl2psPrintPostScriptBeginViewport,
 3187   gl2psPrintPostScriptEndViewport,
 3188   gl2psPrintPostScriptPrimitive,
 3189   gl2psPrintPostScriptFinalPrimitive,
 3190   "eps",
 3191   "Encapsulated Postscript"
 3192 };
 3193 
 3194 /*********************************************************************
 3195  *
 3196  * LaTeX routines
 3197  *
 3198  *********************************************************************/
 3199 
 3200 static void gl2psPrintTeXHeader(void)
 3201 {
 3202   char name[256];
 3203   time_t now;
 3204   int i;
 3205 
 3206   if(gl2ps->filename && strlen(gl2ps->filename) < 256){
 3207     for(i = (int)strlen(gl2ps->filename) - 1; i >= 0; i--){
 3208       if(gl2ps->filename[i] == '.'){
 3209         strncpy(name, gl2ps->filename, i);
 3210         name[i] = '\0';
 3211         break;
 3212       }
 3213     }
 3214     if(i <= 0) strcpy(name, gl2ps->filename);
 3215   }
 3216   else{
 3217     strcpy(name, "untitled");
 3218   }
 3219 
 3220   time(&now);
 3221 
 3222   fprintf(gl2ps->stream,
 3223           "%% Title: %s\n"
 3224           "%% Creator: GL2PS %d.%d.%d%s, %s\n"
 3225           "%% For: %s\n"
 3226           "%% CreationDate: %s",
 3227           gl2ps->title, GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION,
 3228           GL2PS_PATCH_VERSION, GL2PS_EXTRA_VERSION, GL2PS_COPYRIGHT,
 3229           gl2ps->producer, ctime(&now));
 3230 
 3231   fprintf(gl2ps->stream,
 3232           "\\setlength{\\unitlength}{1pt}\n"
 3233           "\\begin{picture}(0,0)\n"
 3234           "\\includegraphics{%s}\n"
 3235           "\\end{picture}%%\n"
 3236           "%s\\begin{picture}(%d,%d)(0,0)\n",
 3237           name, (gl2ps->options & GL2PS_LANDSCAPE) ? "\\rotatebox{90}{" : "",
 3238           (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
 3239 }
 3240 
 3241 static void gl2psPrintTeXPrimitive(void *data)
 3242 {
 3243   GL2PSprimitive *prim;
 3244 
 3245   prim = *(GL2PSprimitive**)data;
 3246 
 3247   switch(prim->type){
 3248   case GL2PS_TEXT :
 3249     fprintf(gl2ps->stream, "\\fontsize{%d}{0}\n\\selectfont",
 3250             prim->data.text->fontsize);
 3251     fprintf(gl2ps->stream, "\\put(%g,%g)",
 3252             prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
 3253     if(prim->data.text->angle)
 3254       fprintf(gl2ps->stream, "{\\rotatebox{%g}", prim->data.text->angle);
 3255     fprintf(gl2ps->stream, "{\\makebox(0,0)");
 3256     switch(prim->data.text->alignment){
 3257     case GL2PS_TEXT_C:
 3258       fprintf(gl2ps->stream, "{");
 3259       break;
 3260     case GL2PS_TEXT_CL:
 3261       fprintf(gl2ps->stream, "[l]{");
 3262       break;
 3263     case GL2PS_TEXT_CR:
 3264       fprintf(gl2ps->stream, "[r]{");
 3265       break;
 3266     case GL2PS_TEXT_B:
 3267       fprintf(gl2ps->stream, "[b]{");
 3268       break;
 3269     case GL2PS_TEXT_BR:
 3270       fprintf(gl2ps->stream, "[br]{");
 3271       break;
 3272     case GL2PS_TEXT_T:
 3273       fprintf(gl2ps->stream, "[t]{");
 3274       break;
 3275     case GL2PS_TEXT_TL:
 3276       fprintf(gl2ps->stream, "[tl]{");
 3277       break;
 3278     case GL2PS_TEXT_TR:
 3279       fprintf(gl2ps->stream, "[tr]{");
 3280       break;
 3281     case GL2PS_TEXT_BL:
 3282     default:
 3283       fprintf(gl2ps->stream, "[bl]{");
 3284       break;
 3285     }
 3286     fprintf(gl2ps->stream, "\\textcolor[rgb]{%g,%g,%g}{{%s}}",
 3287             prim->verts[0].rgba[0], prim->verts[0].rgba[1], prim->verts[0].rgba[2],
 3288             prim->data.text->str);
 3289     if(prim->data.text->angle)
 3290       fprintf(gl2ps->stream, "}");
 3291     fprintf(gl2ps->stream, "}}\n");
 3292     break;
 3293   case GL2PS_SPECIAL :
 3294     /* alignment contains the format for which the special output text
 3295        is intended */
 3296     if (prim->data.text->alignment == GL2PS_TEX)
 3297       fprintf(gl2ps->stream, "%s\n", prim->data.text->str);
 3298     break;
 3299   default :
 3300     break;
 3301   }
 3302 }
 3303 
 3304 static void gl2psPrintTeXFooter(void)
 3305 {
 3306   fprintf(gl2ps->stream, "\\end{picture}%s\n",
 3307           (gl2ps->options & GL2PS_LANDSCAPE) ? "}" : "");
 3308 }
 3309 
 3310 static void gl2psPrintTeXBeginViewport(GLint viewport[4])
 3311 {
 3312   (void) viewport;  /* not used */
 3313   glRenderMode(GL_FEEDBACK);
 3314 
 3315   if(gl2ps->header){
 3316     gl2psPrintTeXHeader();
 3317     gl2ps->header = GL_FALSE;
 3318   }
 3319 }
 3320 
 3321 static GLint gl2psPrintTeXEndViewport(void)
 3322 {
 3323   return gl2psPrintPrimitives();
 3324 }
 3325 
 3326 static void gl2psPrintTeXFinalPrimitive(void)
 3327 {
 3328 }
 3329 
 3330 /* definition of the LaTeX backend */
 3331 
 3332 static GL2PSbackend gl2psTEX = {
 3333   gl2psPrintTeXHeader,
 3334   gl2psPrintTeXFooter,
 3335   gl2psPrintTeXBeginViewport,
 3336   gl2psPrintTeXEndViewport,
 3337   gl2psPrintTeXPrimitive,
 3338   gl2psPrintTeXFinalPrimitive,
 3339   "tex",
 3340   "LaTeX text"
 3341 };
 3342 
 3343 /*********************************************************************
 3344  *
 3345  * PDF routines
 3346  *
 3347  *********************************************************************/
 3348 
 3349 static int gl2psPrintPDFCompressorType(void)
 3350 {
 3351 #if defined(GL2PS_HAVE_ZLIB)
 3352   if(gl2ps->options & GL2PS_COMPRESS){
 3353     return fprintf(gl2ps->stream, "/Filter [/FlateDecode]\n");
 3354   }
 3355 #endif
 3356   return 0;
 3357 }
 3358 
 3359 static int gl2psPrintPDFStrokeColor(GL2PSrgba rgba)
 3360 {
 3361   int i, offs = 0;
 3362 
 3363   gl2psSetLastColor(rgba);
 3364   for(i = 0; i < 3; ++i){
 3365     if(GL2PS_ZERO(rgba[i]))
 3366       offs += gl2psPrintf("%.0f ", 0.);
 3367     else if(rgba[i] < 1e-4 || rgba[i] > 1e6) /* avoid %e formatting */
 3368       offs += gl2psPrintf("%f ", rgba[i]);
 3369     else
 3370       offs += gl2psPrintf("%g ", rgba[i]);
 3371   }
 3372   offs += gl2psPrintf("RG\n");
 3373   return offs;
 3374 }
 3375 
 3376 static int gl2psPrintPDFFillColor(GL2PSrgba rgba)
 3377 {
 3378   int i, offs = 0;
 3379 
 3380   for(i = 0; i < 3; ++i){
 3381     if(GL2PS_ZERO(rgba[i]))
 3382       offs += gl2psPrintf("%.0f ", 0.);
 3383     else if(rgba[i] < 1e-4 || rgba[i] > 1e6) /* avoid %e formatting */
 3384       offs += gl2psPrintf("%f ", rgba[i]);
 3385     else
 3386       offs += gl2psPrintf("%g ", rgba[i]);
 3387   }
 3388   offs += gl2psPrintf("rg\n");
 3389   return offs;
 3390 }
 3391 
 3392 static int gl2psPrintPDFLineWidth(GLfloat lw)
 3393 {
 3394   if(GL2PS_ZERO(lw))
 3395     return gl2psPrintf("%.0f w\n", 0.);
 3396   else if(lw < 1e-4 || lw > 1e6) /* avoid %e formatting */
 3397     return gl2psPrintf("%f w\n", lw);
 3398   else
 3399     return gl2psPrintf("%g w\n", lw);
 3400 }
 3401 
 3402 static void gl2psPutPDFText(GL2PSstring *text, int cnt, GLfloat x, GLfloat y)
 3403 {
 3404   GLfloat rad, crad, srad;
 3405 
 3406   if(text->angle == 0.0F){
 3407     gl2ps->streamlength += gl2psPrintf
 3408       ("BT\n"
 3409        "/F%d %d Tf\n"
 3410        "%f %f Td\n"
 3411        "(%s) Tj\n"
 3412        "ET\n",
 3413        cnt, text->fontsize, x, y, text->str);
 3414   }
 3415   else{
 3416     rad = (GLfloat)(M_PI * text->angle / 180.0F);
 3417     srad = (GLfloat)sin(rad);
 3418     crad = (GLfloat)cos(rad);
 3419     gl2ps->streamlength += gl2psPrintf
 3420       ("BT\n"
 3421        "/F%d %d Tf\n"
 3422        "%f %f %f %f %f %f Tm\n"
 3423        "(%s) Tj\n"
 3424        "ET\n",
 3425        cnt, text->fontsize, crad, srad, -srad, crad, x, y, text->str);
 3426   }
 3427 }
 3428 
 3429 static void gl2psPutPDFImage(GL2PSimage *image, int cnt, GLfloat x, GLfloat y)
 3430 {
 3431   gl2ps->streamlength += gl2psPrintf
 3432     ("q\n"
 3433      "%d 0 0 %d %f %f cm\n"
 3434      "/Im%d Do\n"
 3435      "Q\n",
 3436      (int)image->width, (int)image->height, x, y, cnt);
 3437 }
 3438 
 3439 static void gl2psPDFstacksInit(void)
 3440 {
 3441   gl2ps->objects_stack = 7 /* FIXED_XREF_ENTRIES */ + 1;
 3442   gl2ps->extgs_stack = 0;
 3443   gl2ps->font_stack = 0;
 3444   gl2ps->im_stack = 0;
 3445   gl2ps->trgroupobjects_stack = 0;
 3446   gl2ps->shader_stack = 0;
 3447   gl2ps->mshader_stack = 0;
 3448 }
 3449 
 3450 static void gl2psPDFgroupObjectInit(GL2PSpdfgroup *gro)
 3451 {
 3452   if(!gro)
 3453     return;
 3454 
 3455   gro->ptrlist = NULL;
 3456   gro->fontno = gro->gsno = gro->imno = gro->maskshno = gro->shno
 3457     = gro->trgroupno = gro->fontobjno = gro->imobjno = gro->shobjno
 3458     = gro->maskshobjno = gro->gsobjno = gro->trgroupobjno = -1;
 3459 }
 3460 
 3461 /* Build up group objects and assign name and object numbers */
 3462 
 3463 static void gl2psPDFgroupListInit(void)
 3464 {
 3465   int i;
 3466   GL2PSprimitive *p = NULL;
 3467   GL2PSpdfgroup gro;
 3468   int lasttype = GL2PS_NO_TYPE;
 3469   GL2PSrgba lastrgba = {-1.0F, -1.0F, -1.0F, -1.0F};
 3470   GLushort lastpattern = 0;
 3471   GLint lastfactor = 0;
 3472   GLfloat lastwidth = 1;
 3473   GL2PStriangle lastt, tmpt;
 3474   int lastTriangleWasNotSimpleWithSameColor = 0;
 3475 
 3476   if(!gl2ps->pdfprimlist)
 3477     return;
 3478 
 3479   gl2ps->pdfgrouplist = gl2psListCreate(500, 500, sizeof(GL2PSpdfgroup));
 3480   gl2psInitTriangle(&lastt);
 3481 
 3482   for(i = 0; i < gl2psListNbr(gl2ps->pdfprimlist); ++i){
 3483     p = *(GL2PSprimitive**)gl2psListPointer(gl2ps->pdfprimlist, i);
 3484     switch(p->type){
 3485     case GL2PS_PIXMAP:
 3486       gl2psPDFgroupObjectInit(&gro);
 3487       gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
 3488       gro.imno = gl2ps->im_stack++;
 3489       gl2psListAdd(gro.ptrlist, &p);
 3490       gl2psListAdd(gl2ps->pdfgrouplist, &gro);
 3491       break;
 3492     case GL2PS_TEXT:
 3493       gl2psPDFgroupObjectInit(&gro);
 3494       gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
 3495       gro.fontno = gl2ps->font_stack++;
 3496       gl2psListAdd(gro.ptrlist, &p);
 3497       gl2psListAdd(gl2ps->pdfgrouplist, &gro);
 3498       break;
 3499     case GL2PS_LINE:
 3500       if(lasttype != p->type || lastwidth != p->width ||
 3501          lastpattern != p->pattern || lastfactor != p->factor ||
 3502          !gl2psSameColor(p->verts[0].rgba, lastrgba)){
 3503         gl2psPDFgroupObjectInit(&gro);
 3504         gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
 3505         gl2psListAdd(gro.ptrlist, &p);
 3506         gl2psListAdd(gl2ps->pdfgrouplist, &gro);
 3507       }
 3508       else{
 3509         gl2psListAdd(gro.ptrlist, &p);
 3510       }
 3511       lastpattern = p->pattern;
 3512       lastfactor = p->factor;
 3513       lastwidth = p->width;
 3514       lastrgba[0] = p->verts[0].rgba[0];
 3515       lastrgba[1] = p->verts[0].rgba[1];
 3516       lastrgba[2] = p->verts[0].rgba[2];
 3517       break;
 3518     case GL2PS_POINT:
 3519       if(lasttype != p->type || lastwidth != p->width ||
 3520          !gl2psSameColor(p->verts[0].rgba, lastrgba)){
 3521         gl2psPDFgroupObjectInit(&gro);
 3522         gro.ptrlist = gl2psListCreate(1,2,sizeof(GL2PSprimitive*));
 3523         gl2psListAdd(gro.ptrlist, &p);
 3524         gl2psListAdd(gl2ps->pdfgrouplist, &gro);
 3525       }
 3526       else{
 3527         gl2psListAdd(gro.ptrlist, &p);
 3528       }
 3529       lastwidth = p->width;
 3530       lastrgba[0] = p->verts[0].rgba[0];
 3531       lastrgba[1] = p->verts[0].rgba[1];
 3532       lastrgba[2] = p->verts[0].rgba[2];
 3533       break;
 3534     case GL2PS_TRIANGLE:
 3535       gl2psFillTriangleFromPrimitive(&tmpt, p, GL_TRUE);
 3536       lastTriangleWasNotSimpleWithSameColor =
 3537         !(tmpt.prop & T_CONST_COLOR && tmpt.prop & T_ALPHA_1) ||
 3538         !gl2psSameColor(tmpt.vertex[0].rgba, lastt.vertex[0].rgba);
 3539       if(lasttype == p->type && tmpt.prop == lastt.prop &&
 3540          lastTriangleWasNotSimpleWithSameColor){
 3541         /* TODO Check here for last alpha */
 3542         gl2psListAdd(gro.ptrlist, &p);
 3543       }
 3544       else{
 3545         gl2psPDFgroupObjectInit(&gro);
 3546         gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
 3547         gl2psListAdd(gro.ptrlist, &p);
 3548         gl2psListAdd(gl2ps->pdfgrouplist, &gro);
 3549       }
 3550       lastt = tmpt;
 3551       break;
 3552     default:
 3553       break;
 3554     }
 3555     lasttype = p->type;
 3556   }
 3557 }
 3558 
 3559 static void gl2psSortOutTrianglePDFgroup(GL2PSpdfgroup *gro)
 3560 {
 3561   GL2PStriangle t;
 3562   GL2PSprimitive *prim = NULL;
 3563 
 3564   if(!gro)
 3565     return;
 3566 
 3567   if(!gl2psListNbr(gro->ptrlist))
 3568     return;
 3569 
 3570   prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);
 3571 
 3572   if(prim->type != GL2PS_TRIANGLE)
 3573     return;
 3574 
 3575   gl2psFillTriangleFromPrimitive(&t, prim, GL_TRUE);
 3576 
 3577   if(t.prop & T_CONST_COLOR && t.prop & T_ALPHA_LESS_1){
 3578     gro->gsno = gl2ps->extgs_stack++;
 3579     gro->gsobjno = gl2ps->objects_stack ++;
 3580   }
 3581   else if(t.prop & T_CONST_COLOR && t.prop & T_VAR_ALPHA){
 3582     gro->gsno = gl2ps->extgs_stack++;
 3583     gro->gsobjno = gl2ps->objects_stack++;
 3584     gro->trgroupno = gl2ps->trgroupobjects_stack++;
 3585     gro->trgroupobjno = gl2ps->objects_stack++;
 3586     gro->maskshno = gl2ps->mshader_stack++;
 3587     gro->maskshobjno = gl2ps->objects_stack++;
 3588   }
 3589   else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_1){
 3590     gro->shno = gl2ps->shader_stack++;
 3591     gro->shobjno = gl2ps->objects_stack++;
 3592   }
 3593   else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_LESS_1){
 3594     gro->gsno = gl2ps->extgs_stack++;
 3595     gro->gsobjno = gl2ps->objects_stack++;
 3596     gro->shno = gl2ps->shader_stack++;
 3597     gro->shobjno = gl2ps->objects_stack++;
 3598   }
 3599   else if(t.prop & T_VAR_COLOR && t.prop & T_VAR_ALPHA){
 3600     gro->gsno = gl2ps->extgs_stack++;
 3601     gro->gsobjno = gl2ps->objects_stack++;
 3602     gro->shno = gl2ps->shader_stack++;
 3603     gro->shobjno = gl2ps->objects_stack++;
 3604     gro->trgroupno = gl2ps->trgroupobjects_stack++;
 3605     gro->trgroupobjno = gl2ps->objects_stack++;
 3606     gro->maskshno = gl2ps->mshader_stack++;
 3607     gro->maskshobjno = gl2ps->objects_stack++;
 3608   }
 3609 }
 3610 
 3611 /* Main stream data */
 3612 
 3613 static void gl2psPDFgroupListWriteMainStream(void)
 3614 {
 3615   int i, j, lastel;
 3616   GL2PSprimitive *prim = NULL, *prev = NULL;
 3617   GL2PSpdfgroup *gro;
 3618   GL2PStriangle t;
 3619 
 3620   if(!gl2ps->pdfgrouplist)
 3621     return;
 3622 
 3623   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
 3624     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
 3625 
 3626     lastel = gl2psListNbr(gro->ptrlist) - 1;
 3627     if(lastel < 0)
 3628       continue;
 3629 
 3630     prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);
 3631 
 3632     switch(prim->type){
 3633     case GL2PS_POINT:
 3634       gl2ps->streamlength += gl2psPrintf("1 J\n");
 3635       gl2ps->streamlength += gl2psPrintPDFLineWidth(prim->width);
 3636       gl2ps->streamlength += gl2psPrintPDFStrokeColor(prim->verts[0].rgba);
 3637       for(j = 0; j <= lastel; ++j){
 3638         prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
 3639         gl2ps->streamlength +=
 3640           gl2psPrintf("%f %f m %f %f l\n",
 3641                       prim->verts[0].xyz[0], prim->verts[0].xyz[1],
 3642                       prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
 3643       }
 3644       gl2ps->streamlength += gl2psPrintf("S\n");
 3645       gl2ps->streamlength += gl2psPrintf("0 J\n");
 3646       break;
 3647     case GL2PS_LINE:
 3648       /* We try to use as few paths as possible to draw lines, in
 3649          order to get nice stippling even when the individual segments
 3650          are smaller than the stipple */
 3651       gl2ps->streamlength += gl2psPrintPDFLineWidth(prim->width);
 3652       gl2ps->streamlength += gl2psPrintPDFStrokeColor(prim->verts[0].rgba);
 3653       gl2ps->streamlength += gl2psPrintPostScriptDash(prim->pattern, prim->factor, "d");
 3654       /* start new path */
 3655       gl2ps->streamlength +=
 3656         gl2psPrintf("%f %f m\n",
 3657                     prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
 3658 
 3659       for(j = 1; j <= lastel; ++j){
 3660         prev = prim;
 3661         prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
 3662         if(!gl2psSamePosition(prim->verts[0].xyz, prev->verts[1].xyz)){
 3663           /* the starting point of the new segment does not match the
 3664              end point of the previous line, so we end the current
 3665              path and start a new one */
 3666           gl2ps->streamlength +=
 3667             gl2psPrintf("%f %f l\n",
 3668                         prev->verts[1].xyz[0], prev->verts[1].xyz[1]);
 3669           gl2ps->streamlength +=
 3670             gl2psPrintf("%f %f m\n",
 3671                         prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
 3672         }
 3673         else{
 3674           /* the two segements are connected, so we just append to the
 3675              current path */
 3676           gl2ps->streamlength +=
 3677             gl2psPrintf("%f %f l\n",
 3678                         prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
 3679         }
 3680       }
 3681       /* end last path */
 3682       gl2ps->streamlength +=
 3683         gl2psPrintf("%f %f l\n",
 3684                     prim->verts[1].xyz[0], prim->verts[1].xyz[1]);
 3685       gl2ps->streamlength += gl2psPrintf("S\n");
 3686       break;
 3687     case GL2PS_TRIANGLE:
 3688       gl2psFillTriangleFromPrimitive(&t, prim, GL_TRUE);
 3689       gl2psSortOutTrianglePDFgroup(gro);
 3690 
 3691       /* No alpha and const color: Simple PDF draw orders  */
 3692       if(t.prop & T_CONST_COLOR && t.prop & T_ALPHA_1){
 3693         gl2ps->streamlength += gl2psPrintPDFFillColor(t.vertex[0].rgba);
 3694         for(j = 0; j <= lastel; ++j){
 3695           prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
 3696           gl2psFillTriangleFromPrimitive(&t, prim, GL_FALSE);
 3697           gl2ps->streamlength
 3698             += gl2psPrintf("%f %f m\n"
 3699                            "%f %f l\n"
 3700                            "%f %f l\n"
 3701                            "h f\n",
 3702                            t.vertex[0].xyz[0], t.vertex[0].xyz[1],
 3703                            t.vertex[1].xyz[0], t.vertex[1].xyz[1],
 3704                            t.vertex[2].xyz[0], t.vertex[2].xyz[1]);
 3705         }
 3706       }
 3707       /* Const alpha < 1 and const color: Simple PDF draw orders
 3708          and an extra extended Graphics State for the alpha const */
 3709       else if(t.prop & T_CONST_COLOR && t.prop & T_ALPHA_LESS_1){
 3710         gl2ps->streamlength += gl2psPrintf("q\n"
 3711                                            "/GS%d gs\n",
 3712                                            gro->gsno);
 3713         gl2ps->streamlength += gl2psPrintPDFFillColor(prim->verts[0].rgba);
 3714         for(j = 0; j <= lastel; ++j){
 3715           prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
 3716           gl2psFillTriangleFromPrimitive(&t, prim, GL_FALSE);
 3717           gl2ps->streamlength
 3718             += gl2psPrintf("%f %f m\n"
 3719                            "%f %f l\n"
 3720                            "%f %f l\n"
 3721                            "h f\n",
 3722                            t.vertex[0].xyz[0], t.vertex[0].xyz[1],
 3723                            t.vertex[1].xyz[0], t.vertex[1].xyz[1],
 3724                            t.vertex[2].xyz[0], t.vertex[2].xyz[1]);
 3725         }
 3726         gl2ps->streamlength += gl2psPrintf("Q\n");
 3727       }
 3728       /* Variable alpha and const color: Simple PDF draw orders
 3729          and an extra extended Graphics State + Xobject + Shader
 3730          object for the alpha mask */
 3731       else if(t.prop & T_CONST_COLOR && t.prop & T_VAR_ALPHA){
 3732         gl2ps->streamlength += gl2psPrintf("q\n"
 3733                                            "/GS%d gs\n"
 3734                                            "/TrG%d Do\n",
 3735                                            gro->gsno, gro->trgroupno);
 3736         gl2ps->streamlength += gl2psPrintPDFFillColor(prim->verts[0].rgba);
 3737         for(j = 0; j <= lastel; ++j){
 3738           prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
 3739           gl2psFillTriangleFromPrimitive(&t, prim, GL_FALSE);
 3740           gl2ps->streamlength
 3741             += gl2psPrintf("%f %f m\n"
 3742                            "%f %f l\n"
 3743                            "%f %f l\n"
 3744                            "h f\n",
 3745                            t.vertex[0].xyz[0], t.vertex[0].xyz[1],
 3746                            t.vertex[1].xyz[0], t.vertex[1].xyz[1],
 3747                            t.vertex[2].xyz[0], t.vertex[2].xyz[1]);
 3748         }
 3749         gl2ps->streamlength += gl2psPrintf("Q\n");
 3750       }
 3751       /* Variable color and no alpha: Shader Object for the colored
 3752          triangle(s) */
 3753       else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_1){
 3754         gl2ps->streamlength += gl2psPrintf("/Sh%d sh\n", gro->shno);
 3755       }
 3756       /* Variable color and const alpha < 1: Shader Object for the
 3757          colored triangle(s) and an extra extended Graphics State
 3758          for the alpha const */
 3759       else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_LESS_1){
 3760         gl2ps->streamlength += gl2psPrintf("q\n"
 3761                                            "/GS%d gs\n"
 3762                                            "/Sh%d sh\n"
 3763                                            "Q\n",
 3764                                            gro->gsno, gro->shno);
 3765       }
 3766       /* Variable alpha and color: Shader Object for the colored
 3767          triangle(s) and an extra extended Graphics State
 3768          + Xobject + Shader object for the alpha mask */
 3769       else if(t.prop & T_VAR_COLOR && t.prop & T_VAR_ALPHA){
 3770         gl2ps->streamlength += gl2psPrintf("q\n"
 3771                                            "/GS%d gs\n"
 3772                                            "/TrG%d Do\n"
 3773                                            "/Sh%d sh\n"
 3774                                            "Q\n",
 3775                                            gro->gsno, gro->trgroupno, gro->shno);
 3776       }
 3777       break;
 3778     case GL2PS_PIXMAP:
 3779       for(j = 0; j <= lastel; ++j){
 3780         prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
 3781         gl2psPutPDFImage(prim->data.image, gro->imno, prim->verts[0].xyz[0],
 3782                          prim->verts[0].xyz[1]);
 3783       }
 3784       break;
 3785     case GL2PS_TEXT:
 3786       for(j = 0; j <= lastel; ++j){
 3787         prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
 3788         gl2ps->streamlength += gl2psPrintPDFFillColor(prim->verts[0].rgba);
 3789         gl2psPutPDFText(prim->data.text, gro->fontno, prim->verts[0].xyz[0],
 3790                         prim->verts[0].xyz[1]);
 3791       }
 3792       break;
 3793     default:
 3794       break;
 3795     }
 3796   }
 3797 }
 3798 
 3799 /* Graphics State names */
 3800 
 3801 static int gl2psPDFgroupListWriteGStateResources(void)
 3802 {
 3803   GL2PSpdfgroup *gro;
 3804   int offs = 0;
 3805   int i;
 3806 
 3807   offs += fprintf(gl2ps->stream,
 3808                   "/ExtGState\n"
 3809                   "<<\n"
 3810                   "/GSa 7 0 R\n");
 3811   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
 3812     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
 3813     if(gro->gsno >= 0)
 3814       offs += fprintf(gl2ps->stream, "/GS%d %d 0 R\n", gro->gsno, gro->gsobjno);
 3815   }
 3816   offs += fprintf(gl2ps->stream, ">>\n");
 3817   return offs;
 3818 }
 3819 
 3820 /* Main Shader names */
 3821 
 3822 static int gl2psPDFgroupListWriteShaderResources(void)
 3823 {
 3824   GL2PSpdfgroup *gro;
 3825   int offs = 0;
 3826   int i;
 3827 
 3828   offs += fprintf(gl2ps->stream,
 3829                   "/Shading\n"
 3830                   "<<\n");
 3831   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
 3832     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
 3833     if(gro->shno >= 0)
 3834       offs += fprintf(gl2ps->stream, "/Sh%d %d 0 R\n", gro->shno, gro->shobjno);
 3835     if(gro->maskshno >= 0)
 3836       offs += fprintf(gl2ps->stream, "/TrSh%d %d 0 R\n", gro->maskshno, gro->maskshobjno);
 3837   }
 3838   offs += fprintf(gl2ps->stream,">>\n");
 3839   return offs;
 3840 }
 3841 
 3842 /* Images & Mask Shader XObject names */
 3843 
 3844 static int gl2psPDFgroupListWriteXObjectResources(void)
 3845 {
 3846   int i;
 3847   GL2PSprimitive *p = NULL;
 3848   GL2PSpdfgroup *gro;
 3849   int offs = 0;
 3850 
 3851   offs += fprintf(gl2ps->stream,
 3852                   "/XObject\n"
 3853                   "<<\n");
 3854 
 3855   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
 3856     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
 3857     if(!gl2psListNbr(gro->ptrlist))
 3858       continue;
 3859     p = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);
 3860     switch(p->type){
 3861     case GL2PS_PIXMAP:
 3862       gro->imobjno = gl2ps->objects_stack++;
 3863       if(GL_RGBA == p->data.image->format)  /* reserve one object for image mask */
 3864         gl2ps->objects_stack++;
 3865       offs += fprintf(gl2ps->stream, "/Im%d %d 0 R\n", gro->imno, gro->imobjno);
 3866     case GL2PS_TRIANGLE:
 3867       if(gro->trgroupno >=0)
 3868         offs += fprintf(gl2ps->stream, "/TrG%d %d 0 R\n", gro->trgroupno, gro->trgroupobjno);
 3869       break;
 3870     default:
 3871       break;
 3872     }
 3873   }
 3874   offs += fprintf(gl2ps->stream,">>\n");
 3875   return offs;
 3876 }
 3877 
 3878 /* Font names */
 3879 
 3880 static int gl2psPDFgroupListWriteFontResources(void)
 3881 {
 3882   int i;
 3883   GL2PSpdfgroup *gro;
 3884   int offs = 0;
 3885 
 3886   offs += fprintf(gl2ps->stream, "/Font\n<<\n");
 3887 
 3888   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
 3889     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
 3890     if(gro->fontno < 0)
 3891       continue;
 3892     gro->fontobjno = gl2ps->objects_stack++;
 3893     offs += fprintf(gl2ps->stream, "/F%d %d 0 R\n", gro->fontno, gro->fontobjno);
 3894   }
 3895   offs += fprintf(gl2ps->stream, ">>\n");
 3896 
 3897   return offs;
 3898 }
 3899 
 3900 static void gl2psPDFgroupListDelete(void)
 3901 {
 3902   int i;
 3903   GL2PSpdfgroup *gro = NULL;
 3904 
 3905   if(!gl2ps->pdfgrouplist)
 3906     return;
 3907 
 3908   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
 3909     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist,i);
 3910     gl2psListDelete(gro->ptrlist);
 3911   }
 3912 
 3913   gl2psListDelete(gl2ps->pdfgrouplist);
 3914   gl2ps->pdfgrouplist = NULL;
 3915 }
 3916 
 3917 /* Print 1st PDF object - file info */
 3918 
 3919 static int gl2psPrintPDFInfo(void)
 3920 {
 3921   int offs;
 3922   time_t now;
 3923   struct tm *newtime;
 3924 
 3925   time(&now);
 3926   newtime = gmtime(&now);
 3927 
 3928   offs = fprintf(gl2ps->stream,
 3929                  "1 0 obj\n"
 3930                  "<<\n"
 3931                  "/Title (%s)\n"
 3932                  "/Creator (GL2PS %d.%d.%d%s, %s)\n"
 3933                  "/Producer (%s)\n",
 3934                  gl2ps->title, GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION,
 3935                  GL2PS_PATCH_VERSION, GL2PS_EXTRA_VERSION, GL2PS_COPYRIGHT,
 3936                  gl2ps->producer);
 3937 
 3938   if(!newtime){
 3939     offs += fprintf(gl2ps->stream,
 3940                     ">>\n"
 3941                     "endobj\n");
 3942     return offs;
 3943   }
 3944 
 3945   offs += fprintf(gl2ps->stream,
 3946                   "/CreationDate (D:%d%02d%02d%02d%02d%02d)\n"
 3947                   ">>\n"
 3948                   "endobj\n",
 3949                   newtime->tm_year+1900,
 3950                   newtime->tm_mon+1,
 3951                   newtime->tm_mday,
 3952                   newtime->tm_hour,
 3953                   newtime->tm_min,
 3954                   newtime->tm_sec);
 3955   return offs;
 3956 }
 3957 
 3958 /* Create catalog and page structure - 2nd and 3th PDF object */
 3959 
 3960 static int gl2psPrintPDFCatalog(void)
 3961 {
 3962   return fprintf(gl2ps->stream,
 3963                  "2 0 obj\n"
 3964                  "<<\n"
 3965                  "/Type /Catalog\n"
 3966                  "/Pages 3 0 R\n"
 3967                  ">>\n"
 3968                  "endobj\n");
 3969 }
 3970 
 3971 static int gl2psPrintPDFPages(void)
 3972 {
 3973   return fprintf(gl2ps->stream,
 3974                  "3 0 obj\n"
 3975                  "<<\n"
 3976                  "/Type /Pages\n"
 3977                  "/Kids [6 0 R]\n"
 3978                  "/Count 1\n"
 3979                  ">>\n"
 3980                  "endobj\n");
 3981 }
 3982 
 3983 /* Open stream for data - graphical objects, fonts etc. PDF object 4 */
 3984 
 3985 static int gl2psOpenPDFDataStream(void)
 3986 {
 3987   int offs = 0;
 3988 
 3989   offs += fprintf(gl2ps->stream,
 3990                   "4 0 obj\n"
 3991                   "<<\n"
 3992                   "/Length 5 0 R\n" );
 3993   offs += gl2psPrintPDFCompressorType();
 3994   offs += fprintf(gl2ps->stream,
 3995                   ">>\n"
 3996                   "stream\n");
 3997   return offs;
 3998 }
 3999 
 4000 /* Stream setup - Graphics state, fill background if allowed */
 4001 
 4002 static int gl2psOpenPDFDataStreamWritePreface(void)
 4003 {
 4004   int offs;
 4005 
 4006   offs = gl2psPrintf("/GSa gs\n");
 4007 
 4008   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
 4009     offs += gl2psPrintPDFFillColor(gl2ps->bgcolor);
 4010     offs += gl2psPrintf("%d %d %d %d re\n",
 4011                         (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
 4012                         (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
 4013     offs += gl2psPrintf("f\n");
 4014   }
 4015   return offs;
 4016 }
 4017 
 4018 /* Use the functions above to create the first part of the PDF*/
 4019 
 4020 static void gl2psPrintPDFHeader(void)
 4021 {
 4022   int offs = 0;
 4023   gl2ps->pdfprimlist = gl2psListCreate(500, 500, sizeof(GL2PSprimitive*));
 4024   gl2psPDFstacksInit();
 4025 
 4026   gl2ps->xreflist = (int*)gl2psMalloc(sizeof(int) * gl2ps->objects_stack);
 4027 
 4028 #if defined(GL2PS_HAVE_ZLIB)
 4029   if(gl2ps->options & GL2PS_COMPRESS){
 4030     gl2psSetupCompress();
 4031   }
 4032 #endif
 4033   gl2ps->xreflist[0] = 0;
 4034   offs += fprintf(gl2ps->stream, "%%PDF-1.4\n");
 4035   gl2ps->xreflist[1] = offs;
 4036 
 4037   offs += gl2psPrintPDFInfo();
 4038   gl2ps->xreflist[2] = offs;
 4039 
 4040   offs += gl2psPrintPDFCatalog();
 4041   gl2ps->xreflist[3] = offs;
 4042 
 4043   offs += gl2psPrintPDFPages();
 4044   gl2ps->xreflist[4] = offs;
 4045 
 4046   offs += gl2psOpenPDFDataStream();
 4047   gl2ps->xreflist[5] = offs; /* finished in gl2psPrintPDFFooter */
 4048   gl2ps->streamlength = gl2psOpenPDFDataStreamWritePreface();
 4049 }
 4050 
 4051 /* The central primitive drawing */
 4052 
 4053 static void gl2psPrintPDFPrimitive(void *data)
 4054 {
 4055   GL2PSprimitive *prim = *(GL2PSprimitive**)data;
 4056 
 4057   if((gl2ps->options & GL2PS_OCCLUSION_CULL) && prim->culled)
 4058     return;
 4059 
 4060   prim = gl2psCopyPrimitive(prim); /* deep copy */
 4061   gl2psListAdd(gl2ps->pdfprimlist, &prim);
 4062 }
 4063 
 4064 /* close stream and ... */
 4065 
 4066 static int gl2psClosePDFDataStream(void)
 4067 {
 4068   int offs = 0;
 4069 
 4070 #if defined(GL2PS_HAVE_ZLIB)
 4071   if(gl2ps->options & GL2PS_COMPRESS){
 4072     if(Z_OK != gl2psDeflate())
 4073       gl2psMsg(GL2PS_ERROR, "Zlib deflate error");
 4074     else
 4075       fwrite(gl2ps->compress->dest, gl2ps->compress->destLen, 1, gl2ps->stream);
 4076     gl2ps->streamlength += gl2ps->compress->destLen;
 4077 
 4078     offs += gl2ps->streamlength;
 4079     gl2psFreeCompress();
 4080   }
 4081 #endif
 4082 
 4083   offs += fprintf(gl2ps->stream,
 4084                   "endstream\n"
 4085                   "endobj\n");
 4086   return offs;
 4087 }
 4088 
 4089 /* ... write the now known length object */
 4090 
 4091 static int gl2psPrintPDFDataStreamLength(int val)
 4092 {
 4093   return fprintf(gl2ps->stream,
 4094                  "5 0 obj\n"
 4095                  "%d\n"
 4096                  "endobj\n", val);
 4097 }
 4098 
 4099 /* Put the info created before in PDF objects */
 4100 
 4101 static int gl2psPrintPDFOpenPage(void)
 4102 {
 4103   int offs;
 4104 
 4105   /* Write fixed part */
 4106 
 4107   offs = fprintf(gl2ps->stream,
 4108                  "6 0 obj\n"
 4109                  "<<\n"
 4110                  "/Type /Page\n"
 4111                  "/Parent 3 0 R\n"
 4112                  "/MediaBox [%d %d %d %d]\n",
 4113                  (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
 4114                  (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
 4115 
 4116   if(gl2ps->options & GL2PS_LANDSCAPE)
 4117     offs += fprintf(gl2ps->stream, "/Rotate -90\n");
 4118 
 4119   offs += fprintf(gl2ps->stream,
 4120                   "/Contents 4 0 R\n"
 4121                   "/Resources\n"
 4122                   "<<\n"
 4123                   "/ProcSet [/PDF /Text /ImageB /ImageC]  %%/ImageI\n");
 4124 
 4125   return offs;
 4126 
 4127   /* End fixed part, proceeds in gl2psPDFgroupListWriteVariableResources() */
 4128 }
 4129 
 4130 static int gl2psPDFgroupListWriteVariableResources(void)
 4131 {
 4132   int offs = 0;
 4133 
 4134   /* a) Graphics States for shader alpha masks*/
 4135   offs += gl2psPDFgroupListWriteGStateResources();
 4136 
 4137   /* b) Shader and shader masks */
 4138   offs += gl2psPDFgroupListWriteShaderResources();
 4139 
 4140   /* c) XObjects (Images & Shader Masks) */
 4141   offs += gl2psPDFgroupListWriteXObjectResources();
 4142 
 4143   /* d) Fonts */
 4144   offs += gl2psPDFgroupListWriteFontResources();
 4145 
 4146   /* End resources and page */
 4147   offs += fprintf(gl2ps->stream,
 4148                   ">>\n"
 4149                   ">>\n"
 4150                   "endobj\n");
 4151   return offs;
 4152 }
 4153 
 4154 /* Standard Graphics State */
 4155 
 4156 static int gl2psPrintPDFGSObject(void)
 4157 {
 4158   return fprintf(gl2ps->stream,
 4159                  "7 0 obj\n"
 4160                  "<<\n"
 4161                  "/Type /ExtGState\n"
 4162                  "/SA false\n"
 4163                  "/SM 0.02\n"
 4164                  "/OP false\n"
 4165                  "/op false\n"
 4166                  "/OPM 0\n"
 4167                  "/BG2 /Default\n"
 4168                  "/UCR2 /Default\n"
 4169                  "/TR2 /Default\n"
 4170                  ">>\n"
 4171                  "endobj\n");
 4172 }
 4173 
 4174 /* Put vertex' edge flag (8bit) and coordinates (32bit) in shader stream */
 4175 
 4176 static int gl2psPrintPDFShaderStreamDataCoord(GL2PSvertex *vertex,
 4177                                               int (*action)(unsigned long data, int size),
 4178                                               GLfloat dx, GLfloat dy,
 4179                                               GLfloat xmin, GLfloat ymin)
 4180 {
 4181   int offs = 0;
 4182   unsigned long imap;
 4183   GLfloat diff;
 4184   double dmax = ~1UL;
 4185   char edgeflag = 0;
 4186 
 4187   /* FIXME: temp bux fix for 64 bit archs: */
 4188   if(sizeof(unsigned long) == 8) dmax = dmax - 2048.;
 4189 
 4190   offs += (*action)(edgeflag, 1);
 4191 
 4192   /* The Shader stream in PDF requires to be in a 'big-endian'
 4193      order */
 4194 
 4195   if(GL2PS_ZERO(dx * dy)){
 4196     offs += (*action)(0, 4);
 4197     offs += (*action)(0, 4);
 4198   }
 4199   else{
 4200     diff = (vertex->xyz[0] - xmin) / dx;
 4201     if(diff > 1)
 4202       diff = 1.0F;
 4203     else if(diff < 0)
 4204       diff = 0.0F;
 4205     imap = (unsigned long)(diff * dmax);
 4206     offs += (*action)(imap, 4);
 4207 
 4208     diff = (vertex->xyz[1] - ymin) / dy;
 4209     if(diff > 1)
 4210       diff = 1.0F;
 4211     else if(diff < 0)
 4212       diff = 0.0F;
 4213     imap = (unsigned long)(diff * dmax);
 4214     offs += (*action)(imap, 4);
 4215   }
 4216 
 4217   return offs;
 4218 }
 4219 
 4220 /* Put vertex' rgb value (8bit for every component) in shader stream */
 4221 
 4222 static int gl2psPrintPDFShaderStreamDataRGB(GL2PSvertex *vertex,
 4223                                             int (*action)(unsigned long data, int size))
 4224 {
 4225   int offs = 0;
 4226   unsigned long imap;
 4227   double dmax = ~1UL;
 4228 
 4229   /* FIXME: temp bux fix for 64 bit archs: */
 4230   if(sizeof(unsigned long) == 8) dmax = dmax - 2048.;
 4231 
 4232   imap = (unsigned long)((vertex->rgba[0]) * dmax);
 4233   offs += (*action)(imap, 1);
 4234 
 4235   imap = (unsigned long)((vertex->rgba[1]) * dmax);
 4236   offs += (*action)(imap, 1);
 4237 
 4238   imap = (unsigned long)((vertex->rgba[2]) * dmax);
 4239   offs += (*action)(imap, 1);
 4240 
 4241   return offs;
 4242 }
 4243 
 4244 /* Put vertex' alpha (8/16bit) in shader stream */
 4245 
 4246 static int gl2psPrintPDFShaderStreamDataAlpha(GL2PSvertex *vertex,
 4247                                               int (*action)(unsigned long data, int size),
 4248                                               int sigbyte)
 4249 {
 4250   int offs = 0;
 4251   unsigned long imap;
 4252   double dmax = ~1UL;
 4253 
 4254   /* FIXME: temp bux fix for 64 bit archs: */
 4255   if(sizeof(unsigned long) == 8) dmax = dmax - 2048.;
 4256 
 4257   if(sigbyte != 8 && sigbyte != 16)
 4258     sigbyte = 8;
 4259 
 4260   sigbyte /= 8;
 4261 
 4262   imap = (unsigned long)((vertex->rgba[3]) * dmax);
 4263 
 4264   offs += (*action)(imap, sigbyte);
 4265 
 4266   return offs;
 4267 }
 4268 
 4269 /* Put a triangles raw data in shader stream */
 4270 
 4271 static int gl2psPrintPDFShaderStreamData(GL2PStriangle *triangle,
 4272                                          GLfloat dx, GLfloat dy,
 4273                                          GLfloat xmin, GLfloat ymin,
 4274                                          int (*action)(unsigned long data, int size),
 4275                                          int gray)
 4276 {
 4277   int i, offs = 0;
 4278   GL2PSvertex v;
 4279 
 4280   if(gray && gray != 8 && gray != 16)
 4281     gray = 8;
 4282 
 4283   for(i = 0; i < 3; ++i){
 4284     offs += gl2psPrintPDFShaderStreamDataCoord(&triangle->vertex[i], action,
 4285                                                dx, dy, xmin, ymin);
 4286     if(gray){
 4287       v = triangle->vertex[i];
 4288       offs += gl2psPrintPDFShaderStreamDataAlpha(&v, action, gray);
 4289     }
 4290     else{
 4291       offs += gl2psPrintPDFShaderStreamDataRGB(&triangle->vertex[i], action);
 4292     }
 4293   }
 4294 
 4295   return offs;
 4296 }
 4297 
 4298 static void gl2psPDFRectHull(GLfloat *xmin, GLfloat *xmax,
 4299                              GLfloat *ymin, GLfloat *ymax,
 4300                              GL2PStriangle *triangles, int cnt)
 4301 {
 4302   int i, j;
 4303 
 4304   *xmin = triangles[0].vertex[0].xyz[0];
 4305   *xmax = triangles[0].vertex[0].xyz[0];
 4306   *ymin = triangles[0].vertex[0].xyz[1];
 4307   *ymax = triangles[0].vertex[0].xyz[1];
 4308 
 4309   for(i = 0; i < cnt; ++i){
 4310     for(j = 0; j < 3; ++j){
 4311       if(*xmin > triangles[i].vertex[j].xyz[0])
 4312         *xmin = triangles[i].vertex[j].xyz[0];
 4313       if(*xmax < triangles[i].vertex[j].xyz[0])
 4314         *xmax = triangles[i].vertex[j].xyz[0];
 4315       if(*ymin > triangles[i].vertex[j].xyz[1])
 4316         *ymin = triangles[i].vertex[j].xyz[1];
 4317       if(*ymax < triangles[i].vertex[j].xyz[1])
 4318         *ymax = triangles[i].vertex[j].xyz[1];
 4319     }
 4320   }
 4321 }
 4322 
 4323 /* Writes shaded triangle
 4324    gray == 0 means write RGB triangles
 4325    gray == 8             8bit-grayscale (for alpha masks)
 4326    gray == 16            16bit-grayscale (for alpha masks) */
 4327 
 4328 static int gl2psPrintPDFShader(int obj, GL2PStriangle *triangles,
 4329                                int size, int gray)
 4330 {
 4331   int i, offs = 0, vertexbytes, done = 0;
 4332   GLfloat xmin, xmax, ymin, ymax;
 4333 
 4334   switch(gray){
 4335   case 0:
 4336     vertexbytes = 1+4+4+1+1+1;
 4337     break;
 4338   case 8:
 4339     vertexbytes = 1+4+4+1;
 4340     break;
 4341   case 16:
 4342     vertexbytes = 1+4+4+2;
 4343     break;
 4344   default:
 4345     gray = 8;
 4346     vertexbytes = 1+4+4+1;
 4347     break;
 4348   }
 4349 
 4350   gl2psPDFRectHull(&xmin, &xmax, &ymin, &ymax, triangles, size);
 4351 
 4352   offs += fprintf(gl2ps->stream,
 4353                   "%d 0 obj\n"
 4354                   "<< "
 4355                   "/ShadingType 4 "
 4356                   "/ColorSpace %s "
 4357                   "/BitsPerCoordinate 32 "
 4358                   "/BitsPerComponent %d "
 4359                   "/BitsPerFlag 8 "
 4360                   "/Decode [%f %f %f %f 0 1 %s] ",
 4361                   obj,
 4362                   (gray) ? "/DeviceGray" : "/DeviceRGB",
 4363                   (gray) ? gray : 8,
 4364                   xmin, xmax, ymin, ymax,
 4365                   (gray) ? "" : "0 1 0 1");
 4366 
 4367 #if defined(GL2PS_HAVE_ZLIB)
 4368   if(gl2ps->options & GL2PS_COMPRESS){
 4369     gl2psAllocCompress(vertexbytes * size * 3);
 4370 
 4371     for(i = 0; i < size; ++i)
 4372       gl2psPrintPDFShaderStreamData(&triangles[i],
 4373                                     xmax-xmin, ymax-ymin, xmin, ymin,
 4374                                     gl2psWriteBigEndianCompress, gray);
 4375 
 4376     if(Z_OK == gl2psDeflate() && 23 + gl2ps->compress->destLen < gl2ps->compress->srcLen){
 4377       offs += gl2psPrintPDFCompressorType();
 4378       offs += fprintf(gl2ps->stream,
 4379                       "/Length %d "
 4380                       ">>\n"
 4381                       "stream\n",
 4382                       (int)gl2ps->compress->destLen);
 4383       offs += gl2ps->compress->destLen * fwrite(gl2ps->compress->dest,
 4384                                                 gl2ps->compress->destLen,
 4385                                                 1, gl2ps->stream);
 4386       done = 1;
 4387     }
 4388     gl2psFreeCompress();
 4389   }
 4390 #endif
 4391 
 4392   if(!done){
 4393     /* no compression, or too long after compression, or compress error
 4394        -> write non-compressed entry */
 4395     offs += fprintf(gl2ps->stream,
 4396                     "/Length %d "
 4397                     ">>\n"
 4398                     "stream\n",
 4399                     vertexbytes * 3 * size);
 4400     for(i = 0; i < size; ++i)
 4401       offs += gl2psPrintPDFShaderStreamData(&triangles[i],
 4402                                             xmax-xmin, ymax-ymin, xmin, ymin,
 4403                                             gl2psWriteBigEndian, gray);
 4404   }
 4405 
 4406   offs += fprintf(gl2ps->stream,
 4407                   "\nendstream\n"
 4408                   "endobj\n");
 4409 
 4410   return offs;
 4411 }
 4412 
 4413 /* Writes a XObject for a shaded triangle mask */
 4414 
 4415 static int gl2psPrintPDFShaderMask(int obj, int childobj)
 4416 {
 4417   int offs = 0, len;
 4418 
 4419   offs += fprintf(gl2ps->stream,
 4420                   "%d 0 obj\n"
 4421                   "<<\n"
 4422                   "/Type /XObject\n"
 4423                   "/Subtype /Form\n"
 4424                   "/BBox [ %d %d %d %d ]\n"
 4425                   "/Group \n<<\n/S /Transparency /CS /DeviceRGB\n"
 4426                   ">>\n",
 4427                   obj,
 4428                   (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
 4429                   (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
 4430 
 4431   len = (childobj>0)
 4432     ? strlen("/TrSh sh\n") + (int)log10((double)childobj)+1
 4433     : strlen("/TrSh0 sh\n");
 4434 
 4435   offs += fprintf(gl2ps->stream,
 4436                   "/Length %d\n"
 4437                   ">>\n"
 4438                   "stream\n",
 4439                   len);
 4440   offs += fprintf(gl2ps->stream,
 4441                   "/TrSh%d sh\n",
 4442                   childobj);
 4443   offs += fprintf(gl2ps->stream,
 4444                   "endstream\n"
 4445                   "endobj\n");
 4446 
 4447   return offs;
 4448 }
 4449 
 4450 /* Writes a Extended graphics state for a shaded triangle mask if
 4451    simplealpha ist true the childobj argument is ignored and a /ca
 4452    statement will be written instead */
 4453 
 4454 static int gl2psPrintPDFShaderExtGS(int obj, int childobj)
 4455 {
 4456   int offs = 0;
 4457 
 4458   offs += fprintf(gl2ps->stream,
 4459                   "%d 0 obj\n"
 4460                   "<<\n",
 4461                   obj);
 4462 
 4463   offs += fprintf(gl2ps->stream,
 4464                   "/SMask << /S /Alpha /G %d 0 R >> ",
 4465                   childobj);
 4466 
 4467   offs += fprintf(gl2ps->stream,
 4468                   ">>\n"
 4469                   "endobj\n");
 4470   return offs;
 4471 }
 4472 
 4473 /* a simple graphics state */
 4474 
 4475 static int gl2psPrintPDFShaderSimpleExtGS(int obj, GLfloat alpha)
 4476 {
 4477   int offs = 0;
 4478 
 4479   offs += fprintf(gl2ps->stream,
 4480                   "%d 0 obj\n"
 4481                   "<<\n"
 4482                   "/ca %g"
 4483                   ">>\n"
 4484                   "endobj\n",
 4485                   obj, alpha);
 4486   return offs;
 4487 }
 4488 
 4489 /* Similar groups of functions for pixmaps and text */
 4490 
 4491 static int gl2psPrintPDFPixmapStreamData(GL2PSimage *im,
 4492                                          int (*action)(unsigned long data, int size),
 4493                                          int gray)
 4494 {
 4495   int x, y, shift;
 4496   GLfloat r, g, b, a;
 4497 
 4498   if(im->format != GL_RGBA && gray)
 4499     return 0;
 4500 
 4501   if(gray && gray != 8 && gray != 16)
 4502     gray = 8;
 4503 
 4504   gray /= 8;
 4505 
 4506   shift = (sizeof(unsigned long) - 1) * 8;
 4507 
 4508   for(y = 0; y < im->height; ++y){
 4509     for(x = 0; x < im->width; ++x){
 4510       a = gl2psGetRGB(im, x, y, &r, &g, &b);
 4511       if(im->format == GL_RGBA && gray){
 4512         (*action)((unsigned long)(a * 255) << shift, gray);
 4513       }
 4514       else{
 4515         (*action)((unsigned long)(r * 255) << shift, 1);
 4516         (*action)((unsigned long)(g * 255) << shift, 1);
 4517         (*action)((unsigned long)(b * 255) << shift, 1);
 4518       }
 4519     }
 4520   }
 4521 
 4522   switch(gray){
 4523   case 0: return 3 * im->width * im->height;
 4524   case 1: return im->width * im->height;
 4525   case 2: return 2 * im->width * im->height;
 4526   default: return 3 * im->width * im->height;
 4527   }
 4528 }
 4529 
 4530 static int gl2psPrintPDFPixmap(int obj, int childobj, GL2PSimage *im, int gray)
 4531 {
 4532   int offs = 0, done = 0, sigbytes = 3;
 4533 
 4534   if(gray && gray !=8 && gray != 16)
 4535     gray = 8;
 4536 
 4537   if(gray)
 4538     sigbytes = gray / 8;
 4539 
 4540   offs += fprintf(gl2ps->stream,
 4541                   "%d 0 obj\n"
 4542                   "<<\n"
 4543                   "/Type /XObject\n"
 4544                   "/Subtype /Image\n"
 4545                   "/Width %d\n"
 4546                   "/Height %d\n"
 4547                   "/ColorSpace %s \n"
 4548                   "/BitsPerComponent 8\n",
 4549                   obj,
 4550                   (int)im->width, (int)im->height,
 4551                   (gray) ? "/DeviceGray" : "/DeviceRGB" );
 4552   if(GL_RGBA == im->format && gray == 0){
 4553     offs += fprintf(gl2ps->stream,
 4554                     "/SMask %d 0 R\n",
 4555                     childobj);
 4556   }
 4557 
 4558 #if defined(GL2PS_HAVE_ZLIB)
 4559   if(gl2ps->options & GL2PS_COMPRESS){
 4560     gl2psAllocCompress((int)(im->width * im->height * sigbytes));
 4561 
 4562     gl2psPrintPDFPixmapStreamData(im, gl2psWriteBigEndianCompress, gray);
 4563 
 4564     if(Z_OK == gl2psDeflate() && 23 + gl2ps->compress->destLen < gl2ps->compress->srcLen){
 4565       offs += gl2psPrintPDFCompressorType();
 4566       offs += fprintf(gl2ps->stream,
 4567                       "/Length %d "
 4568                       ">>\n"
 4569                       "stream\n",
 4570                       (int)gl2ps->compress->destLen);
 4571       offs += gl2ps->compress->destLen * fwrite(gl2ps->compress->dest, gl2ps->compress->destLen,
 4572                                                 1, gl2ps->stream);
 4573       done = 1;
 4574     }
 4575     gl2psFreeCompress();
 4576   }
 4577 #endif
 4578 
 4579   if(!done){
 4580     /* no compression, or too long after compression, or compress error
 4581        -> write non-compressed entry */
 4582     offs += fprintf(gl2ps->stream,
 4583                     "/Length %d "
 4584                     ">>\n"
 4585                     "stream\n",
 4586                     (int)(im->width * im->height * sigbytes));
 4587     offs += gl2psPrintPDFPixmapStreamData(im, gl2psWriteBigEndian, gray);
 4588   }
 4589 
 4590   offs += fprintf(gl2ps->stream,
 4591                   "\nendstream\n"
 4592                   "endobj\n");
 4593 
 4594   return offs;
 4595 }
 4596 
 4597 static int gl2psPrintPDFText(int obj, GL2PSstring *s, int fontnumber)
 4598 {
 4599   int offs = 0;
 4600 
 4601   offs += fprintf(gl2ps->stream,
 4602                   "%d 0 obj\n"
 4603                   "<<\n"
 4604                   "/Type /Font\n"
 4605                   "/Subtype /Type1\n"
 4606                   "/Name /F%d\n"
 4607                   "/BaseFont /%s\n"
 4608                   "/Encoding /MacRomanEncoding\n"
 4609                   ">>\n"
 4610                   "endobj\n",
 4611                   obj, fontnumber, s->fontname);
 4612   return offs;
 4613 }
 4614 
 4615 /* Write the physical objects */
 4616 
 4617 static int gl2psPDFgroupListWriteObjects(int entryoffs)
 4618 {
 4619   int i,j;
 4620   GL2PSprimitive *p = NULL;
 4621   GL2PSpdfgroup *gro;
 4622   int offs = entryoffs;
 4623   GL2PStriangle *triangles;
 4624   int size = 0;
 4625 
 4626   if(!gl2ps->pdfgrouplist)
 4627     return offs;
 4628 
 4629   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
 4630     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
 4631     if(!gl2psListNbr(gro->ptrlist))
 4632       continue;
 4633     p = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);
 4634     switch(p->type){
 4635     case GL2PS_POINT:
 4636       break;
 4637     case GL2PS_LINE:
 4638       break;
 4639     case GL2PS_TRIANGLE:
 4640       size = gl2psListNbr(gro->ptrlist);
 4641       triangles = (GL2PStriangle*)gl2psMalloc(sizeof(GL2PStriangle) * size);
 4642       for(j = 0; j < size; ++j){
 4643         p = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
 4644         gl2psFillTriangleFromPrimitive(&triangles[j], p, GL_TRUE);
 4645       }
 4646       if(triangles[0].prop & T_VAR_COLOR){
 4647         gl2ps->xreflist[gro->shobjno] = offs;
 4648         offs += gl2psPrintPDFShader(gro->shobjno, triangles, size, 0);
 4649       }
 4650       if(triangles[0].prop & T_ALPHA_LESS_1){
 4651         gl2ps->xreflist[gro->gsobjno] = offs;
 4652         offs += gl2psPrintPDFShaderSimpleExtGS(gro->gsobjno, triangles[0].vertex[0].rgba[3]);
 4653       }
 4654       if(triangles[0].prop & T_VAR_ALPHA){
 4655         gl2ps->xreflist[gro->gsobjno] = offs;
 4656         offs += gl2psPrintPDFShaderExtGS(gro->gsobjno, gro->trgroupobjno);
 4657         gl2ps->xreflist[gro->trgroupobjno] = offs;
 4658         offs += gl2psPrintPDFShaderMask(gro->trgroupobjno, gro->maskshno);
 4659         gl2ps->xreflist[gro->maskshobjno] = offs;
 4660         offs += gl2psPrintPDFShader(gro->maskshobjno, triangles, size, 8);
 4661       }
 4662       gl2psFree(triangles);
 4663       break;
 4664     case GL2PS_PIXMAP:
 4665       gl2ps->xreflist[gro->imobjno] = offs;
 4666       offs += gl2psPrintPDFPixmap(gro->imobjno, gro->imobjno+1, p->data.image, 0);
 4667       if(p->data.image->format == GL_RGBA){
 4668         gl2ps->xreflist[gro->imobjno+1] = offs;
 4669         offs += gl2psPrintPDFPixmap(gro->imobjno+1, -1, p->data.image, 8);
 4670       }
 4671       break;
 4672     case GL2PS_TEXT:
 4673       gl2ps->xreflist[gro->fontobjno] = offs;
 4674       offs += gl2psPrintPDFText(gro->fontobjno,p->data.text,gro->fontno);
 4675       break;
 4676     case GL2PS_SPECIAL :
 4677       /* alignment contains the format for which the special output text
 4678          is intended */
 4679       if(p->data.text->alignment == GL2PS_PDF)
 4680         offs += fprintf(gl2ps->stream, "%s\n", p->data.text->str);
 4681       break;
 4682     default:
 4683       break;
 4684     }
 4685   }
 4686   return offs;
 4687 }
 4688 
 4689 /* All variable data has been written at this point and all required
 4690    functioninality has been gathered, so we can write now file footer
 4691    with cross reference table and trailer */
 4692 
 4693 static void gl2psPrintPDFFooter(void)
 4694 {
 4695   int i, offs;
 4696 
 4697   gl2psPDFgroupListInit();
 4698   gl2psPDFgroupListWriteMainStream();
 4699 
 4700   offs = gl2ps->xreflist[5] + gl2ps->streamlength;
 4701   offs += gl2psClosePDFDataStream();
 4702   gl2ps->xreflist[5] = offs;
 4703 
 4704   offs += gl2psPrintPDFDataStreamLength(gl2ps->streamlength);
 4705   gl2ps->xreflist[6] = offs;
 4706   gl2ps->streamlength = 0;
 4707 
 4708   offs += gl2psPrintPDFOpenPage();
 4709   offs += gl2psPDFgroupListWriteVariableResources();
 4710   gl2ps->xreflist = (int*)gl2psRealloc(gl2ps->xreflist,
 4711                                        sizeof(int) * (gl2ps->objects_stack + 1));
 4712   gl2ps->xreflist[7] = offs;
 4713 
 4714   offs += gl2psPrintPDFGSObject();
 4715   gl2ps->xreflist[8] = offs;
 4716 
 4717   gl2ps->xreflist[gl2ps->objects_stack] =
 4718     gl2psPDFgroupListWriteObjects(gl2ps->xreflist[8]);
 4719 
 4720   /* Start cross reference table. The file has to been opened in
 4721      binary mode to preserve the 20 digit string length! */
 4722   fprintf(gl2ps->stream,
 4723           "xref\n"
 4724           "0 %d\n