Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(411)

Unified Diff: third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp

Issue 2448193002: ShapeDetection: support CanvasImageSource as detect() source (Closed)
Patch Set: esprehn@ comments and specific LayoutTest for empty HTMLImageElement Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
diff --git a/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp b/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
index 7ad81a6bf0ec83d632043f08af84645e0bd0a8a9..882dc6214fcdbcd3e6a86c5ab33da618b6374e33 100644
--- a/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
+++ b/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
@@ -8,34 +8,102 @@
#include "core/dom/DOMRect.h"
#include "core/dom/Document.h"
#include "core/fetch/ImageResource.h"
+#include "core/frame/ImageBitmap.h"
#include "core/frame/LocalDOMWindow.h"
#include "core/frame/LocalFrame.h"
#include "core/html/HTMLImageElement.h"
+#include "core/html/canvas/CanvasImageSource.h"
#include "platform/graphics/Image.h"
#include "public/platform/InterfaceProvider.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkImageInfo.h"
+#include "wtf/CheckedNumeric.h"
namespace blink {
namespace {
-mojo::ScopedSharedBufferHandle getSharedBufferHandle(
- const HTMLImageElement* img,
- ScriptPromiseResolver* resolver) {
+static CanvasImageSource* toImageSourceInternal(
+ const CanvasImageSourceUnion& value) {
+ if (value.isHTMLImageElement())
+ return value.getAsHTMLImageElement();
+
+ if (value.isImageBitmap()) {
+ if (static_cast<ImageBitmap*>(value.getAsImageBitmap())->isNeutered())
+ return nullptr;
+ return value.getAsImageBitmap();
+ }
+
+ return nullptr;
+}
+
+} // anonymous namespace
+
+FaceDetector* FaceDetector::create(ScriptState* scriptState) {
+ return new FaceDetector(*scriptState->domWindow()->frame());
+}
+
+FaceDetector::FaceDetector(LocalFrame& frame) {
+ DCHECK(!m_service.is_bound());
+ DCHECK(frame.interfaceProvider());
+ frame.interfaceProvider()->getInterface(mojo::GetProxy(&m_service));
+}
+
+ScriptPromise FaceDetector::detect(ScriptState* scriptState,
+ const CanvasImageSourceUnion& imageSource) {
+ CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource);
+
+ ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
+ ScriptPromise promise = resolver->promise();
+
+ if (!imageSourceInternal) {
+ // TODO(mcasas): Implement more CanvasImageSources, https://crbug.com/659138
+ NOTIMPLEMENTED() << "Unsupported CanvasImageSource";
+ resolver->reject(
+ DOMException::create(NotFoundError, "Unsupported source."));
+ return promise;
+ }
+
+ if (imageSourceInternal->wouldTaintOrigin(
+ scriptState->getExecutionContext()->getSecurityOrigin())) {
+ resolver->reject(DOMException::create(SecurityError,
+ "Image source would taint origin."));
+ return promise;
+ }
+
+ if (imageSource.isHTMLImageElement()) {
+ return detectFacesOnImageElement(
+ resolver, static_cast<HTMLImageElement*>(imageSourceInternal));
+ }
+ if (imageSourceInternal->isImageBitmap()) {
+ return detectFacesOnImageBitmap(
+ resolver, static_cast<ImageBitmap*>(imageSourceInternal));
+ }
+ NOTREACHED();
+ return promise;
+}
+
+ScriptPromise FaceDetector::detectFacesOnImageElement(
+ ScriptPromiseResolver* resolver,
+ const HTMLImageElement* img) {
+ ScriptPromise promise = resolver->promise();
+ if (img->bitmapSourceSize().isZero()) {
+ resolver->resolve(HeapVector<Member<DOMRect>>());
+ return promise;
+ }
+
ImageResource* const imageResource = img->cachedImage();
- // TODO(xianglu): Add test case for undecodable images.
if (!imageResource || imageResource->errorOccurred()) {
resolver->reject(DOMException::create(
InvalidStateError, "Failed to load or decode HTMLImageElement."));
- return mojo::ScopedSharedBufferHandle();
+ return promise;
}
Image* const blinkImage = imageResource->getImage();
if (!blinkImage) {
resolver->reject(DOMException::create(
InvalidStateError, "Failed to get image from resource."));
- return mojo::ScopedSharedBufferHandle();
+ return promise;
}
const sk_sp<SkImage> image = blinkImage->imageForCurrentFrame();
@@ -45,7 +113,7 @@ mojo::ScopedSharedBufferHandle getSharedBufferHandle(
if (!image) {
resolver->reject(DOMException::create(
InvalidStateError, "Failed to get image from current frame."));
- return mojo::ScopedSharedBufferHandle();
+ return promise;
}
const SkImageInfo skiaInfo =
@@ -60,10 +128,10 @@ mojo::ScopedSharedBufferHandle getSharedBufferHandle(
<< "B, larger than |mojo::edk::kMaxSharedBufferSize| == 16MB ";
// TODO(xianglu): For now we reject the promise if the image is too large.
// But consider resizing the image to remove restriction on the user side.
- // Also, add layouttest for this case later.
+ // Also, add LayoutTests for this case later.
resolver->reject(
DOMException::create(InvalidStateError, "Image exceeds size limit."));
- return mojo::ScopedSharedBufferHandle();
+ return promise;
}
const mojo::ScopedSharedBufferMapping mappedBuffer =
@@ -74,58 +142,84 @@ mojo::ScopedSharedBufferHandle getSharedBufferHandle(
resolver->reject(DOMException::create(
InvalidStateError,
"Failed to read pixels: Unable to decompress or unsupported format."));
- return mojo::ScopedSharedBufferHandle();
+ return promise;
}
- return sharedBufferHandle;
-}
-
-} // anonymous namespace
-
-FaceDetector* FaceDetector::create(ScriptState* scriptState) {
- return new FaceDetector(*scriptState->domWindow()->frame());
-}
+ if (!m_service) {
+ resolver->reject(DOMException::create(
+ NotSupportedError, "Face detection service unavailable."));
+ return promise;
+ }
-FaceDetector::FaceDetector(LocalFrame& frame) {
- DCHECK(!m_service.is_bound());
- DCHECK(frame.interfaceProvider());
- frame.interfaceProvider()->getInterface(mojo::GetProxy(&m_service));
+ m_serviceRequests.add(resolver);
+ DCHECK(m_service.is_bound());
+ m_service->DetectFace(std::move(sharedBufferHandle), img->naturalWidth(),
+ img->naturalHeight(),
+ convertToBaseCallback(WTF::bind(
+ &FaceDetector::onDetectFace, wrapPersistent(this),
+ wrapPersistent(resolver))));
+ return promise;
}
-ScriptPromise FaceDetector::detect(ScriptState* scriptState,
- const HTMLImageElement* img) {
- ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
+ScriptPromise FaceDetector::detectFacesOnImageBitmap(
+ ScriptPromiseResolver* resolver,
+ ImageBitmap* imageBitmap) {
ScriptPromise promise = resolver->promise();
-
- // TODO(xianglu): Add test cases for cross-origin-images.
- if (img->wouldTaintOrigin(
- scriptState->getExecutionContext()->getSecurityOrigin())) {
- resolver->reject(DOMException::create(
- SecurityError, "Image source from a different origin."));
+ if (!imageBitmap->originClean()) {
+ resolver->reject(
+ DOMException::create(SecurityError, "ImageBitmap is not origin clean"));
return promise;
}
- if (img->bitmapSourceSize().isZero()) {
- resolver->reject(
- DOMException::create(InvalidStateError, "HTMLImageElement is empty."));
+ if (imageBitmap->size().area() == 0) {
+ resolver->resolve(HeapVector<Member<DOMRect>>());
return promise;
}
+ SkPixmap pixmap;
+ RefPtr<Uint8Array> pixelData;
+ uint8_t* pixelDataPtr = nullptr;
+ WTF::CheckedNumeric<int> allocationSize = 0;
+ // Use |skImage|'s pixels if it has direct access to them, otherwise retrieve
+ // them from elsewhere via copyBitmapData().
+ sk_sp<SkImage> skImage = imageBitmap->bitmapImage()->imageForCurrentFrame();
+ if (skImage->peekPixels(&pixmap)) {
+ pixelDataPtr = static_cast<uint8_t*>(pixmap.writable_addr());
+ allocationSize = pixmap.getSafeSize();
+ } else {
+ pixelData = imageBitmap->copyBitmapData(imageBitmap->isPremultiplied()
+ ? PremultiplyAlpha
+ : DontPremultiplyAlpha,
+ N32ColorType);
+ pixelDataPtr = pixelData->data();
+ allocationSize = imageBitmap->size().area() * 4 /* bytes per pixel */;
+ }
+
mojo::ScopedSharedBufferHandle sharedBufferHandle =
- getSharedBufferHandle(img, resolver);
- if (!sharedBufferHandle->is_valid())
+ mojo::SharedBufferHandle::Create(allocationSize.ValueOrDefault(0));
+
+ if (!pixelDataPtr || !sharedBufferHandle->is_valid()) {
+ resolver->reject(
+ DOMException::create(InvalidStateError, "Internal allocation error"));
return promise;
+ }
if (!m_service) {
resolver->reject(DOMException::create(
- NotSupportedError, "Face detection service unavailable."));
+ NotFoundError, "Face detection service unavailable."));
return promise;
}
+ const mojo::ScopedSharedBufferMapping mappedBuffer =
+ sharedBufferHandle->Map(allocationSize.ValueOrDefault(0));
+ DCHECK(mappedBuffer.get());
+
+ memcpy(mappedBuffer.get(), pixelDataPtr, allocationSize.ValueOrDefault(0));
+
m_serviceRequests.add(resolver);
DCHECK(m_service.is_bound());
- m_service->DetectFace(std::move(sharedBufferHandle), img->naturalWidth(),
- img->naturalHeight(),
+ m_service->DetectFace(std::move(sharedBufferHandle), imageBitmap->width(),
+ imageBitmap->height(),
convertToBaseCallback(WTF::bind(
&FaceDetector::onDetectFace, wrapPersistent(this),
wrapPersistent(resolver))));

Powered by Google App Engine
This is Rietveld 408576698