gfx/thebes/gfxContext.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 #ifdef _MSC_VER
michael@0 7 #define _USE_MATH_DEFINES
michael@0 8 #endif
michael@0 9 #include <math.h>
michael@0 10
michael@0 11 #include "mozilla/Alignment.h"
michael@0 12
michael@0 13 #include "cairo.h"
michael@0 14
michael@0 15 #include "gfxContext.h"
michael@0 16
michael@0 17 #include "gfxColor.h"
michael@0 18 #include "gfxMatrix.h"
michael@0 19 #include "gfxASurface.h"
michael@0 20 #include "gfxPattern.h"
michael@0 21 #include "gfxPlatform.h"
michael@0 22 #include "gfxTeeSurface.h"
michael@0 23 #include "GeckoProfiler.h"
michael@0 24 #include "gfx2DGlue.h"
michael@0 25 #include "mozilla/gfx/PathHelpers.h"
michael@0 26 #include <algorithm>
michael@0 27
michael@0 28 #if CAIRO_HAS_DWRITE_FONT
michael@0 29 #include "gfxWindowsPlatform.h"
michael@0 30 #endif
michael@0 31
michael@0 32 using namespace mozilla;
michael@0 33 using namespace mozilla::gfx;
michael@0 34
michael@0 35 UserDataKey gfxContext::sDontUseAsSourceKey;
michael@0 36
michael@0 37 /* This class lives on the stack and allows gfxContext users to easily, and
michael@0 38 * performantly get a gfx::Pattern to use for drawing in their current context.
michael@0 39 */
michael@0 40 class GeneralPattern
michael@0 41 {
michael@0 42 public:
michael@0 43 GeneralPattern(gfxContext *aContext) : mContext(aContext), mPattern(nullptr) {}
michael@0 44 ~GeneralPattern() { if (mPattern) { mPattern->~Pattern(); } }
michael@0 45
michael@0 46 operator mozilla::gfx::Pattern&()
michael@0 47 {
michael@0 48 gfxContext::AzureState &state = mContext->CurrentState();
michael@0 49
michael@0 50 if (state.pattern) {
michael@0 51 return *state.pattern->GetPattern(mContext->mDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
michael@0 52 } else if (state.sourceSurface) {
michael@0 53 Matrix transform = state.surfTransform;
michael@0 54
michael@0 55 if (state.patternTransformChanged) {
michael@0 56 Matrix mat = mContext->GetDTTransform();
michael@0 57 mat.Invert();
michael@0 58
michael@0 59 transform = transform * state.patternTransform * mat;
michael@0 60 }
michael@0 61
michael@0 62 mPattern = new (mSurfacePattern.addr())
michael@0 63 SurfacePattern(state.sourceSurface, ExtendMode::CLAMP, transform);
michael@0 64 return *mPattern;
michael@0 65 } else {
michael@0 66 mPattern = new (mColorPattern.addr())
michael@0 67 ColorPattern(state.color);
michael@0 68 return *mPattern;
michael@0 69 }
michael@0 70 }
michael@0 71
michael@0 72 private:
michael@0 73 union {
michael@0 74 mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern;
michael@0 75 mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern;
michael@0 76 };
michael@0 77
michael@0 78 gfxContext *mContext;
michael@0 79 Pattern *mPattern;
michael@0 80 };
michael@0 81
michael@0 82 gfxContext::gfxContext(gfxASurface *surface)
michael@0 83 : mRefCairo(nullptr)
michael@0 84 , mSurface(surface)
michael@0 85 {
michael@0 86 MOZ_COUNT_CTOR(gfxContext);
michael@0 87
michael@0 88 mCairo = cairo_create(surface->CairoSurface());
michael@0 89 mFlags = surface->GetDefaultContextFlags();
michael@0 90 if (mSurface->GetRotateForLandscape()) {
michael@0 91 // Rotate page 90 degrees to draw landscape page on portrait paper
michael@0 92 gfxIntSize size = mSurface->GetSize();
michael@0 93 Translate(gfxPoint(0, size.width));
michael@0 94 gfxMatrix matrix(0, -1,
michael@0 95 1, 0,
michael@0 96 0, 0);
michael@0 97 Multiply(matrix);
michael@0 98 }
michael@0 99 }
michael@0 100
michael@0 101 gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
michael@0 102 : mPathIsRect(false)
michael@0 103 , mTransformChanged(false)
michael@0 104 , mCairo(nullptr)
michael@0 105 , mRefCairo(nullptr)
michael@0 106 , mSurface(nullptr)
michael@0 107 , mFlags(0)
michael@0 108 , mDT(aTarget)
michael@0 109 , mOriginalDT(aTarget)
michael@0 110 {
michael@0 111 MOZ_COUNT_CTOR(gfxContext);
michael@0 112
michael@0 113 mStateStack.SetLength(1);
michael@0 114 CurrentState().drawTarget = mDT;
michael@0 115 CurrentState().deviceOffset = aDeviceOffset;
michael@0 116 mDT->SetTransform(Matrix());
michael@0 117 }
michael@0 118
michael@0 119 /* static */ already_AddRefed<gfxContext>
michael@0 120 gfxContext::ContextForDrawTarget(DrawTarget* aTarget)
michael@0 121 {
michael@0 122 Matrix transform = aTarget->GetTransform();
michael@0 123 nsRefPtr<gfxContext> result = new gfxContext(aTarget);
michael@0 124 result->SetMatrix(ThebesMatrix(transform));
michael@0 125 return result.forget();
michael@0 126 }
michael@0 127
michael@0 128 gfxContext::~gfxContext()
michael@0 129 {
michael@0 130 if (mCairo) {
michael@0 131 cairo_destroy(mCairo);
michael@0 132 }
michael@0 133 if (mRefCairo) {
michael@0 134 cairo_destroy(mRefCairo);
michael@0 135 }
michael@0 136 if (mDT) {
michael@0 137 for (int i = mStateStack.Length() - 1; i >= 0; i--) {
michael@0 138 for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
michael@0 139 mDT->PopClip();
michael@0 140 }
michael@0 141
michael@0 142 if (mStateStack[i].clipWasReset) {
michael@0 143 break;
michael@0 144 }
michael@0 145 }
michael@0 146 mDT->Flush();
michael@0 147 }
michael@0 148 MOZ_COUNT_DTOR(gfxContext);
michael@0 149 }
michael@0 150
michael@0 151 gfxASurface *
michael@0 152 gfxContext::OriginalSurface()
michael@0 153 {
michael@0 154 if (mCairo || mSurface) {
michael@0 155 return mSurface;
michael@0 156 }
michael@0 157
michael@0 158 if (mOriginalDT && mOriginalDT->GetType() == BackendType::CAIRO) {
michael@0 159 cairo_surface_t *s =
michael@0 160 (cairo_surface_t*)mOriginalDT->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE);
michael@0 161 if (s) {
michael@0 162 mSurface = gfxASurface::Wrap(s);
michael@0 163 return mSurface;
michael@0 164 }
michael@0 165 }
michael@0 166 return nullptr;
michael@0 167 }
michael@0 168
michael@0 169 already_AddRefed<gfxASurface>
michael@0 170 gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy)
michael@0 171 {
michael@0 172 if (mCairo) {
michael@0 173 cairo_surface_t *s = cairo_get_group_target(mCairo);
michael@0 174 if (s == mSurface->CairoSurface()) {
michael@0 175 if (dx && dy)
michael@0 176 cairo_surface_get_device_offset(s, dx, dy);
michael@0 177 nsRefPtr<gfxASurface> ret = mSurface;
michael@0 178 return ret.forget();
michael@0 179 }
michael@0 180
michael@0 181 if (dx && dy)
michael@0 182 cairo_surface_get_device_offset(s, dx, dy);
michael@0 183 return gfxASurface::Wrap(s);
michael@0 184 } else {
michael@0 185 if (mDT->GetType() == BackendType::CAIRO) {
michael@0 186 cairo_surface_t *s =
michael@0 187 (cairo_surface_t*)mDT->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE);
michael@0 188 if (s) {
michael@0 189 if (dx && dy) {
michael@0 190 *dx = -CurrentState().deviceOffset.x;
michael@0 191 *dy = -CurrentState().deviceOffset.y;
michael@0 192 }
michael@0 193 return gfxASurface::Wrap(s);
michael@0 194 }
michael@0 195 }
michael@0 196
michael@0 197 if (dx && dy) {
michael@0 198 *dx = *dy = 0;
michael@0 199 }
michael@0 200 // An Azure context doesn't have a surface backing it.
michael@0 201 return nullptr;
michael@0 202 }
michael@0 203 }
michael@0 204
michael@0 205 cairo_t *
michael@0 206 gfxContext::GetCairo()
michael@0 207 {
michael@0 208 if (mCairo) {
michael@0 209 return mCairo;
michael@0 210 }
michael@0 211
michael@0 212 if (mDT->GetType() == BackendType::CAIRO) {
michael@0 213 cairo_t *ctx =
michael@0 214 (cairo_t*)mDT->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT);
michael@0 215 if (ctx) {
michael@0 216 return ctx;
michael@0 217 }
michael@0 218 }
michael@0 219
michael@0 220 if (mRefCairo) {
michael@0 221 // Set transform!
michael@0 222 return mRefCairo;
michael@0 223 }
michael@0 224
michael@0 225 mRefCairo = cairo_create(gfxPlatform::GetPlatform()->ScreenReferenceSurface()->CairoSurface());
michael@0 226
michael@0 227 return mRefCairo;
michael@0 228 }
michael@0 229
michael@0 230 void
michael@0 231 gfxContext::Save()
michael@0 232 {
michael@0 233 if (mCairo) {
michael@0 234 cairo_save(mCairo);
michael@0 235 } else {
michael@0 236 CurrentState().transform = mTransform;
michael@0 237 mStateStack.AppendElement(AzureState(CurrentState()));
michael@0 238 CurrentState().clipWasReset = false;
michael@0 239 CurrentState().pushedClips.Clear();
michael@0 240 }
michael@0 241 }
michael@0 242
michael@0 243 void
michael@0 244 gfxContext::Restore()
michael@0 245 {
michael@0 246 if (mCairo) {
michael@0 247 cairo_restore(mCairo);
michael@0 248 } else {
michael@0 249 for (unsigned int c = 0; c < CurrentState().pushedClips.Length(); c++) {
michael@0 250 mDT->PopClip();
michael@0 251 }
michael@0 252
michael@0 253 if (CurrentState().clipWasReset &&
michael@0 254 CurrentState().drawTarget == mStateStack[mStateStack.Length() - 2].drawTarget) {
michael@0 255 PushClipsToDT(mDT);
michael@0 256 }
michael@0 257
michael@0 258 mStateStack.RemoveElementAt(mStateStack.Length() - 1);
michael@0 259
michael@0 260 mDT = CurrentState().drawTarget;
michael@0 261
michael@0 262 ChangeTransform(CurrentState().transform, false);
michael@0 263 }
michael@0 264 }
michael@0 265
michael@0 266 // drawing
michael@0 267 void
michael@0 268 gfxContext::NewPath()
michael@0 269 {
michael@0 270 if (mCairo) {
michael@0 271 cairo_new_path(mCairo);
michael@0 272 } else {
michael@0 273 mPath = nullptr;
michael@0 274 mPathBuilder = nullptr;
michael@0 275 mPathIsRect = false;
michael@0 276 mTransformChanged = false;
michael@0 277 }
michael@0 278 }
michael@0 279
michael@0 280 void
michael@0 281 gfxContext::ClosePath()
michael@0 282 {
michael@0 283 if (mCairo) {
michael@0 284 cairo_close_path(mCairo);
michael@0 285 } else {
michael@0 286 EnsurePathBuilder();
michael@0 287 mPathBuilder->Close();
michael@0 288 }
michael@0 289 }
michael@0 290
michael@0 291 already_AddRefed<gfxPath> gfxContext::CopyPath()
michael@0 292 {
michael@0 293 nsRefPtr<gfxPath> path;
michael@0 294 if (mCairo) {
michael@0 295 path = new gfxPath(cairo_copy_path(mCairo));
michael@0 296 } else {
michael@0 297 EnsurePath();
michael@0 298 path = new gfxPath(mPath);
michael@0 299 }
michael@0 300 return path.forget();
michael@0 301 }
michael@0 302
michael@0 303 void gfxContext::SetPath(gfxPath* path)
michael@0 304 {
michael@0 305 if (mCairo) {
michael@0 306 cairo_new_path(mCairo);
michael@0 307 if (path->mPath->status == CAIRO_STATUS_SUCCESS && path->mPath->num_data != 0)
michael@0 308 cairo_append_path(mCairo, path->mPath);
michael@0 309 } else {
michael@0 310 MOZ_ASSERT(path->mMoz2DPath, "Can't mix cairo and azure paths!");
michael@0 311 MOZ_ASSERT(path->mMoz2DPath->GetBackendType() == mDT->GetType());
michael@0 312 mPath = path->mMoz2DPath;
michael@0 313 mPathBuilder = nullptr;
michael@0 314 mPathIsRect = false;
michael@0 315 mTransformChanged = false;
michael@0 316 }
michael@0 317 }
michael@0 318
michael@0 319 gfxPoint
michael@0 320 gfxContext::CurrentPoint()
michael@0 321 {
michael@0 322 if (mCairo) {
michael@0 323 double x, y;
michael@0 324 cairo_get_current_point(mCairo, &x, &y);
michael@0 325 return gfxPoint(x, y);
michael@0 326 } else {
michael@0 327 EnsurePathBuilder();
michael@0 328 return ThebesPoint(mPathBuilder->CurrentPoint());
michael@0 329 }
michael@0 330 }
michael@0 331
michael@0 332 void
michael@0 333 gfxContext::Stroke()
michael@0 334 {
michael@0 335 if (mCairo) {
michael@0 336 cairo_stroke_preserve(mCairo);
michael@0 337 } else {
michael@0 338 AzureState &state = CurrentState();
michael@0 339 if (mPathIsRect) {
michael@0 340 MOZ_ASSERT(!mTransformChanged);
michael@0 341
michael@0 342 mDT->StrokeRect(mRect, GeneralPattern(this),
michael@0 343 state.strokeOptions,
michael@0 344 DrawOptions(1.0f, GetOp(), state.aaMode));
michael@0 345 } else {
michael@0 346 EnsurePath();
michael@0 347
michael@0 348 mDT->Stroke(mPath, GeneralPattern(this), state.strokeOptions,
michael@0 349 DrawOptions(1.0f, GetOp(), state.aaMode));
michael@0 350 }
michael@0 351 }
michael@0 352 }
michael@0 353
michael@0 354 void
michael@0 355 gfxContext::Fill()
michael@0 356 {
michael@0 357 PROFILER_LABEL("gfxContext", "Fill");
michael@0 358 if (mCairo) {
michael@0 359 cairo_fill_preserve(mCairo);
michael@0 360 } else {
michael@0 361 FillAzure(1.0f);
michael@0 362 }
michael@0 363 }
michael@0 364
michael@0 365 void
michael@0 366 gfxContext::FillWithOpacity(gfxFloat aOpacity)
michael@0 367 {
michael@0 368 if (mCairo) {
michael@0 369 // This method exists in the hope that one day cairo gets a direct
michael@0 370 // API for this, and then we would change this method to use that
michael@0 371 // API instead.
michael@0 372 if (aOpacity != 1.0) {
michael@0 373 gfxContextAutoSaveRestore saveRestore(this);
michael@0 374 Clip();
michael@0 375 Paint(aOpacity);
michael@0 376 } else {
michael@0 377 Fill();
michael@0 378 }
michael@0 379 } else {
michael@0 380 FillAzure(Float(aOpacity));
michael@0 381 }
michael@0 382 }
michael@0 383
michael@0 384 void
michael@0 385 gfxContext::MoveTo(const gfxPoint& pt)
michael@0 386 {
michael@0 387 if (mCairo) {
michael@0 388 cairo_move_to(mCairo, pt.x, pt.y);
michael@0 389 } else {
michael@0 390 EnsurePathBuilder();
michael@0 391 mPathBuilder->MoveTo(ToPoint(pt));
michael@0 392 }
michael@0 393 }
michael@0 394
michael@0 395 void
michael@0 396 gfxContext::NewSubPath()
michael@0 397 {
michael@0 398 if (mCairo) {
michael@0 399 cairo_new_sub_path(mCairo);
michael@0 400 } else {
michael@0 401 // XXX - This has no users, we should kill it, it should be equivelant to a
michael@0 402 // MoveTo to the path's current point.
michael@0 403 }
michael@0 404 }
michael@0 405
michael@0 406 void
michael@0 407 gfxContext::LineTo(const gfxPoint& pt)
michael@0 408 {
michael@0 409 if (mCairo) {
michael@0 410 cairo_line_to(mCairo, pt.x, pt.y);
michael@0 411 } else {
michael@0 412 EnsurePathBuilder();
michael@0 413 mPathBuilder->LineTo(ToPoint(pt));
michael@0 414 }
michael@0 415 }
michael@0 416
michael@0 417 void
michael@0 418 gfxContext::CurveTo(const gfxPoint& pt1, const gfxPoint& pt2, const gfxPoint& pt3)
michael@0 419 {
michael@0 420 if (mCairo) {
michael@0 421 cairo_curve_to(mCairo, pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y);
michael@0 422 } else {
michael@0 423 EnsurePathBuilder();
michael@0 424 mPathBuilder->BezierTo(ToPoint(pt1), ToPoint(pt2), ToPoint(pt3));
michael@0 425 }
michael@0 426 }
michael@0 427
michael@0 428 void
michael@0 429 gfxContext::QuadraticCurveTo(const gfxPoint& pt1, const gfxPoint& pt2)
michael@0 430 {
michael@0 431 if (mCairo) {
michael@0 432 double cx, cy;
michael@0 433 cairo_get_current_point(mCairo, &cx, &cy);
michael@0 434 cairo_curve_to(mCairo,
michael@0 435 (cx + pt1.x * 2.0) / 3.0,
michael@0 436 (cy + pt1.y * 2.0) / 3.0,
michael@0 437 (pt1.x * 2.0 + pt2.x) / 3.0,
michael@0 438 (pt1.y * 2.0 + pt2.y) / 3.0,
michael@0 439 pt2.x,
michael@0 440 pt2.y);
michael@0 441 } else {
michael@0 442 EnsurePathBuilder();
michael@0 443 mPathBuilder->QuadraticBezierTo(ToPoint(pt1), ToPoint(pt2));
michael@0 444 }
michael@0 445 }
michael@0 446
michael@0 447 void
michael@0 448 gfxContext::Arc(const gfxPoint& center, gfxFloat radius,
michael@0 449 gfxFloat angle1, gfxFloat angle2)
michael@0 450 {
michael@0 451 if (mCairo) {
michael@0 452 cairo_arc(mCairo, center.x, center.y, radius, angle1, angle2);
michael@0 453 } else {
michael@0 454 EnsurePathBuilder();
michael@0 455 mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle1), Float(angle2));
michael@0 456 }
michael@0 457 }
michael@0 458
michael@0 459 void
michael@0 460 gfxContext::NegativeArc(const gfxPoint& center, gfxFloat radius,
michael@0 461 gfxFloat angle1, gfxFloat angle2)
michael@0 462 {
michael@0 463 if (mCairo) {
michael@0 464 cairo_arc_negative(mCairo, center.x, center.y, radius, angle1, angle2);
michael@0 465 } else {
michael@0 466 EnsurePathBuilder();
michael@0 467 mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle2), Float(angle1));
michael@0 468 }
michael@0 469 }
michael@0 470
michael@0 471 void
michael@0 472 gfxContext::Line(const gfxPoint& start, const gfxPoint& end)
michael@0 473 {
michael@0 474 if (mCairo) {
michael@0 475 MoveTo(start);
michael@0 476 LineTo(end);
michael@0 477 } else {
michael@0 478 EnsurePathBuilder();
michael@0 479 mPathBuilder->MoveTo(ToPoint(start));
michael@0 480 mPathBuilder->LineTo(ToPoint(end));
michael@0 481 }
michael@0 482 }
michael@0 483
michael@0 484 // XXX snapToPixels is only valid when snapping for filled
michael@0 485 // rectangles and for even-width stroked rectangles.
michael@0 486 // For odd-width stroked rectangles, we need to offset x/y by
michael@0 487 // 0.5...
michael@0 488 void
michael@0 489 gfxContext::Rectangle(const gfxRect& rect, bool snapToPixels)
michael@0 490 {
michael@0 491 if (mCairo) {
michael@0 492 if (snapToPixels) {
michael@0 493 gfxRect snappedRect(rect);
michael@0 494
michael@0 495 if (UserToDevicePixelSnapped(snappedRect, true))
michael@0 496 {
michael@0 497 cairo_matrix_t mat;
michael@0 498 cairo_get_matrix(mCairo, &mat);
michael@0 499 cairo_identity_matrix(mCairo);
michael@0 500 Rectangle(snappedRect);
michael@0 501 cairo_set_matrix(mCairo, &mat);
michael@0 502
michael@0 503 return;
michael@0 504 }
michael@0 505 }
michael@0 506
michael@0 507 cairo_rectangle(mCairo, rect.X(), rect.Y(), rect.Width(), rect.Height());
michael@0 508 } else {
michael@0 509 Rect rec = ToRect(rect);
michael@0 510
michael@0 511 if (snapToPixels) {
michael@0 512 gfxRect newRect(rect);
michael@0 513 if (UserToDevicePixelSnapped(newRect, true)) {
michael@0 514 gfxMatrix mat = ThebesMatrix(mTransform);
michael@0 515 mat.Invert();
michael@0 516
michael@0 517 // We need the user space rect.
michael@0 518 rec = ToRect(mat.TransformBounds(newRect));
michael@0 519 }
michael@0 520 }
michael@0 521
michael@0 522 if (!mPathBuilder && !mPathIsRect) {
michael@0 523 mPathIsRect = true;
michael@0 524 mRect = rec;
michael@0 525 return;
michael@0 526 }
michael@0 527
michael@0 528 EnsurePathBuilder();
michael@0 529
michael@0 530 mPathBuilder->MoveTo(rec.TopLeft());
michael@0 531 mPathBuilder->LineTo(rec.TopRight());
michael@0 532 mPathBuilder->LineTo(rec.BottomRight());
michael@0 533 mPathBuilder->LineTo(rec.BottomLeft());
michael@0 534 mPathBuilder->Close();
michael@0 535 }
michael@0 536 }
michael@0 537
michael@0 538 void
michael@0 539 gfxContext::Ellipse(const gfxPoint& center, const gfxSize& dimensions)
michael@0 540 {
michael@0 541 gfxSize halfDim = dimensions / 2.0;
michael@0 542 gfxRect r(center - gfxPoint(halfDim.width, halfDim.height), dimensions);
michael@0 543 gfxCornerSizes c(halfDim, halfDim, halfDim, halfDim);
michael@0 544
michael@0 545 RoundedRectangle (r, c);
michael@0 546 }
michael@0 547
michael@0 548 void
michael@0 549 gfxContext::Polygon(const gfxPoint *points, uint32_t numPoints)
michael@0 550 {
michael@0 551 if (mCairo) {
michael@0 552 if (numPoints == 0)
michael@0 553 return;
michael@0 554
michael@0 555 cairo_move_to(mCairo, points[0].x, points[0].y);
michael@0 556 for (uint32_t i = 1; i < numPoints; ++i) {
michael@0 557 cairo_line_to(mCairo, points[i].x, points[i].y);
michael@0 558 }
michael@0 559 } else {
michael@0 560 if (numPoints == 0) {
michael@0 561 return;
michael@0 562 }
michael@0 563
michael@0 564 EnsurePathBuilder();
michael@0 565
michael@0 566 mPathBuilder->MoveTo(ToPoint(points[0]));
michael@0 567 for (uint32_t i = 1; i < numPoints; i++) {
michael@0 568 mPathBuilder->LineTo(ToPoint(points[i]));
michael@0 569 }
michael@0 570 }
michael@0 571 }
michael@0 572
michael@0 573 void
michael@0 574 gfxContext::DrawSurface(gfxASurface *surface, const gfxSize& size)
michael@0 575 {
michael@0 576 if (mCairo) {
michael@0 577 cairo_save(mCairo);
michael@0 578 cairo_set_source_surface(mCairo, surface->CairoSurface(), 0, 0);
michael@0 579 cairo_new_path(mCairo);
michael@0 580
michael@0 581 // pixel-snap this
michael@0 582 Rectangle(gfxRect(gfxPoint(0.0, 0.0), size), true);
michael@0 583
michael@0 584 cairo_fill(mCairo);
michael@0 585 cairo_restore(mCairo);
michael@0 586 } else {
michael@0 587 // Lifetime needs to be limited here since we may wrap surface's data.
michael@0 588 RefPtr<SourceSurface> surf =
michael@0 589 gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
michael@0 590
michael@0 591 if (!surf) {
michael@0 592 return;
michael@0 593 }
michael@0 594
michael@0 595 Rect rect(0, 0, Float(size.width), Float(size.height));
michael@0 596 rect.Intersect(Rect(0, 0, Float(surf->GetSize().width), Float(surf->GetSize().height)));
michael@0 597
michael@0 598 // XXX - Should fix pixel snapping.
michael@0 599 mDT->DrawSurface(surf, rect, rect);
michael@0 600 }
michael@0 601 }
michael@0 602
michael@0 603 // transform stuff
michael@0 604 void
michael@0 605 gfxContext::Translate(const gfxPoint& pt)
michael@0 606 {
michael@0 607 if (mCairo) {
michael@0 608 cairo_translate(mCairo, pt.x, pt.y);
michael@0 609 } else {
michael@0 610 Matrix newMatrix = mTransform;
michael@0 611
michael@0 612 ChangeTransform(newMatrix.Translate(Float(pt.x), Float(pt.y)));
michael@0 613 }
michael@0 614 }
michael@0 615
michael@0 616 void
michael@0 617 gfxContext::Scale(gfxFloat x, gfxFloat y)
michael@0 618 {
michael@0 619 if (mCairo) {
michael@0 620 cairo_scale(mCairo, x, y);
michael@0 621 } else {
michael@0 622 Matrix newMatrix = mTransform;
michael@0 623
michael@0 624 ChangeTransform(newMatrix.Scale(Float(x), Float(y)));
michael@0 625 }
michael@0 626 }
michael@0 627
michael@0 628 void
michael@0 629 gfxContext::Rotate(gfxFloat angle)
michael@0 630 {
michael@0 631 if (mCairo) {
michael@0 632 cairo_rotate(mCairo, angle);
michael@0 633 } else {
michael@0 634 Matrix rotation = Matrix::Rotation(Float(angle));
michael@0 635 ChangeTransform(rotation * mTransform);
michael@0 636 }
michael@0 637 }
michael@0 638
michael@0 639 void
michael@0 640 gfxContext::Multiply(const gfxMatrix& matrix)
michael@0 641 {
michael@0 642 if (mCairo) {
michael@0 643 const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
michael@0 644 cairo_transform(mCairo, &mat);
michael@0 645 } else {
michael@0 646 ChangeTransform(ToMatrix(matrix) * mTransform);
michael@0 647 }
michael@0 648 }
michael@0 649
michael@0 650 void
michael@0 651 gfxContext::MultiplyAndNudgeToIntegers(const gfxMatrix& matrix)
michael@0 652 {
michael@0 653 if (mCairo) {
michael@0 654 const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
michael@0 655 cairo_transform(mCairo, &mat);
michael@0 656 // XXX nudging to integers not currently supported for Thebes
michael@0 657 } else {
michael@0 658 Matrix transform = ToMatrix(matrix) * mTransform;
michael@0 659 transform.NudgeToIntegers();
michael@0 660 ChangeTransform(transform);
michael@0 661 }
michael@0 662 }
michael@0 663
michael@0 664 void
michael@0 665 gfxContext::SetMatrix(const gfxMatrix& matrix)
michael@0 666 {
michael@0 667 if (mCairo) {
michael@0 668 const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
michael@0 669 cairo_set_matrix(mCairo, &mat);
michael@0 670 } else {
michael@0 671 ChangeTransform(ToMatrix(matrix));
michael@0 672 }
michael@0 673 }
michael@0 674
michael@0 675 void
michael@0 676 gfxContext::IdentityMatrix()
michael@0 677 {
michael@0 678 if (mCairo) {
michael@0 679 cairo_identity_matrix(mCairo);
michael@0 680 } else {
michael@0 681 ChangeTransform(Matrix());
michael@0 682 }
michael@0 683 }
michael@0 684
michael@0 685 gfxMatrix
michael@0 686 gfxContext::CurrentMatrix() const
michael@0 687 {
michael@0 688 if (mCairo) {
michael@0 689 cairo_matrix_t mat;
michael@0 690 cairo_get_matrix(mCairo, &mat);
michael@0 691 return gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat));
michael@0 692 } else {
michael@0 693 return ThebesMatrix(mTransform);
michael@0 694 }
michael@0 695 }
michael@0 696
michael@0 697 void
michael@0 698 gfxContext::NudgeCurrentMatrixToIntegers()
michael@0 699 {
michael@0 700 if (mCairo) {
michael@0 701 cairo_matrix_t mat;
michael@0 702 cairo_get_matrix(mCairo, &mat);
michael@0 703 gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat)).NudgeToIntegers();
michael@0 704 cairo_set_matrix(mCairo, &mat);
michael@0 705 } else {
michael@0 706 gfxMatrix matrix = ThebesMatrix(mTransform);
michael@0 707 matrix.NudgeToIntegers();
michael@0 708 ChangeTransform(ToMatrix(matrix));
michael@0 709 }
michael@0 710 }
michael@0 711
michael@0 712 gfxPoint
michael@0 713 gfxContext::DeviceToUser(const gfxPoint& point) const
michael@0 714 {
michael@0 715 if (mCairo) {
michael@0 716 gfxPoint ret = point;
michael@0 717 cairo_device_to_user(mCairo, &ret.x, &ret.y);
michael@0 718 return ret;
michael@0 719 } else {
michael@0 720 Matrix matrix = mTransform;
michael@0 721
michael@0 722 matrix.Invert();
michael@0 723
michael@0 724 return ThebesPoint(matrix * ToPoint(point));
michael@0 725 }
michael@0 726 }
michael@0 727
michael@0 728 gfxSize
michael@0 729 gfxContext::DeviceToUser(const gfxSize& size) const
michael@0 730 {
michael@0 731 if (mCairo) {
michael@0 732 gfxSize ret = size;
michael@0 733 cairo_device_to_user_distance(mCairo, &ret.width, &ret.height);
michael@0 734 return ret;
michael@0 735 } else {
michael@0 736 Matrix matrix = mTransform;
michael@0 737
michael@0 738 matrix.Invert();
michael@0 739
michael@0 740 return ThebesSize(matrix * ToSize(size));
michael@0 741 }
michael@0 742 }
michael@0 743
michael@0 744 gfxRect
michael@0 745 gfxContext::DeviceToUser(const gfxRect& rect) const
michael@0 746 {
michael@0 747 if (mCairo) {
michael@0 748 gfxRect ret = rect;
michael@0 749 cairo_device_to_user(mCairo, &ret.x, &ret.y);
michael@0 750 cairo_device_to_user_distance(mCairo, &ret.width, &ret.height);
michael@0 751 return ret;
michael@0 752 } else {
michael@0 753 Matrix matrix = mTransform;
michael@0 754
michael@0 755 matrix.Invert();
michael@0 756
michael@0 757 return ThebesRect(matrix.TransformBounds(ToRect(rect)));
michael@0 758 }
michael@0 759 }
michael@0 760
michael@0 761 gfxPoint
michael@0 762 gfxContext::UserToDevice(const gfxPoint& point) const
michael@0 763 {
michael@0 764 if (mCairo) {
michael@0 765 gfxPoint ret = point;
michael@0 766 cairo_user_to_device(mCairo, &ret.x, &ret.y);
michael@0 767 return ret;
michael@0 768 } else {
michael@0 769 return ThebesPoint(mTransform * ToPoint(point));
michael@0 770 }
michael@0 771 }
michael@0 772
michael@0 773 gfxSize
michael@0 774 gfxContext::UserToDevice(const gfxSize& size) const
michael@0 775 {
michael@0 776 if (mCairo) {
michael@0 777 gfxSize ret = size;
michael@0 778 cairo_user_to_device_distance(mCairo, &ret.width, &ret.height);
michael@0 779 return ret;
michael@0 780 } else {
michael@0 781 const Matrix &matrix = mTransform;
michael@0 782
michael@0 783 gfxSize newSize;
michael@0 784 newSize.width = size.width * matrix._11 + size.height * matrix._12;
michael@0 785 newSize.height = size.width * matrix._21 + size.height * matrix._22;
michael@0 786 return newSize;
michael@0 787 }
michael@0 788 }
michael@0 789
michael@0 790 gfxRect
michael@0 791 gfxContext::UserToDevice(const gfxRect& rect) const
michael@0 792 {
michael@0 793 if (mCairo) {
michael@0 794 double xmin = rect.X(), ymin = rect.Y(), xmax = rect.XMost(), ymax = rect.YMost();
michael@0 795
michael@0 796 double x[3], y[3];
michael@0 797 x[0] = xmin; y[0] = ymax;
michael@0 798 x[1] = xmax; y[1] = ymax;
michael@0 799 x[2] = xmax; y[2] = ymin;
michael@0 800
michael@0 801 cairo_user_to_device(mCairo, &xmin, &ymin);
michael@0 802 xmax = xmin;
michael@0 803 ymax = ymin;
michael@0 804 for (int i = 0; i < 3; i++) {
michael@0 805 cairo_user_to_device(mCairo, &x[i], &y[i]);
michael@0 806 xmin = std::min(xmin, x[i]);
michael@0 807 xmax = std::max(xmax, x[i]);
michael@0 808 ymin = std::min(ymin, y[i]);
michael@0 809 ymax = std::max(ymax, y[i]);
michael@0 810 }
michael@0 811
michael@0 812 return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
michael@0 813 } else {
michael@0 814 const Matrix &matrix = mTransform;
michael@0 815 return ThebesRect(matrix.TransformBounds(ToRect(rect)));
michael@0 816 }
michael@0 817 }
michael@0 818
michael@0 819 bool
michael@0 820 gfxContext::UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale) const
michael@0 821 {
michael@0 822 if (GetFlags() & FLAG_DISABLE_SNAPPING)
michael@0 823 return false;
michael@0 824
michael@0 825 // if we're not at 1.0 scale, don't snap, unless we're
michael@0 826 // ignoring the scale. If we're not -just- a scale,
michael@0 827 // never snap.
michael@0 828 const gfxFloat epsilon = 0.0000001;
michael@0 829 #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
michael@0 830 if (mCairo) {
michael@0 831 cairo_matrix_t mat;
michael@0 832 cairo_get_matrix(mCairo, &mat);
michael@0 833 if (!ignoreScale &&
michael@0 834 (!WITHIN_E(mat.xx,1.0) || !WITHIN_E(mat.yy,1.0) ||
michael@0 835 !WITHIN_E(mat.xy,0.0) || !WITHIN_E(mat.yx,0.0)))
michael@0 836 return false;
michael@0 837 } else {
michael@0 838 Matrix mat = mTransform;
michael@0 839 if (!ignoreScale &&
michael@0 840 (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
michael@0 841 !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
michael@0 842 return false;
michael@0 843 }
michael@0 844 #undef WITHIN_E
michael@0 845
michael@0 846 gfxPoint p1 = UserToDevice(rect.TopLeft());
michael@0 847 gfxPoint p2 = UserToDevice(rect.TopRight());
michael@0 848 gfxPoint p3 = UserToDevice(rect.BottomRight());
michael@0 849
michael@0 850 // Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
michael@0 851 // two opposite corners define the entire rectangle. So check if
michael@0 852 // the axis-aligned rectangle with opposite corners p1 and p3
michael@0 853 // define an axis-aligned rectangle whose other corners are p2 and p4.
michael@0 854 // We actually only need to check one of p2 and p4, since an affine
michael@0 855 // transform maps parallelograms to parallelograms.
michael@0 856 if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) {
michael@0 857 p1.Round();
michael@0 858 p3.Round();
michael@0 859
michael@0 860 rect.MoveTo(gfxPoint(std::min(p1.x, p3.x), std::min(p1.y, p3.y)));
michael@0 861 rect.SizeTo(gfxSize(std::max(p1.x, p3.x) - rect.X(),
michael@0 862 std::max(p1.y, p3.y) - rect.Y()));
michael@0 863 return true;
michael@0 864 }
michael@0 865
michael@0 866 return false;
michael@0 867 }
michael@0 868
michael@0 869 bool
michael@0 870 gfxContext::UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale) const
michael@0 871 {
michael@0 872 if (GetFlags() & FLAG_DISABLE_SNAPPING)
michael@0 873 return false;
michael@0 874
michael@0 875 // if we're not at 1.0 scale, don't snap, unless we're
michael@0 876 // ignoring the scale. If we're not -just- a scale,
michael@0 877 // never snap.
michael@0 878 const gfxFloat epsilon = 0.0000001;
michael@0 879 #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
michael@0 880 if (mCairo) {
michael@0 881 cairo_matrix_t mat;
michael@0 882 cairo_get_matrix(mCairo, &mat);
michael@0 883 if (!ignoreScale &&
michael@0 884 (!WITHIN_E(mat.xx,1.0) || !WITHIN_E(mat.yy,1.0) ||
michael@0 885 !WITHIN_E(mat.xy,0.0) || !WITHIN_E(mat.yx,0.0)))
michael@0 886 return false;
michael@0 887 } else {
michael@0 888 Matrix mat = mTransform;
michael@0 889 if (!ignoreScale &&
michael@0 890 (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
michael@0 891 !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
michael@0 892 return false;
michael@0 893 }
michael@0 894 #undef WITHIN_E
michael@0 895
michael@0 896 pt = UserToDevice(pt);
michael@0 897 pt.Round();
michael@0 898 return true;
michael@0 899 }
michael@0 900
michael@0 901 void
michael@0 902 gfxContext::PixelSnappedRectangleAndSetPattern(const gfxRect& rect,
michael@0 903 gfxPattern *pattern)
michael@0 904 {
michael@0 905 gfxRect r(rect);
michael@0 906
michael@0 907 // Bob attempts to pixel-snap the rectangle, and returns true if
michael@0 908 // the snapping succeeds. If it does, we need to set up an
michael@0 909 // identity matrix, because the rectangle given back is in device
michael@0 910 // coordinates.
michael@0 911 //
michael@0 912 // We then have to call a translate to dr.pos afterwards, to make
michael@0 913 // sure the image lines up in the right place with our pixel
michael@0 914 // snapped rectangle.
michael@0 915 //
michael@0 916 // If snapping wasn't successful, we just translate to where the
michael@0 917 // pattern would normally start (in app coordinates) and do the
michael@0 918 // same thing.
michael@0 919 Rectangle(r, true);
michael@0 920 SetPattern(pattern);
michael@0 921 }
michael@0 922
michael@0 923 void
michael@0 924 gfxContext::SetAntialiasMode(AntialiasMode mode)
michael@0 925 {
michael@0 926 if (mCairo) {
michael@0 927 if (mode == MODE_ALIASED) {
michael@0 928 cairo_set_antialias(mCairo, CAIRO_ANTIALIAS_NONE);
michael@0 929 } else if (mode == MODE_COVERAGE) {
michael@0 930 cairo_set_antialias(mCairo, CAIRO_ANTIALIAS_DEFAULT);
michael@0 931 }
michael@0 932 } else {
michael@0 933 if (mode == MODE_ALIASED) {
michael@0 934 CurrentState().aaMode = gfx::AntialiasMode::NONE;
michael@0 935 } else if (mode == MODE_COVERAGE) {
michael@0 936 CurrentState().aaMode = gfx::AntialiasMode::SUBPIXEL;
michael@0 937 }
michael@0 938 }
michael@0 939 }
michael@0 940
michael@0 941 gfxContext::AntialiasMode
michael@0 942 gfxContext::CurrentAntialiasMode() const
michael@0 943 {
michael@0 944 if (mCairo) {
michael@0 945 cairo_antialias_t aa = cairo_get_antialias(mCairo);
michael@0 946 if (aa == CAIRO_ANTIALIAS_NONE)
michael@0 947 return MODE_ALIASED;
michael@0 948 return MODE_COVERAGE;
michael@0 949 } else {
michael@0 950 if (CurrentState().aaMode == gfx::AntialiasMode::NONE) {
michael@0 951 return MODE_ALIASED;
michael@0 952 }
michael@0 953 return MODE_COVERAGE;
michael@0 954 }
michael@0 955 }
michael@0 956
michael@0 957 void
michael@0 958 gfxContext::SetDash(gfxLineType ltype)
michael@0 959 {
michael@0 960 static double dash[] = {5.0, 5.0};
michael@0 961 static double dot[] = {1.0, 1.0};
michael@0 962
michael@0 963 switch (ltype) {
michael@0 964 case gfxLineDashed:
michael@0 965 SetDash(dash, 2, 0.0);
michael@0 966 break;
michael@0 967 case gfxLineDotted:
michael@0 968 SetDash(dot, 2, 0.0);
michael@0 969 break;
michael@0 970 case gfxLineSolid:
michael@0 971 default:
michael@0 972 SetDash(nullptr, 0, 0.0);
michael@0 973 break;
michael@0 974 }
michael@0 975 }
michael@0 976
michael@0 977 void
michael@0 978 gfxContext::SetDash(gfxFloat *dashes, int ndash, gfxFloat offset)
michael@0 979 {
michael@0 980 if (mCairo) {
michael@0 981 cairo_set_dash(mCairo, dashes, ndash, offset);
michael@0 982 } else {
michael@0 983 AzureState &state = CurrentState();
michael@0 984
michael@0 985 state.dashPattern.SetLength(ndash);
michael@0 986 for (int i = 0; i < ndash; i++) {
michael@0 987 state.dashPattern[i] = Float(dashes[i]);
michael@0 988 }
michael@0 989 state.strokeOptions.mDashLength = ndash;
michael@0 990 state.strokeOptions.mDashOffset = Float(offset);
michael@0 991 state.strokeOptions.mDashPattern = ndash ? state.dashPattern.Elements()
michael@0 992 : nullptr;
michael@0 993 }
michael@0 994 }
michael@0 995
michael@0 996 bool
michael@0 997 gfxContext::CurrentDash(FallibleTArray<gfxFloat>& dashes, gfxFloat* offset) const
michael@0 998 {
michael@0 999 if (mCairo) {
michael@0 1000 int count = cairo_get_dash_count(mCairo);
michael@0 1001 if (count <= 0 || !dashes.SetLength(count)) {
michael@0 1002 return false;
michael@0 1003 }
michael@0 1004 cairo_get_dash(mCairo, dashes.Elements(), offset);
michael@0 1005 return true;
michael@0 1006 } else {
michael@0 1007 const AzureState &state = CurrentState();
michael@0 1008 int count = state.strokeOptions.mDashLength;
michael@0 1009
michael@0 1010 if (count <= 0 || !dashes.SetLength(count)) {
michael@0 1011 return false;
michael@0 1012 }
michael@0 1013
michael@0 1014 for (int i = 0; i < count; i++) {
michael@0 1015 dashes[i] = state.dashPattern[i];
michael@0 1016 }
michael@0 1017
michael@0 1018 *offset = state.strokeOptions.mDashOffset;
michael@0 1019
michael@0 1020 return true;
michael@0 1021 }
michael@0 1022 }
michael@0 1023
michael@0 1024 gfxFloat
michael@0 1025 gfxContext::CurrentDashOffset() const
michael@0 1026 {
michael@0 1027 if (mCairo) {
michael@0 1028 if (cairo_get_dash_count(mCairo) <= 0) {
michael@0 1029 return 0.0;
michael@0 1030 }
michael@0 1031 gfxFloat offset;
michael@0 1032 cairo_get_dash(mCairo, nullptr, &offset);
michael@0 1033 return offset;
michael@0 1034 } else {
michael@0 1035 return CurrentState().strokeOptions.mDashOffset;
michael@0 1036 }
michael@0 1037 }
michael@0 1038
michael@0 1039 void
michael@0 1040 gfxContext::SetLineWidth(gfxFloat width)
michael@0 1041 {
michael@0 1042 if (mCairo) {
michael@0 1043 cairo_set_line_width(mCairo, width);
michael@0 1044 } else {
michael@0 1045 CurrentState().strokeOptions.mLineWidth = Float(width);
michael@0 1046 }
michael@0 1047 }
michael@0 1048
michael@0 1049 gfxFloat
michael@0 1050 gfxContext::CurrentLineWidth() const
michael@0 1051 {
michael@0 1052 if (mCairo) {
michael@0 1053 return cairo_get_line_width(mCairo);
michael@0 1054 } else {
michael@0 1055 return CurrentState().strokeOptions.mLineWidth;
michael@0 1056 }
michael@0 1057 }
michael@0 1058
michael@0 1059 void
michael@0 1060 gfxContext::SetOperator(GraphicsOperator op)
michael@0 1061 {
michael@0 1062 if (mCairo) {
michael@0 1063 if (mFlags & FLAG_SIMPLIFY_OPERATORS) {
michael@0 1064 if (op != OPERATOR_SOURCE &&
michael@0 1065 op != OPERATOR_CLEAR &&
michael@0 1066 op != OPERATOR_OVER)
michael@0 1067 op = OPERATOR_OVER;
michael@0 1068 }
michael@0 1069
michael@0 1070 cairo_set_operator(mCairo, (cairo_operator_t)op);
michael@0 1071 } else {
michael@0 1072 if (op == OPERATOR_CLEAR) {
michael@0 1073 CurrentState().opIsClear = true;
michael@0 1074 return;
michael@0 1075 }
michael@0 1076 CurrentState().opIsClear = false;
michael@0 1077 CurrentState().op = CompositionOpForOp(op);
michael@0 1078 }
michael@0 1079 }
michael@0 1080
michael@0 1081 gfxContext::GraphicsOperator
michael@0 1082 gfxContext::CurrentOperator() const
michael@0 1083 {
michael@0 1084 if (mCairo) {
michael@0 1085 return (GraphicsOperator)cairo_get_operator(mCairo);
michael@0 1086 } else {
michael@0 1087 return ThebesOp(CurrentState().op);
michael@0 1088 }
michael@0 1089 }
michael@0 1090
michael@0 1091 void
michael@0 1092 gfxContext::SetLineCap(GraphicsLineCap cap)
michael@0 1093 {
michael@0 1094 if (mCairo) {
michael@0 1095 cairo_set_line_cap(mCairo, (cairo_line_cap_t)cap);
michael@0 1096 } else {
michael@0 1097 CurrentState().strokeOptions.mLineCap = ToCapStyle(cap);
michael@0 1098 }
michael@0 1099 }
michael@0 1100
michael@0 1101 gfxContext::GraphicsLineCap
michael@0 1102 gfxContext::CurrentLineCap() const
michael@0 1103 {
michael@0 1104 if (mCairo) {
michael@0 1105 return (GraphicsLineCap)cairo_get_line_cap(mCairo);
michael@0 1106 } else {
michael@0 1107 return ThebesLineCap(CurrentState().strokeOptions.mLineCap);
michael@0 1108 }
michael@0 1109 }
michael@0 1110
michael@0 1111 void
michael@0 1112 gfxContext::SetLineJoin(GraphicsLineJoin join)
michael@0 1113 {
michael@0 1114 if (mCairo) {
michael@0 1115 cairo_set_line_join(mCairo, (cairo_line_join_t)join);
michael@0 1116 } else {
michael@0 1117 CurrentState().strokeOptions.mLineJoin = ToJoinStyle(join);
michael@0 1118 }
michael@0 1119 }
michael@0 1120
michael@0 1121 gfxContext::GraphicsLineJoin
michael@0 1122 gfxContext::CurrentLineJoin() const
michael@0 1123 {
michael@0 1124 if (mCairo) {
michael@0 1125 return (GraphicsLineJoin)cairo_get_line_join(mCairo);
michael@0 1126 } else {
michael@0 1127 return ThebesLineJoin(CurrentState().strokeOptions.mLineJoin);
michael@0 1128 }
michael@0 1129 }
michael@0 1130
michael@0 1131 void
michael@0 1132 gfxContext::SetMiterLimit(gfxFloat limit)
michael@0 1133 {
michael@0 1134 if (mCairo) {
michael@0 1135 cairo_set_miter_limit(mCairo, limit);
michael@0 1136 } else {
michael@0 1137 CurrentState().strokeOptions.mMiterLimit = Float(limit);
michael@0 1138 }
michael@0 1139 }
michael@0 1140
michael@0 1141 gfxFloat
michael@0 1142 gfxContext::CurrentMiterLimit() const
michael@0 1143 {
michael@0 1144 if (mCairo) {
michael@0 1145 return cairo_get_miter_limit(mCairo);
michael@0 1146 } else {
michael@0 1147 return CurrentState().strokeOptions.mMiterLimit;
michael@0 1148 }
michael@0 1149 }
michael@0 1150
michael@0 1151 void
michael@0 1152 gfxContext::SetFillRule(FillRule rule)
michael@0 1153 {
michael@0 1154 if (mCairo) {
michael@0 1155 cairo_set_fill_rule(mCairo, (cairo_fill_rule_t)rule);
michael@0 1156 } else {
michael@0 1157 CurrentState().fillRule = rule == FILL_RULE_WINDING ? gfx::FillRule::FILL_WINDING : gfx::FillRule::FILL_EVEN_ODD;
michael@0 1158 }
michael@0 1159 }
michael@0 1160
michael@0 1161 gfxContext::FillRule
michael@0 1162 gfxContext::CurrentFillRule() const
michael@0 1163 {
michael@0 1164 if (mCairo) {
michael@0 1165 return (FillRule)cairo_get_fill_rule(mCairo);
michael@0 1166 } else {
michael@0 1167 return FILL_RULE_WINDING;
michael@0 1168 }
michael@0 1169 }
michael@0 1170
michael@0 1171 // clipping
michael@0 1172 void
michael@0 1173 gfxContext::Clip(const gfxRect& rect)
michael@0 1174 {
michael@0 1175 if (mCairo) {
michael@0 1176 cairo_new_path(mCairo);
michael@0 1177 cairo_rectangle(mCairo, rect.X(), rect.Y(), rect.Width(), rect.Height());
michael@0 1178 cairo_clip(mCairo);
michael@0 1179 } else {
michael@0 1180 AzureState::PushedClip clip = { nullptr, ToRect(rect), mTransform };
michael@0 1181 CurrentState().pushedClips.AppendElement(clip);
michael@0 1182 mDT->PushClipRect(ToRect(rect));
michael@0 1183 NewPath();
michael@0 1184 }
michael@0 1185 }
michael@0 1186
michael@0 1187 void
michael@0 1188 gfxContext::Clip()
michael@0 1189 {
michael@0 1190 if (mCairo) {
michael@0 1191 cairo_clip_preserve(mCairo);
michael@0 1192 } else {
michael@0 1193 if (mPathIsRect) {
michael@0 1194 MOZ_ASSERT(!mTransformChanged);
michael@0 1195
michael@0 1196 AzureState::PushedClip clip = { nullptr, mRect, mTransform };
michael@0 1197 CurrentState().pushedClips.AppendElement(clip);
michael@0 1198 mDT->PushClipRect(mRect);
michael@0 1199 } else {
michael@0 1200 EnsurePath();
michael@0 1201 mDT->PushClip(mPath);
michael@0 1202 AzureState::PushedClip clip = { mPath, Rect(), mTransform };
michael@0 1203 CurrentState().pushedClips.AppendElement(clip);
michael@0 1204 }
michael@0 1205 }
michael@0 1206 }
michael@0 1207
michael@0 1208 void
michael@0 1209 gfxContext::ResetClip()
michael@0 1210 {
michael@0 1211 if (mCairo) {
michael@0 1212 cairo_reset_clip(mCairo);
michael@0 1213 } else {
michael@0 1214 for (int i = mStateStack.Length() - 1; i >= 0; i--) {
michael@0 1215 for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
michael@0 1216 mDT->PopClip();
michael@0 1217 }
michael@0 1218
michael@0 1219 if (mStateStack[i].clipWasReset) {
michael@0 1220 break;
michael@0 1221 }
michael@0 1222 }
michael@0 1223 CurrentState().pushedClips.Clear();
michael@0 1224 CurrentState().clipWasReset = true;
michael@0 1225 }
michael@0 1226 }
michael@0 1227
michael@0 1228 void
michael@0 1229 gfxContext::UpdateSurfaceClip()
michael@0 1230 {
michael@0 1231 if (mCairo) {
michael@0 1232 NewPath();
michael@0 1233 // we paint an empty rectangle to ensure the clip is propagated to
michael@0 1234 // the destination surface
michael@0 1235 SetDeviceColor(gfxRGBA(0,0,0,0));
michael@0 1236 Rectangle(gfxRect(0,1,1,0));
michael@0 1237 Fill();
michael@0 1238 }
michael@0 1239 }
michael@0 1240
michael@0 1241 gfxRect
michael@0 1242 gfxContext::GetClipExtents()
michael@0 1243 {
michael@0 1244 if (mCairo) {
michael@0 1245 double xmin, ymin, xmax, ymax;
michael@0 1246 cairo_clip_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
michael@0 1247 return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
michael@0 1248 } else {
michael@0 1249 Rect rect = GetAzureDeviceSpaceClipBounds();
michael@0 1250
michael@0 1251 if (rect.width == 0 || rect.height == 0) {
michael@0 1252 return gfxRect(0, 0, 0, 0);
michael@0 1253 }
michael@0 1254
michael@0 1255 Matrix mat = mTransform;
michael@0 1256 mat.Invert();
michael@0 1257 rect = mat.TransformBounds(rect);
michael@0 1258
michael@0 1259 return ThebesRect(rect);
michael@0 1260 }
michael@0 1261 }
michael@0 1262
michael@0 1263 bool
michael@0 1264 gfxContext::ClipContainsRect(const gfxRect& aRect)
michael@0 1265 {
michael@0 1266 if (mCairo) {
michael@0 1267 cairo_rectangle_list_t *clip =
michael@0 1268 cairo_copy_clip_rectangle_list(mCairo);
michael@0 1269
michael@0 1270 bool result = false;
michael@0 1271
michael@0 1272 if (clip->status == CAIRO_STATUS_SUCCESS) {
michael@0 1273 for (int i = 0; i < clip->num_rectangles; i++) {
michael@0 1274 gfxRect rect(clip->rectangles[i].x, clip->rectangles[i].y,
michael@0 1275 clip->rectangles[i].width, clip->rectangles[i].height);
michael@0 1276 if (rect.Contains(aRect)) {
michael@0 1277 result = true;
michael@0 1278 break;
michael@0 1279 }
michael@0 1280 }
michael@0 1281 }
michael@0 1282
michael@0 1283 cairo_rectangle_list_destroy(clip);
michael@0 1284 return result;
michael@0 1285 } else {
michael@0 1286 unsigned int lastReset = 0;
michael@0 1287 for (int i = mStateStack.Length() - 2; i > 0; i--) {
michael@0 1288 if (mStateStack[i].clipWasReset) {
michael@0 1289 lastReset = i;
michael@0 1290 break;
michael@0 1291 }
michael@0 1292 }
michael@0 1293
michael@0 1294 // Since we always return false when the clip list contains a
michael@0 1295 // non-rectangular clip or a non-rectilinear transform, our 'total' clip
michael@0 1296 // is always a rectangle if we hit the end of this function.
michael@0 1297 Rect clipBounds(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
michael@0 1298
michael@0 1299 for (unsigned int i = lastReset; i < mStateStack.Length(); i++) {
michael@0 1300 for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
michael@0 1301 AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
michael@0 1302 if (clip.path || !clip.transform.IsRectilinear()) {
michael@0 1303 // Cairo behavior is we return false if the clip contains a non-
michael@0 1304 // rectangle.
michael@0 1305 return false;
michael@0 1306 } else {
michael@0 1307 Rect clipRect = mTransform.TransformBounds(clip.rect);
michael@0 1308
michael@0 1309 clipBounds.IntersectRect(clipBounds, clipRect);
michael@0 1310 }
michael@0 1311 }
michael@0 1312 }
michael@0 1313
michael@0 1314 return clipBounds.Contains(ToRect(aRect));
michael@0 1315 }
michael@0 1316 }
michael@0 1317
michael@0 1318 // rendering sources
michael@0 1319
michael@0 1320 void
michael@0 1321 gfxContext::SetColor(const gfxRGBA& c)
michael@0 1322 {
michael@0 1323 if (mCairo) {
michael@0 1324 if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
michael@0 1325
michael@0 1326 gfxRGBA cms;
michael@0 1327 qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
michael@0 1328 if (transform)
michael@0 1329 gfxPlatform::TransformPixel(c, cms, transform);
michael@0 1330
michael@0 1331 // Use the original alpha to avoid unnecessary float->byte->float
michael@0 1332 // conversion errors
michael@0 1333 cairo_set_source_rgba(mCairo, cms.r, cms.g, cms.b, c.a);
michael@0 1334 }
michael@0 1335 else
michael@0 1336 cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a);
michael@0 1337 } else {
michael@0 1338 CurrentState().pattern = nullptr;
michael@0 1339 CurrentState().sourceSurfCairo = nullptr;
michael@0 1340 CurrentState().sourceSurface = nullptr;
michael@0 1341
michael@0 1342 if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
michael@0 1343
michael@0 1344 gfxRGBA cms;
michael@0 1345 qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
michael@0 1346 if (transform)
michael@0 1347 gfxPlatform::TransformPixel(c, cms, transform);
michael@0 1348
michael@0 1349 // Use the original alpha to avoid unnecessary float->byte->float
michael@0 1350 // conversion errors
michael@0 1351 CurrentState().color = ToColor(cms);
michael@0 1352 }
michael@0 1353 else
michael@0 1354 CurrentState().color = ToColor(c);
michael@0 1355 }
michael@0 1356 }
michael@0 1357
michael@0 1358 void
michael@0 1359 gfxContext::SetDeviceColor(const gfxRGBA& c)
michael@0 1360 {
michael@0 1361 if (mCairo) {
michael@0 1362 cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a);
michael@0 1363 } else {
michael@0 1364 CurrentState().pattern = nullptr;
michael@0 1365 CurrentState().sourceSurfCairo = nullptr;
michael@0 1366 CurrentState().sourceSurface = nullptr;
michael@0 1367 CurrentState().color = ToColor(c);
michael@0 1368 }
michael@0 1369 }
michael@0 1370
michael@0 1371 bool
michael@0 1372 gfxContext::GetDeviceColor(gfxRGBA& c)
michael@0 1373 {
michael@0 1374 if (mCairo) {
michael@0 1375 return cairo_pattern_get_rgba(cairo_get_source(mCairo),
michael@0 1376 &c.r,
michael@0 1377 &c.g,
michael@0 1378 &c.b,
michael@0 1379 &c.a) == CAIRO_STATUS_SUCCESS;
michael@0 1380 } else {
michael@0 1381 if (CurrentState().sourceSurface) {
michael@0 1382 return false;
michael@0 1383 }
michael@0 1384 if (CurrentState().pattern) {
michael@0 1385 gfxRGBA color;
michael@0 1386 return CurrentState().pattern->GetSolidColor(c);
michael@0 1387 }
michael@0 1388
michael@0 1389 c = ThebesRGBA(CurrentState().color);
michael@0 1390 return true;
michael@0 1391 }
michael@0 1392 }
michael@0 1393
michael@0 1394 void
michael@0 1395 gfxContext::SetSource(gfxASurface *surface, const gfxPoint& offset)
michael@0 1396 {
michael@0 1397 if (mCairo) {
michael@0 1398 NS_ASSERTION(surface->GetAllowUseAsSource(), "Surface not allowed to be used as source!");
michael@0 1399 cairo_set_source_surface(mCairo, surface->CairoSurface(), offset.x, offset.y);
michael@0 1400 } else {
michael@0 1401 CurrentState().surfTransform = Matrix(1.0f, 0, 0, 1.0f, Float(offset.x), Float(offset.y));
michael@0 1402 CurrentState().pattern = nullptr;
michael@0 1403 CurrentState().patternTransformChanged = false;
michael@0 1404 // Keep the underlying cairo surface around while we keep the
michael@0 1405 // sourceSurface.
michael@0 1406 CurrentState().sourceSurfCairo = surface;
michael@0 1407 CurrentState().sourceSurface =
michael@0 1408 gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
michael@0 1409 CurrentState().color = Color(0, 0, 0, 0);
michael@0 1410 }
michael@0 1411 }
michael@0 1412
michael@0 1413 void
michael@0 1414 gfxContext::SetPattern(gfxPattern *pattern)
michael@0 1415 {
michael@0 1416 if (mCairo) {
michael@0 1417 MOZ_ASSERT(!pattern->IsAzure());
michael@0 1418 cairo_set_source(mCairo, pattern->CairoPattern());
michael@0 1419 } else {
michael@0 1420 CurrentState().sourceSurfCairo = nullptr;
michael@0 1421 CurrentState().sourceSurface = nullptr;
michael@0 1422 CurrentState().patternTransformChanged = false;
michael@0 1423 CurrentState().pattern = pattern;
michael@0 1424 }
michael@0 1425 }
michael@0 1426
michael@0 1427 already_AddRefed<gfxPattern>
michael@0 1428 gfxContext::GetPattern()
michael@0 1429 {
michael@0 1430 if (mCairo) {
michael@0 1431 cairo_pattern_t *pat = cairo_get_source(mCairo);
michael@0 1432 NS_ASSERTION(pat, "I was told this couldn't be null");
michael@0 1433
michael@0 1434 nsRefPtr<gfxPattern> wrapper;
michael@0 1435 if (pat)
michael@0 1436 wrapper = new gfxPattern(pat);
michael@0 1437 else
michael@0 1438 wrapper = new gfxPattern(gfxRGBA(0,0,0,0));
michael@0 1439
michael@0 1440 return wrapper.forget();
michael@0 1441 } else {
michael@0 1442 nsRefPtr<gfxPattern> pat;
michael@0 1443
michael@0 1444 AzureState &state = CurrentState();
michael@0 1445 if (state.pattern) {
michael@0 1446 pat = state.pattern;
michael@0 1447 } else if (state.sourceSurface) {
michael@0 1448 NS_ASSERTION(false, "Ugh, this isn't good.");
michael@0 1449 } else {
michael@0 1450 pat = new gfxPattern(ThebesRGBA(state.color));
michael@0 1451 }
michael@0 1452 return pat.forget();
michael@0 1453 }
michael@0 1454 }
michael@0 1455
michael@0 1456
michael@0 1457 // masking
michael@0 1458 void
michael@0 1459 gfxContext::Mask(gfxPattern *pattern)
michael@0 1460 {
michael@0 1461 if (mCairo) {
michael@0 1462 MOZ_ASSERT(!pattern->IsAzure());
michael@0 1463 cairo_mask(mCairo, pattern->CairoPattern());
michael@0 1464 } else {
michael@0 1465 if (pattern->Extend() == gfxPattern::EXTEND_NONE) {
michael@0 1466 // In this situation the mask will be fully transparent (i.e. nothing
michael@0 1467 // will be drawn) outside of the bounds of the surface. We can support
michael@0 1468 // that by clipping out drawing to that area.
michael@0 1469 Point offset;
michael@0 1470 if (pattern->IsAzure()) {
michael@0 1471 // This is an Azure pattern. i.e. this was the result of a PopGroup and
michael@0 1472 // then the extend mode was changed to EXTEND_NONE.
michael@0 1473 // XXX - We may need some additional magic here in theory to support
michael@0 1474 // device offsets in these patterns, but no problems have been observed
michael@0 1475 // yet because of this. And it would complicate things a little further.
michael@0 1476 offset = Point(0.f, 0.f);
michael@0 1477 } else if (pattern->GetType() == gfxPattern::PATTERN_SURFACE) {
michael@0 1478 nsRefPtr<gfxASurface> asurf = pattern->GetSurface();
michael@0 1479 gfxPoint deviceOffset = asurf->GetDeviceOffset();
michael@0 1480 offset = Point(-deviceOffset.x, -deviceOffset.y);
michael@0 1481
michael@0 1482 // this lets GetAzureSurface work
michael@0 1483 pattern->GetPattern(mDT);
michael@0 1484 }
michael@0 1485
michael@0 1486 if (pattern->IsAzure() || pattern->GetType() == gfxPattern::PATTERN_SURFACE) {
michael@0 1487 RefPtr<SourceSurface> mask = pattern->GetAzureSurface();
michael@0 1488 Matrix mat = ToMatrix(pattern->GetInverseMatrix());
michael@0 1489 Matrix old = mTransform;
michael@0 1490 // add in the inverse of the pattern transform so that when we
michael@0 1491 // MaskSurface we are transformed to the place matching the pattern transform
michael@0 1492 mat = mat * mTransform;
michael@0 1493
michael@0 1494 ChangeTransform(mat);
michael@0 1495 mDT->MaskSurface(GeneralPattern(this), mask, offset, DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode));
michael@0 1496 ChangeTransform(old);
michael@0 1497 return;
michael@0 1498 }
michael@0 1499 }
michael@0 1500 mDT->Mask(GeneralPattern(this), *pattern->GetPattern(mDT), DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode));
michael@0 1501 }
michael@0 1502 }
michael@0 1503
michael@0 1504 void
michael@0 1505 gfxContext::Mask(gfxASurface *surface, const gfxPoint& offset)
michael@0 1506 {
michael@0 1507 PROFILER_LABEL("gfxContext", "Mask");
michael@0 1508 if (mCairo) {
michael@0 1509 cairo_mask_surface(mCairo, surface->CairoSurface(), offset.x, offset.y);
michael@0 1510 } else {
michael@0 1511 // Lifetime needs to be limited here as we may simply wrap surface's data.
michael@0 1512 RefPtr<SourceSurface> sourceSurf =
michael@0 1513 gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
michael@0 1514
michael@0 1515 if (!sourceSurf) {
michael@0 1516 return;
michael@0 1517 }
michael@0 1518
michael@0 1519 gfxPoint pt = surface->GetDeviceOffset();
michael@0 1520
michael@0 1521 Mask(sourceSurf, Point(offset.x - pt.x, offset.y - pt.y));
michael@0 1522 }
michael@0 1523 }
michael@0 1524
michael@0 1525 void
michael@0 1526 gfxContext::Mask(SourceSurface *surface, const Point& offset)
michael@0 1527 {
michael@0 1528 MOZ_ASSERT(mDT);
michael@0 1529
michael@0 1530
michael@0 1531 // We clip here to bind to the mask surface bounds, see above.
michael@0 1532 mDT->MaskSurface(GeneralPattern(this),
michael@0 1533 surface,
michael@0 1534 offset,
michael@0 1535 DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode));
michael@0 1536 }
michael@0 1537
michael@0 1538 void
michael@0 1539 gfxContext::Paint(gfxFloat alpha)
michael@0 1540 {
michael@0 1541 PROFILER_LABEL("gfxContext", "Paint");
michael@0 1542 if (mCairo) {
michael@0 1543 cairo_paint_with_alpha(mCairo, alpha);
michael@0 1544 } else {
michael@0 1545 AzureState &state = CurrentState();
michael@0 1546
michael@0 1547 if (state.sourceSurface && !state.sourceSurfCairo &&
michael@0 1548 !state.patternTransformChanged && !state.opIsClear)
michael@0 1549 {
michael@0 1550 // This is the case where a PopGroupToSource has been done and this
michael@0 1551 // paint is executed without changing the transform or the source.
michael@0 1552 Matrix oldMat = mDT->GetTransform();
michael@0 1553
michael@0 1554 IntSize surfSize = state.sourceSurface->GetSize();
michael@0 1555
michael@0 1556 Matrix mat;
michael@0 1557 mat.Translate(-state.deviceOffset.x, -state.deviceOffset.y);
michael@0 1558 mDT->SetTransform(mat);
michael@0 1559
michael@0 1560 mDT->DrawSurface(state.sourceSurface,
michael@0 1561 Rect(state.sourceSurfaceDeviceOffset, Size(surfSize.width, surfSize.height)),
michael@0 1562 Rect(Point(), Size(surfSize.width, surfSize.height)),
michael@0 1563 DrawSurfaceOptions(), DrawOptions(alpha, GetOp()));
michael@0 1564 mDT->SetTransform(oldMat);
michael@0 1565 return;
michael@0 1566 }
michael@0 1567
michael@0 1568 Matrix mat = mDT->GetTransform();
michael@0 1569 mat.Invert();
michael@0 1570 Rect paintRect = mat.TransformBounds(Rect(Point(0, 0), Size(mDT->GetSize())));
michael@0 1571
michael@0 1572 if (state.opIsClear) {
michael@0 1573 mDT->ClearRect(paintRect);
michael@0 1574 } else {
michael@0 1575 mDT->FillRect(paintRect, GeneralPattern(this),
michael@0 1576 DrawOptions(Float(alpha), GetOp()));
michael@0 1577 }
michael@0 1578 }
michael@0 1579 }
michael@0 1580
michael@0 1581 // groups
michael@0 1582
michael@0 1583 void
michael@0 1584 gfxContext::PushGroup(gfxContentType content)
michael@0 1585 {
michael@0 1586 if (mCairo) {
michael@0 1587 cairo_push_group_with_content(mCairo, (cairo_content_t)(int) content);
michael@0 1588 } else {
michael@0 1589 PushNewDT(content);
michael@0 1590
michael@0 1591 PushClipsToDT(mDT);
michael@0 1592 mDT->SetTransform(GetDTTransform());
michael@0 1593 }
michael@0 1594 }
michael@0 1595
michael@0 1596 static gfxRect
michael@0 1597 GetRoundOutDeviceClipExtents(gfxContext* aCtx)
michael@0 1598 {
michael@0 1599 gfxContextMatrixAutoSaveRestore save(aCtx);
michael@0 1600 aCtx->IdentityMatrix();
michael@0 1601 gfxRect r = aCtx->GetClipExtents();
michael@0 1602 r.RoundOut();
michael@0 1603 return r;
michael@0 1604 }
michael@0 1605
michael@0 1606 /**
michael@0 1607 * Copy the contents of aSrc to aDest, translated by aTranslation.
michael@0 1608 */
michael@0 1609 static void
michael@0 1610 CopySurface(gfxASurface* aSrc, gfxASurface* aDest, const gfxPoint& aTranslation)
michael@0 1611 {
michael@0 1612 cairo_t *cr = cairo_create(aDest->CairoSurface());
michael@0 1613 cairo_set_source_surface(cr, aSrc->CairoSurface(), aTranslation.x, aTranslation.y);
michael@0 1614 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
michael@0 1615 cairo_paint(cr);
michael@0 1616 cairo_destroy(cr);
michael@0 1617 }
michael@0 1618
michael@0 1619 void
michael@0 1620 gfxContext::PushGroupAndCopyBackground(gfxContentType content)
michael@0 1621 {
michael@0 1622 if (mCairo) {
michael@0 1623 if (content == gfxContentType::COLOR_ALPHA &&
michael@0 1624 !(GetFlags() & FLAG_DISABLE_COPY_BACKGROUND)) {
michael@0 1625 nsRefPtr<gfxASurface> s = CurrentSurface();
michael@0 1626 if ((s->GetAllowUseAsSource() || s->GetType() == gfxSurfaceType::Tee) &&
michael@0 1627 (s->GetContentType() == gfxContentType::COLOR ||
michael@0 1628 s->GetOpaqueRect().Contains(GetRoundOutDeviceClipExtents(this)))) {
michael@0 1629 cairo_push_group_with_content(mCairo, CAIRO_CONTENT_COLOR);
michael@0 1630 nsRefPtr<gfxASurface> d = CurrentSurface();
michael@0 1631
michael@0 1632 if (d->GetType() == gfxSurfaceType::Tee) {
michael@0 1633 NS_ASSERTION(s->GetType() == gfxSurfaceType::Tee, "Mismatched types");
michael@0 1634 nsAutoTArray<nsRefPtr<gfxASurface>,2> ss;
michael@0 1635 nsAutoTArray<nsRefPtr<gfxASurface>,2> ds;
michael@0 1636 static_cast<gfxTeeSurface*>(s.get())->GetSurfaces(&ss);
michael@0 1637 static_cast<gfxTeeSurface*>(d.get())->GetSurfaces(&ds);
michael@0 1638 NS_ASSERTION(ss.Length() == ds.Length(), "Mismatched lengths");
michael@0 1639 gfxPoint translation = d->GetDeviceOffset() - s->GetDeviceOffset();
michael@0 1640 for (uint32_t i = 0; i < ss.Length(); ++i) {
michael@0 1641 CopySurface(ss[i], ds[i], translation);
michael@0 1642 }
michael@0 1643 } else {
michael@0 1644 CopySurface(s, d, gfxPoint(0, 0));
michael@0 1645 }
michael@0 1646 d->SetOpaqueRect(s->GetOpaqueRect());
michael@0 1647 return;
michael@0 1648 }
michael@0 1649 }
michael@0 1650 } else {
michael@0 1651 IntRect clipExtents;
michael@0 1652 if (mDT->GetFormat() != SurfaceFormat::B8G8R8X8) {
michael@0 1653 gfxRect clipRect = GetRoundOutDeviceClipExtents(this);
michael@0 1654 clipExtents = IntRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
michael@0 1655 }
michael@0 1656 if ((mDT->GetFormat() == SurfaceFormat::B8G8R8X8 ||
michael@0 1657 mDT->GetOpaqueRect().Contains(clipExtents)) &&
michael@0 1658 !mDT->GetUserData(&sDontUseAsSourceKey)) {
michael@0 1659 DrawTarget *oldDT = mDT;
michael@0 1660 RefPtr<SourceSurface> source = mDT->Snapshot();
michael@0 1661 Point oldDeviceOffset = CurrentState().deviceOffset;
michael@0 1662
michael@0 1663 PushNewDT(gfxContentType::COLOR);
michael@0 1664
michael@0 1665 Point offset = CurrentState().deviceOffset - oldDeviceOffset;
michael@0 1666 Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
michael@0 1667 Rect sourceRect = surfRect;
michael@0 1668 sourceRect.x += offset.x;
michael@0 1669 sourceRect.y += offset.y;
michael@0 1670
michael@0 1671 mDT->SetTransform(Matrix());
michael@0 1672 mDT->DrawSurface(source, surfRect, sourceRect);
michael@0 1673 mDT->SetOpaqueRect(oldDT->GetOpaqueRect());
michael@0 1674
michael@0 1675 PushClipsToDT(mDT);
michael@0 1676 mDT->SetTransform(GetDTTransform());
michael@0 1677 return;
michael@0 1678 }
michael@0 1679 }
michael@0 1680 PushGroup(content);
michael@0 1681 }
michael@0 1682
michael@0 1683 already_AddRefed<gfxPattern>
michael@0 1684 gfxContext::PopGroup()
michael@0 1685 {
michael@0 1686 if (mCairo) {
michael@0 1687 cairo_pattern_t *pat = cairo_pop_group(mCairo);
michael@0 1688 nsRefPtr<gfxPattern> wrapper = new gfxPattern(pat);
michael@0 1689 cairo_pattern_destroy(pat);
michael@0 1690 return wrapper.forget();
michael@0 1691 } else {
michael@0 1692 RefPtr<SourceSurface> src = mDT->Snapshot();
michael@0 1693 Point deviceOffset = CurrentState().deviceOffset;
michael@0 1694
michael@0 1695 Restore();
michael@0 1696
michael@0 1697 Matrix mat = mTransform;
michael@0 1698 mat.Invert();
michael@0 1699
michael@0 1700 Matrix deviceOffsetTranslation;
michael@0 1701 deviceOffsetTranslation.Translate(deviceOffset.x, deviceOffset.y);
michael@0 1702
michael@0 1703 nsRefPtr<gfxPattern> pat = new gfxPattern(src, deviceOffsetTranslation * mat);
michael@0 1704
michael@0 1705 return pat.forget();
michael@0 1706 }
michael@0 1707 }
michael@0 1708
michael@0 1709 void
michael@0 1710 gfxContext::PopGroupToSource()
michael@0 1711 {
michael@0 1712 if (mCairo) {
michael@0 1713 cairo_pop_group_to_source(mCairo);
michael@0 1714 } else {
michael@0 1715 RefPtr<SourceSurface> src = mDT->Snapshot();
michael@0 1716 Point deviceOffset = CurrentState().deviceOffset;
michael@0 1717 Restore();
michael@0 1718 CurrentState().sourceSurfCairo = nullptr;
michael@0 1719 CurrentState().sourceSurface = src;
michael@0 1720 CurrentState().sourceSurfaceDeviceOffset = deviceOffset;
michael@0 1721 CurrentState().pattern = nullptr;
michael@0 1722 CurrentState().patternTransformChanged = false;
michael@0 1723
michael@0 1724 Matrix mat = mTransform;
michael@0 1725 mat.Invert();
michael@0 1726
michael@0 1727 Matrix deviceOffsetTranslation;
michael@0 1728 deviceOffsetTranslation.Translate(deviceOffset.x, deviceOffset.y);
michael@0 1729 CurrentState().surfTransform = deviceOffsetTranslation * mat;
michael@0 1730 }
michael@0 1731 }
michael@0 1732
michael@0 1733 bool
michael@0 1734 gfxContext::PointInFill(const gfxPoint& pt)
michael@0 1735 {
michael@0 1736 if (mCairo) {
michael@0 1737 return cairo_in_fill(mCairo, pt.x, pt.y);
michael@0 1738 } else {
michael@0 1739 EnsurePath();
michael@0 1740 return mPath->ContainsPoint(ToPoint(pt), Matrix());
michael@0 1741 }
michael@0 1742 }
michael@0 1743
michael@0 1744 bool
michael@0 1745 gfxContext::PointInStroke(const gfxPoint& pt)
michael@0 1746 {
michael@0 1747 if (mCairo) {
michael@0 1748 return cairo_in_stroke(mCairo, pt.x, pt.y);
michael@0 1749 } else {
michael@0 1750 EnsurePath();
michael@0 1751 return mPath->StrokeContainsPoint(CurrentState().strokeOptions,
michael@0 1752 ToPoint(pt),
michael@0 1753 Matrix());
michael@0 1754 }
michael@0 1755 }
michael@0 1756
michael@0 1757 gfxRect
michael@0 1758 gfxContext::GetUserPathExtent()
michael@0 1759 {
michael@0 1760 if (mCairo) {
michael@0 1761 double xmin, ymin, xmax, ymax;
michael@0 1762 cairo_path_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
michael@0 1763 return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
michael@0 1764 } else {
michael@0 1765 EnsurePath();
michael@0 1766 return ThebesRect(mPath->GetBounds());
michael@0 1767 }
michael@0 1768 }
michael@0 1769
michael@0 1770 gfxRect
michael@0 1771 gfxContext::GetUserFillExtent()
michael@0 1772 {
michael@0 1773 if (mCairo) {
michael@0 1774 double xmin, ymin, xmax, ymax;
michael@0 1775 cairo_fill_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
michael@0 1776 return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
michael@0 1777 } else {
michael@0 1778 EnsurePath();
michael@0 1779 return ThebesRect(mPath->GetBounds());
michael@0 1780 }
michael@0 1781 }
michael@0 1782
michael@0 1783 gfxRect
michael@0 1784 gfxContext::GetUserStrokeExtent()
michael@0 1785 {
michael@0 1786 if (mCairo) {
michael@0 1787 double xmin, ymin, xmax, ymax;
michael@0 1788 cairo_stroke_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
michael@0 1789 return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
michael@0 1790 } else {
michael@0 1791 EnsurePath();
michael@0 1792 return ThebesRect(mPath->GetStrokedBounds(CurrentState().strokeOptions, mTransform));
michael@0 1793 }
michael@0 1794 }
michael@0 1795
michael@0 1796 bool
michael@0 1797 gfxContext::HasError()
michael@0 1798 {
michael@0 1799 if (mCairo) {
michael@0 1800 return cairo_status(mCairo) != CAIRO_STATUS_SUCCESS;
michael@0 1801 } else {
michael@0 1802 // As far as this is concerned, an Azure context is never in error.
michael@0 1803 return false;
michael@0 1804 }
michael@0 1805 }
michael@0 1806
michael@0 1807 void
michael@0 1808 gfxContext::RoundedRectangle(const gfxRect& rect,
michael@0 1809 const gfxCornerSizes& corners,
michael@0 1810 bool draw_clockwise)
michael@0 1811 {
michael@0 1812 //
michael@0 1813 // For CW drawing, this looks like:
michael@0 1814 //
michael@0 1815 // ...******0** 1 C
michael@0 1816 // ****
michael@0 1817 // *** 2
michael@0 1818 // **
michael@0 1819 // *
michael@0 1820 // *
michael@0 1821 // 3
michael@0 1822 // *
michael@0 1823 // *
michael@0 1824 //
michael@0 1825 // Where 0, 1, 2, 3 are the control points of the Bezier curve for
michael@0 1826 // the corner, and C is the actual corner point.
michael@0 1827 //
michael@0 1828 // At the start of the loop, the current point is assumed to be
michael@0 1829 // the point adjacent to the top left corner on the top
michael@0 1830 // horizontal. Note that corner indices start at the top left and
michael@0 1831 // continue clockwise, whereas in our loop i = 0 refers to the top
michael@0 1832 // right corner.
michael@0 1833 //
michael@0 1834 // When going CCW, the control points are swapped, and the first
michael@0 1835 // corner that's drawn is the top left (along with the top segment).
michael@0 1836 //
michael@0 1837 // There is considerable latitude in how one chooses the four
michael@0 1838 // control points for a Bezier curve approximation to an ellipse.
michael@0 1839 // For the overall path to be continuous and show no corner at the
michael@0 1840 // endpoints of the arc, points 0 and 3 must be at the ends of the
michael@0 1841 // straight segments of the rectangle; points 0, 1, and C must be
michael@0 1842 // collinear; and points 3, 2, and C must also be collinear. This
michael@0 1843 // leaves only two free parameters: the ratio of the line segments
michael@0 1844 // 01 and 0C, and the ratio of the line segments 32 and 3C. See
michael@0 1845 // the following papers for extensive discussion of how to choose
michael@0 1846 // these ratios:
michael@0 1847 //
michael@0 1848 // Dokken, Tor, et al. "Good approximation of circles by
michael@0 1849 // curvature-continuous Bezier curves." Computer-Aided
michael@0 1850 // Geometric Design 7(1990) 33--41.
michael@0 1851 // Goldapp, Michael. "Approximation of circular arcs by cubic
michael@0 1852 // polynomials." Computer-Aided Geometric Design 8(1991) 227--238.
michael@0 1853 // Maisonobe, Luc. "Drawing an elliptical arc using polylines,
michael@0 1854 // quadratic, or cubic Bezier curves."
michael@0 1855 // http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
michael@0 1856 //
michael@0 1857 // We follow the approach in section 2 of Goldapp (least-error,
michael@0 1858 // Hermite-type approximation) and make both ratios equal to
michael@0 1859 //
michael@0 1860 // 2 2 + n - sqrt(2n + 28)
michael@0 1861 // alpha = - * ---------------------
michael@0 1862 // 3 n - 4
michael@0 1863 //
michael@0 1864 // where n = 3( cbrt(sqrt(2)+1) - cbrt(sqrt(2)-1) ).
michael@0 1865 //
michael@0 1866 // This is the result of Goldapp's equation (10b) when the angle
michael@0 1867 // swept out by the arc is pi/2, and the parameter "a-bar" is the
michael@0 1868 // expression given immediately below equation (21).
michael@0 1869 //
michael@0 1870 // Using this value, the maximum radial error for a circle, as a
michael@0 1871 // fraction of the radius, is on the order of 0.2 x 10^-3.
michael@0 1872 // Neither Dokken nor Goldapp discusses error for a general
michael@0 1873 // ellipse; Maisonobe does, but his choice of control points
michael@0 1874 // follows different constraints, and Goldapp's expression for
michael@0 1875 // 'alpha' gives much smaller radial error, even for very flat
michael@0 1876 // ellipses, than Maisonobe's equivalent.
michael@0 1877 //
michael@0 1878 // For the various corners and for each axis, the sign of this
michael@0 1879 // constant changes, or it might be 0 -- it's multiplied by the
michael@0 1880 // appropriate multiplier from the list before using.
michael@0 1881
michael@0 1882 if (mCairo) {
michael@0 1883 const gfxFloat alpha = 0.55191497064665766025;
michael@0 1884
michael@0 1885 typedef struct { gfxFloat a, b; } twoFloats;
michael@0 1886
michael@0 1887 twoFloats cwCornerMults[4] = { { -1, 0 },
michael@0 1888 { 0, -1 },
michael@0 1889 { +1, 0 },
michael@0 1890 { 0, +1 } };
michael@0 1891 twoFloats ccwCornerMults[4] = { { +1, 0 },
michael@0 1892 { 0, -1 },
michael@0 1893 { -1, 0 },
michael@0 1894 { 0, +1 } };
michael@0 1895
michael@0 1896 twoFloats *cornerMults = draw_clockwise ? cwCornerMults : ccwCornerMults;
michael@0 1897
michael@0 1898 gfxPoint pc, p0, p1, p2, p3;
michael@0 1899
michael@0 1900 if (draw_clockwise)
michael@0 1901 cairo_move_to(mCairo, rect.X() + corners[NS_CORNER_TOP_LEFT].width, rect.Y());
michael@0 1902 else
michael@0 1903 cairo_move_to(mCairo, rect.X() + rect.Width() - corners[NS_CORNER_TOP_RIGHT].width, rect.Y());
michael@0 1904
michael@0 1905 NS_FOR_CSS_CORNERS(i) {
michael@0 1906 // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
michael@0 1907 mozilla::css::Corner c = mozilla::css::Corner(draw_clockwise ? ((i+1) % 4) : ((4-i) % 4));
michael@0 1908
michael@0 1909 // i+2 and i+3 respectively. These are used to index into the corner
michael@0 1910 // multiplier table, and were deduced by calculating out the long form
michael@0 1911 // of each corner and finding a pattern in the signs and values.
michael@0 1912 int i2 = (i+2) % 4;
michael@0 1913 int i3 = (i+3) % 4;
michael@0 1914
michael@0 1915 pc = rect.AtCorner(c);
michael@0 1916
michael@0 1917 if (corners[c].width > 0.0 && corners[c].height > 0.0) {
michael@0 1918 p0.x = pc.x + cornerMults[i].a * corners[c].width;
michael@0 1919 p0.y = pc.y + cornerMults[i].b * corners[c].height;
michael@0 1920
michael@0 1921 p3.x = pc.x + cornerMults[i3].a * corners[c].width;
michael@0 1922 p3.y = pc.y + cornerMults[i3].b * corners[c].height;
michael@0 1923
michael@0 1924 p1.x = p0.x + alpha * cornerMults[i2].a * corners[c].width;
michael@0 1925 p1.y = p0.y + alpha * cornerMults[i2].b * corners[c].height;
michael@0 1926
michael@0 1927 p2.x = p3.x - alpha * cornerMults[i3].a * corners[c].width;
michael@0 1928 p2.y = p3.y - alpha * cornerMults[i3].b * corners[c].height;
michael@0 1929
michael@0 1930 cairo_line_to (mCairo, p0.x, p0.y);
michael@0 1931 cairo_curve_to (mCairo,
michael@0 1932 p1.x, p1.y,
michael@0 1933 p2.x, p2.y,
michael@0 1934 p3.x, p3.y);
michael@0 1935 } else {
michael@0 1936 cairo_line_to (mCairo, pc.x, pc.y);
michael@0 1937 }
michael@0 1938 }
michael@0 1939
michael@0 1940 cairo_close_path (mCairo);
michael@0 1941 } else {
michael@0 1942 EnsurePathBuilder();
michael@0 1943 Size radii[] = { ToSize(corners[NS_CORNER_TOP_LEFT]),
michael@0 1944 ToSize(corners[NS_CORNER_TOP_RIGHT]),
michael@0 1945 ToSize(corners[NS_CORNER_BOTTOM_RIGHT]),
michael@0 1946 ToSize(corners[NS_CORNER_BOTTOM_LEFT]) };
michael@0 1947 AppendRoundedRectToPath(mPathBuilder, ToRect(rect), radii, draw_clockwise);
michael@0 1948 }
michael@0 1949 }
michael@0 1950
michael@0 1951 #ifdef MOZ_DUMP_PAINTING
michael@0 1952 void
michael@0 1953 gfxContext::WriteAsPNG(const char* aFile)
michael@0 1954 {
michael@0 1955 nsRefPtr<gfxASurface> surf = CurrentSurface();
michael@0 1956 if (surf) {
michael@0 1957 surf->WriteAsPNG(aFile);
michael@0 1958 } else {
michael@0 1959 NS_WARNING("No surface found!");
michael@0 1960 }
michael@0 1961 }
michael@0 1962
michael@0 1963 void
michael@0 1964 gfxContext::DumpAsDataURL()
michael@0 1965 {
michael@0 1966 nsRefPtr<gfxASurface> surf = CurrentSurface();
michael@0 1967 if (surf) {
michael@0 1968 surf->DumpAsDataURL();
michael@0 1969 } else {
michael@0 1970 NS_WARNING("No surface found!");
michael@0 1971 }
michael@0 1972 }
michael@0 1973
michael@0 1974 void
michael@0 1975 gfxContext::CopyAsDataURL()
michael@0 1976 {
michael@0 1977 nsRefPtr<gfxASurface> surf = CurrentSurface();
michael@0 1978 if (surf) {
michael@0 1979 surf->CopyAsDataURL();
michael@0 1980 } else {
michael@0 1981 NS_WARNING("No surface found!");
michael@0 1982 }
michael@0 1983 }
michael@0 1984 #endif
michael@0 1985
michael@0 1986 void
michael@0 1987 gfxContext::EnsurePath()
michael@0 1988 {
michael@0 1989 if (mPathBuilder) {
michael@0 1990 mPath = mPathBuilder->Finish();
michael@0 1991 mPathBuilder = nullptr;
michael@0 1992 }
michael@0 1993
michael@0 1994 if (mPath) {
michael@0 1995 if (mTransformChanged) {
michael@0 1996 Matrix mat = mTransform;
michael@0 1997 mat.Invert();
michael@0 1998 mat = mPathTransform * mat;
michael@0 1999 mPathBuilder = mPath->TransformedCopyToBuilder(mat, CurrentState().fillRule);
michael@0 2000 mPath = mPathBuilder->Finish();
michael@0 2001 mPathBuilder = nullptr;
michael@0 2002
michael@0 2003 mTransformChanged = false;
michael@0 2004 }
michael@0 2005
michael@0 2006 if (CurrentState().fillRule == mPath->GetFillRule()) {
michael@0 2007 return;
michael@0 2008 }
michael@0 2009
michael@0 2010 mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
michael@0 2011
michael@0 2012 mPath = mPathBuilder->Finish();
michael@0 2013 mPathBuilder = nullptr;
michael@0 2014 return;
michael@0 2015 }
michael@0 2016
michael@0 2017 EnsurePathBuilder();
michael@0 2018 mPath = mPathBuilder->Finish();
michael@0 2019 mPathBuilder = nullptr;
michael@0 2020 }
michael@0 2021
michael@0 2022 void
michael@0 2023 gfxContext::EnsurePathBuilder()
michael@0 2024 {
michael@0 2025 if (mPathBuilder && !mTransformChanged) {
michael@0 2026 return;
michael@0 2027 }
michael@0 2028
michael@0 2029 if (mPath) {
michael@0 2030 if (!mTransformChanged) {
michael@0 2031 mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
michael@0 2032 mPath = nullptr;
michael@0 2033 } else {
michael@0 2034 Matrix invTransform = mTransform;
michael@0 2035 invTransform.Invert();
michael@0 2036 Matrix toNewUS = mPathTransform * invTransform;
michael@0 2037 mPathBuilder = mPath->TransformedCopyToBuilder(toNewUS, CurrentState().fillRule);
michael@0 2038 }
michael@0 2039 return;
michael@0 2040 }
michael@0 2041
michael@0 2042 DebugOnly<PathBuilder*> oldPath = mPathBuilder.get();
michael@0 2043
michael@0 2044 if (!mPathBuilder) {
michael@0 2045 mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule);
michael@0 2046
michael@0 2047 if (mPathIsRect) {
michael@0 2048 mPathBuilder->MoveTo(mRect.TopLeft());
michael@0 2049 mPathBuilder->LineTo(mRect.TopRight());
michael@0 2050 mPathBuilder->LineTo(mRect.BottomRight());
michael@0 2051 mPathBuilder->LineTo(mRect.BottomLeft());
michael@0 2052 mPathBuilder->Close();
michael@0 2053 }
michael@0 2054 }
michael@0 2055
michael@0 2056 if (mTransformChanged) {
michael@0 2057 // This could be an else if since this should never happen when
michael@0 2058 // mPathBuilder is nullptr and mPath is nullptr. But this way we can
michael@0 2059 // assert if all the state is as expected.
michael@0 2060 MOZ_ASSERT(oldPath);
michael@0 2061 MOZ_ASSERT(!mPathIsRect);
michael@0 2062
michael@0 2063 Matrix invTransform = mTransform;
michael@0 2064 invTransform.Invert();
michael@0 2065 Matrix toNewUS = mPathTransform * invTransform;
michael@0 2066
michael@0 2067 RefPtr<Path> path = mPathBuilder->Finish();
michael@0 2068 mPathBuilder = path->TransformedCopyToBuilder(toNewUS, CurrentState().fillRule);
michael@0 2069 }
michael@0 2070
michael@0 2071 mPathIsRect = false;
michael@0 2072 }
michael@0 2073
michael@0 2074 void
michael@0 2075 gfxContext::FillAzure(Float aOpacity)
michael@0 2076 {
michael@0 2077 AzureState &state = CurrentState();
michael@0 2078
michael@0 2079 CompositionOp op = GetOp();
michael@0 2080
michael@0 2081 if (mPathIsRect) {
michael@0 2082 MOZ_ASSERT(!mTransformChanged);
michael@0 2083
michael@0 2084 if (state.opIsClear) {
michael@0 2085 mDT->ClearRect(mRect);
michael@0 2086 } else if (op == CompositionOp::OP_SOURCE) {
michael@0 2087 // Emulate cairo operator source which is bound by mask!
michael@0 2088 mDT->ClearRect(mRect);
michael@0 2089 mDT->FillRect(mRect, GeneralPattern(this), DrawOptions(aOpacity));
michael@0 2090 } else {
michael@0 2091 mDT->FillRect(mRect, GeneralPattern(this), DrawOptions(aOpacity, op, state.aaMode));
michael@0 2092 }
michael@0 2093 } else {
michael@0 2094 EnsurePath();
michael@0 2095
michael@0 2096 NS_ASSERTION(!state.opIsClear, "We shouldn't be clearing complex paths!");
michael@0 2097
michael@0 2098 mDT->Fill(mPath, GeneralPattern(this), DrawOptions(aOpacity, op, state.aaMode));
michael@0 2099 }
michael@0 2100 }
michael@0 2101
michael@0 2102 void
michael@0 2103 gfxContext::PushClipsToDT(DrawTarget *aDT)
michael@0 2104 {
michael@0 2105 // Tricky, we have to restore all clips -since the last time- the clip
michael@0 2106 // was reset. If we didn't reset the clip, just popping the clips we
michael@0 2107 // added was fine.
michael@0 2108 unsigned int lastReset = 0;
michael@0 2109 for (int i = mStateStack.Length() - 2; i > 0; i--) {
michael@0 2110 if (mStateStack[i].clipWasReset) {
michael@0 2111 lastReset = i;
michael@0 2112 break;
michael@0 2113 }
michael@0 2114 }
michael@0 2115
michael@0 2116 // Don't need to save the old transform, we'll be setting a new one soon!
michael@0 2117
michael@0 2118 // Push all clips from the last state on the stack where the clip was
michael@0 2119 // reset to the clip before ours.
michael@0 2120 for (unsigned int i = lastReset; i < mStateStack.Length() - 1; i++) {
michael@0 2121 for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
michael@0 2122 aDT->SetTransform(mStateStack[i].pushedClips[c].transform * GetDeviceTransform());
michael@0 2123 if (mStateStack[i].pushedClips[c].path) {
michael@0 2124 aDT->PushClip(mStateStack[i].pushedClips[c].path);
michael@0 2125 } else {
michael@0 2126 aDT->PushClipRect(mStateStack[i].pushedClips[c].rect);
michael@0 2127 }
michael@0 2128 }
michael@0 2129 }
michael@0 2130 }
michael@0 2131
michael@0 2132 CompositionOp
michael@0 2133 gfxContext::GetOp()
michael@0 2134 {
michael@0 2135 if (CurrentState().op != CompositionOp::OP_SOURCE) {
michael@0 2136 return CurrentState().op;
michael@0 2137 }
michael@0 2138
michael@0 2139 AzureState &state = CurrentState();
michael@0 2140 if (state.pattern) {
michael@0 2141 if (state.pattern->IsOpaque()) {
michael@0 2142 return CompositionOp::OP_OVER;
michael@0 2143 } else {
michael@0 2144 return CompositionOp::OP_SOURCE;
michael@0 2145 }
michael@0 2146 } else if (state.sourceSurface) {
michael@0 2147 if (state.sourceSurface->GetFormat() == SurfaceFormat::B8G8R8X8) {
michael@0 2148 return CompositionOp::OP_OVER;
michael@0 2149 } else {
michael@0 2150 return CompositionOp::OP_SOURCE;
michael@0 2151 }
michael@0 2152 } else {
michael@0 2153 if (state.color.a > 0.999) {
michael@0 2154 return CompositionOp::OP_OVER;
michael@0 2155 } else {
michael@0 2156 return CompositionOp::OP_SOURCE;
michael@0 2157 }
michael@0 2158 }
michael@0 2159 }
michael@0 2160
michael@0 2161 /* SVG font code can change the transform after having set the pattern on the
michael@0 2162 * context. When the pattern is set it is in user space, if the transform is
michael@0 2163 * changed after doing so the pattern needs to be converted back into userspace.
michael@0 2164 * We just store the old pattern transform here so that we only do the work
michael@0 2165 * needed here if the pattern is actually used.
michael@0 2166 * We need to avoid doing this when this ChangeTransform comes from a restore,
michael@0 2167 * since the current pattern and the current transform are both part of the
michael@0 2168 * state we know the new CurrentState()'s values are valid. But if we assume
michael@0 2169 * a change they might become invalid since patternTransformChanged is part of
michael@0 2170 * the state and might be false for the restored AzureState.
michael@0 2171 */
michael@0 2172 void
michael@0 2173 gfxContext::ChangeTransform(const Matrix &aNewMatrix, bool aUpdatePatternTransform)
michael@0 2174 {
michael@0 2175 AzureState &state = CurrentState();
michael@0 2176
michael@0 2177 if (aUpdatePatternTransform && (state.pattern || state.sourceSurface)
michael@0 2178 && !state.patternTransformChanged) {
michael@0 2179 state.patternTransform = GetDTTransform();
michael@0 2180 state.patternTransformChanged = true;
michael@0 2181 }
michael@0 2182
michael@0 2183 if (mPathIsRect) {
michael@0 2184 Matrix invMatrix = aNewMatrix;
michael@0 2185
michael@0 2186 invMatrix.Invert();
michael@0 2187
michael@0 2188 Matrix toNewUS = mTransform * invMatrix;
michael@0 2189
michael@0 2190 if (toNewUS.IsRectilinear()) {
michael@0 2191 mRect = toNewUS.TransformBounds(mRect);
michael@0 2192 mRect.NudgeToIntegers();
michael@0 2193 } else {
michael@0 2194 mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule);
michael@0 2195
michael@0 2196 mPathBuilder->MoveTo(toNewUS * mRect.TopLeft());
michael@0 2197 mPathBuilder->LineTo(toNewUS * mRect.TopRight());
michael@0 2198 mPathBuilder->LineTo(toNewUS * mRect.BottomRight());
michael@0 2199 mPathBuilder->LineTo(toNewUS * mRect.BottomLeft());
michael@0 2200 mPathBuilder->Close();
michael@0 2201
michael@0 2202 mPathIsRect = false;
michael@0 2203 }
michael@0 2204
michael@0 2205 // No need to consider the transform changed now!
michael@0 2206 mTransformChanged = false;
michael@0 2207 } else if ((mPath || mPathBuilder) && !mTransformChanged) {
michael@0 2208 mTransformChanged = true;
michael@0 2209 mPathTransform = mTransform;
michael@0 2210 }
michael@0 2211
michael@0 2212 mTransform = aNewMatrix;
michael@0 2213
michael@0 2214 mDT->SetTransform(GetDTTransform());
michael@0 2215 }
michael@0 2216
michael@0 2217 Rect
michael@0 2218 gfxContext::GetAzureDeviceSpaceClipBounds()
michael@0 2219 {
michael@0 2220 unsigned int lastReset = 0;
michael@0 2221 for (int i = mStateStack.Length() - 1; i > 0; i--) {
michael@0 2222 if (mStateStack[i].clipWasReset) {
michael@0 2223 lastReset = i;
michael@0 2224 break;
michael@0 2225 }
michael@0 2226 }
michael@0 2227
michael@0 2228 Rect rect(CurrentState().deviceOffset.x, CurrentState().deviceOffset.y,
michael@0 2229 Float(mDT->GetSize().width), Float(mDT->GetSize().height));
michael@0 2230 for (unsigned int i = lastReset; i < mStateStack.Length(); i++) {
michael@0 2231 for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
michael@0 2232 AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
michael@0 2233 if (clip.path) {
michael@0 2234 Rect bounds = clip.path->GetBounds(clip.transform);
michael@0 2235 rect.IntersectRect(rect, bounds);
michael@0 2236 } else {
michael@0 2237 rect.IntersectRect(rect, clip.transform.TransformBounds(clip.rect));
michael@0 2238 }
michael@0 2239 }
michael@0 2240 }
michael@0 2241
michael@0 2242 return rect;
michael@0 2243 }
michael@0 2244
michael@0 2245 Point
michael@0 2246 gfxContext::GetDeviceOffset() const
michael@0 2247 {
michael@0 2248 return CurrentState().deviceOffset;
michael@0 2249 }
michael@0 2250
michael@0 2251 Matrix
michael@0 2252 gfxContext::GetDeviceTransform() const
michael@0 2253 {
michael@0 2254 Matrix mat;
michael@0 2255 mat.Translate(-CurrentState().deviceOffset.x, -CurrentState().deviceOffset.y);
michael@0 2256 return mat;
michael@0 2257 }
michael@0 2258
michael@0 2259 Matrix
michael@0 2260 gfxContext::GetDTTransform() const
michael@0 2261 {
michael@0 2262 Matrix mat = mTransform;
michael@0 2263 mat._31 -= CurrentState().deviceOffset.x;
michael@0 2264 mat._32 -= CurrentState().deviceOffset.y;
michael@0 2265 return mat;
michael@0 2266 }
michael@0 2267
michael@0 2268 void
michael@0 2269 gfxContext::PushNewDT(gfxContentType content)
michael@0 2270 {
michael@0 2271 Rect clipBounds = GetAzureDeviceSpaceClipBounds();
michael@0 2272 clipBounds.RoundOut();
michael@0 2273
michael@0 2274 clipBounds.width = std::max(1.0f, clipBounds.width);
michael@0 2275 clipBounds.height = std::max(1.0f, clipBounds.height);
michael@0 2276
michael@0 2277 SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content);
michael@0 2278
michael@0 2279 RefPtr<DrawTarget> newDT =
michael@0 2280 mDT->CreateSimilarDrawTarget(IntSize(int32_t(clipBounds.width), int32_t(clipBounds.height)),
michael@0 2281 format);
michael@0 2282
michael@0 2283 if (!newDT) {
michael@0 2284 NS_WARNING("Failed to create DrawTarget of sufficient size.");
michael@0 2285 newDT = mDT->CreateSimilarDrawTarget(IntSize(64, 64), format);
michael@0 2286
michael@0 2287 if (!newDT) {
michael@0 2288 // If even this fails.. we're most likely just out of memory!
michael@0 2289 NS_ABORT_OOM(BytesPerPixel(format) * 64 * 64);
michael@0 2290 }
michael@0 2291 }
michael@0 2292
michael@0 2293 Save();
michael@0 2294
michael@0 2295 CurrentState().drawTarget = newDT;
michael@0 2296 CurrentState().deviceOffset = clipBounds.TopLeft();
michael@0 2297
michael@0 2298 mDT = newDT;
michael@0 2299 }
michael@0 2300
michael@0 2301 /**
michael@0 2302 * Work out whether cairo will snap inter-glyph spacing to pixels.
michael@0 2303 *
michael@0 2304 * Layout does not align text to pixel boundaries, so, with font drawing
michael@0 2305 * backends that snap glyph positions to pixels, it is important that
michael@0 2306 * inter-glyph spacing within words is always an integer number of pixels.
michael@0 2307 * This ensures that the drawing backend snaps all of the word's glyphs in the
michael@0 2308 * same direction and so inter-glyph spacing remains the same.
michael@0 2309 */
michael@0 2310 void
michael@0 2311 gfxContext::GetRoundOffsetsToPixels(bool *aRoundX, bool *aRoundY)
michael@0 2312 {
michael@0 2313 *aRoundX = false;
michael@0 2314 // Could do something fancy here for ScaleFactors of
michael@0 2315 // AxisAlignedTransforms, but we leave things simple.
michael@0 2316 // Not much point rounding if a matrix will mess things up anyway.
michael@0 2317 // Also return false for non-cairo contexts.
michael@0 2318 if (CurrentMatrix().HasNonTranslation() || mDT) {
michael@0 2319 *aRoundY = false;
michael@0 2320 return;
michael@0 2321 }
michael@0 2322
michael@0 2323 // All raster backends snap glyphs to pixels vertically.
michael@0 2324 // Print backends set CAIRO_HINT_METRICS_OFF.
michael@0 2325 *aRoundY = true;
michael@0 2326
michael@0 2327 cairo_t *cr = GetCairo();
michael@0 2328 cairo_scaled_font_t *scaled_font = cairo_get_scaled_font(cr);
michael@0 2329 // Sometimes hint metrics gets set for us, most notably for printing.
michael@0 2330 cairo_font_options_t *font_options = cairo_font_options_create();
michael@0 2331 cairo_scaled_font_get_font_options(scaled_font, font_options);
michael@0 2332 cairo_hint_metrics_t hint_metrics =
michael@0 2333 cairo_font_options_get_hint_metrics(font_options);
michael@0 2334 cairo_font_options_destroy(font_options);
michael@0 2335
michael@0 2336 switch (hint_metrics) {
michael@0 2337 case CAIRO_HINT_METRICS_OFF:
michael@0 2338 *aRoundY = false;
michael@0 2339 return;
michael@0 2340 case CAIRO_HINT_METRICS_DEFAULT:
michael@0 2341 // Here we mimic what cairo surface/font backends do. Printing
michael@0 2342 // surfaces have already been handled by hint_metrics. The
michael@0 2343 // fallback show_glyphs implementation composites pixel-aligned
michael@0 2344 // glyph surfaces, so we just pick surface/font combinations that
michael@0 2345 // override this.
michael@0 2346 switch (cairo_scaled_font_get_type(scaled_font)) {
michael@0 2347 #if CAIRO_HAS_DWRITE_FONT // dwrite backend is not in std cairo releases yet
michael@0 2348 case CAIRO_FONT_TYPE_DWRITE:
michael@0 2349 // show_glyphs is implemented on the font and so is used for
michael@0 2350 // all surface types; however, it may pixel-snap depending on
michael@0 2351 // the dwrite rendering mode
michael@0 2352 if (!cairo_dwrite_scaled_font_get_force_GDI_classic(scaled_font) &&
michael@0 2353 gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode() ==
michael@0 2354 DWRITE_MEASURING_MODE_NATURAL) {
michael@0 2355 return;
michael@0 2356 }
michael@0 2357 #endif
michael@0 2358 case CAIRO_FONT_TYPE_QUARTZ:
michael@0 2359 // Quartz surfaces implement show_glyphs for Quartz fonts
michael@0 2360 if (cairo_surface_get_type(cairo_get_target(cr)) ==
michael@0 2361 CAIRO_SURFACE_TYPE_QUARTZ) {
michael@0 2362 return;
michael@0 2363 }
michael@0 2364 default:
michael@0 2365 break;
michael@0 2366 }
michael@0 2367 // fall through:
michael@0 2368 case CAIRO_HINT_METRICS_ON:
michael@0 2369 break;
michael@0 2370 }
michael@0 2371 *aRoundX = true;
michael@0 2372 return;
michael@0 2373 }

mercurial