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