no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / gfx / 2d / PathRecording.cpp
blob22509861600895ea2654564f695dd1b363f48a8b
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 #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()) {
20 return true;
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);
28 switch (opType) {
29 case OpType::OP_MOVETO: {
30 NEXT_PARAMS(Point)
31 aPathSink.MoveTo(params);
32 break;
34 case OpType::OP_LINETO: {
35 NEXT_PARAMS(Point)
36 aPathSink.LineTo(params);
37 break;
39 case OpType::OP_BEZIERTO: {
40 NEXT_PARAMS(ThreePoints)
41 aPathSink.BezierTo(params.p1, params.p2, params.p3);
42 break;
44 case OpType::OP_QUADRATICBEZIERTO: {
45 NEXT_PARAMS(TwoPoints)
46 aPathSink.QuadraticBezierTo(params.p1, params.p2);
47 break;
49 case OpType::OP_ARC: {
50 NEXT_PARAMS(ArcParams)
51 aPathSink.Arc(params.origin, params.radius, params.startAngle,
52 params.endAngle, params.antiClockwise);
53 break;
55 case OpType::OP_CLOSE:
56 aPathSink.Close();
57 break;
58 default:
59 return false;
63 return true;
66 #define CHECKED_NEXT_PARAMS(_type) \
67 if (nextByte + sizeof(_type) > end) { \
68 return false; \
69 } \
70 NEXT_PARAMS(_type)
72 bool PathOps::CheckedStreamToSink(PathSink& aPathSink) const {
73 if (mPathData.empty()) {
74 return true;
77 const uint8_t* nextByte = mPathData.data();
78 const uint8_t* end = nextByte + mPathData.size();
79 while (true) {
80 if (nextByte == end) {
81 break;
84 if (nextByte + sizeof(OpType) > end) {
85 return false;
88 const OpType opType = *reinterpret_cast<const OpType*>(nextByte);
89 nextByte += sizeof(OpType);
90 switch (opType) {
91 case OpType::OP_MOVETO: {
92 CHECKED_NEXT_PARAMS(Point)
93 aPathSink.MoveTo(params);
94 break;
96 case OpType::OP_LINETO: {
97 CHECKED_NEXT_PARAMS(Point)
98 aPathSink.LineTo(params);
99 break;
101 case OpType::OP_BEZIERTO: {
102 CHECKED_NEXT_PARAMS(ThreePoints)
103 aPathSink.BezierTo(params.p1, params.p2, params.p3);
104 break;
106 case OpType::OP_QUADRATICBEZIERTO: {
107 CHECKED_NEXT_PARAMS(TwoPoints)
108 aPathSink.QuadraticBezierTo(params.p1, params.p2);
109 break;
111 case OpType::OP_ARC: {
112 CHECKED_NEXT_PARAMS(ArcParams)
113 aPathSink.Arc(params.origin, params.radius, params.startAngle,
114 params.endAngle, params.antiClockwise);
115 break;
117 case OpType::OP_CLOSE:
118 aPathSink.Close();
119 break;
120 default:
121 return false;
125 return true;
127 #undef CHECKED_NEXT_PARAMS
129 PathOps PathOps::TransformedCopy(const Matrix& aTransform) const {
130 PathOps newPathOps;
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);
136 switch (opType) {
137 case OpType::OP_MOVETO: {
138 NEXT_PARAMS(Point)
139 newPathOps.MoveTo(aTransform.TransformPoint(params));
140 break;
142 case OpType::OP_LINETO: {
143 NEXT_PARAMS(Point)
144 newPathOps.LineTo(aTransform.TransformPoint(params));
145 break;
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));
152 break;
154 case OpType::OP_QUADRATICBEZIERTO: {
155 NEXT_PARAMS(TwoPoints)
156 newPathOps.QuadraticBezierTo(aTransform.TransformPoint(params.p1),
157 aTransform.TransformPoint(params.p2));
158 break;
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);
165 break;
167 case OpType::OP_CLOSE:
168 newPathOps.Close();
169 break;
170 default:
171 MOZ_CRASH("We control mOpTypes, so this should never happen.");
175 return newPathOps;
178 Maybe<Circle> PathOps::AsCircle() const {
179 if (mPathData.empty()) {
180 return Nothing();
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});
199 } else {
200 // the circle wasn't closed
201 return Some(Circle{params.origin, params.radius, false});
206 return Nothing();
209 Maybe<Line> PathOps::AsLine() const {
210 if (mPathData.empty()) {
211 return Nothing();
214 Line retval;
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);
224 NEXT_PARAMS(Point)
225 retval.origin = params;
226 } else {
227 return Nothing();
230 if (nextByte >= end) {
231 return Nothing();
234 opType = *reinterpret_cast<const OpType*>(nextByte);
235 nextByte += sizeof(OpType);
237 if (opType == OpType::OP_LINETO) {
238 MOZ_ASSERT(nextByte != end);
240 NEXT_PARAMS(Point)
242 if (nextByte == end) {
243 retval.destination = params;
244 return Some(retval);
248 return Nothing();
250 #undef NEXT_PARAMS
252 size_t PathOps::NumberOfOps() const {
253 size_t size = 0;
254 const uint8_t* nextByte = mPathData.data();
255 const uint8_t* end = nextByte + mPathData.size();
256 while (nextByte < end) {
257 size++;
258 const OpType opType = *reinterpret_cast<const OpType*>(nextByte);
259 nextByte += sizeof(OpType);
260 switch (opType) {
261 case OpType::OP_MOVETO:
262 nextByte += sizeof(Point);
263 break;
264 case OpType::OP_LINETO:
265 nextByte += sizeof(Point);
266 break;
267 case OpType::OP_BEZIERTO:
268 nextByte += sizeof(ThreePoints);
269 break;
270 case OpType::OP_QUADRATICBEZIERTO:
271 nextByte += sizeof(TwoPoints);
272 break;
273 case OpType::OP_ARC:
274 nextByte += sizeof(ArcParams);
275 break;
276 case OpType::OP_CLOSE:
277 break;
278 default:
279 MOZ_CRASH("We control mOpTypes, so this should never happen.");
283 return size;
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);
292 switch (opType) {
293 case OpType::OP_MOVETO:
294 nextByte += sizeof(Point);
295 break;
296 case OpType::OP_CLOSE:
297 break;
298 default:
299 return false;
302 return true;
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,
317 const Point& aCP3) {
318 mPathOps.BezierTo(aCP1, aCP2, aCP3);
319 mCurrentPoint = aCP3;
322 void PathBuilderRecording::QuadraticBezierTo(const Point& aCP1,
323 const Point& aCP2) {
324 mPathOps.QuadraticBezierTo(aCP1, aCP2);
325 mCurrentPoint = aCP2;
328 void PathBuilderRecording::Close() {
329 mPathOps.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 {
363 if (mPath) {
364 return;
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");
370 } else {
371 mPath = pathBuilder->Finish();
372 MOZ_ASSERT(!!mPath, "Failed finishing Path from PathBuilder");
374 } else {
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();
397 } // namespace gfx
398 } // namespace mozilla