Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/* Copyright (C) 1990, 1996, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
2
 
3
  This software is provided AS-IS with no warranty, either express or
4
  implied.
5
 
6
  This software is distributed under license and may not be copied,
7
  modified or distributed except as expressly authorized under the terms
8
  of the license contained in the file LICENSE in this distribution.
9
 
10
  For more information about licensing, please refer to
11
  http://www.ghostscript.com/licensing/. For information on
12
  commercial licensing, go to http://www.artifex.com/licensing/ or
13
  contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14
  San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15
*/
16
 
17
/* $Id: zupath.c,v 1.10 2004/08/04 19:36:13 stefan Exp $ */
18
/* Operators related to user paths */
19
#include "ghost.h"
20
#include "oper.h"
21
#include "oparc.h"
22
#include "idict.h"
23
#include "dstack.h"
24
#include "igstate.h"
25
#include "iname.h"
26
#include "iutil.h"
27
#include "store.h"
28
#include "stream.h"
29
#include "ibnum.h"
30
#include "gsmatrix.h"
31
#include "gsstate.h"
32
#include "gscoord.h"
33
#include "gspaint.h"
34
#include "gxfixed.h"
35
#include "gxdevice.h"
36
#include "gspath.h"
37
#include "gzpath.h"		/* for saving path */
38
#include "gzstate.h"		/* for accessing path */
39
 
40
/* Imported data */
41
extern const gx_device gs_hit_device;
42
extern const int gs_hit_detected;
43
 
44
/* Forward references */
45
private int upath_append(os_ptr, i_ctx_t *);
46
private int upath_stroke(i_ctx_t *, gs_matrix *);
47
 
48
/* ---------------- Insideness testing ---------------- */
49
 
50
/* Forward references */
51
private int in_test(i_ctx_t *, int (*)(gs_state *));
52
private int in_path(os_ptr, i_ctx_t *, gx_device *);
53
private int in_path_result(i_ctx_t *, int, int);
54
private int in_utest(i_ctx_t *, int (*)(gs_state *));
55
private int in_upath(i_ctx_t *, gx_device *);
56
private int in_upath_result(i_ctx_t *, int, int);
57
 
58
/* <x> <y> ineofill <bool> */
59
/* <userpath> ineofill <bool> */
60
private int
61
zineofill(i_ctx_t *i_ctx_p)
62
{
63
    return in_test(i_ctx_p, gs_eofill);
64
}
65
 
66
/* <x> <y> infill <bool> */
67
/* <userpath> infill <bool> */
68
private int
69
zinfill(i_ctx_t *i_ctx_p)
70
{
71
    return in_test(i_ctx_p, gs_fill);
72
}
73
 
74
/* <x> <y> instroke <bool> */
75
/* <userpath> instroke <bool> */
76
private int
77
zinstroke(i_ctx_t *i_ctx_p)
78
{
79
    return in_test(i_ctx_p, gs_stroke);
80
}
81
 
82
/* <x> <y> <userpath> inueofill <bool> */
83
/* <userpath1> <userpath2> inueofill <bool> */
84
private int
85
zinueofill(i_ctx_t *i_ctx_p)
86
{
87
    return in_utest(i_ctx_p, gs_eofill);
88
}
89
 
90
/* <x> <y> <userpath> inufill <bool> */
91
/* <userpath1> <userpath2> inufill <bool> */
92
private int
93
zinufill(i_ctx_t *i_ctx_p)
94
{
95
    return in_utest(i_ctx_p, gs_fill);
96
}
97
 
