gfx/thebes/gfxContext.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  * This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #ifdef _MSC_VER
     7 #define _USE_MATH_DEFINES
     8 #endif
     9 #include <math.h>
    11 #include "mozilla/Alignment.h"
    13 #include "cairo.h"
    15 #include "gfxContext.h"
    17 #include "gfxColor.h"
    18 #include "gfxMatrix.h"
    19 #include "gfxASurface.h"
    20 #include "gfxPattern.h"
    21 #include "gfxPlatform.h"
    22 #include "gfxTeeSurface.h"
    23 #include "GeckoProfiler.h"
    24 #include "gfx2DGlue.h"
    25 #include "mozilla/gfx/PathHelpers.h"
    26 #include <algorithm>
    28 #if CAIRO_HAS_DWRITE_FONT
    29 #include "gfxWindowsPlatform.h"
    30 #endif
    32 using namespace mozilla;
    33 using namespace mozilla::gfx;
    35 UserDataKey gfxContext::sDontUseAsSourceKey;
    37 /* This class lives on the stack and allows gfxContext users to easily, and
    38  * performantly get a gfx::Pattern to use for drawing in their current context.
    39  */
    40 class GeneralPattern
    41 {
    42 public:    
    43   GeneralPattern(gfxContext *aContext) : mContext(aContext), mPattern(nullptr) {}
    44   ~GeneralPattern() { if (mPattern) { mPattern->~Pattern(); } }
    46   operator mozilla::gfx::Pattern&()
    47   {
    48     gfxContext::AzureState &state = mContext->CurrentState();
    50     if (state.pattern) {
    51       return *state.pattern->GetPattern(mContext->mDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
    52     } else if (state.sourceSurface) {
    53       Matrix transform = state.surfTransform;
    55       if (state.patternTransformChanged) {
    56         Matrix mat = mContext->GetDTTransform();
    57         mat.Invert();
    59         transform = transform * state.patternTransform * mat;
    60       }
    62       mPattern = new (mSurfacePattern.addr())
    63         SurfacePattern(state.sourceSurface, ExtendMode::CLAMP, transform);
    64       return *mPattern;
    65     } else {
    66       mPattern = new (mColorPattern.addr())
    67         ColorPattern(state.color);
    68       return *mPattern;
    69     }
    70   }
    72 private:
    73   union {
    74     mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern;
    75     mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern;
    76   };
    78   gfxContext *mContext;
    79   Pattern *mPattern;
    80 };
    82 gfxContext::gfxContext(gfxASurface *surface)
    83   : mRefCairo(nullptr)
    84   , mSurface(surface)
    85 {
    86   MOZ_COUNT_CTOR(gfxContext);
    88   mCairo = cairo_create(surface->CairoSurface());
    89   mFlags = surface->GetDefaultContextFlags();
    90   if (mSurface->GetRotateForLandscape()) {
    91     // Rotate page 90 degrees to draw landscape page on portrait paper
    92     gfxIntSize size = mSurface->GetSize();
    93     Translate(gfxPoint(0, size.width));
    94     gfxMatrix matrix(0, -1,
    95                       1,  0,
    96                       0,  0);
    97     Multiply(matrix);
    98   }
    99 }
   101 gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
   102   : mPathIsRect(false)
   103   , mTransformChanged(false)
   104   , mCairo(nullptr)
   105   , mRefCairo(nullptr)
   106   , mSurface(nullptr)
   107   , mFlags(0)
   108   , mDT(aTarget)
   109   , mOriginalDT(aTarget)
   110 {
   111   MOZ_COUNT_CTOR(gfxContext);
   113   mStateStack.SetLength(1);
   114   CurrentState().drawTarget = mDT;
   115   CurrentState().deviceOffset = aDeviceOffset;
   116   mDT->SetTransform(Matrix());
   117 }
   119 /* static */ already_AddRefed<gfxContext>
   120 gfxContext::ContextForDrawTarget(DrawTarget* aTarget)
   121 {
   122   Matrix transform = aTarget->GetTransform();
   123   nsRefPtr<gfxContext> result = new gfxContext(aTarget);
   124   result->SetMatrix(ThebesMatrix(transform));
   125   return result.forget();
   126 }
   128 gfxContext::~gfxContext()
   129 {
   130   if (mCairo) {
   131     cairo_destroy(mCairo);
   132   }
   133   if (mRefCairo) {
   134     cairo_destroy(mRefCairo);
   135   }
   136   if (mDT) {
   137     for (int i = mStateStack.Length() - 1; i >= 0; i--) {
   138       for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
   139         mDT->PopClip();
   140       }
   142       if (mStateStack[i].clipWasReset) {
   143         break;
   144       }
   145     }
   146     mDT->Flush();
   147   }
   148   MOZ_COUNT_DTOR(gfxContext);
   149 }
   151 gfxASurface *
   152 gfxContext::OriginalSurface()
   153 {
   154     if (mCairo || mSurface) {
   155         return mSurface;
   156     }
   158     if (mOriginalDT && mOriginalDT->GetType() == BackendType::CAIRO) {
   159         cairo_surface_t *s =
   160             (cairo_surface_t*)mOriginalDT->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE);
   161         if (s) {
   162             mSurface = gfxASurface::Wrap(s);
   163             return mSurface;
   164         }
   165     }
   166     return nullptr;
   167 }
   169 already_AddRefed<gfxASurface>
   170 gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy)
   171 {
   172   if (mCairo) {
   173     cairo_surface_t *s = cairo_get_group_target(mCairo);
   174     if (s == mSurface->CairoSurface()) {
   175         if (dx && dy)
   176             cairo_surface_get_device_offset(s, dx, dy);
   177         nsRefPtr<gfxASurface> ret = mSurface;
   178         return ret.forget();
   179     }
   181     if (dx && dy)
   182         cairo_surface_get_device_offset(s, dx, dy);
   183     return gfxASurface::Wrap(s);
   184   } else {
   185     if (mDT->GetType() == BackendType::CAIRO) {
   186         cairo_surface_t *s =
   187             (cairo_surface_t*)mDT->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE);
   188         if (s) {
   189             if (dx && dy) {
   190                 *dx = -CurrentState().deviceOffset.x;
   191                 *dy = -CurrentState().deviceOffset.y;
   192             }
   193             return gfxASurface::Wrap(s);
   194         }
   195     }
   197     if (dx && dy) {
   198       *dx = *dy = 0;
   199     }
   200     // An Azure context doesn't have a surface backing it.
   201     return nullptr;
   202   }
   203 }
   205 cairo_t *
   206 gfxContext::GetCairo()
   207 {
   208   if (mCairo) {
   209     return mCairo;
   210   }
   212   if (mDT->GetType() == BackendType::CAIRO) {
   213     cairo_t *ctx =
   214       (cairo_t*)mDT->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT);
   215     if (ctx) {
   216       return ctx;
   217     }
   218   }
   220   if (mRefCairo) {
   221     // Set transform!
   222     return mRefCairo;
   223   }
   225   mRefCairo = cairo_create(gfxPlatform::GetPlatform()->ScreenReferenceSurface()->CairoSurface()); 
   227   return mRefCairo;
   228 }
   230 void
   231 gfxContext::Save()
   232 {
   233   if (mCairo) {
   234     cairo_save(mCairo);
   235   } else {
   236     CurrentState().transform = mTransform;
   237     mStateStack.AppendElement(AzureState(CurrentState()));
   238     CurrentState().clipWasReset = false;
   239     CurrentState().pushedClips.Clear();
   240   }
   241 }
   243 void
   244 gfxContext::Restore()
   245 {
   246   if (mCairo) {
   247     cairo_restore(mCairo);
   248   } else {
   249     for (unsigned int c = 0; c < CurrentState().pushedClips.Length(); c++) {
   250       mDT->PopClip();
   251     }
   253     if (CurrentState().clipWasReset &&
   254         CurrentState().drawTarget == mStateStack[mStateStack.Length() - 2].drawTarget) {
   255       PushClipsToDT(mDT);
   256     }
   258     mStateStack.RemoveElementAt(mStateStack.Length() - 1);
   260     mDT = CurrentState().drawTarget;
   262     ChangeTransform(CurrentState().transform, false);
   263   }
   264 }
   266 // drawing
   267 void
   268 gfxContext::NewPath()
   269 {
   270   if (mCairo) {
   271     cairo_new_path(mCairo);
   272   } else {
   273     mPath = nullptr;
   274     mPathBuilder = nullptr;
   275     mPathIsRect = false;
   276     mTransformChanged = false;
   277   }
   278 }
   280 void
   281 gfxContext::ClosePath()
   282 {
   283   if (mCairo) {
   284     cairo_close_path(mCairo);
   285   } else {
   286     EnsurePathBuilder();
   287     mPathBuilder->Close();
   288   }
   289 }
   291 already_AddRefed<gfxPath> gfxContext::CopyPath()
   292 {
   293   nsRefPtr<gfxPath> path;
   294   if (mCairo) {
   295     path = new gfxPath(cairo_copy_path(mCairo));
   296   } else {
   297     EnsurePath();
   298     path = new gfxPath(mPath);
   299   }
   300   return path.forget();
   301 }
   303 void gfxContext::SetPath(gfxPath* path)
   304 {
   305   if (mCairo) {
   306     cairo_new_path(mCairo);
   307     if (path->mPath->status == CAIRO_STATUS_SUCCESS && path->mPath->num_data != 0)
   308         cairo_append_path(mCairo, path->mPath);
   309   } else {
   310     MOZ_ASSERT(path->mMoz2DPath, "Can't mix cairo and azure paths!");
   311     MOZ_ASSERT(path->mMoz2DPath->GetBackendType() == mDT->GetType());
   312     mPath = path->mMoz2DPath;
   313     mPathBuilder = nullptr;
   314     mPathIsRect = false;
   315     mTransformChanged = false;
   316   }
   317 }
   319 gfxPoint
   320 gfxContext::CurrentPoint()
   321 {
   322   if (mCairo) {
   323     double x, y;
   324     cairo_get_current_point(mCairo, &x, &y);
   325     return gfxPoint(x, y);
   326   } else {
   327     EnsurePathBuilder();
   328     return ThebesPoint(mPathBuilder->CurrentPoint());
   329   }
   330 }
   332 void
   333 gfxContext::Stroke()
   334 {
   335   if (mCairo) {
   336     cairo_stroke_preserve(mCairo);
   337   } else {
   338     AzureState &state = CurrentState();
   339     if (mPathIsRect) {
   340       MOZ_ASSERT(!mTransformChanged);
   342       mDT->StrokeRect(mRect, GeneralPattern(this),
   343                       state.strokeOptions,
   344                       DrawOptions(1.0f, GetOp(), state.aaMode));
   345     } else {
   346       EnsurePath();
   348       mDT->Stroke(mPath, GeneralPattern(this), state.strokeOptions,
   349                   DrawOptions(1.0f, GetOp(), state.aaMode));
   350     }
   351   }
   352 }
   354 void
   355 gfxContext::Fill()
   356 {
   357   PROFILER_LABEL("gfxContext", "Fill");
   358   if (mCairo) {
   359     cairo_fill_preserve(mCairo);
   360   } else {
   361     FillAzure(1.0f);
   362   }
   363 }
   365 void
   366 gfxContext::FillWithOpacity(gfxFloat aOpacity)
   367 {
   368   if (mCairo) {
   369     // This method exists in the hope that one day cairo gets a direct
   370     // API for this, and then we would change this method to use that
   371     // API instead.
   372     if (aOpacity != 1.0) {
   373       gfxContextAutoSaveRestore saveRestore(this);
   374       Clip();
   375       Paint(aOpacity);
   376     } else {
   377       Fill();
   378     }
   379   } else {
   380     FillAzure(Float(aOpacity));
   381   }
   382 }
   384 void
   385 gfxContext::MoveTo(const gfxPoint& pt)
   386 {
   387   if (mCairo) {
   388     cairo_move_to(mCairo, pt.x, pt.y);
   389   } else {
   390     EnsurePathBuilder();
   391     mPathBuilder->MoveTo(ToPoint(pt));
   392   }
   393 }
   395 void
   396 gfxContext::NewSubPath()
   397 {
   398   if (mCairo) {
   399     cairo_new_sub_path(mCairo);
   400   } else {
   401     // XXX - This has no users, we should kill it, it should be equivelant to a
   402     // MoveTo to the path's current point.
   403   }
   404 }
   406 void
   407 gfxContext::LineTo(const gfxPoint& pt)
   408 {
   409   if (mCairo) {
   410     cairo_line_to(mCairo, pt.x, pt.y);
   411   } else {
   412     EnsurePathBuilder();
   413     mPathBuilder->LineTo(ToPoint(pt));
   414   }
   415 }
   417 void
   418 gfxContext::CurveTo(const gfxPoint& pt1, const gfxPoint& pt2, const gfxPoint& pt3)
   419 {
   420   if (mCairo) {
   421     cairo_curve_to(mCairo, pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y);
   422   } else {
   423     EnsurePathBuilder();
   424     mPathBuilder->BezierTo(ToPoint(pt1), ToPoint(pt2), ToPoint(pt3));
   425   }
   426 }
   428 void
   429 gfxContext::QuadraticCurveTo(const gfxPoint& pt1, const gfxPoint& pt2)
   430 {
   431   if (mCairo) {
   432     double cx, cy;
   433     cairo_get_current_point(mCairo, &cx, &cy);
   434     cairo_curve_to(mCairo,
   435                    (cx + pt1.x * 2.0) / 3.0,
   436                    (cy + pt1.y * 2.0) / 3.0,
   437                    (pt1.x * 2.0 + pt2.x) / 3.0,
   438                    (pt1.y * 2.0 + pt2.y) / 3.0,
   439                    pt2.x,
   440                    pt2.y);
   441   } else {
   442     EnsurePathBuilder();
   443     mPathBuilder->QuadraticBezierTo(ToPoint(pt1), ToPoint(pt2));
   444   }
   445 }
   447 void
   448 gfxContext::Arc(const gfxPoint& center, gfxFloat radius,
   449                 gfxFloat angle1, gfxFloat angle2)
   450 {
   451   if (mCairo) {
   452     cairo_arc(mCairo, center.x, center.y, radius, angle1, angle2);
   453   } else {
   454     EnsurePathBuilder();
   455     mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle1), Float(angle2));
   456   }
   457 }
   459 void
   460 gfxContext::NegativeArc(const gfxPoint& center, gfxFloat radius,
   461                         gfxFloat angle1, gfxFloat angle2)
   462 {
   463   if (mCairo) {
   464     cairo_arc_negative(mCairo, center.x, center.y, radius, angle1, angle2);
   465   } else {
   466     EnsurePathBuilder();
   467     mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle2), Float(angle1));
   468   }
   469 }
   471 void
   472 gfxContext::Line(const gfxPoint& start, const gfxPoint& end)
   473 {
   474   if (mCairo) {
   475     MoveTo(start);
   476     LineTo(end);
   477   } else {
   478     EnsurePathBuilder();
   479     mPathBuilder->MoveTo(ToPoint(start));
   480     mPathBuilder->LineTo(ToPoint(end));
   481   }
   482 }
   484 // XXX snapToPixels is only valid when snapping for filled
   485 // rectangles and for even-width stroked rectangles.
   486 // For odd-width stroked rectangles, we need to offset x/y by
   487 // 0.5...
   488 void
   489 gfxContext::Rectangle(const gfxRect& rect, bool snapToPixels)
   490 {
   491   if (mCairo) {
   492     if (snapToPixels) {
   493         gfxRect snappedRect(rect);
   495         if (UserToDevicePixelSnapped(snappedRect, true))
   496         {
   497             cairo_matrix_t mat;
   498             cairo_get_matrix(mCairo, &mat);
   499             cairo_identity_matrix(mCairo);
   500             Rectangle(snappedRect);
   501             cairo_set_matrix(mCairo, &mat);
   503             return;
   504         }
   505     }
   507     cairo_rectangle(mCairo, rect.X(), rect.Y(), rect.Width(), rect.Height());
   508   } else {
   509     Rect rec = ToRect(rect);
   511     if (snapToPixels) {
   512       gfxRect newRect(rect);
   513       if (UserToDevicePixelSnapped(newRect, true)) {
   514         gfxMatrix mat = ThebesMatrix(mTransform);
   515         mat.Invert();
   517         // We need the user space rect.
   518         rec = ToRect(mat.TransformBounds(newRect));
   519       }
   520     }
   522     if (!mPathBuilder && !mPathIsRect) {
   523       mPathIsRect = true;
   524       mRect = rec;
   525       return;
   526     }
   528     EnsurePathBuilder();
   530     mPathBuilder->MoveTo(rec.TopLeft());
   531     mPathBuilder->LineTo(rec.TopRight());
   532     mPathBuilder->LineTo(rec.BottomRight());
   533     mPathBuilder->LineTo(rec.BottomLeft());
   534     mPathBuilder->Close();
   535   }
   536 }
   538 void
   539 gfxContext::Ellipse(const gfxPoint& center, const gfxSize& dimensions)
   540 {
   541   gfxSize halfDim = dimensions / 2.0;
   542   gfxRect r(center - gfxPoint(halfDim.width, halfDim.height), dimensions);
   543   gfxCornerSizes c(halfDim, halfDim, halfDim, halfDim);
   545   RoundedRectangle (r, c);
   546 }
   548 void
   549 gfxContext::Polygon(const gfxPoint *points, uint32_t numPoints)
   550 {
   551   if (mCairo) {
   552     if (numPoints == 0)
   553         return;
   555     cairo_move_to(mCairo, points[0].x, points[0].y);
   556     for (uint32_t i = 1; i < numPoints; ++i) {
   557         cairo_line_to(mCairo, points[i].x, points[i].y);
   558     }
   559   } else {
   560     if (numPoints == 0) {
   561       return;
   562     }
   564     EnsurePathBuilder();
   566     mPathBuilder->MoveTo(ToPoint(points[0]));
   567     for (uint32_t i = 1; i < numPoints; i++) {
   568       mPathBuilder->LineTo(ToPoint(points[i]));
   569     }
   570   }
   571 }
   573 void
   574 gfxContext::DrawSurface(gfxASurface *surface, const gfxSize& size)
   575 {
   576   if (mCairo) {
   577     cairo_save(mCairo);
   578     cairo_set_source_surface(mCairo, surface->CairoSurface(), 0, 0);
   579     cairo_new_path(mCairo);
   581     // pixel-snap this
   582     Rectangle(gfxRect(gfxPoint(0.0, 0.0), size), true);
   584     cairo_fill(mCairo);
   585     cairo_restore(mCairo);
   586   } else {
   587     // Lifetime needs to be limited here since we may wrap surface's data.
   588     RefPtr<SourceSurface> surf =
   589       gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
   591     if (!surf) {
   592       return;
   593     }
   595     Rect rect(0, 0, Float(size.width), Float(size.height));
   596     rect.Intersect(Rect(0, 0, Float(surf->GetSize().width), Float(surf->GetSize().height)));
   598     // XXX - Should fix pixel snapping.
   599     mDT->DrawSurface(surf, rect, rect);
   600   }
   601 }
   603 // transform stuff
   604 void
   605 gfxContext::Translate(const gfxPoint& pt)
   606 {
   607   if (mCairo) {
   608     cairo_translate(mCairo, pt.x, pt.y);
   609   } else {
   610     Matrix newMatrix = mTransform;
   612     ChangeTransform(newMatrix.Translate(Float(pt.x), Float(pt.y)));
   613   }
   614 }
   616 void
   617 gfxContext::Scale(gfxFloat x, gfxFloat y)
   618 {
   619   if (mCairo) {
   620     cairo_scale(mCairo, x, y);
   621   } else {
   622     Matrix newMatrix = mTransform;
   624     ChangeTransform(newMatrix.Scale(Float(x), Float(y)));
   625   }
   626 }
   628 void
   629 gfxContext::Rotate(gfxFloat angle)
   630 {
   631   if (mCairo) {
   632     cairo_rotate(mCairo, angle);
   633   } else {
   634     Matrix rotation = Matrix::Rotation(Float(angle));
   635     ChangeTransform(rotation * mTransform);
   636   }
   637 }
   639 void
   640 gfxContext::Multiply(const gfxMatrix& matrix)
   641 {
   642   if (mCairo) {
   643     const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
   644     cairo_transform(mCairo, &mat);
   645   } else {
   646     ChangeTransform(ToMatrix(matrix) * mTransform);
   647   }
   648 }
   650 void
   651 gfxContext::MultiplyAndNudgeToIntegers(const gfxMatrix& matrix)
   652 {
   653   if (mCairo) {
   654     const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
   655     cairo_transform(mCairo, &mat);
   656     // XXX nudging to integers not currently supported for Thebes
   657   } else {
   658     Matrix transform = ToMatrix(matrix) * mTransform;
   659     transform.NudgeToIntegers();
   660     ChangeTransform(transform);
   661   }
   662 }
   664 void
   665 gfxContext::SetMatrix(const gfxMatrix& matrix)
   666 {
   667   if (mCairo) {
   668     const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
   669     cairo_set_matrix(mCairo, &mat);
   670   } else {
   671     ChangeTransform(ToMatrix(matrix));
   672   }
   673 }
   675 void
   676 gfxContext::IdentityMatrix()
   677 {
   678   if (mCairo) {
   679     cairo_identity_matrix(mCairo);
   680   } else {
   681     ChangeTransform(Matrix());
   682   }
   683 }
   685 gfxMatrix
   686 gfxContext::CurrentMatrix() const
   687 {
   688   if (mCairo) {
   689     cairo_matrix_t mat;
   690     cairo_get_matrix(mCairo, &mat);
   691     return gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat));
   692   } else {
   693     return ThebesMatrix(mTransform);
   694   }
   695 }
   697 void
   698 gfxContext::NudgeCurrentMatrixToIntegers()
   699 {
   700   if (mCairo) {
   701     cairo_matrix_t mat;
   702     cairo_get_matrix(mCairo, &mat);
   703     gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat)).NudgeToIntegers();
   704     cairo_set_matrix(mCairo, &mat);
   705   } else {
   706     gfxMatrix matrix = ThebesMatrix(mTransform);
   707     matrix.NudgeToIntegers();
   708     ChangeTransform(ToMatrix(matrix));
   709   }
   710 }
   712 gfxPoint
   713 gfxContext::DeviceToUser(const gfxPoint& point) const
   714 {
   715   if (mCairo) {
   716     gfxPoint ret = point;
   717     cairo_device_to_user(mCairo, &ret.x, &ret.y);
   718     return ret;
   719   } else {
   720     Matrix matrix = mTransform;
   722     matrix.Invert();
   724     return ThebesPoint(matrix * ToPoint(point));
   725   }
   726 }
   728 gfxSize
   729 gfxContext::DeviceToUser(const gfxSize& size) const
   730 {
   731   if (mCairo) {
   732     gfxSize ret = size;
   733     cairo_device_to_user_distance(mCairo, &ret.width, &ret.height);
   734     return ret;
   735   } else {
   736     Matrix matrix = mTransform;
   738     matrix.Invert();
   740     return ThebesSize(matrix * ToSize(size));
   741   }
   742 }
   744 gfxRect
   745 gfxContext::DeviceToUser(const gfxRect& rect) const
   746 {
   747   if (mCairo) {
   748     gfxRect ret = rect;
   749     cairo_device_to_user(mCairo, &ret.x, &ret.y);
   750     cairo_device_to_user_distance(mCairo, &ret.width, &ret.height);
   751     return ret;
   752   } else {
   753     Matrix matrix = mTransform;
   755     matrix.Invert();
   757     return ThebesRect(matrix.TransformBounds(ToRect(rect)));
   758   }
   759 }
   761 gfxPoint
   762 gfxContext::UserToDevice(const gfxPoint& point) const
   763 {
   764   if (mCairo) {
   765     gfxPoint ret = point;
   766     cairo_user_to_device(mCairo, &ret.x, &ret.y);
   767     return ret;
   768   } else {
   769     return ThebesPoint(mTransform * ToPoint(point));
   770   }
   771 }
   773 gfxSize
   774 gfxContext::UserToDevice(const gfxSize& size) const
   775 {
   776   if (mCairo) {
   777     gfxSize ret = size;
   778     cairo_user_to_device_distance(mCairo, &ret.width, &ret.height);
   779     return ret;
   780   } else {
   781     const Matrix &matrix = mTransform;
   783     gfxSize newSize;
   784     newSize.width = size.width * matrix._11 + size.height * matrix._12;
   785     newSize.height = size.width * matrix._21 + size.height * matrix._22;
   786     return newSize;
   787   }
   788 }
   790 gfxRect
   791 gfxContext::UserToDevice(const gfxRect& rect) const
   792 {
   793   if (mCairo) {
   794     double xmin = rect.X(), ymin = rect.Y(), xmax = rect.XMost(), ymax = rect.YMost();
   796     double x[3], y[3];
   797     x[0] = xmin;  y[0] = ymax;
   798     x[1] = xmax;  y[1] = ymax;
   799     x[2] = xmax;  y[2] = ymin;
   801     cairo_user_to_device(mCairo, &xmin, &ymin);
   802     xmax = xmin;
   803     ymax = ymin;
   804     for (int i = 0; i < 3; i++) {
   805         cairo_user_to_device(mCairo, &x[i], &y[i]);
   806         xmin = std::min(xmin, x[i]);
   807         xmax = std::max(xmax, x[i]);
   808         ymin = std::min(ymin, y[i]);
   809         ymax = std::max(ymax, y[i]);
   810     }
   812     return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
   813   } else {
   814     const Matrix &matrix = mTransform;
   815     return ThebesRect(matrix.TransformBounds(ToRect(rect)));
   816   }
   817 }
   819 bool
   820 gfxContext::UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale) const
   821 {
   822   if (GetFlags() & FLAG_DISABLE_SNAPPING)
   823       return false;
   825   // if we're not at 1.0 scale, don't snap, unless we're
   826   // ignoring the scale.  If we're not -just- a scale,
   827   // never snap.
   828   const gfxFloat epsilon = 0.0000001;
   829 #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
   830   if (mCairo) {
   831     cairo_matrix_t mat;
   832     cairo_get_matrix(mCairo, &mat);
   833     if (!ignoreScale &&
   834         (!WITHIN_E(mat.xx,1.0) || !WITHIN_E(mat.yy,1.0) ||
   835           !WITHIN_E(mat.xy,0.0) || !WITHIN_E(mat.yx,0.0)))
   836         return false;
   837   } else {
   838     Matrix mat = mTransform;
   839     if (!ignoreScale &&
   840         (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
   841           !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
   842         return false;
   843   }
   844 #undef WITHIN_E
   846   gfxPoint p1 = UserToDevice(rect.TopLeft());
   847   gfxPoint p2 = UserToDevice(rect.TopRight());
   848   gfxPoint p3 = UserToDevice(rect.BottomRight());
   850   // Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
   851   // two opposite corners define the entire rectangle. So check if
   852   // the axis-aligned rectangle with opposite corners p1 and p3
   853   // define an axis-aligned rectangle whose other corners are p2 and p4.
   854   // We actually only need to check one of p2 and p4, since an affine
   855   // transform maps parallelograms to parallelograms.
   856   if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) {
   857       p1.Round();
   858       p3.Round();
   860       rect.MoveTo(gfxPoint(std::min(p1.x, p3.x), std::min(p1.y, p3.y)));
   861       rect.SizeTo(gfxSize(std::max(p1.x, p3.x) - rect.X(),
   862                           std::max(p1.y, p3.y) - rect.Y()));
   863       return true;
   864   }
   866   return false;
   867 }
   869 bool
   870 gfxContext::UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale) const
   871 {
   872   if (GetFlags() & FLAG_DISABLE_SNAPPING)
   873       return false;
   875   // if we're not at 1.0 scale, don't snap, unless we're
   876   // ignoring the scale.  If we're not -just- a scale,
   877   // never snap.
   878   const gfxFloat epsilon = 0.0000001;
   879 #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
   880   if (mCairo) {
   881     cairo_matrix_t mat;
   882     cairo_get_matrix(mCairo, &mat);
   883     if (!ignoreScale &&
   884         (!WITHIN_E(mat.xx,1.0) || !WITHIN_E(mat.yy,1.0) ||
   885           !WITHIN_E(mat.xy,0.0) || !WITHIN_E(mat.yx,0.0)))
   886         return false;
   887   } else {
   888     Matrix mat = mTransform;
   889     if (!ignoreScale &&
   890         (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
   891           !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
   892         return false;
   893   }
   894 #undef WITHIN_E
   896   pt = UserToDevice(pt);
   897   pt.Round();
   898   return true;
   899 }
   901 void
   902 gfxContext::PixelSnappedRectangleAndSetPattern(const gfxRect& rect,
   903                                                gfxPattern *pattern)
   904 {
   905   gfxRect r(rect);
   907   // Bob attempts to pixel-snap the rectangle, and returns true if
   908   // the snapping succeeds.  If it does, we need to set up an
   909   // identity matrix, because the rectangle given back is in device
   910   // coordinates.
   911   //
   912   // We then have to call a translate to dr.pos afterwards, to make
   913   // sure the image lines up in the right place with our pixel
   914   // snapped rectangle.
   915   //
   916   // If snapping wasn't successful, we just translate to where the
   917   // pattern would normally start (in app coordinates) and do the
   918   // same thing.
   919   Rectangle(r, true);
   920   SetPattern(pattern);
   921 }
   923 void
   924 gfxContext::SetAntialiasMode(AntialiasMode mode)
   925 {
   926   if (mCairo) {
   927     if (mode == MODE_ALIASED) {
   928         cairo_set_antialias(mCairo, CAIRO_ANTIALIAS_NONE);
   929     } else if (mode == MODE_COVERAGE) {
   930         cairo_set_antialias(mCairo, CAIRO_ANTIALIAS_DEFAULT);
   931     }
   932   } else {
   933     if (mode == MODE_ALIASED) {
   934       CurrentState().aaMode = gfx::AntialiasMode::NONE;
   935     } else if (mode == MODE_COVERAGE) {
   936       CurrentState().aaMode = gfx::AntialiasMode::SUBPIXEL;
   937     }
   938   }
   939 }
   941 gfxContext::AntialiasMode
   942 gfxContext::CurrentAntialiasMode() const
   943 {
   944   if (mCairo) {
   945     cairo_antialias_t aa = cairo_get_antialias(mCairo);
   946     if (aa == CAIRO_ANTIALIAS_NONE)
   947         return MODE_ALIASED;
   948     return MODE_COVERAGE;
   949   } else {
   950     if (CurrentState().aaMode == gfx::AntialiasMode::NONE) {
   951       return MODE_ALIASED;
   952     }
   953     return MODE_COVERAGE;
   954   }
   955 }
   957 void
   958 gfxContext::SetDash(gfxLineType ltype)
   959 {
   960   static double dash[] = {5.0, 5.0};
   961   static double dot[] = {1.0, 1.0};
   963   switch (ltype) {
   964       case gfxLineDashed:
   965           SetDash(dash, 2, 0.0);
   966           break;
   967       case gfxLineDotted:
   968           SetDash(dot, 2, 0.0);
   969           break;
   970       case gfxLineSolid:
   971       default:
   972           SetDash(nullptr, 0, 0.0);
   973           break;
   974   }
   975 }
   977 void
   978 gfxContext::SetDash(gfxFloat *dashes, int ndash, gfxFloat offset)
   979 {
   980   if (mCairo) {
   981     cairo_set_dash(mCairo, dashes, ndash, offset);
   982   } else {
   983     AzureState &state = CurrentState();
   985     state.dashPattern.SetLength(ndash);
   986     for (int i = 0; i < ndash; i++) {
   987       state.dashPattern[i] = Float(dashes[i]);
   988     }
   989     state.strokeOptions.mDashLength = ndash;
   990     state.strokeOptions.mDashOffset = Float(offset);
   991     state.strokeOptions.mDashPattern = ndash ? state.dashPattern.Elements()
   992                                              : nullptr;
   993   }
   994 }
   996 bool
   997 gfxContext::CurrentDash(FallibleTArray<gfxFloat>& dashes, gfxFloat* offset) const
   998 {
   999   if (mCairo) {
  1000     int count = cairo_get_dash_count(mCairo);
  1001     if (count <= 0 || !dashes.SetLength(count)) {
  1002         return false;
  1004     cairo_get_dash(mCairo, dashes.Elements(), offset);
  1005     return true;
  1006   } else {
  1007     const AzureState &state = CurrentState();
  1008     int count = state.strokeOptions.mDashLength;
  1010     if (count <= 0 || !dashes.SetLength(count)) {
  1011       return false;
  1014     for (int i = 0; i < count; i++) {
  1015       dashes[i] = state.dashPattern[i];
  1018     *offset = state.strokeOptions.mDashOffset;
  1020     return true;
  1024 gfxFloat
  1025 gfxContext::CurrentDashOffset() const
  1027   if (mCairo) {
  1028     if (cairo_get_dash_count(mCairo) <= 0) {
  1029         return 0.0;
  1031     gfxFloat offset;
  1032     cairo_get_dash(mCairo, nullptr, &offset);
  1033     return offset;
  1034   } else {
  1035     return CurrentState().strokeOptions.mDashOffset;
  1039 void
  1040 gfxContext::SetLineWidth(gfxFloat width)
  1042   if (mCairo) {
  1043     cairo_set_line_width(mCairo, width);
  1044   } else {
  1045     CurrentState().strokeOptions.mLineWidth = Float(width);
  1049 gfxFloat
  1050 gfxContext::CurrentLineWidth() const
  1052   if (mCairo) {
  1053     return cairo_get_line_width(mCairo);
  1054   } else {
  1055     return CurrentState().strokeOptions.mLineWidth;
  1059 void
  1060 gfxContext::SetOperator(GraphicsOperator op)
  1062   if (mCairo) {
  1063     if (mFlags & FLAG_SIMPLIFY_OPERATORS) {
  1064         if (op != OPERATOR_SOURCE &&
  1065             op != OPERATOR_CLEAR &&
  1066             op != OPERATOR_OVER)
  1067             op = OPERATOR_OVER;
  1070     cairo_set_operator(mCairo, (cairo_operator_t)op);
  1071   } else {
  1072     if (op == OPERATOR_CLEAR) {
  1073       CurrentState().opIsClear = true;
  1074       return;
  1076     CurrentState().opIsClear = false;
  1077     CurrentState().op = CompositionOpForOp(op);
  1081 gfxContext::GraphicsOperator
  1082 gfxContext::CurrentOperator() const
  1084   if (mCairo) {
  1085     return (GraphicsOperator)cairo_get_operator(mCairo);
  1086   } else {
  1087     return ThebesOp(CurrentState().op);
  1091 void
  1092 gfxContext::SetLineCap(GraphicsLineCap cap)
  1094   if (mCairo) {
  1095     cairo_set_line_cap(mCairo, (cairo_line_cap_t)cap);
  1096   } else {
  1097     CurrentState().strokeOptions.mLineCap = ToCapStyle(cap);
  1101 gfxContext::GraphicsLineCap
  1102 gfxContext::CurrentLineCap() const
  1104   if (mCairo) {
  1105     return (GraphicsLineCap)cairo_get_line_cap(mCairo);
  1106   } else {
  1107     return ThebesLineCap(CurrentState().strokeOptions.mLineCap);
  1111 void
  1112 gfxContext::SetLineJoin(GraphicsLineJoin join)
  1114   if (mCairo) {
  1115     cairo_set_line_join(mCairo, (cairo_line_join_t)join);
  1116   } else {
  1117     CurrentState().strokeOptions.mLineJoin = ToJoinStyle(join);
  1121 gfxContext::GraphicsLineJoin
  1122 gfxContext::CurrentLineJoin() const
  1124   if (mCairo) {
  1125     return (GraphicsLineJoin)cairo_get_line_join(mCairo);
  1126   } else {
  1127     return ThebesLineJoin(CurrentState().strokeOptions.mLineJoin);
  1131 void
  1132 gfxContext::SetMiterLimit(gfxFloat limit)
  1134   if (mCairo) {
  1135     cairo_set_miter_limit(mCairo, limit);
  1136   } else {
  1137     CurrentState().strokeOptions.mMiterLimit = Float(limit);
  1141 gfxFloat
  1142 gfxContext::CurrentMiterLimit() const
  1144   if (mCairo) {
  1145     return cairo_get_miter_limit(mCairo);
  1146   } else {
  1147     return CurrentState().strokeOptions.mMiterLimit;
  1151 void
  1152 gfxContext::SetFillRule(FillRule rule)
  1154   if (mCairo) {
  1155     cairo_set_fill_rule(mCairo, (cairo_fill_rule_t)rule);
  1156   } else {
  1157     CurrentState().fillRule = rule == FILL_RULE_WINDING ? gfx::FillRule::FILL_WINDING : gfx::FillRule::FILL_EVEN_ODD;
  1161 gfxContext::FillRule
  1162 gfxContext::CurrentFillRule() const
  1164   if (mCairo) {
  1165     return (FillRule)cairo_get_fill_rule(mCairo);
  1166   } else {
  1167     return FILL_RULE_WINDING;
  1171 // clipping
  1172 void
  1173 gfxContext::Clip(const gfxRect& rect)
  1175   if (mCairo) {
  1176     cairo_new_path(mCairo);
  1177     cairo_rectangle(mCairo, rect.X(), rect.Y(), rect.Width(), rect.Height());
  1178     cairo_clip(mCairo);
  1179   } else {
  1180     AzureState::PushedClip clip = { nullptr, ToRect(rect), mTransform };
  1181     CurrentState().pushedClips.AppendElement(clip);
  1182     mDT->PushClipRect(ToRect(rect));
  1183     NewPath();
  1187 void
  1188 gfxContext::Clip()
  1190   if (mCairo) {
  1191     cairo_clip_preserve(mCairo);
  1192   } else {
  1193     if (mPathIsRect) {
  1194       MOZ_ASSERT(!mTransformChanged);
  1196       AzureState::PushedClip clip = { nullptr, mRect, mTransform };
  1197       CurrentState().pushedClips.AppendElement(clip);
  1198       mDT->PushClipRect(mRect);
  1199     } else {
  1200       EnsurePath();
  1201       mDT->PushClip(mPath);
  1202       AzureState::PushedClip clip = { mPath, Rect(), mTransform };
  1203       CurrentState().pushedClips.AppendElement(clip);
  1208 void
  1209 gfxContext::ResetClip()
  1211   if (mCairo) {
  1212     cairo_reset_clip(mCairo);
  1213   } else {
  1214     for (int i = mStateStack.Length() - 1; i >= 0; i--) {
  1215       for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
  1216         mDT->PopClip();
  1219       if (mStateStack[i].clipWasReset) {
  1220         break;
  1223     CurrentState().pushedClips.Clear();
  1224     CurrentState().clipWasReset = true;
  1228 void
  1229 gfxContext::UpdateSurfaceClip()
  1231   if (mCairo) {
  1232     NewPath();
  1233     // we paint an empty rectangle to ensure the clip is propagated to
  1234     // the destination surface
  1235     SetDeviceColor(gfxRGBA(0,0,0,0));
  1236     Rectangle(gfxRect(0,1,1,0));
  1237     Fill();
  1241 gfxRect
  1242 gfxContext::GetClipExtents()
  1244   if (mCairo) {
  1245     double xmin, ymin, xmax, ymax;
  1246     cairo_clip_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
  1247     return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
  1248   } else {
  1249     Rect rect = GetAzureDeviceSpaceClipBounds();
  1251     if (rect.width == 0 || rect.height == 0) {
  1252       return gfxRect(0, 0, 0, 0);
  1255     Matrix mat = mTransform;
  1256     mat.Invert();
  1257     rect = mat.TransformBounds(rect);
  1259     return ThebesRect(rect);
  1263 bool
  1264 gfxContext::ClipContainsRect(const gfxRect& aRect)
  1266   if (mCairo) {
  1267     cairo_rectangle_list_t *clip =
  1268         cairo_copy_clip_rectangle_list(mCairo);
  1270     bool result = false;
  1272     if (clip->status == CAIRO_STATUS_SUCCESS) {
  1273         for (int i = 0; i < clip->num_rectangles; i++) {
  1274             gfxRect rect(clip->rectangles[i].x, clip->rectangles[i].y,
  1275                          clip->rectangles[i].width, clip->rectangles[i].height);
  1276             if (rect.Contains(aRect)) {
  1277                 result = true;
  1278                 break;
  1283     cairo_rectangle_list_destroy(clip);
  1284     return result;
  1285   } else {
  1286     unsigned int lastReset = 0;
  1287     for (int i = mStateStack.Length() - 2; i > 0; i--) {
  1288       if (mStateStack[i].clipWasReset) {
  1289         lastReset = i;
  1290         break;
  1294     // Since we always return false when the clip list contains a
  1295     // non-rectangular clip or a non-rectilinear transform, our 'total' clip
  1296     // is always a rectangle if we hit the end of this function.
  1297     Rect clipBounds(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
  1299     for (unsigned int i = lastReset; i < mStateStack.Length(); i++) {
  1300       for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
  1301         AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
  1302         if (clip.path || !clip.transform.IsRectilinear()) {
  1303           // Cairo behavior is we return false if the clip contains a non-
  1304           // rectangle.
  1305           return false;
  1306         } else {
  1307           Rect clipRect = mTransform.TransformBounds(clip.rect);
  1309           clipBounds.IntersectRect(clipBounds, clipRect);
  1314     return clipBounds.Contains(ToRect(aRect));
  1318 // rendering sources
  1320 void
  1321 gfxContext::SetColor(const gfxRGBA& c)
  1323   if (mCairo) {
  1324     if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
  1326         gfxRGBA cms;
  1327         qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
  1328         if (transform)
  1329           gfxPlatform::TransformPixel(c, cms, transform);
  1331         // Use the original alpha to avoid unnecessary float->byte->float
  1332         // conversion errors
  1333         cairo_set_source_rgba(mCairo, cms.r, cms.g, cms.b, c.a);
  1335     else
  1336         cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a);
  1337   } else {
  1338     CurrentState().pattern = nullptr;
  1339     CurrentState().sourceSurfCairo = nullptr;
  1340     CurrentState().sourceSurface = nullptr;
  1342     if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
  1344         gfxRGBA cms;
  1345         qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
  1346         if (transform)
  1347           gfxPlatform::TransformPixel(c, cms, transform);
  1349         // Use the original alpha to avoid unnecessary float->byte->float
  1350         // conversion errors
  1351         CurrentState().color = ToColor(cms);
  1353     else
  1354         CurrentState().color = ToColor(c);
  1358 void
  1359 gfxContext::SetDeviceColor(const gfxRGBA& c)
  1361   if (mCairo) {
  1362     cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a);
  1363   } else {
  1364     CurrentState().pattern = nullptr;
  1365     CurrentState().sourceSurfCairo = nullptr;
  1366     CurrentState().sourceSurface = nullptr;
  1367     CurrentState().color = ToColor(c);
  1371 bool
  1372 gfxContext::GetDeviceColor(gfxRGBA& c)
  1374   if (mCairo) {
  1375     return cairo_pattern_get_rgba(cairo_get_source(mCairo),
  1376                                   &c.r,
  1377                                   &c.g,
  1378                                   &c.b,
  1379                                   &c.a) == CAIRO_STATUS_SUCCESS;
  1380   } else {
  1381     if (CurrentState().sourceSurface) {
  1382       return false;
  1384     if (CurrentState().pattern) {
  1385       gfxRGBA color;
  1386       return CurrentState().pattern->GetSolidColor(c);
  1389     c = ThebesRGBA(CurrentState().color);
  1390     return true;
  1394 void
  1395 gfxContext::SetSource(gfxASurface *surface, const gfxPoint& offset)
  1397   if (mCairo) {
  1398     NS_ASSERTION(surface->GetAllowUseAsSource(), "Surface not allowed to be used as source!");
  1399     cairo_set_source_surface(mCairo, surface->CairoSurface(), offset.x, offset.y);
  1400   } else {
  1401     CurrentState().surfTransform = Matrix(1.0f, 0, 0, 1.0f, Float(offset.x), Float(offset.y));
  1402     CurrentState().pattern = nullptr;
  1403     CurrentState().patternTransformChanged = false;
  1404     // Keep the underlying cairo surface around while we keep the
  1405     // sourceSurface.
  1406     CurrentState().sourceSurfCairo = surface;
  1407     CurrentState().sourceSurface =
  1408       gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
  1409     CurrentState().color = Color(0, 0, 0, 0);
  1413 void
  1414 gfxContext::SetPattern(gfxPattern *pattern)
  1416   if (mCairo) {
  1417     MOZ_ASSERT(!pattern->IsAzure());
  1418     cairo_set_source(mCairo, pattern->CairoPattern());
  1419   } else {
  1420     CurrentState().sourceSurfCairo = nullptr;
  1421     CurrentState().sourceSurface = nullptr;
  1422     CurrentState().patternTransformChanged = false;
  1423     CurrentState().pattern = pattern;
  1427 already_AddRefed<gfxPattern>
  1428 gfxContext::GetPattern()
  1430   if (mCairo) {
  1431     cairo_pattern_t *pat = cairo_get_source(mCairo);
  1432     NS_ASSERTION(pat, "I was told this couldn't be null");
  1434     nsRefPtr<gfxPattern> wrapper;
  1435     if (pat)
  1436         wrapper = new gfxPattern(pat);
  1437     else
  1438         wrapper = new gfxPattern(gfxRGBA(0,0,0,0));
  1440     return wrapper.forget();
  1441   } else {
  1442     nsRefPtr<gfxPattern> pat;
  1444     AzureState &state = CurrentState();
  1445     if (state.pattern) {
  1446       pat = state.pattern;
  1447     } else if (state.sourceSurface) {
  1448       NS_ASSERTION(false, "Ugh, this isn't good.");
  1449     } else {
  1450       pat = new gfxPattern(ThebesRGBA(state.color));
  1452     return pat.forget();
  1457 // masking
  1458 void
  1459 gfxContext::Mask(gfxPattern *pattern)
  1461   if (mCairo) {
  1462     MOZ_ASSERT(!pattern->IsAzure());
  1463     cairo_mask(mCairo, pattern->CairoPattern());
  1464   } else {
  1465     if (pattern->Extend() == gfxPattern::EXTEND_NONE) {
  1466       // In this situation the mask will be fully transparent (i.e. nothing
  1467       // will be drawn) outside of the bounds of the surface. We can support
  1468       // that by clipping out drawing to that area.
  1469       Point offset;
  1470       if (pattern->IsAzure()) {
  1471         // This is an Azure pattern. i.e. this was the result of a PopGroup and
  1472         // then the extend mode was changed to EXTEND_NONE.
  1473         // XXX - We may need some additional magic here in theory to support
  1474         // device offsets in these patterns, but no problems have been observed
  1475         // yet because of this. And it would complicate things a little further.
  1476         offset = Point(0.f, 0.f);
  1477       } else if (pattern->GetType() == gfxPattern::PATTERN_SURFACE) {
  1478         nsRefPtr<gfxASurface> asurf = pattern->GetSurface();
  1479         gfxPoint deviceOffset = asurf->GetDeviceOffset();
  1480         offset = Point(-deviceOffset.x, -deviceOffset.y);
  1482         // this lets GetAzureSurface work
  1483         pattern->GetPattern(mDT);
  1486       if (pattern->IsAzure() || pattern->GetType() == gfxPattern::PATTERN_SURFACE) {
  1487         RefPtr<SourceSurface> mask = pattern->GetAzureSurface();
  1488         Matrix mat = ToMatrix(pattern->GetInverseMatrix());
  1489         Matrix old = mTransform;
  1490         // add in the inverse of the pattern transform so that when we
  1491         // MaskSurface we are transformed to the place matching the pattern transform
  1492         mat = mat * mTransform;
  1494         ChangeTransform(mat);
  1495         mDT->MaskSurface(GeneralPattern(this), mask, offset, DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode));
  1496         ChangeTransform(old);
  1497         return;
  1500     mDT->Mask(GeneralPattern(this), *pattern->GetPattern(mDT), DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode));
  1504 void
  1505 gfxContext::Mask(gfxASurface *surface, const gfxPoint& offset)
  1507   PROFILER_LABEL("gfxContext", "Mask");
  1508   if (mCairo) {
  1509     cairo_mask_surface(mCairo, surface->CairoSurface(), offset.x, offset.y);
  1510   } else {
  1511     // Lifetime needs to be limited here as we may simply wrap surface's data.
  1512     RefPtr<SourceSurface> sourceSurf =
  1513       gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
  1515     if (!sourceSurf) {
  1516       return;
  1519     gfxPoint pt = surface->GetDeviceOffset();
  1521     Mask(sourceSurf, Point(offset.x - pt.x, offset.y - pt.y));
  1525 void
  1526 gfxContext::Mask(SourceSurface *surface, const Point& offset)
  1528   MOZ_ASSERT(mDT);
  1531   // We clip here to bind to the mask surface bounds, see above.
  1532   mDT->MaskSurface(GeneralPattern(this),
  1533             surface,
  1534             offset,
  1535             DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode));
  1538 void
  1539 gfxContext::Paint(gfxFloat alpha)
  1541   PROFILER_LABEL("gfxContext", "Paint");
  1542   if (mCairo) {
  1543     cairo_paint_with_alpha(mCairo, alpha);
  1544   } else {
  1545     AzureState &state = CurrentState();
  1547     if (state.sourceSurface && !state.sourceSurfCairo &&
  1548         !state.patternTransformChanged && !state.opIsClear)
  1550       // This is the case where a PopGroupToSource has been done and this
  1551       // paint is executed without changing the transform or the source.
  1552       Matrix oldMat = mDT->GetTransform();
  1554       IntSize surfSize = state.sourceSurface->GetSize();
  1556       Matrix mat;
  1557       mat.Translate(-state.deviceOffset.x, -state.deviceOffset.y);
  1558       mDT->SetTransform(mat);
  1560       mDT->DrawSurface(state.sourceSurface,
  1561                        Rect(state.sourceSurfaceDeviceOffset, Size(surfSize.width, surfSize.height)),
  1562                        Rect(Point(), Size(surfSize.width, surfSize.height)),
  1563                        DrawSurfaceOptions(), DrawOptions(alpha, GetOp()));
  1564       mDT->SetTransform(oldMat);
  1565       return;
  1568     Matrix mat = mDT->GetTransform();
  1569     mat.Invert();
  1570     Rect paintRect = mat.TransformBounds(Rect(Point(0, 0), Size(mDT->GetSize())));
  1572     if (state.opIsClear) {
  1573       mDT->ClearRect(paintRect);
  1574     } else {
  1575       mDT->FillRect(paintRect, GeneralPattern(this),
  1576                     DrawOptions(Float(alpha), GetOp()));
  1581 // groups
  1583 void
  1584 gfxContext::PushGroup(gfxContentType content)
  1586   if (mCairo) {
  1587     cairo_push_group_with_content(mCairo, (cairo_content_t)(int) content);
  1588   } else {
  1589     PushNewDT(content);
  1591     PushClipsToDT(mDT);
  1592     mDT->SetTransform(GetDTTransform());
  1596 static gfxRect
  1597 GetRoundOutDeviceClipExtents(gfxContext* aCtx)
  1599   gfxContextMatrixAutoSaveRestore save(aCtx);
  1600   aCtx->IdentityMatrix();
  1601   gfxRect r = aCtx->GetClipExtents();
  1602   r.RoundOut();
  1603   return r;
  1606 /**
  1607  * Copy the contents of aSrc to aDest, translated by aTranslation.
  1608  */
  1609 static void
  1610 CopySurface(gfxASurface* aSrc, gfxASurface* aDest, const gfxPoint& aTranslation)
  1612   cairo_t *cr = cairo_create(aDest->CairoSurface());
  1613   cairo_set_source_surface(cr, aSrc->CairoSurface(), aTranslation.x, aTranslation.y);
  1614   cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
  1615   cairo_paint(cr);
  1616   cairo_destroy(cr);
  1619 void
  1620 gfxContext::PushGroupAndCopyBackground(gfxContentType content)
  1622   if (mCairo) {
  1623     if (content == gfxContentType::COLOR_ALPHA &&
  1624       !(GetFlags() & FLAG_DISABLE_COPY_BACKGROUND)) {
  1625       nsRefPtr<gfxASurface> s = CurrentSurface();
  1626       if ((s->GetAllowUseAsSource() || s->GetType() == gfxSurfaceType::Tee) &&
  1627           (s->GetContentType() == gfxContentType::COLOR ||
  1628               s->GetOpaqueRect().Contains(GetRoundOutDeviceClipExtents(this)))) {
  1629         cairo_push_group_with_content(mCairo, CAIRO_CONTENT_COLOR);
  1630         nsRefPtr<gfxASurface> d = CurrentSurface();
  1632         if (d->GetType() == gfxSurfaceType::Tee) {
  1633           NS_ASSERTION(s->GetType() == gfxSurfaceType::Tee, "Mismatched types");
  1634           nsAutoTArray<nsRefPtr<gfxASurface>,2> ss;
  1635           nsAutoTArray<nsRefPtr<gfxASurface>,2> ds;
  1636           static_cast<gfxTeeSurface*>(s.get())->GetSurfaces(&ss);
  1637           static_cast<gfxTeeSurface*>(d.get())->GetSurfaces(&ds);
  1638           NS_ASSERTION(ss.Length() == ds.Length(), "Mismatched lengths");
  1639           gfxPoint translation = d->GetDeviceOffset() - s->GetDeviceOffset();
  1640           for (uint32_t i = 0; i < ss.Length(); ++i) {
  1641               CopySurface(ss[i], ds[i], translation);
  1643         } else {
  1644           CopySurface(s, d, gfxPoint(0, 0));
  1646         d->SetOpaqueRect(s->GetOpaqueRect());
  1647         return;
  1650   } else {
  1651     IntRect clipExtents;
  1652     if (mDT->GetFormat() != SurfaceFormat::B8G8R8X8) {
  1653       gfxRect clipRect = GetRoundOutDeviceClipExtents(this);
  1654       clipExtents = IntRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
  1656     if ((mDT->GetFormat() == SurfaceFormat::B8G8R8X8 ||
  1657          mDT->GetOpaqueRect().Contains(clipExtents)) &&
  1658         !mDT->GetUserData(&sDontUseAsSourceKey)) {
  1659       DrawTarget *oldDT = mDT;
  1660       RefPtr<SourceSurface> source = mDT->Snapshot();
  1661       Point oldDeviceOffset = CurrentState().deviceOffset;
  1663       PushNewDT(gfxContentType::COLOR);
  1665       Point offset = CurrentState().deviceOffset - oldDeviceOffset;
  1666       Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
  1667       Rect sourceRect = surfRect;
  1668       sourceRect.x += offset.x;
  1669       sourceRect.y += offset.y;
  1671       mDT->SetTransform(Matrix());
  1672       mDT->DrawSurface(source, surfRect, sourceRect);
  1673       mDT->SetOpaqueRect(oldDT->GetOpaqueRect());
  1675       PushClipsToDT(mDT);
  1676       mDT->SetTransform(GetDTTransform());
  1677       return;
  1680   PushGroup(content);
  1683 already_AddRefed<gfxPattern>
  1684 gfxContext::PopGroup()
  1686   if (mCairo) {
  1687     cairo_pattern_t *pat = cairo_pop_group(mCairo);
  1688     nsRefPtr<gfxPattern> wrapper = new gfxPattern(pat);
  1689     cairo_pattern_destroy(pat);
  1690     return wrapper.forget();
  1691   } else {
  1692     RefPtr<SourceSurface> src = mDT->Snapshot();
  1693     Point deviceOffset = CurrentState().deviceOffset;
  1695     Restore();
  1697     Matrix mat = mTransform;
  1698     mat.Invert();
  1700     Matrix deviceOffsetTranslation;
  1701     deviceOffsetTranslation.Translate(deviceOffset.x, deviceOffset.y);
  1703     nsRefPtr<gfxPattern> pat = new gfxPattern(src, deviceOffsetTranslation * mat);
  1705     return pat.forget();
  1709 void
  1710 gfxContext::PopGroupToSource()
  1712   if (mCairo) {
  1713     cairo_pop_group_to_source(mCairo);
  1714   } else {
  1715     RefPtr<SourceSurface> src = mDT->Snapshot();
  1716     Point deviceOffset = CurrentState().deviceOffset;
  1717     Restore();
  1718     CurrentState().sourceSurfCairo = nullptr;
  1719     CurrentState().sourceSurface = src;
  1720     CurrentState().sourceSurfaceDeviceOffset = deviceOffset;
  1721     CurrentState().pattern = nullptr;
  1722     CurrentState().patternTransformChanged = false;
  1724     Matrix mat = mTransform;
  1725     mat.Invert();
  1727     Matrix deviceOffsetTranslation;
  1728     deviceOffsetTranslation.Translate(deviceOffset.x, deviceOffset.y);
  1729     CurrentState().surfTransform = deviceOffsetTranslation * mat;
  1733 bool
  1734 gfxContext::PointInFill(const gfxPoint& pt)
  1736   if (mCairo) {
  1737     return cairo_in_fill(mCairo, pt.x, pt.y);
  1738   } else {
  1739     EnsurePath();
  1740     return mPath->ContainsPoint(ToPoint(pt), Matrix());
  1744 bool
  1745 gfxContext::PointInStroke(const gfxPoint& pt)
  1747   if (mCairo) {
  1748     return cairo_in_stroke(mCairo, pt.x, pt.y);
  1749   } else {
  1750     EnsurePath();
  1751     return mPath->StrokeContainsPoint(CurrentState().strokeOptions,
  1752                                       ToPoint(pt),
  1753                                       Matrix());
  1757 gfxRect
  1758 gfxContext::GetUserPathExtent()
  1760   if (mCairo) {
  1761     double xmin, ymin, xmax, ymax;
  1762     cairo_path_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
  1763     return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
  1764   } else {
  1765     EnsurePath();
  1766     return ThebesRect(mPath->GetBounds());
  1770 gfxRect
  1771 gfxContext::GetUserFillExtent()
  1773   if (mCairo) {
  1774     double xmin, ymin, xmax, ymax;
  1775     cairo_fill_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
  1776     return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
  1777   } else {
  1778     EnsurePath();
  1779     return ThebesRect(mPath->GetBounds());
  1783 gfxRect
  1784 gfxContext::GetUserStrokeExtent()
  1786   if (mCairo) {
  1787     double xmin, ymin, xmax, ymax;
  1788     cairo_stroke_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
  1789     return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
  1790   } else {
  1791     EnsurePath();
  1792     return ThebesRect(mPath->GetStrokedBounds(CurrentState().strokeOptions, mTransform));
  1796 bool
  1797 gfxContext::HasError()
  1799   if (mCairo) {
  1800     return cairo_status(mCairo) != CAIRO_STATUS_SUCCESS;
  1801   } else {
  1802     // As far as this is concerned, an Azure context is never in error.
  1803     return false;
  1807 void
  1808 gfxContext::RoundedRectangle(const gfxRect& rect,
  1809                              const gfxCornerSizes& corners,
  1810                              bool draw_clockwise)
  1812     //
  1813     // For CW drawing, this looks like:
  1814     //
  1815     //  ...******0**      1    C
  1816     //              ****
  1817     //                  ***    2
  1818     //                     **
  1819     //                       *
  1820     //                        *
  1821     //                         3
  1822     //                         *
  1823     //                         *
  1824     //
  1825     // Where 0, 1, 2, 3 are the control points of the Bezier curve for
  1826     // the corner, and C is the actual corner point.
  1827     //
  1828     // At the start of the loop, the current point is assumed to be
  1829     // the point adjacent to the top left corner on the top
  1830     // horizontal.  Note that corner indices start at the top left and
  1831     // continue clockwise, whereas in our loop i = 0 refers to the top
  1832     // right corner.
  1833     //
  1834     // When going CCW, the control points are swapped, and the first
  1835     // corner that's drawn is the top left (along with the top segment).
  1836     //
  1837     // There is considerable latitude in how one chooses the four
  1838     // control points for a Bezier curve approximation to an ellipse.
  1839     // For the overall path to be continuous and show no corner at the
  1840     // endpoints of the arc, points 0 and 3 must be at the ends of the
  1841     // straight segments of the rectangle; points 0, 1, and C must be
  1842     // collinear; and points 3, 2, and C must also be collinear.  This
  1843     // leaves only two free parameters: the ratio of the line segments
  1844     // 01 and 0C, and the ratio of the line segments 32 and 3C.  See
  1845     // the following papers for extensive discussion of how to choose
  1846     // these ratios:
  1847     //
  1848     //   Dokken, Tor, et al. "Good approximation of circles by
  1849     //      curvature-continuous Bezier curves."  Computer-Aided
  1850     //      Geometric Design 7(1990) 33--41.
  1851     //   Goldapp, Michael. "Approximation of circular arcs by cubic
  1852     //      polynomials." Computer-Aided Geometric Design 8(1991) 227--238.
  1853     //   Maisonobe, Luc. "Drawing an elliptical arc using polylines,
  1854     //      quadratic, or cubic Bezier curves."
  1855     //      http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
  1856     //
  1857     // We follow the approach in section 2 of Goldapp (least-error,
  1858     // Hermite-type approximation) and make both ratios equal to
  1859     //
  1860     //          2   2 + n - sqrt(2n + 28)
  1861     //  alpha = - * ---------------------
  1862     //          3           n - 4
  1863     //
  1864     // where n = 3( cbrt(sqrt(2)+1) - cbrt(sqrt(2)-1) ).
  1865     //
  1866     // This is the result of Goldapp's equation (10b) when the angle
  1867     // swept out by the arc is pi/2, and the parameter "a-bar" is the
  1868     // expression given immediately below equation (21).
  1869     //
  1870     // Using this value, the maximum radial error for a circle, as a
  1871     // fraction of the radius, is on the order of 0.2 x 10^-3.
  1872     // Neither Dokken nor Goldapp discusses error for a general
  1873     // ellipse; Maisonobe does, but his choice of control points
  1874     // follows different constraints, and Goldapp's expression for
  1875     // 'alpha' gives much smaller radial error, even for very flat
  1876     // ellipses, than Maisonobe's equivalent.
  1877     //
  1878     // For the various corners and for each axis, the sign of this
  1879     // constant changes, or it might be 0 -- it's multiplied by the
  1880     // appropriate multiplier from the list before using.
  1882   if (mCairo) {
  1883     const gfxFloat alpha = 0.55191497064665766025;
  1885     typedef struct { gfxFloat a, b; } twoFloats;
  1887     twoFloats cwCornerMults[4] = { { -1,  0 },
  1888                                    {  0, -1 },
  1889                                    { +1,  0 },
  1890                                    {  0, +1 } };
  1891     twoFloats ccwCornerMults[4] = { { +1,  0 },
  1892                                     {  0, -1 },
  1893                                     { -1,  0 },
  1894                                     {  0, +1 } };
  1896     twoFloats *cornerMults = draw_clockwise ? cwCornerMults : ccwCornerMults;
  1898     gfxPoint pc, p0, p1, p2, p3;
  1900     if (draw_clockwise)
  1901         cairo_move_to(mCairo, rect.X() + corners[NS_CORNER_TOP_LEFT].width, rect.Y());
  1902     else
  1903         cairo_move_to(mCairo, rect.X() + rect.Width() - corners[NS_CORNER_TOP_RIGHT].width, rect.Y());
  1905     NS_FOR_CSS_CORNERS(i) {
  1906         // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
  1907         mozilla::css::Corner c = mozilla::css::Corner(draw_clockwise ? ((i+1) % 4) : ((4-i) % 4));
  1909         // i+2 and i+3 respectively.  These are used to index into the corner
  1910         // multiplier table, and were deduced by calculating out the long form
  1911         // of each corner and finding a pattern in the signs and values.
  1912         int i2 = (i+2) % 4;
  1913         int i3 = (i+3) % 4;
  1915         pc = rect.AtCorner(c);
  1917         if (corners[c].width > 0.0 && corners[c].height > 0.0) {
  1918             p0.x = pc.x + cornerMults[i].a * corners[c].width;
  1919             p0.y = pc.y + cornerMults[i].b * corners[c].height;
  1921             p3.x = pc.x + cornerMults[i3].a * corners[c].width;
  1922             p3.y = pc.y + cornerMults[i3].b * corners[c].height;
  1924             p1.x = p0.x + alpha * cornerMults[i2].a * corners[c].width;
  1925             p1.y = p0.y + alpha * cornerMults[i2].b * corners[c].height;
  1927             p2.x = p3.x - alpha * cornerMults[i3].a * corners[c].width;
  1928             p2.y = p3.y - alpha * cornerMults[i3].b * corners[c].height;
  1930             cairo_line_to (mCairo, p0.x, p0.y);
  1931             cairo_curve_to (mCairo,
  1932                             p1.x, p1.y,
  1933                             p2.x, p2.y,
  1934                             p3.x, p3.y);
  1935         } else {
  1936             cairo_line_to (mCairo, pc.x, pc.y);
  1940     cairo_close_path (mCairo);
  1941   } else {
  1942     EnsurePathBuilder();
  1943     Size radii[] = { ToSize(corners[NS_CORNER_TOP_LEFT]),
  1944                      ToSize(corners[NS_CORNER_TOP_RIGHT]),
  1945                      ToSize(corners[NS_CORNER_BOTTOM_RIGHT]),
  1946                      ToSize(corners[NS_CORNER_BOTTOM_LEFT]) };
  1947     AppendRoundedRectToPath(mPathBuilder, ToRect(rect), radii, draw_clockwise);
  1951 #ifdef MOZ_DUMP_PAINTING
  1952 void
  1953 gfxContext::WriteAsPNG(const char* aFile)
  1955   nsRefPtr<gfxASurface> surf = CurrentSurface();
  1956   if (surf) {
  1957     surf->WriteAsPNG(aFile);
  1958   } else {
  1959     NS_WARNING("No surface found!");
  1963 void 
  1964 gfxContext::DumpAsDataURL()
  1966   nsRefPtr<gfxASurface> surf = CurrentSurface();
  1967   if (surf) {
  1968     surf->DumpAsDataURL();
  1969   } else {
  1970     NS_WARNING("No surface found!");
  1974 void 
  1975 gfxContext::CopyAsDataURL()
  1977   nsRefPtr<gfxASurface> surf = CurrentSurface();
  1978   if (surf) {
  1979     surf->CopyAsDataURL();
  1980   } else {
  1981     NS_WARNING("No surface found!");
  1984 #endif
  1986 void
  1987 gfxContext::EnsurePath()
  1989   if (mPathBuilder) {
  1990     mPath = mPathBuilder->Finish();
  1991     mPathBuilder = nullptr;
  1994   if (mPath) {
  1995     if (mTransformChanged) {
  1996       Matrix mat = mTransform;
  1997       mat.Invert();
  1998       mat = mPathTransform * mat;
  1999       mPathBuilder = mPath->TransformedCopyToBuilder(mat, CurrentState().fillRule);
  2000       mPath = mPathBuilder->Finish();
  2001       mPathBuilder = nullptr;
  2003       mTransformChanged = false;
  2006     if (CurrentState().fillRule == mPath->GetFillRule()) {
  2007       return;
  2010     mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
  2012     mPath = mPathBuilder->Finish();
  2013     mPathBuilder = nullptr;
  2014     return;
  2017   EnsurePathBuilder();
  2018   mPath = mPathBuilder->Finish();
  2019   mPathBuilder = nullptr;
  2022 void
  2023 gfxContext::EnsurePathBuilder()
  2025   if (mPathBuilder && !mTransformChanged) {
  2026     return;
  2029   if (mPath) {
  2030     if (!mTransformChanged) {
  2031       mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
  2032       mPath = nullptr;
  2033     } else {
  2034       Matrix invTransform = mTransform;
  2035       invTransform.Invert();
  2036       Matrix toNewUS = mPathTransform * invTransform;
  2037       mPathBuilder = mPath->TransformedCopyToBuilder(toNewUS, CurrentState().fillRule);
  2039     return;
  2042   DebugOnly<PathBuilder*> oldPath = mPathBuilder.get();
  2044   if (!mPathBuilder) {
  2045     mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule);
  2047     if (mPathIsRect) {
  2048       mPathBuilder->MoveTo(mRect.TopLeft());
  2049       mPathBuilder->LineTo(mRect.TopRight());
  2050       mPathBuilder->LineTo(mRect.BottomRight());
  2051       mPathBuilder->LineTo(mRect.BottomLeft());
  2052       mPathBuilder->Close();
  2056   if (mTransformChanged) {
  2057     // This could be an else if since this should never happen when
  2058     // mPathBuilder is nullptr and mPath is nullptr. But this way we can
  2059     // assert if all the state is as expected.
  2060     MOZ_ASSERT(oldPath);
  2061     MOZ_ASSERT(!mPathIsRect);
  2063     Matrix invTransform = mTransform;
  2064     invTransform.Invert();
  2065     Matrix toNewUS = mPathTransform * invTransform;
  2067     RefPtr<Path> path = mPathBuilder->Finish();
  2068     mPathBuilder = path->TransformedCopyToBuilder(toNewUS, CurrentState().fillRule);
  2071   mPathIsRect = false;
  2074 void
  2075 gfxContext::FillAzure(Float aOpacity)
  2077   AzureState &state = CurrentState();
  2079   CompositionOp op = GetOp();
  2081   if (mPathIsRect) {
  2082     MOZ_ASSERT(!mTransformChanged);
  2084     if (state.opIsClear) {
  2085       mDT->ClearRect(mRect);
  2086     } else if (op == CompositionOp::OP_SOURCE) {
  2087       // Emulate cairo operator source which is bound by mask!
  2088       mDT->ClearRect(mRect);
  2089       mDT->FillRect(mRect, GeneralPattern(this), DrawOptions(aOpacity));
  2090     } else {
  2091       mDT->FillRect(mRect, GeneralPattern(this), DrawOptions(aOpacity, op, state.aaMode));
  2093   } else {
  2094     EnsurePath();
  2096     NS_ASSERTION(!state.opIsClear, "We shouldn't be clearing complex paths!");
  2098     mDT->Fill(mPath, GeneralPattern(this), DrawOptions(aOpacity, op, state.aaMode));
  2102 void
  2103 gfxContext::PushClipsToDT(DrawTarget *aDT)
  2105   // Tricky, we have to restore all clips -since the last time- the clip
  2106   // was reset. If we didn't reset the clip, just popping the clips we
  2107   // added was fine.
  2108   unsigned int lastReset = 0;
  2109   for (int i = mStateStack.Length() - 2; i > 0; i--) {
  2110     if (mStateStack[i].clipWasReset) {
  2111       lastReset = i;
  2112       break;
  2116   // Don't need to save the old transform, we'll be setting a new one soon!
  2118   // Push all clips from the last state on the stack where the clip was
  2119   // reset to the clip before ours.
  2120   for (unsigned int i = lastReset; i < mStateStack.Length() - 1; i++) {
  2121     for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
  2122       aDT->SetTransform(mStateStack[i].pushedClips[c].transform * GetDeviceTransform());
  2123       if (mStateStack[i].pushedClips[c].path) {
  2124         aDT->PushClip(mStateStack[i].pushedClips[c].path);
  2125       } else {
  2126         aDT->PushClipRect(mStateStack[i].pushedClips[c].rect);
  2132 CompositionOp
  2133 gfxContext::GetOp()
  2135   if (CurrentState().op != CompositionOp::OP_SOURCE) {
  2136     return CurrentState().op;
  2139   AzureState &state = CurrentState();
  2140   if (state.pattern) {
  2141     if (state.pattern->IsOpaque()) {
  2142       return CompositionOp::OP_OVER;
  2143     } else {
  2144       return CompositionOp::OP_SOURCE;
  2146   } else if (state.sourceSurface) {
  2147     if (state.sourceSurface->GetFormat() == SurfaceFormat::B8G8R8X8) {
  2148       return CompositionOp::OP_OVER;
  2149     } else {
  2150       return CompositionOp::OP_SOURCE;
  2152   } else {
  2153     if (state.color.a > 0.999) {
  2154       return CompositionOp::OP_OVER;
  2155     } else {
  2156       return CompositionOp::OP_SOURCE;
  2161 /* SVG font code can change the transform after having set the pattern on the
  2162  * context. When the pattern is set it is in user space, if the transform is
  2163  * changed after doing so the pattern needs to be converted back into userspace.
  2164  * We just store the old pattern transform here so that we only do the work
  2165  * needed here if the pattern is actually used.
  2166  * We need to avoid doing this when this ChangeTransform comes from a restore,
  2167  * since the current pattern and the current transform are both part of the
  2168  * state we know the new CurrentState()'s values are valid. But if we assume
  2169  * a change they might become invalid since patternTransformChanged is part of
  2170  * the state and might be false for the restored AzureState.
  2171  */
  2172 void
  2173 gfxContext::ChangeTransform(const Matrix &aNewMatrix, bool aUpdatePatternTransform)
  2175   AzureState &state = CurrentState();
  2177   if (aUpdatePatternTransform && (state.pattern || state.sourceSurface)
  2178       && !state.patternTransformChanged) {
  2179     state.patternTransform = GetDTTransform();
  2180     state.patternTransformChanged = true;
  2183   if (mPathIsRect) {
  2184     Matrix invMatrix = aNewMatrix;
  2186     invMatrix.Invert();
  2188     Matrix toNewUS = mTransform * invMatrix;
  2190     if (toNewUS.IsRectilinear()) {
  2191       mRect = toNewUS.TransformBounds(mRect);
  2192       mRect.NudgeToIntegers();
  2193     } else {
  2194       mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule);
  2196       mPathBuilder->MoveTo(toNewUS * mRect.TopLeft());
  2197       mPathBuilder->LineTo(toNewUS * mRect.TopRight());
  2198       mPathBuilder->LineTo(toNewUS * mRect.BottomRight());
  2199       mPathBuilder->LineTo(toNewUS * mRect.BottomLeft());
  2200       mPathBuilder->Close();
  2202       mPathIsRect = false;
  2205     // No need to consider the transform changed now!
  2206     mTransformChanged = false;
  2207   } else if ((mPath || mPathBuilder) && !mTransformChanged) {
  2208     mTransformChanged = true;
  2209     mPathTransform = mTransform;
  2212   mTransform = aNewMatrix;
  2214   mDT->SetTransform(GetDTTransform());
  2217 Rect
  2218 gfxContext::GetAzureDeviceSpaceClipBounds()
  2220   unsigned int lastReset = 0;
  2221   for (int i = mStateStack.Length() - 1; i > 0; i--) {
  2222     if (mStateStack[i].clipWasReset) {
  2223       lastReset = i;
  2224       break;
  2228   Rect rect(CurrentState().deviceOffset.x, CurrentState().deviceOffset.y,
  2229             Float(mDT->GetSize().width), Float(mDT->GetSize().height));
  2230   for (unsigned int i = lastReset; i < mStateStack.Length(); i++) {
  2231     for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
  2232       AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
  2233       if (clip.path) {
  2234         Rect bounds = clip.path->GetBounds(clip.transform);
  2235         rect.IntersectRect(rect, bounds);
  2236       } else {
  2237         rect.IntersectRect(rect, clip.transform.TransformBounds(clip.rect));
  2242   return rect;
  2245 Point
  2246 gfxContext::GetDeviceOffset() const
  2248   return CurrentState().deviceOffset;
  2251 Matrix
  2252 gfxContext::GetDeviceTransform() const
  2254   Matrix mat;
  2255   mat.Translate(-CurrentState().deviceOffset.x, -CurrentState().deviceOffset.y);
  2256   return mat;
  2259 Matrix
  2260 gfxContext::GetDTTransform() const
  2262   Matrix mat = mTransform;
  2263   mat._31 -= CurrentState().deviceOffset.x;
  2264   mat._32 -= CurrentState().deviceOffset.y;
  2265   return mat;
  2268 void
  2269 gfxContext::PushNewDT(gfxContentType content)
  2271   Rect clipBounds = GetAzureDeviceSpaceClipBounds();
  2272   clipBounds.RoundOut();
  2274   clipBounds.width = std::max(1.0f, clipBounds.width);
  2275   clipBounds.height = std::max(1.0f, clipBounds.height);
  2277   SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content);
  2279   RefPtr<DrawTarget> newDT =
  2280     mDT->CreateSimilarDrawTarget(IntSize(int32_t(clipBounds.width), int32_t(clipBounds.height)),
  2281                                  format);
  2283   if (!newDT) {
  2284     NS_WARNING("Failed to create DrawTarget of sufficient size.");
  2285     newDT = mDT->CreateSimilarDrawTarget(IntSize(64, 64), format);
  2287     if (!newDT) {
  2288       // If even this fails.. we're most likely just out of memory!
  2289       NS_ABORT_OOM(BytesPerPixel(format) * 64 * 64);
  2293   Save();
  2295   CurrentState().drawTarget = newDT;
  2296   CurrentState().deviceOffset = clipBounds.TopLeft();
  2298   mDT = newDT;
  2301 /**
  2302  * Work out whether cairo will snap inter-glyph spacing to pixels.
  2304  * Layout does not align text to pixel boundaries, so, with font drawing
  2305  * backends that snap glyph positions to pixels, it is important that
  2306  * inter-glyph spacing within words is always an integer number of pixels.
  2307  * This ensures that the drawing backend snaps all of the word's glyphs in the
  2308  * same direction and so inter-glyph spacing remains the same.
  2309  */
  2310 void
  2311 gfxContext::GetRoundOffsetsToPixels(bool *aRoundX, bool *aRoundY)
  2313     *aRoundX = false;
  2314     // Could do something fancy here for ScaleFactors of
  2315     // AxisAlignedTransforms, but we leave things simple.
  2316     // Not much point rounding if a matrix will mess things up anyway.
  2317     // Also return false for non-cairo contexts.
  2318     if (CurrentMatrix().HasNonTranslation() || mDT) {
  2319         *aRoundY = false;
  2320         return;
  2323     // All raster backends snap glyphs to pixels vertically.
  2324     // Print backends set CAIRO_HINT_METRICS_OFF.
  2325     *aRoundY = true;
  2327     cairo_t *cr = GetCairo();
  2328     cairo_scaled_font_t *scaled_font = cairo_get_scaled_font(cr);
  2329     // Sometimes hint metrics gets set for us, most notably for printing.
  2330     cairo_font_options_t *font_options = cairo_font_options_create();
  2331     cairo_scaled_font_get_font_options(scaled_font, font_options);
  2332     cairo_hint_metrics_t hint_metrics =
  2333         cairo_font_options_get_hint_metrics(font_options);
  2334     cairo_font_options_destroy(font_options);
  2336     switch (hint_metrics) {
  2337     case CAIRO_HINT_METRICS_OFF:
  2338         *aRoundY = false;
  2339         return;
  2340     case CAIRO_HINT_METRICS_DEFAULT:
  2341         // Here we mimic what cairo surface/font backends do.  Printing
  2342         // surfaces have already been handled by hint_metrics.  The
  2343         // fallback show_glyphs implementation composites pixel-aligned
  2344         // glyph surfaces, so we just pick surface/font combinations that
  2345         // override this.
  2346         switch (cairo_scaled_font_get_type(scaled_font)) {
  2347 #if CAIRO_HAS_DWRITE_FONT // dwrite backend is not in std cairo releases yet
  2348         case CAIRO_FONT_TYPE_DWRITE:
  2349             // show_glyphs is implemented on the font and so is used for
  2350             // all surface types; however, it may pixel-snap depending on
  2351             // the dwrite rendering mode
  2352             if (!cairo_dwrite_scaled_font_get_force_GDI_classic(scaled_font) &&
  2353                 gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode() ==
  2354                     DWRITE_MEASURING_MODE_NATURAL) {
  2355                 return;
  2357 #endif
  2358         case CAIRO_FONT_TYPE_QUARTZ:
  2359             // Quartz surfaces implement show_glyphs for Quartz fonts
  2360             if (cairo_surface_get_type(cairo_get_target(cr)) ==
  2361                 CAIRO_SURFACE_TYPE_QUARTZ) {
  2362                 return;
  2364         default:
  2365             break;
  2367         // fall through:
  2368     case CAIRO_HINT_METRICS_ON:
  2369         break;
  2371     *aRoundX = true;
  2372     return;

mercurial