Home | History | Annotate | Download | only in cg
      1 /*
      2  * Copyright (C) 2003, 2006 Apple Computer, Inc.  All rights reserved.
      3  *                     2006, 2008 Rob Buis <buis (at) kde.org>
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "config.h"
     28 #include "Path.h"
     29 
     30 #if USE(CG)
     31 
     32 #include "AffineTransform.h"
     33 #include "FloatRect.h"
     34 #include "GraphicsContext.h"
     35 #include "IntRect.h"
     36 #include "PlatformString.h"
     37 #include "StrokeStyleApplier.h"
     38 #include <ApplicationServices/ApplicationServices.h>
     39 #include <wtf/MathExtras.h>
     40 #include <wtf/RetainPtr.h>
     41 
     42 namespace WebCore {
     43 
     44 static size_t putBytesNowhere(void*, const void*, size_t count)
     45 {
     46     return count;
     47 }
     48 
     49 static CGContextRef createScratchContext()
     50 {
     51     CGDataConsumerCallbacks callbacks = { putBytesNowhere, 0 };
     52     RetainPtr<CGDataConsumerRef> consumer(AdoptCF, CGDataConsumerCreate(0, &callbacks));
     53     CGContextRef context = CGPDFContextCreate(consumer.get(), 0, 0);
     54 
     55     CGFloat black[4] = { 0, 0, 0, 1 };
     56     CGContextSetFillColor(context, black);
     57     CGContextSetStrokeColor(context, black);
     58 
     59     return context;
     60 }
     61 
     62 static inline CGContextRef scratchContext()
     63 {
     64     static CGContextRef context = createScratchContext();
     65     return context;
     66 }
     67 
     68 Path::Path()
     69     : m_path(CGPathCreateMutable())
     70 {
     71 }
     72 
     73 Path::~Path()
     74 {
     75     CGPathRelease(m_path);
     76 }
     77 
     78 Path::Path(const Path& other)
     79     : m_path(CGPathCreateMutableCopy(other.m_path))
     80 {
     81 }
     82 
     83 Path& Path::operator=(const Path& other)
     84 {
     85     CGMutablePathRef path = CGPathCreateMutableCopy(other.m_path);
     86     CGPathRelease(m_path);
     87     m_path = path;
     88     return *this;
     89 }
     90 
     91 static void copyClosingSubpathsApplierFunction(void* info, const CGPathElement* element)
     92 {
     93     CGMutablePathRef path = static_cast<CGMutablePathRef>(info);
     94     CGPoint* points = element->points;
     95 
     96     switch (element->type) {
     97     case kCGPathElementMoveToPoint:
     98         if (!CGPathIsEmpty(path)) // to silence a warning when trying to close an empty path
     99             CGPathCloseSubpath(path); // This is the only change from CGPathCreateMutableCopy
    100         CGPathMoveToPoint(path, 0, points[0].x, points[0].y);
    101         break;
    102     case kCGPathElementAddLineToPoint:
    103         CGPathAddLineToPoint(path, 0, points[0].x, points[0].y);
    104         break;
    105     case kCGPathElementAddQuadCurveToPoint:
    106         CGPathAddQuadCurveToPoint(path, 0, points[0].x, points[0].y, points[1].x, points[1].y);
    107         break;
    108     case kCGPathElementAddCurveToPoint:
    109         CGPathAddCurveToPoint(path, 0, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
    110         break;
    111     case kCGPathElementCloseSubpath:
    112         CGPathCloseSubpath(path);
    113         break;
    114     }
    115 }
    116 
    117 static CGMutablePathRef copyCGPathClosingSubpaths(CGPathRef originalPath)
    118 {
    119     CGMutablePathRef path = CGPathCreateMutable();
    120     CGPathApply(originalPath, path, copyClosingSubpathsApplierFunction);
    121     CGPathCloseSubpath(path);
    122     return path;
    123 }
    124 
    125 bool Path::contains(const FloatPoint &point, WindRule rule) const
    126 {
    127     if (!boundingRect().contains(point))
    128         return false;
    129 
    130     // CGPathContainsPoint returns false for non-closed paths, as a work-around, we copy and close the path first.  Radar 4758998 asks for a better CG API to use
    131     RetainPtr<CGMutablePathRef> path(AdoptCF, copyCGPathClosingSubpaths(m_path));
    132     bool ret = CGPathContainsPoint(path.get(), 0, point, rule == RULE_EVENODD ? true : false);
    133     return ret;
    134 }
    135 
    136 bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
    137 {
    138     ASSERT(applier);
    139 
    140     CGContextRef context = scratchContext();
    141 
    142     CGContextSaveGState(context);
    143     CGContextBeginPath(context);
    144     CGContextAddPath(context, platformPath());
    145 
    146     GraphicsContext gc(context);
    147     applier->strokeStyle(&gc);
    148 
    149     bool hitSuccess = CGContextPathContainsPoint(context, point, kCGPathStroke);
    150     CGContextRestoreGState(context);
    151 
    152     return hitSuccess;
    153 }
    154 
    155 void Path::translate(const FloatSize& size)
    156 {
    157     CGAffineTransform translation = CGAffineTransformMake(1, 0, 0, 1, size.width(), size.height());
    158     CGMutablePathRef newPath = CGPathCreateMutable();
    159     CGPathAddPath(newPath, &translation, m_path);
    160     CGPathRelease(m_path);
    161     m_path = newPath;
    162 }
    163 
    164 FloatRect Path::boundingRect() const
    165 {
    166     return CGPathGetBoundingBox(m_path);
    167 }
    168 
    169 FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) const
    170 {
    171     CGContextRef context = scratchContext();
    172 
    173     CGContextSaveGState(context);
    174     CGContextBeginPath(context);
    175     CGContextAddPath(context, platformPath());
    176 
    177     if (applier) {
    178         GraphicsContext graphicsContext(context);
    179         applier->strokeStyle(&graphicsContext);
    180     }
    181 
    182     CGContextReplacePathWithStrokedPath(context);
    183     CGRect box = CGContextIsPathEmpty(context) ? CGRectZero : CGContextGetPathBoundingBox(context);
    184     CGContextRestoreGState(context);
    185 
    186     return box;
    187 }
    188 
    189 void Path::moveTo(const FloatPoint& point)
    190 {
    191     CGPathMoveToPoint(m_path, 0, point.x(), point.y());
    192 }
    193 
    194 void Path::addLineTo(const FloatPoint& p)
    195 {
    196     CGPathAddLineToPoint(m_path, 0, p.x(), p.y());
    197 }
    198 
    199 void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& p)
    200 {
    201     CGPathAddQuadCurveToPoint(m_path, 0, cp.x(), cp.y(), p.x(), p.y());
    202 }
    203 
    204 void Path::addBezierCurveTo(const FloatPoint& cp1, const FloatPoint& cp2, const FloatPoint& p)
    205 {
    206     CGPathAddCurveToPoint(m_path, 0, cp1.x(), cp1.y(), cp2.x(), cp2.y(), p.x(), p.y());
    207 }
    208 
    209 void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
    210 {
    211     CGPathAddArcToPoint(m_path, 0, p1.x(), p1.y(), p2.x(), p2.y(), radius);
    212 }
    213 
    214 void Path::closeSubpath()
    215 {
    216     CGPathCloseSubpath(m_path);
    217 }
    218 
    219 void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool clockwise)
    220 {
    221     // Workaround for <rdar://problem/5189233> CGPathAddArc hangs or crashes when passed inf as start or end angle
    222     if (isfinite(sa) && isfinite(ea))
    223         CGPathAddArc(m_path, 0, p.x(), p.y(), r, sa, ea, clockwise);
    224 }
    225 
    226 void Path::addRect(const FloatRect& r)
    227 {
    228     CGPathAddRect(m_path, 0, r);
    229 }
    230 
    231 void Path::addEllipse(const FloatRect& r)
    232 {
    233     CGPathAddEllipseInRect(m_path, 0, r);
    234 }
    235 
    236 void Path::clear()
    237 {
    238     CGPathRelease(m_path);
    239     m_path = CGPathCreateMutable();
    240 }
    241 
    242 bool Path::isEmpty() const
    243 {
    244     return CGPathIsEmpty(m_path);
    245 }
    246 
    247 bool Path::hasCurrentPoint() const
    248 {
    249     return !isEmpty();
    250 }
    251 
    252 FloatPoint Path::currentPoint() const
    253 {
    254     return CGPathGetCurrentPoint(m_path);
    255 }
    256 
    257 // MARK: -
    258 // MARK: Path Management
    259 
    260 struct PathApplierInfo {
    261     void* info;
    262     PathApplierFunction function;
    263 };
    264 
    265 static void CGPathApplierToPathApplier(void *info, const CGPathElement *element)
    266 {
    267     PathApplierInfo* pinfo = (PathApplierInfo*)info;
    268     FloatPoint points[3];
    269     PathElement pelement;
    270     pelement.type = (PathElementType)element->type;
    271     pelement.points = points;
    272     CGPoint* cgPoints = element->points;
    273     switch (element->type) {
    274     case kCGPathElementMoveToPoint:
    275     case kCGPathElementAddLineToPoint:
    276         points[0] = cgPoints[0];
    277         break;
    278     case kCGPathElementAddQuadCurveToPoint:
    279         points[0] = cgPoints[0];
    280         points[1] = cgPoints[1];
    281         break;
    282     case kCGPathElementAddCurveToPoint:
    283         points[0] = cgPoints[0];
    284         points[1] = cgPoints[1];
    285         points[2] = cgPoints[2];
    286         break;
    287     case kCGPathElementCloseSubpath:
    288         break;
    289     }
    290     pinfo->function(pinfo->info, &pelement);
    291 }
    292 
    293 void Path::apply(void* info, PathApplierFunction function) const
    294 {
    295     PathApplierInfo pinfo;
    296     pinfo.info = info;
    297     pinfo.function = function;
    298     CGPathApply(m_path, &pinfo, CGPathApplierToPathApplier);
    299 }
    300 
    301 void Path::transform(const AffineTransform& transform)
    302 {
    303     CGMutablePathRef path = CGPathCreateMutable();
    304     CGAffineTransform transformCG = transform;
    305     CGPathAddPath(path, &transformCG, m_path);
    306     CGPathRelease(m_path);
    307     m_path = path;
    308 }
    309 
    310 }
    311 
    312 #endif // USE(CG)
    313