Accelerated filters should filter unpadded primitives.

Until recently, the Skia image filter infrastructure could
only produce a result image of the same size as its input
image.

For that reason, currently Blink applies filter outsets to
a layer before it passes the layer to cc. So cc sees a
layer padded with transparent black out to the filter
outsets, but has no idea how big the original layer was.
It then passes that to Skia, which produces an image of
the same size.

I've recently fixed Skia to be able to correctly draw from
the original (unpadded) texture size to the correct, padded
texture size. But to take advantage of that, we need Blink
to stop padding the texture (see PaintLayer).

This may result in a destination buffer which is of a
different size than the source, so we modify cc's drawing
to draw the destination rect.

Also, since we're giving the original (unmodified) source
rect to Skia, we no longer need to offset the crop rects
in Blink by the delta between the src and dest rects.
(Note: we can thus remove the crop offset stuff entirely
from Blink, which I'll do in a followup patch.)

Finally, note that the workaround for the
partially-occupied textures implemented (implemented in
https://codereview.chromium.org/909353003) was no
longer working when drawing to exact-size textures, due
to a bug in SkAlphaThresholdFilter. This is fixed in Skia
here:  https://codereview.chromium.org/1609573002/. That
will be required to roll into Chrome before this patch can
be landed.

BUG=568196
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel

Review URL: https://codereview.chromium.org/1517693002

Cr-Commit-Position: refs/heads/master@{#370523}
diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc
index 600206f..493adf9 100644
--- a/cc/layers/render_surface_impl.cc
+++ b/cc/layers/render_surface_impl.cc
@@ -50,6 +50,11 @@
     drawable_content_rect.Union(MathUtil::MapClippedRect(
         replica_draw_transform_, gfx::RectF(content_rect_)));
   }
+  if (!owning_layer_->filters().IsEmpty()) {
+    int left, top, right, bottom;
+    owning_layer_->filters().GetOutsets(&top, &right, &bottom, &left);
+    drawable_content_rect.Inset(-left, -top, -right, -bottom);
+  }
 
   // If the rect has a NaN coordinate, we return empty rect to avoid crashes in
   // functions (for example, gfx::ToEnclosedRect) that are called on this rect.
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
index acd7bf2..b2502d9 100644
--- a/cc/output/gl_renderer.cc
+++ b/cc/output/gl_renderer.cc
@@ -56,6 +56,7 @@
 #include "third_party/skia/include/gpu/gl/GrGLInterface.h"
 #include "ui/gfx/geometry/quad_f.h"
 #include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/skia_util.h"
 
 using gpu::gles2::GLES2Interface;
 
@@ -601,7 +602,8 @@
 static skia::RefPtr<SkImage> ApplyImageFilter(
     scoped_ptr<GLRenderer::ScopedUseGrContext> use_gr_context,
     ResourceProvider* resource_provider,
-    const gfx::Rect& rect,
+    const gfx::RectF& src_rect,
+    const gfx::RectF& dst_rect,
     const gfx::Vector2dF& scale,
     SkImageFilter* filter,
     ScopedResource* source_texture_resource) {
@@ -634,7 +636,7 @@
 
   // Create surface to draw into.
   SkImageInfo dst_info =
-      SkImageInfo::MakeN32Premul(srcImage->width(), srcImage->height());
+      SkImageInfo::MakeN32Premul(dst_rect.width(), dst_rect.height());
   skia::RefPtr<SkSurface> surface = skia::AdoptRef(SkSurface::NewRenderTarget(
       use_gr_context->context(), SkSurface::kYes_Budgeted, dst_info, 0));
   if (!surface) {
@@ -647,17 +649,17 @@
   // bottom-left, but the orientation is the same, so we must translate the
   // filter so that it renders at the bottom of the texture to avoid
   // misregistration.
-  int y_translate = source_texture_resource->size().height() - rect.height() -
-                    rect.origin().y();
-  SkMatrix localM;
-  localM.setTranslate(-rect.origin().x(), y_translate);
-  localM.preScale(scale.x(), scale.y());
-  skia::RefPtr<SkImageFilter> localIMF =
-      skia::AdoptRef(filter->newWithLocalMatrix(localM));
+  float y_offset = source_texture_resource->size().height() - src_rect.height();
+  SkMatrix local_matrix;
+  local_matrix.setScale(scale.x(), scale.y());
+  skia::RefPtr<SkImageFilter> filter_with_local_scale =
+      skia::AdoptRef(filter->newWithLocalMatrix(local_matrix));
 
   SkPaint paint;
-  paint.setImageFilter(localIMF.get());
-  surface->getCanvas()->drawImage(srcImage.get(), 0, 0, &paint);
+  paint.setImageFilter(filter_with_local_scale.get());
+  surface->getCanvas()->translate(-dst_rect.x(), -dst_rect.y());
+  surface->getCanvas()->drawImage(srcImage.get(), src_rect.x(),
+                                  src_rect.y() - y_offset, &paint);
 
   skia::RefPtr<SkImage> image = skia::AdoptRef(surface->newImageSnapshot());
   if (!image || !image->isTextureBacked()) {
@@ -822,6 +824,13 @@
     backdrop_rect.Inset(-kOutsetForAntialiasing, -kOutsetForAntialiasing);
   }
 
+  if (!quad->filters.IsEmpty()) {
+    // If we have filters, grab an extra one-pixel border around the
+    // background, so texture edge clamping gives us a transparent border
+    // in case the filter expands the result.
+    backdrop_rect.Inset(-1, -1, -1, -1);
+  }
+
   backdrop_rect.Intersect(MoveFromDrawToWindowSpace(
       frame, frame->current_render_pass->output_rect));
   return backdrop_rect;
@@ -846,13 +855,14 @@
 skia::RefPtr<SkImage> GLRenderer::ApplyBackgroundFilters(
     DrawingFrame* frame,
     const RenderPassDrawQuad* quad,
-    ScopedResource* background_texture) {
+    ScopedResource* background_texture,
+    const gfx::RectF& rect) {
   DCHECK(ShouldApplyBackgroundFilters(quad));
   skia::RefPtr<SkImageFilter> filter = RenderSurfaceFilters::BuildImageFilter(
       quad->background_filters, gfx::SizeF(background_texture->size()));
 
   skia::RefPtr<SkImage> background_with_filters = ApplyImageFilter(
-      ScopedUseGrContext::Create(this, frame), resource_provider_, quad->rect,
+      ScopedUseGrContext::Create(this, frame), resource_provider_, rect, rect,
       quad->filters_scale, filter.get(), background_texture);
   return background_with_filters;
 }
@@ -925,8 +935,8 @@
       if (ShouldApplyBackgroundFilters(quad) && background_texture) {
         // Apply the background filters to R, so that it is applied in the
         // pixels' coordinate space.
-        background_image =
-            ApplyBackgroundFilters(frame, quad, background_texture.get());
+        background_image = ApplyBackgroundFilters(
+            frame, quad, background_texture.get(), gfx::RectF(background_rect));
         if (background_image)
           background_image_id = background_image->getTextureHandle(true);
         DCHECK(background_image_id);
@@ -963,6 +973,7 @@
   GLuint filter_image_id = 0;
   SkScalar color_matrix[20];
   bool use_color_matrix = false;
+  gfx::RectF rect = gfx::RectF(quad->rect);
   if (!quad->filters.IsEmpty()) {
     skia::RefPtr<SkImageFilter> filter = RenderSurfaceFilters::BuildImageFilter(
         quad->filters, gfx::SizeF(contents_texture->size()));
@@ -980,9 +991,28 @@
         // in the compositor.
         use_color_matrix = true;
       } else {
-        filter_image = ApplyImageFilter(
-            ScopedUseGrContext::Create(this, frame), resource_provider_,
-            quad->rect, quad->filters_scale, filter.get(), contents_texture);
+        gfx::RectF src_rect = rect;
+        gfx::Vector2dF scale = quad->filters_scale;
+        // Compute the destination rect for the filtered output.
+        // Note that we leave the dest rect equal to the src rect when
+        // a filter chain cannot compute its bounds. This is correct
+        // behaviour, but Skia is a little conservative at the moment.
+        // Once Skia makes the fast-bounds traversal crop-rect aware
+        // (http://skbug.com/4627), this won't be a problem
+        // for Chrome since Blink always sets a crop rect on the leaf nodes
+        // of the DAG, making it always computable.
+        // TODO(senorblanco): remove this comment when http://skbug.com/4627
+        // is fixed.
+        if (filter->canComputeFastBounds()) {
+          SkRect result_rect;
+          rect.Scale(1.0f / scale.x(), 1.0f / scale.y());
+          filter->computeFastBounds(gfx::RectFToSkRect(rect), &result_rect);
+          rect = gfx::SkRectToRectF(result_rect);
+          rect.Scale(scale.x(), scale.y());
+        }
+        filter_image = ApplyImageFilter(ScopedUseGrContext::Create(this, frame),
+                                        resource_provider_, src_rect, rect,
+                                        scale, filter.get(), contents_texture);
         if (filter_image) {
           filter_image_id = filter_image->getTextureHandle(true);
           DCHECK(filter_image_id);
@@ -1096,10 +1126,16 @@
     program->fragment_shader().FillLocations(&locations);
     gl_->Uniform1i(locations.sampler, 0);
   }
-  float tex_scale_x =
-      quad->rect.width() / static_cast<float>(contents_texture->size().width());
-  float tex_scale_y = quad->rect.height() /
-                      static_cast<float>(contents_texture->size().height());
+  float tex_scale_x, tex_scale_y;
+  if (filter_image) {
+    // Skia filters always return SkImages with snug textures.
+    tex_scale_x = tex_scale_y = 1.0f;
+  } else {
+    tex_scale_x = quad->rect.width() /
+                  static_cast<float>(contents_texture->size().width());
+    tex_scale_y = quad->rect.height() /
+                  static_cast<float>(contents_texture->size().height());
+  }
   DCHECK_LE(tex_scale_x, 1.0f);
   DCHECK_LE(tex_scale_y, 1.0f);
 
@@ -1197,7 +1233,7 @@
   SetShaderOpacity(quad->shared_quad_state->opacity, locations.alpha);
   SetShaderQuadF(surface_quad, locations.quad);
   DrawQuadGeometry(frame, quad->shared_quad_state->quad_to_target_transform,
-                   gfx::RectF(quad->rect), locations.matrix);
+                   rect, locations.matrix);
 
   // Flush the compositor context before the filter bitmap goes out of
   // scope, so the draw gets processed before the filter texture gets deleted.
diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h
index 9599b07..dc3b6dd 100644
--- a/cc/output/gl_renderer.h
+++ b/cc/output/gl_renderer.h
@@ -176,7 +176,8 @@
   skia::RefPtr<SkImage> ApplyBackgroundFilters(
       DrawingFrame* frame,
       const RenderPassDrawQuad* quad,
-      ScopedResource* background_texture);
+      ScopedResource* background_texture,
+      const gfx::RectF& rect);
 
   void DrawRenderPassQuad(DrawingFrame* frame,
                           const RenderPassDrawQuad* quadi,
diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc
index f5afaea..52a9e6b 100644
--- a/cc/output/renderer_pixeltest.cc
+++ b/cc/output/renderer_pixeltest.cc
@@ -1773,11 +1773,11 @@
           root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
       filter_pass_quad->SetNew(shared_state, filter_pass_layer_rect_,
                                filter_pass_layer_rect_, filter_pass_id,
-                               0,                   // mask_resource_id
-                               gfx::Vector2dF(),    // mask_uv_scale
-                               gfx::Size(),         // mask_texture_size
-                               FilterOperations(),  // filters
-                               gfx::Vector2dF(),    // filters_scale
+                               0,                           // mask_resource_id
+                               gfx::Vector2dF(),            // mask_uv_scale
+                               gfx::Size(),                 // mask_texture_size
+                               FilterOperations(),          // filters
+                               gfx::Vector2dF(1.0f, 1.0f),  // filters_scale
                                this->background_filters_);
     }
 
diff --git a/cc/test/data/enlarged_texture_on_crop_offset.png b/cc/test/data/enlarged_texture_on_crop_offset.png
new file mode 100644
index 0000000..dc0adcb
--- /dev/null
+++ b/cc/test/data/enlarged_texture_on_crop_offset.png
Binary files differ
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index 54f5534..39d5b1c 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -1362,6 +1362,47 @@
             parent->render_surface()->DrawableContentRect());
 }
 
+TEST_F(LayerTreeHostCommonTest, RenderSurfaceListForFilter) {
+  LayerImpl* root = root_layer();
+  LayerImpl* parent = AddChild<LayerImpl>(root);
+  LayerImpl* child1 = AddChild<LayerImpl>(parent);
+  LayerImpl* child2 = AddChild<LayerImpl>(parent);
+  child1->SetDrawsContent(true);
+  child2->SetDrawsContent(true);
+
+  const gfx::Transform identity_matrix;
+  gfx::Transform scale_matrix;
+  scale_matrix.Scale(2.0f, 2.0f);
+  SetLayerPropertiesForTesting(root, identity_matrix, gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(100, 100), true, false,
+                               true);
+  SetLayerPropertiesForTesting(parent, scale_matrix, gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(), true, false, true);
+  SetLayerPropertiesForTesting(child1, identity_matrix, gfx::Point3F(),
+                               gfx::PointF(0, 0), gfx::Size(25, 25), true,
+                               false, true);
+  SetLayerPropertiesForTesting(child2, identity_matrix, gfx::Point3F(),
+                               gfx::PointF(25, 25), gfx::Size(25, 25), true,
+                               false, true);
+  FilterOperations filters;
+  filters.Append(FilterOperation::CreateBlurFilter(10.0f));
+  parent->SetFilters(filters);
+
+  LayerImplList render_surface_layer_list;
+  parent->layer_tree_impl()->IncrementRenderSurfaceListIdForTesting();
+  LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs(
+      root, root->bounds(), &render_surface_layer_list,
+      root->layer_tree_impl()->current_render_surface_list_id());
+  inputs.can_adjust_raster_scales = true;
+  LayerTreeHostCommon::CalculateDrawProperties(&inputs);
+
+  ASSERT_TRUE(parent->render_surface());
+  EXPECT_EQ(2U, parent->render_surface()->layer_list().size());
+  EXPECT_EQ(4U, render_surface_layer_list.size());
+  EXPECT_EQ(gfx::RectF(-29, -29, 158, 158),
+            parent->render_surface()->DrawableContentRect());
+}
+
 TEST_F(LayerTreeHostCommonTest, RenderSurfaceForBlendMode) {
   LayerImpl* parent = root_layer();
   LayerImpl* child = AddChild<LayerImpl>(parent);
diff --git a/cc/trees/layer_tree_host_pixeltest_filters.cc b/cc/trees/layer_tree_host_pixeltest_filters.cc
index 971fc375..cee0cbb 100644
--- a/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -10,6 +10,7 @@
 #include "cc/test/pixel_comparator.h"
 #include "third_party/skia/include/effects/SkColorFilterImageFilter.h"
 #include "third_party/skia/include/effects/SkColorMatrixFilter.h"
+#include "third_party/skia/include/effects/SkOffsetImageFilter.h"
 
 #if !defined(OS_ANDROID)
 
@@ -533,6 +534,59 @@
       base::FilePath(FILE_PATH_LITERAL("enlarged_texture_on_threshold.png")));
 }
 
+class EnlargedTextureWithCropOffsetFilter
+    : public LayerTreeHostFiltersPixelTest {
+ protected:
+  void RunPixelTestType(PixelTestType test_type, base::FilePath image_name) {
+    // Rectangles choosen so that if flipped, the test will fail.
+    gfx::Rect rect1(10, 10, 10, 15);
+    gfx::Rect rect2(20, 25, 70, 65);
+
+    scoped_refptr<SolidColorLayer> child1 =
+        CreateSolidColorLayer(rect1, SK_ColorRED);
+    scoped_refptr<SolidColorLayer> child2 =
+        CreateSolidColorLayer(rect2, SK_ColorGREEN);
+    scoped_refptr<SolidColorLayer> background =
+        CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorBLUE);
+    scoped_refptr<SolidColorLayer> filter_layer =
+        CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE);
+
+    // Make sure a transformation does not cause misregistration of the filter
+    // and source texture.
+    gfx::Transform filter_transform;
+    filter_transform.Scale(2.f, 2.f);
+    filter_layer->SetTransform(filter_transform);
+    filter_layer->AddChild(child1);
+    filter_layer->AddChild(child2);
+
+    FilterOperations filters;
+    SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(10, 10, 80, 80));
+    skia::RefPtr<SkImageFilter> filter(
+        skia::AdoptRef(SkOffsetImageFilter::Create(0, 0, nullptr, &cropRect)));
+    filters.Append(FilterOperation::CreateReferenceFilter(filter));
+    filter_layer->SetFilters(filters);
+
+    background->AddChild(filter_layer);
+
+    // Force the allocation a larger textures.
+    set_enlarge_texture_amount(gfx::Vector2d(50, 50));
+
+    RunPixelTest(test_type, background, image_name);
+  }
+};
+
+TEST_F(EnlargedTextureWithCropOffsetFilter, GL) {
+  RunPixelTestType(
+      PIXEL_TEST_GL,
+      base::FilePath(FILE_PATH_LITERAL("enlarged_texture_on_crop_offset.png")));
+}
+
+TEST_F(EnlargedTextureWithCropOffsetFilter, Software) {
+  RunPixelTestType(
+      PIXEL_TEST_SOFTWARE,
+      base::FilePath(FILE_PATH_LITERAL("enlarged_texture_on_crop_offset.png")));
+}
+
 }  // namespace
 }  // namespace cc
 
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 3e0caac..a0d8fd8 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1439,6 +1439,22 @@
 crbug.com/570899 [ Mac10.6 ] virtual/rootlayerscrolls/fast/scrolling/background-paint-scrolled.html [ Failure ]
 crbug.com/571361 [ Mac10.6 ] virtual/rootlayerscrolls/fast/scrolling/background-paint-scrolled-out-and-in.html [ Failure ]
 
