| /* |
| * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) |
| * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com) |
| * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. |
| * All rights reserved. |
| * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> |
| * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org> |
| * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. |
| * (http://www.torchmobile.com/) |
| * Copyright (c) 2011, Code Aurora Forum. All rights reserved. |
| * Copyright (C) Research In Motion Limited 2011. All rights reserved. |
| * Copyright (C) 2012 Google Inc. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "third_party/blink/renderer/core/css/resolver/filter_operation_resolver.h" |
| |
| #include "third_party/blink/renderer/core/css/css_function_value.h" |
| #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h" |
| #include "third_party/blink/renderer/core/css/css_uri_value.h" |
| #include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h" |
| #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h" |
| #include "third_party/blink/renderer/core/frame/web_feature.h" |
| #include "third_party/blink/renderer/core/style/computed_style.h" |
| #include "third_party/blink/renderer/platform/heap/heap.h" |
| #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" |
| |
| namespace blink { |
| |
| static const float kOffScreenCanvasEmFontSize = 16.0; |
| static const float kOffScreenCanvasRemFontSize = 16.0; |
| |
| FilterOperation::OperationType FilterOperationResolver::FilterOperationForType( |
| CSSValueID type) { |
| switch (type) { |
| case CSSValueID::kGrayscale: |
| return FilterOperation::GRAYSCALE; |
| case CSSValueID::kSepia: |
| return FilterOperation::SEPIA; |
| case CSSValueID::kSaturate: |
| return FilterOperation::SATURATE; |
| case CSSValueID::kHueRotate: |
| return FilterOperation::HUE_ROTATE; |
| case CSSValueID::kInvert: |
| return FilterOperation::INVERT; |
| case CSSValueID::kOpacity: |
| return FilterOperation::OPACITY; |
| case CSSValueID::kBrightness: |
| return FilterOperation::BRIGHTNESS; |
| case CSSValueID::kContrast: |
| return FilterOperation::CONTRAST; |
| case CSSValueID::kBlur: |
| return FilterOperation::BLUR; |
| case CSSValueID::kDropShadow: |
| return FilterOperation::DROP_SHADOW; |
| default: |
| NOTREACHED(); |
| // FIXME: We shouldn't have a type None since we never create them |
| return FilterOperation::NONE; |
| } |
| } |
| |
| static void CountFilterUse(FilterOperation::OperationType operation_type, |
| const Document& document) { |
| // This variable is always reassigned, but MSVC thinks it might be left |
| // uninitialized. |
| WebFeature feature = WebFeature::kNumberOfFeatures; |
| switch (operation_type) { |
| case FilterOperation::NONE: |
| case FilterOperation::BOX_REFLECT: |
| NOTREACHED(); |
| return; |
| case FilterOperation::REFERENCE: |
| feature = WebFeature::kCSSFilterReference; |
| break; |
| case FilterOperation::GRAYSCALE: |
| feature = WebFeature::kCSSFilterGrayscale; |
| break; |
| case FilterOperation::SEPIA: |
| feature = WebFeature::kCSSFilterSepia; |
| break; |
| case FilterOperation::SATURATE: |
| feature = WebFeature::kCSSFilterSaturate; |
| break; |
| case FilterOperation::HUE_ROTATE: |
| feature = WebFeature::kCSSFilterHueRotate; |
| break; |
| case FilterOperation::INVERT: |
| feature = WebFeature::kCSSFilterInvert; |
| break; |
| case FilterOperation::OPACITY: |
| feature = WebFeature::kCSSFilterOpacity; |
| break; |
| case FilterOperation::BRIGHTNESS: |
| feature = WebFeature::kCSSFilterBrightness; |
| break; |
| case FilterOperation::CONTRAST: |
| feature = WebFeature::kCSSFilterContrast; |
| break; |
| case FilterOperation::BLUR: |
| feature = WebFeature::kCSSFilterBlur; |
| break; |
| case FilterOperation::DROP_SHADOW: |
| feature = WebFeature::kCSSFilterDropShadow; |
| break; |
| }; |
| document.CountUse(feature); |
| } |
| |
| double FilterOperationResolver::ResolveNumericArgumentForFunction( |
| const CSSFunctionValue& filter) { |
| switch (filter.FunctionType()) { |
| case CSSValueID::kGrayscale: |
| case CSSValueID::kSepia: |
| case CSSValueID::kSaturate: |
| case CSSValueID::kInvert: |
| case CSSValueID::kBrightness: |
| case CSSValueID::kContrast: |
| case CSSValueID::kOpacity: { |
| double amount = 1; |
| if (filter.length() == 1) { |
| const CSSPrimitiveValue& value = To<CSSPrimitiveValue>(filter.Item(0)); |
| amount = value.GetDoubleValue(); |
| if (value.IsPercentage()) |
| amount /= 100; |
| } |
| return amount; |
| } |
| case CSSValueID::kHueRotate: { |
| double angle = 0; |
| if (filter.length() == 1) { |
| const CSSPrimitiveValue& value = To<CSSPrimitiveValue>(filter.Item(0)); |
| angle = value.ComputeDegrees(); |
| } |
| return angle; |
| } |
| default: |
| return 0; |
| } |
| } |
| |
| FilterOperations FilterOperationResolver::CreateFilterOperations( |
| StyleResolverState& state, |
| const CSSValue& in_value, |
| CSSPropertyID property_id) { |
| FilterOperations operations; |
| |
| if (auto* in_identifier_value = DynamicTo<CSSIdentifierValue>(in_value)) { |
| DCHECK_EQ(in_identifier_value->GetValueID(), CSSValueID::kNone); |
| return operations; |
| } |
| |
| const CSSToLengthConversionData& conversion_data = |
| state.CssToLengthConversionData(); |
| |
| for (auto& curr_value : To<CSSValueList>(in_value)) { |
| if (const auto* url_value = |
| DynamicTo<cssvalue::CSSURIValue>(curr_value.Get())) { |
| CountFilterUse(FilterOperation::REFERENCE, state.GetDocument()); |
| |
| SVGResource* resource = |
| state.GetElementStyleResources().GetSVGResourceFromValue(property_id, |
| *url_value); |
| operations.Operations().push_back( |
| MakeGarbageCollected<ReferenceFilterOperation>( |
| url_value->ValueForSerialization(), resource)); |
| continue; |
| } |
| |
| const auto* filter_value = To<CSSFunctionValue>(curr_value.Get()); |
| FilterOperation::OperationType operation_type = |
| FilterOperationForType(filter_value->FunctionType()); |
| CountFilterUse(operation_type, state.GetDocument()); |
| DCHECK_LE(filter_value->length(), 1u); |
| switch (filter_value->FunctionType()) { |
| case CSSValueID::kGrayscale: |
| case CSSValueID::kSepia: |
| case CSSValueID::kSaturate: |
| case CSSValueID::kHueRotate: { |
| operations.Operations().push_back( |
| MakeGarbageCollected<BasicColorMatrixFilterOperation>( |
| ResolveNumericArgumentForFunction(*filter_value), |
| operation_type)); |
| break; |
| } |
| case CSSValueID::kInvert: |
| case CSSValueID::kBrightness: |
| case CSSValueID::kContrast: |
| case CSSValueID::kOpacity: { |
| operations.Operations().push_back( |
| MakeGarbageCollected<BasicComponentTransferFilterOperation>( |
| ResolveNumericArgumentForFunction(*filter_value), |
| operation_type)); |
| break; |
| } |
| case CSSValueID::kBlur: { |
| Length std_deviation = Length::Fixed(0); |
| if (filter_value->length() >= 1) { |
| const CSSPrimitiveValue* first_value = |
| DynamicTo<CSSPrimitiveValue>(filter_value->Item(0)); |
| std_deviation = first_value->ConvertToLength(conversion_data); |
| } |
| operations.Operations().push_back( |
| MakeGarbageCollected<BlurFilterOperation>(std_deviation)); |
| break; |
| } |
| case CSSValueID::kDropShadow: { |
| ShadowData shadow = StyleBuilderConverter::ConvertShadow( |
| conversion_data, &state, filter_value->Item(0)); |
| // TODO(fs): Resolve 'currentcolor' when constructing the filter chain. |
| if (shadow.GetColor().IsCurrentColor()) { |
| shadow.OverrideColor(state.Style()->GetCurrentColor()); |
| } |
| operations.Operations().push_back( |
| MakeGarbageCollected<DropShadowFilterOperation>(shadow)); |
| break; |
| } |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| return operations; |
| } |
| |
| FilterOperations FilterOperationResolver::CreateOffscreenFilterOperations( |
| const CSSValue& in_value, |
| const Font& font) { |
| FilterOperations operations; |
| |
| if (auto* in_identifier_value = DynamicTo<CSSIdentifierValue>(in_value)) { |
| DCHECK_EQ(in_identifier_value->GetValueID(), CSSValueID::kNone); |
| return operations; |
| } |
| |
| // TODO(layout-dev): Should document zoom factor apply for offscreen canvas? |
| float zoom = 1.0f; |
| CSSToLengthConversionData::FontSizes font_sizes( |
| kOffScreenCanvasEmFontSize, kOffScreenCanvasRemFontSize, &font, zoom); |
| CSSToLengthConversionData::ViewportSize viewport_size(0, 0); |
| CSSToLengthConversionData conversion_data(nullptr, // ComputedStyle |
| font_sizes, viewport_size, |
| 1); // zoom |
| |
| for (auto& curr_value : To<CSSValueList>(in_value)) { |
| if (curr_value->IsURIValue()) |
| continue; |
| |
| const auto* filter_value = To<CSSFunctionValue>(curr_value.Get()); |
| FilterOperation::OperationType operation_type = |
| FilterOperationForType(filter_value->FunctionType()); |
| // TODO(fserb): Take an ExecutionContext argument to this function, |
| // so we can have workers using UseCounter as well. |
| // countFilterUse(operationType, state.document()); |
| DCHECK_LE(filter_value->length(), 1u); |
| switch (filter_value->FunctionType()) { |
| case CSSValueID::kGrayscale: |
| case CSSValueID::kSepia: |
| case CSSValueID::kSaturate: |
| case CSSValueID::kHueRotate: { |
| operations.Operations().push_back( |
| MakeGarbageCollected<BasicColorMatrixFilterOperation>( |
| ResolveNumericArgumentForFunction(*filter_value), |
| operation_type)); |
| break; |
| } |
| case CSSValueID::kInvert: |
| case CSSValueID::kBrightness: |
| case CSSValueID::kContrast: |
| case CSSValueID::kOpacity: { |
| operations.Operations().push_back( |
| MakeGarbageCollected<BasicComponentTransferFilterOperation>( |
| ResolveNumericArgumentForFunction(*filter_value), |
| operation_type)); |
| break; |
| } |
| case CSSValueID::kBlur: { |
| Length std_deviation = Length::Fixed(0); |
| if (filter_value->length() >= 1) { |
| const CSSPrimitiveValue* first_value = |
| DynamicTo<CSSPrimitiveValue>(filter_value->Item(0)); |
| std_deviation = first_value->ConvertToLength(conversion_data); |
| } |
| operations.Operations().push_back( |
| MakeGarbageCollected<BlurFilterOperation>(std_deviation)); |
| break; |
| } |
| case CSSValueID::kDropShadow: { |
| ShadowData shadow = StyleBuilderConverter::ConvertShadow( |
| conversion_data, nullptr, filter_value->Item(0)); |
| // For offscreen canvas, the default color is always black. |
| if (shadow.GetColor().IsCurrentColor()) { |
| shadow.OverrideColor(Color::kBlack); |
| } |
| operations.Operations().push_back( |
| MakeGarbageCollected<DropShadowFilterOperation>(shadow)); |
| break; |
| } |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| return operations; |
| } |
| |
| } // namespace blink |