Title: [117106] trunk/Source/WebCore
Revision
117106
Author
[email protected]
Date
2012-05-15 11:43:21 -0700 (Tue, 15 May 2012)

Log Message

Optimize save/restore with no drawing operations between them (shows up on some canvas benchmarks)
https://bugs.webkit.org/show_bug.cgi?id=86448

Reviewed by Simon Fraser.

Since canvas programmers sometimes end up doing extra save/restore pairs with no actual drawing
in between, optimize that case by not realizing saves until we have to. This is doubly important
because saves are costly.

* html/canvas/CanvasRenderingContext2D.cpp:
(WebCore::CanvasRenderingContext2D::CanvasRenderingContext2D): Initialize m_unrealizedSaveCount.
(WebCore::CanvasRenderingContext2D::isAccelerated): Improve performance by calling the
drawingContext function only once.
(WebCore::CanvasRenderingContext2D::reset): Set m_unrealizedSaveCount to 0.
(WebCore::CanvasRenderingContext2D::realizeSavesLoop): Added. Replaces the save function as the
function that actually pushes context onto the state stack.
(WebCore::CanvasRenderingContext2D::restore): Added code to do an early return if we can do the
restore just by decrementing the unrealized save count.
(WebCore::CanvasRenderingContext2D::setStrokeStyle): Added calls to realizeSaves and replaced
calls to state with calls to modifiableState.
(WebCore::CanvasRenderingContext2D::setFillStyle): Ditto.
(WebCore::CanvasRenderingContext2D::setLineWidth): Ditto. Also added an early out for cases where
the line width is not changing.
(WebCore::CanvasRenderingContext2D::setLineCap): Ditto.
(WebCore::CanvasRenderingContext2D::setLineJoin): Ditto.
(WebCore::CanvasRenderingContext2D::setMiterLimit): Ditto.
(WebCore::CanvasRenderingContext2D::setShadowOffsetX): Ditto.
(WebCore::CanvasRenderingContext2D::setShadowOffsetY): Ditto.
(WebCore::CanvasRenderingContext2D::setShadowBlur): Ditto.
(WebCore::CanvasRenderingContext2D::setShadowColor): Ditto.
(WebCore::CanvasRenderingContext2D::setWebkitLineDash): Ditto.
(WebCore::CanvasRenderingContext2D::setWebkitLineDashOffset): Ditto.
(WebCore::CanvasRenderingContext2D::setGlobalAlpha): Ditto.
(WebCore::CanvasRenderingContext2D::setGlobalCompositeOperation): Ditto.
(WebCore::CanvasRenderingContext2D::scale): Ditto.
(WebCore::CanvasRenderingContext2D::rotate): Ditto.
(WebCore::CanvasRenderingContext2D::translate): Ditto.
(WebCore::CanvasRenderingContext2D::transform): Ditto.
(WebCore::CanvasRenderingContext2D::setTransform): Ditto.
(WebCore::CanvasRenderingContext2D::setStrokeColor): Ditto.
(WebCore::CanvasRenderingContext2D::setFillColor): Ditto.
(WebCore::CanvasRenderingContext2D::clip): Ditto.
(WebCore::CanvasRenderingContext2D::clearRect): Changed implementation so it does not save
the graphics context in the common case where shadows, global alpha, and global compositing
operators do not interfer with the function's operation. This allowed us to get rid of the
setAllAttributesToDefault function, which was used nowhere else.
(WebCore::CanvasRenderingContext2D::setShadow): Added a new common bottleneck and made all
the setShadow functions call it.
(WebCore::CanvasRenderingContext2D::clearShadow): Changed to call the new setShadow.
(WebCore::CanvasRenderingContext2D::setFont): Renamed tempDecl to the more friendly
parsedStyle. Changed code structure so the CSS parser is deleted right after parsing is
done. Used string concatenation instead of more expensive string append. Added calls to
realizeSaves and modifiableState.
(WebCore::CanvasRenderingContext2D::setTextAlign): Added calls to realizeSaves and replaced
calls to state with calls to modifiableState. Also added an early out for cases where the
alignment is not changing.
(WebCore::CanvasRenderingContext2D::setTextBaseline): Ditto.

