Adding isOpaque method to skia shader classes
authorjunov@chromium.org <junov@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 9 Dec 2011 15:48:03 +0000 (15:48 +0000)
committerjunov@chromium.org <junov@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 9 Dec 2011 15:48:03 +0000 (15:48 +0000)
REVIEW=http://codereview.appspot.com/5451102/
TEST=unit test ShaderOpacity

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

gyp/tests.gyp
include/core/SkColorShader.h
include/core/SkShader.h
src/core/SkBitmapProcShader.cpp
src/core/SkBitmapProcShader.h
src/core/SkShader.cpp
src/effects/SkGradientShader.cpp
tests/ShaderOpacityTest.cpp [new file with mode: 0644]

index 46c5ac5..a9c152d 100644 (file)
@@ -53,6 +53,7 @@
         '../tests/RefDictTest.cpp',
         '../tests/RegionTest.cpp',
         '../tests/ScalarTest.cpp',
+        '../tests/ShaderOpacityTest.cpp',
         '../tests/Sk64Test.cpp',
         '../tests/skia_test.cpp',
         '../tests/SortTest.cpp',
index c68617a..9b1fed3 100644 (file)
@@ -34,6 +34,7 @@ public:
 
     virtual uint32_t getFlags() SK_OVERRIDE;
     virtual uint8_t getSpan16Alpha() const SK_OVERRIDE;
+    virtual bool isOpaque() const SK_OVERRIDE;
     virtual bool setContext(const SkBitmap& device, const SkPaint& paint,
                             const SkMatrix& matrix) SK_OVERRIDE;
     virtual void shadeSpan(int x, int y, SkPMColor span[], int count) SK_OVERRIDE;
index db737c9..7c5be06 100644 (file)
@@ -97,6 +97,15 @@ public:
     virtual uint32_t getFlags() { return 0; }
 
     /**
+     *  Returns true if the shader is guaranteed to produce only opaque
+     *  colors, subject to the SkPaint using the shader to apply an opaque
+     *  alpha value. Subclasses should override this to allow some
+     *  optimizations.  isOpaque() can be called at any time, unlike getFlags,
+     *  which only works properly when the context is set.
+     */
+    virtual bool isOpaque() const { return false; }
+
+    /**
      *  Return the alpha associated with the data returned by shadeSpan16(). If
      *  kHasSpan16_Flag is not set, this value is meaningless.
      */
index 85f9645..65387e4 100644 (file)
@@ -81,6 +81,10 @@ static bool only_scale_and_translate(const SkMatrix& matrix) {
     return (matrix.getType() & ~mask) == 0;
 }
 
+bool SkBitmapProcShader::isOpaque() const {
+    return fRawBitmap.isOpaque();
+}
+
 bool SkBitmapProcShader::setContext(const SkBitmap& device,
                                     const SkPaint& paint,
                                     const SkMatrix& matrix) {
index 9af9d2e..cd70279 100644 (file)
@@ -18,6 +18,7 @@ public:
     SkBitmapProcShader(const SkBitmap& src, TileMode tx, TileMode ty);
 
     // overrides from SkShader
+    virtual bool isOpaque() const SK_OVERRIDE;
     virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
     virtual uint32_t getFlags() { return fFlags; }
     virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
index b832f95..44ec62d 100644 (file)
@@ -221,6 +221,13 @@ SkColorShader::SkColorShader(SkColor c) {
 
 SkColorShader::~SkColorShader() {}
 
+bool SkColorShader::isOpaque() const {
+    if (fInheritColor) {
+        return true; // using paint's alpha
+    }
+    return SkColorGetA(fColor) == 255;
+}
+
 SkColorShader::SkColorShader(SkFlattenableReadBuffer& b) : INHERITED(b) {
     fFlags = 0; // computed in setContext
 
index bc7f8a3..af357a6 100644 (file)
@@ -119,6 +119,7 @@ public:
     // overrides
     virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
     virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
+    virtual bool isOpaque() const SK_OVERRIDE;
 
 protected:
     Gradient_Shader(SkFlattenableReadBuffer& );
@@ -161,7 +162,8 @@ private:
         kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
     };
     SkColor     fStorage[(kStorageSize + 3) >> 2];
-    SkColor*    fOrigColors;
+    SkColor*    fOrigColors; // original colors, before modulation by paint in setContext
+    bool        fColorsAreOpaque;
 
     mutable uint16_t*   fCache16;   // working ptr. If this is NULL, we need to recompute the cache values
     mutable SkPMColor*  fCache32;   // working ptr. If this is NULL, we need to recompute the cache values
@@ -174,6 +176,7 @@ private:
     static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
                                 U8CPU alpha);
     void setCacheAlpha(U8CPU alpha) const;
+    void initCommon();
 
     typedef SkShader INHERITED;
 };
