Line data Source code
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 <math.h>
8 :
9 : #include "mozilla/Alignment.h"
10 :
11 : #include "cairo.h"
12 :
13 : #include "gfxContext.h"
14 :
15 : #include "gfxMatrix.h"
16 : #include "gfxUtils.h"
17 : #include "gfxASurface.h"
18 : #include "gfxPattern.h"
19 : #include "gfxPlatform.h"
20 : #include "gfxPrefs.h"
21 : #include "GeckoProfiler.h"
22 : #include "gfx2DGlue.h"
23 : #include "mozilla/gfx/PathHelpers.h"
24 : #include "mozilla/gfx/DrawTargetTiled.h"
25 : #include <algorithm>
26 :
27 : #if XP_WIN
28 : #include "gfxWindowsPlatform.h"
29 : #include "mozilla/gfx/DeviceManagerDx.h"
30 : #endif
31 :
32 : using namespace mozilla;
33 : using namespace mozilla::gfx;
34 :
35 : UserDataKey gfxContext::sDontUseAsSourceKey;
36 :
37 :
38 78 : PatternFromState::operator mozilla::gfx::Pattern&()
39 : {
40 78 : gfxContext::AzureState &state = mContext->CurrentState();
41 :
42 78 : if (state.pattern) {
43 25 : return *state.pattern->GetPattern(mContext->mDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
44 : }
45 :
46 53 : if (state.sourceSurface) {
47 0 : Matrix transform = state.surfTransform;
48 :
49 0 : if (state.patternTransformChanged) {
50 0 : Matrix mat = mContext->GetDTTransform();
51 0 : if (!mat.Invert()) {
52 0 : mPattern = new (mColorPattern.addr())
53 0 : ColorPattern(Color()); // transparent black to paint nothing
54 0 : return *mPattern;
55 : }
56 0 : transform = transform * state.patternTransform * mat;
57 : }
58 :
59 0 : mPattern = new (mSurfacePattern.addr())
60 0 : SurfacePattern(state.sourceSurface, ExtendMode::CLAMP, transform);
61 0 : return *mPattern;
62 : }
63 :
64 159 : mPattern = new (mColorPattern.addr())
65 106 : ColorPattern(state.color);
66 53 : return *mPattern;
67 : }
68 :
69 :
70 151 : gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
71 : : mPathIsRect(false)
72 : , mTransformChanged(false)
73 151 : , mDT(aTarget)
74 : {
75 151 : if (!aTarget) {
76 0 : gfxCriticalError() << "Don't create a gfxContext without a DrawTarget";
77 : }
78 :
79 151 : mStateStack.SetLength(1);
80 151 : CurrentState().drawTarget = mDT;
81 151 : CurrentState().deviceOffset = aDeviceOffset;
82 151 : mDT->SetTransform(GetDTTransform());
83 151 : }
84 :
85 : /* static */ already_AddRefed<gfxContext>
86 118 : gfxContext::CreateOrNull(DrawTarget* aTarget,
87 : const mozilla::gfx::Point& aDeviceOffset)
88 : {
89 118 : if (!aTarget || !aTarget->IsValid()) {
90 0 : gfxCriticalNote << "Invalid target in gfxContext::CreateOrNull " << hexa(aTarget);
91 0 : return nullptr;
92 : }
93 :
94 236 : RefPtr<gfxContext> result = new gfxContext(aTarget, aDeviceOffset);
95 118 : return result.forget();
96 : }
97 :
98 : /* static */ already_AddRefed<gfxContext>
99 33 : gfxContext::CreatePreservingTransformOrNull(DrawTarget* aTarget)
100 : {
101 33 : if (!aTarget || !aTarget->IsValid()) {
102 0 : gfxCriticalNote << "Invalid target in gfxContext::CreatePreservingTransformOrNull " << hexa(aTarget);
103 0 : return nullptr;
104 : }
105 :
106 33 : Matrix transform = aTarget->GetTransform();
107 99 : RefPtr<gfxContext> result = new gfxContext(aTarget);
108 33 : result->SetMatrix(ThebesMatrix(transform));
109 33 : return result.forget();
110 : }
111 :
112 302 : gfxContext::~gfxContext()
113 : {
114 302 : for (int i = mStateStack.Length() - 1; i >= 0; i--) {
115 184 : for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
116 33 : mStateStack[i].drawTarget->PopClip();
117 : }
118 : }
119 151 : }
120 :
121 : void
122 374 : gfxContext::Save()
123 : {
124 374 : CurrentState().transform = mTransform;
125 374 : mStateStack.AppendElement(AzureState(CurrentState()));
126 374 : CurrentState().pushedClips.Clear();
127 374 : }
128 :
129 : void
130 374 : gfxContext::Restore()
131 : {
132 714 : for (unsigned int c = 0; c < CurrentState().pushedClips.Length(); c++) {
133 340 : mDT->PopClip();
134 : }
135 :
136 374 : mStateStack.RemoveElementAt(mStateStack.Length() - 1);
137 :
138 374 : mDT = CurrentState().drawTarget;
139 :
140 374 : ChangeTransform(CurrentState().transform, false);
141 374 : }
142 :
143 : // drawing
144 : void
145 634 : gfxContext::NewPath()
146 : {
147 634 : mPath = nullptr;
148 634 : mPathBuilder = nullptr;
149 634 : mPathIsRect = false;
150 634 : mTransformChanged = false;
151 634 : }
152 :
153 : void
154 0 : gfxContext::ClosePath()
155 : {
156 0 : EnsurePathBuilder();
157 0 : mPathBuilder->Close();
158 0 : }
159 :
160 0 : already_AddRefed<Path> gfxContext::GetPath()
161 : {
162 0 : EnsurePath();
163 0 : RefPtr<Path> path(mPath);
164 0 : return path.forget();
165 : }
166 :
167 10 : void gfxContext::SetPath(Path* path)
168 : {
169 10 : MOZ_ASSERT(path->GetBackendType() == mDT->GetBackendType() ||
170 : path->GetBackendType() == BackendType::RECORDING ||
171 : (mDT->GetBackendType() == BackendType::DIRECT2D1_1 && path->GetBackendType() == BackendType::DIRECT2D));
172 10 : mPath = path;
173 10 : mPathBuilder = nullptr;
174 10 : mPathIsRect = false;
175 10 : mTransformChanged = false;
176 10 : }
177 :
178 : gfxPoint
179 0 : gfxContext::CurrentPoint()
180 : {
181 0 : EnsurePathBuilder();
182 0 : return ThebesPoint(mPathBuilder->CurrentPoint());
183 : }
184 :
185 : void
186 78 : gfxContext::Fill()
187 : {
188 78 : Fill(PatternFromState(this));
189 78 : }
190 :
191 : void
192 78 : gfxContext::Fill(const Pattern& aPattern)
193 : {
194 156 : AUTO_PROFILER_LABEL("gfxContext::Fill", GRAPHICS);
195 78 : FillAzure(aPattern, 1.0f);
196 78 : }
197 :
198 : void
199 0 : gfxContext::MoveTo(const gfxPoint& pt)
200 : {
201 0 : EnsurePathBuilder();
202 0 : mPathBuilder->MoveTo(ToPoint(pt));
203 0 : }
204 :
205 : void
206 0 : gfxContext::LineTo(const gfxPoint& pt)
207 : {
208 0 : EnsurePathBuilder();
209 0 : mPathBuilder->LineTo(ToPoint(pt));
210 0 : }
211 :
212 : void
213 0 : gfxContext::Line(const gfxPoint& start, const gfxPoint& end)
214 : {
215 0 : EnsurePathBuilder();
216 0 : mPathBuilder->MoveTo(ToPoint(start));
217 0 : mPathBuilder->LineTo(ToPoint(end));
218 0 : }
219 :
220 : // XXX snapToPixels is only valid when snapping for filled
221 : // rectangles and for even-width stroked rectangles.
222 : // For odd-width stroked rectangles, we need to offset x/y by
223 : // 0.5...
224 : void
225 421 : gfxContext::Rectangle(const gfxRect& rect, bool snapToPixels)
226 : {
227 421 : Rect rec = ToRect(rect);
228 :
229 421 : if (snapToPixels) {
230 265 : gfxRect newRect(rect);
231 265 : if (UserToDevicePixelSnapped(newRect, true)) {
232 265 : gfxMatrix mat = ThebesMatrix(mTransform);
233 265 : if (mat.Invert()) {
234 : // We need the user space rect.
235 265 : rec = ToRect(mat.TransformBounds(newRect));
236 : } else {
237 0 : rec = Rect();
238 : }
239 : }
240 : }
241 :
242 421 : if (!mPathBuilder && !mPathIsRect) {
243 396 : mPathIsRect = true;
244 396 : mRect = rec;
245 396 : return;
246 : }
247 :
248 25 : EnsurePathBuilder();
249 :
250 25 : mPathBuilder->MoveTo(rec.TopLeft());
251 25 : mPathBuilder->LineTo(rec.TopRight());
252 25 : mPathBuilder->LineTo(rec.BottomRight());
253 25 : mPathBuilder->LineTo(rec.BottomLeft());
254 25 : mPathBuilder->Close();
255 : }
256 :
257 : // transform stuff
258 : void
259 11 : gfxContext::Multiply(const gfxMatrix& matrix)
260 : {
261 11 : ChangeTransform(ToMatrix(matrix) * mTransform);
262 11 : }
263 :
264 : void
265 1126 : gfxContext::SetMatrix(const gfxMatrix& matrix)
266 : {
267 1126 : ChangeTransform(ToMatrix(matrix));
268 1126 : }
269 :
270 : gfxMatrix
271 1088 : gfxContext::CurrentMatrix() const
272 : {
273 1088 : return ThebesMatrix(mTransform);
274 : }
275 :
276 : gfxPoint
277 17 : gfxContext::DeviceToUser(const gfxPoint& point) const
278 : {
279 17 : return ThebesPoint(mTransform.Inverse().TransformPoint(ToPoint(point)));
280 : }
281 :
282 : Size
283 0 : gfxContext::DeviceToUser(const Size& size) const
284 : {
285 0 : return mTransform.Inverse().TransformSize(size);
286 : }
287 :
288 : gfxRect
289 19 : gfxContext::DeviceToUser(const gfxRect& rect) const
290 : {
291 19 : return ThebesRect(mTransform.Inverse().TransformBounds(ToRect(rect)));
292 : }
293 :
294 : gfxPoint
295 1626 : gfxContext::UserToDevice(const gfxPoint& point) const
296 : {
297 1626 : return ThebesPoint(mTransform.TransformPoint(ToPoint(point)));
298 : }
299 :
300 : Size
301 0 : gfxContext::UserToDevice(const Size& size) const
302 : {
303 0 : const Matrix &matrix = mTransform;
304 :
305 0 : Size newSize;
306 0 : newSize.width = size.width * matrix._11 + size.height * matrix._12;
307 0 : newSize.height = size.width * matrix._21 + size.height * matrix._22;
308 0 : return newSize;
309 : }
310 :
311 : gfxRect
312 37 : gfxContext::UserToDevice(const gfxRect& rect) const
313 : {
314 37 : const Matrix &matrix = mTransform;
315 37 : return ThebesRect(matrix.TransformBounds(ToRect(rect)));
316 : }
317 :
318 : bool
319 516 : gfxContext::UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale) const
320 : {
321 516 : if (mDT->GetUserData(&sDisablePixelSnapping))
322 0 : return false;
323 :
324 : // if we're not at 1.0 scale, don't snap, unless we're
325 : // ignoring the scale. If we're not -just- a scale,
326 : // never snap.
327 516 : const gfxFloat epsilon = 0.0000001;
328 : #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
329 516 : Matrix mat = mTransform;
330 534 : if (!ignoreScale &&
331 54 : (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
332 36 : !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
333 0 : return false;
334 : #undef WITHIN_E
335 :
336 516 : gfxPoint p1 = UserToDevice(rect.TopLeft());
337 516 : gfxPoint p2 = UserToDevice(rect.TopRight());
338 516 : gfxPoint p3 = UserToDevice(rect.BottomRight());
339 :
340 : // Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
341 : // two opposite corners define the entire rectangle. So check if
342 : // the axis-aligned rectangle with opposite corners p1 and p3
343 : // define an axis-aligned rectangle whose other corners are p2 and p4.
344 : // We actually only need to check one of p2 and p4, since an affine
345 : // transform maps parallelograms to parallelograms.
346 516 : if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) {
347 516 : p1.Round();
348 516 : p3.Round();
349 :
350 516 : rect.MoveTo(gfxPoint(std::min(p1.x, p3.x), std::min(p1.y, p3.y)));
351 1032 : rect.SizeTo(gfxSize(std::max(p1.x, p3.x) - rect.X(),
352 1032 : std::max(p1.y, p3.y) - rect.Y()));
353 516 : return true;
354 : }
355 :
356 0 : return false;
357 : }
358 :
359 : bool
360 78 : gfxContext::UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale) const
361 : {
362 78 : if (mDT->GetUserData(&sDisablePixelSnapping))
363 0 : return false;
364 :
365 : // if we're not at 1.0 scale, don't snap, unless we're
366 : // ignoring the scale. If we're not -just- a scale,
367 : // never snap.
368 78 : const gfxFloat epsilon = 0.0000001;
369 : #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
370 78 : Matrix mat = mTransform;
371 78 : if (!ignoreScale &&
372 0 : (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
373 0 : !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
374 0 : return false;
375 : #undef WITHIN_E
376 :
377 78 : pt = UserToDevice(pt);
378 78 : pt.Round();
379 78 : return true;
380 : }
381 :
382 : void
383 0 : gfxContext::SetAntialiasMode(AntialiasMode mode)
384 : {
385 0 : CurrentState().aaMode = mode;
386 0 : }
387 :
388 : AntialiasMode
389 108 : gfxContext::CurrentAntialiasMode() const
390 : {
391 108 : return CurrentState().aaMode;
392 : }
393 :
394 : void
395 0 : gfxContext::SetDash(gfxFloat *dashes, int ndash, gfxFloat offset)
396 : {
397 0 : AzureState &state = CurrentState();
398 :
399 0 : state.dashPattern.SetLength(ndash);
400 0 : for (int i = 0; i < ndash; i++) {
401 0 : state.dashPattern[i] = Float(dashes[i]);
402 : }
403 0 : state.strokeOptions.mDashLength = ndash;
404 0 : state.strokeOptions.mDashOffset = Float(offset);
405 0 : state.strokeOptions.mDashPattern = ndash ? state.dashPattern.Elements()
406 : : nullptr;
407 0 : }
408 :
409 : bool
410 0 : gfxContext::CurrentDash(FallibleTArray<gfxFloat>& dashes, gfxFloat* offset) const
411 : {
412 0 : const AzureState &state = CurrentState();
413 0 : int count = state.strokeOptions.mDashLength;
414 :
415 0 : if (count <= 0 || !dashes.SetLength(count, fallible)) {
416 0 : return false;
417 : }
418 :
419 0 : for (int i = 0; i < count; i++) {
420 0 : dashes[i] = state.dashPattern[i];
421 : }
422 :
423 0 : *offset = state.strokeOptions.mDashOffset;
424 :
425 0 : return true;
426 : }
427 :
428 : gfxFloat
429 0 : gfxContext::CurrentDashOffset() const
430 : {
431 0 : return CurrentState().strokeOptions.mDashOffset;
432 : }
433 :
434 : void
435 0 : gfxContext::SetLineWidth(gfxFloat width)
436 : {
437 0 : CurrentState().strokeOptions.mLineWidth = Float(width);
438 0 : }
439 :
440 : gfxFloat
441 0 : gfxContext::CurrentLineWidth() const
442 : {
443 0 : return CurrentState().strokeOptions.mLineWidth;
444 : }
445 :
446 : void
447 0 : gfxContext::SetOp(CompositionOp aOp)
448 : {
449 0 : CurrentState().op = aOp;
450 0 : }
451 :
452 : CompositionOp
453 206 : gfxContext::CurrentOp() const
454 : {
455 206 : return CurrentState().op;
456 : }
457 :
458 : void
459 0 : gfxContext::SetLineCap(CapStyle cap)
460 : {
461 0 : CurrentState().strokeOptions.mLineCap = cap;
462 0 : }
463 :
464 : CapStyle
465 0 : gfxContext::CurrentLineCap() const
466 : {
467 0 : return CurrentState().strokeOptions.mLineCap;
468 : }
469 :
470 : void
471 0 : gfxContext::SetLineJoin(JoinStyle join)
472 : {
473 0 : CurrentState().strokeOptions.mLineJoin = join;
474 0 : }
475 :
476 : JoinStyle
477 0 : gfxContext::CurrentLineJoin() const
478 : {
479 0 : return CurrentState().strokeOptions.mLineJoin;
480 : }
481 :
482 : void
483 0 : gfxContext::SetMiterLimit(gfxFloat limit)
484 : {
485 0 : CurrentState().strokeOptions.mMiterLimit = Float(limit);
486 0 : }
487 :
488 : gfxFloat
489 0 : gfxContext::CurrentMiterLimit() const
490 : {
491 0 : return CurrentState().strokeOptions.mMiterLimit;
492 : }
493 :
494 : // clipping
495 : void
496 37 : gfxContext::Clip(const Rect& rect)
497 : {
498 74 : AzureState::PushedClip clip = { nullptr, rect, mTransform };
499 37 : CurrentState().pushedClips.AppendElement(clip);
500 37 : mDT->PushClipRect(rect);
501 37 : NewPath();
502 37 : }
503 :
504 : void
505 3 : gfxContext::Clip(const gfxRect& rect)
506 : {
507 3 : Clip(ToRect(rect));
508 3 : }
509 :
510 : void
511 32 : gfxContext::Clip(Path* aPath)
512 : {
513 32 : mDT->PushClip(aPath);
514 64 : AzureState::PushedClip clip = { aPath, Rect(), mTransform };
515 32 : CurrentState().pushedClips.AppendElement(clip);
516 32 : }
517 :
518 : void
519 328 : gfxContext::Clip()
520 : {
521 328 : if (mPathIsRect) {
522 321 : MOZ_ASSERT(!mTransformChanged);
523 :
524 642 : AzureState::PushedClip clip = { nullptr, mRect, mTransform };
525 321 : CurrentState().pushedClips.AppendElement(clip);
526 321 : mDT->PushClipRect(mRect);
527 : } else {
528 7 : EnsurePath();
529 7 : mDT->PushClip(mPath);
530 14 : AzureState::PushedClip clip = { mPath, Rect(), mTransform };
531 7 : CurrentState().pushedClips.AppendElement(clip);
532 : }
533 328 : }
534 :
535 : void
536 24 : gfxContext::PopClip()
537 : {
538 24 : MOZ_ASSERT(CurrentState().pushedClips.Length() > 0);
539 :
540 24 : CurrentState().pushedClips.RemoveElementAt(CurrentState().pushedClips.Length() - 1);
541 24 : mDT->PopClip();
542 24 : }
543 :
544 : gfxRect
545 225 : gfxContext::GetClipExtents()
546 : {
547 225 : Rect rect = GetAzureDeviceSpaceClipBounds();
548 :
549 225 : if (rect.width == 0 || rect.height == 0) {
550 0 : return gfxRect(0, 0, 0, 0);
551 : }
552 :
553 225 : Matrix mat = mTransform;
554 225 : mat.Invert();
555 225 : rect = mat.TransformBounds(rect);
556 :
557 225 : return ThebesRect(rect);
558 : }
559 :
560 : bool
561 0 : gfxContext::HasComplexClip() const
562 : {
563 0 : for (int i = mStateStack.Length() - 1; i >= 0; i--) {
564 0 : for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
565 0 : const AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
566 0 : if (clip.path || !clip.transform.IsRectilinear()) {
567 0 : return true;
568 : }
569 : }
570 : }
571 0 : return false;
572 : }
573 :
574 : bool
575 18 : gfxContext::ExportClip(ClipExporter& aExporter)
576 : {
577 45 : for (unsigned int i = 0; i < mStateStack.Length(); i++) {
578 54 : for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
579 27 : AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
580 27 : gfx::Matrix transform = clip.transform;
581 27 : transform.PostTranslate(-GetDeviceOffset());
582 :
583 27 : aExporter.BeginClip(transform);
584 27 : if (clip.path) {
585 6 : clip.path->StreamToSink(&aExporter);
586 : } else {
587 21 : aExporter.MoveTo(clip.rect.TopLeft());
588 21 : aExporter.LineTo(clip.rect.TopRight());
589 21 : aExporter.LineTo(clip.rect.BottomRight());
590 21 : aExporter.LineTo(clip.rect.BottomLeft());
591 21 : aExporter.Close();
592 : }
593 27 : aExporter.EndClip();
594 : }
595 : }
596 :
597 18 : return true;
598 : }
599 :
600 : bool
601 0 : gfxContext::ClipContainsRect(const gfxRect& aRect)
602 : {
603 : // Since we always return false when the clip list contains a
604 : // non-rectangular clip or a non-rectilinear transform, our 'total' clip
605 : // is always a rectangle if we hit the end of this function.
606 0 : Rect clipBounds(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
607 :
608 0 : for (unsigned int i = 0; i < mStateStack.Length(); i++) {
609 0 : for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
610 0 : AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
611 0 : if (clip.path || !clip.transform.IsRectilinear()) {
612 : // Cairo behavior is we return false if the clip contains a non-
613 : // rectangle.
614 0 : return false;
615 : } else {
616 0 : Rect clipRect = mTransform.TransformBounds(clip.rect);
617 :
618 0 : clipBounds.IntersectRect(clipBounds, clipRect);
619 : }
620 : }
621 : }
622 :
623 0 : return clipBounds.Contains(ToRect(aRect));
624 : }
625 :
626 : // rendering sources
627 :
628 : void
629 74 : gfxContext::SetColor(const Color& aColor)
630 : {
631 74 : CurrentState().pattern = nullptr;
632 74 : CurrentState().sourceSurfCairo = nullptr;
633 74 : CurrentState().sourceSurface = nullptr;
634 74 : CurrentState().color = ToDeviceColor(aColor);
635 74 : }
636 :
637 : void
638 0 : gfxContext::SetDeviceColor(const Color& aColor)
639 : {
640 0 : CurrentState().pattern = nullptr;
641 0 : CurrentState().sourceSurfCairo = nullptr;
642 0 : CurrentState().sourceSurface = nullptr;
643 0 : CurrentState().color = aColor;
644 0 : }
645 :
646 : bool
647 42 : gfxContext::GetDeviceColor(Color& aColorOut)
648 : {
649 42 : if (CurrentState().sourceSurface) {
650 0 : return false;
651 : }
652 42 : if (CurrentState().pattern) {
653 0 : return CurrentState().pattern->GetSolidColor(aColorOut);
654 : }
655 :
656 42 : aColorOut = CurrentState().color;
657 42 : return true;
658 : }
659 :
660 : void
661 0 : gfxContext::SetSource(gfxASurface *surface, const gfxPoint& offset)
662 : {
663 0 : CurrentState().surfTransform = Matrix(1.0f, 0, 0, 1.0f, Float(offset.x), Float(offset.y));
664 0 : CurrentState().pattern = nullptr;
665 0 : CurrentState().patternTransformChanged = false;
666 : // Keep the underlying cairo surface around while we keep the
667 : // sourceSurface.
668 0 : CurrentState().sourceSurfCairo = surface;
669 0 : CurrentState().sourceSurface =
670 0 : gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
671 0 : CurrentState().color = Color(0, 0, 0, 0);
672 0 : }
673 :
674 : void
675 25 : gfxContext::SetPattern(gfxPattern *pattern)
676 : {
677 25 : CurrentState().sourceSurfCairo = nullptr;
678 25 : CurrentState().sourceSurface = nullptr;
679 25 : CurrentState().patternTransformChanged = false;
680 25 : CurrentState().pattern = pattern;
681 25 : }
682 :
683 : already_AddRefed<gfxPattern>
684 0 : gfxContext::GetPattern()
685 : {
686 0 : RefPtr<gfxPattern> pat;
687 :
688 0 : AzureState &state = CurrentState();
689 0 : if (state.pattern) {
690 0 : pat = state.pattern;
691 0 : } else if (state.sourceSurface) {
692 0 : NS_ASSERTION(false, "Ugh, this isn't good.");
693 : } else {
694 0 : pat = new gfxPattern(state.color);
695 : }
696 0 : return pat.forget();
697 : }
698 :
699 : void
700 84 : gfxContext::SetFontSmoothingBackgroundColor(const Color& aColor)
701 : {
702 84 : CurrentState().fontSmoothingBackgroundColor = aColor;
703 84 : }
704 :
705 : Color
706 21 : gfxContext::GetFontSmoothingBackgroundColor()
707 : {
708 21 : return CurrentState().fontSmoothingBackgroundColor;
709 : }
710 :
711 : // masking
712 : void
713 0 : gfxContext::Mask(SourceSurface* aSurface, Float aAlpha, const Matrix& aTransform)
714 : {
715 0 : Matrix old = mTransform;
716 0 : Matrix mat = aTransform * mTransform;
717 :
718 0 : ChangeTransform(mat);
719 0 : mDT->MaskSurface(PatternFromState(this), aSurface, Point(),
720 0 : DrawOptions(aAlpha, CurrentState().op, CurrentState().aaMode));
721 0 : ChangeTransform(old);
722 0 : }
723 :
724 : void
725 0 : gfxContext::Mask(SourceSurface *surface, float alpha, const Point& offset)
726 : {
727 : // We clip here to bind to the mask surface bounds, see above.
728 0 : mDT->MaskSurface(PatternFromState(this),
729 : surface,
730 : offset,
731 0 : DrawOptions(alpha, CurrentState().op, CurrentState().aaMode));
732 0 : }
733 :
734 : void
735 0 : gfxContext::Paint(gfxFloat alpha)
736 : {
737 0 : AUTO_PROFILER_LABEL("gfxContext::Paint", GRAPHICS);
738 :
739 0 : AzureState &state = CurrentState();
740 :
741 0 : if (state.sourceSurface && !state.sourceSurfCairo &&
742 0 : !state.patternTransformChanged)
743 : {
744 : // This is the case where a PopGroupToSource has been done and this
745 : // paint is executed without changing the transform or the source.
746 0 : Matrix oldMat = mDT->GetTransform();
747 :
748 0 : IntSize surfSize = state.sourceSurface->GetSize();
749 :
750 0 : mDT->SetTransform(Matrix::Translation(-state.deviceOffset.x,
751 0 : -state.deviceOffset.y));
752 :
753 0 : mDT->DrawSurface(state.sourceSurface,
754 0 : Rect(state.sourceSurfaceDeviceOffset, Size(surfSize.width, surfSize.height)),
755 0 : Rect(Point(), Size(surfSize.width, surfSize.height)),
756 0 : DrawSurfaceOptions(), DrawOptions(alpha, GetOp()));
757 0 : mDT->SetTransform(oldMat);
758 0 : return;
759 : }
760 :
761 0 : Matrix mat = mDT->GetTransform();
762 0 : mat.Invert();
763 0 : Rect paintRect = mat.TransformBounds(Rect(Point(0, 0), Size(mDT->GetSize())));
764 :
765 0 : mDT->FillRect(paintRect, PatternFromState(this),
766 0 : DrawOptions(Float(alpha), GetOp()));
767 : }
768 :
769 : void
770 19 : gfxContext::PushGroupForBlendBack(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform)
771 : {
772 19 : Save();
773 19 : mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform);
774 19 : }
775 :
776 : static gfxRect
777 0 : GetRoundOutDeviceClipExtents(gfxContext* aCtx)
778 : {
779 0 : gfxContextMatrixAutoSaveRestore save(aCtx);
780 0 : aCtx->SetMatrix(gfxMatrix());
781 0 : gfxRect r = aCtx->GetClipExtents();
782 0 : r.RoundOut();
783 0 : return r;
784 : }
785 :
786 : void
787 3 : gfxContext::PushGroupAndCopyBackground(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform)
788 : {
789 3 : IntRect clipExtents;
790 3 : if (mDT->GetFormat() != SurfaceFormat::B8G8R8X8) {
791 0 : gfxRect clipRect = GetRoundOutDeviceClipExtents(this);
792 0 : clipExtents = IntRect::Truncate(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
793 : }
794 3 : bool pushOpaqueWithCopiedBG = (mDT->GetFormat() == SurfaceFormat::B8G8R8X8 ||
795 6 : mDT->GetOpaqueRect().Contains(clipExtents)) &&
796 6 : !mDT->GetUserData(&sDontUseAsSourceKey);
797 :
798 3 : Save();
799 :
800 3 : if (pushOpaqueWithCopiedBG) {
801 3 : mDT->PushLayer(true, aOpacity, aMask, aMaskTransform, IntRect(), true);
802 : } else {
803 0 : mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform, IntRect(), false);
804 : }
805 3 : }
806 :
807 : void
808 22 : gfxContext::PopGroupAndBlend()
809 : {
810 22 : mDT->PopLayer();
811 22 : Restore();
812 22 : }
813 :
814 : #ifdef MOZ_DUMP_PAINTING
815 : void
816 0 : gfxContext::WriteAsPNG(const char* aFile)
817 : {
818 0 : gfxUtils::WriteAsPNG(mDT, aFile);
819 0 : }
820 :
821 : void
822 0 : gfxContext::DumpAsDataURI()
823 : {
824 0 : gfxUtils::DumpAsDataURI(mDT);
825 0 : }
826 :
827 : void
828 0 : gfxContext::CopyAsDataURI()
829 : {
830 0 : gfxUtils::CopyAsDataURI(mDT);
831 0 : }
832 : #endif
833 :
834 : void
835 17 : gfxContext::EnsurePath()
836 : {
837 17 : if (mPathBuilder) {
838 7 : mPath = mPathBuilder->Finish();
839 7 : mPathBuilder = nullptr;
840 : }
841 :
842 17 : if (mPath) {
843 17 : if (mTransformChanged) {
844 0 : Matrix mat = mTransform;
845 0 : mat.Invert();
846 0 : mat = mPathTransform * mat;
847 0 : mPathBuilder = mPath->TransformedCopyToBuilder(mat);
848 0 : mPath = mPathBuilder->Finish();
849 0 : mPathBuilder = nullptr;
850 :
851 0 : mTransformChanged = false;
852 : }
853 17 : return;
854 : }
855 :
856 0 : EnsurePathBuilder();
857 0 : mPath = mPathBuilder->Finish();
858 0 : mPathBuilder = nullptr;
859 : }
860 :
861 : void
862 25 : gfxContext::EnsurePathBuilder()
863 : {
864 25 : if (mPathBuilder && !mTransformChanged) {
865 36 : return;
866 : }
867 :
868 7 : if (mPath) {
869 0 : if (!mTransformChanged) {
870 0 : mPathBuilder = mPath->CopyToBuilder();
871 0 : mPath = nullptr;
872 : } else {
873 0 : Matrix invTransform = mTransform;
874 0 : invTransform.Invert();
875 0 : Matrix toNewUS = mPathTransform * invTransform;
876 0 : mPathBuilder = mPath->TransformedCopyToBuilder(toNewUS);
877 : }
878 0 : return;
879 : }
880 :
881 14 : DebugOnly<PathBuilder*> oldPath = mPathBuilder.get();
882 :
883 7 : if (!mPathBuilder) {
884 7 : mPathBuilder = mDT->CreatePathBuilder(FillRule::FILL_WINDING);
885 :
886 7 : if (mPathIsRect) {
887 7 : mPathBuilder->MoveTo(mRect.TopLeft());
888 7 : mPathBuilder->LineTo(mRect.TopRight());
889 7 : mPathBuilder->LineTo(mRect.BottomRight());
890 7 : mPathBuilder->LineTo(mRect.BottomLeft());
891 7 : mPathBuilder->Close();
892 : }
893 : }
894 :
895 7 : if (mTransformChanged) {
896 : // This could be an else if since this should never happen when
897 : // mPathBuilder is nullptr and mPath is nullptr. But this way we can
898 : // assert if all the state is as expected.
899 0 : MOZ_ASSERT(oldPath);
900 0 : MOZ_ASSERT(!mPathIsRect);
901 :
902 0 : Matrix invTransform = mTransform;
903 0 : invTransform.Invert();
904 0 : Matrix toNewUS = mPathTransform * invTransform;
905 :
906 0 : RefPtr<Path> path = mPathBuilder->Finish();
907 0 : if (!path) {
908 0 : gfxCriticalError() << "gfxContext::EnsurePathBuilder failed in PathBuilder::Finish";
909 : }
910 0 : mPathBuilder = path->TransformedCopyToBuilder(toNewUS);
911 : }
912 :
913 7 : mPathIsRect = false;
914 : }
915 :
916 : void
917 78 : gfxContext::FillAzure(const Pattern& aPattern, Float aOpacity)
918 : {
919 78 : AzureState &state = CurrentState();
920 :
921 78 : CompositionOp op = GetOp();
922 :
923 78 : if (mPathIsRect) {
924 68 : MOZ_ASSERT(!mTransformChanged);
925 :
926 68 : if (op == CompositionOp::OP_SOURCE) {
927 : // Emulate cairo operator source which is bound by mask!
928 0 : mDT->ClearRect(mRect);
929 0 : mDT->FillRect(mRect, aPattern, DrawOptions(aOpacity));
930 : } else {
931 68 : mDT->FillRect(mRect, aPattern, DrawOptions(aOpacity, op, state.aaMode));
932 : }
933 : } else {
934 10 : EnsurePath();
935 10 : mDT->Fill(mPath, aPattern, DrawOptions(aOpacity, op, state.aaMode));
936 : }
937 78 : }
938 :
939 : CompositionOp
940 78 : gfxContext::GetOp()
941 : {
942 78 : if (CurrentState().op != CompositionOp::OP_SOURCE) {
943 78 : return CurrentState().op;
944 : }
945 :
946 0 : AzureState &state = CurrentState();
947 0 : if (state.pattern) {
948 0 : if (state.pattern->IsOpaque()) {
949 0 : return CompositionOp::OP_OVER;
950 : } else {
951 0 : return CompositionOp::OP_SOURCE;
952 : }
953 0 : } else if (state.sourceSurface) {
954 0 : if (state.sourceSurface->GetFormat() == SurfaceFormat::B8G8R8X8) {
955 0 : return CompositionOp::OP_OVER;
956 : } else {
957 0 : return CompositionOp::OP_SOURCE;
958 : }
959 : } else {
960 0 : if (state.color.a > 0.999) {
961 0 : return CompositionOp::OP_OVER;
962 : } else {
963 0 : return CompositionOp::OP_SOURCE;
964 : }
965 : }
966 : }
967 :
968 : /* SVG font code can change the transform after having set the pattern on the
969 : * context. When the pattern is set it is in user space, if the transform is
970 : * changed after doing so the pattern needs to be converted back into userspace.
971 : * We just store the old pattern transform here so that we only do the work
972 : * needed here if the pattern is actually used.
973 : * We need to avoid doing this when this ChangeTransform comes from a restore,
974 : * since the current pattern and the current transform are both part of the
975 : * state we know the new CurrentState()'s values are valid. But if we assume
976 : * a change they might become invalid since patternTransformChanged is part of
977 : * the state and might be false for the restored AzureState.
978 : */
979 : void
980 1511 : gfxContext::ChangeTransform(const Matrix &aNewMatrix, bool aUpdatePatternTransform)
981 : {
982 1511 : AzureState &state = CurrentState();
983 :
984 2648 : if (aUpdatePatternTransform && (state.pattern || state.sourceSurface)
985 1562 : && !state.patternTransformChanged) {
986 25 : state.patternTransform = GetDTTransform();
987 25 : state.patternTransformChanged = true;
988 : }
989 :
990 1511 : if (mPathIsRect) {
991 718 : Matrix invMatrix = aNewMatrix;
992 :
993 718 : invMatrix.Invert();
994 :
995 718 : Matrix toNewUS = mTransform * invMatrix;
996 :
997 718 : if (toNewUS.IsRectilinear()) {
998 718 : mRect = toNewUS.TransformBounds(mRect);
999 718 : mRect.NudgeToIntegers();
1000 : } else {
1001 0 : mPathBuilder = mDT->CreatePathBuilder(FillRule::FILL_WINDING);
1002 :
1003 0 : mPathBuilder->MoveTo(toNewUS.TransformPoint(mRect.TopLeft()));
1004 0 : mPathBuilder->LineTo(toNewUS.TransformPoint(mRect.TopRight()));
1005 0 : mPathBuilder->LineTo(toNewUS.TransformPoint(mRect.BottomRight()));
1006 0 : mPathBuilder->LineTo(toNewUS.TransformPoint(mRect.BottomLeft()));
1007 0 : mPathBuilder->Close();
1008 :
1009 0 : mPathIsRect = false;
1010 : }
1011 :
1012 : // No need to consider the transform changed now!
1013 718 : mTransformChanged = false;
1014 793 : } else if ((mPath || mPathBuilder) && !mTransformChanged) {
1015 17 : mTransformChanged = true;
1016 17 : mPathTransform = mTransform;
1017 : }
1018 :
1019 1511 : mTransform = aNewMatrix;
1020 :
1021 1511 : mDT->SetTransform(GetDTTransform());
1022 1511 : }
1023 :
1024 : Rect
1025 225 : gfxContext::GetAzureDeviceSpaceClipBounds()
1026 : {
1027 450 : Rect rect(CurrentState().deviceOffset.x, CurrentState().deviceOffset.y,
1028 675 : Float(mDT->GetSize().width), Float(mDT->GetSize().height));
1029 795 : for (unsigned int i = 0; i < mStateStack.Length(); i++) {
1030 1031 : for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
1031 461 : AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
1032 461 : if (clip.path) {
1033 58 : Rect bounds = clip.path->GetBounds(clip.transform);
1034 58 : rect.IntersectRect(rect, bounds);
1035 : } else {
1036 403 : rect.IntersectRect(rect, clip.transform.TransformBounds(clip.rect));
1037 : }
1038 : }
1039 : }
1040 :
1041 225 : return rect;
1042 : }
1043 :
1044 : Point
1045 45 : gfxContext::GetDeviceOffset() const
1046 : {
1047 45 : return CurrentState().deviceOffset;
1048 : }
1049 :
1050 : Matrix
1051 0 : gfxContext::GetDeviceTransform() const
1052 : {
1053 0 : return Matrix::Translation(-CurrentState().deviceOffset.x,
1054 0 : -CurrentState().deviceOffset.y);
1055 : }
1056 :
1057 : Matrix
1058 1687 : gfxContext::GetDTTransform() const
1059 : {
1060 1687 : Matrix mat = mTransform;
1061 1687 : mat._31 -= CurrentState().deviceOffset.x;
1062 1687 : mat._32 -= CurrentState().deviceOffset.y;
1063 1687 : return mat;
1064 : }
|