+crbug.com/568196 css3/filters/effect-blur-hw.html [ NeedsRebaseline ]
+crbug.com/568196 css3/filters/effect-brightness-clamping-hw.html [ NeedsRebaseline ]
+crbug.com/568196 css3/filters/effect-drop-shadow-hw.html [ NeedsRebaseline ]
+crbug.com/568196 css3/filters/effect-reference-colorspace-hw.html [ NeedsRebaseline ]
+crbug.com/568196 css3/filters/effect-reference-hidpi-hw.html [ NeedsRebaseline ]
+crbug.com/568196 css3/filters/effect-reference-hw.html [ NeedsRebaseline ]
+crbug.com/568196 css3/filters/effect-reference-ordering-hw.html [ NeedsRebaseline ]
+crbug.com/568196 css3/filters/effect-reference-subregion-hw.html [ NeedsRebaseline ]
+crbug.com/568196 css3/filters/effect-reference-zoom-hw.html [ NeedsRebaseline ]
+crbug.com/568196 css3/filters/filter-change-repaint-composited.html [ NeedsRebaseline ]
+crbug.com/568196 css3/filters/filter-change-repaint.html [ NeedsRebaseline ]
+crbug.com/568196 css3/filters/filter-repaint-composited-fallback-crash.html [ NeedsRebaseline ]
+crbug.com/568196 css3/filters/filter-repaint-composited-fallback.html [ NeedsRebaseline ]
+crbug.com/568196 compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow.html [ NeedsRebaseline ]
+crbug.com/568196 compositing/culling/filter-occlusion-blur.html [ NeedsRebaseline ]
+
 crbug.com/320139 fast/repaint/block-layout-inline-children-replaced.html [ Pass Failure ]
 
 crbug.com/575766 http/tests/inspector/resource-tree/resource-tree-frame-add.html [ Timeout Pass ]
