Bug 435296 - Decode-On-Draw. r=joe,roc,bz,dolske,peterw sr=vlad

This commit is contained in:
Bobby Holley 2009-09-12 16:44:18 -06:00
parent 17eb5a6efc
commit 2d5ec55945
71 changed files with 3232 additions and 1514 deletions

View File

@ -524,7 +524,9 @@ static nsresult
WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
{
nsRefPtr<gfxImageSurface> image;
nsresult rv = aImage->CopyCurrentFrame(getter_AddRefs(image));
nsresult rv = aImage->CopyFrame(imgIContainer::FRAME_FIRST,
imgIContainer::FLAG_SYNC_DECODE,
getter_AddRefs(image));
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 width = image->Width();

View File

@ -268,6 +268,14 @@ nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest, PRBool aLastPart)
return NS_OK;
}
NS_IMETHODIMP
nsImageLoadingContent::OnDiscard(imgIRequest *aRequest)
{
LOOP_OVER_OBSERVERS(OnDiscard(aRequest));
return NS_OK;
}
/*
* nsIImageLoadingContent impl
*/

View File

@ -100,6 +100,12 @@ nsStubImageDecoderObserver::OnStopRequest(imgIRequest *aRequest,
return NS_OK;
}
NS_IMETHODIMP
nsStubImageDecoderObserver::OnDiscard(imgIRequest *aRequest)
{
return NS_OK;
}
NS_IMETHODIMP
nsStubImageDecoderObserver::FrameChanged(imgIContainer *aContainer,
nsIntRect * aDirtyRect)

View File

@ -1329,8 +1329,11 @@ nsCanvasRenderingContext2D::CreatePattern(nsIDOMHTMLElement *image,
return NS_ERROR_DOM_SYNTAX_ERR;
}
// The canvas spec says that createPattern should use the first frame
// of animated images
nsLayoutUtils::SurfaceFromElementResult res =
nsLayoutUtils::SurfaceFromElement(image, nsLayoutUtils::SFE_WANT_NEW_SURFACE);
nsLayoutUtils::SurfaceFromElement(image, nsLayoutUtils::SFE_WANT_FIRST_FRAME |
nsLayoutUtils::SFE_WANT_NEW_SURFACE);
if (!res.mSurface)
return NS_ERROR_NOT_AVAILABLE;
@ -2965,8 +2968,11 @@ nsCanvasRenderingContext2D::DrawImage()
nsRefPtr<gfxPattern> pattern;
nsRefPtr<gfxPath> path;
// The canvas spec says that drawImage should draw the first frame
// of animated images
PRUint32 sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME;
nsLayoutUtils::SurfaceFromElementResult res =
nsLayoutUtils::SurfaceFromElement(imgElt);
nsLayoutUtils::SurfaceFromElement(imgElt, sfeFlags);
if (!res.mSurface)
return NS_ERROR_NOT_AVAILABLE;
@ -2974,7 +2980,8 @@ nsCanvasRenderingContext2D::DrawImage()
// On non-CE, force a copy if we're using drawImage with our destination
// as a source to work around some Cairo self-copy semantics issues.
if (res.mSurface == mSurface) {
res = nsLayoutUtils::SurfaceFromElement(imgElt, nsLayoutUtils::SFE_WANT_NEW_SURFACE);
sfeFlags |= nsLayoutUtils::SFE_WANT_NEW_SURFACE;
res = nsLayoutUtils::SurfaceFromElement(imgElt, sfeFlags);
if (!res.mSurface)
return NS_ERROR_NOT_AVAILABLE;
}

View File

@ -2831,7 +2831,7 @@ function test_2d_drawImage_animated_apng() {
deferTest();
setTimeout(wrapFunction(function () {
ctx108.drawImage(document.getElementById('anim-gr_1.png'), 0, 0);
todo_isPixel(ctx108, 50,25, 0,255,0,255, 2);
isPixel(ctx108, 50,25, 0,255,0,255, 2);
isDone_test_2d_drawImage_animated_apng = true;
}), 5000);
@ -2856,7 +2856,7 @@ function test_2d_drawImage_animated_gif() {
deferTest();
setTimeout(wrapFunction(function () {
ctx109.drawImage(document.getElementById('anim-gr_1.gif'), 0, 0);
todo_isPixel(ctx109, 50,25, 0,255,0,255, 2);
isPixel(ctx109, 50,25, 0,255,0,255, 2);
isDone_test_2d_drawImage_animated_gif = true;
}), 5000);
@ -14490,8 +14490,8 @@ setTimeout(function () {
ctx458.fillRect(0, 0, 50, 50);
setTimeout(wrapFunction(function () {
ctx458.fillRect(50, 0, 50, 50);
todo_isPixel(ctx458, 25,25, 0,255,0,255, 2);
todo_isPixel(ctx458, 75,25, 0,255,0,255, 2);
isPixel(ctx458, 25,25, 0,255,0,255, 2);
isPixel(ctx458, 75,25, 0,255,0,255, 2);
isDone_test_2d_pattern_animated_gif = true;
}), 2500);
}, 2500);

View File

@ -5393,7 +5393,9 @@ nsSVGFEImageElement::Filter(nsSVGFilterInstance *instance,
nsRefPtr<gfxASurface> currentFrame;
if (imageContainer)
imageContainer->GetCurrentFrame(getter_AddRefs(currentFrame));
imageContainer->GetFrame(imgIContainer::FRAME_CURRENT,
imgIContainer::FLAG_NONE,
getter_AddRefs(currentFrame));
// We need to wrap the surface in a pattern to have somewhere to set the
// graphics filter.
@ -5482,6 +5484,12 @@ nsSVGFEImageElement::OnStartContainer(imgIRequest *aRequest,
{
nsresult rv =
nsImageLoadingContent::OnStartContainer(aRequest, aContainer);
// Request a decode
NS_ABORT_IF_FALSE(aContainer, "who sent the notification then?");
aContainer->RequestDecode();
// We have a size - invalidate
Invalidate();
return rv;
}

View File

@ -2104,7 +2104,7 @@ DrawBorderImage(nsPresContext* aPresContext,
if (req)
req->GetImageStatus(&status);
NS_ASSERTION(req && (status & imgIRequest::STATUS_FRAME_COMPLETE),
NS_ASSERTION(req && (status & imgIRequest::STATUS_LOAD_COMPLETE),
"no image to draw");
}
#endif
@ -2300,7 +2300,9 @@ DrawBorderImageComponent(nsIRenderingContext& aRenderingContext,
return;
nsCOMPtr<imgIContainer> subImage;
if (NS_FAILED(aImage->ExtractCurrentFrame(aSrc, getter_AddRefs(subImage))))
if (NS_FAILED(aImage->ExtractFrame(imgIContainer::FRAME_CURRENT, aSrc,
imgIContainer::FLAG_SYNC_DECODE,
getter_AddRefs(subImage))))
return;
gfxPattern::GraphicsFilter graphicsFilter =
@ -2314,7 +2316,7 @@ DrawBorderImageComponent(nsIRenderingContext& aRenderingContext,
aUnitSize.height == aFill.height)) {
nsLayoutUtils::DrawSingleImage(&aRenderingContext, subImage,
graphicsFilter,
aFill, aDirtyRect);
aFill, aDirtyRect, imgIContainer::FLAG_NONE);
return;
}
@ -2359,7 +2361,8 @@ DrawBorderImageComponent(nsIRenderingContext& aRenderingContext,
}
nsLayoutUtils::DrawImage(&aRenderingContext, subImage, graphicsFilter,
tile, aFill, tile.TopLeft(), aDirtyRect);
tile, aFill, tile.TopLeft(), aDirtyRect,
imgIContainer::FLAG_NONE);
}
// Begin table border-collapsing section
@ -3086,6 +3089,9 @@ PRBool
ImageRenderer::PrepareImage()
{
if (mImage.IsEmpty() || !mImage.IsComplete()) {
// Make sure the image is actually decoding
mImage.RequestDecode();
// We can not prepare the image for rendering if it is not fully loaded.
return PR_FALSE;
}
@ -3115,8 +3121,10 @@ ImageRenderer::PrepareImage()
mImageContainer.swap(srcImage);
} else {
nsCOMPtr<imgIContainer> subImage;
nsresult rv = srcImage->ExtractCurrentFrame(actualCropRect,
getter_AddRefs(subImage));
nsresult rv = srcImage->ExtractFrame(imgIContainer::FRAME_CURRENT,
actualCropRect,
imgIContainer::FLAG_NONE,
getter_AddRefs(subImage));
if (NS_FAILED(rv)) {
NS_WARNING("The cropped image contains no pixels to draw; "
"maybe the crop rect is outside the image frame rect");
@ -3190,7 +3198,7 @@ ImageRenderer::Draw(nsPresContext* aPresContext,
case eStyleImageType_Image:
nsLayoutUtils::DrawImage(&aRenderingContext, mImageContainer,
nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame),
aDest, aFill, aAnchor, aDirty);
aDest, aFill, aAnchor, aDirty, imgIContainer::FLAG_NONE);
break;
case eStyleImageType_Gradient:
nsCSSRendering::PaintGradient(aPresContext, aRenderingContext,

View File

@ -73,7 +73,8 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mIsAtRootOfPseudoStackingContext(PR_FALSE),
mPaintAllFrames(PR_FALSE),
mAccurateVisibleRegions(PR_FALSE),
mInTransform(PR_FALSE) {
mInTransform(PR_FALSE),
mSyncDecodeImages(PR_FALSE) {
PL_InitArenaPool(&mPool, "displayListArena", 1024, sizeof(void*)-1);
nsPresContext* pc = aReferenceFrame->PresContext();

View File

@ -290,6 +290,20 @@ public:
*/
void SetInTransform(PRBool aInTransform) { mInTransform = aInTransform; }
/**
* @return PR_TRUE if images have been set to decode synchronously.
*/
PRBool ShouldSyncDecodeImages() { return mSyncDecodeImages; }
/**
* Indicates whether we should synchronously decode images. If true, we decode
* and draw whatever image data has been loaded. If false, we just draw
* whatever has already been decoded.
*/
void SetSyncDecodeImages(PRBool aSyncDecodeImages) {
mSyncDecodeImages = aSyncDecodeImages;
}
/**
* Subtracts aRegion from *aVisibleRegion. We avoid letting
* aVisibleRegion become overcomplex by simplifying it if necessary ---
@ -396,6 +410,7 @@ private:
// True when we're building a display list that's directly or indirectly
// under an nsDisplayTransform
PRPackedBool mInTransform;
PRPackedBool mSyncDecodeImages;
};
class nsDisplayItem;

View File

@ -63,10 +63,10 @@
NS_IMPL_ISUPPORTS2(nsImageLoader, imgIDecoderObserver, imgIContainerObserver)
nsImageLoader::nsImageLoader(nsIFrame *aFrame, PRBool aReflowOnLoad,
nsImageLoader::nsImageLoader(nsIFrame *aFrame, PRUint32 aActions,
nsImageLoader *aNextLoader)
: mFrame(aFrame),
mReflowOnLoad(aReflowOnLoad),
mActions(aActions),
mNextLoader(aNextLoader)
{
}
@ -82,10 +82,10 @@ nsImageLoader::~nsImageLoader()
/* static */ already_AddRefed<nsImageLoader>
nsImageLoader::Create(nsIFrame *aFrame, imgIRequest *aRequest,
PRBool aReflowOnLoad, nsImageLoader *aNextLoader)
PRUint32 aActions, nsImageLoader *aNextLoader)
{
nsRefPtr<nsImageLoader> loader =
new nsImageLoader(aFrame, aReflowOnLoad, aNextLoader);
new nsImageLoader(aFrame, aActions, aNextLoader);
loader->Load(aRequest);
@ -139,17 +139,17 @@ nsImageLoader::Load(imgIRequest *aImage)
NS_IMETHODIMP nsImageLoader::OnStartContainer(imgIRequest *aRequest,
imgIContainer *aImage)
{
if (aImage)
{
/* Get requested animation policy from the pres context:
* normal = 0
* one frame = 1
* one loop = 2
*/
aImage->SetAnimationMode(mFrame->PresContext()->ImageAnimationMode());
// Ensure the animation (if any) is started.
aImage->StartAnimation();
}
NS_ABORT_IF_FALSE(aImage, "Who's calling us then?");
/* Get requested animation policy from the pres context:
* normal = 0
* one frame = 1
* one loop = 2
*/
aImage->SetAnimationMode(mFrame->PresContext()->ImageAnimationMode());
// Ensure the animation (if any) is started.
aImage->StartAnimation();
return NS_OK;
}
@ -158,29 +158,42 @@ NS_IMETHODIMP nsImageLoader::OnStopFrame(imgIRequest *aRequest,
{
if (!mFrame)
return NS_ERROR_FAILURE;
#ifdef NS_DEBUG
// Make sure the image request status's STATUS_FRAME_COMPLETE flag has been set to ensure
// the image will be painted when invalidated
if (aRequest) {
PRUint32 status = imgIRequest::STATUS_ERROR;
nsresult rv = aRequest->GetImageStatus(&status);
if (NS_SUCCEEDED(rv)) {
NS_ASSERTION((status & imgIRequest::STATUS_FRAME_COMPLETE), "imgIRequest::STATUS_FRAME_COMPLETE not set");
}
}
#endif
if (!mRequest) {
// We're in the middle of a paint anyway
return NS_OK;
}
// Draw the background image
RedrawDirtyFrame(nsnull);
// Take requested actions
if (mActions & ACTION_REFLOW_ON_DECODE) {
DoReflow();
}
if (mActions & ACTION_REDRAW_ON_DECODE) {
DoRedraw(nsnull);
}
return NS_OK;
}
NS_IMETHODIMP nsImageLoader::OnStopRequest(imgIRequest *aRequest,
PRBool aLastPart)
{
if (!mFrame)
return NS_ERROR_FAILURE;
if (!mRequest) {
// We're in the middle of a paint anyway
return NS_OK;
}
// Take requested actions
if (mActions & ACTION_REFLOW_ON_LOAD) {
DoReflow();
}
if (mActions & ACTION_REDRAW_ON_LOAD) {
DoRedraw(nsnull);
}
}
NS_IMETHODIMP nsImageLoader::FrameChanged(imgIContainer *aContainer,
nsIntRect *dirtyRect)
{
@ -194,25 +207,25 @@ NS_IMETHODIMP nsImageLoader::FrameChanged(imgIContainer *aContainer,
nsRect r = dirtyRect->ToAppUnits(nsPresContext::AppUnitsPerCSSPixel());
RedrawDirtyFrame(&r);
DoRedraw(&r);
return NS_OK;
}
void
nsImageLoader::DoReflow()
{
nsIPresShell *shell = mFrame->PresContext()->GetPresShell();
#ifdef DEBUG
nsresult rv =
#endif
shell->FrameNeedsReflow(mFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Could not reflow after loading border-image");
}
void
nsImageLoader::RedrawDirtyFrame(const nsRect* aDamageRect)
nsImageLoader::DoRedraw(const nsRect* aDamageRect)
{
if (mReflowOnLoad) {
nsIPresShell *shell = mFrame->PresContext()->GetPresShell();
#ifdef DEBUG
nsresult rv =
#endif
shell->FrameNeedsReflow(mFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Could not reflow after loading border-image");
// The reflow might not do all the invalidation we need, so continue
// on with the invalidation codepath.
}
// NOTE: It is not sufficient to invalidate only the size of the image:
// the image may be tiled!
// The best option is to call into the frame, however lacking this

View File

@ -57,20 +57,33 @@ class nsIURI;
class nsImageLoader : public nsStubImageDecoderObserver
{
private:
nsImageLoader(nsIFrame *aFrame, PRBool aReflowOnLoad,
nsImageLoader(nsIFrame *aFrame, PRUint32 aActions,
nsImageLoader *aNextLoader);
virtual ~nsImageLoader();
public:
/*
* Flags to specify actions that can be taken for the image at various
* times. Reflows always occur before redraws. "Decode" refers to one
* frame being available, whereas "load" refers to all the data being loaded
* from the network.
*/
enum {
ACTION_REFLOW_ON_DECODE = 0x01,
ACTION_REDRAW_ON_DECODE = 0x02,
ACTION_REFLOW_ON_LOAD = 0x04,
ACTION_REDRAW_ON_LOAD = 0x08
};
static already_AddRefed<nsImageLoader>
Create(nsIFrame *aFrame, imgIRequest *aRequest,
PRBool aReflowOnLoad, nsImageLoader *aNextLoader);
PRUint32 aActions, nsImageLoader *aNextLoader);
NS_DECL_ISUPPORTS
// imgIDecoderObserver (override nsStubImageDecoderObserver)
NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
NS_IMETHOD OnStopFrame(imgIRequest *aRequest, PRUint32 aFrame);
NS_IMETHOD OnStopRequest(imgIRequest *aRequest, PRBool aLastPart);
// Do not override OnDataAvailable since background images are not
// displayed incrementally; they are displayed after the entire image
// has been loaded.
@ -87,10 +100,12 @@ public:
private:
nsresult Load(imgIRequest *aImage);
void RedrawDirtyFrame(const nsRect* aDamageRect);
void DoReflow();
/* if aDamageRect is nsnull, the whole frame is redrawn. */
void DoRedraw(const nsRect* aDamageRect);
nsIFrame *mFrame;
nsCOMPtr<imgIRequest> mRequest;
PRBool mReflowOnLoad;
PRUint32 mActions;
nsRefPtr<nsImageLoader> mNextLoader;
};

View File

@ -1061,6 +1061,9 @@ nsLayoutUtils::PaintFrame(nsIRenderingContext* aRenderingContext, nsIFrame* aFra
if (aFlags & PAINT_IN_TRANSFORM) {
builder.SetInTransform(PR_TRUE);
}
if (aFlags & PAINT_SYNC_DECODE_IMAGES) {
builder.SetSyncDecodeImages(PR_TRUE);
}
nsresult rv;
builder.EnterPresShell(aFrame, dirtyRect);
@ -2774,7 +2777,8 @@ DrawImageInternal(nsIRenderingContext* aRenderingContext,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
const nsIntSize& aImageSize)
const nsIntSize& aImageSize,
PRUint32 aImageFlags)
{
if (aDest.IsEmpty() || aFill.IsEmpty())
return NS_OK;
@ -2872,7 +2876,8 @@ DrawImageInternal(nsIRenderingContext* aRenderingContext,
if (finalFillRect.IsEmpty())
return NS_OK;
aImage->Draw(ctx, aGraphicsFilter, transform, finalFillRect, intSubimage);
aImage->Draw(ctx, aGraphicsFilter, transform, finalFillRect, intSubimage,
aImageFlags);
return NS_OK;
}
@ -2881,6 +2886,7 @@ nsLayoutUtils::DrawSingleUnscaledImage(nsIRenderingContext* aRenderingContext,
imgIContainer* aImage,
const nsPoint& aDest,
const nsRect& aDirty,
PRUint32 aImageFlags,
const nsRect* aSourceArea)
{
nsIntSize imageSize;
@ -2906,7 +2912,7 @@ nsLayoutUtils::DrawSingleUnscaledImage(nsIRenderingContext* aRenderingContext,
// translation but we don't want to actually tile the image.
fill.IntersectRect(fill, dest);
return DrawImageInternal(aRenderingContext, aImage, gfxPattern::FILTER_NEAREST,
dest, fill, aDest, aDirty, imageSize);
dest, fill, aDest, aDirty, imageSize, aImageFlags);
}
/* static */ nsresult
@ -2915,6 +2921,7 @@ nsLayoutUtils::DrawSingleImage(nsIRenderingContext* aRenderingContext,
gfxPattern::GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aDirty,
PRUint32 aImageFlags,
const nsRect* aSourceArea)
{
nsIntSize imageSize;
@ -2939,7 +2946,7 @@ nsLayoutUtils::DrawSingleImage(nsIRenderingContext* aRenderingContext,
nsRect fill;
fill.IntersectRect(aDest, dest);
return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter, dest, fill,
fill.TopLeft(), aDirty, imageSize);
fill.TopLeft(), aDirty, imageSize, aImageFlags);
}
/* static */ nsresult
@ -2949,7 +2956,8 @@ nsLayoutUtils::DrawImage(nsIRenderingContext* aRenderingContext,
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty)
const nsRect& aDirty,
PRUint32 aImageFlags)
{
nsIntSize imageSize;
aImage->GetWidth(&imageSize.width);
@ -2958,7 +2966,7 @@ nsLayoutUtils::DrawImage(nsIRenderingContext* aRenderingContext,
return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter,
aDest, aFill, aAnchor, aDirty,
imageSize);
imageSize, aImageFlags);
}
/* static */ nsRect
@ -3364,8 +3372,13 @@ nsLayoutUtils::SurfaceFromElement(nsIDOMElement *aElement,
if (NS_FAILED(rv) || !imgContainer)
return result;
PRUint32 whichFrame = (aSurfaceFlags & SFE_WANT_FIRST_FRAME)
? (PRUint32) imgIContainer::FRAME_FIRST
: (PRUint32) imgIContainer::FRAME_CURRENT;
nsRefPtr<gfxASurface> framesurf;
rv = imgContainer->GetCurrentFrame(getter_AddRefs(framesurf));
rv = imgContainer->GetFrame(whichFrame,
imgIContainer::FLAG_SYNC_DECODE,
getter_AddRefs(framesurf));
if (NS_FAILED(rv))
return result;

View File

@ -62,6 +62,7 @@ class nsIFontMetrics;
#include "nsIPresShell.h"
#include "nsIPrincipal.h"
#include "gfxPattern.h"
#include "imgIContainer.h"
class nsBlockFrame;
class nsTextFragment;
@ -476,7 +477,11 @@ public:
static nsRect RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor);
enum { PAINT_IN_TRANSFORM = 0x01 };
enum {
PAINT_IN_TRANSFORM = 0x01,
PAINT_SYNC_DECODE_IMAGES = 0x02
};
/**
* Given aFrame, the root frame of a stacking context, paint it and its
* descendants to aRenderingContext.
@ -488,7 +493,8 @@ public:
* @param aBackstop paint the dirty area with this color before drawing
* the actual content; pass NS_RGBA(0,0,0,0) to draw no background
* @param aFlags if PAINT_IN_TRANSFORM is set, then we assume
* this is inside a transform or SVG foreignObject.
* this is inside a transform or SVG foreignObject. If
* PAINT_SYNC_DECODE_IMAGES is set, we force synchronous decode on all images.
*/
static nsresult PaintFrame(nsIRenderingContext* aRenderingContext, nsIFrame* aFrame,
const nsRegion& aDirtyRegion, nscolor aBackstop,
@ -880,6 +886,7 @@ public:
* @param aAnchor A point in aFill which we will ensure is
* pixel-aligned in the output.
* @param aDirty Pixels outside this area may be skipped.
* @param aImageFlags Image flags of the imgIContainer::FLAG_* variety
*/
static nsresult DrawImage(nsIRenderingContext* aRenderingContext,
imgIContainer* aImage,
@ -887,7 +894,8 @@ public:
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty);
const nsRect& aDirty,
PRUint32 aImageFlags);
/**
* Draw a whole image without scaling or tiling.
@ -898,6 +906,7 @@ public:
* @param aImage The image.
* @param aDest The top-left where the image should be drawn
* @param aDirty Pixels outside this area may be skipped.
* @param aImageFlags Image flags of the imgIContainer::FLAG_* variety
* @param aSourceArea If non-null, this area is extracted from
* the image and drawn at aDest. It's
* in appunits. For best results it should
@ -907,6 +916,7 @@ public:
imgIContainer* aImage,
const nsPoint& aDest,
const nsRect& aDirty,
PRUint32 aImageFlags,
const nsRect* aSourceArea = nsnull);
/**
@ -922,12 +932,14 @@ public:
* the image and drawn in aDest. It's
* in appunits. For best results it should
* be aligned with image pixels.
* @param aImageFlags Image flags of the imgIContainer::FLAG_* variety
*/
static nsresult DrawSingleImage(nsIRenderingContext* aRenderingContext,
imgIContainer* aImage,
gfxPattern::GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aDirty,
PRUint32 aImageFlags,
const nsRect* aSourceArea = nsnull);
/**
@ -1069,7 +1081,10 @@ public:
/* Always create a new surface for the result */
SFE_WANT_NEW_SURFACE = 1 << 0,
/* When creating a new surface, create an image surface */
SFE_WANT_IMAGE_SURFACE = 1 << 1
SFE_WANT_IMAGE_SURFACE = 1 << 1,
/* Whether to extract the first frame (as opposed to the
current frame) in the case that the element is an image. */
SFE_WANT_FIRST_FRAME = 1 << 2
};
struct SurfaceFromElementResult {

View File

@ -1276,8 +1276,9 @@ nsPresContext::SetupBackgroundImageLoaders(nsIFrame* aFrame,
nsRefPtr<nsImageLoader> loaders;
NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, aStyleBackground) {
if (aStyleBackground->mLayers[i].mImage.GetType() == eStyleImageType_Image) {
PRUint32 actions = nsImageLoader::ACTION_REDRAW_ON_DECODE;
imgIRequest *image = aStyleBackground->mLayers[i].mImage.GetImageData();
loaders = nsImageLoader::Create(aFrame, image, PR_FALSE, loaders);
loaders = nsImageLoader::Create(aFrame, image, actions, loaders);
}
}
SetImageLoaders(aFrame, BACKGROUND_IMAGE, loaders);
@ -1287,9 +1288,12 @@ void
nsPresContext::SetupBorderImageLoaders(nsIFrame* aFrame,
const nsStyleBorder* aStyleBorder)
{
PRUint32 actions = nsImageLoader::ACTION_REDRAW_ON_LOAD;
if (aStyleBorder->ImageBorderDiffers())
actions |= nsImageLoader::ACTION_REFLOW_ON_LOAD;
nsRefPtr<nsImageLoader> loader =
nsImageLoader::Create(aFrame, aStyleBorder->GetBorderImage(),
aStyleBorder->ImageBorderDiffers(), nsnull);
actions, nsnull);
SetImageLoaders(aFrame, BORDER_IMAGE, loader);
}

View File

@ -5182,6 +5182,7 @@ PresShell::RenderDocument(const nsRect& aRect, PRUint32 aFlags,
}
builder.SetBackgroundOnly(PR_FALSE);
builder.SetSyncDecodeImages(PR_TRUE);
builder.EnterPresShell(rootFrame, rect);
// Add the canvas background color.

View File

@ -7,16 +7,24 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=445810
<title>Test for Bug 445810</title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/modules/libpr0n/test/mochitest/imgutils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=445810">Mozilla Bug 445810</a>
<div><p id="display"></p></div>
<div style="display: none;"><img id="currimg"></div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 445810 **/
// Once the border image is loaded, it isn't necessarily decoded. We need to
// force a decode, but only have a good way of doing that for image elements,
// not border images. However, we can take advantage of the image cache (which
// shares images of the same url) and assign the same url to both.
var currImageElem = document.getElementById("currimg");
function new_image_url()
{
var width = 10;
@ -60,12 +68,20 @@ is(divcs.width, "11px", // 3 (border-left) + 5 (width) + 3 (border-right)
is(divcs.height, "11px", // 3 (border-top) + 5 (height) + 3 (border-bottom)
"correct height without a border image");
p.style.MozBorderImage = "url( " + new_image_url() + ") 2 2 2 2 / 7px 2px";
currImageElem.src = new_image_url();
p.style.MozBorderImage = "url( " + currImageElem.src + ") 2 2 2 2 / 7px 2px";
is(divcs.width, "11px", "border image not loaded yet");
is(divcs.height, "11px", "border image not loaded yet");
currImageElem.onload = step2;
setTimeout(step2, 0);
function step2() {
// We got here through onload
ok(isImageLoaded("currimg"), "image loaded");
// Force a decode
forceDecode("currimg");
ok(isFrameDecoded("currimg"), "frame decoded");
is(divcs.width, "9px", "border image loading caused reflow");
is(divcs.height, "19px", "border image loading caused reflow");
@ -77,24 +93,40 @@ function step2() {
is(divcs.width, "5px", "correct width without a border");
is(divcs.height, "5px", "correct height without a border");
p.style.MozBorderImage = "url( " + new_image_url() + ") 2 2 2 2 / 7px 2px";
currImageElem.src = new_image_url();
p.style.MozBorderImage = "url( " + currImageElem.src + ") 2 2 2 2 / 7px 2px";
is(divcs.width, "5px", "border image not loaded yet");
is(divcs.height, "5px", "border image not loaded yet");
setTimeout(step3, 0);
currImageElem.onload = step3;
}
function step3() {
// We got here through onload
ok(isImageLoaded("currimg"), "image loaded");
// Force a decode
forceDecode("currimg");
ok(isFrameDecoded("currimg"), "frame decoded");
is(divcs.width, "9px", "border image loading caused reflow");
is(divcs.height, "19px", "border image loading caused reflow");
p.style.MozBorderImage = "url( " + new_image_url() + ") 2 2 2 2";
currImageElem.src = new_image_url();
p.style.MozBorderImage = "url( " + currImageElem.src + ") 2 2 2 2";
p.style.border = "3px none";
is(divcs.width, "5px", "border image not loaded yet");
is(divcs.height, "5px", "border image not loaded yet");
setTimeout(step4, 0);
currImageElem.onload = step4;
}
function step4() {
// We got here through onload
ok(isImageLoaded("currimg"), "image loaded");
// Force a decode
forceDecode("currimg");
ok(isFrameDecoded("currimg"), "frame decoded");
is(divcs.width, "11px", "border image loading caused reflow");
is(divcs.height, "11px", "border image loading caused reflow");

View File

@ -241,7 +241,7 @@ nsBulletFrame::PaintBullet(nsIRenderingContext& aRenderingContext, nsPoint aPt,
mRect.height - (mPadding.top + mPadding.bottom));
nsLayoutUtils::DrawSingleImage(&aRenderingContext,
imageCon, nsLayoutUtils::GetGraphicsFilterForFrame(this),
dest + aPt, aDirtyRect);
dest + aPt, aDirtyRect, imgIContainer::FLAG_NONE);
return;
}
}

View File

@ -6095,7 +6095,7 @@ void nsFrame::FillCursorInformationFromStyle(const nsStyleUserInterface* ui,
item < item_end; ++item) {
PRUint32 status;
nsresult rv = item->mImage->GetImageStatus(&status);
if (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_FRAME_COMPLETE)) {
if (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_LOAD_COMPLETE)) {
// This is the one we want
item->mImage->GetImage(getter_AddRefs(aCursor.mContainer));
aCursor.mHaveHotspot = item->mHaveHotspot;

View File

@ -1050,7 +1050,8 @@ nsImageFrame::DisplayAltFeedback(nsIRenderingContext& aRenderingContext,
inner.XMost() - size : inner.x,
inner.y, size, size);
nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon,
nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect);
nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
imgIContainer::FLAG_NONE);
iconUsed = PR_TRUE;
}
@ -1144,12 +1145,16 @@ void
nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
nsIRenderingContext* aCtx, const nsRect& aDirtyRect) {
static_cast<nsImageFrame*>(mFrame)->
PaintImage(*aCtx, aBuilder->ToReferenceFrame(mFrame), aDirtyRect, mImage);
PaintImage(*aCtx, aBuilder->ToReferenceFrame(mFrame), aDirtyRect, mImage,
aBuilder->ShouldSyncDecodeImages()
? (PRUint32) imgIContainer::FLAG_SYNC_DECODE
: (PRUint32) imgIContainer::FLAG_NONE);
}
void
nsImageFrame::PaintImage(nsIRenderingContext& aRenderingContext, nsPoint aPt,
const nsRect& aDirtyRect, imgIContainer* aImage)
const nsRect& aDirtyRect, imgIContainer* aImage,
PRUint32 aFlags)
{
// Render the image into our content area (the area inside
// the borders and padding)
@ -1159,7 +1164,8 @@ nsImageFrame::PaintImage(nsIRenderingContext& aRenderingContext, nsPoint aPt,
dest.y -= GetContinuationOffset();
nsLayoutUtils::DrawSingleImage(&aRenderingContext, aImage,
nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect);
nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
aFlags);
nsPresContext* presContext = PresContext();
nsImageMap* map = GetImageMap(presContext);
@ -1848,6 +1854,12 @@ nsImageFrame::IconLoad::OnStopRequest(imgIRequest *aRequest,
return NS_OK;
}
NS_IMETHODIMP
nsImageFrame::IconLoad::OnDiscard(imgIRequest *aRequest)
{
return NS_OK;
}
NS_IMETHODIMP
nsImageFrame::IconLoad::FrameChanged(imgIContainer *aContainer,
nsIntRect * aDirtyRect)

View File

@ -215,7 +215,8 @@ protected:
const nsRect& aRect);
void PaintImage(nsIRenderingContext& aRenderingContext, nsPoint aPt,
const nsRect& aDirtyRect, imgIContainer* aImage);
const nsRect& aDirtyRect, imgIContainer* aImage,
PRUint32 aFlags);
protected:
friend class nsImageListener;

View File

@ -581,7 +581,8 @@ nsPageFrame::PaintPageContent(nsIRenderingContext& aRenderingContext,
rect, backgroundRect, 0);
nsLayoutUtils::PaintFrame(&aRenderingContext, pageContentFrame,
nsRegion(rect), NS_RGBA(0,0,0,0));
nsRegion(rect), NS_RGBA(0,0,0,0),
nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES);
aRenderingContext.PopState();
}

View File

@ -639,7 +639,8 @@ nsSimplePageSequenceFrame::PrintNextPage()
mCurrentPageFrame->GetSize());
nsRegion drawingRegion(drawingRect);
nsLayoutUtils::PaintFrame(renderingContext, mCurrentPageFrame,
drawingRegion, NS_RGBA(0,0,0,0));
drawingRegion, NS_RGBA(0,0,0,0),
nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES);
if (mSelectionHeight >= 0 && selectionY < mSelectionHeight) {
selectionY += height;
@ -707,7 +708,8 @@ nsSimplePageSequenceFrame::PaintPageSequence(nsIRenderingContext& aRenderingCont
aRenderingContext.PushState();
aRenderingContext.Translate(pt.x, pt.y);
nsLayoutUtils::PaintFrame(&aRenderingContext, child,
nsRegion(rect - pt), NS_RGBA(0,0,0,0));
nsRegion(rect - pt), NS_RGBA(0,0,0,0),
nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES);
aRenderingContext.PopState();
child = child->GetNextSibling();
}

