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

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

before tag

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