diff --git a/third_party/WebKit/LayoutTests/compositing/filters/sw-layer-overlaps-hw-shadow-expected.txt b/third_party/WebKit/LayoutTests/compositing/filters/sw-layer-overlaps-hw-shadow-expected.txt
index 3bbb6e1..34958e9 100644
--- a/third_party/WebKit/LayoutTests/compositing/filters/sw-layer-overlaps-hw-shadow-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/filters/sw-layer-overlaps-hw-shadow-expected.txt
@@ -10,9 +10,9 @@
           "shouldFlattenTransform": false,
           "children": [
             {
-              "position": [80, 80],
-              "transformOrigin": [75, 75],
-              "bounds": [125, 125],
+              "position": [105, 105],
+              "bounds": [100, 100],
+              "contentsOpaque": true,
               "drawsContent": true,
               "backgroundColor": "#000000"
             },
diff --git a/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-layer-expected.txt b/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-layer-expected.txt
index fdf59d36..aaae1ed4 100644
--- a/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-layer-expected.txt
@@ -14,7 +14,8 @@
           "backgroundColor": "#000000"
         },
         {
-          "bounds": [125, 125],
+          "bounds": [100, 100],
+          "contentsOpaque": true,
           "drawsContent": true,
           "backgroundColor": "#008000"
         }
