Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / image / src / Decoder.cpp
blob4694958b0cc2b2a3ebb365f7f39846c0fa8dd9a2
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/. */
7 #include "Decoder.h"
8 #include "nsIConsoleService.h"
9 #include "nsIScriptError.h"
10 #include "GeckoProfiler.h"
11 #include "nsServiceManagerUtils.h"
12 #include "nsComponentManagerUtils.h"
14 namespace mozilla {
15 namespace image {
17 Decoder::Decoder(RasterImage &aImage)
18 : mImage(aImage)
19 , mCurrentFrame(nullptr)
20 , mImageData(nullptr)
21 , mColormap(nullptr)
22 , mDecodeFlags(0)
23 , mDecodeDone(false)
24 , mDataError(false)
25 , mFrameCount(0)
26 , mFailCode(NS_OK)
27 , mNeedsNewFrame(false)
28 , mInitialized(false)
29 , mSizeDecode(false)
30 , mInFrame(false)
31 , mIsAnimated(false)
35 Decoder::~Decoder()
37 mInitialized = false;
41 * Common implementation of the decoder interface.
44 void
45 Decoder::Init()
47 // No re-initializing
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.
52 if (!IsSizeDecode())
53 mObserver->OnStartDecode();
55 // Implementation-specific initialization
56 InitInternal();
58 mInitialized = true;
61 // Initializes a decoder whose image and observer is already being used by a
62 // parent decoder
63 void
64 Decoder::InitSharedDecoder(uint8_t* imageData, uint32_t imageDataLength,
65 uint32_t* colormap, uint32_t colormapSize,
66 imgFrame* currentFrame)
68 // No re-initializing
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;
74 mColormap = colormap;
75 mColormapSize = colormapSize;
76 mCurrentFrame = currentFrame;
77 // We have all the frame data, so we've started the frame.
78 if (!IsSizeDecode()) {
79 PostFrameStart();
82 // Implementation-specific initialization
83 InitInternal();
84 mInitialized = true;
87 void
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
100 if (HasDataError())
101 return;
103 if (IsSizeDecode() && HasSize()) {
104 // More data came in since we found the size. We have nothing to do here.
105 return;
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);
123 void
124 Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent)
126 MOZ_ASSERT(NS_IsMainThread());
128 // Implementation-specific finalization
129 if (!HasError())
130 FinishInternal();
132 // If the implementation left us mid-frame, finish that up.
133 if (mInFrame && !HasError())
134 PostFrameStop();
136 // If PostDecodeDone() has not been called, we need to sent teardown
137 // notifications.
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(
151 msg,
152 NS_ConvertUTF8toUTF16(mImage.GetURIString()),
153 EmptyString(), 0, 0, nsIScriptError::errorFlag,
154 "Image", mImage.InnerWindowID()
155 ))) {
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) {
164 usable = false;
168 // If we're usable, do exactly what we should have when the decoder
169 // completed.
170 if (usable) {
171 if (mInFrame) {
172 PostFrameStop();
174 PostDecodeDone();
175 } else {
176 if (mObserver) {
177 mObserver->OnStopDecode(NS_ERROR_FAILURE);
182 // Set image metadata before calling DecodingComplete, because DecodingComplete calls Optimize().
183 mImageMetadata.SetOnImage(&mImage);
185 if (mDecodeDone) {
186 mImage.DecodingComplete();
190 void
191 Decoder::FinishSharedDecoder()
193 MOZ_ASSERT(NS_IsMainThread());
195 if (!HasError()) {
196 FinishInternal();
200 nsresult
201 Decoder::AllocateFrame()
203 MOZ_ASSERT(mNeedsNewFrame);
204 MOZ_ASSERT(NS_IsMainThread());
206 nsresult rv;
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));
216 } else {
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;
226 } else {
227 mCurrentFrame = nullptr;
230 // Notify if appropriate
231 if (NS_SUCCEEDED(rv) && mNewFrameData.mFrameNum == mFrameCount) {
232 PostFrameStart();
233 } else if (NS_FAILED(rv)) {
234 PostDataError();
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;
241 return rv;
244 void
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())
252 return;
254 if (mObserver) {
255 #ifdef XP_MACOSX
256 // Bug 703231
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);
265 #endif
266 mObserver->FrameChanged(&mInvalidRect);
269 // Clear the invalidation rectangle
270 mInvalidRect.SetEmpty();
273 void
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
296 void
297 Decoder::PostSize(int32_t aWidth,
298 int32_t aHeight,
299 Orientation aOrientation /* = Orientation()*/)
301 // Validate
302 NS_ABORT_IF_FALSE(aWidth >= 0, "Width can't be negative!");
303 NS_ABORT_IF_FALSE(aHeight >= 0, "Height can't be negative!");
305 // Tell the image
306 mImageMetadata.SetSize(aWidth, aHeight, aOrientation);
308 // Notify the observer
309 if (mObserver)
310 mObserver->OnStartContainer();
313 void
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
320 // previous frame
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
325 mFrameCount++;
326 mInFrame = true;
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
335 if (mObserver) {
336 mObserver->OnStartFrame();
340 void
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!");
350 // Update our state
351 mInFrame = false;
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
366 if (mObserver) {
367 mObserver->OnStopFrame();
368 if (mFrameCount > 1 && !mIsAnimated) {
369 mIsAnimated = true;
370 mObserver->OnImageIsAnimated();
375 void
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);
387 void
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!");
393 mDecodeDone = true;
395 mImageMetadata.SetLoopCount(aLoopCount);
396 mImageMetadata.SetIsNonPremultiplied(GetDecodeFlags() & DECODER_NO_PREMULTIPLY_ALPHA);
398 if (mObserver) {
399 mObserver->OnStopDecode(NS_OK);
403 void
404 Decoder::PostDataError()
406 mDataError = true;
409 void
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!");
421 void
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;
437 } // namespace image
438 } // namespace mozilla