1/***********************************************************************
2 *                                                                     *
3 * $Id: hpgspaint.c 375 2007-01-24 16:22:49Z softadm $
4 *                                                                     *
5 * hpgs - HPGl Script, a hpgl/2 interpreter, which uses a Postscript   *
6 *        API for rendering a scene and thus renders to a variety of   *
7 *        devices and fileformats.                                     *
8 *                                                                     *
9 * (C) 2004-2006 ev-i Informationstechnologie GmbH  http://www.ev-i.at *
10 *                                                                     *
11 * Author: Wolfgang Glas                                               *
12 *                                                                     *
13 *  hpgs is free software; you can redistribute it and/or              *
14 * modify it under the terms of the GNU Lesser General Public          *
15 * License as published by the Free Software Foundation; either        *
16 * version 2.1 of the License, or (at your option) any later version.  *
17 *                                                                     *
18 * hpgs is distributed in the hope that it will be useful,             *
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of      *
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   *
21 * Lesser General Public License for more details.                     *
22 *                                                                     *
23 * You should have received a copy of the GNU Lesser General Public    *
24 * License along with this library; if not, write to the               *
25 * Free Software  Foundation, Inc., 59 Temple Place, Suite 330,        *
26 * Boston, MA  02111-1307  USA                                         *
27 *                                                                     *
28 ***********************************************************************
29 *                                                                     *
30 * The implementation of the public API for the pixel painter.         *
31 *                                                                     *
32 ***********************************************************************/
33
34#include<hpgspaint.h>
35#include<math.h>
36#include<assert.h>
37#include<string.h>
38#if defined ( __MINGW32__ ) || defined ( _MSC_VER )
39#include <malloc.h>
40#else
41#include <alloca.h>
42#endif
43
44//#define HPGS_PAINT_DEBUG_ROP3
45
46/*! \defgroup paint_device Paint device.
47
48  This module contains the workhorse for rendering a scenery to pixel
49  graphics, \c hpgs_paint_device.
50
51  Most notably, you can call \c hpgs_new_paint_device in order to
52  create a new paint device and perform the usual operations
53  \c hpgs_moveto, \c hpgs_lineto, ... on it.
54
55  Details about the implementation are explained in the documentation
56  of \c hpgs_paint_device_st and \c hpgs_paint_clipper_st and the
57  hpyerlinks therein.
58*/
59
60/* static paint device methods for the hpgs_device vtable. */
61static  int pdv_moveto (hpgs_device *_this, const hpgs_point *p)
62{
63  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
64
65  if (hpgs_paint_path_moveto(pdv->path,p))
66    return hpgs_set_error(hpgs_i18n("moveto error."));
67
68  return 0;
69}
70
71static  int pdv_lineto (hpgs_device *_this, const hpgs_point *p)
72{
73  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
74
75  if (hpgs_paint_path_lineto(pdv->path,p))
76    return hpgs_set_error(hpgs_i18n("lineto error."));
77
78  return 0;
79}
80
81static  int pdv_curveto (hpgs_device *_this, const hpgs_point *p1,
82			 const hpgs_point *p2, const hpgs_point *p3 )
83{
84  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
85
86  if (hpgs_paint_path_curveto(pdv->path,p1,p2,p3))
87    return hpgs_set_error(hpgs_i18n("curveto error."));
88
89  return 0;
90}
91
92static  int pdv_newpath     (hpgs_device *_this)
93{
94  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
95
96  hpgs_paint_path_truncate(pdv->path);
97  return 0;
98}
99
100static  int pdv_closepath   (hpgs_device *_this)
101{
102  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
103
104  if (hpgs_paint_path_closepath(pdv->path))
105    return hpgs_set_error(hpgs_i18n("closepath error."));
106
107  return 0;
108}
109
110static  int pdv_stroke      (hpgs_device *_this)
111{
112  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
113  int ret = 0;
114
115  if (pdv->path->n_points > 1 ||
116      (pdv->path->n_points == 1 && (pdv->path->points[0].flags&HPGS_POINT_DOT))
117      )
118    {
119      if (!pdv->overscan &&
120          pdv->gstate->linewidth < 1.5 * pdv->path_clipper->yfac)
121	{
122          const hpgs_paint_clipper *clip = pdv->clippers[pdv->current_clipper];
123
124	  if (hpgs_paint_clipper_thin_cut(pdv->path_clipper,
125					  pdv->path,pdv->gstate))
126	    return hpgs_set_error(hpgs_i18n("Out of memory cutting thin line."));
127
128	  if (hpgs_paint_clipper_emit(pdv->image,
129				      pdv->path_clipper,clip,
130				      &pdv->color,1,1))
131	    {
132	      if (hpgs_have_error())
133		return hpgs_error_ctxt(hpgs_i18n("stroke: Image error"));
134
135	      return hpgs_set_error(hpgs_i18n("stroke: Error emitting scanlines."));
136	    }
137
138	  hpgs_paint_clipper_clear(pdv->path_clipper);
139	}
140      else
141	{
142	  hpgs_paint_path *p = hpgs_paint_path_stroke_path(pdv->path,
143							   pdv->gstate);
144
145	  if (!p)
146	    return hpgs_set_error(hpgs_i18n("Out of memory creating stroke path."));
147
148	  ret = hpgs_paint_device_fill(pdv,p,HPGS_TRUE,HPGS_TRUE);
149
150	  hpgs_paint_path_destroy(p);
151	}
152    }
153
154  hpgs_paint_path_truncate(pdv->path);
155
156  if (ret)
157    return hpgs_set_error(hpgs_i18n("Error filling stroke path."));
158
159  return 0;
160}
161
162static  int pdv_fill        (hpgs_device *_this, hpgs_bool winding)
163{
164  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
165
166  int ret = hpgs_paint_device_fill(pdv,pdv->path,winding,HPGS_FALSE);
167
168  hpgs_paint_path_truncate(pdv->path);
169
170  return ret;
171}
172
173static  int pdv_clip        (hpgs_device *_this, hpgs_bool winding)
174{
175  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
176
177  int ret = hpgs_paint_device_clip(pdv,pdv->path,winding);
178
179  hpgs_paint_path_truncate(pdv->path);
180
181  return ret;
182}
183
184static  int pdv_clipsave       (hpgs_device *_this)
185{
186  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
187
188  if (pdv->clip_depth >= HPGS_PAINT_MAX_CLIP_DEPTH)
189    return hpgs_set_error(hpgs_i18n("Maximum clip depth of %d exceeded."),
190                          HPGS_PAINT_MAX_CLIP_DEPTH);
191
192  pdv->clippers[pdv->clip_depth] = 0;
193
194  ++pdv->clip_depth;
195
196  return 0;
197}
198
199static  int pdv_cliprestore    (hpgs_device *_this)
200{
201  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
202
203  --pdv->clip_depth;
204
205  if (pdv->clippers[pdv->clip_depth])
206    {
207      hpgs_paint_clipper_destroy(pdv->clippers[pdv->clip_depth]);
208      pdv->clippers[pdv->clip_depth] = 0;
209    }
210
211  if (pdv->current_clipper < pdv->clip_depth)
212    return 0;
213
214  while (pdv->current_clipper > 0)
215    {
216      --pdv->current_clipper;
217      if (pdv->clippers[pdv->current_clipper])
218	return 0;
219    }
220
221  return hpgs_set_error(hpgs_i18n("cliprestore: No valid clipper found."));
222}
223
224static  int pdv_setrgbcolor (hpgs_device *_this, const hpgs_color *rgb)
225{
226  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
227
228  pdv->gstate->color = *rgb;
229
230  pdv->color.r = (unsigned char)(rgb->r*255.0);
231  pdv->color.g = (unsigned char)(rgb->g*255.0);
232  pdv->color.b = (unsigned char)(rgb->b*255.0);
233
234  if  (hpgs_image_define_color(pdv->image,&pdv->color))
235    return hpgs_error_ctxt("setrgbcolor");
236
237  pdv->gstate->pattern_color = *rgb;
238
239  hpgs_palette_color patcol;
240
241  patcol.r = pdv->color.r & (~pdv->patcol.r);
242  patcol.g = pdv->color.g & (~pdv->patcol.g);
243  patcol.b = pdv->color.b & (~pdv->patcol.b);
244
245#ifdef HPGS_PAINT_DEBUG_ROP3
246  hpgs_log("setrgbcol: patcol=%d,%d,%d.\n",patcol.r,patcol.g,patcol.b);
247#endif
248
249  return hpgs_image_setpatcol(pdv->image,&patcol);
250}
251
252static  int pdv_setdash (hpgs_device *_this, const float *segs,
253			 unsigned n, double d)
254{
255  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
256
257  if (hpgs_gstate_setdash(pdv->gstate,segs,n,d))
258    return hpgs_set_error(hpgs_i18n("setdash: Out of memory."));
259
260  return 0;
261}
262
263static  int pdv_setlinewidth(hpgs_device *_this, double lw)
264{
265  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
266
267  if (lw < pdv->thin_alpha * pdv->path_clipper->yfac)
268    pdv->gstate->linewidth = pdv->thin_alpha * pdv->path_clipper->yfac;
269  else
270    pdv->gstate->linewidth = lw;
271  return 0;
272}
273
274static  int pdv_setlinecap  (hpgs_device *_this, hpgs_line_cap lc)
275{
276  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
277
278  pdv->gstate->line_cap = lc;
279  return 0;
280}
281
282static  int pdv_setlinejoin (hpgs_device *_this, hpgs_line_join lj)
283{
284  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
285
286  pdv->gstate->line_join = lj;
287  return 0;
288}
289
290static  int pdv_setmiterlimit (hpgs_device *_this, double l)
291{
292  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
293
294  pdv->gstate->miterlimit = l;
295  return 0;
296}
297
298static  int pdv_setrop3 (hpgs_device *_this, int rop,
299                         hpgs_bool src_transparency, hpgs_bool pattern_transparency)
300{
301  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
302  hpgs_rop3_func_t rop3;
303
304#ifdef HPGS_PAINT_DEBUG_ROP3
305  hpgs_log("setrop3: rop,src_trans,pat_trans=%d,%d,%d.\n",
306          rop,src_transparency,pattern_transparency);
307#endif
308  pdv->gstate->rop3 = rop;
309  pdv->gstate->src_transparency = src_transparency;
310  pdv->gstate->pattern_transparency = pattern_transparency;
311
312  rop3 = hpgs_rop3_func(rop,src_transparency,pattern_transparency);
313
314  if (!rop3)
315    return hpgs_set_error(hpgs_i18n("setrop3: Invalid ROP3 %d specified"),rop);
316
317  return hpgs_image_setrop3(pdv->image,rop3);
318}
319
320static  int pdv_setpatcol(hpgs_device *_this, const hpgs_color *rgb)
321{
322  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
323
324  pdv->gstate->pattern_color = *rgb;
325
326  pdv->patcol.r = (unsigned char)(rgb->r * 255.0);
327  pdv->patcol.g = (unsigned char)(rgb->g * 255.0);
328  pdv->patcol.b = (unsigned char)(rgb->b * 255.0);
329
330  hpgs_palette_color patcol;
331
332  patcol.r = pdv->color.r & (~pdv->patcol.r);
333  patcol.g = pdv->color.g & (~pdv->patcol.g);
334  patcol.b = pdv->color.b & (~pdv->patcol.b);
335
336#ifdef HPGS_PAINT_DEBUG_ROP3
337  hpgs_log("setpatcol: patcol=%d,%d,%d.\n",patcol.r,patcol.g,patcol.b);
338#endif
339
340  return hpgs_image_setpatcol(pdv->image,&patcol);
341}
342
343static int pdv_drawimage(hpgs_device *_this,
344                         const hpgs_image *img,
345                         const hpgs_point *ll, const hpgs_point *lr,
346                         const hpgs_point *ur)
347{
348  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
349
350  if (!img)
351    return hpgs_set_error(hpgs_i18n("drawimage: Null image specified."));
352
353  hpgs_palette_color patcol;
354  patcol.r = 0;
355  patcol.g = 0;
356  patcol.b = 0;
357
358  if (hpgs_image_setpatcol(pdv->image,&patcol))
359    return -1;
360
361  if (hpgs_paint_device_drawimage(pdv,img,ll,lr,ur))
362    {
363      if (hpgs_have_error())
364        return hpgs_error_ctxt(hpgs_i18n("drawimage error"));
365
366      return hpgs_set_error(hpgs_i18n("drawimage error."));
367    }
368
369  patcol.r = pdv->color.r & (~pdv->patcol.r);
370  patcol.g = pdv->color.g & (~pdv->patcol.g);
371  patcol.b = pdv->color.b & (~pdv->patcol.b);
372
373#ifdef HPGS_PAINT_DEBUG_ROP3
374  hpgs_log("setpatcol: patcol=%d,%d,%d.\n",patcol.r,patcol.g,patcol.b);
375#endif
376
377  return hpgs_image_setpatcol(pdv->image,&patcol);
378}
379
380static  int pdv_showpage    (hpgs_device *_this, int i)
381{
382  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
383
384  int ret = 0;
385
386  if (i>0 && pdv->filename)
387    {
388      int l = strlen(pdv->filename);
389      char *fn = hpgs_alloca(l+20);
390      char * dot  = strrchr(pdv->filename,'.');
391      int pos = dot ? dot-pdv->filename : l;
392
393#ifdef WIN32
394      _snprintf(fn,l+20,"%.*s%4.4d%s",
395                pos,pdv->filename,i,pdv->filename+pos);
396#else
397      snprintf(fn,l+20,"%.*s%4.4d%s",
398               pos,pdv->filename,i,pdv->filename+pos);
399#endif
400      ret = hpgs_image_write(pdv->image,fn);
401
402      if (ret == 0)
403        ret = hpgs_image_clear(pdv->image);
404    }
405  else if (pdv->filename == 0 || strlen(pdv->filename) > 0)
406    ret = hpgs_image_write(pdv->image,pdv->filename);
407
408  return ret;
409}
410
411static  int pdv_setplotsize (hpgs_device *_this, const hpgs_bbox *bb)
412{
413  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
414  int i;
415
416  if (hpgs_bbox_isequal(&pdv->page_bb,bb))
417    return 0;
418
419  int w = (int)ceil((bb->urx-bb->llx)*pdv->xres);
420  int h = (int)ceil((bb->ury-bb->lly)*pdv->yres);
421
422  if (w<2 || h<2)
423    return hpgs_set_error(hpgs_i18n("setplotsize: The given bounding box result in a null image."));
424
425  if (hpgs_image_resize(pdv->image,w,h))
426    return hpgs_error_ctxt(hpgs_i18n("setplotsize: error resizing image"));
427
428
429  if (pdv->path_clipper) hpgs_paint_clipper_destroy(pdv->path_clipper);
430
431  pdv->path_clipper =  hpgs_new_paint_clipper(bb,
432                                              pdv->image->height,
433                                              16,pdv->overscan);
434  if (!pdv->path_clipper)
435    return hpgs_set_error(hpgs_i18n("setplotsize: Out of memory allocating path clipper."));
436
437  for (i=0;i<pdv->clip_depth;++i)
438    {
439      if (pdv->clippers[i]) hpgs_paint_clipper_destroy(pdv->clippers[i]);
440      pdv->clippers[i]=0;
441    }
442
443  // Initial dimension of clip segments is 4.
444  pdv->clippers[0] = hpgs_new_paint_clipper(bb,
445                                            pdv->image->height,4,pdv->overscan);
446
447  if (!pdv->clippers[0])
448    return hpgs_set_error(hpgs_i18n("setplotsize: Out of memory allocating clip clipper."));
449
450  if (hpgs_paint_clipper_reset(pdv->clippers[0],bb->llx,bb->urx))
451    return hpgs_set_error(hpgs_i18n("setplotsize: Out of memory initializing clip clipper."));
452
453  pdv->clip_depth = 1;
454  pdv->current_clipper = 0;
455
456  return 0;
457}
458
459static  int pdv_capabilities (hpgs_device *_this)
460{
461  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
462
463  int ret =
464    HPGS_DEVICE_CAP_RASTER |
465    HPGS_DEVICE_CAP_MULTIPAGE |
466    HPGS_DEVICE_CAP_MULTISIZE |
467    HPGS_DEVICE_CAP_DRAWIMAGE;
468
469  if (pdv->overscan)
470    ret |= HPGS_DEVICE_CAP_ANTIALIAS;
471
472  if (pdv->image->vtable->setrop3)
473    ret |= HPGS_DEVICE_CAP_ROP3;
474
475  return ret;
476}
477
478static  void pdv_destroy     (hpgs_device *_this)
479{
480  hpgs_paint_device *pdv = (hpgs_paint_device *)_this;
481  int i;
482
483  if (pdv->filename) free(pdv->filename);
484  if (pdv->path) hpgs_paint_path_destroy(pdv->path);
485  if (pdv->path_clipper) hpgs_paint_clipper_destroy(pdv->path_clipper);
486  if (pdv->gstate) hpgs_gstate_destroy(pdv->gstate);
487  if (pdv->image)  hpgs_image_destroy(pdv->image);
488
489  for (i=0;i<pdv->clip_depth;++i)
490    if (pdv->clippers[i]) hpgs_paint_clipper_destroy(pdv->clippers[i]);
491}
492
493static hpgs_device_vtable pdv_vtable =
494  {
495    "hpgs_paint_device",
496    pdv_moveto,
497    pdv_lineto,
498    pdv_curveto,
499    pdv_newpath,
500    pdv_closepath,
501    pdv_stroke,
502    pdv_fill,
503    pdv_clip,
504    pdv_clipsave,
505    pdv_cliprestore,
506    pdv_setrgbcolor,
507    pdv_setdash,
508    pdv_setlinewidth,
509    pdv_setlinecap,
510    pdv_setlinejoin,
511    pdv_setmiterlimit,
512    pdv_setrop3,
513    pdv_setpatcol,
514    pdv_drawimage,
515    pdv_setplotsize,
516    0 /* pdv_getplotsize */,
517    pdv_showpage,
518    0 /* pdv_finish */,
519    pdv_capabilities,
520    pdv_destroy
521  };
522
523/*! Creates a new paint device on the heap.
524    Use \c hpgs_destroy in order to destroy the returned
525    device pointer.
526
527    The bounding box, which is mapped onto the given \c image is passed
528    in as well as the \c antialiasing switch.
529
530    A null pointer is returned, if the system is out of memory.
531*/
532hpgs_paint_device *hpgs_new_paint_device(hpgs_image *image,
533                                         const char *filename,
534					 const hpgs_bbox *bb,
535					 hpgs_bool antialias )
536{
537  hpgs_paint_device *ret=(hpgs_paint_device *)malloc(sizeof(hpgs_paint_device));
538
539  if (ret)
540    {
541      ret->image = image;
542
543      ret->path = 0;
544      ret->gstate = 0;
545      ret->overscan = antialias;
546      ret->thin_alpha = 0.25;
547
548      if (filename)
549        {
550          ret->filename = strdup(filename);
551
552          if (!ret->filename) goto fatal_error;
553        }
554      else
555        ret->filename = 0;
556
557      memset(ret->clippers,
558	     0,HPGS_PAINT_MAX_CLIP_DEPTH * sizeof(hpgs_paint_clipper *));
559
560      ret->path = hpgs_new_paint_path();
561      if (!ret->path) goto fatal_error;
562
563      ret->path_clipper =  hpgs_new_paint_clipper(bb,
564						  image->height,
565						  16,ret->overscan);
566      if (!ret->path_clipper) goto fatal_error;
567
568      ret->gstate = hpgs_new_gstate();
569      if (!ret->gstate) goto fatal_error;
570
571     // Initial dimesion of clip segments is 4.
572      ret->clippers[0] = hpgs_new_paint_clipper(bb,
573						image->height,4,ret->overscan);
574      if (!ret->clippers[0]) goto fatal_error;
575      if (hpgs_paint_clipper_reset(ret->clippers[0],bb->llx,bb->urx))
576	goto fatal_error;
577
578      ret->clip_depth = 1;
579      ret->current_clipper = 0;
580
581      ret->page_bb = *bb;
582      ret->xres = image->width/(bb->urx-bb->llx);
583      ret->yres = image->height/(bb->ury-bb->lly);
584
585      if (hpgs_image_set_resolution(image,72.0*ret->xres,72.0*ret->yres))
586        goto fatal_error;
587
588      ret->image_interpolation = 0;
589
590      ret->color.r = 0;
591      ret->color.g = 0;
592      ret->color.b = 0;
593      ret->color.index = 0;
594
595      ret->patcol.r = 0;
596      ret->patcol.g = 0;
597      ret->patcol.b = 0;
598
599      ret->inherited.vtable    = &pdv_vtable;
600    }
601
602  return ret;
603
604 fatal_error:
605  if (ret->filename) free(ret->filename);
606  if (ret->path) hpgs_paint_path_destroy(ret->path);
607  if (ret->clippers[0]) hpgs_paint_clipper_destroy(ret->clippers[0]);
608  if (ret->gstate) hpgs_gstate_destroy(ret->gstate);
609  if (ret->image)  hpgs_image_destroy(ret->image);
610  free(ret);
611  return 0;
612}
613
614/*! Sets the image interpolation value of the given paint device.
615    Currently, the following values are supported:
616
617    \li 0 no image iterpolation
618    \li 1 linear image interpolation.
619
620    Other values specifiying square or cubic interpolation may be
621    supported in the future. The default value is 0.
622*/
623void hpgs_paint_device_set_image_interpolation (hpgs_paint_device *pdv, int i)
624{
625  pdv->image_interpolation = i;
626}
627
628/*! Sets the minimal alpha value for thin lines, when antialiasing is
629    in effect. The supplied value must be grater than or equal to 0.01 and
630    lesser than or equal to 10.0. Other values are ignored.
631*/
632void hpgs_paint_device_set_thin_alpha (hpgs_paint_device *pdv, double a)
633{
634  if (a > 0.01 && a <= 10.0)
635    pdv->thin_alpha = a;
636}
637
638/*! Fills the intersection of the given path with the current clip
639    path of the paint device \c pdv using the current graphics state of \c pdv.
640
641    if \c winding is \c HPGS_TRUE, the non-zero winding rule is used for filling,
642    otherwise the exclusive-or rule applies.
643
644    Return values:
645
646    \li 0 Sucess.
647    \li -1 An error ocurred.
648            Call \c hpgs_device_get_error in order to retrieve the error message.
649
650*/
651int hpgs_paint_device_fill(hpgs_paint_device *pdv,
652			   hpgs_paint_path *path,
653			   hpgs_bool winding,
654                           hpgs_bool stroke  )
655{
656  const hpgs_paint_clipper *clip = pdv->clippers[pdv->current_clipper];
657
658  if (hpgs_paint_clipper_cut(pdv->path_clipper,path))
659    return hpgs_set_error(hpgs_i18n("fill: Error cutting current path."));
660
661  if (hpgs_paint_clipper_emit(pdv->image,
662			      pdv->path_clipper,clip,
663			      &pdv->color,winding,stroke))
664    {
665      if (hpgs_have_error())
666	return hpgs_error_ctxt(hpgs_i18n("fill: Image error"));
667
668      return hpgs_set_error(hpgs_i18n("fill: Error emitting scanlines."));
669    }
670
671
672  hpgs_paint_clipper_clear(pdv->path_clipper);
673
674  return 0;
675}
676
677/*! Sets the intersection of the given path with the current clip
678    path of the paint device \c pdv as the current clip path of \c pdv.
679
680    if \c winding is \c HPGS_TRUE, the non-zero winding rule is used for the
681    path intersection, otherwise the exclusive-or rule applies.
682
683    Return values:
684
685    \li 0 Sucess.
686    \li -1 An error ocurred.
687            Call \c hpgs_device_get_error in order to retrieve the error message.
688
689*/
690int hpgs_paint_device_clip(hpgs_paint_device *pdv,
691			   hpgs_paint_path *path,
692			   hpgs_bool winding)
693{
694  const hpgs_paint_clipper *clip = pdv->clippers[pdv->current_clipper];
695
696  if (hpgs_paint_clipper_cut(pdv->path_clipper,path))
697    return hpgs_set_error(hpgs_i18n("clip: Error cutting current path."));
698
699  hpgs_paint_clipper *nclip =
700    hpgs_paint_clipper_clip(pdv->path_clipper,clip,winding);
701
702  if (!nclip)
703    return hpgs_set_error(hpgs_i18n("clip: Out of memory creating new clipper."));
704
705  // push the new clipper onto the stack of clippers.
706  pdv->current_clipper = pdv->clip_depth-1;
707
708  if (pdv->clippers[pdv->current_clipper])
709    hpgs_paint_clipper_destroy(pdv->clippers[pdv->current_clipper]);
710
711  pdv->clippers[pdv->current_clipper] = nclip;
712
713  hpgs_paint_clipper_clear(pdv->path_clipper);
714
715  return 0;
716}
717