View File

@ -1472,6 +1472,14 @@ nsStyleImage::ComputeActualCropRect(nsIntRect& aActualCropRect,
return PR_TRUE;
}
nsresult
nsStyleImage::RequestDecode()
{
if ((mType == eStyleImageType_Image) && mImage)
return mImage->RequestDecode();
return NS_OK;
}
PRBool
nsStyleImage::IsOpaque() const
{

View File

@ -250,6 +250,10 @@ struct nsStyleImage {
PRBool ComputeActualCropRect(nsIntRect& aActualCropRect,
PRBool* aIsEntireImage = nsnull) const;
/**
* Requests a decode on the image.
*/
nsresult RequestDecode();
/**
* @return PR_TRUE if the item is definitely opaque --- i.e., paints every
* pixel within its bounds opaquely, and the bounds contains at least a pixel.
@ -785,6 +789,7 @@ struct nsStyleBorder {
// Defined in nsStyleStructInlines.h
inline PRBool IsBorderImageLoaded() const;
inline nsresult RequestDecode();
void GetBorderColor(PRUint8 aSide, nscolor& aColor,
PRBool& aForeground) const

View File

@ -51,6 +51,15 @@ inline void
nsStyleBorder::SetBorderImage(imgIRequest* aImage)
{
mBorderImage = aImage;
/*
* Request a decode to jump start decoding, and lock it to make sure it
* stays decoded.
*/
if (mBorderImage) {
mBorderImage->RequestDecode();
mBorderImage->LockImage();
}
}
inline imgIRequest*
@ -64,7 +73,7 @@ inline PRBool nsStyleBorder::IsBorderImageLoaded() const
PRUint32 status;
return mBorderImage &&
NS_SUCCEEDED(mBorderImage->GetImageStatus(&status)) &&
(status & imgIRequest::STATUS_FRAME_COMPLETE);
(status & imgIRequest::STATUS_LOAD_COMPLETE);
}
#endif /* !defined(nsStyleStructInlines_h_) */

View File

@ -237,9 +237,15 @@ nsSVGImageFrame::PaintSVG(nsSVGRenderState *aContext,
currentRequest->GetImage(getter_AddRefs(mImageContainer));
}
// XXXbholley - I don't think huge images in SVGs are common enough to
// warrant worrying about the responsiveness impact of doing synchronous
// decodes. The extra code complexity of determinining when we want to
// force sync probably just isn't worth it, so always pass FLAG_SYNC_DECODE
nsRefPtr<gfxASurface> currentFrame;
if (mImageContainer)
mImageContainer->GetCurrentFrame(getter_AddRefs(currentFrame));
mImageContainer->GetFrame(imgIContainer::FRAME_CURRENT,
imgIContainer::FLAG_SYNC_DECODE,
getter_AddRefs(currentFrame));
// We need to wrap the surface in a pattern to have somewhere to set the
// graphics filter.

View File

@ -333,7 +333,10 @@ void nsDisplayXULImage::Paint(nsDisplayListBuilder* aBuilder,
nsIRenderingContext* aCtx, const nsRect& aDirtyRect)
{
static_cast<nsImageBoxFrame*>(mFrame)->
PaintImage(*aCtx, aDirtyRect, aBuilder->ToReferenceFrame(mFrame));
PaintImage(*aCtx, aDirtyRect, aBuilder->ToReferenceFrame(mFrame),
aBuilder->ShouldSyncDecodeImages()
? (PRUint32) imgIContainer::FLAG_SYNC_DECODE
: (PRUint32) imgIContainer::FLAG_NONE);
}
NS_IMETHODIMP
@ -359,7 +362,8 @@ nsImageBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
void
nsImageBoxFrame::PaintImage(nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect, nsPoint aPt)
const nsRect& aDirtyRect, nsPoint aPt,
PRUint32 aFlags)
{
nsRect rect;
GetClientRect(rect);
@ -381,7 +385,7 @@ nsImageBoxFrame::PaintImage(nsIRenderingContext& aRenderingContext,
PRBool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon,
nsLayoutUtils::GetGraphicsFilterForFrame(this),
rect, dirty, hasSubRect ? &mSubRect : nsnull);
rect, dirty, aFlags, hasSubRect ? &mSubRect : nsnull);
}
}

View File

@ -127,7 +127,7 @@ public:
void PaintImage(nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
nsPoint aPt);
nsPoint aPt, PRUint32 aFlags);
protected:
nsImageBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext);

View File

@ -3399,7 +3399,7 @@ nsTreeBodyFrame::PaintTwisty(PRInt32 aRowIndex,
// Paint the image.
nsLayoutUtils::DrawSingleUnscaledImage(&aRenderingContext, image,
pt, aDirtyRect, &imageSize);
pt, aDirtyRect, imgIContainer::FLAG_NONE, &imageSize);
}
}
}
@ -3530,7 +3530,8 @@ nsTreeBodyFrame::PaintImage(PRInt32 aRowIndex,
nsLayoutUtils::DrawImage(&aRenderingContext, image,
nsLayoutUtils::GetGraphicsFilterForFrame(this),
wholeImageDest, destRect, destRect.TopLeft(), aDirtyRect);
wholeImageDest, destRect, destRect.TopLeft(), aDirtyRect,
imgIContainer::FLAG_NONE);
}
// Update the aRemainingWidth and aCurrX values.
@ -3706,7 +3707,7 @@ nsTreeBodyFrame::PaintCheckbox(PRInt32 aRowIndex,
// Paint the image.
nsLayoutUtils::DrawSingleUnscaledImage(&aRenderingContext, image,
pt, aDirtyRect, &imageSize);
pt, aDirtyRect, imgIContainer::FLAG_NONE, &imageSize);
}
}
@ -3771,7 +3772,8 @@ nsTreeBodyFrame::PaintProgressMeter(PRInt32 aRowIndex,
height*nsIDeviceContext::AppUnitsPerCSSPixel());
nsLayoutUtils::DrawImage(&aRenderingContext, image,
nsLayoutUtils::GetGraphicsFilterForFrame(this),
nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(), aDirtyRect);
nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(),
aDirtyRect, imgIContainer::FLAG_NONE);
} else {
aRenderingContext.FillRect(meterRect);
}
@ -3791,7 +3793,8 @@ nsTreeBodyFrame::PaintProgressMeter(PRInt32 aRowIndex,
height*nsIDeviceContext::AppUnitsPerCSSPixel());
nsLayoutUtils::DrawImage(&aRenderingContext, image,
nsLayoutUtils::GetGraphicsFilterForFrame(this),
nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(), aDirtyRect);
nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(),
aDirtyRect, imgIContainer::FLAG_NONE);
}
}
}

View File

