cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / cc / output / render_surface_filters.cc
blob04d8c5ab9f743c4c4e6b67bb902fb27e94a2af4f
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"
7 #include <algorithm>
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"
21 namespace cc {
23 namespace {
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;
31 matrix[18] = 1.f;
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;
46 matrix[18] = 1.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;
65 matrix[18] = 1.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;
86 matrix[18] = 1.f;
87 matrix[19] = 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;
94 matrix[18] = 1.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;
100 matrix[18] = amount;
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;
122 matrix[18] = 1.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;
142 matrix[18] = 1.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) {
172 if (row[i] > 0)
173 max_value += row[i];
174 else
175 min_value += row[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]) {
188 switch (op.type()) {
189 case FilterOperation::BRIGHTNESS: {
190 GetBrightnessMatrix(op.amount(), matrix);
191 return true;
193 case FilterOperation::SATURATING_BRIGHTNESS: {
194 GetSaturatingBrightnessMatrix(op.amount(), matrix);
195 return true;
197 case FilterOperation::CONTRAST: {
198 GetContrastMatrix(op.amount(), matrix);
199 return true;
201 case FilterOperation::GRAYSCALE: {
202 GetGrayscaleMatrix(1.f - op.amount(), matrix);
203 return true;
205 case FilterOperation::SEPIA: {
206 GetSepiaMatrix(1.f - op.amount(), matrix);
207 return true;
209 case FilterOperation::SATURATE: {
210 GetSaturateMatrix(op.amount(), matrix);
211 return true;
213 case FilterOperation::HUE_ROTATE: {
214 GetHueRotateMatrix(op.amount(), matrix);
215 return true;
217 case FilterOperation::INVERT: {
218 GetInvertMatrix(op.amount(), matrix);
219 return true;
221 case FilterOperation::OPACITY: {
222 GetOpacityMatrix(op.amount(), matrix);
223 return true;
225 case FilterOperation::COLOR_MATRIX: {
226 memcpy(matrix, op.matrix(), sizeof(SkScalar[20]));
227 return true;
229 case FilterOperation::BLUR:
230 case FilterOperation::DROP_SHADOW:
231 case FilterOperation::ZOOM:
232 return false;
234 NOTREACHED();
235 return false;
238 class FilterBufferState {
239 public:
240 FilterBufferState(GrContext* gr_context,
241 gfx::SizeF size,
242 unsigned texture_id)
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);
264 GrTextureDesc desc;
265 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
266 desc.fSampleCnt = 0;
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])
275 return false;
277 return true;
280 SkCanvas* Canvas() {
281 if (!canvas_)
282 CreateCanvas();
283 return canvas_.get();
286 const SkBitmap& Source() { return source_; }
288 void Swap() {
289 canvas_->flush();
290 canvas_.clear();
291 device_.clear();
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_;
299 private:
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()));
305 canvas_->clear(0x0);
308 GrContext* gr_context_;
309 SkBitmap source_;
310 skia::RefPtr<GrTexture> scratch_textures_[2];
311 int current_texture_;
312 skia::RefPtr<SkGpuDevice> device_;
313 skia::RefPtr<SkCanvas> canvas_;
316 } // namespace
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.
329 SkScalar matrix[20];
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,
335 new_matrix,
336 sizeof(accumulated_color_matrix));
337 } else {
338 memcpy(accumulated_color_matrix,
339 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))
347 continue;
350 if (have_accumulated_color_matrix) {
351 new_list.Append(FilterOperation::CreateColorMatrixFilter(
352 accumulated_color_matrix));
354 have_accumulated_color_matrix = false;
356 switch (op.type()) {
357 case FilterOperation::BLUR:
358 case FilterOperation::DROP_SHADOW:
359 case FilterOperation::ZOOM:
360 new_list.Append(op);
361 break;
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:
372 break;
375 if (have_accumulated_color_matrix) {
376 new_list.Append(FilterOperation::CreateColorMatrixFilter(
377 accumulated_color_matrix));
379 return new_list;
382 SkBitmap RenderSurfaceFilters::Apply(const FilterOperations& filters,
383 unsigned texture_id,
384 gfx::SizeF size,
385 GrContext* gr_context) {
386 DCHECK(gr_context);
388 FilterBufferState state(gr_context, size, texture_id);
389 if (!state.Init(filters.size()))
390 return SkBitmap();
392 for (unsigned i = 0; i < filters.size(); ++i) {
393 const FilterOperation& op = filters.at(i);
394 SkCanvas* canvas = state.Canvas();
395 switch (op.type()) {
396 case FilterOperation::COLOR_MATRIX: {
397 SkPaint paint;
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);
402 break;
404 case FilterOperation::BLUR: {
405 float std_deviation = op.amount();
406 skia::RefPtr<SkImageFilter> filter =
407 skia::AdoptRef(new SkBlurImageFilter(std_deviation, std_deviation));
408 SkPaint paint;
409 paint.setImageFilter(filter.get());
410 canvas->drawSprite(state.Source(), 0, 0, &paint);
411 break;
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));
419 SkPaint paint;
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());
427 canvas->restore();
428 canvas->drawBitmap(state.Source(), 0, 0);
429 break;
431 case FilterOperation::ZOOM: {
432 SkPaint paint;
433 int width = state.Source().width();
434 int height = state.Source().height();
435 skia::RefPtr<SkImageFilter> zoom_filter = skia::AdoptRef(
436 new SkMagnifierImageFilter(
437 SkRect::MakeXYWH(
438 (width - (width / op.amount())) / 2.f,
439 (height - (height / op.amount())) / 2.f,
440 width / op.amount(),
441 height / op.amount()),
442 op.zoom_inset()));
443 paint.setImageFilter(zoom_filter.get());
444 canvas->saveLayer(NULL, &paint);
445 canvas->drawBitmap(state.Source(), 0, 0);
446 canvas->restore();
447 break;
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:
458 NOTREACHED();
459 break;
461 state.Swap();
463 return state.Source();
466 } // namespace cc