LCOV - code coverage report
Current view: top level - gfx/2d - DrawTargetCairo.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 8 1069 0.7 %
Date: 2017-07-14 16:53:18 Functions: 1 89 1.1 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13