@ -190,7 +190,7 @@ static const nsModuleComponentInfo components[] =
imgLoaderConstructor, },
{ "image container",
NS_IMGCONTAINER_CID,
"@mozilla.org/image/container;2",
"@mozilla.org/image/container;3",
imgContainerConstructor, },
{ "image loader",
NS_IMGLOADER_CID,
@ -211,7 +211,7 @@ static const nsModuleComponentInfo components[] =
// gif
{ "GIF Decoder",
NS_GIFDECODER2_CID,
"@mozilla.org/image/decoder;2?type=image/gif",
"@mozilla.org/image/decoder;3?type=image/gif",
nsGIFDecoder2Constructor, },
#endif
@ -219,15 +219,15 @@ static const nsModuleComponentInfo components[] =
// jpeg
{ "JPEG decoder",
NS_JPEGDECODER_CID,
"@mozilla.org/image/decoder;2?type=image/jpeg",
"@mozilla.org/image/decoder;3?type=image/jpeg",
nsJPEGDecoderConstructor, },
{ "JPEG decoder",
NS_JPEGDECODER_CID,
"@mozilla.org/image/decoder;2?type=image/pjpeg",
"@mozilla.org/image/decoder;3?type=image/pjpeg",
nsJPEGDecoderConstructor, },
{ "JPEG decoder",
NS_JPEGDECODER_CID,
"@mozilla.org/image/decoder;2?type=image/jpg",
"@mozilla.org/image/decoder;3?type=image/jpg",
nsJPEGDecoderConstructor, },
#endif
#ifdef IMG_BUILD_ENCODER_jpeg
@ -242,19 +242,19 @@ static const nsModuleComponentInfo components[] =
// bmp
{ "ICO Decoder",
NS_ICODECODER_CID,
"@mozilla.org/image/decoder;2?type=image/x-icon",
"@mozilla.org/image/decoder;3?type=image/x-icon",
nsICODecoderConstructor, },
{ "ICO Decoder",
NS_ICODECODER_CID,
"@mozilla.org/image/decoder;2?type=image/vnd.microsoft.icon",
"@mozilla.org/image/decoder;3?type=image/vnd.microsoft.icon",
nsICODecoderConstructor, },
{ "BMP Decoder",
NS_BMPDECODER_CID,
"@mozilla.org/image/decoder;2?type=image/bmp",
"@mozilla.org/image/decoder;3?type=image/bmp",
nsBMPDecoderConstructor, },
{ "BMP Decoder",
NS_BMPDECODER_CID,
"@mozilla.org/image/decoder;2?type=image/x-ms-bmp",
"@mozilla.org/image/decoder;3?type=image/x-ms-bmp",
nsBMPDecoderConstructor, },
#endif
@ -262,11 +262,11 @@ static const nsModuleComponentInfo components[] =
// png
{ "PNG Decoder",
NS_PNGDECODER_CID,
"@mozilla.org/image/decoder;2?type=image/png",
"@mozilla.org/image/decoder;3?type=image/png",
nsPNGDecoderConstructor, },
{ "PNG Decoder",
NS_PNGDECODER_CID,
"@mozilla.org/image/decoder;2?type=image/x-png",
"@mozilla.org/image/decoder;3?type=image/x-png",
nsPNGDecoderConstructor, },
#endif
#ifdef IMG_BUILD_ENCODER_png

View File

@ -21,6 +21,7 @@
*
* Contributor(s):
* Neil Rashbrook <neil@parkwaycc.co.uk>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -50,8 +51,6 @@
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "imgILoad.h"
#include "prlog.h"
#ifdef PR_LOGGING
@ -73,40 +72,47 @@ nsBMPDecoder::nsBMPDecoder()
mState = eRLEStateInitial;
mStateData = 0;
mLOH = WIN_HEADER_LENGTH;
mError = PR_FALSE;
}
nsBMPDecoder::~nsBMPDecoder()
{
delete[] mColors;
if (mRow)
free(mRow);
delete[] mColors;
if (mRow)
free(mRow);
}
NS_IMETHODIMP nsBMPDecoder::Init(imgILoad *aLoad)
NS_IMETHODIMP nsBMPDecoder::Init(imgIContainer *aImage,
imgIDecoderObserver *aObserver,
PRUint32 aFlags)
{
PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::Init(%p)\n", aLoad));
mObserver = do_QueryInterface(aLoad);
PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::Init(%p)\n", aImage));
mImage = aImage;
mObserver = aObserver;
mFlags = aFlags;
nsresult rv;
mImage = do_CreateInstance("@mozilla.org/image/container;2", &rv);
if (NS_FAILED(rv))
return rv;
// Fire OnStartDecode at init time to support bug 512435
if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && mObserver)
mObserver->OnStartDecode(nsnull);
return aLoad->SetImage(mImage);
return NS_OK;
}
NS_IMETHODIMP nsBMPDecoder::Close()
NS_IMETHODIMP nsBMPDecoder::Close(PRUint32 aFlags)
{
PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::Close()\n"));
mImage->DecodingComplete();
if (mObserver) {
mObserver->OnStopFrame(nsnull, 0);
mObserver->OnStopContainer(nsnull, mImage);
mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
mObserver = nsnull;
// Send notifications if appropriate
if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) &&
!mError && !(aFlags & CLOSE_FLAG_DONTNOTIFY)) {
if (mObserver)
mObserver->OnStopFrame(nsnull, 0);
mImage->DecodingComplete();
if (mObserver) {
mObserver->OnStopContainer(nsnull, mImage);
mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
}
}
mImage = nsnull;
return NS_OK;
}
@ -120,29 +126,31 @@ NS_METHOD nsBMPDecoder::ReadSegCb(nsIInputStream* aIn, void* aClosure,
PRUint32 aCount, PRUint32 *aWriteCount)
{
nsBMPDecoder *decoder = reinterpret_cast<nsBMPDecoder*>(aClosure);
// Always read everything
*aWriteCount = aCount;
nsresult rv = decoder->ProcessData(aFromRawSegment, aCount);
if (NS_FAILED(rv)) {
*aWriteCount = 0;
}
// Necko doesn't propagate rvs. Set a flag before returning.
if (NS_FAILED(rv))
decoder->mError = PR_TRUE;
return rv;
}
NS_IMETHODIMP nsBMPDecoder::WriteFrom(nsIInputStream *aInStr, PRUint32 aCount, PRUint32 *aRetval)
NS_IMETHODIMP nsBMPDecoder::WriteFrom(nsIInputStream *aInStr, PRUint32 aCount)
{
PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::WriteFrom(%p, %lu, %p)\n", aInStr, aCount, aRetval));
PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::WriteFrom(%p, %lu, %p)\n", aInStr, aCount));
nsresult rv = aInStr->ReadSegments(ReadSegCb, this, aCount, aRetval);
if (aCount != *aRetval) {
*aRetval = aCount;
return NS_ERROR_FAILURE;
// Decode, watching for errors.
nsresult rv = NS_OK;
PRUint32 ignored;
if (!mError)
rv = aInStr->ReadSegments(ReadSegCb, this, aCount, &ignored);
if (mError || NS_FAILED(rv)) {
return NS_ERROR_FAILURE;
}
return rv;
return NS_OK;
}
// ----------------------------------------
@ -202,8 +210,6 @@ NS_METHOD nsBMPDecoder::ProcessData(const char* aBuffer, PRUint32 aCount)
aBuffer += toCopy;
}
if (mPos == BFH_LENGTH) {
rv = mObserver->OnStartDecode(nsnull);
NS_ENSURE_SUCCESS(rv, rv);
ProcessFileHeader();
if (mBFH.signature[0] != 'B' || mBFH.signature[1] != 'M')
return NS_ERROR_FAILURE;
@ -228,6 +234,30 @@ NS_METHOD nsBMPDecoder::ProcessData(const char* aBuffer, PRUint32 aCount)
mBIH.bpp != 16 && mBIH.bpp != 24 && mBIH.bpp != 32)
return NS_ERROR_UNEXPECTED;
// BMPs with negative width are invalid
// Reject extremely wide images to keep the math sane
const PRInt32 k64KWidth = 0x0000FFFF;
if (mBIH.width < 0 || mBIH.width > k64KWidth)
return NS_ERROR_FAILURE;
PRUint32 real_height = (mBIH.height > 0) ? mBIH.height : -mBIH.height;
// Set the size and notify
rv = mImage->SetSize(mBIH.width, real_height);
NS_ENSURE_SUCCESS(rv, rv);
if (mObserver) {
rv = mObserver->OnStartContainer(nsnull, mImage);
NS_ENSURE_SUCCESS(rv, rv);
}
// We have the size. If we're doing a header-only decode, we got what
// we came for.
if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY)
return NS_OK;
// We're doing a real decode.
mOldLine = mCurLine = real_height;
if (mBIH.bpp <= 8) {
mNumColors = 1 << mBIH.bpp;
if (mBIH.colors && mBIH.colors < mNumColors)
@ -247,18 +277,6 @@ NS_METHOD nsBMPDecoder::ProcessData(const char* aBuffer, PRUint32 aCount)
mBitFields.blue = 0x001F;
CalcBitShift();
}
// BMPs with negative width are invalid
// Reject extremely wide images to keep the math sane
const PRInt32 k64KWidth = 0x0000FFFF;
if (mBIH.width < 0 || mBIH.width > k64KWidth)
return NS_ERROR_FAILURE;
PRUint32 real_height = (mBIH.height > 0) ? mBIH.height : -mBIH.height;
rv = mImage->Init(mBIH.width, real_height, mObserver);
NS_ENSURE_SUCCESS(rv, rv);
rv = mObserver->OnStartContainer(nsnull, mImage);
NS_ENSURE_SUCCESS(rv, rv);
mOldLine = mCurLine = real_height;
PRUint32 imageLength;
if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
@ -291,8 +309,10 @@ NS_METHOD nsBMPDecoder::ProcessData(const char* aBuffer, PRUint32 aCount)
memset(mImageData, 0, imageLength);
}
mObserver->OnStartFrame(nsnull, 0);
NS_ENSURE_SUCCESS(rv, rv);
if (mObserver) {
mObserver->OnStartFrame(nsnull, 0);
NS_ENSURE_SUCCESS(rv, rv);
}
}
PRUint8 bpc; // bytes per color
bpc = (mBFH.bihsize == OS2_BIH_LENGTH) ? 3 : 4; // OS/2 Bitmaps have no padding byte
@ -599,7 +619,8 @@ NS_METHOD nsBMPDecoder::ProcessData(const char* aBuffer, PRUint32 aCount)
rv = mImage->FrameUpdated(0, r);
NS_ENSURE_SUCCESS(rv, rv);
mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
if (mObserver)
mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
mOldLine = mCurLine;
}

View File

@ -20,6 +20,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -169,6 +170,7 @@ private:
nsCOMPtr<imgIDecoderObserver> mObserver;
nsCOMPtr<imgIContainer> mImage;
PRUint32 mFlags;
PRUint32 mPos;
@ -192,6 +194,7 @@ private:
ERLEState mState; ///< Maintains the current state of the RLE decoding
PRUint32 mStateData;///< Decoding information that is needed depending on mState
PRBool mError; ///< Did we hit an error?
/** Set mBFH from the raw data in mRawBuf, converting from little-endian
* data to native data as necessary */

View File

@ -22,6 +22,7 @@
* Contributor(s):
* David Hyatt <hyatt@netscape.com> (Original Author)
* Christian Biesinger <cbiesinger@web.de>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -48,7 +49,6 @@
#include "nsIComponentManager.h"
#include "imgIContainerObserver.h"
#include "imgILoad.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
@ -82,45 +82,55 @@ nsICODecoder::nsICODecoder()
mColors = nsnull;
mRow = nsnull;
mHaveAlphaData = mDecodingAndMask = PR_FALSE;
mError = PR_FALSE;
}
nsICODecoder::~nsICODecoder()
{
}
NS_IMETHODIMP nsICODecoder::Init(imgILoad *aLoad)
{
mObserver = do_QueryInterface(aLoad);
mImage = do_CreateInstance("@mozilla.org/image/container;2");
if (!mImage)
return NS_ERROR_OUT_OF_MEMORY;
NS_IMETHODIMP nsICODecoder::Init(imgIContainer *aImage,
imgIDecoderObserver *aObserver,
PRUint32 aFlags)
{
// Grab parameters
mImage = aImage;
mObserver = aObserver;
mFlags = aFlags;
return aLoad->SetImage(mImage);
// Fire OnStartDecode at init time to support bug 512435
if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && mObserver)
mObserver->OnStartDecode(nsnull);
return NS_OK;
}
NS_IMETHODIMP nsICODecoder::Close()
NS_IMETHODIMP nsICODecoder::Close(PRUint32 aFlags)
{
// Tell the image that it's data has been updated
nsIntRect r(0, 0, mDirEntry.mWidth, mDirEntry.mHeight);
nsresult rv = mImage->FrameUpdated(0, r);
mImage->DecodingComplete();
nsresult rv = NS_OK;
if (mObserver) {
mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
mObserver->OnStopFrame(nsnull, 0);
mObserver->OnStopContainer(nsnull, 0);
mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
mObserver = nsnull;
// Send notifications if appropriate
if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) &&
!mError && !(aFlags & CLOSE_FLAG_DONTNOTIFY)) {
// Tell the image that it's data has been updated
nsIntRect r(0, 0, mDirEntry.mWidth, mDirEntry.mHeight);
rv = mImage->FrameUpdated(0, r);
if (mObserver) {
mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
mObserver->OnStopFrame(nsnull, 0);
}
mImage->DecodingComplete();
if (mObserver) {
mObserver->OnStopContainer(nsnull, 0);
mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
}
}
mImage = nsnull;
mPos = 0;
delete[] mColors;
mColors = nsnull;
mCurLine = 0;
mRowBytes = 0;
@ -147,13 +157,29 @@ NS_METHOD nsICODecoder::ReadSegCb(nsIInputStream* aIn, void* aClosure,
const char* aFromRawSegment, PRUint32 aToOffset,
PRUint32 aCount, PRUint32 *aWriteCount) {
nsICODecoder *decoder = reinterpret_cast<nsICODecoder*>(aClosure);
// Always read everything
*aWriteCount = aCount;
return decoder->ProcessData(aFromRawSegment, aCount);
// Process
nsresult rv = decoder->ProcessData(aFromRawSegment, aCount);
// rvs might not propagate correctly. Set a flag before returning.
if (NS_FAILED(rv))
decoder->mError = PR_TRUE;
return rv;
}
NS_IMETHODIMP nsICODecoder::WriteFrom(nsIInputStream *aInStr, PRUint32 aCount, PRUint32 *aRetval)
NS_IMETHODIMP nsICODecoder::WriteFrom(nsIInputStream *aInStr, PRUint32 aCount)
{
return aInStr->ReadSegments(ReadSegCb, this, aCount, aRetval);
// Decode, watching for errors
nsresult rv = NS_OK;
PRUint32 ignored;
if (!mError)
rv = aInStr->ReadSegments(ReadSegCb, this, aCount, &ignored);
if (mError || NS_FAILED(rv))
return NS_ERROR_FAILURE;
return NS_OK;
}
nsresult nsICODecoder::ProcessData(const char* aBuffer, PRUint32 aCount) {
@ -240,10 +266,17 @@ nsresult nsICODecoder::ProcessData(const char* aBuffer, PRUint32 aCount) {
nsresult rv;
if (mPos == mImageOffset + BITMAPINFOSIZE) {
rv = mObserver->OnStartDecode(nsnull);
NS_ENSURE_SUCCESS(rv, rv);
ProcessInfoHeader();
rv = mImage->SetSize(mDirEntry.mWidth, mDirEntry.mHeight);
NS_ENSURE_SUCCESS(rv, rv);
if (mObserver) {
rv = mObserver->OnStartContainer(nsnull, mImage);
NS_ENSURE_SUCCESS(rv, rv);
}
if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY)
return NS_OK;
if (mBIH.bpp <= 8) {
switch (mBIH.bpp) {
case 1:
@ -264,9 +297,6 @@ nsresult nsICODecoder::ProcessData(const char* aBuffer, PRUint32 aCount) {
return NS_ERROR_OUT_OF_MEMORY;
}
rv = mImage->Init(mDirEntry.mWidth, mDirEntry.mHeight, mObserver);
NS_ENSURE_SUCCESS(rv, rv);
if (mIsCursor) {
nsCOMPtr<nsIProperties> props(do_QueryInterface(mImage));
if (props) {
@ -283,9 +313,6 @@ nsresult nsICODecoder::ProcessData(const char* aBuffer, PRUint32 aCount) {
}
}
rv = mObserver->OnStartContainer(nsnull, mImage);
NS_ENSURE_SUCCESS(rv, rv);
mCurLine = mDirEntry.mHeight;
mRow = (PRUint8*)malloc((mDirEntry.mWidth * mBIH.bpp)/8 + 4);
// +4 because the line is padded to a 4 bit boundary, but I don't want
@ -299,8 +326,10 @@ nsresult nsICODecoder::ProcessData(const char* aBuffer, PRUint32 aCount) {
gfxASurface::ImageFormatARGB32, (PRUint8**)&mImageData, &imageLength);
NS_ENSURE_SUCCESS(rv, rv);
mObserver->OnStartFrame(nsnull, 0);
NS_ENSURE_SUCCESS(rv, rv);
if (mObserver) {
mObserver->OnStartFrame(nsnull, 0);
NS_ENSURE_SUCCESS(rv, rv);
}
}
if (mColors && (mPos >= mImageOffset + BITMAPINFOSIZE) &&

View File

@ -21,6 +21,7 @@
*
* Contributor(s):
* David Hyatt <hyatt@netscape.com> (Original Author)
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -93,8 +94,9 @@ private:
PRUint32 CalcAlphaRowSize();
private:
nsCOMPtr<imgIDecoderObserver> mObserver;
nsCOMPtr<imgIContainer> mImage;
nsCOMPtr<imgIDecoderObserver> mObserver;
PRUint32 mFlags;
PRUint32 mPos;
PRUint16 mNumIcons;
@ -119,6 +121,7 @@ private:
PRPackedBool mHaveAlphaData;
PRPackedBool mIsCursor;
PRPackedBool mDecodingAndMask;
PRPackedBool mError;
};

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Chris Saari <saari@netscape.com>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -81,8 +82,6 @@ mailing address.
#include "nsIComponentManager.h"
#include "imgIContainerObserver.h"
#include "imgILoad.h"
#include "gfxColor.h"
#include "gfxPlatform.h"
#include "qcms.h"
@ -122,6 +121,7 @@ nsGIFDecoder2::nsGIFDecoder2()
, mLastFlushedPass(0)
, mGIFOpen(PR_FALSE)
, mSawTransparency(PR_FALSE)
, mError(PR_FALSE)
{
// Clear out the structure, excluding the arrays
memset(&mGIFStruct, 0, sizeof(mGIFStruct));
@ -129,7 +129,6 @@ nsGIFDecoder2::nsGIFDecoder2()
nsGIFDecoder2::~nsGIFDecoder2()
{
Close();
}
//******************************************************************************
@ -137,14 +136,22 @@ nsGIFDecoder2::~nsGIFDecoder2()
//******************************************************************************
//******************************************************************************
/* void init (in imgILoad aLoad); */
NS_IMETHODIMP nsGIFDecoder2::Init(imgILoad *aLoad)
/* void init (in imgIContainer aImage,
in imgIDecoderObserver aObsever,
in unsigned long aFlags); */
NS_IMETHODIMP nsGIFDecoder2::Init(imgIContainer *aImage,
imgIDecoderObserver *aObserver,
PRUint32 aFlags)
{
mObserver = do_QueryInterface(aLoad);
// Store parameters
mImageContainer = aImage;
mObserver = aObserver;
mFlags = aFlags;
// Fire OnStartDecode at init time to support bug 512435
if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && mObserver)
mObserver->OnStartDecode(nsnull);
mImageContainer = do_CreateInstance("@mozilla.org/image/container;2");
aLoad->SetImage(mImageContainer);
// Start with the version (GIF89a|GIF87a)
mGIFStruct.state = gif_type;
mGIFStruct.bytes_to_consume = 6;
@ -159,14 +166,20 @@ NS_IMETHODIMP nsGIFDecoder2::Init(imgILoad *aLoad)
//******************************************************************************
/* void close (); */
NS_IMETHODIMP nsGIFDecoder2::Close()
NS_IMETHODIMP nsGIFDecoder2::Close(PRUint32 aFlags)
{
if (mCurrentFrame == mGIFStruct.images_decoded)
EndImageFrame();
EndGIF();
// Send notifications if appropriate
if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) &&
!mError && !(aFlags & CLOSE_FLAG_DONTNOTIFY)) {
if (mCurrentFrame == mGIFStruct.images_decoded)
EndImageFrame();
EndGIF(/* aSuccess = */ PR_TRUE);
}
PR_FREEIF(mGIFStruct.local_colormap);
mImageContainer = nsnull;
return NS_OK;
}
@ -179,20 +192,44 @@ NS_IMETHODIMP nsGIFDecoder2::Flush()
//******************************************************************************
/* static callback from nsIInputStream::ReadSegments */
static NS_METHOD ReadDataOut(nsIInputStream* in,
void* closure,
const char* fromRawSegment,
PRUint32 toOffset,
PRUint32 count,
PRUint32 *writeCount)
NS_METHOD nsGIFDecoder2::ReadDataOut(nsIInputStream* in,
void* closure,
const char* fromRawSegment,
PRUint32 toOffset,
PRUint32 count,
PRUint32 *writeCount)
{
nsGIFDecoder2 *decoder = static_cast<nsGIFDecoder2*>(closure);
nsresult rv = decoder->ProcessData((unsigned char*)fromRawSegment, count, writeCount);
// Always read everything
*writeCount = count;
// Process
nsresult rv = decoder->ProcessData((unsigned char*)fromRawSegment, count);
// We do some fine-grained error control here. If we have at least one frame
// of an animated gif, we still want to display it (mostly for legacy reasons).
// libpr0n code is strict, so we have to lie and tell it we were successful. So
// if we have something to salvage, we send off final decode notifications, and
// pretend that we're decoded. Otherwise, we set mError.
if (NS_FAILED(rv)) {
*writeCount = 0;
return rv;
// Determine if we want to salvage the situation
PRUint32 numFrames = 0;
if (decoder->mImageContainer)
decoder->mImageContainer->GetNumFrames(&numFrames);
// If we're salvaging, send off notifications
if (numFrames > 1) { // XXXbholley - this is from the old code, but why not > 0?
decoder->EndGIF(/* aSuccess = */ PR_TRUE);
}
// Otherwise, set mError
else
decoder->mError = PR_TRUE;
}
// Necko is dubious with callbacks, so we don't use rvs to propagate errors.
return NS_OK;
}
@ -244,43 +281,36 @@ nsGIFDecoder2::FlushImageData()
}
//******************************************************************************
nsresult nsGIFDecoder2::ProcessData(unsigned char *data, PRUint32 count, PRUint32 *_retval)
nsresult nsGIFDecoder2::ProcessData(unsigned char *data, PRUint32 count)
{
// Push the data to the GIF decoder
nsresult rv = GifWrite(data, count);
NS_ENSURE_SUCCESS(rv, rv);
// Flushing is only needed for first frame
if (!mGIFStruct.images_decoded) {
rv = FlushImageData();
NS_ENSURE_SUCCESS(rv, rv);
mLastFlushedRow = mCurrentRow;
mLastFlushedPass = mCurrentPass;
}
*_retval = count;
return rv;
return NS_OK;
}
//******************************************************************************
/* unsigned long writeFrom (in nsIInputStream inStr, in unsigned long count); */
NS_IMETHODIMP nsGIFDecoder2::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
/* void writeFrom (in nsIInputStream inStr, in unsigned long count); */
NS_IMETHODIMP nsGIFDecoder2::WriteFrom(nsIInputStream *inStr, PRUint32 count)
{
nsresult rv = inStr->ReadSegments(ReadDataOut, this, count, _retval);
/* necko doesn't propagate the errors from ReadDataOut - take matters
into our own hands. if we have at least one frame of an animated
gif, then return success so we keep displaying as much as possible. */
if (mGIFStruct.state == gif_error || mGIFStruct.state == gif_oom) {
PRUint32 numFrames = 0;
if (mImageContainer)
mImageContainer->GetNumFrames(&numFrames);
if (numFrames <= 1)
rv = NS_ERROR_FAILURE;
}
return rv;
// Decode, watching for errors
nsresult rv = NS_OK;
PRUint32 ignored;
if (!mError)
rv = inStr->ReadSegments(nsGIFDecoder2::ReadDataOut, this,
count, &ignored);
if (mError || NS_FAILED(rv))
return NS_ERROR_FAILURE;
return NS_OK;
}
@ -293,31 +323,34 @@ void nsGIFDecoder2::BeginGIF()
{
if (mGIFOpen)
return;
if (mObserver)
mObserver->OnStartDecode(nsnull);
mImageContainer->Init(mGIFStruct.screen_width, mGIFStruct.screen_height, mObserver);
mGIFOpen = PR_TRUE;
mImageContainer->SetSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
if (mObserver)
mObserver->OnStartContainer(nsnull, mImageContainer);
mGIFOpen = PR_TRUE;
// If we're doing a header-only decode, we have what we came for
if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY)
return;
}
//******************************************************************************
void nsGIFDecoder2::EndGIF()
void nsGIFDecoder2::EndGIF(PRBool aSuccess)
{
if (!mGIFOpen)
return;
if (aSuccess)
mImageContainer->DecodingComplete();
if (mObserver) {
mObserver->OnStopContainer(nsnull, mImageContainer);
mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
mObserver->OnStopDecode(nsnull, aSuccess ? NS_OK : NS_ERROR_FAILURE,
nsnull);
}
mImageContainer->SetLoopCount(mGIFStruct.loop_count);
mImageContainer->DecodingComplete();
mGIFOpen = PR_FALSE;
}
@ -335,7 +368,10 @@ nsresult nsGIFDecoder2::BeginImageFrame(gfx_depth aDepth)
PRUint32 imgCurFrame;
mImageContainer->GetCurrentFrameIndex(&imgCurFrame);
nsIntRect r(0, 0, imgWidth, mGIFStruct.y_offset);
mObserver->OnDataAvailable(nsnull, imgCurFrame == PRUint32(mGIFStruct.images_decoded), &r);
if (mObserver)
mObserver->OnDataAvailable(nsnull,
imgCurFrame == PRUint32(mGIFStruct.images_decoded),
&r);
}
}
@ -391,10 +427,13 @@ void nsGIFDecoder2::EndImageFrame()
if (realFrameHeight < mGIFStruct.screen_height) {
PRUint32 imgCurFrame;
mImageContainer->GetCurrentFrameIndex(&imgCurFrame);
nsIntRect r(0, realFrameHeight,
mGIFStruct.screen_width,
mGIFStruct.screen_height - realFrameHeight);
mObserver->OnDataAvailable(nsnull, imgCurFrame == PRUint32(mGIFStruct.images_decoded), &r);
nsIntRect r(0, realFrameHeight,
mGIFStruct.screen_width,
mGIFStruct.screen_height - realFrameHeight);
if (mObserver)
mObserver->OnDataAvailable(nsnull,
imgCurFrame == PRUint32(mGIFStruct.images_decoded),
&r);
}
// This transparency check is only valid for first frame
if (mGIFStruct.is_transparent && !mSawTransparency) {
@ -1051,6 +1090,10 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
}
// Create the image container with the right size.
BeginGIF();
// If we were doing header-only, we're done
if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY)
return NS_OK;
}
/* Work around more broken GIF files that have zero image
@ -1182,12 +1225,12 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
break;
case gif_done:
EndGIF();
EndGIF(/* aSuccess = */ PR_TRUE);
return NS_OK;
break;
case gif_error:
EndGIF();
EndGIF(/* aSuccess = */ PR_FALSE);
return NS_ERROR_FAILURE;
break;
@ -1203,7 +1246,7 @@ nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len)
// if an error state is set but no data remains, code flow reaches here
if (mGIFStruct.state == gif_error) {
EndGIF();
EndGIF(/* aSuccess = */ PR_FALSE);
return NS_ERROR_FAILURE;
}

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Chris Saari <saari@netscape.com>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -67,14 +68,20 @@ public:
nsGIFDecoder2();
~nsGIFDecoder2();
nsresult ProcessData(unsigned char *data, PRUint32 count, PRUint32 *_retval);
nsresult ProcessData(unsigned char *data, PRUint32 count);
static NS_METHOD ReadDataOut(nsIInputStream* in,
void* closure,
const char* fromRawSegment,
PRUint32 toOffset,
PRUint32 count,
PRUint32 *writeCount);
private:
/* These functions will be called when the decoder has a decoded row,
* frame size information, etc. */
void BeginGIF();
void EndGIF();
void EndGIF(PRBool aSuccess);
nsresult BeginImageFrame(gfx_depth aDepth);
void EndImageFrame();
nsresult FlushImageData();
@ -87,7 +94,8 @@ private:
inline int ClearCode() const { return 1 << mGIFStruct.datasize; }
nsCOMPtr<imgIContainer> mImageContainer;
nsCOMPtr<imgIDecoderObserver> mObserver; // this is just qi'd from mRequest for speed
nsCOMPtr<imgIDecoderObserver> mObserver;
PRUint32 mFlags;
PRInt32 mCurrentRow;
PRInt32 mLastFlushedRow;
@ -105,6 +113,7 @@ private:
PRUint8 mColorMask; // Apply this to the pixel to keep within colormap
PRPackedBool mGIFOpen;
PRPackedBool mSawTransparency;
PRPackedBool mError;
gif_struct mGIFStruct;
};

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Scott MacGregor <mscott@netscape.com>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -41,13 +42,13 @@
#include "nsIInputStream.h"
#include "imgIContainer.h"
#include "imgIContainerObserver.h"
#include "imgILoad.h"
#include "nspr.h"
#include "nsIComponentManager.h"
#include "nsRect.h"
#include "nsComponentManagerUtils.h"
#include "nsIInterfaceRequestorUtils.h"
#include "ImageErrors.h"
NS_IMPL_THREADSAFE_ADDREF(nsIconDecoder)
NS_IMPL_THREADSAFE_RELEASE(nsIconDecoder)
@ -56,8 +57,20 @@ NS_INTERFACE_MAP_BEGIN(nsIconDecoder)
NS_INTERFACE_MAP_ENTRY(imgIDecoder)
NS_INTERFACE_MAP_END_THREADSAFE
nsIconDecoder::nsIconDecoder()
nsIconDecoder::nsIconDecoder() :
mImage(nsnull),
mObserver(nsnull),
mFlags(imgIDecoder::DECODER_FLAG_NONE),
mWidth(-1),
mHeight(-1),
mPixBytesRead(0),
mPixBytesTotal(0),
mImageData(nsnull),
mState(iconStateStart),
mNotifiedDone(PR_FALSE)
{
// Nothing to do
}
nsIconDecoder::~nsIconDecoder()
@ -66,29 +79,33 @@ nsIconDecoder::~nsIconDecoder()
/** imgIDecoder methods **/
NS_IMETHODIMP nsIconDecoder::Init(imgILoad *aLoad)
NS_IMETHODIMP nsIconDecoder::Init(imgIContainer *aImage,
imgIDecoderObserver *aObserver,
PRUint32 aFlags)
{
mObserver = do_QueryInterface(aLoad); // we're holding 2 strong refs to the request.
mImage = do_CreateInstance("@mozilla.org/image/container;2");
if (!mImage) return NS_ERROR_OUT_OF_MEMORY;
// Grab parameters
mImage = aImage;
mObserver = aObserver;
mFlags = aFlags;
aLoad->SetImage(mImage);
// Fire OnStartDecode at init time to support bug 512435
if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && mObserver)
mObserver->OnStartDecode(nsnull);
return NS_OK;
}
NS_IMETHODIMP nsIconDecoder::Close()
NS_IMETHODIMP nsIconDecoder::Close(PRUint32 aFlags)
{
mImage->DecodingComplete();
// If we haven't notified of completion yet for a full/success decode, we
// didn't finish. Notify in error mode
if (!(aFlags & CLOSE_FLAG_DONTNOTIFY) &&
!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) &&
!mNotifiedDone)
NotifyDone(/* aSuccess = */ PR_FALSE);
if (mObserver)
{
mObserver->OnStopFrame(nsnull, 0);
mObserver->OnStopContainer(nsnull, mImage);
mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
}
mImage = nsnull;
return NS_OK;
}
@ -97,51 +114,155 @@ NS_IMETHODIMP nsIconDecoder::Flush()
return NS_OK;
}
NS_IMETHODIMP nsIconDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
static nsresult
WriteIconData(nsIInputStream *aInStream, void *aClosure, const char *aFromSegment,
PRUint32 aToOffset, PRUint32 aCount, PRUint32 *aWriteCount)
{
// read the header from the input stram...
PRUint32 readLen;
PRUint8 header[2];
nsresult rv = inStr->Read((char*)header, 2, &readLen);
NS_ENSURE_TRUE(readLen == 2, NS_ERROR_UNEXPECTED); // w, h
count -= 2;
nsresult rv;
PRInt32 w = header[0];
PRInt32 h = header[1];
NS_ENSURE_TRUE(w > 0 && h > 0, NS_ERROR_UNEXPECTED);
// We always read everything
*aWriteCount = aCount;
if (mObserver)
mObserver->OnStartDecode(nsnull);
mImage->Init(w, h, mObserver);
if (mObserver)
mObserver->OnStartContainer(nsnull, mImage);
// We put this here to avoid errors about crossing initialization with case
// jumps on linux.
PRUint32 bytesToRead = 0;
PRUint32 imageLen;
PRUint8 *imageData;
// Grab the parameters
nsIconDecoder *decoder = static_cast<nsIconDecoder*>(aClosure);
rv = mImage->AppendFrame(0, 0, w, h, gfxASurface::ImageFormatARGB32, &imageData, &imageLen);
if (NS_FAILED(rv))
return rv;
// Performance isn't critical here, so our update rectangle is
// always the full icon
nsIntRect r(0, 0, decoder->mWidth, decoder->mHeight);
if (mObserver)
mObserver->OnStartFrame(nsnull, 0);
// Loop until the input data is gone
while (aCount > 0) {
switch (decoder->mState) {
case iconStateStart:
// Ensure that there enough in the inputStream
NS_ENSURE_TRUE(count >= imageLen, NS_ERROR_UNEXPECTED);
// Grab the width
decoder->mWidth = (PRUint8)*aFromSegment;
// Read the image data direct into the frame data
rv = inStr->Read((char*)imageData, imageLen, &readLen);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(readLen == imageLen, NS_ERROR_UNEXPECTED);
// Book Keeping
aFromSegment++;
aCount--;
decoder->mState = iconStateHaveHeight;
break;
// Notify the image...
nsIntRect r(0, 0, w, h);
rv = mImage->FrameUpdated(0, r);
if (NS_FAILED(rv))
return rv;
case iconStateHaveHeight:
mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
// Grab the Height
decoder->mHeight = (PRUint8)*aFromSegment;
// Set up the container and signal
decoder->mImage->SetSize(decoder->mWidth,
decoder->mHeight);
if (decoder->mObserver)
decoder->mObserver->OnStartContainer(nsnull, decoder->mImage);
// If We're doing a header-only decode, we're done
if (decoder->mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) {
decoder->mState = iconStateFinished;
break;
}
// Add the frame and signal
rv = decoder->mImage->AppendFrame(0, 0,
decoder->mWidth,
decoder->mHeight,
gfxASurface::ImageFormatARGB32,
&decoder->mImageData,
&decoder->mPixBytesTotal);
if (NS_FAILED(rv)) {
decoder->mState = iconStateError;
return rv;
}
if (decoder->mObserver)
decoder->mObserver->OnStartFrame(nsnull, 0);
// Book Keeping
aFromSegment++;
aCount--;
decoder->mState = iconStateReadPixels;
break;
case iconStateReadPixels:
// How many bytes are we reading?
bytesToRead = PR_MAX(aCount,
decoder->mPixBytesTotal - decoder->mPixBytesRead);
// Copy the bytes
memcpy(decoder->mImageData + decoder->mPixBytesRead,
aFromSegment, bytesToRead);
// Notify
rv = decoder->mImage->FrameUpdated(0, r);
if (NS_FAILED(rv)) {
decoder->mState = iconStateError;
return rv;
}
if (decoder->mObserver)
decoder->mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
// Book Keeping
aFromSegment += bytesToRead;
aCount -= bytesToRead;
decoder->mPixBytesRead += bytesToRead;
// If we've got all the pixel bytes, we're finished
if (decoder->mPixBytesRead == decoder->mPixBytesTotal) {
decoder->NotifyDone(/* aSuccess = */ PR_TRUE);
decoder->mState = iconStateFinished;
}
break;
case iconStateFinished:
// Consume all excess data silently
aCount = 0;
break;
case iconStateError:
return NS_IMAGELIB_ERROR_FAILURE;
break;
}
}
return NS_OK;
}
void
nsIconDecoder::NotifyDone(PRBool aSuccess)
{
// We should only call this once
NS_ABORT_IF_FALSE(!mNotifiedDone, "Calling NotifyDone twice");
// Notify
if (mObserver)
mObserver->OnStopFrame(nsnull, 0);
if (aSuccess)
mImage->DecodingComplete();
if (mObserver) {
mObserver->OnStopContainer(nsnull, mImage);
mObserver->OnStopDecode(nsnull, aSuccess ? NS_OK : NS_ERROR_FAILURE,
nsnull);
}
// Flag that we've notified
mNotifiedDone = PR_TRUE;
}
NS_IMETHODIMP nsIconDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count)
{
// Decode, watching for errors.
nsresult rv = NS_OK;
PRUint32 ignored;
if (mState != iconStateError)
rv = inStr->ReadSegments(WriteIconData, this, count, &ignored);
if ((mState == iconStateError) || NS_FAILED(rv))
return NS_ERROR_FAILURE;
return NS_OK;
}

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Scott MacGregor <mscott@netscape.com>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -84,9 +85,27 @@ public:
nsIconDecoder();
virtual ~nsIconDecoder();
private:
nsCOMPtr<imgIContainer> mImage;
nsCOMPtr<imgIDecoderObserver> mObserver; // this is just qi'd from mRequest for speed
nsCOMPtr<imgIDecoderObserver> mObserver;
PRUint32 mFlags;
PRUint8 mWidth;
PRUint8 mHeight;
PRUint32 mPixBytesRead;
PRUint32 mPixBytesTotal;
PRUint8* mImageData;
PRUint32 mState;
PRBool mNotifiedDone;
void NotifyDone(PRBool aSuccess);
};
enum {
iconStateStart = 0,
iconStateHaveHeight = 1,
iconStateReadPixels = 2,
iconStateFinished = 3,
iconStateError = 4
};
#endif // nsIconDecoder_h__

