| // Copyright 2025 The Chromium Authors |
| // 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_chrome.h" |
| |
| #include <stdint.h> |
| |
| #include <limits> |
| #include <memory> |
| #include <vector> |
| |
| #include "base/containers/span.h" |
| #include "base/feature_list.h" |
| #include "base/logging.h" |
| #include "base/numerics/checked_math.h" |
| #include "services/shape_detection/features.h" |
| #include "services/shape_detection/public/mojom/barcodedetection.mojom-shared.h" |
| #include "services/shape_detection/shape_detection_library_holder.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "ui/gfx/geometry/rect_f.h" |
| |
| namespace shape_detection { |
| |
| namespace { |
| |
| gfx::RectF CornerPointsToBoundingBox( |
| base::span<const ChromePointF>& corner_points) { |
| float xmin = std::numeric_limits<float>::infinity(); |
| float ymin = std::numeric_limits<float>::infinity(); |
| float xmax = -std::numeric_limits<float>::infinity(); |
| float ymax = -std::numeric_limits<float>::infinity(); |
| for (auto& point : corner_points) { |
| xmin = std::min(xmin, point.x); |
| ymin = std::min(ymin, point.y); |
| xmax = std::max(xmax, point.x); |
| ymax = std::max(ymax, point.y); |
| } |
| return gfx::RectF(xmin, ymin, (xmax - xmin), (ymax - ymin)); |
| } |
| |
| mojom::BarcodeFormat BarcodeFormatToMojo(ChromeBarcodeFormat format) { |
| switch (format) { |
| case CHROME_BARCODE_FORMAT_UNKNOWN: |
| return mojom::BarcodeFormat::UNKNOWN; |
| case CHROME_BARCODE_FORMAT_AZTEC: |
| return mojom::BarcodeFormat::AZTEC; |
| case CHROME_BARCODE_FORMAT_CODE_128: |
| return mojom::BarcodeFormat::CODE_128; |
| case CHROME_BARCODE_FORMAT_CODE_39: |
| return mojom::BarcodeFormat::CODE_39; |
| case CHROME_BARCODE_FORMAT_CODE_93: |
| return mojom::BarcodeFormat::CODE_93; |
| case CHROME_BARCODE_FORMAT_CODABAR: |
| return mojom::BarcodeFormat::CODABAR; |
| case CHROME_BARCODE_FORMAT_DATA_MATRIX: |
| return mojom::BarcodeFormat::DATA_MATRIX; |
| case CHROME_BARCODE_FORMAT_EAN_13: |
| return mojom::BarcodeFormat::EAN_13; |
| case CHROME_BARCODE_FORMAT_EAN_8: |
| return mojom::BarcodeFormat::EAN_8; |
| case CHROME_BARCODE_FORMAT_ITF: |
| return mojom::BarcodeFormat::ITF; |
| case CHROME_BARCODE_FORMAT_PDF417: |
| return mojom::BarcodeFormat::PDF417; |
| case CHROME_BARCODE_FORMAT_QR_CODE: |
| return mojom::BarcodeFormat::QR_CODE; |
| case CHROME_BARCODE_FORMAT_UPC_A: |
| return mojom::BarcodeFormat::UPC_A; |
| case CHROME_BARCODE_FORMAT_UPC_E: |
| return mojom::BarcodeFormat::UPC_E; |
| default: |
| NOTREACHED() << "Invalid barcode format"; |
| } |
| } |
| |
| ChromeBarcodeFormat GetExpectedFormats( |
| const shape_detection::mojom::BarcodeDetectorOptionsPtr& options) { |
| ChromeBarcodeFormat expected_formats = CHROME_BARCODE_FORMAT_UNKNOWN; |
| if (options->formats.empty()) { |
| expected_formats = |
| CHROME_BARCODE_FORMAT_AZTEC | CHROME_BARCODE_FORMAT_CODE_128 | |
| CHROME_BARCODE_FORMAT_CODE_39 | CHROME_BARCODE_FORMAT_CODE_93 | |
| CHROME_BARCODE_FORMAT_CODABAR | CHROME_BARCODE_FORMAT_DATA_MATRIX | |
| CHROME_BARCODE_FORMAT_EAN_13 | CHROME_BARCODE_FORMAT_EAN_8 | |
| CHROME_BARCODE_FORMAT_ITF | CHROME_BARCODE_FORMAT_PDF417 | |
| CHROME_BARCODE_FORMAT_QR_CODE | CHROME_BARCODE_FORMAT_UPC_A | |
| CHROME_BARCODE_FORMAT_UPC_E; |
| return expected_formats; |
| } |
| |
| for (const auto& format : options->formats) { |
| switch (format) { |
| case mojom::BarcodeFormat::AZTEC: |
| expected_formats |= CHROME_BARCODE_FORMAT_AZTEC; |
| break; |
| case mojom::BarcodeFormat::CODE_128: |
| expected_formats |= CHROME_BARCODE_FORMAT_CODE_128; |
| break; |
| case mojom::BarcodeFormat::CODE_39: |
| expected_formats |= CHROME_BARCODE_FORMAT_CODE_39; |
| break; |
| case mojom::BarcodeFormat::CODE_93: |
| expected_formats |= CHROME_BARCODE_FORMAT_CODE_93; |
| break; |
| case mojom::BarcodeFormat::CODABAR: |
| expected_formats |= CHROME_BARCODE_FORMAT_CODABAR; |
| break; |
| case mojom::BarcodeFormat::DATA_MATRIX: |
| expected_formats |= CHROME_BARCODE_FORMAT_DATA_MATRIX; |
| break; |
| case mojom::BarcodeFormat::EAN_13: |
| expected_formats |= CHROME_BARCODE_FORMAT_EAN_13; |
| break; |
| case mojom::BarcodeFormat::EAN_8: |
| expected_formats |= CHROME_BARCODE_FORMAT_EAN_8; |
| break; |
| case mojom::BarcodeFormat::ITF: |
| expected_formats |= CHROME_BARCODE_FORMAT_ITF; |
| break; |
| case mojom::BarcodeFormat::PDF417: |
| expected_formats |= CHROME_BARCODE_FORMAT_PDF417; |
| break; |
| case mojom::BarcodeFormat::QR_CODE: |
| expected_formats |= CHROME_BARCODE_FORMAT_QR_CODE; |
| break; |
| case mojom::BarcodeFormat::UPC_E: |
| expected_formats |= CHROME_BARCODE_FORMAT_UPC_E; |
| break; |
| case mojom::BarcodeFormat::UPC_A: |
| expected_formats |= CHROME_BARCODE_FORMAT_UPC_A; |
| break; |
| case mojom::BarcodeFormat::UNKNOWN: |
| expected_formats |= CHROME_BARCODE_FORMAT_UNKNOWN; |
| break; |
| } |
| } |
| |
| return expected_formats; |
| } |
| |
| } // namespace |
| |
| BarcodeDetectionImplChrome::BarcodeDetectionImplChrome( |
| mojom::BarcodeDetectorOptionsPtr options) |
| : expected_formats_(GetExpectedFormats(options)) {} |
| |
| BarcodeDetectionImplChrome::~BarcodeDetectionImplChrome() = default; |
| |
| DISABLE_CFI_DLSYM |
| void BarcodeDetectionImplChrome::Detect( |
| const SkBitmap& bitmap, |
| shape_detection::mojom::BarcodeDetection::DetectCallback callback) { |
| int width = bitmap.width(); |
| int height = bitmap.height(); |
| std::vector<uint8_t> luminances( |
| (base::CheckedNumeric<size_t>(width) * height).ValueOrDie()); |
| for (int y = 0; y < height; y++) { |
| for (int x = 0; x < width; x++) { |
| SkColor color = bitmap.getColor(x, y); |
| // Fast and approximate luminance calculation: (2*R + 5*G + B) / 8 |
| uint32_t luminance = |
| 2 * SkColorGetR(color) + 5 * SkColorGetG(color) + SkColorGetB(color); |
| luminances[y * width + x] = luminance / 8; |
| } |
| } |
| |
| ChromeBarcodeDetectionResult* detection_results; |
| size_t num_results; |
| const auto* holder = ShapeDetectionLibraryHolder::GetInstance(); |
| // Holder should be valid if it passed pre-sandbox initialization. |
| CHECK(holder); |
| holder->api().DetectBarcodesWithFallback( |
| width, height, luminances.data(), expected_formats_, |
| base::FeatureList::IsEnabled( |
| features::kBarhopperAztecRefineTransformFallback), |
| &detection_results, &num_results); |
| |
| // SAFTY: `detection_results` was allocated with `num_results` by |
| // ChromeShapeDetectionAPI. |
| UNSAFE_BUFFERS( |
| base::span<ChromeBarcodeDetectionResult> detection_results_span( |
| detection_results, num_results);) |
| std::vector<mojom::BarcodeDetectionResultPtr> results; |
| for (const auto& barcode : detection_results_span) { |
| auto result = shape_detection::mojom::BarcodeDetectionResult::New(); |
| |
| // SAFTY: `barcode.corner_points` was allocated with `barcode. |
| // corner_points_size` by ChromeShapeDetectionAPI. |
| UNSAFE_BUFFERS(base::span<const ChromePointF> corner_points_span( |
| barcode.corner_points, barcode.corner_points_size)); |
| result->bounding_box = CornerPointsToBoundingBox(corner_points_span); |
| for (auto& corner_point : corner_points_span) { |
| result->corner_points.emplace_back(corner_point.x, corner_point.y); |
| } |
| result->raw_value = std::string( |
| reinterpret_cast<const char*>(barcode.value), barcode.value_size); |
| result->format = BarcodeFormatToMojo(barcode.format); |
| results.push_back(std::move(result)); |
| } |
| |
| holder->api().DestroyDetectionResults(detection_results, num_results); |
| std::move(callback).Run(std::move(results)); |
| } |
| |
| // static |
| std::vector<mojom::BarcodeFormat> |
| BarcodeDetectionImplChrome::GetSupportedFormats() { |
| return {mojom::BarcodeFormat::AZTEC, mojom::BarcodeFormat::CODE_128, |
| mojom::BarcodeFormat::CODE_39, mojom::BarcodeFormat::CODE_93, |
| mojom::BarcodeFormat::CODABAR, 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_A, |
| mojom::BarcodeFormat::UPC_E}; |
| } |
| |
| } // namespace shape_detection |