1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 *           (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
7 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
10 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
11 * Copyright (C) 2012 Google Inc. All rights reserved.
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Library General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 * Library General Public License for more details.
22 *
23 * You should have received a copy of the GNU Library General Public License
24 * along with this library; see the file COPYING.LIB.  If not, write to
25 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 * Boston, MA 02110-1301, USA.
27 */
28
29#include "config.h"
30#include "core/css/resolver/FilterOperationResolver.h"
31
32#include "core/css/CSSFilterValue.h"
33#include "core/css/CSSPrimitiveValueMappings.h"
34#include "core/css/CSSShadowValue.h"
35#include "core/css/resolver/TransformBuilder.h"
36#include "core/rendering/svg/ReferenceFilterBuilder.h"
37#include "core/svg/SVGURIReference.h"
38
39namespace blink {
40
41static FilterOperation::OperationType filterOperationForType(CSSFilterValue::FilterOperationType type)
42{
43    switch (type) {
44    case CSSFilterValue::ReferenceFilterOperation:
45        return FilterOperation::REFERENCE;
46    case CSSFilterValue::GrayscaleFilterOperation:
47        return FilterOperation::GRAYSCALE;
48    case CSSFilterValue::SepiaFilterOperation:
49        return FilterOperation::SEPIA;
50    case CSSFilterValue::SaturateFilterOperation:
51        return FilterOperation::SATURATE;
52    case CSSFilterValue::HueRotateFilterOperation:
53        return FilterOperation::HUE_ROTATE;
54    case CSSFilterValue::InvertFilterOperation:
55        return FilterOperation::INVERT;
56    case CSSFilterValue::OpacityFilterOperation:
57        return FilterOperation::OPACITY;
58    case CSSFilterValue::BrightnessFilterOperation:
59        return FilterOperation::BRIGHTNESS;
60    case CSSFilterValue::ContrastFilterOperation:
61        return FilterOperation::CONTRAST;
62    case CSSFilterValue::BlurFilterOperation:
63        return FilterOperation::BLUR;
64    case CSSFilterValue::DropShadowFilterOperation:
65        return FilterOperation::DROP_SHADOW;
66    case CSSFilterValue::UnknownFilterOperation:
67        return FilterOperation::NONE;
68    }
69    return FilterOperation::NONE;
70}
71
72bool FilterOperationResolver::createFilterOperations(CSSValue* inValue, const CSSToLengthConversionData& unadjustedConversionData, FilterOperations& outOperations, StyleResolverState& state)
73{
74    ASSERT(outOperations.isEmpty());
75
76    if (!inValue)
77        return false;
78
79    if (inValue->isPrimitiveValue()) {
80        CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(inValue);
81        if (primitiveValue->getValueID() == CSSValueNone)
82            return true;
83    }
84
85    if (!inValue->isValueList())
86        return false;
87
88    float zoomFactor = unadjustedConversionData.zoom();
89    const CSSToLengthConversionData& conversionData = unadjustedConversionData.copyWithAdjustedZoom(zoomFactor);
90    FilterOperations operations;
91    for (CSSValueListIterator i = inValue; i.hasMore(); i.advance()) {
92        CSSValue* currValue = i.value();
93        if (!currValue->isFilterValue())
94            continue;
95
96        CSSFilterValue* filterValue = toCSSFilterValue(i.value());
97        FilterOperation::OperationType operationType = filterOperationForType(filterValue->operationType());
98
99        if (operationType == FilterOperation::REFERENCE) {
100            if (filterValue->length() != 1)
101                continue;
102            CSSValue* argument = filterValue->item(0);
103
104            if (!argument->isSVGDocumentValue())
105                continue;
106
107            CSSSVGDocumentValue* svgDocumentValue = toCSSSVGDocumentValue(argument);
108            KURL url = state.document().completeURL(svgDocumentValue->url());
109
110            RefPtr<ReferenceFilterOperation> operation = ReferenceFilterOperation::create(svgDocumentValue->url(), AtomicString(url.fragmentIdentifier()));
111            if (SVGURIReference::isExternalURIReference(svgDocumentValue->url(), state.document())) {
112                if (!svgDocumentValue->loadRequested())
113                    state.elementStyleResources().addPendingSVGDocument(operation.get(), svgDocumentValue);
114                else if (svgDocumentValue->cachedSVGDocument())
115                    ReferenceFilterBuilder::setDocumentResourceReference(operation.get(), adoptPtr(new DocumentResourceReference(svgDocumentValue->cachedSVGDocument())));
116            }
117            operations.operations().append(operation);
118            continue;
119        }
120
121        // Check that all parameters are primitive values, with the
122        // exception of drop shadow which has a CSSShadowValue parameter.
123        if (operationType != FilterOperation::DROP_SHADOW) {
124            bool haveNonPrimitiveValue = false;
125            for (unsigned j = 0; j < filterValue->length(); ++j) {
126                if (!filterValue->item(j)->isPrimitiveValue()) {
127                    haveNonPrimitiveValue = true;
128                    break;
129                }
130            }
131            if (haveNonPrimitiveValue)
132                continue;
133        }
134
135        CSSPrimitiveValue* firstValue = filterValue->length() && filterValue->item(0)->isPrimitiveValue() ? toCSSPrimitiveValue(filterValue->item(0)) : 0;
136        switch (filterValue->operationType()) {
137        case CSSFilterValue::GrayscaleFilterOperation:
138        case CSSFilterValue::SepiaFilterOperation:
139        case CSSFilterValue::SaturateFilterOperation: {
140            double amount = 1;
141            if (filterValue->length() == 1) {
142                amount = firstValue->getDoubleValue();
143                if (firstValue->isPercentage())
144                    amount /= 100;
145            }
146
147            operations.operations().append(BasicColorMatrixFilterOperation::create(amount, operationType));
148            break;
149        }
150        case CSSFilterValue::HueRotateFilterOperation: {
151            double angle = 0;
152            if (filterValue->length() == 1)
153                angle = firstValue->computeDegrees();
154
155            operations.operations().append(BasicColorMatrixFilterOperation::create(angle, operationType));
156            break;
157        }
158        case CSSFilterValue::InvertFilterOperation:
159        case CSSFilterValue::BrightnessFilterOperation:
160        case CSSFilterValue::ContrastFilterOperation:
161        case CSSFilterValue::OpacityFilterOperation: {
162            double amount = (filterValue->operationType() == CSSFilterValue::BrightnessFilterOperation) ? 0 : 1;
163            if (filterValue->length() == 1) {
164                amount = firstValue->getDoubleValue();
165                if (firstValue->isPercentage())
166                    amount /= 100;
167            }
168
169            operations.operations().append(BasicComponentTransferFilterOperation::create(amount, operationType));
170            break;
171        }
172        case CSSFilterValue::BlurFilterOperation: {
173            Length stdDeviation = Length(0, Fixed);
174            if (filterValue->length() >= 1)
175                stdDeviation = firstValue->convertToLength<FixedConversion | PercentConversion>(conversionData);
176            operations.operations().append(BlurFilterOperation::create(stdDeviation));
177            break;
178        }
179        case CSSFilterValue::DropShadowFilterOperation: {
180            if (filterValue->length() != 1)
181                return false;
182
183            CSSValue* cssValue = filterValue->item(0);
184            if (!cssValue->isShadowValue())
185                continue;
186
187            CSSShadowValue* item = toCSSShadowValue(cssValue);
188            IntPoint location(item->x->computeLength<int>(conversionData), item->y->computeLength<int>(conversionData));
189            int blur = item->blur ? item->blur->computeLength<int>(conversionData) : 0;
190            Color shadowColor = Color::transparent;
191            if (item->color)
192                shadowColor = state.document().textLinkColors().colorFromPrimitiveValue(item->color.get(), state.style()->color());
193
194            operations.operations().append(DropShadowFilterOperation::create(location, blur, shadowColor));
195            break;
196        }
197        case CSSFilterValue::UnknownFilterOperation:
198        default:
199            ASSERT_NOT_REACHED();
200            break;
201        }
202    }
203
204    outOperations = operations;
205    return true;
206}
207
208} // namespace blink
209