[comp.lang.postscript] Thin line woes

tgl@zog.cs.cmu.edu (Tom Lane) (08/15/90)

  I've been running into the old problem of thin lines (one or two pixels
wide) not being drawn with consistent widths.  The green book discusses
fixing this by rounding off line endpoint coordinates to integral values
in device space (example 9-4).  I haven't been very successful in making
this work, though.  A couple of questions:

  1. Line width variation occurs even with "0.12 setlinewidth".  On a
300dpi printer this width is exactly half a pixel.  I don't understand
how this could be translated into a two-pixel-wide line, no matter where
the endpoint positions fall.  If anyone can explain the exact algorithm
for deciding which pixels get blackened, I would be grateful.

  2. In the book "Real World PostScript" (Roth, ed.), Michael Fryd
recommends rounding endpoint coordinates to nearest-integer-plus-0.25
in device space, whereas the green book just shows rounding to nearest
integer.  Which of these is better, and why?

In case it matters, I'm working on an HP LaserJet III, PS interpreter
version 52.2.

-- 
				tom lane
Internet: tgl@cs.cmu.edu
UUCP: <your favorite internet/arpanet gateway>!cs.cmu.edu!tgl
BITNET: tgl%cs.cmu.edu@cmuccvma
CompuServe: >internet:tgl@cs.cmu.edu

isaak@imagen.UUCP (Mark Isaak) (08/16/90)

in article <10244@pt.cs.cmu.edu>, tgl@zog.cs.cmu.edu (Tom Lane) says:
> 
>   1. Line width variation occurs even with "0.12 setlinewidth".  On a
> 300dpi printer this width is exactly half a pixel.  I don't understand
> how this could be translated into a two-pixel-wide line, no matter where
> the endpoint positions fall.  If anyone can explain the exact algorithm
> for deciding which pixels get blackened, I would be grateful.

Adobe's painting algorithm says to blacken every pixel which is touched
by the shape, no matter how little of the pixel falls inside.  This has
the effect of making the line you asked to be n pixels wide appear to be
n+1 pixels wide instead.

The situation is complicated by diagonal lines, which you generally
don't _want_ the same width.  Depending on their width, slope, and
positioning, the lines can appear one pixel wider along some patches
than others.  You can eliminate this artifact by correcting the width,
but the corrections are different for different slopes.  QMS/Imagen's
UltraScript does this correction automatically for thin lines, and
Display PostScript and PostScript Level 2 have a "setstrokeadjust"
operator which will do it.

The simplest solution otherwise is to specify "0 setlinewidth", which
will give a nice-looking one-pixel-wide line at all angles.  For slightly
wider diagonals, some combination of endpoint rounding and/or width
correction may be desirable.

>   2. In the book "Real World PostScript" (Roth, ed.), Michael Fryd
> recommends rounding endpoint coordinates to nearest-integer-plus-0.25
> in device space, whereas the green book just shows rounding to nearest
> integer.  Which of these is better, and why?

For horizontal and vertical lines, it shouldn't matter in theory, although
adding +0.25 might avoid some obscure round-off errors in practice  (That
last part is just a guess).  I'm not sure how the difference would affect
diagonal lines.
-- 
Mark Isaak    {decwrl,sun}!imagen!isaak  or  imagen!isaak@decwrl.dec.com
"Convictions are more dangerous enemies of truth than lies." - Nietzsche

fryd@g.gp.cs.cmu.edu (Michael Fryd) (08/16/90)

In article <10244@pt.cs.cmu.edu> Tom Lane (tgl@zog.cs.cmu.edu) asks
about the problems with line width variations and how to solve them.

His example is that on a 300 dpi printer, a "0.12 setlinewidth" (1 /600th
of an inch) sometimes results in lines which are 1 pixel thick, and sometimes
results in lines which are two pixels thick.  Common sense would lead one to
believe that a request for a 1/2 pixel thick line would simply result
in a 1 pixel thick line.

The problem is PostScript's rendering algorithm.  The algorithm works
by computing the ideal (desired) image, and then turns on any pixel that is
touched by the computed ideal image.  (Some people feel that a better 
choice would have been to only turn on pixels that are more than half 
covered by the ideal image).

If an ideal 1/2 pixel line lies completely in a line of pixels, then
the printed page would have a 1 pixel thick line on it.  If the ideal 1/2
pixel line happens to cover the boundary between two adjacent lines of 
pixels, then the printed page would have a 2 pixel line on it.

