Commit 4db64ed1 authored by junweifu's avatar junweifu Committed by Commit Bot

ShapeDetection: Start the minimum implementation of mojom::BarcodeDetection

The new implementation is based on Mac OS Vision Framework[1] which recognizes
more barcode symbologies, then use the @available(macOS 10.13, *) in the barcode
detection provider to choose different implementation.

Split original large CL[2] up in smaller subpatches including this CL.

[1] https://developer.apple.com/documentation/vision
[2] https://chromium-review.googlesource.com/c/chromium/src/+/1088466

BUG=848182

Cq-Include-Trybots: luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win10_chromium_x64_rel_ng
Change-Id: I5cb8634240a35e52b3c0b696c1ccab9c24cd023e
Reviewed-on: https://chromium-review.googlesource.com/1107493
Commit-Queue: Junwei Fu <junwei.fu@intel.com>
Reviewed-by: default avatarMiguel Casas <mcasas@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#570336}
parent c0d690f1
......@@ -17,6 +17,8 @@ source_set("lib") {
sources += [
"barcode_detection_impl_mac.h",
"barcode_detection_impl_mac.mm",
"barcode_detection_impl_mac_vision.h",
"barcode_detection_impl_mac_vision.mm",
"barcode_detection_provider_mac.h",
"barcode_detection_provider_mac.mm",
"detection_utils_mac.h",
......
......@@ -4,6 +4,11 @@
#include "services/shape_detection/barcode_detection_impl_mac.h"
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/callback_forward.h"
#include "base/command_line.h"
#include "base/mac/mac_util.h"
#include "base/mac/scoped_cftyperef.h"
......@@ -11,84 +16,143 @@
#include "base/mac/sdk_forward_declarations.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "services/shape_detection/barcode_detection_impl_mac_vision.h"
#include "services/shape_detection/public/mojom/barcodedetection.mojom.h"
#include "services/shape_detection/public/mojom/barcodedetection_provider.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/utils/mac/SkCGUtils.h"
#include "ui/gl/gl_switches.h"
using ::testing::TestWithParam;
using ::testing::ValuesIn;
namespace shape_detection {
namespace {
ACTION_P(RunClosure, closure) {
closure.Run();
}
class BarcodeDetectionImplMacTest : public ::testing::Test {
std::unique_ptr<mojom::BarcodeDetection> CreateBarcodeDetectorImplMac(
mojom::BarcodeDetectorOptionsPtr options) {
if (@available(macOS 10.10, *)) {
return std::make_unique<BarcodeDetectionImplMac>();
}
return nullptr;
}
std::unique_ptr<mojom::BarcodeDetection> CreateBarcodeDetectorImplMacVision(
mojom::BarcodeDetectorOptionsPtr options) {
if (@available(macOS 10.13, *)) {
return std::make_unique<BarcodeDetectionImplMacVision>(
mojom::BarcodeDetectorOptions::New());
}
return nullptr;
}
using BarcodeDetectorFactory =
base::Callback<std::unique_ptr<mojom::BarcodeDetection>(
mojom::BarcodeDetectorOptionsPtr)>;
const std::string kInfoString = "https://www.chromium.org";
struct TestParams {
size_t num_barcodes;
const std::string barcode_value;
BarcodeDetectorFactory factory;
} kTestParams[] = {
{1, kInfoString, base::Bind(&CreateBarcodeDetectorImplMac)},
// TODO(junwei.fu): add more tests https://crbug.com/848182.
{0, "", base::Bind(&CreateBarcodeDetectorImplMacVision)},
};
}
class BarcodeDetectionImplMacTest : public TestWithParam<struct TestParams> {
public:
~BarcodeDetectionImplMacTest() override = default;
void DetectCallback(std::vector<mojom::BarcodeDetectionResultPtr> results) {
Detection(results.size(), results.empty() ? "" : results[0]->raw_value);
void DetectCallback(size_t num_barcodes,
const std::string& barcode_value,
std::vector<mojom::BarcodeDetectionResultPtr> results) {
EXPECT_EQ(num_barcodes, results.size());
for (const auto& barcode : results)
EXPECT_EQ(barcode_value, barcode->raw_value);
Detection();
}
MOCK_METHOD2(Detection, void(size_t, const std::string&));
MOCK_METHOD0(Detection, void(void));
API_AVAILABLE(macosx(10.10)) std::unique_ptr<BarcodeDetectionImplMac> impl_;
std::unique_ptr<mojom::BarcodeDetection> impl_;
const base::MessageLoop message_loop_;
};
TEST_F(BarcodeDetectionImplMacTest, CreateAndDestroy) {}
TEST_P(BarcodeDetectionImplMacTest, CreateAndDestroy) {
impl_ = GetParam().factory.Run(mojom::BarcodeDetectorOptions::New());
if (!impl_) {
LOG(WARNING) << "Barcode Detection is not supported before Mac OSX 10.10."
<< "Skipping test.";
return;
}
}
// This test generates a single QR code and scans it back.
TEST_F(BarcodeDetectionImplMacTest, ScanOneBarcode) {
TEST_P(BarcodeDetectionImplMacTest, ScanOneBarcode) {
// Barcode detection needs at least MAC OS X 10.10, and GPU infrastructure.
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kUseGpuInTests)) {
return;
}
if (@available(macOS 10.10, *)) {
impl_.reset(new BarcodeDetectionImplMac());
const std::string kInfoString = "https://www.chromium.org";
// Generate a QR code image as a CIImage by using |qr_code_generator|.
NSData* const qr_code_data =
[[NSString stringWithUTF8String:kInfoString.c_str()]
dataUsingEncoding:NSISOLatin1StringEncoding];
// TODO(mcasas): Consider using other generator types (e.g.
// CI{AztecCode,Code128Barcode,PDF417Barcode}Generator) when the minimal OS
// X is upgraded to 10.10+ (https://crbug.com/624049).
CIFilter* qr_code_generator =
[CIFilter filterWithName:@"CIQRCodeGenerator"];
[qr_code_generator setValue:qr_code_data forKey:@"inputMessage"];
// [CIImage outputImage] is available in macOS 10.10+. Could be added to
// sdk_forward_declarations.h but seems hardly worth it.
EXPECT_TRUE([qr_code_generator respondsToSelector:@selector(outputImage)]);
CIImage* qr_code_image =
[qr_code_generator performSelector:@selector(outputImage)];
const gfx::Size size([qr_code_image extent].size.width,
[qr_code_image extent].size.height);
base::scoped_nsobject<CIContext> context([[CIContext alloc] init]);
base::ScopedCFTypeRef<CGImageRef> cg_image(
[context createCGImage:qr_code_image fromRect:[qr_code_image extent]]);
EXPECT_EQ(static_cast<size_t>(size.width()), CGImageGetWidth(cg_image));
EXPECT_EQ(static_cast<size_t>(size.height()), CGImageGetHeight(cg_image));
SkBitmap bitmap;
ASSERT_TRUE(SkCreateBitmapFromCGImage(&bitmap, cg_image));
base::RunLoop run_loop;
base::Closure quit_closure = run_loop.QuitClosure();
// Send the image Detect() and expect the response in callback.
EXPECT_CALL(*this, Detection(1, kInfoString))
.WillOnce(RunClosure(quit_closure));
impl_->Detect(bitmap,
base::Bind(&BarcodeDetectionImplMacTest::DetectCallback,
base::Unretained(this)));
run_loop.Run();
impl_ = GetParam().factory.Run(mojom::BarcodeDetectorOptions::New());
if (!impl_) {
LOG(WARNING) << "Barcode Detection is not supported before Mac OSX 10.10."
<< "Skipping test.";
return;
}
// Generate a QR code image as a CIImage by using |qr_code_generator|.
NSData* const qr_code_data =
[[NSString stringWithUTF8String:kInfoString.c_str()]
dataUsingEncoding:NSISOLatin1StringEncoding];
// TODO(mcasas): Consider using other generator types (e.g.
// CI{AztecCode,Code128Barcode,PDF417Barcode}Generator) when the minimal OS
// X is upgraded to 10.10+ (https://crbug.com/624049).
CIFilter* qr_code_generator = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[qr_code_generator setValue:qr_code_data forKey:@"inputMessage"];
// [CIImage outputImage] is available in macOS 10.10+. Could be added to
// sdk_forward_declarations.h but seems hardly worth it.
EXPECT_TRUE([qr_code_generator respondsToSelector:@selector(outputImage)]);
CIImage* qr_code_image =
[qr_code_generator performSelector:@selector(outputImage)];
const gfx::Size size([qr_code_image extent].size.width,
[qr_code_image extent].size.height);
base::scoped_nsobject<CIContext> context([[CIContext alloc] init]);
base::ScopedCFTypeRef<CGImageRef> cg_image(
[context createCGImage:qr_code_image fromRect:[qr_code_image extent]]);
EXPECT_EQ(static_cast<size_t>(size.width()), CGImageGetWidth(cg_image));
EXPECT_EQ(static_cast<size_t>(size.height()), CGImageGetHeight(cg_image));
SkBitmap bitmap;
ASSERT_TRUE(SkCreateBitmapFromCGImage(&bitmap, cg_image));
base::RunLoop run_loop;
base::Closure quit_closure = run_loop.QuitClosure();
// Send the image Detect() and expect the response in callback.
EXPECT_CALL(*this, Detection()).WillOnce(RunClosure(quit_closure));
impl_->Detect(bitmap,
base::Bind(&BarcodeDetectionImplMacTest::DetectCallback,
base::Unretained(this), GetParam().num_barcodes,
GetParam().barcode_value));
run_loop.Run();
}
INSTANTIATE_TEST_CASE_P(, BarcodeDetectionImplMacTest, ValuesIn(kTestParams));
} // shape_detection namespace
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SERVICES_SHAPE_DETECTION_BARCODE_DETECTION_IMPL_MAC_VISION_H_
#define SERVICES_SHAPE_DETECTION_BARCODE_DETECTION_IMPL_MAC_VISION_H_
#include "base/mac/availability.h"
#include "base/macros.h"
#include "services/shape_detection/public/mojom/barcodedetection.mojom.h"
#include "services/shape_detection/public/mojom/barcodedetection_provider.mojom.h"
class SkBitmap;
namespace shape_detection {
// This class is the implementation of Barcode Detection based on Mac OS Vision
// framework(https://developer.apple.com/documentation/vision).
class API_AVAILABLE(macos(10.13)) BarcodeDetectionImplMacVision
: public mojom::BarcodeDetection {
public:
explicit BarcodeDetectionImplMacVision(
mojom::BarcodeDetectorOptionsPtr options);
~BarcodeDetectionImplMacVision() override;
void Detect(const SkBitmap& bitmap,
mojom::BarcodeDetection::DetectCallback callback) override;
private:
DISALLOW_COPY_AND_ASSIGN(BarcodeDetectionImplMacVision);
};
} // namespace shape_detection
#endif // SERVICES_SHAPE_DETECTION_BARCODE_DETECTION_IMPL_MAC_VISION_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/shape_detection/barcode_detection_impl_mac_vision.h"
#include <Foundation/Foundation.h>
#include <utility>
#include "base/logging.h"
namespace shape_detection {
BarcodeDetectionImplMacVision::BarcodeDetectionImplMacVision(
mojom::BarcodeDetectorOptionsPtr options) {
Class request_class = NSClassFromString(@"VNDetectBarcodesRequest");
if (!request_class) {
DLOG(ERROR) << "Failed to load VNDetectBarcodesRequest class";
return;
}
}
BarcodeDetectionImplMacVision::~BarcodeDetectionImplMacVision() = default;
void BarcodeDetectionImplMacVision::Detect(const SkBitmap& bitmap,
DetectCallback callback) {
std::move(callback).Run({});
}
} // namespace shape_detection
......@@ -10,6 +10,7 @@
#include "base/logging.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "services/shape_detection/barcode_detection_impl_mac.h"
#include "services/shape_detection/barcode_detection_impl_mac_vision.h"
namespace shape_detection {
......@@ -27,6 +28,14 @@ void BarcodeDetectionProviderMac::Create(
void BarcodeDetectionProviderMac::CreateBarcodeDetection(
mojom::BarcodeDetectionRequest request,
mojom::BarcodeDetectorOptionsPtr options) {
// Vision Framework needs at least MAC OS X 10.13.
if (@available(macOS 10.13, *)) {
mojo::MakeStrongBinding(
std::make_unique<BarcodeDetectionImplMacVision>(std::move(options)),
std::move(request));
return;
}
// Barcode detection needs at least MAC OS X 10.10.
if (@available(macOS 10.10, *)) {
mojo::MakeStrongBinding(std::make_unique<BarcodeDetectionImplMac>(),
......@@ -36,6 +45,19 @@ void BarcodeDetectionProviderMac::CreateBarcodeDetection(
void BarcodeDetectionProviderMac::EnumerateSupportedFormats(
EnumerateSupportedFormatsCallback callback) {
// Vision Framework needs at least MAC OS X 10.13.
if (@available(macOS 10.13, *)) {
// Vision recognizes more barcode symbologies than Core Image Framework.
std::move(callback).Run(
{mojom::BarcodeFormat::AZTEC, mojom::BarcodeFormat::CODE_128,
mojom::BarcodeFormat::CODE_39, mojom::BarcodeFormat::CODE_93,
mojom::BarcodeFormat::DATA_MATRIX, mojom::BarcodeFormat::EAN_13,
mojom::BarcodeFormat::EAN_8, mojom::BarcodeFormat::ITF,
mojom::BarcodeFormat::PDF417, mojom::BarcodeFormat::QR_CODE,
mojom::BarcodeFormat::UPC_E});
return;
}
// Barcode detection needs at least MAC OS X 10.10.
if (@available(macOS 10.10, *)) {
// Mac implementation supports only one BarcodeFormat.
......
......@@ -4,8 +4,6 @@
#include "services/shape_detection/face_detection_impl_mac_vision.h"
#include <dlfcn.h>
#include <objc/runtime.h>
#include <vector>
#include "base/bind.h"
......@@ -64,7 +62,7 @@ class API_AVAILABLE(macos(10.13))
bool PerformRequest(const SkBitmap& bitmap) {
Class image_handler_class = NSClassFromString(@"VNImageRequestHandler");
if (!image_handler_class) {
DLOG(ERROR) << "Failed to create VNImageRequestHandler";
DLOG(ERROR) << "Failed to load VNImageRequestHandler class";
return false;
}
......@@ -115,16 +113,9 @@ class API_AVAILABLE(macos(10.13))
};
FaceDetectionImplMacVision::FaceDetectionImplMacVision() : weak_factory_(this) {
static void* const vision_framework =
dlopen("/System/Library/Frameworks/Vision.framework/Vision", RTLD_LAZY);
if (!vision_framework) {
DLOG(ERROR) << "Failed to load Vision.framework";
return;
}
Class request_class = NSClassFromString(@"VNDetectFaceLandmarksRequest");
if (!request_class) {
DLOG(ERROR) << "Failed to create VNDetectFaceLandmarksRequest object";
DLOG(ERROR) << "Failed to load VNDetectFaceLandmarksRequest class";
return;
}
// The repeating callback will not be run if FaceDetectionImplMacVision object
......
......@@ -9,12 +9,12 @@
#include "base/bind.h"
#include "base/macros.h"
#include "build/build_config.h"
#include "services/service_manager/public/cpp/service_context.h"
#if defined(OS_WIN)
#include "services/shape_detection/barcode_detection_provider_impl.h"
#include "services/shape_detection/face_detection_provider_win.h"
#elif defined(OS_MACOSX)
#include <dlfcn.h>
#include "services/shape_detection/barcode_detection_provider_mac.h"
#include "services/shape_detection/face_detection_provider_mac.h"
#else
......@@ -34,9 +34,23 @@ std::unique_ptr<service_manager::Service> ShapeDetectionService::Create() {
return std::make_unique<ShapeDetectionService>();
}
ShapeDetectionService::ShapeDetectionService() = default;
ShapeDetectionService::ShapeDetectionService() {
#if defined(OS_MACOSX)
if (__builtin_available(macOS 10.13, *)) {
vision_framework_ =
dlopen("/System/Library/Frameworks/Vision.framework/Vision", RTLD_LAZY);
}
#endif
}
ShapeDetectionService::~ShapeDetectionService() = default;
ShapeDetectionService::~ShapeDetectionService() {
#if defined(OS_MACOSX)
if (__builtin_available(macOS 10.13, *)) {
if (vision_framework_)
dlclose(vision_framework_);
}
#endif
}
void ShapeDetectionService::OnStart() {
ref_factory_.reset(new service_manager::ServiceContextRefFactory(
......
......@@ -9,6 +9,7 @@
#include "base/callback.h"
#include "base/macros.h"
#include "build/build_config.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "services/service_manager/public/cpp/service.h"
......@@ -36,6 +37,8 @@ class ShapeDetectionService : public service_manager::Service {
// InterfaceProvider that is bound to the Java-side interface registry.
std::unique_ptr<service_manager::InterfaceProvider> java_interface_provider_;
#elif defined(OS_MACOSX)
void* vision_framework_;
#endif
std::unique_ptr<service_manager::ServiceContextRefFactory> ref_factory_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment