Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/image/Decoder.h
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
#ifndef mozilla_image_Decoder_h
7
#define mozilla_image_Decoder_h
8
9
#include "FrameAnimator.h"
10
#include "RasterImage.h"
11
#include "mozilla/Maybe.h"
12
#include "mozilla/NotNull.h"
13
#include "mozilla/RefPtr.h"
14
#include "AnimationParams.h"
15
#include "DecoderFlags.h"
16
#include "Downscaler.h"
17
#include "ImageMetadata.h"
18
#include "Orientation.h"
19
#include "SourceBuffer.h"
20
#include "StreamingLexer.h"
21
#include "SurfaceFlags.h"
22
23
namespace mozilla {
24
25
namespace Telemetry {
26
  enum HistogramID : uint32_t;
27
} // namespace Telemetry
28
29
namespace image {
30
31
class imgFrame;
32
33
struct DecoderFinalStatus final
34
{
35
  DecoderFinalStatus(bool aWasMetadataDecode,
36
                     bool aFinished,
37
                     bool aHadError,
38
                     bool aShouldReportError)
39
    : mWasMetadataDecode(aWasMetadataDecode)
40
    , mFinished(aFinished)
41
    , mHadError(aHadError)
42
    , mShouldReportError(aShouldReportError)
43
0
  { }
44
45
  /// True if this was a metadata decode.
46
  const bool mWasMetadataDecode : 1;
47
48
  /// True if this decoder finished, whether successfully or due to failure.
49
  const bool mFinished : 1;
50
51
  /// True if this decoder encountered an error.
52
  const bool mHadError : 1;
53
54
  /// True if this decoder encountered the kind of error that should be reported
55
  /// to the console.
56
  const bool mShouldReportError : 1;
57
};
58
59
struct DecoderTelemetry final
60
{
61
  DecoderTelemetry(const Maybe<Telemetry::HistogramID>& aSpeedHistogram,
62
                   size_t aBytesDecoded,
63
                   uint32_t aChunkCount,
64
                   TimeDuration aDecodeTime)
65
    : mSpeedHistogram(aSpeedHistogram)
66
    , mBytesDecoded(aBytesDecoded)
67
    , mChunkCount(aChunkCount)
68
    , mDecodeTime(aDecodeTime)
69
0
  { }
70
71
  /// @return our decoder's speed, in KBps.
72
  int32_t Speed() const
73
0
  {
74
0
    return mBytesDecoded / (1024 * mDecodeTime.ToSeconds());
75
0
  }
76
77
  /// @return our decoder's decode time, in microseconds.
78
0
  int32_t DecodeTimeMicros() { return mDecodeTime.ToMicroseconds(); }
79
80
  /// The per-image-format telemetry ID for recording our decoder's speed, or
81
  /// Nothing() if we don't record speed telemetry for this kind of decoder.
82
  const Maybe<Telemetry::HistogramID> mSpeedHistogram;
83
84
  /// The number of bytes of input our decoder processed.
85
  const size_t mBytesDecoded;
86
87
  /// The number of chunks our decoder's input was divided into.
88
  const uint32_t mChunkCount;
89
90
  /// The amount of time our decoder spent inside DoDecode().
91
  const TimeDuration mDecodeTime;
92
};
93
94
class Decoder
95
{
96
public:
97
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder)
98
99
  explicit Decoder(RasterImage* aImage);
100
101
  /**
102
   * Initialize an image decoder. Decoders may not be re-initialized.
103
   *
104
   * @return NS_OK if the decoder could be initialized successfully.
105
   */
106
  nsresult Init();
107
108
  /**
109
   * Decodes, reading all data currently available in the SourceBuffer.
110
   *
111
   * If more data is needed and @aOnResume is non-null, Decode() will schedule
112
   * @aOnResume to be called when more data is available.
113
   *
114
   * @return a LexerResult which may indicate:
115
   *   - the image has been successfully decoded (TerminalState::SUCCESS), or
116
   *   - the image has failed to decode (TerminalState::FAILURE), or
117
   *   - the decoder is yielding until it gets more data (Yield::NEED_MORE_DATA), or
118
   *   - the decoder is yielding to allow the caller to access intermediate
119
   *     output (Yield::OUTPUT_AVAILABLE).
120
   */
121
  LexerResult Decode(IResumable* aOnResume = nullptr);
122
123
  /**
124
   * Terminate this decoder in a failure state, just as if the decoder
125
   * implementation had returned TerminalState::FAILURE from DoDecode().
126
   *
127
   * XXX(seth): This method should be removed ASAP; it exists only because
128
   * RasterImage::FinalizeDecoder() requires an actual Decoder object as an
129
   * argument, so we can't simply tell RasterImage a decode failed except via an
130
   * intervening decoder. We'll fix this in bug 1291071.
131
   */
132
  LexerResult TerminateFailure();
133
134
  /**
135
   * Given a maximum number of bytes we're willing to decode, @aByteLimit,
136
   * returns true if we should attempt to run this decoder synchronously.
137
   */
138
  bool ShouldSyncDecode(size_t aByteLimit);
139
140
  /**
141
   * Gets the invalidation region accumulated by the decoder so far, and clears
142
   * the decoder's invalidation region. This means that each call to
143
   * TakeInvalidRect() returns only the invalidation region accumulated since
144
   * the last call to TakeInvalidRect().
145
   */
146
  nsIntRect TakeInvalidRect()
147
0
  {
148
0
    nsIntRect invalidRect = mInvalidRect;
149
0
    mInvalidRect.SetEmpty();
150
0
    return invalidRect;
151
0
  }
152
153
  /**
154
   * Gets the progress changes accumulated by the decoder so far, and clears
155
   * them. This means that each call to TakeProgress() returns only the changes
156
   * accumulated since the last call to TakeProgress().
157
   */
158
  Progress TakeProgress()
159
0
  {
160
0
    Progress progress = mProgress;
161
0
    mProgress = NoProgress;
162
0
    return progress;
163
0
  }
164
165
  /**
166
   * Returns true if there's any progress to report.
167
   */
168
  bool HasProgress() const
169
0
  {
170
0
    return mProgress != NoProgress || !mInvalidRect.IsEmpty() || mFinishedNewFrame;
171
0
  }
172
173
  /*
174
   * State.
175
   */
176
177
  /**
178
   * If we're doing a metadata decode, we only decode the image's headers, which
179
   * is enough to determine the image's intrinsic size. A metadata decode is
180
   * enabled by calling SetMetadataDecode() *before* calling Init().
181
   */
182
  void SetMetadataDecode(bool aMetadataDecode)
183
0
  {
184
0
    MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
185
0
    mMetadataDecode = aMetadataDecode;
186
0
  }
187
0
  bool IsMetadataDecode() const { return mMetadataDecode; }
188
189
  /**
190
   * Sets the output size of this decoder. If this is smaller than the intrinsic
191
   * size of the image, we'll downscale it while decoding. For memory usage
192
   * reasons, upscaling is forbidden and will trigger assertions in debug
193
   * builds.
194
   *
195
   * Not calling SetOutputSize() means that we should just decode at the
196
   * intrinsic size, whatever it is.
197
   *
198
   * If SetOutputSize() was called, ExplicitOutputSize() can be used to
199
   * determine the value that was passed to it.
200
   *
201
   * This must be called before Init() is called.
202
   */
203
  void SetOutputSize(const gfx::IntSize& aSize);
204
205
  /**
206
   * @return the output size of this decoder. If this is smaller than the
207
   * intrinsic size, then the image will be downscaled during the decoding
208
   * process.
209
   *
210
   * Illegal to call if HasSize() returns false.
211
   */
212
0
  gfx::IntSize OutputSize() const { MOZ_ASSERT(HasSize()); return *mOutputSize; }
213
214
  /**
215
   * @return either the size passed to SetOutputSize() or Nothing(), indicating
216
   * that SetOutputSize() was not explicitly called.
217
   */
218
  Maybe<gfx::IntSize> ExplicitOutputSize() const;
219
220
  /**
221
   * Sets the expected image size of this decoder. Decoding will fail if this
222
   * does not match.
223
   */
224
  void SetExpectedSize(const gfx::IntSize& aSize)
225
0
  {
226
0
    mExpectedSize.emplace(aSize);
227
0
  }
228
229
  /**
230
   * Is the image size what was expected, if specified?
231
   */
232
  bool IsExpectedSize() const
233
0
  {
234
0
    return mExpectedSize.isNothing() || *mExpectedSize == Size();
235
0
  }
236
237
  /**
238
   * Set an iterator to the SourceBuffer which will feed data to this decoder.
239
   * This must always be called before calling Init(). (And only before Init().)
240
   *
241
   * XXX(seth): We should eliminate this method and pass a SourceBufferIterator
242
   * to the various decoder constructors instead.
243
   */
244
  void SetIterator(SourceBufferIterator&& aIterator)
245
0
  {
246
0
    MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
247
0
    mIterator.emplace(std::move(aIterator));
248
0
  }
249
250
  SourceBuffer* GetSourceBuffer() const
251
0
  {
252
0
    return mIterator->Owner();
253
0
  }
254
255
  /**
256
   * Should this decoder send partial invalidations?
257
   */
258
  bool ShouldSendPartialInvalidations() const
259
0
  {
260
0
    return !(mDecoderFlags & DecoderFlags::IS_REDECODE);
261
0
  }
262
263
  /**
264
   * Should we stop decoding after the first frame?
265
   */
266
  bool IsFirstFrameDecode() const
267
0
  {
268
0
    return bool(mDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY);
269
0
  }
270
271
  /**
272
   * Should blend the current frame with the previous frames to produce a
273
   * complete frame instead of a partial frame for animated images.
274
   */
275
  bool ShouldBlendAnimation() const
276
0
  {
277
0
    return bool(mDecoderFlags & DecoderFlags::BLEND_ANIMATION);
278
0
  }
279
280
  /**
281
   * @return the number of complete animation frames which have been decoded so
282
   * far, if it has changed since the last call to TakeCompleteFrameCount();
283
   * otherwise, returns Nothing().
284
   */
285
  Maybe<uint32_t> TakeCompleteFrameCount();
286
287
  // The number of frames we have, including anything in-progress. Thus, this
288
  // is only 0 if we haven't begun any frames.
289
0
  uint32_t GetFrameCount() { return mFrameCount; }
290
291
  // Did we discover that the image we're decoding is animated?
292
0
  bool HasAnimation() const { return mImageMetadata.HasAnimation(); }
293
294
  // Error tracking
295
0
  bool HasError() const { return mError; }
296
0
  bool ShouldReportError() const { return mShouldReportError; }
297
298
  // Finalize frames
299
0
  void SetFinalizeFrames(bool aFinalize) { mFinalizeFrames = aFinalize; }
300
0
  bool GetFinalizeFrames() const { return mFinalizeFrames; }
301
302
  /// Did we finish decoding enough that calling Decode() again would be useless?
303
  bool GetDecodeDone() const
304
0
  {
305
0
    return mReachedTerminalState || mDecodeDone ||
306
0
           (mMetadataDecode && HasSize()) || HasError();
307
0
  }
308
309
  /// Are we in the middle of a frame right now? Used for assertions only.
310
0
  bool InFrame() const { return mInFrame; }
311
312
  /// Is the image valid if embedded inside an ICO.
313
  virtual bool IsValidICOResource() const
314
0
  {
315
0
    return false;
316
0
  }
317
318
  /// Type of decoder.
319
  virtual DecoderType GetType() const
320
0
  {
321
0
    return DecoderType::UNKNOWN;
322
0
  }
323
324
  enum DecodeStyle {
325
      PROGRESSIVE, // produce intermediate frames representing the partial
326
                   // state of the image
327
      SEQUENTIAL   // decode to final image immediately
328
  };
329
330
  /**
331
   * Get or set the DecoderFlags that influence the behavior of this decoder.
332
   */
333
  void SetDecoderFlags(DecoderFlags aDecoderFlags)
334
0
  {
335
0
    MOZ_ASSERT(!mInitialized);
336
0
    mDecoderFlags = aDecoderFlags;
337
0
  }
338
0
  DecoderFlags GetDecoderFlags() const { return mDecoderFlags; }
339
340
  /**
341
   * Get or set the SurfaceFlags that select the kind of output this decoder
342
   * will produce.
343
   */
344
  void SetSurfaceFlags(SurfaceFlags aSurfaceFlags)
345
0
  {
346
0
    MOZ_ASSERT(!mInitialized);
347
0
    mSurfaceFlags = aSurfaceFlags;
348
0
  }
349
0
  SurfaceFlags GetSurfaceFlags() const { return mSurfaceFlags; }
350
351
  /// @return true if we know the intrinsic size of the image we're decoding.
352
0
  bool HasSize() const { return mImageMetadata.HasSize(); }
353
354
  /**
355
   * @return the intrinsic size of the image we're decoding.
356
   *
357
   * Illegal to call if HasSize() returns false.
358
   */
359
  gfx::IntSize Size() const
360
0
  {
361
0
    MOZ_ASSERT(HasSize());
362
0
    return mImageMetadata.GetSize();
363
0
  }
364
365
  /**
366
   * @return an IntRect which covers the entire area of this image at its
367
   * intrinsic size, appropriate for use as a frame rect when the image itself
368
   * does not specify one.
369
   *
370
   * Illegal to call if HasSize() returns false.
371
   */
372
  gfx::IntRect FullFrame() const
373
0
  {
374
0
    return gfx::IntRect(gfx::IntPoint(), Size());
375
0
  }
376
377
  /**
378
   * @return an IntRect which covers the entire area of this image at its size
379
   * after scaling - that is, at its output size.
380
   *
381
   * XXX(seth): This is only used for decoders which are using the old
382
   * Downscaler code instead of SurfacePipe, since the old AllocateFrame() and
383
   * Downscaler APIs required that the frame rect be specified in output space.
384
   * We should remove this once all decoders use SurfacePipe.
385
   *
386
   * Illegal to call if HasSize() returns false.
387
   */
388
  gfx::IntRect FullOutputFrame() const
389
0
  {
390
0
    return gfx::IntRect(gfx::IntPoint(), OutputSize());
391
0
  }
392
393
  /// @return final status information about this decoder. Should be called
394
  /// after we decide we're not going to run the decoder anymore.
395
  DecoderFinalStatus FinalStatus() const;
396
397
  /// @return the metadata we collected about this image while decoding.
398
0
  const ImageMetadata& GetImageMetadata() { return mImageMetadata; }
399
400
  /// @return performance telemetry we collected while decoding.
401
  DecoderTelemetry Telemetry() const;
402
403
  /**
404
   * @return a weak pointer to the image associated with this decoder. Illegal
405
   * to call if this decoder is not associated with an image.
406
   */
407
0
  NotNull<RasterImage*> GetImage() const { return WrapNotNull(mImage.get()); }
408
409
  /**
410
   * @return a possibly-null weak pointer to the image associated with this
411
   * decoder. May be called even if this decoder is not associated with an
412
   * image.
413
   */
414
0
  RasterImage* GetImageMaybeNull() const { return mImage.get(); }
415
416
  RawAccessFrameRef GetCurrentFrameRef()
417
0
  {
418
0
    return mCurrentFrame ? mCurrentFrame->RawAccessRef()
419
0
                         : RawAccessFrameRef();
420
0
  }
421
422
  /**
423
   * For use during decoding only. Allows the BlendAnimationFilter to get the
424
   * current frame we are producing for its animation parameters.
425
   */
426
  imgFrame* GetCurrentFrame()
427
0
  {
428
0
    MOZ_ASSERT(ShouldBlendAnimation());
429
0
    return mCurrentFrame.get();
430
0
  }
431
432
  /**
433
   * For use during decoding only. Allows the BlendAnimationFilter to get the
434
   * frame it should be pulling the previous frame data from.
435
   */
436
  const RawAccessFrameRef& GetRestoreFrameRef() const
437
0
  {
438
0
    MOZ_ASSERT(ShouldBlendAnimation());
439
0
    return mRestoreFrame;
440
0
  }
441
442
  const gfx::IntRect& GetRestoreDirtyRect() const
443
0
  {
444
0
    MOZ_ASSERT(ShouldBlendAnimation());
445
0
    return mRestoreDirtyRect;
446
0
  }
447
448
0
  bool HasFrameToTake() const { return mHasFrameToTake; }
449
0
  void ClearHasFrameToTake() {
450
0
    MOZ_ASSERT(mHasFrameToTake);
451
0
    mHasFrameToTake = false;
452
0
  }
453
454
protected:
455
  friend class AutoRecordDecoderTelemetry;
456
  friend class DecoderTestHelper;
457
  friend class nsICODecoder;
458
  friend class PalettedSurfaceSink;
459
  friend class SurfaceSink;
460
461
  virtual ~Decoder();
462
463
  /*
464
   * Internal hooks. Decoder implementations may override these and
465
   * only these methods.
466
   *
467
   * BeforeFinishInternal() can be used to detect if decoding is in an
468
   * incomplete state, e.g. due to file truncation, in which case it should
469
   * return a failing nsresult.
470
   */
471
  virtual nsresult InitInternal();
472
  virtual LexerResult DoDecode(SourceBufferIterator& aIterator,
473
                               IResumable* aOnResume) = 0;
474
  virtual nsresult BeforeFinishInternal();
475
  virtual nsresult FinishInternal();
476
  virtual nsresult FinishWithErrorInternal();
477
478
  /**
479
   * @return the per-image-format telemetry ID for recording this decoder's
480
   * speed, or Nothing() if we don't record speed telemetry for this kind of
481
   * decoder.
482
   */
483
0
  virtual Maybe<Telemetry::HistogramID> SpeedHistogram() const { return Nothing(); }
484
485
486
  /*
487
   * Progress notifications.
488
   */
489
490
  // Called by decoders when they determine the size of the image. Informs
491
  // the image of its size and sends notifications.
492
  void PostSize(int32_t aWidth,
493
                int32_t aHeight,
494
                Orientation aOrientation = Orientation());
495
496
  // Called by decoders if they determine that the image has transparency.
497
  //
498
  // This should be fired as early as possible to allow observers to do things
499
  // that affect content, so it's necessarily pessimistic - if there's a
500
  // possibility that the image has transparency, for example because its header
501
  // specifies that it has an alpha channel, we fire PostHasTransparency
502
  // immediately. PostFrameStop's aFrameOpacity argument, on the other hand, is
503
  // only used internally to ImageLib. Because PostFrameStop isn't delivered
504
  // until the entire frame has been decoded, decoders may take into account the
505
  // actual contents of the frame and give a more accurate result.
506
  void PostHasTransparency();
507
508
  // Called by decoders if they determine that the image is animated.
509
  //
510
  // @param aTimeout The time for which the first frame should be shown before
511
  //                 we advance to the next frame.
512
  void PostIsAnimated(FrameTimeout aFirstFrameTimeout);
513
514
  // Called by decoders when they end a frame. Informs the image, sends
515
  // notifications, and does internal book-keeping.
516
  // Specify whether this frame is opaque as an optimization.
517
  // For animated images, specify the disposal, blend method and timeout for
518
  // this frame.
519
  void PostFrameStop(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY);
520
521
  /**
522
   * Called by the decoders when they have a region to invalidate. We may not
523
   * actually pass these invalidations on right away.
524
   *
525
   * @param aRect The invalidation rect in the coordinate system of the unscaled
526
   *              image (that is, the image at its intrinsic size).
527
   * @param aRectAtOutputSize If not Nothing(), the invalidation rect in the
528
   *                          coordinate system of the scaled image (that is,
529
   *                          the image at our output size). This must
530
   *                          be supplied if we're downscaling during decode.
531
   */
532
  void PostInvalidation(const gfx::IntRect& aRect,
533
                        const Maybe<gfx::IntRect>& aRectAtOutputSize = Nothing());
534
535
  // Called by the decoders when they have successfully decoded the image. This
536
  // may occur as the result of the decoder getting to the appropriate point in
537
  // the stream, or by us calling FinishInternal().
538
  //
539
  // May not be called mid-frame.
540
  //
541
  // For animated images, specify the loop count. -1 means loop forever, 0
542
  // means a single iteration, stopping on the last frame.
543
  void PostDecodeDone(int32_t aLoopCount = 0);
544
545
  /**
546
   * Allocates a new frame, making it our current frame if successful.
547
   *
548
   * If a non-paletted frame is desired, pass 0 for aPaletteDepth.
549
   */
550
  nsresult AllocateFrame(const gfx::IntSize& aOutputSize,
551
                         const gfx::IntRect& aFrameRect,
552
                         gfx::SurfaceFormat aFormat,
553
                         uint8_t aPaletteDepth = 0,
554
                         const Maybe<AnimationParams>& aAnimParams = Nothing());
555
556
private:
557
  /// Report that an error was encountered while decoding.
558
  void PostError();
559
560
  /**
561
   * CompleteDecode() finishes up the decoding process after Decode() determines
562
   * that we're finished. It records final progress and does all the cleanup
563
   * that's possible off-main-thread.
564
   */
565
  void CompleteDecode();
566
567
  /// @return the number of complete frames we have. Does not include the
568
  /// current frame if it's unfinished.
569
  uint32_t GetCompleteFrameCount()
570
0
  {
571
0
    if (mFrameCount == 0) {
572
0
      return 0;
573
0
    }
574
0
575
0
    return mInFrame ? mFrameCount - 1 : mFrameCount;
576
0
  }
577
578
  RawAccessFrameRef AllocateFrameInternal(const gfx::IntSize& aOutputSize,
579
                                          const gfx::IntRect& aFrameRect,
580
                                          gfx::SurfaceFormat aFormat,
581
                                          uint8_t aPaletteDepth,
582
                                          const Maybe<AnimationParams>& aAnimParams,
583
                                          RawAccessFrameRef&& aPreviousFrame);
584
585
protected:
586
  Maybe<Downscaler> mDownscaler;
587
588
  uint8_t* mImageData;  // Pointer to image data in either Cairo or 8bit format
589
  uint32_t mImageDataLength;
590
  uint32_t* mColormap;  // Current colormap to be used in Cairo format
591
  uint32_t mColormapSize;
592
593
private:
594
  RefPtr<RasterImage> mImage;
595
  Maybe<SourceBufferIterator> mIterator;
596
597
  // The current frame the decoder is producing.
598
  RawAccessFrameRef mCurrentFrame;
599
600
  // The complete frame to combine with the current partial frame to produce
601
  // a complete current frame.
602
  RawAccessFrameRef mRestoreFrame;
603
604
  ImageMetadata mImageMetadata;
605
606
  gfx::IntRect mInvalidRect; // Tracks new rows as the current frame is decoded.
607
  gfx::IntRect mRestoreDirtyRect; // Tracks an invalidation region between the
608
                                  // restore frame and the previous frame.
609
  Maybe<gfx::IntSize> mOutputSize;  // The size of our output surface.
610
  Maybe<gfx::IntSize> mExpectedSize; // The expected size of the image.
611
  Progress mProgress;
612
613
  uint32_t mFrameCount; // Number of frames, including anything in-progress
614
  FrameTimeout mLoopLength;  // Length of a single loop of this image.
615
  gfx::IntRect mFirstFrameRefreshArea;  // The area of the image that needs to
616
                                        // be invalidated when the animation loops.
617
618
  // Telemetry data for this decoder.
619
  TimeDuration mDecodeTime;
620
621
  DecoderFlags mDecoderFlags;
622
  SurfaceFlags mSurfaceFlags;
623
624
  bool mInitialized : 1;
625
  bool mMetadataDecode : 1;
626
  bool mHaveExplicitOutputSize : 1;
627
  bool mInFrame : 1;
628
  bool mFinishedNewFrame : 1;  // True if PostFrameStop() has been called since
629
                               // the last call to TakeCompleteFrameCount().
630
  // Has a new frame that AnimationSurfaceProvider can take. Unfortunately this
631
  // has to be separate from mFinishedNewFrame because the png decoder yields a
632
  // new frame before calling PostFrameStop().
633
  bool mHasFrameToTake : 1;
634
  bool mReachedTerminalState : 1;
635
  bool mDecodeDone : 1;
636
  bool mError : 1;
637
  bool mShouldReportError : 1;
638
  bool mFinalizeFrames : 1;
639
};
640
641
} // namespace image
642
} // namespace mozilla
643
644
#endif // mozilla_image_Decoder_h