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"
18 using namespace mozilla::gfx
;
25 class ImageCallbackHelper
;
27 HashSet
<RefPtr
<ImageCallbackHelper
>, PointerHasher
<ImageCallbackHelper
*>>
30 class ImageCallbackHelper
: public imgIContainerCallback
,
31 public imgINotificationObserver
{
35 void CompleteExceptionally(nsresult aRv
) {
36 nsPrintfCString
error("Could not process image: 0x%08X", aRv
);
37 mResult
->CompleteExceptionally(
38 java::Image::ImageProcessingException::New(error
.get())
39 .Cast
<jni::Throwable
>());
40 gDecodeRequests
.remove(this);
43 void Complete(DataSourceSurface::ScopedMap
& aSourceSurface
, int32_t width
,
45 auto pixels
= mozilla::jni::ByteBuffer::New(
46 reinterpret_cast<int8_t*>(aSourceSurface
.GetData()),
47 aSourceSurface
.GetStride() * height
);
48 auto bitmap
= java::sdk::Bitmap::CreateBitmap(
49 width
, height
, java::sdk::Config::ARGB_8888());
50 bitmap
->CopyPixelsFromBuffer(pixels
);
51 mResult
->Complete(bitmap
);
52 gDecodeRequests
.remove(this);
55 ImageCallbackHelper(java::GeckoResult::Param aResult
, int32_t aDesiredLength
)
56 : mResult(aResult
), mDesiredLength(aDesiredLength
), mImage(nullptr) {
61 OnImageReady(imgIContainer
* aImage
, nsresult aStatus
) override
{
62 // Let's make sure we are alive until the request completes
63 MOZ_ALWAYS_TRUE(gDecodeRequests
.putNew(this));
65 if (NS_FAILED(aStatus
)) {
66 CompleteExceptionally(aStatus
);
71 return mImage
->StartDecoding(
72 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
,
73 imgIContainer::FRAME_FIRST
);
76 // This method assumes that the image is ready to be processed
77 nsresult
SendBitmap() {
78 RefPtr
<gfx::SourceSurface
> surface
;
80 NS_ENSURE_TRUE(mImage
, NS_ERROR_FAILURE
);
81 if (mDesiredLength
> 0) {
82 surface
= mImage
->GetFrameAtSize(
83 gfx::IntSize(mDesiredLength
, mDesiredLength
),
84 imgIContainer::FRAME_FIRST
,
85 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
);
87 surface
= mImage
->GetFrame(
88 imgIContainer::FRAME_FIRST
,
89 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
);
92 NS_ENSURE_TRUE(surface
, NS_ERROR_FAILURE
);
93 RefPtr
<DataSourceSurface
> dataSurface
= surface
->GetDataSurface();
95 NS_ENSURE_TRUE(dataSurface
, NS_ERROR_FAILURE
);
96 int32_t width
= dataSurface
->GetSize().width
;
97 int32_t height
= dataSurface
->GetSize().height
;
99 DataSourceSurface::ScopedMap
sourceMap(dataSurface
,
100 DataSourceSurface::READ
);
102 // Android's Bitmap only supports R8G8B8A8, so we need to convert the
103 // data to the right format
104 RefPtr
<DataSourceSurface
> destDataSurface
=
105 Factory::CreateDataSourceSurfaceWithStride(dataSurface
->GetSize(),
106 SurfaceFormat::R8G8B8A8
,
107 sourceMap
.GetStride());
108 NS_ENSURE_TRUE(destDataSurface
, NS_ERROR_FAILURE
);
110 DataSourceSurface::ScopedMap
destMap(destDataSurface
,
111 DataSourceSurface::READ_WRITE
);
113 SwizzleData(sourceMap
.GetData(), sourceMap
.GetStride(),
114 surface
->GetFormat(), destMap
.GetData(), destMap
.GetStride(),
115 SurfaceFormat::R8G8B8A8
, destDataSurface
->GetSize());
117 Complete(destMap
, width
, height
);
122 void Notify(imgIRequest
* aRequest
, int32_t aType
,
123 const nsIntRect
* aData
) override
{
124 if (aType
== imgINotificationObserver::DECODE_COMPLETE
) {
125 nsresult status
= SendBitmap();
126 if (NS_FAILED(status
)) {
127 CompleteExceptionally(status
);
130 // Breack the cyclic reference between `ImageDecoderListener` (which is a
131 // `imgIContainer`) and `ImageCallbackHelper`.
137 const java::GeckoResult::GlobalRef mResult
;
138 int32_t mDesiredLength
;
139 nsCOMPtr
<imgIContainer
> mImage
;
140 virtual ~ImageCallbackHelper() {}
143 NS_IMPL_ISUPPORTS(ImageCallbackHelper
, imgIContainerCallback
,
144 imgINotificationObserver
)
148 /* static */ void ImageDecoderSupport::Decode(jni::String::Param aUri
,
149 int32_t aDesiredLength
,
150 jni::Object::Param aResult
) {
151 auto result
= java::GeckoResult::LocalRef(aResult
);
152 RefPtr
<ImageCallbackHelper
> helper
=
153 new ImageCallbackHelper(result
, aDesiredLength
);
155 nsresult rv
= DecodeInternal(aUri
->ToString(), helper
, helper
);
157 helper
->OnImageReady(nullptr, rv
);
161 /* static */ nsresult
ImageDecoderSupport::DecodeInternal(
162 const nsAString
& aUri
, imgIContainerCallback
* aCallback
,
163 imgINotificationObserver
* aObserver
) {
164 nsCOMPtr
<imgITools
> imgTools
= do_GetService("@mozilla.org/image/tools;1");
165 if (NS_WARN_IF(!imgTools
)) {
166 return NS_ERROR_FAILURE
;
169 nsCOMPtr
<nsIURI
> uri
;
170 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aUri
);
171 NS_ENSURE_SUCCESS(rv
, NS_ERROR_MALFORMED_URI
);
173 nsCOMPtr
<nsIChannel
> channel
;
174 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
,
175 nsContentUtils::GetSystemPrincipal(),
176 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
,
177 nsIContentPolicy::TYPE_IMAGE
);
178 NS_ENSURE_SUCCESS(rv
, rv
);
180 return imgTools
->DecodeImageFromChannelAsync(uri
, channel
, aCallback
,
184 } // namespace widget
185 } // namespace mozilla