98
/* <x> <y> <userpath> inustroke <bool> */
99
/* <x> <y> <userpath> <matrix> inustroke <bool> */
100
/* <userpath1> <userpath2> inustroke <bool> */
101
/* <userpath1> <userpath2> <matrix> inustroke <bool> */
102
private int
103
zinustroke(i_ctx_t *i_ctx_p)
104
{	/* This is different because of the optional matrix operand. */
105
    os_ptr op = osp;
106
    int code = gs_gsave(igs);
107
    int spop, npop;
108
    gs_matrix mat;
109
    gx_device hdev;
110
 
111
    if (code < 0)
112
	return code;
113
    if ((spop = upath_stroke(i_ctx_p, &mat)) < 0) {
114
	gs_grestore(igs);
115
	return spop;
116
    }
117
    if ((npop = in_path(op - spop, i_ctx_p, &hdev)) < 0) {
118
	gs_grestore(igs);
119
	return npop;
120
    }
121
    if (npop > 1)		/* matrix was supplied */
122
	code = gs_concat(igs, &mat);
123
    if (code >= 0)
124
	code = gs_stroke(igs);
125
    return in_upath_result(i_ctx_p, npop + spop, code);
126
}
127
 
128
/* ------ Internal routines ------ */
129
 
130
/* Do the work of the non-user-path insideness operators. */
131
private int
132
in_test(i_ctx_t *i_ctx_p, int (*paintproc)(gs_state *))
133
{
134
    os_ptr op = osp;
135
    gx_device hdev;
136
    int npop = in_path(op, i_ctx_p, &hdev);
137
    int code;
138
 
139
    if (npop < 0)
140
	return npop;
141
    code = (*paintproc)(igs);
142
    return in_path_result(i_ctx_p, npop, code);
143
}
144
 
145
/* Set up a clipping path and device for insideness testing. */
146
private int
147
in_path(os_ptr oppath, i_ctx_t *i_ctx_p, gx_device * phdev)
148
{
149
    int code = gs_gsave(igs);
150
    int npop;
151
    double uxy[2];
152
 
153
    if (code < 0)
154
	return code;
155
    code = num_params(oppath, 2, uxy);
156
    if (code >= 0) {		/* Aperture is a single pixel. */
157
	gs_point dxy;
158
	gs_fixed_rect fr;
159
 
160
	gs_transform(igs, uxy[0], uxy[1], &dxy);
161
	fr.p.x = fixed_floor(float2fixed(dxy.x));
162
	fr.p.y = fixed_floor(float2fixed(dxy.y));
163
	fr.q.x = fr.p.x + fixed_1;
164
	fr.q.y = fr.p.y + fixed_1;
165
	code = gx_clip_to_rectangle(igs, &fr);
166
	npop = 2;
167
    } else {			/* Aperture is a user path. */
168
	/* We have to set the clipping path without disturbing */
169
	/* the current path. */
170
	gx_path *ipath = igs->path;
171
	gx_path save;
172
 
173
	gx_path_init_local(&save, imemory);
174
	gx_path_assign_preserve(&save, ipath);
175
	gs_newpath(igs);
176
	code = upath_append(oppath, i_ctx_p);
177
	if (code >= 0)
178
	    code = gx_clip_to_path(igs);
179
	gx_path_assign_free(igs->path, &save);
180
	npop = 1;
181
    }
182
    if (code < 0) {
183
	gs_grestore(igs);
184
	return code;
185
    }
186
    /* Install the hit detection device. */
187
    gx_set_device_color_1(igs);
188
    gx_device_init((gx_device *) phdev, (const gx_device *)&gs_hit_device,
189
		   NULL, true);
190
    phdev->width = phdev->height = max_int;
191
    gx_device_fill_in_procs(phdev);
192
    gx_set_device_only(igs, phdev);
193
    return npop;
194
}
195
 
196
/* Finish an insideness test. */
197
private int
198
in_path_result(i_ctx_t *i_ctx_p, int npop, int code)
199
{
200
    os_ptr op = osp;
201
    bool result;
202
 
203
    gs_grestore(igs);		/* matches gsave in in_path */
204
    if (code == gs_hit_detected)
205
	result = true;
206
    else if (code == 0)		/* completed painting without a hit */
207
	result = false;
208
    else			/* error */
209
	return code;
210
    npop--;
211
    pop(npop);
212
    op -= npop;
213
    make_bool(op, result);
214
    return 0;
215
 
216
}
217
 
218
/* Do the work of the user-path insideness operators. */
219
private int
220
in_utest(i_ctx_t *i_ctx_p, int (*paintproc)(gs_state *))
221
{
222
    gx_device hdev;
223
    int npop = in_upath(i_ctx_p, &hdev);
224
    int code;
225
 
226
    if (npop < 0)
227
	return npop;
228
    code = (*paintproc)(igs);
229
    return in_upath_result(i_ctx_p, npop, code);
230
}
231
 
