1/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25#include "config.h"
26
27#include "platform/graphics/filters/SkiaImageFilterBuilder.h"
28
29#include "SkBlurImageFilter.h"
30#include "SkColorFilterImageFilter.h"
31#include "SkColorMatrixFilter.h"
32#include "SkDropShadowImageFilter.h"
33#include "SkTableColorFilter.h"
34#include "platform/graphics/ImageBuffer.h"
35#include "platform/graphics/filters/FilterEffect.h"
36#include "platform/graphics/filters/FilterOperations.h"
37#include "platform/graphics/filters/SourceGraphic.h"
38#include "public/platform/WebPoint.h"
39
40namespace {
41
42PassRefPtr<SkImageFilter> createMatrixImageFilter(SkScalar matrix[20], SkImageFilter* input)
43{
44    RefPtr<SkColorFilter> colorFilter(adoptRef(new SkColorMatrixFilter(matrix)));
45    return adoptRef(SkColorFilterImageFilter::Create(colorFilter.get(), input));
46}
47
48};
49
50namespace WebCore {
51
52SkiaImageFilterBuilder::SkiaImageFilterBuilder()
53{
54}
55
56SkiaImageFilterBuilder::~SkiaImageFilterBuilder()
57{
58}
59
60PassRefPtr<SkImageFilter> SkiaImageFilterBuilder::build(FilterEffect* effect, ColorSpace colorSpace)
61{
62    if (!effect)
63        return 0;
64
65    FilterColorSpacePair key(effect, colorSpace);
66    FilterBuilderHashMap::iterator it = m_map.find(key);
67    if (it != m_map.end()) {
68        return it->value;
69    } else {
70        // Note that we may still need the color transform even if the filter is null
71        RefPtr<SkImageFilter> origFilter = effect->createImageFilter(this);
72        RefPtr<SkImageFilter> filter = transformColorSpace(origFilter.get(), effect->operatingColorSpace(), colorSpace);
73        m_map.set(key, filter);
74        return filter.release();
75    }
76}
77
78PassRefPtr<SkImageFilter> SkiaImageFilterBuilder::transformColorSpace(
79    SkImageFilter* input, ColorSpace srcColorSpace, ColorSpace dstColorSpace) {
80
81    RefPtr<SkColorFilter> colorFilter = ImageBuffer::createColorSpaceFilter(srcColorSpace, dstColorSpace);
82    if (!colorFilter)
83        return input;
84
85    return adoptRef(SkColorFilterImageFilter::Create(colorFilter.get(), input));
86}
87
88bool SkiaImageFilterBuilder::buildFilterOperations(const FilterOperations& operations, blink::WebFilterOperations* filters)
89{
90    if (!filters)
91        return false;
92
93    ColorSpace currentColorSpace = ColorSpaceDeviceRGB;
94
95    RefPtr<SkImageFilter> noopFilter;
96    SkScalar matrix[20];
97    memset(matrix, 0, 20 * sizeof(SkScalar));
98    matrix[0] = matrix[6] = matrix[12] = matrix[18] = 1.f;
99    noopFilter = createMatrixImageFilter(matrix, 0);
100
101    for (size_t i = 0; i < operations.size(); ++i) {
102        const FilterOperation& op = *operations.at(i);
103        switch (op.type()) {
104        case FilterOperation::REFERENCE: {
105            RefPtr<SkImageFilter> filter;
106            ReferenceFilter* referenceFilter = toReferenceFilterOperation(op).filter();
107            if (referenceFilter && referenceFilter->lastEffect()) {
108                FilterEffect* filterEffect = referenceFilter->lastEffect();
109                // Link SourceGraphic to a noop filter that serves as a placholder for
110                // the previous filter in the chain. We don't know what color space the
111                // interior nodes will request, so we have to populate the map with both
112                // options. (Only one of these will actually have a color transform on it.)
113                FilterColorSpacePair deviceKey(referenceFilter->sourceGraphic(), ColorSpaceDeviceRGB);
114                FilterColorSpacePair linearKey(referenceFilter->sourceGraphic(), ColorSpaceLinearRGB);
115                m_map.set(deviceKey, transformColorSpace(noopFilter.get(), currentColorSpace, ColorSpaceDeviceRGB));
116                m_map.set(linearKey, transformColorSpace(noopFilter.get(), currentColorSpace, ColorSpaceLinearRGB));
117
118                currentColorSpace = filterEffect->operatingColorSpace();
119                filter = SkiaImageFilterBuilder::build(filterEffect, currentColorSpace);
120                // We might have no reference to the SourceGraphic's Skia filter now, so make
121                // sure we don't keep it in the map anymore.
122                m_map.remove(deviceKey);
123                m_map.remove(linearKey);
124                filters->appendReferenceFilter(filter.get());
125            }
126            break;
127        }
128        case FilterOperation::GRAYSCALE:
129        case FilterOperation::SEPIA:
130        case FilterOperation::SATURATE:
131        case FilterOperation::HUE_ROTATE: {
132            float amount = toBasicColorMatrixFilterOperation(op).amount();
133            switch (op.type()) {
134            case FilterOperation::GRAYSCALE:
135                filters->appendGrayscaleFilter(amount);
136                break;
137            case FilterOperation::SEPIA:
138                filters->appendSepiaFilter(amount);
139                break;
140            case FilterOperation::SATURATE:
141                filters->appendSaturateFilter(amount);
142                break;
143            case FilterOperation::HUE_ROTATE:
144                filters->appendHueRotateFilter(amount);
145                break;
146            default:
147                ASSERT_NOT_REACHED();
148            }
149            break;
150        }
151        case FilterOperation::INVERT:
152        case FilterOperation::OPACITY:
153        case FilterOperation::BRIGHTNESS:
154        case FilterOperation::CONTRAST: {
155            float amount = toBasicComponentTransferFilterOperation(op).amount();
156            switch (op.type()) {
157            case FilterOperation::INVERT:
158                filters->appendInvertFilter(amount);
159                break;
160            case FilterOperation::OPACITY:
161                filters->appendOpacityFilter(amount);
162                break;
163            case FilterOperation::BRIGHTNESS:
164                filters->appendBrightnessFilter(amount);
165                break;
166            case FilterOperation::CONTRAST:
167                filters->appendContrastFilter(amount);
168                break;
169            default:
170                ASSERT_NOT_REACHED();
171            }
172            break;
173        }
174        case FilterOperation::BLUR: {
175            float pixelRadius = toBlurFilterOperation(op).stdDeviation().getFloatValue();
176            filters->appendBlurFilter(pixelRadius);
177            break;
178        }
179        case FilterOperation::DROP_SHADOW: {
180            const DropShadowFilterOperation& drop = toDropShadowFilterOperation(op);
181            filters->appendDropShadowFilter(blink::WebPoint(drop.x(), drop.y()), drop.stdDeviation(), drop.color().rgb());
182            break;
183        }
184        case FilterOperation::VALIDATED_CUSTOM:
185        case FilterOperation::CUSTOM:
186            return false; // Not supported.
187        case FilterOperation::NONE:
188            break;
189        }
190    }
191    if (currentColorSpace != ColorSpaceDeviceRGB) {
192        // Transform to device color space at the end of processing, if required
193        RefPtr<SkImageFilter> filter;
194        filter = transformColorSpace(noopFilter.get(), currentColorSpace, ColorSpaceDeviceRGB);
195        if (filter != noopFilter)
196            filters->appendReferenceFilter(filter.get());
197    }
198    return true;
199}
200
201};
202