merge mozilla-aurora to b2g44 a=merge
[gecko.git] / image / imgTools.cpp
blob29905c1ab36ce9d5b9abf6741de36ee41b8592a4
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "imgTools.h"
9 #include "gfxUtils.h"
10 #include "mozilla/gfx/2D.h"
11 #include "mozilla/RefPtr.h"
12 #include "nsCOMPtr.h"
13 #include "nsIDocument.h"
14 #include "nsIDOMDocument.h"
15 #include "nsError.h"
16 #include "imgLoader.h"
17 #include "imgICache.h"
18 #include "imgIContainer.h"
19 #include "imgIEncoder.h"
20 #include "nsStreamUtils.h"
21 #include "nsContentUtils.h"
22 #include "ImageFactory.h"
23 #include "Image.h"
24 #include "ScriptedNotificationObserver.h"
25 #include "imgIScriptedNotificationObserver.h"
26 #include "gfxPlatform.h"
28 using namespace mozilla::gfx;
30 namespace mozilla {
31 namespace image {
32 /* ========== imgITools implementation ========== */
36 NS_IMPL_ISUPPORTS(imgTools, imgITools)
38 imgTools::imgTools()
40 /* member initializers and constructor code */
43 imgTools::~imgTools()
45 /* destructor code */
48 NS_IMETHODIMP
49 imgTools::DecodeImageData(nsIInputStream* aInStr,
50 const nsACString& aMimeType,
51 imgIContainer** aContainer)
53 MOZ_ASSERT(*aContainer == nullptr,
54 "Cannot provide an existing image container to DecodeImageData");
56 return DecodeImage(aInStr, aMimeType, aContainer);
59 NS_IMETHODIMP
60 imgTools::DecodeImage(nsIInputStream* aInStr,
61 const nsACString& aMimeType,
62 imgIContainer** aContainer)
64 MOZ_ASSERT(NS_IsMainThread());
66 nsresult rv;
68 NS_ENSURE_ARG_POINTER(aInStr);
70 // Create a new image container to hold the decoded data.
71 nsAutoCString mimeType(aMimeType);
72 RefPtr<image::Image> image = ImageFactory::CreateAnonymousImage(mimeType);
73 RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
75 if (image->HasError()) {
76 return NS_ERROR_FAILURE;
79 // Prepare the input stream.
80 nsCOMPtr<nsIInputStream> inStream = aInStr;
81 if (!NS_InputStreamIsBuffered(aInStr)) {
82 nsCOMPtr<nsIInputStream> bufStream;
83 rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), aInStr, 1024);
84 if (NS_SUCCEEDED(rv)) {
85 inStream = bufStream;
89 // Figure out how much data we've been passed.
90 uint64_t length;
91 rv = inStream->Available(&length);
92 NS_ENSURE_SUCCESS(rv, rv);
93 NS_ENSURE_TRUE(length <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
95 // Send the source data to the Image.
96 rv = image->OnImageDataAvailable(nullptr, nullptr, inStream, 0,
97 uint32_t(length));
98 NS_ENSURE_SUCCESS(rv, rv);
100 // Let the Image know we've sent all the data.
101 rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
102 tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
103 NS_ENSURE_SUCCESS(rv, rv);
105 // All done.
106 NS_ADDREF(*aContainer = image.get());
107 return NS_OK;
111 * This takes a DataSourceSurface rather than a SourceSurface because some
112 * of the callers have a DataSourceSurface and we don't want to call
113 * GetDataSurface on such surfaces since that may incure a conversion to
114 * SurfaceType::DATA which we don't need.
116 static nsresult
117 EncodeImageData(DataSourceSurface* aDataSurface,
118 const nsACString& aMimeType,
119 const nsAString& aOutputOptions,
120 nsIInputStream** aStream)
122 MOZ_ASSERT(aDataSurface->GetFormat() == SurfaceFormat::B8G8R8A8,
123 "We're assuming B8G8R8A8");
125 // Get an image encoder for the media type
126 nsAutoCString encoderCID(
127 NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType);
129 nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
130 if (!encoder) {
131 return NS_IMAGELIB_ERROR_NO_ENCODER;
134 DataSourceSurface::MappedSurface map;
135 if (!aDataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
136 return NS_ERROR_FAILURE;
139 IntSize size = aDataSurface->GetSize();
140 uint32_t dataLength = map.mStride * size.height;
142 // Encode the bitmap
143 nsresult rv = encoder->InitFromData(map.mData,
144 dataLength,
145 size.width,
146 size.height,
147 map.mStride,
148 imgIEncoder::INPUT_FORMAT_HOSTARGB,
149 aOutputOptions);
150 aDataSurface->Unmap();
151 NS_ENSURE_SUCCESS(rv, rv);
153 encoder.forget(aStream);
154 return NS_OK;
157 NS_IMETHODIMP
158 imgTools::EncodeImage(imgIContainer* aContainer,
159 const nsACString& aMimeType,
160 const nsAString& aOutputOptions,
161 nsIInputStream** aStream)
163 // Use frame 0 from the image container.
164 RefPtr<SourceSurface> frame =
165 aContainer->GetFrame(imgIContainer::FRAME_FIRST,
166 imgIContainer::FLAG_SYNC_DECODE);
167 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
169 RefPtr<DataSourceSurface> dataSurface;
171 if (frame->GetFormat() == SurfaceFormat::B8G8R8A8) {
172 dataSurface = frame->GetDataSurface();
173 } else {
174 // Convert format to SurfaceFormat::B8G8R8A8
175 dataSurface = gfxUtils::
176 CopySurfaceToDataSourceSurfaceWithFormat(frame,
177 SurfaceFormat::B8G8R8A8);
180 NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
182 return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
185 NS_IMETHODIMP
186 imgTools::EncodeScaledImage(imgIContainer* aContainer,
187 const nsACString& aMimeType,
188 int32_t aScaledWidth,
189 int32_t aScaledHeight,
190 const nsAString& aOutputOptions,
191 nsIInputStream** aStream)
193 NS_ENSURE_ARG(aScaledWidth >= 0 && aScaledHeight >= 0);
195 // If no scaled size is specified, we'll just encode the image at its
196 // original size (no scaling).
197 if (aScaledWidth == 0 && aScaledHeight == 0) {
198 return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
201 // Retrieve the image's size.
202 int32_t imageWidth = 0;
203 int32_t imageHeight = 0;
204 aContainer->GetWidth(&imageWidth);
205 aContainer->GetHeight(&imageHeight);
207 // If the given width or height is zero we'll replace it with the image's
208 // original dimensions.
209 IntSize scaledSize(aScaledWidth == 0 ? imageWidth : aScaledWidth,
210 aScaledHeight == 0 ? imageHeight : aScaledHeight);
212 // Use frame 0 from the image container.
213 RefPtr<SourceSurface> frame =
214 aContainer->GetFrameAtSize(scaledSize,
215 imgIContainer::FRAME_FIRST,
216 imgIContainer::FLAG_HIGH_QUALITY_SCALING |
217 imgIContainer::FLAG_SYNC_DECODE);
218 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
220 RefPtr<DataSourceSurface> dataSurface =
221 Factory::CreateDataSourceSurface(scaledSize, SurfaceFormat::B8G8R8A8);
222 if (NS_WARN_IF(!dataSurface)) {
223 return NS_ERROR_FAILURE;
226 DataSourceSurface::MappedSurface map;
227 if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
228 return NS_ERROR_FAILURE;
231 RefPtr<DrawTarget> dt =
232 Factory::CreateDrawTargetForData(BackendType::CAIRO,
233 map.mData,
234 dataSurface->GetSize(),
235 map.mStride,
236 SurfaceFormat::B8G8R8A8);
237 if (!dt) {
238 gfxWarning() << "imgTools::EncodeImage failed in CreateDrawTargetForData";
239 return NS_ERROR_OUT_OF_MEMORY;
242 IntSize frameSize = frame->GetSize();
243 dt->DrawSurface(frame,
244 Rect(0, 0, scaledSize.width, scaledSize.height),
245 Rect(0, 0, frameSize.width, frameSize.height),
246 DrawSurfaceOptions(),
247 DrawOptions(1.0f, CompositionOp::OP_SOURCE));
249 dataSurface->Unmap();
251 return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
254 NS_IMETHODIMP
255 imgTools::EncodeCroppedImage(imgIContainer* aContainer,
256 const nsACString& aMimeType,
257 int32_t aOffsetX,
258 int32_t aOffsetY,
259 int32_t aWidth,
260 int32_t aHeight,
261 const nsAString& aOutputOptions,
262 nsIInputStream** aStream)
264 NS_ENSURE_ARG(aOffsetX >= 0 && aOffsetY >= 0 && aWidth >= 0 && aHeight >= 0);
266 // Offsets must be zero when no width and height are given or else we're out
267 // of bounds.
268 NS_ENSURE_ARG(aWidth + aHeight > 0 || aOffsetX + aOffsetY == 0);
270 // If no size is specified then we'll preserve the image's original dimensions
271 // and don't need to crop.
272 if (aWidth == 0 && aHeight == 0) {
273 return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
276 // Use frame 0 from the image container.
277 RefPtr<SourceSurface> frame =
278 aContainer->GetFrame(imgIContainer::FRAME_FIRST,
279 imgIContainer::FLAG_SYNC_DECODE);
280 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
282 int32_t frameWidth = frame->GetSize().width;
283 int32_t frameHeight = frame->GetSize().height;
285 // If the given width or height is zero we'll replace it with the image's
286 // original dimensions.
287 if (aWidth == 0) {
288 aWidth = frameWidth;
289 } else if (aHeight == 0) {
290 aHeight = frameHeight;
293 // Check that the given crop rectangle is within image bounds.
294 NS_ENSURE_ARG(frameWidth >= aOffsetX + aWidth &&
295 frameHeight >= aOffsetY + aHeight);
297 RefPtr<DataSourceSurface> dataSurface =
298 Factory::CreateDataSourceSurface(IntSize(aWidth, aHeight),
299 SurfaceFormat::B8G8R8A8,
300 /* aZero = */ true);
301 if (NS_WARN_IF(!dataSurface)) {
302 return NS_ERROR_FAILURE;
305 DataSourceSurface::MappedSurface map;
306 if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
307 return NS_ERROR_FAILURE;
310 RefPtr<DrawTarget> dt =
311 Factory::CreateDrawTargetForData(BackendType::CAIRO,
312 map.mData,
313 dataSurface->GetSize(),
314 map.mStride,
315 SurfaceFormat::B8G8R8A8);
316 if (!dt) {
317 gfxWarning() <<
318 "imgTools::EncodeCroppedImage failed in CreateDrawTargetForData";
319 return NS_ERROR_OUT_OF_MEMORY;
321 dt->CopySurface(frame,
322 IntRect(aOffsetX, aOffsetY, aWidth, aHeight),
323 IntPoint(0, 0));
325 dataSurface->Unmap();
327 return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
330 NS_IMETHODIMP
331 imgTools::CreateScriptedObserver(imgIScriptedNotificationObserver* aInner,
332 imgINotificationObserver** aObserver)
334 NS_ADDREF(*aObserver = new ScriptedNotificationObserver(aInner));
335 return NS_OK;
338 NS_IMETHODIMP
339 imgTools::GetImgLoaderForDocument(nsIDOMDocument* aDoc, imgILoader** aLoader)
341 nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
342 NS_IF_ADDREF(*aLoader = nsContentUtils::GetImgLoaderForDocument(doc));
343 return NS_OK;
346 NS_IMETHODIMP
347 imgTools::GetImgCacheForDocument(nsIDOMDocument* aDoc, imgICache** aCache)
349 nsCOMPtr<imgILoader> loader;
350 nsresult rv = GetImgLoaderForDocument(aDoc, getter_AddRefs(loader));
351 NS_ENSURE_SUCCESS(rv, rv);
352 return CallQueryInterface(loader, aCache);
355 } // namespace image
356 } // namespace mozilla