Bug 945655 - Make all filters deal with null inputs and empty output rects consistently: treat them as transparent. r=roc, a=lsblakk
authorMarkus Stange <mstange@themasta.com>
Tue, 10 Dec 2013 14:50:40 +0100
changeset 175483 d06c5131e5730f596bc7b8160497d3a8575b2b31
parent 175482 a42fe878d29bb72a5b1f202fe1b83920f599e976
child 175484 462fcf3088631bf50c7f4d50b3e8420188ab09e2
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, lsblakk
bugs945655
milestone28.0a2
Bug 945655 - Make all filters deal with null inputs and empty output rects consistently: treat them as transparent. r=roc, a=lsblakk
gfx/2d/FilterNodeSoftware.cpp
--- a/gfx/2d/FilterNodeSoftware.cpp
+++ b/gfx/2d/FilterNodeSoftware.cpp
@@ -563,19 +563,23 @@ FilterNodeSoftware::Draw(DrawTarget* aDr
 #endif
 
   Rect renderRect = aSourceRect;
   renderRect.RoundOut();
   IntRect renderIntRect(int32_t(renderRect.x), int32_t(renderRect.y),
                         int32_t(renderRect.width), int32_t(renderRect.height));
   IntRect outputRect = renderIntRect.Intersect(GetOutputRectInRect(renderIntRect));
 
-  // Render.
-  RefPtr<DataSourceSurface> result = GetOutput(outputRect);
+  RefPtr<DataSourceSurface> result;
+  if (!outputRect.IsEmpty()) {
+    result = GetOutput(outputRect);
+  }
+
   if (!result) {
+    // Null results are allowed and treated as transparent. Don't draw anything.
 #ifdef DEBUG_DUMP_SURFACES
     printf("output returned null\n");
     printf("</pre>\n");
 #endif
     return;
   }
 
   // Add transparency around outputRect in renderIntRect.
@@ -598,20 +602,16 @@ FilterNodeSoftware::Draw(DrawTarget* aDr
   aDrawTarget->DrawSurface(result, Rect(aDestPoint, aSourceRect.Size()),
                            aSourceRect - renderIntRect.TopLeft(),
                            DrawSurfaceOptions(), aOptions);
 }
 
 TemporaryRef<DataSourceSurface>
 FilterNodeSoftware::GetOutput(const IntRect &aRect)
 {
-  if (aRect.IsEmpty()) {
-    return nullptr;
-  }
-
   MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect));
   if (!mCachedRect.Contains(aRect)) {
     RequestRect(aRect);
     mCachedOutput = Render(mRequestedRect);
     if (!mCachedOutput) {
       mCachedRect = IntRect();
       mRequestedRect = IntRect();
       return nullptr;
@@ -668,16 +668,20 @@ FilterNodeSoftware::GetInputDataSourceSu
          aRect.x, aRect.y, aRect.width, aRect.height);
 #endif
   int32_t inputIndex = InputIndex(aInputEnumIndex);
   if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
     MOZ_CRASH();
     return nullptr;
   }
 
+  if (aRect.IsEmpty()) {
+    return nullptr;
+  }
+
   RefPtr<SourceSurface> surface;
   IntRect surfaceRect;
   if (mInputSurfaces[inputIndex]) {
     surface = mInputSurfaces[inputIndex];
 #ifdef DEBUG_DUMP_SURFACES
     printf("input from input surface:\n");
     printf("<img src='"); DumpAsPNG(surface); printf("'>\n");
 #endif
@@ -882,21 +886,33 @@ FilterNodeBlendSoftware::SetAttribute(ui
 
 TemporaryRef<DataSourceSurface>
 FilterNodeBlendSoftware::Render(const IntRect& aRect)
 {
   RefPtr<DataSourceSurface> input1 =
     GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS);
   RefPtr<DataSourceSurface> input2 =
     GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS);
-  if (!input1 || !input2) {
+
+  // Null inputs need to be treated as transparent.
+
+  // First case: both are transparent.
+  if (!input1 && !input2) {
+    // Then the result is transparent, too.
     return nullptr;
   }
 
-  return FilterProcessing::ApplyBlending(input1, input2, mBlendMode);
+  // Second case: both are non-transparent.
+  if (input1 && input2) {
+    // Apply normal filtering.
+    return FilterProcessing::ApplyBlending(input1, input2, mBlendMode);
+  }
+
+  // Third case: one of them is transparent. Return the non-transparent one.
+  return input1 ? input1 : input2;
 }
 
 void
 FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect &aRect)
 {
   RequestInputRect(IN_BLEND_IN, aRect);
   RequestInputRect(IN_BLEND_IN2, aRect);
 }
@@ -935,16 +951,20 @@ FilterNodeTransformSoftware::SetAttribut
   MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX);
   mMatrix = aMatrix;
   Invalidate();
 }
 
 IntRect
 FilterNodeTransformSoftware::SourceRectForOutputRect(const IntRect &aRect)
 {
+  if (aRect.IsEmpty()) {
+    return IntRect();
+  }
+
   Matrix inverted(mMatrix);
   if (!inverted.Invert()) {
     return IntRect();
   }
 
   Rect neededRect = inverted.TransformBounds(Rect(aRect));
   neededRect.RoundOut();
   return GetInputRectInRect(IN_TRANSFORM_IN, RoundedToInt(neededRect));
@@ -988,16 +1008,20 @@ FilterNodeTransformSoftware::RequestFrom
 {
   RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect));
 }
 
 IntRect
 FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect)
 {
   IntRect srcRect = SourceRectForOutputRect(aRect);
+  if (srcRect.IsEmpty()) {
+    return IntRect();
+  }
+
   Rect outRect = mMatrix.TransformBounds(Rect(srcRect));
   outRect.RoundOut();
   return RoundedToInt(outRect).Intersect(aRect);
 }
 
 FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
  : mOperator(MORPHOLOGY_OPERATOR_ERODE)
 {}
