blob: ea69697deb3b54c3a9edb8095165b9fdb70762f7 [file] [log] [blame]
/*
* 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