blob: 9dba0d37a38e529133f435225459b8bdb7b2ffcb [file] [log] [blame]
// Copyright 2017 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/egl/draw_rect_shadow_spread.h"
#include <GLES2/gl2.h>
#include <algorithm>
#include "base/logging.h"
#include "cobalt/renderer/backend/egl/utils.h"
#include "starboard/memory.h"
namespace cobalt {
namespace renderer {
namespace rasterizer {
namespace egl {
namespace {
const int kVertexCount = 10;
}
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),
inner_rect_(inner_rect),
outer_rect_(outer_rect),
inner_corners_(inner_corners),
outer_corners_(outer_corners),
offset_scale_(1.0f),
vertex_buffer_(nullptr),
vertex_count_(0) {
color_ = GetGLRGBA(GetDrawColor(color) * base_state_.opacity);
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_);
}
graphics_state->ReserveVertexData(kVertexCount * sizeof(VertexAttributes));
}
DrawRectShadowSpread::DrawRectShadowSpread(GraphicsState* graphics_state,
const BaseState& base_state)
: DrawObject(base_state),
offset_scale_(1.0f),
vertex_buffer_(nullptr),
vertex_count_(0) {
graphics_state->ReserveVertexData(kVertexCount * sizeof(VertexAttributes));
}
void DrawRectShadowSpread::ExecuteUpdateVertexBuffer(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
// Draw the box shadow's spread. This is a triangle strip covering the area
// between outer rect and inner rect.
math::RectF inside_rect(inner_rect_);
math::RectF outside_rect(outer_rect_);
VertexAttributes attributes[kVertexCount];
if (inner_corners_) {
// Inset the inside rect to include the rounded corners.
inside_rect.Inset(
std::max(inner_corners_->bottom_left.horizontal,
inner_corners_->top_left.horizontal),
std::max(inner_corners_->top_left.vertical,
inner_corners_->top_right.vertical),
std::max(inner_corners_->top_right.horizontal,
inner_corners_->bottom_right.horizontal),
std::max(inner_corners_->bottom_right.vertical,
inner_corners_->bottom_left.vertical));
// Add a 1 pixel border to the outer rect for anti-aliasing.
outside_rect.Outset(1.0f, 1.0f);
}
// Only pixels inside the outer rect should be touched.
if (inside_rect.IsEmpty()) {
vertex_count_ = 4;
SetVertex(&attributes[0], outside_rect.x(), outside_rect.y());
SetVertex(&attributes[1], outside_rect.right(), outside_rect.y());
SetVertex(&attributes[2], outside_rect.x(), outside_rect.bottom());
SetVertex(&attributes[3], outside_rect.right(), outside_rect.bottom());
} else {
inside_rect.Intersect(outside_rect);
vertex_count_ = 10;
SetVertex(&attributes[0], outside_rect.x(), outside_rect.y());
SetVertex(&attributes[1], inside_rect.x(), inside_rect.y());
SetVertex(&attributes[2], outside_rect.right(), outside_rect.y());
SetVertex(&attributes[3], inside_rect.right(), inside_rect.y());
SetVertex(&attributes[4], outside_rect.right(), outside_rect.bottom());
SetVertex(&attributes[5], inside_rect.right(), inside_rect.bottom());
SetVertex(&attributes[6], outside_rect.x(), outside_rect.bottom());
SetVertex(&attributes[7], inside_rect.x(), inside_rect.bottom());
SetVertex(&attributes[8], outside_rect.x(), outside_rect.y());
SetVertex(&attributes[9], inside_rect.x(), inside_rect.y());
}
vertex_buffer_ = graphics_state->AllocateVertexData(
vertex_count_ * sizeof(VertexAttributes));
SbMemoryCopy(vertex_buffer_, attributes,
vertex_count_ * sizeof(VertexAttributes));
}
void DrawRectShadowSpread::ExecuteRasterize(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
if (inner_corners_) {
ShaderProgram<CommonVertexShader,
ShaderFragmentColorBetweenRrects>* program;
program_manager->GetProgram(&program);
graphics_state->UseProgram(program->GetHandle());
SetupShader(program->GetVertexShader(), graphics_state);
SetRRectUniforms(program->GetFragmentShader().u_inner_rect(),
program->GetFragmentShader().u_inner_corners(),
inner_rect_, *inner_corners_, 0.5f);
SetRRectUniforms(program->GetFragmentShader().u_outer_rect(),
program->GetFragmentShader().u_outer_corners(),
outer_rect_, *outer_corners_, 0.5f);
} else {
ShaderProgram<CommonVertexShader, ShaderFragmentColorInclude>* program;
program_manager->GetProgram(&program);
graphics_state->UseProgram(program->GetHandle());
SetupShader(program->GetVertexShader(), graphics_state);
float include[4] = {
outer_rect_.x(),
outer_rect_.y(),
outer_rect_.right(),
outer_rect_.bottom()
};
GL_CALL(glUniform4fv(program->GetFragmentShader().u_include(), 1, include));
}
GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count_));
}
void DrawRectShadowSpread::SetupShader(const CommonVertexShader& shader,
GraphicsState* graphics_state) {
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(VertexAttributes), vertex_buffer_ +
offsetof(VertexAttributes, position));
graphics_state->VertexAttribPointer(
shader.a_color(), 4, GL_UNSIGNED_BYTE, GL_TRUE,
sizeof(VertexAttributes), vertex_buffer_ +
offsetof(VertexAttributes, color));
graphics_state->VertexAttribPointer(
shader.a_offset(), 2, GL_FLOAT, GL_FALSE,
sizeof(VertexAttributes), vertex_buffer_ +
offsetof(VertexAttributes, offset));
graphics_state->VertexAttribFinish();
}
base::TypeId DrawRectShadowSpread::GetTypeId() const {
if (inner_corners_) {
return ShaderProgram<CommonVertexShader,
ShaderFragmentColorBetweenRrects>::GetTypeId();
} else {
return ShaderProgram<CommonVertexShader,
ShaderFragmentColorInclude>::GetTypeId();
}
}
void DrawRectShadowSpread::SetVertex(VertexAttributes* vertex,
float x, float y) {
vertex->position[0] = x;
vertex->position[1] = y;
vertex->offset[0] = (x - offset_center_.x()) * offset_scale_;
vertex->offset[1] = (y - offset_center_.y()) * offset_scale_;
vertex->color = color_;
}
} // namespace egl
} // namespace rasterizer
} // namespace renderer
} // namespace cobalt