Add blend optimization helpers and use to convert rect draws to clears.
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 14 Aug 2013 21:56:37 +0000 (21:56 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 14 Aug 2013 21:56:37 +0000 (21:56 +0000)
R=robertphillips@google.com, jvanverth@google.com

Author: bsalomon@google.com

Review URL: https://chromiumcodereview.appspot.com/22558003

git-svn-id: http://skia.googlecode.com/svn/trunk@10723 2bbb7eff-a529-9590-31e7-b0007b416f81

gyp/gpu.gypi
include/core/SkMatrix.h
include/core/SkRect.h
include/gpu/GrPaint.h
include/gpu/GrSurface.h
src/gpu/GrBlend.cpp [new file with mode: 0644]
src/gpu/GrBlend.h [new file with mode: 0644]
src/gpu/GrContext.cpp
src/gpu/GrDrawState.h
src/gpu/GrPaint.cpp

index fff7922..c71abbf 100644 (file)
@@ -55,6 +55,8 @@
       '<(skia_src_path)/gpu/GrAtlas.cpp',
       '<(skia_src_path)/gpu/GrAtlas.h',
       '<(skia_src_path)/gpu/GrBinHashKey.h',
+      '<(skia_src_path)/gpu/GrBlend.cpp',
+      '<(skia_src_path)/gpu/GrBlend.h',
       '<(skia_src_path)/gpu/GrBufferAllocPool.cpp',
       '<(skia_src_path)/gpu/GrBufferAllocPool.h',
       '<(skia_src_path)/gpu/GrCacheID.cpp',
index f148e39..71e6b97 100644 (file)
@@ -470,6 +470,18 @@ public:
         return this->mapRect(rect, *rect);
     }
 
+    /** Apply this matrix to the src rectangle, and write the four transformed
+        points into dst. The points written to dst will be the original top-left, top-right,
+        bottom-right, and bottom-left points transformed by the matrix.
+        @param dst  Where the transformed quad is written.
+        @param rect The original rectangle to be transformed.
+    */
+    void mapRectToQuad(SkPoint dst[4], const SkRect& rect) const {
+        // This could potentially be faster if we only transformed each x and y of the rect once.
+        rect.toQuad(dst);
+        this->mapPoints(dst, 4);
+    }
+
     /** Return the mean radius of a circle after it has been mapped by
         this matrix. NOTE: in perspective this value assumes the circle
         has its center at the origin.
index 9f3b59a..d0eaac4 100644 (file)
@@ -466,8 +466,9 @@ struct SK_API SkRect {
         return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
     }
 
-    /** return the 4 points that enclose the rectangle
-    */
+    /** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right,
+        bottom-left). TODO: Consider adding param to control whether quad is CW or CCW.
+     */
     void toQuad(SkPoint quad[4]) const;
 
     /** Set this rectangle to the empty rectangle (0,0,0,0)
index 9e326f0..59343e7 100644 (file)
@@ -174,7 +174,28 @@ public:
         this->resetColorFilter();
     }
 
+    /**
+     * Determines whether the drawing with this paint is opaque with respect to both color blending
+     * and fractional coverage. It does not consider whether AA has been enabled on the paint or
+     * not. Depending upon whether multisampling or coverage-based AA is in use, AA may make the
+     * result only apply to the interior of primitives.
+     *
+     */
+    bool isOpaque() const;
+
+    /**
+     * Returns true if isOpaque would return true and the paint represents a solid constant color
+     * draw. If the result is true, constantColor will be updated to contain the constant color.
+     */
+    bool isOpaqueAndConstantColor(GrColor* constantColor) const;
+
 private:
+
+    /**
+     * Helper for isOpaque and isOpaqueAndConstantColor.
+     */
+    bool getOpaqueAndKnownColor(GrColor* solidColor, uint32_t* solidColorKnownComponents) const;
+
     /**
      * Called when the source coord system from which geometry is rendered changes. It ensures that
      * the local coordinates seen by effects remains unchanged. oldToNew gives the transformation
index 52a5665..eeb63ad 100644 (file)
@@ -11,6 +11,7 @@
 
 #include "GrTypes.h"
 #include "GrResource.h"
+#include "SkRect.h"
 
 class GrTexture;
 class GrRenderTarget;
@@ -33,6 +34,12 @@ public:
      */
     int height() const { return fDesc.fHeight; }
 
