зеркало из https://github.com/mozilla/pjs.git
Bug 435296 - Decode-On-Draw. r=joe,roc,bz,dolske,peterw sr=vlad
This commit is contained in:
Родитель
e65a77e1b7
Коммит
92e80175ef
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,7 +3121,9 @@ ImageRenderer::PrepareImage()
|
|||
mImageContainer.swap(srcImage);
|
||||
} else {
|
||||
nsCOMPtr<imgIContainer> subImage;
|
||||
nsresult rv = srcImage->ExtractCurrentFrame(actualCropRect,
|
||||
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; "
|
||||
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,8 +139,8 @@ nsImageLoader::Load(imgIRequest *aImage)
|
|||
NS_IMETHODIMP nsImageLoader::OnStartContainer(imgIRequest *aRequest,
|
||||
imgIContainer *aImage)
|
||||
{
|
||||
if (aImage)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aImage, "Who's calling us then?");
|
||||
|
||||
/* Get requested animation policy from the pres context:
|
||||
* normal = 0
|
||||
* one frame = 1
|
||||
|
@ -149,7 +149,7 @@ NS_IMETHODIMP nsImageLoader::OnStartContainer(imgIRequest *aRequest,
|
|||
aImage->SetAnimationMode(mFrame->PresContext()->ImageAnimationMode());
|
||||
// Ensure the animation (if any) is started.
|
||||
aImage->StartAnimation();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -159,26 +159,39 @@ 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");
|
||||
if (!mRequest) {
|
||||
// We're in the middle of a paint anyway
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Take requested actions
|
||||
if (mActions & ACTION_REFLOW_ON_DECODE) {
|
||||
DoReflow();
|
||||
}
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
||||
// Draw the background image
|
||||
RedrawDirtyFrame(nsnull);
|
||||
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,
|
||||
|
@ -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::RedrawDirtyFrame(const nsRect* aDamageRect)
|
||||
nsImageLoader::DoReflow()
|
||||
{
|
||||
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.
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsImageLoader::DoRedraw(const nsRect* aDamageRect)
|
||||
{
|
||||
// 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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_) */
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ public:
|
|||
|
||||
void PaintImage(nsIRenderingContext& aRenderingContext,
|
||||
const nsRect& aDirtyRect,
|
||||
nsPoint aPt);
|
||||
nsPoint aPt, PRUint32 aFlags);
|
||||
|
||||
protected:
|
||||
nsImageBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,6 +72,7 @@ nsBMPDecoder::nsBMPDecoder()
|
|||
mState = eRLEStateInitial;
|
||||
mStateData = 0;
|
||||
mLOH = WIN_HEADER_LENGTH;
|
||||
mError = PR_FALSE;
|
||||
}
|
||||
|
||||
nsBMPDecoder::~nsBMPDecoder()
|
||||
|
@ -82,31 +82,37 @@ nsBMPDecoder::~nsBMPDecoder()
|
|||
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"));
|
||||
|
||||
// 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->OnStopFrame(nsnull, 0);
|
||||
mObserver->OnStopContainer(nsnull, mImage);
|
||||
mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
|
||||
mObserver = 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;
|
||||
// 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,9 +309,11 @@ NS_METHOD nsBMPDecoder::ProcessData(const char* aBuffer, PRUint32 aCount)
|
|||
memset(mImageData, 0, imageLength);
|
||||
}
|
||||
|
||||
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
|
||||
if (mColors && (mPos >= mLOH && (mPos < (mLOH + mNumColors * bpc)))) {
|
||||
|
@ -599,6 +619,7 @@ NS_METHOD nsBMPDecoder::ProcessData(const char* aBuffer, PRUint32 aCount)
|
|||
rv = mImage->FrameUpdated(0, r);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (mObserver)
|
||||
mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
|
||||
mOldLine = mCurLine;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
NS_IMETHODIMP nsICODecoder::Init(imgIContainer *aImage,
|
||||
imgIDecoderObserver *aObserver,
|
||||
PRUint32 aFlags)
|
||||
{
|
||||
mObserver = do_QueryInterface(aLoad);
|
||||
// Grab parameters
|
||||
mImage = aImage;
|
||||
mObserver = aObserver;
|
||||
mFlags = aFlags;
|
||||
|
||||
mImage = do_CreateInstance("@mozilla.org/image/container;2");
|
||||
if (!mImage)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
// 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 nsICODecoder::Close()
|
||||
NS_IMETHODIMP nsICODecoder::Close(PRUint32 aFlags)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// 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);
|
||||
nsresult rv = mImage->FrameUpdated(0, r);
|
||||
rv = mImage->FrameUpdated(0, r);
|
||||
|
||||
mImage->DecodingComplete();
|
||||
|
||||
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);
|
||||
mObserver = 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,9 +326,11 @@ nsresult nsICODecoder::ProcessData(const char* aBuffer, PRUint32 aCount) {
|
|||
gfxASurface::ImageFormatARGB32, (PRUint8**)&mImageData, &imageLength);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (mObserver) {
|
||||
mObserver->OnStartFrame(nsnull, 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
if (mColors && (mPos >= mImageOffset + BITMAPINFOSIZE) &&
|
||||
(mPos < (mImageOffset + BITMAPINFOSIZE + mNumColors * 4))) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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,13 +136,21 @@ 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;
|
||||
|
||||
mImageContainer = do_CreateInstance("@mozilla.org/image/container;2");
|
||||
aLoad->SetImage(mImageContainer);
|
||||
// Fire OnStartDecode at init time to support bug 512435
|
||||
if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && mObserver)
|
||||
mObserver->OnStartDecode(nsnull);
|
||||
|
||||
// Start with the version (GIF89a|GIF87a)
|
||||
mGIFStruct.state = gif_type;
|
||||
|
@ -159,14 +166,20 @@ NS_IMETHODIMP nsGIFDecoder2::Init(imgILoad *aLoad)
|
|||
|
||||
//******************************************************************************
|
||||
/* void close (); */
|
||||
NS_IMETHODIMP nsGIFDecoder2::Close()
|
||||
NS_IMETHODIMP nsGIFDecoder2::Close(PRUint32 aFlags)
|
||||
{
|
||||
// Send notifications if appropriate
|
||||
if (!(mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) &&
|
||||
!mError && !(aFlags & CLOSE_FLAG_DONTNOTIFY)) {
|
||||
if (mCurrentFrame == mGIFStruct.images_decoded)
|
||||
EndImageFrame();
|
||||
EndGIF();
|
||||
EndGIF(/* aSuccess = */ PR_TRUE);
|
||||
}
|
||||
|
||||
PR_FREEIF(mGIFStruct.local_colormap);
|
||||
|
||||
mImageContainer = nsnull;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -179,7 +192,7 @@ NS_IMETHODIMP nsGIFDecoder2::Flush()
|
|||
|
||||
//******************************************************************************
|
||||
/* static callback from nsIInputStream::ReadSegments */
|
||||
static NS_METHOD ReadDataOut(nsIInputStream* in,
|
||||
NS_METHOD nsGIFDecoder2::ReadDataOut(nsIInputStream* in,
|
||||
void* closure,
|
||||
const char* fromRawSegment,
|
||||
PRUint32 toOffset,
|
||||
|
@ -187,12 +200,36 @@ static NS_METHOD ReadDataOut(nsIInputStream* in,
|
|||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -294,30 +324,33 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -394,7 +430,10 @@ void nsGIFDecoder2::EndImageFrame()
|
|||
nsIntRect r(0, realFrameHeight,
|
||||
mGIFStruct.screen_width,
|
||||
mGIFStruct.screen_height - realFrameHeight);
|
||||
mObserver->OnDataAvailable(nsnull, imgCurFrame == PRUint32(mGIFStruct.images_decoded), &r);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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 (mObserver)
|
||||
{
|
||||
mObserver->OnStopFrame(nsnull, 0);
|
||||
mObserver->OnStopContainer(nsnull, mImage);
|
||||
mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
|
||||
}
|
||||
// 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);
|
||||
|
||||
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))
|
||||
// Performance isn't critical here, so our update rectangle is
|
||||
// always the full icon
|
||||
nsIntRect r(0, 0, decoder->mWidth, decoder->mHeight);
|
||||
|
||||
// Loop until the input data is gone
|
||||
while (aCount > 0) {
|
||||
switch (decoder->mState) {
|
||||
case iconStateStart:
|
||||
|
||||
// Grab the width
|
||||
decoder->mWidth = (PRUint8)*aFromSegment;
|
||||
|
||||
// Book Keeping
|
||||
aFromSegment++;
|
||||
aCount--;
|
||||
decoder->mState = iconStateHaveHeight;
|
||||
break;
|
||||
|
||||
case iconStateHaveHeight:
|
||||
|
||||
// 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);
|
||||
|
||||
if (mObserver)
|
||||
mObserver->OnStartFrame(nsnull, 0);
|
||||
// Book Keeping
|
||||
aFromSegment++;
|
||||
aCount--;
|
||||
decoder->mState = iconStateReadPixels;
|
||||
break;
|
||||
|
||||
// Ensure that there enough in the inputStream
|
||||
NS_ENSURE_TRUE(count >= imageLen, NS_ERROR_UNEXPECTED);
|
||||
case iconStateReadPixels:
|
||||
|
||||
// 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);
|
||||
// How many bytes are we reading?
|
||||
bytesToRead = PR_MAX(aCount,
|
||||
decoder->mPixBytesTotal - decoder->mPixBytesRead);
|
||||
|
||||
// Notify the image...
|
||||
nsIntRect r(0, 0, w, h);
|
||||
rv = mImage->FrameUpdated(0, r);
|
||||
if (NS_FAILED(rv))
|
||||
// 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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, },
|
||||
|
|
|
@ -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,6 +456,7 @@ 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));
|
||||
|
||||
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,6 +737,7 @@ 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);
|
||||
if (mObserver)
|
||||
mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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,6 +210,7 @@ void nsPNGDecoder::EndImageFrame()
|
|||
}
|
||||
PRUint32 curFrame;
|
||||
mImage->GetCurrentFrameIndex(&curFrame);
|
||||
if (mObserver)
|
||||
mObserver->OnDataAvailable(nsnull, curFrame == numFrames - 1, &mFrameRect);
|
||||
}
|
||||
|
||||
|
@ -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,9 +719,10 @@ 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) {
|
||||
if (height < PR_INT32_MAX / (width * channels))
|
||||
|
@ -800,6 +866,7 @@ row_callback(png_structp png_ptr, png_bytep new_row,
|
|||
}
|
||||
PRUint32 curFrame;
|
||||
decoder->mImage->GetCurrentFrameIndex(&curFrame);
|
||||
if (decoder->mObserver)
|
||||
decoder->mObserver->OnDataAvailable(nsnull, curFrame == numFrames - 1, &r);
|
||||
}
|
||||
}
|
||||
|
@ -845,20 +912,16 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -54,7 +54,6 @@ XPIDLSRCS = \
|
|||
imgIDecoder.idl \
|
||||
imgIDecoderObserver.idl \
|
||||
imgIEncoder.idl \
|
||||
imgILoad.idl \
|
||||
imgILoader.idl \
|
||||
imgIRequest.idl \
|
||||
imgITools.idl \
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
[noscript] gfxImageSurface copyCurrentFrame();
|
||||
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] 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();
|
||||
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
* @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);
|
||||
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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();
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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 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__ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,29 +276,16 @@ nsresult imgRequest::NotifyProxyListener(imgRequestProxy *proxy)
|
|||
mImage->GetCurrentFrameIndex(&frame);
|
||||
proxy->OnStartFrame(frame);
|
||||
|
||||
if (!(mState & onStopContainer)) {
|
||||
// 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); // 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
|
||||
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");
|
||||
|
@ -301,7 +293,9 @@ nsresult imgRequest::NotifyProxyListener(imgRequestProxy *proxy)
|
|||
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();
|
||||
}
|
||||
|
||||
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,14 +547,19 @@ 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;
|
||||
|
||||
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
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = mDecoder->Init(static_cast<imgILoad*>(this));
|
||||
// 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->Init failed\n", this));
|
||||
|
||||
("[this=%p] imgRequest::OnDataAvailable -- "
|
||||
"copy to container failed\n", this));
|
||||
this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
|
||||
|
||||
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);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
PR_LOG(gImgLog, PR_LOG_WARNING,
|
||||
("[this=%p] imgRequest::OnDataAvailable -- mDecoder->WriteFrom 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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
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)
|
||||
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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
|
|
@ -44,7 +44,8 @@ relativesrcdir = modules/libpr0n/test/mochitest
|
|||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_TEST_FILES = test_bug399925.html \
|
||||
_TEST_FILES = imgutils.js \
|
||||
test_bug399925.html \
|
||||
bug399925.gif \
|
||||
bug468160.sjs \
|
||||
test_bug468160.html \
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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 ========== */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче