| // Copyright 2013 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 <stddef.h> |
| |
| #include <algorithm> |
| |
| #include "base/trace_event/trace_event_argument.h" |
| #include "base/values.h" |
| #include "cc/base/math_util.h" |
| #include "cc/output/filter_operation.h" |
| #include "ui/gfx/animation/tween.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| #include "ui/gfx/skia_util.h" |
| |
| namespace cc { |
| |
| bool FilterOperation::operator==(const FilterOperation& other) const { |
| if (type_ != other.type_) |
| return false; |
| if (type_ == COLOR_MATRIX) |
| return !memcmp(matrix_, other.matrix_, sizeof(matrix_)); |
| if (type_ == DROP_SHADOW) { |
| return amount_ == other.amount_ && |
| drop_shadow_offset_ == other.drop_shadow_offset_ && |
| drop_shadow_color_ == other.drop_shadow_color_; |
| } |
| if (type_ == REFERENCE) { |
| return image_filter_.get() == other.image_filter_.get(); |
| } |
| if (type_ == ALPHA_THRESHOLD) { |
| return region_ == other.region_ && |
| amount_ == other.amount_ && |
| outer_threshold_ == other.outer_threshold_; |
| } |
| return amount_ == other.amount_; |
| } |
| |
| FilterOperation::FilterOperation() : FilterOperation(GRAYSCALE, 0.f) {} |
| |
| FilterOperation::FilterOperation(FilterType type, float amount) |
| : type_(type), |
| amount_(amount), |
| outer_threshold_(0), |
| drop_shadow_offset_(0, 0), |
| drop_shadow_color_(0), |
| zoom_inset_(0) { |
| DCHECK_NE(type_, DROP_SHADOW); |
| DCHECK_NE(type_, COLOR_MATRIX); |
| DCHECK_NE(type_, REFERENCE); |
| memset(matrix_, 0, sizeof(matrix_)); |
| } |
| |
| FilterOperation::FilterOperation(FilterType type, |
| const gfx::Point& offset, |
| float stdDeviation, |
| SkColor color) |
| : type_(type), |
| amount_(stdDeviation), |
| outer_threshold_(0), |
| drop_shadow_offset_(offset), |
| drop_shadow_color_(color), |
| zoom_inset_(0) { |
| DCHECK_EQ(type_, DROP_SHADOW); |
| memset(matrix_, 0, sizeof(matrix_)); |
| } |
| |
| FilterOperation::FilterOperation(FilterType type, SkScalar matrix[20]) |
| : type_(type), |
| amount_(0), |
| outer_threshold_(0), |
| drop_shadow_offset_(0, 0), |
| drop_shadow_color_(0), |
| zoom_inset_(0) { |
| DCHECK_EQ(type_, COLOR_MATRIX); |
| memcpy(matrix_, matrix, sizeof(matrix_)); |
| } |
| |
| FilterOperation::FilterOperation(FilterType type, float amount, int inset) |
| : type_(type), |
| amount_(amount), |
| outer_threshold_(0), |
| drop_shadow_offset_(0, 0), |
| drop_shadow_color_(0), |
| zoom_inset_(inset) { |
| DCHECK_EQ(type_, ZOOM); |
| memset(matrix_, 0, sizeof(matrix_)); |
| } |
| |
| FilterOperation::FilterOperation(FilterType type, |
| sk_sp<SkImageFilter> image_filter) |
| : type_(type), |
| amount_(0), |
| outer_threshold_(0), |
| drop_shadow_offset_(0, 0), |
| drop_shadow_color_(0), |
| image_filter_(std::move(image_filter)), |
| zoom_inset_(0) { |
| DCHECK_EQ(type_, REFERENCE); |
| memset(matrix_, 0, sizeof(matrix_)); |
| } |
| |
| FilterOperation::FilterOperation(FilterType type, |
| const SkRegion& region, |
| float inner_threshold, |
| float outer_threshold) |
| : type_(type), |
| amount_(inner_threshold), |
| outer_threshold_(outer_threshold), |
| drop_shadow_offset_(0, 0), |
| drop_shadow_color_(0), |
| zoom_inset_(0), |
| region_(region) { |
| DCHECK_EQ(type_, ALPHA_THRESHOLD); |
| memset(matrix_, 0, sizeof(matrix_)); |
| } |
| |
| FilterOperation::FilterOperation(const FilterOperation& other) |
| : type_(other.type_), |
| amount_(other.amount_), |
| outer_threshold_(other.outer_threshold_), |
| drop_shadow_offset_(other.drop_shadow_offset_), |
| drop_shadow_color_(other.drop_shadow_color_), |
| image_filter_(other.image_filter_), |
| zoom_inset_(other.zoom_inset_), |
| region_(other.region_) { |
| memcpy(matrix_, other.matrix_, sizeof(matrix_)); |
| } |
| |
| FilterOperation::~FilterOperation() { |
| } |
| |
| static FilterOperation CreateNoOpFilter(FilterOperation::FilterType type) { |
| switch (type) { |
| case FilterOperation::GRAYSCALE: |
| return FilterOperation::CreateGrayscaleFilter(0.f); |
| case FilterOperation::SEPIA: |
| return FilterOperation::CreateSepiaFilter(0.f); |
| case FilterOperation::SATURATE: |
| return FilterOperation::CreateSaturateFilter(1.f); |
| case FilterOperation::HUE_ROTATE: |
| return FilterOperation::CreateHueRotateFilter(0.f); |
| case FilterOperation::INVERT: |
| return FilterOperation::CreateInvertFilter(0.f); |
| case FilterOperation::BRIGHTNESS: |
| return FilterOperation::CreateBrightnessFilter(1.f); |
| case FilterOperation::CONTRAST: |
| return FilterOperation::CreateContrastFilter(1.f); |
| case FilterOperation::OPACITY: |
| return FilterOperation::CreateOpacityFilter(1.f); |
| case FilterOperation::BLUR: |
| return FilterOperation::CreateBlurFilter(0.f); |
| case FilterOperation::DROP_SHADOW: |
| return FilterOperation::CreateDropShadowFilter( |
| gfx::Point(0, 0), 0.f, SK_ColorTRANSPARENT); |
| case FilterOperation::COLOR_MATRIX: { |
| SkScalar matrix[20]; |
| memset(matrix, 0, 20 * sizeof(SkScalar)); |
| matrix[0] = matrix[6] = matrix[12] = matrix[18] = 1.f; |
| return FilterOperation::CreateColorMatrixFilter(matrix); |
| } |
| case FilterOperation::ZOOM: |
| return FilterOperation::CreateZoomFilter(1.f, 0); |
| case FilterOperation::SATURATING_BRIGHTNESS: |
| return FilterOperation::CreateSaturatingBrightnessFilter(0.f); |
| case FilterOperation::REFERENCE: |
| return FilterOperation::CreateReferenceFilter(nullptr); |
| case FilterOperation::ALPHA_THRESHOLD: |
| return FilterOperation::CreateAlphaThresholdFilter(SkRegion(), 1.f, 0.f); |
| } |
| NOTREACHED(); |
| return FilterOperation::CreateEmptyFilter(); |
| } |
| |
| static float ClampAmountForFilterType(float amount, |
| FilterOperation::FilterType type) { |
| switch (type) { |
| case FilterOperation::GRAYSCALE: |
| case FilterOperation::SEPIA: |
| case FilterOperation::INVERT: |
| case FilterOperation::OPACITY: |
| case FilterOperation::ALPHA_THRESHOLD: |
| return MathUtil::ClampToRange(amount, 0.f, 1.f); |
| case FilterOperation::SATURATE: |
| case FilterOperation::BRIGHTNESS: |
| case FilterOperation::CONTRAST: |
| case FilterOperation::BLUR: |
| case FilterOperation::DROP_SHADOW: |
| return std::max(amount, 0.f); |
| case FilterOperation::ZOOM: |
| return std::max(amount, 1.f); |
| case FilterOperation::HUE_ROTATE: |
| case FilterOperation::SATURATING_BRIGHTNESS: |
| return amount; |
| case FilterOperation::COLOR_MATRIX: |
| case FilterOperation::REFERENCE: |
| NOTREACHED(); |
| return amount; |
| } |
| NOTREACHED(); |
| return amount; |
| } |
| |
| // static |
| FilterOperation FilterOperation::Blend(const FilterOperation* from, |
| const FilterOperation* to, |
| double progress) { |
| FilterOperation blended_filter = FilterOperation::CreateEmptyFilter(); |
| |
| if (!from && !to) |
| return blended_filter; |
| |
| const FilterOperation& from_op = from ? *from : CreateNoOpFilter(to->type()); |
| const FilterOperation& to_op = to ? *to : CreateNoOpFilter(from->type()); |
| |
| if (from_op.type() != to_op.type()) |
| return blended_filter; |
| |
| DCHECK(to_op.type() != FilterOperation::COLOR_MATRIX); |
| blended_filter.set_type(to_op.type()); |
| |
| if (to_op.type() == FilterOperation::REFERENCE) { |
| if (progress > 0.5) |
| blended_filter.set_image_filter(to_op.image_filter()); |
| else |
| blended_filter.set_image_filter(from_op.image_filter()); |
| return blended_filter; |
| } |
| |
| blended_filter.set_amount(ClampAmountForFilterType( |
| gfx::Tween::FloatValueBetween(progress, from_op.amount(), to_op.amount()), |
| to_op.type())); |
| |
| if (to_op.type() == FilterOperation::DROP_SHADOW) { |
| gfx::Point blended_offset( |
| gfx::Tween::LinearIntValueBetween(progress, |
| from_op.drop_shadow_offset().x(), |
| to_op.drop_shadow_offset().x()), |
| gfx::Tween::LinearIntValueBetween(progress, |
| from_op.drop_shadow_offset().y(), |
| to_op.drop_shadow_offset().y())); |
| blended_filter.set_drop_shadow_offset(blended_offset); |
| blended_filter.set_drop_shadow_color(gfx::Tween::ColorValueBetween( |
| progress, from_op.drop_shadow_color(), to_op.drop_shadow_color())); |
| } else if (to_op.type() == FilterOperation::ZOOM) { |
| blended_filter.set_zoom_inset( |
| std::max(gfx::Tween::LinearIntValueBetween( |
| from_op.zoom_inset(), to_op.zoom_inset(), progress), |
| 0)); |
| } else if (to_op.type() == FilterOperation::ALPHA_THRESHOLD) { |
| blended_filter.set_outer_threshold(ClampAmountForFilterType( |
| gfx::Tween::FloatValueBetween(progress, |
| from_op.outer_threshold(), |
| to_op.outer_threshold()), |
| to_op.type())); |
| blended_filter.set_region(to_op.region()); |
| } |
| |
| return blended_filter; |
| } |
| |
| void FilterOperation::AsValueInto(base::trace_event::TracedValue* value) const { |
| value->SetInteger("type", type_); |
| switch (type_) { |
| case FilterOperation::GRAYSCALE: |
| case FilterOperation::SEPIA: |
| case FilterOperation::SATURATE: |
| case FilterOperation::HUE_ROTATE: |
| case FilterOperation::INVERT: |
| case FilterOperation::BRIGHTNESS: |
| case FilterOperation::CONTRAST: |
| case FilterOperation::OPACITY: |
| case FilterOperation::BLUR: |
| case FilterOperation::SATURATING_BRIGHTNESS: |
| value->SetDouble("amount", amount_); |
| break; |
| case FilterOperation::DROP_SHADOW: |
| value->SetDouble("std_deviation", amount_); |
| MathUtil::AddToTracedValue("offset", drop_shadow_offset_, value); |
| value->SetInteger("color", drop_shadow_color_); |
| break; |
| case FilterOperation::COLOR_MATRIX: { |
| value->BeginArray("matrix"); |
| for (size_t i = 0; i < arraysize(matrix_); ++i) |
| value->AppendDouble(matrix_[i]); |
| value->EndArray(); |
| break; |
| } |
| case FilterOperation::ZOOM: |
| value->SetDouble("amount", amount_); |
| value->SetDouble("inset", zoom_inset_); |
| break; |
| case FilterOperation::REFERENCE: { |
| int count_inputs = 0; |
| if (image_filter_) { |
| count_inputs = image_filter_->countInputs(); |
| } |
| value->SetBoolean("is_null", !image_filter_); |
| value->SetInteger("count_inputs", count_inputs); |
| break; |
| } |
| case FilterOperation::ALPHA_THRESHOLD: { |
| value->SetDouble("inner_threshold", amount_); |
| value->SetDouble("outer_threshold", outer_threshold_); |
| std::unique_ptr<base::ListValue> region_value(new base::ListValue()); |
| value->BeginArray("region"); |
| for (SkRegion::Iterator it(region_); !it.done(); it.next()) { |
| value->AppendInteger(it.rect().x()); |
| value->AppendInteger(it.rect().y()); |
| value->AppendInteger(it.rect().width()); |
| value->AppendInteger(it.rect().height()); |
| } |
| value->EndArray(); |
| } |
| break; |
| } |
| } |
| |
| namespace { |
| |
| SkVector MapStdDeviation(float std_deviation, const SkMatrix& matrix) { |
| // Corresponds to SpreadForStdDeviation in filter_operations.cc. |
| SkVector sigma = SkVector::Make(std_deviation, std_deviation); |
| matrix.mapVectors(&sigma, 1); |
| return sigma * SkIntToScalar(3); |
| } |
| |
| gfx::Rect MapRectInternal(const FilterOperation& op, |
| const gfx::Rect& rect, |
| const SkMatrix& matrix, |
| SkImageFilter::MapDirection direction) { |
| switch (op.type()) { |
| case FilterOperation::BLUR: { |
| SkVector spread = MapStdDeviation(op.amount(), matrix); |
| float spread_x = std::abs(spread.x()); |
| float spread_y = std::abs(spread.y()); |
| gfx::Rect result = rect; |
| result.Inset(-spread_x, -spread_y, -spread_x, -spread_y); |
| return result; |
| } |
| case FilterOperation::DROP_SHADOW: { |
| SkVector spread = MapStdDeviation(op.amount(), matrix); |
| float spread_x = std::abs(spread.x()); |
| float spread_y = std::abs(spread.y()); |
| gfx::Rect result = rect; |
| result.Inset(-spread_x, -spread_y, -spread_x, -spread_y); |
| |
| gfx::Vector2d drop_shadow_offset = |
| op.drop_shadow_offset().OffsetFromOrigin(); |
| if (direction == SkImageFilter::kForward_MapDirection) |
| result += drop_shadow_offset; |
| else |
| result -= drop_shadow_offset; |
| |
| result.Union(rect); |
| return result; |
| } |
| case FilterOperation::REFERENCE: { |
| if (!op.image_filter()) |
| return rect; |
| return gfx::SkIRectToRect(op.image_filter()->filterBounds( |
| gfx::RectToSkIRect(rect), matrix, direction)); |
| } |
| default: |
| return rect; |
| } |
| } |
| |
| } // namespace |
| |
| gfx::Rect FilterOperation::MapRect(const gfx::Rect& rect, |
| const SkMatrix& matrix) const { |
| return MapRectInternal(*this, rect, matrix, |
| SkImageFilter::kForward_MapDirection); |
| } |
| |
| gfx::Rect FilterOperation::MapRectReverse(const gfx::Rect& rect, |
| const SkMatrix& matrix) const { |
| return MapRectInternal(*this, rect, matrix, |
| SkImageFilter::kReverse_MapDirection); |
| } |
| |
| } // namespace cc |