Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/* Copyright (C) 1989-2003 artofcode LLC.  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: gxfdrop.c,v 1.16 2004/12/08 21:35:13 stefan Exp $ */
18
/* Dropout prevention for a character rasterization. */
19
 
20
#include <assert.h>
21
#include "gx.h"
22
#include "gserrors.h"
23
#include "gsstruct.h"
24
#include "gzpath.h"
25
#include "gxfixed.h"
26
#include "gxdevice.h"
27
#include "gxdcolor.h"
28
#include "gxfdrop.h"
29
#include "gxfill.h"
30
#include "vdtrace.h"
31
 
32
#define INTERTRAP_STEM_BUG 0 /* We're not sure that 1 gives a 
33
                                better painting with neighbour serifs.
34
				Need more testing.
35
 
36
 
37
/*
38
 * Rather some margins are placed in virtual memory,
39
 * we never run garbager while some of them are allocated.
40
 * Therefore we use "st_simple" for margins and sections.
41
 */
42
gs_private_st_simple(st_margin, margin, "margin");
43
gs_public_st_simple(st_section, section, "section");
44
 
45
void init_section(section *sect, int i0, int i1)
46
{   int i;
47
 
48
    for (i = i0; i < i1; i++) {
49
#	if ADJUST_SERIF && CHECK_SPOT_CONTIGUITY
50
	sect[i].x0 = fixed_1;
51
	sect[i].x1 = 0;
52
#	endif
53
	sect[i].y0 = sect[i].y1 = -1;
54
    }
55
}
56
 
57
private margin * alloc_margin(line_list * ll)
58
{   margin *m;
59
 
60
    assert(ll->fo->pseudo_rasterization);
61
    if (ll->free_margin_list != 0) {
62
	m = ll->free_margin_list;
63
	ll->free_margin_list = ll->free_margin_list->next;
64
    } else if (ll->local_margin_alloc_count < MAX_LOCAL_ACTIVE) {
65
	m = ll->local_margins + ll->local_margin_alloc_count;
66
	++ ll->local_margin_alloc_count;
67
    } else {
68
	m = gs_alloc_struct(ll->memory, margin, &st_margin, "filling contiguity margin");
69
	/* The allocation happens only if ll->local_margins[MAX_LOCAL_ACTIVE] 
70
	   is exceeded. We believe it does very seldom. */
71
    }
72
    return m;
73
}
74
 
75
private void release_margin_list(line_list * ll, margin_set *ms)
76
{   margin * m1 = ms->margin_list;
77
 
78
    if (m1 == 0)
79
	return;
80
    while (m1->next != 0)
81
	m1 = m1->next;
82
    m1->next = ll->free_margin_list;
83
    ll->free_margin_list = ms->margin_list;
84
    ms->margin_list = ms->margin_touched = 0;
85
}
86
 
87
void free_all_margins(line_list * ll)
88
{   margin * m = ll->free_margin_list;
89
 
90
    ll->free_margin_list = 0;
91
    while (m != 0)  {
92
	margin * m1 = m->next;
93
 
94
	if (m < ll->local_margins || m >= ll->local_margins + MAX_LOCAL_ACTIVE)
95
	    gs_free_object(ll->memory, m, "filling contiguity margin");
96
	m = m1;
97
    }
98
}
99
 
