blob: 4cee36581f1a350fa98ed63866767e3d50a5c2cc [file] [log] [blame]
// 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 <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/strings/sys_string_conversions.h"
#include "third_party/skia/include/core/SkBitmap.h"
namespace shape_detection {
BarcodeDetectionImplMacVision::BarcodeDetectionImplMacVision(
mojom::BarcodeDetectorOptionsPtr options)
: weak_factory_(this) {
Class request_class = NSClassFromString(@"VNDetectBarcodesRequest");
if (!request_class) {
DLOG(ERROR) << "Failed to load VNDetectBarcodesRequest class";
return;
}
// The repeating callback will not be run if BarcodeDetectionImplMacVision
// object has already been destroyed.
barcodes_async_request_ = VisionAPIAsyncRequestMac::Create(
request_class,
base::BindRepeating(&BarcodeDetectionImplMacVision::OnBarcodesDetected,
weak_factory_.GetWeakPtr()));
}
BarcodeDetectionImplMacVision::~BarcodeDetectionImplMacVision() = default;
void BarcodeDetectionImplMacVision::Detect(const SkBitmap& bitmap,
DetectCallback callback) {
DCHECK(barcodes_async_request_);
if (!barcodes_async_request_->PerformRequest(bitmap)) {
std::move(callback).Run({});
return;
}
image_size_ = CGSizeMake(bitmap.width(), bitmap.height());
// Hold on the callback until async request completes.
detected_callback_ = std::move(callback);
// This prevents the Detect function from being called before the
// VisionAPIAsyncRequestMac completes.
if (binding_) // Can be unbound in unit testing.
binding_->PauseIncomingMethodCallProcessing();
}
void BarcodeDetectionImplMacVision::OnBarcodesDetected(VNRequest* request,
NSError* error) {
if (binding_) // Can be unbound in unit testing.
binding_->ResumeIncomingMethodCallProcessing();
if ([request.results count] == 0 || error) {
std::move(detected_callback_).Run({});
return;
}
std::vector<mojom::BarcodeDetectionResultPtr> results;
for (VNBarcodeObservation* const observation in request.results) {
auto barcode = mojom::BarcodeDetectionResult::New();
// The coordinates are normalized to the dimensions of the processed image.
barcode->bounding_box = ConvertCGToGfxCoordinates(
CGRectMake(observation.boundingBox.origin.x * image_size_.width,
observation.boundingBox.origin.y * image_size_.height,
observation.boundingBox.size.width * image_size_.width,
observation.boundingBox.size.height * image_size_.height),
image_size_.height);
// Enumerate corner points starting from top-left in clockwise fashion:
// https://wicg.github.io/shape-detection-api/#dom-detectedbarcode-cornerpoints
barcode->corner_points.emplace_back(
observation.topLeft.x * image_size_.width,
(1 - observation.topLeft.y) * image_size_.height);
barcode->corner_points.emplace_back(
observation.topRight.x * image_size_.width,
(1 - observation.topRight.y) * image_size_.height);
barcode->corner_points.emplace_back(
observation.bottomRight.x * image_size_.width,
(1 - observation.bottomRight.y) * image_size_.height);
barcode->corner_points.emplace_back(
observation.bottomLeft.x * image_size_.width,
(1 - observation.bottomLeft.y) * image_size_.height);
barcode->raw_value =
base::SysNSStringToUTF8(observation.payloadStringValue);
results.push_back(std::move(barcode));
}
std::move(detected_callback_).Run(std::move(results));
}
} // namespace shape_detection