Bug 1653166 - Add rotation support to computed reference frames and use them for <video>. r=aosmond

Differential Revision: https://phabricator.services.mozilla.com/D85104
This commit is contained in:
Matt Woodrow 2020-08-04 01:15:04 +00:00
Родитель ba116b41d6
Коммит 72cda2d18d
15 изменённых файлов: 150 добавлений и 46 удалений

Просмотреть файл

@ -33,6 +33,7 @@
#include "nsDataHashtable.h"
#include "mozilla/EnumeratedArray.h"
#include "mozilla/UniquePtr.h"
#include "MediaInfo.h"
#ifndef XPCOM_GLUE_AVOID_NSPR
/**
@ -536,6 +537,10 @@ class ImageContainer final : public SupportsWeakPtr {
const gfx::Matrix& GetTransformHint() const { return mTransformHint; }
void SetRotation(VideoInfo::Rotation aRotation) { mRotation = aRotation; }
VideoInfo::Rotation GetRotation() const { return mRotation; }
void SetImageFactory(ImageFactory* aFactory) {
RecursiveMutexAutoLock lock(mRecursiveMutex);
mImageFactory = aFactory ? aFactory : new ImageFactory();
@ -660,6 +665,8 @@ class ImageContainer final : public SupportsWeakPtr {
gfx::Matrix mTransformHint;
VideoInfo::Rotation mRotation = VideoInfo::Rotation::kDegree_0;
RefPtr<BufferRecycleBin> mRecycleBin;
// This member points to an ImageClient if this ImageContainer was

Просмотреть файл

@ -29,6 +29,7 @@ using mozilla::LayoutDeviceRect from "Units.h";
using mozilla::LayoutDeviceSize from "Units.h";
using mozilla::ImageIntRect from "Units.h";
using mozilla::gfx::Rect from "mozilla/gfx/Rect.h";
using mozilla::VideoInfo::Rotation from "MediaInfo.h";
using class mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
@ -80,6 +81,7 @@ struct OpUpdateAsyncImagePipeline {
LayoutDeviceRect scBounds;
Matrix4x4 scTransform;
MaybeIntSize scaleToSize;
Rotation rotation;
ImageRendering filter;
MixBlendMode mixBlendMode;
LayoutDeviceSize scaleFromSize;

Просмотреть файл

@ -181,7 +181,8 @@ void AsyncImagePipelineManager::RemoveAsyncImagePipeline(
void AsyncImagePipelineManager::UpdateAsyncImagePipeline(
const wr::PipelineId& aPipelineId, const LayoutDeviceRect& aScBounds,
const gfx::Matrix4x4& aScTransform, const gfx::MaybeIntSize& aScaleToSize,
const wr::ImageRendering& aFilter, const wr::MixBlendMode& aMixBlendMode,
const VideoInfo::Rotation aRotation, const wr::ImageRendering& aFilter,
const wr::MixBlendMode& aMixBlendMode,
const LayoutDeviceSize& aScaleFromSize) {
if (mDestroyed) {
return;
@ -192,7 +193,7 @@ void AsyncImagePipelineManager::UpdateAsyncImagePipeline(
return;
}
pipeline->mInitialised = true;
pipeline->Update(aScBounds, aScTransform, aScaleToSize, aFilter,
pipeline->Update(aScBounds, aScTransform, aScaleToSize, aRotation, aFilter,
aMixBlendMode, aScaleFromSize);
}
@ -333,6 +334,20 @@ void AsyncImagePipelineManager::ApplyAsyncImagesOfImageBridge(
}
}
wr::WrRotation ToWrRotation(VideoInfo::Rotation aRotation) {
switch (aRotation) {
case VideoInfo::Rotation::kDegree_0:
return wr::WrRotation::Degree0;
case VideoInfo::Rotation::kDegree_90:
return wr::WrRotation::Degree90;
case VideoInfo::Rotation::kDegree_180:
return wr::WrRotation::Degree180;
case VideoInfo::Rotation::kDegree_270:
return wr::WrRotation::Degree270;
}
return wr::WrRotation::Degree0;
}
void AsyncImagePipelineManager::ApplyAsyncImageForPipeline(
const wr::Epoch& aEpoch, const wr::PipelineId& aPipelineId,
AsyncImagePipeline* aPipeline, wr::TransactionBuilder& aSceneBuilderTxn,
@ -372,11 +387,13 @@ void AsyncImagePipelineManager::ApplyAsyncImageForPipeline(
params.mix_blend_mode = aPipeline->mMixBlendMode;
wr::WrComputedTransformData computedTransform;
if (!aPipeline->mScaleFromSize.IsEmpty()) {
if (!aPipeline->mScaleFromSize.IsEmpty() ||
aPipeline->mRotation != VideoInfo::Rotation::kDegree_0) {
MOZ_ASSERT(scTransform.IsIdentity());
computedTransform.vertical_flip =
aPipeline->mCurrentTexture && aPipeline->mCurrentTexture->NeedsYFlip();
computedTransform.scale_from = wr::ToLayoutSize(aPipeline->mScaleFromSize);
computedTransform.rotation = ToWrRotation(aPipeline->mRotation);
params.computed_transform = &computedTransform;
} else {
if (aPipeline->mCurrentTexture &&

Просмотреть файл

@ -103,6 +103,7 @@ class AsyncImagePipelineManager final {
const LayoutDeviceRect& aScBounds,
const gfx::Matrix4x4& aScTransform,
const gfx::MaybeIntSize& aScaleToSize,
VideoInfo::Rotation aRotation,
const wr::ImageRendering& aFilter,
const wr::MixBlendMode& aMixBlendMode,
const LayoutDeviceSize& aScaleFromSize);
@ -182,16 +183,19 @@ class AsyncImagePipelineManager final {
void Update(const LayoutDeviceRect& aScBounds,
const gfx::Matrix4x4& aScTransform,
const gfx::MaybeIntSize& aScaleToSize,
VideoInfo::Rotation aRotation,
const wr::ImageRendering& aFilter,
const wr::MixBlendMode& aMixBlendMode,
const LayoutDeviceSize& aScaleFromSize) {
mIsChanged |=
!mScBounds.IsEqualEdges(aScBounds) || mScTransform != aScTransform ||
mScaleToSize != aScaleToSize || mFilter != aFilter ||
mMixBlendMode != aMixBlendMode || mScaleFromSize != aScaleFromSize;
mIsChanged |= !mScBounds.IsEqualEdges(aScBounds) ||
mScTransform != aScTransform ||
mScaleToSize != aScaleToSize || mRotation != aRotation ||
mFilter != aFilter || mMixBlendMode != aMixBlendMode ||
mScaleFromSize != aScaleFromSize;
mScBounds = aScBounds;
mScTransform = aScTransform;
mScaleToSize = aScaleToSize;
mRotation = aRotation;
mFilter = aFilter;
mMixBlendMode = aMixBlendMode;
mScaleFromSize = aScaleFromSize;
@ -204,6 +208,7 @@ class AsyncImagePipelineManager final {
LayoutDeviceSize mScaleFromSize;
gfx::Matrix4x4 mScTransform;
gfx::MaybeIntSize mScaleToSize;
VideoInfo::Rotation mRotation;
wr::ImageRendering mFilter;
wr::MixBlendMode mMixBlendMode;
RefPtr<WebRenderImageHost> mImageHost;

Просмотреть файл

@ -1353,7 +1353,7 @@ bool WebRenderBridgeParent::ProcessWebRenderParentCommands(
cmd.get_OpUpdateAsyncImagePipeline();
mAsyncImageManager->UpdateAsyncImagePipeline(
op.pipelineId(), op.scBounds(), op.scTransform(), op.scaleToSize(),
op.filter(), op.mixBlendMode(), op.scaleFromSize());
op.rotation(), op.filter(), op.mixBlendMode(), op.scaleFromSize());
mAsyncImageManager->ApplyAsyncImageForPipeline(op.pipelineId(), aTxn,
txnForImageBridge);
break;

Просмотреть файл

@ -1856,18 +1856,14 @@ Maybe<wr::ImageKey> WebRenderCommandBuilder::CreateImageKey(
LayoutDeviceRect rect = aAsyncImageBounds.value();
LayoutDeviceRect scBounds(LayoutDevicePoint(0, 0), rect.Size());
gfx::MaybeIntSize scaleToSize;
if (!aContainer->GetScaleHint().IsEmpty()) {
scaleToSize = Some(aContainer->GetScaleHint());
}
gfx::Matrix4x4 transform =
gfx::Matrix4x4::From2D(aContainer->GetTransformHint());
gfx::Matrix4x4 transform;
// TODO!
// We appear to be using the image bridge for a lot (most/all?) of
// layers-free image handling and that breaks frame consistency.
imageData->CreateAsyncImageWebRenderCommands(
aBuilder, aContainer, aSc, rect, scBounds, transform, scaleToSize,
aRendering, wr::MixBlendMode::Normal, !aItem->BackfaceIsHidden());
aBuilder, aContainer, aSc, rect, scBounds, transform,
gfx::MaybeIntSize(), aContainer->GetRotation(), aRendering,
wr::MixBlendMode::Normal, !aItem->BackfaceIsHidden());
return Nothing();
}

Просмотреть файл

@ -12,6 +12,7 @@
#include "ipc/IPCMessageUtils.h"
#include "mozilla/webrender/webrender_ffi.h"
#include "mozilla/webrender/WebRenderTypes.h"
#include "mozilla/dom/MediaIPCUtils.h"
namespace IPC {

Просмотреть файл

@ -232,8 +232,9 @@ void WebRenderImageData::CreateAsyncImageWebRenderCommands(
mozilla::wr::DisplayListBuilder& aBuilder, ImageContainer* aContainer,
const StackingContextHelper& aSc, const LayoutDeviceRect& aBounds,
const LayoutDeviceRect& aSCBounds, const gfx::Matrix4x4& aSCTransform,
const gfx::MaybeIntSize& aScaleToSize, const wr::ImageRendering& aFilter,
const wr::MixBlendMode& aMixBlendMode, bool aIsBackfaceVisible) {
const gfx::MaybeIntSize& aScaleToSize, VideoInfo::Rotation aRotation,
const wr::ImageRendering& aFilter, const wr::MixBlendMode& aMixBlendMode,
bool aIsBackfaceVisible) {
MOZ_ASSERT(aContainer->IsAsync());
if (mPipelineId.isSome() && mContainer != aContainer) {
@ -253,6 +254,14 @@ void WebRenderImageData::CreateAsyncImageWebRenderCommands(
}
MOZ_ASSERT(!mImageClient);
LayoutDeviceSize scaleFromSize;
AutoLockImage autoLock(aContainer);
if (autoLock.HasImage()) {
mozilla::layers::Image* image = autoLock.GetImage();
gfx::IntSize size = image->GetSize();
scaleFromSize = LayoutDeviceSize(size.width, size.height);
}
// Push IFrame for async image pipeline.
//
// We don't push a stacking context for this async image pipeline here.
@ -266,8 +275,8 @@ void WebRenderImageData::CreateAsyncImageWebRenderCommands(
/*ignoreMissingPipelines*/ false);
WrBridge()->AddWebRenderParentCommand(OpUpdateAsyncImagePipeline(
mPipelineId.value(), aSCBounds, aSCTransform, aScaleToSize, aFilter,
aMixBlendMode, LayoutDeviceSize()));
mPipelineId.value(), aSCBounds, aSCTransform, aScaleToSize, aRotation,
aFilter, aMixBlendMode, scaleFromSize));
}
void WebRenderImageData::CreateImageClientIfNeeded() {

Просмотреть файл

@ -158,8 +158,9 @@ class WebRenderImageData : public WebRenderUserData {
mozilla::wr::DisplayListBuilder& aBuilder, ImageContainer* aContainer,
const StackingContextHelper& aSc, const LayoutDeviceRect& aBounds,
const LayoutDeviceRect& aSCBounds, const gfx::Matrix4x4& aSCTransform,
const gfx::MaybeIntSize& aScaleToSize, const wr::ImageRendering& aFilter,
const wr::MixBlendMode& aMixBlendMode, bool aIsBackfaceVisible);
const gfx::MaybeIntSize& aScaleToSize, VideoInfo::Rotation aRotation,
const wr::ImageRendering& aFilter, const wr::MixBlendMode& aMixBlendMode,
bool aIsBackfaceVisible);
void CreateImageClientIfNeeded();

