image/src/Decoder.cpp

branch
TOR_BUG_9701
changeset 10
ac0c01689b40
equal deleted inserted replaced
-1:000000000000 0:41b94fa7c811
1
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/. */
6
7 #include "Decoder.h"
8 #include "nsIConsoleService.h"
9 #include "nsIScriptError.h"
10 #include "GeckoProfiler.h"
11 #include "nsServiceManagerUtils.h"
12 #include "nsComponentManagerUtils.h"
13
14 namespace mozilla {
15 namespace image {
16
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)
32 {
33 }
34
35 Decoder::~Decoder()
36 {
37 mInitialized = false;
38 }
39
40 /*
41 * Common implementation of the decoder interface.
42 */
43
44 void
45 Decoder::Init()
46 {
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!");
50
51 // Fire OnStartDecode at init time to support bug 512435.
52 if (!IsSizeDecode())
53 mObserver->OnStartDecode();
54
55 // Implementation-specific initialization
56 InitInternal();
57
58 mInitialized = true;
59 }
60
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)
67 {
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!");
71
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();
80 }
81
82 // Implementation-specific initialization
83 InitInternal();
84 mInitialized = true;
85 }
86
87 void
88 Decoder::Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
89 {
90 PROFILER_LABEL("ImageDecoder", "Write");
91 MOZ_ASSERT(NS_IsMainThread() || aStrategy == DECODE_ASYNC);
92
93 // We're strict about decoder errors
94 NS_ABORT_IF_FALSE(!HasDecoderError(),
95 "Not allowed to make more decoder calls after error!");
96
97 // If a data error occured, just ignore future data
98 if (HasDataError())
99 return;
100
101 if (IsSizeDecode() && HasSize()) {
102 // More data came in since we found the size. We have nothing to do here.
103 return;
104 }
105
106 // Pass the data along to the implementation
107 WriteInternal(aBuffer, aCount, aStrategy);
108
109 // If we're a synchronous decoder and we need a new frame to proceed, let's
110 // create one and call it again.
111 while (aStrategy == DECODE_SYNC && NeedsNewFrame() && !HasDataError()) {
112 nsresult rv = AllocateFrame();
113
114 if (NS_SUCCEEDED(rv)) {
115 // Tell the decoder to use the data it saved when it asked for a new frame.
116 WriteInternal(nullptr, 0, aStrategy);
117 }
118 }
119 }
120
121 void
122 Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent)
123 {
124 MOZ_ASSERT(NS_IsMainThread());
125
126 // Implementation-specific finalization
127 if (!HasError())
128 FinishInternal();
129
130 // If the implementation left us mid-frame, finish that up.
131 if (mInFrame && !HasError())
132 PostFrameStop();
133
134 // If PostDecodeDone() has not been called, we need to sent teardown
135 // notifications.
136 if (!IsSizeDecode() && !mDecodeDone) {
137
138 // Log data errors to the error console
139 nsCOMPtr<nsIConsoleService> consoleService =
140 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
141 nsCOMPtr<nsIScriptError> errorObject =
142 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
143
144 if (consoleService && errorObject && !HasDecoderError()) {
145 nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated: ") +
146 NS_ConvertUTF8toUTF16(mImage.GetURIString()));
147
148 if (NS_SUCCEEDED(errorObject->InitWithWindowID(
149 msg,
150 NS_ConvertUTF8toUTF16(mImage.GetURIString()),
151 EmptyString(), 0, 0, nsIScriptError::errorFlag,
152 "Image", mImage.InnerWindowID()
153 ))) {
154 consoleService->LogMessage(errorObject);
155 }
156 }
157
158 bool usable = !HasDecoderError();
159 if (aShutdownIntent != RasterImage::eShutdownIntent_NotNeeded && !HasDecoderError()) {
160 // If we only have a data error, we're usable if we have at least one complete frame.
161 if (GetCompleteFrameCount() == 0) {
162 usable = false;
163 }
164 }
165
166 // If we're usable, do exactly what we should have when the decoder
167 // completed.
168 if (usable) {
169 if (mInFrame) {
170 PostFrameStop();
171 }
172 PostDecodeDone();
173 } else {
174 if (mObserver) {
175 mObserver->OnStopDecode(NS_ERROR_FAILURE);
176 }
177 }
178 }
179
180 // Set image metadata before calling DecodingComplete, because DecodingComplete calls Optimize().
181 mImageMetadata.SetOnImage(&mImage);
182
183 if (mDecodeDone) {
184 mImage.DecodingComplete();
185 }
186 }
187
188 void
189 Decoder::FinishSharedDecoder()
190 {
191 MOZ_ASSERT(NS_IsMainThread());
192
193 if (!HasError()) {
194 FinishInternal();
195 }
196 }
197
198 nsresult
199 Decoder::AllocateFrame()
200 {
201 MOZ_ASSERT(mNeedsNewFrame);
202 MOZ_ASSERT(NS_IsMainThread());
203
204 MarkFrameDirty();
205
206 nsresult rv;
207 imgFrame* frame = nullptr;
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, &frame);
215 } else {
216 rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
217 mNewFrameData.mOffsetY, mNewFrameData.mWidth,
218 mNewFrameData.mHeight, mNewFrameData.mFormat,
219 &mImageData, &mImageDataLength, &frame);
220 }
221
222 if (NS_SUCCEEDED(rv)) {
223 mCurrentFrame = frame;
224 } else {
225 mCurrentFrame = nullptr;
226 }
227
228 // Notify if appropriate
229 if (NS_SUCCEEDED(rv) && mNewFrameData.mFrameNum == mFrameCount) {
230 PostFrameStart();
231 } else if (NS_FAILED(rv)) {
232 PostDataError();
233 }
234
235 // Mark ourselves as not needing another frame before talking to anyone else
236 // so they can tell us if they need yet another.
237 mNeedsNewFrame = false;
238
239 return rv;
240 }
241
242 void
243 Decoder::FlushInvalidations()
244 {
245 NS_ABORT_IF_FALSE(!HasDecoderError(),
246 "Not allowed to make more decoder calls after error!");
247
248 // If we've got an empty invalidation rect, we have nothing to do
249 if (mInvalidRect.IsEmpty())
250 return;
251
252 if (mObserver) {
253 #ifdef XP_MACOSX
254 // Bug 703231
255 // Because of high quality down sampling on mac we show scan lines while decoding.
256 // Bypass this problem by redrawing the border.
257 if (mImageMetadata.HasSize()) {
258 nsIntRect mImageBound(0, 0, mImageMetadata.GetWidth(), mImageMetadata.GetHeight());
259
260 mInvalidRect.Inflate(1);
261 mInvalidRect = mInvalidRect.Intersect(mImageBound);
262 }
263 #endif
264 mObserver->FrameChanged(&mInvalidRect);
265 }
266
267 // Clear the invalidation rectangle
268 mInvalidRect.SetEmpty();
269 }
270
271 void
272 Decoder::SetSizeOnImage()
273 {
274 MOZ_ASSERT(mImageMetadata.HasSize(), "Should have size");
275 MOZ_ASSERT(mImageMetadata.HasOrientation(), "Should have orientation");
276
277 mImage.SetSize(mImageMetadata.GetWidth(),
278 mImageMetadata.GetHeight(),
279 mImageMetadata.GetOrientation());
280 }
281
282 /*
283 * Hook stubs. Override these as necessary in decoder implementations.
284 */
285
286 void Decoder::InitInternal() { }
287 void Decoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy) { }
288 void Decoder::FinishInternal() { }
289
290 /*
291 * Progress Notifications
292 */
293
294 void
295 Decoder::PostSize(int32_t aWidth,
296 int32_t aHeight,
297 Orientation aOrientation /* = Orientation()*/)
298 {
299 // Validate
300 NS_ABORT_IF_FALSE(aWidth >= 0, "Width can't be negative!");
301 NS_ABORT_IF_FALSE(aHeight >= 0, "Height can't be negative!");
302
303 // Tell the image
304 mImageMetadata.SetSize(aWidth, aHeight, aOrientation);
305
306 // Notify the observer
307 if (mObserver)
308 mObserver->OnStartContainer();
309 }
310
311 void
312 Decoder::PostFrameStart()
313 {
314 // We shouldn't already be mid-frame
315 NS_ABORT_IF_FALSE(!mInFrame, "Starting new frame but not done with old one!");
316
317 // We should take care of any invalidation region when wrapping up the
318 // previous frame
319 NS_ABORT_IF_FALSE(mInvalidRect.IsEmpty(),
320 "Start image frame with non-empty invalidation region!");
321
322 // Update our state to reflect the new frame
323 mFrameCount++;
324 mInFrame = true;
325
326 // Decoder implementations should only call this method if they successfully
327 // appended the frame to the image. So mFrameCount should always match that
328 // reported by the Image.
329 NS_ABORT_IF_FALSE(mFrameCount == mImage.GetNumFrames(),
330 "Decoder frame count doesn't match image's!");
331
332 // Fire notifications
333 if (mObserver) {
334 mObserver->OnStartFrame();
335 }
336 }
337
338 void
339 Decoder::PostFrameStop(FrameBlender::FrameAlpha aFrameAlpha /* = FrameBlender::kFrameHasAlpha */,
340 FrameBlender::FrameDisposalMethod aDisposalMethod /* = FrameBlender::kDisposeKeep */,
341 int32_t aTimeout /* = 0 */,
342 FrameBlender::FrameBlendMethod aBlendMethod /* = FrameBlender::kBlendOver */)
343 {
344 // We should be mid-frame
345 NS_ABORT_IF_FALSE(mInFrame, "Stopping frame when we didn't start one!");
346 NS_ABORT_IF_FALSE(mCurrentFrame, "Stopping frame when we don't have one!");
347
348 // Update our state
349 mInFrame = false;
350
351 if (aFrameAlpha == FrameBlender::kFrameOpaque) {
352 mCurrentFrame->SetHasNoAlpha();
353 }
354
355 mCurrentFrame->SetFrameDisposalMethod(aDisposalMethod);
356 mCurrentFrame->SetRawTimeout(aTimeout);
357 mCurrentFrame->SetBlendMethod(aBlendMethod);
358 mCurrentFrame->ImageUpdated(mCurrentFrame->GetRect());
359
360 // Flush any invalidations before we finish the frame
361 FlushInvalidations();
362
363 // Fire notifications
364 if (mObserver) {
365 mObserver->OnStopFrame();
366 if (mFrameCount > 1 && !mIsAnimated) {
367 mIsAnimated = true;
368 mObserver->OnImageIsAnimated();
369 }
370 }
371 }
372
373 void
374 Decoder::PostInvalidation(nsIntRect& aRect)
375 {
376 // We should be mid-frame
377 NS_ABORT_IF_FALSE(mInFrame, "Can't invalidate when not mid-frame!");
378 NS_ABORT_IF_FALSE(mCurrentFrame, "Can't invalidate when not mid-frame!");
379
380 // Account for the new region
381 mInvalidRect.UnionRect(mInvalidRect, aRect);
382 mCurrentFrame->ImageUpdated(aRect);
383 }
384
385 void
386 Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
387 {
388 NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!");
389 NS_ABORT_IF_FALSE(!mInFrame, "Can't be done decoding if we're mid-frame!");
390 NS_ABORT_IF_FALSE(!mDecodeDone, "Decode already done!");
391 mDecodeDone = true;
392
393 mImageMetadata.SetLoopCount(aLoopCount);
394 mImageMetadata.SetIsNonPremultiplied(GetDecodeFlags() & DECODER_NO_PREMULTIPLY_ALPHA);
395
396 if (mObserver) {
397 mObserver->OnStopDecode(NS_OK);
398 }
399 }
400
401 void
402 Decoder::PostDataError()
403 {
404 mDataError = true;
405 }
406
407 void
408 Decoder::PostDecoderError(nsresult aFailureCode)
409 {
410 NS_ABORT_IF_FALSE(NS_FAILED(aFailureCode), "Not a failure code!");
411
412 mFailCode = aFailureCode;
413
414 // XXXbholley - we should report the image URI here, but imgContainer
415 // needs to know its URI first
416 NS_WARNING("Image decoding error - This is probably a bug!");
417 }
418
419 void
420 Decoder::NeedNewFrame(uint32_t framenum, uint32_t x_offset, uint32_t y_offset,
421 uint32_t width, uint32_t height,
422 gfxImageFormat format,
423 uint8_t palette_depth /* = 0 */)
424 {
425 // Decoders should never call NeedNewFrame without yielding back to Write().
426 MOZ_ASSERT(!mNeedsNewFrame);
427
428 // We don't want images going back in time or skipping frames.
429 MOZ_ASSERT(framenum == mFrameCount || framenum == (mFrameCount - 1));
430
431 mNewFrameData = NewFrameData(framenum, x_offset, y_offset, width, height, format, palette_depth);
432 mNeedsNewFrame = true;
433 }
434
435 void
436 Decoder::MarkFrameDirty()
437 {
438 MOZ_ASSERT(NS_IsMainThread());
439
440 if (mCurrentFrame) {
441 mCurrentFrame->ApplyDirtToSurfaces();
442 }
443 }
444
445 } // namespace image
446 } // namespace mozilla

mercurial