/image/src/imgFrame.cpp

http://github.com/zpao/v8monkey · C++ · 793 lines · 532 code · 118 blank · 143 comment · 127 complexity · 7fb592f61c6e71908d0d79a1cc692fe1 MD5 · raw file

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2. *
  3. * ***** BEGIN LICENSE BLOCK *****
  4. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  5. *
  6. * The contents of this file are subject to the Mozilla Public License Version
  7. * 1.1 (the "License"); you may not use this file except in compliance with
  8. * the License. You may obtain a copy of the License at
  9. * http://www.mozilla.org/MPL/
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. *
  16. * The Original Code is mozilla.org code.
  17. *
  18. * The Initial Developer of the Original Code is Mozilla Foundation.
  19. * Portions created by the Initial Developer are Copyright (C) 2009
  20. * the Initial Developer. All Rights Reserved.
  21. *
  22. * Contributor(s):
  23. * Joe Drew <joe@drew.ca> (original author)
  24. *
  25. * Alternatively, the contents of this file may be used under the terms of
  26. * either of the GNU General Public License Version 2 or later (the "GPL"),
  27. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28. * in which case the provisions of the GPL or the LGPL are applicable instead
  29. * of those above. If you wish to allow use of your version of this file only
  30. * under the terms of either the GPL or the LGPL, and not to allow others to
  31. * use your version of this file under the terms of the MPL, indicate your
  32. * decision by deleting the provisions above and replace them with the notice
  33. * and other provisions required by the GPL or the LGPL. If you do not delete
  34. * the provisions above, a recipient may use your version of this file under
  35. * the terms of any one of the MPL, the GPL or the LGPL.
  36. *
  37. * ***** END LICENSE BLOCK ***** */
  38. #include "imgFrame.h"
  39. #include <limits.h>
  40. #include "prmem.h"
  41. #include "prenv.h"
  42. #include "gfxPlatform.h"
  43. #include "gfxUtils.h"
  44. static bool gDisableOptimize = false;
  45. #include "cairo.h"
  46. #include "sampler.h"
  47. #if defined(XP_WIN)
  48. #include "gfxWindowsPlatform.h"
  49. /* Whether to use the windows surface; only for desktop win32 */
  50. #define USE_WIN_SURFACE 1
  51. static PRUint32 gTotalDDBs = 0;
  52. static PRUint32 gTotalDDBSize = 0;
  53. // only use up a maximum of 64MB in DDBs
  54. #define kMaxDDBSize (64*1024*1024)
  55. // and don't let anything in that's bigger than 4MB
  56. #define kMaxSingleDDBSize (4*1024*1024)
  57. #endif
  58. // Returns true if an image of aWidth x aHeight is allowed and legal.
  59. static bool AllowedImageSize(PRInt32 aWidth, PRInt32 aHeight)
  60. {
  61. // reject over-wide or over-tall images
  62. const PRInt32 k64KLimit = 0x0000FFFF;
  63. if (NS_UNLIKELY(aWidth > k64KLimit || aHeight > k64KLimit )) {
  64. NS_WARNING("image too big");
  65. return false;
  66. }
  67. // protect against invalid sizes
  68. if (NS_UNLIKELY(aHeight <= 0 || aWidth <= 0)) {
  69. return false;
  70. }
  71. // check to make sure we don't overflow a 32-bit
  72. PRInt32 tmp = aWidth * aHeight;
  73. if (NS_UNLIKELY(tmp / aHeight != aWidth)) {
  74. NS_WARNING("width or height too large");
  75. return false;
  76. }
  77. tmp = tmp * 4;
  78. if (NS_UNLIKELY(tmp / 4 != aWidth * aHeight)) {
  79. NS_WARNING("width or height too large");
  80. return false;
  81. }
  82. #if defined(XP_MACOSX)
  83. // CoreGraphics is limited to images < 32K in *height*, so clamp all surfaces on the Mac to that height
  84. if (NS_UNLIKELY(aHeight > SHRT_MAX)) {
  85. NS_WARNING("image too big");
  86. return false;
  87. }
  88. #endif
  89. return true;
  90. }
  91. // Returns whether we should, at this time, use image surfaces instead of
  92. // optimized platform-specific surfaces.
  93. static bool ShouldUseImageSurfaces()
  94. {
  95. #if defined(USE_WIN_SURFACE)
  96. static const DWORD kGDIObjectsHighWaterMark = 7000;
  97. if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
  98. gfxWindowsPlatform::RENDER_DIRECT2D) {
  99. return true;
  100. }
  101. // at 7000 GDI objects, stop allocating normal images to make sure
  102. // we never hit the 10k hard limit.
  103. // GetCurrentProcess() just returns (HANDLE)-1, it's inlined afaik
  104. DWORD count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
  105. if (count == 0 ||
  106. count > kGDIObjectsHighWaterMark)
  107. {
  108. // either something's broken (count == 0),
  109. // or we hit our high water mark; disable
  110. // image allocations for a bit.
  111. return true;
  112. }
  113. #endif
  114. return false;
  115. }
  116. imgFrame::imgFrame() :
  117. mDecoded(0, 0, 0, 0),
  118. mPalettedImageData(nsnull),
  119. mSinglePixelColor(0),
  120. mTimeout(100),
  121. mDisposalMethod(0), /* imgIContainer::kDisposeNotSpecified */
  122. mBlendMethod(1), /* imgIContainer::kBlendOver */
  123. mSinglePixel(false),
  124. mNeverUseDeviceSurface(false),
  125. mFormatChanged(false),
  126. mCompositingFailed(false)
  127. #ifdef USE_WIN_SURFACE
  128. , mIsDDBSurface(false)
  129. #endif
  130. , mLocked(false)
  131. {
  132. static bool hasCheckedOptimize = false;
  133. if (!hasCheckedOptimize) {
  134. if (PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")) {
  135. gDisableOptimize = true;
  136. }
  137. hasCheckedOptimize = true;
  138. }
  139. }
  140. imgFrame::~imgFrame()
  141. {
  142. PR_FREEIF(mPalettedImageData);
  143. #ifdef USE_WIN_SURFACE
  144. if (mIsDDBSurface) {
  145. gTotalDDBs--;
  146. gTotalDDBSize -= mSize.width * mSize.height * 4;
  147. }
  148. #endif
  149. }
  150. nsresult imgFrame::Init(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight,
  151. gfxASurface::gfxImageFormat aFormat, PRUint8 aPaletteDepth /* = 0 */)
  152. {
  153. // assert for properties that should be verified by decoders, warn for properties related to bad content
  154. if (!AllowedImageSize(aWidth, aHeight))
  155. return NS_ERROR_FAILURE;
  156. mOffset.MoveTo(aX, aY);
  157. mSize.SizeTo(aWidth, aHeight);
  158. mFormat = aFormat;
  159. mPaletteDepth = aPaletteDepth;
  160. if (aPaletteDepth != 0) {
  161. // We're creating for a paletted image.
  162. if (aPaletteDepth > 8) {
  163. NS_ERROR("This Depth is not supported");
  164. return NS_ERROR_FAILURE;
  165. }
  166. // Use the fallible allocator here
  167. mPalettedImageData = (PRUint8*)moz_malloc(PaletteDataLength() + GetImageDataLength());
  168. NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
  169. } else {
  170. // For Windows, we must create the device surface first (if we're
  171. // going to) so that the image surface can wrap it. Can't be done
  172. // the other way around.
  173. #ifdef USE_WIN_SURFACE
  174. if (!mNeverUseDeviceSurface && !ShouldUseImageSurfaces()) {
  175. mWinSurface = new gfxWindowsSurface(gfxIntSize(mSize.width, mSize.height), mFormat);
  176. if (mWinSurface && mWinSurface->CairoStatus() == 0) {
  177. // no error
  178. mImageSurface = mWinSurface->GetAsImageSurface();
  179. } else {
  180. mWinSurface = nsnull;
  181. }
  182. }
  183. #endif
  184. // For other platforms we create the image surface first and then
  185. // possibly wrap it in a device surface. This branch is also used
  186. // on Windows if we're not using device surfaces or if we couldn't
  187. // create one.
  188. if (!mImageSurface)
  189. mImageSurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height), mFormat);
  190. if (!mImageSurface || mImageSurface->CairoStatus()) {
  191. mImageSurface = nsnull;
  192. // guess
  193. return NS_ERROR_OUT_OF_MEMORY;
  194. }
  195. #ifdef XP_MACOSX
  196. if (!mNeverUseDeviceSurface && !ShouldUseImageSurfaces()) {
  197. mQuartzSurface = new gfxQuartzImageSurface(mImageSurface);
  198. }
  199. #endif
  200. }
  201. return NS_OK;
  202. }
  203. nsresult imgFrame::Optimize()
  204. {
  205. if (gDisableOptimize)
  206. return NS_OK;
  207. if (mPalettedImageData || mOptSurface || mSinglePixel)
  208. return NS_OK;
  209. /* Figure out if the entire image is a constant color */
  210. // this should always be true
  211. if (mImageSurface->Stride() == mSize.width * 4) {
  212. PRUint32 *imgData = (PRUint32*) mImageSurface->Data();
  213. PRUint32 firstPixel = * (PRUint32*) imgData;
  214. PRUint32 pixelCount = mSize.width * mSize.height + 1;
  215. while (--pixelCount && *imgData++ == firstPixel)
  216. ;
  217. if (pixelCount == 0) {
  218. // all pixels were the same
  219. if (mFormat == gfxASurface::ImageFormatARGB32 ||
  220. mFormat == gfxASurface::ImageFormatRGB24)
  221. {
  222. mSinglePixelColor = gfxRGBA
  223. (firstPixel,
  224. (mFormat == gfxImageSurface::ImageFormatRGB24 ?
  225. gfxRGBA::PACKED_XRGB :
  226. gfxRGBA::PACKED_ARGB_PREMULTIPLIED));
  227. mSinglePixel = true;
  228. // blow away the older surfaces (if they exist), to release their memory
  229. mImageSurface = nsnull;
  230. mOptSurface = nsnull;
  231. #ifdef USE_WIN_SURFACE
  232. mWinSurface = nsnull;
  233. #endif
  234. #ifdef XP_MACOSX
  235. mQuartzSurface = nsnull;
  236. #endif
  237. return NS_OK;
  238. }
  239. }
  240. // if it's not RGB24/ARGB32, don't optimize, but we never hit this at the moment
  241. }
  242. // if we're being forced to use image surfaces due to
  243. // resource constraints, don't try to optimize beyond same-pixel.
  244. if (mNeverUseDeviceSurface || ShouldUseImageSurfaces())
  245. return NS_OK;
  246. mOptSurface = nsnull;
  247. #ifdef USE_WIN_SURFACE
  248. // we need to special-case windows here, because windows has
  249. // a distinction between DIB and DDB and we want to use DDBs as much
  250. // as we can.
  251. if (mWinSurface) {
  252. // Don't do DDBs for large images; see bug 359147
  253. // Note that we bother with DDBs at all because they are much faster
  254. // on some systems; on others there isn't much of a speed difference
  255. // between DIBs and DDBs.
  256. //
  257. // Originally this just limited to 1024x1024; but that still
  258. // had us hitting overall total memory usage limits (which was
  259. // around 220MB on my intel shared memory system with 2GB RAM
  260. // and 16-128mb in use by the video card, so I can't make
  261. // heads or tails out of this limit).
  262. //
  263. // So instead, we clamp the max size to 64MB (this limit shuld
  264. // be made dynamic based on.. something.. as soon a we figure
  265. // out that something) and also limit each individual image to
  266. // be less than 4MB to keep very large images out of DDBs.
  267. // assume (almost -- we don't quadword-align) worst-case size
  268. PRUint32 ddbSize = mSize.width * mSize.height * 4;
  269. if (ddbSize <= kMaxSingleDDBSize &&
  270. ddbSize + gTotalDDBSize <= kMaxDDBSize)
  271. {
  272. nsRefPtr<gfxWindowsSurface> wsurf = mWinSurface->OptimizeToDDB(nsnull, gfxIntSize(mSize.width, mSize.height), mFormat);
  273. if (wsurf) {
  274. gTotalDDBs++;
  275. gTotalDDBSize += ddbSize;
  276. mIsDDBSurface = true;
  277. mOptSurface = wsurf;
  278. }
  279. }
  280. if (!mOptSurface && !mFormatChanged) {
  281. // just use the DIB if the format has not changed
  282. mOptSurface = mWinSurface;
  283. }
  284. }
  285. #endif
  286. #ifdef XP_MACOSX
  287. if (mQuartzSurface) {
  288. mQuartzSurface->Flush();
  289. mOptSurface = mQuartzSurface;
  290. }
  291. #endif
  292. if (mOptSurface == nsnull)
  293. mOptSurface = gfxPlatform::GetPlatform()->OptimizeImage(mImageSurface, mFormat);
  294. if (mOptSurface) {
  295. mImageSurface = nsnull;
  296. #ifdef USE_WIN_SURFACE
  297. mWinSurface = nsnull;
  298. #endif
  299. #ifdef XP_MACOSX
  300. mQuartzSurface = nsnull;
  301. #endif
  302. }
  303. return NS_OK;
  304. }
  305. static void
  306. DoSingleColorFastPath(gfxContext* aContext,
  307. const gfxRGBA& aSinglePixelColor,
  308. const gfxRect& aFill)
  309. {
  310. // if a == 0, it's a noop
  311. if (aSinglePixelColor.a == 0.0)
  312. return;
  313. gfxContext::GraphicsOperator op = aContext->CurrentOperator();
  314. if (op == gfxContext::OPERATOR_OVER && aSinglePixelColor.a == 1.0) {
  315. aContext->SetOperator(gfxContext::OPERATOR_SOURCE);
  316. }
  317. aContext->SetDeviceColor(aSinglePixelColor);
  318. aContext->NewPath();
  319. aContext->Rectangle(aFill);
  320. aContext->Fill();
  321. aContext->SetOperator(op);
  322. aContext->SetDeviceColor(gfxRGBA(0,0,0,0));
  323. }
  324. imgFrame::SurfaceWithFormat
  325. imgFrame::SurfaceForDrawing(bool aDoPadding,
  326. bool aDoPartialDecode,
  327. bool aDoTile,
  328. const nsIntMargin& aPadding,
  329. gfxMatrix& aUserSpaceToImageSpace,
  330. gfxRect& aFill,
  331. gfxRect& aSubimage,
  332. gfxRect& aSourceRect,
  333. gfxRect& aImageRect)
  334. {
  335. gfxIntSize size(PRInt32(aImageRect.Width()), PRInt32(aImageRect.Height()));
  336. if (!aDoPadding && !aDoPartialDecode) {
  337. NS_ASSERTION(!mSinglePixel, "This should already have been handled");
  338. return SurfaceWithFormat(new gfxSurfaceDrawable(ThebesSurface(), size), mFormat);
  339. }
  340. gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width, mDecoded.height);
  341. if (aDoTile || mSinglePixel) {
  342. // Create a temporary surface.
  343. // Give this surface an alpha channel because there are
  344. // transparent pixels in the padding or undecoded area
  345. gfxImageSurface::gfxImageFormat format = gfxASurface::ImageFormatARGB32;
  346. nsRefPtr<gfxASurface> surface =
  347. gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, gfxImageSurface::ContentFromFormat(format));
  348. if (!surface || surface->CairoStatus())
  349. return SurfaceWithFormat();
  350. // Fill 'available' with whatever we've got
  351. gfxContext tmpCtx(surface);
  352. tmpCtx.SetOperator(gfxContext::OPERATOR_SOURCE);
  353. if (mSinglePixel) {
  354. tmpCtx.SetDeviceColor(mSinglePixelColor);
  355. } else {
  356. tmpCtx.SetSource(ThebesSurface(), gfxPoint(aPadding.left, aPadding.top));
  357. }
  358. tmpCtx.Rectangle(available);
  359. tmpCtx.Fill();
  360. return SurfaceWithFormat(new gfxSurfaceDrawable(surface, size), format);
  361. }
  362. // Not tiling, and we have a surface, so we can account for
  363. // padding and/or a partial decode just by twiddling parameters.
  364. // First, update our user-space fill rect.
  365. aSourceRect = aSourceRect.Intersect(available);
  366. gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
  367. imageSpaceToUserSpace.Invert();
  368. aFill = imageSpaceToUserSpace.Transform(aSourceRect);
  369. aSubimage = aSubimage.Intersect(available) - gfxPoint(aPadding.left, aPadding.top);
  370. aUserSpaceToImageSpace.Multiply(gfxMatrix().Translate(-gfxPoint(aPadding.left, aPadding.top)));
  371. aSourceRect = aSourceRect - gfxPoint(aPadding.left, aPadding.top);
  372. aImageRect = gfxRect(0, 0, mSize.width, mSize.height);
  373. gfxIntSize availableSize(mDecoded.width, mDecoded.height);
  374. return SurfaceWithFormat(new gfxSurfaceDrawable(ThebesSurface(),
  375. availableSize),
  376. mFormat);
  377. }
  378. void imgFrame::Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter,
  379. const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
  380. const nsIntMargin &aPadding, const nsIntRect &aSubimage)
  381. {
  382. SAMPLE_LABEL("image", "imgFrame::Draw");
  383. NS_ASSERTION(!aFill.IsEmpty(), "zero dest size --- fix caller");
  384. NS_ASSERTION(!aSubimage.IsEmpty(), "zero source size --- fix caller");
  385. NS_ASSERTION(!mPalettedImageData, "Directly drawing a paletted image!");
  386. bool doPadding = aPadding != nsIntMargin(0,0,0,0);
  387. bool doPartialDecode = !ImageComplete();
  388. if (mSinglePixel && !doPadding && !doPartialDecode) {
  389. DoSingleColorFastPath(aContext, mSinglePixelColor, aFill);
  390. return;
  391. }
  392. gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
  393. gfxRect sourceRect = userSpaceToImageSpace.Transform(aFill);
  394. gfxRect imageRect(0, 0, mSize.width + aPadding.LeftRight(),
  395. mSize.height + aPadding.TopBottom());
  396. gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
  397. gfxRect fill = aFill;
  398. NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(),
  399. "We must be allowed to sample *some* source pixels!");
  400. bool doTile = !imageRect.Contains(sourceRect);
  401. SurfaceWithFormat surfaceResult =
  402. SurfaceForDrawing(doPadding, doPartialDecode, doTile, aPadding,
  403. userSpaceToImageSpace, fill, subimage, sourceRect,
  404. imageRect);
  405. if (surfaceResult.IsValid()) {
  406. gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
  407. userSpaceToImageSpace,
  408. subimage, sourceRect, imageRect, fill,
  409. surfaceResult.mFormat, aFilter);
  410. }
  411. }
  412. nsresult imgFrame::Extract(const nsIntRect& aRegion, imgFrame** aResult)
  413. {
  414. nsAutoPtr<imgFrame> subImage(new imgFrame());
  415. // The scaling problems described in bug 468496 are especially
  416. // likely to be visible for the sub-image, as at present the only
  417. // user is the border-image code and border-images tend to get
  418. // stretched a lot. At the same time, the performance concerns
  419. // that prevent us from just using Cairo's fallback scaler when
  420. // accelerated graphics won't cut it are less relevant to such
  421. // images, since they also tend to be small. Thus, we forcibly
  422. // disable the use of anything other than a client-side image
  423. // surface for the sub-image; this ensures that the correct
  424. // (albeit slower) Cairo fallback scaler will be used.
  425. subImage->mNeverUseDeviceSurface = true;
  426. nsresult rv = subImage->Init(0, 0, aRegion.width, aRegion.height,
  427. mFormat, mPaletteDepth);
  428. NS_ENSURE_SUCCESS(rv, rv);
  429. // scope to destroy ctx
  430. {
  431. gfxContext ctx(subImage->ThebesSurface());
  432. ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
  433. if (mSinglePixel) {
  434. ctx.SetDeviceColor(mSinglePixelColor);
  435. } else {
  436. // SetSource() places point (0,0) of its first argument at
  437. // the coordinages given by its second argument. We want
  438. // (x,y) of the image to be (0,0) of source space, so we
  439. // put (0,0) of the image at (-x,-y).
  440. ctx.SetSource(this->ThebesSurface(), gfxPoint(-aRegion.x, -aRegion.y));
  441. }
  442. ctx.Rectangle(gfxRect(0, 0, aRegion.width, aRegion.height));
  443. ctx.Fill();
  444. }
  445. nsIntRect filled(0, 0, aRegion.width, aRegion.height);
  446. rv = subImage->ImageUpdated(filled);
  447. NS_ENSURE_SUCCESS(rv, rv);
  448. subImage->Optimize();
  449. *aResult = subImage.forget();
  450. return NS_OK;
  451. }
  452. nsresult imgFrame::ImageUpdated(const nsIntRect &aUpdateRect)
  453. {
  454. mDecoded.UnionRect(mDecoded, aUpdateRect);
  455. // clamp to bounds, in case someone sends a bogus updateRect (I'm looking at
  456. // you, gif decoder)
  457. nsIntRect boundsRect(mOffset, mSize);
  458. mDecoded.IntersectRect(mDecoded, boundsRect);
  459. #ifdef XP_MACOSX
  460. if (mQuartzSurface)
  461. mQuartzSurface->Flush();
  462. #endif
  463. return NS_OK;
  464. }
  465. nsIntRect imgFrame::GetRect() const
  466. {
  467. return nsIntRect(mOffset, mSize);
  468. }
  469. gfxASurface::gfxImageFormat imgFrame::GetFormat() const
  470. {
  471. return mFormat;
  472. }
  473. bool imgFrame::GetNeedsBackground() const
  474. {
  475. // We need a background painted if we have alpha or we're incomplete.
  476. return (mFormat == gfxASurface::ImageFormatARGB32 || !ImageComplete());
  477. }
  478. PRUint32 imgFrame::GetImageBytesPerRow() const
  479. {
  480. if (mImageSurface)
  481. return mImageSurface->Stride();
  482. if (mPaletteDepth)
  483. return mSize.width;
  484. NS_ERROR("GetImageBytesPerRow called with mImageSurface == null and mPaletteDepth == 0");
  485. return 0;
  486. }
  487. PRUint32 imgFrame::GetImageDataLength() const
  488. {
  489. return GetImageBytesPerRow() * mSize.height;
  490. }
  491. void imgFrame::GetImageData(PRUint8 **aData, PRUint32 *length) const
  492. {
  493. if (mImageSurface)
  494. *aData = mImageSurface->Data();
  495. else if (mPalettedImageData)
  496. *aData = mPalettedImageData + PaletteDataLength();
  497. else
  498. *aData = nsnull;
  499. *length = GetImageDataLength();
  500. }
  501. bool imgFrame::GetIsPaletted() const
  502. {
  503. return mPalettedImageData != nsnull;
  504. }
  505. bool imgFrame::GetHasAlpha() const
  506. {
  507. return mFormat == gfxASurface::ImageFormatARGB32;
  508. }
  509. void imgFrame::GetPaletteData(PRUint32 **aPalette, PRUint32 *length) const
  510. {
  511. if (!mPalettedImageData) {
  512. *aPalette = nsnull;
  513. *length = 0;
  514. } else {
  515. *aPalette = (PRUint32 *) mPalettedImageData;
  516. *length = PaletteDataLength();
  517. }
  518. }
  519. nsresult imgFrame::LockImageData()
  520. {
  521. if (mPalettedImageData)
  522. return NS_ERROR_NOT_AVAILABLE;
  523. NS_ABORT_IF_FALSE(!mLocked, "Trying to lock already locked image data.");
  524. if (mLocked) {
  525. return NS_ERROR_FAILURE;
  526. }
  527. mLocked = true;
  528. if ((mOptSurface || mSinglePixel) && !mImageSurface) {
  529. // Recover the pixels
  530. mImageSurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height),
  531. gfxImageSurface::ImageFormatARGB32);
  532. if (!mImageSurface || mImageSurface->CairoStatus())
  533. return NS_ERROR_OUT_OF_MEMORY;
  534. gfxContext context(mImageSurface);
  535. context.SetOperator(gfxContext::OPERATOR_SOURCE);
  536. if (mSinglePixel)
  537. context.SetDeviceColor(mSinglePixelColor);
  538. else
  539. context.SetSource(mOptSurface);
  540. context.Paint();
  541. mOptSurface = nsnull;
  542. #ifdef USE_WIN_SURFACE
  543. mWinSurface = nsnull;
  544. #endif
  545. #ifdef XP_MACOSX
  546. mQuartzSurface = nsnull;
  547. #endif
  548. }
  549. // We might write to the bits in this image surface, so we need to make the
  550. // surface ready for that.
  551. if (mImageSurface)
  552. mImageSurface->Flush();
  553. #ifdef USE_WIN_SURFACE
  554. if (mWinSurface)
  555. mWinSurface->Flush();
  556. #endif
  557. return NS_OK;
  558. }
  559. nsresult imgFrame::UnlockImageData()
  560. {
  561. if (mPalettedImageData)
  562. return NS_ERROR_NOT_AVAILABLE;
  563. NS_ABORT_IF_FALSE(mLocked, "Unlocking an unlocked image!");
  564. if (!mLocked) {
  565. return NS_ERROR_FAILURE;
  566. }
  567. mLocked = false;
  568. // Assume we've been written to.
  569. if (mImageSurface)
  570. mImageSurface->MarkDirty();
  571. #ifdef USE_WIN_SURFACE
  572. if (mWinSurface)
  573. mWinSurface->MarkDirty();
  574. #endif
  575. #ifdef XP_MACOSX
  576. // The quartz image surface (ab)uses the flush method to get the
  577. // cairo_image_surface data into a CGImage, so we have to call Flush() here.
  578. if (mQuartzSurface)
  579. mQuartzSurface->Flush();
  580. #endif
  581. return NS_OK;
  582. }
  583. PRInt32 imgFrame::GetTimeout() const
  584. {
  585. // Ensure a minimal time between updates so we don't throttle the UI thread.
  586. // consider 0 == unspecified and make it fast but not too fast. See bug
  587. // 125137, bug 139677, and bug 207059. The behavior of recent IE and Opera
  588. // versions seems to be:
  589. // IE 6/Win:
  590. // 10 - 50ms go 100ms
  591. // >50ms go correct speed
  592. // Opera 7 final/Win:
  593. // 10ms goes 100ms
  594. // >10ms go correct speed
  595. // It seems that there are broken tools out there that set a 0ms or 10ms
  596. // timeout when they really want a "default" one. So munge values in that
  597. // range.
  598. if (mTimeout >= 0 && mTimeout <= 10)
  599. return 100;
  600. else
  601. return mTimeout;
  602. }
  603. void imgFrame::SetTimeout(PRInt32 aTimeout)
  604. {
  605. mTimeout = aTimeout;
  606. }
  607. PRInt32 imgFrame::GetFrameDisposalMethod() const
  608. {
  609. return mDisposalMethod;
  610. }
  611. void imgFrame::SetFrameDisposalMethod(PRInt32 aFrameDisposalMethod)
  612. {
  613. mDisposalMethod = aFrameDisposalMethod;
  614. }
  615. PRInt32 imgFrame::GetBlendMethod() const
  616. {
  617. return mBlendMethod;
  618. }
  619. void imgFrame::SetBlendMethod(PRInt32 aBlendMethod)
  620. {
  621. mBlendMethod = (PRInt8)aBlendMethod;
  622. }
  623. bool imgFrame::ImageComplete() const
  624. {
  625. return mDecoded.IsEqualInterior(nsIntRect(mOffset, mSize));
  626. }
  627. // A hint from the image decoders that this image has no alpha, even
  628. // though we created is ARGB32. This changes our format to RGB24,
  629. // which in turn will cause us to Optimize() to RGB24. Has no effect
  630. // after Optimize() is called, though in all cases it will be just a
  631. // performance win -- the pixels are still correct and have the A byte
  632. // set to 0xff.
  633. void imgFrame::SetHasNoAlpha()
  634. {
  635. if (mFormat == gfxASurface::ImageFormatARGB32) {
  636. mFormat = gfxASurface::ImageFormatRGB24;
  637. mFormatChanged = true;
  638. }
  639. }
  640. bool imgFrame::GetCompositingFailed() const
  641. {
  642. return mCompositingFailed;
  643. }
  644. void imgFrame::SetCompositingFailed(bool val)
  645. {
  646. mCompositingFailed = val;
  647. }
  648. PRUint32
  649. imgFrame::EstimateMemoryUsed(gfxASurface::MemoryLocation aLocation) const
  650. {
  651. PRUint32 size = 0;
  652. if (mSinglePixel && aLocation == gfxASurface::MEMORY_IN_PROCESS_HEAP) {
  653. size += sizeof(gfxRGBA);
  654. }
  655. if (mPalettedImageData && aLocation == gfxASurface::MEMORY_IN_PROCESS_HEAP) {
  656. size += GetImageDataLength() + PaletteDataLength();
  657. }
  658. #ifdef USE_WIN_SURFACE
  659. if (mWinSurface && aLocation == mWinSurface->GetMemoryLocation()) {
  660. size += mWinSurface->KnownMemoryUsed();
  661. } else
  662. #endif
  663. #ifdef XP_MACOSX
  664. if (mQuartzSurface && aLocation == gfxASurface::MEMORY_IN_PROCESS_HEAP) {
  665. size += mSize.width * mSize.height * 4;
  666. } else
  667. #endif
  668. if (mImageSurface && aLocation == mImageSurface->GetMemoryLocation()) {
  669. size += mImageSurface->KnownMemoryUsed();
  670. }
  671. if (mOptSurface && aLocation == mOptSurface->GetMemoryLocation()) {
  672. size += mOptSurface->KnownMemoryUsed();
  673. }
  674. return size;
  675. }