Add outline alpha

bug:16140822
bug:16566746

This allows background drawables to alter the opacity of a shadow
being cast with their own alpha values.

Change-Id: I49698cc7c1bf4b2b55ffe2f82899543ca62bc61c
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index e9ec565..eee4973 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -355,9 +355,10 @@
             return nSetOutlineEmpty(mNativeRenderNode);
         } else if (outline.mRect != null) {
             return nSetOutlineRoundRect(mNativeRenderNode, outline.mRect.left, outline.mRect.top,
-                    outline.mRect.right, outline.mRect.bottom, outline.mRadius);
+                    outline.mRect.right, outline.mRect.bottom, outline.mRadius, outline.mAlpha);
         } else if (outline.mPath != null) {
-            return nSetOutlineConvexPath(mNativeRenderNode, outline.mPath.mNativePath);
+            return nSetOutlineConvexPath(mNativeRenderNode, outline.mPath.mNativePath,
+                    outline.mAlpha);
         }
         throw new IllegalArgumentException("Unrecognized outline?");
     }
@@ -849,8 +850,9 @@
     private static native boolean nSetProjectBackwards(long renderNode, boolean shouldProject);
     private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldRecieve);
     private static native boolean nSetOutlineRoundRect(long renderNode, int left, int top,
-            int right, int bottom, float radius);
-    private static native boolean nSetOutlineConvexPath(long renderNode, long nativePath);
+            int right, int bottom, float radius, float alpha);
+    private static native boolean nSetOutlineConvexPath(long renderNode, long nativePath,
+            float alpha);
     private static native boolean nSetOutlineEmpty(long renderNode);
     private static native boolean nSetOutlineNone(long renderNode);
     private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
diff --git a/core/java/android/view/ViewOutlineProvider.java b/core/java/android/view/ViewOutlineProvider.java
index 64624ae..4054031 100644
--- a/core/java/android/view/ViewOutlineProvider.java
+++ b/core/java/android/view/ViewOutlineProvider.java
@@ -25,7 +25,8 @@
 public abstract class ViewOutlineProvider {
     /**
      * Default outline provider for Views, which queries the Outline from the View's background,
-     * or returns <code>false</code> if the View does not have a background.
+     * or generates a 0 alpha, rectangular Outline the size of the View if a background
+     * isn't present.
      *
      * @see Drawable#getOutline(Outline)
      */
@@ -35,6 +36,10 @@
             Drawable background = view.getBackground();
             if (background != null) {
                 background.getOutline(outline);
+            } else {
+
+                outline.setRect(0, 0, view.getWidth(), view.getHeight());
+                outline.setAlpha(0.0f);
             }
         }
     };
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index 790b611..9e24844 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -16,10 +16,12 @@
 
 package com.android.internal.widget;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.Outline;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.ActionMode;
@@ -324,18 +326,36 @@
      * projection surfaces.
      */
     private class ActionBarBackgroundDrawable extends Drawable {
-        @Override
-        public void draw(Canvas canvas) {
+        private Drawable getDrawable() {
             if (mIsSplit) {
-                if (mSplitBackground != null) mSplitBackground.draw(canvas);
+                if (mSplitBackground != null) {
+                    return mSplitBackground;
+                }
             } else {
                 if (mBackground != null) {
-                    mBackground.draw(canvas);
+                    return mBackground;
                 }
                 if (mStackedBackground != null && mIsStacked) {
-                    mStackedBackground.draw(canvas);
+                    return mStackedBackground;
                 }
             }
+            return null;
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            final Drawable drawable = getDrawable();
+            if (drawable != null) {
+                drawable.draw(canvas);
+            }
+        }
+
+        @Override
+        public void getOutline(@NonNull Outline outline) {
+            final Drawable drawable = getDrawable();
+            if (drawable != null) {
+                drawable.getOutline(outline);
+            }
         }
 
         @Override
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index a890eb4..2ce1b15 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -353,7 +353,7 @@
         ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID,
                 peeker.mOpticalInsets[0], peeker.mOpticalInsets[1], peeker.mOpticalInsets[2], peeker.mOpticalInsets[3],
                 peeker.mOutlineInsets[0], peeker.mOutlineInsets[1], peeker.mOutlineInsets[2], peeker.mOutlineInsets[3],
-                peeker.mOutlineRadius, peeker.mOutlineFilled, scale);
+                peeker.mOutlineRadius, peeker.mOutlineAlpha, scale);
         if (javaBitmap != NULL) {
             env->SetObjectField(javaBitmap, gBitmap_ninePatchInsetsFieldID, ninePatchInsets);
         }
