1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/src/imgFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,998 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "imgFrame.h" 1.11 +#include "DiscardTracker.h" 1.12 + 1.13 +#include "prenv.h" 1.14 + 1.15 +#include "gfx2DGlue.h" 1.16 +#include "gfxPlatform.h" 1.17 +#include "gfxUtils.h" 1.18 +#include "gfxAlphaRecovery.h" 1.19 + 1.20 +static bool gDisableOptimize = false; 1.21 + 1.22 +#include "cairo.h" 1.23 +#include "GeckoProfiler.h" 1.24 +#include "mozilla/Likely.h" 1.25 +#include "mozilla/MemoryReporting.h" 1.26 +#include "nsMargin.h" 1.27 +#include "mozilla/CheckedInt.h" 1.28 + 1.29 +#if defined(XP_WIN) 1.30 + 1.31 +#include "gfxWindowsPlatform.h" 1.32 + 1.33 +/* Whether to use the windows surface; only for desktop win32 */ 1.34 +#define USE_WIN_SURFACE 1 1.35 + 1.36 +#endif 1.37 + 1.38 +using namespace mozilla; 1.39 +using namespace mozilla::gfx; 1.40 +using namespace mozilla::image; 1.41 + 1.42 +static cairo_user_data_key_t kVolatileBuffer; 1.43 + 1.44 +static void 1.45 +VolatileBufferRelease(void *vbuf) 1.46 +{ 1.47 + delete static_cast<VolatileBufferPtr<unsigned char>*>(vbuf); 1.48 +} 1.49 + 1.50 +gfxImageSurface * 1.51 +LockedImageSurface::CreateSurface(VolatileBuffer *vbuf, 1.52 + const gfxIntSize& size, 1.53 + gfxImageFormat format) 1.54 +{ 1.55 + VolatileBufferPtr<unsigned char> *vbufptr = 1.56 + new VolatileBufferPtr<unsigned char>(vbuf); 1.57 + MOZ_ASSERT(!vbufptr->WasBufferPurged(), "Expected image data!"); 1.58 + 1.59 + long stride = gfxImageSurface::ComputeStride(size, format); 1.60 + gfxImageSurface *img = new gfxImageSurface(*vbufptr, size, stride, format); 1.61 + if (!img || img->CairoStatus()) { 1.62 + delete img; 1.63 + delete vbufptr; 1.64 + return nullptr; 1.65 + } 1.66 + 1.67 + img->SetData(&kVolatileBuffer, vbufptr, VolatileBufferRelease); 1.68 + return img; 1.69 +} 1.70 + 1.71 +TemporaryRef<VolatileBuffer> 1.72 +LockedImageSurface::AllocateBuffer(const gfxIntSize& size, 1.73 + gfxImageFormat format) 1.74 +{ 1.75 + long stride = gfxImageSurface::ComputeStride(size, format); 1.76 + RefPtr<VolatileBuffer> buf = new VolatileBuffer(); 1.77 + if (buf->Init(stride * size.height, 1.78 + 1 << gfxAlphaRecovery::GoodAlignmentLog2())) 1.79 + return buf; 1.80 + 1.81 + return nullptr; 1.82 +} 1.83 + 1.84 +// Returns true if an image of aWidth x aHeight is allowed and legal. 1.85 +static bool AllowedImageSize(int32_t aWidth, int32_t aHeight) 1.86 +{ 1.87 + // reject over-wide or over-tall images 1.88 + const int32_t k64KLimit = 0x0000FFFF; 1.89 + if (MOZ_UNLIKELY(aWidth > k64KLimit || aHeight > k64KLimit )) { 1.90 + NS_WARNING("image too big"); 1.91 + return false; 1.92 + } 1.93 + 1.94 + // protect against invalid sizes 1.95 + if (MOZ_UNLIKELY(aHeight <= 0 || aWidth <= 0)) { 1.96 + return false; 1.97 + } 1.98 + 1.99 + // check to make sure we don't overflow a 32-bit 1.100 + CheckedInt32 requiredBytes = CheckedInt32(aWidth) * CheckedInt32(aHeight) * 4; 1.101 + if (MOZ_UNLIKELY(!requiredBytes.isValid())) { 1.102 + NS_WARNING("width or height too large"); 1.103 + return false; 1.104 + } 1.105 +#if defined(XP_MACOSX) 1.106 + // CoreGraphics is limited to images < 32K in *height*, so clamp all surfaces on the Mac to that height 1.107 + if (MOZ_UNLIKELY(aHeight > SHRT_MAX)) { 1.108 + NS_WARNING("image too big"); 1.109 + return false; 1.110 + } 1.111 +#endif 1.112 + return true; 1.113 +} 1.114 + 1.115 +// Returns whether we should, at this time, use image surfaces instead of 1.116 +// optimized platform-specific surfaces. 1.117 +static bool ShouldUseImageSurfaces() 1.118 +{ 1.119 +#if defined(USE_WIN_SURFACE) 1.120 + static const DWORD kGDIObjectsHighWaterMark = 7000; 1.121 + 1.122 + if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() == 1.123 + gfxWindowsPlatform::RENDER_DIRECT2D) { 1.124 + return true; 1.125 + } 1.126 + 1.127 + // at 7000 GDI objects, stop allocating normal images to make sure 1.128 + // we never hit the 10k hard limit. 1.129 + // GetCurrentProcess() just returns (HANDLE)-1, it's inlined afaik 1.130 + DWORD count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS); 1.131 + if (count == 0 || 1.132 + count > kGDIObjectsHighWaterMark) 1.133 + { 1.134 + // either something's broken (count == 0), 1.135 + // or we hit our high water mark; disable 1.136 + // image allocations for a bit. 1.137 + return true; 1.138 + } 1.139 +#endif 1.140 + 1.141 + return false; 1.142 +} 1.143 + 1.144 +imgFrame::imgFrame() : 1.145 + mDecoded(0, 0, 0, 0), 1.146 + mDirtyMutex("imgFrame::mDirty"), 1.147 + mPalettedImageData(nullptr), 1.148 + mSinglePixelColor(0), 1.149 + mTimeout(100), 1.150 + mDisposalMethod(0), /* imgIContainer::kDisposeNotSpecified */ 1.151 + mLockCount(0), 1.152 + mBlendMethod(1), /* imgIContainer::kBlendOver */ 1.153 + mSinglePixel(false), 1.154 + mFormatChanged(false), 1.155 + mCompositingFailed(false), 1.156 + mNonPremult(false), 1.157 + mDiscardable(false), 1.158 + mInformedDiscardTracker(false), 1.159 + mDirty(false) 1.160 +{ 1.161 + static bool hasCheckedOptimize = false; 1.162 + if (!hasCheckedOptimize) { 1.163 + if (PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")) { 1.164 + gDisableOptimize = true; 1.165 + } 1.166 + hasCheckedOptimize = true; 1.167 + } 1.168 +} 1.169 + 1.170 +imgFrame::~imgFrame() 1.171 +{ 1.172 + moz_free(mPalettedImageData); 1.173 + mPalettedImageData = nullptr; 1.174 + 1.175 + if (mInformedDiscardTracker) { 1.176 + DiscardTracker::InformDeallocation(4 * mSize.height * mSize.width); 1.177 + } 1.178 +} 1.179 + 1.180 +nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, 1.181 + gfxImageFormat aFormat, uint8_t aPaletteDepth /* = 0 */) 1.182 +{ 1.183 + // assert for properties that should be verified by decoders, warn for properties related to bad content 1.184 + if (!AllowedImageSize(aWidth, aHeight)) { 1.185 + NS_WARNING("Should have legal image size"); 1.186 + return NS_ERROR_FAILURE; 1.187 + } 1.188 + 1.189 + mOffset.MoveTo(aX, aY); 1.190 + mSize.SizeTo(aWidth, aHeight); 1.191 + 1.192 + mFormat = aFormat; 1.193 + mPaletteDepth = aPaletteDepth; 1.194 + 1.195 + if (aPaletteDepth != 0) { 1.196 + // We're creating for a paletted image. 1.197 + if (aPaletteDepth > 8) { 1.198 + NS_WARNING("Should have legal palette depth"); 1.199 + NS_ERROR("This Depth is not supported"); 1.200 + return NS_ERROR_FAILURE; 1.201 + } 1.202 + 1.203 + // Use the fallible allocator here 1.204 + mPalettedImageData = (uint8_t*)moz_malloc(PaletteDataLength() + GetImageDataLength()); 1.205 + if (!mPalettedImageData) 1.206 + NS_WARNING("moz_malloc for paletted image data should succeed"); 1.207 + NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY); 1.208 + } else { 1.209 + // Inform the discard tracker that we are going to allocate some memory. 1.210 + if (!DiscardTracker::TryAllocation(4 * mSize.width * mSize.height)) { 1.211 + NS_WARNING("Exceed the hard limit of decode image size"); 1.212 + return NS_ERROR_OUT_OF_MEMORY; 1.213 + } 1.214 + // For Windows, we must create the device surface first (if we're 1.215 + // going to) so that the image surface can wrap it. Can't be done 1.216 + // the other way around. 1.217 +#ifdef USE_WIN_SURFACE 1.218 + if (!ShouldUseImageSurfaces()) { 1.219 + mWinSurface = new gfxWindowsSurface(gfxIntSize(mSize.width, mSize.height), mFormat); 1.220 + if (mWinSurface && mWinSurface->CairoStatus() == 0) { 1.221 + // no error 1.222 + mImageSurface = mWinSurface->GetAsImageSurface(); 1.223 + } else { 1.224 + mWinSurface = nullptr; 1.225 + } 1.226 + } 1.227 +#endif 1.228 + 1.229 + // For other platforms, space for the image surface is first allocated in 1.230 + // a volatile buffer and then wrapped by a LockedImageSurface. 1.231 + // This branch is also used on Windows if we're not using device surfaces 1.232 + // or if we couldn't create one. 1.233 + if (!mImageSurface) { 1.234 + mVBuf = LockedImageSurface::AllocateBuffer(mSize, mFormat); 1.235 + if (!mVBuf) { 1.236 + return NS_ERROR_OUT_OF_MEMORY; 1.237 + } 1.238 + if (mVBuf->OnHeap()) { 1.239 + long stride = gfxImageSurface::ComputeStride(mSize, mFormat); 1.240 + VolatileBufferPtr<uint8_t> ptr(mVBuf); 1.241 + memset(ptr, 0, stride * mSize.height); 1.242 + } 1.243 + mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat); 1.244 + } 1.245 + 1.246 + if (!mImageSurface || mImageSurface->CairoStatus()) { 1.247 + mImageSurface = nullptr; 1.248 + // guess 1.249 + if (!mImageSurface) { 1.250 + NS_WARNING("Allocation of gfxImageSurface should succeed"); 1.251 + } else if (!mImageSurface->CairoStatus()) { 1.252 + NS_WARNING("gfxImageSurface should have good CairoStatus"); 1.253 + } 1.254 + 1.255 + // Image surface allocation is failed, need to return 1.256 + // the booked buffer size. 1.257 + DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height); 1.258 + return NS_ERROR_OUT_OF_MEMORY; 1.259 + } 1.260 + 1.261 + mInformedDiscardTracker = true; 1.262 + 1.263 +#ifdef XP_MACOSX 1.264 + if (!ShouldUseImageSurfaces()) { 1.265 + mQuartzSurface = new gfxQuartzImageSurface(mImageSurface); 1.266 + } 1.267 +#endif 1.268 + } 1.269 + 1.270 + return NS_OK; 1.271 +} 1.272 + 1.273 +nsresult imgFrame::Optimize() 1.274 +{ 1.275 + MOZ_ASSERT(NS_IsMainThread()); 1.276 + 1.277 + if (gDisableOptimize) 1.278 + return NS_OK; 1.279 + 1.280 + if (mPalettedImageData || mOptSurface || mSinglePixel) 1.281 + return NS_OK; 1.282 + 1.283 + // Don't do single-color opts on non-premult data. 1.284 + // Cairo doesn't support non-premult single-colors. 1.285 + if (mNonPremult) 1.286 + return NS_OK; 1.287 + 1.288 + /* Figure out if the entire image is a constant color */ 1.289 + 1.290 + // this should always be true 1.291 + if (mImageSurface->Stride() == mSize.width * 4) { 1.292 + uint32_t *imgData = (uint32_t*) mImageSurface->Data(); 1.293 + uint32_t firstPixel = * (uint32_t*) imgData; 1.294 + uint32_t pixelCount = mSize.width * mSize.height + 1; 1.295 + 1.296 + while (--pixelCount && *imgData++ == firstPixel) 1.297 + ; 1.298 + 1.299 + if (pixelCount == 0) { 1.300 + // all pixels were the same 1.301 + if (mFormat == gfxImageFormat::ARGB32 || 1.302 + mFormat == gfxImageFormat::RGB24) 1.303 + { 1.304 + // Should already be premult if desired. 1.305 + gfxRGBA::PackedColorType inputType = gfxRGBA::PACKED_XRGB; 1.306 + if (mFormat == gfxImageFormat::ARGB32) 1.307 + inputType = gfxRGBA::PACKED_ARGB_PREMULTIPLIED; 1.308 + 1.309 + mSinglePixelColor = gfxRGBA(firstPixel, inputType); 1.310 + 1.311 + mSinglePixel = true; 1.312 + 1.313 + // blow away the older surfaces (if they exist), to release their memory 1.314 + mVBuf = nullptr; 1.315 + mImageSurface = nullptr; 1.316 + mOptSurface = nullptr; 1.317 +#ifdef USE_WIN_SURFACE 1.318 + mWinSurface = nullptr; 1.319 +#endif 1.320 +#ifdef XP_MACOSX 1.321 + mQuartzSurface = nullptr; 1.322 +#endif 1.323 + mDrawSurface = nullptr; 1.324 + 1.325 + // We just dumped most of our allocated memory, so tell the discard 1.326 + // tracker that we're not using any at all. 1.327 + if (mInformedDiscardTracker) { 1.328 + DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height); 1.329 + mInformedDiscardTracker = false; 1.330 + } 1.331 + 1.332 + return NS_OK; 1.333 + } 1.334 + } 1.335 + 1.336 + // if it's not RGB24/ARGB32, don't optimize, but we never hit this at the moment 1.337 + } 1.338 + 1.339 + // if we're being forced to use image surfaces due to 1.340 + // resource constraints, don't try to optimize beyond same-pixel. 1.341 + if (ShouldUseImageSurfaces()) 1.342 + return NS_OK; 1.343 + 1.344 + mOptSurface = nullptr; 1.345 + 1.346 +#ifdef USE_WIN_SURFACE 1.347 + if (mWinSurface) { 1.348 + if (!mFormatChanged) { 1.349 + // just use the DIB if the format has not changed 1.350 + mOptSurface = mWinSurface; 1.351 + } 1.352 + } 1.353 +#endif 1.354 + 1.355 +#ifdef XP_MACOSX 1.356 + if (mQuartzSurface) { 1.357 + mQuartzSurface->Flush(); 1.358 + } 1.359 +#endif 1.360 + 1.361 +#ifdef ANDROID 1.362 + gfxImageFormat optFormat = 1.363 + gfxPlatform::GetPlatform()-> 1.364 + OptimalFormatForContent(gfxASurface::ContentFromFormat(mFormat)); 1.365 + 1.366 + if (optFormat == gfxImageFormat::RGB16_565) { 1.367 + RefPtr<VolatileBuffer> buf = 1.368 + LockedImageSurface::AllocateBuffer(mSize, optFormat); 1.369 + if (!buf) 1.370 + return NS_OK; 1.371 + 1.372 + nsRefPtr<gfxImageSurface> surf = 1.373 + LockedImageSurface::CreateSurface(buf, mSize, optFormat); 1.374 + 1.375 + gfxContext ctx(surf); 1.376 + ctx.SetOperator(gfxContext::OPERATOR_SOURCE); 1.377 + ctx.SetSource(mImageSurface); 1.378 + ctx.Paint(); 1.379 + 1.380 + mImageSurface = surf; 1.381 + mVBuf = buf; 1.382 + mFormat = optFormat; 1.383 + mDrawSurface = nullptr; 1.384 + } 1.385 +#else 1.386 + if (mOptSurface == nullptr) 1.387 + mOptSurface = gfxPlatform::GetPlatform()->OptimizeImage(mImageSurface, mFormat); 1.388 +#endif 1.389 + 1.390 + if (mOptSurface) { 1.391 + mVBuf = nullptr; 1.392 + mImageSurface = nullptr; 1.393 +#ifdef USE_WIN_SURFACE 1.394 + mWinSurface = nullptr; 1.395 +#endif 1.396 +#ifdef XP_MACOSX 1.397 + mQuartzSurface = nullptr; 1.398 +#endif 1.399 + mDrawSurface = nullptr; 1.400 + } 1.401 + 1.402 + return NS_OK; 1.403 +} 1.404 + 1.405 +static void 1.406 +DoSingleColorFastPath(gfxContext* aContext, 1.407 + const gfxRGBA& aSinglePixelColor, 1.408 + const gfxRect& aFill) 1.409 +{ 1.410 + // if a == 0, it's a noop 1.411 + if (aSinglePixelColor.a == 0.0) 1.412 + return; 1.413 + 1.414 + gfxContext::GraphicsOperator op = aContext->CurrentOperator(); 1.415 + if (op == gfxContext::OPERATOR_OVER && aSinglePixelColor.a == 1.0) { 1.416 + aContext->SetOperator(gfxContext::OPERATOR_SOURCE); 1.417 + } 1.418 + 1.419 + aContext->SetDeviceColor(aSinglePixelColor); 1.420 + aContext->NewPath(); 1.421 + aContext->Rectangle(aFill); 1.422 + aContext->Fill(); 1.423 + aContext->SetOperator(op); 1.424 + aContext->SetDeviceColor(gfxRGBA(0,0,0,0)); 1.425 +} 1.426 + 1.427 +imgFrame::SurfaceWithFormat 1.428 +imgFrame::SurfaceForDrawing(bool aDoPadding, 1.429 + bool aDoPartialDecode, 1.430 + bool aDoTile, 1.431 + const nsIntMargin& aPadding, 1.432 + gfxMatrix& aUserSpaceToImageSpace, 1.433 + gfxRect& aFill, 1.434 + gfxRect& aSubimage, 1.435 + gfxRect& aSourceRect, 1.436 + gfxRect& aImageRect, 1.437 + gfxASurface* aSurface) 1.438 +{ 1.439 + IntSize size(int32_t(aImageRect.Width()), int32_t(aImageRect.Height())); 1.440 + if (!aDoPadding && !aDoPartialDecode) { 1.441 + NS_ASSERTION(!mSinglePixel, "This should already have been handled"); 1.442 + return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, ThebesIntSize(size)), mFormat); 1.443 + } 1.444 + 1.445 + gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width, mDecoded.height); 1.446 + 1.447 + if (aDoTile || mSinglePixel) { 1.448 + // Create a temporary surface. 1.449 + // Give this surface an alpha channel because there are 1.450 + // transparent pixels in the padding or undecoded area 1.451 + gfxImageFormat format = gfxImageFormat::ARGB32; 1.452 + nsRefPtr<gfxASurface> surface = 1.453 + gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, gfxImageSurface::ContentFromFormat(format)); 1.454 + if (!surface || surface->CairoStatus()) 1.455 + return SurfaceWithFormat(); 1.456 + 1.457 + // Fill 'available' with whatever we've got 1.458 + gfxContext tmpCtx(surface); 1.459 + tmpCtx.SetOperator(gfxContext::OPERATOR_SOURCE); 1.460 + if (mSinglePixel) { 1.461 + tmpCtx.SetDeviceColor(mSinglePixelColor); 1.462 + } else { 1.463 + tmpCtx.SetSource(aSurface, gfxPoint(aPadding.left, aPadding.top)); 1.464 + } 1.465 + tmpCtx.Rectangle(available); 1.466 + tmpCtx.Fill(); 1.467 + 1.468 + return SurfaceWithFormat(new gfxSurfaceDrawable(surface, ThebesIntSize(size)), format); 1.469 + } 1.470 + 1.471 + // Not tiling, and we have a surface, so we can account for 1.472 + // padding and/or a partial decode just by twiddling parameters. 1.473 + // First, update our user-space fill rect. 1.474 + aSourceRect = aSourceRect.Intersect(available); 1.475 + gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace; 1.476 + imageSpaceToUserSpace.Invert(); 1.477 + aFill = imageSpaceToUserSpace.Transform(aSourceRect); 1.478 + 1.479 + aSubimage = aSubimage.Intersect(available) - gfxPoint(aPadding.left, aPadding.top); 1.480 + aUserSpaceToImageSpace.Multiply(gfxMatrix().Translate(-gfxPoint(aPadding.left, aPadding.top))); 1.481 + aSourceRect = aSourceRect - gfxPoint(aPadding.left, aPadding.top); 1.482 + aImageRect = gfxRect(0, 0, mSize.width, mSize.height); 1.483 + 1.484 + gfxIntSize availableSize(mDecoded.width, mDecoded.height); 1.485 + return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, availableSize), 1.486 + mFormat); 1.487 +} 1.488 + 1.489 +bool imgFrame::Draw(gfxContext *aContext, GraphicsFilter aFilter, 1.490 + const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill, 1.491 + const nsIntMargin &aPadding, const nsIntRect &aSubimage, 1.492 + uint32_t aImageFlags) 1.493 +{ 1.494 + PROFILER_LABEL("image", "imgFrame::Draw"); 1.495 + NS_ASSERTION(!aFill.IsEmpty(), "zero dest size --- fix caller"); 1.496 + NS_ASSERTION(!aSubimage.IsEmpty(), "zero source size --- fix caller"); 1.497 + NS_ASSERTION(!mPalettedImageData, "Directly drawing a paletted image!"); 1.498 + 1.499 + bool doPadding = aPadding != nsIntMargin(0,0,0,0); 1.500 + bool doPartialDecode = !ImageComplete(); 1.501 + 1.502 + if (mSinglePixel && !doPadding && !doPartialDecode) { 1.503 + DoSingleColorFastPath(aContext, mSinglePixelColor, aFill); 1.504 + return true; 1.505 + } 1.506 + 1.507 + gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace; 1.508 + gfxRect sourceRect = userSpaceToImageSpace.TransformBounds(aFill); 1.509 + gfxRect imageRect(0, 0, mSize.width + aPadding.LeftRight(), 1.510 + mSize.height + aPadding.TopBottom()); 1.511 + gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height); 1.512 + gfxRect fill = aFill; 1.513 + 1.514 + NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(), 1.515 + "We must be allowed to sample *some* source pixels!"); 1.516 + 1.517 + nsRefPtr<gfxASurface> surf = CachedThebesSurface(); 1.518 + VolatileBufferPtr<unsigned char> ref(mVBuf); 1.519 + if (!mSinglePixel && !surf) { 1.520 + if (ref.WasBufferPurged()) { 1.521 + return false; 1.522 + } 1.523 + 1.524 + surf = mDrawSurface; 1.525 + if (!surf) { 1.526 + long stride = gfxImageSurface::ComputeStride(mSize, mFormat); 1.527 + nsRefPtr<gfxImageSurface> imgSurf = 1.528 + new gfxImageSurface(ref, mSize, stride, mFormat); 1.529 +#if defined(XP_MACOSX) 1.530 + surf = mDrawSurface = new gfxQuartzImageSurface(imgSurf); 1.531 +#else 1.532 + surf = mDrawSurface = imgSurf; 1.533 +#endif 1.534 + } 1.535 + if (!surf || surf->CairoStatus()) { 1.536 + mDrawSurface = nullptr; 1.537 + return true; 1.538 + } 1.539 + } 1.540 + 1.541 + bool doTile = !imageRect.Contains(sourceRect) && 1.542 + !(aImageFlags & imgIContainer::FLAG_CLAMP); 1.543 + SurfaceWithFormat surfaceResult = 1.544 + SurfaceForDrawing(doPadding, doPartialDecode, doTile, aPadding, 1.545 + userSpaceToImageSpace, fill, subimage, sourceRect, 1.546 + imageRect, surf); 1.547 + 1.548 + if (surfaceResult.IsValid()) { 1.549 + gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable, 1.550 + userSpaceToImageSpace, 1.551 + subimage, sourceRect, imageRect, fill, 1.552 + surfaceResult.mFormat, aFilter, aImageFlags); 1.553 + } 1.554 + return true; 1.555 +} 1.556 + 1.557 +// This can be called from any thread, but not simultaneously. 1.558 +nsresult imgFrame::ImageUpdated(const nsIntRect &aUpdateRect) 1.559 +{ 1.560 + MutexAutoLock lock(mDirtyMutex); 1.561 + 1.562 + mDecoded.UnionRect(mDecoded, aUpdateRect); 1.563 + 1.564 + // clamp to bounds, in case someone sends a bogus updateRect (I'm looking at 1.565 + // you, gif decoder) 1.566 + nsIntRect boundsRect(mOffset, mSize); 1.567 + mDecoded.IntersectRect(mDecoded, boundsRect); 1.568 + 1.569 + mDirty = true; 1.570 + 1.571 + return NS_OK; 1.572 +} 1.573 + 1.574 +bool imgFrame::GetIsDirty() const 1.575 +{ 1.576 + MutexAutoLock lock(mDirtyMutex); 1.577 + return mDirty; 1.578 +} 1.579 + 1.580 +nsIntRect imgFrame::GetRect() const 1.581 +{ 1.582 + return nsIntRect(mOffset, mSize); 1.583 +} 1.584 + 1.585 +gfxImageFormat imgFrame::GetFormat() const 1.586 +{ 1.587 + return mFormat; 1.588 +} 1.589 + 1.590 +bool imgFrame::GetNeedsBackground() const 1.591 +{ 1.592 + // We need a background painted if we have alpha or we're incomplete. 1.593 + return (mFormat == gfxImageFormat::ARGB32 || !ImageComplete()); 1.594 +} 1.595 + 1.596 +uint32_t imgFrame::GetImageBytesPerRow() const 1.597 +{ 1.598 + if (mImageSurface) 1.599 + return mImageSurface->Stride(); 1.600 + 1.601 + if (mVBuf) 1.602 + return gfxImageSurface::ComputeStride(mSize, mFormat); 1.603 + 1.604 + if (mPaletteDepth) 1.605 + return mSize.width; 1.606 + 1.607 + NS_ERROR("GetImageBytesPerRow called with mImageSurface == null, mVBuf == null and mPaletteDepth == 0"); 1.608 + 1.609 + return 0; 1.610 +} 1.611 + 1.612 +uint32_t imgFrame::GetImageDataLength() const 1.613 +{ 1.614 + return GetImageBytesPerRow() * mSize.height; 1.615 +} 1.616 + 1.617 +void imgFrame::GetImageData(uint8_t **aData, uint32_t *length) const 1.618 +{ 1.619 + NS_ABORT_IF_FALSE(mLockCount != 0, "Can't GetImageData unless frame is locked"); 1.620 + 1.621 + if (mImageSurface) 1.622 + *aData = mImageSurface->Data(); 1.623 + else if (mPalettedImageData) 1.624 + *aData = mPalettedImageData + PaletteDataLength(); 1.625 + else 1.626 + *aData = nullptr; 1.627 + 1.628 + *length = GetImageDataLength(); 1.629 +} 1.630 + 1.631 +uint8_t* imgFrame::GetImageData() const 1.632 +{ 1.633 + uint8_t *data; 1.634 + uint32_t length; 1.635 + GetImageData(&data, &length); 1.636 + return data; 1.637 +} 1.638 + 1.639 +bool imgFrame::GetIsPaletted() const 1.640 +{ 1.641 + return mPalettedImageData != nullptr; 1.642 +} 1.643 + 1.644 +bool imgFrame::GetHasAlpha() const 1.645 +{ 1.646 + return mFormat == gfxImageFormat::ARGB32; 1.647 +} 1.648 + 1.649 +void imgFrame::GetPaletteData(uint32_t **aPalette, uint32_t *length) const 1.650 +{ 1.651 + NS_ABORT_IF_FALSE(mLockCount != 0, "Can't GetPaletteData unless frame is locked"); 1.652 + 1.653 + if (!mPalettedImageData) { 1.654 + *aPalette = nullptr; 1.655 + *length = 0; 1.656 + } else { 1.657 + *aPalette = (uint32_t *) mPalettedImageData; 1.658 + *length = PaletteDataLength(); 1.659 + } 1.660 +} 1.661 + 1.662 +uint32_t* imgFrame::GetPaletteData() const 1.663 +{ 1.664 + uint32_t* data; 1.665 + uint32_t length; 1.666 + GetPaletteData(&data, &length); 1.667 + return data; 1.668 +} 1.669 + 1.670 +nsresult imgFrame::LockImageData() 1.671 +{ 1.672 + MOZ_ASSERT(NS_IsMainThread()); 1.673 + 1.674 + NS_ABORT_IF_FALSE(mLockCount >= 0, "Unbalanced locks and unlocks"); 1.675 + if (mLockCount < 0) { 1.676 + return NS_ERROR_FAILURE; 1.677 + } 1.678 + 1.679 + mLockCount++; 1.680 + 1.681 + // If we are not the first lock, there's nothing to do. 1.682 + if (mLockCount != 1) { 1.683 + return NS_OK; 1.684 + } 1.685 + 1.686 + // Paletted images don't have surfaces, so there's nothing to do. 1.687 + if (mPalettedImageData) 1.688 + return NS_OK; 1.689 + 1.690 + if (!mImageSurface) { 1.691 + if (mVBuf) { 1.692 + VolatileBufferPtr<uint8_t> ref(mVBuf); 1.693 + if (ref.WasBufferPurged()) 1.694 + return NS_ERROR_FAILURE; 1.695 + 1.696 + mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat); 1.697 + if (!mImageSurface || mImageSurface->CairoStatus()) 1.698 + return NS_ERROR_OUT_OF_MEMORY; 1.699 + } 1.700 + if (mOptSurface || mSinglePixel || mFormat == gfxImageFormat::RGB16_565) { 1.701 + gfxImageFormat format = mFormat; 1.702 + if (mFormat == gfxImageFormat::RGB16_565) 1.703 + format = gfxImageFormat::ARGB32; 1.704 + 1.705 + // Recover the pixels 1.706 + RefPtr<VolatileBuffer> buf = 1.707 + LockedImageSurface::AllocateBuffer(mSize, format); 1.708 + if (!buf) { 1.709 + return NS_ERROR_OUT_OF_MEMORY; 1.710 + } 1.711 + 1.712 + RefPtr<gfxImageSurface> surf = 1.713 + LockedImageSurface::CreateSurface(buf, mSize, mFormat); 1.714 + if (!surf || surf->CairoStatus()) 1.715 + return NS_ERROR_OUT_OF_MEMORY; 1.716 + 1.717 + gfxContext context(surf); 1.718 + context.SetOperator(gfxContext::OPERATOR_SOURCE); 1.719 + if (mSinglePixel) 1.720 + context.SetDeviceColor(mSinglePixelColor); 1.721 + else if (mFormat == gfxImageFormat::RGB16_565) 1.722 + context.SetSource(mImageSurface); 1.723 + else 1.724 + context.SetSource(mOptSurface); 1.725 + context.Paint(); 1.726 + 1.727 + mFormat = format; 1.728 + mVBuf = buf; 1.729 + mImageSurface = surf; 1.730 + mOptSurface = nullptr; 1.731 +#ifdef USE_WIN_SURFACE 1.732 + mWinSurface = nullptr; 1.733 +#endif 1.734 +#ifdef XP_MACOSX 1.735 + mQuartzSurface = nullptr; 1.736 +#endif 1.737 + } 1.738 + } 1.739 + 1.740 + // We might write to the bits in this image surface, so we need to make the 1.741 + // surface ready for that. 1.742 + if (mImageSurface) 1.743 + mImageSurface->Flush(); 1.744 + 1.745 +#ifdef USE_WIN_SURFACE 1.746 + if (mWinSurface) 1.747 + mWinSurface->Flush(); 1.748 +#endif 1.749 + 1.750 +#ifdef XP_MACOSX 1.751 + if (!mQuartzSurface && !ShouldUseImageSurfaces()) { 1.752 + mQuartzSurface = new gfxQuartzImageSurface(mImageSurface); 1.753 + } 1.754 +#endif 1.755 + 1.756 + return NS_OK; 1.757 +} 1.758 + 1.759 +nsresult imgFrame::UnlockImageData() 1.760 +{ 1.761 + MOZ_ASSERT(NS_IsMainThread()); 1.762 + 1.763 + NS_ABORT_IF_FALSE(mLockCount != 0, "Unlocking an unlocked image!"); 1.764 + if (mLockCount == 0) { 1.765 + return NS_ERROR_FAILURE; 1.766 + } 1.767 + 1.768 + mLockCount--; 1.769 + 1.770 + NS_ABORT_IF_FALSE(mLockCount >= 0, "Unbalanced locks and unlocks"); 1.771 + if (mLockCount < 0) { 1.772 + return NS_ERROR_FAILURE; 1.773 + } 1.774 + 1.775 + // If we are not the last lock, there's nothing to do. 1.776 + if (mLockCount != 0) { 1.777 + return NS_OK; 1.778 + } 1.779 + 1.780 + // Paletted images don't have surfaces, so there's nothing to do. 1.781 + if (mPalettedImageData) 1.782 + return NS_OK; 1.783 + 1.784 + // FIXME: Bug 795737 1.785 + // If this image has been drawn since we were locked, it has had snapshots 1.786 + // added, and we need to remove them before calling MarkDirty. 1.787 + if (mImageSurface) 1.788 + mImageSurface->Flush(); 1.789 + 1.790 +#ifdef USE_WIN_SURFACE 1.791 + if (mWinSurface) 1.792 + mWinSurface->Flush(); 1.793 +#endif 1.794 + 1.795 + // Assume we've been written to. 1.796 + if (mImageSurface) 1.797 + mImageSurface->MarkDirty(); 1.798 + 1.799 +#ifdef USE_WIN_SURFACE 1.800 + if (mWinSurface) 1.801 + mWinSurface->MarkDirty(); 1.802 +#endif 1.803 + 1.804 +#ifdef XP_MACOSX 1.805 + // The quartz image surface (ab)uses the flush method to get the 1.806 + // cairo_image_surface data into a CGImage, so we have to call Flush() here. 1.807 + if (mQuartzSurface) 1.808 + mQuartzSurface->Flush(); 1.809 +#endif 1.810 + 1.811 + if (mVBuf && mDiscardable) { 1.812 + mImageSurface = nullptr; 1.813 +#ifdef XP_MACOSX 1.814 + mQuartzSurface = nullptr; 1.815 +#endif 1.816 + } 1.817 + 1.818 + return NS_OK; 1.819 +} 1.820 + 1.821 +void imgFrame::ApplyDirtToSurfaces() 1.822 +{ 1.823 + MOZ_ASSERT(NS_IsMainThread()); 1.824 + 1.825 + MutexAutoLock lock(mDirtyMutex); 1.826 + if (mDirty) { 1.827 + // FIXME: Bug 795737 1.828 + // If this image has been drawn since we were locked, it has had snapshots 1.829 + // added, and we need to remove them before calling MarkDirty. 1.830 + if (mImageSurface) 1.831 + mImageSurface->Flush(); 1.832 + 1.833 +#ifdef USE_WIN_SURFACE 1.834 + if (mWinSurface) 1.835 + mWinSurface->Flush(); 1.836 +#endif 1.837 + 1.838 + if (mImageSurface) 1.839 + mImageSurface->MarkDirty(); 1.840 + 1.841 +#ifdef USE_WIN_SURFACE 1.842 + if (mWinSurface) 1.843 + mWinSurface->MarkDirty(); 1.844 +#endif 1.845 + 1.846 +#ifdef XP_MACOSX 1.847 + // The quartz image surface (ab)uses the flush method to get the 1.848 + // cairo_image_surface data into a CGImage, so we have to call Flush() here. 1.849 + if (mQuartzSurface) 1.850 + mQuartzSurface->Flush(); 1.851 +#endif 1.852 + 1.853 + mDirty = false; 1.854 + } 1.855 +} 1.856 + 1.857 +void imgFrame::SetDiscardable() 1.858 +{ 1.859 + MOZ_ASSERT(mLockCount, "Expected to be locked when SetDiscardable is called"); 1.860 + // Disabled elsewhere due to the cost of calling GetSourceSurfaceForSurface. 1.861 +#ifdef MOZ_WIDGET_ANDROID 1.862 + mDiscardable = true; 1.863 +#endif 1.864 +} 1.865 + 1.866 +int32_t imgFrame::GetRawTimeout() const 1.867 +{ 1.868 + return mTimeout; 1.869 +} 1.870 + 1.871 +void imgFrame::SetRawTimeout(int32_t aTimeout) 1.872 +{ 1.873 + mTimeout = aTimeout; 1.874 +} 1.875 + 1.876 +int32_t imgFrame::GetFrameDisposalMethod() const 1.877 +{ 1.878 + return mDisposalMethod; 1.879 +} 1.880 + 1.881 +void imgFrame::SetFrameDisposalMethod(int32_t aFrameDisposalMethod) 1.882 +{ 1.883 + mDisposalMethod = aFrameDisposalMethod; 1.884 +} 1.885 + 1.886 +int32_t imgFrame::GetBlendMethod() const 1.887 +{ 1.888 + return mBlendMethod; 1.889 +} 1.890 + 1.891 +void imgFrame::SetBlendMethod(int32_t aBlendMethod) 1.892 +{ 1.893 + mBlendMethod = (int8_t)aBlendMethod; 1.894 +} 1.895 + 1.896 +// This can be called from any thread. 1.897 +bool imgFrame::ImageComplete() const 1.898 +{ 1.899 + MutexAutoLock lock(mDirtyMutex); 1.900 + 1.901 + return mDecoded.IsEqualInterior(nsIntRect(mOffset, mSize)); 1.902 +} 1.903 + 1.904 +// A hint from the image decoders that this image has no alpha, even 1.905 +// though we created is ARGB32. This changes our format to RGB24, 1.906 +// which in turn will cause us to Optimize() to RGB24. Has no effect 1.907 +// after Optimize() is called, though in all cases it will be just a 1.908 +// performance win -- the pixels are still correct and have the A byte 1.909 +// set to 0xff. 1.910 +void imgFrame::SetHasNoAlpha() 1.911 +{ 1.912 + if (mFormat == gfxImageFormat::ARGB32) { 1.913 + mFormat = gfxImageFormat::RGB24; 1.914 + mFormatChanged = true; 1.915 + ThebesSurface()->SetOpaqueRect(gfxRect(0, 0, mSize.width, mSize.height)); 1.916 + } 1.917 +} 1.918 + 1.919 +void imgFrame::SetAsNonPremult(bool aIsNonPremult) 1.920 +{ 1.921 + mNonPremult = aIsNonPremult; 1.922 +} 1.923 + 1.924 +bool imgFrame::GetCompositingFailed() const 1.925 +{ 1.926 + return mCompositingFailed; 1.927 +} 1.928 + 1.929 +void imgFrame::SetCompositingFailed(bool val) 1.930 +{ 1.931 + mCompositingFailed = val; 1.932 +} 1.933 + 1.934 +// If |aLocation| indicates this is heap memory, we try to measure things with 1.935 +// |aMallocSizeOf|. If that fails (because the platform doesn't support it) or 1.936 +// it's non-heap memory, we fall back to computing the size analytically. 1.937 +size_t 1.938 +imgFrame::SizeOfExcludingThisWithComputedFallbackIfHeap(gfxMemoryLocation aLocation, mozilla::MallocSizeOf aMallocSizeOf) const 1.939 +{ 1.940 + // aMallocSizeOf is only used if aLocation==gfxMemoryLocation::IN_PROCESS_HEAP. It 1.941 + // should be nullptr otherwise. 1.942 + NS_ABORT_IF_FALSE( 1.943 + (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP && aMallocSizeOf) || 1.944 + (aLocation != gfxMemoryLocation::IN_PROCESS_HEAP && !aMallocSizeOf), 1.945 + "mismatch between aLocation and aMallocSizeOf"); 1.946 + 1.947 + size_t n = 0; 1.948 + 1.949 + if (mPalettedImageData && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) { 1.950 + size_t n2 = aMallocSizeOf(mPalettedImageData); 1.951 + if (n2 == 0) { 1.952 + n2 = GetImageDataLength() + PaletteDataLength(); 1.953 + } 1.954 + n += n2; 1.955 + } 1.956 + 1.957 +#ifdef USE_WIN_SURFACE 1.958 + if (mWinSurface && aLocation == mWinSurface->GetMemoryLocation()) { 1.959 + n += mWinSurface->KnownMemoryUsed(); 1.960 + } else 1.961 +#endif 1.962 +#ifdef XP_MACOSX 1.963 + if (mQuartzSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) { 1.964 + n += aMallocSizeOf(mQuartzSurface); 1.965 + } 1.966 +#endif 1.967 + if (mImageSurface && aLocation == mImageSurface->GetMemoryLocation()) { 1.968 + size_t n2 = 0; 1.969 + if (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) { // HEAP: measure 1.970 + n2 = mImageSurface->SizeOfIncludingThis(aMallocSizeOf); 1.971 + } 1.972 + if (n2 == 0) { // non-HEAP or computed fallback for HEAP 1.973 + n2 = mImageSurface->KnownMemoryUsed(); 1.974 + } 1.975 + n += n2; 1.976 + } 1.977 + 1.978 + if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) { 1.979 + n += aMallocSizeOf(mVBuf); 1.980 + n += mVBuf->HeapSizeOfExcludingThis(aMallocSizeOf); 1.981 + } 1.982 + 1.983 + if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_NONHEAP) { 1.984 + n += mVBuf->NonHeapSizeOfExcludingThis(); 1.985 + } 1.986 + 1.987 + if (mOptSurface && aLocation == mOptSurface->GetMemoryLocation()) { 1.988 + size_t n2 = 0; 1.989 + if (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP && 1.990 + mOptSurface->SizeOfIsMeasured()) { 1.991 + // HEAP: measure (but only if the sub-class is capable of measuring) 1.992 + n2 = mOptSurface->SizeOfIncludingThis(aMallocSizeOf); 1.993 + } 1.994 + if (n2 == 0) { // non-HEAP or computed fallback for HEAP 1.995 + n2 = mOptSurface->KnownMemoryUsed(); 1.996 + } 1.997 + n += n2; 1.998 + } 1.999 + 1.1000 + return n; 1.1001 +}