blob: 7d5d71080c158182a03a3d76b91ac9f32ed553d5 [file] [log] [blame]
// Copyright 2017 The Chromium 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 "cc/paint/paint_shader.h"
#include "base/memory/ptr_util.h"
#include "cc/paint/paint_record.h"
#include "third_party/skia/include/effects/SkGradientShader.h"
namespace cc {
sk_sp<PaintShader> PaintShader::MakeColor(SkColor color) {
sk_sp<PaintShader> shader(new PaintShader(Type::kColor));
// Just one color. Store it in the fallback color. Easy.
shader->fallback_color_ = color;
return shader;
}
sk_sp<PaintShader> PaintShader::MakeLinearGradient(const SkPoint points[],
const SkColor colors[],
const SkScalar pos[],
int count,
SkShader::TileMode mode,
uint32_t flags,
const SkMatrix* local_matrix,
SkColor fallback_color) {
sk_sp<PaintShader> shader(new PaintShader(Type::kLinearGradient));
// There are always two points, the start and the end.
shader->start_point_ = points[0];
shader->end_point_ = points[1];
shader->SetColorsAndPositions(colors, pos, count);
shader->SetMatrixAndTiling(local_matrix, mode, mode);
shader->SetFlagsAndFallback(flags, fallback_color);
return shader;
}
sk_sp<PaintShader> PaintShader::MakeRadialGradient(const SkPoint& center,
SkScalar radius,
const SkColor colors[],
const SkScalar pos[],
int count,
SkShader::TileMode mode,
uint32_t flags,
const SkMatrix* local_matrix,
SkColor fallback_color) {
sk_sp<PaintShader> shader(new PaintShader(Type::kRadialGradient));
shader->center_ = center;
shader->start_radius_ = shader->end_radius_ = radius;
shader->SetColorsAndPositions(colors, pos, count);
shader->SetMatrixAndTiling(local_matrix, mode, mode);
shader->SetFlagsAndFallback(flags, fallback_color);
return shader;
}
sk_sp<PaintShader> PaintShader::MakeTwoPointConicalGradient(
const SkPoint& start,
SkScalar start_radius,
const SkPoint& end,
SkScalar end_radius,
const SkColor colors[],
const SkScalar pos[],
int count,
SkShader::TileMode mode,
uint32_t flags,
const SkMatrix* local_matrix,
SkColor fallback_color) {
sk_sp<PaintShader> shader(new PaintShader(Type::kTwoPointConicalGradient));
shader->start_point_ = start;
shader->end_point_ = end;
shader->start_radius_ = start_radius;
shader->end_radius_ = end_radius;
shader->SetColorsAndPositions(colors, pos, count);
shader->SetMatrixAndTiling(local_matrix, mode, mode);
shader->SetFlagsAndFallback(flags, fallback_color);
return shader;
}
sk_sp<PaintShader> PaintShader::MakeSweepGradient(SkScalar cx,
SkScalar cy,
const SkColor colors[],
const SkScalar pos[],
int color_count,
SkShader::TileMode mode,
SkScalar start_degrees,
SkScalar end_degrees,
uint32_t flags,
const SkMatrix* local_matrix,
SkColor fallback_color) {
sk_sp<PaintShader> shader(new PaintShader(Type::kSweepGradient));
shader->center_ = SkPoint::Make(cx, cy);
shader->start_degrees_ = start_degrees;
shader->end_degrees_ = end_degrees;
shader->SetColorsAndPositions(colors, pos, color_count);
shader->SetMatrixAndTiling(local_matrix, mode, mode);
shader->SetFlagsAndFallback(flags, fallback_color);
return shader;
}
sk_sp<PaintShader> PaintShader::MakeImage(const PaintImage& image,
SkShader::TileMode tx,
SkShader::TileMode ty,
const SkMatrix* local_matrix) {
sk_sp<PaintShader> shader(new PaintShader(Type::kImage));
shader->image_ = image;
shader->SetMatrixAndTiling(local_matrix, tx, ty);
return shader;
}
sk_sp<PaintShader> PaintShader::MakePaintRecord(
sk_sp<PaintRecord> record,
const SkRect& tile,
SkShader::TileMode tx,
SkShader::TileMode ty,
const SkMatrix* local_matrix,
ScalingBehavior scaling_behavior) {
sk_sp<PaintShader> shader(new PaintShader(Type::kPaintRecord));
shader->record_ = std::move(record);
shader->tile_ = tile;
shader->scaling_behavior_ = scaling_behavior;
shader->SetMatrixAndTiling(local_matrix, tx, ty);
return shader;
}
PaintShader::PaintShader(Type type) : shader_type_(type) {}
PaintShader::~PaintShader() = default;
sk_sp<SkShader> PaintShader::GetSkShader() const {
if (cached_shader_)
return cached_shader_;
switch (shader_type_) {
case Type::kColor:
// This will be handled by the fallback check below.
break;
case Type::kLinearGradient: {
SkPoint points[2] = {start_point_, end_point_};
cached_shader_ = SkGradientShader::MakeLinear(
points, colors_.data(),
positions_.empty() ? nullptr : positions_.data(),
static_cast<int>(colors_.size()), tx_, flags_,
local_matrix_ ? &*local_matrix_ : nullptr);
break;
}
case Type::kRadialGradient:
cached_shader_ = SkGradientShader::MakeRadial(
center_, start_radius_, colors_.data(),
positions_.empty() ? nullptr : positions_.data(),
static_cast<int>(colors_.size()), tx_, flags_,
local_matrix_ ? &*local_matrix_ : nullptr);
break;
case Type::kTwoPointConicalGradient:
cached_shader_ = SkGradientShader::MakeTwoPointConical(
start_point_, start_radius_, end_point_, end_radius_, colors_.data(),
positions_.empty() ? nullptr : positions_.data(),
static_cast<int>(colors_.size()), tx_, flags_,
local_matrix_ ? &*local_matrix_ : nullptr);
break;
case Type::kSweepGradient:
cached_shader_ = SkGradientShader::MakeSweep(
center_.x(), center_.y(), colors_.data(),
positions_.empty() ? nullptr : positions_.data(),
static_cast<int>(colors_.size()), tx_, start_degrees_, end_degrees_,
flags_, local_matrix_ ? &*local_matrix_ : nullptr);
break;
case Type::kImage:
cached_shader_ = image_.GetSkImage()->makeShader(
tx_, ty_, local_matrix_ ? &*local_matrix_ : nullptr);
break;
case Type::kPaintRecord: {
auto picture = ToSkPicture(record_, tile_);
switch (scaling_behavior_) {
// For raster scale, we create a picture shader directly.
case ScalingBehavior::kRasterAtScale:
cached_shader_ = SkShader::MakePictureShader(
std::move(picture), tx_, ty_,
local_matrix_ ? &*local_matrix_ : nullptr, nullptr);
break;
// For fixed scale, we create an image shader with and image backed by
// the picture.
case ScalingBehavior::kFixedScale: {
auto image = SkImage::MakeFromPicture(
std::move(picture), SkISize::Make(tile_.width(), tile_.height()),
nullptr, nullptr, SkImage::BitDepth::kU8,
SkColorSpace::MakeSRGB());
cached_shader_ = image->makeShader(
tx_, ty_, local_matrix_ ? &*local_matrix_ : nullptr);
break;
}
}
break;
}
case Type::kShaderCount:
NOTREACHED();
break;
}
// If we didn't create a shader for whatever reason, create a fallback color
// one.
if (!cached_shader_)
cached_shader_ = SkShader::MakeColorShader(fallback_color_);
return cached_shader_;
}
void PaintShader::SetColorsAndPositions(const SkColor* colors,
const SkScalar* positions,
int count) {
DCHECK_GE(count, 2);
colors_.assign(colors, colors + count);
if (positions)
positions_.assign(positions, positions + count);
}
void PaintShader::SetMatrixAndTiling(const SkMatrix* matrix,
SkShader::TileMode tx,
SkShader::TileMode ty) {
if (matrix)
local_matrix_ = *matrix;
tx_ = tx;
ty_ = ty;
}
void PaintShader::SetFlagsAndFallback(uint32_t flags, SkColor fallback_color) {
flags_ = flags;
fallback_color_ = fallback_color;
}
bool PaintShader::IsOpaque() const {
// TODO(enne): don't create a shader to answer this.
return GetSkShader()->isOpaque();
}
bool PaintShader::IsValid() const {
// If we managed to create a shader already, then we should be valid.
if (cached_shader_)
return true;
switch (shader_type_) {
case Type::kColor:
return true;
case Type::kLinearGradient:
case Type::kRadialGradient:
case Type::kTwoPointConicalGradient:
case Type::kSweepGradient:
return colors_.size() >= 2 &&
(positions_.empty() || positions_.size() == colors_.size());
case Type::kImage:
return !!image_;
case Type::kPaintRecord:
return !!record_;
case Type::kShaderCount:
return false;
}
return false;
}
} // namespace cc