image/src/imgTools.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/image/src/imgTools.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,316 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + *
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "imgTools.h"
    1.11 +
    1.12 +#include "gfxUtils.h"
    1.13 +#include "mozilla/gfx/2D.h"
    1.14 +#include "mozilla/RefPtr.h"
    1.15 +#include "nsCOMPtr.h"
    1.16 +#include "nsIDocument.h"
    1.17 +#include "nsIDOMDocument.h"
    1.18 +#include "nsError.h"
    1.19 +#include "imgLoader.h"
    1.20 +#include "imgICache.h"
    1.21 +#include "imgIContainer.h"
    1.22 +#include "imgIEncoder.h"
    1.23 +#include "nsStreamUtils.h"
    1.24 +#include "nsContentUtils.h"
    1.25 +#include "ImageFactory.h"
    1.26 +#include "Image.h"
    1.27 +#include "ScriptedNotificationObserver.h"
    1.28 +#include "imgIScriptedNotificationObserver.h"
    1.29 +#include "gfxPlatform.h"
    1.30 +
    1.31 +using namespace mozilla;
    1.32 +using namespace mozilla::image;
    1.33 +using namespace mozilla::gfx;
    1.34 +
    1.35 +/* ========== imgITools implementation ========== */
    1.36 +
    1.37 +
    1.38 +
    1.39 +NS_IMPL_ISUPPORTS(imgTools, imgITools)
    1.40 +
    1.41 +imgTools::imgTools()
    1.42 +{
    1.43 +  /* member initializers and constructor code */
    1.44 +}
    1.45 +
    1.46 +imgTools::~imgTools()
    1.47 +{
    1.48 +  /* destructor code */
    1.49 +}
    1.50 +
    1.51 +NS_IMETHODIMP imgTools::DecodeImageData(nsIInputStream* aInStr,
    1.52 +                                        const nsACString& aMimeType,
    1.53 +                                        imgIContainer **aContainer)
    1.54 +{
    1.55 +  NS_ABORT_IF_FALSE(*aContainer == nullptr,
    1.56 +                    "Cannot provide an existing image container to DecodeImageData");
    1.57 +
    1.58 +  return DecodeImage(aInStr, aMimeType, aContainer);
    1.59 +}
    1.60 +
    1.61 +NS_IMETHODIMP imgTools::DecodeImage(nsIInputStream* aInStr,
    1.62 +                                    const nsACString& aMimeType,
    1.63 +                                    imgIContainer **aContainer)
    1.64 +{
    1.65 +  nsresult rv;
    1.66 +  nsRefPtr<Image> image;
    1.67 +
    1.68 +  NS_ENSURE_ARG_POINTER(aInStr);
    1.69 +
    1.70 +  // Create a new image container to hold the decoded data.
    1.71 +  nsAutoCString mimeType(aMimeType);
    1.72 +  image = ImageFactory::CreateAnonymousImage(mimeType);
    1.73 +
    1.74 +  if (image->HasError())
    1.75 +    return NS_ERROR_FAILURE;
    1.76 +
    1.77 +  // Prepare the input stream.
    1.78 +  nsCOMPtr<nsIInputStream> inStream = aInStr;
    1.79 +  if (!NS_InputStreamIsBuffered(aInStr)) {
    1.80 +    nsCOMPtr<nsIInputStream> bufStream;
    1.81 +    rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), aInStr, 1024);
    1.82 +    if (NS_SUCCEEDED(rv))
    1.83 +      inStream = bufStream;
    1.84 +  }
    1.85 +
    1.86 +  // Figure out how much data we've been passed.
    1.87 +  uint64_t length;
    1.88 +  rv = inStream->Available(&length);
    1.89 +  NS_ENSURE_SUCCESS(rv, rv);
    1.90 +  NS_ENSURE_TRUE(length <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
    1.91 +
    1.92 +  // Send the source data to the Image.
    1.93 +  rv = image->OnImageDataAvailable(nullptr, nullptr, inStream, 0, uint32_t(length));
    1.94 +  NS_ENSURE_SUCCESS(rv, rv);
    1.95 +  // Let the Image know we've sent all the data.
    1.96 +  rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
    1.97 +  NS_ENSURE_SUCCESS(rv, rv);
    1.98 +
    1.99 +  // All done.
   1.100 +  NS_ADDREF(*aContainer = image.get());
   1.101 +  return NS_OK;
   1.102 +}
   1.103 +
   1.104 +/**
   1.105 + * This takes a DataSourceSurface rather than a SourceSurface because some
   1.106 + * of the callers have a DataSourceSurface and we don't want to call
   1.107 + * GetDataSurface on such surfaces since that may incure a conversion to
   1.108 + * SurfaceType::DATA which we don't need.
   1.109 + */
   1.110 +static nsresult EncodeImageData(DataSourceSurface* aDataSurface,
   1.111 +                                const nsACString& aMimeType,
   1.112 +                                const nsAString& aOutputOptions,
   1.113 +                                nsIInputStream **aStream)
   1.114 +{
   1.115 +  MOZ_ASSERT(aDataSurface->GetFormat() ==  SurfaceFormat::B8G8R8A8,
   1.116 +             "We're assuming B8G8R8A8");
   1.117 +
   1.118 +  // Get an image encoder for the media type
   1.119 +  nsAutoCString encoderCID(
   1.120 +    NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType);
   1.121 +
   1.122 +  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
   1.123 +  if (!encoder)
   1.124 +    return NS_IMAGELIB_ERROR_NO_ENCODER;
   1.125 +
   1.126 +  DataSourceSurface::MappedSurface map;
   1.127 +  if (!aDataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
   1.128 +    return NS_ERROR_FAILURE;
   1.129 +  }
   1.130 +
   1.131 +  IntSize size = aDataSurface->GetSize();
   1.132 +  uint32_t dataLength = map.mStride * size.height;
   1.133 +
   1.134 +  // Encode the bitmap
   1.135 +  nsresult rv = encoder->InitFromData(map.mData,
   1.136 +                                      dataLength,
   1.137 +                                      size.width,
   1.138 +                                      size.height,
   1.139 +                                      map.mStride,
   1.140 +                                      imgIEncoder::INPUT_FORMAT_HOSTARGB,
   1.141 +                                      aOutputOptions);
   1.142 +  aDataSurface->Unmap();
   1.143 +  NS_ENSURE_SUCCESS(rv, rv);
   1.144 +
   1.145 +  return CallQueryInterface(encoder, aStream);
   1.146 +}
   1.147 +
   1.148 +NS_IMETHODIMP imgTools::EncodeImage(imgIContainer *aContainer,
   1.149 +                                    const nsACString& aMimeType,
   1.150 +                                    const nsAString& aOutputOptions,
   1.151 +                                    nsIInputStream **aStream)
   1.152 +{
   1.153 +  // Use frame 0 from the image container.
   1.154 +  RefPtr<SourceSurface> frame =
   1.155 +    aContainer->GetFrame(imgIContainer::FRAME_FIRST,
   1.156 +                         imgIContainer::FLAG_SYNC_DECODE);
   1.157 +  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
   1.158 +
   1.159 +  RefPtr<DataSourceSurface> dataSurface;
   1.160 +
   1.161 +  if (frame->GetFormat() == SurfaceFormat::B8G8R8A8) {
   1.162 +    dataSurface = frame->GetDataSurface();
   1.163 +  } else {
   1.164 +    // Convert format to SurfaceFormat::B8G8R8A8
   1.165 +    dataSurface = gfxUtils::
   1.166 +      CopySurfaceToDataSourceSurfaceWithFormat(frame,
   1.167 +                                               SurfaceFormat::B8G8R8A8);
   1.168 +  }
   1.169 +
   1.170 +  NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
   1.171 +
   1.172 +  return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
   1.173 +}
   1.174 +
   1.175 +NS_IMETHODIMP imgTools::EncodeScaledImage(imgIContainer *aContainer,
   1.176 +                                          const nsACString& aMimeType,
   1.177 +                                          int32_t aScaledWidth,
   1.178 +                                          int32_t aScaledHeight,
   1.179 +                                          const nsAString& aOutputOptions,
   1.180 +                                          nsIInputStream **aStream)
   1.181 +{
   1.182 +  NS_ENSURE_ARG(aScaledWidth >= 0 && aScaledHeight >= 0);
   1.183 +
   1.184 +  // If no scaled size is specified, we'll just encode the image at its
   1.185 +  // original size (no scaling).
   1.186 +  if (aScaledWidth == 0 && aScaledHeight == 0) {
   1.187 +    return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
   1.188 +  }
   1.189 +
   1.190 +  // Use frame 0 from the image container.
   1.191 +  RefPtr<SourceSurface> frame =
   1.192 +    aContainer->GetFrame(imgIContainer::FRAME_FIRST,
   1.193 +                         imgIContainer::FLAG_SYNC_DECODE);
   1.194 +  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
   1.195 +
   1.196 +  int32_t frameWidth = frame->GetSize().width;
   1.197 +  int32_t frameHeight = frame->GetSize().height;
   1.198 +
   1.199 +  // If the given width or height is zero we'll replace it with the image's
   1.200 +  // original dimensions.
   1.201 +  if (aScaledWidth == 0) {
   1.202 +    aScaledWidth = frameWidth;
   1.203 +  } else if (aScaledHeight == 0) {
   1.204 +    aScaledHeight = frameHeight;
   1.205 +  }
   1.206 +
   1.207 +  RefPtr<DataSourceSurface> dataSurface =
   1.208 +    Factory::CreateDataSourceSurface(IntSize(aScaledWidth, aScaledHeight),
   1.209 +                                     SurfaceFormat::B8G8R8A8);
   1.210 +  DataSourceSurface::MappedSurface map;
   1.211 +  if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
   1.212 +    return NS_ERROR_FAILURE;
   1.213 +  }
   1.214 +
   1.215 +  RefPtr<DrawTarget> dt =
   1.216 +    Factory::CreateDrawTargetForData(BackendType::CAIRO,
   1.217 +                                     map.mData,
   1.218 +                                     dataSurface->GetSize(),
   1.219 +                                     map.mStride,
   1.220 +                                     SurfaceFormat::B8G8R8A8);
   1.221 +  dt->DrawSurface(frame,
   1.222 +                  Rect(0, 0, aScaledWidth, aScaledHeight),
   1.223 +                  Rect(0, 0, frameWidth, frameHeight),
   1.224 +                  DrawSurfaceOptions(),
   1.225 +                  DrawOptions(1.0f, CompositionOp::OP_SOURCE));
   1.226 +
   1.227 +  dataSurface->Unmap();
   1.228 +
   1.229 +  return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
   1.230 +}
   1.231 +
   1.232 +NS_IMETHODIMP imgTools::EncodeCroppedImage(imgIContainer *aContainer,
   1.233 +                                           const nsACString& aMimeType,
   1.234 +                                           int32_t aOffsetX,
   1.235 +                                           int32_t aOffsetY,
   1.236 +                                           int32_t aWidth,
   1.237 +                                           int32_t aHeight,
   1.238 +                                           const nsAString& aOutputOptions,
   1.239 +                                           nsIInputStream **aStream)
   1.240 +{
   1.241 +  NS_ENSURE_ARG(aOffsetX >= 0 && aOffsetY >= 0 && aWidth >= 0 && aHeight >= 0);
   1.242 +
   1.243 +  // Offsets must be zero when no width and height are given or else we're out
   1.244 +  // of bounds.
   1.245 +  NS_ENSURE_ARG(aWidth + aHeight > 0 || aOffsetX + aOffsetY == 0);
   1.246 +
   1.247 +  // If no size is specified then we'll preserve the image's original dimensions
   1.248 +  // and don't need to crop.
   1.249 +  if (aWidth == 0 && aHeight == 0) {
   1.250 +    return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
   1.251 +  }
   1.252 +
   1.253 +  // Use frame 0 from the image container.
   1.254 +  RefPtr<SourceSurface> frame =
   1.255 +    aContainer->GetFrame(imgIContainer::FRAME_FIRST,
   1.256 +                         imgIContainer::FLAG_SYNC_DECODE);
   1.257 +  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
   1.258 +
   1.259 +  int32_t frameWidth = frame->GetSize().width;
   1.260 +  int32_t frameHeight = frame->GetSize().height;
   1.261 +
   1.262 +  // If the given width or height is zero we'll replace it with the image's
   1.263 +  // original dimensions.
   1.264 +  if (aWidth == 0) {
   1.265 +    aWidth = frameWidth;
   1.266 +  } else if (aHeight == 0) {
   1.267 +    aHeight = frameHeight;
   1.268 +  }
   1.269 +
   1.270 +  // Check that the given crop rectangle is within image bounds.
   1.271 +  NS_ENSURE_ARG(frameWidth >= aOffsetX + aWidth &&
   1.272 +                frameHeight >= aOffsetY + aHeight);
   1.273 +
   1.274 +  RefPtr<DataSourceSurface> dataSurface =
   1.275 +    Factory::CreateDataSourceSurface(IntSize(aWidth, aHeight),
   1.276 +                                     SurfaceFormat::B8G8R8A8);
   1.277 +  DataSourceSurface::MappedSurface map;
   1.278 +  if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
   1.279 +    return NS_ERROR_FAILURE;
   1.280 +  }
   1.281 +
   1.282 +  RefPtr<DrawTarget> dt =
   1.283 +    Factory::CreateDrawTargetForData(BackendType::CAIRO,
   1.284 +                                     map.mData,
   1.285 +                                     dataSurface->GetSize(),
   1.286 +                                     map.mStride,
   1.287 +                                     SurfaceFormat::B8G8R8A8);
   1.288 +  dt->CopySurface(frame,
   1.289 +                  IntRect(aOffsetX, aOffsetY, aWidth, aHeight),
   1.290 +                  IntPoint(0, 0));
   1.291 +
   1.292 +  dataSurface->Unmap();
   1.293 +
   1.294 +  return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
   1.295 +}
   1.296 +
   1.297 +NS_IMETHODIMP imgTools::CreateScriptedObserver(imgIScriptedNotificationObserver* aInner,
   1.298 +                                               imgINotificationObserver** aObserver)
   1.299 +{
   1.300 +  NS_ADDREF(*aObserver = new ScriptedNotificationObserver(aInner));
   1.301 +  return NS_OK;
   1.302 +}
   1.303 +
   1.304 +NS_IMETHODIMP
   1.305 +imgTools::GetImgLoaderForDocument(nsIDOMDocument* aDoc, imgILoader** aLoader)
   1.306 +{
   1.307 +  nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
   1.308 +  NS_IF_ADDREF(*aLoader = nsContentUtils::GetImgLoaderForDocument(doc));
   1.309 +  return NS_OK;
   1.310 +}
   1.311 +
   1.312 +NS_IMETHODIMP
   1.313 +imgTools::GetImgCacheForDocument(nsIDOMDocument* aDoc, imgICache** aCache)
   1.314 +{
   1.315 +  nsCOMPtr<imgILoader> loader;
   1.316 +  nsresult rv = GetImgLoaderForDocument(aDoc, getter_AddRefs(loader));
   1.317 +  NS_ENSURE_SUCCESS(rv, rv);
   1.318 +  return CallQueryInterface(loader, aCache);
   1.319 +}

mercurial