| // Copyright 2017 The Cobalt Authors. 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 "starboard/configuration.h" |
| #if SB_API_VERSION >= 12 || SB_HAS(GLES2) |
| |
| #include "cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h" |
| |
| #include <algorithm> |
| |
| #include "base/logging.h" |
| #include "cobalt/renderer/backend/egl/utils.h" |
| #include "cobalt/renderer/egl_and_gles.h" |
| #include "starboard/memory.h" |
| |
| namespace cobalt { |
| namespace renderer { |
| namespace rasterizer { |
| namespace egl { |
| |
| DrawRectShadowSpread::VertexAttributesSquare::VertexAttributesSquare( |
| float x, float y, uint32_t in_color) { |
| position[0] = x; |
| position[1] = y; |
| color = in_color; |
| } |
| |
| DrawRectShadowSpread::VertexAttributesRound::VertexAttributesRound( |
| float x, float y, const RCorner& inner, const RCorner& outer) { |
| position[0] = x; |
| position[1] = y; |
| rcorner_inner = RCorner(position, inner); |
| rcorner_outer = RCorner(position, outer); |
| } |
| |
| DrawRectShadowSpread::DrawRectShadowSpread( |
| GraphicsState* graphics_state, const BaseState& base_state, |
| const math::RectF& inner_rect, const OptionalRoundedCorners& inner_corners, |
| const math::RectF& outer_rect, const OptionalRoundedCorners& outer_corners, |
| const render_tree::ColorRGBA& color) |
| : DrawObject(base_state), vertex_buffer_(nullptr), index_buffer_(nullptr) { |
| color_ = GetDrawColor(color) * base_state_.opacity; |
| |
| // Extract scale from the transform and move it into the vertex attributes |
| // so that the anti-aliased edges remain 1 pixel wide. |
| math::Vector2dF scale = RemoveScaleFromTransform(); |
| math::RectF inside_rect(inner_rect); |
| math::RectF outside_rect(outer_rect); |
| inside_rect.Scale(scale.x(), scale.y()); |
| outside_rect.Scale(scale.x(), scale.y()); |
| |
| if (inner_corners || outer_corners) { |
| // If using rounded corners, then both inner and outer rects must have |
| // rounded corner definitions. |
| DCHECK(inner_corners); |
| DCHECK(outer_corners); |
| render_tree::RoundedCorners inside_corners = |
| inner_corners->Scale(scale.x(), scale.y()); |
| render_tree::RoundedCorners outside_corners = |
| outer_corners->Scale(scale.x(), scale.y()); |
| SetGeometry(graphics_state, inside_rect, inside_corners, outside_rect, |
| outside_corners); |
| } else { |
| SetGeometry(graphics_state, inside_rect, outside_rect); |
| } |
| } |
| |
| void DrawRectShadowSpread::ExecuteUpdateVertexBuffer( |
| GraphicsState* graphics_state, ShaderProgramManager* program_manager) { |
| if (attributes_square_.size() > 0) { |
| vertex_buffer_ = graphics_state->AllocateVertexData( |
| attributes_square_.size() * sizeof(attributes_square_[0])); |
| SbMemoryCopy(vertex_buffer_, &attributes_square_[0], |
| attributes_square_.size() * sizeof(attributes_square_[0])); |
| } else if (attributes_round_.size() > 0) { |
| vertex_buffer_ = graphics_state->AllocateVertexData( |
| attributes_round_.size() * sizeof(attributes_round_[0])); |
| SbMemoryCopy(vertex_buffer_, &attributes_round_[0], |
| attributes_round_.size() * sizeof(attributes_round_[0])); |
| index_buffer_ = graphics_state->AllocateVertexIndices(indices_.size()); |
| SbMemoryCopy(index_buffer_, &indices_[0], |
| indices_.size() * sizeof(indices_[0])); |
| } |
| } |
| |
| void DrawRectShadowSpread::ExecuteRasterize( |
| GraphicsState* graphics_state, ShaderProgramManager* program_manager) { |
| if (vertex_buffer_ == nullptr) { |
| return; |
| } |
| |
| // Draw the box shadow. |
| if (attributes_square_.size() > 0) { |
| ShaderProgram<ShaderVertexColor, ShaderFragmentColor>* program; |
| program_manager->GetProgram(&program); |
| graphics_state->UseProgram(program->GetHandle()); |
| SetupVertexShader(graphics_state, program->GetVertexShader()); |
| GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_square_.size())); |
| } else { |
| ShaderProgram<ShaderVertexRcorner2, ShaderFragmentRcorner2Color>* program; |
| program_manager->GetProgram(&program); |
| graphics_state->UseProgram(program->GetHandle()); |
| SetupVertexShader(graphics_state, program->GetVertexShader()); |
| GL_CALL(glUniform4f(program->GetFragmentShader().u_color(), color_.r(), |
| color_.g(), color_.b(), color_.a())); |
| GL_CALL( |
| glDrawElements(GL_TRIANGLES, indices_.size(), GL_UNSIGNED_SHORT, |
| graphics_state->GetVertexIndexPointer(index_buffer_))); |
| } |
| } |
| |
| base::TypeId DrawRectShadowSpread::GetTypeId() const { |
| if (attributes_square_.size() > 0) { |
| return ShaderProgram<ShaderVertexColor, ShaderFragmentColor>::GetTypeId(); |
| } else { |
| return ShaderProgram<ShaderVertexRcorner2, |
| ShaderFragmentRcorner2Color>::GetTypeId(); |
| } |
| } |
| |
| void DrawRectShadowSpread::SetupVertexShader(GraphicsState* graphics_state, |
| const ShaderVertexColor& shader) { |
| graphics_state->UpdateClipAdjustment(shader.u_clip_adjustment()); |
| graphics_state->UpdateTransformMatrix(shader.u_view_matrix(), |
| base_state_.transform); |
| graphics_state->Scissor(base_state_.scissor.x(), base_state_.scissor.y(), |
| base_state_.scissor.width(), |
| base_state_.scissor.height()); |
| graphics_state->VertexAttribPointer( |
| shader.a_position(), 2, GL_FLOAT, GL_FALSE, |
| sizeof(VertexAttributesSquare), |
| vertex_buffer_ + offsetof(VertexAttributesSquare, position)); |
| graphics_state->VertexAttribPointer( |
| shader.a_color(), 4, GL_UNSIGNED_BYTE, GL_TRUE, |
| sizeof(VertexAttributesSquare), |
| vertex_buffer_ + offsetof(VertexAttributesSquare, color)); |
| graphics_state->VertexAttribFinish(); |
| } |
| |
| void DrawRectShadowSpread::SetupVertexShader( |
| GraphicsState* graphics_state, const ShaderVertexRcorner2& shader) { |
| graphics_state->UpdateClipAdjustment(shader.u_clip_adjustment()); |
| graphics_state->UpdateTransformMatrix(shader.u_view_matrix(), |
| base_state_.transform); |
| graphics_state->Scissor(base_state_.scissor.x(), base_state_.scissor.y(), |
| base_state_.scissor.width(), |
| base_state_.scissor.height()); |
| graphics_state->VertexAttribPointer( |
| shader.a_position(), 2, GL_FLOAT, GL_FALSE, sizeof(VertexAttributesRound), |
| vertex_buffer_ + offsetof(VertexAttributesRound, position)); |
| graphics_state->VertexAttribPointer( |
| shader.a_rcorner_inner(), 4, GL_FLOAT, GL_FALSE, |
| sizeof(VertexAttributesRound), |
| vertex_buffer_ + offsetof(VertexAttributesRound, rcorner_inner)); |
| graphics_state->VertexAttribPointer( |
| shader.a_rcorner_outer(), 4, GL_FLOAT, GL_FALSE, |
| sizeof(VertexAttributesRound), |
| vertex_buffer_ + offsetof(VertexAttributesRound, rcorner_outer)); |
| graphics_state->VertexAttribFinish(); |
| } |
| |
| void DrawRectShadowSpread::SetGeometry(GraphicsState* graphics_state, |
| const math::RectF& inner_rect, |
| const math::RectF& outer_rect) { |
| // Draw the box shadow's spread. This is a triangle strip covering the area |
| // between outer rect and inner rect. |
| uint32_t color = GetGLRGBA(color_); |
| |
| if (inner_rect.IsEmpty()) { |
| attributes_square_.reserve(4); |
| attributes_square_.emplace_back(outer_rect.x(), outer_rect.y(), color); |
| attributes_square_.emplace_back(outer_rect.right(), outer_rect.y(), color); |
| attributes_square_.emplace_back(outer_rect.x(), outer_rect.bottom(), color); |
| attributes_square_.emplace_back(outer_rect.right(), outer_rect.bottom(), |
| color); |
| } else { |
| math::RectF inside_rect(inner_rect); |
| inside_rect.Intersect(outer_rect); |
| attributes_square_.reserve(10); |
| attributes_square_.emplace_back(outer_rect.x(), outer_rect.y(), color); |
| attributes_square_.emplace_back(inside_rect.x(), inside_rect.y(), color); |
| attributes_square_.emplace_back(outer_rect.right(), outer_rect.y(), color); |
| attributes_square_.emplace_back(inside_rect.right(), inside_rect.y(), |
| color); |
| attributes_square_.emplace_back(outer_rect.right(), outer_rect.bottom(), |
| color); |
| attributes_square_.emplace_back(inside_rect.right(), inside_rect.bottom(), |
| color); |
| attributes_square_.emplace_back(outer_rect.x(), outer_rect.bottom(), color); |
| attributes_square_.emplace_back(inside_rect.x(), inside_rect.bottom(), |
| color); |
| attributes_square_.emplace_back(outer_rect.x(), outer_rect.y(), color); |
| attributes_square_.emplace_back(inside_rect.x(), inside_rect.y(), color); |
| } |
| |
| graphics_state->ReserveVertexData(attributes_square_.size() * |
| sizeof(attributes_square_[0])); |
| } |
| |
| void DrawRectShadowSpread::SetGeometry( |
| GraphicsState* graphics_state, const math::RectF& inner_rect, |
| const render_tree::RoundedCorners& inner_corners, |
| const math::RectF& outer_rect, |
| const render_tree::RoundedCorners& outer_corners) { |
| // Draw the area between the inner rounded rect and outer rounded rect. Add |
| // a 1-pixel border to include antialiasing. |
| math::RectF bounds(outer_rect); |
| bounds.Outset(1.0f, 1.0f); |
| |
| // Get the render quads for the inner rounded rect excluding its inscribed |
| // rectangle. |
| RRectAttributes rrect_inner[8]; |
| GetRRectAttributes(bounds, inner_rect, inner_corners, rrect_inner); |
| |
| // Get the render quads for the outer rounded rect. |
| RRectAttributes rrect_outer[4]; |
| GetRRectAttributes(bounds, outer_rect, outer_corners, rrect_outer); |
| |
| // Add geometry to draw the area between the inner rrect and outer rrect. |
| for (int i = 0; i < arraysize(rrect_inner); ++i) { |
| for (int o = 0; o < arraysize(rrect_outer); ++o) { |
| math::RectF intersection = |
| math::IntersectRects(rrect_inner[i].bounds, rrect_outer[o].bounds); |
| if (!intersection.IsEmpty()) { |
| // Use two triangles to draw the intersection. |
| const RCorner& inner = rrect_inner[i].rcorner; |
| const RCorner& outer = rrect_outer[o].rcorner; |
| uint16_t vert = static_cast<uint16_t>(attributes_round_.size()); |
| attributes_round_.emplace_back(intersection.x(), intersection.y(), |
| inner, outer); |
| attributes_round_.emplace_back(intersection.right(), intersection.y(), |
| inner, outer); |
| attributes_round_.emplace_back(intersection.x(), intersection.bottom(), |
| inner, outer); |
| attributes_round_.emplace_back(intersection.right(), |
| intersection.bottom(), inner, outer); |
| indices_.emplace_back(vert); |
| indices_.emplace_back(vert + 1); |
| indices_.emplace_back(vert + 2); |
| indices_.emplace_back(vert + 1); |
| indices_.emplace_back(vert + 2); |
| indices_.emplace_back(vert + 3); |
| } |
| } |
| } |
| |
| graphics_state->ReserveVertexData(attributes_round_.size() * |
| sizeof(attributes_round_[0])); |
| graphics_state->ReserveVertexIndices(indices_.size()); |
| } |
| |
| } // namespace egl |
| } // namespace rasterizer |
| } // namespace renderer |
| } // namespace cobalt |
| |
| #endif // SB_API_VERSION >= 12 || SB_HAS(GLES2) |