1 /*
2  * GL2PS, an OpenGL to PostScript Printing Library
3  * Copyright (C) 1999-2012 C. Geuzaine
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of either:
7  *
8  * a) the GNU Library General Public License as published by the Free
9  * Software Foundation, either version 2 of the License, or (at your
10  * option) any later version; or
11  *
12  * b) the GL2PS License as published by Christophe Geuzaine, either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See either
18  * the GNU Library General Public License or the GL2PS License for
19  * more details.
20  *
21  * You should have received a copy of the GNU Library General Public
22  * License along with this library in the file named "COPYING.LGPL";
23  * if not, write to the Free Software Foundation, Inc., 51 Franklin
24  * Street, Fifth Floor, Boston, MA 02110-1301, USA.
25  *
26  * You should have received a copy of the GL2PS License with this
27  * library in the file named "COPYING.GL2PS"; if not, I will be glad
28  * to provide one.
29  *
30  * For the latest info about gl2ps and a full list of contributors,
31  * see http://www.geuz.org/gl2ps/.
32  *
33  * Please report all bugs and problems to <gl2ps@geuz.org>.
34  */
35 
36 #include "gl2ps.h"
37 
38 #include <math.h>
39 #include <string.h>
40 #include <sys/types.h>
41 #include <stdarg.h>
42 #include <time.h>
43 #include <float.h>
44 
45 #if defined(GL2PS_HAVE_ZLIB)
46 #include <zlib.h>
47 #endif
48 
49 #if defined(GL2PS_HAVE_LIBPNG)
50 #include <png.h>
51 #endif
52 
53 /*********************************************************************
54  *
55  * Private definitions, data structures and prototypes
56  *
57  *********************************************************************/
58 
59 /* Magic numbers (assuming that the order of magnitude of window
60    coordinates is 10^3) */
61 
62 #define GL2PS_EPSILON       5.0e-3F
63 #define GL2PS_ZSCALE        1000.0F
64 #define GL2PS_ZOFFSET       5.0e-2F
65 #define GL2PS_ZOFFSET_LARGE 20.0F
66 #define GL2PS_ZERO(arg)     (fabs(arg) < 1.e-20)
67 
68 /* Primitive types */
69 
70 #define GL2PS_NO_TYPE          -1
71 #define GL2PS_TEXT             1
72 #define GL2PS_POINT            2
73 #define GL2PS_LINE             3
74 #define GL2PS_QUADRANGLE       4
75 #define GL2PS_TRIANGLE         5
76 #define GL2PS_PIXMAP           6
77 #define GL2PS_IMAGEMAP         7
78 #define GL2PS_IMAGEMAP_WRITTEN 8
79 #define GL2PS_IMAGEMAP_VISIBLE 9
80 #define GL2PS_SPECIAL          10
81 
82 /* BSP tree primitive comparison */
83 
84 #define GL2PS_COINCIDENT  1
85 #define GL2PS_IN_FRONT_OF 2
86 #define GL2PS_IN_BACK_OF  3
87 #define GL2PS_SPANNING    4
88 
89 /* 2D BSP tree primitive comparison */
90 
91 #define GL2PS_POINT_COINCIDENT 0
92 #define GL2PS_POINT_INFRONT    1
93 #define GL2PS_POINT_BACK       2
94 
95 /* Internal feedback buffer pass-through tokens */
96 
97 #define GL2PS_BEGIN_OFFSET_TOKEN   1
98 #define GL2PS_END_OFFSET_TOKEN     2
99 #define GL2PS_BEGIN_BOUNDARY_TOKEN 3
100 #define GL2PS_END_BOUNDARY_TOKEN   4
101 #define GL2PS_BEGIN_STIPPLE_TOKEN  5
102 #define GL2PS_END_STIPPLE_TOKEN    6
103 #define GL2PS_POINT_SIZE_TOKEN     7
104 #define GL2PS_LINE_WIDTH_TOKEN     8
105 #define GL2PS_BEGIN_BLEND_TOKEN    9
106 #define GL2PS_END_BLEND_TOKEN      10
107 #define GL2PS_SRC_BLEND_TOKEN      11
108 #define GL2PS_DST_BLEND_TOKEN      12
109 #define GL2PS_IMAGEMAP_TOKEN       13
110 #define GL2PS_DRAW_PIXELS_TOKEN    14
111 #define GL2PS_TEXT_TOKEN           15
112 
113 typedef enum {
114   T_UNDEFINED    = -1,
115   T_CONST_COLOR  = 1,
116   T_VAR_COLOR    = 1<<1,
117   T_ALPHA_1      = 1<<2,
118   T_ALPHA_LESS_1 = 1<<3,
119   T_VAR_ALPHA    = 1<<4
120 } GL2PS_TRIANGLE_PROPERTY;
121 
122 typedef GLfloat GL2PSxyz[3];
123 typedef GLfloat GL2PSplane[4];
124 
125 typedef struct _GL2PSbsptree2d GL2PSbsptree2d;
126 
127 struct _GL2PSbsptree2d {
128   GL2PSplane plane;
129   GL2PSbsptree2d *front, *back;
130 };
131 
132 typedef struct {
133   GLint nmax, size, incr, n;
134   char *array;
135 } GL2PSlist;
136 
137 typedef struct _GL2PSbsptree GL2PSbsptree;
138 
139 struct _GL2PSbsptree {
140   GL2PSplane plane;
141   GL2PSlist *primitives;
142   GL2PSbsptree *front, *back;
143 };
144 
145 typedef struct {
146   GL2PSxyz xyz;
147   GL2PSrgba rgba;
148 } GL2PSvertex;
149 
150 typedef struct {
151   GL2PSvertex vertex[3];
152   int prop;
153 } GL2PStriangle;
154 
155 typedef struct {
156   GLshort fontsize;
157   char *str, *fontname;
158   /* Note: for a 'special' string, 'alignment' holds the format
159      (PostScript, PDF, etc.) of the special string */
160   GLint alignment;
161   GLfloat angle;
162 } GL2PSstring;
163 
164 typedef struct {
165   GLsizei width, height;
166   /* Note: for an imagemap, 'type' indicates if it has already been
167      written to the file or not, and 'format' indicates if it is
168      visible or not */
169   GLenum format, type;
170   GLfloat zoom_x, zoom_y;
171   GLfloat *pixels;
172 } GL2PSimage;
173 
174 typedef struct _GL2PSimagemap GL2PSimagemap;
175 
176 struct _GL2PSimagemap {
177   GL2PSimage *image;
178   GL2PSimagemap *next;
179 };
180 
181 typedef struct {
182   GLshort type, numverts;
183   GLushort pattern;
184   char boundary, offset, culled;
185   GLint factor;
186   GLfloat width;
187   GL2PSvertex *verts;
188   union {
189     GL2PSstring *text;
190     GL2PSimage *image;
191   } data;
192 } GL2PSprimitive;
193 
194 typedef struct {
195 #if defined(GL2PS_HAVE_ZLIB)
196   Bytef *dest, *src, *start;
197   uLongf destLen, srcLen;
198 #else
199   int dummy;
200 #endif
201 } GL2PScompress;
202 
203 typedef struct{
204   GL2PSlist* ptrlist;
205   int gsno, fontno, imno, shno, maskshno, trgroupno;
206   int gsobjno, fontobjno, imobjno, shobjno, maskshobjno, trgroupobjno;
207 } GL2PSpdfgroup;
208 
209 typedef struct {
210   /* General */
211   GLint format, sort, options, colorsize, colormode, buffersize;
212   char *title, *producer, *filename;
213   GLboolean boundary, blending;
214   GLfloat *feedback, offset[2], lastlinewidth;
215   GLint viewport[4], blendfunc[2], lastfactor;
216   GL2PSrgba *colormap, lastrgba, threshold, bgcolor;
217   GLushort lastpattern;
218   GL2PSvertex lastvertex;
219   GL2PSlist *primitives, *auxprimitives;
220   FILE *stream;
221   GL2PScompress *compress;
222   GLboolean header;
223 
224   /* BSP-specific */
225   GLint maxbestroot;
226 
227   /* Occlusion culling-specific */
228   GLboolean zerosurfacearea;
229   GL2PSbsptree2d *imagetree;
230   GL2PSprimitive *primitivetoadd;
231 
232   /* PDF-specific */
233   int streamlength;
234   GL2PSlist *pdfprimlist, *pdfgrouplist;
235   int *xreflist;
236   int objects_stack; /* available objects */
237   int extgs_stack; /* graphics state object number */
238   int font_stack; /* font object number */
239   int im_stack; /* image object number */
240   int trgroupobjects_stack; /* xobject numbers */
241   int shader_stack; /* shader object numbers */
242   int mshader_stack; /* mask shader object numbers */
243 
244   /* for image map list */
245   GL2PSimagemap *imagemap_head;
246   GL2PSimagemap *imagemap_tail;
247 } GL2PScontext;
248 
249 typedef struct {
250   void  (*printHeader)(void);
251   void  (*printFooter)(void);
252   void  (*beginViewport)(GLint viewport[4]);
253   GLint (*endViewport)(void);
254   void  (*printPrimitive)(void *data);
255   void  (*printFinalPrimitive)(void);
256   const char *file_extension;
257   const char *description;
258 } GL2PSbackend;
259 
260 /* The gl2ps context. gl2ps is not thread safe (we should create a
261    local GL2PScontext during gl2psBeginPage) */
262 
263 static GL2PScontext *gl2ps = NULL;
264 
265 /* Need to forward-declare this one */
266 
267 static GLint gl2psPrintPrimitives(void);
268 
269 /*********************************************************************
270  *
271  * Utility routines
272  *
273  *********************************************************************/
274 
275 static void gl2psMsg(GLint level, const char *fmt, ...)
276 {
277   va_list args;
278 
279   if(!(gl2ps->options & GL2PS_SILENT)){
280     switch(level){
281     case GL2PS_INFO : fprintf(stderr, "GL2PS info: "); break;
282     case GL2PS_WARNING : fprintf(stderr, "GL2PS warning: "); break;
283     case GL2PS_ERROR : fprintf(stderr, "GL2PS error: "); break;
284     }
285     va_start(args, fmt);
286     vfprintf(stderr, fmt, args);
287     va_end(args);
288     fprintf(stderr, "\n");
289   }
290   /* if(level == GL2PS_ERROR) exit(1); */
291 }
292 
293 static void *gl2psMalloc(size_t size)
294 {
295   void *ptr;
296 
297   if(!size) return NULL;
298   ptr = malloc(size);
299   if(!ptr){
300     gl2psMsg(GL2PS_ERROR, "Couldn't allocate requested memory");
301     return NULL;
302   }
303   return ptr;
304 }
305 
306 static void *gl2psRealloc(void *ptr, size_t size)
307 {
308   void *orig = ptr;
309   if(!size) return NULL;
310   ptr = realloc(orig, size);
311   if(!ptr){
312     gl2psMsg(GL2PS_ERROR, "Couldn't reallocate requested memory");
313     free(orig);
314     return NULL;
315   }
316   return ptr;
317 }
318 
319 static void gl2psFree(void *ptr)
320 {
321   if(!ptr) return;
322   free(ptr);
323 }
324 
325 static int gl2psWriteBigEndian(unsigned long data, int bytes)
326 {
327   int i;
328   int size = sizeof(unsigned long);
329   for(i = 1; i <= bytes; ++i){
330     fputc(0xff & (data >> (size - i) * 8), gl2ps->stream);
331   }
332   return bytes;
333 }
334 
335 /* zlib compression helper routines */
336 
337 #if defined(GL2PS_HAVE_ZLIB)
338 
339 static void gl2psSetupCompress(void)
340 {
341   gl2ps->compress = (GL2PScompress*)gl2psMalloc(sizeof(GL2PScompress));
342   gl2ps->compress->src = NULL;
343   gl2ps->compress->start = NULL;
344   gl2ps->compress->dest = NULL;
345   gl2ps->compress->srcLen = 0;
346   gl2ps->compress->destLen = 0;
347 }
348 
349 static void gl2psFreeCompress(void)
350 {
351   if(!gl2ps->compress)
352     return;
353   gl2psFree(gl2ps->compress->start);
354   gl2psFree(gl2ps->compress->dest);
355   gl2ps->compress->src = NULL;
356   gl2ps->compress->start = NULL;
357   gl2ps->compress->dest = NULL;
358   gl2ps->compress->srcLen = 0;
359   gl2ps->compress->destLen = 0;
360 }
361 
362 static int gl2psAllocCompress(unsigned int srcsize)
363 {
364   gl2psFreeCompress();
365 
366   if(!gl2ps->compress || !srcsize)
367     return GL2PS_ERROR;
368 
369   gl2ps->compress->srcLen = srcsize;
370   gl2ps->compress->destLen = (int)ceil(1.001 * gl2ps->compress->srcLen + 12);
371   gl2ps->compress->src = (Bytef*)gl2psMalloc(gl2ps->compress->srcLen);
372   gl2ps->compress->start = gl2ps->compress->src;
373   gl2ps->compress->dest = (Bytef*)gl2psMalloc(gl2ps->compress->destLen);
374 
375   return GL2PS_SUCCESS;
376 }
377 
378 static void *gl2psReallocCompress(unsigned int srcsize)
379 {
380   if(!gl2ps->compress || !srcsize)
381     return NULL;
382 
383   if(srcsize < gl2ps->compress->srcLen)
384     return gl2ps->compress->start;
385 
386   gl2ps->compress->srcLen = srcsize;
387   gl2ps->compress->destLen = (int)ceil(1.001 * gl2ps->compress->srcLen + 12);
388   gl2ps->compress->src = (Bytef*)gl2psRealloc(gl2ps->compress->src,
389                                               gl2ps->compress->srcLen);
390   gl2ps->compress->start = gl2ps->compress->src;
391   gl2ps->compress->dest = (Bytef*)gl2psRealloc(gl2ps->compress->dest,
392                                                gl2ps->compress->destLen);
393 
394   return gl2ps->compress->start;
395 }
396 
397 static int gl2psWriteBigEndianCompress(unsigned long data, int bytes)
398 {
399   int i;
400   int size = sizeof(unsigned long);
401   for(i = 1; i <= bytes; ++i){
402     *gl2ps->compress->src = (Bytef)(0xff & (data >> (size-i) * 8));
403     ++gl2ps->compress->src;
404   }
405   return bytes;
406 }
407 
408 static int gl2psDeflate(void)
409 {
410   /* For compatibility with older zlib versions, we use compress(...)
411      instead of compress2(..., Z_BEST_COMPRESSION) */
412   return compress(gl2ps->compress->dest, &gl2ps->compress->destLen,
413                   gl2ps->compress->start, gl2ps->compress->srcLen);
414 }
415 
416 #endif
417 
418 static int gl2psPrintf(const char* fmt, ...)
419 {
420   int ret;
421   va_list args;
422 
423 #if defined(GL2PS_HAVE_ZLIB)
424   static char buf[1024];
425   char *bufptr = buf;
426   GLboolean freebuf = GL_FALSE;
427   unsigned int oldsize = 0;
428 #if !defined(GL2PS_HAVE_NO_VSNPRINTF)
429   /* Try writing the string to a 1024 byte buffer. If it is too small to fit,
430      keep trying larger sizes until it does. */
431   size_t bufsize = sizeof(buf);
432 #endif
433   if(gl2ps->options & GL2PS_COMPRESS){
434     va_start(args, fmt);
435 #if defined(GL2PS_HAVE_NO_VSNPRINTF)
436     ret = vsprintf(buf, fmt, args);
437 #else
438     ret = vsnprintf(bufptr, bufsize, fmt, args);
439 #endif
440     va_end(args);
441 #if !defined(GL2PS_HAVE_NO_VSNPRINTF)
442     while(ret >= (bufsize - 1) || ret < 0){
443       /* Too big. Allocate a new buffer. */
444       bufsize *= 2;
445       if(freebuf == GL_TRUE) gl2psFree(bufptr);
446       bufptr = (char *)gl2psMalloc(bufsize);
447       freebuf = GL_TRUE;
448       va_start(args, fmt);
449       ret = vsnprintf(bufptr, bufsize, fmt, args);
450       va_end(args);
451     }
452 #endif
453     oldsize = gl2ps->compress->srcLen;
454     gl2ps->compress->start = (Bytef*)gl2psReallocCompress(oldsize + ret);
455     memcpy(gl2ps->compress->start + oldsize, bufptr, ret);
456     if(freebuf == GL_TRUE) gl2psFree(bufptr);
457     ret = 0;
458   }
459   else{
460 #endif
461     va_start(args, fmt);
462     ret = vfprintf(gl2ps->stream, fmt, args);
463     va_end(args);
464 #if defined(GL2PS_HAVE_ZLIB)
465   }
466 #endif
467   return ret;
468 }
469 
470 static void gl2psPrintGzipHeader(void)
471 {
472 #if defined(GL2PS_HAVE_ZLIB)
473   char tmp[10] = {'\x1f', '\x8b', /* magic numbers: 0x1f, 0x8b */
474                   8, /* compression method: Z_DEFLATED */
475                   0, /* flags */
476                   0, 0, 0, 0, /* time */
477                   2, /* extra flags: max compression */
478                   '\x03'}; /* OS code: 0x03 (Unix) */
479 
480   if(gl2ps->options & GL2PS_COMPRESS){
481     gl2psSetupCompress();
482     /* add the gzip file header */
483     fwrite(tmp, 10, 1, gl2ps->stream);
484   }
485 #endif
486 }
487 
488 static void gl2psPrintGzipFooter(void)
489 {
490 #if defined(GL2PS_HAVE_ZLIB)
491   int n;
492   uLong crc, len;
493   char tmp[8];
494 
495   if(gl2ps->options & GL2PS_COMPRESS){
496     if(Z_OK != gl2psDeflate()){
497       gl2psMsg(GL2PS_ERROR, "Zlib deflate error");
498     }
499     else{
500       /* determine the length of the header in the zlib stream */
501       n = 2; /* CMF+FLG */
502       if(gl2ps->compress->dest[1] & (1<<5)){
503         n += 4; /* DICTID */
504       }
505       /* write the data, without the zlib header and footer */
506       fwrite(gl2ps->compress->dest+n, gl2ps->compress->destLen-(n+4),
507              1, gl2ps->stream);
508       /* add the gzip file footer */
509       crc = crc32(0L, gl2ps->compress->start, gl2ps->compress->srcLen);
510       for(n = 0; n < 4; ++n){
511         tmp[n] = (char)(crc & 0xff);
512         crc >>= 8;
513       }
514       len = gl2ps->compress->srcLen;
515       for(n = 4; n < 8; ++n){
516         tmp[n] = (char)(len & 0xff);
517         len >>= 8;
518       }
519       fwrite(tmp, 8, 1, gl2ps->stream);
520     }
521     gl2psFreeCompress();
522     gl2psFree(gl2ps->compress);
523     gl2ps->compress = NULL;
524   }
525 #endif
526 }
527 
528 /* The list handling routines */
529 
530 static void gl2psListRealloc(GL2PSlist *list, GLint n)
531 {
532   if(!list){
533     gl2psMsg(GL2PS_ERROR, "Cannot reallocate NULL list");
534     return;
535   }
536   if(n <= 0) return;
537   if(!list->array){
538     list->nmax = n;
539     list->array = (char*)gl2psMalloc(list->nmax * list->size);
540   }
541   else{
542     if(n > list->nmax){
543       list->nmax = ((n - 1) / list->incr + 1) * list->incr;
544       list->array = (char*)gl2psRealloc(list->array,
545                                         list->nmax * list->size);
546     }
547   }
548 }
549 
550 static GL2PSlist *gl2psListCreate(GLint n, GLint incr, GLint size)
551 {
552   GL2PSlist *list;
553 
554   if(n < 0) n = 0;
555   if(incr <= 0) incr = 1;
556   list = (GL2PSlist*)gl2psMalloc(sizeof(GL2PSlist));
557   list->nmax = 0;
558   list->incr = incr;
559   list->size = size;
560   list->n = 0;
561   list->array = NULL;
562   gl2psListRealloc(list, n);
563   return list;
564 }
565 
566 static void gl2psListReset(GL2PSlist *list)
567 {
568   if(!list) return;
569   list->n = 0;
570 }
571 
572 static void gl2psListDelete(GL2PSlist *list)
573 {
574   if(!list) return;
575   gl2psFree(list->array);
576   gl2psFree(list);
577 }
578 
579 static void gl2psListAdd(GL2PSlist *list, void *data)
580 {
581   if(!list){
582     gl2psMsg(GL2PS_ERROR, "Cannot add into unallocated list");
583     return;
584   }
585   list->n++;
586   gl2psListRealloc(list, list->n);
587   memcpy(&list->array[(list->n - 1) * list->size], data, list->size);
588 }
589 
590 static int gl2psListNbr(GL2PSlist *list)
591 {
592   if(!list)
593     return 0;
594   return list->n;
595 }
596 
597 static void *gl2psListPointer(GL2PSlist *list, GLint idx)
598 {
599   if(!list){
600     gl2psMsg(GL2PS_ERROR, "Cannot point into unallocated list");
601     return NULL;
602   }
603   if((idx < 0) || (idx >= list->n)){
604     gl2psMsg(GL2PS_ERROR, "Wrong list index in gl2psListPointer");
605     return NULL;
606   }
607   return &list->array[idx * list->size];
608 }
609 
610 static void gl2psListSort(GL2PSlist *list,
611                           int (*fcmp)(const void *a, const void *b))
612 {
613   if(!list)
614     return;
615   qsort(list->array, list->n, list->size, fcmp);
616 }
617 
618 static void gl2psListAction(GL2PSlist *list, void (*action)(void *data))
619 {
620   GLint i;
621 
622   for(i = 0; i < gl2psListNbr(list); i++){
623     (*action)(gl2psListPointer(list, i));
624   }
625 }
626 
627 static void gl2psListActionInverse(GL2PSlist *list, void (*action)(void *data))
628 {
629   GLint i;
630 
631   for(i = gl2psListNbr(list); i > 0; i--){
632     (*action)(gl2psListPointer(list, i-1));
633   }
634 }
635 
636 #if defined(GL2PS_HAVE_LIBPNG)
637 
638 static void gl2psListRead(GL2PSlist *list, int index, void *data)
639 {
640   if((index < 0) || (index >= list->n))
641     gl2psMsg(GL2PS_ERROR, "Wrong list index in gl2psListRead");
642   memcpy(data, &list->array[index * list->size], list->size);
643 }
644 
645 static void gl2psEncodeBase64Block(unsigned char in[3], unsigned char out[4], int len)
646 {
647   static const char cb64[] =
648     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
649 
650   out[0] = cb64[ in[0] >> 2 ];
651   out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
652   out[2] = (len > 1) ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=';
653   out[3] = (len > 2) ? cb64[ in[2] & 0x3f ] : '=';
654 }
655 
656 static void gl2psListEncodeBase64(GL2PSlist *list)
657 {
658   unsigned char *buffer, in[3], out[4];
659   int i, n, index, len;
660 
661   n = list->n * list->size;
662   buffer = (unsigned char*)gl2psMalloc(n * sizeof(unsigned char));
663   memcpy(buffer, list->array, n * sizeof(unsigned char));
664   gl2psListReset(list);
665 
666   index = 0;
667   while(index < n) {
668     len = 0;
669     for(i = 0; i < 3; i++) {
670       if(index < n){
671         in[i] = buffer[index];
672         len++;
673       }
674       else{
675         in[i] = 0;
676       }
677       index++;
678     }
679     if(len) {
680       gl2psEncodeBase64Block(in, out, len);
681       for(i = 0; i < 4; i++)
682         gl2psListAdd(list, &out[i]);
683     }
684   }
685   gl2psFree(buffer);
686 }
687 
688 #endif
689 
690 /* Helpers for rgba colors */
691 
692 static GLboolean gl2psSameColor(GL2PSrgba rgba1, GL2PSrgba rgba2)
693 {
694   if(!GL2PS_ZERO(rgba1[0] - rgba2[0]) ||
695      !GL2PS_ZERO(rgba1[1] - rgba2[1]) ||
696      !GL2PS_ZERO(rgba1[2] - rgba2[2]))
697     return GL_FALSE;
698   return GL_TRUE;
699 }
700 
701 static GLboolean gl2psVertsSameColor(const GL2PSprimitive *prim)
702 {
703   int i;
704 
705   for(i = 1; i < prim->numverts; i++){
706     if(!gl2psSameColor(prim->verts[0].rgba, prim->verts[i].rgba)){
707       return GL_FALSE;
708     }
709   }
710   return GL_TRUE;
711 }
712 
713 static GLboolean gl2psSameColorThreshold(int n, GL2PSrgba rgba[],
714                                          GL2PSrgba threshold)
715 {
716   int i;
717 
718   if(n < 2) return GL_TRUE;
719 
720   for(i = 1; i < n; i++){
721     if(fabs(rgba[0][0] - rgba[i][0]) > threshold[0] ||
722        fabs(rgba[0][1] - rgba[i][1]) > threshold[1] ||
723        fabs(rgba[0][2] - rgba[i][2]) > threshold[2])
724       return GL_FALSE;
725   }
726 
727   return GL_TRUE;
728 }
729 
730 static void gl2psSetLastColor(GL2PSrgba rgba)
731 {
732   int i;
733   for(i = 0; i < 3; ++i){
734     gl2ps->lastrgba[i] = rgba[i];
735   }
736 }
737 
738 static GLfloat gl2psGetRGB(GL2PSimage *im, GLuint x, GLuint y,
739                            GLfloat *red, GLfloat *green, GLfloat *blue)
740 {
741 
742   GLsizei width = im->width;
743   GLsizei height = im->height;
744   GLfloat *pixels = im->pixels;
745   GLfloat *pimag;
746 
747   /* OpenGL image is from down to up, PS image is up to down */
748   switch(im->format){
749   case GL_RGBA:
750     pimag = pixels + 4 * (width * (height - 1 - y) + x);
751     break;
752   case GL_RGB:
753   default:
754     pimag = pixels + 3 * (width * (height - 1 - y) + x);
755     break;
756   }
757   *red = *pimag; pimag++;
758   *green = *pimag; pimag++;
759   *blue = *pimag; pimag++;
760 
761   return (im->format == GL_RGBA) ? *pimag : 1.0F;
762 }
763 
764 /* Helper routines for pixmaps */
765 
766 static GL2PSimage *gl2psCopyPixmap(GL2PSimage *im)
767 {
768   int size;
769   GL2PSimage *image = (GL2PSimage*)gl2psMalloc(sizeof(GL2PSimage));
770 
771   image->width = im->width;
772   image->height = im->height;
773   image->format = im->format;
774   image->type = im->type;
775   image->zoom_x = im->zoom_x;
776   image->zoom_y = im->zoom_y;
777 
778   switch(image->format){
779   case GL_RGBA:
780     size = image->height * image->width * 4 * sizeof(GLfloat);
781     break;
782   case GL_RGB:
783   default:
784     size = image->height * image->width * 3 * sizeof(GLfloat);
785     break;
786   }
787 
788   image->pixels = (GLfloat*)gl2psMalloc(size);
789   memcpy(image->pixels, im->pixels, size);
790 
791   return image;
792 }
793 
794 static void gl2psFreePixmap(GL2PSimage *im)
795 {
796   if(!im)
797     return;
798   gl2psFree(im->pixels);
799   gl2psFree(im);
800 }
801 
802 #if defined(GL2PS_HAVE_LIBPNG)
803 
804 #if !defined(png_jmpbuf)
805 #  define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
806 #endif
807 
808 static void gl2psUserWritePNG(png_structp png_ptr, png_bytep data, png_size_t length)
809 {
810   unsigned int i;
811   GL2PSlist *png = (GL2PSlist*)png_get_io_ptr(png_ptr);
812   for(i = 0; i < length; i++)
813     gl2psListAdd(png, &data[i]);
814 }
815 
816 static void gl2psUserFlushPNG(png_structp png_ptr)
817 {
818   (void) png_ptr;  /* not used */
819 }
820 
821 static void gl2psConvertPixmapToPNG(GL2PSimage *pixmap, GL2PSlist *png)
822 {
823   png_structp png_ptr;
824   png_infop info_ptr;
825   unsigned char *row_data;
826   GLfloat dr, dg, db;
827   int row, col;
828 
829   if(!(png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)))
830     return;
831 
832   if(!(info_ptr = png_create_info_struct(png_ptr))){
833     png_destroy_write_struct(&png_ptr, NULL);
834     return;
835   }
836 
837   if(setjmp(png_jmpbuf(png_ptr))) {
838     png_destroy_write_struct(&png_ptr, &info_ptr);
839     return;
840   }
841 
842   png_set_write_fn(png_ptr, (void *)png, gl2psUserWritePNG, gl2psUserFlushPNG);
843   png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
844   png_set_IHDR(png_ptr, info_ptr, pixmap->width, pixmap->height, 8,
845                PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
846                PNG_FILTER_TYPE_BASE);
847   png_write_info(png_ptr, info_ptr);
848 
849   row_data = (unsigned char*)gl2psMalloc(3 * pixmap->width * sizeof(unsigned char));
850   for(row = 0; row < pixmap->height; row++){
851     for(col = 0; col < pixmap->width; col++){
852       gl2psGetRGB(pixmap, col, row, &dr, &dg, &db);
853       row_data[3*col] = (unsigned char)(255. * dr);
854       row_data[3*col+1] = (unsigned char)(255. * dg);
855       row_data[3*col+2] = (unsigned char)(255. * db);
856     }
857     png_write_row(png_ptr, (png_bytep)row_data);
858   }
859   gl2psFree(row_data);
860 
861   png_write_end(png_ptr, info_ptr);
862   png_destroy_write_struct(&png_ptr, &info_ptr);
863 }
864 
865 #endif
866 
867 /* Helper routines for text strings */
868 
869 static GLint gl2psAddText(GLint type, const char *str, const char *fontname,
870                           GLshort fontsize, GLint alignment, GLfloat angle,
871                           GL2PSrgba color)
872 {
873   GLfloat pos[4];
874   GL2PSprimitive *prim;
875   GLboolean valid;
876 
877   if(!gl2ps || !str || !fontname) return GL2PS_UNINITIALIZED;
878 
879   if(gl2ps->options & GL2PS_NO_TEXT) return GL2PS_SUCCESS;
880 
881   glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID, &valid);
882   if(GL_FALSE == valid) return GL2PS_SUCCESS; /* the primitive is culled */
883 
884   glGetFloatv(GL_CURRENT_RASTER_POSITION, pos);
885 
886   prim = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
887   prim->type = type;
888   prim->boundary = 0;
889   prim->numverts = 1;
890   prim->verts = (GL2PSvertex*)gl2psMalloc(sizeof(GL2PSvertex));
891   prim->verts[0].xyz[0] = pos[0];
892   prim->verts[0].xyz[1] = pos[1];
893   prim->verts[0].xyz[2] = pos[2];
894   prim->culled = 0;
895   prim->offset = 0;
896   prim->pattern = 0;
897   prim->factor = 0;
898   prim->width = 1;
899   if (color)
900     memcpy(prim->verts[0].rgba, color, 4 * sizeof(float));
901   else
902     glGetFloatv(GL_CURRENT_RASTER_COLOR, prim->verts[0].rgba);
903   prim->data.text = (GL2PSstring*)gl2psMalloc(sizeof(GL2PSstring));
904   prim->data.text->str = (char*)gl2psMalloc((strlen(str)+1)*sizeof(char));
905   strcpy(prim->data.text->str, str);
906   prim->data.text->fontname = (char*)gl2psMalloc((strlen(fontname)+1)*sizeof(char));
907   strcpy(prim->data.text->fontname, fontname);
908   prim->data.text->fontsize = fontsize;
909   prim->data.text->alignment = alignment;
910   prim->data.text->angle = angle;
911 
912   gl2psListAdd(gl2ps->auxprimitives, &prim);
913   glPassThrough(GL2PS_TEXT_TOKEN);
914 
915   return GL2PS_SUCCESS;
916 }
917 
918 static GL2PSstring *gl2psCopyText(GL2PSstring *t)
919 {
920   GL2PSstring *text = (GL2PSstring*)gl2psMalloc(sizeof(GL2PSstring));
921   text->str = (char*)gl2psMalloc((strlen(t->str)+1)*sizeof(char));
922   strcpy(text->str, t->str);
923   text->fontname = (char*)gl2psMalloc((strlen(t->fontname)+1)*sizeof(char));
924   strcpy(text->fontname, t->fontname);
925   text->fontsize = t->fontsize;
926   text->alignment = t->alignment;
927   text->angle = t->angle;
928 
929   return text;
930 }
931 
932 static void gl2psFreeText(GL2PSstring *text)
933 {
934   if(!text)
935     return;
936   gl2psFree(text->str);
937   gl2psFree(text->fontname);
938   gl2psFree(text);
939 }
940 
941 /* Helpers for blending modes */
942 
943 static GLboolean gl2psSupportedBlendMode(GLenum sfactor, GLenum dfactor)
944 {
945   /* returns TRUE if gl2ps supports the argument combination: only two
946      blending modes have been implemented so far */
947 
948   if( (sfactor == GL_SRC_ALPHA && dfactor == GL_ONE_MINUS_SRC_ALPHA) ||
949       (sfactor == GL_ONE && dfactor == GL_ZERO) )
950     return GL_TRUE;
951   return GL_FALSE;
952 }
953 
954 static void gl2psAdaptVertexForBlending(GL2PSvertex *v)
955 {
956   /* Transforms vertex depending on the actual blending function -
957      currently the vertex v is considered as source vertex and his
958      alpha value is changed to 1.0 if source blending GL_ONE is
959      active. This might be extended in the future */
960 
961   if(!v || !gl2ps)
962     return;
963 
964   if(gl2ps->options & GL2PS_NO_BLENDING || !gl2ps->blending){
965     v->rgba[3] = 1.0F;
966     return;
967   }
968 
969   switch(gl2ps->blendfunc[0]){
970   case GL_ONE:
971     v->rgba[3] = 1.0F;
972     break;
973   default:
974     break;
975   }
976 }
977 
978 static void gl2psAssignTriangleProperties(GL2PStriangle *t)
979 {
980   /* int i; */
981 
982   t->prop = T_VAR_COLOR;
983 
984   /* Uncommenting the following lines activates an even more fine
985      grained distinction between triangle types - please don't delete,
986      a remarkable amount of PDF handling code inside this file depends
987      on it if activated */
988   /*
989   t->prop = T_CONST_COLOR;
990   for(i = 0; i < 3; ++i){
991     if(!GL2PS_ZERO(t->vertex[0].rgba[i] - t->vertex[1].rgba[i]) ||
992        !GL2PS_ZERO(t->vertex[1].rgba[i] - t->vertex[2].rgba[i])){
993       t->prop = T_VAR_COLOR;
994       break;
995     }
996   }
997   */
998 
999   if(!GL2PS_ZERO(t->vertex[0].rgba[3] - t->vertex[1].rgba[3]) ||
1000      !GL2PS_ZERO(t->vertex[1].rgba[3] - t->vertex[2].rgba[3])){
1001     t->prop |= T_VAR_ALPHA;
1002   }
1003   else{
1004     if(t->vertex[0].rgba[3] < 1)
1005       t->prop |= T_ALPHA_LESS_1;
1006     else
1007       t->prop |= T_ALPHA_1;
1008   }
1009 }
1010 
1011 static void gl2psFillTriangleFromPrimitive(GL2PStriangle *t, GL2PSprimitive *p,
1012                                            GLboolean assignprops)
1013 {
1014   t->vertex[0] = p->verts[0];
1015   t->vertex[1] = p->verts[1];
1016   t->vertex[2] = p->verts[2];
1017   if(GL_TRUE == assignprops)
1018     gl2psAssignTriangleProperties(t);
1019 }
1020 
1021 static void gl2psInitTriangle(GL2PStriangle *t)
1022 {
1023   int i;
1024   GL2PSvertex vertex = { {-1.0F, -1.0F, -1.0F}, {-1.0F, -1.0F, -1.0F, -1.0F} };
1025   for(i = 0; i < 3; i++)
1026     t->vertex[i] = vertex;
1027   t->prop = T_UNDEFINED;
1028 }
1029 
1030 /* Miscellaneous helper routines */
1031 
1032 static GL2PSprimitive *gl2psCopyPrimitive(GL2PSprimitive *p)
1033 {
1034   GL2PSprimitive *prim;
1035 
1036   if(!p){
1037     gl2psMsg(GL2PS_ERROR, "Trying to copy an empty primitive");
1038     return NULL;
1039   }
1040 
1041   prim = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
1042 
1043   prim->type = p->type;
1044   prim->numverts = p->numverts;
1045   prim->boundary = p->boundary;
1046   prim->offset = p->offset;
1047   prim->pattern = p->pattern;
1048   prim->factor = p->factor;
1049   prim->culled = p->culled;
1050   prim->width = p->width;
1051   prim->verts = (GL2PSvertex*)gl2psMalloc(p->numverts*sizeof(GL2PSvertex));
1052   memcpy(prim->verts, p->verts, p->numverts * sizeof(GL2PSvertex));
1053 
1054   switch(prim->type){
1055   case GL2PS_PIXMAP :
1056     prim->data.image = gl2psCopyPixmap(p->data.image);
1057     break;
1058   case GL2PS_TEXT :
1059   case GL2PS_SPECIAL :
1060     prim->data.text = gl2psCopyText(p->data.text);
1061     break;
1062   default:
1063     break;
1064   }
1065 
1066   return prim;
1067 }
1068 
1069 static GLboolean gl2psSamePosition(GL2PSxyz p1, GL2PSxyz p2)
1070 {
1071   if(!GL2PS_ZERO(p1[0] - p2[0]) ||
1072      !GL2PS_ZERO(p1[1] - p2[1]) ||
1073      !GL2PS_ZERO(p1[2] - p2[2]))
1074     return GL_FALSE;
1075   return GL_TRUE;
1076 }
1077 
1078 /*********************************************************************
1079  *
1080  * 3D sorting routines
1081  *
1082  *********************************************************************/
1083 
1084 static GLfloat gl2psComparePointPlane(GL2PSxyz point, GL2PSplane plane)
1085 {
1086   return (plane[0] * point[0] +
1087           plane[1] * point[1] +
1088           plane[2] * point[2] +
1089           plane[3]);
1090 }
1091 
1092 static GLfloat gl2psPsca(GLfloat *a, GLfloat *b)
1093 {
1094   return (a[0]*b[0] + a[1]*b[1] + a[2]*b[2]);
1095 }
1096 
1097 static void gl2psPvec(GLfloat *a, GLfloat *b, GLfloat *c)
1098 {
1099   c[0] = a[1]*b[2] - a[2]*b[1];
1100   c[1] = a[2]*b[0] - a[0]*b[2];
1101   c[2] = a[0]*b[1] - a[1]*b[0];
1102 }
1103 
1104 static GLfloat gl2psNorm(GLfloat *a)
1105 {
1106   return (GLfloat)sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
1107 }
1108 
1109 static void gl2psGetNormal(GLfloat *a, GLfloat *b, GLfloat *c)
1110 {
1111   GLfloat norm;
1112 
1113   gl2psPvec(a, b, c);
1114   if(!GL2PS_ZERO(norm = gl2psNorm(c))){
1115     c[0] = c[0] / norm;
1116     c[1] = c[1] / norm;
1117     c[2] = c[2] / norm;
1118   }
1119   else{
1120     /* The plane is still wrong despite our tests in gl2psGetPlane.
1121        Let's return a dummy value for now (this is a hack: we should
1122        do more intelligent tests in GetPlane) */
1123     c[0] = c[1] = 0.0F;
1124     c[2] = 1.0F;
1125   }
1126 }
1127 
1128 static void gl2psGetPlane(GL2PSprimitive *prim, GL2PSplane plane)
1129 {
1130   GL2PSxyz v = {0.0F, 0.0F, 0.0F}, w = {0.0F, 0.0F, 0.0F};
1131 
1132   switch(prim->type){
1133   case GL2PS_TRIANGLE :
1134   case GL2PS_QUADRANGLE :
1135     v[0] = prim->verts[1].xyz[0] - prim->verts[0].xyz[0];
1136     v[1] = prim->verts[1].xyz[1] - prim->verts[0].xyz[1];
1137     v[2] = prim->verts[1].xyz[2] - prim->verts[0].xyz[2];
1138     w[0] = prim->verts[2].xyz[0] - prim->verts[0].xyz[0];
1139     w[1] = prim->verts[2].xyz[1] - prim->verts[0].xyz[1];
1140     w[2] = prim->verts[2].xyz[2] - prim->verts[0].xyz[2];
1141     if((GL2PS_ZERO(v[0]) && GL2PS_ZERO(v[1]) && GL2PS_ZERO(v[2])) ||
1142        (GL2PS_ZERO(w[0]) && GL2PS_ZERO(w[1]) && GL2PS_ZERO(w[2]))){
1143       plane[0] = plane[1] = 0.0F;
1144       plane[2] = 1.0F;
1145       plane[3] = -prim->verts[0].xyz[2];
1146     }
1147     else{
1148       gl2psGetNormal(v, w, plane);
1149       plane[3] =
1150         - plane[0] * prim->verts[0].xyz[0]
1151         - plane[1] * prim->verts[0].xyz[1]
1152         - plane[2] * prim->verts[0].xyz[2];
1153     }
1154     break;
1155   case GL2PS_LINE :
1156     v[0] = prim->verts[1].xyz[0] - prim->verts[0].xyz[0];
1157     v[1] = prim->verts[1].xyz[1] - prim->verts[0].xyz[1];
1158     v[2] = prim->verts[1].xyz[2] - prim->verts[0].xyz[2];
1159     if(GL2PS_ZERO(v[0]) && GL2PS_ZERO(v[1]) && GL2PS_ZERO(v[2])){
1160       plane[0] = plane[1] = 0.0F;
1161       plane[2] = 1.0F;
1162       plane[3] = -prim->verts[0].xyz[2];
1163     }
1164     else{
1165       if(GL2PS_ZERO(v[0]))      w[0] = 1.0F;
1166       else if(GL2PS_ZERO(v[1])) w[1] = 1.0F;
1167       else                      w[2] = 1.0F;
1168       gl2psGetNormal(v, w, plane);
1169       plane[3] =
1170         - plane[0] * prim->verts[0].xyz[0]
1171         - plane[1] * prim->verts[0].xyz[1]
1172         - plane[2] * prim->verts[0].xyz[2];
1173     }
1174     break;
1175   case GL2PS_POINT :
1176   case GL2PS_PIXMAP :
1177   case GL2PS_TEXT :
1178   case GL2PS_SPECIAL :
1179   case GL2PS_IMAGEMAP:
1180     plane[0] = plane[1] = 0.0F;
1181     plane[2] = 1.0F;
1182     plane[3] = -prim->verts[0].xyz[2];
1183     break;
1184   default :
1185     gl2psMsg(GL2PS_ERROR, "Unknown primitive type in BSP tree");
1186     plane[0] = plane[1] = plane[3] = 0.0F;
1187     plane[2] = 1.0F;
1188     break;
1189   }
1190 }
1191 
1192 static void gl2psCutEdge(GL2PSvertex *a, GL2PSvertex *b, GL2PSplane plane,
1193                          GL2PSvertex *c)
1194 {
1195   GL2PSxyz v;
1196   GLfloat sect, psca;
1197 
1198   v[0] = b->xyz[0] - a->xyz[0];
1199   v[1] = b->xyz[1] - a->xyz[1];
1200   v[2] = b->xyz[2] - a->xyz[2];
1201 
1202   if(!GL2PS_ZERO(psca = gl2psPsca(plane, v)))
1203     sect = -gl2psComparePointPlane(a->xyz, plane) / psca;
1204   else
1205     sect = 0.0F;
1206 
1207   c->xyz[0] = a->xyz[0] + v[0] * sect;
1208   c->xyz[1] = a->xyz[1] + v[1] * sect;
1209   c->xyz[2] = a->xyz[2] + v[2] * sect;
1210 
1211   c->rgba[0] = (1 - sect) * a->rgba[0] + sect * b->rgba[0];
1212   c->rgba[1] = (1 - sect) * a->rgba[1] + sect * b->rgba[1];
1213   c->rgba[2] = (1 - sect) * a->rgba[2] + sect * b->rgba[2];
1214   c->rgba[3] = (1 - sect) * a->rgba[3] + sect * b->rgba[3];
1215 }
1216 
1217 static void gl2psCreateSplitPrimitive(GL2PSprimitive *parent, GL2PSplane plane,
1218                                       GL2PSprimitive *child, GLshort numverts,
1219                                       GLshort *index0, GLshort *index1)
1220 {
1221   GLshort i;
1222 
1223   if(parent->type == GL2PS_IMAGEMAP){
1224     child->type = GL2PS_IMAGEMAP;
1225     child->data.image = parent->data.image;
1226   }
1227   else{
1228     if(numverts > 4){
1229       gl2psMsg(GL2PS_WARNING, "%d vertices in polygon", numverts);
1230       numverts = 4;
1231     }
1232     switch(numverts){
1233     case 1 : child->type = GL2PS_POINT; break;
1234     case 2 : child->type = GL2PS_LINE; break;
1235     case 3 : child->type = GL2PS_TRIANGLE; break;
1236     case 4 : child->type = GL2PS_QUADRANGLE; break;
1237     default: child->type = GL2PS_NO_TYPE; break;
1238     }
1239   }
1240 
1241   child->boundary = 0; /* FIXME: not done! */
1242   child->culled = parent->culled;
1243   child->offset = parent->offset;
1244   child->pattern = parent->pattern;
1245   child->factor = parent->factor;
1246   child->width = parent->width;
1247   child->numverts = numverts;
1248   child->verts = (GL2PSvertex*)gl2psMalloc(numverts * sizeof(GL2PSvertex));
1249 
1250   for(i = 0; i < numverts; i++){
1251     if(index1[i] < 0){
1252       child->verts[i] = parent->verts[index0[i]];
1253     }
1254     else{
1255       gl2psCutEdge(&parent->verts[index0[i]], &parent->verts[index1[i]],
1256                    plane, &child->verts[i]);
1257     }
1258   }
1259 }
1260 
1261 static void gl2psAddIndex(GLshort *index0, GLshort *index1, GLshort *nb,
1262                           GLshort i, GLshort j)
1263 {
1264   GLint k;
1265 
1266   for(k = 0; k < *nb; k++){
1267     if((index0[k] == i && index1[k] == j) ||
1268        (index1[k] == i && index0[k] == j)) return;
1269   }
1270   index0[*nb] = i;
1271   index1[*nb] = j;
1272   (*nb)++;
1273 }
1274 
1275 static GLshort gl2psGetIndex(GLshort i, GLshort num)
1276 {
1277   return (i < num - 1) ? i + 1 : 0;
1278 }
1279 
1280 static GLint gl2psTestSplitPrimitive(GL2PSprimitive *prim, GL2PSplane plane)
1281 {
1282   GLint type = GL2PS_COINCIDENT;
1283   GLshort i, j;
1284   GLfloat d[5];
1285 
1286   for(i = 0; i < prim->numverts; i++){
1287     d[i] = gl2psComparePointPlane(prim->verts[i].xyz, plane);
1288   }
1289 
1290   if(prim->numverts < 2){
1291     return 0;
1292   }
1293   else{
1294     for(i = 0; i < prim->numverts; i++){
1295       j = gl2psGetIndex(i, prim->numverts);
1296       if(d[j] > GL2PS_EPSILON){
1297         if(type == GL2PS_COINCIDENT)      type = GL2PS_IN_BACK_OF;
1298         else if(type != GL2PS_IN_BACK_OF) return 1;
1299         if(d[i] < -GL2PS_EPSILON)         return 1;
1300       }
1301       else if(d[j] < -GL2PS_EPSILON){
1302         if(type == GL2PS_COINCIDENT)       type = GL2PS_IN_FRONT_OF;
1303         else if(type != GL2PS_IN_FRONT_OF) return 1;
1304         if(d[i] > GL2PS_EPSILON)           return 1;
1305       }
1306     }
1307   }
1308   return 0;
1309 }
1310 
1311 static GLint gl2psSplitPrimitive(GL2PSprimitive *prim, GL2PSplane plane,
1312                                  GL2PSprimitive **front, GL2PSprimitive **back)
1313 {
1314   GLshort i, j, in = 0, out = 0, in0[5], in1[5], out0[5], out1[5];
1315   GLint type;
1316   GLfloat d[5];
1317 
1318   type = GL2PS_COINCIDENT;
1319 
1320   for(i = 0; i < prim->numverts; i++){
1321     d[i] = gl2psComparePointPlane(prim->verts[i].xyz, plane);
1322   }
1323 
1324   switch(prim->type){
1325   case GL2PS_POINT :
1326     if(d[0] > GL2PS_EPSILON)       type = GL2PS_IN_BACK_OF;
1327     else if(d[0] < -GL2PS_EPSILON) type = GL2PS_IN_FRONT_OF;
1328     else                           type = GL2PS_COINCIDENT;
1329     break;
1330   default :
1331     for(i = 0; i < prim->numverts; i++){
1332       j = gl2psGetIndex(i, prim->numverts);
1333       if(d[j] > GL2PS_EPSILON){
1334         if(type == GL2PS_COINCIDENT)      type = GL2PS_IN_BACK_OF;
1335         else if(type != GL2PS_IN_BACK_OF) type = GL2PS_SPANNING;
1336         if(d[i] < -GL2PS_EPSILON){
1337           gl2psAddIndex(in0, in1, &in, i, j);
1338           gl2psAddIndex(out0, out1, &out, i, j);
1339           type = GL2PS_SPANNING;
1340         }
1341         gl2psAddIndex(out0, out1, &out, j, -1);
1342       }
1343       else if(d[j] < -GL2PS_EPSILON){
1344         if(type == GL2PS_COINCIDENT)       type = GL2PS_IN_FRONT_OF;
1345         else if(type != GL2PS_IN_FRONT_OF) type = GL2PS_SPANNING;
1346         if(d[i] > GL2PS_EPSILON){
1347           gl2psAddIndex(in0, in1, &in, i, j);
1348           gl2psAddIndex(out0, out1, &out, i, j);
1349           type = GL2PS_SPANNING;
1350         }
1351         gl2psAddIndex(in0, in1, &in, j, -1);
1352       }
1353       else{
1354         gl2psAddIndex(in0, in1, &in, j, -1);
1355         gl2psAddIndex(out0, out1, &out, j, -1);
1356       }
1357     }
1358     break;
1359   }
1360 
1361   if(type == GL2PS_SPANNING){
1362     *back = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
1363     *front = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
1364     gl2psCreateSplitPrimitive(prim, plane, *back, out, out0, out1);
1365     gl2psCreateSplitPrimitive(prim, plane, *front, in, in0, in1);
1366   }
1367 
1368   return type;
1369 }
1370 
1371 static void gl2psDivideQuad(GL2PSprimitive *quad,
1372                             GL2PSprimitive **t1, GL2PSprimitive **t2)
1373 {
1374   *t1 = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
1375   *t2 = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
1376   (*t1)->type = (*t2)->type = GL2PS_TRIANGLE;
1377   (*t1)->numverts = (*t2)->numverts = 3;
1378   (*t1)->culled = (*t2)->culled = quad->culled;
1379   (*t1)->offset = (*t2)->offset = quad->offset;
1380   (*t1)->pattern = (*t2)->pattern = quad->pattern;
1381   (*t1)->factor = (*t2)->factor = quad->factor;
1382   (*t1)->width = (*t2)->width = quad->width;
1383   (*t1)->verts = (GL2PSvertex*)gl2psMalloc(3 * sizeof(GL2PSvertex));
1384   (*t2)->verts = (GL2PSvertex*)gl2psMalloc(3 * sizeof(GL2PSvertex));
1385   (*t1)->verts[0] = quad->verts[0];
1386   (*t1)->verts[1] = quad->verts[1];
1387   (*t1)->verts[2] = quad->verts[2];
1388   (*t1)->boundary = ((quad->boundary & 1) ? 1 : 0) | ((quad->boundary & 2) ? 2 : 0);
1389   (*t2)->verts[0] = quad->verts[0];
1390   (*t2)->verts[1] = quad->verts[2];
1391   (*t2)->verts[2] = quad->verts[3];
1392   (*t2)->boundary = ((quad->boundary & 4) ? 2 : 0) | ((quad->boundary & 8) ? 4 : 0);
1393 }
1394 
1395 static int gl2psCompareDepth(const void *a, const void *b)
1396 {
1397   const GL2PSprimitive *q, *w;
1398   GLfloat dq = 0.0F, dw = 0.0F, diff;
1399   int i;
1400 
1401   q = *(const GL2PSprimitive* const*)a;
1402   w = *(const GL2PSprimitive* const*)b;
1403 
1404   for(i = 0; i < q->numverts; i++){
1405     dq += q->verts[i].xyz[2];
1406   }
1407   dq /= (GLfloat)q->numverts;
1408 
1409   for(i = 0; i < w->numverts; i++){
1410     dw += w->verts[i].xyz[2];
1411   }
1412   dw /= (GLfloat)w->numverts;
1413 
1414   diff = dq - dw;
1415   if(diff > 0.){
1416     return -1;
1417   }
1418   else if(diff < 0.){
1419     return 1;
1420   }
1421   else{
1422     return 0;
1423   }
1424 }
1425 
1426 static int gl2psTrianglesFirst(const void *a, const void *b)
1427 {
1428   const GL2PSprimitive *q, *w;
1429 
1430   q = *(const GL2PSprimitive* const*)a;
1431   w = *(const GL2PSprimitive* const*)b;
1432   return (q->type < w->type ? 1 : -1);
1433 }
1434 
1435 static GLint gl2psFindRoot(GL2PSlist *primitives, GL2PSprimitive **root)
1436 {
1437   GLint i, j, count, best = 1000000, idx = 0;
1438   GL2PSprimitive *prim1, *prim2;
1439   GL2PSplane plane;
1440   GLint maxp;
1441 
1442   if(!gl2psListNbr(primitives)){
1443     gl2psMsg(GL2PS_ERROR, "Cannot fint root in empty primitive list");
1444     return 0;
1445   }
1446 
1447   *root = *(GL2PSprimitive**)gl2psListPointer(primitives, 0);
1448 
1449   if(gl2ps->options & GL2PS_BEST_ROOT){
1450     maxp = gl2psListNbr(primitives);
1451     if(maxp > gl2ps->maxbestroot){
1452       maxp = gl2ps->maxbestroot;
1453     }
1454     for(i = 0; i < maxp; i++){
1455       prim1 = *(GL2PSprimitive**)gl2psListPointer(primitives, i);
1456       gl2psGetPlane(prim1, plane);
1457       count = 0;
1458       for(j = 0; j < gl2psListNbr(primitives); j++){
1459         if(j != i){
1460           prim2 = *(GL2PSprimitive**)gl2psListPointer(primitives, j);
1461           count += gl2psTestSplitPrimitive(prim2, plane);
1462         }
1463         if(count > best) break;
1464       }
1465       if(count < best){
1466         best = count;
1467         idx = i;
1468         *root = prim1;
1469         if(!count) return idx;
1470       }
1471     }
1472     /* if(index) gl2psMsg(GL2PS_INFO, "GL2PS_BEST_ROOT was worth it: %d", index); */
1473     return idx;
1474   }
1475   else{
1476     return 0;
1477   }
1478 }
1479 
1480 static void gl2psFreeImagemap(GL2PSimagemap *list)
1481 {
1482   GL2PSimagemap *next;
1483   while(list != NULL){
1484     next = list->next;
1485     gl2psFree(list->image->pixels);
1486     gl2psFree(list->image);
1487     gl2psFree(list);
1488     list = next;
1489   }
1490 }
1491 
1492 static void gl2psFreePrimitive(void *data)
1493 {
1494   GL2PSprimitive *q;
1495 
1496   q = *(GL2PSprimitive**)data;
1497   gl2psFree(q->verts);
1498   if(q->type == GL2PS_TEXT || q->type == GL2PS_SPECIAL){
1499     gl2psFreeText(q->data.text);
1500   }
1501   else if(q->type == GL2PS_PIXMAP){
1502     gl2psFreePixmap(q->data.image);
1503   }
1504   gl2psFree(q);
1505 }
1506 
1507 static void gl2psAddPrimitiveInList(GL2PSprimitive *prim, GL2PSlist *list)
1508 {
1509   GL2PSprimitive *t1, *t2;
1510 
1511   if(prim->type != GL2PS_QUADRANGLE){
1512     gl2psListAdd(list, &prim);
1513   }
1514   else{
1515     gl2psDivideQuad(prim, &t1, &t2);
1516     gl2psListAdd(list, &t1);
1517     gl2psListAdd(list, &t2);
1518     gl2psFreePrimitive(&prim);
1519   }
1520 
1521 }
1522 
1523 static void gl2psFreeBspTree(GL2PSbsptree **tree)
1524 {
1525   if(*tree){
1526     if((*tree)->back) gl2psFreeBspTree(&(*tree)->back);
1527     if((*tree)->primitives){
1528       gl2psListAction((*tree)->primitives, gl2psFreePrimitive);
1529       gl2psListDelete((*tree)->primitives);
1530     }
1531     if((*tree)->front) gl2psFreeBspTree(&(*tree)->front);
1532     gl2psFree(*tree);
1533     *tree = NULL;
1534   }
1535 }
1536 
1537 static GLboolean gl2psGreater(GLfloat f1, GLfloat f2)
1538 {
1539   if(f1 > f2) return GL_TRUE;
1540   else return GL_FALSE;
1541 }
1542 
1543 static GLboolean gl2psLess(GLfloat f1, GLfloat f2)
1544 {
1545   if(f1 < f2) return GL_TRUE;
1546   else return GL_FALSE;
1547 }
1548 
1549 static void gl2psBuildBspTree(GL2PSbsptree *tree, GL2PSlist *primitives)
1550 {
1551   GL2PSprimitive *prim, *frontprim = NULL, *backprim = NULL;
1552   GL2PSlist *frontlist, *backlist;
1553   GLint i, idx;
1554 
1555   tree->front = NULL;
1556   tree->back = NULL;
1557   tree->primitives = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
1558   idx = gl2psFindRoot(primitives, &prim);
1559   gl2psGetPlane(prim, tree->plane);
1560   gl2psAddPrimitiveInList(prim, tree->primitives);
1561 
1562   frontlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
1563   backlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
1564 
1565   for(i = 0; i < gl2psListNbr(primitives); i++){
1566     if(i != idx){
1567       prim = *(GL2PSprimitive**)gl2psListPointer(primitives,i);
1568       switch(gl2psSplitPrimitive(prim, tree->plane, &frontprim, &backprim)){
1569       case GL2PS_COINCIDENT:
1570         gl2psAddPrimitiveInList(prim, tree->primitives);
1571         break;
1572       case GL2PS_IN_BACK_OF:
1573         gl2psAddPrimitiveInList(prim, backlist);
1574         break;
1575       case GL2PS_IN_FRONT_OF:
1576         gl2psAddPrimitiveInList(prim, frontlist);
1577         break;
1578       case GL2PS_SPANNING:
1579         gl2psAddPrimitiveInList(backprim, backlist);
1580         gl2psAddPrimitiveInList(frontprim, frontlist);
1581         gl2psFreePrimitive(&prim);
1582         break;
1583       }
1584     }
1585   }
1586 
1587   if(gl2psListNbr(tree->primitives)){
1588     gl2psListSort(tree->primitives, gl2psTrianglesFirst);
1589   }
1590 
1591   if(gl2psListNbr(frontlist)){
1592     gl2psListSort(frontlist, gl2psTrianglesFirst);
1593     tree->front = (GL2PSbsptree*)gl2psMalloc(sizeof(GL2PSbsptree));
1594     gl2psBuildBspTree(tree->front, frontlist);
1595   }
1596   else{
1597     gl2psListDelete(frontlist);
1598   }
1599 
1600   if(gl2psListNbr(backlist)){
1601     gl2psListSort(backlist, gl2psTrianglesFirst);
1602     tree->back = (GL2PSbsptree*)gl2psMalloc(sizeof(GL2PSbsptree));
1603     gl2psBuildBspTree(tree->back, backlist);
1604   }
1605   else{
1606     gl2psListDelete(backlist);
1607   }
1608 
1609   gl2psListDelete(primitives);
1610 }
1611 
1612 static void gl2psTraverseBspTree(GL2PSbsptree *tree, GL2PSxyz eye, GLfloat epsilon,
1613                                  GLboolean (*compare)(GLfloat f1, GLfloat f2),
1614                                  void (*action)(void *data), int inverse)
1615 {
1616   GLfloat result;
1617 
1618   if(!tree) return;
1619 
1620   result = gl2psComparePointPlane(eye, tree->plane);
1621 
1622   if(GL_TRUE == compare(result, epsilon)){
1623     gl2psTraverseBspTree(tree->back, eye, epsilon, compare, action, inverse);
1624     if(inverse){
1625       gl2psListActionInverse(tree->primitives, action);
1626     }
1627     else{
1628       gl2psListAction(tree->primitives, action);
1629     }
1630     gl2psTraverseBspTree(tree->front, eye, epsilon, compare, action, inverse);
1631   }
1632   else if(GL_TRUE == compare(-epsilon, result)){
1633     gl2psTraverseBspTree(tree->front, eye, epsilon, compare, action, inverse);
1634     if(inverse){
1635       gl2psListActionInverse(tree->primitives, action);
1636     }
1637     else{
1638       gl2psListAction(tree->primitives, action);
1639     }
1640     gl2psTraverseBspTree(tree->back, eye, epsilon, compare, action, inverse);
1641   }
1642   else{
1643     gl2psTraverseBspTree(tree->front, eye, epsilon, compare, action, inverse);
1644     gl2psTraverseBspTree(tree->back, eye, epsilon, compare, action, inverse);
1645   }
1646 }
1647 
1648 static void gl2psRescaleAndOffset(void)
1649 {
1650   GL2PSprimitive *prim;
1651   GLfloat minZ, maxZ, rangeZ, scaleZ;
1652   GLfloat factor, units, area, dZ, dZdX, dZdY, maxdZ;
1653   int i, j;
1654 
1655   if(!gl2psListNbr(gl2ps->primitives))
1656     return;
1657 
1658   /* get z-buffer range */
1659   prim = *(GL2PSprimitive**)gl2psListPointer(gl2ps->primitives, 0);
1660   minZ = maxZ = prim->verts[0].xyz[2];
1661   for(i = 1; i < prim->numverts; i++){
1662     if(prim->verts[i].xyz[2] < minZ) minZ = prim->verts[i].xyz[2];
1663     if(prim->verts[i].xyz[2] > maxZ) maxZ = prim->verts[i].xyz[2];
1664   }
1665   for(i = 1; i < gl2psListNbr(gl2ps->primitives); i++){
1666     prim = *(GL2PSprimitive**)gl2psListPointer(gl2ps->primitives, i);
1667     for(j = 0; j < prim->numverts; j++){
1668       if(prim->verts[j].xyz[2] < minZ) minZ = prim->verts[j].xyz[2];
1669       if(prim->verts[j].xyz[2] > maxZ) maxZ = prim->verts[j].xyz[2];
1670     }
1671   }
1672   rangeZ = (maxZ - minZ);
1673 
1674   /* rescale z-buffer coordinate in [0,GL2PS_ZSCALE], to make it of
1675      the same order of magnitude as the x and y coordinates */
1676   scaleZ = GL2PS_ZERO(rangeZ) ? GL2PS_ZSCALE : (GL2PS_ZSCALE / rangeZ);
1677   /* avoid precision loss (we use floats!) */
1678   if(scaleZ > 100000.F) scaleZ = 100000.F;
1679 
1680   /* apply offsets */
1681   for(i = 0; i < gl2psListNbr(gl2ps->primitives); i++){
1682     prim = *(GL2PSprimitive**)gl2psListPointer(gl2ps->primitives, i);
1683     for(j = 0; j < prim->numverts; j++){
1684       prim->verts[j].xyz[2] = (prim->verts[j].xyz[2] - minZ) * scaleZ;
1685     }
1686     if((gl2ps->options & GL2PS_SIMPLE_LINE_OFFSET) &&
1687        (prim->type == GL2PS_LINE)){
1688       if(gl2ps->sort == GL2PS_SIMPLE_SORT){
1689         prim->verts[0].xyz[2] -= GL2PS_ZOFFSET_LARGE;
1690         prim->verts[1].xyz[2] -= GL2PS_ZOFFSET_LARGE;
1691       }
1692       else{
1693         prim->verts[0].xyz[2] -= GL2PS_ZOFFSET;
1694         prim->verts[1].xyz[2] -= GL2PS_ZOFFSET;
1695       }
1696     }
1697     else if(prim->offset && (prim->type == GL2PS_TRIANGLE)){
1698       factor = gl2ps->offset[0];
1699       units = gl2ps->offset[1];
1700       area =
1701         (prim->verts[1].xyz[0] - prim->verts[0].xyz[0]) *
1702         (prim->verts[2].xyz[1] - prim->verts[1].xyz[1]) -
1703         (prim->verts[2].xyz[0] - prim->verts[1].xyz[0]) *
1704         (prim->verts[1].xyz[1] - prim->verts[0].xyz[1]);
1705       if(!GL2PS_ZERO(area)){
1706         dZdX =
1707           ((prim->verts[2].xyz[1] - prim->verts[1].xyz[1]) *
1708            (prim->verts[1].xyz[2] - prim->verts[0].xyz[2]) -
1709            (prim->verts[1].xyz[1] - prim->verts[0].xyz[1]) *
1710            (prim->verts[2].xyz[2] - prim->verts[1].xyz[2])) / area;
1711         dZdY =
1712           ((prim->verts[1].xyz[0] - prim->verts[0].xyz[0]) *
1713            (prim->verts[2].xyz[2] - prim->verts[1].xyz[2]) -
1714            (prim->verts[2].xyz[0] - prim->verts[1].xyz[0]) *
1715            (prim->verts[1].xyz[2] - prim->verts[0].xyz[2])) / area;
1716         maxdZ = (GLfloat)sqrt(dZdX * dZdX + dZdY * dZdY);
1717       }
1718       else{
1719         maxdZ = 0.0F;
1720       }
1721       dZ = factor * maxdZ + units;
1722       prim->verts[0].xyz[2] += dZ;
1723       prim->verts[1].xyz[2] += dZ;
1724       prim->verts[2].xyz[2] += dZ;
1725     }
1726   }
1727 }
1728 
1729 /*********************************************************************
1730  *
1731  * 2D sorting routines (for occlusion culling)
1732  *
1733  *********************************************************************/
1734 
1735 static GLint gl2psGetPlaneFromPoints(GL2PSxyz a, GL2PSxyz b, GL2PSplane plane)
1736 {
1737   GLfloat n;
1738 
1739   plane[0] = b[1] - a[1];
1740   plane[1] = a[0] - b[0];
1741   n = (GLfloat)sqrt(plane[0]*plane[0] + plane[1]*plane[1]);
1742   plane[2] = 0.0F;
1743   if(!GL2PS_ZERO(n)){
1744     plane[0] /= n;
1745     plane[1] /= n;
1746     plane[3] = -plane[0]*a[0]-plane[1]*a[1];
1747     return 1;
1748   }
1749   else{
1750     plane[0] = -1.0F;
1751     plane[1] = 0.0F;
1752     plane[3] = a[0];
1753     return 0;
1754   }
1755 }
1756 
1757 static void gl2psFreeBspImageTree(GL2PSbsptree2d **tree)
1758 {
1759   if(*tree){
1760     if((*tree)->back)  gl2psFreeBspImageTree(&(*tree)->back);
1761     if((*tree)->front) gl2psFreeBspImageTree(&(*tree)->front);
1762     gl2psFree(*tree);
1763     *tree = NULL;
1764   }
1765 }
1766 
1767 static GLint gl2psCheckPoint(GL2PSxyz point, GL2PSplane plane)
1768 {
1769   GLfloat pt_dis;
1770 
1771   pt_dis = gl2psComparePointPlane(point, plane);
1772   if(pt_dis > GL2PS_EPSILON)        return GL2PS_POINT_INFRONT;
1773   else if(pt_dis < -GL2PS_EPSILON)  return GL2PS_POINT_BACK;
1774   else                              return GL2PS_POINT_COINCIDENT;
1775 }
1776 
1777 static void gl2psAddPlanesInBspTreeImage(GL2PSprimitive *prim,
1778                                          GL2PSbsptree2d **tree)
1779 {
1780   GLint ret = 0;
1781   GLint i;
1782   GLint offset = 0;
1783   GL2PSbsptree2d *head = NULL, *cur = NULL;
1784 
1785   if((*tree == NULL) && (prim->numverts > 2)){
1786     /* don't cull if transparent
1787     for(i = 0; i < prim->numverts - 1; i++)
1788       if(prim->verts[i].rgba[3] < 1.0F) return;
1789     */
1790     head = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
1791     for(i = 0; i < prim->numverts-1; i++){
1792       if(!gl2psGetPlaneFromPoints(prim->verts[i].xyz,
1793                                   prim->verts[i+1].xyz,
1794                                   head->plane)){
1795         if(prim->numverts-i > 3){
1796           offset++;
1797         }
1798         else{
1799           gl2psFree(head);
1800           return;
1801         }
1802       }
1803       else{
1804         break;
1805       }
1806     }
1807     head->back = NULL;
1808     head->front = NULL;
1809     for(i = 2+offset; i < prim->numverts; i++){
1810       ret = gl2psCheckPoint(prim->verts[i].xyz, head->plane);
1811       if(ret != GL2PS_POINT_COINCIDENT) break;
1812     }
1813     switch(ret){
1814     case GL2PS_POINT_INFRONT :
1815       cur = head;
1816       for(i = 1+offset; i < prim->numverts-1; i++){
1817         if(cur->front == NULL){
1818           cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
1819         }
1820         if(gl2psGetPlaneFromPoints(prim->verts[i].xyz,
1821                                    prim->verts[i+1].xyz,
1822                                    cur->front->plane)){
1823           cur = cur->front;
1824           cur->front = NULL;
1825           cur->back = NULL;
1826         }
1827       }
1828       if(cur->front == NULL){
1829         cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
1830       }
1831       if(gl2psGetPlaneFromPoints(prim->verts[i].xyz,
1832                                  prim->verts[offset].xyz,
1833                                  cur->front->plane)){
1834         cur->front->front = NULL;
1835         cur->front->back = NULL;
1836       }
1837       else{
1838         gl2psFree(cur->front);
1839         cur->front = NULL;
1840       }
1841       break;
1842     case GL2PS_POINT_BACK :
1843       for(i = 0; i < 4; i++){
1844         head->plane[i] = -head->plane[i];
1845       }
1846       cur = head;
1847       for(i = 1+offset; i < prim->numverts-1; i++){
1848         if(cur->front == NULL){
1849           cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
1850         }
1851         if(gl2psGetPlaneFromPoints(prim->verts[i+1].xyz,
1852                                    prim->verts[i].xyz,
1853                                    cur->front->plane)){
1854           cur = cur->front;
1855           cur->front = NULL;
1856           cur->back = NULL;
1857         }
1858       }
1859       if(cur->front == NULL){
1860         cur->front = (GL2PSbsptree2d*)gl2psMalloc(sizeof(GL2PSbsptree2d));
1861       }
1862       if(gl2psGetPlaneFromPoints(prim->verts[offset].xyz,
1863                                  prim->verts[i].xyz,
1864                                  cur->front->plane)){
1865         cur->front->front = NULL;
1866         cur->front->back = NULL;
1867       }
1868       else{
1869         gl2psFree(cur->front);
1870         cur->front = NULL;
1871       }
1872       break;
1873     default:
1874       gl2psFree(head);
1875       return;
1876     }
1877     (*tree) = head;
1878   }
1879 }
1880 
1881 static GLint gl2psCheckPrimitive(GL2PSprimitive *prim, GL2PSplane plane)
1882 {
1883   GLint i;
1884   GLint pos;
1885 
1886   pos = gl2psCheckPoint(prim->verts[0].xyz, plane);
1887   for(i = 1; i < prim->numverts; i++){
1888     pos |= gl2psCheckPoint(prim->verts[i].xyz, plane);
1889     if(pos == (GL2PS_POINT_INFRONT | GL2PS_POINT_BACK)) return GL2PS_SPANNING;
1890   }
1891   if(pos & GL2PS_POINT_INFRONT)   return GL2PS_IN_FRONT_OF;
1892   else if(pos & GL2PS_POINT_BACK) return GL2PS_IN_BACK_OF;
1893   else                            return GL2PS_COINCIDENT;
1894 }
1895 
1896 static GL2PSprimitive *gl2psCreateSplitPrimitive2D(GL2PSprimitive *parent,
1897                                                    GLshort numverts,
1898                                                    GL2PSvertex *vertx)
1899 {
1900   GLint i;
1901   GL2PSprimitive *child = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
1902 
1903   if(parent->type == GL2PS_IMAGEMAP){
1904     child->type = GL2PS_IMAGEMAP;
1905     child->data.image = parent->data.image;
1906   }
1907   else {
1908     switch(numverts){
1909     case 1 : child->type = GL2PS_POINT; break;
1910     case 2 : child->type = GL2PS_LINE; break;
1911     case 3 : child->type = GL2PS_TRIANGLE; break;
1912     case 4 : child->type = GL2PS_QUADRANGLE; break;
1913     default: child->type = GL2PS_NO_TYPE; break; /* FIXME */
1914     }
1915   }
1916   child->boundary = 0; /* FIXME: not done! */
1917   child->culled = parent->culled;
1918   child->offset = parent->offset;
1919   child->pattern = parent->pattern;
1920   child->factor = parent->factor;
1921   child->width = parent->width;
1922   child->numverts = numverts;
1923   child->verts = (GL2PSvertex*)gl2psMalloc(numverts * sizeof(GL2PSvertex));
1924   for(i = 0; i < numverts; i++){
1925     child->verts[i] = vertx[i];
1926   }
1927   return child;
1928 }
1929 
1930 static void gl2psSplitPrimitive2D(GL2PSprimitive *prim,
1931                                   GL2PSplane plane,
1932                                   GL2PSprimitive **front,
1933                                   GL2PSprimitive **back)
1934 {
1935   /* cur will hold the position of the current vertex
1936      prev will hold the position of the previous vertex
1937      prev0 will hold the position of the vertex number 0
1938      v1 and v2 represent the current and previous vertices, respectively
1939      flag is set if the current vertex should be checked against the plane */
1940   GLint cur = -1, prev = -1, i, v1 = 0, v2 = 0, flag = 1, prev0 = -1;
1941 
1942   /* list of vertices that will go in front and back primitive */
1943   GL2PSvertex *front_list = NULL, *back_list = NULL;
1944 
1945   /* number of vertices in front and back list */
1946   GLshort front_count = 0, back_count = 0;
1947 
1948   for(i = 0; i <= prim->numverts; i++){
1949     v1 = i;
1950     if(v1 == prim->numverts){
1951       if(prim->numverts < 3) break;
1952       v1 = 0;
1953       v2 = prim->numverts - 1;
1954       cur = prev0;
1955     }
1956     else if(flag){
1957       cur = gl2psCheckPoint(prim->verts[v1].xyz, plane);
1958       if(i == 0){
1959         prev0 = cur;
1960       }
1961     }
1962     if(((prev == -1) || (prev == cur) || (prev == 0) || (cur == 0)) &&
1963        (i < prim->numverts)){
1964       if(cur == GL2PS_POINT_INFRONT){
1965         front_count++;
1966         front_list = (GL2PSvertex*)gl2psRealloc(front_list,
1967                                                 sizeof(GL2PSvertex)*front_count);
1968         front_list[front_count-1] = prim->verts[v1];
1969       }
1970       else if(cur == GL2PS_POINT_BACK){
1971         back_count++;
1972         back_list = (GL2PSvertex*)gl2psRealloc(back_list,
1973                                                sizeof(GL2PSvertex)*back_count);
1974         back_list[back_count-1] = prim->verts[v1];
1975       }
1976       else{
1977         front_count++;
1978         front_list = (GL2PSvertex*)gl2psRealloc(front_list,
1979                                                 sizeof(GL2PSvertex)*front_count);
1980         front_list[front_count-1] = prim->verts[v1];
1981         back_count++;
1982         back_list = (GL2PSvertex*)gl2psRealloc(back_list,
1983                                                sizeof(GL2PSvertex)*back_count);
1984         back_list[back_count-1] = prim->verts[v1];
1985       }
1986       flag = 1;
1987     }
1988     else if((prev != cur) && (cur != 0) && (prev != 0)){
1989       if(v1 != 0){
1990         v2 = v1-1;
1991         i--;
1992       }
1993       front_count++;
1994       front_list = (GL2PSvertex*)gl2psRealloc(front_list,
1995                                               sizeof(GL2PSvertex)*front_count);
1996       gl2psCutEdge(&prim->verts[v2], &prim->verts[v1],
1997                    plane, &front_list[front_count-1]);
1998       back_count++;
1999       back_list = (GL2PSvertex*)gl2psRealloc(back_list,
2000                                              sizeof(GL2PSvertex)*back_count);
2001       back_list[back_count-1] = front_list[front_count-1];
2002       flag = 0;
2003     }
2004     prev = cur;
2005   }
2006   *front = gl2psCreateSplitPrimitive2D(prim, front_count, front_list);
2007   *back = gl2psCreateSplitPrimitive2D(prim, back_count, back_list);
2008   gl2psFree(front_list);
2009   gl2psFree(back_list);
2010 }
2011 
2012 static GLint gl2psAddInBspImageTree(GL2PSprimitive *prim, GL2PSbsptree2d **tree)
2013 {
2014   GLint ret = 0;
2015   GL2PSprimitive *frontprim = NULL, *backprim = NULL;
2016 
2017   /* FIXME: until we consider the actual extent of text strings and
2018      pixmaps, never cull them. Otherwise the whole string/pixmap gets
2019      culled as soon as the reference point is hidden */
2020   if(prim->type == GL2PS_PIXMAP ||
2021      prim->type == GL2PS_TEXT ||
2022      prim->type == GL2PS_SPECIAL){
2023     return 1;
2024   }
2025 
2026   if(*tree == NULL){
2027     if((prim->type != GL2PS_IMAGEMAP) && (GL_FALSE == gl2ps->zerosurfacearea)){
2028       gl2psAddPlanesInBspTreeImage(gl2ps->primitivetoadd, tree);
2029     }
2030     return 1;
2031   }
2032   else{
2033     switch(gl2psCheckPrimitive(prim, (*tree)->plane)){
2034     case GL2PS_IN_BACK_OF: return gl2psAddInBspImageTree(prim, &(*tree)->back);
2035     case GL2PS_IN_FRONT_OF:
2036       if((*tree)->front != NULL) return gl2psAddInBspImageTree(prim, &(*tree)->front);
2037       else                       return 0;
2038     case GL2PS_SPANNING:
2039       gl2psSplitPrimitive2D(prim, (*tree)->plane, &frontprim, &backprim);
2040       ret = gl2psAddInBspImageTree(backprim, &(*tree)->back);
2041       if((*tree)->front != NULL){
2042         if(gl2psAddInBspImageTree(frontprim, &(*tree)->front)){
2043           ret = 1;
2044         }
2045       }
2046       gl2psFree(frontprim->verts);
2047       gl2psFree(frontprim);
2048       gl2psFree(backprim->verts);
2049       gl2psFree(backprim);
2050       return ret;
2051     case GL2PS_COINCIDENT:
2052       if((*tree)->back != NULL){
2053         gl2ps->zerosurfacearea = GL_TRUE;
2054         ret = gl2psAddInBspImageTree(prim, &(*tree)->back);
2055         gl2ps->zerosurfacearea = GL_FALSE;
2056         if(ret) return ret;
2057       }
2058       if((*tree)->front != NULL){
2059         gl2ps->zerosurfacearea = GL_TRUE;
2060         ret = gl2psAddInBspImageTree(prim, &(*tree)->front);
2061         gl2ps->zerosurfacearea = GL_FALSE;
2062         if(ret) return ret;
2063       }
2064       if(prim->type == GL2PS_LINE) return 1;
2065       else                         return 0;
2066     }
2067   }
2068   return 0;
2069 }
2070 
2071 static void gl2psAddInImageTree(void *data)
2072 {
2073   GL2PSprimitive *prim = *(GL2PSprimitive **)data;
2074   gl2ps->primitivetoadd = prim;
2075   if(prim->type == GL2PS_IMAGEMAP && prim->data.image->format == GL2PS_IMAGEMAP_VISIBLE){
2076     prim->culled = 1;
2077   }
2078   else if(!gl2psAddInBspImageTree(prim, &gl2ps->imagetree)){
2079     prim->culled = 1;
2080   }
2081   else if(prim->type == GL2PS_IMAGEMAP){
2082     prim->data.image->format = GL2PS_IMAGEMAP_VISIBLE;
2083   }
2084 }
2085 
2086 /* Boundary construction */
2087 
2088 static void gl2psAddBoundaryInList(GL2PSprimitive *prim, GL2PSlist *list)
2089 {
2090   GL2PSprimitive *b;
2091   GLshort i;
2092   GL2PSxyz c;
2093 
2094   c[0] = c[1] = c[2] = 0.0F;
2095   for(i = 0; i < prim->numverts; i++){
2096     c[0] += prim->verts[i].xyz[0];
2097     c[1] += prim->verts[i].xyz[1];
2098   }
2099   c[0] /= prim->numverts;
2100   c[1] /= prim->numverts;
2101 
2102   for(i = 0; i < prim->numverts; i++){
2103     if(prim->boundary & (GLint)pow(2., i)){
2104       b = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
2105       b->type = GL2PS_LINE;
2106       b->offset = prim->offset;
2107       b->pattern = prim->pattern;
2108       b->factor = prim->factor;
2109       b->culled = prim->culled;
2110       b->width = prim->width;
2111       b->boundary = 0;
2112       b->numverts = 2;
2113       b->verts = (GL2PSvertex*)gl2psMalloc(2 * sizeof(GL2PSvertex));
2114 
2115 #if 0 /* FIXME: need to work on boundary offset... */
2116       v[0] = c[0] - prim->verts[i].xyz[0];
2117       v[1] = c[1] - prim->verts[i].xyz[1];
2118       v[2] = 0.0F;
2119       norm = gl2psNorm(v);
2120       v[0] /= norm;
2121       v[1] /= norm;
2122       b->verts[0].xyz[0] = prim->verts[i].xyz[0] +0.1*v[0];
2123       b->verts[0].xyz[1] = prim->verts[i].xyz[1] +0.1*v[1];
2124       b->verts[0].xyz[2] = prim->verts[i].xyz[2];
2125       v[0] = c[0] - prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[0];
2126       v[1] = c[1] - prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[1];
2127       norm = gl2psNorm(v);
2128       v[0] /= norm;
2129       v[1] /= norm;
2130       b->verts[1].xyz[0] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[0] +0.1*v[0];
2131       b->verts[1].xyz[1] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[1] +0.1*v[1];
2132       b->verts[1].xyz[2] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[2];
2133 #else
2134       b->verts[0].xyz[0] = prim->verts[i].xyz[0];
2135       b->verts[0].xyz[1] = prim->verts[i].xyz[1];
2136       b->verts[0].xyz[2] = prim->verts[i].xyz[2];
2137       b->verts[1].xyz[0] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[0];
2138       b->verts[1].xyz[1] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[1];
2139       b->verts[1].xyz[2] = prim->verts[gl2psGetIndex(i, prim->numverts)].xyz[2];
2140 #endif
2141 
2142       b->verts[0].rgba[0] = 0.0F;
2143       b->verts[0].rgba[1] = 0.0F;
2144       b->verts[0].rgba[2] = 0.0F;
2145       b->verts[0].rgba[3] = 0.0F;
2146       b->verts[1].rgba[0] = 0.0F;
2147       b->verts[1].rgba[1] = 0.0F;
2148       b->verts[1].rgba[2] = 0.0F;
2149       b->verts[1].rgba[3] = 0.0F;
2150       gl2psListAdd(list, &b);
2151     }
2152   }
2153 
2154 }
2155 
2156 static void gl2psBuildPolygonBoundary(GL2PSbsptree *tree)
2157 {
2158   GLint i;
2159   GL2PSprimitive *prim;
2160 
2161   if(!tree) return;
2162   gl2psBuildPolygonBoundary(tree->back);
2163   for(i = 0; i < gl2psListNbr(tree->primitives); i++){
2164     prim = *(GL2PSprimitive**)gl2psListPointer(tree->primitives, i);
2165     if(prim->boundary) gl2psAddBoundaryInList(prim, tree->primitives);
2166   }
2167   gl2psBuildPolygonBoundary(tree->front);
2168 }
2169 
2170 /*********************************************************************
2171  *
2172  * Feedback buffer parser
2173  *
2174  *********************************************************************/
2175 
2176 static void gl2psAddPolyPrimitive(GLshort type, GLshort numverts,
2177                                   GL2PSvertex *verts, GLint offset,
2178                                   GLushort pattern, GLint factor,
2179                                   GLfloat width, char boundary)
2180 {
2181   GL2PSprimitive *prim;
2182 
2183   prim = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
2184   prim->type = type;
2185   prim->numverts = numverts;
2186   prim->verts = (GL2PSvertex*)gl2psMalloc(numverts * sizeof(GL2PSvertex));
2187   memcpy(prim->verts, verts, numverts * sizeof(GL2PSvertex));
2188   prim->boundary = boundary;
2189   prim->offset = offset;
2190   prim->pattern = pattern;
2191   prim->factor = factor;
2192   prim->width = width;
2193   prim->culled = 0;
2194 
2195   /* FIXME: here we should have an option to split stretched
2196      tris/quads to enhance SIMPLE_SORT */
2197 
2198   gl2psListAdd(gl2ps->primitives, &prim);
2199 }
2200 
2201 static GLint gl2psGetVertex(GL2PSvertex *v, GLfloat *p)
2202 {
2203   GLint i;
2204 
2205   v->xyz[0] = p[0];
2206   v->xyz[1] = p[1];
2207   v->xyz[2] = p[2];
2208 
2209   if(gl2ps->colormode == GL_COLOR_INDEX && gl2ps->colorsize > 0){
2210     i = (GLint)(p[3] + 0.5);
2211     v->rgba[0] = gl2ps->colormap[i][0];
2212     v->rgba[1] = gl2ps->colormap[i][1];
2213     v->rgba[2] = gl2ps->colormap[i][2];
2214     v->rgba[3] = gl2ps->colormap[i][3];
2215     return 4;
2216   }
2217   else{
2218     v->rgba[0] = p[3];
2219     v->rgba[1] = p[4];
2220     v->rgba[2] = p[5];
2221     v->rgba[3] = p[6];
2222     return 7;
2223   }
2224 }
2225 
2226 static void gl2psParseFeedbackBuffer(GLint used)
2227 {
2228   char flag;
2229   GLushort pattern = 0;
2230   GLboolean boundary;
2231   GLint i, sizeoffloat, count, v, vtot, offset = 0, factor = 0, auxindex = 0;
2232   GLfloat lwidth = 1.0F, psize = 1.0F;
2233   GLfloat *current;
2234   GL2PSvertex vertices[3];
2235   GL2PSprimitive *prim;
2236   GL2PSimagemap *node;
2237 
2238   current = gl2ps->feedback;
2239   boundary = gl2ps->boundary = GL_FALSE;
2240 
2241   while(used > 0){
2242 
2243     if(GL_TRUE == boundary) gl2ps->boundary = GL_TRUE;
2244 
2245     switch((GLint)*current){
2246     case GL_POINT_TOKEN :
2247       current ++;
2248       used --;
2249       i = gl2psGetVertex(&vertices[0], current);
2250       current += i;
2251       used    -= i;
2252       gl2psAddPolyPrimitive(GL2PS_POINT, 1, vertices, 0,
2253                             pattern, factor, psize, 0);
2254       break;
2255     case GL_LINE_TOKEN :
2256     case GL_LINE_RESET_TOKEN :
2257       current ++;
2258       used --;
2259       i = gl2psGetVertex(&vertices[0], current);
2260       current += i;
2261       used    -= i;
2262       i = gl2psGetVertex(&vertices[1], current);
2263       current += i;
2264       used    -= i;
2265       gl2psAddPolyPrimitive(GL2PS_LINE, 2, vertices, 0,
2266                             pattern, factor, lwidth, 0);
2267       break;
2268     case GL_POLYGON_TOKEN :
2269       count = (GLint)current[1];
2270       current += 2;
2271       used -= 2;
2272       v = vtot = 0;
2273       while(count > 0 && used > 0){
2274         i = gl2psGetVertex(&vertices[v], current);
2275         gl2psAdaptVertexForBlending(&vertices[v]);
2276         current += i;
2277         used    -= i;
2278         count --;
2279         vtot++;
2280         if(v == 2){
2281           if(GL_TRUE == boundary){
2282             if(!count && vtot == 2) flag = 1|2|4;
2283             else if(!count) flag = 2|4;
2284             else if(vtot == 2) flag = 1|2;
2285             else flag = 2;
2286           }
2287           else
2288             flag = 0;
2289           gl2psAddPolyPrimitive(GL2PS_TRIANGLE, 3, vertices, offset,
2290                                 pattern, factor, 1, flag);
2291           vertices[1] = vertices[2];
2292         }
2293         else
2294           v ++;
2295       }
2296       break;
2297     case GL_BITMAP_TOKEN :
2298     case GL_DRAW_PIXEL_TOKEN :
2299     case GL_COPY_PIXEL_TOKEN :
2300       current ++;
2301       used --;
2302       i = gl2psGetVertex(&vertices[0], current);
2303       current += i;
2304       used    -= i;
2305       break;
2306     case GL_PASS_THROUGH_TOKEN :
2307       switch((GLint)current[1]){
2308       case GL2PS_BEGIN_OFFSET_TOKEN : offset = 1; break;
2309       case GL2PS_END_OFFSET_TOKEN : offset = 0; break;
2310       case GL2PS_BEGIN_BOUNDARY_TOKEN : boundary = GL_TRUE; break;
2311       case GL2PS_END_BOUNDARY_TOKEN : boundary = GL_FALSE; break;
2312       case GL2PS_END_STIPPLE_TOKEN : pattern = factor = 0; break;
2313       case GL2PS_BEGIN_BLEND_TOKEN : gl2ps->blending = GL_TRUE; break;
2314       case GL2PS_END_BLEND_TOKEN : gl2ps->blending = GL_FALSE; break;
2315       case GL2PS_BEGIN_STIPPLE_TOKEN :
2316         current += 2;
2317         used -= 2;
2318         pattern = (GLushort)current[1];
2319         current += 2;
2320         used -= 2;
2321         factor = (GLint)current[1];
2322         break;
2323       case GL2PS_SRC_BLEND_TOKEN :
2324         current += 2;
2325         used -= 2;
2326         gl2ps->blendfunc[0] = (GLint)current[1];
2327         break;
2328       case GL2PS_DST_BLEND_TOKEN :
2329         current += 2;
2330         used -= 2;
2331         gl2ps->blendfunc[1] = (GLint)current[1];
2332         break;
2333       case GL2PS_POINT_SIZE_TOKEN :
2334         current += 2;
2335         used -= 2;
2336         psize = current[1];
2337         break;
2338       case GL2PS_LINE_WIDTH_TOKEN :
2339         current += 2;
2340         used -= 2;
2341         lwidth = current[1];
2342         break;
2343       case GL2PS_IMAGEMAP_TOKEN :
2344         prim = (GL2PSprimitive *)gl2psMalloc(sizeof(GL2PSprimitive));
2345         prim->type = GL2PS_IMAGEMAP;
2346         prim->boundary = 0;
2347         prim->numverts = 4;
2348         prim->verts = (GL2PSvertex *)gl2psMalloc(4 * sizeof(GL2PSvertex));
2349         prim->culled = 0;
2350         prim->offset = 0;
2351         prim->pattern = 0;
2352         prim->factor = 0;
2353         prim->width = 1;
2354 
2355         node = (GL2PSimagemap*)gl2psMalloc(sizeof(GL2PSimagemap));
2356         node->image = (GL2PSimage*)gl2psMalloc(sizeof(GL2PSimage));
2357         node->image->type = 0;
2358         node->image->format = 0;
2359         node->image->zoom_x = 1.0F;
2360         node->image->zoom_y = 1.0F;
2361         node->next = NULL;
2362 
2363         if(gl2ps->imagemap_head == NULL)
2364           gl2ps->imagemap_head = node;
2365         else
2366           gl2ps->imagemap_tail->next = node;
2367         gl2ps->imagemap_tail = node;
2368         prim->data.image = node->image;
2369 
2370         current += 2; used -= 2;
2371         i = gl2psGetVertex(&prim->verts[0], &current[1]);
2372         current += i; used -= i;
2373 
2374         node->image->width = (GLint)current[2];
2375         current += 2; used -= 2;
2376         node->image->height = (GLint)current[2];
2377         prim->verts[0].xyz[0] = prim->verts[0].xyz[0] - (int)(node->image->width / 2) + 0.5F;
2378         prim->verts[0].xyz[1] = prim->verts[0].xyz[1] - (int)(node->image->height / 2) + 0.5F;
2379         for(i = 1; i < 4; i++){
2380           for(v = 0; v < 3; v++){
2381             prim->verts[i].xyz[v] = prim->verts[0].xyz[v];
2382             prim->verts[i].rgba[v] = prim->verts[0].rgba[v];
2383           }
2384           prim->verts[i].rgba[v] = prim->verts[0].rgba[v];
2385         }
2386         prim->verts[1].xyz[0] = prim->verts[1].xyz[0] + node->image->width;
2387         prim->verts[2].xyz[0] = prim->verts[1].xyz[0];
2388         prim->verts[2].xyz[1] = prim->verts[2].xyz[1] + node->image->height;
2389         prim->verts[3].xyz[1] = prim->verts[2].xyz[1];
2390 
2391         sizeoffloat = sizeof(GLfloat);
2392         v = 2 * sizeoffloat;
2393         vtot = node->image->height + node->image->height *
2394           ((node->image->width - 1) / 8);
2395         node->image->pixels = (GLfloat*)gl2psMalloc(v + vtot);
2396         node->image->pixels[0] = prim->verts[0].xyz[0];
2397         node->image->pixels[1] = prim->verts[0].xyz[1];
2398 
2399         for(i = 0; i < vtot; i += sizeoffloat){
2400           current += 2; used -= 2;
2401           if((vtot - i) >= 4)
2402             memcpy(&(((char*)(node->image->pixels))[i + v]), &(current[2]), sizeoffloat);
2403           else
2404             memcpy(&(((char*)(node->image->pixels))[i + v]), &(current[2]), vtot - i);
2405         }
2406         current++; used--;
2407         gl2psListAdd(gl2ps->primitives, &prim);
2408         break;
2409       case GL2PS_DRAW_PIXELS_TOKEN :
2410       case GL2PS_TEXT_TOKEN :
2411         if(auxindex < gl2psListNbr(gl2ps->auxprimitives))
2412           gl2psListAdd(gl2ps->primitives,
2413                        gl2psListPointer(gl2ps->auxprimitives, auxindex++));
2414         else
2415           gl2psMsg(GL2PS_ERROR, "Wrong number of auxiliary tokens in buffer");
2416         break;
2417       }
2418       current += 2;
2419       used -= 2;
2420       break;
2421     default :
2422       gl2psMsg(GL2PS_WARNING, "Unknown token in buffer");
2423       current ++;
2424       used --;
2425       break;
2426     }
2427   }
2428 
2429   gl2psListReset(gl2ps->auxprimitives);
2430 }
2431 
2432 /*********************************************************************
2433  *
2434  * PostScript routines
2435  *
2436  *********************************************************************/
2437 
2438 static void gl2psWriteByte(unsigned char byte)
2439 {
2440   unsigned char h = byte / 16;
2441   unsigned char l = byte % 16;
2442   gl2psPrintf("%x%x", h, l);
2443 }
2444 
2445 static void gl2psPrintPostScriptPixmap(GLfloat x, GLfloat y, GL2PSimage *im)
2446 {
2447   GLuint nbhex, nbyte, nrgb, nbits;
2448   GLuint row, col, ibyte, icase;
2449   GLfloat dr, dg, db, fgrey;
2450   unsigned char red = 0, green = 0, blue = 0, b, grey;
2451   GLuint width = (GLuint)im->width;
2452   GLuint height = (GLuint)im->height;
2453 
2454   /* FIXME: should we define an option for these? Or just keep the
2455      8-bit per component case? */
2456   int greyscale = 0; /* set to 1 to output greyscale image */
2457   int nbit = 8; /* number of bits per color compoment (2, 4 or 8) */
2458 
2459   if((width <= 0) || (height <= 0)) return;
2460 
2461   gl2psPrintf("gsave\n");
2462   gl2psPrintf("%.2f %.2f translate\n", x, y);
2463   gl2psPrintf("%.2f %.2f scale\n", width * im->zoom_x, height * im->zoom_y);
2464 
2465   if(greyscale){ /* greyscale */
2466     gl2psPrintf("/picstr %d string def\n", width);
2467     gl2psPrintf("%d %d %d\n", width, height, 8);
2468     gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", width, height, height);
2469     gl2psPrintf("{ currentfile picstr readhexstring pop }\n");
2470     gl2psPrintf("image\n");
2471     for(row = 0; row < height; row++){
2472       for(col = 0; col < width; col++){
2473         gl2psGetRGB(im, col, row, &dr, &dg, &db);
2474         fgrey = (0.30F * dr + 0.59F * dg + 0.11F * db);
2475         grey = (unsigned char)(255. * fgrey);
2476         gl2psWriteByte(grey);
2477       }
2478       gl2psPrintf("\n");
2479     }
2480     nbhex = width * height * 2;
2481     gl2psPrintf("%%%% nbhex digit          :%d\n", nbhex);
2482   }
2483   else if(nbit == 2){ /* color, 2 bits for r and g and b; rgbs following each other */
2484     nrgb = width  * 3;
2485     nbits = nrgb * nbit;
2486     nbyte = nbits / 8;
2487     if((nbyte * 8) != nbits) nbyte++;
2488     gl2psPrintf("/rgbstr %d string def\n", nbyte);
2489     gl2psPrintf("%d %d %d\n", width, height, nbit);
2490     gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", width, height, height);
2491     gl2psPrintf("{ currentfile rgbstr readhexstring pop }\n");
2492     gl2psPrintf("false 3\n");
2493     gl2psPrintf("colorimage\n");
2494     for(row = 0; row < height; row++){
2495       icase = 1;
2496       col = 0;
2497       b = 0;
2498       for(ibyte = 0; ibyte < nbyte; ibyte++){
2499         if(icase == 1) {
2500           if(col < width) {
2501             gl2psGetRGB(im, col, row, &dr, &dg, &db);
2502           }
2503           else {
2504             dr = dg = db = 0;
2505           }
2506           col++;
2507           red = (unsigned char)(3. * dr);
2508           green = (unsigned char)(3. * dg);
2509           blue = (unsigned char)(3. * db);
2510           b = red;
2511           b = (b<<2) + green;
2512           b = (b<<2) + blue;
2513           if(col < width) {
2514             gl2psGetRGB(im, col, row, &dr, &dg, &db);
2515           }
2516           else {
2517             dr = dg = db = 0;
2518           }
2519           col++;
2520           red = (unsigned char)(3. * dr);
2521           green = (unsigned char)(3. * dg);
2522           blue = (unsigned char)(3. * db);
2523           b = (b<<2) + red;
2524           gl2psWriteByte(b);
2525           b = 0;
2526           icase++;
2527         }
2528         else if(icase == 2) {
2529           b = green;
2530           b = (b<<2) + blue;
2531           if(col < width) {
2532             gl2psGetRGB(im, col, row, &dr, &dg, &db);
2533           }
2534           else {
2535             dr = dg = db = 0;
2536           }
2537           col++;
2538           red = (unsigned char)(3. * dr);
2539           green = (unsigned char)(3. * dg);
2540           blue = (unsigned char)(3. * db);
2541           b = (b<<2) + red;
2542           b = (b<<2) + green;
2543           gl2psWriteByte(b);
2544           b = 0;
2545           icase++;
2546         }
2547         else if(icase == 3) {
2548           b = blue;
2549           if(col < width) {
2550             gl2psGetRGB(im, col, row, &dr, &dg, &db);
2551           }
2552           else {
2553             dr = dg = db = 0;
2554           }
2555           col++;
2556           red = (unsigned char)(3. * dr);
2557           green = (unsigned char)(3. * dg);
2558           blue = (unsigned char)(3. * db);
2559           b = (b<<2) + red;
2560           b = (b<<2) + green;
2561           b = (b<<2) + blue;
2562           gl2psWriteByte(b);
2563           b = 0;
2564           icase = 1;
2565         }
2566       }
2567       gl2psPrintf("\n");
2568     }
2569   }
2570   else if(nbit == 4){ /* color, 4 bits for r and g and b; rgbs following each other */
2571     nrgb = width  * 3;
2572     nbits = nrgb * nbit;
2573     nbyte = nbits / 8;
2574     if((nbyte * 8) != nbits) nbyte++;
2575     gl2psPrintf("/rgbstr %d string def\n", nbyte);
2576     gl2psPrintf("%d %d %d\n", width, height, nbit);
2577     gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", width, height, height);
2578     gl2psPrintf("{ currentfile rgbstr readhexstring pop }\n");
2579     gl2psPrintf("false 3\n");
2580     gl2psPrintf("colorimage\n");
2581     for(row = 0; row < height; row++){
2582       col = 0;
2583       icase = 1;
2584       for(ibyte = 0; ibyte < nbyte; ibyte++){
2585         if(icase == 1) {
2586           if(col < width) {
2587             gl2psGetRGB(im, col, row, &dr, &dg, &db);
2588           }
2589           else {
2590             dr = dg = db = 0;
2591           }
2592           col++;
2593           red = (unsigned char)(15. * dr);
2594           green = (unsigned char)(15. * dg);
2595           gl2psPrintf("%x%x", red, green);
2596           icase++;
2597         }
2598         else if(icase == 2) {
2599           blue = (unsigned char)(15. * db);
2600           if(col < width) {
2601             gl2psGetRGB(im, col, row, &dr, &dg, &db);
2602           }
2603           else {
2604             dr = dg = db = 0;
2605           }
2606           col++;
2607           red = (unsigned char)(15. * dr);
2608           gl2psPrintf("%x%x", blue, red);
2609           icase++;
2610         }
2611         else if(icase == 3) {
2612           green = (unsigned char)(15. * dg);
2613           blue = (unsigned char)(15. * db);
2614           gl2psPrintf("%x%x", green, blue);
2615           icase = 1;
2616         }
2617       }
2618       gl2psPrintf("\n");
2619     }
2620   }
2621   else{ /* 8 bit for r and g and b */
2622     nbyte = width * 3;
2623     gl2psPrintf("/rgbstr %d string def\n", nbyte);
2624     gl2psPrintf("%d %d %d\n", width, height, 8);
2625     gl2psPrintf("[ %d 0 0 -%d 0 %d ]\n", width, height, height);
2626     gl2psPrintf("{ currentfile rgbstr readhexstring pop }\n");
2627     gl2psPrintf("false 3\n");
2628     gl2psPrintf("colorimage\n");
2629     for(row = 0; row < height; row++){
2630       for(col = 0; col < width; col++){
2631         gl2psGetRGB(im, col, row, &dr, &dg, &db);
2632         red = (unsigned char)(255. * dr);
2633         gl2psWriteByte(red);
2634         green = (unsigned char)(255. * dg);
2635         gl2psWriteByte(green);
2636         blue = (unsigned char)(255. * db);
2637         gl2psWriteByte(blue);
2638       }
2639       gl2psPrintf("\n");
2640     }
2641   }
2642 
2643   gl2psPrintf("grestore\n");
2644 }
2645 
2646 static void gl2psPrintPostScriptImagemap(GLfloat x, GLfloat y,
2647                                          GLsizei width, GLsizei height,
2648                                          const unsigned char *imagemap){
2649   int i, size;
2650 
2651   if((width <= 0) || (height <= 0)) return;
2652 
2653   size = height + height * (width - 1) / 8;
2654 
2655   gl2psPrintf("gsave\n");
2656   gl2psPrintf("%.2f %.2f translate\n", x, y);
2657   gl2psPrintf("%d %d scale\n%d %d\ntrue\n", width, height,width, height);
2658   gl2psPrintf("[ %d 0 0 -%d 0 %d ] {<", width, height);
2659   for(i = 0; i < size; i++){
2660     gl2psWriteByte(*imagemap);
2661     imagemap++;
2662   }
2663   gl2psPrintf(">} imagemask\ngrestore\n");
2664 }
2665 
2666 static void gl2psPrintPostScriptHeader(void)
2667 {
2668   time_t now;
2669 
2670   /* Since compression is not part of the PostScript standard,
2671      compressed PostScript files are just gzipped PostScript files
2672      ("ps.gz" or "eps.gz") */
2673   gl2psPrintGzipHeader();
2674 
2675   time(&now);
2676 
2677   if(gl2ps->format == GL2PS_PS){
2678     gl2psPrintf("%%!PS-Adobe-3.0\n");
2679   }
2680   else{
2681     gl2psPrintf("%%!PS-Adobe-3.0 EPSF-3.0\n");
2682   }
2683 
2684   gl2psPrintf("%%%%Title: %s\n"
2685               "%%%%Creator: GL2PS %d.%d.%d%s, %s\n"
2686               "%%%%For: %s\n"
2687               "%%%%CreationDate: %s"
2688               "%%%%LanguageLevel: 3\n"
2689               "%%%%DocumentData: Clean7Bit\n"
2690               "%%%%Pages: 1\n",
2691               gl2ps->title, GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION,
2692               GL2PS_PATCH_VERSION, GL2PS_EXTRA_VERSION, GL2PS_COPYRIGHT,
2693               gl2ps->producer, ctime(&now));
2694 
2695   if(gl2ps->format == GL2PS_PS){
2696     gl2psPrintf("%%%%Orientation: %s\n"
2697                 "%%%%DocumentMedia: Default %d %d 0 () ()\n",
2698                 (gl2ps->options & GL2PS_LANDSCAPE) ? "Landscape" : "Portrait",
2699                 (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[3] :
2700                 (int)gl2ps->viewport[2],
2701                 (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[2] :
2702                 (int)gl2ps->viewport[3]);
2703   }
2704 
2705   gl2psPrintf("%%%%BoundingBox: %d %d %d %d\n"
2706               "%%%%EndComments\n",
2707               (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[1] :
2708               (int)gl2ps->viewport[0],
2709               (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[0] :
2710               (int)gl2ps->viewport[1],
2711               (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[3] :
2712               (int)gl2ps->viewport[2],
2713               (gl2ps->options & GL2PS_LANDSCAPE) ? (int)gl2ps->viewport[2] :
2714               (int)gl2ps->viewport[3]);
2715 
2716   /* RGB color: r g b C (replace C by G in output to change from rgb to gray)
2717      Grayscale: r g b G
2718      Font choose: size fontname FC
2719      Text string: (string) x y size fontname S??
2720      Rotated text string: (string) angle x y size fontname S??R
2721      Point primitive: x y size P
2722      Line width: width W
2723      Line start: x y LS
2724      Line joining last point: x y L
2725      Line end: x y LE
2726      Flat-shaded triangle: x3 y3 x2 y2 x1 y1 T
2727      Smooth-shaded triangle: x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 ST */
2728 
2729   gl2psPrintf("%%%%BeginProlog\n"
2730               "/gl2psdict 64 dict def gl2psdict begin\n"
2731               "0 setlinecap 0 setlinejoin\n"
2732               "/tryPS3shading %s def %% set to false to force subdivision\n"
2733               "/rThreshold %g def %% red component subdivision threshold\n"
2734               "/gThreshold %g def %% green component subdivision threshold\n"
2735               "/bThreshold %g def %% blue component subdivision threshold\n",
2736               (gl2ps->options & GL2PS_NO_PS3_SHADING) ? "false" : "true",
2737               gl2ps->threshold[0], gl2ps->threshold[1], gl2ps->threshold[2]);
2738 
2739   gl2psPrintf("/BD { bind def } bind def\n"
2740               "/C  { setrgbcolor } BD\n"
2741               "/G  { 0.082 mul exch 0.6094 mul add exch 0.3086 mul add neg 1.0 add setgray } BD\n"
2742               "/W  { setlinewidth } BD\n");
2743 
2744   gl2psPrintf("/FC { findfont exch /SH exch def SH scalefont setfont } BD\n"
2745               "/SW { dup stringwidth pop } BD\n"
2746               "/S  { FC moveto show } BD\n"
2747               "/SBC{ FC moveto SW -2 div 0 rmoveto show } BD\n"
2748               "/SBR{ FC moveto SW neg 0 rmoveto show } BD\n"
2749               "/SCL{ FC moveto 0 SH -2 div rmoveto show } BD\n"
2750               "/SCC{ FC moveto SW -2 div SH -2 div rmoveto show } BD\n"
2751               "/SCR{ FC moveto SW neg SH -2 div rmoveto show } BD\n"
2752               "/STL{ FC moveto 0 SH neg rmoveto show } BD\n"
2753               "/STC{ FC moveto SW -2 div SH neg rmoveto show } BD\n"
2754               "/STR{ FC moveto SW neg SH neg rmoveto show } BD\n");
2755 
2756   /* rotated text routines: same nameanem with R appended */
2757 
2758   gl2psPrintf("/FCT { FC translate 0 0 } BD\n"
2759               "/SR  { gsave FCT moveto rotate show grestore } BD\n"
2760               "/SBCR{ gsave FCT moveto rotate SW -2 div 0 rmoveto show grestore } BD\n"
2761               "/SBRR{ gsave FCT moveto rotate SW neg 0 rmoveto show grestore } BD\n"
2762               "/SCLR{ gsave FCT moveto rotate 0 SH -2 div rmoveto show grestore} BD\n");
2763   gl2psPrintf("/SCCR{ gsave FCT moveto rotate SW -2 div SH -2 div rmoveto show grestore} BD\n"
2764               "/SCRR{ gsave FCT moveto rotate SW neg SH -2 div rmoveto show grestore} BD\n"
2765               "/STLR{ gsave FCT moveto rotate 0 SH neg rmoveto show grestore } BD\n"
2766               "/STCR{ gsave FCT moveto rotate SW -2 div SH neg rmoveto show grestore } BD\n"
2767               "/STRR{ gsave FCT moveto rotate SW neg SH neg rmoveto show grestore } BD\n");
2768 
2769   gl2psPrintf("/P  { newpath 0.0 360.0 arc closepath fill } BD\n"
2770               "/LS { newpath moveto } BD\n"
2771               "/L  { lineto } BD\n"
2772               "/LE { lineto stroke } BD\n"
2773               "/T  { newpath moveto lineto lineto closepath fill } BD\n");
2774 
2775   /* Smooth-shaded triangle with PostScript level 3 shfill operator:
2776         x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 STshfill */
2777 
2778   gl2psPrintf("/STshfill {\n"
2779               "      /b1 exch def /g1 exch def /r1 exch def /y1 exch def /x1 exch def\n"
2780               "      /b2 exch def /g2 exch def /r2 exch def /y2 exch def /x2 exch def\n"
2781               "      /b3 exch def /g3 exch def /r3 exch def /y3 exch def /x3 exch def\n"
2782               "      gsave << /ShadingType 4 /ColorSpace [/DeviceRGB]\n"
2783               "      /DataSource [ 0 x1 y1 r1 g1 b1 0 x2 y2 r2 g2 b2 0 x3 y3 r3 g3 b3 ] >>\n"
2784               "      shfill grestore } BD\n");
2785 
2786   /* Flat-shaded triangle with middle color:
2787         x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 Tm */
2788 
2789   gl2psPrintf(/* stack : x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 */
2790               "/Tm { 3 -1 roll 8 -1 roll 13 -1 roll add add 3 div\n" /* r = (r1+r2+r3)/3 */
2791               /* stack : x3 y3 g3 b3 x2 y2 g2 b2 x1 y1 g1 b1 r */
2792               "      3 -1 roll 7 -1 roll 11 -1 roll add add 3 div\n" /* g = (g1+g2+g3)/3 */
2793               /* stack : x3 y3 b3 x2 y2 b2 x1 y1 b1 r g b */
2794               "      3 -1 roll 6 -1 roll 9 -1 roll add add 3 div" /* b = (b1+b2+b3)/3 */
2795               /* stack : x3 y3 x2 y2 x1 y1 r g b */
2796               " C T } BD\n");
2797 
2798   /* Split triangle in four sub-triangles (at sides middle points) and call the
2799      STnoshfill procedure on each, interpolating the colors in RGB space:
2800         x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 STsplit
2801      (in procedure comments key: (Vi) = xi yi ri gi bi) */
2802 
2803   gl2psPrintf("/STsplit {\n"
2804               "      4 index 15 index add 0.5 mul\n" /* x13 = (x1+x3)/2 */
2805               "      4 index 15 index add 0.5 mul\n" /* y13 = (y1+y3)/2 */
2806               "      4 index 15 index add 0.5 mul\n" /* r13 = (r1+r3)/2 */
2807               "      4 index 15 index add 0.5 mul\n" /* g13 = (g1+g3)/2 */
2808               "      4 index 15 index add 0.5 mul\n" /* b13 = (b1+b3)/2 */
2809               "      5 copy 5 copy 25 15 roll\n");
2810 
2811   /* at his point, stack = (V3) (V13) (V13) (V13) (V2) (V1) */
2812 
2813   gl2psPrintf("      9 index 30 index add 0.5 mul\n" /* x23 = (x2+x3)/2 */
2814               "      9 index 30 index add 0.5 mul\n" /* y23 = (y2+y3)/2 */
2815               "      9 index 30 index add 0.5 mul\n" /* r23 = (r2+r3)/2 */
2816               "      9 index 30 index add 0.5 mul\n" /* g23 = (g2+g3)/2 */
2817               "      9 index 30 index add 0.5 mul\n" /* b23 = (b2+b3)/2 */
2818               "      5 copy 5 copy 35 5 roll 25 5 roll 15 5 roll\n");
2819 
2820   /* stack = (V3) (V13) (V23) (V13) (V23) (V13) (V23) (V2) (V1) */
2821 
2822   gl2psPrintf("      4 index 10 index add 0.5 mul\n" /* x12 = (x1+x2)/2 */
2823               "      4 index 10 index add 0.5 mul\n" /* y12 = (y1+y2)/2 */
2824               "      4 index 10 index add 0.5 mul\n" /* r12 = (r1+r2)/2 */
2825               "      4 index 10 index add 0.5 mul\n" /* g12 = (g1+g2)/2 */
2826               "      4 index 10 index add 0.5 mul\n" /* b12 = (b1+b2)/2 */
2827               "      5 copy 5 copy 40 5 roll 25 5 roll 15 5 roll 25 5 roll\n");
2828 
2829   /* stack = (V3) (V13) (V23) (V13) (V12) (V23) (V13) (V1) (V12) (V23) (V12) (V2) */
2830 
2831   gl2psPrintf("      STnoshfill STnoshfill STnoshfill STnoshfill } BD\n");
2832 
2833   /* Gouraud shaded triangle using recursive subdivision until the difference
2834      between corner colors does not exceed the thresholds:
2835         x3 y3 r3 g3 b3 x2 y2 r2 g2 b2 x1 y1 r1 g1 b1 STnoshfill  */
2836 
2837   gl2psPrintf("/STnoshfill {\n"
2838               "      2 index 8 index sub abs rThreshold gt\n" /* |r1-r2|>rth */
2839               "      { STsplit }\n"
2840               "      { 1 index 7 index sub abs gThreshold gt\n" /* |g1-g2|>gth */
2841               "        { STsplit }\n"
2842               "        { dup 6 index sub abs bThreshold gt\n" /* |b1-b2|>bth */
2843               "          { STsplit }\n"
2844               "          { 2 index 13 index sub abs rThreshold gt\n" /* |r1-r3|>rht */
2845               "            { STsplit }\n"
2846               "            { 1 index 12 index sub abs gThreshold gt\n" /* |g1-g3|>gth */
2847               "              { STsplit }\n"
2848               "              { dup 11 index sub abs bThreshold gt\n" /* |b1-b3|>bth */
2849               "                { STsplit }\n"
2850               "                { 7 index 13 index sub abs rThreshold gt\n"); /* |r2-r3|>rht */
2851   gl2psPrintf("                  { STsplit }\n"
2852               "                  { 6 index 12 index sub abs gThreshold gt\n" /* |g2-g3|>gth */
2853               "                    { STsplit }\n"
2854               "                    { 5 index 11 index sub abs bThreshold gt\n" /* |b2-b3|>bth */
2855               "                      { STsplit }\n"
2856               "                      { Tm }\n" /* all colors sufficiently similar */
2857               "                      ifelse }\n"
2858               "                    ifelse }\n"
2859               "                  ifelse }\n"
2860               "                ifelse }\n"
2861               "              ifelse }\n"
2862               "            ifelse }\n"
2863               "          ifelse }\n"
2864               "        ifelse }\n"
2865               "      ifelse } BD\n");
2866 
2867   gl2psPrintf("tryPS3shading\n"
2868               "{ /shfill where\n"
2869               "  { /ST { STshfill } BD }\n"
2870               "  { /ST { STnoshfill } BD }\n"
2871               "  ifelse }\n"
2872               "{ /ST { STnoshfill } BD }\n"
2873               "ifelse\n");
2874 
2875   gl2psPrintf("end\n"
2876               "%%%%EndProlog\n"
2877               "%%%%BeginSetup\n"
2878               "/DeviceRGB setcolorspace\n"
2879               "gl2psdict begin\n"
2880               "%%%%EndSetup\n"
2881               "%%%%Page: 1 1\n"
2882               "%%%%BeginPageSetup\n");
2883 
2884   if(gl2ps->options & GL2PS_LANDSCAPE){
2885     gl2psPrintf("%d 0 translate 90 rotate\n",
2886                 (int)gl2ps->viewport[3]);
2887   }
2888 
2889   gl2psPrintf("%%%%EndPageSetup\n"
2890               "mark\n"
2891               "gsave\n"
2892               "1.0 1.0 scale\n");
2893 
2894   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
2895     gl2psPrintf("%g %g %g C\n"
2896                 "newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto\n"
2897                 "closepath fill\n",
2898                 gl2ps->bgcolor[0], gl2ps->bgcolor[1], gl2ps->bgcolor[2],
2899                 (int)gl2ps->viewport[0], (int)gl2ps->viewport[1], (int)gl2ps->viewport[2],
2900                 (int)gl2ps->viewport[1], (int)gl2ps->viewport[2], (int)gl2ps->viewport[3],
2901                 (int)gl2ps->viewport[0], (int)gl2ps->viewport[3]);
2902   }
2903 }
2904 
2905 static void gl2psPrintPostScriptColor(GL2PSrgba rgba)
2906 {
2907   if(!gl2psSameColor(gl2ps->lastrgba, rgba)){
2908     gl2psSetLastColor(rgba);
2909     gl2psPrintf("%g %g %g C\n", rgba[0], rgba[1], rgba[2]);
2910   }
2911 }
2912 
2913 static void gl2psResetPostScriptColor(void)
2914 {
2915   gl2ps->lastrgba[0] = gl2ps->lastrgba[1] = gl2ps->lastrgba[2] = -1.;
2916 }
2917 
2918 static void gl2psEndPostScriptLine(void)
2919 {
2920   int i;
2921   if(gl2ps->lastvertex.rgba[0] >= 0.){
2922     gl2psPrintf("%g %g LE\n", gl2ps->lastvertex.xyz[0], gl2ps->lastvertex.xyz[1]);
2923     for(i = 0; i < 3; i++)
2924       gl2ps->lastvertex.xyz[i] = -1.;
2925     for(i = 0; i < 4; i++)
2926       gl2ps->lastvertex.rgba[i] = -1.;
2927   }
2928 }
2929 
2930 static void gl2psParseStipplePattern(GLushort pattern, GLint factor,
2931                                      int *nb, int array[10])
2932 {
2933   int i, n;
2934   int on[8] = {0, 0, 0, 0, 0, 0, 0, 0};
2935   int off[8] = {0, 0, 0, 0, 0, 0, 0, 0};
2936   char tmp[16];
2937 
2938   /* extract the 16 bits from the OpenGL stipple pattern */
2939   for(n = 15; n >= 0; n--){
2940     tmp[n] = (char)(pattern & 0x01);
2941     pattern >>= 1;
2942   }
2943   /* compute the on/off pixel sequence */
2944   n = 0;
2945   for(i = 0; i < 8; i++){
2946     while(n < 16 && !tmp[n]){ off[i]++; n++; }
2947     while(n < 16 && tmp[n]){ on[i]++; n++; }
2948     if(n >= 15){ i++; break; }
2949   }
2950 
2951   /* store the on/off array from right to left, starting with off
2952      pixels. The PostScript specification allows for at most 11
2953      elements in the on/off array, so we limit ourselves to 5 on/off
2954      couples (our longest possible array is thus [on4 off4 on3 off3
2955      on2 off2 on1 off1 on0 off0]) */
2956   *nb = 0;
2957   for(n = i - 1; n >= 0; n--){
2958     array[(*nb)++] = factor * on[n];
2959     array[(*nb)++] = factor * off[n];
2960     if(*nb == 10) break;
2961   }
2962 }
2963 
2964 static int gl2psPrintPostScriptDash(GLushort pattern, GLint factor, const char *str)
2965 {
2966   int len = 0, i, n, array[10];
2967 
2968   if(pattern == gl2ps->lastpattern && factor == gl2ps->lastfactor)
2969     return 0;
2970 
2971   gl2ps->lastpattern = pattern;
2972   gl2ps->lastfactor = factor;
2973 
2974   if(!pattern || !factor){
2975     /* solid line */
2976     len += gl2psPrintf("[] 0 %s\n", str);
2977   }
2978   else{
2979     gl2psParseStipplePattern(pattern, factor, &n, array);
2980     len += gl2psPrintf("[");
2981     for(i = 0; i < n; i++){
2982       if(i) len += gl2psPrintf(" ");
2983       len += gl2psPrintf("%d", array[i]);
2984     }
2985     len += gl2psPrintf("] 0 %s\n", str);
2986   }
2987 
2988   return len;
2989 }
2990 
2991 static void gl2psPrintPostScriptPrimitive(void *data)
2992 {
2993   int newline;
2994   GL2PSprimitive *prim;
2995 
2996   prim = *(GL2PSprimitive**)data;
2997 
2998   if((gl2ps->options & GL2PS_OCCLUSION_CULL) && prim->culled) return;
2999 
3000   /* Every effort is made to draw lines as connected segments (i.e.,
3001      using a single PostScript path): this is the only way to get nice
3002      line joins and to not restart the stippling for every line
3003      segment. So if the primitive to print is not a line we must first
3004      finish the current line (if any): */
3005   if(prim->type != GL2PS_LINE) gl2psEndPostScriptLine();
3006 
3007   switch(prim->type){
3008   case GL2PS_POINT :
3009     gl2psPrintPostScriptColor(prim->verts[0].rgba);
3010     gl2psPrintf("%g %g %g P\n",
3011                 prim->verts[0].xyz[0], prim->verts[0].xyz[1], 0.5 * prim->width);
3012     break;
3013   case GL2PS_LINE :
3014     if(!gl2psSamePosition(gl2ps->lastvertex.xyz, prim->verts[0].xyz) ||
3015        !gl2psSameColor(gl2ps->lastrgba, prim->verts[0].rgba) ||
3016        gl2ps->lastlinewidth != prim->width ||
3017        gl2ps->lastpattern != prim->pattern ||
3018        gl2ps->lastfactor != prim->factor){
3019       /* End the current line if the new segment does not start where
3020          the last one ended, or if the color, the width or the
3021          stippling have changed (multi-stroking lines with changing
3022          colors is necessary until we use /shfill for lines;
3023          unfortunately this means that at the moment we can screw up
3024          line stippling for smooth-shaded lines) */
3025       gl2psEndPostScriptLine();
3026       newline = 1;
3027     }
3028     else{
3029       newline = 0;
3030     }
3031     if(gl2ps->lastlinewidth != prim->width){
3032       gl2ps->lastlinewidth = prim->width;
3033       gl2psPrintf("%g W\n", gl2ps->lastlinewidth);
3034     }
3035     gl2psPrintPostScriptDash(prim->pattern, prim->factor, "setdash");
3036     gl2psPrintPostScriptColor(prim->verts[0].rgba);
3037     gl2psPrintf("%g %g %s\n", prim->verts[0].xyz[0], prim->verts[0].xyz[1],
3038                 newline ? "LS" : "L");
3039     gl2ps->lastvertex = prim->verts[1];
3040     break;
3041   case GL2PS_TRIANGLE :
3042     if(!gl2psVertsSameColor(prim)){
3043       gl2psResetPostScriptColor();
3044       gl2psPrintf("%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g ST\n",
3045                   prim->verts[2].xyz[0], prim->verts[2].xyz[1],
3046                   prim->verts[2].rgba[0], prim->verts[2].rgba[1],
3047                   prim->verts[2].rgba[2], prim->verts[1].xyz[0],
3048                   prim->verts[1].xyz[1], prim->verts[1].rgba[0],
3049                   prim->verts[1].rgba[1], prim->verts[1].rgba[2],
3050                   prim->verts[0].xyz[0], prim->verts[0].xyz[1],
3051                   prim->verts[0].rgba[0], prim->verts[0].rgba[1],
3052                   prim->verts[0].rgba[2]);
3053     }
3054     else{
3055       gl2psPrintPostScriptColor(prim->verts[0].rgba);
3056       gl2psPrintf("%g %g %g %g %g %g T\n",
3057                   prim->verts[2].xyz[0], prim->verts[2].xyz[1],
3058                   prim->verts[1].xyz[0], prim->verts[1].xyz[1],
3059                   prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
3060     }
3061     break;
3062   case GL2PS_QUADRANGLE :
3063     gl2psMsg(GL2PS_WARNING, "There should not be any quad left to print");
3064     break;
3065   case GL2PS_PIXMAP :
3066     gl2psPrintPostScriptPixmap(prim->verts[0].xyz[0], prim->verts[0].xyz[1],
3067                                prim->data.image);
3068     break;
3069   case GL2PS_IMAGEMAP :
3070     if(prim->data.image->type != GL2PS_IMAGEMAP_WRITTEN){
3071       gl2psPrintPostScriptColor(prim->verts[0].rgba);
3072       gl2psPrintPostScriptImagemap(prim->data.image->pixels[0],
3073                                    prim->data.image->pixels[1],
3074                                    prim->data.image->width, prim->data.image->height,
3075                                    (const unsigned char*)(&(prim->data.image->pixels[2])));
3076       prim->data.image->type = GL2PS_IMAGEMAP_WRITTEN;
3077     }
3078     break;
3079   case GL2PS_TEXT :
3080     gl2psPrintPostScriptColor(prim->verts[0].rgba);
3081     gl2psPrintf("(%s) ", prim->data.text->str);
3082     if(prim->data.text->angle)
3083       gl2psPrintf("%g ", prim->data.text->angle);
3084     gl2psPrintf("%g %g %d /%s ",
3085                 prim->verts[0].xyz[0], prim->verts[0].xyz[1],
3086                 prim->data.text->fontsize, prim->data.text->fontname);
3087     switch(prim->data.text->alignment){
3088     case GL2PS_TEXT_C:
3089       gl2psPrintf(prim->data.text->angle ? "SCCR\n" : "SCC\n");
3090       break;
3091     case GL2PS_TEXT_CL:
3092       gl2psPrintf(prim->data.text->angle ? "SCLR\n" : "SCL\n");
3093       break;
3094     case GL2PS_TEXT_CR:
3095       gl2psPrintf(prim->data.text->angle ? "SCRR\n" : "SCR\n");
3096       break;
3097     case GL2PS_TEXT_B:
3098       gl2psPrintf(prim->data.text->angle ? "SBCR\n" : "SBC\n");
3099       break;
3100     case GL2PS_TEXT_BR:
3101       gl2psPrintf(prim->data.text->angle ? "SBRR\n" : "SBR\n");
3102       break;
3103     case GL2PS_TEXT_T:
3104       gl2psPrintf(prim->data.text->angle ? "STCR\n" : "STC\n");
3105       break;
3106     case GL2PS_TEXT_TL:
3107       gl2psPrintf(prim->data.text->angle ? "STLR\n" : "STL\n");
3108       break;
3109     case GL2PS_TEXT_TR:
3110       gl2psPrintf(prim->data.text->angle ? "STRR\n" : "STR\n");
3111       break;
3112     case GL2PS_TEXT_BL:
3113     default:
3114       gl2psPrintf(prim->data.text->angle ? "SR\n" : "S\n");
3115       break;
3116     }
3117     break;
3118   case GL2PS_SPECIAL :
3119     /* alignment contains the format for which the special output text
3120        is intended */
3121     if(prim->data.text->alignment == GL2PS_PS ||
3122        prim->data.text->alignment == GL2PS_EPS)
3123       gl2psPrintf("%s\n", prim->data.text->str);
3124     break;
3125   default :
3126     break;
3127   }
3128 }
3129 
3130 static void gl2psPrintPostScriptFooter(void)
3131 {
3132   gl2psPrintf("grestore\n"
3133               "showpage\n"
3134               "cleartomark\n"
3135               "%%%%PageTrailer\n"
3136               "%%%%Trailer\n"
3137               "end\n"
3138               "%%%%EOF\n");
3139 
3140   gl2psPrintGzipFooter();
3141 }
3142 
3143 static void gl2psPrintPostScriptBeginViewport(GLint viewport[4])
3144 {
3145   GLint idx;
3146   GLfloat rgba[4];
3147   int x = viewport[0], y = viewport[1], w = viewport[2], h = viewport[3];
3148 
3149   glRenderMode(GL_FEEDBACK);
3150 
3151   if(gl2ps->header){
3152     gl2psPrintPostScriptHeader();
3153     gl2ps->header = GL_FALSE;
3154   }
3155 
3156   gl2psPrintf("gsave\n"
3157               "1.0 1.0 scale\n");
3158 
3159   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
3160     if(gl2ps->colormode == GL_RGBA || gl2ps->colorsize == 0){
3161       glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba);
3162     }
3163     else{
3164       glGetIntegerv(GL_INDEX_CLEAR_VALUE, &idx);
3165       rgba[0] = gl2ps->colormap[idx][0];
3166       rgba[1] = gl2ps->colormap[idx][1];
3167       rgba[2] = gl2ps->colormap[idx][2];
3168       rgba[3] = 1.0F;
3169     }
3170     gl2psPrintf("%g %g %g C\n"
3171                 "newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto\n"
3172                 "closepath fill\n",
3173                 rgba[0], rgba[1], rgba[2],
3174                 x, y, x+w, y, x+w, y+h, x, y+h);
3175   }
3176 
3177   gl2psPrintf("newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto\n"
3178               "closepath clip\n",
3179               x, y, x+w, y, x+w, y+h, x, y+h);
3180 
3181 }
3182 
3183 static GLint gl2psPrintPostScriptEndViewport(void)
3184 {
3185   GLint res;
3186 
3187   res = gl2psPrintPrimitives();
3188   gl2psPrintf("grestore\n");
3189   return res;
3190 }
3191 
3192 static void gl2psPrintPostScriptFinalPrimitive(void)
3193 {
3194   /* End any remaining line, if any */
3195   gl2psEndPostScriptLine();
3196 }
3197 
3198 /* definition of the PostScript and Encapsulated PostScript backends */
3199 
3200 static GL2PSbackend gl2psPS = {
3201   gl2psPrintPostScriptHeader,
3202   gl2psPrintPostScriptFooter,
3203   gl2psPrintPostScriptBeginViewport,
3204   gl2psPrintPostScriptEndViewport,
3205   gl2psPrintPostScriptPrimitive,
3206   gl2psPrintPostScriptFinalPrimitive,
3207   "ps",
3208   "Postscript"
3209 };
3210 
3211 static GL2PSbackend gl2psEPS = {
3212   gl2psPrintPostScriptHeader,
3213   gl2psPrintPostScriptFooter,
3214   gl2psPrintPostScriptBeginViewport,
3215   gl2psPrintPostScriptEndViewport,
3216   gl2psPrintPostScriptPrimitive,
3217   gl2psPrintPostScriptFinalPrimitive,
3218   "eps",
3219   "Encapsulated Postscript"
3220 };
3221 
3222 /*********************************************************************
3223  *
3224  * LaTeX routines
3225  *
3226  *********************************************************************/
3227 
3228 static void gl2psPrintTeXHeader(void)
3229 {
3230   char name[256];
3231   time_t now;
3232   int i;
3233 
3234   if(gl2ps->filename && strlen(gl2ps->filename) < 256){
3235     for(i = (int)strlen(gl2ps->filename) - 1; i >= 0; i--){
3236       if(gl2ps->filename[i] == '.'){
3237         strncpy(name, gl2ps->filename, i);
3238         name[i] = '\0';
3239         break;
3240       }
3241     }
3242     if(i <= 0) strcpy(name, gl2ps->filename);
3243   }
3244   else{
3245     strcpy(name, "untitled");
3246   }
3247 
3248   time(&now);
3249 
3250   fprintf(gl2ps->stream,
3251           "%% Title: %s\n"
3252           "%% Creator: GL2PS %d.%d.%d%s, %s\n"
3253           "%% For: %s\n"
3254           "%% CreationDate: %s",
3255           gl2ps->title, GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION,
3256           GL2PS_PATCH_VERSION, GL2PS_EXTRA_VERSION, GL2PS_COPYRIGHT,
3257           gl2ps->producer, ctime(&now));
3258 
3259   fprintf(gl2ps->stream,
3260           "\\setlength{\\unitlength}{1pt}\n"
3261           "\\begin{picture}(0,0)\n"
3262           "\\includegraphics{%s}\n"
3263           "\\end{picture}%%\n"
3264           "%s\\begin{picture}(%d,%d)(0,0)\n",
3265           name, (gl2ps->options & GL2PS_LANDSCAPE) ? "\\rotatebox{90}{" : "",
3266           (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
3267 }
3268 
3269 static void gl2psPrintTeXPrimitive(void *data)
3270 {
3271   GL2PSprimitive *prim;
3272 
3273   prim = *(GL2PSprimitive**)data;
3274 
3275   switch(prim->type){
3276   case GL2PS_TEXT :
3277     fprintf(gl2ps->stream, "\\fontsize{%d}{0}\n\\selectfont",
3278             prim->data.text->fontsize);
3279     fprintf(gl2ps->stream, "\\put(%g,%g)",
3280             prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
3281     if(prim->data.text->angle)
3282       fprintf(gl2ps->stream, "{\\rotatebox{%g}", prim->data.text->angle);
3283     fprintf(gl2ps->stream, "{\\makebox(0,0)");
3284     switch(prim->data.text->alignment){
3285     case GL2PS_TEXT_C:
3286       fprintf(gl2ps->stream, "{");
3287       break;
3288     case GL2PS_TEXT_CL:
3289       fprintf(gl2ps->stream, "[l]{");
3290       break;
3291     case GL2PS_TEXT_CR:
3292       fprintf(gl2ps->stream, "[r]{");
3293       break;
3294     case GL2PS_TEXT_B:
3295       fprintf(gl2ps->stream, "[b]{");
3296       break;
3297     case GL2PS_TEXT_BR:
3298       fprintf(gl2ps->stream, "[br]{");
3299       break;
3300     case GL2PS_TEXT_T:
3301       fprintf(gl2ps->stream, "[t]{");
3302       break;
3303     case GL2PS_TEXT_TL:
3304       fprintf(gl2ps->stream, "[tl]{");
3305       break;
3306     case GL2PS_TEXT_TR:
3307       fprintf(gl2ps->stream, "[tr]{");
3308       break;
3309     case GL2PS_TEXT_BL:
3310     default:
3311       fprintf(gl2ps->stream, "[bl]{");
3312       break;
3313     }
3314     fprintf(gl2ps->stream, "\\textcolor[rgb]{%g,%g,%g}{{%s}}",
3315             prim->verts[0].rgba[0], prim->verts[0].rgba[1], prim->verts[0].rgba[2],
3316             prim->data.text->str);
3317     if(prim->data.text->angle)
3318       fprintf(gl2ps->stream, "}");
3319     fprintf(gl2ps->stream, "}}\n");
3320     break;
3321   case GL2PS_SPECIAL :
3322     /* alignment contains the format for which the special output text
3323        is intended */
3324     if (prim->data.text->alignment == GL2PS_TEX)
3325       fprintf(gl2ps->stream, "%s\n", prim->data.text->str);
3326     break;
3327   default :
3328     break;
3329   }
3330 }
3331 
3332 static void gl2psPrintTeXFooter(void)
3333 {
3334   fprintf(gl2ps->stream, "\\end{picture}%s\n",
3335           (gl2ps->options & GL2PS_LANDSCAPE) ? "}" : "");
3336 }
3337 
3338 static void gl2psPrintTeXBeginViewport(GLint viewport[4])
3339 {
3340   (void) viewport;  /* not used */
3341   glRenderMode(GL_FEEDBACK);
3342 
3343   if(gl2ps->header){
3344     gl2psPrintTeXHeader();
3345     gl2ps->header = GL_FALSE;
3346   }
3347 }
3348 
3349 static GLint gl2psPrintTeXEndViewport(void)
3350 {
3351   return gl2psPrintPrimitives();
3352 }
3353 
3354 static void gl2psPrintTeXFinalPrimitive(void)
3355 {
3356 }
3357 
3358 /* definition of the LaTeX backend */
3359 
3360 static GL2PSbackend gl2psTEX = {
3361   gl2psPrintTeXHeader,
3362   gl2psPrintTeXFooter,
3363   gl2psPrintTeXBeginViewport,
3364   gl2psPrintTeXEndViewport,
3365   gl2psPrintTeXPrimitive,
3366   gl2psPrintTeXFinalPrimitive,
3367   "tex",
3368   "LaTeX text"
3369 };
3370 
3371 /*********************************************************************
3372  *
3373  * PDF routines
3374  *
3375  *********************************************************************/
3376 
3377 static int gl2psPrintPDFCompressorType(void)
3378 {
3379 #if defined(GL2PS_HAVE_ZLIB)
3380   if(gl2ps->options & GL2PS_COMPRESS){
3381     return fprintf(gl2ps->stream, "/Filter [/FlateDecode]\n");
3382   }
3383 #endif
3384   return 0;
3385 }
3386 
3387 static int gl2psPrintPDFStrokeColor(GL2PSrgba rgba)
3388 {
3389   int i, offs = 0;
3390 
3391   gl2psSetLastColor(rgba);
3392   for(i = 0; i < 3; ++i){
3393     if(GL2PS_ZERO(rgba[i]))
3394       offs += gl2psPrintf("%.0f ", 0.);
3395     else if(rgba[i] < 1e-4 || rgba[i] > 1e6) /* avoid %e formatting */
3396       offs += gl2psPrintf("%f ", rgba[i]);
3397     else
3398       offs += gl2psPrintf("%g ", rgba[i]);
3399   }
3400   offs += gl2psPrintf("RG\n");
3401   return offs;
3402 }
3403 
3404 static int gl2psPrintPDFFillColor(GL2PSrgba rgba)
3405 {
3406   int i, offs = 0;
3407 
3408   for(i = 0; i < 3; ++i){
3409     if(GL2PS_ZERO(rgba[i]))
3410       offs += gl2psPrintf("%.0f ", 0.);
3411     else if(rgba[i] < 1e-4 || rgba[i] > 1e6) /* avoid %e formatting */
3412       offs += gl2psPrintf("%f ", rgba[i]);
3413     else
3414       offs += gl2psPrintf("%g ", rgba[i]);
3415   }
3416   offs += gl2psPrintf("rg\n");
3417   return offs;
3418 }
3419 
3420 static int gl2psPrintPDFLineWidth(GLfloat lw)
3421 {
3422   if(GL2PS_ZERO(lw))
3423     return gl2psPrintf("%.0f w\n", 0.);
3424   else if(lw < 1e-4 || lw > 1e6) /* avoid %e formatting */
3425     return gl2psPrintf("%f w\n", lw);
3426   else
3427     return gl2psPrintf("%g w\n", lw);
3428 }
3429 
3430 static void gl2psPutPDFText(GL2PSstring *text, int cnt, GLfloat x, GLfloat y)
3431 {
3432   GLfloat rad, crad, srad;
3433 
3434   if(text->angle == 0.0F){
3435     gl2ps->streamlength += gl2psPrintf
3436       ("BT\n"
3437        "/F%d %d Tf\n"
3438        "%f %f Td\n"
3439        "(%s) Tj\n"
3440        "ET\n",
3441        cnt, text->fontsize, x, y, text->str);
3442   }
3443   else{
3444     rad = (GLfloat)(3.141593F * text->angle / 180.0F);
3445     srad = (GLfloat)sin(rad);
3446     crad = (GLfloat)cos(rad);
3447     gl2ps->streamlength += gl2psPrintf
3448       ("BT\n"
3449        "/F%d %d Tf\n"
3450        "%f %f %f %f %f %f Tm\n"
3451        "(%s) Tj\n"
3452        "ET\n",
3453        cnt, text->fontsize, crad, srad, -srad, crad, x, y, text->str);
3454   }
3455 }
3456 
3457 static void gl2psPutPDFSpecial(GL2PSstring *text)
3458 {
3459   gl2ps->streamlength += gl2psPrintf("%s\n", text->str);
3460 }
3461 
3462 static void gl2psPutPDFImage(GL2PSimage *image, int cnt, GLfloat x, GLfloat y)
3463 {
3464   gl2ps->streamlength += gl2psPrintf
3465     ("q\n"
3466      "%d 0 0 %d %f %f cm\n"
3467      "/Im%d Do\n"
3468      "Q\n",
3469      (int)image->width, (int)image->height, x, y, cnt);
3470 }
3471 
3472 static void gl2psPDFstacksInit(void)
3473 {
3474   gl2ps->objects_stack = 7 /* FIXED_XREF_ENTRIES */ + 1;
3475   gl2ps->extgs_stack = 0;
3476   gl2ps->font_stack = 0;
3477   gl2ps->im_stack = 0;
3478   gl2ps->trgroupobjects_stack = 0;
3479   gl2ps->shader_stack = 0;
3480   gl2ps->mshader_stack = 0;
3481 }
3482 
3483 static void gl2psPDFgroupObjectInit(GL2PSpdfgroup *gro)
3484 {
3485   if(!gro)
3486     return;
3487 
3488   gro->ptrlist = NULL;
3489   gro->fontno = gro->gsno = gro->imno = gro->maskshno = gro->shno
3490     = gro->trgroupno = gro->fontobjno = gro->imobjno = gro->shobjno
3491     = gro->maskshobjno = gro->gsobjno = gro->trgroupobjno = -1;
3492 }
3493 
3494 /* Build up group objects and assign name and object numbers */
3495 
3496 static void gl2psPDFgroupListInit(void)
3497 {
3498   int i;
3499   GL2PSprimitive *p = NULL;
3500   GL2PSpdfgroup gro;
3501   int lasttype = GL2PS_NO_TYPE;
3502   GL2PSrgba lastrgba = {-1.0F, -1.0F, -1.0F, -1.0F};
3503   GLushort lastpattern = 0;
3504   GLint lastfactor = 0;
3505   GLfloat lastwidth = 1;
3506   GL2PStriangle lastt, tmpt;
3507   int lastTriangleWasNotSimpleWithSameColor = 0;
3508 
3509   if(!gl2ps->pdfprimlist)
3510     return;
3511 
3512   gl2ps->pdfgrouplist = gl2psListCreate(500, 500, sizeof(GL2PSpdfgroup));
3513   gl2psInitTriangle(&lastt);
3514 
3515   for(i = 0; i < gl2psListNbr(gl2ps->pdfprimlist); ++i){
3516     p = *(GL2PSprimitive**)gl2psListPointer(gl2ps->pdfprimlist, i);
3517     switch(p->type){
3518     case GL2PS_PIXMAP:
3519       gl2psPDFgroupObjectInit(&gro);
3520       gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
3521       gro.imno = gl2ps->im_stack++;
3522       gl2psListAdd(gro.ptrlist, &p);
3523       gl2psListAdd(gl2ps->pdfgrouplist, &gro);
3524       break;
3525     case GL2PS_TEXT:
3526       gl2psPDFgroupObjectInit(&gro);
3527       gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
3528       gro.fontno = gl2ps->font_stack++;
3529       gl2psListAdd(gro.ptrlist, &p);
3530       gl2psListAdd(gl2ps->pdfgrouplist, &gro);
3531       break;
3532     case GL2PS_LINE:
3533       if(lasttype != p->type || lastwidth != p->width ||
3534          lastpattern != p->pattern || lastfactor != p->factor ||
3535          !gl2psSameColor(p->verts[0].rgba, lastrgba)){
3536         gl2psPDFgroupObjectInit(&gro);
3537         gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
3538         gl2psListAdd(gro.ptrlist, &p);
3539         gl2psListAdd(gl2ps->pdfgrouplist, &gro);
3540       }
3541       else{
3542         gl2psListAdd(gro.ptrlist, &p);
3543       }
3544       lastpattern = p->pattern;
3545       lastfactor = p->factor;
3546       lastwidth = p->width;
3547       lastrgba[0] = p->verts[0].rgba[0];
3548       lastrgba[1] = p->verts[0].rgba[1];
3549       lastrgba[2] = p->verts[0].rgba[2];
3550       break;
3551     case GL2PS_POINT:
3552       if(lasttype != p->type || lastwidth != p->width ||
3553          !gl2psSameColor(p->verts[0].rgba, lastrgba)){
3554         gl2psPDFgroupObjectInit(&gro);
3555         gro.ptrlist = gl2psListCreate(1,2,sizeof(GL2PSprimitive*));
3556         gl2psListAdd(gro.ptrlist, &p);
3557         gl2psListAdd(gl2ps->pdfgrouplist, &gro);
3558       }
3559       else{
3560         gl2psListAdd(gro.ptrlist, &p);
3561       }
3562       lastwidth = p->width;
3563       lastrgba[0] = p->verts[0].rgba[0];
3564       lastrgba[1] = p->verts[0].rgba[1];
3565       lastrgba[2] = p->verts[0].rgba[2];
3566       break;
3567     case GL2PS_TRIANGLE:
3568       gl2psFillTriangleFromPrimitive(&tmpt, p, GL_TRUE);
3569       lastTriangleWasNotSimpleWithSameColor =
3570         !(tmpt.prop & T_CONST_COLOR && tmpt.prop & T_ALPHA_1) ||
3571         !gl2psSameColor(tmpt.vertex[0].rgba, lastt.vertex[0].rgba);
3572       if(lasttype == p->type && tmpt.prop == lastt.prop &&
3573          lastTriangleWasNotSimpleWithSameColor){
3574         /* Check here for last alpha */
3575         gl2psListAdd(gro.ptrlist, &p);
3576       }
3577       else{
3578         gl2psPDFgroupObjectInit(&gro);
3579         gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
3580         gl2psListAdd(gro.ptrlist, &p);
3581         gl2psListAdd(gl2ps->pdfgrouplist, &gro);
3582       }
3583       lastt = tmpt;
3584       break;
3585     case GL2PS_SPECIAL:
3586       gl2psPDFgroupObjectInit(&gro);
3587       gro.ptrlist = gl2psListCreate(1, 2, sizeof(GL2PSprimitive*));
3588       gl2psListAdd(gro.ptrlist, &p);
3589       gl2psListAdd(gl2ps->pdfgrouplist, &gro);
3590       break;
3591     default:
3592       break;
3593     }
3594     lasttype = p->type;
3595   }
3596 }
3597 
3598 static void gl2psSortOutTrianglePDFgroup(GL2PSpdfgroup *gro)
3599 {
3600   GL2PStriangle t;
3601   GL2PSprimitive *prim = NULL;
3602 
3603   if(!gro)
3604     return;
3605 
3606   if(!gl2psListNbr(gro->ptrlist))
3607     return;
3608 
3609   prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);
3610 
3611   if(prim->type != GL2PS_TRIANGLE)
3612     return;
3613 
3614   gl2psFillTriangleFromPrimitive(&t, prim, GL_TRUE);
3615 
3616   if(t.prop & T_CONST_COLOR && t.prop & T_ALPHA_LESS_1){
3617     gro->gsno = gl2ps->extgs_stack++;
3618     gro->gsobjno = gl2ps->objects_stack ++;
3619   }
3620   else if(t.prop & T_CONST_COLOR && t.prop & T_VAR_ALPHA){
3621     gro->gsno = gl2ps->extgs_stack++;
3622     gro->gsobjno = gl2ps->objects_stack++;
3623     gro->trgroupno = gl2ps->trgroupobjects_stack++;
3624     gro->trgroupobjno = gl2ps->objects_stack++;
3625     gro->maskshno = gl2ps->mshader_stack++;
3626     gro->maskshobjno = gl2ps->objects_stack++;
3627   }
3628   else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_1){
3629     gro->shno = gl2ps->shader_stack++;
3630     gro->shobjno = gl2ps->objects_stack++;
3631   }
3632   else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_LESS_1){
3633     gro->gsno = gl2ps->extgs_stack++;
3634     gro->gsobjno = gl2ps->objects_stack++;
3635     gro->shno = gl2ps->shader_stack++;
3636     gro->shobjno = gl2ps->objects_stack++;
3637   }
3638   else if(t.prop & T_VAR_COLOR && t.prop & T_VAR_ALPHA){
3639     gro->gsno = gl2ps->extgs_stack++;
3640     gro->gsobjno = gl2ps->objects_stack++;
3641     gro->shno = gl2ps->shader_stack++;
3642     gro->shobjno = gl2ps->objects_stack++;
3643     gro->trgroupno = gl2ps->trgroupobjects_stack++;
3644     gro->trgroupobjno = gl2ps->objects_stack++;
3645     gro->maskshno = gl2ps->mshader_stack++;
3646     gro->maskshobjno = gl2ps->objects_stack++;
3647   }
3648 }
3649 
3650 /* Main stream data */
3651 
3652 static void gl2psPDFgroupListWriteMainStream(void)
3653 {
3654   int i, j, lastel;
3655   GL2PSprimitive *prim = NULL, *prev = NULL;
3656   GL2PSpdfgroup *gro;
3657   GL2PStriangle t;
3658 
3659   if(!gl2ps->pdfgrouplist)
3660     return;
3661 
3662   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
3663     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
3664 
3665     lastel = gl2psListNbr(gro->ptrlist) - 1;
3666     if(lastel < 0)
3667       continue;
3668 
3669     prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);
3670 
3671     switch(prim->type){
3672     case GL2PS_POINT:
3673       gl2ps->streamlength += gl2psPrintf("1 J\n");
3674       gl2ps->streamlength += gl2psPrintPDFLineWidth(prim->width);
3675       gl2ps->streamlength += gl2psPrintPDFStrokeColor(prim->verts[0].rgba);
3676       for(j = 0; j <= lastel; ++j){
3677         prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
3678         gl2ps->streamlength +=
3679           gl2psPrintf("%f %f m %f %f l\n",
3680                       prim->verts[0].xyz[0], prim->verts[0].xyz[1],
3681                       prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
3682       }
3683       gl2ps->streamlength += gl2psPrintf("S\n");
3684       gl2ps->streamlength += gl2psPrintf("0 J\n");
3685       break;
3686     case GL2PS_LINE:
3687       /* We try to use as few paths as possible to draw lines, in
3688          order to get nice stippling even when the individual segments
3689          are smaller than the stipple */
3690       gl2ps->streamlength += gl2psPrintPDFLineWidth(prim->width);
3691       gl2ps->streamlength += gl2psPrintPDFStrokeColor(prim->verts[0].rgba);
3692       gl2ps->streamlength += gl2psPrintPostScriptDash(prim->pattern, prim->factor, "d");
3693       /* start new path */
3694       gl2ps->streamlength +=
3695         gl2psPrintf("%f %f m\n",
3696                     prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
3697 
3698       for(j = 1; j <= lastel; ++j){
3699         prev = prim;
3700         prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
3701         if(!gl2psSamePosition(prim->verts[0].xyz, prev->verts[1].xyz)){
3702           /* the starting point of the new segment does not match the
3703              end point of the previous line, so we end the current
3704              path and start a new one */
3705           gl2ps->streamlength +=
3706             gl2psPrintf("%f %f l\n",
3707                         prev->verts[1].xyz[0], prev->verts[1].xyz[1]);
3708           gl2ps->streamlength +=
3709             gl2psPrintf("%f %f m\n",
3710                         prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
3711         }
3712         else{
3713           /* the two segments are connected, so we just append to the
3714              current path */
3715           gl2ps->streamlength +=
3716             gl2psPrintf("%f %f l\n",
3717                         prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
3718         }
3719       }
3720       /* end last path */
3721       gl2ps->streamlength +=
3722         gl2psPrintf("%f %f l\n",
3723                     prim->verts[1].xyz[0], prim->verts[1].xyz[1]);
3724       gl2ps->streamlength += gl2psPrintf("S\n");
3725       break;
3726     case GL2PS_TRIANGLE:
3727       gl2psFillTriangleFromPrimitive(&t, prim, GL_TRUE);
3728       gl2psSortOutTrianglePDFgroup(gro);
3729 
3730       /* No alpha and const color: Simple PDF draw orders  */
3731       if(t.prop & T_CONST_COLOR && t.prop & T_ALPHA_1){
3732         gl2ps->streamlength += gl2psPrintPDFFillColor(t.vertex[0].rgba);
3733         for(j = 0; j <= lastel; ++j){
3734           prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
3735           gl2psFillTriangleFromPrimitive(&t, prim, GL_FALSE);
3736           gl2ps->streamlength
3737             += gl2psPrintf("%f %f m\n"
3738                            "%f %f l\n"
3739                            "%f %f l\n"
3740                            "h f\n",
3741                            t.vertex[0].xyz[0], t.vertex[0].xyz[1],
3742                            t.vertex[1].xyz[0], t.vertex[1].xyz[1],
3743                            t.vertex[2].xyz[0], t.vertex[2].xyz[1]);
3744         }
3745       }
3746       /* Const alpha < 1 and const color: Simple PDF draw orders
3747          and an extra extended Graphics State for the alpha const */
3748       else if(t.prop & T_CONST_COLOR && t.prop & T_ALPHA_LESS_1){
3749         gl2ps->streamlength += gl2psPrintf("q\n"
3750                                            "/GS%d gs\n",
3751                                            gro->gsno);
3752         gl2ps->streamlength += gl2psPrintPDFFillColor(prim->verts[0].rgba);
3753         for(j = 0; j <= lastel; ++j){
3754           prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
3755           gl2psFillTriangleFromPrimitive(&t, prim, GL_FALSE);
3756           gl2ps->streamlength
3757             += gl2psPrintf("%f %f m\n"
3758                            "%f %f l\n"
3759                            "%f %f l\n"
3760                            "h f\n",
3761                            t.vertex[0].xyz[0], t.vertex[0].xyz[1],
3762                            t.vertex[1].xyz[0], t.vertex[1].xyz[1],
3763                            t.vertex[2].xyz[0], t.vertex[2].xyz[1]);
3764         }
3765         gl2ps->streamlength += gl2psPrintf("Q\n");
3766       }
3767       /* Variable alpha and const color: Simple PDF draw orders
3768          and an extra extended Graphics State + Xobject + Shader
3769          object for the alpha mask */
3770       else if(t.prop & T_CONST_COLOR && t.prop & T_VAR_ALPHA){
3771         gl2ps->streamlength += gl2psPrintf("q\n"
3772                                            "/GS%d gs\n"
3773                                            "/TrG%d Do\n",
3774                                            gro->gsno, gro->trgroupno);
3775         gl2ps->streamlength += gl2psPrintPDFFillColor(prim->verts[0].rgba);
3776         for(j = 0; j <= lastel; ++j){
3777           prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
3778           gl2psFillTriangleFromPrimitive(&t, prim, GL_FALSE);
3779           gl2ps->streamlength
3780             += gl2psPrintf("%f %f m\n"
3781                            "%f %f l\n"
3782                            "%f %f l\n"
3783                            "h f\n",
3784                            t.vertex[0].xyz[0], t.vertex[0].xyz[1],
3785                            t.vertex[1].xyz[0], t.vertex[1].xyz[1],
3786                            t.vertex[2].xyz[0], t.vertex[2].xyz[1]);
3787         }
3788         gl2ps->streamlength += gl2psPrintf("Q\n");
3789       }
3790       /* Variable color and no alpha: Shader Object for the colored
3791          triangle(s) */
3792       else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_1){
3793         gl2ps->streamlength += gl2psPrintf("/Sh%d sh\n", gro->shno);
3794       }
3795       /* Variable color and const alpha < 1: Shader Object for the
3796          colored triangle(s) and an extra extended Graphics State
3797          for the alpha const */
3798       else if(t.prop & T_VAR_COLOR && t.prop & T_ALPHA_LESS_1){
3799         gl2ps->streamlength += gl2psPrintf("q\n"
3800                                            "/GS%d gs\n"
3801                                            "/Sh%d sh\n"
3802                                            "Q\n",
3803                                            gro->gsno, gro->shno);
3804       }
3805       /* Variable alpha and color: Shader Object for the colored
3806          triangle(s) and an extra extended Graphics State
3807          + Xobject + Shader object for the alpha mask */
3808       else if(t.prop & T_VAR_COLOR && t.prop & T_VAR_ALPHA){
3809         gl2ps->streamlength += gl2psPrintf("q\n"
3810                                            "/GS%d gs\n"
3811                                            "/TrG%d Do\n"
3812                                            "/Sh%d sh\n"
3813                                            "Q\n",
3814                                            gro->gsno, gro->trgroupno, gro->shno);
3815       }
3816       break;
3817     case GL2PS_PIXMAP:
3818       for(j = 0; j <= lastel; ++j){
3819         prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
3820         gl2psPutPDFImage(prim->data.image, gro->imno, prim->verts[0].xyz[0],
3821                          prim->verts[0].xyz[1]);
3822       }
3823       break;
3824     case GL2PS_TEXT:
3825       for(j = 0; j <= lastel; ++j){
3826         prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
3827         gl2ps->streamlength += gl2psPrintPDFFillColor(prim->verts[0].rgba);
3828         gl2psPutPDFText(prim->data.text, gro->fontno, prim->verts[0].xyz[0],
3829                         prim->verts[0].xyz[1]);
3830       }
3831       break;
3832     case GL2PS_SPECIAL:
3833       for(j = 0; j <= lastel; ++j){
3834         prim = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
3835         gl2psPutPDFSpecial(prim->data.text);
3836       }
3837     default:
3838       break;
3839     }
3840   }
3841 }
3842 
3843 /* Graphics State names */
3844 
3845 static int gl2psPDFgroupListWriteGStateResources(void)
3846 {
3847   GL2PSpdfgroup *gro;
3848   int offs = 0;
3849   int i;
3850 
3851   offs += fprintf(gl2ps->stream,
3852                   "/ExtGState\n"
3853                   "<<\n"
3854                   "/GSa 7 0 R\n");
3855   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
3856     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
3857     if(gro->gsno >= 0)
3858       offs += fprintf(gl2ps->stream, "/GS%d %d 0 R\n", gro->gsno, gro->gsobjno);
3859   }
3860   offs += fprintf(gl2ps->stream, ">>\n");
3861   return offs;
3862 }
3863 
3864 /* Main Shader names */
3865 
3866 static int gl2psPDFgroupListWriteShaderResources(void)
3867 {
3868   GL2PSpdfgroup *gro;
3869   int offs = 0;
3870   int i;
3871 
3872   offs += fprintf(gl2ps->stream,
3873                   "/Shading\n"
3874                   "<<\n");
3875   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
3876     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
3877     if(gro->shno >= 0)
3878       offs += fprintf(gl2ps->stream, "/Sh%d %d 0 R\n", gro->shno, gro->shobjno);
3879     if(gro->maskshno >= 0)
3880       offs += fprintf(gl2ps->stream, "/TrSh%d %d 0 R\n", gro->maskshno, gro->maskshobjno);
3881   }
3882   offs += fprintf(gl2ps->stream,">>\n");
3883   return offs;
3884 }
3885 
3886 /* Images & Mask Shader XObject names */
3887 
3888 static int gl2psPDFgroupListWriteXObjectResources(void)
3889 {
3890   int i;
3891   GL2PSprimitive *p = NULL;
3892   GL2PSpdfgroup *gro;
3893   int offs = 0;
3894 
3895   offs += fprintf(gl2ps->stream,
3896                   "/XObject\n"
3897                   "<<\n");
3898 
3899   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
3900     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
3901     if(!gl2psListNbr(gro->ptrlist))
3902       continue;
3903     p = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);
3904     switch(p->type){
3905     case GL2PS_PIXMAP:
3906       gro->imobjno = gl2ps->objects_stack++;
3907       if(GL_RGBA == p->data.image->format)  /* reserve one object for image mask */
3908         gl2ps->objects_stack++;
3909       offs += fprintf(gl2ps->stream, "/Im%d %d 0 R\n", gro->imno, gro->imobjno);
3910     case GL2PS_TRIANGLE:
3911       if(gro->trgroupno >=0)
3912         offs += fprintf(gl2ps->stream, "/TrG%d %d 0 R\n", gro->trgroupno, gro->trgroupobjno);
3913       break;
3914     default:
3915       break;
3916     }
3917   }
3918   offs += fprintf(gl2ps->stream,">>\n");
3919   return offs;
3920 }
3921 
3922 /* Font names */
3923 
3924 static int gl2psPDFgroupListWriteFontResources(void)
3925 {
3926   int i;
3927   GL2PSpdfgroup *gro;
3928   int offs = 0;
3929 
3930   offs += fprintf(gl2ps->stream, "/Font\n<<\n");
3931 
3932   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
3933     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
3934     if(gro->fontno < 0)
3935       continue;
3936     gro->fontobjno = gl2ps->objects_stack++;
3937     offs += fprintf(gl2ps->stream, "/F%d %d 0 R\n", gro->fontno, gro->fontobjno);
3938   }
3939   offs += fprintf(gl2ps->stream, ">>\n");
3940 
3941   return offs;
3942 }
3943 
3944 static void gl2psPDFgroupListDelete(void)
3945 {
3946   int i;
3947   GL2PSpdfgroup *gro = NULL;
3948 
3949   if(!gl2ps->pdfgrouplist)
3950     return;
3951 
3952   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
3953     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist,i);
3954     gl2psListDelete(gro->ptrlist);
3955   }
3956 
3957   gl2psListDelete(gl2ps->pdfgrouplist);
3958   gl2ps->pdfgrouplist = NULL;
3959 }
3960 
3961 /* Print 1st PDF object - file info */
3962 
3963 static int gl2psPrintPDFInfo(void)
3964 {
3965   int offs;
3966   time_t now;
3967   struct tm *newtime;
3968 
3969   time(&now);
3970   newtime = gmtime(&now);
3971 
3972   offs = fprintf(gl2ps->stream,
3973                  "1 0 obj\n"
3974                  "<<\n"
3975                  "/Title (%s)\n"
3976                  "/Creator (GL2PS %d.%d.%d%s, %s)\n"
3977                  "/Producer (%s)\n",
3978                  gl2ps->title, GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION,
3979                  GL2PS_PATCH_VERSION, GL2PS_EXTRA_VERSION, GL2PS_COPYRIGHT,
3980                  gl2ps->producer);
3981 
3982   if(!newtime){
3983     offs += fprintf(gl2ps->stream,
3984                     ">>\n"
3985                     "endobj\n");
3986     return offs;
3987   }
3988 
3989   offs += fprintf(gl2ps->stream,
3990                   "/CreationDate (D:%d%02d%02d%02d%02d%02d)\n"
3991                   ">>\n"
3992                   "endobj\n",
3993                   newtime->tm_year+1900,
3994                   newtime->tm_mon+1,
3995                   newtime->tm_mday,
3996                   newtime->tm_hour,
3997                   newtime->tm_min,
3998                   newtime->tm_sec);
3999   return offs;
4000 }
4001 
4002 /* Create catalog and page structure - 2nd and 3th PDF object */
4003 
4004 static int gl2psPrintPDFCatalog(void)
4005 {
4006   return fprintf(gl2ps->stream,
4007                  "2 0 obj\n"
4008                  "<<\n"
4009                  "/Type /Catalog\n"
4010                  "/Pages 3 0 R\n"
4011                  ">>\n"
4012                  "endobj\n");
4013 }
4014 
4015 static int gl2psPrintPDFPages(void)
4016 {
4017   return fprintf(gl2ps->stream,
4018                  "3 0 obj\n"
4019                  "<<\n"
4020                  "/Type /Pages\n"
4021                  "/Kids [6 0 R]\n"
4022                  "/Count 1\n"
4023                  ">>\n"
4024                  "endobj\n");
4025 }
4026 
4027 /* Open stream for data - graphical objects, fonts etc. PDF object 4 */
4028 
4029 static int gl2psOpenPDFDataStream(void)
4030 {
4031   int offs = 0;
4032 
4033   offs += fprintf(gl2ps->stream,
4034                   "4 0 obj\n"
4035                   "<<\n"
4036                   "/Length 5 0 R\n" );
4037   offs += gl2psPrintPDFCompressorType();
4038   offs += fprintf(gl2ps->stream,
4039                   ">>\n"
4040                   "stream\n");
4041   return offs;
4042 }
4043 
4044 /* Stream setup - Graphics state, fill background if allowed */
4045 
4046 static int gl2psOpenPDFDataStreamWritePreface(void)
4047 {
4048   int offs;
4049 
4050   offs = gl2psPrintf("/GSa gs\n");
4051 
4052   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
4053     offs += gl2psPrintPDFFillColor(gl2ps->bgcolor);
4054     offs += gl2psPrintf("%d %d %d %d re\n",
4055                         (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
4056                         (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
4057     offs += gl2psPrintf("f\n");
4058   }
4059   return offs;
4060 }
4061 
4062 /* Use the functions above to create the first part of the PDF*/
4063 
4064 static void gl2psPrintPDFHeader(void)
4065 {
4066   int offs = 0;
4067   gl2ps->pdfprimlist = gl2psListCreate(500, 500, sizeof(GL2PSprimitive*));
4068   gl2psPDFstacksInit();
4069 
4070   gl2ps->xreflist = (int*)gl2psMalloc(sizeof(int) * gl2ps->objects_stack);
4071 
4072 #if defined(GL2PS_HAVE_ZLIB)
4073   if(gl2ps->options & GL2PS_COMPRESS){
4074     gl2psSetupCompress();
4075   }
4076 #endif
4077   gl2ps->xreflist[0] = 0;
4078   offs += fprintf(gl2ps->stream, "%%PDF-1.4\n");
4079   gl2ps->xreflist[1] = offs;
4080 
4081   offs += gl2psPrintPDFInfo();
4082   gl2ps->xreflist[2] = offs;
4083 
4084   offs += gl2psPrintPDFCatalog();
4085   gl2ps->xreflist[3] = offs;
4086 
4087   offs += gl2psPrintPDFPages();
4088   gl2ps->xreflist[4] = offs;
4089 
4090   offs += gl2psOpenPDFDataStream();
4091   gl2ps->xreflist[5] = offs; /* finished in gl2psPrintPDFFooter */
4092   gl2ps->streamlength = gl2psOpenPDFDataStreamWritePreface();
4093 }
4094 
4095 /* The central primitive drawing */
4096 
4097 static void gl2psPrintPDFPrimitive(void *data)
4098 {
4099   GL2PSprimitive *prim = *(GL2PSprimitive**)data;
4100 
4101   if((gl2ps->options & GL2PS_OCCLUSION_CULL) && prim->culled)
4102     return;
4103 
4104   prim = gl2psCopyPrimitive(prim); /* deep copy */
4105   gl2psListAdd(gl2ps->pdfprimlist, &prim);
4106 }
4107 
4108 /* close stream and ... */
4109 
4110 static int gl2psClosePDFDataStream(void)
4111 {
4112   int offs = 0;
4113 
4114 #if defined(GL2PS_HAVE_ZLIB)
4115   if(gl2ps->options & GL2PS_COMPRESS){
4116     if(Z_OK != gl2psDeflate())
4117       gl2psMsg(GL2PS_ERROR, "Zlib deflate error");
4118     else
4119       fwrite(gl2ps->compress->dest, gl2ps->compress->destLen, 1, gl2ps->stream);
4120     gl2ps->streamlength += gl2ps->compress->destLen;
4121 
4122     offs += gl2ps->streamlength;
4123     gl2psFreeCompress();
4124   }
4125 #endif
4126 
4127   offs += fprintf(gl2ps->stream,
4128                   "endstream\n"
4129                   "endobj\n");
4130   return offs;
4131 }
4132 
4133 /* ... write the now known length object */
4134 
4135 static int gl2psPrintPDFDataStreamLength(int val)
4136 {
4137   return fprintf(gl2ps->stream,
4138                  "5 0 obj\n"
4139                  "%d\n"
4140                  "endobj\n", val);
4141 }
4142 
4143 /* Put the info created before in PDF objects */
4144 
4145 static int gl2psPrintPDFOpenPage(void)
4146 {
4147   int offs;
4148 
4149   /* Write fixed part */
4150 
4151   offs = fprintf(gl2ps->stream,
4152                  "6 0 obj\n"
4153                  "<<\n"
4154                  "/Type /Page\n"
4155                  "/Parent 3 0 R\n"
4156                  "/MediaBox [%d %d %d %d]\n",
4157                  (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
4158                  (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
4159 
4160   if(gl2ps->options & GL2PS_LANDSCAPE)
4161     offs += fprintf(gl2ps->stream, "/Rotate -90\n");
4162 
4163   offs += fprintf(gl2ps->stream,
4164                   "/Contents 4 0 R\n"
4165                   "/Resources\n"
4166                   "<<\n"
4167                   "/ProcSet [/PDF /Text /ImageB /ImageC]  %%/ImageI\n");
4168 
4169   return offs;
4170 
4171   /* End fixed part, proceeds in gl2psPDFgroupListWriteVariableResources() */
4172 }
4173 
4174 static int gl2psPDFgroupListWriteVariableResources(void)
4175 {
4176   int offs = 0;
4177 
4178   /* a) Graphics States for shader alpha masks*/
4179   offs += gl2psPDFgroupListWriteGStateResources();
4180 
4181   /* b) Shader and shader masks */
4182   offs += gl2psPDFgroupListWriteShaderResources();
4183 
4184   /* c) XObjects (Images & Shader Masks) */
4185   offs += gl2psPDFgroupListWriteXObjectResources();
4186 
4187   /* d) Fonts */
4188   offs += gl2psPDFgroupListWriteFontResources();
4189 
4190   /* End resources and page */
4191   offs += fprintf(gl2ps->stream,
4192                   ">>\n"
4193                   ">>\n"
4194                   "endobj\n");
4195   return offs;
4196 }
4197 
4198 /* Standard Graphics State */
4199 
4200 static int gl2psPrintPDFGSObject(void)
4201 {
4202   return fprintf(gl2ps->stream,
4203                  "7 0 obj\n"
4204                  "<<\n"
4205                  "/Type /ExtGState\n"
4206                  "/SA false\n"
4207                  "/SM 0.02\n"
4208                  "/OP false\n"
4209                  "/op false\n"
4210                  "/OPM 0\n"
4211                  "/BG2 /Default\n"
4212                  "/UCR2 /Default\n"
4213                  "/TR2 /Default\n"
4214                  ">>\n"
4215                  "endobj\n");
4216 }
4217 
4218 /* Put vertex' edge flag (8bit) and coordinates (32bit) in shader stream */
4219 
4220 static int gl2psPrintPDFShaderStreamDataCoord(GL2PSvertex *vertex,
4221                                               int (*action)(unsigned long data, int size),
4222                                               GLfloat dx, GLfloat dy,
4223                                               GLfloat xmin, GLfloat ymin)
4224 {
4225   int offs = 0;
4226   unsigned long imap;
4227   GLfloat diff;
4228   double dmax = ~1UL;
4229   char edgeflag = 0;
4230 
4231   /* FIXME: temp bux fix for 64 bit archs: */
4232   if(sizeof(unsigned long) == 8) dmax = dmax - 2048.;
4233 
4234   offs += (*action)(edgeflag, 1);
4235 
4236   /* The Shader stream in PDF requires to be in a 'big-endian'
4237      order */
4238 
4239   if(GL2PS_ZERO(dx * dy)){
4240     offs += (*action)(0, 4);
4241     offs += (*action)(0, 4);
4242   }
4243   else{
4244     diff = (vertex->xyz[0] - xmin) / dx;
4245     if(diff > 1)
4246       diff = 1.0F;
4247     else if(diff < 0)
4248       diff = 0.0F;
4249     imap = (unsigned long)(diff * dmax);
4250     offs += (*action)(imap, 4);
4251 
4252     diff = (vertex->xyz[1] - ymin) / dy;
4253     if(diff > 1)
4254       diff = 1.0F;
4255     else if(diff < 0)
4256       diff = 0.0F;
4257     imap = (unsigned long)(diff * dmax);
4258     offs += (*action)(imap, 4);
4259   }
4260 
4261   return offs;
4262 }
4263 
4264 /* Put vertex' rgb value (8bit for every component) in shader stream */
4265 
4266 static int gl2psPrintPDFShaderStreamDataRGB(GL2PSvertex *vertex,
4267                                             int (*action)(unsigned long data, int size))
4268 {
4269   int offs = 0;
4270   unsigned long imap;
4271   double dmax = ~1UL;
4272 
4273   /* FIXME: temp bux fix for 64 bit archs: */
4274   if(sizeof(unsigned long) == 8) dmax = dmax - 2048.;
4275 
4276   imap = (unsigned long)((vertex->rgba[0]) * dmax);
4277   offs += (*action)(imap, 1);
4278 
4279   imap = (unsigned long)((vertex->rgba[1]) * dmax);
4280   offs += (*action)(imap, 1);
4281 
4282   imap = (unsigned long)((vertex->rgba[2]) * dmax);
4283   offs += (*action)(imap, 1);
4284 
4285   return offs;
4286 }
4287 
4288 /* Put vertex' alpha (8/16bit) in shader stream */
4289 
4290 static int gl2psPrintPDFShaderStreamDataAlpha(GL2PSvertex *vertex,
4291                                               int (*action)(unsigned long data, int size),
4292                                               int sigbyte)
4293 {
4294   int offs = 0;
4295   unsigned long imap;
4296   double dmax = ~1UL;
4297 
4298   /* FIXME: temp bux fix for 64 bit archs: */
4299   if(sizeof(unsigned long) == 8) dmax = dmax - 2048.;
4300 
4301   if(sigbyte != 8 && sigbyte != 16)
4302     sigbyte = 8;
4303 
4304   sigbyte /= 8;
4305 
4306   imap = (unsigned long)((vertex->rgba[3]) * dmax);
4307 
4308   offs += (*action)(imap, sigbyte);
4309 
4310   return offs;
4311 }
4312 
4313 /* Put a triangles raw data in shader stream */
4314 
4315 static int gl2psPrintPDFShaderStreamData(GL2PStriangle *triangle,
4316                                          GLfloat dx, GLfloat dy,
4317                                          GLfloat xmin, GLfloat ymin,
4318                                          int (*action)(unsigned long data, int size),
4319                                          int gray)
4320 {
4321   int i, offs = 0;
4322   GL2PSvertex v;
4323 
4324   if(gray && gray != 8 && gray != 16)
4325     gray = 8;
4326 
4327   for(i = 0; i < 3; ++i){
4328     offs += gl2psPrintPDFShaderStreamDataCoord(&triangle->vertex[i], action,
4329                                                dx, dy, xmin, ymin);
4330     if(gray){
4331       v = triangle->vertex[i];
4332       offs += gl2psPrintPDFShaderStreamDataAlpha(&v, action, gray);
4333     }
4334     else{
4335       offs += gl2psPrintPDFShaderStreamDataRGB(&triangle->vertex[i], action);
4336     }
4337   }
4338 
4339   return offs;
4340 }
4341 
4342 static void gl2psPDFRectHull(GLfloat *xmin, GLfloat *xmax,
4343                              GLfloat *ymin, GLfloat *ymax,
4344                              GL2PStriangle *triangles, int cnt)
4345 {
4346   int i, j;
4347 
4348   *xmin = triangles[0].vertex[0].xyz[0];
4349   *xmax = triangles[0].vertex[0].xyz[0];
4350   *ymin = triangles[0].vertex[0].xyz[1];
4351   *ymax = triangles[0].vertex[0].xyz[1];
4352 
4353   for(i = 0; i < cnt; ++i){
4354     for(j = 0; j < 3; ++j){
4355       if(*xmin > triangles[i].vertex[j].xyz[0])
4356         *xmin = triangles[i].vertex[j].xyz[0];
4357       if(*xmax < triangles[i].vertex[j].xyz[0])
4358         *xmax = triangles[i].vertex[j].xyz[0];
4359       if(*ymin > triangles[i].vertex[j].xyz[1])
4360         *ymin = triangles[i].vertex[j].xyz[1];
4361       if(*ymax < triangles[i].vertex[j].xyz[1])
4362         *ymax = triangles[i].vertex[j].xyz[1];
4363     }
4364   }
4365 }
4366 
4367 /* Writes shaded triangle
4368    gray == 0 means write RGB triangles
4369    gray == 8             8bit-grayscale (for alpha masks)
4370    gray == 16            16bit-grayscale (for alpha masks) */
4371 
4372 static int gl2psPrintPDFShader(int obj, GL2PStriangle *triangles,
4373                                int size, int gray)
4374 {
4375   int i, offs = 0, vertexbytes, done = 0;
4376   GLfloat xmin, xmax, ymin, ymax;
4377 
4378   switch(gray){
4379   case 0:
4380     vertexbytes = 1+4+4+1+1+1;
4381     break;
4382   case 8:
4383     vertexbytes = 1+4+4+1;
4384     break;
4385   case 16:
4386     vertexbytes = 1+4+4+2;
4387     break;
4388   default:
4389     gray = 8;
4390     vertexbytes = 1+4+4+1;
4391     break;
4392   }
4393 
4394   gl2psPDFRectHull(&xmin, &xmax, &ymin, &ymax, triangles, size);
4395 
4396   offs += fprintf(gl2ps->stream,
4397                   "%d 0 obj\n"
4398                   "<< "
4399                   "/ShadingType 4 "
4400                   "/ColorSpace %s "
4401                   "/BitsPerCoordinate 32 "
4402                   "/BitsPerComponent %d "
4403                   "/BitsPerFlag 8 "
4404                   "/Decode [%f %f %f %f 0 1 %s] ",
4405                   obj,
4406                   (gray) ? "/DeviceGray" : "/DeviceRGB",
4407                   (gray) ? gray : 8,
4408                   xmin, xmax, ymin, ymax,
4409                   (gray) ? "" : "0 1 0 1");
4410 
4411 #if defined(GL2PS_HAVE_ZLIB)
4412   if(gl2ps->options & GL2PS_COMPRESS){
4413     gl2psAllocCompress(vertexbytes * size * 3);
4414 
4415     for(i = 0; i < size; ++i)
4416       gl2psPrintPDFShaderStreamData(&triangles[i],
4417                                     xmax-xmin, ymax-ymin, xmin, ymin,
4418                                     gl2psWriteBigEndianCompress, gray);
4419 
4420     if(Z_OK == gl2psDeflate() && 23 + gl2ps->compress->destLen < gl2ps->compress->srcLen){
4421       offs += gl2psPrintPDFCompressorType();
4422       offs += fprintf(gl2ps->stream,
4423                       "/Length %d "
4424                       ">>\n"
4425                       "stream\n",
4426                       (int)gl2ps->compress->destLen);
4427       offs += gl2ps->compress->destLen * fwrite(gl2ps->compress->dest,
4428                                                 gl2ps->compress->destLen,
4429                                                 1, gl2ps->stream);
4430       done = 1;
4431     }
4432     gl2psFreeCompress();
4433   }
4434 #endif
4435 
4436   if(!done){
4437     /* no compression, or too long after compression, or compress error
4438        -> write non-compressed entry */
4439     offs += fprintf(gl2ps->stream,
4440                     "/Length %d "
4441                     ">>\n"
4442                     "stream\n",
4443                     vertexbytes * 3 * size);
4444     for(i = 0; i < size; ++i)
4445       offs += gl2psPrintPDFShaderStreamData(&triangles[i],
4446                                             xmax-xmin, ymax-ymin, xmin, ymin,
4447                                             gl2psWriteBigEndian, gray);
4448   }
4449 
4450   offs += fprintf(gl2ps->stream,
4451                   "\nendstream\n"
4452                   "endobj\n");
4453 
4454   return offs;
4455 }
4456 
4457 /* Writes a XObject for a shaded triangle mask */
4458 
4459 static int gl2psPrintPDFShaderMask(int obj, int childobj)
4460 {
4461   int offs = 0, len;
4462 
4463   offs += fprintf(gl2ps->stream,
4464                   "%d 0 obj\n"
4465                   "<<\n"
4466                   "/Type /XObject\n"
4467                   "/Subtype /Form\n"
4468                   "/BBox [ %d %d %d %d ]\n"
4469                   "/Group \n<<\n/S /Transparency /CS /DeviceRGB\n"
4470                   ">>\n",
4471                   obj,
4472                   (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
4473                   (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
4474 
4475   len = (childobj>0)
4476     ? strlen("/TrSh sh\n") + (int)log10((double)childobj)+1
4477     : strlen("/TrSh0 sh\n");
4478 
4479   offs += fprintf(gl2ps->stream,
4480                   "/Length %d\n"
4481                   ">>\n"
4482                   "stream\n",
4483                   len);
4484   offs += fprintf(gl2ps->stream,
4485                   "/TrSh%d sh\n",
4486                   childobj);
4487   offs += fprintf(gl2ps->stream,
4488                   "endstream\n"
4489                   "endobj\n");
4490 
4491   return offs;
4492 }
4493 
4494 /* Writes a Extended graphics state for a shaded triangle mask if
4495    simplealpha ist true the childobj argument is ignored and a /ca
4496    statement will be written instead */
4497 
4498 static int gl2psPrintPDFShaderExtGS(int obj, int childobj)
4499 {
4500   int offs = 0;
4501 
4502   offs += fprintf(gl2ps->stream,
4503                   "%d 0 obj\n"
4504                   "<<\n",
4505                   obj);
4506 
4507   offs += fprintf(gl2ps->stream,
4508                   "/SMask << /S /Alpha /G %d 0 R >> ",
4509                   childobj);
4510 
4511   offs += fprintf(gl2ps->stream,
4512                   ">>\n"
4513                   "endobj\n");
4514   return offs;
4515 }
4516 
4517 /* a simple graphics state */
4518 
4519 static int gl2psPrintPDFShaderSimpleExtGS(int obj, GLfloat alpha)
4520 {
4521   int offs = 0;
4522 
4523   offs += fprintf(gl2ps->stream,
4524                   "%d 0 obj\n"
4525                   "<<\n"
4526                   "/ca %g"
4527                   ">>\n"
4528                   "endobj\n",
4529                   obj, alpha);
4530   return offs;
4531 }
4532 
4533 /* Similar groups of functions for pixmaps and text */
4534 
4535 static int gl2psPrintPDFPixmapStreamData(GL2PSimage *im,
4536                                          int (*action)(unsigned long data, int size),
4537                                          int gray)
4538 {
4539   int x, y, shift;
4540   GLfloat r, g, b, a;
4541 
4542   if(im->format != GL_RGBA && gray)
4543     return 0;
4544 
4545   if(gray && gray != 8 && gray != 16)
4546     gray = 8;
4547 
4548   gray /= 8;
4549 
4550   shift = (sizeof(unsigned long) - 1) * 8;
4551 
4552   for(y = 0; y < im->height; ++y){
4553     for(x = 0; x < im->width; ++x){
4554       a = gl2psGetRGB(im, x, y, &r, &g, &b);
4555       if(im->format == GL_RGBA && gray){
4556         (*action)((unsigned long)(a * 255) << shift, gray);
4557       }
4558       else{
4559         (*action)((unsigned long)(r * 255) << shift, 1);
4560         (*action)((unsigned long)(g * 255) << shift, 1);
4561         (*action)((unsigned long)(b * 255) << shift, 1);
4562       }
4563     }
4564   }
4565 
4566   switch(gray){
4567   case 0: return 3 * im->width * im->height;
4568   case 1: return im->width * im->height;
4569   case 2: return 2 * im->width * im->height;
4570   default: return 3 * im->width * im->height;
4571   }
4572 }
4573 
4574 static int gl2psPrintPDFPixmap(int obj, int childobj, GL2PSimage *im, int gray)
4575 {
4576   int offs = 0, done = 0, sigbytes = 3;
4577 
4578   if(gray && gray !=8 && gray != 16)
4579     gray = 8;
4580 
4581   if(gray)
4582     sigbytes = gray / 8;
4583 
4584   offs += fprintf(gl2ps->stream,
4585                   "%d 0 obj\n"
4586                   "<<\n"
4587                   "/Type /XObject\n"
4588                   "/Subtype /Image\n"
4589                   "/Width %d\n"
4590                   "/Height %d\n"
4591                   "/ColorSpace %s \n"
4592                   "/BitsPerComponent 8\n",
4593                   obj,
4594                   (int)im->width, (int)im->height,
4595                   (gray) ? "/DeviceGray" : "/DeviceRGB" );
4596   if(GL_RGBA == im->format && gray == 0){
4597     offs += fprintf(gl2ps->stream,
4598                     "/SMask %d 0 R\n",
4599                     childobj);
4600   }
4601 
4602 #if defined(GL2PS_HAVE_ZLIB)
4603   if(gl2ps->options & GL2PS_COMPRESS){
4604     gl2psAllocCompress((int)(im->width * im->height * sigbytes));
4605 
4606     gl2psPrintPDFPixmapStreamData(im, gl2psWriteBigEndianCompress, gray);
4607 
4608     if(Z_OK == gl2psDeflate() && 23 + gl2ps->compress->destLen < gl2ps->compress->srcLen){
4609       offs += gl2psPrintPDFCompressorType();
4610       offs += fprintf(gl2ps->stream,
4611                       "/Length %d "
4612                       ">>\n"
4613                       "stream\n",
4614                       (int)gl2ps->compress->destLen);
4615       offs += gl2ps->compress->destLen * fwrite(gl2ps->compress->dest, gl2ps->compress->destLen,
4616                                                 1, gl2ps->stream);
4617       done = 1;
4618     }
4619     gl2psFreeCompress();
4620   }
4621 #endif
4622 
4623   if(!done){
4624     /* no compression, or too long after compression, or compress error
4625        -> write non-compressed entry */
4626     offs += fprintf(gl2ps->stream,
4627                     "/Length %d "
4628                     ">>\n"
4629                     "stream\n",
4630                     (int)(im->width * im->height * sigbytes));
4631     offs += gl2psPrintPDFPixmapStreamData(im, gl2psWriteBigEndian, gray);
4632   }
4633 
4634   offs += fprintf(gl2ps->stream,
4635                   "\nendstream\n"
4636                   "endobj\n");
4637 
4638   return offs;
4639 }
4640 
4641 static int gl2psPrintPDFText(int obj, GL2PSstring *s, int fontnumber)
4642 {
4643   int offs = 0;
4644 
4645   offs += fprintf(gl2ps->stream,
4646                   "%d 0 obj\n"
4647                   "<<\n"
4648                   "/Type /Font\n"
4649                   "/Subtype /Type1\n"
4650                   "/Name /F%d\n"
4651                   "/BaseFont /%s\n"
4652                   "/Encoding /MacRomanEncoding\n"
4653                   ">>\n"
4654                   "endobj\n",
4655                   obj, fontnumber, s->fontname);
4656   return offs;
4657 }
4658 
4659 /* Write the physical objects */
4660 
4661 static int gl2psPDFgroupListWriteObjects(int entryoffs)
4662 {
4663   int i,j;
4664   GL2PSprimitive *p = NULL;
4665   GL2PSpdfgroup *gro;
4666   int offs = entryoffs;
4667   GL2PStriangle *triangles;
4668   int size = 0;
4669 
4670   if(!gl2ps->pdfgrouplist)
4671     return offs;
4672 
4673   for(i = 0; i < gl2psListNbr(gl2ps->pdfgrouplist); ++i){
4674     gro = (GL2PSpdfgroup*)gl2psListPointer(gl2ps->pdfgrouplist, i);
4675     if(!gl2psListNbr(gro->ptrlist))
4676       continue;
4677     p = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, 0);
4678     switch(p->type){
4679     case GL2PS_POINT:
4680       break;
4681     case GL2PS_LINE:
4682       break;
4683     case GL2PS_TRIANGLE:
4684       size = gl2psListNbr(gro->ptrlist);
4685       triangles = (GL2PStriangle*)gl2psMalloc(sizeof(GL2PStriangle) * size);
4686       for(j = 0; j < size; ++j){
4687         p = *(GL2PSprimitive**)gl2psListPointer(gro->ptrlist, j);
4688         gl2psFillTriangleFromPrimitive(&triangles[j], p, GL_TRUE);
4689       }
4690       if(triangles[0].prop & T_VAR_COLOR){
4691         gl2ps->xreflist[gro->shobjno] = offs;
4692         offs += gl2psPrintPDFShader(gro->shobjno, triangles, size, 0);
4693       }
4694       if(triangles[0].prop & T_ALPHA_LESS_1){
4695         gl2ps->xreflist[gro->gsobjno] = offs;
4696         offs += gl2psPrintPDFShaderSimpleExtGS(gro->gsobjno, triangles[0].vertex[0].rgba[3]);
4697       }
4698       if(triangles[0].prop & T_VAR_ALPHA){
4699         gl2ps->xreflist[gro->gsobjno] = offs;
4700         offs += gl2psPrintPDFShaderExtGS(gro->gsobjno, gro->trgroupobjno);
4701         gl2ps->xreflist[gro->trgroupobjno] = offs;
4702         offs += gl2psPrintPDFShaderMask(gro->trgroupobjno, gro->maskshno);
4703         gl2ps->xreflist[gro->maskshobjno] = offs;
4704         offs += gl2psPrintPDFShader(gro->maskshobjno, triangles, size, 8);
4705       }
4706       gl2psFree(triangles);
4707       break;
4708     case GL2PS_PIXMAP:
4709       gl2ps->xreflist[gro->imobjno] = offs;
4710       offs += gl2psPrintPDFPixmap(gro->imobjno, gro->imobjno+1, p->data.image, 0);
4711       if(p->data.image->format == GL_RGBA){
4712         gl2ps->xreflist[gro->imobjno+1] = offs;
4713         offs += gl2psPrintPDFPixmap(gro->imobjno+1, -1, p->data.image, 8);
4714       }
4715       break;
4716     case GL2PS_TEXT:
4717       gl2ps->xreflist[gro->fontobjno] = offs;
4718       offs += gl2psPrintPDFText(gro->fontobjno,p->data.text,gro->fontno);
4719       break;
4720     case GL2PS_SPECIAL :
4721       /* alignment contains the format for which the special output text
4722          is intended */
4723       if(p->data.text->alignment == GL2PS_PDF)
4724         offs += fprintf(gl2ps->stream, "%s\n", p->data.text->str);
4725       break;
4726     default:
4727       break;
4728     }
4729   }
4730   return offs;
4731 }
4732 
4733 /* All variable data has been written at this point and all required
4734    functioninality has been gathered, so we can write now file footer
4735    with cross reference table and trailer */
4736 
4737 static void gl2psPrintPDFFooter(void)
4738 {
4739   int i, offs;
4740 
4741   gl2psPDFgroupListInit();
4742   gl2psPDFgroupListWriteMainStream();
4743 
4744   offs = gl2ps->xreflist[5] + gl2ps->streamlength;
4745   offs += gl2psClosePDFDataStream();
4746   gl2ps->xreflist[5] = offs;
4747 
4748   offs += gl2psPrintPDFDataStreamLength(gl2ps->streamlength);
4749   gl2ps->xreflist[6] = offs;
4750   gl2ps->streamlength = 0;
4751 
4752   offs += gl2psPrintPDFOpenPage();
4753   offs += gl2psPDFgroupListWriteVariableResources();
4754   gl2ps->xreflist = (int*)gl2psRealloc(gl2ps->xreflist,
4755                                        sizeof(int) * (gl2ps->objects_stack + 1));
4756   gl2ps->xreflist[7] = offs;
4757 
4758   offs += gl2psPrintPDFGSObject();
4759   gl2ps->xreflist[8] = offs;
4760 
4761   gl2ps->xreflist[gl2ps->objects_stack] =
4762     gl2psPDFgroupListWriteObjects(gl2ps->xreflist[8]);
4763 
4764   /* Start cross reference table. The file has to been opened in
4765      binary mode to preserve the 20 digit string length! */
4766   fprintf(gl2ps->stream,
4767           "xref\n"
4768           "0 %d\n"
4769           "%010d 65535 f \n", gl2ps->objects_stack, 0);
4770 
4771   for(i = 1; i < gl2ps->objects_stack; ++i)
4772     fprintf(gl2ps->stream, "%010d 00000 n \n", gl2ps->xreflist[i]);
4773 
4774   fprintf(gl2ps->stream,
4775           "trailer\n"
4776           "<<\n"
4777           "/Size %d\n"
4778           "/Info 1 0 R\n"
4779           "/Root 2 0 R\n"
4780           ">>\n"
4781           "startxref\n%d\n"
4782           "%%%%EOF\n",
4783           gl2ps->objects_stack, gl2ps->xreflist[gl2ps->objects_stack]);
4784 
4785   /* Free auxiliary lists and arrays */
4786   gl2psFree(gl2ps->xreflist);
4787   gl2psListAction(gl2ps->pdfprimlist, gl2psFreePrimitive);
4788   gl2psListDelete(gl2ps->pdfprimlist);
4789   gl2psPDFgroupListDelete();
4790 
4791 #if defined(GL2PS_HAVE_ZLIB)
4792   if(gl2ps->options & GL2PS_COMPRESS){
4793     gl2psFreeCompress();
4794     gl2psFree(gl2ps->compress);
4795     gl2ps->compress = NULL;
4796   }
4797 #endif
4798 }
4799 
4800 /* PDF begin viewport */
4801 
4802 static void gl2psPrintPDFBeginViewport(GLint viewport[4])
4803 {
4804   int offs = 0;
4805   GLint idx;
4806   GLfloat rgba[4];
4807   int x = viewport[0], y = viewport[1], w = viewport[2], h = viewport[3];
4808 
4809   glRenderMode(GL_FEEDBACK);
4810 
4811   if(gl2ps->header){
4812     gl2psPrintPDFHeader();
4813     gl2ps->header = GL_FALSE;
4814   }
4815 
4816   offs += gl2psPrintf("q\n");
4817 
4818   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
4819     if(gl2ps->colormode == GL_RGBA || gl2ps->colorsize == 0){
4820       glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba);
4821     }
4822     else{
4823       glGetIntegerv(GL_INDEX_CLEAR_VALUE, &idx);
4824       rgba[0] = gl2ps->colormap[idx][0];
4825       rgba[1] = gl2ps->colormap[idx][1];
4826       rgba[2] = gl2ps->colormap[idx][2];
4827       rgba[3] = 1.0F;
4828     }
4829     offs += gl2psPrintPDFFillColor(rgba);
4830     offs += gl2psPrintf("%d %d %d %d re\n"
4831                         "W\n"
4832                         "f\n",
4833                         x, y, w, h);
4834   }
4835   else{
4836     offs += gl2psPrintf("%d %d %d %d re\n"
4837                         "W\n"
4838                         "n\n",
4839                         x, y, w, h);
4840   }
4841 
4842   gl2ps->streamlength += offs;
4843 }
4844 
4845 static GLint gl2psPrintPDFEndViewport(void)
4846 {
4847   GLint res;
4848 
4849   res = gl2psPrintPrimitives();
4850   gl2ps->streamlength += gl2psPrintf("Q\n");
4851   return res;
4852 }
4853 
4854 static void gl2psPrintPDFFinalPrimitive(void)
4855 {
4856 }
4857 
4858 /* definition of the PDF backend */
4859 
4860 static GL2PSbackend gl2psPDF = {
4861   gl2psPrintPDFHeader,
4862   gl2psPrintPDFFooter,
4863   gl2psPrintPDFBeginViewport,
4864   gl2psPrintPDFEndViewport,
4865   gl2psPrintPDFPrimitive,
4866   gl2psPrintPDFFinalPrimitive,
4867   "pdf",
4868   "Portable Document Format"
4869 };
4870 
4871 /*********************************************************************
4872  *
4873  * SVG routines
4874  *
4875  *********************************************************************/
4876 
4877 static void gl2psSVGGetCoordsAndColors(int n, GL2PSvertex *verts,
4878                                        GL2PSxyz *xyz, GL2PSrgba *rgba)
4879 {
4880   int i, j;
4881 
4882   for(i = 0; i < n; i++){
4883     xyz[i][0] = verts[i].xyz[0];
4884     xyz[i][1] = gl2ps->viewport[3] - verts[i].xyz[1];
4885     xyz[i][2] = 0.0F;
4886     for(j = 0; j < 4; j++)
4887       rgba[i][j] = verts[i].rgba[j];
4888   }
4889 }
4890 
4891 static void gl2psSVGGetColorString(GL2PSrgba rgba, char str[32])
4892 {
4893   int r = (int)(255. * rgba[0]);
4894   int g = (int)(255. * rgba[1]);
4895   int b = (int)(255. * rgba[2]);
4896   int rc = (r < 0) ? 0 : (r > 255) ? 255 : r;
4897   int gc = (g < 0) ? 0 : (g > 255) ? 255 : g;
4898   int bc = (b < 0) ? 0 : (b > 255) ? 255 : b;
4899   sprintf(str, "#%2.2x%2.2x%2.2x", rc, gc, bc);
4900 }
4901 
4902 static void gl2psPrintSVGHeader(void)
4903 {
4904   int x, y, width, height;
4905   char col[32];
4906   time_t now;
4907 
4908   time(&now);
4909 
4910   if (gl2ps->options & GL2PS_LANDSCAPE){
4911     x = (int)gl2ps->viewport[1];
4912     y = (int)gl2ps->viewport[0];
4913     width = (int)gl2ps->viewport[3];
4914     height = (int)gl2ps->viewport[2];
4915   }
4916   else{
4917     x = (int)gl2ps->viewport[0];
4918     y = (int)gl2ps->viewport[1];
4919     width = (int)gl2ps->viewport[2];
4920     height = (int)gl2ps->viewport[3];
4921   }
4922 
4923   /* Compressed SVG files (.svgz) are simply gzipped SVG files */
4924   gl2psPrintGzipHeader();
4925 
4926   gl2psPrintf("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
4927   gl2psPrintf("<svg xmlns=\"http://www.w3.org/2000/svg\"\n");
4928   gl2psPrintf("     xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
4929               "     width=\"%dpx\" height=\"%dpx\" viewBox=\"%d %d %d %d\">\n",
4930               width, height, x, y, width, height);
4931   gl2psPrintf("<title>%s</title>\n", gl2ps->title);
4932   gl2psPrintf("<desc>\n");
4933   gl2psPrintf("Creator: GL2PS %d.%d.%d%s, %s\n"
4934               "For: %s\n"
4935               "CreationDate: %s",
4936               GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION, GL2PS_PATCH_VERSION,
4937               GL2PS_EXTRA_VERSION, GL2PS_COPYRIGHT, gl2ps->producer, ctime(&now));
4938   gl2psPrintf("</desc>\n");
4939   gl2psPrintf("<defs>\n");
4940   gl2psPrintf("</defs>\n");
4941 
4942   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
4943     gl2psSVGGetColorString(gl2ps->bgcolor, col);
4944     gl2psPrintf("<polygon fill=\"%s\" points=\"%d,%d %d,%d %d,%d %d,%d\"/>\n", col,
4945                 (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
4946                 (int)gl2ps->viewport[2], (int)gl2ps->viewport[1],
4947                 (int)gl2ps->viewport[2], (int)gl2ps->viewport[3],
4948                 (int)gl2ps->viewport[0], (int)gl2ps->viewport[3]);
4949   }
4950 
4951   /* group all the primitives and disable antialiasing */
4952   gl2psPrintf("<g shape-rendering=\"crispEdges\">\n");
4953 }
4954 
4955 static void gl2psPrintSVGSmoothTriangle(GL2PSxyz xyz[3], GL2PSrgba rgba[3])
4956 {
4957   int i;
4958   GL2PSxyz xyz2[3];
4959   GL2PSrgba rgba2[3];
4960   char col[32];
4961 
4962   /* Apparently there is no easy way to do Gouraud shading in SVG
4963      without explicitly pre-defining gradients, so for now we just do
4964      recursive subdivision */
4965 
4966   if(gl2psSameColorThreshold(3, rgba, gl2ps->threshold)){
4967     gl2psSVGGetColorString(rgba[0], col);
4968     gl2psPrintf("<polygon fill=\"%s\" ", col);
4969     if(rgba[0][3] < 1.0F) gl2psPrintf("fill-opacity=\"%g\" ", rgba[0][3]);
4970     gl2psPrintf("points=\"%g,%g %g,%g %g,%g\"/>\n", xyz[0][0], xyz[0][1],
4971                 xyz[1][0], xyz[1][1], xyz[2][0], xyz[2][1]);
4972   }
4973   else{
4974     /* subdivide into 4 subtriangles */
4975     for(i = 0; i < 3; i++){
4976       xyz2[0][i] = xyz[0][i];
4977       xyz2[1][i] = 0.5F * (xyz[0][i] + xyz[1][i]);
4978       xyz2[2][i] = 0.5F * (xyz[0][i] + xyz[2][i]);
4979     }
4980     for(i = 0; i < 4; i++){
4981       rgba2[0][i] = rgba[0][i];
4982       rgba2[1][i] = 0.5F * (rgba[0][i] + rgba[1][i]);
4983       rgba2[2][i] = 0.5F * (rgba[0][i] + rgba[2][i]);
4984     }
4985     gl2psPrintSVGSmoothTriangle(xyz2, rgba2);
4986     for(i = 0; i < 3; i++){
4987       xyz2[0][i] = 0.5F * (xyz[0][i] + xyz[1][i]);
4988       xyz2[1][i] = xyz[1][i];
4989       xyz2[2][i] = 0.5F * (xyz[1][i] + xyz[2][i]);
4990     }
4991     for(i = 0; i < 4; i++){
4992       rgba2[0][i] = 0.5F * (rgba[0][i] + rgba[1][i]);
4993       rgba2[1][i] = rgba[1][i];
4994       rgba2[2][i] = 0.5F * (rgba[1][i] + rgba[2][i]);
4995     }
4996     gl2psPrintSVGSmoothTriangle(xyz2, rgba2);
4997     for(i = 0; i < 3; i++){
4998       xyz2[0][i] = 0.5F * (xyz[0][i] + xyz[2][i]);
4999       xyz2[1][i] = xyz[2][i];
5000       xyz2[2][i] = 0.5F * (xyz[1][i] + xyz[2][i]);
5001     }
5002     for(i = 0; i < 4; i++){
5003       rgba2[0][i] = 0.5F * (rgba[0][i] + rgba[2][i]);
5004       rgba2[1][i] = rgba[2][i];
5005       rgba2[2][i] = 0.5F * (rgba[1][i] + rgba[2][i]);
5006     }
5007     gl2psPrintSVGSmoothTriangle(xyz2, rgba2);
5008     for(i = 0; i < 3; i++){
5009       xyz2[0][i] = 0.5F * (xyz[0][i] + xyz[1][i]);
5010       xyz2[1][i] = 0.5F * (xyz[1][i] + xyz[2][i]);
5011       xyz2[2][i] = 0.5F * (xyz[0][i] + xyz[2][i]);
5012     }
5013     for(i = 0; i < 4; i++){
5014       rgba2[0][i] = 0.5F * (rgba[0][i] + rgba[1][i]);
5015       rgba2[1][i] = 0.5F * (rgba[1][i] + rgba[2][i]);
5016       rgba2[2][i] = 0.5F * (rgba[0][i] + rgba[2][i]);
5017     }
5018     gl2psPrintSVGSmoothTriangle(xyz2, rgba2);
5019   }
5020 }
5021 
5022 static void gl2psPrintSVGDash(GLushort pattern, GLint factor)
5023 {
5024   int i, n, array[10];
5025 
5026   if(!pattern || !factor) return; /* solid line */
5027 
5028   gl2psParseStipplePattern(pattern, factor, &n, array);
5029   gl2psPrintf("stroke-dasharray=\"");
5030   for(i = 0; i < n; i++){
5031     if(i) gl2psPrintf(",");
5032     gl2psPrintf("%d", array[i]);
5033   }
5034   gl2psPrintf("\" ");
5035 }
5036 
5037 static void gl2psEndSVGLine(void)
5038 {
5039   int i;
5040   if(gl2ps->lastvertex.rgba[0] >= 0.){
5041     gl2psPrintf("%g,%g\"/>\n", gl2ps->lastvertex.xyz[0],
5042                 gl2ps->viewport[3] - gl2ps->lastvertex.xyz[1]);
5043     for(i = 0; i < 3; i++)
5044       gl2ps->lastvertex.xyz[i] = -1.;
5045     for(i = 0; i < 4; i++)
5046       gl2ps->lastvertex.rgba[i] = -1.;
5047   }
5048 }
5049 
5050 static void gl2psPrintSVGPixmap(GLfloat x, GLfloat y, GL2PSimage *pixmap)
5051 {
5052 #if defined(GL2PS_HAVE_LIBPNG)
5053   GL2PSlist *png;
5054   unsigned char c;
5055   int i;
5056 
5057   /* The only image types supported by the SVG standard are JPEG, PNG
5058      and SVG. Here we choose PNG, and since we want to embed the image
5059      directly in the SVG stream (and not link to an external image
5060      file), we need to encode the pixmap into PNG in memory, then
5061      encode it into base64. */
5062 
5063   png = gl2psListCreate(pixmap->width * pixmap->height * 3, 1000,
5064                         sizeof(unsigned char));
5065   gl2psConvertPixmapToPNG(pixmap, png);
5066   gl2psListEncodeBase64(png);
5067   gl2psPrintf("<image x=\"%g\" y=\"%g\" width=\"%d\" height=\"%d\"\n",
5068               x, y - pixmap->height, pixmap->width, pixmap->height);
5069   gl2psPrintf("xlink:href=\"data:image/png;base64,");
5070   for(i = 0; i < gl2psListNbr(png); i++){
5071     gl2psListRead(png, i, &c);
5072     gl2psPrintf("%c", c);
5073   }
5074   gl2psPrintf("\"/>\n");
5075   gl2psListDelete(png);
5076 #else
5077   (void) x; (void) y; (void) pixmap;  /* not used */
5078   gl2psMsg(GL2PS_WARNING, "GL2PS must be compiled with PNG support in "
5079            "order to embed images in SVG streams");
5080 #endif
5081 }
5082 
5083 static void gl2psPrintSVGPrimitive(void *data)
5084 {
5085   GL2PSprimitive *prim;
5086   GL2PSxyz xyz[4];
5087   GL2PSrgba rgba[4];
5088   char col[32];
5089   int newline;
5090 
5091   prim = *(GL2PSprimitive**)data;
5092 
5093   if((gl2ps->options & GL2PS_OCCLUSION_CULL) && prim->culled) return;
5094 
5095   /* We try to draw connected lines as a single path to get nice line
5096      joins and correct stippling. So if the primitive to print is not
5097      a line we must first finish the current line (if any): */
5098   if(prim->type != GL2PS_LINE) gl2psEndSVGLine();
5099 
5100   gl2psSVGGetCoordsAndColors(prim->numverts, prim->verts, xyz, rgba);
5101 
5102   switch(prim->type){
5103   case GL2PS_POINT :
5104     gl2psSVGGetColorString(rgba[0], col);
5105     gl2psPrintf("<circle fill=\"%s\" ", col);
5106     if(rgba[0][3] < 1.0F) gl2psPrintf("fill-opacity=\"%g\" ", rgba[0][3]);
5107     gl2psPrintf("cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
5108                 xyz[0][0], xyz[0][1], 0.5 * prim->width);
5109     break;
5110   case GL2PS_LINE :
5111     if(!gl2psSamePosition(gl2ps->lastvertex.xyz, prim->verts[0].xyz) ||
5112        !gl2psSameColor(gl2ps->lastrgba, prim->verts[0].rgba) ||
5113        gl2ps->lastlinewidth != prim->width ||
5114        gl2ps->lastpattern != prim->pattern ||
5115        gl2ps->lastfactor != prim->factor){
5116       /* End the current line if the new segment does not start where
5117          the last one ended, or if the color, the width or the
5118          stippling have changed (we will need to use multi-point
5119          gradients for smooth-shaded lines) */
5120       gl2psEndSVGLine();
5121       newline = 1;
5122     }
5123     else{
5124       newline = 0;
5125     }
5126     gl2ps->lastvertex = prim->verts[1];
5127     gl2psSetLastColor(prim->verts[0].rgba);
5128     gl2ps->lastlinewidth = prim->width;
5129     gl2ps->lastpattern = prim->pattern;
5130     gl2ps->lastfactor = prim->factor;
5131     if(newline){
5132       gl2psSVGGetColorString(rgba[0], col);
5133       gl2psPrintf("<polyline fill=\"none\" stroke=\"%s\" stroke-width=\"%g\" ",
5134                   col, prim->width);
5135       if(rgba[0][3] < 1.0F) gl2psPrintf("stroke-opacity=\"%g\" ", rgba[0][3]);
5136       gl2psPrintSVGDash(prim->pattern, prim->factor);
5137       gl2psPrintf("points=\"%g,%g ", xyz[0][0], xyz[0][1]);
5138     }
5139     else{
5140       gl2psPrintf("%g,%g ", xyz[0][0], xyz[0][1]);
5141     }
5142     break;
5143   case GL2PS_TRIANGLE :
5144     gl2psPrintSVGSmoothTriangle(xyz, rgba);
5145     break;
5146   case GL2PS_QUADRANGLE :
5147     gl2psMsg(GL2PS_WARNING, "There should not be any quad left to print");
5148     break;
5149   case GL2PS_PIXMAP :
5150     gl2psPrintSVGPixmap(xyz[0][0], xyz[0][1], prim->data.image);
5151     break;
5152   case GL2PS_TEXT :
5153     gl2psSVGGetColorString(prim->verts[0].rgba, col);
5154     gl2psPrintf("<text fill=\"%s\" x=\"%g\" y=\"%g\" font-size=\"%d\" ",
5155                 col, xyz[0][0], xyz[0][1], prim->data.text->fontsize);
5156     if(prim->data.text->angle)
5157       gl2psPrintf("transform=\"rotate(%g, %g, %g)\" ",
5158                   -prim->data.text->angle, xyz[0][0], xyz[0][1]);
5159     switch(prim->data.text->alignment){
5160     case GL2PS_TEXT_C:
5161       gl2psPrintf("text-anchor=\"middle\" baseline-shift=\"%d\" ",
5162                   -prim->data.text->fontsize / 2);
5163       break;
5164     case GL2PS_TEXT_CL:
5165       gl2psPrintf("text-anchor=\"start\" baseline-shift=\"%d\" ",
5166                   -prim->data.text->fontsize / 2);
5167       break;
5168     case GL2PS_TEXT_CR:
5169       gl2psPrintf("text-anchor=\"end\" baseline-shift=\"%d\" ",
5170                   -prim->data.text->fontsize / 2);
5171       break;
5172     case GL2PS_TEXT_B:
5173       gl2psPrintf("text-anchor=\"middle\" baseline-shift=\"0\" ");
5174       break;
5175     case GL2PS_TEXT_BR:
5176       gl2psPrintf("text-anchor=\"end\" baseline-shift=\"0\" ");
5177       break;
5178     case GL2PS_TEXT_T:
5179       gl2psPrintf("text-anchor=\"middle\" baseline-shift=\"%d\" ",
5180                   -prim->data.text->fontsize);
5181       break;
5182     case GL2PS_TEXT_TL:
5183       gl2psPrintf("text-anchor=\"start\" baseline-shift=\"%d\" ",
5184                   -prim->data.text->fontsize);
5185       break;
5186     case GL2PS_TEXT_TR:
5187       gl2psPrintf("text-anchor=\"end\" baseline-shift=\"%d\" ",
5188                   -prim->data.text->fontsize);
5189       break;
5190     case GL2PS_TEXT_BL:
5191     default: /* same as GL2PS_TEXT_BL */
5192       gl2psPrintf("text-anchor=\"start\" baseline-shift=\"0\" ");
5193       break;
5194     }
5195     if(!strcmp(prim->data.text->fontname, "Times-Roman"))
5196       gl2psPrintf("font-family=\"Times\">");
5197     else if(!strcmp(prim->data.text->fontname, "Times-Bold"))
5198       gl2psPrintf("font-family=\"Times\" font-weight=\"bold\">");
5199     else if(!strcmp(prim->data.text->fontname, "Times-Italic"))
5200       gl2psPrintf("font-family=\"Times\" font-style=\"italic\">");
5201     else if(!strcmp(prim->data.text->fontname, "Times-BoldItalic"))
5202       gl2psPrintf("font-family=\"Times\" font-style=\"italic\" font-weight=\"bold\">");
5203     else if(!strcmp(prim->data.text->fontname, "Helvetica-Bold"))
5204       gl2psPrintf("font-family=\"Helvetica\" font-weight=\"bold\">");
5205     else if(!strcmp(prim->data.text->fontname, "Helvetica-Oblique"))
5206       gl2psPrintf("font-family=\"Helvetica\" font-style=\"oblique\">");
5207     else if(!strcmp(prim->data.text->fontname, "Helvetica-BoldOblique"))
5208       gl2psPrintf("font-family=\"Helvetica\" font-style=\"oblique\" font-weight=\"bold\">");
5209     else if(!strcmp(prim->data.text->fontname, "Courier-Bold"))
5210       gl2psPrintf("font-family=\"Courier\" font-weight=\"bold\">");
5211     else if(!strcmp(prim->data.text->fontname, "Courier-Oblique"))
5212       gl2psPrintf("font-family=\"Courier\" font-style=\"oblique\">");
5213     else if(!strcmp(prim->data.text->fontname, "Courier-BoldOblique"))
5214       gl2psPrintf("font-family=\"Courier\" font-style=\"oblique\" font-weight=\"bold\">");
5215     else
5216       gl2psPrintf("font-family=\"%s\">", prim->data.text->fontname);
5217     gl2psPrintf("%s</text>\n", prim->data.text->str);
5218     break;
5219   case GL2PS_SPECIAL :
5220     /* alignment contains the format for which the special output text
5221        is intended */
5222     if(prim->data.text->alignment == GL2PS_SVG)
5223       gl2psPrintf("%s\n", prim->data.text->str);
5224     break;
5225   default :
5226     break;
5227   }
5228 }
5229 
5230 static void gl2psPrintSVGFooter(void)
5231 {
5232   gl2psPrintf("</g>\n");
5233   gl2psPrintf("</svg>\n");
5234 
5235   gl2psPrintGzipFooter();
5236 }
5237 
5238 static void gl2psPrintSVGBeginViewport(GLint viewport[4])
5239 {
5240   GLint idx;
5241   char col[32];
5242   GLfloat rgba[4];
5243   int x = viewport[0], y = viewport[1], w = viewport[2], h = viewport[3];
5244 
5245   glRenderMode(GL_FEEDBACK);
5246 
5247   if(gl2ps->header){
5248     gl2psPrintSVGHeader();
5249     gl2ps->header = GL_FALSE;
5250   }
5251 
5252   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
5253     if(gl2ps->colormode == GL_RGBA || gl2ps->colorsize == 0){
5254       glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba);
5255     }
5256     else{
5257       glGetIntegerv(GL_INDEX_CLEAR_VALUE, &idx);
5258       rgba[0] = gl2ps->colormap[idx][0];
5259       rgba[1] = gl2ps->colormap[idx][1];
5260       rgba[2] = gl2ps->colormap[idx][2];
5261       rgba[3] = 1.0F;
5262     }
5263     gl2psSVGGetColorString(rgba, col);
5264     gl2psPrintf("<polygon fill=\"%s\" points=\"%d,%d %d,%d %d,%d %d,%d\"/>\n", col,
5265                 x, gl2ps->viewport[3] - y,
5266                 x + w, gl2ps->viewport[3] - y,
5267                 x + w, gl2ps->viewport[3] - (y + h),
5268                 x, gl2ps->viewport[3] - (y + h));
5269   }
5270 
5271   gl2psPrintf("<clipPath id=\"cp%d%d%d%d\">\n", x, y, w, h);
5272   gl2psPrintf("  <polygon points=\"%d,%d %d,%d %d,%d %d,%d\"/>\n",
5273               x, gl2ps->viewport[3] - y,
5274               x + w, gl2ps->viewport[3] - y,
5275               x + w, gl2ps->viewport[3] - (y + h),
5276               x, gl2ps->viewport[3] - (y + h));
5277   gl2psPrintf("</clipPath>\n");
5278   gl2psPrintf("<g clip-path=\"url(#cp%d%d%d%d)\">\n", x, y, w, h);
5279 }
5280 
5281 static GLint gl2psPrintSVGEndViewport(void)
5282 {
5283   GLint res;
5284 
5285   res = gl2psPrintPrimitives();
5286   gl2psPrintf("</g>\n");
5287   return res;
5288 }
5289 
5290 static void gl2psPrintSVGFinalPrimitive(void)
5291 {
5292   /* End any remaining line, if any */
5293   gl2psEndSVGLine();
5294 }
5295 
5296 /* definition of the SVG backend */
5297 
5298 static GL2PSbackend gl2psSVG = {
5299   gl2psPrintSVGHeader,
5300   gl2psPrintSVGFooter,
5301   gl2psPrintSVGBeginViewport,
5302   gl2psPrintSVGEndViewport,
5303   gl2psPrintSVGPrimitive,
5304   gl2psPrintSVGFinalPrimitive,
5305   "svg",
5306   "Scalable Vector Graphics"
5307 };
5308 
5309 /*********************************************************************
5310  *
5311  * PGF routines
5312  *
5313  *********************************************************************/
5314 
5315 static void gl2psPrintPGFColor(GL2PSrgba rgba)
5316 {
5317   if(!gl2psSameColor(gl2ps->lastrgba, rgba)){
5318     gl2psSetLastColor(rgba);
5319     fprintf(gl2ps->stream, "\\color[rgb]{%f,%f,%f}\n", rgba[0], rgba[1], rgba[2]);
5320   }
5321 }
5322 
5323 static void gl2psPrintPGFHeader(void)
5324 {
5325   time_t now;
5326 
5327   time(&now);
5328 
5329   fprintf(gl2ps->stream,
5330           "%% Title: %s\n"
5331           "%% Creator: GL2PS %d.%d.%d%s, %s\n"
5332           "%% For: %s\n"
5333           "%% CreationDate: %s",
5334           gl2ps->title, GL2PS_MAJOR_VERSION, GL2PS_MINOR_VERSION,
5335           GL2PS_PATCH_VERSION, GL2PS_EXTRA_VERSION, GL2PS_COPYRIGHT,
5336           gl2ps->producer, ctime(&now));
5337 
5338   fprintf(gl2ps->stream, "\\begin{pgfpicture}\n");
5339   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
5340     gl2psPrintPGFColor(gl2ps->bgcolor);
5341     fprintf(gl2ps->stream,
5342             "\\pgfpathrectanglecorners{"
5343             "\\pgfpoint{%dpt}{%dpt}}{\\pgfpoint{%dpt}{%dpt}}\n"
5344             "\\pgfusepath{fill}\n",
5345             (int)gl2ps->viewport[0], (int)gl2ps->viewport[1],
5346             (int)gl2ps->viewport[2], (int)gl2ps->viewport[3]);
5347   }
5348 }
5349 
5350 static void gl2psPrintPGFDash(GLushort pattern, GLint factor)
5351 {
5352   int i, n, array[10];
5353 
5354   if(pattern == gl2ps->lastpattern && factor == gl2ps->lastfactor)
5355     return;
5356 
5357   gl2ps->lastpattern = pattern;
5358   gl2ps->lastfactor = factor;
5359 
5360   if(!pattern || !factor){
5361     /* solid line */
5362     fprintf(gl2ps->stream, "\\pgfsetdash{}{0pt}\n");
5363   }
5364   else{
5365     gl2psParseStipplePattern(pattern, factor, &n, array);
5366     fprintf(gl2ps->stream, "\\pgfsetdash{");
5367     for(i = 0; i < n; i++) fprintf(gl2ps->stream, "{%dpt}", array[i]);
5368     fprintf(gl2ps->stream, "}{0pt}\n");
5369   }
5370 }
5371 
5372 static const char *gl2psPGFTextAlignment(int align)
5373 {
5374   switch(align){
5375   case GL2PS_TEXT_C  : return "center";
5376   case GL2PS_TEXT_CL : return "west";
5377   case GL2PS_TEXT_CR : return "east";
5378   case GL2PS_TEXT_B  : return "south";
5379   case GL2PS_TEXT_BR : return "south east";
5380   case GL2PS_TEXT_T  : return "north";
5381   case GL2PS_TEXT_TL : return "north west";
5382   case GL2PS_TEXT_TR : return "north east";
5383   case GL2PS_TEXT_BL :
5384   default            : return "south west";
5385   }
5386 }
5387 
5388 static void gl2psPrintPGFPrimitive(void *data)
5389 {
5390   GL2PSprimitive *prim;
5391 
5392   prim = *(GL2PSprimitive**)data;
5393 
5394   switch(prim->type){
5395   case GL2PS_POINT :
5396     /* Points in openGL are rectangular */
5397     gl2psPrintPGFColor(prim->verts[0].rgba);
5398     fprintf(gl2ps->stream,
5399             "\\pgfpathrectangle{\\pgfpoint{%fpt}{%fpt}}"
5400             "{\\pgfpoint{%fpt}{%fpt}}\n\\pgfusepath{fill}\n",
5401             prim->verts[0].xyz[0]-0.5*prim->width,
5402             prim->verts[0].xyz[1]-0.5*prim->width,
5403             prim->width,prim->width);
5404     break;
5405   case GL2PS_LINE :
5406     gl2psPrintPGFColor(prim->verts[0].rgba);
5407     if(gl2ps->lastlinewidth != prim->width){
5408       gl2ps->lastlinewidth = prim->width;
5409       fprintf(gl2ps->stream, "\\pgfsetlinewidth{%fpt}\n", gl2ps->lastlinewidth);
5410     }
5411     gl2psPrintPGFDash(prim->pattern, prim->factor);
5412     fprintf(gl2ps->stream,
5413             "\\pgfpathmoveto{\\pgfpoint{%fpt}{%fpt}}\n"
5414             "\\pgflineto{\\pgfpoint{%fpt}{%fpt}}\n"
5415             "\\pgfusepath{stroke}\n",
5416             prim->verts[1].xyz[0], prim->verts[1].xyz[1],
5417             prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
5418     break;
5419   case GL2PS_TRIANGLE :
5420     if(gl2ps->lastlinewidth != 0){
5421       gl2ps->lastlinewidth = 0;
5422       fprintf(gl2ps->stream, "\\pgfsetlinewidth{0.01pt}\n");
5423     }
5424     gl2psPrintPGFColor(prim->verts[0].rgba);
5425     fprintf(gl2ps->stream,
5426             "\\pgfpathmoveto{\\pgfpoint{%fpt}{%fpt}}\n"
5427             "\\pgflineto{\\pgfpoint{%fpt}{%fpt}}\n"
5428             "\\pgflineto{\\pgfpoint{%fpt}{%fpt}}\n"
5429             "\\pgfpathclose\n"
5430             "\\pgfusepath{fill,stroke}\n",
5431             prim->verts[2].xyz[0], prim->verts[2].xyz[1],
5432             prim->verts[1].xyz[0], prim->verts[1].xyz[1],
5433             prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
5434     break;
5435   case GL2PS_TEXT :
5436     fprintf(gl2ps->stream, "{\n\\pgftransformshift{\\pgfpoint{%fpt}{%fpt}}\n",
5437             prim->verts[0].xyz[0], prim->verts[0].xyz[1]);
5438 
5439     if(prim->data.text->angle)
5440       fprintf(gl2ps->stream, "\\pgftransformrotate{%f}{", prim->data.text->angle);
5441 
5442     fprintf(gl2ps->stream, "\\pgfnode{rectangle}{%s}{\\fontsize{%d}{0}\\selectfont",
5443             gl2psPGFTextAlignment(prim->data.text->alignment),
5444             prim->data.text->fontsize);
5445 
5446     fprintf(gl2ps->stream, "\\textcolor[rgb]{%g,%g,%g}{{%s}}",
5447             prim->verts[0].rgba[0], prim->verts[0].rgba[1],
5448             prim->verts[0].rgba[2], prim->data.text->str);
5449 
5450     fprintf(gl2ps->stream, "}{}{\\pgfusepath{discard}}}\n");
5451     break;
5452   case GL2PS_SPECIAL :
5453     /* alignment contains the format for which the special output text
5454        is intended */
5455     if (prim->data.text->alignment == GL2PS_PGF)
5456       fprintf(gl2ps->stream, "%s\n", prim->data.text->str);
5457     break;
5458   default :
5459     break;
5460   }
5461 }
5462 
5463 static void gl2psPrintPGFFooter(void)
5464 {
5465   fprintf(gl2ps->stream, "\\end{pgfpicture}\n");
5466 }
5467 
5468 static void gl2psPrintPGFBeginViewport(GLint viewport[4])
5469 {
5470   GLint idx;
5471   GLfloat rgba[4];
5472   int x = viewport[0], y = viewport[1], w = viewport[2], h = viewport[3];
5473 
5474   glRenderMode(GL_FEEDBACK);
5475 
5476   if(gl2ps->header){
5477     gl2psPrintPGFHeader();
5478     gl2ps->header = GL_FALSE;
5479   }
5480 
5481   fprintf(gl2ps->stream, "\\begin{pgfscope}\n");
5482   if(gl2ps->options & GL2PS_DRAW_BACKGROUND){
5483     if(gl2ps->colormode == GL_RGBA || gl2ps->colorsize == 0){
5484       glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba);
5485     }
5486     else{
5487       glGetIntegerv(GL_INDEX_CLEAR_VALUE, &idx);
5488       rgba[0] = gl2ps->colormap[idx][0];
5489       rgba[1] = gl2ps->colormap[idx][1];
5490       rgba[2] = gl2ps->colormap[idx][2];
5491       rgba[3] = 1.0F;
5492     }
5493     gl2psPrintPGFColor(rgba);
5494     fprintf(gl2ps->stream,
5495             "\\pgfpathrectangle{\\pgfpoint{%dpt}{%dpt}}"
5496             "{\\pgfpoint{%dpt}{%dpt}}\n"
5497             "\\pgfusepath{fill}\n",
5498             x, y, w, h);
5499   }
5500 
5501   fprintf(gl2ps->stream,
5502           "\\pgfpathrectangle{\\pgfpoint{%dpt}{%dpt}}"
5503           "{\\pgfpoint{%dpt}{%dpt}}\n"
5504           "\\pgfusepath{clip}\n",
5505           x, y, w, h);
5506 }
5507 
5508 static GLint gl2psPrintPGFEndViewport(void)
5509 {
5510   GLint res;
5511   res = gl2psPrintPrimitives();
5512   fprintf(gl2ps->stream, "\\end{pgfscope}\n");
5513   return res;
5514 }
5515 
5516 static void gl2psPrintPGFFinalPrimitive(void)
5517 {
5518 }
5519 
5520 /* definition of the PGF backend */
5521 
5522 static GL2PSbackend gl2psPGF = {
5523   gl2psPrintPGFHeader,
5524   gl2psPrintPGFFooter,
5525   gl2psPrintPGFBeginViewport,
5526   gl2psPrintPGFEndViewport,
5527   gl2psPrintPGFPrimitive,
5528   gl2psPrintPGFFinalPrimitive,
5529   "tex",
5530   "PGF Latex Graphics"
5531 };
5532 
5533 /*********************************************************************
5534  *
5535  * General primitive printing routine
5536  *
5537  *********************************************************************/
5538 
5539 /* Warning: the ordering of the backends must match the format
5540    #defines in gl2ps.h */
5541 
5542 static GL2PSbackend *gl2psbackends[] = {
5543   &gl2psPS,  /* 0 */
5544   &gl2psEPS, /* 1 */
5545   &gl2psTEX, /* 2 */
5546   &gl2psPDF, /* 3 */
5547   &gl2psSVG, /* 4 */
5548   &gl2psPGF  /* 5 */
5549 };
5550 
5551 static void gl2psComputeTightBoundingBox(void *data)
5552 {
5553   GL2PSprimitive *prim;
5554   int i;
5555 
5556   prim = *(GL2PSprimitive**)data;
5557 
5558   for(i = 0; i < prim->numverts; i++){
5559     if(prim->verts[i].xyz[0] < gl2ps->viewport[0])
5560       gl2ps->viewport[0] = (GLint)prim->verts[i].xyz[0];
5561     if(prim->verts[i].xyz[0] > gl2ps->viewport[2])
5562       gl2ps->viewport[2] = (GLint)(prim->verts[i].xyz[0] + 0.5F);
5563     if(prim->verts[i].xyz[1] < gl2ps->viewport[1])
5564       gl2ps->viewport[1] = (GLint)prim->verts[i].xyz[1];
5565     if(prim->verts[i].xyz[1] > gl2ps->viewport[3])
5566       gl2ps->viewport[3] = (GLint)(prim->verts[i].xyz[1] + 0.5F);
5567   }
5568 }
5569 
5570 static GLint gl2psPrintPrimitives(void)
5571 {
5572   GL2PSbsptree *root;
5573   GL2PSxyz eye = {0.0F, 0.0F, 100.0F * GL2PS_ZSCALE};
5574   GLint used;
5575 
5576   used = glRenderMode(GL_RENDER);
5577 
5578   if(used < 0){
5579     gl2psMsg(GL2PS_INFO, "OpenGL feedback buffer overflow");
5580     return GL2PS_OVERFLOW;
5581   }
5582 
5583   if(used > 0)
5584     gl2psParseFeedbackBuffer(used);
5585 
5586   gl2psRescaleAndOffset();
5587 
5588   if(gl2ps->header){
5589     if(gl2psListNbr(gl2ps->primitives) &&
5590        (gl2ps->options & GL2PS_TIGHT_BOUNDING_BOX)){
5591       gl2ps->viewport[0] = gl2ps->viewport[1] = 100000;
5592       gl2ps->viewport[2] = gl2ps->viewport[3] = -100000;
5593       gl2psListAction(gl2ps->primitives, gl2psComputeTightBoundingBox);
5594     }
5595     (gl2psbackends[gl2ps->format]->printHeader)();
5596     gl2ps->header = GL_FALSE;
5597   }
5598 
5599   if(!gl2psListNbr(gl2ps->primitives)){
5600     /* empty feedback buffer and/or nothing else to print */
5601     return GL2PS_NO_FEEDBACK;
5602   }
5603 
5604   switch(gl2ps->sort){
5605   case GL2PS_NO_SORT :
5606     gl2psListAction(gl2ps->primitives, gl2psbackends[gl2ps->format]->printPrimitive);
5607     gl2psListAction(gl2ps->primitives, gl2psFreePrimitive);
5608     /* reset the primitive list, waiting for the next viewport */
5609     gl2psListReset(gl2ps->primitives);
5610     break;
5611   case GL2PS_SIMPLE_SORT :
5612     gl2psListSort(gl2ps->primitives, gl2psCompareDepth);
5613     if(gl2ps->options & GL2PS_OCCLUSION_CULL){
5614       gl2psListActionInverse(gl2ps->primitives, gl2psAddInImageTree);
5615       gl2psFreeBspImageTree(&gl2ps->imagetree);
5616     }
5617     gl2psListAction(gl2ps->primitives, gl2psbackends[gl2ps->format]->printPrimitive);
5618     gl2psListAction(gl2ps->primitives, gl2psFreePrimitive);
5619     /* reset the primitive list, waiting for the next viewport */
5620     gl2psListReset(gl2ps->primitives);
5621     break;
5622   case GL2PS_BSP_SORT :
5623     root = (GL2PSbsptree*)gl2psMalloc(sizeof(GL2PSbsptree));
5624     gl2psBuildBspTree(root, gl2ps->primitives);
5625     if(GL_TRUE == gl2ps->boundary) gl2psBuildPolygonBoundary(root);
5626     if(gl2ps->options & GL2PS_OCCLUSION_CULL){
5627       gl2psTraverseBspTree(root, eye, -GL2PS_EPSILON, gl2psLess,
5628                            gl2psAddInImageTree, 1);
5629       gl2psFreeBspImageTree(&gl2ps->imagetree);
5630     }
5631     gl2psTraverseBspTree(root, eye, GL2PS_EPSILON, gl2psGreater,
5632                          gl2psbackends[gl2ps->format]->printPrimitive, 0);
5633     gl2psFreeBspTree(&root);
5634     /* reallocate the primitive list (it's been deleted by
5635        gl2psBuildBspTree) in case there is another viewport */
5636     gl2ps->primitives = gl2psListCreate(500, 500, sizeof(GL2PSprimitive*));
5637     break;
5638   }
5639   gl2psbackends[gl2ps->format]->printFinalPrimitive();
5640 
5641   return GL2PS_SUCCESS;
5642 }
5643 
5644 /*********************************************************************
5645  *
5646  * Public routines
5647  *
5648  *********************************************************************/
5649 
5650 GL2PSDLL_API GLint gl2psBeginPage(const char *title, const char *producer,
5651                                   GLint viewport[4], GLint format, GLint sort,
5652                                   GLint options, GLint colormode,
5653                                   GLint colorsize, GL2PSrgba *colormap,
5654                                   GLint nr, GLint ng, GLint nb, GLint buffersize,
5655                                   FILE *stream, const char *filename)
5656 {
5657   GLint idx;
5658   int i;
5659 
5660   if(gl2ps){
5661     gl2psMsg(GL2PS_ERROR, "gl2psBeginPage called in wrong program state");
5662     return GL2PS_ERROR;
5663   }
5664 
5665   gl2ps = (GL2PScontext*)gl2psMalloc(sizeof(GL2PScontext));
5666 
5667   if(format >= 0 && format < (GLint)(sizeof(gl2psbackends) / sizeof(gl2psbackends[0]))){
5668     gl2ps->format = format;
5669   }
5670   else {
5671     gl2psMsg(GL2PS_ERROR, "Unknown output format: %d", format);
5672     gl2psFree(gl2ps);
5673     gl2ps = NULL;
5674     return GL2PS_ERROR;
5675   }
5676 
5677   switch(sort){
5678   case GL2PS_NO_SORT :
5679   case GL2PS_SIMPLE_SORT :
5680   case GL2PS_BSP_SORT :
5681     gl2ps->sort = sort;
5682     break;
5683   default :
5684     gl2psMsg(GL2PS_ERROR, "Unknown sorting algorithm: %d", sort);
5685     gl2psFree(gl2ps);
5686     gl2ps = NULL;
5687     return GL2PS_ERROR;
5688   }
5689 
5690   if(stream){
5691     gl2ps->stream = stream;
5692   }
5693   else{
5694     gl2psMsg(GL2PS_ERROR, "Bad file pointer");
5695     gl2psFree(gl2ps);
5696     gl2ps = NULL;
5697     return GL2PS_ERROR;
5698   }
5699 
5700   gl2ps->header = GL_TRUE;
5701   gl2ps->maxbestroot = 10;
5702   gl2ps->options = options;
5703   gl2ps->compress = NULL;
5704   gl2ps->imagemap_head = NULL;
5705   gl2ps->imagemap_tail = NULL;
5706 
5707   if(gl2ps->options & GL2PS_USE_CURRENT_VIEWPORT){
5708     glGetIntegerv(GL_VIEWPORT, gl2ps->viewport);
5709   }
5710   else{
5711     for(i = 0; i < 4; i++){
5712       gl2ps->viewport[i] = viewport[i];
5713     }
5714   }
5715 
5716   if(!gl2ps->viewport[2] || !gl2ps->viewport[3]){
5717     gl2psMsg(GL2PS_ERROR, "Incorrect viewport (x=%d, y=%d, width=%d, height=%d)",
5718              gl2ps->viewport[0], gl2ps->viewport[1],
5719              gl2ps->viewport[2], gl2ps->viewport[3]);
5720     gl2psFree(gl2ps);
5721     gl2ps = NULL;
5722     return GL2PS_ERROR;
5723   }
5724 
5725   gl2ps->threshold[0] = nr ? 1.0F / (GLfloat)nr : 0.064F;
5726   gl2ps->threshold[1] = ng ? 1.0F / (GLfloat)ng : 0.034F;
5727   gl2ps->threshold[2] = nb ? 1.0F / (GLfloat)nb : 0.100F;
5728   gl2ps->colormode = colormode;
5729   gl2ps->buffersize = buffersize > 0 ? buffersize : 2048 * 2048;
5730   for(i = 0; i < 3; i++){
5731     gl2ps->lastvertex.xyz[i] = -1.0F;
5732   }
5733   for(i = 0; i < 4; i++){
5734     gl2ps->lastvertex.rgba[i] = -1.0F;
5735     gl2ps->lastrgba[i] = -1.0F;
5736   }
5737   gl2ps->lastlinewidth = -1.0F;
5738   gl2ps->lastpattern = 0;
5739   gl2ps->lastfactor = 0;
5740   gl2ps->imagetree = NULL;
5741   gl2ps->primitivetoadd = NULL;
5742   gl2ps->zerosurfacearea = GL_FALSE;
5743   gl2ps->pdfprimlist = NULL;
5744   gl2ps->pdfgrouplist = NULL;
5745   gl2ps->xreflist = NULL;
5746 
5747   /* get default blending mode from current OpenGL state (enabled by
5748      default for SVG) */
5749   gl2ps->blending = (gl2ps->format == GL2PS_SVG) ? GL_TRUE : glIsEnabled(GL_BLEND);
5750   glGetIntegerv(GL_BLEND_SRC, &gl2ps->blendfunc[0]);
5751   glGetIntegerv(GL_BLEND_DST, &gl2ps->blendfunc[1]);
5752 
5753   if(gl2ps->colormode == GL_RGBA){
5754     gl2ps->colorsize = 0;
5755     gl2ps->colormap = NULL;
5756     glGetFloatv(GL_COLOR_CLEAR_VALUE, gl2ps->bgcolor);
5757   }
5758   else if(gl2ps->colormode == GL_COLOR_INDEX){
5759     if(!colorsize || !colormap){
5760       gl2psMsg(GL2PS_ERROR, "Missing colormap for GL_COLOR_INDEX rendering");
5761       gl2psFree(gl2ps);
5762       gl2ps = NULL;
5763       return GL2PS_ERROR;
5764     }
5765     gl2ps->colorsize = colorsize;
5766     gl2ps->colormap = (GL2PSrgba*)gl2psMalloc(gl2ps->colorsize * sizeof(GL2PSrgba));
5767     memcpy(gl2ps->colormap, colormap, gl2ps->colorsize * sizeof(GL2PSrgba));
5768     glGetIntegerv(GL_INDEX_CLEAR_VALUE, &idx);
5769     gl2ps->bgcolor[0] = gl2ps->colormap[idx][0];
5770     gl2ps->bgcolor[1] = gl2ps->colormap[idx][1];
5771     gl2ps->bgcolor[2] = gl2ps->colormap[idx][2];
5772     gl2ps->bgcolor[3] = 1.0F;
5773   }
5774   else{
5775     gl2psMsg(GL2PS_ERROR, "Unknown color mode in gl2psBeginPage");
5776     gl2psFree(gl2ps);
5777     gl2ps = NULL;
5778     return GL2PS_ERROR;
5779   }
5780 
5781   if(!title){
5782     gl2ps->title = (char*)gl2psMalloc(sizeof(char));
5783     gl2ps->title[0] = '\0';
5784   }
5785   else{
5786     gl2ps->title = (char*)gl2psMalloc((strlen(title)+1)*sizeof(char));
5787     strcpy(gl2ps->title, title);
5788   }
5789 
5790   if(!producer){
5791     gl2ps->producer = (char*)gl2psMalloc(sizeof(char));
5792     gl2ps->producer[0] = '\0';
5793   }
5794   else{
5795     gl2ps->producer = (char*)gl2psMalloc((strlen(producer)+1)*sizeof(char));
5796     strcpy(gl2ps->producer, producer);
5797   }
5798 
5799   if(!filename){
5800     gl2ps->filename = (char*)gl2psMalloc(sizeof(char));
5801     gl2ps->filename[0] = '\0';
5802   }
5803   else{
5804     gl2ps->filename = (char*)gl2psMalloc((strlen(filename)+1)*sizeof(char));
5805     strcpy(gl2ps->filename, filename);
5806   }
5807 
5808   gl2ps->primitives = gl2psListCreate(500, 500, sizeof(GL2PSprimitive*));
5809   gl2ps->auxprimitives = gl2psListCreate(100, 100, sizeof(GL2PSprimitive*));
5810   gl2ps->feedback = (GLfloat*)gl2psMalloc(gl2ps->buffersize * sizeof(GLfloat));
5811   glFeedbackBuffer(gl2ps->buffersize, GL_3D_COLOR, gl2ps->feedback);
5812   glRenderMode(GL_FEEDBACK);
5813 
5814   return GL2PS_SUCCESS;
5815 }
5816 
5817 GL2PSDLL_API GLint gl2psEndPage(void)
5818 {
5819   GLint res;
5820 
5821   if(!gl2ps) return GL2PS_UNINITIALIZED;
5822 
5823   res = gl2psPrintPrimitives();
5824 
5825   if(res != GL2PS_OVERFLOW)
5826     (gl2psbackends[gl2ps->format]->printFooter)();
5827 
5828   fflush(gl2ps->stream);
5829 
5830   gl2psListDelete(gl2ps->primitives);
5831   gl2psListDelete(gl2ps->auxprimitives);
5832   gl2psFreeImagemap(gl2ps->imagemap_head);
5833   gl2psFree(gl2ps->colormap);
5834   gl2psFree(gl2ps->title);
5835   gl2psFree(gl2ps->producer);
5836   gl2psFree(gl2ps->filename);
5837   gl2psFree(gl2ps->feedback);
5838   gl2psFree(gl2ps);
5839   gl2ps = NULL;
5840 
5841   return res;
5842 }
5843 
5844 GL2PSDLL_API GLint gl2psBeginViewport(GLint viewport[4])
5845 {
5846   if(!gl2ps) return GL2PS_UNINITIALIZED;
5847 
5848   (gl2psbackends[gl2ps->format]->beginViewport)(viewport);
5849 
5850   return GL2PS_SUCCESS;
5851 }
5852 
5853 GL2PSDLL_API GLint gl2psEndViewport(void)
5854 {
5855   GLint res;
5856 
5857   if(!gl2ps) return GL2PS_UNINITIALIZED;
5858 
5859   res = (gl2psbackends[gl2ps->format]->endViewport)();
5860 
5861   /* reset last used colors, line widths */
5862   gl2ps->lastlinewidth = -1.0F;
5863 
5864   return res;
5865 }
5866 
5867 GL2PSDLL_API GLint gl2psTextOptColor(const char *str, const char *fontname,
5868                                      GLshort fontsize, GLint alignment, GLfloat angle,
5869                                      GL2PSrgba color)
5870 {
5871   return gl2psAddText(GL2PS_TEXT, str, fontname, fontsize, alignment, angle,
5872                       color);
5873 }
5874 
5875 GL2PSDLL_API GLint gl2psTextOpt(const char *str, const char *fontname,
5876                                 GLshort fontsize, GLint alignment, GLfloat angle)
5877 {
5878   return gl2psAddText(GL2PS_TEXT, str, fontname, fontsize, alignment, angle, NULL);
5879 }
5880 
5881 GL2PSDLL_API GLint gl2psText(const char *str, const char *fontname, GLshort fontsize)
5882 {
5883   return gl2psAddText(GL2PS_TEXT, str, fontname, fontsize, GL2PS_TEXT_BL, 0.0F,
5884                       NULL);
5885 }
5886 
5887 GL2PSDLL_API GLint gl2psSpecial(GLint format, const char *str)
5888 {
5889   return gl2psAddText(GL2PS_SPECIAL, str, "", 0, format, 0.0F, NULL);
5890 }
5891 
5892 GL2PSDLL_API GLint gl2psDrawPixels(GLsizei width, GLsizei height,
5893                                    GLint xorig, GLint yorig,
5894                                    GLenum format, GLenum type,
5895                                    const void *pixels)
5896 {
5897   int size, i;
5898   const GLfloat *piv;
5899   GLfloat pos[4], zoom_x, zoom_y;
5900   GL2PSprimitive *prim;
5901   GLboolean valid;
5902 
5903   if(!gl2ps || !pixels) return GL2PS_UNINITIALIZED;
5904 
5905   if((width <= 0) || (height <= 0)) return GL2PS_ERROR;
5906 
5907   if(gl2ps->options & GL2PS_NO_PIXMAP) return GL2PS_SUCCESS;
5908 
5909   if((format != GL_RGB && format != GL_RGBA) || type != GL_FLOAT){
5910     gl2psMsg(GL2PS_ERROR, "gl2psDrawPixels only implemented for "
5911              "GL_RGB/GL_RGBA, GL_FLOAT pixels");
5912     return GL2PS_ERROR;
5913   }
5914 
5915   glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID, &valid);
5916   if(GL_FALSE == valid) return GL2PS_SUCCESS; /* the primitive is culled */
5917 
5918   glGetFloatv(GL_CURRENT_RASTER_POSITION, pos);
5919   glGetFloatv(GL_ZOOM_X, &zoom_x);
5920   glGetFloatv(GL_ZOOM_Y, &zoom_y);
5921 
5922   prim = (GL2PSprimitive*)gl2psMalloc(sizeof(GL2PSprimitive));
5923   prim->type = GL2PS_PIXMAP;
5924   prim->boundary = 0;
5925   prim->numverts = 1;
5926   prim->verts = (GL2PSvertex*)gl2psMalloc(sizeof(GL2PSvertex));
5927   prim->verts[0].xyz[0] = pos[0] + xorig;
5928   prim->verts[0].xyz[1] = pos[1] + yorig;
5929   prim->verts[0].xyz[2] = pos[2];
5930   prim->culled = 0;
5931   prim->offset = 0;
5932   prim->pattern = 0;
5933   prim->factor = 0;
5934   prim->width = 1;
5935   glGetFloatv(GL_CURRENT_RASTER_COLOR, prim->verts[0].rgba);
5936   prim->data.image = (GL2PSimage*)gl2psMalloc(sizeof(GL2PSimage));
5937   prim->data.image->width = width;
5938   prim->data.image->height = height;
5939   prim->data.image->zoom_x = zoom_x;
5940   prim->data.image->zoom_y = zoom_y;
5941   prim->data.image->format = format;
5942   prim->data.image->type = type;
5943 
5944   switch(format){
5945   case GL_RGBA:
5946     if(gl2ps->options & GL2PS_NO_BLENDING || !gl2ps->blending){
5947       /* special case: blending turned off */
5948       prim->data.image->format = GL_RGB;
5949       size = height * width * 3;
5950       prim->data.image->pixels = (GLfloat*)gl2psMalloc(size * sizeof(GLfloat));
5951       piv = (const GLfloat*)pixels;
5952       for(i = 0; i < size; ++i, ++piv){
5953         prim->data.image->pixels[i] = *piv;
5954         if(!((i + 1) % 3))
5955           ++piv;
5956       }
5957     }
5958     else{
5959       size = height * width * 4;
5960       prim->data.image->pixels = (GLfloat*)gl2psMalloc(size * sizeof(GLfloat));
5961       memcpy(prim->data.image->pixels, pixels, size * sizeof(GLfloat));
5962     }
5963     break;
5964   case GL_RGB:
5965   default:
5966     size = height * width * 3;
5967     prim->data.image->pixels = (GLfloat*)gl2psMalloc(size * sizeof(GLfloat));
5968     memcpy(prim->data.image->pixels, pixels, size * sizeof(GLfloat));
5969     break;
5970   }
5971 
5972   gl2psListAdd(gl2ps->auxprimitives, &prim);
5973   glPassThrough(GL2PS_DRAW_PIXELS_TOKEN);
5974 
5975   return GL2PS_SUCCESS;
5976 }
5977 
5978 GL2PSDLL_API GLint gl2psDrawImageMap(GLsizei width, GLsizei height,
5979                                      const GLfloat position[3],
5980                                      const unsigned char *imagemap){
5981   int size, i;
5982   int sizeoffloat = sizeof(GLfloat);
5983 
5984   if(!gl2ps || !imagemap) return GL2PS_UNINITIALIZED;
5985 
5986   if((width <= 0) || (height <= 0)) return GL2PS_ERROR;
5987 
5988   size = height + height * ((width - 1) / 8);
5989   glPassThrough(GL2PS_IMAGEMAP_TOKEN);
5990   glBegin(GL_POINTS);
5991   glVertex3f(position[0], position[1],position[2]);
5992   glEnd();
5993   glPassThrough((GLfloat)width);
5994   glPassThrough((GLfloat)height);
5995   for(i = 0; i < size; i += sizeoffloat){
5996     const float *value = (const float*)imagemap;
5997     glPassThrough(*value);
5998     imagemap += sizeoffloat;
5999   }
6000   return GL2PS_SUCCESS;
6001 }
6002 
6003 GL2PSDLL_API GLint gl2psEnable(GLint mode)
6004 {
6005   GLint tmp;
6006 
6007   if(!gl2ps) return GL2PS_UNINITIALIZED;
6008 
6009   switch(mode){
6010   case GL2PS_POLYGON_OFFSET_FILL :
6011     glPassThrough(GL2PS_BEGIN_OFFSET_TOKEN);
6012     glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &gl2ps->offset[0]);
6013     glGetFloatv(GL_POLYGON_OFFSET_UNITS, &gl2ps->offset[1]);
6014     break;
6015   case GL2PS_POLYGON_BOUNDARY :
6016     glPassThrough(GL2PS_BEGIN_BOUNDARY_TOKEN);
6017     break;
6018   case GL2PS_LINE_STIPPLE :
6019     glPassThrough(GL2PS_BEGIN_STIPPLE_TOKEN);
6020     glGetIntegerv(GL_LINE_STIPPLE_PATTERN, &tmp);
6021     glPassThrough((GLfloat)tmp);
6022     glGetIntegerv(GL_LINE_STIPPLE_REPEAT, &tmp);
6023     glPassThrough((GLfloat)tmp);
6024     break;
6025   case GL2PS_BLEND :
6026     glPassThrough(GL2PS_BEGIN_BLEND_TOKEN);
6027     break;
6028   default :
6029     gl2psMsg(GL2PS_WARNING, "Unknown mode in gl2psEnable: %d", mode);
6030     return GL2PS_WARNING;
6031   }
6032 
6033   return GL2PS_SUCCESS;
6034 }
6035 
6036 GL2PSDLL_API GLint gl2psDisable(GLint mode)
6037 {
6038   if(!gl2ps) return GL2PS_UNINITIALIZED;
6039 
6040   switch(mode){
6041   case GL2PS_POLYGON_OFFSET_FILL :
6042     glPassThrough(GL2PS_END_OFFSET_TOKEN);
6043     break;
6044   case GL2PS_POLYGON_BOUNDARY :
6045     glPassThrough(GL2PS_END_BOUNDARY_TOKEN);
6046     break;
6047   case GL2PS_LINE_STIPPLE :
6048     glPassThrough(GL2PS_END_STIPPLE_TOKEN);
6049     break;
6050   case GL2PS_BLEND :
6051     glPassThrough(GL2PS_END_BLEND_TOKEN);
6052     break;
6053   default :
6054     gl2psMsg(GL2PS_WARNING, "Unknown mode in gl2psDisable: %d", mode);
6055     return GL2PS_WARNING;
6056   }
6057 
6058   return GL2PS_SUCCESS;
6059 }
6060 
6061 GL2PSDLL_API GLint gl2psPointSize(GLfloat value)
6062 {
6063   if(!gl2ps) return GL2PS_UNINITIALIZED;
6064 
6065   glPassThrough(GL2PS_POINT_SIZE_TOKEN);
6066   glPassThrough(value);
6067 
6068   return GL2PS_SUCCESS;
6069 }
6070 
6071 GL2PSDLL_API GLint gl2psLineWidth(GLfloat value)
6072 {
6073   if(!gl2ps) return GL2PS_UNINITIALIZED;
6074 
6075   glPassThrough(GL2PS_LINE_WIDTH_TOKEN);
6076   glPassThrough(value);
6077 
6078   return GL2PS_SUCCESS;
6079 }
6080 
6081 GL2PSDLL_API GLint gl2psBlendFunc(GLenum sfactor, GLenum dfactor)
6082 {
6083   if(!gl2ps) return GL2PS_UNINITIALIZED;
6084 
6085   if(GL_FALSE == gl2psSupportedBlendMode(sfactor, dfactor))
6086     return GL2PS_WARNING;
6087 
6088   glPassThrough(GL2PS_SRC_BLEND_TOKEN);
6089   glPassThrough((GLfloat)sfactor);
6090   glPassThrough(GL2PS_DST_BLEND_TOKEN);
6091   glPassThrough((GLfloat)dfactor);
6092 
6093   return GL2PS_SUCCESS;
6094 }
6095 
6096 GL2PSDLL_API GLint gl2psSetOptions(GLint options)
6097 {
6098   if(!gl2ps) return GL2PS_UNINITIALIZED;
6099 
6100   gl2ps->options = options;
6101 
6102   return GL2PS_SUCCESS;
6103 }
6104 
6105 GL2PSDLL_API GLint gl2psGetOptions(GLint *options)
6106 {
6107   if(!gl2ps) {
6108     *options = 0;
6109     return GL2PS_UNINITIALIZED;
6110   }
6111 
6112   *options = gl2ps->options;
6113 
6114   return GL2PS_SUCCESS;
6115 }
6116 
6117 GL2PSDLL_API const char *gl2psGetFileExtension(GLint format)
6118 {
6119   if(format >= 0 && format < (GLint)(sizeof(gl2psbackends) / sizeof(gl2psbackends[0])))
6120     return gl2psbackends[format]->file_extension;
6121   else
6122     return "Unknown format";
6123 }
6124 
6125 GL2PSDLL_API const char *gl2psGetFormatDescription(GLint format)
6126 {
6127   if(format >= 0 && format < (GLint)(sizeof(gl2psbackends) / sizeof(gl2psbackends[0])))
6128     return gl2psbackends[format]->description;
6129   else
6130     return "Unknown format";
6131 }
6132 
6133 GL2PSDLL_API GLint gl2psGetFileFormat()
6134 {
6135   return gl2ps->format;
6136 }
6137