123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * 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 "imgTools.h"
- #include "gfxUtils.h"
- #include "mozilla/gfx/2D.h"
- #include "mozilla/RefPtr.h"
- #include "nsCOMPtr.h"
- #include "nsIDocument.h"
- #include "nsIDOMDocument.h"
- #include "nsError.h"
- #include "imgLoader.h"
- #include "imgICache.h"
- #include "imgIContainer.h"
- #include "imgIEncoder.h"
- #include "nsStreamUtils.h"
- #include "nsContentUtils.h"
- #include "ImageFactory.h"
- #include "Image.h"
- #include "ScriptedNotificationObserver.h"
- #include "imgIScriptedNotificationObserver.h"
- #include "gfxPlatform.h"
- using namespace mozilla::gfx;
- namespace mozilla {
- namespace image {
- /* ========== imgITools implementation ========== */
- NS_IMPL_ISUPPORTS(imgTools, imgITools)
- imgTools::imgTools()
- {
- /* member initializers and constructor code */
- }
- imgTools::~imgTools()
- {
- /* destructor code */
- }
- NS_IMETHODIMP
- imgTools::DecodeImageData(nsIInputStream* aInStr,
- const nsACString& aMimeType,
- imgIContainer** aContainer)
- {
- MOZ_ASSERT(*aContainer == nullptr,
- "Cannot provide an existing image container to DecodeImageData");
- return DecodeImage(aInStr, aMimeType, aContainer);
- }
- NS_IMETHODIMP
- imgTools::DecodeImage(nsIInputStream* aInStr,
- const nsACString& aMimeType,
- imgIContainer** aContainer)
- {
- MOZ_ASSERT(NS_IsMainThread());
- nsresult rv;
- NS_ENSURE_ARG_POINTER(aInStr);
- // Create a new image container to hold the decoded data.
- nsAutoCString mimeType(aMimeType);
- RefPtr<image::Image> image = ImageFactory::CreateAnonymousImage(mimeType);
- RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
- if (image->HasError()) {
- return NS_ERROR_FAILURE;
- }
- // Prepare the input stream.
- nsCOMPtr<nsIInputStream> inStream = aInStr;
- if (!NS_InputStreamIsBuffered(aInStr)) {
- nsCOMPtr<nsIInputStream> bufStream;
- rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), aInStr, 1024);
- if (NS_SUCCEEDED(rv)) {
- inStream = bufStream;
- }
- }
- // Figure out how much data we've been passed.
- uint64_t length;
- rv = inStream->Available(&length);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(length <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
- // Send the source data to the Image.
- rv = image->OnImageDataAvailable(nullptr, nullptr, inStream, 0,
- uint32_t(length));
- NS_ENSURE_SUCCESS(rv, rv);
- // Let the Image know we've sent all the data.
- rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
- tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
- NS_ENSURE_SUCCESS(rv, rv);
- // All done.
- NS_ADDREF(*aContainer = image.get());
- return NS_OK;
- }
- /**
- * This takes a DataSourceSurface rather than a SourceSurface because some
- * of the callers have a DataSourceSurface and we don't want to call
- * GetDataSurface on such surfaces since that may incure a conversion to
- * SurfaceType::DATA which we don't need.
- */
- static nsresult
- EncodeImageData(DataSourceSurface* aDataSurface,
- const nsACString& aMimeType,
- const nsAString& aOutputOptions,
- nsIInputStream** aStream)
- {
- MOZ_ASSERT(aDataSurface->GetFormat() == SurfaceFormat::B8G8R8A8,
- "We're assuming B8G8R8A8");
- // Get an image encoder for the media type
- nsAutoCString encoderCID(
- NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType);
- nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
- if (!encoder) {
- return NS_IMAGELIB_ERROR_NO_ENCODER;
- }
- DataSourceSurface::MappedSurface map;
- if (!aDataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
- return NS_ERROR_FAILURE;
- }
- IntSize size = aDataSurface->GetSize();
- uint32_t dataLength = map.mStride * size.height;
- // Encode the bitmap
- nsresult rv = encoder->InitFromData(map.mData,
- dataLength,
- size.width,
- size.height,
- map.mStride,
- imgIEncoder::INPUT_FORMAT_HOSTARGB,
- aOutputOptions);
- aDataSurface->Unmap();
- NS_ENSURE_SUCCESS(rv, rv);
- encoder.forget(aStream);
- return NS_OK;
- }
- NS_IMETHODIMP
- imgTools::EncodeImage(imgIContainer* aContainer,
- const nsACString& aMimeType,
- const nsAString& aOutputOptions,
- nsIInputStream** aStream)
- {
- // Use frame 0 from the image container.
- RefPtr<SourceSurface> frame =
- aContainer->GetFrame(imgIContainer::FRAME_FIRST,
- imgIContainer::FLAG_SYNC_DECODE);
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
- RefPtr<DataSourceSurface> dataSurface;
- if (frame->GetFormat() == SurfaceFormat::B8G8R8A8) {
- dataSurface = frame->GetDataSurface();
- } else {
- // Convert format to SurfaceFormat::B8G8R8A8
- dataSurface = gfxUtils::
- CopySurfaceToDataSourceSurfaceWithFormat(frame,
- SurfaceFormat::B8G8R8A8);
- }
- NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
- return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
- }
- NS_IMETHODIMP
- imgTools::EncodeScaledImage(imgIContainer* aContainer,
- const nsACString& aMimeType,
- int32_t aScaledWidth,
- int32_t aScaledHeight,
- const nsAString& aOutputOptions,
- nsIInputStream** aStream)
- {
- NS_ENSURE_ARG(aScaledWidth >= 0 && aScaledHeight >= 0);
- // If no scaled size is specified, we'll just encode the image at its
- // original size (no scaling).
- if (aScaledWidth == 0 && aScaledHeight == 0) {
- return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
- }
- // Retrieve the image's size.
- int32_t imageWidth = 0;
- int32_t imageHeight = 0;
- aContainer->GetWidth(&imageWidth);
- aContainer->GetHeight(&imageHeight);
- // If the given width or height is zero we'll replace it with the image's
- // original dimensions.
- IntSize scaledSize(aScaledWidth == 0 ? imageWidth : aScaledWidth,
- aScaledHeight == 0 ? imageHeight : aScaledHeight);
- // Use frame 0 from the image container.
- RefPtr<SourceSurface> frame =
- aContainer->GetFrameAtSize(scaledSize,
- imgIContainer::FRAME_FIRST,
- imgIContainer::FLAG_HIGH_QUALITY_SCALING |
- imgIContainer::FLAG_SYNC_DECODE);
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
- RefPtr<DataSourceSurface> dataSurface =
- Factory::CreateDataSourceSurface(scaledSize, SurfaceFormat::B8G8R8A8);
- if (NS_WARN_IF(!dataSurface)) {
- return NS_ERROR_FAILURE;
- }
- DataSourceSurface::MappedSurface map;
- if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
- return NS_ERROR_FAILURE;
- }
- RefPtr<DrawTarget> dt =
- Factory::CreateDrawTargetForData(BackendType::CAIRO,
- map.mData,
- dataSurface->GetSize(),
- map.mStride,
- SurfaceFormat::B8G8R8A8);
- if (!dt) {
- gfxWarning() << "imgTools::EncodeImage failed in CreateDrawTargetForData";
- return NS_ERROR_OUT_OF_MEMORY;
- }
- IntSize frameSize = frame->GetSize();
- dt->DrawSurface(frame,
- Rect(0, 0, scaledSize.width, scaledSize.height),
- Rect(0, 0, frameSize.width, frameSize.height),
- DrawSurfaceOptions(),
- DrawOptions(1.0f, CompositionOp::OP_SOURCE));
- dataSurface->Unmap();
- return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
- }
- NS_IMETHODIMP
- imgTools::EncodeCroppedImage(imgIContainer* aContainer,
- const nsACString& aMimeType,
- int32_t aOffsetX,
- int32_t aOffsetY,
- int32_t aWidth,
- int32_t aHeight,
- const nsAString& aOutputOptions,
- nsIInputStream** aStream)
- {
- NS_ENSURE_ARG(aOffsetX >= 0 && aOffsetY >= 0 && aWidth >= 0 && aHeight >= 0);
- // Offsets must be zero when no width and height are given or else we're out
- // of bounds.
- NS_ENSURE_ARG(aWidth + aHeight > 0 || aOffsetX + aOffsetY == 0);
- // If no size is specified then we'll preserve the image's original dimensions
- // and don't need to crop.
- if (aWidth == 0 && aHeight == 0) {
- return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
- }
- // Use frame 0 from the image container.
- RefPtr<SourceSurface> frame =
- aContainer->GetFrame(imgIContainer::FRAME_FIRST,
- imgIContainer::FLAG_SYNC_DECODE);
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
- int32_t frameWidth = frame->GetSize().width;
- int32_t frameHeight = frame->GetSize().height;
- // If the given width or height is zero we'll replace it with the image's
- // original dimensions.
- if (aWidth == 0) {
- aWidth = frameWidth;
- } else if (aHeight == 0) {
- aHeight = frameHeight;
- }
- // Check that the given crop rectangle is within image bounds.
- NS_ENSURE_ARG(frameWidth >= aOffsetX + aWidth &&
- frameHeight >= aOffsetY + aHeight);
- RefPtr<DataSourceSurface> dataSurface =
- Factory::CreateDataSourceSurface(IntSize(aWidth, aHeight),
- SurfaceFormat::B8G8R8A8,
- /* aZero = */ true);
- if (NS_WARN_IF(!dataSurface)) {
- return NS_ERROR_FAILURE;
- }
- DataSourceSurface::MappedSurface map;
- if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
- return NS_ERROR_FAILURE;
- }
- RefPtr<DrawTarget> dt =
- Factory::CreateDrawTargetForData(BackendType::CAIRO,
- map.mData,
- dataSurface->GetSize(),
- map.mStride,
- SurfaceFormat::B8G8R8A8);
- if (!dt) {
- gfxWarning() <<
- "imgTools::EncodeCroppedImage failed in CreateDrawTargetForData";
- return NS_ERROR_OUT_OF_MEMORY;
- }
- dt->CopySurface(frame,
- IntRect(aOffsetX, aOffsetY, aWidth, aHeight),
- IntPoint(0, 0));
- dataSurface->Unmap();
- return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
- }
- NS_IMETHODIMP
- imgTools::CreateScriptedObserver(imgIScriptedNotificationObserver* aInner,
- imgINotificationObserver** aObserver)
- {
- NS_ADDREF(*aObserver = new ScriptedNotificationObserver(aInner));
- return NS_OK;
- }
- NS_IMETHODIMP
- imgTools::GetImgLoaderForDocument(nsIDOMDocument* aDoc, imgILoader** aLoader)
- {
- nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
- NS_IF_ADDREF(*aLoader = nsContentUtils::GetImgLoaderForDocument(doc));
- return NS_OK;
- }
- NS_IMETHODIMP
- imgTools::GetImgCacheForDocument(nsIDOMDocument* aDoc, imgICache** aCache)
- {
- nsCOMPtr<imgILoader> loader;
- nsresult rv = GetImgLoaderForDocument(aDoc, getter_AddRefs(loader));
- NS_ENSURE_SUCCESS(rv, rv);
- return CallQueryInterface(loader, aCache);
- }
- } // namespace image
- } // namespace mozilla
|