diff -r 000000000000 -r 6474c204b198 image/src/imgTools.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/image/src/imgTools.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,316 @@ +/* -*- 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; +using namespace mozilla::image; +using namespace mozilla::gfx; + +/* ========== 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) +{ + NS_ABORT_IF_FALSE(*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) +{ + nsresult rv; + nsRefPtr image; + + NS_ENSURE_ARG_POINTER(aInStr); + + // Create a new image container to hold the decoded data. + nsAutoCString mimeType(aMimeType); + image = ImageFactory::CreateAnonymousImage(mimeType); + + if (image->HasError()) + return NS_ERROR_FAILURE; + + // Prepare the input stream. + nsCOMPtr inStream = aInStr; + if (!NS_InputStreamIsBuffered(aInStr)) { + nsCOMPtr 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); + 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 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); + + return CallQueryInterface(encoder, aStream); +} + +NS_IMETHODIMP imgTools::EncodeImage(imgIContainer *aContainer, + const nsACString& aMimeType, + const nsAString& aOutputOptions, + nsIInputStream **aStream) +{ + // Use frame 0 from the image container. + RefPtr frame = + aContainer->GetFrame(imgIContainer::FRAME_FIRST, + imgIContainer::FLAG_SYNC_DECODE); + NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); + + RefPtr 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); + } + + // Use frame 0 from the image container. + RefPtr 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 (aScaledWidth == 0) { + aScaledWidth = frameWidth; + } else if (aScaledHeight == 0) { + aScaledHeight = frameHeight; + } + + RefPtr dataSurface = + Factory::CreateDataSourceSurface(IntSize(aScaledWidth, aScaledHeight), + SurfaceFormat::B8G8R8A8); + DataSourceSurface::MappedSurface map; + if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) { + return NS_ERROR_FAILURE; + } + + RefPtr dt = + Factory::CreateDrawTargetForData(BackendType::CAIRO, + map.mData, + dataSurface->GetSize(), + map.mStride, + SurfaceFormat::B8G8R8A8); + dt->DrawSurface(frame, + Rect(0, 0, aScaledWidth, aScaledHeight), + Rect(0, 0, frameWidth, frameHeight), + 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 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 dataSurface = + Factory::CreateDataSourceSurface(IntSize(aWidth, aHeight), + SurfaceFormat::B8G8R8A8); + DataSourceSurface::MappedSurface map; + if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) { + return NS_ERROR_FAILURE; + } + + RefPtr dt = + Factory::CreateDrawTargetForData(BackendType::CAIRO, + map.mData, + dataSurface->GetSize(), + map.mStride, + SurfaceFormat::B8G8R8A8); + 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 doc = do_QueryInterface(aDoc); + NS_IF_ADDREF(*aLoader = nsContentUtils::GetImgLoaderForDocument(doc)); + return NS_OK; +} + +NS_IMETHODIMP +imgTools::GetImgCacheForDocument(nsIDOMDocument* aDoc, imgICache** aCache) +{ + nsCOMPtr loader; + nsresult rv = GetImgLoaderForDocument(aDoc, getter_AddRefs(loader)); + NS_ENSURE_SUCCESS(rv, rv); + return CallQueryInterface(loader, aCache); +}