Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/dom/base/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 16 kB image not shown  

Quelle  ImageEncoder.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "ImageEncoder.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
#include "mozilla/dom/GeneratePlaceholderCanvasData.h"
#include "mozilla/dom/MemoryBlobImpl.h"
#include "mozilla/dom/OffscreenCanvasDisplayHelper.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/layers/CanvasRenderer.h"
#include "mozilla/RefPtr.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/Unused.h"
#include "gfxUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsThreadUtils.h"
#include "nsNetUtil.h"
#include "nsXPCOMCIDInternal.h"
#include "YCbCrUtils.h"

using namespace mozilla::gfx;

namespace mozilla::dom {

// This class should be placed inside GetBRGADataSourceSurfaceSync(). However,
// due to B2G ICS uses old complier (C++98/03) which forbids local class as
// template parameter, we need to move this class outside.
class SurfaceHelper : public Runnable {
 public:
  explicit SurfaceHelper(already_AddRefed<layers::Image> aImage)
      : Runnable("SurfaceHelper"), mImage(aImage) {}

  // It retrieves a SourceSurface reference and convert color format on main
  // thread and passes DataSourceSurface to caller thread.
  NS_IMETHOD Run() override {
    RefPtr<gfx::SourceSurface> surface = mImage->GetAsSourceSurface();

    if (surface->GetFormat() == gfx::SurfaceFormat::B8G8R8A8) {
      mDataSourceSurface = surface->GetDataSurface();
    } else {
      mDataSourceSurface = gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(
          surface, gfx::SurfaceFormat::B8G8R8A8);
    }

    // It guarantees the reference will be released on main thread.
    NS_ReleaseOnMainThread("SurfaceHelper::surface", surface.forget());
    return NS_OK;
  }

  already_AddRefed<gfx::DataSourceSurface> GetDataSurfaceSafe() {
    nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadSerialEventTarget();
    MOZ_ASSERT(mainTarget);
    SyncRunnable::DispatchToThread(mainTarget, thisfalse);

    return mDataSourceSurface.forget();
  }

 private:
  RefPtr<layers::Image> mImage;
  RefPtr<gfx::DataSourceSurface> mDataSourceSurface;
};

// This function returns a DataSourceSurface in B8G8R8A8 format.
// It uses SourceSurface to do format convert. Because most SourceSurface in
// image formats should be referenced or dereferenced on main thread, it uses a
// sync class SurfaceHelper to retrieve SourceSurface and convert to B8G8R8A8 on
// main thread.
already_AddRefed<DataSourceSurface> GetBRGADataSourceSurfaceSync(
    already_AddRefed<layers::Image> aImage) {
  RefPtr<SurfaceHelper> helper = new SurfaceHelper(std::move(aImage));
  return helper->GetDataSurfaceSafe();
}

class EncodingCompleteEvent final : public DiscardableRunnable {
  virtual ~EncodingCompleteEvent() = default;

 public:
  explicit EncodingCompleteEvent(
      EncodeCompleteCallback* aEncodeCompleteCallback)
      : DiscardableRunnable("EncodingCompleteEvent"),
        mImgSize(0),
        mImgData(nullptr),
        mEncodeCompleteCallback(aEncodeCompleteCallback),
        mFailed(false) {
    if (!NS_IsMainThread() && IsCurrentThreadRunningWorker()) {
      mCreationEventTarget = GetCurrentSerialEventTarget();
    } else {
      mCreationEventTarget = GetMainThreadSerialEventTarget();
    }
  }

  // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT.  See
  // bug 1535398.
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
  NS_IMETHOD Run() override {
    nsresult rv = NS_OK;

    // We want to null out mEncodeCompleteCallback no matter what.
    RefPtr<EncodeCompleteCallback> callback(std::move(mEncodeCompleteCallback));
    if (!mFailed) {
      RefPtr<BlobImpl> blobImpl = new MemoryBlobImpl(mImgData, mImgSize, mType);
      rv = callback->ReceiveBlobImpl(blobImpl.forget());
    } else {
      rv = callback->ReceiveBlobImpl(nullptr);
    }

    return rv;
  }

  void SetMembers(void* aImgData, uint64_t aImgSize,
                  const nsAutoString& aType) {
    mImgData = aImgData;
    mImgSize = aImgSize;
    mType = aType;
  }

  void SetFailed() { mFailed = true; }

  nsIEventTarget* GetCreationThreadEventTarget() {
    return mCreationEventTarget;
  }

