| // Copyright 2016 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/renderer/rasterizer/common/offscreen_render_coordinate_mapping.h" |
| |
| #include "cobalt/math/transform_2d.h" |
| |
| namespace cobalt { |
| namespace renderer { |
| namespace rasterizer { |
| namespace common { |
| |
| using math::Matrix3F; |
| using math::Rect; |
| using math::RectF; |
| using math::Vector2dF; |
| using math::Vector3dF; |
| using math::TranslateMatrix; |
| using math::ScaleMatrix; |
| |
| namespace { |
| float GetFractionalComponent(float value) { return value - std::floor(value); } |
| } // namespace |
| |
| OffscreenRenderCoordinateMapping GetOffscreenRenderCoordinateMapping( |
| const RectF& local_bounds, const Matrix3F& transform, |
| const base::optional<Rect>& viewport_bounds) { |
| bool has_rotation_or_skew = |
| transform.Get(0, 1) != 0 || transform.Get(1, 0) != 0; |
| |
| // We decide at this point whether to attempt to align the fractional value of |
| // our content's translation within the offscreen surface such that it would |
| // be equal to what it would be in the outer surface. If we are rotating |
| // or skewing, it is not useful to do this so we don't bother in that case. |
| bool align_translate = !has_rotation_or_skew; |
| |
| // We extract the scale from the transform and let that affect the size of our |
| // output bounding box so that our offscreen render is in the same resolution |
| // as its outer surface area. |
| Vector3dF column_0(transform.column(0)); |
| Vector3dF column_1(transform.column(1)); |
| Vector2dF scale(column_0.Length(), column_1.Length()); |
| |
| // If we're aligning translate, bring the fractional component of the |
| // translation into the offscreen render so that we can ensure that we are |
| // sub-pixel aligned when rendering it. |
| Vector2dF translate; |
| if (align_translate) { |
| translate.SetVector(GetFractionalComponent(transform.Get(0, 2)), |
| GetFractionalComponent(transform.Get(1, 2))); |
| } |
| |
| // |scaled_local_bounds| represents |local_bounds| transformed into a hybrid |
| // local/viewport space where only scale (and possibly translate) components |
| // of |transform| are applied. This is the space in which we will rasterize |
| // to the offscreen surface. |
| RectF scaled_local_bounds = local_bounds; |
| scaled_local_bounds.Scale(scale.x(), scale.y()); |
| scaled_local_bounds += translate; |
| |
| // Since our offscreen render is already taking into account scale (and |
| // possibly translation), we communicate to the caller that they should not |
| // apply these components of the transformation when rendering the offscreen |
| // surface. |
| Vector2dF output_pre_translate(-translate.x(), -translate.y()); |
| Vector2dF output_post_scale(1.0f / scale.x(), 1.0f / scale.y()); |
| |
| // To avoid rendering anything we don't need to render, intersect the |
| // bounding box with our current viewport bounds. We do this in the scaled |
| // local bounds space since that is where our rendering will occur, thus |
| // we must take the inverse of the components of the transform not reflected |
| // in scaled local bounds space. |
| RectF clipped_scaled_local_bounds; |
| if (viewport_bounds) { |
| Matrix3F inverse_transform = (TranslateMatrix(output_pre_translate) * |
| transform * ScaleMatrix(output_post_scale)) |
| .Inverse(); |
| clipped_scaled_local_bounds = IntersectRects( |
| inverse_transform.MapRect(*viewport_bounds), scaled_local_bounds); |
| } else { |
| clipped_scaled_local_bounds = scaled_local_bounds; |
| } |
| |
| // Round out our RectF to a Rect here to get integer coordinates that can |
| // be used to define offscreen surface dimensions. |
| Rect output_bounds = math::RoundOut(clipped_scaled_local_bounds); |
| |
| // Now determine the offscreen transform that should be used when rendering |
| // the offscreen render tree to an offscreen surface. |
| // The |translate| component is already present in |output_bounds|, so |
| // we remove it here. Ultimately we will be left with only the fractional |
| // part of the |translate| component. |
| float offscreen_translate_x = translate.x() - output_bounds.origin().x(); |
| float offscreen_translate_y = translate.y() - output_bounds.origin().y(); |
| |
| Matrix3F offscreen_transform = |
| Matrix3F::FromValues(scale.x(), 0, offscreen_translate_x, 0, scale.y(), |
| offscreen_translate_y, 0, 0, 1); |
| |
| return OffscreenRenderCoordinateMapping(output_bounds, output_pre_translate, |
| output_post_scale, |
| offscreen_transform); |
| } |
| |
| } // namespace common |
| } // namespace rasterizer |
| } // namespace renderer |
| } // namespace cobalt |