Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bd8db2e8 authored by Chris Craik's avatar Chris Craik
Browse files

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
parent 3acf66f5
Loading
Loading
Loading
Loading
+30 −17
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <androidfw/Asset.h>
#include <androidfw/ResourceTypes.h>
#include <cutils/compiler.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/mman.h>
@@ -87,29 +88,41 @@ 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) {
    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]++;
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
        }
    }

    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]++;
    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);

    scaleDivRange(chunk->getXDivs(), chunk->numXDivs, scale, scaledWidth);
    scaleDivRange(chunk->getYDivs(), chunk->numYDivs, scale, scaledHeight);
}

static SkColorType colorTypeForScaledOutput(SkColorType colorType) {
    switch (colorType) {
        case kUnknown_SkColorType:
@@ -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();