Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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;
29 using namespace mozilla::image;
30 using namespace mozilla::gfx;
32 /* ========== imgITools implementation ========== */
36 NS_IMPL_ISUPPORTS(imgTools, imgITools)
38 imgTools::imgTools()
39 {
40 /* member initializers and constructor code */
41 }
43 imgTools::~imgTools()
44 {
45 /* destructor code */
46 }
48 NS_IMETHODIMP imgTools::DecodeImageData(nsIInputStream* aInStr,
49 const nsACString& aMimeType,
50 imgIContainer **aContainer)
51 {
52 NS_ABORT_IF_FALSE(*aContainer == nullptr,
53 "Cannot provide an existing image container to DecodeImageData");
55 return DecodeImage(aInStr, aMimeType, aContainer);
56 }
58 NS_IMETHODIMP imgTools::DecodeImage(nsIInputStream* aInStr,
59 const nsACString& aMimeType,
60 imgIContainer **aContainer)
61 {
62 nsresult rv;
63 nsRefPtr<Image> image;
65 NS_ENSURE_ARG_POINTER(aInStr);
67 // Create a new image container to hold the decoded data.
68 nsAutoCString mimeType(aMimeType);
69 image = ImageFactory::CreateAnonymousImage(mimeType);
71 if (image->HasError())
72 return NS_ERROR_FAILURE;
74 // Prepare the input stream.
75 nsCOMPtr<nsIInputStream> inStream = aInStr;
76 if (!NS_InputStreamIsBuffered(aInStr)) {
77 nsCOMPtr<nsIInputStream> bufStream;
78 rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), aInStr, 1024);
79 if (NS_SUCCEEDED(rv))
80 inStream = bufStream;
81 }
83 // Figure out how much data we've been passed.
84 uint64_t length;
85 rv = inStream->Available(&length);
86 NS_ENSURE_SUCCESS(rv, rv);
87 NS_ENSURE_TRUE(length <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
89 // Send the source data to the Image.
90 rv = image->OnImageDataAvailable(nullptr, nullptr, inStream, 0, uint32_t(length));
91 NS_ENSURE_SUCCESS(rv, rv);
92 // Let the Image know we've sent all the data.
93 rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
94 NS_ENSURE_SUCCESS(rv, rv);
96 // All done.
97 NS_ADDREF(*aContainer = image.get());
98 return NS_OK;
99 }
101 /**
102 * This takes a DataSourceSurface rather than a SourceSurface because some
103 * of the callers have a DataSourceSurface and we don't want to call
104 * GetDataSurface on such surfaces since that may incure a conversion to
105 * SurfaceType::DATA which we don't need.
106 */
107 static nsresult EncodeImageData(DataSourceSurface* aDataSurface,
108 const nsACString& aMimeType,
109 const nsAString& aOutputOptions,
110 nsIInputStream **aStream)
111 {
112 MOZ_ASSERT(aDataSurface->GetFormat() == SurfaceFormat::B8G8R8A8,
113 "We're assuming B8G8R8A8");
115 // Get an image encoder for the media type
116 nsAutoCString encoderCID(
117 NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType);
119 nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
120 if (!encoder)
121 return NS_IMAGELIB_ERROR_NO_ENCODER;
123 DataSourceSurface::MappedSurface map;
124 if (!aDataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
125 return NS_ERROR_FAILURE;
126 }
128 IntSize size = aDataSurface->GetSize();
129 uint32_t dataLength = map.mStride * size.height;
131 // Encode the bitmap
132 nsresult rv = encoder->InitFromData(map.mData,
133 dataLength,
134 size.width,
135 size.height,
136 map.mStride,
137 imgIEncoder::INPUT_FORMAT_HOSTARGB,
138 aOutputOptions);
139 aDataSurface->Unmap();
140 NS_ENSURE_SUCCESS(rv, rv);
142 return CallQueryInterface(encoder, aStream);
143 }
145 NS_IMETHODIMP imgTools::EncodeImage(imgIContainer *aContainer,
146 const nsACString& aMimeType,
147 const nsAString& aOutputOptions,
148 nsIInputStream **aStream)
149 {
150 // Use frame 0 from the image container.
151 RefPtr<SourceSurface> frame =
152 aContainer->GetFrame(imgIContainer::FRAME_FIRST,
153 imgIContainer::FLAG_SYNC_DECODE);
154 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
156 RefPtr<DataSourceSurface> dataSurface;
158 if (frame->GetFormat() == SurfaceFormat::B8G8R8A8) {
159 dataSurface = frame->GetDataSurface();
160 } else {
161 // Convert format to SurfaceFormat::B8G8R8A8
162 dataSurface = gfxUtils::
163 CopySurfaceToDataSourceSurfaceWithFormat(frame,
164 SurfaceFormat::B8G8R8A8);
165 }
167 NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
169 return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
170 }
172 NS_IMETHODIMP imgTools::EncodeScaledImage(imgIContainer *aContainer,
173 const nsACString& aMimeType,
174 int32_t aScaledWidth,
175 int32_t aScaledHeight,
176 const nsAString& aOutputOptions,
177 nsIInputStream **aStream)
178 {
179 NS_ENSURE_ARG(aScaledWidth >= 0 && aScaledHeight >= 0);
181 // If no scaled size is specified, we'll just encode the image at its
182 // original size (no scaling).
183 if (aScaledWidth == 0 && aScaledHeight == 0) {
184 return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
185 }
187 // Use frame 0 from the image container.
188 RefPtr<SourceSurface> frame =
189 aContainer->GetFrame(imgIContainer::FRAME_FIRST,
190 imgIContainer::FLAG_SYNC_DECODE);
191 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
193 int32_t frameWidth = frame->GetSize().width;
194 int32_t frameHeight = frame->GetSize().height;
196 // If the given width or height is zero we'll replace it with the image's
197 // original dimensions.
198 if (aScaledWidth == 0) {
199 aScaledWidth = frameWidth;
200 } else if (aScaledHeight == 0) {
201 aScaledHeight = frameHeight;
202 }
204 RefPtr<DataSourceSurface> dataSurface =
205 Factory::CreateDataSourceSurface(IntSize(aScaledWidth, aScaledHeight),
206 SurfaceFormat::B8G8R8A8);
207 DataSourceSurface::MappedSurface map;
208 if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
209 return NS_ERROR_FAILURE;
210 }
212 RefPtr<DrawTarget> dt =
213 Factory::CreateDrawTargetForData(BackendType::CAIRO,
214 map.mData,
215 dataSurface->GetSize(),
216 map.mStride,
217 SurfaceFormat::B8G8R8A8);
218 dt->DrawSurface(frame,
219 Rect(0, 0, aScaledWidth, aScaledHeight),
220 Rect(0, 0, frameWidth, frameHeight),
221 DrawSurfaceOptions(),
222 DrawOptions(1.0f, CompositionOp::OP_SOURCE));
224 dataSurface->Unmap();
226 return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
227 }
229 NS_IMETHODIMP imgTools::EncodeCroppedImage(imgIContainer *aContainer,
230 const nsACString& aMimeType,
231 int32_t aOffsetX,
232 int32_t aOffsetY,
233 int32_t aWidth,
234 int32_t aHeight,
235 const nsAString& aOutputOptions,
236 nsIInputStream **aStream)
237 {
238 NS_ENSURE_ARG(aOffsetX >= 0 && aOffsetY >= 0 && aWidth >= 0 && aHeight >= 0);
240 // Offsets must be zero when no width and height are given or else we're out
241 // of bounds.
242 NS_ENSURE_ARG(aWidth + aHeight > 0 || aOffsetX + aOffsetY == 0);
244 // If no size is specified then we'll preserve the image's original dimensions
245 // and don't need to crop.
246 if (aWidth == 0 && aHeight == 0) {
247 return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
248 }
250 // Use frame 0 from the image container.
251 RefPtr<SourceSurface> frame =
252 aContainer->GetFrame(imgIContainer::FRAME_FIRST,
253 imgIContainer::FLAG_SYNC_DECODE);
254 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
256 int32_t frameWidth = frame->GetSize().width;
257 int32_t frameHeight = frame->GetSize().height;
259 // If the given width or height is zero we'll replace it with the image's
260 // original dimensions.
261 if (aWidth == 0) {
262 aWidth = frameWidth;
263 } else if (aHeight == 0) {
264 aHeight = frameHeight;
265 }
267 // Check that the given crop rectangle is within image bounds.
268 NS_ENSURE_ARG(frameWidth >= aOffsetX + aWidth &&
269 frameHeight >= aOffsetY + aHeight);
271 RefPtr<DataSourceSurface> dataSurface =
272 Factory::CreateDataSourceSurface(IntSize(aWidth, aHeight),
273 SurfaceFormat::B8G8R8A8);
274 DataSourceSurface::MappedSurface map;
275 if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
276 return NS_ERROR_FAILURE;
277 }
279 RefPtr<DrawTarget> dt =
280 Factory::CreateDrawTargetForData(BackendType::CAIRO,
281 map.mData,
282 dataSurface->GetSize(),
283 map.mStride,
284 SurfaceFormat::B8G8R8A8);
285 dt->CopySurface(frame,
286 IntRect(aOffsetX, aOffsetY, aWidth, aHeight),
287 IntPoint(0, 0));
289 dataSurface->Unmap();
291 return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
292 }
294 NS_IMETHODIMP imgTools::CreateScriptedObserver(imgIScriptedNotificationObserver* aInner,
295 imgINotificationObserver** aObserver)
296 {
297 NS_ADDREF(*aObserver = new ScriptedNotificationObserver(aInner));
298 return NS_OK;
299 }
301 NS_IMETHODIMP
302 imgTools::GetImgLoaderForDocument(nsIDOMDocument* aDoc, imgILoader** aLoader)
303 {
304 nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
305 NS_IF_ADDREF(*aLoader = nsContentUtils::GetImgLoaderForDocument(doc));
306 return NS_OK;
307 }
309 NS_IMETHODIMP
310 imgTools::GetImgCacheForDocument(nsIDOMDocument* aDoc, imgICache** aCache)
311 {
312 nsCOMPtr<imgILoader> loader;
313 nsresult rv = GetImgLoaderForDocument(aDoc, getter_AddRefs(loader));
314 NS_ENSURE_SUCCESS(rv, rv);
315 return CallQueryInterface(loader, aCache);
316 }