summaryrefslogtreecommitdiff
path: root/image/imgTools.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'image/imgTools.cpp')
-rw-r--r--image/imgTools.cpp356
1 files changed, 356 insertions, 0 deletions
diff --git a/image/imgTools.cpp b/image/imgTools.cpp
new file mode 100644
index 0000000000..29905c1ab3
--- /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