Source code

Revision control

Other Tools

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