source: trunk/source/visualization/externals/gl2ps/src/gl2ps.cc~ @ 1358

Last change on this file since 1358 was 1350, checked in by garnier, 14 years ago

update to last version 4.9.4

File size: 178.7 KB
Line 
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
113typedef 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
122typedef GLfloat GL2PSxyz[3];
123typedef GLfloat GL2PSplane[4];
124
125typedef struct _GL2PSbsptree2d GL2PSbsptree2d;
126
127struct _GL2PSbsptree2d {
128  GL2PSplane plane;
129  GL2PSbsptree2d *front, *back;
130};
131
132typedef struct {
133  GLint nmax, size, incr, n;
134  char *array;
135} GL2PSlist;
136
137typedef struct _GL2PSbsptree GL2PSbsptree;
138
139struct _GL2PSbsptree {
140  GL2PSplane plane;
141  GL2PSlist *primitives;
142  GL2PSbsptree *front, *back;
143};
144
145typedef struct {
146  GL2PSxyz xyz;
147  GL2PSrgba rgba;
148} GL2PSvertex;
149
150typedef struct {
151  GL2PSvertex vertex[3];
152  int prop;
153} GL2PStriangle;
154
155typedef 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
164typedef 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
173typedef struct _GL2PSimagemap GL2PSimagemap;
174
175struct _GL2PSimagemap {
176  GL2PSimage *image;
177  GL2PSimagemap *next;
178};
179
180typedef 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
193typedef 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
202typedef struct{
203  GL2PSlist* ptrlist;
204  int gsno, fontno, imno, shno, maskshno, trgroupno;
205  int gsobjno, fontobjno, imobjno, shobjno, maskshobjno, trgroupobjno;
206} GL2PSpdfgroup;
207
208typedef 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
248typedef 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
262static GL2PScontext *gl2ps = NULL;
263
264/* Need to forward-declare this one */
265
266static GLint gl2psPrintPrimitives(void);
267
268/*********************************************************************
269 *
270 * Utility routines
271 *
272 *********************************************************************/
273
274static 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
292static 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
305static 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
316static void gl2psFree(void *ptr)
317{
318  if(!ptr) return;
319  free(ptr);
320}
321
322static 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
336static 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
346static 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
359static 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
375static 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
394static 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
405static 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
415static 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
443static 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
461static 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
503static 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
523static 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
539static void gl2psListReset(GL2PSlist *list)
540{
541  if(!list) return;
542  list->n = 0;
543}
544
545static void gl2psListDelete(GL2PSlist *list)
546{
547  if(!list) return; 
548  gl2psFree(list->array);
549  gl2psFree(list);
550}
551
552static 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
563static int gl2psListNbr(GL2PSlist *list)
564{
565  if(!list)
566    return 0;
567  return list->n;
568}
569
570static 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
583static 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
591static 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
600static 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
611static 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
618static 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
629static 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
665static 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 
674static 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
686static 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
703static void gl2psSetLastColor(GL2PSrgba rgba)
704{
705  int i;       
706  for(i = 0; i < 3; ++i){
707    gl2ps->lastrgba[i] = rgba[i];
708  }
709}
710
711static 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
739static 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
765static 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
779static 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
787static void gl2psUserFlushPNG(png_structp png_ptr)
788{
789}
790
791static 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
839static 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
884static 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
898static 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
909static 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
920static 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
944static 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
977static 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
987static 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
998static 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
1035static 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
1050static 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
1058static GLfloat gl2psPsca(GLfloat *a, GLfloat *b)
1059{
1060  return (a[0]*b[0] + a[1]*b[1] + a[2]*b[2]);
1061}
1062
1063static 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
1070static GLfloat gl2psNorm(GLfloat *a)
1071{
1072  return (GLfloat)sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
1073}
1074
1075static 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
1094static 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
1158static 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
1183static 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
1227static 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
1241static GLshort gl2psGetIndex(GLshort i, GLshort num)
1242{
1243  return (i < num - 1) ? i + 1 : 0;
1244}
1245
1246static 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
1277static 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
1337static 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
1361static 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
1392static 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
1401static 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
1446static 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
1458static 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
1473static 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
1489static 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
1503static GLboolean gl2psGreater(GLfloat f1, GLfloat f2)
1504{
1505  if(f1 > f2) return GL_TRUE;
1506  else return GL_FALSE;
1507}
1508
1509static GLboolean gl2psLess(GLfloat f1, GLfloat f2)
1510{
1511  if(f1 < f2) return GL_TRUE;
1512  else return GL_FALSE;
1513}
1514
1515static 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
1578static 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
1614static 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
1701static 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
1723static 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
1733static 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
1743static 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
1847static 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
1862static 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
1896static 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
1978static 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
2037static 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
2054static 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
2122static 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
2142static 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
2167static 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
2192static 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
2402static 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
2409static 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
2610static 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
2630static 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
2869static 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
2877static void gl2psResetPostScriptColor(void)
2878{
2879  gl2ps->lastrgba[0] = gl2ps->lastrgba[1] = gl2ps->lastrgba[2] = -1.;
2880}
2881
2882static 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
2894static 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
2928static 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
2955static 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
3094static 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
3107static 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
3147static GLint gl2psPrintPostScriptEndViewport(void)
3148{
3149  GLint res;
3150
3151  res = gl2psPrintPrimitives();
3152  gl2psPrintf("grestore\n");
3153  return res;
3154}
3155
3156static 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
3164static GL2PSbackend gl2psPS = {
3165  gl2psPrintPostScriptHeader,
3166  gl2psPrintPostScriptFooter,
3167  gl2psPrintPostScriptBeginViewport,
3168  gl2psPrintPostScriptEndViewport,
3169  gl2psPrintPostScriptPrimitive,
3170  gl2psPrintPostScriptFinalPrimitive,
3171  "ps",
3172  "Postscript"
3173};
3174
3175static 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
3192static 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 = 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
3233static 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
3295static void gl2psPrintTeXFooter(void)
3296{
3297  fprintf(gl2ps->stream, "\\end{picture}%s\n",
3298          (gl2ps->options & GL2PS_LANDSCAPE) ? "}" : "");
3299}
3300
3301static 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
3311static GLint gl2psPrintTeXEndViewport(void)
3312{
3313  return gl2psPrintPrimitives();
3314}
3315
3316static void gl2psPrintTeXFinalPrimitive(void)
3317{
3318}
3319
3320/* definition of the LaTeX backend */
3321
3322static 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
3339static 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
3349static 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
3366static 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
3382static 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
3392static 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
3419static 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
3429static 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
3440static 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
3453static 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
3549static 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
3603static 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
3791static 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
3812static 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
3834static 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
3870static 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
3890static 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
3909static 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
3950static 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
3961static 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
3975static 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
3992static 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
4010static 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
4043static 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
4056static 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
4081static 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
4091static 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
4120static 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
4146static 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
4166static 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
4213static 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
4238static 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
4264static 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
4292static 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
4322static 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
4409static 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
4448static 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
4469static 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
4485static 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
4525static 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
4592static 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
4612static 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
4688static 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
4753static 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
4796static GLint gl2psPrintPDFEndViewport(void)
4797{
4798  GLint res;
4799 
4800  res = gl2psPrintPrimitives();
4801  gl2ps->streamlength += gl2psPrintf("Q\n");
4802  return res;
4803}
4804
4805static void gl2psPrintPDFFinalPrimitive(void)
4806{
4807}
4808
4809/* definition of the PDF backend */
4810
4811static 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
4828static 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
4842static 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
4853static 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
4906static 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
4973static 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
4988static 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
5001static 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
5033static 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
5144static void gl2psPrintSVGFooter(void)
5145{
5146  gl2psPrintf("</g>\n");
5147  gl2psPrintf("</svg>\n"); 
5148 
5149  gl2psPrintGzipFooter();
5150}
5151
5152static 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
5195static GLint gl2psPrintSVGEndViewport(void)
5196{
5197  GLint res;
5198
5199  res = gl2psPrintPrimitives();
5200  gl2psPrintf("</g>\n");
5201  return res;
5202}
5203
5204static void gl2psPrintSVGFinalPrimitive(void)
5205{
5206  /* End any remaining line, if any */
5207  gl2psEndSVGLine();
5208}
5209
5210/* definition of the SVG backend */
5211
5212static 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
5229static 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
5237static 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
5264static 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
5286static 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
5302static 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
5377static void gl2psPrintPGFFooter(void)
5378{
5379  fprintf(gl2ps->stream, "\\end{pgfpicture}\n");
5380}
5381
5382static 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
5422static GLint gl2psPrintPGFEndViewport(void)
5423{
5424  GLint res;
5425  res = gl2psPrintPrimitives();
5426  fprintf(gl2ps->stream, "\\end{pgfscope}\n");
5427  return res;
5428}
5429
5430static void gl2psPrintPGFFinalPrimitive(void)
5431{
5432}
5433
5434/* definition of the PGF backend */
5435
5436static 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
5456static GL2PSbackend *gl2psbackends[] = {
5457  &gl2psPS,  /* 0 */
5458  &gl2psEPS, /* 1 */
5459  &gl2psTEX, /* 2 */
5460  &gl2psPDF, /* 3 */
5461  &gl2psSVG, /* 4 */
5462  &gl2psPGF  /* 5 */
5463};
5464
5465static 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
5484static 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
5564GL2PSDLL_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
5731GL2PSDLL_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
5758GL2PSDLL_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
5767GL2PSDLL_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
5781GL2PSDLL_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
5787GL2PSDLL_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
5792GL2PSDLL_API GLint gl2psSpecial(GLint format, const char *str)
5793{
5794  return gl2psAddText(GL2PS_SPECIAL, str, "", 0, format, 0.0F);
5795}
5796
5797GL2PSDLL_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
5878GL2PSDLL_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
5903GL2PSDLL_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
5936GL2PSDLL_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
5961GL2PSDLL_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
5971GL2PSDLL_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
5981GL2PSDLL_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
5996GL2PSDLL_API GLint gl2psSetOptions(GLint options)
5997{
5998  if(!gl2ps) return GL2PS_UNINITIALIZED;
5999
6000  gl2ps->options = options;
6001
6002  return GL2PS_SUCCESS;
6003}
6004
6005GL2PSDLL_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
6017GL2PSDLL_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
6025GL2PSDLL_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}
Note: See TracBrowser for help on using the repository browser.