Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / gpu / GrOvalRenderer.cpp
index 23ce230..45564bc 100644 (file)
 
 #include "SkRRect.h"
 #include "SkStrokeRec.h"
+#include "SkTLazy.h"
 
 #include "effects/GrVertexEffect.h"
+#include "effects/GrRRectEffect.h"
 
 namespace {
 
 struct CircleVertex {
-    GrPoint  fPos;
-    GrPoint  fOffset;
+    SkPoint  fPos;
+    SkPoint  fOffset;
     SkScalar fOuterRadius;
     SkScalar fInnerRadius;
 };
 
 struct EllipseVertex {
-    GrPoint  fPos;
-    GrPoint  fOffset;
-    GrPoint  fOuterRadii;
-    GrPoint  fInnerRadii;
+    SkPoint  fPos;
+    SkPoint  fOffset;
+    SkPoint  fOuterRadii;
+    SkPoint  fInnerRadii;
 };
 
 struct DIEllipseVertex {
-    GrPoint  fPos;
-    GrPoint  fOuterOffset;
-    GrPoint  fInnerOffset;
+    SkPoint  fPos;
+    SkPoint  fOuterOffset;
+    SkPoint  fInnerOffset;
 };
 
 inline bool circle_stays_circle(const SkMatrix& m) {
@@ -230,11 +232,8 @@ public:
             builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n");
             builder->fsCodeAppendf("\tvec2 grad = 2.0*scaledOffset*%s.xy;\n", fsRadiiName);
             builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
-            // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
-            // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
-            if (builder->ctxInfo().caps()->dropsTileOnZeroDivide()) {
-                builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
-            }
+            // avoid calling inversesqrt on zero.
+            builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
             builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
             builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n");
 
@@ -379,11 +378,8 @@ public:
                                    fsOffsetName0, fsOffsetName0, fsOffsetName0, fsOffsetName0);
 
             builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
-            // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
-            // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
-            if (builder->ctxInfo().caps()->dropsTileOnZeroDivide()) {
-                builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
-            }
+            // avoid calling inversesqrt on zero.
+            builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
             builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
             if (kHairline == ellipseEffect.getMode()) {
                 // can probably do this with one step
@@ -492,7 +488,7 @@ bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, bo
 // position + edge
 extern const GrVertexAttrib gCircleVertexAttribs[] = {
     {kVec2f_GrVertexAttribType, 0,               kPosition_GrVertexAttribBinding},
-    {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
+    {kVec4f_GrVertexAttribType, sizeof(SkPoint), kEffect_GrVertexAttribBinding}
 };
 
 void GrOvalRenderer::drawCircle(GrDrawTarget* target,
@@ -503,7 +499,7 @@ void GrOvalRenderer::drawCircle(GrDrawTarget* target,
     GrDrawState* drawState = target->drawState();
 
     const SkMatrix& vm = drawState->getViewMatrix();
-    GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY());
+    SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
     vm.mapPoints(&center, 1);
     SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width()));
     SkScalar strokeWidth = vm.mapRadius(stroke.getWidth());
@@ -525,12 +521,14 @@ void GrOvalRenderer::drawCircle(GrDrawTarget* target,
     CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
 
     SkStrokeRec::Style style = stroke.getStyle();
-    bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
+    bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
+                        SkStrokeRec::kHairline_Style == style;
+    bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
 
     SkScalar innerRadius = 0.0f;
     SkScalar outerRadius = radius;
     SkScalar halfWidth = 0;
-    if (style != SkStrokeRec::kFill_Style) {
+    if (hasStroke) {
         if (SkScalarNearlyZero(strokeWidth)) {
             halfWidth = SK_ScalarHalf;
         } else {
@@ -538,12 +536,12 @@ void GrOvalRenderer::drawCircle(GrDrawTarget* target,
         }
 
         outerRadius += halfWidth;
-        if (isStroked) {
+        if (isStrokeOnly) {
             innerRadius = radius - halfWidth;
         }
     }
 
-    GrEffectRef* effect = CircleEdgeEffect::Create(isStroked && innerRadius > 0);
+    GrEffectRef* effect = CircleEdgeEffect::Create(isStrokeOnly && innerRadius > 0);
     static const int kCircleEdgeAttrIndex = 1;
     drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
 
@@ -589,15 +587,15 @@ void GrOvalRenderer::drawCircle(GrDrawTarget* target,
 // position + offset + 1/radii
 extern const GrVertexAttrib gEllipseVertexAttribs[] = {
     {kVec2f_GrVertexAttribType, 0,                 kPosition_GrVertexAttribBinding},
-    {kVec2f_GrVertexAttribType, sizeof(GrPoint),   kEffect_GrVertexAttribBinding},
-    {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}
+    {kVec2f_GrVertexAttribType, sizeof(SkPoint),   kEffect_GrVertexAttribBinding},
+    {kVec4f_GrVertexAttribType, 2*sizeof(SkPoint), kEffect_GrVertexAttribBinding}
 };
 
 // position + offsets
 extern const GrVertexAttrib gDIEllipseVertexAttribs[] = {
     {kVec2f_GrVertexAttribType, 0,                 kPosition_GrVertexAttribBinding},
-    {kVec2f_GrVertexAttribType, sizeof(GrPoint),   kEffect_GrVertexAttribBinding},
-    {kVec2f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding},
+    {kVec2f_GrVertexAttribType, sizeof(SkPoint),   kEffect_GrVertexAttribBinding},
+    {kVec2f_GrVertexAttribType, 2*sizeof(SkPoint), kEffect_GrVertexAttribBinding},
 };
 
 bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
@@ -616,7 +614,7 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
 
     // do any matrix crunching before we reset the draw state for device coords
     const SkMatrix& vm = drawState->getViewMatrix();
-    GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
+    SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
     vm.mapPoints(&center, 1);
     SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
     SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
@@ -632,11 +630,13 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
     scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
 
     SkStrokeRec::Style style = stroke.getStyle();
-    bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
+    bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
+                        SkStrokeRec::kHairline_Style == style;
+    bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
 
     SkScalar innerXRadius = 0;
     SkScalar innerYRadius = 0;
-    if (SkStrokeRec::kFill_Style != style) {
+    if (hasStroke) {
         if (SkScalarNearlyZero(scaledStroke.length())) {
             scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
         } else {
@@ -656,7 +656,7 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
         }
 
         // this is legit only if scale & translation (which should be the case at the moment)
-        if (isStroked) {
+        if (isStrokeOnly) {
             innerXRadius = xRadius - scaledStroke.fX;
             innerYRadius = yRadius - scaledStroke.fY;
         }
@@ -681,7 +681,7 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
 
     EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
 
-    GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked &&
+    GrEffectRef* effect = EllipseEdgeEffect::Create(isStrokeOnly &&
                                                     innerXRadius > 0 && innerYRadius > 0);
 
     static const int kEllipseCenterAttrIndex = 1;
@@ -740,7 +740,7 @@ bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
     GrDrawState* drawState = target->drawState();
     const SkMatrix& vm = drawState->getViewMatrix();
 
-    GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
+    SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
     SkScalar xRadius = SkScalarHalf(ellipse.width());
     SkScalar yRadius = SkScalarHalf(ellipse.height());
 
@@ -883,9 +883,39 @@ GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(GrGpu* gpu) {
     return fRRectIndexBuffer;
 }
 
-bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, bool useAA,
-                                     const SkRRect& rrect, const SkStrokeRec& stroke)
-{
+bool GrOvalRenderer::drawDRRect(GrDrawTarget* target, GrContext* context, bool useAA,
+                                const SkRRect& outer, const SkRRect& origInner) {
+    GrDrawState::AutoRestoreEffects are;
+    if (!origInner.isEmpty()) {
+        SkTCopyOnFirstWrite<SkRRect> inner(origInner);
+        if (!context->getMatrix().isIdentity()) {
+            if (!origInner.transform(context->getMatrix(), inner.writable())) {
+                return false;
+            }
+        }
+        bool applyAA = useAA &&
+                       !target->getDrawState().getRenderTarget()->isMultisampled() &&
+                       !target->shouldDisableCoverageAAForBlend();
+        GrEffectEdgeType edgeType = applyAA ? kInverseFillAA_GrEffectEdgeType :
+                                              kInverseFillBW_GrEffectEdgeType;
+        GrEffectRef* effect = GrRRectEffect::Create(edgeType, *inner);
+        if (NULL == effect) {
+            return false;
+        }
+        are.set(target->drawState());
+        target->drawState()->addCoverageEffect(effect)->unref();
+    }
+
+    SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
+    return this->drawRRect(target, context, useAA, outer, fillRec);
+}
+
+bool GrOvalRenderer::drawRRect(GrDrawTarget* target, GrContext* context, bool useAA,
+                               const SkRRect& rrect, const SkStrokeRec& stroke) {
+    if (rrect.isOval()) {
+        return this->drawOval(target, context, useAA, rrect.getBounds(), stroke);
+    }
+
     bool useCoverageAA = useAA &&
         !target->getDrawState().getRenderTarget()->isMultisampled() &&
         !target->shouldDisableCoverageAAForBlend();
@@ -896,12 +926,10 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
     }
 
     const SkMatrix& vm = context->getMatrix();
-#ifdef SK_DEBUG
-    {
-        // we should have checked for this previously
-        SkASSERT(useCoverageAA && vm.rectStaysRect() && rrect.isSimple());
+
+    if (!vm.rectStaysRect() || !rrect.isSimple()) {
+        return false;
     }
-#endif
 
     // do any matrix crunching before we reset the draw state for device coords
     const SkRect& rrectBounds = rrect.getBounds();
@@ -914,21 +942,38 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
     SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*radii.fX +
                                    vm[SkMatrix::kMScaleY]*radii.fY);
 
-    // if hairline stroke is greater than radius, we don't handle that right now
     SkStrokeRec::Style style = stroke.getStyle();
-    if (SkStrokeRec::kHairline_Style == style &&
-        (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
-        return false;
-    }
 
     // do (potentially) anisotropic mapping of stroke
     SkVector scaledStroke;
     SkScalar strokeWidth = stroke.getWidth();
-    scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
-    scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
 
-    // if half of strokewidth is greater than radius, we don't handle that right now
-    if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
+    bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
+                        SkStrokeRec::kHairline_Style == style;
+    bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
+
+    if (hasStroke) {
+        if (SkStrokeRec::kHairline_Style == style) {
+            scaledStroke.set(1, 1);
+        } else {
+            scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] +
+                                                       vm[SkMatrix::kMSkewY]));
+            scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] +
+                                                       vm[SkMatrix::kMScaleY]));
+        }
+
+        // if half of strokewidth is greater than radius, we don't handle that right now
+        if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
+            return false;
+        }
+    }
+
+    // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
+    // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
+    // patch will have fractional coverage. This only matters when the interior is actually filled.
+    // We could consider falling back to rect rendering here, since a tiny radius is
+    // indistinguishable from a square corner.
+    if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
         return false;
     }
 
@@ -939,8 +984,6 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
         return false;
     }
 
-    bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
-
     GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(context->getGpu());
     if (NULL == indexBuffer) {
         GrPrintf("Failed to create index buffer!\n");
@@ -948,7 +991,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
     }
 
     // if the corners are circles, use the circle renderer
-    if ((!isStroked || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
+    if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
         drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
         SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize());
 
@@ -962,23 +1005,23 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
         SkScalar innerRadius = 0.0f;
         SkScalar outerRadius = xRadius;
         SkScalar halfWidth = 0;
-        if (style != SkStrokeRec::kFill_Style) {
+        if (hasStroke) {
             if (SkScalarNearlyZero(scaledStroke.fX)) {
                 halfWidth = SK_ScalarHalf;
             } else {
                 halfWidth = SkScalarHalf(scaledStroke.fX);
             }
 
-            if (isStroked) {
+            if (isStrokeOnly) {
                 innerRadius = xRadius - halfWidth;
             }
             outerRadius += halfWidth;
             bounds.outset(halfWidth, halfWidth);
         }
 
-        isStroked = (isStroked && innerRadius >= 0);
+        isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
 
-        GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
+        GrEffectRef* effect = CircleEdgeEffect::Create(isStrokeOnly);
         static const int kCircleEdgeAttrIndex = 1;
         drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
 
@@ -1031,7 +1074,8 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
         }
 
         // drop out the middle quad if we're stroked
-        int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices);
+        int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
+                                      SK_ARRAY_COUNT(gRRectIndices);
         target->setIndexSourceToBuffer(indexBuffer);
         target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
 
@@ -1042,7 +1086,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
 
         SkScalar innerXRadius = 0.0f;
         SkScalar innerYRadius = 0.0f;
-        if (SkStrokeRec::kFill_Style != style) {
+        if (hasStroke) {
             if (SkScalarNearlyZero(scaledStroke.length())) {
                 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
             } else {
@@ -1062,7 +1106,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
             }
 
             // this is legit only if scale & translation (which should be the case at the moment)
-            if (isStroked) {
+            if (isStrokeOnly) {
                 innerXRadius = xRadius - scaledStroke.fX;
                 innerYRadius = yRadius - scaledStroke.fY;
             }
@@ -1072,7 +1116,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
             bounds.outset(scaledStroke.fX, scaledStroke.fY);
         }
 
-        isStroked = (isStroked && innerXRadius >= 0 && innerYRadius >= 0);
+        isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
 
         GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
         if (!geo.succeeded()) {
@@ -1081,7 +1125,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
         }
         EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
 
-        GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
+        GrEffectRef* effect = EllipseEdgeEffect::Create(isStrokeOnly);
         static const int kEllipseOffsetAttrIndex = 1;
         static const int kEllipseRadiiAttrIndex = 2;
         drawState->addCoverageEffect(effect,
@@ -1140,7 +1184,8 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
         }
 
         // drop out the middle quad if we're stroked
-        int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices);
+        int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
+                                      SK_ARRAY_COUNT(gRRectIndices);
         target->setIndexSourceToBuffer(indexBuffer);
         target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
     }