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 "cc/output/filter_operation.h"
11 #include "cc/output/filter_operations.h"
12 #include "skia/ext/refptr.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 FilterOperation
& op
, SkScalar matrix
[20]) {
189 case FilterOperation::BRIGHTNESS
: {
190 GetBrightnessMatrix(op
.amount(), matrix
);
193 case FilterOperation::SATURATING_BRIGHTNESS
: {
194 GetSaturatingBrightnessMatrix(op
.amount(), matrix
);
197 case FilterOperation::CONTRAST
: {
198 GetContrastMatrix(op
.amount(), matrix
);
201 case FilterOperation::GRAYSCALE
: {
202 GetGrayscaleMatrix(1.f
- op
.amount(), matrix
);
205 case FilterOperation::SEPIA
: {
206 GetSepiaMatrix(1.f
- op
.amount(), matrix
);
209 case FilterOperation::SATURATE
: {
210 GetSaturateMatrix(op
.amount(), matrix
);
213 case FilterOperation::HUE_ROTATE
: {
214 GetHueRotateMatrix(op
.amount(), matrix
);
217 case FilterOperation::INVERT
: {
218 GetInvertMatrix(op
.amount(), matrix
);
221 case FilterOperation::OPACITY
: {
222 GetOpacityMatrix(op
.amount(), matrix
);
225 case FilterOperation::COLOR_MATRIX
: {
226 memcpy(matrix
, op
.matrix(), sizeof(SkScalar
[20]));
229 case FilterOperation::BLUR
:
230 case FilterOperation::DROP_SHADOW
:
231 case FilterOperation::ZOOM
:
238 class FilterBufferState
{
240 FilterBufferState(GrContext
* gr_context
,
243 : gr_context_(gr_context
),
244 current_texture_(0) {
245 // Wrap the source texture in a Ganesh platform texture.
246 GrBackendTextureDesc backend_texture_description
;
247 backend_texture_description
.fWidth
= size
.width();
248 backend_texture_description
.fHeight
= size
.height();
249 backend_texture_description
.fConfig
= kSkia8888_GrPixelConfig
;
250 backend_texture_description
.fTextureHandle
= texture_id
;
251 skia::RefPtr
<GrTexture
> texture
= skia::AdoptRef(
252 gr_context
->wrapBackendTexture(backend_texture_description
));
253 // Place the platform texture inside an SkBitmap.
254 source_
.setConfig(SkBitmap::kARGB_8888_Config
, size
.width(), size
.height());
255 skia::RefPtr
<SkGrPixelRef
> pixel_ref
=
256 skia::AdoptRef(new SkGrPixelRef(texture
.get()));
257 source_
.setPixelRef(pixel_ref
.get());
260 ~FilterBufferState() {}
262 bool Init(int filter_count
) {
263 int scratch_count
= std::min(2, filter_count
);
265 desc
.fFlags
= kRenderTarget_GrTextureFlagBit
| kNoStencil_GrTextureFlagBit
;
267 desc
.fWidth
= source_
.width();
268 desc
.fHeight
= source_
.height();
269 desc
.fConfig
= kSkia8888_GrPixelConfig
;
270 for (int i
= 0; i
< scratch_count
; ++i
) {
271 GrAutoScratchTexture
scratch_texture(
272 gr_context_
, desc
, GrContext::kExact_ScratchTexMatch
);
273 scratch_textures_
[i
] = skia::AdoptRef(scratch_texture
.detach());
274 if (!scratch_textures_
[i
])
283 return canvas_
.get();
286 const SkBitmap
& Source() { return source_
; }
293 skia::RefPtr
<SkGrPixelRef
> pixel_ref
= skia::AdoptRef(
294 new SkGrPixelRef(scratch_textures_
[current_texture_
].get()));
295 source_
.setPixelRef(pixel_ref
.get());
296 current_texture_
= 1 - current_texture_
;
300 void CreateCanvas() {
301 DCHECK(scratch_textures_
[current_texture_
].get());
302 device_
= skia::AdoptRef(new SkGpuDevice(
303 gr_context_
, scratch_textures_
[current_texture_
].get()));
304 canvas_
= skia::AdoptRef(new SkCanvas(device_
.get()));
308 GrContext
* gr_context_
;
310 skia::RefPtr
<GrTexture
> scratch_textures_
[2];
311 int current_texture_
;
312 skia::RefPtr
<SkGpuDevice
> device_
;
313 skia::RefPtr
<SkCanvas
> canvas_
;
318 FilterOperations
RenderSurfaceFilters::Optimize(
319 const FilterOperations
& filters
) {
320 FilterOperations new_list
;
322 SkScalar accumulated_color_matrix
[20];
323 bool have_accumulated_color_matrix
= false;
324 for (unsigned i
= 0; i
< filters
.size(); ++i
) {
325 const FilterOperation
& op
= filters
.at(i
);
327 // If the filter is a color matrix, we may be able to combine it with
328 // following Filter(s) that also are color matrices.
330 if (GetColorMatrix(op
, matrix
)) {
331 if (have_accumulated_color_matrix
) {
332 SkScalar new_matrix
[20];
333 MultColorMatrix(matrix
, accumulated_color_matrix
, new_matrix
);
334 memcpy(accumulated_color_matrix
,
336 sizeof(accumulated_color_matrix
));
338 memcpy(accumulated_color_matrix
,
340 sizeof(accumulated_color_matrix
));
341 have_accumulated_color_matrix
= true;
344 // We can only combine matrices if clamping of color components
345 // would have no effect.
346 if (!MatrixNeedsClamping(accumulated_color_matrix
))
350 if (have_accumulated_color_matrix
) {
351 new_list
.Append(FilterOperation::CreateColorMatrixFilter(
352 accumulated_color_matrix
));
354 have_accumulated_color_matrix
= false;
357 case FilterOperation::BLUR
:
358 case FilterOperation::DROP_SHADOW
:
359 case FilterOperation::ZOOM
:
362 case FilterOperation::BRIGHTNESS
:
363 case FilterOperation::SATURATING_BRIGHTNESS
:
364 case FilterOperation::CONTRAST
:
365 case FilterOperation::GRAYSCALE
:
366 case FilterOperation::SEPIA
:
367 case FilterOperation::SATURATE
:
368 case FilterOperation::HUE_ROTATE
:
369 case FilterOperation::INVERT
:
370 case FilterOperation::OPACITY
:
371 case FilterOperation::COLOR_MATRIX
:
375 if (have_accumulated_color_matrix
) {
376 new_list
.Append(FilterOperation::CreateColorMatrixFilter(
377 accumulated_color_matrix
));
382 SkBitmap
RenderSurfaceFilters::Apply(const FilterOperations
& filters
,
385 GrContext
* gr_context
) {
388 FilterBufferState
state(gr_context
, size
, texture_id
);
389 if (!state
.Init(filters
.size()))
392 for (unsigned i
= 0; i
< filters
.size(); ++i
) {
393 const FilterOperation
& op
= filters
.at(i
);
394 SkCanvas
* canvas
= state
.Canvas();
396 case FilterOperation::COLOR_MATRIX
: {
398 skia::RefPtr
<SkColorMatrixFilter
> filter
=
399 skia::AdoptRef(new SkColorMatrixFilter(op
.matrix()));
400 paint
.setColorFilter(filter
.get());
401 canvas
->drawBitmap(state
.Source(), 0, 0, &paint
);
404 case FilterOperation::BLUR
: {
405 float std_deviation
= op
.amount();
406 skia::RefPtr
<SkImageFilter
> filter
=
407 skia::AdoptRef(new SkBlurImageFilter(std_deviation
, std_deviation
));
409 paint
.setImageFilter(filter
.get());
410 canvas
->drawSprite(state
.Source(), 0, 0, &paint
);
413 case FilterOperation::DROP_SHADOW
: {
414 skia::RefPtr
<SkImageFilter
> blur_filter
=
415 skia::AdoptRef(new SkBlurImageFilter(op
.amount(), op
.amount()));
416 skia::RefPtr
<SkColorFilter
> color_filter
=
417 skia::AdoptRef(SkColorFilter::CreateModeFilter(
418 op
.drop_shadow_color(), SkXfermode::kSrcIn_Mode
));
420 paint
.setImageFilter(blur_filter
.get());
421 paint
.setColorFilter(color_filter
.get());
422 paint
.setXfermodeMode(SkXfermode::kSrcOver_Mode
);
423 canvas
->saveLayer(NULL
, &paint
);
424 canvas
->drawBitmap(state
.Source(),
425 op
.drop_shadow_offset().x(),
426 op
.drop_shadow_offset().y());
428 canvas
->drawBitmap(state
.Source(), 0, 0);
431 case FilterOperation::ZOOM
: {
433 int width
= state
.Source().width();
434 int height
= state
.Source().height();
435 skia::RefPtr
<SkImageFilter
> zoom_filter
= skia::AdoptRef(
436 new SkMagnifierImageFilter(
438 (width
- (width
/ op
.amount())) / 2.f
,
439 (height
- (height
/ op
.amount())) / 2.f
,
441 height
/ op
.amount()),
443 paint
.setImageFilter(zoom_filter
.get());
444 canvas
->saveLayer(NULL
, &paint
);
445 canvas
->drawBitmap(state
.Source(), 0, 0);
449 case FilterOperation::BRIGHTNESS
:
450 case FilterOperation::SATURATING_BRIGHTNESS
:
451 case FilterOperation::CONTRAST
:
452 case FilterOperation::GRAYSCALE
:
453 case FilterOperation::SEPIA
:
454 case FilterOperation::SATURATE
:
455 case FilterOperation::HUE_ROTATE
:
456 case FilterOperation::INVERT
:
457 case FilterOperation::OPACITY
:
463 return state
.Source();