Assuming a random distribution of line placement, a 1/2 pixel ideal line would
print as a 1 pixel line half the time and a 2 pixel line half the time.

The suggested solution in both "Real World PostScript" and the green book
is to fine tune the positions of the ideal lines (by up to 1/2 pixel) to give
a consistent relationship between line positions and the underlying device
pixels.  This consistent relationship insures that a particular line
width will always image with consistent thickness.

Various sources differ in suggesting exactly how to adjust the center
of the ideal line to the device pixels.  The common suggestions are:
    - align the ideal line to the center of the nearest pixel (.5)
    - align the ideal line to the nearest pixel boundary (.0)
    - align between the center and edge of the nearest pixel (.25)

Each of these suggestions has advantages and disadvantages.

If aligned to the center of the nearest pixel (.5) all lines will image
at an odd number of pixels thick.  The actual thickness will be the ideal 
thickness rounded up to the nearest odd number.  ideal lines less than one 
pixel will image at exactly one pixel.  Ideal lines from 1 to 3 will
image at exactly 3 pixels.  Assuming a random distribution of ideal line
thickness, imaged lines will be about 1 pixel too big.

If aligned to pixel boundaries (.0) all lines will image an even number
of pixels thick.  The actual thickness will be the ideal thickness rounded
up to the nearest even number.  Ideal lines less than 2 pixels thick
will render at exactly 2 pixels.  Ideal lines from 2.00001 to 4 will
render at 4 pixels thick.  Assuming a random distribution of ideal line
thickness,  imaged lines will be about 1 pixel too big.

If aligned between the edge and center of the pixels,  both odd and even
thicknesses can be rendered. Ideal lines from 0 to .5 pixels will render
at exactly one pixel.  Ideal lines from .5 to 1.5 will render at 2 pixels
thick, from 1.5 to 2.5 at 3 pixels, etc.
The .25 method results the widest variety of line widths and
the smallest maximum error (1.5 vs. 2 pixels), but it also results in the
greatest minimum error (.5 vs. 0).


I prefer the .25 solution because it allows a greater variety of line
widths.  If greater accuracy is desired, I suggest combining the .25 method
with a patch to setlinewidth that reduces the requested line width by 1 pixel.
The combination of the .25 method and the 1 pixel reduction results in:
lines from 0 to 1.5 pixels image at 1 pixel.
lines from 1.5 to 2.5 image at 2 pixels.
lines from 2.5 to 3.5 image at 3, etc.

Michael Fryd
President                           Voice: (412) 751-5557
MEFCO, Inc.                         Fax:   (412) 751-8403
2401 Coulter Road                   Email: Michael.Fryd@CS.CMU.EDU
McKeesport, PA  15131-4251

orthlieb@adobe.COM (Carl Orthlieb) (08/17/90)

In article <15253@imagen.UUCP> isaak@imagen.UUCP (Mark Isaak) writes:
>The simplest solution otherwise is to specify "0 setlinewidth", which
>will give a nice-looking one-pixel-wide line at all angles.  For slightly
>wider diagonals, some combination of endpoint rounding and/or width
>correction may be desirable.
>
>in article <10244@pt.cs.cmu.edu>, tgl@zog.cs.cmu.edu (Tom Lane) says:
>>   2. In the book "Real World PostScript" (Roth, ed.), Michael Fryd
>> recommends rounding endpoint coordinates to nearest-integer-plus-0.25
>> in device space, whereas the green book just shows rounding to nearest
>> integer.  Which of these is better, and why?
>
>For horizontal and vertical lines, it shouldn't matter in theory, although
>adding +0.25 might avoid some obscure round-off errors in practice  (That
>last part is just a guess).  I'm not sure how the difference would affect
>diagonal lines.

The "Real World PostScript" book is correct. If straight rounding is
performed, lines will be biased to be an even number of pixels wide. If
rounding + 0.5 is performed, lines will be biased towards an odd number of
pixels. Rounding + 0.25 ensures an even (no pun intended) distribution of
even and odd pixel line weights.

0 setlinewidth is discouraged because it is device dependent, lines will
be different point sizes on printers with different resolutions.

To obtain a one-pixel wide line on a 300 dpi device, use the round + 0.25
method and specify 0.12 setlinewidth. This will give you wider diagonals and
maintain a 1 pixel width on horizontal and vertical lines.

Carl 8-)