tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ImageOps.cpp (7933B)


      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/. */
      6 
      7 #include "ImageOps.h"
      8 
      9 #include "ClippedImage.h"
     10 #include "Decoder.h"
     11 #include "DecoderFactory.h"
     12 #include "DynamicImage.h"
     13 #include "FrozenImage.h"
     14 #include "IDecodingTask.h"
     15 #include "Image.h"
     16 #include "ImageMetadata.h"
     17 #include "imgIContainer.h"
     18 #include "mozilla/gfx/2D.h"
     19 #include "nsNetUtil.h"  // for NS_NewBufferedInputStream
     20 #include "nsStreamUtils.h"
     21 #include "OrientedImage.h"
     22 #include "SourceBuffer.h"
     23 
     24 using namespace mozilla::gfx;
     25 
     26 namespace mozilla::image {
     27 
     28 /* static */
     29 already_AddRefed<Image> ImageOps::Freeze(Image* aImage) {
     30  RefPtr<Image> frozenImage = new FrozenImage(aImage);
     31  return frozenImage.forget();
     32 }
     33 
     34 /* static */
     35 already_AddRefed<imgIContainer> ImageOps::Freeze(imgIContainer* aImage) {
     36  nsCOMPtr<imgIContainer> frozenImage =
     37      new FrozenImage(static_cast<Image*>(aImage));
     38  return frozenImage.forget();
     39 }
     40 
     41 /* static */
     42 already_AddRefed<Image> ImageOps::Clip(Image* aImage, nsIntRect aClip,
     43                                       const Maybe<nsSize>& aSVGViewportSize) {
     44  RefPtr<Image> clippedImage =
     45      new ClippedImage(aImage, aClip, aSVGViewportSize);
     46  return clippedImage.forget();
     47 }
     48 
     49 /* static */
     50 already_AddRefed<imgIContainer> ImageOps::Clip(
     51    imgIContainer* aImage, nsIntRect aClip,
     52    const Maybe<nsSize>& aSVGViewportSize) {
     53  nsCOMPtr<imgIContainer> clippedImage =
     54      new ClippedImage(static_cast<Image*>(aImage), aClip, aSVGViewportSize);
     55  return clippedImage.forget();
     56 }
     57 
     58 /* static */
     59 already_AddRefed<Image> ImageOps::Orient(Image* aImage,
     60                                         Orientation aOrientation) {
     61  if (aOrientation.IsIdentity()) {
     62    return do_AddRef(aImage);
     63  }
     64  RefPtr<Image> image = new OrientedImage(aImage, aOrientation);
     65  return image.forget();
     66 }
     67 
     68 /* static */
     69 already_AddRefed<imgIContainer> ImageOps::Orient(imgIContainer* aImage,
     70                                                 Orientation aOrientation) {
     71  return Orient(static_cast<Image*>(aImage), aOrientation);
     72 }
     73 
     74 /* static */
     75 already_AddRefed<imgIContainer> ImageOps::Unorient(imgIContainer* aImage) {
     76  return Orient(aImage, aImage->GetOrientation().Reversed());
     77 }
     78 
     79 /* static */
     80 already_AddRefed<imgIContainer> ImageOps::CreateFromDrawable(
     81    gfxDrawable* aDrawable) {
     82  nsCOMPtr<imgIContainer> drawableImage = new DynamicImage(aDrawable);
     83  return drawableImage.forget();
     84 }
     85 
     86 class ImageOps::ImageBufferImpl final : public ImageOps::ImageBuffer {
     87 public:
     88  explicit ImageBufferImpl(already_AddRefed<SourceBuffer> aSourceBuffer)
     89      : mSourceBuffer(aSourceBuffer) {}
     90 
     91 protected:
     92  ~ImageBufferImpl() override {}
     93 
     94  already_AddRefed<SourceBuffer> GetSourceBuffer() const override {
     95    RefPtr<SourceBuffer> sourceBuffer = mSourceBuffer;
     96    return sourceBuffer.forget();
     97  }
     98 
     99 private:
    100  RefPtr<SourceBuffer> mSourceBuffer;
    101 };
    102 
    103 /* static */ already_AddRefed<ImageOps::ImageBuffer>
    104 ImageOps::CreateImageBuffer(already_AddRefed<nsIInputStream> aInputStream) {
    105  nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
    106  MOZ_ASSERT(inputStream);
    107 
    108  nsresult rv;
    109 
    110  // Prepare the input stream.
    111  if (!NS_InputStreamIsBuffered(inputStream)) {
    112    nsCOMPtr<nsIInputStream> bufStream;
    113    rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream),
    114                                   inputStream.forget(), 1024);
    115    if (NS_WARN_IF(NS_FAILED(rv))) {
    116      return nullptr;
    117    }
    118    inputStream = std::move(bufStream);
    119  }
    120 
    121  // Figure out how much data we've been passed.
    122  uint64_t length;
    123  rv = inputStream->Available(&length);
    124  if (NS_FAILED(rv) || length > UINT32_MAX) {
    125    return nullptr;
    126  }
    127 
    128  // Write the data into a SourceBuffer.
    129  RefPtr<SourceBuffer> sourceBuffer = new SourceBuffer();
    130  sourceBuffer->ExpectLength(length);
    131  rv = sourceBuffer->AppendFromInputStream(inputStream, length);
    132  if (NS_FAILED(rv)) {
    133    return nullptr;
    134  }
    135  // Make sure our sourceBuffer is marked as complete.
    136  if (sourceBuffer->IsComplete()) {
    137    NS_WARNING(
    138        "The SourceBuffer was unexpectedly marked as complete. This may "
    139        "indicate either an OOM condition, or that imagelib was not "
    140        "initialized properly.");
    141    return nullptr;
    142  }
    143  sourceBuffer->Complete(NS_OK);
    144 
    145  RefPtr<ImageBuffer> imageBuffer = new ImageBufferImpl(sourceBuffer.forget());
    146  return imageBuffer.forget();
    147 }
    148 
    149 /* static */
    150 nsresult ImageOps::DecodeMetadata(already_AddRefed<nsIInputStream> aInputStream,
    151                                  const nsACString& aMimeType,
    152                                  ImageMetadata& aMetadata) {
    153  nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
    154  RefPtr<ImageBuffer> buffer = CreateImageBuffer(inputStream.forget());
    155  return DecodeMetadata(buffer, aMimeType, aMetadata);
    156 }
    157 
    158 /* static */
    159 nsresult ImageOps::DecodeMetadata(ImageBuffer* aBuffer,
    160                                  const nsACString& aMimeType,
    161                                  ImageMetadata& aMetadata) {
    162  if (!aBuffer) {
    163    return NS_ERROR_FAILURE;
    164  }
    165 
    166  RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer();
    167  if (NS_WARN_IF(!sourceBuffer)) {
    168    return NS_ERROR_FAILURE;
    169  }
    170 
    171  // Create a decoder.
    172  DecoderType decoderType =
    173      DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get());
    174  DecoderFlags decoderFlags =
    175      DecoderFactory::GetDefaultDecoderFlagsForType(decoderType);
    176  decoderFlags |= DecoderFlags::FIRST_FRAME_ONLY;
    177  RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousMetadataDecoder(
    178      decoderType, WrapNotNull(sourceBuffer), decoderFlags);
    179  if (!decoder) {
    180    return NS_ERROR_FAILURE;
    181  }
    182 
    183  // Run the decoder synchronously.
    184  RefPtr<IDecodingTask> task =
    185      new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
    186  task->Run();
    187  if (!decoder->GetDecodeDone() || decoder->HasError()) {
    188    return NS_ERROR_FAILURE;
    189  }
    190 
    191  aMetadata = decoder->GetImageMetadata();
    192  if (aMetadata.GetNativeSizes().IsEmpty() && aMetadata.HasSize()) {
    193    aMetadata.AddNativeSize(aMetadata.GetSize());
    194  }
    195 
    196  return NS_OK;
    197 }
    198 
    199 /* static */ already_AddRefed<gfx::SourceSurface> ImageOps::DecodeToSurface(
    200    already_AddRefed<nsIInputStream> aInputStream, const nsACString& aMimeType,
    201    uint32_t aFlags, const Maybe<IntSize>& aSize /* = Nothing() */) {
    202  nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
    203  RefPtr<ImageBuffer> buffer = CreateImageBuffer(inputStream.forget());
    204  return DecodeToSurface(buffer, aMimeType, aFlags, aSize);
    205 }
    206 
    207 /* static */ already_AddRefed<gfx::SourceSurface> ImageOps::DecodeToSurface(
    208    ImageBuffer* aBuffer, const nsACString& aMimeType, uint32_t aFlags,
    209    const Maybe<IntSize>& aSize /* = Nothing() */) {
    210  if (!aBuffer) {
    211    return nullptr;
    212  }
    213 
    214  RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer();
    215  if (NS_WARN_IF(!sourceBuffer)) {
    216    return nullptr;
    217  }
    218 
    219  // Create a decoder.
    220  DecoderType decoderType =
    221      DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get());
    222  RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
    223      decoderType, WrapNotNull(sourceBuffer), aSize,
    224      DecoderFlags::FIRST_FRAME_ONLY, ToSurfaceFlags(aFlags));
    225  if (!decoder) {
    226    return nullptr;
    227  }
    228 
    229  // Run the decoder synchronously.
    230  RefPtr<IDecodingTask> task =
    231      new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
    232  task->Run();
    233  if (!decoder->GetDecodeDone() || decoder->HasError()) {
    234    return nullptr;
    235  }
    236 
    237  // Pull out the surface.
    238  RawAccessFrameRef frame = decoder->GetCurrentFrameRef();
    239  if (!frame) {
    240    return nullptr;
    241  }
    242 
    243  RefPtr<SourceSurface> surface = frame->GetSourceSurface();
    244  if (!surface) {
    245    return nullptr;
    246  }
    247 
    248  return surface.forget();
    249 }
    250 
    251 }  // namespace mozilla::image