Home | History | Annotate | Download | only in android
      1 /*
      2  * Copyright 2007, The Android Open Source Project
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *  * Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  *  * Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "Path.h"
     28 
     29 #include "AffineTransform.h"
     30 #include "FloatRect.h"
     31 #include "GraphicsContext.h"
     32 #include "ImageBuffer.h"
     33 #include "PlatformGraphicsContext.h"
     34 #include "SkiaUtils.h"
     35 #include "SkPaint.h"
     36 #include "SkPath.h"
     37 #include "SkRegion.h"
     38 #include "StrokeStyleApplier.h"
     39 #include "TransformationMatrix.h"
     40 #include "android_graphics.h"
     41 
     42 namespace WebCore {
     43 
     44 Path::Path()
     45 {
     46     m_path = new SkPath;
     47 //    m_path->setFlags(SkPath::kWinding_FillType);
     48 }
     49 
     50 Path::Path(const Path& other)
     51 {
     52     m_path = new SkPath(*other.m_path);
     53 }
     54 
     55 Path::~Path()
     56 {
     57     delete m_path;
     58 }
     59 
     60 Path& Path::operator=(const Path& other)
     61 {
     62     *m_path = *other.m_path;
     63     return *this;
     64 }
     65 
     66 bool Path::isEmpty() const
     67 {
     68     return m_path->isEmpty();
     69 }
     70 
     71 bool Path::hasCurrentPoint() const
     72 {
     73     // webkit wants to know if we have any points, including any moveTos.
     74     // Skia's empty() will return true if it has just a moveTo, so we need to
     75     // call getPoints(NULL), which returns the number of points,
     76     // including moveTo.
     77     return m_path->getPoints(0, 0) > 0;
     78 }
     79 
     80 FloatPoint Path::currentPoint() const
     81 {
     82     if (hasCurrentPoint()) {
     83         SkPoint lastPt;
     84         m_path->getLastPt(&lastPt);
     85         return lastPt;
     86     }
     87     float quietNaN = std::numeric_limits<float>::quiet_NaN();
     88     return FloatPoint(quietNaN, quietNaN);
     89 }
     90 
     91 bool Path::contains(const FloatPoint& point, WindRule rule) const
     92 {
     93     SkRegion    rgn, clip;
     94 
     95     int x = (int)floorf(point.x());
     96     int y = (int)floorf(point.y());
     97     clip.setRect(x, y, x + 1, y + 1);
     98 
     99     SkPath::FillType ft = m_path->getFillType();    // save
    100     m_path->setFillType(rule == RULE_NONZERO ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
    101 
    102     bool contains = rgn.setPath(*m_path, clip);
    103 
    104     m_path->setFillType(ft);    // restore
    105     return contains;
    106 }
    107 
    108 void Path::translate(const FloatSize& size)
    109 {
    110     m_path->offset(SkFloatToScalar(size.width()), SkFloatToScalar(size.height()));
    111 }
    112 
    113 FloatRect Path::boundingRect() const
    114 {
    115     const SkRect& r = m_path->getBounds();
    116     return FloatRect(   SkScalarToFloat(r.fLeft),
    117                         SkScalarToFloat(r.fTop),
    118                         SkScalarToFloat(r.width()),
    119                         SkScalarToFloat(r.height()));
    120 }
    121 
    122 void Path::moveTo(const FloatPoint& point)
    123 {
    124     m_path->moveTo(SkFloatToScalar(point.x()), SkFloatToScalar(point.y()));
    125 }
    126 
    127 void Path::addLineTo(const FloatPoint& p)
    128 {
    129     m_path->lineTo(SkFloatToScalar(p.x()), SkFloatToScalar(p.y()));
    130 }
    131 
    132 void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep)
    133 {
    134     m_path->quadTo( SkFloatToScalar(cp.x()), SkFloatToScalar(cp.y()),
    135                     SkFloatToScalar(ep.x()), SkFloatToScalar(ep.y()));
    136 }
    137 
    138 void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep)
    139 {
    140     m_path->cubicTo(SkFloatToScalar(p1.x()), SkFloatToScalar(p1.y()),
    141                     SkFloatToScalar(p2.x()), SkFloatToScalar(p2.y()),
    142                     SkFloatToScalar(ep.x()), SkFloatToScalar(ep.y()));
    143 }
    144 
    145 void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
    146 {
    147     m_path->arcTo(SkFloatToScalar(p1.x()), SkFloatToScalar(p1.y()),
    148                   SkFloatToScalar(p2.x()), SkFloatToScalar(p2.y()),
    149                   SkFloatToScalar(radius));
    150 }
    151 
    152 void Path::closeSubpath()
    153 {
    154     m_path->close();
    155 }
    156 
    157 static const float gPI  = 3.14159265f;
    158 static const float g2PI = 6.28318531f;
    159 static const float g180OverPI = 57.29577951308f;
    160 
    161 static float fast_mod(float angle, float max) {
    162     if (angle >= max || angle <= -max) {
    163         angle = fmodf(angle, max);
    164     }
    165     return angle;
    166 }
    167 
    168 void Path::addArc(const FloatPoint& p, float r, float sa, float ea,
    169                   bool clockwise) {
    170     SkScalar    cx = SkFloatToScalar(p.x());
    171     SkScalar    cy = SkFloatToScalar(p.y());
    172     SkScalar    radius = SkFloatToScalar(r);
    173 
    174     SkRect  oval;
    175     oval.set(cx - radius, cy - radius, cx + radius, cy + radius);
    176 
    177     float sweep = ea - sa;
    178     bool prependOval = false;
    179 
    180     /*  Note if clockwise and the sign of the sweep disagree. This particular
    181         logic was deduced from http://canvex.lazyilluminati.com/misc/arc.html
    182     */
    183     if (clockwise && (sweep > 0 || sweep < -g2PI)) {
    184         sweep = fmodf(sweep, g2PI) - g2PI;
    185     } else if (!clockwise && (sweep < 0 || sweep > g2PI)) {
    186         sweep = fmodf(sweep, g2PI) + g2PI;
    187     }
    188 
    189     // If the abs(sweep) >= 2PI, then we need to add a circle before we call
    190     // arcTo, since it treats the sweep mod 2PI. We don't have a prepend call,
    191     // so we just remember this, and at the end create a new path with an oval
    192     // and our current path, and then swap then.
    193     //
    194     if (sweep >= g2PI || sweep <= -g2PI) {
    195         prependOval = true;
    196 //        SkDebugf("addArc sa=%g ea=%g cw=%d sweep %g treat as circle\n", sa, ea, clockwise, sweep);
    197 
    198         // now reduce sweep to just the amount we need, so that the current
    199         // point is left where the caller expects it.
    200         sweep = fmodf(sweep, g2PI);
    201     }
    202 
    203     sa = fast_mod(sa, g2PI);
    204     SkScalar startDegrees = SkFloatToScalar(sa * g180OverPI);
    205     SkScalar sweepDegrees = SkFloatToScalar(sweep * g180OverPI);
    206 
    207 //    SkDebugf("addArc sa=%g ea=%g cw=%d sweep=%g ssweep=%g\n", sa, ea, clockwise, sweep, SkScalarToFloat(sweepDegrees));
    208     m_path->arcTo(oval, startDegrees, sweepDegrees, false);
    209 
    210     if (prependOval) {
    211         SkPath tmp;
    212         tmp.addOval(oval);
    213         tmp.addPath(*m_path);
    214         m_path->swap(tmp);
    215     }
    216 }
    217 
    218 void Path::addRect(const FloatRect& rect)
    219 {
    220     m_path->addRect(rect);
    221 }
    222 
    223 void Path::addEllipse(const FloatRect& rect)
    224 {
    225     m_path->addOval(rect);
    226 }
    227 
    228 void Path::clear()
    229 {
    230     m_path->reset();
    231 }
    232 
    233 static FloatPoint* setfpts(FloatPoint dst[], const SkPoint src[], int count)
    234 {
    235     for (int i = 0; i < count; i++)
    236     {
    237         dst[i].setX(SkScalarToFloat(src[i].fX));
    238         dst[i].setY(SkScalarToFloat(src[i].fY));
    239     }
    240     return dst;
    241 }
    242 
    243 void Path::apply(void* info, PathApplierFunction function) const
    244 {
    245     SkPath::Iter    iter(*m_path, false);
    246     SkPoint         pts[4];
    247 
    248     PathElement     elem;
    249     FloatPoint      fpts[3];
    250 
    251     for (;;)
    252     {
    253         switch (iter.next(pts)) {
    254         case SkPath::kMove_Verb:
    255             elem.type = PathElementMoveToPoint;
    256             elem.points = setfpts(fpts, &pts[0], 1);
    257             break;
    258         case SkPath::kLine_Verb:
    259             elem.type = PathElementAddLineToPoint;
    260             elem.points = setfpts(fpts, &pts[1], 1);
    261             break;
    262         case SkPath::kQuad_Verb:
    263             elem.type = PathElementAddQuadCurveToPoint;
    264             elem.points = setfpts(fpts, &pts[1], 2);
    265             break;
    266         case SkPath::kCubic_Verb:
    267             elem.type = PathElementAddCurveToPoint;
    268             elem.points = setfpts(fpts, &pts[1], 3);
    269             break;
    270         case SkPath::kClose_Verb:
    271             elem.type = PathElementCloseSubpath;
    272             elem.points = setfpts(fpts, 0, 0);
    273             break;
    274         case SkPath::kDone_Verb:
    275             return;
    276         }
    277         function(info, &elem);
    278     }
    279 }
    280 
    281 void Path::transform(const AffineTransform& xform)
    282 {
    283     m_path->transform(xform);
    284 }
    285 
    286 ///////////////////////////////////////////////////////////////////////////////
    287 
    288 FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) const
    289 {
    290     GraphicsContext* scratch = scratchContext();
    291     scratch->save();
    292 
    293     if (applier)
    294         applier->strokeStyle(scratch);
    295 
    296     SkPaint paint;
    297     scratch->setupStrokePaint(&paint);
    298     SkPath boundingPath;
    299     paint.getFillPath(*platformPath(), &boundingPath);
    300 
    301     FloatRect r = boundingPath.getBounds();
    302     scratch->restore();
    303     return r;
    304 }
    305 
    306 #if ENABLE(SVG)
    307 bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
    308 {
    309     GraphicsContext* scratch = scratchContext();
    310     scratch->save();
    311 
    312     applier->strokeStyle(scratch);
    313 
    314     SkPaint paint;
    315     scratch->setupStrokePaint(&paint);
    316     SkPath strokePath;
    317     paint.getFillPath(*platformPath(), &strokePath);
    318     bool contains = SkPathContainsPoint(&strokePath, point,
    319                                         SkPath::kWinding_FillType);
    320 
    321     scratch->restore();
    322     return contains;
    323 }
    324 #endif
    325 
    326 }
    327