Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,25 @@
[
"OS=='win'",
{
"sources": ["src/binding_windows.cpp", "src/screenshot_windows.cpp"],
"sources": [
"src/binding_windows.cpp",
"src/screenshot/capture_windows.cpp",
"src/screenshot/icons_windows.cpp",
"src/screenshot/overlay_ui_windows.cpp",
"src/screenshot/overlay_paint_windows.cpp",
"src/screenshot/overlay_input_windows.cpp",
"src/screenshot/annotations_windows.cpp",
"src/screenshot/mosaic_windows.cpp",
"src/screenshot/output_windows.cpp",
"src/screenshot/lc_match_core.cpp",
"src/screenshot/lc_stitch_state.cpp",
"src/screenshot/lc_frame_io.cpp",
"src/screenshot/lc_panel_ui.cpp",
"src/screenshot/lc_toolbar_ui.cpp",
"src/screenshot/lc_session.cpp",
"src/screenshot/wndproc_windows.cpp",
"src/screenshot/session_windows.cpp"
],
"defines": ["_SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS"],
"libraries": [
"user32.lib",
Expand Down
31 changes: 28 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -491,19 +491,33 @@ class ScreenCapture {
* 启动区域截图
* @param {Object|Function} [options] - 截图选项;直接传函数时按旧签名 start(callback) 处理
* @param {boolean} [options.autoConfirm=true] - 选区确定后直接出图,跳过编辑态(工具栏/标注)
* @param {Object} [options.longCapture] - 长截图(手动滚动捕获)参数,进入编辑态后点工具栏「长截图」按钮生效。
* 点击后进入长截图预览界面:全屏黑色遮罩保留、选区框与底部工具栏保持展示(仅剩完成/取消,
* 其他操作禁用);选区内直通底层应用,滚轮滚动页面(鼠标按键被吞,防止误触),
* 侧边小地图实时展示拼接长图(外框=已捕获区域,蓝色内框=当前可见区域,随滚动移动);
* 向下滚动追加新内容到底部、向上滚动前插到头部;点「完成」出图、「取消」或 ESC 中止
* @param {number} [options.longCapture.maxFrames=100] - 最大拼接帧数(1~200,达到上限自动完成)
* @param {number} [options.longCapture.interval=250] - 滚轮停止后等待内容稳定的毫秒数(50~2000,采样防抖;
* 滚动进行中也会按不低于 min(interval, 250)ms 的节拍主动采样,保证相邻帧有大重叠区域)
* @param {Function} [callback] - 截图完成时的回调函数
* - 参数: { success: boolean, width?: number, height?: number, base64?: string }
* - success: 是否成功截图
* - width: 截图宽度(成功时)
* - height: 截图高度(成功时)
* - base64: 截图 PNG 的 base64(成功时)
* - width: 截图宽度(成功时;长截图为拼接后的总宽度
* - height: 截图高度(成功时;长截图为拼接后的总高度
* - base64: 截图 PNG 的 base64(成功时;长截图已同时写入剪贴板
*
* @example
* // 默认:框选/点选完成即出图,不再二次编辑
* ScreenCapture.start((result) => { ... });
*
* // 进入编辑态:选区确定后停留在工具栏,可标注/调整
* ScreenCapture.start({ autoConfirm: false }, (result) => { ... });
*
* // 编辑态 + 长截图:选区确定后点工具栏「长截图」按钮进入手动滚动捕获
* ScreenCapture.start({
* autoConfirm: false,
* longCapture: { maxFrames: 100, interval: 250 }
* }, (result) => { ... });
*/
static start(options, callback) {
if (platform === 'darwin') {
Expand All @@ -525,6 +539,17 @@ class ScreenCapture {
callback(result);
});
}

/**
* 中止进行中的长截图滚动捕获(Windows)
* 滚动捕获会以失败结果(success: false)回调后结束
*/
static abortLongCapture() {
if (platform === 'darwin') {
throw new Error('ScreenCapture is not yet supported on macOS');
}
addon.abortLongCapture();
}
}

// 应用图标提取类
Expand Down
30 changes: 16 additions & 14 deletions scripts/gen-icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
const fs = require('fs');
const path = require('path');

// 工具栏按钮 -> SVG 文件名映射(顺序与 src/screenshot_windows.cpp 的 ToolButton 枚举一致)
// 工具栏按钮 -> SVG 文件名映射(顺序与 src/screenshot/internal.h 的 ToolButton 枚举一致)
// 分隔线(Separator)对应 null,不生成图标。
const ICON_MAP = {
Drag: 'drag',
Expand All @@ -24,28 +24,30 @@ const ICON_MAP = {
Mosaic: 'mosaic',
Text: 'text',
Translate: 'translate',
LongCapture: 'long-capture',
Undo: 'arrow-back-up',
Redo: 'arrow-forward-up',
Save: 'download',
Cancel: 'x',
Confirm: 'check',
// —— 长截图工具栏专用图标(不进 kIconSvgs 数组,长截图窗口按名直取常量)——
DirectionV: 'direction-vertical',
DirectionH: 'direction-horizontal',
AutoScrollV: 'auto-scroll-vertical',
AutoScrollH: 'auto-scroll-horizontal',
CropIcon: 'crop',
// —— 长截图裁剪 popover 子操作图标 ——
CropDiscardTop: 'crop-discard-top',
CropDiscardBottom: 'crop-discard-bottom',
CropDiscardLeft: 'crop-discard-left',
CropDiscardRight: 'crop-discard-right',
CropReset: 'crop-reset',
};

const ASSETS_DIR = path.join(__dirname, '..', 'src', 'assets');
const OUT_DIR = path.join(__dirname, '..', 'src', 'generated');
const OUT_FILE = path.join(OUT_DIR, 'icon_svgs.h');

// C 字符串字面量转义:处理 " \ 和换行
function toCLiteral(str) {
// 把字符串按行拆开,每行用独立字面量拼接,保证可读性且避免超长行
return str
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.split(/\r?\n/)
.map((line) => ` "${line}"`)
.join('\n');
}

function main() {
if (!fs.existsSync(ASSETS_DIR)) {
console.error(`✖ assets dir not found: ${ASSETS_DIR}`);
Expand Down Expand Up @@ -76,9 +78,9 @@ function main() {
// 所有图标文本的数组(按 ToolButton 枚举顺序,分隔线为 nullptr)
lines.push('// 按 ToolButton 枚举顺序的 SVG 文本数组,分隔线为 nullptr。');
lines.push('static const char* const kIconSvgs[] = {');
// 顺序:Drag Rect Circle Arrow Brush Mosaic Text Translate SEP1 Undo Redo SEP2 Save Cancel Confirm
// 顺序:Drag Rect Circle Arrow Brush Mosaic Text Translate LongCapture SEP1 Undo Redo SEP2 Save Cancel Confirm
lines.push(' kIconSvg_Drag, kIconSvg_Rect, kIconSvg_Circle, kIconSvg_Arrow,');
lines.push(' kIconSvg_Brush, kIconSvg_Mosaic, kIconSvg_Text, kIconSvg_Translate, nullptr,');
lines.push(' kIconSvg_Brush, kIconSvg_Mosaic, kIconSvg_Text, kIconSvg_Translate, kIconSvg_LongCapture, nullptr,');
lines.push(' kIconSvg_Undo, kIconSvg_Redo, nullptr,');
lines.push(' kIconSvg_Save, kIconSvg_Cancel, kIconSvg_Confirm');
lines.push('};');
Expand Down
1 change: 1 addition & 0 deletions src/assets/auto-scroll-horizontal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/auto-scroll-vertical.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/crop-discard-bottom.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/crop-discard-left.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/crop-discard-right.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/crop-discard-top.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/crop-reset.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/crop.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/direction-horizontal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/direction-vertical.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/long-capture.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 5 additions & 22 deletions src/binding_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
#pragma comment(lib, "dwmapi.lib")
#pragma comment(lib, "uiautomationcore.lib")

#include "screenshot_windows.h"
#include "screenshot/screenshot_windows.h"

// DWMWA_CLOAKED 在较新的 Windows SDK 中定义,为了兼容性手动定义
#ifndef DWMWA_CLOAKED
Expand Down Expand Up @@ -3972,27 +3972,8 @@ static std::unique_ptr<Gdiplus::Bitmap> CreateBitmapFromIcon(
return bitmap;
}

// 获取 PNG 编码器 CLSID
int GetPngEncoderClsid(CLSID* pClsid) {
UINT num = 0u;
UINT size = 0u;
Gdiplus::GetImageEncodersSize(std::addressof(num), std::addressof(size));
if (size == 0u) return -1;

std::unique_ptr<Gdiplus::ImageCodecInfo> pImageCodecInfo(
static_cast<Gdiplus::ImageCodecInfo*>(static_cast<void*>(new BYTE[size])));
if (pImageCodecInfo == nullptr) return -1;

Gdiplus::GetImageEncoders(num, size, pImageCodecInfo.get());

for (UINT i = 0u; i < num; i++) {
if (std::wcscmp(pImageCodecInfo.get()[i].MimeType, L"image/png") == 0) {
*pClsid = pImageCodecInfo.get()[i].Clsid;
return (int)i;
}
}
return -1;
}
// GetPngEncoderClsid 已迁移至 src/screenshot/output_windows.cpp(CR-019),
// 此处经 screenshot_windows.h 声明继续复用(含进程内 CLSID 缓存)。

// 将 HICON 转换为 PNG 字节数组
static std::vector<unsigned char> HIconToPNG(HICON hIcon) {
Expand Down Expand Up @@ -6830,6 +6811,8 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set("startRegionCapture", Napi::Function::New(env, StartRegionCapture));
exports.Set("primeScreenshotFrame", Napi::Function::New(env, PrimeScreenshotFrame));
exports.Set("startRegionCaptureWithPrimedFrame", Napi::Function::New(env, StartRegionCaptureWithPrimedFrame));
// 中止进行中的长截图滚动捕获
exports.Set("abortLongCapture", Napi::Function::New(env, AbortLongCapture));
exports.Set("getClipboardFiles", Napi::Function::New(env, GetClipboardFiles));
exports.Set("setClipboardFiles", Napi::Function::New(env, SetClipboardFiles));
exports.Set("startMouseMonitor", Napi::Function::New(env, StartMouseMonitor));
Expand Down
Loading
Loading