/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */
if (mSnapshot) {
MutexAutoLock lock(*mSnapshotLock); // We may hold the only reference. MarkIndependent will clear mSnapshot; // keep the snapshot object alive so it doesn't get destroyed while // MarkIndependent is running.
RefPtr<SourceSurfaceD2D1> deathGrip = mSnapshot; // mSnapshot can be treated as independent of this DrawTarget since we know // this DrawTarget won't change again.
deathGrip->MarkIndependent(); // mSnapshot will be cleared now.
}
if (mDC && IsDeviceContextValid()) { // The only way mDC can be null is if Init failed, but it can happen and the // destructor is the only place where we need to check for it since the // DrawTarget will destroyed right after Init fails.
mDC->EndDraw();
}
{ // Until this point in the destructor it -must- still be valid for // FlushInternal to be called on this.
StaticMutexAutoLock lock(Factory::mDTDependencyLock); // Targets depending on us can break that dependency, since we're obviously // not going to be modified in the future. for (auto iter = mDependentTargets.begin(); iter != mDependentTargets.end();
iter++) {
(*iter)->mDependingOnTargets.erase(this);
} // Our dependencies on other targets no longer matter. for (TargetSet::iterator iter = mDependingOnTargets.begin();
iter != mDependingOnTargets.end(); iter++) {
(*iter)->mDependentTargets.erase(this);
}
}
}
bool DrawTargetD2D1::IsValid() const { if (mInitState != InitState::Uninitialized && !IsDeviceContextValid()) { returnfalse;
} if (NS_IsMainThread()) { // Uninitialized DTs are considered valid. return mInitState != InitState::Failure;
} else { returnconst_cast<DrawTargetD2D1*>(this)->EnsureInitialized();
}
}
already_AddRefed<SourceSurface> DrawTargetD2D1::Snapshot() { if (!EnsureInitialized()) { return nullptr;
}
already_AddRefed<SourceSurface> DrawTargetD2D1::IntoLuminanceSource(
LuminanceType aLuminanceType, float aOpacity) { if (!EnsureInitialized()) { return nullptr;
} if ((aLuminanceType != LuminanceType::LUMINANCE) || // See bug 1372577, some race condition where we get invalid // results with D2D in the parent process. Fallback in that case.
XRE_IsParentProcess()) { return DrawTarget::IntoLuminanceSource(aLuminanceType, aOpacity);
}
// Create the luminance effect if (!EnsureLuminanceEffect()) { return DrawTarget::IntoLuminanceSource(aLuminanceType, aOpacity);
}
// Command lists are kept around by device contexts until EndDraw is called, // this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw // are expensive though, especially relatively when little work is done, so // we try to reduce the amount of times we execute these purges. staticconst uint32_t kPushedLayersBeforePurge = 25; // Rendering glyphs with different transforms causes the glyph cache to grow // very large (see bug 1474883) so we must call EndDraw every so often. staticconst uint32_t kTransformedGlyphsBeforePurge = 1000;
// Here we scale the source pattern up to the size and position where we want // it to be.
Matrix transform;
transform.PreTranslate(aDest.X() - source.X() * xScale,
aDest.Y() - source.Y() * yScale);
transform.PreScale(xScale, yScale);
if (!image) {
gfxWarning() << *this << ": Unable to get D2D image for surface."; return;
}
RefPtr<ID2D1Bitmap> bitmap;
HRESULT hr = E_FAIL; if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) { // If this is called with a DataSourceSurface it might do a partial upload // that our DrawBitmap call doesn't support.
hr = image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
}
if (SUCCEEDED(hr) && bitmap &&
aSurfOptions.mSamplingBounds == SamplingBounds::UNBOUNDED) {
mDC->DrawBitmap(bitmap, D2DRect(aDest), aOptions.mAlpha,
D2DFilter(aSurfOptions.mSamplingFilter), D2DRect(source));
} else { // This has issues ignoring the alpha channel on windows 7 with images // marked opaque.
MOZ_ASSERT(aSurface->GetFormat() != SurfaceFormat::B8G8R8X8);
if (!image) {
gfxWarning() << "Failed to get image for surface."; return;
}
if (!PrepareForDrawing(aOptions.mCompositionOp, aSource)) { return;
}
IntSize size =
IntSize::Truncate(aMask->GetSize().width, aMask->GetSize().height);
Rect dest =
Rect(aOffset.x + aMask->GetRect().x, aOffset.y + aMask->GetRect().y, Float(size.width), Float(size.height));
HRESULT hr = image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap)); if (!bitmap || FAILED(hr)) { // D2D says if we have an actual ID2D1Image and not a bitmap underlying the // object, we can't query for a bitmap. Instead, Push/PopLayer
gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces. " "Falling back to push/pop layer";
FinalizeDrawing(aOptions.mCompositionOp, aSource); return;
} else { // If this is a data source surface, we might have created a partial bitmap // for this surface and only uploaded part of the mask. In that case, // we have to fixup our sizes here.
size.width = bitmap->GetSize().width;
size.height = bitmap->GetSize().height;
dest.SetWidth(size.width);
dest.SetHeight(size.height);
}
// FillOpacityMask only works if the antialias mode is MODE_ALIASED
mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
ScaledFontDWrite* font = static_cast<ScaledFontDWrite*>(aFont);
// May be null, if we failed to initialize the default rendering params.
RefPtr<IDWriteRenderingParams> params =
font->DWriteSettings().RenderingParams();
AntialiasMode aaMode = font->GetDefaultAAMode();
if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
aaMode = aOptions.mAntialiasMode;
}
if (!PrepareForDrawing(aOptions.mCompositionOp, aPattern)) { return;
}
// If we have a complex clip in our stack and we have a transparent // background, and subpixel AA is permitted, we need to repush our layer // stack limited by the glyph run bounds initializing our layers for // subpixel AA. if (needsRepushedLayers) {
mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
DWRITE_MEASURING_MODE_NATURAL, &rect);
rect.left = std::floor(rect.left);
rect.right = std::ceil(rect.right);
rect.top = std::floor(rect.top);
rect.bottom = std::ceil(rect.bottom);
PopAllClips();
if (!mTransform.IsRectilinear()) { // We must limit the pixels we touch to the -user space- bounds of // the glyphs being drawn. In order not to get transparent pixels // copied up in our pushed layer stack.
D2D1_RECT_F userRect;
mDC->SetTransform(D2D1::IdentityMatrix());
mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
DWRITE_MEASURING_MODE_NATURAL, &userRect);
// The transform of clips is relative to the world matrix, since we use the // total transform for the clips, make the world matrix identity.
mDC->SetTransform(D2D1::IdentityMatrix());
mTransformDirty = true;
if (CurrentLayer().mClipsArePushed) {
PushD2DLayer(mDC, clip.mGeometry, clip.mTransform, clip.mIsPixelAligned);
}
}
void DrawTargetD2D1::PushClipRect(const Rect& aRect) { if (!EnsureInitialized()) { return;
} if (!mTransform.IsRectilinear()) { // Whoops, this isn't a rectangle in device space, Direct2D will not deal // with this transform the way we want it to. // See remarks: // http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx
RefPtr<ID2D1Geometry> geom = ConvertRectToGeometry(D2DRect(aRect)); return PushClipGeometry(geom, D2DMatrix(mTransform));
}
mCurrentClippedGeometry = nullptr;
PushedClip clip;
Rect rect = mTransform.TransformBounds(aRect);
IntRect intRect;
clip.mIsPixelAligned = rect.ToIntRect(&intRect);
// Do not store the transform, just store the device space rectangle directly.
clip.mBounds = D2DRect(rect);
void DrawTargetD2D1::PushDeviceSpaceClipRects(const IntRect* aRects,
uint32_t aCount) { if (!EnsureInitialized()) { return;
} // Build a path for the union of the rects.
RefPtr<ID2D1PathGeometry> path;
factory()->CreatePathGeometry(getter_AddRefs(path));
RefPtr<ID2D1GeometrySink> sink;
path->Open(getter_AddRefs(sink));
sink->SetFillMode(D2D1_FILL_MODE_WINDING); for (uint32_t i = 0; i < aCount; i++) { const IntRect& rect = aRects[i];
sink->BeginFigure(D2DPoint(rect.TopLeft()), D2D1_FIGURE_BEGIN_FILLED);
D2D1_POINT_2F lines[3] = {D2DPoint(rect.TopRight()),
D2DPoint(rect.BottomRight()),
D2DPoint(rect.BottomLeft())};
sink->AddLines(lines, 3);
sink->EndFigure(D2D1_FIGURE_END_CLOSED);
}
sink->Close();
// The path is in device-space, so there is no transform needed, // and all rects are pixel aligned.
PushClipGeometry(path, D2D1::IdentityMatrix(), true);
}
void DrawTargetD2D1::PopClip() { if (!EnsureInitialized()) { return;
}
mCurrentClippedGeometry = nullptr; if (CurrentLayer().mPushedClips.empty()) {
gfxDevCrash(LogReason::UnbalancedClipStack)
<< "DrawTargetD2D1::PopClip: No clip to pop."; return;
}
if (CurrentLayer().mClipsArePushed) { if (CurrentLayer().mPushedClips.back().mGeometry) {
mDC->PopLayer();
} else {
mDC->PopAxisAlignedClip();
}
}
CurrentLayer().mPushedClips.pop_back();
}
bool DrawTargetD2D1::RemoveAllClips() { if (!EnsureInitialized()) { returnfalse;
}
mCurrentClippedGeometry = nullptr; while (!CurrentLayer().mPushedClips.empty()) {
PopClip();
} returntrue;
}
maskTransform =
maskTransform.PreTranslate(aMask->GetRect().X(), aMask->GetRect().Y()); // The mask is given in user space. Our layer will apply it in device space.
maskTransform = maskTransform * mTransform;
if (image) {
IntSize maskSize = aMask->GetSize();
HRESULT hr = mDC->CreateImageBrush(
image,
D2D1::ImageBrushProperties(
D2D1::RectF(0, 0, maskSize.width, maskSize.height)),
D2D1::BrushProperties(1.0f, D2DMatrix(maskTransform)),
getter_AddRefs(mask)); if (FAILED(hr)) {
gfxWarning() << "[D2D1.1] Failed to create a ImageBrush, code: "
<< hexa(hr);
}
factory()->CreatePathGeometry(getter_AddRefs(clip));
RefPtr<ID2D1GeometrySink> sink;
clip->Open(getter_AddRefs(sink));
AddRectToSink(sink, D2D1::RectF(0, 0, aMask->GetSize().width,
aMask->GetSize().height));
sink->Close();
} else {
gfxCriticalError() << "Failed to get image for mask surface!";
}
}
void DrawTargetD2D1::PopLayer() { // We must have at least one layer at all times.
MOZ_ASSERT(mPushedLayers.size() > 1);
MOZ_ASSERT(CurrentLayer().mPushedClips.size() == 0); if (!EnsureInitialized() || mPushedLayers.size() <= 1) { return;
}
RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
mPermitSubpixelAA = CurrentLayer().mOldPermitSubpixelAA;
void DrawTargetD2D1::FlushInternal(bool aHasDependencyMutex /* = false */) { if (IsDeviceContextValid()) { if ((mUsedCommandListsSincePurge >= kPushedLayersBeforePurge ||
mTransformedGlyphsSinceLastPurge >= kTransformedGlyphsBeforePurge) &&
mPushedLayers.size() == 1) { // It's important to pop all clips as otherwise layers can forget about // their clip when doing an EndDraw. When we have layers pushed we cannot // easily pop all underlying clips to delay the purge until we have no // layers pushed.
PopAllClips();
mUsedCommandListsSincePurge = 0;
mTransformedGlyphsSinceLastPurge = 0;
mDC->EndDraw();
mDC->BeginDraw();
} else {
mDC->Flush();
}
}
Maybe<StaticMutexAutoLock> lock;
if (!aHasDependencyMutex) {
lock.emplace(Factory::mDTDependencyLock);
}
Factory::mDTDependencyLock.AssertCurrentThreadOwns(); // We no longer depend on any target. for (TargetSet::iterator iter = mDependingOnTargets.begin();
iter != mDependingOnTargets.end(); iter++) {
(*iter)->mDependentTargets.erase(this);
}
mDependingOnTargets.clear();
}
RefPtr<ID2D1Device> device = Factory::GetD2D1Device(&mDeviceSeq); if (!device) {
gfxCriticalNote << "[D2D1.1] Failed to obtain a device for " "DrawTargetD2D1::EnsureInitialized()."; returnfalse;
}
if (FAILED(hr)) {
gfxCriticalError() << "[D2D1.1] 2Failed to create a DeviceContext, code: "
<< hexa(hr) << " format " << (int)mFormat; returnfalse;
}
if (!mSurface) { if (mDC->GetMaximumBitmapSize() < UINT32(mSize.width) ||
mDC->GetMaximumBitmapSize() < UINT32(mSize.height)) { // This is 'ok', so don't assert
gfxCriticalNote << "[D2D1.1] Attempt to use unsupported surface size "
<< mSize; returnfalse;
}
void DrawTargetD2D1::MarkChanged() { if (mSnapshot) {
MutexAutoLock lock(*mSnapshotLock); if (mSnapshot->hasOneRef()) { // Just destroy it, since no-one else knows about it.
mSnapshot = nullptr;
} else {
mSnapshot->DrawTargetWillChange(); // The snapshot will no longer depend on this target.
MOZ_ASSERT(!mSnapshot);
}
}
{
StaticMutexAutoLock lock(Factory::mDTDependencyLock); if (mDependentTargets.size()) { // Copy mDependentTargets since the Flush()es below will modify it.
TargetSet tmpTargets = mDependentTargets; for (TargetSet::iterator iter = tmpTargets.begin();
iter != tmpTargets.end(); iter++) {
(*iter)->FlushInternal(true);
} // The Flush() should have broken all dependencies on this target.
MOZ_ASSERT(!mDependentTargets.size());
}
}
}
bool patternSupported = IsPatternSupportedByD2D(aPattern, aOp); if (D2DSupportsPrimitiveBlendMode(aOp) && patternSupported) { // It's important to do this before FlushTransformToDC! As this will cause // the transform to become dirty.
FlushTransformToDC();
if (aOp != CompositionOp::OP_OVER) {
mDC->SetPrimitiveBlend(D2DPrimitiveBlendMode(aOp));
}
returntrue;
}
HRESULT result = mDC->CreateCommandList(getter_AddRefs(mCommandList));
mDC->SetTarget(mCommandList);
mUsedCommandListsSincePurge++;
// This is where we should have a valid command list. If we don't, something // is wrong, and it's likely an OOM. if (!mCommandList) {
gfxDevCrash(LogReason::InvalidCommandList)
<< "Invalid D2D1.1 command list on creation "
<< mUsedCommandListsSincePurge << ", " << gfx::hexa(result);
}
if (ShouldClipTemporarySurfaceDrawing(aOp, aPattern, clipIsComplex)) {
PopClipsFromDC(mDC);
}
mDC->SetTarget(CurrentTarget()); if (!mCommandList) {
gfxDevCrash(LogReason::InvalidCommandList)
<< "Invalid D21.1 command list on finalize"; return;
}
mCommandList->Close();
if (aPattern.GetType() == PatternType::CONIC_GRADIENT) { const ConicGradientPattern* pat = static_cast<const ConicGradientPattern*>(&aPattern);
if (!pat->mStops ||
pat->mStops->GetBackendType() != BackendType::DIRECT2D) { // Draw nothing because of no color stops return;
}
RefPtr<ID2D1Effect> conicGradientEffect;
void DrawTargetD2D1::AddDependencyOnSource(SourceSurfaceD2D1* aSource) {
Maybe<MutexAutoLock> snapshotLock; // We grab the SnapshotLock as well, this guaranteeds aSource->mDrawTarget // cannot be cleared in between the if statement and the dereference. if (aSource->mSnapshotLock) {
snapshotLock.emplace(*aSource->mSnapshotLock);
}
{
StaticMutexAutoLock lock(Factory::mDTDependencyLock); if (aSource->mDrawTarget &&
!mDependingOnTargets.count(aSource->mDrawTarget)) {
aSource->mDrawTarget->mDependentTargets.insert(this);
mDependingOnTargets.insert(aSource->mDrawTarget);
}
}
}
// if pathGeom is null then pathRect represents the path.
RefPtr<ID2D1Geometry> pathGeom;
D2D1_RECT_F pathRect; bool pathRectIsAxisAligned = false; auto iter = CurrentLayer().mPushedClips.begin();
iter++; for (; iter != CurrentLayer().mPushedClips.end(); iter++) { // Do nothing but add it to the current clip bounds. if (!iter->mGeometry && iter->mIsPixelAligned) {
mCurrentClipBounds.IntersectRect(
mCurrentClipBounds,
IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top),
int32_t(iter->mBounds.right - iter->mBounds.left),
int32_t(iter->mBounds.bottom - iter->mBounds.top))); continue;
}
if (!pathGeom) { if (pathRectIsAxisAligned) {
mCurrentClipBounds.IntersectRect(
mCurrentClipBounds,
IntRect(int32_t(pathRect.left), int32_t(pathRect.top),
int32_t(pathRect.right - pathRect.left),
int32_t(pathRect.bottom - pathRect.top)));
} if (iter->mGeometry) { // See if pathRect needs to go into the path geometry. if (!pathRectIsAxisAligned) {
pathGeom = ConvertRectToGeometry(pathRect);
} else {
pathGeom = GetTransformedGeometry(iter->mGeometry, iter->mTransform);
}
} else {
pathRect = IntersectRect(pathRect, iter->mBounds);
pathRectIsAxisAligned = false; continue;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.