gfx/2d/PathCG.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "PathCG.h"
michael@0 7 #include <math.h>
michael@0 8 #include "DrawTargetCG.h"
michael@0 9 #include "Logging.h"
michael@0 10
michael@0 11 namespace mozilla {
michael@0 12 namespace gfx {
michael@0 13
michael@0 14 PathBuilderCG::~PathBuilderCG()
michael@0 15 {
michael@0 16 CGPathRelease(mCGPath);
michael@0 17 }
michael@0 18
michael@0 19 void
michael@0 20 PathBuilderCG::MoveTo(const Point &aPoint)
michael@0 21 {
michael@0 22 CGPathMoveToPoint(mCGPath, nullptr, aPoint.x, aPoint.y);
michael@0 23 }
michael@0 24
michael@0 25 void
michael@0 26 PathBuilderCG::LineTo(const Point &aPoint)
michael@0 27 {
michael@0 28 if (CGPathIsEmpty(mCGPath))
michael@0 29 MoveTo(aPoint);
michael@0 30 else
michael@0 31 CGPathAddLineToPoint(mCGPath, nullptr, aPoint.x, aPoint.y);
michael@0 32 }
michael@0 33
michael@0 34 void
michael@0 35 PathBuilderCG::BezierTo(const Point &aCP1,
michael@0 36 const Point &aCP2,
michael@0 37 const Point &aCP3)
michael@0 38 {
michael@0 39
michael@0 40 if (CGPathIsEmpty(mCGPath))
michael@0 41 MoveTo(aCP1);
michael@0 42 CGPathAddCurveToPoint(mCGPath, nullptr,
michael@0 43 aCP1.x, aCP1.y,
michael@0 44 aCP2.x, aCP2.y,
michael@0 45 aCP3.x, aCP3.y);
michael@0 46
michael@0 47 }
michael@0 48
michael@0 49 void
michael@0 50 PathBuilderCG::QuadraticBezierTo(const Point &aCP1,
michael@0 51 const Point &aCP2)
michael@0 52 {
michael@0 53 if (CGPathIsEmpty(mCGPath))
michael@0 54 MoveTo(aCP1);
michael@0 55 CGPathAddQuadCurveToPoint(mCGPath, nullptr,
michael@0 56 aCP1.x, aCP1.y,
michael@0 57 aCP2.x, aCP2.y);
michael@0 58 }
michael@0 59
michael@0 60 void
michael@0 61 PathBuilderCG::Close()
michael@0 62 {
michael@0 63 if (!CGPathIsEmpty(mCGPath))
michael@0 64 CGPathCloseSubpath(mCGPath);
michael@0 65 }
michael@0 66
michael@0 67 void
michael@0 68 PathBuilderCG::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
michael@0 69 Float aEndAngle, bool aAntiClockwise)
michael@0 70 {
michael@0 71 // Core Graphic's initial coordinate system is y-axis up, whereas Moz2D's is
michael@0 72 // y-axis down. Core Graphics therefore considers "clockwise" to mean "sweep
michael@0 73 // in the direction of decreasing angle" whereas Moz2D considers it to mean
michael@0 74 // "sweep in the direction of increasing angle". In other words if this
michael@0 75 // Moz2D method is instructed to sweep anti-clockwise we need to tell
michael@0 76 // CGPathAddArc to sweep clockwise, and vice versa. Hence why we pass the
michael@0 77 // value of aAntiClockwise directly to CGPathAddArc's "clockwise" bool
michael@0 78 // parameter.
michael@0 79 CGPathAddArc(mCGPath, nullptr,
michael@0 80 aOrigin.x, aOrigin.y,
michael@0 81 aRadius,
michael@0 82 aStartAngle,
michael@0 83 aEndAngle,
michael@0 84 aAntiClockwise);
michael@0 85 }
michael@0 86
michael@0 87 Point
michael@0 88 PathBuilderCG::CurrentPoint() const
michael@0 89 {
michael@0 90 CGPoint pt = CGPathGetCurrentPoint(mCGPath);
michael@0 91 Point ret(pt.x, pt.y);
michael@0 92 return ret;
michael@0 93 }
michael@0 94
michael@0 95 void
michael@0 96 PathBuilderCG::EnsureActive(const Point &aPoint)
michael@0 97 {
michael@0 98 }
michael@0 99
michael@0 100 TemporaryRef<Path>
michael@0 101 PathBuilderCG::Finish()
michael@0 102 {
michael@0 103 RefPtr<PathCG> path = new PathCG(mCGPath, mFillRule);
michael@0 104 return path;
michael@0 105 }
michael@0 106
michael@0 107 TemporaryRef<PathBuilder>
michael@0 108 PathCG::CopyToBuilder(FillRule aFillRule) const
michael@0 109 {
michael@0 110 CGMutablePathRef path = CGPathCreateMutableCopy(mPath);
michael@0 111 RefPtr<PathBuilderCG> builder = new PathBuilderCG(path, aFillRule);
michael@0 112 return builder;
michael@0 113 }
michael@0 114
michael@0 115
michael@0 116
michael@0 117 TemporaryRef<PathBuilder>
michael@0 118 PathCG::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
michael@0 119 {
michael@0 120 // 10.7 adds CGPathCreateMutableCopyByTransformingPath it might be faster than doing
michael@0 121 // this by hand
michael@0 122
michael@0 123 struct TransformApplier {
michael@0 124 CGMutablePathRef path;
michael@0 125 CGAffineTransform transform;
michael@0 126 static void
michael@0 127 TranformCGPathApplierFunc(void *vinfo, const CGPathElement *element)
michael@0 128 {
michael@0 129 TransformApplier *info = reinterpret_cast<TransformApplier*>(vinfo);
michael@0 130 switch (element->type) {
michael@0 131 case kCGPathElementMoveToPoint:
michael@0 132 {
michael@0 133 CGPoint pt = element->points[0];
michael@0 134 CGPathMoveToPoint(info->path, &info->transform, pt.x, pt.y);
michael@0 135 break;
michael@0 136 }
michael@0 137 case kCGPathElementAddLineToPoint:
michael@0 138 {
michael@0 139 CGPoint pt = element->points[0];
michael@0 140 CGPathAddLineToPoint(info->path, &info->transform, pt.x, pt.y);
michael@0 141 break;
michael@0 142 }
michael@0 143 case kCGPathElementAddQuadCurveToPoint:
michael@0 144 {
michael@0 145 CGPoint cpt = element->points[0];
michael@0 146 CGPoint pt = element->points[1];
michael@0 147 CGPathAddQuadCurveToPoint(info->path, &info->transform, cpt.x, cpt.y, pt.x, pt.y);
michael@0 148 break;
michael@0 149 }
michael@0 150 case kCGPathElementAddCurveToPoint:
michael@0 151 {
michael@0 152 CGPoint cpt1 = element->points[0];
michael@0 153 CGPoint cpt2 = element->points[1];
michael@0 154 CGPoint pt = element->points[2];
michael@0 155 CGPathAddCurveToPoint(info->path, &info->transform, cpt1.x, cpt1.y, cpt2.x, cpt2.y, pt.x, pt.y);
michael@0 156 break;
michael@0 157 }
michael@0 158 case kCGPathElementCloseSubpath:
michael@0 159 {
michael@0 160 CGPathCloseSubpath(info->path);
michael@0 161 break;
michael@0 162 }
michael@0 163 }
michael@0 164 }
michael@0 165 };
michael@0 166
michael@0 167 TransformApplier ta;
michael@0 168 ta.path = CGPathCreateMutable();
michael@0 169 ta.transform = GfxMatrixToCGAffineTransform(aTransform);
michael@0 170
michael@0 171 CGPathApply(mPath, &ta, TransformApplier::TranformCGPathApplierFunc);
michael@0 172 RefPtr<PathBuilderCG> builder = new PathBuilderCG(ta.path, aFillRule);
michael@0 173 return builder;
michael@0 174 }
michael@0 175
michael@0 176 static void
michael@0 177 StreamPathToSinkApplierFunc(void *vinfo, const CGPathElement *element)
michael@0 178 {
michael@0 179 PathSink *sink = reinterpret_cast<PathSink*>(vinfo);
michael@0 180 switch (element->type) {
michael@0 181 case kCGPathElementMoveToPoint:
michael@0 182 {
michael@0 183 CGPoint pt = element->points[0];
michael@0 184 sink->MoveTo(CGPointToPoint(pt));
michael@0 185 break;
michael@0 186 }
michael@0 187 case kCGPathElementAddLineToPoint:
michael@0 188 {
michael@0 189 CGPoint pt = element->points[0];
michael@0 190 sink->LineTo(CGPointToPoint(pt));
michael@0 191 break;
michael@0 192 }
michael@0 193 case kCGPathElementAddQuadCurveToPoint:
michael@0 194 {
michael@0 195 CGPoint cpt = element->points[0];
michael@0 196 CGPoint pt = element->points[1];
michael@0 197 sink->QuadraticBezierTo(CGPointToPoint(cpt),
michael@0 198 CGPointToPoint(pt));
michael@0 199 break;
michael@0 200 }
michael@0 201 case kCGPathElementAddCurveToPoint:
michael@0 202 {
michael@0 203 CGPoint cpt1 = element->points[0];
michael@0 204 CGPoint cpt2 = element->points[1];
michael@0 205 CGPoint pt = element->points[2];
michael@0 206 sink->BezierTo(CGPointToPoint(cpt1),
michael@0 207 CGPointToPoint(cpt2),
michael@0 208 CGPointToPoint(pt));
michael@0 209 break;
michael@0 210 }
michael@0 211 case kCGPathElementCloseSubpath:
michael@0 212 {
michael@0 213 sink->Close();
michael@0 214 break;
michael@0 215 }
michael@0 216 }
michael@0 217 }
michael@0 218
michael@0 219 void
michael@0 220 PathCG::StreamToSink(PathSink *aSink) const
michael@0 221 {
michael@0 222 CGPathApply(mPath, aSink, StreamPathToSinkApplierFunc);
michael@0 223 }
michael@0 224
michael@0 225 bool
michael@0 226 PathCG::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
michael@0 227 {
michael@0 228 Matrix inverse = aTransform;
michael@0 229 inverse.Invert();
michael@0 230 Point transformedPoint = inverse*aPoint;
michael@0 231 // We could probably drop the input transform and just transform the point at the caller?
michael@0 232 CGPoint point = {transformedPoint.x, transformedPoint.y};
michael@0 233
michael@0 234 // The transform parameter of CGPathContainsPoint doesn't seem to work properly on OS X 10.5
michael@0 235 // so we transform aPoint ourselves.
michael@0 236 return CGPathContainsPoint(mPath, nullptr, point, mFillRule == FillRule::FILL_EVEN_ODD);
michael@0 237 }
michael@0 238
michael@0 239 static size_t
michael@0 240 PutBytesNull(void *info, const void *buffer, size_t count)
michael@0 241 {
michael@0 242 return count;
michael@0 243 }
michael@0 244
michael@0 245 /* The idea of a scratch context comes from WebKit */
michael@0 246 static CGContextRef
michael@0 247 CreateScratchContext()
michael@0 248 {
michael@0 249 CGDataConsumerCallbacks callbacks = {PutBytesNull, nullptr};
michael@0 250 CGDataConsumerRef consumer = CGDataConsumerCreate(nullptr, &callbacks);
michael@0 251 CGContextRef cg = CGPDFContextCreate(consumer, nullptr, nullptr);
michael@0 252 CGDataConsumerRelease(consumer);
michael@0 253 return cg;
michael@0 254 }
michael@0 255
michael@0 256 static CGContextRef
michael@0 257 ScratchContext()
michael@0 258 {
michael@0 259 static CGContextRef cg = CreateScratchContext();
michael@0 260 return cg;
michael@0 261 }
michael@0 262
michael@0 263 bool
michael@0 264 PathCG::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
michael@0 265 const Point &aPoint,
michael@0 266 const Matrix &aTransform) const
michael@0 267 {
michael@0 268 Matrix inverse = aTransform;
michael@0 269 inverse.Invert();
michael@0 270 Point transformedPoint = inverse*aPoint;
michael@0 271 // We could probably drop the input transform and just transform the point at the caller?
michael@0 272 CGPoint point = {transformedPoint.x, transformedPoint.y};
michael@0 273
michael@0 274 CGContextRef cg = ScratchContext();
michael@0 275
michael@0 276 CGContextSaveGState(cg);
michael@0 277
michael@0 278 CGContextBeginPath(cg);
michael@0 279 CGContextAddPath(cg, mPath);
michael@0 280
michael@0 281 SetStrokeOptions(cg, aStrokeOptions);
michael@0 282
michael@0 283 CGContextReplacePathWithStrokedPath(cg);
michael@0 284 CGContextRestoreGState(cg);
michael@0 285
michael@0 286 CGPathRef sPath = CGContextCopyPath(cg);
michael@0 287 bool inStroke = CGPathContainsPoint(sPath, nullptr, point, false);
michael@0 288 CGPathRelease(sPath);
michael@0 289
michael@0 290 return inStroke;
michael@0 291 }
michael@0 292
michael@0 293 //XXX: what should these functions return for an empty path?
michael@0 294 // currently they return CGRectNull {inf,inf, 0, 0}
michael@0 295 Rect
michael@0 296 PathCG::GetBounds(const Matrix &aTransform) const
michael@0 297 {
michael@0 298 //XXX: are these bounds tight enough
michael@0 299 Rect bounds = CGRectToRect(CGPathGetBoundingBox(mPath));
michael@0 300
michael@0 301 //XXX: currently this returns the bounds of the transformed bounds
michael@0 302 // this is strictly looser than the bounds of the transformed path
michael@0 303 return aTransform.TransformBounds(bounds);
michael@0 304 }
michael@0 305
michael@0 306 Rect
michael@0 307 PathCG::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
michael@0 308 const Matrix &aTransform) const
michael@0 309 {
michael@0 310 // 10.7 has CGPathCreateCopyByStrokingPath which we could use
michael@0 311 // instead of this scratch context business
michael@0 312 CGContextRef cg = ScratchContext();
michael@0 313
michael@0 314 CGContextSaveGState(cg);
michael@0 315
michael@0 316 CGContextBeginPath(cg);
michael@0 317 CGContextAddPath(cg, mPath);
michael@0 318
michael@0 319 SetStrokeOptions(cg, aStrokeOptions);
michael@0 320
michael@0 321 CGContextReplacePathWithStrokedPath(cg);
michael@0 322 Rect bounds = CGRectToRect(CGContextGetPathBoundingBox(cg));
michael@0 323
michael@0 324 CGContextRestoreGState(cg);
michael@0 325
michael@0 326 if (!bounds.IsFinite()) {
michael@0 327 return Rect();
michael@0 328 }
michael@0 329
michael@0 330 return aTransform.TransformBounds(bounds);
michael@0 331 }
michael@0 332
michael@0 333
michael@0 334 }
michael@0 335
michael@0 336 }

mercurial