* html/canvas/CanvasRenderingContext2D.h: Made save an inline function that bumps
m_unrealizedSaveCount. Removed setAllAttributesToDefault. Added OVERRIDE for all the
virtual function overrides, and made them all private. Moved m_path down with the other data
members. Renamed the non-const state function to modifiableState. Added a realizeSaves
function and the realizeSavesLoop for the unusual case where there is work to do.
Added m_unrealizedSaveCount.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (117105 => 117106)


--- trunk/Source/WebCore/ChangeLog	2012-05-15 18:31:16 UTC (rev 117105)
+++ trunk/Source/WebCore/ChangeLog	2012-05-15 18:43:21 UTC (rev 117106)
@@ -1,3 +1,70 @@
+2012-05-15  Darin Adler  <[email protected]>
+
+        Optimize save/restore with no drawing operations between them (shows up on some canvas benchmarks)
+        https://bugs.webkit.org/show_bug.cgi?id=86448
+
+        Reviewed by Simon Fraser.
+
+        Since canvas programmers sometimes end up doing extra save/restore pairs with no actual drawing
+        in between, optimize that case by not realizing saves until we have to. This is doubly important
+        because saves are costly.
+
+        * html/canvas/CanvasRenderingContext2D.cpp:
+        (WebCore::CanvasRenderingContext2D::CanvasRenderingContext2D): Initialize m_unrealizedSaveCount.
+        (WebCore::CanvasRenderingContext2D::isAccelerated): Improve performance by calling the
+        drawingContext function only once.
+        (WebCore::CanvasRenderingContext2D::reset): Set m_unrealizedSaveCount to 0.
+        (WebCore::CanvasRenderingContext2D::realizeSavesLoop): Added. Replaces the save function as the
+        function that actually pushes context onto the state stack.
+        (WebCore::CanvasRenderingContext2D::restore): Added code to do an early return if we can do the
+        restore just by decrementing the unrealized save count.
+        (WebCore::CanvasRenderingContext2D::setStrokeStyle): Added calls to realizeSaves and replaced
+        calls to state with calls to modifiableState.
+        (WebCore::CanvasRenderingContext2D::setFillStyle): Ditto.
+        (WebCore::CanvasRenderingContext2D::setLineWidth): Ditto. Also added an early out for cases where
+        the line width is not changing.
+        (WebCore::CanvasRenderingContext2D::setLineCap): Ditto.
+        (WebCore::CanvasRenderingContext2D::setLineJoin): Ditto.
+        (WebCore::CanvasRenderingContext2D::setMiterLimit): Ditto.
+        (WebCore::CanvasRenderingContext2D::setShadowOffsetX): Ditto.
+        (WebCore::CanvasRenderingContext2D::setShadowOffsetY): Ditto.
+        (WebCore::CanvasRenderingContext2D::setShadowBlur): Ditto.
+        (WebCore::CanvasRenderingContext2D::setShadowColor): Ditto.
+        (WebCore::CanvasRenderingContext2D::setWebkitLineDash): Ditto.
+        (WebCore::CanvasRenderingContext2D::setWebkitLineDashOffset): Ditto.
+        (WebCore::CanvasRenderingContext2D::setGlobalAlpha): Ditto.
+        (WebCore::CanvasRenderingContext2D::setGlobalCompositeOperation): Ditto.
+        (WebCore::CanvasRenderingContext2D::scale): Ditto.
+        (WebCore::CanvasRenderingContext2D::rotate): Ditto.
+        (WebCore::CanvasRenderingContext2D::translate): Ditto.
+        (WebCore::CanvasRenderingContext2D::transform): Ditto.
+        (WebCore::CanvasRenderingContext2D::setTransform): Ditto.
+        (WebCore::CanvasRenderingContext2D::setStrokeColor): Ditto.
+        (WebCore::CanvasRenderingContext2D::setFillColor): Ditto.
+        (WebCore::CanvasRenderingContext2D::clip): Ditto.
+        (WebCore::CanvasRenderingContext2D::clearRect): Changed implementation so it does not save
+        the graphics context in the common case where shadows, global alpha, and global compositing
+        operators do not interfer with the function's operation. This allowed us to get rid of the
+        setAllAttributesToDefault function, which was used nowhere else.
+        (WebCore::CanvasRenderingContext2D::setShadow): Added a new common bottleneck and made all
+        the setShadow functions call it.
+        (WebCore::CanvasRenderingContext2D::clearShadow): Changed to call the new setShadow.
+        (WebCore::CanvasRenderingContext2D::setFont): Renamed tempDecl to the more friendly
+        parsedStyle. Changed code structure so the CSS parser is deleted right after parsing is
+        done. Used string concatenation instead of more expensive string append. Added calls to
+        realizeSaves and modifiableState.
+        (WebCore::CanvasRenderingContext2D::setTextAlign): Added calls to realizeSaves and replaced
+        calls to state with calls to modifiableState. Also added an early out for cases where the
+        alignment is not changing.
+        (WebCore::CanvasRenderingContext2D::setTextBaseline): Ditto.
+
+        * html/canvas/CanvasRenderingContext2D.h: Made save an inline function that bumps
+        m_unrealizedSaveCount. Removed setAllAttributesToDefault. Added OVERRIDE for all the
+        virtual function overrides, and made them all private. Moved m_path down with the other data
+        members. Renamed the non-const state function to modifiableState. Added a realizeSaves
+        function and the realizeSavesLoop for the unusual case where there is work to do.
+        Added m_unrealizedSaveCount.
+
 2012-05-15  Tommy Widenflycht  <[email protected]>
 
         MediaStream API: Setting onended on a LocalMediaStream triggers an assertion in V8

