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 "SkPaint.h"
     34 #include "SkPath.h"
     35 #include "SkRegion.h"
     36 #include "StrokeStyleApplier.h"
     37 #include "TransformationMatrix.h"
     38 #include "android_graphics.h"
     39 
     40 namespace WebCore {
     41 
     42 Path::Path()
     43 {
     44     m_path = new SkPath;
     45 //    m_path->setFlags(SkPath::kWinding_FillType);
     46 }
     47 
     48 Path::Path(const Path& other)
     49 {
     50     m_path = new SkPath(*other.m_path);
     51 }
     52 
     53 Path::~Path()
     54 {
     55     delete m_path;
     56 }
     57 
     58 Path& Path::operator=(const Path& other)
     59 {
     60     *m_path = *other.m_path;
     61     return *this;
     62 }
     63 
     64 bool Path::isEmpty() const
     65 {
     66     return m_path->isEmpty();
     67 }
     68 
     69 bool Path::hasCurrentPoint() const
     70 {
     71     // webkit wants to know if we have any points, including any moveTos.
     72     // Skia's empty() will return true if it has just a moveTo, so we need to
     73     // call getPoints(NULL), which returns the number of points,
     74     // including moveTo.
     75     return m_path->getPoints(0, 0) > 0;
     76 }
     77 
     78 bool Path::contains(const FloatPoint& point, WindRule rule) const
     79 {
     80     SkRegion    rgn, clip;
     81 
     82     int x = (int)floorf(point.x());
     83     int y = (int)floorf(point.y());
     84     clip.setRect(x, y, x + 1, y + 1);
     85 
     86     SkPath::FillType ft = m_path->getFillType();    // save
     87     m_path->setFillType(rule == RULE_NONZERO ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
     88 
     89     bool contains = rgn.setPath(*m_path, clip);
     90 
     91     m_path->setFillType(ft);    // restore
     92     return contains;
     93 }
     94 
     95 void Path::translate(const FloatSize& size)
     96 {
     97     m_path->offset(SkFloatToScalar(size.width()), SkFloatToScalar(size.height()));
     98 }
     99 
    100 FloatRect Path::boundingRect() const
    101 {
    102     const SkRect& r = m_path->getBounds();
    103     return FloatRect(   SkScalarToFloat(r.fLeft),
    104                         SkScalarToFloat(r.fTop),
    105                         SkScalarToFloat(r.width()),
    106                         SkScalarToFloat(r.height()));
    107 }
    108 
    109 void Path::moveTo(const FloatPoint& point)
    110 {
    111     m_path->moveTo(SkFloatToScalar(point.x()), SkFloatToScalar(point.y()));
    112 }
    113 
    114 void Path::addLineTo(const FloatPoint& p)
    115 {
    116     m_path->lineTo(SkFloatToScalar(p.x()), SkFloatToScalar(p.y()));
    117 }
    118 
    119 void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep)
    120 {
    121     m_path->quadTo( SkFloatToScalar(cp.x()), SkFloatToScalar(cp.y()),
    122                     SkFloatToScalar(ep.x()), SkFloatToScalar(ep.y()));
    123 }
    124 
    125 void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep)
    126 {
    127     m_path->cubicTo(SkFloatToScalar(p1.x()), SkFloatToScalar(p1.y()),
    128                     SkFloatToScalar(p2.x()), SkFloatToScalar(p2.y()),
    129                     SkFloatToScalar(ep.x()), SkFloatToScalar(ep.y()));
    130 }
    131 
    132 void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
    133 {
    134     m_path->arcTo(SkFloatToScalar(p1.x()), SkFloatToScalar(p1.y()),
    135                   SkFloatToScalar(p2.x()), SkFloatToScalar(p2.y()),
    136                   SkFloatToScalar(radius));
    137 }
    138 
    139 void Path::closeSubpath()
    140 {
    141     m_path->close();
    142 }
    143 
    144 static const float gPI  = 3.14159265f;
    145 static const float g2PI = 6.28318531f;
    146 static const float g180OverPI = 57.29577951308f;
    147 
    148 static float fast_mod(float angle, float max) {
    149     if (angle >= max || angle <= -max) {
    150         angle = fmodf(angle, max);
    151     }
    152     return angle;
    153 }
    154 
    155 void Path::addArc(const FloatPoint& p, float r, float sa, float ea,
    156                   bool clockwise) {
    157     SkScalar    cx = SkFloatToScalar(p.x());
    158     SkScalar    cy = SkFloatToScalar(p.y());
    159     SkScalar    radius = SkFloatToScalar(r);
    160 
    161     SkRect  oval;
    162     oval.set(cx - radius, cy - radius, cx + radius, cy + radius);
    163 
    164     float sweep = ea - sa;
    165     bool prependOval = false;
    166 
    167     /*  Note if clockwise and the sign of the sweep disagree. This particular
    168         logic was deduced from http://canvex.lazyilluminati.com/misc/arc.html
    169     */
    170     if (clockwise && (sweep > 0 || sweep < -g2PI)) {
    171         sweep = fmodf(sweep, g2PI) - g2PI;
    172     } else if (!clockwise && (sweep < 0 || sweep > g2PI)) {
    173         sweep = fmodf(sweep, g2PI) + g2PI;
    174     }
    175 
    176     // If the abs(sweep) >= 2PI, then we need to add a circle before we call
    177     // arcTo, since it treats the sweep mod 2PI. We don't have a prepend call,
    178     // so we just remember this, and at the end create a new path with an oval
    179     // and our current path, and then swap then.
    180     //
    181     if (sweep >= g2PI || sweep <= -g2PI) {
    182         prependOval = true;
    183 //        SkDebugf("addArc sa=%g ea=%g cw=%d sweep %g treat as circle\n", sa, ea, clockwise, sweep);
    184 
    185         // now reduce sweep to just the amount we need, so that the current
    186         // point is left where the caller expects it.
    187         sweep = fmodf(sweep, g2PI);
    188     }
    189 
    190     sa = fast_mod(sa, g2PI);
    191     SkScalar startDegrees = SkFloatToScalar(sa * g180OverPI);
    192     SkScalar sweepDegrees = SkFloatToScalar(sweep * g180OverPI);
    193 
    194 //    SkDebugf("addArc sa=%g ea=%g cw=%d sweep=%g ssweep=%g\n", sa, ea, clockwise, sweep, SkScalarToFloat(sweepDegrees));
    195     m_path->arcTo(oval, startDegrees, sweepDegrees, false);
    196 
    197     if (prependOval) {
    198         SkPath tmp;
    199         tmp.addOval(oval);
    200         tmp.addPath(*m_path);
    201         m_path->swap(tmp);
    202     }
    203 }
    204 
    205 void Path::addRect(const FloatRect& rect)
    206 {
    207     m_path->addRect(rect);
    208 }
    209 
    210 void Path::addEllipse(const FloatRect& rect)
    211 {
    212     m_path->addOval(rect);
    213 }
    214 
    215 void Path::clear()
    216 {
    217     m_path->reset();
    218 }
    219 
    220 static FloatPoint* setfpts(FloatPoint dst[], const SkPoint src[], int count)
    221 {
    222     for (int i = 0; i < count; i++)
    223     {
    224         dst[i].setX(SkScalarToFloat(src[i].fX));
    225         dst[i].setY(SkScalarToFloat(src[i].fY));
    226     }
    227     return dst;
    228 }
    229 
    230 void Path::apply(void* info, PathApplierFunction function) const
    231 {
    232     SkPath::Iter    iter(*m_path, false);
    233     SkPoint         pts[4];
    234 
    235     PathElement     elem;
    236     FloatPoint      fpts[3];
    237 
    238     for (;;)
    239     {
    240         switch (iter.next(pts)) {
    241         case SkPath::kMove_Verb:
    242             elem.type = PathElementMoveToPoint;
    243             elem.points = setfpts(fpts, &pts[0], 1);
    244             break;
    245         case SkPath::kLine_Verb:
    246             elem.type = PathElementAddLineToPoint;
    247             elem.points = setfpts(fpts, &pts[1], 1);
    248             break;
    249         case SkPath::kQuad_Verb:
    250             elem.type = PathElementAddQuadCurveToPoint;
    251             elem.points = setfpts(fpts, &pts[1], 2);
    252             break;
    253         case SkPath::kCubic_Verb:
    254             elem.type = PathElementAddCurveToPoint;
    255             elem.points = setfpts(fpts, &pts[1], 3);
    256             break;
    257         case SkPath::kClose_Verb:
    258             elem.type = PathElementCloseSubpath;
    259             elem.points = setfpts(fpts, 0, 0);
    260             break;
    261         case SkPath::kDone_Verb:
    262             return;
    263         }
    264         function(info, &elem);
    265     }
    266 }
    267 
    268 void Path::transform(const AffineTransform& xform)
    269 {
    270     m_path->transform(xform);
    271 }
    272 
    273 #if ENABLE(SVG)
    274 String Path::debugString() const
    275 {
    276     String result;
    277 
    278     SkPath::Iter iter(*m_path, false);
    279     SkPoint pts[4];
    280 
    281     int numPoints = m_path->getPoints(0, 0);
    282     SkPath::Verb verb;
    283 
    284     do {
    285         verb = iter.next(pts);
    286         switch (verb) {
    287         case SkPath::kMove_Verb:
    288             result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY);
    289             numPoints -= 1;
    290             break;
    291         case SkPath::kLine_Verb:
    292           if (!iter.isCloseLine()) {
    293                 result += String::format("L%.2f,%.2f ", pts[1].fX, pts[1].fY);
    294                 numPoints -= 1;
    295             }
    296             break;
    297         case SkPath::kQuad_Verb:
    298             result += String::format("Q%.2f,%.2f,%.2f,%.2f ",
    299                 pts[1].fX, pts[1].fY,
    300                 pts[2].fX, pts[2].fY);
    301             numPoints -= 2;
    302             break;
    303         case SkPath::kCubic_Verb:
    304             result += String::format("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f ",
    305                 pts[1].fX, pts[1].fY,
    306                 pts[2].fX, pts[2].fY,
    307                 pts[3].fX, pts[3].fY);
    308             numPoints -= 3;
    309             break;
    310         case SkPath::kClose_Verb:
    311             result += "Z ";
    312             break;
    313         case SkPath::kDone_Verb:
    314             break;
    315         }
    316     } while (verb != SkPath::kDone_Verb);
    317 
    318     // If you have a path that ends with an M, Skia will not iterate the
    319     // trailing M. That's nice of it, but Apple's paths output the trailing M
    320     // and we want out layout dumps to look like theirs
    321     if (numPoints) {
    322         ASSERT(numPoints==1);
    323         m_path->getLastPt(pts);
    324         result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY);
    325     }
    326 
    327     return result.stripWhiteSpace();
    328 }
    329 #endif
    330 
    331 ///////////////////////////////////////////////////////////////////////////////
    332 
    333 // Computes the bounding box for the stroke and style currently selected into
    334 // the given bounding box. This also takes into account the stroke width.
    335 static FloatRect boundingBoxForCurrentStroke(GraphicsContext* context)
    336 {
    337     const SkPath* path = context->getCurrPath();
    338     if (NULL == path) {
    339         return FloatRect();
    340     }
    341 
    342     SkPaint paint;
    343     context->setupStrokePaint(&paint);
    344     SkPath fillPath;
    345     paint.getFillPath(*path, &fillPath);
    346     const SkRect& r = fillPath.getBounds();
    347     return FloatRect(SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop),
    348                      SkScalarToFloat(r.width()), SkScalarToFloat(r.height()));
    349 }
    350 
    351 static GraphicsContext* scratchContext()
    352 {
    353     static ImageBuffer* scratch = 0;
    354     // TODO(benm): Confirm with reed that it's correct to use the (default) DeviceRGB ColorSpace parameter in the call to create below.
    355     if (!scratch)
    356         scratch = ImageBuffer::create(IntSize(1, 1)).release();
    357     // We don't bother checking for failure creating the ImageBuffer, since our
    358     // ImageBuffer initializer won't fail.
    359     return scratch->context();
    360 }
    361 
    362 FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier)
    363 {
    364     GraphicsContext* scratch = scratchContext();
    365     scratch->save();
    366     scratch->beginPath();
    367     scratch->addPath(*this);
    368 
    369     if (applier)
    370         applier->strokeStyle(scratch);
    371 
    372     FloatRect r = boundingBoxForCurrentStroke(scratch);
    373     scratch->restore();
    374     return r;
    375 }
    376 
    377 #if ENABLE(SVG)
    378 bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
    379 {
    380 #if 0
    381     ASSERT(applier);
    382     GraphicsContext* scratch = scratchContext();
    383     scratch->save();
    384 
    385     applier->strokeStyle(scratch);
    386 
    387     SkPaint paint;
    388     scratch->platformContext()->setupPaintForStroking(&paint, 0, 0);
    389     SkPath strokePath;
    390     paint.getFillPath(*platformPath(), &strokePath);
    391     bool contains = SkPathContainsPoint(&strokePath, point,
    392                                         SkPath::kWinding_FillType);
    393 
    394     scratch->restore();
    395     return contains;
    396 #else
    397     // FIXME:
    398     return false;
    399 #endif
    400 }
    401 #endif
    402 
    403 }
    404