Add ninePatch support to Canvas.h

Change-Id: Ic095291fe55911c6501c1bdefa4b8da973c77319
diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk
new file mode 100644
index 0000000..5c48709
--- /dev/null
+++ b/libs/hwui/Android.common.mk
@@ -0,0 +1,118 @@
+# getConfig in external/skia/include/core/SkBitmap.h is deprecated.
+# Allow Gnu extension: in-class initializer of static 'const float' member.
+# DeferredLayerUpdater.h: private field 'mRenderThread' is not used.
+
+LOCAL_SRC_FILES := \
+    font/CacheTexture.cpp \
+    font/Font.cpp \
+    renderstate/Blend.cpp \
+    renderstate/MeshState.cpp \
+    renderstate/PixelBufferState.cpp \
+    renderstate/RenderState.cpp \
+    renderstate/Scissor.cpp \
+    renderstate/Stencil.cpp \
+    renderstate/TextureState.cpp \
+    renderthread/CanvasContext.cpp \
+    renderthread/DrawFrameTask.cpp \
+    renderthread/EglManager.cpp \
+    renderthread/RenderProxy.cpp \
+    renderthread/RenderTask.cpp \
+    renderthread/RenderThread.cpp \
+    renderthread/TimeLord.cpp \
+    thread/TaskManager.cpp \
+    utils/Blur.cpp \
+    utils/GLUtils.cpp \
+    utils/LinearAllocator.cpp \
+    utils/NinePatchImpl.cpp \
+    AmbientShadow.cpp \
+    AnimationContext.cpp \
+    Animator.cpp \
+    AnimatorManager.cpp \
+    AssetAtlas.cpp \
+    Caches.cpp \
+    CanvasState.cpp \
+    ClipArea.cpp \
+    DamageAccumulator.cpp \
+    DeferredDisplayList.cpp \
+    DeferredLayerUpdater.cpp \
+    DisplayList.cpp \
+    DisplayListCanvas.cpp \
+    Dither.cpp \
+    Extensions.cpp \
+    FboCache.cpp \
+    FontRenderer.cpp \
+    FrameInfo.cpp \
+    FrameInfoVisualizer.cpp \
+    GammaFontRenderer.cpp \
+    GlopBuilder.cpp \
+    GradientCache.cpp \
+    Image.cpp \
+    Interpolator.cpp \
+    JankTracker.cpp \
+    Layer.cpp \
+    LayerCache.cpp \
+    LayerRenderer.cpp \
+    Matrix.cpp \
+    OpenGLRenderer.cpp \
+    Patch.cpp \
+    PatchCache.cpp \
+    PathCache.cpp \
+    PathTessellator.cpp \
+    PixelBuffer.cpp \
+    Program.cpp \
+    ProgramCache.cpp \
+    Properties.cpp \
+    RenderBufferCache.cpp \
+    RenderNode.cpp \
+    RenderProperties.cpp \
+    ResourceCache.cpp \
+    ShadowTessellator.cpp \
+    SkiaCanvas.cpp \
+    SkiaCanvasProxy.cpp \
+    SkiaShader.cpp \
+    Snapshot.cpp \
+    SpotShadow.cpp \
+    TessellationCache.cpp \
+    TextDropShadowCache.cpp \
+    Texture.cpp \
+    TextureCache.cpp
+
+intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)
+
+LOCAL_C_INCLUDES += \
+    external/skia/src/core
+
+LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
+LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui libgui
+
+ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
+    LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT
+    LOCAL_SHARED_LIBRARIES += libRS libRScpp
+    LOCAL_C_INCLUDES += \
+        $(intermediates) \
+        frameworks/rs/cpp \
+        frameworks/rs \
+
+endif
+
+ifndef HWUI_COMPILE_SYMBOLS
+    LOCAL_CFLAGS += -fvisibility=hidden
+endif
+
+ifdef HWUI_COMPILE_FOR_PERF
+    # TODO: Non-arm?
+    LOCAL_CFLAGS += -fno-omit-frame-pointer -marm -mapcs
+endif
+
+ifeq (true, $(HWUI_NULL_GPU))
+    LOCAL_SRC_FILES += \
+        tests/nullegl.cpp \
+        tests/nullgles.cpp
+
+    LOCAL_CFLAGS += -DHWUI_NULL_GPU
+endif
+
+# Defaults for ATRACE_TAG and LOG_TAG for libhwui
+LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\"
+LOCAL_CFLAGS += -Wall -Wno-unused-parameter -Wunreachable-code
+LOCAL_CFLAGS += -ffast-math -O3 -Werror
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index e5bc664..b3b3479 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -23,6 +23,7 @@
     utils/Blur.cpp \
     utils/GLUtils.cpp \
     utils/LinearAllocator.cpp \