100
private int store_margin(line_list * ll, margin_set * set, int ii0, int ii1)
101
{
102
    /*
103
     * We need to add margin to the ordered margin list.
104
     * Contacting margins to be united.
105
     */
106
    int i0 = ii0, i1 = ii1;
107
    margin *m0 = set->margin_touched, *m1;
108
 
109
    assert(ii0 >= 0 && ii1 <= ll->bbox_width);
110
    set->margin_touched = 0; /* safety */
111
    /* Find contacting elements. */
112
    if (m0 != 0) {
113
	margin  *m_last = m0, *mb, *me;
114
 
115
	assert(set->margin_list != 0);
116
	if (i1 < m0->ibeg) {
117
	    do {
118
		m0 = m0->prev;
119
	    } while (m0 != 0 && i0 <= m0->iend);
120
	    /* m0 points to a non-contacting at left. */
121
	    m1 = (m0 == 0 ? set->margin_list : m0)->next;
122
	    while (m1 != 0 && m1->ibeg <= i1) {
123
		m_last = m1;
124
		m1 = m1->next;
125
	    }
126
	    /* m1 points to a non-contacting at right. */
127
	} else if (i0 > m0->iend) {
128
	    m1 = m0;
129
	    do {
130
		m_last = m1;
131
		m1 = m1->next;
132
	    } while (m1 != 0 && i1 >= m1->ibeg);
133
	    /* m0 points to a non-contacting at right. */
134
	    m0 = (m1 == 0 ? m_last : m1->prev);
135
	    while (m0 != 0 && m0->iend >= i0)
136
		m0 = m0->prev;
137
	    /* m1 points to a non-contacting at left. */
138
	} else {
139
	    m1 = m0;
140
	    while (m1 != 0 && m1->ibeg <= i1) {
141
		m_last = m1;
142
		m1 = m1->next;
143
	    }
144
	    /* m1 points to a non-contacting at right. */
145
	    while (m0 != 0 && m0->iend >= i0)
146
		m0 = m0->prev;
147
	    /* m1 points to a non-contacting at left. */
148
	}
149
	/* Remove elements from m0->next to m1->prev, excluding the latter.
150
	   m0 may be NULL if we riched list start. 
151
	   m1 may be NULL if we riched list end. */
152
	mb = (m0 == 0 ? set->margin_list : m0->next);
153
	if (mb != 0 && mb != m1) {
154
	    me = (m1 == 0 ? m_last : m1->prev);
155
	    /* Remove elements from mb to me, excluding the latter.
156
	       me may be NULL if we riched list start. */
157
	    if (me != 0) {
158
		 if (mb != me && me->prev != 0) {
159
		    margin *mf = me->prev;
160
 
161
		    /* Remove elements from mb to mf. */
162
		    if (mb->prev != 0)
163
			mb->prev->next = mf->next;
164
		    if (mf->next != 0)
165
			mf->next->prev = mb->prev;
166
		    if (set->margin_list == mb)
167
			set->margin_list = mf->next;
168
		    mf->next = ll->free_margin_list;
169
		    ll->free_margin_list = mb;
170
		    i0 = min(i0, mb->ibeg);
171
		    i1 = max(i1, mf->iend);
172
		    /* 'prev' links are not used in ll->free_margin_list. */
173
		}
174
	    }
175
	} 
176
	me = (m0 == 0 ? set->margin_list : m0->next);
177
	if (me == 0)
178
	    m0 = m0; /* Already set. */
179
	else if (me->iend < i0)
180
	    m0 = me; /* Insert after me. */
181
	else if (me->ibeg > i1)
182
	    m0 = me->prev; /* Insert before me. */
183
	else if (me->iend >= i0 && me->ibeg <= i1) {
184
	    /* Intersects with me. Replace me boundaries. */
185
	    me->ibeg = min(i0, me->ibeg);
186
	    me->iend = max(i1, me->iend);
187
	    set->margin_touched = me;
188
	    return 0;
189
	}
190
    }
191
    /* Insert after m0 */
192
    m1 = alloc_margin(ll);
193
    if (m1 == 0)
194
	return_error(gs_error_VMerror);
195
    if (m0 != 0) {
196
	m1->next = m0->next;
197
	m1->prev = m0;
198
	m0->next = m1;
199
	if (m1->next!= 0)
200
	    m1->next->prev = m1;
201
    } else {
202
	m1->next = set->margin_list;
203
	m1->prev = 0;
204
	if (set->margin_list != 0)
205
	    set->margin_list->prev = m1;
206
	set->margin_list = m1;
207
    }
208
    m1->ibeg = i0;
209
    m1->iend = i1;
210
    set->margin_touched = m1;
211
    return 0;
212
}
213
 
