Home | History | Annotate | Download | only in effects
      1 /*
      2  * Copyright 2006 The Android Open Source Project
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 
      9 #include "SkCornerPathEffect.h"
     10 #include "SkPath.h"
     11 #include "SkPoint.h"
     12 #include "SkReadBuffer.h"
     13 #include "SkWriteBuffer.h"
     14 
     15 SkCornerPathEffect::SkCornerPathEffect(SkScalar radius) {
     16     // check with ! to catch NaNs
     17     if (!(radius > 0)) {
     18         radius = 0;
     19     }
     20     fRadius = radius;
     21 }
     22 SkCornerPathEffect::~SkCornerPathEffect() {}
     23 
     24 static bool ComputeStep(const SkPoint& a, const SkPoint& b, SkScalar radius,
     25                         SkPoint* step) {
     26     SkScalar dist = SkPoint::Distance(a, b);
     27 
     28     *step = b - a;
     29     if (dist <= radius * 2) {
     30         *step *= SK_ScalarHalf;
     31         return false;
     32     } else {
     33         *step *= radius / dist;
     34         return true;
     35     }
     36 }
     37 
     38 bool SkCornerPathEffect::onFilterPath(SkPath* dst, const SkPath& src,
     39                                       SkStrokeRec*, const SkRect*) const {
     40     if (fRadius <= 0) {
     41         return false;
     42     }
     43 
     44     SkPath::Iter    iter(src, false);
     45     SkPath::Verb    verb, prevVerb = SkPath::kDone_Verb;
     46     SkPoint         pts[4];
     47 
     48     bool        closed;
     49     SkPoint     moveTo, lastCorner;
     50     SkVector    firstStep, step;
     51     bool        prevIsValid = true;
     52 
     53     // to avoid warnings
     54     step.set(0, 0);
     55     moveTo.set(0, 0);
     56     firstStep.set(0, 0);
     57     lastCorner.set(0, 0);
     58 
     59     for (;;) {
     60         switch (verb = iter.next(pts, false)) {
     61             case SkPath::kMove_Verb:
     62                 // close out the previous (open) contour
     63                 if (SkPath::kLine_Verb == prevVerb) {
     64                     dst->lineTo(lastCorner);
     65                 }
     66                 closed = iter.isClosedContour();
     67                 if (closed) {
     68                     moveTo = pts[0];
     69                     prevIsValid = false;
     70                 } else {
     71                     dst->moveTo(pts[0]);
     72                     prevIsValid = true;
     73                 }
     74                 break;
     75             case SkPath::kLine_Verb: {
     76                 bool drawSegment = ComputeStep(pts[0], pts[1], fRadius, &step);
     77                 // prev corner
     78                 if (!prevIsValid) {
     79                     dst->moveTo(moveTo + step);
     80                     prevIsValid = true;
     81                 } else {
     82                     dst->quadTo(pts[0].fX, pts[0].fY, pts[0].fX + step.fX,
     83                                 pts[0].fY + step.fY);
     84                 }
     85                 if (drawSegment) {
     86                     dst->lineTo(pts[1].fX - step.fX, pts[1].fY - step.fY);
     87                 }
     88                 lastCorner = pts[1];
     89                 prevIsValid = true;
     90                 break;
     91             }
     92             case SkPath::kQuad_Verb:
     93                 // TBD - just replicate the curve for now
     94                 if (!prevIsValid) {
     95                     dst->moveTo(pts[0]);
     96                     prevIsValid = true;
     97                 }
     98                 dst->quadTo(pts[1], pts[2]);
     99                 lastCorner = pts[2];
    100                 firstStep.set(0, 0);
    101                 break;
    102             case SkPath::kConic_Verb:
    103                 // TBD - just replicate the curve for now
    104                 if (!prevIsValid) {
    105                     dst->moveTo(pts[0]);
    106                     prevIsValid = true;
    107                 }
    108                 dst->conicTo(pts[1], pts[2], iter.conicWeight());
    109                 lastCorner = pts[2];
    110                 firstStep.set(0, 0);
    111                 break;
    112             case SkPath::kCubic_Verb:
    113                 if (!prevIsValid) {
    114                     dst->moveTo(pts[0]);
    115                     prevIsValid = true;
    116                 }
    117                 // TBD - just replicate the curve for now
    118                 dst->cubicTo(pts[1], pts[2], pts[3]);
    119                 lastCorner = pts[3];
    120                 firstStep.set(0, 0);
    121                 break;
    122             case SkPath::kClose_Verb:
    123                 if (firstStep.fX || firstStep.fY) {
    124                     dst->quadTo(lastCorner.fX, lastCorner.fY,
    125                                 lastCorner.fX + firstStep.fX,
    126                                 lastCorner.fY + firstStep.fY);
    127                 }
    128                 dst->close();
    129                 prevIsValid = false;
    130                 break;
    131             case SkPath::kDone_Verb:
    132                 if (prevIsValid) {
    133                     dst->lineTo(lastCorner);
    134                 }
    135                 return true;
    136             default:
    137                 SkDEBUGFAIL("default should not be reached");
    138                 return false;
    139         }
    140 
    141         if (SkPath::kMove_Verb == prevVerb) {
    142             firstStep = step;
    143         }
    144         prevVerb = verb;
    145     }
    146 
    147     return true;
    148 }
    149 
    150 sk_sp<SkFlattenable> SkCornerPathEffect::CreateProc(SkReadBuffer& buffer) {
    151     return SkCornerPathEffect::Make(buffer.readScalar());
    152 }
    153 
    154 void SkCornerPathEffect::flatten(SkWriteBuffer& buffer) const {
    155     buffer.writeScalar(fRadius);
    156 }
    157