LCOV - code coverage report
Current view: top level - gfx/thebes - gfxContext.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 293 540 54.3 %
Date: 2017-07-14 16:53:18 Functions: 46 80 57.5 %
Legend: Lines: hit not hit

          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             : }

Generated by: LCOV version 1.13