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