Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 | } |