| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cc/paint/paint_flags.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include "base/memory/values_equivalent.h" |
| #include "base/notreached.h" |
| #include "cc/paint/paint_filter.h" |
| #include "cc/paint/paint_op.h" |
| #include "cc/paint/paint_op_buffer.h" |
| #include "cc/paint/paint_shader.h" |
| #include "third_party/skia/include/core/SkColorFilter.h" |
| #include "third_party/skia/include/core/SkPathEffect.h" |
| #include "third_party/skia/include/core/SkPathUtils.h" |
| |
| namespace { |
| |
| template <typename T> |
| bool AreValuesEqualForTesting(const sk_sp<T>& a, const sk_sp<T>& b) { |
| return base::ValuesEquivalent(a, b, [](const T& x, const T& y) { |
| return x.EqualsForTesting(y); // IN-TEST |
| }); |
| } |
| |
| } // namespace |
| |
| namespace cc { |
| |
| CorePaintFlags::CorePaintFlags() { |
| // Match SkPaint defaults. |
| bitfields_uint_ = 0u; |
| bitfields_.cap_type_ = SkPaint::kDefault_Cap; |
| bitfields_.join_type_ = SkPaint::kDefault_Join; |
| bitfields_.style_ = SkPaint::kFill_Style; |
| bitfields_.blend_mode_ = static_cast<int>(SkBlendMode::kSrcOver); |
| bitfields_.filter_quality_ = |
| static_cast<int>(PaintFlags::FilterQuality::kNone); |
| bitfields_.dynamic_range_limit_standard_mix_ = 0; |
| bitfields_.dynamic_range_limit_constrained_high_mix_ = 0; |
| |
| static_assert(sizeof(bitfields_) <= sizeof(bitfields_uint_), |
| "Too many bitfields"); |
| } |
| |
| bool CorePaintFlags::operator==(const CorePaintFlags& other) const { |
| return color_ == other.color_ && width_ == other.width_ && |
| miter_limit_ == other.miter_limit_ && |
| bitfields_uint_ == other.bitfields_uint_; |
| } |
| |
| PaintFlags::PaintFlags() = default; |
| |
| PaintFlags::PaintFlags(const PaintFlags& flags) = default; |
| |
| PaintFlags::PaintFlags(const CorePaintFlags& flags) : CorePaintFlags(flags) {} |
| |
| PaintFlags::PaintFlags(PaintFlags&& other) = default; |
| |
| PaintFlags& PaintFlags::operator=(const PaintFlags& other) = default; |
| |
| PaintFlags& PaintFlags::operator=(PaintFlags&& other) = default; |
| |
| PaintFlags::~PaintFlags() = default; |
| |
| bool PaintFlags::CanConvertToCorePaintFlags() const { |
| return IsValid() && !path_effect_ && !shader_ && !color_filter_ && |
| !draw_looper_ && !image_filter_; |
| } |
| |
| CorePaintFlags PaintFlags::ToCorePaintFlags() const { |
| DCHECK(CanConvertToCorePaintFlags()); |
| return CorePaintFlags(*this); |
| } |
| |
| void PaintFlags::setImageFilter(sk_sp<PaintFilter> filter) { |
| image_filter_ = std::move(filter); |
| } |
| |
| bool PaintFlags::ShaderIsOpaque() const { |
| return shader_->IsOpaque(); |
| } |
| |
| void PaintFlags::setShader(sk_sp<PaintShader> shader) { |
| shader_ = std::move(shader); |
| } |
| |
| bool PaintFlags::nothingToDraw() const { |
| // Duplicated from SkPaint to avoid having to construct an SkPaint to |
| // answer this question. |
| if (getLooper()) |
| return false; |
| |
| switch (getBlendMode()) { |
| case SkBlendMode::kSrcOver: |
| case SkBlendMode::kSrcATop: |
| case SkBlendMode::kDstOut: |
| case SkBlendMode::kDstOver: |
| case SkBlendMode::kPlus: |
| if (isFullyTransparent()) { |
| return !color_filter_ && !image_filter_; |
| } |
| break; |
| case SkBlendMode::kDst: |
| return true; |
| default: |
| break; |
| } |
| return false; |
| } |
| |
| bool PaintFlags::getFillPath(const SkPath& src, |
| SkPath* dst, |
| const SkRect* cull_rect, |
| SkScalar res_scale) const { |
| SkPaint paint = ToSkPaint(); |
| return skpathutils::FillPathWithPaint(src, paint, dst, cull_rect, res_scale); |
| } |
| |
| bool PaintFlags::SupportsFoldingAlpha() const { |
| if (getBlendMode() != SkBlendMode::kSrcOver) { |
| return false; |
| } |
| if (getColorFilter()) { |
| return false; |
| } |
| if (getImageFilter()) { |
| return false; |
| } |
| if (getLooper()) { |
| return false; |
| } |
| return true; |
| } |
| |
| SkPaint PaintFlags::ToSkPaint() const { |
| SkPaint paint; |
| if (path_effect_) { |
| paint.setPathEffect(path_effect_->GetSkPathEffect()); |
| } |
| if (shader_) { |
| paint.setShader(shader_->GetSkShader(getFilterQuality())); |
| } |
| if (color_filter_) { |
| paint.setColorFilter(color_filter_->sk_color_filter_); |
| } |
| if (image_filter_) { |
| paint.setImageFilter(image_filter_->cached_sk_filter_); |
| } |
| paint.setColor(getColor4f()); |
| paint.setStrokeWidth(getStrokeWidth()); |
| paint.setStrokeMiter(getStrokeMiter()); |
| paint.setBlendMode(getBlendMode()); |
| paint.setAntiAlias(isAntiAlias()); |
| paint.setDither(isDither()); |
| paint.setStrokeCap(static_cast<SkPaint::Cap>(getStrokeCap())); |
| paint.setStrokeJoin(static_cast<SkPaint::Join>(getStrokeJoin())); |
| paint.setStyle(static_cast<SkPaint::Style>(getStyle())); |
| return paint; |
| } |
| |
| SkSamplingOptions PaintFlags::FilterQualityToSkSamplingOptions( |
| PaintFlags::FilterQuality filter_quality) { |
| return FilterQualityToSkSamplingOptions(filter_quality, |
| ScalingOperation::kDefault); |
| } |
| |
| SkSamplingOptions PaintFlags::FilterQualityToSkSamplingOptions( |
| PaintFlags::FilterQuality filter_quality, |
| PaintFlags::ScalingOperation scaling_op) { |
| switch (filter_quality) { |
| case PaintFlags::FilterQuality::kHigh: |
| switch (scaling_op) { |
| case PaintFlags::ScalingOperation::kDefault: |
| return SkSamplingOptions(SkCubicResampler::CatmullRom()); |
| case PaintFlags::ScalingOperation::kUnknown: |
| return SkSamplingOptions(SkFilterMode::kLinear, |
| SkMipmapMode::kLinear); |
| case PaintFlags::ScalingOperation::kUpscale: |
| return SkSamplingOptions(SkCubicResampler::Mitchell()); |
| } |
| case PaintFlags::FilterQuality::kMedium: |
| switch (scaling_op) { |
| case PaintFlags::ScalingOperation::kDefault: |
| return SkSamplingOptions(SkFilterMode::kLinear, |
| SkMipmapMode::kNearest); |
| case PaintFlags::ScalingOperation::kUnknown: |
| case PaintFlags::ScalingOperation::kUpscale: |
| return SkSamplingOptions(SkFilterMode::kLinear, |
| SkMipmapMode::kLinear); |
| } |
| case PaintFlags::FilterQuality::kLow: |
| return SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone); |
| case PaintFlags::FilterQuality::kNone: |
| return SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone); |
| } |
| } |
| |
| bool CorePaintFlags::IsValid() const { |
| return PaintOp::IsValidPaintFlagsSkBlendMode(getBlendMode()); |
| } |
| |
| bool PaintFlags::EqualsForTesting(const PaintFlags& other) const { |
| // Can't just ToSkPaint and operator== here as SkPaint does pointer |
| // comparisons on all the ref'd skia objects on the SkPaint, which |
| // is not true after serialization. |
| return getColor() == other.getColor() && |
| getStrokeWidth() == other.getStrokeWidth() && |
| getStrokeMiter() == other.getStrokeMiter() && |
| getBlendMode() == other.getBlendMode() && |
| getStrokeCap() == other.getStrokeCap() && |
| getStrokeJoin() == other.getStrokeJoin() && |
| getStyle() == other.getStyle() && |
| getFilterQuality() == other.getFilterQuality() && |
| getDynamicRangeLimit() == other.getDynamicRangeLimit() && |
| isArcClosed() == other.isArcClosed() && |
| AreValuesEqualForTesting(path_effect_, // IN-TEST |
| other.path_effect_) && |
| AreValuesEqualForTesting(color_filter_, // IN-TEST |
| other.color_filter_) && |
| AreValuesEqualForTesting(draw_looper_, // IN-TEST |
| other.draw_looper_) && |
| AreValuesEqualForTesting(image_filter_, // IN-TEST |
| other.image_filter_) && |
| AreValuesEqualForTesting(shader_, other.shader_); // IN-TEST |
| } |
| |
| bool PaintFlags::HasDiscardableImages( |
| gfx::ContentColorUsage* content_color_usage) const { |
| bool has_discardable_images = false; |
| if (shader_) { |
| has_discardable_images = shader_->HasDiscardableImages(content_color_usage); |
| } |
| if (image_filter_ && image_filter_->has_discardable_images()) { |
| if (content_color_usage) { |
| *content_color_usage = |
| std::max(*content_color_usage, image_filter_->GetContentColorUsage()); |
| } |
| has_discardable_images = true; |
| } |
| return has_discardable_images; |
| } |
| |
| float PaintFlags::DynamicRangeLimitMixture::ComputeEffectiveHdrHeadroom( |
| float target_hdr_headroom) const { |
| // It would make more sense to store only `high_mix` and `constrained_mix`, |
| // since `standard_mix` is multiplied by zero. |
| const float high_mix = 1.f - constrained_high_mix - standard_mix; |
| constexpr float kConstrainedMax = 1.f; // Constrained allows at most 1 stop |
| return constrained_high_mix * std::min(kConstrainedMax, target_hdr_headroom) + |
| high_mix * target_hdr_headroom; |
| } |
| |
| } // namespace cc |