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

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

update ti head

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