+    /**
+     * Helper that gets the width and height of the surface as a bounding rectangle.
+     */
+    void getBoundsRect(SkRect* rect) const { rect->setWH(SkIntToScalar(this->width()),
+                                                         SkIntToScalar(this->height())); }
+
     GrSurfaceOrigin origin() const {
         GrAssert(kTopLeft_GrSurfaceOrigin == fDesc.fOrigin || kBottomLeft_GrSurfaceOrigin == fDesc.fOrigin);
         return fDesc.fOrigin;
diff --git a/src/gpu/GrBlend.cpp b/src/gpu/GrBlend.cpp
new file mode 100644 (file)
index 0000000..c0621a9
--- /dev/null
@@ -0,0 +1,154 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrBlend.h"
+
+static inline GrBlendCoeff swap_coeff_src_dst(GrBlendCoeff coeff) {
+    switch (coeff) {
+        case kDC_GrBlendCoeff:
+            return kSC_GrBlendCoeff;
+        case kIDC_GrBlendCoeff:
+            return kISC_GrBlendCoeff;
+        case kDA_GrBlendCoeff:
+            return kSA_GrBlendCoeff;
+        case kIDA_GrBlendCoeff:
+            return kISA_GrBlendCoeff;
+        case kSC_GrBlendCoeff:
+            return kDC_GrBlendCoeff;
+        case kISC_GrBlendCoeff:
+            return kIDC_GrBlendCoeff;
+        case kSA_GrBlendCoeff:
+            return kDA_GrBlendCoeff;
+        case kISA_GrBlendCoeff:
+            return kIDA_GrBlendCoeff;
+        default:
+            return coeff;
+    }
+}
+
+static inline unsigned saturated_add(unsigned a, unsigned b) {
+    SkASSERT(a <= 255);
+    SkASSERT(b <= 255);
+    unsigned sum = a + b;
+    if (sum > 255) {
+        sum = 255;
+    }
+    return sum;
+}
+
+static GrColor add_colors(GrColor src, GrColor dst) {
+    unsigned r = saturated_add(GrColorUnpackR(src), GrColorUnpackR(dst));
+    unsigned g = saturated_add(GrColorUnpackG(src), GrColorUnpackG(dst));
+    unsigned b = saturated_add(GrColorUnpackB(src), GrColorUnpackB(dst));
+    unsigned a = saturated_add(GrColorUnpackA(src), GrColorUnpackA(dst));
+    return GrColorPackRGBA(r, g, b, a);
+}
+
+static inline bool valid_color(uint32_t compFlags) {
+     return (kRGBA_GrColorComponentFlags & compFlags) == kRGBA_GrColorComponentFlags;
+}
+
+static GrColor simplify_blend_term(GrBlendCoeff* srcCoeff,
+                                   GrColor srcColor, uint32_t srcCompFlags,
+                                   GrColor dstColor, uint32_t dstCompFlags,
+                                   GrColor constantColor) {
+
+    GrAssert(!GrBlendCoeffRefsSrc(*srcCoeff));
+    GrAssert(NULL != srcCoeff);
+
+    // Check whether srcCoeff can be reduced to kOne or kZero based on known color inputs.
+    // We could pick out the coeff r,g,b,a values here and use them to compute the blend term color,
+    // if possible, below but that is not implemented now.
+    switch (*srcCoeff) {
+        case kIDC_GrBlendCoeff:
+            dstColor = ~dstColor; // fallthrough
+        case kDC_GrBlendCoeff:
+            if (valid_color(dstCompFlags)) {
+                if (0xffffffff == dstColor) {
+                    *srcCoeff = kOne_GrBlendCoeff;
+                } else if (0 == dstColor) {
+                    *srcCoeff = kZero_GrBlendCoeff;
+                }
+            }
+            break;
+
+        case kIDA_GrBlendCoeff:
+            dstColor = ~dstColor; // fallthrough
+        case kDA_GrBlendCoeff:
+            if (kA_GrColorComponentFlag & dstCompFlags) {
+                if (0xff == GrColorUnpackA(dstColor)) {
+                    *srcCoeff = kOne_GrBlendCoeff;
+                } else if (0 == GrColorUnpackA(dstColor)) {
+                    *srcCoeff = kZero_GrBlendCoeff;
+                }
+            }
+            break;
+
+        case kIConstC_GrBlendCoeff:
+            constantColor = ~constantColor; // fallthrough
+        case kConstC_GrBlendCoeff:
+            if (0xffffffff == constantColor) {
+                *srcCoeff = kOne_GrBlendCoeff;
+            } else if (0 == constantColor) {
+                *srcCoeff = kZero_GrBlendCoeff;
+            }
+            break;
+
+        case kIConstA_GrBlendCoeff:
+            constantColor = ~constantColor; // fallthrough
+        case kConstA_GrBlendCoeff:
+            if (0xff == GrColorUnpackA(constantColor)) {
+                *srcCoeff = kOne_GrBlendCoeff;
+            } else if (0 == GrColorUnpackA(constantColor)) {
+                *srcCoeff = kZero_GrBlendCoeff;
+            }
+            break;
+            
+        default:
+            break;
+    }
+    // We may have invalidated these above and shouldn't read them again.
+    GR_DEBUGCODE(dstColor = constantColor = GrColor_ILLEGAL;)
+
+    if (kZero_GrBlendCoeff == *srcCoeff || (valid_color(srcCompFlags) && 0 == srcColor)) {
+        *srcCoeff = kZero_GrBlendCoeff;
+        return 0;
+    }
+
+    if (kOne_GrBlendCoeff == *srcCoeff && valid_color(srcCompFlags)) {
+        return srcColor;
+    } else {
+        return GrColor_ILLEGAL;
+    }
+}
+
+GrColor GrSimplifyBlend(GrBlendCoeff* srcCoeff,
+                        GrBlendCoeff* dstCoeff,
+                        GrColor srcColor, uint32_t srcCompFlags,
+                        GrColor dstColor, uint32_t dstCompFlags,
+                        GrColor constantColor) {
+    GrColor srcTermColor = simplify_blend_term(srcCoeff,
+                                               srcColor, srcCompFlags,
+                                               dstColor, dstCompFlags,
+                                               constantColor);
+
+    // We call the same function to simplify the dst blend coeff. We trick it out by swapping the
+    // src and dst.
+    GrBlendCoeff spoofedCoeff = swap_coeff_src_dst(*dstCoeff);
+    GrColor dstTermColor = simplify_blend_term(&spoofedCoeff,
+                                               dstColor, dstCompFlags,
+                                               srcColor, srcCompFlags,
+                                               constantColor);
+    *dstCoeff = swap_coeff_src_dst(spoofedCoeff);
+
+    if (GrColor_ILLEGAL != srcTermColor && GrColor_ILLEGAL != dstTermColor) {
+        return add_colors(srcTermColor, dstTermColor);
+    } else {
+        return GrColor_ILLEGAL;
+    }
+}
diff --git a/src/gpu/GrBlend.h b/src/gpu/GrBlend.h
new file mode 100644 (file)
index 0000000..e70d945
--- /dev/null
@@ -0,0 +1,45 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrTypes.h"
+#include "GrColor.h"
+
+#ifndef GrBlend_DEFINED
+#define GrBlend_DEFINED
+
+static inline bool GrBlendCoeffRefsSrc(GrBlendCoeff coeff) {
+    switch (coeff) {
+        case kSC_GrBlendCoeff:
+        case kISC_GrBlendCoeff:
+        case kSA_GrBlendCoeff:
+        case kISA_GrBlendCoeff:
+            return true;
+        default:
+            return false;
+    }
+}
+
+static inline bool GrBlendCoeffRefsDst(GrBlendCoeff coeff) {
+    switch (coeff) {
+        case kDC_GrBlendCoeff:
+        case kIDC_GrBlendCoeff:
+        case kDA_GrBlendCoeff:
+        case kIDA_GrBlendCoeff:
+            return true;
+        default:
+            return false;
+    }
+}
+
+GrColor GrSimplifyBlend(GrBlendCoeff* srcCoeff,
+                        GrBlendCoeff* dstCoeff,
+                        GrColor srcColor, uint32_t srcCompFlags,
+                        GrColor dstColor, uint32_t dstCompFlags,
+                        GrColor constantColor);
+
+#endif
index 20c633d..4d5551e 100644 (file)
@@ -681,9 +681,8 @@ static bool isIRect(const SkRect& r) {
 static bool apply_aa_to_rect(GrDrawTarget* target,
                              const SkRect& rect,
                              SkScalar strokeWidth,
-                             const SkMatrix* matrix,
-                             SkMatrix* combinedMatrix,
-                             SkRect* devRect,
+                             const SkMatrix& combinedMatrix,
+                             SkRect* devBoundRect,
                              bool* useVertexCoverage) {
     // we use a simple coverage ramp to do aa on axis-aligned rects
     // we check if the rect will be axis-aligned, and the rect won't land on
@@ -716,52 +715,32 @@ static bool apply_aa_to_rect(GrDrawTarget* target,
 #if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
     if (strokeWidth >= 0) {
 #endif
-        if (!drawState.getViewMatrix().preservesAxisAlignment()) {
+        if (!combinedMatrix.preservesAxisAlignment()) {
             return false;
         }
 
-        if (NULL != matrix && !matrix->preservesAxisAlignment()) {
-            return false;
-        }
 #if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
     } else {
-        if (!drawState.getViewMatrix().preservesAxisAlignment() &&
-            !drawState.getViewMatrix().preservesRightAngles()) {
-            return false;
-        }
-
-        if (NULL != matrix && !matrix->preservesRightAngles()) {
+        if (!combinedMatrix.preservesRightAngles()) {
             return false;
         }
     }
 #endif
 
-    *combinedMatrix = drawState.getViewMatrix();
-    if (NULL != matrix) {
-        combinedMatrix->preConcat(*matrix);
-
-#if GR_DEBUG
-#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
-        if (strokeWidth >= 0) {
-#endif
-            GrAssert(combinedMatrix->preservesAxisAlignment());
-#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
-        } else {
-            GrAssert(combinedMatrix->preservesRightAngles());
-        }
-#endif
-#endif
-    }
-
-    combinedMatrix->mapRect(devRect, rect);
+    combinedMatrix.mapRect(devBoundRect, rect);
 
     if (strokeWidth < 0) {
-        return !isIRect(*devRect);
+        return !isIRect(*devBoundRect);
     } else {
         return true;
     }
 }
 
+static inline bool rect_contains_inclusive(const SkRect& rect, const SkPoint& point) {
+    return point.fX >= rect.fLeft && point.fX <= rect.fRight &&
+           point.fY >= rect.fTop && point.fY <= rect.fBottom;
+}
+
 void GrContext::drawRect(const GrPaint& paint,
                          const SkRect& rect,
                          SkScalar width,
@@ -771,13 +750,51 @@ void GrContext::drawRect(const GrPaint& paint,
     AutoRestoreEffects are;
     GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are);
 
-    SkRect devRect;
-    SkMatrix combinedMatrix;
+    SkMatrix combinedMatrix = target->drawState()->getViewMatrix();
+    if (NULL != matrix) {
+        combinedMatrix.preConcat(*matrix);
+    }
+
+    // Check if this is a full RT draw and can be replaced with a clear. We don't bother checking
+    // cases where the RT is fully inside a stroke.
+    if (width < 0) {
+        SkRect rtRect;
+        target->getDrawState().getRenderTarget()->getBoundsRect(&rtRect);
+        SkRect clipSpaceRTRect = rtRect;
+        bool checkClip = false;
+        if (NULL != this->getClip()) {
+            checkClip = true;
+            clipSpaceRTRect.offset(SkIntToScalar(this->getClip()->fOrigin.fX),
+                                   SkIntToScalar(this->getClip()->fOrigin.fY));
+        }
+        // Does the clip contain the entire RT?
+        if (!checkClip || target->getClip()->fClipStack->quickContains(clipSpaceRTRect)) {
+            SkMatrix invM;
+            if (!combinedMatrix.invert(&invM)) {
+                return;
+            }
+            // Does the rect bound the RT?
+            SkPoint srcSpaceRTQuad[4];
+            invM.mapRectToQuad(srcSpaceRTQuad, rtRect);
+            if (rect_contains_inclusive(rect, srcSpaceRTQuad[0]) &&
+                rect_contains_inclusive(rect, srcSpaceRTQuad[1]) &&
+                rect_contains_inclusive(rect, srcSpaceRTQuad[2]) &&
+                rect_contains_inclusive(rect, srcSpaceRTQuad[3])) {
+                // Will it blend?
+                GrColor clearColor;
+                if (paint.isOpaqueAndConstantColor(&clearColor)) {
+                    target->clear(NULL, clearColor);
+                    return;
+                }
+            }
+        }
+    }
+
+    SkRect devBoundRect;
     bool useVertexCoverage;
     bool needAA = paint.isAntiAlias() &&
                   !target->getDrawState().getRenderTarget()->isMultisampled();
-    bool doAA = needAA && apply_aa_to_rect(target, rect, width, matrix,
-                                           &combinedMatrix, &devRect,
+    bool doAA = needAA && apply_aa_to_rect(target, rect, width, combinedMatrix, &devBoundRect,
                                            &useVertexCoverage);
     if (doAA) {
         GrDrawState::AutoViewMatrixRestore avmr;
@@ -786,12 +803,12 @@ void GrContext::drawRect(const GrPaint& paint,
         }
         if (width >= 0) {
             fAARectRenderer->strokeAARect(this->getGpu(), target,
-                                          rect, combinedMatrix, devRect,
+                                          rect, combinedMatrix, devBoundRect,
                                           width, useVertexCoverage);
         } else {
             // filled AA rect
             fAARectRenderer->fillAARect(this->getGpu(), target,
-                                        rect, combinedMatrix, devRect,
+                                        rect, combinedMatrix, devBoundRect,
                                         useVertexCoverage);
         }
         return;
index c006e6c..785be77 100644 (file)
@@ -9,6 +9,7 @@
 #define GrDrawState_DEFINED
 
 #include "GrBackendEffectFactory.h"
+#include "GrBlend.h"
 #include "GrColor.h"
 #include "GrEffectStage.h"
 #include "GrPaint.h"
@@ -469,27 +470,11 @@ public:
         fCommon.fSrcBlend = srcCoeff;
         fCommon.fDstBlend = dstCoeff;
     #if GR_DEBUG
-        switch (dstCoeff) {
-        case kDC_GrBlendCoeff:
-        case kIDC_GrBlendCoeff:
-        case kDA_GrBlendCoeff:
-        case kIDA_GrBlendCoeff:
-            GrPrintf("Unexpected dst blend coeff. Won't work correctly with"
-                     "coverage stages.\n");
-            break;
-        default:
-            break;
+        if (GrBlendCoeffRefsDst(dstCoeff)) {
+            GrPrintf("Unexpected dst blend coeff. Won't work correctly with coverage stages.\n");
         }
-        switch (srcCoeff) {
-        case kSC_GrBlendCoeff:
-        case kISC_GrBlendCoeff:
-        case kSA_GrBlendCoeff:
-        case kISA_GrBlendCoeff:
-            GrPrintf("Unexpected src blend coeff. Won't work correctly with"
-                     "coverage stages.\n");
-            break;
-        default:
-            break;
+        if (GrBlendCoeffRefsSrc(srcCoeff)) {
+            GrPrintf("Unexpected src blend coeff. Won't work correctly with coverage stages.\n");
         }
     #endif
     }
index d67f2e8..898a32f 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "GrPaint.h"
 
+#include "GrBlend.h"
 #include "effects/GrSimpleTextureEffect.h"
 
 void GrPaint::addColorTextureEffect(GrTexture* texture, const SkMatrix& matrix) {
@@ -33,3 +34,96 @@ void GrPaint::addCoverageTextureEffect(GrTexture* texture,
     GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix, params);
     this->addCoverageEffect(effect)->unref();
 }
+
+bool GrPaint::isOpaque() const {
+    return this->getOpaqueAndKnownColor(NULL, NULL);
+}
+
+bool GrPaint::isOpaqueAndConstantColor(GrColor* color) const {
+    GrColor tempColor;
+    uint32_t colorComps;
+    if (this->getOpaqueAndKnownColor(&tempColor, &colorComps)) {
+        if (kRGBA_GrColorComponentFlags == colorComps) {
+            *color = tempColor;
+            return true;
+        }
+    }
+    return false;
+}
+
+bool GrPaint::getOpaqueAndKnownColor(GrColor* solidColor,
+                                     uint32_t* solidColorKnownComponents) const {
+
+    // TODO: Share this implementation with GrDrawState
+
+    // Since fColorFilterXfermode is going away soon, we aren't attempting to handle anything but
+    // the default setting.
+    if (SkXfermode::kDst_Mode != fColorFilterXfermode) {
+        return false;
+    }
+
+    GrColor coverage = GrColorPackRGBA(fCoverage, fCoverage, fCoverage, fCoverage);
+    uint32_t coverageComps = kRGBA_GrColorComponentFlags;
+    int count = fCoverageStages.count();
+    for (int i = 0; i < count; ++i) {
+        (*fCoverageStages[i].getEffect())->getConstantColorComponents(&coverage, &coverageComps);
+    }
+    if (kRGBA_GrColorComponentFlags != coverageComps || 0xffffffff != coverage) {
+        return false;
+    }
+
+    GrColor color = fColor;
+    uint32_t colorComps = kRGBA_GrColorComponentFlags;
+    count = fColorStages.count();
+    for (int i = 0; i < count; ++i) {
+        (*fColorStages[i].getEffect())->getConstantColorComponents(&color, &colorComps);
+    }
+
+    GrAssert((NULL == solidColor) == (NULL == solidColorKnownComponents));
+
+    GrBlendCoeff srcCoeff = fSrcBlendCoeff;
+    GrBlendCoeff dstCoeff = fDstBlendCoeff;
+    GrSimplifyBlend(&srcCoeff, &dstCoeff, color, colorComps, 0, 0, 0);
+
+    bool opaque = kZero_GrBlendCoeff == dstCoeff && !GrBlendCoeffRefsDst(srcCoeff);
+    if (NULL != solidColor) {
+        if (opaque) {
+            switch (srcCoeff) {
+                case kZero_GrBlendCoeff:
+                    *solidColor = 0;
+                    *solidColorKnownComponents = kRGBA_GrColorComponentFlags;
+                    break;
+
+                case kOne_GrBlendCoeff:
+                    *solidColor = color;
+                    *solidColorKnownComponents = colorComps;
+                    break;
+
+                // The src coeff should never refer to the src and if it refers to dst then opaque
+                // should have been false.
+                case kSC_GrBlendCoeff:
+                case kISC_GrBlendCoeff:
+                case kDC_GrBlendCoeff:
+                case kIDC_GrBlendCoeff:
+                case kSA_GrBlendCoeff:
+                case kISA_GrBlendCoeff:
+                case kDA_GrBlendCoeff:
+                case kIDA_GrBlendCoeff:
+                default:
+                    GrCrash("srcCoeff should not refer to src or dst.");
+                    break;
+
+                // TODO: update this once GrPaint actually has a const color.
+                case kConstC_GrBlendCoeff:
+                case kIConstC_GrBlendCoeff:
+                case kConstA_GrBlendCoeff:
+                case kIConstA_GrBlendCoeff:
+                    *solidColorKnownComponents = 0;
+                    break;
+            }
+        } else {
+            solidColorKnownComponents = 0;
+        }
+    }
+    return opaque;
+}