2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "nsIConsoleService.h"
9 #include "nsIScriptError.h"
10 #include "GeckoProfiler.h"
11 #include "nsServiceManagerUtils.h"
12 #include "nsComponentManagerUtils.h"
17 Decoder::Decoder(RasterImage
&aImage
)
19 , mCurrentFrame(nullptr)
27 , mNeedsNewFrame(false)
41 * Common implementation of the decoder interface.
48 NS_ABORT_IF_FALSE(!mInitialized
, "Can't re-initialize a decoder!");
49 NS_ABORT_IF_FALSE(mObserver
, "Need an observer!");
51 // Fire OnStartDecode at init time to support bug 512435.
53 mObserver
->OnStartDecode();
55 // Implementation-specific initialization
61 // Initializes a decoder whose image and observer is already being used by a
64 Decoder::InitSharedDecoder(uint8_t* imageData
, uint32_t imageDataLength
,
65 uint32_t* colormap
, uint32_t colormapSize
,
66 imgFrame
* currentFrame
)
69 NS_ABORT_IF_FALSE(!mInitialized
, "Can't re-initialize a decoder!");
70 NS_ABORT_IF_FALSE(mObserver
, "Need an observer!");
72 mImageData
= imageData
;
73 mImageDataLength
= imageDataLength
;
75 mColormapSize
= colormapSize
;
76 mCurrentFrame
= currentFrame
;
77 // We have all the frame data, so we've started the frame.
78 if (!IsSizeDecode()) {
82 // Implementation-specific initialization
88 Decoder::Write(const char* aBuffer
, uint32_t aCount
, DecodeStrategy aStrategy
)
90 PROFILER_LABEL("ImageDecoder", "Write",
91 js::ProfileEntry::Category::GRAPHICS
);
93 MOZ_ASSERT(NS_IsMainThread() || aStrategy
== DECODE_ASYNC
);
95 // We're strict about decoder errors
96 NS_ABORT_IF_FALSE(!HasDecoderError(),
97 "Not allowed to make more decoder calls after error!");
99 // If a data error occured, just ignore future data
103 if (IsSizeDecode() && HasSize()) {
104 // More data came in since we found the size. We have nothing to do here.
108 // Pass the data along to the implementation
109 WriteInternal(aBuffer
, aCount
, aStrategy
);
111 // If we're a synchronous decoder and we need a new frame to proceed, let's
112 // create one and call it again.
113 while (aStrategy
== DECODE_SYNC
&& NeedsNewFrame() && !HasDataError()) {
114 nsresult rv
= AllocateFrame();
116 if (NS_SUCCEEDED(rv
)) {
117 // Tell the decoder to use the data it saved when it asked for a new frame.
118 WriteInternal(nullptr, 0, aStrategy
);
124 Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent
)
126 MOZ_ASSERT(NS_IsMainThread());
128 // Implementation-specific finalization
132 // If the implementation left us mid-frame, finish that up.
133 if (mInFrame
&& !HasError())
136 // If PostDecodeDone() has not been called, we need to sent teardown
138 if (!IsSizeDecode() && !mDecodeDone
) {
140 // Log data errors to the error console
141 nsCOMPtr
<nsIConsoleService
> consoleService
=
142 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
143 nsCOMPtr
<nsIScriptError
> errorObject
=
144 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
);
146 if (consoleService
&& errorObject
&& !HasDecoderError()) {
147 nsAutoString
msg(NS_LITERAL_STRING("Image corrupt or truncated: ") +
148 NS_ConvertUTF8toUTF16(mImage
.GetURIString()));
150 if (NS_SUCCEEDED(errorObject
->InitWithWindowID(
152 NS_ConvertUTF8toUTF16(mImage
.GetURIString()),
153 EmptyString(), 0, 0, nsIScriptError::errorFlag
,
154 "Image", mImage
.InnerWindowID()
156 consoleService
->LogMessage(errorObject
);
160 bool usable
= !HasDecoderError();
161 if (aShutdownIntent
!= RasterImage::eShutdownIntent_NotNeeded
&& !HasDecoderError()) {
162 // If we only have a data error, we're usable if we have at least one complete frame.
163 if (GetCompleteFrameCount() == 0) {
168 // If we're usable, do exactly what we should have when the decoder
177 mObserver
->OnStopDecode(NS_ERROR_FAILURE
);
182 // Set image metadata before calling DecodingComplete, because DecodingComplete calls Optimize().
183 mImageMetadata
.SetOnImage(&mImage
);
186 mImage
.DecodingComplete();
191 Decoder::FinishSharedDecoder()
193 MOZ_ASSERT(NS_IsMainThread());
201 Decoder::AllocateFrame()
203 MOZ_ASSERT(mNeedsNewFrame
);
204 MOZ_ASSERT(NS_IsMainThread());
207 nsRefPtr
<imgFrame
> frame
;
208 if (mNewFrameData
.mPaletteDepth
) {
209 rv
= mImage
.EnsureFrame(mNewFrameData
.mFrameNum
, mNewFrameData
.mOffsetX
,
210 mNewFrameData
.mOffsetY
, mNewFrameData
.mWidth
,
211 mNewFrameData
.mHeight
, mNewFrameData
.mFormat
,
212 mNewFrameData
.mPaletteDepth
,
213 &mImageData
, &mImageDataLength
,
214 &mColormap
, &mColormapSize
,
215 getter_AddRefs(frame
));
217 rv
= mImage
.EnsureFrame(mNewFrameData
.mFrameNum
, mNewFrameData
.mOffsetX
,
218 mNewFrameData
.mOffsetY
, mNewFrameData
.mWidth
,
219 mNewFrameData
.mHeight
, mNewFrameData
.mFormat
,
220 &mImageData
, &mImageDataLength
,
221 getter_AddRefs(frame
));
224 if (NS_SUCCEEDED(rv
)) {
225 mCurrentFrame
= frame
;
227 mCurrentFrame
= nullptr;
230 // Notify if appropriate
231 if (NS_SUCCEEDED(rv
) && mNewFrameData
.mFrameNum
== mFrameCount
) {
233 } else if (NS_FAILED(rv
)) {
237 // Mark ourselves as not needing another frame before talking to anyone else
238 // so they can tell us if they need yet another.
239 mNeedsNewFrame
= false;
245 Decoder::FlushInvalidations()
247 NS_ABORT_IF_FALSE(!HasDecoderError(),
248 "Not allowed to make more decoder calls after error!");
250 // If we've got an empty invalidation rect, we have nothing to do
251 if (mInvalidRect
.IsEmpty())
257 // Because of high quality down sampling on mac we show scan lines while decoding.
258 // Bypass this problem by redrawing the border.
259 if (mImageMetadata
.HasSize()) {
260 nsIntRect
mImageBound(0, 0, mImageMetadata
.GetWidth(), mImageMetadata
.GetHeight());
262 mInvalidRect
.Inflate(1);
263 mInvalidRect
= mInvalidRect
.Intersect(mImageBound
);
266 mObserver
->FrameChanged(&mInvalidRect
);
269 // Clear the invalidation rectangle
270 mInvalidRect
.SetEmpty();
274 Decoder::SetSizeOnImage()
276 MOZ_ASSERT(mImageMetadata
.HasSize(), "Should have size");
277 MOZ_ASSERT(mImageMetadata
.HasOrientation(), "Should have orientation");
279 mImage
.SetSize(mImageMetadata
.GetWidth(),
280 mImageMetadata
.GetHeight(),
281 mImageMetadata
.GetOrientation());
285 * Hook stubs. Override these as necessary in decoder implementations.
288 void Decoder::InitInternal() { }
289 void Decoder::WriteInternal(const char* aBuffer
, uint32_t aCount
, DecodeStrategy aStrategy
) { }
290 void Decoder::FinishInternal() { }
293 * Progress Notifications
297 Decoder::PostSize(int32_t aWidth
,
299 Orientation aOrientation
/* = Orientation()*/)
302 NS_ABORT_IF_FALSE(aWidth
>= 0, "Width can't be negative!");
303 NS_ABORT_IF_FALSE(aHeight
>= 0, "Height can't be negative!");
306 mImageMetadata
.SetSize(aWidth
, aHeight
, aOrientation
);
308 // Notify the observer
310 mObserver
->OnStartContainer();
314 Decoder::PostFrameStart()
316 // We shouldn't already be mid-frame
317 NS_ABORT_IF_FALSE(!mInFrame
, "Starting new frame but not done with old one!");
319 // We should take care of any invalidation region when wrapping up the
321 NS_ABORT_IF_FALSE(mInvalidRect
.IsEmpty(),
322 "Start image frame with non-empty invalidation region!");
324 // Update our state to reflect the new frame
328 // Decoder implementations should only call this method if they successfully
329 // appended the frame to the image. So mFrameCount should always match that
330 // reported by the Image.
331 NS_ABORT_IF_FALSE(mFrameCount
== mImage
.GetNumFrames(),
332 "Decoder frame count doesn't match image's!");
334 // Fire notifications
336 mObserver
->OnStartFrame();
341 Decoder::PostFrameStop(FrameBlender::FrameAlpha aFrameAlpha
/* = FrameBlender::kFrameHasAlpha */,
342 FrameBlender::FrameDisposalMethod aDisposalMethod
/* = FrameBlender::kDisposeKeep */,
343 int32_t aTimeout
/* = 0 */,
344 FrameBlender::FrameBlendMethod aBlendMethod
/* = FrameBlender::kBlendOver */)
346 // We should be mid-frame
347 NS_ABORT_IF_FALSE(mInFrame
, "Stopping frame when we didn't start one!");
348 NS_ABORT_IF_FALSE(mCurrentFrame
, "Stopping frame when we don't have one!");
353 if (aFrameAlpha
== FrameBlender::kFrameOpaque
) {
354 mCurrentFrame
->SetHasNoAlpha();
357 mCurrentFrame
->SetFrameDisposalMethod(aDisposalMethod
);
358 mCurrentFrame
->SetRawTimeout(aTimeout
);
359 mCurrentFrame
->SetBlendMethod(aBlendMethod
);
360 mCurrentFrame
->ImageUpdated(mCurrentFrame
->GetRect());
362 // Flush any invalidations before we finish the frame
363 FlushInvalidations();
365 // Fire notifications
367 mObserver
->OnStopFrame();
368 if (mFrameCount
> 1 && !mIsAnimated
) {
370 mObserver
->OnImageIsAnimated();
376 Decoder::PostInvalidation(nsIntRect
& aRect
)
378 // We should be mid-frame
379 NS_ABORT_IF_FALSE(mInFrame
, "Can't invalidate when not mid-frame!");
380 NS_ABORT_IF_FALSE(mCurrentFrame
, "Can't invalidate when not mid-frame!");
382 // Account for the new region
383 mInvalidRect
.UnionRect(mInvalidRect
, aRect
);
384 mCurrentFrame
->ImageUpdated(aRect
);
388 Decoder::PostDecodeDone(int32_t aLoopCount
/* = 0 */)
390 NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!");
391 NS_ABORT_IF_FALSE(!mInFrame
, "Can't be done decoding if we're mid-frame!");
392 NS_ABORT_IF_FALSE(!mDecodeDone
, "Decode already done!");
395 mImageMetadata
.SetLoopCount(aLoopCount
);
396 mImageMetadata
.SetIsNonPremultiplied(GetDecodeFlags() & DECODER_NO_PREMULTIPLY_ALPHA
);
399 mObserver
->OnStopDecode(NS_OK
);
404 Decoder::PostDataError()
410 Decoder::PostDecoderError(nsresult aFailureCode
)
412 NS_ABORT_IF_FALSE(NS_FAILED(aFailureCode
), "Not a failure code!");
414 mFailCode
= aFailureCode
;
416 // XXXbholley - we should report the image URI here, but imgContainer
417 // needs to know its URI first
418 NS_WARNING("Image decoding error - This is probably a bug!");
422 Decoder::NeedNewFrame(uint32_t framenum
, uint32_t x_offset
, uint32_t y_offset
,
423 uint32_t width
, uint32_t height
,
424 gfx::SurfaceFormat format
,
425 uint8_t palette_depth
/* = 0 */)
427 // Decoders should never call NeedNewFrame without yielding back to Write().
428 MOZ_ASSERT(!mNeedsNewFrame
);
430 // We don't want images going back in time or skipping frames.
431 MOZ_ASSERT(framenum
== mFrameCount
|| framenum
== (mFrameCount
- 1));
433 mNewFrameData
= NewFrameData(framenum
, x_offset
, y_offset
, width
, height
, format
, palette_depth
);
434 mNeedsNewFrame
= true;
438 } // namespace mozilla