Home | History | Annotate | Download | only in skia
      1 // Copyright (c) 2008, Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 #include "config.h"
     31 #include "Path.h"
     32 
     33 #include "AffineTransform.h"
     34 #include "FloatRect.h"
     35 #include "ImageBuffer.h"
     36 #include "StrokeStyleApplier.h"
     37 
     38 #include "SkPath.h"
     39 #include "SkRegion.h"
     40 #include "SkiaUtils.h"
     41 
     42 #include <wtf/MathExtras.h>
     43 
     44 namespace WebCore {
     45 
     46 Path::Path()
     47 {
     48     m_path = new SkPath;
     49 }
     50 
     51 Path::Path(const Path& other)
     52 {
     53     m_path = new SkPath(*other.m_path);
     54 }
     55 
     56 Path::~Path()
     57 {
     58     delete m_path;
     59 }
     60 
     61 Path& Path::operator=(const Path& other)
     62 {
     63     *m_path = *other.m_path;
     64     return *this;
     65 }
     66 
     67 bool Path::isEmpty() const
     68 {
     69     return m_path->isEmpty();
     70 }
     71 
     72 bool Path::hasCurrentPoint() const
     73 {
     74     return m_path->getPoints(NULL, 0) != 0;
     75 }
     76 
     77 bool Path::contains(const FloatPoint& point, WindRule rule) const
     78 {
     79     return SkPathContainsPoint(m_path, point,
     80       rule == RULE_NONZERO ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
     81 }
     82 
     83 void Path::translate(const FloatSize& size)
     84 {
     85     m_path->offset(WebCoreFloatToSkScalar(size.width()), WebCoreFloatToSkScalar(size.height()));
     86 }
     87 
     88 FloatRect Path::boundingRect() const
     89 {
     90     return m_path->getBounds();
     91 }
     92 
     93 void Path::moveTo(const FloatPoint& point)
     94 {
     95     m_path->moveTo(point);
     96 }
     97 
     98 void Path::addLineTo(const FloatPoint& point)
     99 {
    100     m_path->lineTo(point);
    101 }
    102 
    103 void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep)
    104 {
    105     m_path->quadTo(cp, ep);
    106 }
    107 
    108 void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep)
    109 {
    110     m_path->cubicTo(p1, p2, ep);
    111 }
    112 
    113 void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
    114 {
    115     m_path->arcTo(p1, p2, WebCoreFloatToSkScalar(radius));
    116 }
    117 
    118 void Path::closeSubpath()
    119 {
    120     m_path->close();
    121 }
    122 
    123 void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool anticlockwise) {
    124     SkScalar cx = WebCoreFloatToSkScalar(p.x());
    125     SkScalar cy = WebCoreFloatToSkScalar(p.y());
    126     SkScalar radius = WebCoreFloatToSkScalar(r);
    127     SkScalar s360 = SkIntToScalar(360);
    128 
    129     SkRect oval;
    130     oval.set(cx - radius, cy - radius, cx + radius, cy + radius);
    131 
    132     float sweep = ea - sa;
    133     SkScalar startDegrees = WebCoreFloatToSkScalar(sa * 180 / piFloat);
    134     SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat);
    135     // Check for a circle.
    136     if (sweepDegrees >= s360 || sweepDegrees <= -s360) {
    137         // Move to the start position (0 sweep means we add a single point).
    138         m_path->arcTo(oval, startDegrees, 0, false);
    139         // Draw the circle.
    140         m_path->addOval(oval);
    141         // Force a moveTo the end position.
    142         m_path->arcTo(oval, startDegrees + sweepDegrees, 0, true);
    143     } else {
    144         // Counterclockwise arcs should be drawn with negative sweeps, while
    145         // clockwise arcs should be drawn with positive sweeps. Check to see
    146         // if the situation is reversed and correct it by adding or subtracting
    147         // a full circle
    148         if (anticlockwise && sweepDegrees > 0) {
    149             sweepDegrees -= s360;
    150         } else if (!anticlockwise && sweepDegrees < 0) {
    151             sweepDegrees += s360;
    152         }
    153 
    154         m_path->arcTo(oval, startDegrees, sweepDegrees, false);
    155     }
    156 }
    157 
    158 void Path::addRect(const FloatRect& rect)
    159 {
    160     m_path->addRect(rect);
    161 }
    162 
    163 void Path::addEllipse(const FloatRect& rect)
    164 {
    165     m_path->addOval(rect);
    166 }
    167 
    168 void Path::clear()
    169 {
    170     m_path->reset();
    171 }
    172 
    173 static FloatPoint* convertPathPoints(FloatPoint dst[], const SkPoint src[], int count)
    174 {
    175     for (int i = 0; i < count; i++) {
    176         dst[i].setX(SkScalarToFloat(src[i].fX));
    177         dst[i].setY(SkScalarToFloat(src[i].fY));
    178     }
    179     return dst;
    180 }
    181 
    182 void Path::apply(void* info, PathApplierFunction function) const
    183 {
    184     SkPath::Iter iter(*m_path, false);
    185     SkPoint pts[4];
    186     PathElement pathElement;
    187     FloatPoint pathPoints[3];
    188 
    189     for (;;) {
    190         switch (iter.next(pts)) {
    191         case SkPath::kMove_Verb:
    192             pathElement.type = PathElementMoveToPoint;
    193             pathElement.points = convertPathPoints(pathPoints, &pts[0], 1);
    194             break;
    195         case SkPath::kLine_Verb:
    196             pathElement.type = PathElementAddLineToPoint;
    197             pathElement.points = convertPathPoints(pathPoints, &pts[1], 1);
    198             break;
    199         case SkPath::kQuad_Verb:
    200             pathElement.type = PathElementAddQuadCurveToPoint;
    201             pathElement.points = convertPathPoints(pathPoints, &pts[1], 2);
    202             break;
    203         case SkPath::kCubic_Verb:
    204             pathElement.type = PathElementAddCurveToPoint;
    205             pathElement.points = convertPathPoints(pathPoints, &pts[1], 3);
    206             break;
    207         case SkPath::kClose_Verb:
    208             pathElement.type = PathElementCloseSubpath;
    209             pathElement.points = convertPathPoints(pathPoints, 0, 0);
    210             break;
    211         case SkPath::kDone_Verb:
    212             return;
    213         }
    214         function(info, &pathElement);
    215     }
    216 }
    217 
    218 void Path::transform(const AffineTransform& xform)
    219 {
    220     m_path->transform(xform);
    221 }
    222 
    223 String Path::debugString() const
    224 {
    225     String result;
    226 
    227     SkPath::Iter iter(*m_path, false);
    228     SkPoint pts[4];
    229 
    230     int numPoints = m_path->getPoints(0, 0);
    231     SkPath::Verb verb;
    232 
    233     do {
    234         verb = iter.next(pts);
    235         switch (verb) {
    236         case SkPath::kMove_Verb:
    237             result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY);
    238             numPoints -= 1;
    239             break;
    240         case SkPath::kLine_Verb:
    241           if (!iter.isCloseLine()) {
    242                 result += String::format("L%.2f,%.2f ", pts[1].fX, pts[1].fY);
    243                 numPoints -= 1;
    244             }
    245             break;
    246         case SkPath::kQuad_Verb:
    247             result += String::format("Q%.2f,%.2f,%.2f,%.2f ",
    248                 pts[1].fX, pts[1].fY,
    249                 pts[2].fX, pts[2].fY);
    250             numPoints -= 2;
    251             break;
    252         case SkPath::kCubic_Verb:
    253             result += String::format("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f ",
    254                 pts[1].fX, pts[1].fY,
    255                 pts[2].fX, pts[2].fY,
    256                 pts[3].fX, pts[3].fY);
    257             numPoints -= 3;
    258             break;
    259         case SkPath::kClose_Verb:
    260             result += "Z ";
    261             break;
    262         case SkPath::kDone_Verb:
    263             break;
    264         }
    265     } while (verb != SkPath::kDone_Verb);
    266 
    267     // If you have a path that ends with an M, Skia will not iterate the
    268     // trailing M. That's nice of it, but Apple's paths output the trailing M
    269     // and we want out layout dumps to look like theirs
    270     if (numPoints) {
    271         ASSERT(numPoints==1);
    272         m_path->getLastPt(pts);
    273         result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY);
    274     }
    275 
    276     return result.stripWhiteSpace();
    277 }
    278 
    279 // Computes the bounding box for the stroke and style currently selected into
    280 // the given bounding box. This also takes into account the stroke width.
    281 static FloatRect boundingBoxForCurrentStroke(const GraphicsContext* context)
    282 {
    283     SkPaint paint;
    284     context->platformContext()->setupPaintForStroking(&paint, 0, 0);
    285     SkPath boundingPath;
    286     paint.getFillPath(context->platformContext()->currentPathInLocalCoordinates(), &boundingPath);
    287     return boundingPath.getBounds();
    288 }
    289 
    290 FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier)
    291 {
    292     GraphicsContext* scratch = scratchContext();
    293     scratch->save();
    294     scratch->beginPath();
    295     scratch->addPath(*this);
    296 
    297     if (applier)
    298         applier->strokeStyle(scratch);
    299 
    300     FloatRect r = boundingBoxForCurrentStroke(scratch);
    301     scratch->restore();
    302     return r;
    303 }
    304 
    305 bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
    306 {
    307     ASSERT(applier);
    308     GraphicsContext* scratch = scratchContext();
    309     scratch->save();
    310 
    311     applier->strokeStyle(scratch);
    312 
    313     SkPaint paint;
    314     scratch->platformContext()->setupPaintForStroking(&paint, 0, 0);
    315     SkPath strokePath;
    316     paint.getFillPath(*platformPath(), &strokePath);
    317     bool contains = SkPathContainsPoint(&strokePath, point,
    318                                         SkPath::kWinding_FillType);
    319 
    320     scratch->restore();
    321     return contains;
    322 }
    323 } // namespace WebCore
    324