Bug 1184996 (Part 1) - Create decoders with a DecoderFactory. r=tn
authorSeth Fowler <mark.seth.fowler@gmail.com>
Wed, 22 Jul 2015 22:39:48 -0700
changeset 254222 2ac5cb9669db666fb358be9ad5613ef71c7cac30
parent 254221 69bee90b0abc2b025b3f82aeedd0e7a4d0a256bf
child 254223 97a30fa80826f06992cd085b12a2f6eee83c1d9e
push id62693
push usermfowler@mozilla.com
push dateThu, 23 Jul 2015 05:40:31 +0000
treeherdermozilla-inbound@aaf3459a20b5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn
bugs1184996
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1184996 (Part 1) - Create decoders with a DecoderFactory. r=tn
image/DecoderFactory.cpp
image/DecoderFactory.h
image/Image.cpp
image/Image.h
image/RasterImage.cpp
image/RasterImage.h
image/SVGDocumentWrapper.cpp
image/imgLoader.cpp
image/moz.build
new file mode 100644
--- /dev/null
+++ b/image/DecoderFactory.cpp
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DecoderFactory.h"
+
+#include "nsMimeTypes.h"
+#include "nsRefPtr.h"
+#include "nsString.h"
+
+#include "Decoder.h"
+#include "nsPNGDecoder.h"
+#include "nsGIFDecoder2.h"
+#include "nsJPEGDecoder.h"
+#include "nsBMPDecoder.h"
+#include "nsICODecoder.h"
+#include "nsIconDecoder.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace image {
+
+/* static */ DecoderType
+DecoderFactory::GetDecoderType(const char* aMimeType)
+{
+  // By default we don't know.
+  DecoderType type = DecoderType::UNKNOWN;
+
+  // PNG
+  if (!strcmp(aMimeType, IMAGE_PNG)) {
+    type = DecoderType::PNG;
+  } else if (!strcmp(aMimeType, IMAGE_X_PNG)) {
+    type = DecoderType::PNG;
+
+  // GIF
+  } else if (!strcmp(aMimeType, IMAGE_GIF)) {
+    type = DecoderType::GIF;
+
+  // JPEG
+  } else if (!strcmp(aMimeType, IMAGE_JPEG)) {
+    type = DecoderType::JPEG;
+  } else if (!strcmp(aMimeType, IMAGE_PJPEG)) {
+    type = DecoderType::JPEG;
+  } else if (!strcmp(aMimeType, IMAGE_JPG)) {
+    type = DecoderType::JPEG;
+
+  // BMP
+  } else if (!strcmp(aMimeType, IMAGE_BMP)) {
+    type = DecoderType::BMP;
+  } else if (!strcmp(aMimeType, IMAGE_BMP_MS)) {
+    type = DecoderType::BMP;
+
+  // ICO
+  } else if (!strcmp(aMimeType, IMAGE_ICO)) {
+    type = DecoderType::ICO;
+  } else if (!strcmp(aMimeType, IMAGE_ICO_MS)) {
+    type = DecoderType::ICO;
+
+  // Icon
+  } else if (!strcmp(aMimeType, IMAGE_ICON_MS)) {
+    type = DecoderType::ICON;
+  }
+
+  return type;
+}
+
+static already_AddRefed<Decoder>
+GetDecoder(DecoderType aType,
+           RasterImage* aImage,
+           bool aIsRedecode)
+{
+  nsRefPtr<Decoder> decoder;
+
+  switch (aType) {
+    case DecoderType::PNG:
+      decoder = new nsPNGDecoder(aImage);
+      break;
+    case DecoderType::GIF:
+      decoder = new nsGIFDecoder2(aImage);
+      break;
+    case DecoderType::JPEG:
+      // If we have all the data we don't want to waste cpu time doing
+      // a progressive decode.
+      decoder = new nsJPEGDecoder(aImage,
+                                  aIsRedecode ? Decoder::SEQUENTIAL
+                                              : Decoder::PROGRESSIVE);
+      break;
+    case DecoderType::BMP:
+      decoder = new nsBMPDecoder(aImage);
+      break;
+    case DecoderType::ICO:
+      decoder = new nsICODecoder(aImage);
+      break;
+    case DecoderType::ICON:
+      decoder = new nsIconDecoder(aImage);
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unknown decoder type");
+  }
+
+  return decoder.forget();
+}
+
+/* static */ already_AddRefed<Decoder>
+DecoderFactory::CreateDecoder(DecoderType aType,
+                              RasterImage* aImage,
+                              SourceBuffer* aSourceBuffer,
+                              const Maybe<IntSize>& aTargetSize,
+                              uint32_t aFlags,
+                              bool aIsRedecode,
+                              bool aImageIsTransient,
+                              bool aImageIsLocked)
+{
+  if (aType == DecoderType::UNKNOWN) {
+    return nullptr;
+  }
+
+  nsRefPtr<Decoder> decoder = GetDecoder(aType, aImage, aIsRedecode);
+  MOZ_ASSERT(decoder, "Should have a decoder now");
+
+  // Initialize the decoder.
+  decoder->SetSizeDecode(false);
+  decoder->SetIterator(aSourceBuffer->Iterator());
+  decoder->SetFlags(aFlags);
+  decoder->SetSendPartialInvalidations(!aIsRedecode);
+  decoder->SetImageIsTransient(aImageIsTransient);
+
+  if (aImageIsLocked) {
+    decoder->SetImageIsLocked();
+  }
+
+  // Set a target size for downscale-during-decode if applicable.
+  if (aTargetSize) {
+    DebugOnly<nsresult> rv = decoder->SetTargetSize(*aTargetSize);
+    MOZ_ASSERT(nsresult(rv) != NS_ERROR_NOT_AVAILABLE,
+               "We're downscale-during-decode but decoder doesn't support it?");
+    MOZ_ASSERT(NS_SUCCEEDED(rv), "Bad downscale-during-decode target size?");
+  }
+
+  decoder->Init();
+  if (NS_FAILED(decoder->GetDecoderError())) {
+    return nullptr;
+  }
+
+  return decoder.forget();
+}
+
+/* static */ already_AddRefed<Decoder>
+DecoderFactory::CreateMetadataDecoder(DecoderType aType,
+                                      RasterImage* aImage,
+                                      SourceBuffer* aSourceBuffer)
+{
+  if (aType == DecoderType::UNKNOWN) {
+    return nullptr;
+  }
+
+  nsRefPtr<Decoder> decoder =
+    GetDecoder(aType, aImage, /* aIsRedecode = */ false);
+  MOZ_ASSERT(decoder, "Should have a decoder now");
+
+  // Initialize the decoder.
+  decoder->SetSizeDecode(true);
+  decoder->SetIterator(aSourceBuffer->Iterator());
+
+  decoder->Init();
+  if (NS_FAILED(decoder->GetDecoderError())) {
+    return nullptr;
+  }
+
+  return decoder.forget();
+}
+
+} // namespace image
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/image/DecoderFactory.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_image_DecoderFactory_h
+#define mozilla_image_DecoderFactory_h
+
+#include "mozilla/Maybe.h"
+#include "mozilla/gfx/2D.h"
+#include "nsCOMPtr.h"
+
+class nsACString;
+
+namespace mozilla {
+namespace image {
+
+class Decoder;
+class RasterImage;
+class SourceBuffer;
+
+enum class DecoderType
+{
+  PNG,
+  GIF,
+  JPEG,
+  BMP,
+  ICO,
+  ICON,
+  UNKNOWN
+};
+
+class DecoderFactory
+{
+public:
+  /// @return the type of decoder which is appropriate for @aMimeType.
+  static DecoderType GetDecoderType(const char* aMimeType);
+
+  /**
+   * Creates and initializes a decoder of type @aType. The decoder will send
+   * notifications to @aImage.
+   *
+   * XXX(seth): @aIsRedecode, @aImageIsTransient, and @aImageIsLocked should
+   * really be part of @aFlags. This requires changes to the way that decoder
+   * flags work, though. See bug 1185800.
+   *
+   * @param aType Which type of decoder to create - JPEG, PNG, etc.
+   * @param aImage The image will own the decoder and which should receive
+   *               notifications as decoding progresses.
+   * @param aSourceBuffer The SourceBuffer which the decoder will read its data
+   *                      from.
+   * @param aTargetSize If not Nothing(), the target size which the image should
+   *                    be scaled to during decoding. It's an error to specify
+   *                    a target size for a decoder type which doesn't support
+   *                    downscale-during-decode.
+   * @param aFlags Flags specifying what type of output the decoder should
+   *               produce; see GetDecodeFlags() in RasterImage.h.
+   * @param aIsRedecode Specify 'true' if this image has been decoded before.
+   * @param aImageIsTransient Specify 'true' if this image is transient.
+   * @param aImageIsLocked Specify 'true' if this image is locked for the
+   *                       lifetime of this decoder, and should be unlocked
+   *                       when the decoder finishes.
+   */
+  static already_AddRefed<Decoder>
+  CreateDecoder(DecoderType aType,
+                RasterImage* aImage,
+                SourceBuffer* aSourceBuffer,
+                const Maybe<gfx::IntSize>& aTargetSize,
+                uint32_t aFlags,
+                bool aIsRedecode,
+                bool aImageIsTransient,
+                bool aImageIsLocked);
+
+  /**
+   * Creates and initializes a metadata decoder of type @aType. This decoder
+   * will only decode the image's header, extracting metadata like the size of
+   * the image. No actual image data will be decoded and no surfaces will be
+   * allocated. The decoder will send notifications to @aImage.
+   *
+   * XXX(seth): A metadata decode is called a "size decode" in most of ImageLib,
+   * but we are going to move away from that term, as it's both confusing to
+   * people and increasingly inaccurate.
+   *
+   * @param aType Which type of decoder to create - JPEG, PNG, etc.
+   * @param aImage The image will own the decoder and which should receive
+   *               notifications as decoding progresses.
+   * @param aSourceBuffer The SourceBuffer which the decoder will read its data
+   *                      from.
+   */
+  static already_AddRefed<Decoder>
+  CreateMetadataDecoder(DecoderType aType,
+                        RasterImage* aImage,
+                        SourceBuffer* aSourceBuffer);
+
+private:
+  virtual ~DecoderFactory() = 0;
+};
+
+} // namespace image
+} // namespace mozilla
+
+#endif // mozilla_image_DecoderFactory_h
--- a/image/Image.cpp
+++ b/image/Image.cpp
@@ -1,15 +1,13 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsMimeTypes.h"
-
 #include "Image.h"
 #include "nsRefreshDriver.h"
 #include "mozilla/TimeStamp.h"
 
 namespace mozilla {
 namespace image {
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -64,62 +62,16 @@ ImageResource::ImageResource(ImageURL* a
 { }
 
 ImageResource::~ImageResource()
 {
   // Ask our ProgressTracker to drop its weak reference to us.
   mProgressTracker->ResetImage();
 }
 
-// Translates a mimetype into a concrete decoder
-Image::eDecoderType
-Image::GetDecoderType(const char* aMimeType)
-{
-  // By default we don't know
-  eDecoderType rv = eDecoderType_unknown;
-
-  // PNG
-  if (!strcmp(aMimeType, IMAGE_PNG)) {
-    rv = eDecoderType_png;
-
-  } else if (!strcmp(aMimeType, IMAGE_X_PNG)) {
-    rv = eDecoderType_png;
-
-  // GIF
-  } else if (!strcmp(aMimeType, IMAGE_GIF)) {
-    rv = eDecoderType_gif;
-
-  // JPEG
-  } else if (!strcmp(aMimeType, IMAGE_JPEG)) {
-    rv = eDecoderType_jpeg;
-  } else if (!strcmp(aMimeType, IMAGE_PJPEG)) {
-    rv = eDecoderType_jpeg;
-  } else if (!strcmp(aMimeType, IMAGE_JPG)) {
-    rv = eDecoderType_jpeg;
-
-  // BMP
-  } else if (!strcmp(aMimeType, IMAGE_BMP)) {
-    rv = eDecoderType_bmp;
-  } else if (!strcmp(aMimeType, IMAGE_BMP_MS)) {
-    rv = eDecoderType_bmp;
-
-  // ICO
-  } else if (!strcmp(aMimeType, IMAGE_ICO)) {
-    rv = eDecoderType_ico;
-  } else if (!strcmp(aMimeType, IMAGE_ICO_MS)) {
-    rv = eDecoderType_ico;
-
-  // Icon
-  } else if (!strcmp(aMimeType, IMAGE_ICON_MS)) {
-    rv = eDecoderType_icon;
-  }
-
-  return rv;
-}
-
 void
 ImageResource::IncrementAnimationConsumers()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Main thread only to encourage serialization "
                                 "with DecrementAnimationConsumers");
   mAnimationConsumers++;
 }
 
--- a/image/Image.h
+++ b/image/Image.h
@@ -124,28 +124,16 @@ private:
 
 ///////////////////////////////////////////////////////////////////////////////
 // Image Base Types
 ///////////////////////////////////////////////////////////////////////////////
 
 class Image : public imgIContainer
 {
 public:
-  // Mimetype translation
-  enum eDecoderType {
-    eDecoderType_png     = 0,
-    eDecoderType_gif     = 1,
-    eDecoderType_jpeg    = 2,
-    eDecoderType_bmp     = 3,
-    eDecoderType_ico     = 4,
-    eDecoderType_icon    = 5,
-    eDecoderType_unknown = 6
-  };
-  static eDecoderType GetDecoderType(const char* aMimeType);
-
   /**
    * Flags for Image initialization.
    *
    * Meanings:
    *
    * INIT_FLAG_NONE: Lack of flags
    *
    * INIT_FLAG_DISCARDABLE: The container should be discardable
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -16,28 +16,22 @@
 #include "Decoder.h"
 #include "nsAutoPtr.h"
 #include "prenv.h"
 #include "prsystem.h"
 #include "ImageContainer.h"
 #include "ImageRegion.h"
 #include "Layers.h"
 #include "LookupResult.h"
+#include "nsIInputStream.h"
 #include "nsPresContext.h"
 #include "SourceBuffer.h"
 #include "SurfaceCache.h"
 #include "FrameAnimator.h"
 
-#include "nsPNGDecoder.h"
-#include "nsGIFDecoder2.h"
-#include "nsJPEGDecoder.h"
-#include "nsBMPDecoder.h"
-#include "nsICODecoder.h"
-#include "nsIconDecoder.h"
-
 #include "gfxContext.h"
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Likely.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Move.h"
 #include "mozilla/MemoryReporting.h"
@@ -277,18 +271,18 @@ RasterImage::Init(const char* aMimeType,
 #ifndef MOZ_ENABLE_SKIA
   // Downscale-during-decode requires Skia.
   mDownscaleDuringDecode = false;
 #endif
 
   // Use the MIME type to select a decoder type, and make sure there *is* a
   // decoder for this MIME type.
   NS_ENSURE_ARG_POINTER(aMimeType);
-  mDecoderType = GetDecoderType(aMimeType);
-  if (mDecoderType == eDecoderType_unknown) {
+  mDecoderType = DecoderFactory::GetDecoderType(aMimeType);
+  if (mDecoderType == DecoderType::UNKNOWN) {
     return NS_ERROR_FAILURE;
   }
 
   // Lock this image's surfaces in the SurfaceCache if we're not discardable.
   if (!mDiscardable) {
     mLockCount++;
     SurfaceCache::LockImage(ImageKey(this));
   }
@@ -1334,74 +1328,38 @@ RasterImage::CreateDecoder(const Maybe<I
     MOZ_ASSERT(mHasSize, "Must do a size decode before a full decode!");
     MOZ_ASSERT(mDownscaleDuringDecode || *aSize == mSize,
                "Can only decode to our intrinsic size if we're not allowed to "
                "downscale-during-decode");
   } else {
     MOZ_ASSERT(!mHasSize, "Should not do unnecessary size decodes");
   }
 
-  // Instantiate the appropriate decoder.
-  nsRefPtr<Decoder> decoder;
-  switch (mDecoderType) {
-    case eDecoderType_png:
-      decoder = new nsPNGDecoder(this);
-      break;
-    case eDecoderType_gif:
-      decoder = new nsGIFDecoder2(this);
-      break;
-    case eDecoderType_jpeg:
-      // If we have all the data we don't want to waste cpu time doing
-      // a progressive decode.
-      decoder = new nsJPEGDecoder(this,
-                                  mHasBeenDecoded ? Decoder::SEQUENTIAL :
-                                                    Decoder::PROGRESSIVE);
-      break;
-    case eDecoderType_bmp:
-      decoder = new nsBMPDecoder(this);
-      break;
-    case eDecoderType_ico:
-      decoder = new nsICODecoder(this);
-      break;
-    case eDecoderType_icon:
-      decoder = new nsIconDecoder(this);
-      break;
-    default:
-      MOZ_ASSERT_UNREACHABLE("Unknown decoder type");
-  }
-
-  MOZ_ASSERT(decoder, "Should have a decoder now");
-
-  // Initialize the decoder.
-  decoder->SetSizeDecode(!aSize);
-  decoder->SetSendPartialInvalidations(!mHasBeenDecoded);
-  decoder->SetImageIsTransient(mTransient);
-  decoder->SetFlags(aFlags);
-
+  bool imageIsLocked = false;
   if (!mHasBeenDecoded && aSize) {
     // Lock the image while we're decoding, so that it doesn't get evicted from
     // the SurfaceCache before we have a chance to realize that it's animated.
     // The corresponding unlock happens in FinalizeDecoder.
     LockImage();
-    decoder->SetImageIsLocked();
+    imageIsLocked = true;
   }
 
-  decoder->SetIterator(mSourceBuffer->Iterator());
-
-  // Set a target size for downscale-during-decode if applicable.
-  if (mDownscaleDuringDecode && aSize && *aSize != mSize) {
-    DebugOnly<nsresult> rv = decoder->SetTargetSize(*aSize);
-    MOZ_ASSERT(nsresult(rv) != NS_ERROR_NOT_AVAILABLE,
-               "We're downscale-during-decode but decoder doesn't support it?");
-    MOZ_ASSERT(NS_SUCCEEDED(rv), "Bad downscale-during-decode target size?");
+  nsRefPtr<Decoder> decoder;
+  if (aSize) {
+    Maybe<IntSize> targetSize = mSize != *aSize ? aSize : Nothing();
+    decoder = DecoderFactory::CreateDecoder(mDecoderType, this, mSourceBuffer,
+                                            targetSize, aFlags, mHasBeenDecoded,
+                                            mTransient, imageIsLocked);
+  } else {
+    decoder = DecoderFactory::CreateMetadataDecoder(mDecoderType, this,
+                                                    mSourceBuffer);
   }
 
-  decoder->Init();
-
-  if (NS_FAILED(decoder->GetDecoderError())) {
+  // Make sure DecoderFactory was able to create a decoder successfully.
+  if (!decoder) {
     return nullptr;
   }
 
   if (aSize) {
     // Add a placeholder for the first frame to the SurfaceCache so we won't
     // trigger any more decoders with the same parameters.
     InsertOutcome outcome =
       SurfaceCache::InsertPlaceholder(ImageKey(this),
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -21,16 +21,17 @@
 #include "nsCOMPtr.h"
 #include "imgIContainer.h"
 #include "nsIProperties.h"
 #include "nsTArray.h"
 #include "imgFrame.h"
 #include "LookupResult.h"
 #include "nsThreadUtils.h"
 #include "DecodePool.h"
+#include "DecoderFactory.h"
 #include "Orientation.h"
 #include "nsIObserver.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Pair.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/WeakPtr.h"
@@ -371,17 +372,17 @@ private: // data
 
   /// If this image is animated, a FrameAnimator which manages its animation.
   UniquePtr<FrameAnimator> mAnim;
 
   // Image locking.
   uint32_t                   mLockCount;
 
   // The type of decoder this image needs. Computed from the MIME type in Init().
-  eDecoderType               mDecoderType;
+  DecoderType                mDecoderType;
 
   // How many times we've decoded this image.
   // This is currently only used for statistics
   int32_t                        mDecodeCount;
 
   // If the image contains multiple resolutions, a hint as to which one
   // should be used
   nsIntSize                  mRequestedResolution;
--- a/image/SVGDocumentWrapper.cpp
+++ b/image/SVGDocumentWrapper.cpp
@@ -27,19 +27,21 @@
 #include "nsSVGEffects.h"
 #include "mozilla/dom/SVGAnimatedLength.h"
 #include "nsMimeTypes.h"
 #include "DOMSVGLength.h"
 
 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
 #undef GetCurrentTime
 
-using namespace mozilla::dom;
+namespace mozilla {
 
-namespace mozilla {
+using namespace dom;
+using namespace gfx;
+
 namespace image {
 
 NS_IMPL_ISUPPORTS(SVGDocumentWrapper,
                   nsIStreamListener,
                   nsIRequestObserver,
                   nsIObserver,
                   nsISupportsWeakReference)
 
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -36,16 +36,17 @@
 #include "nsCRT.h"
 #include "nsINetworkPredictor.h"
 #include "mozilla/dom/nsMixedContentBlocker.h"
 
 #include "nsIApplicationCache.h"
 #include "nsIApplicationCacheContainer.h"
 
 #include "nsIMemoryReporter.h"
+#include "DecoderFactory.h"
 #include "Image.h"
 #include "gfxPrefs.h"
 #include "prtime.h"
 
 // we want to explore making the document own the load group
 // so we can associate the document URI with the load group.
 // until this point, we have an evil hack:
 #include "nsIHttpChannelInternal.h"
@@ -2470,17 +2471,18 @@ imgLoader::SupportImageWithMimeType(cons
   nsAutoCString mimeType(aMimeType);
   ToLowerCase(mimeType);
 
   if (aAccept == AcceptedMimeTypes::IMAGES_AND_DOCUMENTS &&
       mimeType.EqualsLiteral("image/svg+xml")) {
     return true;
   }
 
-  return Image::GetDecoderType(mimeType.get()) != Image::eDecoderType_unknown;
+  DecoderType type = DecoderFactory::GetDecoderType(mimeType.get());
+  return type != DecoderType::UNKNOWN;
 }
 
 NS_IMETHODIMP
 imgLoader::GetMIMETypeFromContent(nsIRequest* aRequest,
                                   const uint8_t* aContents,
                                   uint32_t aLength,
                                   nsACString& aContentType)
 {
--- a/image/moz.build
+++ b/image/moz.build
@@ -45,16 +45,17 @@ EXPORTS += [
     'Orientation.h',
     'SurfaceCache.h',
 ]
 
 UNIFIED_SOURCES += [
     'ClippedImage.cpp',
     'DecodePool.cpp',
     'Decoder.cpp',
+    'DecoderFactory.cpp',
     'DynamicImage.cpp',
     'FrameAnimator.cpp',
     'FrozenImage.cpp',
     'Image.cpp',
     'ImageCacheKey.cpp',
     'ImageFactory.cpp',
     'ImageMetadata.cpp',
     'ImageOps.cpp',