Bug 23065 - Enable incremental painting for canvas
Summary: Enable incremental painting for canvas
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Layout and Rendering (show other bugs)
Version: 528+ (Nightly build)
Hardware: Mac OS X 10.5
: P2 Normal
Assignee: Simon Fraser (smfr)
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2009-01-01 11:57 PST by Simon Fraser (smfr)
Modified: 2009-01-01 18:07 PST (History)
3 users (show)

See Also:


Attachments
Patch, testcase, changelog. (17.73 KB, patch)
2009-01-01 12:19 PST, Simon Fraser (smfr)
darin: review+
Details | Formatted Diff | Diff
Final patch (18.88 KB, patch)
2009-01-01 16:22 PST, Simon Fraser (smfr)
oliver: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Simon Fraser (smfr) 2009-01-01 11:57:12 PST
Whenever something draws in a <canvas>, the entire canvas is repainted. This makes drawing with large canvas elements very slow.

There was an attempt to fix this in <http://trac.webkit.org/changeset/29448>, but it was turned off.
Comment 1 Simon Fraser (smfr) 2009-01-01 12:15:27 PST
<rdar://problem/6151191>
Comment 2 Simon Fraser (smfr) 2009-01-01 12:19:01 PST
Created attachment 26350 [details]
Patch, testcase, changelog.
Comment 3 Darin Adler 2009-01-01 12:59:49 PST
Comment on attachment 26350 [details]
Patch, testcase, changelog.

> -const char* defaultFont = "10px sans-serif";
> +static const char* defaultFont = "10px sans-serif";

This is still not doing what the original author probably intended. This is a variable, not a constant. It should be:

    static const char defaultFont[] = 10px sans-serif";

or

    static const char* const defaultFont = 10px sans-serif";

> +class CanvasStrokeStyleApplier : public StrokeStyleApplier {
> +public:
> +    CanvasStrokeStyleApplier(CanvasRenderingContext2D* canvasContext)
> +        : m_canvasContext(canvasContext)
> +    {
> +    }
> +    
> +    virtual void strokeStyle(GraphicsContext* c)
> +    {
> +        c->setStrokeThickness(m_canvasContext->lineWidth());
> +
> +        LineCap cap;
> +        if (parseLineCap(m_canvasContext->lineCap(), cap))
> +            c->setLineCap(cap);
> +
> +        LineJoin join;
> +        if (parseLineJoin(m_canvasContext->lineJoin(), join))
> +            c->setLineJoin(join);
> +
> +        c->setMiterLimit(m_canvasContext->miterLimit());
> +    }
> +
> +    CanvasRenderingContext2D* m_canvasContext;
> +};

Both strokeStyle and m_canvasContext should be private, not public. Because ... because why not? I say always make things private unless they're needed publicly.

> -void CanvasRenderingContext2D::willDraw(const FloatRect& r)
> +void CanvasRenderingContext2D::willDraw(const FloatRect& r, bool applyTransform, bool applyShadow, bool applyClip)

Functions that take a bunch of booleans make the code hard to read. This should either use an enum for each boolean or a flags word. Either is easier to read at the call site because of the use of named constants.

> +    if (fill)
> +        m_canvas->willDraw(textRect);
> +    else
> +        // When stroking text, pointy miters can extend outside of textRect, so we
> +        // punt and dirty the whole canvas.
> +        m_canvas->willDraw(FloatRect(0, 0, m_canvas->width(), m_canvas->height()));

Need braces around multi-line else body here (number of lines includes things like comments).

Would be better to have a helper function for "will draw the entire canvas". Code that constructs the rectangle is confusing.

>      CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_strokeStyle.get();
>      if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) {
> +        // FIXME The rect is not big enough for miters on stroked text

We should have a colon after the FIXME. We should have a period at the end of the sentence.

I don't understand why it's OK to check in with this FIXME. Will this result in incorrect canvas drawing?

> +// Map rect r from srcRect to an equivalent rect in destRect

Sentence-capitalized comment needs a period.

> +inline FloatRect mapRect(const FloatRect& r, const FloatRect& srcRect, const FloatRect& destRect)
> +{
> +    float widthScale = destRect.width() / srcRect.width();
> +    float heightScale = destRect.height() / srcRect.height();
> +    return FloatRect(destRect.x() + (r.x() - srcRect.x()) * widthScale,
> +                     destRect.y() + (r.y() - srcRect.y()) * heightScale,
> +                     r.width() * widthScale, r.height() * heightScale);
> +}

Why is this inline? Is the behavior with 0 width and 0 height acceptable?

Does this really work reliably? Our previous attempts to turn this on have had bad results. Is the test you added extensive enough to prove we won't have trouble? Did you test with the sites that exercise canvas heavily, like the one that does the 3D Doom-style game?

I'm going to say r=em, because none of these comments seem like showstoppers to me
Comment 4 Sam Weinig 2009-01-01 13:57:58 PST
+        LineCap cap;
+        if (parseLineCap(m_canvasContext->lineCap(), cap))
+            c->setLineCap(cap);
+
+        LineJoin join;
+        if (parseLineJoin(m_canvasContext->lineJoin(), join))
+            c->setLineJoin(join);

It seems a little silly to call these parse methods here, since we store the LineCap and LineJoin data internally already.  For instance, m_canvasContext->lineCap() calls lineCapName(state().m_lineCap) for the sake of the API.  We can just add accessors that get at the data directly to avoid this round trip.
Comment 5 Simon Fraser (smfr) 2009-01-01 13:58:45 PST
(In reply to comment #3)
> (From update of attachment 26350 [details] [review])

> >      CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_strokeStyle.get();
> >      if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) {
> > +        // FIXME The rect is not big enough for miters on stroked text
> 
> We should have a colon after the FIXME. We should have a period at the end of
> the sentence.
> 
> I don't understand why it's OK to check in with this FIXME. Will this result in
> incorrect canvas drawing?

This is not a new issue with this patch; it's an existing problem. I filed bug 23067.
Comment 6 Simon Fraser (smfr) 2009-01-01 15:51:13 PST
Test sites:
http://jsmsxdemo.googlepages.com/jsmsx.html
http://caimansys.com/painter/
Comment 7 Simon Fraser (smfr) 2009-01-01 16:22:29 PST
Created attachment 26353 [details]
Final patch

Final patch for reference.
Comment 8 Oliver Hunt 2009-01-01 16:30:41 PST
Comment on attachment 26353 [details]
Final patch

Instead of <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> use <!DOCTYPE html>

otherwise r=me!
Comment 9 Simon Fraser (smfr) 2009-01-01 18:07:11 PST
Committing to http://svn.webkit.org/repository/webkit/trunk ...
	M	LayoutTests/ChangeLog
	A	LayoutTests/fast/canvas/canvas-incremental-repaint.html
	A	LayoutTests/platform/mac/fast/canvas/canvas-incremental-repaint-expected.checksum
	A	LayoutTests/platform/mac/fast/canvas/canvas-incremental-repaint-expected.png
	A	LayoutTests/platform/mac/fast/canvas/canvas-incremental-repaint-expected.txt
	M	WebCore/ChangeLog
	M	WebCore/html/CanvasRenderingContext2D.cpp
	M	WebCore/html/CanvasRenderingContext2D.h
	M	WebCore/html/HTMLCanvasElement.cpp
	M	WebCore/platform/graphics/FloatRect.cpp
	M	WebCore/platform/graphics/FloatRect.h
Committed r39538