View File

@ -97,7 +97,7 @@ static const nsModuleComponentInfo components[] =
#ifdef USE_ICON_DECODER
{ "icon decoder",
NS_ICONDECODER_CID,
"@mozilla.org/image/decoder;2?type=image/icon",
"@mozilla.org/image/decoder;3?type=image/icon",
nsIconDecoderConstructor,
IconDecoderRegisterProc,
IconDecoderUnregisterProc, },

View File

@ -23,6 +23,7 @@
* Contributor(s):
* Stuart Parmenter <stuart@mozilla.com>
* Federico Mena-Quintero <federico@novell.com>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -99,7 +100,8 @@ nsJPEGDecoder::nsJPEGDecoder()
{
mState = JPEG_HEADER;
mReading = PR_TRUE;
mError = NS_OK;
mNotifiedDone = PR_FALSE;
mError = PR_FALSE;
mImageData = nsnull;
mBytesToSkip = 0;
@ -137,11 +139,22 @@ nsJPEGDecoder::~nsJPEGDecoder()
/** imgIDecoder methods **/
/* void init (in imgILoad aLoad); */
NS_IMETHODIMP nsJPEGDecoder::Init(imgILoad *aLoad)
/* void init (in imgIContainer aImage,
in imgIDecoderObserver aObserver,
in unsigned long aFlags); */
NS_IMETHODIMP nsJPEGDecoder::Init(imgIContainer *aImage,
imgIDecoderObserver *aObserver,
PRUint32 aFlags)
{
mImageLoad = aLoad;
mObserver = do_QueryInterface(aLoad);
/* Grab the parameters. */
mImage = aImage;
mObserver = aObserver;
mFlags = aFlags;
/* Fire OnStartDecode at init time to support bug 512435 */
if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && mObserver)
mObserver->OnStartDecode(nsnull);
/* We set up the normal JPEG error routines, then override error_exit. */
mInfo.err = jpeg_std_error(&mErr.pub);
@ -173,40 +186,12 @@ NS_IMETHODIMP nsJPEGDecoder::Init(imgILoad *aLoad)
for (PRUint32 m = 0; m < 16; m++)
jpeg_save_markers(&mInfo, JPEG_APP0 + m, 0xFFFF);
/* Check if the request already has an image container.
* this is the case when multipart/x-mixed-replace is being downloaded
* if we already have one and it has the same width and height, reuse it.
* This is also the case when an existing container is reloading itself from
* us.
*
* If we have a mismatch in width/height for the container later on we will
* generate an error.
*/
mImageLoad->GetImage(getter_AddRefs(mImage));
if (!mImage) {
mImage = do_CreateInstance("@mozilla.org/image/container;2");
if (!mImage)
return NS_ERROR_OUT_OF_MEMORY;
mImageLoad->SetImage(mImage);
nsresult result = mImage->SetDiscardable("image/jpeg");
if (NS_FAILED(result)) {
mState = JPEG_ERROR;
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
(" (could not set image container to discardable)"));
return result;
}
}
return NS_OK;
}
/* void close (); */
NS_IMETHODIMP nsJPEGDecoder::Close()
NS_IMETHODIMP nsJPEGDecoder::Close(PRUint32 aFlags)
{
PR_LOG(gJPEGlog, PR_LOG_DEBUG,
("[this=%p] nsJPEGDecoder::Close\n", this));
@ -216,12 +201,19 @@ NS_IMETHODIMP nsJPEGDecoder::Close()
jpeg_destroy_decompress(&mInfo);
if (mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER) {
NS_WARNING("Never finished decoding the JPEG.");
/* Tell imgLoader that image decoding has failed */
return NS_ERROR_FAILURE;
}
/* If we already know we're in an error state, don't
bother flagging another one here. */
if (mError)
return NS_OK;
/* If we're doing a full decode and haven't notified of completion yet,
* we must not have got everything we wanted. Send error notifications. */
if (!(aFlags & CLOSE_FLAG_DONTNOTIFY) &&
!(mFlags && imgIDecoder::DECODER_FLAG_HEADERONLY) &&
!mNotifiedDone)
NotifyDone(/* aSuccess = */ PR_FALSE);
/* Otherwise, no problems. */
return NS_OK;
}
@ -230,9 +222,8 @@ NS_IMETHODIMP nsJPEGDecoder::Flush()
{
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::Flush");
PRUint32 ret;
if (mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER && mState != JPEG_ERROR)
return this->ProcessData(nsnull, 0, &ret);
return this->ProcessData(nsnull, 0);
return NS_OK;
}
@ -245,57 +236,42 @@ static NS_METHOD ReadDataOut(nsIInputStream* in,
PRUint32 *writeCount)
{
nsJPEGDecoder *decoder = static_cast<nsJPEGDecoder*>(closure);
nsresult rv = decoder->ProcessData(fromRawSegment, count, writeCount);
if (NS_FAILED(rv)) {
/* Tell imgLoader that image decoding has failed */
// We always read everything
*writeCount = count;
// Process some data
nsresult rv = decoder->ProcessData(fromRawSegment, count);
// Necko's error propagation is dubious - use an explicit flag
if (NS_FAILED(rv))
decoder->mError = rv;
*writeCount = 0;
}
return NS_OK;
}
/* unsigned long writeFrom (in nsIInputStream inStr, in unsigned long count); */
NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *writeCount)
{
NS_ENSURE_ARG_POINTER(inStr);
NS_ENSURE_ARG_POINTER(writeCount);
/* necko doesn't propagate the errors from ReadDataOut */
nsresult rv = inStr->ReadSegments(ReadDataOut, this, count, writeCount);
if (NS_FAILED(mError)) {
/* Tell imgLoader that image decoding has failed */
rv = NS_ERROR_FAILURE;
}
return rv;
}
/* void writeFrom (in nsIInputStream inStr, in unsigned long count); */
NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count)
{
NS_ENSURE_ARG_POINTER(inStr);
// Decode, watching for errors
nsresult rv = NS_OK;
PRUint32 ignored;
if (!mError)
rv = inStr->ReadSegments(ReadDataOut, this, count, &ignored);
if (mError || NS_FAILED(rv))
return NS_ERROR_FAILURE;
return NS_OK;
}
//******************************************************************************
nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count, PRUint32 *writeCount)
nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count)
{
LOG_SCOPE_WITH_PARAM(gJPEGlog, "nsJPEGDecoder::ProcessData", "count", count);
mSegment = (const JOCTET *)data;
mSegmentLen = count;
*writeCount = count;
if (data && count) {
nsresult result = mImage->AddRestoreData((char *) data, count);
if (NS_FAILED(result)) {
mState = JPEG_ERROR;
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
("} (could not add restore data)"));
return result;
}
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
(" added %u bytes to restore data",
count));
}
// else no input stream.. Flush() ?
/* Return here if there is a fatal error. */
nsresult error_code;
@ -332,6 +308,16 @@ nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count, PRUint32 *
return NS_OK; /* I/O suspension */
}
/* Set Width and height, and notify that the container is ready to go. */
mImage->SetSize(mInfo.image_width, mInfo.image_height);
if (mObserver)
mObserver->OnStartContainer(nsnull, mImage);
/* If we're doing a header-only decode, we're done. */
if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY)
return NS_OK;
/* We're doing a full decode. */
JOCTET *profile;
PRUint32 profileLength;
eCMSMode cmsMode = gfxPlatform::GetCMSMode();
@ -453,25 +439,6 @@ nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count, PRUint32 *
/* Used to set up image size so arrays can be allocated */
jpeg_calc_output_dimensions(&mInfo);
mObserver->OnStartDecode(nsnull);
/* verify that the width and height of the image are the same as
* the container we're about to put things in to.
* XXX it might not matter maybe we should just resize the image.
*/
PRInt32 width, height;
mImage->GetWidth(&width);
mImage->GetHeight(&height);
if (width == 0 && height == 0) {
mImage->Init(mInfo.image_width, mInfo.image_height, mObserver);
} else if ((width != (PRInt32)mInfo.image_width) || (height != (PRInt32)mInfo.image_height)) {
mState = JPEG_ERROR;
return NS_ERROR_UNEXPECTED;
}
mImage->Init(mInfo.image_width, mInfo.image_height, mObserver);
mObserver->OnStartContainer(nsnull, mImage);
// Use EnsureCleanFrame so we don't create a new frame if we're being
// reused for e.g. multipart/x-replace
@ -489,7 +456,8 @@ nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count, PRUint32 *
(" JPEGDecoderAccounting: nsJPEGDecoder::ProcessData -- created image frame with %ux%u pixels",
mInfo.image_width, mInfo.image_height));
mObserver->OnStartFrame(nsnull, 0);
if (mObserver)
mObserver->OnStartFrame(nsnull, 0);
mState = JPEG_START_DECOMPRESS;
}
@ -621,8 +589,6 @@ nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count, PRUint32 *
case JPEG_DONE:
{
nsresult result;
LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::ProcessData -- entering JPEG_DONE case");
/* Step 7: Finish decompression */
@ -635,14 +601,6 @@ nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count, PRUint32 *
mState = JPEG_SINK_NON_JPEG_TRAILER;
result = mImage->RestoreDataDone();
if (NS_FAILED (result)) {
mState = JPEG_ERROR;
PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
("} (could not mark image container with RestoreDataDone)"));
return result;
}
/* we're done dude */
break;
}
@ -664,6 +622,26 @@ nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count, PRUint32 *
return NS_OK;
}
void
nsJPEGDecoder::NotifyDone(PRBool aSuccess)
{
// We should only be called once
NS_ABORT_IF_FALSE(!mNotifiedDone, "calling NotifyDone twice!");
// Notify
if (mObserver)
mObserver->OnStopFrame(nsnull, 0);
if (aSuccess)
mImage->DecodingComplete();
if (mObserver) {
mObserver->OnStopContainer(nsnull, mImage);
mObserver->OnStopDecode(nsnull, aSuccess ? NS_OK : NS_ERROR_FAILURE,
nsnull);
}
// Mark that we've been called
mNotifiedDone = PR_TRUE;
}
nsresult
nsJPEGDecoder::OutputScanlines(PRBool* suspend)
@ -759,7 +737,8 @@ nsJPEGDecoder::OutputScanlines(PRBool* suspend)
if (top != mInfo.output_scanline) {
nsIntRect r(0, top, mInfo.output_width, mInfo.output_scanline-top);
rv = mImage->FrameUpdated(0, r);
mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
if (mObserver)
mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
}
return rv;
@ -976,17 +955,12 @@ term_source (j_decompress_ptr jd)
{
nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data);
if (decoder->mObserver) {
decoder->mObserver->OnStopFrame(nsnull, 0);
decoder->mObserver->OnStopContainer(nsnull, decoder->mImage);
decoder->mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
}
// This function shouldn't be called if we ran into an error
NS_ABORT_IF_FALSE(!decoder->mError,
"Calling term_source on a JPEG with mError=true!");
PRBool multipart = PR_FALSE;
if (decoder->mImageLoad)
decoder->mImageLoad->GetIsMultiPartChannel(&multipart);
if (!multipart)
decoder->mImage->DecodingComplete();
// Notify
decoder->NotifyDone(/* aSuccess = */ PR_TRUE);
}

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.com>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -46,7 +47,6 @@
#include "imgIContainer.h"
#include "imgIDecoderObserver.h"
#include "imgILoad.h"
#include "nsIInputStream.h"
#include "nsIPipe.h"
#include "qcms.h"
@ -90,23 +90,24 @@ public:
nsJPEGDecoder();
virtual ~nsJPEGDecoder();
nsresult ProcessData(const char *data, PRUint32 count, PRUint32 *writeCount);
nsresult ProcessData(const char *data, PRUint32 count);
void NotifyDone(PRBool aSuccess);
protected:
nsresult OutputScanlines(PRBool* suspend);
public:
nsCOMPtr<imgIContainer> mImage;
nsCOMPtr<imgILoad> mImageLoad;
nsCOMPtr<imgIDecoderObserver> mObserver;
PRUint32 mFlags;
PRUint8 *mImageData;
struct jpeg_decompress_struct mInfo;
struct jpeg_source_mgr mSourceMgr;
decoder_error_mgr mErr;
jstate mState;
nsresult mError;
PRBool mError;
PRUint32 mBytesToSkip;
@ -125,6 +126,7 @@ public:
qcms_transform *mTransform;
PRPackedBool mReading;
PRPackedBool mNotifiedDone;
};
#endif // nsJPEGDecoder_h__

