xref: /plan9/sys/src/cmd/gs/src/gxfdrop.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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 				0 is compatible to the old code. */
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 
init_section(section * sect,int i0,int i1)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 
alloc_margin(line_list * ll)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 
release_margin_list(line_list * ll,margin_set * ms)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 
free_all_margins(line_list * ll)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 
store_margin(line_list * ll,margin_set * set,int ii0,int ii1)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 
to_interval(int x,int l,int u)214 private inline int to_interval(int x, int l, int u)
215 {   return x < l ? l : x > u ? u : x;
216 }
217 
Y_AT_X(active_line * alp,fixed xp)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 
margin_boundary(line_list * ll,margin_set * set,active_line * alp,fixed xx0,fixed xx1,fixed yy0,fixed yy1,int dir,fixed y0,fixed y1)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 
continue_margin_common(line_list * ll,margin_set * set,active_line * flp,active_line * alp,fixed y0,fixed y1)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 
mark_margin_interior(line_list * ll,margin_set * set,active_line * flp,active_line * alp,fixed y,fixed y0,fixed y1)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 
margin_interior(line_list * ll,active_line * flp,active_line * alp,fixed y0,fixed y1)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 
process_h_sect(line_list * ll,margin_set * set,active_line * hlp0,active_line * plp,active_line * flp,int side,fixed y0,fixed y1)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 
process_h_side(line_list * ll,margin_set * set,active_line * hlp,active_line * plp,active_line * flp,active_line * alp,int side,fixed y0,fixed y1)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 
process_h_list(line_list * ll,active_line * hlp,active_line * plp,active_line * flp,active_line * alp,int side,fixed y0,fixed y1)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 
process_h_lists(line_list * ll,active_line * plp,active_line * flp,active_line * alp,fixed y0,fixed y1)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 
compute_padding(section * s)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 
fill_margin(gx_device * dev,const line_list * ll,margin_set * ms,int i0,int i1)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 
close_margins(gx_device * dev,line_list * ll,margin_set * ms)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 
start_margin_set(gx_device * dev,line_list * ll,fixed y0)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 
639