[Texmap] Accelerated versions of drop-shadow and blur filters
authornoam.rosenthal@nokia.com <noam.rosenthal@nokia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Jun 2012 18:39:37 +0000 (18:39 +0000)
committernoam.rosenthal@nokia.com <noam.rosenthal@nokia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Jun 2012 18:39:37 +0000 (18:39 +0000)
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):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@119849 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/texmap/TextureMapperGL.cpp
Source/WebCore/platform/graphics/texmap/TextureMapperGL.h
Source/WebCore/platform/graphics/texmap/TextureMapperLayer.cpp
Source/WebCore/platform/graphics/texmap/TextureMapperShaderManager.cpp
Source/WebCore/platform/graphics/texmap/TextureMapperShaderManager.h

index 8e8ef0f..825f76b 100644 (file)
@@ -1,3 +1,36 @@
+2012-06-08  No'am Rosenthal  <noam.rosenthal@nokia.com>
+
+        [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  <mitz@apple.com>
 
         Tried to fix the build after r119844.
index 10123d2..b164a1f 100644 (file)
@@ -558,14 +558,14 @@ void BitmapTextureGL::updateContents(Image* image, const IntRect& targetRect, co
 }
 
 #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 @@ PassRefPtr<BitmapTexture> BitmapTextureGL::applyFilters(const BitmapTexture& con
         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());
index c3bbff3..16d686b 100644 (file)
@@ -67,7 +67,7 @@ public:
     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
 
 
index 96cafac..b30b4b1 100644 (file)
@@ -216,6 +216,20 @@ IntRect TextureMapperLayer::intermediateSurfaceRect(const TransformationMatrix&
             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 @@ void TextureMapperLayer::paintSelfAndChildrenWithReplica(const TextureMapperPain
 }
 
 #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
index e8bfba1..12a45e3 100644 (file)
@@ -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 @@ StandardFilterProgram::~StandardFilterProgram()
     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 @@ StandardFilterProgram::StandardFilterProgram(FilterOperation::OperationType type
             }
         );
         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 @@ StandardFilterProgram::StandardFilterProgram(FilterOperation::OperationType type
     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 @@ StandardFilterProgram::StandardFilterProgram(FilterOperation::OperationType type
     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 @@ StandardFilterProgram::StandardFilterProgram(FilterOperation::OperationType type
     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
 };
 
index 681855c..3f0b489 100644 (file)
@@ -74,13 +74,13 @@ protected:
 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 @@ private:
     GLuint m_textureUniformLocation;
     union {
         GLuint amount;
-        GLuint stddev;
+
+        struct {
+            GLuint radius;
+        } blur;
+
         struct {
-            GLuint stddev;
+            GLuint blurRadius;
             GLuint color;
             GLuint offset;
+            GLuint contentTexture;
         } shadow;
     } m_uniformLocations;
 };
@@ -182,8 +187,10 @@ public:
     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 @@ private:
     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