diff options
Diffstat (limited to 'image/imgTools.cpp')
-rw-r--r-- | image/imgTools.cpp | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/image/imgTools.cpp b/image/imgTools.cpp new file mode 100644 index 000000000..29905c1ab --- /dev/null +++ b/image/imgTools.cpp @@ -0,0 +1,356 @@ +/* -*- 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 |