more sophisticated opacity in SkImageShader

The tricky bit here is tracking when we care if the image is opaque,
and when we care if the shader output is opaque. These are usually
the same, except for decal tiling modes.

We can exploit both of these properties:
   - if the shader output is opaque, set alpha to 1.0f after all
     sampling and interpolation to retroactively skip any the unpacking
     and interpolation we did to calculate alpha.
   - if the input is opaque, set alpha to 1.0f after each sample to skip
     unpacking it.  This mostly just helps decal modes, as this will
     itself all be skipped when the output is opaque.

Change-Id: Ie2954e59f0f9da8be7ad5d11f30fdfd4635c4b70
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/270656
Commit-Queue: Mike Klein <mtklein@google.com>
Reviewed-by: Mike Reed <reed@google.com>
diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp
index 6aa1d35..8fec905 100755
--- a/src/shaders/SkImageShader.cpp
+++ b/src/shaders/SkImageShader.cpp
@@ -675,6 +675,10 @@
         case kBGRA_8888_SkColorType: break;
     }
 
+    // We can exploit image opacity to skip work unpacking alpha channels.
+    const bool input_is_opaque = SkAlphaTypeIsOpaque(pm.alphaType())
+                              || SkColorTypeIsAlwaysOpaque(pm.colorType());
+
     // Each call to sample() will try to rewrite the same uniforms over and over,
     // so remember where we start and reset back there each time.  That way each
     // sample() call uses the same uniform offsets.
@@ -737,8 +741,8 @@
                                          std::swap(c.r, c.b);
                                          break;
         }
-        // TODO: move this later, as *a = p->splat(1.0f) after all sampling's done?
-        if (SkAlphaTypeIsOpaque(pm.alphaType()) || SkColorTypeIsAlwaysOpaque(pm.colorType())) {
+        // If we know the image is opaque, jump right to alpha = 1.0f, skipping work to unpack it.
+        if (input_is_opaque) {
             c.a = p->splat(1.0f);
         }
 
@@ -751,6 +755,7 @@
             c.g = p->bit_cast(p->bit_and(mask, p->bit_cast(c.g)));
             c.b = p->bit_cast(p->bit_and(mask, p->bit_cast(c.b)));
             c.a = p->bit_cast(p->bit_and(mask, p->bit_cast(c.a)));
+            // Notice that even if input_is_opaque, c.a might now be 0.
         }
 
         return c;
@@ -832,7 +837,17 @@
                 *a = p->mad(c.a,w, *a);
             }
         }
+    }
 
+    // If the input is opaque and we're not in decal mode, that means the output is too.
+    // Forcing *a to 1.0 here will retroactively skip any work we did to interpolate sample alphas.
+    if (input_is_opaque
+            && fTileModeX != SkTileMode::kDecal
+            && fTileModeY != SkTileMode::kDecal) {
+        *a = p->splat(1.0f);
+    }
+
+    if (quality == kHigh_SkFilterQuality) {
         // Bicubic filtering naturally produces out of range values on both sides of [0,1].
         *a = p->clamp(*a, p->splat(0.0f), p->splat(1.0f));