  bool CanBeDeletedOnAnyThread() {
    return !mEncodeCompleteCallback ||
           mEncodeCompleteCallback->CanBeDeletedOnAnyThread();
  }

 private:
  uint64_t mImgSize;
  nsAutoString mType;
  void* mImgData;
  nsCOMPtr<nsIEventTarget> mCreationEventTarget;
  RefPtr<EncodeCompleteCallback> mEncodeCompleteCallback;
  bool mFailed;
};

class EncodingRunnable : public Runnable {
  virtual ~EncodingRunnable() = default;

 public:
  NS_INLINE_DECL_REFCOUNTING_INHERITED(EncodingRunnable, Runnable)

  EncodingRunnable(const nsAString& aType, const nsAString& aOptions,
                   UniquePtr<uint8_t[]> aImageBuffer, layers::Image* aImage,
                   imgIEncoder* aEncoder,
                   EncodingCompleteEvent* aEncodingCompleteEvent,
                   int32_t aFormat, const CSSIntSize aSize,
                   bool aUsePlaceholder, bool aUsingCustomOptions)
      : Runnable("EncodingRunnable"),
        mType(aType),
        mOptions(aOptions),
        mImageBuffer(std::move(aImageBuffer)),
        mImage(aImage),
        mEncoder(aEncoder),
        mEncodingCompleteEvent(aEncodingCompleteEvent),
        mFormat(aFormat),
        mSize(aSize),
        mUsePlaceholder(aUsePlaceholder),
        mUsingCustomOptions(aUsingCustomOptions) {}

  nsresult ProcessImageData(uint64_t* aImgSize, void** aImgData) {
    nsCOMPtr<nsIInputStream> stream;
    nsresult rv = ImageEncoder::ExtractDataInternal(
        mType, mOptions, mImageBuffer.get(), mFormat, mSize, mUsePlaceholder,
        mImage, nullptr, nullptr, getter_AddRefs(stream), mEncoder);

    // If there are unrecognized custom parse options, we should fall back to
    // the default values for the encoder without any options at all.
    if (rv == NS_ERROR_INVALID_ARG && mUsingCustomOptions) {
      rv = ImageEncoder::ExtractDataInternal(
          mType, u""_ns, mImageBuffer.get(), mFormat, mSize, mUsePlaceholder,
          mImage, nullptr, nullptr, getter_AddRefs(stream), mEncoder);
    }
    NS_ENSURE_SUCCESS(rv, rv);

    rv = NS_ReadInputStreamToBuffer(stream, aImgData, -1, aImgSize);
    NS_ENSURE_SUCCESS(rv, rv);

    return rv;
  }

  NS_IMETHOD Run() override {
    uint64_t imgSize;
    void* imgData = nullptr;

    nsresult rv = ProcessImageData(&imgSize, &imgData);
    if (NS_FAILED(rv)) {
      mEncodingCompleteEvent->SetFailed();
    } else {
      mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
    }
    rv = mEncodingCompleteEvent->GetCreationThreadEventTarget()->Dispatch(
        mEncodingCompleteEvent, nsIThread::DISPATCH_NORMAL);
    if (NS_FAILED(rv)) {
      if (!mEncodingCompleteEvent->CanBeDeletedOnAnyThread()) {
        // Better to leak than to crash.
        Unused << mEncodingCompleteEvent.forget();
      }
      return rv;
    }

    return rv;
  }

 private:
  nsAutoString mType;
  nsAutoString mOptions;
  UniquePtr<uint8_t[]> mImageBuffer;
  RefPtr<layers::Image> mImage;
  nsCOMPtr<imgIEncoder> mEncoder;
  RefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
  int32_t mFormat;
  const CSSIntSize mSize;
  bool mUsePlaceholder;
  bool mUsingCustomOptions;
};

/* static */
nsresult ImageEncoder::ExtractData(
    nsAString& aType, const nsAString& aOptions, const CSSIntSize aSize,
    bool aUsePlaceholder, nsICanvasRenderingContextInternal* aContext,
    OffscreenCanvasDisplayHelper* aOffscreenDisplay, nsIInputStream** aStream) {
  nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
  if (!encoder) {
    return NS_IMAGELIB_ERROR_NO_ENCODER;
  }

  return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize,
                             aUsePlaceholder, nullptr, aContext,
                             aOffscreenDisplay, aStream, encoder);
}