Modified: trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp (117105 => 117106)


--- trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp	2012-05-15 18:31:16 UTC (rev 117105)
+++ trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp	2012-05-15 18:43:21 UTC (rev 117106)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
  * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies)
  * Copyright (C) 2007 Alp Toker <[email protected]>
  * Copyright (C) 2008 Eric Seidel <[email protected]>
@@ -115,6 +115,7 @@
 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode)
     : CanvasRenderingContext(canvas)
     , m_stateStack(1)
+    , m_unrealizedSaveCount(0)
     , m_usesCSSCompatibilityParseMode(usesCSSCompatibilityParseMode)
 #if ENABLE(DASHBOARD_SUPPORT)
     , m_usesDashboardCompatibilityMode(usesDashboardCompatibilityMode)
@@ -148,7 +149,10 @@
 bool CanvasRenderingContext2D::isAccelerated() const
 {
 #if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
-    return canvas()->hasCreatedImageBuffer() && drawingContext() && drawingContext()->isAcceleratedContext();
+    if (!canvas()->hasCreatedImageBuffer())
+        return false;
+    GraphicsContext* context = drawingContext();
+    return context && context->isAcceleratedContext();
 #else
     return false;
 #endif
@@ -160,6 +164,7 @@
     m_stateStack.resize(1);
     m_stateStack.first() = State();
     m_path.clear();
+    m_unrealizedSaveCount = 0;
 }
 
 CanvasRenderingContext2D::State::State()
@@ -259,18 +264,24 @@
     m_font.update(fontSelector);
 }
 
-void CanvasRenderingContext2D::save()
+void CanvasRenderingContext2D::realizeSavesLoop()
 {
+    ASSERT(m_unrealizedSaveCount);
     ASSERT(m_stateStack.size() >= 1);
-    m_stateStack.append(state());
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    c->save();
+    GraphicsContext* context = drawingContext();
+    do {
+        m_stateStack.append(state());
+        if (context)
+            context->save();
+    } while (--m_unrealizedSaveCount);
 }
 
 void CanvasRenderingContext2D::restore()
 {
+    if (m_unrealizedSaveCount) {
+        --m_unrealizedSaveCount;
+        return;
+    }
     ASSERT(m_stateStack.size() >= 1);
     if (m_stateStack.size() <= 1)
         return;
@@ -283,23 +294,6 @@
     c->restore();
 }
 
-void CanvasRenderingContext2D::setAllAttributesToDefault()
-{
-    state().m_globalAlpha = 1;
-    state().m_shadowOffset = FloatSize();
-    state().m_shadowBlur = 0;
-    state().m_shadowColor = Color::transparent;
-    state().m_globalComposite = CompositeSourceOver;
-
-    GraphicsContext* context = drawingContext();
-    if (!context)
-        return;
-
-    applyShadow();
-    context->setAlpha(1);
-    context->setCompositeOperation(CompositeSourceOver);
-}
-
 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
 {
     return state().m_strokeStyle.get();
@@ -323,12 +317,13 @@
     } else
         checkOrigin(style->canvasPattern());
 