View File

@ -24,6 +24,7 @@
* Stuart Parmenter <stuart@mozilla.com>
* Andrew Smith
* Federico Mena-Quintero <federico@novell.com>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -71,13 +72,29 @@ static PRLogModuleInfo *gPNGLog = PR_NewLogModule("PNGDecoder");
static PRLogModuleInfo *gPNGDecoderAccountingLog = PR_NewLogModule("PNGDecoderAccounting");
#endif
/* limit image dimensions (bug #251381) */
#define MOZ_PNG_MAX_DIMENSION 1000000L
// For header-only decodes
#define WIDTH_OFFSET 16
#define HEIGHT_OFFSET (WIDTH_OFFSET + 4)
#define BYTES_NEEDED_FOR_DIMENSIONS (HEIGHT_OFFSET + 4)
// This is defined in the PNG spec as an invariant. We use it to
// do manual validation without libpng.
static const PRUint8 pngSignatureBytes[] =
{ 137, 80, 78, 71, 13, 10, 26, 10 };
NS_IMPL_ISUPPORTS1(nsPNGDecoder, imgIDecoder)
nsPNGDecoder::nsPNGDecoder() :
mPNG(nsnull), mInfo(nsnull),
mCMSLine(nsnull), interlacebuf(nsnull),
mInProfile(nsnull), mTransform(nsnull),
mChannels(0), mError(PR_FALSE), mFrameIsHidden(PR_FALSE)
mHeaderBuf(nsnull), mHeaderBytesRead(0),
mChannels(0), mError(PR_FALSE), mFrameIsHidden(PR_FALSE),
mNotifiedDone(PR_FALSE)
{
}
@ -94,6 +111,8 @@ nsPNGDecoder::~nsPNGDecoder()
if (mTransform)
qcms_transform_release(mTransform);
}
if (mHeaderBuf)
nsMemory::Free(mHeaderBuf);
}
// CreateFrame() is used for both simple and animated images
@ -191,7 +210,8 @@ void nsPNGDecoder::EndImageFrame()
}
PRUint32 curFrame;
mImage->GetCurrentFrameIndex(&curFrame);
mObserver->OnDataAvailable(nsnull, curFrame == numFrames - 1, &mFrameRect);
if (mObserver)
mObserver->OnDataAvailable(nsnull, curFrame == numFrames - 1, &mFrameRect);
}
mImage->EndFrameDecode(numFrames - 1);
@ -202,8 +222,12 @@ void nsPNGDecoder::EndImageFrame()
/** imgIDecoder methods **/
/* void init (in imgILoad aLoad); */
NS_IMETHODIMP nsPNGDecoder::Init(imgILoad *aLoad)
/* void init (in imgIContainer aImage,
imgIDecoderObserver aObserver,
unsigned long aFlags); */
NS_IMETHODIMP nsPNGDecoder::Init(imgIContainer *aImage,
imgIDecoderObserver *aObserver,
PRUint32 aFlags)
{
#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
static png_byte color_chunks[]=
@ -224,10 +248,23 @@ NS_IMETHODIMP nsPNGDecoder::Init(imgILoad *aLoad)
122, 84, 88, 116, '\0'}; /* zTXt */
#endif
mImageLoad = aLoad;
mObserver = do_QueryInterface(aLoad); // we're holding 2 strong refs to the request.
mImage = aImage;
mObserver = aObserver;
mFlags = aFlags;
/* do png init stuff */
// Fire OnStartDecode at init time to support bug 512435
if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && mObserver)
mObserver->OnStartDecode(nsnull);
// For header-only decodes, we only need a small buffer
if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) {
mHeaderBuf = (PRUint8 *)nsMemory::Alloc(BYTES_NEEDED_FOR_DIMENSIONS);
if (!mHeaderBuf)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
/* For full decodes, do png init stuff */
/* Initialize the container's source image header. */
/* Always decode to 24 bit pixdepth */
@ -258,48 +295,23 @@ NS_IMETHODIMP nsPNGDecoder::Init(imgILoad *aLoad)
info_callback, row_callback, end_callback);
/* The image container may already exist if it is reloading itself from us.
* Check that it has the same width/height; otherwise create a new container.
*/
mImageLoad->GetImage(getter_AddRefs(mImage));
if (!mImage) {
mImage = do_CreateInstance("@mozilla.org/image/container;2");
if (!mImage)
return NS_ERROR_OUT_OF_MEMORY;
mImageLoad->SetImage(mImage);
if (NS_FAILED(mImage->SetDiscardable("image/png"))) {
PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
("PNGDecoderAccounting: info_callback(): failed to set image container %p as discardable",
mImage.get()));
return NS_ERROR_FAILURE;
}
}
return NS_OK;
}
/* void close (); */
NS_IMETHODIMP nsPNGDecoder::Close()
NS_IMETHODIMP nsPNGDecoder::Close(PRUint32 aFlags)
{
if (mPNG)
png_destroy_read_struct(&mPNG, mInfo ? &mInfo : NULL, NULL);
if (mImage) { // mImage could be null in the case of an error
nsresult result = mImage->RestoreDataDone();
if (NS_FAILED(result)) {
PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
("PNGDecoderAccounting: nsPNGDecoder::Close(): failure in RestoreDataDone() for image container %p",
mImage.get()));
// If we're a full/success decode but haven't sent stop notifications yet,
// we didn't get all the data we needed. Send error notifications.
if (!(aFlags & CLOSE_FLAG_DONTNOTIFY) &&
!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) &&
!mNotifiedDone)
NotifyDone(/* aSuccess = */ PR_FALSE);
mError = PR_TRUE;
return result;
}
PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
("PNGDecoderAccounting: nsPNGDecoder::Close(): image container %p is now with RestoreDataDone",
mImage.get()));
}
mImage = nsnull;
return NS_OK;
}
@ -309,6 +321,99 @@ NS_IMETHODIMP nsPNGDecoder::Flush()
return NS_OK;
}
// We make this a method to get the benefit of the 'this' parameter
NS_METHOD
nsPNGDecoder::ProcessData(unsigned char* aBuffer, PRUint32 aCount)
{
// We use gotos, so we need to declare variables here
nsresult rv;
PRUint32 width = 0;
PRUint32 height = 0;
// No forgiveness if we previously hit an error
if (mError)
goto error;
// If we only want width/height, we don't need to go through libpng
if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) {
// Are we done?
if (mHeaderBytesRead == BYTES_NEEDED_FOR_DIMENSIONS)
return NS_OK;
// Read data into our header buffer
PRUint32 bytesToRead = PR_MIN(aCount, BYTES_NEEDED_FOR_DIMENSIONS - mHeaderBytesRead);
memcpy(mHeaderBuf + mHeaderBytesRead, aBuffer, bytesToRead);
mHeaderBytesRead += bytesToRead;
// If we're done now, verify the data and set up the container
if (mHeaderBytesRead == BYTES_NEEDED_FOR_DIMENSIONS) {
// Check that the signature bytes are right
if (memcmp(mHeaderBuf, pngSignatureBytes, sizeof(pngSignatureBytes)))
goto error;
// Grab the width and height, accounting for endianness (thanks libpng!)
width = png_get_uint_32(mHeaderBuf + WIDTH_OFFSET);
height = png_get_uint_32(mHeaderBuf + HEIGHT_OFFSET);
// Too big?
if ((width > MOZ_PNG_MAX_DIMENSION) || (height > MOZ_PNG_MAX_DIMENSION))
goto error;
// Set the size
rv = mImage->SetSize(width, height);
if (NS_FAILED(rv))
goto error;
// Notify the observer that the container is up
if (mObserver)
mObserver->OnStartContainer(nsnull, mImage);
}
}
// Otherwise, we're doing a standard decode
else {
// libpng uses setjmp/longjmp for error handling - set the buffer
if (setjmp(mPNG->jmpbuf)) {
png_destroy_read_struct(&mPNG, &mInfo, NULL);
goto error;
}
// Pass the data off to libpng
png_process_data(mPNG, mInfo, aBuffer, aCount);
}
return NS_OK;
// Consolidate error handling
error:
mError = PR_TRUE;
return NS_ERROR_FAILURE;
}
void
nsPNGDecoder::NotifyDone(PRBool aSuccess)
{
// We should only be called once
NS_ABORT_IF_FALSE(!mNotifiedDone, "Calling NotifyDone twice!");
// Notify
if (!mFrameIsHidden)
EndImageFrame();
if (aSuccess)
mImage->DecodingComplete();
if (mObserver) {
mObserver->OnStopContainer(nsnull, mImage);
mObserver->OnStopDecode(nsnull, aSuccess ? NS_OK : NS_ERROR_FAILURE,
nsnull);
}
// Mark that we've been called
mNotifiedDone = PR_TRUE;
}
static NS_METHOD ReadDataOut(nsIInputStream* in,
void* closure,
@ -317,60 +422,32 @@ static NS_METHOD ReadDataOut(nsIInputStream* in,
PRUint32 count,
PRUint32 *writeCount)
{
// Grab the decoder
NS_ENSURE_ARG_POINTER(closure);
nsPNGDecoder *decoder = static_cast<nsPNGDecoder*>(closure);
if (decoder->mError) {
*writeCount = 0;
return NS_ERROR_FAILURE;
}
// we force to add even erroneous data to restore halfway frame information
// later - bug 441563
nsresult result = decoder->mImage->AddRestoreData(const_cast<char *>(fromRawSegment), count);
if (NS_FAILED (result)) {
PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
("PNGDecoderAccounting: ReadDataOut(): failed to add restore data to image container %p",
decoder->mImage.get()));
decoder->mError = PR_TRUE;
*writeCount = 0;
return result;
}
// we need to do the setjmp here otherwise bad things will happen
if (setjmp(decoder->mPNG->jmpbuf)) {
png_destroy_read_struct(&decoder->mPNG, &decoder->mInfo, NULL);
decoder->mError = PR_TRUE;
*writeCount = 0;
return NS_ERROR_FAILURE;
}
png_process_data(decoder->mPNG, decoder->mInfo,
reinterpret_cast<unsigned char *>(const_cast<char *>(fromRawSegment)), count);
PR_LOG(gPNGDecoderAccountingLog, PR_LOG_DEBUG,
("PNGDecoderAccounting: ReadDataOut(): Added restore data to image container %p",
decoder->mImage.get()));
// We always read everything
*writeCount = count;
return NS_OK;
// Twiddle the types, then process the data
char *unConst = const_cast<char *>(fromRawSegment);
unsigned char *buffer = reinterpret_cast<unsigned char *>(unConst);
return decoder->ProcessData(buffer, count);
}
/* unsigned long writeFrom (in nsIInputStream inStr, in unsigned long count); */
NS_IMETHODIMP nsPNGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
/* writeFrom (in nsIInputStream inStr, in unsigned long count); */
NS_IMETHODIMP nsPNGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count)
{
NS_ASSERTION(inStr, "Got a null input stream!");
nsresult rv;
// Decode, watching for errors
nsresult rv = NS_OK;
PRUint32 ignored;
if (!mError)
rv = inStr->ReadSegments(ReadDataOut, this, count, _retval);
if (mError) {
*_retval = 0;
rv = inStr->ReadSegments(ReadDataOut, this, count, &ignored);
if (mError || NS_FAILED(rv))
rv = NS_ERROR_FAILURE;
}
return rv;
}
@ -505,16 +582,23 @@ info_callback(png_structp png_ptr, png_infop info_ptr)
int num_trans = 0;
nsPNGDecoder *decoder = static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
nsresult rv;
/* always decode to 24-bit RGB or 32-bit RGBA */
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
&interlace_type, &compression_type, &filter_type);
/* limit image dimensions (bug #251381) */
#define MOZ_PNG_MAX_DIMENSION 1000000L
/* Are we too big? */
if (width > MOZ_PNG_MAX_DIMENSION || height > MOZ_PNG_MAX_DIMENSION)
longjmp(decoder->mPNG->jmpbuf, 1);
#undef MOZ_PNG_MAX_DIMENSION
// Set the size and notify that the container is set up
rv = decoder->mImage->SetSize(width, height);
if (NS_FAILED(rv)) {
longjmp(decoder->mPNG->jmpbuf, 5); // NS_ERROR_UNEXPECTED
}
if (decoder->mObserver)
decoder->mObserver->OnStartContainer(nsnull, decoder->mImage);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_expand(png_ptr);
@ -616,25 +700,6 @@ info_callback(png_structp png_ptr, png_infop info_ptr)
}
}
if (decoder->mObserver)
decoder->mObserver->OnStartDecode(nsnull);
/* The image container may already exist if it is reloading itself from us.
* Check that it has the same width/height; otherwise create a new container.
*/
PRInt32 containerWidth, containerHeight;
decoder->mImage->GetWidth(&containerWidth);
decoder->mImage->GetHeight(&containerHeight);
if (containerWidth == 0 && containerHeight == 0) {
// the image hasn't been inited yet
decoder->mImage->Init(width, height, decoder->mObserver);
} else if (containerWidth != PRInt32(width) || containerHeight != PRInt32(height)) {
longjmp(decoder->mPNG->jmpbuf, 5); // NS_ERROR_UNEXPECTED
}
if (decoder->mObserver)
decoder->mObserver->OnStartContainer(nsnull, decoder->mImage);
if (channels == 1 || channels == 3)
decoder->format = gfxASurface::ImageFormatRGB24;
else if (channels == 2 || channels == 4)
@ -654,8 +719,9 @@ info_callback(png_structp png_ptr, png_infop info_ptr)
PRUint32 bpp[] = { 0, 3, 4, 3, 4 };
decoder->mCMSLine =
(PRUint8 *)nsMemory::Alloc(bpp[channels] * width);
if (!decoder->mCMSLine)
if (!decoder->mCMSLine) {
longjmp(decoder->mPNG->jmpbuf, 5); // NS_ERROR_OUT_OF_MEMORY
}
}
if (interlace_type == PNG_INTERLACE_ADAM7) {
@ -800,7 +866,8 @@ row_callback(png_structp png_ptr, png_bytep new_row,
}
PRUint32 curFrame;
decoder->mImage->GetCurrentFrameIndex(&curFrame);
decoder->mObserver->OnDataAvailable(nsnull, curFrame == numFrames - 1, &r);
if (decoder->mObserver)
decoder->mObserver->OnDataAvailable(nsnull, curFrame == numFrames - 1, &r);
}
}
}
@ -844,21 +911,17 @@ end_callback(png_structp png_ptr, png_infop info_ptr)
*/
nsPNGDecoder *decoder = static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
// We shouldn't get here if we've hit an error
NS_ABORT_IF_FALSE(!decoder->mError, "Finishing up PNG but hit error!");
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) {
PRInt32 num_plays = png_get_num_plays(png_ptr, info_ptr);
decoder->mImage->SetLoopCount(num_plays - 1);
}
if (!decoder->mFrameIsHidden)
decoder->EndImageFrame();
decoder->mImage->DecodingComplete();
if (decoder->mObserver) {
decoder->mObserver->OnStopContainer(nsnull, decoder->mImage);
decoder->mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
}
// Send final notifications
decoder->NotifyDone(/* aSuccess = */ PR_TRUE);
}

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.com>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -44,7 +45,6 @@
#include "imgIContainer.h"
#include "imgIDecoderObserver.h"
#include "imgILoad.h"
#include "gfxASurface.h"
#include "nsCOMPtr.h"
@ -76,11 +76,13 @@ public:
void SetAnimFrameInfo();
void EndImageFrame();
NS_METHOD ProcessData(unsigned char* aBuffer, PRUint32 aCount);
void NotifyDone(PRBool aSuccess);
public:
nsCOMPtr<imgIContainer> mImage;
nsCOMPtr<imgILoad> mImageLoad;
nsCOMPtr<imgIDecoderObserver> mObserver; // this is just qi'd from mRequest for speed
nsCOMPtr<imgIDecoderObserver> mObserver;
PRUint32 mFlags;
png_structp mPNG;
png_infop mInfo;
@ -92,10 +94,16 @@ public:
qcms_transform *mTransform;
gfxASurface::gfxImageFormat format;
// For header-only decodes
PRUint8 *mHeaderBuf;
PRUint32 mHeaderBytesRead;
PRUint8 mChannels;
PRPackedBool mError;
PRPackedBool mFrameHasNoAlpha;
PRPackedBool mFrameIsHidden;
PRPackedBool mNotifiedDone;
};
#endif // nsPNGDecoder_h__

View File

@ -54,7 +54,6 @@ XPIDLSRCS = \
imgIDecoder.idl \
imgIDecoderObserver.idl \
imgIEncoder.idl \
imgILoad.idl \
imgILoader.idl \
imgIRequest.idl \
imgITools.idl \

View File

