Bug 715652 - Jeff's review requested changes which were easier to implement on top of path support. r=jrmuizel
authorJoe Drew <joe@drew.ca>
Mon, 09 Jan 2012 17:19:11 -0500
changeset 84196 7349c6b4ac7f856f12b606894872c526e8ffae28
parent 84195 14930a83054b9cfda27d7e60f54680215fd8ac63
child 84197 603df6854a5296c095df575bafd5f4353770d1ee
push id4781
push userjdrew@mozilla.com
push dateWed, 11 Jan 2012 03:41:41 +0000
treeherdermozilla-inbound@603df6854a52 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs715652
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 715652 - Jeff's review requested changes which were easier to implement on top of path support. r=jrmuizel
gfx/2d/DrawTargetCairo.cpp
gfx/2d/SourceSurfaceCairo.cpp
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -121,103 +121,133 @@ GetCairoSurfaceSize(cairo_surface_t* sur
     }
 #endif
 
     default:
       return false;
   }
 }
 
+static bool
+PatternIsCompatible(const Pattern& aPattern)
+{
+  switch (aPattern.GetType())
+  {
+    case PATTERN_LINEAR_GRADIENT:
+    {
+      const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern);
+      return pattern.mStops->GetBackendType() == BACKEND_CAIRO;
+    }
+    case PATTERN_RADIAL_GRADIENT:
+    {
+      const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
+      return pattern.mStops->GetBackendType() == BACKEND_CAIRO;
+    }
+  }
+
+  return true;
+}
+
+// Never returns NULL. As such, you must always pass in Cairo-compatible
+// patterns, most notably gradients with a GradientStopCairo.
+// The pattern returned must have cairo_pattern_destroy() called on it by the
+// caller.
+// As the cairo_pattern_t returned may depend on the Pattern passed in, the
+// lifetime of the cairo_pattern_t returned must not exceed the lifetime of the
+// Pattern passed in.
 static cairo_pattern_t*
 GfxPatternToCairoPattern(const Pattern& aPattern, Float aAlpha)
 {
-  cairo_pattern_t* pat = NULL;
+  cairo_pattern_t* pat;
 
   switch (aPattern.GetType())
   {
     case PATTERN_COLOR:
     {
       Color color = static_cast<const ColorPattern&>(aPattern).mColor;
       pat = cairo_pattern_create_rgba(color.r, color.g, color.b, color.a * aAlpha);
       break;
     }
 
     case PATTERN_SURFACE:
     {
       const SurfacePattern& pattern = static_cast<const SurfacePattern&>(aPattern);
-      cairo_surface_t* surf = NULL;
+      cairo_surface_t* surf;
 
+      // After this block, |surf| always has an extra cairo reference to be
+      // destroyed. This makes creating new surfaces or reusing old ones more
+      // uniform.
       if (pattern.mSurface->GetType() == SURFACE_CAIRO) {
-        const SourceSurfaceCairo* sourcesurf = static_cast<const SourceSurfaceCairo*>(pattern.mSurface.get());
-        surf = sourcesurf->GetSurface();
+        const SourceSurfaceCairo* source = static_cast<const SourceSurfaceCairo*>(pattern.mSurface.get());
+        surf = source->GetSurface();
         cairo_surface_reference(surf);
       } else if (pattern.mSurface->GetType() == SURFACE_CAIRO_IMAGE) {
-        const DataSourceSurfaceCairo* sourcesurf =
+        const DataSourceSurfaceCairo* source =
           static_cast<const DataSourceSurfaceCairo*>(pattern.mSurface.get());
-        surf = sourcesurf->GetSurface();
+        surf = source->GetSurface();
         cairo_surface_reference(surf);
       } else {
-        RefPtr<DataSourceSurface> sourcesurf = pattern.mSurface->GetDataSurface();
-        surf = cairo_image_surface_create_for_data(sourcesurf->GetData(),
-                                                   GfxFormatToCairoFormat(sourcesurf->GetFormat()),
-                                                   sourcesurf->GetSize().width,
-                                                   sourcesurf->GetSize().height,
-                                                   sourcesurf->Stride());
+        RefPtr<DataSourceSurface> source = pattern.mSurface->GetDataSurface();
+        surf = cairo_image_surface_create_for_data(source->GetData(),
+                                                   GfxFormatToCairoFormat(source->GetFormat()),
+                                                   source->GetSize().width,
+                                                   source->GetSize().height,
+                                                   source->Stride());
       }
 
       pat = cairo_pattern_create_for_surface(surf);
       cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(pattern.mFilter));
       cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(pattern.mExtendMode));
 
       cairo_surface_destroy(surf);
 
       break;
     }
     case PATTERN_LINEAR_GRADIENT:
     {
       const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern);
