Example #1
0
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(&region);
#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;
}
Example #4
0
/**
 * 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());
}
Example #7
0
//******************************************************************************
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);
  }
}
Example #8
0
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();
}
Example #9
0
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();
}
Example #11
0
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();
}
Example #13
0
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();
}