Merge "Implement getTransparentRegion() using SkLatticeIter"

This commit is contained in:
TreeHugger Robot
2016-10-27 14:13:15 +00:00
committed by Android (Google) Code Review
8 changed files with 70 additions and 381 deletions

View File

@@ -26,10 +26,10 @@
#include <ResourceCache.h>
#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<Res_png_9patch*>(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, &region);
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<jlong>(region);
}

View File

@@ -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 \

View File

@@ -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

View File

@@ -26,7 +26,6 @@
#include "hwui/Canvas.h"
#include "utils/LinearAllocator.h"
#include "utils/Macros.h"
#include "utils/NinePatch.h"
#include <SkDrawFilter.h>
#include <SkPaint.h>

View File

@@ -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;

View File

@@ -21,7 +21,7 @@
#include "GlFunctorLifecycleListener.h"
#include "utils/Macros.h"
#include "utils/NinePatch.h"
#include <androidfw/ResourceTypes.h>
#include <SkBitmap.h>
#include <SkCanvas.h>

View File

@@ -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 <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

View File

@@ -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 <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, 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