[OpenJDK Rasterizer] [OpenJDK 2D-Dev] Path2d needRoom very slow for huge paths

Jim Graham james.graham at oracle.com
Mon Apr 6 22:38:25 UTC 2015



On 4/4/15 3:15 AM, Laurent Bourgès wrote:
> I may look at this implementation to get some idea or maybe I should
> just optimize openjdk's Area class that has also a clipping
> implementation but is known too be very slow as the shape complexity
> increases !

The Area class is mostly a red herring.  It doesn't usually get involved 
in basic rendering of shapes.  In particular:

- rectangular clips never touch it
- shaped clips never touch it as long as they are the only clip involved
- clipping to multiple shapes at least one of which is non-rectangular 
will involve Area only in bookkeeping in the SG2D class and the Area 
class won't be necessary for the rendering phase at all

Basically SG2D has an API contract for getClip() to satisfy.  It has 
been returning geometrical representations of the intersection of all 
clips, in user space.  For actual rendering we boil the shapes down into 
a Region object which is a list of pixel rectangles.  If all clip() 
calls are subclasses of Rectangle2D then we just do basic Rectangle2D 
intersections which are fast and we compute the pixels that fall within 
that geometric rectangle and save that as a Region.  If there is a 
single call to clip() or setClip() with a non-rectangular shape then we 
just save (a copy of) that Shape for returning from getClip() and we 
rasterize it to a Region for actual clipping.

Only when someone does something like "setClip(circle); clip(other 
shape);" do we use Area to compute the intersection of the two once, 
then we remember that geometry for getClip() and we rasterize it to a 
Region for the rendering routines.

Either way, that is likely orthogonal to the issue that many of the 
applications here face which is that they use basic rectangular clipping 
(or even if they use just a single shape for clipping) and then they 
render huge paths that are zoomed in and we perform computation in the 
dasher/stroker that doesn't affect the pixels rendered.  That has 
nothing to do with the work that we did with Area.

Still, there is an optimization to be made there.  I'm not sure if I 
ever submitted a bug against it, but the use of Area is only to 
facilitate getClip() and even then, it is only to facilitate iterating 
the geometry of that return value.  That call is extremely rarely used 
and when it is used, it is often to save the clip so it can be set back 
on the graphics, but the caller never independently iterates its 
geometry.  To that end, we could modify SG2D's clipping code to perform 
Region intersections instead and simply save copies of all input clips 
and lazily compute the geometry only if someone actually retrieved and 
iterated it manually.  Even setClip(getClip()) could avoid Area by 
simply returning an "IntersectedList" object that listed all of the 
component shapes - it would use Area only if someone called 
getPathIterator() on it - and when we see it again in setClip() we 
simply extract its component shapes rather than reading its geometry.

But, again, that optimization is independent of dasher/stroker doing 
extra work on a zoomed shape.

> Jim, could you point all clipping implentations in openjdk (java or c)
> that I can study to create my own ?

For filled paths, the Pisces and Marlin renderers already deal with 
clipping in the Renderer, but it is at the addLine() level.  For a quad 
or cubic that is entirely above or below or to the right of the clip we 
could just return early from addQuad and addCubic.  For curves to the 
left, we only need to insert a token line between the endpoints so that 
the line scanning code can determine the correct winding count as 
segments enter the clip from the left.  It won't save much on memory 
since all of the resulting addLine() calls from those addQuad() and 
addCubic() calls get dropped on the floor anyway, so this just saves 
computation.

But, for stroked paths, we ignore the clip entirely as we dash and widen 
the path and for these use cases of extremely large paths or complicated 
paths that might be viewed under a large scale we end up doing a lot of 
unnecessary computation and generate intermediate path segments for a 
lot of segments that never appear.  I'm not sure that existing clipping 
code applies, but the following may prove helpful:

- ShapeSpanIterator.c has a non-AA rasterizer that does early 
elimination in the subdivideQuad/Cubic functions
- DrawPath.c and FillPath.c use ProcessPath.c - I've never been a fan of 
the complexity in this renderer since complexity often correlates with 
missing something in the bigger picture, but I'll note that it also 
eliminates quads and cubics early as it iterates through them.  Also, 
this rendering engine only deals with thin 1-pixel strokes, it doesn't 
handle wide lines and paths.
- DrawLine.c and LineUtils.h contain some basic outcode logic for single 
bresenham line segments

				...jim


More information about the graphics-rasterizer-dev mailing list