1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/render_surface_filters.h"
7 #include "base/logging.h"
8 #include "skia/ext/refptr.h"
9 #include "third_party/WebKit/Source/Platform/chromium/public/WebFilterOperation.h"
10 #include "third_party/WebKit/Source/Platform/chromium/public/WebFilterOperations.h"
11 #include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
12 #include "third_party/skia/include/core/SkCanvas.h"
13 #include "third_party/skia/include/effects/SkBlurImageFilter.h"
14 #include "third_party/skia/include/effects/SkColorMatrixFilter.h"
15 #include "third_party/skia/include/effects/SkMagnifierImageFilter.h"
16 #include "third_party/skia/include/gpu/SkGpuDevice.h"
17 #include "third_party/skia/include/gpu/SkGrPixelRef.h"
18 #include "ui/gfx/size_f.h"
24 void getBrightnessMatrix(float amount
, SkScalar matrix
[20])
26 memset(matrix
, 0, 20 * sizeof(SkScalar
));
27 // Old implementation, a la the draft spec, a straight-up scale,
28 // representing <feFunc[R|G|B] type="linear" slope="[amount]">
29 // (See http://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#brightnessEquivalent)
30 // matrix[0] = matrix[6] = matrix[12] = amount;
32 // New implementation, a translation in color space, representing
33 // <feFunc[R|G|B] type="linear" intercept="[amount]"/>
34 // (See https://www.w3.org/Bugs/Public/show_bug.cgi?id=15647)
35 matrix
[0] = matrix
[6] = matrix
[12] = matrix
[18] = 1;
36 matrix
[4] = matrix
[9] = matrix
[14] = amount
* 255;
39 void getContrastMatrix(float amount
, SkScalar matrix
[20])
41 memset(matrix
, 0, 20 * sizeof(SkScalar
));
42 matrix
[0] = matrix
[6] = matrix
[12] = amount
;
43 matrix
[4] = matrix
[9] = matrix
[14] = (-0.5f
* amount
+ 0.5f
) * 255;
47 void getSaturateMatrix(float amount
, SkScalar matrix
[20])
49 // Note, these values are computed to ensure matrixNeedsClamping is false
50 // for amount in [0..1]
51 matrix
[0] = 0.213f
+ 0.787f
* amount
;
52 matrix
[1] = 0.715f
- 0.715f
* amount
;
53 matrix
[2] = 1.f
- (matrix
[0] + matrix
[1]);
54 matrix
[3] = matrix
[4] = 0;
55 matrix
[5] = 0.213f
- 0.213f
* amount
;
56 matrix
[6] = 0.715f
+ 0.285f
* amount
;
57 matrix
[7] = 1.f
- (matrix
[5] + matrix
[6]);
58 matrix
[8] = matrix
[9] = 0;
59 matrix
[10] = 0.213f
- 0.213f
* amount
;
60 matrix
[11] = 0.715f
- 0.715f
* amount
;
61 matrix
[12] = 1.f
- (matrix
[10] + matrix
[11]);
62 matrix
[13] = matrix
[14] = 0;
63 matrix
[15] = matrix
[16] = matrix
[17] = matrix
[19] = 0;
67 void getHueRotateMatrix(float hue
, SkScalar matrix
[20])
69 const float kPi
= 3.1415926535897932384626433832795f
;
71 float cosHue
= cosf(hue
* kPi
/ 180);
72 float sinHue
= sinf(hue
* kPi
/ 180);
73 matrix
[0] = 0.213f
+ cosHue
* 0.787f
- sinHue
* 0.213f
;
74 matrix
[1] = 0.715f
- cosHue
* 0.715f
- sinHue
* 0.715f
;
75 matrix
[2] = 0.072f
- cosHue
* 0.072f
+ sinHue
* 0.928f
;
76 matrix
[3] = matrix
[4] = 0;
77 matrix
[5] = 0.213f
- cosHue
* 0.213f
+ sinHue
* 0.143f
;
78 matrix
[6] = 0.715f
+ cosHue
* 0.285f
+ sinHue
* 0.140f
;
79 matrix
[7] = 0.072f
- cosHue
* 0.072f
- sinHue
* 0.283f
;
80 matrix
[8] = matrix
[9] = 0;
81 matrix
[10] = 0.213f
- cosHue
* 0.213f
- sinHue
* 0.787f
;
82 matrix
[11] = 0.715f
- cosHue
* 0.715f
+ sinHue
* 0.715f
;
83 matrix
[12] = 0.072f
+ cosHue
* 0.928f
+ sinHue
* 0.072f
;
84 matrix
[13] = matrix
[14] = 0;
85 matrix
[15] = matrix
[16] = matrix
[17] = 0;
90 void getInvertMatrix(float amount
, SkScalar matrix
[20])
92 memset(matrix
, 0, 20 * sizeof(SkScalar
));
93 matrix
[0] = matrix
[6] = matrix
[12] = 1 - 2 * amount
;
94 matrix
[4] = matrix
[9] = matrix
[14] = amount
* 255;
98 void getOpacityMatrix(float amount
, SkScalar matrix
[20])
100 memset(matrix
, 0, 20 * sizeof(SkScalar
));
101 matrix
[0] = matrix
[6] = matrix
[12] = 1;
105 void getGrayscaleMatrix(float amount
, SkScalar matrix
[20])
107 // Note, these values are computed to ensure matrixNeedsClamping is false
108 // for amount in [0..1]
109 matrix
[0] = 0.2126f
+ 0.7874f
* amount
;
110 matrix
[1] = 0.7152f
- 0.7152f
* amount
;
111 matrix
[2] = 1.f
- (matrix
[0] + matrix
[1]);
112 matrix
[3] = matrix
[4] = 0;
114 matrix
[5] = 0.2126f
- 0.2126f
* amount
;
115 matrix
[6] = 0.7152f
+ 0.2848f
* amount
;
116 matrix
[7] = 1.f
- (matrix
[5] + matrix
[6]);
117 matrix
[8] = matrix
[9] = 0;
119 matrix
[10] = 0.2126f
- 0.2126f
* amount
;
120 matrix
[11] = 0.7152f
- 0.7152f
* amount
;
121 matrix
[12] = 1.f
- (matrix
[10] + matrix
[11]);
122 matrix
[13] = matrix
[14] = 0;
124 matrix
[15] = matrix
[16] = matrix
[17] = matrix
[19] = 0;
128 void getSepiaMatrix(float amount
, SkScalar matrix
[20])
130 matrix
[0] = 0.393f
+ 0.607f
* amount
;
131 matrix
[1] = 0.769f
- 0.769f
* amount
;
132 matrix
[2] = 0.189f
- 0.189f
* amount
;
133 matrix
[3] = matrix
[4] = 0;
135 matrix
[5] = 0.349f
- 0.349f
* amount
;
136 matrix
[6] = 0.686f
+ 0.314f
* amount
;
137 matrix
[7] = 0.168f
- 0.168f
* amount
;
138 matrix
[8] = matrix
[9] = 0;
140 matrix
[10] = 0.272f
- 0.272f
* amount
;
141 matrix
[11] = 0.534f
- 0.534f
* amount
;
142 matrix
[12] = 0.131f
+ 0.869f
* amount
;
143 matrix
[13] = matrix
[14] = 0;
145 matrix
[15] = matrix
[16] = matrix
[17] = matrix
[19] = 0;
149 // The 5x4 matrix is really a "compressed" version of a 5x5 matrix that'd have
150 // (0 0 0 0 1) as a last row, and that would be applied to a 5-vector extended
151 // from the 4-vector color with a 1.
152 void multColorMatrix(SkScalar a
[20], SkScalar b
[20], SkScalar out
[20])
154 for (int j
= 0; j
< 4; ++j
) {
155 for (int i
= 0; i
< 5; ++i
) {
156 out
[i
+j
*5] = i
== 4 ? a
[4+j
*5] : 0;
157 for (int k
= 0; k
< 4; ++k
)
158 out
[i
+j
*5] += a
[k
+j
*5] * b
[i
+k
*5];
163 // To detect if we need to apply clamping after applying a matrix, we check if
164 // any output component might go outside of [0, 255] for any combination of
165 // input components in [0..255].
166 // Each output component is an affine transformation of the input component, so
167 // the minimum and maximum values are for any combination of minimum or maximum
168 // values of input components (i.e. 0 or 255).
169 // E.g. if R' = x*R + y*G + z*B + w*A + t
170 // Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the
171 // minimum value will be for R=0 if x>0 or R=255 if x<0.
172 // Same goes for all components.
173 bool componentNeedsClamping(SkScalar row
[5])
175 SkScalar maxValue
= row
[4] / 255;
176 SkScalar minValue
= row
[4] / 255;
177 for (int i
= 0; i
< 4; ++i
) {
183 return (maxValue
> 1) || (minValue
< 0);
186 bool matrixNeedsClamping(SkScalar matrix
[20])
188 return componentNeedsClamping(matrix
)
189 || componentNeedsClamping(matrix
+5)
190 || componentNeedsClamping(matrix
+10)
191 || componentNeedsClamping(matrix
+15);
194 bool getColorMatrix(const WebKit::WebFilterOperation
& op
, SkScalar matrix
[20])
197 case WebKit::WebFilterOperation::FilterTypeBrightness
: {
198 getBrightnessMatrix(op
.amount(), matrix
);
201 case WebKit::WebFilterOperation::FilterTypeContrast
: {
202 getContrastMatrix(op
.amount(), matrix
);
205 case WebKit::WebFilterOperation::FilterTypeGrayscale
: {
206 getGrayscaleMatrix(1 - op
.amount(), matrix
);
209 case WebKit::WebFilterOperation::FilterTypeSepia
: {
210 getSepiaMatrix(1 - op
.amount(), matrix
);
213 case WebKit::WebFilterOperation::FilterTypeSaturate
: {
214 getSaturateMatrix(op
.amount(), matrix
);
217 case WebKit::WebFilterOperation::FilterTypeHueRotate
: {
218 getHueRotateMatrix(op
.amount(), matrix
);
221 case WebKit::WebFilterOperation::FilterTypeInvert
: {
222 getInvertMatrix(op
.amount(), matrix
);
225 case WebKit::WebFilterOperation::FilterTypeOpacity
: {
226 getOpacityMatrix(op
.amount(), matrix
);
229 case WebKit::WebFilterOperation::FilterTypeColorMatrix
: {
230 memcpy(matrix
, op
.matrix(), sizeof(SkScalar
[20]));
238 class FilterBufferState
{
240 FilterBufferState(GrContext
* grContext
, const gfx::SizeF
& size
, unsigned textureId
)
241 : m_grContext(grContext
)
242 , m_currentTexture(0)
244 // Wrap the source texture in a Ganesh platform texture.
245 GrPlatformTextureDesc platformTextureDescription
;
246 platformTextureDescription
.fWidth
= size
.width();
247 platformTextureDescription
.fHeight
= size
.height();
248 platformTextureDescription
.fConfig
= kSkia8888_GrPixelConfig
;
249 platformTextureDescription
.fTextureHandle
= textureId
;
250 skia::RefPtr
<GrTexture
> texture
= skia::AdoptRef(grContext
->createPlatformTexture(platformTextureDescription
));
251 // Place the platform texture inside an SkBitmap.
252 m_source
.setConfig(SkBitmap::kARGB_8888_Config
, size
.width(), size
.height());
253 skia::RefPtr
<SkGrPixelRef
> pixelRef
= skia::AdoptRef(new SkGrPixelRef(texture
.get()));
254 m_source
.setPixelRef(pixelRef
.get());
257 ~FilterBufferState() { }
259 bool init(int filterCount
)
261 int scratchCount
= std::min(2, filterCount
);
263 desc
.fFlags
= kRenderTarget_GrTextureFlagBit
| kNoStencil_GrTextureFlagBit
;
265 desc
.fWidth
= m_source
.width();
266 desc
.fHeight
= m_source
.height();
267 desc
.fConfig
= kSkia8888_GrPixelConfig
;
268 for (int i
= 0; i
< scratchCount
; ++i
) {
269 GrAutoScratchTexture
scratchTexture(m_grContext
, desc
, GrContext::kExact_ScratchTexMatch
);
270 m_scratchTextures
[i
] = skia::AdoptRef(scratchTexture
.detach());
271 if (!m_scratchTextures
[i
])
281 return m_canvas
.get();
284 const SkBitmap
& source() { return m_source
; }
292 skia::RefPtr
<SkGrPixelRef
> pixelRef
= skia::AdoptRef(new SkGrPixelRef(m_scratchTextures
[m_currentTexture
].get()));
293 m_source
.setPixelRef(pixelRef
.get());
294 m_currentTexture
= 1 - m_currentTexture
;
300 DCHECK(m_scratchTextures
[m_currentTexture
].get());
301 m_device
= skia::AdoptRef(new SkGpuDevice(m_grContext
, m_scratchTextures
[m_currentTexture
].get()));
302 m_canvas
= skia::AdoptRef(new SkCanvas(m_device
.get()));
303 m_canvas
->clear(0x0);
306 GrContext
* m_grContext
;
308 skia::RefPtr
<GrTexture
> m_scratchTextures
[2];
309 int m_currentTexture
;
310 skia::RefPtr
<SkGpuDevice
> m_device
;
311 skia::RefPtr
<SkCanvas
> m_canvas
;
316 WebKit::WebFilterOperations
RenderSurfaceFilters::optimize(const WebKit::WebFilterOperations
& filters
)
318 WebKit::WebFilterOperations newList
;
320 SkScalar accumulatedColorMatrix
[20];
321 bool haveAccumulatedColorMatrix
= false;
322 for (unsigned i
= 0; i
< filters
.size(); ++i
) {
323 const WebKit::WebFilterOperation
& op
= filters
.at(i
);
325 // If the filter is a color matrix, we may be able to combine it with
326 // following filter(s) that also are color matrices.
328 if (getColorMatrix(op
, matrix
)) {
329 if (haveAccumulatedColorMatrix
) {
330 SkScalar newMatrix
[20];
331 multColorMatrix(matrix
, accumulatedColorMatrix
, newMatrix
);
332 memcpy(accumulatedColorMatrix
, newMatrix
, sizeof(accumulatedColorMatrix
));
334 memcpy(accumulatedColorMatrix
, matrix
, sizeof(accumulatedColorMatrix
));
335 haveAccumulatedColorMatrix
= true;
338 // We can only combine matrices if clamping of color components
339 // would have no effect.
340 if (!matrixNeedsClamping(accumulatedColorMatrix
))
344 if (haveAccumulatedColorMatrix
)
345 newList
.append(WebKit::WebFilterOperation::createColorMatrixFilter(accumulatedColorMatrix
));
346 haveAccumulatedColorMatrix
= false;
349 case WebKit::WebFilterOperation::FilterTypeBlur
:
350 case WebKit::WebFilterOperation::FilterTypeDropShadow
:
351 case WebKit::WebFilterOperation::FilterTypeZoom
:
354 case WebKit::WebFilterOperation::FilterTypeBrightness
:
355 case WebKit::WebFilterOperation::FilterTypeContrast
:
356 case WebKit::WebFilterOperation::FilterTypeGrayscale
:
357 case WebKit::WebFilterOperation::FilterTypeSepia
:
358 case WebKit::WebFilterOperation::FilterTypeSaturate
:
359 case WebKit::WebFilterOperation::FilterTypeHueRotate
:
360 case WebKit::WebFilterOperation::FilterTypeInvert
:
361 case WebKit::WebFilterOperation::FilterTypeOpacity
:
362 case WebKit::WebFilterOperation::FilterTypeColorMatrix
:
366 if (haveAccumulatedColorMatrix
)
367 newList
.append(WebKit::WebFilterOperation::createColorMatrixFilter(accumulatedColorMatrix
));
371 SkBitmap
RenderSurfaceFilters::apply(const WebKit::WebFilterOperations
& filters
, unsigned textureId
, const gfx::SizeF
& size
, WebKit::WebGraphicsContext3D
* context3D
, GrContext
* grContext
)
373 if (!context3D
|| !grContext
)
376 WebKit::WebFilterOperations optimizedFilters
= optimize(filters
);
377 FilterBufferState
state(grContext
, size
, textureId
);
378 if (!state
.init(optimizedFilters
.size()))
381 for (unsigned i
= 0; i
< optimizedFilters
.size(); ++i
) {
382 const WebKit::WebFilterOperation
& op
= optimizedFilters
.at(i
);
383 SkCanvas
* canvas
= state
.canvas();
385 case WebKit::WebFilterOperation::FilterTypeColorMatrix
: {
387 skia::RefPtr
<SkColorMatrixFilter
> filter
= skia::AdoptRef(new SkColorMatrixFilter(op
.matrix()));
388 paint
.setColorFilter(filter
.get());
389 canvas
->drawBitmap(state
.source(), 0, 0, &paint
);
392 case WebKit::WebFilterOperation::FilterTypeBlur
: {
393 float stdDeviation
= op
.amount();
394 skia::RefPtr
<SkImageFilter
> filter
= skia::AdoptRef(new SkBlurImageFilter(stdDeviation
, stdDeviation
));
396 paint
.setImageFilter(filter
.get());
397 canvas
->drawSprite(state
.source(), 0, 0, &paint
);
400 case WebKit::WebFilterOperation::FilterTypeDropShadow
: {
401 skia::RefPtr
<SkImageFilter
> blurFilter
= skia::AdoptRef(new SkBlurImageFilter(op
.amount(), op
.amount()));
402 skia::RefPtr
<SkColorFilter
> colorFilter
= skia::AdoptRef(SkColorFilter::CreateModeFilter(op
.dropShadowColor(), SkXfermode::kSrcIn_Mode
));
404 paint
.setImageFilter(blurFilter
.get());
405 paint
.setColorFilter(colorFilter
.get());
406 paint
.setXfermodeMode(SkXfermode::kSrcOver_Mode
);
407 canvas
->saveLayer(0, &paint
);
408 canvas
->drawBitmap(state
.source(), op
.dropShadowOffset().x
, -op
.dropShadowOffset().y
);
410 canvas
->drawBitmap(state
.source(), 0, 0);
413 case WebKit::WebFilterOperation::FilterTypeZoom
: {
415 skia::RefPtr
<SkImageFilter
> zoomFilter
= skia::AdoptRef(
416 new SkMagnifierImageFilter(
417 SkRect::MakeXYWH(op
.zoomRect().x
,
420 op
.zoomRect().height
),
422 paint
.setImageFilter(zoomFilter
.get());
423 canvas
->saveLayer(0, &paint
);
424 canvas
->drawBitmap(state
.source(), 0, 0);
428 case WebKit::WebFilterOperation::FilterTypeBrightness
:
429 case WebKit::WebFilterOperation::FilterTypeContrast
:
430 case WebKit::WebFilterOperation::FilterTypeGrayscale
:
431 case WebKit::WebFilterOperation::FilterTypeSepia
:
432 case WebKit::WebFilterOperation::FilterTypeSaturate
:
433 case WebKit::WebFilterOperation::FilterTypeHueRotate
:
434 case WebKit::WebFilterOperation::FilterTypeInvert
:
435 case WebKit::WebFilterOperation::FilterTypeOpacity
:
442 return state
.source();