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