/* static */ bool HwcUtils::PrepareLayerRects(nsIntRect aVisible, const gfx::Matrix& aLayerTransform, const gfx::Matrix& aLayerBufferTransform, nsIntRect aClip, nsIntRect aBufferRect, bool aYFlipped, hwc_rect_t* aSourceCrop, hwc_rect_t* aVisibleRegionScreen) { gfxMatrix aTransform = gfx::ThebesMatrix(aLayerTransform); gfxRect visibleRect(ThebesRect(aVisible)); gfxRect clip(ThebesRect(aClip)); gfxRect visibleRectScreen = aTransform.TransformBounds(visibleRect); // |clip| is guaranteed to be integer visibleRectScreen.IntersectRect(visibleRectScreen, clip); if (visibleRectScreen.IsEmpty()) { return false; } gfxMatrix inverse = gfx::ThebesMatrix(aLayerBufferTransform); inverse.Invert(); gfxRect crop = inverse.TransformBounds(visibleRectScreen); //clip to buffer size crop.IntersectRect(crop, ThebesRect(aBufferRect)); crop.Round(); if (crop.IsEmpty()) { return false; } //propagate buffer clipping back to visible rect gfxMatrix layerBufferTransform = gfx::ThebesMatrix(aLayerBufferTransform); visibleRectScreen = layerBufferTransform.TransformBounds(crop); visibleRectScreen.Round(); // Map from layer space to buffer space crop -= aBufferRect.TopLeft(); if (aYFlipped) { crop.y = aBufferRect.height - (crop.y + crop.height); } aSourceCrop->left = crop.x; aSourceCrop->top = crop.y; aSourceCrop->right = crop.x + crop.width; aSourceCrop->bottom = crop.y + crop.height; aVisibleRegionScreen->left = visibleRectScreen.x; aVisibleRegionScreen->top = visibleRectScreen.y; aVisibleRegionScreen->right = visibleRectScreen.x + visibleRectScreen.width; aVisibleRegionScreen->bottom = visibleRectScreen.y + visibleRectScreen.height; return true; }
/* static */ bool HwcUtils::CalculateClipRect(const gfx::Matrix& transform, const nsIntRect* aLayerClip, nsIntRect aParentClip, nsIntRect* aRenderClip) { gfxMatrix aTransform = gfx::ThebesMatrix(transform); *aRenderClip = aParentClip; if (!aLayerClip) { return true; } if (aLayerClip->IsEmpty()) { return false; } nsIntRect clip = *aLayerClip; gfxRect r = ThebesRect(clip); gfxRect trClip = aTransform.TransformBounds(r); trClip.Round(); gfxUtils::GfxRectToIntRect(trClip, &clip); aRenderClip->IntersectRect(*aRenderClip, clip); return true; }
/* static */ bool HwcUtils::PrepareVisibleRegion(const nsIntRegion& aVisible, const gfx::Matrix& aLayerTransform, const gfx::Matrix& aLayerBufferTransform, nsIntRect aClip, nsIntRect aBufferRect, RectVector* aVisibleRegionScreen, bool& aIsVisible) { const float MIN_SRC_WIDTH = 2.f; const float MIN_SRC_HEIGHT = 2.f; gfxMatrix layerTransform = gfx::ThebesMatrix(aLayerTransform); gfxMatrix layerBufferTransform = gfx::ThebesMatrix(aLayerBufferTransform); gfxRect bufferRect = layerBufferTransform.TransformBounds(ThebesRect(aBufferRect)); gfxMatrix inverse = gfx::ThebesMatrix(aLayerBufferTransform); inverse.Invert(); nsIntRegionRectIterator rect(aVisible); aIsVisible = false; while (const nsIntRect* visibleRect = rect.Next()) { hwc_rect_t visibleRectScreen; gfxRect screenRect; screenRect = layerTransform.TransformBounds(ThebesRect(*visibleRect)); screenRect.IntersectRect(screenRect, bufferRect); screenRect.IntersectRect(screenRect, ThebesRect(aClip)); screenRect.Round(); if (screenRect.IsEmpty()) { continue; } visibleRectScreen.left = screenRect.x; visibleRectScreen.top = screenRect.y; visibleRectScreen.right = screenRect.XMost(); visibleRectScreen.bottom = screenRect.YMost(); gfxRect srcCrop = inverse.TransformBounds(screenRect); // When src crop is very small, HWC could not render correctly in some cases. // See Bug 1169093 if(srcCrop.Width() < MIN_SRC_WIDTH || srcCrop.Height() < MIN_SRC_HEIGHT) { return false; } aVisibleRegionScreen->push_back(visibleRectScreen); aIsVisible = true; } return true; }
nsIntRect Layer::CalculateScissorRect(const nsIntRect& aCurrentScissorRect, const gfx::Matrix* aWorldTransform) { ContainerLayer* container = GetParent(); NS_ASSERTION(container, "This can't be called on the root!"); // Establish initial clip rect: it's either the one passed in, or // if the parent has an intermediate surface, it's the extents of that surface. nsIntRect currentClip; if (container->UseIntermediateSurface()) { currentClip.SizeTo(container->GetIntermediateSurfaceRect().Size()); } else { currentClip = aCurrentScissorRect; } const nsIntRect *clipRect = GetEffectiveClipRect(); if (!clipRect) return currentClip; if (clipRect->IsEmpty()) { // We might have a non-translation transform in the container so we can't // use the code path below. return nsIntRect(currentClip.TopLeft(), nsIntSize(0, 0)); } nsIntRect scissor = *clipRect; if (!container->UseIntermediateSurface()) { gfx::Matrix matrix; DebugOnly<bool> is2D = container->GetEffectiveTransform().Is2D(&matrix); // See DefaultComputeEffectiveTransforms below NS_ASSERTION(is2D && matrix.PreservesAxisAlignedRectangles(), "Non preserves axis aligned transform with clipped child should have forced intermediate surface"); gfx::Rect r(scissor.x, scissor.y, scissor.width, scissor.height); gfxRect trScissor = gfx::ThebesRect(matrix.TransformBounds(r)); trScissor.Round(); if (!gfxUtils::GfxRectToIntRect(trScissor, &scissor)) { return nsIntRect(currentClip.TopLeft(), nsIntSize(0, 0)); } // Find the nearest ancestor with an intermediate surface do { container = container->GetParent(); } while (container && !container->UseIntermediateSurface()); } if (container) { scissor.MoveBy(-container->GetIntermediateSurfaceRect().TopLeft()); } else if (aWorldTransform) { gfx::Rect r(scissor.x, scissor.y, scissor.width, scissor.height); gfx::Rect trScissor = aWorldTransform->TransformBounds(r); trScissor.Round(); if (!gfxUtils::GfxRectToIntRect(ThebesRect(trScissor), &scissor)) return nsIntRect(currentClip.TopLeft(), nsIntSize(0, 0)); } return currentClip.Intersect(scissor); }
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; }
static mozilla::gfx::IntRect TransformRect(const mozilla::gfx::IntRect& aRect, const mozilla::gfx::Matrix4x4& aTransform) { if (aRect.IsEmpty()) { return mozilla::gfx::IntRect(); } mozilla::gfx::RectDouble rect(aRect.x, aRect.y, aRect.width, aRect.height); rect = aTransform.TransformAndClipBounds(rect, mozilla::gfx::RectDouble::MaxIntRect()); rect.RoundOut(); mozilla::gfx::IntRect intRect; if (!gfxUtils::GfxRectToIntRect(ThebesRect(rect), &intRect)) { return mozilla::gfx::IntRect(); } return intRect; }
void BasicCompositor::DrawQuad(const gfx::Rect& aRect, const gfx::Rect& aClipRect, const EffectChain &aEffectChain, gfx::Float aOpacity, const gfx::Matrix4x4& aTransform, const gfx::Rect& aVisibleRect) { RefPtr<DrawTarget> buffer = mRenderTarget->mDrawTarget; // For 2D drawing, |dest| and |buffer| are the same surface. For 3D drawing, // |dest| is a temporary surface. RefPtr<DrawTarget> dest = buffer; buffer->PushClipRect(aClipRect); AutoRestoreTransform autoRestoreTransform(dest); Matrix newTransform; Rect transformBounds; gfx3DMatrix new3DTransform; IntPoint offset = mRenderTarget->GetOrigin(); if (aTransform.Is2D()) { newTransform = aTransform.As2D(); } else { // Create a temporary surface for the transform. dest = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(RoundOut(aRect).Size(), SurfaceFormat::B8G8R8A8); if (!dest) { return; } dest->SetTransform(Matrix::Translation(-aRect.x, -aRect.y)); // Get the bounds post-transform. new3DTransform = To3DMatrix(aTransform); gfxRect bounds = new3DTransform.TransformBounds(ThebesRect(aRect)); bounds.IntersectRect(bounds, gfxRect(offset.x, offset.y, buffer->GetSize().width, buffer->GetSize().height)); transformBounds = ToRect(bounds); transformBounds.RoundOut(); // Propagate the coordinate offset to our 2D draw target. newTransform = Matrix::Translation(transformBounds.x, transformBounds.y); // When we apply the 3D transformation, we do it against a temporary // surface, so undo the coordinate offset. new3DTransform = gfx3DMatrix::Translation(aRect.x, aRect.y, 0) * new3DTransform; } newTransform.PostTranslate(-offset.x, -offset.y); buffer->SetTransform(newTransform); RefPtr<SourceSurface> sourceMask; Matrix maskTransform; if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) { EffectMask *effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get()); sourceMask = effectMask->mMaskTexture->AsSourceBasic()->GetSurface(dest); MOZ_ASSERT(effectMask->mMaskTransform.Is2D(), "How did we end up with a 3D transform here?!"); MOZ_ASSERT(!effectMask->mIs3D); maskTransform = effectMask->mMaskTransform.As2D(); maskTransform.PreTranslate(-offset.x, -offset.y); } switch (aEffectChain.mPrimaryEffect->mType) { case EffectTypes::SOLID_COLOR: { EffectSolidColor* effectSolidColor = static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get()); FillRectWithMask(dest, aRect, effectSolidColor->mColor, DrawOptions(aOpacity), sourceMask, &maskTransform); break; } case EffectTypes::RGB: { TexturedEffect* texturedEffect = static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get()); TextureSourceBasic* source = texturedEffect->mTexture->AsSourceBasic(); if (texturedEffect->mPremultiplied) { DrawSurfaceWithTextureCoords(dest, aRect, source->GetSurface(dest), texturedEffect->mTextureCoords, texturedEffect->mFilter, aOpacity, sourceMask, &maskTransform); } else { RefPtr<DataSourceSurface> srcData = source->GetSurface(dest)->GetDataSurface(); // Yes, we re-create the premultiplied data every time. // This might be better with a cache, eventually. RefPtr<DataSourceSurface> premultData = gfxUtils::CreatePremultipliedDataSurface(srcData); DrawSurfaceWithTextureCoords(dest, aRect, premultData, texturedEffect->mTextureCoords, texturedEffect->mFilter, aOpacity, sourceMask, &maskTransform); } break; } case EffectTypes::YCBCR: { NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!"); break; } case EffectTypes::RENDER_TARGET: { EffectRenderTarget* effectRenderTarget = static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get()); RefPtr<BasicCompositingRenderTarget> surface = static_cast<BasicCompositingRenderTarget*>(effectRenderTarget->mRenderTarget.get()); RefPtr<SourceSurface> sourceSurf = surface->mDrawTarget->Snapshot(); DrawSurfaceWithTextureCoords(dest, aRect, sourceSurf, effectRenderTarget->mTextureCoords, effectRenderTarget->mFilter, aOpacity, sourceMask, &maskTransform); break; } case EffectTypes::COMPONENT_ALPHA: { NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!"); break; } default: { NS_RUNTIMEABORT("Invalid effect type!"); break; } } if (!aTransform.Is2D()) { dest->Flush(); RefPtr<SourceSurface> snapshot = dest->Snapshot(); RefPtr<DataSourceSurface> source = snapshot->GetDataSurface(); RefPtr<DataSourceSurface> temp = Factory::CreateDataSourceSurface(RoundOut(transformBounds).Size(), SurfaceFormat::B8G8R8A8 #ifdef MOZ_ENABLE_SKIA , true #endif ); if (NS_WARN_IF(!temp)) { buffer->PopClip(); return; } Transform(temp, source, new3DTransform, transformBounds.TopLeft()); transformBounds.MoveTo(0, 0); buffer->DrawSurface(temp, transformBounds, transformBounds); } buffer->PopClip(); }
nsresult imgFrame::InitWithDrawable(gfxDrawable* aDrawable, const nsIntSize& aSize, const SurfaceFormat aFormat, Filter aFilter, uint32_t aImageFlags) { // Assert for properties that should be verified by decoders, // warn for properties related to bad content. if (!AllowedImageSize(aSize.width, aSize.height)) { NS_WARNING("Should have legal image size"); mAborted = true; return NS_ERROR_FAILURE; } mImageSize = aSize; mOffset.MoveTo(0, 0); mSize.SizeTo(aSize.width, aSize.height); mFormat = aFormat; mPaletteDepth = 0; RefPtr<DrawTarget> target; bool canUseDataSurface = gfxPlatform::GetPlatform()->CanRenderContentToDataSurface(); if (canUseDataSurface) { // It's safe to use data surfaces for content on this platform, so we can // get away with using volatile buffers. MOZ_ASSERT(!mImageSurface, "Called imgFrame::InitWithDrawable() twice?"); mVBuf = AllocateBufferForImage(mSize, mFormat); if (!mVBuf) { mAborted = true; return NS_ERROR_OUT_OF_MEMORY; } int32_t stride = VolatileSurfaceStride(mSize, mFormat); VolatileBufferPtr<uint8_t> ptr(mVBuf); if (!ptr) { mAborted = true; return NS_ERROR_OUT_OF_MEMORY; } if (mVBuf->OnHeap()) { memset(ptr, 0, stride * mSize.height); } mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat); target = gfxPlatform::GetPlatform()-> CreateDrawTargetForData(ptr, mSize, stride, mFormat); } else { // We can't use data surfaces for content, so we'll create an offscreen // surface instead. This means if someone later calls RawAccessRef(), we // may have to do an expensive readback, but we warned callers about that in // the documentation for this method. MOZ_ASSERT(!mOptSurface, "Called imgFrame::InitWithDrawable() twice?"); target = gfxPlatform::GetPlatform()-> CreateOffscreenContentDrawTarget(mSize, mFormat); } if (!target) { mAborted = true; return NS_ERROR_OUT_OF_MEMORY; } // Draw using the drawable the caller provided. nsIntRect imageRect(0, 0, mSize.width, mSize.height); RefPtr<gfxContext> ctx = new gfxContext(target); gfxUtils::DrawPixelSnapped(ctx, aDrawable, mSize, ImageRegion::Create(ThebesRect(imageRect)), mFormat, aFilter, aImageFlags); if (canUseDataSurface && !mImageSurface) { NS_WARNING("Failed to create VolatileDataSourceSurface"); mAborted = true; return NS_ERROR_OUT_OF_MEMORY; } if (!canUseDataSurface) { // We used an offscreen surface, which is an "optimized" surface from // imgFrame's perspective. mOptSurface = target->Snapshot(); } // If we reach this point, we should regard ourselves as complete. mDecoded = GetRect(); MOZ_ASSERT(IsImageComplete()); return NS_OK; }