void UseTileTexture(CompositableTextureHostRef& aTexture, CompositableTextureSourceRef& aTextureSource, const IntRect& aUpdateRect, Compositor* aCompositor) { MOZ_ASSERT(aTexture); if (!aTexture) { return; } if (aCompositor) { aTexture->SetCompositor(aCompositor); } if (!aUpdateRect.IsEmpty()) { #ifdef MOZ_GFX_OPTIMIZE_MOBILE aTexture->Updated(nullptr); #else // We possibly upload the entire texture contents here. This is a purposeful // decision, as sub-image upload can often be slow and/or unreliable, but // we may want to reevaluate this in the future. // For !HasInternalBuffer() textures, this is likely a no-op. nsIntRegion region = aUpdateRect; aTexture->Updated(®ion); #endif } aTexture->PrepareTextureSource(aTextureSource); }
void DrawEventRecorderMemory::FlushItem(IntRect aRect) { MOZ_RELEASE_ASSERT(!aRect.IsEmpty()); // Detaching our existing resources will add some // destruction events to our stream so we need to do that // first. DetachResources(); // See moz2d_renderer.rs for a description of the stream format WriteElement(mIndex, mOutputStream.mLength); // write out the fonts into the extra data section mSerializeCallback(mOutputStream, mScaledFonts); WriteElement(mIndex, mOutputStream.mLength); WriteElement(mIndex, aRect.x); WriteElement(mIndex, aRect.y); WriteElement(mIndex, aRect.XMost()); WriteElement(mIndex, aRect.YMost()); ClearResources(); // write out a new header for the next recording in the stream WriteHeader(mOutputStream); }
void ClientLayerManager::MakeSnapshotIfRequired() { if (!mShadowTarget) { return; } if (mWidget) { if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) { // The compositor doesn't draw to a different sized surface // when there's a rotation. Instead we rotate the result // when drawing into dt LayoutDeviceIntRect outerBounds; mWidget->GetBounds(outerBounds); IntRect bounds = ToOutsideIntRect(mShadowTarget->GetClipExtents()); if (mTargetRotation) { bounds = RotateRect(bounds, outerBounds.ToUnknownRect(), mTargetRotation); } SurfaceDescriptor inSnapshot; if (!bounds.IsEmpty() && mForwarder->AllocSurfaceDescriptor(bounds.Size(), gfxContentType::COLOR_ALPHA, &inSnapshot)) { // Make a copy of |inSnapshot| because the call to send it over IPC // will call forget() on the Shmem inside, and zero it out. SurfaceDescriptor outSnapshot = inSnapshot; if (remoteRenderer->SendMakeSnapshot(inSnapshot, bounds)) { RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(outSnapshot); DrawTarget* dt = mShadowTarget->GetDrawTarget(); Rect dstRect(bounds.x, bounds.y, bounds.width, bounds.height); Rect srcRect(0, 0, bounds.width, bounds.height); gfx::Matrix rotate = ComputeTransformForUnRotation(outerBounds.ToUnknownRect(), mTargetRotation); gfx::Matrix oldMatrix = dt->GetTransform(); dt->SetTransform(rotate * oldMatrix); dt->DrawSurface(surf, dstRect, srcRect, DrawSurfaceOptions(), DrawOptions(1.0f, CompositionOp::OP_OVER)); dt->SetTransform(oldMatrix); } mForwarder->DestroySurfaceDescriptor(&outSnapshot); } } } mShadowTarget = nullptr; }
/** * aSrcRect: Rect relative to the aSrc surface * aDestPoint: Point inside aDest surface */ void CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest, IntRect aSrcRect, IntPoint aDestPoint) { if (aSrcRect.Overflows() || IntRect(aDestPoint, aSrcRect.Size()).Overflows()) { MOZ_CRASH("we should never be getting invalid rects at this point"); } MOZ_RELEASE_ASSERT(aSrc->GetFormat() == aDest->GetFormat(), "different surface formats"); MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aSrc->GetSize()).Contains(aSrcRect), "source rect too big for source surface"); MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aDest->GetSize()).Contains(IntRect(aDestPoint, aSrcRect.Size())), "dest surface too small"); if (aSrcRect.IsEmpty()) { return; } DataSourceSurface::ScopedMap srcMap(aSrc, DataSourceSurface::READ); DataSourceSurface::ScopedMap destMap(aDest, DataSourceSurface::WRITE); if (MOZ2D_WARN_IF(!srcMap.IsMapped() || !destMap.IsMapped())) { return; } uint8_t* sourceData = DataAtOffset(aSrc, srcMap.GetMappedSurface(), aSrcRect.TopLeft()); uint32_t sourceStride = srcMap.GetStride(); uint8_t* destData = DataAtOffset(aDest, destMap.GetMappedSurface(), aDestPoint); uint32_t destStride = destMap.GetStride(); if (BytesPerPixel(aSrc->GetFormat()) == 4) { for (int32_t y = 0; y < aSrcRect.height; y++) { PodCopy((int32_t*)destData, (int32_t*)sourceData, aSrcRect.width); sourceData += sourceStride; destData += destStride; } } else if (BytesPerPixel(aSrc->GetFormat()) == 1) { for (int32_t y = 0; y < aSrcRect.height; y++) { PodCopy(destData, sourceData, aSrcRect.width); sourceData += sourceStride; destData += destStride; } } }
static IntRect TransformRect(const IntRect& aRect, const Matrix4x4& aTransform) { if (aRect.IsEmpty()) { return IntRect(); } Rect rect(aRect.x, aRect.y, aRect.width, aRect.height); rect = aTransform.TransformBounds(rect); rect.RoundOut(); IntRect intRect; if (!gfxUtils::GfxRectToIntRect(ThebesRect(rect), &intRect)) { return IntRect(); } return intRect; }
void TexturedLayerMLGPU::AssignBigImage(FrameBuilder* aBuilder, RenderViewMLGPU* aView, BigImageIterator* aIter, const Maybe<Polygon>& aGeometry) { const Matrix4x4& transform = GetLayer()->GetEffectiveTransformForBuffer(); // Note that we don't need to assign these in any particular order, since // they do not overlap. do { IntRect tileRect = aIter->GetTileRect(); IntRect rect = tileRect.Intersect(mPictureRect); if (rect.IsEmpty()) { continue; } { Rect screenRect = transform.TransformBounds(Rect(rect)); screenRect.MoveBy(-aView->GetTargetOffset()); screenRect = screenRect.Intersect(Rect(mComputedClipRect.ToUnknownRect())); if (screenRect.IsEmpty()) { // This tile is not in the clip region, so skip it. continue; } } RefPtr<TextureSource> tile = mBigImageTexture->ExtractCurrentTile(); if (!tile) { continue; } // Create a temporary item. RefPtr<TempImageLayerMLGPU> item = new TempImageLayerMLGPU(aBuilder->GetManager()); item->Init(this, tile, rect); Maybe<Polygon> geometry = aGeometry; item->AddBoundsToView(aBuilder, aView, std::move(geometry)); // Since the layer tree is not holding this alive, we have to ask the // FrameBuilder to do it for us. aBuilder->RetainTemporaryLayer(item); } while (aIter->NextTile()); }
//****************************************************************************** void FrameAnimator::ClearFrame(uint8_t* aFrameData, const IntRect& aFrameRect, const IntRect& aRectToClear) { if (!aFrameData || aFrameRect.width <= 0 || aFrameRect.height <= 0 || aRectToClear.width <= 0 || aRectToClear.height <= 0) { return; } IntRect toClear = aFrameRect.Intersect(aRectToClear); if (toClear.IsEmpty()) { return; } uint32_t bytesPerRow = aFrameRect.width * 4; for (int row = toClear.y; row < toClear.y + toClear.height; ++row) { memset(aFrameData + toClear.x * 4 + row * bytesPerRow, 0, toClear.width * 4); } }
already_AddRefed<DrawTarget> nsSVGClipPathFrame::CreateClipMask(gfxContext& aReferenceContext, IntPoint& aOffset) { gfxContextMatrixAutoSaveRestore autoRestoreMatrix(&aReferenceContext); aReferenceContext.SetMatrix(gfxMatrix()); gfxRect rect = aReferenceContext.GetClipExtents(); IntRect bounds = RoundedOut(ToRect(rect)); if (bounds.IsEmpty()) { // We don't need to create a mask surface, all drawing is clipped anyway. return nullptr; } DrawTarget* referenceDT = aReferenceContext.GetDrawTarget(); RefPtr<DrawTarget> maskDT = referenceDT->CreateSimilarDrawTarget(bounds.Size(), SurfaceFormat::A8); aOffset = bounds.TopLeft(); return maskDT.forget(); }
void RasterImage::NotifyProgress(Progress aProgress, const IntRect& aInvalidRect /* = IntRect() */, const Maybe<uint32_t>& aFrameCount /* = Nothing() */, DecoderFlags aDecoderFlags /* = DefaultDecoderFlags() */, SurfaceFlags aSurfaceFlags /* = DefaultSurfaceFlags() */) { MOZ_ASSERT(NS_IsMainThread()); // Ensure that we stay alive long enough to finish notifying. RefPtr<RasterImage> image = this; const bool wasDefaultFlags = aSurfaceFlags == DefaultSurfaceFlags(); if (!aInvalidRect.IsEmpty() && wasDefaultFlags) { // Update our image container since we're invalidating. UpdateImageContainer(); } if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY)) { // We may have decoded new animation frames; update our animation state. MOZ_ASSERT_IF(aFrameCount && *aFrameCount > 1, mAnimationState || mError); if (mAnimationState && aFrameCount) { mAnimationState->UpdateKnownFrameCount(*aFrameCount); } // If we should start animating right now, do so. if (mAnimationState && aFrameCount == Some(1u) && mPendingAnimation && ShouldAnimate()) { StartAnimation(); } } // Tell the observers what happened. image->mProgressTracker->SyncNotifyProgress(aProgress, aInvalidRect); }
already_AddRefed<SourceSurface> nsSVGClipPathFrame::GetClipMask(gfxContext& aReferenceContext, nsIFrame* aClippedFrame, const gfxMatrix& aMatrix, Matrix* aMaskTransform, SourceSurface* aExtraMask, const Matrix& aExtraMasksTransform) { MOZ_ASSERT(!IsTrivial(), "Caller needs to use ApplyClipPath"); DrawTarget& aReferenceDT = *aReferenceContext.GetDrawTarget(); // A clipPath can reference another clipPath. We re-enter this method for // each clipPath in a reference chain, so here we limit chain length: static int16_t sRefChainLengthCounter = AutoReferenceLimiter::notReferencing; AutoReferenceLimiter refChainLengthLimiter(&sRefChainLengthCounter, MAX_SVG_CLIP_PATH_REFERENCE_CHAIN_LENGTH); if (!refChainLengthLimiter.Reference()) { return nullptr; // Reference chain is too long! } // And to prevent reference loops we check that this clipPath only appears // once in the reference chain (if any) that we're currently processing: AutoReferenceLimiter refLoopDetector(&mReferencing, 1); if (!refLoopDetector.Reference()) { return nullptr; // Reference loop! } IntRect devSpaceClipExtents; { gfxContextMatrixAutoSaveRestore autoRestoreMatrix(&aReferenceContext); aReferenceContext.SetMatrix(gfxMatrix()); gfxRect rect = aReferenceContext.GetClipExtents(); devSpaceClipExtents = RoundedOut(ToRect(rect)); if (devSpaceClipExtents.IsEmpty()) { // We don't need to create a mask surface, all drawing is clipped anyway. return nullptr; } } RefPtr<DrawTarget> maskDT = aReferenceDT.CreateSimilarDrawTarget(devSpaceClipExtents.Size(), SurfaceFormat::A8); gfxMatrix mat = aReferenceContext.CurrentMatrix() * gfxMatrix::Translation(-devSpaceClipExtents.TopLeft()); // Paint this clipPath's contents into maskDT: { RefPtr<gfxContext> ctx = new gfxContext(maskDT); ctx->SetMatrix(mat); // We need to set mMatrixForChildren here so that under the PaintSVG calls // on our children (below) our GetCanvasTM() method will return the correct // transform. mMatrixForChildren = GetClipPathTransform(aClippedFrame) * aMatrix; // Check if this clipPath is itself clipped by another clipPath: nsSVGClipPathFrame* clipPathThatClipsClipPath = nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr); bool clippingOfClipPathRequiredMasking; if (clipPathThatClipsClipPath) { ctx->Save(); clippingOfClipPathRequiredMasking = !clipPathThatClipsClipPath->IsTrivial(); if (!clippingOfClipPathRequiredMasking) { clipPathThatClipsClipPath->ApplyClipPath(*ctx, aClippedFrame, aMatrix); } else { Matrix maskTransform; RefPtr<SourceSurface> mask = clipPathThatClipsClipPath->GetClipMask(*ctx, aClippedFrame, aMatrix, &maskTransform); ctx->PushGroupForBlendBack(gfxContentType::ALPHA, 1.0, mask, maskTransform); // The corresponding PopGroupAndBlend call below will mask the // blend using |mask|. } } // Paint our children into the mask: for (nsIFrame* kid = mFrames.FirstChild(); kid; kid = kid->GetNextSibling()) { nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); if (SVGFrame) { // The CTM of each frame referencing us can be different. SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED); bool isOK = true; // Children of this clipPath may themselves be clipped. nsSVGClipPathFrame *clipPathThatClipsChild = nsSVGEffects::GetEffectProperties(kid).GetClipPathFrame(&isOK); if (!isOK) { continue; } bool childsClipPathRequiresMasking; if (clipPathThatClipsChild) { childsClipPathRequiresMasking = !clipPathThatClipsChild->IsTrivial(); ctx->Save(); if (!childsClipPathRequiresMasking) { clipPathThatClipsChild->ApplyClipPath(*ctx, aClippedFrame, aMatrix); } else { Matrix maskTransform; RefPtr<SourceSurface> mask = clipPathThatClipsChild->GetClipMask(*ctx, aClippedFrame, aMatrix, &maskTransform); ctx->PushGroupForBlendBack(gfxContentType::ALPHA, 1.0, mask, maskTransform); // The corresponding PopGroupAndBlend call below will mask the // blend using |mask|. } } gfxMatrix toChildsUserSpace = mMatrixForChildren; nsIFrame* child = do_QueryFrame(SVGFrame); nsIContent* childContent = child->GetContent(); if (childContent->IsSVGElement()) { toChildsUserSpace = static_cast<const nsSVGElement*>(childContent)-> PrependLocalTransformsTo(mMatrixForChildren, eUserSpaceToParent); } // Our children have NS_STATE_SVG_CLIPPATH_CHILD set on them, and // nsSVGPathGeometryFrame::Render checks for that state bit and paints // only the geometry (opaque black) if set. SVGFrame->PaintSVG(*ctx, toChildsUserSpace); if (clipPathThatClipsChild) { if (childsClipPathRequiresMasking) { ctx->PopGroupAndBlend(); } ctx->Restore(); } } } if (clipPathThatClipsClipPath) { if (clippingOfClipPathRequiredMasking) { ctx->PopGroupAndBlend(); } ctx->Restore(); } } // Moz2D transforms in the opposite direction to Thebes mat.Invert(); if (aExtraMask) { // We could potentially due this more efficiently with OPERATOR_IN // but that operator does not work well on CG or D2D RefPtr<SourceSurface> currentMask = maskDT->Snapshot(); Matrix transform = maskDT->GetTransform(); maskDT->SetTransform(Matrix()); maskDT->ClearRect(Rect(0, 0, devSpaceClipExtents.width, devSpaceClipExtents.height)); maskDT->SetTransform(aExtraMasksTransform * transform); // draw currentMask with the inverse of the transform that we just so that // it ends up in the same spot with aExtraMask transformed by aExtraMasksTransform maskDT->MaskSurface(SurfacePattern(currentMask, ExtendMode::CLAMP, aExtraMasksTransform.Inverse() * ToMatrix(mat)), aExtraMask, Point(0, 0)); } *aMaskTransform = ToMatrix(mat); return maskDT->Snapshot(); }
already_AddRefed<SourceSurface> ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget) { MOZ_ASSERT(aTarget); if (!mData) { return nullptr; } if (!mSurface) { mSurface = mData->GetAsSourceSurface(); } if (!mSurface) { return nullptr; } RefPtr<DrawTarget> target = aTarget; IntRect surfRect(0, 0, mSurface->GetSize().width, mSurface->GetSize().height); // Check if we still need to crop our surface if (!mPictureRect.IsEqualEdges(surfRect)) { IntRect surfPortion = surfRect.Intersect(mPictureRect); // the crop lies entirely outside the surface area, nothing to draw if (surfPortion.IsEmpty()) { mSurface = nullptr; RefPtr<gfx::SourceSurface> surface(mSurface); return surface.forget(); } IntPoint dest(std::max(0, surfPortion.X() - mPictureRect.X()), std::max(0, surfPortion.Y() - mPictureRect.Y())); // We must initialize this target with mPictureRect.Size() because the // specification states that if the cropping area is given, then return an // ImageBitmap with the size equals to the cropping area. target = target->CreateSimilarDrawTarget(mPictureRect.Size(), target->GetFormat()); if (!target) { mSurface = nullptr; RefPtr<gfx::SourceSurface> surface(mSurface); return surface.forget(); } // We need to fall back to generic copying and cropping for the Windows8.1, // D2D1 backend. // In the Windows8.1 D2D1 backend, it might trigger "partial upload" from a // non-SourceSurfaceD2D1 surface to a D2D1Image in the following // CopySurface() step. However, the "partial upload" only supports uploading // a rectangle starts from the upper-left point, which means it cannot // upload an arbitrary part of the source surface and this causes problems // if the mPictureRect is not starts from the upper-left point. if (target->GetBackendType() == BackendType::DIRECT2D1_1 && mSurface->GetType() != SurfaceType::D2D1_1_IMAGE) { RefPtr<DataSourceSurface> dataSurface = mSurface->GetDataSurface(); if (NS_WARN_IF(!dataSurface)) { mSurface = nullptr; RefPtr<gfx::SourceSurface> surface(mSurface); return surface.forget(); } mSurface = CropAndCopyDataSourceSurface(dataSurface, mPictureRect); } else { target->CopySurface(mSurface, surfPortion, dest); mSurface = target->Snapshot(); } // Make mCropRect match new surface we've cropped to mPictureRect.MoveTo(0, 0); } // Replace our surface with one optimized for the target we're about to draw // to, under the assumption it'll likely be drawn again to that target. // This call should be a no-op for already-optimized surfaces mSurface = target->OptimizeSourceSurface(mSurface); RefPtr<gfx::SourceSurface> surface(mSurface); return surface.forget(); }
void LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion) { PROFILER_LABEL("LayerManagerComposite", "Render", js::ProfileEntry::Category::GRAPHICS); if (mDestroyed || !mCompositor || mCompositor->IsDestroyed()) { NS_WARNING("Call on destroyed layer manager"); return; } ClearLayerFlags(mRoot); // At this time, it doesn't really matter if these preferences change // during the execution of the function; we should be safe in all // permutations. However, may as well just get the values onces and // then use them, just in case the consistency becomes important in // the future. bool invertVal = gfxPrefs::LayersEffectInvert(); bool grayscaleVal = gfxPrefs::LayersEffectGrayscale(); float contrastVal = gfxPrefs::LayersEffectContrast(); bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0); // Set LayerScope begin/end frame LayerScopeAutoFrame frame(PR_Now()); // Dump to console if (gfxPrefs::LayersDump()) { this->Dump(/* aSorted= */true); } else if (profiler_feature_active("layersdump")) { std::stringstream ss; Dump(ss); profiler_log(ss.str().c_str()); } // Dump to LayerScope Viewer if (LayerScope::CheckSendable()) { // Create a LayersPacket, dump Layers into it and transfer the // packet('s ownership) to LayerScope. auto packet = MakeUnique<layerscope::Packet>(); layerscope::LayersPacket* layersPacket = packet->mutable_layers(); this->Dump(layersPacket); LayerScope::SendLayerDump(Move(packet)); } mozilla::widget::WidgetRenderingContext widgetContext; #if defined(XP_MACOSX) widgetContext.mLayerManager = this; #elif defined(MOZ_WIDGET_ANDROID) widgetContext.mCompositor = GetCompositor(); #endif { PROFILER_LABEL("LayerManagerComposite", "PreRender", js::ProfileEntry::Category::GRAPHICS); if (!mCompositor->GetWidget()->PreRender(&widgetContext)) { return; } } ParentLayerIntRect clipRect; IntRect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height); IntRect actualBounds; CompositorBench(mCompositor, bounds); MOZ_ASSERT(mRoot->GetOpacity() == 1); #if defined(MOZ_WIDGET_ANDROID) LayerMetricsWrapper wrapper = GetRootContentLayer(); if (wrapper) { mCompositor->SetClearColor(wrapper.Metadata().GetBackgroundColor()); } else { mCompositor->SetClearColorToDefault(); } #endif if (mRoot->GetClipRect()) { clipRect = *mRoot->GetClipRect(); IntRect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height); mCompositor->BeginFrame(aInvalidRegion, &rect, bounds, aOpaqueRegion, nullptr, &actualBounds); } else { gfx::IntRect rect; mCompositor->BeginFrame(aInvalidRegion, nullptr, bounds, aOpaqueRegion, &rect, &actualBounds); clipRect = ParentLayerIntRect(rect.x, rect.y, rect.width, rect.height); } if (actualBounds.IsEmpty()) { mCompositor->GetWidget()->PostRender(&widgetContext); return; } // Allow widget to render a custom background. mCompositor->GetWidget()->DrawWindowUnderlay( &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds)); RefPtr<CompositingRenderTarget> previousTarget; if (haveLayerEffects) { previousTarget = PushGroupForLayerEffects(); } else { mTwoPassTmpTarget = nullptr; } // Render our layers. RootLayer()->Prepare(ViewAs<RenderTargetPixel>(clipRect, PixelCastJustification::RenderTargetIsParentLayerForRoot)); RootLayer()->RenderLayer(clipRect.ToUnknownRect()); if (!mRegionToClear.IsEmpty()) { for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) { const IntRect& r = iter.Get(); mCompositor->ClearRect(Rect(r.x, r.y, r.width, r.height)); } } if (mTwoPassTmpTarget) { MOZ_ASSERT(haveLayerEffects); PopGroupForLayerEffects(previousTarget, clipRect.ToUnknownRect(), grayscaleVal, invertVal, contrastVal); } // Allow widget to render a custom foreground. mCompositor->GetWidget()->DrawWindowOverlay( &widgetContext, LayoutDeviceIntRect::FromUnknownRect(actualBounds)); // Debugging RenderDebugOverlay(actualBounds); { PROFILER_LABEL("LayerManagerComposite", "EndFrame", js::ProfileEntry::Category::GRAPHICS); mCompositor->EndFrame(); // Call after EndFrame() mCompositor->SetDispAcquireFence(mRoot); } mCompositor->GetWidget()->PostRender(&widgetContext); RecordFrame(); }
already_AddRefed<SourceSurface> ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget) { MOZ_ASSERT(aTarget); if (!mSurface) { mSurface = mData->GetAsSourceSurface(); } if (!mSurface) { return nullptr; } RefPtr<DrawTarget> target = aTarget; IntRect surfRect(0, 0, mSurface->GetSize().width, mSurface->GetSize().height); // Check if we still need to crop our surface if (!mPictureRect.IsEqualEdges(surfRect)) { IntRect surfPortion = surfRect.Intersect(mPictureRect); // the crop lies entirely outside the surface area, nothing to draw if (surfPortion.IsEmpty()) { mSurface = nullptr; RefPtr<gfx::SourceSurface> surface(mSurface); return surface.forget(); } IntPoint dest(std::max(0, surfPortion.X() - mPictureRect.X()), std::max(0, surfPortion.Y() - mPictureRect.Y())); // Do not initialize this target with mPictureRect.Size(). // In the Windows8 D2D1 backend, it might trigger "partial upload" from a // non-SourceSurfaceD2D1 surface to a D2D1Image in the following // CopySurface() step. However, the "partial upload" only supports uploading // a rectangle starts from the upper-left point, which means it cannot // upload an arbitrary part of the source surface and this causes problems // if the mPictureRect is not starts from the upper-left point. target = target->CreateSimilarDrawTarget(mSurface->GetSize(), target->GetFormat()); if (!target) { mSurface = nullptr; RefPtr<gfx::SourceSurface> surface(mSurface); return surface.forget(); } // Make mCropRect match new surface we've cropped to mPictureRect.MoveTo(0, 0); target->CopySurface(mSurface, surfPortion, dest); mSurface = target->Snapshot(); } // Replace our surface with one optimized for the target we're about to draw // to, under the assumption it'll likely be drawn again to that target. // This call should be a no-op for already-optimized surfaces mSurface = target->OptimizeSourceSurface(mSurface); RefPtr<gfx::SourceSurface> surface(mSurface); return surface.forget(); }
static void GenerateMaskSurface(const nsSVGIntegrationUtils::PaintFramesParams& aParams, float aOpacity, nsStyleContext* aSC, const nsTArray<nsSVGMaskFrame *>& aMaskFrames, const nsPoint& aOffsetToUserSpace, Matrix& aOutMaskTransform, RefPtr<SourceSurface>& aOutMaskSurface) { const nsStyleSVGReset *svgReset = aSC->StyleSVGReset(); MOZ_ASSERT(aMaskFrames.Length() > 0); gfxMatrix cssPxToDevPxMatrix = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aParams.frame); gfxContext& ctx = aParams.ctx; // There is only one SVG mask. if (((aMaskFrames.Length() == 1) && aMaskFrames[0])) { aOutMaskSurface = aMaskFrames[0]->GetMaskForMaskedFrame(&ctx, aParams.frame, cssPxToDevPxMatrix, aOpacity, &aOutMaskTransform, svgReset->mMask.mLayers[0].mMaskMode); return; } IntRect maskSurfaceRect = ComputeMaskGeometry(aParams, svgReset, aOffsetToUserSpace, aMaskFrames); if (maskSurfaceRect.IsEmpty()) { return; } // Mask composition result on CoreGraphic::A8 surface is not correct // when mask-mode is not add(source over). Switch to skia when CG backend // detected. RefPtr<DrawTarget> maskDT = (ctx.GetDrawTarget()->GetBackendType() == BackendType::COREGRAPHICS || ctx.GetDrawTarget()->GetBackendType() == BackendType::DIRECT2D1_1) ? Factory::CreateDrawTarget(BackendType::SKIA, maskSurfaceRect.Size(), SurfaceFormat::A8) : ctx.GetDrawTarget()->CreateSimilarDrawTarget(maskSurfaceRect.Size(), SurfaceFormat::A8); if (!maskDT || !maskDT->IsValid()) { return; } RefPtr<gfxContext> maskContext = gfxContext::CreateOrNull(maskDT); MOZ_ASSERT(maskContext); nsPresContext* presContext = aParams.frame->PresContext(); gfxPoint devPixelOffsetToUserSpace = nsLayoutUtils::PointToGfxPoint(aOffsetToUserSpace, presContext->AppUnitsPerDevPixel()); // Set ctx's matrix on maskContext, offset by the maskSurfaceRect's position. // This makes sure that we combine the masks in device space. gfxMatrix maskSurfaceMatrix = ctx.CurrentMatrix() * gfxMatrix::Translation(-maskSurfaceRect.TopLeft()); maskContext->SetMatrix(maskSurfaceMatrix); // Multiple SVG masks interleave with image mask. Paint each layer onto // maskDT one at a time. for (int i = aMaskFrames.Length() - 1; i >= 0 ; i--) { nsSVGMaskFrame *maskFrame = aMaskFrames[i]; CompositionOp compositionOp = (i == int(aMaskFrames.Length() - 1)) ? CompositionOp::OP_OVER : nsCSSRendering::GetGFXCompositeMode(svgReset->mMask.mLayers[i].mComposite); // maskFrame != nullptr means we get a SVG mask. // maskFrame == nullptr means we get an image mask. if (maskFrame) { Matrix svgMaskMatrix; RefPtr<SourceSurface> svgMask = maskFrame->GetMaskForMaskedFrame(maskContext, aParams.frame, cssPxToDevPxMatrix, aOpacity, &svgMaskMatrix, svgReset->mMask.mLayers[i].mMaskMode); if (svgMask) { gfxContextMatrixAutoSaveRestore matRestore(maskContext); maskContext->Multiply(ThebesMatrix(svgMaskMatrix)); Rect drawRect = IntRectToRect(IntRect(IntPoint(0, 0), svgMask->GetSize())); maskDT->DrawSurface(svgMask, drawRect, drawRect, DrawSurfaceOptions(), DrawOptions(1.0f, compositionOp)); } } else { gfxContextMatrixAutoSaveRestore matRestore(maskContext); maskContext->Multiply(gfxMatrix::Translation(-devPixelOffsetToUserSpace)); nsRenderingContext rc(maskContext); nsCSSRendering::PaintBGParams params = nsCSSRendering::PaintBGParams::ForSingleLayer(*presContext, rc, aParams.dirtyRect, aParams.borderArea, aParams.frame, aParams.builder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE, i, compositionOp); // FIXME We should use the return value, see bug 1258510. Unused << nsCSSRendering::PaintBackgroundWithSC(params, aSC, *aParams.frame->StyleBorder()); } } aOutMaskTransform = ToMatrix(maskSurfaceMatrix); if (!aOutMaskTransform.Invert()) { return; } aOutMaskSurface = maskDT->Snapshot(); }