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

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

suppress warnings

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