forked from mirrors/gecko-dev
		
	 5af31a8886
			
		
	
	
		5af31a8886
		
	
	
	
	
		
			
			There is no implicit conversion for scoped enums, so using them without an explicit conversion in varargs functions is undefined behavior. GCC has had a warning about this for a long while, but clang only gained this a few days ago on trunk. Differential Revision: https://phabricator.services.mozilla.com/D181723
		
			
				
	
	
		
			185 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "ImageDecoderSupport.h"
 | |
| 
 | |
| #include "imgINotificationObserver.h"
 | |
| #include "imgITools.h"
 | |
| #include "imgINotificationObserver.h"
 | |
| #include "gfxUtils.h"
 | |
| #include "AndroidGraphics.h"
 | |
| #include "JavaExceptions.h"
 | |
| #include "mozilla/gfx/Point.h"
 | |
| #include "mozilla/gfx/Swizzle.h"
 | |
| #include "mozilla/java/ImageWrappers.h"
 | |
| #include "nsNetUtil.h"
 | |
| 
 | |
| using namespace mozilla::gfx;
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace widget {
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| class ImageCallbackHelper;
 | |
| 
 | |
| HashSet<RefPtr<ImageCallbackHelper>, PointerHasher<ImageCallbackHelper*>>
 | |
|     gDecodeRequests;
 | |
| 
 | |
| class ImageCallbackHelper : public imgIContainerCallback,
 | |
|                             public imgINotificationObserver {
 | |
|  public:
 | |
|   NS_DECL_ISUPPORTS
 | |
| 
 | |
|   void CompleteExceptionally(nsresult aRv) {
 | |
|     nsPrintfCString error("Could not process image: 0x%08X", uint32_t(aRv));
 | |
|     mResult->CompleteExceptionally(
 | |
|         java::Image::ImageProcessingException::New(error.get())
 | |
|             .Cast<jni::Throwable>());
 | |
|     gDecodeRequests.remove(this);
 | |
|   }
 | |
| 
 | |
|   void Complete(DataSourceSurface::ScopedMap& aSourceSurface, int32_t width,
 | |
|                 int32_t height) {
 | |
|     auto pixels = mozilla::jni::ByteBuffer::New(
 | |
|         reinterpret_cast<int8_t*>(aSourceSurface.GetData()),
 | |
|         aSourceSurface.GetStride() * height);
 | |
|     auto bitmap = java::sdk::Bitmap::CreateBitmap(
 | |
|         width, height, java::sdk::Bitmap::Config::ARGB_8888());
 | |
|     bitmap->CopyPixelsFromBuffer(pixels);
 | |
|     mResult->Complete(bitmap);
 | |
|     gDecodeRequests.remove(this);
 | |
|   }
 | |
| 
 | |
|   ImageCallbackHelper(java::GeckoResult::Param aResult, int32_t aDesiredLength)
 | |
|       : mResult(aResult), mDesiredLength(aDesiredLength), mImage(nullptr) {
 | |
|     MOZ_ASSERT(mResult);
 | |
|   }
 | |
| 
 | |
|   NS_IMETHOD
 | |
|   OnImageReady(imgIContainer* aImage, nsresult aStatus) override {
 | |
|     // Let's make sure we are alive until the request completes
 | |
|     MOZ_ALWAYS_TRUE(gDecodeRequests.putNew(this));
 | |
| 
 | |
|     if (NS_FAILED(aStatus)) {
 | |
|       CompleteExceptionally(aStatus);
 | |
|       return aStatus;
 | |
|     }
 | |
| 
 | |
|     mImage = aImage;
 | |
|     return mImage->StartDecoding(
 | |
|         imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY,
 | |
|         imgIContainer::FRAME_FIRST);
 | |
|   }
 | |
| 
 | |
|   // This method assumes that the image is ready to be processed
 | |
|   nsresult SendBitmap() {
 | |
|     RefPtr<gfx::SourceSurface> surface;
 | |
| 
 | |
|     NS_ENSURE_TRUE(mImage, NS_ERROR_FAILURE);
 | |
|     if (mDesiredLength > 0) {
 | |
|       surface = mImage->GetFrameAtSize(
 | |
|           gfx::IntSize(mDesiredLength, mDesiredLength),
 | |
|           imgIContainer::FRAME_FIRST,
 | |
|           imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
 | |
|     } else {
 | |
|       surface = mImage->GetFrame(
 | |
|           imgIContainer::FRAME_FIRST,
 | |
|           imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
 | |
|     }
 | |
| 
 | |
|     NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
 | |
|     RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
 | |
| 
 | |
|     NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
 | |
|     int32_t width = dataSurface->GetSize().width;
 | |
|     int32_t height = dataSurface->GetSize().height;
 | |
| 
 | |
|     DataSourceSurface::ScopedMap sourceMap(dataSurface,
 | |
|                                            DataSourceSurface::READ);
 | |
| 
 | |
|     // Android's Bitmap only supports R8G8B8A8, so we need to convert the
 | |
|     // data to the right format
 | |
|     RefPtr<DataSourceSurface> destDataSurface =
 | |
|         Factory::CreateDataSourceSurfaceWithStride(dataSurface->GetSize(),
 | |
|                                                    SurfaceFormat::R8G8B8A8,
 | |
|                                                    sourceMap.GetStride());
 | |
|     NS_ENSURE_TRUE(destDataSurface, NS_ERROR_FAILURE);
 | |
| 
 | |
|     DataSourceSurface::ScopedMap destMap(destDataSurface,
 | |
|                                          DataSourceSurface::READ_WRITE);
 | |
| 
 | |
|     SwizzleData(sourceMap.GetData(), sourceMap.GetStride(),
 | |
|                 surface->GetFormat(), destMap.GetData(), destMap.GetStride(),
 | |
|                 SurfaceFormat::R8G8B8A8, destDataSurface->GetSize());
 | |
| 
 | |
|     Complete(destMap, width, height);
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   void Notify(imgIRequest* aRequest, int32_t aType,
 | |
|               const nsIntRect* aData) override {
 | |
|     if (aType == imgINotificationObserver::DECODE_COMPLETE) {
 | |
|       nsresult status = SendBitmap();
 | |
|       if (NS_FAILED(status)) {
 | |
|         CompleteExceptionally(status);
 | |
|       }
 | |
| 
 | |
|       // Breack the cyclic reference between `ImageDecoderListener` (which is a
 | |
|       // `imgIContainer`) and `ImageCallbackHelper`.
 | |
|       mImage = nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   const java::GeckoResult::GlobalRef mResult;
 | |
|   int32_t mDesiredLength;
 | |
|   nsCOMPtr<imgIContainer> mImage;
 | |
|   virtual ~ImageCallbackHelper() {}
 | |
| };
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(ImageCallbackHelper, imgIContainerCallback,
 | |
|                   imgINotificationObserver)
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| /* static */ void ImageDecoderSupport::Decode(jni::String::Param aUri,
 | |
|                                               int32_t aDesiredLength,
 | |
|                                               jni::Object::Param aResult) {
 | |
|   auto result = java::GeckoResult::LocalRef(aResult);
 | |
|   RefPtr<ImageCallbackHelper> helper =
 | |
|       new ImageCallbackHelper(result, aDesiredLength);
 | |
| 
 | |
|   nsresult rv = DecodeInternal(aUri->ToString(), helper, helper);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     helper->OnImageReady(nullptr, rv);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */ nsresult ImageDecoderSupport::DecodeInternal(
 | |
|     const nsAString& aUri, imgIContainerCallback* aCallback,
 | |
|     imgINotificationObserver* aObserver) {
 | |
|   nsCOMPtr<imgITools> imgTools = do_GetService("@mozilla.org/image/tools;1");
 | |
|   if (NS_WARN_IF(!imgTools)) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIURI> uri;
 | |
|   nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri);
 | |
|   NS_ENSURE_SUCCESS(rv, NS_ERROR_MALFORMED_URI);
 | |
| 
 | |
|   nsCOMPtr<nsIChannel> channel;
 | |
|   rv = NS_NewChannel(getter_AddRefs(channel), uri,
 | |
|                      nsContentUtils::GetSystemPrincipal(),
 | |
|                      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
 | |
|                      nsIContentPolicy::TYPE_IMAGE);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   return imgTools->DecodeImageFromChannelAsync(uri, channel, aCallback,
 | |
|                                                aObserver);
 | |
| }
 | |
| 
 | |
| }  // namespace widget
 | |
| }  // namespace mozilla
 |