diff options
Diffstat (limited to 'chromium/components/viz/service/display/overlay_candidate.cc')
-rw-r--r-- | chromium/components/viz/service/display/overlay_candidate.cc | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/chromium/components/viz/service/display/overlay_candidate.cc b/chromium/components/viz/service/display/overlay_candidate.cc new file mode 100644 index 00000000000..2cac5568008 --- /dev/null +++ b/chromium/components/viz/service/display/overlay_candidate.cc @@ -0,0 +1,397 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/viz/service/display/overlay_candidate.h" + +#include <algorithm> +#include <limits> + +#include "base/logging.h" +#include "build/build_config.h" +#include "cc/base/math_util.h" +#include "components/viz/common/quads/solid_color_draw_quad.h" +#include "components/viz/common/quads/stream_video_draw_quad.h" +#include "components/viz/common/quads/texture_draw_quad.h" +#include "components/viz/common/quads/tile_draw_quad.h" +#include "components/viz/service/display/display_resource_provider.h" +#include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gfx/geometry/vector3d_f.h" + +namespace viz { + +namespace { +// Tolerance for considering axis vector elements to be zero. +const SkMScalar kEpsilon = std::numeric_limits<float>::epsilon(); + +const gfx::BufferFormat kOverlayFormats[] = { + gfx::BufferFormat::RGBX_8888, gfx::BufferFormat::RGBA_8888, + gfx::BufferFormat::BGRX_8888, gfx::BufferFormat::BGRA_8888, + gfx::BufferFormat::BGR_565, gfx::BufferFormat::YUV_420_BIPLANAR}; + +enum Axis { NONE, AXIS_POS_X, AXIS_NEG_X, AXIS_POS_Y, AXIS_NEG_Y }; + +Axis VectorToAxis(const gfx::Vector3dF& vec) { + if (std::abs(vec.z()) > kEpsilon) + return NONE; + const bool x_zero = (std::abs(vec.x()) <= kEpsilon); + const bool y_zero = (std::abs(vec.y()) <= kEpsilon); + if (x_zero && !y_zero) + return (vec.y() > 0) ? AXIS_POS_Y : AXIS_NEG_Y; + else if (y_zero && !x_zero) + return (vec.x() > 0) ? AXIS_POS_X : AXIS_NEG_X; + else + return NONE; +} + +gfx::OverlayTransform GetOverlayTransform(const gfx::Transform& quad_transform, + bool y_flipped) { + if (!quad_transform.Preserves2dAxisAlignment()) { + return gfx::OVERLAY_TRANSFORM_INVALID; + } + + gfx::Vector3dF x_axis = cc::MathUtil::GetXAxis(quad_transform); + gfx::Vector3dF y_axis = cc::MathUtil::GetYAxis(quad_transform); + if (y_flipped) { + y_axis.Scale(-1); + } + + Axis x_to = VectorToAxis(x_axis); + Axis y_to = VectorToAxis(y_axis); + + if (x_to == AXIS_POS_X && y_to == AXIS_POS_Y) + return gfx::OVERLAY_TRANSFORM_NONE; + else if (x_to == AXIS_NEG_X && y_to == AXIS_POS_Y) + return gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL; + else if (x_to == AXIS_POS_X && y_to == AXIS_NEG_Y) + return gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL; + else if (x_to == AXIS_NEG_Y && y_to == AXIS_POS_X) + return gfx::OVERLAY_TRANSFORM_ROTATE_270; + else if (x_to == AXIS_NEG_X && y_to == AXIS_NEG_Y) + return gfx::OVERLAY_TRANSFORM_ROTATE_180; + else if (x_to == AXIS_POS_Y && y_to == AXIS_NEG_X) + return gfx::OVERLAY_TRANSFORM_ROTATE_90; + else + return gfx::OVERLAY_TRANSFORM_INVALID; +} + +// Apply transform |delta| to |in| and return the resulting transform, +// or OVERLAY_TRANSFORM_INVALID. +gfx::OverlayTransform ComposeTransforms(gfx::OverlayTransform delta, + gfx::OverlayTransform in) { + // There are 8 different possible transforms. We can characterize these + // by looking at where the origin moves and the direction the horizontal goes. + // (TL=top-left, BR=bottom-right, H=horizontal, V=vertical). + // NONE: TL, H + // FLIP_VERTICAL: BL, H + // FLIP_HORIZONTAL: TR, H + // ROTATE_90: TR, V + // ROTATE_180: BR, H + // ROTATE_270: BL, V + // Missing transforms: TL, V & BR, V + // Basic combinations: + // Flip X & Y -> Rotate 180 (TL,H -> TR,H -> BR,H or TL,H -> BL,H -> BR,H) + // Flip X or Y + Rotate 180 -> other flip (eg, TL,H -> TR,H -> BL,H) + // Rotate + Rotate simply adds values. + // Rotate 90/270 + flip is invalid because we can only have verticals with + // the origin in TR or BL. + if (delta == gfx::OVERLAY_TRANSFORM_NONE) + return in; + switch (in) { + case gfx::OVERLAY_TRANSFORM_NONE: + return delta; + case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL: + switch (delta) { + case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL: + return gfx::OVERLAY_TRANSFORM_NONE; + case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL: + return gfx::OVERLAY_TRANSFORM_ROTATE_180; + case gfx::OVERLAY_TRANSFORM_ROTATE_180: + return gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL; + default: + return gfx::OVERLAY_TRANSFORM_INVALID; + } + break; + case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL: + switch (delta) { + case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL: + return gfx::OVERLAY_TRANSFORM_NONE; + case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL: + return gfx::OVERLAY_TRANSFORM_ROTATE_180; + case gfx::OVERLAY_TRANSFORM_ROTATE_90: + case gfx::OVERLAY_TRANSFORM_ROTATE_180: + return gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL; + case gfx::OVERLAY_TRANSFORM_ROTATE_270: + default: + return gfx::OVERLAY_TRANSFORM_INVALID; + } + break; + case gfx::OVERLAY_TRANSFORM_ROTATE_90: + switch (delta) { + case gfx::OVERLAY_TRANSFORM_ROTATE_90: + return gfx::OVERLAY_TRANSFORM_ROTATE_180; + case gfx::OVERLAY_TRANSFORM_ROTATE_180: + return gfx::OVERLAY_TRANSFORM_ROTATE_270; + case gfx::OVERLAY_TRANSFORM_ROTATE_270: + return gfx::OVERLAY_TRANSFORM_NONE; + default: + return gfx::OVERLAY_TRANSFORM_INVALID; + } + break; + case gfx::OVERLAY_TRANSFORM_ROTATE_180: + switch (delta) { + case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL: + return gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL; + case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL: + return gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL; + case gfx::OVERLAY_TRANSFORM_ROTATE_90: + return gfx::OVERLAY_TRANSFORM_ROTATE_270; + case gfx::OVERLAY_TRANSFORM_ROTATE_180: + return gfx::OVERLAY_TRANSFORM_NONE; + case gfx::OVERLAY_TRANSFORM_ROTATE_270: + return gfx::OVERLAY_TRANSFORM_ROTATE_90; + default: + return gfx::OVERLAY_TRANSFORM_INVALID; + } + break; + case gfx::OVERLAY_TRANSFORM_ROTATE_270: + switch (delta) { + case gfx::OVERLAY_TRANSFORM_ROTATE_90: + return gfx::OVERLAY_TRANSFORM_NONE; + case gfx::OVERLAY_TRANSFORM_ROTATE_180: + return gfx::OVERLAY_TRANSFORM_ROTATE_90; + case gfx::OVERLAY_TRANSFORM_ROTATE_270: + return gfx::OVERLAY_TRANSFORM_ROTATE_180; + default: + return gfx::OVERLAY_TRANSFORM_INVALID; + } + break; + default: + return gfx::OVERLAY_TRANSFORM_INVALID; + } +} + +} // namespace + +OverlayCandidate::OverlayCandidate() + : transform(gfx::OVERLAY_TRANSFORM_NONE), + format(gfx::BufferFormat::RGBA_8888), + uv_rect(0.f, 0.f, 1.f, 1.f), + is_clipped(false), + is_opaque(false), + use_output_surface_for_resource(false), + resource_id(0), +#if defined(OS_ANDROID) + is_backed_by_surface_texture(false), + is_promotable_hint(false), +#endif + plane_z_order(0), + is_unoccluded(false), + overlay_handled(false), + gpu_fence_id(0) { +} + +OverlayCandidate::OverlayCandidate(const OverlayCandidate& other) = default; + +OverlayCandidate::~OverlayCandidate() = default; + +// static +bool OverlayCandidate::FromDrawQuad(DisplayResourceProvider* resource_provider, + const SkMatrix44& output_color_matrix, + const DrawQuad* quad, + OverlayCandidate* candidate) { + // It is currently not possible to set a color conversion matrix on an HW + // overlay plane. + // TODO(https://crbug.com/792757): Remove this check once the bug is resolved. + if (!output_color_matrix.isIdentity()) + return false; + + // We don't support an opacity value different than one for an overlay plane. + if (quad->shared_quad_state->opacity != 1.f) + return false; + // We support only kSrc (no blending) and kSrcOver (blending with premul). + if (!(quad->shared_quad_state->blend_mode == SkBlendMode::kSrc || + quad->shared_quad_state->blend_mode == SkBlendMode::kSrcOver)) + return false; + + switch (quad->material) { + case DrawQuad::TEXTURE_CONTENT: + return FromTextureQuad(resource_provider, + TextureDrawQuad::MaterialCast(quad), candidate); + case DrawQuad::TILED_CONTENT: + return FromTileQuad(resource_provider, TileDrawQuad::MaterialCast(quad), + candidate); + case DrawQuad::STREAM_VIDEO_CONTENT: + return FromStreamVideoQuad(resource_provider, + StreamVideoDrawQuad::MaterialCast(quad), + candidate); + default: + break; + } + + return false; +} + +// static +bool OverlayCandidate::IsInvisibleQuad(const DrawQuad* quad) { + float opacity = quad->shared_quad_state->opacity; + if (opacity < std::numeric_limits<float>::epsilon()) + return true; + if (quad->material == DrawQuad::SOLID_COLOR) { + SkColor color = SolidColorDrawQuad::MaterialCast(quad)->color; + float alpha = (SkColorGetA(color) * (1.0f / 255.0f)) * opacity; + return quad->ShouldDrawWithBlending() && + alpha < std::numeric_limits<float>::epsilon(); + } + return false; +} + +// static +bool OverlayCandidate::IsOccluded(const OverlayCandidate& candidate, + QuadList::ConstIterator quad_list_begin, + QuadList::ConstIterator quad_list_end) { + // Check that no visible quad overlaps the candidate. + for (auto overlap_iter = quad_list_begin; overlap_iter != quad_list_end; + ++overlap_iter) { + gfx::RectF overlap_rect = cc::MathUtil::MapClippedRect( + overlap_iter->shared_quad_state->quad_to_target_transform, + gfx::RectF(overlap_iter->rect)); + if (candidate.display_rect.Intersects(overlap_rect) && + !OverlayCandidate::IsInvisibleQuad(*overlap_iter)) + return true; + } + return false; +} + +// static +bool OverlayCandidate::FromDrawQuadResource( + DisplayResourceProvider* resource_provider, + const DrawQuad* quad, + ResourceId resource_id, + bool y_flipped, + OverlayCandidate* candidate) { + if (!resource_provider->IsOverlayCandidate(resource_id)) + return false; + + candidate->format = resource_provider->GetBufferFormat(resource_id); + if (std::find(std::begin(kOverlayFormats), std::end(kOverlayFormats), + candidate->format) == std::end(kOverlayFormats)) + return false; + + gfx::OverlayTransform overlay_transform = GetOverlayTransform( + quad->shared_quad_state->quad_to_target_transform, y_flipped); + if (overlay_transform == gfx::OVERLAY_TRANSFORM_INVALID) + return false; + + auto& transform = quad->shared_quad_state->quad_to_target_transform; + candidate->display_rect = gfx::RectF(quad->rect); + transform.TransformRect(&candidate->display_rect); + + candidate->clip_rect = quad->shared_quad_state->clip_rect; + candidate->is_clipped = quad->shared_quad_state->is_clipped; + candidate->is_opaque = !quad->ShouldDrawWithBlending(); + + candidate->resource_id = resource_id; + candidate->transform = overlay_transform; + + return true; +} + +// static +bool OverlayCandidate::FromTextureQuad( + DisplayResourceProvider* resource_provider, + const TextureDrawQuad* quad, + OverlayCandidate* candidate) { + if (quad->background_color != SK_ColorTRANSPARENT) + return false; + if (!FromDrawQuadResource(resource_provider, quad, quad->resource_id(), + quad->y_flipped, candidate)) { + return false; + } + candidate->resource_size_in_pixels = quad->resource_size_in_pixels(); + candidate->uv_rect = BoundingRect(quad->uv_top_left, quad->uv_bottom_right); + return true; +} + +// static +bool OverlayCandidate::FromTileQuad(DisplayResourceProvider* resource_provider, + const TileDrawQuad* quad, + OverlayCandidate* candidate) { + if (!FromDrawQuadResource(resource_provider, quad, quad->resource_id(), false, + candidate)) { + return false; + } + candidate->resource_size_in_pixels = quad->texture_size; + candidate->uv_rect = quad->tex_coord_rect; + return true; +} + +// static +bool OverlayCandidate::FromStreamVideoQuad( + DisplayResourceProvider* resource_provider, + const StreamVideoDrawQuad* quad, + OverlayCandidate* candidate) { + if (!FromDrawQuadResource(resource_provider, quad, quad->resource_id(), false, + candidate)) { + return false; + } + if (!quad->matrix.IsScaleOrTranslation()) { + // We cannot handle anything other than scaling & translation for texture + // coordinates yet. + return false; + } + candidate->resource_id = quad->resource_id(); + candidate->resource_size_in_pixels = quad->resource_size_in_pixels(); +#if defined(OS_ANDROID) + candidate->is_backed_by_surface_texture = + resource_provider->IsBackedBySurfaceTexture(quad->resource_id()); +#endif + + gfx::Point3F uv0 = gfx::Point3F(0, 0, 0); + gfx::Point3F uv1 = gfx::Point3F(1, 1, 0); + quad->matrix.TransformPoint(&uv0); + quad->matrix.TransformPoint(&uv1); + gfx::Vector3dF delta = uv1 - uv0; + if (delta.x() < 0) { + candidate->transform = ComposeTransforms( + gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL, candidate->transform); + float x0 = uv0.x(); + uv0.set_x(uv1.x()); + uv1.set_x(x0); + delta.set_x(-delta.x()); + } + + if (delta.y() < 0) { + // In this situation, uv0y < uv1y. Since we overlay inverted, a request + // to invert the source texture means we can just output the texture + // normally and it will be correct. + candidate->uv_rect = gfx::RectF(uv0.x(), uv1.y(), delta.x(), -delta.y()); + } else { + candidate->transform = ComposeTransforms( + gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL, candidate->transform); + candidate->uv_rect = gfx::RectF(uv0.x(), uv0.y(), delta.x(), delta.y()); + } + return true; +} + +OverlayCandidateList::OverlayCandidateList() = default; + +OverlayCandidateList::OverlayCandidateList(const OverlayCandidateList& other) = + default; + +OverlayCandidateList::OverlayCandidateList(OverlayCandidateList&& other) = + default; + +OverlayCandidateList::~OverlayCandidateList() = default; + +OverlayCandidateList& OverlayCandidateList::operator=( + const OverlayCandidateList& other) = default; + +OverlayCandidateList& OverlayCandidateList::operator=( + OverlayCandidateList&& other) = default; + +void OverlayCandidateList::AddPromotionHint(const OverlayCandidate& candidate) { + promotion_hint_info_map_[candidate.resource_id] = candidate.display_rect; +} + +} // namespace viz |