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

Last change on this file since 1022 was 1022, checked in by garnier, 15 years ago

solution de John

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