gl2ps.c

Go to the documentation of this file.
00001 /*
00002  * GL2PS, an OpenGL to PostScript Printing Library
00003  * Copyright (C) 1999-2011 C. Geuzaine
00004  *
00005  * This program is free software; you can redistribute it and/or
00006  * modify it under the terms of either:
00007  *
00008  * a) the GNU Library General Public License as published by the Free
00009  * Software Foundation, either version 2 of the License, or (at your
00010  * option) any later version; or
00011  *
00012  * b) the GL2PS License as published by Christophe Geuzaine, either
00013  * version 2 of the License, or (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See either
00018  * the GNU Library General Public License or the GL2PS License for
00019  * more details.
00020  *
00021  * You should have received a copy of the GNU Library General Public
00022  * License along with this library in the file named "COPYING.LGPL";
00023  * if not, write to the Free Software Foundation, Inc., 675 Mass Ave,
00024  * Cambridge, MA 02139, USA.
00025  *
00026  * You should have received a copy of the GL2PS License with this
00027  * library in the file named "COPYING.GL2PS"; if not, I will be glad
00028  * to provide one.
00029  *
00030  * For the latest info about gl2ps and a full list of contributors,
00031  * see http://www.geuz.org/gl2ps/.
00032  *
00033  * Please report all bugs and problems to <gl2ps@geuz.org>.
00034  */
00035 
00036 #include "gl2ps.h"
00037 
00038 #include <math.h>
00039 #include <string.h>
00040 #include <sys/types.h>
00041 #include <stdarg.h>
00042 #include <time.h>
00043 #include <float.h>
00044 
00045 #if defined(GL2PS_HAVE_ZLIB)
00046 #include <zlib.h>
00047 #endif
00048 
00049 #if defined(GL2PS_HAVE_LIBPNG)
00050 #include <png.h>
00051 #endif
00052 
00053 /*********************************************************************
00054  *
00055  * Private definitions, data structures and prototypes
00056  *
00057  *********************************************************************/
00058 
00059 /* Magic numbers (assuming that the order of magnitude of window
00060    coordinates is 10^3) */
00061 
00062 #define GL2PS_EPSILON       5.0e-3F
00063 #define GL2PS_ZSCALE        1000.0F
00064 #define GL2PS_ZOFFSET       5.0e-2F
00065 #define GL2PS_ZOFFSET_LARGE 20.0F
00066 #define GL2PS_ZERO(arg)     (fabs(arg) < 1.e-20)
00067 
00068 /* Primitive types */
00069 
00070 #define GL2PS_NO_TYPE          -1
00071 #define GL2PS_TEXT             1
00072 #define GL2PS_POINT            2
00073 #define GL2PS_LINE             3
00074 #define GL2PS_QUADRANGLE       4
00075 #define GL2PS_TRIANGLE         5
00076 #define GL2PS_PIXMAP           6
00077 #define GL2PS_IMAGEMAP         7
00078 #define GL2PS_IMAGEMAP_WRITTEN 8
00079 #define GL2PS_IMAGEMAP_VISIBLE 9
00080 #define GL2PS_SPECIAL          10
00081 
00082 /* BSP tree primitive comparison */
00083 
00084 #define GL2PS_COINCIDENT  1
00085 #define GL2PS_IN_FRONT_OF 2
00086 #define GL2PS_IN_BACK_OF  3
00087 #define GL2PS_SPANNING    4
00088 
00089 /* 2D BSP tree primitive comparison */
00090 
00091 #define GL2PS_POINT_COINCIDENT 0
00092 #define GL2PS_POINT_INFRONT    1
00093 #define GL2PS_POINT_BACK       2
00094 
00095 /* Internal feedback buffer pass-through tokens */
00096 
00097 #define GL2PS_BEGIN_OFFSET_TOKEN   1
00098 #define GL2PS_END_OFFSET_TOKEN     2
00099 #define GL2PS_BEGIN_BOUNDARY_TOKEN 3
00100 #define GL2PS_END_BOUNDARY_TOKEN   4
00101 #define GL2PS_BEGIN_STIPPLE_TOKEN  5
00102 #define GL2PS_END_STIPPLE_TOKEN    6
00103 #define GL2PS_POINT_SIZE_TOKEN     7
00104 #define GL2PS_LINE_WIDTH_TOKEN     8
00105 #define GL2PS_BEGIN_BLEND_TOKEN    9
00106 #define GL2PS_END_BLEND_TOKEN      10
00107 #define GL2PS_SRC_BLEND_TOKEN      11
00108 #define GL2PS_DST_BLEND_TOKEN      12
00109 #define GL2PS_IMAGEMAP_TOKEN       13
00110 #define GL2PS_DRAW_PIXELS_TOKEN    14
00111 #define GL2PS_TEXT_TOKEN           15
00112 
00113 typedef enum {
00114   T_UNDEFINED    = -1,
00115   T_CONST_COLOR  = 1,
00116   T_VAR_COLOR    = 1<<1,
00117   T_ALPHA_1      = 1<<2,
00118   T_ALPHA_LESS_1 = 1<<3,
00119   T_VAR_ALPHA    = 1<<4
00120 } GL2PS_TRIANGLE_PROPERTY;
00121 
00122 typedef GLfloat GL2PSxyz[3];
00123 typedef GLfloat GL2PSplane[4];
00124 
00125 typedef struct _GL2PSbsptree2d GL2PSbsptree2d;
00126 
00127 struct _GL2PSbsptree2d {
00128   GL2PSplane plane;
00129   GL2PSbsptree2d *front, *back;
00130 };
00131 
00132 typedef struct {
00133   GLint nmax, size, incr, n;
00134   char *array;
00135 } GL2PSlist;
00136 
00137 typedef struct _GL2PSbsptree GL2PSbsptree;
00138 
00139 struct _GL2PSbsptree {
00140   GL2PSplane plane;
00141   GL2PSlist *primitives;
00142   GL2PSbsptree *front, *back;
00143 };
00144 
00145 typedef struct {
00146   GL2PSxyz xyz;
00147   GL2PSrgba rgba;
00148 } GL2PSvertex;
00149 
00150 typedef struct {
00151   GL2PSvertex vertex[3];
00152   int prop;
00153 } GL2PStriangle;
00154 
00155 typedef struct {
00156   GLshort fontsize;
00157   char *str, *fontname;
00158   /* Note: for a 'special' string, 'alignment' holds the format
00159      (PostScript, PDF, etc.) of the special string */
00160   GLint alignment;
00161   GLfloat angle;
00162 } GL2PSstring;
00163 
00164 typedef struct {
00165   GLsizei width, height;
00166   /* Note: for an imagemap, 'type' indicates if it has already been
00167      written to the file or not, and 'format' indicates if it is
00168      visible or not */
00169   GLenum format, type;
00170   GLfloat zoom_x, zoom_y;
00171   GLfloat *pixels;
00172 } GL2PSimage;
00173 
00174 typedef struct _GL2PSimagemap GL2PSimagemap;
00175 
00176 struct _GL2PSimagemap {
00177   GL2PSimage *image;
00178   GL2PSimagemap *next;
00179 };
00180 
00181 typedef struct {
00182   GLshort type, numverts;
00183   GLushort pattern;
00184   char boundary, offset, culled;
00185   GLint factor;
00186   GLfloat width;
00187   GL2PSvertex *verts;
00188   union {
00189     GL2PSstring *text;
00190     GL2PSimage *image;
00191   } data;
00192 } GL2PSprimitive;
00193 
00194 typedef struct {
00195 #if defined(GL2PS_HAVE_ZLIB)
00196   Bytef *dest, *src, *start;
00197   uLongf destLen, srcLen;
00198 #else
00199   int dummy;
00200 #endif
00201 } GL2PScompress;
00202 
00203 typedef struct{
00204   GL2PSlist* ptrlist;
00205   int gsno, fontno, imno, shno, maskshno, trgroupno;
00206   int gsobjno, fontobjno, imobjno, shobjno, maskshobjno, trgroupobjno;
00207 } GL2PSpdfgroup;
00208 
00209 typedef struct {
00210   /* General */
00211   GLint format, sort, options, colorsize, colormode, buffersize;
00212   char *title, *producer, *filename;
00213   GLboolean boundary, blending;
00214   GLfloat *feedback, offset[2], lastlinewidth;
00215   GLint viewport[4], blendfunc[2], lastfactor;
00216   GL2PSrgba *colormap, lastrgba, threshold, bgcolor;
00217   GLushort lastpattern;
00218   GL2PSvertex lastvertex;
00219   GL2PSlist *primitives, *auxprimitives;
00220   FILE *stream;
00221   GL2PScompress *compress;
00222   GLboolean header;
00223 
00224   /* BSP-specific */
00225   GLint maxbestroot;
00226 
00227   /* Occlusion culling-specific */
00228   GLboolean zerosurfacearea;
00229   GL2PSbsptree2d *imagetree;
00230   GL2PSprimitive *primitivetoadd;
00231 
00232   /* PDF-specific */
00233   int streamlength;
00234   GL2PSlist *pdfprimlist, *pdfgrouplist;
00235   int *xreflist;
00236   int objects_stack; /* available objects */
00237   int extgs_stack; /* graphics state object number */
00238   int font_stack; /* font object number */
00239   int im_stack; /* image object number */
00240   int trgroupobjects_stack; /* xobject numbers */
00241   int shader_stack; /* shader object numbers */
00242   int mshader_stack; /* mask shader object numbers */
00243 
00244   /* for image map list */
00245   GL2PSimagemap *imagemap_head;
00246   GL2PSimagemap *imagemap_tail;
00247 } GL2PScontext;
00248 
00249 typedef struct {
00250   void  (*printHeader)(void);
00251   void  (*printFooter)(void);
00252   void  (*beginViewport)(GLint viewport[4]);
00253   GLint (*endViewport)(void);
00254   void  (*printPrimitive)(void *data);
00255   void  (*printFinalPrimitive)(void);
00256   const char *file_extension;
00257   const char *description;
00258 } GL2PSbackend;
00259 
00260 /* The gl2ps context. gl2ps is not thread safe (we should create a
00261    local GL2PScontext during gl2psBeginPage) */
00262 
00263 static GL2PScontext *gl2ps = NULL;
00264 
00265 /* Need to forward-declare this one */
00266 
00267 static GLint gl2psPrintPrimitives(void);
00268 
00269 /*********************************************************************
00270  *
00271  * Utility routines
00272  *
00273  *********************************************************************/
00274 
00275 static void gl2psMsg(GLint level, const char *fmt, ...)
00276 {
00277   va_list args;
00278 
00279   if(!(gl2ps->options & GL2PS_SILENT)){
00280     switch(level){
00281     case GL2PS_INFO : fprintf(stderr, "GL2PS info: "); break;
00282     case GL2PS_WARNING : fprintf(stderr, "GL2PS warning: "); break;
00283     case GL2PS_ERROR : fprintf(stderr, "GL2PS error: "); break;
00284     }
00285     va_start(args, fmt);
00286     vfprintf(stderr, fmt, args);
00287     va_end(args);
00288     fprintf(stderr, "\n");
00289   }
00290   /* if(level == GL2PS_ERROR) exit(1); */
00291 }
00292 
00293 static void *gl2psMalloc(size_t size)
00294 {
00295   void *ptr;
00296 
00297   if(!size) return NULL;
00298   ptr = malloc(size);
00299   if(!ptr){
00300     gl2psMsg(GL2PS_ERROR, "Couldn't allocate requested memory");
00301     return NULL;
00302   }
00303   return ptr;
00304 }
00305 
00306 static void *gl2psRealloc(void *ptr, size_t size)
00307 {
00308   void *orig = ptr;
00309   if(!size) return NULL;
00310   ptr = realloc(orig, size);
00311   if(!ptr){
00312     gl2psMsg(GL2PS_ERROR, "Couldn't reallocate requested memory");
00313     free(orig);
00314     return NULL;
00315   }
00316   return ptr;
00317 }
00318 
00319 static void gl2psFree(void *ptr)
00320 {
00321   if(!ptr) return;
00322   free(ptr);
00323 }
00324 
00325 static int gl2psWriteBigEndian(unsigned long data, int bytes)
00326 {
00327   int i;
00328   int size = sizeof(unsigned long);
00329   for(i = 1; i <= bytes; ++i){
00330     fputc(0xff & (data >> (size - i) * 8), gl2ps->stream);
00331   }
00332   return bytes;
00333 }
00334 
00335 /* zlib compression helper routines */
00336 
00337 #if defined(GL2PS_HAVE_ZLIB)
00338 
00339 static void gl2psSetupCompress(void)
00340 {
00341   gl2ps->compress = (GL2PScompress*)gl2psMalloc(sizeof(GL2PScompress));
00342   gl2ps->compress->src = NULL;
00343   gl2ps->compress->start = NULL;
00344   gl2ps->compress->dest = NULL;
00345   gl2ps->compress->srcLen = 0;
00346   gl2ps->compress->destLen = 0;
00347 }
00348 
00349 static void gl2psFreeCompress(void)
00350 {
00351   if(!gl2ps->compress)
00352     return;
00353   gl2psFree(gl2ps->compress->start);
00354   gl2psFree(gl2ps->compress->dest);
00355   gl2ps->compress->src = NULL;
00356   gl2ps->compress->start = NULL;
00357   gl2ps->compress->dest = NULL;
00358   gl2ps->compress->srcLen = 0;
00359   gl2ps->compress->destLen = 0;
00360 }
00361 
00362 static int gl2psAllocCompress(unsigned int srcsize)
00363 {
00364   gl2psFreeCompress();
00365 
00366   if(!gl2ps->compress || !srcsize)
00367     return GL2PS_ERROR;
00368 
00369   gl2ps->compress->srcLen = srcsize;
00370   gl2ps->compress->destLen = (int)ceil(1.001 * gl2ps->compress->srcLen + 12);
00371   gl2ps->compress->src = (Bytef*)gl2psMalloc(gl2ps->compress->srcLen);
00372   gl2ps->compress->start = gl2ps->compress->src;
00373   gl2ps->compress->dest = (Bytef*)gl2psMalloc(gl2ps->compress->destLen);
00374 
00375   return GL2PS_SUCCESS;
00376 }
00377 
00378 static void *gl2psReallocCompress(unsigned int srcsize)
00379 {
00380   if(!gl2ps->compress || !srcsize)
00381     return NULL;
00382 
00383   if(srcsize < gl2ps->compress->srcLen)
00384     return gl2ps->compress->start;
00385 
00386   gl2ps->compress->srcLen = srcsize;
00387   gl2ps->compress->destLen = (int)ceil(1.001 * gl2ps->compress->srcLen + 12);
00388   gl2ps->compress->src = (Bytef*)gl2psRealloc(gl2ps->compress->src,
00389                                               gl2ps->compress->srcLen);
00390   gl2ps->compress->start = gl2ps->compress->src;
00391   gl2ps->compress->dest = (Bytef*)gl2psRealloc(gl2ps->compress->dest,
00392                                                gl2ps->compress->destLen);
00393 
00394   return gl2ps->compress->start;
00395 }
00396 
00397 static int gl2psWriteBigEndianCompress(unsigned long data, int bytes)
00398 {
00399   int i;
00400   int size = sizeof(unsigned long);
00401   for(i = 1; i <= bytes; ++i){
00402     *gl2ps->compress->src = (Bytef)(0xff & (data >> (size-i) * 8));
00403     ++gl2ps->compress->src;
00404   }
00405   return bytes;
00406 }
00407 
00408 static int gl2psDeflate(void)
00409 {
00410   /* For compatibility with older zlib versions, we use compress(...)
00411      instead of compress2(..., Z_BEST_COMPRESSION) */
00412   return compress(gl2ps->compress->dest, &gl2ps->compress->destLen,
00413                   gl2ps->compress->start, gl2ps->compress->srcLen);
00414 }
00415 
00416 #endif
00417 
00418 static int gl2psPrintf(const char* fmt, ...)
00419 {
00420   int ret;
00421   va_list args;
00422 
00423 #if defined(GL2PS_HAVE_ZLIB)
00424   unsigned int oldsize = 0;
00425   static char buf[1000];
00426   if(gl2ps->options & GL2PS_COMPRESS){
00427     va_start(args, fmt);
00428     ret = vsprintf(buf, fmt, args);
00429     va_end(args);
00430     oldsize = gl2ps->compress->srcLen;
00431     gl2ps->compress->start = (Bytef*)gl2psReallocCompress(oldsize + ret);
00432     memcpy(gl2ps->compress->start+oldsize, buf, ret);
00433     ret = 0;
00434   }
00435   else{
00436 #endif
00437     va_start(args, fmt);
00438     ret = vfprintf(gl2ps->stream, fmt, args);
00439     va_end(args);
00440 #if defined(GL2PS_HAVE_ZLIB)
00441   }
00442 #endif
00443   return ret;
00444 }
00445 
00446 static void gl2psPrintGzipHeader(void)
00447 {
00448 #if defined(GL2PS_HAVE_ZLIB)
00449   char tmp[10] = {'\x1f', '\x8b', /* magic numbers: 0x1f, 0x8b */
00450                   8, /* compression method: Z_DEFLATED */
00451                   0, /* flags */
00452                   0, 0, 0, 0, /* time */
00453                   2, /* extra flags: max compression */
00454                   '\x03'}; /* OS code: 0x03 (Unix) */
00455 
00456   if(gl2ps->options & GL2PS_COMPRESS){
00457     gl2psSetupCompress();
00458     /* add the gzip file header */
00459     fwrite(tmp, 10, 1, gl2ps->stream);
00460   }
00461 #endif
00462 }
00463 
00464 static void gl2psPrintGzipFooter(void)
00465 {
00466 #if defined(GL2PS_HAVE_ZLIB)
00467   int n;
00468   uLong crc, len;
00469   char tmp[8];
00470 
00471   if(gl2ps->options & GL2PS_COMPRESS){
00472     if(Z_OK != gl2psDeflate()){
00473       gl2psMsg(GL2PS_ERROR, "Zlib deflate error");
00474     }
00475     else{
00476       /* determine the length of the header in the zlib stream */
00477       n = 2; /* CMF+FLG */
00478       if(gl2ps->compress->dest[1] & (1<<5)){
00479         n += 4; /* DICTID */
00480       }
00481       /* write the data, without the zlib header and footer */
00482       fwrite(gl2ps->compress->dest+n, gl2ps->compress->destLen-(n+4),
00483              1, gl2ps->stream);
00484       /* add the gzip file footer */
00485       crc = crc32(0L, gl2ps->compress->start, gl2ps->compress->srcLen);
00486       for(n = 0; n < 4; ++n){
00487         tmp[n] = (char)(crc & 0xff);
00488         crc >>= 8;
00489       }
00490       len = gl2ps->compress->srcLen;
00491       for(n = 4; n < 8; ++n){
00492         tmp[n] = (char)(len & 0xff);
00493         len >>= 8;
00494       }
00495       fwrite(tmp, 8, 1, gl2ps->stream);
00496     }
00497     gl2psFreeCompress();
00498     gl2psFree(gl2ps->compress);
00499     gl2ps->compress = NULL;
00500   }
00501 #endif
00502 }
00503 
00504 /* The list handling routines */
00505 
00506 static void gl2psListRealloc(GL2PSlist *list, GLint n)
00507 {
00508   if(!list){
00509     gl2psMsg(GL2PS_ERROR, "Cannot reallocate NULL list");
00510     return;
00511   }
00512   if(n <= 0) return;
00513   if(!list->array){
00514     list->nmax = n;
00515     list->array = (char*)gl2psMalloc(list->nmax * list->size);
00516   }
00517   else{
00518     if(n > list->nmax){
00519       list->nmax = ((n - 1) / list->incr + 1) * list->incr;
00520       list->array = (char*)gl2psRealloc(list->array,
00521                                         list->nmax * list->size);
00522     }
00523   }
00524 }
00525 
00526 static GL2PSlist *gl2psListCreate(GLint n, GLint incr, GLint size)
00527 {
00528   GL2PSlist *list;
00529 
00530   if(n < 0) n = 0;
00531   if(incr <= 0) incr = 1;
00532   list = (GL2PSlist*)gl2psMalloc(sizeof(GL2PSlist));
00533   list->nmax = 0;
00534   list->incr = incr;
00535   list->size = size;
00536   list->n = 0;
00537   list->array = NULL;
00538   gl2psListRealloc(list, n);
00539   return list;
00540 }
00541 
00542 static void gl2psListReset(GL2PSlist *list)
00543 {
00544   if(!list) return;
00545   list->n = 0;
00546 }
00547 
00548 static void gl2psListDelete(GL2PSlist *list)
00549 {
00550   if(!list) return;
00551   gl2psFree(list->array);
00552   gl2psFree(list);
00553 }
00554 
00555 static void gl2psListAdd(GL2PSlist *list, void *data)
00556 {
00557   if(!list){
00558     gl2psMsg(GL2PS_ERROR, "Cannot add into unallocated list");
00559     return;
00560   }
00561   list->n++;
00562   gl2psListRealloc(list, list->n);
00563   memcpy(&list->array[(list->n - 1) * list->size], data, list->size);
00564 }
00565 
00566 static int gl2psListNbr(GL2PSlist *list)
00567 {
00568   if(!list)
00569     return 0;
00570   return list->n;
00571 }
00572 
00573 static void *gl2psListPointer(GL2PSlist *list, GLint index)
00574 {
00575   if(!list){
00576     gl2psMsg(GL2PS_ERROR, "Cannot point into unallocated list");
00577     return NULL;
00578   }
00579   if((index < 0) || (index >= list->n)){
00580     gl2psMsg(GL2PS_ERROR, "Wrong list index in gl2psListPointer");
00581     return NULL;
00582   }
00583   return &list->array[index * list->size];
00584 }
00585 
00586 static void gl2psListSort(GL2PSlist *list,
00587                           int (*fcmp)(const void *a, const void *b))
00588 {
00589   if(!list)
00590     return;
00591   qsort(list->array, list->n, list->size, fcmp);
00592 }
00593 
00594 static void gl2psListAction(GL2PSlist *list, void (*action)(void *data))
00595 {
00596   GLint i;
00597 
00598   for(i = 0; i < gl2psListNbr(list); i++){
00599     (*action)(gl2psListPointer(list, i));
00600   }
00601 }
00602 
00603 static void gl2psListActionInverse(GL2PSlist *list, void (*action)(void *data))
00604 {
00605   GLint i;
00606 
00607   for(i = gl2psListNbr(list); i > 0; i--){
00608     (*action)(gl2psListPointer(list, i-1));
00609   }
00610 }
00611 
00612 #if defined(GL2PS_HAVE_LIBPNG)
00613 
00614 static void gl2psListRead(GL2PSlist *list, int index, void *data)
00615 {
00616   if((index < 0) || (index >= list->n))
00617     gl2psMsg(GL2PS_ERROR, "Wrong list index in gl2psListRead");
00618   memcpy(data, &list->array[index * list->size], list->size);
00619 }
00620 
00621 static void gl2psEncodeBase64Block(unsigned char in[3], unsigned char out[4], int len)
00622 {
00623   static const char cb64[] =
00624     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
00625 
00626   out[0] = cb64[ in[0] >> 2 ];
00627   out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
00628   out[2] = (len > 1) ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=';
00629   out[3] = (len > 2) ? cb64[ in[2] & 0x3f ] : '=';
00630 }
00631 
00632 static void gl2psListEncodeBase64(GL2PSlist *list)
00633 {
00634   unsigned char *buffer, in[3], out[4];
00635   int i, n, index, len;
00636 
00637   n = list->n * list->size;
00638   buffer = (unsigned char*)gl2psMalloc(n * sizeof(unsigned char));
00639   memcpy(buffer, list->array, n * sizeof(unsigned char));
00640   gl2psListReset(list);
00641 
00642   index = 0;
00643   while(index < n) {
00644     len = 0;
00645     for(i = 0; i < 3; i++) {
00646       if(index < n){
00647         in[i] = buffer[index];
00648         len++;
00649       }
00650       else{
00651         in[i] = 0;
00652       }
00653       index++;
00654     }
00655     if(len) {
00656       gl2psEncodeBase64Block(in, out, len);
00657       for(i = 0; i < 4; i++)
00658         gl2psListAdd(list, &out[i]);
00659     }
00660   }
00661   gl2psFree(buffer);
00662 }
00663 
00664 #endif
00665 
00666 /* Helpers for rgba colors */
00667 
00668 static GLboolean gl2psSameColor(GL2PSrgba rgba1, GL2PSrgba rgba2)
00669 {
00670   if(!GL2PS_ZERO(rgba1[0] - rgba2[0]) ||
00671      !GL2PS_ZERO(rgba1[1] - rgba2[1]) ||
00672      !GL2PS_ZERO(rgba1[2] - rgba2[2]))
00673     return GL_FALSE;
00674   return GL_TRUE;
00675 }
00676 
00677 static GLboolean gl2psVertsSameColor(const GL2PSprimitive *prim)
00678 {
00679   int i;
00680 
00681   for(i = 1; i < prim->numverts; i++){
00682     if(!gl2psSameColor(prim->verts[0].rgba, prim->verts[i].rgba)){
00683       return GL_FALSE;
00684     }
00685   }
00686   return GL_TRUE;
00687 }
00688 
00689 static GLboolean gl2psSameColorThreshold(int n, GL2PSrgba rgba[],
00690                                          GL2PSrgba threshold)
00691 {
00692   int i;
00693 
00694   if(n < 2) return GL_TRUE;
00695 
00696   for(i = 1; i < n; i++){
00697     if(fabs(rgba[0][0] - rgba[i][0]) > threshold[0] ||
00698        fabs(rgba[0][1] - rgba[i][1]) > threshold[1] ||
00699        fabs(rgba[0][2] - rgba[i][2]) > threshold[2])
00700       return GL_FALSE;
00701   }
00702 
00703   return GL_TRUE;
00704 }
00705 
00706 static void gl2psSetLastColor(GL2PSrgba rgba)
00707 {
00708   int i;
00709   for(i = 0; i < 3; ++i){
00710     gl2ps->lastrgba[i] = rgba[i];
00711   }
00712 }
00713 
00714 static GLfloat gl2psGetRGB(GL2PSimage *im, GLuint x, GLuint y,
00715                            GLfloat *red, GLfloat *green, GLfloat *blue)
00716 {
00717 
00718   GLsizei width = im->width;
00719   GLsizei height = im->height;
00720   GLfloat *pixels = im->pixels;
00721   GLfloat *pimag;
00722 
00723   /* OpenGL image is from down to up, PS image is up to down */
00724   switch(im->format){
00725   case GL_RGBA:
00726     pimag = pixels + 4 * (width * (height - 1 - y) + x);
00727     break;
00728   case GL_RGB:
00729   default:
00730     pimag = pixels + 3 * (width * (height - 1 - y) + x);
00731     break;
00732   }
00733   *red = *pimag; pimag++;
00734   *green = *pimag; pimag++;
00735   *blue = *pimag; pimag++;
00736 
00737   return (im->format == GL_RGBA) ? *pimag : 1.0F;
00738 }
00739 
00740 /* Helper routines for pixmaps */
00741 
00742 static GL2PSimage *gl2psCopyPixmap(GL2PSimage *im)
00743 {
00744   int size;
00745   GL2PSimage *image = (GL2PSimage*)gl2psMalloc(sizeof(GL2PSimage));
00746 
00747   image->width = im->width;
00748   image->height = im->height;
00749   image->format = im->format;
00750   image->type = im->type;
00751   image->zoom_x = im->zoom_x;
00752   image->zoom_y = im->zoom_y;
00753 
00754   switch(image->format){
00755   case GL_RGBA:
00756     size = image->height * image->width * 4 * sizeof(GLfloat);
00757     break;
00758   case GL_RGB:
00759   default:
00760     size = image->height * image->width * 3 * sizeof(GLfloat);
00761     break;
00762   }
00763 
00764   image->pixels = (GLfloat*)gl2psMalloc(size);
00765   memcpy(image->pixels, im->pixels, size);
00766 
00767   return image;
00768 }
00769 
00770 static void gl2psFreePixmap(GL2PSimage *im)
00771 {
00772   if(!im)
00773     return;
00774   gl2psFree(im->pixels);
00775   gl2psFree(im);
00776 }
00777 
00778 #if defined(GL2PS_HAVE_LIBPNG)
00779 
00780 #if !defined(png_jmpbuf)
00781 #  define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
00782 #endif
00783 
00784 static void gl2psUserWritePNG(png_structp png_ptr, png_bytep data, png_size_t length)
00785 {
00786   unsigned int i;
00787   GL2PSlist *png = (GL2PSlist*)png_get_io_ptr(png_ptr);
00788   for(i = 0; i < length; i++)
00789     gl2psListAdd(png, &data[i]);
00790 }
00791 
00792 static void gl2psUserFlushPNG(png_structp png_ptr)
00793 {
00794   (void) png_ptr;  /* not used */
00795 }
00796 
00797 static void gl2psConvertPixmapToPNG(GL2PSimage *pixmap, GL2PSlist *png)
00798 {
00799   png_structp png_ptr;
00800   png_infop info_ptr;
00801   unsigned char *row_data;
00802   GLfloat dr, dg, db;
00803   int row, col;
00804 
00805   if(!(png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)))
00806     return;
00807 
00808   if(!(info_ptr = png_create_info_struct(png_ptr))){
00809     png_destroy_write_struct(&png_ptr, NULL);
00810     return;
00811   }
00812 
00813   if(setjmp(png_jmpbuf(png_ptr))) {
00814     png_destroy_write_struct(&png_ptr, &info_ptr);
00815     return;
00816   }
00817 
00818   png_set_write_fn(png_ptr, (void *)png, gl2psUserWritePNG, gl2psUserFlushPNG);
00819   png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
00820   png_set_IHDR(png_ptr, info_ptr, pixmap->width, pixmap->height, 8,
00821                PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
00822                PNG_FILTER_TYPE_BASE);
00823   png_write_info(png_ptr, info_ptr);
00824 
00825   row_data = (unsigned char*)gl2psMalloc(3 * pixmap->width * sizeof(unsigned char));
00826   for(row = 0; row < pixmap->height; row++){
00827     for(col = 0; col < pixmap->width; col++){
00828       gl2psGetRGB(pixmap, col, row, &dr, &dg, &db);
00829       row_data[3*col] = (unsigned char)(255. * dr);
00830       row_data[3*col+1] = (unsigned char)(255. * dg);
00831       row_data[3*col+2] = (unsigned char)(255. * db);
00832     }
00833     png_write_row(png_ptr, (png_bytep)row_data);
00834   }
00835   gl2psFree(row_data);
00836 
00837   png_write_end(png_ptr, info_ptr);
00838   png_destroy_write_struct(&png_ptr, &info_ptr);
00839 }
00840 
00841 #endif
00842 
00843 /* Helper routines for text strings */
00844 
00845 static GLint gl2psAddText(GLint type, const char *str, const char *fontname,
00846                           GLshort fontsize, GLint alignment, GLfloat angle)
00847 {
00848   GLfloat pos[4];
00849   GL2PSprimitive *prim;
00850   GLboolean valid;
00851 
00852   if(!gl2ps || !str || !fontname) return GL2PS_UNINITIALIZED;
00853 
00854   if(gl2ps->options & GL2PS_NO_TEXT) return GL2PS_SUCCESS;
00855 
00856   glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID, &valid);
00857   if(GL_FALSE == valid) return GL2PS_SUCCESS; /* the primitive is culled */
00858 
00859   glGetFloatv(GL_CURRENT_RASTER_POSITION, pos);
00860 
00861   prim = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
00862   prim->type = type;
00863   prim->boundary = 0;
00864   prim->numverts = 1;
00865   prim->verts = (GL2PSvertex*)gl2psMalloc(sizeof(GL2PSvertex));
00866   prim->verts[0].xyz[0] = pos[0];
00867   prim->verts[0].xyz[1] = pos[1];
00868   prim->verts[0].xyz[2] = pos[2];
00869   prim->culled = 0;
00870   prim->offset = 0;
00871   prim->pattern = 0;
00872   prim->factor = 0;
00873   prim->width = 1;
00874   glGetFloatv(GL_CURRENT_RASTER_COLOR, prim->verts[0].rgba);
00875   prim->data.text = (GL2PSstring*)gl2psMalloc(sizeof(GL2PSstring));
00876   prim->data.text->str = (char*)gl2psMalloc((strlen(str)+1)*sizeof(char));
00877   strcpy(prim->data.text->str, str);
00878   prim->data.text->fontname = (char*)gl2psMalloc((strlen(fontname)+1)*sizeof(char));
00879   strcpy(prim->data.text->fontname, fontname);
00880   prim->data.text->fontsize = fontsize;
00881   prim->data.text->alignment = alignment;
00882   prim->data.text->angle = angle;
00883 
00884   gl2psListAdd(gl2ps->auxprimitives, &prim);
00885   glPassThrough(GL2PS_TEXT_TOKEN);
00886 
00887   return GL2PS_SUCCESS;
00888 }
00889 
00890 static GL2PSstring *gl2psCopyText(GL2PSstring *t)
00891 {
00892   GL2PSstring *text = (GL2PSstring*)gl2psMalloc(sizeof(GL2PSstring));
00893   text->str = (char*)gl2psMalloc((strlen(t->str)+1)*sizeof(char));
00894   strcpy(text->str, t->str);
00895   text->fontname = (char*)gl2psMalloc((strlen(t->fontname)+1)*sizeof(char));
00896   strcpy(text->fontname, t->fontname);
00897   text->fontsize = t->fontsize;
00898   text->alignment = t->alignment;
00899   text->angle = t->angle;
00900 
00901   return text;
00902 }
00903 
00904 static void gl2psFreeText(GL2PSstring *text)
00905 {
00906   if(!text)
00907     return;
00908   gl2psFree(text->str);
00909   gl2psFree(text->fontname);
00910   gl2psFree(text);
00911 }
00912 
00913 /* Helpers for blending modes */
00914 
00915 static GLboolean gl2psSupportedBlendMode(GLenum sfactor, GLenum dfactor)
00916 {
00917   /* returns TRUE if gl2ps supports the argument combination: only two
00918      blending modes have been implemented so far */
00919 
00920   if( (sfactor == GL_SRC_ALPHA && dfactor == GL_ONE_MINUS_SRC_ALPHA) ||
00921       (sfactor == GL_ONE && dfactor == GL_ZERO) )
00922     return GL_TRUE;
00923   return GL_FALSE;
00924 }
00925 
00926 static void gl2psAdaptVertexForBlending(GL2PSvertex *v)
00927 {
00928   /* Transforms vertex depending on the actual blending function -
00929      currently the vertex v is considered as source vertex and his
00930      alpha value is changed to 1.0 if source blending GL_ONE is
00931      active. This might be extended in the future */
00932 
00933   if(!v || !gl2ps)
00934     return;
00935 
00936   if(gl2ps->options & GL2PS_NO_BLENDING || !gl2ps->blending){
00937     v->rgba[3] = 1.0F;
00938     return;
00939   }
00940 
00941   switch(gl2ps->blendfunc[0]){
00942   case GL_ONE:
00943     v->rgba[3] = 1.0F;
00944     break;
00945   default:
00946     break;
00947   }
00948 }
00949 
00950 static void gl2psAssignTriangleProperties(GL2PStriangle *t)
00951 {
00952   /* int i; */
00953 
00954   t->prop = T_VAR_COLOR;
00955 
00956   /* Uncommenting the following lines activates an even more fine
00957      grained distinction between triangle types - please don't delete,
00958      a remarkable amount of PDF handling code inside this file depends
00959      on it if activated */
00960   /*
00961   t->prop = T_CONST_COLOR;
00962   for(i = 0; i < 3; ++i){
00963     if(!GL2PS_ZERO(t->vertex[0].rgba[i] - t->vertex[1].rgba[i]) ||
00964        !GL2PS_ZERO(t->vertex[1].rgba[i] - t->vertex[2].rgba[i])){
00965       t->prop = T_VAR_COLOR;
00966       break;
00967     }
00968   }
00969   */
00970 
00971   if(!GL2PS_ZERO(t->vertex[0].rgba[3] - t->vertex[1].rgba[3]) ||
00972      !GL2PS_ZERO(t->vertex[1].rgba[3] - t->vertex[2].rgba[3])){
00973     t->prop |= T_VAR_ALPHA;
00974   }
00975   else{
00976     if(t->vertex[0].rgba[3] < 1)
00977       t->prop |= T_ALPHA_LESS_1;
00978     else
00979       t->prop |= T_ALPHA_1;
00980   }
00981 }
00982 
00983 static void gl2psFillTriangleFromPrimitive(GL2PStriangle *t, GL2PSprimitive *p,
00984                                            GLboolean assignprops)
00985 {
00986   t->vertex[0] = p->verts[0];
00987   t->vertex[1] = p->verts[1];
00988   t->vertex[2] = p->verts[2];
00989   if(GL_TRUE == assignprops)
00990     gl2psAssignTriangleProperties(t);
00991 }
00992 
00993 static void gl2psInitTriangle(GL2PStriangle *t)
00994 {
00995   int i;
00996   GL2PSvertex vertex = { {-1.0F, -1.0F, -1.0F}, {-1.0F, -1.0F, -1.0F, -1.0F} };
00997   for(i = 0; i < 3; i++)
00998     t->vertex[i] = vertex;
00999   t->prop = T_UNDEFINED;
01000 }
01001 
01002 /* Miscellaneous helper routines */
01003 
01004 static GL2PSprimitive *gl2psCopyPrimitive(GL2PSprimitive *p)
01005 {
01006   GL2PSprimitive *prim;
01007 
01008   if(!p){
01009     gl2psMsg(GL2PS_ERROR, "Trying to copy an empty primitive");
01010     return NULL;
01011   }
01012 
01013   prim = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
01014 
01015   prim->type = p->type;
01016   prim->numverts = p->numverts;
01017   prim->boundary = p->boundary;
01018   prim->offset = p->offset;
01019   prim->pattern = p->pattern;
01020   prim->factor = p->factor;
01021   prim->culled = p->culled;
01022   prim->width = p->width;
01023   prim->verts = (GL2PSvertex*)gl2psMalloc(p->numverts*sizeof(GL2PSvertex));
01024   memcpy(prim->verts, p->verts, p->numverts * sizeof(GL2PSvertex));
01025 
01026   switch(prim->type){
01027   case GL2PS_PIXMAP :
01028     prim->data.image = gl2psCopyPixmap(p->data.image);
01029     break;
01030   case GL2PS_TEXT :
01031   case GL2PS_SPECIAL :
01032     prim->data.text = gl2psCopyText(p->data.text);
01033     break;
01034   default:
01035     break;
01036   }
01037 
01038   return prim;
01039 }
01040 
01041 static GLboolean gl2psSamePosition(GL2PSxyz p1, GL2PSxyz p2)
01042 {
01043   if(!GL2PS_ZERO(p1[0] - p2[0]) ||
01044      !GL2PS_ZERO(p1[1] - p2[1]) ||
01045      !GL2PS_ZERO(p1[2] - p2[2]))
01046     return GL_FALSE;
01047   return GL_TRUE;
01048 }
01049 
01050 /*********************************************************************
01051  *
01052  * 3D sorting routines
01053  *
01054  *********************************************************************/
01055 
01056 static GLfloat gl2psComparePointPlane(GL2PSxyz point, GL2PSplane plane)
01057 {
01058   return (plane[0] * point[0] +
01059           plane[1] * point[1] +
01060           plane[2] * point[2] +
01061           plane[3]);
01062 }
01063 
01064 static GLfloat gl2psPsca(GLfloat *a, GLfloat *b)
01065 {
01066   return (a[0]*b[0] + a[1]*b[1] + a[2]*b[2]);
01067 }
01068 
01069 static void gl2psPvec(GLfloat *a, GLfloat *b, GLfloat *c)
01070 {
01071   c[0] = a[1]*b[2] - a[2]*b[1];
01072   c[1] = a[2]*b[0] - a[0]*b[2];
01073   c[2] = a[0]*b[1] - a[1]*b[0];
01074 }
01075 
01076 static GLfloat gl2psNorm(GLfloat *a)
01077 {
01078   return (GLfloat)sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
01079 }
01080 
01081 static void gl2psGetNormal(GLfloat *a, GLfloat *b, GLfloat *c)
01082 {
01083   GLfloat norm;
01084 
01085   gl2psPvec(a, b, c);
01086   if(!GL2PS_ZERO(norm = gl2psNorm(c))){
01087     c[0] = c[0] / norm;
01088     c[1] = c[1] / norm;
01089     c[2] = c[2] / norm;
01090   }
01091   else{
01092     /* The plane is still wrong despite our tests in gl2psGetPlane.
01093        Let's return a dummy value for now (this is a hack: we should
01094        do more intelligent tests in GetPlane) */
01095     c[0] = c[1] = 0.0F;
01096     c[2] = 1.0F;
01097   }
01098 }
01099 
01100 static void gl2psGetPlane(GL2PSprimitive *prim, GL2PSplane plane)
01101 {
01102   GL2PSxyz v = {0.0F, 0.0F, 0.0F}, w = {0.0F, 0.0F, 0.0F};
01103 
01104   switch(prim->type){
01105   case GL2PS_TRIANGLE :
01106   case GL2PS_QUADRANGLE :
01107     v[0] = prim->verts[1].xyz[0] - prim->verts[0].xyz[0];
01108     v[1] = prim->verts[1].xyz[1] - prim->verts[0].xyz[1];
01109     v[2] = prim->verts[1].xyz[2] - prim->verts[0].xyz[2];
01110     w[0] = prim->verts[2].xyz[0] - prim->verts[0].xyz[0];
01111     w[1] = prim->verts[2].xyz[1] - prim->verts[0].xyz[1];
01112     w[2] = prim->verts[2].xyz[2] - prim->verts[0].xyz[2];
01113     if((GL2PS_ZERO(v[0]) && GL2PS_ZERO(v[1]) && GL2PS_ZERO(v[2])) ||
01114        (GL2PS_ZERO(w[0]) && GL2PS_ZERO(w[1]) && GL2PS_ZERO(w[2]))){
01115       plane[0] = plane[1] = 0.0F;
01116       plane[2] = 1.0F;
01117       plane[3] = -prim->verts[0].xyz[2];
01118     }
01119     else{
01120       gl2psGetNormal(v, w, plane);
01121       plane[3] =
01122         - plane[0] * prim->verts[0].xyz[0]
01123         - plane[1] * prim->verts[0].xyz[1]
01124         - plane[2] * prim->verts[0].xyz[2];
01125     }
01126     break;
01127   case GL2PS_LINE :
01128     v[0] = prim->verts[1].xyz[0] - prim->verts[0].xyz[0];
01129     v[1] = prim->verts[1].xyz[1] - prim->verts[0].xyz[1];
01130     v[2] = prim->verts[1].xyz[2] - prim->verts[0].xyz[2];
01131     if(GL2PS_ZERO(v[0]) && GL2PS_ZERO(v[1]) && GL2PS_ZERO(v[2])){
01132       plane[0] = plane[1] = 0.0F;
01133       plane[2] = 1.0F;
01134       plane[3] = -prim->verts[0].xyz[2];
01135     }
01136     else{
01137       if(GL2PS_ZERO(v[0]))      w[0] = 1.0F;
01138       else if(GL2PS_ZERO(v[1])) w[1] = 1.0F;
01139       else                      w[2] = 1.0F;
01140       gl2psGetNormal(v, w, plane);
01141       plane[3] =
01142         - plane[0] * prim->verts[0].xyz[0]
01143         - plane[1] * prim->verts[0].xyz[1]
01144         - plane[2] * prim->verts[0].xyz[2];
01145     }
01146     break;
01147   case GL2PS_POINT :
01148   case GL2PS_PIXMAP :
01149   case GL2PS_TEXT :
01150   case GL2PS_SPECIAL :
01151   case GL2PS_IMAGEMAP:
01152     plane[0] = plane[1] = 0.0F;
01153     plane[2] = 1.0F;
01154     plane[3] = -prim->verts[0].xyz[2];
01155     break;
01156   default :
01157     gl2psMsg(GL2PS_ERROR, "Unknown primitive type in BSP tree");
01158     plane[0] = plane[1] = plane[3] = 0.0F;
01159     plane[2] = 1.0F;
01160     break;
01161   }
01162 }
01163 
01164 static void gl2psCutEdge(GL2PSvertex *a, GL2PSvertex *b, GL2PSplane plane,
01165                          GL2PSvertex *c)
01166 {
01167   GL2PSxyz v;
01168   GLfloat sect, psca;
01169 
01170   v[0] = b->xyz[0] - a->xyz[0];
01171   v[1] = b->xyz[1] - a->xyz[1];
01172   v[2] = b->xyz[2] - a->xyz[2];
01173 
01174   if(!GL2PS_ZERO(psca = gl2psPsca(plane, v)))
01175     sect = -gl2psComparePointPlane(a->xyz, plane) / psca;
01176   else
01177     sect = 0.0F;
01178 
01179   c->xyz[0] = a->xyz[0] + v[0] * sect;
01180   c->xyz[1] = a->xyz[1] + v[1] * sect;
01181   c->xyz[2] = a->xyz[2] + v[2] * sect;
01182 
01183   c->rgba[0] = (1 - sect) * a->rgba[0] + sect * b->rgba[0];
01184   c->rgba[1] = (1 - sect) * a->rgba[1] + sect * b->rgba[1];
01185   c->rgba[2] = (1 - sect) * a->rgba[2] + sect * b->rgba[2];
01186   c->rgba[3] = (1 - sect) * a->rgba[3] + sect * b->rgba[3];
01187 }
01188 
01189 static void gl2psCreateSplitPrimitive(GL2PSprimitive *parent, GL2PSplane plane,
01190                                       GL2PSprimitive *child, GLshort numverts,
01191                                       GLshort *index0, GLshort *index1)
01192 {
01193   GLshort i;
01194 
01195   if(parent->type == GL2PS_IMAGEMAP){
01196     child->type = GL2PS_IMAGEMAP;
01197     child->data.image = parent->data.image;
01198   }
01199   else{
01200     if(numverts > 4){
01201       gl2psMsg(GL2PS_WARNING, "%d vertices in polygon", numverts);
01202       numverts = 4;
01203     }
01204     switch(numverts){
01205     case 1 : child->type = GL2PS_POINT; break;
01206     case 2 : child->type = GL2PS_LINE; break;
01207     case 3 : child->type = GL2PS_TRIANGLE; break;
01208     case 4 : child->type = GL2PS_QUADRANGLE; break;
01209     default: child->type = GL2PS_NO_TYPE; break;
01210     }
01211   }
01212 
01213   child->boundary = 0; /* FIXME: not done! */
01214   child->culled = parent->culled;
01215   child->offset = parent->offset;
01216   child->pattern = parent->pattern;
01217   child->factor = parent->factor;
01218   child->width = parent->width;
01219   child->numverts = numverts;
01220   child->verts = (GL2PSvertex*)gl2psMalloc(numverts * sizeof(GL2PSvertex));
01221 
01222   for(i = 0; i < numverts; i++){
01223     if(index1[i] < 0){
01224       child->verts[i] = parent->verts[index0[i]];
01225     }
01226     else{
01227       gl2psCutEdge(&parent->verts[index0[i]], &parent->verts[index1[i]],
01228                    plane, &child->verts[i]);
01229     }
01230   }
01231 }
01232 
01233 static void gl2psAddIndex(GLshort *index0, GLshort *index1, GLshort *nb,
01234                           GLshort i, GLshort j)
01235 {
01236   GLint k;
01237 
01238   for(k = 0; k < *nb; k++){
01239     if((index0[k] == i && index1[k] == j) ||
01240        (index1[k] == i && index0[k] == j)) return;
01241   }
01242   index0[*nb] = i;
01243   index1[*nb] = j;
01244   (*nb)++;
01245 }
01246 
01247 static GLshort gl2psGetIndex(GLshort i, GLshort num)
01248 {
01249   return (i < num - 1) ? i + 1 : 0;
01250 }
01251 
01252 static GLint gl2psTestSplitPrimitive(GL2PSprimitive *prim, GL2PSplane plane)
01253 {
01254   GLint type = GL2PS_COINCIDENT;
01255   GLshort i, j;
01256   GLfloat d[5];
01257 
01258   for(i = 0; i < prim->numverts; i++){
01259     d[i] = gl2psComparePointPlane(prim->verts[i].xyz, plane);
01260   }
01261 
01262   if(prim->numverts < 2){
01263     return 0;
01264   }
01265   else{
01266     for(i = 0; i < prim->numverts; i++){
01267       j = gl2psGetIndex(i, prim->numverts);
01268       if(d[j] > GL2PS_EPSILON){
01269         if(type == GL2PS_COINCIDENT)      type = GL2PS_IN_BACK_OF;
01270         else if(type != GL2PS_IN_BACK_OF) return 1;
01271         if(d[i] < -GL2PS_EPSILON)         return 1;
01272       }
01273       else if(d[j] < -GL2PS_EPSILON){
01274         if(type == GL2PS_COINCIDENT)       type = GL2PS_IN_FRONT_OF;
01275         else if(type != GL2PS_IN_FRONT_OF) return 1;
01276         if(d[i] > GL2PS_EPSILON)           return 1;
01277       }
01278     }
01279   }
01280   return 0;
01281 }
01282 
01283 static GLint gl2psSplitPrimitive(GL2PSprimitive *prim, GL2PSplane plane,
01284                                  GL2PSprimitive **front, GL2PSprimitive **back)
01285 {
01286   GLshort i, j, in = 0, out = 0, in0[5], in1[5], out0[5], out1[5];
01287   GLint type;
01288   GLfloat d[5];
01289 
01290   type = GL2PS_COINCIDENT;
01291 
01292   for(i = 0; i < prim->numverts; i++){
01293     d[i] = gl2psComparePointPlane(prim->verts[i].xyz, plane);
01294   }
01295 
01296   switch(prim->type){
01297   case GL2PS_POINT :
01298     if(d[0] > GL2PS_EPSILON)       type = GL2PS_IN_BACK_OF;
01299     else if(d[0] < -GL2PS_EPSILON) type = GL2PS_IN_FRONT_OF;
01300     else                           type = GL2PS_COINCIDENT;
01301     break;
01302   default :
01303     for(i = 0; i < prim->numverts; i++){
01304       j = gl2psGetIndex(i, prim->numverts);
01305       if(d[j] > GL2PS_EPSILON){
01306         if(type == GL2PS_COINCIDENT)      type = GL2PS_IN_BACK_OF;
01307         else if(type != GL2PS_IN_BACK_OF) type = GL2PS_SPANNING;
01308         if(d[i] < -GL2PS_EPSILON){
01309           gl2psAddIndex(in0, in1, &in, i, j);
01310           gl2psAddIndex(out0, out1, &out, i, j);
01311           type = GL2PS_SPANNING;
01312         }
01313         gl2psAddIndex(out0, out1, &out, j, -1);
01314       }
01315       else if(d[j] < -GL2PS_EPSILON){
01316         if(type == GL2PS_COINCIDENT)       type = GL2PS_IN_FRONT_OF;
01317         else if(type != GL2PS_IN_FRONT_OF) type = GL2PS_SPANNING;
01318         if(d[i] > GL2PS_EPSILON){
01319           gl2psAddIndex(in0, in1, &in, i, j);
01320           gl2psAddIndex(out0, out1, &out, i, j);
01321           type = GL2PS_SPANNING;
01322         }
01323         gl2psAddIndex(in0, in1, &in, j, -1);
01324       }
01325       else{
01326         gl2psAddIndex(in0, in1, &in, j, -1);
01327         gl2psAddIndex(out0, out1, &out, j, -1);
01328       }
01329     }
01330     break;
01331   }
01332 
01333   if(type == GL2PS_SPANNING){
01334     *back = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
01335     *front = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
01336     gl2psCreateSplitPrimitive(prim, plane, *back, out, out0, out1);
01337     gl2psCreateSplitPrimitive(prim, plane, *front, in, in0, in1);
01338   }
01339 
01340   return type;
01341 }
01342 
01343 static void gl2psDivideQuad(GL2PSprimitive *quad,
01344                             GL2PSprimitive **t1, GL2PSprimitive **t2)
01345 {
01346   *t1 = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
01347   *t2 = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
01348   (*t1)->type = (*t2)->type = GL2PS_TRIANGLE;
01349   (*t1)->numverts = (*t2)->numverts = 3;
01350   (*t1)->culled = (*t2)->culled = quad->culled;
01351   (*t1)->offset = (*t2)->offset = quad->offset;
01352   (*t1)->pattern = (*t2)->pattern = quad->pattern;
01353   (*t1)->factor = (*t2)->factor = quad->factor;
01354   (*t1)->width = (*t2)->width = quad->width;
01355   (*t1)->verts = (GL2PSvertex*)gl2psMalloc(3 * sizeof(GL2PSvertex));
01356   (*t2)->verts = (GL2PSvertex*)gl2psMalloc(3 * sizeof(GL2PSvertex));
01357   (*t1)->verts[0] = quad->verts[0];
01358   (*t1)->verts[1] = quad->verts[1];
01359   (*t1)->verts[2] = quad->verts[2];
01360   (*t1)->boundary = ((quad->boundary & 1) ? 1 : 0) | ((quad->boundary & 2) ? 2 : 0);
01361   (*t2)->verts[0] = quad->verts[0];
01362   (*t2)->verts[1] = quad->verts[2];
01363   (*t2)->verts[2] = quad->verts[3];
01364   (*t2)->boundary = ((quad->boundary & 4) ? 2 : 0) | ((quad->boundary & 8) ? 4 : 0);
01365 }
01366 
01367 static int gl2psCompareDepth(const void *a, const void *b)
01368 {
01369   const GL2PSprimitive *q, *w;
01370   GLfloat dq = 0.0F, dw = 0.0F, diff;
01371   int i;
01372 
01373   q = *(const GL2PSprimitive* const*)a;
01374   w = *(const GL2PSprimitive* const*)b;
01375 
01376   for(i = 0; i < q->numverts; i++){
01377     dq += q->verts[i].xyz[2];
01378   }
01379   dq /= (GLfloat)q->numverts;
01380 
01381   for(i = 0; i < w->numverts; i++){
01382     dw += w->verts[i].xyz[2];
01383   }
01384   dw /= (GLfloat)w->numverts;
01385 
01386   diff = dq - dw;
01387   if(diff > 0.){
01388     return -1;
01389   }
01390   else if(diff < 0.){
01391     return 1;
01392   }
01393   else{
01394     return 0;
01395   }
01396 }
01397 
01398 static int gl2psTrianglesFirst(const void *a, const void *b)
01399 {
01400   const GL2PSprimitive *q, *w;
01401 
01402   q = *(const GL2PSprimitive* const*)a;
01403   w = *(const GL2PSprimitive* const*)b;
01404   return (q->type < w->type ? 1 : -1);
01405 }
01406 
01407 static GLint gl2psFindRoot(GL2PSlist *primitives, GL2PSprimitive **root)
01408 {
01409   GLint i, j, count, best = 1000000, index = 0;
01410   GL2PSprimitive *prim1, *prim2;
01411   GL2PSplane plane;
01412   GLint maxp;
01413 
01414   if(!gl2psListNbr(primitives)){
01415     gl2psMsg(GL2PS_ERROR, "Cannot fint root in empty primitive list");
01416     return 0;
01417   }
01418 
01419   *root = *(GL2PSprimitive**)gl2psListPointer(primitives, 0);
01420 
01421   if(gl2ps->options & GL2PS_BEST_ROOT){
01422     maxp = gl2psListNbr(primitives);
01423     if(maxp > gl2ps->maxbestroot){
01424       maxp = gl2ps->maxbestroot;
01425     }
01426     for(i = 0; i < maxp; i++){
01427       prim1 = *(GL2PSprimitive**)gl2psListPointer(primitives, i);
01428       gl2psGetPlane(prim1, plane);
01429       count = 0;
01430       for(j = 0; j < gl2psListNbr(primitives); j++){
01431         if(j != i){
01432           prim2 = *(GL2PSprimitive**)gl2psListPointer(primitives, j);
01433           count += gl2psTestSplitPrimitive(prim2, plane);
01434         }
01435         if(count > best) break;
01436       }
01437       if(count < best){
01438         best = count;
01439         index = i;
01440         *root = prim1;
01441         if(!count) return index;
01442       }
01443     }
01444     /* if(index) gl2psMsg(GL2PS_INFO, "GL2PS_BEST_ROOT was worth it: %d", index); */
01445     return index;
01446   }
01447   else{
01448     return 0;
01449   }
01450 }
01451 
01452 static void gl2psFreeImagemap(GL2PSimagemap *list)
01453 {
01454   GL2PSimagemap *next;
01455   while(list != NULL){
01456     next = list->next;
01457     gl2psFree(list->image->pixels);
01458     gl2psFree(list->image);
01459     gl2psFree(list);
01460     list = next;
01461   }
01462 }
01463 
01464 static void gl2psFreePrimitive(void *data)
01465 {
01466   GL2PSprimitive *q;
01467 
01468   q = *(GL2PSprimitive**)data;
01469   gl2psFree(q->verts);
01470   if(q->type == GL2PS_TEXT || q->type == GL2PS_SPECIAL){
01471     gl2psFreeText(q->data.text);
01472   }
01473   else if(q->type == GL2PS_PIXMAP){
01474     gl2psFreePixmap(q->data.image);
01475   }
01476   gl2psFree(q);
01477 }
01478 
01479 static void gl2psAddPrimitiveInList(GL2PSprimitive *prim, GL2PSlist *list)
01480 {
01481   GL2PSprimitive *t1, *t2;
01482 
01483   if(prim->type != GL2PS_QUADRANGLE){
01484     gl2psListAdd(list, &prim);
01485   }
01486   else{
01487     gl2psDivideQuad(prim, &t1, &t2);
01488     gl2psListAdd(list, &t1);
01489     gl2psListAdd(list, &t2);
01490     gl2psFreePrimitive(&prim);
01491   }
01492 
01493 }
01494 
01495 static void gl2psFreeBspTree(GL2PSbsptree **tree)
01496 {
01497   if(*tree){
01498     if((*tree)->back) gl2psFreeBspTree(&(*tree)->back);
01499     if((*tree)->primitives){
01500       gl2psListAction((*tree)->primitives, gl2psFreePrimitive);
01501       gl2psListDelete((*tree)->primitives);
01502     }
01503     if((*tree)->front) gl2psFreeBspTree(&(*tree)->front);
01504     gl2psFree(*tree);
01505     *tree = NULL;
01506   }
01507 }
01508 
01509 static GLboolean gl2psGreater(GLfloat f1, GLfloat f2)
01510 {
01511   if(f1 > f2) return GL_TRUE;
01512   else return GL_FALSE;
01513 }
01514 
01515 static GLboolean gl2psLess(GLfloat f1, GLfloat f2)
01516 {
01517   if(f1 < f2) return GL_TRUE;
01518   else return GL_FALSE;
01519 }
01520 
01521 static void gl2psBuildBspTree(GL2PSbsptree *tree, GL2PSlist *primitives)
01522 {
01523   GL2PSprimitive *prim, *frontprim = NULL, *backprim = NULL;
01524   GL2PSlist *frontlist, *backlist;
01525   GLint i, index;
01526 
01527   tree->front = NULL;
01528   tree->back = NULL;
01529   tree->primitives = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
01530   index = gl2psFindRoot(primitives, &prim);
01531   gl2psGetPlane(prim, tree->plane);
01532   gl2psAddPrimitiveInList(prim, tree->primitives);
01533 
01534   frontlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
01535   backlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
01536 
01537   for(i = 0; i < gl2psListNbr(primitives); i++){
01538     if(i != index){
01539       prim = *(GL2PSprimitive**)gl2psListPointer(primitives,i);
01540       switch(gl2psSplitPrimitive(prim, tree->plane, &frontprim, &backprim)){
01541       case GL2PS_COINCIDENT:
01542         gl2psAddPrimitiveInList(prim, tree->primitives);
01543         break;
01544       case GL2PS_IN_BACK_OF:
01545         gl2psAddPrimitiveInList(prim, backlist);
01546         break;
01547       case GL2PS_IN_FRONT_OF:
01548         gl2psAddPrimitiveInList(prim, frontlist);
01549         break;
01550       case GL2PS_SPANNING:
01551         gl2psAddPrimitiveInList(backprim, backlist);
01552         gl2psAddPrimitiveInList(frontprim, frontlist);
01553         gl2psFreePrimitive(&prim);
01554         break;
01555       }
01556     }
01557   }
01558 
01559   if(gl2psListNbr(tree->primitives)){
01560     gl2psListSort(tree->primitives, gl2psTrianglesFirst);
01561   }
01562 
01563   if(gl2psListNbr(frontlist)){
01564     gl2psListSort(frontlist, gl2psTrianglesFirst);
01565     tree->front = (GL2PSbsptree*)gl2psMalloc(sizeof(GL2PSbsptree));
01566     gl2psBuildBspTree(tree->front, frontlist);
01567   }
01568   else{
01569     gl2psListDelete(frontlist);
01570   }
01571 
01572   if(gl2psListNbr(backlist)){
01573     gl2psListSort(backlist, gl2psTrianglesFirst);
01574     tree->back = (GL2PSbsptree*)gl2psMalloc(sizeof(GL2PSbsptree));
01575     gl2psBuildBspTree(tree->back, backlist);
01576   }
01577   else{
01578     gl2psListDelete(backlist);
01579   }
01580 
01581   gl2psListDelete(primitives);
01582 }
01583 
01584 static void gl2psTraverseBspTree(GL2PSbsptree *tree, GL2PSxyz eye, GLfloat epsilon,
01585                                  GLboolean (*compare)(GLfloat f1, GLfloat f2),
01586                                  void (*action)(void *data), int inverse)
01587 {
01588   GLfloat result;
01589 
01590   if(!tree) return;
01591 
01592   result = gl2psComparePointPlane(eye, tree->plane);
01593 
01594   if(GL_TRUE == compare(result, epsilon)){
01595     gl2psTraverseBspTree(tree->back, eye, epsilon, compare, action, inverse);
01596     if(inverse){
01597       gl2psListActionInverse(tree->primitives, action);
01598     }
01599     else{
01600       gl2psListAction(tree->primitives, action);
01601     }
01602     gl2psTraverseBspTree(tree->front, eye, epsilon, compare, action, inverse);
01603   }
01604   else if(GL_TRUE == compare(-epsilon, result)){
01605     gl2psTraverseBspTree(tree->front, eye, epsilon, compare, action, inverse);
01606     if(inverse){
01607       gl2psListActionInverse(tree->primitives, action);
01608     }
01609     else{
01610       gl2psListAction(tree->primitives, action);
01611     }
01612     gl2psTraverseBspTree(tree->back, eye, epsilon, compare, action, inverse);
01613   }
01614   else{
01615     gl2psTraverseBspTree(tree->front, eye, epsilon, compare, action, inverse);
01616     gl2psTraverseBspTree(tree->back, eye, epsilon, compare, action, inverse);
01617   }
01618 }
01619 
01620 static void gl2psRescaleAndOffset(void)
01621 {
01622   GL2PSprimitive *prim;
01623   GLfloat minZ, maxZ, rangeZ, scaleZ;
01624   GLfloat factor, units, area, dZ, dZdX, dZdY, maxdZ;
01625   int i, j;
01626 
01627   if(!gl2psListNbr(gl2ps->primitives))
01628     return;
01629 
01630   /* get z-buffer range */
01631   prim = *(GL2PSprimitive**)gl2psListPointer(gl2ps->primitives, 0);
01632   minZ = maxZ = prim->verts[0].xyz[2];
01633   for(i = 1; i < prim->numverts; i++){
01634     if(prim->verts[i].xyz[2] < minZ) minZ = prim->verts[i].xyz[2];
01635     if(prim->verts[i].xyz[2] > maxZ) maxZ = prim->verts[i].xyz[2];
01636   }
01637   for(i = 1; i < gl2psListNbr(gl2ps->primitives); i++){
01638     prim = *(GL2PSprimitive**)gl2psListPointer(gl2ps->primitives, i);
01639     for(j = 0; j < prim->numverts; j++){
01640       if(prim->verts[j].xyz[2] < minZ) minZ = prim->verts[j].xyz[2];
01641       if(prim->verts[j].xyz[2] > maxZ) maxZ = prim->verts[j].xyz[2];
01642     }
01643   }
01644   rangeZ = (maxZ - minZ);
01645 
01646   /* rescale z-buffer coordinate in [0,GL2PS_ZSCALE], to make it of
01647      the same order of magnitude as the x and y coordinates */
01648   scaleZ = GL2PS_ZERO(rangeZ) ? GL2PS_ZSCALE : (GL2PS_ZSCALE / rangeZ);
01649   /* avoid precision loss (we use floats!) */
01650   if(scaleZ > 100000.F) scaleZ = 100000.F;
01651 
01652   /* apply offsets */
01653   for(i = 0; i < gl2psListNbr(gl2ps->primitives); i++){
01654     prim = *(GL2PSprimitive**)gl2psListPointer(gl2ps->primitives, i);
01655     for(j = 0; j < prim->numverts; j++){
01656       prim->verts[j].xyz[2] = (prim->verts[j].xyz[2] - minZ) * scaleZ;
01657     }
01658     if((gl2ps->options & GL2PS_SIMPLE_LINE_OFFSET) &&
01659        (prim->type == GL2PS_LINE)){
01660       if(gl2ps->sort == GL2PS_SIMPLE_SORT){
01661         prim->verts[0].xyz[2] -= GL2PS_ZOFFSET_LARGE;
01662         prim->verts[1].xyz[2] -= GL2PS_ZOFFSET_LARGE;
01663       }
01664       else{
01665         prim->verts[0].xyz[2] -= GL2PS_ZOFFSET;
01666         prim->verts[1].xyz[2] -= GL2PS_ZOFFSET;
01667       }
01668     }
01669     else if(prim->offset && (prim->type == GL2PS_TRIANGLE)){
01670       factor = gl2ps->offset[0];
01671       units = gl2ps->offset[1];
01672       area =
01673         (prim->verts[1].xyz[0] - prim->verts[0].xyz[0]) *
01674         (prim->verts[2].xyz[1] - prim->verts[1].xyz[1]) -
01675         (prim->verts[2].xyz[0] - prim->verts[1].xyz[0]) *
01676         (prim->verts[1].xyz[1] - prim->verts[0].xyz[1]);
01677       if(!GL2PS_ZERO(area)){
01678         dZdX =
01679           ((prim->verts[2].xyz[1] - prim->verts[1].xyz[1]) *
01680            (prim->verts[1].xyz[2] - prim->verts[0].xyz[2]) -
01681            (prim->verts[1].xyz[1] - prim->verts[0].xyz[1]) *
01682            (prim->verts[2].xyz[2] - prim->verts[1].xyz[2])) / area;
01683         dZdY =
01684           ((prim->verts[1].xyz[0] - prim->verts[0].xyz[0]) *
01685            (prim->verts[2].xyz[2] - prim->verts[1].xyz[2]) -
01686            (prim->verts[2].xyz[0] - prim->verts[1].xyz[0]) *
01687            (prim->verts[1].xyz[2] - prim->verts[0].xyz[2])) / area;
01688         maxdZ = (GLfloat)sqrt(dZdX * dZdX + dZdY * dZdY);
01689       }
01690       else{
01691         maxdZ = 0.0F;
01692       }
01693       dZ = factor * maxdZ + units;
01694       prim->verts[0].xyz[2] += dZ;
01695       prim->verts[1].xyz[2] += dZ;
01696       prim->verts[2].xyz[2] += dZ;
01697     }
01698   }
01699 }
01700 
01701 /*********************************************************************
01702  *
01703  * 2D sorting routines (for occlusion culling)
01704  *
01705  *********************************************************************/
01706 
01707 static GLint gl2psGetPlaneFromPoints(GL2PSxyz a, GL2PSxyz b, GL2PSplane plane)
01708 {
01709   GLfloat n;
01710 
01711   plane[0] = b[1] - a[1];
01712   plane[1] = a[0] - b[0];
01713   n = (GLfloat)sqrt(plane[0]*plane[0] + plane[1]*plane[1]);
01714   plane[2] = 0.0F;
01715   if(!GL2PS_ZERO(n)){
01716     plane[0] /= n;
01717     plane[1] /= n;
01718     plane[3] = -plane[0]*a[0]-plane[1]*a[1];
01719     return 1;
01720   }
01721   else{
01722     plane[0] = -1.0F;
01723     plane[1] = 0.0F;
01724     plane[3] = a[0];
01725     return 0;
01726   }
01727 }
01728 
01729 static void gl2psFreeBspImageTree(GL2PSbsptree2d **tree)
01730 {
01731   if(*tree){
01732     if((*tree)->back)  gl2psFreeBspImageTree(&(*tree)->back);
01733     if((*tree)->front) gl2psFreeBspImageTree(&(*tree)->front);
01734     gl2psFree(*tree);
01735     *tree = NULL;
01736   }
01737 }
01738 
01739 static GLint gl2psCheckPoint(GL2PSxyz point, GL2PSplane plane)
01740 {
01741   GLfloat pt_dis;
01742 
01743   pt_dis = gl2psComparePointPlane(point, plane);
01744   if(pt_dis > GL2PS_EPSILON)        return GL2PS_POINT_INFRONT;
01745   else if(pt_dis < -GL2PS_EPSILON)  return GL2PS_POINT_BACK;
01746   else                              return GL2PS_POINT_COINCIDENT;
01747 }
01748 
01749 static void gl2psAddPlanesInBspTreeImage(GL2PSprimitive *prim,
01750                                          GL2PSbsptree2d **tree)
01751 {
01752   GLint ret = 0;
01753   GLint i;
01754   GLint offset = 0;
01755   GL2PSbsptree2d *head = NULL, *cur = NULL;
01756 
01757   if((*tree == NULL) && (prim->numverts > 2)){
01758     /* don't cull if transparent
01759     for(i = 0; i < prim->numverts - 1; i++)
01760       if(prim->verts[i].rgba[3] < 1.0F) return;
01761     */
01762     head = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
01763     for(i = 0; i < prim->numverts-1; i++){
01764       if(!gl2psGetPlaneFromPoints(prim->verts[i].xyz,
01765                                   prim->verts[i+1].xyz,
01766                                   head->plane)){
01767         if(prim->numverts-i > 3){
01768           offset++;
01769         }
01770         else{
01771           gl2psFree(head);
01772           return;
01773         }
01774       }
01775       else{
01776         break;
01777       }
01778     }
01779     head->back = NULL;
01780     head->front = NULL;
01781     for(i = 2+offset; i < prim->numverts; i++){
01782       ret = gl2psCheckPoint(prim->verts[i].xyz, head->plane);
01783       if(ret != GL2PS_POINT_COINCIDENT) break;
01784     }
01785     switch(ret){
01786     case GL2PS_POINT_INFRONT :
01787       cur = head;
01788       for(i = 1+offset; i < prim->numverts-1; i++){
01789         if(cur->front == NULL){
01790           cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
01791         }
01792         if(gl2psGetPlaneFromPoints(prim->verts[i].xyz,
01793                                    prim->verts[i+1].xyz,
01794                                    cur->front->plane)){
01795           cur = cur->front;
01796           cur->front = NULL;
01797           cur->back = NULL;
01798         }
01799       }
01800       if(cur->front == NULL){
01801         cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
01802       }
01803       if(gl2psGetPlaneFromPoints(prim->verts[i].xyz,
01804                                  prim->verts[offset].xyz,
01805                                  cur->front->plane)){
01806         cur->front->front = NULL;
01807         cur->front->back = NULL;
01808       }
01809       else{
01810         gl2psFree(cur->front);
01811         cur->front = NULL;
01812       }
01813       break;
01814     case GL2PS_POINT_BACK :
01815       for(i = 0; i < 4; i++){
01816         head->plane[i] = -head->plane[i];
01817       }
01818       cur = head;
01819       for(i = 1+offset; i < prim->numverts-1; i++){
01820         if(cur->front == NULL){
01821           cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
01822         }
01823         if(gl2psGetPlaneFromPoints(prim->verts[i+1].xyz,
01824                                    prim->verts[i].xyz,
01825                                    cur->front->plane)){
01826           cur = cur->front;
01827           cur->front = NULL;
01828           cur->back = NULL;
01829         }
01830       }
01831       if(cur->front == NULL){
01832         cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
01833       }
01834       if(gl2psGetPlaneFromPoints(prim->verts[offset].xyz,
01835                                  prim->verts[i].xyz,
01836                                  cur->front->plane)){
01837         cur->front->front = NULL;
01838         cur->front->back = NULL;
01839       }
01840       else{
01841         gl2psFree(cur->front);
01842         cur->front = NULL;
01843       }
01844       break;
01845     default:
01846       gl2psFree(head);
01847       return;
01848     }
01849     (*tree) = head;
01850   }
01851 }
01852 
01853 static GLint gl2psCheckPrimitive(GL2PSprimitive *prim, GL2PSplane plane)
01854 {
01855   GLint i;
01856   GLint pos;
01857 
01858   pos = gl2psCheckPoint(prim->verts[0].xyz, plane);
01859   for(i = 1; i < prim->numverts; i++){
01860     pos |= gl2psCheckPoint(prim->verts[i].xyz, plane);
01861     if(pos == (GL2PS_POINT_INFRONT | GL2PS_POINT_BACK)) return GL2PS_SPANNING;
01862   }
01863   if(pos & GL2PS_POINT_INFRONT)   return GL2PS_IN_FRONT_OF;
01864   else if(pos & GL2PS_POINT_BACK) return GL2PS_IN_BACK_OF;
01865   else                            return GL2PS_COINCIDENT;
01866 }
01867 
01868 static GL2PSprimitive *gl2psCreateSplitPrimitive2D(GL2PSprimitive *parent,
01869                                                    GLshort numverts,
01870                                                    GL2PSvertex *vertx)
01871 {
01872   GLint i;
01873   GL2PSprimitive *child = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
01874 
01875   if(parent->type == GL2PS_IMAGEMAP){
01876     child->type = GL2PS_IMAGEMAP;
01877     child->data.image = parent->data.image;
01878   }
01879   else {
01880     switch(numverts){
01881     case 1 : child->type = GL2PS_POINT; break;
01882     case 2 : child->type = GL2PS_LINE; break;
01883     case 3 : child->type = GL2PS_TRIANGLE; break;
01884     case 4 : child->type = GL2PS_QUADRANGLE; break;
01885     default: child->type = GL2PS_NO_TYPE; break; /* FIXME */
01886     }
01887   }
01888   child->boundary = 0; /* FIXME: not done! */
01889   child->culled = parent->culled;
01890   child->offset = parent->offset;
01891   child->pattern = parent->pattern;
01892   child->factor = parent->factor;
01893   child->width = parent->width;
01894   child->numverts = numverts;
01895   child->verts = (GL2PSvertex*)gl2psMalloc(numverts * sizeof(GL2PSvertex));
01896   for(i = 0; i < numverts; i++){
01897     child->verts[i] = vertx[i];
01898   }
01899   return child;
01900 }
01901 
01902 static void gl2psSplitPrimitive2D(GL2PSprimitive *prim,
01903                                   GL2PSplane plane,
01904                                   GL2PSprimitive **front,
01905                                   GL2PSprimitive **back)
01906 {
01907   /* cur will hold the position of the current vertex
01908      prev will hold the position of the previous vertex
01909      prev0 will hold the position of the vertex number 0
01910      v1 and v2 represent the current and previous vertices, respectively
01911      flag is set if the current vertex should be checked against the plane */
01912   GLint cur = -1, prev = -1, i, v1 = 0, v2 = 0, flag = 1, prev0 = -1;
01913 
01914   /* list of vertices that will go in front and back primitive */
01915   GL2PSvertex *front_list = NULL, *back_list = NULL;
01916 
01917   /* number of vertices in front and back list */
01918   GLshort front_count = 0, back_count = 0;
01919 
01920   for(i = 0; i <= prim->numverts; i++){
01921     v1 = i;
01922     if(v1 == prim->numverts){
01923       if(prim->numverts < 3) break;
01924       v1 = 0;
01925       v2 = prim->numverts - 1;
01926       cur = prev0;
01927     }
01928     else if(flag){
01929       cur = gl2psCheckPoint(prim->verts[v1].xyz, plane);
01930       if(i == 0){
01931         prev0 = cur;
01932       }
01933     }
01934     if(((prev == -1) || (prev == cur) || (prev == 0) || (cur == 0)) &&
01935        (i < prim->numverts)){
01936       if(cur == GL2PS_POINT_INFRONT){
01937         front_count++;
01938         front_list = (GL2PSvertex*)gl2psRealloc(front_list,
01939                                                 sizeof(GL2PSvertex)*front_count);
01940         front_list[front_count-1] = prim->verts[v1];
01941       }
01942       else if(cur == GL2PS_POINT_BACK){
01943         back_count++;
01944         back_list = (GL2PSvertex*)gl2psRealloc(back_list,
01945                                                sizeof(GL2PSvertex)*back_count);
01946         back_list[back_count-1] = prim->verts[v1];
01947       }
01948       else{
01949         front_count++;
01950         front_list = (GL2PSvertex*)gl2psRealloc(front_list,
01951                                                 sizeof(GL2PSvertex)*front_count);
01952         front_list[front_count-1] = prim->verts[v1];
01953         back_count++;
01954         back_list = (GL2PSvertex*)gl2psRealloc(back_list,
01955                                                sizeof(GL2PSvertex)*back_count);
01956         back_list[back_count-1] = prim->verts[v1];
01957       }
01958       flag = 1;
01959     }
01960     else if((prev != cur) && (cur != 0) && (prev != 0)){
01961       if(v1 != 0){
01962         v2 = v1-1;
01963         i--;
01964       }
01965       front_count++;
01966       front_list = (GL2PSvertex*)gl2psRealloc(front_list,
01967                                               sizeof(GL2PSvertex)*front_count);
01968       gl2psCutEdge(&prim->verts[v2], &prim->verts[v1],
01969                    plane, &front_list[front_count-1]);
01970       back_count++;
01971       back_list = (GL2PSvertex*)gl2psRealloc(back_list,
01972                                              sizeof(GL2PSvertex)*back_count);
01973       back_list[back_count-1] = front_list[front_count-1];
01974       flag = 0;
01975     }
01976     prev = cur;
01977   }
01978   *front = gl2psCreateSplitPrimitive2D(prim, front_count, front_list);
01979   *back = gl2psCreateSplitPrimitive2D(prim, back_count, back_list);
01980   gl2psFree(front_list);
01981   gl2psFree(back_list);
01982 }
01983 
01984 static GLint gl2psAddInBspImageTree(GL2PSprimitive *prim, GL2PSbsptree2d **tree)
01985 {
01986   GLint ret = 0;
01987   GL2PSprimitive *frontprim = NULL, *backprim = NULL;
01988 
01989   /* FIXME: until we consider the actual extent of text strings and
01990      pixmaps, never cull them. Otherwise the whole string/pixmap gets
01991      culled as soon as the reference point is hidden */
01992   if(prim->type == GL2PS_PIXMAP ||
01993      prim->type == GL2PS_TEXT ||
01994      prim->type == GL2PS_SPECIAL){
01995     return 1;
01996   }
01997 
01998   if(*tree == NULL){
01999     if((prim->type != GL2PS_IMAGEMAP) && (GL_FALSE == gl2ps->zerosurfacearea)){
02000       gl2psAddPlanesInBspTreeImage(gl2ps->primitivetoadd, tree);
02001     }
02002     return 1;
02003   }
02004   else{
02005     switch(gl2psCheckPrimitive(prim, (*tree)->plane)){
02006     case GL2PS_IN_BACK_OF: return gl2psAddInBspImageTree(prim, &(*tree)->back);
02007     case GL2PS_IN_FRONT_OF:
02008       if((*tree)->front != NULL) return gl2psAddInBspImageTree(prim, &(*tree)->front);
02009       else                       return 0;
02010     case GL2PS_SPANNING:
02011       gl2psSplitPrimitive2D(prim, (*tree)->plane, &frontprim, &backprim);
02012       ret = gl2psAddInBspImageTree(backprim, &(*tree)->back);
02013       if((*tree)->front != NULL){
02014         if(gl2psAddInBspImageTree(frontprim, &(*tree)->front)){
02015           ret = 1;
02016         }
02017       }
02018       gl2psFree(frontprim->verts);
02019       gl2psFree(frontprim);
02020       gl2psFree(backprim->verts);
02021       gl2psFree(backprim);
02022       return ret;
02023     case GL2PS_COINCIDENT:
02024       if((*tree)->back != NULL){
02025         gl2ps->zerosurfacearea = GL_TRUE;
02026         ret = gl2psAddInBspImageTree(prim, &(*tree)->back);
02027         gl2ps->zerosurfacearea = GL_FALSE;
02028         if(ret) return ret;
02029       }
02030       if((*tree)->front != NULL){
02031         gl2ps->zerosurfacearea = GL_TRUE;
02032         ret = gl2psAddInBspImageTree(prim, &(*tree)->front);
02033         gl2ps->zerosurfacearea = GL_FALSE;
02034         if(ret) return ret;
02035       }
02036       if(prim->type == GL2PS_LINE) return 1;
02037       else                         return 0;
02038     }
02039   }
02040   return 0;
02041 }
02042 
02043 static void gl2psAddInImageTree(void *data)
02044 {
02045   GL2PSprimitive *prim = *(GL2PSprimitive **)data;
02046   gl2ps->primitivetoadd = prim;
02047   if(prim->type == GL2PS_IMAGEMAP && prim->data.image->format == GL2PS_IMAGEMAP_VISIBLE){
02048     prim->culled = 1;
02049   }
02050   else if(!gl2psAddInBspImageTree(prim, &gl2ps->imagetree)){
02051     prim->culled = 1;
02052   }
02053   else if(prim->type == GL2PS_IMAGEMAP){
02054     prim->data.image->format = GL2PS_IMAGEMAP_VISIBLE;
02055   }
02056 }
02057 
02058 /* Boundary construction */
02059 
02060 static void gl2psAddBoundaryInList(GL2PSprimitive *prim, GL2PSlist *list)
02061 {
02062   GL2PSprimitive *b;
02063   GLshort i;
02064   GL2PSxyz c;
02065 
02066   c[0] = c[1] = c[2] = 0.0F;
02067   for(i = 0; i < prim->numverts; i++){
02068     c[0] += prim->verts[i].xyz[0];
02069     c[1] += prim->verts[i].xyz[1];
02070   }
02071   c[0] /= prim->numverts;
02072   c[1] /= prim->numverts;
02073 
02074   for(i = 0; i < prim->numverts; i++){
02075     if(prim->boundary & (GLint)pow(2., i)){
02076       b = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
02077       b->type = GL2PS_LINE;
02078       b->offset = prim->offset;
02079       b->pattern = prim->pattern;
02080       b->factor = prim->factor;
02081       b->culled = prim->culled;
02082       b->width = prim->width;
02083       b->boundary = 0;
02084       b->numverts = 2;
02085       b->verts = (GL2PSvertex*)gl2psMalloc(2 * sizeof(GL2PSvertex));
02086 
02087 #if 0 /* FIXME: need to work on boundary offset... */
02088       v[0] = c[0] - prim->verts[i].xyz[0];
02089       v[1] = c[1] - prim->verts[i].xyz[1];
02090       v[2] = 0.0F;
02091       norm = gl2psNorm(v);
02092       v[0] /= norm;
02093       v[1] /= norm;
02094       b->verts[0].xyz[0] = prim->verts[i].xyz[0] +0.1*v[0];
02095       b->verts[0].xyz[1] = prim->verts[i].xyz[1] +0.1*v[1];
02096       b->verts[0].xyz[2] = prim->verts[i].xyz[2];
02097       v[0] = c[0] - prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[0];
02098       v[1] = c[1] - prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[1];
02099       norm = gl2psNorm(v);
02100       v[0] /= norm;
02101       v[1] /= norm;
02102       b->verts[1].xyz[0] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[0] +0.1*v[0];
02103       b->verts[1].xyz[1] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[1] +0.1*v[1];
02104       b->verts[1].xyz[2] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[2];
02105 #else
02106       b->verts[0].xyz[0] = prim->verts[i].xyz[0];
02107       b->verts[0].xyz[1] = prim->verts[i].xyz[1];
02108       b->verts[0].xyz[2] = prim->verts[i].xyz[2];
02109       b->verts[1].xyz[0] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[0];
02110       b->verts[1].xyz[1] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[1];
02111       b->verts[1].xyz[2] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[2];
02112 #endif
02113 
02114       b->verts[0].rgba[0] = 0.0F;
02115       b->verts[0].rgba[1] = 0.0F;
02116       b->verts[0].rgba[2] = 0.0F;
02117       b->verts[0].rgba[3] = 0.0F;
02118       b->verts[1].rgba[0] = 0.0F;
02119       b->verts[1].rgba[1] = 0.0F;
02120       b->verts[1].rgba[2] = 0.0F;
02121       b->verts[1].rgba[3] = 0.0F;
02122       gl2psListAdd(list, &b);
02123     }
02124   }
02125 
02126 }
02127 
02128 static void gl2psBuildPolygonBoundary(GL2PSbsptree *tree)
02129 {
02130   GLint i;
02131   GL2PSprimitive *prim;
02132 
02133   if(!tree) return;
02134   gl2psBuildPolygonBoundary(tree->back);
02135   for(i = 0; i < gl2psListNbr(tree->primitives); i++){
02136     prim = *(GL2PSprimitive**)gl2psListPointer(tree->primitives, i);
02137     if(prim->boundary) gl2psAddBoundaryInList(prim, tree->primitives);
02138   }
02139   gl2psBuildPolygonBoundary(tree->front);
02140 }
02141 
02142 /*********************************************************************
02143  *
02144  * Feedback buffer parser
02145  *
02146  *********************************************************************/
02147 
02148 static void gl2psAddPolyPrimitive(GLshort type, GLshort numverts,
02149                                   GL2PSvertex *verts, GLint offset,
02150                                   GLushort pattern, GLint factor,
02151                                   GLfloat width, char boundary)
02152 {
02153   GL2PSprimitive *prim;
02154 
02155   prim = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
02156   prim->type = type;
02157   prim->numverts = numverts;
02158   prim->verts = (GL2PSvertex*)gl2psMalloc(numverts * sizeof(GL2PSvertex));
02159   memcpy(prim->verts, verts, numverts * sizeof(GL2PSvertex));
02160   prim->boundary = boundary;
02161   prim->offset = offset;
02162   prim->pattern = pattern;
02163   prim->factor = factor;
02164   prim->width = width;
02165   prim->culled = 0;
02166 
02167   /* FIXME: here we should have an option to split stretched
02168      tris/quads to enhance SIMPLE_SORT */
02169 
02170   gl2psListAdd(gl2ps->primitives, &prim);
02171 }
02172 
02173 static GLint gl2psGetVertex(GL2PSvertex *v, GLfloat *p)
02174 {
02175   GLint i;
02176 
02177   v->xyz[0] = p[0];
02178   v->xyz[1] = p[1];
02179   v->xyz[2] = p[2];
02180 
02181   if(gl2ps->colormode == GL_COLOR_INDEX && gl2ps->colorsize > 0){
02182     i = (GLint)(p[3] + 0.5);
02183     v->rgba[0] = gl2ps->colormap[i][0];
02184     v->rgba[1] = gl2ps->colormap[i][1];
02185     v->rgba[2] = gl2ps->colormap[i][2];
02186     v->rgba[3] = gl2ps->colormap[i][3];
02187     return 4;
02188   }
02189   else{
02190     v->rgba[0] = p[3];
02191     v->rgba[1] = p[4];
02192     v->rgba[2] = p[5];
02193     v->rgba[3] = p[6];
02194     return 7;
02195   }
02196 }
02197 
02198 static void gl2psParseFeedbackBuffer(GLint used)
02199 {
02200   char flag;
02201   GLushort pattern = 0;
02202   GLboolean boundary;
02203   GLint i, sizeoffloat, count, v, vtot, offset = 0, factor = 0, auxindex = 0;
02204   GLfloat lwidth = 1.0F, psize = 1.0F;
02205   GLfloat *current;
02206   GL2PSvertex vertices[3];
02207   GL2PSprimitive *prim;
02208   GL2PSimagemap *node;
02209 
02210   current = gl2ps->feedback;
02211   boundary = gl2ps->boundary = GL_FALSE;
02212 
02213   while(used > 0){
02214 
02215     if(GL_TRUE == boundary) gl2ps->boundary = GL_TRUE;
02216 
02217     switch((GLint)*current){
02218     case GL_POINT_TOKEN :
02219       current ++;
02220       used --;
02221       i = gl2psGetVertex(&vertices[0], current);
02222       current += i;
02223       used    -= i;
02224       gl2psAddPolyPrimitive(GL2PS_POINT, 1, vertices, 0,
02225                             pattern, factor, psize, 0);
02226       break;
02227     case GL_LINE_TOKEN :
02228     case GL_LINE_RESET_TOKEN :
02229       current ++;
02230       used --;
02231       i = gl2psGetVertex(&vertices[0], current);
02232       current += i;
02233       used    -= i;
02234       i = gl2psGetVertex(&vertices[1], current);
02235       current += i;
02236       used    -= i;
02237       gl2psAddPolyPrimitive(GL2PS_LINE, 2, vertices, 0,
02238                             pattern, factor, lwidth, 0);
02239       break;
02240     case GL_POLYGON_TOKEN :
02241       count = (GLint)current[1];
02242       current += 2;
02243       used -= 2;
02244       v = vtot = 0;
02245       while(count > 0 && used > 0){
02246         i = gl2psGetVertex(&vertices[v], current);
02247         gl2psAdaptVertexForBlending(&vertices[v]);
02248         current += i;
02249         used    -= i;
02250         count --;
02251         vtot++;
02252         if(v == 2){
02253           if(GL_TRUE == boundary){
02254             if(!count && vtot == 2) flag = 1|2|4;
02255             else if(!count) flag = 2|4;
02256             else if(vtot == 2) flag = 1|2;
02257             else flag = 2;
02258           }
02259           else
02260             flag = 0;
02261           gl2psAddPolyPrimitive(GL2PS_TRIANGLE, 3, vertices, offset,
02262                                 pattern, factor, 1, flag);
02263           vertices[1] = vertices[2];
02264         }
02265         else
02266           v ++;
02267       }
02268       break;
02269     case GL_BITMAP_TOKEN :
02270     case GL_DRAW_PIXEL_TOKEN :
02271     case GL_COPY_PIXEL_TOKEN :
02272       current ++;
02273       used --;
02274       i = gl2psGetVertex(&vertices[0], current);
02275       current += i;
02276       used    -= i;
02277       break;
02278     case GL_PASS_THROUGH_TOKEN :
02279       switch((GLint)current[1]){
02280       case GL2PS_BEGIN_OFFSET_TOKEN : offset = 1; break;
02281       case GL2PS_END_OFFSET_TOKEN : offset = 0; break;
02282       case GL2PS_BEGIN_BOUNDARY_TOKEN : boundary = GL_TRUE; break;
02283       case GL2PS_END_BOUNDARY_TOKEN : boundary = GL_FALSE; break;
02284       case GL2PS_END_STIPPLE_TOKEN : pattern = factor = 0; break;
02285       case GL2PS_BEGIN_BLEND_TOKEN : gl2ps->blending = GL_TRUE; break;
02286       case GL2PS_END_BLEND_TOKEN : gl2ps->blending = GL_FALSE; break;
02287       case GL2PS_BEGIN_STIPPLE_TOKEN :
02288         current += 2;
02289         used -= 2;
02290         pattern = (GLushort)current[1];
02291         current += 2;
02292         used -= 2;
02293         factor = (GLint)current[1];
02294         break;
02295       case GL2PS_SRC_BLEND_TOKEN :
02296         current += 2;
02297         used -= 2;
02298         gl2ps->blendfunc[0] = (GLint)current[1];
02299         break;
02300       case GL2PS_DST_BLEND_TOKEN :
02301         current += 2;
02302         used -= 2;
02303         gl2ps->blendfunc[1] = (GLint)current[1];
02304         break;
02305       case GL2PS_POINT_SIZE_TOKEN :
02306         current += 2;
02307         used -= 2;
02308         psize = current[1];
02309         break;
02310       case GL2PS_LINE_WIDTH_TOKEN :
02311         current += 2;
02312         used -= 2;
02313         lwidth = current[1];
02314         break;
02315       case GL2PS_IMAGEMAP_TOKEN :
02316         prim = (GL2PSprimitive *)gl2psMalloc(sizeof(GL2PSprimitive));
02317         prim->type = GL2PS_IMAGEMAP;
02318         prim->boundary = 0;
02319         prim->numverts = 4;
02320         prim->verts = (GL2PSvertex *)gl2psMalloc(4 * sizeof(GL2PSvertex));
02321         prim->culled = 0;
02322         prim->offset = 0;
02323         prim->pattern = 0;
02324         prim->factor = 0;
02325         prim->width = 1;
02326 
02327         node = (GL2PSimagemap*)gl2psMalloc(sizeof(GL2PSimagemap));
02328         node->image = (GL2PSimage*)gl2psMalloc(sizeof(GL2PSimage));
02329         node->image->type = 0;
02330         node->image->format = 0;
02331         node->image->zoom_x = 1.0F;
02332         node->image->zoom_y = 1.0F;
02333         node->next = NULL;
02334 
02335         if(gl2ps->imagemap_head == NULL)
02336           gl2ps->imagemap_head = node;
02337         else
02338           gl2ps->imagemap_tail->next = node;
02339         gl2ps->imagemap_tail = node;
02340         prim->data.image = node->image;
02341 
02342         current += 2; used -= 2;
02343         i = gl2psGetVertex(&prim->verts[0], &current[1]);
02344         current += i; used -= i;
02345 
02346         node->image->width = (GLint)current[2];
02347         current += 2; used -= 2;
02348         node->image->height = (GLint)current[2];
02349         prim->verts[0].xyz[0] = prim->verts[0].xyz[0] - (int)(node->image->width / 2) + 0.5F;
02350         prim->verts[0].xyz[1] = prim->verts[0].xyz[1] - (int)(node->image->height / 2) + 0.5F;
02351         for(i = 1; i < 4; i++){
02352           for(v = 0; v < 3; v++){
02353             prim->verts[i].xyz[v] = prim->verts[0].xyz[v];
02354             prim->verts[i].rgba[v] = prim->verts[0].rgba[v];
02355           }
02356           prim->verts[i].rgba[v] = prim->verts[0].rgba[v];
02357         }
02358         prim->verts[1].xyz[0] = prim->verts[1].xyz[0] + node->image->width;
02359         prim->verts[2].xyz[0] = prim->verts[1].xyz[0];
02360         prim->verts[2].xyz[1] = prim->verts[2].xyz[1] + node->image->height;
02361         prim->verts[3].xyz[1] = prim->verts[2].xyz[1];
02362 
02363         sizeoffloat = sizeof(GLfloat);
02364         v = 2 * sizeoffloat;
02365         vtot = node->image->height + node->image->height *
02366           ((node->image->width - 1) / 8);
02367         node->image->pixels = (GLfloat*)gl2psMalloc(v + vtot);
02368         node->image->pixels[0] = prim->verts[0].xyz[0];
02369         node->image->pixels[1] = prim->verts[0].xyz[1];
02370 
02371         for(i = 0; i < vtot; i += sizeoffloat){
02372           current += 2; used -= 2;
02373           if((vtot - i) >= 4)
02374             memcpy(&(((char*)(node->image->pixels))[i + v]), &(current[2]), sizeoffloat);
02375           else
02376             memcpy(&(((char*)(node->image->pixels))[i + v]), &(current[2]), vtot - i);
02377         }
02378         current++; used--;
02379         gl2psListAdd(gl2ps->primitives, &prim);
02380         break;
02381       case GL2PS_DRAW_PIXELS_TOKEN :
02382       case GL2PS_TEXT_TOKEN :
02383         if(auxindex < gl2psListNbr(gl2ps->auxprimitives))
02384           gl2psListAdd(gl2ps->primitives,
02385                        gl2psListPointer(gl2ps->auxprimitives, auxindex++));
02386         else
02387           gl2psMsg(GL2PS_ERROR, "Wrong number of auxiliary tokens in buffer");
02388         break;
02389       }
02390       current += 2;
02391       used -= 2;
02392       break;
02393     default :
02394       gl2psMsg(GL2PS_WARNING, "Unknown token in buffer");
02395       current ++;
02396       used --;
02397       break;
02398     }
02399   }
02400 
02401   gl2psListReset(gl2ps->auxprimitives);
02402 }
02403 
02404 /*********************************************************************
02405  *
02406  * PostScript routines
02407  *
02408  *********************************************************************/
02409 
02410 static void gl2psWriteByte(unsigned char byte)
02411 {
02412   unsigned char h = byte / 16;
02413   unsigned char l = byte % 16;
02414   gl2psPrintf("%x%x", h, l);
02415 }
02416 
02417 static void gl2psPrintPostScriptPixmap(GLfloat x, GLfloat y, GL2PSimage *im)
02418 {
02419   GLuint nbhex, nbyte, nrgb, nbits;
02420   GLuint row, col, ibyte, icase;
02421   GLfloat dr, dg, db, fgrey;
02422   unsigned char red = 0, green = 0, blue = 0, b, grey;
02423   GLuint width = (GLuint)im->width;
02424   GLuint height = (GLuint)im->height;
02425 
02426   /* FIXME: should we define an option for these? Or just keep the
02427      8-bit per component case? */
02428   int greyscale = 0; /* set to 1 to output greyscale image */
02429   int nbit = 8; /* number of bits per color compoment (2, 4 or 8) */
02430 
02431   if((width <= 0) || (height <= 0)) return;
02432 
02433   gl2psPrintf("gsave\n");
02434   gl2psPrintf("%.2f %.2f translate\n", x, y);
02435   gl2psPrintf("%.2f %.2f scale\n", width * im->zoom_x, height * im->zoom_y);
02436 
02437   if(greyscale){ /* greyscale */
02438     gl2psPrintf("/picstr %d string def\n", width);
02439     gl2psPrintf("%d %d %d\n", width, height, 8);
02440     gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", width, height, height);
02441     gl2psPrintf("{ currentfile picstr readhexstring pop }\n");
02442     gl2psPrintf("image\n");
02443     for(row = 0; row < height; row++){
02444       for(col = 0; col < width; col++){
02445         gl2psGetRGB(im, col, row, &dr, &dg, &db);
02446         fgrey = (0.30F * dr + 0.59F * dg + 0.11F * db);
02447         grey = (unsigned char)(255. * fgrey);
02448         gl2psWriteByte(grey);
02449       }
02450       gl2psPrintf("\n");
02451     }
02452     nbhex = width * height * 2;
02453     gl2psPrintf("%%%% nbhex digit          :%d\n", nbhex);
02454   }
02455   else if(nbit == 2){ /* color, 2 bits for r and g and b; rgbs following each other */
02456     nrgb = width  * 3;
02457     nbits = nrgb * nbit;
02458     nbyte = nbits / 8;
02459     if((nbyte * 8) != nbits) nbyte++;
02460     gl2psPrintf("/rgbstr %d string def\n", nbyte);
02461     gl2psPrintf("%d %d %d\n", width, height, nbit);
02462     gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", width, height, height);
02463     gl2psPrintf("{ currentfile rgbstr readhexstring pop }\n");
02464     gl2psPrintf("false 3\n");
02465     gl2psPrintf("colorimage\n");
02466     for(row = 0; row < height; row++){
02467       icase = 1;
02468       col = 0;
02469       b = 0;
02470       for(ibyte = 0; ibyte < nbyte; ibyte++){
02471         if(icase == 1) {
02472           if(col < width) {
02473             gl2psGetRGB(im, col, row, &dr, &dg, &db);
02474           }
02475           else {
02476             dr = dg = db = 0;
02477           }
02478           col++;
02479           red = (unsigned char)(3. * dr);
02480           green = (unsigned char)(3. * dg);
02481           blue = (unsigned char)(3. * db);
02482           b = red;
02483           b = (b<<2) + green;
02484           b = (b<<2) + blue;
02485           if(col < width) {
02486             gl2psGetRGB(im, col, row, &dr, &dg, &db);
02487           }
02488           else {
02489             dr = dg = db = 0;
02490           }
02491           col++;
02492           red = (unsigned char)(3. * dr);
02493           green = (unsigned char)(3. * dg);
02494           blue = (unsigned char)(3. * db);
02495           b = (b<<2) + red;
02496           gl2psWriteByte(b);
02497           b = 0;
02498           icase++;
02499         }
02500         else if(icase == 2) {
02501           b = green;
02502           b = (b<<2) + blue;
02503           if(col < width) {
02504             gl2psGetRGB(im, col, row, &dr, &dg, &db);
02505           }
02506           else {
02507             dr = dg = db = 0;
02508           }
02509           col++;
02510           red = (unsigned char)(3. * dr);
02511           green = (unsigned char)(3. * dg);
02512           blue = (unsigned char)(3. * db);
02513           b = (b<<2) + red;
02514           b = (b<<2) + green;
02515           gl2psWriteByte(b);
02516           b = 0;
02517           icase++;
02518         }
02519         else if(icase == 3) {
02520           b = blue;
02521           if(col < width) {
02522             gl2psGetRGB(im, col, row, &dr, &dg, &db);
02523           }
02524           else {
02525             dr = dg = db = 0;
02526           }
02527           col++;
02528           red = (unsigned char)(3. * dr);
02529           green = (unsigned char)(3. * dg);
02530           blue = (unsigned char)(3. * db);
02531           b = (b<<2) + red;
02532           b = (b<<2) + green;
02533           b = (b<<2) + blue;
02534           gl2psWriteByte(b);
02535           b = 0;
02536           icase = 1;
02537         }
02538       }
02539       gl2psPrintf("\n");
02540     }
02541   }
02542   else if(nbit == 4){ /* color, 4 bits for r and g and b; rgbs following each other */
02543     nrgb = width  * 3;
02544     nbits = nrgb * nbit;
02545     nbyte = nbits / 8;
02546     if((nbyte * 8) != nbits) nbyte++;
02547     gl2psPrintf("/rgbstr %d string def\n", nbyte);
02548     gl2psPrintf("%d %d %d\n", width, height, nbit);
02549     gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", width, height, height);
02550     gl2psPrintf("{ currentfile rgbstr readhexstring pop }\n");
02551     gl2psPrintf("false 3\n");
02552     gl2psPrintf("colorimage\n");
02553     for(row = 0; row < height; row++){
02554       col = 0;
02555       icase = 1;
02556       for(ibyte = 0; ibyte < nbyte; ibyte++){
02557         if(icase == 1) {
02558           if(col < width) {
02559             gl2psGetRGB(im, col, row, &dr, &dg, &db);
02560           }
02561           else {
02562             dr = dg = db = 0;
02563           }
02564           col++;
02565           red = (unsigned char)(15. * dr);
02566           green = (unsigned char)(15. * dg);
02567           gl2psPrintf("%x%x", red, green);
02568           icase++;
02569         }
02570         else if(icase == 2) {
02571           blue = (unsigned char)(15. * db);
02572           if(col < width) {
02573             gl2psGetRGB(im, col, row, &dr, &dg, &db);
02574           }
02575           else {
02576             dr = dg = db = 0;
02577           }
02578           col++;
02579           red = (unsigned char)(15. * dr);
02580           gl2psPrintf("%x%x", blue, red);
02581           icase++;
02582         }
02583         else if(icase == 3) {
02584           green = (unsigned char)(15. * dg);
02585           blue = (unsigned char)(15. * db);
02586           gl2psPrintf("%x%x", green, blue);
02587           icase = 1;
02588         }
02589       }
02590       gl2psPrintf("\n");
02591     }
02592   }
02593   else{ /* 8 bit for r and g and b */
02594     nbyte = width * 3;
02595     gl2psPrintf("/rgbstr %d string def\n", nbyte);
02596     gl2psPrintf("%d %d %d\n", width, height, 8);
02597     gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", width, height, height);
02598     gl2psPrintf("{ currentfile rgbstr readhexstring pop }\n");
02599     gl2psPrintf("false 3\n");
02600     gl2psPrintf("colorimage\n");
02601     for(row = 0; row < height; row++){
02602       for(col = 0; col < width; col++){
02603         gl2psGetRGB(im, col, row, &dr, &dg, &db);
02604         red = (unsigned char)(255. * dr);
02605         gl2psWriteByte(red);
02606         green = (unsigned char)(255. * dg);
02607         gl2psWriteByte(green);
02608         blue = (unsigned char)(255. * db);
02609         gl2psWriteByte(blue);
02610       }
02611       gl2psPrintf("\n");
02612     }
02613   }
02614 
02615   gl2psPrintf("grestore\n");
02616 }
02617 
02618 static void gl2psPrintPostScriptImagemap(GLfloat x, GLfloat y,
02619                                          GLsizei width, GLsizei height,
02620                                          const unsigned char *imagemap){
02621   int i, size;
02622 
02623   if((width <= 0) || (height <= 0)) return;
02624 
02625   size = height + height * (width - 1) / 8;
02626 
02627   gl2psPrintf("gsave\n");
02628   gl2psPrintf("%.2f %.2f translate\n", x, y);
02629   gl2psPrintf("%d %d scale\n%d %d\ntrue\n", width, height,width, height);
02630   gl2psPrintf("[ %d 0 0 -%d 0 %d ] {<", width, height);
02631   for(i = 0; i < size; i++){
02632     gl2psWriteByte(*imagemap);
02633     imagemap++;
02634   }
02635   gl2psPrintf(">} imagemask\ngrestore\n");
02636 }
02637 
02638 static void gl2psPrintPostScriptHeader(void)
02639 {
02640   time_t now;
02641 
02642   /* Since compression is not part of the PostScript standard,
02643      compressed PostScript files are just gzipped PostScript files
02644      ("ps.gz" or "eps.gz") */
02645   gl2psPrintGzipHeader();
02646 
02647   time(&now);
02648 
02649   if(gl2ps->format == GL2PS_PS){
02650     gl2psPrintf("%%!PS-Adobe-3.0\n");
02651   }
02652   else{
02653     gl2psPrintf("%%!PS-Adobe-3.0 EPSF-3.0\n");
02654   }
02655 
02656   gl2psPrintf("%%%%Title: %s\n"
02657               "%%%%Creator: GL2PS %d.%d.%d%s, %s\n"
02658               "%%%%For: %s\n"
02659               "%%%%CreationDate: %s"
02660               "%%%%LanguageLevel: 3\n"
02661               "%%%%DocumentData: Clean7Bit\n"
02662               "%%%%Pages: 1\n",
02663               gl2ps->title, GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION,
02664               GL2PS_PATCH_VERSION, GL2PS_EXTRA_VERSION, GL2PS_COPYRIGHT,
02665               gl2ps->producer, ctime(&now));
02666 
02667   if(gl2ps->format == GL2PS_PS){
02668     gl2psPrintf("%%%%Orientation: %s\n"
02669                 "%%%%DocumentMedia: Default %d %d 0 () ()\n",
02670                 (gl2ps->options & GL2PS_LANDSCAPE) ? "Landscape" : "Portrait",
02671                 (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[3] :
02672                 (int)gl2ps->viewport[2],
02673                 (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[2] :
02674                 (int)gl2ps->viewport[3]);
02675   }
02676 
02677   gl2psPrintf("%%%%BoundingBox: %d %d %d %d\n"
02678               "%%%%EndComments\n",
02679               (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[1] :
02680               (int)gl2ps->viewport[0],
02681               (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[0] :
02682               (int)gl2ps->viewport[1],
02683               (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[3] :
02684               (int)gl2ps->viewport[2],
02685               (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[2] :
02686               (int)gl2ps->viewport[3]);
02687 
02688   /* RGB color: r g b C (replace C by G in output to change from rgb to gray)
02689      Grayscale: r g b G
02690      Font choose: size fontname FC
02691      Text string: (string) x y size fontname S??
02692      Rotated text string: (string) angle x y size fontname S??R
02693      Point primitive: x y size P
02694      Line width: width W
02695      Line start: x y LS
02696      Line joining last point: x y L
02697      Line end: x y LE
02698      Flat-shaded triangle: x3 y3 x2 y2 x1 y1 T
02699      Smooth-shaded triangle: x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 ST */
02700 
02701   gl2psPrintf("%%%%BeginProlog\n"
02702               "/gl2psdict 64 dict def gl2psdict begin\n"
02703               "0 setlinecap 0 setlinejoin\n"
02704               "/tryPS3shading %s def %% set to false to force subdivision\n"
02705               "/rThreshold %g def %% red component subdivision threshold\n"
02706               "/gThreshold %g def %% green component subdivision threshold\n"
02707               "/bThreshold %g def %% blue component subdivision threshold\n",
02708               (gl2ps->options & GL2PS_NO_PS3_SHADING) ? "false" : "true",
02709               gl2ps->threshold[0], gl2ps->threshold[1], gl2ps->threshold[2]);
02710 
02711   gl2psPrintf("/BD { bind def } bind def\n"
02712               "/C  { setrgbcolor } BD\n"
02713               "/G  { 0.082 mul exch 0.6094 mul add exch 0.3086 mul add neg 1.0 add setgray } BD\n"
02714               "/W  { setlinewidth } BD\n");
02715 
02716   gl2psPrintf("/FC { findfont exch /SH exch def SH scalefont setfont } BD\n"
02717               "/SW { dup stringwidth pop } BD\n"
02718               "/S  { FC moveto show } BD\n"
02719               "/SBC{ FC moveto SW -2 div 0 rmoveto show } BD\n"
02720               "/SBR{ FC moveto SW neg 0 rmoveto show } BD\n"
02721               "/SCL{ FC moveto 0 SH -2 div rmoveto show } BD\n"
02722               "/SCC{ FC moveto SW -2 div SH -2 div rmoveto show } BD\n"
02723               "/SCR{ FC moveto SW neg SH -2 div rmoveto show } BD\n"
02724               "/STL{ FC moveto 0 SH neg rmoveto show } BD\n"
02725               "/STC{ FC moveto SW -2 div SH neg rmoveto show } BD\n"
02726               "/STR{ FC moveto SW neg SH neg rmoveto show } BD\n");
02727 
02728   /* rotated text routines: same nameanem with R appended */
02729 
02730   gl2psPrintf("/FCT { FC translate 0 0 } BD\n"
02731               "/SR  { gsave FCT moveto rotate show grestore } BD\n"
02732               "/SBCR{ gsave FCT moveto rotate SW -2 div 0 rmoveto show grestore } BD\n"
02733               "/SBRR{ gsave FCT moveto rotate SW neg 0 rmoveto show grestore } BD\n"
02734               "/SCLR{ gsave FCT moveto rotate 0 SH -2 div rmoveto show grestore} BD\n");
02735   gl2psPrintf("/SCCR{ gsave FCT moveto rotate SW -2 div SH -2 div rmoveto show grestore} BD\n"
02736               "/SCRR{ gsave FCT moveto rotate SW neg SH -2 div rmoveto show grestore} BD\n"
02737               "/STLR{ gsave FCT moveto rotate 0 SH neg rmoveto show grestore } BD\n"
02738               "/STCR{ gsave FCT moveto rotate SW -2 div SH neg rmoveto show grestore } BD\n"
02739               "/STRR{ gsave FCT moveto rotate SW neg SH neg rmoveto show grestore } BD\n");
02740 
02741   gl2psPrintf("/P  { newpath 0.0 360.0 arc closepath fill } BD\n"
02742               "/LS { newpath moveto } BD\n"
02743               "/L  { lineto } BD\n"
02744               "/LE { lineto stroke } BD\n"
02745               "/T  { newpath moveto lineto lineto closepath fill } BD\n");
02746 
02747   /* Smooth-shaded triangle with PostScript level 3 shfill operator:
02748         x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 STshfill */
02749 
02750   gl2psPrintf("/STshfill {\n"
02751               "      /b1 exch def /g1 exch def /r1 exch def /y1 exch def /x1 exch def\n"
02752               "      /b2 exch def /g2 exch def /r2 exch def /y2 exch def /x2 exch def\n"
02753               "      /b3 exch def /g3 exch def /r3 exch def /y3 exch def /x3 exch def\n"
02754               "      gsave << /ShadingType 4 /ColorSpace [/DeviceRGB]\n"
02755               "      /DataSource [ 0 x1 y1 r1 g1 b1 0 x2 y2 r2 g2 b2 0 x3 y3 r3 g3 b3 ] >>\n"
02756               "      shfill grestore } BD\n");
02757 
02758   /* Flat-shaded triangle with middle color:
02759         x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 Tm */
02760 
02761   gl2psPrintf(/* stack : x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 */
02762               "/Tm { 3 -1 roll 8 -1 roll 13 -1 roll add add 3 div\n" /* r = (r1+r2+r3)/3 */
02763               /* stack : x3 y3 g3 b3 x2 y2 g2 b2 x1 y1 g1 b1 r */
02764               "      3 -1 roll 7 -1 roll 11 -1 roll add add 3 div\n" /* g = (g1+g2+g3)/3 */
02765               /* stack : x3 y3 b3 x2 y2 b2 x1 y1 b1 r g b */
02766               "      3 -1 roll 6 -1 roll 9 -1 roll add add 3 div" /* b = (b1+b2+b3)/3 */
02767               /* stack : x3 y3 x2 y2 x1 y1 r g b */
02768               " C T } BD\n");
02769 
02770   /* Split triangle in four sub-triangles (at sides middle points) and call the
02771      STnoshfill procedure on each, interpolating the colors in RGB space:
02772         x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 STsplit
02773      (in procedure comments key: (Vi) = xi yi ri gi bi) */
02774 
02775   gl2psPrintf("/STsplit {\n"
02776               "      4 index 15 index add 0.5 mul\n" /* x13 = (x1+x3)/2 */
02777               "      4 index 15 index add 0.5 mul\n" /* y13 = (y1+y3)/2 */
02778               "      4 index 15 index add 0.5 mul\n" /* r13 = (r1+r3)/2 */
02779               "      4 index 15 index add 0.5 mul\n" /* g13 = (g1+g3)/2 */
02780               "      4 index 15 index add 0.5 mul\n" /* b13 = (b1+b3)/2 */
02781               "      5 copy 5 copy 25 15 roll\n");
02782 
02783   /* at his point, stack = (V3) (V13) (V13) (V13) (V2) (V1) */
02784 
02785   gl2psPrintf("      9 index 30 index add 0.5 mul\n" /* x23 = (x2+x3)/2 */
02786               "      9 index 30 index add 0.5 mul\n" /* y23 = (y2+y3)/2 */
02787               "      9 index 30 index add 0.5 mul\n" /* r23 = (r2+r3)/2 */
02788               "      9 index 30 index add 0.5 mul\n" /* g23 = (g2+g3)/2 */
02789               "      9 index 30 index add 0.5 mul\n" /* b23 = (b2+b3)/2 */
02790               "      5 copy 5 copy 35 5 roll 25 5 roll 15 5 roll\n");
02791 
02792   /* stack = (V3) (V13) (V23) (V13) (V23) (V13) (V23) (V2) (V1) */
02793 
02794   gl2psPrintf("      4 index 10 index add 0.5 mul\n" /* x12 = (x1+x2)/2 */
02795               "      4 index 10 index add 0.5 mul\n" /* y12 = (y1+y2)/2 */
02796               "      4 index 10 index add 0.5 mul\n" /* r12 = (r1+r2)/2 */
02797               "      4 index 10 index add 0.5 mul\n" /* g12 = (g1+g2)/2 */
02798               "      4 index 10 index add 0.5 mul\n" /* b12 = (b1+b2)/2 */
02799               "      5 copy 5 copy 40 5 roll 25 5 roll 15 5 roll 25 5 roll\n");
02800 
02801   /* stack = (V3) (V13) (V23) (V13) (V12) (V23) (V13) (V1) (V12) (V23) (V12) (V2) */
02802 
02803   gl2psPrintf("      STnoshfill STnoshfill STnoshfill STnoshfill } BD\n");
02804 
02805   /* Gouraud shaded triangle using recursive subdivision until the difference
02806      between corner colors does not exceed the thresholds:
02807         x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 STnoshfill  */
02808 
02809   gl2psPrintf("/STnoshfill {\n"
02810               "      2 index 8 index sub abs rThreshold gt\n" /* |r1-r2|>rth */
02811               "      { STsplit }\n"
02812               "      { 1 index 7 index sub abs gThreshold gt\n" /* |g1-g2|>gth */
02813               "        { STsplit }\n"
02814               "        { dup 6 index sub abs bThreshold gt\n" /* |b1-b2|>bth */
02815               "          { STsplit }\n"
02816               "          { 2 index 13 index sub abs rThreshold gt\n" /* |r1-r3|>rht */
02817               "            { STsplit }\n"
02818               "            { 1 index 12 index sub abs gThreshold gt\n" /* |g1-g3|>gth */
02819               "              { STsplit }\n"
02820               "              { dup 11 index sub abs bThreshold gt\n" /* |b1-b3|>bth */
02821               "                { STsplit }\n"
02822               "                { 7 index 13 index sub abs rThreshold gt\n"); /* |r2-r3|>rht */
02823   gl2psPrintf("                  { STsplit }\n"
02824               "                  { 6 index 12 index sub abs gThreshold gt\n" /* |g2-g3|>gth */
02825               "                    { STsplit }\n"
02826               "                    { 5 index 11 index sub abs bThreshold gt\n" /* |b2-b3|>bth */
02827               "                      { STsplit }\n"
02828               "                      { Tm }\n" /* all colors sufficiently similar */
02829               "                      ifelse }\n"
02830               "                    ifelse }\n"
02831               "                  ifelse }\n"
02832               "                ifelse }\n"
02833               "              ifelse }\n"
02834               "            ifelse }\n"
02835               "          ifelse }\n"
02836               "        ifelse }\n"
02837               "      ifelse } BD\n");
02838 
02839   gl2psPrintf("tryPS3shading\n"
02840               "{ /shfill where\n"
02841               "  { /ST { STshfill } BD }\n"
02842               "  { /ST { STnoshfill } BD }\n"
02843               "  ifelse }\n"
02844               "{ /ST { STnoshfill } BD }\n"
02845               "ifelse\n");
02846 
02847   gl2psPrintf("end\n"
02848               "%%%%EndProlog\n"
02849               "%%%%BeginSetup\n"
02850               "/DeviceRGB setcolorspace\n"
02851               "gl2psdict begin\n"
02852               "%%%%EndSetup\n"
02853               "%%%%Page: 1 1\n"
02854               "%%%%BeginPageSetup\n");
02855 
02856   if(gl2ps->options & GL2PS_LANDSCAPE){
02857     gl2psPrintf("%d 0 translate 90 rotate\n",
02858                 (int)gl2ps->viewport[3]);
02859   }
02860 
02861   gl2psPrintf("%%%%EndPageSetup\n"
02862               "mark\n"
02863               "gsave\n"
02864               "1.0 1.0 scale\n");
02865 
02866   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
02867     gl2psPrintf("%g %g %g C\n"
02868                 "newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto\n"
02869                 "closepath fill\n",
02870                 gl2ps->bgcolor[0], gl2ps->bgcolor[1], gl2ps->bgcolor[2],
02871                 (int)gl2ps->viewport[0], (int)gl2ps->viewport[1], (int)gl2ps->viewport[2],
02872                 (int)gl2ps->viewport[1], (int)gl2ps->viewport[2], (int)gl2ps->viewport[3],
02873                 (int)gl2ps->viewport[0], (int)gl2ps->viewport[3]);
02874   }
02875 }
02876 
02877 static void gl2psPrintPostScriptColor(GL2PSrgba rgba)
02878 {
02879   if(!gl2psSameColor(gl2ps->lastrgba, rgba)){
02880     gl2psSetLastColor(rgba);
02881     gl2psPrintf("%g %g %g C\n", rgba[0], rgba[1], rgba[2]);
02882   }
02883 }
02884 
02885 static void gl2psResetPostScriptColor(void)
02886 {
02887   gl2ps->lastrgba[0] = gl2ps->lastrgba[1] = gl2ps->lastrgba[2] = -1.;
02888 }
02889 
02890 static void gl2psEndPostScriptLine(void)
02891 {
02892   int i;
02893   if(gl2ps->lastvertex.rgba[0] >= 0.){
02894     gl2psPrintf("%g %g LE\n", gl2ps->lastvertex.xyz[0], gl2ps->lastvertex.xyz[1]);
02895     for(i = 0; i < 3; i++)
02896       gl2ps->lastvertex.xyz[i] = -1.;
02897     for(i = 0; i < 4; i++)
02898       gl2ps->lastvertex.rgba[i] = -1.;
02899   }
02900 }
02901 
02902 static void gl2psParseStipplePattern(GLushort pattern, GLint factor,
02903                                      int *nb, int array[10])
02904 {
02905   int i, n;
02906   int on[8] = {0, 0, 0, 0, 0, 0, 0, 0};
02907   int off[8] = {0, 0, 0, 0, 0, 0, 0, 0};
02908   char tmp[16];
02909 
02910   /* extract the 16 bits from the OpenGL stipple pattern */
02911   for(n = 15; n >= 0; n--){
02912     tmp[n] = (char)(pattern & 0x01);
02913     pattern >>= 1;
02914   }
02915   /* compute the on/off pixel sequence */
02916   n = 0;
02917   for(i = 0; i < 8; i++){
02918     while(n < 16 && !tmp[n]){ off[i]++; n++; }
02919     while(n < 16 && tmp[n]){ on[i]++; n++; }
02920     if(n >= 15){ i++; break; }
02921   }
02922 
02923   /* store the on/off array from right to left, starting with off
02924      pixels. The PostScript specification allows for at most 11
02925      elements in the on/off array, so we limit ourselves to 5 on/off
02926      couples (our longest possible array is thus [on4 off4 on3 off3
02927      on2 off2 on1 off1 on0 off0]) */
02928   *nb = 0;
02929   for(n = i - 1; n >= 0; n--){
02930     array[(*nb)++] = factor * on[n];
02931     array[(*nb)++] = factor * off[n];
02932     if(*nb == 10) break;
02933   }
02934 }
02935 
02936 static int gl2psPrintPostScriptDash(GLushort pattern, GLint factor, const char *str)
02937 {
02938   int len = 0, i, n, array[10];
02939 
02940   if(pattern == gl2ps->lastpattern && factor == gl2ps->lastfactor)
02941     return 0;
02942 
02943   gl2ps->lastpattern = pattern;
02944   gl2ps->lastfactor = factor;
02945 
02946   if(!pattern || !factor){
02947     /* solid line */
02948     len += gl2psPrintf("[] 0 %s\n", str);
02949   }
02950   else{
02951     gl2psParseStipplePattern(pattern, factor, &n, array);
02952     len += gl2psPrintf("[");
02953     for(i = 0; i < n; i++){
02954       if(i) len += gl2psPrintf(" ");
02955       len += gl2psPrintf("%d", array[i]);
02956     }
02957     len += gl2psPrintf("] 0 %s\n", str);
02958   }
02959 
02960   return len;
02961 }
02962 
02963 static void gl2psPrintPostScriptPrimitive(void *data)
02964 {
02965   int newline;
02966   GL2PSprimitive *prim;
02967 
02968   prim = *(GL2PSprimitive**)data;
02969 
02970   if((gl2ps->options & GL2PS_OCCLUSION_CULL) && prim->culled) return;
02971 
02972   /* Every effort is made to draw lines as connected segments (i.e.,
02973      using a single PostScript path): this is the only way to get nice
02974      line joins and to not restart the stippling for every line
02975      segment. So if the primitive to print is not a line we must first
02976      finish the current line (if any): */
02977   if(prim->type != GL2PS_LINE) gl2psEndPostScriptLine();
02978 
02979   switch(prim->type){
02980   case GL2PS_POINT :
02981     gl2psPrintPostScriptColor(prim->verts[0].rgba);
02982     gl2psPrintf("%g %g %g P\n",
02983                 prim->verts[0].xyz[0], prim->verts[0].xyz[1], 0.5 * prim->width);
02984     break;
02985   case GL2PS_LINE :
02986     if(!gl2psSamePosition(gl2ps->lastvertex.xyz, prim->verts[0].xyz) ||
02987        !gl2psSameColor(gl2ps->lastrgba, prim->verts[0].rgba) ||
02988        gl2ps->lastlinewidth != prim->width ||
02989        gl2ps->lastpattern != prim->pattern ||
02990        gl2ps->lastfactor != prim->factor){
02991       /* End the current line if the new segment does not start where
02992          the last one ended, or if the color, the width or the
02993          stippling have changed (multi-stroking lines with changing
02994          colors is necessary until we use /shfill for lines;
02995          unfortunately this means that at the moment we can screw up
02996          line stippling for smooth-shaded lines) */
02997       gl2psEndPostScriptLine();
02998       newline = 1;
02999     }
03000     else{
03001       newline = 0;
03002     }
03003     if(gl2ps->lastlinewidth != prim->width){
03004       gl2ps->lastlinewidth = prim->width;
03005       gl2psPrintf("%g W\n", gl2ps->lastlinewidth);
03006     }
03007     gl2psPrintPostScriptDash(prim->pattern, prim->factor, "setdash");
03008     gl2psPrintPostScriptColor(prim->verts[0].rgba);
03009     gl2psPrintf("%g %g %s\n", prim->verts[0].xyz[0], prim->verts[0].xyz[1],
03010                 newline ? "LS" : "L");
03011     gl2ps->lastvertex = prim->verts[1];
03012     break;
03013   case GL2PS_TRIANGLE :
03014     if(!gl2psVertsSameColor(prim)){
03015       gl2psResetPostScriptColor();
03016       gl2psPrintf("%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g ST\n",
03017                   prim->verts[2].xyz[0], prim->verts[2].xyz[1],
03018                   prim->verts[2].rgba[0], prim->verts[2].rgba[1],
03019                   prim->verts[2].rgba[2], prim->verts[1].xyz[0],
03020                   prim->verts[1].xyz[1], prim->verts[1].rgba[0],
03021                   prim->verts[1].rgba[1], prim->verts[1].rgba[2],
03022                   prim->verts[0].xyz[0], prim->verts[0].xyz[1],
03023                   prim->verts[0].rgba[0], prim->verts[0].rgba[1],
03024                   prim->verts[0].rgba[2]);
03025     }
03026     else{
03027       gl2psPrintPostScriptColor(prim->verts[0].rgba);
03028       gl2psPrintf("%g %g %g %g %g %g T\n",
03029                   prim->verts[2].xyz[0], prim->verts[2].xyz[1],
03030                   prim->verts[1].xyz[0], prim->verts[1].xyz[1],
03031                   prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
03032     }
03033     break;
03034   case GL2PS_QUADRANGLE :
03035     gl2psMsg(GL2PS_WARNING, "There should not be any quad left to print");
03036     break;
03037   case GL2PS_PIXMAP :
03038     gl2psPrintPostScriptPixmap(prim->verts[0].xyz[0], prim->verts[0].xyz[1],
03039                                prim->data.image);
03040     break;
03041   case GL2PS_IMAGEMAP :
03042     if(prim->data.image->type != GL2PS_IMAGEMAP_WRITTEN){
03043       gl2psPrintPostScriptColor(prim->verts[0].rgba);
03044       gl2psPrintPostScriptImagemap(prim->data.image->pixels[0],
03045                                    prim->data.image->pixels[1],
03046                                    prim->data.image->width, prim->data.image->height,
03047                                    (const unsigned char*)(&(prim->data.image->pixels[2])));
03048       prim->data.image->type = GL2PS_IMAGEMAP_WRITTEN;
03049     }
03050     break;
03051   case GL2PS_TEXT :
03052     gl2psPrintPostScriptColor(prim->verts[0].rgba);
03053     gl2psPrintf("(%s) ", prim->data.text->str);
03054     if(prim->data.text->angle)
03055       gl2psPrintf("%g ", prim->data.text->angle);
03056     gl2psPrintf("%g %g %d /%s ",
03057                 prim->verts[0].xyz[0], prim->verts[0].xyz[1],
03058                 prim->data.text->fontsize, prim->data.text->fontname);
03059     switch(prim->data.text->alignment){
03060     case GL2PS_TEXT_C:
03061       gl2psPrintf(prim->data.text->angle ? "SCCR\n" : "SCC\n");
03062       break;
03063     case GL2PS_TEXT_CL:
03064       gl2psPrintf(prim->data.text->angle ? "SCLR\n" : "SCL\n");
03065       break;
03066     case GL2PS_TEXT_CR:
03067       gl2psPrintf(prim->data.text->angle ? "SCRR\n" : "SCR\n");
03068       break;
03069     case GL2PS_TEXT_B:
03070       gl2psPrintf(prim->data.text->angle ? "SBCR\n" : "SBC\n");
03071       break;
03072     case GL2PS_TEXT_BR:
03073       gl2psPrintf(prim->data.text->angle ? "SBRR\n" : "SBR\n");
03074       break;
03075     case GL2PS_TEXT_T:
03076       gl2psPrintf(prim->data.text->angle ? "STCR\n" : "STC\n");
03077       break;
03078     case GL2PS_TEXT_TL:
03079       gl2psPrintf(prim->data.text->angle ? "STLR\n" : "STL\n");
03080       break;
03081     case GL2PS_TEXT_TR:
03082       gl2psPrintf(prim->data.text->angle ? "STRR\n" : "STR\n");
03083       break;
03084     case GL2PS_TEXT_BL:
03085     default:
03086       gl2psPrintf(prim->data.text->angle ? "SR\n" : "S\n");
03087       break;
03088     }
03089     break;
03090   case GL2PS_SPECIAL :
03091     /* alignment contains the format for which the special output text
03092        is intended */
03093     if(prim->data.text->alignment == GL2PS_PS ||
03094        prim->data.text->alignment == GL2PS_EPS)
03095       gl2psPrintf("%s\n", prim->data.text->str);
03096     break;
03097   default :
03098     break;
03099   }
03100 }
03101 
03102 static void gl2psPrintPostScriptFooter(void)
03103 {
03104   gl2psPrintf("grestore\n"
03105               "showpage\n"
03106               "cleartomark\n"
03107               "%%%%PageTrailer\n"
03108               "%%%%Trailer\n"
03109               "end\n"
03110               "%%%%EOF\n");
03111 
03112   gl2psPrintGzipFooter();
03113 }
03114 
03115 static void gl2psPrintPostScriptBeginViewport(GLint viewport[4])
03116 {
03117   GLint index;
03118   GLfloat rgba[4];
03119   int x = viewport[0], y = viewport[1], w = viewport[2], h = viewport[3];
03120 
03121   glRenderMode(GL_FEEDBACK);
03122 
03123   if(gl2ps->header){
03124     gl2psPrintPostScriptHeader();
03125     gl2ps->header = GL_FALSE;
03126   }
03127 
03128   gl2psPrintf("gsave\n"
03129               "1.0 1.0 scale\n");
03130 
03131   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
03132     if(gl2ps->colormode == GL_RGBA || gl2ps->colorsize == 0){
03133       glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba);
03134     }
03135     else{
03136       glGetIntegerv(GL_INDEX_CLEAR_VALUE, &index);
03137       rgba[0] = gl2ps->colormap[index][0];
03138       rgba[1] = gl2ps->colormap[index][1];
03139       rgba[2] = gl2ps->colormap[index][2];
03140       rgba[3] = 1.0F;
03141     }
03142     gl2psPrintf("%g %g %g C\n"
03143                 "newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto\n"
03144                 "closepath fill\n",
03145                 rgba[0], rgba[1], rgba[2],
03146                 x, y, x+w, y, x+w, y+h, x, y+h);
03147   }
03148 
03149   gl2psPrintf("newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto\n"
03150               "closepath clip\n",
03151               x, y, x+w, y, x+w, y+h, x, y+h);
03152 
03153 }
03154 
03155 static GLint gl2psPrintPostScriptEndViewport(void)
03156 {
03157   GLint res;
03158 
03159   res = gl2psPrintPrimitives();
03160   gl2psPrintf("grestore\n");
03161   return res;
03162 }
03163 
03164 static void gl2psPrintPostScriptFinalPrimitive(void)
03165 {
03166   /* End any remaining line, if any */
03167   gl2psEndPostScriptLine();
03168 }
03169 
03170 /* definition of the PostScript and Encapsulated PostScript backends */
03171 
03172 static GL2PSbackend gl2psPS = {
03173   gl2psPrintPostScriptHeader,
03174   gl2psPrintPostScriptFooter,
03175   gl2psPrintPostScriptBeginViewport,
03176   gl2psPrintPostScriptEndViewport,
03177   gl2psPrintPostScriptPrimitive,
03178   gl2psPrintPostScriptFinalPrimitive,
03179   "ps",
03180   "Postscript"
03181 };
03182 
03183 static GL2PSbackend gl2psEPS = {
03184   gl2psPrintPostScriptHeader,
03185   gl2psPrintPostScriptFooter,
03186   gl2psPrintPostScriptBeginViewport,
03187   gl2psPrintPostScriptEndViewport,
03188   gl2psPrintPostScriptPrimitive,
03189   gl2psPrintPostScriptFinalPrimitive,
03190   "eps",
03191   "Encapsulated Postscript"
03192 };
03193 
03194 /*********************************************************************
03195  *
03196  * LaTeX routines
03197  *
03198  *********************************************************************/
03199 
03200 static void gl2psPrintTeXHeader(void)
03201 {
03202   char name[256];
03203   time_t now;
03204   int i;
03205 
03206   if(gl2ps->filename && strlen(gl2ps->filename) < 256){
03207     for(i = (int)strlen(gl2ps->filename) - 1; i >= 0; i--){
03208       if(gl2ps->filename[i] == '.'){
03209         strncpy(name, gl2ps->filename, i);
03210         name[i] = '\0';
03211         break;
03212       }
03213     }
03214     if(i <= 0) strcpy(name, gl2ps->filename);
03215   }
03216   else{
03217     strcpy(name, "untitled");
03218   }
03219 
03220   time(&now);
03221 
03222   fprintf(gl2ps->stream,
03223           "%% Title: %s\n"
03224           "%% Creator: GL2PS %d.%d.%d%s, %s\n"
03225           "%% For: %s\n"
03226           "%% CreationDate: %s",
03227           gl2ps->title, GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION,
03228           GL2PS_PATCH_VERSION, GL2PS_EXTRA_VERSION, GL2PS_COPYRIGHT,
03229           gl2ps->producer, ctime(&now));
03230 
03231   fprintf(gl2ps->stream,
03232           "\\setlength{\\unitlength}{1pt}\n"
03233           "\\begin{picture}(0,0)\n"
03234           "\\includegraphics{%s}\n"
03235           "\\end{picture}%%\n"
03236           "%s\\begin{picture}(%d,%d)(0,0)\n",
03237           name, (gl2ps->options & GL2PS_LANDSCAPE) ? "\\rotatebox{90}{" : "",
03238           (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
03239 }
03240 
03241 static void gl2psPrintTeXPrimitive(void *data)
03242 {
03243   GL2PSprimitive *prim;
03244 
03245   prim = *(GL2PSprimitive**)data;
03246 
03247   switch(prim->type){
03248   case GL2PS_TEXT :
03249     fprintf(gl2ps->stream, "\\fontsize{%d}{0}\n\\selectfont",
03250             prim->data.text->fontsize);
03251     fprintf(gl2ps->stream, "\\put(%g,%g)",
03252             prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
03253     if(prim->data.text->angle)
03254       fprintf(gl2ps->stream, "{\\rotatebox{%g}", prim->data.text->angle);
03255     fprintf(gl2ps->stream, "{\\makebox(0,0)");
03256     switch(prim->data.text->alignment){
03257     case GL2PS_TEXT_C:
03258       fprintf(gl2ps->stream, "{");
03259       break;
03260     case GL2PS_TEXT_CL:
03261       fprintf(gl2ps->stream, "[l]{");
03262       break;
03263     case GL2PS_TEXT_CR:
03264       fprintf(gl2ps->stream, "[r]{");
03265       break;
03266     case GL2PS_TEXT_B:
03267       fprintf(gl2ps->stream, "[b]{");
03268       break;
03269     case GL2PS_TEXT_BR:
03270       fprintf(gl2ps->stream, "[br]{");
03271       break;
03272     case GL2PS_TEXT_T:
03273       fprintf(gl2ps->stream, "[t]{");
03274       break;
03275     case GL2PS_TEXT_TL:
03276       fprintf(gl2ps->stream, "[tl]{");
03277       break;
03278     case GL2PS_TEXT_TR:
03279       fprintf(gl2ps->stream, "[tr]{");
03280       break;
03281     case GL2PS_TEXT_BL:
03282     default:
03283       fprintf(gl2ps->stream, "[bl]{");
03284       break;
03285     }
03286     fprintf(gl2ps->stream, "\\textcolor[rgb]{%g,%g,%g}{{%s}}",
03287             prim->verts[0].rgba[0], prim->verts[0].rgba[1], prim->verts[0].rgba[2],
03288             prim->data.text->str);
03289     if(prim->data.text->angle)
03290       fprintf(gl2ps->stream, "}");
03291     fprintf(gl2ps->stream, "}}\n");
03292     break;
03293   case GL2PS_SPECIAL :
03294     /* alignment contains the format for which the special output text
03295        is intended */
03296     if (prim->data.text->alignment == GL2PS_TEX)
03297       fprintf(gl2ps->stream, "%s\n", prim->data.text->str);
03298     break;
03299   default :
03300     break;
03301   }
03302 }
03303 
03304 static void gl2psPrintTeXFooter(void)
03305 {
03306   fprintf(gl2ps->stream, "\\end{picture}%s\n",
03307           (gl2ps->options & GL2PS_LANDSCAPE) ? "}" : "");
03308 }
03309 
03310 static void gl2psPrintTeXBeginViewport(GLint viewport[4])
03311 {
03312   (void) viewport;  /* not used */
03313   glRenderMode(GL_FEEDBACK);
03314 
03315   if(gl2ps->header){
03316     gl2psPrintTeXHeader();
03317     gl2ps->header = GL_FALSE;
03318   }
03319 }
03320 
03321 static GLint gl2psPrintTeXEndViewport(void)
03322 {
03323   return gl2psPrintPrimitives();
03324 }
03325 
03326 static void gl2psPrintTeXFinalPrimitive(void)
03327 {
03328 }
03329 
03330 /* definition of the LaTeX backend */
03331 
03332 static GL2PSbackend gl2psTEX = {
03333   gl2psPrintTeXHeader,
03334   gl2psPrintTeXFooter,
03335   gl2psPrintTeXBeginViewport,
03336   gl2psPrintTeXEndViewport,
03337   gl2psPrintTeXPrimitive,
03338   gl2psPrintTeXFinalPrimitive,
03339   "tex",
03340   "LaTeX text"
03341 };
03342 
03343 /*********************************************************************
03344  *
03345  * PDF routines
03346  *
03347  *********************************************************************/
03348 
03349 static int gl2psPrintPDFCompressorType(void)
03350 {
03351 #if defined(GL2PS_HAVE_ZLIB)
03352   if(gl2ps->options & GL2PS_COMPRESS){
03353     return fprintf(gl2ps->stream, "/Filter [/FlateDecode]\n");
03354   }
03355 #endif
03356   return 0;
03357 }
03358 
03359 static int gl2psPrintPDFStrokeColor(GL2PSrgba rgba)
03360 {
03361   int i, offs = 0;
03362 
03363   gl2psSetLastColor(rgba);
03364   for(i = 0; i < 3; ++i){
03365     if(GL2PS_ZERO(rgba[i]))
03366       offs += gl2psPrintf("%.0f ", 0.);
03367     else if(rgba[i] < 1e-4 || rgba[i] > 1e6) /* avoid %e formatting */
03368       offs += gl2psPrintf("%f ", rgba[i]);
03369     else
03370       offs += gl2psPrintf("%g ", rgba[i]);
03371   }
03372   offs += gl2psPrintf("RG\n");
03373   return offs;
03374 }
03375 
03376 static int gl2psPrintPDFFillColor(GL2PSrgba rgba)
03377 {
03378   int i, offs = 0;
03379 
03380   for(i = 0; i < 3; ++i){
03381     if(GL2PS_ZERO(rgba[i]))
03382       offs += gl2psPrintf("%.0f ", 0.);
03383     else if(rgba[i] < 1e-4 || rgba[i] > 1e6) /* avoid %e formatting */
03384       offs += gl2psPrintf("%f ", rgba[i]);
03385     else
03386       offs += gl2psPrintf("%g ", rgba[i]);
03387   }
03388   offs += gl2psPrintf("rg\n");
03389   return offs;
03390 }
03391 
03392 static int gl2psPrintPDFLineWidth(GLfloat lw)
03393 {
03394   if(GL2PS_ZERO(lw))
03395     return gl2psPrintf("%.0f w\n", 0.);
03396   else if(lw < 1e-4 || lw > 1e6) /* avoid %e formatting */
03397     return gl2psPrintf("%f w\n", lw);
03398   else
03399     return gl2psPrintf("%g w\n", lw);
03400 }
03401 
03402 static void gl2psPutPDFText(GL2PSstring *text, int cnt, GLfloat x, GLfloat y)
03403 {
03404   GLfloat rad, crad, srad;
03405 
03406   if(text->angle == 0.0F){
03407     gl2ps->streamlength += gl2psPrintf
03408       ("BT\n"
03409        "/F%d %d Tf\n"
03410        "%f %f Td\n"
03411        "(%s) Tj\n"
03412        "ET\n",
03413        cnt, text->fontsize, x, y, text->str);
03414   }
03415   else{
03416     rad = (GLfloat)(M_PI * text->angle / 180.0F);
03417     srad = (GLfloat)sin(rad);
03418     crad = (GLfloat)cos(rad);
03419     gl2ps->streamlength += gl2psPrintf
03420       ("BT\n"
03421        "/F%d %d Tf\n"
03422        "%f %f %f %f %f %f Tm\n"
03423        "(%s) Tj\n"
03424        "ET\n",
03425        cnt, text->fontsize, crad, srad, -srad, crad, x, y, text->str);
03426   }
03427 }
03428 
03429 static void gl2psPutPDFImage(GL2PSimage *image, int cnt, GLfloat x, GLfloat y)
03430 {
03431   gl2ps->streamlength += gl2psPrintf
03432     ("q\n"
03433      "%d 0 0 %d %f %f cm\n"
03434      "/Im%d Do\n"
03435      "Q\n",
03436      (int)image->width, (int)image->height, x, y, cnt);
03437 }
03438 
03439 static void gl2psPDFstacksInit(void)
03440 {
03441   gl2ps->objects_stack = 7 /* FIXED_XREF_ENTRIES */ + 1;
03442   gl2ps->extgs_stack = 0;
03443   gl2ps->font_stack = 0;
03444   gl2ps->im_stack = 0;
03445   gl2ps->trgroupobjects_stack = 0;
03446   gl2ps->shader_stack = 0;
03447   gl2ps->mshader_stack = 0;
03448 }
03449 
03450 static void gl2psPDFgroupObjectInit(GL2PSpdfgroup *gro)
03451 {
03452   if(!gro)
03453     return;
03454 
03455   gro->ptrlist = NULL;
03456   gro->fontno = gro->gsno = gro->imno = gro->maskshno = gro->shno
03457     = gro->trgroupno = gro->fontobjno = gro->imobjno = gro->shobjno
03458     = gro->maskshobjno = gro->gsobjno = gro->trgroupobjno = -1;
03459 }
03460 
03461 /* Build up group objects and assign name and object numbers */
03462 
03463 static void gl2psPDFgroupListInit(void)
03464 {
03465   int i;
03466   GL2PSprimitive *p = NULL;
03467   GL2PSpdfgroup gro;
03468   int lasttype = GL2PS_NO_TYPE;
03469   GL2PSrgba lastrgba = {-1.0F, -1.0F, -1.0F, -1.0F};
03470   GLushort lastpattern = 0;
03471   GLint lastfactor = 0;
03472   GLfloat lastwidth = 1;
03473   GL2PStriangle lastt, tmpt;
03474   int lastTriangleWasNotSimpleWithSameColor = 0;
03475 
03476   if(!gl2ps->pdfprimlist)
03477     return;
03478 
03479   gl2ps->pdfgrouplist = gl2psListCreate(500, 500, sizeof(GL2PSpdfgroup));
03480   gl2psInitTriangle(&lastt);
03481 
03482   for(i = 0; i < gl2psListNbr(gl2ps->pdfprimlist); ++i){
03483     p = *(GL2PSprimitive**)gl2psListPointer(gl2ps->pdfprimlist, i);
03484     switch(p->type){
03485     case GL2PS_PIXMAP:
03486       gl2psPDFgroupObjectInit(&gro);
03487       gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
03488       gro.imno = gl2ps->im_stack++;
03489       gl2psListAdd(gro.ptrlist, &p);
03490       gl2psListAdd(gl2ps->pdfgrouplist, &gro);
03491       break;
03492     case GL2PS_TEXT:
03493       gl2psPDFgroupObjectInit(&gro);
03494       gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
03495       gro.fontno = gl2ps->font_stack++;
03496       gl2psListAdd(gro.ptrlist, &p);
03497       gl2psListAdd(gl2ps->pdfgrouplist, &gro);
03498       break;
03499     case GL2PS_LINE:
03500       if(lasttype != p->type || lastwidth != p->width ||
03501          lastpattern != p->pattern || lastfactor != p->factor ||
03502          !gl2psSameColor(p->verts[0].rgba, lastrgba)){
03503         gl2psPDFgroupObjectInit(&gro);
03504         gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
03505         gl2psListAdd(gro.ptrlist, &p);
03506         gl2psListAdd(gl2ps->pdfgrouplist, &gro);
03507       }
03508       else{
03509         gl2psListAdd(gro.ptrlist, &p);
03510       }
03511       lastpattern = p->pattern;
03512       lastfactor = p->factor;
03513       lastwidth = p->width;
03514       lastrgba[0] = p->verts[0].rgba[0];
03515       lastrgba[1] = p->verts[0].rgba[1];
03516       lastrgba[2] = p->verts[0].rgba[2];
03517       break;
03518     case GL2PS_POINT:
03519       if(lasttype != p->type || lastwidth != p->width ||
03520          !gl2psSameColor(p->verts[0].rgba, lastrgba)){
03521         gl2psPDFgroupObjectInit(&gro);
03522         gro.ptrlist = gl2psListCreate(1,2,sizeof(GL2PSprimitive*));
03523         gl2psListAdd(gro.ptrlist, &p);
03524         gl2psListAdd(gl2ps->pdfgrouplist, &gro);
03525       }
03526       else{
03527         gl2psListAdd(gro.ptrlist, &p);
03528       }
03529       lastwidth = p->width;
03530       lastrgba[0] = p->verts[0].rgba[0];
03531       lastrgba[1] = p->verts[0].rgba[1];
03532       lastrgba[2] = p->verts[0].rgba[2];
03533       break;
03534     case GL2PS_TRIANGLE:
03535       gl2psFillTriangleFromPrimitive(&tmpt, p, GL_TRUE);
03536       lastTriangleWasNotSimpleWithSameColor =
03537         !(tmpt.prop & T_CONST_COLOR && tmpt.prop & T_ALPHA_1) ||
03538         !gl2psSameColor(tmpt.vertex[0].rgba, lastt.vertex[0].rgba);
03539       if(lasttype == p->type && tmpt.prop == lastt.prop &&
03540          lastTriangleWasNotSimpleWithSameColor){
03541         /* TODO Check here for last alpha */
03542         gl2psListAdd(gro.ptrlist, &p);
03543       }
03544       else{
03545         gl2psPDFgroupObjectInit(&gro);
03546         gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
03547         gl2psListAdd(gro.ptrlist, &p);
03548         gl2psListAdd(gl2ps->pdfgrouplist, &gro);
03549       }
03550       lastt = tmpt;
03551       break;
03552     default:
03553       break;
03554     }
03555     lasttype = p->type;
03556   }
03557 }
03558 
03559 static void gl2psSortOutTrianglePDFgroup(GL2PSpdfgroup *gro)
03560 {
03561   GL2PStriangle t;
03562   GL2PSprimitive *prim = NULL;
03563 
03564   if(!gro)
03565     return;
03566 
03567   if(!gl2psListNbr(gro->ptrlist))
03568     return;
03569 
03570   prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);
03571 
03572   if(prim->type != GL2PS_TRIANGLE)
03573     return;
03574 
03575   gl2psFillTriangleFromPrimitive(&t, prim, GL_TRUE);
03576 
03577   if(t.prop & T_CONST_COLOR && t.prop & T_ALPHA_LESS_1){
03578     gro->gsno = gl2ps->extgs_stack++;
03579     gro->gsobjno = gl2ps->objects_stack ++;
03580   }
03581   else if(t.prop & T_CONST_COLOR && t.prop & T_VAR_ALPHA){
03582     gro->gsno = gl2ps->extgs_stack++;
03583     gro->gsobjno = gl2ps->objects_stack++;
03584     gro->trgroupno = gl2ps->trgroupobjects_stack++;
03585     gro->trgroupobjno = gl2ps->objects_stack++;
03586     gro->maskshno = gl2ps->mshader_stack++;
03587     gro->maskshobjno = gl2ps->objects_stack++;
03588   }
03589   else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_1){
03590     gro->shno = gl2ps->shader_stack++;
03591     gro->shobjno = gl2ps->objects_stack++;
03592   }
03593   else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_LESS_1){
03594     gro->gsno = gl2ps->extgs_stack++;
03595     gro->gsobjno = gl2ps->objects_stack++;
03596     gro->shno = gl2ps->shader_stack++;
03597     gro->shobjno = gl2ps->objects_stack++;
03598   }
03599   else if(t.prop & T_VAR_COLOR && t.prop & T_VAR_ALPHA){
03600     gro->gsno = gl2ps->extgs_stack++;
03601     gro->gsobjno = gl2ps->objects_stack++;
03602     gro->shno = gl2ps->shader_stack++;
03603     gro->shobjno = gl2ps->objects_stack++;
03604     gro->trgroupno = gl2ps->trgroupobjects_stack++;
03605     gro->trgroupobjno = gl2ps->objects_stack++;
03606     gro->maskshno = gl2ps->mshader_stack++;
03607     gro->maskshobjno = gl2ps->objects_stack++;
03608   }
03609 }
03610 
03611 /* Main stream data */
03612 
03613 static void gl2psPDFgroupListWriteMainStream(void)
03614 {
03615   int i, j, lastel;
03616   GL2PSprimitive *prim = NULL, *prev = NULL;
03617   GL2PSpdfgroup *gro;
03618   GL2PStriangle t;
03619 
03620   if(!gl2ps->pdfgrouplist)
03621     return;
03622 
03623   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
03624     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
03625 
03626     lastel = gl2psListNbr(gro->ptrlist) - 1;
03627     if(lastel < 0)
03628       continue;
03629 
03630     prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);
03631 
03632     switch(prim->type){
03633     case GL2PS_POINT:
03634       gl2ps->streamlength += gl2psPrintf("1 J\n");
03635       gl2ps->streamlength += gl2psPrintPDFLineWidth(prim->width);
03636       gl2ps->streamlength += gl2psPrintPDFStrokeColor(prim->verts[0].rgba);
03637       for(j = 0; j <= lastel; ++j){
03638         prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
03639         gl2ps->streamlength +=
03640           gl2psPrintf("%f %f m %f %f l\n",
03641                       prim->verts[0].xyz[0], prim->verts[0].xyz[1],
03642                       prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
03643       }
03644       gl2ps->streamlength += gl2psPrintf("S\n");
03645       gl2ps->streamlength += gl2psPrintf("0 J\n");
03646       break;
03647     case GL2PS_LINE:
03648       /* We try to use as few paths as possible to draw lines, in
03649          order to get nice stippling even when the individual segments
03650          are smaller than the stipple */
03651       gl2ps->streamlength += gl2psPrintPDFLineWidth(prim->width);
03652       gl2ps->streamlength += gl2psPrintPDFStrokeColor(prim->verts[0].rgba);
03653       gl2ps->streamlength += gl2psPrintPostScriptDash(prim->pattern, prim->factor, "d");
03654       /* start new path */
03655       gl2ps->streamlength +=
03656         gl2psPrintf("%f %f m\n",
03657                     prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
03658 
03659       for(j = 1; j <= lastel; ++j){
03660         prev = prim;
03661         prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
03662         if(!gl2psSamePosition(prim->verts[0].xyz, prev->verts[1].xyz)){
03663           /* the starting point of the new segment does not match the
03664              end point of the previous line, so we end the current
03665              path and start a new one */
03666           gl2ps->streamlength +=
03667             gl2psPrintf("%f %f l\n",
03668                         prev->verts[1].xyz[0], prev->verts[1].xyz[1]);
03669           gl2ps->streamlength +=
03670             gl2psPrintf("%f %f m\n",
03671                         prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
03672         }
03673         else{
03674           /* the two segements are connected, so we just append to the
03675              current path */
03676           gl2ps->streamlength +=
03677             gl2psPrintf("%f %f l\n",
03678                         prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
03679         }
03680       }
03681       /* end last path */
03682       gl2ps->streamlength +=
03683         gl2psPrintf("%f %f l\n",
03684                     prim->verts[1].xyz[0], prim->verts[1].xyz[1]);
03685       gl2ps->streamlength += gl2psPrintf("S\n");
03686       break;
03687     case GL2PS_TRIANGLE:
03688       gl2psFillTriangleFromPrimitive(&t, prim, GL_TRUE);
03689       gl2psSortOutTrianglePDFgroup(gro);
03690 
03691       /* No alpha and const color: Simple PDF draw orders  */
03692       if(t.prop & T_CONST_COLOR && t.prop & T_ALPHA_1){
03693         gl2ps->streamlength += gl2psPrintPDFFillColor(t.vertex[0].rgba);
03694         for(j = 0; j <= lastel; ++j){
03695           prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
03696           gl2psFillTriangleFromPrimitive(&t, prim, GL_FALSE);
03697           gl2ps->streamlength
03698             += gl2psPrintf("%f %f m\n"
03699                            "%f %f l\n"
03700                            "%f %f l\n"
03701                            "h f\n",
03702                            t.vertex[0].xyz[0], t.vertex[0].xyz[1],
03703                            t.vertex[1].xyz[0], t.vertex[1].xyz[1],
03704                            t.vertex[2].xyz[0], t.vertex[2].xyz[1]);
03705         }
03706       }
03707       /* Const alpha < 1 and const color: Simple PDF draw orders
03708          and an extra extended Graphics State for the alpha const */
03709       else if(t.prop & T_CONST_COLOR && t.prop & T_ALPHA_LESS_1){
03710         gl2ps->streamlength += gl2psPrintf("q\n"
03711                                            "/GS%d gs\n",
03712                                            gro->gsno);
03713         gl2ps->streamlength += gl2psPrintPDFFillColor(prim->verts[0].rgba);
03714         for(j = 0; j <= lastel; ++j){
03715           prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
03716           gl2psFillTriangleFromPrimitive(&t, prim, GL_FALSE);
03717           gl2ps->streamlength
03718             += gl2psPrintf("%f %f m\n"
03719                            "%f %f l\n"
03720                            "%f %f l\n"
03721                            "h f\n",
03722                            t.vertex[0].xyz[0], t.vertex[0].xyz[1],
03723                            t.vertex[1].xyz[0], t.vertex[1].xyz[1],
03724                            t.vertex[2].xyz[0], t.vertex[2].xyz[1]);
03725         }
03726         gl2ps->streamlength += gl2psPrintf("Q\n");
03727       }
03728       /* Variable alpha and const color: Simple PDF draw orders
03729          and an extra extended Graphics State + Xobject + Shader
03730          object for the alpha mask */
03731       else if(t.prop & T_CONST_COLOR && t.prop & T_VAR_ALPHA){
03732         gl2ps->streamlength += gl2psPrintf("q\n"
03733                                            "/GS%d gs\n"
03734                                            "/TrG%d Do\n",
03735                                            gro->gsno, gro->trgroupno);
03736         gl2ps->streamlength += gl2psPrintPDFFillColor(prim->verts[0].rgba);
03737         for(j = 0; j <= lastel; ++j){
03738           prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
03739           gl2psFillTriangleFromPrimitive(&t, prim, GL_FALSE);
03740           gl2ps->streamlength
03741             += gl2psPrintf("%f %f m\n"
03742                            "%f %f l\n"
03743                            "%f %f l\n"
03744                            "h f\n",
03745                            t.vertex[0].xyz[0], t.vertex[0].xyz[1],
03746                            t.vertex[1].xyz[0], t.vertex[1].xyz[1],
03747                            t.vertex[2].xyz[0], t.vertex[2].xyz[1]);
03748         }
03749         gl2ps->streamlength += gl2psPrintf("Q\n");
03750       }
03751       /* Variable color and no alpha: Shader Object for the colored
03752          triangle(s) */
03753       else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_1){
03754         gl2ps->streamlength += gl2psPrintf("/Sh%d sh\n", gro->shno);
03755       }
03756       /* Variable color and const alpha < 1: Shader Object for the
03757          colored triangle(s) and an extra extended Graphics State
03758          for the alpha const */
03759       else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_LESS_1){
03760         gl2ps->streamlength += gl2psPrintf("q\n"
03761                                            "/GS%d gs\n"
03762                                            "/Sh%d sh\n"
03763                                            "Q\n",
03764                                            gro->gsno, gro->shno);
03765       }
03766       /* Variable alpha and color: Shader Object for the colored
03767          triangle(s) and an extra extended Graphics State
03768          + Xobject + Shader object for the alpha mask */
03769       else if(t.prop & T_VAR_COLOR && t.prop & T_VAR_ALPHA){
03770         gl2ps->streamlength += gl2psPrintf("q\n"
03771                                            "/GS%d gs\n"
03772                                            "/TrG%d Do\n"
03773                                            "/Sh%d sh\n"
03774                                            "Q\n",
03775                                            gro->gsno, gro->trgroupno, gro->shno);
03776       }
03777       break;
03778     case GL2PS_PIXMAP:
03779       for(j = 0; j <= lastel; ++j){
03780         prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
03781         gl2psPutPDFImage(prim->data.image, gro->imno, prim->verts[0].xyz[0],
03782                          prim->verts[0].xyz[1]);
03783       }
03784       break;
03785     case GL2PS_TEXT:
03786       for(j = 0; j <= lastel; ++j){
03787         prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
03788         gl2ps->streamlength += gl2psPrintPDFFillColor(prim->verts[0].rgba);
03789         gl2psPutPDFText(prim->data.text, gro->fontno, prim->verts[0].xyz[0],
03790                         prim->verts[0].xyz[1]);
03791       }
03792       break;
03793     default:
03794       break;
03795     }
03796   }
03797 }
03798 
03799 /* Graphics State names */
03800 
03801 static int gl2psPDFgroupListWriteGStateResources(void)
03802 {
03803   GL2PSpdfgroup *gro;
03804   int offs = 0;
03805   int i;
03806 
03807   offs += fprintf(gl2ps->stream,
03808                   "/ExtGState\n"
03809                   "<<\n"
03810                   "/GSa 7 0 R\n");
03811   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
03812     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
03813     if(gro->gsno >= 0)
03814       offs += fprintf(gl2ps->stream, "/GS%d %d 0 R\n", gro->gsno, gro->gsobjno);
03815   }
03816   offs += fprintf(gl2ps->stream, ">>\n");
03817   return offs;
03818 }
03819 
03820 /* Main Shader names */
03821 
03822 static int gl2psPDFgroupListWriteShaderResources(void)
03823 {
03824   GL2PSpdfgroup *gro;
03825   int offs = 0;
03826   int i;
03827 
03828   offs += fprintf(gl2ps->stream,
03829                   "/Shading\n"
03830                   "<<\n");
03831   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
03832     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
03833     if(gro->shno >= 0)
03834       offs += fprintf(gl2ps->stream, "/Sh%d %d 0 R\n", gro->shno, gro->shobjno);
03835     if(gro->maskshno >= 0)
03836       offs += fprintf(gl2ps->stream, "/TrSh%d %d 0 R\n", gro->maskshno, gro->maskshobjno);
03837   }
03838   offs += fprintf(gl2ps->stream,">>\n");
03839   return offs;
03840 }
03841 
03842 /* Images & Mask Shader XObject names */
03843 
03844 static int gl2psPDFgroupListWriteXObjectResources(void)
03845 {
03846   int i;
03847   GL2PSprimitive *p = NULL;
03848   GL2PSpdfgroup *gro;
03849   int offs = 0;
03850 
03851   offs += fprintf(gl2ps->stream,
03852                   "/XObject\n"
03853                   "<<\n");
03854 
03855   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
03856     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
03857     if(!gl2psListNbr(gro->ptrlist))
03858       continue;
03859     p = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);
03860     switch(p->type){
03861     case GL2PS_PIXMAP:
03862       gro->imobjno = gl2ps->objects_stack++;
03863       if(GL_RGBA == p->data.image->format)  /* reserve one object for image mask */
03864         gl2ps->objects_stack++;
03865       offs += fprintf(gl2ps->stream, "/Im%d %d 0 R\n", gro->imno, gro->imobjno);
03866     case GL2PS_TRIANGLE:
03867       if(gro->trgroupno >=0)
03868         offs += fprintf(gl2ps->stream, "/TrG%d %d 0 R\n", gro->trgroupno, gro->trgroupobjno);
03869       break;
03870     default:
03871       break;
03872     }
03873   }
03874   offs += fprintf(gl2ps->stream,">>\n");
03875   return offs;
03876 }
03877 
03878 /* Font names */
03879 
03880 static int gl2psPDFgroupListWriteFontResources(void)
03881 {
03882   int i;
03883   GL2PSpdfgroup *gro;
03884   int offs = 0;
03885 
03886   offs += fprintf(gl2ps->stream, "/Font\n<<\n");
03887 
03888   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
03889     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
03890     if(gro->fontno < 0)
03891       continue;
03892     gro->fontobjno = gl2ps->objects_stack++;
03893     offs += fprintf(gl2ps->stream, "/F%d %d 0 R\n", gro->fontno, gro->fontobjno);
03894   }
03895   offs += fprintf(gl2ps->stream, ">>\n");
03896 
03897   return offs;
03898 }
03899 
03900 static void gl2psPDFgroupListDelete(void)
03901 {
03902   int i;
03903   GL2PSpdfgroup *gro = NULL;
03904 
03905   if(!gl2ps->pdfgrouplist)
03906     return;
03907 
03908   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
03909     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist,i);
03910     gl2psListDelete(gro->ptrlist);
03911   }
03912 
03913   gl2psListDelete(gl2ps->pdfgrouplist);
03914   gl2ps->pdfgrouplist = NULL;
03915 }
03916 
03917 /* Print 1st PDF object - file info */
03918 
03919 static int gl2psPrintPDFInfo(void)
03920 {
03921   int offs;
03922   time_t now;
03923   struct tm *newtime;
03924 
03925   time(&now);
03926   newtime = gmtime(&now);
03927 
03928   offs = fprintf(gl2ps->stream,
03929                  "1 0 obj\n"
03930                  "<<\n"
03931                  "/Title (%s)\n"
03932                  "/Creator (GL2PS %d.%d.%d%s, %s)\n"
03933                  "/Producer (%s)\n",
03934                  gl2ps->title, GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION,
03935                  GL2PS_PATCH_VERSION, GL2PS_EXTRA_VERSION, GL2PS_COPYRIGHT,
03936                  gl2ps->producer);
03937 
03938   if(!newtime){
03939     offs += fprintf(gl2ps->stream,
03940                     ">>\n"
03941                     "endobj\n");
03942     return offs;
03943   }
03944 
03945   offs += fprintf(gl2ps->stream,
03946                   "/CreationDate (D:%d%02d%02d%02d%02d%02d)\n"
03947                   ">>\n"
03948                   "endobj\n",
03949                   newtime->tm_year+1900,
03950                   newtime->tm_mon+1,
03951                   newtime->tm_mday,
03952                   newtime->tm_hour,
03953                   newtime->tm_min,
03954                   newtime->tm_sec);
03955   return offs;
03956 }
03957 
03958 /* Create catalog and page structure - 2nd and 3th PDF object */
03959 
03960 static int gl2psPrintPDFCatalog(void)
03961 {
03962   return fprintf(gl2ps->stream,
03963                  "2 0 obj\n"
03964                  "<<\n"
03965                  "/Type /Catalog\n"
03966                  "/Pages 3 0 R\n"
03967                  ">>\n"
03968                  "endobj\n");
03969 }
03970 
03971 static int gl2psPrintPDFPages(void)
03972 {
03973   return fprintf(gl2ps->stream,
03974                  "3 0 obj\n"
03975                  "<<\n"
03976                  "/Type /Pages\n"
03977                  "/Kids [6 0 R]\n"
03978                  "/Count 1\n"
03979                  ">>\n"
03980                  "endobj\n");
03981 }
03982 
03983 /* Open stream for data - graphical objects, fonts etc. PDF object 4 */
03984 
03985 static int gl2psOpenPDFDataStream(void)
03986 {
03987   int offs = 0;
03988 
03989   offs += fprintf(gl2ps->stream,
03990                   "4 0 obj\n"
03991                   "<<\n"
03992                   "/Length 5 0 R\n" );
03993   offs += gl2psPrintPDFCompressorType();
03994   offs += fprintf(gl2ps->stream,
03995                   ">>\n"
03996                   "stream\n");
03997   return offs;
03998 }
03999 
04000 /* Stream setup - Graphics state, fill background if allowed */
04001 
04002 static int gl2psOpenPDFDataStreamWritePreface(void)
04003 {
04004   int offs;
04005 
04006   offs = gl2psPrintf("/GSa gs\n");
04007 
04008   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
04009     offs += gl2psPrintPDFFillColor(gl2ps->bgcolor);
04010     offs += gl2psPrintf("%d %d %d %d re\n",
04011                         (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
04012                         (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
04013     offs += gl2psPrintf("f\n");
04014   }
04015   return offs;
04016 }
04017 
04018 /* Use the functions above to create the first part of the PDF*/
04019 
04020 static void gl2psPrintPDFHeader(void)
04021 {
04022   int offs = 0;
04023   gl2ps->pdfprimlist = gl2psListCreate(500, 500, sizeof(GL2PSprimitive*));
04024   gl2psPDFstacksInit();
04025 
04026   gl2ps->xreflist = (int*)gl2psMalloc(sizeof(int) * gl2ps->objects_stack);
04027 
04028 #if defined(GL2PS_HAVE_ZLIB)
04029   if(gl2ps->options & GL2PS_COMPRESS){
04030     gl2psSetupCompress();
04031   }
04032 #endif
04033   gl2ps->xreflist[0] = 0;
04034   offs += fprintf(gl2ps->stream, "%%PDF-1.4\n");
04035   gl2ps->xreflist[1] = offs;
04036 
04037   offs += gl2psPrintPDFInfo();
04038   gl2ps->xreflist[2] = offs;
04039 
04040   offs += gl2psPrintPDFCatalog();
04041   gl2ps->xreflist[3] = offs;
04042 
04043   offs += gl2psPrintPDFPages();
04044   gl2ps->xreflist[4] = offs;
04045 
04046   offs += gl2psOpenPDFDataStream();
04047   gl2ps->xreflist[5] = offs; /* finished in gl2psPrintPDFFooter */
04048   gl2ps->streamlength = gl2psOpenPDFDataStreamWritePreface();
04049 }
04050 
04051 /* The central primitive drawing */
04052 
04053 static void gl2psPrintPDFPrimitive(void *data)
04054 {
04055   GL2PSprimitive *prim = *(GL2PSprimitive**)data;
04056 
04057   if((gl2ps->options & GL2PS_OCCLUSION_CULL) && prim->culled)
04058     return;
04059 
04060   prim = gl2psCopyPrimitive(prim); /* deep copy */
04061   gl2psListAdd(gl2ps->pdfprimlist, &prim);
04062 }
04063 
04064 /* close stream and ... */
04065 
04066 static int gl2psClosePDFDataStream(void)
04067 {
04068   int offs = 0;
04069 
04070 #if defined(GL2PS_HAVE_ZLIB)
04071   if(gl2ps->options & GL2PS_COMPRESS){
04072     if(Z_OK != gl2psDeflate())
04073       gl2psMsg(GL2PS_ERROR, "Zlib deflate error");
04074     else
04075       fwrite(gl2ps->compress->dest, gl2ps->compress->destLen, 1, gl2ps->stream);
04076     gl2ps->streamlength += gl2ps->compress->destLen;
04077 
04078     offs += gl2ps->streamlength;
04079     gl2psFreeCompress();
04080   }
04081 #endif
04082 
04083   offs += fprintf(gl2ps->stream,
04084                   "endstream\n"
04085                   "endobj\n");
04086   return offs;
04087 }
04088 
04089 /* ... write the now known length object */
04090 
04091 static int gl2psPrintPDFDataStreamLength(int val)
04092 {
04093   return fprintf(gl2ps->stream,
04094                  "5 0 obj\n"
04095                  "%d\n"
04096                  "endobj\n", val);
04097 }
04098 
04099 /* Put the info created before in PDF objects */
04100 
04101 static int gl2psPrintPDFOpenPage(void)
04102 {
04103   int offs;
04104 
04105   /* Write fixed part */
04106 
04107   offs = fprintf(gl2ps->stream,
04108                  "6 0 obj\n"
04109                  "<<\n"
04110                  "/Type /Page\n"
04111                  "/Parent 3 0 R\n"
04112                  "/MediaBox [%d %d %d %d]\n",
04113                  (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
04114                  (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
04115 
04116   if(gl2ps->options & GL2PS_LANDSCAPE)
04117     offs += fprintf(gl2ps->stream, "/Rotate -90\n");
04118 
04119   offs += fprintf(gl2ps->stream,
04120                   "/Contents 4 0 R\n"
04121                   "/Resources\n"
04122                   "<<\n"
04123                   "/ProcSet [/PDF /Text /ImageB /ImageC]  %%/ImageI\n");
04124 
04125   return offs;
04126 
04127   /* End fixed part, proceeds in gl2psPDFgroupListWriteVariableResources() */
04128 }
04129 
04130 static int gl2psPDFgroupListWriteVariableResources(void)
04131 {
04132   int offs = 0;
04133 
04134   /* a) Graphics States for shader alpha masks*/
04135   offs += gl2psPDFgroupListWriteGStateResources();
04136 
04137   /* b) Shader and shader masks */
04138   offs += gl2psPDFgroupListWriteShaderResources();
04139 
04140   /* c) XObjects (Images & Shader Masks) */
04141   offs += gl2psPDFgroupListWriteXObjectResources();
04142 
04143   /* d) Fonts */
04144   offs += gl2psPDFgroupListWriteFontResources();
04145 
04146   /* End resources and page */
04147   offs += fprintf(gl2ps->stream,
04148                   ">>\n"
04149                   ">>\n"
04150                   "endobj\n");
04151   return offs;
04152 }
04153 
04154 /* Standard Graphics State */
04155 
04156 static int gl2psPrintPDFGSObject(void)
04157 {
04158   return fprintf(gl2ps->stream,
04159                  "7 0 obj\n"
04160                  "<<\n"
04161                  "/Type /ExtGState\n"
04162                  "/SA false\n"
04163                  "/SM 0.02\n"
04164                  "/OP false\n"
04165                  "/op false\n"
04166                  "/OPM 0\n"
04167                  "/BG2 /Default\n"
04168                  "/UCR2 /Default\n"
04169                  "/TR2 /Default\n"
04170                  ">>\n"
04171                  "endobj\n");
04172 }
04173 
04174 /* Put vertex' edge flag (8bit) and coordinates (32bit) in shader stream */
04175 
04176 static int gl2psPrintPDFShaderStreamDataCoord(GL2PSvertex *vertex,
04177                                               int (*action)(unsigned long data, int size),
04178                                               GLfloat dx, GLfloat dy,
04179                                               GLfloat xmin, GLfloat ymin)
04180 {
04181   int offs = 0;
04182   unsigned long imap;
04183   GLfloat diff;
04184   double dmax = ~1UL;
04185   char edgeflag = 0;
04186 
04187   /* FIXME: temp bux fix for 64 bit archs: */
04188   if(sizeof(unsigned long) == 8) dmax = dmax - 2048.;
04189 
04190   offs += (*action)(edgeflag, 1);
04191 
04192   /* The Shader stream in PDF requires to be in a 'big-endian'
04193      order */
04194 
04195   if(GL2PS_ZERO(dx * dy)){
04196     offs += (*action)(0, 4);
04197     offs += (*action)(0, 4);
04198   }
04199   else{
04200     diff = (vertex->xyz[0] - xmin) / dx;
04201     if(diff > 1)
04202       diff = 1.0F;
04203     else if(diff < 0)
04204       diff = 0.0F;
04205     imap = (unsigned long)(diff * dmax);
04206     offs += (*action)(imap, 4);
04207 
04208     diff = (vertex->xyz[1] - ymin) / dy;
04209     if(diff > 1)
04210       diff = 1.0F;
04211     else if(diff < 0)
04212       diff = 0.0F;
04213     imap = (unsigned long)(diff * dmax);
04214     offs += (*action)(imap, 4);
04215   }
04216 
04217   return offs;
04218 }
04219 
04220 /* Put vertex' rgb value (8bit for every component) in shader stream */
04221 
04222 static int gl2psPrintPDFShaderStreamDataRGB(GL2PSvertex *vertex,
04223                                             int (*action)(unsigned long data, int size))
04224 {
04225   int offs = 0;
04226   unsigned long imap;
04227   double dmax = ~1UL;
04228 
04229   /* FIXME: temp bux fix for 64 bit archs: */
04230   if(sizeof(unsigned long) == 8) dmax = dmax - 2048.;
04231 
04232   imap = (unsigned long)((vertex->rgba[0]) * dmax);
04233   offs += (*action)(imap, 1);
04234 
04235   imap = (unsigned long)((vertex->rgba[1]) * dmax);
04236   offs += (*action)(imap, 1);
04237 
04238   imap = (unsigned long)((vertex->rgba[2]) * dmax);
04239   offs += (*action)(imap, 1);
04240 
04241   return offs;
04242 }
04243 
04244 /* Put vertex' alpha (8/16bit) in shader stream */
04245 
04246 static int gl2psPrintPDFShaderStreamDataAlpha(GL2PSvertex *vertex,
04247                                               int (*action)(unsigned long data, int size),
04248                                               int sigbyte)
04249 {
04250   int offs = 0;
04251   unsigned long imap;
04252   double dmax = ~1UL;
04253 
04254   /* FIXME: temp bux fix for 64 bit archs: */
04255   if(sizeof(unsigned long) == 8) dmax = dmax - 2048.;
04256 
04257   if(sigbyte != 8 && sigbyte != 16)
04258     sigbyte = 8;
04259 
04260   sigbyte /= 8;
04261 
04262   imap = (unsigned long)((vertex->rgba[3]) * dmax);
04263 
04264   offs += (*action)(imap, sigbyte);
04265 
04266   return offs;
04267 }
04268 
04269 /* Put a triangles raw data in shader stream */
04270 
04271 static int gl2psPrintPDFShaderStreamData(GL2PStriangle *triangle,
04272                                          GLfloat dx, GLfloat dy,
04273                                          GLfloat xmin, GLfloat ymin,
04274                                          int (*action)(unsigned long data, int size),
04275                                          int gray)
04276 {
04277   int i, offs = 0;
04278   GL2PSvertex v;
04279 
04280   if(gray && gray != 8 && gray != 16)
04281     gray = 8;
04282 
04283   for(i = 0; i < 3; ++i){
04284     offs += gl2psPrintPDFShaderStreamDataCoord(&triangle->vertex[i], action,
04285                                                dx, dy, xmin, ymin);
04286     if(gray){
04287       v = triangle->vertex[i];
04288       offs += gl2psPrintPDFShaderStreamDataAlpha(&v, action, gray);
04289     }
04290     else{
04291       offs += gl2psPrintPDFShaderStreamDataRGB(&triangle->vertex[i], action);
04292     }
04293   }
04294 
04295   return offs;
04296 }
04297 
04298 static void gl2psPDFRectHull(GLfloat *xmin, GLfloat *xmax,
04299                              GLfloat *ymin, GLfloat *ymax,
04300                              GL2PStriangle *triangles, int cnt)
04301 {
04302   int i, j;
04303 
04304   *xmin = triangles[0].vertex[0].xyz[0];
04305   *xmax = triangles[0].vertex[0].xyz[0];
04306   *ymin = triangles[0].vertex[0].xyz[1];
04307   *ymax = triangles[0].vertex[0].xyz[1];
04308 
04309   for(i = 0; i < cnt; ++i){
04310     for(j = 0; j < 3; ++j){
04311       if(*xmin > triangles[i].vertex[j].xyz[0])
04312         *xmin = triangles[i].vertex[j].xyz[0];
04313       if(*xmax < triangles[i].vertex[j].xyz[0])
04314         *xmax = triangles[i].vertex[j].xyz[0];
04315       if(*ymin > triangles[i].vertex[j].xyz[1])
04316         *ymin = triangles[i].vertex[j].xyz[1];
04317       if(*ymax < triangles[i].vertex[j].xyz[1])
04318         *ymax = triangles[i].vertex[j].xyz[1];
04319     }
04320   }
04321 }
04322 
04323 /* Writes shaded triangle
04324    gray == 0 means write RGB triangles
04325    gray == 8             8bit-grayscale (for alpha masks)
04326    gray == 16            16bit-grayscale (for alpha masks) */
04327 
04328 static int gl2psPrintPDFShader(int obj, GL2PStriangle *triangles,
04329                                int size, int gray)
04330 {
04331   int i, offs = 0, vertexbytes, done = 0;
04332   GLfloat xmin, xmax, ymin, ymax;
04333 
04334   switch(gray){
04335   case 0:
04336     vertexbytes = 1+4+4+1+1+1;
04337     break;
04338   case 8:
04339     vertexbytes = 1+4+4+1;
04340     break;
04341   case 16:
04342     vertexbytes = 1+4+4+2;
04343     break;
04344   default:
04345     gray = 8;
04346     vertexbytes = 1+4+4+1;
04347     break;
04348   }
04349 
04350   gl2psPDFRectHull(&xmin, &xmax, &ymin, &ymax, triangles, size);
04351 
04352   offs += fprintf(gl2ps->stream,
04353                   "%d 0 obj\n"
04354                   "<< "
04355                   "/ShadingType 4 "
04356                   "/ColorSpace %s "
04357                   "/BitsPerCoordinate 32 "
04358                   "/BitsPerComponent %d "
04359                   "/BitsPerFlag 8 "
04360                   "/Decode [%f %f %f %f 0 1 %s] ",
04361                   obj,
04362                   (gray) ? "/DeviceGray" : "/DeviceRGB",
04363                   (gray) ? gray : 8,
04364                   xmin, xmax, ymin, ymax,
04365                   (gray) ? "" : "0 1 0 1");
04366 
04367 #if defined(GL2PS_HAVE_ZLIB)
04368   if(gl2ps->options & GL2PS_COMPRESS){
04369     gl2psAllocCompress(vertexbytes * size * 3);
04370 
04371     for(i = 0; i < size; ++i)
04372       gl2psPrintPDFShaderStreamData(&triangles[i],
04373                                     xmax-xmin, ymax-ymin, xmin, ymin,
04374                                     gl2psWriteBigEndianCompress, gray);
04375 
04376     if(Z_OK == gl2psDeflate() && 23 + gl2ps->compress->destLen < gl2ps->compress->srcLen){
04377       offs += gl2psPrintPDFCompressorType();
04378       offs += fprintf(gl2ps->stream,
04379                       "/Length %d "
04380                       ">>\n"
04381                       "stream\n",
04382                       (int)gl2ps->compress->destLen);
04383       offs += gl2ps->compress->destLen * fwrite(gl2ps->compress->dest,
04384                                                 gl2ps->compress->destLen,
04385                                                 1, gl2ps->stream);
04386       done = 1;
04387     }
04388     gl2psFreeCompress();
04389   }
04390 #endif
04391 
04392   if(!done){
04393     /* no compression, or too long after compression, or compress error
04394        -> write non-compressed entry */
04395     offs += fprintf(gl2ps->stream,
04396                     "/Length %d "
04397                     ">>\n"
04398                     "stream\n",
04399                     vertexbytes * 3 * size);
04400     for(i = 0; i < size; ++i)
04401       offs += gl2psPrintPDFShaderStreamData(&triangles[i],
04402                                             xmax-xmin, ymax-ymin, xmin, ymin,
04403                                             gl2psWriteBigEndian, gray);
04404   }
04405 
04406   offs += fprintf(gl2ps->stream,
04407                   "\nendstream\n"
04408                   "endobj\n");
04409 
04410   return offs;
04411 }
04412 
04413 /* Writes a XObject for a shaded triangle mask */
04414 
04415 static int gl2psPrintPDFShaderMask(int obj, int childobj)
04416 {
04417   int offs = 0, len;
04418 
04419   offs += fprintf(gl2ps->stream,
04420                   "%d 0 obj\n"
04421                   "<<\n"
04422                   "/Type /XObject\n"
04423                   "/Subtype /Form\n"
04424                   "/BBox [ %d %d %d %d ]\n"
04425                   "/Group \n<<\n/S /Transparency /CS /DeviceRGB\n"
04426                   ">>\n",
04427                   obj,
04428                   (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
04429                   (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
04430 
04431   len = (childobj>0)
04432     ? strlen("/TrSh sh\n") + (int)log10((double)childobj)+1
04433     : strlen("/TrSh0 sh\n");
04434 
04435   offs += fprintf(gl2ps->stream,
04436                   "/Length %d\n"
04437                   ">>\n"
04438                   "stream\n",
04439                   len);
04440   offs += fprintf(gl2ps->stream,
04441                   "/TrSh%d sh\n",
04442                   childobj);
04443   offs += fprintf(gl2ps->stream,
04444                   "endstream\n"
04445                   "endobj\n");
04446 
04447   return offs;
04448 }
04449 
04450 /* Writes a Extended graphics state for a shaded triangle mask if
04451    simplealpha ist true the childobj argument is ignored and a /ca
04452    statement will be written instead */
04453 
04454 static int gl2psPrintPDFShaderExtGS(int obj, int childobj)
04455 {
04456   int offs = 0;
04457 
04458   offs += fprintf(gl2ps->stream,
04459                   "%d 0 obj\n"
04460                   "<<\n",
04461                   obj);
04462 
04463   offs += fprintf(gl2ps->stream,
04464                   "/SMask << /S /Alpha /G %d 0 R >> ",
04465                   childobj);
04466 
04467   offs += fprintf(gl2ps->stream,
04468                   ">>\n"
04469                   "endobj\n");
04470   return offs;
04471 }
04472 
04473 /* a simple graphics state */
04474 
04475 static int gl2psPrintPDFShaderSimpleExtGS(int obj, GLfloat alpha)
04476 {
04477   int offs = 0;
04478 
04479   offs += fprintf(gl2ps->stream,
04480                   "%d 0 obj\n"
04481                   "<<\n"
04482                   "/ca %g"
04483                   ">>\n"
04484                   "endobj\n",
04485                   obj, alpha);
04486   return offs;
04487 }
04488 
04489 /* Similar groups of functions for pixmaps and text */
04490 
04491 static int gl2psPrintPDFPixmapStreamData(GL2PSimage *im,
04492                                          int (*action)(unsigned long data, int size),
04493                                          int gray)
04494 {
04495   int x, y, shift;
04496   GLfloat r, g, b, a;
04497 
04498   if(im->format != GL_RGBA && gray)
04499     return 0;
04500 
04501   if(gray && gray != 8 && gray != 16)
04502     gray = 8;
04503 
04504   gray /= 8;
04505 
04506   shift = (sizeof(unsigned long) - 1) * 8;
04507 
04508   for(y = 0; y < im->height; ++y){
04509     for(x = 0; x < im->width; ++x){
04510       a = gl2psGetRGB(im, x, y, &r, &g, &b);
04511       if(im->format == GL_RGBA && gray){
04512         (*action)((unsigned long)(a * 255) << shift, gray);
04513       }
04514       else{
04515         (*action)((unsigned long)(r * 255) << shift, 1);
04516         (*action)((unsigned long)(g * 255) << shift, 1);
04517         (*action)((unsigned long)(b * 255) << shift, 1);
04518       }
04519     }
04520   }
04521 
04522   switch(gray){
04523   case 0: return 3 * im->width * im->height;
04524   case 1: return im->width * im->height;
04525   case 2: return 2 * im->width * im->height;
04526   default: return 3 * im->width * im->height;
04527   }
04528 }
04529 
04530 static int gl2psPrintPDFPixmap(int obj, int childobj, GL2PSimage *im, int gray)
04531 {
04532   int offs = 0, done = 0, sigbytes = 3;
04533 
04534   if(gray && gray !=8 && gray != 16)
04535     gray = 8;
04536 
04537   if(gray)
04538     sigbytes = gray / 8;
04539 
04540   offs += fprintf(gl2ps->stream,
04541                   "%d 0 obj\n"
04542                   "<<\n"
04543                   "/Type /XObject\n"
04544                   "/Subtype /Image\n"
04545                   "/Width %d\n"
04546                   "/Height %d\n"
04547                   "/ColorSpace %s \n"
04548                   "/BitsPerComponent 8\n",
04549                   obj,
04550                   (int)im->width, (int)im->height,
04551                   (gray) ? "/DeviceGray" : "/DeviceRGB" );
04552   if(GL_RGBA == im->format && gray == 0){
04553     offs += fprintf(gl2ps->stream,
04554                     "/SMask %d 0 R\n",
04555                     childobj);
04556   }
04557 
04558 #if defined(GL2PS_HAVE_ZLIB)
04559   if(gl2ps->options & GL2PS_COMPRESS){
04560     gl2psAllocCompress((int)(im->width * im->height * sigbytes));
04561 
04562     gl2psPrintPDFPixmapStreamData(im, gl2psWriteBigEndianCompress, gray);
04563 
04564     if(Z_OK == gl2psDeflate() && 23 + gl2ps->compress->destLen < gl2ps->compress->srcLen){
04565       offs += gl2psPrintPDFCompressorType();
04566       offs += fprintf(gl2ps->stream,
04567                       "/Length %d "
04568                       ">>\n"
04569                       "stream\n",
04570                       (int)gl2ps->compress->destLen);
04571       offs += gl2ps->compress->destLen * fwrite(gl2ps->compress->dest, gl2ps->compress->destLen,
04572                                                 1, gl2ps->stream);
04573       done = 1;
04574     }
04575     gl2psFreeCompress();
04576   }
04577 #endif
04578 
04579   if(!done){
04580     /* no compression, or too long after compression, or compress error
04581        -> write non-compressed entry */
04582     offs += fprintf(gl2ps->stream,
04583                     "/Length %d "
04584                     ">>\n"
04585                     "stream\n",
04586                     (int)(im->width * im->height * sigbytes));
04587     offs += gl2psPrintPDFPixmapStreamData(im, gl2psWriteBigEndian, gray);
04588   }
04589 
04590   offs += fprintf(gl2ps->stream,
04591                   "\nendstream\n"
04592                   "endobj\n");
04593 
04594   return offs;
04595 }
04596 
04597 static int gl2psPrintPDFText(int obj, GL2PSstring *s, int fontnumber)
04598 {
04599   int offs = 0;
04600 
04601   offs += fprintf(gl2ps->stream,
04602                   "%d 0 obj\n"
04603                   "<<\n"
04604                   "/Type /Font\n"
04605                   "/Subtype /Type1\n"
04606                   "/Name /F%d\n"
04607                   "/BaseFont /%s\n"
04608                   "/Encoding /MacRomanEncoding\n"
04609                   ">>\n"
04610                   "endobj\n",
04611                   obj, fontnumber, s->fontname);
04612   return offs;
04613 }
04614 
04615 /* Write the physical objects */
04616 
04617 static int gl2psPDFgroupListWriteObjects(int entryoffs)
04618 {
04619   int i,j;
04620   GL2PSprimitive *p = NULL;
04621   GL2PSpdfgroup *gro;
04622   int offs = entryoffs;
04623   GL2PStriangle *triangles;
04624   int size = 0;
04625 
04626   if(!gl2ps->pdfgrouplist)
04627     return offs;
04628 
04629   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
04630     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
04631     if(!gl2psListNbr(gro->ptrlist))
04632       continue;
04633     p = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);
04634     switch(p->type){
04635     case GL2PS_POINT:
04636       break;
04637     case GL2PS_LINE:
04638       break;
04639     case GL2PS_TRIANGLE:
04640       size = gl2psListNbr(gro->ptrlist);
04641       triangles = (GL2PStriangle*)gl2psMalloc(sizeof(GL2PStriangle) * size);
04642       for(j = 0; j < size; ++j){
04643         p = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
04644         gl2psFillTriangleFromPrimitive(&triangles[j], p, GL_TRUE);
04645       }
04646       if(triangles[0].prop & T_VAR_COLOR){
04647         gl2ps->xreflist[gro->shobjno] = offs;
04648         offs += gl2psPrintPDFShader(gro->shobjno, triangles, size, 0);
04649       }
04650       if(triangles[0].prop & T_ALPHA_LESS_1){
04651         gl2ps->xreflist[gro->gsobjno] = offs;
04652         offs += gl2psPrintPDFShaderSimpleExtGS(gro->gsobjno, triangles[0].vertex[0].rgba[3]);
04653       }
04654       if(triangles[0].prop & T_VAR_ALPHA){
04655         gl2ps->xreflist[gro->gsobjno] = offs;
04656         offs += gl2psPrintPDFShaderExtGS(gro->gsobjno, gro->trgroupobjno);
04657         gl2ps->xreflist[gro->trgroupobjno] = offs;
04658         offs += gl2psPrintPDFShaderMask(gro->trgroupobjno, gro->maskshno);
04659         gl2ps->xreflist[gro->maskshobjno] = offs;
04660         offs += gl2psPrintPDFShader(gro->maskshobjno, triangles, size, 8);
04661       }
04662       gl2psFree(triangles);
04663       break;
04664     case GL2PS_PIXMAP:
04665       gl2ps->xreflist[gro->imobjno] = offs;
04666       offs += gl2psPrintPDFPixmap(gro->imobjno, gro->imobjno+1, p->data.image, 0);
04667       if(p->data.image->format == GL_RGBA){
04668         gl2ps->xreflist[gro->imobjno+1] = offs;
04669         offs += gl2psPrintPDFPixmap(gro->imobjno+1, -1, p->data.image, 8);
04670       }
04671       break;
04672     case GL2PS_TEXT:
04673       gl2ps->xreflist[gro->fontobjno] = offs;
04674       offs += gl2psPrintPDFText(gro->fontobjno,p->data.text,gro->fontno);
04675       break;
04676     case GL2PS_SPECIAL :
04677       /* alignment contains the format for which the special output text
04678          is intended */
04679       if(p->data.text->alignment == GL2PS_PDF)
04680         offs += fprintf(gl2ps->stream, "%s\n", p->data.text->str);
04681       break;
04682     default:
04683       break;
04684     }
04685   }
04686   return offs;
04687 }
04688 
04689 /* All variable data has been written at this point and all required
04690    functioninality has been gathered, so we can write now file footer
04691    with cross reference table and trailer */
04692 
04693 static void gl2psPrintPDFFooter(void)
04694 {
04695   int i, offs;
04696 
04697   gl2psPDFgroupListInit();
04698   gl2psPDFgroupListWriteMainStream();
04699 
04700   offs = gl2ps->xreflist[5] + gl2ps->streamlength;
04701   offs += gl2psClosePDFDataStream();
04702   gl2ps->xreflist[5] = offs;
04703 
04704   offs += gl2psPrintPDFDataStreamLength(gl2ps->streamlength);
04705   gl2ps->xreflist[6] = offs;
04706   gl2ps->streamlength = 0;
04707 
04708   offs += gl2psPrintPDFOpenPage();
04709   offs += gl2psPDFgroupListWriteVariableResources();
04710   gl2ps->xreflist = (int*)gl2psRealloc(gl2ps->xreflist,
04711                                        sizeof(int) * (gl2ps->objects_stack + 1));
04712   gl2ps->xreflist[7] = offs;
04713 
04714   offs += gl2psPrintPDFGSObject();
04715   gl2ps->xreflist[8] = offs;
04716 
04717   gl2ps->xreflist[gl2ps->objects_stack] =
04718     gl2psPDFgroupListWriteObjects(gl2ps->xreflist[8]);
04719 
04720   /* Start cross reference table. The file has to been opened in
04721      binary mode to preserve the 20 digit string length! */
04722   fprintf(gl2ps->stream,
04723           "xref\n"
04724           "0 %d\n"
04725           "%010d 65535 f \n", gl2ps->objects_stack, 0);
04726 
04727   for(i = 1; i < gl2ps->objects_stack; ++i)
04728     fprintf(gl2ps->stream, "%010d 00000 n \n", gl2ps->xreflist[i]);
04729 
04730   fprintf(gl2ps->stream,
04731           "trailer\n"
04732           "<<\n"
04733           "/Size %d\n"
04734           "/Info 1 0 R\n"
04735           "/Root 2 0 R\n"
04736           ">>\n"
04737           "startxref\n%d\n"
04738           "%%%%EOF\n",
04739           gl2ps->objects_stack, gl2ps->xreflist[gl2ps->objects_stack]);
04740 
04741   /* Free auxiliary lists and arrays */
04742   gl2psFree(gl2ps->xreflist);
04743   gl2psListAction(gl2ps->pdfprimlist, gl2psFreePrimitive);
04744   gl2psListDelete(gl2ps->pdfprimlist);
04745   gl2psPDFgroupListDelete();
04746 
04747 #if defined(GL2PS_HAVE_ZLIB)
04748   if(gl2ps->options & GL2PS_COMPRESS){
04749     gl2psFreeCompress();
04750     gl2psFree(gl2ps->compress);
04751     gl2ps->compress = NULL;
04752   }
04753 #endif
04754 }
04755 
04756 /* PDF begin viewport */
04757 
04758 static void gl2psPrintPDFBeginViewport(GLint viewport[4])
04759 {
04760   int offs = 0;
04761   GLint index;
04762   GLfloat rgba[4];
04763   int x = viewport[0], y = viewport[1], w = viewport[2], h = viewport[3];
04764 
04765   glRenderMode(GL_FEEDBACK);
04766 
04767   if(gl2ps->header){
04768     gl2psPrintPDFHeader();
04769     gl2ps->header = GL_FALSE;
04770   }
04771 
04772   offs += gl2psPrintf("q\n");
04773 
04774   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
04775     if(gl2ps->colormode == GL_RGBA || gl2ps->colorsize == 0){
04776       glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba);
04777     }
04778     else{
04779       glGetIntegerv(GL_INDEX_CLEAR_VALUE, &index);
04780       rgba[0] = gl2ps->colormap[index][0];
04781       rgba[1] = gl2ps->colormap[index][1];
04782       rgba[2] = gl2ps->colormap[index][2];
04783       rgba[3] = 1.0F;
04784     }
04785     offs += gl2psPrintPDFFillColor(rgba);
04786     offs += gl2psPrintf("%d %d %d %d re\n"
04787                         "W\n"
04788                         "f\n",
04789                         x, y, w, h);
04790   }
04791   else{
04792     offs += gl2psPrintf("%d %d %d %d re\n"
04793                         "W\n"
04794                         "n\n",
04795                         x, y, w, h);
04796   }
04797 
04798   gl2ps->streamlength += offs;
04799 }
04800 
04801 static GLint gl2psPrintPDFEndViewport(void)
04802 {
04803   GLint res;
04804 
04805   res = gl2psPrintPrimitives();
04806   gl2ps->streamlength += gl2psPrintf("Q\n");
04807   return res;
04808 }
04809 
04810 static void gl2psPrintPDFFinalPrimitive(void)
04811 {
04812 }
04813 
04814 /* definition of the PDF backend */
04815 
04816 static GL2PSbackend gl2psPDF = {
04817   gl2psPrintPDFHeader,
04818   gl2psPrintPDFFooter,
04819   gl2psPrintPDFBeginViewport,
04820   gl2psPrintPDFEndViewport,
04821   gl2psPrintPDFPrimitive,
04822   gl2psPrintPDFFinalPrimitive,
04823   "pdf",
04824   "Portable Document Format"
04825 };
04826 
04827 /*********************************************************************
04828  *
04829  * SVG routines
04830  *
04831  *********************************************************************/
04832 
04833 static void gl2psSVGGetCoordsAndColors(int n, GL2PSvertex *verts,
04834                                        GL2PSxyz *xyz, GL2PSrgba *rgba)
04835 {
04836   int i, j;
04837 
04838   for(i = 0; i < n; i++){
04839     xyz[i][0] = verts[i].xyz[0];
04840     xyz[i][1] = gl2ps->viewport[3] - verts[i].xyz[1];
04841     xyz[i][2] = 0.0F;
04842     for(j = 0; j < 4; j++)
04843       rgba[i][j] = verts[i].rgba[j];
04844   }
04845 }
04846 
04847 static void gl2psSVGGetColorString(GL2PSrgba rgba, char str[32])
04848 {
04849   int r = (int)(255. * rgba[0]);
04850   int g = (int)(255. * rgba[1]);
04851   int b = (int)(255. * rgba[2]);
04852   int rc = (r < 0) ? 0 : (r > 255) ? 255 : r;
04853   int gc = (g < 0) ? 0 : (g > 255) ? 255 : g;
04854   int bc = (b < 0) ? 0 : (b > 255) ? 255 : b;
04855   sprintf(str, "#%2.2x%2.2x%2.2x", rc, gc, bc);
04856 }
04857 
04858 static void gl2psPrintSVGHeader(void)
04859 {
04860   int x, y, width, height;
04861   char col[32];
04862   time_t now;
04863 
04864   time(&now);
04865 
04866   if (gl2ps->options & GL2PS_LANDSCAPE){
04867     x = (int)gl2ps->viewport[1];
04868     y = (int)gl2ps->viewport[0];
04869     width = (int)gl2ps->viewport[3];
04870     height = (int)gl2ps->viewport[2];
04871   }
04872   else{
04873     x = (int)gl2ps->viewport[0];
04874     y = (int)gl2ps->viewport[1];
04875     width = (int)gl2ps->viewport[2];
04876     height = (int)gl2ps->viewport[3];
04877   }
04878 
04879   /* Compressed SVG files (.svgz) are simply gzipped SVG files */
04880   gl2psPrintGzipHeader();
04881 
04882   gl2psPrintf("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
04883   gl2psPrintf("<svg xmlns=\"http://www.w3.org/2000/svg\"\n");
04884   gl2psPrintf("     xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
04885               "     width=\"%dpx\" height=\"%dpx\" viewBox=\"%d %d %d %d\">\n",
04886               width, height, x, y, width, height);
04887   gl2psPrintf("<title>%s</title>\n", gl2ps->title);
04888   gl2psPrintf("<desc>\n");
04889   gl2psPrintf("Creator: GL2PS %d.%d.%d%s, %s\n"
04890               "For: %s\n"
04891               "CreationDate: %s",
04892               GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION, GL2PS_PATCH_VERSION,
04893               GL2PS_EXTRA_VERSION, GL2PS_COPYRIGHT, gl2ps->producer, ctime(&now));
04894   gl2psPrintf("</desc>\n");
04895   gl2psPrintf("<defs>\n");
04896   gl2psPrintf("</defs>\n");
04897 
04898   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
04899     gl2psSVGGetColorString(gl2ps->bgcolor, col);
04900     gl2psPrintf("<polygon fill=\"%s\" points=\"%d,%d %d,%d %d,%d %d,%d\"/>\n", col,
04901                 (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
04902                 (int)gl2ps->viewport[2], (int)gl2ps->viewport[1],
04903                 (int)gl2ps->viewport[2], (int)gl2ps->viewport[3],
04904                 (int)gl2ps->viewport[0], (int)gl2ps->viewport[3]);
04905   }
04906 
04907   /* group all the primitives and disable antialiasing */
04908   gl2psPrintf("<g shape-rendering=\"crispEdges\">\n");
04909 }
04910 
04911 static void gl2psPrintSVGSmoothTriangle(GL2PSxyz xyz[3], GL2PSrgba rgba[3])
04912 {
04913   int i;
04914   GL2PSxyz xyz2[3];
04915   GL2PSrgba rgba2[3];
04916   char col[32];
04917 
04918   /* Apparently there is no easy way to do Gouraud shading in SVG
04919      without explicitly pre-defining gradients, so for now we just do
04920      recursive subdivision */
04921 
04922   if(gl2psSameColorThreshold(3, rgba, gl2ps->threshold)){
04923     gl2psSVGGetColorString(rgba[0], col);
04924     gl2psPrintf("<polygon fill=\"%s\" ", col);
04925     if(rgba[0][3] < 1.0F) gl2psPrintf("fill-opacity=\"%g\" ", rgba[0][3]);
04926     gl2psPrintf("points=\"%g,%g %g,%g %g,%g\"/>\n", xyz[0][0], xyz[0][1],
04927                 xyz[1][0], xyz[1][1], xyz[2][0], xyz[2][1]);
04928   }
04929   else{
04930     /* subdivide into 4 subtriangles */
04931     for(i = 0; i < 3; i++){
04932       xyz2[0][i] = xyz[0][i];
04933       xyz2[1][i] = 0.5F * (xyz[0][i] + xyz[1][i]);
04934       xyz2[2][i] = 0.5F * (xyz[0][i] + xyz[2][i]);
04935     }
04936     for(i = 0; i < 4; i++){
04937       rgba2[0][i] = rgba[0][i];
04938       rgba2[1][i] = 0.5F * (rgba[0][i] + rgba[1][i]);
04939       rgba2[2][i] = 0.5F * (rgba[0][i] + rgba[2][i]);
04940     }
04941     gl2psPrintSVGSmoothTriangle(xyz2, rgba2);
04942     for(i = 0; i < 3; i++){
04943       xyz2[0][i] = 0.5F * (xyz[0][i] + xyz[1][i]);
04944       xyz2[1][i] = xyz[1][i];
04945       xyz2[2][i] = 0.5F * (xyz[1][i] + xyz[2][i]);
04946     }
04947     for(i = 0; i < 4; i++){
04948       rgba2[0][i] = 0.5F * (rgba[0][i] + rgba[1][i]);
04949       rgba2[1][i] = rgba[1][i];
04950       rgba2[2][i] = 0.5F * (rgba[1][i] + rgba[2][i]);
04951     }
04952     gl2psPrintSVGSmoothTriangle(xyz2, rgba2);
04953     for(i = 0; i < 3; i++){
04954       xyz2[0][i] = 0.5F * (xyz[0][i] + xyz[2][i]);
04955       xyz2[1][i] = xyz[2][i];
04956       xyz2[2][i] = 0.5F * (xyz[1][i] + xyz[2][i]);
04957     }
04958     for(i = 0; i < 4; i++){
04959       rgba2[0][i] = 0.5F * (rgba[0][i] + rgba[2][i]);
04960       rgba2[1][i] = rgba[2][i];
04961       rgba2[2][i] = 0.5F * (rgba[1][i] + rgba[2][i]);
04962     }
04963     gl2psPrintSVGSmoothTriangle(xyz2, rgba2);
04964     for(i = 0; i < 3; i++){
04965       xyz2[0][i] = 0.5F * (xyz[0][i] + xyz[1][i]);
04966       xyz2[1][i] = 0.5F * (xyz[1][i] + xyz[2][i]);
04967       xyz2[2][i] = 0.5F * (xyz[0][i] + xyz[2][i]);
04968     }
04969     for(i = 0; i < 4; i++){
04970       rgba2[0][i] = 0.5F * (rgba[0][i] + rgba[1][i]);
04971       rgba2[1][i] = 0.5F * (rgba[1][i] + rgba[2][i]);
04972       rgba2[2][i] = 0.5F * (rgba[0][i] + rgba[2][i]);
04973     }
04974     gl2psPrintSVGSmoothTriangle(xyz2, rgba2);
04975   }
04976 }
04977 
04978 static void gl2psPrintSVGDash(GLushort pattern, GLint factor)
04979 {
04980   int i, n, array[10];
04981 
04982   if(!pattern || !factor) return; /* solid line */
04983 
04984   gl2psParseStipplePattern(pattern, factor, &n, array);
04985   gl2psPrintf("stroke-dasharray=\"");
04986   for(i = 0; i < n; i++){
04987     if(i) gl2psPrintf(",");
04988     gl2psPrintf("%d", array[i]);
04989   }
04990   gl2psPrintf("\" ");
04991 }
04992 
04993 static void gl2psEndSVGLine(void)
04994 {
04995   int i;
04996   if(gl2ps->lastvertex.rgba[0] >= 0.){
04997     gl2psPrintf("%g,%g\"/>\n", gl2ps->lastvertex.xyz[0],
04998                 gl2ps->viewport[3] - gl2ps->lastvertex.xyz[1]);
04999     for(i = 0; i < 3; i++)
05000       gl2ps->lastvertex.xyz[i] = -1.;
05001     for(i = 0; i < 4; i++)
05002       gl2ps->lastvertex.rgba[i] = -1.;
05003   }
05004 }
05005 
05006 static void gl2psPrintSVGPixmap(GLfloat x, GLfloat y, GL2PSimage *pixmap)
05007 {
05008 #if defined(GL2PS_HAVE_LIBPNG)
05009   GL2PSlist *png;
05010   unsigned char c;
05011   int i;
05012 
05013   /* The only image types supported by the SVG standard are JPEG, PNG
05014      and SVG. Here we choose PNG, and since we want to embed the image
05015      directly in the SVG stream (and not link to an external image
05016      file), we need to encode the pixmap into PNG in memory, then
05017      encode it into base64. */
05018 
05019   png = gl2psListCreate(pixmap->width * pixmap->height * 3, 1000,
05020                         sizeof(unsigned char));
05021   gl2psConvertPixmapToPNG(pixmap, png);
05022   gl2psListEncodeBase64(png);
05023   gl2psPrintf("<image x=\"%g\" y=\"%g\" width=\"%d\" height=\"%d\"\n",
05024               x, y - pixmap->height, pixmap->width, pixmap->height);
05025   gl2psPrintf("xlink:href=\"data:image/png;base64,");
05026   for(i = 0; i < gl2psListNbr(png); i++){
05027     gl2psListRead(png, i, &c);
05028     gl2psPrintf("%c", c);
05029   }
05030   gl2psPrintf("\"/>\n");
05031   gl2psListDelete(png);
05032 #else
05033   (void) x; (void) y; (void) pixmap;  /* not used */
05034   gl2psMsg(GL2PS_WARNING, "GL2PS must be compiled with PNG support in "
05035            "order to embed images in SVG streams");
05036 #endif
05037 }
05038 
05039 static void gl2psPrintSVGPrimitive(void *data)
05040 {
05041   GL2PSprimitive *prim;
05042   GL2PSxyz xyz[4];
05043   GL2PSrgba rgba[4];
05044   char col[32];
05045   int newline;
05046 
05047   prim = *(GL2PSprimitive**)data;
05048 
05049   if((gl2ps->options & GL2PS_OCCLUSION_CULL) && prim->culled) return;
05050 
05051   /* We try to draw connected lines as a single path to get nice line
05052      joins and correct stippling. So if the primitive to print is not
05053      a line we must first finish the current line (if any): */
05054   if(prim->type != GL2PS_LINE) gl2psEndSVGLine();
05055 
05056   gl2psSVGGetCoordsAndColors(prim->numverts, prim->verts, xyz, rgba);
05057 
05058   switch(prim->type){
05059   case GL2PS_POINT :
05060     gl2psSVGGetColorString(rgba[0], col);
05061     gl2psPrintf("<circle fill=\"%s\" ", col);
05062     if(rgba[0][3] < 1.0F) gl2psPrintf("fill-opacity=\"%g\" ", rgba[0][3]);
05063     gl2psPrintf("cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
05064                 xyz[0][0], xyz[0][1], 0.5 * prim->width);
05065     break;
05066   case GL2PS_LINE :
05067     if(!gl2psSamePosition(gl2ps->lastvertex.xyz, prim->verts[0].xyz) ||
05068        !gl2psSameColor(gl2ps->lastrgba, prim->verts[0].rgba) ||
05069        gl2ps->lastlinewidth != prim->width ||
05070        gl2ps->lastpattern != prim->pattern ||
05071        gl2ps->lastfactor != prim->factor){
05072       /* End the current line if the new segment does not start where
05073          the last one ended, or if the color, the width or the
05074          stippling have changed (we will need to use multi-point
05075          gradients for smooth-shaded lines) */
05076       gl2psEndSVGLine();
05077       newline = 1;
05078     }
05079     else{
05080       newline = 0;
05081     }
05082     gl2ps->lastvertex = prim->verts[1];
05083     gl2psSetLastColor(prim->verts[0].rgba);
05084     gl2ps->lastlinewidth = prim->width;
05085     gl2ps->lastpattern = prim->pattern;
05086     gl2ps->lastfactor = prim->factor;
05087     if(newline){
05088       gl2psSVGGetColorString(rgba[0], col);
05089       gl2psPrintf("<polyline fill=\"none\" stroke=\"%s\" stroke-width=\"%g\" ",
05090                   col, prim->width);
05091       if(rgba[0][3] < 1.0F) gl2psPrintf("stroke-opacity=\"%g\" ", rgba[0][3]);
05092       gl2psPrintSVGDash(prim->pattern, prim->factor);
05093       gl2psPrintf("points=\"%g,%g ", xyz[0][0], xyz[0][1]);
05094     }
05095     else{
05096       gl2psPrintf("%g,%g ", xyz[0][0], xyz[0][1]);
05097     }
05098     break;
05099   case GL2PS_TRIANGLE :
05100     gl2psPrintSVGSmoothTriangle(xyz, rgba);
05101     break;
05102   case GL2PS_QUADRANGLE :
05103     gl2psMsg(GL2PS_WARNING, "There should not be any quad left to print");
05104     break;
05105   case GL2PS_PIXMAP :
05106     gl2psPrintSVGPixmap(xyz[0][0], xyz[0][1], prim->data.image);
05107     break;
05108   case GL2PS_TEXT :
05109     gl2psSVGGetColorString(prim->verts[0].rgba, col);
05110     gl2psPrintf("<text fill=\"%s\" x=\"%g\" y=\"%g\" font-size=\"%d\" ",
05111                 col, xyz[0][0], xyz[0][1], prim->data.text->fontsize);
05112     if(prim->data.text->angle)
05113       gl2psPrintf("transform=\"rotate(%g, %g, %g)\" ",
05114                   -prim->data.text->angle, xyz[0][0], xyz[0][1]);
05115     switch(prim->data.text->alignment){
05116     case GL2PS_TEXT_C:
05117       gl2psPrintf("text-anchor=\"middle\" baseline-shift=\"%d\" ",
05118                   -prim->data.text->fontsize / 2);
05119       break;
05120     case GL2PS_TEXT_CL:
05121       gl2psPrintf("text-anchor=\"start\" baseline-shift=\"%d\" ",
05122                   -prim->data.text->fontsize / 2);
05123       break;
05124     case GL2PS_TEXT_CR:
05125       gl2psPrintf("text-anchor=\"end\" baseline-shift=\"%d\" ",
05126                   -prim->data.text->fontsize / 2);
05127       break;
05128     case GL2PS_TEXT_B:
05129       gl2psPrintf("text-anchor=\"middle\" baseline-shift=\"0\" ");
05130       break;
05131     case GL2PS_TEXT_BR:
05132       gl2psPrintf("text-anchor=\"end\" baseline-shift=\"0\" ");
05133       break;
05134     case GL2PS_TEXT_T:
05135       gl2psPrintf("text-anchor=\"middle\" baseline-shift=\"%d\" ",
05136                   -prim->data.text->fontsize);
05137       break;
05138     case GL2PS_TEXT_TL:
05139       gl2psPrintf("text-anchor=\"start\" baseline-shift=\"%d\" ",
05140                   -prim->data.text->fontsize);
05141       break;
05142     case GL2PS_TEXT_TR:
05143       gl2psPrintf("text-anchor=\"end\" baseline-shift=\"%d\" ",
05144                   -prim->data.text->fontsize);
05145       break;
05146     case GL2PS_TEXT_BL:
05147     default: /* same as GL2PS_TEXT_BL */
05148       gl2psPrintf("text-anchor=\"start\" baseline-shift=\"0\" ");
05149       break;
05150     }
05151     if(!strcmp(prim->data.text->fontname, "Times-Roman"))
05152       gl2psPrintf("font-family=\"Times\">");
05153     else if(!strcmp(prim->data.text->fontname, "Times-Bold"))
05154       gl2psPrintf("font-family=\"Times\" font-weight=\"bold\">");
05155     else if(!strcmp(prim->data.text->fontname, "Times-Italic"))
05156       gl2psPrintf("font-family=\"Times\" font-style=\"italic\">");
05157     else if(!strcmp(prim->data.text->fontname, "Times-BoldItalic"))
05158       gl2psPrintf("font-family=\"Times\" font-style=\"italic\" font-weight=\"bold\">");
05159     else if(!strcmp(prim->data.text->fontname, "Helvetica-Bold"))
05160       gl2psPrintf("font-family=\"Helvetica\" font-weight=\"bold\">");
05161     else if(!strcmp(prim->data.text->fontname, "Helvetica-Oblique"))
05162       gl2psPrintf("font-family=\"Helvetica\" font-style=\"oblique\">");
05163     else if(!strcmp(prim->data.text->fontname, "Helvetica-BoldOblique"))
05164       gl2psPrintf("font-family=\"Helvetica\" font-style=\"oblique\" font-weight=\"bold\">");
05165     else if(!strcmp(prim->data.text->fontname, "Courier-Bold"))
05166       gl2psPrintf("font-family=\"Courier\" font-weight=\"bold\">");
05167     else if(!strcmp(prim->data.text->fontname, "Courier-Oblique"))
05168       gl2psPrintf("font-family=\"Courier\" font-style=\"oblique\">");
05169     else if(!strcmp(prim->data.text->fontname, "Courier-BoldOblique"))
05170       gl2psPrintf("font-family=\"Courier\" font-style=\"oblique\" font-weight=\"bold\">");
05171     else
05172       gl2psPrintf("font-family=\"%s\">", prim->data.text->fontname);
05173     gl2psPrintf("%s</text>\n", prim->data.text->str);
05174     break;
05175   case GL2PS_SPECIAL :
05176     /* alignment contains the format for which the special output text
05177        is intended */
05178     if(prim->data.text->alignment == GL2PS_SVG)
05179       gl2psPrintf("%s\n", prim->data.text->str);
05180     break;
05181   default :
05182     break;
05183   }
05184 }
05185 
05186 static void gl2psPrintSVGFooter(void)
05187 {
05188   gl2psPrintf("</g>\n");
05189   gl2psPrintf("</svg>\n");
05190 
05191   gl2psPrintGzipFooter();
05192 }
05193 
05194 static void gl2psPrintSVGBeginViewport(GLint viewport[4])
05195 {
05196   GLint index;
05197   char col[32];
05198   GLfloat rgba[4];
05199   int x = viewport[0], y = viewport[1], w = viewport[2], h = viewport[3];
05200 
05201   glRenderMode(GL_FEEDBACK);
05202 
05203   if(gl2ps->header){
05204     gl2psPrintSVGHeader();
05205     gl2ps->header = GL_FALSE;
05206   }
05207 
05208   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
05209     if(gl2ps->colormode == GL_RGBA || gl2ps->colorsize == 0){
05210       glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba);
05211     }
05212     else{
05213       glGetIntegerv(GL_INDEX_CLEAR_VALUE, &index);
05214       rgba[0] = gl2ps->colormap[index][0];
05215       rgba[1] = gl2ps->colormap[index][1];
05216       rgba[2] = gl2ps->colormap[index][2];
05217       rgba[3] = 1.0F;
05218     }
05219     gl2psSVGGetColorString(rgba, col);
05220     gl2psPrintf("<polygon fill=\"%s\" points=\"%d,%d %d,%d %d,%d %d,%d\"/>\n", col,
05221                 x, gl2ps->viewport[3] - y,
05222                 x + w, gl2ps->viewport[3] - y,
05223                 x + w, gl2ps->viewport[3] - (y + h),
05224                 x, gl2ps->viewport[3] - (y + h));
05225   }
05226 
05227   gl2psPrintf("<clipPath id=\"cp%d%d%d%d\">\n", x, y, w, h);
05228   gl2psPrintf("  <polygon points=\"%d,%d %d,%d %d,%d %d,%d\"/>\n",
05229               x, gl2ps->viewport[3] - y,
05230               x + w, gl2ps->viewport[3] - y,
05231               x + w, gl2ps->viewport[3] - (y + h),
05232               x, gl2ps->viewport[3] - (y + h));
05233   gl2psPrintf("</clipPath>\n");
05234   gl2psPrintf("<g clip-path=\"url(#cp%d%d%d%d)\">\n", x, y, w, h);
05235 }
05236 
05237 static GLint gl2psPrintSVGEndViewport(void)
05238 {
05239   GLint res;
05240 
05241   res = gl2psPrintPrimitives();
05242   gl2psPrintf("</g>\n");
05243   return res;
05244 }
05245 
05246 static void gl2psPrintSVGFinalPrimitive(void)
05247 {
05248   /* End any remaining line, if any */
05249   gl2psEndSVGLine();
05250 }
05251 
05252 /* definition of the SVG backend */
05253 
05254 static GL2PSbackend gl2psSVG = {
05255   gl2psPrintSVGHeader,
05256   gl2psPrintSVGFooter,
05257   gl2psPrintSVGBeginViewport,
05258   gl2psPrintSVGEndViewport,
05259   gl2psPrintSVGPrimitive,
05260   gl2psPrintSVGFinalPrimitive,
05261   "svg",
05262   "Scalable Vector Graphics"
05263 };
05264 
05265 /*********************************************************************
05266  *
05267  * PGF routines
05268  *
05269  *********************************************************************/
05270 
05271 static void gl2psPrintPGFColor(GL2PSrgba rgba)
05272 {
05273   if(!gl2psSameColor(gl2ps->lastrgba, rgba)){
05274     gl2psSetLastColor(rgba);
05275     fprintf(gl2ps->stream, "\\color[rgb]{%f,%f,%f}\n", rgba[0], rgba[1], rgba[2]);
05276   }
05277 }
05278 
05279 static void gl2psPrintPGFHeader(void)
05280 {
05281   time_t now;
05282 
05283   time(&now);
05284 
05285   fprintf(gl2ps->stream,
05286           "%% Title: %s\n"
05287           "%% Creator: GL2PS %d.%d.%d%s, %s\n"
05288           "%% For: %s\n"
05289           "%% CreationDate: %s",
05290           gl2ps->title, GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION,
05291           GL2PS_PATCH_VERSION, GL2PS_EXTRA_VERSION, GL2PS_COPYRIGHT,
05292           gl2ps->producer, ctime(&now));
05293 
05294   fprintf(gl2ps->stream, "\\begin{pgfpicture}\n");
05295   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
05296     gl2psPrintPGFColor(gl2ps->bgcolor);
05297     fprintf(gl2ps->stream,
05298             "\\pgfpathrectanglecorners{"
05299             "\\pgfpoint{%dpt}{%dpt}}{\\pgfpoint{%dpt}{%dpt}}\n"
05300             "\\pgfusepath{fill}\n",
05301             (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
05302             (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
05303   }
05304 }
05305 
05306 static void gl2psPrintPGFDash(GLushort pattern, GLint factor)
05307 {
05308   int i, n, array[10];
05309 
05310   if(pattern == gl2ps->lastpattern && factor == gl2ps->lastfactor)
05311     return;
05312 
05313   gl2ps->lastpattern = pattern;
05314   gl2ps->lastfactor = factor;
05315 
05316   if(!pattern || !factor){
05317     /* solid line */
05318     fprintf(gl2ps->stream, "\\pgfsetdash{}{0pt}\n");
05319   }
05320   else{
05321     gl2psParseStipplePattern(pattern, factor, &n, array);
05322     fprintf(gl2ps->stream, "\\pgfsetdash{");
05323     for(i = 0; i < n; i++) fprintf(gl2ps->stream, "{%dpt}", array[i]);
05324     fprintf(gl2ps->stream, "}{0pt}\n");
05325   }
05326 }
05327 
05328 static const char *gl2psPGFTextAlignment(int align)
05329 {
05330   switch(align){
05331   case GL2PS_TEXT_C  : return "center";
05332   case GL2PS_TEXT_CL : return "west";
05333   case GL2PS_TEXT_CR : return "east";
05334   case GL2PS_TEXT_B  : return "south";
05335   case GL2PS_TEXT_BR : return "south east";
05336   case GL2PS_TEXT_T  : return "north";
05337   case GL2PS_TEXT_TL : return "north west";
05338   case GL2PS_TEXT_TR : return "north east";
05339   case GL2PS_TEXT_BL :
05340   default            : return "south west";
05341   }
05342 }
05343 
05344 static void gl2psPrintPGFPrimitive(void *data)
05345 {
05346   GL2PSprimitive *prim;
05347 
05348   prim = *(GL2PSprimitive**)data;
05349 
05350   switch(prim->type){
05351   case GL2PS_POINT :
05352     /* Points in openGL are rectangular */
05353     gl2psPrintPGFColor(prim->verts[0].rgba);
05354     fprintf(gl2ps->stream,
05355             "\\pgfpathrectangle{\\pgfpoint{%fpt}{%fpt}}"
05356             "{\\pgfpoint{%fpt}{%fpt}}\n\\pgfusepath{fill}\n",
05357             prim->verts[0].xyz[0]-0.5*prim->width,
05358             prim->verts[0].xyz[1]-0.5*prim->width,
05359             prim->width,prim->width);
05360     break;
05361   case GL2PS_LINE :
05362     gl2psPrintPGFColor(prim->verts[0].rgba);
05363     if(gl2ps->lastlinewidth != prim->width){
05364       gl2ps->lastlinewidth = prim->width;
05365       fprintf(gl2ps->stream, "\\pgfsetlinewidth{%fpt}\n", gl2ps->lastlinewidth);
05366     }
05367     gl2psPrintPGFDash(prim->pattern, prim->factor);
05368     fprintf(gl2ps->stream,
05369             "\\pgfpathmoveto{\\pgfpoint{%fpt}{%fpt}}\n"
05370             "\\pgflineto{\\pgfpoint{%fpt}{%fpt}}\n"
05371             "\\pgfusepath{stroke}\n",
05372             prim->verts[1].xyz[0], prim->verts[1].xyz[1],
05373             prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
05374     break;
05375   case GL2PS_TRIANGLE :
05376     if(gl2ps->lastlinewidth != 0){
05377       gl2ps->lastlinewidth = 0;
05378       fprintf(gl2ps->stream, "\\pgfsetlinewidth{0.01pt}\n");
05379     }
05380     gl2psPrintPGFColor(prim->verts[0].rgba);
05381     fprintf(gl2ps->stream,
05382             "\\pgfpathmoveto{\\pgfpoint{%fpt}{%fpt}}\n"
05383             "\\pgflineto{\\pgfpoint{%fpt}{%fpt}}\n"
05384             "\\pgflineto{\\pgfpoint{%fpt}{%fpt}}\n"
05385             "\\pgfpathclose\n"
05386             "\\pgfusepath{fill,stroke}\n",
05387             prim->verts[2].xyz[0], prim->verts[2].xyz[1],
05388             prim->verts[1].xyz[0], prim->verts[1].xyz[1],
05389             prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
05390     break;
05391   case GL2PS_TEXT :
05392     fprintf(gl2ps->stream, "{\n\\pgftransformshift{\\pgfpoint{%fpt}{%fpt}}\n",
05393             prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
05394 
05395     if(prim->data.text->angle)
05396       fprintf(gl2ps->stream, "\\pgftransformrotate{%f}{", prim->data.text->angle);
05397 
05398     fprintf(gl2ps->stream, "\\pgfnode{rectangle}{%s}{\\fontsize{%d}{0}\\selectfont",
05399             gl2psPGFTextAlignment(prim->data.text->alignment),
05400             prim->data.text->fontsize);
05401 
05402     fprintf(gl2ps->stream, "\\textcolor[rgb]{%g,%g,%g}{{%s}}",
05403             prim->verts[0].rgba[0], prim->verts[0].rgba[1],
05404             prim->verts[0].rgba[2], prim->data.text->str);
05405 
05406     fprintf(gl2ps->stream, "}{}{\\pgfusepath{discard}}}\n");
05407     break;
05408   case GL2PS_SPECIAL :
05409     /* alignment contains the format for which the special output text
05410        is intended */
05411     if (prim->data.text->alignment == GL2PS_PGF)
05412       fprintf(gl2ps->stream, "%s\n", prim->data.text->str);
05413     break;
05414   default :
05415     break;
05416   }
05417 }
05418 
05419 static void gl2psPrintPGFFooter(void)
05420 {
05421   fprintf(gl2ps->stream, "\\end{pgfpicture}\n");
05422 }
05423 
05424 static void gl2psPrintPGFBeginViewport(GLint viewport[4])
05425 {
05426   GLint index;
05427   GLfloat rgba[4];
05428   int x = viewport[0], y = viewport[1], w = viewport[2], h = viewport[3];
05429 
05430   glRenderMode(GL_FEEDBACK);
05431 
05432   if(gl2ps->header){
05433     gl2psPrintPGFHeader();
05434     gl2ps->header = GL_FALSE;
05435   }
05436 
05437   fprintf(gl2ps->stream, "\\begin{pgfscope}\n");
05438   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
05439     if(gl2ps->colormode == GL_RGBA || gl2ps->colorsize == 0){
05440       glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba);
05441     }
05442     else{
05443       glGetIntegerv(GL_INDEX_CLEAR_VALUE, &index);
05444       rgba[0] = gl2ps->colormap[index][0];
05445       rgba[1] = gl2ps->colormap[index][1];
05446       rgba[2] = gl2ps->colormap[index][2];
05447       rgba[3] = 1.0F;
05448     }
05449     gl2psPrintPGFColor(rgba);
05450     fprintf(gl2ps->stream,
05451             "\\pgfpathrectangle{\\pgfpoint{%dpt}{%dpt}}"
05452             "{\\pgfpoint{%dpt}{%dpt}}\n"
05453             "\\pgfusepath{fill}\n",
05454             x, y, w, h);
05455   }
05456 
05457   fprintf(gl2ps->stream,
05458           "\\pgfpathrectangle{\\pgfpoint{%dpt}{%dpt}}"
05459           "{\\pgfpoint{%dpt}{%dpt}}\n"
05460           "\\pgfusepath{clip}\n",
05461           x, y, w, h);
05462 }
05463 
05464 static GLint gl2psPrintPGFEndViewport(void)
05465 {
05466   GLint res;
05467   res = gl2psPrintPrimitives();
05468   fprintf(gl2ps->stream, "\\end{pgfscope}\n");
05469   return res;
05470 }
05471 
05472 static void gl2psPrintPGFFinalPrimitive(void)
05473 {
05474 }
05475 
05476 /* definition of the PGF backend */
05477 
05478 static GL2PSbackend gl2psPGF = {
05479   gl2psPrintPGFHeader,
05480   gl2psPrintPGFFooter,
05481   gl2psPrintPGFBeginViewport,
05482   gl2psPrintPGFEndViewport,
05483   gl2psPrintPGFPrimitive,
05484   gl2psPrintPGFFinalPrimitive,
05485   "tex",
05486   "PGF Latex Graphics"
05487 };
05488 
05489 /*********************************************************************
05490  *
05491  * General primitive printing routine
05492  *
05493  *********************************************************************/
05494 
05495 /* Warning: the ordering of the backends must match the format
05496    #defines in gl2ps.h */
05497 
05498 static GL2PSbackend *gl2psbackends[] = {
05499   &gl2psPS,  /* 0 */
05500   &gl2psEPS, /* 1 */
05501   &gl2psTEX, /* 2 */
05502   &gl2psPDF, /* 3 */
05503   &gl2psSVG, /* 4 */
05504   &gl2psPGF  /* 5 */
05505 };
05506 
05507 static void gl2psComputeTightBoundingBox(void *data)
05508 {
05509   GL2PSprimitive *prim;
05510   int i;
05511 
05512   prim = *(GL2PSprimitive**)data;
05513 
05514   for(i = 0; i < prim->numverts; i++){
05515     if(prim->verts[i].xyz[0] < gl2ps->viewport[0])
05516       gl2ps->viewport[0] = (GLint)prim->verts[i].xyz[0];
05517     if(prim->verts[i].xyz[0] > gl2ps->viewport[2])
05518       gl2ps->viewport[2] = (GLint)(prim->verts[i].xyz[0] + 0.5F);
05519     if(prim->verts[i].xyz[1] < gl2ps->viewport[1])
05520       gl2ps->viewport[1] = (GLint)prim->verts[i].xyz[1];
05521     if(prim->verts[i].xyz[1] > gl2ps->viewport[3])
05522       gl2ps->viewport[3] = (GLint)(prim->verts[i].xyz[1] + 0.5F);
05523   }
05524 }
05525 
05526 static GLint gl2psPrintPrimitives(void)
05527 {
05528   GL2PSbsptree *root;
05529   GL2PSxyz eye = {0.0F, 0.0F, 100.0F * GL2PS_ZSCALE};
05530   GLint used;
05531 
05532   used = glRenderMode(GL_RENDER);
05533 
05534   if(used < 0){
05535     gl2psMsg(GL2PS_INFO, "OpenGL feedback buffer overflow");
05536     return GL2PS_OVERFLOW;
05537   }
05538 
05539   if(used > 0)
05540     gl2psParseFeedbackBuffer(used);
05541 
05542   gl2psRescaleAndOffset();
05543 
05544   if(gl2ps->header){
05545     if(gl2psListNbr(gl2ps->primitives) &&
05546        (gl2ps->options & GL2PS_TIGHT_BOUNDING_BOX)){
05547       gl2ps->viewport[0] = gl2ps->viewport[1] = 100000;
05548       gl2ps->viewport[2] = gl2ps->viewport[3] = -100000;
05549       gl2psListAction(gl2ps->primitives, gl2psComputeTightBoundingBox);
05550     }
05551     (gl2psbackends[gl2ps->format]->printHeader)();
05552     gl2ps->header = GL_FALSE;
05553   }
05554 
05555   if(!gl2psListNbr(gl2ps->primitives)){
05556     /* empty feedback buffer and/or nothing else to print */
05557     return GL2PS_NO_FEEDBACK;
05558   }
05559 
05560   switch(gl2ps->sort){
05561   case GL2PS_NO_SORT :
05562     gl2psListAction(gl2ps->primitives, gl2psbackends[gl2ps->format]->printPrimitive);
05563     gl2psListAction(gl2ps->primitives, gl2psFreePrimitive);
05564     /* reset the primitive list, waiting for the next viewport */
05565     gl2psListReset(gl2ps->primitives);
05566     break;
05567   case GL2PS_SIMPLE_SORT :
05568     gl2psListSort(gl2ps->primitives, gl2psCompareDepth);
05569     if(gl2ps->options & GL2PS_OCCLUSION_CULL){
05570       gl2psListActionInverse(gl2ps->primitives, gl2psAddInImageTree);
05571       gl2psFreeBspImageTree(&gl2ps->imagetree);
05572     }
05573     gl2psListAction(gl2ps->primitives, gl2psbackends[gl2ps->format]->printPrimitive);
05574     gl2psListAction(gl2ps->primitives, gl2psFreePrimitive);
05575     /* reset the primitive list, waiting for the next viewport */
05576     gl2psListReset(gl2ps->primitives);
05577     break;
05578   case GL2PS_BSP_SORT :
05579     root = (GL2PSbsptree*)gl2psMalloc(sizeof(GL2PSbsptree));
05580     gl2psBuildBspTree(root, gl2ps->primitives);
05581     if(GL_TRUE == gl2ps->boundary) gl2psBuildPolygonBoundary(root);
05582     if(gl2ps->options & GL2PS_OCCLUSION_CULL){
05583       gl2psTraverseBspTree(root, eye, -GL2PS_EPSILON, gl2psLess,
05584                            gl2psAddInImageTree, 1);
05585       gl2psFreeBspImageTree(&gl2ps->imagetree);
05586     }
05587     gl2psTraverseBspTree(root, eye, GL2PS_EPSILON, gl2psGreater,
05588                          gl2psbackends[gl2ps->format]->printPrimitive, 0);
05589     gl2psFreeBspTree(&root);
05590     /* reallocate the primitive list (it's been deleted by
05591        gl2psBuildBspTree) in case there is another viewport */
05592     gl2ps->primitives = gl2psListCreate(500, 500, sizeof(GL2PSprimitive*));
05593     break;
05594   }
05595   gl2psbackends[gl2ps->format]->printFinalPrimitive();
05596 
05597   return GL2PS_SUCCESS;
05598 }
05599 
05600 /*********************************************************************
05601  *
05602  * Public routines
05603  *
05604  *********************************************************************/
05605 
05606 GL2PSDLL_API GLint gl2psBeginPage(const char *title, const char *producer,
05607                                   GLint viewport[4], GLint format, GLint sort,
05608                                   GLint options, GLint colormode,
05609                                   GLint colorsize, GL2PSrgba *colormap,
05610                                   GLint nr, GLint ng, GLint nb, GLint buffersize,
05611                                   FILE *stream, const char *filename)
05612 {
05613   GLint index;
05614   int i;
05615 
05616   if(gl2ps){
05617     gl2psMsg(GL2PS_ERROR, "gl2psBeginPage called in wrong program state");
05618     return GL2PS_ERROR;
05619   }
05620 
05621   gl2ps = (GL2PScontext*)gl2psMalloc(sizeof(GL2PScontext));
05622 
05623   if(format >= 0 && format < (GLint)(sizeof(gl2psbackends) / sizeof(gl2psbackends[0]))){
05624     gl2ps->format = format;
05625   }
05626   else {
05627     gl2psMsg(GL2PS_ERROR, "Unknown output format: %d", format);
05628     gl2psFree(gl2ps);
05629     gl2ps = NULL;
05630     return GL2PS_ERROR;
05631   }
05632 
05633   switch(sort){
05634   case GL2PS_NO_SORT :
05635   case GL2PS_SIMPLE_SORT :
05636   case GL2PS_BSP_SORT :
05637     gl2ps->sort = sort;
05638     break;
05639   default :
05640     gl2psMsg(GL2PS_ERROR, "Unknown sorting algorithm: %d", sort);
05641     gl2psFree(gl2ps);
05642     gl2ps = NULL;
05643     return GL2PS_ERROR;
05644   }
05645 
05646   if(stream){
05647     gl2ps->stream = stream;
05648   }
05649   else{
05650     gl2psMsg(GL2PS_ERROR, "Bad file pointer");
05651     gl2psFree(gl2ps);
05652     gl2ps = NULL;
05653     return GL2PS_ERROR;
05654   }
05655 
05656   gl2ps->header = GL_TRUE;
05657   gl2ps->maxbestroot = 10;
05658   gl2ps->options = options;
05659   gl2ps->compress = NULL;
05660   gl2ps->imagemap_head = NULL;
05661   gl2ps->imagemap_tail = NULL;
05662 
05663   if(gl2ps->options & GL2PS_USE_CURRENT_VIEWPORT){
05664     glGetIntegerv(GL_VIEWPORT, gl2ps->viewport);
05665   }
05666   else{
05667     for(i = 0; i < 4; i++){
05668       gl2ps->viewport[i] = viewport[i];
05669     }
05670   }
05671 
05672   if(!gl2ps->viewport[2] || !gl2ps->viewport[3]){
05673     gl2psMsg(GL2PS_ERROR, "Incorrect viewport (x=%d, y=%d, width=%d, height=%d)",
05674              gl2ps->viewport[0], gl2ps->viewport[1],
05675              gl2ps->viewport[2], gl2ps->viewport[3]);
05676     gl2psFree(gl2ps);
05677     gl2ps = NULL;
05678     return GL2PS_ERROR;
05679   }
05680 
05681   gl2ps->threshold[0] = nr ? 1.0F / (GLfloat)nr : 0.064F;
05682   gl2ps->threshold[1] = ng ? 1.0F / (GLfloat)ng : 0.034F;
05683   gl2ps->threshold[2] = nb ? 1.0F / (GLfloat)nb : 0.100F;
05684   gl2ps->colormode = colormode;
05685   gl2ps->buffersize = buffersize > 0 ? buffersize : 2048 * 2048;
05686   for(i = 0; i < 3; i++){
05687     gl2ps->lastvertex.xyz[i] = -1.0F;
05688   }
05689   for(i = 0; i < 4; i++){
05690     gl2ps->lastvertex.rgba[i] = -1.0F;
05691     gl2ps->lastrgba[i] = -1.0F;
05692   }
05693   gl2ps->lastlinewidth = -1.0F;
05694   gl2ps->lastpattern = 0;
05695   gl2ps->lastfactor = 0;
05696   gl2ps->imagetree = NULL;
05697   gl2ps->primitivetoadd = NULL;
05698   gl2ps->zerosurfacearea = GL_FALSE;
05699   gl2ps->pdfprimlist = NULL;
05700   gl2ps->pdfgrouplist = NULL;
05701   gl2ps->xreflist = NULL;
05702 
05703   /* get default blending mode from current OpenGL state (enabled by
05704      default for SVG) */
05705   gl2ps->blending = (gl2ps->format == GL2PS_SVG) ? GL_TRUE : glIsEnabled(GL_BLEND);
05706   glGetIntegerv(GL_BLEND_SRC, &gl2ps->blendfunc[0]);
05707   glGetIntegerv(GL_BLEND_DST, &gl2ps->blendfunc[1]);
05708 
05709   if(gl2ps->colormode == GL_RGBA){
05710     gl2ps->colorsize = 0;
05711     gl2ps->colormap = NULL;
05712     glGetFloatv(GL_COLOR_CLEAR_VALUE, gl2ps->bgcolor);
05713   }
05714   else if(gl2ps->colormode == GL_COLOR_INDEX){
05715     if(!colorsize || !colormap){
05716       gl2psMsg(GL2PS_ERROR, "Missing colormap for GL_COLOR_INDEX rendering");
05717       gl2psFree(gl2ps);
05718       gl2ps = NULL;
05719       return GL2PS_ERROR;
05720     }
05721     gl2ps->colorsize = colorsize;
05722     gl2ps->colormap = (GL2PSrgba*)gl2psMalloc(gl2ps->colorsize * sizeof(GL2PSrgba));
05723     memcpy(gl2ps->colormap, colormap, gl2ps->colorsize * sizeof(GL2PSrgba));
05724     glGetIntegerv(GL_INDEX_CLEAR_VALUE, &index);
05725     gl2ps->bgcolor[0] = gl2ps->colormap[index][0];
05726     gl2ps->bgcolor[1] = gl2ps->colormap[index][1];
05727     gl2ps->bgcolor[2] = gl2ps->colormap[index][2];
05728     gl2ps->bgcolor[3] = 1.0F;
05729   }
05730   else{
05731     gl2psMsg(GL2PS_ERROR, "Unknown color mode in gl2psBeginPage");
05732     gl2psFree(gl2ps);
05733     gl2ps = NULL;
05734     return GL2PS_ERROR;
05735   }
05736 
05737   if(!title){
05738     gl2ps->title = (char*)gl2psMalloc(sizeof(char));
05739     gl2ps->title[0] = '\0';
05740   }
05741   else{
05742     gl2ps->title = (char*)gl2psMalloc((strlen(title)+1)*sizeof(char));
05743     strcpy(gl2ps->title, title);
05744   }
05745 
05746   if(!producer){
05747     gl2ps->producer = (char*)gl2psMalloc(sizeof(char));
05748     gl2ps->producer[0] = '\0';
05749   }
05750   else{
05751     gl2ps->producer = (char*)gl2psMalloc((strlen(producer)+1)*sizeof(char));
05752     strcpy(gl2ps->producer, producer);
05753   }
05754 
05755   if(!filename){
05756     gl2ps->filename = (char*)gl2psMalloc(sizeof(char));
05757     gl2ps->filename[0] = '\0';
05758   }
05759   else{
05760     gl2ps->filename = (char*)gl2psMalloc((strlen(filename)+1)*sizeof(char));
05761     strcpy(gl2ps->filename, filename);
05762   }
05763 
05764   gl2ps->primitives = gl2psListCreate(500, 500, sizeof(GL2PSprimitive*));
05765   gl2ps->auxprimitives = gl2psListCreate(100, 100, sizeof(GL2PSprimitive*));
05766   gl2ps->feedback = (GLfloat*)gl2psMalloc(gl2ps->buffersize * sizeof(GLfloat));
05767   glFeedbackBuffer(gl2ps->buffersize, GL_3D_COLOR, gl2ps->feedback);
05768   glRenderMode(GL_FEEDBACK);
05769 
05770   return GL2PS_SUCCESS;
05771 }
05772 
05773 GL2PSDLL_API GLint gl2psEndPage(void)
05774 {
05775   GLint res;
05776 
05777   if(!gl2ps) return GL2PS_UNINITIALIZED;
05778 
05779   res = gl2psPrintPrimitives();
05780 
05781   if(res != GL2PS_OVERFLOW)
05782     (gl2psbackends[gl2ps->format]->printFooter)();
05783 
05784   fflush(gl2ps->stream);
05785 
05786   gl2psListDelete(gl2ps->primitives);
05787   gl2psListDelete(gl2ps->auxprimitives);
05788   gl2psFreeImagemap(gl2ps->imagemap_head);
05789   gl2psFree(gl2ps->colormap);
05790   gl2psFree(gl2ps->title);
05791   gl2psFree(gl2ps->producer);
05792   gl2psFree(gl2ps->filename);
05793   gl2psFree(gl2ps->feedback);
05794   gl2psFree(gl2ps);
05795   gl2ps = NULL;
05796 
05797   return res;
05798 }
05799 
05800 GL2PSDLL_API GLint gl2psBeginViewport(GLint viewport[4])
05801 {
05802   if(!gl2ps) return GL2PS_UNINITIALIZED;
05803 
05804   (gl2psbackends[gl2ps->format]->beginViewport)(viewport);
05805 
05806   return GL2PS_SUCCESS;
05807 }
05808 
05809 GL2PSDLL_API GLint gl2psEndViewport(void)
05810 {
05811   GLint res;
05812 
05813   if(!gl2ps) return GL2PS_UNINITIALIZED;
05814 
05815   res = (gl2psbackends[gl2ps->format]->endViewport)();
05816 
05817   /* reset last used colors, line widths */
05818   gl2ps->lastlinewidth = -1.0F;
05819 
05820   return res;
05821 }
05822 
05823 GL2PSDLL_API GLint gl2psTextOpt(const char *str, const char *fontname,
05824                                 GLshort fontsize, GLint alignment, GLfloat angle)
05825 {
05826   return gl2psAddText(GL2PS_TEXT, str, fontname, fontsize, alignment, angle);
05827 }
05828 
05829 GL2PSDLL_API GLint gl2psText(const char *str, const char *fontname, GLshort fontsize)
05830 {
05831   return gl2psAddText(GL2PS_TEXT, str, fontname, fontsize, GL2PS_TEXT_BL, 0.0F);
05832 }
05833 
05834 GL2PSDLL_API GLint gl2psSpecial(GLint format, const char *str)
05835 {
05836   return gl2psAddText(GL2PS_SPECIAL, str, "", 0, format, 0.0F);
05837 }
05838 
05839 GL2PSDLL_API GLint gl2psDrawPixels(GLsizei width, GLsizei height,
05840                                    GLint xorig, GLint yorig,
05841                                    GLenum format, GLenum type,
05842                                    const void *pixels)
05843 {
05844   int size, i;
05845   const GLfloat *piv;
05846   GLfloat pos[4], zoom_x, zoom_y;
05847   GL2PSprimitive *prim;
05848   GLboolean valid;
05849 
05850   if(!gl2ps || !pixels) return GL2PS_UNINITIALIZED;
05851 
05852   if((width <= 0) || (height <= 0)) return GL2PS_ERROR;
05853 
05854   if(gl2ps->options & GL2PS_NO_PIXMAP) return GL2PS_SUCCESS;
05855 
05856   if((format != GL_RGB && format != GL_RGBA) || type != GL_FLOAT){
05857     gl2psMsg(GL2PS_ERROR, "gl2psDrawPixels only implemented for "
05858              "GL_RGB/GL_RGBA, GL_FLOAT pixels");
05859     return GL2PS_ERROR;
05860   }
05861 
05862   glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID, &valid);
05863   if(GL_FALSE == valid) return GL2PS_SUCCESS; /* the primitive is culled */
05864 
05865   glGetFloatv(GL_CURRENT_RASTER_POSITION, pos);
05866   glGetFloatv(GL_ZOOM_X, &zoom_x);
05867   glGetFloatv(GL_ZOOM_Y, &zoom_y);
05868 
05869   prim = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
05870   prim->type = GL2PS_PIXMAP;
05871   prim->boundary = 0;
05872   prim->numverts = 1;
05873   prim->verts = (GL2PSvertex*)gl2psMalloc(sizeof(GL2PSvertex));
05874   prim->verts[0].xyz[0] = pos[0] + xorig;
05875   prim->verts[0].xyz[1] = pos[1] + yorig;
05876   prim->verts[0].xyz[2] = pos[2];
05877   prim->culled = 0;
05878   prim->offset = 0;
05879   prim->pattern = 0;
05880   prim->factor = 0;
05881   prim->width = 1;
05882   glGetFloatv(GL_CURRENT_RASTER_COLOR, prim->verts[0].rgba);
05883   prim->data.image = (GL2PSimage*)gl2psMalloc(sizeof(GL2PSimage));
05884   prim->data.image->width = width;
05885   prim->data.image->height = height;
05886   prim->data.image->zoom_x = zoom_x;
05887   prim->data.image->zoom_y = zoom_y;
05888   prim->data.image->format = format;
05889   prim->data.image->type = type;
05890 
05891   switch(format){
05892   case GL_RGBA:
05893     if(gl2ps->options & GL2PS_NO_BLENDING || !gl2ps->blending){
05894       /* special case: blending turned off */
05895       prim->data.image->format = GL_RGB;
05896       size = height * width * 3;
05897       prim->data.image->pixels = (GLfloat*)gl2psMalloc(size * sizeof(GLfloat));
05898       piv = (const GLfloat*)pixels;
05899       for(i = 0; i < size; ++i, ++piv){
05900         prim->data.image->pixels[i] = *piv;
05901         if(!((i + 1) % 3))
05902           ++piv;
05903       }
05904     }
05905     else{
05906       size = height * width * 4;
05907       prim->data.image->pixels = (GLfloat*)gl2psMalloc(size * sizeof(GLfloat));
05908       memcpy(prim->data.image->pixels, pixels, size * sizeof(GLfloat));
05909     }
05910     break;
05911   case GL_RGB:
05912   default:
05913     size = height * width * 3;
05914     prim->data.image->pixels = (GLfloat*)gl2psMalloc(size * sizeof(GLfloat));
05915     memcpy(prim->data.image->pixels, pixels, size * sizeof(GLfloat));
05916     break;
05917   }
05918 
05919   gl2psListAdd(gl2ps->auxprimitives, &prim);
05920   glPassThrough(GL2PS_DRAW_PIXELS_TOKEN);
05921 
05922   return GL2PS_SUCCESS;
05923 }
05924 
05925 GL2PSDLL_API GLint gl2psDrawImageMap(GLsizei width, GLsizei height,
05926                                      const GLfloat position[3],
05927                                      const unsigned char *imagemap){
05928   int size, i;
05929   int sizeoffloat = sizeof(GLfloat);
05930 
05931   if(!gl2ps || !imagemap) return GL2PS_UNINITIALIZED;
05932 
05933   if((width <= 0) || (height <= 0)) return GL2PS_ERROR;
05934 
05935   size = height + height * ((width - 1) / 8);
05936   glPassThrough(GL2PS_IMAGEMAP_TOKEN);
05937   glBegin(GL_POINTS);
05938   glVertex3f(position[0], position[1],position[2]);
05939   glEnd();
05940   glPassThrough((GLfloat)width);
05941   glPassThrough((GLfloat)height);
05942   for(i = 0; i < size; i += sizeoffloat){
05943     const float *value = (const float*)imagemap;
05944     glPassThrough(*value);
05945     imagemap += sizeoffloat;
05946   }
05947   return GL2PS_SUCCESS;
05948 }
05949 
05950 GL2PSDLL_API GLint gl2psEnable(GLint mode)
05951 {
05952   GLint tmp;
05953 
05954   if(!gl2ps) return GL2PS_UNINITIALIZED;
05955 
05956   switch(mode){
05957   case GL2PS_POLYGON_OFFSET_FILL :
05958     glPassThrough(GL2PS_BEGIN_OFFSET_TOKEN);
05959     glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &gl2ps->offset[0]);
05960     glGetFloatv(GL_POLYGON_OFFSET_UNITS, &gl2ps->offset[1]);
05961     break;
05962   case GL2PS_POLYGON_BOUNDARY :
05963     glPassThrough(GL2PS_BEGIN_BOUNDARY_TOKEN);
05964     break;
05965   case GL2PS_LINE_STIPPLE :
05966     glPassThrough(GL2PS_BEGIN_STIPPLE_TOKEN);
05967     glGetIntegerv(GL_LINE_STIPPLE_PATTERN, &tmp);
05968     glPassThrough((GLfloat)tmp);
05969     glGetIntegerv(GL_LINE_STIPPLE_REPEAT, &tmp);
05970     glPassThrough((GLfloat)tmp);
05971     break;
05972   case GL2PS_BLEND :
05973     glPassThrough(GL2PS_BEGIN_BLEND_TOKEN);
05974     break;
05975   default :
05976     gl2psMsg(GL2PS_WARNING, "Unknown mode in gl2psEnable: %d", mode);
05977     return GL2PS_WARNING;
05978   }
05979 
05980   return GL2PS_SUCCESS;
05981 }
05982 
05983 GL2PSDLL_API GLint gl2psDisable(GLint mode)
05984 {
05985   if(!gl2ps) return GL2PS_UNINITIALIZED;
05986 
05987   switch(mode){
05988   case GL2PS_POLYGON_OFFSET_FILL :
05989     glPassThrough(GL2PS_END_OFFSET_TOKEN);
05990     break;
05991   case GL2PS_POLYGON_BOUNDARY :
05992     glPassThrough(GL2PS_END_BOUNDARY_TOKEN);
05993     break;
05994   case GL2PS_LINE_STIPPLE :
05995     glPassThrough(GL2PS_END_STIPPLE_TOKEN);
05996     break;
05997   case GL2PS_BLEND :
05998     glPassThrough(GL2PS_END_BLEND_TOKEN);
05999     break;
06000   default :
06001     gl2psMsg(GL2PS_WARNING, "Unknown mode in gl2psDisable: %d", mode);
06002     return GL2PS_WARNING;
06003   }
06004 
06005   return GL2PS_SUCCESS;
06006 }
06007 
06008 GL2PSDLL_API GLint gl2psPointSize(GLfloat value)
06009 {
06010   if(!gl2ps) return GL2PS_UNINITIALIZED;
06011 
06012   glPassThrough(GL2PS_POINT_SIZE_TOKEN);
06013   glPassThrough(value);
06014 
06015   return GL2PS_SUCCESS;
06016 }
06017 
06018 GL2PSDLL_API GLint gl2psLineWidth(GLfloat value)
06019 {
06020   if(!gl2ps) return GL2PS_UNINITIALIZED;
06021 
06022   glPassThrough(GL2PS_LINE_WIDTH_TOKEN);
06023   glPassThrough(value);
06024 
06025   return GL2PS_SUCCESS;
06026 }
06027 
06028 GL2PSDLL_API GLint gl2psBlendFunc(GLenum sfactor, GLenum dfactor)
06029 {
06030   if(!gl2ps) return GL2PS_UNINITIALIZED;
06031 
06032   if(GL_FALSE == gl2psSupportedBlendMode(sfactor, dfactor))
06033     return GL2PS_WARNING;
06034 
06035   glPassThrough(GL2PS_SRC_BLEND_TOKEN);
06036   glPassThrough((GLfloat)sfactor);
06037   glPassThrough(GL2PS_DST_BLEND_TOKEN);
06038   glPassThrough((GLfloat)dfactor);
06039 
06040   return GL2PS_SUCCESS;
06041 }
06042 
06043 GL2PSDLL_API GLint gl2psSetOptions(GLint options)
06044 {
06045   if(!gl2ps) return GL2PS_UNINITIALIZED;
06046 
06047   gl2ps->options = options;
06048 
06049   return GL2PS_SUCCESS;
06050 }
06051 
06052 GL2PSDLL_API GLint gl2psGetOptions(GLint *options)
06053 {
06054   if(!gl2ps) {
06055     *options = 0;
06056     return GL2PS_UNINITIALIZED;
06057   }
06058 
06059   *options = gl2ps->options;
06060 
06061   return GL2PS_SUCCESS;
06062 }
06063 
06064 GL2PSDLL_API const char *gl2psGetFileExtension(GLint format)
06065 {
06066   if(format >= 0 && format < (GLint)(sizeof(gl2psbackends) / sizeof(gl2psbackends[0])))
06067     return gl2psbackends[format]->file_extension;
06068   else
06069     return "Unknown format";
06070 }
06071 
06072 GL2PSDLL_API const char *gl2psGetFormatDescription(GLint format)
06073 {
06074   if(format >= 0 && format < (GLint)(sizeof(gl2psbackends) / sizeof(gl2psbackends[0])))
06075     return gl2psbackends[format]->description;
06076   else
06077     return "Unknown format";
06078 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines