tor-browser

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

DrawTargetCairo.cpp (68451B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "DrawTargetCairo.h"
      8 
      9 #include "SourceSurfaceCairo.h"
     10 #include "PathCairo.h"
     11 #include "HelpersCairo.h"
     12 #include "BorrowedContext.h"
     13 #include "FilterNodeSoftware.h"
     14 #include "mozilla/Vector.h"
     15 #include "mozilla/StaticPrefs_gfx.h"
     16 #include "mozilla/StaticPrefs_print.h"
     17 #include "nsPrintfCString.h"
     18 
     19 #include "cairo.h"
     20 #include "cairo-tee.h"
     21 #include <string.h>
     22 
     23 #include "Blur.h"
     24 #include "Logging.h"
     25 #include "Tools.h"
     26 
     27 #ifdef CAIRO_HAS_QUARTZ_SURFACE
     28 #  include "cairo-quartz.h"
     29 #  ifdef MOZ_WIDGET_COCOA
     30 #    include <ApplicationServices/ApplicationServices.h>
     31 #  endif
     32 #endif
     33 
     34 #ifdef CAIRO_HAS_XLIB_SURFACE
     35 #  include "cairo-xlib.h"
     36 #endif
     37 
     38 #ifdef CAIRO_HAS_WIN32_SURFACE
     39 #  include "cairo-win32.h"
     40 #endif
     41 
     42 #define PIXMAN_DONT_DEFINE_STDINT
     43 #include "pixman.h"
     44 
     45 // 2^23
     46 #define CAIRO_COORD_MAX (Float(0x7fffff))
     47 
     48 namespace mozilla {
     49 namespace gfx {
     50 
     51 cairo_surface_t* DrawTargetCairo::mDummySurface;
     52 
     53 namespace {
     54 
     55 // An RAII class to prepare to draw a context and optional path. Saves and
     56 // restores the context on construction/destruction.
     57 class AutoPrepareForDrawing {
     58 public:
     59  AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx) : mCtx(ctx) {
     60    dt->PrepareForDrawing(ctx);
     61    cairo_save(mCtx);
     62    MOZ_ASSERT(cairo_status(mCtx) ||
     63               dt->GetTransform().FuzzyEquals(GetTransform()));
     64  }
     65 
     66  AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx, const Path* path)
     67      : mCtx(ctx) {
     68    dt->PrepareForDrawing(ctx, path);
     69    cairo_save(mCtx);
     70    MOZ_ASSERT(cairo_status(mCtx) ||
     71               dt->GetTransform().FuzzyEquals(GetTransform()));
     72  }
     73 
     74  ~AutoPrepareForDrawing() {
     75    cairo_restore(mCtx);
     76    cairo_status_t status = cairo_status(mCtx);
     77    if (status) {
     78      gfxWarning() << "DrawTargetCairo context in error state: "
     79                   << cairo_status_to_string(status) << "(" << status << ")";
     80    }
     81  }
     82 
     83 private:
     84 #ifdef DEBUG
     85  Matrix GetTransform() {
     86    cairo_matrix_t mat;
     87    cairo_get_matrix(mCtx, &mat);
     88    return Matrix(mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0);
     89  }
     90 #endif
     91 
     92  cairo_t* mCtx;
     93 };
     94 
     95 /* Clamp r to (0,0) (2^23,2^23)
     96 * these are to be device coordinates.
     97 *
     98 * Returns false if the rectangle is completely out of bounds,
     99 * true otherwise.
    100 *
    101 * This function assumes that it will be called with a rectangle being
    102 * drawn into a surface with an identity transformation matrix; that
    103 * is, anything above or to the left of (0,0) will be offscreen.
    104 *
    105 * First it checks if the rectangle is entirely beyond
    106 * CAIRO_COORD_MAX; if so, it can't ever appear on the screen --
    107 * false is returned.
    108 *
    109 * Then it shifts any rectangles with x/y < 0 so that x and y are = 0,
    110 * and adjusts the width and height appropriately.  For example, a
    111 * rectangle from (0,-5) with dimensions (5,10) will become a
    112 * rectangle from (0,0) with dimensions (5,5).
    113 *
    114 * If after negative x/y adjustment to 0, either the width or height
    115 * is negative, then the rectangle is completely offscreen, and
    116 * nothing is drawn -- false is returned.
    117 *
    118 * Finally, if x+width or y+height are greater than CAIRO_COORD_MAX,
    119 * the width and height are clamped such x+width or y+height are equal
    120 * to CAIRO_COORD_MAX, and true is returned.
    121 */
    122 static bool ConditionRect(Rect& r) {
    123  // if either x or y is way out of bounds;
    124  // note that we don't handle negative w/h here
    125  if (r.X() > CAIRO_COORD_MAX || r.Y() > CAIRO_COORD_MAX) return false;
    126 
    127  if (r.X() < 0.f) {
    128    r.SetWidth(r.XMost());
    129    if (r.Width() < 0.f) return false;
    130    r.MoveToX(0.f);
    131  }
    132 
    133  if (r.XMost() > CAIRO_COORD_MAX) {
    134    r.SetRightEdge(CAIRO_COORD_MAX);
    135  }
    136 
    137  if (r.Y() < 0.f) {
    138    r.SetHeight(r.YMost());
    139    if (r.Height() < 0.f) return false;
    140 
    141    r.MoveToY(0.f);
    142  }
    143 
    144  if (r.YMost() > CAIRO_COORD_MAX) {
    145    r.SetBottomEdge(CAIRO_COORD_MAX);
    146  }
    147  return true;
    148 }
    149 
    150 }  // end anonymous namespace
    151 
    152 static bool SupportsSelfCopy(cairo_surface_t* surface) {
    153  switch (cairo_surface_get_type(surface)) {
    154 #ifdef CAIRO_HAS_QUARTZ_SURFACE
    155    case CAIRO_SURFACE_TYPE_QUARTZ:
    156      return true;
    157 #endif
    158 #ifdef CAIRO_HAS_WIN32_SURFACE
    159    case CAIRO_SURFACE_TYPE_WIN32:
    160    case CAIRO_SURFACE_TYPE_WIN32_PRINTING:
    161      return true;
    162 #endif
    163    default:
    164      return false;
    165  }
    166 }
    167 
    168 static bool PatternIsCompatible(const Pattern& aPattern) {
    169  switch (aPattern.GetType()) {
    170    case PatternType::LINEAR_GRADIENT: {
    171      const LinearGradientPattern& pattern =
    172          static_cast<const LinearGradientPattern&>(aPattern);
    173      return pattern.mStops->GetBackendType() == BackendType::CAIRO;
    174    }
    175    case PatternType::RADIAL_GRADIENT: {
    176      const RadialGradientPattern& pattern =
    177          static_cast<const RadialGradientPattern&>(aPattern);
    178      return pattern.mStops->GetBackendType() == BackendType::CAIRO;
    179    }
    180    case PatternType::CONIC_GRADIENT: {
    181      const ConicGradientPattern& pattern =
    182          static_cast<const ConicGradientPattern&>(aPattern);
    183      return pattern.mStops->GetBackendType() == BackendType::CAIRO;
    184    }
    185    default:
    186      return true;
    187  }
    188 }
    189 
    190 static cairo_user_data_key_t surfaceDataKey;
    191 
    192 static void ReleaseData(void* aData) {
    193  DataSourceSurface* data = static_cast<DataSourceSurface*>(aData);
    194  data->Unmap();
    195  data->Release();
    196 }
    197 
    198 static cairo_surface_t* CopyToImageSurface(unsigned char* aData,
    199                                           const IntSize& aSize,
    200                                           const IntRect& aRect,
    201                                           int32_t aStride,
    202                                           SurfaceFormat aFormat) {
    203  MOZ_ASSERT(aData);
    204 
    205  auto aRectWidth = aRect.Width();
    206  auto aRectHeight = aRect.Height();
    207 
    208  cairo_surface_t* surf = cairo_image_surface_create(
    209      GfxFormatToCairoFormat(aFormat), aRectWidth, aRectHeight);
    210  // In certain scenarios, requesting larger than 8k image fails.  Bug 803568
    211  // covers the details of how to run into it, but the full detailed
    212  // investigation hasn't been done to determine the underlying cause.  We
    213  // will just handle the failure to allocate the surface to avoid a crash.
    214  if (cairo_surface_status(surf)) {
    215    gfxWarning() << "Invalid surface DTC " << cairo_surface_status(surf);
    216    return nullptr;
    217  }
    218 
    219  unsigned char* surfData = cairo_image_surface_get_data(surf);
    220  size_t surfStride = cairo_image_surface_get_stride(surf);
    221  size_t pixelWidth = BytesPerPixel(aFormat);
    222  size_t rowDataWidth = size_t(aRectWidth) * pixelWidth;
    223  if (rowDataWidth > surfStride || rowDataWidth > size_t(aStride) ||
    224      !IntRect(IntPoint(), aSize).Contains(aRect)) {
    225    cairo_surface_destroy(surf);
    226    return nullptr;
    227  }
    228 
    229  const unsigned char* sourceRow = aData + size_t(aRect.Y()) * size_t(aStride) +
    230                                   size_t(aRect.X()) * pixelWidth;
    231  unsigned char* destRow = surfData;
    232 
    233  for (int32_t y = 0; y < aRectHeight; ++y) {
    234    memcpy(destRow, sourceRow, rowDataWidth);
    235    sourceRow += aStride;
    236    destRow += surfStride;
    237  }
    238  cairo_surface_mark_dirty(surf);
    239  return surf;
    240 }
    241 
    242 /**
    243 * If aSurface can be represented as a surface of type
    244 * CAIRO_SURFACE_TYPE_IMAGE then returns that surface. Does
    245 * not add a reference.
    246 */
    247 static cairo_surface_t* GetAsImageSurface(cairo_surface_t* aSurface) {
    248  if (cairo_surface_get_type(aSurface) == CAIRO_SURFACE_TYPE_IMAGE) {
    249    return aSurface;
    250 #ifdef CAIRO_HAS_WIN32_SURFACE
    251  } else if (cairo_surface_get_type(aSurface) == CAIRO_SURFACE_TYPE_WIN32) {
    252    return cairo_win32_surface_get_image(aSurface);
    253 #endif
    254  }
    255 
    256  return nullptr;
    257 }
    258 
    259 static cairo_surface_t* CreateSubImageForData(unsigned char* aData,
    260                                              const IntSize& aSize,
    261                                              const IntRect& aRect, int aStride,
    262                                              SurfaceFormat aFormat) {
    263  if (!aData || aStride < 0 || !IntRect(IntPoint(), aSize).Contains(aRect)) {
    264    gfxWarning() << "DrawTargetCairo.CreateSubImageForData null aData";
    265    return nullptr;
    266  }
    267  unsigned char* data = aData + size_t(aRect.Y()) * size_t(aStride) +
    268                        size_t(aRect.X()) * size_t(BytesPerPixel(aFormat));
    269 
    270  cairo_surface_t* image = cairo_image_surface_create_for_data(
    271      data, GfxFormatToCairoFormat(aFormat), aRect.Width(), aRect.Height(),
    272      aStride);
    273  // Set the subimage's device offset so that in remains in the same place
    274  // relative to the parent
    275  cairo_surface_set_device_offset(image, -aRect.X(), -aRect.Y());
    276  return image;
    277 }
    278 
    279 /**
    280 * Returns a referenced cairo_surface_t representing the
    281 * sub-image specified by aSubImage.
    282 */
    283 static cairo_surface_t* ExtractSubImage(cairo_surface_t* aSurface,
    284                                        const IntRect& aSubImage,
    285                                        SurfaceFormat aFormat) {
    286  // No need to worry about retaining a reference to the original
    287  // surface since the only caller of this function guarantees
    288  // that aSurface will stay alive as long as the result
    289 
    290  cairo_surface_t* image = GetAsImageSurface(aSurface);
    291  if (image) {
    292    image = CreateSubImageForData(
    293        cairo_image_surface_get_data(image),
    294        IntSize(cairo_image_surface_get_width(image),
    295                cairo_image_surface_get_height(image)),
    296        aSubImage, cairo_image_surface_get_stride(image), aFormat);
    297    return image;
    298  }
    299 
    300  cairo_surface_t* similar = cairo_surface_create_similar(
    301      aSurface, cairo_surface_get_content(aSurface), aSubImage.Width(),
    302      aSubImage.Height());
    303 
    304  cairo_t* ctx = cairo_create(similar);
    305  cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
    306  cairo_set_source_surface(ctx, aSurface, -aSubImage.X(), -aSubImage.Y());
    307  cairo_paint(ctx);
    308  cairo_destroy(ctx);
    309 
    310  cairo_surface_set_device_offset(similar, -aSubImage.X(), -aSubImage.Y());
    311  return similar;
    312 }
    313 
    314 /**
    315 * Returns cairo surface for the given SourceSurface.
    316 * If possible, it will use the cairo_surface associated with aSurface,
    317 * otherwise, it will create a new cairo_surface.
    318 * In either case, the caller must call cairo_surface_destroy on the
    319 * result when it is done with it.
    320 */
    321 static cairo_surface_t* GetCairoSurfaceForSourceSurface(
    322    SourceSurface* aSurface, bool aExistingOnly = false,
    323    const IntRect& aSubImage = IntRect()) {
    324  if (!aSurface) {
    325    return nullptr;
    326  }
    327 
    328  IntRect subimage = IntRect(IntPoint(), aSurface->GetSize());
    329  if (!aSubImage.IsEmpty()) {
    330    MOZ_ASSERT(!aExistingOnly);
    331    MOZ_ASSERT(subimage.Contains(aSubImage));
    332    subimage = aSubImage;
    333  }
    334 
    335  if (aSurface->GetType() == SurfaceType::CAIRO) {
    336    cairo_surface_t* surf =
    337        static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface();
    338    if (aSubImage.IsEmpty()) {
    339      cairo_surface_reference(surf);
    340    } else {
    341      surf = ExtractSubImage(surf, subimage, aSurface->GetFormat());
    342    }
    343    return surf;
    344  }
    345 
    346  if (aSurface->GetType() == SurfaceType::CAIRO_IMAGE) {
    347    cairo_surface_t* surf =
    348        static_cast<const DataSourceSurfaceCairo*>(aSurface)->GetSurface();
    349    if (aSubImage.IsEmpty()) {
    350      cairo_surface_reference(surf);
    351    } else {
    352      surf = ExtractSubImage(surf, subimage, aSurface->GetFormat());
    353    }
    354    return surf;
    355  }
    356 
    357  if (aExistingOnly) {
    358    return nullptr;
    359  }
    360 
    361  RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
    362  if (!data) {
    363    return nullptr;
    364  }
    365 
    366  DataSourceSurface::MappedSurface map;
    367  if (!data->Map(DataSourceSurface::READ, &map)) {
    368    return nullptr;
    369  }
    370 
    371  cairo_surface_t* surf = CreateSubImageForData(
    372      map.mData, data->GetSize(), subimage, map.mStride, data->GetFormat());
    373 
    374  // In certain scenarios, requesting larger than 8k image fails.  Bug 803568
    375  // covers the details of how to run into it, but the full detailed
    376  // investigation hasn't been done to determine the underlying cause.  We
    377  // will just handle the failure to allocate the surface to avoid a crash.
    378  if (!surf || cairo_surface_status(surf)) {
    379    if (surf && (cairo_surface_status(surf) == CAIRO_STATUS_INVALID_STRIDE)) {
    380      // If we failed because of an invalid stride then copy into
    381      // a new surface with a stride that cairo chooses. No need to
    382      // set user data since we're not dependent on the original
    383      // data.
    384      cairo_surface_t* result = CopyToImageSurface(
    385          map.mData, data->GetSize(), subimage, map.mStride, data->GetFormat());
    386      data->Unmap();
    387      return result;
    388    }
    389    data->Unmap();
    390    return nullptr;
    391  }
    392 
    393  cairo_surface_set_user_data(surf, &surfaceDataKey, data.forget().take(),
    394                              ReleaseData);
    395  return surf;
    396 }
    397 
    398 // An RAII class to temporarily clear any device offset set
    399 // on a surface. Note that this does not take a reference to the
    400 // surface.
    401 class AutoClearDeviceOffset final {
    402 public:
    403  explicit AutoClearDeviceOffset(SourceSurface* aSurface)
    404      : mSurface(nullptr), mX(0), mY(0) {
    405    Init(aSurface);
    406  }
    407 
    408  explicit AutoClearDeviceOffset(const Pattern& aPattern)
    409      : mSurface(nullptr), mX(0.0), mY(0.0) {
    410    if (aPattern.GetType() == PatternType::SURFACE) {
    411      const SurfacePattern& pattern =
    412          static_cast<const SurfacePattern&>(aPattern);
    413      Init(pattern.mSurface);
    414    }
    415  }
    416 
    417  ~AutoClearDeviceOffset() {
    418    if (mSurface) {
    419      cairo_surface_set_device_offset(mSurface, mX, mY);
    420    }
    421  }
    422 
    423 private:
    424  void Init(SourceSurface* aSurface) {
    425    cairo_surface_t* surface = GetCairoSurfaceForSourceSurface(aSurface, true);
    426    if (surface) {
    427      Init(surface);
    428      cairo_surface_destroy(surface);
    429    }
    430  }
    431 
    432  void Init(cairo_surface_t* aSurface) {
    433    mSurface = aSurface;
    434    cairo_surface_get_device_offset(mSurface, &mX, &mY);
    435    cairo_surface_set_device_offset(mSurface, 0, 0);
    436  }
    437 
    438  cairo_surface_t* mSurface;
    439  double mX;
    440  double mY;
    441 };
    442 
    443 static inline void CairoPatternAddGradientStop(cairo_pattern_t* aPattern,
    444                                               const GradientStop& aStop,
    445                                               Float aNudge = 0) {
    446  cairo_pattern_add_color_stop_rgba(aPattern, aStop.offset + aNudge,
    447                                    aStop.color.r, aStop.color.g, aStop.color.b,
    448                                    aStop.color.a);
    449 }
    450 
    451 // Never returns nullptr. As such, you must always pass in Cairo-compatible
    452 // patterns, most notably gradients with a GradientStopCairo.
    453 // The pattern returned must have cairo_pattern_destroy() called on it by the
    454 // caller.
    455 // As the cairo_pattern_t returned may depend on the Pattern passed in, the
    456 // lifetime of the cairo_pattern_t returned must not exceed the lifetime of the
    457 // Pattern passed in.
    458 static cairo_pattern_t* GfxPatternToCairoPattern(const Pattern& aPattern,
    459                                                 Float aAlpha,
    460                                                 const Matrix& aTransform) {
    461  cairo_pattern_t* pat;
    462  const Matrix* matrix = nullptr;
    463 
    464  switch (aPattern.GetType()) {
    465    case PatternType::COLOR: {
    466      DeviceColor color = static_cast<const ColorPattern&>(aPattern).mColor;
    467      pat = cairo_pattern_create_rgba(color.r, color.g, color.b,
    468                                      color.a * aAlpha);
    469      break;
    470    }
    471 
    472    case PatternType::SURFACE: {
    473      const SurfacePattern& pattern =
    474          static_cast<const SurfacePattern&>(aPattern);
    475      cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(
    476          pattern.mSurface, false, pattern.mSamplingRect);
    477      if (!surf) return nullptr;
    478 
    479      pat = cairo_pattern_create_for_surface(surf);
    480 
    481      matrix = &pattern.mMatrix;
    482 
    483      cairo_pattern_set_filter(
    484          pat, GfxSamplingFilterToCairoFilter(pattern.mSamplingFilter));
    485      cairo_pattern_set_extend(pat,
    486                               GfxExtendToCairoExtend(pattern.mExtendMode));
    487 
    488      cairo_surface_destroy(surf);
    489      break;
    490    }
    491    case PatternType::LINEAR_GRADIENT: {
    492      const LinearGradientPattern& pattern =
    493          static_cast<const LinearGradientPattern&>(aPattern);
    494 
    495      pat = cairo_pattern_create_linear(pattern.mBegin.x, pattern.mBegin.y,
    496                                        pattern.mEnd.x, pattern.mEnd.y);
    497 
    498      MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO);
    499      GradientStopsCairo* cairoStops =
    500          static_cast<GradientStopsCairo*>(pattern.mStops.get());
    501      cairo_pattern_set_extend(
    502          pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode()));
    503 
    504      matrix = &pattern.mMatrix;
    505 
    506      const std::vector<GradientStop>& stops = cairoStops->GetStops();
    507      for (size_t i = 0; i < stops.size(); ++i) {
    508        CairoPatternAddGradientStop(pat, stops[i]);
    509      }
    510 
    511      break;
    512    }
    513    case PatternType::RADIAL_GRADIENT: {
    514      const RadialGradientPattern& pattern =
    515          static_cast<const RadialGradientPattern&>(aPattern);
    516 
    517      pat = cairo_pattern_create_radial(pattern.mCenter1.x, pattern.mCenter1.y,
    518                                        pattern.mRadius1, pattern.mCenter2.x,
    519                                        pattern.mCenter2.y, pattern.mRadius2);
    520 
    521      MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO);
    522      GradientStopsCairo* cairoStops =
    523          static_cast<GradientStopsCairo*>(pattern.mStops.get());
    524      cairo_pattern_set_extend(
    525          pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode()));
    526 
    527      matrix = &pattern.mMatrix;
    528 
    529      const std::vector<GradientStop>& stops = cairoStops->GetStops();
    530      for (size_t i = 0; i < stops.size(); ++i) {
    531        CairoPatternAddGradientStop(pat, stops[i]);
    532      }
    533 
    534      break;
    535    }
    536    case PatternType::CONIC_GRADIENT: {
    537      // XXX(ntim): Bug 1617039 - Implement conic-gradient for Cairo
    538      pat = cairo_pattern_create_rgba(0.0, 0.0, 0.0, 0.0);
    539 
    540      break;
    541    }
    542    default: {
    543      // We should support all pattern types!
    544      MOZ_ASSERT(false);
    545    }
    546  }
    547 
    548  // The pattern matrix is a matrix that transforms the pattern into user
    549  // space. Cairo takes a matrix that converts from user space to pattern
    550  // space. Cairo therefore needs the inverse.
    551  if (matrix) {
    552    cairo_matrix_t mat;
    553    GfxMatrixToCairoMatrix(*matrix, mat);
    554    cairo_matrix_invert(&mat);
    555    cairo_pattern_set_matrix(pat, &mat);
    556  }
    557 
    558  return pat;
    559 }
    560 
    561 static bool NeedIntermediateSurface(const Pattern& aPattern,
    562                                    const DrawOptions& aOptions) {
    563  // We pre-multiply colours' alpha by the global alpha, so we don't need to
    564  // use an intermediate surface for them.
    565  if (aPattern.GetType() == PatternType::COLOR) return false;
    566 
    567  if (aOptions.mAlpha == 1.0) return false;
    568 
    569  return true;
    570 }
    571 
    572 DrawTargetCairo::DrawTargetCairo()
    573    : mContext(nullptr),
    574      mSurface(nullptr),
    575      mTransformSingular(false),
    576      mLockedBits(nullptr),
    577      mFontOptions(nullptr) {}
    578 
    579 DrawTargetCairo::~DrawTargetCairo() {
    580  cairo_destroy(mContext);
    581  if (mSurface) {
    582    cairo_surface_destroy(mSurface);
    583    mSurface = nullptr;
    584  }
    585  if (mFontOptions) {
    586    cairo_font_options_destroy(mFontOptions);
    587    mFontOptions = nullptr;
    588  }
    589  MOZ_ASSERT(!mLockedBits);
    590 }
    591 
    592 bool DrawTargetCairo::IsValid() const {
    593  return mSurface && !cairo_surface_status(mSurface) && mContext &&
    594         !cairo_surface_status(cairo_get_group_target(mContext));
    595 }
    596 
    597 DrawTargetType DrawTargetCairo::GetType() const {
    598  if (mContext) {
    599    cairo_surface_type_t type = cairo_surface_get_type(mSurface);
    600    if (type == CAIRO_SURFACE_TYPE_TEE) {
    601      type = cairo_surface_get_type(cairo_tee_surface_index(mSurface, 0));
    602      MOZ_ASSERT(type != CAIRO_SURFACE_TYPE_TEE, "C'mon!");
    603      MOZ_ASSERT(
    604          type == cairo_surface_get_type(cairo_tee_surface_index(mSurface, 1)),
    605          "What should we do here?");
    606    }
    607    switch (type) {
    608      case CAIRO_SURFACE_TYPE_PDF:
    609      case CAIRO_SURFACE_TYPE_PS:
    610      case CAIRO_SURFACE_TYPE_SVG:
    611      case CAIRO_SURFACE_TYPE_WIN32_PRINTING:
    612      case CAIRO_SURFACE_TYPE_XML:
    613        return DrawTargetType::VECTOR;
    614 
    615      case CAIRO_SURFACE_TYPE_VG:
    616      case CAIRO_SURFACE_TYPE_GL:
    617      case CAIRO_SURFACE_TYPE_GLITZ:
    618      case CAIRO_SURFACE_TYPE_QUARTZ:
    619      case CAIRO_SURFACE_TYPE_DIRECTFB:
    620        return DrawTargetType::HARDWARE_RASTER;
    621 
    622      case CAIRO_SURFACE_TYPE_SKIA:
    623      case CAIRO_SURFACE_TYPE_QT:
    624        MOZ_FALLTHROUGH_ASSERT(
    625            "Can't determine actual DrawTargetType for DrawTargetCairo - "
    626            "assuming SOFTWARE_RASTER");
    627      case CAIRO_SURFACE_TYPE_IMAGE:
    628      case CAIRO_SURFACE_TYPE_XLIB:
    629      case CAIRO_SURFACE_TYPE_XCB:
    630      case CAIRO_SURFACE_TYPE_WIN32:
    631      case CAIRO_SURFACE_TYPE_BEOS:
    632      case CAIRO_SURFACE_TYPE_OS2:
    633      case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE:
    634      case CAIRO_SURFACE_TYPE_SCRIPT:
    635      case CAIRO_SURFACE_TYPE_RECORDING:
    636      case CAIRO_SURFACE_TYPE_DRM:
    637      case CAIRO_SURFACE_TYPE_SUBSURFACE:
    638      case CAIRO_SURFACE_TYPE_TEE:  // included to silence warning about
    639                                    // unhandled enum value
    640        return DrawTargetType::SOFTWARE_RASTER;
    641      default:
    642        MOZ_CRASH("GFX: Unsupported cairo surface type");
    643    }
    644  }
    645  MOZ_ASSERT(false, "Could not determine DrawTargetType for DrawTargetCairo");
    646  return DrawTargetType::SOFTWARE_RASTER;
    647 }
    648 
    649 IntSize DrawTargetCairo::GetSize() const { return mSize; }
    650 
    651 SurfaceFormat GfxFormatForCairoSurface(cairo_surface_t* surface) {
    652  cairo_surface_type_t type = cairo_surface_get_type(surface);
    653  if (type == CAIRO_SURFACE_TYPE_IMAGE) {
    654    return CairoFormatToGfxFormat(cairo_image_surface_get_format(surface));
    655  }
    656 #ifdef CAIRO_HAS_XLIB_SURFACE
    657  // xlib is currently the only Cairo backend that creates 16bpp surfaces
    658  if (type == CAIRO_SURFACE_TYPE_XLIB &&
    659      cairo_xlib_surface_get_depth(surface) == 16) {
    660    return SurfaceFormat::R5G6B5_UINT16;
    661  }
    662 #endif
    663  return CairoContentToGfxFormat(cairo_surface_get_content(surface));
    664 }
    665 
    666 // We need to \-escape any single-quotes in the destination and URI strings,
    667 // in order to pass them via the attributes arg to cairo_tag_begin.
    668 //
    669 // We also need to escape any backslashes (bug 1748077), as per doc at
    670 // https://www.cairographics.org/manual/cairo-Tags-and-Links.html#cairo-tag-begin
    671 //
    672 // (Encoding of non-ASCII chars etc gets handled later by the PDF backend.)
    673 static void EscapeForCairo(nsACString& aStr) {
    674  for (size_t i = aStr.Length(); i > 0;) {
    675    --i;
    676    if (aStr[i] == '\'') {
    677      aStr.ReplaceLiteral(i, 1, "\\'");
    678    } else if (aStr[i] == '\\') {
    679      aStr.ReplaceLiteral(i, 1, "\\\\");
    680    }
    681  }
    682 }
    683 
    684 void DrawTargetCairo::Link(const char* aDest, const char* aURI,
    685                           const Rect& aRect) {
    686  if ((!aURI || !*aURI) && (!aDest || !*aDest)) {
    687    // No destination? Just bail out.
    688    return;
    689  }
    690 
    691  if (!IsValid()) {
    692    gfxCriticalNote << "Link with bad surface "
    693                    << cairo_surface_status(cairo_get_group_target(mContext));
    694    return;
    695  }
    696 
    697  double x = aRect.x, y = aRect.y, w = aRect.width, h = aRect.height;
    698  cairo_user_to_device(mContext, &x, &y);
    699  cairo_user_to_device_distance(mContext, &w, &h);
    700  nsPrintfCString attributes("rect=[%f %f %f %f]", x, y, w, h);
    701 
    702  if (aDest && *aDest) {
    703    nsAutoCString dest(aDest);
    704    EscapeForCairo(dest);
    705    attributes.AppendPrintf(" dest='%s'", dest.get());
    706  }
    707  if (aURI && *aURI) {
    708    nsAutoCString uri(aURI);
    709    EscapeForCairo(uri);
    710    attributes.AppendPrintf(" uri='%s'", uri.get());
    711  }
    712 
    713  // We generate a begin/end pair with no content in between, because we are
    714  // using the rect attribute of the begin tag to specify the link region
    715  // rather than depending on cairo to accumulate the painted area.
    716  cairo_tag_begin(mContext, CAIRO_TAG_LINK, attributes.get());
    717  cairo_tag_end(mContext, CAIRO_TAG_LINK);
    718 }
    719 
    720 void DrawTargetCairo::Destination(const char* aDestination,
    721                                  const Point& aPoint) {
    722  if (!aDestination || !*aDestination) {
    723    // No destination? Just bail out.
    724    return;
    725  }
    726 
    727  if (!IsValid()) {
    728    gfxCriticalNote << "Destination with bad surface "
    729                    << cairo_surface_status(cairo_get_group_target(mContext));
    730    return;
    731  }
    732 
    733  nsAutoCString dest(aDestination);
    734  EscapeForCairo(dest);
    735 
    736  double x = aPoint.x, y = aPoint.y;
    737  cairo_user_to_device(mContext, &x, &y);
    738 
    739  nsPrintfCString attributes("name='%s' x=%f y=%f internal", dest.get(), x, y);
    740  cairo_tag_begin(mContext, CAIRO_TAG_DEST, attributes.get());
    741  cairo_tag_end(mContext, CAIRO_TAG_DEST);
    742 }
    743 
    744 already_AddRefed<SourceSurface> DrawTargetCairo::Snapshot() {
    745  if (!IsValid()) {
    746    gfxCriticalNote << "DrawTargetCairo::Snapshot with bad surface "
    747                    << hexa(mSurface) << ", context " << hexa(mContext)
    748                    << ", status "
    749                    << (mSurface ? cairo_surface_status(mSurface) : -1);
    750    return nullptr;
    751  }
    752 
    753  if (mSnapshot) {
    754    RefPtr<SourceSurface> snapshot(mSnapshot);
    755    return snapshot.forget();
    756  }
    757 
    758  IntSize size = GetSize();
    759 
    760  mSnapshot = new SourceSurfaceCairo(mSurface, size,
    761                                     GfxFormatForCairoSurface(mSurface), this);
    762  RefPtr<SourceSurface> snapshot(mSnapshot);
    763  return snapshot.forget();
    764 }
    765 
    766 bool DrawTargetCairo::LockBits(uint8_t** aData, IntSize* aSize,
    767                               int32_t* aStride, SurfaceFormat* aFormat,
    768                               IntPoint* aOrigin) {
    769  if (!IsValid()) {
    770    gfxCriticalNote << "LockBits with bad surface "
    771                    << cairo_surface_status(cairo_get_group_target(mContext));
    772    return false;
    773  }
    774 
    775  cairo_surface_t* target = cairo_get_group_target(mContext);
    776  cairo_surface_t* surf = target;
    777 #ifdef CAIRO_HAS_WIN32_SURFACE
    778  if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_WIN32) {
    779    cairo_surface_t* imgsurf = cairo_win32_surface_get_image(surf);
    780    if (imgsurf) {
    781      surf = imgsurf;
    782    }
    783  }
    784 #endif
    785  if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_IMAGE &&
    786      cairo_surface_status(surf) == CAIRO_STATUS_SUCCESS) {
    787    PointDouble offset;
    788    cairo_surface_get_device_offset(target, &offset.x.value, &offset.y.value);
    789    // verify the device offset can be converted to integers suitable for a
    790    // bounds rect
    791    IntPoint origin(int32_t(-offset.x), int32_t(-offset.y));
    792    if (-PointDouble(origin) != offset || (!aOrigin && origin != IntPoint())) {
    793      return false;
    794    }
    795 
    796    WillChange();
    797    Flush();
    798 
    799    mLockedBits = cairo_image_surface_get_data(surf);
    800    *aData = mLockedBits;
    801    *aSize = IntSize(cairo_image_surface_get_width(surf),
    802                     cairo_image_surface_get_height(surf));
    803    *aStride = cairo_image_surface_get_stride(surf);
    804    *aFormat = CairoFormatToGfxFormat(cairo_image_surface_get_format(surf));
    805    if (aOrigin) {
    806      *aOrigin = origin;
    807    }
    808    return true;
    809  }
    810 
    811  return false;
    812 }
    813 
    814 void DrawTargetCairo::ReleaseBits(uint8_t* aData) {
    815  MOZ_ASSERT(mLockedBits == aData);
    816  mLockedBits = nullptr;
    817  cairo_surface_t* surf = cairo_get_group_target(mContext);
    818 #ifdef CAIRO_HAS_WIN32_SURFACE
    819  if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_WIN32) {
    820    cairo_surface_t* imgsurf = cairo_win32_surface_get_image(surf);
    821    if (imgsurf) {
    822      cairo_surface_mark_dirty(imgsurf);
    823    }
    824  }
    825 #endif
    826  cairo_surface_mark_dirty(surf);
    827 }
    828 
    829 void DrawTargetCairo::Flush() {
    830  if (!IsValid()) {
    831    gfxCriticalNote << "Flush with bad surface "
    832                    << cairo_surface_status(cairo_get_group_target(mContext));
    833    return;
    834  }
    835 
    836  cairo_surface_t* surf = cairo_get_group_target(mContext);
    837  cairo_surface_flush(surf);
    838 }
    839 
    840 void DrawTargetCairo::PrepareForDrawing(cairo_t* aContext,
    841                                        const Path* aPath /* = nullptr */) {
    842  WillChange(aPath);
    843 }
    844 
    845 cairo_surface_t* DrawTargetCairo::GetDummySurface() {
    846  if (mDummySurface) {
    847    return mDummySurface;
    848  }
    849 
    850  mDummySurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
    851 
    852  return mDummySurface;
    853 }
    854 
    855 static void PaintWithAlpha(cairo_t* aContext, const DrawOptions& aOptions) {
    856  if (aOptions.mCompositionOp == CompositionOp::OP_SOURCE) {
    857    // Cairo treats the source operator like a lerp when alpha is < 1.
    858    // Approximate the desired operator by: out = 0; out += src*alpha;
    859    if (aOptions.mAlpha == 1) {
    860      cairo_set_operator(aContext, CAIRO_OPERATOR_SOURCE);
    861      cairo_paint(aContext);
    862    } else {
    863      cairo_set_operator(aContext, CAIRO_OPERATOR_CLEAR);
    864      cairo_paint(aContext);
    865      cairo_set_operator(aContext, CAIRO_OPERATOR_ADD);
    866      cairo_paint_with_alpha(aContext, aOptions.mAlpha);
    867    }
    868  } else {
    869    cairo_set_operator(aContext, GfxOpToCairoOp(aOptions.mCompositionOp));
    870    cairo_paint_with_alpha(aContext, aOptions.mAlpha);
    871  }
    872 }
    873 
    874 void DrawTargetCairo::DrawSurface(SourceSurface* aSurface, const Rect& aDest,
    875                                  const Rect& aSource,
    876                                  const DrawSurfaceOptions& aSurfOptions,
    877                                  const DrawOptions& aOptions) {
    878  if (mTransformSingular || aDest.IsEmpty()) {
    879    return;
    880  }
    881 
    882  if (!IsValid() || !aSurface) {
    883    gfxCriticalNote << "DrawSurface with bad surface "
    884                    << cairo_surface_status(cairo_get_group_target(mContext));
    885    return;
    886  }
    887 
    888  AutoPrepareForDrawing prep(this, mContext);
    889  AutoClearDeviceOffset clear(aSurface);
    890 
    891  float sx = aSource.Width() / aDest.Width();
    892  float sy = aSource.Height() / aDest.Height();
    893 
    894  cairo_matrix_t src_mat;
    895  cairo_matrix_init_translate(&src_mat, aSource.X() - aSurface->GetRect().x,
    896                              aSource.Y() - aSurface->GetRect().y);
    897  cairo_matrix_scale(&src_mat, sx, sy);
    898 
    899  cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface);
    900  if (!surf) {
    901    gfxWarning()
    902        << "Failed to create cairo surface for DrawTargetCairo::DrawSurface";
    903    return;
    904  }
    905  cairo_pattern_t* pat = cairo_pattern_create_for_surface(surf);
    906  cairo_surface_destroy(surf);
    907 
    908  cairo_pattern_set_matrix(pat, &src_mat);
    909  cairo_pattern_set_filter(
    910      pat, GfxSamplingFilterToCairoFilter(aSurfOptions.mSamplingFilter));
    911  // For PDF output, we avoid using EXTEND_PAD here because floating-point
    912  // error accumulation may lead cairo_pdf_surface to conclude that padding
    913  // is needed due to an apparent one- or two-pixel mismatch between source
    914  // pattern and destination rect sizes when we're rendering a pdf.js page,
    915  // and this forces undesirable fallback to the rasterization codepath
    916  // instead of simply replaying the recording.
    917  // (See bug 1777209.)
    918  cairo_pattern_set_extend(
    919      pat, cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_PDF
    920               ? CAIRO_EXTEND_NONE
    921               : CAIRO_EXTEND_PAD);
    922 
    923  cairo_set_antialias(mContext,
    924                      GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
    925 
    926  // If the destination rect covers the entire clipped area, then unbounded and
    927  // bounded operations are identical, and we don't need to push a group.
    928  bool needsGroup = !IsOperatorBoundByMask(aOptions.mCompositionOp) &&
    929                    !aDest.Contains(GetUserSpaceClip());
    930 
    931  cairo_translate(mContext, aDest.X(), aDest.Y());
    932 
    933  if (needsGroup) {
    934    cairo_push_group(mContext);
    935    cairo_new_path(mContext);
    936    cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height());
    937    cairo_set_source(mContext, pat);
    938    cairo_fill(mContext);
    939    cairo_pop_group_to_source(mContext);
    940  } else {
    941    cairo_new_path(mContext);
    942    cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height());
    943    cairo_clip(mContext);
    944    cairo_set_source(mContext, pat);
    945  }
    946 
    947  PaintWithAlpha(mContext, aOptions);
    948 
    949  cairo_pattern_destroy(pat);
    950 }
    951 
    952 void DrawTargetCairo::DrawFilter(FilterNode* aNode, const Rect& aSourceRect,
    953                                 const Point& aDestPoint,
    954                                 const DrawOptions& aOptions) {
    955  if (!IsValid() || !aNode) {
    956    gfxCriticalNote << "DrawFilter with bad surface "
    957                    << cairo_surface_status(cairo_get_group_target(mContext));
    958    return;
    959  }
    960  FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
    961  filter->Draw(this, aSourceRect, aDestPoint, aOptions);
    962 }
    963 
    964 void DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface* aSurface,
    965                                            const Point& aDest,
    966                                            const ShadowOptions& aShadow,
    967                                            CompositionOp aOperator) {
    968  if (!IsValid() || !aSurface) {
    969    gfxCriticalNote << "DrawSurfaceWithShadow with bad surface "
    970                    << cairo_surface_status(cairo_get_group_target(mContext));
    971    return;
    972  }
    973 
    974  if (aSurface->GetType() != SurfaceType::CAIRO) {
    975    return;
    976  }
    977 
    978  AutoClearDeviceOffset clear(aSurface);
    979 
    980  IntSize size = aSurface->GetSize();
    981  Float width = Float(size.width);
    982  Float height = Float(size.height);
    983 
    984  SourceSurfaceCairo* source = static_cast<SourceSurfaceCairo*>(aSurface);
    985  cairo_surface_t* sourcesurf = source->GetSurface();
    986  cairo_surface_t* blursurf;
    987  cairo_surface_t* surf;
    988 
    989  // We only use the A8 surface for blurred shadows. Unblurred shadows can just
    990  // use the RGBA surface directly.
    991  if (cairo_surface_get_type(sourcesurf) == CAIRO_SURFACE_TYPE_TEE) {
    992    blursurf = cairo_tee_surface_index(sourcesurf, 0);
    993    surf = cairo_tee_surface_index(sourcesurf, 1);
    994  } else {
    995    blursurf = sourcesurf;
    996    surf = sourcesurf;
    997  }
    998 
    999  if (aShadow.mSigma != 0.0f) {
   1000    MOZ_ASSERT(cairo_surface_get_type(blursurf) == CAIRO_SURFACE_TYPE_IMAGE);
   1001    GaussianBlur blur(Point(aShadow.mSigma, aShadow.mSigma));
   1002    blur.Blur(cairo_image_surface_get_data(blursurf),
   1003              cairo_image_surface_get_stride(blursurf), size,
   1004              aSurface->GetFormat());
   1005  }
   1006 
   1007  WillChange();
   1008  ClearSurfaceForUnboundedSource(aOperator);
   1009 
   1010  cairo_save(mContext);
   1011  cairo_set_operator(mContext, GfxOpToCairoOp(aOperator));
   1012  cairo_identity_matrix(mContext);
   1013  cairo_translate(mContext, aDest.x, aDest.y);
   1014 
   1015  bool needsGroup = !IsOperatorBoundByMask(aOperator);
   1016  if (needsGroup) {
   1017    cairo_push_group(mContext);
   1018  }
   1019 
   1020  cairo_set_source_rgba(mContext, aShadow.mColor.r, aShadow.mColor.g,
   1021                        aShadow.mColor.b, aShadow.mColor.a);
   1022  cairo_mask_surface(mContext, blursurf, aShadow.mOffset.x, aShadow.mOffset.y);
   1023 
   1024  if (blursurf != surf || aSurface->GetFormat() != SurfaceFormat::A8) {
   1025    // Now that the shadow has been drawn, we can draw the surface on top.
   1026    cairo_set_source_surface(mContext, surf, 0, 0);
   1027    cairo_new_path(mContext);
   1028    cairo_rectangle(mContext, 0, 0, width, height);
   1029    cairo_fill(mContext);
   1030  }
   1031 
   1032  if (needsGroup) {
   1033    cairo_pop_group_to_source(mContext);
   1034    cairo_paint(mContext);
   1035  }
   1036 
   1037  cairo_restore(mContext);
   1038 }
   1039 
   1040 void DrawTargetCairo::DrawPattern(const Pattern& aPattern,
   1041                                  const StrokeOptions& aStrokeOptions,
   1042                                  const DrawOptions& aOptions,
   1043                                  DrawPatternType aDrawType,
   1044                                  bool aPathBoundsClip) {
   1045  if (!IsValid()) {
   1046    gfxCriticalNote << "DrawPattern with bad surface "
   1047                    << cairo_surface_status(cairo_get_group_target(mContext));
   1048    return;
   1049  }
   1050 
   1051  if (!PatternIsCompatible(aPattern)) {
   1052    return;
   1053  }
   1054 
   1055  AutoClearDeviceOffset clear(aPattern);
   1056 
   1057  cairo_pattern_t* pat =
   1058      GfxPatternToCairoPattern(aPattern, aOptions.mAlpha, GetTransform());
   1059  if (!pat) {
   1060    return;
   1061  }
   1062  if (cairo_pattern_status(pat)) {
   1063    cairo_pattern_destroy(pat);
   1064    gfxWarning() << "Invalid pattern";
   1065    return;
   1066  }
   1067 
   1068  cairo_set_source(mContext, pat);
   1069 
   1070  cairo_set_antialias(mContext,
   1071                      GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
   1072 
   1073  if (NeedIntermediateSurface(aPattern, aOptions) ||
   1074      (!IsOperatorBoundByMask(aOptions.mCompositionOp) && !aPathBoundsClip)) {
   1075    cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA);
   1076 
   1077    // Don't want operators to be applied twice
   1078    cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
   1079 
   1080    if (aDrawType == DRAW_STROKE) {
   1081      SetCairoStrokeOptions(mContext, aStrokeOptions);
   1082      cairo_stroke_preserve(mContext);
   1083    } else {
   1084      cairo_fill_preserve(mContext);
   1085    }
   1086 
   1087    cairo_pop_group_to_source(mContext);
   1088 
   1089    // Now draw the content using the desired operator
   1090    PaintWithAlpha(mContext, aOptions);
   1091  } else {
   1092    cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
   1093 
   1094    if (aDrawType == DRAW_STROKE) {
   1095      SetCairoStrokeOptions(mContext, aStrokeOptions);
   1096      cairo_stroke_preserve(mContext);
   1097    } else {
   1098      cairo_fill_preserve(mContext);
   1099    }
   1100  }
   1101 
   1102  cairo_pattern_destroy(pat);
   1103 }
   1104 
   1105 void DrawTargetCairo::FillRect(const Rect& aRect, const Pattern& aPattern,
   1106                               const DrawOptions& aOptions) {
   1107  if (mTransformSingular) {
   1108    return;
   1109  }
   1110 
   1111  if (!IsValid()) {
   1112    gfxCriticalNote << "FillRect with bad surface "
   1113                    << cairo_surface_status(cairo_get_group_target(mContext));
   1114    return;
   1115  }
   1116 
   1117  AutoPrepareForDrawing prep(this, mContext);
   1118 
   1119  bool restoreTransform = false;
   1120  Matrix mat;
   1121  Rect r = aRect;
   1122 
   1123  /* Clamp coordinates to work around a design bug in cairo */
   1124  if (r.Width() > CAIRO_COORD_MAX || r.Height() > CAIRO_COORD_MAX ||
   1125      r.X() < -CAIRO_COORD_MAX || r.X() > CAIRO_COORD_MAX ||
   1126      r.Y() < -CAIRO_COORD_MAX || r.Y() > CAIRO_COORD_MAX) {
   1127    if (!mat.IsRectilinear()) {
   1128      gfxWarning() << "DrawTargetCairo::FillRect() misdrawing huge Rect "
   1129                      "with non-rectilinear transform";
   1130    }
   1131 
   1132    mat = GetTransform();
   1133    r = mat.TransformBounds(r);
   1134 
   1135    if (!ConditionRect(r)) {
   1136      gfxWarning() << "Ignoring DrawTargetCairo::FillRect() call with "
   1137                      "out-of-bounds Rect";
   1138      return;
   1139    }
   1140 
   1141    restoreTransform = true;
   1142    SetTransform(Matrix());
   1143  }
   1144 
   1145  cairo_new_path(mContext);
   1146  cairo_rectangle(mContext, r.X(), r.Y(), r.Width(), r.Height());
   1147 
   1148  bool pathBoundsClip = false;
   1149 
   1150  if (r.Contains(GetUserSpaceClip())) {
   1151    pathBoundsClip = true;
   1152  }
   1153 
   1154  DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL, pathBoundsClip);
   1155 
   1156  if (restoreTransform) {
   1157    SetTransform(mat);
   1158  }
   1159 }
   1160 
   1161 void DrawTargetCairo::CopySurfaceInternal(cairo_surface_t* aSurface,
   1162                                          const IntRect& aSource,
   1163                                          const IntPoint& aDest) {
   1164  if (!IsValid()) {
   1165    gfxCriticalNote << "CopySurfaceInternal with bad surface "
   1166                    << cairo_surface_status(cairo_get_group_target(mContext));
   1167    return;
   1168  }
   1169 
   1170  if (cairo_surface_status(aSurface)) {
   1171    gfxWarning() << "Invalid surface" << cairo_surface_status(aSurface);
   1172    return;
   1173  }
   1174 
   1175  cairo_identity_matrix(mContext);
   1176 
   1177  cairo_set_source_surface(mContext, aSurface, aDest.x - aSource.X(),
   1178                           aDest.y - aSource.Y());
   1179  cairo_set_operator(mContext, CAIRO_OPERATOR_SOURCE);
   1180  cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE);
   1181 
   1182  cairo_reset_clip(mContext);
   1183  cairo_new_path(mContext);
   1184  cairo_rectangle(mContext, aDest.x, aDest.y, aSource.Width(),
   1185                  aSource.Height());
   1186  cairo_fill(mContext);
   1187 }
   1188 
   1189 void DrawTargetCairo::CopySurface(SourceSurface* aSurface,
   1190                                  const IntRect& aSource,
   1191                                  const IntPoint& aDest) {
   1192  if (mTransformSingular) {
   1193    return;
   1194  }
   1195 
   1196  if (!IsValid()) {
   1197    gfxCriticalNote << "CopySurface with bad surface "
   1198                    << cairo_surface_status(cairo_get_group_target(mContext));
   1199    return;
   1200  }
   1201 
   1202  AutoPrepareForDrawing prep(this, mContext);
   1203  AutoClearDeviceOffset clear(aSurface);
   1204 
   1205  if (!aSurface) {
   1206    gfxWarning() << "Unsupported surface type specified";
   1207    return;
   1208  }
   1209 
   1210  cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface);
   1211  if (!surf) {
   1212    gfxWarning() << "Unsupported surface type specified";
   1213    return;
   1214  }
   1215 
   1216  CopySurfaceInternal(surf, aSource - aSurface->GetRect().TopLeft(), aDest);
   1217  cairo_surface_destroy(surf);
   1218 }
   1219 
   1220 void DrawTargetCairo::CopyRect(const IntRect& aSource, const IntPoint& aDest) {
   1221  if (mTransformSingular) {
   1222    return;
   1223  }
   1224 
   1225  if (!IsValid()) {
   1226    gfxCriticalNote << "CopyRect with bad surface "
   1227                    << cairo_surface_status(cairo_get_group_target(mContext));
   1228    return;
   1229  }
   1230 
   1231  AutoPrepareForDrawing prep(this, mContext);
   1232 
   1233  IntRect source = aSource;
   1234  cairo_surface_t* surf = mSurface;
   1235 
   1236  if (!SupportsSelfCopy(mSurface) && aSource.ContainsY(aDest.y)) {
   1237    cairo_surface_t* similar = cairo_surface_create_similar(
   1238        mSurface, GfxFormatToCairoContent(GetFormat()), aSource.Width(),
   1239        aSource.Height());
   1240    cairo_t* ctx = cairo_create(similar);
   1241    cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
   1242    cairo_set_source_surface(ctx, surf, -aSource.X(), -aSource.Y());
   1243    cairo_paint(ctx);
   1244    cairo_destroy(ctx);
   1245 
   1246    source.MoveTo(0, 0);
   1247    surf = similar;
   1248  }
   1249 
   1250  CopySurfaceInternal(surf, source, aDest);
   1251 
   1252  if (surf != mSurface) {
   1253    cairo_surface_destroy(surf);
   1254  }
   1255 }
   1256 
   1257 void DrawTargetCairo::ClearRect(const Rect& aRect) {
   1258  if (mTransformSingular) {
   1259    return;
   1260  }
   1261 
   1262  if (!IsValid()) {
   1263    gfxCriticalNote << "ClearRect with bad surface "
   1264                    << cairo_surface_status(cairo_get_group_target(mContext));
   1265    return;
   1266  }
   1267 
   1268  AutoPrepareForDrawing prep(this, mContext);
   1269 
   1270  if (!mContext || aRect.Width() < 0 || aRect.Height() < 0 ||
   1271      !std::isfinite(aRect.X()) || !std::isfinite(aRect.Width()) ||
   1272      !std::isfinite(aRect.Y()) || !std::isfinite(aRect.Height())) {
   1273    gfxCriticalNote << "ClearRect with invalid argument " << gfx::hexa(mContext)
   1274                    << " with " << aRect.Width() << "x" << aRect.Height()
   1275                    << " [" << aRect.X() << ", " << aRect.Y() << "]";
   1276  }
   1277 
   1278  cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE);
   1279  cairo_new_path(mContext);
   1280  cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR);
   1281  cairo_rectangle(mContext, aRect.X(), aRect.Y(), aRect.Width(),
   1282                  aRect.Height());
   1283  cairo_fill(mContext);
   1284 }
   1285 
   1286 void DrawTargetCairo::StrokeRect(
   1287    const Rect& aRect, const Pattern& aPattern,
   1288    const StrokeOptions& aStrokeOptions /* = StrokeOptions() */,
   1289    const DrawOptions& aOptions /* = DrawOptions() */) {
   1290  if (mTransformSingular) {
   1291    return;
   1292  }
   1293 
   1294  if (!IsValid()) {
   1295    gfxCriticalNote << "StrokeRect with bad surface "
   1296                    << cairo_surface_status(cairo_get_group_target(mContext));
   1297    return;
   1298  }
   1299 
   1300  AutoPrepareForDrawing prep(this, mContext);
   1301 
   1302  cairo_new_path(mContext);
   1303  cairo_rectangle(mContext, aRect.X(), aRect.Y(), aRect.Width(),
   1304                  aRect.Height());
   1305 
   1306  DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
   1307 }
   1308 
   1309 void DrawTargetCairo::StrokeLine(
   1310    const Point& aStart, const Point& aEnd, const Pattern& aPattern,
   1311    const StrokeOptions& aStrokeOptions /* = StrokeOptions() */,
   1312    const DrawOptions& aOptions /* = DrawOptions() */) {
   1313  if (mTransformSingular) {
   1314    return;
   1315  }
   1316 
   1317  if (!IsValid()) {
   1318    gfxCriticalNote << "StrokeLine with bad surface "
   1319                    << cairo_surface_status(cairo_get_group_target(mContext));
   1320    return;
   1321  }
   1322 
   1323  AutoPrepareForDrawing prep(this, mContext);
   1324 
   1325  cairo_new_path(mContext);
   1326  cairo_move_to(mContext, aStart.x, aStart.y);
   1327  cairo_line_to(mContext, aEnd.x, aEnd.y);
   1328 
   1329  DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
   1330 }
   1331 
   1332 void DrawTargetCairo::Stroke(
   1333    const Path* aPath, const Pattern& aPattern,
   1334    const StrokeOptions& aStrokeOptions /* = StrokeOptions() */,
   1335    const DrawOptions& aOptions /* = DrawOptions() */) {
   1336  if (mTransformSingular) {
   1337    return;
   1338  }
   1339 
   1340  if (!IsValid()) {
   1341    gfxCriticalNote << "Stroke with bad surface "
   1342                    << cairo_surface_status(cairo_get_group_target(mContext));
   1343    return;
   1344  }
   1345 
   1346  AutoPrepareForDrawing prep(this, mContext, aPath);
   1347 
   1348  if (aPath->GetBackendType() != BackendType::CAIRO) return;
   1349 
   1350  PathCairo* path =
   1351      const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
   1352  path->SetPathOnContext(mContext);
   1353 
   1354  DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
   1355 }
   1356 
   1357 void DrawTargetCairo::Fill(const Path* aPath, const Pattern& aPattern,
   1358                           const DrawOptions& aOptions /* = DrawOptions() */) {
   1359  if (mTransformSingular) {
   1360    return;
   1361  }
   1362 
   1363  if (!IsValid()) {
   1364    gfxCriticalNote << "Fill with bad surface "
   1365                    << cairo_surface_status(cairo_get_group_target(mContext));
   1366    return;
   1367  }
   1368 
   1369  AutoPrepareForDrawing prep(this, mContext, aPath);
   1370 
   1371  if (aPath->GetBackendType() != BackendType::CAIRO) return;
   1372 
   1373  PathCairo* path =
   1374      const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
   1375  path->SetPathOnContext(mContext);
   1376 
   1377  DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL);
   1378 }
   1379 
   1380 bool DrawTargetCairo::IsCurrentGroupOpaque() {
   1381  cairo_surface_t* surf = cairo_get_group_target(mContext);
   1382 
   1383  if (!surf) {
   1384    return false;
   1385  }
   1386 
   1387  return cairo_surface_get_content(surf) == CAIRO_CONTENT_COLOR;
   1388 }
   1389 
   1390 void DrawTargetCairo::SetFontOptions(cairo_antialias_t aAAMode) {
   1391  //   This will attempt to detect if the currently set scaled font on the
   1392  // context has enabled subpixel AA. If it is not permitted, then it will
   1393  // downgrade to grayscale AA.
   1394  //   This only currently works effectively for the cairo-ft backend relative
   1395  // to system defaults, as only cairo-ft reflect system defaults in the scaled
   1396  // font state. However, this will work for cairo-ft on both tree Cairo and
   1397  // system Cairo.
   1398  //   Other backends leave the CAIRO_ANTIALIAS_DEFAULT setting untouched while
   1399  // potentially interpreting it as subpixel or even other types of AA that
   1400  // can't be safely equivocated with grayscale AA. For this reason we don't
   1401  // try to also detect and modify the default AA setting, only explicit
   1402  // subpixel AA. These other backends must instead rely on tree Cairo's
   1403  // cairo_surface_set_subpixel_antialiasing extension.
   1404 
   1405  // If allowing subpixel AA, then leave Cairo's default AA state.
   1406  if (mPermitSubpixelAA && aAAMode == CAIRO_ANTIALIAS_DEFAULT) {
   1407    return;
   1408  }
   1409 
   1410  if (!IsValid()) {
   1411    gfxCriticalNote << "SetFontOptions with bad surface "
   1412                    << cairo_surface_status(cairo_get_group_target(mContext));
   1413    return;
   1414  }
   1415 
   1416  if (!mFontOptions) {
   1417    mFontOptions = cairo_font_options_create();
   1418    if (!mFontOptions) {
   1419      gfxWarning() << "Failed allocating Cairo font options";
   1420      return;
   1421    }
   1422  }
   1423 
   1424  cairo_get_font_options(mContext, mFontOptions);
   1425  cairo_antialias_t oldAA = cairo_font_options_get_antialias(mFontOptions);
   1426  cairo_antialias_t newAA =
   1427      aAAMode == CAIRO_ANTIALIAS_DEFAULT ? oldAA : aAAMode;
   1428  // Nothing to change if switching to default AA.
   1429  if (newAA == CAIRO_ANTIALIAS_DEFAULT) {
   1430    return;
   1431  }
   1432  // If the current font requests subpixel AA, force it to gray since we don't
   1433  // allow subpixel AA.
   1434  if (!mPermitSubpixelAA && newAA == CAIRO_ANTIALIAS_SUBPIXEL) {
   1435    newAA = CAIRO_ANTIALIAS_GRAY;
   1436  }
   1437  // Only override old AA with lower levels of AA.
   1438  if (oldAA == CAIRO_ANTIALIAS_DEFAULT || (int)newAA < (int)oldAA) {
   1439    cairo_font_options_set_antialias(mFontOptions, newAA);
   1440    cairo_set_font_options(mContext, mFontOptions);
   1441  }
   1442 }
   1443 
   1444 void DrawTargetCairo::SetPermitSubpixelAA(bool aPermitSubpixelAA) {
   1445  DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA);
   1446  cairo_surface_set_subpixel_antialiasing(
   1447      cairo_get_group_target(mContext),
   1448      aPermitSubpixelAA ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED
   1449                        : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
   1450 }
   1451 
   1452 static bool SupportsVariationSettings(cairo_surface_t* surface) {
   1453  switch (cairo_surface_get_type(surface)) {
   1454    case CAIRO_SURFACE_TYPE_PDF:
   1455    case CAIRO_SURFACE_TYPE_PS:
   1456      return false;
   1457    default:
   1458      return true;
   1459  }
   1460 }
   1461 
   1462 void DrawTargetCairo::FillGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
   1463                                 const Pattern& aPattern,
   1464                                 const DrawOptions& aOptions) {
   1465  if (mTransformSingular) {
   1466    return;
   1467  }
   1468 
   1469  if (!IsValid()) {
   1470    gfxDebug() << "FillGlyphs bad surface "
   1471               << cairo_surface_status(cairo_get_group_target(mContext));
   1472    return;
   1473  }
   1474 
   1475  cairo_scaled_font_t* cairoScaledFont =
   1476      aFont ? aFont->GetCairoScaledFont() : nullptr;
   1477  if (!cairoScaledFont) {
   1478    gfxDevCrash(LogReason::InvalidFont) << "Invalid scaled font";
   1479    return;
   1480  }
   1481 
   1482  AutoPrepareForDrawing prep(this, mContext);
   1483  AutoClearDeviceOffset clear(aPattern);
   1484 
   1485  cairo_set_scaled_font(mContext, cairoScaledFont);
   1486 
   1487  cairo_pattern_t* pat =
   1488      GfxPatternToCairoPattern(aPattern, aOptions.mAlpha, GetTransform());
   1489  if (!pat) return;
   1490 
   1491  cairo_set_source(mContext, pat);
   1492  cairo_pattern_destroy(pat);
   1493 
   1494  cairo_antialias_t aa = GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode);
   1495  cairo_set_antialias(mContext, aa);
   1496 
   1497  // Override any font-specific options as necessary.
   1498  SetFontOptions(aa);
   1499 
   1500  // Convert our GlyphBuffer into a vector of Cairo glyphs. This code can
   1501  // execute millions of times in short periods, so we want to avoid heap
   1502  // allocation whenever possible. So we use an inline vector capacity of 1024
   1503  // bytes (the maximum allowed by mozilla::Vector), which gives an inline
   1504  // length of 1024 / 24 = 42 elements, which is enough to typically avoid heap
   1505  // allocation in ~99% of cases.
   1506  Vector<cairo_glyph_t, 1024 / sizeof(cairo_glyph_t)> glyphs;
   1507  if (!glyphs.resizeUninitialized(aBuffer.mNumGlyphs)) {
   1508    gfxDevCrash(LogReason::GlyphAllocFailedCairo) << "glyphs allocation failed";
   1509    return;
   1510  }
   1511  for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
   1512    glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
   1513    glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
   1514    glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
   1515  }
   1516 
   1517  if (!SupportsVariationSettings(mSurface) && aFont->HasVariationSettings() &&
   1518      StaticPrefs::print_font_variations_as_paths()) {
   1519    cairo_set_fill_rule(mContext, CAIRO_FILL_RULE_WINDING);
   1520    cairo_new_path(mContext);
   1521    cairo_glyph_path(mContext, &glyphs[0], aBuffer.mNumGlyphs);
   1522    cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
   1523    cairo_fill(mContext);
   1524  } else {
   1525    cairo_show_glyphs(mContext, &glyphs[0], aBuffer.mNumGlyphs);
   1526  }
   1527 
   1528  if (cairo_surface_status(cairo_get_group_target(mContext))) {
   1529    gfxDebug() << "Ending FillGlyphs with a bad surface "
   1530               << cairo_surface_status(cairo_get_group_target(mContext));
   1531  }
   1532 }
   1533 
   1534 void DrawTargetCairo::Mask(const Pattern& aSource, const Pattern& aMask,
   1535                           const DrawOptions& aOptions /* = DrawOptions() */) {
   1536  if (mTransformSingular) {
   1537    return;
   1538  }
   1539 
   1540  if (!IsValid()) {
   1541    gfxCriticalNote << "Mask with bad surface "
   1542                    << cairo_surface_status(cairo_get_group_target(mContext));
   1543    return;
   1544  }
   1545 
   1546  AutoPrepareForDrawing prep(this, mContext);
   1547  AutoClearDeviceOffset clearSource(aSource);
   1548  AutoClearDeviceOffset clearMask(aMask);
   1549 
   1550  cairo_set_antialias(mContext,
   1551                      GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
   1552 
   1553  cairo_pattern_t* source =
   1554      GfxPatternToCairoPattern(aSource, aOptions.mAlpha, GetTransform());
   1555  if (!source) {
   1556    return;
   1557  }
   1558 
   1559  cairo_pattern_t* mask =
   1560      GfxPatternToCairoPattern(aMask, aOptions.mAlpha, GetTransform());
   1561  if (!mask) {
   1562    cairo_pattern_destroy(source);
   1563    return;
   1564  }
   1565 
   1566  if (cairo_pattern_status(source) || cairo_pattern_status(mask)) {
   1567    cairo_pattern_destroy(source);
   1568    cairo_pattern_destroy(mask);
   1569    gfxWarning() << "Invalid pattern";
   1570    return;
   1571  }
   1572 
   1573  cairo_set_source(mContext, source);
   1574  cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
   1575  cairo_mask(mContext, mask);
   1576 
   1577  cairo_pattern_destroy(mask);
   1578  cairo_pattern_destroy(source);
   1579 }
   1580 
   1581 void DrawTargetCairo::MaskSurface(const Pattern& aSource, SourceSurface* aMask,
   1582                                  Point aOffset, const DrawOptions& aOptions) {
   1583  if (mTransformSingular) {
   1584    return;
   1585  }
   1586 
   1587  if (!IsValid()) {
   1588    gfxCriticalNote << "MaskSurface with bad surface "
   1589                    << cairo_surface_status(cairo_get_group_target(mContext));
   1590    return;
   1591  }
   1592 
   1593  AutoPrepareForDrawing prep(this, mContext);
   1594  AutoClearDeviceOffset clearSource(aSource);
   1595  AutoClearDeviceOffset clearMask(aMask);
   1596 
   1597  if (!PatternIsCompatible(aSource)) {
   1598    return;
   1599  }
   1600 
   1601  cairo_set_antialias(mContext,
   1602                      GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
   1603 
   1604  cairo_pattern_t* pat =
   1605      GfxPatternToCairoPattern(aSource, aOptions.mAlpha, GetTransform());
   1606  if (!pat) {
   1607    return;
   1608  }
   1609 
   1610  if (cairo_pattern_status(pat)) {
   1611    cairo_pattern_destroy(pat);
   1612    gfxWarning() << "Invalid pattern";
   1613    return;
   1614  }
   1615 
   1616  cairo_set_source(mContext, pat);
   1617 
   1618  if (NeedIntermediateSurface(aSource, aOptions)) {
   1619    cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA);
   1620 
   1621    // Don't want operators to be applied twice
   1622    cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
   1623 
   1624    // Now draw the content using the desired operator
   1625    cairo_paint_with_alpha(mContext, aOptions.mAlpha);
   1626 
   1627    cairo_pop_group_to_source(mContext);
   1628  }
   1629 
   1630  cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aMask);
   1631  if (!surf) {
   1632    cairo_pattern_destroy(pat);
   1633    return;
   1634  }
   1635  cairo_pattern_t* mask = cairo_pattern_create_for_surface(surf);
   1636  cairo_matrix_t matrix;
   1637 
   1638  cairo_matrix_init_translate(&matrix, -aOffset.x - aMask->GetRect().x,
   1639                              -aOffset.y - aMask->GetRect().y);
   1640  cairo_pattern_set_matrix(mask, &matrix);
   1641 
   1642  cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
   1643 
   1644  cairo_mask(mContext, mask);
   1645 
   1646  cairo_surface_destroy(surf);
   1647  cairo_pattern_destroy(mask);
   1648  cairo_pattern_destroy(pat);
   1649 }
   1650 
   1651 void DrawTargetCairo::PushClip(const Path* aPath) {
   1652  if (aPath->GetBackendType() != BackendType::CAIRO) {
   1653    return;
   1654  }
   1655 
   1656  if (!IsValid()) {
   1657    gfxCriticalNote << "PushClip with bad surface "
   1658                    << cairo_surface_status(cairo_get_group_target(mContext));
   1659    return;
   1660  }
   1661 
   1662  WillChange(aPath);
   1663  cairo_save(mContext);
   1664 
   1665  PathCairo* path =
   1666      const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
   1667 
   1668  if (mTransformSingular) {
   1669    cairo_new_path(mContext);
   1670    cairo_rectangle(mContext, 0, 0, 0, 0);
   1671  } else {
   1672    path->SetPathOnContext(mContext);
   1673  }
   1674  cairo_clip_preserve(mContext);
   1675 
   1676  ++mClipDepth;
   1677 }
   1678 
   1679 void DrawTargetCairo::PushClipRect(const Rect& aRect) {
   1680  if (!IsValid()) {
   1681    gfxCriticalNote << "PushClipRect with bad surface "
   1682                    << cairo_surface_status(cairo_get_group_target(mContext));
   1683    return;
   1684  }
   1685 
   1686  WillChange();
   1687  cairo_save(mContext);
   1688 
   1689  cairo_new_path(mContext);
   1690  if (mTransformSingular) {
   1691    cairo_rectangle(mContext, 0, 0, 0, 0);
   1692  } else {
   1693    cairo_rectangle(mContext, aRect.X(), aRect.Y(), aRect.Width(),
   1694                    aRect.Height());
   1695  }
   1696  cairo_clip_preserve(mContext);
   1697 
   1698  ++mClipDepth;
   1699 }
   1700 
   1701 void DrawTargetCairo::PopClip() {
   1702  if (NS_WARN_IF(mClipDepth <= 0)) {
   1703    return;
   1704  }
   1705 
   1706  if (!IsValid()) {
   1707    gfxCriticalNote << "PopClip with bad surface "
   1708                    << cairo_surface_status(cairo_get_group_target(mContext));
   1709    return;
   1710  }
   1711 
   1712  // save/restore does not affect the path, so no need to call WillChange()
   1713 
   1714  // cairo_restore will restore the transform too and we don't want to do that
   1715  // so we'll save it now and restore it after the cairo_restore
   1716  cairo_matrix_t mat;
   1717  cairo_get_matrix(mContext, &mat);
   1718 
   1719  cairo_restore(mContext);
   1720 
   1721  cairo_set_matrix(mContext, &mat);
   1722 
   1723  --mClipDepth;
   1724 }
   1725 
   1726 bool DrawTargetCairo::RemoveAllClips() {
   1727  while (mClipDepth > 0) {
   1728    PopClip();
   1729  }
   1730  return true;
   1731 }
   1732 
   1733 void DrawTargetCairo::PushLayer(bool aOpaque, Float aOpacity,
   1734                                SourceSurface* aMask,
   1735                                const Matrix& aMaskTransform,
   1736                                const IntRect& aBounds, bool aCopyBackground) {
   1737  PushLayerWithBlend(aOpaque, aOpacity, aMask, aMaskTransform, aBounds,
   1738                     aCopyBackground, CompositionOp::OP_OVER);
   1739 }
   1740 
   1741 void DrawTargetCairo::PushLayerWithBlend(bool aOpaque, Float aOpacity,
   1742                                         SourceSurface* aMask,
   1743                                         const Matrix& aMaskTransform,
   1744                                         const IntRect& aBounds,
   1745                                         bool aCopyBackground,
   1746                                         CompositionOp aCompositionOp) {
   1747  if (!IsValid()) {
   1748    gfxCriticalNote << "PushLayerWithBlend with bad surface "
   1749                    << cairo_surface_status(cairo_get_group_target(mContext));
   1750    return;
   1751  }
   1752 
   1753  cairo_content_t content = CAIRO_CONTENT_COLOR_ALPHA;
   1754 
   1755  if (mFormat == SurfaceFormat::A8) {
   1756    content = CAIRO_CONTENT_ALPHA;
   1757  } else if (aOpaque) {
   1758    content = CAIRO_CONTENT_COLOR;
   1759  }
   1760 
   1761  if (aCopyBackground) {
   1762    cairo_surface_t* source = cairo_get_group_target(mContext);
   1763    cairo_push_group_with_content(mContext, content);
   1764    cairo_surface_t* dest = cairo_get_group_target(mContext);
   1765    cairo_t* ctx = cairo_create(dest);
   1766    cairo_set_source_surface(ctx, source, 0, 0);
   1767    cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
   1768    cairo_paint(ctx);
   1769    cairo_destroy(ctx);
   1770  } else {
   1771    cairo_push_group_with_content(mContext, content);
   1772  }
   1773 
   1774  PushedLayer layer(aOpacity, aCompositionOp, mPermitSubpixelAA);
   1775 
   1776  if (aMask) {
   1777    cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aMask);
   1778    if (surf) {
   1779      layer.mMaskPattern = cairo_pattern_create_for_surface(surf);
   1780      Matrix maskTransform = aMaskTransform;
   1781      maskTransform.PreTranslate(aMask->GetRect().X(), aMask->GetRect().Y());
   1782      cairo_matrix_t mat;
   1783      GfxMatrixToCairoMatrix(maskTransform, mat);
   1784      cairo_matrix_invert(&mat);
   1785      cairo_pattern_set_matrix(layer.mMaskPattern, &mat);
   1786      cairo_surface_destroy(surf);
   1787    } else {
   1788      gfxCriticalError() << "Failed to get cairo surface for mask surface!";
   1789    }
   1790  }
   1791 
   1792  mPushedLayers.push_back(layer);
   1793 
   1794  SetPermitSubpixelAA(aOpaque);
   1795 }
   1796 
   1797 void DrawTargetCairo::PopLayer() {
   1798  if (!IsValid()) {
   1799    gfxCriticalNote << "PopLayer with bad surface "
   1800                    << cairo_surface_status(cairo_get_group_target(mContext));
   1801    return;
   1802  }
   1803 
   1804  MOZ_RELEASE_ASSERT(!mPushedLayers.empty());
   1805 
   1806  cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
   1807 
   1808  cairo_pop_group_to_source(mContext);
   1809 
   1810  PushedLayer layer = mPushedLayers.back();
   1811  mPushedLayers.pop_back();
   1812 
   1813  if (!layer.mMaskPattern) {
   1814    cairo_set_operator(mContext, GfxOpToCairoOp(layer.mCompositionOp));
   1815    cairo_paint_with_alpha(mContext, layer.mOpacity);
   1816  } else {
   1817    if (layer.mOpacity != Float(1.0)) {
   1818      cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA);
   1819 
   1820      // Now draw the content using the desired operator
   1821      cairo_paint_with_alpha(mContext, layer.mOpacity);
   1822 
   1823      cairo_pop_group_to_source(mContext);
   1824    }
   1825    cairo_set_operator(mContext, GfxOpToCairoOp(layer.mCompositionOp));
   1826    cairo_mask(mContext, layer.mMaskPattern);
   1827  }
   1828 
   1829  cairo_matrix_t mat;
   1830  GfxMatrixToCairoMatrix(mTransform, mat);
   1831  cairo_set_matrix(mContext, &mat);
   1832 
   1833  cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
   1834 
   1835  cairo_pattern_destroy(layer.mMaskPattern);
   1836  SetPermitSubpixelAA(layer.mWasPermittingSubpixelAA);
   1837 }
   1838 
   1839 void DrawTargetCairo::ClearSurfaceForUnboundedSource(
   1840    const CompositionOp& aOperator) {
   1841  if (aOperator != CompositionOp::OP_SOURCE) {
   1842    return;
   1843  }
   1844 
   1845  if (!IsValid()) {
   1846    gfxCriticalNote << "ClearSurfaceForUnboundedSource with bad surface "
   1847                    << cairo_surface_status(cairo_get_group_target(mContext));
   1848    return;
   1849  }
   1850 
   1851  cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR);
   1852  // It doesn't really matter what the source is here, since Paint
   1853  // isn't bounded by the source and the mask covers the entire clip
   1854  // region.
   1855  cairo_paint(mContext);
   1856 }
   1857 
   1858 already_AddRefed<GradientStops> DrawTargetCairo::CreateGradientStops(
   1859    GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) const {
   1860  return MakeAndAddRef<GradientStopsCairo>(aStops, aNumStops, aExtendMode);
   1861 }
   1862 
   1863 already_AddRefed<FilterNode> DrawTargetCairo::CreateFilter(FilterType aType) {
   1864  return FilterNodeSoftware::Create(aType);
   1865 }
   1866 
   1867 already_AddRefed<SourceSurface> DrawTargetCairo::CreateSourceSurfaceFromData(
   1868    unsigned char* aData, const IntSize& aSize, int32_t aStride,
   1869    SurfaceFormat aFormat) const {
   1870  if (!aData) {
   1871    gfxWarning() << "DrawTargetCairo::CreateSourceSurfaceFromData null aData";
   1872    return nullptr;
   1873  }
   1874 
   1875  cairo_surface_t* surf = CopyToImageSurface(
   1876      aData, aSize, IntRect(IntPoint(), aSize), aStride, aFormat);
   1877  if (!surf) {
   1878    return nullptr;
   1879  }
   1880 
   1881  RefPtr<SourceSurfaceCairo> source_surf =
   1882      new SourceSurfaceCairo(surf, aSize, aFormat);
   1883  cairo_surface_destroy(surf);
   1884 
   1885  return source_surf.forget();
   1886 }
   1887 
   1888 already_AddRefed<SourceSurface> DrawTargetCairo::OptimizeSourceSurface(
   1889    SourceSurface* aSurface) const {
   1890  RefPtr<SourceSurface> surface(aSurface);
   1891  return surface.forget();
   1892 }
   1893 
   1894 already_AddRefed<SourceSurface>
   1895 DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(
   1896    const NativeSurface& aSurface) const {
   1897  return nullptr;
   1898 }
   1899 
   1900 already_AddRefed<DrawTarget> DrawTargetCairo::CreateSimilarDrawTarget(
   1901    const IntSize& aSize, SurfaceFormat aFormat) const {
   1902  if (cairo_surface_status(cairo_get_group_target(mContext))) {
   1903    RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
   1904    if (target->Init(aSize, aFormat)) {
   1905      return target.forget();
   1906    }
   1907  }
   1908 
   1909  cairo_surface_t* similar;
   1910  switch (cairo_surface_get_type(mSurface)) {
   1911 #ifdef CAIRO_HAS_WIN32_SURFACE
   1912    case CAIRO_SURFACE_TYPE_WIN32:
   1913      similar = cairo_win32_surface_create_with_dib(
   1914          GfxFormatToCairoFormat(aFormat), aSize.width, aSize.height);
   1915      break;
   1916 #endif
   1917 #ifdef CAIRO_HAS_QUARTZ_SURFACE
   1918    case CAIRO_SURFACE_TYPE_QUARTZ:
   1919      if (StaticPrefs::gfx_cairo_quartz_cg_layer_enabled()) {
   1920        similar = cairo_quartz_surface_create_cg_layer(
   1921            mSurface, GfxFormatToCairoContent(aFormat), aSize.width,
   1922            aSize.height);
   1923        break;
   1924      }
   1925      [[fallthrough]];
   1926 #endif
   1927    default:
   1928      similar = cairo_surface_create_similar(mSurface,
   1929                                             GfxFormatToCairoContent(aFormat),
   1930                                             aSize.width, aSize.height);
   1931      break;
   1932  }
   1933 
   1934  if (!cairo_surface_status(similar)) {
   1935    RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
   1936    if (target->InitAlreadyReferenced(similar, aSize)) {
   1937      return target.forget();
   1938    }
   1939  }
   1940 
   1941  gfxCriticalError(
   1942      CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize)))
   1943      << "Failed to create similar cairo surface! Size: " << aSize
   1944      << " Status: " << cairo_surface_status(similar)
   1945      << cairo_surface_status(cairo_get_group_target(mContext)) << " format "
   1946      << (int)aFormat;
   1947  cairo_surface_destroy(similar);
   1948 
   1949  return nullptr;
   1950 }
   1951 
   1952 RefPtr<DrawTarget> DrawTargetCairo::CreateClippedDrawTarget(
   1953    const Rect& aBounds, SurfaceFormat aFormat) {
   1954  RefPtr<DrawTarget> result;
   1955  // Doing this save()/restore() dance is wasteful
   1956  cairo_save(mContext);
   1957 
   1958  if (!aBounds.IsEmpty()) {
   1959    cairo_new_path(mContext);
   1960    cairo_rectangle(mContext, aBounds.X(), aBounds.Y(), aBounds.Width(),
   1961                    aBounds.Height());
   1962    cairo_clip(mContext);
   1963  }
   1964  cairo_identity_matrix(mContext);
   1965  IntRect clipBounds = IntRect::RoundOut(GetUserSpaceClip());
   1966  if (!clipBounds.IsEmpty()) {
   1967    RefPtr<DrawTarget> dt = CreateSimilarDrawTarget(
   1968        IntSize(clipBounds.width, clipBounds.height), aFormat);
   1969    if (dt) {
   1970      result = gfx::Factory::CreateOffsetDrawTarget(
   1971          dt, IntPoint(clipBounds.x, clipBounds.y));
   1972      if (result) {
   1973        result->SetTransform(mTransform);
   1974      }
   1975    }
   1976  } else {
   1977    // Everything is clipped but we still want some kind of surface
   1978    result = CreateSimilarDrawTarget(IntSize(1, 1), aFormat);
   1979  }
   1980 
   1981  cairo_restore(mContext);
   1982  return result;
   1983 }
   1984 
   1985 bool DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface,
   1986                                            const IntSize& aSize,
   1987                                            SurfaceFormat* aFormat) {
   1988  if (cairo_surface_status(aSurface)) {
   1989    gfxCriticalNote << "Attempt to create DrawTarget for invalid surface. "
   1990                    << aSize
   1991                    << " Cairo Status: " << cairo_surface_status(aSurface);
   1992    cairo_surface_destroy(aSurface);
   1993    return false;
   1994  }
   1995 
   1996  mContext = cairo_create(aSurface);
   1997  mSurface = aSurface;
   1998  mSize = aSize;
   1999  mFormat = aFormat ? *aFormat : GfxFormatForCairoSurface(aSurface);
   2000 
   2001  // Cairo image surface have a bug where they will allocate a mask surface (for
   2002  // clipping) the size of the clip extents, and don't take the surface extents
   2003  // into account. Add a manual clip to the surface extents to prevent this.
   2004  cairo_new_path(mContext);
   2005  cairo_rectangle(mContext, 0, 0, mSize.width, mSize.height);
   2006  cairo_clip(mContext);
   2007 
   2008  if (mFormat == SurfaceFormat::A8R8G8B8_UINT32 ||
   2009      mFormat == SurfaceFormat::R8G8B8A8) {
   2010    SetPermitSubpixelAA(false);
   2011  } else {
   2012    SetPermitSubpixelAA(true);
   2013  }
   2014 
   2015  return true;
   2016 }
   2017 
   2018 already_AddRefed<DrawTarget> DrawTargetCairo::CreateShadowDrawTarget(
   2019    const IntSize& aSize, SurfaceFormat aFormat, float aSigma) const {
   2020  cairo_surface_t* similar = cairo_surface_create_similar(
   2021      cairo_get_target(mContext), GfxFormatToCairoContent(aFormat), aSize.width,
   2022      aSize.height);
   2023 
   2024  if (cairo_surface_status(similar)) {
   2025    return nullptr;
   2026  }
   2027 
   2028  // If we don't have a blur then we can use the RGBA mask and keep all the
   2029  // operations in graphics memory.
   2030  if (aSigma == 0.0f || aFormat == SurfaceFormat::A8) {
   2031    RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
   2032    if (target->InitAlreadyReferenced(similar, aSize)) {
   2033      return target.forget();
   2034    } else {
   2035      return nullptr;
   2036    }
   2037  }
   2038 
   2039  cairo_surface_t* blursurf =
   2040      cairo_image_surface_create(CAIRO_FORMAT_A8, aSize.width, aSize.height);
   2041 
   2042  if (cairo_surface_status(blursurf)) {
   2043    return nullptr;
   2044  }
   2045 
   2046  cairo_surface_t* tee = cairo_tee_surface_create(blursurf);
   2047  cairo_surface_destroy(blursurf);
   2048  if (cairo_surface_status(tee)) {
   2049    cairo_surface_destroy(similar);
   2050    return nullptr;
   2051  }
   2052 
   2053  cairo_tee_surface_add(tee, similar);
   2054  cairo_surface_destroy(similar);
   2055 
   2056  RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
   2057  if (target->InitAlreadyReferenced(tee, aSize)) {
   2058    return target.forget();
   2059  }
   2060  return nullptr;
   2061 }
   2062 
   2063 bool DrawTargetCairo::Draw3DTransformedSurface(SourceSurface* aSurface,
   2064                                               const Matrix4x4& aMatrix) {
   2065  return DrawTarget::Draw3DTransformedSurface(aSurface, aMatrix);
   2066 }
   2067 
   2068 bool DrawTargetCairo::Init(cairo_surface_t* aSurface, const IntSize& aSize,
   2069                           SurfaceFormat* aFormat) {
   2070  cairo_surface_reference(aSurface);
   2071  return InitAlreadyReferenced(aSurface, aSize, aFormat);
   2072 }
   2073 
   2074 bool DrawTargetCairo::Init(const IntSize& aSize, SurfaceFormat aFormat) {
   2075  cairo_surface_t* surf = cairo_image_surface_create(
   2076      GfxFormatToCairoFormat(aFormat), aSize.width, aSize.height);
   2077  return InitAlreadyReferenced(surf, aSize);
   2078 }
   2079 
   2080 bool DrawTargetCairo::Init(unsigned char* aData, const IntSize& aSize,
   2081                           int32_t aStride, SurfaceFormat aFormat) {
   2082  cairo_surface_t* surf = cairo_image_surface_create_for_data(
   2083      aData, GfxFormatToCairoFormat(aFormat), aSize.width, aSize.height,
   2084      aStride);
   2085  return InitAlreadyReferenced(surf, aSize);
   2086 }
   2087 
   2088 void* DrawTargetCairo::GetNativeSurface(NativeSurfaceType aType) {
   2089  if (aType == NativeSurfaceType::CAIRO_CONTEXT) {
   2090    return mContext;
   2091  }
   2092 
   2093  return nullptr;
   2094 }
   2095 
   2096 void DrawTargetCairo::MarkSnapshotIndependent() {
   2097  if (mSnapshot) {
   2098    if (mSnapshot->refCount() > 1) {
   2099      // We only need to worry about snapshots that someone else knows about
   2100      mSnapshot->DrawTargetWillChange();
   2101    }
   2102    mSnapshot = nullptr;
   2103  }
   2104 }
   2105 
   2106 void DrawTargetCairo::WillChange(const Path* aPath /* = nullptr */) {
   2107  MarkSnapshotIndependent();
   2108  MOZ_ASSERT(!mLockedBits);
   2109 }
   2110 
   2111 void DrawTargetCairo::SetTransform(const Matrix& aTransform) {
   2112  DrawTarget::SetTransform(aTransform);
   2113 
   2114  mTransformSingular = aTransform.IsSingular();
   2115  if (!mTransformSingular) {
   2116    cairo_matrix_t mat;
   2117    GfxMatrixToCairoMatrix(mTransform, mat);
   2118    cairo_set_matrix(mContext, &mat);
   2119  }
   2120 }
   2121 
   2122 Rect DrawTargetCairo::GetUserSpaceClip() const {
   2123  double clipX1, clipY1, clipX2, clipY2;
   2124  cairo_clip_extents(mContext, &clipX1, &clipY1, &clipX2, &clipY2);
   2125  return Rect(clipX1, clipY1, clipX2 - clipX1,
   2126              clipY2 - clipY1);  // Narrowing of doubles to floats
   2127 }
   2128 
   2129 }  // namespace gfx
   2130 }  // namespace mozilla