NinePatchImpl.cpp revision 8b5f80ef9f4c98bf18f97e28c7747acb896a365e
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project**
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** Copyright 2006, The Android Open Source Project
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project**
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** you may not use this file except in compliance with the License.
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** You may obtain a copy of the License at
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project**
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project**     http://www.apache.org/licenses/LICENSE-2.0
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project**
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** Unless required by applicable law or agreed to in writing, software
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** distributed under the License is distributed on an "AS IS" BASIS,
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** See the License for the specific language governing permissions and
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** limitations under the License.
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project*/
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#define LOG_TAG "NinePatch"
198cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn#define LOG_NDEBUG 1
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
21b13b9bdad2baf6ad1ec2e56b6b7598fa20f55fc4Mathias Agopian#include <androidfw/ResourceTypes.h>
228cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn#include <utils/Log.h>
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "SkBitmap.h"
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "SkCanvas.h"
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "SkNinePatch.h"
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "SkPaint.h"
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "SkUnPreMultiply.h"
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
308cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn#define USE_TRACE
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#ifdef USE_TRACE
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static bool gTrace;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#endif
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "SkColorPriv.h"
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include <utils/Log.h>
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectstatic bool getColor(const SkBitmap& bitmap, int x, int y, SkColor* c) {
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    switch (bitmap.getConfig()) {
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case SkBitmap::kARGB_8888_Config:
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            *c = SkUnPreMultiply::PMColorToColor(*bitmap.getAddr32(x, y));
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case SkBitmap::kRGB_565_Config:
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            *c = SkPixel16ToPixel32(*bitmap.getAddr16(x, y));
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case SkBitmap::kARGB_4444_Config:
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            *c = SkUnPreMultiply::PMColorToColor(
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                SkPixel4444ToPixel32(*bitmap.getAddr16(x, y)));
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        case SkBitmap::kIndex8_Config: {
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            SkColorTable* ctable = bitmap.getColorTable();
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            *c = SkUnPreMultiply::PMColorToColor(
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                            (*ctable)[*bitmap.getAddr8(x, y)]);
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        default:
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    return true;
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectstatic SkColor modAlpha(SkColor c, int alpha) {
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int scale = alpha + (alpha >> 7);
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int a = SkColorGetA(c) * scale >> 8;
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    return SkColorSetA(c, a);
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectstatic void drawStretchyPatch(SkCanvas* canvas, SkIRect& src, const SkRect& dst,
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              const SkBitmap& bitmap, const SkPaint& paint,
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              SkColor initColor, uint32_t colorHint,
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              bool hasXfer) {
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    if (colorHint !=  android::Res_png_9patch::NO_COLOR) {
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ((SkPaint*)&paint)->setColor(modAlpha(colorHint, paint.getAlpha()));
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        canvas->drawRect(dst, paint);
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ((SkPaint*)&paint)->setColor(initColor);
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    } else if (src.width() == 1 && src.height() == 1) {
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SkColor c;
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!getColor(bitmap, src.fLeft, src.fTop, &c)) {
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            goto SLOW_CASE;
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (0 != c || hasXfer) {
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            SkColor prev = paint.getColor();
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ((SkPaint*)&paint)->setColor(c);
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            canvas->drawRect(dst, paint);
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ((SkPaint*)&paint)->setColor(prev);
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    } else {
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SLOW_CASE:
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        canvas->drawBitmapRect(bitmap, &src, dst, &paint);
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source ProjectSkScalar calculateStretch(SkScalar boundsLimit, SkScalar startingPoint,
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                          int srcSpace, int numStrechyPixelsRemaining,
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                          int numFixedPixelsRemaining) {
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SkScalar spaceRemaining = boundsLimit - startingPoint;
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SkScalar stretchySpaceRemaining =
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                spaceRemaining - SkIntToScalar(numFixedPixelsRemaining);
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    return SkScalarMulDiv(srcSpace, stretchySpaceRemaining,
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                          numStrechyPixelsRemaining);
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectvoid NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds,
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                       const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                       const SkPaint* paint, SkRegion** outRegion) {
108ca79cf69d09efa0c327e9b1237d86a119aea5da7Derek Sollenberger    if (canvas && canvas->quickReject(bounds)) {
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return;
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
111211db4a2874f1a2d0e7a8cb8d33e81fa08801763Mike Reed
112211db4a2874f1a2d0e7a8cb8d33e81fa08801763Mike Reed    SkPaint defaultPaint;
113211db4a2874f1a2d0e7a8cb8d33e81fa08801763Mike Reed    if (NULL == paint) {
114211db4a2874f1a2d0e7a8cb8d33e81fa08801763Mike Reed        // matches default dither in NinePatchDrawable.java.
115211db4a2874f1a2d0e7a8cb8d33e81fa08801763Mike Reed        defaultPaint.setDither(true);
116211db4a2874f1a2d0e7a8cb8d33e81fa08801763Mike Reed        paint = &defaultPaint;
117211db4a2874f1a2d0e7a8cb8d33e81fa08801763Mike Reed    }
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
119d39d1affe82cb8c21d32baaa5fbb2d6afb806f8eDerek Sollenberger    // if our SkCanvas were back by GL we should enable this and draw this as
120d39d1affe82cb8c21d32baaa5fbb2d6afb806f8eDerek Sollenberger    // a mesh, which will be faster in most cases.
121d39d1affe82cb8c21d32baaa5fbb2d6afb806f8eDerek Sollenberger    if (false) {
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SkNinePatch::DrawMesh(canvas, bounds, bitmap,
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              chunk.xDivs, chunk.numXDivs,
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              chunk.yDivs, chunk.numYDivs,
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              paint);
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return;
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#ifdef USE_TRACE
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    gTrace = true;
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#endif
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SkASSERT(canvas || outRegion);
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1358cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn#ifdef USE_TRACE
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    if (canvas) {
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        const SkMatrix& m = canvas->getTotalMatrix();
13871f2cf116aab893e224056c38ab146bd1538dd3eSteve Block        ALOGV("ninepatch [%g %g %g] [%g %g %g]\n",
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]),
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                 SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5]));
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#endif
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#ifdef USE_TRACE
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    if (gTrace) {
14671f2cf116aab893e224056c38ab146bd1538dd3eSteve Block        ALOGV("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()));
14771f2cf116aab893e224056c38ab146bd1538dd3eSteve Block        ALOGV("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height());
14871f2cf116aab893e224056c38ab146bd1538dd3eSteve Block        ALOGV("======== ninepatch xDivs [%d,%d]\n", chunk.xDivs[0], chunk.xDivs[1]);
14971f2cf116aab893e224056c38ab146bd1538dd3eSteve Block        ALOGV("======== ninepatch yDivs [%d,%d]\n", chunk.yDivs[0], chunk.yDivs[1]);
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#endif
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    if (bounds.isEmpty() ||
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        bitmap.width() == 0 || bitmap.height() == 0 ||
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        (paint && paint->getXfermode() == NULL && paint->getAlpha() == 0))
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#ifdef USE_TRACE
15871f2cf116aab893e224056c38ab146bd1538dd3eSteve Block        if (gTrace) ALOGV("======== abort ninepatch draw\n");
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#endif
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return;
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // should try a quick-reject test before calling lockPixels
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SkAutoLockPixels alp(bitmap);
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // after the lock, it is valid to check getPixels()
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    if (bitmap.getPixels() == NULL)
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return;
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const bool hasXfer = paint->getXfermode() != NULL;
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SkRect      dst;
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SkIRect     src;
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const int32_t x0 = chunk.xDivs[0];
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const int32_t y0 = chunk.yDivs[0];
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const SkColor initColor = ((SkPaint*)paint)->getColor();
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const uint8_t numXDivs = chunk.numXDivs;
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const uint8_t numYDivs = chunk.numYDivs;
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int i;
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int j;
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int colorIndex = 0;
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    uint32_t color;
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    bool xIsStretchable;
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const bool initialXIsStretchable =  (x0 == 0);
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    bool yIsStretchable = (y0 == 0);
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const int bitmapWidth = bitmap.width();
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const int bitmapHeight = bitmap.height();
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SkScalar* dstRights = (SkScalar*) alloca((numXDivs + 1) * sizeof(SkScalar));
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    bool dstRightsHaveBeenCached = false;
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int numStretchyXPixelsRemaining = 0;
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    for (i = 0; i < numXDivs; i += 2) {
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        numStretchyXPixelsRemaining += chunk.xDivs[i + 1] - chunk.xDivs[i];
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining;
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int numStretchyYPixelsRemaining = 0;
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    for (i = 0; i < numYDivs; i += 2) {
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        numStretchyYPixelsRemaining += chunk.yDivs[i + 1] - chunk.yDivs[i];
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining;
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2038cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn#ifdef USE_TRACE
20471f2cf116aab893e224056c38ab146bd1538dd3eSteve Block    ALOGV("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n",
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             bitmap.width(), bitmap.height(),
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()),
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project             numXDivs, numYDivs);
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#endif
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    src.fTop = 0;
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    dst.fTop = bounds.fTop;
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The first row always starts with the top being at y=0 and the bottom
2148b5f80ef9f4c98bf18f97e28c7747acb896a365eRoger Hu    // being either yDivs[1] (if yDivs[0]=0) or yDivs[0].  In the former case
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // the first row is stretchable along the Y axis, otherwise it is fixed.
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The last row always ends with the bottom being bitmap.height and the top
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // yDivs[numYDivs-1]. In the former case the last row is stretchable along
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // the Y axis, otherwise it is fixed.
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    //
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The first and last columns are similarly treated with respect to the X
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // axis.
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    //
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The above is to help explain some of the special casing that goes on the
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // code below.
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The initial yDiv and whether the first row is considered stretchable or
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // not depends on whether yDiv[0] was zero or not.
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    for (j = yIsStretchable ? 1 : 0;
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project          j <= numYDivs && src.fTop < bitmapHeight;
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project          j++, yIsStretchable = !yIsStretchable) {
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        src.fLeft = 0;
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dst.fLeft = bounds.fLeft;
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (j == numYDivs) {
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            src.fBottom = bitmapHeight;
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dst.fBottom = bounds.fBottom;
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            src.fBottom = chunk.yDivs[j];
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            const int srcYSize = src.fBottom - src.fTop;
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (yIsStretchable) {
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dst.fBottom = dst.fTop + calculateStretch(bounds.fBottom, dst.fTop,
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                          srcYSize,
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                          numStretchyYPixelsRemaining,
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                          numFixedYPixelsRemaining);
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                numStretchyYPixelsRemaining -= srcYSize;
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dst.fBottom = dst.fTop + SkIntToScalar(srcYSize);
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                numFixedYPixelsRemaining -= srcYSize;
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        xIsStretchable = initialXIsStretchable;
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // The initial xDiv and whether the first column is considered
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // stretchable or not depends on whether xDiv[0] was zero or not.
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (i = xIsStretchable ? 1 : 0;
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project              i <= numXDivs && src.fLeft < bitmapWidth;
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project              i++, xIsStretchable = !xIsStretchable) {
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            color = chunk.colors[colorIndex++];
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (i == numXDivs) {
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                src.fRight = bitmapWidth;
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dst.fRight = bounds.fRight;
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                src.fRight = chunk.xDivs[i];
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (dstRightsHaveBeenCached) {
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    dst.fRight = dstRights[i];
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    const int srcXSize = src.fRight - src.fLeft;
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (xIsStretchable) {
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        dst.fRight = dst.fLeft + calculateStretch(bounds.fRight, dst.fLeft,
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                                  srcXSize,
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                                  numStretchyXPixelsRemaining,
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                                  numFixedXPixelsRemaining);
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        numStretchyXPixelsRemaining -= srcXSize;
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        dst.fRight = dst.fLeft + SkIntToScalar(srcXSize);
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        numFixedXPixelsRemaining -= srcXSize;
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    dstRights[i] = dst.fRight;
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // If this horizontal patch is too small to be displayed, leave
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // the destination left edge where it is and go on to the next patch
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // in the source.
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (src.fLeft >= src.fRight) {
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                src.fLeft = src.fRight;
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                continue;
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Make sure that we actually have room to draw any bits
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dst.fRight <= dst.fLeft || dst.fBottom <= dst.fTop) {
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                goto nextDiv;
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // If this patch is transparent, skip and don't draw.
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (color == android::Res_png_9patch::TRANSPARENT_COLOR && !hasXfer) {
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (outRegion) {
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (*outRegion == NULL) {
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        *outRegion = new SkRegion();
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    SkIRect idst;
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    dst.round(&idst);
3006215d3ff4b5dfa52a5d8b9a42e343051f31066a5Steve Block                    //ALOGI("Adding trans rect: (%d,%d)-(%d,%d)\n",
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    //     idst.fLeft, idst.fTop, idst.fRight, idst.fBottom);
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    (*outRegion)->op(idst, SkRegion::kUnion_Op);
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                goto nextDiv;
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (canvas) {
3078cae124af2142687a6833dbaab8a43df6dd67b43Dianne Hackborn#ifdef USE_TRACE
30871f2cf116aab893e224056c38ab146bd1538dd3eSteve Block                ALOGV("-- src [%d %d %d %d] dst [%g %g %g %g]\n",
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         src.fLeft, src.fTop, src.width(), src.height(),
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         SkScalarToFloat(dst.fLeft), SkScalarToFloat(dst.fTop),
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         SkScalarToFloat(dst.width()), SkScalarToFloat(dst.height()));
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (2 == src.width() && SkIntToScalar(5) == dst.width()) {
31371f2cf116aab893e224056c38ab146bd1538dd3eSteve Block                    ALOGV("--- skip patch\n");
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#endif
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                drawStretchyPatch(canvas, src, dst, bitmap, *paint, initColor,
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                  color, hasXfer);
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source ProjectnextDiv:
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            src.fLeft = src.fRight;
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dst.fLeft = dst.fRight;
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        src.fTop = src.fBottom;
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dst.fTop = dst.fBottom;
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dstRightsHaveBeenCached = true;
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
329