@@ -589,7 +589,7 @@
             "Landroid/graphics/NinePatch$InsetStruct;");
 
     gInsetStruct_class = (jclass) env->NewGlobalRef(env->FindClass("android/graphics/NinePatch$InsetStruct"));
-    gInsetStruct_constructorMethodID = env->GetMethodID(gInsetStruct_class, "<init>", "(IIIIIIIIFZF)V");
+    gInsetStruct_constructorMethodID = env->GetMethodID(gInsetStruct_class, "<init>", "(IIIIIIIIFIF)V");
 
     int ret = AndroidRuntime::registerNativeMethods(env,
                                     "android/graphics/BitmapFactory$Options",
diff --git a/core/jni/android/graphics/NinePatchPeeker.cpp b/core/jni/android/graphics/NinePatchPeeker.cpp
index ea5193b..1dafa1b 100644
--- a/core/jni/android/graphics/NinePatchPeeker.cpp
+++ b/core/jni/android/graphics/NinePatchPeeker.cpp
@@ -48,11 +48,11 @@
     } else if (!strcmp("npLb", tag) && length == sizeof(int32_t) * 4) {
         mHasInsets = true;
         memcpy(&mOpticalInsets, data, sizeof(int32_t) * 4);
-    } else if (!strcmp("npOl", tag) && length == 24) { // 4 int32_ts, 1 float, 1 int32_t sized bool
+    } else if (!strcmp("npOl", tag) && length == 24) { // 4 int32_ts, 1 float, 1 int32_t sized byte
         mHasInsets = true;
         memcpy(&mOutlineInsets, data, sizeof(int32_t) * 4);
         mOutlineRadius = ((const float*)data)[4];
-        mOutlineFilled = ((const int32_t*)data)[5] & 0x01;
+        mOutlineAlpha = ((const int32_t*)data)[5] & 0xff;
     }
     return true;    // keep on decoding
 }
diff --git a/core/jni/android/graphics/NinePatchPeeker.h b/core/jni/android/graphics/NinePatchPeeker.h
index 8d3e6cf..7c18b2d 100644
--- a/core/jni/android/graphics/NinePatchPeeker.h
+++ b/core/jni/android/graphics/NinePatchPeeker.h
@@ -33,7 +33,7 @@
             , mPatchSize(0)
             , mHasInsets(false)
             , mOutlineRadius(0)
-            , mOutlineFilled(false) {
+            , mOutlineAlpha(0) {
         memset(mOpticalInsets, 0, 4 * sizeof(int32_t));
         memset(mOutlineInsets, 0, 4 * sizeof(int32_t));
     }
@@ -50,7 +50,7 @@
     int32_t mOpticalInsets[4];
     int32_t mOutlineInsets[4];
     float mOutlineRadius;
-    bool mOutlineFilled;
+    uint8_t mOutlineAlpha;
 };
 
 #endif // NinePatchPeeker_h
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index ff54fb9..1e9d722 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -140,18 +140,19 @@
 
 static jboolean android_view_RenderNode_setOutlineRoundRect(JNIEnv* env,
         jobject clazz, jlong renderNodePtr, jint left, jint top,
-        jint right, jint bottom, jfloat radius) {
+        jint right, jint bottom, jfloat radius, jfloat alpha) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
-    renderNode->mutateStagingProperties().mutableOutline().setRoundRect(left, top, right, bottom, radius);
+    renderNode->mutateStagingProperties().mutableOutline().setRoundRect(left, top, right, bottom,
+            radius, alpha);
     renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
     return true;
 }
 
 static jboolean android_view_RenderNode_setOutlineConvexPath(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jlong outlinePathPtr) {
+        jobject clazz, jlong renderNodePtr, jlong outlinePathPtr, jfloat alpha) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     SkPath* outlinePath = reinterpret_cast<SkPath*>(outlinePathPtr);
-    renderNode->mutateStagingProperties().mutableOutline().setConvexPath(outlinePath);
+    renderNode->mutateStagingProperties().mutableOutline().setConvexPath(outlinePath, alpha);
     renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
     return true;
 }