/* static */
nsresult ImageEncoder::ExtractDataFromLayersImageAsync(
    nsAString& aType, const nsAString& aOptions, bool aUsingCustomOptions,
    layers::Image* aImage, bool aUsePlaceholder,
    EncodeCompleteCallback* aEncodeCallback) {
  nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
  if (!encoder) {
    return NS_IMAGELIB_ERROR_NO_ENCODER;
  }

  RefPtr<EncodingCompleteEvent> completeEvent =
      new EncodingCompleteEvent(aEncodeCallback);

  CSSIntSize size = CSSIntSize::FromUnknownSize(aImage->GetSize());
  nsCOMPtr<nsIRunnable> event =
      new EncodingRunnable(aType, aOptions, nullptr, aImage, encoder,
                           completeEvent, imgIEncoder::INPUT_FORMAT_HOSTARGB,
                           size, aUsePlaceholder, aUsingCustomOptions);
  return NS_DispatchBackgroundTask(event.forget());
}

/* static */
nsresult ImageEncoder::ExtractDataAsync(
    nsAString& aType, const nsAString& aOptions, bool aUsingCustomOptions,
    UniquePtr<uint8_t[]> aImageBuffer, int32_t aFormat, const CSSIntSize aSize,
    bool aUsePlaceholder, EncodeCompleteCallback* aEncodeCallback) {
  nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
  if (!encoder) {
    return NS_IMAGELIB_ERROR_NO_ENCODER;
  }

  RefPtr<EncodingCompleteEvent> completeEvent =
      new EncodingCompleteEvent(aEncodeCallback);

  nsCOMPtr<nsIRunnable> event = new EncodingRunnable(
      aType, aOptions, std::move(aImageBuffer), nullptr, encoder, completeEvent,
      aFormat, aSize, aUsePlaceholder, aUsingCustomOptions);
  return NS_DispatchBackgroundTask(event.forget());
}

/*static*/
nsresult ImageEncoder::GetInputStream(int32_t aWidth, int32_t aHeight,
                                      uint8_t* aImageBuffer, int32_t aFormat,
                                      imgIEncoder* aEncoder,
                                      const nsAString& aEncoderOptions,
                                      nsIInputStream** aStream) {
  nsresult rv =
      aEncoder->InitFromData(aImageBuffer, aWidth * aHeight * 4, aWidth,
                             aHeight, aWidth * 4, aFormat, aEncoderOptions);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<imgIEncoder> encoder(aEncoder);
  encoder.forget(aStream);
  return NS_OK;
}

