/src/mozilla-central/image/Decoder.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #include "Decoder.h" |
7 | | |
8 | | #include "DecodePool.h" |
9 | | #include "GeckoProfiler.h" |
10 | | #include "IDecodingTask.h" |
11 | | #include "ISurfaceProvider.h" |
12 | | #include "mozilla/gfx/2D.h" |
13 | | #include "mozilla/gfx/Point.h" |
14 | | #include "mozilla/Telemetry.h" |
15 | | #include "nsComponentManagerUtils.h" |
16 | | #include "nsProxyRelease.h" |
17 | | #include "nsServiceManagerUtils.h" |
18 | | |
19 | | using mozilla::gfx::IntPoint; |
20 | | using mozilla::gfx::IntSize; |
21 | | using mozilla::gfx::IntRect; |
22 | | using mozilla::gfx::SurfaceFormat; |
23 | | |
24 | | namespace mozilla { |
25 | | namespace image { |
26 | | |
27 | | class MOZ_STACK_CLASS AutoRecordDecoderTelemetry final |
28 | | { |
29 | | public: |
30 | | explicit AutoRecordDecoderTelemetry(Decoder* aDecoder) |
31 | | : mDecoder(aDecoder) |
32 | 0 | { |
33 | 0 | MOZ_ASSERT(mDecoder); |
34 | 0 |
|
35 | 0 | // Begin recording telemetry data. |
36 | 0 | mStartTime = TimeStamp::Now(); |
37 | 0 | } |
38 | | |
39 | | ~AutoRecordDecoderTelemetry() |
40 | 0 | { |
41 | 0 | // Finish telemetry. |
42 | 0 | mDecoder->mDecodeTime += (TimeStamp::Now() - mStartTime); |
43 | 0 | } |
44 | | |
45 | | private: |
46 | | Decoder* mDecoder; |
47 | | TimeStamp mStartTime; |
48 | | }; |
49 | | |
50 | | Decoder::Decoder(RasterImage* aImage) |
51 | | : mImageData(nullptr) |
52 | | , mImageDataLength(0) |
53 | | , mColormap(nullptr) |
54 | | , mColormapSize(0) |
55 | | , mImage(aImage) |
56 | | , mProgress(NoProgress) |
57 | | , mFrameCount(0) |
58 | | , mLoopLength(FrameTimeout::Zero()) |
59 | | , mDecoderFlags(DefaultDecoderFlags()) |
60 | | , mSurfaceFlags(DefaultSurfaceFlags()) |
61 | | , mInitialized(false) |
62 | | , mMetadataDecode(false) |
63 | | , mHaveExplicitOutputSize(false) |
64 | | , mInFrame(false) |
65 | | , mFinishedNewFrame(false) |
66 | | , mHasFrameToTake(false) |
67 | | , mReachedTerminalState(false) |
68 | | , mDecodeDone(false) |
69 | | , mError(false) |
70 | | , mShouldReportError(false) |
71 | | , mFinalizeFrames(true) |
72 | 0 | { } |
73 | | |
74 | | Decoder::~Decoder() |
75 | 0 | { |
76 | 0 | MOZ_ASSERT(mProgress == NoProgress || !mImage, |
77 | 0 | "Destroying Decoder without taking all its progress changes"); |
78 | 0 | MOZ_ASSERT(mInvalidRect.IsEmpty() || !mImage, |
79 | 0 | "Destroying Decoder without taking all its invalidations"); |
80 | 0 | mInitialized = false; |
81 | 0 |
|
82 | 0 | if (mImage && !NS_IsMainThread()) { |
83 | 0 | // Dispatch mImage to main thread to prevent it from being destructed by the |
84 | 0 | // decode thread. |
85 | 0 | NS_ReleaseOnMainThreadSystemGroup(mImage.forget()); |
86 | 0 | } |
87 | 0 | } |
88 | | |
89 | | /* |
90 | | * Common implementation of the decoder interface. |
91 | | */ |
92 | | |
93 | | nsresult |
94 | | Decoder::Init() |
95 | 0 | { |
96 | 0 | // No re-initializing |
97 | 0 | MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!"); |
98 | 0 |
|
99 | 0 | // All decoders must have a SourceBufferIterator. |
100 | 0 | MOZ_ASSERT(mIterator); |
101 | 0 |
|
102 | 0 | // Metadata decoders must not set an output size. |
103 | 0 | MOZ_ASSERT_IF(mMetadataDecode, !mHaveExplicitOutputSize); |
104 | 0 |
|
105 | 0 | // All decoders must be anonymous except for metadata decoders. |
106 | 0 | // XXX(seth): Soon that exception will be removed. |
107 | 0 | MOZ_ASSERT_IF(mImage, IsMetadataDecode()); |
108 | 0 |
|
109 | 0 | // Implementation-specific initialization. |
110 | 0 | nsresult rv = InitInternal(); |
111 | 0 |
|
112 | 0 | mInitialized = true; |
113 | 0 |
|
114 | 0 | return rv; |
115 | 0 | } |
116 | | |
117 | | LexerResult |
118 | | Decoder::Decode(IResumable* aOnResume /* = nullptr */) |
119 | 0 | { |
120 | 0 | MOZ_ASSERT(mInitialized, "Should be initialized here"); |
121 | 0 | MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator"); |
122 | 0 |
|
123 | 0 | // If we're already done, don't attempt to keep decoding. |
124 | 0 | if (GetDecodeDone()) { |
125 | 0 | return LexerResult(HasError() ? TerminalState::FAILURE |
126 | 0 | : TerminalState::SUCCESS); |
127 | 0 | } |
128 | 0 |
|
129 | 0 | LexerResult lexerResult(TerminalState::FAILURE); |
130 | 0 | { |
131 | 0 | AUTO_PROFILER_LABEL("Decoder::Decode", GRAPHICS); |
132 | 0 | AutoRecordDecoderTelemetry telemetry(this); |
133 | 0 |
|
134 | 0 | lexerResult = DoDecode(*mIterator, aOnResume); |
135 | 0 | }; |
136 | 0 |
|
137 | 0 | if (lexerResult.is<Yield>()) { |
138 | 0 | // We either need more data to continue (in which case either @aOnResume or |
139 | 0 | // the caller will reschedule us to run again later), or the decoder is |
140 | 0 | // yielding to allow the caller access to some intermediate output. |
141 | 0 | return lexerResult; |
142 | 0 | } |
143 | 0 | |
144 | 0 | // We reached a terminal state; we're now done decoding. |
145 | 0 | MOZ_ASSERT(lexerResult.is<TerminalState>()); |
146 | 0 | mReachedTerminalState = true; |
147 | 0 |
|
148 | 0 | // If decoding failed, record that fact. |
149 | 0 | if (lexerResult.as<TerminalState>() == TerminalState::FAILURE) { |
150 | 0 | PostError(); |
151 | 0 | } |
152 | 0 |
|
153 | 0 | // Perform final cleanup. |
154 | 0 | CompleteDecode(); |
155 | 0 |
|
156 | 0 | return LexerResult(HasError() ? TerminalState::FAILURE |
157 | 0 | : TerminalState::SUCCESS); |
158 | 0 | } |
159 | | |
160 | | LexerResult |
161 | | Decoder::TerminateFailure() |
162 | 0 | { |
163 | 0 | PostError(); |
164 | 0 |
|
165 | 0 | // Perform final cleanup if need be. |
166 | 0 | if (!mReachedTerminalState) { |
167 | 0 | mReachedTerminalState = true; |
168 | 0 | CompleteDecode(); |
169 | 0 | } |
170 | 0 |
|
171 | 0 | return LexerResult(TerminalState::FAILURE); |
172 | 0 | } |
173 | | |
174 | | bool |
175 | | Decoder::ShouldSyncDecode(size_t aByteLimit) |
176 | 0 | { |
177 | 0 | MOZ_ASSERT(aByteLimit > 0); |
178 | 0 | MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator"); |
179 | 0 |
|
180 | 0 | return mIterator->RemainingBytesIsNoMoreThan(aByteLimit); |
181 | 0 | } |
182 | | |
183 | | void |
184 | | Decoder::CompleteDecode() |
185 | 0 | { |
186 | 0 | // Implementation-specific finalization. |
187 | 0 | nsresult rv = BeforeFinishInternal(); |
188 | 0 | if (NS_FAILED(rv)) { |
189 | 0 | PostError(); |
190 | 0 | } |
191 | 0 |
|
192 | 0 | rv = HasError() ? FinishWithErrorInternal() |
193 | 0 | : FinishInternal(); |
194 | 0 | if (NS_FAILED(rv)) { |
195 | 0 | PostError(); |
196 | 0 | } |
197 | 0 |
|
198 | 0 | if (IsMetadataDecode()) { |
199 | 0 | // If this was a metadata decode and we never got a size, the decode failed. |
200 | 0 | if (!HasSize()) { |
201 | 0 | PostError(); |
202 | 0 | } |
203 | 0 | return; |
204 | 0 | } |
205 | 0 |
|
206 | 0 | // If the implementation left us mid-frame, finish that up. Note that it may |
207 | 0 | // have left us transparent. |
208 | 0 | if (mInFrame) { |
209 | 0 | PostHasTransparency(); |
210 | 0 | PostFrameStop(); |
211 | 0 | } |
212 | 0 |
|
213 | 0 | // If PostDecodeDone() has not been called, we may need to send teardown |
214 | 0 | // notifications if it is unrecoverable. |
215 | 0 | if (!mDecodeDone) { |
216 | 0 | // We should always report an error to the console in this case. |
217 | 0 | mShouldReportError = true; |
218 | 0 |
|
219 | 0 | if (GetCompleteFrameCount() > 0) { |
220 | 0 | // We're usable if we have at least one complete frame, so do exactly |
221 | 0 | // what we should have when the decoder completed. |
222 | 0 | PostHasTransparency(); |
223 | 0 | PostDecodeDone(); |
224 | 0 | } else { |
225 | 0 | // We're not usable. Record some final progress indicating the error. |
226 | 0 | mProgress |= FLAG_DECODE_COMPLETE | FLAG_HAS_ERROR; |
227 | 0 | } |
228 | 0 | } |
229 | 0 |
|
230 | 0 | if (mDecodeDone) { |
231 | 0 | MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame"); |
232 | 0 |
|
233 | 0 | // If this image wasn't animated and isn't a transient image, mark its frame |
234 | 0 | // as optimizable. We don't support optimizing animated images and |
235 | 0 | // optimizing transient images isn't worth it. |
236 | 0 | if (!HasAnimation() && |
237 | 0 | !(mDecoderFlags & DecoderFlags::IMAGE_IS_TRANSIENT) && |
238 | 0 | mCurrentFrame) { |
239 | 0 | mCurrentFrame->SetOptimizable(); |
240 | 0 | } |
241 | 0 | } |
242 | 0 | } |
243 | | |
244 | | void |
245 | | Decoder::SetOutputSize(const gfx::IntSize& aSize) |
246 | 0 | { |
247 | 0 | mOutputSize = Some(aSize); |
248 | 0 | mHaveExplicitOutputSize = true; |
249 | 0 | } |
250 | | |
251 | | Maybe<gfx::IntSize> |
252 | | Decoder::ExplicitOutputSize() const |
253 | 0 | { |
254 | 0 | MOZ_ASSERT_IF(mHaveExplicitOutputSize, mOutputSize); |
255 | 0 | return mHaveExplicitOutputSize ? mOutputSize : Nothing(); |
256 | 0 | } |
257 | | |
258 | | Maybe<uint32_t> |
259 | | Decoder::TakeCompleteFrameCount() |
260 | 0 | { |
261 | 0 | const bool finishedNewFrame = mFinishedNewFrame; |
262 | 0 | mFinishedNewFrame = false; |
263 | 0 | return finishedNewFrame ? Some(GetCompleteFrameCount()) : Nothing(); |
264 | 0 | } |
265 | | |
266 | | DecoderFinalStatus |
267 | | Decoder::FinalStatus() const |
268 | 0 | { |
269 | 0 | return DecoderFinalStatus(IsMetadataDecode(), |
270 | 0 | GetDecodeDone(), |
271 | 0 | HasError(), |
272 | 0 | ShouldReportError()); |
273 | 0 | } |
274 | | |
275 | | DecoderTelemetry |
276 | | Decoder::Telemetry() const |
277 | 0 | { |
278 | 0 | MOZ_ASSERT(mIterator); |
279 | 0 | return DecoderTelemetry(SpeedHistogram(), |
280 | 0 | mIterator ? mIterator->ByteCount() : 0, |
281 | 0 | mIterator ? mIterator->ChunkCount() : 0, |
282 | 0 | mDecodeTime); |
283 | 0 | } |
284 | | |
285 | | nsresult |
286 | | Decoder::AllocateFrame(const gfx::IntSize& aOutputSize, |
287 | | const gfx::IntRect& aFrameRect, |
288 | | gfx::SurfaceFormat aFormat, |
289 | | uint8_t aPaletteDepth, |
290 | | const Maybe<AnimationParams>& aAnimParams) |
291 | 0 | { |
292 | 0 | mCurrentFrame = AllocateFrameInternal(aOutputSize, aFrameRect, aFormat, |
293 | 0 | aPaletteDepth, aAnimParams, |
294 | 0 | std::move(mCurrentFrame)); |
295 | 0 |
|
296 | 0 | if (mCurrentFrame) { |
297 | 0 | mHasFrameToTake = true; |
298 | 0 |
|
299 | 0 | // Gather the raw pointers the decoders will use. |
300 | 0 | mCurrentFrame->GetImageData(&mImageData, &mImageDataLength); |
301 | 0 | mCurrentFrame->GetPaletteData(&mColormap, &mColormapSize); |
302 | 0 |
|
303 | 0 | // We should now be on |aFrameNum|. (Note that we're comparing the frame |
304 | 0 | // number, which is zero-based, with the frame count, which is one-based.) |
305 | 0 | MOZ_ASSERT_IF(aAnimParams, aAnimParams->mFrameNum + 1 == mFrameCount); |
306 | 0 |
|
307 | 0 | // If we're past the first frame, PostIsAnimated() should've been called. |
308 | 0 | MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation()); |
309 | 0 |
|
310 | 0 | // Update our state to reflect the new frame. |
311 | 0 | MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!"); |
312 | 0 | mInFrame = true; |
313 | 0 | } |
314 | 0 |
|
315 | 0 | return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE; |
316 | 0 | } |
317 | | |
318 | | RawAccessFrameRef |
319 | | Decoder::AllocateFrameInternal(const gfx::IntSize& aOutputSize, |
320 | | const gfx::IntRect& aFrameRect, |
321 | | SurfaceFormat aFormat, |
322 | | uint8_t aPaletteDepth, |
323 | | const Maybe<AnimationParams>& aAnimParams, |
324 | | RawAccessFrameRef&& aPreviousFrame) |
325 | 0 | { |
326 | 0 | if (HasError()) { |
327 | 0 | return RawAccessFrameRef(); |
328 | 0 | } |
329 | 0 | |
330 | 0 | uint32_t frameNum = aAnimParams ? aAnimParams->mFrameNum : 0; |
331 | 0 | if (frameNum != mFrameCount) { |
332 | 0 | MOZ_ASSERT_UNREACHABLE("Allocating frames out of order"); |
333 | 0 | return RawAccessFrameRef(); |
334 | 0 | } |
335 | 0 |
|
336 | 0 | if (aOutputSize.width <= 0 || aOutputSize.height <= 0 || |
337 | 0 | aFrameRect.Width() <= 0 || aFrameRect.Height() <= 0) { |
338 | 0 | NS_WARNING("Trying to add frame with zero or negative size"); |
339 | 0 | return RawAccessFrameRef(); |
340 | 0 | } |
341 | 0 |
|
342 | 0 | auto frame = MakeNotNull<RefPtr<imgFrame>>(); |
343 | 0 | bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA); |
344 | 0 | if (NS_FAILED(frame->InitForDecoder(aOutputSize, aFrameRect, aFormat, |
345 | 0 | aPaletteDepth, nonPremult, |
346 | 0 | aAnimParams, ShouldBlendAnimation()))) { |
347 | 0 | NS_WARNING("imgFrame::Init should succeed"); |
348 | 0 | return RawAccessFrameRef(); |
349 | 0 | } |
350 | 0 |
|
351 | 0 | RawAccessFrameRef ref = frame->RawAccessRef(); |
352 | 0 | if (!ref) { |
353 | 0 | frame->Abort(); |
354 | 0 | return RawAccessFrameRef(); |
355 | 0 | } |
356 | 0 | |
357 | 0 | if (frameNum == 1) { |
358 | 0 | MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated"); |
359 | 0 | aPreviousFrame->SetRawAccessOnly(); |
360 | 0 |
|
361 | 0 | // If we dispose of the first frame by clearing it, then the first frame's |
362 | 0 | // refresh area is all of itself. |
363 | 0 | // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR). |
364 | 0 | DisposalMethod prevDisposal = aPreviousFrame->GetDisposalMethod(); |
365 | 0 | if (prevDisposal == DisposalMethod::CLEAR || |
366 | 0 | prevDisposal == DisposalMethod::CLEAR_ALL || |
367 | 0 | prevDisposal == DisposalMethod::RESTORE_PREVIOUS) { |
368 | 0 | mFirstFrameRefreshArea = aPreviousFrame->GetRect(); |
369 | 0 | } |
370 | 0 | } |
371 | 0 |
|
372 | 0 | if (frameNum > 0) { |
373 | 0 | ref->SetRawAccessOnly(); |
374 | 0 |
|
375 | 0 | // Some GIFs are huge but only have a small area that they animate. We only |
376 | 0 | // need to refresh that small area when frame 0 comes around again. |
377 | 0 | mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea, |
378 | 0 | ref->GetBoundedBlendRect()); |
379 | 0 |
|
380 | 0 | if (ShouldBlendAnimation()) { |
381 | 0 | if (aPreviousFrame->GetDisposalMethod() != |
382 | 0 | DisposalMethod::RESTORE_PREVIOUS) { |
383 | 0 | // If the new restore frame is the direct previous frame, then we know |
384 | 0 | // the dirty rect is composed only of the current frame's blend rect and |
385 | 0 | // the restore frame's clear rect (if applicable) which are handled in |
386 | 0 | // filters. |
387 | 0 | mRestoreFrame = std::move(aPreviousFrame); |
388 | 0 | mRestoreDirtyRect.SetBox(0, 0, 0, 0); |
389 | 0 | } else { |
390 | 0 | // We only need the previous frame's dirty rect, because while there may |
391 | 0 | // have been several frames between us and mRestoreFrame, the only areas |
392 | 0 | // that changed are the restore frame's clear rect, the current frame |
393 | 0 | // blending rect, and the previous frame's blending rect. All else is |
394 | 0 | // forgotten due to us restoring the same frame again. |
395 | 0 | mRestoreDirtyRect = aPreviousFrame->GetBoundedBlendRect(); |
396 | 0 | } |
397 | 0 | } |
398 | 0 | } |
399 | 0 |
|
400 | 0 | mFrameCount++; |
401 | 0 |
|
402 | 0 | return ref; |
403 | 0 | } |
404 | | |
405 | | /* |
406 | | * Hook stubs. Override these as necessary in decoder implementations. |
407 | | */ |
408 | | |
409 | 0 | nsresult Decoder::InitInternal() { return NS_OK; } |
410 | 0 | nsresult Decoder::BeforeFinishInternal() { return NS_OK; } |
411 | 0 | nsresult Decoder::FinishInternal() { return NS_OK; } |
412 | | |
413 | | nsresult Decoder::FinishWithErrorInternal() |
414 | 0 | { |
415 | 0 | MOZ_ASSERT(!mInFrame); |
416 | 0 | return NS_OK; |
417 | 0 | } |
418 | | |
419 | | /* |
420 | | * Progress Notifications |
421 | | */ |
422 | | |
423 | | void |
424 | | Decoder::PostSize(int32_t aWidth, |
425 | | int32_t aHeight, |
426 | | Orientation aOrientation /* = Orientation()*/) |
427 | 0 | { |
428 | 0 | // Validate. |
429 | 0 | MOZ_ASSERT(aWidth >= 0, "Width can't be negative!"); |
430 | 0 | MOZ_ASSERT(aHeight >= 0, "Height can't be negative!"); |
431 | 0 |
|
432 | 0 | // Set our intrinsic size. |
433 | 0 | mImageMetadata.SetSize(aWidth, aHeight, aOrientation); |
434 | 0 |
|
435 | 0 | // Verify it is the expected size, if given. Note that this is only used by |
436 | 0 | // the ICO decoder for embedded image types, so only its subdecoders are |
437 | 0 | // required to handle failures in PostSize. |
438 | 0 | if (!IsExpectedSize()) { |
439 | 0 | PostError(); |
440 | 0 | return; |
441 | 0 | } |
442 | 0 | |
443 | 0 | // Set our output size if it's not already set. |
444 | 0 | if (!mOutputSize) { |
445 | 0 | mOutputSize = Some(IntSize(aWidth, aHeight)); |
446 | 0 | } |
447 | 0 |
|
448 | 0 | MOZ_ASSERT(mOutputSize->width <= aWidth && mOutputSize->height <= aHeight, |
449 | 0 | "Output size will result in upscaling"); |
450 | 0 |
|
451 | 0 | // Create a downscaler if we need to downscale. This is used by legacy |
452 | 0 | // decoders that haven't been converted to use SurfacePipe yet. |
453 | 0 | // XXX(seth): Obviously, we'll remove this once all decoders use SurfacePipe. |
454 | 0 | if (mOutputSize->width < aWidth || mOutputSize->height < aHeight) { |
455 | 0 | mDownscaler.emplace(*mOutputSize); |
456 | 0 | } |
457 | 0 |
|
458 | 0 | // Record this notification. |
459 | 0 | mProgress |= FLAG_SIZE_AVAILABLE; |
460 | 0 | } |
461 | | |
462 | | void |
463 | | Decoder::PostHasTransparency() |
464 | 0 | { |
465 | 0 | mProgress |= FLAG_HAS_TRANSPARENCY; |
466 | 0 | } |
467 | | |
468 | | void |
469 | | Decoder::PostIsAnimated(FrameTimeout aFirstFrameTimeout) |
470 | 0 | { |
471 | 0 | mProgress |= FLAG_IS_ANIMATED; |
472 | 0 | mImageMetadata.SetHasAnimation(); |
473 | 0 | mImageMetadata.SetFirstFrameTimeout(aFirstFrameTimeout); |
474 | 0 | } |
475 | | |
476 | | void |
477 | | Decoder::PostFrameStop(Opacity aFrameOpacity) |
478 | 0 | { |
479 | 0 | // We should be mid-frame |
480 | 0 | MOZ_ASSERT(!IsMetadataDecode(), "Stopping frame during metadata decode"); |
481 | 0 | MOZ_ASSERT(mInFrame, "Stopping frame when we didn't start one"); |
482 | 0 | MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one"); |
483 | 0 |
|
484 | 0 | // Update our state. |
485 | 0 | mInFrame = false; |
486 | 0 | mFinishedNewFrame = true; |
487 | 0 |
|
488 | 0 | mCurrentFrame->Finish(aFrameOpacity, mFinalizeFrames); |
489 | 0 |
|
490 | 0 | mProgress |= FLAG_FRAME_COMPLETE; |
491 | 0 |
|
492 | 0 | mLoopLength += mCurrentFrame->GetTimeout(); |
493 | 0 |
|
494 | 0 | // If we're not sending partial invalidations, then we send an invalidation |
495 | 0 | // here when the first frame is complete. |
496 | 0 | if (!ShouldSendPartialInvalidations() && mFrameCount == 1) { |
497 | 0 | mInvalidRect.UnionRect(mInvalidRect, |
498 | 0 | IntRect(IntPoint(), Size())); |
499 | 0 | } |
500 | 0 | } |
501 | | |
502 | | void |
503 | | Decoder::PostInvalidation(const gfx::IntRect& aRect, |
504 | | const Maybe<gfx::IntRect>& aRectAtOutputSize |
505 | | /* = Nothing() */) |
506 | 0 | { |
507 | 0 | // We should be mid-frame |
508 | 0 | MOZ_ASSERT(mInFrame, "Can't invalidate when not mid-frame!"); |
509 | 0 | MOZ_ASSERT(mCurrentFrame, "Can't invalidate when not mid-frame!"); |
510 | 0 |
|
511 | 0 | // Record this invalidation, unless we're not sending partial invalidations |
512 | 0 | // or we're past the first frame. |
513 | 0 | if (ShouldSendPartialInvalidations() && mFrameCount == 1) { |
514 | 0 | mInvalidRect.UnionRect(mInvalidRect, aRect); |
515 | 0 | mCurrentFrame->ImageUpdated(aRectAtOutputSize.valueOr(aRect)); |
516 | 0 | } |
517 | 0 | } |
518 | | |
519 | | void |
520 | | Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */) |
521 | 0 | { |
522 | 0 | MOZ_ASSERT(!IsMetadataDecode(), "Done with decoding in metadata decode"); |
523 | 0 | MOZ_ASSERT(!mInFrame, "Can't be done decoding if we're mid-frame!"); |
524 | 0 | MOZ_ASSERT(!mDecodeDone, "Decode already done!"); |
525 | 0 | mDecodeDone = true; |
526 | 0 |
|
527 | 0 | mImageMetadata.SetLoopCount(aLoopCount); |
528 | 0 |
|
529 | 0 | // Some metadata that we track should take into account every frame in the |
530 | 0 | // image. If this is a first-frame-only decode, our accumulated loop length |
531 | 0 | // and first frame refresh area only includes the first frame, so it's not |
532 | 0 | // correct and we don't record it. |
533 | 0 | if (!IsFirstFrameDecode()) { |
534 | 0 | mImageMetadata.SetLoopLength(mLoopLength); |
535 | 0 | mImageMetadata.SetFirstFrameRefreshArea(mFirstFrameRefreshArea); |
536 | 0 | } |
537 | 0 |
|
538 | 0 | mProgress |= FLAG_DECODE_COMPLETE; |
539 | 0 | } |
540 | | |
541 | | void |
542 | | Decoder::PostError() |
543 | 0 | { |
544 | 0 | mError = true; |
545 | 0 |
|
546 | 0 | if (mInFrame) { |
547 | 0 | MOZ_ASSERT(mCurrentFrame); |
548 | 0 | MOZ_ASSERT(mFrameCount > 0); |
549 | 0 | mCurrentFrame->Abort(); |
550 | 0 | mInFrame = false; |
551 | 0 | --mFrameCount; |
552 | 0 | mHasFrameToTake = false; |
553 | 0 | } |
554 | 0 | } |
555 | | |
556 | | } // namespace image |
557 | | } // namespace mozilla |