/src/mozilla-central/image/imgTools.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #include "imgTools.h" |
8 | | |
9 | | #include "DecodePool.h" |
10 | | #include "gfxUtils.h" |
11 | | #include "mozilla/gfx/2D.h" |
12 | | #include "mozilla/gfx/Logging.h" |
13 | | #include "mozilla/RefPtr.h" |
14 | | #include "nsCOMPtr.h" |
15 | | #include "nsIDocument.h" |
16 | | #include "nsError.h" |
17 | | #include "imgLoader.h" |
18 | | #include "imgICache.h" |
19 | | #include "imgIContainer.h" |
20 | | #include "imgIEncoder.h" |
21 | | #include "nsNetUtil.h" // for NS_NewBufferedInputStream |
22 | | #include "nsStreamUtils.h" |
23 | | #include "nsStringStream.h" |
24 | | #include "nsContentUtils.h" |
25 | | #include "nsProxyRelease.h" |
26 | | #include "ImageFactory.h" |
27 | | #include "Image.h" |
28 | | #include "ScriptedNotificationObserver.h" |
29 | | #include "imgIScriptedNotificationObserver.h" |
30 | | #include "gfxPlatform.h" |
31 | | #include "jsfriendapi.h" |
32 | | |
33 | | using namespace mozilla::gfx; |
34 | | |
35 | | namespace mozilla { |
36 | | namespace image { |
37 | | |
38 | | namespace { |
39 | | |
40 | | class ImageDecoderHelper final : public Runnable |
41 | | , public nsIInputStreamCallback |
42 | | { |
43 | | public: |
44 | | NS_DECL_ISUPPORTS_INHERITED |
45 | | |
46 | | ImageDecoderHelper(already_AddRefed<image::Image> aImage, |
47 | | already_AddRefed<nsIInputStream> aInputStream, |
48 | | nsIEventTarget* aEventTarget, |
49 | | imgIContainerCallback* aCallback, |
50 | | nsIEventTarget* aCallbackEventTarget) |
51 | | : Runnable("ImageDecoderHelper") |
52 | | , mImage(std::move(aImage)) |
53 | | , mInputStream(std::move(aInputStream)) |
54 | | , mEventTarget(aEventTarget) |
55 | | , mCallback(aCallback) |
56 | | , mCallbackEventTarget(aCallbackEventTarget) |
57 | | , mStatus(NS_OK) |
58 | 0 | { |
59 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
60 | 0 | } |
61 | | |
62 | | NS_IMETHOD |
63 | | Run() override |
64 | 0 | { |
65 | 0 | // This runnable is dispatched on the Image thread when reading data, but |
66 | 0 | // at the end, it goes back to the main-thread in order to complete the |
67 | 0 | // operation. |
68 | 0 | if (NS_IsMainThread()) { |
69 | 0 | // Let the Image know we've sent all the data. |
70 | 0 | mImage->OnImageDataComplete(nullptr, nullptr, mStatus, true); |
71 | 0 |
|
72 | 0 | RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker(); |
73 | 0 | tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE); |
74 | 0 |
|
75 | 0 | nsCOMPtr<imgIContainer> container; |
76 | 0 | if (NS_SUCCEEDED(mStatus)) { |
77 | 0 | container = do_QueryInterface(mImage); |
78 | 0 | } |
79 | 0 |
|
80 | 0 | mCallback->OnImageReady(container, mStatus); |
81 | 0 | return NS_OK; |
82 | 0 | } |
83 | 0 |
|
84 | 0 | uint64_t length; |
85 | 0 | nsresult rv = mInputStream->Available(&length); |
86 | 0 | if (rv == NS_BASE_STREAM_CLOSED) { |
87 | 0 | return OperationCompleted(NS_OK); |
88 | 0 | } |
89 | 0 | |
90 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
91 | 0 | return OperationCompleted(rv); |
92 | 0 | } |
93 | 0 | |
94 | 0 | // Nothing else to read, but maybe we just need to wait. |
95 | 0 | if (length == 0) { |
96 | 0 | nsCOMPtr<nsIAsyncInputStream> asyncInputStream = |
97 | 0 | do_QueryInterface(mInputStream); |
98 | 0 | if (asyncInputStream) { |
99 | 0 | rv = asyncInputStream->AsyncWait(this, 0, 0, mEventTarget); |
100 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
101 | 0 | return OperationCompleted(rv); |
102 | 0 | } |
103 | 0 | return NS_OK; |
104 | 0 | } |
105 | 0 | |
106 | 0 | // We really have nothing else to read. |
107 | 0 | if (length == 0) { |
108 | 0 | return OperationCompleted(NS_OK); |
109 | 0 | } |
110 | 0 | } |
111 | 0 | |
112 | 0 | // Send the source data to the Image. |
113 | 0 | rv = mImage->OnImageDataAvailable(nullptr, nullptr, mInputStream, 0, |
114 | 0 | uint32_t(length)); |
115 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
116 | 0 | return OperationCompleted(rv); |
117 | 0 | } |
118 | 0 | |
119 | 0 | rv = mEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); |
120 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
121 | 0 | return OperationCompleted(rv); |
122 | 0 | } |
123 | 0 | |
124 | 0 | return NS_OK; |
125 | 0 | } |
126 | | |
127 | | NS_IMETHOD |
128 | | OnInputStreamReady(nsIAsyncInputStream* aAsyncInputStream) override |
129 | 0 | { |
130 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
131 | 0 | return Run(); |
132 | 0 | } |
133 | | |
134 | | nsresult |
135 | | OperationCompleted(nsresult aStatus) |
136 | 0 | { |
137 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
138 | 0 |
|
139 | 0 | mStatus = aStatus; |
140 | 0 | mCallbackEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); |
141 | 0 | return NS_OK; |
142 | 0 | } |
143 | | |
144 | | private: |
145 | | ~ImageDecoderHelper() |
146 | 0 | { |
147 | 0 | NS_ReleaseOnMainThreadSystemGroup("ImageDecoderHelper::mImage", |
148 | 0 | mImage.forget()); |
149 | 0 | NS_ReleaseOnMainThreadSystemGroup("ImageDecoderHelper::mCallback", |
150 | 0 | mCallback.forget()); |
151 | 0 | } |
152 | | |
153 | | RefPtr<image::Image> mImage; |
154 | | |
155 | | nsCOMPtr<nsIInputStream> mInputStream; |
156 | | nsCOMPtr<nsIEventTarget> mEventTarget; |
157 | | nsCOMPtr<imgIContainerCallback> mCallback; |
158 | | nsCOMPtr<nsIEventTarget> mCallbackEventTarget; |
159 | | |
160 | | nsresult mStatus; |
161 | | }; |
162 | | |
163 | | NS_IMPL_ISUPPORTS_INHERITED(ImageDecoderHelper, Runnable, |
164 | | nsIInputStreamCallback) |
165 | | |
166 | | } // anonymous |
167 | | |
168 | | /* ========== imgITools implementation ========== */ |
169 | | |
170 | | |
171 | | |
172 | | NS_IMPL_ISUPPORTS(imgTools, imgITools) |
173 | | |
174 | | imgTools::imgTools() |
175 | 0 | { |
176 | 0 | /* member initializers and constructor code */ |
177 | 0 | } |
178 | | |
179 | | imgTools::~imgTools() |
180 | 0 | { |
181 | 0 | /* destructor code */ |
182 | 0 | } |
183 | | |
184 | | NS_IMETHODIMP |
185 | | imgTools::DecodeImageFromArrayBuffer(JS::HandleValue aArrayBuffer, |
186 | | const nsACString& aMimeType, |
187 | | JSContext* aCx, |
188 | | imgIContainer** aContainer) |
189 | 0 | { |
190 | 0 | if (!aArrayBuffer.isObject()) { |
191 | 0 | return NS_ERROR_FAILURE; |
192 | 0 | } |
193 | 0 | |
194 | 0 | JS::Rooted<JSObject*> obj(aCx, |
195 | 0 | js::UnwrapArrayBuffer(&aArrayBuffer.toObject())); |
196 | 0 | if (!obj) { |
197 | 0 | return NS_ERROR_FAILURE; |
198 | 0 | } |
199 | 0 | |
200 | 0 | uint8_t* bufferData = nullptr; |
201 | 0 | uint32_t bufferLength = 0; |
202 | 0 | bool isSharedMemory = false; |
203 | 0 |
|
204 | 0 | js::GetArrayBufferLengthAndData(obj, &bufferLength, &isSharedMemory, |
205 | 0 | &bufferData); |
206 | 0 | return DecodeImageFromBuffer((char*)bufferData, bufferLength, aMimeType, |
207 | 0 | aContainer); |
208 | 0 | } |
209 | | |
210 | | NS_IMETHODIMP |
211 | | imgTools::DecodeImageFromBuffer(const char* aBuffer, uint32_t aSize, |
212 | | const nsACString& aMimeType, |
213 | | imgIContainer** aContainer) |
214 | 0 | { |
215 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
216 | 0 |
|
217 | 0 | NS_ENSURE_ARG_POINTER(aBuffer); |
218 | 0 |
|
219 | 0 | // Create a new image container to hold the decoded data. |
220 | 0 | nsAutoCString mimeType(aMimeType); |
221 | 0 | RefPtr<image::Image> image = |
222 | 0 | ImageFactory::CreateAnonymousImage(mimeType, aSize); |
223 | 0 | RefPtr<ProgressTracker> tracker = image->GetProgressTracker(); |
224 | 0 |
|
225 | 0 | if (image->HasError()) { |
226 | 0 | return NS_ERROR_FAILURE; |
227 | 0 | } |
228 | 0 | |
229 | 0 | // Let's create a temporary inputStream. |
230 | 0 | nsCOMPtr<nsIInputStream> stream; |
231 | 0 | nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), |
232 | 0 | aBuffer, aSize, |
233 | 0 | NS_ASSIGNMENT_DEPEND); |
234 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
235 | 0 | MOZ_ASSERT(stream); |
236 | 0 | MOZ_ASSERT(NS_InputStreamIsBuffered(stream)); |
237 | 0 |
|
238 | 0 | rv = image->OnImageDataAvailable(nullptr, nullptr, stream, 0, |
239 | 0 | aSize); |
240 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
241 | 0 |
|
242 | 0 | // Let the Image know we've sent all the data. |
243 | 0 | rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true); |
244 | 0 | tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE); |
245 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
246 | 0 |
|
247 | 0 | // All done. |
248 | 0 | image.forget(aContainer); |
249 | 0 | return NS_OK; |
250 | 0 | } |
251 | | |
252 | | NS_IMETHODIMP |
253 | | imgTools::DecodeImageAsync(nsIInputStream* aInStr, |
254 | | const nsACString& aMimeType, |
255 | | imgIContainerCallback* aCallback, |
256 | | nsIEventTarget* aEventTarget) |
257 | 0 | { |
258 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
259 | 0 |
|
260 | 0 | NS_ENSURE_ARG_POINTER(aInStr); |
261 | 0 | NS_ENSURE_ARG_POINTER(aCallback); |
262 | 0 | NS_ENSURE_ARG_POINTER(aEventTarget); |
263 | 0 |
|
264 | 0 | nsresult rv; |
265 | 0 |
|
266 | 0 | // Let's continuing the reading on a separate thread. |
267 | 0 | DecodePool* decodePool = DecodePool::Singleton(); |
268 | 0 | MOZ_ASSERT(decodePool); |
269 | 0 |
|
270 | 0 | RefPtr<nsIEventTarget> target = decodePool->GetIOEventTarget(); |
271 | 0 | NS_ENSURE_TRUE(target, NS_ERROR_FAILURE); |
272 | 0 |
|
273 | 0 | // Prepare the input stream. |
274 | 0 | nsCOMPtr<nsIInputStream> stream = aInStr; |
275 | 0 | if (!NS_InputStreamIsBuffered(aInStr)) { |
276 | 0 | nsCOMPtr<nsIInputStream> bufStream; |
277 | 0 | rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), |
278 | 0 | stream.forget(), 1024); |
279 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
280 | 0 | stream = bufStream.forget(); |
281 | 0 | } |
282 | 0 |
|
283 | 0 | // Create a new image container to hold the decoded data. |
284 | 0 | nsAutoCString mimeType(aMimeType); |
285 | 0 | RefPtr<image::Image> image = ImageFactory::CreateAnonymousImage(mimeType, 0); |
286 | 0 |
|
287 | 0 | // Already an error? |
288 | 0 | if (image->HasError()) { |
289 | 0 | return NS_ERROR_FAILURE; |
290 | 0 | } |
291 | 0 | |
292 | 0 | RefPtr<ImageDecoderHelper> helper = |
293 | 0 | new ImageDecoderHelper(image.forget(), stream.forget(), target, aCallback, |
294 | 0 | aEventTarget); |
295 | 0 | rv = target->Dispatch(helper.forget(), NS_DISPATCH_NORMAL); |
296 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
297 | 0 |
|
298 | 0 | return NS_OK; |
299 | 0 | } |
300 | | |
301 | | /** |
302 | | * This takes a DataSourceSurface rather than a SourceSurface because some |
303 | | * of the callers have a DataSourceSurface and we don't want to call |
304 | | * GetDataSurface on such surfaces since that may incure a conversion to |
305 | | * SurfaceType::DATA which we don't need. |
306 | | */ |
307 | | static nsresult |
308 | | EncodeImageData(DataSourceSurface* aDataSurface, |
309 | | DataSourceSurface::ScopedMap& aMap, |
310 | | const nsACString& aMimeType, |
311 | | const nsAString& aOutputOptions, |
312 | | nsIInputStream** aStream) |
313 | 0 | { |
314 | 0 | MOZ_ASSERT(aDataSurface->GetFormat() == SurfaceFormat::B8G8R8A8 || |
315 | 0 | aDataSurface->GetFormat() == SurfaceFormat::B8G8R8X8, |
316 | 0 | "We're assuming B8G8R8A8/X8"); |
317 | 0 |
|
318 | 0 | // Get an image encoder for the media type |
319 | 0 | nsAutoCString encoderCID( |
320 | 0 | NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType); |
321 | 0 |
|
322 | 0 | nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get()); |
323 | 0 | if (!encoder) { |
324 | 0 | return NS_IMAGELIB_ERROR_NO_ENCODER; |
325 | 0 | } |
326 | 0 | |
327 | 0 | IntSize size = aDataSurface->GetSize(); |
328 | 0 | uint32_t dataLength = aMap.GetStride() * size.height; |
329 | 0 |
|
330 | 0 | // Encode the bitmap |
331 | 0 | nsresult rv = encoder->InitFromData(aMap.GetData(), |
332 | 0 | dataLength, |
333 | 0 | size.width, |
334 | 0 | size.height, |
335 | 0 | aMap.GetStride(), |
336 | 0 | imgIEncoder::INPUT_FORMAT_HOSTARGB, |
337 | 0 | aOutputOptions); |
338 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
339 | 0 |
|
340 | 0 | encoder.forget(aStream); |
341 | 0 | return NS_OK; |
342 | 0 | } |
343 | | |
344 | | static nsresult |
345 | | EncodeImageData(DataSourceSurface* aDataSurface, |
346 | | const nsACString& aMimeType, |
347 | | const nsAString& aOutputOptions, |
348 | | nsIInputStream** aStream) |
349 | 0 | { |
350 | 0 | DataSourceSurface::ScopedMap map(aDataSurface, DataSourceSurface::READ); |
351 | 0 | if (!map.IsMapped()) { |
352 | 0 | return NS_ERROR_FAILURE; |
353 | 0 | } |
354 | 0 | |
355 | 0 | return EncodeImageData(aDataSurface, map, aMimeType, aOutputOptions, aStream); |
356 | 0 | } |
357 | | |
358 | | NS_IMETHODIMP |
359 | | imgTools::EncodeImage(imgIContainer* aContainer, |
360 | | const nsACString& aMimeType, |
361 | | const nsAString& aOutputOptions, |
362 | | nsIInputStream** aStream) |
363 | 0 | { |
364 | 0 | // Use frame 0 from the image container. |
365 | 0 | RefPtr<SourceSurface> frame = |
366 | 0 | aContainer->GetFrame(imgIContainer::FRAME_FIRST, |
367 | 0 | imgIContainer::FLAG_SYNC_DECODE); |
368 | 0 | NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); |
369 | 0 |
|
370 | 0 | RefPtr<DataSourceSurface> dataSurface; |
371 | 0 |
|
372 | 0 | if (frame->GetFormat() == SurfaceFormat::B8G8R8A8 || |
373 | 0 | frame->GetFormat() == SurfaceFormat::B8G8R8X8) { |
374 | 0 | dataSurface = frame->GetDataSurface(); |
375 | 0 | } else { |
376 | 0 | // Convert format to SurfaceFormat::B8G8R8A8 |
377 | 0 | dataSurface = gfxUtils:: |
378 | 0 | CopySurfaceToDataSourceSurfaceWithFormat(frame, |
379 | 0 | SurfaceFormat::B8G8R8A8); |
380 | 0 | } |
381 | 0 |
|
382 | 0 | NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE); |
383 | 0 |
|
384 | 0 | return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream); |
385 | 0 | } |
386 | | |
387 | | NS_IMETHODIMP |
388 | | imgTools::EncodeScaledImage(imgIContainer* aContainer, |
389 | | const nsACString& aMimeType, |
390 | | int32_t aScaledWidth, |
391 | | int32_t aScaledHeight, |
392 | | const nsAString& aOutputOptions, |
393 | | nsIInputStream** aStream) |
394 | 0 | { |
395 | 0 | NS_ENSURE_ARG(aScaledWidth >= 0 && aScaledHeight >= 0); |
396 | 0 |
|
397 | 0 | // If no scaled size is specified, we'll just encode the image at its |
398 | 0 | // original size (no scaling). |
399 | 0 | if (aScaledWidth == 0 && aScaledHeight == 0) { |
400 | 0 | return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream); |
401 | 0 | } |
402 | 0 | |
403 | 0 | // Retrieve the image's size. |
404 | 0 | int32_t imageWidth = 0; |
405 | 0 | int32_t imageHeight = 0; |
406 | 0 | aContainer->GetWidth(&imageWidth); |
407 | 0 | aContainer->GetHeight(&imageHeight); |
408 | 0 |
|
409 | 0 | // If the given width or height is zero we'll replace it with the image's |
410 | 0 | // original dimensions. |
411 | 0 | IntSize scaledSize(aScaledWidth == 0 ? imageWidth : aScaledWidth, |
412 | 0 | aScaledHeight == 0 ? imageHeight : aScaledHeight); |
413 | 0 |
|
414 | 0 | // Use frame 0 from the image container. |
415 | 0 | RefPtr<SourceSurface> frame = |
416 | 0 | aContainer->GetFrameAtSize(scaledSize, |
417 | 0 | imgIContainer::FRAME_FIRST, |
418 | 0 | imgIContainer::FLAG_HIGH_QUALITY_SCALING | |
419 | 0 | imgIContainer::FLAG_SYNC_DECODE); |
420 | 0 | NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); |
421 | 0 |
|
422 | 0 | // If the given surface is the right size/format, we can encode it directly. |
423 | 0 | if (scaledSize == frame->GetSize() && |
424 | 0 | (frame->GetFormat() == SurfaceFormat::B8G8R8A8 || |
425 | 0 | frame->GetFormat() == SurfaceFormat::B8G8R8X8)) { |
426 | 0 | RefPtr<DataSourceSurface> dataSurface = frame->GetDataSurface(); |
427 | 0 | if (dataSurface) { |
428 | 0 | return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream); |
429 | 0 | } |
430 | 0 | } |
431 | 0 | |
432 | 0 | // Otherwise we need to scale it using a draw target. |
433 | 0 | RefPtr<DataSourceSurface> dataSurface = |
434 | 0 | Factory::CreateDataSourceSurface(scaledSize, SurfaceFormat::B8G8R8A8); |
435 | 0 | if (NS_WARN_IF(!dataSurface)) { |
436 | 0 | return NS_ERROR_FAILURE; |
437 | 0 | } |
438 | 0 | |
439 | 0 | DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ_WRITE); |
440 | 0 | if (!map.IsMapped()) { |
441 | 0 | return NS_ERROR_FAILURE; |
442 | 0 | } |
443 | 0 | |
444 | 0 | RefPtr<DrawTarget> dt = |
445 | 0 | Factory::CreateDrawTargetForData(BackendType::SKIA, |
446 | 0 | map.GetData(), |
447 | 0 | dataSurface->GetSize(), |
448 | 0 | map.GetStride(), |
449 | 0 | SurfaceFormat::B8G8R8A8); |
450 | 0 | if (!dt) { |
451 | 0 | gfxWarning() << "imgTools::EncodeImage failed in CreateDrawTargetForData"; |
452 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
453 | 0 | } |
454 | 0 |
|
455 | 0 | IntSize frameSize = frame->GetSize(); |
456 | 0 | dt->DrawSurface(frame, |
457 | 0 | Rect(0, 0, scaledSize.width, scaledSize.height), |
458 | 0 | Rect(0, 0, frameSize.width, frameSize.height), |
459 | 0 | DrawSurfaceOptions(), |
460 | 0 | DrawOptions(1.0f, CompositionOp::OP_SOURCE)); |
461 | 0 |
|
462 | 0 | return EncodeImageData(dataSurface, map, aMimeType, aOutputOptions, aStream); |
463 | 0 | } |
464 | | |
465 | | NS_IMETHODIMP |
466 | | imgTools::EncodeCroppedImage(imgIContainer* aContainer, |
467 | | const nsACString& aMimeType, |
468 | | int32_t aOffsetX, |
469 | | int32_t aOffsetY, |
470 | | int32_t aWidth, |
471 | | int32_t aHeight, |
472 | | const nsAString& aOutputOptions, |
473 | | nsIInputStream** aStream) |
474 | 0 | { |
475 | 0 | NS_ENSURE_ARG(aOffsetX >= 0 && aOffsetY >= 0 && aWidth >= 0 && aHeight >= 0); |
476 | 0 |
|
477 | 0 | // Offsets must be zero when no width and height are given or else we're out |
478 | 0 | // of bounds. |
479 | 0 | NS_ENSURE_ARG(aWidth + aHeight > 0 || aOffsetX + aOffsetY == 0); |
480 | 0 |
|
481 | 0 | // If no size is specified then we'll preserve the image's original dimensions |
482 | 0 | // and don't need to crop. |
483 | 0 | if (aWidth == 0 && aHeight == 0) { |
484 | 0 | return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream); |
485 | 0 | } |
486 | 0 | |
487 | 0 | // Use frame 0 from the image container. |
488 | 0 | RefPtr<SourceSurface> frame = |
489 | 0 | aContainer->GetFrame(imgIContainer::FRAME_FIRST, |
490 | 0 | imgIContainer::FLAG_SYNC_DECODE); |
491 | 0 | NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); |
492 | 0 |
|
493 | 0 | int32_t frameWidth = frame->GetSize().width; |
494 | 0 | int32_t frameHeight = frame->GetSize().height; |
495 | 0 |
|
496 | 0 | // If the given width or height is zero we'll replace it with the image's |
497 | 0 | // original dimensions. |
498 | 0 | if (aWidth == 0) { |
499 | 0 | aWidth = frameWidth; |
500 | 0 | } else if (aHeight == 0) { |
501 | 0 | aHeight = frameHeight; |
502 | 0 | } |
503 | 0 |
|
504 | 0 | // Check that the given crop rectangle is within image bounds. |
505 | 0 | NS_ENSURE_ARG(frameWidth >= aOffsetX + aWidth && |
506 | 0 | frameHeight >= aOffsetY + aHeight); |
507 | 0 |
|
508 | 0 | RefPtr<DataSourceSurface> dataSurface = |
509 | 0 | Factory::CreateDataSourceSurface(IntSize(aWidth, aHeight), |
510 | 0 | SurfaceFormat::B8G8R8A8, |
511 | 0 | /* aZero = */ true); |
512 | 0 | if (NS_WARN_IF(!dataSurface)) { |
513 | 0 | return NS_ERROR_FAILURE; |
514 | 0 | } |
515 | 0 | |
516 | 0 | DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ_WRITE); |
517 | 0 | if (!map.IsMapped()) { |
518 | 0 | return NS_ERROR_FAILURE; |
519 | 0 | } |
520 | 0 | |
521 | 0 | RefPtr<DrawTarget> dt = |
522 | 0 | Factory::CreateDrawTargetForData(BackendType::SKIA, |
523 | 0 | map.GetData(), |
524 | 0 | dataSurface->GetSize(), |
525 | 0 | map.GetStride(), |
526 | 0 | SurfaceFormat::B8G8R8A8); |
527 | 0 | if (!dt) { |
528 | 0 | gfxWarning() << |
529 | 0 | "imgTools::EncodeCroppedImage failed in CreateDrawTargetForData"; |
530 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
531 | 0 | } |
532 | 0 | dt->CopySurface(frame, |
533 | 0 | IntRect(aOffsetX, aOffsetY, aWidth, aHeight), |
534 | 0 | IntPoint(0, 0)); |
535 | 0 |
|
536 | 0 | return EncodeImageData(dataSurface, map, aMimeType, aOutputOptions, aStream); |
537 | 0 | } |
538 | | |
539 | | NS_IMETHODIMP |
540 | | imgTools::CreateScriptedObserver(imgIScriptedNotificationObserver* aInner, |
541 | | imgINotificationObserver** aObserver) |
542 | 0 | { |
543 | 0 | NS_ADDREF(*aObserver = new ScriptedNotificationObserver(aInner)); |
544 | 0 | return NS_OK; |
545 | 0 | } |
546 | | |
547 | | NS_IMETHODIMP |
548 | | imgTools::GetImgLoaderForDocument(nsIDocument* aDoc, imgILoader** aLoader) |
549 | 0 | { |
550 | 0 | NS_IF_ADDREF(*aLoader = nsContentUtils::GetImgLoaderForDocument(aDoc)); |
551 | 0 | return NS_OK; |
552 | 0 | } |
553 | | |
554 | | NS_IMETHODIMP |
555 | | imgTools::GetImgCacheForDocument(nsIDocument* aDoc, imgICache** aCache) |
556 | 0 | { |
557 | 0 | nsCOMPtr<imgILoader> loader; |
558 | 0 | nsresult rv = GetImgLoaderForDocument(aDoc, getter_AddRefs(loader)); |
559 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
560 | 0 | return CallQueryInterface(loader, aCache); |
561 | 0 | } |
562 | | |
563 | | } // namespace image |
564 | | } // namespace mozilla |