source: trunk/source/visualization/externals/gl2ps/src/#gl2ps.cc2# @ 1350

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

update to last version 4.9.4

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