| 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 |