123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include <math.h>
- #include "mozilla/Alignment.h"
- #include "cairo.h"
- #include "gfxContext.h"
- #include "gfxMatrix.h"
- #include "gfxUtils.h"
- #include "gfxASurface.h"
- #include "gfxPattern.h"
- #include "gfxPlatform.h"
- #include "gfxPrefs.h"
- #include "GeckoProfiler.h"
- #include "gfx2DGlue.h"
- #include "mozilla/gfx/PathHelpers.h"
- #include "mozilla/gfx/DrawTargetTiled.h"
- #include <algorithm>
- #if XP_WIN
- #include "gfxWindowsPlatform.h"
- #include "mozilla/gfx/DeviceManagerDx.h"
- #endif
- using namespace mozilla;
- using namespace mozilla::gfx;
- UserDataKey gfxContext::sDontUseAsSourceKey;
- PatternFromState::operator mozilla::gfx::Pattern&()
- {
- gfxContext::AzureState &state = mContext->CurrentState();
- if (state.pattern) {
- return *state.pattern->GetPattern(mContext->mDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
- }
- if (state.sourceSurface) {
- Matrix transform = state.surfTransform;
- if (state.patternTransformChanged) {
- Matrix mat = mContext->GetDTTransform();
- if (!mat.Invert()) {
- mPattern = new (mColorPattern.addr())
- ColorPattern(Color()); // transparent black to paint nothing
- return *mPattern;
- }
- transform = transform * state.patternTransform * mat;
- }
- mPattern = new (mSurfacePattern.addr())
- SurfacePattern(state.sourceSurface, ExtendMode::CLAMP, transform);
- return *mPattern;
- }
- mPattern = new (mColorPattern.addr())
- ColorPattern(state.color);
- return *mPattern;
- }
- gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
- : mPathIsRect(false)
- , mTransformChanged(false)
- , mDT(aTarget)
- {
- if (!aTarget) {
- gfxCriticalError() << "Don't create a gfxContext without a DrawTarget";
- }
- MOZ_COUNT_CTOR(gfxContext);
- mStateStack.SetLength(1);
- CurrentState().drawTarget = mDT;
- CurrentState().deviceOffset = aDeviceOffset;
- mDT->SetTransform(GetDTTransform());
- }
- /* static */ already_AddRefed<gfxContext>
- gfxContext::CreateOrNull(DrawTarget* aTarget,
- const mozilla::gfx::Point& aDeviceOffset)
- {
- if (!aTarget || !aTarget->IsValid()) {
- gfxCriticalNote << "Invalid target in gfxContext::CreateOrNull " << hexa(aTarget);
- return nullptr;
- }
- RefPtr<gfxContext> result = new gfxContext(aTarget, aDeviceOffset);
- return result.forget();
- }
- /* static */ already_AddRefed<gfxContext>
- gfxContext::CreatePreservingTransformOrNull(DrawTarget* aTarget)
- {
- if (!aTarget || !aTarget->IsValid()) {
- gfxCriticalNote << "Invalid target in gfxContext::CreatePreservingTransformOrNull " << hexa(aTarget);
- return nullptr;
- }
- Matrix transform = aTarget->GetTransform();
- RefPtr<gfxContext> result = new gfxContext(aTarget);
- result->SetMatrix(ThebesMatrix(transform));
- return result.forget();
- }
- gfxContext::~gfxContext()
- {
- for (int i = mStateStack.Length() - 1; i >= 0; i--) {
- for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
- mStateStack[i].drawTarget->PopClip();
- }
- }
- MOZ_COUNT_DTOR(gfxContext);
- }
- void
- gfxContext::Save()
- {
- CurrentState().transform = mTransform;
- mStateStack.AppendElement(AzureState(CurrentState()));
- CurrentState().pushedClips.Clear();
- }
- void
- gfxContext::Restore()
- {
- for (unsigned int c = 0; c < CurrentState().pushedClips.Length(); c++) {
- mDT->PopClip();
- }
- mStateStack.RemoveElementAt(mStateStack.Length() - 1);
- mDT = CurrentState().drawTarget;
- ChangeTransform(CurrentState().transform, false);
- }
- // drawing
- void
- gfxContext::NewPath()
- {
- mPath = nullptr;
- mPathBuilder = nullptr;
- mPathIsRect = false;
- mTransformChanged = false;
- }
- void
- gfxContext::ClosePath()
- {
- EnsurePathBuilder();
- mPathBuilder->Close();
- }
- already_AddRefed<Path> gfxContext::GetPath()
- {
- EnsurePath();
- RefPtr<Path> path(mPath);
- return path.forget();
- }
- void gfxContext::SetPath(Path* path)
- {
- MOZ_ASSERT(path->GetBackendType() == mDT->GetBackendType() ||
- path->GetBackendType() == BackendType::RECORDING ||
- (mDT->GetBackendType() == BackendType::DIRECT2D1_1 && path->GetBackendType() == BackendType::DIRECT2D));
- mPath = path;
- mPathBuilder = nullptr;
- mPathIsRect = false;
- mTransformChanged = false;
- }
- gfxPoint
- gfxContext::CurrentPoint()
- {
- EnsurePathBuilder();
- return ThebesPoint(mPathBuilder->CurrentPoint());
- }
- void
- gfxContext::Fill()
- {
- Fill(PatternFromState(this));
- }
- void
- gfxContext::Fill(const Pattern& aPattern)
- {
- PROFILER_LABEL("gfxContext", "Fill",
- js::ProfileEntry::Category::GRAPHICS);
- FillAzure(aPattern, 1.0f);
- }
- void
- gfxContext::MoveTo(const gfxPoint& pt)
- {
- EnsurePathBuilder();
- mPathBuilder->MoveTo(ToPoint(pt));
- }
- void
- gfxContext::LineTo(const gfxPoint& pt)
- {
- EnsurePathBuilder();
- mPathBuilder->LineTo(ToPoint(pt));
- }
- void
- gfxContext::Line(const gfxPoint& start, const gfxPoint& end)
- {
- EnsurePathBuilder();
- mPathBuilder->MoveTo(ToPoint(start));
- mPathBuilder->LineTo(ToPoint(end));
- }
- // XXX snapToPixels is only valid when snapping for filled
- // rectangles and for even-width stroked rectangles.
- // For odd-width stroked rectangles, we need to offset x/y by
- // 0.5...
- void
- gfxContext::Rectangle(const gfxRect& rect, bool snapToPixels)
- {
- Rect rec = ToRect(rect);
- if (snapToPixels) {
- gfxRect newRect(rect);
- if (UserToDevicePixelSnapped(newRect, true)) {
- gfxMatrix mat = ThebesMatrix(mTransform);
- if (mat.Invert()) {
- // We need the user space rect.
- rec = ToRect(mat.TransformBounds(newRect));
- } else {
- rec = Rect();
- }
- }
- }
- if (!mPathBuilder && !mPathIsRect) {
- mPathIsRect = true;
- mRect = rec;
- return;
- }
- EnsurePathBuilder();
- mPathBuilder->MoveTo(rec.TopLeft());
- mPathBuilder->LineTo(rec.TopRight());
- mPathBuilder->LineTo(rec.BottomRight());
- mPathBuilder->LineTo(rec.BottomLeft());
- mPathBuilder->Close();
- }
- // transform stuff
- void
- gfxContext::Multiply(const gfxMatrix& matrix)
- {
- ChangeTransform(ToMatrix(matrix) * mTransform);
- }
- void
- gfxContext::SetMatrix(const gfxMatrix& matrix)
- {
- ChangeTransform(ToMatrix(matrix));
- }
- gfxMatrix
- gfxContext::CurrentMatrix() const
- {
- return ThebesMatrix(mTransform);
- }
- gfxPoint
- gfxContext::DeviceToUser(const gfxPoint& point) const
- {
- return ThebesPoint(mTransform.Inverse().TransformPoint(ToPoint(point)));
- }
- Size
- gfxContext::DeviceToUser(const Size& size) const
- {
- return mTransform.Inverse().TransformSize(size);
- }
- gfxRect
- gfxContext::DeviceToUser(const gfxRect& rect) const
- {
- return ThebesRect(mTransform.Inverse().TransformBounds(ToRect(rect)));
- }
- gfxPoint
- gfxContext::UserToDevice(const gfxPoint& point) const
- {
- return ThebesPoint(mTransform.TransformPoint(ToPoint(point)));
- }
- Size
- gfxContext::UserToDevice(const Size& size) const
- {
- const Matrix &matrix = mTransform;
- Size newSize;
- newSize.width = size.width * matrix._11 + size.height * matrix._12;
- newSize.height = size.width * matrix._21 + size.height * matrix._22;
- return newSize;
- }
- gfxRect
- gfxContext::UserToDevice(const gfxRect& rect) const
- {
- const Matrix &matrix = mTransform;
- return ThebesRect(matrix.TransformBounds(ToRect(rect)));
- }
- bool
- gfxContext::UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale) const
- {
- if (mDT->GetUserData(&sDisablePixelSnapping))
- return false;
- // if we're not at 1.0 scale, don't snap, unless we're
- // ignoring the scale. If we're not -just- a scale,
- // never snap.
- const gfxFloat epsilon = 0.0000001;
- #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
- Matrix mat = mTransform;
- if (!ignoreScale &&
- (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
- !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
- return false;
- #undef WITHIN_E
- gfxPoint p1 = UserToDevice(rect.TopLeft());
- gfxPoint p2 = UserToDevice(rect.TopRight());
- gfxPoint p3 = UserToDevice(rect.BottomRight());
- // Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
- // two opposite corners define the entire rectangle. So check if
- // the axis-aligned rectangle with opposite corners p1 and p3
- // define an axis-aligned rectangle whose other corners are p2 and p4.
- // We actually only need to check one of p2 and p4, since an affine
- // transform maps parallelograms to parallelograms.
- if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) {
- p1.Round();
- p3.Round();
- rect.MoveTo(gfxPoint(std::min(p1.x, p3.x), std::min(p1.y, p3.y)));
- rect.SizeTo(gfxSize(std::max(p1.x, p3.x) - rect.X(),
- std::max(p1.y, p3.y) - rect.Y()));
- return true;
- }
- return false;
- }
- bool
- gfxContext::UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale) const
- {
- if (mDT->GetUserData(&sDisablePixelSnapping))
- return false;
- // if we're not at 1.0 scale, don't snap, unless we're
- // ignoring the scale. If we're not -just- a scale,
- // never snap.
- const gfxFloat epsilon = 0.0000001;
- #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
- Matrix mat = mTransform;
- if (!ignoreScale &&
- (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
- !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
- return false;
- #undef WITHIN_E
- pt = UserToDevice(pt);
- pt.Round();
- return true;
- }
- void
- gfxContext::SetAntialiasMode(AntialiasMode mode)
- {
- CurrentState().aaMode = mode;
- }
- AntialiasMode
- gfxContext::CurrentAntialiasMode() const
- {
- return CurrentState().aaMode;
- }
- void
- gfxContext::SetDash(gfxFloat *dashes, int ndash, gfxFloat offset)
- {
- AzureState &state = CurrentState();
- state.dashPattern.SetLength(ndash);
- for (int i = 0; i < ndash; i++) {
- state.dashPattern[i] = Float(dashes[i]);
- }
- state.strokeOptions.mDashLength = ndash;
- state.strokeOptions.mDashOffset = Float(offset);
- state.strokeOptions.mDashPattern = ndash ? state.dashPattern.Elements()
- : nullptr;
- }
- bool
- gfxContext::CurrentDash(FallibleTArray<gfxFloat>& dashes, gfxFloat* offset) const
- {
- const AzureState &state = CurrentState();
- int count = state.strokeOptions.mDashLength;
- if (count <= 0 || !dashes.SetLength(count, fallible)) {
- return false;
- }
- for (int i = 0; i < count; i++) {
- dashes[i] = state.dashPattern[i];
- }
- *offset = state.strokeOptions.mDashOffset;
- return true;
- }
- gfxFloat
- gfxContext::CurrentDashOffset() const
- {
- return CurrentState().strokeOptions.mDashOffset;
- }
- void
- gfxContext::SetLineWidth(gfxFloat width)
- {
- CurrentState().strokeOptions.mLineWidth = Float(width);
- }
- gfxFloat
- gfxContext::CurrentLineWidth() const
- {
- return CurrentState().strokeOptions.mLineWidth;
- }
- void
- gfxContext::SetOp(CompositionOp aOp)
- {
- CurrentState().op = aOp;
- }
- CompositionOp
- gfxContext::CurrentOp() const
- {
- return CurrentState().op;
- }
- void
- gfxContext::SetLineCap(CapStyle cap)
- {
- CurrentState().strokeOptions.mLineCap = cap;
- }
- CapStyle
- gfxContext::CurrentLineCap() const
- {
- return CurrentState().strokeOptions.mLineCap;
- }
- void
- gfxContext::SetLineJoin(JoinStyle join)
- {
- CurrentState().strokeOptions.mLineJoin = join;
- }
- JoinStyle
- gfxContext::CurrentLineJoin() const
- {
- return CurrentState().strokeOptions.mLineJoin;
- }
- void
- gfxContext::SetMiterLimit(gfxFloat limit)
- {
- CurrentState().strokeOptions.mMiterLimit = Float(limit);
- }
- gfxFloat
- gfxContext::CurrentMiterLimit() const
- {
- return CurrentState().strokeOptions.mMiterLimit;
- }
- // clipping
- void
- gfxContext::Clip(const Rect& rect)
- {
- AzureState::PushedClip clip = { nullptr, rect, mTransform };
- CurrentState().pushedClips.AppendElement(clip);
- mDT->PushClipRect(rect);
- NewPath();
- }
- void
- gfxContext::Clip(const gfxRect& rect)
- {
- Clip(ToRect(rect));
- }
- void
- gfxContext::Clip(Path* aPath)
- {
- mDT->PushClip(aPath);
- AzureState::PushedClip clip = { aPath, Rect(), mTransform };
- CurrentState().pushedClips.AppendElement(clip);
- }
- void
- gfxContext::Clip()
- {
- if (mPathIsRect) {
- MOZ_ASSERT(!mTransformChanged);
- AzureState::PushedClip clip = { nullptr, mRect, mTransform };
- CurrentState().pushedClips.AppendElement(clip);
- mDT->PushClipRect(mRect);
- } else {
- EnsurePath();
- mDT->PushClip(mPath);
- AzureState::PushedClip clip = { mPath, Rect(), mTransform };
- CurrentState().pushedClips.AppendElement(clip);
- }
- }
- void
- gfxContext::PopClip()
- {
- MOZ_ASSERT(CurrentState().pushedClips.Length() > 0);
- CurrentState().pushedClips.RemoveElementAt(CurrentState().pushedClips.Length() - 1);
- mDT->PopClip();
- }
- gfxRect
- gfxContext::GetClipExtents()
- {
- Rect rect = GetAzureDeviceSpaceClipBounds();
- if (rect.width == 0 || rect.height == 0) {
- return gfxRect(0, 0, 0, 0);
- }
- Matrix mat = mTransform;
- mat.Invert();
- rect = mat.TransformBounds(rect);
- return ThebesRect(rect);
- }
- bool
- gfxContext::HasComplexClip() const
- {
- for (int i = mStateStack.Length() - 1; i >= 0; i--) {
- for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
- const AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
- if (clip.path || !clip.transform.IsRectilinear()) {
- return true;
- }
- }
- }
- return false;
- }
- bool
- gfxContext::ExportClip(ClipExporter& aExporter)
- {
- for (unsigned int i = 0; i < mStateStack.Length(); i++) {
- for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
- AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
- gfx::Matrix transform = clip.transform;
- transform.PostTranslate(-GetDeviceOffset());
- aExporter.BeginClip(transform);
- if (clip.path) {
- clip.path->StreamToSink(&aExporter);
- } else {
- aExporter.MoveTo(clip.rect.TopLeft());
- aExporter.LineTo(clip.rect.TopRight());
- aExporter.LineTo(clip.rect.BottomRight());
- aExporter.LineTo(clip.rect.BottomLeft());
- aExporter.Close();
- }
- aExporter.EndClip();
- }
- }
- return true;
- }
- bool
- gfxContext::ClipContainsRect(const gfxRect& aRect)
- {
- // Since we always return false when the clip list contains a
- // non-rectangular clip or a non-rectilinear transform, our 'total' clip
- // is always a rectangle if we hit the end of this function.
- Rect clipBounds(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
- for (unsigned int i = 0; i < mStateStack.Length(); i++) {
- for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
- AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
- if (clip.path || !clip.transform.IsRectilinear()) {
- // Cairo behavior is we return false if the clip contains a non-
- // rectangle.
- return false;
- } else {
- Rect clipRect = mTransform.TransformBounds(clip.rect);
- clipBounds.IntersectRect(clipBounds, clipRect);
- }
- }
- }
- return clipBounds.Contains(ToRect(aRect));
- }
- // rendering sources
- void
- gfxContext::SetColor(const Color& aColor)
- {
- CurrentState().pattern = nullptr;
- CurrentState().sourceSurfCairo = nullptr;
- CurrentState().sourceSurface = nullptr;
- CurrentState().color = ToDeviceColor(aColor);
- }
- void
- gfxContext::SetDeviceColor(const Color& aColor)
- {
- CurrentState().pattern = nullptr;
- CurrentState().sourceSurfCairo = nullptr;
- CurrentState().sourceSurface = nullptr;
- CurrentState().color = aColor;
- }
- bool
- gfxContext::GetDeviceColor(Color& aColorOut)
- {
- if (CurrentState().sourceSurface) {
- return false;
- }
- if (CurrentState().pattern) {
- return CurrentState().pattern->GetSolidColor(aColorOut);
- }
- aColorOut = CurrentState().color;
- return true;
- }
- void
- gfxContext::SetSource(gfxASurface *surface, const gfxPoint& offset)
- {
- CurrentState().surfTransform = Matrix(1.0f, 0, 0, 1.0f, Float(offset.x), Float(offset.y));
- CurrentState().pattern = nullptr;
- CurrentState().patternTransformChanged = false;
- // Keep the underlying cairo surface around while we keep the
- // sourceSurface.
- CurrentState().sourceSurfCairo = surface;
- CurrentState().sourceSurface =
- gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
- CurrentState().color = Color(0, 0, 0, 0);
- }
- void
- gfxContext::SetPattern(gfxPattern *pattern)
- {
- CurrentState().sourceSurfCairo = nullptr;
- CurrentState().sourceSurface = nullptr;
- CurrentState().patternTransformChanged = false;
- CurrentState().pattern = pattern;
- }
- already_AddRefed<gfxPattern>
- gfxContext::GetPattern()
- {
- RefPtr<gfxPattern> pat;
- AzureState &state = CurrentState();
- if (state.pattern) {
- pat = state.pattern;
- } else if (state.sourceSurface) {
- NS_ASSERTION(false, "Ugh, this isn't good.");
- } else {
- pat = new gfxPattern(state.color);
- }
- return pat.forget();
- }
- void
- gfxContext::SetFontSmoothingBackgroundColor(const Color& aColor)
- {
- CurrentState().fontSmoothingBackgroundColor = aColor;
- }
- Color
- gfxContext::GetFontSmoothingBackgroundColor()
- {
- return CurrentState().fontSmoothingBackgroundColor;
- }
- // masking
- void
- gfxContext::Mask(SourceSurface* aSurface, Float aAlpha, const Matrix& aTransform)
- {
- Matrix old = mTransform;
- Matrix mat = aTransform * mTransform;
- ChangeTransform(mat);
- mDT->MaskSurface(PatternFromState(this), aSurface, Point(),
- DrawOptions(aAlpha, CurrentState().op, CurrentState().aaMode));
- ChangeTransform(old);
- }
- void
- gfxContext::Mask(SourceSurface *surface, float alpha, const Point& offset)
- {
- // We clip here to bind to the mask surface bounds, see above.
- mDT->MaskSurface(PatternFromState(this),
- surface,
- offset,
- DrawOptions(alpha, CurrentState().op, CurrentState().aaMode));
- }
- void
- gfxContext::Paint(gfxFloat alpha)
- {
- PROFILER_LABEL("gfxContext", "Paint",
- js::ProfileEntry::Category::GRAPHICS);
- AzureState &state = CurrentState();
- if (state.sourceSurface && !state.sourceSurfCairo &&
- !state.patternTransformChanged)
- {
- // This is the case where a PopGroupToSource has been done and this
- // paint is executed without changing the transform or the source.
- Matrix oldMat = mDT->GetTransform();
- IntSize surfSize = state.sourceSurface->GetSize();
- mDT->SetTransform(Matrix::Translation(-state.deviceOffset.x,
- -state.deviceOffset.y));
- mDT->DrawSurface(state.sourceSurface,
- Rect(state.sourceSurfaceDeviceOffset, Size(surfSize.width, surfSize.height)),
- Rect(Point(), Size(surfSize.width, surfSize.height)),
- DrawSurfaceOptions(), DrawOptions(alpha, GetOp()));
- mDT->SetTransform(oldMat);
- return;
- }
- Matrix mat = mDT->GetTransform();
- mat.Invert();
- Rect paintRect = mat.TransformBounds(Rect(Point(0, 0), Size(mDT->GetSize())));
- mDT->FillRect(paintRect, PatternFromState(this),
- DrawOptions(Float(alpha), GetOp()));
- }
- void
- gfxContext::PushGroupForBlendBack(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform)
- {
- if (gfxPrefs::UseNativePushLayer()) {
- Save();
- mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform);
- } else {
- DrawTarget* oldDT = mDT;
- PushNewDT(content);
- if (oldDT != mDT) {
- PushClipsToDT(mDT);
- }
- mDT->SetTransform(GetDTTransform());
- CurrentState().mBlendOpacity = aOpacity;
- CurrentState().mBlendMask = aMask;
- #ifdef DEBUG
- CurrentState().mWasPushedForBlendBack = true;
- #endif
- CurrentState().mBlendMaskTransform = aMaskTransform;
- }
- }
- static gfxRect
- GetRoundOutDeviceClipExtents(gfxContext* aCtx)
- {
- gfxContextMatrixAutoSaveRestore save(aCtx);
- aCtx->SetMatrix(gfxMatrix());
- gfxRect r = aCtx->GetClipExtents();
- r.RoundOut();
- return r;
- }
- void
- gfxContext::PushGroupAndCopyBackground(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform)
- {
- IntRect clipExtents;
- if (mDT->GetFormat() != SurfaceFormat::B8G8R8X8) {
- gfxRect clipRect = GetRoundOutDeviceClipExtents(this);
- clipExtents = IntRect::Truncate(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
- }
- bool pushOpaqueWithCopiedBG = (mDT->GetFormat() == SurfaceFormat::B8G8R8X8 ||
- mDT->GetOpaqueRect().Contains(clipExtents)) &&
- !mDT->GetUserData(&sDontUseAsSourceKey);
- if (gfxPrefs::UseNativePushLayer()) {
- Save();
- if (pushOpaqueWithCopiedBG) {
- mDT->PushLayer(true, aOpacity, aMask, aMaskTransform, IntRect(), true);
- } else {
- mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform, IntRect(), false);
- }
- } else {
- RefPtr<SourceSurface> source;
- // This snapshot can be nullptr if the DrawTarget is a cairo target that is currently
- // in an error state.
- if (pushOpaqueWithCopiedBG && (source = mDT->Snapshot())) {
- DrawTarget *oldDT = mDT;
- Point oldDeviceOffset = CurrentState().deviceOffset;
- PushNewDT(gfxContentType::COLOR);
- if (oldDT == mDT) {
- // Creating new DT failed.
- return;
- }
- CurrentState().mBlendOpacity = aOpacity;
- CurrentState().mBlendMask = aMask;
- #ifdef DEBUG
- CurrentState().mWasPushedForBlendBack = true;
- #endif
- CurrentState().mBlendMaskTransform = aMaskTransform;
- Point offset = CurrentState().deviceOffset - oldDeviceOffset;
- Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
- Rect sourceRect = surfRect + offset;
- mDT->SetTransform(Matrix());
- // XXX: It's really sad that we have to do this (for performance).
- // Once DrawTarget gets a PushLayer API we can implement this within
- // DrawTargetTiled.
- if (source->GetType() == SurfaceType::TILED) {
- SnapshotTiled *sourceTiled = static_cast<SnapshotTiled*>(source.get());
- for (uint32_t i = 0; i < sourceTiled->mSnapshots.size(); i++) {
- Rect tileSourceRect = sourceRect.Intersect(Rect(sourceTiled->mOrigins[i].x,
- sourceTiled->mOrigins[i].y,
- sourceTiled->mSnapshots[i]->GetSize().width,
- sourceTiled->mSnapshots[i]->GetSize().height));
- if (tileSourceRect.IsEmpty()) {
- continue;
- }
- Rect tileDestRect = tileSourceRect - offset;
- tileSourceRect -= sourceTiled->mOrigins[i];
- mDT->DrawSurface(sourceTiled->mSnapshots[i], tileDestRect, tileSourceRect);
- }
- } else {
- mDT->DrawSurface(source, surfRect, sourceRect);
- }
- mDT->SetOpaqueRect(oldDT->GetOpaqueRect());
- PushClipsToDT(mDT);
- mDT->SetTransform(GetDTTransform());
- return;
- }
- DrawTarget* oldDT = mDT;
- PushNewDT(content);
- if (oldDT != mDT) {
- PushClipsToDT(mDT);
- }
- mDT->SetTransform(GetDTTransform());
- CurrentState().mBlendOpacity = aOpacity;
- CurrentState().mBlendMask = aMask;
- #ifdef DEBUG
- CurrentState().mWasPushedForBlendBack = true;
- #endif
- CurrentState().mBlendMaskTransform = aMaskTransform;
- }
- }
- void
- gfxContext::PopGroupAndBlend()
- {
- if (gfxPrefs::UseNativePushLayer()) {
- mDT->PopLayer();
- Restore();
- } else {
- MOZ_ASSERT(CurrentState().mWasPushedForBlendBack);
- Float opacity = CurrentState().mBlendOpacity;
- RefPtr<SourceSurface> mask = CurrentState().mBlendMask;
- Matrix maskTransform = CurrentState().mBlendMaskTransform;
- RefPtr<SourceSurface> src = mDT->Snapshot();
- Point deviceOffset = CurrentState().deviceOffset;
- Restore();
- CurrentState().sourceSurfCairo = nullptr;
- CurrentState().sourceSurface = src;
- CurrentState().sourceSurfaceDeviceOffset = deviceOffset;
- CurrentState().pattern = nullptr;
- CurrentState().patternTransformChanged = false;
- Matrix mat = mTransform;
- mat.Invert();
- mat.PreTranslate(deviceOffset.x, deviceOffset.y); // device offset translation
- CurrentState().surfTransform = mat;
- CompositionOp oldOp = GetOp();
- SetOp(CompositionOp::OP_OVER);
- if (mask) {
- if (!maskTransform.HasNonTranslation()) {
- Mask(mask, opacity, Point(maskTransform._31, maskTransform._32));
- } else {
- Mask(mask, opacity, maskTransform);
- }
- } else {
- Paint(opacity);
- }
- SetOp(oldOp);
- }
- }
- #ifdef MOZ_DUMP_PAINTING
- void
- gfxContext::WriteAsPNG(const char* aFile)
- {
- gfxUtils::WriteAsPNG(mDT, aFile);
- }
- void
- gfxContext::DumpAsDataURI()
- {
- gfxUtils::DumpAsDataURI(mDT);
- }
- void
- gfxContext::CopyAsDataURI()
- {
- gfxUtils::CopyAsDataURI(mDT);
- }
- #endif
- void
- gfxContext::EnsurePath()
- {
- if (mPathBuilder) {
- mPath = mPathBuilder->Finish();
- mPathBuilder = nullptr;
- }
- if (mPath) {
- if (mTransformChanged) {
- Matrix mat = mTransform;
- mat.Invert();
- mat = mPathTransform * mat;
- mPathBuilder = mPath->TransformedCopyToBuilder(mat);
- mPath = mPathBuilder->Finish();
- mPathBuilder = nullptr;
- mTransformChanged = false;
- }
- return;
- }
- EnsurePathBuilder();
- mPath = mPathBuilder->Finish();
- mPathBuilder = nullptr;
- }
- void
- gfxContext::EnsurePathBuilder()
- {
- if (mPathBuilder && !mTransformChanged) {
- return;
- }
- if (mPath) {
- if (!mTransformChanged) {
- mPathBuilder = mPath->CopyToBuilder();
- mPath = nullptr;
- } else {
- Matrix invTransform = mTransform;
- invTransform.Invert();
- Matrix toNewUS = mPathTransform * invTransform;
- mPathBuilder = mPath->TransformedCopyToBuilder(toNewUS);
- }
- return;
- }
- DebugOnly<PathBuilder*> oldPath = mPathBuilder.get();
- if (!mPathBuilder) {
- mPathBuilder = mDT->CreatePathBuilder(FillRule::FILL_WINDING);
- if (mPathIsRect) {
- mPathBuilder->MoveTo(mRect.TopLeft());
- mPathBuilder->LineTo(mRect.TopRight());
- mPathBuilder->LineTo(mRect.BottomRight());
- mPathBuilder->LineTo(mRect.BottomLeft());
- mPathBuilder->Close();
- }
- }
- if (mTransformChanged) {
- // This could be an else if since this should never happen when
- // mPathBuilder is nullptr and mPath is nullptr. But this way we can
- // assert if all the state is as expected.
- MOZ_ASSERT(oldPath);
- MOZ_ASSERT(!mPathIsRect);
- Matrix invTransform = mTransform;
- invTransform.Invert();
- Matrix toNewUS = mPathTransform * invTransform;
- RefPtr<Path> path = mPathBuilder->Finish();
- if (!path) {
- gfxCriticalError() << "gfxContext::EnsurePathBuilder failed in PathBuilder::Finish";
- }
- mPathBuilder = path->TransformedCopyToBuilder(toNewUS);
- }
- mPathIsRect = false;
- }
- void
- gfxContext::FillAzure(const Pattern& aPattern, Float aOpacity)
- {
- AzureState &state = CurrentState();
- CompositionOp op = GetOp();
- if (mPathIsRect) {
- MOZ_ASSERT(!mTransformChanged);
- if (op == CompositionOp::OP_SOURCE) {
- // Emulate cairo operator source which is bound by mask!
- mDT->ClearRect(mRect);
- mDT->FillRect(mRect, aPattern, DrawOptions(aOpacity));
- } else {
- mDT->FillRect(mRect, aPattern, DrawOptions(aOpacity, op, state.aaMode));
- }
- } else {
- EnsurePath();
- mDT->Fill(mPath, aPattern, DrawOptions(aOpacity, op, state.aaMode));
- }
- }
- void
- gfxContext::PushClipsToDT(DrawTarget *aDT)
- {
- // Don't need to save the old transform, we'll be setting a new one soon!
- // Push all clips from the bottom of the stack to the clip before ours.
- for (unsigned int i = 0; i < mStateStack.Length() - 1; i++) {
- for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
- aDT->SetTransform(mStateStack[i].pushedClips[c].transform * GetDeviceTransform());
- if (mStateStack[i].pushedClips[c].path) {
- aDT->PushClip(mStateStack[i].pushedClips[c].path);
- } else {
- aDT->PushClipRect(mStateStack[i].pushedClips[c].rect);
- }
- }
- }
- }
- CompositionOp
- gfxContext::GetOp()
- {
- if (CurrentState().op != CompositionOp::OP_SOURCE) {
- return CurrentState().op;
- }
- AzureState &state = CurrentState();
- if (state.pattern) {
- if (state.pattern->IsOpaque()) {
- return CompositionOp::OP_OVER;
- } else {
- return CompositionOp::OP_SOURCE;
- }
- } else if (state.sourceSurface) {
- if (state.sourceSurface->GetFormat() == SurfaceFormat::B8G8R8X8) {
- return CompositionOp::OP_OVER;
- } else {
- return CompositionOp::OP_SOURCE;
- }
- } else {
- if (state.color.a > 0.999) {
- return CompositionOp::OP_OVER;
- } else {
- return CompositionOp::OP_SOURCE;
- }
- }
- }
- /* SVG font code can change the transform after having set the pattern on the
- * context. When the pattern is set it is in user space, if the transform is
- * changed after doing so the pattern needs to be converted back into userspace.
- * We just store the old pattern transform here so that we only do the work
- * needed here if the pattern is actually used.
- * We need to avoid doing this when this ChangeTransform comes from a restore,
- * since the current pattern and the current transform are both part of the
- * state we know the new CurrentState()'s values are valid. But if we assume
- * a change they might become invalid since patternTransformChanged is part of
- * the state and might be false for the restored AzureState.
- */
- void
- gfxContext::ChangeTransform(const Matrix &aNewMatrix, bool aUpdatePatternTransform)
- {
- AzureState &state = CurrentState();
- if (aUpdatePatternTransform && (state.pattern || state.sourceSurface)
- && !state.patternTransformChanged) {
- state.patternTransform = GetDTTransform();
- state.patternTransformChanged = true;
- }
- if (mPathIsRect) {
- Matrix invMatrix = aNewMatrix;
-
- invMatrix.Invert();
- Matrix toNewUS = mTransform * invMatrix;
- if (toNewUS.IsRectilinear()) {
- mRect = toNewUS.TransformBounds(mRect);
- mRect.NudgeToIntegers();
- } else {
- mPathBuilder = mDT->CreatePathBuilder(FillRule::FILL_WINDING);
- mPathBuilder->MoveTo(toNewUS.TransformPoint(mRect.TopLeft()));
- mPathBuilder->LineTo(toNewUS.TransformPoint(mRect.TopRight()));
- mPathBuilder->LineTo(toNewUS.TransformPoint(mRect.BottomRight()));
- mPathBuilder->LineTo(toNewUS.TransformPoint(mRect.BottomLeft()));
- mPathBuilder->Close();
- mPathIsRect = false;
- }
- // No need to consider the transform changed now!
- mTransformChanged = false;
- } else if ((mPath || mPathBuilder) && !mTransformChanged) {
- mTransformChanged = true;
- mPathTransform = mTransform;
- }
- mTransform = aNewMatrix;
- mDT->SetTransform(GetDTTransform());
- }
- Rect
- gfxContext::GetAzureDeviceSpaceClipBounds()
- {
- Rect rect(CurrentState().deviceOffset.x, CurrentState().deviceOffset.y,
- Float(mDT->GetSize().width), Float(mDT->GetSize().height));
- for (unsigned int i = 0; i < mStateStack.Length(); i++) {
- for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
- AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
- if (clip.path) {
- Rect bounds = clip.path->GetBounds(clip.transform);
- rect.IntersectRect(rect, bounds);
- } else {
- rect.IntersectRect(rect, clip.transform.TransformBounds(clip.rect));
- }
- }
- }
- return rect;
- }
- Point
- gfxContext::GetDeviceOffset() const
- {
- return CurrentState().deviceOffset;
- }
- Matrix
- gfxContext::GetDeviceTransform() const
- {
- return Matrix::Translation(-CurrentState().deviceOffset.x,
- -CurrentState().deviceOffset.y);
- }
- Matrix
- gfxContext::GetDTTransform() const
- {
- Matrix mat = mTransform;
- mat._31 -= CurrentState().deviceOffset.x;
- mat._32 -= CurrentState().deviceOffset.y;
- return mat;
- }
- void
- gfxContext::PushNewDT(gfxContentType content)
- {
- Rect clipBounds = GetAzureDeviceSpaceClipBounds();
- clipBounds.RoundOut();
- clipBounds.width = std::max(1.0f, clipBounds.width);
- clipBounds.height = std::max(1.0f, clipBounds.height);
- SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content);
- RefPtr<DrawTarget> newDT =
- mDT->CreateSimilarDrawTarget(IntSize(int32_t(clipBounds.width), int32_t(clipBounds.height)),
- format);
- if (!newDT) {
- NS_WARNING("Failed to create DrawTarget of sufficient size.");
- newDT = mDT->CreateSimilarDrawTarget(IntSize(64, 64), format);
- if (!newDT) {
- if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()
- #ifdef XP_WIN
- && !(mDT->GetBackendType() == BackendType::DIRECT2D1_1 &&
- !DeviceManagerDx::Get()->GetContentDevice())
- #endif
- ) {
- // If even this fails.. we're most likely just out of memory!
- NS_ABORT_OOM(BytesPerPixel(format) * 64 * 64);
- }
- newDT = CurrentState().drawTarget;
- }
- }
- Save();
- CurrentState().drawTarget = newDT;
- CurrentState().deviceOffset = clipBounds.TopLeft();
- mDT = newDT;
- }
|