Coverage Report

Created: 2018-09-25 14:53

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