From bd8db2e87e16900ff9b87937d3ccff6a50bd5b2a Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Wed, 20 Aug 2014 16:31:57 -0700 Subject: [PATCH] Add bounds checking to nine patch scaling bug:17114103 Better handle the case, when scaling the divs in a nine patch, where divs fall outside of the bounds of the bitmap. Change-Id: I244b9c45b938c2a15f29e4563a86825ee9439b5f --- core/jni/android/graphics/BitmapFactory.cpp | 47 +++++++++++++-------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 2ce1b15780dad..62ea35194954d 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -87,27 +88,39 @@ static bool optionsJustBounds(JNIEnv* env, jobject options) { return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID); } -static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) { +static void scaleDivRange(int32_t* divs, int count, float scale, int maxValue) { + for (int i = 0; i < count; i++) { + divs[i] = int32_t(divs[i] * scale + 0.5f); + if (i > 0 && divs[i] == divs[i - 1]) { + divs[i]++; // avoid collisions + } + } + + if (CC_UNLIKELY(divs[count - 1] > maxValue)) { + // if the collision avoidance above put some divs outside the bounds of the bitmap, + // slide outer stretchable divs inward to stay within bounds + int highestAvailable = maxValue; + for (int i = count - 1; i >= 0; i--) { + divs[i] = highestAvailable; + if (i > 0 && divs[i] <= divs[i-1]){ + // keep shifting + highestAvailable = divs[i] - 1; + } else { + break; + } + } + } +} + +static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale, + int scaledWidth, int scaledHeight) { chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f); chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f); chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); - int32_t* xDivs = chunk->getXDivs(); - for (int i = 0; i < chunk->numXDivs; i++) { - xDivs[i] = int32_t(xDivs[i] * scale + 0.5f); - if (i > 0 && xDivs[i] == xDivs[i - 1]) { - xDivs[i]++; - } - } - - int32_t* yDivs = chunk->getYDivs(); - for (int i = 0; i < chunk->numYDivs; i++) { - yDivs[i] = int32_t(yDivs[i] * scale + 0.5f); - if (i > 0 && yDivs[i] == yDivs[i - 1]) { - yDivs[i]++; - } - } + scaleDivRange(chunk->getXDivs(), chunk->numXDivs, scale, scaledWidth); + scaleDivRange(chunk->getYDivs(), chunk->numYDivs, scale, scaledHeight); } static SkColorType colorTypeForScaledOutput(SkColorType colorType) { @@ -330,7 +343,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding jbyteArray ninePatchChunk = NULL; if (peeker.mPatch != NULL) { if (willScale) { - scaleNinePatchChunk(peeker.mPatch, scale); + scaleNinePatchChunk(peeker.mPatch, scale, scaledWidth, scaledHeight); } size_t ninePatchArraySize = peeker.mPatch->serializedSize();