Coverage Report

Created: 2018-09-25 14:53

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