214
private inline int to_interval(int x, int l, int u)
215
{   return x < l ? l : x > u ? u : x;
216
}
217
 
218
private inline fixed Y_AT_X(active_line *alp, fixed xp)
219
{   return alp->start.y + fixed_mult_quo(xp - alp->start.x,  alp->diff.y, alp->diff.x);
220
}
221
 
222
private int margin_boundary(line_list * ll, margin_set * set, active_line * alp, 
223
			    fixed xx0, fixed xx1, fixed yy0, fixed yy1, int dir, fixed y0, fixed y1)
224
{   section *sect = set->sect;
225
    fixed x0, x1, xmin, xmax;
226
    int xp0, xp;
227
    int i0, i;
228
#   if !CHECK_SPOT_CONTIGUITY
229
    int i1;
230
#   endif
231
 
232
    if (yy0 > yy1)
233
	return 0;
234
    /* enumerate integral x's in [yy0,yy1] : */
235
 
236
    if (alp == 0)
237
	x0 = xx0, x1 = xx1;
238
    else {
239
	x0 = (yy0 == y0 ? alp->x_current : AL_X_AT_Y(alp, yy0));
240
	x1 = (yy1 == y1 ? alp->x_next : AL_X_AT_Y(alp, yy1));
241
    }
242
    xmin = min(x0, x1);
243
    xmax = max(x0, x1);
244
#   if !CHECK_SPOT_CONTIGUITY
245
	xp0 = fixed_floor(xmin) + fixed_half;
246
	i0 = fixed2int(xp0) - ll->bbox_left;
247
	if (xp0 < xmin) {
248
	    xp0 += fixed_1;
249
	    i0++;
250
	}
251
	assert(i0 >= 0);
252
	for (i = i0, xp = xp0; xp < xmax && i < ll->bbox_width; xp += fixed_1, i++) {
253
	    fixed y = (alp == 0 ? yy0 : Y_AT_X(alp, xp));
254
	    fixed dy = y - set->y;
255
	    bool ud;
256
	    short *b, h;
257
	    section *s = &sect[i];
258
 
259
	    if (dy < 0)
260
		dy = 0; /* fix rounding errors in Y_AT_X */
261
	    if (dy >= fixed_1)
262
		dy = fixed_1; /* safety */
263
	    vd_circle(xp, y, 2, 0);
264
	    ud = (alp == 0 ? (dir > 0) : ((alp->start.x - alp->end.x) * dir > 0));
265
	    b = (ud ? &s->y0 : &s->y1);
266
	    h = (short)dy;
267
	    if (*b == -1 || (*b != -2 && ( ud ? *b > h : *b < h)))
268
		*b = h;
269
	}
270
#   else
271
	xp0 = fixed_floor(xmin) + fixed_half;
272
	i0 = fixed2int(xp0) - ll->bbox_left;
273
	if (xp0 < xmin) {
274
	    i0++;
275
	    xp0 += fixed_1;
276
	}
277
	for (i = i0, xp = xp0; xp < xmax; xp += fixed_1, i++) {
278
	    section *s = &sect[i];
279
	    fixed y = (alp==0 ? yy0 : Y_AT_X(alp, xp));
280
	    fixed dy = y - set->y;
281
	    bool ud;
282
	    short *b, h;
283
 
284
	    if (dy < 0)
285
		dy = 0; /* fix rounding errors in Y_AT_X */
286
	    if (dy >= fixed_1)
287
		dy = fixed_1; /* safety */
288
	    vd_circle(xp, y, 2, 0);
289
	    ud = (alp == 0 ? (dir > 0) : ((alp->start.x - alp->end.x) * dir > 0));
290
	    b = (ud ? &s->y0 : &s->y1);
291
	    h = (short)dy;
292
	    if (*b == -1 || (*b != -2 && ( ud ? *b > h : *b < h)))
293
		*b = h;
294
	}
295
	assert(i0 >= 0 && i <= ll->bbox_width);
296
#	endif
297
    if (i > i0)
298
	return store_margin(ll, set, i0, i);
299
    return 0;
300
}
301
 