232
/* Set up a clipping path and device for insideness testing */
233
/* with a user path. */
234
private int
235
in_upath(i_ctx_t *i_ctx_p, gx_device * phdev)
236
{
237
    os_ptr op = osp;
238
    int code = gs_gsave(igs);
239
    int npop;
240
 
241
    if (code < 0)
242
	return code;
243
    if ((code = upath_append(op, i_ctx_p)) < 0 ||
244
	(npop = in_path(op - 1, i_ctx_p, phdev)) < 0
245
	) {
246
	gs_grestore(igs);
247
	return code;
248
    }
249
    return npop + 1;
250
}
251
 
252
/* Finish an insideness test with a user path. */
253
private int
254
in_upath_result(i_ctx_t *i_ctx_p, int npop, int code)
255
{
256
    gs_grestore(igs);		/* matches gsave in in_upath */
257
    return in_path_result(i_ctx_p, npop, code);
258
}
259
 
260
/* ---------------- User paths ---------------- */
261
 
262
/* User path operator codes */
263
typedef enum {
264
    upath_op_setbbox = 0,
265
    upath_op_moveto = 1,
266
    upath_op_rmoveto = 2,
267
    upath_op_lineto = 3,
268
    upath_op_rlineto = 4,
269
    upath_op_curveto = 5,
270
    upath_op_rcurveto = 6,
271
    upath_op_arc = 7,
272
    upath_op_arcn = 8,
273
    upath_op_arct = 9,
274
    upath_op_closepath = 10,
275
    upath_op_ucache = 11
276
} upath_op;
277
 
278
#define UPATH_MAX_OP 11
279
#define UPATH_REPEAT 32
280
static const byte up_nargs[UPATH_MAX_OP + 1] = {
281
    4, 2, 2, 2, 2, 6, 6, 5, 5, 5, 0, 0
282
};
283
 
284
/* Declare operator procedures not declared in opextern.h. */
285
int zsetbbox(i_ctx_t *);
286
private int zucache(i_ctx_t *);
287
 
288
#undef zp
289
static const op_proc_t up_ops[UPATH_MAX_OP + 1] = {
290
    zsetbbox, zmoveto, zrmoveto, zlineto, zrlineto,
291
    zcurveto, zrcurveto, zarc, zarcn, zarct,
292
    zclosepath, zucache
293
};
294
 
295
/* - ucache - */
296
private int
297
zucache(i_ctx_t *i_ctx_p)
298
{
299
    /* A no-op for now. */
300
    return 0;
301
}
302
 
303
/* <userpath> uappend - */
304
private int
305
zuappend(i_ctx_t *i_ctx_p)
306
{
307
    os_ptr op = osp;
308
    int code = gs_gsave(igs);
309
 
310
    if (code < 0)
311
	return code;
312
    if ((code = upath_append(op, i_ctx_p)) >= 0)
313
	code = gs_upmergepath(igs);
314
    gs_grestore(igs);
315
    if (code < 0)
316
	return code;
317
    pop(1);
318
    return 0;
319
}
320
 
321
/* <userpath> ueofill - */
322
private int
323
zueofill(i_ctx_t *i_ctx_p)
324
{
325
    os_ptr op = osp;
326
    int code = gs_gsave(igs);
327
 
328
    if (code < 0)
329
	return code;
330
    if ((code = upath_append(op, i_ctx_p)) >= 0)
331
	code = gs_eofill(igs);
332
    gs_grestore(igs);
333
    if (code < 0)
334
	return code;
335
    pop(1);
336
    return 0;
337
}
338
 
339
/* <userpath> ufill - */
340
private int
341
zufill(i_ctx_t *i_ctx_p)
342
{
343
    os_ptr op = osp;
344
    int code = gs_gsave(igs);
345
 
346
    if (code < 0)
347
	return code;
348
    if ((code = upath_append(op, i_ctx_p)) >= 0)
349
	code = gs_fill(igs);
350
    gs_grestore(igs);
351
    if (code < 0)
352
	return code;
353
    pop(1);
354
    return 0;
355
}
356
 
