gfx/skia/trunk/src/core/SkRRect.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/core/SkRRect.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,431 @@
     1.4 +/*
     1.5 + * Copyright 2012 Google Inc.
     1.6 + *
     1.7 + * Use of this source code is governed by a BSD-style license that can be
     1.8 + * found in the LICENSE file.
     1.9 + */
    1.10 +
    1.11 +#include "SkRRect.h"
    1.12 +#include "SkMatrix.h"
    1.13 +
    1.14 +///////////////////////////////////////////////////////////////////////////////
    1.15 +
    1.16 +void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
    1.17 +    if (rect.isEmpty()) {
    1.18 +        this->setEmpty();
    1.19 +        return;
    1.20 +    }
    1.21 +
    1.22 +    if (xRad <= 0 || yRad <= 0) {
    1.23 +        // all corners are square in this case
    1.24 +        this->setRect(rect);
    1.25 +        return;
    1.26 +    }
    1.27 +
    1.28 +    if (rect.width() < xRad+xRad || rect.height() < yRad+yRad) {
    1.29 +        SkScalar scale = SkMinScalar(SkScalarDiv(rect.width(), xRad + xRad),
    1.30 +                                     SkScalarDiv(rect.height(), yRad + yRad));
    1.31 +        SkASSERT(scale < SK_Scalar1);
    1.32 +        xRad = SkScalarMul(xRad, scale);
    1.33 +        yRad = SkScalarMul(yRad, scale);
    1.34 +    }
    1.35 +
    1.36 +    fRect = rect;
    1.37 +    for (int i = 0; i < 4; ++i) {
    1.38 +        fRadii[i].set(xRad, yRad);
    1.39 +    }
    1.40 +    fType = kSimple_Type;
    1.41 +    if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) {
    1.42 +        fType = kOval_Type;
    1.43 +        // TODO: assert that all the x&y radii are already W/2 & H/2
    1.44 +    }
    1.45 +
    1.46 +    SkDEBUGCODE(this->validate();)
    1.47 +}
    1.48 +
    1.49 +void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) {
    1.50 +    if (rect.isEmpty()) {
    1.51 +        this->setEmpty();
    1.52 +        return;
    1.53 +    }
    1.54 +
    1.55 +    fRect = rect;
    1.56 +    memcpy(fRadii, radii, sizeof(fRadii));
    1.57 +
    1.58 +    bool allCornersSquare = true;
    1.59 +
    1.60 +    // Clamp negative radii to zero
    1.61 +    for (int i = 0; i < 4; ++i) {
    1.62 +        if (fRadii[i].fX <= 0 || fRadii[i].fY <= 0) {
    1.63 +            // In this case we are being a little fast & loose. Since one of
    1.64 +            // the radii is 0 the corner is square. However, the other radii
    1.65 +            // could still be non-zero and play in the global scale factor
    1.66 +            // computation.
    1.67 +            fRadii[i].fX = 0;
    1.68 +            fRadii[i].fY = 0;
    1.69 +        } else {
    1.70 +            allCornersSquare = false;
    1.71 +        }
    1.72 +    }
    1.73 +
    1.74 +    if (allCornersSquare) {
    1.75 +        this->setRect(rect);
    1.76 +        return;
    1.77 +    }
    1.78 +
    1.79 +    // Proportionally scale down all radii to fit. Find the minimum ratio
    1.80 +    // of a side and the radii on that side (for all four sides) and use
    1.81 +    // that to scale down _all_ the radii. This algorithm is from the
    1.82 +    // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping
    1.83 +    // Curves:
    1.84 +    // "Let f = min(Li/Si), where i is one of { top, right, bottom, left },
    1.85 +    //   Si is the sum of the two corresponding radii of the corners on side i,
    1.86 +    //   and Ltop = Lbottom = the width of the box,
    1.87 +    //   and Lleft = Lright = the height of the box.
    1.88 +    // If f < 1, then all corner radii are reduced by multiplying them by f."
    1.89 +    SkScalar scale = SK_Scalar1;
    1.90 +
    1.91 +    if (fRadii[0].fX + fRadii[1].fX > rect.width()) {
    1.92 +        scale = SkMinScalar(scale,
    1.93 +                            SkScalarDiv(rect.width(), fRadii[0].fX + fRadii[1].fX));
    1.94 +    }
    1.95 +    if (fRadii[1].fY + fRadii[2].fY > rect.height()) {
    1.96 +        scale = SkMinScalar(scale,
    1.97 +                            SkScalarDiv(rect.height(), fRadii[1].fY + fRadii[2].fY));
    1.98 +    }
    1.99 +    if (fRadii[2].fX + fRadii[3].fX > rect.width()) {
   1.100 +        scale = SkMinScalar(scale,
   1.101 +                            SkScalarDiv(rect.width(), fRadii[2].fX + fRadii[3].fX));
   1.102 +    }
   1.103 +    if (fRadii[3].fY + fRadii[0].fY > rect.height()) {
   1.104 +        scale = SkMinScalar(scale,
   1.105 +                            SkScalarDiv(rect.height(), fRadii[3].fY + fRadii[0].fY));
   1.106 +    }
   1.107 +
   1.108 +    if (scale < SK_Scalar1) {
   1.109 +        for (int i = 0; i < 4; ++i) {
   1.110 +            fRadii[i].fX = SkScalarMul(fRadii[i].fX, scale);
   1.111 +            fRadii[i].fY = SkScalarMul(fRadii[i].fY, scale);
   1.112 +        }
   1.113 +    }
   1.114 +
   1.115 +    // At this point we're either oval, simple, or complex (not empty or rect)
   1.116 +    // but we lazily resolve the type to avoid the work if the information
   1.117 +    // isn't required.
   1.118 +    fType = (SkRRect::Type) kUnknown_Type;
   1.119 +
   1.120 +    SkDEBUGCODE(this->validate();)
   1.121 +}
   1.122 +
   1.123 +// This method determines if a point known to be inside the RRect's bounds is
   1.124 +// inside all the corners.
   1.125 +bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const {
   1.126 +    SkPoint canonicalPt; // (x,y) translated to one of the quadrants
   1.127 +    int index;
   1.128 +
   1.129 +    if (kOval_Type == this->type()) {
   1.130 +        canonicalPt.set(x - fRect.centerX(), y - fRect.centerY());
   1.131 +        index = kUpperLeft_Corner;  // any corner will do in this case
   1.132 +    } else {
   1.133 +        if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX &&
   1.134 +            y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) {
   1.135 +            // UL corner
   1.136 +            index = kUpperLeft_Corner;
   1.137 +            canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX),
   1.138 +                            y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY));
   1.139 +            SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0);
   1.140 +        } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX &&
   1.141 +                   y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) {
   1.142 +            // LL corner
   1.143 +            index = kLowerLeft_Corner;
   1.144 +            canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX),
   1.145 +                            y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY));
   1.146 +            SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0);
   1.147 +        } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX &&
   1.148 +                   y < fRect.fTop + fRadii[kUpperRight_Corner].fY) {
   1.149 +            // UR corner
   1.150 +            index = kUpperRight_Corner;
   1.151 +            canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX),
   1.152 +                            y - (fRect.fTop + fRadii[kUpperRight_Corner].fY));
   1.153 +            SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0);
   1.154 +        } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX &&
   1.155 +                   y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) {
   1.156 +            // LR corner
   1.157 +            index = kLowerRight_Corner;
   1.158 +            canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX),
   1.159 +                            y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY));
   1.160 +            SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0);
   1.161 +        } else {
   1.162 +            // not in any of the corners
   1.163 +            return true;
   1.164 +        }
   1.165 +    }
   1.166 +
   1.167 +    // A point is in an ellipse (in standard position) if:
   1.168 +    //      x^2     y^2
   1.169 +    //     ----- + ----- <= 1
   1.170 +    //      a^2     b^2
   1.171 +    // or :
   1.172 +    //     b^2*x^2 + a^2*y^2 <= (ab)^2
   1.173 +    SkScalar dist =  SkScalarMul(SkScalarSquare(canonicalPt.fX), SkScalarSquare(fRadii[index].fY)) +
   1.174 +                     SkScalarMul(SkScalarSquare(canonicalPt.fY), SkScalarSquare(fRadii[index].fX));
   1.175 +    return dist <= SkScalarSquare(SkScalarMul(fRadii[index].fX, fRadii[index].fY));
   1.176 +}
   1.177 +
   1.178 +bool SkRRect::allCornersCircular() const {
   1.179 +    return fRadii[0].fX == fRadii[0].fY &&
   1.180 +        fRadii[1].fX == fRadii[1].fY &&
   1.181 +        fRadii[2].fX == fRadii[2].fY &&
   1.182 +        fRadii[3].fX == fRadii[3].fY;
   1.183 +}
   1.184 +
   1.185 +bool SkRRect::contains(const SkRect& rect) const {
   1.186 +    if (!this->getBounds().contains(rect)) {
   1.187 +        // If 'rect' isn't contained by the RR's bounds then the
   1.188 +        // RR definitely doesn't contain it
   1.189 +        return false;
   1.190 +    }
   1.191 +
   1.192 +    if (this->isRect()) {
   1.193 +        // the prior test was sufficient
   1.194 +        return true;
   1.195 +    }
   1.196 +
   1.197 +    // At this point we know all four corners of 'rect' are inside the
   1.198 +    // bounds of of this RR. Check to make sure all the corners are inside
   1.199 +    // all the curves
   1.200 +    return this->checkCornerContainment(rect.fLeft, rect.fTop) &&
   1.201 +           this->checkCornerContainment(rect.fRight, rect.fTop) &&
   1.202 +           this->checkCornerContainment(rect.fRight, rect.fBottom) &&
   1.203 +           this->checkCornerContainment(rect.fLeft, rect.fBottom);
   1.204 +}
   1.205 +
   1.206 +// There is a simplified version of this method in setRectXY
   1.207 +void SkRRect::computeType() const {
   1.208 +    SkDEBUGCODE(this->validate();)
   1.209 +
   1.210 +    if (fRect.isEmpty()) {
   1.211 +        fType = kEmpty_Type;
   1.212 +        return;
   1.213 +    }
   1.214 +
   1.215 +    bool allRadiiEqual = true; // are all x radii equal and all y radii?
   1.216 +    bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY;
   1.217 +
   1.218 +    for (int i = 1; i < 4; ++i) {
   1.219 +        if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
   1.220 +            // if either radius is zero the corner is square so both have to
   1.221 +            // be non-zero to have a rounded corner
   1.222 +            allCornersSquare = false;
   1.223 +        }
   1.224 +        if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
   1.225 +            allRadiiEqual = false;
   1.226 +        }
   1.227 +    }
   1.228 +
   1.229 +    if (allCornersSquare) {
   1.230 +        fType = kRect_Type;
   1.231 +        return;
   1.232 +    }
   1.233 +
   1.234 +    if (allRadiiEqual) {
   1.235 +        if (fRadii[0].fX >= SkScalarHalf(fRect.width()) &&
   1.236 +            fRadii[0].fY >= SkScalarHalf(fRect.height())) {
   1.237 +            fType = kOval_Type;
   1.238 +        } else {
   1.239 +            fType = kSimple_Type;
   1.240 +        }
   1.241 +        return;
   1.242 +    }
   1.243 +
   1.244 +    fType = kComplex_Type;
   1.245 +}
   1.246 +
   1.247 +static bool matrix_only_scale_and_translate(const SkMatrix& matrix) {
   1.248 +    const SkMatrix::TypeMask m = (SkMatrix::TypeMask) (SkMatrix::kAffine_Mask
   1.249 +                                    | SkMatrix::kPerspective_Mask);
   1.250 +    return (matrix.getType() & m) == 0;
   1.251 +}
   1.252 +
   1.253 +bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const {
   1.254 +    if (NULL == dst) {
   1.255 +        return false;
   1.256 +    }
   1.257 +
   1.258 +    // Assert that the caller is not trying to do this in place, which
   1.259 +    // would violate const-ness. Do not return false though, so that
   1.260 +    // if they know what they're doing and want to violate it they can.
   1.261 +    SkASSERT(dst != this);
   1.262 +
   1.263 +    if (matrix.isIdentity()) {
   1.264 +        *dst = *this;
   1.265 +        return true;
   1.266 +    }
   1.267 +
   1.268 +    // If transform supported 90 degree rotations (which it could), we could
   1.269 +    // use SkMatrix::rectStaysRect() to check for a valid transformation.
   1.270 +    if (!matrix_only_scale_and_translate(matrix)) {
   1.271 +        return false;
   1.272 +    }
   1.273 +
   1.274 +    SkRect newRect;
   1.275 +    if (!matrix.mapRect(&newRect, fRect)) {
   1.276 +        return false;
   1.277 +    }
   1.278 +
   1.279 +    // At this point, this is guaranteed to succeed, so we can modify dst.
   1.280 +    dst->fRect = newRect;
   1.281 +
   1.282 +    // Now scale each corner
   1.283 +    SkScalar xScale = matrix.getScaleX();
   1.284 +    const bool flipX = xScale < 0;
   1.285 +    if (flipX) {
   1.286 +        xScale = -xScale;
   1.287 +    }
   1.288 +    SkScalar yScale = matrix.getScaleY();
   1.289 +    const bool flipY = yScale < 0;
   1.290 +    if (flipY) {
   1.291 +        yScale = -yScale;
   1.292 +    }
   1.293 +
   1.294 +    // Scale the radii without respecting the flip.
   1.295 +    for (int i = 0; i < 4; ++i) {
   1.296 +        dst->fRadii[i].fX = SkScalarMul(fRadii[i].fX, xScale);
   1.297 +        dst->fRadii[i].fY = SkScalarMul(fRadii[i].fY, yScale);
   1.298 +    }
   1.299 +
   1.300 +    // Now swap as necessary.
   1.301 +    if (flipX) {
   1.302 +        if (flipY) {
   1.303 +            // Swap with opposite corners
   1.304 +            SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]);
   1.305 +            SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]);
   1.306 +        } else {
   1.307 +            // Only swap in x
   1.308 +            SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]);
   1.309 +            SkTSwap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]);
   1.310 +        }
   1.311 +    } else if (flipY) {
   1.312 +        // Only swap in y
   1.313 +        SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]);
   1.314 +        SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]);
   1.315 +    }
   1.316 +
   1.317 +    // Since the only transforms that were allowed are scale and translate, the type
   1.318 +    // remains unchanged.
   1.319 +    dst->fType = fType;
   1.320 +
   1.321 +    SkDEBUGCODE(dst->validate();)
   1.322 +
   1.323 +    return true;
   1.324 +}
   1.325 +
   1.326 +///////////////////////////////////////////////////////////////////////////////
   1.327 +
   1.328 +void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
   1.329 +    SkRect r = fRect;
   1.330 +
   1.331 +    r.inset(dx, dy);
   1.332 +    if (r.isEmpty()) {
   1.333 +        dst->setEmpty();
   1.334 +        return;
   1.335 +    }
   1.336 +
   1.337 +    SkVector radii[4];
   1.338 +    memcpy(radii, fRadii, sizeof(radii));
   1.339 +    for (int i = 0; i < 4; ++i) {
   1.340 +        if (radii[i].fX) {
   1.341 +            radii[i].fX -= dx;
   1.342 +        }
   1.343 +        if (radii[i].fY) {
   1.344 +            radii[i].fY -= dy;
   1.345 +        }
   1.346 +    }
   1.347 +    dst->setRectRadii(r, radii);
   1.348 +}
   1.349 +
   1.350 +///////////////////////////////////////////////////////////////////////////////
   1.351 +
   1.352 +size_t SkRRect::writeToMemory(void* buffer) const {
   1.353 +    SkASSERT(kSizeInMemory == sizeof(SkRect) + sizeof(fRadii));
   1.354 +
   1.355 +    memcpy(buffer, &fRect, sizeof(SkRect));
   1.356 +    memcpy((char*)buffer + sizeof(SkRect), fRadii, sizeof(fRadii));
   1.357 +    return kSizeInMemory;
   1.358 +}
   1.359 +
   1.360 +size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
   1.361 +    if (length < kSizeInMemory) {
   1.362 +        return 0;
   1.363 +    }
   1.364 +
   1.365 +    SkScalar storage[12];
   1.366 +    SkASSERT(sizeof(storage) == kSizeInMemory);
   1.367 +
   1.368 +    // we make a local copy, to ensure alignment before we cast
   1.369 +    memcpy(storage, buffer, kSizeInMemory);
   1.370 +
   1.371 +    this->setRectRadii(*(const SkRect*)&storage[0],
   1.372 +                       (const SkVector*)&storage[4]);
   1.373 +    return kSizeInMemory;
   1.374 +}
   1.375 +
   1.376 +///////////////////////////////////////////////////////////////////////////////
   1.377 +
   1.378 +#ifdef SK_DEBUG
   1.379 +void SkRRect::validate() const {
   1.380 +    bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
   1.381 +    bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
   1.382 +    bool allRadiiSame = true;
   1.383 +
   1.384 +    for (int i = 1; i < 4; ++i) {
   1.385 +        if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
   1.386 +            allRadiiZero = false;
   1.387 +        }
   1.388 +
   1.389 +        if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
   1.390 +            allRadiiSame = false;
   1.391 +        }
   1.392 +
   1.393 +        if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
   1.394 +            allCornersSquare = false;
   1.395 +        }
   1.396 +    }
   1.397 +
   1.398 +    switch (fType) {
   1.399 +        case kEmpty_Type:
   1.400 +            SkASSERT(fRect.isEmpty());
   1.401 +            SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);
   1.402 +
   1.403 +            SkASSERT(0 == fRect.fLeft && 0 == fRect.fTop &&
   1.404 +                     0 == fRect.fRight && 0 == fRect.fBottom);
   1.405 +            break;
   1.406 +        case kRect_Type:
   1.407 +            SkASSERT(!fRect.isEmpty());
   1.408 +            SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);
   1.409 +            break;
   1.410 +        case kOval_Type:
   1.411 +            SkASSERT(!fRect.isEmpty());
   1.412 +            SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);
   1.413 +
   1.414 +            for (int i = 0; i < 4; ++i) {
   1.415 +                SkASSERT(SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width())));
   1.416 +                SkASSERT(SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height())));
   1.417 +            }
   1.418 +            break;
   1.419 +        case kSimple_Type:
   1.420 +            SkASSERT(!fRect.isEmpty());
   1.421 +            SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);
   1.422 +            break;
   1.423 +        case kComplex_Type:
   1.424 +            SkASSERT(!fRect.isEmpty());
   1.425 +            SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare);
   1.426 +            break;
   1.427 +        case kUnknown_Type:
   1.428 +            // no limits on this
   1.429 +            break;
   1.430 +    }
   1.431 +}
   1.432 +#endif // SK_DEBUG
   1.433 +
   1.434 +///////////////////////////////////////////////////////////////////////////////

mercurial