already_AddRefed<Path>
nsSVGPathGeometryElement::GetOrBuildPath(const DrawTarget& aDrawTarget,
                                         FillRule aFillRule)
{
  // We only cache the path if it matches the backend used for screen painting:
  bool cacheable  = aDrawTarget.GetBackendType() ==
                    gfxPlatform::GetPlatform()->GetDefaultContentBackend();

  // Checking for and returning mCachedPath before checking the pref means
  // that the pref is only live on page reload (or app restart for SVG in
  // chrome). The benefit is that we avoid causing a CPU memory cache miss by
  // looking at the global variable that the pref's stored in.
  if (cacheable && mCachedPath) {
    if (aDrawTarget.GetBackendType() == mCachedPath->GetBackendType()) {
      RefPtr<Path> path(mCachedPath);
      return path.forget();
    }
  }
  RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder(aFillRule);
  RefPtr<Path> path = BuildPath(builder);
  if (cacheable && NS_SVGPathCachingEnabled()) {
    mCachedPath = path;
  }
  return path.forget();
}
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;
}
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);
}
CGContextRef
gfxQuartzNativeDrawing::BeginNativeDrawing()
{
  NS_ASSERTION(!mCGContext, "BeginNativeDrawing called when drawing already in progress");

  if (mContext->IsCairo()) {
    // We're past that now. Any callers that still supply a Cairo context
    // don't deserve native theming.
    NS_WARNING("gfxQuartzNativeDrawing being used with a gfxContext that is not backed by a DrawTarget");
    return nullptr;
  }

  DrawTarget *dt = mContext->GetDrawTarget();
  if (dt->GetBackendType() != BackendType::COREGRAPHICS || dt->IsDualDrawTarget()) {
    IntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale),
                        NSToIntFloor(mNativeRect.height * mBackingScale));

    if (backingSize.IsEmpty()) {
      return nullptr;
    }

    mDrawTarget = Factory::CreateDrawTarget(BackendType::COREGRAPHICS, backingSize, SurfaceFormat::B8G8R8A8);

    Matrix transform;
    transform.Scale(mBackingScale, mBackingScale);
    transform.Translate(-mNativeRect.x, -mNativeRect.y);

    mDrawTarget->SetTransform(transform);
    dt = mDrawTarget;
  }

  mCGContext = mBorrowedContext.Init(dt);
  MOZ_ASSERT(mCGContext);
  return mCGContext;
}
Exemple #5
0
static already_AddRefed<SourceSurface>
GetBlur(gfxContext* aDestinationCtx,
        const IntSize& aRectSize,
        const IntSize& aBlurRadius,
        const RectCornerRadii* aCornerRadii,
        const Color& aShadowColor,
        bool aMirrorCorners,
        IntMargin& aOutBlurMargin,
        IntMargin& aOutSlice,
        IntSize& aOutMinSize)
{
  if (!gBlurCache) {
    gBlurCache = new BlurCache();
  }

  IntSize minSize =
    ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, aOutSlice, aRectSize);

  // We can get seams using the min size rect when drawing to the destination rect
  // if we have a non-pixel aligned destination transformation. In those cases,
  // fallback to just rendering the destination rect.
  Matrix destMatrix = ToMatrix(aDestinationCtx->CurrentMatrix());
  bool useDestRect = !destMatrix.IsRectilinear() || destMatrix.HasNonIntegerTranslation();
  if (useDestRect) {
    minSize = aRectSize;
  }
  aOutMinSize = minSize;

  DrawTarget* destDT = aDestinationCtx->GetDrawTarget();

  if (!useDestRect) {
    BlurCacheData* cached = gBlurCache->Lookup(minSize, aBlurRadius,
                                               aCornerRadii, aShadowColor,
                                               destDT->GetBackendType());
    if (cached) {
      // See CreateBoxShadow() for these values
      aOutBlurMargin = cached->mBlurMargin;
      RefPtr<SourceSurface> blur = cached->mBlur;
      return blur.forget();
    }
  }

  RefPtr<SourceSurface> boxShadow =
    CreateBoxShadow(destDT, minSize, aCornerRadii, aBlurRadius,
                    aShadowColor, aMirrorCorners, aOutBlurMargin);
  if (!boxShadow) {
    return nullptr;
  }

  if (!useDestRect) {
    CacheBlur(destDT, minSize, aBlurRadius, aCornerRadii, aShadowColor,
              aOutBlurMargin, boxShadow);
  }
  return boxShadow.forget();
}
void
CacheBlur(DrawTarget& aDT,
          const IntSize& aMinSize,
          const gfxIntSize& aBlurRadius,
          RectCornerRadii* aCornerRadii,
          const gfxRGBA& aShadowColor,
          IntMargin aExtendDest,
          SourceSurface* aBoxShadow)
{
  BlurCacheKey key(aMinSize, aBlurRadius, aCornerRadii, aShadowColor, aDT.GetBackendType());
  BlurCacheData* data = new BlurCacheData(aBoxShadow, aExtendDest, key);
  if (!gBlurCache->RegisterEntry(data)) {
    delete data;
  }
}
HDC
gfxWindowsNativeDrawing::BeginNativeDrawing()
{
    if (mRenderState == RENDER_STATE_INIT) {
        RefPtr<gfxASurface> surf;
        DrawTarget* drawTarget = mContext->GetDrawTarget();
        cairo_t* cairo = nullptr;
        if (drawTarget->GetBackendType() == BackendType::CAIRO) {
            cairo = static_cast<cairo_t*>
                (drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
            if (cairo) {
                cairo_surface_t* s = cairo_get_target(cairo);
                if (s) {
                    mDeviceOffset = mContext->GetDeviceOffset();
                    surf = gfxASurface::Wrap(s);
                }
            }
        }

        if (surf && surf->CairoStatus() != 0)
            return nullptr;

        gfxMatrix m = mContext->CurrentMatrix();
        if (!m.HasNonTranslation())
            mTransformType = TRANSLATION_ONLY;
        else if (m.HasNonAxisAlignedTransform())
            mTransformType = COMPLEX;
        else
            mTransformType = AXIS_ALIGNED_SCALE;

        // if this is a native win32 surface, we don't have to
        // redirect rendering to our own HDC; in some cases,
        // we may be able to use the HDC from the surface directly.
        if (surf &&
            ((surf->GetType() == gfxSurfaceType::Win32 ||
              surf->GetType() == gfxSurfaceType::Win32Printing) &&
              (surf->GetContentType() == gfxContentType::COLOR ||
               (surf->GetContentType() == gfxContentType::COLOR_ALPHA &&
               (mNativeDrawFlags & CAN_DRAW_TO_COLOR_ALPHA)))))
        {
            // grab the DC. This can fail if there is a complex clipping path,
            // in which case we'll have to fall back.
            mWinSurface = static_cast<gfxWindowsSurface*>(static_cast<gfxASurface*>(surf.get()));
            mDC = cairo_win32_get_dc_with_clip(cairo);

            if (mDC) {
                if (mTransformType == TRANSLATION_ONLY) {
                    mRenderState = RENDER_STATE_NATIVE_DRAWING;

                    mTranslation = m.GetTranslation();
                } else if (((mTransformType == AXIS_ALIGNED_SCALE)
                            && (mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE)) ||
                           (mNativeDrawFlags & CAN_COMPLEX_TRANSFORM))
                {
                    mWorldTransform.eM11 = (FLOAT) m._11;
                    mWorldTransform.eM12 = (FLOAT) m._12;
                    mWorldTransform.eM21 = (FLOAT) m._21;
                    mWorldTransform.eM22 = (FLOAT) m._22;
                    mWorldTransform.eDx  = (FLOAT) m._31;
                    mWorldTransform.eDy  = (FLOAT) m._32;

                    mRenderState = RENDER_STATE_NATIVE_DRAWING;
                }
            }
        }

        // If we couldn't do native drawing, then we have to do two-buffer drawing
        // and do alpha recovery
        if (mRenderState == RENDER_STATE_INIT) {
            mRenderState = RENDER_STATE_ALPHA_RECOVERY_BLACK;

            // We round out our native rect here, that way the snapping will
            // happen correctly.
            mNativeRect.RoundOut();

            // we only do the scale bit if we can do an axis aligned
            // scale; otherwise we scale (if necessary) after
            // rendering with cairo.  Note that if we're doing alpha recovery,
            // we cannot do a full complex transform with win32 (I mean, we could, but
            // it would require more code that's not here.)
            if (mTransformType == TRANSLATION_ONLY || !(mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE)) {
                mScale = gfxSize(1.0, 1.0);

                // Add 1 to the surface size; it's guaranteed to not be incorrect,
                // and it fixes bug 382458
                // There's probably a better fix, but I haven't figured out
                // the root cause of the problem.
                mTempSurfaceSize =
                    IntSize((int32_t) ceil(mNativeRect.Width() + 1),
                               (int32_t) ceil(mNativeRect.Height() + 1));
            } else {
                // figure out the scale factors
                mScale = m.ScaleFactors(true);

                mWorldTransform.eM11 = (FLOAT) mScale.width;
                mWorldTransform.eM12 = 0.0f;
                mWorldTransform.eM21 = 0.0f;
                mWorldTransform.eM22 = (FLOAT) mScale.height;
                mWorldTransform.eDx  = 0.0f;
                mWorldTransform.eDy  = 0.0f;

                // See comment above about "+1"
                mTempSurfaceSize =
                    IntSize((int32_t) ceil(mNativeRect.Width() * mScale.width + 1),
                               (int32_t) ceil(mNativeRect.Height() * mScale.height + 1));
            }
        }
    }

    if (mRenderState == RENDER_STATE_NATIVE_DRAWING) {
        // we can just do native drawing directly to the context's surface

        // do we need to use SetWorldTransform?
        if (mTransformType != TRANSLATION_ONLY) {
            SetGraphicsMode(mDC, GM_ADVANCED);
            GetWorldTransform(mDC, &mOldWorldTransform);
            SetWorldTransform(mDC, &mWorldTransform);
        }
        GetViewportOrgEx(mDC, &mOrigViewportOrigin);
        SetViewportOrgEx(mDC,
                         mOrigViewportOrigin.x - (int)mDeviceOffset.x,
                         mOrigViewportOrigin.y - (int)mDeviceOffset.y,
                         nullptr);

        return mDC;
    } else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_BLACK ||
               mRenderState == RENDER_STATE_ALPHA_RECOVERY_WHITE)
    {
        // we're going to use mWinSurface to create our temporary surface here

        // get us a RGB24 DIB; DIB is important, because
        // we can later call GetImageSurface on it.
        mWinSurface = new gfxWindowsSurface(mTempSurfaceSize);
        mDC = mWinSurface->GetDC();

        RECT r = { 0, 0, mTempSurfaceSize.width, mTempSurfaceSize.height };
        if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_BLACK)
            FillRect(mDC, &r, (HBRUSH)GetStockObject(BLACK_BRUSH));
        else
            FillRect(mDC, &r, (HBRUSH)GetStockObject(WHITE_BRUSH));

        if ((mTransformType != TRANSLATION_ONLY) &&
            (mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE))
        {
            SetGraphicsMode(mDC, GM_ADVANCED);
            SetWorldTransform(mDC, &mWorldTransform);
        }

        return mDC;
    } else {
        NS_ERROR("Bogus render state!");
        return nullptr;
    }
}
Exemple #8
0
static already_AddRefed<SourceSurface>
GetBlur(gfxContext* aDestinationCtx,
        const IntSize& aRectSize,
        const IntSize& aBlurRadius,
        const RectCornerRadii* aCornerRadii,
        const Color& aShadowColor,
        bool aMirrorCorners,
        IntMargin& aOutBlurMargin,
        IntMargin& aOutSlice,
        IntSize& aOutMinSize)
{
  if (!gBlurCache) {
    gBlurCache = new BlurCache();
  }

  IntSize minSize =
    ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, aOutSlice, aRectSize);

  // We can get seams using the min size rect when drawing to the destination rect
  // if we have a non-pixel aligned destination transformation. In those cases,
  // fallback to just rendering the destination rect.
  // During printing, we record all the Moz 2d commands and replay them on the parent side
  // with Cairo. Cairo printing uses StretchDIBits to stretch the surface. However,
  // since our source image is only 1px for some parts, we make thousands of calls.
  // Instead just render the blur ourself here as one image and send it over for printing.
  // TODO: May need to change this with the blob renderer in WR since it also records.
  Matrix destMatrix = ToMatrix(aDestinationCtx->CurrentMatrix());
  bool useDestRect = !destMatrix.IsRectilinear() || destMatrix.HasNonIntegerTranslation() ||
                     aDestinationCtx->GetDrawTarget()->IsRecording();
  if (useDestRect) {
    minSize = aRectSize;
  }
  aOutMinSize = minSize;

  DrawTarget* destDT = aDestinationCtx->GetDrawTarget();

  if (!useDestRect) {
    BlurCacheData* cached = gBlurCache->Lookup(minSize, aBlurRadius,
                                               aCornerRadii, aShadowColor,
                                               destDT->GetBackendType());
    if (cached) {
      // See CreateBoxShadow() for these values
      aOutBlurMargin = cached->mBlurMargin;
      RefPtr<SourceSurface> blur = cached->mBlur;
      return blur.forget();
    }
  }

  RefPtr<SourceSurface> boxShadow =
    CreateBoxShadow(destDT, minSize, aCornerRadii, aBlurRadius,
                    aShadowColor, aMirrorCorners, aOutBlurMargin);
  if (!boxShadow) {
    return nullptr;
  }

  if (RefPtr<SourceSurface> opt = destDT->OptimizeSourceSurface(boxShadow)) {
    boxShadow = opt;
  }

  if (!useDestRect) {
    CacheBlur(destDT, minSize, aBlurRadius, aCornerRadii, aShadowColor,
              aOutBlurMargin, boxShadow);
  }
  return boxShadow.forget();
}
already_AddRefed<mozilla::gfx::SourceSurface>
gfxAlphaBoxBlur::GetInsetBlur(Rect& aOuterRect,
                              Rect& aInnerRect,
                              const gfxIntSize& aBlurRadius,
                              const gfxIntSize& aSpreadRadius,
                              const RectCornerRadii& aInnerClipRadii,
                              const Color& aShadowColor,
                              const bool& aHasBorderRadius,
                              IntPoint& aOutTopLeft,
                              gfxContext* aDestinationCtx)