+    utils/NinePatchImpl.cpp \
     AmbientShadow.cpp \
     AnimationContext.cpp \
     Animator.cpp \
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index a0b87f9..3c77b3d 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -19,6 +19,8 @@
 
 #include <cutils/compiler.h>
 
+#include "utils/NinePatch.h"
+
 #include <SkBitmap.h>
 #include <SkCanvas.h>
 #include <SkMatrix.h>
@@ -144,6 +146,9 @@
             float dstRight, float dstBottom, const SkPaint* paint) = 0;
     virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
             const float* vertices, const int* colors, const SkPaint* paint) = 0;
+    virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+            float dstLeft, float dstTop, float dstRight, float dstBottom,
+            const SkPaint* paint) = 0;
 
     // Text
     /**
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index c48b070..a8dd71e 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -320,13 +320,14 @@
            vertices, colors, paint));
 }
 
-void DisplayListCanvas::drawPatch(const SkBitmap& bitmap, const Res_png_9patch* patch,
-        float left, float top, float right, float bottom, const SkPaint* paint) {
+void DisplayListCanvas::drawNinePatch(const SkBitmap& bitmap, const Res_png_9patch& patch,
+        float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
     const SkBitmap* bitmapPtr = refBitmap(bitmap);
-    patch = refPatch(patch);
+    const Res_png_9patch* patchPtr = refPatch(&patch);
     paint = refPaint(paint);
 
-    addDrawOp(new (alloc()) DrawPatchOp(bitmapPtr, patch, left, top, right, bottom, paint));
+    addDrawOp(new (alloc()) DrawPatchOp(bitmapPtr, patchPtr,
+            dstLeft, dstTop, dstRight, dstBottom, paint));
 }
 
 void DisplayListCanvas::drawColor(int color, SkXfermode::Mode mode) {
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index f3f5713..3b61904 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -79,10 +79,6 @@
 // HWUI Canvas draw operations
 // ----------------------------------------------------------------------------
 
-    // TODO: move drawPatch() to Canvas.h
-    void drawPatch(const SkBitmap& bitmap, const Res_png_9patch* patch,
-            float left, float top, float right, float bottom, const SkPaint* paint);
-
     // Shapes
     void drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
                 CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
@@ -209,6 +205,9 @@
             float dstRight, float dstBottom, const SkPaint* paint) override;
     virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
             const float* vertices, const int* colors, const SkPaint* paint) override;
+    virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+            float dstLeft, float dstTop, float dstRight, float dstBottom,
+            const SkPaint* paint) override;
 
     // Text
     virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index b2d1fec..f7dd83a 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -124,6 +124,9 @@
             float dstRight, float dstBottom, const SkPaint* paint) override;
     virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
             const float* vertices, const int* colors, const SkPaint* paint) override;
+    virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+            float dstLeft, float dstTop, float dstRight, float dstBottom,
+            const SkPaint* paint) override;
 
     virtual void drawText(const uint16_t* text, const float* positions, int count,
             const SkPaint& paint, float x, float y,
@@ -680,6 +683,12 @@
                          indexCount, tmpPaint);
 }
 
+void SkiaCanvas::drawNinePatch(const SkBitmap& bitmap, const Res_png_9patch& chunk,
+        float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
+    SkRect bounds = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+    NinePatch::Draw(mCanvas, bounds, bitmap, chunk, paint, nullptr);
+}
+
 // ----------------------------------------------------------------------------
 // Canvas draw operations: Text
 // ----------------------------------------------------------------------------
diff --git a/libs/hwui/utils/NinePatch.h b/libs/hwui/utils/NinePatch.h
new file mode 100644
index 0000000..323e563
--- /dev/null
+++ b/libs/hwui/utils/NinePatch.h
@@ -0,0 +1,37 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_GRAPHICS_NINEPATCH_H
+#define ANDROID_GRAPHICS_NINEPATCH_H
+
+#include <androidfw/ResourceTypes.h>
+#include <cutils/compiler.h>
+
+#include "SkCanvas.h"
+#include "SkRegion.h"
+
+namespace android {
+
+class ANDROID_API NinePatch {
+public:
+    static void Draw(SkCanvas* canvas, const SkRect& bounds, const SkBitmap& bitmap,
+            const Res_png_9patch& chunk, const SkPaint* paint, SkRegion** outRegion);
+};
+
+} // namespace android
+
+#endif // ANDROID_GRAPHICS_NINEPATCH_H
diff --git a/libs/hwui/utils/NinePatchImpl.cpp b/libs/hwui/utils/NinePatchImpl.cpp
new file mode 100644
index 0000000..f51f5df
--- /dev/null
+++ b/libs/hwui/utils/NinePatchImpl.cpp
@@ -0,0 +1,326 @@
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "utils/NinePatch.h"
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkNinePatch.h"
+#include "SkPaint.h"
+#include "SkUnPreMultiply.h"
+
+#include <utils/Log.h>
+
+namespace android {
+
+static const bool kUseTrace = true;
+static bool gTrace = false;
+
+static bool getColor(const SkBitmap& bitmap, int x, int y, SkColor* c) {
+    switch (bitmap.colorType()) {
+        case kN32_SkColorType:
+            *c = SkUnPreMultiply::PMColorToColor(*bitmap.getAddr32(x, y));
+            break;
+        case kRGB_565_SkColorType:
+            *c = SkPixel16ToPixel32(*bitmap.getAddr16(x, y));
+            break;
+        case kARGB_4444_SkColorType:
+            *c = SkUnPreMultiply::PMColorToColor(
+                                SkPixel4444ToPixel32(*bitmap.getAddr16(x, y)));
+            break;
+        case kIndex_8_SkColorType: {
+            SkColorTable* ctable = bitmap.getColorTable();
+            *c = SkUnPreMultiply::PMColorToColor(
+                                            (*ctable)[*bitmap.getAddr8(x, y)]);
+            break;
+        }
+        default:
+            return false;
+    }
+    return true;
+}
+
+static SkColor modAlpha(SkColor c, int alpha) {
+    int scale = alpha + (alpha >> 7);
+    int a = SkColorGetA(c) * scale >> 8;
+    return SkColorSetA(c, a);
+}
+
+static void drawStretchyPatch(SkCanvas* canvas, SkIRect& src, const SkRect& dst,
+                              const SkBitmap& bitmap, const SkPaint& paint,
+                              SkColor initColor, uint32_t colorHint,
+                              bool hasXfer) {
+    if (colorHint !=  android::Res_png_9patch::NO_COLOR) {
+        ((SkPaint*)&paint)->setColor(modAlpha(colorHint, paint.getAlpha()));
+        canvas->drawRect(dst, paint);
+        ((SkPaint*)&paint)->setColor(initColor);
+    } else if (src.width() == 1 && src.height() == 1) {
+        SkColor c;
+        if (!getColor(bitmap, src.fLeft, src.fTop, &c)) {
+            goto SLOW_CASE;
+        }
+        if (0 != c || hasXfer) {
+            SkColor prev = paint.getColor();
+            ((SkPaint*)&paint)->setColor(c);
+            canvas->drawRect(dst, paint);
+            ((SkPaint*)&paint)->setColor(prev);
+        }
+    } else {
+    SLOW_CASE:
+        canvas->drawBitmapRect(bitmap, &src, dst, &paint);
+    }
+}
+
+SkScalar calculateStretch(SkScalar boundsLimit, SkScalar startingPoint,
+                          int srcSpace, int numStrechyPixelsRemaining,
+                          int numFixedPixelsRemaining) {
+    SkScalar spaceRemaining = boundsLimit - startingPoint;
+    SkScalar stretchySpaceRemaining =
+                spaceRemaining - SkIntToScalar(numFixedPixelsRemaining);
+    return srcSpace * stretchySpaceRemaining / numStrechyPixelsRemaining;
+}
+
+void NinePatch::Draw(SkCanvas* canvas, const SkRect& bounds,
+                     const SkBitmap& bitmap, const Res_png_9patch& chunk,
+                     const SkPaint* paint, SkRegion** outRegion) {
+    if (canvas && canvas->quickReject(bounds)) {
+        return;
+    }
+
+    SkPaint defaultPaint;
+    if (NULL == paint) {
+        // matches default dither in NinePatchDrawable.java.
+        defaultPaint.setDither(true);
+        paint = &defaultPaint;
+    }
+   
+    const int32_t* xDivs = chunk.getXDivs();
+    const int32_t* yDivs = chunk.getYDivs();
+    // if our SkCanvas were back by GL we should enable this and draw this as
+    // a mesh, which will be faster in most cases.
+    if ((false)) {
+        SkNinePatch::DrawMesh(canvas, bounds, bitmap,
+                              xDivs, chunk.numXDivs,
+                              yDivs, chunk.numYDivs,
+                              paint);
+        return;
+    }
+
+    if (kUseTrace) {
+        gTrace = true;
+    }
+
+    SkASSERT(canvas || outRegion);
+
+    if (kUseTrace) {
+        if (canvas) {
+            const SkMatrix& m = canvas->getTotalMatrix();
+            ALOGV("ninepatch [%g %g %g] [%g %g %g]\n",
+                    SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]),
+                    SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5]));
+        }
+
+        ALOGV("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()),
+                SkScalarToFloat(bounds.height()));
+        ALOGV("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height());
+        ALOGV("======== ninepatch xDivs [%d,%d]\n", xDivs[0], xDivs[1]);
+        ALOGV("======== ninepatch yDivs [%d,%d]\n", yDivs[0], yDivs[1]);
+    }
+
+    if (bounds.isEmpty() ||
+        bitmap.width() == 0 || bitmap.height() == 0 ||
+        (paint && paint->getXfermode() == NULL && paint->getAlpha() == 0))
+    {
+        if (kUseTrace) {
+            ALOGV("======== abort ninepatch draw\n");
+        }
+        return;
+    }
+    
+    // should try a quick-reject test before calling lockPixels 
+
+    SkAutoLockPixels alp(bitmap);
+    // after the lock, it is valid to check getPixels()
+    if (bitmap.getPixels() == NULL)
+        return;
+
+    const bool hasXfer = paint->getXfermode() != NULL;
+    SkRect      dst;
+    SkIRect     src;
+
+    const int32_t x0 = xDivs[0];
+    const int32_t y0 = yDivs[0];
+    const SkColor initColor = ((SkPaint*)paint)->getColor();
+    const uint8_t numXDivs = chunk.numXDivs;
+    const uint8_t numYDivs = chunk.numYDivs;
+    int i;
+    int j;
+    int colorIndex = 0;
+    uint32_t color;
+    bool xIsStretchable;
+    const bool initialXIsStretchable =  (x0 == 0);
+    bool yIsStretchable = (y0 == 0);
+    const int bitmapWidth = bitmap.width();
+    const int bitmapHeight = bitmap.height();
+
+    // Number of bytes needed for dstRights array.
+    // Need to cast numXDivs to a larger type to avoid overflow.
+    const size_t dstBytes = ((size_t) numXDivs + 1) * sizeof(SkScalar);
+    SkScalar* dstRights = (SkScalar*) alloca(dstBytes);
+    bool dstRightsHaveBeenCached = false;
+
+    int numStretchyXPixelsRemaining = 0;
+    for (i = 0; i < numXDivs; i += 2) {
+        numStretchyXPixelsRemaining += xDivs[i + 1] - xDivs[i];
+    }
+    int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining;
+    int numStretchyYPixelsRemaining = 0;
+    for (i = 0; i < numYDivs; i += 2) {
+        numStretchyYPixelsRemaining += yDivs[i + 1] - yDivs[i];
+    }
+    int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining;
+
+    if (kUseTrace) {
+        ALOGV("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n",
+                bitmap.width(), bitmap.height(),
+                SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
+                SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()),
+                numXDivs, numYDivs);
+    }
+
+    src.fTop = 0;
+    dst.fTop = bounds.fTop;
+    // The first row always starts with the top being at y=0 and the bottom
+    // being either yDivs[1] (if yDivs[0]=0) or yDivs[0].  In the former case
+    // the first row is stretchable along the Y axis, otherwise it is fixed.
+    // The last row always ends with the bottom being bitmap.height and the top
+    // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
+    // yDivs[numYDivs-1]. In the former case the last row is stretchable along
+    // the Y axis, otherwise it is fixed.
+    //
+    // The first and last columns are similarly treated with respect to the X
+    // axis.
+    //
+    // The above is to help explain some of the special casing that goes on the
+    // code below.
+
+    // The initial yDiv and whether the first row is considered stretchable or
+    // not depends on whether yDiv[0] was zero or not.
+    for (j = yIsStretchable ? 1 : 0;
+          j <= numYDivs && src.fTop < bitmapHeight;
+          j++, yIsStretchable = !yIsStretchable) {
+        src.fLeft = 0;
+        dst.fLeft = bounds.fLeft;
+        if (j == numYDivs) {
+            src.fBottom = bitmapHeight;
+            dst.fBottom = bounds.fBottom;
+        } else {
+            src.fBottom = yDivs[j];
+            const int srcYSize = src.fBottom - src.fTop;
+            if (yIsStretchable) {
+                dst.fBottom = dst.fTop + calculateStretch(bounds.fBottom, dst.fTop,
+                                                          srcYSize,
+                                                          numStretchyYPixelsRemaining,
+                                                          numFixedYPixelsRemaining);
+                numStretchyYPixelsRemaining -= srcYSize;
+            } else {
+                dst.fBottom = dst.fTop + SkIntToScalar(srcYSize);
+                numFixedYPixelsRemaining -= srcYSize;
+            }
+        }
+
+        xIsStretchable = initialXIsStretchable;
+        // The initial xDiv and whether the first column is considered
+        // stretchable or not depends on whether xDiv[0] was zero or not.
+        const uint32_t* colors = chunk.getColors();
+        for (i = xIsStretchable ? 1 : 0;
+              i <= numXDivs && src.fLeft < bitmapWidth;
+              i++, xIsStretchable = !xIsStretchable) {
+            color = colors[colorIndex++];
+            if (i == numXDivs) {
+                src.fRight = bitmapWidth;
+                dst.fRight = bounds.fRight;
+            } else {
+                src.fRight = xDivs[i];
+                if (dstRightsHaveBeenCached) {
+                    dst.fRight = dstRights[i];
+                } else {
+                    const int srcXSize = src.fRight - src.fLeft;
+                    if (xIsStretchable) {
+                        dst.fRight = dst.fLeft + calculateStretch(bounds.fRight, dst.fLeft,
+                                                                  srcXSize,
+                                                                  numStretchyXPixelsRemaining,
+                                                                  numFixedXPixelsRemaining);
+                        numStretchyXPixelsRemaining -= srcXSize;
+                    } else {
+                        dst.fRight = dst.fLeft + SkIntToScalar(srcXSize);
+                        numFixedXPixelsRemaining -= srcXSize;
+                    }
+                    dstRights[i] = dst.fRight;
+                }
+            }
+            // If this horizontal patch is too small to be displayed, leave
+            // the destination left edge where it is and go on to the next patch
+            // in the source.
+            if (src.fLeft >= src.fRight) {
+                src.fLeft = src.fRight;
+                continue;
+            }
+            // Make sure that we actually have room to draw any bits
+            if (dst.fRight <= dst.fLeft || dst.fBottom <= dst.fTop) {
+                goto nextDiv;
+            }
+            // If this patch is transparent, skip and don't draw.
+            if (color == android::Res_png_9patch::TRANSPARENT_COLOR && !hasXfer) {
+                if (outRegion) {
+                    if (*outRegion == NULL) {
+                        *outRegion = new SkRegion();
+                    }
+                    SkIRect idst;
+                    dst.round(&idst);
+                    //ALOGI("Adding trans rect: (%d,%d)-(%d,%d)\n",
+                    //     idst.fLeft, idst.fTop, idst.fRight, idst.fBottom);
+                    (*outRegion)->op(idst, SkRegion::kUnion_Op);
+                }
+                goto nextDiv;
+            }
+            if (canvas) {
+                if (kUseTrace) {
+                    ALOGV("-- src [%d %d %d %d] dst [%g %g %g %g]\n",
+                            src.fLeft, src.fTop, src.width(), src.height(),
+                            SkScalarToFloat(dst.fLeft), SkScalarToFloat(dst.fTop),
+                            SkScalarToFloat(dst.width()), SkScalarToFloat(dst.height()));
+                    if (2 == src.width() && SkIntToScalar(5) == dst.width()) {
+                        ALOGV("--- skip patch\n");
+                    }
+                }
+                drawStretchyPatch(canvas, src, dst, bitmap, *paint, initColor,
+                                  color, hasXfer);
+            }
+
+nextDiv:
+            src.fLeft = src.fRight;
+            dst.fLeft = dst.fRight;
+        }
+        src.fTop = src.fBottom;
+        dst.fTop = dst.fBottom;
+        dstRightsHaveBeenCached = true;
+    }
+}
+
+} // namespace android