Introduced copy text from image feature on macOS
fix https://github.com/brave/brave-browser/issues/27513 "Copy Text From Image" entry is added to render view's context menu. It gets bitmap data from renderer via LocalFrame::GetImage() mojom interface, and then passed it to TextRecognitionDialog to extract and show text from that image.
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
import("//brave/browser/shell_integrations/buildflags/buildflags.gni")
|
||||
import("//brave/components/brave_vpn/common/buildflags/buildflags.gni")
|
||||
import("//brave/components/speedreader/common/buildflags/buildflags.gni")
|
||||
import("//brave/components/text_recognition/common/buildflags/buildflags.gni")
|
||||
import("//brave/resources/brave_grit.gni")
|
||||
import("//build/config/features.gni")
|
||||
import("//build/config/locales.gni")
|
||||
@@ -19,6 +20,7 @@ brave_grit("brave_generated_resources_grit") {
|
||||
"enable_speedreader=$enable_speedreader",
|
||||
"enable_brave_vpn=$enable_brave_vpn",
|
||||
"enable_pin_shortcut=$enable_pin_shortcut",
|
||||
"enable_text_recognition=$enable_text_recognition",
|
||||
]
|
||||
source = "brave_generated_resources.grd"
|
||||
output_dir = "$root_gen_dir/brave"
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#define IDC_COPY_CLEAN_LINK 56040
|
||||
#define IDC_TOGGLE_TAB_MUTE 56041
|
||||
#define IDC_SIDEBAR_TOGGLE_POSITION 56042
|
||||
#define IDC_CONTENT_CONTEXT_COPY_TEXT_FROM_IMAGE 56043
|
||||
|
||||
#define IDC_CONTENT_CONTEXT_IMPORT_IPNS_KEYS_START 56100
|
||||
#define IDC_CONTENT_CONTEXT_IMPORT_IPNS_KEYS_END 56199
|
||||
|
||||
@@ -896,6 +896,25 @@ Or change later at <ph name="SETTINGS_EXTENIONS_LINK">$2<ex>brave://settings/ext
|
||||
</message>
|
||||
</if>
|
||||
|
||||
<!-- Text recognition dialog-->
|
||||
<if expr="enable_text_recognition">
|
||||
<message name="IDS_CONTENT_CONTEXT_COPY_TEXT_FROM_IMAGE" desc="The name of the context menu item to get text from image">
|
||||
Copy Text From Image
|
||||
</message>
|
||||
<message name="IDS_TEXT_RECOG_DIALOG_CLOSE_BUTTON" desc="The text for the close button of text recognition dialog">
|
||||
Close
|
||||
</message>
|
||||
<message name="IDS_TEXT_RECOG_DIALOG_HEADER_IN_PROGRESS" desc="The text for the header of text recognition dialog">
|
||||
Copying text from image...
|
||||
</message>
|
||||
<message name="IDS_TEXT_RECOG_DIALOG_HEADER_COMPLETE" desc="The text for the header of text recognition dialog">
|
||||
Text copied from image
|
||||
</message>
|
||||
<message name="IDS_TEXT_RECOG_DIALOG_HEADER_FAILED" desc="The text for the header of text recognition dialog">
|
||||
Text copy failed
|
||||
</message>
|
||||
</if>
|
||||
|
||||
<!-- Brave VPN -->
|
||||
<if expr="enable_brave_vpn">
|
||||
<message name="IDS_SETTINGS_SHOW_VPN_BUTTON" desc="The label for showing vpn button in the toolbar">
|
||||
|
||||
@@ -12,6 +12,7 @@ import("//brave/components/ipfs/buildflags/buildflags.gni")
|
||||
import("//brave/components/ntp_background_images/buildflags/buildflags.gni")
|
||||
import("//brave/components/playlist/common/buildflags/buildflags.gni")
|
||||
import("//brave/components/speedreader/common/buildflags/buildflags.gni")
|
||||
import("//brave/components/text_recognition/common/buildflags/buildflags.gni")
|
||||
import("//brave/components/tor/buildflags/buildflags.gni")
|
||||
import("//build/config/features.gni")
|
||||
import("//chrome/common/features.gni")
|
||||
@@ -443,6 +444,15 @@ source_set("ui") {
|
||||
]
|
||||
}
|
||||
|
||||
if (enable_text_recognition) {
|
||||
sources += [
|
||||
"views/text_recognition_dialog_view.cc",
|
||||
"views/text_recognition_dialog_view.h",
|
||||
]
|
||||
|
||||
deps += [ "//brave/components/text_recognition/browser" ]
|
||||
}
|
||||
|
||||
# This is no longer compiled into Chromium on Android, but we still
|
||||
# need it
|
||||
if (is_android) {
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
#include "base/callback_forward.h"
|
||||
|
||||
class Browser;
|
||||
class SkBitmap;
|
||||
|
||||
namespace content {
|
||||
class WebContents;
|
||||
} // namespace content
|
||||
|
||||
namespace brave {
|
||||
|
||||
@@ -18,6 +23,10 @@ void ShowCrashReportPermissionAskDialog(Browser* browser);
|
||||
// Run |callback| when dialog closed.
|
||||
void ShowObsoleteSystemConfirmDialog(base::OnceCallback<void(bool)> callback);
|
||||
|
||||
// Show web modal dialog for showing text that recognized from |image|.
|
||||
void ShowTextRecognitionDialog(content::WebContents* web_contents,
|
||||
const SkBitmap& image);
|
||||
|
||||
} // namespace brave
|
||||
|
||||
#endif // BRAVE_BROWSER_UI_BROWSER_DIALOGS_H_
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/* Copyright (c) 2023 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "brave/browser/ui/views/text_recognition_dialog_view.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/task/task_traits.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "brave/components/l10n/common/localization_util.h"
|
||||
#include "brave/components/text_recognition/browser/text_recognition.h"
|
||||
#include "brave/grit/brave_generated_resources.h"
|
||||
#include "components/constrained_window/constrained_window_views.h"
|
||||
#include "ui/base/clipboard/scoped_clipboard_writer.h"
|
||||
#include "ui/base/metadata/metadata_impl_macros.h"
|
||||
#include "ui/gfx/geometry/insets.h"
|
||||
#include "ui/views/controls/label.h"
|
||||
#include "ui/views/controls/scroll_view.h"
|
||||
#include "ui/views/layout/flex_layout.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "ui/views/window/dialog_client_view.h"
|
||||
|
||||
namespace brave {
|
||||
|
||||
void ShowTextRecognitionDialog(content::WebContents* web_contents,
|
||||
const SkBitmap& image) {
|
||||
constrained_window::ShowWebModalDialogViews(
|
||||
new TextRecognitionDialogView(image), web_contents)
|
||||
->Show();
|
||||
}
|
||||
|
||||
} // namespace brave
|
||||
|
||||
TextRecognitionDialogView::TextRecognitionDialogView(const SkBitmap& image) {
|
||||
SetModalType(ui::MODAL_TYPE_CHILD);
|
||||
SetButtons(ui::DIALOG_BUTTON_OK);
|
||||
SetButtonLabel(ui::DIALOG_BUTTON_OK,
|
||||
brave_l10n::GetLocalizedResourceUTF16String(
|
||||
IDS_TEXT_RECOG_DIALOG_CLOSE_BUTTON));
|
||||
SetShowCloseButton(false);
|
||||
|
||||
SetLayoutManager(std::make_unique<views::FlexLayout>())
|
||||
->SetOrientation(views::LayoutOrientation::kVertical)
|
||||
.SetMainAxisAlignment(views::LayoutAlignment::kStart)
|
||||
.SetInteriorMargin(gfx::Insets::TLBR(24, 26, 0, 26));
|
||||
|
||||
header_label_ = AddChildView(std::make_unique<views::Label>());
|
||||
const int size_diff = 14 - views::Label::GetDefaultFontList().GetFontSize();
|
||||
header_label_->SetFontList(
|
||||
views::Label::GetDefaultFontList()
|
||||
.DeriveWithSizeDelta(size_diff)
|
||||
.DeriveWithWeight(gfx::Font::Weight::SEMIBOLD));
|
||||
header_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
|
||||
header_label_->SetProperty(views::kMarginsKey,
|
||||
gfx::Insets::TLBR(0, 0, 10, 0));
|
||||
|
||||
if (image.empty()) {
|
||||
UpdateContents({});
|
||||
return;
|
||||
}
|
||||
|
||||
StartExtractingText(image);
|
||||
}
|
||||
|
||||
TextRecognitionDialogView::~TextRecognitionDialogView() = default;
|
||||
|
||||
void TextRecognitionDialogView::StartExtractingText(const SkBitmap& image) {
|
||||
header_label_->SetText(brave_l10n::GetLocalizedResourceUTF16String(
|
||||
IDS_TEXT_RECOG_DIALOG_HEADER_IN_PROGRESS));
|
||||
|
||||
base::ThreadPool::PostTaskAndReplyWithResult(
|
||||
FROM_HERE,
|
||||
{base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
|
||||
base::BindOnce(&GetTextFromImage, image),
|
||||
base::BindOnce(&TextRecognitionDialogView::OnGetTextFromImage,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void TextRecognitionDialogView::OnGetTextFromImage(
|
||||
const std::vector<std::string>& text) {
|
||||
UpdateContents(text);
|
||||
AdjustWidgetSize();
|
||||
}
|
||||
|
||||
void TextRecognitionDialogView::UpdateContents(
|
||||
const std::vector<std::string>& text) {
|
||||
if (text.empty()) {
|
||||
header_label_->SetText(brave_l10n::GetLocalizedResourceUTF16String(
|
||||
IDS_TEXT_RECOG_DIALOG_HEADER_FAILED));
|
||||
return;
|
||||
}
|
||||
|
||||
header_label_->SetText(brave_l10n::GetLocalizedResourceUTF16String(
|
||||
IDS_TEXT_RECOG_DIALOG_HEADER_COMPLETE));
|
||||
|
||||
// Treat each string in |text| as a separated line string.
|
||||
std::string unified_string;
|
||||
for (const auto& t : text) {
|
||||
unified_string += t;
|
||||
unified_string += '\n';
|
||||
}
|
||||
|
||||
ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste)
|
||||
.WriteText(base::UTF8ToUTF16(unified_string));
|
||||
|
||||
auto* scroll_view = AddChildView(std::make_unique<views::ScrollView>());
|
||||
scroll_view->SetProperty(views::kMarginsKey, gfx::Insets::VH(0, 10));
|
||||
scroll_view->ClipHeightTo(0, 350);
|
||||
auto* label = scroll_view->SetContents(
|
||||
std::make_unique<views::Label>(base::UTF8ToUTF16(unified_string)));
|
||||
|
||||
label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
|
||||
label->SetSelectable(true);
|
||||
label->SetMultiLine(true);
|
||||
}
|
||||
|
||||
void TextRecognitionDialogView::AdjustWidgetSize() {
|
||||
GetWidget()->SetSize(GetDialogClientView()->GetPreferredSize());
|
||||
}
|
||||
|
||||
BEGIN_METADATA(TextRecognitionDialogView, views::DialogDelegateView)
|
||||
END_METADATA
|
||||
@@ -0,0 +1,45 @@
|
||||
/* Copyright (c) 2023 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef BRAVE_BROWSER_UI_VIEWS_TEXT_RECOGNITION_DIALOG_VIEW_H_
|
||||
#define BRAVE_BROWSER_UI_VIEWS_TEXT_RECOGNITION_DIALOG_VIEW_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "ui/base/metadata/metadata_header_macros.h"
|
||||
#include "ui/views/window/dialog_delegate.h"
|
||||
|
||||
class SkBitmap;
|
||||
|
||||
namespace views {
|
||||
class Label;
|
||||
} // namespace views
|
||||
|
||||
class TextRecognitionDialogView : public views::DialogDelegateView {
|
||||
public:
|
||||
METADATA_HEADER(TextRecognitionDialogView);
|
||||
|
||||
explicit TextRecognitionDialogView(const SkBitmap& image);
|
||||
TextRecognitionDialogView(const TextRecognitionDialogView&) = delete;
|
||||
TextRecognitionDialogView& operator=(const TextRecognitionDialogView&) =
|
||||
delete;
|
||||
~TextRecognitionDialogView() override;
|
||||
|
||||
private:
|
||||
void StartExtractingText(const SkBitmap& image);
|
||||
void OnGetTextFromImage(const std::vector<std::string>& text);
|
||||
|
||||
// Show |text| in this dialog and copy it to clipboard.
|
||||
void UpdateContents(const std::vector<std::string>& text);
|
||||
void AdjustWidgetSize();
|
||||
|
||||
raw_ptr<views::Label> header_label_ = nullptr;
|
||||
base::WeakPtrFactory<TextRecognitionDialogView> weak_factory_{this};
|
||||
};
|
||||
|
||||
#endif // BRAVE_BROWSER_UI_VIEWS_TEXT_RECOGNITION_DIALOG_VIEW_H_
|
||||
@@ -32,6 +32,7 @@ include_rules = [
|
||||
"+brave/components/privacy_sandbox",
|
||||
"+brave/components/sidebar",
|
||||
"+brave/components/sync",
|
||||
"+brave/components/text_recognition",
|
||||
"+brave/components/tor",
|
||||
"+brave/components/translate",
|
||||
"+brave/components/url_sanitizer",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2018 The Brave Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
/* Copyright (c) 2018 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
|
||||
|
||||
@@ -9,6 +10,7 @@
|
||||
#include "brave/browser/profiles/profile_util.h"
|
||||
#include "brave/browser/renderer_context_menu/brave_spelling_options_submenu_observer.h"
|
||||
#include "brave/browser/ui/browser_commands.h"
|
||||
#include "brave/browser/ui/browser_dialogs.h"
|
||||
#include "brave/components/ipfs/buildflags/buildflags.h"
|
||||
#include "brave/components/tor/buildflags/buildflags.h"
|
||||
#include "brave/grit/brave_theme_resources.h"
|
||||
@@ -137,6 +139,16 @@ void OnTorProfileCreated(const GURL& link_url,
|
||||
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_TEXT_RECOGNITION)
|
||||
void OnGetImage(base::WeakPtr<content::WebContents> web_contents,
|
||||
const SkBitmap& image) {
|
||||
if (!web_contents)
|
||||
return;
|
||||
|
||||
brave::ShowTextRecognitionDialog(web_contents.get(), image);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
BraveRenderViewContextMenu::BraveRenderViewContextMenu(
|
||||
@@ -152,6 +164,10 @@ BraveRenderViewContextMenu::BraveRenderViewContextMenu(
|
||||
|
||||
bool BraveRenderViewContextMenu::IsCommandIdEnabled(int id) const {
|
||||
switch (id) {
|
||||
#if BUILDFLAG(ENABLE_TEXT_RECOGNITION)
|
||||
case IDC_CONTENT_CONTEXT_COPY_TEXT_FROM_IMAGE:
|
||||
return params_.has_image_contents;
|
||||
#endif
|
||||
case IDC_COPY_CLEAN_LINK:
|
||||
return params_.link_url.is_valid();
|
||||
case IDC_CONTENT_CONTEXT_FORCE_PASTE:
|
||||
@@ -249,12 +265,27 @@ void BraveRenderViewContextMenu::ExecuteCommand(int id, int event_flags) {
|
||||
base::BindRepeating(OnTorProfileCreated, params_.link_url,
|
||||
HasAlreadyOpenedTorWindow(GetProfile())));
|
||||
break;
|
||||
#endif
|
||||
#if BUILDFLAG(ENABLE_TEXT_RECOGNITION)
|
||||
case IDC_CONTENT_CONTEXT_COPY_TEXT_FROM_IMAGE:
|
||||
CopyTextFromImage();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
RenderViewContextMenu_Chromium::ExecuteCommand(id, event_flags);
|
||||
}
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_TEXT_RECOGNITION)
|
||||
void BraveRenderViewContextMenu::CopyTextFromImage() {
|
||||
RenderFrameHost* frame_host = GetRenderFrameHost();
|
||||
if (frame_host)
|
||||
frame_host->GetImageAt(
|
||||
params_.x, params_.y,
|
||||
base::BindOnce(OnGetImage, source_web_contents_->GetWeakPtr()));
|
||||
}
|
||||
#endif
|
||||
|
||||
void BraveRenderViewContextMenu::AddSpellCheckServiceItem(bool is_checked) {
|
||||
// Call our implementation, not the one in the base class.
|
||||
// Assumption:
|
||||
@@ -382,6 +413,18 @@ void BraveRenderViewContextMenu::InitMenu() {
|
||||
IDC_CONTENT_CONTEXT_FORCE_PASTE,
|
||||
IDS_CONTENT_CONTEXT_FORCE_PASTE);
|
||||
}
|
||||
#if BUILDFLAG(ENABLE_TEXT_RECOGNITION)
|
||||
bool media_image = content_type_->SupportsGroup(
|
||||
ContextMenuContentType::ITEM_GROUP_MEDIA_IMAGE);
|
||||
if (media_image) {
|
||||
index =
|
||||
menu_model_.GetIndexOfCommandId(IDC_CONTENT_CONTEXT_COPYIMAGELOCATION);
|
||||
DCHECK(index);
|
||||
menu_model_.InsertItemWithStringIdAt(
|
||||
index.value() + 1, IDC_CONTENT_CONTEXT_COPY_TEXT_FROM_IMAGE,
|
||||
IDS_CONTENT_CONTEXT_COPY_TEXT_FROM_IMAGE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_TOR)
|
||||
// Add Open Link with Tor
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// Copyright 2018 The Brave Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
/* Copyright (c) 2018 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef BRAVE_CHROMIUM_SRC_CHROME_BROWSER_RENDERER_CONTEXT_MENU_RENDER_VIEW_CONTEXT_MENU_H_
|
||||
#define BRAVE_CHROMIUM_SRC_CHROME_BROWSER_RENDERER_CONTEXT_MENU_RENDER_VIEW_CONTEXT_MENU_H_
|
||||
|
||||
#include "brave/components/ipfs/buildflags/buildflags.h"
|
||||
#include "brave/components/text_recognition/common/buildflags/buildflags.h"
|
||||
|
||||
#define BRAVE_RENDER_VIEW_CONTEXT_MENU_H_ \
|
||||
private: \
|
||||
@@ -53,6 +55,10 @@ class BraveRenderViewContextMenu : public RenderViewContextMenu_Chromium {
|
||||
|
||||
ui::SimpleMenuModel ipfs_submenu_model_;
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_TEXT_RECOGNITION)
|
||||
void CopyTextFromImage();
|
||||
#endif
|
||||
};
|
||||
|
||||
// Use our own subclass as the real RenderViewContextMenu.
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import("//brave/components/brave_vpn/common/buildflags/buildflags.gni")
|
||||
import("//brave/components/text_recognition/common/buildflags/buildflags.gni")
|
||||
import("//build/config/ui.gni")
|
||||
|
||||
brave_chromium_src_chrome_browser_deps = [
|
||||
"//base",
|
||||
"//brave/components/brave_vpn/common/buildflags",
|
||||
"//brave/components/playlist/common/buildflags",
|
||||
"//brave/components/text_recognition/common/buildflags",
|
||||
"//chrome/common:channel_info",
|
||||
"//components/version_info",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/* Copyright (c) 2023 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "src/content/browser/renderer_host/render_frame_host_impl.cc"
|
||||
|
||||
namespace content {
|
||||
|
||||
void RenderFrameHostImpl::GetImageAt(
|
||||
int x,
|
||||
int y,
|
||||
base::OnceCallback<void(const SkBitmap&)> callback) {
|
||||
gfx::PointF point_in_view =
|
||||
GetView()->TransformRootPointToViewCoordSpace(gfx::PointF(x, y));
|
||||
GetAssociatedLocalFrame()->GetImageAt(
|
||||
gfx::Point(point_in_view.x(), point_in_view.y()), std::move(callback));
|
||||
}
|
||||
|
||||
} // namespace content
|
||||
@@ -0,0 +1,21 @@
|
||||
/* Copyright (c) 2023 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef BRAVE_CHROMIUM_SRC_CONTENT_BROWSER_RENDERER_HOST_RENDER_FRAME_HOST_IMPL_H_
|
||||
#define BRAVE_CHROMIUM_SRC_CONTENT_BROWSER_RENDERER_HOST_RENDER_FRAME_HOST_IMPL_H_
|
||||
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "third_party/blink/public/mojom/frame/frame.mojom.h"
|
||||
|
||||
#define CopyImageAt \
|
||||
GetImageAt(int x, int y, base::OnceCallback<void(const SkBitmap&)> callback) \
|
||||
override; \
|
||||
void CopyImageAt
|
||||
|
||||
#include "src/content/browser/renderer_host/render_frame_host_impl.h"
|
||||
|
||||
#undef CopyImageAt
|
||||
|
||||
#endif // BRAVE_CHROMIUM_SRC_CONTENT_BROWSER_RENDERER_HOST_RENDER_FRAME_HOST_IMPL_H_
|
||||
@@ -0,0 +1,20 @@
|
||||
/* Copyright (c) 2023 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef BRAVE_CHROMIUM_SRC_CONTENT_PUBLIC_BROWSER_RENDER_FRAME_HOST_H_
|
||||
#define BRAVE_CHROMIUM_SRC_CONTENT_PUBLIC_BROWSER_RENDER_FRAME_HOST_H_
|
||||
|
||||
class SkBitmap;
|
||||
|
||||
#define CopyImageAt \
|
||||
GetImageAt(int x, int y, \
|
||||
base::OnceCallback<void(const SkBitmap&)> callback) = 0; \
|
||||
virtual void CopyImageAt
|
||||
|
||||
#include "src/content/public/browser/render_frame_host.h"
|
||||
|
||||
#undef CopyImageAt
|
||||
|
||||
#endif // BRAVE_CHROMIUM_SRC_CONTENT_PUBLIC_BROWSER_RENDER_FRAME_HOST_H_
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2023 The Brave Authors. All rights reserved.
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
module blink.mojom;
|
||||
|
||||
import "skia/public/mojom/bitmap.mojom";
|
||||
import "ui/gfx/geometry/mojom/geometry.mojom";
|
||||
|
||||
[BraveExtend]
|
||||
interface LocalFrame {
|
||||
GetImageAt(gfx.mojom.Point window_point)
|
||||
=> (skia.mojom.BitmapN32? bitmap_image);
|
||||
};
|
||||
@@ -6,7 +6,11 @@
|
||||
#include "third_party/blink/renderer/core/frame/local_frame.h"
|
||||
|
||||
#include "brave/components/brave_page_graph/common/buildflags.h"
|
||||
#include "skia/ext/skia_utils_base.h"
|
||||
#include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
|
||||
#include "third_party/blink/renderer/core/layout/layout_image.h"
|
||||
#include "third_party/blink/renderer/core/probe/core_probes.h"
|
||||
#include "third_party/blink/renderer/platform/graphics/graphics_types_3d.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_BRAVE_PAGE_GRAPH)
|
||||
#include "brave/third_party/blink/renderer/core/brave_page_graph/page_graph.h"
|
||||
@@ -23,4 +27,79 @@
|
||||
|
||||
#include "src/third_party/blink/renderer/core/frame/local_frame.cc"
|
||||
|
||||
namespace blink {
|
||||
|
||||
namespace {
|
||||
|
||||
static scoped_refptr<Image> ImageFromNode(const Node& node) {
|
||||
DCHECK(!node.GetDocument().NeedsLayoutTreeUpdate());
|
||||
DocumentLifecycle::DisallowTransitionScope disallow_transition(
|
||||
node.GetDocument().Lifecycle());
|
||||
|
||||
const LayoutObject* const layout_object = node.GetLayoutObject();
|
||||
if (!layout_object)
|
||||
return nullptr;
|
||||
|
||||
if (layout_object->IsCanvas()) {
|
||||
return To<HTMLCanvasElement>(const_cast<Node&>(node))
|
||||
.Snapshot(kFrontBuffer);
|
||||
}
|
||||
|
||||
if (!layout_object->IsImage())
|
||||
return nullptr;
|
||||
|
||||
const auto& layout_image = To<LayoutImage>(*layout_object);
|
||||
const ImageResourceContent* const cached_image = layout_image.CachedImage();
|
||||
if (!cached_image || cached_image->ErrorOccurred())
|
||||
return nullptr;
|
||||
return cached_image->GetImage();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SkBitmap LocalFrame::GetImageAtViewportPoint(const gfx::Point& viewport_point) {
|
||||
HitTestResult result = HitTestResultForVisualViewportPos(viewport_point);
|
||||
if (!IsA<HTMLCanvasElement>(result.InnerNodeOrImageMapImage()) &&
|
||||
result.AbsoluteImageURL().IsEmpty()) {
|
||||
// There isn't actually an image at these coordinates. Might be because
|
||||
// the window scrolled while the context menu was open or because the page
|
||||
// changed itself between when we thought there was an image here and when
|
||||
// we actually tried to retrieve the image.
|
||||
//
|
||||
// FIXME: implement a cache of the most recent HitTestResult to avoid having
|
||||
// to do two hit tests.
|
||||
return {};
|
||||
}
|
||||
|
||||
const scoped_refptr<Image> image =
|
||||
ImageFromNode(*result.InnerNodeOrImageMapImage());
|
||||
if (!image.get())
|
||||
return {};
|
||||
|
||||
PaintImage paint_image = image->PaintImageForCurrentFrame();
|
||||
// Orient the data.
|
||||
if (!image->HasDefaultOrientation()) {
|
||||
paint_image = Image::ResizeAndOrientImage(
|
||||
paint_image, image->CurrentFrameOrientation(), gfx::Vector2dF(1, 1), 1,
|
||||
kInterpolationNone);
|
||||
}
|
||||
SkBitmap bitmap;
|
||||
if (sk_sp<SkImage> sk_image = paint_image.GetSwSkImage())
|
||||
sk_image->asLegacyBitmap(&bitmap);
|
||||
|
||||
// The bitmap backing a canvas can be in non-native skia pixel order (aka
|
||||
// RGBA when kN32_SkColorType is BGRA-ordered, or higher bit-depth color-types
|
||||
// like F16. The IPC to the browser requires the bitmap to be in N32 format
|
||||
// so we convert it here if needed.
|
||||
SkBitmap n32_bitmap;
|
||||
if (skia::SkBitmapToN32OpaqueOrPremul(bitmap, &n32_bitmap) &&
|
||||
!n32_bitmap.isNull()) {
|
||||
return n32_bitmap;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace blink
|
||||
|
||||
#undef FrameAttachedToParent
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/* Copyright (c) 2023 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_LOCAL_FRAME_H_
|
||||
#define BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_LOCAL_FRAME_H_
|
||||
|
||||
class SkBitmap;
|
||||
|
||||
#define CopyImageAtViewportPoint \
|
||||
CopyImageAtViewportPoint_UnUsed() {} \
|
||||
SkBitmap GetImageAtViewportPoint(const gfx::Point& viewport_point); \
|
||||
void CopyImageAtViewportPoint
|
||||
|
||||
#include "src/third_party/blink/renderer/core/frame/local_frame.h"
|
||||
|
||||
#undef CopyImageAtViewportPoint
|
||||
|
||||
#endif // BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_LOCAL_FRAME_H_
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/* Copyright (c) 2023 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "src/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc"
|
||||
|
||||
namespace blink {
|
||||
|
||||
void LocalFrameMojoHandler::GetImageAt(const gfx::Point& window_point,
|
||||
GetImageAtCallback callback) {
|
||||
gfx::Point viewport_position =
|
||||
frame_->GetWidgetForLocalRoot()->DIPsToRoundedBlinkSpace(window_point);
|
||||
std::move(callback).Run(frame_->GetImageAtViewportPoint(viewport_position));
|
||||
}
|
||||
|
||||
} // namespace blink
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/* Copyright (c) 2023 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_LOCAL_FRAME_MOJO_HANDLER_H_
|
||||
#define BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_LOCAL_FRAME_MOJO_HANDLER_H_
|
||||
|
||||
#include "third_party/blink/public/mojom/frame/frame.mojom-blink.h"
|
||||
|
||||
#define CopyImageAt \
|
||||
GetImageAt(const gfx::Point& window_point, GetImageAtCallback callback) \
|
||||
final; \
|
||||
void CopyImageAt
|
||||
|
||||
#include "src/third_party/blink/renderer/core/frame/local_frame_mojo_handler.h"
|
||||
|
||||
#undef CopyImageAt
|
||||
|
||||
#endif // BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_LOCAL_FRAME_MOJO_HANDLER_H_
|
||||
@@ -0,0 +1,5 @@
|
||||
include_rules = [
|
||||
"+base",
|
||||
"+skia",
|
||||
"+third_party/skia",
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
# Copyright (c) 2023 The Brave Authors. All rights reserved.
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
import("//brave/components/text_recognition/common/buildflags/buildflags.gni")
|
||||
|
||||
assert(enable_text_recognition)
|
||||
|
||||
source_set("browser") {
|
||||
sources = [ "text_recognition.h" ]
|
||||
|
||||
if (is_mac) {
|
||||
sources += [ "text_recognition.mm" ]
|
||||
|
||||
frameworks = [
|
||||
"Foundation.framework",
|
||||
"Vision.framework",
|
||||
]
|
||||
}
|
||||
|
||||
deps = [
|
||||
"//base",
|
||||
"//skia",
|
||||
]
|
||||
|
||||
configs += [ "//build/config/compiler:enable_arc" ]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/* Copyright (c) 2023 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef BRAVE_COMPONENTS_TEXT_RECOGNITION_BROWSER_TEXT_RECOGNITION_H_
|
||||
#define BRAVE_COMPONENTS_TEXT_RECOGNITION_BROWSER_TEXT_RECOGNITION_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class SkBitmap;
|
||||
|
||||
// Returns recognized texts from |image|.
|
||||
std::vector<std::string> GetTextFromImage(const SkBitmap& image);
|
||||
|
||||
#endif // BRAVE_COMPONENTS_TEXT_RECOGNITION_BROWSER_TEXT_RECOGNITION_H_
|
||||
@@ -0,0 +1,75 @@
|
||||
/* Copyright (c) 2023 The Brave Authors. All rights reserved.
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "brave/components/text_recognition/browser/text_recognition.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Vision/Vision.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/foundation_util.h"
|
||||
#include "base/mac/mac_util.h"
|
||||
#include "base/mac/scoped_cftyperef.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "skia/ext/skia_utils_base.h"
|
||||
#include "skia/ext/skia_utils_mac.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "third_party/skia/include/utils/mac/SkCGUtils.h"
|
||||
|
||||
std::vector<std::string> GetTextFromImage(const SkBitmap& image) {
|
||||
std::vector<std::string> result;
|
||||
if (@available(macOS 10.15, *)) {
|
||||
// The bitmap type is sanitized to be N32 before we get here. The conversion
|
||||
// to an NSImage would not explode if we got this wrong, so this is not a
|
||||
// security CHECK.
|
||||
DCHECK_EQ(image.colorType(), kN32_SkColorType);
|
||||
|
||||
auto* p_result = &result;
|
||||
VNRecognizeTextRequest* textRecognitionRequest =
|
||||
[[VNRecognizeTextRequest alloc] initWithCompletionHandler:^(
|
||||
VNRequest* _Nonnull request,
|
||||
NSError* _Nullable error) {
|
||||
NSArray<VNRecognizedTextObservation*>* observations = request.results;
|
||||
|
||||
[observations enumerateObjectsUsingBlock:^(
|
||||
VNRecognizedTextObservation* _Nonnull obj,
|
||||
NSUInteger idx, BOOL* _Nonnull stop) {
|
||||
// Ask first top candidate for a recognized text string.
|
||||
VNRecognizedText* recognizedText =
|
||||
[obj topCandidates:1].firstObject;
|
||||
|
||||
p_result->push_back(
|
||||
base::SysNSStringToUTF8([recognizedText string]));
|
||||
}];
|
||||
}];
|
||||
|
||||
textRecognitionRequest.recognitionLevel =
|
||||
VNRequestTextRecognitionLevelAccurate;
|
||||
textRecognitionRequest.usesLanguageCorrection = true;
|
||||
// Copied FF's supported language list.
|
||||
// See https://support.mozilla.org/en-US/kb/text-recognition
|
||||
// English, Chinese, Portuguese, French, Italian, German, and Spanish
|
||||
textRecognitionRequest.recognitionLanguages = @[
|
||||
@"en-US", @"zh-Hans", @"zh-Hant", @"pt-BR", @"fr-FR", @"it-IT", @"de-DE",
|
||||
@"es-ES"
|
||||
];
|
||||
|
||||
if (@available(macOS 13.0, *)) {
|
||||
textRecognitionRequest.automaticallyDetectsLanguage = true;
|
||||
}
|
||||
|
||||
NSError* error = nil;
|
||||
base::ScopedCFTypeRef<CGImageRef> cg_image(SkCreateCGImageRef(image));
|
||||
VNImageRequestHandler* requestHandler =
|
||||
[[VNImageRequestHandler alloc] initWithCGImage:cg_image.get()
|
||||
options:@{}];
|
||||
[requestHandler performRequests:@[ textRecognitionRequest ] error:&error];
|
||||
if (error) {
|
||||
LOG(ERROR) << base::SysNSStringToUTF8([error localizedDescription]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# Copyright (c) 2023 The Brave Authors. All rights reserved.
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
import("//brave/components/text_recognition/common/buildflags/buildflags.gni")
|
||||
import("//build/buildflag_header.gni")
|
||||
|
||||
buildflag_header("buildflags") {
|
||||
header = "buildflags.h"
|
||||
flags = [ "ENABLE_TEXT_RECOGNITION=$enable_text_recognition" ]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Copyright (c) 2023 The Brave Authors. All rights reserved.
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
declare_args() {
|
||||
enable_text_recognition = is_mac
|
||||
}
|
||||
Reference in New Issue
Block a user