{
  if (!gBlurCache) {
    gBlurCache = new BlurCache();
  }

  gfxIntSize outerRectSize = RoundedToInt(aOuterRect).Size();
  gfxIntSize innerRectSize = RoundedToInt(aInnerRect).Size();
  DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget();

  BlurCacheData* cached =
      gBlurCache->LookupInsetBoxShadow(outerRectSize, innerRectSize, aBlurRadius, aSpreadRadius,
                                       &aInnerClipRadii, ThebesColor(aShadowColor),
                                       aHasBorderRadius, destDrawTarget->GetBackendType());

  if (cached) {
    IntMargin extends = cached->mExtendDest;
    aOutTopLeft.x = extends.left;
    aOutTopLeft.y = extends.top;
    // 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.
  gfxIntSize zeroSpread(0, 0);
  gfxContext* minGfxContext = Init(ThebesRect(aOuterRect), zeroSpread, aBlurRadius, nullptr, nullptr);
  if (!minGfxContext) {
    return nullptr;
  }

  DrawTarget* minDrawTarget = minGfxContext->GetDrawTarget();
  RefPtr<Path> maskPath = GetBoxShadowInsetPath(minDrawTarget, aOuterRect,
                                                aInnerRect, aHasBorderRadius,
                                                aInnerClipRadii);

  minGfxContext->SetColor(ThebesColor(aShadowColor));
  minGfxContext->SetPath(maskPath);
  minGfxContext->Fill();

  RefPtr<SourceSurface> minMask = DoBlur(minDrawTarget, &aOutTopLeft);
  if (!minMask) {
    return nullptr;
  }

  RefPtr<SourceSurface> minInsetBlur = CreateBoxShadow(minMask, ThebesColor(aShadowColor));
  if (!minInsetBlur) {
    return nullptr;
  }

  IntMargin extendBy(aOutTopLeft.y, 0, 0, aOutTopLeft.x);
  CacheInsetBlur(outerRectSize, innerRectSize,
                 aBlurRadius, aSpreadRadius,
                 &aInnerClipRadii, ThebesColor(aShadowColor),
                 aHasBorderRadius, destDrawTarget->GetBackendType(),
                 extendBy, minInsetBlur);
  return minInsetBlur.forget();
}
/***
 * If we can, let's paint this ClientPaintedLayer's contents off the main thread.
 * The essential idea is that we ask the ContentClient for a DrawTarget and record
 * the moz2d commands. On the Paint Thread, we replay those commands to the
 * destination draw target. There are a couple of lifetime issues here though:
 *
 * 1) TextureClient owns the underlying buffer and DrawTarget. Because of this
 *    we have to keep the TextureClient and DrawTarget alive but trick the
 *    TextureClient into thinking it's already returned the DrawTarget
 *    since we iterate through different Rects to get DrawTargets*. If
 *    the TextureClient goes away, the DrawTarget and thus buffer can too.
 * 2) When ContentClient::EndPaint happens, it flushes the DrawTarget. We have
 *    to Reflush on the Paint Thread
 * 3) DrawTarget API is NOT thread safe. We get around this by recording
 *    on the main thread and painting on the paint thread. Logically,
 *    ClientLayerManager will force a flushed paint and block the main thread
 *    if we have another transaction. Thus we have a gap between when the main
 *    thread records, the paint thread paints, and we block the main thread
 *    from trying to paint again. The underlying API however is NOT thread safe.
 *  4) We have both "sync" and "async" OMTP. Sync OMTP means we paint on the main thread
 *     but block the main thread while the paint thread paints. Async OMTP doesn't block
 *     the main thread. Sync OMTP is only meant to be used as a debugging tool.
 */
bool
ClientPaintedLayer::PaintOffMainThread()
{
  mContentClient->BeginAsyncPaint();

  uint32_t flags = GetPaintFlags();

  PaintState state = mContentClient->BeginPaintBuffer(this, flags);
  if (!UpdatePaintRegion(state)) {
    return false;
  }

  bool didUpdate = false;
  RotatedContentBuffer::DrawIterator iter;

  // Debug Protip: Change to BorrowDrawTargetForPainting if using sync OMTP.
  while (RefPtr<CapturedPaintState> captureState =
          mContentClient->BorrowDrawTargetForRecording(state, &iter))
  {
    DrawTarget* target = captureState->mTarget;
    if (!target || !target->IsValid()) {
      if (target) {
        mContentClient->ReturnDrawTargetToBuffer(target);
      }
      continue;
    }

    RefPtr<DrawTargetCapture> captureDT =
      Factory::CreateCaptureDrawTarget(target->GetBackendType(),
                                       target->GetSize(),
                                       target->GetFormat());

    captureDT->SetTransform(captureState->mTargetTransform);
    SetAntialiasingFlags(this, captureDT);

    RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(captureDT);
    MOZ_ASSERT(ctx); // already checked the target above

    ClientManager()->GetPaintedLayerCallback()(this,
                                              ctx,
                                              iter.mDrawRegion,
                                              iter.mDrawRegion,
                                              state.mClip,
                                              state.mRegionToInvalidate,
                                              ClientManager()->GetPaintedLayerCallbackData());

    ctx = nullptr;

    captureState->mCapture = captureDT.forget();
    PaintThread::Get()->PaintContents(captureState,
                                      RotatedContentBuffer::PrepareDrawTargetForPainting);

    mContentClient->ReturnDrawTargetToBuffer(target);

    didUpdate = true;
  }
  mContentClient->EndPaint(nullptr);

  if (didUpdate) {
    UpdateContentClient(state);
  }
  return true;
}