@@ -302,7 +305,7 @@ Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
             }
         }
     }
-    fFlags = 0;
+    this->initCommon();
 }
 
 Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
@@ -336,7 +339,7 @@ Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
         }
     }
     SkReadMatrix(&buffer, &fPtsToUnit);
-    fFlags = 0;
+    this->initCommon();
 }
 
 Gradient_Shader::~Gradient_Shader() {
@@ -350,6 +353,15 @@ Gradient_Shader::~Gradient_Shader() {
     SkSafeUnref(fMapper);
 }
 
+void Gradient_Shader::initCommon() {
+    fFlags = 0;
+    unsigned colorAlpha = 0xFF;
+    for (int i = 0; i < fColorCount; i++) {
+        colorAlpha &= SkColorGetA(fOrigColors[i]);
+    }
+    fColorsAreOpaque = colorAlpha == 0xFF;
+}
+
 void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
     this->INHERITED::flatten(buffer);
     buffer.writeFlattenable(fMapper);
@@ -366,6 +378,10 @@ void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
     SkWriteMatrix(&buffer, fPtsToUnit);
 }
 
+bool Gradient_Shader::isOpaque() const {
+    return fColorsAreOpaque;
+}
+
 bool Gradient_Shader::setContext(const SkBitmap& device,
                                  const SkPaint& paint,
                                  const SkMatrix& matrix) {
@@ -384,23 +400,14 @@ bool Gradient_Shader::setContext(const SkBitmap& device,
 
     // now convert our colors in to PMColors
     unsigned paintAlpha = this->getPaintAlpha();
-    unsigned colorAlpha = 0xFF;
-
-    // FIXME: record colorAlpha in constructor, since this is not affected
-    // by setContext()
-    for (int i = 0; i < fColorCount; i++) {
-        SkColor src = fOrigColors[i];
-        unsigned sa = SkColorGetA(src);
-        colorAlpha &= sa;
-    }
 
     fFlags = this->INHERITED::getFlags();
-    if ((colorAlpha & paintAlpha) == 0xFF) {
+    if (fColorsAreOpaque && paintAlpha == 0xFF) {
         fFlags |= kOpaqueAlpha_Flag;
     }
     // we can do span16 as long as our individual colors are opaque,
     // regardless of the paint's alpha
-    if (0xFF == colorAlpha) {
+    if (fColorsAreOpaque) {
         fFlags |= kHasSpan16_Flag;
     }
 
diff --git a/tests/ShaderOpacityTest.cpp b/tests/ShaderOpacityTest.cpp
new file mode 100644 (file)
index 0000000..a8177ee
--- /dev/null
@@ -0,0 +1,119 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Test.h"
+#include "SkShader.h"
+#include "SkGradientShader.h"
+#include "SkColorShader.h"
+
+static void test_bitmap(skiatest::Reporter* reporter) {
+    SkBitmap bmp;
+    bmp.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+    
+    // test 1: bitmap without pixel data
+    SkShader* shader = SkShader::CreateBitmapShader(bmp, 
+        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+    REPORTER_ASSERT(reporter, shader);
+    REPORTER_ASSERT(reporter, !shader->isOpaque());
+    shader->unref();
+
+    // From this point on, we have pixels
+    bmp.allocPixels();
+
+    // test 2: not opaque by default
+    shader = SkShader::CreateBitmapShader(bmp, 
+        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+    REPORTER_ASSERT(reporter, shader);
+    REPORTER_ASSERT(reporter, !shader->isOpaque());
+    shader->unref();
+
+    // test 3: explicitly opaque
+    bmp.setIsOpaque(true);
+    shader = SkShader::CreateBitmapShader(bmp, 
+        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+    REPORTER_ASSERT(reporter, shader);
+    REPORTER_ASSERT(reporter, shader->isOpaque());
+    shader->unref();
+
+    // test 4: explicitly not opaque
+    bmp.setIsOpaque(false);
+    shader = SkShader::CreateBitmapShader(bmp, 
+        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+    REPORTER_ASSERT(reporter, shader);
+    REPORTER_ASSERT(reporter, !shader->isOpaque());
+    shader->unref();
+
+}
+
+static void test_gradient(skiatest::Reporter* reporter)
+{
+    SkPoint pts[2];
+    pts[0].iset(0, 0);
+    pts[1].iset(1, 0);
+    SkColor colors[2];
+    SkScalar pos[2] = {SkIntToScalar(0), SkIntToScalar(1)};
+    int count = 2;
+    SkShader::TileMode mode = SkShader::kClamp_TileMode;
+
+    // test 1: all opaque
+    colors[0] = SkColorSetARGB(0xFF, 0, 0, 0);
+    colors[1] = SkColorSetARGB(0xFF, 0, 0, 0);
+    SkShader* grad = SkGradientShader::CreateLinear(pts, colors, pos, count,
+                                                    mode);
+    REPORTER_ASSERT(reporter, grad);
+    REPORTER_ASSERT(reporter, grad->isOpaque());
+    grad->unref();
+
+    // test 2: all 0 alpha
+    colors[0] = SkColorSetARGB(0, 0, 0, 0);
+    colors[1] = SkColorSetARGB(0, 0, 0, 0);
+    grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode);
+    REPORTER_ASSERT(reporter, grad);
+    REPORTER_ASSERT(reporter, !grad->isOpaque());
+    grad->unref();
+
+    // test 3: one opaque, one transparent
+    colors[0] = SkColorSetARGB(0xFF, 0, 0, 0);
+    colors[1] = SkColorSetARGB(0x40, 0, 0, 0);
+    grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode);
+    REPORTER_ASSERT(reporter, grad);
+    REPORTER_ASSERT(reporter, !grad->isOpaque());
+    grad->unref();
+
+    // test 4: test 3, swapped
+    colors[0] = SkColorSetARGB(0x40, 0, 0, 0);
+    colors[1] = SkColorSetARGB(0xFF, 0, 0, 0);
+    grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode);
+    REPORTER_ASSERT(reporter, grad);
+    REPORTER_ASSERT(reporter, !grad->isOpaque());
+    grad->unref();
+}
+
+static void test_color(skiatest::Reporter* reporter)
+{
+    SkColorShader colorShader1(SkColorSetARGB(0,0,0,0));
+    REPORTER_ASSERT(reporter, !colorShader1.isOpaque());
+    SkColorShader colorShader2(SkColorSetARGB(0xFF,0,0,0));
+    REPORTER_ASSERT(reporter, colorShader2.isOpaque());
+    SkColorShader colorShader3(SkColorSetARGB(0x7F,0,0,0));
+    REPORTER_ASSERT(reporter, !colorShader3.isOpaque());
+
+    // with inherrited color, shader must declare itself as opaque, 
+    // since lack of opacity will depend solely on the paint
+    SkColorShader colorShader4;
+    REPORTER_ASSERT(reporter, colorShader4.isOpaque());
+}
+
+static void test_shader_opacity(skiatest::Reporter* reporter)
+{
+    test_gradient(reporter);
+    test_color(reporter);
+    test_bitmap(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("ShaderOpacity", ShaderOpacityTestClass, test_shader_opacity)