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