357
/* <userpath> ustroke - */
358
/* <userpath> <matrix> ustroke - */
359
private int
360
zustroke(i_ctx_t *i_ctx_p)
361
{
362
    int code = gs_gsave(igs);
363
    int npop;
364
 
365
    if (code < 0)
366
	return code;
367
    if ((code = npop = upath_stroke(i_ctx_p, NULL)) >= 0)
368
	code = gs_stroke(igs);
369
    gs_grestore(igs);
370
    if (code < 0)
371
	return code;
372
    pop(npop);
373
    return 0;
374
}
375
 
376
/* <userpath> ustrokepath - */
377
/* <userpath> <matrix> ustrokepath - */
378
private int
379
zustrokepath(i_ctx_t *i_ctx_p)
380
{
381
    gx_path save;
382
    int code, npop;
383
 
384
    /* Save and reset the path. */
385
    gx_path_init_local(&save, imemory);
386
    gx_path_assign_preserve(&save, igs->path);
387
    if ((code = npop = upath_stroke(i_ctx_p, NULL)) < 0 ||
388
	(code = gs_strokepath(igs)) < 0
389
	) {
390
	gx_path_assign_free(igs->path, &save);
391
	return code;
392
    }
393
    gx_path_free(&save, "ustrokepath");
394
    pop(npop);
395
    return 0;
396
}
397
 
398
/* <with_ucache> upath <userpath> */
399
/* We do all the work in a procedure that is also used to construct */
400
/* the UnpaintedPath user path for ImageType 2 images. */
401
int make_upath(i_ctx_t *i_ctx_p, ref *rupath, gs_state *pgs, gx_path *ppath,
402
	       bool with_ucache);
403
private int
404
zupath(i_ctx_t *i_ctx_p)
405
{
406
    os_ptr op = osp;
407
 
408
    check_type(*op, t_boolean);
409
    return make_upath(i_ctx_p, op, igs, igs->path, op->value.boolval);
410
}
411
int
412
make_upath(i_ctx_t *i_ctx_p, ref *rupath, gs_state *pgs, gx_path *ppath,
413
	   bool with_ucache)