302
int continue_margin_common(line_list * ll, margin_set * set, active_line * flp, active_line * alp, fixed y0, fixed y1)
303
{   int code;
304
#   if ADJUST_SERIF
305
    section *sect = set->sect;
306
    fixed yy0 = max(max(y0, alp->start.y), set->y);
307
    fixed yy1 = min(min(y1, alp->end.y), set->y + fixed_1);
308
 
309
    if (yy0 <= yy1) {
310
	fixed x00 = (yy0 == y0 ? flp->x_current : AL_X_AT_Y(flp, yy0));
311
	fixed x10 = (yy0 == y0 ? alp->x_current : AL_X_AT_Y(alp, yy0));
312
	fixed x01 = (yy1 == y1 ? flp->x_next : AL_X_AT_Y(flp, yy1));
313
	fixed x11 = (yy1 == y1 ? alp->x_next : AL_X_AT_Y(alp, yy1));
314
	fixed xmin = min(x00, x01), xmax = max(x10, x11);
315
 
316
	int i0 = fixed2int(xmin) - ll->bbox_left, i;
317
	int i1 = fixed2int_ceiling(xmax) - ll->bbox_left;
318
 
319
	for (i = i0; i < i1; i++) {
320
	    section *s = &sect[i];
321
	    int x_pixel = int2fixed(i + ll->bbox_left);
322
	    int xl = max(xmin - x_pixel, 0);
323
	    int xu = min(xmax - x_pixel, fixed_1);
324
 
325
	    s->x0 = min(s->x0, xl);
326
	    s->x1 = max(s->x1, xu);
327
	    x_pixel+=0; /* Just a place for breakpoint */
328
	}
329
	code = store_margin(ll, set, i0, i1);
330
	if (code < 0)
331
	    return code;
332
	/* fixme : after ADJUST_SERIF becames permanent,
333
	 * don't call margin_boundary if yy0 > yy1.
334
	 */
335
    }
336
#   endif
337
 
338
    code = margin_boundary(ll, set, flp, 0, 0, yy0, yy1, 1, y0, y1);
339
    if (code < 0)
340
	return code;
341
    return margin_boundary(ll, set, alp, 0, 0, yy0, yy1, -1, y0, y1);
342
}
343
 
344
private inline int mark_margin_interior(line_list * ll, margin_set * set, active_line * flp, active_line * alp, fixed y, fixed y0, fixed y1)
345
{
346
    section *sect = set->sect;
347
    fixed x0 = (y == y0 ? flp->x_current : y == y1 ? flp->x_next : AL_X_AT_Y(flp, y));
348
    fixed x1 = (y == y0 ? alp->x_current : y == y1 ? alp->x_next : AL_X_AT_Y(alp, y));
349
    int i0 = fixed2int(x0), ii0, ii1, i, code;
350
 
351
    if (int2fixed(i0) + fixed_half < x0)
352
	i0++;
353
    ii0 = i0 - ll->bbox_left;
354
    ii1 = fixed2int_var_pixround(x1) - ll->bbox_left;
355
    if (ii0 < ii1) {
356
	assert(ii0 >= 0 && ii1 <= ll->bbox_width);
357
	for (i = ii0; i < ii1; i++) {
358
	    sect[i].y0 = sect[i].y1 = -2;
359
	    vd_circle(int2fixed(i + ll->bbox_left) + fixed_half, y, 3, RGB(255, 0, 0));
360
	}
361
	code = store_margin(ll, set, ii0, ii1);
362
	if (code < 0)
363
	    return code;
364
    }
365
    return 0;
366
}
367
 
