NinePatchImpl.cpp revision d8f904f256b82e48e9a85561eb96e15399b0b2d9
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
184c5efe9290543b723b76a8bd48518da1ae1dcb26Derek Sollenberger#include "utils/NinePatch.h"
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "SkBitmap.h"
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "SkCanvas.h"
22ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe#include "SkColorPriv.h"
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "SkNinePatch.h"
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "SkPaint.h"
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "SkUnPreMultiply.h"
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include <utils/Log.h>
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
294c5efe9290543b723b76a8bd48518da1ae1dcb26Derek Sollenbergernamespace android {
304c5efe9290543b723b76a8bd48518da1ae1dcb26Derek Sollenberger
31ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampestatic const bool kUseTrace = true;
32ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampestatic bool gTrace = false;
33ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectstatic bool getColor(const SkBitmap& bitmap, int x, int y, SkColor* c) {
351103b3255945d2eb2fa9c191e84e2270b343cca9Mike Reed    switch (bitmap.colorType()) {
361103b3255945d2eb2fa9c191e84e2270b343cca9Mike Reed        case kN32_SkColorType:
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            *c = SkUnPreMultiply::PMColorToColor(*bitmap.getAddr32(x, y));
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
391103b3255945d2eb2fa9c191e84e2270b343cca9Mike Reed        case kRGB_565_SkColorType:
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            *c = SkPixel16ToPixel32(*bitmap.getAddr16(x, y));
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
421103b3255945d2eb2fa9c191e84e2270b343cca9Mike Reed        case kARGB_4444_SkColorType:
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            *c = SkUnPreMultiply::PMColorToColor(
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                SkPixel4444ToPixel32(*bitmap.getAddr16(x, y)));
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
461103b3255945d2eb2fa9c191e84e2270b343cca9Mike Reed        case kIndex_8_SkColorType: {
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            SkColorTable* ctable = bitmap.getColorTable();
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            *c = SkUnPreMultiply::PMColorToColor(
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                            (*ctable)[*bitmap.getAddr8(x, y)]);
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            break;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        default:
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    return true;
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectstatic SkColor modAlpha(SkColor c, int alpha) {
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int scale = alpha + (alpha >> 7);
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int a = SkColorGetA(c) * scale >> 8;
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    return SkColorSetA(c, a);
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
64d8f904f256b82e48e9a85561eb96e15399b0b2d9Tom Hudsonstatic void drawStretchyPatch(SkCanvas* canvas, SkIRect& src, const SkRect& dst,
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              const SkBitmap& bitmap, const SkPaint& paint,
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              SkColor initColor, uint32_t colorHint,
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              bool hasXfer) {
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    if (colorHint !=  android::Res_png_9patch::NO_COLOR) {
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ((SkPaint*)&paint)->setColor(modAlpha(colorHint, paint.getAlpha()));
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        canvas->drawRect(dst, paint);
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ((SkPaint*)&paint)->setColor(initColor);
72d8f904f256b82e48e9a85561eb96e15399b0b2d9Tom Hudson    } else if (src.width() == 1 && src.height() == 1) {
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SkColor c;
74d8f904f256b82e48e9a85561eb96e15399b0b2d9Tom Hudson        if (!getColor(bitmap, src.fLeft, src.fTop, &c)) {
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            goto SLOW_CASE;
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (0 != c || hasXfer) {
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            SkColor prev = paint.getColor();
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ((SkPaint*)&paint)->setColor(c);
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            canvas->drawRect(dst, paint);
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ((SkPaint*)&paint)->setColor(prev);
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    } else {
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SLOW_CASE:
85d8f904f256b82e48e9a85561eb96e15399b0b2d9Tom Hudson        canvas->drawBitmapRect(bitmap, &src, dst, &paint);
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source ProjectSkScalar calculateStretch(SkScalar boundsLimit, SkScalar startingPoint,
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                          int srcSpace, int numStrechyPixelsRemaining,
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                          int numFixedPixelsRemaining) {
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SkScalar spaceRemaining = boundsLimit - startingPoint;
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SkScalar stretchySpaceRemaining =
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                spaceRemaining - SkIntToScalar(numFixedPixelsRemaining);
9547b3441537b182a61d3a41d39095c40761d49457Mike Reed    return srcSpace * stretchySpaceRemaining / numStrechyPixelsRemaining;
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
984c5efe9290543b723b76a8bd48518da1ae1dcb26Derek Sollenbergervoid NinePatch::Draw(SkCanvas* canvas, const SkRect& bounds,
994c5efe9290543b723b76a8bd48518da1ae1dcb26Derek Sollenberger                     const SkBitmap& bitmap, const Res_png_9patch& chunk,
1004c5efe9290543b723b76a8bd48518da1ae1dcb26Derek Sollenberger                     const SkPaint* paint, SkRegion** outRegion) {
101ca79cf69d09efa0c327e9b1237d86a119aea5da7Derek Sollenberger    if (canvas && canvas->quickReject(bounds)) {
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return;
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
104211db4a2874f1a2d0e7a8cb8d33e81fa08801763Mike Reed
105211db4a2874f1a2d0e7a8cb8d33e81fa08801763Mike Reed    SkPaint defaultPaint;
106211db4a2874f1a2d0e7a8cb8d33e81fa08801763Mike Reed    if (NULL == paint) {
107211db4a2874f1a2d0e7a8cb8d33e81fa08801763Mike Reed        // matches default dither in NinePatchDrawable.java.
108211db4a2874f1a2d0e7a8cb8d33e81fa08801763Mike Reed        defaultPaint.setDither(true);
109211db4a2874f1a2d0e7a8cb8d33e81fa08801763Mike Reed        paint = &defaultPaint;
110211db4a2874f1a2d0e7a8cb8d33e81fa08801763Mike Reed    }
1116381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath
1126381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    const int32_t* xDivs = chunk.getXDivs();
1136381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    const int32_t* yDivs = chunk.getYDivs();
114d39d1affe82cb8c21d32baaa5fbb2d6afb806f8eDerek Sollenberger    // if our SkCanvas were back by GL we should enable this and draw this as
115d39d1affe82cb8c21d32baaa5fbb2d6afb806f8eDerek Sollenberger    // a mesh, which will be faster in most cases.
11646d8444631b4b1253a76bfcc78a29d26014d022fDan Albert    if ((false)) {
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SkNinePatch::DrawMesh(canvas, bounds, bitmap,
1186381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath                              xDivs, chunk.numXDivs,
1196381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath                              yDivs, chunk.numYDivs,
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              paint);
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return;
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
124ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe    if (kUseTrace) {
125ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe        gTrace = true;
126ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe    }
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SkASSERT(canvas || outRegion);
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
130ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe    if (kUseTrace) {
131ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe        if (canvas) {
132ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe            const SkMatrix& m = canvas->getTotalMatrix();
133ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe            ALOGV("ninepatch [%g %g %g] [%g %g %g]\n",
134ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                    SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]),
135ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                    SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5]));
136ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe        }
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
138ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe        ALOGV("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()),
139ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                SkScalarToFloat(bounds.height()));
14071f2cf116aab893e224056c38ab146bd1538dd3eSteve Block        ALOGV("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height());
1416381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        ALOGV("======== ninepatch xDivs [%d,%d]\n", xDivs[0], xDivs[1]);
1426381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        ALOGV("======== ninepatch yDivs [%d,%d]\n", yDivs[0], yDivs[1]);
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    if (bounds.isEmpty() ||
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        bitmap.width() == 0 || bitmap.height() == 0 ||
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        (paint && paint->getXfermode() == NULL && paint->getAlpha() == 0))
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
149ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe        if (kUseTrace) {
150ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe            ALOGV("======== abort ninepatch draw\n");
151ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe        }
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // should try a quick-reject test before calling lockPixels
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SkAutoLockPixels alp(bitmap);
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // after the lock, it is valid to check getPixels()
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    if (bitmap.getPixels() == NULL)
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return;
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const bool hasXfer = paint->getXfermode() != NULL;
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SkRect      dst;
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    SkIRect     src;
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1666381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    const int32_t x0 = xDivs[0];
1676381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath    const int32_t y0 = yDivs[0];
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const SkColor initColor = ((SkPaint*)paint)->getColor();
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const uint8_t numXDivs = chunk.numXDivs;
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const uint8_t numYDivs = chunk.numYDivs;
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int i;
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int j;
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int colorIndex = 0;
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    uint32_t color;
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    bool xIsStretchable;
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const bool initialXIsStretchable =  (x0 == 0);
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    bool yIsStretchable = (y0 == 0);
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const int bitmapWidth = bitmap.width();
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    const int bitmapHeight = bitmap.height();
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
181462dd010467fdf1ab0cb49e021a92d14f2163c8cLeon Scroggins III    // Number of bytes needed for dstRights array.
182462dd010467fdf1ab0cb49e021a92d14f2163c8cLeon Scroggins III    // Need to cast numXDivs to a larger type to avoid overflow.
183462dd010467fdf1ab0cb49e021a92d14f2163c8cLeon Scroggins III    const size_t dstBytes = ((size_t) numXDivs + 1) * sizeof(SkScalar);
184462dd010467fdf1ab0cb49e021a92d14f2163c8cLeon Scroggins III    SkScalar* dstRights = (SkScalar*) alloca(dstBytes);
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    bool dstRightsHaveBeenCached = false;
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int numStretchyXPixelsRemaining = 0;
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    for (i = 0; i < numXDivs; i += 2) {
1896381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        numStretchyXPixelsRemaining += xDivs[i + 1] - xDivs[i];
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining;
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int numStretchyYPixelsRemaining = 0;
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    for (i = 0; i < numYDivs; i += 2) {
1946381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        numStretchyYPixelsRemaining += yDivs[i + 1] - yDivs[i];
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining;
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
198ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe    if (kUseTrace) {
199ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe        ALOGV("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n",
200ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                bitmap.width(), bitmap.height(),
201ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
202ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()),
203ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                numXDivs, numYDivs);
204ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe    }
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    src.fTop = 0;
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    dst.fTop = bounds.fTop;
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The first row always starts with the top being at y=0 and the bottom
2098b5f80ef9f4c98bf18f97e28c7747acb896a365eRoger Hu    // being either yDivs[1] (if yDivs[0]=0) or yDivs[0].  In the former case
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // the first row is stretchable along the Y axis, otherwise it is fixed.
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The last row always ends with the bottom being bitmap.height and the top
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // yDivs[numYDivs-1]. In the former case the last row is stretchable along
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // the Y axis, otherwise it is fixed.
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    //
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The first and last columns are similarly treated with respect to the X
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // axis.
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    //
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The above is to help explain some of the special casing that goes on the
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // code below.
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The initial yDiv and whether the first row is considered stretchable or
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // not depends on whether yDiv[0] was zero or not.
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    for (j = yIsStretchable ? 1 : 0;
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project          j <= numYDivs && src.fTop < bitmapHeight;
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project          j++, yIsStretchable = !yIsStretchable) {
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        src.fLeft = 0;
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dst.fLeft = bounds.fLeft;
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (j == numYDivs) {
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            src.fBottom = bitmapHeight;
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dst.fBottom = bounds.fBottom;
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
2336381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath            src.fBottom = yDivs[j];
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            const int srcYSize = src.fBottom - src.fTop;
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (yIsStretchable) {
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dst.fBottom = dst.fTop + calculateStretch(bounds.fBottom, dst.fTop,
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                          srcYSize,
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                          numStretchyYPixelsRemaining,
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                          numFixedYPixelsRemaining);
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                numStretchyYPixelsRemaining -= srcYSize;
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dst.fBottom = dst.fTop + SkIntToScalar(srcYSize);
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                numFixedYPixelsRemaining -= srcYSize;
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        xIsStretchable = initialXIsStretchable;
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // The initial xDiv and whether the first column is considered
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // stretchable or not depends on whether xDiv[0] was zero or not.
2506381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath        const uint32_t* colors = chunk.getColors();
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (i = xIsStretchable ? 1 : 0;
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project              i <= numXDivs && src.fLeft < bitmapWidth;
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project              i++, xIsStretchable = !xIsStretchable) {
2546381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath            color = colors[colorIndex++];
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (i == numXDivs) {
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                src.fRight = bitmapWidth;
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dst.fRight = bounds.fRight;
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
2596381dd4ff212a95be30d2b445d40ff419ab076b4Narayan Kamath                src.fRight = xDivs[i];
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (dstRightsHaveBeenCached) {
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    dst.fRight = dstRights[i];
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    const int srcXSize = src.fRight - src.fLeft;
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (xIsStretchable) {
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        dst.fRight = dst.fLeft + calculateStretch(bounds.fRight, dst.fLeft,
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                                  srcXSize,
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                                  numStretchyXPixelsRemaining,
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                                  numFixedXPixelsRemaining);
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        numStretchyXPixelsRemaining -= srcXSize;
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        dst.fRight = dst.fLeft + SkIntToScalar(srcXSize);
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        numFixedXPixelsRemaining -= srcXSize;
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    dstRights[i] = dst.fRight;
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // If this horizontal patch is too small to be displayed, leave
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // the destination left edge where it is and go on to the next patch
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // in the source.
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (src.fLeft >= src.fRight) {
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                src.fLeft = src.fRight;
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                continue;
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Make sure that we actually have room to draw any bits
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (dst.fRight <= dst.fLeft || dst.fBottom <= dst.fTop) {
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                goto nextDiv;
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // If this patch is transparent, skip and don't draw.
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (color == android::Res_png_9patch::TRANSPARENT_COLOR && !hasXfer) {
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (outRegion) {
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (*outRegion == NULL) {
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        *outRegion = new SkRegion();
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    SkIRect idst;
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    dst.round(&idst);
2966215d3ff4b5dfa52a5d8b9a42e343051f31066a5Steve Block                    //ALOGI("Adding trans rect: (%d,%d)-(%d,%d)\n",
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    //     idst.fLeft, idst.fTop, idst.fRight, idst.fBottom);
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    (*outRegion)->op(idst, SkRegion::kUnion_Op);
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                goto nextDiv;
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (canvas) {
303ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                if (kUseTrace) {
304ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                    ALOGV("-- src [%d %d %d %d] dst [%g %g %g %g]\n",
305ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                            src.fLeft, src.fTop, src.width(), src.height(),
306ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                            SkScalarToFloat(dst.fLeft), SkScalarToFloat(dst.fTop),
307ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                            SkScalarToFloat(dst.width()), SkScalarToFloat(dst.height()));
308ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                    if (2 == src.width() && SkIntToScalar(5) == dst.width()) {
309ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                        ALOGV("--- skip patch\n");
310ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe                    }
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                drawStretchyPatch(canvas, src, dst, bitmap, *paint, initColor,
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                  color, hasXfer);
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source ProjectnextDiv:
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            src.fLeft = src.fRight;
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dst.fLeft = dst.fRight;
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        src.fTop = src.fBottom;
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dst.fTop = dst.fBottom;
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dstRightsHaveBeenCached = true;
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
3254c5efe9290543b723b76a8bd48518da1ae1dcb26Derek Sollenberger
3264c5efe9290543b723b76a8bd48518da1ae1dcb26Derek Sollenberger} // namespace android
327