414
{
415
    int size = (with_ucache ? 6 : 5);
416
    gs_path_enum penum;
417
    int op;
418
    ref *next;
419
    int code;
420
 
421
    /* Compute the size of the user path array. */
422
    {
423
	gs_fixed_point pts[3];
424
 
425
	gx_path_enum_init(&penum, ppath);
426
	while ((op = gx_path_enum_next(&penum, pts)) != 0) {
427
	    switch (op) {
428
		case gs_pe_moveto:
429
		case gs_pe_lineto:
430
		    size += 3;
431
		    continue;
432
		case gs_pe_curveto:
433
		    size += 7;
434
		    continue;
435
		case gs_pe_closepath:
436
		    size += 1;
437
		    continue;
438
		default:
439
		    return_error(e_unregistered);
440
	    }
441
	}
442
    }
443
    code = ialloc_ref_array(rupath, a_all | a_executable, size,
444
			    "make_upath");
445
    if (code < 0)
446
	return code;
447
    /* Construct the path. */
448
    next = rupath->value.refs;
449
    if (with_ucache) {
450
        if ((code = name_enter_string(pgs->memory, "ucache", next)) < 0)
451
	    return code;
452
	r_set_attrs(next, a_executable | l_new);
453
	++next;
454
    } {
455
	gs_rect bbox;
456
 
457
	if ((code = gs_upathbbox(pgs, &bbox, true)) < 0) {
458
	    /*
459
	     * Note: Adobe throws 'nocurrentpoint' error, but the PLRM
460
	     * not list this as a possible error from 'upath', so we
461
	     * set a reasonable default bbox instead.
462
	     */
463
	    if (code != e_nocurrentpoint)
464
		return code;
465
	    bbox.p.x = bbox.p.y = bbox.q.x = bbox.q.y = 0;
466
	}
467
	make_real_new(next, bbox.p.x);
468
	make_real_new(next + 1, bbox.p.y);
469
	make_real_new(next + 2, bbox.q.x);
470
	make_real_new(next + 3, bbox.q.y);
471
	next += 4;
472
	if ((code = name_enter_string(pgs->memory, "setbbox", next)) < 0)
473
	    return code;
474
	r_set_attrs(next, a_executable | l_new);
475
	++next;
476
    }
477
    {
478
	gs_point pts[3];
479
 
480
	/* Patch the path in the gstate to set up the enumerator. */
481
	gx_path *save_path = pgs->path;
482
 
483
	pgs->path = ppath;
484
	gs_path_enum_copy_init(&penum, pgs, false);
485
	pgs->path = save_path;
486
	while ((op = gs_path_enum_next(&penum, pts)) != 0) {
487
	    const char *opstr;
488
 
489
	    switch (op) {
490
		case gs_pe_moveto:
491
		    opstr = "moveto";
492
		    goto ml;
493
		case gs_pe_lineto:
494
		    opstr = "lineto";
495
		  ml:make_real_new(next, pts[0].x);
496
		    make_real_new(next + 1, pts[0].y);
497
		    next += 2;
498
		    break;
499
		case gs_pe_curveto:
500
		    opstr = "curveto";
501
		    make_real_new(next, pts[0].x);
502
		    make_real_new(next + 1, pts[0].y);
503
		    make_real_new(next + 2, pts[1].x);
504
		    make_real_new(next + 3, pts[1].y);
505
		    make_real_new(next + 4, pts[2].x);
506
		    make_real_new(next + 5, pts[2].y);
507
		    next += 6;
508
		    break;
509
		case gs_pe_closepath:
510
		    opstr = "closepath";
511
		    break;
512
		default:
513
		    return_error(e_unregistered);
514
	    }
515
	    if ((code = name_enter_string(pgs->memory, opstr, next)) < 0)
516
		return code;
517
	    r_set_attrs(next, a_executable);
518
	    ++next;
519
	}
520
    }
521
    return 0;
522
}
523
 
524
/* ------ Internal routines ------ */
525
 
