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