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 "SkMatrixImageFilter.h"
34#include "SkTableColorFilter.h"
35#include "platform/graphics/ImageBuffer.h"
36#include "platform/graphics/filters/FilterEffect.h"
37#include "platform/graphics/filters/FilterOperations.h"
38#include "platform/graphics/filters/SourceGraphic.h"
39#include "platform/graphics/skia/SkiaUtils.h"
40#include "public/platform/WebPoint.h"
41
42namespace blink {
43
44SkiaImageFilterBuilder::SkiaImageFilterBuilder()
45    : m_context(0)
46    , m_sourceGraphic(0)
47{
48}
49
50SkiaImageFilterBuilder::SkiaImageFilterBuilder(GraphicsContext* context)
51    : m_context(context)
52    , m_sourceGraphic(0)
53{
54}
55
56SkiaImageFilterBuilder::~SkiaImageFilterBuilder()
57{
58}
59
60PassRefPtr<SkImageFilter> SkiaImageFilterBuilder::build(FilterEffect* effect, ColorSpace colorSpace, bool destinationRequiresValidPreMultipliedPixels)
61{
62    if (!effect)
63        return nullptr;
64
65    bool requiresPMColorValidation = effect->mayProduceInvalidPreMultipliedPixels() && destinationRequiresValidPreMultipliedPixels;
66
67    if (SkImageFilter* filter = effect->getImageFilter(colorSpace, requiresPMColorValidation))
68        return filter;
69
70    // Note that we may still need the color transform even if the filter is null
71    RefPtr<SkImageFilter> origFilter = requiresPMColorValidation ? effect->createImageFilter(this) : effect->createImageFilterWithoutValidation(this);
72    RefPtr<SkImageFilter> filter = transformColorSpace(origFilter.get(), effect->operatingColorSpace(), colorSpace);
73    effect->setImageFilter(colorSpace, requiresPMColorValidation, filter.get());
74    if (filter.get() != origFilter.get())
75        effect->setImageFilter(effect->operatingColorSpace(), requiresPMColorValidation, origFilter.get());
76    return filter.release();
77}
78
79PassRefPtr<SkImageFilter> SkiaImageFilterBuilder::transformColorSpace(
80    SkImageFilter* input, ColorSpace srcColorSpace, ColorSpace dstColorSpace) {
81
82    RefPtr<SkColorFilter> colorFilter = ImageBuffer::createColorSpaceFilter(srcColorSpace, dstColorSpace);
83    if (!colorFilter)
84        return input;
85
86    return adoptRef(SkColorFilterImageFilter::Create(colorFilter.get(), input));
87}
88
89void SkiaImageFilterBuilder::buildFilterOperations(const FilterOperations& operations, WebFilterOperations* filters)
90{
91    ColorSpace currentColorSpace = ColorSpaceDeviceRGB;
92    SkImageFilter* const nullFilter = 0;
93
94    for (size_t i = 0; i < operations.size(); ++i) {
95        const FilterOperation& op = *operations.at(i);
96        switch (op.type()) {
97        case FilterOperation::REFERENCE: {
98            RefPtr<SkImageFilter> filter;
99            ReferenceFilter* referenceFilter = toReferenceFilterOperation(op).filter();
100            if (referenceFilter && referenceFilter->lastEffect()) {
101                FilterEffect* filterEffect = referenceFilter->lastEffect();
102                // Prepopulate SourceGraphic with two image filters: one with a null image
103                // filter, and the other with a colorspace conversion filter.
104                // We don't know what color space the interior nodes will request, so we have to
105                // initialize SourceGraphic with both options.
106                // Since we know SourceGraphic is always PM-valid, we also use
107                // these for the PM-validated options.
108                RefPtr<SkImageFilter> deviceFilter = transformColorSpace(nullFilter, currentColorSpace, ColorSpaceDeviceRGB);
109                RefPtr<SkImageFilter> linearFilter = transformColorSpace(nullFilter, currentColorSpace, ColorSpaceLinearRGB);
110                FilterEffect* sourceGraphic = referenceFilter->sourceGraphic();
111                sourceGraphic->setImageFilter(ColorSpaceDeviceRGB, false, deviceFilter.get());
112                sourceGraphic->setImageFilter(ColorSpaceLinearRGB, false, linearFilter.get());
113                sourceGraphic->setImageFilter(ColorSpaceDeviceRGB, true, deviceFilter.get());
114                sourceGraphic->setImageFilter(ColorSpaceLinearRGB, true, linearFilter.get());
115
116                currentColorSpace = filterEffect->operatingColorSpace();
117                filter = SkiaImageFilterBuilder::build(filterEffect, currentColorSpace);
118                filters->appendReferenceFilter(filter.get());
119            }
120            break;
121        }
122        case FilterOperation::GRAYSCALE:
123        case FilterOperation::SEPIA:
124        case FilterOperation::SATURATE:
125        case FilterOperation::HUE_ROTATE: {
126            float amount = toBasicColorMatrixFilterOperation(op).amount();
127            switch (op.type()) {
128            case FilterOperation::GRAYSCALE:
129                filters->appendGrayscaleFilter(amount);
130                break;
131            case FilterOperation::SEPIA:
132                filters->appendSepiaFilter(amount);
133                break;
134            case FilterOperation::SATURATE:
135                filters->appendSaturateFilter(amount);
136                break;
137            case FilterOperation::HUE_ROTATE:
138                filters->appendHueRotateFilter(amount);
139                break;
140            default:
141                ASSERT_NOT_REACHED();
142            }
143            break;
144        }
145        case FilterOperation::INVERT:
146        case FilterOperation::OPACITY:
147        case FilterOperation::BRIGHTNESS:
148        case FilterOperation::CONTRAST: {
149            float amount = toBasicComponentTransferFilterOperation(op).amount();
150            switch (op.type()) {
151            case FilterOperation::INVERT:
152                filters->appendInvertFilter(amount);
153                break;
154            case FilterOperation::OPACITY:
155                filters->appendOpacityFilter(amount);
156                break;
157            case FilterOperation::BRIGHTNESS:
158                filters->appendBrightnessFilter(amount);
159                break;
160            case FilterOperation::CONTRAST:
161                filters->appendContrastFilter(amount);
162                break;
163            default:
164                ASSERT_NOT_REACHED();
165            }
166            break;
167        }
168        case FilterOperation::BLUR: {
169            float pixelRadius = toBlurFilterOperation(op).stdDeviation().getFloatValue();
170            filters->appendBlurFilter(pixelRadius);
171            break;
172        }
173        case FilterOperation::DROP_SHADOW: {
174            const DropShadowFilterOperation& drop = toDropShadowFilterOperation(op);
175            filters->appendDropShadowFilter(WebPoint(drop.x(), drop.y()), drop.stdDeviation(), drop.color().rgb());
176            break;
177        }
178        case FilterOperation::NONE:
179            break;
180        }
181    }
182    if (currentColorSpace != ColorSpaceDeviceRGB) {
183        // Transform to device color space at the end of processing, if required
184        RefPtr<SkImageFilter> filter = transformColorSpace(nullFilter, currentColorSpace, ColorSpaceDeviceRGB);
185        filters->appendReferenceFilter(filter.get());
186    }
187}
188
189PassRefPtr<SkImageFilter> SkiaImageFilterBuilder::buildTransform(const AffineTransform& transform, SkImageFilter* input)
190{
191    return adoptRef(SkMatrixImageFilter::Create(affineTransformToSkMatrix(transform), SkPaint::kHigh_FilterLevel, input));
192}
193
194} // namespace blink
195