1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "ImageDecoderSupport.h"
7 #include "imgINotificationObserver.h"
9 #include "imgINotificationObserver.h"
11 #include "AndroidGraphics.h"
12 #include "JavaExceptions.h"
13 #include "mozilla/gfx/Point.h"
14 #include "mozilla/gfx/Swizzle.h"
15 #include "mozilla/java/ImageWrappers.h"
16 #include "nsNetUtil.h"
23 class ImageCallbackHelper
;
25 HashSet
<RefPtr
<ImageCallbackHelper
>, PointerHasher
<ImageCallbackHelper
*>>
28 class ImageCallbackHelper
: public imgIContainerCallback
,
29 public imgINotificationObserver
{
33 void CompleteExceptionally(nsresult aRv
) {
34 nsPrintfCString
error("Could not process image: 0x%08X", aRv
);
35 mResult
->CompleteExceptionally(
36 java::Image::ImageProcessingException::New(error
.get())
37 .Cast
<jni::Throwable
>());
38 gDecodeRequests
.remove(this);
41 void Complete(DataSourceSurface::ScopedMap
& aSourceSurface
, int32_t width
,
43 auto pixels
= mozilla::jni::ByteBuffer::New(
44 reinterpret_cast<int8_t*>(aSourceSurface
.GetData()),
45 aSourceSurface
.GetStride() * height
);
46 auto bitmap
= java::sdk::Bitmap::CreateBitmap(
47 width
, height
, java::sdk::Config::ARGB_8888());
48 bitmap
->CopyPixelsFromBuffer(pixels
);
49 mResult
->Complete(bitmap
);
50 gDecodeRequests
.remove(this);
53 ImageCallbackHelper(java::GeckoResult::Param aResult
, int32_t aDesiredLength
)
54 : mResult(aResult
), mDesiredLength(aDesiredLength
), mImage(nullptr) {
59 OnImageReady(imgIContainer
* aImage
, nsresult aStatus
) override
{
60 // Let's make sure we are alive until the request completes
61 MOZ_ALWAYS_TRUE(gDecodeRequests
.putNew(this));
63 if (NS_FAILED(aStatus
)) {
64 CompleteExceptionally(aStatus
);
69 return mImage
->StartDecoding(
70 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
,
71 imgIContainer::FRAME_FIRST
);
74 // This method assumes that the image is ready to be processed
75 nsresult
SendBitmap() {
76 RefPtr
<gfx::SourceSurface
> surface
;
78 NS_ENSURE_TRUE(mImage
, NS_ERROR_FAILURE
);
79 if (mDesiredLength
> 0) {
80 surface
= mImage
->GetFrameAtSize(
81 gfx::IntSize(mDesiredLength
, mDesiredLength
),
82 imgIContainer::FRAME_FIRST
,
83 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
);
85 surface
= mImage
->GetFrame(
86 imgIContainer::FRAME_FIRST
,
87 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
);
90 NS_ENSURE_TRUE(surface
, NS_ERROR_FAILURE
);
91 RefPtr
<DataSourceSurface
> dataSurface
= surface
->GetDataSurface();
93 NS_ENSURE_TRUE(dataSurface
, NS_ERROR_FAILURE
);
94 int32_t width
= dataSurface
->GetSize().width
;
95 int32_t height
= dataSurface
->GetSize().height
;
97 DataSourceSurface::ScopedMap
sourceMap(dataSurface
,
98 DataSourceSurface::READ
);
100 // Android's Bitmap only supports R8G8B8A8, so we need to convert the
101 // data to the right format
102 RefPtr
<DataSourceSurface
> destDataSurface
=
103 Factory::CreateDataSourceSurfaceWithStride(dataSurface
->GetSize(),
104 SurfaceFormat::R8G8B8A8
,
105 sourceMap
.GetStride());
106 NS_ENSURE_TRUE(destDataSurface
, NS_ERROR_FAILURE
);
108 DataSourceSurface::ScopedMap
destMap(destDataSurface
,
109 DataSourceSurface::READ_WRITE
);
111 SwizzleData(sourceMap
.GetData(), sourceMap
.GetStride(),
112 surface
->GetFormat(), destMap
.GetData(), destMap
.GetStride(),
113 SurfaceFormat::R8G8B8A8
, destDataSurface
->GetSize());
115 Complete(destMap
, width
, height
);
120 void Notify(imgIRequest
* aRequest
, int32_t aType
,
121 const nsIntRect
* aData
) override
{
122 if (aType
== imgINotificationObserver::DECODE_COMPLETE
) {
123 nsresult status
= SendBitmap();
124 if (NS_FAILED(status
)) {
125 CompleteExceptionally(status
);
128 // Breack the cyclic reference between `ImageDecoderListener` (which is a
129 // `imgIContainer`) and `ImageCallbackHelper`.
135 const java::GeckoResult::GlobalRef mResult
;
136 int32_t mDesiredLength
;
137 nsCOMPtr
<imgIContainer
> mImage
;
138 virtual ~ImageCallbackHelper() {}
141 NS_IMPL_ISUPPORTS(ImageCallbackHelper
, imgIContainerCallback
,
142 imgINotificationObserver
)
146 /* static */ void ImageDecoderSupport::Decode(jni::String::Param aUri
,
147 int32_t aDesiredLength
,
148 jni::Object::Param aResult
) {
149 auto result
= java::GeckoResult::LocalRef(aResult
);
150 RefPtr
<ImageCallbackHelper
> helper
=
151 new ImageCallbackHelper(result
, aDesiredLength
);
153 nsresult rv
= DecodeInternal(aUri
->ToString(), helper
, helper
);
155 helper
->OnImageReady(nullptr, rv
);
159 /* static */ nsresult
ImageDecoderSupport::DecodeInternal(
160 const nsAString
& aUri
, imgIContainerCallback
* aCallback
,
161 imgINotificationObserver
* aObserver
) {
162 nsCOMPtr
<imgITools
> imgTools
= do_GetService("@mozilla.org/image/tools;1");
163 if (NS_WARN_IF(!imgTools
)) {
164 return NS_ERROR_FAILURE
;
167 nsCOMPtr
<nsIURI
> uri
;
168 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aUri
);
169 NS_ENSURE_SUCCESS(rv
, NS_ERROR_MALFORMED_URI
);
171 nsCOMPtr
<nsIChannel
> channel
;
172 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
,
173 nsContentUtils::GetSystemPrincipal(),
174 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
,
175 nsIContentPolicy::TYPE_IMAGE
);
176 NS_ENSURE_SUCCESS(rv
, rv
);
178 return imgTools
->DecodeImageFromChannelAsync(uri
, channel
, aCallback
,
182 } // namespace widget
183 } // namespace mozilla