-      RefPtr<GradientStops> stops = pattern.mStops;
-      if (stops->GetBackendType() == BACKEND_CAIRO) {
-        pat = cairo_pattern_create_linear(pattern.mBegin.x, pattern.mBegin.y,
-                                          pattern.mEnd.x, pattern.mEnd.y);
+
+      pat = cairo_pattern_create_linear(pattern.mBegin.x, pattern.mBegin.y,
+                                        pattern.mEnd.x, pattern.mEnd.y);
 
-        const std::vector<GradientStop>& stops =
-          static_cast<GradientStopsCairo*>(pattern.mStops.get())->GetStops();
-        for (std::vector<GradientStop>::const_iterator i = stops.begin();
-             i != stops.end();
-             ++i) {
-          cairo_pattern_add_color_stop_rgba(pat, i->offset, i->color.r,
-                                            i->color.g, i->color.b,
-                                            i->color.a);
-        }
+      MOZ_ASSERT(pattern.mStops->GetBackendType() == BACKEND_CAIRO);
+      const std::vector<GradientStop>& stops =
+        static_cast<GradientStopsCairo*>(pattern.mStops.get())->GetStops();
+      for (size_t i = 0; i < stops.size(); ++i) {
+        const GradientStop& stop = stops[i];
+        cairo_pattern_add_color_stop_rgba(pat, stop.offset, stop.color.r,
+                                          stop.color.g, stop.color.b,
+                                          stop.color.a);
       }
 
       break;
     }
     case PATTERN_RADIAL_GRADIENT:
     {
       const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
-      RefPtr<GradientStops> stops = pattern.mStops;
-      if (stops->GetBackendType() == BACKEND_CAIRO) {
-        pat = cairo_pattern_create_radial(pattern.mCenter1.x, pattern.mCenter1.y, pattern.mRadius1,
-                                          pattern.mCenter2.x, pattern.mCenter2.y, pattern.mRadius2);
+
+      pat = cairo_pattern_create_radial(pattern.mCenter1.x, pattern.mCenter1.y, pattern.mRadius1,
+                                        pattern.mCenter2.x, pattern.mCenter2.y, pattern.mRadius2);
 
-        const std::vector<GradientStop>& stops =
-          static_cast<GradientStopsCairo*>(pattern.mStops.get())->GetStops();
-        for (std::vector<GradientStop>::const_iterator i = stops.begin();
-             i != stops.end();
-             ++i) {
-          cairo_pattern_add_color_stop_rgba(pat, i->offset, i->color.r,
-                                            i->color.g, i->color.b,
-                                            i->color.a);
-        }
+      const std::vector<GradientStop>& stops =
+        static_cast<GradientStopsCairo*>(pattern.mStops.get())->GetStops();
+      for (size_t i = 0; i < stops.size(); ++i) {
+        const GradientStop& stop = stops[i];
+        cairo_pattern_add_color_stop_rgba(pat, stop.offset, stop.color.r,
+                                          stop.color.g, stop.color.b,
+                                          stop.color.a);
       }
 
       break;
     }
+    default:
+    {
+      // We should support all pattern types!
+      MOZ_ASSERT(false);
+    }
   }
 
   return pat;
 }
 
 static bool
 NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions)
 {
@@ -334,20 +364,20 @@ DrawTargetCairo::DrawSurfaceWithShadow(S
                                        CompositionOp aOperator)
 {
   WillChange();
 
   if (aSurface->GetType() != SURFACE_CAIRO) {
     return;
   }
 
-  SourceSurfaceCairo* sourcesurf = static_cast<SourceSurfaceCairo*>(aSurface);
+  SourceSurfaceCairo* source = static_cast<SourceSurfaceCairo*>(aSurface);
 
-  Float width = aSurface->GetSize().width,
-        height = aSurface->GetSize().height;
+  Float width = aSurface->GetSize().width;
+  Float height = aSurface->GetSize().height;
   Rect extents(0, 0, width, height);
 
   AlphaBoxBlur blur(extents, IntSize(0, 0),
                     AlphaBoxBlur::CalculateBlurRadius(Point(aSigma, aSigma)),
                     NULL, NULL);
   if (!blur.GetData()) {
     return;
   }
@@ -356,17 +386,17 @@ DrawTargetCairo::DrawSurfaceWithShadow(S
 
   cairo_surface_t* blursurf = cairo_image_surface_create_for_data(blur.GetData(),
                                                                   CAIRO_FORMAT_A8,
                                                                   blursize.width,
                                                                   blursize.height,
                                                                   blur.GetStride());
 
   // Draw the source surface into the surface we're going to blur.
-  cairo_surface_t* surf = sourcesurf->GetSurface();
+  cairo_surface_t* surf = source->GetSurface();
   cairo_pattern_t* pat = cairo_pattern_create_for_surface(surf);
 
   cairo_t* ctx = cairo_create(blursurf);
 
   cairo_set_source(ctx, pat);
 
   IntRect blurrect = blur.GetRect();
   cairo_new_path(ctx);
@@ -408,16 +438,20 @@ DrawTargetCairo::DrawSurfaceWithShadow(S
 }
 
 void
 DrawTargetCairo::DrawPattern(const Pattern& aPattern,
                              const StrokeOptions& aStrokeOptions,
                              const DrawOptions& aOptions,
                              DrawPatternType aDrawType)
 {
+  if (!PatternIsCompatible(aPattern)) {
+    return;
+  }
+
   cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha);
   cairo_set_source(mContext, pat);
 
   if (NeedIntermediateSurface(aPattern, aOptions)) {
     cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA);
 
     // Don't want operators to be applied twice
     cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
@@ -626,19 +660,19 @@ DrawTargetCairo::OptimizeSourceSurface(S
 
 TemporaryRef<SourceSurface>
 DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
 {
   if (aSurface.mType == NATIVE_SURFACE_CAIRO_SURFACE) {
     IntSize size;
     cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface);
     if (GetCairoSurfaceSize(surf, size)) {
-      RefPtr<SourceSurfaceCairo> sourcesurf =
+      RefPtr<SourceSurfaceCairo> source =
         new SourceSurfaceCairo(surf, size, aSurface.mFormat);
-      return sourcesurf;
+      return source;
     }
   }
 
   return NULL;
 }
 
 TemporaryRef<DrawTarget>
 DrawTargetCairo::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
--- a/gfx/2d/SourceSurfaceCairo.cpp
+++ b/gfx/2d/SourceSurfaceCairo.cpp
@@ -129,18 +129,17 @@ SourceSurfaceCairo::GetDataSurface()
   if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_IMAGE) {
     dataSurf = new DataSourceSurfaceCairo(mSurface);
   } else {
     cairo_surface_t* imageSurf = cairo_image_surface_create(GfxFormatToCairoFormat(mFormat),
                                                             mSize.width, mSize.height);
 
     // Fill the new image surface with the contents of our surface.
     cairo_t* ctx = cairo_create(imageSurf);
-    cairo_pattern_t* pat = cairo_pattern_create_for_surface(mSurface);
-    cairo_set_source(ctx, pat);
+    cairo_set_source_surface(ctx, mSurface, 0, 0);
     cairo_paint(ctx);
     cairo_destroy(ctx);
 
     dataSurf = new DataSourceSurfaceCairo(imageSurf);
     cairo_surface_destroy(imageSurf);
   }
 
   return dataSurf;