Bug 756767 - Part 2: Deal with CreateSourceSurfaceFromData failing. r=jrmuizel
authorBas Schouten <bschouten@mozilla.com>
Mon, 21 May 2012 17:27:32 +0200
changeset 99077 bbb12d0bcf4966ee6dfbcc9d42a69a83256bfc12
parent 99076 63a4c2f2a0b9611e8f921330936adf63300ca70c
child 99078 8574f784774aa94db31eb10cb0e7b0ddd1173da1
push id173
push userlsblakk@mozilla.com
push dateFri, 24 Aug 2012 15:39:16 +0000
treeherdermozilla-release@bcc45eb1fb41 [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);