@ -24,6 +24,7 @@
* Stuart Parmenter <pavlov@netscape.com>
* Federico Mena-Quintero <federico@novell.com>
* Joe Drew <joe@drew.ca>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -41,7 +42,7 @@
#include "nsISupports.idl"
interface imgIContainerObserver;
interface imgIDecoderObserver;
%{C++
#include "gfxImageSurface.h"
@ -70,7 +71,7 @@ native gfxGraphicsFilter(gfxPattern::GraphicsFilter);
*
* Internally, imgIContainer also manages animation of images.
*/
[scriptable, uuid(1bcf7a25-1356-47a8-bf80-e284989ea38f)]
[scriptable, uuid(74c63935-b54f-43a4-9449-2e9c815e3bef)]
interface imgIContainer : nsISupports
{
/**
@ -95,25 +96,64 @@ interface imgIContainer : nsISupports
readonly attribute boolean currentFrameIsOpaque;
/**
* Get a surface for the current frame. This may be a platform-native,
* optimized frame, so you cannot inspect its pixel data.
* Flags for imgIContainer operations.
*
* Meanings:
*
* FLAG_NONE: Lack of flags
*
* FLAG_SYNC_DECODE: Forces synchronous/non-progressive decode of all
* available data before the call returns.
*/
[noscript] readonly attribute gfxASurface currentFrame;
const long FLAG_NONE = 0x0;
const long FLAG_SYNC_DECODE = 0x1;
/**
* Create and return a new copy of the current frame that you can write to
* and otherwise inspect the pixels of.
* Constants for specifying various "special" frames.
*
* FRAME_FIRST: The first frame
* FRAME_CURRENT: The current frame
*
* FRAME_MAX_VALUE should be set to the value of the maximum constant above,
* as it is used for ensuring that a valid value was passed in.
*/
const unsigned long FRAME_FIRST = 0;
const unsigned long FRAME_CURRENT = 1;
const unsigned long FRAME_MAX_VALUE = 1;
/**
* Get a surface for the given frame. This may be a platform-native,
* optimized surface, so you cannot inspect its pixel data.
*
* @param aWhichFrame Frame specifier of the FRAME_* variety.
* @param aFlags Flags of the FLAG_* variety
*/
[noscript] gfxImageSurface copyCurrentFrame();
[noscript] gfxASurface getFrame(in PRUint32 aWhichFrame,
in PRUint32 aFlags);
/**
* Create and return a new copy of the given frame that you can write to
* and otherwise inspect the pixels of.
*
* @param aWhichFrame Frame specifier of the FRAME_* variety.
* @param aFlags Flags of the FLAG_* variety
*/
[noscript] gfxImageSurface copyFrame(in PRUint32 aWhichFrame,
in PRUint32 aFlags);
/**
* Create a new imgContainer that contains only a single frame, which itself
* contains a subregion of the current frame.
* contains a subregion of the given frame.
*
* @param aWhichFrame Frame specifier of the FRAME_* variety.
* @param aRect the area of the current frame to be duplicated in the
* returned imgContainer's frame.
* @param aFlags Flags of the FLAG_* variety
*/
[noscript] imgIContainer extractCurrentFrame([const] in nsIntRect aRect);
[noscript] imgIContainer extractFrame(in PRUint32 aWhichFrame,
[const] in nsIntRect aRect,
in PRUint32 aFlags);
/**
* Draw the current frame on to the context specified.
@ -126,26 +166,76 @@ interface imgIContainer : nsISupports
* automatically tiled as necessary.
* @param aSubimage The area of the image, in pixels, that we are allowed to
* sample from.
* @param aFlags Flags of the FLAG_* variety
*/
[noscript] void draw(in gfxContext aContext, in gfxGraphicsFilter aFilter,
in gfxMatrix aUserSpaceToImageSpace, in gfxRect aFill,
in nsIntRect aSubimage);
in nsIntRect aSubimage, in PRUint32 aFlags);
/*
* Ensures that an image is decoding. Calling this function guarantees that
* the image will at some point fire off decode notifications. Calling draw(),
* getFrame(), copyFrame(), or extractCurrentFrame() triggers the same
* mechanism internally. Thus, if you want to be sure that the image will be
* decoded but don't want to access it until then, you must call
* requestDecode().
*/
void requestDecode();
/**
* Increments the lock count on the image. An image will not be discarded
* as long as the lock count is nonzero. Note that it is still possible for
* the image to be undecoded if decode-on-draw is enabled and the image
* was never drawn.
*
* Upon instantiation images have a lock count of zero.
*/
void lockImage();
/**
* Decreases the lock count on the image. If the lock count drops to zero,
* the image is allowed to discard its frame data to save memory.
*
* Upon instantiation images have a lock count of zero. It is an error to
* call this method without first having made a matching lockImage() call.
* In other words, the lock count is not allowed to be negative.
*/
void unlockImage();
/************ Internal libpr0n use only below here. *****************/
/**
* Create a new \a aWidth x \a aHeight sized image container.
* Flags for imgIContainer initialization.
*
* @param aWidth The width of the container in which all the
* frames will fit.
* @param aHeight The height of the container in which all the
* frames will fit.
* @param aObserver Observer to send animation notifications to.
* Meanings:
*
* INIT_FLAG_NONE: Lack of flags
*
* INIT_FLAG_DISCARDABLE: The container should be discardable
*
* INIT_FLAG_DECODE_ON_DRAW: The container should decode on draw rather than
* decoding on load.
*
* INIT_FLAG_MULTIPART: The container will be used to display a stream of
* images in a multipart channel. If this flag is set, INIT_FLAG_DISCARDABLE
* and INIT_FLAG_DECODE_ON_DRAW must not be set.
*/
void init(in PRInt32 aWidth,
in PRInt32 aHeight,
in imgIContainerObserver aObserver);
const long INIT_FLAG_NONE = 0x0;
const long INIT_FLAG_DISCARDABLE = 0x1;
const long INIT_FLAG_DECODE_ON_DRAW = 0x2;
const long INIT_FLAG_MULTIPART = 0x4;
/**
* Creates a new image container.
*
* @param aObserver Observer to send decoder and animation notifications to.
* @param aMimeType The mimetype of the image.
* @param aFlags Initialization flags of the INIT_FLAG_* variety.
*/
void init(in imgIDecoderObserver aObserver,
in string aMimeType,
in PRUint32 aFlags);
/**
* "Disposal" method indicates how the image should be handled before the
@ -201,19 +291,22 @@ interface imgIContainer : nsISupports
readonly attribute unsigned long numFrames;
/**
* Get the size, in bytes, of a particular frame's image data.
* The size, in bytes, occupied by the significant data portions of the image.
* This includes both compressed source data and decoded frames.
*/
unsigned long getFrameImageDataLength(in unsigned long framenumber);
void getFrameColormap(in unsigned long framenumber,
[array, size_is(paletteLength)] out PRUint32 paletteData,
out unsigned long paletteLength);
readonly attribute unsigned long dataSize;
void setFrameDisposalMethod(in unsigned long framenumber, in PRInt32 aDisposalMethod);
void setFrameBlendMethod(in unsigned long framenumber, in PRInt32 aBlendMethod);
void setFrameTimeout(in unsigned long framenumber, in PRInt32 aTimeout);
void setFrameHasNoAlpha(in unsigned long framenumber);
/**
* Sets the size of the container. This should only be called by the decoder. This function may be called multiple
* times, but will throw an error if subsequent calls do not match the first.
*/
[noscript] void setSize(in long aWidth, in long aHeight);
/**
* Create or re-use a frame at index aFrameNum. It is an error to call this with aFrameNum not in the range [0, numFrames].
*/
@ -249,9 +342,18 @@ interface imgIContainer : nsISupports
*/
attribute long loopCount;
/* Methods to discard uncompressed images and restore them again */
[noscript] void setDiscardable(in string aMimeType);
[noscript] void addRestoreData([array, size_is(aCount), const] in char data,
/* Add compressed source data to the imgContainer.
*
* The decoder will use this data, either immediately or at draw time, do
* decode the image.
*/
[noscript] void addSourceData([array, size_is(aCount), const] in char data,
in unsigned long aCount);
[noscript] void restoreDataDone();
/* Called after the all the source data has been added with addSourceData. */
[noscript] void sourceDataComplete();
/* Called for multipart images when there's a new source image to add. */
[noscript] void newSourceData();
};

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.com>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -40,7 +41,8 @@
#include "nsISupports.idl"
interface nsIInputStream;
interface imgILoad;
interface imgIContainer;
interface imgIDecoderObserver;
/**
* imgIDecoder interface
@ -49,24 +51,47 @@ interface imgILoad;
* @version 0.2
* @see imagelib2
*/
[scriptable, uuid(9eebf43a-1dd1-11b2-953e-f1782f4cbad3)]
[scriptable, uuid(139c6f97-cd2f-4a4f-890d-8d9f4a693682)]
interface imgIDecoder : nsISupports
{
/**
* Initialize an image decoder.
* @param aRequest the request that owns the decoder.
* Bits that can be passed to the decoder to affect decoding.
* @name decodeflags
*
* @note The decode should QI \a aLoad to an imgIDecoderObserver
* and should send decoder notifications to the request.
* The decoder should always pass NULL as the first two parameters to
* all of the imgIDecoderObserver APIs.
* Meanings:
*
* DECODER_FLAG_NONE: No flags
*
* DECODER_FLAG_HEADERONLY: Read basic data from the image in order to
* set up the image container, but don't read any actual image data.
*/
void init(in imgILoad aLoad);
const long DECODER_FLAG_NONE = 0x0;
const long DECODER_FLAG_HEADERONLY = 0x1;
/**
* Initialize an image decoder.
* @param aContainer The image container to decode to.
* @param aObserver The observer for decode notification events.
* @param aFlags Flags for the decoder
*
* @note The decoder should always pass NULL as the
* first two parameters to all of the imgIDecoderObserver APIs.
*/
void init(in imgIContainer aImage, in imgIDecoderObserver aObserver,
in unsigned long aFlags);
/**
* Closes the stream.
* Closes the stream.
* @param aFlags Close flags of the CLOSE_FLAG_* Variety
*
* Resources are always freed with this call. If notifications are sent,
* OnStopDecode is guaranteed to be called if it hasn't been called already.
*
* CLOSE_FLAG_DONTNOTIFY - Don't send any observer notifications, and don't
* call imgIContainer::decodingComplete().
*/
void close();
const long CLOSE_FLAG_DONTNOTIFY = 0x01;
void close(in PRUint32 aFlags);
/**
* Flushes the stream.
@ -75,6 +100,16 @@ interface imgIDecoder : nsISupports
/**
* Writes data into the stream from an input stream.
*
* For Header-Only decodes, OnStartContainer is the only notification
* fired.
*
* If a decoding error occurs, an internal flag is set and an error is
* returned. Each subsequent call to writeFrom will fail immediately
* for the lifetime of the decoder. Shutdown notifications of the OnStopX
* variety, as well as DecodingComplete(), are guaranteed not to be called
* if a decoding error occurs.
*
* Implementer's note: This method is defined by this interface in order
* to allow the output stream to efficiently copy the data from the input
* stream into its internal buffer (if any). If this method was provide
@ -82,11 +117,7 @@ interface imgIDecoder : nsISupports
* in order to call the output stream's other Write method.
* @param fromStream the stream from which the data is read
* @param count the maximun number of bytes to write
* @return aWriteCount out parameter to hold the number of
* bytes written. if an error occurs, the writecount
* is undefined
*/
unsigned long writeFrom(in nsIInputStream inStr,
in unsigned long count);
void writeFrom(in nsIInputStream inStr, in unsigned long count);
};

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.com>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -52,16 +53,36 @@ interface imgIContainer;
* This interface is used both for observing imgIDecoder objects and for
* observing imgIRequest objects. In the former case, aRequest is
* always null.
* XXXldb The two functions should probably be split.
*
* We make the distinction here between "load" and "decode" notifications. Load
* notifications are fired as the image is loaded from the network or
* filesystem. Decode notifications are fired as the image is decoded. If an
* image is decoded on load and not visibly discarded, decode notifications are
* nested logically inside load notifications as one might expect. However, with
* decode-on-draw, the set of decode notifications can come completely _after_
* the load notifications, and can come multiple times if the image is
* discardable. Moreover, they can be interleaved in various ways. In general,
* any presumed ordering between load and decode notifications should not be
* relied upon.
*
* Decode notifications may or may not be synchronous, depending on the
* situation. If imgIDecoder::FLAG_SYNC_DECODE is passed to a function that
* triggers a decode, all notifications that can be generated from the currently
* loaded data fire before the call returns. If FLAG_SYNC_DECODE is not passed,
* all, some, or none of the notifications may fire before the call returns.
*
* This interface will be cleaned up in bug 505385.
*
* @author Stuart Parmenter <pavlov@netscape.com>
* @version 0.1
* @see imagelib2
*/
[scriptable, uuid(1dfc9189-6421-4281-83b2-d9c1c9ba4d1b)]
[scriptable, uuid(9f6bfbee-9e04-43a0-b8f6-2159973efec8)]
interface imgIDecoderObserver : imgIContainerObserver
{
/**
* Load notification.
*
* called at the same time that nsIRequestObserver::onStartRequest would be
* (used only for observers of imgIRequest objects, which are nsIRequests,
* not imgIDecoder objects)
@ -72,42 +93,68 @@ interface imgIDecoderObserver : imgIContainerObserver
void onStartRequest(in imgIRequest aRequest);
/**
* called as soon as the image begins getting decoded
* Decode notification.
*
* Called as soon as the image begins getting decoded. This does not include
* "header-only" decodes used by decode-on-draw to parse the width/height
* out of the image. Thus, it is a decode notification only.
*/
void onStartDecode(in imgIRequest aRequest);
/**
* called once the image has been inited and therefore has a width and height
* Load notification.
*
* Called once enough data has been loaded from the network that we were able
* to parse the width/height from the image. By the time this callback is been
* called, the size has been set on the container and STATUS_SIZE_AVAILABLE
* has been set on the associated imgRequest.
*/
void onStartContainer(in imgIRequest aRequest, in imgIContainer aContainer);
/**
* called when each frame is created
* Decode notification.
*
* called when each frame is created.
*/
void onStartFrame(in imgIRequest aRequest, in unsigned long aFrame);
/**
* called when some part of the frame has new data in it
* Decode notification.
*
* called when there is more to paint.
*/
[noscript] void onDataAvailable(in imgIRequest aRequest, in boolean aCurrentFrame, [const] in nsIntRect aRect);
/**
* called when a frame is finished decoding
* Decode notification.
*
* called when a frame is finished decoding.
*/
void onStopFrame(in imgIRequest aRequest, in unsigned long aFrame);
/**
* probably not needed. called right before onStopDecode
* Do not implement this. It is useless and going away.
*/
void onStopContainer(in imgIRequest aRequest, in imgIContainer aContainer);
/**
* called when the decoder is dying off
* In theory a decode notification, but currently a load notification.
*
* Ideally this would be called when the decode is complete. Unfortunately,
* this is currently the only way to signal decoding errors to consumers,
* and the only decoding errors that consumers care about (indeed, the only
* ones that they're prepared to hear about) are failures to instantiate the
* decoder (<img src="foo.html"> for example). Thus, currently this is just
* a companion to onStopDecode to signal success or failure. This will be
* revisited in bug 505385. If you're thinking of doing something new with
* this, please talk to bholley first.
*/
void onStopDecode(in imgIRequest aRequest, in nsresult status,
in wstring statusArg);
/**
* Load notification.
*
* called at the same time that nsIRequestObserver::onStopRequest would be
* (used only for observers of imgIRequest objects, which are nsIRequests,
* not imgIDecoder objects)
@ -117,4 +164,11 @@ interface imgIDecoderObserver : imgIContainerObserver
*/
void onStopRequest(in imgIRequest aRequest, in boolean aIsLastPart);
/**
* Called when the decoded image data is discarded. This means that the frames
* no longer exist in decoded form, and any attempt to access or draw the
* image will initiate a new series of progressive decode notifications.
*/
void onDiscard(in imgIRequest aRequest);
};

View File

@ -1,61 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
interface imgIContainer;
/**
* imgILoad interface
*
* @author Stuart Parmenter <pavlov@netscape.com>
* @version 0.1
* @see imagelib2
*/
[scriptable, uuid(e6273acc-1dd1-11b2-a08b-824ad1b1628d)]
interface imgILoad : nsISupports
{
/**
* the image container...
* @return the image object associated with the request.
* @attention NEED DOCS
*/
attribute imgIContainer image;
readonly attribute PRBool isMultiPartChannel;
};

View File

@ -101,8 +101,7 @@ interface imgIRequest : nsIRequest
//@}
/**
* something
* @attention NEED DOCS
* Status flags of the STATUS_* variety.
*/
readonly attribute unsigned long imageStatus;
@ -140,5 +139,41 @@ interface imgIRequest : nsIRequest
* you're the observer, you can't call cancel() from your destructor.
*/
void cancelAndForgetObserver(in nsresult aStatus);
/**
* Requests a decode for the image.
*
* imgIContainer has a requestDecode() method, but callers may want to request
* a decode before the container has necessarily been instantiated. Calling
* requestDecode() on the imgIRequest simply forwards along the request if the
* container already exists, or calls it once it gets OnStartContainer if the
* container does not yet exist.
*/
void requestDecode();
/**
* Locks an image. If the image does not exist yet, locks it once it becomes
* available. The lock persists for the lifetime of the imgIRequest (until
* unlockImage is called) even if the underlying image changes.
*
* If you don't call unlockImage() by the time this imgIRequest goes away, it
* will be called for you automatically.
*
* Only one lock at a time per imgIRequest is supported. That is to say, you
* can do foo->LockImage(); foo->UnlockImage(); foo->LockImage();
* foo->UnlockImage(); but not foo->LockImage(); foo->LockImage();
* foo->UnlockImage(); foo->UnlockImage();
*
* @see imgIContainer::lockImage for documentation of the underlying call.
*/
void lockImage();
/**
* Unlocks an image.
*
* @see imgIContainer::unlockImage for documentation of the underlying call.
*/
void unlockImage();
};

View File

