зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1530402
- Provide GeckoImageDecoder. r=snorp
This class allows GeckoView embedders to decode images using Gecko's image library. Differential Revision: https://phabricator.services.mozilla.com/D49038 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
f1fcfd9cc9
Коммит
d917224183
|
@ -0,0 +1,82 @@
|
|||
package org.mozilla.geckoview;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.support.annotation.AnyThread;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.mozilla.gecko.GeckoThread;
|
||||
import org.mozilla.gecko.annotation.WrapForJNI;
|
||||
|
||||
/**
|
||||
* Provides access to Gecko's Image processing library.
|
||||
*/
|
||||
@AnyThread
|
||||
/* protected */ class ImageDecoder {
|
||||
private static ImageDecoder instance;
|
||||
|
||||
private ImageDecoder() {}
|
||||
|
||||
public static ImageDecoder instance() {
|
||||
if (instance == null) {
|
||||
instance = new ImageDecoder();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@WrapForJNI(dispatchTo = "gecko", stubName = "Decode")
|
||||
private static native void nativeDecode(final String uri, final int desiredLength,
|
||||
GeckoResult<Bitmap> result);
|
||||
|
||||
/**
|
||||
* Fetches and decodes an image at the specified location.
|
||||
* This method supports SVG, PNG, Bitmap and other formats supported by Gecko.
|
||||
*
|
||||
* @param uri location of the image. Can be either a remote https:// location, file:/// if the
|
||||
* file is local or a resource://android/ if the file is located inside the APK.
|
||||
*
|
||||
* e.g. if the image file is locate at /assets/test.png inside the apk, set the uri
|
||||
* to resource://android/assets/test.png.
|
||||
* @return A {@link GeckoResult} to the decoded image.
|
||||
*/
|
||||
@NonNull
|
||||
public GeckoResult<Bitmap> decode(final @NonNull String uri) {
|
||||
return decode(uri, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches and decodes an image at the specified location and resizes it to the desired length.
|
||||
* This method supports SVG, PNG, Bitmap and other formats supported by Gecko.
|
||||
*
|
||||
* Note: The final size might differ slightly from the requested output.
|
||||
*
|
||||
* @param uri location of the image. Can be either a remote https:// location, file:/// if the
|
||||
* file is local or a resource://android/ if the file is located inside the APK.
|
||||
*
|
||||
* e.g. if the image file is locate at /assets/test.png inside the apk, set the uri
|
||||
* to resource://android/assets/test.png.
|
||||
* @param desiredLength Longest size for the image in device pixel units. The resulting image
|
||||
* might be slightly different if the image cannot be resized efficiently.
|
||||
* If desiredLength is 0 then the image will be decoded to its natural
|
||||
* size.
|
||||
* @return A {@link GeckoResult} to the decoded image.
|
||||
*/
|
||||
@NonNull
|
||||
public GeckoResult<Bitmap> decode(final @NonNull String uri, final int desiredLength) {
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("Uri cannot be null");
|
||||
}
|
||||
|
||||
final GeckoResult<Bitmap> result = new GeckoResult<>();
|
||||
|
||||
if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
|
||||
nativeDecode(uri, desiredLength, result);
|
||||
} else {
|
||||
GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY, this,
|
||||
"nativeDecode", String.class, uri, int.class, desiredLength,
|
||||
GeckoResult.class, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/* 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 "imgITools.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "AndroidGraphics.h"
|
||||
#include "JavaExceptions.h"
|
||||
#include "mozilla/gfx/Point.h"
|
||||
#include "mozilla/gfx/Swizzle.h"
|
||||
|
||||
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(const char* aMessage) {
|
||||
mResult->CompleteExceptionally(java::sdk::IllegalArgumentException::New(aMessage)
|
||||
.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::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("Could not process image.");
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Notify(imgIRequest* aRequest, int32_t aType,
|
||||
const nsIntRect* aData) override {
|
||||
if (aType == imgINotificationObserver::DECODE_COMPLETE) {
|
||||
SendBitmap();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
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_DATA_IS_NULL,
|
||||
nsIContentPolicy::TYPE_IMAGE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return imgTools->DecodeImageFromChannelAsync(uri, channel, aCallback,
|
||||
aObserver);
|
||||
}
|
||||
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,30 @@
|
|||
/* 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/. */
|
||||
|
||||
#ifndef ImageDecoderSupport_h__
|
||||
#define ImageDecoderSupport_h__
|
||||
|
||||
#include "GeneratedJNINatives.h"
|
||||
|
||||
class imgIContainerCallback;
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
||||
class ImageDecoderSupport final
|
||||
: public java::ImageDecoder::Natives<ImageDecoderSupport> {
|
||||
public:
|
||||
static void Decode(jni::String::Param aUri, int32_t aDesiredLength,
|
||||
jni::Object::Param aResult);
|
||||
|
||||
private:
|
||||
static nsresult DecodeInternal(const nsAString& aUri,
|
||||
imgIContainerCallback* aCallback,
|
||||
imgINotificationObserver* aObserver);
|
||||
};
|
||||
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ImageDecoderSupport_h__
|
|
@ -1,2 +1,5 @@
|
|||
[java.lang.IllegalStateException = skip:true]
|
||||
<init>(Ljava/lang/String;)V =
|
||||
<init>(Ljava/lang/String;)V =
|
||||
|
||||
[java.lang.IllegalArgumentException = skip:true]
|
||||
<init>(Ljava/lang/String;)V =
|
||||
|
|
|
@ -51,6 +51,7 @@ UNIFIED_SOURCES += [
|
|||
'EventDispatcher.cpp',
|
||||
'GeckoEditableSupport.cpp',
|
||||
'GfxInfo.cpp',
|
||||
'ImageDecoderSupport.cpp',
|
||||
'nsAndroidProtocolHandler.cpp',
|
||||
'nsAppShell.cpp',
|
||||
'nsClipboard.cpp',
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
#include "GeckoSystemStateListener.h"
|
||||
#include "GeckoTelemetryDelegate.h"
|
||||
#include "GeckoVRManager.h"
|
||||
#include "ImageDecoderSupport.h"
|
||||
#include "PrefsHelper.h"
|
||||
#include "ScreenHelperAndroid.h"
|
||||
#include "Telemetry.h"
|
||||
|
@ -434,6 +435,7 @@ nsAppShell::nsAppShell()
|
|||
mozilla::GeckoSystemStateListener::Init();
|
||||
mozilla::PrefsHelper::Init();
|
||||
mozilla::widget::Telemetry::Init();
|
||||
mozilla::widget::ImageDecoderSupport::Init();
|
||||
mozilla::widget::WebExecutorSupport::Init();
|
||||
mozilla::widget::Base64UtilsSupport::Init();
|
||||
nsWindow::InitNatives();
|
||||
|
|
Загрузка…
Ссылка в новой задаче