already_AddRefed<DrawTarget> nsShmImage::CreateDrawTarget(const mozilla::LayoutDeviceIntRegion& aRegion) { // Wait for any in-flight requests to complete. // Typically X clients would wait for a XShmCompletionEvent to be received, // but this works as it's sent immediately after the request is processed. xcb_generic_error_t* error; if (mLastRequest.sequence != XCB_NONE && (error = xcb_request_check(mConnection, mLastRequest))) { gShmAvailable = false; free(error); return nullptr; } // Due to bug 1205045, we must avoid making GTK calls off the main thread to query window size. // Instead we just track the largest offset within the image we are drawing to and grow the image // to accomodate it. Since usually the entire window is invalidated on the first paint to it, // this should grow the image to the necessary size quickly without many intermediate reallocations. IntRect bounds = aRegion.GetBounds().ToUnknownRect(); IntSize size(bounds.XMost(), bounds.YMost()); if (size.width > mSize.width || size.height > mSize.height) { DestroyImage(); if (!CreateImage(size)) { return nullptr; } } return gfxPlatform::GetPlatform()->CreateDrawTargetForData( reinterpret_cast<unsigned char*>(mShmAddr) + BytesPerPixel(mFormat) * (bounds.y * mSize.width + bounds.x), bounds.Size(), BytesPerPixel(mFormat) * mSize.width, mFormat); }
gfx::DrawTarget* TiledTextureImage::BeginUpdate(nsIntRegion& aRegion) { NS_ASSERTION(!mInUpdate, "nested update"); mInUpdate = true; // Note, we don't call GetUpdateRegion here as if the updated region is // fully contained in a single tile, we get to avoid iterating through // the tiles again (and a little copying). if (mTextureState != Valid) { // if the texture hasn't been initialized yet, or something important // changed, we need to recreate our backing surface and force the // client to paint everything aRegion = IntRect(IntPoint(0, 0), mSize); } IntRect bounds = aRegion.GetBounds(); for (unsigned i = 0; i < mImages.Length(); i++) { int xPos = (i % mColumns) * mTileSize; int yPos = (i / mColumns) * mTileSize; nsIntRegion imageRegion = nsIntRegion(IntRect(IntPoint(xPos,yPos), mImages[i]->GetSize())); // a single Image can handle this update request if (imageRegion.Contains(aRegion)) { // adjust for tile offset aRegion.MoveBy(-xPos, -yPos); // forward the actual call RefPtr<gfx::DrawTarget> drawTarget = mImages[i]->BeginUpdate(aRegion); // caller expects container space aRegion.MoveBy(xPos, yPos); // we don't have a temp surface mUpdateDrawTarget = nullptr; // remember which image to EndUpdate mCurrentImage = i; return drawTarget.get(); } } // Get the real updated region, taking into account the capabilities of // each TextureImage tile GetUpdateRegion(aRegion); mUpdateRegion = aRegion; bounds = aRegion.GetBounds(); // update covers multiple Images - create a temp surface to paint in gfx::SurfaceFormat format = (GetContentType() == gfxContentType::COLOR) ? gfx::SurfaceFormat::B8G8R8X8: gfx::SurfaceFormat::B8G8R8A8; mUpdateDrawTarget = gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO, bounds.Size(), format); return mUpdateDrawTarget;; }
IntSize DeprecatedTextureHostD3D9::GetSize() const { if (mIterating) { IntRect rect = GetTileRect(mCurrentTile); return rect.Size(); } return TextureSourceD3D9::GetSize(); }
TemporaryRef<CompositingRenderTarget> BasicCompositor::CreateRenderTarget(const IntRect& aRect, SurfaceInitMode aInit) { RefPtr<DrawTarget> target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8); RefPtr<BasicCompositingRenderTarget> rt = new BasicCompositingRenderTarget(target, aRect); return rt.forget(); }
/* static */ already_AddRefed<VideoData> VideoData::Create(const VideoInfo& aInfo, ImageContainer* aContainer, int64_t aOffset, int64_t aTime, int64_t aDuration, mozilla::layers::TextureClient* aBuffer, bool aKeyframe, int64_t aTimecode, const IntRect& aPicture) { if (!aContainer) { // Create a dummy VideoData with no image. This gives us something to // send to media streams if necessary. RefPtr<VideoData> v(new VideoData(aOffset, aTime, aDuration, aKeyframe, aTimecode, aInfo.mDisplay, 0)); return v.forget(); } // The following situations could be triggered by invalid input if (aPicture.width <= 0 || aPicture.height <= 0) { NS_WARNING("Empty picture rect"); return nullptr; } // Ensure the picture size specified in the headers can be extracted out of // the frame we've been supplied without indexing out of bounds. CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width); CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height); if (!xLimit.isValid() || !yLimit.isValid()) { // The specified picture dimensions can't be contained inside the video // frame, we'll stomp memory if we try to copy it. Fail. NS_WARNING("Overflowing picture rect"); return nullptr; } RefPtr<VideoData> v(new VideoData(aOffset, aTime, aDuration, aKeyframe, aTimecode, aInfo.mDisplay, 0)); RefPtr<layers::GrallocImage> image = new layers::GrallocImage(); image->AdoptData(aBuffer, aPicture.Size()); v->mImage = image; return v.forget(); }
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 DrawTargetTiled::CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect, const IntPoint &aDestination) { for (size_t i = 0; i < mTiles.size(); i++) { IntPoint tileOrigin = mTiles[i].mTileOrigin; IntSize tileSize = mTiles[i].mDrawTarget->GetSize(); if (!IntRect(aDestination, aSourceRect.Size()).Intersects(IntRect(tileOrigin, tileSize))) { continue; } // CopySurface ignores the transform, account for that here. mTiles[i].mDrawTarget->CopySurface(aSurface, aSourceRect, aDestination - tileOrigin); } }
/** * 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; } } }
already_AddRefed<CompositingRenderTarget> BasicCompositor::CreateRenderTarget(const IntRect& aRect, SurfaceInitMode aInit) { MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size"); if (aRect.width * aRect.height == 0) { return nullptr; } RefPtr<DrawTarget> target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8); if (!target) { return nullptr; } RefPtr<BasicCompositingRenderTarget> rt = new BasicCompositingRenderTarget(target, aRect); return rt.forget(); }
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(); }
/* static */ bool VideoData::SetVideoDataToImage(PlanarYCbCrImage* aVideoImage, const VideoInfo& aInfo, const YCbCrBuffer &aBuffer, const IntRect& aPicture, bool aCopyData) { if (!aVideoImage) { return false; } const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0]; const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1]; const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2]; PlanarYCbCrData data; data.mYChannel = Y.mData + Y.mOffset; data.mYSize = IntSize(Y.mWidth, Y.mHeight); data.mYStride = Y.mStride; data.mYSkip = Y.mSkip; data.mCbChannel = Cb.mData + Cb.mOffset; data.mCrChannel = Cr.mData + Cr.mOffset; data.mCbCrSize = IntSize(Cb.mWidth, Cb.mHeight); data.mCbCrStride = Cb.mStride; data.mCbSkip = Cb.mSkip; data.mCrSkip = Cr.mSkip; data.mPicX = aPicture.x; data.mPicY = aPicture.y; data.mPicSize = aPicture.Size(); data.mStereoMode = aInfo.mStereoMode; aVideoImage->SetDelayedConversion(true); if (aCopyData) { return aVideoImage->SetData(data); } else { return aVideoImage->SetDataNoCopy(data); } }
/* static */ void gfxAlphaBoxBlur::BlurRectangle(gfxContext* aDestinationCtx, const gfxRect& aRect, RectCornerRadii* aCornerRadii, const gfxPoint& aBlurStdDev, const gfxRGBA& aShadowColor, const gfxRect& aDirtyRect, const gfxRect& aSkipRect) { DrawTarget& destDrawTarget = *aDestinationCtx->GetDrawTarget(); IntSize blurRadius = CalculateBlurRadius(aBlurStdDev); IntRect rect = RoundedToInt(ToRect(aRect)); IntMargin extendDestBy; IntMargin slice; RefPtr<SourceSurface> boxShadow = GetBlur(destDrawTarget, rect.Size(), blurRadius, aCornerRadii, aShadowColor, extendDestBy, slice); if (!boxShadow) { return; } destDrawTarget.PushClipRect(ToRect(aDirtyRect)); // Copy the right parts from boxShadow into destDrawTarget. The middle parts // will be stretched, border-image style. Rect srcOuter(Point(), Size(boxShadow->GetSize())); Rect srcInner = srcOuter; srcInner.Deflate(Margin(slice)); rect.Inflate(extendDestBy); Rect dstOuter(rect); Rect dstInner(rect); dstInner.Deflate(Margin(slice)); Rect skipRect = ToRect(aSkipRect); if (srcInner.IsEqualInterior(srcOuter)) { MOZ_ASSERT(dstInner.IsEqualInterior(dstOuter)); // The target rect is smaller than the minimal size so just draw the surface destDrawTarget.DrawSurface(boxShadow, dstInner, srcInner); } else { DrawBoxShadows(destDrawTarget, boxShadow, dstOuter, dstInner, srcOuter, srcInner, skipRect); // Middle part RepeatOrStretchSurface(destDrawTarget, boxShadow, RectWithEdgesTRBL(dstInner.Y(), dstInner.XMost(), dstInner.YMost(), dstInner.X()), RectWithEdgesTRBL(srcInner.Y(), srcInner.XMost(), srcInner.YMost(), srcInner.X()), skipRect); } // A note about anti-aliasing and seems between adjacent parts: // We don't explicitly disable anti-aliasing in the DrawSurface calls above, // so if there's a transform on destDrawTarget that is not pixel-aligned, // there will be seams between adjacent parts of the box-shadow. It's hard to // avoid those without the use of an intermediate surface. // You might think that we could avoid those by just turning of AA, but there // is a problem with that: Box-shadow rendering needs to clip out the // element's border box, and we'd like that clip to have anti-aliasing - // especially if the element has rounded corners! So we can't do that unless // we have a way to say "Please anti-alias the clip, but don't antialias the // destination rect of the DrawSurface call". // On OS X there is an additional problem with turning off AA: CoreGraphics // will not just fill the pixels that have their pixel center inside the // filled shape. Instead, it will fill all the pixels which are partially // covered by the shape. So for pixels on the edge between two adjacent parts, // all those pixels will be painted to by both parts, which looks very bad. destDrawTarget.PopClip(); }
void nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, nsIFrame* aFrame, const nsRect& aDirtyRect, const nsRect& aBorderArea, nsDisplayListBuilder* aBuilder, LayerManager *aLayerManager) { #ifdef DEBUG NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) || (NS_SVGDisplayListPaintingEnabled() && !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)), "Should not use nsSVGIntegrationUtils on this SVG frame"); #endif /* SVG defines the following rendering model: * * 1. Render geometry * 2. Apply filter * 3. Apply clipping, masking, group opacity * * We follow this, but perform a couple of optimizations: * * + Use cairo's clipPath when representable natively (single object * clip region). * * + Merge opacity and masking if both used together. */ const nsIContent* content = aFrame->GetContent(); bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT); if (hasSVGLayout) { nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame); if (!svgChildFrame || !aFrame->GetContent()->IsSVGElement()) { NS_ASSERTION(false, "why?"); return; } if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) { return; // The SVG spec says not to draw _anything_ } } float opacity = aFrame->StyleDisplay()->mOpacity; if (opacity == 0.0f) { return; } if (opacity != 1.0f && hasSVGLayout && nsSVGUtils::CanOptimizeOpacity(aFrame)) { opacity = 1.0f; } /* Properties are added lazily and may have been removed by a restyle, so make sure all applicable ones are set again. */ nsIFrame* firstFrame = nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); nsSVGEffects::EffectProperties effectProperties = nsSVGEffects::GetEffectProperties(firstFrame); bool isOK = effectProperties.HasNoFilterOrHasValidFilter(); nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK); bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true; DrawTarget* drawTarget = aContext.GetDrawTarget(); gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&aContext); nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame); nsPoint offsetToBoundingBox = aBuilder->ToReferenceFrame(firstFrame) - firstFrameOffset; if (!firstFrame->IsFrameOfType(nsIFrame::eSVG)) { /* Snap the offset if the reference frame is not a SVG frame, * since other frames will be snapped to pixel when rendering. */ offsetToBoundingBox = nsPoint( aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.x), aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.y)); } // After applying only "offsetToBoundingBox", aCtx would have its origin at // the top left corner of aFrame's bounding box (over all continuations). // However, SVG painting needs the origin to be located at the origin of the // SVG frame's "user space", i.e. the space in which, for example, the // frame's BBox lives. // SVG geometry frames and foreignObject frames apply their own offsets, so // their position is relative to their user space. So for these frame types, // if we want aCtx to be in user space, we first need to subtract the // frame's position so that SVG painting can later add it again and the // frame is painted in the right place. gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame); nsPoint toUserSpace(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)), nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y))); nsPoint offsetToUserSpace = offsetToBoundingBox - toUserSpace; NS_ASSERTION(hasSVGLayout || offsetToBoundingBox == offsetToUserSpace, "For non-SVG frames there shouldn't be any additional offset"); gfxPoint devPixelOffsetToUserSpace = nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, aFrame->PresContext()->AppUnitsPerDevPixel()); aContext.SetMatrix(aContext.CurrentMatrix().Translate(devPixelOffsetToUserSpace)); gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(aFrame); const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset(); // Keep moving forward even if svgMaskFrame is nullptr or isOK is false. // This source is not a svg mask, but it still can be a correct mask image. nsSVGMaskFrame *svgMaskFrame = effectProperties.GetMaskFrame(&isOK); bool complexEffects = false; bool hasValidLayers = svgReset->mMask.HasLayerWithImage(); // These are used if we require a temporary surface for a custom blend mode. RefPtr<gfxContext> target = &aContext; IntPoint targetOffset; /* Check if we need to do additional operations on this child's * rendering, which necessitates rendering into another surface. */ if (opacity != 1.0f || (clipPathFrame && !isTrivialClip) || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL || svgMaskFrame || hasValidLayers) { complexEffects = true; aContext.Save(); nsRect clipRect = aFrame->GetVisualOverflowRectRelativeToSelf() + toUserSpace; aContext.Clip(NSRectToSnappedRect(clipRect, aFrame->PresContext()->AppUnitsPerDevPixel(), *drawTarget)); Matrix maskTransform; RefPtr<SourceSurface> maskSurface; if (svgMaskFrame) { maskSurface = svgMaskFrame->GetMaskForMaskedFrame(&aContext, aFrame, cssPxToDevPxMatrix, opacity, &maskTransform); } else if (hasValidLayers) { gfxRect clipRect = aContext.GetClipExtents(); { gfxContextMatrixAutoSaveRestore matRestore(&aContext); aContext.SetMatrix(gfxMatrix()); clipRect = aContext.GetClipExtents(); } IntRect drawRect = RoundedOut(ToRect(clipRect)); RefPtr<DrawTarget> targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::A8); if (!targetDT) { aContext.Restore(); return; } RefPtr<gfxContext> target = new gfxContext(targetDT); target->SetMatrix(matrixAutoSaveRestore.Matrix() * gfxMatrix::Translation(-drawRect.TopLeft())); // Generate mask surface. uint32_t flags = aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE; nsRenderingContext rc(target); nsCSSRendering::PaintBackgroundWithSC(aFrame->PresContext(), rc, aFrame, aDirtyRect, aBorderArea, firstFrame->StyleContext(), *aFrame->StyleBorder(), flags, nullptr, -1); maskSurface = targetDT->Snapshot(); // Compute mask transform. Matrix mat = ToMatrix(aContext.CurrentMatrix()); mat.Invert(); maskTransform = Matrix::Translation(drawRect.x, drawRect.y) * mat; } if ((svgMaskFrame || hasValidLayers) && !maskSurface) { // Entire surface is clipped out. aContext.Restore(); return; } if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { // Create a temporary context to draw to so we can blend it back with // another operator. gfxRect clipRect; { gfxContextMatrixAutoSaveRestore matRestore(&aContext); aContext.SetMatrix(gfxMatrix()); clipRect = aContext.GetClipExtents(); } IntRect drawRect = RoundedOut(ToRect(clipRect)); RefPtr<DrawTarget> targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8); if (!targetDT) { aContext.Restore(); return; } target = new gfxContext(targetDT); target->SetMatrix(aContext.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft())); targetOffset = drawRect.TopLeft(); } if (clipPathFrame && !isTrivialClip) { Matrix clippedMaskTransform; RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(aContext, aFrame, cssPxToDevPxMatrix, &clippedMaskTransform, maskSurface, maskTransform); if (clipMaskSurface) { maskSurface = clipMaskSurface; maskTransform = clippedMaskTransform; } } if (opacity != 1.0f || svgMaskFrame || hasValidLayers || (clipPathFrame && !isTrivialClip)) { target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform); } } /* If this frame has only a trivial clipPath, set up cairo's clipping now so * we can just do normal painting and get it clipped appropriately. */ if (clipPathFrame && isTrivialClip) { aContext.Save(); clipPathFrame->ApplyClipPath(aContext, aFrame, cssPxToDevPxMatrix); } /* Paint the child */ if (effectProperties.HasValidFilter()) { RegularFramePaintCallback callback(aBuilder, aLayerManager, offsetToUserSpace); nsRegion dirtyRegion = aDirtyRect - offsetToBoundingBox; gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame); nsFilterInstance::PaintFilteredFrame(aFrame, target->GetDrawTarget(), tm, &callback, &dirtyRegion); } else { target->SetMatrix(matrixAutoSaveRestore.Matrix()); BasicLayerManager* basic = static_cast<BasicLayerManager*>(aLayerManager); RefPtr<gfxContext> oldCtx = basic->GetTarget(); basic->SetTarget(target); aLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, aBuilder); basic->SetTarget(oldCtx); } if (clipPathFrame && isTrivialClip) { aContext.Restore(); } /* No more effects, we're done. */ if (!complexEffects) { return; } if (opacity != 1.0f || svgMaskFrame || hasValidLayers || (clipPathFrame && !isTrivialClip)) { target->PopGroupAndBlend(); } if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { RefPtr<DrawTarget> targetDT = target->GetDrawTarget(); target = nullptr; RefPtr<SourceSurface> targetSurf = targetDT->Snapshot(); aContext.SetMatrix(gfxMatrix()); // This will be restored right after. RefPtr<gfxPattern> pattern = new gfxPattern(targetSurf, Matrix::Translation(targetOffset.x, targetOffset.y)); aContext.SetPattern(pattern); aContext.Paint(); } aContext.Restore(); }
static IntRect ComputeMaskGeometry(const nsSVGIntegrationUtils::PaintFramesParams& aParams, const nsStyleSVGReset *svgReset, const nsPoint& aOffsetToUserSpace, const nsTArray<nsSVGMaskFrame *>& aMaskFrames) { gfxContext& ctx = aParams.ctx; nsIFrame* frame = aParams.frame; // Convert boaderArea and dirtyRect to user space. int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel(); nsRect userSpaceBorderArea = aParams.borderArea - aOffsetToUserSpace; nsRect userSpaceDirtyRect = aParams.dirtyRect - aOffsetToUserSpace; // Union all mask layer rectangles in user space. gfxRect maskInUserSpace; for (size_t i = 0; i < aMaskFrames.Length() ; i++) { nsSVGMaskFrame* maskFrame = aMaskFrames[i]; gfxRect currentMaskSurfaceRect; if (maskFrame) { currentMaskSurfaceRect = maskFrame->GetMaskArea(aParams.frame); } else { nsCSSRendering::ImageLayerClipState clipState; nsCSSRendering::GetImageLayerClip(svgReset->mMask.mLayers[i], frame, *frame->StyleBorder(), userSpaceBorderArea, userSpaceDirtyRect, false, /* aWillPaintBorder */ appUnitsPerDevPixel, &clipState); currentMaskSurfaceRect = clipState.mDirtyRectGfx; } maskInUserSpace = maskInUserSpace.Union(currentMaskSurfaceRect); } ctx.Save(); // Clip ctx by both frame's visual overflow rect and mask union. gfxRect frameVisualOverflowRect = nsLayoutUtils::RectToGfxRect(frame->GetVisualOverflowRectRelativeToSelf(), appUnitsPerDevPixel); ctx.Clip(frameVisualOverflowRect); // maskInUserSpace might be empty if all mask references are not resolvable // or the size of them are empty. We still need to create a transparent mask // before bug 1276834 fixed, so don't clip ctx by an empty rectangle for for // now. if (!maskInUserSpace.IsEmpty()) { ctx.Clip(maskInUserSpace); } // Get the clip extents in device space. ctx.SetMatrix(gfxMatrix()); gfxRect clippedFrameSurfaceRect = ctx.GetClipExtents(); clippedFrameSurfaceRect.RoundOut(); ctx.Restore(); IntRect result; ToRect(clippedFrameSurfaceRect).ToIntRect(&result); return mozilla::gfx::Factory::CheckSurfaceSize(result.Size()) ? result : IntRect(); }
void DeprecatedTextureHostShmemD3D9::UpdateImpl(const SurfaceDescriptor& aImage, nsIntRegion *aRegion, nsIntPoint *aOffset) { MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TShmem || aImage.type() == SurfaceDescriptor::TMemoryImage); MOZ_ASSERT(mCompositor, "Must have compositor to update."); if (!mCompositor->device()) { return; } AutoOpenSurface openSurf(OPEN_READ_ONLY, aImage); nsRefPtr<gfxImageSurface> surf = openSurf.GetAsImage(); mSize = ToIntSize(surf->GetSize()); uint32_t bpp = 0; _D3DFORMAT format = D3DFMT_A8R8G8B8; switch (surf->Format()) { case gfxImageFormat::RGB24: mFormat = SurfaceFormat::B8G8R8X8; format = D3DFMT_X8R8G8B8; bpp = 4; break; case gfxImageFormat::ARGB32: mFormat = SurfaceFormat::B8G8R8A8; format = D3DFMT_A8R8G8B8; bpp = 4; break; case gfxImageFormat::A8: mFormat = SurfaceFormat::A8; format = D3DFMT_A8; bpp = 1; break; default: NS_ERROR("Bad image format"); } int32_t maxSize = mCompositor->GetMaxTextureSize(); if (mSize.width <= maxSize && mSize.height <= maxSize) { mTexture = DataToTexture(gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager(), surf->Data(), surf->Stride(), mSize, format, bpp); if (!mTexture) { NS_WARNING("Could not upload texture"); Reset(); return; } mIsTiled = false; } else { mIsTiled = true; uint32_t tileCount = GetRequiredTilesD3D9(mSize.width, maxSize) * GetRequiredTilesD3D9(mSize.height, maxSize); mTileTextures.resize(tileCount); for (uint32_t i = 0; i < tileCount; i++) { IntRect tileRect = GetTileRect(i); unsigned char* data = surf->Data() + tileRect.y * surf->Stride() + tileRect.x * bpp; mTileTextures[i] = DataToTexture(gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager(), data, surf->Stride(), tileRect.Size(), format, bpp); if (!mTileTextures[i]) { NS_WARNING("Could not upload texture"); Reset(); return; } } } }
void nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) { #ifdef DEBUG NS_ASSERTION(!(aParams.frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) || (NS_SVGDisplayListPaintingEnabled() && !(aParams.frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)), "Should not use nsSVGIntegrationUtils on this SVG frame"); #endif /* SVG defines the following rendering model: * * 1. Render geometry * 2. Apply filter * 3. Apply clipping, masking, group opacity * * We follow this, but perform a couple of optimizations: * * + Use cairo's clipPath when representable natively (single object * clip region). * * + Merge opacity and masking if both used together. */ nsIFrame* frame = aParams.frame; const nsIContent* content = frame->GetContent(); bool hasSVGLayout = (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT); if (hasSVGLayout) { nsISVGChildFrame *svgChildFrame = do_QueryFrame(frame); if (!svgChildFrame || !frame->GetContent()->IsSVGElement()) { NS_ASSERTION(false, "why?"); return; } if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) { return; // The SVG spec says not to draw _anything_ } } float opacity = frame->StyleEffects()->mOpacity; if (opacity == 0.0f) { return; } if (opacity != 1.0f && (nsSVGUtils::CanOptimizeOpacity(frame) || aParams.callerPaintsOpacity)) { opacity = 1.0f; } MOZ_ASSERT(!nsSVGUtils::CanOptimizeOpacity(frame) || !aParams.callerPaintsOpacity, "How can we be optimizing the opacity into the svg as well as having the caller paint it?"); /* Properties are added lazily and may have been removed by a restyle, so make sure all applicable ones are set again. */ nsIFrame* firstFrame = nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame); nsSVGEffects::EffectProperties effectProperties = nsSVGEffects::GetEffectProperties(firstFrame); bool isOK = effectProperties.HasNoFilterOrHasValidFilter(); nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK); bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true; gfxContext& context = aParams.ctx; DrawTarget* drawTarget = context.GetDrawTarget(); gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context); nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame); nsPoint offsetToBoundingBox = aParams.builder->ToReferenceFrame(firstFrame) - firstFrameOffset; if (!firstFrame->IsFrameOfType(nsIFrame::eSVG)) { /* Snap the offset if the reference frame is not a SVG frame, * since other frames will be snapped to pixel when rendering. */ offsetToBoundingBox = nsPoint( frame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.x), frame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.y)); } // After applying only "offsetToBoundingBox", aCtx would have its origin at // the top left corner of frame's bounding box (over all continuations). // However, SVG painting needs the origin to be located at the origin of the // SVG frame's "user space", i.e. the space in which, for example, the // frame's BBox lives. // SVG geometry frames and foreignObject frames apply their own offsets, so // their position is relative to their user space. So for these frame types, // if we want aCtx to be in user space, we first need to subtract the // frame's position so that SVG painting can later add it again and the // frame is painted in the right place. gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(frame); nsPoint toUserSpace(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)), nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y))); nsPoint offsetToUserSpace = offsetToBoundingBox - toUserSpace; NS_ASSERTION(hasSVGLayout || offsetToBoundingBox == offsetToUserSpace, "For non-SVG frames there shouldn't be any additional offset"); gfxPoint devPixelOffsetToUserSpace = nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, frame->PresContext()->AppUnitsPerDevPixel()); context.SetMatrix(context.CurrentMatrix().Translate(devPixelOffsetToUserSpace)); gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(frame); const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset(); nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames(); // For a HTML doc: // According to css-masking spec, always create a mask surface when we // have any item in maskFrame even if all of those items are // non-resolvable <mask-sources> or <images>, we still need to create a // transparent black mask layer under this condition. // For a SVG doc: // SVG 1.1 say that if we fail to resolve a mask, we should draw the // object unmasked. nsIDocument* currentDoc = frame->PresContext()->Document(); bool shouldGenerateMaskLayer = currentDoc->IsSVGDocument() ? maskFrames.Length() == 1 && maskFrames[0] : maskFrames.Length() > 0; // These are used if we require a temporary surface for a custom blend mode. RefPtr<gfxContext> target = &aParams.ctx; IntPoint targetOffset; bool complexEffects = false; /* Check if we need to do additional operations on this child's * rendering, which necessitates rendering into another surface. */ if (opacity != 1.0f || (clipPathFrame && !isTrivialClip) || frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL || shouldGenerateMaskLayer) { complexEffects = true; context.Save(); nsRect clipRect = frame->GetVisualOverflowRectRelativeToSelf() + toUserSpace; context.Clip(NSRectToSnappedRect(clipRect, frame->PresContext()->AppUnitsPerDevPixel(), *drawTarget)); Matrix maskTransform; RefPtr<SourceSurface> maskSurface; if (shouldGenerateMaskLayer) { GenerateMaskSurface(aParams, opacity, firstFrame->StyleContext(), maskFrames, offsetToUserSpace, maskTransform, maskSurface); } if (shouldGenerateMaskLayer && !maskSurface) { // Entire surface is clipped out. context.Restore(); return; } if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { // Create a temporary context to draw to so we can blend it back with // another operator. gfxRect clipRect; { gfxContextMatrixAutoSaveRestore matRestore(&context); context.SetMatrix(gfxMatrix()); clipRect = context.GetClipExtents(); } IntRect drawRect = RoundedOut(ToRect(clipRect)); RefPtr<DrawTarget> targetDT = context.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8); if (!targetDT || !targetDT->IsValid()) { context.Restore(); return; } target = gfxContext::CreateOrNull(targetDT); MOZ_ASSERT(target); // already checked the draw target above target->SetMatrix(context.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft())); targetOffset = drawRect.TopLeft(); } if (clipPathFrame && !isTrivialClip) { Matrix clippedMaskTransform; RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix, &clippedMaskTransform, maskSurface, maskTransform); if (clipMaskSurface) { maskSurface = clipMaskSurface; maskTransform = clippedMaskTransform; } } if (opacity != 1.0f || shouldGenerateMaskLayer || (clipPathFrame && !isTrivialClip)) { target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform); } } /* If this frame has only a trivial clipPath, set up cairo's clipping now so * we can just do normal painting and get it clipped appropriately. */ if (clipPathFrame && isTrivialClip) { context.Save(); clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix); } else if (!clipPathFrame && svgReset->HasClipPath()) { context.Save(); nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame); } /* Paint the child */ if (effectProperties.HasValidFilter()) { RegularFramePaintCallback callback(aParams.builder, aParams.layerManager, offsetToUserSpace); nsRegion dirtyRegion = aParams.dirtyRect - offsetToBoundingBox; gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame); nsFilterInstance::PaintFilteredFrame(frame, target->GetDrawTarget(), tm, &callback, &dirtyRegion); } else { target->SetMatrix(matrixAutoSaveRestore.Matrix()); BasicLayerManager* basic = static_cast<BasicLayerManager*>(aParams.layerManager); RefPtr<gfxContext> oldCtx = basic->GetTarget(); basic->SetTarget(target); aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, aParams.builder); basic->SetTarget(oldCtx); } if ((clipPathFrame && isTrivialClip) || (!clipPathFrame && svgReset->HasClipPath())) { context.Restore(); } /* No more effects, we're done. */ if (!complexEffects) { return; } if (opacity != 1.0f || shouldGenerateMaskLayer || (clipPathFrame && !isTrivialClip)) { target->PopGroupAndBlend(); } if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { RefPtr<DrawTarget> targetDT = target->GetDrawTarget(); target = nullptr; RefPtr<SourceSurface> targetSurf = targetDT->Snapshot(); context.SetMatrix(gfxMatrix()); // This will be restored right after. RefPtr<gfxPattern> pattern = new gfxPattern(targetSurf, Matrix::Translation(targetOffset.x, targetOffset.y)); context.SetPattern(pattern); context.Paint(); } context.Restore(); }
/* static */ void gfxAlphaBoxBlur::BlurRectangle(gfxContext* aDestinationCtx, const gfxRect& aRect, const RectCornerRadii* aCornerRadii, const gfxPoint& aBlurStdDev, const Color& aShadowColor, const gfxRect& aDirtyRect, const gfxRect& aSkipRect) { IntSize blurRadius = CalculateBlurRadius(aBlurStdDev); bool mirrorCorners = !aCornerRadii || aCornerRadii->AreRadiiSame(); IntRect rect = RoundedToInt(ToRect(aRect)); IntMargin blurMargin; IntMargin slice; IntSize minSize; RefPtr<SourceSurface> boxShadow = GetBlur(aDestinationCtx, rect.Size(), blurRadius, aCornerRadii, aShadowColor, mirrorCorners, blurMargin, slice, minSize); if (!boxShadow) { return; } DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget(); destDrawTarget->PushClipRect(ToRect(aDirtyRect)); // Copy the right parts from boxShadow into destDrawTarget. The middle parts // will be stretched, border-image style. Rect srcOuter(Point(blurMargin.left, blurMargin.top), Size(minSize)); Rect srcInner(srcOuter); srcOuter.Inflate(Margin(blurMargin)); srcInner.Deflate(Margin(slice)); Rect dstOuter(rect); Rect dstInner(rect); dstOuter.Inflate(Margin(blurMargin)); dstInner.Deflate(Margin(slice)); Rect skipRect = ToRect(aSkipRect); if (minSize == rect.Size()) { // The target rect is smaller than the minimal size so just draw the surface if (mirrorCorners) { DrawMirroredBoxShadow(destDrawTarget, boxShadow, dstOuter); } else { destDrawTarget->DrawSurface(boxShadow, dstOuter, srcOuter); } } else { if (mirrorCorners) { DrawMirroredMinBoxShadow(destDrawTarget, boxShadow, dstOuter, dstInner, srcOuter, srcInner, skipRect, true); } else { DrawMinBoxShadow(destDrawTarget, boxShadow, dstOuter, dstInner, srcOuter, srcInner, skipRect, true); } } // A note about anti-aliasing and seems between adjacent parts: // We don't explicitly disable anti-aliasing in the DrawSurface calls above, // so if there's a transform on destDrawTarget that is not pixel-aligned, // there will be seams between adjacent parts of the box-shadow. It's hard to // avoid those without the use of an intermediate surface. // You might think that we could avoid those by just turning of AA, but there // is a problem with that: Box-shadow rendering needs to clip out the // element's border box, and we'd like that clip to have anti-aliasing - // especially if the element has rounded corners! So we can't do that unless // we have a way to say "Please anti-alias the clip, but don't antialias the // destination rect of the DrawSurface call". // On OS X there is an additional problem with turning off AA: CoreGraphics // will not just fill the pixels that have their pixel center inside the // filled shape. Instead, it will fill all the pixels which are partially // covered by the shape. So for pixels on the edge between two adjacent parts, // all those pixels will be painted to by both parts, which looks very bad. destDrawTarget->PopClip(); }
static void GenerateMaskSurface(const nsSVGIntegrationUtils::PaintFramesParams& aParams, float aOpacity, nsStyleContext* aSC, nsSVGEffects::EffectProperties& aEffectProperties, const gfxPoint& aOffest, Matrix& aOutMaskTransform, RefPtr<SourceSurface>& aOutMaskSurface) { const nsStyleSVGReset *svgReset = aSC->StyleSVGReset(); MOZ_ASSERT(HasMaskToDraw(svgReset, aEffectProperties)); nsTArray<nsSVGMaskFrame *> svgMaskFrames = aEffectProperties.GetMaskFrames(); MOZ_ASSERT(svgMaskFrames.Length() == svgReset->mMask.mImageCount); gfxMatrix cssPxToDevPxMatrix = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aParams.frame); gfxContext& ctx = aParams.ctx; // There is only one mask. And that mask is a SVG mask. if ((svgMaskFrames.Length() == 1) && svgMaskFrames[0]) { aOutMaskSurface = svgMaskFrames[0]->GetMaskForMaskedFrame(&ctx, aParams.frame, cssPxToDevPxMatrix, aOpacity, &aOutMaskTransform, svgReset->mMask.mLayers[0].mMaskMode); return; } ctx.Save(); ctx.SetMatrix(gfxMatrix()); gfxRect clipExtents = ctx.GetClipExtents(); IntRect maskSurfaceRect = RoundedOut(ToRect(clipExtents)); ctx.Restore(); // 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) ? Factory::CreateDrawTarget(BackendType::SKIA, maskSurfaceRect.Size(), SurfaceFormat::A8) : ctx.GetDrawTarget()->CreateSimilarDrawTarget(maskSurfaceRect.Size(), SurfaceFormat::A8); RefPtr<gfxContext> maskContext = gfxContext::ForDrawTarget(maskDT); // 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 = svgMaskFrames.Length() - 1; i >= 0 ; i--) { nsSVGMaskFrame *maskFrame = svgMaskFrames[i]; CompositionOp compositionOp = (i == int(svgMaskFrames.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(-aOffest)); nsRenderingContext rc(maskContext); nsCSSRendering::PaintBGParams params = nsCSSRendering::PaintBGParams::ForSingleLayer(*aParams.frame->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(); }
void DeprecatedTextureHostSystemMemD3D9::UpdateImpl(const SurfaceDescriptor& aImage, nsIntRegion *aRegion, nsIntPoint *aOffset) { MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TSurfaceDescriptorD3D9); MOZ_ASSERT(mCompositor, "Must have compositor to update."); if (!mCompositor->device()) { return; } IDirect3DTexture9* texture = reinterpret_cast<IDirect3DTexture9*>(aImage.get_SurfaceDescriptorD3D9().texture()); if (!texture) { Reset(); return; } D3DSURFACE_DESC desc; texture->GetLevelDesc(0, &desc); HRESULT hr = texture->GetLevelDesc(0, &desc); if (FAILED(hr)) { Reset(); return; } mSize.width = desc.Width; mSize.height = desc.Height; _D3DFORMAT format = desc.Format; uint32_t bpp = 0; switch (format) { case D3DFMT_X8R8G8B8: mFormat = SurfaceFormat::B8G8R8X8; bpp = 4; break; case D3DFMT_A8R8G8B8: mFormat = SurfaceFormat::B8G8R8A8; bpp = 4; break; case D3DFMT_A8: mFormat = SurfaceFormat::A8; bpp = 1; break; default: NS_ERROR("Bad image format"); } int32_t maxSize = mCompositor->GetMaxTextureSize(); if (mSize.width <= maxSize && mSize.height <= maxSize) { mIsTiled = false; mTexture = TextureToTexture(gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager(), texture, mSize, format); if (!mTexture) { NS_WARNING("Could not upload texture"); Reset(); return; } } else { mIsTiled = true; uint32_t tileCount = GetRequiredTilesD3D9(mSize.width, maxSize) * GetRequiredTilesD3D9(mSize.height, maxSize); mTileTextures.resize(tileCount); for (uint32_t i = 0; i < tileCount; i++) { IntRect tileRect = GetTileRect(i); RECT d3dTileRect; d3dTileRect.left = tileRect.x; d3dTileRect.top = tileRect.y; d3dTileRect.right = tileRect.XMost(); d3dTileRect.bottom = tileRect.YMost(); D3DLOCKED_RECT lockedRect; texture->LockRect(0, &lockedRect, &d3dTileRect, 0); mTileTextures[i] = DataToTexture(gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager(), reinterpret_cast<unsigned char*>(lockedRect.pBits), lockedRect.Pitch, tileRect.Size(), format, bpp); texture->UnlockRect(0); if (!mTileTextures[i]) { NS_WARNING("Could not upload texture"); Reset(); return; } } } }
void DeprecatedTextureHostDIB::UpdateImpl(const SurfaceDescriptor& aImage, nsIntRegion *aRegion, nsIntPoint *aOffset) { MOZ_ASSERT(aImage.type() == SurfaceDescriptor::TSurfaceDescriptorDIB); MOZ_ASSERT(mCompositor, "Must have compositor to update."); if (!mCompositor->device()) { return; } // We added an extra ref for transport, so we shouldn't AddRef now. nsRefPtr<gfxWindowsSurface> surf = dont_AddRef(reinterpret_cast<gfxWindowsSurface*>(aImage.get_SurfaceDescriptorDIB().surface())); mSize = ToIntSize(surf->GetSize()); uint32_t bpp = 0; _D3DFORMAT format = D3DFMT_A8R8G8B8; switch (gfxPlatform::GetPlatform()->OptimalFormatForContent(surf->GetContentType())) { case gfxImageFormat::RGB24: mFormat = SurfaceFormat::B8G8R8X8; format = D3DFMT_X8R8G8B8; bpp = 4; break; case gfxImageFormat::ARGB32: mFormat = SurfaceFormat::B8G8R8A8; format = D3DFMT_A8R8G8B8; bpp = 4; break; case gfxImageFormat::A8: mFormat = SurfaceFormat::A8; format = D3DFMT_A8; bpp = 1; break; default: NS_ERROR("Bad image format"); } int32_t maxSize = mCompositor->GetMaxTextureSize(); if (mSize.width <= maxSize && mSize.height <= maxSize) { mTexture = SurfaceToTexture(gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager(), surf, mSize, format); if (!mTexture) { NS_WARNING("Could not upload texture"); Reset(); return; } mIsTiled = false; } else { mIsTiled = true; uint32_t tileCount = GetRequiredTilesD3D9(mSize.width, maxSize) * GetRequiredTilesD3D9(mSize.height, maxSize); mTileTextures.resize(tileCount); for (uint32_t i = 0; i < tileCount; i++) { IntRect tileRect = GetTileRect(i); nsRefPtr<gfxImageSurface> imgSurface = surf->GetAsImageSurface(); unsigned char* data = imgSurface->Data() + tileRect.y * imgSurface->Stride() + tileRect.x * bpp; mTileTextures[i] = DataToTexture(gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager(), data, imgSurface->Stride(), tileRect.Size(), format, bpp); if (!mTileTextures[i]) { NS_WARNING("Could not upload texture"); Reset(); return; } } } }
/* static */ already_AddRefed<VideoData> VideoData::Create(const VideoInfo& aInfo, ImageContainer* aContainer, int64_t aOffset, int64_t aTime, int64_t aDuration, mozilla::layers::TextureClient* aBuffer, bool aKeyframe, int64_t aTimecode, const IntRect& aPicture) { if (!aContainer) { // Create a dummy VideoData with no image. This gives us something to // send to media streams if necessary. RefPtr<VideoData> v(new VideoData(aOffset, aTime, aDuration, aKeyframe, aTimecode, aInfo.mDisplay, 0)); return v.forget(); } // The following situations could be triggered by invalid input if (aPicture.width <= 0 || aPicture.height <= 0) { NS_WARNING("Empty picture rect"); return nullptr; } // Ensure the picture size specified in the headers can be extracted out of // the frame we've been supplied without indexing out of bounds. CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width); CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height); if (!xLimit.isValid() || !yLimit.isValid()) { // The specified picture dimensions can't be contained inside the video // frame, we'll stomp memory if we try to copy it. Fail. NS_WARNING("Overflowing picture rect"); return nullptr; } RefPtr<VideoData> v(new VideoData(aOffset, aTime, aDuration, aKeyframe, aTimecode, aInfo.mDisplay, 0)); v->mImage = aContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR); if (!v->mImage) { return nullptr; } NS_ASSERTION(v->mImage->GetFormat() == ImageFormat::GRALLOC_PLANAR_YCBCR, "Wrong format?"); typedef mozilla::layers::GrallocImage GrallocImage; GrallocImage* videoImage = static_cast<GrallocImage*>(v->mImage.get()); GrallocImage::GrallocData data; data.mPicSize = aPicture.Size(); data.mGraphicBuffer = aBuffer; if (!videoImage->SetData(data)) { return nullptr; } return v.forget(); }
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<mozilla::gfx::SourceSurface> gfxAlphaBoxBlur::GetInsetBlur(IntMargin& aExtendDestBy, IntMargin& aSlice, const Rect aDestinationRect, const Rect aShadowClipRect, const IntSize& aBlurRadius, const IntSize& aSpreadRadius, const RectCornerRadii& aInnerClipRadii, const Color& aShadowColor, const bool& aHasBorderRadius, const Point aShadowOffset, bool& aMovedOffset, DrawTarget* aDestDrawTarget) { if (!gBlurCache) { gBlurCache = new BlurCache(); } IntRect outerRect; IntRect innerRect; ComputeRectsForInsetBoxShadow(aBlurRadius, aSpreadRadius, outerRect, innerRect, aSlice, aDestinationRect, aShadowClipRect, aHasBorderRadius, aInnerClipRadii); // If we have a shadow offset larger than the min rect, // there's no clean way we can properly create a min rect with the offset // in the correct place and still render correctly. In those cases, // fallback to just rendering the dest rect as is. bool useDestRect = (std::abs(aShadowOffset.x) > aSlice.left) || (std::abs(aShadowOffset.y) > aSlice.top); aMovedOffset = false; if (useDestRect) { aDestinationRect.ToIntRect(&outerRect); aShadowClipRect.ToIntRect(&innerRect); aMovedOffset = true; } BlurCacheData* cached = gBlurCache->LookupInsetBoxShadow(outerRect.Size(), innerRect.Size(), aBlurRadius, aSpreadRadius, &aInnerClipRadii, aShadowColor, aHasBorderRadius, aDestDrawTarget->GetBackendType()); if (cached && !useDestRect) { aExtendDestBy = cached->mExtendDest; // Need to extend it twice: once for the outer rect and once for the inner rect. aSlice += aExtendDestBy; aSlice += aExtendDestBy; // So we don't forget the actual cached blur RefPtr<SourceSurface> cachedBlur = cached->mBlur; return cachedBlur.forget(); } // Dirty rect and skip rect are null for the min inset shadow. // When rendering inset box shadows, we respect the spread radius by changing // the shape of the unblurred shadow, and can pass a spread radius of zero here. IntSize zeroSpread(0, 0); gfxContext* minGfxContext = Init(ThebesRect(outerRect), zeroSpread, aBlurRadius, nullptr, nullptr); if (!minGfxContext) { return nullptr; } DrawTarget* minDrawTarget = minGfxContext->GetDrawTarget(); RefPtr<Path> maskPath = GetBoxShadowInsetPath(minDrawTarget, IntRectToRect(outerRect), IntRectToRect(innerRect), aHasBorderRadius, aInnerClipRadii); Color black(0.f, 0.f, 0.f, 1.f); minGfxContext->SetColor(black); minGfxContext->SetPath(maskPath); minGfxContext->Fill(); IntPoint topLeft; RefPtr<SourceSurface> minMask = DoBlur(minDrawTarget, &topLeft); if (!minMask) { return nullptr; } RefPtr<SourceSurface> minInsetBlur = CreateBoxShadow(minMask, aShadowColor); if (!minInsetBlur) { return nullptr; } IntRect blurRect(topLeft, minInsetBlur->GetSize()); aExtendDestBy = blurRect - outerRect; if (useDestRect) { // Since we're just going to paint the actual rect to the destination aSlice.SizeTo(0, 0, 0, 0); } else { aSlice += aExtendDestBy; aSlice += aExtendDestBy; CacheInsetBlur(outerRect.Size(), innerRect.Size(), aBlurRadius, aSpreadRadius, &aInnerClipRadii, aShadowColor, aHasBorderRadius, aDestDrawTarget->GetBackendType(), aExtendDestBy, minInsetBlur); } return minInsetBlur.forget(); }