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/output/render_surface_filters.h"
9 #include "base/logging.h"
10 #include "skia/ext/refptr.h"
11 #include "third_party/WebKit/Source/Platform/chromium/public/WebFilterOperation.h"
12 #include "third_party/WebKit/Source/Platform/chromium/public/WebFilterOperations.h"
13 #include "third_party/skia/include/core/SkCanvas.h"
14 #include "third_party/skia/include/effects/SkBlurImageFilter.h"
15 #include "third_party/skia/include/effects/SkColorMatrixFilter.h"
16 #include "third_party/skia/include/effects/SkMagnifierImageFilter.h"
17 #include "third_party/skia/include/gpu/SkGpuDevice.h"
18 #include "third_party/skia/include/gpu/SkGrPixelRef.h"
19 #include "ui/gfx/size_f.h"
25 void GetBrightnessMatrix(float amount
, SkScalar matrix
[20]) {
26 // Spec implementation
27 // (http://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#brightnessEquivalent)
28 // <feFunc[R|G|B] type="linear" slope="[amount]">
29 memset(matrix
, 0, 20 * sizeof(SkScalar
));
30 matrix
[0] = matrix
[6] = matrix
[12] = amount
;
34 void GetSaturatingBrightnessMatrix(float amount
, SkScalar matrix
[20]) {
35 // Legacy implementation used by internal clients.
36 // <feFunc[R|G|B] type="linear" intercept="[amount]"/>
37 memset(matrix
, 0, 20 * sizeof(SkScalar
));
38 matrix
[0] = matrix
[6] = matrix
[12] = matrix
[18] = 1.f
;
39 matrix
[4] = matrix
[9] = matrix
[14] = amount
* 255.f
;
42 void GetContrastMatrix(float amount
, SkScalar matrix
[20]) {
43 memset(matrix
, 0, 20 * sizeof(SkScalar
));
44 matrix
[0] = matrix
[6] = matrix
[12] = amount
;
45 matrix
[4] = matrix
[9] = matrix
[14] = (-0.5f
* amount
+ 0.5f
) * 255.f
;
49 void GetSaturateMatrix(float amount
, SkScalar matrix
[20]) {
50 // Note, these values are computed to ensure MatrixNeedsClamping is false
51 // for amount in [0..1]
52 matrix
[0] = 0.213f
+ 0.787f
* amount
;
53 matrix
[1] = 0.715f
- 0.715f
* amount
;
54 matrix
[2] = 1.f
- (matrix
[0] + matrix
[1]);
55 matrix
[3] = matrix
[4] = 0.f
;
56 matrix
[5] = 0.213f
- 0.213f
* amount
;
57 matrix
[6] = 0.715f
+ 0.285f
* amount
;
58 matrix
[7] = 1.f
- (matrix
[5] + matrix
[6]);
59 matrix
[8] = matrix
[9] = 0.f
;
60 matrix
[10] = 0.213f
- 0.213f
* amount
;
61 matrix
[11] = 0.715f
- 0.715f
* amount
;
62 matrix
[12] = 1.f
- (matrix
[10] + matrix
[11]);
63 matrix
[13] = matrix
[14] = 0.f
;
64 matrix
[15] = matrix
[16] = matrix
[17] = matrix
[19] = 0.f
;
68 void GetHueRotateMatrix(float hue
, SkScalar matrix
[20]) {
69 const float kPi
= 3.1415926535897932384626433832795f
;
71 float cos_hue
= cosf(hue
* kPi
/ 180.f
);
72 float sin_hue
= sinf(hue
* kPi
/ 180.f
);
73 matrix
[0] = 0.213f
+ cos_hue
* 0.787f
- sin_hue
* 0.213f
;
74 matrix
[1] = 0.715f
- cos_hue
* 0.715f
- sin_hue
* 0.715f
;
75 matrix
[2] = 0.072f
- cos_hue
* 0.072f
+ sin_hue
* 0.928f
;
76 matrix
[3] = matrix
[4] = 0.f
;
77 matrix
[5] = 0.213f
- cos_hue
* 0.213f
+ sin_hue
* 0.143f
;
78 matrix
[6] = 0.715f
+ cos_hue
* 0.285f
+ sin_hue
* 0.140f
;
79 matrix
[7] = 0.072f
- cos_hue
* 0.072f
- sin_hue
* 0.283f
;
80 matrix
[8] = matrix
[9] = 0.f
;
81 matrix
[10] = 0.213f
- cos_hue
* 0.213f
- sin_hue
* 0.787f
;
82 matrix
[11] = 0.715f
- cos_hue
* 0.715f
+ sin_hue
* 0.715f
;
83 matrix
[12] = 0.072f
+ cos_hue
* 0.928f
+ sin_hue
* 0.072f
;
84 matrix
[13] = matrix
[14] = 0.f
;
85 matrix
[15] = matrix
[16] = matrix
[17] = 0.f
;
90 void GetInvertMatrix(float amount
, SkScalar matrix
[20]) {
91 memset(matrix
, 0, 20 * sizeof(SkScalar
));
92 matrix
[0] = matrix
[6] = matrix
[12] = 1.f
- 2.f
* amount
;
93 matrix
[4] = matrix
[9] = matrix
[14] = amount
* 255.f
;
97 void GetOpacityMatrix(float amount
, SkScalar matrix
[20]) {
98 memset(matrix
, 0, 20 * sizeof(SkScalar
));
99 matrix
[0] = matrix
[6] = matrix
[12] = 1.f
;
103 void GetGrayscaleMatrix(float amount
, SkScalar matrix
[20]) {
104 // Note, these values are computed to ensure MatrixNeedsClamping is false
105 // for amount in [0..1]
106 matrix
[0] = 0.2126f
+ 0.7874f
* amount
;
107 matrix
[1] = 0.7152f
- 0.7152f
* amount
;
108 matrix
[2] = 1.f
- (matrix
[0] + matrix
[1]);
109 matrix
[3] = matrix
[4] = 0.f
;
111 matrix
[5] = 0.2126f
- 0.2126f
* amount
;
112 matrix
[6] = 0.7152f
+ 0.2848f
* amount
;
113 matrix
[7] = 1.f
- (matrix
[5] + matrix
[6]);
114 matrix
[8] = matrix
[9] = 0.f
;
116 matrix
[10] = 0.2126f
- 0.2126f
* amount
;
117 matrix
[11] = 0.7152f
- 0.7152f
* amount
;
118 matrix
[12] = 1.f
- (matrix
[10] + matrix
[11]);
119 matrix
[13] = matrix
[14] = 0.f
;
121 matrix
[15] = matrix
[16] = matrix
[17] = matrix
[19] = 0.f
;
125 void GetSepiaMatrix(float amount
, SkScalar matrix
[20]) {
126 matrix
[0] = 0.393f
+ 0.607f
* amount
;
127 matrix
[1] = 0.769f
- 0.769f
* amount
;
128 matrix
[2] = 0.189f
- 0.189f
* amount
;
129 matrix
[3] = matrix
[4] = 0.f
;
131 matrix
[5] = 0.349f
- 0.349f
* amount
;
132 matrix
[6] = 0.686f
+ 0.314f
* amount
;
133 matrix
[7] = 0.168f
- 0.168f
* amount
;
134 matrix
[8] = matrix
[9] = 0.f
;
136 matrix
[10] = 0.272f
- 0.272f
* amount
;
137 matrix
[11] = 0.534f
- 0.534f
* amount
;
138 matrix
[12] = 0.131f
+ 0.869f
* amount
;
139 matrix
[13] = matrix
[14] = 0.f
;
141 matrix
[15] = matrix
[16] = matrix
[17] = matrix
[19] = 0.f
;
145 // The 5x4 matrix is really a "compressed" version of a 5x5 matrix that'd have
146 // (0 0 0 0 1) as a last row, and that would be applied to a 5-vector extended
147 // from the 4-vector color with a 1.
148 void MultColorMatrix(SkScalar a
[20], SkScalar b
[20], SkScalar out
[20]) {
149 for (int j
= 0; j
< 4; ++j
) {
150 for (int i
= 0; i
< 5; ++i
) {
151 out
[i
+j
*5] = i
== 4 ? a
[4+j
*5] : 0.f
;
152 for (int k
= 0; k
< 4; ++k
)
153 out
[i
+j
*5] += a
[k
+j
*5] * b
[i
+k
*5];
158 // To detect if we need to apply clamping after applying a matrix, we check if
159 // any output component might go outside of [0, 255] for any combination of
160 // input components in [0..255].
161 // Each output component is an affine transformation of the input component, so
162 // the minimum and maximum values are for any combination of minimum or maximum
163 // values of input components (i.e. 0 or 255).
164 // E.g. if R' = x*R + y*G + z*B + w*A + t
165 // Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the
166 // minimum value will be for R=0 if x>0 or R=255 if x<0.
167 // Same goes for all components.
168 bool ComponentNeedsClamping(SkScalar row
[5]) {
169 SkScalar max_value
= row
[4] / 255.f
;
170 SkScalar min_value
= row
[4] / 255.f
;
171 for (int i
= 0; i
< 4; ++i
) {
177 return (max_value
> 1.f
) || (min_value
< 0.f
);
180 bool MatrixNeedsClamping(SkScalar matrix
[20]) {
181 return ComponentNeedsClamping(matrix
)
182 || ComponentNeedsClamping(matrix
+5)
183 || ComponentNeedsClamping(matrix
+10)
184 || ComponentNeedsClamping(matrix
+15);
187 bool GetColorMatrix(const WebKit::WebFilterOperation
& op
, SkScalar matrix
[20]) {
189 case WebKit::WebFilterOperation::FilterTypeBrightness
: {
190 GetBrightnessMatrix(op
.amount(), matrix
);
193 case WebKit::WebFilterOperation::FilterTypeSaturatingBrightness
: {
194 GetSaturatingBrightnessMatrix(op
.amount(), matrix
);
197 case WebKit::WebFilterOperation::FilterTypeContrast
: {
198 GetContrastMatrix(op
.amount(), matrix
);
201 case WebKit::WebFilterOperation::FilterTypeGrayscale
: {
202 GetGrayscaleMatrix(1.f
- op
.amount(), matrix
);
205 case WebKit::WebFilterOperation::FilterTypeSepia
: {
206 GetSepiaMatrix(1.f
- op
.amount(), matrix
);
209 case WebKit::WebFilterOperation::FilterTypeSaturate
: {
210 GetSaturateMatrix(op
.amount(), matrix
);
213 case WebKit::WebFilterOperation::FilterTypeHueRotate
: {
214 GetHueRotateMatrix(op
.amount(), matrix
);
217 case WebKit::WebFilterOperation::FilterTypeInvert
: {
218 GetInvertMatrix(op
.amount(), matrix
);
221 case WebKit::WebFilterOperation::FilterTypeOpacity
: {
222 GetOpacityMatrix(op
.amount(), matrix
);
225 case WebKit::WebFilterOperation::FilterTypeColorMatrix
: {
226 memcpy(matrix
, op
.matrix(), sizeof(SkScalar
[20]));
234 class FilterBufferState
{
236 FilterBufferState(GrContext
* gr_context
,
239 : gr_context_(gr_context
),
240 current_texture_(0) {
241 // Wrap the source texture in a Ganesh platform texture.
242 GrBackendTextureDesc backend_texture_description
;
243 backend_texture_description
.fWidth
= size
.width();
244 backend_texture_description
.fHeight
= size
.height();
245 backend_texture_description
.fConfig
= kSkia8888_GrPixelConfig
;
246 backend_texture_description
.fTextureHandle
= texture_id
;
247 skia::RefPtr
<GrTexture
> texture
= skia::AdoptRef(
248 gr_context
->wrapBackendTexture(backend_texture_description
));
249 // Place the platform texture inside an SkBitmap.
250 source_
.setConfig(SkBitmap::kARGB_8888_Config
, size
.width(), size
.height());
251 skia::RefPtr
<SkGrPixelRef
> pixel_ref
=
252 skia::AdoptRef(new SkGrPixelRef(texture
.get()));
253 source_
.setPixelRef(pixel_ref
.get());
256 ~FilterBufferState() {}
258 bool Init(int filter_count
) {
259 int scratch_count
= std::min(2, filter_count
);
261 desc
.fFlags
= kRenderTarget_GrTextureFlagBit
| kNoStencil_GrTextureFlagBit
;
263 desc
.fWidth
= source_
.width();
264 desc
.fHeight
= source_
.height();
265 desc
.fConfig
= kSkia8888_GrPixelConfig
;
266 for (int i
= 0; i
< scratch_count
; ++i
) {
267 GrAutoScratchTexture
scratch_texture(
268 gr_context_
, desc
, GrContext::kExact_ScratchTexMatch
);
269 scratch_textures_
[i
] = skia::AdoptRef(scratch_texture
.detach());
270 if (!scratch_textures_
[i
])
279 return canvas_
.get();
282 const SkBitmap
& Source() { return source_
; }
289 skia::RefPtr
<SkGrPixelRef
> pixel_ref
= skia::AdoptRef(
290 new SkGrPixelRef(scratch_textures_
[current_texture_
].get()));
291 source_
.setPixelRef(pixel_ref
.get());
292 current_texture_
= 1 - current_texture_
;
296 void CreateCanvas() {
297 DCHECK(scratch_textures_
[current_texture_
].get());
298 device_
= skia::AdoptRef(new SkGpuDevice(
299 gr_context_
, scratch_textures_
[current_texture_
].get()));
300 canvas_
= skia::AdoptRef(new SkCanvas(device_
.get()));
304 GrContext
* gr_context_
;
306 skia::RefPtr
<GrTexture
> scratch_textures_
[2];
307 int current_texture_
;
308 skia::RefPtr
<SkGpuDevice
> device_
;
309 skia::RefPtr
<SkCanvas
> canvas_
;
314 WebKit::WebFilterOperations
RenderSurfaceFilters::Optimize(
315 const WebKit::WebFilterOperations
& filters
) {
316 WebKit::WebFilterOperations new_list
;
318 SkScalar accumulated_color_matrix
[20];
319 bool have_accumulated_color_matrix
= false;
320 for (unsigned i
= 0; i
< filters
.size(); ++i
) {
321 const WebKit::WebFilterOperation
& op
= filters
.at(i
);
323 // If the filter is a color matrix, we may be able to combine it with
324 // following Filter(s) that also are color matrices.
326 if (GetColorMatrix(op
, matrix
)) {
327 if (have_accumulated_color_matrix
) {
328 SkScalar new_matrix
[20];
329 MultColorMatrix(matrix
, accumulated_color_matrix
, new_matrix
);
330 memcpy(accumulated_color_matrix
,
332 sizeof(accumulated_color_matrix
));
334 memcpy(accumulated_color_matrix
,
336 sizeof(accumulated_color_matrix
));
337 have_accumulated_color_matrix
= true;
340 // We can only combine matrices if clamping of color components
341 // would have no effect.
342 if (!MatrixNeedsClamping(accumulated_color_matrix
))
346 if (have_accumulated_color_matrix
) {
347 new_list
.append(WebKit::WebFilterOperation::createColorMatrixFilter(
348 accumulated_color_matrix
));
350 have_accumulated_color_matrix
= false;
353 case WebKit::WebFilterOperation::FilterTypeBlur
:
354 case WebKit::WebFilterOperation::FilterTypeDropShadow
:
355 case WebKit::WebFilterOperation::FilterTypeZoom
:
358 case WebKit::WebFilterOperation::FilterTypeBrightness
:
359 case WebKit::WebFilterOperation::FilterTypeSaturatingBrightness
:
360 case WebKit::WebFilterOperation::FilterTypeContrast
:
361 case WebKit::WebFilterOperation::FilterTypeGrayscale
:
362 case WebKit::WebFilterOperation::FilterTypeSepia
:
363 case WebKit::WebFilterOperation::FilterTypeSaturate
:
364 case WebKit::WebFilterOperation::FilterTypeHueRotate
:
365 case WebKit::WebFilterOperation::FilterTypeInvert
:
366 case WebKit::WebFilterOperation::FilterTypeOpacity
:
367 case WebKit::WebFilterOperation::FilterTypeColorMatrix
:
371 if (have_accumulated_color_matrix
) {
372 new_list
.append(WebKit::WebFilterOperation::createColorMatrixFilter(
373 accumulated_color_matrix
));
378 SkBitmap
RenderSurfaceFilters::Apply(const WebKit::WebFilterOperations
& filters
,
381 GrContext
* gr_context
) {
384 WebKit::WebFilterOperations optimized_filters
= Optimize(filters
);
385 FilterBufferState
state(gr_context
, size
, texture_id
);
386 if (!state
.Init(optimized_filters
.size()))
389 for (unsigned i
= 0; i
< optimized_filters
.size(); ++i
) {
390 const WebKit::WebFilterOperation
& op
= optimized_filters
.at(i
);
391 SkCanvas
* canvas
= state
.Canvas();
393 case WebKit::WebFilterOperation::FilterTypeColorMatrix
: {
395 skia::RefPtr
<SkColorMatrixFilter
> filter
=
396 skia::AdoptRef(new SkColorMatrixFilter(op
.matrix()));
397 paint
.setColorFilter(filter
.get());
398 canvas
->drawBitmap(state
.Source(), 0, 0, &paint
);
401 case WebKit::WebFilterOperation::FilterTypeBlur
: {
402 float std_deviation
= op
.amount();
403 skia::RefPtr
<SkImageFilter
> filter
=
404 skia::AdoptRef(new SkBlurImageFilter(std_deviation
, std_deviation
));
406 paint
.setImageFilter(filter
.get());
407 canvas
->drawSprite(state
.Source(), 0, 0, &paint
);
410 case WebKit::WebFilterOperation::FilterTypeDropShadow
: {
411 skia::RefPtr
<SkImageFilter
> blur_filter
=
412 skia::AdoptRef(new SkBlurImageFilter(op
.amount(), op
.amount()));
413 skia::RefPtr
<SkColorFilter
> color_filter
=
414 skia::AdoptRef(SkColorFilter::CreateModeFilter(
415 op
.dropShadowColor(), SkXfermode::kSrcIn_Mode
));
417 paint
.setImageFilter(blur_filter
.get());
418 paint
.setColorFilter(color_filter
.get());
419 paint
.setXfermodeMode(SkXfermode::kSrcOver_Mode
);
420 canvas
->saveLayer(NULL
, &paint
);
421 canvas
->drawBitmap(state
.Source(),
422 op
.dropShadowOffset().x
,
423 -op
.dropShadowOffset().y
);
425 canvas
->drawBitmap(state
.Source(), 0, 0);
428 case WebKit::WebFilterOperation::FilterTypeZoom
: {
430 int width
= state
.Source().width();
431 int height
= state
.Source().height();
432 skia::RefPtr
<SkImageFilter
> zoom_filter
= skia::AdoptRef(
433 new SkMagnifierImageFilter(
435 (width
- (width
/ op
.amount())) / 2.f
,
436 (height
- (height
/ op
.amount())) / 2.f
,
438 height
/ op
.amount()),
440 paint
.setImageFilter(zoom_filter
.get());
441 canvas
->saveLayer(NULL
, &paint
);
442 canvas
->drawBitmap(state
.Source(), 0, 0);
446 case WebKit::WebFilterOperation::FilterTypeBrightness
:
447 case WebKit::WebFilterOperation::FilterTypeSaturatingBrightness
:
448 case WebKit::WebFilterOperation::FilterTypeContrast
:
449 case WebKit::WebFilterOperation::FilterTypeGrayscale
:
450 case WebKit::WebFilterOperation::FilterTypeSepia
:
451 case WebKit::WebFilterOperation::FilterTypeSaturate
:
452 case WebKit::WebFilterOperation::FilterTypeHueRotate
:
453 case WebKit::WebFilterOperation::FilterTypeInvert
:
454 case WebKit::WebFilterOperation::FilterTypeOpacity
:
460 return state
.Source();