Bug 756767 - Part 2: Deal with CreateSourceSurfaceFromData failing. r=jrmuizel
authorBas Schouten <bschouten@mozilla.com>
Mon, 21 May 2012 17:27:32 +0200
changeset 96836 bbb12d0bcf4966ee6dfbcc9d42a69a83256bfc12
parent 96835 63a4c2f2a0b9611e8f921330936adf63300ca70c
child 96837 8574f784774aa94db31eb10cb0e7b0ddd1173da1
push id1439
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 20:19:22 +0000
treeherdermozilla-aurora@ea74834dccd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs756767
milestone15.0a1
Bug 756767 - Part 2: Deal with CreateSourceSurfaceFromData failing. r=jrmuizel
gfx/thebes/gfxContext.cpp
gfx/thebes/gfxContext.h
gfx/thebes/gfxPattern.cpp
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -527,16 +527,17 @@ gfxContext::DrawSurface(gfxASurface *sur
     cairo_new_path(mCairo);
 
     // pixel-snap this
     Rectangle(gfxRect(gfxPoint(0.0, 0.0), size), true);
 
     cairo_fill(mCairo);
     cairo_restore(mCairo);
   } else {
+    // Lifetime needs to be limited here since we may wrap surface's data.
     RefPtr<SourceSurface> surf =
       gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
 
     Rect rect(0, 0, Float(size.width), Float(size.height));
     rect.Intersect(Rect(0, 0, Float(surf->GetSize().width), Float(surf->GetSize().height)));
 
     // XXX - Should fix pixel snapping.
     mDT->DrawSurface(surf, rect, rect);
@@ -1288,16 +1289,17 @@ gfxContext::SetColor(const gfxRGBA& c)
         // Use the original alpha to avoid unnecessary float->byte->float
         // conversion errors
         cairo_set_source_rgba(mCairo, cms.r, cms.g, cms.b, c.a);
     }
     else
         cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a);
   } else {
     CurrentState().pattern = NULL;
+    CurrentState().sourceSurfCairo = NULL;
     CurrentState().sourceSurface = NULL;
 
     if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
 
         gfxRGBA cms;
         gfxPlatform::TransformPixel(c, cms, gfxPlatform::GetCMSRGBTransform());
 
         // Use the original alpha to avoid unnecessary float->byte->float
@@ -1311,16 +1313,17 @@ gfxContext::SetColor(const gfxRGBA& c)
 
 void
 gfxContext::SetDeviceColor(const gfxRGBA& c)
 {
   if (mCairo) {
     cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a);
   } else {
     CurrentState().pattern = NULL;
+    CurrentState().sourceSurfCairo = NULL;
     CurrentState().sourceSurface = NULL;
     CurrentState().color = ToColor(c);
   }
 }
 
 bool
 gfxContext::GetDeviceColor(gfxRGBA& c)
 {
@@ -1349,27 +1352,31 @@ gfxContext::SetSource(gfxASurface *surfa
 {
   if (mCairo) {
     NS_ASSERTION(surface->GetAllowUseAsSource(), "Surface not allowed to be used as source!");
     cairo_set_source_surface(mCairo, surface->CairoSurface(), offset.x, offset.y);
   } else {
     CurrentState().surfTransform = Matrix(1.0f, 0, 0, 1.0f, Float(offset.x), Float(offset.y));
     CurrentState().pattern = NULL;
     CurrentState().patternTransformChanged = false;
+    // Keep the underlying cairo surface around while we keep the
+    // sourceSurface.
+    CurrentState().sourceSurfCairo = surface;
     CurrentState().sourceSurface =
       gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
   }
 }
 
 void
 gfxContext::SetPattern(gfxPattern *pattern)
 {
   if (mCairo) {
     cairo_set_source(mCairo, pattern->CairoPattern());
   } else {
+    CurrentState().sourceSurfCairo = NULL;
     CurrentState().sourceSurface = NULL;
     CurrentState().patternTransformChanged = false;
     CurrentState().pattern = pattern;
   }
 }
 
 already_AddRefed<gfxPattern>
 gfxContext::GetPattern()
@@ -1416,16 +1423,17 @@ gfxContext::Mask(gfxPattern *pattern)
 
 void
 gfxContext::Mask(gfxASurface *surface, const gfxPoint& offset)
 {
   SAMPLE_LABEL("gfxContext", "Mask");
   if (mCairo) {
     cairo_mask_surface(mCairo, surface->CairoSurface(), offset.x, offset.y);
   } else {
+    // Lifetime needs to be limited here as we may simply wrap surface's data.
     RefPtr<SourceSurface> sourceSurf =
       gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
 
     gfxPoint pt = surface->GetDeviceOffset();
     mDT->Mask(GeneralPattern(this), 
               SurfacePattern(sourceSurf, EXTEND_CLAMP,
                              Matrix(1.0f, 0, 0, 1.0f, Float(offset.x - pt.x), Float(offset.y - pt.y))),
                              DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode));
@@ -1577,16 +1585,17 @@ gfxContext::PopGroup()
 void
 gfxContext::PopGroupToSource()
 {
   if (mCairo) {
     cairo_pop_group_to_source(mCairo);
   } else {
     RefPtr<SourceSurface> src = mDT->Snapshot();
     Restore();
+    CurrentState().sourceSurfCairo = NULL;
     CurrentState().sourceSurface = src;
     CurrentState().pattern = NULL;
     CurrentState().patternTransformChanged = false;
 
     Matrix mat = mDT->GetTransform();
     mat.Invert();
     CurrentState().surfTransform = mat;
   }
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -708,16 +708,17 @@ private:
       , aaMode(mozilla::gfx::AA_SUBPIXEL)
       , patternTransformChanged(false)
     {}
 
     mozilla::gfx::CompositionOp op;
     bool opIsClear;
     Color color;
     nsRefPtr<gfxPattern> pattern;
+    nsRefPtr<gfxASurface> sourceSurfCairo;
     mozilla::RefPtr<SourceSurface> sourceSurface;
     Matrix surfTransform;
     Matrix transform;
     struct PushedClip {
       mozilla::RefPtr<Path> path;
       Rect rect;
       Matrix transform;
     };
--- a/gfx/thebes/gfxPattern.cpp
+++ b/gfx/thebes/gfxPattern.cpp
@@ -146,16 +146,18 @@ gfxPattern::GetPattern(DrawTarget *aTarg
       cairo_pattern_get_matrix(mPattern, &mat);
       gfxMatrix matrix(*reinterpret_cast<gfxMatrix*>(&mat));
 
       cairo_surface_t *surf = NULL;
       cairo_pattern_get_surface(mPattern, &surf);
 
       if (!mSourceSurface) {
         nsRefPtr<gfxASurface> gfxSurf = gfxASurface::Wrap(surf);
+        // The underlying surface here will be kept around by the gfxPattern.
+        // This function is intended to be used right away.
         mSourceSurface =
           gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTarget, gfxSurf);
       }
 
       if (mSourceSurface) {
         Matrix newMat = ToMatrix(matrix);
 
         AdjustTransformForPattern(newMat, aTarget->GetTransform(), aPatternTransform);
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef MOZ_LOGGING
 #define FORCE_PR_LOG /* Allow logging in the release build */
 #endif
 #include "prlog.h"
@@ -467,16 +467,22 @@ gfxPlatform::GetSourceSurfaceForSurface(
     }
     srcBuffer = aTarget->CreateSourceSurfaceFromNativeSurface(surf);
   }
 #endif
 
   if (!srcBuffer) {
     nsRefPtr<gfxImageSurface> imgSurface = aSurface->GetAsImageSurface();
 
+    bool isWin32ImageSurf = false;
+
+    if (imgSurface && aSurface->GetType() != gfxASurface::SurfaceTypeWin32) {
+      isWin32ImageSurf = true;
+    }
+
     if (!imgSurface) {
       imgSurface = new gfxImageSurface(aSurface->GetSize(), gfxASurface::FormatFromContent(aSurface->GetContentType()));
       nsRefPtr<gfxContext> ctx = new gfxContext(imgSurface);
       ctx->SetSource(aSurface);
       ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
       ctx->Paint();
     }
 
@@ -493,27 +499,46 @@ gfxPlatform::GetSourceSurfaceForSurface(
         break;
       case gfxASurface::ImageFormatRGB16_565:
         format = FORMAT_R5G6B5;
         break;
       default:
         NS_RUNTIMEABORT("Invalid surface format!");
     }
 
+    IntSize size = IntSize(imgSurface->GetSize().width, imgSurface->GetSize().height);
     srcBuffer = aTarget->CreateSourceSurfaceFromData(imgSurface->Data(),
-                                                     IntSize(imgSurface->GetSize().width, imgSurface->GetSize().height),
+                                                     size,
                                                      imgSurface->Stride(),
                                                      format);
 
+    if (!srcBuffer) {
+      // We need to check if our gfxASurface will keep the underlying data
+      // alive! This is true if gfxASurface actually -is- an ImageSurface or
+      // if it is a gfxWindowsSurface which supportes GetAsImageSurface.
+      if (imgSurface != aSurface && !isWin32ImageSurf) {
+        // This shouldn't happen for now, it can be easily supported by making
+        // a copy. For now let's just abort.
+        NS_RUNTIMEABORT("Attempt to create unsupported SourceSurface from"
+            "non-image surface.");
+        return nsnull;
+      }
+
+      srcBuffer = Factory::CreateWrappingDataSourceSurface(imgSurface->Data(),
+                                                           imgSurface->Stride(),
+                                                           size, format);
+
+    }
+
     cairo_surface_t *nullSurf =
 	cairo_null_surface_create(CAIRO_CONTENT_COLOR_ALPHA);
     cairo_surface_set_user_data(nullSurf,
-				&kSourceSurface,
-				imgSurface,
-				NULL);
+                                &kSourceSurface,
+                                imgSurface,
+                                NULL);
     cairo_surface_attach_snapshot(imgSurface->CairoSurface(), nullSurf, SourceSnapshotDetached);
     cairo_surface_destroy(nullSurf);
   }
 
   srcBuffer->AddRef();
   aSurface->SetData(&kSourceSurface, srcBuffer, SourceBufferDestroy);
 
   return srcBuffer;
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -158,16 +158,21 @@ public:
 
 
     virtual already_AddRefed<gfxASurface> OptimizeImage(gfxImageSurface *aSurface,
                                                         gfxASurface::gfxImageFormat format);
 
     virtual mozilla::RefPtr<mozilla::gfx::DrawTarget>
       CreateDrawTargetForSurface(gfxASurface *aSurface);
 
+    /*
+     * Creates a SourceSurface for a gfxASurface. This surface should -not- be
+     * held around by the user after the underlying gfxASurface has been
+     * destroyed as a copy of the data is not guaranteed.
+     */
     virtual mozilla::RefPtr<mozilla::gfx::SourceSurface>
       GetSourceSurfaceForSurface(mozilla::gfx::DrawTarget *aTarget, gfxASurface *aSurface);
 
     virtual mozilla::RefPtr<mozilla::gfx::ScaledFont>
       GetScaledFontForFont(gfxFont *aFont);
 
     virtual already_AddRefed<gfxASurface>
       GetThebesSurfaceForDrawTarget(mozilla::gfx::DrawTarget *aTarget);