diff --git a/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-shadow-expected.txt b/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-shadow-expected.txt
index f01b495b..6528642f 100644
--- a/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-shadow-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-shadow-expected.txt
@@ -7,14 +7,15 @@
       "drawsContent": true,
       "children": [
         {
-          "position": [105, 105],
-          "transformOrigin": [75, 75],
-          "bounds": [125, 125],
+          "position": [130, 130],
+          "bounds": [100, 100],
+          "contentsOpaque": true,
           "drawsContent": true,
           "backgroundColor": "#000000"
         },
         {
-          "bounds": [125, 125],
+          "bounds": [100, 100],
+          "contentsOpaque": true,
           "drawsContent": true,
           "backgroundColor": "#008000"
         }
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-for-filters-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-for-filters-expected.txt
index 8a87e986..205f651 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-for-filters-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-for-filters-expected.txt
@@ -17,8 +17,8 @@
           "shouldFlattenTransform": false,
           "children": [
             {
-              "position": [-5, -5],
-              "bounds": [62, 62],
+              "bounds": [50, 50],
+              "contentsOpaque": true,
               "drawsContent": true,
               "backgroundColor": "#D3D3D3"
             },
diff --git a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
index 769ceab..842feacc 100644
--- a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -7,9 +7,9 @@
       "drawsContent": true,
       "children": [
         {
-          "position": [30, 30],
-          "transformOrigin": [120, 120],
-          "bounds": [250, 250],
+          "position": [100, 100],
+          "bounds": [100, 100],
+          "contentsOpaque": true,
           "drawsContent": true,
           "backgroundColor": "#008000"
         }
diff --git a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
index 415e295..842feacc 100644
--- a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -7,8 +7,9 @@
       "drawsContent": true,
       "children": [
         {
-          "position": [30, 30],
-          "bounds": [240, 240],
+          "position": [100, 100],
+          "bounds": [100, 100],
+          "contentsOpaque": true,
           "drawsContent": true,
           "backgroundColor": "#008000"
         }
diff --git a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
index 83a081c..1bd31b864 100644
--- a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -7,14 +7,13 @@
       "drawsContent": true,
       "children": [
         {
-          "position": [50, 50],
-          "transformOrigin": [100, 100],
-          "bounds": [150, 150],
+          "position": [100, 100],
+          "bounds": [100, 100],
+          "contentsOpaque": true,
           "drawsContent": true,
           "backgroundColor": "#FF0000",
           "children": [
             {
-              "position": [50, 50],
               "bounds": [100, 100],
               "contentsOpaque": true,
               "drawsContent": true,
@@ -35,14 +34,13 @@
       "drawsContent": true,
       "children": [
         {
-          "position": [50, 50],
-          "transformOrigin": [100, 100],
-          "bounds": [150, 150],
+          "position": [100, 100],
+          "bounds": [100, 100],
+          "contentsOpaque": true,
           "drawsContent": true,
           "backgroundColor": "#FF0000",
           "children": [
             {
-              "position": [50, 50],
               "bounds": [100, 100],
               "contentsOpaque": true,
               "drawsContent": true,
diff --git a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
index 861b6d86..0c8ec75 100644
--- a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -34,7 +34,8 @@
         },
         {
           "position": [100, 100],
-          "bounds": [200, 200],
+          "bounds": [100, 100],
+          "contentsOpaque": true,
           "drawsContent": true,
           "backgroundColor": "#000000"
         }
diff --git a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
index 861b6d86..0c8ec75 100644
--- a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -34,7 +34,8 @@
         },
         {
           "position": [100, 100],
-          "bounds": [200, 200],
+          "bounds": [100, 100],
+          "contentsOpaque": true,
           "drawsContent": true,
           "backgroundColor": "#000000"
         }
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/filter-invalidation-with-composited-container-change-expected.txt b/third_party/WebKit/LayoutTests/fast/repaint/filter-invalidation-with-composited-container-change-expected.txt
index 7576108..57d9d50 100644
--- a/third_party/WebKit/LayoutTests/fast/repaint/filter-invalidation-with-composited-container-change-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/repaint/filter-invalidation-with-composited-container-change-expected.txt
@@ -13,12 +13,13 @@
       ],
       "children": [
         {
-          "position": [-20, -20],
-          "bounds": [256, 256],
+          "position": [8, 8],
+          "bounds": [200, 200],
+          "contentsOpaque": true,
           "drawsContent": true,
           "backgroundColor": "#008000",
           "repaintRects": [
-            [0, 0, 256, 256]
+            [-28, -28, 256, 256]
           ],
           "paintInvalidationClients": [
             "LayoutBlockFlow DIV id='box' class='green box blurry'"
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/filter-repaint-accelerated-child-with-filter-child-expected.txt b/third_party/WebKit/LayoutTests/fast/repaint/filter-repaint-accelerated-child-with-filter-child-expected.txt
index d0f3433..7f2bd19 100644
--- a/third_party/WebKit/LayoutTests/fast/repaint/filter-repaint-accelerated-child-with-filter-child-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/repaint/filter-repaint-accelerated-child-with-filter-child-expected.txt
@@ -7,12 +7,12 @@
       "drawsContent": true,
       "children": [
         {
-          "position": [-20, -20],
-          "bounds": [256, 256],
+          "position": [8, 8],
+          "bounds": [200, 200],
           "drawsContent": true,
           "children": [
             {
-              "position": [16, 16],
+              "position": [-12, -12],
               "transformOrigin": [112, 112],
               "bounds": [212, 256],
               "drawsContent": true,
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/filter-repaint-accelerated-on-accelerated-filter-expected.txt b/third_party/WebKit/LayoutTests/fast/repaint/filter-repaint-accelerated-on-accelerated-filter-expected.txt
index 6a4a5f4..f170e49b 100644
--- a/third_party/WebKit/LayoutTests/fast/repaint/filter-repaint-accelerated-on-accelerated-filter-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/repaint/filter-repaint-accelerated-on-accelerated-filter-expected.txt
@@ -7,19 +7,18 @@
       "drawsContent": true,
       "children": [
         {
-          "position": [-20, -20],
-          "bounds": [256, 256],
+          "position": [8, 8],
+          "bounds": [200, 200],
           "drawsContent": true,
           "children": [
             {
-              "position": [16, 16],
-              "transformOrigin": [62, 112],
-              "bounds": [156, 256],
+              "bounds": [100, 200],
+              "contentsOpaque": true,
               "drawsContent": true,
               "backgroundColor": "#008000",
               "repaintRects": [
-                [0, 0, 256, 256],
-                [0, 0, 156, 256]
+                [-12, -12, 256, 256],
+                [-12, -12, 156, 256]
               ],
               "paintInvalidationClients": [
                 "LayoutBlockFlow DIV id='resize' class='drop-shadow accelerated'"
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/filter-repaint-on-accelerated-layer-expected.txt b/third_party/WebKit/LayoutTests/fast/repaint/filter-repaint-on-accelerated-layer-expected.txt
index 5e906e9..bcf1ffc 100644
--- a/third_party/WebKit/LayoutTests/fast/repaint/filter-repaint-on-accelerated-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/repaint/filter-repaint-on-accelerated-layer-expected.txt
@@ -7,12 +7,11 @@
       "drawsContent": true,
       "children": [
         {
-          "position": [-20, -20],
-          "bounds": [256, 256],
+          "position": [8, 8],
+          "bounds": [200, 200],
           "drawsContent": true,
           "children": [
             {
-              "position": [28, 28],
               "bounds": [100, 200],
               "contentsOpaque": true,
               "drawsContent": true,
diff --git a/third_party/WebKit/Source/core/animation/AnimationTranslationUtil.cpp b/third_party/WebKit/Source/core/animation/AnimationTranslationUtil.cpp
index fe85013..1db74b09 100644
--- a/third_party/WebKit/Source/core/animation/AnimationTranslationUtil.cpp
+++ b/third_party/WebKit/Source/core/animation/AnimationTranslationUtil.cpp
@@ -116,8 +116,6 @@
 void toWebFilterOperations(const FilterOperations& inOperations, WebFilterOperations* outOperations)
 {
     SkiaImageFilterBuilder builder;
-    FilterOutsets outsets = inOperations.outsets();
-    builder.setCropOffset(FloatSize(outsets.left(), outsets.top()));
     builder.buildFilterOperations(inOperations, outOperations);
 }
 
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.cpp b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
index df982ee..f1f683b 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
@@ -2243,10 +2243,11 @@
         else
             expandRectForReflectionAndStackingChildren(this, result);
 
-        // FIXME: We can optimize the size of the composited layers, by not enlarging
-        // filtered areas with the outsets if we know that the filter is going to render in hardware.
-        // https://bugs.webkit.org/show_bug.cgi?id=81239
-        result.expand(filterOutsets());
+        // Only enlarge by the filter outsets if we know the filter is going to be rendered in software.
+        // Accelerated filters will handle their own outsets.
+        if (paintsWithFilters()) {
+            result.expand(filterOutsets());
+        }
     }
 
     if (transform() && paintsWithTransform(GlobalPaintNormalPhase) && (this != ancestorLayer || options == MaybeIncludeTransformForAncestorLayer))
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
index c9a9515f..b7b79c3 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
@@ -1142,8 +1142,6 @@
 {
     SkiaImageFilterBuilder builder;
     OwnPtr<WebFilterOperations> webFilters = adoptPtr(Platform::current()->compositorSupport()->createFilterOperations());
-    FilterOutsets outsets = filters.outsets();
-    builder.setCropOffset(FloatSize(outsets.left(), outsets.top()));
     builder.buildFilterOperations(filters, webFilters.get());
     m_layer->layer()->setFilters(*webFilters);
 }
@@ -1152,8 +1150,6 @@
 {
     SkiaImageFilterBuilder builder;
     OwnPtr<WebFilterOperations> webFilters = adoptPtr(Platform::current()->compositorSupport()->createFilterOperations());
-    FilterOutsets outsets = filters.outsets();
-    builder.setCropOffset(FloatSize(outsets.left(), outsets.top()));
     builder.buildFilterOperations(filters, webFilters.get());
     m_layer->layer()->setBackgroundFilters(*webFilters);
 }
diff --git a/third_party/WebKit/Source/platform/graphics/filters/SkiaImageFilterBuilder.h b/third_party/WebKit/Source/platform/graphics/filters/SkiaImageFilterBuilder.h
index a0a297b..e07970a7c 100644
--- a/third_party/WebKit/Source/platform/graphics/filters/SkiaImageFilterBuilder.h
+++ b/third_party/WebKit/Source/platform/graphics/filters/SkiaImageFilterBuilder.h
@@ -50,8 +50,6 @@
 
     PassRefPtr<SkImageFilter> transformColorSpace(
         SkImageFilter* input, ColorSpace srcColorSpace, ColorSpace dstColorSpace);
-
-    void setCropOffset(const FloatSize& cropOffset) { m_cropOffset = cropOffset; }
     FloatSize cropOffset() { return m_cropOffset; }
 
 private: