Update configs. IGNORE BROKEN CHANGESETS CLOSED TREE NO BUG a=release ba=release
[gecko.git] / gfx / 2d / PathRecording.cpp
blobec79c992f7849040ba635aefb03f09dd3abcbfc7
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/. */
7 #include "PathRecording.h"
8 #include "DrawEventRecorder.h"
9 #include "RecordedEventImpl.h"
11 namespace mozilla {
12 namespace gfx {
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);
21 return Nothing();
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);
34 #define NEXT_PARAMS(_type) \
35 const _type params = *reinterpret_cast<const _type*>(nextByte); \
36 nextByte += sizeof(_type);
38 bool PathOps::StreamToSink(PathSink& aPathSink) const {
39 if (mPathData.empty()) {
40 return true;
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;
54 case OpType::OP_LINETO: {
55 NEXT_PARAMS(Point)
56 aPathSink.LineTo(params);
57 break;
59 case OpType::OP_BEZIERTO: {
60 NEXT_PARAMS(ThreePoints)
61 aPathSink.BezierTo(params.p1, params.p2, params.p3);
62 break;
64 case OpType::OP_QUADRATICBEZIERTO: {
65 NEXT_PARAMS(TwoPoints)
66 aPathSink.QuadraticBezierTo(params.p1, params.p2);
67 break;
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;
75 case OpType::OP_CLOSE:
76 aPathSink.Close();
77 break;
78 default:
79 return false;
83 return true;
86 #define CHECKED_NEXT_PARAMS(_type) \
87 if (nextByte + sizeof(_type) > end) { \
88 return false; \
89 } \
90 NEXT_PARAMS(_type)
92 bool PathOps::CheckedStreamToSink(PathSink& aPathSink) const {
93 if (mPathData.empty()) {
94 return true;
97 const uint8_t* nextByte = mPathData.data();
98 const uint8_t* end = nextByte + mPathData.size();
99 while (true) {
100 if (nextByte == end) {
101 break;
104 if (nextByte + sizeof(OpType) > end) {
105 return false;
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;
116 case OpType::OP_LINETO: {
117 CHECKED_NEXT_PARAMS(Point)
118 aPathSink.LineTo(params);
119 break;
121 case OpType::OP_BEZIERTO: {
122 CHECKED_NEXT_PARAMS(ThreePoints)
123 aPathSink.BezierTo(params.p1, params.p2, params.p3);
124 break;
126 case OpType::OP_QUADRATICBEZIERTO: {
127 CHECKED_NEXT_PARAMS(TwoPoints)
128 aPathSink.QuadraticBezierTo(params.p1, params.p2);
129 break;
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;
137 case OpType::OP_CLOSE:
138 aPathSink.Close();
139 break;
140 default:
141 return false;
145 return true;
147 #undef CHECKED_NEXT_PARAMS
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;
163 case OpType::OP_LINETO: {
164 NEXT_PARAMS(Point)
165 newPathOps.LineTo(aTransform.TransformPoint(params));
166 break;
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;
175 case OpType::OP_QUADRATICBEZIERTO: {
176 NEXT_PARAMS(TwoPoints)
177 newPathOps.QuadraticBezierTo(aTransform.TransformPoint(params.p1),
178 aTransform.TransformPoint(params.p2));
179 break;
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;
188 case OpType::OP_CLOSE:
189 newPathOps.Close();
190 break;
191 default:
192 MOZ_CRASH("We control mOpTypes, so this should never happen.");
196 return newPathOps;
199 #define MODIFY_NEXT_PARAMS(_type) \
200 _type& params = *reinterpret_cast<_type*>(nextByte); \
201 nextByte += sizeof(_type);
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;
215 case OpType::OP_LINETO: {
216 MODIFY_NEXT_PARAMS(Point)
217 params = aTransform.TransformPoint(params);
218 break;
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;
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;
233 case OpType::OP_ARC_CW:
234 case OpType::OP_ARC_CCW: {
235 MODIFY_NEXT_PARAMS(ArcParams)
236 params.transform *= aTransform;
237 break;
239 case OpType::OP_CLOSE:
240 break;
241 default:
242 MOZ_CRASH("We control mOpTypes, so this should never happen.");
247 Maybe<Circle> PathOps::AsCircle() const {
248 if (mPathData.empty()) {
249 return Nothing();
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});
269 } else {
270 // the circle wasn't closed
271 return Some(Circle{params.GetOrigin(), *radius, false});
277 return Nothing();
280 Maybe<Line> PathOps::AsLine() const {
281 if (mPathData.empty()) {
282 return Nothing();
285 Line retval;
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);
292 if (opType == OpType::OP_MOVETO) {
293 MOZ_ASSERT(nextByte != end);
295 NEXT_PARAMS(Point)
296 retval.origin = params;
297 } else {
298 return Nothing();
301 if (nextByte >= end) {
302 return Nothing();
305 opType = *reinterpret_cast<const OpType*>(nextByte);
306 nextByte += sizeof(OpType);
308 if (opType == OpType::OP_LINETO) {
309 MOZ_ASSERT(nextByte != end);
311 NEXT_PARAMS(Point)
313 if (nextByte == end) {
314 retval.destination = params;
315 return Some(retval);
319 return Nothing();
321 #undef NEXT_PARAMS
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.");
355 return size;
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;
374 return true;
377 void PathBuilderRecording::MoveTo(const Point& aPoint) {
378 mPathOps.MoveTo(aPoint);
379 mBeginPoint = aPoint;
380 mCurrentPoint = aPoint;
383 void PathBuilderRecording::LineTo(const Point& aPoint) {
384 mPathOps.LineTo(aPoint);
385 mCurrentPoint = aPoint;
388 void PathBuilderRecording::BezierTo(const Point& aCP1, const Point& aCP2,
389 const Point& aCP3) {
390 mPathOps.BezierTo(aCP1, aCP2, aCP3);
391 mCurrentPoint = aCP3;
394 void PathBuilderRecording::QuadraticBezierTo(const Point& aCP1,
395 const Point& aCP2) {
396 mPathOps.QuadraticBezierTo(aCP1, aCP2);
397 mCurrentPoint = aCP2;
400 void PathBuilderRecording::Close() {
401 mPathOps.Close();
402 mCurrentPoint = mBeginPoint;
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);
410 mCurrentPoint = aOrigin + Point(cosf(aEndAngle), sinf(aEndAngle)) * aRadius;
413 already_AddRefed<Path> PathBuilderRecording::Finish() {
414 return MakeAndAddRef<PathRecording>(mBackendType, std::move(mPathOps),
415 mFillRule, mCurrentPoint, mBeginPoint);
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) {}
427 PathRecording::~PathRecording() {
428 for (size_t i = 0; i < mStoredRecorders.size(); i++) {
429 mStoredRecorders[i]->RemoveStoredObject(this);
430 mStoredRecorders[i]->RecordEvent(RecordedPathDestruction(this));
434 void PathRecording::EnsurePath() const {
435 if (mPath) {
436 return;
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");
446 } else {
447 MOZ_ASSERT(false, "Failed to create PathBuilder for PathRecording");
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();
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();
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();
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();
487 } // namespace gfx
488 } // namespace mozilla