blob: 5dc78f7a823e2b5a98a6c91a63800c4cf06589a5 [file] [log] [blame]
// Copyright 2013 The Flutter 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 "impeller/entity/geometry/rect_geometry.h"
namespace impeller {
FillRectGeometry::FillRectGeometry(Rect rect) : rect_(rect) {}
FillRectGeometry::~FillRectGeometry() = default;
GeometryResult FillRectGeometry::GetPositionBuffer(
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
auto& data_host_buffer = renderer.GetTransientsDataBuffer();
return GeometryResult{
.type = PrimitiveType::kTriangleStrip,
.vertex_buffer =
{
.vertex_buffer = data_host_buffer.Emplace(
rect_.GetPoints().data(), 8 * sizeof(float), alignof(float)),
.vertex_count = 4,
.index_type = IndexType::kNone,
},
.transform = entity.GetShaderTransform(pass),
.mode = GeometryResult::Mode::kNormal,
};
}
std::optional<Rect> FillRectGeometry::GetCoverage(
const Matrix& transform) const {
return rect_.TransformBounds(transform);
}
bool FillRectGeometry::CoversArea(const Matrix& transform,
const Rect& rect) const {
if (!transform.IsTranslationScaleOnly()) {
return false;
}
Rect coverage = rect_.TransformBounds(transform);
return coverage.Contains(rect);
}
bool FillRectGeometry::IsAxisAlignedRect() const {
return true;
}
StrokeRectGeometry::StrokeRectGeometry(const Rect& rect,
const StrokeParameters& stroke)
: rect_(rect),
stroke_width_(stroke.width),
stroke_join_(AdjustStrokeJoin(stroke)) {}
StrokeRectGeometry::~StrokeRectGeometry() = default;
GeometryResult StrokeRectGeometry::GetPositionBuffer(
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
if (stroke_width_ < 0.0) {
return {};
}
Scalar max_basis = entity.GetTransform().GetMaxBasisLengthXY();
if (max_basis == 0) {
return {};
}
Scalar min_size = kMinStrokeSize / max_basis;
Scalar stroke_width = std::max(stroke_width_, min_size);
Scalar half_stroke_width = stroke_width * 0.5f;
auto& data_host_buffer = renderer.GetTransientsDataBuffer();
const Rect& rect = rect_;
bool interior_filled = (stroke_width >= rect.GetSize().MinDimension());
switch (stroke_join_) {
case Join::kRound: {
Tessellator::Trigs trigs =
renderer.GetTessellator().GetTrigsForDeviceRadius(half_stroke_width *
max_basis);
FML_DCHECK(trigs.size() >= 2u);
auto vertex_count = trigs.size() * 4;
if (!interior_filled) {
// If there is a hole in the interior (as with most stroked rects
// unless the stroke width is really really wide) then we need
// to perform some surgery to generate the hollowed-out interior.
vertex_count += 12;
}
return GeometryResult{
.type = PrimitiveType::kTriangleStrip,
.vertex_buffer =
{
.vertex_buffer = data_host_buffer.Emplace(
vertex_count * sizeof(Point), alignof(Point),
[hsw = half_stroke_width, &rect, vertex_count, &trigs,
interior_filled](uint8_t* buffer) {
Scalar left = rect.GetLeft();
Scalar top = rect.GetTop();
Scalar right = rect.GetRight();
Scalar bottom = rect.GetBottom();
auto vertices = reinterpret_cast<Point*>(buffer);
[[maybe_unused]]
auto vertices_end = vertices + vertex_count;
// Traverse top down, left to right across slices.
// Slice 1: Draw across between top pair of round joins.
for (auto trig : trigs) {
// trig.sin goes from 0 to 1
// trig.cos goes from 1 to 0
*vertices++ = Point(left - trig.sin * hsw,
top - trig.cos * hsw);
*vertices++ = Point(right + trig.sin * hsw,
top - trig.cos * hsw);
}
// Ends up with vertices that draw across the bottom
// of the top curved section (left - hsw, top) to
// (right + hsw, top). This is the starting pair of
// vertices for the following square section.
if (interior_filled) {
// If interior is filled, we can just let the bottom
// pair of vertices of the top edge connect to the
// top pair of vertices of the bottom edge generated
// in slice 5 below. They both go left-right so they
// will create a proper zig-zag box to connect the
// 2 sections.
} else {
// Slice 2: Draw the inner part of the top stroke.
// Simply extend down from the last horizontal pair
// of vertices to (top + hsw).
*vertices++ = Point(left - hsw, top + hsw);
*vertices++ = Point(right + hsw, top + hsw);
// Slice 3: Draw the left and right edges.
// Slice 3a: Draw the right edge first.
// Since we are already at the right edge from the
// previous slice, we just have to add 2 vertices
// to get to the bottom of that right edge, but we
// have to start with an additional vertex that
// connects to (right - hsw) instead of the left
// side of the rectangle to avoid a big triangle
// through the hollow interior section.
*vertices++ = Point(right - hsw, top + hsw);
*vertices++ = Point(right + hsw, bottom - hsw);
*vertices++ = Point(right - hsw, bottom - hsw);
// Now we need to jump up for the left edge, but we
// need to dupliate the last point and the next point
// to avoid drawing anything connecting them. These
// 2 vertices end up generating 2 empty triangles.
*vertices++ = Point(right - hsw, bottom - hsw);
*vertices++ = Point(left + hsw, top + hsw);
// Slice 3b: Now draw the left edge.
// We draw this in a specific zig zag order so that
// we end up at (left - hsw, bottom - hsw) to connect
// properly to the next section.
*vertices++ = Point(left + hsw, top + hsw);
*vertices++ = Point(left - hsw, top + hsw);
*vertices++ = Point(left + hsw, bottom - hsw);
*vertices++ = Point(left - hsw, bottom - hsw);
// Slice 4: Draw the inner part of the bottom stroke.
// Since the next section starts by drawing across
// the width of the rect at Y=bottom, we simple have
// to make sure that we presently have a pair of
// vertices that span the top of that section. The
// last point was (left - hsw, bottom - hsw), so we
// just have to add its right side partner which
// is not the same as the vertex before that. This
// extra vertex ends up defining an empty triangle,
// but sets us up for the final slice to complete
// this interior part of the bottom stroke.
*vertices++ = Point(right + hsw, bottom - hsw);
// Now the first pair of vertices below will
// "complete the zig-zag box" for the inner part
// of the bottom stroke.
}
// Slice 5: Draw between bottom pair of round joins.
for (auto trig : trigs) {
// trig.sin goes from 0 to 1
// trig.cos goes from 1 to 0
*vertices++ = Point(left - trig.cos * hsw,
bottom + trig.sin * hsw);
*vertices++ = Point(right + trig.cos * hsw,
bottom + trig.sin * hsw);
}
// Make sure our estimate is always up to date.
FML_DCHECK(vertices == vertices_end);
}),
.vertex_count = vertex_count,
.index_type = IndexType::kNone,
},
.transform = entity.GetShaderTransform(pass),
.mode = GeometryResult::Mode::kNormal,
};
}
case Join::kBevel: {
if (interior_filled) {
return GeometryResult{
.type = PrimitiveType::kTriangleStrip,
.vertex_buffer =
{
.vertex_buffer = data_host_buffer.Emplace(
8 * sizeof(Point), alignof(Point),
[hsw = half_stroke_width, &rect](uint8_t* buffer) {
Scalar left = rect.GetLeft();
Scalar top = rect.GetTop();
Scalar right = rect.GetRight();
Scalar bottom = rect.GetBottom();
auto vertices = reinterpret_cast<Point*>(buffer);
vertices[0] = Point(left, top - hsw);
vertices[1] = Point(right, top - hsw);
vertices[2] = Point(left - hsw, top);
vertices[3] = Point(right + hsw, top);
vertices[4] = Point(left - hsw, bottom);
vertices[5] = Point(right + hsw, bottom);
vertices[6] = Point(left, bottom + hsw);
vertices[7] = Point(right, bottom + hsw);
}),
.vertex_count = 8u,
.index_type = IndexType::kNone,
},
.transform = entity.GetShaderTransform(pass),
.mode = GeometryResult::Mode::kNormal,
};
}
return GeometryResult{
.type = PrimitiveType::kTriangleStrip,
.vertex_buffer =
{
.vertex_buffer = data_host_buffer.Emplace(
17 * sizeof(Point), alignof(Point),
[hsw = half_stroke_width, &rect](uint8_t* buffer) {
Scalar left = rect.GetLeft();
Scalar top = rect.GetTop();
Scalar right = rect.GetRight();
Scalar bottom = rect.GetBottom();
auto vertices = reinterpret_cast<Point*>(buffer);
vertices[0] = Point(left - hsw, top);
vertices[1] = Point(left + hsw, top + hsw);
vertices[2] = Point(left, top - hsw);
vertices[3] = Point(right - hsw, top + hsw);
vertices[4] = Point(right, top - hsw);
vertices[5] = Point(right - hsw, top + hsw);
vertices[6] = Point(right + hsw, top);
vertices[7] = Point(right - hsw, bottom - hsw);
vertices[8] = Point(right + hsw, bottom);
vertices[9] = Point(right - hsw, bottom - hsw);
vertices[10] = Point(right, bottom + hsw);
vertices[11] = Point(left + hsw, bottom - hsw);
vertices[12] = Point(left, bottom + hsw);
vertices[13] = Point(left + hsw, bottom - hsw);
vertices[14] = Point(left - hsw, bottom);
vertices[15] = Point(left + hsw, top + hsw);
vertices[16] = Point(left - hsw, top);
}),
.vertex_count = 17u,
.index_type = IndexType::kNone,
},
.transform = entity.GetShaderTransform(pass),
.mode = GeometryResult::Mode::kNormal,
};
}
case Join::kMiter: {
if (interior_filled) {
return GeometryResult{
.type = PrimitiveType::kTriangleStrip,
.vertex_buffer =
{
.vertex_buffer = data_host_buffer.Emplace(
4 * sizeof(Point), alignof(Point),
[hsw = half_stroke_width, &rect](uint8_t* buffer) {
Scalar left = rect.GetLeft();
Scalar top = rect.GetTop();
Scalar right = rect.GetRight();
Scalar bottom = rect.GetBottom();
auto vertices = reinterpret_cast<Point*>(buffer);
vertices[0] = Point(left - hsw, top - hsw);
vertices[1] = Point(right + hsw, top - hsw);
vertices[2] = Point(left - hsw, bottom + hsw);
vertices[3] = Point(right + hsw, bottom + hsw);
}),
.vertex_count = 4u,
.index_type = IndexType::kNone,
},
.transform = entity.GetShaderTransform(pass),
.mode = GeometryResult::Mode::kNormal,
};
}
return GeometryResult{
.type = PrimitiveType::kTriangleStrip,
.vertex_buffer =
{
.vertex_buffer = data_host_buffer.Emplace(
10 * sizeof(Point), alignof(Point),
[hsw = half_stroke_width, &rect](uint8_t* buffer) {
Scalar left = rect.GetLeft();
Scalar top = rect.GetTop();
Scalar right = rect.GetRight();
Scalar bottom = rect.GetBottom();
auto vertices = reinterpret_cast<Point*>(buffer);
vertices[0] = Point(left - hsw, top - hsw);
vertices[1] = Point(left + hsw, top + hsw);
vertices[2] = Point(right + hsw, top - hsw);
vertices[3] = Point(right - hsw, top + hsw);
vertices[4] = Point(right + hsw, bottom + hsw);
vertices[5] = Point(right - hsw, bottom - hsw);
vertices[6] = Point(left - hsw, bottom + hsw);
vertices[7] = Point(left + hsw, bottom - hsw);
vertices[8] = Point(left - hsw, top - hsw);
vertices[9] = Point(left + hsw, top + hsw);
}),
.vertex_count = 10u,
.index_type = IndexType::kNone,
},
.transform = entity.GetShaderTransform(pass),
.mode = GeometryResult::Mode::kNormal,
};
}
}
}
std::optional<Rect> StrokeRectGeometry::GetCoverage(
const Matrix& transform) const {
return rect_.TransformBounds(transform);
}
Join StrokeRectGeometry::AdjustStrokeJoin(const StrokeParameters& stroke) {
return (stroke.join == Join::kMiter && stroke.miter_limit < kSqrt2)
? Join::kBevel
: stroke.join;
}
} // namespace impeller