blob: f606a3bff2b8e328cd71a74cd5ff6d3466559a83 [file] [log] [blame]
// 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)