[gs-code-review] A Type 1 hinter patch with big rendering
difference.
Igor V. Melichev
igor.melichev at artifex.com
Mon Aug 29 08:56:34 PDT 2005
The attached patch causes a small progression and a massive minor difference
in rasters. I'd like to commit it for fixing the bug 687727
"Type 1 hinter : A horizontal line condition maybe too strong".
Julia has verified the rendering difference.
Igor.
-------------- next part --------------
Not committed - need a human testing.
[Log message beg]
Type 1 hinter : Improve the criterion for stem recognition.
DETAILS :
Bug 687727 "Type 1 hinter : A horizontal line condition maybe too strong".
1. Many fonts, including URW TimesRoman, needs to apply alignemnt zones
to upper or lower corners of a glyph. An example is '1' in TimesRoman.
This patch detect upper an lower corners as local extremes by Y,
and pass them to t1_hinter__find_zone.
2. After the change (1) we detected, that the document comparefiles/LD.pdf
renders much worse. Analyzing it so far, we detected that the top of the left vertical arm
of 'm' applies same hint as horizontal arc stems. To avoid this,
this patch computes a "stem boundary quality", which is the tangent of the stem boundary,
and use it as a priority for choosing a pole for stem alignment
(the lower tangent, the higher priority).
After the change (2) we detected that most Genoa test render differently.
The reason appears that the old code erroneusely aligned a diagonal pole
of the character 'm', which is placed between the right vertical arm and
the nearest upper arc stem. This patch fixes that due to other (right) poles
get a better priority, but to our apologies this progression causes
numerous differences in rasters.
EXPECTED DIFFERENCES :
Almost ALL FILES with a text RENDER DIFFERENTLY.
[Log message end]
*** f:\casper\HEAD\gs\src\gxhintn.c Mon Aug 1 21:25:55 2005
--- files\gs\src\gxhintn.c Mon Aug 22 12:44:13 2005
***************
*** 345,366 ****
/* --------------------- t1_hint class members ---------------------*/
! private void t1_hint__set_aligned_coord(t1_hint * this, t1_glyph_space_coord gc, t1_pole * pole, enum t1_align_type align)
{ t1_glyph_space_coord g = (this->type == hstem ? pole->gy : pole->gx);
#if FINE_STEM_COMPLEXES
if (any_abs(this->g0 - g) < any_abs(this->g1 - g)) {
! if (this->aligned0 <= align)
! this->ag0 = gc, this->aligned0 = align;
} else {
! if (this->aligned1 <= align)
! this->ag1 = gc, this->aligned1 = align;
}
#else
if (any_abs(this->g0 - g) < any_abs(this->g1 - g)) {
! if (this->aligned0 < align)
! this->ag0 = gc, this->aligned0 = align;
} else {
! if (this->aligned1 < align)
! this->ag1 = gc, this->aligned1 = align;
}
#endif
--- 345,366 ----
/* --------------------- t1_hint class members ---------------------*/
! private void t1_hint__set_aligned_coord(t1_hint * this, t1_glyph_space_coord gc, t1_pole * pole, enum t1_align_type align, int quality)
{ t1_glyph_space_coord g = (this->type == hstem ? pole->gy : pole->gx);
#if FINE_STEM_COMPLEXES
if (any_abs(this->g0 - g) < any_abs(this->g1 - g)) {
! if (this->aligned0 <= align && this->q0 > quality)
! this->ag0 = gc, this->aligned0 = align, this->q0 = quality;
} else {
! if (this->aligned1 <= align && this->q1 > quality)
! this->ag1 = gc, this->aligned1 = align, this->q1 = quality;
}
#else
if (any_abs(this->g0 - g) < any_abs(this->g1 - g)) {
! if (this->aligned0 < align && this->q0 > quality)
! this->ag0 = gc, this->aligned0 = align, this->q0 = quality;
} else {
! if (this->aligned1 < align && this->q1 > quality)
! this->ag1 = gc, this->aligned1 = align, this->q1 = quality;
}
#endif
***************
*** 1252,1255 ****
--- 1252,1256 ----
hint->g1 = hint->ag1 = g1;
hint->aligned0 = hint->aligned1 = unaligned;
+ hint->q0 = hint->q1 = max_int;
hint->b0 = hint->b1 = false;
hint->stem3_index = stem3_index;
***************
*** 1415,1419 ****
private inline bool t1_hinter__is_small_angle(t1_hinter * this, int pole_index0, int pole_index1,
! long tan_x, long tan_y, int alpha, int alpha_div)
{ long gx = this->pole[pole_index1].gx - this->pole[pole_index0].gx;
long gy = this->pole[pole_index1].gy - this->pole[pole_index0].gy;
--- 1416,1420 ----
private inline bool t1_hinter__is_small_angle(t1_hinter * this, int pole_index0, int pole_index1,
! long tan_x, long tan_y, int alpha, int alpha_div, int *quality)
{ long gx = this->pole[pole_index1].gx - this->pole[pole_index0].gx;
long gy = this->pole[pole_index1].gy - this->pole[pole_index0].gy;
***************
*** 1422,1428 ****
long vp1 = any_abs(vp), sp1 = any_abs(sp);
! if (gx == 0 && gy == 0)
return false;
! return vp1 / alpha_div <= sp1 / alpha;
}
--- 1423,1440 ----
long vp1 = any_abs(vp), sp1 = any_abs(sp);
! if (gx == 0 && gy == 0) {
! *quality = max_int;
return false;
! }
! if (vp1 >= sp1) {
! *quality = max_int;
! return false;
! }
! if (vp1 / alpha_div > sp1 / alpha) {
! *quality = max_int;
! return false;
! }
! *quality = vp1 * 100 / sp1; /* The best quality is 0. */
! return true;
}
***************
*** 1452,1468 ****
}
! private inline bool t1_hinter__is_good_tangent(t1_hinter * this, int pole_index, long tan_x, long tan_y)
{ int contour_index = this->pole[pole_index].contour_index;
int beg_contour_pole = this->contour[contour_index];
int end_contour_pole = this->contour[contour_index + 1] - 2, prev, next;
int const alpha = 9, alpha_div = 10;
prev = ranger_step_b(pole_index, beg_contour_pole, end_contour_pole);
! if (t1_hinter__is_small_angle(this, prev, pole_index, tan_x, tan_y, alpha, alpha_div))
return true;
next = ranger_step_f(pole_index, beg_contour_pole, end_contour_pole);
! if (t1_hinter__is_small_angle(this, next, pole_index, tan_x, tan_y, alpha, alpha_div))
! return true;
! return false;
}
--- 1464,1485 ----
}
! private inline bool t1_hinter__is_good_tangent(t1_hinter * this, int pole_index, long tan_x, long tan_y, int *quality)
{ int contour_index = this->pole[pole_index].contour_index;
int beg_contour_pole = this->contour[contour_index];
int end_contour_pole = this->contour[contour_index + 1] - 2, prev, next;
int const alpha = 9, alpha_div = 10;
+ int quality0, quality1;
+ bool good0, good1;
prev = ranger_step_b(pole_index, beg_contour_pole, end_contour_pole);
! good0 = t1_hinter__is_small_angle(this, prev, pole_index, tan_x, tan_y, alpha, alpha_div, &quality0);
! if (quality0 == 0) {
! *quality = 0;
return true;
+ }
next = ranger_step_f(pole_index, beg_contour_pole, end_contour_pole);
! good1 = t1_hinter__is_small_angle(this, next, pole_index, tan_x, tan_y, alpha, alpha_div, &quality1);
! *quality = min(quality0, quality1);
! return good0 || good1;
}
***************
*** 1515,1519 ****
}
! private int t1_hinter__is_stem_hint_applicable(t1_hinter * this, t1_hint *hint, int pole_index)
{ /* We don't check hint->side_mask because the unused coord should be outside the design bbox. */
int k;
--- 1532,1536 ----
}
! private int t1_hinter__is_stem_hint_applicable(t1_hinter * this, t1_hint *hint, int pole_index, int *quality)
{ /* We don't check hint->side_mask because the unused coord should be outside the design bbox. */
int k;
***************
*** 1522,1531 ****
&& ((k = 1, t1_hinter__is_stem_boundary_near(this, hint, this->pole[pole_index].gy, 0)) ||
(k = 2, t1_hinter__is_stem_boundary_near(this, hint, this->pole[pole_index].gy, 1)))
! && t1_hinter__is_good_tangent(this, pole_index, 1, 0))
return k;
if (hint->type == vstem
&& ((k = 1, t1_hinter__is_stem_boundary_near(this, hint, this->pole[pole_index].gx, 0)) ||
(k = 2, t1_hinter__is_stem_boundary_near(this, hint, this->pole[pole_index].gx, 1)))
! && t1_hinter__is_good_tangent(this, pole_index, 0, 1))
return k;
return 0;
--- 1539,1548 ----
&& ((k = 1, t1_hinter__is_stem_boundary_near(this, hint, this->pole[pole_index].gy, 0)) ||
(k = 2, t1_hinter__is_stem_boundary_near(this, hint, this->pole[pole_index].gy, 1)))
! && t1_hinter__is_good_tangent(this, pole_index, 1, 0, quality))
return k;
if (hint->type == vstem
&& ((k = 1, t1_hinter__is_stem_boundary_near(this, hint, this->pole[pole_index].gx, 0)) ||
(k = 2, t1_hinter__is_stem_boundary_near(this, hint, this->pole[pole_index].gx, 1)))
! && t1_hinter__is_good_tangent(this, pole_index, 0, 1, quality))
return k;
return 0;
***************
*** 1791,1796 ****
bool bckwd_horiz = (any_abs(this->pole[prev1].gy - pole->gy) <=
max(this->blue_fuzz, any_abs(this->pole[prev1].gx - pole->gx) / 10));
! if (forwd_horiz || bckwd_horiz) {
bool forwd_curve = (this->pole[next1].type == offcurve);
bool bckwd_curve = (this->pole[prev1].type == offcurve);
--- 1808,1817 ----
bool bckwd_horiz = (any_abs(this->pole[prev1].gy - pole->gy) <=
max(this->blue_fuzz, any_abs(this->pole[prev1].gx - pole->gx) / 10));
+ bool maximum = (this->pole[next1].gy - pole->gy < 0 &&
+ this->pole[prev1].gy - pole->gy < 0);
+ bool minimum = (this->pole[next1].gy - pole->gy > 0 &&
+ this->pole[prev1].gy - pole->gy > 0);
! if (forwd_horiz || bckwd_horiz || maximum || minimum) {
bool forwd_curve = (this->pole[next1].type == offcurve);
bool bckwd_curve = (this->pole[prev1].type == offcurve);
***************
*** 1800,1806 ****
bool concave = (curve && this->pole[prev2].gy >= pole->gy &&
this->pole[next2].gy >= pole->gy);
! t1_zone *zone = t1_hinter__find_zone(this, pole->gy, curve, convex, concave);
! if (zone != NULL) {
if (this->suppress_overshoots)
# if ADOBE_OVERSHOOT_COMPATIBILIY
--- 1821,1831 ----
bool concave = (curve && this->pole[prev2].gy >= pole->gy &&
this->pole[next2].gy >= pole->gy);
! t1_zone *zone = t1_hinter__find_zone(this, pole->gy, curve || maximum || minimum,
! convex || maximum, concave || minimum);
! if (zone != NULL &&
! (forwd_horiz || bckwd_horiz ||
! (maximum && zone->type == topzone) ||
! (minimum && zone->type == botzone))) {
if (this->suppress_overshoots)
# if ADOBE_OVERSHOOT_COMPATIBILIY
***************
*** 1875,1881 ****
int next = t1_hinter__next_contour_pole(this, pole_index);
const int alpha = 10;
bool curve = this->pole[next].type == offcurve;
! bool continuing = (horiz ? t1_hinter__is_small_angle(this, next, pole_index, 1, 0, alpha, 1)
! : t1_hinter__is_small_angle(this, next, pole_index, 0, 1, alpha, 1));
*t = (!curve && continuing ? fixed_half : 0);
--- 1900,1907 ----
int next = t1_hinter__next_contour_pole(this, pole_index);
const int alpha = 10;
+ int quality;
bool curve = this->pole[next].type == offcurve;
! bool continuing = (horiz ? t1_hinter__is_small_angle(this, next, pole_index, 1, 0, alpha, 1, &quality)
! : t1_hinter__is_small_angle(this, next, pole_index, 0, 1, alpha, 1, &quality));
*t = (!curve && continuing ? fixed_half : 0);
***************
*** 1890,1896 ****
long tan_x = (horiz ? 1 : 0);
long tan_y = (horiz ? 0 : 1);
! while (t1_hinter__is_small_angle(this, i, next_pole, tan_x, tan_y, 1000, 1) && /* The threshold is taken from scratch. */
! t1_hinter__is_small_angle(this, i, next_segm, tan_x, tan_y, 1000, 1)) {
i = t1_hinter__segment_end(this, i);
if (i == pole_index) {
--- 1916,1923 ----
long tan_x = (horiz ? 1 : 0);
long tan_y = (horiz ? 0 : 1);
+ int quality;
! while (t1_hinter__is_small_angle(this, i, next_pole, tan_x, tan_y, 1000, 1, &quality) && /* The threshold is taken from scratch. */
! t1_hinter__is_small_angle(this, i, next_segm, tan_x, tan_y, 1000, 1, &quality)) {
i = t1_hinter__segment_end(this, i);
if (i == pole_index) {
***************
*** 1913,1916 ****
--- 1940,1944 ----
int beg_range_pole = this->hint_range[k].beg_pole;
int end_range_pole = this->hint_range[k].end_pole;
+ int quality;
if (this->pole[beg_range_pole].type == closepath) {
***************
*** 1922,1926 ****
}
for (j = beg_range_pole; j <= end_range_pole;) {
! int k = t1_hinter__is_stem_hint_applicable(this, &this->hint[i], j);
if (k == 1)
this->hint[i].b0 = true;
--- 1950,1954 ----
}
for (j = beg_range_pole; j <= end_range_pole;) {
! int k = t1_hinter__is_stem_hint_applicable(this, &this->hint[i], j, &quality);
if (k == 1)
this->hint[i].b0 = true;
***************
*** 1946,1949 ****
--- 1974,1978 ----
int end_range_pole = this->hint_range[k].end_pole;
bool horiz = (this->hint[i].type == hstem);
+ int quality = max_int;
if (this->pole[beg_range_pole].type == closepath) {
***************
*** 1955,1959 ****
}
for (j = beg_range_pole; j <= end_range_pole;) {
! if (t1_hinter__is_stem_hint_applicable(this, &this->hint[i], j)) {
fixed t; /* Type 1 spec implies that it is 0 for curves, 0.5 for bars */
int segment_index = t1_hinter__find_stem_middle(this, &t, j, horiz);
--- 1984,1988 ----
}
for (j = beg_range_pole; j <= end_range_pole;) {
! if (t1_hinter__is_stem_hint_applicable(this, &this->hint[i], j, &quality)) {
fixed t; /* Type 1 spec implies that it is 0 for curves, 0.5 for bars */
int segment_index = t1_hinter__find_stem_middle(this, &t, j, horiz);
***************
*** 1977,1981 ****
(horiz ? 7 : 9), (i < this->primary_hint_count ? RGB(0,0,255) : RGB(0,255,0)));
/* todo: optimize: primary commands don't need to align, if suppressed by secondary ones. */
! t1_hint__set_aligned_coord(&this->hint[i], gc, &this->pole[j], align);
jj = j;
j = t1_hinter__skip_stem(this, j, horiz);
--- 2006,2010 ----
(horiz ? 7 : 9), (i < this->primary_hint_count ? RGB(0,0,255) : RGB(0,255,0)));
/* todo: optimize: primary commands don't need to align, if suppressed by secondary ones. */
! t1_hint__set_aligned_coord(&this->hint[i], gc, &this->pole[j], align, quality);
jj = j;
j = t1_hinter__skip_stem(this, j, horiz);
*** f:\casper\HEAD\gs\src\gxhintn.h Tue Oct 5 14:24:36 2004
--- files\gs\src\gxhintn.h Mon Aug 22 12:16:04 2005
***************
*** 101,104 ****
--- 101,105 ----
#endif
enum t1_align_type aligned0, aligned1; /* ag0, ag1 is aligned */
+ int q0, q1; /* Stem quality tangent. */
unsigned int stem3_index; /* 1,2,3 for stem3 (not used yet), 0 for other types */
int range_index; /* type 2 only */
More information about the gs-code-review
mailing list