Просмотреть файл

@ -490,6 +490,7 @@ pub struct WrWindowId(u64);
pub struct WrComputedTransformData {
pub scale_from: LayoutSize,
pub vertical_flip: bool,
pub rotation: WrRotation,
}
fn get_proc_address(glcontext_ptr: *mut c_void, name: &str) -> *const c_void {
@ -2317,6 +2318,15 @@ pub enum WrReferenceFrameKind {
Zoom,
}
#[repr(u8)]
#[derive(PartialEq, Eq, Debug)]
pub enum WrRotation {
Degree0,
Degree90,
Degree180,
Degree270,
}
/// IMPORTANT: If you add fields to this struct, you need to also add initializers
/// for those fields in WebRenderAPI.h.
#[repr(C)]
@ -2444,11 +2454,18 @@ pub extern "C" fn wr_dp_push_stacking_context(
result.id = wr_spatial_id.0;
assert_ne!(wr_spatial_id.0, 0);
} else if let Some(data) = computed_ref {
let rotation = match data.rotation {
WrRotation::Degree0 => Rotation::Degree0,
WrRotation::Degree90 => Rotation::Degree90,
WrRotation::Degree180 => Rotation::Degree180,
WrRotation::Degree270 => Rotation::Degree270,
};
wr_spatial_id = state.frame_builder.dl_builder.push_computed_frame(
bounds.origin,
wr_spatial_id,
Some(data.scale_from),
data.vertical_flip,
rotation,
);
bounds.origin = LayoutPoint::zero();

Просмотреть файл

@ -11,7 +11,7 @@ use api::{LineOrientation, LineStyle, NinePatchBorderSource, PipelineId, MixBlen
use api::{PropertyBinding, ReferenceFrameKind, ScrollFrameDisplayItem, ScrollSensitivity};
use api::{Shadow, SpaceAndClipInfo, SpatialId, StickyFrameDisplayItem, ImageMask};
use api::{ClipMode, PrimitiveKeyKind, TransformStyle, YuvColorSpace, ColorRange, YuvData, TempFilterData};
use api::{ReferenceTransformBinding};
use api::{ReferenceTransformBinding, Rotation};
use api::image_tiling::simplify_repeated_primitive;
use api::units::*;
use crate::clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore, ClipItemKeyKind};
@ -482,14 +482,32 @@ impl<'a> SceneBuilder<'a> {
let transform = match info.reference_frame.transform {
ReferenceTransformBinding::Static { binding } => binding,
ReferenceTransformBinding::Computed { scale_from, vertical_flip } => {
ReferenceTransformBinding::Computed { scale_from, vertical_flip, rotation } => {
let content_size = &self.iframe_size.last().unwrap();
let mut transform = if let Some(scale_from) = scale_from {
let content_size = &self.iframe_size.last().unwrap();
LayoutTransform::create_scale(
content_size.width / scale_from.width,
content_size.height / scale_from.height,
1.0
)
// If we have a 90/270 degree rotation, then scale_from
// and content_size are in different coordinate spaces and
// we need to swap width/height for them to be correct.
match rotation {
Rotation::Degree0 |
Rotation::Degree180 => {
LayoutTransform::create_scale(
content_size.width / scale_from.width,
content_size.height / scale_from.height,
1.0
)
},
Rotation::Degree90 |
Rotation::Degree270 => {
LayoutTransform::create_scale(
content_size.height / scale_from.width,
content_size.width / scale_from.height,
1.0
)
}
}
} else {
LayoutTransform::identity()
};
@ -501,6 +519,9 @@ impl<'a> SceneBuilder<'a> {
.pre_scale(1.0, -1.0, 1.0);
}
let rotate = rotation.to_matrix(**content_size);
let transform = transform.post_transform(&rotate);
PropertyBinding::Value(transform)
},
};

Просмотреть файл

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use euclid::SideOffsets2D;
use euclid::{SideOffsets2D, Angle};
use peek_poke::PeekPoke;
use std::ops::Not;
// local imports
@ -721,6 +721,41 @@ pub enum ReferenceFrameKind {
}
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
pub enum Rotation {
Degree0,
Degree90,
Degree180,
Degree270,
}
impl Rotation {
pub fn to_matrix(
&self,
size: LayoutSize,
) -> LayoutTransform {
let (shift_center_to_origin, angle) = match self {
Rotation::Degree0 => {
(LayoutTransform::create_translation(-size.width / 2., -size.height / 2., 0.), Angle::degrees(0.))
},
Rotation::Degree90 => {
(LayoutTransform::create_translation(-size.height / 2., -size.width / 2., 0.), Angle::degrees(90.))
},
Rotation::Degree180 => {
(LayoutTransform::create_translation(-size.width / 2., -size.height / 2., 0.), Angle::degrees(180.))
},
Rotation::Degree270 => {
(LayoutTransform::create_translation(-size.height / 2., -size.width / 2., 0.), Angle::degrees(270.))
},
};
let shift_origin_to_center = LayoutTransform::create_translation(size.width / 2., size.height / 2., 0.);
LayoutTransform::create_rotation(0., 0., -1.0, angle)
.pre_transform(&shift_center_to_origin)
.post_transform(&shift_origin_to_center)
}
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
pub enum ReferenceTransformBinding {
/// Standard reference frame which contains a precomputed transform.
@ -733,6 +768,7 @@ pub enum ReferenceTransformBinding {
Computed {
scale_from: Option<LayoutSize>,
vertical_flip: bool,
rotation: Rotation,
},
}
@ -1598,6 +1634,7 @@ impl_default_for_enums! {
ClipMode => Clip,
ClipId => ClipId::invalid(),
ReferenceFrameKind => Transform,
Rotation => Degree0,
TransformStyle => Flat,
RasterSpace => Local(f32::default()),
MixBlendMode => Normal,

Просмотреть файл

@ -1545,6 +1545,7 @@ impl DisplayListBuilder {
parent_spatial_id: di::SpatialId,
scale_from: Option<LayoutSize>,
vertical_flip: bool,
rotation: di::Rotation,
) -> di::SpatialId {
let id = self.generate_spatial_index();
@ -1555,7 +1556,8 @@ impl DisplayListBuilder {
transform_style: di::TransformStyle::Flat,
transform: di::ReferenceTransformBinding::Computed {
scale_from,
vertical_flip
vertical_flip,
rotation,
},
kind: di::ReferenceFrameKind::Transform,
id,

Просмотреть файл

@ -174,7 +174,8 @@ class nsDisplayCanvas final : public nsPaintedDisplayItem {
aManager->WrBridge()->AddWebRenderParentCommand(
OpUpdateAsyncImagePipeline(
data->GetPipelineId().value(), scBounds, scTransform,
scaleToSize, filter, mixBlendMode,
scaleToSize, VideoInfo::Rotation::kDegree_0, filter,
mixBlendMode,
LayoutDeviceSize(canvasSizeInPx.width, canvasSizeInPx.height)));
break;
}

Просмотреть файл

@ -461,19 +461,7 @@ class nsDisplayVideo : public nsPaintedDisplayItem {
return true;
}
VideoInfo::Rotation rotationDeg = element->RotationDegrees();
IntSize scaleHint(static_cast<int32_t>(destGFXRect.Width()),
static_cast<int32_t>(destGFXRect.Height()));
// scaleHint is set regardless of rotation, so swap w/h if needed.
SwapScaleWidthHeightForRotation(scaleHint, rotationDeg);
container->SetScaleHint(scaleHint);
Matrix transformHint;
if (rotationDeg != VideoInfo::Rotation::kDegree_0) {
transformHint = ComputeRotationMatrix(destGFXRect.Width(),
destGFXRect.Height(), rotationDeg);
}
container->SetTransformHint(transformHint);
container->SetRotation(element->RotationDegrees());
// If the image container is empty, we don't want to fallback. Any other
// failure will be due to resource constraints and fallback is unlikely to