blob: 27bd523850e21409a2e048959796795dcc6c6e12 [file] [log] [blame]
// Copyright 2017 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/detection_utils_mac.h"
#include <vector>
#include "base/bind.h"
#include "base/logging.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/memory/ptr_util.h"
#include "base/numerics/checked_math.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/sys_string_conversions.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "third_party/skia/include/utils/mac/SkCGUtils.h"
namespace shape_detection {
base::scoped_nsobject<CIImage> CreateCIImageFromSkBitmap(
const SkBitmap& bitmap) {
base::CheckedNumeric<uint32_t> num_pixels =
base::CheckedNumeric<uint32_t>(bitmap.width()) * bitmap.height();
base::CheckedNumeric<uint32_t> num_bytes = num_pixels * 4;
if (!num_bytes.IsValid()) {
DLOG(ERROR) << "Data overflow";
return base::scoped_nsobject<CIImage>();
// First convert SkBitmap to CGImageRef.
base::ScopedCFTypeRef<CGImageRef> cg_image(
SkCreateCGImageRefWithColorspace(bitmap, NULL));
if (!cg_image) {
DLOG(ERROR) << "Failed to create CGImageRef";
return base::scoped_nsobject<CIImage>();
base::scoped_nsobject<CIImage> ci_image(
[[CIImage alloc] initWithCGImage:cg_image]);
if (!ci_image) {
DLOG(ERROR) << "Failed to create CIImage";
return base::scoped_nsobject<CIImage>();
return ci_image;
gfx::RectF ConvertCGToGfxCoordinates(CGRect bounds, int height) {
// In the default Core Graphics coordinate space, the origin is located
// in the lower-left corner, and thus |ci_image| is flipped vertically.
// We need to adjust |y| coordinate of bounding box before sending it.
return gfx::RectF(bounds.origin.x,
height - bounds.origin.y - bounds.size.height,
bounds.size.width, bounds.size.height);
// static
// Creates an VisionAPIAsyncRequestMac instance which sets |callback| to be
// called when the asynchronous action completes.
std::unique_ptr<VisionAPIAsyncRequestMac> VisionAPIAsyncRequestMac::Create(
Class request_class,
Callback callback) {
return base::WrapUnique(
new VisionAPIAsyncRequestMac(std::move(callback), request_class));
VisionAPIAsyncRequestMac::VisionAPIAsyncRequestMac(Callback callback,
Class request_class)
: callback_(std::move(callback)) {
scoped_refptr<base::SequencedTaskRunner> task_runner =
const auto handler = ^(VNRequest* request, NSError* error) {
task_runner->PostTask(FROM_HERE, base::BindOnce(callback_, request, error));
request_.reset([[request_class alloc] initWithCompletionHandler:handler]);
VisionAPIAsyncRequestMac::~VisionAPIAsyncRequestMac() = default;
// Processes asynchronously an image analysis request and returns results with
// |callback_| when the asynchronous request completes.
bool VisionAPIAsyncRequestMac::PerformRequest(const SkBitmap& bitmap) {
Class image_handler_class = NSClassFromString(@"VNImageRequestHandler");
if (!image_handler_class) {
DLOG(ERROR) << "Failed to load VNImageRequestHandler class";
return false;
base::scoped_nsobject<CIImage> ci_image = CreateCIImageFromSkBitmap(bitmap);
if (!ci_image) {
DLOG(ERROR) << "Failed to create image from SkBitmap";
return false;
base::scoped_nsobject<VNImageRequestHandler> image_handler(
[[image_handler_class alloc] initWithCIImage:ci_image options:@{}]);
if (!image_handler) {
DLOG(ERROR) << "Failed to create image request handler";
return false;
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError* ns_error = nil;
if ([image_handler performRequests:@[ request_ ] error:&ns_error])
DLOG(ERROR) << base::SysNSStringToUTF8([ns_error localizedDescription]);
return true;
} // namespace shape_detection