@ -58,7 +58,8 @@ interface imgITools : nsISupports
* @param aContainer
* An imgIContainer holding the decoded image. Specify |null| when
* calling to have one created, otherwise specify a container to
* be reused.
* be used. It is an error to pass an already-initialized container
* as aContainer.
*/
void decodeImageData(in nsIInputStream aStream,
in ACString aMimeType,

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,7 @@
* Stuart Parmenter <pavlov@netscape.com>
* Chris Saari <saari@netscape.com>
* Federico Mena-Quintero <federico@novell.com>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -55,11 +56,14 @@
#include "nsCOMArray.h"
#include "nsCOMPtr.h"
#include "imgIContainer.h"
#include "imgIDecoder.h"
#include "nsIProperties.h"
#include "nsITimer.h"
#include "nsWeakReference.h"
#include "nsTArray.h"
#include "nsIStringStream.h"
#include "imgFrame.h"
#include "nsThreadUtils.h"
#define NS_IMGCONTAINER_CID \
{ /* c76ff2c1-9bf6-418a-b143-3340c00112f7 */ \
@ -131,9 +135,11 @@
* because the first two have public setters and the observer we only get
* in Init().
*/
class imgDecodeWorker;
class imgContainer : public imgIContainer,
public nsITimerCallback,
public nsIProperties
public nsITimerCallback,
public nsIProperties,
public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
@ -144,14 +150,19 @@ public:
imgContainer();
virtual ~imgContainer();
static NS_METHOD WriteToContainer(nsIInputStream* in, void* closure,
const char* fromRawSegment,
PRUint32 toOffset, PRUint32 count,
PRUint32 *writeCount);
private:
struct Anim
{
//! Area of the first frame that needs to be redrawn on subsequent loops.
nsIntRect firstFrameRefreshArea;
// Note this doesn't hold a proper value until frame 2 finished decoding.
PRInt32 currentDecodingFrameIndex; // 0 to numFrames-1
PRInt32 currentAnimationFrameIndex; // 0 to numFrames-1
PRUint32 currentDecodingFrameIndex; // 0 to numFrames-1
PRUint32 currentAnimationFrameIndex; // 0 to numFrames-1
//! Track the last composited frame for Optimizations (See DoComposite code)
PRInt32 lastCompositedFrameIndex;
//! Whether we can assume there will be no more frames
@ -197,11 +208,26 @@ private:
imgFrame* GetImgFrame(PRUint32 framenum);
imgFrame* GetCurrentImgFrame();
PRInt32 GetCurrentImgFrameIndex() const;
PRUint32 GetCurrentImgFrameIndex() const;
inline Anim* ensureAnimExists() {
if (!mAnim)
inline Anim* ensureAnimExists()
{
if (!mAnim) {
// Create the animation context
mAnim = new Anim();
// We don't support discarding animated images (See bug 414259)
// Flag that we are no longer discardable (if we were before)
// and cancel any discard timer.
mDiscardable = PR_FALSE;
if (mDiscardTimer) {
nsresult rv = mDiscardTimer->Cancel();
if (!NS_SUCCEEDED(rv))
NS_WARNING("Discard Timer failed to cancel!");
mDiscardTimer = nsnull;
}
}
return mAnim;
}
@ -256,17 +282,19 @@ private:
private: // data
nsIntSize mSize;
PRBool mHasSize;
//! All the frames of the image
// *** IMPORTANT: if you use mFrames in a method, call RestoreDiscardedData() first to ensure
// that the frames actually exist (they may have been discarded to save memory).
// IMPORTANT: if you use mFrames in a method, call EnsureImageIsDecoded() first
// to ensure that the frames actually exist (they may have been discarded to save
// memory, or we may be decoding on draw).
nsTArray<imgFrame *> mFrames;
int mNumFrames; /* stored separately from mFrames.Count() to support discarded images */
nsCOMPtr<nsIProperties> mProperties;
// *** IMPORTANT: if you use mAnim in a method, call RestoreDiscardedData() first to ensure
// that the frames actually exist (they may have been discarded to save memory).
// IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure
// that the frames actually exist (they may have been discarded to save memory, or
// we maybe decoding on draw).
imgContainer::Anim* mAnim;
//! See imgIContainer for mode constants
@ -275,21 +303,117 @@ private: // data
//! # loops remaining before animation stops (-1 no stop)
PRInt32 mLoopCount;
//! imgIContainerObserver
//! imgIDecoderObserver
nsWeakPtr mObserver;
PRBool mDiscardable;
PRBool mDiscarded;
nsCString mDiscardableMimeType;
// Decoding on draw?
PRBool mDecodeOnDraw;
nsTArray<char> mRestoreData;
PRBool mRestoreDataDone;
// Multipart?
PRBool mMultipart;
// Have we been initalized?
PRBool mInitialized;
// Discard members
PRBool mDiscardable;
PRUint32 mLockCount;
nsCOMPtr<nsITimer> mDiscardTimer;
nsresult ResetDiscardTimer (void);
nsresult RestoreDiscardedData (void);
nsresult ReloadImages (void);
static void sDiscardTimerCallback (nsITimer *aTimer, void *aClosure);
// Source data members
nsTArray<char> mSourceData;
PRBool mHasSourceData;
nsCString mSourceDataMimeType;
// Do we have the frames in decoded form?
PRBool mDecoded;
friend class imgDecodeWorker;
// Decoder and friends
nsCOMPtr<imgIDecoder> mDecoder;
nsRefPtr<imgDecodeWorker> mWorker;
PRUint32 mBytesDecoded;
nsCOMPtr<nsIStringInputStream> mDecoderInput;
PRUint32 mDecoderFlags;
PRBool mWorkerPending;
PRBool mInDecoder;
// Error handling
PRBool mError;
// Discard code
nsresult ResetDiscardTimer();
static void sDiscardTimerCallback(nsITimer *aTimer, void *aClosure);
// Decoding
nsresult WantDecodedFrames();
nsresult SyncDecode();
nsresult InitDecoder(PRUint32 dFlags);
nsresult WriteToDecoder(const char *aBuffer, PRUint32 aCount);
nsresult DecodeSomeData(PRUint32 aMaxBytes);
PRBool IsDecodeFinished();
nsresult EnableDiscarding();
// Decoder shutdown
enum eShutdownIntent {
eShutdownIntent_Done = 0,
eShutdownIntent_Interrupted = 1,
eShutdownIntent_Error = 2,
eShutdownIntent_AllCount = 3
};
nsresult ShutdownDecoder(eShutdownIntent aIntent);
// Helpers
void DoError();
PRBool CanDiscard();
PRBool StoringSourceData();
};
// Decoding Helper Class
//
// We use this class to mimic the interactivity benefits of threading
// in a single-threaded event loop. We want to progressively decode
// and keep a responsive UI while we're at it, so we have a runnable
// class that does a bit of decoding, and then "yields" by dispatching
// itself to the end of the event queue.
class imgDecodeWorker : public nsRunnable
{
public:
imgDecodeWorker(imgIContainer* aContainer) {
mContainer = do_GetWeakReference(aContainer);
}
NS_IMETHOD Run();
NS_METHOD Dispatch();
private:
nsWeakPtr mContainer;
};
// Asynchronous Decode Requestor
//
// We use this class when someone calls requestDecode() from within a decode
// notification. Since requestDecode() involves modifying the decoder's state
// (for example, possibly shutting down a header-only decode and starting a
// full decode), we don't want to do this from inside a decoder.
class imgDecodeRequestor : public nsRunnable
{
public:
imgDecodeRequestor(imgIContainer *aContainer) {
mContainer = do_GetWeakReference(aContainer);
}
NS_IMETHOD Run() {
nsCOMPtr<imgIContainer> con = do_QueryReferent(mContainer);
if (con)
con->RequestDecode();
return NS_OK;
}
private:
nsWeakPtr mContainer;
};
#endif /* __imgContainer_h__ */

View File

@ -102,7 +102,7 @@ static void PrintImageDecoders()
nsCAutoString xcs;
ss->GetData(xcs);
NS_NAMED_LITERAL_CSTRING(decoderContract, "@mozilla.org/image/decoder;2?type=");
NS_NAMED_LITERAL_CSTRING(decoderContract, "@mozilla.org/image/decoder;3?type=");
if (StringBeginsWith(xcs, decoderContract)) {
printf("Have decoder for mime type: %s\n", xcs.get()+decoderContract.Length());
@ -372,21 +372,6 @@ imgCacheEntry::~imgCacheEntry()
LOG_FUNC(gImgLog, "imgCacheEntry::~imgCacheEntry()");
}
void imgCacheEntry::TouchWithSize(PRInt32 diff)
{
LOG_SCOPE(gImgLog, "imgCacheEntry::TouchWithSize");
mTouchedTime = SecondsFromPRTime(PR_Now());
// Don't update the cache if we've been removed from it or it doesn't care
// about our size or usage.
if (!Evicted() && HasNoProxies()) {
nsCOMPtr<nsIURI> uri;
mRequest->GetKeyURI(getter_AddRefs(uri));
imgLoader::CacheEntriesChanged(uri, diff);
}
}
void imgCacheEntry::Touch(PRBool updateTime /* = PR_TRUE */)
{
LOG_SCOPE(gImgLog, "imgCacheEntry::Touch");
@ -394,12 +379,17 @@ void imgCacheEntry::Touch(PRBool updateTime /* = PR_TRUE */)
if (updateTime)
mTouchedTime = SecondsFromPRTime(PR_Now());
UpdateCache();
}
void imgCacheEntry::UpdateCache(PRInt32 diff /* = 0 */)
{
// Don't update the cache if we've been removed from it or it doesn't care
// about our size or usage.
if (!Evicted() && HasNoProxies()) {
nsCOMPtr<nsIURI> uri;
mRequest->GetKeyURI(getter_AddRefs(uri));
imgLoader::CacheEntriesChanged(uri);
imgLoader::CacheEntriesChanged(uri, diff);
}
}
@ -1641,7 +1631,7 @@ NS_IMETHODIMP imgLoader::SupportImageWithMimeType(const char* aMimeType, PRBool
return rv;
nsCAutoString mimeType(aMimeType);
ToLowerCase(mimeType);
nsCAutoString decoderId(NS_LITERAL_CSTRING("@mozilla.org/image/decoder;2?type=") + mimeType);
nsCAutoString decoderId(NS_LITERAL_CSTRING("@mozilla.org/image/decoder;3?type=") + mimeType);
return reg->IsContractIDRegistered(decoderId.get(), _retval);
}

View File

@ -97,7 +97,7 @@ public:
{
PRInt32 oldsize = mDataSize;
mDataSize = aDataSize;
TouchWithSize(mDataSize - oldsize);
UpdateCache(mDataSize - oldsize);
}
PRInt32 GetTouchedTime() const
@ -156,7 +156,7 @@ private: // methods
friend class imgLoader;
friend class imgCacheQueue;
void Touch(PRBool updateTime = PR_TRUE);
void TouchWithSize(PRInt32 diff);
void UpdateCache(PRInt32 diff = 0);
void SetEvicted(PRBool evict)
{
mEvicted = evict;

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Stuart Parmenter <stuart@mozilla.com>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -41,6 +42,7 @@
#include "imgLoader.h"
#include "imgRequestProxy.h"
#include "imgContainer.h"
#include "imgILoader.h"
#include "ImageErrors.h"
@ -68,11 +70,14 @@
#include "nsXPIDLString.h"
#include "plstr.h" // PL_strcasestr(...)
static PRBool gDecodeOnDraw = PR_TRUE;
static PRBool gDiscardable = PR_TRUE;
#if defined(PR_LOGGING)
PRLogModuleInfo *gImgLog = PR_NewLogModule("imgRequest");
#endif
NS_IMPL_ISUPPORTS8(imgRequest, imgILoad,
NS_IMPL_ISUPPORTS7(imgRequest,
imgIDecoderObserver, imgIContainerObserver,
nsIStreamListener, nsIRequestObserver,
nsISupportsWeakReference,
@ -82,7 +87,7 @@ NS_IMPL_ISUPPORTS8(imgRequest, imgILoad,
imgRequest::imgRequest() :
mImageStatus(imgIRequest::STATUS_NONE), mState(0), mCacheId(0),
mValidator(nsnull), mImageSniffers("image-sniffing-services"),
mIsMultiPartChannel(PR_FALSE), mLoading(PR_FALSE), mProcessing(PR_FALSE),
mIsMultiPartChannel(PR_FALSE), mLoading(PR_FALSE),
mHadLastPart(PR_FALSE), mGotData(PR_FALSE), mIsInCache(PR_FALSE)
{
/* member initializers and constructor code */
@ -186,14 +191,14 @@ nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBoo
if (aNotify) {
// make sure that observer gets an OnStopDecode message sent to it
if (!(mState & onStopDecode)) {
if (!(mState & stateRequestStopped)) {
proxy->OnStopDecode(aStatus, nsnull);
}
}
// make sure that observer gets an OnStopRequest message sent to it
if (!(mState & onStopRequest)) {
if (!(mState & stateRequestStopped)) {
proxy->OnStopRequest(nsnull, nsnull, NS_BINDING_ABORTED, PR_TRUE);
}
@ -250,17 +255,17 @@ nsresult imgRequest::NotifyProxyListener(imgRequestProxy *proxy)
nsCOMPtr<imgIRequest> kungFuDeathGrip(proxy);
// OnStartRequest
if (mState & onStartRequest)
if (mState & stateRequestStarted)
proxy->OnStartRequest(nsnull, nsnull);
// OnStartDecode
if (mState & onStartDecode)
proxy->OnStartDecode();
// OnStartContainer
if (mState & onStartContainer)
if (mState & stateHasSize)
proxy->OnStartContainer(mImage);
// OnStartDecode
if (mState & stateDecodeStarted)
proxy->OnStartDecode();
// Send frame messages (OnStartFrame, OnDataAvailable, OnStopFrame)
PRUint32 nframes = 0;
if (mImage)
@ -271,37 +276,26 @@ nsresult imgRequest::NotifyProxyListener(imgRequestProxy *proxy)
mImage->GetCurrentFrameIndex(&frame);
proxy->OnStartFrame(frame);
if (!(mState & onStopContainer)) {
// OnDataAvailable
nsIntRect r;
mImage->GetCurrentFrameRect(r); // XXX we should only send the currently decoded rectangle here.
proxy->OnDataAvailable(frame, &r);
} else {
// OnDataAvailable
nsIntRect r;
mImage->GetCurrentFrameRect(r); // We're done loading this image, send the the whole rect
proxy->OnDataAvailable(frame, &r);
// OnDataAvailable
// XXX - Should only send partial rects here, but that needs to
// wait until we fix up the observer interface
nsIntRect r;
mImage->GetCurrentFrameRect(r);
proxy->OnDataAvailable(frame, &r);
// OnStopFrame
if (mState & stateRequestStopped)
proxy->OnStopFrame(frame);
}
}
// OnStopContainer
if (mState & onStopContainer)
proxy->OnStopContainer(mImage);
// OnStopDecode
if (mState & onStopDecode)
proxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), nsnull);
if (mImage && !HaveProxyWithObserver(proxy) && proxy->HasObserver()) {
LOG_MSG(gImgLog, "imgRequest::NotifyProxyListener", "resetting animation");
mImage->ResetAnimation();
}
if (mState & onStopRequest) {
if (mState & stateRequestStopped) {
proxy->OnStopContainer(mImage);
proxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), nsnull);
proxy->OnStopRequest(nsnull, nsnull,
GetResultFromImageStatus(mImageStatus),
mHadLastPart);
@ -337,9 +331,7 @@ void imgRequest::Cancel(nsresult aStatus)
if (!(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL))
mImageStatus |= imgIRequest::STATUS_ERROR;
if (aStatus != NS_IMAGELIB_ERROR_NO_DECODER) {
RemoveFromCache();
}
RemoveFromCache();
if (mRequest && mLoading)
mRequest->Cancel(aStatus);
@ -473,18 +465,25 @@ void imgRequest::SetIsInCache(PRBool incache)
mIsInCache = incache;
}
/** imgILoad methods **/
NS_IMETHODIMP imgRequest::SetImage(imgIContainer *aImage)
void imgRequest::UpdateCacheEntrySize()
{
LOG_FUNC(gImgLog, "imgRequest::SetImage");
if (mCacheEntry) {
PRUint32 imageSize = 0;
if (mImage)
mImage->GetDataSize(&imageSize);
mCacheEntry->SetDataSize(imageSize);
mImage = aImage;
#ifdef DEBUG_joe
nsCAutoString url;
mURI->GetSpec(url);
printf("CACHEPUT: %d %s %d\n", time(NULL), url.get(), imageSize);
#endif
}
return NS_OK;
}
NS_IMETHODIMP imgRequest::GetImage(imgIContainer **aImage)
nsresult
imgRequest::GetImage(imgIContainer **aImage)
{
LOG_FUNC(gImgLog, "imgRequest::GetImage");
@ -493,15 +492,6 @@ NS_IMETHODIMP imgRequest::GetImage(imgIContainer **aImage)
return NS_OK;
}
NS_IMETHODIMP imgRequest::GetIsMultiPartChannel(PRBool *aIsMultiPartChannel)
{
LOG_FUNC(gImgLog, "imgRequest::GetIsMultiPartChannel");
*aIsMultiPartChannel = mIsMultiPartChannel;
return NS_OK;
}
/** imgIContainerObserver methods **/
/* [noscript] void frameChanged (in imgIContainer container, in nsIntRect dirtyRect); */
@ -525,7 +515,7 @@ NS_IMETHODIMP imgRequest::OnStartDecode(imgIRequest *request)
{
LOG_SCOPE(gImgLog, "imgRequest::OnStartDecode");
mState |= onStartDecode;
mState |= stateDecodeStarted;
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
@ -557,13 +547,18 @@ NS_IMETHODIMP imgRequest::OnStartContainer(imgIRequest *request, imgIContainer *
NS_ASSERTION(image, "imgRequest::OnStartContainer called with a null image!");
if (!image) return NS_ERROR_UNEXPECTED;
mState |= onStartContainer;
// We only want to send onStartContainer once
PRBool alreadySent = (mState & stateHasSize) != 0;
mState |= stateHasSize;
mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE;
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStartContainer(image);
if (!alreadySent) {
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStartContainer(image);
}
}
return NS_OK;
@ -606,23 +601,6 @@ NS_IMETHODIMP imgRequest::OnStopFrame(imgIRequest *request,
mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE;
if (mCacheEntry) {
PRUint32 cacheSize = mCacheEntry->GetDataSize();
PRUint32 imageSize = 0;
if (mImage)
mImage->GetFrameImageDataLength(frame, &imageSize);
mCacheEntry->SetDataSize(cacheSize + imageSize);
#ifdef DEBUG_joe
nsCAutoString url;
mURI->GetSpec(url);
printf("CACHEPUT: %d %s %d\n", time(NULL), url.get(), cacheSize + imageSize);
#endif
}
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStopFrame(frame);
@ -637,8 +615,6 @@ NS_IMETHODIMP imgRequest::OnStopContainer(imgIRequest *request,
{
LOG_SCOPE(gImgLog, "imgRequest::OnStopContainer");
mState |= onStopContainer;
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStopContainer(image);
@ -654,18 +630,21 @@ NS_IMETHODIMP imgRequest::OnStopDecode(imgIRequest *aRequest,
{
LOG_SCOPE(gImgLog, "imgRequest::OnStopDecode");
NS_ASSERTION(!(mState & onStopDecode), "OnStopDecode called multiple times.");
// We finished the decode, and thus have the decoded frames. Update the cache
// entry size to take this into account.
UpdateCacheEntrySize();
mState |= onStopDecode;
if (NS_FAILED(aStatus) && !(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL)) {
mImageStatus |= imgIRequest::STATUS_ERROR;
}
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStopDecode(GetResultFromImageStatus(mImageStatus), aStatusArg);
}
// ImgContainer and everything below it is completely correct and
// bulletproof about its handling of decoder notifications.
// Unfortunately, here and above we have to make some gross and
// inappropriate use of things to get things to work without
// completely overhauling the decoder observer interface (this will,
// thankfully, happen in bug 505385). From imgRequest and above (for
// the time being), OnStopDecode is just a companion to OnStopRequest
// that signals success or failure of the _load_ (not the _decode_).
// As such, we ignore OnStopDecode notifications from the decoder and
// container and generate our own every time we send OnStopRequest.
// For more information, see bug 435296.
return NS_OK;
}
@ -677,6 +656,29 @@ NS_IMETHODIMP imgRequest::OnStopRequest(imgIRequest *aRequest,
return NS_OK;
}
/* void onDiscard (in imgIRequest request); */
NS_IMETHODIMP imgRequest::OnDiscard(imgIRequest *aRequest)
{
// Clear the state bits we no longer deserve.
PRUint32 stateBitsToClear = stateDecodeStarted;
mState &= ~stateBitsToClear;
// Clear the status bits we no longer deserve.
PRUint32 statusBitsToClear = imgIRequest::STATUS_FRAME_COMPLETE;
mImageStatus &= ~statusBitsToClear;
// Update the cache entry size, since we just got rid of frame data
UpdateCacheEntrySize();
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnDiscard();
}
return NS_OK;
}
/** nsIRequestObserver methods **/
/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
@ -686,12 +688,30 @@ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt
LOG_SCOPE(gImgLog, "imgRequest::OnStartRequest");
NS_ASSERTION(!mDecoder, "imgRequest::OnStartRequest -- we already have a decoder");
// Figure out if we're multipart
nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
if (mpchan)
mIsMultiPartChannel = PR_TRUE;
// If we're not multipart, we shouldn't have an image yet
NS_ABORT_IF_FALSE(mIsMultiPartChannel || !mImage,
"Already have an image for non-multipart request");
// If we're multipart and have an image, fix things up for another round
if (mIsMultiPartChannel && mImage) {
// Inform the container that we have new source data
mImage->NewSourceData();
// Clear any status and state bits indicating load/decode
mImageStatus &= ~imgIRequest::STATUS_LOAD_PARTIAL;
mImageStatus &= ~imgIRequest::STATUS_LOAD_COMPLETE;
mImageStatus &= ~imgIRequest::STATUS_FRAME_COMPLETE;
mState &= ~stateRequestStarted;
mState &= ~stateDecodeStarted;
mState &= ~stateRequestStopped;
}
/*
* If mRequest is null here, then we need to set it so that we'll be able to
* cancel it if our Cancel() method is called. Note that this can only
@ -707,10 +727,8 @@ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt
mRequest = chan;
}
/* set our state variables to their initial values, but advance mState
to onStartRequest. */
mImageStatus = imgIRequest::STATUS_NONE;
mState = onStartRequest;
// The request has started
mState |= stateRequestStarted;
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
if (channel)
@ -802,14 +820,11 @@ NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt,
{
LOG_FUNC(gImgLog, "imgRequest::OnStopRequest");
mState |= onStopRequest;
mState |= stateRequestStopped;
/* set our loading flag to false */
mLoading = PR_FALSE;
/* set our processing flag to false */
mProcessing = PR_FALSE;
mHadLastPart = PR_TRUE;
nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
if (mpchan) {
@ -834,39 +849,55 @@ NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt,
mChannel = nsnull;
}
// If mImage is still null, we didn't properly load the image.
if (NS_FAILED(status) || !mImage) {
this->Cancel(status); // sets status, stops animations, removes from cache
} else {
// Tell the image that it has all of the source data. Note that this can
// trigger a failure, since the image might be waiting for more non-optional
// data and this is the point where we break the news that it's not coming.
if (mImage) {
// Notify the image
nsresult rv = mImage->SourceDataComplete();
// If we got an error in the SourceDataComplete() call, we don't want to
// proceed as if nothing bad happened. However, we also want to give
// precedence to failure status codes from necko, since presumably
// they're more meaningful.
if (NS_FAILED(rv) && NS_SUCCEEDED(status))
status = rv;
}
// If the request went through, say we loaded the image, and update the
// cache entry size. Otherwise, cancel the request, which adds an error
// flag to mImageStatus.
if (NS_SUCCEEDED(status)) {
// Flag that we loaded the image
mImageStatus |= imgIRequest::STATUS_LOAD_COMPLETE;
}
if (mDecoder) {
mDecoder->Flush();
mDecoder->Close();
mDecoder = nsnull; // release the decoder so that it can rest peacefully ;)
}
// if there was an error loading the image, (mState & onStopDecode) won't be true.
// Send an onStopDecode message
if (!(mState & onStopDecode)) {
this->OnStopDecode(nsnull, status, nsnull);
// We update the cache entry size here because this is where we finish
// loading compressed source data, which is part of our size calculus.
UpdateCacheEntrySize();
}
else
this->Cancel(status); // sets status, stops animations, removes from cache
/* notify the kids */
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStopRequest(aRequest, ctxt, status, mHadLastPart);
nsTObserverArray<imgRequestProxy*>::ForwardIterator sdIter(mObservers);
while (sdIter.HasMore()) {
sdIter.GetNext()->OnStopDecode(GetResultFromImageStatus(mImageStatus), nsnull);
}
nsTObserverArray<imgRequestProxy*>::ForwardIterator srIter(mObservers);
while (srIter.HasMore()) {
srIter.GetNext()->OnStopRequest(aRequest, ctxt, status, mHadLastPart);
}
return NS_OK;
}
/* prototype for this defined below */
/* prototype for these defined below */
static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* closure, const char* fromRawSegment,
PRUint32 toOffset, PRUint32 count, PRUint32 *writeCount);
/** nsIStreamListener methods **/
/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
@ -877,13 +908,11 @@ NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctx
NS_ASSERTION(aRequest, "imgRequest::OnDataAvailable -- no request!");
mGotData = PR_TRUE;
nsresult rv;
if (!mProcessing) {
if (!mImage) {
LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |First time through... finding mimetype|");
/* set our processing flag to true if this is the first OnDataAvailable() */
mProcessing = PR_TRUE;
/* look at the first few bytes and see if we can tell what the data is from that
* since servers tend to lie. :(
*/
@ -899,7 +928,7 @@ NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctx
nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
nsresult rv = NS_ERROR_FAILURE;
rv = NS_ERROR_FAILURE;
if (chan) {
rv = chan->GetContentType(mContentType);
}
@ -945,51 +974,79 @@ NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctx
LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "content type", mContentType.get());
nsCAutoString conid(NS_LITERAL_CSTRING("@mozilla.org/image/decoder;2?type=") + mContentType);
//
// Figure out if our container initialization flags
//
mDecoder = do_CreateInstance(conid.get());
// We default to the static globals
PRBool isDiscardable = gDiscardable;
PRBool doDecodeOnDraw = gDecodeOnDraw;
if (!mDecoder) {
PR_LOG(gImgLog, PR_LOG_WARNING,
("[this=%p] imgRequest::OnDataAvailable -- Decoder not available\n", this));
// We want UI to be as snappy as possible and not to flicker. Disable discarding
// and decode-on-draw for chrome URLS
PRBool isChrome = PR_FALSE;
rv = mURI->SchemeIs("chrome", &isChrome);
if (NS_SUCCEEDED(rv) && isChrome)
isDiscardable = doDecodeOnDraw = PR_FALSE;
// no image decoder for this mimetype :(
this->Cancel(NS_IMAGELIB_ERROR_NO_DECODER);
// We don't want resources like the "loading" icon to be discardable or
// decode-on-draw either.
PRBool isResource = PR_FALSE;
rv = mURI->SchemeIs("resource", &isResource);
if (NS_SUCCEEDED(rv) && isResource)
isDiscardable = doDecodeOnDraw = PR_FALSE;
return NS_IMAGELIB_ERROR_NO_DECODER;
// For multipart/x-mixed-replace, we basically want a direct channel to the
// decoder. Disable both for this case as well.
if (mIsMultiPartChannel)
isDiscardable = doDecodeOnDraw = PR_FALSE;
// We have all the information we need
PRUint32 containerFlags = imgIContainer::INIT_FLAG_NONE;
if (isDiscardable)
containerFlags |= imgIContainer::INIT_FLAG_DISCARDABLE;
if (doDecodeOnDraw)
containerFlags |= imgIContainer::INIT_FLAG_DECODE_ON_DRAW;
if (mIsMultiPartChannel)
containerFlags |= imgIContainer::INIT_FLAG_MULTIPART;
// Create and initialize the imgContainer. This instantiates a decoder behind
// the scenes, so if we don't have a decoder for this mimetype we'll find out
// about it here.
mImage = do_CreateInstance("@mozilla.org/image/container;3");
if (!mImage) {
this->Cancel(NS_ERROR_OUT_OF_MEMORY);
return NS_ERROR_OUT_OF_MEMORY;
}
rv = mImage->Init(this, mContentType.get(), containerFlags);
if (NS_FAILED(rv)) { // Probably bad mimetype
nsresult rv = mDecoder->Init(static_cast<imgILoad*>(this));
if (NS_FAILED(rv)) {
PR_LOG(gImgLog, PR_LOG_WARNING,
("[this=%p] imgRequest::OnDataAvailable -- mDecoder->Init failed\n", this));
this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
// There's no reason to keep the image around. Save memory.
//
// XXXbholley - This is also here because I'm not sure we've found
// all the consumers who (incorrectly) check whether the container
// is null to determine things like size availability (they should
// be checking the image status instead).
mImage = nsnull;
this->Cancel(rv);
return NS_BINDING_ABORTED;
}
}
if (!mDecoder) {
PR_LOG(gImgLog, PR_LOG_WARNING,
("[this=%p] imgRequest::OnDataAvailable -- no decoder\n", this));
this->Cancel(NS_IMAGELIB_ERROR_NO_DECODER);
return NS_BINDING_ABORTED;
}
PRUint32 wrote;
nsresult rv = mDecoder->WriteFrom(inStr, count, &wrote);
// WriteToContainer always consumes everything it gets
PRUint32 bytesRead;
rv = inStr->ReadSegments(imgContainer::WriteToContainer,
static_cast<void*>(mImage),
count, &bytesRead);
if (NS_FAILED(rv)) {
PR_LOG(gImgLog, PR_LOG_WARNING,
("[this=%p] imgRequest::OnDataAvailable -- mDecoder->WriteFrom failed\n", this));
("[this=%p] imgRequest::OnDataAvailable -- "
"copy to container failed\n", this));
this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
return NS_BINDING_ABORTED;
}
NS_ABORT_IF_FALSE(bytesRead == count, "WriteToContainer should consume everything!");
return NS_OK;
}
@ -1036,6 +1093,7 @@ imgRequest::SniffMimeType(const char *buf, PRUint32 len)
}
}
/** nsIInterfaceRequestor methods **/
NS_IMETHODIMP

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Stuart Parmenter <pavlov@netscape.com>
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -40,8 +41,6 @@
#ifndef imgRequest_h__
#define imgRequest_h__
#include "imgILoad.h"
#include "imgIContainer.h"
#include "imgIDecoder.h"
#include "imgIDecoderObserver.h"
@ -67,16 +66,13 @@ class imgRequestProxy;
class imgCacheEntry;
enum {
onStartRequest = PR_BIT(0),
onStartDecode = PR_BIT(1),
onStartContainer = PR_BIT(2),
onStopContainer = PR_BIT(3),
onStopDecode = PR_BIT(4),
onStopRequest = PR_BIT(5)
stateRequestStarted = PR_BIT(0),
stateHasSize = PR_BIT(1),
stateDecodeStarted = PR_BIT(2),
stateRequestStopped = PR_BIT(4)
};
class imgRequest : public imgILoad,
public imgIDecoderObserver,
class imgRequest : public imgIDecoderObserver,
public nsIStreamListener,
public nsSupportsWeakReference,
public nsIChannelEventSink,
@ -115,6 +111,8 @@ public:
// won't be sufficient.
void CancelAndAbort(nsresult aStatus);
nsresult GetImage(imgIContainer **aImage);
private:
friend class imgCacheEntry;
friend class imgRequestProxy;
@ -169,8 +167,10 @@ private:
// try to update or modify the image cache.
void SetIsInCache(PRBool cacheable);
// Update the cache entry size based on the image container
void UpdateCacheEntrySize();
public:
NS_DECL_IMGILOAD
NS_DECL_IMGIDECODEROBSERVER
NS_DECL_IMGICONTAINEROBSERVER
NS_DECL_NSISTREAMLISTENER
@ -186,7 +186,6 @@ private:
nsCOMPtr<nsIURI> mKeyURI;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<imgIContainer> mImage;
nsCOMPtr<imgIDecoder> mDecoder;
nsCOMPtr<nsIProperties> mProperties;
nsCOMPtr<nsISupports> mSecurityInfo;
nsCOMPtr<nsIChannel> mChannel;
@ -210,7 +209,6 @@ private:
PRPackedBool mIsMultiPartChannel : 1;
PRPackedBool mLoading : 1;
PRPackedBool mProcessing : 1;
PRPackedBool mHadLastPart : 1;
PRPackedBool mGotData : 1;
PRPackedBool mIsInCache : 1;

View File

@ -64,7 +64,9 @@ imgRequestProxy::imgRequestProxy() :
mLoadFlags(nsIRequest::LOAD_NORMAL),
mCanceled(PR_FALSE),
mIsInLoadGroup(PR_FALSE),
mListenerIsStrongRef(PR_FALSE)
mListenerIsStrongRef(PR_FALSE),
mShouldRequestDecode(PR_FALSE),
mLockHeld(PR_FALSE)
{
/* member initializers and constructor code */
@ -75,6 +77,10 @@ imgRequestProxy::~imgRequestProxy()
/* destructor code */
NS_PRECONDITION(!mListener, "Someone forgot to properly cancel this request!");
// Unlock the image if we're holding a lock on it
if (mLockHeld && mOwner)
UnlockImage();
// Explicitly set mListener to null to ensure that the RemoveProxy
// call below can't send |this| to an arbitrary listener while |this|
// is being destroyed. This is all belt-and-suspenders in view of the
@ -129,6 +135,16 @@ nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner)
if (mCanceled)
return NS_OK;
// Were we decoded before?
PRBool wasDecoded = PR_FALSE;
if (mOwner->GetImageStatus() & imgIRequest::STATUS_FRAME_COMPLETE)
wasDecoded = PR_TRUE;
// If we're holding a lock, unlock the old image
PRBool wasLocked = mLockHeld;
if (mLockHeld)
UnlockImage();
// Passing false to aNotify means that mListener will still get
// OnStopRequest, if needed.
mOwner->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER, PR_FALSE);
@ -137,6 +153,14 @@ nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner)
mOwner->AddProxy(this);
// If we were decoded, request a decode on the new image
if (wasDecoded)
RequestDecode();
// If we were locked, apply the lock here
if (wasLocked)
LockImage();
return NS_OK;
}
@ -242,6 +266,74 @@ NS_IMETHODIMP imgRequestProxy::CancelAndForgetObserver(nsresult aStatus)
return NS_OK;
}
/* void requestDecode (); */
NS_IMETHODIMP
imgRequestProxy::RequestDecode()
{
if (!mOwner)
return NS_ERROR_FAILURE;
// See if we can get the image
nsCOMPtr<imgIContainer> container;
nsresult rv = mOwner->GetImage(getter_AddRefs(container));
if (NS_FAILED(rv))
return rv;
// If we've got the container, just forward along the request
if (container)
return container->RequestDecode();
// Otherwise, flag that we should do it when the container is ready
mShouldRequestDecode = PR_TRUE;
return NS_OK;
}
/* void lockImage (); */
NS_IMETHODIMP
imgRequestProxy::LockImage()
{
if (!mOwner)
return NS_ERROR_FAILURE;
// Flag that we're holding a lock
NS_ABORT_IF_FALSE(!mLockHeld, "Only call lockImage once per imgIRequest!");
mLockHeld = PR_TRUE;
// If we've got the container, forward along the request. If we don't, well
// do it in OnStartContainer.
nsCOMPtr<imgIContainer> container;
nsresult rv = mOwner->GetImage(getter_AddRefs(container));
if (NS_FAILED(rv))
return rv;
if (container)
return container->LockImage();
return NS_OK;
}
/* void unlockImage (); */
NS_IMETHODIMP
imgRequestProxy::UnlockImage()
{
if (!mOwner)
return NS_ERROR_FAILURE;
// Flag that we're not holding a lock
NS_ABORT_IF_FALSE(mLockHeld, "calling unlock but not locked!");
mLockHeld = PR_FALSE;
// If we've got the container, forward along the request. If we don't, it
// doesn't matter.
nsCOMPtr<imgIContainer> container;
nsresult rv = mOwner->GetImage(getter_AddRefs(container));
if (NS_FAILED(rv))
return rv;
if (container)
return container->UnlockImage();
return NS_OK;
}
/* void suspend (); */
NS_IMETHODIMP imgRequestProxy::Suspend()
{
@ -458,6 +550,16 @@ void imgRequestProxy::OnStartContainer(imgIContainer *image)
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->OnStartContainer(this, image);
}
// Request a decode if we said we should
if (mShouldRequestDecode) {
image->RequestDecode();
mShouldRequestDecode = PR_FALSE;
}
// Lock if we said we should
if (mLockHeld)
image->LockImage();
}
void imgRequestProxy::OnStartFrame(PRUint32 frame)
@ -515,6 +617,18 @@ void imgRequestProxy::OnStopDecode(nsresult status, const PRUnichar *statusArg)
}
}
void imgRequestProxy::OnDiscard()
{
LOG_FUNC(gImgLog, "imgRequestProxy::OnDiscard");
if (mListener && !mCanceled) {
// Hold a ref to the listener while we call it, just in case.
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->OnDiscard(this);
}
}
void imgRequestProxy::OnStartRequest(nsIRequest *request, nsISupports *ctxt)