368
int margin_interior(line_list * ll, active_line * flp, active_line * alp, fixed y0, fixed y1)
369
{   int code;
370
    fixed yy0, yy1;
371
 
372
    yy0 = ll->margin_set0.y;
373
    if (y0 <= yy0 && yy0 <= y1) {
374
	code = mark_margin_interior(ll, &ll->margin_set0, flp, alp, yy0, y0, y1);
375
	if (code < 0)
376
	    return code;
377
    }
378
    yy1 = ll->margin_set1.y + fixed_1;
379
    if (y0 <= yy1 && yy1 <= y1) {
380
	code = mark_margin_interior(ll, &ll->margin_set1, flp, alp, yy1, y0, y1);
381
	if (code < 0)
382
	    return code;
383
    }
384
    return 0;
385
}
386
 
387
private inline int process_h_sect(line_list * ll, margin_set * set, active_line * hlp0, 
388
    active_line * plp, active_line * flp, int side, fixed y0, fixed y1)
389
{
390
    active_line *hlp = hlp0;
391
    fixed y = hlp->start.y;
392
    fixed x0 = (plp != 0 ? (y == y0 ? plp->x_current : y == y1 ? plp->x_next : AL_X_AT_Y(plp, y)) 
393
			 : int2fixed(ll->bbox_left));
394
    fixed x1 = (flp != 0 ? (y == y0 ? flp->x_current : y == y1 ? flp->x_next : AL_X_AT_Y(flp, y))
395
                         : int2fixed(ll->bbox_left + ll->bbox_width));
396
    int code;
397
 
398
    for (; hlp != 0; hlp = hlp->next) {
399
	fixed xx0 = max(x0, min(hlp->start.x, hlp->end.x));
400
	fixed xx1 = min(x1, max(hlp->start.x, hlp->end.x));
401
 
402
	if (xx0 < xx1) {
403
	    vd_bar(xx0, y, xx1, y, 1, RGB(255, 0, 255));
404
	    code =  margin_boundary(ll, set, 0, xx0, xx1, y, y, side, 0, 0);
405
	    if (code < 0)
406
		return code;
407
	}
408
    }
409
    return 0;	
410
}
411
 
412
private inline int process_h_side(line_list * ll, margin_set * set, active_line * hlp, 
413
    active_line * plp, active_line * flp, active_line * alp, int side, fixed y0, fixed y1)
414
{   if (plp != 0 || flp != 0 || (plp == 0 && flp == 0 && alp == 0)) {
415
	/* We don't know here, whether the opposite (-) side is painted with
416
	 * a trapezoid. mark_margin_interior may rewrite it later.
417
	 */
418
	int code = process_h_sect(ll, set, hlp, plp, flp, -side, y0, y1);
419
 
420
	if (code < 0)
421
	    return code;
422
    }
423
    if (flp != 0 && alp != 0) {
424
	int code = process_h_sect(ll, set, hlp, flp, alp, side, y0, y1);
425
 
426
	if (code < 0)
427
	    return code;
428
    }
429
    return 0;
430
}
431
 
432
private inline int process_h_list(line_list * ll, active_line * hlp, active_line * plp, 
433
    active_line * flp, active_line * alp, int side, fixed y0, fixed y1)
434
{   fixed y = hlp->start.y;
435
 
436
    if (ll->margin_set0.y <= y && y <= ll->margin_set0.y + fixed_1) {
437
	int code = process_h_side(ll, &ll->margin_set0, hlp, plp, flp, alp, side, y0, y1);
438
 
439
	if (code < 0)
440
	    return code;
441
    }
442
    if (ll->margin_set1.y <= y && y <= ll->margin_set1.y + fixed_1) {
443
	int code = process_h_side(ll, &ll->margin_set1, hlp, plp, flp, alp, side, y0, y1);
444
 
445
	if (code < 0)
446
	    return code;
447
    }
448
    return 0;
449
}
450
 
451
int process_h_lists(line_list * ll, active_line * plp, active_line * flp, active_line * alp,
452
		    fixed y0, fixed y1)
