From 7de73858975fc4dbccfa1c188f6d1a12d39ce2c5 Mon Sep 17 00:00:00 2001 From: Matt Sarett Date: Tue, 25 Oct 2016 18:36:39 -0400 Subject: [PATCH] Implement getTransparentRegion() using SkLatticeIter This allows us to delete utils/NinePatchImpl.cpp and utils/NinePatch.h Test: Passed cts tests - DrawableTest, NinePatchTest, NinePatchDrawableTest. Change-Id: I6b5d09fa3479e758d8b931fa0e977c25f4435a7c --- core/jni/android/graphics/NinePatch.cpp | 36 ++- libs/hwui/Android.mk | 1 - libs/hwui/NinePatchUtils.h | 38 +++ libs/hwui/RecordingCanvas.h | 1 - libs/hwui/SkiaCanvas.cpp | 20 +- libs/hwui/hwui/Canvas.h | 2 +- libs/hwui/utils/NinePatch.h | 37 --- libs/hwui/utils/NinePatchImpl.cpp | 316 ------------------------ 8 files changed, 70 insertions(+), 381 deletions(-) create mode 100644 libs/hwui/NinePatchUtils.h delete mode 100644 libs/hwui/utils/NinePatch.h delete mode 100644 libs/hwui/utils/NinePatchImpl.cpp diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index 4f2f3897469ce..91e5cafce06e5 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -26,10 +26,10 @@ #include #include "SkCanvas.h" +#include "SkLatticeIter.h" #include "SkRegion.h" #include "GraphicsJNI.h" - -#include "utils/NinePatch.h" +#include "NinePatchUtils.h" #include "JNIHelp.h" #include "core_jni_helpers.h" @@ -88,18 +88,40 @@ public: } static jlong getTransparentRegion(JNIEnv* env, jobject, jobject jbitmap, - jlong chunkHandle, jobject boundsRect) { + jlong chunkHandle, jobject dstRect) { Res_png_9patch* chunk = reinterpret_cast(chunkHandle); SkASSERT(chunk); SkASSERT(boundsRect); SkBitmap bitmap; GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); - SkRect bounds; - GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds); + SkRect dst; + GraphicsJNI::jrect_to_rect(env, dstRect, &dst); - SkRegion* region = NULL; - NinePatch::Draw(NULL, bounds, bitmap, *chunk, NULL, ®ion); + SkCanvas::Lattice lattice; + SkIRect src = SkIRect::MakeWH(bitmap.width(), bitmap.height()); + lattice.fBounds = &src; + NinePatchUtils::SetLatticeDivs(&lattice, *chunk, bitmap.width(), bitmap.height()); + lattice.fFlags = nullptr; + + SkRegion* region = nullptr; + if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), lattice)) { + SkLatticeIter iter(lattice, dst); + if (iter.numRectsToDraw() == chunk->numColors) { + SkRect dummy; + SkRect iterDst; + int index = 0; + while (iter.next(&dummy, &iterDst)) { + if (0 == chunk->getColors()[index++] && !iterDst.isEmpty()) { + if (!region) { + region = new SkRegion(); + } + + region->op(iterDst.round(), SkRegion::kUnion_Op); + } + } + } + } return reinterpret_cast(region); } diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 0aa78081b0167..719b11584dfb2 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -38,7 +38,6 @@ hwui_src_files := \ utils/Blur.cpp \ utils/GLUtils.cpp \ utils/LinearAllocator.cpp \ - utils/NinePatchImpl.cpp \ utils/StringUtils.cpp \ utils/TestWindowContext.cpp \ utils/VectorDrawableUtils.cpp \ diff --git a/libs/hwui/NinePatchUtils.h b/libs/hwui/NinePatchUtils.h new file mode 100644 index 0000000000000..7a271b7ce03af --- /dev/null +++ b/libs/hwui/NinePatchUtils.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 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. + */ + +namespace android { +namespace NinePatchUtils { + +static inline void SetLatticeDivs(SkCanvas::Lattice* lattice, const Res_png_9patch& chunk, + int width, int height) { + lattice->fXCount = chunk.numXDivs; + lattice->fYCount = chunk.numYDivs; + lattice->fXDivs = chunk.getXDivs(); + lattice->fYDivs = chunk.getYDivs(); + + // We'll often see ninepatches where the last div is equal to the width or height. + // This doesn't provide any additional information and is not supported by Skia. + if (lattice->fXCount > 0 && width == lattice->fXDivs[lattice->fXCount - 1]) { + lattice->fXCount--; + } + if (lattice->fYCount > 0 && height == lattice->fYDivs[lattice->fYCount - 1]) { + lattice->fYCount--; + } +} + +}; // namespace NinePatchUtils +}; // namespace android diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index a8fcfeba8e042..b6031c4e023d1 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -26,7 +26,6 @@ #include "hwui/Canvas.h" #include "utils/LinearAllocator.h" #include "utils/Macros.h" -#include "utils/NinePatch.h" #include #include diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 11aaebd3e8b4d..c48b4dcc83e5d 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -17,6 +17,7 @@ #include "SkiaCanvas.h" #include "CanvasProperty.h" +#include "NinePatchUtils.h" #include "VectorDrawable.h" #include "hwui/Bitmap.h" #include "hwui/MinikinUtils.h" @@ -670,23 +671,6 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeigh indexCount, tmpPaint); } -static inline void set_lattice_divs(SkCanvas::Lattice* lattice, const Res_png_9patch& chunk, - int width, int height) { - lattice->fXCount = chunk.numXDivs; - lattice->fYCount = chunk.numYDivs; - lattice->fXDivs = chunk.getXDivs(); - lattice->fYDivs = chunk.getYDivs(); - - // We'll often see ninepatches where the last div is equal to the width or height. - // This doesn't provide any additional information and is not supported by Skia. - if (lattice->fXCount > 0 && width == lattice->fXDivs[lattice->fXCount - 1]) { - lattice->fXCount--; - } - if (lattice->fYCount > 0 && height == lattice->fYDivs[lattice->fYCount - 1]) { - lattice->fYCount--; - } -} - static inline int num_distinct_rects(const SkCanvas::Lattice& lattice) { int xRects; if (lattice.fXCount > 0) { @@ -750,7 +734,7 @@ void SkiaCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk, hwuiBitmap.getSkBitmap(&bitmap); SkCanvas::Lattice lattice; - set_lattice_divs(&lattice, chunk, bitmap.width(), bitmap.height()); + NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height()); lattice.fFlags = nullptr; int numFlags = 0; diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index f275ce1e92101..d7839b429648c 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -21,7 +21,7 @@ #include "GlFunctorLifecycleListener.h" #include "utils/Macros.h" -#include "utils/NinePatch.h" +#include #include #include diff --git a/libs/hwui/utils/NinePatch.h b/libs/hwui/utils/NinePatch.h deleted file mode 100644 index 323e56312fb77..0000000000000 --- a/libs/hwui/utils/NinePatch.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -** -** 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 -#include - -#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 deleted file mode 100644 index cef214be6a859..0000000000000 --- a/libs/hwui/utils/NinePatchImpl.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/* -** -** 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 "SkPaint.h" -#include "SkUnPreMultiply.h" - -#include - -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, SkRect::Make(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 (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->isSrcOver() && 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->isSrcOver(); - 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