@@ -2239,30 +2263,38 @@ void
 FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
 {
   RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect));
 }
 
 IntRect
 FilterNodeConvolveMatrixSoftware::InflatedSourceRect(const IntRect &aDestRect)
 {
+  if (aDestRect.IsEmpty()) {
+    return IntRect();
+  }
+
   IntMargin margin;
   margin.left = ceil(mTarget.x * mKernelUnitLength.width);
   margin.top = ceil(mTarget.y * mKernelUnitLength.height);
   margin.right = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
   margin.bottom = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
 
   IntRect srcRect = aDestRect;
   srcRect.Inflate(margin);
   return srcRect;
 }
 
 IntRect
 FilterNodeConvolveMatrixSoftware::InflatedDestRect(const IntRect &aSourceRect)
 {
+  if (aSourceRect.IsEmpty()) {
+    return IntRect();
+  }
+
   IntMargin margin;
   margin.left = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
   margin.top = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
   margin.right = ceil(mTarget.x * mKernelUnitLength.width);
   margin.bottom = ceil(mTarget.y * mKernelUnitLength.height);
 
   IntRect destRect = aSourceRect;
   destRect.Inflate(margin);
@@ -2508,21 +2540,35 @@ FilterNodeArithmeticCombineSoftware::Set
 
 TemporaryRef<DataSourceSurface>
 FilterNodeArithmeticCombineSoftware::Render(const IntRect& aRect)
 {
   RefPtr<DataSourceSurface> input1 =
     GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS);
   RefPtr<DataSourceSurface> input2 =
     GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS);
-  if (!input1 || !input2) {
+  if (!input1 && !input2) {
     return nullptr;
   }
 
-  return FilterProcessing::ApplyArithmeticCombine(input1, input2, mK1, mK2, mK3, mK4);
+  // If one input is null, treat it as transparent by adjusting the factors.
+  Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4;
+  if (!input1) {
+    k1 = 0.0f;
+    k2 = 0.0f;
+    input1 = input2;
+  }
+
+  if (!input2) {
+    k1 = 0.0f;
+    k3 = 0.0f;
+    input2 = input1;
+  }
+
+  return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3, k4);
 }
 
 void
 FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(const IntRect &aRect)
 {
   RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect);
   RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect);
 }
@@ -2554,27 +2600,51 @@ FilterNodeCompositeSoftware::SetAttribut
 
 TemporaryRef<DataSourceSurface>
 FilterNodeCompositeSoftware::Render(const IntRect& aRect)
 {
   RefPtr<DataSourceSurface> start =
     GetInputDataSourceSurface(IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS);
   RefPtr<DataSourceSurface> dest =
     Factory::CreateDataSourceSurface(aRect.Size(), FORMAT_B8G8R8A8);
-  if (!start || !dest) {
+  if (!dest) {
     return nullptr;
   }
-  CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint());
+
+  if (start) {
+    CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint());
+  } else {
+    ClearDataSourceSurface(dest);
+  }
+
   for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) {
     RefPtr<DataSourceSurface> input =
       GetInputDataSourceSurface(IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS);
-    if (!input) {
-      return nullptr;
+    if (input) {
+      FilterProcessing::ApplyComposition(input, dest, mOperator);
+    } else {
+      // We need to treat input as transparent. Depending on the composite
+      // operator, different things happen to dest.
+      switch (mOperator) {
+        case COMPOSITE_OPERATOR_OVER:
+        case COMPOSITE_OPERATOR_ATOP:
+        case COMPOSITE_OPERATOR_XOR:
+          // dest is unchanged.
+          break;
+        case COMPOSITE_OPERATOR_OUT:
+          // dest is now transparent, but it can become non-transparent again
+          // when compositing additional inputs.
+          ClearDataSourceSurface(dest);
+          break;
+        case COMPOSITE_OPERATOR_IN:
+          // Transparency always wins. We're completely transparent now and
+          // no additional input can get rid of that transparency.
+          return nullptr;
+      }
     }
-    FilterProcessing::ApplyComposition(input, dest, mOperator);
   }
   return dest;
 }
 
 void
 FilterNodeCompositeSoftware::RequestFromInputsForRect(const IntRect &aRect)
 {
   for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
@@ -3117,23 +3187,27 @@ FilterNodeLightingSoftware<LightType, Li
   IntRect srcRect = aRect;
   IntSize size = aRect.Size();
   srcRect.Inflate(ceil(float(aKernelUnitLengthX)),
                   ceil(float(aKernelUnitLengthY)));
   RefPtr<DataSourceSurface> input =
     GetInputDataSourceSurface(IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8,
                               EDGE_MODE_DUPLICATE);
 
+  if (!input) {
+    return nullptr;
+  }
+
   if (input->GetFormat() != FORMAT_A8) {
     input = FilterProcessing::ExtractAlpha(input);
   }
 
   RefPtr<DataSourceSurface> target =
     Factory::CreateDataSourceSurface(size, FORMAT_B8G8R8A8);
-  if (!input || !target) {
+  if (!target) {
     return nullptr;
   }
 
   uint8_t* sourceData = input->GetData();
   int32_t sourceStride = input->Stride();
   uint8_t* targetData = target->GetData();
   int32_t targetStride = target->Stride();