-    state().m_strokeStyle = style.release();
+    realizeSaves();
+    modifiableState().m_strokeStyle = style.release();
     GraphicsContext* c = drawingContext();
     if (!c)
         return;
     state().m_strokeStyle->applyStrokeColor(c);
-    state().m_unparsedStrokeColor = String();
+    modifiableState().m_unparsedStrokeColor = String();
 }
 
 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
@@ -354,12 +349,13 @@
     } else
         checkOrigin(style->canvasPattern());
 
-    state().m_fillStyle = style.release();
+    realizeSaves();
+    modifiableState().m_fillStyle = style.release();
     GraphicsContext* c = drawingContext();
     if (!c)
         return;
     state().m_fillStyle->applyFillColor(c);
-    state().m_unparsedFillColor = String();
+    modifiableState().m_unparsedFillColor = String();
 }
 
 float CanvasRenderingContext2D::lineWidth() const
@@ -371,7 +367,10 @@
 {
     if (!(isfinite(width) && width > 0))
         return;
-    state().m_lineWidth = width;
+    if (state().m_lineWidth == width)
+        return;
+    realizeSaves();
+    modifiableState().m_lineWidth = width;
     GraphicsContext* c = drawingContext();
     if (!c)
         return;
@@ -388,7 +387,10 @@
     LineCap cap;
     if (!parseLineCap(s, cap))
         return;
-    state().m_lineCap = cap;
+    if (state().m_lineCap == cap)
+        return;
+    realizeSaves();
+    modifiableState().m_lineCap = cap;
     GraphicsContext* c = drawingContext();
     if (!c)
         return;
@@ -405,7 +407,10 @@
     LineJoin join;
     if (!parseLineJoin(s, join))
         return;
-    state().m_lineJoin = join;
+    if (state().m_lineJoin == join)
+        return;
+    realizeSaves();
+    modifiableState().m_lineJoin = join;
     GraphicsContext* c = drawingContext();
     if (!c)
         return;
@@ -421,7 +426,10 @@
 {
     if (!(isfinite(limit) && limit > 0))
         return;
-    state().m_miterLimit = limit;
+    if (state().m_miterLimit == limit)
+        return;
+    realizeSaves();
+    modifiableState().m_miterLimit = limit;
     GraphicsContext* c = drawingContext();
     if (!c)
         return;
@@ -437,7 +445,10 @@
 {
     if (!isfinite(x))
         return;
-    state().m_shadowOffset.setWidth(x);
+    if (state().m_shadowOffset.width() == x)
+        return;
+    realizeSaves();
+    modifiableState().m_shadowOffset.setWidth(x);
     applyShadow();
 }
 
@@ -450,7 +461,10 @@
 {
     if (!isfinite(y))
         return;
-    state().m_shadowOffset.setHeight(y);
+    if (state().m_shadowOffset.height() == y)
+        return;
+    realizeSaves();
+    modifiableState().m_shadowOffset.setHeight(y);
     applyShadow();
 }
 
@@ -463,7 +477,10 @@
 {
     if (!(isfinite(blur) && blur >= 0))
         return;
-    state().m_shadowBlur = blur;
+    if (state().m_shadowBlur == blur)
+        return;
+    realizeSaves();
+    modifiableState().m_shadowBlur = blur;
     applyShadow();
 }
 
@@ -474,8 +491,13 @@
 
 void CanvasRenderingContext2D::setShadowColor(const String& color)
 {
-    if (!parseColorOrCurrentColor(state().m_shadowColor, color, canvas()))
+    RGBA32 rgba;
+    if (!parseColorOrCurrentColor(rgba, color, canvas()))
         return;
+    if (state().m_shadowColor == rgba)
+        return;
+    realizeSaves();
+    modifiableState().m_shadowColor = rgba;
     applyShadow();
 }
 
@@ -486,8 +508,10 @@
 
 void CanvasRenderingContext2D::setWebkitLineDash(const DashArray& dash)
 {
-    state().m_lineDash = dash;
-
+    if (state().m_lineDash == dash)
+        return;
+    realizeSaves();
+    modifiableState().m_lineDash = dash;
     GraphicsContext* c = drawingContext();
     if (!c)
         return;
@@ -503,9 +527,10 @@
 {
     if (!isfinite(offset))
         return;
-
-    state().m_lineDashOffset = offset;
-
+    if (state().m_lineDashOffset == offset)
+        return;
+    realizeSaves();
+    modifiableState().m_lineDashOffset = offset;
     GraphicsContext* c = drawingContext();
     if (!c)
         return;
@@ -521,7 +546,10 @@
 {
     if (!(alpha >= 0 && alpha <= 1))
         return;
-    state().m_globalAlpha = alpha;
+    if (state().m_globalAlpha == alpha)
+        return;
+    realizeSaves();
+    modifiableState().m_globalAlpha = alpha;
     GraphicsContext* c = drawingContext();
     if (!c)
         return;
@@ -538,7 +566,10 @@
     CompositeOperator op;
     if (!parseCompositeOperator(operation, op))
         return;
-    state().m_globalComposite = op;
+    if (state().m_globalComposite == op)
+        return;
+    realizeSaves();
+    modifiableState().m_globalComposite = op;
     GraphicsContext* c = drawingContext();
     if (!c)
         return;
@@ -558,12 +589,17 @@
 
     AffineTransform newTransform = state().m_transform;
     newTransform.scaleNonUniform(sx, sy);
+    if (state().m_transform == newTransform)
+        return;
+
+    realizeSaves();
+
     if (!newTransform.isInvertible()) {
-        state().m_invertibleCTM = false;
+        modifiableState().m_invertibleCTM = false;
         return;
     }
 
-    state().m_transform = newTransform;
+    modifiableState().m_transform = newTransform;
     c->scale(FloatSize(sx, sy));
     m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy));
 }
@@ -581,12 +617,17 @@
 
     AffineTransform newTransform = state().m_transform;
     newTransform.rotate(angleInRadians / piDouble * 180.0);
+    if (state().m_transform == newTransform)
+        return;
+
+    realizeSaves();
+
     if (!newTransform.isInvertible()) {
-        state().m_invertibleCTM = false;
+        modifiableState().m_invertibleCTM = false;
         return;
     }
 
-    state().m_transform = newTransform;
+    modifiableState().m_transform = newTransform;
     c->rotate(angleInRadians);
     m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
 }
@@ -604,12 +645,17 @@
 
     AffineTransform newTransform = state().m_transform;
     newTransform.translate(tx, ty);
+    if (state().m_transform == newTransform)
+        return;
+
+    realizeSaves();
+
     if (!newTransform.isInvertible()) {
-        state().m_invertibleCTM = false;
+        modifiableState().m_invertibleCTM = false;
         return;
     }
 
-    state().m_transform = newTransform;
+    modifiableState().m_transform = newTransform;
     c->translate(tx, ty);
     m_path.transform(AffineTransform().translate(-tx, -ty));
 }
@@ -627,12 +673,17 @@
 
     AffineTransform transform(m11, m12, m21, m22, dx, dy);
     AffineTransform newTransform = state().m_transform * transform;
+    if (state().m_transform == newTransform)
+        return;
+
+    realizeSaves();
+
     if (!newTransform.isInvertible()) {
-        state().m_invertibleCTM = false;
+        modifiableState().m_invertibleCTM = false;
         return;
     }
 
-    state().m_transform = newTransform;
+    modifiableState().m_transform = newTransform;
     c->concatCTM(transform);
     m_path.transform(transform.inverse());
 }
@@ -649,11 +700,14 @@
     AffineTransform ctm = state().m_transform;
     if (!ctm.isInvertible())
         return;
+
+    realizeSaves();
+    
     c->setCTM(canvas()->baseTransform());
-    state().m_transform = AffineTransform();
+    modifiableState().m_transform = AffineTransform();
     m_path.transform(ctm);
 
-    state().m_invertibleCTM = true;
+    modifiableState().m_invertibleCTM = true;
     transform(m11, m12, m21, m22, dx, dy);
 }
 
@@ -661,8 +715,9 @@
 {
     if (color == state().m_unparsedStrokeColor)
         return;
+    realizeSaves();
     setStrokeStyle(CanvasStyle::createFromString(color, canvas()->document()));
-    state().m_unparsedStrokeColor = color;
+    modifiableState().m_unparsedStrokeColor = color;
 }
 
 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
@@ -702,8 +757,9 @@
 {
     if (color == state().m_unparsedFillColor)
         return;
+    realizeSaves();
     setFillStyle(CanvasStyle::createFromString(color, canvas()->document()));
-    state().m_unparsedFillColor = color;
+    modifiableState().m_unparsedFillColor = color;
 }
 
 void CanvasRenderingContext2D::setFillColor(float grayLevel)
@@ -971,6 +1027,7 @@
         return;
     if (!state().m_invertibleCTM)
         return;
+    realizeSaves();
     c->canvasClip(m_path);
 #if ENABLE(DASHBOARD_SUPPORT)
     clearPathForDashboardBackwardCompatibilityMode();
@@ -1004,11 +1061,30 @@
         return;
     FloatRect rect(x, y, width, height);
 
-    save();
-    setAllAttributesToDefault();
+    bool saved = false;
+    if (shouldDrawShadows()) {
+        context->save();
+        saved = true;
+        context->setLegacyShadow(FloatSize(), 0, Color::transparent, ColorSpaceDeviceRGB);
+    }
+    if (state().m_globalAlpha != 1) {
+        if (!saved) {
+            context->save();
+            saved = true;
+        }
+        context->setAlpha(1);
+    }
+    if (state().m_globalComposite != CompositeSourceOver) {
+        if (!saved) {
+            context->save();
+            saved = true;
+        }
+        context->setCompositeOperation(CompositeSourceOver);
+    }
     context->clearRect(rect);
+    if (saved)
+        context->restore();
     didDraw(rect);
-    restore();
 }
 
 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
@@ -1079,72 +1155,61 @@
 
 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
 {
-    state().m_shadowOffset = FloatSize(width, height);
-    state().m_shadowBlur = blur;
-    state().m_shadowColor = Color::transparent;
-    applyShadow();
+    setShadow(FloatSize(width, height), blur, Color::transparent);
 }
 
 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
 {
-    if (!parseColorOrCurrentColor(state().m_shadowColor, color, canvas()))
+    RGBA32 rgba;
+    if (!parseColorOrCurrentColor(rgba, color, canvas()))
         return;
-
-    state().m_shadowOffset = FloatSize(width, height);
-    state().m_shadowBlur = blur;
-    applyShadow();
+    setShadow(FloatSize(width, height), blur, rgba);
 }
 
 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
 {
-    state().m_shadowOffset = FloatSize(width, height);
-    state().m_shadowBlur = blur;
-    state().m_shadowColor = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1.0f);
-    applyShadow();
+    setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1));
 }
 
 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
 {
     RGBA32 rgba;
-
     if (!parseColorOrCurrentColor(rgba, color, canvas()))
         return;
-
-    state().m_shadowColor = colorWithOverrideAlpha(rgba, alpha);
-    state().m_shadowOffset = FloatSize(width, height);
-    state().m_shadowBlur = blur;
-    applyShadow();
+    setShadow(FloatSize(width, height), blur, colorWithOverrideAlpha(rgba, alpha));
 }
 
 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
 {
-    state().m_shadowOffset = FloatSize(width, height);
-    state().m_shadowBlur = blur;
-    state().m_shadowColor = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha);
-    applyShadow();
+    setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha));
 }
 
 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
 {
-    state().m_shadowOffset = FloatSize(width, height);
-    state().m_shadowBlur = blur;
-    state().m_shadowColor = makeRGBA32FromFloats(r, g, b, a);
-    applyShadow();
+    setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(r, g, b, a));
 }
 
 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
 {
-    state().m_shadowOffset = FloatSize(width, height);
-    state().m_shadowBlur = blur;
-    state().m_shadowColor = makeRGBAFromCMYKA(c, m, y, k, a);
-    applyShadow();
+    setShadow(FloatSize(width, height), blur, makeRGBAFromCMYKA(c, m, y, k, a));
 }
 
 void CanvasRenderingContext2D::clearShadow()
 {
-    state().m_shadowOffset = FloatSize();
-    state().m_shadowBlur = 0;
-    state().m_shadowColor = Color::transparent;
+    setShadow(FloatSize(), 0, Color::transparent);
+}
+
+void CanvasRenderingContext2D::setShadow(const FloatSize& offset, float blur, RGBA32 color)
+{
+    if (state().m_shadowOffset == offset && state().m_shadowBlur == blur && state().m_shadowColor == color)
+        return;
+    bool wasDrawingShadows = shouldDrawShadows();
+    realizeSaves();
+    modifiableState().m_shadowOffset = offset;
+    modifiableState().m_shadowBlur = blur;
+    modifiableState().m_shadowColor = color;
+    if (!wasDrawingShadows && !shouldDrawShadows())
+        return;
     applyShadow();
 }
 
@@ -1923,17 +1988,14 @@
 
 void CanvasRenderingContext2D::setFont(const String& newFont)
 {
-    RefPtr<StylePropertySet> tempDecl = StylePropertySet::create();
-    CSSParser parser(strictToCSSParserMode(!m_usesCSSCompatibilityParseMode));
-
-    String declarationText("font: ");
-    declarationText += newFont;
-    parser.parseDeclaration(tempDecl.get(), declarationText, 0, 0);
-    if (tempDecl->isEmpty())
+    RefPtr<StylePropertySet> parsedStyle = StylePropertySet::create();
+    CSSParser(strictToCSSParserMode(!m_usesCSSCompatibilityParseMode)).parseDeclaration(parsedStyle.get(), "font:" + newFont, 0, 0);
+    if (parsedStyle->isEmpty())
         return;
 
     // The parse succeeded.
-    state().m_unparsedFont = newFont;
+    realizeSaves();
+    modifiableState().m_unparsedFont = newFont;
 
     // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work
     // relative to the canvas.
@@ -1944,22 +2006,22 @@
 
     // Now map the font property longhands into the style.
     StyleResolver* styleResolver = canvas()->styleResolver();
-    styleResolver->applyPropertyToStyle(CSSPropertyFontFamily, tempDecl->getPropertyCSSValue(CSSPropertyFontFamily).get(), newStyle.get());
-    styleResolver->applyPropertyToCurrentStyle(CSSPropertyFontStyle, tempDecl->getPropertyCSSValue(CSSPropertyFontStyle).get());
-    styleResolver->applyPropertyToCurrentStyle(CSSPropertyFontVariant, tempDecl->getPropertyCSSValue(CSSPropertyFontVariant).get());
-    styleResolver->applyPropertyToCurrentStyle(CSSPropertyFontWeight, tempDecl->getPropertyCSSValue(CSSPropertyFontWeight).get());
+    styleResolver->applyPropertyToStyle(CSSPropertyFontFamily, parsedStyle->getPropertyCSSValue(CSSPropertyFontFamily).get(), newStyle.get());
+    styleResolver->applyPropertyToCurrentStyle(CSSPropertyFontStyle, parsedStyle->getPropertyCSSValue(CSSPropertyFontStyle).get());
+    styleResolver->applyPropertyToCurrentStyle(CSSPropertyFontVariant, parsedStyle->getPropertyCSSValue(CSSPropertyFontVariant).get());
+    styleResolver->applyPropertyToCurrentStyle(CSSPropertyFontWeight, parsedStyle->getPropertyCSSValue(CSSPropertyFontWeight).get());
 
     // As described in BUG66291, setting font-size on a font may entail a CSSPrimitiveValue::computeLengthDouble call,
     // which assumes the fontMetrics are available for the affected font, otherwise a crash occurs (see http://trac.webkit.org/changeset/96122).
     // The updateFont() call below updates the fontMetrics and ensures the proper setting of font-size.
     styleResolver->updateFont();
-    styleResolver->applyPropertyToCurrentStyle(CSSPropertyFontSize, tempDecl->getPropertyCSSValue(CSSPropertyFontSize).get());
-    styleResolver->applyPropertyToCurrentStyle(CSSPropertyLineHeight, tempDecl->getPropertyCSSValue(CSSPropertyLineHeight).get());
+    styleResolver->applyPropertyToCurrentStyle(CSSPropertyFontSize, parsedStyle->getPropertyCSSValue(CSSPropertyFontSize).get());
+    styleResolver->applyPropertyToCurrentStyle(CSSPropertyLineHeight, parsedStyle->getPropertyCSSValue(CSSPropertyLineHeight).get());
 
-    state().m_font = newStyle->font();
-    state().m_font.update(styleResolver->fontSelector());
-    state().m_realizedFont = true;
-    styleResolver->fontSelector()->registerForInvalidationCallbacks(&state());
+    modifiableState().m_font = newStyle->font();
+    modifiableState().m_font.update(styleResolver->fontSelector());
+    modifiableState().m_realizedFont = true;
+    styleResolver->fontSelector()->registerForInvalidationCallbacks(&modifiableState());
 }
 
 String CanvasRenderingContext2D::textAlign() const
