OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "config.h" | |
6 | |
7 #if USE(ACCELERATED_COMPOSITING) | |
8 | |
9 #include "CCRenderSurfaceFilters.h" | |
10 | |
11 #include "FloatSize.h" | |
12 #include "SkBlurImageFilter.h" | |
13 #include "SkCanvas.h" | |
14 #include "SkColorMatrixFilter.h" | |
15 #include "SkGpuDevice.h" | |
16 #include "SkGrPixelRef.h" | |
17 #include "SkMagnifierImageFilter.h" | |
18 #include <public/WebFilterOperation.h> | |
19 #include <public/WebFilterOperations.h> | |
20 #include <public/WebGraphicsContext3D.h> | |
21 #include <wtf/MathExtras.h> | |
22 | |
23 using namespace cc; | |
24 | |
25 namespace { | |
26 | |
27 void getBrightnessMatrix(float amount, SkScalar matrix[20]) | |
28 { | |
29 memset(matrix, 0, 20 * sizeof(SkScalar)); | |
30 // Old implementation, a la the draft spec, a straight-up scale, | |
31 // representing <feFunc[R|G|B] type="linear" slope="[amount]"> | |
32 // (See http://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#bright
nessEquivalent) | |
33 // matrix[0] = matrix[6] = matrix[12] = amount; | |
34 // matrix[18] = 1; | |
35 // New implementation, a translation in color space, representing | |
36 // <feFunc[R|G|B] type="linear" intercept="[amount]"/> | |
37 // (See https://www.w3.org/Bugs/Public/show_bug.cgi?id=15647) | |
38 matrix[0] = matrix[6] = matrix[12] = matrix[18] = 1; | |
39 matrix[4] = matrix[9] = matrix[14] = amount * 255; | |
40 } | |
41 | |
42 void getContrastMatrix(float amount, SkScalar matrix[20]) | |
43 { | |
44 memset(matrix, 0, 20 * sizeof(SkScalar)); | |
45 matrix[0] = matrix[6] = matrix[12] = amount; | |
46 matrix[4] = matrix[9] = matrix[14] = (-0.5f * amount + 0.5f) * 255; | |
47 matrix[18] = 1; | |
48 } | |
49 | |
50 void getSaturateMatrix(float amount, SkScalar matrix[20]) | |
51 { | |
52 // Note, these values are computed to ensure matrixNeedsClamping is false | |
53 // for amount in [0..1] | |
54 matrix[0] = 0.213f + 0.787f * amount; | |
55 matrix[1] = 0.715f - 0.715f * amount; | |
56 matrix[2] = 1.f - (matrix[0] + matrix[1]); | |
57 matrix[3] = matrix[4] = 0; | |
58 matrix[5] = 0.213f - 0.213f * amount; | |
59 matrix[6] = 0.715f + 0.285f * amount; | |
60 matrix[7] = 1.f - (matrix[5] + matrix[6]); | |
61 matrix[8] = matrix[9] = 0; | |
62 matrix[10] = 0.213f - 0.213f * amount; | |
63 matrix[11] = 0.715f - 0.715f * amount; | |
64 matrix[12] = 1.f - (matrix[10] + matrix[11]); | |
65 matrix[13] = matrix[14] = 0; | |
66 matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0; | |
67 matrix[18] = 1; | |
68 } | |
69 | |
70 void getHueRotateMatrix(float hue, SkScalar matrix[20]) | |
71 { | |
72 float cosHue = cosf(hue * piFloat / 180); | |
73 float sinHue = sinf(hue * piFloat / 180); | |
74 matrix[0] = 0.213f + cosHue * 0.787f - sinHue * 0.213f; | |
75 matrix[1] = 0.715f - cosHue * 0.715f - sinHue * 0.715f; | |
76 matrix[2] = 0.072f - cosHue * 0.072f + sinHue * 0.928f; | |
77 matrix[3] = matrix[4] = 0; | |
78 matrix[5] = 0.213f - cosHue * 0.213f + sinHue * 0.143f; | |
79 matrix[6] = 0.715f + cosHue * 0.285f + sinHue * 0.140f; | |
80 matrix[7] = 0.072f - cosHue * 0.072f - sinHue * 0.283f; | |
81 matrix[8] = matrix[9] = 0; | |
82 matrix[10] = 0.213f - cosHue * 0.213f - sinHue * 0.787f; | |
83 matrix[11] = 0.715f - cosHue * 0.715f + sinHue * 0.715f; | |
84 matrix[12] = 0.072f + cosHue * 0.928f + sinHue * 0.072f; | |
85 matrix[13] = matrix[14] = 0; | |
86 matrix[15] = matrix[16] = matrix[17] = 0; | |
87 matrix[18] = 1; | |
88 matrix[19] = 0; | |
89 } | |
90 | |
91 void getInvertMatrix(float amount, SkScalar matrix[20]) | |
92 { | |
93 memset(matrix, 0, 20 * sizeof(SkScalar)); | |
94 matrix[0] = matrix[6] = matrix[12] = 1 - 2 * amount; | |
95 matrix[4] = matrix[9] = matrix[14] = amount * 255; | |
96 matrix[18] = 1; | |
97 } | |
98 | |
99 void getOpacityMatrix(float amount, SkScalar matrix[20]) | |
100 { | |
101 memset(matrix, 0, 20 * sizeof(SkScalar)); | |
102 matrix[0] = matrix[6] = matrix[12] = 1; | |
103 matrix[18] = amount; | |
104 } | |
105 | |
106 void getGrayscaleMatrix(float amount, SkScalar matrix[20]) | |
107 { | |
108 // Note, these values are computed to ensure matrixNeedsClamping is false | |
109 // for amount in [0..1] | |
110 matrix[0] = 0.2126f + 0.7874f * amount; | |
111 matrix[1] = 0.7152f - 0.7152f * amount; | |
112 matrix[2] = 1.f - (matrix[0] + matrix[1]); | |
113 matrix[3] = matrix[4] = 0; | |
114 | |
115 matrix[5] = 0.2126f - 0.2126f * amount; | |
116 matrix[6] = 0.7152f + 0.2848f * amount; | |
117 matrix[7] = 1.f - (matrix[5] + matrix[6]); | |
118 matrix[8] = matrix[9] = 0; | |
119 | |
120 matrix[10] = 0.2126f - 0.2126f * amount; | |
121 matrix[11] = 0.7152f - 0.7152f * amount; | |
122 matrix[12] = 1.f - (matrix[10] + matrix[11]); | |
123 matrix[13] = matrix[14] = 0; | |
124 | |
125 matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0; | |
126 matrix[18] = 1; | |
127 } | |
128 | |
129 void getSepiaMatrix(float amount, SkScalar matrix[20]) | |
130 { | |
131 matrix[0] = 0.393f + 0.607f * amount; | |
132 matrix[1] = 0.769f - 0.769f * amount; | |
133 matrix[2] = 0.189f - 0.189f * amount; | |
134 matrix[3] = matrix[4] = 0; | |
135 | |
136 matrix[5] = 0.349f - 0.349f * amount; | |
137 matrix[6] = 0.686f + 0.314f * amount; | |
138 matrix[7] = 0.168f - 0.168f * amount; | |
139 matrix[8] = matrix[9] = 0; | |
140 | |
141 matrix[10] = 0.272f - 0.272f * amount; | |
142 matrix[11] = 0.534f - 0.534f * amount; | |
143 matrix[12] = 0.131f + 0.869f * amount; | |
144 matrix[13] = matrix[14] = 0; | |
145 | |
146 matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0; | |
147 matrix[18] = 1; | |
148 } | |
149 | |
150 // The 5x4 matrix is really a "compressed" version of a 5x5 matrix that'd have | |
151 // (0 0 0 0 1) as a last row, and that would be applied to a 5-vector extended | |
152 // from the 4-vector color with a 1. | |
153 void multColorMatrix(SkScalar a[20], SkScalar b[20], SkScalar out[20]) | |
154 { | |
155 for (int j = 0; j < 4; ++j) { | |
156 for (int i = 0; i < 5; ++i) { | |
157 out[i+j*5] = i == 4 ? a[4+j*5] : 0; | |
158 for (int k = 0; k < 4; ++k) | |
159 out[i+j*5] += a[k+j*5] * b[i+k*5]; | |
160 } | |
161 } | |
162 } | |
163 | |
164 // To detect if we need to apply clamping after applying a matrix, we check if | |
165 // any output component might go outside of [0, 255] for any combination of | |
166 // input components in [0..255]. | |
167 // Each output component is an affine transformation of the input component, so | |
168 // the minimum and maximum values are for any combination of minimum or maximum | |
169 // values of input components (i.e. 0 or 255). | |
170 // E.g. if R' = x*R + y*G + z*B + w*A + t | |
171 // Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the | |
172 // minimum value will be for R=0 if x>0 or R=255 if x<0. | |
173 // Same goes for all components. | |
174 bool componentNeedsClamping(SkScalar row[5]) | |
175 { | |
176 SkScalar maxValue = row[4] / 255; | |
177 SkScalar minValue = row[4] / 255; | |
178 for (int i = 0; i < 4; ++i) { | |
179 if (row[i] > 0) | |
180 maxValue += row[i]; | |
181 else | |
182 minValue += row[i]; | |
183 } | |
184 return (maxValue > 1) || (minValue < 0); | |
185 } | |
186 | |
187 bool matrixNeedsClamping(SkScalar matrix[20]) | |
188 { | |
189 return componentNeedsClamping(matrix) | |
190 || componentNeedsClamping(matrix+5) | |
191 || componentNeedsClamping(matrix+10) | |
192 || componentNeedsClamping(matrix+15); | |
193 } | |
194 | |
195 bool getColorMatrix(const WebKit::WebFilterOperation& op, SkScalar matrix[20]) | |
196 { | |
197 switch (op.type()) { | |
198 case WebKit::WebFilterOperation::FilterTypeBrightness: { | |
199 getBrightnessMatrix(op.amount(), matrix); | |
200 return true; | |
201 } | |
202 case WebKit::WebFilterOperation::FilterTypeContrast: { | |
203 getContrastMatrix(op.amount(), matrix); | |
204 return true; | |
205 } | |
206 case WebKit::WebFilterOperation::FilterTypeGrayscale: { | |
207 getGrayscaleMatrix(1 - op.amount(), matrix); | |
208 return true; | |
209 } | |
210 case WebKit::WebFilterOperation::FilterTypeSepia: { | |
211 getSepiaMatrix(1 - op.amount(), matrix); | |
212 return true; | |
213 } | |
214 case WebKit::WebFilterOperation::FilterTypeSaturate: { | |
215 getSaturateMatrix(op.amount(), matrix); | |
216 return true; | |
217 } | |
218 case WebKit::WebFilterOperation::FilterTypeHueRotate: { | |
219 getHueRotateMatrix(op.amount(), matrix); | |
220 return true; | |
221 } | |
222 case WebKit::WebFilterOperation::FilterTypeInvert: { | |
223 getInvertMatrix(op.amount(), matrix); | |
224 return true; | |
225 } | |
226 case WebKit::WebFilterOperation::FilterTypeOpacity: { | |
227 getOpacityMatrix(op.amount(), matrix); | |
228 return true; | |
229 } | |
230 case WebKit::WebFilterOperation::FilterTypeColorMatrix: { | |
231 memcpy(matrix, op.matrix(), sizeof(SkScalar[20])); | |
232 return true; | |
233 } | |
234 default: | |
235 return false; | |
236 } | |
237 } | |
238 | |
239 class FilterBufferState { | |
240 public: | |
241 FilterBufferState(GrContext* grContext, const FloatSize& size, unsigned text
ureId) | |
242 : m_grContext(grContext) | |
243 , m_currentTexture(0) | |
244 { | |
245 // Wrap the source texture in a Ganesh platform texture. | |
246 GrPlatformTextureDesc platformTextureDescription; | |
247 platformTextureDescription.fWidth = size.width(); | |
248 platformTextureDescription.fHeight = size.height(); | |
249 platformTextureDescription.fConfig = kSkia8888_GrPixelConfig; | |
250 platformTextureDescription.fTextureHandle = textureId; | |
251 SkAutoTUnref<GrTexture> texture(grContext->createPlatformTexture(platfor
mTextureDescription)); | |
252 // Place the platform texture inside an SkBitmap. | |
253 m_source.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.heigh
t()); | |
254 m_source.setPixelRef(new SkGrPixelRef(texture.get()))->unref(); | |
255 } | |
256 | |
257 ~FilterBufferState() { } | |
258 | |
259 bool init(int filterCount) | |
260 { | |
261 int scratchCount = std::min(2, filterCount); | |
262 GrTextureDesc desc; | |
263 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagB
it; | |
264 desc.fSampleCnt = 0; | |
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::kE
xact_ScratchTexMatch); | |
270 m_scratchTextures[i].reset(scratchTexture.detach()); | |
271 if (!m_scratchTextures[i].get()) | |
272 return false; | |
273 } | |
274 return true; | |
275 } | |
276 | |
277 SkCanvas* canvas() | |
278 { | |
279 if (!m_canvas.get()) | |
280 createCanvas(); | |
281 return m_canvas.get(); | |
282 } | |
283 | |
284 const SkBitmap& source() { return m_source; } | |
285 | |
286 void swap() | |
287 { | |
288 m_canvas->flush(); | |
289 m_canvas.reset(0); | |
290 m_device.reset(0); | |
291 | |
292 m_source.setPixelRef(new SkGrPixelRef(m_scratchTextures[m_currentTexture
].get()))->unref(); | |
293 m_currentTexture = 1 - m_currentTexture; | |
294 } | |
295 | |
296 private: | |
297 void createCanvas() | |
298 { | |
299 ASSERT(m_scratchTextures[m_currentTexture].get()); | |
300 m_device.reset(new SkGpuDevice(m_grContext, m_scratchTextures[m_currentT
exture].get())); | |
301 m_canvas.reset(new SkCanvas(m_device.get())); | |
302 m_canvas->clear(0x0); | |
303 } | |
304 | |
305 GrContext* m_grContext; | |
306 SkBitmap m_source; | |
307 SkAutoTUnref<GrTexture> m_scratchTextures[2]; | |
308 int m_currentTexture; | |
309 SkAutoTUnref<SkGpuDevice> m_device; | |
310 SkAutoTUnref<SkCanvas> m_canvas; | |
311 }; | |
312 | |
313 } | |
314 | |
315 namespace cc { | |
316 | |
317 WebKit::WebFilterOperations CCRenderSurfaceFilters::optimize(const WebKit::WebFi
lterOperations& filters) | |
318 { | |
319 WebKit::WebFilterOperations newList; | |
320 | |
321 SkScalar accumulatedColorMatrix[20]; | |
322 bool haveAccumulatedColorMatrix = false; | |
323 for (unsigned i = 0; i < filters.size(); ++i) { | |
324 const WebKit::WebFilterOperation& op = filters.at(i); | |
325 | |
326 // If the filter is a color matrix, we may be able to combine it with | |
327 // following filter(s) that also are color matrices. | |
328 SkScalar matrix[20]; | |
329 if (getColorMatrix(op, matrix)) { | |
330 if (haveAccumulatedColorMatrix) { | |
331 SkScalar newMatrix[20]; | |
332 multColorMatrix(matrix, accumulatedColorMatrix, newMatrix); | |
333 memcpy(accumulatedColorMatrix, newMatrix, sizeof(accumulatedColo
rMatrix)); | |
334 } else { | |
335 memcpy(accumulatedColorMatrix, matrix, sizeof(accumulatedColorMa
trix)); | |
336 haveAccumulatedColorMatrix = true; | |
337 } | |
338 | |
339 // We can only combine matrices if clamping of color components | |
340 // would have no effect. | |
341 if (!matrixNeedsClamping(accumulatedColorMatrix)) | |
342 continue; | |
343 } | |
344 | |
345 if (haveAccumulatedColorMatrix) | |
346 newList.append(WebKit::WebFilterOperation::createColorMatrixFilter(a
ccumulatedColorMatrix)); | |
347 haveAccumulatedColorMatrix = false; | |
348 | |
349 switch (op.type()) { | |
350 case WebKit::WebFilterOperation::FilterTypeBlur: | |
351 case WebKit::WebFilterOperation::FilterTypeDropShadow: | |
352 case WebKit::WebFilterOperation::FilterTypeZoom: | |
353 newList.append(op); | |
354 break; | |
355 case WebKit::WebFilterOperation::FilterTypeBrightness: | |
356 case WebKit::WebFilterOperation::FilterTypeContrast: | |
357 case WebKit::WebFilterOperation::FilterTypeGrayscale: | |
358 case WebKit::WebFilterOperation::FilterTypeSepia: | |
359 case WebKit::WebFilterOperation::FilterTypeSaturate: | |
360 case WebKit::WebFilterOperation::FilterTypeHueRotate: | |
361 case WebKit::WebFilterOperation::FilterTypeInvert: | |
362 case WebKit::WebFilterOperation::FilterTypeOpacity: | |
363 case WebKit::WebFilterOperation::FilterTypeColorMatrix: | |
364 break; | |
365 } | |
366 } | |
367 if (haveAccumulatedColorMatrix) | |
368 newList.append(WebKit::WebFilterOperation::createColorMatrixFilter(accum
ulatedColorMatrix)); | |
369 return newList; | |
370 } | |
371 | |
372 SkBitmap CCRenderSurfaceFilters::apply(const WebKit::WebFilterOperations& filter
s, unsigned textureId, const FloatSize& size, WebKit::WebGraphicsContext3D* cont
ext3D, GrContext* grContext) | |
373 { | |
374 if (!context3D || !grContext) | |
375 return SkBitmap(); | |
376 | |
377 WebKit::WebFilterOperations optimizedFilters = optimize(filters); | |
378 FilterBufferState state(grContext, size, textureId); | |
379 if (!state.init(optimizedFilters.size())) | |
380 return SkBitmap(); | |
381 | |
382 for (unsigned i = 0; i < optimizedFilters.size(); ++i) { | |
383 const WebKit::WebFilterOperation& op = optimizedFilters.at(i); | |
384 SkCanvas* canvas = state.canvas(); | |
385 switch (op.type()) { | |
386 case WebKit::WebFilterOperation::FilterTypeColorMatrix: { | |
387 SkPaint paint; | |
388 paint.setColorFilter(new SkColorMatrixFilter(op.matrix()))->unref(); | |
389 canvas->drawBitmap(state.source(), 0, 0, &paint); | |
390 break; | |
391 } | |
392 case WebKit::WebFilterOperation::FilterTypeBlur: { | |
393 float stdDeviation = op.amount(); | |
394 SkAutoTUnref<SkImageFilter> filter(new SkBlurImageFilter(stdDeviatio
n, stdDeviation)); | |
395 SkPaint paint; | |
396 paint.setImageFilter(filter.get()); | |
397 canvas->drawSprite(state.source(), 0, 0, &paint); | |
398 break; | |
399 } | |
400 case WebKit::WebFilterOperation::FilterTypeDropShadow: { | |
401 SkAutoTUnref<SkImageFilter> blurFilter(new SkBlurImageFilter(op.amou
nt(), op.amount())); | |
402 SkAutoTUnref<SkColorFilter> colorFilter(SkColorFilter::CreateModeFil
ter(op.dropShadowColor(), SkXfermode::kSrcIn_Mode)); | |
403 SkPaint paint; | |
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.drop
ShadowOffset().y); | |
409 canvas->restore(); | |
410 canvas->drawBitmap(state.source(), 0, 0); | |
411 break; | |
412 } | |
413 case WebKit::WebFilterOperation::FilterTypeZoom: { | |
414 SkPaint paint; | |
415 SkAutoTUnref<SkImageFilter> zoomFilter( | |
416 new SkMagnifierImageFilter( | |
417 SkRect::MakeXYWH(op.zoomRect().x, | |
418 op.zoomRect().y, | |
419 op.zoomRect().width, | |
420 op.zoomRect().height), | |
421 op.amount())); | |
422 paint.setImageFilter(zoomFilter.get()); | |
423 canvas->saveLayer(0, &paint); | |
424 canvas->drawBitmap(state.source(), 0, 0); | |
425 canvas->restore(); | |
426 break; | |
427 } | |
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: | |
436 ASSERT_NOT_REACHED(); | |
437 break; | |
438 } | |
439 state.swap(); | |
440 } | |
441 context3D->flush(); | |
442 return state.source(); | |
443 } | |
444 | |
445 } | |
446 #endif // USE(ACCELERATED_COMPOSITING) | |
OLD | NEW |