1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/src/imgTools.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,316 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "imgTools.h" 1.11 + 1.12 +#include "gfxUtils.h" 1.13 +#include "mozilla/gfx/2D.h" 1.14 +#include "mozilla/RefPtr.h" 1.15 +#include "nsCOMPtr.h" 1.16 +#include "nsIDocument.h" 1.17 +#include "nsIDOMDocument.h" 1.18 +#include "nsError.h" 1.19 +#include "imgLoader.h" 1.20 +#include "imgICache.h" 1.21 +#include "imgIContainer.h" 1.22 +#include "imgIEncoder.h" 1.23 +#include "nsStreamUtils.h" 1.24 +#include "nsContentUtils.h" 1.25 +#include "ImageFactory.h" 1.26 +#include "Image.h" 1.27 +#include "ScriptedNotificationObserver.h" 1.28 +#include "imgIScriptedNotificationObserver.h" 1.29 +#include "gfxPlatform.h" 1.30 + 1.31 +using namespace mozilla; 1.32 +using namespace mozilla::image; 1.33 +using namespace mozilla::gfx; 1.34 + 1.35 +/* ========== imgITools implementation ========== */ 1.36 + 1.37 + 1.38 + 1.39 +NS_IMPL_ISUPPORTS(imgTools, imgITools) 1.40 + 1.41 +imgTools::imgTools() 1.42 +{ 1.43 + /* member initializers and constructor code */ 1.44 +} 1.45 + 1.46 +imgTools::~imgTools() 1.47 +{ 1.48 + /* destructor code */ 1.49 +} 1.50 + 1.51 +NS_IMETHODIMP imgTools::DecodeImageData(nsIInputStream* aInStr, 1.52 + const nsACString& aMimeType, 1.53 + imgIContainer **aContainer) 1.54 +{ 1.55 + NS_ABORT_IF_FALSE(*aContainer == nullptr, 1.56 + "Cannot provide an existing image container to DecodeImageData"); 1.57 + 1.58 + return DecodeImage(aInStr, aMimeType, aContainer); 1.59 +} 1.60 + 1.61 +NS_IMETHODIMP imgTools::DecodeImage(nsIInputStream* aInStr, 1.62 + const nsACString& aMimeType, 1.63 + imgIContainer **aContainer) 1.64 +{ 1.65 + nsresult rv; 1.66 + nsRefPtr<Image> image; 1.67 + 1.68 + NS_ENSURE_ARG_POINTER(aInStr); 1.69 + 1.70 + // Create a new image container to hold the decoded data. 1.71 + nsAutoCString mimeType(aMimeType); 1.72 + image = ImageFactory::CreateAnonymousImage(mimeType); 1.73 + 1.74 + if (image->HasError()) 1.75 + return NS_ERROR_FAILURE; 1.76 + 1.77 + // Prepare the input stream. 1.78 + nsCOMPtr<nsIInputStream> inStream = aInStr; 1.79 + if (!NS_InputStreamIsBuffered(aInStr)) { 1.80 + nsCOMPtr<nsIInputStream> bufStream; 1.81 + rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), aInStr, 1024); 1.82 + if (NS_SUCCEEDED(rv)) 1.83 + inStream = bufStream; 1.84 + } 1.85 + 1.86 + // Figure out how much data we've been passed. 1.87 + uint64_t length; 1.88 + rv = inStream->Available(&length); 1.89 + NS_ENSURE_SUCCESS(rv, rv); 1.90 + NS_ENSURE_TRUE(length <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG); 1.91 + 1.92 + // Send the source data to the Image. 1.93 + rv = image->OnImageDataAvailable(nullptr, nullptr, inStream, 0, uint32_t(length)); 1.94 + NS_ENSURE_SUCCESS(rv, rv); 1.95 + // Let the Image know we've sent all the data. 1.96 + rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true); 1.97 + NS_ENSURE_SUCCESS(rv, rv); 1.98 + 1.99 + // All done. 1.100 + NS_ADDREF(*aContainer = image.get()); 1.101 + return NS_OK; 1.102 +} 1.103 + 1.104 +/** 1.105 + * This takes a DataSourceSurface rather than a SourceSurface because some 1.106 + * of the callers have a DataSourceSurface and we don't want to call 1.107 + * GetDataSurface on such surfaces since that may incure a conversion to 1.108 + * SurfaceType::DATA which we don't need. 1.109 + */ 1.110 +static nsresult EncodeImageData(DataSourceSurface* aDataSurface, 1.111 + const nsACString& aMimeType, 1.112 + const nsAString& aOutputOptions, 1.113 + nsIInputStream **aStream) 1.114 +{ 1.115 + MOZ_ASSERT(aDataSurface->GetFormat() == SurfaceFormat::B8G8R8A8, 1.116 + "We're assuming B8G8R8A8"); 1.117 + 1.118 + // Get an image encoder for the media type 1.119 + nsAutoCString encoderCID( 1.120 + NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType); 1.121 + 1.122 + nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get()); 1.123 + if (!encoder) 1.124 + return NS_IMAGELIB_ERROR_NO_ENCODER; 1.125 + 1.126 + DataSourceSurface::MappedSurface map; 1.127 + if (!aDataSurface->Map(DataSourceSurface::MapType::READ, &map)) { 1.128 + return NS_ERROR_FAILURE; 1.129 + } 1.130 + 1.131 + IntSize size = aDataSurface->GetSize(); 1.132 + uint32_t dataLength = map.mStride * size.height; 1.133 + 1.134 + // Encode the bitmap 1.135 + nsresult rv = encoder->InitFromData(map.mData, 1.136 + dataLength, 1.137 + size.width, 1.138 + size.height, 1.139 + map.mStride, 1.140 + imgIEncoder::INPUT_FORMAT_HOSTARGB, 1.141 + aOutputOptions); 1.142 + aDataSurface->Unmap(); 1.143 + NS_ENSURE_SUCCESS(rv, rv); 1.144 + 1.145 + return CallQueryInterface(encoder, aStream); 1.146 +} 1.147 + 1.148 +NS_IMETHODIMP imgTools::EncodeImage(imgIContainer *aContainer, 1.149 + const nsACString& aMimeType, 1.150 + const nsAString& aOutputOptions, 1.151 + nsIInputStream **aStream) 1.152 +{ 1.153 + // Use frame 0 from the image container. 1.154 + RefPtr<SourceSurface> frame = 1.155 + aContainer->GetFrame(imgIContainer::FRAME_FIRST, 1.156 + imgIContainer::FLAG_SYNC_DECODE); 1.157 + NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); 1.158 + 1.159 + RefPtr<DataSourceSurface> dataSurface; 1.160 + 1.161 + if (frame->GetFormat() == SurfaceFormat::B8G8R8A8) { 1.162 + dataSurface = frame->GetDataSurface(); 1.163 + } else { 1.164 + // Convert format to SurfaceFormat::B8G8R8A8 1.165 + dataSurface = gfxUtils:: 1.166 + CopySurfaceToDataSourceSurfaceWithFormat(frame, 1.167 + SurfaceFormat::B8G8R8A8); 1.168 + } 1.169 + 1.170 + NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE); 1.171 + 1.172 + return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream); 1.173 +} 1.174 + 1.175 +NS_IMETHODIMP imgTools::EncodeScaledImage(imgIContainer *aContainer, 1.176 + const nsACString& aMimeType, 1.177 + int32_t aScaledWidth, 1.178 + int32_t aScaledHeight, 1.179 + const nsAString& aOutputOptions, 1.180 + nsIInputStream **aStream) 1.181 +{ 1.182 + NS_ENSURE_ARG(aScaledWidth >= 0 && aScaledHeight >= 0); 1.183 + 1.184 + // If no scaled size is specified, we'll just encode the image at its 1.185 + // original size (no scaling). 1.186 + if (aScaledWidth == 0 && aScaledHeight == 0) { 1.187 + return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream); 1.188 + } 1.189 + 1.190 + // Use frame 0 from the image container. 1.191 + RefPtr<SourceSurface> frame = 1.192 + aContainer->GetFrame(imgIContainer::FRAME_FIRST, 1.193 + imgIContainer::FLAG_SYNC_DECODE); 1.194 + NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); 1.195 + 1.196 + int32_t frameWidth = frame->GetSize().width; 1.197 + int32_t frameHeight = frame->GetSize().height; 1.198 + 1.199 + // If the given width or height is zero we'll replace it with the image's 1.200 + // original dimensions. 1.201 + if (aScaledWidth == 0) { 1.202 + aScaledWidth = frameWidth; 1.203 + } else if (aScaledHeight == 0) { 1.204 + aScaledHeight = frameHeight; 1.205 + } 1.206 + 1.207 + RefPtr<DataSourceSurface> dataSurface = 1.208 + Factory::CreateDataSourceSurface(IntSize(aScaledWidth, aScaledHeight), 1.209 + SurfaceFormat::B8G8R8A8); 1.210 + DataSourceSurface::MappedSurface map; 1.211 + if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) { 1.212 + return NS_ERROR_FAILURE; 1.213 + } 1.214 + 1.215 + RefPtr<DrawTarget> dt = 1.216 + Factory::CreateDrawTargetForData(BackendType::CAIRO, 1.217 + map.mData, 1.218 + dataSurface->GetSize(), 1.219 + map.mStride, 1.220 + SurfaceFormat::B8G8R8A8); 1.221 + dt->DrawSurface(frame, 1.222 + Rect(0, 0, aScaledWidth, aScaledHeight), 1.223 + Rect(0, 0, frameWidth, frameHeight), 1.224 + DrawSurfaceOptions(), 1.225 + DrawOptions(1.0f, CompositionOp::OP_SOURCE)); 1.226 + 1.227 + dataSurface->Unmap(); 1.228 + 1.229 + return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream); 1.230 +} 1.231 + 1.232 +NS_IMETHODIMP imgTools::EncodeCroppedImage(imgIContainer *aContainer, 1.233 + const nsACString& aMimeType, 1.234 + int32_t aOffsetX, 1.235 + int32_t aOffsetY, 1.236 + int32_t aWidth, 1.237 + int32_t aHeight, 1.238 + const nsAString& aOutputOptions, 1.239 + nsIInputStream **aStream) 1.240 +{ 1.241 + NS_ENSURE_ARG(aOffsetX >= 0 && aOffsetY >= 0 && aWidth >= 0 && aHeight >= 0); 1.242 + 1.243 + // Offsets must be zero when no width and height are given or else we're out 1.244 + // of bounds. 1.245 + NS_ENSURE_ARG(aWidth + aHeight > 0 || aOffsetX + aOffsetY == 0); 1.246 + 1.247 + // If no size is specified then we'll preserve the image's original dimensions 1.248 + // and don't need to crop. 1.249 + if (aWidth == 0 && aHeight == 0) { 1.250 + return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream); 1.251 + } 1.252 + 1.253 + // Use frame 0 from the image container. 1.254 + RefPtr<SourceSurface> frame = 1.255 + aContainer->GetFrame(imgIContainer::FRAME_FIRST, 1.256 + imgIContainer::FLAG_SYNC_DECODE); 1.257 + NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); 1.258 + 1.259 + int32_t frameWidth = frame->GetSize().width; 1.260 + int32_t frameHeight = frame->GetSize().height; 1.261 + 1.262 + // If the given width or height is zero we'll replace it with the image's 1.263 + // original dimensions. 1.264 + if (aWidth == 0) { 1.265 + aWidth = frameWidth; 1.266 + } else if (aHeight == 0) { 1.267 + aHeight = frameHeight; 1.268 + } 1.269 + 1.270 + // Check that the given crop rectangle is within image bounds. 1.271 + NS_ENSURE_ARG(frameWidth >= aOffsetX + aWidth && 1.272 + frameHeight >= aOffsetY + aHeight); 1.273 + 1.274 + RefPtr<DataSourceSurface> dataSurface = 1.275 + Factory::CreateDataSourceSurface(IntSize(aWidth, aHeight), 1.276 + SurfaceFormat::B8G8R8A8); 1.277 + DataSourceSurface::MappedSurface map; 1.278 + if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) { 1.279 + return NS_ERROR_FAILURE; 1.280 + } 1.281 + 1.282 + RefPtr<DrawTarget> dt = 1.283 + Factory::CreateDrawTargetForData(BackendType::CAIRO, 1.284 + map.mData, 1.285 + dataSurface->GetSize(), 1.286 + map.mStride, 1.287 + SurfaceFormat::B8G8R8A8); 1.288 + dt->CopySurface(frame, 1.289 + IntRect(aOffsetX, aOffsetY, aWidth, aHeight), 1.290 + IntPoint(0, 0)); 1.291 + 1.292 + dataSurface->Unmap(); 1.293 + 1.294 + return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream); 1.295 +} 1.296 + 1.297 +NS_IMETHODIMP imgTools::CreateScriptedObserver(imgIScriptedNotificationObserver* aInner, 1.298 + imgINotificationObserver** aObserver) 1.299 +{ 1.300 + NS_ADDREF(*aObserver = new ScriptedNotificationObserver(aInner)); 1.301 + return NS_OK; 1.302 +} 1.303 + 1.304 +NS_IMETHODIMP 1.305 +imgTools::GetImgLoaderForDocument(nsIDOMDocument* aDoc, imgILoader** aLoader) 1.306 +{ 1.307 + nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc); 1.308 + NS_IF_ADDREF(*aLoader = nsContentUtils::GetImgLoaderForDocument(doc)); 1.309 + return NS_OK; 1.310 +} 1.311 + 1.312 +NS_IMETHODIMP 1.313 +imgTools::GetImgCacheForDocument(nsIDOMDocument* aDoc, imgICache** aCache) 1.314 +{ 1.315 + nsCOMPtr<imgILoader> loader; 1.316 + nsresult rv = GetImgLoaderForDocument(aDoc, getter_AddRefs(loader)); 1.317 + NS_ENSURE_SUCCESS(rv, rv); 1.318 + return CallQueryInterface(loader, aCache); 1.319 +}