453
{   
454
    if (y0 == y1) {
455
	/*  fixme : Must not happen. Remove. */
456
	return 0;
457
    }
458
    if (ll->h_list0 != 0) {
459
	int code = process_h_list(ll, ll->h_list0, plp, flp, alp, 1, y0, y1);
460
 
461
	if (code < 0)
462
	    return code;
463
    }
464
    if (ll->h_list1 != 0) {
465
	int code = process_h_list(ll, ll->h_list1, plp, flp, alp, -1, y0, y1);
466
 
467
	if (code < 0)
468
	    return code;
469
    }
470
    return 0;
471
}
472
 
473
private inline int compute_padding(section *s)
474
{
475
    return (s->y0 < 0 || s->y1 < 0 ? -2 : /* contacts a trapezoid - don't paint */
476
	    s->y1 < fixed_half ? 0 : 
477
	    s->y0 > fixed_half ? 1 : 
478
	    fixed_half - s->y0 < s->y1 - fixed_half ? 1 : 0);
479
}
480
 
481
private int fill_margin(gx_device * dev, const line_list * ll, margin_set *ms, int i0, int i1)
482
{   /* Returns the new index (positive) or return code (negative). */
483
    section *sect = ms->sect;
484
    int iy = fixed2int_var_pixround(ms->y);
485
    int i, ir, h = -2, code;
486
    const fill_options * const fo = ll->fo;
487
    const bool FILL_DIRECT = fo->fill_direct;
488
 
489
    assert(i0 >= 0 && i1 <= ll->bbox_width);
490
    ir = i0;
491
    for (i = i0; i < i1; i++) {
492
	int y0 = sect[i].y0, y1 = sect[i].y1, hh;
493
 
494
	if (y0 == -1) 
495
	    y0 = 0;
496
	if (y1 == -1) 
497
	    y1 = fixed_scale - 1;
498
	hh = compute_padding(&sect[i]);
499
#	if ADJUST_SERIF
500
	    if (hh >= 0) {
501
#		if !CHECK_SPOT_CONTIGUITY
502
		    if (i == i0 && i + 1 < i1) {
503
			int hhh = compute_padding(&sect[i + 1]);
504
 
505
			hh = hhh;
506
		    } else if (i == i1 - 1 && i > i0)
507
			hh = h; 
508
		    /* We could optimize it with moving outside the cycle.
509
		     * Delaying the optimization until the code is well tested.
510
		     */
511
#		else
512
		    if (sect[i].x0 > 0 && sect[i].x1 == fixed_1 && i + 1 < i1) {
513
#			if INTERTRAP_STEM_BUG
514
			int hhh = hh;
515
#			endif
516
			hh = (i + 1 < i1 ? compute_padding(&sect[i + 1]) : -2);
517
			/* We could cache hh.
518
			 * Delaying the optimization until the code is well tested.
519
			 */
520
#			if INTERTRAP_STEM_BUG
521
			/* A bug in the old code. */
522
			if (i > i0 && i + 1 < i1 && hh == -2 && 
523
				compute_padding(&sect[i - 1]) == -2) {
524
			    /* It can be either a thin stem going from left to up or down
525
			       (See 'r' in 01-001.ps in 'General', ppmraw, 72dpi),
526
			       or a serif from the left.
527
			       Since it is between 2 trapezoids, it is better to paint it
528
			       against a dropout. */
529
			    hh = hhh;
530
			}
531
#			endif
532
		    } else if (sect[i].x0 == 0 && sect[i].x1 < fixed_1) {
533
#			if INTERTRAP_STEM_BUG
534
			int hhh = hh;
535
#			endif
536
			hh = h;
537
#			if INTERTRAP_STEM_BUG
538
			/* A bug in the old code. */
539
			if (i > i0 && i + 1 < i1 && hh == -2 && 
540
				compute_padding(&sect[i - 1]) == -2) {
541
			    /* It can be either a thin stem going from right to up or down
542
			       (See 'r' in 01-001.ps in 'General', ppmraw, 72dpi),
543
			       or a serif from the right.
544
			       Since it is between 2 trapezoids, it is better to paint it. 
545
			       against a dropout. */
546
			    DO_NOTHING;
547
			}
548
#			endif
549
		    }
550
#		endif
551
	    }
552
#	endif
553
	if (h != hh) {
554
	    if (h >= 0) {
555
		VD_RECT(ir + ll->bbox_left, iy + h, i - ir, 1, VD_MARG_COLOR);
556
		code = LOOP_FILL_RECTANGLE_DIRECT(fo, ir + ll->bbox_left, iy + h, i - ir, 1);
557
		if (code < 0)
558
		    return code;
559
	    }
560
	    ir = i;
561
	    h = hh;
562
	}
563
    }
564
    if (h >= 0) {
565
	VD_RECT(ir + ll->bbox_left, iy + h, i - ir, 1, VD_MARG_COLOR);
566
	code = LOOP_FILL_RECTANGLE_DIRECT(fo, ir + ll->bbox_left, iy + h, i - ir, 1);
567
	if (code < 0)
568
	    return code;
569
    }
570
    init_section(sect, i0, i1);
571
    return 0;
572
/*
573
 *  We added the ADJUST_SERIF feature for small fonts, which are poorly hinted.
574
 *  An example is 033-52-5873.pdf at 72 dpi.
575
 *  We either suppress a serif or move it up or down for 1 pixel.
576
 *  If we would paint it as an entire pixel where it occures, it looks too big
577
 *  relatively to the character size. Besides, a stem end may
578
 *  be placed a little bit below the baseline, and our dropout prevention 
579
 *  method desides to paint a pixel below baseline, so that it looks
580
 *  fallen down (or fallen up in the case of character top).
581
 *  
582
 *  We assume that contacting margins are merged in margin_list.
583
 *  This implies that areas outside a margin are not painted
584
 *  (Only useful without CHECK_SPOT_CONTIGUITY).
585
 *
586
 *  With no CHECK_SPOT_CONTIGUITY we can't perfectly handle the case when 2 serifs
587
 *  contact each another inside a margin interior (such as Serif 'n').
588
 *  Since we don't know the contiguty, we misrecognize them as a stem and 
589
 *  leave them as they are (possibly still fallen down or up).
590
 *
591
 *  CHECK_SPOT_CONTIGUITY computes the contiguity of the intersection of the spot
592
 *  and the section window. It allows to recognize contacting serifs properly.
593
 *
594
 *  If a serif isn't painted with regular trapezoids, 
595
 *  it appears a small one, so we don't need to measure its size.
596
 *  This heuristic isn't perfect, but it is very fast.
597
 *  Meanwhile with CHECK_SPOT_CONTIGUITY we actually have something
598
 *  like a bbox for a small serif, and a rough estimation is possible.
599
 * 
600
 *  We believe that in normal cases this stuff should work idle,
601
 *  because a perfect rendering should either use anti-aliasing 
602
 *  (so that the character isn't small in the subpixel grid),
603
 *  and/or the path must be well fitted into the grid. So please consider
604
 *  this code as an attempt to do our best for the case of a 
605
 *  non-well-setup rendering.
606
 */
607
}
608
 
609
int close_margins(gx_device * dev, line_list * ll, margin_set *ms)
610
{   margin *m = ms->margin_list;
611
    int code;
612
 
613
    for (; m != 0; m = m->next) {
614
	code = fill_margin(dev, ll, ms, m->ibeg, m->iend);
615
	if (code < 0)
616
	    return code;
617
    }
618
    release_margin_list(ll, ms);
619
    return 0;
620
}
621
 
622
int start_margin_set(gx_device * dev, line_list * ll, fixed y0)
623
{   int code;
624
    fixed ym = fixed_pixround(y0) - fixed_half;
625
    margin_set s;
626
 
627
    if (ll->margin_set0.y == ym)
628
	return 0;
629
    s = ll->margin_set1;
630
    ll->margin_set1 = ll->margin_set0;
631
    ll->margin_set0 = s;
632
    code = close_margins(dev, ll, &ll->margin_set0);
633
    ll->margin_set0.y = ym;
634
    return code;
635
}
636
 
637
 
638