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

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

mercurial