Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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 | } |