/* static */
nsresult ImageEncoder::ExtractDataInternal(
    const nsAString& aType, const nsAString& aOptions, uint8_t* aImageBuffer,
    int32_t aFormat, const CSSIntSize aSize, bool aUsePlaceholder,
    layers::Image* aImage, nsICanvasRenderingContextInternal* aContext,
    OffscreenCanvasDisplayHelper* aOffscreenDisplay, nsIInputStream** aStream,
    imgIEncoder* aEncoder) {
  if (aSize.IsEmpty()) {
    return NS_ERROR_INVALID_ARG;
  }

  nsCOMPtr<nsIInputStream> imgStream;

  // get image bytes
  nsresult rv;
  if (aImageBuffer && !aUsePlaceholder) {
    if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) {
      return NS_ERROR_INVALID_ARG;
    }

    rv = ImageEncoder::GetInputStream(aSize.width, aSize.height, aImageBuffer,
                                      aFormat, aEncoder, aOptions,
                                      getter_AddRefs(imgStream));
  } else if (aContext && !aUsePlaceholder) {
    NS_ConvertUTF16toUTF8 encoderType(aType);
    rv = aContext->GetInputStream(encoderType.get(), aOptions,
                                  getter_AddRefs(imgStream));
  } else if (aOffscreenDisplay && !aUsePlaceholder) {
    const NS_ConvertUTF16toUTF8 encoderType(aType);
    if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) {
      return NS_ERROR_INVALID_ARG;
    }

    const RefPtr<SourceSurface> snapshot =
        aOffscreenDisplay->GetSurfaceSnapshot();
    if (!snapshot) {
      return NS_ERROR_OUT_OF_MEMORY;
    }

    const RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
    if (!data) {
      return NS_ERROR_OUT_OF_MEMORY;
    }

    {
      DataSourceSurface::MappedSurface map;
      if (!data->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
        return NS_ERROR_INVALID_ARG;
      }
      auto size = data->GetSize();
      rv = aEncoder->InitFromData(map.mData, size.width * size.height * 4,
                                  size.width, size.height, size.width * 4,
                                  imgIEncoder::INPUT_FORMAT_HOSTARGB, aOptions);
      data->Unmap();
    }
    if (NS_SUCCEEDED(rv)) {
      imgStream = aEncoder;
    }
  } else if (aImage && !aUsePlaceholder) {
    // It is safe to convert PlanarYCbCr format from YUV to RGB off-main-thread.
    // Other image formats could have problem to convert format off-main-thread.
    // So here it uses a help function GetBRGADataSourceSurfaceSync() to convert
    // format on main thread.
    if (aImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
      nsTArray<uint8_t> data;
      layers::PlanarYCbCrImage* ycbcrImage =
          static_cast<layers::PlanarYCbCrImage*>(aImage);
      gfxImageFormat format = SurfaceFormat::A8R8G8B8_UINT32;
      int32_t stride = GetAlignedStride<16>(aSize.width, 4);
      size_t length = BufferSizeFromStrideAndHeight(stride, aSize.height);
      if (length == 0) {
        return NS_ERROR_INVALID_ARG;
      }
      data.SetCapacity(length);

      rv = ConvertYCbCrToRGB(*ycbcrImage->GetData(), format,
                             aSize.ToUnknownSize(), data.Elements(), stride);
      if (NS_FAILED(rv)) {
        MOZ_ASSERT_UNREACHABLE("Failed to convert YUV into RGB data");
        return rv;
      }

      rv = aEncoder->InitFromData(data.Elements(),
                                  aSize.width * aSize.height * 4, aSize.width,
                                  aSize.height, aSize.width * 4,
                                  imgIEncoder::INPUT_FORMAT_HOSTARGB, aOptions);
    } else {
      if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) {
        return NS_ERROR_INVALID_ARG;
      }

      RefPtr<gfx::DataSourceSurface> dataSurface;
      RefPtr<layers::Image> image(aImage);
      dataSurface = GetBRGADataSourceSurfaceSync(image.forget());

      DataSourceSurface::MappedSurface map;
      if (!dataSurface->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
        return NS_ERROR_INVALID_ARG;
      }
      auto size = dataSurface->GetSize();
      rv = aEncoder->InitFromData(map.mData, size.width * size.height * 4,
                                  size.width, size.height, size.width * 4,
                                  imgIEncoder::INPUT_FORMAT_HOSTARGB, aOptions);
      dataSurface->Unmap();
    }

    if (NS_SUCCEEDED(rv)) {
      imgStream = aEncoder;
    }
  } else {
    if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) {
      return NS_ERROR_INVALID_ARG;
    }

    // no context, so we have to encode an empty image
    // note that if we didn't have a current context, the spec says we're
    // supposed to just return transparent black pixels of the canvas
    // dimensions.
    RefPtr<DataSourceSurface> emptyCanvas =
        Factory::CreateDataSourceSurfaceWithStride(
            IntSize(aSize.width, aSize.height), SurfaceFormat::B8G8R8A8,
            4 * aSize.width, true);
    if (NS_WARN_IF(!emptyCanvas)) {
      return NS_ERROR_INVALID_ARG;
    }

    DataSourceSurface::MappedSurface map;
    if (!emptyCanvas->Map(DataSourceSurface::MapType::WRITE, &map)) {
      return NS_ERROR_INVALID_ARG;
    }
    if (aUsePlaceholder) {
      auto size = 4 * aSize.width * aSize.height;
      auto* data = map.mData;
      GeneratePlaceholderCanvasData(size, data);
    }
    rv = aEncoder->InitFromData(map.mData, aSize.width * aSize.height * 4,
                                aSize.width, aSize.height, aSize.width * 4,
                                imgIEncoder::INPUT_FORMAT_HOSTARGB, aOptions);
    emptyCanvas->Unmap();
    if (NS_SUCCEEDED(rv)) {
      imgStream = aEncoder;
    }
  }
  NS_ENSURE_SUCCESS(rv, rv);

  imgStream.forget(aStream);
  return rv;
}

/* static */
already_AddRefed<imgIEncoder> ImageEncoder::GetImageEncoder(nsAString& aType) {
  // Get an image encoder for the media type.
  nsCString encoderCID("@mozilla.org/image/encoder;2?type=");
  NS_ConvertUTF16toUTF8 encoderType(aType);
  encoderCID += encoderType;
  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());

  if (!encoder && aType != u"image/png"_ns) {
    // Unable to create an encoder instance of the specified type. Falling back
    // to PNG.
    aType.AssignLiteral("image/png");
    nsCString PNGEncoderCID("@mozilla.org/image/encoder;2?type=image/png");
    encoder = do_CreateInstance(PNGEncoderCID.get());
  }

  return encoder.forget();
}

}  // namespace mozilla::dom

100%


¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.