CGContextRef gfxQuartzNativeDrawing::BeginNativeDrawing() { NS_ASSERTION(!mCGContext, "BeginNativeDrawing called when drawing already in progress"); DrawTarget *dt = mDrawTarget; if (dt->IsDualDrawTarget() || dt->IsTiledDrawTarget() || dt->GetBackendType() != BackendType::SKIA || dt->IsRecording()) { // We need a DrawTarget that we can get a CGContextRef from: Matrix transform = dt->GetTransform(); mNativeRect = transform.TransformBounds(mNativeRect); mNativeRect.RoundOut(); if (mNativeRect.IsEmpty()) { return nullptr; } mTempDrawTarget = Factory::CreateDrawTarget(BackendType::SKIA, IntSize::Truncate(mNativeRect.width, mNativeRect.height), SurfaceFormat::B8G8R8A8); if (!mTempDrawTarget) { return nullptr; } transform.PostTranslate(-mNativeRect.x, -mNativeRect.y); mTempDrawTarget->SetTransform(transform); dt = mTempDrawTarget; } else { // Clip the DT in case BorrowedCGContext needs to create a new layer. // This prevents it from creating a new layer the size of the window. // But make sure that this clip is device pixel aligned. Matrix transform = dt->GetTransform(); Rect deviceRect = transform.TransformBounds(mNativeRect); deviceRect.RoundOut(); mNativeRect = transform.Inverse().TransformBounds(deviceRect); mDrawTarget->PushClipRect(mNativeRect); } MOZ_ASSERT(dt->GetBackendType() == BackendType::SKIA); mCGContext = mBorrowedContext.Init(dt); if (NS_WARN_IF(!mCGContext)) { // Failed borrowing CG context, so we need to clean up. if (!mTempDrawTarget) { mDrawTarget->PopClip(); } return nullptr; } return mCGContext; }
void DrawBlur(gfxContext* aDestinationCtx, SourceSurface* aBlur, const IntPoint& aTopLeft, const Rect* aDirtyRect) { DrawTarget *dest = aDestinationCtx->GetDrawTarget(); nsRefPtr<gfxPattern> thebesPat = aDestinationCtx->GetPattern(); Pattern* pat = thebesPat->GetPattern(dest, nullptr); Matrix oldTransform = dest->GetTransform(); Matrix newTransform = oldTransform; newTransform.Translate(aTopLeft.x, aTopLeft.y); // Avoid a semi-expensive clip operation if we can, otherwise // clip to the dirty rect if (aDirtyRect) { dest->PushClipRect(*aDirtyRect); } dest->SetTransform(newTransform); dest->MaskSurface(*pat, aBlur, Point(0, 0)); dest->SetTransform(oldTransform); if (aDirtyRect) { dest->PopClip(); } }
bool SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2, const DrawTarget& aDrawTarget) { Matrix mat = aDrawTarget.GetTransform(); if (mat.HasNonTranslation()) { return false; } if (aP1.x != aP2.x && aP1.y != aP2.y) { return false; // not a horizontal or vertical line } Point p1 = aP1 + mat.GetTranslation(); // into device space Point p2 = aP2 + mat.GetTranslation(); p1.Round(); p2.Round(); p1 -= mat.GetTranslation(); // back into user space p2 -= mat.GetTranslation(); if (aP1.x == aP2.x) { // snap vertical line, adding 0.5 to align it to be mid-pixel: aP1 = p1 + Point(0.5, 0); aP2 = p2 + Point(0.5, 0); } else { // snap horizontal line, adding 0.5 to align it to be mid-pixel: aP1 = p1 + Point(0, 0.5); aP2 = p2 + Point(0, 0.5); } return true; }
static void RepeatOrStretchSurface(DrawTarget& aDT, SourceSurface* aSurface, const Rect& aDest, const Rect& aSrc, Rect& aSkipRect) { if (aSkipRect.Contains(aDest)) { return; } if ((!aDT.GetTransform().IsRectilinear() && aDT.GetBackendType() != BackendType::CAIRO) || (aDT.GetBackendType() == BackendType::DIRECT2D)) { // Use stretching if possible, since it leads to less seams when the // destination is transformed. However, don't do this if we're using cairo, // because if cairo is using pixman it won't render anything for large // stretch factors because pixman's internal fixed point precision is not // high enough to handle those scale factors. // Calling FillRect on a D2D backend with a repeating pattern is much slower // than DrawSurface, so special case the D2D backend here. aDT.DrawSurface(aSurface, aDest, aSrc); return; } SurfacePattern pattern(aSurface, ExtendMode::REPEAT, Matrix::Translation(aDest.TopLeft() - aSrc.TopLeft()), Filter::GOOD, RoundedToInt(aSrc)); aDT.FillRect(aDest, pattern); }
void gfxQuartzNativeDrawing::EndNativeDrawing() { NS_ASSERTION(mCGContext, "EndNativeDrawing called without BeginNativeDrawing"); MOZ_ASSERT(!mContext->IsCairo(), "BeginNativeDrawing succeeded with cairo context?"); mBorrowedContext.Finish(); if (mDrawTarget) { DrawTarget *dest = mContext->GetDrawTarget(); RefPtr<SourceSurface> source = mDrawTarget->Snapshot(); IntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale), NSToIntFloor(mNativeRect.height * mBackingScale)); Matrix oldTransform = dest->GetTransform(); Matrix newTransform = oldTransform; newTransform.Translate(mNativeRect.x, mNativeRect.y); newTransform.Scale(1.0f / mBackingScale, 1.0f / mBackingScale); dest->SetTransform(newTransform); dest->DrawSurface(source, gfx::Rect(0, 0, backingSize.width, backingSize.height), gfx::Rect(0, 0, backingSize.width, backingSize.height)); dest->SetTransform(oldTransform); } }
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; }
void gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx) { if (!mContext) return; mBlur->Blur(mData); mozilla::gfx::Rect* dirtyRect = mBlur->GetDirtyRect(); DrawTarget *dest = aDestinationCtx->GetDrawTarget(); if (!dest) { NS_ERROR("Blurring not supported for Thebes contexts!"); return; } mozilla::RefPtr<SourceSurface> mask = dest->CreateSourceSurfaceFromData(mData, mBlur->GetSize(), mBlur->GetStride(), FORMAT_A8); if (!mask) { NS_ERROR("Failed to create mask!"); return; } nsRefPtr<gfxPattern> thebesPat = aDestinationCtx->GetPattern(); Pattern* pat = thebesPat->GetPattern(dest, nullptr); Matrix oldTransform = dest->GetTransform(); Matrix newTransform = oldTransform; newTransform.Translate(mBlur->GetRect().x, mBlur->GetRect().y); // Avoid a semi-expensive clip operation if we can, otherwise // clip to the dirty rect if (dirtyRect) { dest->PushClipRect(*dirtyRect); } dest->SetTransform(newTransform); dest->MaskSurface(*pat, mask, Point(0, 0)); dest->SetTransform(oldTransform); if (dirtyRect) { dest->PopClip(); } }
void gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx) { if (!mAccelerated && !mData) { return; } DrawTarget *dest = aDestinationCtx->GetDrawTarget(); if (!dest) { NS_WARNING("Blurring not supported for Thebes contexts!"); return; } RefPtr<gfxPattern> thebesPat = aDestinationCtx->GetPattern(); Pattern* pat = thebesPat->GetPattern(dest, nullptr); if (!pat) { NS_WARNING("Failed to get pattern for blur!"); return; } IntPoint topLeft; RefPtr<SourceSurface> mask = DoBlur(nullptr, &topLeft); if (!mask) { NS_ERROR("Failed to create mask!"); return; } // Avoid a semi-expensive clip operation if we can, otherwise // clip to the dirty rect Rect* dirtyRect = mBlur.GetDirtyRect(); if (dirtyRect) { dest->PushClipRect(*dirtyRect); } Matrix oldTransform = dest->GetTransform(); Matrix newTransform = oldTransform; newTransform.PreTranslate(topLeft); dest->SetTransform(newTransform); dest->MaskSurface(*pat, mask, Point(0, 0)); dest->SetTransform(oldTransform); if (dirtyRect) { dest->PopClip(); } }
void ClientLayerManager::MakeSnapshotIfRequired() { if (!mShadowTarget) { return; } if (mWidget) { if (CompositorChild* 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 nsIntRect outerBounds; mWidget->GetBounds(outerBounds); nsIntRect bounds = ToOutsideIntRect(mShadowTarget->GetClipExtents()); if (mTargetRotation) { bounds = RotateRect(bounds, outerBounds, mTargetRotation); } SurfaceDescriptor inSnapshot; if (!bounds.IsEmpty() && mForwarder->AllocSurfaceDescriptor(bounds.Size().ToIntSize(), gfxContentType::COLOR_ALPHA, &inSnapshot) && remoteRenderer->SendMakeSnapshot(inSnapshot, bounds)) { RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(inSnapshot); 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, mTargetRotation); gfx::Matrix oldMatrix = dt->GetTransform(); dt->SetTransform(oldMatrix * rotate); dt->DrawSurface(surf, dstRect, srcRect, DrawSurfaceOptions(), DrawOptions(1.0f, CompositionOp::OP_OVER)); dt->SetTransform(oldMatrix); } mForwarder->DestroySharedSurface(&inSnapshot); } } mShadowTarget = nullptr; }
void gfxQuartzNativeDrawing::EndNativeDrawing() { NS_ASSERTION(mCGContext, "EndNativeDrawing called without BeginNativeDrawing"); if (mBorrowedContext.cg) { MOZ_ASSERT(!mContext->IsCairo()); mBorrowedContext.Finish(); if (mDrawTarget) { DrawTarget *dest = mContext->GetDrawTarget(); RefPtr<SourceSurface> source = mDrawTarget->Snapshot(); IntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale), NSToIntFloor(mNativeRect.height * mBackingScale)); Matrix oldTransform = dest->GetTransform(); Matrix newTransform = oldTransform; newTransform.Translate(mNativeRect.x, mNativeRect.y); newTransform.Scale(1.0f / mBackingScale, 1.0f / mBackingScale); dest->SetTransform(newTransform); dest->DrawSurface(source, gfx::Rect(0, 0, backingSize.width, backingSize.height), gfx::Rect(0, 0, backingSize.width, backingSize.height)); dest->SetTransform(oldTransform); } return; } cairo_quartz_finish_cg_context_with_clip(mSurfaceContext->GetCairo()); mQuartzSurface->MarkDirty(); if (mSurfaceContext != mContext) { gfxContextMatrixAutoSaveRestore save(mContext); // Copy back to destination mContext->Translate(mNativeRect.TopLeft()); mContext->Scale(1.0f / mBackingScale, 1.0f / mBackingScale); mContext->DrawSurface(mQuartzSurface, mQuartzSurface->GetSize()); } }
void gfxWindowsNativeDrawing::PaintToContext() { if (mRenderState == RENDER_STATE_NATIVE_DRAWING_DONE) { // nothing to do, it already went to the context mRenderState = RENDER_STATE_DONE; } else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE) { RefPtr<gfxImageSurface> black = mBlackSurface->GetAsImageSurface(); RefPtr<gfxImageSurface> white = mWhiteSurface->GetAsImageSurface(); if (!gfxAlphaRecovery::RecoverAlpha(black, white)) { NS_ERROR("Alpha recovery failure"); return; } RefPtr<DataSourceSurface> source = Factory::CreateWrappingDataSourceSurface(black->Data(), black->Stride(), black->GetSize(), SurfaceFormat::B8G8R8A8); { DrawTarget* dt = mContext->GetDrawTarget(); AutoRestoreTransform autoRestoreTransform(dt); Matrix newTransform = dt->GetTransform(); newTransform.PreTranslate(ToPoint(mNativeRect.TopLeft())); dt->SetTransform(newTransform); Rect rect(Point(0.0, 0.0), ToSize(mNativeRect.Size())); Matrix m = Matrix::Scaling(1.0 / mScale.width, 1.0 / mScale.height); Filter filter = (mNativeDrawFlags & DO_NEAREST_NEIGHBOR_FILTERING) ? Filter::LINEAR : Filter::GOOD; SurfacePattern pat(source, ExtendMode::CLAMP, m, filter); dt->FillRect(rect, pat); } mRenderState = RENDER_STATE_DONE; } else { NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::PaintToContext"); } }
void nsTableCellFrame::DecorateForSelection(nsRenderingContext& aRenderingContext, nsPoint aPt) { NS_ASSERTION(IsSelected(), "Should only be called for selected cells"); int16_t displaySelection; nsPresContext* presContext = PresContext(); displaySelection = DisplaySelection(presContext); if (displaySelection) { RefPtr<nsFrameSelection> frameSelection = presContext->PresShell()->FrameSelection(); if (frameSelection->GetTableCellSelection()) { nscolor bordercolor; if (displaySelection == nsISelectionController::SELECTION_DISABLED) { bordercolor = NS_RGB(176,176,176);// disabled color } else { bordercolor = LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground); } nscoord threePx = nsPresContext::CSSPixelsToAppUnits(3); if ((mRect.width > threePx) && (mRect.height > threePx)) { //compare bordercolor to ((nsStyleColor *)myColor)->mBackgroundColor) bordercolor = EnsureDifferentColors(bordercolor, StyleBackground()->mBackgroundColor); int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel(); Point devPixelOffset = NSPointToPoint(aPt, appUnitsPerDevPixel); DrawTarget* drawTarget = aRenderingContext.GetDrawTarget(); AutoRestoreTransform autoRestoreTransform(drawTarget); drawTarget->SetTransform( drawTarget->GetTransform().PreTranslate(devPixelOffset)); ColorPattern color(ToDeviceColor(bordercolor)); nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); StrokeLineWithSnapping(nsPoint(onePixel, 0), nsPoint(mRect.width, 0), appUnitsPerDevPixel, *drawTarget, color); StrokeLineWithSnapping(nsPoint(0, onePixel), nsPoint(0, mRect.height), appUnitsPerDevPixel, *drawTarget, color); StrokeLineWithSnapping(nsPoint(onePixel, mRect.height), nsPoint(mRect.width, mRect.height), appUnitsPerDevPixel, *drawTarget, color); StrokeLineWithSnapping(nsPoint(mRect.width, onePixel), nsPoint(mRect.width, mRect.height), appUnitsPerDevPixel, *drawTarget, color); //middle nsRect r(onePixel, onePixel, mRect.width - onePixel, mRect.height - onePixel); Rect devPixelRect = NSRectToSnappedRect(r, appUnitsPerDevPixel, *drawTarget); drawTarget->StrokeRect(devPixelRect, color); //shading StrokeLineWithSnapping(nsPoint(2*onePixel, mRect.height-2*onePixel), nsPoint(mRect.width-onePixel, mRect.height- (2*onePixel)), appUnitsPerDevPixel, *drawTarget, color); StrokeLineWithSnapping(nsPoint(mRect.width - (2*onePixel), 2*onePixel), nsPoint(mRect.width - (2*onePixel), mRect.height-onePixel), appUnitsPerDevPixel, *drawTarget, color); } } } }
void gfxXlibNativeRenderer::Draw(gfxContext* ctx, nsIntSize size, uint32_t flags, Screen *screen, Visual *visual) { gfxMatrix matrix = ctx->CurrentMatrix(); // We can only draw direct or onto a copied background if pixels align and // native drawing is compatible with the current operator. (The matrix is // actually also pixel-exact for flips and right-angle rotations, which // would permit copying the background but not drawing direct.) bool matrixIsIntegerTranslation = !matrix.HasNonIntegerTranslation(); bool canDrawOverBackground = matrixIsIntegerTranslation && ctx->CurrentOperator() == gfxContext::OPERATOR_OVER; // The padding of 0.5 for non-pixel-exact transformations used here is // the same as what _cairo_pattern_analyze_filter uses. const gfxFloat filterRadius = 0.5; gfxRect affectedRect(0.0, 0.0, size.width, size.height); if (!matrixIsIntegerTranslation) { // The filter footprint means that the affected rectangle is a // little larger than the drawingRect; affectedRect.Inflate(filterRadius); NATIVE_DRAWING_NOTE("FALLBACK: matrix not integer translation"); } else if (!canDrawOverBackground) { NATIVE_DRAWING_NOTE("FALLBACK: unsupported operator"); } // Clipping to the region affected by drawing allows us to consider only // the portions of the clip region that will be affected by drawing. gfxRect clipExtents; { gfxContextAutoSaveRestore autoSR(ctx); ctx->Clip(affectedRect); clipExtents = ctx->GetClipExtents(); if (clipExtents.IsEmpty()) return; // nothing to do if (canDrawOverBackground && DrawDirect(ctx, size, flags, screen, visual)) return; } IntRect drawingRect(IntPoint(0, 0), size); // Drawing need only be performed within the clip extents // (and padding for the filter). if (!matrixIsIntegerTranslation) { // The source surface may need to be a little larger than the clip // extents due to the filter footprint. clipExtents.Inflate(filterRadius); } clipExtents.RoundOut(); IntRect intExtents(int32_t(clipExtents.X()), int32_t(clipExtents.Y()), int32_t(clipExtents.Width()), int32_t(clipExtents.Height())); drawingRect.IntersectRect(drawingRect, intExtents); gfxPoint offset(drawingRect.x, drawingRect.y); DrawingMethod method; DrawTarget* drawTarget = ctx->GetDrawTarget(); Matrix dtTransform = drawTarget->GetTransform(); gfxPoint deviceTranslation = gfxPoint(dtTransform._31, dtTransform._32); cairo_surface_t* cairoTarget = static_cast<cairo_surface_t*> (drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE)); cairo_surface_t* tempXlibSurface = CreateTempXlibSurface(cairoTarget, drawTarget, size, canDrawOverBackground, flags, screen, visual, &method); if (!tempXlibSurface) return; bool drawIsOpaque = (flags & DRAW_IS_OPAQUE) != 0; if (!drawIsOpaque) { cairo_t* tmpCtx = cairo_create(tempXlibSurface); if (method == eCopyBackground) { NS_ASSERTION(cairoTarget, "eCopyBackground only used when there's a cairoTarget"); cairo_set_operator(tmpCtx, CAIRO_OPERATOR_SOURCE); gfxPoint pt = -(offset + deviceTranslation); cairo_set_source_surface(tmpCtx, cairoTarget, pt.x, pt.y); // The copy from the tempXlibSurface to the target context should // use operator SOURCE, but that would need a mask to bound the // operation. Here we only copy opaque backgrounds so operator // OVER will behave like SOURCE masked by the surface. NS_ASSERTION(cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR, "Don't copy background with a transparent surface"); } else { cairo_set_operator(tmpCtx, CAIRO_OPERATOR_CLEAR); } cairo_paint(tmpCtx); cairo_destroy(tmpCtx); } if (!DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft())) { cairo_surface_destroy(tempXlibSurface); return; } SurfaceFormat moz2DFormat = cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8; if (method != eAlphaExtraction) { if (drawTarget) { NativeSurface native; native.mFormat = moz2DFormat; native.mType = NativeSurfaceType::CAIRO_SURFACE; native.mSurface = tempXlibSurface; native.mSize = size; RefPtr<SourceSurface> sourceSurface = drawTarget->CreateSourceSurfaceFromNativeSurface(native); if (sourceSurface) { drawTarget->DrawSurface(sourceSurface, Rect(offset.x, offset.y, size.width, size.height), Rect(0, 0, size.width, size.height)); } } else { nsRefPtr<gfxASurface> tmpSurf = gfxASurface::Wrap(tempXlibSurface); ctx->SetSource(tmpSurf, offset); ctx->Paint(); } cairo_surface_destroy(tempXlibSurface); return; } nsRefPtr<gfxImageSurface> blackImage = CopyXlibSurfaceToImage(tempXlibSurface, size, gfxImageFormat::ARGB32); cairo_t* tmpCtx = cairo_create(tempXlibSurface); cairo_set_source_rgba(tmpCtx, 1.0, 1.0, 1.0, 1.0); cairo_set_operator(tmpCtx, CAIRO_OPERATOR_SOURCE); cairo_paint(tmpCtx); cairo_destroy(tmpCtx); DrawOntoTempSurface(tempXlibSurface, -drawingRect.TopLeft()); nsRefPtr<gfxImageSurface> whiteImage = CopyXlibSurfaceToImage(tempXlibSurface, size, gfxImageFormat::RGB24); if (blackImage->CairoStatus() == CAIRO_STATUS_SUCCESS && whiteImage->CairoStatus() == CAIRO_STATUS_SUCCESS) { if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage)) { cairo_surface_destroy(tempXlibSurface); return; } gfxASurface* paintSurface = blackImage; if (drawTarget) { NativeSurface native; native.mFormat = moz2DFormat; native.mType = NativeSurfaceType::CAIRO_SURFACE; native.mSurface = paintSurface->CairoSurface(); native.mSize = size; RefPtr<SourceSurface> sourceSurface = drawTarget->CreateSourceSurfaceFromNativeSurface(native); if (sourceSurface) { drawTarget->DrawSurface(sourceSurface, Rect(offset.x, offset.y, size.width, size.height), Rect(0, 0, size.width, size.height)); } } else { ctx->SetSource(paintSurface, offset); ctx->Paint(); } } cairo_surface_destroy(tempXlibSurface); }