blob: 0369ae54a0817bd3711e899d89dcb4cc851ee1ca [file] [log] [blame]
// Copyright 2014 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.
#ifndef COBALT_RENDER_TREE_BRUSH_H_
#define COBALT_RENDER_TREE_BRUSH_H_
#include <cmath>
#include <memory>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "cobalt/base/type_id.h"
#include "cobalt/math/point_f.h"
#include "cobalt/math/size_f.h"
#include "cobalt/render_tree/brush_visitor.h"
#include "cobalt/render_tree/color_rgba.h"
namespace cobalt {
namespace render_tree {
// A base class of all render tree brushes.
class Brush {
public:
virtual ~Brush() {}
// A type-safe branching.
virtual void Accept(BrushVisitor* visitor) const = 0;
// Returns an ID that is unique to the brush type. This can be used to
// polymorphically identify what type a brush is.
virtual base::TypeId GetTypeId() const = 0;
};
class SolidColorBrush : public Brush {
public:
explicit SolidColorBrush(const ColorRGBA& color) : color_(color) {}
bool operator==(const SolidColorBrush& other) const {
return color_ == other.color_;
}
// A type-safe branching.
void Accept(BrushVisitor* visitor) const override;
base::TypeId GetTypeId() const override {
return base::GetTypeId<SolidColorBrush>();
}
const ColorRGBA& color() const { return color_; }
private:
ColorRGBA color_;
};
// ColorStops are used to describe linear and radial gradients. For linear
// gradients, |position| represents the faction between the source and
// destination points that |color| should be applied. For radial gradients,
// it represents the fraction between the center point and radius at which
// |color| should apply. In both cases, 0 <= |position| <= 1.
struct ColorStop {
ColorStop() : position(-1) {}
ColorStop(float position, const ColorRGBA& color)
: position(position), color(color) {}
bool operator==(const ColorStop& other) const {
return position == other.position && color == other.color;
}
float position;
ColorRGBA color;
};
// Returns true if and only if two color stops are very close to each other.
inline bool IsNear(const render_tree::ColorStop& lhs,
const render_tree::ColorStop& rhs, float epsilon) {
if (std::fabs(lhs.position - rhs.position) > epsilon) return false;
return lhs.color == rhs.color;
}
typedef std::vector<ColorStop> ColorStopList;
bool IsNear(const render_tree::ColorStopList& lhs,
const render_tree::ColorStopList& rhs, float epsilon);
// Calculate the source and destination points to use for a linear gradient
// of the specified angle to cover the given frame.
//
// The method of defining the source and destination points for the linear
// gradient are defined here:
// https://www.w3.org/TR/2012/CR-css3-images-20120417/#linear-gradients
//
// "Starting from the center of the gradient box, extend a line at the
// specified angle in both directions. The ending point is the point on the
// gradient line where a line drawn perpendicular to the gradient line would
// intersect the corner of the gradient box in the specified direction. The
// starting point is determined identically, but in the opposite direction."
std::pair<math::PointF, math::PointF> LinearGradientPointsFromAngle(
float ccw_radians_from_right, const math::SizeF& frame_size);
// Linear gradient brushes can be used to fill a shape with a linear color
// gradient with arbitrary many color stops. It is specified by a source
// and destination point, which define a line segment along which the color
// stops apply, each having a position between 0 and 1 representing the
// position of the stop along that line. Interpolation occurs in premultiplied
// alpha space.
// NOTE: The source and destination points may lie inside or outside the shape
// which uses the gradient brush. Always consider the shape as a mask over the
// gradient whose first and last color stops extend infinitely in their
// respective directions.
class LinearGradientBrush : public Brush {
public:
// The ColorStopList passed into LienarGradientBrush must have at least two
// stops and they must be sorted in order of increasing position.
LinearGradientBrush(const math::PointF& source, const math::PointF& dest,
const ColorStopList& color_stops);
LinearGradientBrush(const std::pair<math::PointF, math::PointF>& source_dest,
const ColorStopList& color_stops)
: LinearGradientBrush(source_dest.first, source_dest.second,
color_stops) {}
// Creates a 2-stop linear gradient from source to dest.
LinearGradientBrush(const math::PointF& source, const math::PointF& dest,
const ColorRGBA& source_color,
const ColorRGBA& dest_color);
LinearGradientBrush(const std::pair<math::PointF, math::PointF>& source_dest,
const ColorRGBA& source_color,
const ColorRGBA& dest_color)
: LinearGradientBrush(source_dest.first, source_dest.second, source_color,
dest_color) {}
bool operator==(const LinearGradientBrush& other) const {
return data_ == other.data_;
}
struct Data {
Data();
Data(const math::PointF& source, const math::PointF& dest,
const ColorStopList& color_stops);
Data(const math::PointF& source, const math::PointF& dest);
bool operator==(const Data& other) const {
return source_ == other.source_ && dest_ == other.dest_ &&
color_stops_ == other.color_stops_;
}
math::PointF source_;
math::PointF dest_;
ColorStopList color_stops_;
};
// A type-safe branching.
void Accept(BrushVisitor* visitor) const override;
base::TypeId GetTypeId() const override {
return base::GetTypeId<LinearGradientBrush>();
}
// Returns the source and destination points of the linear gradient.
const math::PointF& source() const { return data_.source_; }
const math::PointF& dest() const { return data_.dest_; }
// Returns the list of color stops along the line segment between the source
// and destination points.
const ColorStopList& color_stops() const { return data_.color_stops_; }
// Returns true if, and only if the brush is horizontal.
bool IsHorizontal(float epsilon = 0.001f) const {
return std::abs(data_.source_.y() - data_.dest_.y()) < epsilon;
}
// Returns true if, and only if the brush is vertical.
bool IsVertical(float epsilon = 0.001f) const {
return std::abs(data_.source_.x() - data_.dest_.x()) < epsilon;
}
const Data& data() const { return data_; }
private:
Data data_;
};
// A radial gradient brush can be used to fill a shape with a color gradient
// that expands from a given center point up to a specified radius. The list
// of color stops have position values between 0 and 1 which represent the
// distance between the center point and the specified x-axis radius that the
// color should apply to. Interpolation occurs in premultiplied alpha space.
class RadialGradientBrush : public Brush {
public:
// The ColorStopList passed into RadialGradientBrush must have at least two
// stops and they must be sorted in order of increasing position.
RadialGradientBrush(const math::PointF& center, float radius,
const ColorStopList& color_stops);
// Constructor that allows for ellipses instead of circles.
RadialGradientBrush(const math::PointF& center, float radius_x,
float radius_y, const ColorStopList& color_stops);
// Creates a 2-stop radial gradient.
RadialGradientBrush(const math::PointF& center, float radius,
const ColorRGBA& source_color,
const ColorRGBA& dest_color);
// Constructor for ellipses instead of circles.
RadialGradientBrush(const math::PointF& center, float radius_x,
float radius_y, const ColorRGBA& source_color,
const ColorRGBA& dest_color);
bool operator==(const RadialGradientBrush& other) const {
return center_ == other.center_ && radius_x_ == other.radius_x_ &&
radius_y_ == other.radius_y_ && color_stops_ == other.color_stops_;
}
// A type-safe branching.
void Accept(BrushVisitor* visitor) const override;
base::TypeId GetTypeId() const override {
return base::GetTypeId<RadialGradientBrush>();
}
// Returns the source and destination points of the linear gradient.
const math::PointF& center() const { return center_; }
float radius_x() const { return radius_x_; }
float radius_y() const { return radius_y_; }
// Returns the list of color stops from the center point to the radius.
const ColorStopList& color_stops() const { return color_stops_; }
private:
math::PointF center_;
float radius_x_;
float radius_y_;
ColorStopList color_stops_;
};
std::unique_ptr<Brush> CloneBrush(const Brush* brush);
class EqualsBrushVisitor : public BrushVisitor {
public:
explicit EqualsBrushVisitor(Brush* brush_a)
: brush_a_(brush_a), result_(false) {}
bool result() const { return result_; }
void Visit(const SolidColorBrush* solid_color_brush) override {
result_ =
brush_a_->GetTypeId() == base::GetTypeId<SolidColorBrush>() &&
*static_cast<const SolidColorBrush*>(brush_a_) == *solid_color_brush;
}
void Visit(const LinearGradientBrush* linear_gradient_brush) override {
result_ = brush_a_->GetTypeId() == base::GetTypeId<LinearGradientBrush>() &&
*static_cast<const LinearGradientBrush*>(brush_a_) ==
*linear_gradient_brush;
}
void Visit(const RadialGradientBrush* radial_gradient_brush) override {
result_ = brush_a_->GetTypeId() == base::GetTypeId<RadialGradientBrush>() &&
*static_cast<const RadialGradientBrush*>(brush_a_) ==
*radial_gradient_brush;
}
private:
Brush* brush_a_;
bool result_;
};
} // namespace render_tree
} // namespace cobalt
#endif // COBALT_RENDER_TREE_BRUSH_H_