@@ -480,8 +481,8 @@
     { "nSetProjectBackwards",  "(JZ)Z",  (void*) android_view_RenderNode_setProjectBackwards },
     { "nSetProjectionReceiver","(JZ)Z",  (void*) android_view_RenderNode_setProjectionReceiver },
 
-    { "nSetOutlineRoundRect",  "(JIIIIF)Z", (void*) android_view_RenderNode_setOutlineRoundRect },
-    { "nSetOutlineConvexPath", "(JJ)Z",  (void*) android_view_RenderNode_setOutlineConvexPath },
+    { "nSetOutlineRoundRect",  "(JIIIIFF)Z", (void*) android_view_RenderNode_setOutlineRoundRect },
+    { "nSetOutlineConvexPath", "(JJF)Z", (void*) android_view_RenderNode_setOutlineConvexPath },
     { "nSetOutlineEmpty",      "(J)Z",   (void*) android_view_RenderNode_setOutlineEmpty },
     { "nSetOutlineNone",       "(J)Z",   (void*) android_view_RenderNode_setOutlineNone },
     { "nSetClipToOutline",     "(JZ)Z",  (void*) android_view_RenderNode_setClipToOutline },
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 335bce0..3bccf08 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -43,7 +43,7 @@
         @SuppressWarnings({"UnusedDeclaration"}) // called from JNI
         InsetStruct(int opticalLeft, int opticalTop, int opticalRight, int opticalBottom,
                 int outlineLeft, int outlineTop, int outlineRight, int outlineBottom,
-                float outlineRadius, boolean outlineFilled, float decodeScale) {
+                float outlineRadius, int outlineAlpha, float decodeScale) {
             opticalRect = new Rect(opticalLeft, opticalTop, opticalRight, opticalBottom);
             outlineRect = new Rect(outlineLeft, outlineTop, outlineRight, outlineBottom);
 
@@ -55,13 +55,13 @@
                 outlineRect.scaleRoundIn(decodeScale);
             }
             this.outlineRadius = outlineRadius * decodeScale;
-            this.outlineFilled = outlineFilled;
+            this.outlineAlpha = outlineAlpha / 255.0f;
         }
 
         public final Rect opticalRect;
         public final Rect outlineRect;
         public final float outlineRadius;
-        public final boolean outlineFilled;
+        public final float outlineAlpha;
     }
 
     private final Bitmap mBitmap;
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 3a4c2a7..1cf5f37 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -37,9 +37,8 @@
     public Rect mRect;
     /** @hide */
     public float mRadius;
-
     /** @hide */
-    public boolean mIsFilled;
+    public float mAlpha;
 
     /**
      * Constructs an empty Outline. Call one of the setter methods to make
@@ -63,7 +62,6 @@
         mPath = null;
         mRect = null;
         mRadius = 0;
-        mIsFilled = true;
     }
 
     /**
@@ -92,24 +90,24 @@
     }
 
     /**
-     * Sets whether the outline represents a fully opaque area.
+     * Sets the alpha represented by the Outline.
      *
-     * A filled outline is assumed, by the drawing system, to fully cover content beneath it,
-     * meaning content beneath may be optimized away.
+     * Content producing a fully opaque (alpha = 1.0f) outline is assumed, by the drawing system,
+     * to fully cover content beneath it, meaning content beneath may be optimized away.
      *
      * @hide
      */
