gfx/2d/DrawTargetCairo.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "DrawTargetCairo.h"
michael@0 7
michael@0 8 #include "SourceSurfaceCairo.h"
michael@0 9 #include "PathCairo.h"
michael@0 10 #include "HelpersCairo.h"
michael@0 11 #include "ScaledFontBase.h"
michael@0 12 #include "BorrowedContext.h"
michael@0 13 #include "FilterNodeSoftware.h"
michael@0 14
michael@0 15 #include "cairo.h"
michael@0 16 #include "cairo-tee.h"
michael@0 17 #include <string.h>
michael@0 18
michael@0 19 #include "Blur.h"
michael@0 20 #include "Logging.h"
michael@0 21 #include "Tools.h"
michael@0 22
michael@0 23 #ifdef CAIRO_HAS_QUARTZ_SURFACE
michael@0 24 #include "cairo-quartz.h"
michael@0 25 #include <ApplicationServices/ApplicationServices.h>
michael@0 26 #endif
michael@0 27
michael@0 28 #ifdef CAIRO_HAS_XLIB_SURFACE
michael@0 29 #include "cairo-xlib.h"
michael@0 30 #endif
michael@0 31
michael@0 32 #ifdef CAIRO_HAS_WIN32_SURFACE
michael@0 33 #include "cairo-win32.h"
michael@0 34 #endif
michael@0 35
michael@0 36 #include <algorithm>
michael@0 37
michael@0 38 namespace mozilla {
michael@0 39 namespace gfx {
michael@0 40
michael@0 41 cairo_surface_t *DrawTargetCairo::mDummySurface;
michael@0 42
michael@0 43 namespace {
michael@0 44
michael@0 45 // An RAII class to prepare to draw a context and optional path. Saves and
michael@0 46 // restores the context on construction/destruction.
michael@0 47 class AutoPrepareForDrawing
michael@0 48 {
michael@0 49 public:
michael@0 50 AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx)
michael@0 51 : mCtx(ctx)
michael@0 52 {
michael@0 53 dt->PrepareForDrawing(ctx);
michael@0 54 cairo_save(mCtx);
michael@0 55 MOZ_ASSERT(cairo_status(mCtx) || dt->GetTransform() == GetTransform());
michael@0 56 }
michael@0 57
michael@0 58 AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx, const Path* path)
michael@0 59 : mCtx(ctx)
michael@0 60 {
michael@0 61 dt->PrepareForDrawing(ctx, path);
michael@0 62 cairo_save(mCtx);
michael@0 63 MOZ_ASSERT(cairo_status(mCtx) || dt->GetTransform() == GetTransform());
michael@0 64 }
michael@0 65
michael@0 66 ~AutoPrepareForDrawing() { cairo_restore(mCtx); }
michael@0 67
michael@0 68 private:
michael@0 69 #ifdef DEBUG
michael@0 70 Matrix GetTransform()
michael@0 71 {
michael@0 72 cairo_matrix_t mat;
michael@0 73 cairo_get_matrix(mCtx, &mat);
michael@0 74 return Matrix(mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0);
michael@0 75 }
michael@0 76 #endif
michael@0 77
michael@0 78 cairo_t* mCtx;
michael@0 79 };
michael@0 80
michael@0 81
michael@0 82 } // end anonymous namespace
michael@0 83
michael@0 84 static bool
michael@0 85 SupportsSelfCopy(cairo_surface_t* surface)
michael@0 86 {
michael@0 87 switch (cairo_surface_get_type(surface))
michael@0 88 {
michael@0 89 #ifdef CAIRO_HAS_QUARTZ_SURFACE
michael@0 90 case CAIRO_SURFACE_TYPE_QUARTZ:
michael@0 91 return true;
michael@0 92 #endif
michael@0 93 #ifdef CAIRO_HAS_WIN32_SURFACE
michael@0 94 case CAIRO_SURFACE_TYPE_WIN32:
michael@0 95 case CAIRO_SURFACE_TYPE_WIN32_PRINTING:
michael@0 96 return true;
michael@0 97 #endif
michael@0 98 default:
michael@0 99 return false;
michael@0 100 }
michael@0 101 }
michael@0 102
michael@0 103 static bool
michael@0 104 PatternIsCompatible(const Pattern& aPattern)
michael@0 105 {
michael@0 106 switch (aPattern.GetType())
michael@0 107 {
michael@0 108 case PatternType::LINEAR_GRADIENT:
michael@0 109 {
michael@0 110 const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern);
michael@0 111 return pattern.mStops->GetBackendType() == BackendType::CAIRO;
michael@0 112 }
michael@0 113 case PatternType::RADIAL_GRADIENT:
michael@0 114 {
michael@0 115 const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
michael@0 116 return pattern.mStops->GetBackendType() == BackendType::CAIRO;
michael@0 117 }
michael@0 118 default:
michael@0 119 return true;
michael@0 120 }
michael@0 121 }
michael@0 122
michael@0 123 static cairo_user_data_key_t surfaceDataKey;
michael@0 124
michael@0 125 void
michael@0 126 ReleaseData(void* aData)
michael@0 127 {
michael@0 128 static_cast<DataSourceSurface*>(aData)->Release();
michael@0 129 }
michael@0 130
michael@0 131 /**
michael@0 132 * Returns cairo surface for the given SourceSurface.
michael@0 133 * If possible, it will use the cairo_surface associated with aSurface,
michael@0 134 * otherwise, it will create a new cairo_surface.
michael@0 135 * In either case, the caller must call cairo_surface_destroy on the
michael@0 136 * result when it is done with it.
michael@0 137 */
michael@0 138 cairo_surface_t*
michael@0 139 GetCairoSurfaceForSourceSurface(SourceSurface *aSurface, bool aExistingOnly = false)
michael@0 140 {
michael@0 141 if (aSurface->GetType() == SurfaceType::CAIRO) {
michael@0 142 cairo_surface_t* surf = static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface();
michael@0 143 cairo_surface_reference(surf);
michael@0 144 return surf;
michael@0 145 }
michael@0 146
michael@0 147 if (aSurface->GetType() == SurfaceType::CAIRO_IMAGE) {
michael@0 148 cairo_surface_t* surf =
michael@0 149 static_cast<const DataSourceSurfaceCairo*>(aSurface)->GetSurface();
michael@0 150 cairo_surface_reference(surf);
michael@0 151 return surf;
michael@0 152 }
michael@0 153
michael@0 154 if (aExistingOnly) {
michael@0 155 return nullptr;
michael@0 156 }
michael@0 157
michael@0 158 RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
michael@0 159 if (!data) {
michael@0 160 return nullptr;
michael@0 161 }
michael@0 162
michael@0 163 cairo_surface_t* surf =
michael@0 164 cairo_image_surface_create_for_data(data->GetData(),
michael@0 165 GfxFormatToCairoFormat(data->GetFormat()),
michael@0 166 data->GetSize().width,
michael@0 167 data->GetSize().height,
michael@0 168 data->Stride());
michael@0 169
michael@0 170 // In certain scenarios, requesting larger than 8k image fails. Bug 803568
michael@0 171 // covers the details of how to run into it, but the full detailed
michael@0 172 // investigation hasn't been done to determine the underlying cause. We
michael@0 173 // will just handle the failure to allocate the surface to avoid a crash.
michael@0 174 if (cairo_surface_status(surf)) {
michael@0 175 return nullptr;
michael@0 176 }
michael@0 177
michael@0 178 cairo_surface_set_user_data(surf,
michael@0 179 &surfaceDataKey,
michael@0 180 data.forget().drop(),
michael@0 181 ReleaseData);
michael@0 182 return surf;
michael@0 183 }
michael@0 184
michael@0 185 // An RAII class to temporarily clear any device offset set
michael@0 186 // on a surface. Note that this does not take a reference to the
michael@0 187 // surface.
michael@0 188 class AutoClearDeviceOffset
michael@0 189 {
michael@0 190 public:
michael@0 191 AutoClearDeviceOffset(SourceSurface* aSurface)
michael@0 192 : mSurface(nullptr)
michael@0 193 {
michael@0 194 Init(aSurface);
michael@0 195 }
michael@0 196
michael@0 197 AutoClearDeviceOffset(const Pattern& aPattern)
michael@0 198 : mSurface(nullptr)
michael@0 199 {
michael@0 200 if (aPattern.GetType() == PatternType::SURFACE) {
michael@0 201 const SurfacePattern& pattern = static_cast<const SurfacePattern&>(aPattern);
michael@0 202 Init(pattern.mSurface);
michael@0 203 }
michael@0 204 }
michael@0 205
michael@0 206 ~AutoClearDeviceOffset()
michael@0 207 {
michael@0 208 if (mSurface) {
michael@0 209 cairo_surface_set_device_offset(mSurface, mX, mY);
michael@0 210 }
michael@0 211 }
michael@0 212
michael@0 213 private:
michael@0 214 void Init(SourceSurface* aSurface)
michael@0 215 {
michael@0 216 cairo_surface_t* surface = GetCairoSurfaceForSourceSurface(aSurface, true);
michael@0 217 if (surface) {
michael@0 218 Init(surface);
michael@0 219 cairo_surface_destroy(surface);
michael@0 220 }
michael@0 221 }
michael@0 222
michael@0 223 void Init(cairo_surface_t *aSurface)
michael@0 224 {
michael@0 225 mSurface = aSurface;
michael@0 226 cairo_surface_get_device_offset(mSurface, &mX, &mY);
michael@0 227 cairo_surface_set_device_offset(mSurface, 0, 0);
michael@0 228 }
michael@0 229
michael@0 230 cairo_surface_t* mSurface;
michael@0 231 double mX;
michael@0 232 double mY;
michael@0 233 };
michael@0 234
michael@0 235 // Never returns nullptr. As such, you must always pass in Cairo-compatible
michael@0 236 // patterns, most notably gradients with a GradientStopCairo.
michael@0 237 // The pattern returned must have cairo_pattern_destroy() called on it by the
michael@0 238 // caller.
michael@0 239 // As the cairo_pattern_t returned may depend on the Pattern passed in, the
michael@0 240 // lifetime of the cairo_pattern_t returned must not exceed the lifetime of the
michael@0 241 // Pattern passed in.
michael@0 242 static cairo_pattern_t*
michael@0 243 GfxPatternToCairoPattern(const Pattern& aPattern, Float aAlpha)
michael@0 244 {
michael@0 245 cairo_pattern_t* pat;
michael@0 246 const Matrix* matrix = nullptr;
michael@0 247
michael@0 248 switch (aPattern.GetType())
michael@0 249 {
michael@0 250 case PatternType::COLOR:
michael@0 251 {
michael@0 252 Color color = static_cast<const ColorPattern&>(aPattern).mColor;
michael@0 253 pat = cairo_pattern_create_rgba(color.r, color.g, color.b, color.a * aAlpha);
michael@0 254 break;
michael@0 255 }
michael@0 256
michael@0 257 case PatternType::SURFACE:
michael@0 258 {
michael@0 259 const SurfacePattern& pattern = static_cast<const SurfacePattern&>(aPattern);
michael@0 260 cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(pattern.mSurface);
michael@0 261 if (!surf)
michael@0 262 return nullptr;
michael@0 263
michael@0 264 pat = cairo_pattern_create_for_surface(surf);
michael@0 265
michael@0 266 matrix = &pattern.mMatrix;
michael@0 267
michael@0 268 cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(pattern.mFilter));
michael@0 269 cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(pattern.mExtendMode));
michael@0 270
michael@0 271 cairo_surface_destroy(surf);
michael@0 272 break;
michael@0 273 }
michael@0 274 case PatternType::LINEAR_GRADIENT:
michael@0 275 {
michael@0 276 const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern);
michael@0 277
michael@0 278 pat = cairo_pattern_create_linear(pattern.mBegin.x, pattern.mBegin.y,
michael@0 279 pattern.mEnd.x, pattern.mEnd.y);
michael@0 280
michael@0 281 MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO);
michael@0 282 GradientStopsCairo* cairoStops = static_cast<GradientStopsCairo*>(pattern.mStops.get());
michael@0 283 cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode()));
michael@0 284
michael@0 285 matrix = &pattern.mMatrix;
michael@0 286
michael@0 287 const std::vector<GradientStop>& stops = cairoStops->GetStops();
michael@0 288 for (size_t i = 0; i < stops.size(); ++i) {
michael@0 289 const GradientStop& stop = stops[i];
michael@0 290 cairo_pattern_add_color_stop_rgba(pat, stop.offset, stop.color.r,
michael@0 291 stop.color.g, stop.color.b,
michael@0 292 stop.color.a);
michael@0 293 }
michael@0 294
michael@0 295 break;
michael@0 296 }
michael@0 297 case PatternType::RADIAL_GRADIENT:
michael@0 298 {
michael@0 299 const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
michael@0 300
michael@0 301 pat = cairo_pattern_create_radial(pattern.mCenter1.x, pattern.mCenter1.y, pattern.mRadius1,
michael@0 302 pattern.mCenter2.x, pattern.mCenter2.y, pattern.mRadius2);
michael@0 303
michael@0 304 MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO);
michael@0 305 GradientStopsCairo* cairoStops = static_cast<GradientStopsCairo*>(pattern.mStops.get());
michael@0 306 cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode()));
michael@0 307
michael@0 308 matrix = &pattern.mMatrix;
michael@0 309
michael@0 310 const std::vector<GradientStop>& stops = cairoStops->GetStops();
michael@0 311 for (size_t i = 0; i < stops.size(); ++i) {
michael@0 312 const GradientStop& stop = stops[i];
michael@0 313 cairo_pattern_add_color_stop_rgba(pat, stop.offset, stop.color.r,
michael@0 314 stop.color.g, stop.color.b,
michael@0 315 stop.color.a);
michael@0 316 }
michael@0 317
michael@0 318 break;
michael@0 319 }
michael@0 320 default:
michael@0 321 {
michael@0 322 // We should support all pattern types!
michael@0 323 MOZ_ASSERT(false);
michael@0 324 }
michael@0 325 }
michael@0 326
michael@0 327 // The pattern matrix is a matrix that transforms the pattern into user
michael@0 328 // space. Cairo takes a matrix that converts from user space to pattern
michael@0 329 // space. Cairo therefore needs the inverse.
michael@0 330 if (matrix) {
michael@0 331 cairo_matrix_t mat;
michael@0 332 GfxMatrixToCairoMatrix(*matrix, mat);
michael@0 333 cairo_matrix_invert(&mat);
michael@0 334 cairo_pattern_set_matrix(pat, &mat);
michael@0 335 }
michael@0 336
michael@0 337 return pat;
michael@0 338 }
michael@0 339
michael@0 340 static bool
michael@0 341 NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions)
michael@0 342 {
michael@0 343 // We pre-multiply colours' alpha by the global alpha, so we don't need to
michael@0 344 // use an intermediate surface for them.
michael@0 345 if (aPattern.GetType() == PatternType::COLOR)
michael@0 346 return false;
michael@0 347
michael@0 348 if (aOptions.mAlpha == 1.0)
michael@0 349 return false;
michael@0 350
michael@0 351 return true;
michael@0 352 }
michael@0 353
michael@0 354 DrawTargetCairo::DrawTargetCairo()
michael@0 355 : mContext(nullptr)
michael@0 356 , mSurface(nullptr)
michael@0 357 , mLockedBits(nullptr)
michael@0 358 {
michael@0 359 }
michael@0 360
michael@0 361 DrawTargetCairo::~DrawTargetCairo()
michael@0 362 {
michael@0 363 cairo_destroy(mContext);
michael@0 364 if (mSurface) {
michael@0 365 cairo_surface_destroy(mSurface);
michael@0 366 }
michael@0 367 MOZ_ASSERT(!mLockedBits);
michael@0 368 }
michael@0 369
michael@0 370 IntSize
michael@0 371 DrawTargetCairo::GetSize()
michael@0 372 {
michael@0 373 return mSize;
michael@0 374 }
michael@0 375
michael@0 376 TemporaryRef<SourceSurface>
michael@0 377 DrawTargetCairo::Snapshot()
michael@0 378 {
michael@0 379 if (mSnapshot) {
michael@0 380 return mSnapshot;
michael@0 381 }
michael@0 382
michael@0 383 IntSize size = GetSize();
michael@0 384
michael@0 385 cairo_content_t content = cairo_surface_get_content(mSurface);
michael@0 386 mSnapshot = new SourceSurfaceCairo(mSurface,
michael@0 387 size,
michael@0 388 CairoContentToGfxFormat(content),
michael@0 389 this);
michael@0 390 return mSnapshot;
michael@0 391 }
michael@0 392
michael@0 393 bool
michael@0 394 DrawTargetCairo::LockBits(uint8_t** aData, IntSize* aSize,
michael@0 395 int32_t* aStride, SurfaceFormat* aFormat)
michael@0 396 {
michael@0 397 if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_IMAGE) {
michael@0 398 WillChange();
michael@0 399
michael@0 400 mLockedBits = cairo_image_surface_get_data(mSurface);
michael@0 401 *aData = mLockedBits;
michael@0 402 *aSize = GetSize();
michael@0 403 *aStride = cairo_image_surface_get_stride(mSurface);
michael@0 404 *aFormat = GetFormat();
michael@0 405 return true;
michael@0 406 }
michael@0 407
michael@0 408 return false;
michael@0 409 }
michael@0 410
michael@0 411 void
michael@0 412 DrawTargetCairo::ReleaseBits(uint8_t* aData)
michael@0 413 {
michael@0 414 MOZ_ASSERT(mLockedBits == aData);
michael@0 415 mLockedBits = nullptr;
michael@0 416 }
michael@0 417
michael@0 418 void
michael@0 419 DrawTargetCairo::Flush()
michael@0 420 {
michael@0 421 cairo_surface_t* surf = cairo_get_target(mContext);
michael@0 422 cairo_surface_flush(surf);
michael@0 423 }
michael@0 424
michael@0 425 void
michael@0 426 DrawTargetCairo::PrepareForDrawing(cairo_t* aContext, const Path* aPath /* = nullptr */)
michael@0 427 {
michael@0 428 WillChange(aPath);
michael@0 429 }
michael@0 430
michael@0 431 cairo_surface_t*
michael@0 432 DrawTargetCairo::GetDummySurface()
michael@0 433 {
michael@0 434 if (mDummySurface) {
michael@0 435 return mDummySurface;
michael@0 436 }
michael@0 437
michael@0 438 mDummySurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
michael@0 439
michael@0 440 return mDummySurface;
michael@0 441 }
michael@0 442
michael@0 443 void
michael@0 444 DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
michael@0 445 const Rect &aDest,
michael@0 446 const Rect &aSource,
michael@0 447 const DrawSurfaceOptions &aSurfOptions,
michael@0 448 const DrawOptions &aOptions)
michael@0 449 {
michael@0 450 AutoPrepareForDrawing prep(this, mContext);
michael@0 451 AutoClearDeviceOffset clear(aSurface);
michael@0 452
michael@0 453 float sx = aSource.Width() / aDest.Width();
michael@0 454 float sy = aSource.Height() / aDest.Height();
michael@0 455
michael@0 456 cairo_matrix_t src_mat;
michael@0 457 cairo_matrix_init_translate(&src_mat, aSource.X(), aSource.Y());
michael@0 458 cairo_matrix_scale(&src_mat, sx, sy);
michael@0 459
michael@0 460 cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface);
michael@0 461 cairo_pattern_t* pat = cairo_pattern_create_for_surface(surf);
michael@0 462 cairo_surface_destroy(surf);
michael@0 463
michael@0 464 cairo_pattern_set_matrix(pat, &src_mat);
michael@0 465 cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(aSurfOptions.mFilter));
michael@0 466 cairo_pattern_set_extend(pat, CAIRO_EXTEND_PAD);
michael@0 467
michael@0 468 cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
michael@0 469
michael@0 470 // If the destination rect covers the entire clipped area, then unbounded and bounded
michael@0 471 // operations are identical, and we don't need to push a group.
michael@0 472 bool needsGroup = !IsOperatorBoundByMask(aOptions.mCompositionOp) &&
michael@0 473 !aDest.Contains(GetUserSpaceClip());
michael@0 474
michael@0 475 cairo_translate(mContext, aDest.X(), aDest.Y());
michael@0 476
michael@0 477 if (needsGroup) {
michael@0 478 cairo_push_group(mContext);
michael@0 479 cairo_new_path(mContext);
michael@0 480 cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height());
michael@0 481 cairo_set_source(mContext, pat);
michael@0 482 cairo_fill(mContext);
michael@0 483 cairo_pop_group_to_source(mContext);
michael@0 484 } else {
michael@0 485 cairo_new_path(mContext);
michael@0 486 cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height());
michael@0 487 cairo_clip(mContext);
michael@0 488 cairo_set_source(mContext, pat);
michael@0 489 }
michael@0 490
michael@0 491 cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
michael@0 492
michael@0 493 cairo_paint_with_alpha(mContext, aOptions.mAlpha);
michael@0 494
michael@0 495 cairo_pattern_destroy(pat);
michael@0 496 }
michael@0 497
michael@0 498 void
michael@0 499 DrawTargetCairo::DrawFilter(FilterNode *aNode,
michael@0 500 const Rect &aSourceRect,
michael@0 501 const Point &aDestPoint,
michael@0 502 const DrawOptions &aOptions)
michael@0 503 {
michael@0 504 FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
michael@0 505 filter->Draw(this, aSourceRect, aDestPoint, aOptions);
michael@0 506 }
michael@0 507
michael@0 508 void
michael@0 509 DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface,
michael@0 510 const Point &aDest,
michael@0 511 const Color &aColor,
michael@0 512 const Point &aOffset,
michael@0 513 Float aSigma,
michael@0 514 CompositionOp aOperator)
michael@0 515 {
michael@0 516 if (aSurface->GetType() != SurfaceType::CAIRO) {
michael@0 517 return;
michael@0 518 }
michael@0 519
michael@0 520 AutoClearDeviceOffset clear(aSurface);
michael@0 521
michael@0 522 Float width = Float(aSurface->GetSize().width);
michael@0 523 Float height = Float(aSurface->GetSize().height);
michael@0 524
michael@0 525 SourceSurfaceCairo* source = static_cast<SourceSurfaceCairo*>(aSurface);
michael@0 526 cairo_surface_t* sourcesurf = source->GetSurface();
michael@0 527 cairo_surface_t* blursurf;
michael@0 528 cairo_surface_t* surf;
michael@0 529
michael@0 530 // We only use the A8 surface for blurred shadows. Unblurred shadows can just
michael@0 531 // use the RGBA surface directly.
michael@0 532 if (cairo_surface_get_type(sourcesurf) == CAIRO_SURFACE_TYPE_TEE) {
michael@0 533 blursurf = cairo_tee_surface_index(sourcesurf, 0);
michael@0 534 surf = cairo_tee_surface_index(sourcesurf, 1);
michael@0 535
michael@0 536 MOZ_ASSERT(cairo_surface_get_type(blursurf) == CAIRO_SURFACE_TYPE_IMAGE);
michael@0 537 Rect extents(0, 0, width, height);
michael@0 538 AlphaBoxBlur blur(extents,
michael@0 539 cairo_image_surface_get_stride(blursurf),
michael@0 540 aSigma, aSigma);
michael@0 541 blur.Blur(cairo_image_surface_get_data(blursurf));
michael@0 542 } else {
michael@0 543 blursurf = sourcesurf;
michael@0 544 surf = sourcesurf;
michael@0 545 }
michael@0 546
michael@0 547 WillChange();
michael@0 548 ClearSurfaceForUnboundedSource(aOperator);
michael@0 549
michael@0 550 cairo_save(mContext);
michael@0 551 cairo_set_operator(mContext, GfxOpToCairoOp(aOperator));
michael@0 552 cairo_identity_matrix(mContext);
michael@0 553 cairo_translate(mContext, aDest.x, aDest.y);
michael@0 554
michael@0 555 if (IsOperatorBoundByMask(aOperator)){
michael@0 556 cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a);
michael@0 557 cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y);
michael@0 558
michael@0 559 // Now that the shadow has been drawn, we can draw the surface on top.
michael@0 560 cairo_set_source_surface(mContext, surf, 0, 0);
michael@0 561 cairo_new_path(mContext);
michael@0 562 cairo_rectangle(mContext, 0, 0, width, height);
michael@0 563 cairo_fill(mContext);
michael@0 564 } else {
michael@0 565 cairo_push_group(mContext);
michael@0 566 cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a);
michael@0 567 cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y);
michael@0 568
michael@0 569 // Now that the shadow has been drawn, we can draw the surface on top.
michael@0 570 cairo_set_source_surface(mContext, surf, 0, 0);
michael@0 571 cairo_new_path(mContext);
michael@0 572 cairo_rectangle(mContext, 0, 0, width, height);
michael@0 573 cairo_fill(mContext);
michael@0 574 cairo_pop_group_to_source(mContext);
michael@0 575 cairo_paint(mContext);
michael@0 576 }
michael@0 577
michael@0 578 cairo_restore(mContext);
michael@0 579 }
michael@0 580
michael@0 581 void
michael@0 582 DrawTargetCairo::DrawPattern(const Pattern& aPattern,
michael@0 583 const StrokeOptions& aStrokeOptions,
michael@0 584 const DrawOptions& aOptions,
michael@0 585 DrawPatternType aDrawType,
michael@0 586 bool aPathBoundsClip)
michael@0 587 {
michael@0 588 if (!PatternIsCompatible(aPattern)) {
michael@0 589 return;
michael@0 590 }
michael@0 591
michael@0 592 AutoClearDeviceOffset clear(aPattern);
michael@0 593
michael@0 594 cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha);
michael@0 595 if (!pat) {
michael@0 596 return;
michael@0 597 }
michael@0 598 if (cairo_pattern_status(pat)) {
michael@0 599 cairo_pattern_destroy(pat);
michael@0 600 gfxWarning() << "Invalid pattern";
michael@0 601 return;
michael@0 602 }
michael@0 603
michael@0 604 cairo_set_source(mContext, pat);
michael@0 605
michael@0 606 cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
michael@0 607
michael@0 608 if (NeedIntermediateSurface(aPattern, aOptions) ||
michael@0 609 (!IsOperatorBoundByMask(aOptions.mCompositionOp) && !aPathBoundsClip)) {
michael@0 610 cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA);
michael@0 611
michael@0 612 // Don't want operators to be applied twice
michael@0 613 cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
michael@0 614
michael@0 615 if (aDrawType == DRAW_STROKE) {
michael@0 616 SetCairoStrokeOptions(mContext, aStrokeOptions);
michael@0 617 cairo_stroke_preserve(mContext);
michael@0 618 } else {
michael@0 619 cairo_fill_preserve(mContext);
michael@0 620 }
michael@0 621
michael@0 622 cairo_pop_group_to_source(mContext);
michael@0 623
michael@0 624 // Now draw the content using the desired operator
michael@0 625 cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
michael@0 626 cairo_paint_with_alpha(mContext, aOptions.mAlpha);
michael@0 627 } else {
michael@0 628 cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
michael@0 629
michael@0 630 if (aDrawType == DRAW_STROKE) {
michael@0 631 SetCairoStrokeOptions(mContext, aStrokeOptions);
michael@0 632 cairo_stroke_preserve(mContext);
michael@0 633 } else {
michael@0 634 cairo_fill_preserve(mContext);
michael@0 635 }
michael@0 636 }
michael@0 637
michael@0 638 cairo_pattern_destroy(pat);
michael@0 639 }
michael@0 640
michael@0 641 void
michael@0 642 DrawTargetCairo::FillRect(const Rect &aRect,
michael@0 643 const Pattern &aPattern,
michael@0 644 const DrawOptions &aOptions)
michael@0 645 {
michael@0 646 AutoPrepareForDrawing prep(this, mContext);
michael@0 647
michael@0 648 cairo_new_path(mContext);
michael@0 649 cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height());
michael@0 650
michael@0 651 bool pathBoundsClip = false;
michael@0 652
michael@0 653 if (aRect.Contains(GetUserSpaceClip())) {
michael@0 654 pathBoundsClip = true;
michael@0 655 }
michael@0 656
michael@0 657 DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL, pathBoundsClip);
michael@0 658 }
michael@0 659
michael@0 660 void
michael@0 661 DrawTargetCairo::CopySurfaceInternal(cairo_surface_t* aSurface,
michael@0 662 const IntRect &aSource,
michael@0 663 const IntPoint &aDest)
michael@0 664 {
michael@0 665 if (cairo_surface_status(aSurface)) {
michael@0 666 gfxWarning() << "Invalid surface";
michael@0 667 return;
michael@0 668 }
michael@0 669
michael@0 670 cairo_identity_matrix(mContext);
michael@0 671
michael@0 672 cairo_set_source_surface(mContext, aSurface, aDest.x - aSource.x, aDest.y - aSource.y);
michael@0 673 cairo_set_operator(mContext, CAIRO_OPERATOR_SOURCE);
michael@0 674 cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE);
michael@0 675
michael@0 676 cairo_reset_clip(mContext);
michael@0 677 cairo_new_path(mContext);
michael@0 678 cairo_rectangle(mContext, aDest.x, aDest.y, aSource.width, aSource.height);
michael@0 679 cairo_fill(mContext);
michael@0 680 }
michael@0 681
michael@0 682 void
michael@0 683 DrawTargetCairo::CopySurface(SourceSurface *aSurface,
michael@0 684 const IntRect &aSource,
michael@0 685 const IntPoint &aDest)
michael@0 686 {
michael@0 687 AutoPrepareForDrawing prep(this, mContext);
michael@0 688 AutoClearDeviceOffset clear(aSurface);
michael@0 689
michael@0 690 if (!aSurface) {
michael@0 691 gfxWarning() << "Unsupported surface type specified";
michael@0 692 return;
michael@0 693 }
michael@0 694
michael@0 695 cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface);
michael@0 696 if (!surf) {
michael@0 697 gfxWarning() << "Unsupported surface type specified";
michael@0 698 return;
michael@0 699 }
michael@0 700
michael@0 701 CopySurfaceInternal(surf, aSource, aDest);
michael@0 702 cairo_surface_destroy(surf);
michael@0 703 }
michael@0 704
michael@0 705 void
michael@0 706 DrawTargetCairo::CopyRect(const IntRect &aSource,
michael@0 707 const IntPoint &aDest)
michael@0 708 {
michael@0 709 AutoPrepareForDrawing prep(this, mContext);
michael@0 710
michael@0 711 IntRect source = aSource;
michael@0 712 cairo_surface_t* surf = mSurface;
michael@0 713
michael@0 714 if (!SupportsSelfCopy(mSurface) &&
michael@0 715 aDest.y >= aSource.y &&
michael@0 716 aDest.y < aSource.YMost()) {
michael@0 717 cairo_surface_t* similar = cairo_surface_create_similar(mSurface,
michael@0 718 GfxFormatToCairoContent(GetFormat()),
michael@0 719 aSource.width, aSource.height);
michael@0 720 cairo_t* ctx = cairo_create(similar);
michael@0 721 cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
michael@0 722 cairo_set_source_surface(ctx, surf, -aSource.x, -aSource.y);
michael@0 723 cairo_paint(ctx);
michael@0 724 cairo_destroy(ctx);
michael@0 725
michael@0 726 source.x = 0;
michael@0 727 source.y = 0;
michael@0 728 surf = similar;
michael@0 729 }
michael@0 730
michael@0 731 CopySurfaceInternal(surf, source, aDest);
michael@0 732
michael@0 733 if (surf != mSurface) {
michael@0 734 cairo_surface_destroy(surf);
michael@0 735 }
michael@0 736 }
michael@0 737
michael@0 738 void
michael@0 739 DrawTargetCairo::ClearRect(const Rect& aRect)
michael@0 740 {
michael@0 741 AutoPrepareForDrawing prep(this, mContext);
michael@0 742
michael@0 743 cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE);
michael@0 744 cairo_new_path(mContext);
michael@0 745 cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR);
michael@0 746 cairo_rectangle(mContext, aRect.X(), aRect.Y(),
michael@0 747 aRect.Width(), aRect.Height());
michael@0 748 cairo_fill(mContext);
michael@0 749 }
michael@0 750
michael@0 751 void
michael@0 752 DrawTargetCairo::StrokeRect(const Rect &aRect,
michael@0 753 const Pattern &aPattern,
michael@0 754 const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
michael@0 755 const DrawOptions &aOptions /* = DrawOptions() */)
michael@0 756 {
michael@0 757 AutoPrepareForDrawing prep(this, mContext);
michael@0 758
michael@0 759 cairo_new_path(mContext);
michael@0 760 cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height());
michael@0 761
michael@0 762 DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
michael@0 763 }
michael@0 764
michael@0 765 void
michael@0 766 DrawTargetCairo::StrokeLine(const Point &aStart,
michael@0 767 const Point &aEnd,
michael@0 768 const Pattern &aPattern,
michael@0 769 const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
michael@0 770 const DrawOptions &aOptions /* = DrawOptions() */)
michael@0 771 {
michael@0 772 AutoPrepareForDrawing prep(this, mContext);
michael@0 773
michael@0 774 cairo_new_path(mContext);
michael@0 775 cairo_move_to(mContext, aStart.x, aStart.y);
michael@0 776 cairo_line_to(mContext, aEnd.x, aEnd.y);
michael@0 777
michael@0 778 DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
michael@0 779 }
michael@0 780
michael@0 781 void
michael@0 782 DrawTargetCairo::Stroke(const Path *aPath,
michael@0 783 const Pattern &aPattern,
michael@0 784 const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
michael@0 785 const DrawOptions &aOptions /* = DrawOptions() */)
michael@0 786 {
michael@0 787 AutoPrepareForDrawing prep(this, mContext, aPath);
michael@0 788
michael@0 789 if (aPath->GetBackendType() != BackendType::CAIRO)
michael@0 790 return;
michael@0 791
michael@0 792 PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
michael@0 793 path->SetPathOnContext(mContext);
michael@0 794
michael@0 795 DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
michael@0 796 }
michael@0 797
michael@0 798 void
michael@0 799 DrawTargetCairo::Fill(const Path *aPath,
michael@0 800 const Pattern &aPattern,
michael@0 801 const DrawOptions &aOptions /* = DrawOptions() */)
michael@0 802 {
michael@0 803 AutoPrepareForDrawing prep(this, mContext, aPath);
michael@0 804
michael@0 805 if (aPath->GetBackendType() != BackendType::CAIRO)
michael@0 806 return;
michael@0 807
michael@0 808 PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
michael@0 809 path->SetPathOnContext(mContext);
michael@0 810
michael@0 811 DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL);
michael@0 812 }
michael@0 813
michael@0 814 void
michael@0 815 DrawTargetCairo::SetPermitSubpixelAA(bool aPermitSubpixelAA)
michael@0 816 {
michael@0 817 DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA);
michael@0 818 #ifdef MOZ_TREE_CAIRO
michael@0 819 cairo_surface_set_subpixel_antialiasing(mSurface,
michael@0 820 aPermitSubpixelAA ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
michael@0 821 #endif
michael@0 822 }
michael@0 823
michael@0 824 void
michael@0 825 DrawTargetCairo::FillGlyphs(ScaledFont *aFont,
michael@0 826 const GlyphBuffer &aBuffer,
michael@0 827 const Pattern &aPattern,
michael@0 828 const DrawOptions &aOptions,
michael@0 829 const GlyphRenderingOptions*)
michael@0 830 {
michael@0 831 AutoPrepareForDrawing prep(this, mContext);
michael@0 832 AutoClearDeviceOffset clear(aPattern);
michael@0 833
michael@0 834 ScaledFontBase* scaledFont = static_cast<ScaledFontBase*>(aFont);
michael@0 835 cairo_set_scaled_font(mContext, scaledFont->GetCairoScaledFont());
michael@0 836
michael@0 837 cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha);
michael@0 838 if (!pat)
michael@0 839 return;
michael@0 840
michael@0 841 cairo_set_source(mContext, pat);
michael@0 842 cairo_pattern_destroy(pat);
michael@0 843
michael@0 844 cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
michael@0 845
michael@0 846 // Convert our GlyphBuffer into an array of Cairo glyphs.
michael@0 847 std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
michael@0 848 for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
michael@0 849 glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
michael@0 850 glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
michael@0 851 glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
michael@0 852 }
michael@0 853
michael@0 854 cairo_show_glyphs(mContext, &glyphs[0], aBuffer.mNumGlyphs);
michael@0 855 }
michael@0 856
michael@0 857 void
michael@0 858 DrawTargetCairo::Mask(const Pattern &aSource,
michael@0 859 const Pattern &aMask,
michael@0 860 const DrawOptions &aOptions /* = DrawOptions() */)
michael@0 861 {
michael@0 862 AutoPrepareForDrawing prep(this, mContext);
michael@0 863 AutoClearDeviceOffset clearSource(aSource);
michael@0 864 AutoClearDeviceOffset clearMask(aMask);
michael@0 865
michael@0 866 cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
michael@0 867
michael@0 868 cairo_pattern_t* source = GfxPatternToCairoPattern(aSource, aOptions.mAlpha);
michael@0 869 if (!source) {
michael@0 870 return;
michael@0 871 }
michael@0 872
michael@0 873 cairo_pattern_t* mask = GfxPatternToCairoPattern(aMask, aOptions.mAlpha);
michael@0 874 if (!mask) {
michael@0 875 cairo_pattern_destroy(source);
michael@0 876 return;
michael@0 877 }
michael@0 878
michael@0 879 if (cairo_pattern_status(source) || cairo_pattern_status(mask)) {
michael@0 880 cairo_pattern_destroy(source);
michael@0 881 cairo_pattern_destroy(mask);
michael@0 882 gfxWarning() << "Invalid pattern";
michael@0 883 return;
michael@0 884 }
michael@0 885
michael@0 886 cairo_set_source(mContext, source);
michael@0 887 cairo_mask(mContext, mask);
michael@0 888
michael@0 889 cairo_pattern_destroy(mask);
michael@0 890 cairo_pattern_destroy(source);
michael@0 891 }
michael@0 892
michael@0 893 void
michael@0 894 DrawTargetCairo::MaskSurface(const Pattern &aSource,
michael@0 895 SourceSurface *aMask,
michael@0 896 Point aOffset,
michael@0 897 const DrawOptions &aOptions)
michael@0 898 {
michael@0 899 AutoPrepareForDrawing prep(this, mContext);
michael@0 900 AutoClearDeviceOffset clearSource(aSource);
michael@0 901 AutoClearDeviceOffset clearMask(aMask);
michael@0 902
michael@0 903 if (!PatternIsCompatible(aSource)) {
michael@0 904 return;
michael@0 905 }
michael@0 906
michael@0 907 cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
michael@0 908
michael@0 909 cairo_pattern_t* pat = GfxPatternToCairoPattern(aSource, aOptions.mAlpha);
michael@0 910 if (!pat) {
michael@0 911 return;
michael@0 912 }
michael@0 913
michael@0 914 if (cairo_pattern_status(pat)) {
michael@0 915 cairo_pattern_destroy(pat);
michael@0 916 gfxWarning() << "Invalid pattern";
michael@0 917 return;
michael@0 918 }
michael@0 919
michael@0 920 cairo_set_source(mContext, pat);
michael@0 921
michael@0 922 if (NeedIntermediateSurface(aSource, aOptions)) {
michael@0 923 cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA);
michael@0 924
michael@0 925 // Don't want operators to be applied twice
michael@0 926 cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
michael@0 927
michael@0 928 // Now draw the content using the desired operator
michael@0 929 cairo_paint_with_alpha(mContext, aOptions.mAlpha);
michael@0 930
michael@0 931 cairo_pop_group_to_source(mContext);
michael@0 932 }
michael@0 933
michael@0 934 cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aMask);
michael@0 935 if (!surf) {
michael@0 936 cairo_pattern_destroy(pat);
michael@0 937 return;
michael@0 938 }
michael@0 939 cairo_pattern_t* mask = cairo_pattern_create_for_surface(surf);
michael@0 940 cairo_matrix_t matrix;
michael@0 941
michael@0 942 cairo_matrix_init_translate (&matrix, -aOffset.x, -aOffset.y);
michael@0 943 cairo_pattern_set_matrix (mask, &matrix);
michael@0 944
michael@0 945 cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
michael@0 946
michael@0 947 cairo_mask(mContext, mask);
michael@0 948
michael@0 949 cairo_surface_destroy(surf);
michael@0 950 cairo_pattern_destroy(mask);
michael@0 951 cairo_pattern_destroy(pat);
michael@0 952 }
michael@0 953
michael@0 954 void
michael@0 955 DrawTargetCairo::PushClip(const Path *aPath)
michael@0 956 {
michael@0 957 if (aPath->GetBackendType() != BackendType::CAIRO) {
michael@0 958 return;
michael@0 959 }
michael@0 960
michael@0 961 WillChange(aPath);
michael@0 962 cairo_save(mContext);
michael@0 963
michael@0 964 PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
michael@0 965 path->SetPathOnContext(mContext);
michael@0 966 cairo_clip_preserve(mContext);
michael@0 967 }
michael@0 968
michael@0 969 void
michael@0 970 DrawTargetCairo::PushClipRect(const Rect& aRect)
michael@0 971 {
michael@0 972 WillChange();
michael@0 973 cairo_save(mContext);
michael@0 974
michael@0 975 cairo_new_path(mContext);
michael@0 976 cairo_rectangle(mContext, aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
michael@0 977 cairo_clip_preserve(mContext);
michael@0 978 }
michael@0 979
michael@0 980 void
michael@0 981 DrawTargetCairo::PopClip()
michael@0 982 {
michael@0 983 // save/restore does not affect the path, so no need to call WillChange()
michael@0 984
michael@0 985 // cairo_restore will restore the transform too and we don't want to do that
michael@0 986 // so we'll save it now and restore it after the cairo_restore
michael@0 987 cairo_matrix_t mat;
michael@0 988 cairo_get_matrix(mContext, &mat);
michael@0 989
michael@0 990 cairo_restore(mContext);
michael@0 991
michael@0 992 cairo_set_matrix(mContext, &mat);
michael@0 993
michael@0 994 MOZ_ASSERT(cairo_status(mContext) || GetTransform() == Matrix(mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0),
michael@0 995 "Transforms are out of sync");
michael@0 996 }
michael@0 997
michael@0 998 TemporaryRef<PathBuilder>
michael@0 999 DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FillRule::FILL_WINDING */) const
michael@0 1000 {
michael@0 1001 RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
michael@0 1002
michael@0 1003 return builder;
michael@0 1004 }
michael@0 1005
michael@0 1006 void
michael@0 1007 DrawTargetCairo::ClearSurfaceForUnboundedSource(const CompositionOp &aOperator)
michael@0 1008 {
michael@0 1009 if (aOperator != CompositionOp::OP_SOURCE)
michael@0 1010 return;
michael@0 1011 cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR);
michael@0 1012 // It doesn't really matter what the source is here, since Paint
michael@0 1013 // isn't bounded by the source and the mask covers the entire clip
michael@0 1014 // region.
michael@0 1015 cairo_paint(mContext);
michael@0 1016 }
michael@0 1017
michael@0 1018
michael@0 1019 TemporaryRef<GradientStops>
michael@0 1020 DrawTargetCairo::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops,
michael@0 1021 ExtendMode aExtendMode) const
michael@0 1022 {
michael@0 1023 RefPtr<GradientStopsCairo> stops = new GradientStopsCairo(aStops, aNumStops,
michael@0 1024 aExtendMode);
michael@0 1025 return stops;
michael@0 1026 }
michael@0 1027
michael@0 1028 TemporaryRef<FilterNode>
michael@0 1029 DrawTargetCairo::CreateFilter(FilterType aType)
michael@0 1030 {
michael@0 1031 return FilterNodeSoftware::Create(aType);
michael@0 1032 }
michael@0 1033
michael@0 1034 /**
michael@0 1035 * Copies pixel data from aData into aSurface; aData must have the dimensions
michael@0 1036 * given in aSize, with a stride of aStride bytes and aPixelWidth bytes per pixel
michael@0 1037 */
michael@0 1038 static void
michael@0 1039 CopyDataToCairoSurface(cairo_surface_t* aSurface,
michael@0 1040 unsigned char *aData,
michael@0 1041 const IntSize &aSize,
michael@0 1042 int32_t aStride,
michael@0 1043 int32_t aPixelWidth)
michael@0 1044 {
michael@0 1045 unsigned char* surfData = cairo_image_surface_get_data(aSurface);
michael@0 1046 int surfStride = cairo_image_surface_get_stride(aSurface);
michael@0 1047 // In certain scenarios, requesting larger than 8k image fails. Bug 803568
michael@0 1048 // covers the details of how to run into it, but the full detailed
michael@0 1049 // investigation hasn't been done to determine the underlying cause. We
michael@0 1050 // will just handle the failure to allocate the surface to avoid a crash.
michael@0 1051 if (!surfData) {
michael@0 1052 return;
michael@0 1053 }
michael@0 1054 for (int32_t y = 0; y < aSize.height; ++y) {
michael@0 1055 memcpy(surfData + y * surfStride,
michael@0 1056 aData + y * aStride,
michael@0 1057 aSize.width * aPixelWidth);
michael@0 1058 }
michael@0 1059 cairo_surface_mark_dirty(aSurface);
michael@0 1060 }
michael@0 1061
michael@0 1062 TemporaryRef<SourceSurface>
michael@0 1063 DrawTargetCairo::CreateSourceSurfaceFromData(unsigned char *aData,
michael@0 1064 const IntSize &aSize,
michael@0 1065 int32_t aStride,
michael@0 1066 SurfaceFormat aFormat) const
michael@0 1067 {
michael@0 1068 cairo_surface_t* surf = cairo_image_surface_create(GfxFormatToCairoFormat(aFormat),
michael@0 1069 aSize.width,
michael@0 1070 aSize.height);
michael@0 1071 // In certain scenarios, requesting larger than 8k image fails. Bug 803568
michael@0 1072 // covers the details of how to run into it, but the full detailed
michael@0 1073 // investigation hasn't been done to determine the underlying cause. We
michael@0 1074 // will just handle the failure to allocate the surface to avoid a crash.
michael@0 1075 if (cairo_surface_status(surf)) {
michael@0 1076 return nullptr;
michael@0 1077 }
michael@0 1078
michael@0 1079 CopyDataToCairoSurface(surf, aData, aSize, aStride, BytesPerPixel(aFormat));
michael@0 1080
michael@0 1081 RefPtr<SourceSurfaceCairo> source_surf = new SourceSurfaceCairo(surf, aSize, aFormat);
michael@0 1082 cairo_surface_destroy(surf);
michael@0 1083
michael@0 1084 return source_surf;
michael@0 1085 }
michael@0 1086
michael@0 1087 TemporaryRef<SourceSurface>
michael@0 1088 DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const
michael@0 1089 {
michael@0 1090 return aSurface;
michael@0 1091 }
michael@0 1092
michael@0 1093 TemporaryRef<SourceSurface>
michael@0 1094 DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
michael@0 1095 {
michael@0 1096 if (aSurface.mType == NativeSurfaceType::CAIRO_SURFACE) {
michael@0 1097 if (aSurface.mSize.width <= 0 ||
michael@0 1098 aSurface.mSize.height <= 0) {
michael@0 1099 gfxWarning() << "Can't create a SourceSurface without a valid size";
michael@0 1100 return nullptr;
michael@0 1101 }
michael@0 1102 cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface);
michael@0 1103 RefPtr<SourceSurfaceCairo> source =
michael@0 1104 new SourceSurfaceCairo(surf, aSurface.mSize, aSurface.mFormat);
michael@0 1105 return source;
michael@0 1106 }
michael@0 1107
michael@0 1108 return nullptr;
michael@0 1109 }
michael@0 1110
michael@0 1111 TemporaryRef<DrawTarget>
michael@0 1112 DrawTargetCairo::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
michael@0 1113 {
michael@0 1114 cairo_surface_t* similar = cairo_surface_create_similar(cairo_get_target(mContext),
michael@0 1115 GfxFormatToCairoContent(aFormat),
michael@0 1116 aSize.width, aSize.height);
michael@0 1117
michael@0 1118 if (!cairo_surface_status(similar)) {
michael@0 1119 RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
michael@0 1120 target->InitAlreadyReferenced(similar, aSize);
michael@0 1121 return target;
michael@0 1122 }
michael@0 1123
michael@0 1124 return nullptr;
michael@0 1125 }
michael@0 1126
michael@0 1127 bool
michael@0 1128 DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
michael@0 1129 {
michael@0 1130 mContext = cairo_create(aSurface);
michael@0 1131 mSurface = aSurface;
michael@0 1132 mSize = aSize;
michael@0 1133 mFormat = aFormat ? *aFormat : CairoContentToGfxFormat(cairo_surface_get_content(aSurface));
michael@0 1134
michael@0 1135 if (mFormat == SurfaceFormat::B8G8R8A8 ||
michael@0 1136 mFormat == SurfaceFormat::R8G8B8A8) {
michael@0 1137 SetPermitSubpixelAA(false);
michael@0 1138 } else {
michael@0 1139 SetPermitSubpixelAA(true);
michael@0 1140 }
michael@0 1141
michael@0 1142 return true;
michael@0 1143 }
michael@0 1144
michael@0 1145 TemporaryRef<DrawTarget>
michael@0 1146 DrawTargetCairo::CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat,
michael@0 1147 float aSigma) const
michael@0 1148 {
michael@0 1149 cairo_surface_t* similar = cairo_surface_create_similar(cairo_get_target(mContext),
michael@0 1150 GfxFormatToCairoContent(aFormat),
michael@0 1151 aSize.width, aSize.height);
michael@0 1152
michael@0 1153 if (cairo_surface_status(similar)) {
michael@0 1154 return nullptr;
michael@0 1155 }
michael@0 1156
michael@0 1157 // If we don't have a blur then we can use the RGBA mask and keep all the
michael@0 1158 // operations in graphics memory.
michael@0 1159 if (aSigma == 0.0F) {
michael@0 1160 RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
michael@0 1161 target->InitAlreadyReferenced(similar, aSize);
michael@0 1162 return target;
michael@0 1163 }
michael@0 1164
michael@0 1165 cairo_surface_t* blursurf = cairo_image_surface_create(CAIRO_FORMAT_A8,
michael@0 1166 aSize.width,
michael@0 1167 aSize.height);
michael@0 1168
michael@0 1169 if (cairo_surface_status(blursurf)) {
michael@0 1170 return nullptr;
michael@0 1171 }
michael@0 1172
michael@0 1173 cairo_surface_t* tee = cairo_tee_surface_create(blursurf);
michael@0 1174 cairo_surface_destroy(blursurf);
michael@0 1175 if (cairo_surface_status(tee)) {
michael@0 1176 cairo_surface_destroy(similar);
michael@0 1177 return nullptr;
michael@0 1178 }
michael@0 1179
michael@0 1180 cairo_tee_surface_add(tee, similar);
michael@0 1181 cairo_surface_destroy(similar);
michael@0 1182
michael@0 1183 RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
michael@0 1184 target->InitAlreadyReferenced(tee, aSize);
michael@0 1185 return target;
michael@0 1186 }
michael@0 1187
michael@0 1188 bool
michael@0 1189 DrawTargetCairo::Init(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
michael@0 1190 {
michael@0 1191 cairo_surface_reference(aSurface);
michael@0 1192 return InitAlreadyReferenced(aSurface, aSize, aFormat);
michael@0 1193 }
michael@0 1194
michael@0 1195 bool
michael@0 1196 DrawTargetCairo::Init(const IntSize& aSize, SurfaceFormat aFormat)
michael@0 1197 {
michael@0 1198 cairo_surface_t *surf = cairo_image_surface_create(GfxFormatToCairoFormat(aFormat), aSize.width, aSize.height);
michael@0 1199 return InitAlreadyReferenced(surf, aSize);
michael@0 1200 }
michael@0 1201
michael@0 1202 bool
michael@0 1203 DrawTargetCairo::Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat)
michael@0 1204 {
michael@0 1205 cairo_surface_t* surf =
michael@0 1206 cairo_image_surface_create_for_data(aData,
michael@0 1207 GfxFormatToCairoFormat(aFormat),
michael@0 1208 aSize.width,
michael@0 1209 aSize.height,
michael@0 1210 aStride);
michael@0 1211 return InitAlreadyReferenced(surf, aSize);
michael@0 1212 }
michael@0 1213
michael@0 1214 void *
michael@0 1215 DrawTargetCairo::GetNativeSurface(NativeSurfaceType aType)
michael@0 1216 {
michael@0 1217 if (aType == NativeSurfaceType::CAIRO_SURFACE) {
michael@0 1218 return cairo_get_target(mContext);
michael@0 1219 }
michael@0 1220 if (aType == NativeSurfaceType::CAIRO_CONTEXT) {
michael@0 1221 return mContext;
michael@0 1222 }
michael@0 1223
michael@0 1224 return nullptr;
michael@0 1225 }
michael@0 1226
michael@0 1227 void
michael@0 1228 DrawTargetCairo::MarkSnapshotIndependent()
michael@0 1229 {
michael@0 1230 if (mSnapshot) {
michael@0 1231 if (mSnapshot->refCount() > 1) {
michael@0 1232 // We only need to worry about snapshots that someone else knows about
michael@0 1233 mSnapshot->DrawTargetWillChange();
michael@0 1234 }
michael@0 1235 mSnapshot = nullptr;
michael@0 1236 }
michael@0 1237 }
michael@0 1238
michael@0 1239 void
michael@0 1240 DrawTargetCairo::WillChange(const Path* aPath /* = nullptr */)
michael@0 1241 {
michael@0 1242 MarkSnapshotIndependent();
michael@0 1243 MOZ_ASSERT(!mLockedBits);
michael@0 1244 }
michael@0 1245
michael@0 1246 void
michael@0 1247 DrawTargetCairo::SetTransform(const Matrix& aTransform)
michael@0 1248 {
michael@0 1249 mTransform = aTransform;
michael@0 1250
michael@0 1251 cairo_matrix_t mat;
michael@0 1252 GfxMatrixToCairoMatrix(mTransform, mat);
michael@0 1253 cairo_set_matrix(mContext, &mat);
michael@0 1254 }
michael@0 1255
michael@0 1256 Rect
michael@0 1257 DrawTargetCairo::GetUserSpaceClip()
michael@0 1258 {
michael@0 1259 double clipX1, clipY1, clipX2, clipY2;
michael@0 1260 cairo_clip_extents(mContext, &clipX1, &clipY1, &clipX2, &clipY2);
michael@0 1261 return Rect(clipX1, clipY1, clipX2 - clipX1, clipY2 - clipY1); // Narrowing of doubles to floats
michael@0 1262 }
michael@0 1263
michael@0 1264 cairo_t*
michael@0 1265 BorrowedCairoContext::BorrowCairoContextFromDrawTarget(DrawTarget* aDT)
michael@0 1266 {
michael@0 1267 if (aDT->GetType() != BackendType::CAIRO || aDT->IsDualDrawTarget()) {
michael@0 1268 return nullptr;
michael@0 1269 }
michael@0 1270 DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
michael@0 1271
michael@0 1272 cairoDT->WillChange();
michael@0 1273
michael@0 1274 // save the state to make it easier for callers to avoid mucking with things
michael@0 1275 cairo_save(cairoDT->mContext);
michael@0 1276
michael@0 1277 // Neuter the DrawTarget while the context is being borrowed
michael@0 1278 cairo_t* cairo = cairoDT->mContext;
michael@0 1279 cairoDT->mContext = nullptr;
michael@0 1280
michael@0 1281 return cairo;
michael@0 1282 }
michael@0 1283
michael@0 1284 void
michael@0 1285 BorrowedCairoContext::ReturnCairoContextToDrawTarget(DrawTarget* aDT,
michael@0 1286 cairo_t* aCairo)
michael@0 1287 {
michael@0 1288 if (aDT->GetType() != BackendType::CAIRO || aDT->IsDualDrawTarget()) {
michael@0 1289 return;
michael@0 1290 }
michael@0 1291 DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
michael@0 1292
michael@0 1293 cairo_restore(aCairo);
michael@0 1294 cairoDT->mContext = aCairo;
michael@0 1295 }
michael@0 1296
michael@0 1297 }
michael@0 1298 }

mercurial