content/canvas/src/ImageEncoder.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     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/. */
     6 #include "gfxImageSurface.h"
     7 #include "ImageEncoder.h"
     8 #include "mozilla/dom/CanvasRenderingContext2D.h"
    10 namespace mozilla {
    11 namespace dom {
    13 class EncodingCompleteEvent : public nsRunnable
    14 {
    15 public:
    16   NS_DECL_THREADSAFE_ISUPPORTS
    18   EncodingCompleteEvent(nsIScriptContext* aScriptContext,
    19                         nsIThread* aEncoderThread,
    20                         FileCallback& aCallback)
    21     : mImgSize(0)
    22     , mType()
    23     , mImgData(nullptr)
    24     , mScriptContext(aScriptContext)
    25     , mEncoderThread(aEncoderThread)
    26     , mCallback(&aCallback)
    27     , mFailed(false)
    28   {}
    29   virtual ~EncodingCompleteEvent() {}
    31   NS_IMETHOD Run()
    32   {
    33     MOZ_ASSERT(NS_IsMainThread());
    35     mozilla::ErrorResult rv;
    37     if (!mFailed) {
    38       nsRefPtr<nsDOMMemoryFile> blob =
    39         new nsDOMMemoryFile(mImgData, mImgSize, mType);
    41       if (mScriptContext) {
    42         JSContext* jsContext = mScriptContext->GetNativeContext();
    43         if (jsContext) {
    44           JS_updateMallocCounter(jsContext, mImgSize);
    45         }
    46       }
    48       mCallback->Call(blob, rv);
    49     }
    51     // These members aren't thread-safe. We're making sure that they're being
    52     // released on the main thread here. Otherwise, they could be getting
    53     // released by EncodingRunnable's destructor on the encoding thread
    54     // (bug 916128).
    55     mScriptContext = nullptr;
    56     mCallback = nullptr;
    58     mEncoderThread->Shutdown();
    59     return rv.ErrorCode();
    60   }
    62   void SetMembers(void* aImgData, uint64_t aImgSize, const nsAutoString& aType)
    63   {
    64     mImgData = aImgData;
    65     mImgSize = aImgSize;
    66     mType = aType;
    67   }
    69   void SetFailed()
    70   {
    71     mFailed = true;
    72   }
    74 private:
    75   uint64_t mImgSize;
    76   nsAutoString mType;
    77   void* mImgData;
    78   nsCOMPtr<nsIScriptContext> mScriptContext;
    79   nsCOMPtr<nsIThread> mEncoderThread;
    80   nsRefPtr<FileCallback> mCallback;
    81   bool mFailed;
    82 };
    84 NS_IMPL_ISUPPORTS(EncodingCompleteEvent, nsIRunnable);
    86 class EncodingRunnable : public nsRunnable
    87 {
    88 public:
    89   NS_DECL_THREADSAFE_ISUPPORTS
    91   EncodingRunnable(const nsAString& aType,
    92                    const nsAString& aOptions,
    93                    uint8_t* aImageBuffer,
    94                    imgIEncoder* aEncoder,
    95                    EncodingCompleteEvent* aEncodingCompleteEvent,
    96                    int32_t aFormat,
    97                    const nsIntSize aSize,
    98                    bool aUsePlaceholder,
    99                    bool aUsingCustomOptions)
   100     : mType(aType)
   101     , mOptions(aOptions)
   102     , mImageBuffer(aImageBuffer)
   103     , mEncoder(aEncoder)
   104     , mEncodingCompleteEvent(aEncodingCompleteEvent)
   105     , mFormat(aFormat)
   106     , mSize(aSize)
   107     , mUsePlaceholder(aUsePlaceholder)
   108     , mUsingCustomOptions(aUsingCustomOptions)
   109   {}
   110   virtual ~EncodingRunnable() {}
   112   nsresult ProcessImageData(uint64_t* aImgSize, void** aImgData)
   113   {
   114     nsCOMPtr<nsIInputStream> stream;
   115     nsresult rv = ImageEncoder::ExtractDataInternal(mType,
   116                                                     mOptions,
   117                                                     mImageBuffer,
   118                                                     mFormat,
   119                                                     mSize,
   120                                                     mUsePlaceholder,
   121                                                     nullptr,
   122                                                     getter_AddRefs(stream),
   123                                                     mEncoder);
   125     // If there are unrecognized custom parse options, we should fall back to
   126     // the default values for the encoder without any options at all.
   127     if (rv == NS_ERROR_INVALID_ARG && mUsingCustomOptions) {
   128       rv = ImageEncoder::ExtractDataInternal(mType,
   129                                              EmptyString(),
   130                                              mImageBuffer,
   131                                              mFormat,
   132                                              mSize,
   133                                              mUsePlaceholder,
   134                                              nullptr,
   135                                              getter_AddRefs(stream),
   136                                              mEncoder);
   137     }
   138     NS_ENSURE_SUCCESS(rv, rv);
   140     rv = stream->Available(aImgSize);
   141     NS_ENSURE_SUCCESS(rv, rv);
   142     NS_ENSURE_TRUE(*aImgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
   144     rv = NS_ReadInputStreamToBuffer(stream, aImgData, *aImgSize);
   145     NS_ENSURE_SUCCESS(rv, rv);
   147     return rv;
   148   }
   150   NS_IMETHOD Run()
   151   {
   152     uint64_t imgSize;
   153     void* imgData = nullptr;
   155     nsresult rv = ProcessImageData(&imgSize, &imgData);
   156     if (NS_FAILED(rv)) {
   157       mEncodingCompleteEvent->SetFailed();
   158     } else {
   159       mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
   160     }
   161     rv = NS_DispatchToMainThread(mEncodingCompleteEvent, NS_DISPATCH_NORMAL);
   162     if (NS_FAILED(rv)) {
   163       // Better to leak than to crash.
   164       mEncodingCompleteEvent.forget();
   165       return rv;
   166     }
   168     return rv;
   169   }
   171 private:
   172   nsAutoString mType;
   173   nsAutoString mOptions;
   174   nsAutoArrayPtr<uint8_t> mImageBuffer;
   175   nsCOMPtr<imgIEncoder> mEncoder;
   176   nsRefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
   177   int32_t mFormat;
   178   const nsIntSize mSize;
   179   bool mUsePlaceholder;
   180   bool mUsingCustomOptions;
   181 };
   183 NS_IMPL_ISUPPORTS(EncodingRunnable, nsIRunnable)
   185 /* static */
   186 nsresult
   187 ImageEncoder::ExtractData(nsAString& aType,
   188                           const nsAString& aOptions,
   189                           const nsIntSize aSize,
   190                           bool aUsePlaceholder,
   191                           nsICanvasRenderingContextInternal* aContext,
   192                           nsIInputStream** aStream)
   193 {
   194   nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
   195   if (!encoder) {
   196     return NS_IMAGELIB_ERROR_NO_ENCODER;
   197   }
   199   return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize,
   200                              aUsePlaceholder, aContext, aStream, encoder);
   201 }
   203 /* static */
   204 nsresult
   205 ImageEncoder::ExtractDataAsync(nsAString& aType,
   206                                const nsAString& aOptions,
   207                                bool aUsingCustomOptions,
   208                                uint8_t* aImageBuffer,
   209                                int32_t aFormat,
   210                                const nsIntSize aSize,
   211                                bool aUsePlaceholder,
   212                                nsICanvasRenderingContextInternal* aContext,
   213                                nsIScriptContext* aScriptContext,
   214                                FileCallback& aCallback)
   215 {
   216   nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
   217   if (!encoder) {
   218     return NS_IMAGELIB_ERROR_NO_ENCODER;
   219   }
   221   nsCOMPtr<nsIThread> encoderThread;
   222   nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
   223   NS_ENSURE_SUCCESS(rv, rv);
   225   nsRefPtr<EncodingCompleteEvent> completeEvent =
   226     new EncodingCompleteEvent(aScriptContext, encoderThread, aCallback);
   228   nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
   229                                                      aOptions,
   230                                                      aImageBuffer,
   231                                                      encoder,
   232                                                      completeEvent,
   233                                                      aFormat,
   234                                                      aSize,
   235                                                      aUsePlaceholder,
   236                                                      aUsingCustomOptions);
   237   return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
   238 }
   240 /*static*/ nsresult
   241 ImageEncoder::GetInputStream(int32_t aWidth,
   242                              int32_t aHeight,
   243                              uint8_t* aImageBuffer,
   244                              int32_t aFormat,
   245                              imgIEncoder* aEncoder,
   246                              const char16_t* aEncoderOptions,
   247                              nsIInputStream** aStream)
   248 {
   249   nsresult rv =
   250     aEncoder->InitFromData(aImageBuffer,
   251                            aWidth * aHeight * 4, aWidth, aHeight, aWidth * 4,
   252                            aFormat,
   253                            nsDependentString(aEncoderOptions));
   254   NS_ENSURE_SUCCESS(rv, rv);
   256   return CallQueryInterface(aEncoder, aStream);
   257 }
   259 /* static */
   260 nsresult
   261 ImageEncoder::ExtractDataInternal(const nsAString& aType,
   262                                   const nsAString& aOptions,
   263                                   uint8_t* aImageBuffer,
   264                                   int32_t aFormat,
   265                                   const nsIntSize aSize,
   266                                   bool aUsePlaceholder,
   267                                   nsICanvasRenderingContextInternal* aContext,
   268                                   nsIInputStream** aStream,
   269                                   imgIEncoder* aEncoder)
   270 {
   271   nsCOMPtr<nsIInputStream> imgStream;
   273   // get image bytes
   274   nsresult rv;
   275   if (aImageBuffer && !aUsePlaceholder) {
   276     rv = ImageEncoder::GetInputStream(
   277       aSize.width,
   278       aSize.height,
   279       aImageBuffer,
   280       aFormat,
   281       aEncoder,
   282       nsPromiseFlatString(aOptions).get(),
   283       getter_AddRefs(imgStream));
   284   } else if (aContext && !aUsePlaceholder) {
   285     NS_ConvertUTF16toUTF8 encoderType(aType);
   286     rv = aContext->GetInputStream(encoderType.get(),
   287                                   nsPromiseFlatString(aOptions).get(),
   288                                   getter_AddRefs(imgStream));
   289   } else {
   290     // If placeholder data requested or no context, encode an empty image.
   291     // note that if we didn't have a current context, the spec says we're
   292     // supposed to just return transparent black pixels of the canvas
   293     // dimensions.
   294     nsRefPtr<gfxImageSurface> emptyCanvas =
   295       new gfxImageSurface(gfxIntSize(aSize.width, aSize.height),
   296                           gfxImageFormat::ARGB32);
   297     if (emptyCanvas->CairoStatus()) {
   298       return NS_ERROR_INVALID_ARG;
   299     }
   300     if (aUsePlaceholder) {
   301       // If placeholder data was requested, return all-white, opaque image data.
   302       int32_t dataSize = emptyCanvas->GetDataSize();
   303       memset(emptyCanvas->Data(), 0xFF, dataSize);
   304     }
   305     rv = aEncoder->InitFromData(emptyCanvas->Data(),
   306                                 aSize.width * aSize.height * 4,
   307                                 aSize.width,
   308                                 aSize.height,
   309                                 aSize.width * 4,
   310                                 imgIEncoder::INPUT_FORMAT_HOSTARGB,
   311                                 aOptions);
   312     if (NS_SUCCEEDED(rv)) {
   313       imgStream = do_QueryInterface(aEncoder);
   314     }
   315   }
   316   NS_ENSURE_SUCCESS(rv, rv);
   318   imgStream.forget(aStream);
   319   return rv;
   320 }
   322 /* static */
   323 already_AddRefed<imgIEncoder>
   324 ImageEncoder::GetImageEncoder(nsAString& aType)
   325 {
   326   // Get an image encoder for the media type.
   327   nsCString encoderCID("@mozilla.org/image/encoder;2?type=");
   328   NS_ConvertUTF16toUTF8 encoderType(aType);
   329   encoderCID += encoderType;
   330   nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
   332   if (!encoder && aType != NS_LITERAL_STRING("image/png")) {
   333     // Unable to create an encoder instance of the specified type. Falling back
   334     // to PNG.
   335     aType.AssignLiteral("image/png");
   336     nsCString PNGEncoderCID("@mozilla.org/image/encoder;2?type=image/png");
   337     encoder = do_CreateInstance(PNGEncoderCID.get());
   338   }
   340   return encoder.forget();
   341 }
   343 } // namespace dom
   344 } // namespace mozilla

mercurial