-    public void setFilled(boolean isFilled) {
-        mIsFilled = isFilled;
+    public void setAlpha(float alpha) {
+        mAlpha = alpha;
     }
 
     /**
-     * Returns whether the outline represents a fully opaque area.
+     * Sets the alpha represented by the Outline.
      *
      * @hide
      */
-    public boolean isFilled() {
-        return !isEmpty() && mIsFilled;
+    public float getAlpha() {
+        return mAlpha;
     }
 
     /**
@@ -132,7 +130,7 @@
             mRect.set(src.mRect);
         }
         mRadius = src.mRadius;
-        mIsFilled = src.mIsFilled;
+        mAlpha = src.mAlpha;
     }
 
     /**
@@ -164,7 +162,6 @@
         mRect.set(left, top, right, bottom);
         mRadius = radius;
         mPath = null;
-        mIsFilled = true;
     }
 
     /**
@@ -193,7 +190,6 @@
         mPath.reset();
         mPath.addOval(left, top, right, bottom, Path.Direction.CW);
         mRect = null;
-        mIsFilled = true;
     }
 
     /**
@@ -220,6 +216,5 @@
         mPath.set(convexPath);
         mRect = null;
         mRadius = -1.0f;
-        mIsFilled = true;
     }
 }
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index acb34c2..df9f3c3 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -861,16 +861,14 @@
      * This method is called by the default {@link android.view.ViewOutlineProvider} to define
      * the outline of the View.
      * <p>
-     * The default behavior defines the outline to be the bounding rectangle. Subclasses that wish
-     * to convey a different shape must override this method.
-     *
-     * @return true if this drawable actually has an outline, else false. The outline must be
-     *         populated by the drawable if true is returned.
+     * The default behavior defines the outline to be the bounding rectangle of 0 alpha.
+     * Subclasses that wish to convey a different shape or alpha value must override this method.
      *
      * @see android.view.View#setOutlineProvider(android.view.ViewOutlineProvider)
      */
     public void getOutline(@NonNull Outline outline) {
         outline.setRect(getBounds());
+        outline.setAlpha(getAlpha() / 255.0f);
     }
 
     /**
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 4815586..a383aab 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -1413,6 +1413,7 @@
     public void getOutline(Outline outline) {
         final GradientState st = mGradientState;
         final Rect bounds = getBounds();
+        outline.setAlpha(mAlpha / 255.0f);
 
         switch (st.mShape) {
             case RECTANGLE:
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 073100a..d094ce4 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -595,12 +595,9 @@
     }
 
     /**
-     * Populates <code>outline</code> with the first available layer outline.
-     * Returns <code>true</code> if an outline is available, <code>false</code>
-     * otherwise.
+     * Populates <code>outline</code> with the first available (non-empty) layer outline.
      *
      * @param outline Outline in which to place the first available layer outline
-     * @return <code>true</code> if an outline is available
      */
     @Override
     public void getOutline(@NonNull Outline outline) {
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 241ec65..eb313ae 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -297,7 +297,7 @@
                         bounds.right - outlineInsets.right,
                         bounds.bottom - outlineInsets.bottom,
                         insets.outlineRadius);
-                outline.setFilled(insets.outlineFilled);
+                outline.setAlpha(insets.outlineAlpha * (getAlpha() / 255.0f));
                 return;
             }
         }
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index eb7291c..cf675ed 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -585,11 +585,9 @@
 
     /**
      * Populates <code>outline</code> with the first available layer outline,
-     * excluding the mask layer. Returns <code>true</code> if an outline is
-     * available, <code>false</code> otherwise.
+     * excluding the mask layer.
      *
      * @param outline Outline in which to place the first available layer outline
-     * @return <code>true</code> if an outline is available
      */
     @Override
     public void getOutline(@NonNull Outline outline) {
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index 2bed3b0..394f584 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -470,6 +470,7 @@
     public void getOutline(Outline outline) {
         if (mShapeState.mShape != null) {
             mShapeState.mShape.getOutline(outline);
+            outline.setAlpha(getAlpha() / 255.0f);
         }
     }
 
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
index 83426e8..6dacd5ed 100644
--- a/libs/hwui/Outline.h
+++ b/libs/hwui/Outline.h
@@ -28,18 +28,20 @@
     Outline()
             : mShouldClip(false)
             , mType(kOutlineType_None)
-            , mRadius(0) {}
+            , mRadius(0)
+            , mAlpha(0.0f) {}
 
-    void setRoundRect(int left, int top, int right, int bottom, float radius) {
+    void setRoundRect(int left, int top, int right, int bottom, float radius, float alpha) {
         mType = kOutlineType_RoundRect;
         mBounds.set(left, top, right, bottom);
         mRadius = radius;
         mPath.reset();
         mPath.addRoundRect(SkRect::MakeLTRB(left, top, right, bottom),
                 radius, radius);
+        mAlpha = alpha;
     }
 
-    void setConvexPath(const SkPath* outline) {
+    void setConvexPath(const SkPath* outline, float alpha) {
         if (!outline) {
             setEmpty();
             return;
@@ -47,22 +49,29 @@
         mType = kOutlineType_ConvexPath;
         mPath = *outline;
         mBounds.set(outline->getBounds());
+        mAlpha = alpha;
     }
 
     void setEmpty() {
         mType = kOutlineType_Empty;
         mPath.reset();
+        mAlpha = 0.0f;
     }
 
     void setNone() {
         mType = kOutlineType_None;
         mPath.reset();
+        mAlpha = 0.0f;
     }
 
     bool isEmpty() const {
         return mType == kOutlineType_Empty;
     }
 
+    float getAlpha() const {
+        return mAlpha;
+    }
+
     void setShouldClip(bool clip) {
         mShouldClip = clip;
     }
@@ -103,6 +112,7 @@
     OutlineType mType;
     Rect mBounds;
     float mRadius;
+    float mAlpha;
     SkPath mPath;
 };
 
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 0662ca2..fa1b21d 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -594,7 +594,12 @@
 
 template <class T>
 void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
-    if (properties().getAlpha() <= 0.0f || !properties().getOutline().getPath()) return;
+    if (properties().getAlpha() <= 0.0f
+            || properties().getOutline().getAlpha() <= 0.0f
+            || !properties().getOutline().getPath()) {
+        // no shadow to draw
+        return;
+    }
 
     mat4 shadowMatrixXY(transformFromParent);
     applyViewPropertyTransforms(shadowMatrixXY);
@@ -607,8 +612,9 @@
     const SkPath* revealClipPath = properties().getRevealClip().getPath();
     if (revealClipPath && revealClipPath->isEmpty()) return;
 
+    float casterAlpha = properties().getAlpha() * properties().getOutline().getAlpha();
     DisplayListOp* shadowOp  = new (handler.allocator()) DrawShadowOp(
-            shadowMatrixXY, shadowMatrixZ, properties().getAlpha(),
+            shadowMatrixXY, shadowMatrixZ, casterAlpha,
             outlinePath, revealClipPath);
     handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
 }
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 28de933..cf3dd0a 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -83,7 +83,7 @@
     int32_t outlineInsetsRight;
     int32_t outlineInsetsBottom;
     float outlineRadius;
-    bool outlineFilled;
+    uint8_t outlineAlpha;
 
     png_uint_32 allocHeight;
     png_bytepp allocRows;
@@ -410,12 +410,12 @@
                              int* out_inset)
 {
     bool opaque_within_inset = true;
-    unsigned char max_opacity = 0;
+    uint8_t max_opacity = 0;
     int inset = 0;
     *out_inset = 0;
     for (int x = startX, y = startY; x != endX && y != endY; x += dX, y += dY, inset++) {
         png_byte* color = rows[y] + x * 4;
-        unsigned char opacity = color[3];
+        uint8_t opacity = color[3];
         if (opacity > max_opacity) {
             max_opacity = opacity;
             *out_inset = inset;
@@ -424,22 +424,24 @@
     }
 }
 
-static bool is_opaque_over_row(png_byte* row, int startX, int endX)
+static uint8_t max_alpha_over_row(png_byte* row, int startX, int endX)
 {
+    uint8_t max_alpha = 0;
     for (int x = startX; x < endX; x++) {
-        png_byte* color = row + x * 4;
-        if (color[3] != 0xff) return false;
+        uint8_t alpha = (row + x * 4)[3];
+        if (alpha > max_alpha) max_alpha = alpha;
     }
-    return true;
+    return max_alpha;
 }
 
-static bool is_opaque_over_col(png_byte** rows, int offsetX, int startY, int endY)
+static uint8_t max_alpha_over_col(png_byte** rows, int offsetX, int startY, int endY)
 {
+    uint8_t max_alpha = 0;
     for (int y = startY; y < endY; y++) {
-        png_byte* color = rows[y] + offsetX * 4;
-        if (color[3] != 0xff) return false;
+        uint8_t alpha = (rows[y] + offsetX * 4)[3];
+        if (alpha > max_alpha) max_alpha = alpha;
     }
-    return true;
+    return max_alpha;
 }
 
 static void get_outline(image_info* image)
@@ -476,8 +478,8 @@
 
     // assuming the image is a round rect, compute the radius by marching
     // diagonally from the top left corner towards the center
-    image->outlineFilled = is_opaque_over_row(image->rows[innerMidY], innerStartX, innerEndX)
-            && is_opaque_over_col(image->rows, innerMidX, innerStartY, innerStartY);
+    image->outlineAlpha = max(max_alpha_over_row(image->rows[innerMidY], innerStartX, innerEndX),
+            max_alpha_over_col(image->rows, innerMidX, innerStartY, innerStartY));
 
     int diagonalInset = 0;
     find_max_opacity(image->rows, innerStartX, innerStartY, innerMidX, innerMidY, 1, 1,
@@ -487,14 +489,13 @@
     // radius = 1 / (sqrt(2) - 1) * inset
     image->outlineRadius = 2.4142f * diagonalInset;
 
-    NOISY(printf("outline insets %d %d %d %d, rad %f, filled %d\n",
-            image->outlineFilled,
+    NOISY(printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
             image->outlineInsetsLeft,
             image->outlineInsetsTop,
             image->outlineInsetsRight,
             image->outlineInsetsBottom,
             image->outlineRadius,
-            image->outlineFilled));
+            image->outlineAlpha));
 }
 
 
@@ -691,8 +692,8 @@
     }
 
     NOISY(printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
-                 image->info9Patch.xDivs[0], image->info9Patch.xDivs[1],
-                 image->info9Patch.yDivs[0], image->info9Patch.yDivs[1]));
+                 xDivs[0], xDivs[1],
+                 yDivs[0], yDivs[1]));
     NOISY(printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
                  image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
                  image->info9Patch.paddingTop, image->info9Patch.paddingBottom));
@@ -1167,7 +1168,7 @@
         png_byte outputData[chunk_size];
         memcpy(&outputData, &imageInfo.outlineInsetsLeft, 4 * sizeof(png_uint_32));
         ((float*) outputData)[4] = imageInfo.outlineRadius;
-        ((png_uint_32*) outputData)[5] = imageInfo.outlineFilled ? 1 : 0;
+        ((png_uint_32*) outputData)[5] = imageInfo.outlineAlpha;
         memcpy(unknowns[o_index].data, &outputData, chunk_size);
         unknowns[o_index].size = chunk_size;