tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gfxContext.h (22820B)


      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/. */
      5 
      6 #ifndef GFX_CONTEXT_H
      7 #define GFX_CONTEXT_H
      8 
      9 #include "gfx2DGlue.h"
     10 #include "gfxPattern.h"
     11 #include "gfxUtils.h"
     12 #include "nsTArray.h"
     13 
     14 #include "mozilla/EnumSet.h"
     15 #include "mozilla/gfx/2D.h"
     16 
     17 typedef struct _cairo cairo_t;
     18 class GlyphBufferAzure;
     19 
     20 namespace mozilla {
     21 namespace gfx {
     22 struct RectCornerRadii;
     23 }  // namespace gfx
     24 namespace layout {
     25 class TextDrawTarget;
     26 }  // namespace layout
     27 }  // namespace mozilla
     28 
     29 class ClipExporter;
     30 
     31 /* This class lives on the stack and allows gfxContext users to easily, and
     32 * performantly get a gfx::Pattern to use for drawing in their current context.
     33 */
     34 class PatternFromState {
     35 public:
     36  explicit PatternFromState(const gfxContext* aContext)
     37      : mContext(aContext), mPattern(nullptr) {}
     38  ~PatternFromState() {
     39    if (mPattern) {
     40      mPattern->~Pattern();
     41    }
     42  }
     43 
     44  operator mozilla::gfx::Pattern&();
     45 
     46 private:
     47  mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern;
     48 
     49  const gfxContext* mContext;
     50  mozilla::gfx::Pattern* mPattern;
     51 };
     52 
     53 /**
     54 * This is the main class for doing actual drawing. It is initialized using
     55 * a surface and can be drawn on. It manages various state information like
     56 * a current transformation matrix (CTM), a current path, current color,
     57 * etc.
     58 *
     59 * All drawing happens by creating a path and then stroking or filling it.
     60 * The functions like Rectangle and Arc do not do any drawing themselves.
     61 * When a path is drawn (stroked or filled), it is filled/stroked with a
     62 * pattern set by SetPattern or SetColor.
     63 *
     64 * Note that the gfxContext takes coordinates in device pixels,
     65 * as opposed to app units.
     66 */
     67 class gfxContext final {
     68 #ifdef DEBUG
     69 #  define CURRENTSTATE_CHANGED() mAzureState.mContentChanged = true;
     70 #else
     71 #  define CURRENTSTATE_CHANGED()
     72 #endif
     73 
     74  typedef mozilla::gfx::BackendType BackendType;
     75  typedef mozilla::gfx::CapStyle CapStyle;
     76  typedef mozilla::gfx::CompositionOp CompositionOp;
     77  typedef mozilla::gfx::DeviceColor DeviceColor;
     78  typedef mozilla::gfx::DrawOptions DrawOptions;
     79  typedef mozilla::gfx::DrawTarget DrawTarget;
     80  typedef mozilla::gfx::JoinStyle JoinStyle;
     81  typedef mozilla::gfx::FillRule FillRule;
     82  typedef mozilla::gfx::Float Float;
     83  typedef mozilla::gfx::Matrix Matrix;
     84  typedef mozilla::gfx::Path Path;
     85  typedef mozilla::gfx::Pattern Pattern;
     86  typedef mozilla::gfx::Point Point;
     87  typedef mozilla::gfx::Rect Rect;
     88  typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
     89  typedef mozilla::gfx::Size Size;
     90 
     91 public:
     92  /**
     93   * Initialize this context from a DrawTarget, which must be non-null.
     94   * Strips any transform from aTarget, unless aPreserveTransform is true.
     95   * aTarget will be flushed in the gfxContext's destructor.
     96   */
     97  MOZ_NONNULL(2)
     98  explicit gfxContext(DrawTarget* aTarget, const Point& aDeviceOffset = Point())
     99      : mDT(aTarget) {
    100    mAzureState.deviceOffset = aDeviceOffset;
    101    mDT->SetTransform(GetDTTransform());
    102  }
    103 
    104  MOZ_NONNULL(2)
    105  gfxContext(DrawTarget* aTarget, bool aPreserveTransform) : mDT(aTarget) {
    106    if (aPreserveTransform) {
    107      SetMatrix(aTarget->GetTransform());
    108    } else {
    109      mDT->SetTransform(GetDTTransform());
    110    }
    111  }
    112 
    113  ~gfxContext();
    114 
    115  /**
    116   * Initialize this context from a DrawTarget.
    117   * Strips any transform from aTarget.
    118   * aTarget will be flushed in the gfxContext's destructor.
    119   * If aTarget is null or invalid, nullptr is returned.  The caller
    120   * is responsible for handling this scenario as appropriate.
    121   */
    122  static mozilla::UniquePtr<gfxContext> CreateOrNull(DrawTarget* aTarget);
    123 
    124  DrawTarget* GetDrawTarget() const { return mDT; }
    125 
    126  /**
    127   * Returns the DrawTarget if it's actually a TextDrawTarget.
    128   */
    129  mozilla::layout::TextDrawTarget* GetTextDrawer() const;
    130 
    131  /**
    132   ** State
    133   **/
    134  // XXX document exactly what bits are saved
    135  void Save();
    136  void Restore();
    137 
    138  /**
    139   ** Paths & Drawing
    140   **/
    141 
    142  /**
    143   * Fill the current path according to the current settings.
    144   *
    145   * Does not consume the current path.
    146   */
    147  void Fill() { Fill(PatternFromState(this)); }
    148  void Fill(const Pattern& aPattern);
    149 
    150  /**
    151   * Forgets the current path.
    152   */
    153  void NewPath() {
    154    mPath = nullptr;
    155    mPathBuilder = nullptr;
    156    mPathIsRect = false;
    157    mTransformChanged = false;
    158  }
    159 
    160  /**
    161   * Returns the current path.
    162   */
    163  already_AddRefed<Path> GetPath() {
    164    EnsurePath();
    165    RefPtr<Path> path(mPath);
    166    return path.forget();
    167  }
    168 
    169  /**
    170   * Sets the given path as the current path.
    171   */
    172  void SetPath(Path* path) {
    173    MOZ_ASSERT(path->GetBackendType() == mDT->GetBackendType() ||
    174               path->GetBackendType() == BackendType::RECORDING);
    175    mPath = path;
    176    mPathBuilder = nullptr;
    177    mPathIsRect = false;
    178    mTransformChanged = false;
    179  }
    180 
    181  /**
    182   * Draws the rectangle given by rect.
    183   */
    184  void Rectangle(const gfxRect& rect) { return Rectangle(rect, false); }
    185  void SnappedRectangle(const gfxRect& rect) { return Rectangle(rect, true); }
    186 
    187 private:
    188  void Rectangle(const gfxRect& rect, bool snapToPixels);
    189 
    190 public:
    191  /**
    192   ** Transformation Matrix manipulation
    193   **/
    194 
    195  /**
    196   * Post-multiplies 'other' onto the current CTM, i.e. this
    197   * matrix's transformation will take place before the previously set
    198   * transformations.
    199   */
    200  void Multiply(const gfxMatrix& aMatrix) { Multiply(ToMatrix(aMatrix)); }
    201  void Multiply(const Matrix& aOther) {
    202    CURRENTSTATE_CHANGED()
    203    ChangeTransform(aOther * mAzureState.transform);
    204  }
    205 
    206  /**
    207   * Replaces the current transformation matrix with matrix.
    208   */
    209  void SetMatrix(const Matrix& aMatrix) {
    210    CURRENTSTATE_CHANGED()
    211    ChangeTransform(aMatrix);
    212  }
    213  void SetMatrixDouble(const gfxMatrix& aMatrix) {
    214    SetMatrix(ToMatrix(aMatrix));
    215  }
    216 
    217  void SetCrossProcessPaintScale(float aScale) {
    218    MOZ_ASSERT(mCrossProcessPaintScale == 1.0f,
    219               "Should only be initialized once");
    220    mCrossProcessPaintScale = aScale;
    221  }
    222 
    223  float GetCrossProcessPaintScale() const { return mCrossProcessPaintScale; }
    224 
    225  /**
    226   * Returns the current transformation matrix.
    227   */
    228  Matrix CurrentMatrix() const { return mAzureState.transform; }
    229  gfxMatrix CurrentMatrixDouble() const {
    230    return ThebesMatrix(CurrentMatrix());
    231  }
    232 
    233  /**
    234   * Converts a point from device to user coordinates using the inverse
    235   * transformation matrix.
    236   */
    237  gfxPoint DeviceToUser(const gfxPoint& aPoint) const {
    238    return ThebesPoint(
    239        mAzureState.transform.Inverse().TransformPoint(ToPoint(aPoint)));
    240  }
    241 
    242  /**
    243   * Converts a size from device to user coordinates. This does not apply
    244   * translation components of the matrix.
    245   */
    246  Size DeviceToUser(const Size& aSize) const {
    247    return mAzureState.transform.Inverse().TransformSize(aSize);
    248  }
    249 
    250  /**
    251   * Converts a rectangle from device to user coordinates; this has the
    252   * same effect as using DeviceToUser on both the rectangle's point and
    253   * size.
    254   */
    255  gfxRect DeviceToUser(const gfxRect& aRect) const {
    256    return ThebesRect(
    257        mAzureState.transform.Inverse().TransformBounds(ToRect(aRect)));
    258  }
    259 
    260  /**
    261   * Converts a point from user to device coordinates using the transformation
    262   * matrix.
    263   */
    264  gfxPoint UserToDevice(const gfxPoint& aPoint) const {
    265    return ThebesPoint(mAzureState.transform.TransformPoint(ToPoint(aPoint)));
    266  }
    267 
    268  /**
    269   * Converts a size from user to device coordinates. This does not apply
    270   * translation components of the matrix.
    271   */
    272  Size UserToDevice(const Size& aSize) const {
    273    const auto& mtx = mAzureState.transform;
    274    return Size(aSize.width * mtx._11 + aSize.height * mtx._12,
    275                aSize.width * mtx._21 + aSize.height * mtx._22);
    276  }
    277 
    278  /**
    279   * Converts a rectangle from user to device coordinates.  The
    280   * resulting rectangle is the minimum device-space rectangle that
    281   * encloses the user-space rectangle given.
    282   */
    283  gfxRect UserToDevice(const gfxRect& rect) const {
    284    return ThebesRect(mAzureState.transform.TransformBounds(ToRect(rect)));
    285  }
    286 
    287  /**
    288   * Takes the given rect and tries to align it to device pixels.  If
    289   * this succeeds, the method will return true, and the rect will
    290   * be in device coordinates (already transformed by the CTM).  If it
    291   * fails, the method will return false, and the rect will not be
    292   * changed.
    293   *
    294   * aOptions parameter:
    295   *   If IgnoreScale is set, then snapping will take place even if the CTM
    296   *   has a scale applied. Snapping never takes place if there is a rotation
    297   *   in the CTM.
    298   *
    299   *   If PrioritizeSize is set, the rect's dimensions will first be snapped
    300   *   and then its position aligned to device pixels, rather than snapping
    301   *   the position of each edge independently.
    302   */
    303  enum class SnapOption : uint8_t {
    304    IgnoreScale = 1,
    305    PrioritizeSize = 2,
    306  };
    307  using SnapOptions = mozilla::EnumSet<SnapOption>;
    308  bool UserToDevicePixelSnapped(gfxRect& rect, SnapOptions aOptions = {}) const;
    309 
    310  /**
    311   * Takes the given point and tries to align it to device pixels.  If
    312   * this succeeds, the method will return true, and the point will
    313   * be in device coordinates (already transformed by the CTM).  If it
    314   * fails, the method will return false, and the point will not be
    315   * changed.
    316   *
    317   * If ignoreScale is true, then snapping will take place even if
    318   * the CTM has a scale applied.  Snapping never takes place if
    319   * there is a rotation in the CTM.
    320   */
    321  bool UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale = false) const;
    322 
    323  /**
    324   ** Painting sources
    325   **/
    326 
    327  /**
    328   * Set a solid color to use for drawing.  This color is in the device color
    329   * space and is not transformed.
    330   */
    331  void SetDeviceColor(const DeviceColor& aColor) {
    332    CURRENTSTATE_CHANGED()
    333    mAzureState.pattern = nullptr;
    334    mAzureState.color = aColor;
    335  }
    336 
    337  /**
    338   * Gets the current color.  It's returned in the device color space.
    339   * returns false if there is something other than a color
    340   *         set as the current source (pattern, surface, etc)
    341   */
    342  bool GetDeviceColor(DeviceColor& aColorOut) const;
    343 
    344  /**
    345   * Returns true if color is neither opaque nor transparent (i.e. alpha is not
    346   * 0 or 1), and false otherwise. If true, aColorOut is set on output.
    347   */
    348  bool HasNonOpaqueNonTransparentColor(DeviceColor& aColorOut) const {
    349    return GetDeviceColor(aColorOut) && 0.f < aColorOut.a && aColorOut.a < 1.f;
    350  }
    351 
    352  /**
    353   * Set a solid color in the sRGB color space to use for drawing.
    354   * If CMS is not enabled, the color is treated as a device-space color
    355   * and this call is identical to SetDeviceColor().
    356   */
    357  void SetColor(const mozilla::gfx::sRGBColor& aColor) {
    358    CURRENTSTATE_CHANGED()
    359    mAzureState.pattern = nullptr;
    360    mAzureState.color = ToDeviceColor(aColor);
    361  }
    362 
    363  /**
    364   * Uses a pattern for drawing.
    365   */
    366  void SetPattern(gfxPattern* pattern) {
    367    CURRENTSTATE_CHANGED()
    368    mAzureState.patternTransformChanged = false;
    369    mAzureState.pattern = pattern;
    370  }
    371 
    372  /**
    373   * Get the source pattern (solid color, normal pattern, surface, etc)
    374   */
    375  already_AddRefed<gfxPattern> GetPattern() const;
    376 
    377  /**
    378   ** Painting
    379   **/
    380  /**
    381   * Paints the current source surface/pattern everywhere in the current
    382   * clip region.
    383   */
    384  void Paint(Float alpha = 1.0) const;
    385 
    386  /**
    387   ** Line Properties
    388   **/
    389 
    390  // Set the dash pattern, applying devPxScale to convert passed-in lengths
    391  // to device pixels (used by the SVGUtils::SetupStrokeGeometry caller,
    392  // which has the desired dash pattern in CSS px).
    393  void SetDash(const Float* dashes, int ndash, Float offset, Float devPxScale);
    394 
    395  // Return true if dashing is set, false if it's not enabled or the
    396  // context is in an error state.  |offset| can be nullptr to mean
    397  // "don't care".
    398  bool CurrentDash(FallibleTArray<Float>& dashes, Float* offset) const;
    399 
    400  /**
    401   * Sets the line width that's used for line drawing.
    402   */
    403  void SetLineWidth(Float width) {
    404    CURRENTSTATE_CHANGED()
    405    mAzureState.strokeOptions.mLineWidth = width;
    406  }
    407 
    408  /**
    409   * Returns the currently set line width.
    410   *
    411   * @see SetLineWidth
    412   */
    413  Float CurrentLineWidth() const {
    414    return mAzureState.strokeOptions.mLineWidth;
    415  }
    416 
    417  /**
    418   * Sets the line caps, i.e. how line endings are drawn.
    419   */
    420  void SetLineCap(CapStyle cap) {
    421    CURRENTSTATE_CHANGED()
    422    mAzureState.strokeOptions.mLineCap = cap;
    423  }
    424  CapStyle CurrentLineCap() const { return mAzureState.strokeOptions.mLineCap; }
    425 
    426  /**
    427   * Sets the line join, i.e. how the connection between two lines is
    428   * drawn.
    429   */
    430  void SetLineJoin(JoinStyle join) {
    431    CURRENTSTATE_CHANGED()
    432    mAzureState.strokeOptions.mLineJoin = join;
    433  }
    434  JoinStyle CurrentLineJoin() const {
    435    return mAzureState.strokeOptions.mLineJoin;
    436  }
    437 
    438  void SetMiterLimit(Float limit) {
    439    CURRENTSTATE_CHANGED()
    440    mAzureState.strokeOptions.mMiterLimit = limit;
    441  }
    442  Float CurrentMiterLimit() const {
    443    return mAzureState.strokeOptions.mMiterLimit;
    444  }
    445 
    446  /**
    447   * Sets the operator used for all further drawing. The operator affects
    448   * how drawing something will modify the destination. For example, the
    449   * OVER operator will do alpha blending of source and destination, while
    450   * SOURCE will replace the destination with the source.
    451   */
    452  void SetOp(CompositionOp aOp) {
    453    CURRENTSTATE_CHANGED()
    454    mAzureState.op = aOp;
    455  }
    456  CompositionOp CurrentOp() const { return mAzureState.op; }
    457 
    458  void SetAntialiasMode(mozilla::gfx::AntialiasMode aMode) {
    459    CURRENTSTATE_CHANGED()
    460    mAzureState.aaMode = aMode;
    461  }
    462  mozilla::gfx::AntialiasMode CurrentAntialiasMode() const {
    463    return mAzureState.aaMode;
    464  }
    465 
    466  /**
    467   ** Clipping
    468   **/
    469 
    470  /**
    471   * Clips all further drawing to the current path.
    472   * This does not consume the current path.
    473   */
    474  void Clip();
    475 
    476  /**
    477   * Helper functions that will create a rect path and call Clip().
    478   * Any current path will be destroyed by these functions!
    479   */
    480  void Clip(const gfxRect& aRect) { Clip(ToRect(aRect)); }
    481  void Clip(const Rect& rect);            // will clip to a rect
    482  void SnappedClip(const gfxRect& rect);  // snap rect and clip to the result
    483  void Clip(Path* aPath);
    484 
    485  void PopClip() {
    486    MOZ_ASSERT(!mAzureState.pushedClips.IsEmpty());
    487    mAzureState.pushedClips.RemoveLastElement();
    488    mDT->PopClip();
    489  }
    490 
    491  enum ClipExtentsSpace {
    492    eUserSpace = 0,
    493    eDeviceSpace = 1,
    494  };
    495 
    496  /**
    497   * According to aSpace, this function will return the current bounds of
    498   * the clip region in user space or device space.
    499   */
    500  gfxRect GetClipExtents(ClipExtentsSpace aSpace = eUserSpace) const;
    501 
    502  /**
    503   * Exports the current clip using the provided exporter.
    504   */
    505  bool ExportClip(ClipExporter& aExporter) const;
    506 
    507  /**
    508   * Groups
    509   */
    510  void PushGroupForBlendBack(gfxContentType content, Float aOpacity = 1.0f,
    511                             mozilla::gfx::SourceSurface* aMask = nullptr,
    512                             const Matrix& aMaskTransform = Matrix()) const {
    513    mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask,
    514                   aMaskTransform);
    515  }
    516 
    517  void PopGroupAndBlend() const { mDT->PopLayer(); }
    518 
    519  Point GetDeviceOffset() const { return mAzureState.deviceOffset; }
    520  void SetDeviceOffset(const Point& aOffset) {
    521    mAzureState.deviceOffset = aOffset;
    522  }
    523 
    524 #ifdef MOZ_DUMP_PAINTING
    525  /**
    526   * Debug functions to encode the current surface as a PNG and export it.
    527   */
    528 
    529  /**
    530   * Writes a binary PNG file.
    531   */
    532  void WriteAsPNG(const char* aFile);
    533 
    534  /**
    535   * Write as a PNG encoded Data URL to stdout.
    536   */
    537  void DumpAsDataURI();
    538 
    539  /**
    540   * Copy a PNG encoded Data URL to the clipboard.
    541   */
    542  void CopyAsDataURI();
    543 #endif
    544 
    545 private:
    546  friend class PatternFromState;
    547  friend class GlyphBufferAzure;
    548 
    549  typedef mozilla::gfx::sRGBColor sRGBColor;
    550  typedef mozilla::gfx::StrokeOptions StrokeOptions;
    551  typedef mozilla::gfx::PathBuilder PathBuilder;
    552  typedef mozilla::gfx::SourceSurface SourceSurface;
    553 
    554  struct AzureState {
    555    AzureState()
    556        : op(CompositionOp::OP_OVER),
    557          color(0, 0, 0, 1.0f),
    558          aaMode(mozilla::gfx::AntialiasMode::SUBPIXEL),
    559          patternTransformChanged(false)
    560 #ifdef DEBUG
    561          ,
    562          mContentChanged(false)
    563 #endif
    564    {
    565    }
    566 
    567    CompositionOp op;
    568    DeviceColor color;
    569    RefPtr<gfxPattern> pattern;
    570    Matrix transform;
    571    struct PushedClip {
    572      RefPtr<Path> path;
    573      Rect rect;
    574      Matrix transform;
    575    };
    576    CopyableTArray<PushedClip> pushedClips;
    577    CopyableTArray<Float> dashPattern;
    578    StrokeOptions strokeOptions;
    579    mozilla::gfx::AntialiasMode aaMode;
    580    bool patternTransformChanged;
    581    Matrix patternTransform;
    582    // This is used solely for using minimal intermediate surface size.
    583    Point deviceOffset;
    584 #ifdef DEBUG
    585    // Whether the content of this AzureState changed after construction.
    586    bool mContentChanged;
    587 #endif
    588  };
    589 
    590  // This ensures mPath contains a valid path (in user space!)
    591  void EnsurePath();
    592  // This ensures mPathBuilder contains a valid PathBuilder (in user space!)
    593  void EnsurePathBuilder();
    594  CompositionOp GetOp() const;
    595  void ChangeTransform(const Matrix& aNewMatrix,
    596                       bool aUpdatePatternTransform = true);
    597  Rect GetAzureDeviceSpaceClipBounds() const;
    598  Matrix GetDTTransform() const {
    599    Matrix mat = mAzureState.transform;
    600    mat.PostTranslate(-mAzureState.deviceOffset);
    601    return mat;
    602  }
    603 
    604  bool mPathIsRect = false;
    605  bool mTransformChanged = false;
    606  Matrix mPathTransform;
    607  Rect mRect;
    608  RefPtr<PathBuilder> mPathBuilder;
    609  RefPtr<Path> mPath;
    610  AzureState mAzureState;
    611  nsTArray<AzureState> mSavedStates;
    612 
    613  // Iterate over all clips in the saved and current states, calling aLambda
    614  // with each of them.
    615  template <typename F>
    616  void ForAllClips(F&& aLambda) const;
    617 
    618  const AzureState& CurrentState() const { return mAzureState; }
    619 
    620  RefPtr<DrawTarget> const mDT;
    621  float mCrossProcessPaintScale = 1.0f;
    622 
    623 #ifdef DEBUG
    624 #  undef CURRENTSTATE_CHANGED
    625 #endif
    626 };
    627 
    628 /**
    629 * Sentry helper class for functions with multiple return points that need to
    630 * call Save() on a gfxContext and have Restore() called automatically on the
    631 * gfxContext before they return.
    632 */
    633 class MOZ_STACK_CLASS gfxContextAutoSaveRestore final {
    634 public:
    635  gfxContextAutoSaveRestore() : mContext(nullptr) {}
    636 
    637  explicit gfxContextAutoSaveRestore(gfxContext* aContext)
    638      : mContext(aContext) {
    639    mContext->Save();
    640  }
    641 
    642  ~gfxContextAutoSaveRestore() { Restore(); }
    643 
    644  void SetContext(gfxContext* aContext) {
    645    MOZ_ASSERT(!mContext, "no context?");
    646    mContext = aContext;
    647    mContext->Save();
    648  }
    649 
    650  void EnsureSaved(gfxContext* aContext) {
    651    MOZ_ASSERT(!mContext || mContext == aContext, "wrong context");
    652    if (!mContext) {
    653      mContext = aContext;
    654      mContext->Save();
    655    }
    656  }
    657 
    658  void Restore() {
    659    if (mContext) {
    660      mContext->Restore();
    661      mContext = nullptr;
    662    }
    663  }
    664 
    665 private:
    666  gfxContext* mContext;
    667 };
    668 
    669 /**
    670 * Sentry helper class for functions with multiple return points that need to
    671 * back up the current matrix of a context and have it automatically restored
    672 * before they return.
    673 */
    674 class MOZ_STACK_CLASS gfxContextMatrixAutoSaveRestore final {
    675 public:
    676  gfxContextMatrixAutoSaveRestore() : mContext(nullptr) {}
    677 
    678  explicit gfxContextMatrixAutoSaveRestore(gfxContext* aContext)
    679      : mContext(aContext), mMatrix(aContext->CurrentMatrix()) {}
    680 
    681  ~gfxContextMatrixAutoSaveRestore() {
    682    if (mContext) {
    683      mContext->SetMatrix(mMatrix);
    684    }
    685  }
    686 
    687  void SetContext(gfxContext* aContext) {
    688    NS_ASSERTION(!mContext, "Not going to restore the matrix on some context!");
    689    mContext = aContext;
    690    mMatrix = aContext->CurrentMatrix();
    691  }
    692 
    693  void Restore() {
    694    if (mContext) {
    695      mContext->SetMatrix(mMatrix);
    696      mContext = nullptr;
    697    }
    698  }
    699 
    700  const mozilla::gfx::Matrix& Matrix() {
    701    MOZ_ASSERT(mContext, "mMatrix doesn't contain a useful matrix");
    702    return mMatrix;
    703  }
    704 
    705  bool HasMatrix() const { return !!mContext; }
    706 
    707 private:
    708  gfxContext* mContext;
    709  mozilla::gfx::Matrix mMatrix;
    710 };
    711 
    712 class MOZ_STACK_CLASS gfxGroupForBlendAutoSaveRestore final {
    713 public:
    714  using Float = mozilla::gfx::Float;
    715  using Matrix = mozilla::gfx::Matrix;
    716 
    717  explicit gfxGroupForBlendAutoSaveRestore(gfxContext* aContext)
    718      : mContext(aContext) {}
    719 
    720  ~gfxGroupForBlendAutoSaveRestore() {
    721    if (mPushedGroup) {
    722      mContext->PopGroupAndBlend();
    723    }
    724  }
    725 
    726  void PushGroupForBlendBack(gfxContentType aContent, Float aOpacity = 1.0f,
    727                             mozilla::gfx::SourceSurface* aMask = nullptr,
    728                             const Matrix& aMaskTransform = Matrix()) {
    729    MOZ_ASSERT(!mPushedGroup, "Already called PushGroupForBlendBack once");
    730    mContext->PushGroupForBlendBack(aContent, aOpacity, aMask, aMaskTransform);
    731    mPushedGroup = true;
    732  }
    733 
    734 private:
    735  gfxContext* mContext;
    736  bool mPushedGroup = false;
    737 };
    738 
    739 class MOZ_STACK_CLASS gfxClipAutoSaveRestore final {
    740 public:
    741  using Rect = mozilla::gfx::Rect;
    742 
    743  explicit gfxClipAutoSaveRestore(gfxContext* aContext) : mContext(aContext) {}
    744 
    745  void Clip(const gfxRect& aRect) { Clip(ToRect(aRect)); }
    746 
    747  void Clip(const Rect& aRect) {
    748    MOZ_ASSERT(!mClipped, "Already called Clip once");
    749    mContext->Clip(aRect);
    750    mClipped = true;
    751  }
    752 
    753  void TransformedClip(const gfxMatrix& aTransform, const gfxRect& aRect) {
    754    MOZ_ASSERT(!mClipped, "Already called Clip once");
    755    if (aTransform.IsSingular()) {
    756      return;
    757    }
    758    gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(mContext);
    759    mContext->Multiply(aTransform);
    760    mContext->Clip(aRect);
    761    mClipped = true;
    762  }
    763 
    764  ~gfxClipAutoSaveRestore() {
    765    if (mClipped) {
    766      mContext->PopClip();
    767    }
    768  }
    769 
    770 private:
    771  gfxContext* mContext;
    772  bool mClipped = false;
    773 };
    774 
    775 class MOZ_STACK_CLASS DrawTargetAutoDisableSubpixelAntialiasing final {
    776 public:
    777  typedef mozilla::gfx::DrawTarget DrawTarget;
    778 
    779  DrawTargetAutoDisableSubpixelAntialiasing(DrawTarget* aDT, bool aDisable)
    780      : mSubpixelAntialiasingEnabled(false) {
    781    if (aDisable) {
    782      mDT = aDT;
    783      mSubpixelAntialiasingEnabled = mDT->GetPermitSubpixelAA();
    784      mDT->SetPermitSubpixelAA(false);
    785    }
    786  }
    787  ~DrawTargetAutoDisableSubpixelAntialiasing() {
    788    if (mDT) {
    789      mDT->SetPermitSubpixelAA(mSubpixelAntialiasingEnabled);
    790    }
    791  }
    792 
    793 private:
    794  RefPtr<DrawTarget> mDT;
    795  bool mSubpixelAntialiasingEnabled;
    796 };
    797 
    798 /* This interface should be implemented to handle exporting the clip from a
    799 * context.
    800 */
    801 class ClipExporter : public mozilla::gfx::PathSink {
    802 public:
    803  virtual void BeginClip(const mozilla::gfx::Matrix& aMatrix) = 0;
    804  virtual void EndClip() = 0;
    805 };
    806 
    807 #endif /* GFX_CONTEXT_H */