Bug 1755316 - Add audio tests with simultaneous processes r=alwu
[gecko.git] / widget / android / ImageDecoderSupport.cpp
blob9413c87b136710fe076f56bf59a2dcb7afcf748a
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"
8 #include "imgITools.h"
9 #include "imgINotificationObserver.h"
10 #include "gfxUtils.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;
20 namespace mozilla {
21 namespace widget {
23 namespace {
25 class ImageCallbackHelper;
27 HashSet<RefPtr<ImageCallbackHelper>, PointerHasher<ImageCallbackHelper*>>
28 gDecodeRequests;
30 class ImageCallbackHelper : public imgIContainerCallback,
31 public imgINotificationObserver {
32 public:
33 NS_DECL_ISUPPORTS
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,
44 int32_t height) {
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) {
57 MOZ_ASSERT(mResult);
60 NS_IMETHOD
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);
67 return aStatus;
70 mImage = aImage;
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);
86 } else {
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);
119 return NS_OK;
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`.
132 mImage = nullptr;
136 private:
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)
146 } // namespace
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);
156 if (NS_FAILED(rv)) {
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,
181 aObserver);
184 } // namespace widget
185 } // namespace mozilla