Diff
Modified: trunk/Source/WebCore/ChangeLog (119848 => 119849)
--- trunk/Source/WebCore/ChangeLog 2012-06-08 18:36:30 UTC (rev 119848)
+++ trunk/Source/WebCore/ChangeLog 2012-06-08 18:39:37 UTC (rev 119849)
@@ -1,3 +1,36 @@
+2012-06-08 No'am Rosenthal <[email protected]>
+
+ [Texmap] Accelerated versions of drop-shadow and blur filters
+ https://bugs.webkit.org/show_bug.cgi?id=87695
+
+ Reviewed by Kenneth Rohde Christiansen.
+
+ Added shaders for blur and drop-shadow effects.
+ The shaders use sampling of multiple coordinates and averaging them with normal-
+ distribution to create a fast gaussian blur effect.
+
+ Covered by existing tests in css3/filters.
+
+ * platform/graphics/texmap/TextureMapperGL.cpp:
+ (WebCore::TextureMapperGL::drawFiltered):
+ Call the prepare function explicitly, with a size argument that's used to calculate
+ some of the uniform values.
+
+ * platform/graphics/texmap/TextureMapperLayer.cpp:
+ (WebCore::TextureMapperLayer::intermediateSurfaceRect):
+ The outsets of the effect have to be considered when calculating the intermediate rect.
+ Otherwise the resulting image is scaled instead of outsetted.
+
+ (WebCore::applyFilters):
+ * platform/graphics/texmap/TextureMapperShaderManager.cpp:
+ (WebCore::StandardFilterProgram::StandardFilterProgram):
+ Added the new shaders.
+
+ (WebCore::StandardFilterProgram::prepare):
+ (WebCore::TextureMapperShaderManager::getShaderForFilter):
+ * platform/graphics/texmap/TextureMapperShaderManager.h:
+ (StandardFilterProgram):
+
2012-06-08 Dan Bernstein <[email protected]>
Tried to fix the build after r119844.
Modified: trunk/Source/WebCore/platform/graphics/texmap/TextureMapperGL.cpp (119848 => 119849)
--- trunk/Source/WebCore/platform/graphics/texmap/TextureMapperGL.cpp 2012-06-08 18:36:30 UTC (rev 119848)
+++ trunk/Source/WebCore/platform/graphics/texmap/TextureMapperGL.cpp 2012-06-08 18:39:37 UTC (rev 119849)
@@ -558,14 +558,14 @@
}
#if ENABLE(CSS_FILTERS)
-void TextureMapperGL::drawFiltered(const BitmapTexture& sourceTexture, const BitmapTexture& contentTexture, const FilterOperation& filter)
+void TextureMapperGL::drawFiltered(const BitmapTexture& sourceTexture, const BitmapTexture& contentTexture, const FilterOperation& filter, int pass)
{
// For standard filters, we always draw the whole texture without transformations.
- RefPtr<StandardFilterProgram> program = data().sharedGLData().textureMapperShaderManager.getShaderForFilter(filter);
- if (!program) {
- drawTexture(sourceTexture, FloatRect(FloatPoint::zero(), sourceTexture.size()), TransformationMatrix(), 1, 0);
- return;
- }
+ RefPtr<StandardFilterProgram> program = data().sharedGLData().textureMapperShaderManager.getShaderForFilter(filter, pass);
+ ASSERT(program);
+
+ program->prepare(filter, pass, sourceTexture.contentSize(), static_cast<const BitmapTextureGL&>(contentTexture).id());
+
GL_CMD(glEnableVertexAttribArray(program->vertexAttrib()));
GL_CMD(glEnableVertexAttribArray(program->texCoordAttrib()));
GL_CMD(glActiveTexture(GL_TEXTURE0));
@@ -591,9 +591,12 @@
const FilterOperation* filter = filters.at(i);
ASSERT(filter);
- m_textureMapper->bindSurface(target.get());
- m_textureMapper->drawFiltered(i ? *source.get() : contentTexture, contentTexture, *filter);
- std::swap(source, target);
+ int numPasses = m_textureMapper->data().sharedGLData().textureMapperShaderManager.getPassesRequiredForFilter(*filter);
+ for (int j = 0; j < numPasses; ++j) {
+ m_textureMapper->bindSurface(target.get());
+ m_textureMapper->drawFiltered((i || j) ? *source : contentTexture, contentTexture, *filter, j);
+ std::swap(source, target);
+ }
}
m_textureMapper->bindSurface(previousSurface.get());
Modified: trunk/Source/WebCore/platform/graphics/texmap/TextureMapperGL.h (119848 => 119849)
--- trunk/Source/WebCore/platform/graphics/texmap/TextureMapperGL.h 2012-06-08 18:36:30 UTC (rev 119848)
+++ trunk/Source/WebCore/platform/graphics/texmap/TextureMapperGL.h 2012-06-08 18:39:37 UTC (rev 119849)
@@ -67,7 +67,7 @@
virtual void setGraphicsContext(GraphicsContext* context) OVERRIDE { m_context = context; }
#if ENABLE(CSS_FILTERS)
- void drawFiltered(const BitmapTexture& sourceTexture, const BitmapTexture& contentTexture, const FilterOperation&);
+ void drawFiltered(const BitmapTexture& sourceTexture, const BitmapTexture& contentTexture, const FilterOperation&, int pass);
#endif
Modified: trunk/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp (119848 => 119849)
--- trunk/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp 2012-06-08 18:36:30 UTC (rev 119848)
+++ trunk/Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp 2012-06-08 18:39:37 UTC (rev 119849)
@@ -216,6 +216,20 @@
rect.unite(m_children[i]->intermediateSurfaceRect(matrix));
}
+#if ENABLE(CSS_FILTERS)
+ if (m_state.filters.hasOutsets()) {
+ int leftOutset;
+ int topOutset;
+ int bottomOutset;
+ int rightOutset;
+ m_state.filters.getOutsets(topOutset, rightOutset, bottomOutset, leftOutset);
+ IntRect unfilteredTargetRect(rect);
+ rect.move(std::max(0, -leftOutset), std::max(0, -topOutset));
+ rect.expand(leftOutset + rightOutset, topOutset + bottomOutset);
+ rect.unite(unfilteredTargetRect);
+ }
+#endif
+
if (m_state.replicaLayer)
rect.unite(m_state.replicaLayer->intermediateSurfaceRect(matrix));
@@ -296,22 +310,28 @@
}
#if ENABLE(CSS_FILTERS)
+static bool shouldKeepContentTexture(const FilterOperations& filters)
+{
+ for (int i = 0; i < filters.size(); ++i) {
+ switch (filters.operations().at(i)->getOperationType()) {
+ // The drop-shadow filter requires the content texture, because it needs to composite it
+ // on top of the blurred shadow color.
+ case FilterOperation::DROP_SHADOW:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
static PassRefPtr<BitmapTexture> applyFilters(const FilterOperations& filters, TextureMapper* textureMapper, BitmapTexture* source, IntRect& targetRect)
{
if (!filters.size())
return source;
- RefPtr<BitmapTexture> filterSurface(source);
- int leftOutset, topOutset, bottomOutset, rightOutset;
- if (filters.hasOutsets()) {
- filters.getOutsets(topOutset, rightOutset, bottomOutset, leftOutset);
- IntRect unfilteredTargetRect(targetRect);
- targetRect.move(std::max(0, -leftOutset), std::max(0, -topOutset));
- targetRect.expand(leftOutset + rightOutset, topOutset + bottomOutset);
- targetRect.unite(unfilteredTargetRect);
- filterSurface = textureMapper->acquireTextureFromPool(targetRect.size());
- }
-
+ RefPtr<BitmapTexture> filterSurface = shouldKeepContentTexture(filters) ? textureMapper->acquireTextureFromPool(source->size()) : source;
return filterSurface->applyFilters(*source, filters);
}
#endif
Modified: trunk/Source/WebCore/platform/graphics/texmap/TextureMapperShaderManager.cpp (119848 => 119849)
--- trunk/Source/WebCore/platform/graphics/texmap/TextureMapperShaderManager.cpp 2012-06-08 18:36:30 UTC (rev 119848)
+++ trunk/Source/WebCore/platform/graphics/texmap/TextureMapperShaderManager.cpp 2012-06-08 18:39:37 UTC (rev 119849)
@@ -22,6 +22,8 @@
#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
+#include "LengthFunctions.h"
+#include "Logging.h"
#include "TextureMapperGL.h"
namespace WebCore {
@@ -359,7 +361,7 @@
glDeleteProgram(m_id);
}
-StandardFilterProgram::StandardFilterProgram(FilterOperation::OperationType type)
+StandardFilterProgram::StandardFilterProgram(FilterOperation::OperationType type, unsigned pass)
: m_id(0)
{
const char* vertexShaderSource =
@@ -468,6 +470,147 @@
}
);
break;
+ case FilterOperation::BLUR:
+ fragmentShaderSource = FRAGMENT_SHADER(
+ varying highp vec2 v_texCoord;
+ uniform lowp vec2 u_blurRadius;
+ uniform sampler2D u_texture;
+ const float pi = 3.14159;
+ const float e = 2.71828;
+
+ // FIXME: share gaussian formula between shaders.
+ lowp float gaussian(lowp float value)
+ {
+ // Normal distribution formula, when the mean is 0 and the standard deviation is 1.
+ return pow(e, -pow(value, 2.) / 2.) / (sqrt(2. * pi));
+ }
+
+ lowp vec4 sampleColor(float radius)
+ {
+ vec2 coord = v_texCoord + radius * u_blurRadius;
+ return texture2D(u_texture, coord) * float(coord.x > 0. && coord.y > 0. && coord.x < 1. && coord.y < 1.);
+ }
+
+ vec4 blur()
+ {
+ // Create a normal distribution of 20 values between 0. and 2.
+ vec4 total = vec4(0., 0., 0., 0.);
+ float totalWeight = 0.;
+ for (float i = -2.; i <= 2.; i += .2) {
+ float weight = gaussian(i);
+ total += sampleColor(i) * weight;
+ totalWeight += weight;
+ }
+
+ return total / totalWeight;
+ }
+
+ void main(void)
+ {
+ gl_FragColor = blur();
+ }
+ );
+ break;
+ case FilterOperation::DROP_SHADOW:
+ switch (pass) {
+ case 0: {
+ // First pass: horizontal alpha blur.
+ fragmentShaderSource = FRAGMENT_SHADER(
+ varying highp vec2 v_texCoord;
+ uniform lowp float u_shadowBlurRadius;
+ uniform lowp vec2 u_shadowOffset;
+ uniform sampler2D u_texture;
+ const float pi = 3.14159;
+ const float e = 2.71828;
+
+ // FIXME: share gaussian formula between shaders.
+ lowp float gaussian(lowp float value)
+ {
+ // Normal distribution formula, when the mean is 0 and the standard deviation is 1.
+ return pow(e, -pow(value, 2.) / 2.) / (sqrt(2. * pi));
+ }
+
+ lowp float sampleAlpha(float radius)
+ {
+ vec2 coord = v_texCoord - u_shadowOffset + vec2(radius * u_shadowBlurRadius, 0.);
+ return texture2D(u_texture, coord).a * float(coord.x > 0. && coord.y > 0. && coord.x < 1. && coord.y < 1.);
+ }
+
+ lowp float shadowBlurHorizontal()
+ {
+ // Create a normal distribution of 20 values between -2 and 2.
+ float total = 0.;
+ float totalWeight = 0.;
+ for (float i = -2.; i <= 2.; i += .2) {
+ float weight = gaussian(i);
+ total += sampleAlpha(i) * weight;
+ totalWeight += weight;
+ }
+
+ return total / totalWeight;
+ }
+
+ void main(void)
+ {
+ gl_FragColor = vec4(1., 1., 1., 1.) * shadowBlurHorizontal();
+ }
+ );
+ break;
+ }
+ case 1: {
+ // Second pass: vertical alpha blur and composite with origin.
+ fragmentShaderSource = FRAGMENT_SHADER(
+ varying highp vec2 v_texCoord;
+ uniform lowp float u_shadowBlurRadius;
+ uniform lowp vec4 u_shadowColor;
+ uniform sampler2D u_texture;
+ uniform sampler2D u_contentTexture;
+
+ // FIXME: share gaussian formula between shaders.
+ const float pi = 3.14159;
+ const float e = 2.71828;
+
+ lowp float gaussian(float value)
+ {
+ // Normal distribution formula, when the mean is 0 and the standard deviation is 1.
+ return pow(e, -pow(value, 2.) / 2.) / (sqrt(2. * pi));
+ }
+
+ lowp float sampleAlpha(float r)
+ {
+ vec2 coord = v_texCoord + vec2(0., r * u_shadowBlurRadius);
+ return texture2D(u_texture, coord).a * float(coord.x > 0. && coord.y > 0. && coord.x < 1. && coord.y < 1.);
+ }
+
+ lowp float shadowBlurVertical()
+ {
+ // Create a normal distribution of 20 values between -2 and 2.
+ float total = 0.;
+ float totalWeight = 0.;
+ for (float i = -2.; i <= 2.; i += .2) {
+ float weight = gaussian(i);
+ total += sampleAlpha(i) * weight;
+ totalWeight += weight;
+ }
+
+ return total / totalWeight;
+ }
+
+ lowp vec4 sourceOver(lowp vec4 source, lowp vec4 destination)
+ {
+ // Composite the shadow with the original texture.
+ return source + destination * (1. - source.a);
+ }
+
+ void main(void)
+ {
+ gl_FragColor = sourceOver(texture2D(u_contentTexture, v_texCoord), shadowBlurVertical() * u_shadowColor);
+ }
+ );
+ break;
+ }
+ break;
+ }
default:
break;
}
@@ -478,12 +621,15 @@
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, 0);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, 0);
- GLchar log[100];
- GLint len;
GLuint programID = glCreateProgram();
glCompileShader(vertexShader);
glCompileShader(fragmentShader);
+#if !LOG_DISABLED
+ GLchar log[100];
+ GLint len;
glGetShaderInfoLog(fragmentShader, 100, &len, log);
+ WTFLog(&LogCompositing, "%s\n", log);
+#endif
glAttachShader(programID, vertexShader);
glAttachShader(programID, fragmentShader);
glLinkProgram(programID);
@@ -502,6 +648,19 @@
case FilterOperation::OPACITY:
m_uniformLocations.amount = glGetUniformLocation(programID, "u_amount");
break;
+ case FilterOperation::BLUR:
+ m_uniformLocations.blur.radius = glGetUniformLocation(programID, "u_blurRadius");
+ break;
+ case FilterOperation::DROP_SHADOW:
+ m_uniformLocations.shadow.blurRadius = glGetUniformLocation(programID, "u_shadowBlurRadius");
+ if (!pass)
+ m_uniformLocations.shadow.offset = glGetUniformLocation(programID, "u_shadowOffset");
+ else {
+ // We only need the color and the content texture in the second pass, the first pass is only a horizontal alpha blur.
+ m_uniformLocations.shadow.color = glGetUniformLocation(programID, "u_shadowColor");
+ m_uniformLocations.shadow.contentTexture = glGetUniformLocation(programID, "u_contentTexture");
+ }
+ break;
default:
break;
}
@@ -510,56 +669,110 @@
m_fragmentShader = fragmentShader;
}
-PassRefPtr<StandardFilterProgram> StandardFilterProgram::create(FilterOperation::OperationType type)
+PassRefPtr<StandardFilterProgram> StandardFilterProgram::create(FilterOperation::OperationType type, unsigned pass)
{
- RefPtr<StandardFilterProgram> program = adoptRef(new StandardFilterProgram(type));
+ RefPtr<StandardFilterProgram> program = adoptRef(new StandardFilterProgram(type, pass));
if (!program->m_id)
return 0;
return program;
}
-void StandardFilterProgram::prepare(const FilterOperation& operation)
+void StandardFilterProgram::prepare(const FilterOperation& operation, unsigned pass, const IntSize& size, GLuint contentTexture)
{
- double amount = 0;
+ glUseProgram(m_id);
switch (operation.getOperationType()) {
case FilterOperation::GRAYSCALE:
case FilterOperation::SEPIA:
case FilterOperation::SATURATE:
case FilterOperation::HUE_ROTATE:
- amount = static_cast<const BasicColorMatrixFilterOperation&>(operation).amount();
+ glUniform1f(m_uniformLocations.amount, static_cast<const BasicColorMatrixFilterOperation&>(operation).amount());
break;
case FilterOperation::INVERT:
case FilterOperation::BRIGHTNESS:
case FilterOperation::CONTRAST:
case FilterOperation::OPACITY:
- amount = static_cast<const BasicComponentTransferFilterOperation&>(operation).amount();
+ glUniform1f(m_uniformLocations.amount, static_cast<const BasicComponentTransferFilterOperation&>(operation).amount());
break;
+ case FilterOperation::BLUR: {
+ const BlurFilterOperation& blur = static_cast<const BlurFilterOperation&>(operation);
+ FloatSize radius;
+
+ // Blur is done in two passes, first horizontally and then vertically. The same shader is used for both.
+ if (pass)
+ radius.setHeight(floatValueForLength(blur.stdDeviation(), size.height()) / size.height());
+ else
+ radius.setWidth(floatValueForLength(blur.stdDeviation(), size.width()) / size.width());
+
+ glUniform2f(m_uniformLocations.blur.radius, radius.width(), radius.height());
+ break;
+ }
+ case FilterOperation::DROP_SHADOW: {
+ const DropShadowFilterOperation& shadow = static_cast<const DropShadowFilterOperation&>(operation);
+ switch (pass) {
+ case 0:
+ // First pass: vertical alpha blur.
+ glUniform2f(m_uniformLocations.shadow.offset, float(shadow.location().x()) / float(size.width()), float(shadow.location().y()) / float(size.height()));
+ glUniform1f(m_uniformLocations.shadow.blurRadius, shadow.stdDeviation() / float(size.width()));
+ break;
+ case 1:
+ // Second pass: we need the shadow color and the content texture for compositing.
+ glUniform1f(m_uniformLocations.shadow.blurRadius, shadow.stdDeviation() / float(size.height()));
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, contentTexture);
+ glUniform1i(m_uniformLocations.shadow.contentTexture, 1);
+ float r, g, b, a;
+ shadow.color().getRGBA(r, g, b, a);
+ glUniform4f(m_uniformLocations.shadow.color, r, g, b, a);
+ break;
+ }
+ break;
+ }
default:
break;
}
- glUseProgram(m_id);
- glUniform1f(m_uniformLocations.amount, amount);
}
-PassRefPtr<StandardFilterProgram> TextureMapperShaderManager::getShaderForFilter(const FilterOperation& filter)
+PassRefPtr<StandardFilterProgram> TextureMapperShaderManager::getShaderForFilter(const FilterOperation& filter, unsigned pass)
{
RefPtr<StandardFilterProgram> program;
FilterOperation::OperationType type = filter.getOperationType();
- FilterMap::iterator iterator = m_filterMap.find(type);
+ int key = int(type) | (pass << 16);
+ FilterMap::iterator iterator = m_filterMap.find(key);
if (iterator == m_filterMap.end()) {
- program = StandardFilterProgram::create(type);
+ program = StandardFilterProgram::create(type, pass);
if (!program)
return 0;
- m_filterMap.add(type, program);
+ m_filterMap.add(key, program);
} else
program = iterator->second;
- program->prepare(filter);
return program;
}
+unsigned TextureMapperShaderManager::getPassesRequiredForFilter(const FilterOperation& operation) const
+{
+ switch (operation.getOperationType()) {
+ case FilterOperation::GRAYSCALE:
+ case FilterOperation::SEPIA:
+ case FilterOperation::SATURATE:
+ case FilterOperation::HUE_ROTATE:
+ case FilterOperation::INVERT:
+ case FilterOperation::BRIGHTNESS:
+ case FilterOperation::CONTRAST:
+ case FilterOperation::OPACITY:
+ return 1;
+ case FilterOperation::BLUR:
+ case FilterOperation::DROP_SHADOW:
+ // We use two-passes (vertical+horizontal) for blur and drop-shadow.
+ return 2;
+ default:
+ return 0;
+ }
+
+}
+
#endif
};
Modified: trunk/Source/WebCore/platform/graphics/texmap/TextureMapperShaderManager.h (119848 => 119849)
--- trunk/Source/WebCore/platform/graphics/texmap/TextureMapperShaderManager.h 2012-06-08 18:36:30 UTC (rev 119848)
+++ trunk/Source/WebCore/platform/graphics/texmap/TextureMapperShaderManager.h 2012-06-08 18:39:37 UTC (rev 119849)
@@ -74,13 +74,13 @@
class StandardFilterProgram : public RefCounted<StandardFilterProgram> {
public:
virtual ~StandardFilterProgram();
- virtual void prepare(const FilterOperation&);
- static PassRefPtr<StandardFilterProgram> create(FilterOperation::OperationType);
+ virtual void prepare(const FilterOperation&, unsigned pass, const IntSize&, GLuint contentTexture);
+ static PassRefPtr<StandardFilterProgram> create(FilterOperation::OperationType, unsigned pass);
GLuint vertexAttrib() const { return m_vertexAttrib; }
GLuint texCoordAttrib() const { return m_texCoordAttrib; }
GLuint textureUniform() const { return m_textureUniformLocation; }
private:
- StandardFilterProgram(FilterOperation::OperationType);
+ StandardFilterProgram(FilterOperation::OperationType, unsigned pass);
GLuint m_id;
GLuint m_vertexShader;
GLuint m_fragmentShader;
@@ -89,11 +89,16 @@
GLuint m_textureUniformLocation;
union {
GLuint amount;
- GLuint stddev;
+
struct {
- GLuint stddev;
+ GLuint radius;
+ } blur;
+
+ struct {
+ GLuint blurRadius;
GLuint color;
GLuint offset;
+ GLuint contentTexture;
} shadow;
} m_uniformLocations;
};
@@ -182,8 +187,10 @@
virtual ~TextureMapperShaderManager();
#if ENABLE(CSS_FILTERS)
- PassRefPtr<StandardFilterProgram> getShaderForFilter(const FilterOperation&);
+ unsigned getPassesRequiredForFilter(const FilterOperation&) const;
+ PassRefPtr<StandardFilterProgram> getShaderForFilter(const FilterOperation&, unsigned pass);
#endif
+
PassRefPtr<TextureMapperShaderProgram> getShaderProgram(ShaderType);
PassRefPtr<TextureMapperShaderProgramSolidColor> solidColorProgram();
@@ -192,7 +199,7 @@
TextureMapperShaderProgramMap m_textureMapperShaderProgramMap;
#if ENABLE(CSS_FILTERS)
- typedef HashMap<FilterOperation::OperationType, RefPtr<StandardFilterProgram>, DefaultHash<int>::Hash, HashTraits<int> > FilterMap;
+ typedef HashMap<int, RefPtr<StandardFilterProgram> > FilterMap;
FilterMap m_filterMap;
#endif