Bug 1290293 - Part 2a. Make nsPNGDecoder clear on which surface format is being used. r=tnikkel
authorAndrew Osmond <aosmond@mozilla.com>
Thu, 18 Aug 2016 09:55:45 -0400
changeset 378117 5410a208a5cf0b473de1787a6c3a5829f2208c11
parent 378116 fee2e7e5a59c752bdcdd3dc964040cf05dba16d9
child 378118 1e631015acc8513043c0a3ad1c4a31b50679f7b5
push id7198
push userjlorenzo@mozilla.com
push dateTue, 18 Apr 2017 12:07:49 +0000
treeherdermozilla-beta@d57aa49c3948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstnikkel
bugs1290293
milestone54.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 1290293 - Part 2a. Make nsPNGDecoder clear on which surface format is being used. r=tnikkel
image/decoders/nsPNGDecoder.cpp
image/decoders/nsPNGDecoder.h
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -108,17 +108,17 @@ nsPNGDecoder::nsPNGDecoder(RasterImage* 
  , mNextTransition(Transition::ContinueUnbuffered(State::PNG_DATA))
  , mLastChunkLength(0)
  , mPNG(nullptr)
  , mInfo(nullptr)
  , mCMSLine(nullptr)
  , interlacebuf(nullptr)
  , mInProfile(nullptr)
  , mTransform(nullptr)
- , format(gfx::SurfaceFormat::UNKNOWN)
+ , mFormat(SurfaceFormat::UNKNOWN)
  , mCMSMode(0)
  , mChannels(0)
  , mPass(0)
  , mFrameIsHidden(false)
  , mDisablePremultipliedAlpha(false)
  , mNumFrames(0)
 { }
 
@@ -139,21 +139,20 @@ nsPNGDecoder::~nsPNGDecoder()
     // mTransform belongs to us only if mInProfile is non-null
     if (mTransform) {
       qcms_transform_release(mTransform);
     }
   }
 }
 
 nsPNGDecoder::TransparencyType
