Pass the radius as a float deeper into HWUI allowing RS to generate more accurate blurs.
Also, when converting radius to an integer value snap to the appropriate integer boundaries.
bug: 10650594
Change-Id: Icca4bc17d88162bbcbc6035d4f81bd1d98a4de2d
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 647c281..5244635 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -592,7 +592,7 @@
}
FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text,
- uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
+ uint32_t startIndex, uint32_t len, int numGlyphs, float radius, const float* positions) {
checkInit();
DropShadow image;
@@ -613,8 +613,9 @@
Rect bounds;
mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
- uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
- uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
+ uint32_t intRadius = Blur::convertRadiusToInt(radius);
+ uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius;
+ uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius;
uint32_t maxSize = Caches::getInstance().maxTextureSize;
if (paddedWidth > maxSize || paddedHeight > maxSize) {
@@ -635,8 +636,8 @@
memset(dataBuffer, 0, size);
- int penX = radius - bounds.left;
- int penY = radius - bounds.bottom;
+ int penX = intRadius - bounds.left;
+ int penY = intRadius - bounds.bottom;
if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
// text has non-whitespace, so draw and blur to create the shadow
@@ -727,9 +728,10 @@
}
}
-void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
+void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
+ uint32_t intRadius = Blur::convertRadiusToInt(radius);
#ifdef ANDROID_ENABLE_RENDERSCRIPT
- if (width * height * radius >= RS_MIN_INPUT_CUTOFF) {
+ if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) {
uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
if (mRs == 0) {
@@ -768,12 +770,12 @@
}
#endif
- float *gaussian = new float[2 * radius + 1];
- Blur::generateGaussianWeights(gaussian, radius);
+ float *gaussian = new float[2 * intRadius + 1];
+ Blur::generateGaussianWeights(gaussian, intRadius);
uint8_t* scratch = new uint8_t[width * height];
- Blur::horizontal(gaussian, radius, *image, scratch, width, height);
- Blur::vertical(gaussian, radius, scratch, *image, width, height);
+ Blur::horizontal(gaussian, intRadius, *image, scratch, width, height);
+ Blur::vertical(gaussian, intRadius, scratch, *image, width, height);
delete[] gaussian;
delete[] scratch;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 9259028..8ce22b0 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -129,7 +129,7 @@
// After renderDropShadow returns, the called owns the memory in DropShadow.image
// and is responsible for releasing it when it's done with it
DropShadow renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex,
- uint32_t len, int numGlyphs, uint32_t radius, const float* positions);
+ uint32_t len, int numGlyphs, float radius, const float* positions);
void setTextureFiltering(bool linearFiltering) {
mLinearFiltering = linearFiltering;
@@ -218,7 +218,7 @@
int32_t width, int32_t height);
// the input image handle may have its pointer replaced (to avoid copies)
- void blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius);
+ void blurImage(uint8_t** image, int32_t width, int32_t height, float radius);
};
}; // namespace uirenderer
diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp
index c020b40..877a422 100644
--- a/libs/hwui/utils/Blur.cpp
+++ b/libs/hwui/utils/Blur.cpp
@@ -19,6 +19,7 @@
#include <math.h>
#include "Blur.h"
+#include "MathUtils.h"
namespace android {
namespace uirenderer {
@@ -35,6 +36,17 @@
return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f;
}
+// if the original radius was on an integer boundary and the resulting radius
+// is within the conversion error tolerance then we attempt to snap to the
+// original integer boundary.
+uint32_t Blur::convertRadiusToInt(float radius) {
+ const float radiusCeil = ceilf(radius);
+ if (MathUtils::areEqual(radiusCeil, radius)) {
+ return radiusCeil;
+ }
+ return radius;
+}
+
/**
* HWUI has used a slightly different equation than Skia to generate the value
* for sigma and to preserve compatibility we have kept that logic.
diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h
index 79aff65..b145333 100644
--- a/libs/hwui/utils/Blur.h
+++ b/libs/hwui/utils/Blur.h
@@ -27,8 +27,12 @@
public:
// If radius > 0, return the corresponding sigma, else return 0
ANDROID_API static float convertRadiusToSigma(float radius);
- // If sigma > 0.6, return the corresponding radius, else return 0
+ // If sigma > 0.5, return the corresponding radius, else return 0
ANDROID_API static float convertSigmaToRadius(float sigma);
+ // If the original radius was on an integer boundary then after the sigma to
+ // radius conversion a small rounding error may be introduced. This function
+ // accounts for that error and snaps to the appropriate integer boundary.
+ static uint32_t convertRadiusToInt(float radius);
static void generateGaussianWeights(float* weights, int32_t radius);
static void horizontal(float* weights, int32_t radius, const uint8_t* source,