526
/* Append a user path to the current path. */
527
private inline int
528
upath_append_aux(os_ptr oppath, i_ctx_t *i_ctx_p)
529
{
530
    ref opcodes;
531
    check_read(*oppath);
532
    gs_newpath(igs);
533
/****** ROUND tx AND ty ******/
534
    if (!r_is_array(oppath))
535
	return_error(e_typecheck);
536
 
537
    if ( r_size(oppath) == 2 &&
538
	 array_get(imemory, oppath, 1, &opcodes) >= 0 &&
539
         r_has_type(&opcodes, t_string)
540
	) {			/* 1st element is operands, 2nd is operators */
541
	ref operands;
542
	int code, format;
543
	int repcount = 1;
544
	const byte *opp;
545
	uint ocount, i = 0;
546
 
547
        array_get(imemory, oppath, 0, &operands);
548
        code = num_array_format(&operands);
549
	if (code < 0)
550
	    return code;
551
	format = code;
552
	opp = opcodes.value.bytes;
553
	ocount = r_size(&opcodes);
554
	while (ocount--) {
555
	    byte opx = *opp++;
556
 
557
	    if (opx > UPATH_REPEAT)
558
		repcount = opx - UPATH_REPEAT;
559
	    else if (opx > UPATH_MAX_OP)
560
		return_error(e_rangecheck);
561
	    else {		/* operator */
562
		do {
563
		    os_ptr op = osp;
564
		    byte opargs = up_nargs[opx];
565
 
566
		    while (opargs--) {
567
			push(1);
568
			code = num_array_get(imemory, &operands, format, i++, op);
569
			switch (code) {
570
			    case t_integer:
571
				r_set_type_attrs(op, t_integer, 0);
572
				break;
573
			    case t_real:
574
				r_set_type_attrs(op, t_real, 0);
575
				break;
576
			    default:
577
				return_error(e_typecheck);
578
			}
579
		    }
580
		    code = (*up_ops[opx])(i_ctx_p);
581
		    if (code < 0)
582
			return code;
583
		}
584
		while (--repcount);
585
		repcount = 1;
586
	    }
587
	}
588
    } else {	/* Ordinary executable array. */
589
	const ref *arp = oppath;
590
	uint ocount = r_size(oppath);
591
	long index = 0;
592
	int argcount = 0;
593
	op_proc_t oproc;
594
	int opx, code;
595
 
596
	for (; index < ocount; index++) {
597
	    ref rup;
598
	    ref *defp;
599
	    os_ptr op = osp;
600
 
601
	    array_get(imemory, arp, index, &rup);
602
	    switch (r_type(&rup)) {
603
		case t_integer:
604
		case t_real:
605
		    argcount++;
606
		    push(1);
607
		    *op = rup;
608
		    break;
609
		case t_name:
610
		    if (!r_has_attr(&rup, a_executable))
611
			return_error(e_typecheck);
612
		    if (dict_find(systemdict, &rup, &defp) <= 0)
613
			return_error(e_undefined);
614
		    if (r_btype(defp) != t_operator)
615
			return_error(e_typecheck);
616
		    goto xop;
617
		case t_operator:
618
		    defp = &rup;
619
		  xop:if (!r_has_attr(defp, a_executable))
620
			return_error(e_typecheck);
621
		    oproc = real_opproc(defp);
622
		    for (opx = 0; opx <= UPATH_MAX_OP; opx++)
623
			if (oproc == up_ops[opx])
624
			    break;
625
		    if (opx > UPATH_MAX_OP || argcount != up_nargs[opx])
626
			return_error(e_typecheck);
627
		    code = (*oproc)(i_ctx_p);
628
		    if (code < 0)
629
			return code;
630
		    argcount = 0;
631
		    break;
632
		default:
633
		    return_error(e_typecheck);
634
	    }
635
	}
636
	if (argcount)
637
	    return_error(e_typecheck);	/* leftover args */
638
    }
639
    return 0;
640
}
641
private int
642
upath_append(os_ptr oppath, i_ctx_t *i_ctx_p)
643
{
644
    int code = upath_append_aux(oppath, i_ctx_p);
645
 
646
    if (code < 0)
647
	return code;
648
    igs->current_point.x = fixed2float(igs->path->position.x);
649
    igs->current_point.y = fixed2float(igs->path->position.y);
650
    return 0;
651
}
652
 
653
/* Append a user path to the current path, and then apply or return */
654
/* a transformation if one is supplied. */
655
private int
656
upath_stroke(i_ctx_t *i_ctx_p, gs_matrix *pmat)
657
{
658
    os_ptr op = osp;
659
    int code, npop;
660
    gs_matrix mat;
661
 
662
    if ((code = read_matrix(imemory, op, &mat)) >= 0) {
663
	if ((code = upath_append(op - 1, i_ctx_p)) >= 0) {
664
	    if (pmat)
665
		*pmat = mat;
666
	    else
667
		code = gs_concat(igs, &mat);
668
	}
669
	npop = 2;
670
    } else {
671
	if ((code = upath_append(op, i_ctx_p)) >= 0)
672
	    if (pmat)
673
		gs_make_identity(pmat);
674
	npop = 1;
675
    }
676
    return (code < 0 ? code : npop);
677
}
678
 
679
/* ---------------- Initialization procedure ---------------- */
680
 
681
const op_def zupath_l2_op_defs[] =
682
{
683
    op_def_begin_level2(),
684
		/* Insideness testing */
685
    {"1ineofill", zineofill},
686
    {"1infill", zinfill},
687
    {"1instroke", zinstroke},
688
    {"2inueofill", zinueofill},
689
    {"2inufill", zinufill},
690
    {"2inustroke", zinustroke},
691
		/* User paths */
692
    {"1uappend", zuappend},
693
    {"0ucache", zucache},
694
    {"1ueofill", zueofill},
695
    {"1ufill", zufill},
696
    {"1upath", zupath},
697
    {"1ustroke", zustroke},
698
    {"1ustrokepath", zustrokepath},
699
    op_def_end(0)
700
};