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/. */
10 #include "mozilla/gfx/2D.h"
11 #include "mozilla/RefPtr.h"
13 #include "nsIDocument.h"
14 #include "nsIDOMDocument.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"
24 #include "ScriptedNotificationObserver.h"
25 #include "imgIScriptedNotificationObserver.h"
26 #include "gfxPlatform.h"
28 using namespace mozilla::gfx
;
32 /* ========== imgITools implementation ========== */
36 NS_IMPL_ISUPPORTS(imgTools
, imgITools
)
40 /* member initializers and constructor code */
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
);
60 imgTools::DecodeImage(nsIInputStream
* aInStr
,
61 const nsACString
& aMimeType
,
62 imgIContainer
** aContainer
)
64 MOZ_ASSERT(NS_IsMainThread());
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
)) {
89 // Figure out how much data we've been passed.
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,
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
);
106 NS_ADDREF(*aContainer
= image
.get());
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.
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());
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
;
143 nsresult rv
= encoder
->InitFromData(map
.mData
,
148 imgIEncoder::INPUT_FORMAT_HOSTARGB
,
150 aDataSurface
->Unmap();
151 NS_ENSURE_SUCCESS(rv
, rv
);
153 encoder
.forget(aStream
);
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();
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
);
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
,
234 dataSurface
->GetSize(),
236 SurfaceFormat::B8G8R8A8
);
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
);
255 imgTools::EncodeCroppedImage(imgIContainer
* aContainer
,
256 const nsACString
& aMimeType
,
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
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.
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
,
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
,
313 dataSurface
->GetSize(),
315 SurfaceFormat::B8G8R8A8
);
318 "imgTools::EncodeCroppedImage failed in CreateDrawTargetForData";
319 return NS_ERROR_OUT_OF_MEMORY
;
321 dt
->CopySurface(frame
,
322 IntRect(aOffsetX
, aOffsetY
, aWidth
, aHeight
),
325 dataSurface
->Unmap();
327 return EncodeImageData(dataSurface
, aMimeType
, aOutputOptions
, aStream
);
331 imgTools::CreateScriptedObserver(imgIScriptedNotificationObserver
* aInner
,
332 imgINotificationObserver
** aObserver
)
334 NS_ADDREF(*aObserver
= new ScriptedNotificationObserver(aInner
));
339 imgTools::GetImgLoaderForDocument(nsIDOMDocument
* aDoc
, imgILoader
** aLoader
)
341 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(aDoc
);
342 NS_IF_ADDREF(*aLoader
= nsContentUtils::GetImgLoaderForDocument(doc
));
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
);
356 } // namespace mozilla