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