image/src/imgTools.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     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/. */
     7 #include "imgTools.h"
     9 #include "gfxUtils.h"
    10 #include "mozilla/gfx/2D.h"
    11 #include "mozilla/RefPtr.h"
    12 #include "nsCOMPtr.h"
    13 #include "nsIDocument.h"
    14 #include "nsIDOMDocument.h"
    15 #include "nsError.h"
    16 #include "imgLoader.h"
    17 #include "imgICache.h"
    18 #include "imgIContainer.h"
    19 #include "imgIEncoder.h"
    20 #include "nsStreamUtils.h"
    21 #include "nsContentUtils.h"
    22 #include "ImageFactory.h"
    23 #include "Image.h"
    24 #include "ScriptedNotificationObserver.h"
    25 #include "imgIScriptedNotificationObserver.h"
    26 #include "gfxPlatform.h"
    28 using namespace mozilla;
    29 using namespace mozilla::image;
    30 using namespace mozilla::gfx;
    32 /* ========== imgITools implementation ========== */
    36 NS_IMPL_ISUPPORTS(imgTools, imgITools)
    38 imgTools::imgTools()
    39 {
    40   /* member initializers and constructor code */
    41 }
    43 imgTools::~imgTools()
    44 {
    45   /* destructor code */
    46 }
    48 NS_IMETHODIMP imgTools::DecodeImageData(nsIInputStream* aInStr,
    49                                         const nsACString& aMimeType,
    50                                         imgIContainer **aContainer)
    51 {
    52   NS_ABORT_IF_FALSE(*aContainer == nullptr,
    53                     "Cannot provide an existing image container to DecodeImageData");
    55   return DecodeImage(aInStr, aMimeType, aContainer);
    56 }
    58 NS_IMETHODIMP imgTools::DecodeImage(nsIInputStream* aInStr,
    59                                     const nsACString& aMimeType,
    60                                     imgIContainer **aContainer)
    61 {
    62   nsresult rv;
    63   nsRefPtr<Image> image;
    65   NS_ENSURE_ARG_POINTER(aInStr);
    67   // Create a new image container to hold the decoded data.
    68   nsAutoCString mimeType(aMimeType);
    69   image = ImageFactory::CreateAnonymousImage(mimeType);
    71   if (image->HasError())
    72     return NS_ERROR_FAILURE;
    74   // Prepare the input stream.
    75   nsCOMPtr<nsIInputStream> inStream = aInStr;
    76   if (!NS_InputStreamIsBuffered(aInStr)) {
    77     nsCOMPtr<nsIInputStream> bufStream;
    78     rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), aInStr, 1024);
    79     if (NS_SUCCEEDED(rv))
    80       inStream = bufStream;
    81   }
    83   // Figure out how much data we've been passed.
    84   uint64_t length;
    85   rv = inStream->Available(&length);
    86   NS_ENSURE_SUCCESS(rv, rv);
    87   NS_ENSURE_TRUE(length <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
    89   // Send the source data to the Image.
    90   rv = image->OnImageDataAvailable(nullptr, nullptr, inStream, 0, uint32_t(length));
    91   NS_ENSURE_SUCCESS(rv, rv);
    92   // Let the Image know we've sent all the data.
    93   rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
    94   NS_ENSURE_SUCCESS(rv, rv);
    96   // All done.
    97   NS_ADDREF(*aContainer = image.get());
    98   return NS_OK;
    99 }
   101 /**
   102  * This takes a DataSourceSurface rather than a SourceSurface because some
   103  * of the callers have a DataSourceSurface and we don't want to call
   104  * GetDataSurface on such surfaces since that may incure a conversion to
   105  * SurfaceType::DATA which we don't need.
   106  */
   107 static nsresult EncodeImageData(DataSourceSurface* aDataSurface,
   108                                 const nsACString& aMimeType,
   109                                 const nsAString& aOutputOptions,
   110                                 nsIInputStream **aStream)
   111 {
   112   MOZ_ASSERT(aDataSurface->GetFormat() ==  SurfaceFormat::B8G8R8A8,
   113              "We're assuming B8G8R8A8");
   115   // Get an image encoder for the media type
   116   nsAutoCString encoderCID(
   117     NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType);
   119   nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
   120   if (!encoder)
   121     return NS_IMAGELIB_ERROR_NO_ENCODER;
   123   DataSourceSurface::MappedSurface map;
   124   if (!aDataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
   125     return NS_ERROR_FAILURE;
   126   }
   128   IntSize size = aDataSurface->GetSize();
   129   uint32_t dataLength = map.mStride * size.height;
   131   // Encode the bitmap
   132   nsresult rv = encoder->InitFromData(map.mData,
   133                                       dataLength,
   134                                       size.width,
   135                                       size.height,
   136                                       map.mStride,
   137                                       imgIEncoder::INPUT_FORMAT_HOSTARGB,
   138                                       aOutputOptions);
   139   aDataSurface->Unmap();
   140   NS_ENSURE_SUCCESS(rv, rv);
   142   return CallQueryInterface(encoder, aStream);
   143 }
   145 NS_IMETHODIMP imgTools::EncodeImage(imgIContainer *aContainer,
   146                                     const nsACString& aMimeType,
   147                                     const nsAString& aOutputOptions,
   148                                     nsIInputStream **aStream)
   149 {
   150   // Use frame 0 from the image container.
   151   RefPtr<SourceSurface> frame =
   152     aContainer->GetFrame(imgIContainer::FRAME_FIRST,
   153                          imgIContainer::FLAG_SYNC_DECODE);
   154   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
   156   RefPtr<DataSourceSurface> dataSurface;
   158   if (frame->GetFormat() == SurfaceFormat::B8G8R8A8) {
   159     dataSurface = frame->GetDataSurface();
   160   } else {
   161     // Convert format to SurfaceFormat::B8G8R8A8
   162     dataSurface = gfxUtils::
   163       CopySurfaceToDataSourceSurfaceWithFormat(frame,
   164                                                SurfaceFormat::B8G8R8A8);
   165   }
   167   NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
   169   return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
   170 }
   172 NS_IMETHODIMP imgTools::EncodeScaledImage(imgIContainer *aContainer,
   173                                           const nsACString& aMimeType,
   174                                           int32_t aScaledWidth,
   175                                           int32_t aScaledHeight,
   176                                           const nsAString& aOutputOptions,
   177                                           nsIInputStream **aStream)
   178 {
   179   NS_ENSURE_ARG(aScaledWidth >= 0 && aScaledHeight >= 0);
   181   // If no scaled size is specified, we'll just encode the image at its
   182   // original size (no scaling).
   183   if (aScaledWidth == 0 && aScaledHeight == 0) {
   184     return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
   185   }
   187   // Use frame 0 from the image container.
   188   RefPtr<SourceSurface> frame =
   189     aContainer->GetFrame(imgIContainer::FRAME_FIRST,
   190                          imgIContainer::FLAG_SYNC_DECODE);
   191   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
   193   int32_t frameWidth = frame->GetSize().width;
   194   int32_t frameHeight = frame->GetSize().height;
   196   // If the given width or height is zero we'll replace it with the image's
   197   // original dimensions.
   198   if (aScaledWidth == 0) {
   199     aScaledWidth = frameWidth;
   200   } else if (aScaledHeight == 0) {
   201     aScaledHeight = frameHeight;
   202   }
   204   RefPtr<DataSourceSurface> dataSurface =
   205     Factory::CreateDataSourceSurface(IntSize(aScaledWidth, aScaledHeight),
   206                                      SurfaceFormat::B8G8R8A8);
   207   DataSourceSurface::MappedSurface map;
   208   if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
   209     return NS_ERROR_FAILURE;
   210   }
   212   RefPtr<DrawTarget> dt =
   213     Factory::CreateDrawTargetForData(BackendType::CAIRO,
   214                                      map.mData,
   215                                      dataSurface->GetSize(),
   216                                      map.mStride,
   217                                      SurfaceFormat::B8G8R8A8);
   218   dt->DrawSurface(frame,
   219                   Rect(0, 0, aScaledWidth, aScaledHeight),
   220                   Rect(0, 0, frameWidth, frameHeight),
   221                   DrawSurfaceOptions(),
   222                   DrawOptions(1.0f, CompositionOp::OP_SOURCE));
   224   dataSurface->Unmap();
   226   return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
   227 }
   229 NS_IMETHODIMP imgTools::EncodeCroppedImage(imgIContainer *aContainer,
   230                                            const nsACString& aMimeType,
   231                                            int32_t aOffsetX,
   232                                            int32_t aOffsetY,
   233                                            int32_t aWidth,
   234                                            int32_t aHeight,
   235                                            const nsAString& aOutputOptions,
   236                                            nsIInputStream **aStream)
   237 {
   238   NS_ENSURE_ARG(aOffsetX >= 0 && aOffsetY >= 0 && aWidth >= 0 && aHeight >= 0);
   240   // Offsets must be zero when no width and height are given or else we're out
   241   // of bounds.
   242   NS_ENSURE_ARG(aWidth + aHeight > 0 || aOffsetX + aOffsetY == 0);
   244   // If no size is specified then we'll preserve the image's original dimensions
   245   // and don't need to crop.
   246   if (aWidth == 0 && aHeight == 0) {
   247     return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
   248   }
   250   // Use frame 0 from the image container.
   251   RefPtr<SourceSurface> frame =
   252     aContainer->GetFrame(imgIContainer::FRAME_FIRST,
   253                          imgIContainer::FLAG_SYNC_DECODE);
   254   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
   256   int32_t frameWidth = frame->GetSize().width;
   257   int32_t frameHeight = frame->GetSize().height;
   259   // If the given width or height is zero we'll replace it with the image's
   260   // original dimensions.
   261   if (aWidth == 0) {
   262     aWidth = frameWidth;
   263   } else if (aHeight == 0) {
   264     aHeight = frameHeight;
   265   }
   267   // Check that the given crop rectangle is within image bounds.
   268   NS_ENSURE_ARG(frameWidth >= aOffsetX + aWidth &&
   269                 frameHeight >= aOffsetY + aHeight);
   271   RefPtr<DataSourceSurface> dataSurface =
   272     Factory::CreateDataSourceSurface(IntSize(aWidth, aHeight),
   273                                      SurfaceFormat::B8G8R8A8);
   274   DataSourceSurface::MappedSurface map;
   275   if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
   276     return NS_ERROR_FAILURE;
   277   }
   279   RefPtr<DrawTarget> dt =
   280     Factory::CreateDrawTargetForData(BackendType::CAIRO,
   281                                      map.mData,
   282                                      dataSurface->GetSize(),
   283                                      map.mStride,
   284                                      SurfaceFormat::B8G8R8A8);
   285   dt->CopySurface(frame,
   286                   IntRect(aOffsetX, aOffsetY, aWidth, aHeight),
   287                   IntPoint(0, 0));
   289   dataSurface->Unmap();
   291   return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
   292 }
   294 NS_IMETHODIMP imgTools::CreateScriptedObserver(imgIScriptedNotificationObserver* aInner,
   295                                                imgINotificationObserver** aObserver)
   296 {
   297   NS_ADDREF(*aObserver = new ScriptedNotificationObserver(aInner));
   298   return NS_OK;
   299 }
   301 NS_IMETHODIMP
   302 imgTools::GetImgLoaderForDocument(nsIDOMDocument* aDoc, imgILoader** aLoader)
   303 {
   304   nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
   305   NS_IF_ADDREF(*aLoader = nsContentUtils::GetImgLoaderForDocument(doc));
   306   return NS_OK;
   307 }
   309 NS_IMETHODIMP
   310 imgTools::GetImgCacheForDocument(nsIDOMDocument* aDoc, imgICache** aCache)
   311 {
   312   nsCOMPtr<imgILoader> loader;
   313   nsresult rv = GetImgLoaderForDocument(aDoc, getter_AddRefs(loader));
   314   NS_ENSURE_SUCCESS(rv, rv);
   315   return CallQueryInterface(loader, aCache);
   316 }

mercurial