-nsPNGDecoder::GetTransparencyType(SurfaceFormat aFormat,
-                                  const IntRect& aFrameRect)
+nsPNGDecoder::GetTransparencyType(const IntRect& aFrameRect)
 {
   // Check if the image has a transparent color in its palette.
-  if (aFormat == SurfaceFormat::B8G8R8A8) {
+  if (HasAlphaChannel()) {
     return TransparencyType::eAlpha;
   }
   if (!aFrameRect.IsEqualEdges(FullFrame())) {
     MOZ_ASSERT(HasAnimation());
     return TransparencyType::eFrameRect;
   }
 
   return TransparencyType::eNone;
@@ -185,22 +184,21 @@ nsPNGDecoder::PostHasTransparencyIfNeede
 // CreateFrame() is used for both simple and animated images.
 nsresult
 nsPNGDecoder::CreateFrame(const FrameInfo& aFrameInfo)
 {
   MOZ_ASSERT(HasSize());
   MOZ_ASSERT(!IsMetadataDecode());
 
   // Check if we have transparency, and send notifications if needed.
-  auto transparency = GetTransparencyType(aFrameInfo.mFormat,
-                                          aFrameInfo.mFrameRect);
+  auto transparency = GetTransparencyType(aFrameInfo.mFrameRect);
   PostHasTransparencyIfNeeded(transparency);
-  SurfaceFormat format = transparency == TransparencyType::eNone
-                       ? SurfaceFormat::B8G8R8X8
-                       : SurfaceFormat::B8G8R8A8;
+  mFormat = transparency == TransparencyType::eNone
+          ? SurfaceFormat::B8G8R8X8
+          : SurfaceFormat::B8G8R8A8;
 
   // Make sure there's no animation or padding if we're downscaling.
   MOZ_ASSERT_IF(Size() != OutputSize(), mNumFrames == 0);
   MOZ_ASSERT_IF(Size() != OutputSize(), !GetImageMetadata().HasAnimation());
   MOZ_ASSERT_IF(Size() != OutputSize(),
                 transparency != TransparencyType::eFrameRect);
 
   // If this image is interlaced, we can display better quality intermediate
@@ -212,17 +210,17 @@ nsPNGDecoder::CreateFrame(const FrameInf
   if (mNumFrames == 0) {
     // The first frame may be displayed progressively.
     pipeFlags |= SurfacePipeFlags::PROGRESSIVE_DISPLAY;
   }
 
   Maybe<SurfacePipe> pipe =
     SurfacePipeFactory::CreateSurfacePipe(this, mNumFrames, Size(),
                                           OutputSize(), aFrameInfo.mFrameRect,
-                                          format, pipeFlags);
+                                          mFormat, pipeFlags);
 
   if (!pipe) {
     mPipe = SurfacePipe();
     return NS_ERROR_FAILURE;
   }
 
   mPipe = Move(*pipe);
 
@@ -254,20 +252,19 @@ void
 nsPNGDecoder::EndImageFrame()
 {
   if (mFrameIsHidden) {
     return;
   }
 
   mNumFrames++;
 
-  Opacity opacity = Opacity::SOME_TRANSPARENCY;
-  if (format == gfx::SurfaceFormat::B8G8R8X8) {
-    opacity = Opacity::FULLY_OPAQUE;
-  }
+  Opacity opacity = mFormat == SurfaceFormat::B8G8R8X8
+                  ? Opacity::FULLY_OPAQUE
+                  : Opacity::SOME_TRANSPARENCY;
 
   PostFrameStop(opacity, mAnimInfo.mDispose,
                 FrameTimeout::FromRawMilliseconds(mAnimInfo.mTimeout),
                 mAnimInfo.mBlend, Some(mFrameRect));
 }
 
 nsresult
 nsPNGDecoder::InitInternal()
@@ -666,21 +663,17 @@ nsPNGDecoder::info_callback(png_structp 
   // members and whatnot, after which we can get channels, rowbytes, etc.
   png_read_update_info(png_ptr, info_ptr);
   decoder->mChannels = channels = png_get_channels(png_ptr, info_ptr);
 
   //---------------------------------------------------------------//
   // copy PNG info into imagelib structs (formerly png_set_dims()) //
   //---------------------------------------------------------------//
 
-  if (channels == 1 || channels == 3) {
-    decoder->format = gfx::SurfaceFormat::B8G8R8X8;
-  } else if (channels == 2 || channels == 4) {
-    decoder->format = gfx::SurfaceFormat::B8G8R8A8;
-  } else {
+  if (channels < 1 || channels > 4) {
     png_error(decoder->mPNG, "Invalid number of channels");
   }
 
 #ifdef PNG_APNG_SUPPORTED
   bool isAnimated = png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL);
   if (isAnimated) {
     int32_t rawTimeout = GetNextFrameDelay(png_ptr, info_ptr);
     decoder->PostIsAnimated(FrameTimeout::FromRawMilliseconds(rawTimeout));
@@ -697,18 +690,17 @@ nsPNGDecoder::info_callback(png_structp 
   if (decoder->IsMetadataDecode()) {
     // If we are animated then the first frame rect is either:
     // 1) the whole image if the IDAT chunk is part of the animation
     // 2) the frame rect of the first fDAT chunk otherwise.
     // If we are not animated then we want to make sure to call
     // PostHasTransparency in the metadata decode if we need to. So it's
     // okay to pass IntRect(0, 0, width, height) here for animated images;
     // they will call with the proper first frame rect in the full decode.
-    auto transparency = decoder->GetTransparencyType(decoder->format,
-                                                     frameRect);
+    auto transparency = decoder->GetTransparencyType(frameRect);
     decoder->PostHasTransparencyIfNeeded(transparency);
 
     // We have the metadata we're looking for, so stop here, before we allocate
     // buffers below.
     return decoder->DoTerminate(png_ptr, TerminalState::SUCCESS);
   }
 
 #ifdef PNG_APNG_SUPPORTED
@@ -716,18 +708,17 @@ nsPNGDecoder::info_callback(png_structp 
     png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback,
                                  nullptr);
   }
 
   if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) {
     decoder->mFrameIsHidden = true;
   } else {
 #endif
-    nsresult rv = decoder->CreateFrame(FrameInfo{ decoder->format,
-                                                  frameRect,
+    nsresult rv = decoder->CreateFrame(FrameInfo{ frameRect,
                                                   isInterlaced });
     if (NS_FAILED(rv)) {
       png_error(decoder->mPNG, "CreateFrame failed");
     }
     MOZ_ASSERT(decoder->mImageData, "Should have a buffer now");
 #ifdef PNG_APNG_SUPPORTED
   }
 #endif
@@ -880,51 +871,44 @@ nsPNGDecoder::WriteRow(uint8_t* aRow)
   uint32_t width = uint32_t(mFrameRect.width);
 
   // Apply color management to the row, if necessary, before writing it out.
   if (mTransform) {
     if (mCMSLine) {
       qcms_transform_data(mTransform, rowToWrite, mCMSLine, width);
 
       // Copy alpha over.
-      if (mChannels == 2 || mChannels == 4) {
+      if (HasAlphaChannel()) {
         for (uint32_t i = 0; i < width; ++i) {
           mCMSLine[4 * i + 3] = rowToWrite[mChannels * i + mChannels - 1];
         }
       }
 
       rowToWrite = mCMSLine;
     } else {
       qcms_transform_data(mTransform, rowToWrite, rowToWrite, width);
     }
   }
 
   // Write this row to the SurfacePipe.
-  DebugOnly<WriteState> result = WriteState::FAILURE;
-  switch (format) {
-    case SurfaceFormat::B8G8R8X8:
+  DebugOnly<WriteState> result;
+  if (HasAlphaChannel()) {
+    if (mDisablePremultipliedAlpha) {
       result = mPipe.WritePixelsToRow<uint32_t>([&]{
-        return PackRGBPixelAndAdvance(rowToWrite);
+        return PackUnpremultipliedRGBAPixelAndAdvance(rowToWrite);
       });
-      break;
-
-    case SurfaceFormat::B8G8R8A8:
-      if (mDisablePremultipliedAlpha) {
-        result = mPipe.WritePixelsToRow<uint32_t>([&]{
-          return PackUnpremultipliedRGBAPixelAndAdvance(rowToWrite);
-        });
-      } else {
-        result = mPipe.WritePixelsToRow<uint32_t>([&]{
-          return PackRGBAPixelAndAdvance(rowToWrite);
-        });
-      }
-      break;
-
-    default:
-      png_error(mPNG, "Invalid SurfaceFormat");
+    } else {
+      result = mPipe.WritePixelsToRow<uint32_t>([&]{
+        return PackRGBAPixelAndAdvance(rowToWrite);
+      });
+    }
+  } else {
+    result = mPipe.WritePixelsToRow<uint32_t>([&]{
+      return PackRGBPixelAndAdvance(rowToWrite);
+    });
   }
 
   MOZ_ASSERT(WriteState(result) != WriteState::FAILURE);
 
   PostInvalidationIfNeeded();
 }
 
 void
@@ -995,17 +979,17 @@ nsPNGDecoder::frame_info_callback(png_st
   if (frameRect.width == 0) {
     png_error(png_ptr, "Frame width must not be 0");
   }
   if (frameRect.height == 0) {
     png_error(png_ptr, "Frame height must not be 0");
   }
 #endif
 
-  const FrameInfo info { decoder->format, frameRect, isInterlaced };
+  const FrameInfo info { frameRect, isInterlaced };
 
   // If the previous frame was hidden, skip the yield (which will mislead the
   // caller, who will think the previous frame was real) and just allocate the
   // new frame here.
   if (previousFrameWasHidden) {
     if (NS_FAILED(decoder->CreateFrame(info))) {
       return decoder->DoTerminate(png_ptr, TerminalState::FAILURE);
     }
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -36,33 +36,36 @@ private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
   explicit nsPNGDecoder(RasterImage* aImage);
 
   /// The information necessary to create a frame.
   struct FrameInfo
   {
-    gfx::SurfaceFormat mFormat;
     gfx::IntRect mFrameRect;
     bool mIsInterlaced;
   };
 
   nsresult CreateFrame(const FrameInfo& aFrameInfo);
   void EndImageFrame();
 
+  bool HasAlphaChannel() const
+  {
+    return mChannels == 2 || mChannels == 4;
+  }
+
   enum class TransparencyType
   {
     eNone,
     eAlpha,
     eFrameRect
   };
 
-  TransparencyType GetTransparencyType(gfx::SurfaceFormat aFormat,
-                                       const gfx::IntRect& aFrameRect);
+  TransparencyType GetTransparencyType(const gfx::IntRect& aFrameRect);
   void PostHasTransparencyIfNeeded(TransparencyType aTransparencyType);
 
   void PostInvalidationIfNeeded();
 
   void WriteRow(uint8_t* aRow);
 
   // Convenience methods to make interacting with StreamingLexer from inside
   // a libpng callback easier.
@@ -97,18 +100,17 @@ private:
 public:
   png_structp mPNG;
   png_infop mInfo;
   nsIntRect mFrameRect;
   uint8_t* mCMSLine;
   uint8_t* interlacebuf;
   qcms_profile* mInProfile;
   qcms_transform* mTransform;
-
-  gfx::SurfaceFormat format;
+  gfx::SurfaceFormat mFormat;
 
   // whether CMS or premultiplied alpha are forced off
   uint32_t mCMSMode;
 
   uint8_t mChannels;
   uint8_t mPass;
   bool mFrameIsHidden;
   bool mDisablePremultipliedAlpha;