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 = §[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 = §[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 = §[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(§[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(§[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(§[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(§[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(§[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