PathRecording.cpp (14652B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "PathRecording.h" 8 #include "DrawEventRecorder.h" 9 #include "RecordedEventImpl.h" 10 11 namespace mozilla { 12 namespace gfx { 13 14 inline Maybe<float> PathOps::ArcParams::GetRadius() const { 15 // Do a quick check for a uniform scale and/or translation transform. In the 16 // worst case scenario, failing just causes a fallback to ArcToBezier. 17 if (transform._11 == transform._22 && transform._12 == 0.0f && 18 transform._21 == 0.0f && transform._11 > 0.0f) { 19 return Some(transform._11); 20 } 21 return Nothing(); 22 } 23 24 inline void PathOps::ArcParams::ToSink(PathSink& aPathSink, 25 bool aAntiClockwise) const { 26 if (Maybe<float> radius = GetRadius()) { 27 aPathSink.Arc(GetOrigin(), *radius, startAngle, endAngle, aAntiClockwise); 28 } else { 29 ArcToBezier(&aPathSink, Point(), Size(1.0f, 1.0f), startAngle, endAngle, 30 aAntiClockwise, 0.0f, transform); 31 } 32 } 33 34 #define NEXT_PARAMS(_type) \ 35 const _type params = *reinterpret_cast<const _type*>(nextByte); \ 36 nextByte += sizeof(_type); 37 38 bool PathOps::StreamToSink(PathSink& aPathSink) const { 39 if (mPathData.empty()) { 40 return true; 41 } 42 43 const uint8_t* nextByte = mPathData.data(); 44 const uint8_t* end = nextByte + mPathData.size(); 45 while (nextByte < end) { 46 const OpType opType = *reinterpret_cast<const OpType*>(nextByte); 47 nextByte += sizeof(OpType); 48 switch (opType) { 49 case OpType::OP_MOVETO: { 50 NEXT_PARAMS(Point) 51 aPathSink.MoveTo(params); 52 break; 53 } 54 case OpType::OP_LINETO: { 55 NEXT_PARAMS(Point) 56 aPathSink.LineTo(params); 57 break; 58 } 59 case OpType::OP_BEZIERTO: { 60 NEXT_PARAMS(ThreePoints) 61 aPathSink.BezierTo(params.p1, params.p2, params.p3); 62 break; 63 } 64 case OpType::OP_QUADRATICBEZIERTO: { 65 NEXT_PARAMS(TwoPoints) 66 aPathSink.QuadraticBezierTo(params.p1, params.p2); 67 break; 68 } 69 case OpType::OP_ARC_CW: 70 case OpType::OP_ARC_CCW: { 71 NEXT_PARAMS(ArcParams) 72 params.ToSink(aPathSink, opType == OpType::OP_ARC_CCW); 73 break; 74 } 75 case OpType::OP_CLOSE: 76 aPathSink.Close(); 77 break; 78 default: 79 return false; 80 } 81 } 82 83 return true; 84 } 85 86 #define CHECKED_NEXT_PARAMS(_type) \ 87 if (nextByte + sizeof(_type) > end) { \ 88 return false; \ 89 } \ 90 NEXT_PARAMS(_type) 91 92 bool PathOps::CheckedStreamToSink(PathSink& aPathSink) const { 93 if (mPathData.empty()) { 94 return true; 95 } 96 97 const uint8_t* nextByte = mPathData.data(); 98 const uint8_t* end = nextByte + mPathData.size(); 99 while (true) { 100 if (nextByte == end) { 101 break; 102 } 103 104 if (nextByte + sizeof(OpType) > end) { 105 return false; 106 } 107 108 const OpType opType = *reinterpret_cast<const OpType*>(nextByte); 109 nextByte += sizeof(OpType); 110 switch (opType) { 111 case OpType::OP_MOVETO: { 112 CHECKED_NEXT_PARAMS(Point) 113 aPathSink.MoveTo(params); 114 break; 115 } 116 case OpType::OP_LINETO: { 117 CHECKED_NEXT_PARAMS(Point) 118 aPathSink.LineTo(params); 119 break; 120 } 121 case OpType::OP_BEZIERTO: { 122 CHECKED_NEXT_PARAMS(ThreePoints) 123 aPathSink.BezierTo(params.p1, params.p2, params.p3); 124 break; 125 } 126 case OpType::OP_QUADRATICBEZIERTO: { 127 CHECKED_NEXT_PARAMS(TwoPoints) 128 aPathSink.QuadraticBezierTo(params.p1, params.p2); 129 break; 130 } 131 case OpType::OP_ARC_CW: 132 case OpType::OP_ARC_CCW: { 133 CHECKED_NEXT_PARAMS(ArcParams) 134 params.ToSink(aPathSink, opType == OpType::OP_ARC_CCW); 135 break; 136 } 137 case OpType::OP_CLOSE: 138 aPathSink.Close(); 139 break; 140 default: 141 return false; 142 } 143 } 144 145 return true; 146 } 147 #undef CHECKED_NEXT_PARAMS 148 149 PathOps PathOps::TransformedCopy(const Matrix& aTransform) const { 150 PathOps newPathOps; 151 newPathOps.mPathData.reserve(mPathData.size()); 152 const uint8_t* nextByte = mPathData.data(); 153 const uint8_t* end = nextByte + mPathData.size(); 154 while (nextByte < end) { 155 const OpType opType = *reinterpret_cast<const OpType*>(nextByte); 156 nextByte += sizeof(OpType); 157 switch (opType) { 158 case OpType::OP_MOVETO: { 159 NEXT_PARAMS(Point) 160 newPathOps.MoveTo(aTransform.TransformPoint(params)); 161 break; 162 } 163 case OpType::OP_LINETO: { 164 NEXT_PARAMS(Point) 165 newPathOps.LineTo(aTransform.TransformPoint(params)); 166 break; 167 } 168 case OpType::OP_BEZIERTO: { 169 NEXT_PARAMS(ThreePoints) 170 newPathOps.BezierTo(aTransform.TransformPoint(params.p1), 171 aTransform.TransformPoint(params.p2), 172 aTransform.TransformPoint(params.p3)); 173 break; 174 } 175 case OpType::OP_QUADRATICBEZIERTO: { 176 NEXT_PARAMS(TwoPoints) 177 newPathOps.QuadraticBezierTo(aTransform.TransformPoint(params.p1), 178 aTransform.TransformPoint(params.p2)); 179 break; 180 } 181 case OpType::OP_ARC_CW: 182 case OpType::OP_ARC_CCW: { 183 NEXT_PARAMS(ArcParams) 184 newPathOps.Arc(params.transform * aTransform, params.startAngle, 185 params.endAngle, opType == OpType::OP_ARC_CCW); 186 break; 187 } 188 case OpType::OP_CLOSE: 189 newPathOps.Close(); 190 break; 191 default: 192 MOZ_CRASH("We control mOpTypes, so this should never happen."); 193 } 194 } 195 196 return newPathOps; 197 } 198 199 #define MODIFY_NEXT_PARAMS(_type) \ 200 _type& params = *reinterpret_cast<_type*>(nextByte); \ 201 nextByte += sizeof(_type); 202 203 void PathOps::TransformInPlace(const Matrix& aTransform) { 204 uint8_t* nextByte = mPathData.data(); 205 uint8_t* end = nextByte + mPathData.size(); 206 while (nextByte < end) { 207 const OpType opType = *reinterpret_cast<const OpType*>(nextByte); 208 nextByte += sizeof(OpType); 209 switch (opType) { 210 case OpType::OP_MOVETO: { 211 MODIFY_NEXT_PARAMS(Point) 212 params = aTransform.TransformPoint(params); 213 break; 214 } 215 case OpType::OP_LINETO: { 216 MODIFY_NEXT_PARAMS(Point) 217 params = aTransform.TransformPoint(params); 218 break; 219 } 220 case OpType::OP_BEZIERTO: { 221 MODIFY_NEXT_PARAMS(ThreePoints) 222 params.p1 = aTransform.TransformPoint(params.p1); 223 params.p2 = aTransform.TransformPoint(params.p2); 224 params.p3 = aTransform.TransformPoint(params.p3); 225 break; 226 } 227 case OpType::OP_QUADRATICBEZIERTO: { 228 MODIFY_NEXT_PARAMS(TwoPoints) 229 params.p1 = aTransform.TransformPoint(params.p1); 230 params.p2 = aTransform.TransformPoint(params.p2); 231 break; 232 } 233 case OpType::OP_ARC_CW: 234 case OpType::OP_ARC_CCW: { 235 MODIFY_NEXT_PARAMS(ArcParams) 236 params.transform *= aTransform; 237 break; 238 } 239 case OpType::OP_CLOSE: 240 break; 241 default: 242 MOZ_CRASH("We control mOpTypes, so this should never happen."); 243 } 244 } 245 } 246 247 Maybe<Circle> PathOps::AsCircle() const { 248 if (mPathData.empty()) { 249 return Nothing(); 250 } 251 252 const uint8_t* nextByte = mPathData.data(); 253 const uint8_t* end = nextByte + mPathData.size(); 254 const OpType opType = *reinterpret_cast<const OpType*>(nextByte); 255 nextByte += sizeof(OpType); 256 if (opType == OpType::OP_ARC_CW || opType == OpType::OP_ARC_CCW) { 257 NEXT_PARAMS(ArcParams) 258 if (fabs(fabs(params.startAngle - params.endAngle) - 2 * M_PI) < 1e-6) { 259 if (Maybe<float> radius = params.GetRadius()) { 260 // we have a full circle 261 if (nextByte < end) { 262 const OpType nextOpType = *reinterpret_cast<const OpType*>(nextByte); 263 nextByte += sizeof(OpType); 264 if (nextOpType == OpType::OP_CLOSE) { 265 if (nextByte == end) { 266 return Some(Circle{params.GetOrigin(), *radius, true}); 267 } 268 } 269 } else { 270 // the circle wasn't closed 271 return Some(Circle{params.GetOrigin(), *radius, false}); 272 } 273 } 274 } 275 } 276 277 return Nothing(); 278 } 279 280 Maybe<Line> PathOps::AsLine() const { 281 if (mPathData.empty()) { 282 return Nothing(); 283 } 284 285 Line retval; 286 287 const uint8_t* nextByte = mPathData.data(); 288 const uint8_t* end = nextByte + mPathData.size(); 289 OpType opType = *reinterpret_cast<const OpType*>(nextByte); 290 nextByte += sizeof(OpType); 291 292 if (opType == OpType::OP_MOVETO) { 293 MOZ_ASSERT(nextByte != end); 294 295 NEXT_PARAMS(Point) 296 retval.origin = params; 297 } else { 298 return Nothing(); 299 } 300 301 if (nextByte >= end) { 302 return Nothing(); 303 } 304 305 opType = *reinterpret_cast<const OpType*>(nextByte); 306 nextByte += sizeof(OpType); 307 308 if (opType == OpType::OP_LINETO) { 309 MOZ_ASSERT(nextByte != end); 310 311 NEXT_PARAMS(Point) 312 313 if (nextByte == end) { 314 retval.destination = params; 315 return Some(retval); 316 } 317 } 318 319 return Nothing(); 320 } 321 #undef NEXT_PARAMS 322 323 size_t PathOps::NumberOfOps() const { 324 size_t size = 0; 325 const uint8_t* nextByte = mPathData.data(); 326 const uint8_t* end = nextByte + mPathData.size(); 327 while (nextByte < end) { 328 size++; 329 const OpType opType = *reinterpret_cast<const OpType*>(nextByte); 330 nextByte += sizeof(OpType); 331 switch (opType) { 332 case OpType::OP_MOVETO: 333 nextByte += sizeof(Point); 334 break; 335 case OpType::OP_LINETO: 336 nextByte += sizeof(Point); 337 break; 338 case OpType::OP_BEZIERTO: 339 nextByte += sizeof(ThreePoints); 340 break; 341 case OpType::OP_QUADRATICBEZIERTO: 342 nextByte += sizeof(TwoPoints); 343 break; 344 case OpType::OP_ARC_CW: 345 case OpType::OP_ARC_CCW: 346 nextByte += sizeof(ArcParams); 347 break; 348 case OpType::OP_CLOSE: 349 break; 350 default: 351 MOZ_CRASH("We control mOpTypes, so this should never happen."); 352 } 353 } 354 355 return size; 356 } 357 358 bool PathOps::IsEmpty() const { 359 const uint8_t* nextByte = mPathData.data(); 360 const uint8_t* end = nextByte + mPathData.size(); 361 while (nextByte < end) { 362 const OpType opType = *reinterpret_cast<const OpType*>(nextByte); 363 nextByte += sizeof(OpType); 364 switch (opType) { 365 case OpType::OP_MOVETO: 366 nextByte += sizeof(Point); 367 break; 368 case OpType::OP_CLOSE: 369 break; 370 default: 371 return false; 372 } 373 } 374 return true; 375 } 376 377 void PathBuilderRecording::MoveTo(const Point& aPoint) { 378 mPathOps.MoveTo(aPoint); 379 mBeginPoint = aPoint; 380 mCurrentPoint = aPoint; 381 } 382 383 void PathBuilderRecording::LineTo(const Point& aPoint) { 384 mPathOps.LineTo(aPoint); 385 mCurrentPoint = aPoint; 386 } 387 388 void PathBuilderRecording::BezierTo(const Point& aCP1, const Point& aCP2, 389 const Point& aCP3) { 390 mPathOps.BezierTo(aCP1, aCP2, aCP3); 391 mCurrentPoint = aCP3; 392 } 393 394 void PathBuilderRecording::QuadraticBezierTo(const Point& aCP1, 395 const Point& aCP2) { 396 mPathOps.QuadraticBezierTo(aCP1, aCP2); 397 mCurrentPoint = aCP2; 398 } 399 400 void PathBuilderRecording::Close() { 401 mPathOps.Close(); 402 mCurrentPoint = mBeginPoint; 403 } 404 405 void PathBuilderRecording::Arc(const Point& aOrigin, float aRadius, 406 float aStartAngle, float aEndAngle, 407 bool aAntiClockwise) { 408 mPathOps.Arc(aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise); 409 410 mCurrentPoint = aOrigin + Point(cosf(aEndAngle), sinf(aEndAngle)) * aRadius; 411 } 412 413 already_AddRefed<Path> PathBuilderRecording::Finish() { 414 return MakeAndAddRef<PathRecording>(mBackendType, std::move(mPathOps), 415 mFillRule, mCurrentPoint, mBeginPoint); 416 } 417 418 PathRecording::PathRecording(BackendType aBackend, PathOps&& aOps, 419 FillRule aFillRule, const Point& aCurrentPoint, 420 const Point& aBeginPoint) 421 : mBackendType(aBackend), 422 mPathOps(std::move(aOps)), 423 mFillRule(aFillRule), 424 mCurrentPoint(aCurrentPoint), 425 mBeginPoint(aBeginPoint) {} 426 427 PathRecording::~PathRecording() { 428 for (size_t i = 0; i < mStoredRecorders.size(); i++) { 429 mStoredRecorders[i]->RemoveStoredObject(this); 430 mStoredRecorders[i]->RecordEvent(RecordedPathDestruction(this)); 431 } 432 } 433 434 void PathRecording::EnsurePath() const { 435 if (mPath) { 436 return; 437 } 438 if (RefPtr<PathBuilder> pathBuilder = 439 Factory::CreatePathBuilder(mBackendType, mFillRule)) { 440 if (!mPathOps.StreamToSink(*pathBuilder)) { 441 MOZ_ASSERT(false, "Failed to stream PathOps to PathBuilder"); 442 } else { 443 mPath = pathBuilder->Finish(); 444 MOZ_ASSERT(!!mPath, "Failed finishing Path from PathBuilder"); 445 } 446 } else { 447 MOZ_ASSERT(false, "Failed to create PathBuilder for PathRecording"); 448 } 449 } 450 451 already_AddRefed<PathBuilder> PathRecording::CopyToBuilder( 452 FillRule aFillRule) const { 453 RefPtr<PathBuilderRecording> recording = 454 new PathBuilderRecording(mBackendType, PathOps(mPathOps), aFillRule); 455 recording->SetCurrentPoint(mCurrentPoint); 456 recording->SetBeginPoint(mBeginPoint); 457 return recording.forget(); 458 } 459 460 already_AddRefed<PathBuilder> PathRecording::TransformedCopyToBuilder( 461 const Matrix& aTransform, FillRule aFillRule) const { 462 RefPtr<PathBuilderRecording> recording = new PathBuilderRecording( 463 mBackendType, mPathOps.TransformedCopy(aTransform), aFillRule); 464 recording->SetCurrentPoint(aTransform.TransformPoint(mCurrentPoint)); 465 recording->SetBeginPoint(aTransform.TransformPoint(mBeginPoint)); 466 return recording.forget(); 467 } 468 469 already_AddRefed<PathBuilder> PathRecording::MoveToBuilder(FillRule aFillRule) { 470 RefPtr<PathBuilderRecording> recording = 471 new PathBuilderRecording(mBackendType, std::move(mPathOps), aFillRule); 472 recording->SetCurrentPoint(mCurrentPoint); 473 recording->SetBeginPoint(mBeginPoint); 474 return recording.forget(); 475 } 476 477 already_AddRefed<PathBuilder> PathRecording::TransformedMoveToBuilder( 478 const Matrix& aTransform, FillRule aFillRule) { 479 mPathOps.TransformInPlace(aTransform); 480 RefPtr<PathBuilderRecording> recording = 481 new PathBuilderRecording(mBackendType, std::move(mPathOps), aFillRule); 482 recording->SetCurrentPoint(aTransform.TransformPoint(mCurrentPoint)); 483 recording->SetBeginPoint(aTransform.TransformPoint(mBeginPoint)); 484 return recording.forget(); 485 } 486 487 } // namespace gfx 488 } // namespace mozilla