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(); }
/** * Determine if this transform layer should be drawn before another when they * are both preserve-3d children. * * We want to find the relative z depths of the 2 layers at points where they * intersect when projected onto the 2d screen plane. Intersections are defined * as corners that are positioned within the other quad, as well as intersections * of the lines. * * We then choose the intersection point with the greatest difference in Z * depths and use this point to determine an ordering for the two layers. * For layers that are intersecting in 3d space, this essentially guesses an * order. In a lot of cases we only intersect right at the edge point (3d cubes * in particular) and this generates the 'correct' looking ordering. For planes * that truely intersect, then there is no correct ordering and this remains * unsolved without changing our rendering code. */ static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) { gfxRect ourRect = aOne->GetEffectiveVisibleRegion().GetBounds(); gfxRect otherRect = aTwo->GetEffectiveVisibleRegion().GetBounds(); gfx3DMatrix ourTransform; To3DMatrix(aOne->GetTransform(), ourTransform); gfx3DMatrix otherTransform; To3DMatrix(aTwo->GetTransform(), otherTransform); // Transform both rectangles and project into 2d space. gfxQuad ourTransformedRect = ourTransform.TransformRect(ourRect); gfxQuad otherTransformedRect = otherTransform.TransformRect(otherRect); gfxRect ourBounds = ourTransformedRect.GetBounds(); gfxRect otherBounds = otherTransformedRect.GetBounds(); if (!ourBounds.Intersects(otherBounds)) { return Undefined; } // Make a list of all points that are within the other rect. // Could we just check Contains() on the bounds rects. ie, is it possible // for layers to overlap without intersections (in 2d space) and yet still // have their bounds rects not completely enclose each other? nsTArray<gfxPoint> points; for (uint32_t i = 0; i < 4; i++) { if (ourTransformedRect.Contains(otherTransformedRect.mPoints[i])) { points.AppendElement(otherTransformedRect.mPoints[i]); } if (otherTransformedRect.Contains(ourTransformedRect.mPoints[i])) { points.AppendElement(ourTransformedRect.mPoints[i]); } } // Look for intersections between lines (in 2d space) and use these as // depth testing points. for (uint32_t i = 0; i < 4; i++) { for (uint32_t j = 0; j < 4; j++) { gfxPoint intersection; gfxLineSegment one(ourTransformedRect.mPoints[i], ourTransformedRect.mPoints[(i + 1) % 4]); gfxLineSegment two(otherTransformedRect.mPoints[j], otherTransformedRect.mPoints[(j + 1) % 4]); if (one.Intersects(two, intersection)) { points.AppendElement(intersection); } } } // No intersections, no defined order between these layers. if (points.IsEmpty()) { return Undefined; } // Find the relative Z depths of each intersection point and check that the layers are in the same order. gfxFloat highest = 0; for (uint32_t i = 0; i < points.Length(); i++) { gfxFloat ourDepth = RecoverZDepth(ourTransform, points.ElementAt(i)); gfxFloat otherDepth = RecoverZDepth(otherTransform, points.ElementAt(i)); gfxFloat difference = otherDepth - ourDepth; if (fabs(difference) > fabs(highest)) { highest = difference; } } // If layers have the same depth keep the original order if (fabs(highest) < 0.1 || highest >= 0) { return ABeforeB; } else { return BBeforeA; } }