@@ -1972,7 +2034,10 @@
     TextAlign align;
     if (!parseTextAlign(s, align))
         return;
-    state().m_textAlign = align;
+    if (state().m_textAlign == align)
+        return;
+    realizeSaves();
+    modifiableState().m_textAlign = align;
 }
 
 String CanvasRenderingContext2D::textBaseline() const
@@ -1985,7 +2050,10 @@
     TextBaseline baseline;
     if (!parseTextBaseline(s, baseline))
         return;
-    state().m_textBaseline = baseline;
+    if (state().m_textBaseline == baseline)
+        return;
+    realizeSaves();
+    modifiableState().m_textBaseline = baseline;
 }
 
 void CanvasRenderingContext2D::fillText(const String& text, float x, float y)

Modified: trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.h (117105 => 117106)


--- trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.h	2012-05-15 18:31:16 UTC (rev 117105)
+++ trunk/Source/WebCore/html/canvas/CanvasRenderingContext2D.h	2012-05-15 18:43:21 UTC (rev 117106)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -66,9 +66,6 @@
     }
     virtual ~CanvasRenderingContext2D();
 
-    virtual bool is2d() const { return true; }
-    virtual bool isAccelerated() const;
-
     CanvasStyle* strokeStyle() const;
     void setStrokeStyle(PassRefPtr<CanvasStyle>);
 
@@ -111,9 +108,8 @@
     String globalCompositeOperation() const;
     void setGlobalCompositeOperation(const String&);
 
-    void save();
+    void save() { ++m_unrealizedSaveCount; }
     void restore();
-    void setAllAttributesToDefault();
 
     void scale(float sx, float sy);
     void rotate(float angleInRadians);
@@ -226,10 +222,6 @@
     LineCap getLineCap() const { return state().m_lineCap; }
     LineJoin getLineJoin() const { return state().m_lineJoin; }
 
-#if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING)
-    virtual PlatformLayer* platformLayer() const;
-#endif
-
 private:
     struct State : FontSelectorClient {
         State();
@@ -238,7 +230,7 @@
         State(const State&);
         State& operator=(const State&);
 
-        virtual void fontsNeedUpdate(FontSelector*);
+        virtual void fontsNeedUpdate(FontSelector*) OVERRIDE;
 
         String m_unparsedStrokeColor;
         String m_unparsedFillColor;
@@ -277,11 +269,10 @@
 
     CanvasRenderingContext2D(HTMLCanvasElement*, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode);
 
-    Path m_path;
-
-    State& state() { return m_stateStack.last(); }
+    State& modifiableState() { ASSERT(!m_unrealizedSaveCount); return m_stateStack.last(); }
     const State& state() const { return m_stateStack.last(); }
 
+    void setShadow(const FloatSize& offset, float blur, RGBA32 color);
     void applyShadow();
     bool shouldDrawShadows() const;
 
@@ -291,6 +282,12 @@
     GraphicsContext* drawingContext() const;
 
     void unwindStateStack();
+    void realizeSaves()
+    {
+        if (m_unrealizedSaveCount)
+            realizeSavesLoop();
+    }
+    void realizeSavesLoop();
 
     void applyStrokePattern();
     void applyFillPattern();
@@ -322,7 +319,16 @@
     PassRefPtr<ImageData> getImageData(ImageBuffer::CoordinateSystem, float sx, float sy, float sw, float sh, ExceptionCode&) const;
     void putImageData(ImageData*, ImageBuffer::CoordinateSystem, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight, ExceptionCode&);
 
+    virtual bool is2d() const OVERRIDE { return true; }
+    virtual bool isAccelerated() const OVERRIDE;
+
+#if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING)
+    virtual PlatformLayer* platformLayer() const OVERRIDE;
+#endif
+
+    Path m_path;    
     Vector<State, 1> m_stateStack;
+    unsigned m_unrealizedSaveCount;
     bool m_usesCSSCompatibilityParseMode;
 #if ENABLE(DASHBOARD_SUPPORT)
     bool m_usesDashboardCompatibilityMode;
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to