Bad performance with Canvas and extensive clipping

Jim Graham james.graham at
Tue May 27 23:00:02 UTC 2014

I've filed RT-37300 for this:


On 5/27/14 3:54 PM, Jim Graham wrote:
> Hi Tom,
> There are 2 upgrades to consider.  One involves new API, but is probably
> best in the long run.
> Without API, we'd have to detect if the path were rectangular in the
> processing of the CLIP command in  If the 4 coordinates
> are an axis aligned rectangle on integer coordinates then we could
> special case that with g.setClipRect().  There are other considerations,
> such as:
> - If there is already a soft non-rect clip, then it should probably not
> bother with the special case since it won't simplify anything.
> - If we have special cased the rectangle, then we must track that across
> save/restore properly.
> - If we have a special case cliprect and then we get a non-special case
> rect as the argument of a CLIP command, then we need to resolve it into
> a singular case (most likely default back to soft clipping).
> - The processing that tries to detect "are they clearing the entire
> buffer" needs to be aware of any clip in effect - those tests are done
> at the javafx.scene.canvas.GraphicsContext level.
> We could put that fix in with no new API so it could go in as soon as we
> are satisfied with its stability.
> If we want to add new API, so that you don't have to construct a path
> every time you want to do clipRect() and we don't have to decipher your
> path to figure out that it is a rectangle, then we would have to wait
> for the next opportunity to add API (FX can add API in between major JDK
> releases, but there is a process to go through and I don't think we can
> do it for 8u20 any more).  The process for that would be:
> javafx.scene.canvas.GraphicsContext would need a new method that would
> take the rectangular clipping parameters and put them into the buffer.
> The existing fillRect() method would provide a good template.  A new
> "command code constant" would have to be added to represent "This is a
> clip rectangle request".
> NGCanvas would then need to digest the new buffer commands and I believe
> that the existing Prism call g.setClipRect() would work to enable the
> scissor clip (fast rectangular clipping).
> The question is what is the proper API?  If we have it take doubles,
> would that imply to developers that there would be soft clipping of the
> edges similar to if you used a rectangular path and clip()?  Right now
> Node.setClipNode(Rectangle) will do the fast scissor clip
> (g.setClipRect()) if the coordinates fall on integer axis-aligned
> coordinates, but it will do soft-edged clipping if there is
> rotation/skewing, or the coordinates are not integers.  That would
> probably be the best API to mimic since HTML5 doesn't have a similar
> "cliprect" method...
>              ...jim
> On 5/27/14 2:57 PM, Tom Schindl wrote:
>> Is there anything I could do to help getting rectangular clipping into
>> JavaFX - I tried to find my way through the sources but I'm not sure I
>> have enough knowledge to provide a patch in this area.
>> BTW it looks like I'm not alone with the clipping performance problem
>> see
>> Tom
>> On 27.05.14 23:47, Jim Graham wrote:
>>> Canvas is, essentially, a "draw pixels" mechanism.  We have to bundle
>>> the requests into a command stream due to threading issues, but when the
>>> requests get to the render thread then they get turned into pixels so
>>> the command stream is a temporary intermediary.  Some of the hw J2D
>>> pipelines also have a temporary command stream due to platform threading
>>> issues as well.  It all depends on which pipeline you use and on which
>>> platform in the case of J2D.  FX simply normalized the threading on all
>>> pipelines/platforms so that we have a separate UI and render thread in
>>> all cases, but that concept is not foreign to J2D either.
>>> I'm fairly certain that the lack of simple rectangular clipping is
>>> probably the biggest cause of your performance problems.  We do AA on
>>> everything in FX, though, whereas rendering to a BufferedImage by
>>> default will be non-AA unless you requested AA using the graphics hints.
>>>   But on the up-side, we hw accelerate just about every operation in FX
>>> so it should be on par with performance there, modulo the lack of
>>> rectangular clipping...
>>>              ...jim
>>> On 5/23/14 5:46 PM, Tom Schindl wrote:
>>>> Hi,
>>>> As an experiment I've now written a SWT-GC implementation using a
>>>> BufferedImage & Graphics2D and transfering the pixels over to JavaFX
>>>> and
>>>> the performance is as it is with native SWT.
>>>> I always thought Canvas works similar to Image and one only draws
>>>> pixels
>>>> - looks like that is not the case, having a dep in my application
>>>> java.awt is not what I'm aiming at but without acceptable
>>>> performance in
>>>> conjunction with clipping it looks like i have to go this route :-(
>>>> Tom
>>>> On 23.05.14 23:57, Tom Schindl wrote:
>>>>> In the current usecase it is a rect all time but that's just in this
>>>>> special use case.
>>>>> I guess that rect clipping is the most common one so having an
>>>>> optimization for rects and a slow path for none rects might help.
>>>>> Tom
>>>>> Von meinem iPhone gesendet
>>>>>> Am 23.05.2014 um 23:35 schrieb Jim Graham <james.graham at>:
>>>>>> Are you clipping to an arbitrary path in all cases or just a
>>>>>> rectangle?  Unfortunately we only offer the arbitrary
>>>>>> clip-to-current-path method that isn't optimized for basic
>>>>>> rectangular clipping and it implements soft clipping.
>>>>>> There is an outstanding tweak that we added faster clipping support
>>>>>> for WebNode and we need to start using it for
>>>>>> Node.setClipNode(non-rectangle) and Canvas, but we haven't
>>>>>> implemented that yet.
>>>>>> (  It basically is a
>>>>>> direct "render this texture through that other texture as a clip"
>>>>>> operation instead of the current code that runs it through some
>>>>>> Blend effect filters.  It would definitely improve your run times,
>>>>>> but I'm not sure how much.
>>>>>> Even more savings could be had for rectangular clips if we provided
>>>>>> some way to communicate them to the GC...
>>>>>>              ...jim
>>>>>>> On 5/23/14 11:47 AM, Tom Schindl wrote:
>>>>>>> Hi,
>>>>>>> Maybe as some of you might know I've been working since sometime on
>>>>>>> SWT
>>>>>>> on JavaFX and to implement direct drawing operations we use
>>>>>>> JavaFX-Canvas.
>>>>>>> I've today tried to run a heavy direct drawing grid
>>>>>>> implementation and
>>>>>>> it performed very bad because it makes heavy use of clipping.
>>>>>>> For a grid I've counted ~1500 clipping operations the library works
>>>>>>> something like this:
>>>>>>> boolean activeClip;
>>>>>>> Canvas canvas = new Canvas();
>>>>>>> public void setClipping(PathIterator pathIterator) {
>>>>>>>     GraphicsContext gc = canvas.getGraphicsContext2D();
>>>>>>>     if(activeClip) {
>>>>>>>       gc.restore();
>>>>>>>       activeClip= false;
>>>>>>>     }
>>>>>>>     if( pathIterator == null ) {
>>>>>>>       return;
>>>>>>>     }
>>>>>>>     activeClip = true;
>>>>>>>     float coords[] = new float[6];
>>>>>>>          gc.beginPath();
>>>>>>>          float x = 0;
>>>>>>>          float y = 0;
>>>>>>>          gc.moveTo(0, 0);
>>>>>>>          while( ! pathIterator.isDone() ) {
>>>>>>>              switch (pathIterator.currentSegment(coords)) {
>>>>>>>              case PathIterator.SEG_CLOSE:
>>>>>>>                  gc.lineTo(x, y);
>>>>>>>                  break;
>>>>>>>              case PathIterator.SEG_CUBICTO:
>>>>>>>                  gc.bezierCurveTo(coords[0], coords[1], coords[2],
>>>>>>> coords[3],
>>>>>>> coords[4], coords[5]);
>>>>>>>                  break;
>>>>>>>              case PathIterator.SEG_LINETO:
>>>>>>>                  gc.lineTo(coords[0], coords[1]);
>>>>>>>                  break;
>>>>>>>              case PathIterator.SEG_MOVETO:
>>>>>>>                  gc.moveTo(coords[0], coords[1]);
>>>>>>>                  x = coords[0];
>>>>>>>                  y = coords[1];
>>>>>>>                  break;
>>>>>>>              case PathIterator.SEG_QUADTO:
>>>>>>>                  gc.quadraticCurveTo(coords[0], coords[1],
>>>>>>> coords[2], coords[3]);
>>>>>>>                  break;
>>>>>>>              default:
>>>>>>>                  break;
>>>>>>>              }
>>>>>>>    ;
>>>>>>>          }
>>>>>>>          gc.clip();
>>>>>>>          gc.closePath();
>>>>>>> }
>>>>>>> Am I doing something ultimately wrong, totally wrong? Has anyone an
>>>>>>> idea
>>>>>>> how I would work around the problem?
>>>>>>> Tom

More information about the openjfx-dev mailing list