image/src/imgFrame.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "imgFrame.h"
     8 #include "DiscardTracker.h"
    10 #include "prenv.h"
    12 #include "gfx2DGlue.h"
    13 #include "gfxPlatform.h"
    14 #include "gfxUtils.h"
    15 #include "gfxAlphaRecovery.h"
    17 static bool gDisableOptimize = false;
    19 #include "cairo.h"
    20 #include "GeckoProfiler.h"
    21 #include "mozilla/Likely.h"
    22 #include "mozilla/MemoryReporting.h"
    23 #include "nsMargin.h"
    24 #include "mozilla/CheckedInt.h"
    26 #if defined(XP_WIN)
    28 #include "gfxWindowsPlatform.h"
    30 /* Whether to use the windows surface; only for desktop win32 */
    31 #define USE_WIN_SURFACE 1
    33 #endif
    35 using namespace mozilla;
    36 using namespace mozilla::gfx;
    37 using namespace mozilla::image;
    39 static cairo_user_data_key_t kVolatileBuffer;
    41 static void
    42 VolatileBufferRelease(void *vbuf)
    43 {
    44   delete static_cast<VolatileBufferPtr<unsigned char>*>(vbuf);
    45 }
    47 gfxImageSurface *
    48 LockedImageSurface::CreateSurface(VolatileBuffer *vbuf,
    49                                   const gfxIntSize& size,
    50                                   gfxImageFormat format)
    51 {
    52   VolatileBufferPtr<unsigned char> *vbufptr =
    53     new VolatileBufferPtr<unsigned char>(vbuf);
    54   MOZ_ASSERT(!vbufptr->WasBufferPurged(), "Expected image data!");
    56   long stride = gfxImageSurface::ComputeStride(size, format);
    57   gfxImageSurface *img = new gfxImageSurface(*vbufptr, size, stride, format);
    58   if (!img || img->CairoStatus()) {
    59     delete img;
    60     delete vbufptr;
    61     return nullptr;
    62   }
    64   img->SetData(&kVolatileBuffer, vbufptr, VolatileBufferRelease);
    65   return img;
    66 }
    68 TemporaryRef<VolatileBuffer>
    69 LockedImageSurface::AllocateBuffer(const gfxIntSize& size,
    70                                    gfxImageFormat format)
    71 {
    72   long stride = gfxImageSurface::ComputeStride(size, format);
    73   RefPtr<VolatileBuffer> buf = new VolatileBuffer();
    74   if (buf->Init(stride * size.height,
    75                 1 << gfxAlphaRecovery::GoodAlignmentLog2()))
    76     return buf;
    78   return nullptr;
    79 }
    81 // Returns true if an image of aWidth x aHeight is allowed and legal.
    82 static bool AllowedImageSize(int32_t aWidth, int32_t aHeight)
    83 {
    84   // reject over-wide or over-tall images
    85   const int32_t k64KLimit = 0x0000FFFF;
    86   if (MOZ_UNLIKELY(aWidth > k64KLimit || aHeight > k64KLimit )) {
    87     NS_WARNING("image too big");
    88     return false;
    89   }
    91   // protect against invalid sizes
    92   if (MOZ_UNLIKELY(aHeight <= 0 || aWidth <= 0)) {
    93     return false;
    94   }
    96   // check to make sure we don't overflow a 32-bit
    97   CheckedInt32 requiredBytes = CheckedInt32(aWidth) * CheckedInt32(aHeight) * 4;
    98   if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
    99     NS_WARNING("width or height too large");
   100     return false;
   101   }
   102 #if defined(XP_MACOSX)
   103   // CoreGraphics is limited to images < 32K in *height*, so clamp all surfaces on the Mac to that height
   104   if (MOZ_UNLIKELY(aHeight > SHRT_MAX)) {
   105     NS_WARNING("image too big");
   106     return false;
   107   }
   108 #endif
   109   return true;
   110 }
   112 // Returns whether we should, at this time, use image surfaces instead of
   113 // optimized platform-specific surfaces.
   114 static bool ShouldUseImageSurfaces()
   115 {
   116 #if defined(USE_WIN_SURFACE)
   117   static const DWORD kGDIObjectsHighWaterMark = 7000;
   119   if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
   120       gfxWindowsPlatform::RENDER_DIRECT2D) {
   121     return true;
   122   }
   124   // at 7000 GDI objects, stop allocating normal images to make sure
   125   // we never hit the 10k hard limit.
   126   // GetCurrentProcess() just returns (HANDLE)-1, it's inlined afaik
   127   DWORD count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
   128   if (count == 0 ||
   129       count > kGDIObjectsHighWaterMark)
   130   {
   131     // either something's broken (count == 0),
   132     // or we hit our high water mark; disable
   133     // image allocations for a bit.
   134     return true;
   135   }
   136 #endif
   138   return false;
   139 }
   141 imgFrame::imgFrame() :
   142   mDecoded(0, 0, 0, 0),
   143   mDirtyMutex("imgFrame::mDirty"),
   144   mPalettedImageData(nullptr),
   145   mSinglePixelColor(0),
   146   mTimeout(100),
   147   mDisposalMethod(0), /* imgIContainer::kDisposeNotSpecified */
   148   mLockCount(0),
   149   mBlendMethod(1), /* imgIContainer::kBlendOver */
   150   mSinglePixel(false),
   151   mFormatChanged(false),
   152   mCompositingFailed(false),
   153   mNonPremult(false),
   154   mDiscardable(false),
   155   mInformedDiscardTracker(false),
   156   mDirty(false)
   157 {
   158   static bool hasCheckedOptimize = false;
   159   if (!hasCheckedOptimize) {
   160     if (PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")) {
   161       gDisableOptimize = true;
   162     }
   163     hasCheckedOptimize = true;
   164   }
   165 }
   167 imgFrame::~imgFrame()
   168 {
   169   moz_free(mPalettedImageData);
   170   mPalettedImageData = nullptr;
   172   if (mInformedDiscardTracker) {
   173     DiscardTracker::InformDeallocation(4 * mSize.height * mSize.width);
   174   }
   175 }
   177 nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
   178                         gfxImageFormat aFormat, uint8_t aPaletteDepth /* = 0 */)
   179 {
   180   // assert for properties that should be verified by decoders, warn for properties related to bad content
   181   if (!AllowedImageSize(aWidth, aHeight)) {
   182     NS_WARNING("Should have legal image size");
   183     return NS_ERROR_FAILURE;
   184   }
   186   mOffset.MoveTo(aX, aY);
   187   mSize.SizeTo(aWidth, aHeight);
   189   mFormat = aFormat;
   190   mPaletteDepth = aPaletteDepth;
   192   if (aPaletteDepth != 0) {
   193     // We're creating for a paletted image.
   194     if (aPaletteDepth > 8) {
   195       NS_WARNING("Should have legal palette depth");
   196       NS_ERROR("This Depth is not supported");
   197       return NS_ERROR_FAILURE;
   198     }
   200     // Use the fallible allocator here
   201     mPalettedImageData = (uint8_t*)moz_malloc(PaletteDataLength() + GetImageDataLength());
   202     if (!mPalettedImageData)
   203       NS_WARNING("moz_malloc for paletted image data should succeed");
   204     NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
   205   } else {
   206     // Inform the discard tracker that we are going to allocate some memory.
   207     if (!DiscardTracker::TryAllocation(4 * mSize.width * mSize.height)) {
   208       NS_WARNING("Exceed the hard limit of decode image size");
   209       return NS_ERROR_OUT_OF_MEMORY;
   210     }
   211     // For Windows, we must create the device surface first (if we're
   212     // going to) so that the image surface can wrap it.  Can't be done
   213     // the other way around.
   214 #ifdef USE_WIN_SURFACE
   215     if (!ShouldUseImageSurfaces()) {
   216       mWinSurface = new gfxWindowsSurface(gfxIntSize(mSize.width, mSize.height), mFormat);
   217       if (mWinSurface && mWinSurface->CairoStatus() == 0) {
   218         // no error
   219         mImageSurface = mWinSurface->GetAsImageSurface();
   220       } else {
   221         mWinSurface = nullptr;
   222       }
   223     }
   224 #endif
   226     // For other platforms, space for the image surface is first allocated in
   227     // a volatile buffer and then wrapped by a LockedImageSurface.
   228     // This branch is also used on Windows if we're not using device surfaces
   229     // or if we couldn't create one.
   230     if (!mImageSurface) {
   231       mVBuf = LockedImageSurface::AllocateBuffer(mSize, mFormat);
   232       if (!mVBuf) {
   233         return NS_ERROR_OUT_OF_MEMORY;
   234       }
   235       if (mVBuf->OnHeap()) {
   236         long stride = gfxImageSurface::ComputeStride(mSize, mFormat);
   237         VolatileBufferPtr<uint8_t> ptr(mVBuf);
   238         memset(ptr, 0, stride * mSize.height);
   239       }
   240       mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
   241     }
   243     if (!mImageSurface || mImageSurface->CairoStatus()) {
   244       mImageSurface = nullptr;
   245       // guess
   246       if (!mImageSurface) {
   247         NS_WARNING("Allocation of gfxImageSurface should succeed");
   248       } else if (!mImageSurface->CairoStatus()) {
   249         NS_WARNING("gfxImageSurface should have good CairoStatus");
   250       }
   252       // Image surface allocation is failed, need to return
   253       // the booked buffer size.
   254       DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height);
   255       return NS_ERROR_OUT_OF_MEMORY;
   256     }
   258     mInformedDiscardTracker = true;
   260 #ifdef XP_MACOSX
   261     if (!ShouldUseImageSurfaces()) {
   262       mQuartzSurface = new gfxQuartzImageSurface(mImageSurface);
   263     }
   264 #endif
   265   }
   267   return NS_OK;
   268 }
   270 nsresult imgFrame::Optimize()
   271 {
   272   MOZ_ASSERT(NS_IsMainThread());
   274   if (gDisableOptimize)
   275     return NS_OK;
   277   if (mPalettedImageData || mOptSurface || mSinglePixel)
   278     return NS_OK;
   280   // Don't do single-color opts on non-premult data.
   281   // Cairo doesn't support non-premult single-colors.
   282   if (mNonPremult)
   283     return NS_OK;
   285   /* Figure out if the entire image is a constant color */
   287   // this should always be true
   288   if (mImageSurface->Stride() == mSize.width * 4) {
   289     uint32_t *imgData = (uint32_t*) mImageSurface->Data();
   290     uint32_t firstPixel = * (uint32_t*) imgData;
   291     uint32_t pixelCount = mSize.width * mSize.height + 1;
   293     while (--pixelCount && *imgData++ == firstPixel)
   294       ;
   296     if (pixelCount == 0) {
   297       // all pixels were the same
   298       if (mFormat == gfxImageFormat::ARGB32 ||
   299           mFormat == gfxImageFormat::RGB24)
   300       {
   301         // Should already be premult if desired.
   302         gfxRGBA::PackedColorType inputType = gfxRGBA::PACKED_XRGB;
   303         if (mFormat == gfxImageFormat::ARGB32)
   304           inputType = gfxRGBA::PACKED_ARGB_PREMULTIPLIED;
   306         mSinglePixelColor = gfxRGBA(firstPixel, inputType);
   308         mSinglePixel = true;
   310         // blow away the older surfaces (if they exist), to release their memory
   311         mVBuf = nullptr;
   312         mImageSurface = nullptr;
   313         mOptSurface = nullptr;
   314 #ifdef USE_WIN_SURFACE
   315         mWinSurface = nullptr;
   316 #endif
   317 #ifdef XP_MACOSX
   318         mQuartzSurface = nullptr;
   319 #endif
   320         mDrawSurface = nullptr;
   322         // We just dumped most of our allocated memory, so tell the discard
   323         // tracker that we're not using any at all.
   324         if (mInformedDiscardTracker) {
   325           DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height);
   326           mInformedDiscardTracker = false;
   327         }
   329         return NS_OK;
   330       }
   331     }
   333     // if it's not RGB24/ARGB32, don't optimize, but we never hit this at the moment
   334   }
   336   // if we're being forced to use image surfaces due to
   337   // resource constraints, don't try to optimize beyond same-pixel.
   338   if (ShouldUseImageSurfaces())
   339     return NS_OK;
   341   mOptSurface = nullptr;
   343 #ifdef USE_WIN_SURFACE
   344   if (mWinSurface) {
   345     if (!mFormatChanged) {
   346       // just use the DIB if the format has not changed
   347       mOptSurface = mWinSurface;
   348     }
   349   }
   350 #endif
   352 #ifdef XP_MACOSX
   353   if (mQuartzSurface) {
   354     mQuartzSurface->Flush();
   355   }
   356 #endif
   358 #ifdef ANDROID
   359   gfxImageFormat optFormat =
   360     gfxPlatform::GetPlatform()->
   361       OptimalFormatForContent(gfxASurface::ContentFromFormat(mFormat));
   363   if (optFormat == gfxImageFormat::RGB16_565) {
   364     RefPtr<VolatileBuffer> buf =
   365       LockedImageSurface::AllocateBuffer(mSize, optFormat);
   366     if (!buf)
   367       return NS_OK;
   369     nsRefPtr<gfxImageSurface> surf =
   370       LockedImageSurface::CreateSurface(buf, mSize, optFormat);
   372     gfxContext ctx(surf);
   373     ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
   374     ctx.SetSource(mImageSurface);
   375     ctx.Paint();
   377     mImageSurface = surf;
   378     mVBuf = buf;
   379     mFormat = optFormat;
   380     mDrawSurface = nullptr;
   381   }
   382 #else
   383   if (mOptSurface == nullptr)
   384     mOptSurface = gfxPlatform::GetPlatform()->OptimizeImage(mImageSurface, mFormat);
   385 #endif
   387   if (mOptSurface) {
   388     mVBuf = nullptr;
   389     mImageSurface = nullptr;
   390 #ifdef USE_WIN_SURFACE
   391     mWinSurface = nullptr;
   392 #endif
   393 #ifdef XP_MACOSX
   394     mQuartzSurface = nullptr;
   395 #endif
   396     mDrawSurface = nullptr;
   397   }
   399   return NS_OK;
   400 }
   402 static void
   403 DoSingleColorFastPath(gfxContext*    aContext,
   404                       const gfxRGBA& aSinglePixelColor,
   405                       const gfxRect& aFill)
   406 {
   407   // if a == 0, it's a noop
   408   if (aSinglePixelColor.a == 0.0)
   409     return;
   411   gfxContext::GraphicsOperator op = aContext->CurrentOperator();
   412   if (op == gfxContext::OPERATOR_OVER && aSinglePixelColor.a == 1.0) {
   413     aContext->SetOperator(gfxContext::OPERATOR_SOURCE);
   414   }
   416   aContext->SetDeviceColor(aSinglePixelColor);
   417   aContext->NewPath();
   418   aContext->Rectangle(aFill);
   419   aContext->Fill();
   420   aContext->SetOperator(op);
   421   aContext->SetDeviceColor(gfxRGBA(0,0,0,0));
   422 }
   424 imgFrame::SurfaceWithFormat
   425 imgFrame::SurfaceForDrawing(bool               aDoPadding,
   426                             bool               aDoPartialDecode,
   427                             bool               aDoTile,
   428                             const nsIntMargin& aPadding,
   429                             gfxMatrix&         aUserSpaceToImageSpace,
   430                             gfxRect&           aFill,
   431                             gfxRect&           aSubimage,
   432                             gfxRect&           aSourceRect,
   433                             gfxRect&           aImageRect,
   434                             gfxASurface*       aSurface)
   435 {
   436   IntSize size(int32_t(aImageRect.Width()), int32_t(aImageRect.Height()));
   437   if (!aDoPadding && !aDoPartialDecode) {
   438     NS_ASSERTION(!mSinglePixel, "This should already have been handled");
   439     return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, ThebesIntSize(size)), mFormat);
   440   }
   442   gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width, mDecoded.height);
   444   if (aDoTile || mSinglePixel) {
   445     // Create a temporary surface.
   446     // Give this surface an alpha channel because there are
   447     // transparent pixels in the padding or undecoded area
   448     gfxImageFormat format = gfxImageFormat::ARGB32;
   449     nsRefPtr<gfxASurface> surface =
   450       gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, gfxImageSurface::ContentFromFormat(format));
   451     if (!surface || surface->CairoStatus())
   452       return SurfaceWithFormat();
   454     // Fill 'available' with whatever we've got
   455     gfxContext tmpCtx(surface);
   456     tmpCtx.SetOperator(gfxContext::OPERATOR_SOURCE);
   457     if (mSinglePixel) {
   458       tmpCtx.SetDeviceColor(mSinglePixelColor);
   459     } else {
   460       tmpCtx.SetSource(aSurface, gfxPoint(aPadding.left, aPadding.top));
   461     }
   462     tmpCtx.Rectangle(available);
   463     tmpCtx.Fill();
   465     return SurfaceWithFormat(new gfxSurfaceDrawable(surface, ThebesIntSize(size)), format);
   466   }
   468   // Not tiling, and we have a surface, so we can account for
   469   // padding and/or a partial decode just by twiddling parameters.
   470   // First, update our user-space fill rect.
   471   aSourceRect = aSourceRect.Intersect(available);
   472   gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
   473   imageSpaceToUserSpace.Invert();
   474   aFill = imageSpaceToUserSpace.Transform(aSourceRect);
   476   aSubimage = aSubimage.Intersect(available) - gfxPoint(aPadding.left, aPadding.top);
   477   aUserSpaceToImageSpace.Multiply(gfxMatrix().Translate(-gfxPoint(aPadding.left, aPadding.top)));
   478   aSourceRect = aSourceRect - gfxPoint(aPadding.left, aPadding.top);
   479   aImageRect = gfxRect(0, 0, mSize.width, mSize.height);
   481   gfxIntSize availableSize(mDecoded.width, mDecoded.height);
   482   return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, availableSize),
   483                            mFormat);
   484 }
   486 bool imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter,
   487                     const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
   488                     const nsIntMargin &aPadding, const nsIntRect &aSubimage,
   489                     uint32_t aImageFlags)
   490 {
   491   PROFILER_LABEL("image", "imgFrame::Draw");
   492   NS_ASSERTION(!aFill.IsEmpty(), "zero dest size --- fix caller");
   493   NS_ASSERTION(!aSubimage.IsEmpty(), "zero source size --- fix caller");
   494   NS_ASSERTION(!mPalettedImageData, "Directly drawing a paletted image!");
   496   bool doPadding = aPadding != nsIntMargin(0,0,0,0);
   497   bool doPartialDecode = !ImageComplete();
   499   if (mSinglePixel && !doPadding && !doPartialDecode) {
   500     DoSingleColorFastPath(aContext, mSinglePixelColor, aFill);
   501     return true;
   502   }
   504   gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
   505   gfxRect sourceRect = userSpaceToImageSpace.TransformBounds(aFill);
   506   gfxRect imageRect(0, 0, mSize.width + aPadding.LeftRight(),
   507                     mSize.height + aPadding.TopBottom());
   508   gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
   509   gfxRect fill = aFill;
   511   NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(),
   512                "We must be allowed to sample *some* source pixels!");
   514   nsRefPtr<gfxASurface> surf = CachedThebesSurface();
   515   VolatileBufferPtr<unsigned char> ref(mVBuf);
   516   if (!mSinglePixel && !surf) {
   517     if (ref.WasBufferPurged()) {
   518       return false;
   519     }
   521     surf = mDrawSurface;
   522     if (!surf) {
   523       long stride = gfxImageSurface::ComputeStride(mSize, mFormat);
   524       nsRefPtr<gfxImageSurface> imgSurf =
   525         new gfxImageSurface(ref, mSize, stride, mFormat);
   526 #if defined(XP_MACOSX)
   527       surf = mDrawSurface = new gfxQuartzImageSurface(imgSurf);
   528 #else
   529       surf = mDrawSurface = imgSurf;
   530 #endif
   531     }
   532     if (!surf || surf->CairoStatus()) {
   533       mDrawSurface = nullptr;
   534       return true;
   535     }
   536   }
   538   bool doTile = !imageRect.Contains(sourceRect) &&
   539                 !(aImageFlags & imgIContainer::FLAG_CLAMP);
   540   SurfaceWithFormat surfaceResult =
   541     SurfaceForDrawing(doPadding, doPartialDecode, doTile, aPadding,
   542                       userSpaceToImageSpace, fill, subimage, sourceRect,
   543                       imageRect, surf);
   545   if (surfaceResult.IsValid()) {
   546     gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
   547                                userSpaceToImageSpace,
   548                                subimage, sourceRect, imageRect, fill,
   549                                surfaceResult.mFormat, aFilter, aImageFlags);
   550   }
   551   return true;
   552 }
   554 // This can be called from any thread, but not simultaneously.
   555 nsresult imgFrame::ImageUpdated(const nsIntRect &aUpdateRect)
   556 {
   557   MutexAutoLock lock(mDirtyMutex);
   559   mDecoded.UnionRect(mDecoded, aUpdateRect);
   561   // clamp to bounds, in case someone sends a bogus updateRect (I'm looking at
   562   // you, gif decoder)
   563   nsIntRect boundsRect(mOffset, mSize);
   564   mDecoded.IntersectRect(mDecoded, boundsRect);
   566   mDirty = true;
   568   return NS_OK;
   569 }
   571 bool imgFrame::GetIsDirty() const
   572 {
   573   MutexAutoLock lock(mDirtyMutex);
   574   return mDirty;
   575 }
   577 nsIntRect imgFrame::GetRect() const
   578 {
   579   return nsIntRect(mOffset, mSize);
   580 }
   582 gfxImageFormat imgFrame::GetFormat() const
   583 {
   584   return mFormat;
   585 }
   587 bool imgFrame::GetNeedsBackground() const
   588 {
   589   // We need a background painted if we have alpha or we're incomplete.
   590   return (mFormat == gfxImageFormat::ARGB32 || !ImageComplete());
   591 }
   593 uint32_t imgFrame::GetImageBytesPerRow() const
   594 {
   595   if (mImageSurface)
   596     return mImageSurface->Stride();
   598   if (mVBuf)
   599     return gfxImageSurface::ComputeStride(mSize, mFormat);
   601   if (mPaletteDepth)
   602     return mSize.width;
   604   NS_ERROR("GetImageBytesPerRow called with mImageSurface == null, mVBuf == null and mPaletteDepth == 0");
   606   return 0;
   607 }
   609 uint32_t imgFrame::GetImageDataLength() const
   610 {
   611   return GetImageBytesPerRow() * mSize.height;
   612 }
   614 void imgFrame::GetImageData(uint8_t **aData, uint32_t *length) const
   615 {
   616   NS_ABORT_IF_FALSE(mLockCount != 0, "Can't GetImageData unless frame is locked");
   618   if (mImageSurface)
   619     *aData = mImageSurface->Data();
   620   else if (mPalettedImageData)
   621     *aData = mPalettedImageData + PaletteDataLength();
   622   else
   623     *aData = nullptr;
   625   *length = GetImageDataLength();
   626 }
   628 uint8_t* imgFrame::GetImageData() const
   629 {
   630   uint8_t *data;
   631   uint32_t length;
   632   GetImageData(&data, &length);
   633   return data;
   634 }
   636 bool imgFrame::GetIsPaletted() const
   637 {
   638   return mPalettedImageData != nullptr;
   639 }
   641 bool imgFrame::GetHasAlpha() const
   642 {
   643   return mFormat == gfxImageFormat::ARGB32;
   644 }
   646 void imgFrame::GetPaletteData(uint32_t **aPalette, uint32_t *length) const
   647 {
   648   NS_ABORT_IF_FALSE(mLockCount != 0, "Can't GetPaletteData unless frame is locked");
   650   if (!mPalettedImageData) {
   651     *aPalette = nullptr;
   652     *length = 0;
   653   } else {
   654     *aPalette = (uint32_t *) mPalettedImageData;
   655     *length = PaletteDataLength();
   656   }
   657 }
   659 uint32_t* imgFrame::GetPaletteData() const
   660 {
   661   uint32_t* data;
   662   uint32_t length;
   663   GetPaletteData(&data, &length);
   664   return data;
   665 }
   667 nsresult imgFrame::LockImageData()
   668 {
   669   MOZ_ASSERT(NS_IsMainThread());
   671   NS_ABORT_IF_FALSE(mLockCount >= 0, "Unbalanced locks and unlocks");
   672   if (mLockCount < 0) {
   673     return NS_ERROR_FAILURE;
   674   }
   676   mLockCount++;
   678   // If we are not the first lock, there's nothing to do.
   679   if (mLockCount != 1) {
   680     return NS_OK;
   681   }
   683   // Paletted images don't have surfaces, so there's nothing to do.
   684   if (mPalettedImageData)
   685     return NS_OK;
   687   if (!mImageSurface) {
   688     if (mVBuf) {
   689       VolatileBufferPtr<uint8_t> ref(mVBuf);
   690       if (ref.WasBufferPurged())
   691         return NS_ERROR_FAILURE;
   693       mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
   694       if (!mImageSurface || mImageSurface->CairoStatus())
   695         return NS_ERROR_OUT_OF_MEMORY;
   696     }
   697     if (mOptSurface || mSinglePixel || mFormat == gfxImageFormat::RGB16_565) {
   698       gfxImageFormat format = mFormat;
   699       if (mFormat == gfxImageFormat::RGB16_565)
   700         format = gfxImageFormat::ARGB32;
   702       // Recover the pixels
   703       RefPtr<VolatileBuffer> buf =
   704         LockedImageSurface::AllocateBuffer(mSize, format);
   705       if (!buf) {
   706         return NS_ERROR_OUT_OF_MEMORY;
   707       }
   709       RefPtr<gfxImageSurface> surf =
   710         LockedImageSurface::CreateSurface(buf, mSize, mFormat);
   711       if (!surf || surf->CairoStatus())
   712         return NS_ERROR_OUT_OF_MEMORY;
   714       gfxContext context(surf);
   715       context.SetOperator(gfxContext::OPERATOR_SOURCE);
   716       if (mSinglePixel)
   717         context.SetDeviceColor(mSinglePixelColor);
   718       else if (mFormat == gfxImageFormat::RGB16_565)
   719         context.SetSource(mImageSurface);
   720       else
   721         context.SetSource(mOptSurface);
   722       context.Paint();
   724       mFormat = format;
   725       mVBuf = buf;
   726       mImageSurface = surf;
   727       mOptSurface = nullptr;
   728 #ifdef USE_WIN_SURFACE
   729       mWinSurface = nullptr;
   730 #endif
   731 #ifdef XP_MACOSX
   732       mQuartzSurface = nullptr;
   733 #endif
   734     }
   735   }
   737   // We might write to the bits in this image surface, so we need to make the
   738   // surface ready for that.
   739   if (mImageSurface)
   740     mImageSurface->Flush();
   742 #ifdef USE_WIN_SURFACE
   743   if (mWinSurface)
   744     mWinSurface->Flush();
   745 #endif
   747 #ifdef XP_MACOSX
   748   if (!mQuartzSurface && !ShouldUseImageSurfaces()) {
   749     mQuartzSurface = new gfxQuartzImageSurface(mImageSurface);
   750   }
   751 #endif
   753   return NS_OK;
   754 }
   756 nsresult imgFrame::UnlockImageData()
   757 {
   758   MOZ_ASSERT(NS_IsMainThread());
   760   NS_ABORT_IF_FALSE(mLockCount != 0, "Unlocking an unlocked image!");
   761   if (mLockCount == 0) {
   762     return NS_ERROR_FAILURE;
   763   }
   765   mLockCount--;
   767   NS_ABORT_IF_FALSE(mLockCount >= 0, "Unbalanced locks and unlocks");
   768   if (mLockCount < 0) {
   769     return NS_ERROR_FAILURE;
   770   }
   772   // If we are not the last lock, there's nothing to do.
   773   if (mLockCount != 0) {
   774     return NS_OK;
   775   }
   777   // Paletted images don't have surfaces, so there's nothing to do.
   778   if (mPalettedImageData)
   779     return NS_OK;
   781   // FIXME: Bug 795737
   782   // If this image has been drawn since we were locked, it has had snapshots
   783   // added, and we need to remove them before calling MarkDirty.
   784   if (mImageSurface)
   785     mImageSurface->Flush();
   787 #ifdef USE_WIN_SURFACE
   788   if (mWinSurface)
   789     mWinSurface->Flush();
   790 #endif
   792   // Assume we've been written to.
   793   if (mImageSurface)
   794     mImageSurface->MarkDirty();
   796 #ifdef USE_WIN_SURFACE
   797   if (mWinSurface)
   798     mWinSurface->MarkDirty();
   799 #endif
   801 #ifdef XP_MACOSX
   802   // The quartz image surface (ab)uses the flush method to get the
   803   // cairo_image_surface data into a CGImage, so we have to call Flush() here.
   804   if (mQuartzSurface)
   805     mQuartzSurface->Flush();
   806 #endif
   808   if (mVBuf && mDiscardable) {
   809     mImageSurface = nullptr;
   810 #ifdef XP_MACOSX
   811     mQuartzSurface = nullptr;
   812 #endif
   813   }
   815   return NS_OK;
   816 }
   818 void imgFrame::ApplyDirtToSurfaces()
   819 {
   820   MOZ_ASSERT(NS_IsMainThread());
   822   MutexAutoLock lock(mDirtyMutex);
   823   if (mDirty) {
   824     // FIXME: Bug 795737
   825     // If this image has been drawn since we were locked, it has had snapshots
   826     // added, and we need to remove them before calling MarkDirty.
   827     if (mImageSurface)
   828       mImageSurface->Flush();
   830 #ifdef USE_WIN_SURFACE
   831     if (mWinSurface)
   832       mWinSurface->Flush();
   833 #endif
   835     if (mImageSurface)
   836       mImageSurface->MarkDirty();
   838 #ifdef USE_WIN_SURFACE
   839     if (mWinSurface)
   840       mWinSurface->MarkDirty();
   841 #endif
   843 #ifdef XP_MACOSX
   844     // The quartz image surface (ab)uses the flush method to get the
   845     // cairo_image_surface data into a CGImage, so we have to call Flush() here.
   846     if (mQuartzSurface)
   847       mQuartzSurface->Flush();
   848 #endif
   850     mDirty = false;
   851   }
   852 }
   854 void imgFrame::SetDiscardable()
   855 {
   856   MOZ_ASSERT(mLockCount, "Expected to be locked when SetDiscardable is called");
   857   // Disabled elsewhere due to the cost of calling GetSourceSurfaceForSurface.
   858 #ifdef MOZ_WIDGET_ANDROID
   859   mDiscardable = true;
   860 #endif
   861 }
   863 int32_t imgFrame::GetRawTimeout() const
   864 {
   865   return mTimeout;
   866 }
   868 void imgFrame::SetRawTimeout(int32_t aTimeout)
   869 {
   870   mTimeout = aTimeout;
   871 }
   873 int32_t imgFrame::GetFrameDisposalMethod() const
   874 {
   875   return mDisposalMethod;
   876 }
   878 void imgFrame::SetFrameDisposalMethod(int32_t aFrameDisposalMethod)
   879 {
   880   mDisposalMethod = aFrameDisposalMethod;
   881 }
   883 int32_t imgFrame::GetBlendMethod() const
   884 {
   885   return mBlendMethod;
   886 }
   888 void imgFrame::SetBlendMethod(int32_t aBlendMethod)
   889 {
   890   mBlendMethod = (int8_t)aBlendMethod;
   891 }
   893 // This can be called from any thread.
   894 bool imgFrame::ImageComplete() const
   895 {
   896   MutexAutoLock lock(mDirtyMutex);
   898   return mDecoded.IsEqualInterior(nsIntRect(mOffset, mSize));
   899 }
   901 // A hint from the image decoders that this image has no alpha, even
   902 // though we created is ARGB32.  This changes our format to RGB24,
   903 // which in turn will cause us to Optimize() to RGB24.  Has no effect
   904 // after Optimize() is called, though in all cases it will be just a
   905 // performance win -- the pixels are still correct and have the A byte
   906 // set to 0xff.
   907 void imgFrame::SetHasNoAlpha()
   908 {
   909   if (mFormat == gfxImageFormat::ARGB32) {
   910       mFormat = gfxImageFormat::RGB24;
   911       mFormatChanged = true;
   912       ThebesSurface()->SetOpaqueRect(gfxRect(0, 0, mSize.width, mSize.height));
   913   }
   914 }
   916 void imgFrame::SetAsNonPremult(bool aIsNonPremult)
   917 {
   918   mNonPremult = aIsNonPremult;
   919 }
   921 bool imgFrame::GetCompositingFailed() const
   922 {
   923   return mCompositingFailed;
   924 }
   926 void imgFrame::SetCompositingFailed(bool val)
   927 {
   928   mCompositingFailed = val;
   929 }
   931 // If |aLocation| indicates this is heap memory, we try to measure things with
   932 // |aMallocSizeOf|.  If that fails (because the platform doesn't support it) or
   933 // it's non-heap memory, we fall back to computing the size analytically.
   934 size_t
   935 imgFrame::SizeOfExcludingThisWithComputedFallbackIfHeap(gfxMemoryLocation aLocation, mozilla::MallocSizeOf aMallocSizeOf) const
   936 {
   937   // aMallocSizeOf is only used if aLocation==gfxMemoryLocation::IN_PROCESS_HEAP.  It
   938   // should be nullptr otherwise.
   939   NS_ABORT_IF_FALSE(
   940     (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP &&  aMallocSizeOf) ||
   941     (aLocation != gfxMemoryLocation::IN_PROCESS_HEAP && !aMallocSizeOf),
   942     "mismatch between aLocation and aMallocSizeOf");
   944   size_t n = 0;
   946   if (mPalettedImageData && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
   947     size_t n2 = aMallocSizeOf(mPalettedImageData);
   948     if (n2 == 0) {
   949       n2 = GetImageDataLength() + PaletteDataLength();
   950     }
   951     n += n2;
   952   }
   954 #ifdef USE_WIN_SURFACE
   955   if (mWinSurface && aLocation == mWinSurface->GetMemoryLocation()) {
   956     n += mWinSurface->KnownMemoryUsed();
   957   } else
   958 #endif
   959 #ifdef XP_MACOSX
   960   if (mQuartzSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
   961     n += aMallocSizeOf(mQuartzSurface);
   962   }
   963 #endif
   964   if (mImageSurface && aLocation == mImageSurface->GetMemoryLocation()) {
   965     size_t n2 = 0;
   966     if (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) { // HEAP: measure
   967       n2 = mImageSurface->SizeOfIncludingThis(aMallocSizeOf);
   968     }
   969     if (n2 == 0) {  // non-HEAP or computed fallback for HEAP
   970       n2 = mImageSurface->KnownMemoryUsed();
   971     }
   972     n += n2;
   973   }
   975   if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
   976     n += aMallocSizeOf(mVBuf);
   977     n += mVBuf->HeapSizeOfExcludingThis(aMallocSizeOf);
   978   }
   980   if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_NONHEAP) {
   981     n += mVBuf->NonHeapSizeOfExcludingThis();
   982   }
   984   if (mOptSurface && aLocation == mOptSurface->GetMemoryLocation()) {
   985     size_t n2 = 0;
   986     if (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP &&
   987         mOptSurface->SizeOfIsMeasured()) {
   988       // HEAP: measure (but only if the sub-class is capable of measuring)
   989       n2 = mOptSurface->SizeOfIncludingThis(aMallocSizeOf);
   990     }
   991     if (n2 == 0) {  // non-HEAP or computed fallback for HEAP
   992       n2 = mOptSurface->KnownMemoryUsed();
   993     }
   994     n += n2;
   995   }
   997   return n;
   998 }

mercurial