NS_IMETHODIMP nsSimplePageSequenceFrame::PrePrintNextPage(nsITimerCallback* aCallback, bool* aDone) { nsIFrame* currentPage = GetCurrentPageFrame(); if (!currentPage) { *aDone = true; return NS_ERROR_FAILURE; } DetermineWhetherToPrintPage(); // Nothing to do if the current page doesn't get printed OR rendering to // preview. For preview, the `CallPrintCallback` is called from within the // HTMLCanvasElement::HandlePrintCallback. if (!mPrintThisPage || !PresContext()->IsRootPaginatedDocument()) { *aDone = true; return NS_OK; } // If the canvasList is null, then generate it and start the render // process for all the canvas. if (!mCurrentCanvasListSetup) { mCurrentCanvasListSetup = true; GetPrintCanvasElementsInFrame(currentPage, &mCurrentCanvasList); if (mCurrentCanvasList.Length() != 0) { nsresult rv = NS_OK; // Begin printing of the document nsDeviceContext *dc = PresContext()->DeviceContext(); PR_PL(("\n")); PR_PL(("***************** BeginPage *****************\n")); rv = dc->BeginPage(); NS_ENSURE_SUCCESS(rv, rv); mCalledBeginPage = true; RefPtr<gfxContext> renderingContext = dc->CreateRenderingContext(); NS_ENSURE_TRUE(renderingContext, NS_ERROR_OUT_OF_MEMORY); DrawTarget* drawTarget = renderingContext->GetDrawTarget(); if (NS_WARN_IF(!drawTarget)) { return NS_ERROR_FAILURE; } for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) { HTMLCanvasElement* canvas = mCurrentCanvasList[i]; nsIntSize size = canvas->GetSize(); RefPtr<DrawTarget> canvasTarget = drawTarget->CreateSimilarDrawTarget(size, drawTarget->GetFormat()); if (!canvasTarget) { continue; } nsICanvasRenderingContextInternal* ctx = canvas->GetContextAtIndex(0); if (!ctx) { continue; } // Initialize the context with the new DrawTarget. ctx->InitializeWithDrawTarget(nullptr, WrapNotNull(canvasTarget)); // Start the rendering process. AutoWeakFrame weakFrame = this; canvas->DispatchPrintCallback(aCallback); NS_ENSURE_STATE(weakFrame.IsAlive()); } } } uint32_t doneCounter = 0; for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) { HTMLCanvasElement* canvas = mCurrentCanvasList[i]; if (canvas->IsPrintCallbackDone()) { doneCounter++; } } // If all canvas have finished rendering, return true, otherwise false. *aDone = doneCounter == mCurrentCanvasList.Length(); return NS_OK; }
DrawResult nsSVGClipPathFrame::PaintClipMask(gfxContext& aMaskContext, nsIFrame* aClippedFrame, const gfxMatrix& aMatrix, Matrix* aMaskTransform, SourceSurface* aExtraMask, const Matrix& aExtraMasksTransform) { // 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 DrawResult::SUCCESS; // 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 DrawResult::SUCCESS; // Reference loop! } DrawResult result = DrawResult::SUCCESS; DrawTarget* maskDT = aMaskContext.GetDrawTarget(); MOZ_ASSERT(maskDT->GetFormat() == SurfaceFormat::A8); // Paint this clipPath's contents into aMaskDT: // 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(); nsSVGUtils::MaskUsage maskUsage; nsSVGUtils::DetermineMaskUsage(this, true, maskUsage); if (maskUsage.shouldApplyClipPath) { clipPathThatClipsClipPath->ApplyClipPath(aMaskContext, aClippedFrame, aMatrix); } else if (maskUsage.shouldGenerateClipMaskLayer) { Matrix maskTransform; RefPtr<SourceSurface> maskSurface; Tie(result, maskSurface) = clipPathThatClipsClipPath->GetClipMask(aMaskContext, aClippedFrame, aMatrix, &maskTransform); aMaskContext.PushGroupForBlendBack(gfxContentType::ALPHA, 1.0, maskSurface, maskTransform); // The corresponding PopGroupAndBlend call below will mask the // blend using |maskSurface|. } // Paint our children into the mask: for (nsIFrame* kid = mFrames.FirstChild(); kid; kid = kid->GetNextSibling()) { result &= PaintFrameIntoMask(kid, aClippedFrame, aMaskContext, aMatrix); } if (maskUsage.shouldGenerateClipMaskLayer) { aMaskContext.PopGroupAndBlend(); } else if (maskUsage.shouldApplyClipPath) { aMaskContext.PopClip(); } // Moz2D transforms in the opposite direction to Thebes gfxMatrix maskTransfrom = aMaskContext.CurrentMatrix(); maskTransfrom.Invert(); if (aExtraMask) { ComposeExtraMask(maskDT, maskTransfrom, aExtraMask, aExtraMasksTransform); } *aMaskTransform = ToMatrix(maskTransfrom); return result; }
/*** * If we can, let's paint this ClientPaintedLayer's contents off the main thread. * The essential idea is that we ask the ContentClient for a DrawTarget and record * the moz2d commands. On the Paint Thread, we replay those commands to the * destination draw target. There are a couple of lifetime issues here though: * * 1) TextureClient owns the underlying buffer and DrawTarget. Because of this * we have to keep the TextureClient and DrawTarget alive but trick the * TextureClient into thinking it's already returned the DrawTarget * since we iterate through different Rects to get DrawTargets*. If * the TextureClient goes away, the DrawTarget and thus buffer can too. * 2) When ContentClient::EndPaint happens, it flushes the DrawTarget. We have * to Reflush on the Paint Thread * 3) DrawTarget API is NOT thread safe. We get around this by recording * on the main thread and painting on the paint thread. Logically, * ClientLayerManager will force a flushed paint and block the main thread * if we have another transaction. Thus we have a gap between when the main * thread records, the paint thread paints, and we block the main thread * from trying to paint again. The underlying API however is NOT thread safe. * 4) We have both "sync" and "async" OMTP. Sync OMTP means we paint on the main thread * but block the main thread while the paint thread paints. Async OMTP doesn't block * the main thread. Sync OMTP is only meant to be used as a debugging tool. */ bool ClientPaintedLayer::PaintOffMainThread() { mContentClient->BeginAsyncPaint(); uint32_t flags = GetPaintFlags(); PaintState state = mContentClient->BeginPaintBuffer(this, flags); if (!UpdatePaintRegion(state)) { return false; } bool didUpdate = false; RotatedContentBuffer::DrawIterator iter; // Debug Protip: Change to BorrowDrawTargetForPainting if using sync OMTP. while (RefPtr<CapturedPaintState> captureState = mContentClient->BorrowDrawTargetForRecording(state, &iter)) { DrawTarget* target = captureState->mTarget; if (!target || !target->IsValid()) { if (target) { mContentClient->ReturnDrawTargetToBuffer(target); } continue; } RefPtr<DrawTargetCapture> captureDT = Factory::CreateCaptureDrawTarget(target->GetBackendType(), target->GetSize(), target->GetFormat()); captureDT->SetTransform(captureState->mTargetTransform); SetAntialiasingFlags(this, captureDT); RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(captureDT); MOZ_ASSERT(ctx); // already checked the target above ClientManager()->GetPaintedLayerCallback()(this, ctx, iter.mDrawRegion, iter.mDrawRegion, state.mClip, state.mRegionToInvalidate, ClientManager()->GetPaintedLayerCallbackData()); ctx = nullptr; captureState->mCapture = captureDT.forget(); PaintThread::Get()->PaintContents(captureState, RotatedContentBuffer::PrepareDrawTargetForPainting); mContentClient->ReturnDrawTargetToBuffer(target); didUpdate = true; } mContentClient->EndPaint(nullptr); if (didUpdate) { UpdateContentClient(state); } return true; }