1/* 2 * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. 3 * 2006, 2008 Rob Buis <buis@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 42namespace WebCore { 43 44static size_t putBytesNowhere(void*, const void*, size_t count) 45{ 46 return count; 47} 48 49static 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 62static inline CGContextRef scratchContext() 63{ 64 static CGContextRef context = createScratchContext(); 65 return context; 66} 67 68Path::Path() 69 : m_path(CGPathCreateMutable()) 70{ 71} 72 73Path::~Path() 74{ 75 CGPathRelease(m_path); 76} 77 78Path::Path(const Path& other) 79 : m_path(CGPathCreateMutableCopy(other.m_path)) 80{ 81} 82 83Path& 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 91static 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 117static CGMutablePathRef copyCGPathClosingSubpaths(CGPathRef originalPath) 118{ 119 CGMutablePathRef path = CGPathCreateMutable(); 120 CGPathApply(originalPath, path, copyClosingSubpathsApplierFunction); 121 CGPathCloseSubpath(path); 122 return path; 123} 124 125bool 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 136bool 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 155void 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 164FloatRect Path::boundingRect() const 165{ 166 return CGPathGetBoundingBox(m_path); 167} 168 169FloatRect 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 189void Path::moveTo(const FloatPoint& point) 190{ 191 CGPathMoveToPoint(m_path, 0, point.x(), point.y()); 192} 193 194void Path::addLineTo(const FloatPoint& p) 195{ 196 CGPathAddLineToPoint(m_path, 0, p.x(), p.y()); 197} 198 199void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& p) 200{ 201 CGPathAddQuadCurveToPoint(m_path, 0, cp.x(), cp.y(), p.x(), p.y()); 202} 203 204void 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 209void 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 214void Path::closeSubpath() 215{ 216 CGPathCloseSubpath(m_path); 217} 218 219void 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 226void Path::addRect(const FloatRect& r) 227{ 228 CGPathAddRect(m_path, 0, r); 229} 230 231void Path::addEllipse(const FloatRect& r) 232{ 233 CGPathAddEllipseInRect(m_path, 0, r); 234} 235 236void Path::clear() 237{ 238 CGPathRelease(m_path); 239 m_path = CGPathCreateMutable(); 240} 241 242bool Path::isEmpty() const 243{ 244 return CGPathIsEmpty(m_path); 245} 246 247bool Path::hasCurrentPoint() const 248{ 249 return !isEmpty(); 250} 251 252FloatPoint Path::currentPoint() const 253{ 254 return CGPathGetCurrentPoint(m_path); 255} 256 257// MARK: - 258// MARK: Path Management 259 260struct PathApplierInfo { 261 void* info; 262 PathApplierFunction function; 263}; 264 265static 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 293void 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 301void 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