Bug 756767 - Part 2: Deal with CreateSourceSurfaceFromData failing. r=jrmuizel
authorBas Schouten <bschouten@mozilla.com>
Mon, 21 May 2012 17:27:32 +0200
changeset 94496 c520957cc03f16b99aef7843fc3a574a9e9131be
parent 94495 713ed280a80d891ce73827ec05ba7d7d66b2b337
child 94497 a693c64dc64e856839039b1d5614bf3e33ef2884
push id9638
push userbschouten@mozilla.com
push dateMon, 21 May 2012 15:28:58 +0000
treeherdermozilla-inbound@a693c64dc64e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs756767
milestone15.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 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);