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