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"
14 #define NEXT_PARAMS(_type) \
15 const _type params = *reinterpret_cast<const _type*>(nextByte); \
16 nextByte += sizeof(_type);
18 bool PathOps::StreamToSink(PathSink
& aPathSink
) const {
19 if (mPathData
.empty()) {
23 const uint8_t* nextByte
= mPathData
.data();
24 const uint8_t* end
= nextByte
+ mPathData
.size();
25 while (nextByte
< end
) {
26 const OpType opType
= *reinterpret_cast<const OpType
*>(nextByte
);
27 nextByte
+= sizeof(OpType
);
29 case OpType::OP_MOVETO
: {
31 aPathSink
.MoveTo(params
);
34 case OpType::OP_LINETO
: {
36 aPathSink
.LineTo(params
);
39 case OpType::OP_BEZIERTO
: {
40 NEXT_PARAMS(ThreePoints
)
41 aPathSink
.BezierTo(params
.p1
, params
.p2
, params
.p3
);
44 case OpType::OP_QUADRATICBEZIERTO
: {
45 NEXT_PARAMS(TwoPoints
)
46 aPathSink
.QuadraticBezierTo(params
.p1
, params
.p2
);
49 case OpType::OP_ARC
: {
50 NEXT_PARAMS(ArcParams
)
51 aPathSink
.Arc(params
.origin
, params
.radius
, params
.startAngle
,
52 params
.endAngle
, params
.antiClockwise
);
55 case OpType::OP_CLOSE
:
66 #define CHECKED_NEXT_PARAMS(_type) \
67 if (nextByte + sizeof(_type) > end) { \
72 bool PathOps::CheckedStreamToSink(PathSink
& aPathSink
) const {
73 if (mPathData
.empty()) {
77 const uint8_t* nextByte
= mPathData
.data();
78 const uint8_t* end
= nextByte
+ mPathData
.size();
80 if (nextByte
== end
) {
84 if (nextByte
+ sizeof(OpType
) > end
) {
88 const OpType opType
= *reinterpret_cast<const OpType
*>(nextByte
);
89 nextByte
+= sizeof(OpType
);
91 case OpType::OP_MOVETO
: {
92 CHECKED_NEXT_PARAMS(Point
)
93 aPathSink
.MoveTo(params
);
96 case OpType::OP_LINETO
: {
97 CHECKED_NEXT_PARAMS(Point
)
98 aPathSink
.LineTo(params
);
101 case OpType::OP_BEZIERTO
: {
102 CHECKED_NEXT_PARAMS(ThreePoints
)
103 aPathSink
.BezierTo(params
.p1
, params
.p2
, params
.p3
);
106 case OpType::OP_QUADRATICBEZIERTO
: {
107 CHECKED_NEXT_PARAMS(TwoPoints
)
108 aPathSink
.QuadraticBezierTo(params
.p1
, params
.p2
);
111 case OpType::OP_ARC
: {
112 CHECKED_NEXT_PARAMS(ArcParams
)
113 aPathSink
.Arc(params
.origin
, params
.radius
, params
.startAngle
,
114 params
.endAngle
, params
.antiClockwise
);
117 case OpType::OP_CLOSE
:
127 #undef CHECKED_NEXT_PARAMS
129 PathOps
PathOps::TransformedCopy(const Matrix
& aTransform
) const {
131 const uint8_t* nextByte
= mPathData
.data();
132 const uint8_t* end
= nextByte
+ mPathData
.size();
133 while (nextByte
< end
) {
134 const OpType opType
= *reinterpret_cast<const OpType
*>(nextByte
);
135 nextByte
+= sizeof(OpType
);
137 case OpType::OP_MOVETO
: {
139 newPathOps
.MoveTo(aTransform
.TransformPoint(params
));
142 case OpType::OP_LINETO
: {
144 newPathOps
.LineTo(aTransform
.TransformPoint(params
));
147 case OpType::OP_BEZIERTO
: {
148 NEXT_PARAMS(ThreePoints
)
149 newPathOps
.BezierTo(aTransform
.TransformPoint(params
.p1
),
150 aTransform
.TransformPoint(params
.p2
),
151 aTransform
.TransformPoint(params
.p3
));
154 case OpType::OP_QUADRATICBEZIERTO
: {
155 NEXT_PARAMS(TwoPoints
)
156 newPathOps
.QuadraticBezierTo(aTransform
.TransformPoint(params
.p1
),
157 aTransform
.TransformPoint(params
.p2
));
160 case OpType::OP_ARC
: {
161 NEXT_PARAMS(ArcParams
)
162 ArcToBezier(&newPathOps
, params
.origin
,
163 gfx::Size(params
.radius
, params
.radius
), params
.startAngle
,
164 params
.endAngle
, params
.antiClockwise
, 0.0f
, aTransform
);
167 case OpType::OP_CLOSE
:
171 MOZ_CRASH("We control mOpTypes, so this should never happen.");
178 Maybe
<Circle
> PathOps::AsCircle() const {
179 if (mPathData
.empty()) {
183 const uint8_t* nextByte
= mPathData
.data();
184 const uint8_t* end
= nextByte
+ mPathData
.size();
185 const OpType opType
= *reinterpret_cast<const OpType
*>(nextByte
);
186 nextByte
+= sizeof(OpType
);
187 if (opType
== OpType::OP_ARC
) {
188 NEXT_PARAMS(ArcParams
)
189 if (fabs(fabs(params
.startAngle
- params
.endAngle
) - 2 * M_PI
) < 1e-6) {
190 // we have a full circle
191 if (nextByte
< end
) {
192 const OpType nextOpType
= *reinterpret_cast<const OpType
*>(nextByte
);
193 nextByte
+= sizeof(OpType
);
194 if (nextOpType
== OpType::OP_CLOSE
) {
195 if (nextByte
== end
) {
196 return Some(Circle
{params
.origin
, params
.radius
, true});
200 // the circle wasn't closed
201 return Some(Circle
{params
.origin
, params
.radius
, false});
209 Maybe
<Line
> PathOps::AsLine() const {
210 if (mPathData
.empty()) {
216 const uint8_t* nextByte
= mPathData
.data();
217 const uint8_t* end
= nextByte
+ mPathData
.size();
218 OpType opType
= *reinterpret_cast<const OpType
*>(nextByte
);
219 nextByte
+= sizeof(OpType
);
221 if (opType
== OpType::OP_MOVETO
) {
222 MOZ_ASSERT(nextByte
!= end
);
225 retval
.origin
= params
;
230 if (nextByte
>= end
) {
234 opType
= *reinterpret_cast<const OpType
*>(nextByte
);
235 nextByte
+= sizeof(OpType
);
237 if (opType
== OpType::OP_LINETO
) {
238 MOZ_ASSERT(nextByte
!= end
);
242 if (nextByte
== end
) {
243 retval
.destination
= params
;
252 size_t PathOps::NumberOfOps() const {
254 const uint8_t* nextByte
= mPathData
.data();
255 const uint8_t* end
= nextByte
+ mPathData
.size();
256 while (nextByte
< end
) {
258 const OpType opType
= *reinterpret_cast<const OpType
*>(nextByte
);
259 nextByte
+= sizeof(OpType
);
261 case OpType::OP_MOVETO
:
262 nextByte
+= sizeof(Point
);
264 case OpType::OP_LINETO
:
265 nextByte
+= sizeof(Point
);
267 case OpType::OP_BEZIERTO
:
268 nextByte
+= sizeof(ThreePoints
);
270 case OpType::OP_QUADRATICBEZIERTO
:
271 nextByte
+= sizeof(TwoPoints
);
274 nextByte
+= sizeof(ArcParams
);
276 case OpType::OP_CLOSE
:
279 MOZ_CRASH("We control mOpTypes, so this should never happen.");
286 bool PathOps::IsEmpty() const {
287 const uint8_t* nextByte
= mPathData
.data();
288 const uint8_t* end
= nextByte
+ mPathData
.size();
289 while (nextByte
< end
) {
290 const OpType opType
= *reinterpret_cast<const OpType
*>(nextByte
);
291 nextByte
+= sizeof(OpType
);
293 case OpType::OP_MOVETO
:
294 nextByte
+= sizeof(Point
);
296 case OpType::OP_CLOSE
:
305 void PathBuilderRecording::MoveTo(const Point
& aPoint
) {
306 mPathOps
.MoveTo(aPoint
);
307 mBeginPoint
= aPoint
;
308 mCurrentPoint
= aPoint
;
311 void PathBuilderRecording::LineTo(const Point
& aPoint
) {
312 mPathOps
.LineTo(aPoint
);
313 mCurrentPoint
= aPoint
;
316 void PathBuilderRecording::BezierTo(const Point
& aCP1
, const Point
& aCP2
,
318 mPathOps
.BezierTo(aCP1
, aCP2
, aCP3
);
319 mCurrentPoint
= aCP3
;
322 void PathBuilderRecording::QuadraticBezierTo(const Point
& aCP1
,
324 mPathOps
.QuadraticBezierTo(aCP1
, aCP2
);
325 mCurrentPoint
= aCP2
;
328 void PathBuilderRecording::Close() {
330 mCurrentPoint
= mBeginPoint
;
333 void PathBuilderRecording::Arc(const Point
& aOrigin
, float aRadius
,
334 float aStartAngle
, float aEndAngle
,
335 bool aAntiClockwise
) {
336 mPathOps
.Arc(aOrigin
, aRadius
, aStartAngle
, aEndAngle
, aAntiClockwise
);
338 mCurrentPoint
= aOrigin
+ Point(cosf(aEndAngle
), sinf(aEndAngle
)) * aRadius
;
341 already_AddRefed
<Path
> PathBuilderRecording::Finish() {
342 return MakeAndAddRef
<PathRecording
>(mBackendType
, std::move(mPathOps
),
343 mFillRule
, mCurrentPoint
, mBeginPoint
);
346 PathRecording::PathRecording(BackendType aBackend
, PathOps
&& aOps
,
347 FillRule aFillRule
, const Point
& aCurrentPoint
,
348 const Point
& aBeginPoint
)
349 : mBackendType(aBackend
),
350 mPathOps(std::move(aOps
)),
351 mFillRule(aFillRule
),
352 mCurrentPoint(aCurrentPoint
),
353 mBeginPoint(aBeginPoint
) {}
355 PathRecording::~PathRecording() {
356 for (size_t i
= 0; i
< mStoredRecorders
.size(); i
++) {
357 mStoredRecorders
[i
]->RemoveStoredObject(this);
358 mStoredRecorders
[i
]->RecordEvent(RecordedPathDestruction(this));
362 void PathRecording::EnsurePath() const {
366 if (RefPtr
<PathBuilder
> pathBuilder
=
367 Factory::CreatePathBuilder(mBackendType
, mFillRule
)) {
368 if (!mPathOps
.StreamToSink(*pathBuilder
)) {
369 MOZ_ASSERT(false, "Failed to stream PathOps to PathBuilder");
371 mPath
= pathBuilder
->Finish();
372 MOZ_ASSERT(!!mPath
, "Failed finishing Path from PathBuilder");
375 MOZ_ASSERT(false, "Failed to create PathBuilder for PathRecording");
379 already_AddRefed
<PathBuilder
> PathRecording::CopyToBuilder(
380 FillRule aFillRule
) const {
381 RefPtr
<PathBuilderRecording
> recording
=
382 new PathBuilderRecording(mBackendType
, PathOps(mPathOps
), aFillRule
);
383 recording
->SetCurrentPoint(mCurrentPoint
);
384 recording
->SetBeginPoint(mBeginPoint
);
385 return recording
.forget();
388 already_AddRefed
<PathBuilder
> PathRecording::TransformedCopyToBuilder(
389 const Matrix
& aTransform
, FillRule aFillRule
) const {
390 RefPtr
<PathBuilderRecording
> recording
= new PathBuilderRecording(
391 mBackendType
, mPathOps
.TransformedCopy(aTransform
), aFillRule
);
392 recording
->SetCurrentPoint(aTransform
.TransformPoint(mCurrentPoint
));
393 recording
->SetBeginPoint(aTransform
.TransformPoint(mBeginPoint
));
394 return recording
.forget();
398 } // namespace mozilla