1/*
2 * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
4 * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
5 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
6 * Copyright (C) 2013 Google Inc. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB.  If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#include "config.h"
25#include "platform/graphics/filters/FEColorMatrix.h"
26
27#include "SkColorFilterImageFilter.h"
28#include "SkColorMatrixFilter.h"
29#include "platform/graphics/GraphicsContext.h"
30#include "platform/graphics/filters/SkiaImageFilterBuilder.h"
31#include "platform/graphics/skia/NativeImageSkia.h"
32#include "platform/text/TextStream.h"
33#include "wtf/MathExtras.h"
34#include "wtf/Uint8ClampedArray.h"
35
36namespace blink {
37
38FEColorMatrix::FEColorMatrix(Filter* filter, ColorMatrixType type, const Vector<float>& values)
39    : FilterEffect(filter)
40    , m_type(type)
41    , m_values(values)
42{
43}
44
45PassRefPtr<FEColorMatrix> FEColorMatrix::create(Filter* filter, ColorMatrixType type, const Vector<float>& values)
46{
47    return adoptRef(new FEColorMatrix(filter, type, values));
48}
49
50ColorMatrixType FEColorMatrix::type() const
51{
52    return m_type;
53}
54
55bool FEColorMatrix::setType(ColorMatrixType type)
56{
57    if (m_type == type)
58        return false;
59    m_type = type;
60    return true;
61}
62
63const Vector<float>& FEColorMatrix::values() const
64{
65    return m_values;
66}
67
68bool FEColorMatrix::setValues(const Vector<float> &values)
69{
70    if (m_values == values)
71        return false;
72    m_values = values;
73    return true;
74}
75
76static void saturateMatrix(float s, SkScalar matrix[20])
77{
78    matrix[0] = 0.213f + 0.787f * s;
79    matrix[1] = 0.715f - 0.715f * s;
80    matrix[2] = 0.072f - 0.072f * s;
81    matrix[3] = matrix[4] = 0;
82    matrix[5] = 0.213f - 0.213f * s;
83    matrix[6] = 0.715f + 0.285f * s;
84    matrix[7] = 0.072f - 0.072f * s;
85    matrix[8] = matrix[9] = 0;
86    matrix[10] = 0.213f - 0.213f * s;
87    matrix[11] = 0.715f - 0.715f * s;
88    matrix[12] = 0.072f + 0.928f * s;
89    matrix[13] = matrix[14] = 0;
90    matrix[15] = matrix[16] = matrix[17] = 0;
91    matrix[18] = 1;
92    matrix[19] = 0;
93}
94
95static void hueRotateMatrix(float hue, SkScalar matrix[20])
96{
97    float cosHue = cosf(hue * piFloat / 180);
98    float sinHue = sinf(hue * piFloat / 180);
99    matrix[0] = 0.213f + cosHue * 0.787f - sinHue * 0.213f;
100    matrix[1] = 0.715f - cosHue * 0.715f - sinHue * 0.715f;
101    matrix[2] = 0.072f - cosHue * 0.072f + sinHue * 0.928f;
102    matrix[3] = matrix[4] = 0;
103    matrix[5] = 0.213f - cosHue * 0.213f + sinHue * 0.143f;
104    matrix[6] = 0.715f + cosHue * 0.285f + sinHue * 0.140f;
105    matrix[7] = 0.072f - cosHue * 0.072f - sinHue * 0.283f;
106    matrix[8] = matrix[9] = 0;
107    matrix[10] = 0.213f - cosHue * 0.213f - sinHue * 0.787f;
108    matrix[11] = 0.715f - cosHue * 0.715f + sinHue * 0.715f;
109    matrix[12] = 0.072f + cosHue * 0.928f + sinHue * 0.072f;
110    matrix[13] = matrix[14] = 0;
111    matrix[15] = matrix[16] = matrix[17] = 0;
112    matrix[18] = 1;
113    matrix[19] = 0;
114}
115
116static void luminanceToAlphaMatrix(SkScalar matrix[20])
117{
118    memset(matrix, 0, 20 * sizeof(SkScalar));
119    matrix[15] = 0.2125f;
120    matrix[16] = 0.7154f;
121    matrix[17] = 0.0721f;
122}
123
124static SkColorFilter* createColorFilter(ColorMatrixType type, const float* values)
125{
126    SkScalar matrix[20];
127    switch (type) {
128    case FECOLORMATRIX_TYPE_UNKNOWN:
129        break;
130    case FECOLORMATRIX_TYPE_MATRIX:
131        for (int i = 0; i < 20; ++i)
132            matrix[i] = values[i];
133
134        matrix[4] *= SkScalar(255);
135        matrix[9] *= SkScalar(255);
136        matrix[14] *= SkScalar(255);
137        matrix[19] *= SkScalar(255);
138        break;
139    case FECOLORMATRIX_TYPE_SATURATE:
140        saturateMatrix(values[0], matrix);
141        break;
142    case FECOLORMATRIX_TYPE_HUEROTATE:
143        hueRotateMatrix(values[0], matrix);
144        break;
145    case FECOLORMATRIX_TYPE_LUMINANCETOALPHA:
146        luminanceToAlphaMatrix(matrix);
147        break;
148    }
149    return SkColorMatrixFilter::Create(matrix);
150}
151
152void FEColorMatrix::applySoftware()
153{
154    ImageBuffer* resultImage = createImageBufferResult();
155    if (!resultImage)
156        return;
157
158    FilterEffect* in = inputEffect(0);
159
160    IntRect drawingRegion = drawingRegionOfInputImage(in->absolutePaintRect());
161
162    SkAutoTUnref<SkColorFilter> filter(createColorFilter(m_type, m_values.data()));
163
164    RefPtr<Image> image = in->asImageBuffer()->copyImage(DontCopyBackingStore);
165    RefPtr<NativeImageSkia> nativeImage = image->nativeImageForCurrentFrame();
166    if (!nativeImage)
167        return;
168
169    SkPaint paint;
170    paint.setColorFilter(filter);
171    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
172    resultImage->context()->drawBitmap(nativeImage->bitmap(), drawingRegion.x(), drawingRegion.y(), &paint);
173
174    if (affectsTransparentPixels()) {
175        IntRect fullRect = IntRect(IntPoint(), absolutePaintRect().size());
176        resultImage->context()->clipOut(drawingRegion);
177        resultImage->context()->fillRect(fullRect, Color(m_values[4], m_values[9], m_values[14], m_values[19]));
178    }
179    return;
180}
181
182bool FEColorMatrix::affectsTransparentPixels()
183{
184    // Because the input pixels are premultiplied, the only way clear pixels can be
185    // painted is if the additive component for the alpha is not 0.
186    return m_type == FECOLORMATRIX_TYPE_MATRIX && m_values[19] > 0;
187}
188
189PassRefPtr<SkImageFilter> FEColorMatrix::createImageFilter(SkiaImageFilterBuilder* builder)
190{
191    RefPtr<SkImageFilter> input(builder->build(inputEffect(0), operatingColorSpace()));
192    SkAutoTUnref<SkColorFilter> filter(createColorFilter(m_type, m_values.data()));
193    SkImageFilter::CropRect rect = getCropRect(builder->cropOffset());
194    return adoptRef(SkColorFilterImageFilter::Create(filter, input.get(), &rect));
195}
196
197static TextStream& operator<<(TextStream& ts, const ColorMatrixType& type)
198{
199    switch (type) {
200    case FECOLORMATRIX_TYPE_UNKNOWN:
201        ts << "UNKNOWN";
202        break;
203    case FECOLORMATRIX_TYPE_MATRIX:
204        ts << "MATRIX";
205        break;
206    case FECOLORMATRIX_TYPE_SATURATE:
207        ts << "SATURATE";
208        break;
209    case FECOLORMATRIX_TYPE_HUEROTATE:
210        ts << "HUEROTATE";
211        break;
212    case FECOLORMATRIX_TYPE_LUMINANCETOALPHA:
213        ts << "LUMINANCETOALPHA";
214        break;
215    }
216    return ts;
217}
218
219TextStream& FEColorMatrix::externalRepresentation(TextStream& ts, int indent) const
220{
221    writeIndent(ts, indent);
222    ts << "[feColorMatrix";
223    FilterEffect::externalRepresentation(ts);
224    ts << " type=\"" << m_type << "\"";
225    if (!m_values.isEmpty()) {
226        ts << " values=\"";
227        Vector<float>::const_iterator ptr = m_values.begin();
228        const Vector<float>::const_iterator end = m_values.end();
229        while (ptr < end) {
230            ts << *ptr;
231            ++ptr;
232            if (ptr < end)
233                ts << " ";
234        }
235        ts << "\"";
236    }
237    ts << "]\n";
238    inputEffect(0)->externalRepresentation(ts, indent + 1);
239    return ts;
240}
241
242} // namespace blink
243