[OpenJDK 2D-Dev] X11 uniform scaled wide lines and dashed lines; STROKE_CONTROL in Pisces

Denis Lila dlila at redhat.com
Mon Aug 2 22:17:10 UTC 2010


Hi Jim.

I implemented STROKE_CONTROL today. I used the intermediate 
NormalizingPathIterator, instead of implementing flattening in
pisces, because I wanted to get something working asap, and this
would be the easiest way.

The webrev is http://icedtea.classpath.org/~dlila/webrevs/fpWithStrokeControl/webrev/

I guess I'm not asking that you take a look at it, because it's probably
not the way we're going to end up doing things, but I wrote it, so
I'm sending the link just in case anyone wants to see it.

The webrev is big because it includes the floating point conversion, but all the
STROKE_CONTROL changes are in PiscesRenderingEngine.java.

Thanks,
Denis.

----- "Jim Graham" <james.graham at oracle.com> wrote:

> Hi Denis,
> 
> It would be ill-advised to normalize the coordinates after flattening.
> 
> The quality would be really bad.
> 
> Perhaps this is a good reason to start looking at updating Pisces to 
> take curves and flatten at the lowest level?
> 
> Or, I suppose we could get a non-flattened iterator from the source 
> path, send it through a "normalizing" filter, and then through a 
> flattening filter (the way many of the existing objects do flattening
> is 
> to just get their regular iterator and run it through an instance of 
> FlatteningPathIterator so we could do this manually with an
> intervening 
> "NormalizingPathIterator" if normalization is needed)...
> 
> 			...jim
> 
> Denis Lila wrote:
> > Hello Jim.
> > 
> > Thanks for that. I'll get to work on implementing it.
> > 
> > One thing though, about normalizing the control points of bezier
> > curves: pisces never gets any bezier curves as input. It only gets
> > lines that are the product of flattening bezier curves.
> > 
> > Pisces receives its input from flattening path iterators which get
> it
> > from other path iterators. Of course we can't require these to send
> out
> > normalized points. In order to normalize the control points we need
> to
> > be able to look at the bezier curves in Pisces, so we can't just
> take
> > all the input from the flattener. However, pisces can't handle
> curves
> > (yet, hopefully), so after the normalization, they must be
> flattened, and
> > this is the problem. I think it's a pretty good idea to do this by 
> > storing the input form the iterator into pisces (after
> normalization), 
> > creating a nameless path iterator that just iterates through all
> that,
> > and using this iterator to create a flattening iterator, which then
> > is used as before.
> > 
> > Does anyone have any other ideas?
> > 
> > Thank you,
> > Denis.
> > 
> > 
> > ----- "Jim Graham" <james.graham at oracle.com> wrote:
> > 
> >> For AA this is exactly what we do (round to nearest pixel centers
> for
> >>
> >> strokes).  Note that this is done prior to any line widening code
> is 
> >> executed.
> >>
> >> For non-AA we normalize coordinates to, I believe the (0.25, 0.25)
> 
> >> sub-pixel location.  This is so that the transitions between
> widening
> >> of 
> >> lines occurs evenly (particularly for horizontal and vertical wide
> 
> >> lines).  If you round to pixel edges then you have the following 
> >> progression (note that the line width grows by half on either side
> of
> >>
> >> the original geometry so you have to consider the "line widths"
> where
> >>
> >> you encounter the pixel centers to your left and right (or above
> and 
> >> below) which govern when that column (or row) of pixels first
> turns
> >> on):
> >>
> >> width 0.00 => 0.99      nothing drawn (except we kludge this)
> >> width 1.00 => 1.00      1 pixel wide (col to left turns on)
> >> width 1.01 => 2.99      2 pixels wide (col to right turns on)
> >> width 3.00 => 3.00      3 pixels wide (etc.)
> >> width 3.01 => 4.99      4 pixels wide
> >>
> >> Note that it is nearly impossible to get an odd-width line.  You 
> >> basically have to have exactly an integer width to get an odd-width
> 
> >> line.  This is because at the odd widths you reach the "half pixel"
> 
> >> locations on both sides of the line at the same time.  Due to the 
> >> "half-open" insideness rules only one of the pixels will be chosen
> to
> >> be 
> >> inside this path.  Just below these sizes and you fail to hit
> either 
> >> pixel center.  Just at the integer size you reach both pixel
> centers
> >> at 
> >> the same time.  Just slightly larger than that width and now you've
> 
> >> fully enclosed both pixel centers and the line width has to
> increase
> >> by 
> >> nearly 2.0 until you reach the next pixel centers.
> >>
> >> (The kludge I talk about above is that we set a minimum pen width
> so 
> >> that we never fail to draw a line even if the line width is set to
> >> 0.0, 
> >> but the above table was a theoretical description of the absolute
> >> rules.)
> >>
> >> If we rounded them to pixel centers, then the transitions look
> like
> >> this:
> >>
> >> width 0.00 => 0.00      nothing drawn (modulo kludge)
> >> width 0.01 => 1.99      1 pixel wide (column you are in turns on)
> >> width 2.00 => 2.00      2 pixels wide (column to left turns on)
> >> width 2.01 => 3.99      3 pixels wide (column to right turns on)
> >> width 4.00 => 4.00      4 pixels wide (etc.)
> >> width 4.01 => 5.99      5 pixels wide
> >>
> >> We have a similar effect as above, but biased towards making even
> line
> >>
> >> widths harder.
> >>
> >> So, by locating lines at (0.25, 0.25) subpixel location we end up
> with
> >> a 
> >>   very even progression:
> >>
> >> width 0.00 => 0.50      nothing drawn (modulo kludge)
> >> width 0.51 => 1.50      1 pixel wide (column you are in turns on)
> >> width 1.51 => 2.50      2 pixel wide (column to left gets added)
> >> width 2.51 => 3.50      3 pixel wide (column to right gets added)
> >> width 3.51 => 4.50      4 pixel wide (etc.)
> >>
> >> This gives us nice even and gradual widening of the lines as we
> >> increase 
> >> the line width by sub-pixel amounts and the line widths are fairly
> 
> >> stable around integer widths.
> >>
> >> Also, note that we don't say "when stroking" as you might want to 
> >> normalize both strokes and fills so that they continue to match.  I
> 
> >> believe that we normalize both strokes and fills for non-AA and we
> >> only 
> >> normalize strokes for AA (and leave AA fills as "pure").  AA is
> less 
> >> problematic with respect to creating gaps if your stroke and fill 
> >> normalization are not consistent.
> >>
> >> The rounding equations are along the lines of:
> >>
> >> 	v = Math.floor(v + rval) + aval;
> >>
> >> For center of pixel you use (rval=0.0, aval=0.5)
> >> For 0.25,0.25 rounding use  (rval=0.25, aval=0.25)
> >> For edge of pixel you use   (rval=0.5, aval=0.0)
> >>
> >> Also, we came up with an interesting way of adjusting the control
> >> points 
> >> of quads and cubics if we adjusted their end points, but I don't
> know
> >> if 
> >> what we did was really the best idea.  For quads we adjust the
> control
> >>
> >> point by the average of the adjustments that we applied to its 2
> end 
> >> points.  For cubics, we move the first control point by the same
> >> amount 
> >> as we moved the starting endpoint and the second control point by
> the
> >>
> >> amount we moved the final endpoint.  The jury is out on whether
> that
> >> is 
> >> the most aesthetic technique...
> >>
> >> 			...jim
> >>
> >> Denis Lila wrote:
> >>> Regarding VALUE_STROKE_NORMALIZE the API says:
> >>>                 Stroke normalization control hint value --
> geometry
> >> should
> >>>                 be normalized to improve uniformity or spacing of
> >> lines and
> >>>                 overall aesthetics. Note that different
> >> normalization 
> >>>                 algorithms may be more successful than others for
> >> given 
> >>>                 input paths. 
> >>>
> >>> I can only think of one example where VALUE_STROKE_NORMALIZE makes
> a
> >> visible
> >>> difference between the closed source implementation and OpenJDK:
> >>> when drawing anti-aliased horizontal or vertical lines of width
> 1,
> >> Pisces 
> >>> draws a 2 pixel wide line with half intensity (because integer
> >> coordinates
> >>> are between pixels). Sun's jdk with VALUE_SROKE_NORMALIZE turned
> on
> >> draws
> >>> a 1 pixel line with full intensity. This could to achieved by
> just
> >>> checking for normalization and rounding coordinates to the
> nearest
> >> half 
> >>> pixel, but this solution seems too simple, and I'm not sure
> whether
> >> I'm missing
> >>> anything. It would also probably cause problems when drawing
> >> anti-aliased 
> >>> short lines (which is done when drawing any sort of curve)
> >>> Unless, of course, this rounding was restricted to just
> horizontal
> >> and 
> >>> vertical lines.
> >>>
> >>> Regards,
> >>> Denis.



More information about the 2d-dev mailing list