OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. | 2 * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. |
3 * 2006, 2008 Rob Buis <buis@kde.org> | 3 * 2006, 2008 Rob Buis <buis@kde.org> |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 23 matching lines...) Expand all Loading... |
34 #include "GraphicsContext.h" | 34 #include "GraphicsContext.h" |
35 #include "IntRect.h" | 35 #include "IntRect.h" |
36 #include "PlatformString.h" | 36 #include "PlatformString.h" |
37 #include "StrokeStyleApplier.h" | 37 #include "StrokeStyleApplier.h" |
38 #include <ApplicationServices/ApplicationServices.h> | 38 #include <ApplicationServices/ApplicationServices.h> |
39 #include <wtf/MathExtras.h> | 39 #include <wtf/MathExtras.h> |
40 #include <wtf/RetainPtr.h> | 40 #include <wtf/RetainPtr.h> |
41 | 41 |
42 namespace WebCore { | 42 namespace WebCore { |
43 | 43 |
| 44 // A class to provide an isEmpty test that considers a one-element path with onl
y a MoveTo element |
| 45 // to be empty. This behavior is consistent with other platforms in WebKit, and
is needed to prevent |
| 46 // incorrect (according to the spec) linecap stroking for zero length paths in S
VG. |
| 47 class PathIsEmptyOrSingleMoveTester { |
| 48 public: |
| 49 PathIsEmptyOrSingleMoveTester() : m_moveCount(0) { } |
| 50 |
| 51 bool isEmpty() const |
| 52 { |
| 53 return m_moveCount <= 1; |
| 54 } |
| 55 |
| 56 static void testPathElement(void* info, const CGPathElement* element) |
| 57 { |
| 58 PathIsEmptyOrSingleMoveTester* tester = static_cast<PathIsEmptyOrSingleM
oveTester*>(info); |
| 59 if (element->type == kCGPathElementMoveToPoint) |
| 60 ++tester->m_moveCount; |
| 61 else { |
| 62 // Any non move element implies a non-empty path; set the count to 2
to force |
| 63 // isEmpty to return false. |
| 64 tester->m_moveCount = 2; |
| 65 } |
| 66 } |
| 67 |
| 68 private: |
| 69 // Any non-move-to element, or more than one move-to element, will make the
count >= 2. |
| 70 unsigned m_moveCount; |
| 71 }; |
| 72 |
| 73 // Paths with only move-to elements do not draw under any circumstances, so thei
r bound should |
| 74 // be empty. Currently, CoreGraphics returns non-empty bounds for such paths. Ra
dar 10450621 |
| 75 // tracks this. This class reports paths that have only move-to elements, allowi
ng the |
| 76 // bounding box code to work around the CoreGraphics problem. |
| 77 class PathHasOnlyMoveToTester { |
| 78 public: |
| 79 PathHasOnlyMoveToTester() : m_hasSeenOnlyMoveTo(true) { } |
| 80 |
| 81 bool hasOnlyMoveTo() const |
| 82 { |
| 83 return m_hasSeenOnlyMoveTo; |
| 84 } |
| 85 |
| 86 static void testPathElement(void* info, const CGPathElement* element) |
| 87 { |
| 88 PathHasOnlyMoveToTester* tester = static_cast<PathHasOnlyMoveToTester*>(
info); |
| 89 if (tester->m_hasSeenOnlyMoveTo && element->type != kCGPathElementMoveTo
Point) |
| 90 tester->m_hasSeenOnlyMoveTo = false; |
| 91 } |
| 92 |
| 93 private: |
| 94 bool m_hasSeenOnlyMoveTo; |
| 95 }; |
| 96 |
44 static size_t putBytesNowhere(void*, const void*, size_t count) | 97 static size_t putBytesNowhere(void*, const void*, size_t count) |
45 { | 98 { |
46 return count; | 99 return count; |
47 } | 100 } |
48 | 101 |
49 static CGContextRef createScratchContext() | 102 static CGContextRef createScratchContext() |
50 { | 103 { |
51 CGDataConsumerCallbacks callbacks = { putBytesNowhere, 0 }; | 104 CGDataConsumerCallbacks callbacks = { putBytesNowhere, 0 }; |
52 RetainPtr<CGDataConsumerRef> consumer(AdoptCF, CGDataConsumerCreate(0, &call
backs)); | 105 RetainPtr<CGDataConsumerRef> consumer(AdoptCF, CGDataConsumerCreate(0, &call
backs)); |
53 CGContextRef context = CGPDFContextCreate(consumer.get(), 0, 0); | 106 CGContextRef context = CGPDFContextCreate(consumer.get(), 0, 0); |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
158 CGMutablePathRef newPath = CGPathCreateMutable(); | 211 CGMutablePathRef newPath = CGPathCreateMutable(); |
159 CGPathAddPath(newPath, &translation, m_path); | 212 CGPathAddPath(newPath, &translation, m_path); |
160 CGPathRelease(m_path); | 213 CGPathRelease(m_path); |
161 m_path = newPath; | 214 m_path = newPath; |
162 } | 215 } |
163 | 216 |
164 FloatRect Path::boundingRect() const | 217 FloatRect Path::boundingRect() const |
165 { | 218 { |
166 // CGPathGetBoundingBox includes the path's control points, CGPathGetPathBou
ndingBox | 219 // CGPathGetBoundingBox includes the path's control points, CGPathGetPathBou
ndingBox |
167 // does not, but only exists on 10.6 and above. | 220 // does not, but only exists on 10.6 and above. |
| 221 // A bug in CoreGraphics leads to an incorrect bound on paths containing onl
y move-to elements |
| 222 // with a linecap style that is non-butt. All paths with only move-to elemen
ts (regardless of |
| 223 // linecap) are effectively empty for bounding purposes and here we make it
so. |
| 224 PathHasOnlyMoveToTester tester; |
| 225 CGPathApply(m_path, &tester, PathHasOnlyMoveToTester::testPathElement); |
| 226 if (tester.hasOnlyMoveTo()) |
| 227 return FloatRect(0, 0, 0, 0); |
| 228 |
168 #if !defined(BUILDING_ON_LEOPARD) | 229 #if !defined(BUILDING_ON_LEOPARD) |
169 return CGPathGetPathBoundingBox(m_path); | 230 return CGPathGetPathBoundingBox(m_path); |
170 #else | 231 #else |
171 return CGPathGetBoundingBox(m_path); | 232 return CGPathGetBoundingBox(m_path); |
172 #endif | 233 #endif |
173 } | 234 } |
174 | 235 |
175 FloatRect Path::fastBoundingRect() const | 236 FloatRect Path::fastBoundingRect() const |
176 { | 237 { |
| 238 // A bug in CoreGraphics leads to an incorrect bound on paths containing onl
y move-to elements |
| 239 // with a linecap style that is non-butt. All paths with only move-to elemen
ts (regardless of |
| 240 // linecap) are effectively empty for bounding purposes and here we make it
so. |
| 241 PathHasOnlyMoveToTester tester; |
| 242 CGPathApply(m_path, &tester, PathHasOnlyMoveToTester::testPathElement); |
| 243 if (tester.hasOnlyMoveTo()) |
| 244 return FloatRect(0, 0, 0, 0); |
| 245 |
177 return CGPathGetBoundingBox(m_path); | 246 return CGPathGetBoundingBox(m_path); |
178 } | 247 } |
179 | 248 |
180 FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) const | 249 FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) const |
181 { | 250 { |
182 CGContextRef context = scratchContext(); | 251 CGContextRef context = scratchContext(); |
183 | 252 |
184 CGContextSaveGState(context); | 253 CGContextSaveGState(context); |
185 CGContextBeginPath(context); | 254 CGContextBeginPath(context); |
186 CGContextAddPath(context, platformPath()); | 255 CGContextAddPath(context, platformPath()); |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
245 } | 314 } |
246 | 315 |
247 void Path::clear() | 316 void Path::clear() |
248 { | 317 { |
249 CGPathRelease(m_path); | 318 CGPathRelease(m_path); |
250 m_path = CGPathCreateMutable(); | 319 m_path = CGPathCreateMutable(); |
251 } | 320 } |
252 | 321 |
253 bool Path::isEmpty() const | 322 bool Path::isEmpty() const |
254 { | 323 { |
255 return CGPathIsEmpty(m_path); | 324 // The SVG rendering code that uses this method relies on paths with a singl
e move-to |
| 325 // element, and nothing else, as being empty. Until that code is refactored
to avoid |
| 326 // the dependence on isEmpty, we match the behavior of other platforms. |
| 327 // When the SVG code is refactored, we could use CGPathIsEmpty(m_path); |
| 328 PathIsEmptyOrSingleMoveTester tester; |
| 329 CGPathApply(m_path, &tester, PathIsEmptyOrSingleMoveTester::testPathElement)
; |
| 330 return tester.isEmpty(); |
256 } | 331 } |
257 | 332 |
258 bool Path::hasCurrentPoint() const | 333 bool Path::hasCurrentPoint() const |
259 { | 334 { |
260 return !isEmpty(); | 335 return !CGPathIsEmpty(m_path); |
261 } | 336 } |
262 | 337 |
263 FloatPoint Path::currentPoint() const | 338 FloatPoint Path::currentPoint() const |
264 { | 339 { |
265 return CGPathGetCurrentPoint(m_path); | 340 return CGPathGetCurrentPoint(m_path); |
266 } | 341 } |
267 | 342 |
268 // MARK: - | 343 // MARK: - |
269 // MARK: Path Management | 344 // MARK: Path Management |
270 | 345 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
304 void Path::apply(void* info, PathApplierFunction function) const | 379 void Path::apply(void* info, PathApplierFunction function) const |
305 { | 380 { |
306 PathApplierInfo pinfo; | 381 PathApplierInfo pinfo; |
307 pinfo.info = info; | 382 pinfo.info = info; |
308 pinfo.function = function; | 383 pinfo.function = function; |
309 CGPathApply(m_path, &pinfo, CGPathApplierToPathApplier); | 384 CGPathApply(m_path, &pinfo, CGPathApplierToPathApplier); |
310 } | 385 } |
311 | 386 |
312 void Path::transform(const AffineTransform& transform) | 387 void Path::transform(const AffineTransform& transform) |
313 { | 388 { |
314 if (transform.isIdentity() || isEmpty()) | 389 if (transform.isIdentity() || CGPathIsEmpty(m_path)) |
315 return; | 390 return; |
316 | 391 |
317 CGMutablePathRef path = CGPathCreateMutable(); | 392 CGMutablePathRef path = CGPathCreateMutable(); |
318 CGAffineTransform transformCG = transform; | 393 CGAffineTransform transformCG = transform; |
319 CGPathAddPath(path, &transformCG, m_path); | 394 CGPathAddPath(path, &transformCG, m_path); |
320 CGPathRelease(m_path); | 395 CGPathRelease(m_path); |
321 m_path = path; | 396 m_path = path; |
322 } | 397 } |
323 | 398 |
324 } | 399 } |
325 | 400 |
326 #endif // USE(CG) | 401 #endif // USE(CG) |
OLD | NEW |