View File

@ -116,6 +116,7 @@ protected:
void OnStopFrame (PRUint32 aFrame);
void OnStopContainer (imgIContainer *aContainer);
void OnStopDecode (nsresult status, const PRUnichar *statusArg);
void OnDiscard ();
/* non-virtual imgIContainerObserver methods */
void FrameChanged(imgIContainer *aContainer, nsIntRect * aDirtyRect);
@ -155,4 +156,6 @@ private:
PRPackedBool mCanceled;
PRPackedBool mIsInLoadGroup;
PRPackedBool mListenerIsStrongRef;
PRPackedBool mShouldRequestDecode;
PRPackedBool mLockHeld;
};

View File

@ -41,7 +41,6 @@
#include "nsString.h"
#include "ImageErrors.h"
#include "imgIContainer.h"
#include "imgILoad.h"
#include "imgIDecoder.h"
#include "imgIEncoder.h"
#include "imgIDecoderObserver.h"
@ -53,131 +52,7 @@
#include "nsIInterfaceRequestorUtils.h"
#include "nsStreamUtils.h"
#include "nsNetUtil.h"
/* ========== Utility classes ========== */
class HelperLoader : public imgILoad,
public imgIDecoderObserver,
public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IMGILOAD
NS_DECL_IMGIDECODEROBSERVER
NS_DECL_IMGICONTAINEROBSERVER
HelperLoader(void);
private:
nsCOMPtr<imgIContainer> mContainer;
};
NS_IMPL_ISUPPORTS4 (HelperLoader, imgILoad, imgIDecoderObserver, imgIContainerObserver, nsISupportsWeakReference)
HelperLoader::HelperLoader (void)
{
}
/* Implement imgILoad::image getter */
NS_IMETHODIMP
HelperLoader::GetImage(imgIContainer **aImage)
{
*aImage = mContainer;
NS_IF_ADDREF (*aImage);
return NS_OK;
}
/* Implement imgILoad::image setter */
NS_IMETHODIMP
HelperLoader::SetImage(imgIContainer *aImage)
{
mContainer = aImage;
return NS_OK;
}
/* Implement imgILoad::isMultiPartChannel getter */
NS_IMETHODIMP
HelperLoader::GetIsMultiPartChannel(PRBool *aIsMultiPartChannel)
{
*aIsMultiPartChannel = PR_FALSE;
return NS_OK;
}
/* Implement imgIDecoderObserver::onStartRequest() */
NS_IMETHODIMP
HelperLoader::OnStartRequest(imgIRequest *aRequest)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onStartDecode() */
NS_IMETHODIMP
HelperLoader::OnStartDecode(imgIRequest *aRequest)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onStartContainer() */
NS_IMETHODIMP
HelperLoader::OnStartContainer(imgIRequest *aRequest, imgIContainer
*aContainer)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onStartFrame() */
NS_IMETHODIMP
HelperLoader::OnStartFrame(imgIRequest *aRequest, PRUint32 aFrame)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onDataAvailable() */
NS_IMETHODIMP
HelperLoader::OnDataAvailable(imgIRequest *aRequest, PRBool aCurrentFrame, const nsIntRect * aRect)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onStopFrame() */
NS_IMETHODIMP
HelperLoader::OnStopFrame(imgIRequest *aRequest, PRUint32 aFrame)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onStopContainer() */
NS_IMETHODIMP
HelperLoader::OnStopContainer(imgIRequest *aRequest, imgIContainer
*aContainer)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onStopDecode() */
NS_IMETHODIMP
HelperLoader::OnStopDecode(imgIRequest *aRequest, nsresult status, const
PRUnichar *statusArg)
{
return NS_OK;
}
/* Implement imgIDecoderObserver::onStopRequest() */
NS_IMETHODIMP
HelperLoader::OnStopRequest(imgIRequest *aRequest, PRBool aIsLastPart)
{
return NS_OK;
}
/* implement imgIContainerObserver::frameChanged() */
NS_IMETHODIMP
HelperLoader::FrameChanged(imgIContainer *aContainer, nsIntRect * aDirtyRect)
{
return NS_OK;
}
#include "imgContainer.h"
/* ========== imgITools implementation ========== */
@ -202,24 +77,19 @@ NS_IMETHODIMP imgTools::DecodeImageData(nsIInputStream* aInStr,
{
nsresult rv;
// Get an image decoder for our media type
nsCAutoString decoderCID(
NS_LITERAL_CSTRING("@mozilla.org/image/decoder;2?type=") + aMimeType);
NS_ENSURE_ARG_POINTER(aInStr);
// If the caller didn't provide a container, create one
if (!*aContainer) {
NS_NEWXPCOM(*aContainer, imgContainer);
if (!*aContainer)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aContainer);
}
nsCOMPtr<imgIDecoder> decoder = do_CreateInstance(decoderCID.get());
if (!decoder)
return NS_IMAGELIB_ERROR_NO_DECODER;
// Init the decoder, we use a small utility class here.
nsCOMPtr<imgILoad> loader = new HelperLoader();
if (!loader)
return NS_ERROR_OUT_OF_MEMORY;
// If caller provided an existing container, use it.
if (*aContainer)
loader->SetImage(*aContainer);
rv = decoder->Init(loader);
// Initialize the container. If we're using the one from the caller, we
// require that it not be initialized
nsCString mimeType(aMimeType);
rv = (*aContainer)->Init(nsnull, mimeType.get(), imgIContainer::INIT_FLAG_NONE);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIInputStream> inStream = aInStr;
@ -230,24 +100,25 @@ NS_IMETHODIMP imgTools::DecodeImageData(nsIInputStream* aInStr,
inStream = bufStream;
}
// Figure out how much data we've been passed
PRUint32 length;
rv = inStream->Available(&length);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 written;
rv = decoder->WriteFrom(inStream, length, &written);
NS_ENSURE_SUCCESS(rv, rv);
if (written != length)
NS_WARNING("decoder didn't eat all of its vegetables");
rv = decoder->Flush();
NS_ENSURE_SUCCESS(rv, rv);
rv = decoder->Close();
// Send the source data to the container. WriteToContainer always
// consumes everything it gets.
PRUint32 bytesRead;
rv = inStream->ReadSegments(imgContainer::WriteToContainer,
static_cast<void*>(*aContainer),
length, &bytesRead);
NS_ENSURE_SUCCESS(rv, rv);
// If caller didn't provide an existing container, return the new one.
if (!*aContainer)
loader->GetImage(aContainer);
// Let the container know we've sent all the data
rv = (*aContainer)->SourceDataComplete();
NS_ENSURE_SUCCESS(rv, rv);
// All done
return NS_OK;
}
@ -290,7 +161,8 @@ NS_IMETHODIMP imgTools::EncodeScaledImage(imgIContainer *aContainer,
// Use frame 0 from the image container.
nsRefPtr<gfxImageSurface> frame;
rv = aContainer->CopyCurrentFrame(getter_AddRefs(frame));
rv = aContainer->CopyFrame(imgIContainer::FRAME_CURRENT, PR_TRUE,
getter_AddRefs(frame));
NS_ENSURE_SUCCESS(rv, rv);
if (!frame)
return NS_ERROR_NOT_AVAILABLE;

View File

@ -44,8 +44,9 @@ relativesrcdir = modules/libpr0n/test/mochitest
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_TEST_FILES = test_bug399925.html \
bug399925.gif \
_TEST_FILES = imgutils.js \
test_bug399925.html \
bug399925.gif \
bug468160.sjs \
test_bug468160.html \
red.png \

View File

@ -0,0 +1,92 @@
// Helper file for shared image functionality
//
// Note that this is use by tests elsewhere in the source tree. When in doubt,
// check mxr before removing or changing functionality.
// Helper function to determine if the frame is decoded for a given image id
function isFrameDecoded(id)
{
return (getImageStatus(id) &
Components.interfaces.imgIRequest.STATUS_FRAME_COMPLETE)
? true : false;
}
// Helper function to determine if the image is loaded for a given image id
function isImageLoaded(id)
{
return (getImageStatus(id) &
Components.interfaces.imgIRequest.STATUS_LOAD_COMPLETE)
? true : false;
}
// Helper function to get the status flags of an image
function getImageStatus(id)
{
// Escalate
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
// Get the image
var img = document.getElementById(id);
// QI the image to nsImageLoadingContent
img.QueryInterface(Components.interfaces.nsIImageLoadingContent);
// Get the request
var request = img.getRequest(Components.interfaces
.nsIImageLoadingContent
.CURRENT_REQUEST);
// Return the status
return request.imageStatus;
}
// Forces a synchronous decode of an image by drawing it to a canvas. Only
// really meaningful if the image is fully loaded first
function forceDecode(id)
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
// Get the image
var img = document.getElementById(id);
// Make a new canvas
var canvas = document.createElement("canvas");
// Draw the image to the canvas. This forces a synchronous decode
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
}
// Functions to facilitate getting/setting the discard timer pref
//
// Don't forget to reset the pref to the original value!
//
// Null indicates no pref set
const DISCARD_BRANCH_NAME = "image.cache.";
const DISCARD_PREF_NAME = "discard_timer_ms";
function setDiscardTimerPref(timeMS)
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var prefService = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
var branch = prefService.getBranch(DISCARD_BRANCH_NAME);
if (timeMS != null)
branch.setIntPref(DISCARD_PREF_NAME, timeMS);
else if (branch.prefHasUserValue(DISCARD_PREF_NAME))
branch.clearUserPref(DISCARD_PREF_NAME);
}
function getDiscardTimerPref()
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var prefService = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
var branch = prefService.getBranch(DISCARD_BRANCH_NAME);
if (branch.prefHasUserValue(DISCARD_PREF_NAME))
return branch.getIntPref("discard_timeout_ms");
else
return null;
}

View File

@ -6,19 +6,36 @@
<body>
<img id="image1">
<script>
// This loads a externally specified image, waits 100ms, and then triggers the
// reftest snapshot. This allows the animation on the page to complete.
// This loads a externally specified image, forces a draw (in case of
// decode-on-draw), waits 100ms, and then triggers the reftest snapshot.
// This allows the animation on the page to complete.
//
// Use as "delaytest.html?animation.png"
//
// Get the image URL from our URL
var imgURL = document.location.search.substr(1);
// Load the image
var img = document.images[0];
img.src = imgURL;
img.onload = forceDecode;
function forceDecode() {
// We need to force drawing of the image in an invisible context
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// We've force the decode. start the timer to trigger the reftest
startTimer();
}
function startTimer() {
const delay = 100;
setTimeout("document.documentElement.className = '';", delay);
}
var imgURL = document.location.search.substr(1);
var img = document.images[0];
img.src = imgURL;
img.onload = startTimer;
</script>
</body>
</html>

View File

@ -382,6 +382,7 @@ imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
do_check_eq(istream.available(), 17759);
var errsrc = "none";
try {
outParam = { value: null };
@ -394,12 +395,15 @@ try {
istream = imgTools.encodeImage(container, "image/png");
} catch (e) {
err = e;
errsrc = "encode";
}
} catch (e) {
err = e;
errsrc = "decode";
}
checkExpectedError(/NS_ERROR_ILLEGAL_VALUE/, err);
do_check_eq(errsrc, "decode");
checkExpectedError(/NS_ERROR_FAILURE/, err);
/* ========== end ========== */

View File

@ -161,6 +161,12 @@ nsAlertsIconListener::OnStopRequest(imgIRequest* aRequest,
return NS_OK;
}
NS_IMETHODIMP
nsAlertsIconListener::OnDiscard(imgIRequest *aRequest)
{
return NS_OK;
}
NS_IMETHODIMP
nsAlertsIconListener::OnStopFrame(imgIRequest* aRequest,
PRUint32 aFrame)

View File

@ -432,7 +432,9 @@ nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable)
}
nsRefPtr<gfxImageSurface> currentFrame;
if (NS_FAILED(image->CopyCurrentFrame(getter_AddRefs(currentFrame))))
if (NS_FAILED(image->CopyFrame(imgIContainer::FRAME_CURRENT,
imgIContainer::FLAG_SYNC_DECODE,
getter_AddRefs(currentFrame))))
continue;
PRInt32 height = currentFrame->Height();

View File

@ -306,6 +306,10 @@ NS_IMETHODIMP
nsMenuItemIconX::OnStartContainer(imgIRequest* aRequest,
imgIContainer* aContainer)
{
// Request a decode
NS_ABORT_IF_FALSE(aContainer, "who sent the notification then?");
aContainer->RequestDecode();
return NS_OK;
}
@ -344,7 +348,9 @@ nsMenuItemIconX::OnStopFrame(imgIRequest* aRequest,
return NS_ERROR_FAILURE;
nsRefPtr<gfxImageSurface> image;
imageContainer->CopyCurrentFrame(getter_AddRefs(image));
imageContainer->CopyFrame(imgIContainer::FRAME_CURRENT,
imgIContainer::FLAG_NONE,
getter_AddRefs(image));
PRInt32 height = image->Height();
PRInt32 stride = image->Stride();
@ -478,3 +484,9 @@ nsMenuItemIconX::OnStopRequest(imgIRequest* aRequest,
}
return NS_OK;
}
NS_IMETHODIMP
nsMenuItemIconX::OnDiscard(imgIRequest* aRequest)
{
return NS_OK;
}

View File

@ -69,7 +69,9 @@ GdkPixbuf*
nsImageToPixbuf::ImageToPixbuf(imgIContainer* aImage)
{
nsRefPtr<gfxImageSurface> frame;
aImage->CopyCurrentFrame(getter_AddRefs(frame));
aImage->CopyFrame(imgIContainer::FRAME_CURRENT,
imgIContainer::FLAG_SYNC_DECODE,
getter_AddRefs(frame));
return ImgSurfaceToPixbuf(frame, frame->Width(), frame->Height());
}

View File

@ -1619,7 +1619,9 @@ NS_IMETHODIMP nsWindow::SetCursor(imgIContainer* aCursor,
}
nsRefPtr<gfxImageSurface> frame;
aCursor->CopyCurrentFrame(getter_AddRefs(frame));
aCursor->CopyFrame(imgIContainer::FRAME_CURRENT,
imgIContainer::FLAG_SYNC_DECODE,
getter_AddRefs(frame));
if (!frame)
return NS_ERROR_NOT_AVAILABLE;

View File

@ -149,7 +149,9 @@ nsImageToClipboard::CreateFromImage ( imgIContainer* inImage, HANDLE* outBitmap
*outBitmap = nsnull;
nsRefPtr<gfxImageSurface> frame;
nsresult rv = inImage->CopyCurrentFrame(getter_AddRefs(frame));
nsresult rv = inImage->CopyFrame(imgIContainer::FRAME_CURRENT,
imgIContainer::FLAG_SYNC_DECODE,
getter_AddRefs(frame));
if (NS_FAILED(rv))
return rv;

View File

@ -692,7 +692,9 @@ nsresult nsWindowGfx::CreateIcon(imgIContainer *aContainer,
// Get the image data
nsRefPtr<gfxImageSurface> frame;
aContainer->CopyCurrentFrame(getter_AddRefs(frame));
aContainer->CopyFrame(imgIContainer::FRAME_CURRENT,
imgIContainer::FLAG_SYNC_DECODE,
getter_AddRefs(frame));
if (!frame)
return NS_ERROR_NOT_AVAILABLE;

View File

@ -601,7 +601,8 @@ nsBaseDragService::DrawDragForImage(nsPresContext* aPresContext,
gfxMatrix scale =
gfxMatrix().Scale(srcSize.width/outRect.Width(), srcSize.height/outRect.Height());
nsIntRect imgSize(0, 0, srcSize.width, srcSize.height);
imgContainer->Draw(ctx, gfxPattern::FILTER_GOOD, scale, outRect, imgSize);
imgContainer->Draw(ctx, gfxPattern::FILTER_GOOD, scale, outRect, imgSize,
imgIContainer::FLAG_SYNC_DECODE);
return NS_OK;
} else {
return aCanvas->RenderContexts(ctx, gfxPattern::FILTER_GOOD);