1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxContext.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2373 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifdef _MSC_VER 1.10 +#define _USE_MATH_DEFINES 1.11 +#endif 1.12 +#include <math.h> 1.13 + 1.14 +#include "mozilla/Alignment.h" 1.15 + 1.16 +#include "cairo.h" 1.17 + 1.18 +#include "gfxContext.h" 1.19 + 1.20 +#include "gfxColor.h" 1.21 +#include "gfxMatrix.h" 1.22 +#include "gfxASurface.h" 1.23 +#include "gfxPattern.h" 1.24 +#include "gfxPlatform.h" 1.25 +#include "gfxTeeSurface.h" 1.26 +#include "GeckoProfiler.h" 1.27 +#include "gfx2DGlue.h" 1.28 +#include "mozilla/gfx/PathHelpers.h" 1.29 +#include <algorithm> 1.30 + 1.31 +#if CAIRO_HAS_DWRITE_FONT 1.32 +#include "gfxWindowsPlatform.h" 1.33 +#endif 1.34 + 1.35 +using namespace mozilla; 1.36 +using namespace mozilla::gfx; 1.37 + 1.38 +UserDataKey gfxContext::sDontUseAsSourceKey; 1.39 + 1.40 +/* This class lives on the stack and allows gfxContext users to easily, and 1.41 + * performantly get a gfx::Pattern to use for drawing in their current context. 1.42 + */ 1.43 +class GeneralPattern 1.44 +{ 1.45 +public: 1.46 + GeneralPattern(gfxContext *aContext) : mContext(aContext), mPattern(nullptr) {} 1.47 + ~GeneralPattern() { if (mPattern) { mPattern->~Pattern(); } } 1.48 + 1.49 + operator mozilla::gfx::Pattern&() 1.50 + { 1.51 + gfxContext::AzureState &state = mContext->CurrentState(); 1.52 + 1.53 + if (state.pattern) { 1.54 + return *state.pattern->GetPattern(mContext->mDT, state.patternTransformChanged ? &state.patternTransform : nullptr); 1.55 + } else if (state.sourceSurface) { 1.56 + Matrix transform = state.surfTransform; 1.57 + 1.58 + if (state.patternTransformChanged) { 1.59 + Matrix mat = mContext->GetDTTransform(); 1.60 + mat.Invert(); 1.61 + 1.62 + transform = transform * state.patternTransform * mat; 1.63 + } 1.64 + 1.65 + mPattern = new (mSurfacePattern.addr()) 1.66 + SurfacePattern(state.sourceSurface, ExtendMode::CLAMP, transform); 1.67 + return *mPattern; 1.68 + } else { 1.69 + mPattern = new (mColorPattern.addr()) 1.70 + ColorPattern(state.color); 1.71 + return *mPattern; 1.72 + } 1.73 + } 1.74 + 1.75 +private: 1.76 + union { 1.77 + mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern; 1.78 + mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern; 1.79 + }; 1.80 + 1.81 + gfxContext *mContext; 1.82 + Pattern *mPattern; 1.83 +}; 1.84 + 1.85 +gfxContext::gfxContext(gfxASurface *surface) 1.86 + : mRefCairo(nullptr) 1.87 + , mSurface(surface) 1.88 +{ 1.89 + MOZ_COUNT_CTOR(gfxContext); 1.90 + 1.91 + mCairo = cairo_create(surface->CairoSurface()); 1.92 + mFlags = surface->GetDefaultContextFlags(); 1.93 + if (mSurface->GetRotateForLandscape()) { 1.94 + // Rotate page 90 degrees to draw landscape page on portrait paper 1.95 + gfxIntSize size = mSurface->GetSize(); 1.96 + Translate(gfxPoint(0, size.width)); 1.97 + gfxMatrix matrix(0, -1, 1.98 + 1, 0, 1.99 + 0, 0); 1.100 + Multiply(matrix); 1.101 + } 1.102 +} 1.103 + 1.104 +gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset) 1.105 + : mPathIsRect(false) 1.106 + , mTransformChanged(false) 1.107 + , mCairo(nullptr) 1.108 + , mRefCairo(nullptr) 1.109 + , mSurface(nullptr) 1.110 + , mFlags(0) 1.111 + , mDT(aTarget) 1.112 + , mOriginalDT(aTarget) 1.113 +{ 1.114 + MOZ_COUNT_CTOR(gfxContext); 1.115 + 1.116 + mStateStack.SetLength(1); 1.117 + CurrentState().drawTarget = mDT; 1.118 + CurrentState().deviceOffset = aDeviceOffset; 1.119 + mDT->SetTransform(Matrix()); 1.120 +} 1.121 + 1.122 +/* static */ already_AddRefed<gfxContext> 1.123 +gfxContext::ContextForDrawTarget(DrawTarget* aTarget) 1.124 +{ 1.125 + Matrix transform = aTarget->GetTransform(); 1.126 + nsRefPtr<gfxContext> result = new gfxContext(aTarget); 1.127 + result->SetMatrix(ThebesMatrix(transform)); 1.128 + return result.forget(); 1.129 +} 1.130 + 1.131 +gfxContext::~gfxContext() 1.132 +{ 1.133 + if (mCairo) { 1.134 + cairo_destroy(mCairo); 1.135 + } 1.136 + if (mRefCairo) { 1.137 + cairo_destroy(mRefCairo); 1.138 + } 1.139 + if (mDT) { 1.140 + for (int i = mStateStack.Length() - 1; i >= 0; i--) { 1.141 + for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) { 1.142 + mDT->PopClip(); 1.143 + } 1.144 + 1.145 + if (mStateStack[i].clipWasReset) { 1.146 + break; 1.147 + } 1.148 + } 1.149 + mDT->Flush(); 1.150 + } 1.151 + MOZ_COUNT_DTOR(gfxContext); 1.152 +} 1.153 + 1.154 +gfxASurface * 1.155 +gfxContext::OriginalSurface() 1.156 +{ 1.157 + if (mCairo || mSurface) { 1.158 + return mSurface; 1.159 + } 1.160 + 1.161 + if (mOriginalDT && mOriginalDT->GetType() == BackendType::CAIRO) { 1.162 + cairo_surface_t *s = 1.163 + (cairo_surface_t*)mOriginalDT->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE); 1.164 + if (s) { 1.165 + mSurface = gfxASurface::Wrap(s); 1.166 + return mSurface; 1.167 + } 1.168 + } 1.169 + return nullptr; 1.170 +} 1.171 + 1.172 +already_AddRefed<gfxASurface> 1.173 +gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy) 1.174 +{ 1.175 + if (mCairo) { 1.176 + cairo_surface_t *s = cairo_get_group_target(mCairo); 1.177 + if (s == mSurface->CairoSurface()) { 1.178 + if (dx && dy) 1.179 + cairo_surface_get_device_offset(s, dx, dy); 1.180 + nsRefPtr<gfxASurface> ret = mSurface; 1.181 + return ret.forget(); 1.182 + } 1.183 + 1.184 + if (dx && dy) 1.185 + cairo_surface_get_device_offset(s, dx, dy); 1.186 + return gfxASurface::Wrap(s); 1.187 + } else { 1.188 + if (mDT->GetType() == BackendType::CAIRO) { 1.189 + cairo_surface_t *s = 1.190 + (cairo_surface_t*)mDT->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE); 1.191 + if (s) { 1.192 + if (dx && dy) { 1.193 + *dx = -CurrentState().deviceOffset.x; 1.194 + *dy = -CurrentState().deviceOffset.y; 1.195 + } 1.196 + return gfxASurface::Wrap(s); 1.197 + } 1.198 + } 1.199 + 1.200 + if (dx && dy) { 1.201 + *dx = *dy = 0; 1.202 + } 1.203 + // An Azure context doesn't have a surface backing it. 1.204 + return nullptr; 1.205 + } 1.206 +} 1.207 + 1.208 +cairo_t * 1.209 +gfxContext::GetCairo() 1.210 +{ 1.211 + if (mCairo) { 1.212 + return mCairo; 1.213 + } 1.214 + 1.215 + if (mDT->GetType() == BackendType::CAIRO) { 1.216 + cairo_t *ctx = 1.217 + (cairo_t*)mDT->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT); 1.218 + if (ctx) { 1.219 + return ctx; 1.220 + } 1.221 + } 1.222 + 1.223 + if (mRefCairo) { 1.224 + // Set transform! 1.225 + return mRefCairo; 1.226 + } 1.227 + 1.228 + mRefCairo = cairo_create(gfxPlatform::GetPlatform()->ScreenReferenceSurface()->CairoSurface()); 1.229 + 1.230 + return mRefCairo; 1.231 +} 1.232 + 1.233 +void 1.234 +gfxContext::Save() 1.235 +{ 1.236 + if (mCairo) { 1.237 + cairo_save(mCairo); 1.238 + } else { 1.239 + CurrentState().transform = mTransform; 1.240 + mStateStack.AppendElement(AzureState(CurrentState())); 1.241 + CurrentState().clipWasReset = false; 1.242 + CurrentState().pushedClips.Clear(); 1.243 + } 1.244 +} 1.245 + 1.246 +void 1.247 +gfxContext::Restore() 1.248 +{ 1.249 + if (mCairo) { 1.250 + cairo_restore(mCairo); 1.251 + } else { 1.252 + for (unsigned int c = 0; c < CurrentState().pushedClips.Length(); c++) { 1.253 + mDT->PopClip(); 1.254 + } 1.255 + 1.256 + if (CurrentState().clipWasReset && 1.257 + CurrentState().drawTarget == mStateStack[mStateStack.Length() - 2].drawTarget) { 1.258 + PushClipsToDT(mDT); 1.259 + } 1.260 + 1.261 + mStateStack.RemoveElementAt(mStateStack.Length() - 1); 1.262 + 1.263 + mDT = CurrentState().drawTarget; 1.264 + 1.265 + ChangeTransform(CurrentState().transform, false); 1.266 + } 1.267 +} 1.268 + 1.269 +// drawing 1.270 +void 1.271 +gfxContext::NewPath() 1.272 +{ 1.273 + if (mCairo) { 1.274 + cairo_new_path(mCairo); 1.275 + } else { 1.276 + mPath = nullptr; 1.277 + mPathBuilder = nullptr; 1.278 + mPathIsRect = false; 1.279 + mTransformChanged = false; 1.280 + } 1.281 +} 1.282 + 1.283 +void 1.284 +gfxContext::ClosePath() 1.285 +{ 1.286 + if (mCairo) { 1.287 + cairo_close_path(mCairo); 1.288 + } else { 1.289 + EnsurePathBuilder(); 1.290 + mPathBuilder->Close(); 1.291 + } 1.292 +} 1.293 + 1.294 +already_AddRefed<gfxPath> gfxContext::CopyPath() 1.295 +{ 1.296 + nsRefPtr<gfxPath> path; 1.297 + if (mCairo) { 1.298 + path = new gfxPath(cairo_copy_path(mCairo)); 1.299 + } else { 1.300 + EnsurePath(); 1.301 + path = new gfxPath(mPath); 1.302 + } 1.303 + return path.forget(); 1.304 +} 1.305 + 1.306 +void gfxContext::SetPath(gfxPath* path) 1.307 +{ 1.308 + if (mCairo) { 1.309 + cairo_new_path(mCairo); 1.310 + if (path->mPath->status == CAIRO_STATUS_SUCCESS && path->mPath->num_data != 0) 1.311 + cairo_append_path(mCairo, path->mPath); 1.312 + } else { 1.313 + MOZ_ASSERT(path->mMoz2DPath, "Can't mix cairo and azure paths!"); 1.314 + MOZ_ASSERT(path->mMoz2DPath->GetBackendType() == mDT->GetType()); 1.315 + mPath = path->mMoz2DPath; 1.316 + mPathBuilder = nullptr; 1.317 + mPathIsRect = false; 1.318 + mTransformChanged = false; 1.319 + } 1.320 +} 1.321 + 1.322 +gfxPoint 1.323 +gfxContext::CurrentPoint() 1.324 +{ 1.325 + if (mCairo) { 1.326 + double x, y; 1.327 + cairo_get_current_point(mCairo, &x, &y); 1.328 + return gfxPoint(x, y); 1.329 + } else { 1.330 + EnsurePathBuilder(); 1.331 + return ThebesPoint(mPathBuilder->CurrentPoint()); 1.332 + } 1.333 +} 1.334 + 1.335 +void 1.336 +gfxContext::Stroke() 1.337 +{ 1.338 + if (mCairo) { 1.339 + cairo_stroke_preserve(mCairo); 1.340 + } else { 1.341 + AzureState &state = CurrentState(); 1.342 + if (mPathIsRect) { 1.343 + MOZ_ASSERT(!mTransformChanged); 1.344 + 1.345 + mDT->StrokeRect(mRect, GeneralPattern(this), 1.346 + state.strokeOptions, 1.347 + DrawOptions(1.0f, GetOp(), state.aaMode)); 1.348 + } else { 1.349 + EnsurePath(); 1.350 + 1.351 + mDT->Stroke(mPath, GeneralPattern(this), state.strokeOptions, 1.352 + DrawOptions(1.0f, GetOp(), state.aaMode)); 1.353 + } 1.354 + } 1.355 +} 1.356 + 1.357 +void 1.358 +gfxContext::Fill() 1.359 +{ 1.360 + PROFILER_LABEL("gfxContext", "Fill"); 1.361 + if (mCairo) { 1.362 + cairo_fill_preserve(mCairo); 1.363 + } else { 1.364 + FillAzure(1.0f); 1.365 + } 1.366 +} 1.367 + 1.368 +void 1.369 +gfxContext::FillWithOpacity(gfxFloat aOpacity) 1.370 +{ 1.371 + if (mCairo) { 1.372 + // This method exists in the hope that one day cairo gets a direct 1.373 + // API for this, and then we would change this method to use that 1.374 + // API instead. 1.375 + if (aOpacity != 1.0) { 1.376 + gfxContextAutoSaveRestore saveRestore(this); 1.377 + Clip(); 1.378 + Paint(aOpacity); 1.379 + } else { 1.380 + Fill(); 1.381 + } 1.382 + } else { 1.383 + FillAzure(Float(aOpacity)); 1.384 + } 1.385 +} 1.386 + 1.387 +void 1.388 +gfxContext::MoveTo(const gfxPoint& pt) 1.389 +{ 1.390 + if (mCairo) { 1.391 + cairo_move_to(mCairo, pt.x, pt.y); 1.392 + } else { 1.393 + EnsurePathBuilder(); 1.394 + mPathBuilder->MoveTo(ToPoint(pt)); 1.395 + } 1.396 +} 1.397 + 1.398 +void 1.399 +gfxContext::NewSubPath() 1.400 +{ 1.401 + if (mCairo) { 1.402 + cairo_new_sub_path(mCairo); 1.403 + } else { 1.404 + // XXX - This has no users, we should kill it, it should be equivelant to a 1.405 + // MoveTo to the path's current point. 1.406 + } 1.407 +} 1.408 + 1.409 +void 1.410 +gfxContext::LineTo(const gfxPoint& pt) 1.411 +{ 1.412 + if (mCairo) { 1.413 + cairo_line_to(mCairo, pt.x, pt.y); 1.414 + } else { 1.415 + EnsurePathBuilder(); 1.416 + mPathBuilder->LineTo(ToPoint(pt)); 1.417 + } 1.418 +} 1.419 + 1.420 +void 1.421 +gfxContext::CurveTo(const gfxPoint& pt1, const gfxPoint& pt2, const gfxPoint& pt3) 1.422 +{ 1.423 + if (mCairo) { 1.424 + cairo_curve_to(mCairo, pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y); 1.425 + } else { 1.426 + EnsurePathBuilder(); 1.427 + mPathBuilder->BezierTo(ToPoint(pt1), ToPoint(pt2), ToPoint(pt3)); 1.428 + } 1.429 +} 1.430 + 1.431 +void 1.432 +gfxContext::QuadraticCurveTo(const gfxPoint& pt1, const gfxPoint& pt2) 1.433 +{ 1.434 + if (mCairo) { 1.435 + double cx, cy; 1.436 + cairo_get_current_point(mCairo, &cx, &cy); 1.437 + cairo_curve_to(mCairo, 1.438 + (cx + pt1.x * 2.0) / 3.0, 1.439 + (cy + pt1.y * 2.0) / 3.0, 1.440 + (pt1.x * 2.0 + pt2.x) / 3.0, 1.441 + (pt1.y * 2.0 + pt2.y) / 3.0, 1.442 + pt2.x, 1.443 + pt2.y); 1.444 + } else { 1.445 + EnsurePathBuilder(); 1.446 + mPathBuilder->QuadraticBezierTo(ToPoint(pt1), ToPoint(pt2)); 1.447 + } 1.448 +} 1.449 + 1.450 +void 1.451 +gfxContext::Arc(const gfxPoint& center, gfxFloat radius, 1.452 + gfxFloat angle1, gfxFloat angle2) 1.453 +{ 1.454 + if (mCairo) { 1.455 + cairo_arc(mCairo, center.x, center.y, radius, angle1, angle2); 1.456 + } else { 1.457 + EnsurePathBuilder(); 1.458 + mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle1), Float(angle2)); 1.459 + } 1.460 +} 1.461 + 1.462 +void 1.463 +gfxContext::NegativeArc(const gfxPoint& center, gfxFloat radius, 1.464 + gfxFloat angle1, gfxFloat angle2) 1.465 +{ 1.466 + if (mCairo) { 1.467 + cairo_arc_negative(mCairo, center.x, center.y, radius, angle1, angle2); 1.468 + } else { 1.469 + EnsurePathBuilder(); 1.470 + mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle2), Float(angle1)); 1.471 + } 1.472 +} 1.473 + 1.474 +void 1.475 +gfxContext::Line(const gfxPoint& start, const gfxPoint& end) 1.476 +{ 1.477 + if (mCairo) { 1.478 + MoveTo(start); 1.479 + LineTo(end); 1.480 + } else { 1.481 + EnsurePathBuilder(); 1.482 + mPathBuilder->MoveTo(ToPoint(start)); 1.483 + mPathBuilder->LineTo(ToPoint(end)); 1.484 + } 1.485 +} 1.486 + 1.487 +// XXX snapToPixels is only valid when snapping for filled 1.488 +// rectangles and for even-width stroked rectangles. 1.489 +// For odd-width stroked rectangles, we need to offset x/y by 1.490 +// 0.5... 1.491 +void 1.492 +gfxContext::Rectangle(const gfxRect& rect, bool snapToPixels) 1.493 +{ 1.494 + if (mCairo) { 1.495 + if (snapToPixels) { 1.496 + gfxRect snappedRect(rect); 1.497 + 1.498 + if (UserToDevicePixelSnapped(snappedRect, true)) 1.499 + { 1.500 + cairo_matrix_t mat; 1.501 + cairo_get_matrix(mCairo, &mat); 1.502 + cairo_identity_matrix(mCairo); 1.503 + Rectangle(snappedRect); 1.504 + cairo_set_matrix(mCairo, &mat); 1.505 + 1.506 + return; 1.507 + } 1.508 + } 1.509 + 1.510 + cairo_rectangle(mCairo, rect.X(), rect.Y(), rect.Width(), rect.Height()); 1.511 + } else { 1.512 + Rect rec = ToRect(rect); 1.513 + 1.514 + if (snapToPixels) { 1.515 + gfxRect newRect(rect); 1.516 + if (UserToDevicePixelSnapped(newRect, true)) { 1.517 + gfxMatrix mat = ThebesMatrix(mTransform); 1.518 + mat.Invert(); 1.519 + 1.520 + // We need the user space rect. 1.521 + rec = ToRect(mat.TransformBounds(newRect)); 1.522 + } 1.523 + } 1.524 + 1.525 + if (!mPathBuilder && !mPathIsRect) { 1.526 + mPathIsRect = true; 1.527 + mRect = rec; 1.528 + return; 1.529 + } 1.530 + 1.531 + EnsurePathBuilder(); 1.532 + 1.533 + mPathBuilder->MoveTo(rec.TopLeft()); 1.534 + mPathBuilder->LineTo(rec.TopRight()); 1.535 + mPathBuilder->LineTo(rec.BottomRight()); 1.536 + mPathBuilder->LineTo(rec.BottomLeft()); 1.537 + mPathBuilder->Close(); 1.538 + } 1.539 +} 1.540 + 1.541 +void 1.542 +gfxContext::Ellipse(const gfxPoint& center, const gfxSize& dimensions) 1.543 +{ 1.544 + gfxSize halfDim = dimensions / 2.0; 1.545 + gfxRect r(center - gfxPoint(halfDim.width, halfDim.height), dimensions); 1.546 + gfxCornerSizes c(halfDim, halfDim, halfDim, halfDim); 1.547 + 1.548 + RoundedRectangle (r, c); 1.549 +} 1.550 + 1.551 +void 1.552 +gfxContext::Polygon(const gfxPoint *points, uint32_t numPoints) 1.553 +{ 1.554 + if (mCairo) { 1.555 + if (numPoints == 0) 1.556 + return; 1.557 + 1.558 + cairo_move_to(mCairo, points[0].x, points[0].y); 1.559 + for (uint32_t i = 1; i < numPoints; ++i) { 1.560 + cairo_line_to(mCairo, points[i].x, points[i].y); 1.561 + } 1.562 + } else { 1.563 + if (numPoints == 0) { 1.564 + return; 1.565 + } 1.566 + 1.567 + EnsurePathBuilder(); 1.568 + 1.569 + mPathBuilder->MoveTo(ToPoint(points[0])); 1.570 + for (uint32_t i = 1; i < numPoints; i++) { 1.571 + mPathBuilder->LineTo(ToPoint(points[i])); 1.572 + } 1.573 + } 1.574 +} 1.575 + 1.576 +void 1.577 +gfxContext::DrawSurface(gfxASurface *surface, const gfxSize& size) 1.578 +{ 1.579 + if (mCairo) { 1.580 + cairo_save(mCairo); 1.581 + cairo_set_source_surface(mCairo, surface->CairoSurface(), 0, 0); 1.582 + cairo_new_path(mCairo); 1.583 + 1.584 + // pixel-snap this 1.585 + Rectangle(gfxRect(gfxPoint(0.0, 0.0), size), true); 1.586 + 1.587 + cairo_fill(mCairo); 1.588 + cairo_restore(mCairo); 1.589 + } else { 1.590 + // Lifetime needs to be limited here since we may wrap surface's data. 1.591 + RefPtr<SourceSurface> surf = 1.592 + gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface); 1.593 + 1.594 + if (!surf) { 1.595 + return; 1.596 + } 1.597 + 1.598 + Rect rect(0, 0, Float(size.width), Float(size.height)); 1.599 + rect.Intersect(Rect(0, 0, Float(surf->GetSize().width), Float(surf->GetSize().height))); 1.600 + 1.601 + // XXX - Should fix pixel snapping. 1.602 + mDT->DrawSurface(surf, rect, rect); 1.603 + } 1.604 +} 1.605 + 1.606 +// transform stuff 1.607 +void 1.608 +gfxContext::Translate(const gfxPoint& pt) 1.609 +{ 1.610 + if (mCairo) { 1.611 + cairo_translate(mCairo, pt.x, pt.y); 1.612 + } else { 1.613 + Matrix newMatrix = mTransform; 1.614 + 1.615 + ChangeTransform(newMatrix.Translate(Float(pt.x), Float(pt.y))); 1.616 + } 1.617 +} 1.618 + 1.619 +void 1.620 +gfxContext::Scale(gfxFloat x, gfxFloat y) 1.621 +{ 1.622 + if (mCairo) { 1.623 + cairo_scale(mCairo, x, y); 1.624 + } else { 1.625 + Matrix newMatrix = mTransform; 1.626 + 1.627 + ChangeTransform(newMatrix.Scale(Float(x), Float(y))); 1.628 + } 1.629 +} 1.630 + 1.631 +void 1.632 +gfxContext::Rotate(gfxFloat angle) 1.633 +{ 1.634 + if (mCairo) { 1.635 + cairo_rotate(mCairo, angle); 1.636 + } else { 1.637 + Matrix rotation = Matrix::Rotation(Float(angle)); 1.638 + ChangeTransform(rotation * mTransform); 1.639 + } 1.640 +} 1.641 + 1.642 +void 1.643 +gfxContext::Multiply(const gfxMatrix& matrix) 1.644 +{ 1.645 + if (mCairo) { 1.646 + const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix); 1.647 + cairo_transform(mCairo, &mat); 1.648 + } else { 1.649 + ChangeTransform(ToMatrix(matrix) * mTransform); 1.650 + } 1.651 +} 1.652 + 1.653 +void 1.654 +gfxContext::MultiplyAndNudgeToIntegers(const gfxMatrix& matrix) 1.655 +{ 1.656 + if (mCairo) { 1.657 + const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix); 1.658 + cairo_transform(mCairo, &mat); 1.659 + // XXX nudging to integers not currently supported for Thebes 1.660 + } else { 1.661 + Matrix transform = ToMatrix(matrix) * mTransform; 1.662 + transform.NudgeToIntegers(); 1.663 + ChangeTransform(transform); 1.664 + } 1.665 +} 1.666 + 1.667 +void 1.668 +gfxContext::SetMatrix(const gfxMatrix& matrix) 1.669 +{ 1.670 + if (mCairo) { 1.671 + const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix); 1.672 + cairo_set_matrix(mCairo, &mat); 1.673 + } else { 1.674 + ChangeTransform(ToMatrix(matrix)); 1.675 + } 1.676 +} 1.677 + 1.678 +void 1.679 +gfxContext::IdentityMatrix() 1.680 +{ 1.681 + if (mCairo) { 1.682 + cairo_identity_matrix(mCairo); 1.683 + } else { 1.684 + ChangeTransform(Matrix()); 1.685 + } 1.686 +} 1.687 + 1.688 +gfxMatrix 1.689 +gfxContext::CurrentMatrix() const 1.690 +{ 1.691 + if (mCairo) { 1.692 + cairo_matrix_t mat; 1.693 + cairo_get_matrix(mCairo, &mat); 1.694 + return gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat)); 1.695 + } else { 1.696 + return ThebesMatrix(mTransform); 1.697 + } 1.698 +} 1.699 + 1.700 +void 1.701 +gfxContext::NudgeCurrentMatrixToIntegers() 1.702 +{ 1.703 + if (mCairo) { 1.704 + cairo_matrix_t mat; 1.705 + cairo_get_matrix(mCairo, &mat); 1.706 + gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat)).NudgeToIntegers(); 1.707 + cairo_set_matrix(mCairo, &mat); 1.708 + } else { 1.709 + gfxMatrix matrix = ThebesMatrix(mTransform); 1.710 + matrix.NudgeToIntegers(); 1.711 + ChangeTransform(ToMatrix(matrix)); 1.712 + } 1.713 +} 1.714 + 1.715 +gfxPoint 1.716 +gfxContext::DeviceToUser(const gfxPoint& point) const 1.717 +{ 1.718 + if (mCairo) { 1.719 + gfxPoint ret = point; 1.720 + cairo_device_to_user(mCairo, &ret.x, &ret.y); 1.721 + return ret; 1.722 + } else { 1.723 + Matrix matrix = mTransform; 1.724 + 1.725 + matrix.Invert(); 1.726 + 1.727 + return ThebesPoint(matrix * ToPoint(point)); 1.728 + } 1.729 +} 1.730 + 1.731 +gfxSize 1.732 +gfxContext::DeviceToUser(const gfxSize& size) const 1.733 +{ 1.734 + if (mCairo) { 1.735 + gfxSize ret = size; 1.736 + cairo_device_to_user_distance(mCairo, &ret.width, &ret.height); 1.737 + return ret; 1.738 + } else { 1.739 + Matrix matrix = mTransform; 1.740 + 1.741 + matrix.Invert(); 1.742 + 1.743 + return ThebesSize(matrix * ToSize(size)); 1.744 + } 1.745 +} 1.746 + 1.747 +gfxRect 1.748 +gfxContext::DeviceToUser(const gfxRect& rect) const 1.749 +{ 1.750 + if (mCairo) { 1.751 + gfxRect ret = rect; 1.752 + cairo_device_to_user(mCairo, &ret.x, &ret.y); 1.753 + cairo_device_to_user_distance(mCairo, &ret.width, &ret.height); 1.754 + return ret; 1.755 + } else { 1.756 + Matrix matrix = mTransform; 1.757 + 1.758 + matrix.Invert(); 1.759 + 1.760 + return ThebesRect(matrix.TransformBounds(ToRect(rect))); 1.761 + } 1.762 +} 1.763 + 1.764 +gfxPoint 1.765 +gfxContext::UserToDevice(const gfxPoint& point) const 1.766 +{ 1.767 + if (mCairo) { 1.768 + gfxPoint ret = point; 1.769 + cairo_user_to_device(mCairo, &ret.x, &ret.y); 1.770 + return ret; 1.771 + } else { 1.772 + return ThebesPoint(mTransform * ToPoint(point)); 1.773 + } 1.774 +} 1.775 + 1.776 +gfxSize 1.777 +gfxContext::UserToDevice(const gfxSize& size) const 1.778 +{ 1.779 + if (mCairo) { 1.780 + gfxSize ret = size; 1.781 + cairo_user_to_device_distance(mCairo, &ret.width, &ret.height); 1.782 + return ret; 1.783 + } else { 1.784 + const Matrix &matrix = mTransform; 1.785 + 1.786 + gfxSize newSize; 1.787 + newSize.width = size.width * matrix._11 + size.height * matrix._12; 1.788 + newSize.height = size.width * matrix._21 + size.height * matrix._22; 1.789 + return newSize; 1.790 + } 1.791 +} 1.792 + 1.793 +gfxRect 1.794 +gfxContext::UserToDevice(const gfxRect& rect) const 1.795 +{ 1.796 + if (mCairo) { 1.797 + double xmin = rect.X(), ymin = rect.Y(), xmax = rect.XMost(), ymax = rect.YMost(); 1.798 + 1.799 + double x[3], y[3]; 1.800 + x[0] = xmin; y[0] = ymax; 1.801 + x[1] = xmax; y[1] = ymax; 1.802 + x[2] = xmax; y[2] = ymin; 1.803 + 1.804 + cairo_user_to_device(mCairo, &xmin, &ymin); 1.805 + xmax = xmin; 1.806 + ymax = ymin; 1.807 + for (int i = 0; i < 3; i++) { 1.808 + cairo_user_to_device(mCairo, &x[i], &y[i]); 1.809 + xmin = std::min(xmin, x[i]); 1.810 + xmax = std::max(xmax, x[i]); 1.811 + ymin = std::min(ymin, y[i]); 1.812 + ymax = std::max(ymax, y[i]); 1.813 + } 1.814 + 1.815 + return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin); 1.816 + } else { 1.817 + const Matrix &matrix = mTransform; 1.818 + return ThebesRect(matrix.TransformBounds(ToRect(rect))); 1.819 + } 1.820 +} 1.821 + 1.822 +bool 1.823 +gfxContext::UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale) const 1.824 +{ 1.825 + if (GetFlags() & FLAG_DISABLE_SNAPPING) 1.826 + return false; 1.827 + 1.828 + // if we're not at 1.0 scale, don't snap, unless we're 1.829 + // ignoring the scale. If we're not -just- a scale, 1.830 + // never snap. 1.831 + const gfxFloat epsilon = 0.0000001; 1.832 +#define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon) 1.833 + if (mCairo) { 1.834 + cairo_matrix_t mat; 1.835 + cairo_get_matrix(mCairo, &mat); 1.836 + if (!ignoreScale && 1.837 + (!WITHIN_E(mat.xx,1.0) || !WITHIN_E(mat.yy,1.0) || 1.838 + !WITHIN_E(mat.xy,0.0) || !WITHIN_E(mat.yx,0.0))) 1.839 + return false; 1.840 + } else { 1.841 + Matrix mat = mTransform; 1.842 + if (!ignoreScale && 1.843 + (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) || 1.844 + !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0))) 1.845 + return false; 1.846 + } 1.847 +#undef WITHIN_E 1.848 + 1.849 + gfxPoint p1 = UserToDevice(rect.TopLeft()); 1.850 + gfxPoint p2 = UserToDevice(rect.TopRight()); 1.851 + gfxPoint p3 = UserToDevice(rect.BottomRight()); 1.852 + 1.853 + // Check that the rectangle is axis-aligned. For an axis-aligned rectangle, 1.854 + // two opposite corners define the entire rectangle. So check if 1.855 + // the axis-aligned rectangle with opposite corners p1 and p3 1.856 + // define an axis-aligned rectangle whose other corners are p2 and p4. 1.857 + // We actually only need to check one of p2 and p4, since an affine 1.858 + // transform maps parallelograms to parallelograms. 1.859 + if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) { 1.860 + p1.Round(); 1.861 + p3.Round(); 1.862 + 1.863 + rect.MoveTo(gfxPoint(std::min(p1.x, p3.x), std::min(p1.y, p3.y))); 1.864 + rect.SizeTo(gfxSize(std::max(p1.x, p3.x) - rect.X(), 1.865 + std::max(p1.y, p3.y) - rect.Y())); 1.866 + return true; 1.867 + } 1.868 + 1.869 + return false; 1.870 +} 1.871 + 1.872 +bool 1.873 +gfxContext::UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale) const 1.874 +{ 1.875 + if (GetFlags() & FLAG_DISABLE_SNAPPING) 1.876 + return false; 1.877 + 1.878 + // if we're not at 1.0 scale, don't snap, unless we're 1.879 + // ignoring the scale. If we're not -just- a scale, 1.880 + // never snap. 1.881 + const gfxFloat epsilon = 0.0000001; 1.882 +#define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon) 1.883 + if (mCairo) { 1.884 + cairo_matrix_t mat; 1.885 + cairo_get_matrix(mCairo, &mat); 1.886 + if (!ignoreScale && 1.887 + (!WITHIN_E(mat.xx,1.0) || !WITHIN_E(mat.yy,1.0) || 1.888 + !WITHIN_E(mat.xy,0.0) || !WITHIN_E(mat.yx,0.0))) 1.889 + return false; 1.890 + } else { 1.891 + Matrix mat = mTransform; 1.892 + if (!ignoreScale && 1.893 + (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) || 1.894 + !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0))) 1.895 + return false; 1.896 + } 1.897 +#undef WITHIN_E 1.898 + 1.899 + pt = UserToDevice(pt); 1.900 + pt.Round(); 1.901 + return true; 1.902 +} 1.903 + 1.904 +void 1.905 +gfxContext::PixelSnappedRectangleAndSetPattern(const gfxRect& rect, 1.906 + gfxPattern *pattern) 1.907 +{ 1.908 + gfxRect r(rect); 1.909 + 1.910 + // Bob attempts to pixel-snap the rectangle, and returns true if 1.911 + // the snapping succeeds. If it does, we need to set up an 1.912 + // identity matrix, because the rectangle given back is in device 1.913 + // coordinates. 1.914 + // 1.915 + // We then have to call a translate to dr.pos afterwards, to make 1.916 + // sure the image lines up in the right place with our pixel 1.917 + // snapped rectangle. 1.918 + // 1.919 + // If snapping wasn't successful, we just translate to where the 1.920 + // pattern would normally start (in app coordinates) and do the 1.921 + // same thing. 1.922 + Rectangle(r, true); 1.923 + SetPattern(pattern); 1.924 +} 1.925 + 1.926 +void 1.927 +gfxContext::SetAntialiasMode(AntialiasMode mode) 1.928 +{ 1.929 + if (mCairo) { 1.930 + if (mode == MODE_ALIASED) { 1.931 + cairo_set_antialias(mCairo, CAIRO_ANTIALIAS_NONE); 1.932 + } else if (mode == MODE_COVERAGE) { 1.933 + cairo_set_antialias(mCairo, CAIRO_ANTIALIAS_DEFAULT); 1.934 + } 1.935 + } else { 1.936 + if (mode == MODE_ALIASED) { 1.937 + CurrentState().aaMode = gfx::AntialiasMode::NONE; 1.938 + } else if (mode == MODE_COVERAGE) { 1.939 + CurrentState().aaMode = gfx::AntialiasMode::SUBPIXEL; 1.940 + } 1.941 + } 1.942 +} 1.943 + 1.944 +gfxContext::AntialiasMode 1.945 +gfxContext::CurrentAntialiasMode() const 1.946 +{ 1.947 + if (mCairo) { 1.948 + cairo_antialias_t aa = cairo_get_antialias(mCairo); 1.949 + if (aa == CAIRO_ANTIALIAS_NONE) 1.950 + return MODE_ALIASED; 1.951 + return MODE_COVERAGE; 1.952 + } else { 1.953 + if (CurrentState().aaMode == gfx::AntialiasMode::NONE) { 1.954 + return MODE_ALIASED; 1.955 + } 1.956 + return MODE_COVERAGE; 1.957 + } 1.958 +} 1.959 + 1.960 +void 1.961 +gfxContext::SetDash(gfxLineType ltype) 1.962 +{ 1.963 + static double dash[] = {5.0, 5.0}; 1.964 + static double dot[] = {1.0, 1.0}; 1.965 + 1.966 + switch (ltype) { 1.967 + case gfxLineDashed: 1.968 + SetDash(dash, 2, 0.0); 1.969 + break; 1.970 + case gfxLineDotted: 1.971 + SetDash(dot, 2, 0.0); 1.972 + break; 1.973 + case gfxLineSolid: 1.974 + default: 1.975 + SetDash(nullptr, 0, 0.0); 1.976 + break; 1.977 + } 1.978 +} 1.979 + 1.980 +void 1.981 +gfxContext::SetDash(gfxFloat *dashes, int ndash, gfxFloat offset) 1.982 +{ 1.983 + if (mCairo) { 1.984 + cairo_set_dash(mCairo, dashes, ndash, offset); 1.985 + } else { 1.986 + AzureState &state = CurrentState(); 1.987 + 1.988 + state.dashPattern.SetLength(ndash); 1.989 + for (int i = 0; i < ndash; i++) { 1.990 + state.dashPattern[i] = Float(dashes[i]); 1.991 + } 1.992 + state.strokeOptions.mDashLength = ndash; 1.993 + state.strokeOptions.mDashOffset = Float(offset); 1.994 + state.strokeOptions.mDashPattern = ndash ? state.dashPattern.Elements() 1.995 + : nullptr; 1.996 + } 1.997 +} 1.998 + 1.999 +bool 1.1000 +gfxContext::CurrentDash(FallibleTArray<gfxFloat>& dashes, gfxFloat* offset) const 1.1001 +{ 1.1002 + if (mCairo) { 1.1003 + int count = cairo_get_dash_count(mCairo); 1.1004 + if (count <= 0 || !dashes.SetLength(count)) { 1.1005 + return false; 1.1006 + } 1.1007 + cairo_get_dash(mCairo, dashes.Elements(), offset); 1.1008 + return true; 1.1009 + } else { 1.1010 + const AzureState &state = CurrentState(); 1.1011 + int count = state.strokeOptions.mDashLength; 1.1012 + 1.1013 + if (count <= 0 || !dashes.SetLength(count)) { 1.1014 + return false; 1.1015 + } 1.1016 + 1.1017 + for (int i = 0; i < count; i++) { 1.1018 + dashes[i] = state.dashPattern[i]; 1.1019 + } 1.1020 + 1.1021 + *offset = state.strokeOptions.mDashOffset; 1.1022 + 1.1023 + return true; 1.1024 + } 1.1025 +} 1.1026 + 1.1027 +gfxFloat 1.1028 +gfxContext::CurrentDashOffset() const 1.1029 +{ 1.1030 + if (mCairo) { 1.1031 + if (cairo_get_dash_count(mCairo) <= 0) { 1.1032 + return 0.0; 1.1033 + } 1.1034 + gfxFloat offset; 1.1035 + cairo_get_dash(mCairo, nullptr, &offset); 1.1036 + return offset; 1.1037 + } else { 1.1038 + return CurrentState().strokeOptions.mDashOffset; 1.1039 + } 1.1040 +} 1.1041 + 1.1042 +void 1.1043 +gfxContext::SetLineWidth(gfxFloat width) 1.1044 +{ 1.1045 + if (mCairo) { 1.1046 + cairo_set_line_width(mCairo, width); 1.1047 + } else { 1.1048 + CurrentState().strokeOptions.mLineWidth = Float(width); 1.1049 + } 1.1050 +} 1.1051 + 1.1052 +gfxFloat 1.1053 +gfxContext::CurrentLineWidth() const 1.1054 +{ 1.1055 + if (mCairo) { 1.1056 + return cairo_get_line_width(mCairo); 1.1057 + } else { 1.1058 + return CurrentState().strokeOptions.mLineWidth; 1.1059 + } 1.1060 +} 1.1061 + 1.1062 +void 1.1063 +gfxContext::SetOperator(GraphicsOperator op) 1.1064 +{ 1.1065 + if (mCairo) { 1.1066 + if (mFlags & FLAG_SIMPLIFY_OPERATORS) { 1.1067 + if (op != OPERATOR_SOURCE && 1.1068 + op != OPERATOR_CLEAR && 1.1069 + op != OPERATOR_OVER) 1.1070 + op = OPERATOR_OVER; 1.1071 + } 1.1072 + 1.1073 + cairo_set_operator(mCairo, (cairo_operator_t)op); 1.1074 + } else { 1.1075 + if (op == OPERATOR_CLEAR) { 1.1076 + CurrentState().opIsClear = true; 1.1077 + return; 1.1078 + } 1.1079 + CurrentState().opIsClear = false; 1.1080 + CurrentState().op = CompositionOpForOp(op); 1.1081 + } 1.1082 +} 1.1083 + 1.1084 +gfxContext::GraphicsOperator 1.1085 +gfxContext::CurrentOperator() const 1.1086 +{ 1.1087 + if (mCairo) { 1.1088 + return (GraphicsOperator)cairo_get_operator(mCairo); 1.1089 + } else { 1.1090 + return ThebesOp(CurrentState().op); 1.1091 + } 1.1092 +} 1.1093 + 1.1094 +void 1.1095 +gfxContext::SetLineCap(GraphicsLineCap cap) 1.1096 +{ 1.1097 + if (mCairo) { 1.1098 + cairo_set_line_cap(mCairo, (cairo_line_cap_t)cap); 1.1099 + } else { 1.1100 + CurrentState().strokeOptions.mLineCap = ToCapStyle(cap); 1.1101 + } 1.1102 +} 1.1103 + 1.1104 +gfxContext::GraphicsLineCap 1.1105 +gfxContext::CurrentLineCap() const 1.1106 +{ 1.1107 + if (mCairo) { 1.1108 + return (GraphicsLineCap)cairo_get_line_cap(mCairo); 1.1109 + } else { 1.1110 + return ThebesLineCap(CurrentState().strokeOptions.mLineCap); 1.1111 + } 1.1112 +} 1.1113 + 1.1114 +void 1.1115 +gfxContext::SetLineJoin(GraphicsLineJoin join) 1.1116 +{ 1.1117 + if (mCairo) { 1.1118 + cairo_set_line_join(mCairo, (cairo_line_join_t)join); 1.1119 + } else { 1.1120 + CurrentState().strokeOptions.mLineJoin = ToJoinStyle(join); 1.1121 + } 1.1122 +} 1.1123 + 1.1124 +gfxContext::GraphicsLineJoin 1.1125 +gfxContext::CurrentLineJoin() const 1.1126 +{ 1.1127 + if (mCairo) { 1.1128 + return (GraphicsLineJoin)cairo_get_line_join(mCairo); 1.1129 + } else { 1.1130 + return ThebesLineJoin(CurrentState().strokeOptions.mLineJoin); 1.1131 + } 1.1132 +} 1.1133 + 1.1134 +void 1.1135 +gfxContext::SetMiterLimit(gfxFloat limit) 1.1136 +{ 1.1137 + if (mCairo) { 1.1138 + cairo_set_miter_limit(mCairo, limit); 1.1139 + } else { 1.1140 + CurrentState().strokeOptions.mMiterLimit = Float(limit); 1.1141 + } 1.1142 +} 1.1143 + 1.1144 +gfxFloat 1.1145 +gfxContext::CurrentMiterLimit() const 1.1146 +{ 1.1147 + if (mCairo) { 1.1148 + return cairo_get_miter_limit(mCairo); 1.1149 + } else { 1.1150 + return CurrentState().strokeOptions.mMiterLimit; 1.1151 + } 1.1152 +} 1.1153 + 1.1154 +void 1.1155 +gfxContext::SetFillRule(FillRule rule) 1.1156 +{ 1.1157 + if (mCairo) { 1.1158 + cairo_set_fill_rule(mCairo, (cairo_fill_rule_t)rule); 1.1159 + } else { 1.1160 + CurrentState().fillRule = rule == FILL_RULE_WINDING ? gfx::FillRule::FILL_WINDING : gfx::FillRule::FILL_EVEN_ODD; 1.1161 + } 1.1162 +} 1.1163 + 1.1164 +gfxContext::FillRule 1.1165 +gfxContext::CurrentFillRule() const 1.1166 +{ 1.1167 + if (mCairo) { 1.1168 + return (FillRule)cairo_get_fill_rule(mCairo); 1.1169 + } else { 1.1170 + return FILL_RULE_WINDING; 1.1171 + } 1.1172 +} 1.1173 + 1.1174 +// clipping 1.1175 +void 1.1176 +gfxContext::Clip(const gfxRect& rect) 1.1177 +{ 1.1178 + if (mCairo) { 1.1179 + cairo_new_path(mCairo); 1.1180 + cairo_rectangle(mCairo, rect.X(), rect.Y(), rect.Width(), rect.Height()); 1.1181 + cairo_clip(mCairo); 1.1182 + } else { 1.1183 + AzureState::PushedClip clip = { nullptr, ToRect(rect), mTransform }; 1.1184 + CurrentState().pushedClips.AppendElement(clip); 1.1185 + mDT->PushClipRect(ToRect(rect)); 1.1186 + NewPath(); 1.1187 + } 1.1188 +} 1.1189 + 1.1190 +void 1.1191 +gfxContext::Clip() 1.1192 +{ 1.1193 + if (mCairo) { 1.1194 + cairo_clip_preserve(mCairo); 1.1195 + } else { 1.1196 + if (mPathIsRect) { 1.1197 + MOZ_ASSERT(!mTransformChanged); 1.1198 + 1.1199 + AzureState::PushedClip clip = { nullptr, mRect, mTransform }; 1.1200 + CurrentState().pushedClips.AppendElement(clip); 1.1201 + mDT->PushClipRect(mRect); 1.1202 + } else { 1.1203 + EnsurePath(); 1.1204 + mDT->PushClip(mPath); 1.1205 + AzureState::PushedClip clip = { mPath, Rect(), mTransform }; 1.1206 + CurrentState().pushedClips.AppendElement(clip); 1.1207 + } 1.1208 + } 1.1209 +} 1.1210 + 1.1211 +void 1.1212 +gfxContext::ResetClip() 1.1213 +{ 1.1214 + if (mCairo) { 1.1215 + cairo_reset_clip(mCairo); 1.1216 + } else { 1.1217 + for (int i = mStateStack.Length() - 1; i >= 0; i--) { 1.1218 + for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) { 1.1219 + mDT->PopClip(); 1.1220 + } 1.1221 + 1.1222 + if (mStateStack[i].clipWasReset) { 1.1223 + break; 1.1224 + } 1.1225 + } 1.1226 + CurrentState().pushedClips.Clear(); 1.1227 + CurrentState().clipWasReset = true; 1.1228 + } 1.1229 +} 1.1230 + 1.1231 +void 1.1232 +gfxContext::UpdateSurfaceClip() 1.1233 +{ 1.1234 + if (mCairo) { 1.1235 + NewPath(); 1.1236 + // we paint an empty rectangle to ensure the clip is propagated to 1.1237 + // the destination surface 1.1238 + SetDeviceColor(gfxRGBA(0,0,0,0)); 1.1239 + Rectangle(gfxRect(0,1,1,0)); 1.1240 + Fill(); 1.1241 + } 1.1242 +} 1.1243 + 1.1244 +gfxRect 1.1245 +gfxContext::GetClipExtents() 1.1246 +{ 1.1247 + if (mCairo) { 1.1248 + double xmin, ymin, xmax, ymax; 1.1249 + cairo_clip_extents(mCairo, &xmin, &ymin, &xmax, &ymax); 1.1250 + return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin); 1.1251 + } else { 1.1252 + Rect rect = GetAzureDeviceSpaceClipBounds(); 1.1253 + 1.1254 + if (rect.width == 0 || rect.height == 0) { 1.1255 + return gfxRect(0, 0, 0, 0); 1.1256 + } 1.1257 + 1.1258 + Matrix mat = mTransform; 1.1259 + mat.Invert(); 1.1260 + rect = mat.TransformBounds(rect); 1.1261 + 1.1262 + return ThebesRect(rect); 1.1263 + } 1.1264 +} 1.1265 + 1.1266 +bool 1.1267 +gfxContext::ClipContainsRect(const gfxRect& aRect) 1.1268 +{ 1.1269 + if (mCairo) { 1.1270 + cairo_rectangle_list_t *clip = 1.1271 + cairo_copy_clip_rectangle_list(mCairo); 1.1272 + 1.1273 + bool result = false; 1.1274 + 1.1275 + if (clip->status == CAIRO_STATUS_SUCCESS) { 1.1276 + for (int i = 0; i < clip->num_rectangles; i++) { 1.1277 + gfxRect rect(clip->rectangles[i].x, clip->rectangles[i].y, 1.1278 + clip->rectangles[i].width, clip->rectangles[i].height); 1.1279 + if (rect.Contains(aRect)) { 1.1280 + result = true; 1.1281 + break; 1.1282 + } 1.1283 + } 1.1284 + } 1.1285 + 1.1286 + cairo_rectangle_list_destroy(clip); 1.1287 + return result; 1.1288 + } else { 1.1289 + unsigned int lastReset = 0; 1.1290 + for (int i = mStateStack.Length() - 2; i > 0; i--) { 1.1291 + if (mStateStack[i].clipWasReset) { 1.1292 + lastReset = i; 1.1293 + break; 1.1294 + } 1.1295 + } 1.1296 + 1.1297 + // Since we always return false when the clip list contains a 1.1298 + // non-rectangular clip or a non-rectilinear transform, our 'total' clip 1.1299 + // is always a rectangle if we hit the end of this function. 1.1300 + Rect clipBounds(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height)); 1.1301 + 1.1302 + for (unsigned int i = lastReset; i < mStateStack.Length(); i++) { 1.1303 + for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) { 1.1304 + AzureState::PushedClip &clip = mStateStack[i].pushedClips[c]; 1.1305 + if (clip.path || !clip.transform.IsRectilinear()) { 1.1306 + // Cairo behavior is we return false if the clip contains a non- 1.1307 + // rectangle. 1.1308 + return false; 1.1309 + } else { 1.1310 + Rect clipRect = mTransform.TransformBounds(clip.rect); 1.1311 + 1.1312 + clipBounds.IntersectRect(clipBounds, clipRect); 1.1313 + } 1.1314 + } 1.1315 + } 1.1316 + 1.1317 + return clipBounds.Contains(ToRect(aRect)); 1.1318 + } 1.1319 +} 1.1320 + 1.1321 +// rendering sources 1.1322 + 1.1323 +void 1.1324 +gfxContext::SetColor(const gfxRGBA& c) 1.1325 +{ 1.1326 + if (mCairo) { 1.1327 + if (gfxPlatform::GetCMSMode() == eCMSMode_All) { 1.1328 + 1.1329 + gfxRGBA cms; 1.1330 + qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); 1.1331 + if (transform) 1.1332 + gfxPlatform::TransformPixel(c, cms, transform); 1.1333 + 1.1334 + // Use the original alpha to avoid unnecessary float->byte->float 1.1335 + // conversion errors 1.1336 + cairo_set_source_rgba(mCairo, cms.r, cms.g, cms.b, c.a); 1.1337 + } 1.1338 + else 1.1339 + cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a); 1.1340 + } else { 1.1341 + CurrentState().pattern = nullptr; 1.1342 + CurrentState().sourceSurfCairo = nullptr; 1.1343 + CurrentState().sourceSurface = nullptr; 1.1344 + 1.1345 + if (gfxPlatform::GetCMSMode() == eCMSMode_All) { 1.1346 + 1.1347 + gfxRGBA cms; 1.1348 + qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); 1.1349 + if (transform) 1.1350 + gfxPlatform::TransformPixel(c, cms, transform); 1.1351 + 1.1352 + // Use the original alpha to avoid unnecessary float->byte->float 1.1353 + // conversion errors 1.1354 + CurrentState().color = ToColor(cms); 1.1355 + } 1.1356 + else 1.1357 + CurrentState().color = ToColor(c); 1.1358 + } 1.1359 +} 1.1360 + 1.1361 +void 1.1362 +gfxContext::SetDeviceColor(const gfxRGBA& c) 1.1363 +{ 1.1364 + if (mCairo) { 1.1365 + cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a); 1.1366 + } else { 1.1367 + CurrentState().pattern = nullptr; 1.1368 + CurrentState().sourceSurfCairo = nullptr; 1.1369 + CurrentState().sourceSurface = nullptr; 1.1370 + CurrentState().color = ToColor(c); 1.1371 + } 1.1372 +} 1.1373 + 1.1374 +bool 1.1375 +gfxContext::GetDeviceColor(gfxRGBA& c) 1.1376 +{ 1.1377 + if (mCairo) { 1.1378 + return cairo_pattern_get_rgba(cairo_get_source(mCairo), 1.1379 + &c.r, 1.1380 + &c.g, 1.1381 + &c.b, 1.1382 + &c.a) == CAIRO_STATUS_SUCCESS; 1.1383 + } else { 1.1384 + if (CurrentState().sourceSurface) { 1.1385 + return false; 1.1386 + } 1.1387 + if (CurrentState().pattern) { 1.1388 + gfxRGBA color; 1.1389 + return CurrentState().pattern->GetSolidColor(c); 1.1390 + } 1.1391 + 1.1392 + c = ThebesRGBA(CurrentState().color); 1.1393 + return true; 1.1394 + } 1.1395 +} 1.1396 + 1.1397 +void 1.1398 +gfxContext::SetSource(gfxASurface *surface, const gfxPoint& offset) 1.1399 +{ 1.1400 + if (mCairo) { 1.1401 + NS_ASSERTION(surface->GetAllowUseAsSource(), "Surface not allowed to be used as source!"); 1.1402 + cairo_set_source_surface(mCairo, surface->CairoSurface(), offset.x, offset.y); 1.1403 + } else { 1.1404 + CurrentState().surfTransform = Matrix(1.0f, 0, 0, 1.0f, Float(offset.x), Float(offset.y)); 1.1405 + CurrentState().pattern = nullptr; 1.1406 + CurrentState().patternTransformChanged = false; 1.1407 + // Keep the underlying cairo surface around while we keep the 1.1408 + // sourceSurface. 1.1409 + CurrentState().sourceSurfCairo = surface; 1.1410 + CurrentState().sourceSurface = 1.1411 + gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface); 1.1412 + CurrentState().color = Color(0, 0, 0, 0); 1.1413 + } 1.1414 +} 1.1415 + 1.1416 +void 1.1417 +gfxContext::SetPattern(gfxPattern *pattern) 1.1418 +{ 1.1419 + if (mCairo) { 1.1420 + MOZ_ASSERT(!pattern->IsAzure()); 1.1421 + cairo_set_source(mCairo, pattern->CairoPattern()); 1.1422 + } else { 1.1423 + CurrentState().sourceSurfCairo = nullptr; 1.1424 + CurrentState().sourceSurface = nullptr; 1.1425 + CurrentState().patternTransformChanged = false; 1.1426 + CurrentState().pattern = pattern; 1.1427 + } 1.1428 +} 1.1429 + 1.1430 +already_AddRefed<gfxPattern> 1.1431 +gfxContext::GetPattern() 1.1432 +{ 1.1433 + if (mCairo) { 1.1434 + cairo_pattern_t *pat = cairo_get_source(mCairo); 1.1435 + NS_ASSERTION(pat, "I was told this couldn't be null"); 1.1436 + 1.1437 + nsRefPtr<gfxPattern> wrapper; 1.1438 + if (pat) 1.1439 + wrapper = new gfxPattern(pat); 1.1440 + else 1.1441 + wrapper = new gfxPattern(gfxRGBA(0,0,0,0)); 1.1442 + 1.1443 + return wrapper.forget(); 1.1444 + } else { 1.1445 + nsRefPtr<gfxPattern> pat; 1.1446 + 1.1447 + AzureState &state = CurrentState(); 1.1448 + if (state.pattern) { 1.1449 + pat = state.pattern; 1.1450 + } else if (state.sourceSurface) { 1.1451 + NS_ASSERTION(false, "Ugh, this isn't good."); 1.1452 + } else { 1.1453 + pat = new gfxPattern(ThebesRGBA(state.color)); 1.1454 + } 1.1455 + return pat.forget(); 1.1456 + } 1.1457 +} 1.1458 + 1.1459 + 1.1460 +// masking 1.1461 +void 1.1462 +gfxContext::Mask(gfxPattern *pattern) 1.1463 +{ 1.1464 + if (mCairo) { 1.1465 + MOZ_ASSERT(!pattern->IsAzure()); 1.1466 + cairo_mask(mCairo, pattern->CairoPattern()); 1.1467 + } else { 1.1468 + if (pattern->Extend() == gfxPattern::EXTEND_NONE) { 1.1469 + // In this situation the mask will be fully transparent (i.e. nothing 1.1470 + // will be drawn) outside of the bounds of the surface. We can support 1.1471 + // that by clipping out drawing to that area. 1.1472 + Point offset; 1.1473 + if (pattern->IsAzure()) { 1.1474 + // This is an Azure pattern. i.e. this was the result of a PopGroup and 1.1475 + // then the extend mode was changed to EXTEND_NONE. 1.1476 + // XXX - We may need some additional magic here in theory to support 1.1477 + // device offsets in these patterns, but no problems have been observed 1.1478 + // yet because of this. And it would complicate things a little further. 1.1479 + offset = Point(0.f, 0.f); 1.1480 + } else if (pattern->GetType() == gfxPattern::PATTERN_SURFACE) { 1.1481 + nsRefPtr<gfxASurface> asurf = pattern->GetSurface(); 1.1482 + gfxPoint deviceOffset = asurf->GetDeviceOffset(); 1.1483 + offset = Point(-deviceOffset.x, -deviceOffset.y); 1.1484 + 1.1485 + // this lets GetAzureSurface work 1.1486 + pattern->GetPattern(mDT); 1.1487 + } 1.1488 + 1.1489 + if (pattern->IsAzure() || pattern->GetType() == gfxPattern::PATTERN_SURFACE) { 1.1490 + RefPtr<SourceSurface> mask = pattern->GetAzureSurface(); 1.1491 + Matrix mat = ToMatrix(pattern->GetInverseMatrix()); 1.1492 + Matrix old = mTransform; 1.1493 + // add in the inverse of the pattern transform so that when we 1.1494 + // MaskSurface we are transformed to the place matching the pattern transform 1.1495 + mat = mat * mTransform; 1.1496 + 1.1497 + ChangeTransform(mat); 1.1498 + mDT->MaskSurface(GeneralPattern(this), mask, offset, DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode)); 1.1499 + ChangeTransform(old); 1.1500 + return; 1.1501 + } 1.1502 + } 1.1503 + mDT->Mask(GeneralPattern(this), *pattern->GetPattern(mDT), DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode)); 1.1504 + } 1.1505 +} 1.1506 + 1.1507 +void 1.1508 +gfxContext::Mask(gfxASurface *surface, const gfxPoint& offset) 1.1509 +{ 1.1510 + PROFILER_LABEL("gfxContext", "Mask"); 1.1511 + if (mCairo) { 1.1512 + cairo_mask_surface(mCairo, surface->CairoSurface(), offset.x, offset.y); 1.1513 + } else { 1.1514 + // Lifetime needs to be limited here as we may simply wrap surface's data. 1.1515 + RefPtr<SourceSurface> sourceSurf = 1.1516 + gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface); 1.1517 + 1.1518 + if (!sourceSurf) { 1.1519 + return; 1.1520 + } 1.1521 + 1.1522 + gfxPoint pt = surface->GetDeviceOffset(); 1.1523 + 1.1524 + Mask(sourceSurf, Point(offset.x - pt.x, offset.y - pt.y)); 1.1525 + } 1.1526 +} 1.1527 + 1.1528 +void 1.1529 +gfxContext::Mask(SourceSurface *surface, const Point& offset) 1.1530 +{ 1.1531 + MOZ_ASSERT(mDT); 1.1532 + 1.1533 + 1.1534 + // We clip here to bind to the mask surface bounds, see above. 1.1535 + mDT->MaskSurface(GeneralPattern(this), 1.1536 + surface, 1.1537 + offset, 1.1538 + DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode)); 1.1539 +} 1.1540 + 1.1541 +void 1.1542 +gfxContext::Paint(gfxFloat alpha) 1.1543 +{ 1.1544 + PROFILER_LABEL("gfxContext", "Paint"); 1.1545 + if (mCairo) { 1.1546 + cairo_paint_with_alpha(mCairo, alpha); 1.1547 + } else { 1.1548 + AzureState &state = CurrentState(); 1.1549 + 1.1550 + if (state.sourceSurface && !state.sourceSurfCairo && 1.1551 + !state.patternTransformChanged && !state.opIsClear) 1.1552 + { 1.1553 + // This is the case where a PopGroupToSource has been done and this 1.1554 + // paint is executed without changing the transform or the source. 1.1555 + Matrix oldMat = mDT->GetTransform(); 1.1556 + 1.1557 + IntSize surfSize = state.sourceSurface->GetSize(); 1.1558 + 1.1559 + Matrix mat; 1.1560 + mat.Translate(-state.deviceOffset.x, -state.deviceOffset.y); 1.1561 + mDT->SetTransform(mat); 1.1562 + 1.1563 + mDT->DrawSurface(state.sourceSurface, 1.1564 + Rect(state.sourceSurfaceDeviceOffset, Size(surfSize.width, surfSize.height)), 1.1565 + Rect(Point(), Size(surfSize.width, surfSize.height)), 1.1566 + DrawSurfaceOptions(), DrawOptions(alpha, GetOp())); 1.1567 + mDT->SetTransform(oldMat); 1.1568 + return; 1.1569 + } 1.1570 + 1.1571 + Matrix mat = mDT->GetTransform(); 1.1572 + mat.Invert(); 1.1573 + Rect paintRect = mat.TransformBounds(Rect(Point(0, 0), Size(mDT->GetSize()))); 1.1574 + 1.1575 + if (state.opIsClear) { 1.1576 + mDT->ClearRect(paintRect); 1.1577 + } else { 1.1578 + mDT->FillRect(paintRect, GeneralPattern(this), 1.1579 + DrawOptions(Float(alpha), GetOp())); 1.1580 + } 1.1581 + } 1.1582 +} 1.1583 + 1.1584 +// groups 1.1585 + 1.1586 +void 1.1587 +gfxContext::PushGroup(gfxContentType content) 1.1588 +{ 1.1589 + if (mCairo) { 1.1590 + cairo_push_group_with_content(mCairo, (cairo_content_t)(int) content); 1.1591 + } else { 1.1592 + PushNewDT(content); 1.1593 + 1.1594 + PushClipsToDT(mDT); 1.1595 + mDT->SetTransform(GetDTTransform()); 1.1596 + } 1.1597 +} 1.1598 + 1.1599 +static gfxRect 1.1600 +GetRoundOutDeviceClipExtents(gfxContext* aCtx) 1.1601 +{ 1.1602 + gfxContextMatrixAutoSaveRestore save(aCtx); 1.1603 + aCtx->IdentityMatrix(); 1.1604 + gfxRect r = aCtx->GetClipExtents(); 1.1605 + r.RoundOut(); 1.1606 + return r; 1.1607 +} 1.1608 + 1.1609 +/** 1.1610 + * Copy the contents of aSrc to aDest, translated by aTranslation. 1.1611 + */ 1.1612 +static void 1.1613 +CopySurface(gfxASurface* aSrc, gfxASurface* aDest, const gfxPoint& aTranslation) 1.1614 +{ 1.1615 + cairo_t *cr = cairo_create(aDest->CairoSurface()); 1.1616 + cairo_set_source_surface(cr, aSrc->CairoSurface(), aTranslation.x, aTranslation.y); 1.1617 + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); 1.1618 + cairo_paint(cr); 1.1619 + cairo_destroy(cr); 1.1620 +} 1.1621 + 1.1622 +void 1.1623 +gfxContext::PushGroupAndCopyBackground(gfxContentType content) 1.1624 +{ 1.1625 + if (mCairo) { 1.1626 + if (content == gfxContentType::COLOR_ALPHA && 1.1627 + !(GetFlags() & FLAG_DISABLE_COPY_BACKGROUND)) { 1.1628 + nsRefPtr<gfxASurface> s = CurrentSurface(); 1.1629 + if ((s->GetAllowUseAsSource() || s->GetType() == gfxSurfaceType::Tee) && 1.1630 + (s->GetContentType() == gfxContentType::COLOR || 1.1631 + s->GetOpaqueRect().Contains(GetRoundOutDeviceClipExtents(this)))) { 1.1632 + cairo_push_group_with_content(mCairo, CAIRO_CONTENT_COLOR); 1.1633 + nsRefPtr<gfxASurface> d = CurrentSurface(); 1.1634 + 1.1635 + if (d->GetType() == gfxSurfaceType::Tee) { 1.1636 + NS_ASSERTION(s->GetType() == gfxSurfaceType::Tee, "Mismatched types"); 1.1637 + nsAutoTArray<nsRefPtr<gfxASurface>,2> ss; 1.1638 + nsAutoTArray<nsRefPtr<gfxASurface>,2> ds; 1.1639 + static_cast<gfxTeeSurface*>(s.get())->GetSurfaces(&ss); 1.1640 + static_cast<gfxTeeSurface*>(d.get())->GetSurfaces(&ds); 1.1641 + NS_ASSERTION(ss.Length() == ds.Length(), "Mismatched lengths"); 1.1642 + gfxPoint translation = d->GetDeviceOffset() - s->GetDeviceOffset(); 1.1643 + for (uint32_t i = 0; i < ss.Length(); ++i) { 1.1644 + CopySurface(ss[i], ds[i], translation); 1.1645 + } 1.1646 + } else { 1.1647 + CopySurface(s, d, gfxPoint(0, 0)); 1.1648 + } 1.1649 + d->SetOpaqueRect(s->GetOpaqueRect()); 1.1650 + return; 1.1651 + } 1.1652 + } 1.1653 + } else { 1.1654 + IntRect clipExtents; 1.1655 + if (mDT->GetFormat() != SurfaceFormat::B8G8R8X8) { 1.1656 + gfxRect clipRect = GetRoundOutDeviceClipExtents(this); 1.1657 + clipExtents = IntRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height); 1.1658 + } 1.1659 + if ((mDT->GetFormat() == SurfaceFormat::B8G8R8X8 || 1.1660 + mDT->GetOpaqueRect().Contains(clipExtents)) && 1.1661 + !mDT->GetUserData(&sDontUseAsSourceKey)) { 1.1662 + DrawTarget *oldDT = mDT; 1.1663 + RefPtr<SourceSurface> source = mDT->Snapshot(); 1.1664 + Point oldDeviceOffset = CurrentState().deviceOffset; 1.1665 + 1.1666 + PushNewDT(gfxContentType::COLOR); 1.1667 + 1.1668 + Point offset = CurrentState().deviceOffset - oldDeviceOffset; 1.1669 + Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height)); 1.1670 + Rect sourceRect = surfRect; 1.1671 + sourceRect.x += offset.x; 1.1672 + sourceRect.y += offset.y; 1.1673 + 1.1674 + mDT->SetTransform(Matrix()); 1.1675 + mDT->DrawSurface(source, surfRect, sourceRect); 1.1676 + mDT->SetOpaqueRect(oldDT->GetOpaqueRect()); 1.1677 + 1.1678 + PushClipsToDT(mDT); 1.1679 + mDT->SetTransform(GetDTTransform()); 1.1680 + return; 1.1681 + } 1.1682 + } 1.1683 + PushGroup(content); 1.1684 +} 1.1685 + 1.1686 +already_AddRefed<gfxPattern> 1.1687 +gfxContext::PopGroup() 1.1688 +{ 1.1689 + if (mCairo) { 1.1690 + cairo_pattern_t *pat = cairo_pop_group(mCairo); 1.1691 + nsRefPtr<gfxPattern> wrapper = new gfxPattern(pat); 1.1692 + cairo_pattern_destroy(pat); 1.1693 + return wrapper.forget(); 1.1694 + } else { 1.1695 + RefPtr<SourceSurface> src = mDT->Snapshot(); 1.1696 + Point deviceOffset = CurrentState().deviceOffset; 1.1697 + 1.1698 + Restore(); 1.1699 + 1.1700 + Matrix mat = mTransform; 1.1701 + mat.Invert(); 1.1702 + 1.1703 + Matrix deviceOffsetTranslation; 1.1704 + deviceOffsetTranslation.Translate(deviceOffset.x, deviceOffset.y); 1.1705 + 1.1706 + nsRefPtr<gfxPattern> pat = new gfxPattern(src, deviceOffsetTranslation * mat); 1.1707 + 1.1708 + return pat.forget(); 1.1709 + } 1.1710 +} 1.1711 + 1.1712 +void 1.1713 +gfxContext::PopGroupToSource() 1.1714 +{ 1.1715 + if (mCairo) { 1.1716 + cairo_pop_group_to_source(mCairo); 1.1717 + } else { 1.1718 + RefPtr<SourceSurface> src = mDT->Snapshot(); 1.1719 + Point deviceOffset = CurrentState().deviceOffset; 1.1720 + Restore(); 1.1721 + CurrentState().sourceSurfCairo = nullptr; 1.1722 + CurrentState().sourceSurface = src; 1.1723 + CurrentState().sourceSurfaceDeviceOffset = deviceOffset; 1.1724 + CurrentState().pattern = nullptr; 1.1725 + CurrentState().patternTransformChanged = false; 1.1726 + 1.1727 + Matrix mat = mTransform; 1.1728 + mat.Invert(); 1.1729 + 1.1730 + Matrix deviceOffsetTranslation; 1.1731 + deviceOffsetTranslation.Translate(deviceOffset.x, deviceOffset.y); 1.1732 + CurrentState().surfTransform = deviceOffsetTranslation * mat; 1.1733 + } 1.1734 +} 1.1735 + 1.1736 +bool 1.1737 +gfxContext::PointInFill(const gfxPoint& pt) 1.1738 +{ 1.1739 + if (mCairo) { 1.1740 + return cairo_in_fill(mCairo, pt.x, pt.y); 1.1741 + } else { 1.1742 + EnsurePath(); 1.1743 + return mPath->ContainsPoint(ToPoint(pt), Matrix()); 1.1744 + } 1.1745 +} 1.1746 + 1.1747 +bool 1.1748 +gfxContext::PointInStroke(const gfxPoint& pt) 1.1749 +{ 1.1750 + if (mCairo) { 1.1751 + return cairo_in_stroke(mCairo, pt.x, pt.y); 1.1752 + } else { 1.1753 + EnsurePath(); 1.1754 + return mPath->StrokeContainsPoint(CurrentState().strokeOptions, 1.1755 + ToPoint(pt), 1.1756 + Matrix()); 1.1757 + } 1.1758 +} 1.1759 + 1.1760 +gfxRect 1.1761 +gfxContext::GetUserPathExtent() 1.1762 +{ 1.1763 + if (mCairo) { 1.1764 + double xmin, ymin, xmax, ymax; 1.1765 + cairo_path_extents(mCairo, &xmin, &ymin, &xmax, &ymax); 1.1766 + return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin); 1.1767 + } else { 1.1768 + EnsurePath(); 1.1769 + return ThebesRect(mPath->GetBounds()); 1.1770 + } 1.1771 +} 1.1772 + 1.1773 +gfxRect 1.1774 +gfxContext::GetUserFillExtent() 1.1775 +{ 1.1776 + if (mCairo) { 1.1777 + double xmin, ymin, xmax, ymax; 1.1778 + cairo_fill_extents(mCairo, &xmin, &ymin, &xmax, &ymax); 1.1779 + return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin); 1.1780 + } else { 1.1781 + EnsurePath(); 1.1782 + return ThebesRect(mPath->GetBounds()); 1.1783 + } 1.1784 +} 1.1785 + 1.1786 +gfxRect 1.1787 +gfxContext::GetUserStrokeExtent() 1.1788 +{ 1.1789 + if (mCairo) { 1.1790 + double xmin, ymin, xmax, ymax; 1.1791 + cairo_stroke_extents(mCairo, &xmin, &ymin, &xmax, &ymax); 1.1792 + return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin); 1.1793 + } else { 1.1794 + EnsurePath(); 1.1795 + return ThebesRect(mPath->GetStrokedBounds(CurrentState().strokeOptions, mTransform)); 1.1796 + } 1.1797 +} 1.1798 + 1.1799 +bool 1.1800 +gfxContext::HasError() 1.1801 +{ 1.1802 + if (mCairo) { 1.1803 + return cairo_status(mCairo) != CAIRO_STATUS_SUCCESS; 1.1804 + } else { 1.1805 + // As far as this is concerned, an Azure context is never in error. 1.1806 + return false; 1.1807 + } 1.1808 +} 1.1809 + 1.1810 +void 1.1811 +gfxContext::RoundedRectangle(const gfxRect& rect, 1.1812 + const gfxCornerSizes& corners, 1.1813 + bool draw_clockwise) 1.1814 +{ 1.1815 + // 1.1816 + // For CW drawing, this looks like: 1.1817 + // 1.1818 + // ...******0** 1 C 1.1819 + // **** 1.1820 + // *** 2 1.1821 + // ** 1.1822 + // * 1.1823 + // * 1.1824 + // 3 1.1825 + // * 1.1826 + // * 1.1827 + // 1.1828 + // Where 0, 1, 2, 3 are the control points of the Bezier curve for 1.1829 + // the corner, and C is the actual corner point. 1.1830 + // 1.1831 + // At the start of the loop, the current point is assumed to be 1.1832 + // the point adjacent to the top left corner on the top 1.1833 + // horizontal. Note that corner indices start at the top left and 1.1834 + // continue clockwise, whereas in our loop i = 0 refers to the top 1.1835 + // right corner. 1.1836 + // 1.1837 + // When going CCW, the control points are swapped, and the first 1.1838 + // corner that's drawn is the top left (along with the top segment). 1.1839 + // 1.1840 + // There is considerable latitude in how one chooses the four 1.1841 + // control points for a Bezier curve approximation to an ellipse. 1.1842 + // For the overall path to be continuous and show no corner at the 1.1843 + // endpoints of the arc, points 0 and 3 must be at the ends of the 1.1844 + // straight segments of the rectangle; points 0, 1, and C must be 1.1845 + // collinear; and points 3, 2, and C must also be collinear. This 1.1846 + // leaves only two free parameters: the ratio of the line segments 1.1847 + // 01 and 0C, and the ratio of the line segments 32 and 3C. See 1.1848 + // the following papers for extensive discussion of how to choose 1.1849 + // these ratios: 1.1850 + // 1.1851 + // Dokken, Tor, et al. "Good approximation of circles by 1.1852 + // curvature-continuous Bezier curves." Computer-Aided 1.1853 + // Geometric Design 7(1990) 33--41. 1.1854 + // Goldapp, Michael. "Approximation of circular arcs by cubic 1.1855 + // polynomials." Computer-Aided Geometric Design 8(1991) 227--238. 1.1856 + // Maisonobe, Luc. "Drawing an elliptical arc using polylines, 1.1857 + // quadratic, or cubic Bezier curves." 1.1858 + // http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf 1.1859 + // 1.1860 + // We follow the approach in section 2 of Goldapp (least-error, 1.1861 + // Hermite-type approximation) and make both ratios equal to 1.1862 + // 1.1863 + // 2 2 + n - sqrt(2n + 28) 1.1864 + // alpha = - * --------------------- 1.1865 + // 3 n - 4 1.1866 + // 1.1867 + // where n = 3( cbrt(sqrt(2)+1) - cbrt(sqrt(2)-1) ). 1.1868 + // 1.1869 + // This is the result of Goldapp's equation (10b) when the angle 1.1870 + // swept out by the arc is pi/2, and the parameter "a-bar" is the 1.1871 + // expression given immediately below equation (21). 1.1872 + // 1.1873 + // Using this value, the maximum radial error for a circle, as a 1.1874 + // fraction of the radius, is on the order of 0.2 x 10^-3. 1.1875 + // Neither Dokken nor Goldapp discusses error for a general 1.1876 + // ellipse; Maisonobe does, but his choice of control points 1.1877 + // follows different constraints, and Goldapp's expression for 1.1878 + // 'alpha' gives much smaller radial error, even for very flat 1.1879 + // ellipses, than Maisonobe's equivalent. 1.1880 + // 1.1881 + // For the various corners and for each axis, the sign of this 1.1882 + // constant changes, or it might be 0 -- it's multiplied by the 1.1883 + // appropriate multiplier from the list before using. 1.1884 + 1.1885 + if (mCairo) { 1.1886 + const gfxFloat alpha = 0.55191497064665766025; 1.1887 + 1.1888 + typedef struct { gfxFloat a, b; } twoFloats; 1.1889 + 1.1890 + twoFloats cwCornerMults[4] = { { -1, 0 }, 1.1891 + { 0, -1 }, 1.1892 + { +1, 0 }, 1.1893 + { 0, +1 } }; 1.1894 + twoFloats ccwCornerMults[4] = { { +1, 0 }, 1.1895 + { 0, -1 }, 1.1896 + { -1, 0 }, 1.1897 + { 0, +1 } }; 1.1898 + 1.1899 + twoFloats *cornerMults = draw_clockwise ? cwCornerMults : ccwCornerMults; 1.1900 + 1.1901 + gfxPoint pc, p0, p1, p2, p3; 1.1902 + 1.1903 + if (draw_clockwise) 1.1904 + cairo_move_to(mCairo, rect.X() + corners[NS_CORNER_TOP_LEFT].width, rect.Y()); 1.1905 + else 1.1906 + cairo_move_to(mCairo, rect.X() + rect.Width() - corners[NS_CORNER_TOP_RIGHT].width, rect.Y()); 1.1907 + 1.1908 + NS_FOR_CSS_CORNERS(i) { 1.1909 + // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw) 1.1910 + mozilla::css::Corner c = mozilla::css::Corner(draw_clockwise ? ((i+1) % 4) : ((4-i) % 4)); 1.1911 + 1.1912 + // i+2 and i+3 respectively. These are used to index into the corner 1.1913 + // multiplier table, and were deduced by calculating out the long form 1.1914 + // of each corner and finding a pattern in the signs and values. 1.1915 + int i2 = (i+2) % 4; 1.1916 + int i3 = (i+3) % 4; 1.1917 + 1.1918 + pc = rect.AtCorner(c); 1.1919 + 1.1920 + if (corners[c].width > 0.0 && corners[c].height > 0.0) { 1.1921 + p0.x = pc.x + cornerMults[i].a * corners[c].width; 1.1922 + p0.y = pc.y + cornerMults[i].b * corners[c].height; 1.1923 + 1.1924 + p3.x = pc.x + cornerMults[i3].a * corners[c].width; 1.1925 + p3.y = pc.y + cornerMults[i3].b * corners[c].height; 1.1926 + 1.1927 + p1.x = p0.x + alpha * cornerMults[i2].a * corners[c].width; 1.1928 + p1.y = p0.y + alpha * cornerMults[i2].b * corners[c].height; 1.1929 + 1.1930 + p2.x = p3.x - alpha * cornerMults[i3].a * corners[c].width; 1.1931 + p2.y = p3.y - alpha * cornerMults[i3].b * corners[c].height; 1.1932 + 1.1933 + cairo_line_to (mCairo, p0.x, p0.y); 1.1934 + cairo_curve_to (mCairo, 1.1935 + p1.x, p1.y, 1.1936 + p2.x, p2.y, 1.1937 + p3.x, p3.y); 1.1938 + } else { 1.1939 + cairo_line_to (mCairo, pc.x, pc.y); 1.1940 + } 1.1941 + } 1.1942 + 1.1943 + cairo_close_path (mCairo); 1.1944 + } else { 1.1945 + EnsurePathBuilder(); 1.1946 + Size radii[] = { ToSize(corners[NS_CORNER_TOP_LEFT]), 1.1947 + ToSize(corners[NS_CORNER_TOP_RIGHT]), 1.1948 + ToSize(corners[NS_CORNER_BOTTOM_RIGHT]), 1.1949 + ToSize(corners[NS_CORNER_BOTTOM_LEFT]) }; 1.1950 + AppendRoundedRectToPath(mPathBuilder, ToRect(rect), radii, draw_clockwise); 1.1951 + } 1.1952 +} 1.1953 + 1.1954 +#ifdef MOZ_DUMP_PAINTING 1.1955 +void 1.1956 +gfxContext::WriteAsPNG(const char* aFile) 1.1957 +{ 1.1958 + nsRefPtr<gfxASurface> surf = CurrentSurface(); 1.1959 + if (surf) { 1.1960 + surf->WriteAsPNG(aFile); 1.1961 + } else { 1.1962 + NS_WARNING("No surface found!"); 1.1963 + } 1.1964 +} 1.1965 + 1.1966 +void 1.1967 +gfxContext::DumpAsDataURL() 1.1968 +{ 1.1969 + nsRefPtr<gfxASurface> surf = CurrentSurface(); 1.1970 + if (surf) { 1.1971 + surf->DumpAsDataURL(); 1.1972 + } else { 1.1973 + NS_WARNING("No surface found!"); 1.1974 + } 1.1975 +} 1.1976 + 1.1977 +void 1.1978 +gfxContext::CopyAsDataURL() 1.1979 +{ 1.1980 + nsRefPtr<gfxASurface> surf = CurrentSurface(); 1.1981 + if (surf) { 1.1982 + surf->CopyAsDataURL(); 1.1983 + } else { 1.1984 + NS_WARNING("No surface found!"); 1.1985 + } 1.1986 +} 1.1987 +#endif 1.1988 + 1.1989 +void 1.1990 +gfxContext::EnsurePath() 1.1991 +{ 1.1992 + if (mPathBuilder) { 1.1993 + mPath = mPathBuilder->Finish(); 1.1994 + mPathBuilder = nullptr; 1.1995 + } 1.1996 + 1.1997 + if (mPath) { 1.1998 + if (mTransformChanged) { 1.1999 + Matrix mat = mTransform; 1.2000 + mat.Invert(); 1.2001 + mat = mPathTransform * mat; 1.2002 + mPathBuilder = mPath->TransformedCopyToBuilder(mat, CurrentState().fillRule); 1.2003 + mPath = mPathBuilder->Finish(); 1.2004 + mPathBuilder = nullptr; 1.2005 + 1.2006 + mTransformChanged = false; 1.2007 + } 1.2008 + 1.2009 + if (CurrentState().fillRule == mPath->GetFillRule()) { 1.2010 + return; 1.2011 + } 1.2012 + 1.2013 + mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule); 1.2014 + 1.2015 + mPath = mPathBuilder->Finish(); 1.2016 + mPathBuilder = nullptr; 1.2017 + return; 1.2018 + } 1.2019 + 1.2020 + EnsurePathBuilder(); 1.2021 + mPath = mPathBuilder->Finish(); 1.2022 + mPathBuilder = nullptr; 1.2023 +} 1.2024 + 1.2025 +void 1.2026 +gfxContext::EnsurePathBuilder() 1.2027 +{ 1.2028 + if (mPathBuilder && !mTransformChanged) { 1.2029 + return; 1.2030 + } 1.2031 + 1.2032 + if (mPath) { 1.2033 + if (!mTransformChanged) { 1.2034 + mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule); 1.2035 + mPath = nullptr; 1.2036 + } else { 1.2037 + Matrix invTransform = mTransform; 1.2038 + invTransform.Invert(); 1.2039 + Matrix toNewUS = mPathTransform * invTransform; 1.2040 + mPathBuilder = mPath->TransformedCopyToBuilder(toNewUS, CurrentState().fillRule); 1.2041 + } 1.2042 + return; 1.2043 + } 1.2044 + 1.2045 + DebugOnly<PathBuilder*> oldPath = mPathBuilder.get(); 1.2046 + 1.2047 + if (!mPathBuilder) { 1.2048 + mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule); 1.2049 + 1.2050 + if (mPathIsRect) { 1.2051 + mPathBuilder->MoveTo(mRect.TopLeft()); 1.2052 + mPathBuilder->LineTo(mRect.TopRight()); 1.2053 + mPathBuilder->LineTo(mRect.BottomRight()); 1.2054 + mPathBuilder->LineTo(mRect.BottomLeft()); 1.2055 + mPathBuilder->Close(); 1.2056 + } 1.2057 + } 1.2058 + 1.2059 + if (mTransformChanged) { 1.2060 + // This could be an else if since this should never happen when 1.2061 + // mPathBuilder is nullptr and mPath is nullptr. But this way we can 1.2062 + // assert if all the state is as expected. 1.2063 + MOZ_ASSERT(oldPath); 1.2064 + MOZ_ASSERT(!mPathIsRect); 1.2065 + 1.2066 + Matrix invTransform = mTransform; 1.2067 + invTransform.Invert(); 1.2068 + Matrix toNewUS = mPathTransform * invTransform; 1.2069 + 1.2070 + RefPtr<Path> path = mPathBuilder->Finish(); 1.2071 + mPathBuilder = path->TransformedCopyToBuilder(toNewUS, CurrentState().fillRule); 1.2072 + } 1.2073 + 1.2074 + mPathIsRect = false; 1.2075 +} 1.2076 + 1.2077 +void 1.2078 +gfxContext::FillAzure(Float aOpacity) 1.2079 +{ 1.2080 + AzureState &state = CurrentState(); 1.2081 + 1.2082 + CompositionOp op = GetOp(); 1.2083 + 1.2084 + if (mPathIsRect) { 1.2085 + MOZ_ASSERT(!mTransformChanged); 1.2086 + 1.2087 + if (state.opIsClear) { 1.2088 + mDT->ClearRect(mRect); 1.2089 + } else if (op == CompositionOp::OP_SOURCE) { 1.2090 + // Emulate cairo operator source which is bound by mask! 1.2091 + mDT->ClearRect(mRect); 1.2092 + mDT->FillRect(mRect, GeneralPattern(this), DrawOptions(aOpacity)); 1.2093 + } else { 1.2094 + mDT->FillRect(mRect, GeneralPattern(this), DrawOptions(aOpacity, op, state.aaMode)); 1.2095 + } 1.2096 + } else { 1.2097 + EnsurePath(); 1.2098 + 1.2099 + NS_ASSERTION(!state.opIsClear, "We shouldn't be clearing complex paths!"); 1.2100 + 1.2101 + mDT->Fill(mPath, GeneralPattern(this), DrawOptions(aOpacity, op, state.aaMode)); 1.2102 + } 1.2103 +} 1.2104 + 1.2105 +void 1.2106 +gfxContext::PushClipsToDT(DrawTarget *aDT) 1.2107 +{ 1.2108 + // Tricky, we have to restore all clips -since the last time- the clip 1.2109 + // was reset. If we didn't reset the clip, just popping the clips we 1.2110 + // added was fine. 1.2111 + unsigned int lastReset = 0; 1.2112 + for (int i = mStateStack.Length() - 2; i > 0; i--) { 1.2113 + if (mStateStack[i].clipWasReset) { 1.2114 + lastReset = i; 1.2115 + break; 1.2116 + } 1.2117 + } 1.2118 + 1.2119 + // Don't need to save the old transform, we'll be setting a new one soon! 1.2120 + 1.2121 + // Push all clips from the last state on the stack where the clip was 1.2122 + // reset to the clip before ours. 1.2123 + for (unsigned int i = lastReset; i < mStateStack.Length() - 1; i++) { 1.2124 + for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) { 1.2125 + aDT->SetTransform(mStateStack[i].pushedClips[c].transform * GetDeviceTransform()); 1.2126 + if (mStateStack[i].pushedClips[c].path) { 1.2127 + aDT->PushClip(mStateStack[i].pushedClips[c].path); 1.2128 + } else { 1.2129 + aDT->PushClipRect(mStateStack[i].pushedClips[c].rect); 1.2130 + } 1.2131 + } 1.2132 + } 1.2133 +} 1.2134 + 1.2135 +CompositionOp 1.2136 +gfxContext::GetOp() 1.2137 +{ 1.2138 + if (CurrentState().op != CompositionOp::OP_SOURCE) { 1.2139 + return CurrentState().op; 1.2140 + } 1.2141 + 1.2142 + AzureState &state = CurrentState(); 1.2143 + if (state.pattern) { 1.2144 + if (state.pattern->IsOpaque()) { 1.2145 + return CompositionOp::OP_OVER; 1.2146 + } else { 1.2147 + return CompositionOp::OP_SOURCE; 1.2148 + } 1.2149 + } else if (state.sourceSurface) { 1.2150 + if (state.sourceSurface->GetFormat() == SurfaceFormat::B8G8R8X8) { 1.2151 + return CompositionOp::OP_OVER; 1.2152 + } else { 1.2153 + return CompositionOp::OP_SOURCE; 1.2154 + } 1.2155 + } else { 1.2156 + if (state.color.a > 0.999) { 1.2157 + return CompositionOp::OP_OVER; 1.2158 + } else { 1.2159 + return CompositionOp::OP_SOURCE; 1.2160 + } 1.2161 + } 1.2162 +} 1.2163 + 1.2164 +/* SVG font code can change the transform after having set the pattern on the 1.2165 + * context. When the pattern is set it is in user space, if the transform is 1.2166 + * changed after doing so the pattern needs to be converted back into userspace. 1.2167 + * We just store the old pattern transform here so that we only do the work 1.2168 + * needed here if the pattern is actually used. 1.2169 + * We need to avoid doing this when this ChangeTransform comes from a restore, 1.2170 + * since the current pattern and the current transform are both part of the 1.2171 + * state we know the new CurrentState()'s values are valid. But if we assume 1.2172 + * a change they might become invalid since patternTransformChanged is part of 1.2173 + * the state and might be false for the restored AzureState. 1.2174 + */ 1.2175 +void 1.2176 +gfxContext::ChangeTransform(const Matrix &aNewMatrix, bool aUpdatePatternTransform) 1.2177 +{ 1.2178 + AzureState &state = CurrentState(); 1.2179 + 1.2180 + if (aUpdatePatternTransform && (state.pattern || state.sourceSurface) 1.2181 + && !state.patternTransformChanged) { 1.2182 + state.patternTransform = GetDTTransform(); 1.2183 + state.patternTransformChanged = true; 1.2184 + } 1.2185 + 1.2186 + if (mPathIsRect) { 1.2187 + Matrix invMatrix = aNewMatrix; 1.2188 + 1.2189 + invMatrix.Invert(); 1.2190 + 1.2191 + Matrix toNewUS = mTransform * invMatrix; 1.2192 + 1.2193 + if (toNewUS.IsRectilinear()) { 1.2194 + mRect = toNewUS.TransformBounds(mRect); 1.2195 + mRect.NudgeToIntegers(); 1.2196 + } else { 1.2197 + mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule); 1.2198 + 1.2199 + mPathBuilder->MoveTo(toNewUS * mRect.TopLeft()); 1.2200 + mPathBuilder->LineTo(toNewUS * mRect.TopRight()); 1.2201 + mPathBuilder->LineTo(toNewUS * mRect.BottomRight()); 1.2202 + mPathBuilder->LineTo(toNewUS * mRect.BottomLeft()); 1.2203 + mPathBuilder->Close(); 1.2204 + 1.2205 + mPathIsRect = false; 1.2206 + } 1.2207 + 1.2208 + // No need to consider the transform changed now! 1.2209 + mTransformChanged = false; 1.2210 + } else if ((mPath || mPathBuilder) && !mTransformChanged) { 1.2211 + mTransformChanged = true; 1.2212 + mPathTransform = mTransform; 1.2213 + } 1.2214 + 1.2215 + mTransform = aNewMatrix; 1.2216 + 1.2217 + mDT->SetTransform(GetDTTransform()); 1.2218 +} 1.2219 + 1.2220 +Rect 1.2221 +gfxContext::GetAzureDeviceSpaceClipBounds() 1.2222 +{ 1.2223 + unsigned int lastReset = 0; 1.2224 + for (int i = mStateStack.Length() - 1; i > 0; i--) { 1.2225 + if (mStateStack[i].clipWasReset) { 1.2226 + lastReset = i; 1.2227 + break; 1.2228 + } 1.2229 + } 1.2230 + 1.2231 + Rect rect(CurrentState().deviceOffset.x, CurrentState().deviceOffset.y, 1.2232 + Float(mDT->GetSize().width), Float(mDT->GetSize().height)); 1.2233 + for (unsigned int i = lastReset; i < mStateStack.Length(); i++) { 1.2234 + for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) { 1.2235 + AzureState::PushedClip &clip = mStateStack[i].pushedClips[c]; 1.2236 + if (clip.path) { 1.2237 + Rect bounds = clip.path->GetBounds(clip.transform); 1.2238 + rect.IntersectRect(rect, bounds); 1.2239 + } else { 1.2240 + rect.IntersectRect(rect, clip.transform.TransformBounds(clip.rect)); 1.2241 + } 1.2242 + } 1.2243 + } 1.2244 + 1.2245 + return rect; 1.2246 +} 1.2247 + 1.2248 +Point 1.2249 +gfxContext::GetDeviceOffset() const 1.2250 +{ 1.2251 + return CurrentState().deviceOffset; 1.2252 +} 1.2253 + 1.2254 +Matrix 1.2255 +gfxContext::GetDeviceTransform() const 1.2256 +{ 1.2257 + Matrix mat; 1.2258 + mat.Translate(-CurrentState().deviceOffset.x, -CurrentState().deviceOffset.y); 1.2259 + return mat; 1.2260 +} 1.2261 + 1.2262 +Matrix 1.2263 +gfxContext::GetDTTransform() const 1.2264 +{ 1.2265 + Matrix mat = mTransform; 1.2266 + mat._31 -= CurrentState().deviceOffset.x; 1.2267 + mat._32 -= CurrentState().deviceOffset.y; 1.2268 + return mat; 1.2269 +} 1.2270 + 1.2271 +void 1.2272 +gfxContext::PushNewDT(gfxContentType content) 1.2273 +{ 1.2274 + Rect clipBounds = GetAzureDeviceSpaceClipBounds(); 1.2275 + clipBounds.RoundOut(); 1.2276 + 1.2277 + clipBounds.width = std::max(1.0f, clipBounds.width); 1.2278 + clipBounds.height = std::max(1.0f, clipBounds.height); 1.2279 + 1.2280 + SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content); 1.2281 + 1.2282 + RefPtr<DrawTarget> newDT = 1.2283 + mDT->CreateSimilarDrawTarget(IntSize(int32_t(clipBounds.width), int32_t(clipBounds.height)), 1.2284 + format); 1.2285 + 1.2286 + if (!newDT) { 1.2287 + NS_WARNING("Failed to create DrawTarget of sufficient size."); 1.2288 + newDT = mDT->CreateSimilarDrawTarget(IntSize(64, 64), format); 1.2289 + 1.2290 + if (!newDT) { 1.2291 + // If even this fails.. we're most likely just out of memory! 1.2292 + NS_ABORT_OOM(BytesPerPixel(format) * 64 * 64); 1.2293 + } 1.2294 + } 1.2295 + 1.2296 + Save(); 1.2297 + 1.2298 + CurrentState().drawTarget = newDT; 1.2299 + CurrentState().deviceOffset = clipBounds.TopLeft(); 1.2300 + 1.2301 + mDT = newDT; 1.2302 +} 1.2303 + 1.2304 +/** 1.2305 + * Work out whether cairo will snap inter-glyph spacing to pixels. 1.2306 + * 1.2307 + * Layout does not align text to pixel boundaries, so, with font drawing 1.2308 + * backends that snap glyph positions to pixels, it is important that 1.2309 + * inter-glyph spacing within words is always an integer number of pixels. 1.2310 + * This ensures that the drawing backend snaps all of the word's glyphs in the 1.2311 + * same direction and so inter-glyph spacing remains the same. 1.2312 + */ 1.2313 +void 1.2314 +gfxContext::GetRoundOffsetsToPixels(bool *aRoundX, bool *aRoundY) 1.2315 +{ 1.2316 + *aRoundX = false; 1.2317 + // Could do something fancy here for ScaleFactors of 1.2318 + // AxisAlignedTransforms, but we leave things simple. 1.2319 + // Not much point rounding if a matrix will mess things up anyway. 1.2320 + // Also return false for non-cairo contexts. 1.2321 + if (CurrentMatrix().HasNonTranslation() || mDT) { 1.2322 + *aRoundY = false; 1.2323 + return; 1.2324 + } 1.2325 + 1.2326 + // All raster backends snap glyphs to pixels vertically. 1.2327 + // Print backends set CAIRO_HINT_METRICS_OFF. 1.2328 + *aRoundY = true; 1.2329 + 1.2330 + cairo_t *cr = GetCairo(); 1.2331 + cairo_scaled_font_t *scaled_font = cairo_get_scaled_font(cr); 1.2332 + // Sometimes hint metrics gets set for us, most notably for printing. 1.2333 + cairo_font_options_t *font_options = cairo_font_options_create(); 1.2334 + cairo_scaled_font_get_font_options(scaled_font, font_options); 1.2335 + cairo_hint_metrics_t hint_metrics = 1.2336 + cairo_font_options_get_hint_metrics(font_options); 1.2337 + cairo_font_options_destroy(font_options); 1.2338 + 1.2339 + switch (hint_metrics) { 1.2340 + case CAIRO_HINT_METRICS_OFF: 1.2341 + *aRoundY = false; 1.2342 + return; 1.2343 + case CAIRO_HINT_METRICS_DEFAULT: 1.2344 + // Here we mimic what cairo surface/font backends do. Printing 1.2345 + // surfaces have already been handled by hint_metrics. The 1.2346 + // fallback show_glyphs implementation composites pixel-aligned 1.2347 + // glyph surfaces, so we just pick surface/font combinations that 1.2348 + // override this. 1.2349 + switch (cairo_scaled_font_get_type(scaled_font)) { 1.2350 +#if CAIRO_HAS_DWRITE_FONT // dwrite backend is not in std cairo releases yet 1.2351 + case CAIRO_FONT_TYPE_DWRITE: 1.2352 + // show_glyphs is implemented on the font and so is used for 1.2353 + // all surface types; however, it may pixel-snap depending on 1.2354 + // the dwrite rendering mode 1.2355 + if (!cairo_dwrite_scaled_font_get_force_GDI_classic(scaled_font) && 1.2356 + gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode() == 1.2357 + DWRITE_MEASURING_MODE_NATURAL) { 1.2358 + return; 1.2359 + } 1.2360 +#endif 1.2361 + case CAIRO_FONT_TYPE_QUARTZ: 1.2362 + // Quartz surfaces implement show_glyphs for Quartz fonts 1.2363 + if (cairo_surface_get_type(cairo_get_target(cr)) == 1.2364 + CAIRO_SURFACE_TYPE_QUARTZ) { 1.2365 + return; 1.2366 + } 1.2367 + default: 1.2368 + break; 1.2369 + } 1.2370 + // fall through: 1.2371 + case CAIRO_HINT_METRICS_ON: 1.2372 + break; 1.2373 + } 1.2374 + *aRoundX = true; 1.2375 + return; 1.2376 +}